[PATCH 11/11] CCID2: Add network profiling code for monitoring state

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add "profiling" code to CCID2.  It is now possible to monitor the state of CCID2
from user-land.  Currently, the information which is available is:
* cwnd
* pipe
* srtt
* packets / second.

If you are interested in a userland tool for collecting such data let me know.

Signed-off-by: Andrea Bittau <a.bittau@xxxxxxxxxxxx>

---

diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c
index b6f6681..50da0f5 100644
--- a/net/dccp/ccids/ccid2.c
+++ b/net/dccp/ccids/ccid2.c
@@ -35,6 +35,7 @@ #include "../dccp.h"
 #include "ccid2.h"
 
 static int ccid2_debug;
+static int ccid2_profile;
 
 #ifdef CONFIG_IP_DCCP_CCID2_DEBUG
 #define ccid2_pr_debug(format, a...) \
@@ -90,6 +91,131 @@ #else
 #define ccid2_hc_tx_check_sanity(hctx) do {} while (0)
 #endif
 
+static int ccid2_pv_init(struct ccid2_profvar *pv, int type, int count)
+{
+	pv->ccid2pv_size = count;
+	pv->ccid2pv_pos = 0;
+	pv->ccid2pv_overflow = 0;
+	pv->ccid2pv_type = type;
+
+	pv->ccid2pv_data = kmalloc(sizeof(*pv->ccid2pv_data) * pv->ccid2pv_size,
+				   gfp_any());
+	if (!pv->ccid2pv_data)
+		return -ENOMEM;
+	
+	return 0;
+}
+
+static void ccid2_pv_del(struct ccid2_profvar *pv)
+{
+	if (pv->ccid2pv_data) {
+		kfree(pv->ccid2pv_data);
+		pv->ccid2pv_data = 0;
+	}
+}
+
+static u64 ccid2_profile_time(void)
+{
+#if defined(__i386__)
+	u32 l, h;
+	u64 t;
+
+	rdtsc(l, h);
+
+	t = h;
+	t <<= 32;
+	t |= l;
+
+	return t;
+#else
+	return jiffies;
+#endif	
+}
+
+static void ccid2_pv_add(struct ccid2_profvar *pv, long val)
+{
+	struct ccid2_profdata *pd = &pv->ccid2pv_data[pv->ccid2pv_pos];
+
+	pd->ccid2pd_time = ccid2_profile_time();
+	pd->ccid2pd_val = val;
+
+	pv->ccid2pv_pos++;
+	if (pv->ccid2pv_pos >= pv->ccid2pv_size) {
+		pv->ccid2pv_pos = 0;
+
+		/* strictly speaking, there is no overflow until the next write
+		 * happens, but for ease of programming, we set the bit here.
+		 */
+		pv->ccid2pv_overflow = 1;
+	}
+}
+
+static int ccid2_pv_copy_to_user(struct ccid2_profvar *pv, void __user *buf,
+		  	         int copylen)
+{
+	int totlen;
+	int rc;
+	unsigned char over = pv->ccid2pv_overflow;
+	unsigned char __user *bufp = buf;
+	struct ccid2_pdhdr hdr;
+
+	totlen = sizeof(hdr);
+
+	/* If we overflowed, the whole buffer has new data */
+	if (over)
+		totlen += pv->ccid2pv_size * sizeof(*pv->ccid2pv_data);
+	else
+		totlen += pv->ccid2pv_pos  * sizeof(*pv->ccid2pv_data);
+
+	if (copylen > totlen)
+		copylen = totlen;
+
+	if (copylen < sizeof(hdr))
+		return -EINVAL;
+	totlen = copylen;
+
+	/* header */
+	copylen -= sizeof(hdr);
+	hdr.ccid2pdh_overflow = over;
+	hdr.ccid2pdh_type = pv->ccid2pv_type;
+	hdr.ccid2pdh_len = copylen;
+	if ((rc = copy_to_user(bufp, &hdr, sizeof(hdr))))
+		return rc;
+	bufp += sizeof(hdr);
+
+	/* copy data */
+	if (over) {
+		struct ccid2_profdata *pd;
+		int rem = pv->ccid2pv_size - pv->ccid2pv_pos;
+
+		/* copy tail of buffer first */
+		pd = &pv->ccid2pv_data[pv->ccid2pv_pos];
+		while (rem--) {
+			if (copylen < sizeof(*pd))
+				break;
+			
+			rc = copy_to_user(bufp, pd, sizeof(*pd));
+			if (rc)
+				return rc;
+
+			bufp += sizeof(*pd);
+			copylen -= sizeof(*pd);
+			pd++;
+		}
+	}
+	
+	rc = copy_to_user(bufp, pv->ccid2pv_data, copylen);
+	if (rc)
+		return rc;
+
+	/* reset buffer */
+	/* XXX assumes that the user supplied a buffer large enough */
+	pv->ccid2pv_overflow = 0;
+	pv->ccid2pv_pos = 0;
+
+	return totlen;
+}
+
 static int ccid2_hc_tx_alloc_seq(struct ccid2_hc_tx_sock *hctx, int num)
 {
 	struct ccid2_seq *seqp;
@@ -187,6 +313,13 @@ static void ccid2_change_l_ack_ratio(str
 	dp->dccps_l_ack_ratio = val;
 }
 
+static inline void ccid2_profile_var(struct ccid2_hc_tx_sock *hctx, int var,
+				     long val)
+{
+	if (ccid2_profile)
+		ccid2_pv_add(&hctx->ccid2hctx_profile.ccid2txp_vars[var], val);
+}
+
 static void ccid2_change_cwnd(struct ccid2_hc_tx_sock *hctx, int val)
 {
 	if (val == 0)
@@ -197,6 +330,7 @@ static void ccid2_change_cwnd(struct cci
 
 	BUG_ON(val < 1);
 	hctx->ccid2hctx_cwnd = val;
+	ccid2_profile_var(hctx, CCID2_PROF_CWND, hctx->ccid2hctx_cwnd);
 }
 
 static void ccid2_change_srtt(struct ccid2_hc_tx_sock *hctx, long val)
@@ -207,10 +341,17 @@ static void ccid2_change_srtt(struct cci
 		return;
  
 	hctx->ccid2hctx_srtt = val;
+	ccid2_profile_var(hctx, CCID2_PROF_SRTT, hctx->ccid2hctx_srtt);
 }
  
 static void ccid2_change_pipe(struct ccid2_hc_tx_sock *hctx, long val)
 {
+	/* Don't log all pipe changes---too much stuff */
+	if (abs(hctx->ccid2hctx_profile.ccid2txp_lpipe - val) > 2) {
+		ccid2_profile_var(hctx, CCID2_PROF_PIPE, val);
+		hctx->ccid2hctx_profile.ccid2txp_lpipe = val;
+	}
+	
 	hctx->ccid2hctx_pipe = val;
 }
 
@@ -287,6 +428,22 @@ static void ccid2_hc_tx_packet_sent(stru
 
 	ccid2_hc_tx_check_sanity(hctx);
 
+	if (ccid2_profile) {
+		unsigned long diff;
+ 
+		hctx->ccid2hctx_profile.ccid2txp_packets++;
+		diff = (long)jiffies -
+		       (long)hctx->ccid2hctx_profile.ccid2txp_packets_last;
+ 
+		if (diff >= HZ) {
+			ccid2_profile_var(hctx, CCID2_PROF_PS,
+					  (hctx->ccid2hctx_profile.
+					   ccid2txp_packets/diff)*HZ);
+			hctx->ccid2hctx_profile.ccid2txp_packets = 0;
+			hctx->ccid2hctx_profile.ccid2txp_packets_last = jiffies;
+		}
+	}
+
 	BUG_ON(!hctx->ccid2hctx_sendwait);
 	hctx->ccid2hctx_sendwait = 0;
 	ccid2_change_pipe(hctx, hctx->ccid2hctx_pipe+1);
@@ -754,6 +911,26 @@ static int ccid2_hc_tx_init(struct ccid 
 {
         struct ccid2_hc_tx_sock *hctx = ccid_priv(ccid);
 
+	if (ccid2_profile) {
+		int rc;
+		int i;
+ 
+		for (i = 0; i < CCID2_PROF_LAST; i++) {
+			rc = ccid2_pv_init(&hctx->ccid2hctx_profile.
+					   ccid2txp_vars[i], i, 6000);
+			if (rc) {
+				while (--i > 0)
+					ccid2_pv_del(&hctx->ccid2hctx_profile.
+						     ccid2txp_vars[i]);
+				return rc;
+			}
+		}
+ 
+		hctx->ccid2hctx_profile.ccid2txp_lpipe = 0;
+		hctx->ccid2hctx_profile.ccid2txp_packets = 0;
+		hctx->ccid2hctx_profile.ccid2txp_packets_last = jiffies;
+	}
+
 	ccid2_change_cwnd(hctx, 1);
 	hctx->ccid2hctx_ssthresh  = 666666; /* "infinite" [for a while] */
 	hctx->ccid2hctx_numdupack = 3;
@@ -789,6 +966,13 @@ static void ccid2_hc_tx_exit(struct sock
 	for (i = 0; i < hctx->ccid2hctx_seqbufc; i++)
 		kfree(hctx->ccid2hctx_seqbuf[i]);
 	hctx->ccid2hctx_seqbufc = 0;
+
+	if (ccid2_profile) {
+		int i;
+ 
+		for (i = 0; i < CCID2_PROF_LAST; i++)
+			ccid2_pv_del(&hctx->ccid2hctx_profile.ccid2txp_vars[i]);
+	}
 }
 
 static void ccid2_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb)
@@ -808,6 +992,58 @@ static void ccid2_hc_rx_packet_recv(stru
 	}
 }
 
+static int ccid2_hc_rx_getsockopt(struct sock *sk, const int optname, int len,
+				  u32 __user *optval, int __user *optlen)
+{
+	return -ENOPROTOOPT;
+}
+
+static int ccid2_hc_tx_profile(struct sock *sk, u32 __user *optval,
+			       int __user *optlen)
+{
+	struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+	int rem;
+	int rc;
+	int copied = 0;
+	int i;
+	unsigned char __user *buf = (unsigned char *) optval;
+ 
+	rc = get_user(rem, optlen);
+	if (rc)
+		return rc;
+ 
+	for (i = 0; i < CCID2_PROF_LAST; i++) {
+		rc = ccid2_pv_copy_to_user(&hctx->ccid2hctx_profile.
+					   ccid2txp_vars[i], buf, rem);
+		if (rc < 0)
+			return rc;
+		
+		rem -= rc;
+		copied += rc;
+		buf += rc;
+		if (rem <= 0)
+			break;
+	}
+ 
+	rc = put_user(copied, optlen);
+	if (rc)
+		return rc;
+	
+	return 0;
+}
+
+static int ccid2_hc_tx_getsockopt(struct sock *sk, const int optname, int len,
+				  u32 __user *optval, int __user *optlen)
+{
+	switch (optname) {
+	case DCCP_SOCKOPT_CCID_TX_INFO:
+		if (ccid2_profile)
+			return ccid2_hc_tx_profile(sk, optval, optlen);
+	}
+	
+	return -ENOPROTOOPT;
+}
+
 static struct ccid_operations ccid2 = {
 	.ccid_id		= 2,
 	.ccid_name		= "ccid2",
@@ -820,11 +1056,16 @@ static struct ccid_operations ccid2 = {
 	.ccid_hc_tx_packet_recv	= ccid2_hc_tx_packet_recv,
 	.ccid_hc_rx_obj_size	= sizeof(struct ccid2_hc_rx_sock),
 	.ccid_hc_rx_packet_recv	= ccid2_hc_rx_packet_recv,
+	.ccid_hc_rx_getsockopt	= ccid2_hc_rx_getsockopt,
+	.ccid_hc_tx_getsockopt	= ccid2_hc_tx_getsockopt,
 };
 
 module_param(ccid2_debug, int, 0444);
 MODULE_PARM_DESC(ccid2_debug, "Enable debug messages");
 
+module_param(ccid2_profile, int, 0444);
+MODULE_PARM_DESC(ccid2_profile, "Enable profiling");
+
 static __init int ccid2_module_init(void)
 {
 	return ccid_register(&ccid2);
diff --git a/net/dccp/ccids/ccid2.h b/net/dccp/ccids/ccid2.h
index 3bc4b76..d208657 100644
--- a/net/dccp/ccids/ccid2.h
+++ b/net/dccp/ccids/ccid2.h
@@ -35,6 +35,41 @@ struct ccid2_seq {
 	struct ccid2_seq	*ccid2s_next;
 };
 
+struct ccid2_pdhdr {
+	u8	ccid2pdh_overflow;
+	u8	ccid2pdh_type;
+	int	ccid2pdh_len;
+};
+ 
+struct ccid2_profdata {
+	u64	ccid2pd_time;
+	long	ccid2pd_val;
+};
+ 
+enum {
+	CCID2_PROF_CWND = 0,
+	CCID2_PROF_SRTT,
+	CCID2_PROF_PIPE,
+	CCID2_PROF_PS,
+ 
+	CCID2_PROF_LAST
+};
+ 
+struct ccid2_profvar {
+	struct ccid2_profdata	*ccid2pv_data;
+	int			ccid2pv_size;
+	int			ccid2pv_pos;
+	int			ccid2pv_overflow;
+	int			ccid2pv_type;
+};
+ 
+struct ccid2_txprofile {
+	struct ccid2_profvar	ccid2txp_vars[CCID2_PROF_LAST];
+	long			ccid2txp_lpipe;
+	long			ccid2txp_packets;
+	unsigned long		ccid2txp_packets_last;
+};
+
 #define CCID2_SEQBUF_LEN 256
 #define CCID2_SEQBUF_MAX 128
 
@@ -72,6 +107,7 @@ struct ccid2_hc_tx_sock {
 	int			ccid2hctx_rpdupack;
 	int			ccid2hctx_sendwait;
 	unsigned long		ccid2hctx_last_cong;
+	struct ccid2_txprofile	ccid2hctx_profile;
 };
 
 struct ccid2_hc_rx_sock {
-
: send the line "unsubscribe dccp" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel]     [IETF DCCP]     [Linux Networking]     [Git]     [Security]     [Linux Assembly]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]

  Powered by Linux