History

drop_monitor: Add packet alert mode

So far drop monitor supported only one alert mode in which a summary of locations in which packets were recently dropped was sent to user space. This alert mode is sufficient in order to understand that packets were dropped, but lacks information to perform a more detailed analysis. Add a new alert mode in which the dropped packet itself is passed to user space along with metadata: The drop location (as program counter and resolved symbol), ingress netdevice and drop timestamp. More metadata can be added in the future. To avoid performing expensive operations in the context in which kfree_skb() is invoked (can be hard IRQ), the dropped skb is cloned and queued on per-CPU skb drop list. Then, in process context the netlink message is allocated, prepared and finally sent to user space. The per-CPU skb drop list is limited to 1000 skbs to prevent exhausting the system's memory. Subsequent patches will make this limit configurable and also add a counter that indicates how many skbs were tail dropped.

Ido Schimmel committed 7 years ago

Commit ca307071 parent 28315f72 files · +305 −2Browse files

Unified · Split

2 files
  1. include/uapi/linux/net_dropmon.h +27 −0
  2. net/core/drop_monitor.c +278 −2
include/uapi/linux/net_dropmon.h+27−0
@@ -53,6 +53,7 @@ enum { · 52 unmodified lines
53 NET_DM_CMD_CONFIG,53 NET_DM_CMD_CONFIG,
54 NET_DM_CMD_START,54 NET_DM_CMD_START,
55 NET_DM_CMD_STOP,55 NET_DM_CMD_STOP,
56 NET_DM_CMD_PACKET_ALERT,
56 _NET_DM_CMD_MAX,57 _NET_DM_CMD_MAX,
57};58};
58 59
@@ -63,12 +64,38 @@ enum { · 4 unmodified lines
63 */64 */
64#define NET_DM_GRP_ALERT 165#define NET_DM_GRP_ALERT 1
65 66
67enum net_dm_attr {
68 NET_DM_ATTR_UNSPEC,
69
70 NET_DM_ATTR_ALERT_MODE, /* u8 */
71 NET_DM_ATTR_PC, /* u64 */
72 NET_DM_ATTR_SYMBOL, /* string */
73 NET_DM_ATTR_IN_PORT, /* nested */
74 NET_DM_ATTR_TIMESTAMP, /* struct timespec */
75 NET_DM_ATTR_PROTO, /* u16 */
76 NET_DM_ATTR_PAYLOAD, /* binary */
77 NET_DM_ATTR_PAD,
78
79 __NET_DM_ATTR_MAX,
80 NET_DM_ATTR_MAX = __NET_DM_ATTR_MAX - 1
81};
82
66/**83/**
67 * enum net_dm_alert_mode - Alert mode.84 * enum net_dm_alert_mode - Alert mode.
68 * @NET_DM_ALERT_MODE_SUMMARY: A summary of recent drops is sent to user space.85 * @NET_DM_ALERT_MODE_SUMMARY: A summary of recent drops is sent to user space.
86 * @NET_DM_ALERT_MODE_PACKET: Each dropped packet is sent to user space along
87 * with metadata.
69 */88 */
70enum net_dm_alert_mode {89enum net_dm_alert_mode {
71 NET_DM_ALERT_MODE_SUMMARY,90 NET_DM_ALERT_MODE_SUMMARY,
91 NET_DM_ALERT_MODE_PACKET,
92};
93
94enum {
95 NET_DM_ATTR_PORT_NETDEV_IFINDEX, /* u32 */
96
97 __NET_DM_ATTR_PORT_MAX,
98 NET_DM_ATTR_PORT_MAX = __NET_DM_ATTR_PORT_MAX - 1
72};99};
73 100
74#endif101#endif
net/core/drop_monitor.c+278−2
@@ -54,6 +54,7 @@ static DEFINE_MUTEX(net_dm_mutex); · 53 unmodified lines
54struct per_cpu_dm_data {54struct per_cpu_dm_data {
55 spinlock_t lock; /* Protects 'skb' and 'send_timer' */55 spinlock_t lock; /* Protects 'skb' and 'send_timer' */
56 struct sk_buff *skb;56 struct sk_buff *skb;
57 struct sk_buff_head drop_queue;
57 struct work_struct dm_alert_work;58 struct work_struct dm_alert_work;
58 struct timer_list send_timer;59 struct timer_list send_timer;
59};60};
@@ -85,6 +86,14 @@ struct net_dm_alert_ops { · 25 unmodified lines
85 void (*work_item_func)(struct work_struct *work);86 void (*work_item_func)(struct work_struct *work);
86};87};
87 88
89struct net_dm_skb_cb {
90 void *pc;
91};
92
93#define NET_DM_SKB_CB(__skb) ((struct net_dm_skb_cb *)&((__skb)->cb[0]))
94
95#define NET_DM_QUEUE_LEN 1000
96
88static struct sk_buff *reset_per_cpu_data(struct per_cpu_dm_data *data)97static struct sk_buff *reset_per_cpu_data(struct per_cpu_dm_data *data)
89{98{
90 size_t al;99 size_t al;
@@ -257,8 +266,214 @@ static const struct net_dm_alert_ops net_dm_alert_summary_ops = { · 166 unmodified lines
257 .work_item_func = send_dm_alert,266 .work_item_func = send_dm_alert,
258};267};
259 268
269static void net_dm_packet_trace_kfree_skb_hit(void *ignore,
270 struct sk_buff *skb,
271 void *location)
272{
273 ktime_t tstamp = ktime_get_real();
274 struct per_cpu_dm_data *data;
275 struct sk_buff *nskb;
276 unsigned long flags;
277
278 nskb = skb_clone(skb, GFP_ATOMIC);
279 if (!nskb)
280 return;
281
282 NET_DM_SKB_CB(nskb)->pc = location;
283 /* Override the timestamp because we care about the time when the
284 * packet was dropped.
285 */
286 nskb->tstamp = tstamp;
287
288 data = this_cpu_ptr(&dm_cpu_data);
289
290 spin_lock_irqsave(&data->drop_queue.lock, flags);
291 if (skb_queue_len(&data->drop_queue) < NET_DM_QUEUE_LEN)
292 __skb_queue_tail(&data->drop_queue, nskb);
293 else
294 goto unlock_free;
295 spin_unlock_irqrestore(&data->drop_queue.lock, flags);
296
297 schedule_work(&data->dm_alert_work);
298
299 return;
300
301unlock_free:
302 spin_unlock_irqrestore(&data->drop_queue.lock, flags);
303 consume_skb(nskb);
304}
305
306static void net_dm_packet_trace_napi_poll_hit(void *ignore,
307 struct napi_struct *napi,
308 int work, int budget)
309{
310}
311
312static size_t net_dm_in_port_size(void)
313{
314 /* NET_DM_ATTR_IN_PORT nest */
315 return nla_total_size(0) +
316 /* NET_DM_ATTR_PORT_NETDEV_IFINDEX */
317 nla_total_size(sizeof(u32));
318}
319
320#define NET_DM_MAX_SYMBOL_LEN 40
321
322static size_t net_dm_packet_report_size(size_t payload_len)
323{
324 size_t size;
325
326 size = nlmsg_msg_size(GENL_HDRLEN + net_drop_monitor_family.hdrsize);
327
328 return NLMSG_ALIGN(size) +
329 /* NET_DM_ATTR_PC */
330 nla_total_size(sizeof(u64)) +
331 /* NET_DM_ATTR_SYMBOL */
332 nla_total_size(NET_DM_MAX_SYMBOL_LEN + 1) +
333 /* NET_DM_ATTR_IN_PORT */
334 net_dm_in_port_size() +
335 /* NET_DM_ATTR_TIMESTAMP */
336 nla_total_size(sizeof(struct timespec)) +
337 /* NET_DM_ATTR_PROTO */
338 nla_total_size(sizeof(u16)) +
339 /* NET_DM_ATTR_PAYLOAD */
340 nla_total_size(payload_len);
341}
342
343static int net_dm_packet_report_in_port_put(struct sk_buff *msg, int ifindex)
344{
345 struct nlattr *attr;
346
347 attr = nla_nest_start(msg, NET_DM_ATTR_IN_PORT);
348 if (!attr)
349 return -EMSGSIZE;
350
351 if (ifindex &&
352 nla_put_u32(msg, NET_DM_ATTR_PORT_NETDEV_IFINDEX, ifindex))
353 goto nla_put_failure;
354
355 nla_nest_end(msg, attr);
356
357 return 0;
358
359nla_put_failure:
360 nla_nest_cancel(msg, attr);
361 return -EMSGSIZE;
362}
363
364static int net_dm_packet_report_fill(struct sk_buff *msg, struct sk_buff *skb,
365 size_t payload_len)
366{
367 u64 pc = (u64)(uintptr_t) NET_DM_SKB_CB(skb)->pc;
368 char buf[NET_DM_MAX_SYMBOL_LEN];
369 struct nlattr *attr;
370 struct timespec ts;
371 void *hdr;
372 int rc;
373
374 hdr = genlmsg_put(msg, 0, 0, &net_drop_monitor_family, 0,
375 NET_DM_CMD_PACKET_ALERT);
376 if (!hdr)
377 return -EMSGSIZE;
378
379 if (nla_put_u64_64bit(msg, NET_DM_ATTR_PC, pc, NET_DM_ATTR_PAD))
380 goto nla_put_failure;
381
382 snprintf(buf, sizeof(buf), "%pS", NET_DM_SKB_CB(skb)->pc);
383 if (nla_put_string(msg, NET_DM_ATTR_SYMBOL, buf))
384 goto nla_put_failure;
385
386 rc = net_dm_packet_report_in_port_put(msg, skb->skb_iif);
387 if (rc)
388 goto nla_put_failure;
389
390 if (ktime_to_timespec_cond(skb->tstamp, &ts) &&
391 nla_put(msg, NET_DM_ATTR_TIMESTAMP, sizeof(ts), &ts))
392 goto nla_put_failure;
393
394 if (!payload_len)
395 goto out;
396
397 if (nla_put_u16(msg, NET_DM_ATTR_PROTO, be16_to_cpu(skb->protocol)))
398 goto nla_put_failure;
399
400 attr = skb_put(msg, nla_total_size(payload_len));
401 attr->nla_type = NET_DM_ATTR_PAYLOAD;
402 attr->nla_len = nla_attr_size(payload_len);
403 if (skb_copy_bits(skb, 0, nla_data(attr), payload_len))
404 goto nla_put_failure;
405
406out:
407 genlmsg_end(msg, hdr);
408
409 return 0;
410
411nla_put_failure:
412 genlmsg_cancel(msg, hdr);
413 return -EMSGSIZE;
414}
415
416#define NET_DM_MAX_PACKET_SIZE (0xffff - NLA_HDRLEN - NLA_ALIGNTO)
417
418static void net_dm_packet_report(struct sk_buff *skb)
419{
420 struct sk_buff *msg;
421 size_t payload_len;
422 int rc;
423
424 /* Make sure we start copying the packet from the MAC header */
425 if (skb->data > skb_mac_header(skb))
426 skb_push(skb, skb->data - skb_mac_header(skb));
427 else
428 skb_pull(skb, skb_mac_header(skb) - skb->data);
429
430 /* Ensure packet fits inside a single netlink attribute */
431 payload_len = min_t(size_t, skb->len, NET_DM_MAX_PACKET_SIZE);
432
433 msg = nlmsg_new(net_dm_packet_report_size(payload_len), GFP_KERNEL);
434 if (!msg)
435 goto out;
436
437 rc = net_dm_packet_report_fill(msg, skb, payload_len);
438 if (rc) {
439 nlmsg_free(msg);
440 goto out;
441 }
442
443 genlmsg_multicast(&net_drop_monitor_family, msg, 0, 0, GFP_KERNEL);
444
445out:
446 consume_skb(skb);
447}
448
449static void net_dm_packet_work(struct work_struct *work)
450{
451 struct per_cpu_dm_data *data;
452 struct sk_buff_head list;
453 struct sk_buff *skb;
454 unsigned long flags;
455
456 data = container_of(work, struct per_cpu_dm_data, dm_alert_work);
457
458 __skb_queue_head_init(&list);
459
460 spin_lock_irqsave(&data->drop_queue.lock, flags);
461 skb_queue_splice_tail_init(&data->drop_queue, &list);
462 spin_unlock_irqrestore(&data->drop_queue.lock, flags);
463
464 while ((skb = __skb_dequeue(&list)))
465 net_dm_packet_report(skb);
466}
467
468static const struct net_dm_alert_ops net_dm_alert_packet_ops = {
469 .kfree_skb_probe = net_dm_packet_trace_kfree_skb_hit,
470 .napi_poll_probe = net_dm_packet_trace_napi_poll_hit,
471 .work_item_func = net_dm_packet_work,
472};
473
260static const struct net_dm_alert_ops *net_dm_alert_ops_arr[] = {474static const struct net_dm_alert_ops *net_dm_alert_ops_arr[] = {
261 [NET_DM_ALERT_MODE_SUMMARY] = &net_dm_alert_summary_ops,475 [NET_DM_ALERT_MODE_SUMMARY] = &net_dm_alert_summary_ops,
476 [NET_DM_ALERT_MODE_PACKET] = &net_dm_alert_packet_ops,
262};477};
263 478
264static int net_dm_trace_on_set(struct netlink_ext_ack *extack)479static int net_dm_trace_on_set(struct netlink_ext_ack *extack)
@@ -326,9 +541,12 @@ static void net_dm_trace_off_set(void) · 61 unmodified lines
326 */541 */
327 for_each_possible_cpu(cpu) {542 for_each_possible_cpu(cpu) {
328 struct per_cpu_dm_data *data = &per_cpu(dm_cpu_data, cpu);543 struct per_cpu_dm_data *data = &per_cpu(dm_cpu_data, cpu);
544 struct sk_buff *skb;
329 545
330 del_timer_sync(&data->send_timer);546 del_timer_sync(&data->send_timer);
331 cancel_work_sync(&data->dm_alert_work);547 cancel_work_sync(&data->dm_alert_work);
548 while ((skb = __skb_dequeue(&data->drop_queue)))
549 consume_skb(skb);
332 }550 }
333 551
334 list_for_each_entry_safe(new_stat, temp, &hw_stats_list, list) {552 list_for_each_entry_safe(new_stat, temp, &hw_stats_list, list) {
@@ -370,12 +588,61 @@ static int set_all_monitor_traces(int state, struct netlink_ext_ack *extack) · 35 unmodified lines
370 return rc;588 return rc;
371}589}
372 590
591static int net_dm_alert_mode_get_from_info(struct genl_info *info,
592 enum net_dm_alert_mode *p_alert_mode)
593{
594 u8 val;
595
596 val = nla_get_u8(info->attrs[NET_DM_ATTR_ALERT_MODE]);
597
598 switch (val) {
599 case NET_DM_ALERT_MODE_SUMMARY: /* fall-through */
600 case NET_DM_ALERT_MODE_PACKET:
601 *p_alert_mode = val;
602 break;
603 default:
604 return -EINVAL;
605 }
606
607 return 0;
608}
609
610static int net_dm_alert_mode_set(struct genl_info *info)
611{
612 struct netlink_ext_ack *extack = info->extack;
613 enum net_dm_alert_mode alert_mode;
614 int rc;
615
616 if (!info->attrs[NET_DM_ATTR_ALERT_MODE])
617 return 0;
618
619 rc = net_dm_alert_mode_get_from_info(info, &alert_mode);
620 if (rc) {
621 NL_SET_ERR_MSG_MOD(extack, "Invalid alert mode");
622 return -EINVAL;
623 }
624
625 net_dm_alert_mode = alert_mode;
626
627 return 0;
628}
629
373static int net_dm_cmd_config(struct sk_buff *skb,630static int net_dm_cmd_config(struct sk_buff *skb,
374 struct genl_info *info)631 struct genl_info *info)
375{632{
376 NL_SET_ERR_MSG_MOD(info->extack, "Command not supported");633 struct netlink_ext_ack *extack = info->extack;
634 int rc;
377 635
378 return -EOPNOTSUPP;636 if (trace_state == TRACE_ON) {
637 NL_SET_ERR_MSG_MOD(extack, "Cannot configure drop monitor while tracing is on");
638 return -EBUSY;
639 }
640
641 rc = net_dm_alert_mode_set(info);
642 if (rc)
643 return rc;
644
645 return 0;
379}646}
380 647
381static int net_dm_cmd_trace(struct sk_buff *skb,648static int net_dm_cmd_trace(struct sk_buff *skb,
@@ -430,6 +697,11 @@ out: · 48 unmodified lines
430 return NOTIFY_DONE;697 return NOTIFY_DONE;
431}698}
432 699
700static const struct nla_policy net_dm_nl_policy[NET_DM_ATTR_MAX + 1] = {
701 [NET_DM_ATTR_UNSPEC] = { .strict_start_type = NET_DM_ATTR_UNSPEC + 1 },
702 [NET_DM_ATTR_ALERT_MODE] = { .type = NLA_U8 },
703};
704
433static const struct genl_ops dropmon_ops[] = {705static const struct genl_ops dropmon_ops[] = {
434 {706 {
435 .cmd = NET_DM_CMD_CONFIG,707 .cmd = NET_DM_CMD_CONFIG,
@@ -467,6 +739,8 @@ static struct genl_family net_drop_monitor_family __ro_after_init = { · 31 unmodified lines
467 .hdrsize = 0,739 .hdrsize = 0,
468 .name = "NET_DM",740 .name = "NET_DM",
469 .version = 2,741 .version = 2,
742 .maxattr = NET_DM_ATTR_MAX,
743 .policy = net_dm_nl_policy,
470 .pre_doit = net_dm_nl_pre_doit,744 .pre_doit = net_dm_nl_pre_doit,
471 .post_doit = net_dm_nl_post_doit,745 .post_doit = net_dm_nl_post_doit,
472 .module = THIS_MODULE,746 .module = THIS_MODULE,
@@ -510,6 +784,7 @@ static int __init init_net_drop_monitor(void) · 37 unmodified lines
510 for_each_possible_cpu(cpu) {784 for_each_possible_cpu(cpu) {
511 data = &per_cpu(dm_cpu_data, cpu);785 data = &per_cpu(dm_cpu_data, cpu);
512 spin_lock_init(&data->lock);786 spin_lock_init(&data->lock);
787 skb_queue_head_init(&data->drop_queue);
513 }788 }
514 789
515 goto out;790 goto out;
@@ -539,6 +814,7 @@ static void exit_net_drop_monitor(void) · 23 unmodified lines
539 * to this struct and can free the skb inside it814 * to this struct and can free the skb inside it
540 */815 */
541 kfree_skb(data->skb);816 kfree_skb(data->skb);
817 WARN_ON(!skb_queue_empty(&data->drop_queue));
542 }818 }
543 819
544 BUG_ON(genl_unregister_family(&net_drop_monitor_family));820 BUG_ON(genl_unregister_family(&net_drop_monitor_family));