Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: tap: track dropped skb via kfree_skb_reason()

The TAP can be used as vhost-net backend. E.g., the tap_handle_frame() is
the interface to forward the skb from TAP to vhost-net/virtio-net.

However, there are many "goto drop" in the TAP driver. Therefore, the
kfree_skb_reason() is involved at each "goto drop" to help userspace
ftrace/ebpf to track the reason for the loss of packets.

The below reasons are introduced:

- SKB_DROP_REASON_SKB_CSUM
- SKB_DROP_REASON_SKB_GSO_SEG
- SKB_DROP_REASON_SKB_UCOPY_FAULT
- SKB_DROP_REASON_DEV_HDR
- SKB_DROP_REASON_FULL_RING

Cc: Joao Martins <joao.m.martins@oracle.com>
Cc: Joe Jin <joe.jin@oracle.com>
Signed-off-by: Dongli Zhang <dongli.zhang@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Dongli Zhang and committed by
David S. Miller
736f16de 925a2421

+43 -10
+25 -10
drivers/net/tap.c
··· 322 322 struct tap_dev *tap; 323 323 struct tap_queue *q; 324 324 netdev_features_t features = TAP_FEATURES; 325 + enum skb_drop_reason drop_reason; 325 326 326 327 tap = tap_dev_get_rcu(dev); 327 328 if (!tap) ··· 344 343 struct sk_buff *segs = __skb_gso_segment(skb, features, false); 345 344 struct sk_buff *next; 346 345 347 - if (IS_ERR(segs)) 346 + if (IS_ERR(segs)) { 347 + drop_reason = SKB_DROP_REASON_SKB_GSO_SEG; 348 348 goto drop; 349 + } 349 350 350 351 if (!segs) { 351 - if (ptr_ring_produce(&q->ring, skb)) 352 + if (ptr_ring_produce(&q->ring, skb)) { 353 + drop_reason = SKB_DROP_REASON_FULL_RING; 352 354 goto drop; 355 + } 353 356 goto wake_up; 354 357 } 355 358 ··· 361 356 skb_list_walk_safe(segs, skb, next) { 362 357 skb_mark_not_on_list(skb); 363 358 if (ptr_ring_produce(&q->ring, skb)) { 364 - kfree_skb(skb); 365 - kfree_skb_list(next); 359 + drop_reason = SKB_DROP_REASON_FULL_RING; 360 + kfree_skb_reason(skb, drop_reason); 361 + kfree_skb_list_reason(next, drop_reason); 366 362 break; 367 363 } 368 364 } ··· 375 369 */ 376 370 if (skb->ip_summed == CHECKSUM_PARTIAL && 377 371 !(features & NETIF_F_CSUM_MASK) && 378 - skb_checksum_help(skb)) 372 + skb_checksum_help(skb)) { 373 + drop_reason = SKB_DROP_REASON_SKB_CSUM; 379 374 goto drop; 380 - if (ptr_ring_produce(&q->ring, skb)) 375 + } 376 + if (ptr_ring_produce(&q->ring, skb)) { 377 + drop_reason = SKB_DROP_REASON_FULL_RING; 381 378 goto drop; 379 + } 382 380 } 383 381 384 382 wake_up: ··· 393 383 /* Count errors/drops only here, thus don't care about args. */ 394 384 if (tap->count_rx_dropped) 395 385 tap->count_rx_dropped(tap); 396 - kfree_skb(skb); 386 + kfree_skb_reason(skb, drop_reason); 397 387 return RX_HANDLER_CONSUMED; 398 388 } 399 389 EXPORT_SYMBOL_GPL(tap_handle_frame); ··· 642 632 int depth; 643 633 bool zerocopy = false; 644 634 size_t linear; 635 + enum skb_drop_reason drop_reason; 645 636 646 637 if (q->flags & IFF_VNET_HDR) { 647 638 vnet_hdr_len = READ_ONCE(q->vnet_hdr_sz); ··· 707 696 else 708 697 err = skb_copy_datagram_from_iter(skb, 0, from, len); 709 698 710 - if (err) 699 + if (err) { 700 + drop_reason = SKB_DROP_REASON_SKB_UCOPY_FAULT; 711 701 goto err_kfree; 702 + } 712 703 713 704 skb_set_network_header(skb, ETH_HLEN); 714 705 skb_reset_mac_header(skb); ··· 719 706 if (vnet_hdr_len) { 720 707 err = virtio_net_hdr_to_skb(skb, &vnet_hdr, 721 708 tap_is_little_endian(q)); 722 - if (err) 709 + if (err) { 710 + drop_reason = SKB_DROP_REASON_DEV_HDR; 723 711 goto err_kfree; 712 + } 724 713 } 725 714 726 715 skb_probe_transport_header(skb); ··· 753 738 return total_len; 754 739 755 740 err_kfree: 756 - kfree_skb(skb); 741 + kfree_skb_reason(skb, drop_reason); 757 742 758 743 err: 759 744 rcu_read_lock();
+13
include/linux/skbuff.h
··· 412 412 * this means that L3 protocol is 413 413 * not supported 414 414 */ 415 + SKB_DROP_REASON_SKB_CSUM, /* sk_buff checksum computation 416 + * error 417 + */ 418 + SKB_DROP_REASON_SKB_GSO_SEG, /* gso segmentation error */ 419 + SKB_DROP_REASON_SKB_UCOPY_FAULT, /* failed to copy data from 420 + * user space, e.g., via 421 + * zerocopy_sg_from_iter() 422 + * or skb_orphan_frags_rx() 423 + */ 424 + SKB_DROP_REASON_DEV_HDR, /* device driver specific 425 + * header/metadata is invalid 426 + */ 427 + SKB_DROP_REASON_FULL_RING, /* ring buffer is full */ 415 428 SKB_DROP_REASON_MAX, 416 429 }; 417 430
+5
include/trace/events/skb.h
··· 51 51 EM(SKB_DROP_REASON_XDP, XDP) \ 52 52 EM(SKB_DROP_REASON_TC_INGRESS, TC_INGRESS) \ 53 53 EM(SKB_DROP_REASON_PTYPE_ABSENT, PTYPE_ABSENT) \ 54 + EM(SKB_DROP_REASON_SKB_CSUM, SKB_CSUM) \ 55 + EM(SKB_DROP_REASON_SKB_GSO_SEG, SKB_GSO_SEG) \ 56 + EM(SKB_DROP_REASON_SKB_UCOPY_FAULT, SKB_UCOPY_FAULT) \ 57 + EM(SKB_DROP_REASON_DEV_HDR, DEV_HDR) \ 58 + EM(SKB_DROP_REASON_FULL_RING, FULL_RING) \ 54 59 EMe(SKB_DROP_REASON_MAX, MAX) 55 60 56 61 #undef EM