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