paging: Introduce a GSM_PAGING_BUSY event for a special timeout

Start counting the attempts of each paging request and call
the callback with the PAGING_BUSY type when the paging request
timed out but the subscriber was not paged at all. This can
only happen with a huge paging backlog.
In case the system has so many pending paging
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index f42ae1b..5badde2 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -83,6 +83,7 @@
 	GSM_PAGING_SUCCEEDED,
 	GSM_PAGING_EXPIRED,
 	GSM_PAGING_OOM,
+	GSM_PAGING_BUSY,
 };
 
 enum bts_gprs_mode {
diff --git a/openbsc/include/openbsc/paging.h b/openbsc/include/openbsc/paging.h
index 68d8a6a..a78eb8e 100644
--- a/openbsc/include/openbsc/paging.h
+++ b/openbsc/include/openbsc/paging.h
@@ -46,6 +46,9 @@
 	/* Timer 3113: how long do we try to page? */
 	struct timer_list T3113;
 
+	/* How often did we ask the BTS to page? */
+	int attempts;
+
 	/* callback to be called in case paging completes */
 	gsm_cbfn *cbfn;
 	void *cbfn_param;
diff --git a/openbsc/include/openbsc/signal.h b/openbsc/include/openbsc/signal.h
index 40766db..fd95c1e 100644
--- a/openbsc/include/openbsc/signal.h
+++ b/openbsc/include/openbsc/signal.h
@@ -140,6 +140,8 @@
 	struct gsm_subscriber *subscr;
 	struct gsm_bts *bts;
 
+	int paging_result;
+
 	/* NULL in case the paging didn't work */
 	struct gsm_subscriber_connection *conn;
 };
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index 42dd1b7..92da6cc 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -1409,6 +1409,7 @@
 			gsm48_cc_tx_setup(transt, &transt->cc.msg);
 			break;
 		case GSM_PAGING_EXPIRED:
+		case GSM_PAGING_BUSY:
 			DEBUGP(DCC, "Paging subscr %s expired!\n",
 				subscr->extension);
 			/* Temporarily out of order */
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index c46f772..4ef9ee7 100644
--- a/openbsc/src/gsm_04_11.c
+++ b/openbsc/src/gsm_04_11.c
@@ -1126,6 +1126,7 @@
 		break;
 	case GSM_PAGING_EXPIRED:
 	case GSM_PAGING_OOM:
+	case GSM_PAGING_BUSY:
 		sms_free(sms);
 		rc = -ETIMEDOUT;
 		break;
diff --git a/openbsc/src/gsm_subscriber.c b/openbsc/src/gsm_subscriber.c
index f066eca..5dd680f 100644
--- a/openbsc/src/gsm_subscriber.c
+++ b/openbsc/src/gsm_subscriber.c
@@ -84,6 +84,7 @@
 	sig_data.subscr = subscr;
 	sig_data.bts	= conn ? conn->bts : NULL;
 	sig_data.conn	= conn;
+	sig_data.paging_result = event;
 	dispatch_signal(
 		SS_PAGING,
 		event == GSM_PAGING_SUCCEEDED ?
@@ -169,7 +170,7 @@
 
 	/* paging failed, quit now */
 	if (rc <= 0) {
-		subscr_paging_cb(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED,
+		subscr_paging_cb(GSM_HOOK_RR_PAGING, GSM_PAGING_BUSY,
 				 NULL, NULL, subscr);
 	}
 }
diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c
index 06e6860..bfe3020 100644
--- a/openbsc/src/paging.c
+++ b/openbsc/src/paging.c
@@ -209,6 +209,7 @@
 	/* handle the paging request now */
 	page_ms(request);
 	paging_bts->available_slots--;
+	request->attempts++;
 
 	/* take the current and add it to the back */
 	llist_del(&request->entry);
@@ -253,6 +254,7 @@
 	struct gsm_paging_request *req = (struct gsm_paging_request *)data;
 	void *cbfn_param;
 	gsm_cbfn *cbfn;
+	int msg;
 
 	LOGP(DPAG, LOGL_INFO, "T3113 expired for request %p (%s)\n",
 		req, req->subscr->imsi);
@@ -261,10 +263,15 @@
 	counter_inc(req->bts->network->stats.paging.expired);
 	cbfn_param = req->cbfn_param;
 	cbfn = req->cbfn;
+
+	/* did we ever manage to page the subscriber */
+	msg = req->attempts > 0 ? GSM_PAGING_EXPIRED : GSM_PAGING_BUSY;
+
+	/* destroy it now. Do not access req afterwards */
 	paging_remove_request(&req->bts->paging, req);
 
 	if (cbfn)
-		cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, NULL, NULL,
+		cbfn(GSM_HOOK_RR_PAGING, msg, NULL, NULL,
 			  cbfn_param);
 }
 
diff --git a/openbsc/src/silent_call.c b/openbsc/src/silent_call.c
index 29ade00..2eb37ba 100644
--- a/openbsc/src/silent_call.c
+++ b/openbsc/src/silent_call.c
@@ -60,6 +60,7 @@
 		dispatch_signal(SS_SCALL, S_SCALL_SUCCESS, &sigdata);
 		break;
 	case GSM_PAGING_EXPIRED:
+	case GSM_PAGING_BUSY:
 		DEBUGP(DSMS, "expired\n");
 		dispatch_signal(SS_SCALL, S_SCALL_EXPIRED, &sigdata);
 		break;