static bool map_collect_percpu(int fd, __u32 key, struct record *rec) { /* For percpu maps, userspace gets a value per possible CPU */ unsigned int nr_cpus = bpf_num_possible_cpus(); struct datarec values[nr_cpus]; __u64 sum_processed = 0; __u64 sum_dropped = 0; __u64 sum_issue = 0; int i; if ((bpf_map_lookup_elem(fd, &key, values)) != 0) { fprintf(stderr, "ERR: bpf_map_lookup_elem failed key:0x%X\n", key); return false; } /* Get time as close as possible to reading map contents */ rec->timestamp = gettime(); /* Record and sum values from each CPU */ for (i = 0; i < nr_cpus; i++) { rec->cpu[i].processed = values[i].processed; sum_processed += values[i].processed; rec->cpu[i].dropped = values[i].dropped; sum_dropped += values[i].dropped; rec->cpu[i].issue = values[i].issue; sum_issue += values[i].issue; } rec->total.processed = sum_processed; rec->total.dropped = sum_dropped; rec->total.issue = sum_issue; return true; }
/* simple per-protocol drop counter */ static void poll_stats(unsigned int kill_after_s) { const unsigned int nr_protos = 256; unsigned int nr_cpus = bpf_num_possible_cpus(); time_t started_at = time(NULL); __u64 values[nr_cpus], prev[nr_protos][nr_cpus]; __u32 proto; int i; memset(prev, 0, sizeof(prev)); while (!kill_after_s || time(NULL) - started_at <= kill_after_s) { sleep(STATS_INTERVAL_S); for (proto = 0; proto < nr_protos; proto++) { __u64 sum = 0; assert(bpf_map_lookup_elem(map_fd[0], &proto, values) == 0); for (i = 0; i < nr_cpus; i++) sum += (values[i] - prev[proto][i]); if (sum) printf("proto %u: sum:%10llu pkts, rate:%10llu pkts/s\n", proto, sum, sum / STATS_INTERVAL_S); memcpy(prev[proto], values, sizeof(values)); } } }
static void print_hist(int fd) { unsigned int nr_cpus = bpf_num_possible_cpus(); __u64 total_events = 0; long values[nr_cpus]; __u64 max_cnt = 0; __u64 cnt[SLOTS]; __u64 value; __u32 key; int i; for (key = 0; key < SLOTS; key++) { bpf_map_lookup_elem(fd, &key, values); value = 0; for (i = 0; i < nr_cpus; i++) value += values[i]; cnt[key] = value; total_events += value; if (value > max_cnt) max_cnt = value; } clear_stats(fd); for (key = full_range ? 0 : 29; key < SLOTS; key++) { int c = num_colors * cnt[key] / (max_cnt + 1); if (text_only) printf("%s", sym[c]); else printf("%s %s", color[c], nocolor); } printf(" # %lld\n", total_events); }
int main(int argc, char **argv) { struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; int map_types[] = {BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH}; int map_flags[] = {0, BPF_F_NO_COMMON_LRU}; int t, f; setbuf(stdout, NULL); assert(!setrlimit(RLIMIT_MEMLOCK, &r)); nr_cpus = bpf_num_possible_cpus(); assert(nr_cpus != -1); printf("nr_cpus:%d\n\n", nr_cpus); for (f = 0; f < sizeof(map_flags) / sizeof(*map_flags); f++) { unsigned int tgt_free = (map_flags[f] & BPF_F_NO_COMMON_LRU) ? PERCPU_FREE_TARGET : LOCAL_FREE_TARGET; for (t = 0; t < sizeof(map_types) / sizeof(*map_types); t++) { test_lru_sanity0(map_types[t], map_flags[f]); test_lru_sanity1(map_types[t], map_flags[f], tgt_free); test_lru_sanity2(map_types[t], map_flags[f], tgt_free); test_lru_sanity3(map_types[t], map_flags[f], tgt_free); test_lru_sanity4(map_types[t], map_flags[f], tgt_free); test_lru_sanity5(map_types[t], map_flags[f]); printf("\n"); } } return 0; }
/* simple per-protocol drop counter */ static void poll_stats(int interval) { unsigned int nr_cpus = bpf_num_possible_cpus(); const unsigned int nr_keys = 256; __u64 values[nr_cpus], prev[nr_keys][nr_cpus]; __u32 key; int i; memset(prev, 0, sizeof(prev)); while (1) { sleep(interval); for (key = 0; key < nr_keys; key++) { __u64 sum = 0; assert(bpf_map_lookup_elem(map_fd[0], &key, values) == 0); for (i = 0; i < nr_cpus; i++) sum += (values[i] - prev[key][i]); if (sum) printf("proto %u: %10llu pkt/s\n", key, sum / interval); memcpy(prev[key], values, sizeof(values)); } } }
static void clear_stats(int fd) { unsigned int nr_cpus = bpf_num_possible_cpus(); __u64 values[nr_cpus]; __u32 key; memset(values, 0, sizeof(values)); for (key = 0; key < SLOTS; key++) bpf_map_update_elem(fd, &key, values, BPF_ANY); }
int main(int argc, char **argv) { unsigned int nr_cpus = bpf_num_possible_cpus(); const char *map_filename = "/sys/fs/bpf/tc/globals/lwt_len_hist_map"; uint64_t values[nr_cpus], sum, max_value = 0, data[MAX_INDEX] = {}; uint64_t key = 0, next_key, max_key = 0; char starstr[MAX_STARS]; int i, map_fd; map_fd = bpf_obj_get(map_filename); if (map_fd < 0) { fprintf(stderr, "bpf_obj_get(%s): %s(%d)\n", map_filename, strerror(errno), errno); return -1; } while (bpf_map_get_next_key(map_fd, &key, &next_key) == 0) { if (next_key >= MAX_INDEX) { fprintf(stderr, "Key %lu out of bounds\n", next_key); continue; } bpf_map_lookup_elem(map_fd, &next_key, values); sum = 0; for (i = 0; i < nr_cpus; i++) sum += values[i]; data[next_key] = sum; if (sum && next_key > max_key) max_key = next_key; if (sum > max_value) max_value = sum; key = next_key; } for (i = 1; i <= max_key + 1; i++) { stars(starstr, data[i - 1], max_value, MAX_STARS); printf("%8ld -> %-8ld : %-8ld |%-*s|\n", (1l << i) >> 1, (1l << i) - 1, data[i - 1], MAX_STARS, starstr); } close(map_fd); return 0; }
static struct datarec *alloc_record_per_cpu(void) { unsigned int nr_cpus = bpf_num_possible_cpus(); struct datarec *array; size_t size; size = sizeof(struct datarec) * nr_cpus; array = malloc(size); memset(array, 0, size); if (!array) { fprintf(stderr, "Mem alloc error (nr_cpus:%u)\n", nr_cpus); exit(EXIT_FAIL_MEM); } return array; }
static void *alloc_rec_per_cpu(int record_size) { unsigned int nr_cpus = bpf_num_possible_cpus(); void *array; size_t size; size = record_size * nr_cpus; array = malloc(size); memset(array, 0, size); if (!array) { fprintf(stderr, "Mem alloc error (nr_cpus:%u)\n", nr_cpus); exit(EXIT_FAIL_MEM); } return array; }
static __u64 get_key32_value64_percpu(int fd, __u32 key) { /* For percpu maps, userspace gets a value per possible CPU */ unsigned int nr_cpus = bpf_num_possible_cpus(); __u64 values[nr_cpus]; __u64 sum = 0; int i; if ((bpf_map_lookup_elem(fd, &key, values)) != 0) { fprintf(stderr, "ERR: bpf_map_lookup_elem failed key:0x%X\n", key); return 0; } /* Sum values from each CPU */ for (i = 0; i < nr_cpus; i++) { sum += values[i]; } return sum; }
static void poll_stats(int interval, int ifindex) { unsigned int nr_cpus = bpf_num_possible_cpus(); __u64 values[nr_cpus], prev[nr_cpus]; memset(prev, 0, sizeof(prev)); while (1) { __u64 sum = 0; __u32 key = 0; int i; sleep(interval); assert(bpf_map_lookup_elem(map_fd[1], &key, values) == 0); for (i = 0; i < nr_cpus; i++) sum += (values[i] - prev[i]); if (sum) printf("ifindex %i: %10llu pkt/s\n", ifindex, sum / interval); memcpy(prev, values, sizeof(values)); } }
static void stats_print(struct stats_record *stats_rec, struct stats_record *stats_prev, int prog_num) { unsigned int nr_cpus = bpf_num_possible_cpus(); double pps = 0, drop = 0, err = 0; struct record *rec, *prev; int to_cpu; double t; int i; /* Header */ printf("Running XDP/eBPF prog_num:%d\n", prog_num); printf("%-15s %-7s %-14s %-11s %-9s\n", "XDP-cpumap", "CPU:to", "pps", "drop-pps", "extra-info"); /* XDP rx_cnt */ { char *fmt_rx = "%-15s %-7d %'-14.0f %'-11.0f %'-10.0f %s\n"; char *fm2_rx = "%-15s %-7s %'-14.0f %'-11.0f\n"; char *errstr = ""; rec = &stats_rec->rx_cnt; prev = &stats_prev->rx_cnt; t = calc_period(rec, prev); for (i = 0; i < nr_cpus; i++) { struct datarec *r = &rec->cpu[i]; struct datarec *p = &prev->cpu[i]; pps = calc_pps(r, p, t); drop = calc_drop_pps(r, p, t); err = calc_errs_pps(r, p, t); if (err > 0) errstr = "cpu-dest/err"; if (pps > 0) printf(fmt_rx, "XDP-RX", i, pps, drop, err, errstr); } pps = calc_pps(&rec->total, &prev->total, t); drop = calc_drop_pps(&rec->total, &prev->total, t); err = calc_errs_pps(&rec->total, &prev->total, t); printf(fm2_rx, "XDP-RX", "total", pps, drop); } /* cpumap enqueue stats */ for (to_cpu = 0; to_cpu < MAX_CPUS; to_cpu++) { char *fmt = "%-15s %3d:%-3d %'-14.0f %'-11.0f %'-10.2f %s\n"; char *fm2 = "%-15s %3s:%-3d %'-14.0f %'-11.0f %'-10.2f %s\n"; char *errstr = ""; rec = &stats_rec->enq[to_cpu]; prev = &stats_prev->enq[to_cpu]; t = calc_period(rec, prev); for (i = 0; i < nr_cpus; i++) { struct datarec *r = &rec->cpu[i]; struct datarec *p = &prev->cpu[i]; pps = calc_pps(r, p, t); drop = calc_drop_pps(r, p, t); err = calc_errs_pps(r, p, t); if (err > 0) { errstr = "bulk-average"; err = pps / err; /* calc average bulk size */ } if (pps > 0) printf(fmt, "cpumap-enqueue", i, to_cpu, pps, drop, err, errstr); } pps = calc_pps(&rec->total, &prev->total, t); if (pps > 0) { drop = calc_drop_pps(&rec->total, &prev->total, t); err = calc_errs_pps(&rec->total, &prev->total, t); if (err > 0) { errstr = "bulk-average"; err = pps / err; /* calc average bulk size */ } printf(fm2, "cpumap-enqueue", "sum", to_cpu, pps, drop, err, errstr); } } /* cpumap kthread stats */ { char *fmt_k = "%-15s %-7d %'-14.0f %'-11.0f %'-10.0f %s\n"; char *fm2_k = "%-15s %-7s %'-14.0f %'-11.0f %'-10.0f %s\n"; char *e_str = ""; rec = &stats_rec->kthread; prev = &stats_prev->kthread; t = calc_period(rec, prev); for (i = 0; i < nr_cpus; i++) { struct datarec *r = &rec->cpu[i]; struct datarec *p = &prev->cpu[i]; pps = calc_pps(r, p, t); drop = calc_drop_pps(r, p, t); err = calc_errs_pps(r, p, t); if (err > 0) e_str = "sched"; if (pps > 0) printf(fmt_k, "cpumap_kthread", i, pps, drop, err, e_str); } pps = calc_pps(&rec->total, &prev->total, t); drop = calc_drop_pps(&rec->total, &prev->total, t); err = calc_errs_pps(&rec->total, &prev->total, t); if (err > 0) e_str = "sched-sum"; printf(fm2_k, "cpumap_kthread", "total", pps, drop, err, e_str); } /* XDP redirect err tracepoints (very unlikely) */ { char *fmt_err = "%-15s %-7d %'-14.0f %'-11.0f\n"; char *fm2_err = "%-15s %-7s %'-14.0f %'-11.0f\n"; rec = &stats_rec->redir_err; prev = &stats_prev->redir_err; t = calc_period(rec, prev); for (i = 0; i < nr_cpus; i++) { struct datarec *r = &rec->cpu[i]; struct datarec *p = &prev->cpu[i]; pps = calc_pps(r, p, t); drop = calc_drop_pps(r, p, t); if (pps > 0) printf(fmt_err, "redirect_err", i, pps, drop); } pps = calc_pps(&rec->total, &prev->total, t); drop = calc_drop_pps(&rec->total, &prev->total, t); printf(fm2_err, "redirect_err", "total", pps, drop); } /* XDP general exception tracepoints */ { char *fmt_err = "%-15s %-7d %'-14.0f %'-11.0f\n"; char *fm2_err = "%-15s %-7s %'-14.0f %'-11.0f\n"; rec = &stats_rec->exception; prev = &stats_prev->exception; t = calc_period(rec, prev); for (i = 0; i < nr_cpus; i++) { struct datarec *r = &rec->cpu[i]; struct datarec *p = &prev->cpu[i]; pps = calc_pps(r, p, t); drop = calc_drop_pps(r, p, t); if (pps > 0) printf(fmt_err, "xdp_exception", i, pps, drop); } pps = calc_pps(&rec->total, &prev->total, t); drop = calc_drop_pps(&rec->total, &prev->total, t); printf(fm2_err, "xdp_exception", "total", pps, drop); } printf("\n"); fflush(stdout); }
static void test_xdp(void) { struct vip key4 = {.protocol = 6, .family = AF_INET}; struct vip key6 = {.protocol = 6, .family = AF_INET6}; struct iptnl_info value4 = {.family = AF_INET}; struct iptnl_info value6 = {.family = AF_INET6}; const char *file = "./test_xdp.o"; struct bpf_object *obj; char buf[128]; struct ipv6hdr *iph6 = (void *)buf + sizeof(struct ethhdr); struct iphdr *iph = (void *)buf + sizeof(struct ethhdr); __u32 duration, retval, size; int err, prog_fd, map_fd; err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd); if (err) { error_cnt++; return; } map_fd = bpf_find_map(__func__, obj, "vip2tnl"); if (map_fd < 0) goto out; bpf_map_update_elem(map_fd, &key4, &value4, 0); bpf_map_update_elem(map_fd, &key6, &value6, 0); err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), buf, &size, &retval, &duration); CHECK(err || errno || retval != XDP_TX || size != 74 || iph->protocol != IPPROTO_IPIP, "ipv4", "err %d errno %d retval %d size %d\n", err, errno, retval, size); err = bpf_prog_test_run(prog_fd, 1, &pkt_v6, sizeof(pkt_v6), buf, &size, &retval, &duration); CHECK(err || errno || retval != XDP_TX || size != 114 || iph6->nexthdr != IPPROTO_IPV6, "ipv6", "err %d errno %d retval %d size %d\n", err, errno, retval, size); out: bpf_object__close(obj); } #define MAGIC_VAL 0x1234 #define NUM_ITER 100000 #define VIP_NUM 5 static void test_l4lb(void) { unsigned int nr_cpus = bpf_num_possible_cpus(); const char *file = "./test_l4lb.o"; struct vip key = {.protocol = 6}; struct vip_meta { __u32 flags; __u32 vip_num; } value = {.vip_num = VIP_NUM}; __u32 stats_key = VIP_NUM; struct vip_stats { __u64 bytes; __u64 pkts; } stats[nr_cpus]; struct real_definition { union { __be32 dst; __be32 dstv6[4]; }; __u8 flags; } real_def = {.dst = MAGIC_VAL}; __u32 ch_key = 11, real_num = 3; __u32 duration, retval, size; int err, i, prog_fd, map_fd; __u64 bytes = 0, pkts = 0; struct bpf_object *obj; char buf[128]; u32 *magic = (u32 *)buf; err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); if (err) { error_cnt++; return; } map_fd = bpf_find_map(__func__, obj, "vip_map"); if (map_fd < 0) goto out; bpf_map_update_elem(map_fd, &key, &value, 0); map_fd = bpf_find_map(__func__, obj, "ch_rings"); if (map_fd < 0) goto out; bpf_map_update_elem(map_fd, &ch_key, &real_num, 0); map_fd = bpf_find_map(__func__, obj, "reals"); if (map_fd < 0) goto out; bpf_map_update_elem(map_fd, &real_num, &real_def, 0); err = bpf_prog_test_run(prog_fd, NUM_ITER, &pkt_v4, sizeof(pkt_v4), buf, &size, &retval, &duration); CHECK(err || errno || retval != 7/*TC_ACT_REDIRECT*/ || size != 54 || *magic != MAGIC_VAL, "ipv4", "err %d errno %d retval %d size %d magic %x\n", err, errno, retval, size, *magic); err = bpf_prog_test_run(prog_fd, NUM_ITER, &pkt_v6, sizeof(pkt_v6), buf, &size, &retval, &duration); CHECK(err || errno || retval != 7/*TC_ACT_REDIRECT*/ || size != 74 || *magic != MAGIC_VAL, "ipv6", "err %d errno %d retval %d size %d magic %x\n", err, errno, retval, size, *magic); map_fd = bpf_find_map(__func__, obj, "stats"); if (map_fd < 0) goto out; bpf_map_lookup_elem(map_fd, &stats_key, stats); for (i = 0; i < nr_cpus; i++) { bytes += stats[i].bytes; pkts += stats[i].pkts; } if (bytes != MAGIC_BYTES * NUM_ITER * 2 || pkts != NUM_ITER * 2) { error_cnt++; printf("test_l4lb:FAIL:stats %lld %lld\n", bytes, pkts); } out: bpf_object__close(obj); }
static void stats_print(struct stats_record *stats_rec, struct stats_record *stats_prev, bool err_only) { unsigned int nr_cpus = bpf_num_possible_cpus(); int rec_i = 0, i, to_cpu; double t = 0, pps = 0; /* Header */ printf("%-15s %-7s %-12s %-12s %-9s\n", "XDP-event", "CPU:to", "pps", "drop-pps", "extra-info"); /* tracepoint: xdp:xdp_redirect_* */ if (err_only) rec_i = REDIR_ERROR; for (; rec_i < REDIR_RES_MAX; rec_i++) { struct record_u64 *rec, *prev; char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %s\n"; char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %s\n"; rec = &stats_rec->xdp_redirect[rec_i]; prev = &stats_prev->xdp_redirect[rec_i]; t = calc_period_u64(rec, prev); for (i = 0; i < nr_cpus; i++) { struct u64rec *r = &rec->cpu[i]; struct u64rec *p = &prev->cpu[i]; pps = calc_pps_u64(r, p, t); if (pps > 0) printf(fmt1, "XDP_REDIRECT", i, rec_i ? 0.0: pps, rec_i ? pps : 0.0, err2str(rec_i)); } pps = calc_pps_u64(&rec->total, &prev->total, t); printf(fmt2, "XDP_REDIRECT", "total", rec_i ? 0.0: pps, rec_i ? pps : 0.0, err2str(rec_i)); } /* tracepoint: xdp:xdp_exception */ for (rec_i = 0; rec_i < XDP_ACTION_MAX; rec_i++) { struct record_u64 *rec, *prev; char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %s\n"; char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %s\n"; rec = &stats_rec->xdp_exception[rec_i]; prev = &stats_prev->xdp_exception[rec_i]; t = calc_period_u64(rec, prev); for (i = 0; i < nr_cpus; i++) { struct u64rec *r = &rec->cpu[i]; struct u64rec *p = &prev->cpu[i]; pps = calc_pps_u64(r, p, t); if (pps > 0) printf(fmt1, "Exception", i, 0.0, pps, action2str(rec_i)); } pps = calc_pps_u64(&rec->total, &prev->total, t); if (pps > 0) printf(fmt2, "Exception", "total", 0.0, pps, action2str(rec_i)); } /* cpumap enqueue stats */ for (to_cpu = 0; to_cpu < MAX_CPUS; to_cpu++) { char *fmt1 = "%-15s %3d:%-3d %'-12.0f %'-12.0f %'-10.2f %s\n"; char *fmt2 = "%-15s %3s:%-3d %'-12.0f %'-12.0f %'-10.2f %s\n"; struct record *rec, *prev; char *info_str = ""; double drop, info; rec = &stats_rec->xdp_cpumap_enqueue[to_cpu]; prev = &stats_prev->xdp_cpumap_enqueue[to_cpu]; t = calc_period(rec, prev); for (i = 0; i < nr_cpus; i++) { struct datarec *r = &rec->cpu[i]; struct datarec *p = &prev->cpu[i]; pps = calc_pps(r, p, t); drop = calc_drop(r, p, t); info = calc_info(r, p, t); if (info > 0) { info_str = "bulk-average"; info = pps / info; /* calc average bulk size */ } if (pps > 0) printf(fmt1, "cpumap-enqueue", i, to_cpu, pps, drop, info, info_str); } pps = calc_pps(&rec->total, &prev->total, t); if (pps > 0) { drop = calc_drop(&rec->total, &prev->total, t); info = calc_info(&rec->total, &prev->total, t); if (info > 0) { info_str = "bulk-average"; info = pps / info; /* calc average bulk size */ } printf(fmt2, "cpumap-enqueue", "sum", to_cpu, pps, drop, info, info_str); } } /* cpumap kthread stats */ { char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %'-10.0f %s\n"; char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %'-10.0f %s\n"; struct record *rec, *prev; double drop, info; char *i_str = ""; rec = &stats_rec->xdp_cpumap_kthread; prev = &stats_prev->xdp_cpumap_kthread; t = calc_period(rec, prev); for (i = 0; i < nr_cpus; i++) { struct datarec *r = &rec->cpu[i]; struct datarec *p = &prev->cpu[i]; pps = calc_pps(r, p, t); drop = calc_drop(r, p, t); info = calc_info(r, p, t); if (info > 0) i_str = "sched"; if (pps > 0 || drop > 0) printf(fmt1, "cpumap-kthread", i, pps, drop, info, i_str); } pps = calc_pps(&rec->total, &prev->total, t); drop = calc_drop(&rec->total, &prev->total, t); info = calc_info(&rec->total, &prev->total, t); if (info > 0) i_str = "sched-sum"; printf(fmt2, "cpumap-kthread", "total", pps, drop, info, i_str); } /* devmap ndo_xdp_xmit stats */ { char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %'-10.2f %s %s\n"; char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %'-10.2f %s %s\n"; struct record *rec, *prev; double drop, info, err; char *i_str = ""; char *err_str = ""; rec = &stats_rec->xdp_devmap_xmit; prev = &stats_prev->xdp_devmap_xmit; t = calc_period(rec, prev); for (i = 0; i < nr_cpus; i++) { struct datarec *r = &rec->cpu[i]; struct datarec *p = &prev->cpu[i]; pps = calc_pps(r, p, t); drop = calc_drop(r, p, t); info = calc_info(r, p, t); err = calc_err(r, p, t); if (info > 0) { i_str = "bulk-average"; info = (pps+drop) / info; /* calc avg bulk */ } if (err > 0) err_str = "drv-err"; if (pps > 0 || drop > 0) printf(fmt1, "devmap-xmit", i, pps, drop, info, i_str, err_str); } pps = calc_pps(&rec->total, &prev->total, t); drop = calc_drop(&rec->total, &prev->total, t); info = calc_info(&rec->total, &prev->total, t); err = calc_err(&rec->total, &prev->total, t); if (info > 0) { i_str = "bulk-average"; info = (pps+drop) / info; /* calc avg bulk */ } if (err > 0) err_str = "drv-err"; printf(fmt2, "devmap-xmit", "total", pps, drop, info, i_str, err_str); } printf("\n"); }