linux/samples/bpf/xdp_monitor_user.c
<<
>>
Prefs
   1/* Copyright(c) 2017 Jesper Dangaard Brouer, Red Hat, Inc.
   2 */
   3static const char *__doc__=
   4 "XDP monitor tool, based on tracepoints\n"
   5;
   6
   7static const char *__doc_err_only__=
   8 " NOTICE: Only tracking XDP redirect errors\n"
   9 "         Enable TX success stats via '--stats'\n"
  10 "         (which comes with a per packet processing overhead)\n"
  11;
  12
  13#include <errno.h>
  14#include <stdio.h>
  15#include <stdlib.h>
  16#include <stdbool.h>
  17#include <stdint.h>
  18#include <string.h>
  19#include <ctype.h>
  20#include <unistd.h>
  21#include <locale.h>
  22
  23#include <sys/resource.h>
  24#include <getopt.h>
  25#include <net/if.h>
  26#include <time.h>
  27
  28#include "libbpf.h"
  29#include "bpf_load.h"
  30#include "bpf_util.h"
  31
  32static int verbose = 1;
  33static bool debug = false;
  34
  35static const struct option long_options[] = {
  36        {"help",        no_argument,            NULL, 'h' },
  37        {"debug",       no_argument,            NULL, 'D' },
  38        {"stats",       no_argument,            NULL, 'S' },
  39        {"sec",         required_argument,      NULL, 's' },
  40        {0, 0, NULL,  0 }
  41};
  42
  43static void usage(char *argv[])
  44{
  45        int i;
  46        printf("\nDOCUMENTATION:\n%s\n", __doc__);
  47        printf("\n");
  48        printf(" Usage: %s (options-see-below)\n",
  49               argv[0]);
  50        printf(" Listing options:\n");
  51        for (i = 0; long_options[i].name != 0; i++) {
  52                printf(" --%-15s", long_options[i].name);
  53                if (long_options[i].flag != NULL)
  54                        printf(" flag (internal value:%d)",
  55                               *long_options[i].flag);
  56                else
  57                        printf("(internal short-option: -%c)",
  58                               long_options[i].val);
  59                printf("\n");
  60        }
  61        printf("\n");
  62}
  63
  64#define NANOSEC_PER_SEC 1000000000 /* 10^9 */
  65static __u64 gettime(void)
  66{
  67        struct timespec t;
  68        int res;
  69
  70        res = clock_gettime(CLOCK_MONOTONIC, &t);
  71        if (res < 0) {
  72                fprintf(stderr, "Error with gettimeofday! (%i)\n", res);
  73                exit(EXIT_FAILURE);
  74        }
  75        return (__u64) t.tv_sec * NANOSEC_PER_SEC + t.tv_nsec;
  76}
  77
  78enum {
  79        REDIR_SUCCESS = 0,
  80        REDIR_ERROR = 1,
  81};
  82#define REDIR_RES_MAX 2
  83static const char *redir_names[REDIR_RES_MAX] = {
  84        [REDIR_SUCCESS] = "Success",
  85        [REDIR_ERROR]   = "Error",
  86};
  87static const char *err2str(int err)
  88{
  89        if (err < REDIR_RES_MAX)
  90                return redir_names[err];
  91        return NULL;
  92}
  93/* enum xdp_action */
  94#define XDP_UNKNOWN     XDP_REDIRECT + 1
  95#define XDP_ACTION_MAX (XDP_UNKNOWN + 1)
  96static const char *xdp_action_names[XDP_ACTION_MAX] = {
  97        [XDP_ABORTED]   = "XDP_ABORTED",
  98        [XDP_DROP]      = "XDP_DROP",
  99        [XDP_PASS]      = "XDP_PASS",
 100        [XDP_TX]        = "XDP_TX",
 101        [XDP_REDIRECT]  = "XDP_REDIRECT",
 102        [XDP_UNKNOWN]   = "XDP_UNKNOWN",
 103};
 104static const char *action2str(int action)
 105{
 106        if (action < XDP_ACTION_MAX)
 107                return xdp_action_names[action];
 108        return NULL;
 109}
 110
 111struct record {
 112        __u64 counter;
 113        __u64 timestamp;
 114};
 115
 116struct stats_record {
 117        struct record xdp_redir[REDIR_RES_MAX];
 118        struct record xdp_exception[XDP_ACTION_MAX];
 119};
 120
 121static void stats_print_headers(bool err_only)
 122{
 123        if (err_only)
 124                printf("\n%s\n", __doc_err_only__);
 125
 126        printf("%-14s %-11s %-10s %-18s %-9s\n",
 127               "ACTION", "result", "pps ", "pps-human-readable", "measure-period");
 128}
 129
 130static double calc_period(struct record *r, struct record *p)
 131{
 132        double period_ = 0;
 133        __u64 period = 0;
 134
 135        period = r->timestamp - p->timestamp;
 136        if (period > 0)
 137                period_ = ((double) period / NANOSEC_PER_SEC);
 138
 139        return period_;
 140}
 141
 142static double calc_pps(struct record *r, struct record *p, double period)
 143{
 144        __u64 packets = 0;
 145        double pps = 0;
 146
 147        if (period > 0) {
 148                packets = r->counter - p->counter;
 149                pps = packets / period;
 150        }
 151        return pps;
 152}
 153
 154static void stats_print(struct stats_record *rec,
 155                        struct stats_record *prev,
 156                        bool err_only)
 157{
 158        double period = 0, pps = 0;
 159        struct record *r, *p;
 160        int i = 0;
 161
 162        char *fmt = "%-14s %-11s %-10.0f %'-18.0f %f\n";
 163
 164        /* tracepoint: xdp:xdp_redirect_* */
 165        if (err_only)
 166                i = REDIR_ERROR;
 167
 168        for (; i < REDIR_RES_MAX; i++) {
 169                r = &rec->xdp_redir[i];
 170                p = &prev->xdp_redir[i];
 171
 172                if (p->timestamp) {
 173                        period = calc_period(r, p);
 174                        pps = calc_pps(r, p, period);
 175                }
 176                printf(fmt, "XDP_REDIRECT", err2str(i), pps, pps, period);
 177        }
 178
 179        /* tracepoint: xdp:xdp_exception */
 180        for (i = 0; i < XDP_ACTION_MAX; i++) {
 181                r = &rec->xdp_exception[i];
 182                p = &prev->xdp_exception[i];
 183                if (p->timestamp) {
 184                        period = calc_period(r, p);
 185                        pps = calc_pps(r, p, period);
 186                }
 187                if (pps > 0)
 188                        printf(fmt, action2str(i), "Exception",
 189                               pps, pps, period);
 190        }
 191        printf("\n");
 192}
 193
 194static __u64 get_key32_value64_percpu(int fd, __u32 key)
 195{
 196        /* For percpu maps, userspace gets a value per possible CPU */
 197        unsigned int nr_cpus = bpf_num_possible_cpus();
 198        __u64 values[nr_cpus];
 199        __u64 sum = 0;
 200        int i;
 201
 202        if ((bpf_map_lookup_elem(fd, &key, values)) != 0) {
 203                fprintf(stderr,
 204                        "ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
 205                return 0;
 206        }
 207
 208        /* Sum values from each CPU */
 209        for (i = 0; i < nr_cpus; i++) {
 210                sum += values[i];
 211        }
 212        return sum;
 213}
 214
 215static bool stats_collect(struct stats_record *rec)
 216{
 217        int fd;
 218        int i;
 219
 220        /* TODO: Detect if someone unloaded the perf event_fd's, as
 221         * this can happen by someone running perf-record -e
 222         */
 223
 224        fd = map_data[0].fd; /* map0: redirect_err_cnt */
 225        for (i = 0; i < REDIR_RES_MAX; i++) {
 226                rec->xdp_redir[i].timestamp = gettime();
 227                rec->xdp_redir[i].counter = get_key32_value64_percpu(fd, i);
 228        }
 229
 230        fd = map_data[1].fd; /* map1: exception_cnt */
 231        for (i = 0; i < XDP_ACTION_MAX; i++) {
 232                rec->xdp_exception[i].timestamp = gettime();
 233                rec->xdp_exception[i].counter = get_key32_value64_percpu(fd, i);
 234        }
 235
 236        return true;
 237}
 238
 239static void stats_poll(int interval, bool err_only)
 240{
 241        struct stats_record rec, prev;
 242
 243        memset(&rec, 0, sizeof(rec));
 244
 245        /* Trick to pretty printf with thousands separators use %' */
 246        setlocale(LC_NUMERIC, "en_US");
 247
 248        /* Header */
 249        if (verbose)
 250                printf("\n%s", __doc__);
 251
 252        /* TODO Need more advanced stats on error types */
 253        if (verbose) {
 254                printf(" - Stats map0: %s\n", map_data[0].name);
 255                printf(" - Stats map1: %s\n", map_data[1].name);
 256                printf("\n");
 257        }
 258        fflush(stdout);
 259
 260        while (1) {
 261                memcpy(&prev, &rec, sizeof(rec));
 262                stats_collect(&rec);
 263                stats_print_headers(err_only);
 264                stats_print(&rec, &prev, err_only);
 265                fflush(stdout);
 266                sleep(interval);
 267        }
 268}
 269
 270static void print_bpf_prog_info(void)
 271{
 272        int i;
 273
 274        /* Prog info */
 275        printf("Loaded BPF prog have %d bpf program(s)\n", prog_cnt);
 276        for (i = 0; i < prog_cnt; i++) {
 277                printf(" - prog_fd[%d] = fd(%d)\n", i, prog_fd[i]);
 278        }
 279
 280        /* Maps info */
 281        printf("Loaded BPF prog have %d map(s)\n", map_data_count);
 282        for (i = 0; i < map_data_count; i++) {
 283                char *name = map_data[i].name;
 284                int fd     = map_data[i].fd;
 285
 286                printf(" - map_data[%d] = fd(%d) name:%s\n", i, fd, name);
 287        }
 288
 289        /* Event info */
 290        printf("Searching for (max:%d) event file descriptor(s)\n", prog_cnt);
 291        for (i = 0; i < prog_cnt; i++) {
 292                if (event_fd[i] != -1)
 293                        printf(" - event_fd[%d] = fd(%d)\n", i, event_fd[i]);
 294        }
 295}
 296
 297int main(int argc, char **argv)
 298{
 299        struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
 300        int longindex = 0, opt;
 301        int ret = EXIT_SUCCESS;
 302        char bpf_obj_file[256];
 303
 304        /* Default settings: */
 305        bool errors_only = true;
 306        int interval = 2;
 307
 308        snprintf(bpf_obj_file, sizeof(bpf_obj_file), "%s_kern.o", argv[0]);
 309
 310        /* Parse commands line args */
 311        while ((opt = getopt_long(argc, argv, "h",
 312                                  long_options, &longindex)) != -1) {
 313                switch (opt) {
 314                case 'D':
 315                        debug = true;
 316                        break;
 317                case 'S':
 318                        errors_only = false;
 319                        break;
 320                case 's':
 321                        interval = atoi(optarg);
 322                        break;
 323                case 'h':
 324                default:
 325                        usage(argv);
 326                        return EXIT_FAILURE;
 327                }
 328        }
 329
 330        if (setrlimit(RLIMIT_MEMLOCK, &r)) {
 331                perror("setrlimit(RLIMIT_MEMLOCK)");
 332                return EXIT_FAILURE;
 333        }
 334
 335        if (load_bpf_file(bpf_obj_file)) {
 336                printf("ERROR - bpf_log_buf: %s", bpf_log_buf);
 337                return EXIT_FAILURE;
 338        }
 339        if (!prog_fd[0]) {
 340                printf("ERROR - load_bpf_file: %s\n", strerror(errno));
 341                return EXIT_FAILURE;
 342        }
 343
 344        if (debug) {
 345                print_bpf_prog_info();
 346        }
 347
 348        /* Unload/stop tracepoint event by closing fd's */
 349        if (errors_only) {
 350                /* The prog_fd[i] and event_fd[i] depend on the
 351                 * order the functions was defined in _kern.c
 352                 */
 353                close(event_fd[2]); /* tracepoint/xdp/xdp_redirect */
 354                close(prog_fd[2]);  /* func: trace_xdp_redirect */
 355                close(event_fd[3]); /* tracepoint/xdp/xdp_redirect_map */
 356                close(prog_fd[3]);  /* func: trace_xdp_redirect_map */
 357        }
 358
 359        stats_poll(interval, errors_only);
 360
 361        return ret;
 362}
 363