gprs: Block other GSUP procedures during PURGE_MS

GSM 09.02, 19.4.1.4 mandates that no other MAP procedures shall be
started until the PURGE_MS procedure has been completed.

This patch implements this by adding corresponding state and checks
to gprs_subscr_purge, gprs_subscr_location_update, and
gprs_subscr_update_auth_info. If an Update Location or a Send Auth
Info Req procedure is not started because of blocking, the retry
mechanism is aborted to shorten the blocking time. The outstanding
Purge MS procedure itself is not aborted.

Sponsored-by: On-Waves ehf
diff --git a/openbsc/src/gprs/gprs_subscriber.c b/openbsc/src/gprs/gprs_subscriber.c
index 8399ea1..88e037e 100644
--- a/openbsc/src/gprs/gprs_subscriber.c
+++ b/openbsc/src/gprs/gprs_subscriber.c
@@ -76,6 +76,23 @@
 	return rc;
 }
 
+static int check_blocking(
+	struct gsm_subscriber *subscr,
+	enum sgsn_subscriber_proc what)
+{
+	if (subscr->sgsn_data->blocked_by == SGSN_SUBSCR_PROC_NONE ||
+	    subscr->sgsn_data->blocked_by == what)
+		return 1;
+
+	return 0;
+}
+
+static void abort_blocking_procedure(struct gsm_subscriber *subscr)
+{
+	/* Best effort, stop retries at least */
+	subscr->sgsn_data->retries = SGSN_SUBSCR_MAX_RETRIES;
+}
+
 static void sgsn_subscriber_timeout_cb(void *subscr_);
 int gprs_subscr_purge(struct gsm_subscriber *subscr);
 
@@ -132,6 +149,10 @@
 	return;
 
 force_cleanup:
+	/* Make sure to clear blocking */
+	if (check_blocking(subscr, SGSN_SUBSCR_PROC_PURGE))
+		subscr->sgsn_data->blocked_by = SGSN_SUBSCR_PROC_NONE;
+
 	/* Make sure, the timer is cleaned up */
 	subscr->keep_in_ram = 0;
 	gprs_subscr_stop_timer(subscr);
@@ -544,17 +565,42 @@
 int gprs_subscr_purge(struct gsm_subscriber *subscr)
 {
 	struct gprs_gsup_message gsup_msg = {0};
+	int rc;
+
+	if (!check_blocking(subscr, SGSN_SUBSCR_PROC_PURGE)) {
+		LOGGSUBSCRP(
+			LOGL_NOTICE, subscr,
+			"Cannot purge MS subscriber, blocked\n");
+		return -EAGAIN;
+	}
+
+	/* GSM 09.02, 19.4.1.4 requires other MAP requests to be blocked until
+	 * this procedure is completed
+	 */
+	subscr->sgsn_data->blocked_by = SGSN_SUBSCR_PROC_PURGE;
 
 	LOGGSUBSCRP(LOGL_INFO, subscr, "purging MS subscriber\n");
 
 	gsup_msg.message_type = GPRS_GSUP_MSGT_PURGE_MS_REQUEST;
-	return gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
+	rc = gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
+	if (rc < 0)
+		subscr->sgsn_data->blocked_by = SGSN_SUBSCR_PROC_NONE;
+
+	return rc;
 }
 
 int gprs_subscr_query_auth_info(struct gsm_subscriber *subscr)
 {
 	struct gprs_gsup_message gsup_msg = {0};
 
+	if (!check_blocking(subscr, SGSN_SUBSCR_PROC_UPD_AUTH)) {
+		LOGGSUBSCRP(
+			LOGL_NOTICE, subscr,
+			"Cannot start update auth info request procedure, blocked\n");
+		abort_blocking_procedure(subscr);
+		return -EAGAIN;
+	}
+
 	LOGGSUBSCRP(LOGL_INFO, subscr,
 		"subscriber auth info is not available\n");
 
@@ -566,6 +612,14 @@
 {
 	struct gprs_gsup_message gsup_msg = {0};
 
+	if (!check_blocking(subscr, SGSN_SUBSCR_PROC_UPD_LOC)) {
+		LOGGSUBSCRP(
+			LOGL_NOTICE, subscr,
+			"Cannot start update location procedure, blocked\n");
+		abort_blocking_procedure(subscr);
+		return -EAGAIN;
+	}
+
 	LOGGSUBSCRP(LOGL_INFO, subscr,
 		"subscriber data is not available\n");