19a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman/*
29a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman * Monitoring code for network dropped packet alerts
39a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman *
49a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman * Copyright (C) 2009 Neil Horman <nhorman@tuxdriver.com>
59a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman */
69a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
7e005d193d55ee5f757b13306112d8c23aac27a88Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
8e005d193d55ee5f757b13306112d8c23aac27a88Joe Perches
99a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <linux/netdevice.h>
109a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <linux/etherdevice.h>
119a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <linux/string.h>
129a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <linux/if_arp.h>
139a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <linux/inetdevice.h>
149a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <linux/inet.h>
159a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <linux/interrupt.h>
169a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <linux/netpoll.h>
179a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <linux/sched.h>
189a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <linux/delay.h>
199a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <linux/types.h>
209a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <linux/workqueue.h>
219a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <linux/netlink.h>
229a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <linux/net_dropmon.h>
239a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <linux/percpu.h>
249a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <linux/timer.h>
259a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <linux/bitops.h>
265a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
27cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman#include <linux/module.h>
289a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <net/genetlink.h>
294ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman#include <net/netevent.h>
309a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
31ad8d75fff811a6a230f7f43b05a6483099349533Steven Rostedt#include <trace/events/skb.h>
329cbc1cb8cd46ce1f7645b9de249b2ce8460129bbDavid S. Miller#include <trace/events/napi.h>
339a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
349a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#include <asm/unaligned.h>
359a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
369a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#define TRACE_ON 1
379a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman#define TRACE_OFF 0
389a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
399a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman/*
409a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman * Globals, our netlink socket pointer
419a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman * and the work handle that will send up
429a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman * netlink alerts
439a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman */
444ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Hormanstatic int trace_state = TRACE_OFF;
45cde2e9a651b76d8db36ae94cd0febc82b637e5ddNeil Hormanstatic DEFINE_MUTEX(trace_state_mutex);
469a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
479a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Hormanstruct per_cpu_dm_data {
48bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	spinlock_t		lock;
49bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	struct sk_buff		*skb;
50bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	struct work_struct	dm_alert_work;
51bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	struct timer_list	send_timer;
529a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman};
539a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
544ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Hormanstruct dm_hw_stat_delta {
554ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	struct net_device *dev;
565848cc096a23b80b3d15c27d72299f79caf7c517Neil Horman	unsigned long last_rx;
574ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	struct list_head list;
584ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	struct rcu_head rcu;
594ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	unsigned long last_drop_val;
604ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman};
614ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman
629a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Hormanstatic struct genl_family net_drop_monitor_family = {
639a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	.id             = GENL_ID_GENERATE,
649a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	.hdrsize        = 0,
659a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	.name           = "NET_DM",
66683703a26e4677db437a1480682851e27c7a154fNeil Horman	.version        = 2,
679a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman};
689a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
699a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Hormanstatic DEFINE_PER_CPU(struct per_cpu_dm_data, dm_cpu_data);
709a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
719a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Hormanstatic int dm_hit_limit = 64;
729a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Hormanstatic int dm_delay = 1;
734ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Hormanstatic unsigned long dm_hw_check_delta = 2*HZ;
744ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Hormanstatic LIST_HEAD(hw_stats_list);
759a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
76bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazetstatic struct sk_buff *reset_per_cpu_data(struct per_cpu_dm_data *data)
779a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman{
789a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	size_t al;
799a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	struct net_dm_alert_msg *msg;
80683703a26e4677db437a1480682851e27c7a154fNeil Horman	struct nlattr *nla;
813885ca785a3618593226687ced84f3f336dc3860Neil Horman	struct sk_buff *skb;
82bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	unsigned long flags;
839a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
849a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	al = sizeof(struct net_dm_alert_msg);
859a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	al += dm_hit_limit * sizeof(struct net_dm_drop_point);
86683703a26e4677db437a1480682851e27c7a154fNeil Horman	al += sizeof(struct nlattr);
87683703a26e4677db437a1480682851e27c7a154fNeil Horman
883885ca785a3618593226687ced84f3f336dc3860Neil Horman	skb = genlmsg_new(al, GFP_KERNEL);
893885ca785a3618593226687ced84f3f336dc3860Neil Horman
903885ca785a3618593226687ced84f3f336dc3860Neil Horman	if (skb) {
913885ca785a3618593226687ced84f3f336dc3860Neil Horman		genlmsg_put(skb, 0, 0, &net_drop_monitor_family,
923885ca785a3618593226687ced84f3f336dc3860Neil Horman				0, NET_DM_CMD_ALERT);
933885ca785a3618593226687ced84f3f336dc3860Neil Horman		nla = nla_reserve(skb, NLA_UNSPEC,
943885ca785a3618593226687ced84f3f336dc3860Neil Horman				  sizeof(struct net_dm_alert_msg));
953885ca785a3618593226687ced84f3f336dc3860Neil Horman		msg = nla_data(nla);
963885ca785a3618593226687ced84f3f336dc3860Neil Horman		memset(msg, 0, al);
97bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	} else {
98bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet		mod_timer(&data->send_timer, jiffies + HZ / 10);
993885ca785a3618593226687ced84f3f336dc3860Neil Horman	}
1003885ca785a3618593226687ced84f3f336dc3860Neil Horman
101bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	spin_lock_irqsave(&data->lock, flags);
102bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	swap(data->skb, skb);
103bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	spin_unlock_irqrestore(&data->lock, flags);
104bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet
105bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	return skb;
1069a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman}
1079a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
1082a94fe48f32ccf7321450a2cc07f2b724a444e5bJohannes Bergstatic struct genl_multicast_group dropmon_mcgrps[] = {
1092a94fe48f32ccf7321450a2cc07f2b724a444e5bJohannes Berg	{ .name = "events", },
110e5dcecba015f9774a402ba559b80b16999747e3bJohannes Berg};
111e5dcecba015f9774a402ba559b80b16999747e3bJohannes Berg
112bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazetstatic void send_dm_alert(struct work_struct *work)
1139a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman{
1149a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	struct sk_buff *skb;
115bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	struct per_cpu_dm_data *data;
1169a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
117bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	data = container_of(work, struct per_cpu_dm_data, dm_alert_work);
1184fdcfa12843bca38d0c9deff70c8720e4e8f515fNeil Horman
119bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	skb = reset_per_cpu_data(data);
1209a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
1213885ca785a3618593226687ced84f3f336dc3860Neil Horman	if (skb)
12268eb55031da7c967d954e5f9415cd05f4abdb692Johannes Berg		genlmsg_multicast(&net_drop_monitor_family, skb, 0,
1232a94fe48f32ccf7321450a2cc07f2b724a444e5bJohannes Berg				  0, GFP_KERNEL);
1249a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman}
1259a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
1269a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman/*
1279a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman * This is the timer function to delay the sending of an alert
1289a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman * in the event that more drops will arrive during the
129bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet * hysteresis period.
1309a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman */
131bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazetstatic void sched_send_work(unsigned long _data)
1329a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman{
133bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	struct per_cpu_dm_data *data = (struct per_cpu_dm_data *)_data;
1343885ca785a3618593226687ced84f3f336dc3860Neil Horman
135bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	schedule_work(&data->dm_alert_work);
1369a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman}
1379a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
1384ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Hormanstatic void trace_drop_common(struct sk_buff *skb, void *location)
1399a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman{
1409a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	struct net_dm_alert_msg *msg;
1419a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	struct nlmsghdr *nlh;
142683703a26e4677db437a1480682851e27c7a154fNeil Horman	struct nlattr *nla;
1439a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	int i;
1443885ca785a3618593226687ced84f3f336dc3860Neil Horman	struct sk_buff *dskb;
145bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	struct per_cpu_dm_data *data;
146bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	unsigned long flags;
1479a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
148bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	local_irq_save(flags);
149903ceff7ca7b4d80c083a80ee5163b74e9fa359fChristoph Lameter	data = this_cpu_ptr(&dm_cpu_data);
150bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	spin_lock(&data->lock);
151bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	dskb = data->skb;
1523885ca785a3618593226687ced84f3f336dc3860Neil Horman
1533885ca785a3618593226687ced84f3f336dc3860Neil Horman	if (!dskb)
1543885ca785a3618593226687ced84f3f336dc3860Neil Horman		goto out;
1553885ca785a3618593226687ced84f3f336dc3860Neil Horman
1563885ca785a3618593226687ced84f3f336dc3860Neil Horman	nlh = (struct nlmsghdr *)dskb->data;
157683703a26e4677db437a1480682851e27c7a154fNeil Horman	nla = genlmsg_data(nlmsg_data(nlh));
158683703a26e4677db437a1480682851e27c7a154fNeil Horman	msg = nla_data(nla);
1599a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	for (i = 0; i < msg->entries; i++) {
1609a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		if (!memcmp(&location, msg->points[i].pc, sizeof(void *))) {
1619a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman			msg->points[i].count++;
1629a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman			goto out;
1639a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		}
1649a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	}
165bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	if (msg->entries == dm_hit_limit)
166bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet		goto out;
1679a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	/*
1689a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	 * We need to create a new entry
1699a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	 */
1703885ca785a3618593226687ced84f3f336dc3860Neil Horman	__nla_reserve_nohdr(dskb, sizeof(struct net_dm_drop_point));
171683703a26e4677db437a1480682851e27c7a154fNeil Horman	nla->nla_len += NLA_ALIGN(sizeof(struct net_dm_drop_point));
1729a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	memcpy(msg->points[msg->entries].pc, &location, sizeof(void *));
1739a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	msg->points[msg->entries].count = 1;
1749a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	msg->entries++;
1759a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
1769a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	if (!timer_pending(&data->send_timer)) {
1779a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		data->send_timer.expires = jiffies + dm_delay * HZ;
178bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet		add_timer(&data->send_timer);
1799a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	}
1809a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
1819a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Hormanout:
182bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet	spin_unlock_irqrestore(&data->lock, flags);
1839a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman}
1849a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
18538516ab59fbc5b3bb278cf5e1fe2867c70cff32eSteven Rostedtstatic void trace_kfree_skb_hit(void *ignore, struct sk_buff *skb, void *location)
1864ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman{
1874ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	trace_drop_common(skb, location);
1884ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman}
1894ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman
19038516ab59fbc5b3bb278cf5e1fe2867c70cff32eSteven Rostedtstatic void trace_napi_poll_hit(void *ignore, struct napi_struct *napi)
1914ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman{
1924ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	struct dm_hw_stat_delta *new_stat;
1934ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman
1944ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	/*
1955848cc096a23b80b3d15c27d72299f79caf7c517Neil Horman	 * Don't check napi structures with no associated device
1964ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	 */
1975848cc096a23b80b3d15c27d72299f79caf7c517Neil Horman	if (!napi->dev)
1984ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		return;
1994ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman
2004ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	rcu_read_lock();
2014ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	list_for_each_entry_rcu(new_stat, &hw_stats_list, list) {
2025848cc096a23b80b3d15c27d72299f79caf7c517Neil Horman		/*
2035848cc096a23b80b3d15c27d72299f79caf7c517Neil Horman		 * only add a note to our monitor buffer if:
2045848cc096a23b80b3d15c27d72299f79caf7c517Neil Horman		 * 1) this is the dev we received on
2055848cc096a23b80b3d15c27d72299f79caf7c517Neil Horman		 * 2) its after the last_rx delta
2065848cc096a23b80b3d15c27d72299f79caf7c517Neil Horman		 * 3) our rx_dropped count has gone up
2075848cc096a23b80b3d15c27d72299f79caf7c517Neil Horman		 */
2084ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		if ((new_stat->dev == napi->dev)  &&
2095848cc096a23b80b3d15c27d72299f79caf7c517Neil Horman		    (time_after(jiffies, new_stat->last_rx + dm_hw_check_delta)) &&
2104ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		    (napi->dev->stats.rx_dropped != new_stat->last_drop_val)) {
2114ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman			trace_drop_common(NULL, NULL);
2124ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman			new_stat->last_drop_val = napi->dev->stats.rx_dropped;
2135848cc096a23b80b3d15c27d72299f79caf7c517Neil Horman			new_stat->last_rx = jiffies;
2144ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman			break;
2154ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		}
2164ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	}
2174ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	rcu_read_unlock();
2184ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman}
2194ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman
2209a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Hormanstatic int set_all_monitor_traces(int state)
2219a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman{
2229a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	int rc = 0;
2234ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	struct dm_hw_stat_delta *new_stat = NULL;
2244ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	struct dm_hw_stat_delta *temp;
2254ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman
226cde2e9a651b76d8db36ae94cd0febc82b637e5ddNeil Horman	mutex_lock(&trace_state_mutex);
2279a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
2284b706372f18de53970e4c6887a96459590fef80aNeil Horman	if (state == trace_state) {
2294b706372f18de53970e4c6887a96459590fef80aNeil Horman		rc = -EAGAIN;
2304b706372f18de53970e4c6887a96459590fef80aNeil Horman		goto out_unlock;
2314b706372f18de53970e4c6887a96459590fef80aNeil Horman	}
2324b706372f18de53970e4c6887a96459590fef80aNeil Horman
2339a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	switch (state) {
2349a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	case TRACE_ON:
235cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman		if (!try_module_get(THIS_MODULE)) {
236cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman			rc = -ENODEV;
237cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman			break;
238cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman		}
239cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman
24038516ab59fbc5b3bb278cf5e1fe2867c70cff32eSteven Rostedt		rc |= register_trace_kfree_skb(trace_kfree_skb_hit, NULL);
24138516ab59fbc5b3bb278cf5e1fe2867c70cff32eSteven Rostedt		rc |= register_trace_napi_poll(trace_napi_poll_hit, NULL);
2429a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		break;
243cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman
2449a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	case TRACE_OFF:
24538516ab59fbc5b3bb278cf5e1fe2867c70cff32eSteven Rostedt		rc |= unregister_trace_kfree_skb(trace_kfree_skb_hit, NULL);
24638516ab59fbc5b3bb278cf5e1fe2867c70cff32eSteven Rostedt		rc |= unregister_trace_napi_poll(trace_napi_poll_hit, NULL);
2479a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
2489a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		tracepoint_synchronize_unregister();
2494ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman
2504ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		/*
2514ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		 * Clean the device list
2524ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		 */
2534ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		list_for_each_entry_safe(new_stat, temp, &hw_stats_list, list) {
2544ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman			if (new_stat->dev == NULL) {
2554ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman				list_del_rcu(&new_stat->list);
256fa81c0e1d2d2176f1136c72c080c9ea4a98be347Lai Jiangshan				kfree_rcu(new_stat, rcu);
2574ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman			}
2584ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		}
259cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman
260cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman		module_put(THIS_MODULE);
261cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman
2629a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		break;
2639a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	default:
2649a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		rc = 1;
2659a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		break;
2669a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	}
2679a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
2684ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	if (!rc)
2694ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		trace_state = state;
2704b706372f18de53970e4c6887a96459590fef80aNeil Horman	else
2714b706372f18de53970e4c6887a96459590fef80aNeil Horman		rc = -EINPROGRESS;
2724ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman
2734b706372f18de53970e4c6887a96459590fef80aNeil Hormanout_unlock:
274cde2e9a651b76d8db36ae94cd0febc82b637e5ddNeil Horman	mutex_unlock(&trace_state_mutex);
2754ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman
2769a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	return rc;
2779a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman}
2789a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
2799a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
2809a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Hormanstatic int net_dm_cmd_config(struct sk_buff *skb,
2819a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman			struct genl_info *info)
2829a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman{
2839a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	return -ENOTSUPP;
2849a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman}
2859a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
2869a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Hormanstatic int net_dm_cmd_trace(struct sk_buff *skb,
2879a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman			struct genl_info *info)
2889a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman{
2899a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	switch (info->genlhdr->cmd) {
2909a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	case NET_DM_CMD_START:
2919a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		return set_all_monitor_traces(TRACE_ON);
2929a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	case NET_DM_CMD_STOP:
2939a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		return set_all_monitor_traces(TRACE_OFF);
2949a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	}
2959a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
2969a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	return -ENOTSUPP;
2979a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman}
2989a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
2994ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Hormanstatic int dropmon_net_event(struct notifier_block *ev_block,
300351638e7deeed2ec8ce451b53d33921b3da68f83Jiri Pirko			     unsigned long event, void *ptr)
3014ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman{
302351638e7deeed2ec8ce451b53d33921b3da68f83Jiri Pirko	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3034ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	struct dm_hw_stat_delta *new_stat = NULL;
3044ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	struct dm_hw_stat_delta *tmp;
3054ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman
3064ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	switch (event) {
3074ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	case NETDEV_REGISTER:
3084ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		new_stat = kzalloc(sizeof(struct dm_hw_stat_delta), GFP_KERNEL);
3094ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman
3104ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		if (!new_stat)
3114ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman			goto out;
3124ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman
3134ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		new_stat->dev = dev;
3145848cc096a23b80b3d15c27d72299f79caf7c517Neil Horman		new_stat->last_rx = jiffies;
315cde2e9a651b76d8db36ae94cd0febc82b637e5ddNeil Horman		mutex_lock(&trace_state_mutex);
3164ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		list_add_rcu(&new_stat->list, &hw_stats_list);
317cde2e9a651b76d8db36ae94cd0febc82b637e5ddNeil Horman		mutex_unlock(&trace_state_mutex);
3184ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		break;
3194ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	case NETDEV_UNREGISTER:
320cde2e9a651b76d8db36ae94cd0febc82b637e5ddNeil Horman		mutex_lock(&trace_state_mutex);
3214ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		list_for_each_entry_safe(new_stat, tmp, &hw_stats_list, list) {
3224ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman			if (new_stat->dev == dev) {
3234ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman				new_stat->dev = NULL;
3244ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman				if (trace_state == TRACE_OFF) {
3254ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman					list_del_rcu(&new_stat->list);
326fa81c0e1d2d2176f1136c72c080c9ea4a98be347Lai Jiangshan					kfree_rcu(new_stat, rcu);
3274ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman					break;
3284ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman				}
3294ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman			}
3304ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		}
331cde2e9a651b76d8db36ae94cd0febc82b637e5ddNeil Horman		mutex_unlock(&trace_state_mutex);
3324ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		break;
3334ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	}
3344ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Hormanout:
3354ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	return NOTIFY_DONE;
3364ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman}
3379a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
3384534de8305b3f1460a527a0cda0e3dc2224c6f0cJohannes Bergstatic const struct genl_ops dropmon_ops[] = {
3399a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	{
3409a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		.cmd = NET_DM_CMD_CONFIG,
3419a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		.doit = net_dm_cmd_config,
3429a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	},
3439a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	{
3449a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		.cmd = NET_DM_CMD_START,
3459a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		.doit = net_dm_cmd_trace,
3469a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	},
3479a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	{
3489a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		.cmd = NET_DM_CMD_STOP,
3499a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		.doit = net_dm_cmd_trace,
3509a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	},
3519a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman};
3529a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
3534ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Hormanstatic struct notifier_block dropmon_net_notifier = {
3544ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	.notifier_call = dropmon_net_event
3554ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman};
3564ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman
3579a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Hormanstatic int __init init_net_drop_monitor(void)
3589a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman{
3599a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	struct per_cpu_dm_data *data;
360a256be70c57d6f8c827d09d645a1f6fe9330af72Changli Gao	int cpu, rc;
361a256be70c57d6f8c827d09d645a1f6fe9330af72Changli Gao
362e005d193d55ee5f757b13306112d8c23aac27a88Joe Perches	pr_info("Initializing network drop monitor service\n");
3639a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
3649a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	if (sizeof(void *) > 8) {
365e005d193d55ee5f757b13306112d8c23aac27a88Joe Perches		pr_err("Unable to store program counters on this arch, Drop monitor failed\n");
3669a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		return -ENOSPC;
3679a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	}
3689a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
3692a94fe48f32ccf7321450a2cc07f2b724a444e5bJohannes Berg	rc = genl_register_family_with_ops_groups(&net_drop_monitor_family,
3702a94fe48f32ccf7321450a2cc07f2b724a444e5bJohannes Berg						  dropmon_ops, dropmon_mcgrps);
371a256be70c57d6f8c827d09d645a1f6fe9330af72Changli Gao	if (rc) {
372e005d193d55ee5f757b13306112d8c23aac27a88Joe Perches		pr_err("Could not create drop monitor netlink family\n");
373a256be70c57d6f8c827d09d645a1f6fe9330af72Changli Gao		return rc;
3749a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	}
3752a94fe48f32ccf7321450a2cc07f2b724a444e5bJohannes Berg	WARN_ON(net_drop_monitor_family.mcgrp_offset != NET_DM_GRP_ALERT);
376e5dcecba015f9774a402ba559b80b16999747e3bJohannes Berg
3774ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	rc = register_netdevice_notifier(&dropmon_net_notifier);
3784ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	if (rc < 0) {
379e005d193d55ee5f757b13306112d8c23aac27a88Joe Perches		pr_crit("Failed to register netdevice notifier\n");
3804ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman		goto out_unreg;
3814ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman	}
3824ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman
3839a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	rc = 0;
3849a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
385cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman	for_each_possible_cpu(cpu) {
3869a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		data = &per_cpu(dm_cpu_data, cpu);
3879a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		INIT_WORK(&data->dm_alert_work, send_dm_alert);
3889a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		init_timer(&data->send_timer);
389bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet		data->send_timer.data = (unsigned long)data;
3909a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman		data->send_timer.function = sched_send_work;
391bec4596b4e6770c7037f21f6bd27567b152dc0d6Eric Dumazet		spin_lock_init(&data->lock);
3924fdcfa12843bca38d0c9deff70c8720e4e8f515fNeil Horman		reset_per_cpu_data(data);
3939a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	}
3944ea7e38696c7e798c47ebbecadfd392f23f814f9Neil Horman
3953885ca785a3618593226687ced84f3f336dc3860Neil Horman
3969a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	goto out;
3979a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
3989a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Hormanout_unreg:
3999a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	genl_unregister_family(&net_drop_monitor_family);
4009a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Hormanout:
4019a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman	return rc;
4029a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman}
4039a8afc8d3962f3ed26fd6b56db34133860ed1e72Neil Horman
404cad456d5abbb6307be7a658d701bc44ca689e906Neil Hormanstatic void exit_net_drop_monitor(void)
405cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman{
406cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman	struct per_cpu_dm_data *data;
407cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman	int cpu;
408cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman
409cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman	BUG_ON(unregister_netdevice_notifier(&dropmon_net_notifier));
410cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman
411cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman	/*
412cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman	 * Because of the module_get/put we do in the trace state change path
413cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman	 * we are guarnateed not to have any current users when we get here
414cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman	 * all we need to do is make sure that we don't have any running timers
415cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman	 * or pending schedule calls
416cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman	 */
417cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman
418cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman	for_each_possible_cpu(cpu) {
419cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman		data = &per_cpu(dm_cpu_data, cpu);
420cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman		del_timer_sync(&data->send_timer);
421cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman		cancel_work_sync(&data->dm_alert_work);
422cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman		/*
423cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman		 * At this point, we should have exclusive access
424cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman		 * to this struct and can free the skb inside it
425cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman		 */
426cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman		kfree_skb(data->skb);
427cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman	}
428cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman
429cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman	BUG_ON(genl_unregister_family(&net_drop_monitor_family));
430cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman}
431cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman
432cad456d5abbb6307be7a658d701bc44ca689e906Neil Hormanmodule_init(init_net_drop_monitor);
433cad456d5abbb6307be7a658d701bc44ca689e906Neil Hormanmodule_exit(exit_net_drop_monitor);
434cad456d5abbb6307be7a658d701bc44ca689e906Neil Horman
435cad456d5abbb6307be7a658d701bc44ca689e906Neil HormanMODULE_LICENSE("GPL v2");
436cad456d5abbb6307be7a658d701bc44ca689e906Neil HormanMODULE_AUTHOR("Neil Horman <nhorman@tuxdriver.com>");
4373fdcbd453152329002f12dfda0be90b714458164Neil HormanMODULE_ALIAS_GENL_FAMILY("NET_DM");
438