Maintain per ggsn pdp ctx list

This way we can easily track all pdp context associated to a specific
ggsn, which is useful to handle some scenarios, such as the one
implemented in next commit, in which specs references that GSNs should
ping only other GSNs with at least one pdp ctx in common. So the list
of pdp ctx per GGSN is really useful too (and cheap computationally)
to check if we should arm or disarm the echo procedure timer.

So this commit can be seen as a preparation for next commit.

Change-Id: I3bbcc0883df2bf1290ba8d4bd70db8baa494087a
diff --git a/src/gprs/gprs_sgsn.c b/src/gprs/gprs_sgsn.c
index 688eaed..a58e504 100644
--- a/src/gprs/gprs_sgsn.c
+++ b/src/gprs/gprs_sgsn.c
@@ -388,6 +388,7 @@
 
 /* you don't want to use this directly, call sgsn_create_pdp_ctx() */
 struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
+					struct sgsn_ggsn_ctx *ggsn,
 					uint8_t nsapi)
 {
 	struct sgsn_pdp_ctx *pdp;
@@ -401,6 +402,7 @@
 		return NULL;
 
 	pdp->mm = mm;
+	pdp->ggsn = ggsn;
 	pdp->nsapi = nsapi;
 	pdp->ctrg = rate_ctr_group_alloc(pdp, &pdpctx_ctrg_desc, nsapi);
 	if (!pdp->ctrg) {
@@ -409,6 +411,7 @@
 		return NULL;
 	}
 	llist_add(&pdp->list, &mm->pdp_list);
+	llist_add(&pdp->ggsn_list, &ggsn->pdp_list);
 	llist_add(&pdp->g_list, &sgsn_pdp_ctxts);
 
 	return pdp;
@@ -462,6 +465,8 @@
 	rate_ctr_group_free(pdp->ctrg);
 	if (pdp->mm)
 		llist_del(&pdp->list);
+	if (pdp->ggsn)
+		llist_del(&pdp->ggsn_list);
 	llist_del(&pdp->g_list);
 
 	/* _if_ we still have a library handle, at least set it to NULL
@@ -496,6 +501,7 @@
 	ggc->remote_restart_ctr = -1;
 	/* if we are called from config file parse, this gsn doesn't exist yet */
 	ggc->gsn = sgsn->gsn;
+	INIT_LLIST_HEAD(&ggc->pdp_list);
 	llist_add(&ggc->list, &sgsn_ggsn_ctxts);
 
 	return ggc;
@@ -503,6 +509,7 @@
 
 void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc)
 {
+	OSMO_ASSERT(llist_empty(&ggc->pdp_list));
 	llist_del(&ggc->list);
 	talloc_free(ggc);
 }
@@ -702,19 +709,14 @@
 
 /* High-level function to be called in case a GGSN has disappeared or
  * otherwise lost state (recovery procedure) */
-int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn)
+int sgsn_ggsn_ctx_drop_all_pdp(struct sgsn_ggsn_ctx *ggsn)
 {
-	struct sgsn_mm_ctx *mm;
 	int num = 0;
 
-	llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
-		struct sgsn_pdp_ctx *pdp;
-		llist_for_each_entry(pdp, &mm->pdp_list, list) {
-			if (pdp->ggsn == ggsn) {
-				drop_one_pdp(pdp);
-				num++;
-			}
-		}
+	struct sgsn_pdp_ctx *pdp, *pdp2;
+	llist_for_each_entry_safe(pdp, pdp2, &ggsn->pdp_list, ggsn_list) {
+		drop_one_pdp(pdp);
+		num++;
 	}
 
 	return num;