nacc: Avoid RIM procedures targeting cells under same PCU

Now that we have the required System Information in osmo-pcu to craft
the Packet Neigbour Cell Change packet for cells it controls, let's
avoid starting a RIM procedure to gather the SI info, since the SGSN
would end up routing the RIM request back at us and we'd answer back to
ourselves.

This same optimization cannot be done on the first step (CTRL Neighbor
Resolution against BSC), because the PCU cannot know if the target
ARFCN+BSIC requested by the MS is actually managed by a cell under the
PCU or it's another cell managed by another external PCU, because
ARFCN+BSIC keys can be resued among non-neighbor cells. Hence, it shall
always ask the BSC since only it holds the information about neighboring
cells.

Related: OS#4909
Change-Id: I928875b6b66dff55fc12f51b6b1ad919fef7d03b
diff --git a/src/nacc_fsm.c b/src/nacc_fsm.c
index b479f9c..6a92f83 100644
--- a/src/nacc_fsm.c
+++ b/src/nacc_fsm.c
@@ -268,6 +268,27 @@
 	return 0;
 }
 
+#define SI_HDR_LEN 2
+static void bts_fill_si_cache_value(const struct gprs_rlcmac_bts *bts, struct si_cache_value *val)
+{
+	val->type_psi = false;
+	val->si_len = 0;
+	if (bts->si1_is_set) {
+		osmo_static_assert(sizeof(bts->si1) - SI_HDR_LEN == BSSGP_RIM_SI_LEN, _si1_header_size);
+		memcpy(&val->si_buf[val->si_len], bts->si1 + SI_HDR_LEN, BSSGP_RIM_SI_LEN);
+		val->si_len += BSSGP_RIM_SI_LEN;
+	}
+	if (bts->si3_is_set) {
+		osmo_static_assert(sizeof(bts->si3) - SI_HDR_LEN == BSSGP_RIM_SI_LEN, _si3_header_size);
+		memcpy(&val->si_buf[val->si_len], bts->si3 + SI_HDR_LEN, BSSGP_RIM_SI_LEN);
+		val->si_len += BSSGP_RIM_SI_LEN;
+	}
+	if (bts->si13_is_set) {
+		osmo_static_assert(sizeof(bts->si13) - SI_HDR_LEN == BSSGP_RIM_SI_LEN, _si13_header_size);
+		memcpy(&val->si_buf[val->si_len], bts->si13 + SI_HDR_LEN, BSSGP_RIM_SI_LEN);
+		val->si_len += BSSGP_RIM_SI_LEN;
+	}
+}
 
 ////////////////
 // FSM states //
@@ -382,8 +403,28 @@
 	struct gprs_pcu *pcu = bts->pcu;
 	struct bssgp_ran_information_pdu pdu;
 	const struct si_cache_value *si;
+	struct gprs_rlcmac_bts *bts_i;
 	int rc;
 
+	/* First check if the CGI-PS addresses a cell managed by this PCU. If
+	 * that's the case, we already have the info and there's no need to go
+	 * the RIM way since we'd end up to this same PCU on the other end anyway.
+	 */
+	llist_for_each_entry(bts_i, &the_pcu->bts_list, list) {
+		if (bts_i == bts) /* Makes no sense targeting the same cell */
+			continue;
+		if (osmo_cgi_ps_cmp(&ctx->cgi_ps, &bts_i->cgi_ps) != 0)
+			continue;
+
+		LOGPFSML(fi, LOGL_DEBUG, "neighbor CGI-PS %s addresses local BTS %d\n",
+			 osmo_cgi_ps_name(&ctx->cgi_ps), bts_i->nr);
+		bts_fill_si_cache_value(bts, &ctx->si_info);
+		/* Tell the PCU scheduler we are ready to go, from here one we
+		 * are polled/driven by the scheduler */
+		nacc_fsm_state_chg(fi, NACC_ST_TX_NEIGHBOUR_DATA);
+		return;
+	}
+
 	/* First check if we have SI info for the target cell in cache */
 	si = si_cache_lookup_value(pcu->si_cache, &ctx->cgi_ps);
 	if (si) {