Blame view

net/core/drop_monitor.c 8.46 KB
9a8afc8d3   Neil Horman   Network Drop Moni...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  /*
   * Monitoring code for network dropped packet alerts
   *
   * Copyright (C) 2009 Neil Horman <nhorman@tuxdriver.com>
   */
  
  #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   Tejun Heo   include cleanup: ...
24
  #include <linux/slab.h>
9a8afc8d3   Neil Horman   Network Drop Moni...
25
  #include <net/genetlink.h>
4ea7e3869   Neil Horman   dropmon: add abil...
26
  #include <net/netevent.h>
9a8afc8d3   Neil Horman   Network Drop Moni...
27

ad8d75fff   Steven Rostedt   tracing/events: m...
28
  #include <trace/events/skb.h>
9cbc1cb8c   David S. Miller   Merge branch 'mas...
29
  #include <trace/events/napi.h>
9a8afc8d3   Neil Horman   Network Drop Moni...
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  
  #include <asm/unaligned.h>
  
  #define TRACE_ON 1
  #define TRACE_OFF 0
  
  static void send_dm_alert(struct work_struct *unused);
  
  
  /*
   * Globals, our netlink socket pointer
   * and the work handle that will send up
   * netlink alerts
   */
4ea7e3869   Neil Horman   dropmon: add abil...
44
  static int trace_state = TRACE_OFF;
d3bcfefac   Thomas Gleixner   net: Replace old ...
45
  static DEFINE_SPINLOCK(trace_state_lock);
9a8afc8d3   Neil Horman   Network Drop Moni...
46
47
48
49
50
51
52
  
  struct per_cpu_dm_data {
  	struct work_struct dm_alert_work;
  	struct sk_buff *skb;
  	atomic_t dm_hit_count;
  	struct timer_list send_timer;
  };
4ea7e3869   Neil Horman   dropmon: add abil...
53
54
  struct dm_hw_stat_delta {
  	struct net_device *dev;
5848cc096   Neil Horman   net: drop_monitor...
55
  	unsigned long last_rx;
4ea7e3869   Neil Horman   dropmon: add abil...
56
57
58
59
  	struct list_head list;
  	struct rcu_head rcu;
  	unsigned long last_drop_val;
  };
9a8afc8d3   Neil Horman   Network Drop Moni...
60
61
62
63
  static struct genl_family net_drop_monitor_family = {
  	.id             = GENL_ID_GENERATE,
  	.hdrsize        = 0,
  	.name           = "NET_DM",
683703a26   Neil Horman   drop_monitor: Upd...
64
  	.version        = 2,
9a8afc8d3   Neil Horman   Network Drop Moni...
65
66
67
68
69
70
71
  	.maxattr        = NET_DM_CMD_MAX,
  };
  
  static DEFINE_PER_CPU(struct per_cpu_dm_data, dm_cpu_data);
  
  static int dm_hit_limit = 64;
  static int dm_delay = 1;
4ea7e3869   Neil Horman   dropmon: add abil...
72
73
  static unsigned long dm_hw_check_delta = 2*HZ;
  static LIST_HEAD(hw_stats_list);
9a8afc8d3   Neil Horman   Network Drop Moni...
74
75
76
77
78
  
  static void reset_per_cpu_data(struct per_cpu_dm_data *data)
  {
  	size_t al;
  	struct net_dm_alert_msg *msg;
683703a26   Neil Horman   drop_monitor: Upd...
79
  	struct nlattr *nla;
9a8afc8d3   Neil Horman   Network Drop Moni...
80
81
82
  
  	al = sizeof(struct net_dm_alert_msg);
  	al += dm_hit_limit * sizeof(struct net_dm_drop_point);
683703a26   Neil Horman   drop_monitor: Upd...
83
  	al += sizeof(struct nlattr);
9a8afc8d3   Neil Horman   Network Drop Moni...
84
85
86
  	data->skb = genlmsg_new(al, GFP_KERNEL);
  	genlmsg_put(data->skb, 0, 0, &net_drop_monitor_family,
  			0, NET_DM_CMD_ALERT);
683703a26   Neil Horman   drop_monitor: Upd...
87
88
  	nla = nla_reserve(data->skb, NLA_UNSPEC, sizeof(struct net_dm_alert_msg));
  	msg = nla_data(nla);
9a8afc8d3   Neil Horman   Network Drop Moni...
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  	memset(msg, 0, al);
  	atomic_set(&data->dm_hit_count, dm_hit_limit);
  }
  
  static void send_dm_alert(struct work_struct *unused)
  {
  	struct sk_buff *skb;
  	struct per_cpu_dm_data *data = &__get_cpu_var(dm_cpu_data);
  
  	/*
  	 * Grab the skb we're about to send
  	 */
  	skb = data->skb;
  
  	/*
  	 * Replace it with a new one
  	 */
  	reset_per_cpu_data(data);
  
  	/*
  	 * Ship it!
  	 */
  	genlmsg_multicast(skb, 0, NET_DM_GRP_ALERT, GFP_KERNEL);
  
  }
  
  /*
   * This is the timer function to delay the sending of an alert
   * in the event that more drops will arrive during the
   * hysteresis period.  Note that it operates under the timer interrupt
   * so we don't need to disable preemption here
   */
  static void sched_send_work(unsigned long unused)
  {
  	struct per_cpu_dm_data *data =  &__get_cpu_var(dm_cpu_data);
  
  	schedule_work(&data->dm_alert_work);
  }
4ea7e3869   Neil Horman   dropmon: add abil...
127
  static void trace_drop_common(struct sk_buff *skb, void *location)
9a8afc8d3   Neil Horman   Network Drop Moni...
128
129
130
  {
  	struct net_dm_alert_msg *msg;
  	struct nlmsghdr *nlh;
683703a26   Neil Horman   drop_monitor: Upd...
131
  	struct nlattr *nla;
9a8afc8d3   Neil Horman   Network Drop Moni...
132
133
134
135
136
137
138
139
140
141
142
143
  	int i;
  	struct per_cpu_dm_data *data = &__get_cpu_var(dm_cpu_data);
  
  
  	if (!atomic_add_unless(&data->dm_hit_count, -1, 0)) {
  		/*
  		 * we're already at zero, discard this hit
  		 */
  		goto out;
  	}
  
  	nlh = (struct nlmsghdr *)data->skb->data;
683703a26   Neil Horman   drop_monitor: Upd...
144
145
  	nla = genlmsg_data(nlmsg_data(nlh));
  	msg = nla_data(nla);
9a8afc8d3   Neil Horman   Network Drop Moni...
146
147
148
149
150
151
152
153
154
155
156
  	for (i = 0; i < msg->entries; i++) {
  		if (!memcmp(&location, msg->points[i].pc, sizeof(void *))) {
  			msg->points[i].count++;
  			goto out;
  		}
  	}
  
  	/*
  	 * We need to create a new entry
  	 */
  	__nla_reserve_nohdr(data->skb, sizeof(struct net_dm_drop_point));
683703a26   Neil Horman   drop_monitor: Upd...
157
  	nla->nla_len += NLA_ALIGN(sizeof(struct net_dm_drop_point));
9a8afc8d3   Neil Horman   Network Drop Moni...
158
159
160
161
162
163
164
165
166
167
168
169
  	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;
  		add_timer_on(&data->send_timer, smp_processor_id());
  	}
  
  out:
  	return;
  }
38516ab59   Steven Rostedt   tracing: Let trac...
170
  static void trace_kfree_skb_hit(void *ignore, struct sk_buff *skb, void *location)
4ea7e3869   Neil Horman   dropmon: add abil...
171
172
173
  {
  	trace_drop_common(skb, location);
  }
38516ab59   Steven Rostedt   tracing: Let trac...
174
  static void trace_napi_poll_hit(void *ignore, struct napi_struct *napi)
4ea7e3869   Neil Horman   dropmon: add abil...
175
176
177
178
  {
  	struct dm_hw_stat_delta *new_stat;
  
  	/*
5848cc096   Neil Horman   net: drop_monitor...
179
  	 * Don't check napi structures with no associated device
4ea7e3869   Neil Horman   dropmon: add abil...
180
  	 */
5848cc096   Neil Horman   net: drop_monitor...
181
  	if (!napi->dev)
4ea7e3869   Neil Horman   dropmon: add abil...
182
183
184
185
  		return;
  
  	rcu_read_lock();
  	list_for_each_entry_rcu(new_stat, &hw_stats_list, list) {
5848cc096   Neil Horman   net: drop_monitor...
186
187
188
189
190
191
  		/*
  		 * 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   Neil Horman   dropmon: add abil...
192
  		if ((new_stat->dev == napi->dev)  &&
5848cc096   Neil Horman   net: drop_monitor...
193
  		    (time_after(jiffies, new_stat->last_rx + dm_hw_check_delta)) &&
4ea7e3869   Neil Horman   dropmon: add abil...
194
195
196
  		    (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   Neil Horman   net: drop_monitor...
197
  			new_stat->last_rx = jiffies;
4ea7e3869   Neil Horman   dropmon: add abil...
198
199
200
201
202
  			break;
  		}
  	}
  	rcu_read_unlock();
  }
9a8afc8d3   Neil Horman   Network Drop Moni...
203
204
205
  static int set_all_monitor_traces(int state)
  {
  	int rc = 0;
4ea7e3869   Neil Horman   dropmon: add abil...
206
207
208
209
  	struct dm_hw_stat_delta *new_stat = NULL;
  	struct dm_hw_stat_delta *temp;
  
  	spin_lock(&trace_state_lock);
9a8afc8d3   Neil Horman   Network Drop Moni...
210

4b706372f   Neil Horman   drop_monitor: Add...
211
212
213
214
  	if (state == trace_state) {
  		rc = -EAGAIN;
  		goto out_unlock;
  	}
9a8afc8d3   Neil Horman   Network Drop Moni...
215
216
  	switch (state) {
  	case TRACE_ON:
38516ab59   Steven Rostedt   tracing: Let trac...
217
218
  		rc |= register_trace_kfree_skb(trace_kfree_skb_hit, NULL);
  		rc |= register_trace_napi_poll(trace_napi_poll_hit, NULL);
9a8afc8d3   Neil Horman   Network Drop Moni...
219
220
  		break;
  	case TRACE_OFF:
38516ab59   Steven Rostedt   tracing: Let trac...
221
222
  		rc |= unregister_trace_kfree_skb(trace_kfree_skb_hit, NULL);
  		rc |= unregister_trace_napi_poll(trace_napi_poll_hit, NULL);
9a8afc8d3   Neil Horman   Network Drop Moni...
223
224
  
  		tracepoint_synchronize_unregister();
4ea7e3869   Neil Horman   dropmon: add abil...
225
226
227
228
229
230
231
  
  		/*
  		 * 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   Lai Jiangshan   net,rcu: convert ...
232
  				kfree_rcu(new_stat, rcu);
4ea7e3869   Neil Horman   dropmon: add abil...
233
234
  			}
  		}
9a8afc8d3   Neil Horman   Network Drop Moni...
235
236
237
238
239
  		break;
  	default:
  		rc = 1;
  		break;
  	}
4ea7e3869   Neil Horman   dropmon: add abil...
240
241
  	if (!rc)
  		trace_state = state;
4b706372f   Neil Horman   drop_monitor: Add...
242
243
  	else
  		rc = -EINPROGRESS;
4ea7e3869   Neil Horman   dropmon: add abil...
244

4b706372f   Neil Horman   drop_monitor: Add...
245
  out_unlock:
4ea7e3869   Neil Horman   dropmon: add abil...
246
  	spin_unlock(&trace_state_lock);
9a8afc8d3   Neil Horman   Network Drop Moni...
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
  	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);
  		break;
  	case NET_DM_CMD_STOP:
  		return set_all_monitor_traces(TRACE_OFF);
  		break;
  	}
  
  	return -ENOTSUPP;
  }
4ea7e3869   Neil Horman   dropmon: add abil...
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
  static int dropmon_net_event(struct notifier_block *ev_block,
  			unsigned long event, void *ptr)
  {
  	struct net_device *dev = ptr;
  	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   Neil Horman   net: drop_monitor...
286
  		new_stat->last_rx = jiffies;
4ea7e3869   Neil Horman   dropmon: add abil...
287
288
289
290
291
292
293
294
295
296
297
  		spin_lock(&trace_state_lock);
  		list_add_rcu(&new_stat->list, &hw_stats_list);
  		spin_unlock(&trace_state_lock);
  		break;
  	case NETDEV_UNREGISTER:
  		spin_lock(&trace_state_lock);
  		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   Lai Jiangshan   net,rcu: convert ...
298
  					kfree_rcu(new_stat, rcu);
4ea7e3869   Neil Horman   dropmon: add abil...
299
300
301
302
303
304
305
306
307
308
  					break;
  				}
  			}
  		}
  		spin_unlock(&trace_state_lock);
  		break;
  	}
  out:
  	return NOTIFY_DONE;
  }
9a8afc8d3   Neil Horman   Network Drop Moni...
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
  
  static struct genl_ops dropmon_ops[] = {
  	{
  		.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   Neil Horman   dropmon: add abil...
324
325
326
  static struct notifier_block dropmon_net_notifier = {
  	.notifier_call = dropmon_net_event
  };
9a8afc8d3   Neil Horman   Network Drop Moni...
327
328
  static int __init init_net_drop_monitor(void)
  {
9a8afc8d3   Neil Horman   Network Drop Moni...
329
  	struct per_cpu_dm_data *data;
a256be70c   Changli Gao   drop_monitor: use...
330
  	int cpu, rc;
ac0a121d7   Neil Horman   net: fix incorrec...
331
332
  	printk(KERN_INFO "Initializing network drop monitor service
  ");
9a8afc8d3   Neil Horman   Network Drop Moni...
333
334
335
336
337
338
  
  	if (sizeof(void *) > 8) {
  		printk(KERN_ERR "Unable to store program counters on this arch, Drop monitor failed
  ");
  		return -ENOSPC;
  	}
a256be70c   Changli Gao   drop_monitor: use...
339
340
341
342
  	rc = genl_register_family_with_ops(&net_drop_monitor_family,
  					   dropmon_ops,
  					   ARRAY_SIZE(dropmon_ops));
  	if (rc) {
9a8afc8d3   Neil Horman   Network Drop Moni...
343
344
  		printk(KERN_ERR "Could not create drop monitor netlink family
  ");
a256be70c   Changli Gao   drop_monitor: use...
345
  		return rc;
9a8afc8d3   Neil Horman   Network Drop Moni...
346
  	}
4ea7e3869   Neil Horman   dropmon: add abil...
347
348
349
350
351
352
  	rc = register_netdevice_notifier(&dropmon_net_notifier);
  	if (rc < 0) {
  		printk(KERN_CRIT "Failed to register netdevice notifier
  ");
  		goto out_unreg;
  	}
9a8afc8d3   Neil Horman   Network Drop Moni...
353
354
355
356
357
358
359
360
361
362
  	rc = 0;
  
  	for_each_present_cpu(cpu) {
  		data = &per_cpu(dm_cpu_data, cpu);
  		reset_per_cpu_data(data);
  		INIT_WORK(&data->dm_alert_work, send_dm_alert);
  		init_timer(&data->send_timer);
  		data->send_timer.data = cpu;
  		data->send_timer.function = sched_send_work;
  	}
4ea7e3869   Neil Horman   dropmon: add abil...
363

9a8afc8d3   Neil Horman   Network Drop Moni...
364
365
366
367
368
369
370
371
372
  	goto out;
  
  out_unreg:
  	genl_unregister_family(&net_drop_monitor_family);
  out:
  	return rc;
  }
  
  late_initcall(init_net_drop_monitor);