commit 822aece80371a6850411fa206a690d691df032de Author: Daniel Turull Date: Thu Aug 26 16:19:46 2010 +0200 addition of all the statistics except configuration packet diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 3d84935..871002a 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -172,6 +172,7 @@ #include #include #include /* do_div */ +#include "kmap_skb.h" #define VERSION "2.75" #define IP_NAME_SZ 32 @@ -389,12 +390,15 @@ struct pktgen_dev { #endif char result[512]; }; +#define ntohll(x) (((s64)(ntohl((int)((x << 32) >> 32))) << 32) |\ + (unsigned int)ntohl(((int)(x >> 32)))) + +#define htonll(x) ntohll(x) struct pktgen_hdr { __be32 pgh_magic; __be32 seq_num; - __be32 tv_sec; - __be32 tv_usec; + __s64 time; }; struct pktgen_thread { @@ -414,6 +418,19 @@ struct pktgen_thread { struct completion start_done; }; +#define RX_COUNTER 1 +#define RX_BASIC 2 +#define RX_TIME 3 +#define PG_DISPLAY_HUMAN 0 +#define PG_DISPLAY_SCRIPT 1 + +struct pktgen_stats { + u64 sum; + u64 square_sum; + u64 samples; + u64 min; + u64 max; +}; /*Recevier parameters per cpu*/ struct pktgen_rx { u64 rx_packets; /*packets arrived*/ @@ -421,6 +438,23 @@ struct pktgen_rx { ktime_t start_time; /*first time stamp of a packet*/ ktime_t last_time; /*last packet arrival */ + + /*inter-arrival variables*/ + struct pktgen_stats inter_arrival; + ktime_t last_time_ktime; + u64 inter_arrival_last; + + struct pktgen_stats jitter; + + struct pktgen_stats latency; + ktime_t latency_last_tx; +}; + +struct pktgen_rx_global { + u8 stats_option; /* Counter, basic, time*/ + u8 display_option; /* Text or no text*/ + u64 pkts_to_send; /* Received in the config pkt*/ + u64 bytes_to_send; /* Received in the config pkt*/ }; #define REMOVE 1 @@ -428,10 +462,7 @@ struct pktgen_rx { static inline ktime_t ktime_now(void) { - struct timespec ts; - ktime_get_ts(&ts); - - return timespec_to_ktime(ts); + return ktime_get(); } /* This works even if 32 bit because of careful byte order choice */ @@ -459,7 +490,13 @@ static void pktgen_clear_counters(struct pktgen_dev *pkt_dev); /*Receiver functions*/ static int pktgen_rcv_basic(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev); +static int pktgen_rcv_time(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev); +static int pktgen_rcv_counter(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev); static int pktgen_add_rx(const char *ifname); +static int pktgen_set_statistics(const char *f); +static int pktgen_set_display(const char *f); static int pktgen_clean_rx(void); static void pg_reset_rx(void); @@ -476,7 +513,9 @@ static DEFINE_MUTEX(pktgen_thread_lock); static LIST_HEAD(pktgen_threads); DEFINE_PER_CPU(struct pktgen_rx, pktgen_rx_data); +static struct pktgen_rx_global *pg_rx_global; static int pg_initialized; +static int is_pktgen_sending; static struct notifier_block pktgen_notifier_block = { .notifier_call = pktgen_device_event, @@ -1894,37 +1933,98 @@ static const struct file_operations pktgen_thread_fops = { .release = single_release, }; + +#define DISPLAY_RX(opt, seq, fmt, fmt1, args...) \ + do { \ + if (opt == PG_DISPLAY_HUMAN) \ + seq_printf(seq, fmt1 , ## args);\ + else \ + seq_printf(seq, fmt, ## args); \ + } while (0) + +static void show_stats(int option, struct seq_file *seq, char *name, + struct pktgen_stats *stats) +{ + u64 average_ns = 0, var_ns2 = 0; + if (stats->samples < 1) + return; + average_ns = stats->sum/stats->samples; + var_ns2 = (stats->square_sum/stats->samples)-(average_ns*average_ns); + if (option == PG_DISPLAY_HUMAN) + seq_printf(seq, "\t%s\n", name); + + DISPLAY_RX(option, seq, "%llu %llu ", + "\t\tAverage: %llu ns Variance %llu ns2\n", + average_ns, var_ns2); + DISPLAY_RX(option, seq, "%llu %llu ", + "\t\tMax: %llu ns Min:: %llu ns\n", + stats->max, stats->min); + DISPLAY_RX(option, seq, "%llu ", + "\t\tSamples: %llu\n", + stats->samples); +} + +static void show_bw(int option, struct seq_file *seq, ktime_t start, + ktime_t stop, u64 packets, u64 bytes) +{ + u64 work_time_us = 0; + __u64 bps, mbps, pps; + work_time_us = ktime_to_us(ktime_sub(stop, start)); + + if (!work_time_us) { + if (option == PG_DISPLAY_SCRIPT) + seq_puts(seq, "0 0 0 0 "); + return; + } + + bps = div64_u64(bytes*8*USEC_PER_SEC, work_time_us); + mbps = bps; + do_div(mbps, 100000); + pps = div64_u64(packets * USEC_PER_SEC, work_time_us); + + DISPLAY_RX(option, seq, "%llu ", "\tWork time %llu us\n", + work_time_us); + DISPLAY_RX(option, seq, "%llu %llu %llu ", + "\tRate: %llupps %lluMb/sec (%llubps)\n", + (unsigned long long)pps, + (unsigned long long)mbps, + (unsigned long long)bps); +} + /* * Function that show Receiver statistics */ static int pgrx_show(struct seq_file *seq, void *v) { struct pktgen_rx *data_cpu; - __u64 bps, mbps, pps; int cpu; - u64 total_packets = 0, total_bytes = 0, work_time_us = 0; + int option = PG_DISPLAY_HUMAN; + u64 total_packets = 0, total_bytes = 0; u64 packets = 0, bytes = 0; ktime_t start_global, stop_global, tmp; start_global.tv64 = 0; stop_global.tv64 = 0; - - seq_puts(seq, "\t\tRECEPTION STATISTICS\n"); - if (pg_initialized == 0) { + if (!pg_initialized) { seq_puts(seq, "Not enabled.\n"); return 0; } - seq_puts(seq, "\tPER-CPU Stats.\n"); + option = pg_rx_global->display_option; + if (option == PG_DISPLAY_HUMAN) + seq_puts(seq, "\t\tRECEPTION STATISTICS\n"); + if (option == PG_DISPLAY_HUMAN) + seq_puts(seq, "\tPER-CPU Stats\n"); for_each_online_cpu(cpu) { data_cpu = &per_cpu(pktgen_rx_data, cpu); - seq_printf(seq, "CPU %d: ", cpu); + DISPLAY_RX(option, seq, "%d ", "CPU %d:", cpu); packets = data_cpu->rx_packets; bytes = data_cpu->rx_bytes; total_packets += packets; total_bytes += bytes; - seq_printf(seq, "\tRx packets: %llu\t Rx bytes: %llu\n", + DISPLAY_RX(option, seq, "%llu %llu ", + "\tRx packets: %llu\t Rx bytes: %llu\n", packets, bytes); tmp = data_cpu->start_time; @@ -1937,54 +2037,45 @@ static int pgrx_show(struct seq_file *seq, void *v) if (ktime_to_ns(tmp) > ktime_to_ns(stop_global)) stop_global = tmp; - work_time_us = ktime_to_us(ktime_sub(data_cpu->last_time, - data_cpu->start_time)); - - if (!work_time_us) - continue; - - bps = div64_u64(bytes*8*USEC_PER_SEC, work_time_us); - mbps = bps; - do_div(mbps, 1000000); - pps = div64_u64(packets * USEC_PER_SEC, work_time_us); - - seq_printf(seq, "\tRate: %llupps %llu Mb/sec (%llubps)\n", - (unsigned long long)pps, - (unsigned long long)mbps, - (unsigned long long)bps); - seq_printf(seq, "\tWork time %llu us\n", work_time_us); + show_bw(option, seq, data_cpu->start_time, data_cpu->last_time, + packets, bytes); + show_stats(option, seq, "Inter-arrival", + &data_cpu->inter_arrival); + show_stats(option, seq, "Jitter", + &data_cpu->jitter); + show_stats(option, seq, "Latency", + &data_cpu->latency); + if (option == PG_DISPLAY_SCRIPT) + seq_puts(seq, "\n"); } - seq_puts(seq, "\n\tGlobal Statistics\n"); + DISPLAY_RX(option, seq, "G ", "\n\tGlobal Statistics\n"); - seq_printf(seq, "Packets Rx: %llu\t Bytes Rx: %llu\n", + DISPLAY_RX(option, seq, "%llu %llu ", + "Packets Rx: %llu\t Bytes Rx: %llu\n", (unsigned long long) total_packets, (unsigned long long) total_bytes); - + if (pg_rx_global->pkts_to_send != 0) { + u64 lost_pkt; + s64 lost_bytes; + + DISPLAY_RX(option, seq, "%llu %llu ", + "Packets Ex: %llu\t Bytes Ex: %llu\n", + (unsigned long long) pg_rx_global->pkts_to_send, + (unsigned long long) pg_rx_global->bytes_to_send); + lost_pkt = pg_rx_global->pkts_to_send-total_packets; + lost_bytes = pg_rx_global->bytes_to_send-total_bytes; + DISPLAY_RX(option, seq, "%llu %llu ", + "Packets Lost: %llu\t Bytes Lost: %llu\n", + (unsigned long long) lost_pkt, + (signed long long) lost_bytes); + } /*Bandwidth*/ - work_time_us = ktime_to_us(ktime_sub(stop_global, start_global)); - - seq_printf(seq, "Start: %llu us\t Stop: %llu us\t Work time %llu us\n", - ktime_to_us(start_global), - ktime_to_us(stop_global), - work_time_us); - - if (!work_time_us) - return 0; - - bps = div64_u64(total_bytes*8*USEC_PER_SEC, work_time_us); - mbps = bps; - do_div(mbps, 1000000); - pps = div64_u64(total_packets * USEC_PER_SEC, work_time_us); - - seq_puts(seq, "Received throughput:\n"); - - seq_printf(seq, " %llupps %llu Mb/sec (%llubps)\n", - (unsigned long long)pps, - (unsigned long long)mbps, - (unsigned long long)bps); - + show_bw(option, seq, start_global, stop_global, total_packets, + total_bytes); + if (option == PG_DISPLAY_SCRIPT) + seq_puts(seq, "\n"); return 0; } /*receiver configuration*/ @@ -2049,6 +2140,39 @@ static ssize_t pgrx_write(struct file *file, const char __user * user_buffer, if (debug) printk(KERN_INFO "pktgen: Reseting reception\n"); goto out; + } else if (!strcmp(name, "statistics")) { + char f[32]; + memset(f, 0, 32); + len = strn_len(&user_buffer[i], sizeof(f) - 1); + if (len < 0) { + ret = len; + goto out; + } + if (copy_from_user(f, &user_buffer[i], len)) + return -EFAULT; + i += len; + if (debug) + printk(KERN_INFO "Setting statistics to %s\n", f); + pktgen_set_statistics(f); + ret = count; + goto out; + } else if (!strcmp(name, "display")) { + char f[32]; + memset(f, 0, 32); + len = strn_len(&user_buffer[i], sizeof(f) - 1); + if (len < 0) { + ret = len; + goto out; + } + if (copy_from_user(f, &user_buffer[i], len)) + return -EFAULT; + i += len; + + if (debug) + printk(KERN_INFO "Setting display to %s\n", f); + pktgen_set_display(f); + ret = count; + goto out; } else if (!strcmp(name, "rx_disable")) { ret = count; pktgen_clean_rx(); @@ -2987,8 +3111,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, pgh->seq_num = htonl(pkt_dev->seq_num); do_gettimeofday(×tamp); - pgh->tv_sec = htonl(timestamp.tv_sec); - pgh->tv_usec = htonl(timestamp.tv_usec); + pgh->time = htonll(ktime_to_ns(ktime_get())); } #ifdef CONFIG_XFRM @@ -3331,16 +3454,11 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, * should we update cloned packets too ? */ if (pgh) { - struct timeval timestamp; - pgh->pgh_magic = htonl(PKTGEN_MAGIC); pgh->seq_num = htonl(pkt_dev->seq_num); - do_gettimeofday(×tamp); - pgh->tv_sec = htonl(timestamp.tv_sec); - pgh->tv_usec = htonl(timestamp.tv_usec); + pgh->time = htonl(ktime_to_ns(ktime_now())); } - /* pkt_dev->seq_num++; FF: you really mean this? */ return skb; } @@ -3460,6 +3578,7 @@ static int pktgen_wait_all_threads_run(void) list_for_each_entry(t, &pktgen_threads, th_list) t->control |= (T_STOP); + is_pktgen_sending = 0; mutex_unlock(&pktgen_thread_lock); return sig; } @@ -3470,6 +3589,7 @@ static void pktgen_run_all_threads(void) func_enter(); + is_pktgen_sending = 1; mutex_lock(&pktgen_thread_lock); list_for_each_entry(t, &pktgen_threads, th_list) @@ -3697,6 +3817,9 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) return; } + if (pkt_dev->delay && pkt_dev->last_ok) + spin(pkt_dev, pkt_dev->next_tx); + /* If no skb or clone count exhausted then get new one */ if (!pkt_dev->skb || (pkt_dev->last_ok && ++pkt_dev->clone_count >= pkt_dev->clone_skb)) { @@ -3715,9 +3838,6 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) pkt_dev->clone_count = 0; /* reset counter */ } - if (pkt_dev->delay && pkt_dev->last_ok) - spin(pkt_dev, pkt_dev->next_tx); - queue_map = skb_get_queue_mapping(pkt_dev->skb); txq = netdev_get_tx_queue(odev, queue_map); @@ -4073,8 +4193,15 @@ static int pktgen_remove_device(struct pktgen_thread *t, kfree(pkt_dev); return 0; } - -static void pg_reset_rx(void) +void pg_init_stats(struct pktgen_stats *stats) +{ + stats->sum = 0; + stats->square_sum = 0; + stats->min = ULLONG_MAX; + stats->max = 0; + stats->samples = 0; +} +void pg_reset_rx(void) { int cpu; for_each_online_cpu(cpu) { @@ -4082,6 +4209,16 @@ static void pg_reset_rx(void) per_cpu(pktgen_rx_data, cpu).rx_bytes = 0; per_cpu(pktgen_rx_data, cpu).last_time.tv64 = 0; per_cpu(pktgen_rx_data, cpu).start_time.tv64 = 0; + per_cpu(pktgen_rx_data, cpu).inter_arrival_last = 0; + per_cpu(pktgen_rx_data, cpu).last_time_ktime.tv64 = 0; + per_cpu(pktgen_rx_data, cpu).latency_last_tx.tv64 = 0; + pg_init_stats(&per_cpu(pktgen_rx_data, cpu).inter_arrival); + pg_init_stats(&per_cpu(pktgen_rx_data, cpu).jitter); + pg_init_stats(&per_cpu(pktgen_rx_data, cpu).latency); + } + if (pg_initialized) { + pg_rx_global->pkts_to_send = 0; + pg_rx_global->bytes_to_send = 0; } } @@ -4098,6 +4235,11 @@ static int pktgen_add_rx(const char *ifname) "pktgen: device not present %s. Using all\n", ifname); if (!pg_initialized) { + pg_rx_global = kmalloc(sizeof(struct pktgen_rx_global), + GFP_KERNEL); + pg_rx_global->stats_option = RX_BASIC; + pg_rx_global->display_option = PG_DISPLAY_HUMAN; + pktgen_packet_type.dev = idev; dev_add_pack(&pktgen_packet_type); err = 0; @@ -4114,44 +4256,236 @@ static int pktgen_add_rx(const char *ifname) return err; } +/*Function for select the type of statisitcs*/ +static int pktgen_set_statistics(const char *f) +{ + int ret = 0; + if (pg_rx_global == NULL) + return -ENOMEM; + + net_disable_timestamp(); + + dev_remove_pack(&pktgen_packet_type); + if (!strcmp(f, "counter")) { + pg_rx_global->stats_option = RX_COUNTER; + pktgen_packet_type.func = pktgen_rcv_counter; + ret = 0; + } else if (!strcmp(f, "basic")) { + pg_rx_global->stats_option = RX_BASIC; + pktgen_packet_type.func = pktgen_rcv_basic; + ret = 0; + } else if (!strcmp(f, "time")) { + pg_rx_global->stats_option = RX_TIME; + pktgen_packet_type.func = pktgen_rcv_time; + ret = 0; + } else + ret = -EINVAL; + + dev_add_pack(&pktgen_packet_type); + return ret; +} + +static int pktgen_set_display(const char *f) +{ + if (pg_rx_global == NULL) + return -ENOMEM; + if (!strcmp(f, "human")) { + pg_rx_global->display_option = PG_DISPLAY_HUMAN; + return 0; + } else if (!strcmp(f, "script")) { + pg_rx_global->display_option = PG_DISPLAY_SCRIPT; + return 0; + } else + return -EINVAL; +} + +/* + * Function for clean the statitics and disable the reception of packets + */ static int pktgen_clean_rx(void) { if (pg_initialized) { + kfree(pg_rx_global); dev_remove_pack(&pktgen_packet_type); pg_initialized = 0; } return 0; } +/* + * Function that gets the necessary data for througput calculation + */ +static inline int throughput_data(ktime_t now, struct pktgen_rx *data_cpu) +{ + if (unlikely(data_cpu->rx_packets == 0)) + data_cpu->start_time = now; + data_cpu->last_time = now; + + return 0; +} -int pktgen_rcv_basic(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) +void process_stats(u64 value, struct pktgen_stats *stats) +{ + stats->square_sum += value*value; + stats->sum += value; + + stats->samples++; + + if (value > stats->max) + stats->max = value; + if (value < stats->min) + stats->min = value; +} +/* + * Function to collect inter_arrival data +*/ +static int inter_arrival_ktime(ktime_t now, struct pktgen_rx *data_cpu) +{ + ktime_t last_time; + u64 inter_arrival = 0, inter_arrival_last = 0; + s64 jitter = 0; + + last_time = data_cpu->last_time_ktime; + if (last_time.tv64 == 0) { + data_cpu->last_time_ktime = now; + return 0; + } + + inter_arrival = ktime_to_ns(ktime_sub(now, last_time)); + process_stats(inter_arrival, + &data_cpu->inter_arrival); + data_cpu->last_time_ktime = now; + /* Jitter calculation*/ + inter_arrival_last = data_cpu->inter_arrival_last; + if (inter_arrival_last == 0) { + data_cpu->inter_arrival_last = inter_arrival; + return 0; + } + + jitter = inter_arrival_last - inter_arrival; + if (jitter < 0) + jitter = -jitter; + + process_stats(jitter, &data_cpu->jitter); + + + data_cpu->inter_arrival_last = inter_arrival; + + return 0; +} + +static int latency_calc(struct pktgen_hdr *pgh, ktime_t now, + struct pktgen_rx *data_cpu) +{ + u64 latency = 0; + ktime_t ktime_tx; + if (!is_pktgen_sending) + return 0; + ktime_tx.tv64 = ntohll(pgh->time); + + if (ktime_equal(ktime_tx, data_cpu->latency_last_tx)) + return 0; + latency = ktime_to_ns(ktime_sub(now, ktime_tx)); + process_stats(latency, &data_cpu->latency); + + data_cpu->latency_last_tx = ktime_tx; + return 0; +} + +/*Reception function*/ +static int pktgen_rcv_counter(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) { - /* Check magic*/ struct iphdr *iph = ip_hdr(skb); struct pktgen_hdr *pgh; + void *vaddr; struct pktgen_rx *data_cpu; - int pktgen_offset = iph->ihl*4 + sizeof(struct udphdr); + if (skb_is_nonlinear(skb)) { + vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[0]); + pgh = (struct pktgen_hdr *) + (vaddr+skb_shinfo(skb)->frags[0].page_offset); + } else { + pgh = (struct pktgen_hdr *)(((char *)(iph)) + 28); + } - if (!pskb_may_pull(skb, pktgen_offset + sizeof(struct pktgen_hdr))) + if (unlikely(pgh->pgh_magic != PKTGEN_MAGIC_NET)) goto end; - pgh = (struct pktgen_hdr *)(((char *)(iph)) + pktgen_offset); + data_cpu = &__get_cpu_var(pktgen_rx_data); + /* Update counter of packets*/ + data_cpu->rx_packets++; + data_cpu->rx_bytes += skb->len + ETH_HLEN; + +end: + if (skb_is_nonlinear(skb)) + kunmap_skb_frag(vaddr); + kfree_skb(skb); + return 0; +} +static int pktgen_rcv_time(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + /*check magic*/ + struct iphdr *iph = ip_hdr(skb); + struct pktgen_hdr *pgh; + void *vaddr; + struct pktgen_rx *data_cpu; + ktime_t now = ktime_get(); + if (skb_is_nonlinear(skb)) { + vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[0]); + pgh = (struct pktgen_hdr *) + (vaddr+skb_shinfo(skb)->frags[0].page_offset); + } else { + pgh = (struct pktgen_hdr *)(((char *)(iph)) + 28); + } if (unlikely(pgh->pgh_magic != PKTGEN_MAGIC_NET)) goto end; data_cpu = &__get_cpu_var(pktgen_rx_data); - if (unlikely(!data_cpu->rx_packets)) - data_cpu->start_time = ktime_now(); + inter_arrival_ktime(now, data_cpu); - data_cpu->last_time = ktime_now(); + latency_calc(pgh, now, data_cpu); + throughput_data(now, data_cpu); /* Update counter of packets*/ data_cpu->rx_packets++; data_cpu->rx_bytes += skb->len + ETH_HLEN; +end: + if (skb_is_nonlinear(skb)) + kunmap_skb_frag(vaddr); + kfree_skb(skb); + return 0; +} +int pktgen_rcv_basic(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + /* Check magic*/ + struct iphdr *iph = ip_hdr(skb); + struct pktgen_hdr *pgh; + void *vaddr; + struct pktgen_rx *data_cpu; + if (skb_is_nonlinear(skb)) { + vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[0]); + pgh = (struct pktgen_hdr *) + (vaddr+skb_shinfo(skb)->frags[0].page_offset); + } else { + pgh = (struct pktgen_hdr *)(((char *)(iph)) + 28); + } + if (unlikely(pgh->pgh_magic != PKTGEN_MAGIC_NET)) + goto end; + + data_cpu = &__get_cpu_var(pktgen_rx_data); + + throughput_data(ktime_get(), data_cpu); + + /*update counter of packets*/ + data_cpu->rx_packets++; + data_cpu->rx_bytes += skb->len + ETH_HLEN; end: + if (skb_is_nonlinear(skb)) + kunmap_skb_frag(vaddr); kfree_skb(skb); return 0; }