109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29a8afc8dSNeil Horman /*
39a8afc8dSNeil Horman * Monitoring code for network dropped packet alerts
49a8afc8dSNeil Horman *
59a8afc8dSNeil Horman * Copyright (C) 2009 Neil Horman <nhorman@tuxdriver.com>
69a8afc8dSNeil Horman */
79a8afc8dSNeil Horman
8e005d193SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9e005d193SJoe Perches
109a8afc8dSNeil Horman #include <linux/netdevice.h>
119a8afc8dSNeil Horman #include <linux/etherdevice.h>
129a8afc8dSNeil Horman #include <linux/string.h>
139a8afc8dSNeil Horman #include <linux/if_arp.h>
149a8afc8dSNeil Horman #include <linux/inetdevice.h>
159a8afc8dSNeil Horman #include <linux/inet.h>
169a8afc8dSNeil Horman #include <linux/interrupt.h>
179a8afc8dSNeil Horman #include <linux/netpoll.h>
189a8afc8dSNeil Horman #include <linux/sched.h>
199a8afc8dSNeil Horman #include <linux/delay.h>
209a8afc8dSNeil Horman #include <linux/types.h>
219a8afc8dSNeil Horman #include <linux/workqueue.h>
229a8afc8dSNeil Horman #include <linux/netlink.h>
239a8afc8dSNeil Horman #include <linux/net_dropmon.h>
24071c0fc6SJohannes Berg #include <linux/bitfield.h>
259a8afc8dSNeil Horman #include <linux/percpu.h>
269a8afc8dSNeil Horman #include <linux/timer.h>
279a8afc8dSNeil Horman #include <linux/bitops.h>
285a0e3ad6STejun Heo #include <linux/slab.h>
29cad456d5SNeil Horman #include <linux/module.h>
309a8afc8dSNeil Horman #include <net/genetlink.h>
314ea7e386SNeil Horman #include <net/netevent.h>
32742b8cceSJiri Pirko #include <net/flow_offload.h>
33071c0fc6SJohannes Berg #include <net/dropreason.h>
345855357cSIdo Schimmel #include <net/devlink.h>
359a8afc8dSNeil Horman
36ad8d75ffSSteven Rostedt #include <trace/events/skb.h>
379cbc1cb8SDavid S. Miller #include <trace/events/napi.h>
388ee2267aSIdo Schimmel #include <trace/events/devlink.h>
399a8afc8dSNeil Horman
409a8afc8dSNeil Horman #include <asm/unaligned.h>
419a8afc8dSNeil Horman
429a8afc8dSNeil Horman #define TRACE_ON 1
439a8afc8dSNeil Horman #define TRACE_OFF 0
449a8afc8dSNeil Horman
459a8afc8dSNeil Horman /*
469a8afc8dSNeil Horman * Globals, our netlink socket pointer
479a8afc8dSNeil Horman * and the work handle that will send up
489a8afc8dSNeil Horman * netlink alerts
499a8afc8dSNeil Horman */
504ea7e386SNeil Horman static int trace_state = TRACE_OFF;
51edd3d007SIdo Schimmel static bool monitor_hw;
52dbf896b7SIdo Schimmel
53dbf896b7SIdo Schimmel /* net_dm_mutex
54dbf896b7SIdo Schimmel *
55dbf896b7SIdo Schimmel * An overall lock guarding every operation coming from userspace.
56dbf896b7SIdo Schimmel */
57dbf896b7SIdo Schimmel static DEFINE_MUTEX(net_dm_mutex);
589a8afc8dSNeil Horman
59e9feb580SIdo Schimmel struct net_dm_stats {
60c6cce71eSEric Dumazet u64_stats_t dropped;
61e9feb580SIdo Schimmel struct u64_stats_sync syncp;
62e9feb580SIdo Schimmel };
63e9feb580SIdo Schimmel
64d40e1debSIdo Schimmel #define NET_DM_MAX_HW_TRAP_NAME_LEN 40
65d40e1debSIdo Schimmel
66d40e1debSIdo Schimmel struct net_dm_hw_entry {
67d40e1debSIdo Schimmel char trap_name[NET_DM_MAX_HW_TRAP_NAME_LEN];
68d40e1debSIdo Schimmel u32 count;
69d40e1debSIdo Schimmel };
70d40e1debSIdo Schimmel
71d40e1debSIdo Schimmel struct net_dm_hw_entries {
72d40e1debSIdo Schimmel u32 num_entries;
73d2afb41aSGustavo A. R. Silva struct net_dm_hw_entry entries[];
74d40e1debSIdo Schimmel };
75d40e1debSIdo Schimmel
769a8afc8dSNeil Horman struct per_cpu_dm_data {
7776ce2f91SWander Lairson Costa raw_spinlock_t lock; /* Protects 'skb', 'hw_entries' and
78d40e1debSIdo Schimmel * 'send_timer'
79d40e1debSIdo Schimmel */
80d40e1debSIdo Schimmel union {
81bec4596bSEric Dumazet struct sk_buff *skb;
82d40e1debSIdo Schimmel struct net_dm_hw_entries *hw_entries;
83d40e1debSIdo Schimmel };
84ca30707dSIdo Schimmel struct sk_buff_head drop_queue;
859a8afc8dSNeil Horman struct work_struct dm_alert_work;
869a8afc8dSNeil Horman struct timer_list send_timer;
87e9feb580SIdo Schimmel struct net_dm_stats stats;
889a8afc8dSNeil Horman };
899a8afc8dSNeil Horman
904ea7e386SNeil Horman struct dm_hw_stat_delta {
915848cc09SNeil Horman unsigned long last_rx;
924ea7e386SNeil Horman unsigned long last_drop_val;
93b26ef81cSEric Dumazet struct rcu_head rcu;
944ea7e386SNeil Horman };
954ea7e386SNeil Horman
96489111e5SJohannes Berg static struct genl_family net_drop_monitor_family;
979a8afc8dSNeil Horman
989a8afc8dSNeil Horman static DEFINE_PER_CPU(struct per_cpu_dm_data, dm_cpu_data);
99cac1174fSIdo Schimmel static DEFINE_PER_CPU(struct per_cpu_dm_data, dm_hw_cpu_data);
1009a8afc8dSNeil Horman
1019a8afc8dSNeil Horman static int dm_hit_limit = 64;
1029a8afc8dSNeil Horman static int dm_delay = 1;
1034ea7e386SNeil Horman static unsigned long dm_hw_check_delta = 2*HZ;
1049a8afc8dSNeil Horman
10528315f79SIdo Schimmel static enum net_dm_alert_mode net_dm_alert_mode = NET_DM_ALERT_MODE_SUMMARY;
10657986617SIdo Schimmel static u32 net_dm_trunc_len;
10730328d46SIdo Schimmel static u32 net_dm_queue_len = 1000;
10828315f79SIdo Schimmel
10928315f79SIdo Schimmel struct net_dm_alert_ops {
11028315f79SIdo Schimmel void (*kfree_skb_probe)(void *ignore, struct sk_buff *skb,
111c504e5c2SMenglong Dong void *location,
112c504e5c2SMenglong Dong enum skb_drop_reason reason);
11328315f79SIdo Schimmel void (*napi_poll_probe)(void *ignore, struct napi_struct *napi,
11428315f79SIdo Schimmel int work, int budget);
11528315f79SIdo Schimmel void (*work_item_func)(struct work_struct *work);
1165e58109bSIdo Schimmel void (*hw_work_item_func)(struct work_struct *work);
1175855357cSIdo Schimmel void (*hw_trap_probe)(void *ignore, const struct devlink *devlink,
1185855357cSIdo Schimmel struct sk_buff *skb,
1195855357cSIdo Schimmel const struct devlink_trap_metadata *metadata);
12028315f79SIdo Schimmel };
12128315f79SIdo Schimmel
122ca30707dSIdo Schimmel struct net_dm_skb_cb {
1235e58109bSIdo Schimmel union {
124a848c05fSIdo Schimmel struct devlink_trap_metadata *hw_metadata;
125ca30707dSIdo Schimmel void *pc;
126ca30707dSIdo Schimmel };
1275cad527dSMenglong Dong enum skb_drop_reason reason;
1285e58109bSIdo Schimmel };
129ca30707dSIdo Schimmel
130ca30707dSIdo Schimmel #define NET_DM_SKB_CB(__skb) ((struct net_dm_skb_cb *)&((__skb)->cb[0]))
131ca30707dSIdo Schimmel
reset_per_cpu_data(struct per_cpu_dm_data * data)132bec4596bSEric Dumazet static struct sk_buff *reset_per_cpu_data(struct per_cpu_dm_data *data)
1339a8afc8dSNeil Horman {
1349a8afc8dSNeil Horman size_t al;
1359a8afc8dSNeil Horman struct net_dm_alert_msg *msg;
136683703a2SNeil Horman struct nlattr *nla;
1373885ca78SNeil Horman struct sk_buff *skb;
138bec4596bSEric Dumazet unsigned long flags;
1394200462dSReiter Wolfgang void *msg_header;
1409a8afc8dSNeil Horman
1419a8afc8dSNeil Horman al = sizeof(struct net_dm_alert_msg);
1429a8afc8dSNeil Horman al += dm_hit_limit * sizeof(struct net_dm_drop_point);
143683703a2SNeil Horman al += sizeof(struct nlattr);
144683703a2SNeil Horman
1453885ca78SNeil Horman skb = genlmsg_new(al, GFP_KERNEL);
1463885ca78SNeil Horman
1474200462dSReiter Wolfgang if (!skb)
1484200462dSReiter Wolfgang goto err;
1494200462dSReiter Wolfgang
1504200462dSReiter Wolfgang msg_header = genlmsg_put(skb, 0, 0, &net_drop_monitor_family,
1519a8afc8dSNeil Horman 0, NET_DM_CMD_ALERT);
1524200462dSReiter Wolfgang if (!msg_header) {
1534200462dSReiter Wolfgang nlmsg_free(skb);
1544200462dSReiter Wolfgang skb = NULL;
1554200462dSReiter Wolfgang goto err;
1564200462dSReiter Wolfgang }
1573885ca78SNeil Horman nla = nla_reserve(skb, NLA_UNSPEC,
1583885ca78SNeil Horman sizeof(struct net_dm_alert_msg));
1594200462dSReiter Wolfgang if (!nla) {
1604200462dSReiter Wolfgang nlmsg_free(skb);
1614200462dSReiter Wolfgang skb = NULL;
1624200462dSReiter Wolfgang goto err;
1634200462dSReiter Wolfgang }
164683703a2SNeil Horman msg = nla_data(nla);
1659a8afc8dSNeil Horman memset(msg, 0, al);
1664200462dSReiter Wolfgang goto out;
1679a8afc8dSNeil Horman
1684200462dSReiter Wolfgang err:
1694200462dSReiter Wolfgang mod_timer(&data->send_timer, jiffies + HZ / 10);
1704200462dSReiter Wolfgang out:
17176ce2f91SWander Lairson Costa raw_spin_lock_irqsave(&data->lock, flags);
172bec4596bSEric Dumazet swap(data->skb, skb);
17376ce2f91SWander Lairson Costa raw_spin_unlock_irqrestore(&data->lock, flags);
174bec4596bSEric Dumazet
1753b48ab22SReiter Wolfgang if (skb) {
1763b48ab22SReiter Wolfgang struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data;
1773b48ab22SReiter Wolfgang struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlh);
1783b48ab22SReiter Wolfgang
1793b48ab22SReiter Wolfgang genlmsg_end(skb, genlmsg_data(gnlh));
1803b48ab22SReiter Wolfgang }
1813b48ab22SReiter Wolfgang
182bec4596bSEric Dumazet return skb;
1833885ca78SNeil Horman }
1843885ca78SNeil Horman
18585bae4bdSstephen hemminger static const struct genl_multicast_group dropmon_mcgrps[] = {
186e036a325SIdo Schimmel { .name = "events", .cap_sys_admin = 1 },
187e5dcecbaSJohannes Berg };
188e5dcecbaSJohannes Berg
send_dm_alert(struct work_struct * work)189bec4596bSEric Dumazet static void send_dm_alert(struct work_struct *work)
1909a8afc8dSNeil Horman {
1919a8afc8dSNeil Horman struct sk_buff *skb;
192bec4596bSEric Dumazet struct per_cpu_dm_data *data;
1939a8afc8dSNeil Horman
194bec4596bSEric Dumazet data = container_of(work, struct per_cpu_dm_data, dm_alert_work);
1954fdcfa12SNeil Horman
196bec4596bSEric Dumazet skb = reset_per_cpu_data(data);
1979a8afc8dSNeil Horman
1983885ca78SNeil Horman if (skb)
19968eb5503SJohannes Berg genlmsg_multicast(&net_drop_monitor_family, skb, 0,
2002a94fe48SJohannes Berg 0, GFP_KERNEL);
2019a8afc8dSNeil Horman }
2029a8afc8dSNeil Horman
2039a8afc8dSNeil Horman /*
2049a8afc8dSNeil Horman * This is the timer function to delay the sending of an alert
2059a8afc8dSNeil Horman * in the event that more drops will arrive during the
206bec4596bSEric Dumazet * hysteresis period.
2079a8afc8dSNeil Horman */
sched_send_work(struct timer_list * t)208e99e88a9SKees Cook static void sched_send_work(struct timer_list *t)
2099a8afc8dSNeil Horman {
210e99e88a9SKees Cook struct per_cpu_dm_data *data = from_timer(data, t, send_timer);
2119a8afc8dSNeil Horman
212bec4596bSEric Dumazet schedule_work(&data->dm_alert_work);
2139a8afc8dSNeil Horman }
2149a8afc8dSNeil Horman
trace_drop_common(struct sk_buff * skb,void * location)2154ea7e386SNeil Horman static void trace_drop_common(struct sk_buff *skb, void *location)
2169a8afc8dSNeil Horman {
2179a8afc8dSNeil Horman struct net_dm_alert_msg *msg;
218dc30b405SArnd Bergmann struct net_dm_drop_point *point;
2199a8afc8dSNeil Horman struct nlmsghdr *nlh;
220683703a2SNeil Horman struct nlattr *nla;
2219a8afc8dSNeil Horman int i;
2223885ca78SNeil Horman struct sk_buff *dskb;
223bec4596bSEric Dumazet struct per_cpu_dm_data *data;
224bec4596bSEric Dumazet unsigned long flags;
2259a8afc8dSNeil Horman
226bec4596bSEric Dumazet local_irq_save(flags);
227903ceff7SChristoph Lameter data = this_cpu_ptr(&dm_cpu_data);
22876ce2f91SWander Lairson Costa raw_spin_lock(&data->lock);
229bec4596bSEric Dumazet dskb = data->skb;
2303885ca78SNeil Horman
2313885ca78SNeil Horman if (!dskb)
2323885ca78SNeil Horman goto out;
2333885ca78SNeil Horman
2343885ca78SNeil Horman nlh = (struct nlmsghdr *)dskb->data;
235683703a2SNeil Horman nla = genlmsg_data(nlmsg_data(nlh));
236683703a2SNeil Horman msg = nla_data(nla);
237dc30b405SArnd Bergmann point = msg->points;
2389a8afc8dSNeil Horman for (i = 0; i < msg->entries; i++) {
239dc30b405SArnd Bergmann if (!memcmp(&location, &point->pc, sizeof(void *))) {
240dc30b405SArnd Bergmann point->count++;
2419a8afc8dSNeil Horman goto out;
2429a8afc8dSNeil Horman }
243dc30b405SArnd Bergmann point++;
2449a8afc8dSNeil Horman }
245bec4596bSEric Dumazet if (msg->entries == dm_hit_limit)
246bec4596bSEric Dumazet goto out;
2479a8afc8dSNeil Horman /*
2489a8afc8dSNeil Horman * We need to create a new entry
2499a8afc8dSNeil Horman */
2503885ca78SNeil Horman __nla_reserve_nohdr(dskb, sizeof(struct net_dm_drop_point));
251683703a2SNeil Horman nla->nla_len += NLA_ALIGN(sizeof(struct net_dm_drop_point));
252dc30b405SArnd Bergmann memcpy(point->pc, &location, sizeof(void *));
253dc30b405SArnd Bergmann point->count = 1;
2549a8afc8dSNeil Horman msg->entries++;
2559a8afc8dSNeil Horman
2569a8afc8dSNeil Horman if (!timer_pending(&data->send_timer)) {
2579a8afc8dSNeil Horman data->send_timer.expires = jiffies + dm_delay * HZ;
258bec4596bSEric Dumazet add_timer(&data->send_timer);
2599a8afc8dSNeil Horman }
2609a8afc8dSNeil Horman
2619a8afc8dSNeil Horman out:
26276ce2f91SWander Lairson Costa raw_spin_unlock_irqrestore(&data->lock, flags);
2639a8afc8dSNeil Horman }
2649a8afc8dSNeil Horman
trace_kfree_skb_hit(void * ignore,struct sk_buff * skb,void * location,enum skb_drop_reason reason)265c504e5c2SMenglong Dong static void trace_kfree_skb_hit(void *ignore, struct sk_buff *skb,
266c504e5c2SMenglong Dong void *location,
267c504e5c2SMenglong Dong enum skb_drop_reason reason)
2684ea7e386SNeil Horman {
2694ea7e386SNeil Horman trace_drop_common(skb, location);
2704ea7e386SNeil Horman }
2714ea7e386SNeil Horman
trace_napi_poll_hit(void * ignore,struct napi_struct * napi,int work,int budget)2721db19db7SJesper Dangaard Brouer static void trace_napi_poll_hit(void *ignore, struct napi_struct *napi,
2731db19db7SJesper Dangaard Brouer int work, int budget)
2744ea7e386SNeil Horman {
275b26ef81cSEric Dumazet struct net_device *dev = napi->dev;
276b26ef81cSEric Dumazet struct dm_hw_stat_delta *stat;
2774ea7e386SNeil Horman /*
2785848cc09SNeil Horman * Don't check napi structures with no associated device
2794ea7e386SNeil Horman */
280b26ef81cSEric Dumazet if (!dev)
2814ea7e386SNeil Horman return;
2824ea7e386SNeil Horman
2834ea7e386SNeil Horman rcu_read_lock();
284b26ef81cSEric Dumazet stat = rcu_dereference(dev->dm_private);
285b26ef81cSEric Dumazet if (stat) {
2865848cc09SNeil Horman /*
2875848cc09SNeil Horman * only add a note to our monitor buffer if:
288b26ef81cSEric Dumazet * 1) its after the last_rx delta
289b26ef81cSEric Dumazet * 2) our rx_dropped count has gone up
2905848cc09SNeil Horman */
291b26ef81cSEric Dumazet if (time_after(jiffies, stat->last_rx + dm_hw_check_delta) &&
292b26ef81cSEric Dumazet (dev->stats.rx_dropped != stat->last_drop_val)) {
2934ea7e386SNeil Horman trace_drop_common(NULL, NULL);
294b26ef81cSEric Dumazet stat->last_drop_val = dev->stats.rx_dropped;
295b26ef81cSEric Dumazet stat->last_rx = jiffies;
2964ea7e386SNeil Horman }
2974ea7e386SNeil Horman }
2984ea7e386SNeil Horman rcu_read_unlock();
2994ea7e386SNeil Horman }
3004ea7e386SNeil Horman
301d40e1debSIdo Schimmel static struct net_dm_hw_entries *
net_dm_hw_reset_per_cpu_data(struct per_cpu_dm_data * hw_data)302d40e1debSIdo Schimmel net_dm_hw_reset_per_cpu_data(struct per_cpu_dm_data *hw_data)
303d40e1debSIdo Schimmel {
304d40e1debSIdo Schimmel struct net_dm_hw_entries *hw_entries;
305d40e1debSIdo Schimmel unsigned long flags;
306d40e1debSIdo Schimmel
307d40e1debSIdo Schimmel hw_entries = kzalloc(struct_size(hw_entries, entries, dm_hit_limit),
308d40e1debSIdo Schimmel GFP_KERNEL);
309d40e1debSIdo Schimmel if (!hw_entries) {
310d40e1debSIdo Schimmel /* If the memory allocation failed, we try to perform another
311d40e1debSIdo Schimmel * allocation in 1/10 second. Otherwise, the probe function
312d40e1debSIdo Schimmel * will constantly bail out.
313d40e1debSIdo Schimmel */
314d40e1debSIdo Schimmel mod_timer(&hw_data->send_timer, jiffies + HZ / 10);
315d40e1debSIdo Schimmel }
316d40e1debSIdo Schimmel
31776ce2f91SWander Lairson Costa raw_spin_lock_irqsave(&hw_data->lock, flags);
318d40e1debSIdo Schimmel swap(hw_data->hw_entries, hw_entries);
31976ce2f91SWander Lairson Costa raw_spin_unlock_irqrestore(&hw_data->lock, flags);
320d40e1debSIdo Schimmel
321d40e1debSIdo Schimmel return hw_entries;
322d40e1debSIdo Schimmel }
323d40e1debSIdo Schimmel
net_dm_hw_entry_put(struct sk_buff * msg,const struct net_dm_hw_entry * hw_entry)324d40e1debSIdo Schimmel static int net_dm_hw_entry_put(struct sk_buff *msg,
325d40e1debSIdo Schimmel const struct net_dm_hw_entry *hw_entry)
326d40e1debSIdo Schimmel {
327d40e1debSIdo Schimmel struct nlattr *attr;
328d40e1debSIdo Schimmel
329d40e1debSIdo Schimmel attr = nla_nest_start(msg, NET_DM_ATTR_HW_ENTRY);
330d40e1debSIdo Schimmel if (!attr)
331d40e1debSIdo Schimmel return -EMSGSIZE;
332d40e1debSIdo Schimmel
333d40e1debSIdo Schimmel if (nla_put_string(msg, NET_DM_ATTR_HW_TRAP_NAME, hw_entry->trap_name))
334d40e1debSIdo Schimmel goto nla_put_failure;
335d40e1debSIdo Schimmel
336d40e1debSIdo Schimmel if (nla_put_u32(msg, NET_DM_ATTR_HW_TRAP_COUNT, hw_entry->count))
337d40e1debSIdo Schimmel goto nla_put_failure;
338d40e1debSIdo Schimmel
339d40e1debSIdo Schimmel nla_nest_end(msg, attr);
340d40e1debSIdo Schimmel
341d40e1debSIdo Schimmel return 0;
342d40e1debSIdo Schimmel
343d40e1debSIdo Schimmel nla_put_failure:
344d40e1debSIdo Schimmel nla_nest_cancel(msg, attr);
345d40e1debSIdo Schimmel return -EMSGSIZE;
346d40e1debSIdo Schimmel }
347d40e1debSIdo Schimmel
net_dm_hw_entries_put(struct sk_buff * msg,const struct net_dm_hw_entries * hw_entries)348d40e1debSIdo Schimmel static int net_dm_hw_entries_put(struct sk_buff *msg,
349d40e1debSIdo Schimmel const struct net_dm_hw_entries *hw_entries)
350d40e1debSIdo Schimmel {
351d40e1debSIdo Schimmel struct nlattr *attr;
352d40e1debSIdo Schimmel int i;
353d40e1debSIdo Schimmel
354d40e1debSIdo Schimmel attr = nla_nest_start(msg, NET_DM_ATTR_HW_ENTRIES);
355d40e1debSIdo Schimmel if (!attr)
356d40e1debSIdo Schimmel return -EMSGSIZE;
357d40e1debSIdo Schimmel
358d40e1debSIdo Schimmel for (i = 0; i < hw_entries->num_entries; i++) {
359d40e1debSIdo Schimmel int rc;
360d40e1debSIdo Schimmel
361d40e1debSIdo Schimmel rc = net_dm_hw_entry_put(msg, &hw_entries->entries[i]);
362d40e1debSIdo Schimmel if (rc)
363d40e1debSIdo Schimmel goto nla_put_failure;
364d40e1debSIdo Schimmel }
365d40e1debSIdo Schimmel
366d40e1debSIdo Schimmel nla_nest_end(msg, attr);
367d40e1debSIdo Schimmel
368d40e1debSIdo Schimmel return 0;
369d40e1debSIdo Schimmel
370d40e1debSIdo Schimmel nla_put_failure:
371d40e1debSIdo Schimmel nla_nest_cancel(msg, attr);
372d40e1debSIdo Schimmel return -EMSGSIZE;
373d40e1debSIdo Schimmel }
374d40e1debSIdo Schimmel
375d40e1debSIdo Schimmel static int
net_dm_hw_summary_report_fill(struct sk_buff * msg,const struct net_dm_hw_entries * hw_entries)376d40e1debSIdo Schimmel net_dm_hw_summary_report_fill(struct sk_buff *msg,
377d40e1debSIdo Schimmel const struct net_dm_hw_entries *hw_entries)
378d40e1debSIdo Schimmel {
379d40e1debSIdo Schimmel struct net_dm_alert_msg anc_hdr = { 0 };
380d40e1debSIdo Schimmel void *hdr;
381d40e1debSIdo Schimmel int rc;
382d40e1debSIdo Schimmel
383d40e1debSIdo Schimmel hdr = genlmsg_put(msg, 0, 0, &net_drop_monitor_family, 0,
384d40e1debSIdo Schimmel NET_DM_CMD_ALERT);
385d40e1debSIdo Schimmel if (!hdr)
386d40e1debSIdo Schimmel return -EMSGSIZE;
387d40e1debSIdo Schimmel
388d40e1debSIdo Schimmel /* We need to put the ancillary header in order not to break user
389d40e1debSIdo Schimmel * space.
390d40e1debSIdo Schimmel */
391d40e1debSIdo Schimmel if (nla_put(msg, NLA_UNSPEC, sizeof(anc_hdr), &anc_hdr))
392d40e1debSIdo Schimmel goto nla_put_failure;
393d40e1debSIdo Schimmel
394d40e1debSIdo Schimmel rc = net_dm_hw_entries_put(msg, hw_entries);
395d40e1debSIdo Schimmel if (rc)
396d40e1debSIdo Schimmel goto nla_put_failure;
397d40e1debSIdo Schimmel
398d40e1debSIdo Schimmel genlmsg_end(msg, hdr);
399d40e1debSIdo Schimmel
400d40e1debSIdo Schimmel return 0;
401d40e1debSIdo Schimmel
402d40e1debSIdo Schimmel nla_put_failure:
403d40e1debSIdo Schimmel genlmsg_cancel(msg, hdr);
404d40e1debSIdo Schimmel return -EMSGSIZE;
405d40e1debSIdo Schimmel }
406d40e1debSIdo Schimmel
net_dm_hw_summary_work(struct work_struct * work)407d40e1debSIdo Schimmel static void net_dm_hw_summary_work(struct work_struct *work)
408d40e1debSIdo Schimmel {
409d40e1debSIdo Schimmel struct net_dm_hw_entries *hw_entries;
410d40e1debSIdo Schimmel struct per_cpu_dm_data *hw_data;
411d40e1debSIdo Schimmel struct sk_buff *msg;
412d40e1debSIdo Schimmel int rc;
413d40e1debSIdo Schimmel
414d40e1debSIdo Schimmel hw_data = container_of(work, struct per_cpu_dm_data, dm_alert_work);
415d40e1debSIdo Schimmel
416d40e1debSIdo Schimmel hw_entries = net_dm_hw_reset_per_cpu_data(hw_data);
417d40e1debSIdo Schimmel if (!hw_entries)
418d40e1debSIdo Schimmel return;
419d40e1debSIdo Schimmel
420d40e1debSIdo Schimmel msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
421d40e1debSIdo Schimmel if (!msg)
422d40e1debSIdo Schimmel goto out;
423d40e1debSIdo Schimmel
424d40e1debSIdo Schimmel rc = net_dm_hw_summary_report_fill(msg, hw_entries);
425d40e1debSIdo Schimmel if (rc) {
426d40e1debSIdo Schimmel nlmsg_free(msg);
427d40e1debSIdo Schimmel goto out;
428d40e1debSIdo Schimmel }
429d40e1debSIdo Schimmel
430d40e1debSIdo Schimmel genlmsg_multicast(&net_drop_monitor_family, msg, 0, 0, GFP_KERNEL);
431d40e1debSIdo Schimmel
432d40e1debSIdo Schimmel out:
433d40e1debSIdo Schimmel kfree(hw_entries);
434d40e1debSIdo Schimmel }
435d40e1debSIdo Schimmel
436edd3d007SIdo Schimmel static void
net_dm_hw_trap_summary_probe(void * ignore,const struct devlink * devlink,struct sk_buff * skb,const struct devlink_trap_metadata * metadata)4375855357cSIdo Schimmel net_dm_hw_trap_summary_probe(void *ignore, const struct devlink *devlink,
4385855357cSIdo Schimmel struct sk_buff *skb,
4395855357cSIdo Schimmel const struct devlink_trap_metadata *metadata)
4405855357cSIdo Schimmel {
4415855357cSIdo Schimmel struct net_dm_hw_entries *hw_entries;
4425855357cSIdo Schimmel struct net_dm_hw_entry *hw_entry;
4435855357cSIdo Schimmel struct per_cpu_dm_data *hw_data;
4445855357cSIdo Schimmel unsigned long flags;
4455855357cSIdo Schimmel int i;
4465855357cSIdo Schimmel
44793e15596SIdo Schimmel if (metadata->trap_type == DEVLINK_TRAP_TYPE_CONTROL)
44893e15596SIdo Schimmel return;
44993e15596SIdo Schimmel
4505855357cSIdo Schimmel hw_data = this_cpu_ptr(&dm_hw_cpu_data);
45176ce2f91SWander Lairson Costa raw_spin_lock_irqsave(&hw_data->lock, flags);
4525855357cSIdo Schimmel hw_entries = hw_data->hw_entries;
4535855357cSIdo Schimmel
4545855357cSIdo Schimmel if (!hw_entries)
4555855357cSIdo Schimmel goto out;
4565855357cSIdo Schimmel
4575855357cSIdo Schimmel for (i = 0; i < hw_entries->num_entries; i++) {
4585855357cSIdo Schimmel hw_entry = &hw_entries->entries[i];
4595855357cSIdo Schimmel if (!strncmp(hw_entry->trap_name, metadata->trap_name,
4605855357cSIdo Schimmel NET_DM_MAX_HW_TRAP_NAME_LEN - 1)) {
4615855357cSIdo Schimmel hw_entry->count++;
4625855357cSIdo Schimmel goto out;
4635855357cSIdo Schimmel }
4645855357cSIdo Schimmel }
4655855357cSIdo Schimmel if (WARN_ON_ONCE(hw_entries->num_entries == dm_hit_limit))
4665855357cSIdo Schimmel goto out;
4675855357cSIdo Schimmel
4685855357cSIdo Schimmel hw_entry = &hw_entries->entries[hw_entries->num_entries];
46970986397SWolfram Sang strscpy(hw_entry->trap_name, metadata->trap_name,
4705855357cSIdo Schimmel NET_DM_MAX_HW_TRAP_NAME_LEN - 1);
4715855357cSIdo Schimmel hw_entry->count = 1;
4725855357cSIdo Schimmel hw_entries->num_entries++;
4735855357cSIdo Schimmel
4745855357cSIdo Schimmel if (!timer_pending(&hw_data->send_timer)) {
4755855357cSIdo Schimmel hw_data->send_timer.expires = jiffies + dm_delay * HZ;
4765855357cSIdo Schimmel add_timer(&hw_data->send_timer);
4775855357cSIdo Schimmel }
4785855357cSIdo Schimmel
4795855357cSIdo Schimmel out:
48076ce2f91SWander Lairson Costa raw_spin_unlock_irqrestore(&hw_data->lock, flags);
4815855357cSIdo Schimmel }
4825855357cSIdo Schimmel
48328315f79SIdo Schimmel static const struct net_dm_alert_ops net_dm_alert_summary_ops = {
48428315f79SIdo Schimmel .kfree_skb_probe = trace_kfree_skb_hit,
48528315f79SIdo Schimmel .napi_poll_probe = trace_napi_poll_hit,
48628315f79SIdo Schimmel .work_item_func = send_dm_alert,
487d40e1debSIdo Schimmel .hw_work_item_func = net_dm_hw_summary_work,
4885855357cSIdo Schimmel .hw_trap_probe = net_dm_hw_trap_summary_probe,
48928315f79SIdo Schimmel };
49028315f79SIdo Schimmel
net_dm_packet_trace_kfree_skb_hit(void * ignore,struct sk_buff * skb,void * location,enum skb_drop_reason reason)491ca30707dSIdo Schimmel static void net_dm_packet_trace_kfree_skb_hit(void *ignore,
492ca30707dSIdo Schimmel struct sk_buff *skb,
493c504e5c2SMenglong Dong void *location,
494c504e5c2SMenglong Dong enum skb_drop_reason reason)
495ca30707dSIdo Schimmel {
496ca30707dSIdo Schimmel ktime_t tstamp = ktime_get_real();
497ca30707dSIdo Schimmel struct per_cpu_dm_data *data;
4985cad527dSMenglong Dong struct net_dm_skb_cb *cb;
499ca30707dSIdo Schimmel struct sk_buff *nskb;
500ca30707dSIdo Schimmel unsigned long flags;
501ca30707dSIdo Schimmel
502bef17466SIdo Schimmel if (!skb_mac_header_was_set(skb))
503bef17466SIdo Schimmel return;
504bef17466SIdo Schimmel
505ca30707dSIdo Schimmel nskb = skb_clone(skb, GFP_ATOMIC);
506ca30707dSIdo Schimmel if (!nskb)
507ca30707dSIdo Schimmel return;
508ca30707dSIdo Schimmel
5095cad527dSMenglong Dong cb = NET_DM_SKB_CB(nskb);
5105cad527dSMenglong Dong cb->reason = reason;
5115cad527dSMenglong Dong cb->pc = location;
512ca30707dSIdo Schimmel /* Override the timestamp because we care about the time when the
513ca30707dSIdo Schimmel * packet was dropped.
514ca30707dSIdo Schimmel */
515ca30707dSIdo Schimmel nskb->tstamp = tstamp;
516ca30707dSIdo Schimmel
517ca30707dSIdo Schimmel data = this_cpu_ptr(&dm_cpu_data);
518ca30707dSIdo Schimmel
519ca30707dSIdo Schimmel spin_lock_irqsave(&data->drop_queue.lock, flags);
52030328d46SIdo Schimmel if (skb_queue_len(&data->drop_queue) < net_dm_queue_len)
521ca30707dSIdo Schimmel __skb_queue_tail(&data->drop_queue, nskb);
522ca30707dSIdo Schimmel else
523ca30707dSIdo Schimmel goto unlock_free;
524ca30707dSIdo Schimmel spin_unlock_irqrestore(&data->drop_queue.lock, flags);
525ca30707dSIdo Schimmel
526ca30707dSIdo Schimmel schedule_work(&data->dm_alert_work);
527ca30707dSIdo Schimmel
528ca30707dSIdo Schimmel return;
529ca30707dSIdo Schimmel
530ca30707dSIdo Schimmel unlock_free:
531ca30707dSIdo Schimmel spin_unlock_irqrestore(&data->drop_queue.lock, flags);
532e9feb580SIdo Schimmel u64_stats_update_begin(&data->stats.syncp);
533c6cce71eSEric Dumazet u64_stats_inc(&data->stats.dropped);
534e9feb580SIdo Schimmel u64_stats_update_end(&data->stats.syncp);
535ca30707dSIdo Schimmel consume_skb(nskb);
536ca30707dSIdo Schimmel }
537ca30707dSIdo Schimmel
net_dm_packet_trace_napi_poll_hit(void * ignore,struct napi_struct * napi,int work,int budget)538ca30707dSIdo Schimmel static void net_dm_packet_trace_napi_poll_hit(void *ignore,
539ca30707dSIdo Schimmel struct napi_struct *napi,
540ca30707dSIdo Schimmel int work, int budget)
541ca30707dSIdo Schimmel {
542ca30707dSIdo Schimmel }
543ca30707dSIdo Schimmel
net_dm_in_port_size(void)544ca30707dSIdo Schimmel static size_t net_dm_in_port_size(void)
545ca30707dSIdo Schimmel {
546ca30707dSIdo Schimmel /* NET_DM_ATTR_IN_PORT nest */
547ca30707dSIdo Schimmel return nla_total_size(0) +
548ca30707dSIdo Schimmel /* NET_DM_ATTR_PORT_NETDEV_IFINDEX */
5495e58109bSIdo Schimmel nla_total_size(sizeof(u32)) +
5505e58109bSIdo Schimmel /* NET_DM_ATTR_PORT_NETDEV_NAME */
5515e58109bSIdo Schimmel nla_total_size(IFNAMSIZ + 1);
552ca30707dSIdo Schimmel }
553ca30707dSIdo Schimmel
554ca30707dSIdo Schimmel #define NET_DM_MAX_SYMBOL_LEN 40
555071c0fc6SJohannes Berg #define NET_DM_MAX_REASON_LEN 50
556ca30707dSIdo Schimmel
net_dm_packet_report_size(size_t payload_len)557071c0fc6SJohannes Berg static size_t net_dm_packet_report_size(size_t payload_len)
558ca30707dSIdo Schimmel {
559ca30707dSIdo Schimmel size_t size;
560ca30707dSIdo Schimmel
561ca30707dSIdo Schimmel size = nlmsg_msg_size(GENL_HDRLEN + net_drop_monitor_family.hdrsize);
562ca30707dSIdo Schimmel
563ca30707dSIdo Schimmel return NLMSG_ALIGN(size) +
5645e58109bSIdo Schimmel /* NET_DM_ATTR_ORIGIN */
5655e58109bSIdo Schimmel nla_total_size(sizeof(u16)) +
566ca30707dSIdo Schimmel /* NET_DM_ATTR_PC */
567ca30707dSIdo Schimmel nla_total_size(sizeof(u64)) +
568ca30707dSIdo Schimmel /* NET_DM_ATTR_SYMBOL */
569ca30707dSIdo Schimmel nla_total_size(NET_DM_MAX_SYMBOL_LEN + 1) +
570ca30707dSIdo Schimmel /* NET_DM_ATTR_IN_PORT */
571ca30707dSIdo Schimmel net_dm_in_port_size() +
572ca30707dSIdo Schimmel /* NET_DM_ATTR_TIMESTAMP */
573bd1200b7SIdo Schimmel nla_total_size(sizeof(u64)) +
57457986617SIdo Schimmel /* NET_DM_ATTR_ORIG_LEN */
57557986617SIdo Schimmel nla_total_size(sizeof(u32)) +
576ca30707dSIdo Schimmel /* NET_DM_ATTR_PROTO */
577ca30707dSIdo Schimmel nla_total_size(sizeof(u16)) +
5785cad527dSMenglong Dong /* NET_DM_ATTR_REASON */
579071c0fc6SJohannes Berg nla_total_size(NET_DM_MAX_REASON_LEN + 1) +
580ca30707dSIdo Schimmel /* NET_DM_ATTR_PAYLOAD */
581ca30707dSIdo Schimmel nla_total_size(payload_len);
582ca30707dSIdo Schimmel }
583ca30707dSIdo Schimmel
net_dm_packet_report_in_port_put(struct sk_buff * msg,int ifindex,const char * name)5845e58109bSIdo Schimmel static int net_dm_packet_report_in_port_put(struct sk_buff *msg, int ifindex,
5855e58109bSIdo Schimmel const char *name)
586ca30707dSIdo Schimmel {
587ca30707dSIdo Schimmel struct nlattr *attr;
588ca30707dSIdo Schimmel
589ca30707dSIdo Schimmel attr = nla_nest_start(msg, NET_DM_ATTR_IN_PORT);
590ca30707dSIdo Schimmel if (!attr)
591ca30707dSIdo Schimmel return -EMSGSIZE;
592ca30707dSIdo Schimmel
593ca30707dSIdo Schimmel if (ifindex &&
594ca30707dSIdo Schimmel nla_put_u32(msg, NET_DM_ATTR_PORT_NETDEV_IFINDEX, ifindex))
595ca30707dSIdo Schimmel goto nla_put_failure;
596ca30707dSIdo Schimmel
5975e58109bSIdo Schimmel if (name && nla_put_string(msg, NET_DM_ATTR_PORT_NETDEV_NAME, name))
5985e58109bSIdo Schimmel goto nla_put_failure;
5995e58109bSIdo Schimmel
600ca30707dSIdo Schimmel nla_nest_end(msg, attr);
601ca30707dSIdo Schimmel
602ca30707dSIdo Schimmel return 0;
603ca30707dSIdo Schimmel
604ca30707dSIdo Schimmel nla_put_failure:
605ca30707dSIdo Schimmel nla_nest_cancel(msg, attr);
606ca30707dSIdo Schimmel return -EMSGSIZE;
607ca30707dSIdo Schimmel }
608ca30707dSIdo Schimmel
net_dm_packet_report_fill(struct sk_buff * msg,struct sk_buff * skb,size_t payload_len)609ca30707dSIdo Schimmel static int net_dm_packet_report_fill(struct sk_buff *msg, struct sk_buff *skb,
610ca30707dSIdo Schimmel size_t payload_len)
611ca30707dSIdo Schimmel {
6125cad527dSMenglong Dong struct net_dm_skb_cb *cb = NET_DM_SKB_CB(skb);
613071c0fc6SJohannes Berg const struct drop_reason_list *list = NULL;
614071c0fc6SJohannes Berg unsigned int subsys, subsys_reason;
615ca30707dSIdo Schimmel char buf[NET_DM_MAX_SYMBOL_LEN];
616ca30707dSIdo Schimmel struct nlattr *attr;
617ca30707dSIdo Schimmel void *hdr;
618ca30707dSIdo Schimmel int rc;
619ca30707dSIdo Schimmel
620ca30707dSIdo Schimmel hdr = genlmsg_put(msg, 0, 0, &net_drop_monitor_family, 0,
621ca30707dSIdo Schimmel NET_DM_CMD_PACKET_ALERT);
622ca30707dSIdo Schimmel if (!hdr)
623ca30707dSIdo Schimmel return -EMSGSIZE;
624ca30707dSIdo Schimmel
6255e58109bSIdo Schimmel if (nla_put_u16(msg, NET_DM_ATTR_ORIGIN, NET_DM_ORIGIN_SW))
6265e58109bSIdo Schimmel goto nla_put_failure;
6275e58109bSIdo Schimmel
6285cad527dSMenglong Dong if (nla_put_u64_64bit(msg, NET_DM_ATTR_PC, (u64)(uintptr_t)cb->pc,
6295cad527dSMenglong Dong NET_DM_ATTR_PAD))
630ca30707dSIdo Schimmel goto nla_put_failure;
631ca30707dSIdo Schimmel
632071c0fc6SJohannes Berg rcu_read_lock();
633071c0fc6SJohannes Berg subsys = u32_get_bits(cb->reason, SKB_DROP_REASON_SUBSYS_MASK);
634071c0fc6SJohannes Berg if (subsys < SKB_DROP_REASON_SUBSYS_NUM)
635071c0fc6SJohannes Berg list = rcu_dereference(drop_reasons_by_subsys[subsys]);
636071c0fc6SJohannes Berg subsys_reason = cb->reason & ~SKB_DROP_REASON_SUBSYS_MASK;
637071c0fc6SJohannes Berg if (!list ||
638071c0fc6SJohannes Berg subsys_reason >= list->n_reasons ||
639071c0fc6SJohannes Berg !list->reasons[subsys_reason] ||
640071c0fc6SJohannes Berg strlen(list->reasons[subsys_reason]) > NET_DM_MAX_REASON_LEN) {
641071c0fc6SJohannes Berg list = rcu_dereference(drop_reasons_by_subsys[SKB_DROP_REASON_SUBSYS_CORE]);
642071c0fc6SJohannes Berg subsys_reason = SKB_DROP_REASON_NOT_SPECIFIED;
643071c0fc6SJohannes Berg }
6445cad527dSMenglong Dong if (nla_put_string(msg, NET_DM_ATTR_REASON,
645071c0fc6SJohannes Berg list->reasons[subsys_reason])) {
646071c0fc6SJohannes Berg rcu_read_unlock();
6475cad527dSMenglong Dong goto nla_put_failure;
648071c0fc6SJohannes Berg }
649071c0fc6SJohannes Berg rcu_read_unlock();
6505cad527dSMenglong Dong
6515cad527dSMenglong Dong snprintf(buf, sizeof(buf), "%pS", cb->pc);
652ca30707dSIdo Schimmel if (nla_put_string(msg, NET_DM_ATTR_SYMBOL, buf))
653ca30707dSIdo Schimmel goto nla_put_failure;
654ca30707dSIdo Schimmel
6555e58109bSIdo Schimmel rc = net_dm_packet_report_in_port_put(msg, skb->skb_iif, NULL);
656ca30707dSIdo Schimmel if (rc)
657ca30707dSIdo Schimmel goto nla_put_failure;
658ca30707dSIdo Schimmel
659bd1200b7SIdo Schimmel if (nla_put_u64_64bit(msg, NET_DM_ATTR_TIMESTAMP,
660bd1200b7SIdo Schimmel ktime_to_ns(skb->tstamp), NET_DM_ATTR_PAD))
661ca30707dSIdo Schimmel goto nla_put_failure;
662ca30707dSIdo Schimmel
66357986617SIdo Schimmel if (nla_put_u32(msg, NET_DM_ATTR_ORIG_LEN, skb->len))
66457986617SIdo Schimmel goto nla_put_failure;
66557986617SIdo Schimmel
666ca30707dSIdo Schimmel if (!payload_len)
667ca30707dSIdo Schimmel goto out;
668ca30707dSIdo Schimmel
669ca30707dSIdo Schimmel if (nla_put_u16(msg, NET_DM_ATTR_PROTO, be16_to_cpu(skb->protocol)))
670ca30707dSIdo Schimmel goto nla_put_failure;
671ca30707dSIdo Schimmel
672ca30707dSIdo Schimmel attr = skb_put(msg, nla_total_size(payload_len));
673ca30707dSIdo Schimmel attr->nla_type = NET_DM_ATTR_PAYLOAD;
674ca30707dSIdo Schimmel attr->nla_len = nla_attr_size(payload_len);
675ca30707dSIdo Schimmel if (skb_copy_bits(skb, 0, nla_data(attr), payload_len))
676ca30707dSIdo Schimmel goto nla_put_failure;
677ca30707dSIdo Schimmel
678ca30707dSIdo Schimmel out:
679ca30707dSIdo Schimmel genlmsg_end(msg, hdr);
680ca30707dSIdo Schimmel
681ca30707dSIdo Schimmel return 0;
682ca30707dSIdo Schimmel
683ca30707dSIdo Schimmel nla_put_failure:
684ca30707dSIdo Schimmel genlmsg_cancel(msg, hdr);
685ca30707dSIdo Schimmel return -EMSGSIZE;
686ca30707dSIdo Schimmel }
687ca30707dSIdo Schimmel
688ca30707dSIdo Schimmel #define NET_DM_MAX_PACKET_SIZE (0xffff - NLA_HDRLEN - NLA_ALIGNTO)
689ca30707dSIdo Schimmel
net_dm_packet_report(struct sk_buff * skb)690ca30707dSIdo Schimmel static void net_dm_packet_report(struct sk_buff *skb)
691ca30707dSIdo Schimmel {
692ca30707dSIdo Schimmel struct sk_buff *msg;
693ca30707dSIdo Schimmel size_t payload_len;
694ca30707dSIdo Schimmel int rc;
695ca30707dSIdo Schimmel
696ca30707dSIdo Schimmel /* Make sure we start copying the packet from the MAC header */
697ca30707dSIdo Schimmel if (skb->data > skb_mac_header(skb))
698ca30707dSIdo Schimmel skb_push(skb, skb->data - skb_mac_header(skb));
699ca30707dSIdo Schimmel else
700ca30707dSIdo Schimmel skb_pull(skb, skb_mac_header(skb) - skb->data);
701ca30707dSIdo Schimmel
702ca30707dSIdo Schimmel /* Ensure packet fits inside a single netlink attribute */
703ca30707dSIdo Schimmel payload_len = min_t(size_t, skb->len, NET_DM_MAX_PACKET_SIZE);
70457986617SIdo Schimmel if (net_dm_trunc_len)
70557986617SIdo Schimmel payload_len = min_t(size_t, net_dm_trunc_len, payload_len);
706ca30707dSIdo Schimmel
707071c0fc6SJohannes Berg msg = nlmsg_new(net_dm_packet_report_size(payload_len), GFP_KERNEL);
708ca30707dSIdo Schimmel if (!msg)
709ca30707dSIdo Schimmel goto out;
710ca30707dSIdo Schimmel
711ca30707dSIdo Schimmel rc = net_dm_packet_report_fill(msg, skb, payload_len);
712ca30707dSIdo Schimmel if (rc) {
713ca30707dSIdo Schimmel nlmsg_free(msg);
714ca30707dSIdo Schimmel goto out;
715ca30707dSIdo Schimmel }
716ca30707dSIdo Schimmel
717ca30707dSIdo Schimmel genlmsg_multicast(&net_drop_monitor_family, msg, 0, 0, GFP_KERNEL);
718ca30707dSIdo Schimmel
719ca30707dSIdo Schimmel out:
720ca30707dSIdo Schimmel consume_skb(skb);
721ca30707dSIdo Schimmel }
722ca30707dSIdo Schimmel
net_dm_packet_work(struct work_struct * work)723ca30707dSIdo Schimmel static void net_dm_packet_work(struct work_struct *work)
724ca30707dSIdo Schimmel {
725ca30707dSIdo Schimmel struct per_cpu_dm_data *data;
726ca30707dSIdo Schimmel struct sk_buff_head list;
727ca30707dSIdo Schimmel struct sk_buff *skb;
728ca30707dSIdo Schimmel unsigned long flags;
729ca30707dSIdo Schimmel
730ca30707dSIdo Schimmel data = container_of(work, struct per_cpu_dm_data, dm_alert_work);
731ca30707dSIdo Schimmel
732ca30707dSIdo Schimmel __skb_queue_head_init(&list);
733ca30707dSIdo Schimmel
734ca30707dSIdo Schimmel spin_lock_irqsave(&data->drop_queue.lock, flags);
735ca30707dSIdo Schimmel skb_queue_splice_tail_init(&data->drop_queue, &list);
736ca30707dSIdo Schimmel spin_unlock_irqrestore(&data->drop_queue.lock, flags);
737ca30707dSIdo Schimmel
738ca30707dSIdo Schimmel while ((skb = __skb_dequeue(&list)))
739ca30707dSIdo Schimmel net_dm_packet_report(skb);
740ca30707dSIdo Schimmel }
741ca30707dSIdo Schimmel
7425e58109bSIdo Schimmel static size_t
net_dm_flow_action_cookie_size(const struct devlink_trap_metadata * hw_metadata)743a848c05fSIdo Schimmel net_dm_flow_action_cookie_size(const struct devlink_trap_metadata *hw_metadata)
744742b8cceSJiri Pirko {
745742b8cceSJiri Pirko return hw_metadata->fa_cookie ?
746742b8cceSJiri Pirko nla_total_size(hw_metadata->fa_cookie->cookie_len) : 0;
747742b8cceSJiri Pirko }
748742b8cceSJiri Pirko
749742b8cceSJiri Pirko static size_t
net_dm_hw_packet_report_size(size_t payload_len,const struct devlink_trap_metadata * hw_metadata)7505e58109bSIdo Schimmel net_dm_hw_packet_report_size(size_t payload_len,
751a848c05fSIdo Schimmel const struct devlink_trap_metadata *hw_metadata)
7525e58109bSIdo Schimmel {
7535e58109bSIdo Schimmel size_t size;
7545e58109bSIdo Schimmel
7555e58109bSIdo Schimmel size = nlmsg_msg_size(GENL_HDRLEN + net_drop_monitor_family.hdrsize);
7565e58109bSIdo Schimmel
7575e58109bSIdo Schimmel return NLMSG_ALIGN(size) +
7585e58109bSIdo Schimmel /* NET_DM_ATTR_ORIGIN */
7595e58109bSIdo Schimmel nla_total_size(sizeof(u16)) +
7605e58109bSIdo Schimmel /* NET_DM_ATTR_HW_TRAP_GROUP_NAME */
7615e58109bSIdo Schimmel nla_total_size(strlen(hw_metadata->trap_group_name) + 1) +
7625e58109bSIdo Schimmel /* NET_DM_ATTR_HW_TRAP_NAME */
7635e58109bSIdo Schimmel nla_total_size(strlen(hw_metadata->trap_name) + 1) +
7645e58109bSIdo Schimmel /* NET_DM_ATTR_IN_PORT */
7655e58109bSIdo Schimmel net_dm_in_port_size() +
766742b8cceSJiri Pirko /* NET_DM_ATTR_FLOW_ACTION_COOKIE */
767742b8cceSJiri Pirko net_dm_flow_action_cookie_size(hw_metadata) +
7685e58109bSIdo Schimmel /* NET_DM_ATTR_TIMESTAMP */
769bd1200b7SIdo Schimmel nla_total_size(sizeof(u64)) +
7705e58109bSIdo Schimmel /* NET_DM_ATTR_ORIG_LEN */
7715e58109bSIdo Schimmel nla_total_size(sizeof(u32)) +
7725e58109bSIdo Schimmel /* NET_DM_ATTR_PROTO */
7735e58109bSIdo Schimmel nla_total_size(sizeof(u16)) +
7745e58109bSIdo Schimmel /* NET_DM_ATTR_PAYLOAD */
7755e58109bSIdo Schimmel nla_total_size(payload_len);
7765e58109bSIdo Schimmel }
7775e58109bSIdo Schimmel
net_dm_hw_packet_report_fill(struct sk_buff * msg,struct sk_buff * skb,size_t payload_len)7785e58109bSIdo Schimmel static int net_dm_hw_packet_report_fill(struct sk_buff *msg,
7795e58109bSIdo Schimmel struct sk_buff *skb, size_t payload_len)
7805e58109bSIdo Schimmel {
781a848c05fSIdo Schimmel struct devlink_trap_metadata *hw_metadata;
7825e58109bSIdo Schimmel struct nlattr *attr;
7835e58109bSIdo Schimmel void *hdr;
7845e58109bSIdo Schimmel
7855e58109bSIdo Schimmel hw_metadata = NET_DM_SKB_CB(skb)->hw_metadata;
7865e58109bSIdo Schimmel
7875e58109bSIdo Schimmel hdr = genlmsg_put(msg, 0, 0, &net_drop_monitor_family, 0,
7885e58109bSIdo Schimmel NET_DM_CMD_PACKET_ALERT);
7895e58109bSIdo Schimmel if (!hdr)
7905e58109bSIdo Schimmel return -EMSGSIZE;
7915e58109bSIdo Schimmel
7925e58109bSIdo Schimmel if (nla_put_u16(msg, NET_DM_ATTR_ORIGIN, NET_DM_ORIGIN_HW))
7935e58109bSIdo Schimmel goto nla_put_failure;
7945e58109bSIdo Schimmel
7955e58109bSIdo Schimmel if (nla_put_string(msg, NET_DM_ATTR_HW_TRAP_GROUP_NAME,
7965e58109bSIdo Schimmel hw_metadata->trap_group_name))
7975e58109bSIdo Schimmel goto nla_put_failure;
7985e58109bSIdo Schimmel
7995e58109bSIdo Schimmel if (nla_put_string(msg, NET_DM_ATTR_HW_TRAP_NAME,
8005e58109bSIdo Schimmel hw_metadata->trap_name))
8015e58109bSIdo Schimmel goto nla_put_failure;
8025e58109bSIdo Schimmel
8035e58109bSIdo Schimmel if (hw_metadata->input_dev) {
8045e58109bSIdo Schimmel struct net_device *dev = hw_metadata->input_dev;
8055e58109bSIdo Schimmel int rc;
8065e58109bSIdo Schimmel
8075e58109bSIdo Schimmel rc = net_dm_packet_report_in_port_put(msg, dev->ifindex,
8085e58109bSIdo Schimmel dev->name);
8095e58109bSIdo Schimmel if (rc)
8105e58109bSIdo Schimmel goto nla_put_failure;
8115e58109bSIdo Schimmel }
8125e58109bSIdo Schimmel
813742b8cceSJiri Pirko if (hw_metadata->fa_cookie &&
814742b8cceSJiri Pirko nla_put(msg, NET_DM_ATTR_FLOW_ACTION_COOKIE,
815742b8cceSJiri Pirko hw_metadata->fa_cookie->cookie_len,
816742b8cceSJiri Pirko hw_metadata->fa_cookie->cookie))
817742b8cceSJiri Pirko goto nla_put_failure;
818742b8cceSJiri Pirko
819bd1200b7SIdo Schimmel if (nla_put_u64_64bit(msg, NET_DM_ATTR_TIMESTAMP,
820bd1200b7SIdo Schimmel ktime_to_ns(skb->tstamp), NET_DM_ATTR_PAD))
8215e58109bSIdo Schimmel goto nla_put_failure;
8225e58109bSIdo Schimmel
8235e58109bSIdo Schimmel if (nla_put_u32(msg, NET_DM_ATTR_ORIG_LEN, skb->len))
8245e58109bSIdo Schimmel goto nla_put_failure;
8255e58109bSIdo Schimmel
8265e58109bSIdo Schimmel if (!payload_len)
8275e58109bSIdo Schimmel goto out;
8285e58109bSIdo Schimmel
8295e58109bSIdo Schimmel if (nla_put_u16(msg, NET_DM_ATTR_PROTO, be16_to_cpu(skb->protocol)))
8305e58109bSIdo Schimmel goto nla_put_failure;
8315e58109bSIdo Schimmel
8325e58109bSIdo Schimmel attr = skb_put(msg, nla_total_size(payload_len));
8335e58109bSIdo Schimmel attr->nla_type = NET_DM_ATTR_PAYLOAD;
8345e58109bSIdo Schimmel attr->nla_len = nla_attr_size(payload_len);
8355e58109bSIdo Schimmel if (skb_copy_bits(skb, 0, nla_data(attr), payload_len))
8365e58109bSIdo Schimmel goto nla_put_failure;
8375e58109bSIdo Schimmel
8385e58109bSIdo Schimmel out:
8395e58109bSIdo Schimmel genlmsg_end(msg, hdr);
8405e58109bSIdo Schimmel
8415e58109bSIdo Schimmel return 0;
8425e58109bSIdo Schimmel
8435e58109bSIdo Schimmel nla_put_failure:
8445e58109bSIdo Schimmel genlmsg_cancel(msg, hdr);
8455e58109bSIdo Schimmel return -EMSGSIZE;
8465e58109bSIdo Schimmel }
8475e58109bSIdo Schimmel
848a848c05fSIdo Schimmel static struct devlink_trap_metadata *
net_dm_hw_metadata_copy(const struct devlink_trap_metadata * metadata)8495855357cSIdo Schimmel net_dm_hw_metadata_copy(const struct devlink_trap_metadata *metadata)
8505855357cSIdo Schimmel {
8515855357cSIdo Schimmel const struct flow_action_cookie *fa_cookie;
852a848c05fSIdo Schimmel struct devlink_trap_metadata *hw_metadata;
8535855357cSIdo Schimmel const char *trap_group_name;
8545855357cSIdo Schimmel const char *trap_name;
8555855357cSIdo Schimmel
8565855357cSIdo Schimmel hw_metadata = kzalloc(sizeof(*hw_metadata), GFP_ATOMIC);
8575855357cSIdo Schimmel if (!hw_metadata)
8585855357cSIdo Schimmel return NULL;
8595855357cSIdo Schimmel
8605855357cSIdo Schimmel trap_group_name = kstrdup(metadata->trap_group_name, GFP_ATOMIC);
8615855357cSIdo Schimmel if (!trap_group_name)
8625855357cSIdo Schimmel goto free_hw_metadata;
8635855357cSIdo Schimmel hw_metadata->trap_group_name = trap_group_name;
8645855357cSIdo Schimmel
8655855357cSIdo Schimmel trap_name = kstrdup(metadata->trap_name, GFP_ATOMIC);
8665855357cSIdo Schimmel if (!trap_name)
8675855357cSIdo Schimmel goto free_trap_group;
8685855357cSIdo Schimmel hw_metadata->trap_name = trap_name;
8695855357cSIdo Schimmel
8705855357cSIdo Schimmel if (metadata->fa_cookie) {
8715855357cSIdo Schimmel size_t cookie_size = sizeof(*fa_cookie) +
8725855357cSIdo Schimmel metadata->fa_cookie->cookie_len;
8735855357cSIdo Schimmel
8745855357cSIdo Schimmel fa_cookie = kmemdup(metadata->fa_cookie, cookie_size,
8755855357cSIdo Schimmel GFP_ATOMIC);
8765855357cSIdo Schimmel if (!fa_cookie)
8775855357cSIdo Schimmel goto free_trap_name;
8785855357cSIdo Schimmel hw_metadata->fa_cookie = fa_cookie;
8795855357cSIdo Schimmel }
8805855357cSIdo Schimmel
8815855357cSIdo Schimmel hw_metadata->input_dev = metadata->input_dev;
882d62607c3SJakub Kicinski netdev_hold(hw_metadata->input_dev, &hw_metadata->dev_tracker,
883d62607c3SJakub Kicinski GFP_ATOMIC);
8845855357cSIdo Schimmel
8855855357cSIdo Schimmel return hw_metadata;
8865855357cSIdo Schimmel
8875855357cSIdo Schimmel free_trap_name:
8885855357cSIdo Schimmel kfree(trap_name);
8895855357cSIdo Schimmel free_trap_group:
8905855357cSIdo Schimmel kfree(trap_group_name);
8915855357cSIdo Schimmel free_hw_metadata:
8925855357cSIdo Schimmel kfree(hw_metadata);
8935855357cSIdo Schimmel return NULL;
8945855357cSIdo Schimmel }
8955855357cSIdo Schimmel
8965e58109bSIdo Schimmel static void
net_dm_hw_metadata_free(struct devlink_trap_metadata * hw_metadata)8974dbd24f6SEric Dumazet net_dm_hw_metadata_free(struct devlink_trap_metadata *hw_metadata)
8985e58109bSIdo Schimmel {
899d62607c3SJakub Kicinski netdev_put(hw_metadata->input_dev, &hw_metadata->dev_tracker);
900742b8cceSJiri Pirko kfree(hw_metadata->fa_cookie);
9015e58109bSIdo Schimmel kfree(hw_metadata->trap_name);
9025e58109bSIdo Schimmel kfree(hw_metadata->trap_group_name);
9035e58109bSIdo Schimmel kfree(hw_metadata);
9045e58109bSIdo Schimmel }
9055e58109bSIdo Schimmel
net_dm_hw_packet_report(struct sk_buff * skb)9065e58109bSIdo Schimmel static void net_dm_hw_packet_report(struct sk_buff *skb)
9075e58109bSIdo Schimmel {
908a848c05fSIdo Schimmel struct devlink_trap_metadata *hw_metadata;
9095e58109bSIdo Schimmel struct sk_buff *msg;
9105e58109bSIdo Schimmel size_t payload_len;
9115e58109bSIdo Schimmel int rc;
9125e58109bSIdo Schimmel
9135e58109bSIdo Schimmel if (skb->data > skb_mac_header(skb))
9145e58109bSIdo Schimmel skb_push(skb, skb->data - skb_mac_header(skb));
9155e58109bSIdo Schimmel else
9165e58109bSIdo Schimmel skb_pull(skb, skb_mac_header(skb) - skb->data);
9175e58109bSIdo Schimmel
9185e58109bSIdo Schimmel payload_len = min_t(size_t, skb->len, NET_DM_MAX_PACKET_SIZE);
9195e58109bSIdo Schimmel if (net_dm_trunc_len)
9205e58109bSIdo Schimmel payload_len = min_t(size_t, net_dm_trunc_len, payload_len);
9215e58109bSIdo Schimmel
9225e58109bSIdo Schimmel hw_metadata = NET_DM_SKB_CB(skb)->hw_metadata;
9235e58109bSIdo Schimmel msg = nlmsg_new(net_dm_hw_packet_report_size(payload_len, hw_metadata),
9245e58109bSIdo Schimmel GFP_KERNEL);
9255e58109bSIdo Schimmel if (!msg)
9265e58109bSIdo Schimmel goto out;
9275e58109bSIdo Schimmel
9285e58109bSIdo Schimmel rc = net_dm_hw_packet_report_fill(msg, skb, payload_len);
9295e58109bSIdo Schimmel if (rc) {
9305e58109bSIdo Schimmel nlmsg_free(msg);
9315e58109bSIdo Schimmel goto out;
9325e58109bSIdo Schimmel }
9335e58109bSIdo Schimmel
9345e58109bSIdo Schimmel genlmsg_multicast(&net_drop_monitor_family, msg, 0, 0, GFP_KERNEL);
9355e58109bSIdo Schimmel
9365e58109bSIdo Schimmel out:
9375e58109bSIdo Schimmel net_dm_hw_metadata_free(NET_DM_SKB_CB(skb)->hw_metadata);
9385e58109bSIdo Schimmel consume_skb(skb);
9395e58109bSIdo Schimmel }
9405e58109bSIdo Schimmel
net_dm_hw_packet_work(struct work_struct * work)9415e58109bSIdo Schimmel static void net_dm_hw_packet_work(struct work_struct *work)
9425e58109bSIdo Schimmel {
9435e58109bSIdo Schimmel struct per_cpu_dm_data *hw_data;
9445e58109bSIdo Schimmel struct sk_buff_head list;
9455e58109bSIdo Schimmel struct sk_buff *skb;
9465e58109bSIdo Schimmel unsigned long flags;
9475e58109bSIdo Schimmel
9485e58109bSIdo Schimmel hw_data = container_of(work, struct per_cpu_dm_data, dm_alert_work);
9495e58109bSIdo Schimmel
9505e58109bSIdo Schimmel __skb_queue_head_init(&list);
9515e58109bSIdo Schimmel
9525e58109bSIdo Schimmel spin_lock_irqsave(&hw_data->drop_queue.lock, flags);
9535e58109bSIdo Schimmel skb_queue_splice_tail_init(&hw_data->drop_queue, &list);
9545e58109bSIdo Schimmel spin_unlock_irqrestore(&hw_data->drop_queue.lock, flags);
9555e58109bSIdo Schimmel
9565e58109bSIdo Schimmel while ((skb = __skb_dequeue(&list)))
9575e58109bSIdo Schimmel net_dm_hw_packet_report(skb);
9585e58109bSIdo Schimmel }
9595e58109bSIdo Schimmel
960edd3d007SIdo Schimmel static void
net_dm_hw_trap_packet_probe(void * ignore,const struct devlink * devlink,struct sk_buff * skb,const struct devlink_trap_metadata * metadata)9615855357cSIdo Schimmel net_dm_hw_trap_packet_probe(void *ignore, const struct devlink *devlink,
9625855357cSIdo Schimmel struct sk_buff *skb,
9635855357cSIdo Schimmel const struct devlink_trap_metadata *metadata)
9645855357cSIdo Schimmel {
965a848c05fSIdo Schimmel struct devlink_trap_metadata *n_hw_metadata;
9665855357cSIdo Schimmel ktime_t tstamp = ktime_get_real();
9675855357cSIdo Schimmel struct per_cpu_dm_data *hw_data;
9685855357cSIdo Schimmel struct sk_buff *nskb;
9695855357cSIdo Schimmel unsigned long flags;
9705855357cSIdo Schimmel
97193e15596SIdo Schimmel if (metadata->trap_type == DEVLINK_TRAP_TYPE_CONTROL)
97293e15596SIdo Schimmel return;
97393e15596SIdo Schimmel
9745855357cSIdo Schimmel if (!skb_mac_header_was_set(skb))
9755855357cSIdo Schimmel return;
9765855357cSIdo Schimmel
9775855357cSIdo Schimmel nskb = skb_clone(skb, GFP_ATOMIC);
9785855357cSIdo Schimmel if (!nskb)
9795855357cSIdo Schimmel return;
9805855357cSIdo Schimmel
9815855357cSIdo Schimmel n_hw_metadata = net_dm_hw_metadata_copy(metadata);
9825855357cSIdo Schimmel if (!n_hw_metadata)
9835855357cSIdo Schimmel goto free;
9845855357cSIdo Schimmel
9855855357cSIdo Schimmel NET_DM_SKB_CB(nskb)->hw_metadata = n_hw_metadata;
9865855357cSIdo Schimmel nskb->tstamp = tstamp;
9875855357cSIdo Schimmel
9885855357cSIdo Schimmel hw_data = this_cpu_ptr(&dm_hw_cpu_data);
9895855357cSIdo Schimmel
9905855357cSIdo Schimmel spin_lock_irqsave(&hw_data->drop_queue.lock, flags);
9915855357cSIdo Schimmel if (skb_queue_len(&hw_data->drop_queue) < net_dm_queue_len)
9925855357cSIdo Schimmel __skb_queue_tail(&hw_data->drop_queue, nskb);
9935855357cSIdo Schimmel else
9945855357cSIdo Schimmel goto unlock_free;
9955855357cSIdo Schimmel spin_unlock_irqrestore(&hw_data->drop_queue.lock, flags);
9965855357cSIdo Schimmel
9975855357cSIdo Schimmel schedule_work(&hw_data->dm_alert_work);
9985855357cSIdo Schimmel
9995855357cSIdo Schimmel return;
10005855357cSIdo Schimmel
10015855357cSIdo Schimmel unlock_free:
10025855357cSIdo Schimmel spin_unlock_irqrestore(&hw_data->drop_queue.lock, flags);
10035855357cSIdo Schimmel u64_stats_update_begin(&hw_data->stats.syncp);
1004c6cce71eSEric Dumazet u64_stats_inc(&hw_data->stats.dropped);
10055855357cSIdo Schimmel u64_stats_update_end(&hw_data->stats.syncp);
10065855357cSIdo Schimmel net_dm_hw_metadata_free(n_hw_metadata);
10075855357cSIdo Schimmel free:
10085855357cSIdo Schimmel consume_skb(nskb);
10095855357cSIdo Schimmel }
10105855357cSIdo Schimmel
1011ca30707dSIdo Schimmel static const struct net_dm_alert_ops net_dm_alert_packet_ops = {
1012ca30707dSIdo Schimmel .kfree_skb_probe = net_dm_packet_trace_kfree_skb_hit,
1013ca30707dSIdo Schimmel .napi_poll_probe = net_dm_packet_trace_napi_poll_hit,
1014ca30707dSIdo Schimmel .work_item_func = net_dm_packet_work,
10155e58109bSIdo Schimmel .hw_work_item_func = net_dm_hw_packet_work,
10165855357cSIdo Schimmel .hw_trap_probe = net_dm_hw_trap_packet_probe,
1017ca30707dSIdo Schimmel };
1018ca30707dSIdo Schimmel
101928315f79SIdo Schimmel static const struct net_dm_alert_ops *net_dm_alert_ops_arr[] = {
102028315f79SIdo Schimmel [NET_DM_ALERT_MODE_SUMMARY] = &net_dm_alert_summary_ops,
1021ca30707dSIdo Schimmel [NET_DM_ALERT_MODE_PACKET] = &net_dm_alert_packet_ops,
102228315f79SIdo Schimmel };
102328315f79SIdo Schimmel
10248ee2267aSIdo Schimmel #if IS_ENABLED(CONFIG_NET_DEVLINK)
net_dm_hw_probe_register(const struct net_dm_alert_ops * ops)10258ee2267aSIdo Schimmel static int net_dm_hw_probe_register(const struct net_dm_alert_ops *ops)
1026edd3d007SIdo Schimmel {
10278ee2267aSIdo Schimmel return register_trace_devlink_trap_report(ops->hw_trap_probe, NULL);
1028edd3d007SIdo Schimmel }
10298ee2267aSIdo Schimmel
net_dm_hw_probe_unregister(const struct net_dm_alert_ops * ops)10308ee2267aSIdo Schimmel static void net_dm_hw_probe_unregister(const struct net_dm_alert_ops *ops)
10318ee2267aSIdo Schimmel {
10328ee2267aSIdo Schimmel unregister_trace_devlink_trap_report(ops->hw_trap_probe, NULL);
10338ee2267aSIdo Schimmel tracepoint_synchronize_unregister();
10348ee2267aSIdo Schimmel }
10358ee2267aSIdo Schimmel #else
net_dm_hw_probe_register(const struct net_dm_alert_ops * ops)10368ee2267aSIdo Schimmel static int net_dm_hw_probe_register(const struct net_dm_alert_ops *ops)
10378ee2267aSIdo Schimmel {
10388ee2267aSIdo Schimmel return -EOPNOTSUPP;
10398ee2267aSIdo Schimmel }
10408ee2267aSIdo Schimmel
net_dm_hw_probe_unregister(const struct net_dm_alert_ops * ops)10418ee2267aSIdo Schimmel static void net_dm_hw_probe_unregister(const struct net_dm_alert_ops *ops)
10428ee2267aSIdo Schimmel {
10438ee2267aSIdo Schimmel }
10448ee2267aSIdo Schimmel #endif
1045edd3d007SIdo Schimmel
net_dm_hw_monitor_start(struct netlink_ext_ack * extack)10468e94c3bcSIdo Schimmel static int net_dm_hw_monitor_start(struct netlink_ext_ack *extack)
10478e94c3bcSIdo Schimmel {
10488e94c3bcSIdo Schimmel const struct net_dm_alert_ops *ops;
10498ee2267aSIdo Schimmel int cpu, rc;
10508e94c3bcSIdo Schimmel
10518e94c3bcSIdo Schimmel if (monitor_hw) {
10528e94c3bcSIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Hardware monitoring already enabled");
10538e94c3bcSIdo Schimmel return -EAGAIN;
10548e94c3bcSIdo Schimmel }
10558e94c3bcSIdo Schimmel
10568e94c3bcSIdo Schimmel ops = net_dm_alert_ops_arr[net_dm_alert_mode];
10578e94c3bcSIdo Schimmel
10588e94c3bcSIdo Schimmel if (!try_module_get(THIS_MODULE)) {
10598e94c3bcSIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Failed to take reference on module");
10608e94c3bcSIdo Schimmel return -ENODEV;
10618e94c3bcSIdo Schimmel }
10628e94c3bcSIdo Schimmel
10638e94c3bcSIdo Schimmel for_each_possible_cpu(cpu) {
10648e94c3bcSIdo Schimmel struct per_cpu_dm_data *hw_data = &per_cpu(dm_hw_cpu_data, cpu);
10658e94c3bcSIdo Schimmel struct net_dm_hw_entries *hw_entries;
10668e94c3bcSIdo Schimmel
10678e94c3bcSIdo Schimmel INIT_WORK(&hw_data->dm_alert_work, ops->hw_work_item_func);
10688e94c3bcSIdo Schimmel timer_setup(&hw_data->send_timer, sched_send_work, 0);
10698e94c3bcSIdo Schimmel hw_entries = net_dm_hw_reset_per_cpu_data(hw_data);
10708e94c3bcSIdo Schimmel kfree(hw_entries);
10718e94c3bcSIdo Schimmel }
10728e94c3bcSIdo Schimmel
10738ee2267aSIdo Schimmel rc = net_dm_hw_probe_register(ops);
10748ee2267aSIdo Schimmel if (rc) {
10758ee2267aSIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Failed to connect probe to devlink_trap_probe() tracepoint");
10768ee2267aSIdo Schimmel goto err_module_put;
10778ee2267aSIdo Schimmel }
10788ee2267aSIdo Schimmel
10798e94c3bcSIdo Schimmel monitor_hw = true;
10808e94c3bcSIdo Schimmel
10818e94c3bcSIdo Schimmel return 0;
10828ee2267aSIdo Schimmel
10838ee2267aSIdo Schimmel err_module_put:
10849398e9c0SIdo Schimmel for_each_possible_cpu(cpu) {
10859398e9c0SIdo Schimmel struct per_cpu_dm_data *hw_data = &per_cpu(dm_hw_cpu_data, cpu);
10869398e9c0SIdo Schimmel struct sk_buff *skb;
10879398e9c0SIdo Schimmel
10889398e9c0SIdo Schimmel del_timer_sync(&hw_data->send_timer);
10899398e9c0SIdo Schimmel cancel_work_sync(&hw_data->dm_alert_work);
10909398e9c0SIdo Schimmel while ((skb = __skb_dequeue(&hw_data->drop_queue))) {
10919398e9c0SIdo Schimmel struct devlink_trap_metadata *hw_metadata;
10929398e9c0SIdo Schimmel
10939398e9c0SIdo Schimmel hw_metadata = NET_DM_SKB_CB(skb)->hw_metadata;
10949398e9c0SIdo Schimmel net_dm_hw_metadata_free(hw_metadata);
10959398e9c0SIdo Schimmel consume_skb(skb);
10969398e9c0SIdo Schimmel }
10979398e9c0SIdo Schimmel }
10988ee2267aSIdo Schimmel module_put(THIS_MODULE);
10998ee2267aSIdo Schimmel return rc;
11008e94c3bcSIdo Schimmel }
11018e94c3bcSIdo Schimmel
net_dm_hw_monitor_stop(struct netlink_ext_ack * extack)11028e94c3bcSIdo Schimmel static void net_dm_hw_monitor_stop(struct netlink_ext_ack *extack)
11038e94c3bcSIdo Schimmel {
11048ee2267aSIdo Schimmel const struct net_dm_alert_ops *ops;
11058e94c3bcSIdo Schimmel int cpu;
11068e94c3bcSIdo Schimmel
1107dfa7f709SIdo Schimmel if (!monitor_hw) {
11088e94c3bcSIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Hardware monitoring already disabled");
1109dfa7f709SIdo Schimmel return;
1110dfa7f709SIdo Schimmel }
11118e94c3bcSIdo Schimmel
11128ee2267aSIdo Schimmel ops = net_dm_alert_ops_arr[net_dm_alert_mode];
11138ee2267aSIdo Schimmel
11148e94c3bcSIdo Schimmel monitor_hw = false;
11158e94c3bcSIdo Schimmel
11168ee2267aSIdo Schimmel net_dm_hw_probe_unregister(ops);
11178e94c3bcSIdo Schimmel
11188e94c3bcSIdo Schimmel for_each_possible_cpu(cpu) {
11198e94c3bcSIdo Schimmel struct per_cpu_dm_data *hw_data = &per_cpu(dm_hw_cpu_data, cpu);
11208e94c3bcSIdo Schimmel struct sk_buff *skb;
11218e94c3bcSIdo Schimmel
11228e94c3bcSIdo Schimmel del_timer_sync(&hw_data->send_timer);
11238e94c3bcSIdo Schimmel cancel_work_sync(&hw_data->dm_alert_work);
11248e94c3bcSIdo Schimmel while ((skb = __skb_dequeue(&hw_data->drop_queue))) {
1125a848c05fSIdo Schimmel struct devlink_trap_metadata *hw_metadata;
11268e94c3bcSIdo Schimmel
11278e94c3bcSIdo Schimmel hw_metadata = NET_DM_SKB_CB(skb)->hw_metadata;
11288e94c3bcSIdo Schimmel net_dm_hw_metadata_free(hw_metadata);
11298e94c3bcSIdo Schimmel consume_skb(skb);
11308e94c3bcSIdo Schimmel }
11318e94c3bcSIdo Schimmel }
11328e94c3bcSIdo Schimmel
11338e94c3bcSIdo Schimmel module_put(THIS_MODULE);
11348e94c3bcSIdo Schimmel }
11358e94c3bcSIdo Schimmel
net_dm_trace_on_set(struct netlink_ext_ack * extack)11367c747838SIdo Schimmel static int net_dm_trace_on_set(struct netlink_ext_ack *extack)
11379a8afc8dSNeil Horman {
113828315f79SIdo Schimmel const struct net_dm_alert_ops *ops;
113970c69274SIdo Schimmel int cpu, rc;
11404ea7e386SNeil Horman
114128315f79SIdo Schimmel ops = net_dm_alert_ops_arr[net_dm_alert_mode];
114228315f79SIdo Schimmel
1143cad456d5SNeil Horman if (!try_module_get(THIS_MODULE)) {
114496510096SIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Failed to take reference on module");
11457c747838SIdo Schimmel return -ENODEV;
1146cad456d5SNeil Horman }
1147cad456d5SNeil Horman
114870c69274SIdo Schimmel for_each_possible_cpu(cpu) {
114970c69274SIdo Schimmel struct per_cpu_dm_data *data = &per_cpu(dm_cpu_data, cpu);
115044075f56SIdo Schimmel struct sk_buff *skb;
115170c69274SIdo Schimmel
115228315f79SIdo Schimmel INIT_WORK(&data->dm_alert_work, ops->work_item_func);
115370c69274SIdo Schimmel timer_setup(&data->send_timer, sched_send_work, 0);
115444075f56SIdo Schimmel /* Allocate a new per-CPU skb for the summary alert message and
115544075f56SIdo Schimmel * free the old one which might contain stale data from
115644075f56SIdo Schimmel * previous tracing.
115744075f56SIdo Schimmel */
115844075f56SIdo Schimmel skb = reset_per_cpu_data(data);
115944075f56SIdo Schimmel consume_skb(skb);
116070c69274SIdo Schimmel }
116170c69274SIdo Schimmel
116228315f79SIdo Schimmel rc = register_trace_kfree_skb(ops->kfree_skb_probe, NULL);
11637c747838SIdo Schimmel if (rc) {
11647c747838SIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Failed to connect probe to kfree_skb() tracepoint");
11657c747838SIdo Schimmel goto err_module_put;
11667c747838SIdo Schimmel }
1167cad456d5SNeil Horman
116828315f79SIdo Schimmel rc = register_trace_napi_poll(ops->napi_poll_probe, NULL);
11697c747838SIdo Schimmel if (rc) {
11707c747838SIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Failed to connect probe to napi_poll() tracepoint");
11717c747838SIdo Schimmel goto err_unregister_trace;
11727c747838SIdo Schimmel }
11737c747838SIdo Schimmel
11747c747838SIdo Schimmel return 0;
11757c747838SIdo Schimmel
11767c747838SIdo Schimmel err_unregister_trace:
117728315f79SIdo Schimmel unregister_trace_kfree_skb(ops->kfree_skb_probe, NULL);
11787c747838SIdo Schimmel err_module_put:
11799398e9c0SIdo Schimmel for_each_possible_cpu(cpu) {
11809398e9c0SIdo Schimmel struct per_cpu_dm_data *data = &per_cpu(dm_cpu_data, cpu);
11819398e9c0SIdo Schimmel struct sk_buff *skb;
11829398e9c0SIdo Schimmel
11839398e9c0SIdo Schimmel del_timer_sync(&data->send_timer);
11849398e9c0SIdo Schimmel cancel_work_sync(&data->dm_alert_work);
11859398e9c0SIdo Schimmel while ((skb = __skb_dequeue(&data->drop_queue)))
11869398e9c0SIdo Schimmel consume_skb(skb);
11879398e9c0SIdo Schimmel }
11887c747838SIdo Schimmel module_put(THIS_MODULE);
11897c747838SIdo Schimmel return rc;
11907c747838SIdo Schimmel }
11917c747838SIdo Schimmel
net_dm_trace_off_set(void)11927c747838SIdo Schimmel static void net_dm_trace_off_set(void)
11937c747838SIdo Schimmel {
119428315f79SIdo Schimmel const struct net_dm_alert_ops *ops;
119570c69274SIdo Schimmel int cpu;
11967c747838SIdo Schimmel
119728315f79SIdo Schimmel ops = net_dm_alert_ops_arr[net_dm_alert_mode];
119828315f79SIdo Schimmel
119928315f79SIdo Schimmel unregister_trace_napi_poll(ops->napi_poll_probe, NULL);
120028315f79SIdo Schimmel unregister_trace_kfree_skb(ops->kfree_skb_probe, NULL);
12019a8afc8dSNeil Horman
12029a8afc8dSNeil Horman tracepoint_synchronize_unregister();
12034ea7e386SNeil Horman
120470c69274SIdo Schimmel /* Make sure we do not send notifications to user space after request
120570c69274SIdo Schimmel * to stop tracing returns.
120670c69274SIdo Schimmel */
120770c69274SIdo Schimmel for_each_possible_cpu(cpu) {
120870c69274SIdo Schimmel struct per_cpu_dm_data *data = &per_cpu(dm_cpu_data, cpu);
1209ca30707dSIdo Schimmel struct sk_buff *skb;
121070c69274SIdo Schimmel
121170c69274SIdo Schimmel del_timer_sync(&data->send_timer);
121270c69274SIdo Schimmel cancel_work_sync(&data->dm_alert_work);
1213ca30707dSIdo Schimmel while ((skb = __skb_dequeue(&data->drop_queue)))
1214ca30707dSIdo Schimmel consume_skb(skb);
121570c69274SIdo Schimmel }
121670c69274SIdo Schimmel
1217cad456d5SNeil Horman module_put(THIS_MODULE);
12187c747838SIdo Schimmel }
1219cad456d5SNeil Horman
set_all_monitor_traces(int state,struct netlink_ext_ack * extack)12207c747838SIdo Schimmel static int set_all_monitor_traces(int state, struct netlink_ext_ack *extack)
12217c747838SIdo Schimmel {
12227c747838SIdo Schimmel int rc = 0;
12237c747838SIdo Schimmel
12247c747838SIdo Schimmel if (state == trace_state) {
12257c747838SIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Trace state already set to requested state");
12267c747838SIdo Schimmel return -EAGAIN;
12277c747838SIdo Schimmel }
12287c747838SIdo Schimmel
12297c747838SIdo Schimmel switch (state) {
12307c747838SIdo Schimmel case TRACE_ON:
12317c747838SIdo Schimmel rc = net_dm_trace_on_set(extack);
12327c747838SIdo Schimmel break;
12337c747838SIdo Schimmel case TRACE_OFF:
12347c747838SIdo Schimmel net_dm_trace_off_set();
12359a8afc8dSNeil Horman break;
12369a8afc8dSNeil Horman default:
12379a8afc8dSNeil Horman rc = 1;
12389a8afc8dSNeil Horman break;
12399a8afc8dSNeil Horman }
12409a8afc8dSNeil Horman
12414ea7e386SNeil Horman if (!rc)
12424ea7e386SNeil Horman trace_state = state;
12434b706372SNeil Horman else
12444b706372SNeil Horman rc = -EINPROGRESS;
12454ea7e386SNeil Horman
12469a8afc8dSNeil Horman return rc;
12479a8afc8dSNeil Horman }
12489a8afc8dSNeil Horman
net_dm_is_monitoring(void)124980cebed8SIdo Schimmel static bool net_dm_is_monitoring(void)
125080cebed8SIdo Schimmel {
125180cebed8SIdo Schimmel return trace_state == TRACE_ON || monitor_hw;
125280cebed8SIdo Schimmel }
125380cebed8SIdo Schimmel
net_dm_alert_mode_get_from_info(struct genl_info * info,enum net_dm_alert_mode * p_alert_mode)1254ca30707dSIdo Schimmel static int net_dm_alert_mode_get_from_info(struct genl_info *info,
1255ca30707dSIdo Schimmel enum net_dm_alert_mode *p_alert_mode)
1256ca30707dSIdo Schimmel {
1257ca30707dSIdo Schimmel u8 val;
1258ca30707dSIdo Schimmel
1259ca30707dSIdo Schimmel val = nla_get_u8(info->attrs[NET_DM_ATTR_ALERT_MODE]);
1260ca30707dSIdo Schimmel
1261ca30707dSIdo Schimmel switch (val) {
1262df561f66SGustavo A. R. Silva case NET_DM_ALERT_MODE_SUMMARY:
1263ca30707dSIdo Schimmel case NET_DM_ALERT_MODE_PACKET:
1264ca30707dSIdo Schimmel *p_alert_mode = val;
1265ca30707dSIdo Schimmel break;
1266ca30707dSIdo Schimmel default:
1267ca30707dSIdo Schimmel return -EINVAL;
1268ca30707dSIdo Schimmel }
1269ca30707dSIdo Schimmel
1270ca30707dSIdo Schimmel return 0;
1271ca30707dSIdo Schimmel }
1272ca30707dSIdo Schimmel
net_dm_alert_mode_set(struct genl_info * info)1273ca30707dSIdo Schimmel static int net_dm_alert_mode_set(struct genl_info *info)
1274ca30707dSIdo Schimmel {
1275ca30707dSIdo Schimmel struct netlink_ext_ack *extack = info->extack;
1276ca30707dSIdo Schimmel enum net_dm_alert_mode alert_mode;
1277ca30707dSIdo Schimmel int rc;
1278ca30707dSIdo Schimmel
1279ca30707dSIdo Schimmel if (!info->attrs[NET_DM_ATTR_ALERT_MODE])
1280ca30707dSIdo Schimmel return 0;
1281ca30707dSIdo Schimmel
1282ca30707dSIdo Schimmel rc = net_dm_alert_mode_get_from_info(info, &alert_mode);
1283ca30707dSIdo Schimmel if (rc) {
1284ca30707dSIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Invalid alert mode");
1285ca30707dSIdo Schimmel return -EINVAL;
1286ca30707dSIdo Schimmel }
1287ca30707dSIdo Schimmel
1288ca30707dSIdo Schimmel net_dm_alert_mode = alert_mode;
1289ca30707dSIdo Schimmel
1290ca30707dSIdo Schimmel return 0;
1291ca30707dSIdo Schimmel }
1292ca30707dSIdo Schimmel
net_dm_trunc_len_set(struct genl_info * info)129357986617SIdo Schimmel static void net_dm_trunc_len_set(struct genl_info *info)
129457986617SIdo Schimmel {
129557986617SIdo Schimmel if (!info->attrs[NET_DM_ATTR_TRUNC_LEN])
129657986617SIdo Schimmel return;
129757986617SIdo Schimmel
129857986617SIdo Schimmel net_dm_trunc_len = nla_get_u32(info->attrs[NET_DM_ATTR_TRUNC_LEN]);
129957986617SIdo Schimmel }
130057986617SIdo Schimmel
net_dm_queue_len_set(struct genl_info * info)130130328d46SIdo Schimmel static void net_dm_queue_len_set(struct genl_info *info)
130230328d46SIdo Schimmel {
130330328d46SIdo Schimmel if (!info->attrs[NET_DM_ATTR_QUEUE_LEN])
130430328d46SIdo Schimmel return;
130530328d46SIdo Schimmel
130630328d46SIdo Schimmel net_dm_queue_len = nla_get_u32(info->attrs[NET_DM_ATTR_QUEUE_LEN]);
130730328d46SIdo Schimmel }
130830328d46SIdo Schimmel
net_dm_cmd_config(struct sk_buff * skb,struct genl_info * info)13099a8afc8dSNeil Horman static int net_dm_cmd_config(struct sk_buff *skb,
13109a8afc8dSNeil Horman struct genl_info *info)
13119a8afc8dSNeil Horman {
1312ca30707dSIdo Schimmel struct netlink_ext_ack *extack = info->extack;
1313ca30707dSIdo Schimmel int rc;
131496510096SIdo Schimmel
131580cebed8SIdo Schimmel if (net_dm_is_monitoring()) {
131680cebed8SIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Cannot configure drop monitor during monitoring");
1317ca30707dSIdo Schimmel return -EBUSY;
1318ca30707dSIdo Schimmel }
1319ca30707dSIdo Schimmel
1320ca30707dSIdo Schimmel rc = net_dm_alert_mode_set(info);
1321ca30707dSIdo Schimmel if (rc)
1322ca30707dSIdo Schimmel return rc;
1323ca30707dSIdo Schimmel
132457986617SIdo Schimmel net_dm_trunc_len_set(info);
132557986617SIdo Schimmel
132630328d46SIdo Schimmel net_dm_queue_len_set(info);
132730328d46SIdo Schimmel
1328ca30707dSIdo Schimmel return 0;
13299a8afc8dSNeil Horman }
13309a8afc8dSNeil Horman
net_dm_monitor_start(bool set_sw,bool set_hw,struct netlink_ext_ack * extack)13318e94c3bcSIdo Schimmel static int net_dm_monitor_start(bool set_sw, bool set_hw,
13328e94c3bcSIdo Schimmel struct netlink_ext_ack *extack)
13338e94c3bcSIdo Schimmel {
13348e94c3bcSIdo Schimmel bool sw_set = false;
13358e94c3bcSIdo Schimmel int rc;
13368e94c3bcSIdo Schimmel
13378e94c3bcSIdo Schimmel if (set_sw) {
13388e94c3bcSIdo Schimmel rc = set_all_monitor_traces(TRACE_ON, extack);
13398e94c3bcSIdo Schimmel if (rc)
13408e94c3bcSIdo Schimmel return rc;
13418e94c3bcSIdo Schimmel sw_set = true;
13428e94c3bcSIdo Schimmel }
13438e94c3bcSIdo Schimmel
13448e94c3bcSIdo Schimmel if (set_hw) {
13458e94c3bcSIdo Schimmel rc = net_dm_hw_monitor_start(extack);
13468e94c3bcSIdo Schimmel if (rc)
13478e94c3bcSIdo Schimmel goto err_monitor_hw;
13488e94c3bcSIdo Schimmel }
13498e94c3bcSIdo Schimmel
13508e94c3bcSIdo Schimmel return 0;
13518e94c3bcSIdo Schimmel
13528e94c3bcSIdo Schimmel err_monitor_hw:
13538e94c3bcSIdo Schimmel if (sw_set)
13548e94c3bcSIdo Schimmel set_all_monitor_traces(TRACE_OFF, extack);
13558e94c3bcSIdo Schimmel return rc;
13568e94c3bcSIdo Schimmel }
13578e94c3bcSIdo Schimmel
net_dm_monitor_stop(bool set_sw,bool set_hw,struct netlink_ext_ack * extack)13588e94c3bcSIdo Schimmel static void net_dm_monitor_stop(bool set_sw, bool set_hw,
13598e94c3bcSIdo Schimmel struct netlink_ext_ack *extack)
13608e94c3bcSIdo Schimmel {
13618e94c3bcSIdo Schimmel if (set_hw)
13628e94c3bcSIdo Schimmel net_dm_hw_monitor_stop(extack);
13638e94c3bcSIdo Schimmel if (set_sw)
13648e94c3bcSIdo Schimmel set_all_monitor_traces(TRACE_OFF, extack);
13658e94c3bcSIdo Schimmel }
13668e94c3bcSIdo Schimmel
net_dm_cmd_trace(struct sk_buff * skb,struct genl_info * info)13679a8afc8dSNeil Horman static int net_dm_cmd_trace(struct sk_buff *skb,
13689a8afc8dSNeil Horman struct genl_info *info)
13699a8afc8dSNeil Horman {
13708e94c3bcSIdo Schimmel bool set_sw = !!info->attrs[NET_DM_ATTR_SW_DROPS];
13718e94c3bcSIdo Schimmel bool set_hw = !!info->attrs[NET_DM_ATTR_HW_DROPS];
13728e94c3bcSIdo Schimmel struct netlink_ext_ack *extack = info->extack;
13738e94c3bcSIdo Schimmel
13748e94c3bcSIdo Schimmel /* To maintain backward compatibility, we start / stop monitoring of
13758e94c3bcSIdo Schimmel * software drops if no flag is specified.
13768e94c3bcSIdo Schimmel */
13778e94c3bcSIdo Schimmel if (!set_sw && !set_hw)
13788e94c3bcSIdo Schimmel set_sw = true;
13798e94c3bcSIdo Schimmel
13809a8afc8dSNeil Horman switch (info->genlhdr->cmd) {
13819a8afc8dSNeil Horman case NET_DM_CMD_START:
13828e94c3bcSIdo Schimmel return net_dm_monitor_start(set_sw, set_hw, extack);
13839a8afc8dSNeil Horman case NET_DM_CMD_STOP:
13848e94c3bcSIdo Schimmel net_dm_monitor_stop(set_sw, set_hw, extack);
13858e94c3bcSIdo Schimmel return 0;
13869a8afc8dSNeil Horman }
13879a8afc8dSNeil Horman
13882230a7efSIdo Schimmel return -EOPNOTSUPP;
13899a8afc8dSNeil Horman }
13909a8afc8dSNeil Horman
net_dm_config_fill(struct sk_buff * msg,struct genl_info * info)1391444be061SIdo Schimmel static int net_dm_config_fill(struct sk_buff *msg, struct genl_info *info)
1392444be061SIdo Schimmel {
1393444be061SIdo Schimmel void *hdr;
1394444be061SIdo Schimmel
1395444be061SIdo Schimmel hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
1396444be061SIdo Schimmel &net_drop_monitor_family, 0, NET_DM_CMD_CONFIG_NEW);
1397444be061SIdo Schimmel if (!hdr)
1398444be061SIdo Schimmel return -EMSGSIZE;
1399444be061SIdo Schimmel
1400444be061SIdo Schimmel if (nla_put_u8(msg, NET_DM_ATTR_ALERT_MODE, net_dm_alert_mode))
1401444be061SIdo Schimmel goto nla_put_failure;
1402444be061SIdo Schimmel
1403444be061SIdo Schimmel if (nla_put_u32(msg, NET_DM_ATTR_TRUNC_LEN, net_dm_trunc_len))
1404444be061SIdo Schimmel goto nla_put_failure;
1405444be061SIdo Schimmel
140630328d46SIdo Schimmel if (nla_put_u32(msg, NET_DM_ATTR_QUEUE_LEN, net_dm_queue_len))
140730328d46SIdo Schimmel goto nla_put_failure;
140830328d46SIdo Schimmel
1409444be061SIdo Schimmel genlmsg_end(msg, hdr);
1410444be061SIdo Schimmel
1411444be061SIdo Schimmel return 0;
1412444be061SIdo Schimmel
1413444be061SIdo Schimmel nla_put_failure:
1414444be061SIdo Schimmel genlmsg_cancel(msg, hdr);
1415444be061SIdo Schimmel return -EMSGSIZE;
1416444be061SIdo Schimmel }
1417444be061SIdo Schimmel
net_dm_cmd_config_get(struct sk_buff * skb,struct genl_info * info)1418444be061SIdo Schimmel static int net_dm_cmd_config_get(struct sk_buff *skb, struct genl_info *info)
1419444be061SIdo Schimmel {
1420444be061SIdo Schimmel struct sk_buff *msg;
1421444be061SIdo Schimmel int rc;
1422444be061SIdo Schimmel
1423444be061SIdo Schimmel msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
1424444be061SIdo Schimmel if (!msg)
1425444be061SIdo Schimmel return -ENOMEM;
1426444be061SIdo Schimmel
1427444be061SIdo Schimmel rc = net_dm_config_fill(msg, info);
1428444be061SIdo Schimmel if (rc)
1429444be061SIdo Schimmel goto free_msg;
1430444be061SIdo Schimmel
1431444be061SIdo Schimmel return genlmsg_reply(msg, info);
1432444be061SIdo Schimmel
1433444be061SIdo Schimmel free_msg:
1434444be061SIdo Schimmel nlmsg_free(msg);
1435444be061SIdo Schimmel return rc;
1436444be061SIdo Schimmel }
1437444be061SIdo Schimmel
net_dm_stats_read(struct net_dm_stats * stats)1438e9feb580SIdo Schimmel static void net_dm_stats_read(struct net_dm_stats *stats)
1439e9feb580SIdo Schimmel {
1440e9feb580SIdo Schimmel int cpu;
1441e9feb580SIdo Schimmel
1442e9feb580SIdo Schimmel memset(stats, 0, sizeof(*stats));
1443e9feb580SIdo Schimmel for_each_possible_cpu(cpu) {
1444e9feb580SIdo Schimmel struct per_cpu_dm_data *data = &per_cpu(dm_cpu_data, cpu);
1445e9feb580SIdo Schimmel struct net_dm_stats *cpu_stats = &data->stats;
1446e9feb580SIdo Schimmel unsigned int start;
1447e9feb580SIdo Schimmel u64 dropped;
1448e9feb580SIdo Schimmel
1449e9feb580SIdo Schimmel do {
1450d120d1a6SThomas Gleixner start = u64_stats_fetch_begin(&cpu_stats->syncp);
1451c6cce71eSEric Dumazet dropped = u64_stats_read(&cpu_stats->dropped);
1452d120d1a6SThomas Gleixner } while (u64_stats_fetch_retry(&cpu_stats->syncp, start));
1453e9feb580SIdo Schimmel
1454c6cce71eSEric Dumazet u64_stats_add(&stats->dropped, dropped);
1455e9feb580SIdo Schimmel }
1456e9feb580SIdo Schimmel }
1457e9feb580SIdo Schimmel
net_dm_stats_put(struct sk_buff * msg)1458e9feb580SIdo Schimmel static int net_dm_stats_put(struct sk_buff *msg)
1459e9feb580SIdo Schimmel {
1460e9feb580SIdo Schimmel struct net_dm_stats stats;
1461e9feb580SIdo Schimmel struct nlattr *attr;
1462e9feb580SIdo Schimmel
1463e9feb580SIdo Schimmel net_dm_stats_read(&stats);
1464e9feb580SIdo Schimmel
1465e9feb580SIdo Schimmel attr = nla_nest_start(msg, NET_DM_ATTR_STATS);
1466e9feb580SIdo Schimmel if (!attr)
1467e9feb580SIdo Schimmel return -EMSGSIZE;
1468e9feb580SIdo Schimmel
1469e9feb580SIdo Schimmel if (nla_put_u64_64bit(msg, NET_DM_ATTR_STATS_DROPPED,
1470c6cce71eSEric Dumazet u64_stats_read(&stats.dropped), NET_DM_ATTR_PAD))
1471e9feb580SIdo Schimmel goto nla_put_failure;
1472e9feb580SIdo Schimmel
1473e9feb580SIdo Schimmel nla_nest_end(msg, attr);
1474e9feb580SIdo Schimmel
1475e9feb580SIdo Schimmel return 0;
1476e9feb580SIdo Schimmel
1477e9feb580SIdo Schimmel nla_put_failure:
1478e9feb580SIdo Schimmel nla_nest_cancel(msg, attr);
1479e9feb580SIdo Schimmel return -EMSGSIZE;
1480e9feb580SIdo Schimmel }
1481e9feb580SIdo Schimmel
net_dm_hw_stats_read(struct net_dm_stats * stats)14825e58109bSIdo Schimmel static void net_dm_hw_stats_read(struct net_dm_stats *stats)
14835e58109bSIdo Schimmel {
14845e58109bSIdo Schimmel int cpu;
14855e58109bSIdo Schimmel
14865e58109bSIdo Schimmel memset(stats, 0, sizeof(*stats));
14875e58109bSIdo Schimmel for_each_possible_cpu(cpu) {
14885e58109bSIdo Schimmel struct per_cpu_dm_data *hw_data = &per_cpu(dm_hw_cpu_data, cpu);
14895e58109bSIdo Schimmel struct net_dm_stats *cpu_stats = &hw_data->stats;
14905e58109bSIdo Schimmel unsigned int start;
14915e58109bSIdo Schimmel u64 dropped;
14925e58109bSIdo Schimmel
14935e58109bSIdo Schimmel do {
1494d120d1a6SThomas Gleixner start = u64_stats_fetch_begin(&cpu_stats->syncp);
1495c6cce71eSEric Dumazet dropped = u64_stats_read(&cpu_stats->dropped);
1496d120d1a6SThomas Gleixner } while (u64_stats_fetch_retry(&cpu_stats->syncp, start));
14975e58109bSIdo Schimmel
1498c6cce71eSEric Dumazet u64_stats_add(&stats->dropped, dropped);
14995e58109bSIdo Schimmel }
15005e58109bSIdo Schimmel }
15015e58109bSIdo Schimmel
net_dm_hw_stats_put(struct sk_buff * msg)15025e58109bSIdo Schimmel static int net_dm_hw_stats_put(struct sk_buff *msg)
15035e58109bSIdo Schimmel {
15045e58109bSIdo Schimmel struct net_dm_stats stats;
15055e58109bSIdo Schimmel struct nlattr *attr;
15065e58109bSIdo Schimmel
15075e58109bSIdo Schimmel net_dm_hw_stats_read(&stats);
15085e58109bSIdo Schimmel
15095e58109bSIdo Schimmel attr = nla_nest_start(msg, NET_DM_ATTR_HW_STATS);
15105e58109bSIdo Schimmel if (!attr)
15115e58109bSIdo Schimmel return -EMSGSIZE;
15125e58109bSIdo Schimmel
15135e58109bSIdo Schimmel if (nla_put_u64_64bit(msg, NET_DM_ATTR_STATS_DROPPED,
1514c6cce71eSEric Dumazet u64_stats_read(&stats.dropped), NET_DM_ATTR_PAD))
15155e58109bSIdo Schimmel goto nla_put_failure;
15165e58109bSIdo Schimmel
15175e58109bSIdo Schimmel nla_nest_end(msg, attr);
15185e58109bSIdo Schimmel
15195e58109bSIdo Schimmel return 0;
15205e58109bSIdo Schimmel
15215e58109bSIdo Schimmel nla_put_failure:
15225e58109bSIdo Schimmel nla_nest_cancel(msg, attr);
15235e58109bSIdo Schimmel return -EMSGSIZE;
15245e58109bSIdo Schimmel }
15255e58109bSIdo Schimmel
net_dm_stats_fill(struct sk_buff * msg,struct genl_info * info)1526e9feb580SIdo Schimmel static int net_dm_stats_fill(struct sk_buff *msg, struct genl_info *info)
1527e9feb580SIdo Schimmel {
1528e9feb580SIdo Schimmel void *hdr;
1529e9feb580SIdo Schimmel int rc;
1530e9feb580SIdo Schimmel
1531e9feb580SIdo Schimmel hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
1532e9feb580SIdo Schimmel &net_drop_monitor_family, 0, NET_DM_CMD_STATS_NEW);
1533e9feb580SIdo Schimmel if (!hdr)
1534e9feb580SIdo Schimmel return -EMSGSIZE;
1535e9feb580SIdo Schimmel
1536e9feb580SIdo Schimmel rc = net_dm_stats_put(msg);
1537e9feb580SIdo Schimmel if (rc)
1538e9feb580SIdo Schimmel goto nla_put_failure;
1539e9feb580SIdo Schimmel
15405e58109bSIdo Schimmel rc = net_dm_hw_stats_put(msg);
15415e58109bSIdo Schimmel if (rc)
15425e58109bSIdo Schimmel goto nla_put_failure;
15435e58109bSIdo Schimmel
1544e9feb580SIdo Schimmel genlmsg_end(msg, hdr);
1545e9feb580SIdo Schimmel
1546e9feb580SIdo Schimmel return 0;
1547e9feb580SIdo Schimmel
1548e9feb580SIdo Schimmel nla_put_failure:
1549e9feb580SIdo Schimmel genlmsg_cancel(msg, hdr);
1550e9feb580SIdo Schimmel return -EMSGSIZE;
1551e9feb580SIdo Schimmel }
1552e9feb580SIdo Schimmel
net_dm_cmd_stats_get(struct sk_buff * skb,struct genl_info * info)1553e9feb580SIdo Schimmel static int net_dm_cmd_stats_get(struct sk_buff *skb, struct genl_info *info)
1554e9feb580SIdo Schimmel {
1555e9feb580SIdo Schimmel struct sk_buff *msg;
1556e9feb580SIdo Schimmel int rc;
1557e9feb580SIdo Schimmel
1558e9feb580SIdo Schimmel msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
1559e9feb580SIdo Schimmel if (!msg)
1560e9feb580SIdo Schimmel return -ENOMEM;
1561e9feb580SIdo Schimmel
1562e9feb580SIdo Schimmel rc = net_dm_stats_fill(msg, info);
1563e9feb580SIdo Schimmel if (rc)
1564e9feb580SIdo Schimmel goto free_msg;
1565e9feb580SIdo Schimmel
1566e9feb580SIdo Schimmel return genlmsg_reply(msg, info);
1567e9feb580SIdo Schimmel
1568e9feb580SIdo Schimmel free_msg:
1569e9feb580SIdo Schimmel nlmsg_free(msg);
1570e9feb580SIdo Schimmel return rc;
1571e9feb580SIdo Schimmel }
1572e9feb580SIdo Schimmel
dropmon_net_event(struct notifier_block * ev_block,unsigned long event,void * ptr)15734ea7e386SNeil Horman static int dropmon_net_event(struct notifier_block *ev_block,
15744ea7e386SNeil Horman unsigned long event, void *ptr)
15754ea7e386SNeil Horman {
1576351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1577b26ef81cSEric Dumazet struct dm_hw_stat_delta *stat;
15784ea7e386SNeil Horman
15794ea7e386SNeil Horman switch (event) {
15804ea7e386SNeil Horman case NETDEV_REGISTER:
1581b26ef81cSEric Dumazet if (WARN_ON_ONCE(rtnl_dereference(dev->dm_private)))
1582b26ef81cSEric Dumazet break;
1583b26ef81cSEric Dumazet stat = kzalloc(sizeof(*stat), GFP_KERNEL);
1584b26ef81cSEric Dumazet if (!stat)
1585b26ef81cSEric Dumazet break;
15864ea7e386SNeil Horman
1587b26ef81cSEric Dumazet stat->last_rx = jiffies;
1588b26ef81cSEric Dumazet rcu_assign_pointer(dev->dm_private, stat);
15894ea7e386SNeil Horman
15904ea7e386SNeil Horman break;
15914ea7e386SNeil Horman case NETDEV_UNREGISTER:
1592b26ef81cSEric Dumazet stat = rtnl_dereference(dev->dm_private);
1593b26ef81cSEric Dumazet if (stat) {
1594b26ef81cSEric Dumazet rcu_assign_pointer(dev->dm_private, NULL);
1595b26ef81cSEric Dumazet kfree_rcu(stat, rcu);
1596b26ef81cSEric Dumazet }
15974ea7e386SNeil Horman break;
15984ea7e386SNeil Horman }
15994ea7e386SNeil Horman return NOTIFY_DONE;
16004ea7e386SNeil Horman }
16019a8afc8dSNeil Horman
1602ca30707dSIdo Schimmel static const struct nla_policy net_dm_nl_policy[NET_DM_ATTR_MAX + 1] = {
1603ca30707dSIdo Schimmel [NET_DM_ATTR_UNSPEC] = { .strict_start_type = NET_DM_ATTR_UNSPEC + 1 },
1604ca30707dSIdo Schimmel [NET_DM_ATTR_ALERT_MODE] = { .type = NLA_U8 },
160557986617SIdo Schimmel [NET_DM_ATTR_TRUNC_LEN] = { .type = NLA_U32 },
160630328d46SIdo Schimmel [NET_DM_ATTR_QUEUE_LEN] = { .type = NLA_U32 },
16078e94c3bcSIdo Schimmel [NET_DM_ATTR_SW_DROPS] = {. type = NLA_FLAG },
16088e94c3bcSIdo Schimmel [NET_DM_ATTR_HW_DROPS] = {. type = NLA_FLAG },
1609ca30707dSIdo Schimmel };
1610ca30707dSIdo Schimmel
161166a9b928SJakub Kicinski static const struct genl_small_ops dropmon_ops[] = {
16129a8afc8dSNeil Horman {
16139a8afc8dSNeil Horman .cmd = NET_DM_CMD_CONFIG,
1614ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
16159a8afc8dSNeil Horman .doit = net_dm_cmd_config,
1616c5ab9b1cSIdo Schimmel .flags = GENL_ADMIN_PERM,
16179a8afc8dSNeil Horman },
16189a8afc8dSNeil Horman {
16199a8afc8dSNeil Horman .cmd = NET_DM_CMD_START,
1620ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
16219a8afc8dSNeil Horman .doit = net_dm_cmd_trace,
1622e036a325SIdo Schimmel .flags = GENL_ADMIN_PERM,
16239a8afc8dSNeil Horman },
16249a8afc8dSNeil Horman {
16259a8afc8dSNeil Horman .cmd = NET_DM_CMD_STOP,
1626ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
16279a8afc8dSNeil Horman .doit = net_dm_cmd_trace,
1628e036a325SIdo Schimmel .flags = GENL_ADMIN_PERM,
16299a8afc8dSNeil Horman },
1630444be061SIdo Schimmel {
1631444be061SIdo Schimmel .cmd = NET_DM_CMD_CONFIG_GET,
1632444be061SIdo Schimmel .doit = net_dm_cmd_config_get,
1633444be061SIdo Schimmel },
1634e9feb580SIdo Schimmel {
1635e9feb580SIdo Schimmel .cmd = NET_DM_CMD_STATS_GET,
1636e9feb580SIdo Schimmel .doit = net_dm_cmd_stats_get,
1637e9feb580SIdo Schimmel },
16389a8afc8dSNeil Horman };
16399a8afc8dSNeil Horman
net_dm_nl_pre_doit(const struct genl_split_ops * ops,struct sk_buff * skb,struct genl_info * info)164020b0b53aSJakub Kicinski static int net_dm_nl_pre_doit(const struct genl_split_ops *ops,
1641b19d9550SIdo Schimmel struct sk_buff *skb, struct genl_info *info)
1642b19d9550SIdo Schimmel {
1643b19d9550SIdo Schimmel mutex_lock(&net_dm_mutex);
1644b19d9550SIdo Schimmel
1645b19d9550SIdo Schimmel return 0;
1646b19d9550SIdo Schimmel }
1647b19d9550SIdo Schimmel
net_dm_nl_post_doit(const struct genl_split_ops * ops,struct sk_buff * skb,struct genl_info * info)164820b0b53aSJakub Kicinski static void net_dm_nl_post_doit(const struct genl_split_ops *ops,
1649b19d9550SIdo Schimmel struct sk_buff *skb, struct genl_info *info)
1650b19d9550SIdo Schimmel {
1651b19d9550SIdo Schimmel mutex_unlock(&net_dm_mutex);
1652b19d9550SIdo Schimmel }
1653b19d9550SIdo Schimmel
165456989f6dSJohannes Berg static struct genl_family net_drop_monitor_family __ro_after_init = {
1655489111e5SJohannes Berg .hdrsize = 0,
1656489111e5SJohannes Berg .name = "NET_DM",
1657489111e5SJohannes Berg .version = 2,
1658ca30707dSIdo Schimmel .maxattr = NET_DM_ATTR_MAX,
1659ca30707dSIdo Schimmel .policy = net_dm_nl_policy,
1660b19d9550SIdo Schimmel .pre_doit = net_dm_nl_pre_doit,
1661b19d9550SIdo Schimmel .post_doit = net_dm_nl_post_doit,
1662489111e5SJohannes Berg .module = THIS_MODULE,
166366a9b928SJakub Kicinski .small_ops = dropmon_ops,
166466a9b928SJakub Kicinski .n_small_ops = ARRAY_SIZE(dropmon_ops),
16659c5d03d3SJakub Kicinski .resv_start_op = NET_DM_CMD_STATS_GET + 1,
1666489111e5SJohannes Berg .mcgrps = dropmon_mcgrps,
1667489111e5SJohannes Berg .n_mcgrps = ARRAY_SIZE(dropmon_mcgrps),
1668489111e5SJohannes Berg };
1669489111e5SJohannes Berg
16704ea7e386SNeil Horman static struct notifier_block dropmon_net_notifier = {
16714ea7e386SNeil Horman .notifier_call = dropmon_net_event
16724ea7e386SNeil Horman };
16734ea7e386SNeil Horman
__net_dm_cpu_data_init(struct per_cpu_dm_data * data)16749b63f57dSIdo Schimmel static void __net_dm_cpu_data_init(struct per_cpu_dm_data *data)
16759b63f57dSIdo Schimmel {
167676ce2f91SWander Lairson Costa raw_spin_lock_init(&data->lock);
16779b63f57dSIdo Schimmel skb_queue_head_init(&data->drop_queue);
16789b63f57dSIdo Schimmel u64_stats_init(&data->stats.syncp);
16799b63f57dSIdo Schimmel }
16809b63f57dSIdo Schimmel
__net_dm_cpu_data_fini(struct per_cpu_dm_data * data)16819b63f57dSIdo Schimmel static void __net_dm_cpu_data_fini(struct per_cpu_dm_data *data)
16829b63f57dSIdo Schimmel {
16839b63f57dSIdo Schimmel WARN_ON(!skb_queue_empty(&data->drop_queue));
16849b63f57dSIdo Schimmel }
16859b63f57dSIdo Schimmel
net_dm_cpu_data_init(int cpu)16869b63f57dSIdo Schimmel static void net_dm_cpu_data_init(int cpu)
16879a8afc8dSNeil Horman {
16889a8afc8dSNeil Horman struct per_cpu_dm_data *data;
16899b63f57dSIdo Schimmel
16909b63f57dSIdo Schimmel data = &per_cpu(dm_cpu_data, cpu);
16919b63f57dSIdo Schimmel __net_dm_cpu_data_init(data);
16929b63f57dSIdo Schimmel }
16939b63f57dSIdo Schimmel
net_dm_cpu_data_fini(int cpu)16949b63f57dSIdo Schimmel static void net_dm_cpu_data_fini(int cpu)
16959b63f57dSIdo Schimmel {
16969b63f57dSIdo Schimmel struct per_cpu_dm_data *data;
16979b63f57dSIdo Schimmel
16989b63f57dSIdo Schimmel data = &per_cpu(dm_cpu_data, cpu);
16999b63f57dSIdo Schimmel /* At this point, we should have exclusive access
17009b63f57dSIdo Schimmel * to this struct and can free the skb inside it.
17019b63f57dSIdo Schimmel */
17029b63f57dSIdo Schimmel consume_skb(data->skb);
17039b63f57dSIdo Schimmel __net_dm_cpu_data_fini(data);
17049b63f57dSIdo Schimmel }
17059b63f57dSIdo Schimmel
net_dm_hw_cpu_data_init(int cpu)1706cac1174fSIdo Schimmel static void net_dm_hw_cpu_data_init(int cpu)
1707cac1174fSIdo Schimmel {
1708cac1174fSIdo Schimmel struct per_cpu_dm_data *hw_data;
1709cac1174fSIdo Schimmel
1710cac1174fSIdo Schimmel hw_data = &per_cpu(dm_hw_cpu_data, cpu);
1711cac1174fSIdo Schimmel __net_dm_cpu_data_init(hw_data);
1712cac1174fSIdo Schimmel }
1713cac1174fSIdo Schimmel
net_dm_hw_cpu_data_fini(int cpu)1714cac1174fSIdo Schimmel static void net_dm_hw_cpu_data_fini(int cpu)
1715cac1174fSIdo Schimmel {
1716cac1174fSIdo Schimmel struct per_cpu_dm_data *hw_data;
1717cac1174fSIdo Schimmel
1718cac1174fSIdo Schimmel hw_data = &per_cpu(dm_hw_cpu_data, cpu);
1719d40e1debSIdo Schimmel kfree(hw_data->hw_entries);
1720cac1174fSIdo Schimmel __net_dm_cpu_data_fini(hw_data);
1721cac1174fSIdo Schimmel }
1722cac1174fSIdo Schimmel
init_net_drop_monitor(void)17239b63f57dSIdo Schimmel static int __init init_net_drop_monitor(void)
17249b63f57dSIdo Schimmel {
1725a256be70SChangli Gao int cpu, rc;
1726a256be70SChangli Gao
1727e005d193SJoe Perches pr_info("Initializing network drop monitor service\n");
17289a8afc8dSNeil Horman
17299a8afc8dSNeil Horman if (sizeof(void *) > 8) {
1730e005d193SJoe Perches pr_err("Unable to store program counters on this arch, Drop monitor failed\n");
17319a8afc8dSNeil Horman return -ENOSPC;
17329a8afc8dSNeil Horman }
17339a8afc8dSNeil Horman
1734cac1174fSIdo Schimmel for_each_possible_cpu(cpu) {
17359b63f57dSIdo Schimmel net_dm_cpu_data_init(cpu);
1736cac1174fSIdo Schimmel net_dm_hw_cpu_data_init(cpu);
1737cac1174fSIdo Schimmel }
17384ea7e386SNeil Horman
1739*0efa6c42SGavrilov Ilia rc = register_netdevice_notifier(&dropmon_net_notifier);
1740*0efa6c42SGavrilov Ilia if (rc < 0) {
1741*0efa6c42SGavrilov Ilia pr_crit("Failed to register netdevice notifier\n");
1742*0efa6c42SGavrilov Ilia return rc;
1743*0efa6c42SGavrilov Ilia }
1744*0efa6c42SGavrilov Ilia
1745*0efa6c42SGavrilov Ilia rc = genl_register_family(&net_drop_monitor_family);
1746*0efa6c42SGavrilov Ilia if (rc) {
1747*0efa6c42SGavrilov Ilia pr_err("Could not create drop monitor netlink family\n");
1748*0efa6c42SGavrilov Ilia goto out_unreg;
1749*0efa6c42SGavrilov Ilia }
1750*0efa6c42SGavrilov Ilia WARN_ON(net_drop_monitor_family.mcgrp_offset != NET_DM_GRP_ALERT);
1751*0efa6c42SGavrilov Ilia
1752*0efa6c42SGavrilov Ilia rc = 0;
1753*0efa6c42SGavrilov Ilia
17549a8afc8dSNeil Horman goto out;
17559a8afc8dSNeil Horman
17569a8afc8dSNeil Horman out_unreg:
1757*0efa6c42SGavrilov Ilia WARN_ON(unregister_netdevice_notifier(&dropmon_net_notifier));
17589a8afc8dSNeil Horman out:
17599a8afc8dSNeil Horman return rc;
17609a8afc8dSNeil Horman }
17619a8afc8dSNeil Horman
exit_net_drop_monitor(void)1762cad456d5SNeil Horman static void exit_net_drop_monitor(void)
1763cad456d5SNeil Horman {
1764cad456d5SNeil Horman int cpu;
1765cad456d5SNeil Horman
1766cad456d5SNeil Horman /*
1767cad456d5SNeil Horman * Because of the module_get/put we do in the trace state change path
1768a835f903SXiong Zhenwu * we are guaranteed not to have any current users when we get here
1769cad456d5SNeil Horman */
1770*0efa6c42SGavrilov Ilia BUG_ON(genl_unregister_family(&net_drop_monitor_family));
1771*0efa6c42SGavrilov Ilia
1772*0efa6c42SGavrilov Ilia BUG_ON(unregister_netdevice_notifier(&dropmon_net_notifier));
1773cad456d5SNeil Horman
1774cac1174fSIdo Schimmel for_each_possible_cpu(cpu) {
1775cac1174fSIdo Schimmel net_dm_hw_cpu_data_fini(cpu);
17769b63f57dSIdo Schimmel net_dm_cpu_data_fini(cpu);
1777cac1174fSIdo Schimmel }
1778cad456d5SNeil Horman }
1779cad456d5SNeil Horman
1780cad456d5SNeil Horman module_init(init_net_drop_monitor);
1781cad456d5SNeil Horman module_exit(exit_net_drop_monitor);
1782cad456d5SNeil Horman
1783cad456d5SNeil Horman MODULE_LICENSE("GPL v2");
1784cad456d5SNeil Horman MODULE_AUTHOR("Neil Horman <nhorman@tuxdriver.com>");
17853fdcbd45SNeil Horman MODULE_ALIAS_GENL_FAMILY("NET_DM");
178667c20de3SRob Gill MODULE_DESCRIPTION("Monitoring code for network dropped packet alerts");
1787