osmux: Add connection and global rate counters

Change-Id: I200a4aa3908cac0ec729d980a66f3df7f55e4da7
diff --git a/include/osmocom/mgcp/mgcp_conn.h b/include/osmocom/mgcp/mgcp_conn.h
index ce864b8..14ddfbb 100644
--- a/include/osmocom/mgcp/mgcp_conn.h
+++ b/include/osmocom/mgcp/mgcp_conn.h
@@ -88,11 +88,8 @@
 		struct osmux_in_handle *in;
 		/* handle to unbatch messages, one allocated and owned per conn */
 		struct osmux_out_handle *out;
-		/* statistics */
-		struct {
-			uint32_t chunks;
-			uint32_t octets;
-		} stats;
+		/* statistics: */
+		struct rate_ctr_group *ctrg;
 	} osmux;
 
 	struct {
@@ -181,6 +178,40 @@
 	[RTP_NUM_CONNECTIONS] = {"all_rtp:num_closed_conns", "Total number of rtp connections closed."}
 };
 
+/* Osmux connection related counters */
+enum {
+	OSMUX_CHUNKS_RX_CTR,
+	OSMUX_OCTETS_RX_CTR,
+	OSMUX_DROPPED_AMR_PAYLOADS_CTR,
+	/* Only available in global stats: */
+	OSMUX_NUM_CONNECTIONS,
+	OSMUX_PACKETS_RX_CTR,
+	OSMUX_PACKETS_TX_CTR,
+	OSMUX_DROPPED_PACKETS_CTR,
+};
+
+/* RTP per-connection statistics. Instances of the corresponding rate counter group
+ * exist for the lifetime of an RTP connection.
+ * Must be kept in sync with all_rtp_conn_rate_ctr_desc below */
+static const struct rate_ctr_desc mgcp_conn_osmux_rate_ctr_desc[] = {
+	[OSMUX_CHUNKS_RX_CTR] = {"osmux:chunks_rx", "Inbound Osmux chunks."},
+	[OSMUX_OCTETS_RX_CTR] = {"osmux:octets_rx", "Inbound Osmux octets."},
+	[OSMUX_DROPPED_AMR_PAYLOADS_CTR] = {"osmux:dropped_amr_payloads", "Dropped outbound AMR payloads."}
+};
+
+/* Aggregated Osmux connection stats. These are updated when an Osmux connection is freed.
+ * Must be kept in sync with mgcp_conn_osmux_rate_ctr_desc above */
+static const struct rate_ctr_desc all_osmux_conn_rate_ctr_desc[] = {
+	[OSMUX_CHUNKS_RX_CTR] = {"all_osmux:chunks_rx", "Inbound Osmux chunks."},
+	[OSMUX_OCTETS_RX_CTR] = {"all_osmux:octets_rx", "Inbound Osmux octets."},
+	[OSMUX_DROPPED_AMR_PAYLOADS_CTR] = {"all_osmux:dropped_amr_payloads", "Dropped outbound AMR payloads."},
+	/* These last counters below do not exist in per-connection stats, only here: */
+	[OSMUX_NUM_CONNECTIONS] = {"all_osmux:num_closed_conns", "Total number of osmux connections closed."},
+	[OSMUX_PACKETS_RX_CTR] = {"all_osmux:packets_rx", "Total inbound UDP/Osmux packets."},
+	[OSMUX_PACKETS_TX_CTR] = {"all_osmux:packets_tx", "Total outbound UDP/Osmux packets."},
+	[OSMUX_DROPPED_PACKETS_CTR] = {"all_osmux:dropped_packets", "Dropped outbound UDP/Osmux packets."}
+};
+
 /* Was conn configured to handle Osmux? */
 static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) {
 	return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT;
diff --git a/include/osmocom/mgcp/mgcp_network.h b/include/osmocom/mgcp/mgcp_network.h
index 9c075b0..5668711 100644
--- a/include/osmocom/mgcp/mgcp_network.h
+++ b/include/osmocom/mgcp/mgcp_network.h
@@ -178,5 +178,7 @@
 
 void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
 				 int id, int inc);
+void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
+				 int id);
 void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg);
 uint32_t mgcp_get_current_ts(unsigned codec_rate);
diff --git a/include/osmocom/mgcp/mgcp_ratectr.h b/include/osmocom/mgcp/mgcp_ratectr.h
index c03c4e1..f566625 100644
--- a/include/osmocom/mgcp/mgcp_ratectr.h
+++ b/include/osmocom/mgcp/mgcp_ratectr.h
@@ -80,6 +80,8 @@
 	struct rate_ctr_group *mgcp_dlcx_ctr_group;
 	/* Rate counter group which aggregates stats of individual RTP connections. */
 	struct rate_ctr_group *all_rtp_conn_stats;
+	/* Rate counter group which aggregates stats of individual Osmux connections. */
+	struct rate_ctr_group *all_osmux_conn_stats;
 	/* Rate counter group which contains stats for E1 events (only valid for E1 trunks) */
 	struct rate_ctr_group *e1_stats;
 };
diff --git a/src/libosmo-mgcp/mgcp_network.c b/src/libosmo-mgcp/mgcp_network.c
index dfbe4d3..d492563 100644
--- a/src/libosmo-mgcp/mgcp_network.c
+++ b/src/libosmo-mgcp/mgcp_network.c
@@ -70,7 +70,7 @@
 	rate_ctr_add(rate_ctr_group_get_ctr(mgw_stats, id), inc);
 }
 
-static void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, int id)
+void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, int id)
 {
 	rtpconn_rate_ctr_add(conn_rtp, endp, id, 1);
 }
diff --git a/src/libosmo-mgcp/mgcp_osmux.c b/src/libosmo-mgcp/mgcp_osmux.c
index 4ad3f10..5ba48e8 100644
--- a/src/libosmo-mgcp/mgcp_osmux.c
+++ b/src/libosmo-mgcp/mgcp_osmux.c
@@ -41,12 +41,22 @@
 	int refcnt;
 };
 
+static const struct rate_ctr_group_desc rate_ctr_group_osmux_desc = {
+	.group_name_prefix = "conn_osmux",
+	.group_description = "Osmux connection statistics",
+	.class_id = 1,
+	.num_ctr = ARRAY_SIZE(mgcp_conn_osmux_rate_ctr_desc),
+	.ctr_desc = mgcp_conn_osmux_rate_ctr_desc
+};
+
 /* Deliver OSMUX batch to the remote end */
 static void osmux_deliver_cb(struct msgb *batch_msg, void *data)
 {
 	struct osmux_handle *handle = data;
 	socklen_t dest_len;
 	int rc;
+	struct mgcp_trunk *trunk = (struct mgcp_trunk *)osmux_fd.data;
+	struct rate_ctr_group *all_osmux_stats = trunk->ratectr.all_osmux_conn_stats;
 
 	switch (handle->rem_addr.u.sa.sa_family) {
 	case AF_INET6:
@@ -64,6 +74,9 @@
 		strerror_r(errno, errbuf, sizeof(errbuf));
 		LOGP(DOSMUX, LOGL_NOTICE, "osmux sendto(%s) failed: %s\n",
 			 osmo_sockaddr_to_str(&handle->rem_addr), errbuf);
+		rate_ctr_inc(rate_ctr_group_get_ctr(all_osmux_stats, OSMUX_DROPPED_PACKETS_CTR));
+	} else {
+		rate_ctr_inc(rate_ctr_group_get_ctr(all_osmux_stats, OSMUX_PACKETS_TX_CTR));
 	}
 	msgb_free(batch_msg);
 }
@@ -190,12 +203,15 @@
 	int ret;
 	struct msgb *msg;
 
-	if (!conn->end.output_enabled)
+	if (!conn->end.output_enabled) {
+		rtpconn_rate_ctr_inc(conn, conn->conn->endp, OSMUX_DROPPED_AMR_PAYLOADS_CTR);
 		return -1;
+	}
 
 	if (conn->osmux.state != OSMUX_STATE_ENABLED) {
 		LOGPCONN(conn->conn, DOSMUX, LOGL_INFO, "forwarding RTP to Osmux conn not yet enabled, dropping (cid=%d)\n",
 		conn->osmux.cid);
+		rtpconn_rate_ctr_inc(conn, conn->conn->endp, OSMUX_DROPPED_AMR_PAYLOADS_CTR);
 		return -1;
 	}
 
@@ -368,14 +384,16 @@
 	struct msgb *msg;
 	struct osmux_hdr *osmuxh;
 	struct osmo_sockaddr rem_addr;
-	struct mgcp_trunk *trunk = ofd->data;
 	uint32_t rem;
-	struct mgcp_conn_rtp *conn_src;
+	struct mgcp_trunk *trunk = ofd->data;
+	struct rate_ctr_group *all_rtp_stats = trunk->ratectr.all_osmux_conn_stats;
 
 	msg = osmux_recv(ofd, &rem_addr);
 	if (!msg)
 		return -1;
 
+	rate_ctr_inc(rate_ctr_group_get_ctr(all_rtp_stats, OSMUX_PACKETS_RX_CTR));
+
 	if (!trunk->cfg->osmux) {
 		LOGP(DOSMUX, LOGL_ERROR,
 		     "bsc-nat wants to use Osmux but bsc did not request it\n");
@@ -388,7 +406,8 @@
 
 	rem = msg->len;
 	while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
-
+		struct mgcp_endpoint *endp;
+		struct mgcp_conn_rtp *conn_src;
 		conn_src = osmux_conn_lookup(trunk, osmuxh->circuit_id,
 					     &rem_addr);
 		if (!conn_src) {
@@ -397,7 +416,7 @@
 			     osmuxh->circuit_id);
 			goto out;
 		}
-
+		endp = conn_src->conn->endp;
 		mgcp_conn_watchdog_kick(conn_src->conn);
 
 		/*conn_dst = mgcp_find_dst_conn(conn_src->conn);
@@ -407,10 +426,10 @@
 			     osmuxh->circuit_id);
 			goto out;
 		}*/
-
-		if (endp_osmux_state_check(conn_src->conn->endp, conn_src, false) == 0) {
-			conn_src->osmux.stats.octets += osmux_chunk_length(msg, rem);
-			conn_src->osmux.stats.chunks++;
+		if (endp_osmux_state_check(endp, conn_src, false) == 0) {
+			rtpconn_rate_ctr_inc(conn_src, endp, OSMUX_CHUNKS_RX_CTR);
+			rtpconn_rate_ctr_add(conn_src, endp, OSMUX_OCTETS_RX_CTR,
+					     osmux_chunk_length(msg, rem));
 			osmux_xfrm_output_sched(conn_src->osmux.out, osmuxh);
 		}
 		rem = msg->len;
@@ -513,6 +532,8 @@
 	osmux_xfrm_output_set_tx_cb(conn->osmux.out,
 				    scheduled_from_osmux_tx_rtp_cb, conn);
 
+	conn->osmux.ctrg = rate_ctr_group_alloc(conn->conn, &rate_ctr_group_osmux_desc, conn->ctrg->idx);
+
 	conn->osmux.state = OSMUX_STATE_ENABLED;
 
 	return 0;
@@ -528,6 +549,9 @@
 	LOGPCONN(conn->conn, DOSMUX, LOGL_INFO,
 		"Releasing connection using Osmux CID %u\n", conn->osmux.cid);
 
+	struct rate_ctr_group *all_osmux_stats = conn->conn->endp->trunk->ratectr.all_osmux_conn_stats;
+	rate_ctr_inc(rate_ctr_group_get_ctr(all_osmux_stats, OSMUX_NUM_CONNECTIONS));
+
 	if (conn->osmux.state == OSMUX_STATE_ENABLED) {
 		/* We are closing, we don't need pending RTP packets to be transmitted */
 		osmux_xfrm_output_set_tx_cb(conn->osmux.out, NULL, NULL);
@@ -536,6 +560,9 @@
 		osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
 		conn->osmux.state = OSMUX_STATE_DISABLED;
 		osmux_handle_put(conn->osmux.in);
+
+		rate_ctr_group_free(conn->osmux.ctrg);
+		conn->osmux.ctrg = NULL;
 	}
 	conn_osmux_release_cid(conn);
 }
diff --git a/src/libosmo-mgcp/mgcp_ratectr.c b/src/libosmo-mgcp/mgcp_ratectr.c
index bc6aa3c..bf3d9d3 100644
--- a/src/libosmo-mgcp/mgcp_ratectr.c
+++ b/src/libosmo-mgcp/mgcp_ratectr.c
@@ -146,6 +146,14 @@
 	.ctr_desc = all_rtp_conn_rate_ctr_desc
 };
 
+static const struct rate_ctr_group_desc all_osmux_conn_rate_ctr_group_desc = {
+	.group_name_prefix = "all_osmux_conn",
+	.group_description = "aggregated statistics for all osmux connections",
+	.class_id = 1,
+	.num_ctr = ARRAY_SIZE(all_osmux_conn_rate_ctr_desc),
+	.ctr_desc = all_rtp_conn_rate_ctr_desc
+};
+
 /*! allocate global rate counters
  *  (called once at startup).
  *  \param[in] cfg mgw configuration for which the rate counters are allocated.
@@ -191,6 +199,7 @@
 	static atomic_uint mdcx_rate_ctr_index = 0;
 	static atomic_uint dlcx_rate_ctr_index = 0;
 	static atomic_uint all_rtp_conn_rate_ctr_index = 0;
+	static atomic_uint all_osmux_conn_rate_ctr_index = 0;
 	char ctr_name[256];
 
 	if (ratectr->mgcp_crcx_ctr_group == NULL) {
@@ -229,6 +238,15 @@
 			 trunk->trunk_nr);
 		rate_ctr_group_set_name(ratectr->all_rtp_conn_stats, ctr_name);
 	}
+	if (ratectr->all_osmux_conn_stats == NULL) {
+		ratectr->all_osmux_conn_stats = rate_ctr_group_alloc(trunk, &all_osmux_conn_rate_ctr_group_desc,
+								   all_osmux_conn_rate_ctr_index++);
+		if (!ratectr->all_osmux_conn_stats)
+			return -EINVAL;
+		snprintf(ctr_name, sizeof(ctr_name), "%s-%u:osmux_conn", mgcp_trunk_type_strs_str(trunk->trunk_type),
+			 trunk->trunk_nr);
+		rate_ctr_group_set_name(ratectr->all_osmux_conn_stats, ctr_name);
+	}
 
 	/* E1 specific */
 	if (trunk->trunk_type == MGCP_TRUNK_E1 && ratectr->e1_stats == NULL) {
@@ -265,6 +283,10 @@
 		rate_ctr_group_free(ratectr->all_rtp_conn_stats);
 		ratectr->all_rtp_conn_stats = NULL;
 	}
+	if (ratectr->all_osmux_conn_stats) {
+		rate_ctr_group_free(ratectr->all_osmux_conn_stats);
+		ratectr->all_osmux_conn_stats = NULL;
+	}
 
 	/* E1 specific */
 	if (ratectr->e1_stats) {
diff --git a/src/libosmo-mgcp/mgcp_stat.c b/src/libosmo-mgcp/mgcp_stat.c
index d134526..e63feac 100644
--- a/src/libosmo-mgcp/mgcp_stat.c
+++ b/src/libosmo-mgcp/mgcp_stat.c
@@ -112,9 +112,12 @@
 		str_len -= nchars;
 
 		if (conn->osmux.state == OSMUX_STATE_ENABLED) {
+			struct rate_ctr *osmux_chunks_rx, *osmux_octets_rx;
+			osmux_chunks_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_CHUNKS_RX_CTR);
+			osmux_octets_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_OCTETS_RX_CTR);
 			snprintf(str, str_len,
-				 "\r\nX-Osmux-ST: CR=%u, BR=%u",
-				 conn->osmux.stats.chunks, conn->osmux.stats.octets);
+				 "\r\nX-Osmux-ST: CR=%" PRIu64 ", BR=%" PRIu64,
+				 osmux_chunks_rx->current, osmux_octets_rx->current);
 		}
 	}
 
diff --git a/src/libosmo-mgcp/mgcp_vty.c b/src/libosmo-mgcp/mgcp_vty.c
index 6183867..5abbf0e 100644
--- a/src/libosmo-mgcp/mgcp_vty.c
+++ b/src/libosmo-mgcp/mgcp_vty.c
@@ -289,6 +289,14 @@
 					   "   %25n: %10c (%S/s %M/m %H/h %D/d) %d",
 					   ratectr->all_rtp_conn_stats);
 	}
+	if (ratectr->all_osmux_conn_stats) {
+		vty_out(vty, "   %s:%s",
+			ratectr->all_osmux_conn_stats->desc->group_description,
+			VTY_NEWLINE);
+		vty_out_rate_ctr_group_fmt(vty,
+					   "   %25n: %10c (%S/s %M/m %H/h %D/d) %d",
+					   ratectr->all_osmux_conn_stats);
+	}
 
 	if (ratectr->e1_stats && trunk->trunk_type == MGCP_TRUNK_E1) {
 		vty_out(vty, "   %s:%s",