Blame view
net/core/drop_monitor.c
10.1 KB
9a8afc8d3 Network Drop Moni... |
1 2 3 4 5 |
/* * Monitoring code for network dropped packet alerts * * Copyright (C) 2009 Neil Horman <nhorman@tuxdriver.com> */ |
e005d193d net: core: Use pr... |
6 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
9a8afc8d3 Network Drop Moni... |
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/string.h> #include <linux/if_arp.h> #include <linux/inetdevice.h> #include <linux/inet.h> #include <linux/interrupt.h> #include <linux/netpoll.h> #include <linux/sched.h> #include <linux/delay.h> #include <linux/types.h> #include <linux/workqueue.h> #include <linux/netlink.h> #include <linux/net_dropmon.h> #include <linux/percpu.h> #include <linux/timer.h> #include <linux/bitops.h> |
5a0e3ad6a include cleanup: ... |
24 |
#include <linux/slab.h> |
cad456d5a drop_monitor: con... |
25 |
#include <linux/module.h> |
9a8afc8d3 Network Drop Moni... |
26 |
#include <net/genetlink.h> |
4ea7e3869 dropmon: add abil... |
27 |
#include <net/netevent.h> |
9a8afc8d3 Network Drop Moni... |
28 |
|
ad8d75fff tracing/events: m... |
29 |
#include <trace/events/skb.h> |
9cbc1cb8c Merge branch 'mas... |
30 |
#include <trace/events/napi.h> |
9a8afc8d3 Network Drop Moni... |
31 32 33 34 35 |
#include <asm/unaligned.h> #define TRACE_ON 1 #define TRACE_OFF 0 |
9a8afc8d3 Network Drop Moni... |
36 37 38 39 40 |
/* * Globals, our netlink socket pointer * and the work handle that will send up * netlink alerts */ |
4ea7e3869 dropmon: add abil... |
41 |
static int trace_state = TRACE_OFF; |
cde2e9a65 drop_monitor: fix... |
42 |
static DEFINE_MUTEX(trace_state_mutex); |
9a8afc8d3 Network Drop Moni... |
43 44 |
struct per_cpu_dm_data { |
bec4596b4 drop_monitor: don... |
45 46 47 48 |
spinlock_t lock; struct sk_buff *skb; struct work_struct dm_alert_work; struct timer_list send_timer; |
9a8afc8d3 Network Drop Moni... |
49 |
}; |
4ea7e3869 dropmon: add abil... |
50 51 |
struct dm_hw_stat_delta { struct net_device *dev; |
5848cc096 net: drop_monitor... |
52 |
unsigned long last_rx; |
4ea7e3869 dropmon: add abil... |
53 54 55 56 |
struct list_head list; struct rcu_head rcu; unsigned long last_drop_val; }; |
9a8afc8d3 Network Drop Moni... |
57 58 59 60 |
static struct genl_family net_drop_monitor_family = { .id = GENL_ID_GENERATE, .hdrsize = 0, .name = "NET_DM", |
683703a26 drop_monitor: Upd... |
61 |
.version = 2, |
9a8afc8d3 Network Drop Moni... |
62 63 64 65 66 67 |
}; static DEFINE_PER_CPU(struct per_cpu_dm_data, dm_cpu_data); static int dm_hit_limit = 64; static int dm_delay = 1; |
4ea7e3869 dropmon: add abil... |
68 69 |
static unsigned long dm_hw_check_delta = 2*HZ; static LIST_HEAD(hw_stats_list); |
9a8afc8d3 Network Drop Moni... |
70 |
|
bec4596b4 drop_monitor: don... |
71 |
static struct sk_buff *reset_per_cpu_data(struct per_cpu_dm_data *data) |
9a8afc8d3 Network Drop Moni... |
72 73 74 |
{ size_t al; struct net_dm_alert_msg *msg; |
683703a26 drop_monitor: Upd... |
75 |
struct nlattr *nla; |
3885ca785 drop_monitor: Mak... |
76 |
struct sk_buff *skb; |
bec4596b4 drop_monitor: don... |
77 |
unsigned long flags; |
9f7551e05 drop_monitor: add... |
78 |
void *msg_header; |
9a8afc8d3 Network Drop Moni... |
79 80 81 |
al = sizeof(struct net_dm_alert_msg); al += dm_hit_limit * sizeof(struct net_dm_drop_point); |
683703a26 drop_monitor: Upd... |
82 |
al += sizeof(struct nlattr); |
3885ca785 drop_monitor: Mak... |
83 |
skb = genlmsg_new(al, GFP_KERNEL); |
9f7551e05 drop_monitor: add... |
84 85 |
if (!skb) goto err; |
3885ca785 drop_monitor: Mak... |
86 |
|
9f7551e05 drop_monitor: add... |
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
msg_header = genlmsg_put(skb, 0, 0, &net_drop_monitor_family, 0, NET_DM_CMD_ALERT); if (!msg_header) { nlmsg_free(skb); skb = NULL; goto err; } nla = nla_reserve(skb, NLA_UNSPEC, sizeof(struct net_dm_alert_msg)); if (!nla) { nlmsg_free(skb); skb = NULL; goto err; } msg = nla_data(nla); memset(msg, 0, al); |
9f7551e05 drop_monitor: add... |
103 104 105 106 107 |
goto out; err: mod_timer(&data->send_timer, jiffies + HZ / 10); out: |
bec4596b4 drop_monitor: don... |
108 109 110 |
spin_lock_irqsave(&data->lock, flags); swap(data->skb, skb); spin_unlock_irqrestore(&data->lock, flags); |
9f65f5d47 drop_monitor: con... |
111 112 113 114 115 116 |
if (skb) { struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data; struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlh); genlmsg_end(skb, genlmsg_data(gnlh)); } |
bec4596b4 drop_monitor: don... |
117 |
return skb; |
9a8afc8d3 Network Drop Moni... |
118 |
} |
85bae4bd8 drop_monitor: mak... |
119 |
static const struct genl_multicast_group dropmon_mcgrps[] = { |
2a94fe48f genetlink: make m... |
120 |
{ .name = "events", }, |
e5dcecba0 drop_monitor/gene... |
121 |
}; |
bec4596b4 drop_monitor: don... |
122 |
static void send_dm_alert(struct work_struct *work) |
9a8afc8d3 Network Drop Moni... |
123 124 |
{ struct sk_buff *skb; |
bec4596b4 drop_monitor: don... |
125 |
struct per_cpu_dm_data *data; |
9a8afc8d3 Network Drop Moni... |
126 |
|
bec4596b4 drop_monitor: don... |
127 |
data = container_of(work, struct per_cpu_dm_data, dm_alert_work); |
4fdcfa128 drop_monitor: pre... |
128 |
|
bec4596b4 drop_monitor: don... |
129 |
skb = reset_per_cpu_data(data); |
9a8afc8d3 Network Drop Moni... |
130 |
|
3885ca785 drop_monitor: Mak... |
131 |
if (skb) |
68eb55031 genetlink: pass f... |
132 |
genlmsg_multicast(&net_drop_monitor_family, skb, 0, |
2a94fe48f genetlink: make m... |
133 |
0, GFP_KERNEL); |
9a8afc8d3 Network Drop Moni... |
134 135 136 137 138 |
} /* * This is the timer function to delay the sending of an alert * in the event that more drops will arrive during the |
bec4596b4 drop_monitor: don... |
139 |
* hysteresis period. |
9a8afc8d3 Network Drop Moni... |
140 |
*/ |
bec4596b4 drop_monitor: don... |
141 |
static void sched_send_work(unsigned long _data) |
9a8afc8d3 Network Drop Moni... |
142 |
{ |
bec4596b4 drop_monitor: don... |
143 |
struct per_cpu_dm_data *data = (struct per_cpu_dm_data *)_data; |
3885ca785 drop_monitor: Mak... |
144 |
|
bec4596b4 drop_monitor: don... |
145 |
schedule_work(&data->dm_alert_work); |
9a8afc8d3 Network Drop Moni... |
146 |
} |
4ea7e3869 dropmon: add abil... |
147 |
static void trace_drop_common(struct sk_buff *skb, void *location) |
9a8afc8d3 Network Drop Moni... |
148 149 150 |
{ struct net_dm_alert_msg *msg; struct nlmsghdr *nlh; |
683703a26 drop_monitor: Upd... |
151 |
struct nlattr *nla; |
9a8afc8d3 Network Drop Moni... |
152 |
int i; |
3885ca785 drop_monitor: Mak... |
153 |
struct sk_buff *dskb; |
bec4596b4 drop_monitor: don... |
154 155 |
struct per_cpu_dm_data *data; unsigned long flags; |
9a8afc8d3 Network Drop Moni... |
156 |
|
bec4596b4 drop_monitor: don... |
157 |
local_irq_save(flags); |
903ceff7c net: Replace get_... |
158 |
data = this_cpu_ptr(&dm_cpu_data); |
bec4596b4 drop_monitor: don... |
159 160 |
spin_lock(&data->lock); dskb = data->skb; |
3885ca785 drop_monitor: Mak... |
161 162 163 |
if (!dskb) goto out; |
3885ca785 drop_monitor: Mak... |
164 |
nlh = (struct nlmsghdr *)dskb->data; |
683703a26 drop_monitor: Upd... |
165 166 |
nla = genlmsg_data(nlmsg_data(nlh)); msg = nla_data(nla); |
9a8afc8d3 Network Drop Moni... |
167 168 169 170 171 172 |
for (i = 0; i < msg->entries; i++) { if (!memcmp(&location, msg->points[i].pc, sizeof(void *))) { msg->points[i].count++; goto out; } } |
bec4596b4 drop_monitor: don... |
173 174 |
if (msg->entries == dm_hit_limit) goto out; |
9a8afc8d3 Network Drop Moni... |
175 176 177 |
/* * We need to create a new entry */ |
3885ca785 drop_monitor: Mak... |
178 |
__nla_reserve_nohdr(dskb, sizeof(struct net_dm_drop_point)); |
683703a26 drop_monitor: Upd... |
179 |
nla->nla_len += NLA_ALIGN(sizeof(struct net_dm_drop_point)); |
9a8afc8d3 Network Drop Moni... |
180 181 182 183 184 185 |
memcpy(msg->points[msg->entries].pc, &location, sizeof(void *)); msg->points[msg->entries].count = 1; msg->entries++; if (!timer_pending(&data->send_timer)) { data->send_timer.expires = jiffies + dm_delay * HZ; |
bec4596b4 drop_monitor: don... |
186 |
add_timer(&data->send_timer); |
9a8afc8d3 Network Drop Moni... |
187 188 189 |
} out: |
bec4596b4 drop_monitor: don... |
190 |
spin_unlock_irqrestore(&data->lock, flags); |
9a8afc8d3 Network Drop Moni... |
191 |
} |
38516ab59 tracing: Let trac... |
192 |
static void trace_kfree_skb_hit(void *ignore, struct sk_buff *skb, void *location) |
4ea7e3869 dropmon: add abil... |
193 194 195 |
{ trace_drop_common(skb, location); } |
1db19db7f net: tracepoint n... |
196 197 |
static void trace_napi_poll_hit(void *ignore, struct napi_struct *napi, int work, int budget) |
4ea7e3869 dropmon: add abil... |
198 199 200 201 |
{ struct dm_hw_stat_delta *new_stat; /* |
5848cc096 net: drop_monitor... |
202 |
* Don't check napi structures with no associated device |
4ea7e3869 dropmon: add abil... |
203 |
*/ |
5848cc096 net: drop_monitor... |
204 |
if (!napi->dev) |
4ea7e3869 dropmon: add abil... |
205 206 207 208 |
return; rcu_read_lock(); list_for_each_entry_rcu(new_stat, &hw_stats_list, list) { |
5848cc096 net: drop_monitor... |
209 210 211 212 213 214 |
/* * only add a note to our monitor buffer if: * 1) this is the dev we received on * 2) its after the last_rx delta * 3) our rx_dropped count has gone up */ |
4ea7e3869 dropmon: add abil... |
215 |
if ((new_stat->dev == napi->dev) && |
5848cc096 net: drop_monitor... |
216 |
(time_after(jiffies, new_stat->last_rx + dm_hw_check_delta)) && |
4ea7e3869 dropmon: add abil... |
217 218 219 |
(napi->dev->stats.rx_dropped != new_stat->last_drop_val)) { trace_drop_common(NULL, NULL); new_stat->last_drop_val = napi->dev->stats.rx_dropped; |
5848cc096 net: drop_monitor... |
220 |
new_stat->last_rx = jiffies; |
4ea7e3869 dropmon: add abil... |
221 222 223 224 225 |
break; } } rcu_read_unlock(); } |
9a8afc8d3 Network Drop Moni... |
226 227 228 |
static int set_all_monitor_traces(int state) { int rc = 0; |
4ea7e3869 dropmon: add abil... |
229 230 |
struct dm_hw_stat_delta *new_stat = NULL; struct dm_hw_stat_delta *temp; |
cde2e9a65 drop_monitor: fix... |
231 |
mutex_lock(&trace_state_mutex); |
9a8afc8d3 Network Drop Moni... |
232 |
|
4b706372f drop_monitor: Add... |
233 234 235 236 |
if (state == trace_state) { rc = -EAGAIN; goto out_unlock; } |
9a8afc8d3 Network Drop Moni... |
237 238 |
switch (state) { case TRACE_ON: |
cad456d5a drop_monitor: con... |
239 240 241 242 |
if (!try_module_get(THIS_MODULE)) { rc = -ENODEV; break; } |
38516ab59 tracing: Let trac... |
243 244 |
rc |= register_trace_kfree_skb(trace_kfree_skb_hit, NULL); rc |= register_trace_napi_poll(trace_napi_poll_hit, NULL); |
9a8afc8d3 Network Drop Moni... |
245 |
break; |
cad456d5a drop_monitor: con... |
246 |
|
9a8afc8d3 Network Drop Moni... |
247 |
case TRACE_OFF: |
38516ab59 tracing: Let trac... |
248 249 |
rc |= unregister_trace_kfree_skb(trace_kfree_skb_hit, NULL); rc |= unregister_trace_napi_poll(trace_napi_poll_hit, NULL); |
9a8afc8d3 Network Drop Moni... |
250 251 |
tracepoint_synchronize_unregister(); |
4ea7e3869 dropmon: add abil... |
252 253 254 255 256 257 258 |
/* * Clean the device list */ list_for_each_entry_safe(new_stat, temp, &hw_stats_list, list) { if (new_stat->dev == NULL) { list_del_rcu(&new_stat->list); |
fa81c0e1d net,rcu: convert ... |
259 |
kfree_rcu(new_stat, rcu); |
4ea7e3869 dropmon: add abil... |
260 261 |
} } |
cad456d5a drop_monitor: con... |
262 263 |
module_put(THIS_MODULE); |
9a8afc8d3 Network Drop Moni... |
264 265 266 267 268 |
break; default: rc = 1; break; } |
4ea7e3869 dropmon: add abil... |
269 270 |
if (!rc) trace_state = state; |
4b706372f drop_monitor: Add... |
271 272 |
else rc = -EINPROGRESS; |
4ea7e3869 dropmon: add abil... |
273 |
|
4b706372f drop_monitor: Add... |
274 |
out_unlock: |
cde2e9a65 drop_monitor: fix... |
275 |
mutex_unlock(&trace_state_mutex); |
4ea7e3869 dropmon: add abil... |
276 |
|
9a8afc8d3 Network Drop Moni... |
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
return rc; } static int net_dm_cmd_config(struct sk_buff *skb, struct genl_info *info) { return -ENOTSUPP; } static int net_dm_cmd_trace(struct sk_buff *skb, struct genl_info *info) { switch (info->genlhdr->cmd) { case NET_DM_CMD_START: return set_all_monitor_traces(TRACE_ON); |
9a8afc8d3 Network Drop Moni... |
293 294 |
case NET_DM_CMD_STOP: return set_all_monitor_traces(TRACE_OFF); |
9a8afc8d3 Network Drop Moni... |
295 296 297 298 |
} return -ENOTSUPP; } |
4ea7e3869 dropmon: add abil... |
299 |
static int dropmon_net_event(struct notifier_block *ev_block, |
351638e7d net: pass info st... |
300 |
unsigned long event, void *ptr) |
4ea7e3869 dropmon: add abil... |
301 |
{ |
351638e7d net: pass info st... |
302 |
struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
4ea7e3869 dropmon: add abil... |
303 304 305 306 307 308 309 310 311 312 313 |
struct dm_hw_stat_delta *new_stat = NULL; struct dm_hw_stat_delta *tmp; switch (event) { case NETDEV_REGISTER: new_stat = kzalloc(sizeof(struct dm_hw_stat_delta), GFP_KERNEL); if (!new_stat) goto out; new_stat->dev = dev; |
5848cc096 net: drop_monitor... |
314 |
new_stat->last_rx = jiffies; |
cde2e9a65 drop_monitor: fix... |
315 |
mutex_lock(&trace_state_mutex); |
4ea7e3869 dropmon: add abil... |
316 |
list_add_rcu(&new_stat->list, &hw_stats_list); |
cde2e9a65 drop_monitor: fix... |
317 |
mutex_unlock(&trace_state_mutex); |
4ea7e3869 dropmon: add abil... |
318 319 |
break; case NETDEV_UNREGISTER: |
cde2e9a65 drop_monitor: fix... |
320 |
mutex_lock(&trace_state_mutex); |
4ea7e3869 dropmon: add abil... |
321 322 323 324 325 |
list_for_each_entry_safe(new_stat, tmp, &hw_stats_list, list) { if (new_stat->dev == dev) { new_stat->dev = NULL; if (trace_state == TRACE_OFF) { list_del_rcu(&new_stat->list); |
fa81c0e1d net,rcu: convert ... |
326 |
kfree_rcu(new_stat, rcu); |
4ea7e3869 dropmon: add abil... |
327 328 329 330 |
break; } } } |
cde2e9a65 drop_monitor: fix... |
331 |
mutex_unlock(&trace_state_mutex); |
4ea7e3869 dropmon: add abil... |
332 333 334 335 336 |
break; } out: return NOTIFY_DONE; } |
9a8afc8d3 Network Drop Moni... |
337 |
|
4534de830 genetlink: make a... |
338 |
static const struct genl_ops dropmon_ops[] = { |
9a8afc8d3 Network Drop Moni... |
339 340 341 342 343 344 345 346 347 348 349 350 351 |
{ .cmd = NET_DM_CMD_CONFIG, .doit = net_dm_cmd_config, }, { .cmd = NET_DM_CMD_START, .doit = net_dm_cmd_trace, }, { .cmd = NET_DM_CMD_STOP, .doit = net_dm_cmd_trace, }, }; |
4ea7e3869 dropmon: add abil... |
352 353 354 |
static struct notifier_block dropmon_net_notifier = { .notifier_call = dropmon_net_event }; |
9a8afc8d3 Network Drop Moni... |
355 356 |
static int __init init_net_drop_monitor(void) { |
9a8afc8d3 Network Drop Moni... |
357 |
struct per_cpu_dm_data *data; |
a256be70c drop_monitor: use... |
358 |
int cpu, rc; |
e005d193d net: core: Use pr... |
359 360 |
pr_info("Initializing network drop monitor service "); |
9a8afc8d3 Network Drop Moni... |
361 362 |
if (sizeof(void *) > 8) { |
e005d193d net: core: Use pr... |
363 364 |
pr_err("Unable to store program counters on this arch, Drop monitor failed "); |
9a8afc8d3 Network Drop Moni... |
365 366 |
return -ENOSPC; } |
2a94fe48f genetlink: make m... |
367 368 |
rc = genl_register_family_with_ops_groups(&net_drop_monitor_family, dropmon_ops, dropmon_mcgrps); |
a256be70c drop_monitor: use... |
369 |
if (rc) { |
e005d193d net: core: Use pr... |
370 371 |
pr_err("Could not create drop monitor netlink family "); |
a256be70c drop_monitor: use... |
372 |
return rc; |
9a8afc8d3 Network Drop Moni... |
373 |
} |
2a94fe48f genetlink: make m... |
374 |
WARN_ON(net_drop_monitor_family.mcgrp_offset != NET_DM_GRP_ALERT); |
e5dcecba0 drop_monitor/gene... |
375 |
|
4ea7e3869 dropmon: add abil... |
376 377 |
rc = register_netdevice_notifier(&dropmon_net_notifier); if (rc < 0) { |
e005d193d net: core: Use pr... |
378 379 |
pr_crit("Failed to register netdevice notifier "); |
4ea7e3869 dropmon: add abil... |
380 381 |
goto out_unreg; } |
9a8afc8d3 Network Drop Moni... |
382 |
rc = 0; |
cad456d5a drop_monitor: con... |
383 |
for_each_possible_cpu(cpu) { |
9a8afc8d3 Network Drop Moni... |
384 |
data = &per_cpu(dm_cpu_data, cpu); |
9a8afc8d3 Network Drop Moni... |
385 386 |
INIT_WORK(&data->dm_alert_work, send_dm_alert); init_timer(&data->send_timer); |
bec4596b4 drop_monitor: don... |
387 |
data->send_timer.data = (unsigned long)data; |
9a8afc8d3 Network Drop Moni... |
388 |
data->send_timer.function = sched_send_work; |
bec4596b4 drop_monitor: don... |
389 |
spin_lock_init(&data->lock); |
4fdcfa128 drop_monitor: pre... |
390 |
reset_per_cpu_data(data); |
9a8afc8d3 Network Drop Moni... |
391 |
} |
4ea7e3869 dropmon: add abil... |
392 |
|
3885ca785 drop_monitor: Mak... |
393 |
|
9a8afc8d3 Network Drop Moni... |
394 395 396 397 398 399 400 |
goto out; out_unreg: genl_unregister_family(&net_drop_monitor_family); out: return rc; } |
cad456d5a drop_monitor: con... |
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 |
static void exit_net_drop_monitor(void) { struct per_cpu_dm_data *data; int cpu; BUG_ON(unregister_netdevice_notifier(&dropmon_net_notifier)); /* * Because of the module_get/put we do in the trace state change path * we are guarnateed not to have any current users when we get here * all we need to do is make sure that we don't have any running timers * or pending schedule calls */ for_each_possible_cpu(cpu) { data = &per_cpu(dm_cpu_data, cpu); del_timer_sync(&data->send_timer); cancel_work_sync(&data->dm_alert_work); /* * At this point, we should have exclusive access * to this struct and can free the skb inside it */ kfree_skb(data->skb); } BUG_ON(genl_unregister_family(&net_drop_monitor_family)); } module_init(init_net_drop_monitor); module_exit(exit_net_drop_monitor); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Neil Horman <nhorman@tuxdriver.com>"); |
3fdcbd453 drop_monitor: Add... |
434 |
MODULE_ALIAS_GENL_FAMILY("NET_DM"); |