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