diff options
author | Jesper Dangaard Brouer <brouer@redhat.com> | 2017-10-06 04:41:46 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-10-06 13:04:35 -0400 |
commit | 280b058d4801cb431477dc101a776e4b24995f2f (patch) | |
tree | 993a220ce48d529b5b3cd71a4af56429eaf4ef1b | |
parent | f4ce0a0116bc90803adac10865f14429313cb2b6 (diff) |
samples/bpf: xdp_monitor also record xdp_exception tracepoint
Also monitor the tracepoint xdp_exception. This tracepoint is usually
invoked by the drivers. Programs themselves can activate this by
returning XDP_ABORTED, which will drop the packet but also trigger the
tracepoint. This is useful for distinguishing intentional (XDP_DROP)
vs. ebpf-program error cases that cased a drop (XDP_ABORTED).
Drivers also use this tracepoint for reporting on XDP actions that are
unknown to the specific driver. This can help the user to detect if a
driver e.g. doesn't implement XDP_REDIRECT yet.
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | samples/bpf/xdp_monitor_kern.c | 38 | ||||
-rw-r--r-- | samples/bpf/xdp_monitor_user.c | 108 |
2 files changed, 121 insertions, 25 deletions
diff --git a/samples/bpf/xdp_monitor_kern.c b/samples/bpf/xdp_monitor_kern.c index cc7e19d2ad76..2fe2f761a0d0 100644 --- a/samples/bpf/xdp_monitor_kern.c +++ b/samples/bpf/xdp_monitor_kern.c | |||
@@ -13,6 +13,14 @@ struct bpf_map_def SEC("maps") redirect_err_cnt = { | |||
13 | /* TODO: have entries for all possible errno's */ | 13 | /* TODO: have entries for all possible errno's */ |
14 | }; | 14 | }; |
15 | 15 | ||
16 | #define XDP_UNKNOWN XDP_REDIRECT + 1 | ||
17 | struct bpf_map_def SEC("maps") exception_cnt = { | ||
18 | .type = BPF_MAP_TYPE_PERCPU_ARRAY, | ||
19 | .key_size = sizeof(u32), | ||
20 | .value_size = sizeof(u64), | ||
21 | .max_entries = XDP_UNKNOWN + 1, | ||
22 | }; | ||
23 | |||
16 | /* Tracepoint format: /sys/kernel/debug/tracing/events/xdp/xdp_redirect/format | 24 | /* Tracepoint format: /sys/kernel/debug/tracing/events/xdp/xdp_redirect/format |
17 | * Code in: kernel/include/trace/events/xdp.h | 25 | * Code in: kernel/include/trace/events/xdp.h |
18 | */ | 26 | */ |
@@ -44,7 +52,7 @@ int xdp_redirect_collect_stat(struct xdp_redirect_ctx *ctx) | |||
44 | 52 | ||
45 | cnt = bpf_map_lookup_elem(&redirect_err_cnt, &key); | 53 | cnt = bpf_map_lookup_elem(&redirect_err_cnt, &key); |
46 | if (!cnt) | 54 | if (!cnt) |
47 | return 0; | 55 | return 1; |
48 | *cnt += 1; | 56 | *cnt += 1; |
49 | 57 | ||
50 | return 0; /* Indicate event was filtered (no further processing)*/ | 58 | return 0; /* Indicate event was filtered (no further processing)*/ |
@@ -82,3 +90,31 @@ int trace_xdp_redirect_map(struct xdp_redirect_ctx *ctx) | |||
82 | { | 90 | { |
83 | return xdp_redirect_collect_stat(ctx); | 91 | return xdp_redirect_collect_stat(ctx); |
84 | } | 92 | } |
93 | |||
94 | /* Tracepoint format: /sys/kernel/debug/tracing/events/xdp/xdp_exception/format | ||
95 | * Code in: kernel/include/trace/events/xdp.h | ||
96 | */ | ||
97 | struct xdp_exception_ctx { | ||
98 | u64 __pad; // First 8 bytes are not accessible by bpf code | ||
99 | int prog_id; // offset:8; size:4; signed:1; | ||
100 | u32 act; // offset:12; size:4; signed:0; | ||
101 | int ifindex; // offset:16; size:4; signed:1; | ||
102 | }; | ||
103 | |||
104 | SEC("tracepoint/xdp/xdp_exception") | ||
105 | int trace_xdp_exception(struct xdp_exception_ctx *ctx) | ||
106 | { | ||
107 | u64 *cnt;; | ||
108 | u32 key; | ||
109 | |||
110 | key = ctx->act; | ||
111 | if (key > XDP_REDIRECT) | ||
112 | key = XDP_UNKNOWN; | ||
113 | |||
114 | cnt = bpf_map_lookup_elem(&exception_cnt, &key); | ||
115 | if (!cnt) | ||
116 | return 1; | ||
117 | *cnt += 1; | ||
118 | |||
119 | return 0; | ||
120 | } | ||
diff --git a/samples/bpf/xdp_monitor_user.c b/samples/bpf/xdp_monitor_user.c index c5ab8b776973..97c3456c11b2 100644 --- a/samples/bpf/xdp_monitor_user.c +++ b/samples/bpf/xdp_monitor_user.c | |||
@@ -89,6 +89,23 @@ static const char *err2str(int err) | |||
89 | return redir_names[err]; | 89 | return redir_names[err]; |
90 | return NULL; | 90 | return NULL; |
91 | } | 91 | } |
92 | /* enum xdp_action */ | ||
93 | #define XDP_UNKNOWN XDP_REDIRECT + 1 | ||
94 | #define XDP_ACTION_MAX (XDP_UNKNOWN + 1) | ||
95 | static const char *xdp_action_names[XDP_ACTION_MAX] = { | ||
96 | [XDP_ABORTED] = "XDP_ABORTED", | ||
97 | [XDP_DROP] = "XDP_DROP", | ||
98 | [XDP_PASS] = "XDP_PASS", | ||
99 | [XDP_TX] = "XDP_TX", | ||
100 | [XDP_REDIRECT] = "XDP_REDIRECT", | ||
101 | [XDP_UNKNOWN] = "XDP_UNKNOWN", | ||
102 | }; | ||
103 | static const char *action2str(int action) | ||
104 | { | ||
105 | if (action < XDP_ACTION_MAX) | ||
106 | return xdp_action_names[action]; | ||
107 | return NULL; | ||
108 | } | ||
92 | 109 | ||
93 | struct record { | 110 | struct record { |
94 | __u64 counter; | 111 | __u64 counter; |
@@ -97,6 +114,7 @@ struct record { | |||
97 | 114 | ||
98 | struct stats_record { | 115 | struct stats_record { |
99 | struct record xdp_redir[REDIR_RES_MAX]; | 116 | struct record xdp_redir[REDIR_RES_MAX]; |
117 | struct record xdp_exception[XDP_ACTION_MAX]; | ||
100 | }; | 118 | }; |
101 | 119 | ||
102 | static void stats_print_headers(bool err_only) | 120 | static void stats_print_headers(bool err_only) |
@@ -104,39 +122,72 @@ static void stats_print_headers(bool err_only) | |||
104 | if (err_only) | 122 | if (err_only) |
105 | printf("\n%s\n", __doc_err_only__); | 123 | printf("\n%s\n", __doc_err_only__); |
106 | 124 | ||
107 | printf("%-14s %-10s %-18s %-9s\n", | 125 | printf("%-14s %-11s %-10s %-18s %-9s\n", |
108 | "XDP_REDIRECT", "pps ", "pps-human-readable", "measure-period"); | 126 | "ACTION", "result", "pps ", "pps-human-readable", "measure-period"); |
127 | } | ||
128 | |||
129 | static double calc_period(struct record *r, struct record *p) | ||
130 | { | ||
131 | double period_ = 0; | ||
132 | __u64 period = 0; | ||
133 | |||
134 | period = r->timestamp - p->timestamp; | ||
135 | if (period > 0) | ||
136 | period_ = ((double) period / NANOSEC_PER_SEC); | ||
137 | |||
138 | return period_; | ||
139 | } | ||
140 | |||
141 | static double calc_pps(struct record *r, struct record *p, double period) | ||
142 | { | ||
143 | __u64 packets = 0; | ||
144 | double pps = 0; | ||
145 | |||
146 | if (period > 0) { | ||
147 | packets = r->counter - p->counter; | ||
148 | pps = packets / period; | ||
149 | } | ||
150 | return pps; | ||
109 | } | 151 | } |
110 | 152 | ||
111 | static void stats_print(struct stats_record *rec, | 153 | static void stats_print(struct stats_record *rec, |
112 | struct stats_record *prev, | 154 | struct stats_record *prev, |
113 | bool err_only) | 155 | bool err_only) |
114 | { | 156 | { |
157 | double period = 0, pps = 0; | ||
158 | struct record *r, *p; | ||
115 | int i = 0; | 159 | int i = 0; |
116 | 160 | ||
161 | char *fmt = "%-14s %-11s %-10.0f %'-18.0f %f\n"; | ||
162 | |||
163 | /* tracepoint: xdp:xdp_redirect_* */ | ||
117 | if (err_only) | 164 | if (err_only) |
118 | i = REDIR_ERROR; | 165 | i = REDIR_ERROR; |
119 | 166 | ||
120 | for (; i < REDIR_RES_MAX; i++) { | 167 | for (; i < REDIR_RES_MAX; i++) { |
121 | struct record *r = &rec->xdp_redir[i]; | 168 | r = &rec->xdp_redir[i]; |
122 | struct record *p = &prev->xdp_redir[i]; | 169 | p = &prev->xdp_redir[i]; |
123 | __u64 period = 0; | ||
124 | __u64 packets = 0; | ||
125 | double pps = 0; | ||
126 | double period_ = 0; | ||
127 | 170 | ||
128 | if (p->timestamp) { | 171 | if (p->timestamp) { |
129 | packets = r->counter - p->counter; | 172 | period = calc_period(r, p); |
130 | period = r->timestamp - p->timestamp; | 173 | pps = calc_pps(r, p, period); |
131 | if (period > 0) { | ||
132 | period_ = ((double) period / NANOSEC_PER_SEC); | ||
133 | pps = packets / period_; | ||
134 | } | ||
135 | } | 174 | } |
175 | printf(fmt, "XDP_REDIRECT", err2str(i), pps, pps, period); | ||
176 | } | ||
136 | 177 | ||
137 | printf("%-14s %-10.0f %'-18.0f %f\n", | 178 | /* tracepoint: xdp:xdp_exception */ |
138 | err2str(i), pps, pps, period_); | 179 | for (i = 0; i < XDP_ACTION_MAX; i++) { |
180 | r = &rec->xdp_exception[i]; | ||
181 | p = &prev->xdp_exception[i]; | ||
182 | if (p->timestamp) { | ||
183 | period = calc_period(r, p); | ||
184 | pps = calc_pps(r, p, period); | ||
185 | } | ||
186 | if (pps > 0) | ||
187 | printf(fmt, action2str(i), "Exception", | ||
188 | pps, pps, period); | ||
139 | } | 189 | } |
190 | printf("\n"); | ||
140 | } | 191 | } |
141 | 192 | ||
142 | static __u64 get_key32_value64_percpu(int fd, __u32 key) | 193 | static __u64 get_key32_value64_percpu(int fd, __u32 key) |
@@ -160,25 +211,33 @@ static __u64 get_key32_value64_percpu(int fd, __u32 key) | |||
160 | return sum; | 211 | return sum; |
161 | } | 212 | } |
162 | 213 | ||
163 | static bool stats_collect(int fd, struct stats_record *rec) | 214 | static bool stats_collect(struct stats_record *rec) |
164 | { | 215 | { |
216 | int fd; | ||
165 | int i; | 217 | int i; |
166 | 218 | ||
167 | /* TODO: Detect if someone unloaded the perf event_fd's, as | 219 | /* TODO: Detect if someone unloaded the perf event_fd's, as |
168 | * this can happen by someone running perf-record -e | 220 | * this can happen by someone running perf-record -e |
169 | */ | 221 | */ |
170 | 222 | ||
223 | fd = map_data[0].fd; /* map0: redirect_err_cnt */ | ||
171 | for (i = 0; i < REDIR_RES_MAX; i++) { | 224 | for (i = 0; i < REDIR_RES_MAX; i++) { |
172 | rec->xdp_redir[i].timestamp = gettime(); | 225 | rec->xdp_redir[i].timestamp = gettime(); |
173 | rec->xdp_redir[i].counter = get_key32_value64_percpu(fd, i); | 226 | rec->xdp_redir[i].counter = get_key32_value64_percpu(fd, i); |
174 | } | 227 | } |
228 | |||
229 | fd = map_data[1].fd; /* map1: exception_cnt */ | ||
230 | for (i = 0; i < XDP_ACTION_MAX; i++) { | ||
231 | rec->xdp_exception[i].timestamp = gettime(); | ||
232 | rec->xdp_exception[i].counter = get_key32_value64_percpu(fd, i); | ||
233 | } | ||
234 | |||
175 | return true; | 235 | return true; |
176 | } | 236 | } |
177 | 237 | ||
178 | static void stats_poll(int interval, bool err_only) | 238 | static void stats_poll(int interval, bool err_only) |
179 | { | 239 | { |
180 | struct stats_record rec, prev; | 240 | struct stats_record rec, prev; |
181 | int map_fd; | ||
182 | 241 | ||
183 | memset(&rec, 0, sizeof(rec)); | 242 | memset(&rec, 0, sizeof(rec)); |
184 | 243 | ||
@@ -190,16 +249,17 @@ static void stats_poll(int interval, bool err_only) | |||
190 | printf("\n%s", __doc__); | 249 | printf("\n%s", __doc__); |
191 | 250 | ||
192 | /* TODO Need more advanced stats on error types */ | 251 | /* TODO Need more advanced stats on error types */ |
193 | if (verbose) | 252 | if (verbose) { |
194 | printf(" - Stats map: %s\n", map_data[0].name); | 253 | printf(" - Stats map0: %s\n", map_data[0].name); |
195 | map_fd = map_data[0].fd; | 254 | printf(" - Stats map1: %s\n", map_data[1].name); |
196 | 255 | printf("\n"); | |
197 | stats_print_headers(err_only); | 256 | } |
198 | fflush(stdout); | 257 | fflush(stdout); |
199 | 258 | ||
200 | while (1) { | 259 | while (1) { |
201 | memcpy(&prev, &rec, sizeof(rec)); | 260 | memcpy(&prev, &rec, sizeof(rec)); |
202 | stats_collect(map_fd, &rec); | 261 | stats_collect(&rec); |
262 | stats_print_headers(err_only); | ||
203 | stats_print(&rec, &prev, err_only); | 263 | stats_print(&rec, &prev, err_only); |
204 | fflush(stdout); | 264 | fflush(stdout); |
205 | sleep(interval); | 265 | sleep(interval); |