1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Monitoring code for network dropped packet alerts |
4 | * |
5 | * Copyright (C) 2009 Neil Horman <nhorman@tuxdriver.com> |
6 | */ |
7 | |
8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
9 | |
10 | #include <linux/netdevice.h> |
11 | #include <linux/etherdevice.h> |
12 | #include <linux/string.h> |
13 | #include <linux/if_arp.h> |
14 | #include <linux/inetdevice.h> |
15 | #include <linux/inet.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/netpoll.h> |
18 | #include <linux/sched.h> |
19 | #include <linux/delay.h> |
20 | #include <linux/types.h> |
21 | #include <linux/workqueue.h> |
22 | #include <linux/netlink.h> |
23 | #include <linux/net_dropmon.h> |
24 | #include <linux/bitfield.h> |
25 | #include <linux/percpu.h> |
26 | #include <linux/timer.h> |
27 | #include <linux/bitops.h> |
28 | #include <linux/slab.h> |
29 | #include <linux/module.h> |
30 | #include <net/genetlink.h> |
31 | #include <net/netevent.h> |
32 | #include <net/flow_offload.h> |
33 | #include <net/dropreason.h> |
34 | #include <net/devlink.h> |
35 | |
36 | #include <trace/events/skb.h> |
37 | #include <trace/events/napi.h> |
38 | #include <trace/events/devlink.h> |
39 | |
40 | #include <asm/unaligned.h> |
41 | |
42 | #define TRACE_ON 1 |
43 | #define TRACE_OFF 0 |
44 | |
45 | /* |
46 | * Globals, our netlink socket pointer |
47 | * and the work handle that will send up |
48 | * netlink alerts |
49 | */ |
50 | static int trace_state = TRACE_OFF; |
51 | static bool monitor_hw; |
52 | |
53 | /* net_dm_mutex |
54 | * |
55 | * An overall lock guarding every operation coming from userspace. |
56 | */ |
57 | static DEFINE_MUTEX(net_dm_mutex); |
58 | |
59 | struct net_dm_stats { |
60 | u64_stats_t dropped; |
61 | struct u64_stats_sync syncp; |
62 | }; |
63 | |
64 | #define NET_DM_MAX_HW_TRAP_NAME_LEN 40 |
65 | |
66 | struct net_dm_hw_entry { |
67 | char trap_name[NET_DM_MAX_HW_TRAP_NAME_LEN]; |
68 | u32 count; |
69 | }; |
70 | |
71 | struct net_dm_hw_entries { |
72 | u32 num_entries; |
73 | struct net_dm_hw_entry entries[]; |
74 | }; |
75 | |
76 | struct per_cpu_dm_data { |
77 | spinlock_t lock; /* Protects 'skb', 'hw_entries' and |
78 | * 'send_timer' |
79 | */ |
80 | union { |
81 | struct sk_buff *skb; |
82 | struct net_dm_hw_entries *hw_entries; |
83 | }; |
84 | struct sk_buff_head drop_queue; |
85 | struct work_struct dm_alert_work; |
86 | struct timer_list send_timer; |
87 | struct net_dm_stats stats; |
88 | }; |
89 | |
90 | struct dm_hw_stat_delta { |
91 | unsigned long last_rx; |
92 | unsigned long last_drop_val; |
93 | struct rcu_head rcu; |
94 | }; |
95 | |
96 | static struct genl_family net_drop_monitor_family; |
97 | |
98 | static DEFINE_PER_CPU(struct per_cpu_dm_data, dm_cpu_data); |
99 | static DEFINE_PER_CPU(struct per_cpu_dm_data, dm_hw_cpu_data); |
100 | |
101 | static int dm_hit_limit = 64; |
102 | static int dm_delay = 1; |
103 | static unsigned long dm_hw_check_delta = 2*HZ; |
104 | |
105 | static enum net_dm_alert_mode net_dm_alert_mode = NET_DM_ALERT_MODE_SUMMARY; |
106 | static u32 net_dm_trunc_len; |
107 | static u32 net_dm_queue_len = 1000; |
108 | |
109 | struct net_dm_alert_ops { |
110 | void (*kfree_skb_probe)(void *ignore, struct sk_buff *skb, |
111 | void *location, |
112 | enum skb_drop_reason reason); |
113 | void (*napi_poll_probe)(void *ignore, struct napi_struct *napi, |
114 | int work, int budget); |
115 | void (*work_item_func)(struct work_struct *work); |
116 | void (*hw_work_item_func)(struct work_struct *work); |
117 | void (*hw_trap_probe)(void *ignore, const struct devlink *devlink, |
118 | struct sk_buff *skb, |
119 | const struct devlink_trap_metadata *metadata); |
120 | }; |
121 | |
122 | struct net_dm_skb_cb { |
123 | union { |
124 | struct devlink_trap_metadata *hw_metadata; |
125 | void *pc; |
126 | }; |
127 | enum skb_drop_reason reason; |
128 | }; |
129 | |
130 | #define NET_DM_SKB_CB(__skb) ((struct net_dm_skb_cb *)&((__skb)->cb[0])) |
131 | |
132 | static struct sk_buff *reset_per_cpu_data(struct per_cpu_dm_data *data) |
133 | { |
134 | size_t al; |
135 | struct net_dm_alert_msg *msg; |
136 | struct nlattr *nla; |
137 | struct sk_buff *skb; |
138 | unsigned long flags; |
139 | void *; |
140 | |
141 | al = sizeof(struct net_dm_alert_msg); |
142 | al += dm_hit_limit * sizeof(struct net_dm_drop_point); |
143 | al += sizeof(struct nlattr); |
144 | |
145 | skb = genlmsg_new(payload: al, GFP_KERNEL); |
146 | |
147 | if (!skb) |
148 | goto err; |
149 | |
150 | msg_header = genlmsg_put(skb, portid: 0, seq: 0, family: &net_drop_monitor_family, |
151 | flags: 0, cmd: NET_DM_CMD_ALERT); |
152 | if (!msg_header) { |
153 | nlmsg_free(skb); |
154 | skb = NULL; |
155 | goto err; |
156 | } |
157 | nla = nla_reserve(skb, attrtype: NLA_UNSPEC, |
158 | attrlen: sizeof(struct net_dm_alert_msg)); |
159 | if (!nla) { |
160 | nlmsg_free(skb); |
161 | skb = NULL; |
162 | goto err; |
163 | } |
164 | msg = nla_data(nla); |
165 | memset(msg, 0, al); |
166 | goto out; |
167 | |
168 | err: |
169 | mod_timer(timer: &data->send_timer, expires: jiffies + HZ / 10); |
170 | out: |
171 | spin_lock_irqsave(&data->lock, flags); |
172 | swap(data->skb, skb); |
173 | spin_unlock_irqrestore(lock: &data->lock, flags); |
174 | |
175 | if (skb) { |
176 | struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data; |
177 | struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlh); |
178 | |
179 | genlmsg_end(skb, hdr: genlmsg_data(gnlh)); |
180 | } |
181 | |
182 | return skb; |
183 | } |
184 | |
185 | static const struct genl_multicast_group dropmon_mcgrps[] = { |
186 | { .name = "events" , }, |
187 | }; |
188 | |
189 | static void send_dm_alert(struct work_struct *work) |
190 | { |
191 | struct sk_buff *skb; |
192 | struct per_cpu_dm_data *data; |
193 | |
194 | data = container_of(work, struct per_cpu_dm_data, dm_alert_work); |
195 | |
196 | skb = reset_per_cpu_data(data); |
197 | |
198 | if (skb) |
199 | genlmsg_multicast(family: &net_drop_monitor_family, skb, portid: 0, |
200 | group: 0, GFP_KERNEL); |
201 | } |
202 | |
203 | /* |
204 | * This is the timer function to delay the sending of an alert |
205 | * in the event that more drops will arrive during the |
206 | * hysteresis period. |
207 | */ |
208 | static void sched_send_work(struct timer_list *t) |
209 | { |
210 | struct per_cpu_dm_data *data = from_timer(data, t, send_timer); |
211 | |
212 | schedule_work(work: &data->dm_alert_work); |
213 | } |
214 | |
215 | static void trace_drop_common(struct sk_buff *skb, void *location) |
216 | { |
217 | struct net_dm_alert_msg *msg; |
218 | struct net_dm_drop_point *point; |
219 | struct nlmsghdr *nlh; |
220 | struct nlattr *nla; |
221 | int i; |
222 | struct sk_buff *dskb; |
223 | struct per_cpu_dm_data *data; |
224 | unsigned long flags; |
225 | |
226 | local_irq_save(flags); |
227 | data = this_cpu_ptr(&dm_cpu_data); |
228 | spin_lock(lock: &data->lock); |
229 | dskb = data->skb; |
230 | |
231 | if (!dskb) |
232 | goto out; |
233 | |
234 | nlh = (struct nlmsghdr *)dskb->data; |
235 | nla = genlmsg_data(gnlh: nlmsg_data(nlh)); |
236 | msg = nla_data(nla); |
237 | point = msg->points; |
238 | for (i = 0; i < msg->entries; i++) { |
239 | if (!memcmp(p: &location, q: &point->pc, size: sizeof(void *))) { |
240 | point->count++; |
241 | goto out; |
242 | } |
243 | point++; |
244 | } |
245 | if (msg->entries == dm_hit_limit) |
246 | goto out; |
247 | /* |
248 | * We need to create a new entry |
249 | */ |
250 | __nla_reserve_nohdr(skb: dskb, attrlen: sizeof(struct net_dm_drop_point)); |
251 | nla->nla_len += NLA_ALIGN(sizeof(struct net_dm_drop_point)); |
252 | memcpy(point->pc, &location, sizeof(void *)); |
253 | point->count = 1; |
254 | msg->entries++; |
255 | |
256 | if (!timer_pending(timer: &data->send_timer)) { |
257 | data->send_timer.expires = jiffies + dm_delay * HZ; |
258 | add_timer(timer: &data->send_timer); |
259 | } |
260 | |
261 | out: |
262 | spin_unlock_irqrestore(lock: &data->lock, flags); |
263 | } |
264 | |
265 | static void trace_kfree_skb_hit(void *ignore, struct sk_buff *skb, |
266 | void *location, |
267 | enum skb_drop_reason reason) |
268 | { |
269 | trace_drop_common(skb, location); |
270 | } |
271 | |
272 | static void trace_napi_poll_hit(void *ignore, struct napi_struct *napi, |
273 | int work, int budget) |
274 | { |
275 | struct net_device *dev = napi->dev; |
276 | struct dm_hw_stat_delta *stat; |
277 | /* |
278 | * Don't check napi structures with no associated device |
279 | */ |
280 | if (!dev) |
281 | return; |
282 | |
283 | rcu_read_lock(); |
284 | stat = rcu_dereference(dev->dm_private); |
285 | if (stat) { |
286 | /* |
287 | * only add a note to our monitor buffer if: |
288 | * 1) its after the last_rx delta |
289 | * 2) our rx_dropped count has gone up |
290 | */ |
291 | if (time_after(jiffies, stat->last_rx + dm_hw_check_delta) && |
292 | (dev->stats.rx_dropped != stat->last_drop_val)) { |
293 | trace_drop_common(NULL, NULL); |
294 | stat->last_drop_val = dev->stats.rx_dropped; |
295 | stat->last_rx = jiffies; |
296 | } |
297 | } |
298 | rcu_read_unlock(); |
299 | } |
300 | |
301 | static struct net_dm_hw_entries * |
302 | net_dm_hw_reset_per_cpu_data(struct per_cpu_dm_data *hw_data) |
303 | { |
304 | struct net_dm_hw_entries *hw_entries; |
305 | unsigned long flags; |
306 | |
307 | hw_entries = kzalloc(struct_size(hw_entries, entries, dm_hit_limit), |
308 | GFP_KERNEL); |
309 | if (!hw_entries) { |
310 | /* If the memory allocation failed, we try to perform another |
311 | * allocation in 1/10 second. Otherwise, the probe function |
312 | * will constantly bail out. |
313 | */ |
314 | mod_timer(timer: &hw_data->send_timer, expires: jiffies + HZ / 10); |
315 | } |
316 | |
317 | spin_lock_irqsave(&hw_data->lock, flags); |
318 | swap(hw_data->hw_entries, hw_entries); |
319 | spin_unlock_irqrestore(lock: &hw_data->lock, flags); |
320 | |
321 | return hw_entries; |
322 | } |
323 | |
324 | static int net_dm_hw_entry_put(struct sk_buff *msg, |
325 | const struct net_dm_hw_entry *hw_entry) |
326 | { |
327 | struct nlattr *attr; |
328 | |
329 | attr = nla_nest_start(skb: msg, attrtype: NET_DM_ATTR_HW_ENTRY); |
330 | if (!attr) |
331 | return -EMSGSIZE; |
332 | |
333 | if (nla_put_string(skb: msg, attrtype: NET_DM_ATTR_HW_TRAP_NAME, str: hw_entry->trap_name)) |
334 | goto nla_put_failure; |
335 | |
336 | if (nla_put_u32(skb: msg, attrtype: NET_DM_ATTR_HW_TRAP_COUNT, value: hw_entry->count)) |
337 | goto nla_put_failure; |
338 | |
339 | nla_nest_end(skb: msg, start: attr); |
340 | |
341 | return 0; |
342 | |
343 | nla_put_failure: |
344 | nla_nest_cancel(skb: msg, start: attr); |
345 | return -EMSGSIZE; |
346 | } |
347 | |
348 | static int net_dm_hw_entries_put(struct sk_buff *msg, |
349 | const struct net_dm_hw_entries *hw_entries) |
350 | { |
351 | struct nlattr *attr; |
352 | int i; |
353 | |
354 | attr = nla_nest_start(skb: msg, attrtype: NET_DM_ATTR_HW_ENTRIES); |
355 | if (!attr) |
356 | return -EMSGSIZE; |
357 | |
358 | for (i = 0; i < hw_entries->num_entries; i++) { |
359 | int rc; |
360 | |
361 | rc = net_dm_hw_entry_put(msg, hw_entry: &hw_entries->entries[i]); |
362 | if (rc) |
363 | goto nla_put_failure; |
364 | } |
365 | |
366 | nla_nest_end(skb: msg, start: attr); |
367 | |
368 | return 0; |
369 | |
370 | nla_put_failure: |
371 | nla_nest_cancel(skb: msg, start: attr); |
372 | return -EMSGSIZE; |
373 | } |
374 | |
375 | static int |
376 | net_dm_hw_summary_report_fill(struct sk_buff *msg, |
377 | const struct net_dm_hw_entries *hw_entries) |
378 | { |
379 | struct net_dm_alert_msg anc_hdr = { 0 }; |
380 | void *hdr; |
381 | int rc; |
382 | |
383 | hdr = genlmsg_put(skb: msg, portid: 0, seq: 0, family: &net_drop_monitor_family, flags: 0, |
384 | cmd: NET_DM_CMD_ALERT); |
385 | if (!hdr) |
386 | return -EMSGSIZE; |
387 | |
388 | /* We need to put the ancillary header in order not to break user |
389 | * space. |
390 | */ |
391 | if (nla_put(skb: msg, attrtype: NLA_UNSPEC, attrlen: sizeof(anc_hdr), data: &anc_hdr)) |
392 | goto nla_put_failure; |
393 | |
394 | rc = net_dm_hw_entries_put(msg, hw_entries); |
395 | if (rc) |
396 | goto nla_put_failure; |
397 | |
398 | genlmsg_end(skb: msg, hdr); |
399 | |
400 | return 0; |
401 | |
402 | nla_put_failure: |
403 | genlmsg_cancel(skb: msg, hdr); |
404 | return -EMSGSIZE; |
405 | } |
406 | |
407 | static void net_dm_hw_summary_work(struct work_struct *work) |
408 | { |
409 | struct net_dm_hw_entries *hw_entries; |
410 | struct per_cpu_dm_data *hw_data; |
411 | struct sk_buff *msg; |
412 | int rc; |
413 | |
414 | hw_data = container_of(work, struct per_cpu_dm_data, dm_alert_work); |
415 | |
416 | hw_entries = net_dm_hw_reset_per_cpu_data(hw_data); |
417 | if (!hw_entries) |
418 | return; |
419 | |
420 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
421 | if (!msg) |
422 | goto out; |
423 | |
424 | rc = net_dm_hw_summary_report_fill(msg, hw_entries); |
425 | if (rc) { |
426 | nlmsg_free(skb: msg); |
427 | goto out; |
428 | } |
429 | |
430 | genlmsg_multicast(family: &net_drop_monitor_family, skb: msg, portid: 0, group: 0, GFP_KERNEL); |
431 | |
432 | out: |
433 | kfree(objp: hw_entries); |
434 | } |
435 | |
436 | static void |
437 | net_dm_hw_trap_summary_probe(void *ignore, const struct devlink *devlink, |
438 | struct sk_buff *skb, |
439 | const struct devlink_trap_metadata *metadata) |
440 | { |
441 | struct net_dm_hw_entries *hw_entries; |
442 | struct net_dm_hw_entry *hw_entry; |
443 | struct per_cpu_dm_data *hw_data; |
444 | unsigned long flags; |
445 | int i; |
446 | |
447 | if (metadata->trap_type == DEVLINK_TRAP_TYPE_CONTROL) |
448 | return; |
449 | |
450 | hw_data = this_cpu_ptr(&dm_hw_cpu_data); |
451 | spin_lock_irqsave(&hw_data->lock, flags); |
452 | hw_entries = hw_data->hw_entries; |
453 | |
454 | if (!hw_entries) |
455 | goto out; |
456 | |
457 | for (i = 0; i < hw_entries->num_entries; i++) { |
458 | hw_entry = &hw_entries->entries[i]; |
459 | if (!strncmp(hw_entry->trap_name, metadata->trap_name, |
460 | NET_DM_MAX_HW_TRAP_NAME_LEN - 1)) { |
461 | hw_entry->count++; |
462 | goto out; |
463 | } |
464 | } |
465 | if (WARN_ON_ONCE(hw_entries->num_entries == dm_hit_limit)) |
466 | goto out; |
467 | |
468 | hw_entry = &hw_entries->entries[hw_entries->num_entries]; |
469 | strscpy(p: hw_entry->trap_name, q: metadata->trap_name, |
470 | NET_DM_MAX_HW_TRAP_NAME_LEN - 1); |
471 | hw_entry->count = 1; |
472 | hw_entries->num_entries++; |
473 | |
474 | if (!timer_pending(timer: &hw_data->send_timer)) { |
475 | hw_data->send_timer.expires = jiffies + dm_delay * HZ; |
476 | add_timer(timer: &hw_data->send_timer); |
477 | } |
478 | |
479 | out: |
480 | spin_unlock_irqrestore(lock: &hw_data->lock, flags); |
481 | } |
482 | |
483 | static const struct net_dm_alert_ops net_dm_alert_summary_ops = { |
484 | .kfree_skb_probe = trace_kfree_skb_hit, |
485 | .napi_poll_probe = trace_napi_poll_hit, |
486 | .work_item_func = send_dm_alert, |
487 | .hw_work_item_func = net_dm_hw_summary_work, |
488 | .hw_trap_probe = net_dm_hw_trap_summary_probe, |
489 | }; |
490 | |
491 | static void net_dm_packet_trace_kfree_skb_hit(void *ignore, |
492 | struct sk_buff *skb, |
493 | void *location, |
494 | enum skb_drop_reason reason) |
495 | { |
496 | ktime_t tstamp = ktime_get_real(); |
497 | struct per_cpu_dm_data *data; |
498 | struct net_dm_skb_cb *cb; |
499 | struct sk_buff *nskb; |
500 | unsigned long flags; |
501 | |
502 | if (!skb_mac_header_was_set(skb)) |
503 | return; |
504 | |
505 | nskb = skb_clone(skb, GFP_ATOMIC); |
506 | if (!nskb) |
507 | return; |
508 | |
509 | cb = NET_DM_SKB_CB(nskb); |
510 | cb->reason = reason; |
511 | cb->pc = location; |
512 | /* Override the timestamp because we care about the time when the |
513 | * packet was dropped. |
514 | */ |
515 | nskb->tstamp = tstamp; |
516 | |
517 | data = this_cpu_ptr(&dm_cpu_data); |
518 | |
519 | spin_lock_irqsave(&data->drop_queue.lock, flags); |
520 | if (skb_queue_len(list_: &data->drop_queue) < net_dm_queue_len) |
521 | __skb_queue_tail(list: &data->drop_queue, newsk: nskb); |
522 | else |
523 | goto unlock_free; |
524 | spin_unlock_irqrestore(lock: &data->drop_queue.lock, flags); |
525 | |
526 | schedule_work(work: &data->dm_alert_work); |
527 | |
528 | return; |
529 | |
530 | unlock_free: |
531 | spin_unlock_irqrestore(lock: &data->drop_queue.lock, flags); |
532 | u64_stats_update_begin(syncp: &data->stats.syncp); |
533 | u64_stats_inc(p: &data->stats.dropped); |
534 | u64_stats_update_end(syncp: &data->stats.syncp); |
535 | consume_skb(skb: nskb); |
536 | } |
537 | |
538 | static void net_dm_packet_trace_napi_poll_hit(void *ignore, |
539 | struct napi_struct *napi, |
540 | int work, int budget) |
541 | { |
542 | } |
543 | |
544 | static size_t net_dm_in_port_size(void) |
545 | { |
546 | /* NET_DM_ATTR_IN_PORT nest */ |
547 | return nla_total_size(payload: 0) + |
548 | /* NET_DM_ATTR_PORT_NETDEV_IFINDEX */ |
549 | nla_total_size(payload: sizeof(u32)) + |
550 | /* NET_DM_ATTR_PORT_NETDEV_NAME */ |
551 | nla_total_size(IFNAMSIZ + 1); |
552 | } |
553 | |
554 | #define NET_DM_MAX_SYMBOL_LEN 40 |
555 | #define NET_DM_MAX_REASON_LEN 50 |
556 | |
557 | static size_t net_dm_packet_report_size(size_t payload_len) |
558 | { |
559 | size_t size; |
560 | |
561 | size = nlmsg_msg_size(GENL_HDRLEN + net_drop_monitor_family.hdrsize); |
562 | |
563 | return NLMSG_ALIGN(size) + |
564 | /* NET_DM_ATTR_ORIGIN */ |
565 | nla_total_size(payload: sizeof(u16)) + |
566 | /* NET_DM_ATTR_PC */ |
567 | nla_total_size(payload: sizeof(u64)) + |
568 | /* NET_DM_ATTR_SYMBOL */ |
569 | nla_total_size(NET_DM_MAX_SYMBOL_LEN + 1) + |
570 | /* NET_DM_ATTR_IN_PORT */ |
571 | net_dm_in_port_size() + |
572 | /* NET_DM_ATTR_TIMESTAMP */ |
573 | nla_total_size(payload: sizeof(u64)) + |
574 | /* NET_DM_ATTR_ORIG_LEN */ |
575 | nla_total_size(payload: sizeof(u32)) + |
576 | /* NET_DM_ATTR_PROTO */ |
577 | nla_total_size(payload: sizeof(u16)) + |
578 | /* NET_DM_ATTR_REASON */ |
579 | nla_total_size(NET_DM_MAX_REASON_LEN + 1) + |
580 | /* NET_DM_ATTR_PAYLOAD */ |
581 | nla_total_size(payload: payload_len); |
582 | } |
583 | |
584 | static int net_dm_packet_report_in_port_put(struct sk_buff *msg, int ifindex, |
585 | const char *name) |
586 | { |
587 | struct nlattr *attr; |
588 | |
589 | attr = nla_nest_start(skb: msg, attrtype: NET_DM_ATTR_IN_PORT); |
590 | if (!attr) |
591 | return -EMSGSIZE; |
592 | |
593 | if (ifindex && |
594 | nla_put_u32(skb: msg, attrtype: NET_DM_ATTR_PORT_NETDEV_IFINDEX, value: ifindex)) |
595 | goto nla_put_failure; |
596 | |
597 | if (name && nla_put_string(skb: msg, attrtype: NET_DM_ATTR_PORT_NETDEV_NAME, str: name)) |
598 | goto nla_put_failure; |
599 | |
600 | nla_nest_end(skb: msg, start: attr); |
601 | |
602 | return 0; |
603 | |
604 | nla_put_failure: |
605 | nla_nest_cancel(skb: msg, start: attr); |
606 | return -EMSGSIZE; |
607 | } |
608 | |
609 | static int net_dm_packet_report_fill(struct sk_buff *msg, struct sk_buff *skb, |
610 | size_t payload_len) |
611 | { |
612 | struct net_dm_skb_cb *cb = NET_DM_SKB_CB(skb); |
613 | const struct drop_reason_list *list = NULL; |
614 | unsigned int subsys, subsys_reason; |
615 | char buf[NET_DM_MAX_SYMBOL_LEN]; |
616 | struct nlattr *attr; |
617 | void *hdr; |
618 | int rc; |
619 | |
620 | hdr = genlmsg_put(skb: msg, portid: 0, seq: 0, family: &net_drop_monitor_family, flags: 0, |
621 | cmd: NET_DM_CMD_PACKET_ALERT); |
622 | if (!hdr) |
623 | return -EMSGSIZE; |
624 | |
625 | if (nla_put_u16(skb: msg, attrtype: NET_DM_ATTR_ORIGIN, value: NET_DM_ORIGIN_SW)) |
626 | goto nla_put_failure; |
627 | |
628 | if (nla_put_u64_64bit(skb: msg, attrtype: NET_DM_ATTR_PC, value: (u64)(uintptr_t)cb->pc, |
629 | padattr: NET_DM_ATTR_PAD)) |
630 | goto nla_put_failure; |
631 | |
632 | rcu_read_lock(); |
633 | subsys = u32_get_bits(v: cb->reason, field: SKB_DROP_REASON_SUBSYS_MASK); |
634 | if (subsys < SKB_DROP_REASON_SUBSYS_NUM) |
635 | list = rcu_dereference(drop_reasons_by_subsys[subsys]); |
636 | subsys_reason = cb->reason & ~SKB_DROP_REASON_SUBSYS_MASK; |
637 | if (!list || |
638 | subsys_reason >= list->n_reasons || |
639 | !list->reasons[subsys_reason] || |
640 | strlen(list->reasons[subsys_reason]) > NET_DM_MAX_REASON_LEN) { |
641 | list = rcu_dereference(drop_reasons_by_subsys[SKB_DROP_REASON_SUBSYS_CORE]); |
642 | subsys_reason = SKB_DROP_REASON_NOT_SPECIFIED; |
643 | } |
644 | if (nla_put_string(skb: msg, attrtype: NET_DM_ATTR_REASON, |
645 | str: list->reasons[subsys_reason])) { |
646 | rcu_read_unlock(); |
647 | goto nla_put_failure; |
648 | } |
649 | rcu_read_unlock(); |
650 | |
651 | snprintf(buf, size: sizeof(buf), fmt: "%pS" , cb->pc); |
652 | if (nla_put_string(skb: msg, attrtype: NET_DM_ATTR_SYMBOL, str: buf)) |
653 | goto nla_put_failure; |
654 | |
655 | rc = net_dm_packet_report_in_port_put(msg, ifindex: skb->skb_iif, NULL); |
656 | if (rc) |
657 | goto nla_put_failure; |
658 | |
659 | if (nla_put_u64_64bit(skb: msg, attrtype: NET_DM_ATTR_TIMESTAMP, |
660 | value: ktime_to_ns(kt: skb->tstamp), padattr: NET_DM_ATTR_PAD)) |
661 | goto nla_put_failure; |
662 | |
663 | if (nla_put_u32(skb: msg, attrtype: NET_DM_ATTR_ORIG_LEN, value: skb->len)) |
664 | goto nla_put_failure; |
665 | |
666 | if (!payload_len) |
667 | goto out; |
668 | |
669 | if (nla_put_u16(skb: msg, attrtype: NET_DM_ATTR_PROTO, be16_to_cpu(skb->protocol))) |
670 | goto nla_put_failure; |
671 | |
672 | attr = skb_put(skb: msg, len: nla_total_size(payload: payload_len)); |
673 | attr->nla_type = NET_DM_ATTR_PAYLOAD; |
674 | attr->nla_len = nla_attr_size(payload: payload_len); |
675 | if (skb_copy_bits(skb, offset: 0, to: nla_data(nla: attr), len: payload_len)) |
676 | goto nla_put_failure; |
677 | |
678 | out: |
679 | genlmsg_end(skb: msg, hdr); |
680 | |
681 | return 0; |
682 | |
683 | nla_put_failure: |
684 | genlmsg_cancel(skb: msg, hdr); |
685 | return -EMSGSIZE; |
686 | } |
687 | |
688 | #define NET_DM_MAX_PACKET_SIZE (0xffff - NLA_HDRLEN - NLA_ALIGNTO) |
689 | |
690 | static void net_dm_packet_report(struct sk_buff *skb) |
691 | { |
692 | struct sk_buff *msg; |
693 | size_t payload_len; |
694 | int rc; |
695 | |
696 | /* Make sure we start copying the packet from the MAC header */ |
697 | if (skb->data > skb_mac_header(skb)) |
698 | skb_push(skb, len: skb->data - skb_mac_header(skb)); |
699 | else |
700 | skb_pull(skb, len: skb_mac_header(skb) - skb->data); |
701 | |
702 | /* Ensure packet fits inside a single netlink attribute */ |
703 | payload_len = min_t(size_t, skb->len, NET_DM_MAX_PACKET_SIZE); |
704 | if (net_dm_trunc_len) |
705 | payload_len = min_t(size_t, net_dm_trunc_len, payload_len); |
706 | |
707 | msg = nlmsg_new(payload: net_dm_packet_report_size(payload_len), GFP_KERNEL); |
708 | if (!msg) |
709 | goto out; |
710 | |
711 | rc = net_dm_packet_report_fill(msg, skb, payload_len); |
712 | if (rc) { |
713 | nlmsg_free(skb: msg); |
714 | goto out; |
715 | } |
716 | |
717 | genlmsg_multicast(family: &net_drop_monitor_family, skb: msg, portid: 0, group: 0, GFP_KERNEL); |
718 | |
719 | out: |
720 | consume_skb(skb); |
721 | } |
722 | |
723 | static void net_dm_packet_work(struct work_struct *work) |
724 | { |
725 | struct per_cpu_dm_data *data; |
726 | struct sk_buff_head list; |
727 | struct sk_buff *skb; |
728 | unsigned long flags; |
729 | |
730 | data = container_of(work, struct per_cpu_dm_data, dm_alert_work); |
731 | |
732 | __skb_queue_head_init(list: &list); |
733 | |
734 | spin_lock_irqsave(&data->drop_queue.lock, flags); |
735 | skb_queue_splice_tail_init(list: &data->drop_queue, head: &list); |
736 | spin_unlock_irqrestore(lock: &data->drop_queue.lock, flags); |
737 | |
738 | while ((skb = __skb_dequeue(list: &list))) |
739 | net_dm_packet_report(skb); |
740 | } |
741 | |
742 | static size_t |
743 | net_dm_flow_action_cookie_size(const struct devlink_trap_metadata *hw_metadata) |
744 | { |
745 | return hw_metadata->fa_cookie ? |
746 | nla_total_size(payload: hw_metadata->fa_cookie->cookie_len) : 0; |
747 | } |
748 | |
749 | static size_t |
750 | net_dm_hw_packet_report_size(size_t payload_len, |
751 | const struct devlink_trap_metadata *hw_metadata) |
752 | { |
753 | size_t size; |
754 | |
755 | size = nlmsg_msg_size(GENL_HDRLEN + net_drop_monitor_family.hdrsize); |
756 | |
757 | return NLMSG_ALIGN(size) + |
758 | /* NET_DM_ATTR_ORIGIN */ |
759 | nla_total_size(payload: sizeof(u16)) + |
760 | /* NET_DM_ATTR_HW_TRAP_GROUP_NAME */ |
761 | nla_total_size(strlen(hw_metadata->trap_group_name) + 1) + |
762 | /* NET_DM_ATTR_HW_TRAP_NAME */ |
763 | nla_total_size(strlen(hw_metadata->trap_name) + 1) + |
764 | /* NET_DM_ATTR_IN_PORT */ |
765 | net_dm_in_port_size() + |
766 | /* NET_DM_ATTR_FLOW_ACTION_COOKIE */ |
767 | net_dm_flow_action_cookie_size(hw_metadata) + |
768 | /* NET_DM_ATTR_TIMESTAMP */ |
769 | nla_total_size(payload: sizeof(u64)) + |
770 | /* NET_DM_ATTR_ORIG_LEN */ |
771 | nla_total_size(payload: sizeof(u32)) + |
772 | /* NET_DM_ATTR_PROTO */ |
773 | nla_total_size(payload: sizeof(u16)) + |
774 | /* NET_DM_ATTR_PAYLOAD */ |
775 | nla_total_size(payload: payload_len); |
776 | } |
777 | |
778 | static int net_dm_hw_packet_report_fill(struct sk_buff *msg, |
779 | struct sk_buff *skb, size_t payload_len) |
780 | { |
781 | struct devlink_trap_metadata *hw_metadata; |
782 | struct nlattr *attr; |
783 | void *hdr; |
784 | |
785 | hw_metadata = NET_DM_SKB_CB(skb)->hw_metadata; |
786 | |
787 | hdr = genlmsg_put(skb: msg, portid: 0, seq: 0, family: &net_drop_monitor_family, flags: 0, |
788 | cmd: NET_DM_CMD_PACKET_ALERT); |
789 | if (!hdr) |
790 | return -EMSGSIZE; |
791 | |
792 | if (nla_put_u16(skb: msg, attrtype: NET_DM_ATTR_ORIGIN, value: NET_DM_ORIGIN_HW)) |
793 | goto nla_put_failure; |
794 | |
795 | if (nla_put_string(skb: msg, attrtype: NET_DM_ATTR_HW_TRAP_GROUP_NAME, |
796 | str: hw_metadata->trap_group_name)) |
797 | goto nla_put_failure; |
798 | |
799 | if (nla_put_string(skb: msg, attrtype: NET_DM_ATTR_HW_TRAP_NAME, |
800 | str: hw_metadata->trap_name)) |
801 | goto nla_put_failure; |
802 | |
803 | if (hw_metadata->input_dev) { |
804 | struct net_device *dev = hw_metadata->input_dev; |
805 | int rc; |
806 | |
807 | rc = net_dm_packet_report_in_port_put(msg, ifindex: dev->ifindex, |
808 | name: dev->name); |
809 | if (rc) |
810 | goto nla_put_failure; |
811 | } |
812 | |
813 | if (hw_metadata->fa_cookie && |
814 | nla_put(skb: msg, attrtype: NET_DM_ATTR_FLOW_ACTION_COOKIE, |
815 | attrlen: hw_metadata->fa_cookie->cookie_len, |
816 | data: hw_metadata->fa_cookie->cookie)) |
817 | goto nla_put_failure; |
818 | |
819 | if (nla_put_u64_64bit(skb: msg, attrtype: NET_DM_ATTR_TIMESTAMP, |
820 | value: ktime_to_ns(kt: skb->tstamp), padattr: NET_DM_ATTR_PAD)) |
821 | goto nla_put_failure; |
822 | |
823 | if (nla_put_u32(skb: msg, attrtype: NET_DM_ATTR_ORIG_LEN, value: skb->len)) |
824 | goto nla_put_failure; |
825 | |
826 | if (!payload_len) |
827 | goto out; |
828 | |
829 | if (nla_put_u16(skb: msg, attrtype: NET_DM_ATTR_PROTO, be16_to_cpu(skb->protocol))) |
830 | goto nla_put_failure; |
831 | |
832 | attr = skb_put(skb: msg, len: nla_total_size(payload: payload_len)); |
833 | attr->nla_type = NET_DM_ATTR_PAYLOAD; |
834 | attr->nla_len = nla_attr_size(payload: payload_len); |
835 | if (skb_copy_bits(skb, offset: 0, to: nla_data(nla: attr), len: payload_len)) |
836 | goto nla_put_failure; |
837 | |
838 | out: |
839 | genlmsg_end(skb: msg, hdr); |
840 | |
841 | return 0; |
842 | |
843 | nla_put_failure: |
844 | genlmsg_cancel(skb: msg, hdr); |
845 | return -EMSGSIZE; |
846 | } |
847 | |
848 | static struct devlink_trap_metadata * |
849 | net_dm_hw_metadata_copy(const struct devlink_trap_metadata *metadata) |
850 | { |
851 | const struct flow_action_cookie *fa_cookie; |
852 | struct devlink_trap_metadata *hw_metadata; |
853 | const char *trap_group_name; |
854 | const char *trap_name; |
855 | |
856 | hw_metadata = kzalloc(size: sizeof(*hw_metadata), GFP_ATOMIC); |
857 | if (!hw_metadata) |
858 | return NULL; |
859 | |
860 | trap_group_name = kstrdup(s: metadata->trap_group_name, GFP_ATOMIC); |
861 | if (!trap_group_name) |
862 | goto free_hw_metadata; |
863 | hw_metadata->trap_group_name = trap_group_name; |
864 | |
865 | trap_name = kstrdup(s: metadata->trap_name, GFP_ATOMIC); |
866 | if (!trap_name) |
867 | goto free_trap_group; |
868 | hw_metadata->trap_name = trap_name; |
869 | |
870 | if (metadata->fa_cookie) { |
871 | size_t cookie_size = sizeof(*fa_cookie) + |
872 | metadata->fa_cookie->cookie_len; |
873 | |
874 | fa_cookie = kmemdup(p: metadata->fa_cookie, size: cookie_size, |
875 | GFP_ATOMIC); |
876 | if (!fa_cookie) |
877 | goto free_trap_name; |
878 | hw_metadata->fa_cookie = fa_cookie; |
879 | } |
880 | |
881 | hw_metadata->input_dev = metadata->input_dev; |
882 | netdev_hold(dev: hw_metadata->input_dev, tracker: &hw_metadata->dev_tracker, |
883 | GFP_ATOMIC); |
884 | |
885 | return hw_metadata; |
886 | |
887 | free_trap_name: |
888 | kfree(objp: trap_name); |
889 | free_trap_group: |
890 | kfree(objp: trap_group_name); |
891 | free_hw_metadata: |
892 | kfree(objp: hw_metadata); |
893 | return NULL; |
894 | } |
895 | |
896 | static void |
897 | net_dm_hw_metadata_free(struct devlink_trap_metadata *hw_metadata) |
898 | { |
899 | netdev_put(dev: hw_metadata->input_dev, tracker: &hw_metadata->dev_tracker); |
900 | kfree(objp: hw_metadata->fa_cookie); |
901 | kfree(objp: hw_metadata->trap_name); |
902 | kfree(objp: hw_metadata->trap_group_name); |
903 | kfree(objp: hw_metadata); |
904 | } |
905 | |
906 | static void net_dm_hw_packet_report(struct sk_buff *skb) |
907 | { |
908 | struct devlink_trap_metadata *hw_metadata; |
909 | struct sk_buff *msg; |
910 | size_t payload_len; |
911 | int rc; |
912 | |
913 | if (skb->data > skb_mac_header(skb)) |
914 | skb_push(skb, len: skb->data - skb_mac_header(skb)); |
915 | else |
916 | skb_pull(skb, len: skb_mac_header(skb) - skb->data); |
917 | |
918 | payload_len = min_t(size_t, skb->len, NET_DM_MAX_PACKET_SIZE); |
919 | if (net_dm_trunc_len) |
920 | payload_len = min_t(size_t, net_dm_trunc_len, payload_len); |
921 | |
922 | hw_metadata = NET_DM_SKB_CB(skb)->hw_metadata; |
923 | msg = nlmsg_new(payload: net_dm_hw_packet_report_size(payload_len, hw_metadata), |
924 | GFP_KERNEL); |
925 | if (!msg) |
926 | goto out; |
927 | |
928 | rc = net_dm_hw_packet_report_fill(msg, skb, payload_len); |
929 | if (rc) { |
930 | nlmsg_free(skb: msg); |
931 | goto out; |
932 | } |
933 | |
934 | genlmsg_multicast(family: &net_drop_monitor_family, skb: msg, portid: 0, group: 0, GFP_KERNEL); |
935 | |
936 | out: |
937 | net_dm_hw_metadata_free(NET_DM_SKB_CB(skb)->hw_metadata); |
938 | consume_skb(skb); |
939 | } |
940 | |
941 | static void net_dm_hw_packet_work(struct work_struct *work) |
942 | { |
943 | struct per_cpu_dm_data *hw_data; |
944 | struct sk_buff_head list; |
945 | struct sk_buff *skb; |
946 | unsigned long flags; |
947 | |
948 | hw_data = container_of(work, struct per_cpu_dm_data, dm_alert_work); |
949 | |
950 | __skb_queue_head_init(list: &list); |
951 | |
952 | spin_lock_irqsave(&hw_data->drop_queue.lock, flags); |
953 | skb_queue_splice_tail_init(list: &hw_data->drop_queue, head: &list); |
954 | spin_unlock_irqrestore(lock: &hw_data->drop_queue.lock, flags); |
955 | |
956 | while ((skb = __skb_dequeue(list: &list))) |
957 | net_dm_hw_packet_report(skb); |
958 | } |
959 | |
960 | static void |
961 | net_dm_hw_trap_packet_probe(void *ignore, const struct devlink *devlink, |
962 | struct sk_buff *skb, |
963 | const struct devlink_trap_metadata *metadata) |
964 | { |
965 | struct devlink_trap_metadata *n_hw_metadata; |
966 | ktime_t tstamp = ktime_get_real(); |
967 | struct per_cpu_dm_data *hw_data; |
968 | struct sk_buff *nskb; |
969 | unsigned long flags; |
970 | |
971 | if (metadata->trap_type == DEVLINK_TRAP_TYPE_CONTROL) |
972 | return; |
973 | |
974 | if (!skb_mac_header_was_set(skb)) |
975 | return; |
976 | |
977 | nskb = skb_clone(skb, GFP_ATOMIC); |
978 | if (!nskb) |
979 | return; |
980 | |
981 | n_hw_metadata = net_dm_hw_metadata_copy(metadata); |
982 | if (!n_hw_metadata) |
983 | goto free; |
984 | |
985 | NET_DM_SKB_CB(nskb)->hw_metadata = n_hw_metadata; |
986 | nskb->tstamp = tstamp; |
987 | |
988 | hw_data = this_cpu_ptr(&dm_hw_cpu_data); |
989 | |
990 | spin_lock_irqsave(&hw_data->drop_queue.lock, flags); |
991 | if (skb_queue_len(list_: &hw_data->drop_queue) < net_dm_queue_len) |
992 | __skb_queue_tail(list: &hw_data->drop_queue, newsk: nskb); |
993 | else |
994 | goto unlock_free; |
995 | spin_unlock_irqrestore(lock: &hw_data->drop_queue.lock, flags); |
996 | |
997 | schedule_work(work: &hw_data->dm_alert_work); |
998 | |
999 | return; |
1000 | |
1001 | unlock_free: |
1002 | spin_unlock_irqrestore(lock: &hw_data->drop_queue.lock, flags); |
1003 | u64_stats_update_begin(syncp: &hw_data->stats.syncp); |
1004 | u64_stats_inc(p: &hw_data->stats.dropped); |
1005 | u64_stats_update_end(syncp: &hw_data->stats.syncp); |
1006 | net_dm_hw_metadata_free(hw_metadata: n_hw_metadata); |
1007 | free: |
1008 | consume_skb(skb: nskb); |
1009 | } |
1010 | |
1011 | static const struct net_dm_alert_ops net_dm_alert_packet_ops = { |
1012 | .kfree_skb_probe = net_dm_packet_trace_kfree_skb_hit, |
1013 | .napi_poll_probe = net_dm_packet_trace_napi_poll_hit, |
1014 | .work_item_func = net_dm_packet_work, |
1015 | .hw_work_item_func = net_dm_hw_packet_work, |
1016 | .hw_trap_probe = net_dm_hw_trap_packet_probe, |
1017 | }; |
1018 | |
1019 | static const struct net_dm_alert_ops *net_dm_alert_ops_arr[] = { |
1020 | [NET_DM_ALERT_MODE_SUMMARY] = &net_dm_alert_summary_ops, |
1021 | [NET_DM_ALERT_MODE_PACKET] = &net_dm_alert_packet_ops, |
1022 | }; |
1023 | |
1024 | #if IS_ENABLED(CONFIG_NET_DEVLINK) |
1025 | static int net_dm_hw_probe_register(const struct net_dm_alert_ops *ops) |
1026 | { |
1027 | return register_trace_devlink_trap_report(probe: ops->hw_trap_probe, NULL); |
1028 | } |
1029 | |
1030 | static void net_dm_hw_probe_unregister(const struct net_dm_alert_ops *ops) |
1031 | { |
1032 | unregister_trace_devlink_trap_report(probe: ops->hw_trap_probe, NULL); |
1033 | tracepoint_synchronize_unregister(); |
1034 | } |
1035 | #else |
1036 | static int net_dm_hw_probe_register(const struct net_dm_alert_ops *ops) |
1037 | { |
1038 | return -EOPNOTSUPP; |
1039 | } |
1040 | |
1041 | static void net_dm_hw_probe_unregister(const struct net_dm_alert_ops *ops) |
1042 | { |
1043 | } |
1044 | #endif |
1045 | |
1046 | static int net_dm_hw_monitor_start(struct netlink_ext_ack *extack) |
1047 | { |
1048 | const struct net_dm_alert_ops *ops; |
1049 | int cpu, rc; |
1050 | |
1051 | if (monitor_hw) { |
1052 | NL_SET_ERR_MSG_MOD(extack, "Hardware monitoring already enabled" ); |
1053 | return -EAGAIN; |
1054 | } |
1055 | |
1056 | ops = net_dm_alert_ops_arr[net_dm_alert_mode]; |
1057 | |
1058 | if (!try_module_get(THIS_MODULE)) { |
1059 | NL_SET_ERR_MSG_MOD(extack, "Failed to take reference on module" ); |
1060 | return -ENODEV; |
1061 | } |
1062 | |
1063 | for_each_possible_cpu(cpu) { |
1064 | struct per_cpu_dm_data *hw_data = &per_cpu(dm_hw_cpu_data, cpu); |
1065 | struct net_dm_hw_entries *hw_entries; |
1066 | |
1067 | INIT_WORK(&hw_data->dm_alert_work, ops->hw_work_item_func); |
1068 | timer_setup(&hw_data->send_timer, sched_send_work, 0); |
1069 | hw_entries = net_dm_hw_reset_per_cpu_data(hw_data); |
1070 | kfree(objp: hw_entries); |
1071 | } |
1072 | |
1073 | rc = net_dm_hw_probe_register(ops); |
1074 | if (rc) { |
1075 | NL_SET_ERR_MSG_MOD(extack, "Failed to connect probe to devlink_trap_probe() tracepoint" ); |
1076 | goto err_module_put; |
1077 | } |
1078 | |
1079 | monitor_hw = true; |
1080 | |
1081 | return 0; |
1082 | |
1083 | err_module_put: |
1084 | for_each_possible_cpu(cpu) { |
1085 | struct per_cpu_dm_data *hw_data = &per_cpu(dm_hw_cpu_data, cpu); |
1086 | struct sk_buff *skb; |
1087 | |
1088 | del_timer_sync(timer: &hw_data->send_timer); |
1089 | cancel_work_sync(work: &hw_data->dm_alert_work); |
1090 | while ((skb = __skb_dequeue(list: &hw_data->drop_queue))) { |
1091 | struct devlink_trap_metadata *hw_metadata; |
1092 | |
1093 | hw_metadata = NET_DM_SKB_CB(skb)->hw_metadata; |
1094 | net_dm_hw_metadata_free(hw_metadata); |
1095 | consume_skb(skb); |
1096 | } |
1097 | } |
1098 | module_put(THIS_MODULE); |
1099 | return rc; |
1100 | } |
1101 | |
1102 | static void net_dm_hw_monitor_stop(struct netlink_ext_ack *extack) |
1103 | { |
1104 | const struct net_dm_alert_ops *ops; |
1105 | int cpu; |
1106 | |
1107 | if (!monitor_hw) { |
1108 | NL_SET_ERR_MSG_MOD(extack, "Hardware monitoring already disabled" ); |
1109 | return; |
1110 | } |
1111 | |
1112 | ops = net_dm_alert_ops_arr[net_dm_alert_mode]; |
1113 | |
1114 | monitor_hw = false; |
1115 | |
1116 | net_dm_hw_probe_unregister(ops); |
1117 | |
1118 | for_each_possible_cpu(cpu) { |
1119 | struct per_cpu_dm_data *hw_data = &per_cpu(dm_hw_cpu_data, cpu); |
1120 | struct sk_buff *skb; |
1121 | |
1122 | del_timer_sync(timer: &hw_data->send_timer); |
1123 | cancel_work_sync(work: &hw_data->dm_alert_work); |
1124 | while ((skb = __skb_dequeue(list: &hw_data->drop_queue))) { |
1125 | struct devlink_trap_metadata *hw_metadata; |
1126 | |
1127 | hw_metadata = NET_DM_SKB_CB(skb)->hw_metadata; |
1128 | net_dm_hw_metadata_free(hw_metadata); |
1129 | consume_skb(skb); |
1130 | } |
1131 | } |
1132 | |
1133 | module_put(THIS_MODULE); |
1134 | } |
1135 | |
1136 | static int net_dm_trace_on_set(struct netlink_ext_ack *extack) |
1137 | { |
1138 | const struct net_dm_alert_ops *ops; |
1139 | int cpu, rc; |
1140 | |
1141 | ops = net_dm_alert_ops_arr[net_dm_alert_mode]; |
1142 | |
1143 | if (!try_module_get(THIS_MODULE)) { |
1144 | NL_SET_ERR_MSG_MOD(extack, "Failed to take reference on module" ); |
1145 | return -ENODEV; |
1146 | } |
1147 | |
1148 | for_each_possible_cpu(cpu) { |
1149 | struct per_cpu_dm_data *data = &per_cpu(dm_cpu_data, cpu); |
1150 | struct sk_buff *skb; |
1151 | |
1152 | INIT_WORK(&data->dm_alert_work, ops->work_item_func); |
1153 | timer_setup(&data->send_timer, sched_send_work, 0); |
1154 | /* Allocate a new per-CPU skb for the summary alert message and |
1155 | * free the old one which might contain stale data from |
1156 | * previous tracing. |
1157 | */ |
1158 | skb = reset_per_cpu_data(data); |
1159 | consume_skb(skb); |
1160 | } |
1161 | |
1162 | rc = register_trace_kfree_skb(probe: ops->kfree_skb_probe, NULL); |
1163 | if (rc) { |
1164 | NL_SET_ERR_MSG_MOD(extack, "Failed to connect probe to kfree_skb() tracepoint" ); |
1165 | goto err_module_put; |
1166 | } |
1167 | |
1168 | rc = register_trace_napi_poll(probe: ops->napi_poll_probe, NULL); |
1169 | if (rc) { |
1170 | NL_SET_ERR_MSG_MOD(extack, "Failed to connect probe to napi_poll() tracepoint" ); |
1171 | goto err_unregister_trace; |
1172 | } |
1173 | |
1174 | return 0; |
1175 | |
1176 | err_unregister_trace: |
1177 | unregister_trace_kfree_skb(probe: ops->kfree_skb_probe, NULL); |
1178 | err_module_put: |
1179 | for_each_possible_cpu(cpu) { |
1180 | struct per_cpu_dm_data *data = &per_cpu(dm_cpu_data, cpu); |
1181 | struct sk_buff *skb; |
1182 | |
1183 | del_timer_sync(timer: &data->send_timer); |
1184 | cancel_work_sync(work: &data->dm_alert_work); |
1185 | while ((skb = __skb_dequeue(list: &data->drop_queue))) |
1186 | consume_skb(skb); |
1187 | } |
1188 | module_put(THIS_MODULE); |
1189 | return rc; |
1190 | } |
1191 | |
1192 | static void net_dm_trace_off_set(void) |
1193 | { |
1194 | const struct net_dm_alert_ops *ops; |
1195 | int cpu; |
1196 | |
1197 | ops = net_dm_alert_ops_arr[net_dm_alert_mode]; |
1198 | |
1199 | unregister_trace_napi_poll(probe: ops->napi_poll_probe, NULL); |
1200 | unregister_trace_kfree_skb(probe: ops->kfree_skb_probe, NULL); |
1201 | |
1202 | tracepoint_synchronize_unregister(); |
1203 | |
1204 | /* Make sure we do not send notifications to user space after request |
1205 | * to stop tracing returns. |
1206 | */ |
1207 | for_each_possible_cpu(cpu) { |
1208 | struct per_cpu_dm_data *data = &per_cpu(dm_cpu_data, cpu); |
1209 | struct sk_buff *skb; |
1210 | |
1211 | del_timer_sync(timer: &data->send_timer); |
1212 | cancel_work_sync(work: &data->dm_alert_work); |
1213 | while ((skb = __skb_dequeue(list: &data->drop_queue))) |
1214 | consume_skb(skb); |
1215 | } |
1216 | |
1217 | module_put(THIS_MODULE); |
1218 | } |
1219 | |
1220 | static int set_all_monitor_traces(int state, struct netlink_ext_ack *extack) |
1221 | { |
1222 | int rc = 0; |
1223 | |
1224 | if (state == trace_state) { |
1225 | NL_SET_ERR_MSG_MOD(extack, "Trace state already set to requested state" ); |
1226 | return -EAGAIN; |
1227 | } |
1228 | |
1229 | switch (state) { |
1230 | case TRACE_ON: |
1231 | rc = net_dm_trace_on_set(extack); |
1232 | break; |
1233 | case TRACE_OFF: |
1234 | net_dm_trace_off_set(); |
1235 | break; |
1236 | default: |
1237 | rc = 1; |
1238 | break; |
1239 | } |
1240 | |
1241 | if (!rc) |
1242 | trace_state = state; |
1243 | else |
1244 | rc = -EINPROGRESS; |
1245 | |
1246 | return rc; |
1247 | } |
1248 | |
1249 | static bool net_dm_is_monitoring(void) |
1250 | { |
1251 | return trace_state == TRACE_ON || monitor_hw; |
1252 | } |
1253 | |
1254 | static int net_dm_alert_mode_get_from_info(struct genl_info *info, |
1255 | enum net_dm_alert_mode *p_alert_mode) |
1256 | { |
1257 | u8 val; |
1258 | |
1259 | val = nla_get_u8(nla: info->attrs[NET_DM_ATTR_ALERT_MODE]); |
1260 | |
1261 | switch (val) { |
1262 | case NET_DM_ALERT_MODE_SUMMARY: |
1263 | case NET_DM_ALERT_MODE_PACKET: |
1264 | *p_alert_mode = val; |
1265 | break; |
1266 | default: |
1267 | return -EINVAL; |
1268 | } |
1269 | |
1270 | return 0; |
1271 | } |
1272 | |
1273 | static int net_dm_alert_mode_set(struct genl_info *info) |
1274 | { |
1275 | struct netlink_ext_ack *extack = info->extack; |
1276 | enum net_dm_alert_mode alert_mode; |
1277 | int rc; |
1278 | |
1279 | if (!info->attrs[NET_DM_ATTR_ALERT_MODE]) |
1280 | return 0; |
1281 | |
1282 | rc = net_dm_alert_mode_get_from_info(info, p_alert_mode: &alert_mode); |
1283 | if (rc) { |
1284 | NL_SET_ERR_MSG_MOD(extack, "Invalid alert mode" ); |
1285 | return -EINVAL; |
1286 | } |
1287 | |
1288 | net_dm_alert_mode = alert_mode; |
1289 | |
1290 | return 0; |
1291 | } |
1292 | |
1293 | static void net_dm_trunc_len_set(struct genl_info *info) |
1294 | { |
1295 | if (!info->attrs[NET_DM_ATTR_TRUNC_LEN]) |
1296 | return; |
1297 | |
1298 | net_dm_trunc_len = nla_get_u32(nla: info->attrs[NET_DM_ATTR_TRUNC_LEN]); |
1299 | } |
1300 | |
1301 | static void net_dm_queue_len_set(struct genl_info *info) |
1302 | { |
1303 | if (!info->attrs[NET_DM_ATTR_QUEUE_LEN]) |
1304 | return; |
1305 | |
1306 | net_dm_queue_len = nla_get_u32(nla: info->attrs[NET_DM_ATTR_QUEUE_LEN]); |
1307 | } |
1308 | |
1309 | static int net_dm_cmd_config(struct sk_buff *skb, |
1310 | struct genl_info *info) |
1311 | { |
1312 | struct netlink_ext_ack *extack = info->extack; |
1313 | int rc; |
1314 | |
1315 | if (net_dm_is_monitoring()) { |
1316 | NL_SET_ERR_MSG_MOD(extack, "Cannot configure drop monitor during monitoring" ); |
1317 | return -EBUSY; |
1318 | } |
1319 | |
1320 | rc = net_dm_alert_mode_set(info); |
1321 | if (rc) |
1322 | return rc; |
1323 | |
1324 | net_dm_trunc_len_set(info); |
1325 | |
1326 | net_dm_queue_len_set(info); |
1327 | |
1328 | return 0; |
1329 | } |
1330 | |
1331 | static int net_dm_monitor_start(bool set_sw, bool set_hw, |
1332 | struct netlink_ext_ack *extack) |
1333 | { |
1334 | bool sw_set = false; |
1335 | int rc; |
1336 | |
1337 | if (set_sw) { |
1338 | rc = set_all_monitor_traces(TRACE_ON, extack); |
1339 | if (rc) |
1340 | return rc; |
1341 | sw_set = true; |
1342 | } |
1343 | |
1344 | if (set_hw) { |
1345 | rc = net_dm_hw_monitor_start(extack); |
1346 | if (rc) |
1347 | goto err_monitor_hw; |
1348 | } |
1349 | |
1350 | return 0; |
1351 | |
1352 | err_monitor_hw: |
1353 | if (sw_set) |
1354 | set_all_monitor_traces(TRACE_OFF, extack); |
1355 | return rc; |
1356 | } |
1357 | |
1358 | static void net_dm_monitor_stop(bool set_sw, bool set_hw, |
1359 | struct netlink_ext_ack *extack) |
1360 | { |
1361 | if (set_hw) |
1362 | net_dm_hw_monitor_stop(extack); |
1363 | if (set_sw) |
1364 | set_all_monitor_traces(TRACE_OFF, extack); |
1365 | } |
1366 | |
1367 | static int net_dm_cmd_trace(struct sk_buff *skb, |
1368 | struct genl_info *info) |
1369 | { |
1370 | bool set_sw = !!info->attrs[NET_DM_ATTR_SW_DROPS]; |
1371 | bool set_hw = !!info->attrs[NET_DM_ATTR_HW_DROPS]; |
1372 | struct netlink_ext_ack *extack = info->extack; |
1373 | |
1374 | /* To maintain backward compatibility, we start / stop monitoring of |
1375 | * software drops if no flag is specified. |
1376 | */ |
1377 | if (!set_sw && !set_hw) |
1378 | set_sw = true; |
1379 | |
1380 | switch (info->genlhdr->cmd) { |
1381 | case NET_DM_CMD_START: |
1382 | return net_dm_monitor_start(set_sw, set_hw, extack); |
1383 | case NET_DM_CMD_STOP: |
1384 | net_dm_monitor_stop(set_sw, set_hw, extack); |
1385 | return 0; |
1386 | } |
1387 | |
1388 | return -EOPNOTSUPP; |
1389 | } |
1390 | |
1391 | static int net_dm_config_fill(struct sk_buff *msg, struct genl_info *info) |
1392 | { |
1393 | void *hdr; |
1394 | |
1395 | hdr = genlmsg_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, |
1396 | family: &net_drop_monitor_family, flags: 0, cmd: NET_DM_CMD_CONFIG_NEW); |
1397 | if (!hdr) |
1398 | return -EMSGSIZE; |
1399 | |
1400 | if (nla_put_u8(skb: msg, attrtype: NET_DM_ATTR_ALERT_MODE, value: net_dm_alert_mode)) |
1401 | goto nla_put_failure; |
1402 | |
1403 | if (nla_put_u32(skb: msg, attrtype: NET_DM_ATTR_TRUNC_LEN, value: net_dm_trunc_len)) |
1404 | goto nla_put_failure; |
1405 | |
1406 | if (nla_put_u32(skb: msg, attrtype: NET_DM_ATTR_QUEUE_LEN, value: net_dm_queue_len)) |
1407 | goto nla_put_failure; |
1408 | |
1409 | genlmsg_end(skb: msg, hdr); |
1410 | |
1411 | return 0; |
1412 | |
1413 | nla_put_failure: |
1414 | genlmsg_cancel(skb: msg, hdr); |
1415 | return -EMSGSIZE; |
1416 | } |
1417 | |
1418 | static int net_dm_cmd_config_get(struct sk_buff *skb, struct genl_info *info) |
1419 | { |
1420 | struct sk_buff *msg; |
1421 | int rc; |
1422 | |
1423 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
1424 | if (!msg) |
1425 | return -ENOMEM; |
1426 | |
1427 | rc = net_dm_config_fill(msg, info); |
1428 | if (rc) |
1429 | goto free_msg; |
1430 | |
1431 | return genlmsg_reply(skb: msg, info); |
1432 | |
1433 | free_msg: |
1434 | nlmsg_free(skb: msg); |
1435 | return rc; |
1436 | } |
1437 | |
1438 | static void net_dm_stats_read(struct net_dm_stats *stats) |
1439 | { |
1440 | int cpu; |
1441 | |
1442 | memset(stats, 0, sizeof(*stats)); |
1443 | for_each_possible_cpu(cpu) { |
1444 | struct per_cpu_dm_data *data = &per_cpu(dm_cpu_data, cpu); |
1445 | struct net_dm_stats *cpu_stats = &data->stats; |
1446 | unsigned int start; |
1447 | u64 dropped; |
1448 | |
1449 | do { |
1450 | start = u64_stats_fetch_begin(syncp: &cpu_stats->syncp); |
1451 | dropped = u64_stats_read(p: &cpu_stats->dropped); |
1452 | } while (u64_stats_fetch_retry(syncp: &cpu_stats->syncp, start)); |
1453 | |
1454 | u64_stats_add(p: &stats->dropped, val: dropped); |
1455 | } |
1456 | } |
1457 | |
1458 | static int net_dm_stats_put(struct sk_buff *msg) |
1459 | { |
1460 | struct net_dm_stats stats; |
1461 | struct nlattr *attr; |
1462 | |
1463 | net_dm_stats_read(stats: &stats); |
1464 | |
1465 | attr = nla_nest_start(skb: msg, attrtype: NET_DM_ATTR_STATS); |
1466 | if (!attr) |
1467 | return -EMSGSIZE; |
1468 | |
1469 | if (nla_put_u64_64bit(skb: msg, attrtype: NET_DM_ATTR_STATS_DROPPED, |
1470 | value: u64_stats_read(p: &stats.dropped), padattr: NET_DM_ATTR_PAD)) |
1471 | goto nla_put_failure; |
1472 | |
1473 | nla_nest_end(skb: msg, start: attr); |
1474 | |
1475 | return 0; |
1476 | |
1477 | nla_put_failure: |
1478 | nla_nest_cancel(skb: msg, start: attr); |
1479 | return -EMSGSIZE; |
1480 | } |
1481 | |
1482 | static void net_dm_hw_stats_read(struct net_dm_stats *stats) |
1483 | { |
1484 | int cpu; |
1485 | |
1486 | memset(stats, 0, sizeof(*stats)); |
1487 | for_each_possible_cpu(cpu) { |
1488 | struct per_cpu_dm_data *hw_data = &per_cpu(dm_hw_cpu_data, cpu); |
1489 | struct net_dm_stats *cpu_stats = &hw_data->stats; |
1490 | unsigned int start; |
1491 | u64 dropped; |
1492 | |
1493 | do { |
1494 | start = u64_stats_fetch_begin(syncp: &cpu_stats->syncp); |
1495 | dropped = u64_stats_read(p: &cpu_stats->dropped); |
1496 | } while (u64_stats_fetch_retry(syncp: &cpu_stats->syncp, start)); |
1497 | |
1498 | u64_stats_add(p: &stats->dropped, val: dropped); |
1499 | } |
1500 | } |
1501 | |
1502 | static int net_dm_hw_stats_put(struct sk_buff *msg) |
1503 | { |
1504 | struct net_dm_stats stats; |
1505 | struct nlattr *attr; |
1506 | |
1507 | net_dm_hw_stats_read(stats: &stats); |
1508 | |
1509 | attr = nla_nest_start(skb: msg, attrtype: NET_DM_ATTR_HW_STATS); |
1510 | if (!attr) |
1511 | return -EMSGSIZE; |
1512 | |
1513 | if (nla_put_u64_64bit(skb: msg, attrtype: NET_DM_ATTR_STATS_DROPPED, |
1514 | value: u64_stats_read(p: &stats.dropped), padattr: NET_DM_ATTR_PAD)) |
1515 | goto nla_put_failure; |
1516 | |
1517 | nla_nest_end(skb: msg, start: attr); |
1518 | |
1519 | return 0; |
1520 | |
1521 | nla_put_failure: |
1522 | nla_nest_cancel(skb: msg, start: attr); |
1523 | return -EMSGSIZE; |
1524 | } |
1525 | |
1526 | static int net_dm_stats_fill(struct sk_buff *msg, struct genl_info *info) |
1527 | { |
1528 | void *hdr; |
1529 | int rc; |
1530 | |
1531 | hdr = genlmsg_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, |
1532 | family: &net_drop_monitor_family, flags: 0, cmd: NET_DM_CMD_STATS_NEW); |
1533 | if (!hdr) |
1534 | return -EMSGSIZE; |
1535 | |
1536 | rc = net_dm_stats_put(msg); |
1537 | if (rc) |
1538 | goto nla_put_failure; |
1539 | |
1540 | rc = net_dm_hw_stats_put(msg); |
1541 | if (rc) |
1542 | goto nla_put_failure; |
1543 | |
1544 | genlmsg_end(skb: msg, hdr); |
1545 | |
1546 | return 0; |
1547 | |
1548 | nla_put_failure: |
1549 | genlmsg_cancel(skb: msg, hdr); |
1550 | return -EMSGSIZE; |
1551 | } |
1552 | |
1553 | static int net_dm_cmd_stats_get(struct sk_buff *skb, struct genl_info *info) |
1554 | { |
1555 | struct sk_buff *msg; |
1556 | int rc; |
1557 | |
1558 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
1559 | if (!msg) |
1560 | return -ENOMEM; |
1561 | |
1562 | rc = net_dm_stats_fill(msg, info); |
1563 | if (rc) |
1564 | goto free_msg; |
1565 | |
1566 | return genlmsg_reply(skb: msg, info); |
1567 | |
1568 | free_msg: |
1569 | nlmsg_free(skb: msg); |
1570 | return rc; |
1571 | } |
1572 | |
1573 | static int dropmon_net_event(struct notifier_block *ev_block, |
1574 | unsigned long event, void *ptr) |
1575 | { |
1576 | struct net_device *dev = netdev_notifier_info_to_dev(info: ptr); |
1577 | struct dm_hw_stat_delta *stat; |
1578 | |
1579 | switch (event) { |
1580 | case NETDEV_REGISTER: |
1581 | if (WARN_ON_ONCE(rtnl_dereference(dev->dm_private))) |
1582 | break; |
1583 | stat = kzalloc(size: sizeof(*stat), GFP_KERNEL); |
1584 | if (!stat) |
1585 | break; |
1586 | |
1587 | stat->last_rx = jiffies; |
1588 | rcu_assign_pointer(dev->dm_private, stat); |
1589 | |
1590 | break; |
1591 | case NETDEV_UNREGISTER: |
1592 | stat = rtnl_dereference(dev->dm_private); |
1593 | if (stat) { |
1594 | rcu_assign_pointer(dev->dm_private, NULL); |
1595 | kfree_rcu(stat, rcu); |
1596 | } |
1597 | break; |
1598 | } |
1599 | return NOTIFY_DONE; |
1600 | } |
1601 | |
1602 | static const struct nla_policy net_dm_nl_policy[NET_DM_ATTR_MAX + 1] = { |
1603 | [NET_DM_ATTR_UNSPEC] = { .strict_start_type = NET_DM_ATTR_UNSPEC + 1 }, |
1604 | [NET_DM_ATTR_ALERT_MODE] = { .type = NLA_U8 }, |
1605 | [NET_DM_ATTR_TRUNC_LEN] = { .type = NLA_U32 }, |
1606 | [NET_DM_ATTR_QUEUE_LEN] = { .type = NLA_U32 }, |
1607 | [NET_DM_ATTR_SW_DROPS] = {. type = NLA_FLAG }, |
1608 | [NET_DM_ATTR_HW_DROPS] = {. type = NLA_FLAG }, |
1609 | }; |
1610 | |
1611 | static const struct genl_small_ops dropmon_ops[] = { |
1612 | { |
1613 | .cmd = NET_DM_CMD_CONFIG, |
1614 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1615 | .doit = net_dm_cmd_config, |
1616 | .flags = GENL_ADMIN_PERM, |
1617 | }, |
1618 | { |
1619 | .cmd = NET_DM_CMD_START, |
1620 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1621 | .doit = net_dm_cmd_trace, |
1622 | }, |
1623 | { |
1624 | .cmd = NET_DM_CMD_STOP, |
1625 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1626 | .doit = net_dm_cmd_trace, |
1627 | }, |
1628 | { |
1629 | .cmd = NET_DM_CMD_CONFIG_GET, |
1630 | .doit = net_dm_cmd_config_get, |
1631 | }, |
1632 | { |
1633 | .cmd = NET_DM_CMD_STATS_GET, |
1634 | .doit = net_dm_cmd_stats_get, |
1635 | }, |
1636 | }; |
1637 | |
1638 | static int net_dm_nl_pre_doit(const struct genl_split_ops *ops, |
1639 | struct sk_buff *skb, struct genl_info *info) |
1640 | { |
1641 | mutex_lock(&net_dm_mutex); |
1642 | |
1643 | return 0; |
1644 | } |
1645 | |
1646 | static void net_dm_nl_post_doit(const struct genl_split_ops *ops, |
1647 | struct sk_buff *skb, struct genl_info *info) |
1648 | { |
1649 | mutex_unlock(lock: &net_dm_mutex); |
1650 | } |
1651 | |
1652 | static struct genl_family net_drop_monitor_family __ro_after_init = { |
1653 | .hdrsize = 0, |
1654 | .name = "NET_DM" , |
1655 | .version = 2, |
1656 | .maxattr = NET_DM_ATTR_MAX, |
1657 | .policy = net_dm_nl_policy, |
1658 | .pre_doit = net_dm_nl_pre_doit, |
1659 | .post_doit = net_dm_nl_post_doit, |
1660 | .module = THIS_MODULE, |
1661 | .small_ops = dropmon_ops, |
1662 | .n_small_ops = ARRAY_SIZE(dropmon_ops), |
1663 | .resv_start_op = NET_DM_CMD_STATS_GET + 1, |
1664 | .mcgrps = dropmon_mcgrps, |
1665 | .n_mcgrps = ARRAY_SIZE(dropmon_mcgrps), |
1666 | }; |
1667 | |
1668 | static struct notifier_block dropmon_net_notifier = { |
1669 | .notifier_call = dropmon_net_event |
1670 | }; |
1671 | |
1672 | static void __net_dm_cpu_data_init(struct per_cpu_dm_data *data) |
1673 | { |
1674 | spin_lock_init(&data->lock); |
1675 | skb_queue_head_init(list: &data->drop_queue); |
1676 | u64_stats_init(syncp: &data->stats.syncp); |
1677 | } |
1678 | |
1679 | static void __net_dm_cpu_data_fini(struct per_cpu_dm_data *data) |
1680 | { |
1681 | WARN_ON(!skb_queue_empty(&data->drop_queue)); |
1682 | } |
1683 | |
1684 | static void net_dm_cpu_data_init(int cpu) |
1685 | { |
1686 | struct per_cpu_dm_data *data; |
1687 | |
1688 | data = &per_cpu(dm_cpu_data, cpu); |
1689 | __net_dm_cpu_data_init(data); |
1690 | } |
1691 | |
1692 | static void net_dm_cpu_data_fini(int cpu) |
1693 | { |
1694 | struct per_cpu_dm_data *data; |
1695 | |
1696 | data = &per_cpu(dm_cpu_data, cpu); |
1697 | /* At this point, we should have exclusive access |
1698 | * to this struct and can free the skb inside it. |
1699 | */ |
1700 | consume_skb(skb: data->skb); |
1701 | __net_dm_cpu_data_fini(data); |
1702 | } |
1703 | |
1704 | static void net_dm_hw_cpu_data_init(int cpu) |
1705 | { |
1706 | struct per_cpu_dm_data *hw_data; |
1707 | |
1708 | hw_data = &per_cpu(dm_hw_cpu_data, cpu); |
1709 | __net_dm_cpu_data_init(data: hw_data); |
1710 | } |
1711 | |
1712 | static void net_dm_hw_cpu_data_fini(int cpu) |
1713 | { |
1714 | struct per_cpu_dm_data *hw_data; |
1715 | |
1716 | hw_data = &per_cpu(dm_hw_cpu_data, cpu); |
1717 | kfree(objp: hw_data->hw_entries); |
1718 | __net_dm_cpu_data_fini(data: hw_data); |
1719 | } |
1720 | |
1721 | static int __init init_net_drop_monitor(void) |
1722 | { |
1723 | int cpu, rc; |
1724 | |
1725 | pr_info("Initializing network drop monitor service\n" ); |
1726 | |
1727 | if (sizeof(void *) > 8) { |
1728 | pr_err("Unable to store program counters on this arch, Drop monitor failed\n" ); |
1729 | return -ENOSPC; |
1730 | } |
1731 | |
1732 | rc = genl_register_family(family: &net_drop_monitor_family); |
1733 | if (rc) { |
1734 | pr_err("Could not create drop monitor netlink family\n" ); |
1735 | return rc; |
1736 | } |
1737 | WARN_ON(net_drop_monitor_family.mcgrp_offset != NET_DM_GRP_ALERT); |
1738 | |
1739 | rc = register_netdevice_notifier(nb: &dropmon_net_notifier); |
1740 | if (rc < 0) { |
1741 | pr_crit("Failed to register netdevice notifier\n" ); |
1742 | goto out_unreg; |
1743 | } |
1744 | |
1745 | rc = 0; |
1746 | |
1747 | for_each_possible_cpu(cpu) { |
1748 | net_dm_cpu_data_init(cpu); |
1749 | net_dm_hw_cpu_data_init(cpu); |
1750 | } |
1751 | |
1752 | goto out; |
1753 | |
1754 | out_unreg: |
1755 | genl_unregister_family(family: &net_drop_monitor_family); |
1756 | out: |
1757 | return rc; |
1758 | } |
1759 | |
1760 | static void exit_net_drop_monitor(void) |
1761 | { |
1762 | int cpu; |
1763 | |
1764 | BUG_ON(unregister_netdevice_notifier(&dropmon_net_notifier)); |
1765 | |
1766 | /* |
1767 | * Because of the module_get/put we do in the trace state change path |
1768 | * we are guaranteed not to have any current users when we get here |
1769 | */ |
1770 | |
1771 | for_each_possible_cpu(cpu) { |
1772 | net_dm_hw_cpu_data_fini(cpu); |
1773 | net_dm_cpu_data_fini(cpu); |
1774 | } |
1775 | |
1776 | BUG_ON(genl_unregister_family(&net_drop_monitor_family)); |
1777 | } |
1778 | |
1779 | module_init(init_net_drop_monitor); |
1780 | module_exit(exit_net_drop_monitor); |
1781 | |
1782 | MODULE_LICENSE("GPL v2" ); |
1783 | MODULE_AUTHOR("Neil Horman <nhorman@tuxdriver.com>" ); |
1784 | MODULE_ALIAS_GENL_FAMILY("NET_DM" ); |
1785 | MODULE_DESCRIPTION("Monitoring code for network dropped packet alerts" ); |
1786 | |