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>
This commit is contained in:
parent
925a24213b
commit
736f16de75
@ -322,6 +322,7 @@ rx_handler_result_t tap_handle_frame(struct sk_buff **pskb)
|
||||
struct tap_dev *tap;
|
||||
struct tap_queue *q;
|
||||
netdev_features_t features = TAP_FEATURES;
|
||||
enum skb_drop_reason drop_reason;
|
||||
|
||||
tap = tap_dev_get_rcu(dev);
|
||||
if (!tap)
|
||||
@ -343,12 +344,16 @@ rx_handler_result_t tap_handle_frame(struct sk_buff **pskb)
|
||||
struct sk_buff *segs = __skb_gso_segment(skb, features, false);
|
||||
struct sk_buff *next;
|
||||
|
||||
if (IS_ERR(segs))
|
||||
if (IS_ERR(segs)) {
|
||||
drop_reason = SKB_DROP_REASON_SKB_GSO_SEG;
|
||||
goto drop;
|
||||
}
|
||||
|
||||
if (!segs) {
|
||||
if (ptr_ring_produce(&q->ring, skb))
|
||||
if (ptr_ring_produce(&q->ring, skb)) {
|
||||
drop_reason = SKB_DROP_REASON_FULL_RING;
|
||||
goto drop;
|
||||
}
|
||||
goto wake_up;
|
||||
}
|
||||
|
||||
@ -356,8 +361,9 @@ rx_handler_result_t tap_handle_frame(struct sk_buff **pskb)
|
||||
skb_list_walk_safe(segs, skb, next) {
|
||||
skb_mark_not_on_list(skb);
|
||||
if (ptr_ring_produce(&q->ring, skb)) {
|
||||
kfree_skb(skb);
|
||||
kfree_skb_list(next);
|
||||
drop_reason = SKB_DROP_REASON_FULL_RING;
|
||||
kfree_skb_reason(skb, drop_reason);
|
||||
kfree_skb_list_reason(next, drop_reason);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -369,10 +375,14 @@ rx_handler_result_t tap_handle_frame(struct sk_buff **pskb)
|
||||
*/
|
||||
if (skb->ip_summed == CHECKSUM_PARTIAL &&
|
||||
!(features & NETIF_F_CSUM_MASK) &&
|
||||
skb_checksum_help(skb))
|
||||
skb_checksum_help(skb)) {
|
||||
drop_reason = SKB_DROP_REASON_SKB_CSUM;
|
||||
goto drop;
|
||||
if (ptr_ring_produce(&q->ring, skb))
|
||||
}
|
||||
if (ptr_ring_produce(&q->ring, skb)) {
|
||||
drop_reason = SKB_DROP_REASON_FULL_RING;
|
||||
goto drop;
|
||||
}
|
||||
}
|
||||
|
||||
wake_up:
|
||||
@ -383,7 +393,7 @@ drop:
|
||||
/* Count errors/drops only here, thus don't care about args. */
|
||||
if (tap->count_rx_dropped)
|
||||
tap->count_rx_dropped(tap);
|
||||
kfree_skb(skb);
|
||||
kfree_skb_reason(skb, drop_reason);
|
||||
return RX_HANDLER_CONSUMED;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tap_handle_frame);
|
||||
@ -632,6 +642,7 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control,
|
||||
int depth;
|
||||
bool zerocopy = false;
|
||||
size_t linear;
|
||||
enum skb_drop_reason drop_reason;
|
||||
|
||||
if (q->flags & IFF_VNET_HDR) {
|
||||
vnet_hdr_len = READ_ONCE(q->vnet_hdr_sz);
|
||||
@ -696,8 +707,10 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control,
|
||||
else
|
||||
err = skb_copy_datagram_from_iter(skb, 0, from, len);
|
||||
|
||||
if (err)
|
||||
if (err) {
|
||||
drop_reason = SKB_DROP_REASON_SKB_UCOPY_FAULT;
|
||||
goto err_kfree;
|
||||
}
|
||||
|
||||
skb_set_network_header(skb, ETH_HLEN);
|
||||
skb_reset_mac_header(skb);
|
||||
@ -706,8 +719,10 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control,
|
||||
if (vnet_hdr_len) {
|
||||
err = virtio_net_hdr_to_skb(skb, &vnet_hdr,
|
||||
tap_is_little_endian(q));
|
||||
if (err)
|
||||
if (err) {
|
||||
drop_reason = SKB_DROP_REASON_DEV_HDR;
|
||||
goto err_kfree;
|
||||
}
|
||||
}
|
||||
|
||||
skb_probe_transport_header(skb);
|
||||
@ -738,7 +753,7 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control,
|
||||
return total_len;
|
||||
|
||||
err_kfree:
|
||||
kfree_skb(skb);
|
||||
kfree_skb_reason(skb, drop_reason);
|
||||
|
||||
err:
|
||||
rcu_read_lock();
|
||||
|
@ -412,6 +412,19 @@ enum skb_drop_reason {
|
||||
* this means that L3 protocol is
|
||||
* not supported
|
||||
*/
|
||||
SKB_DROP_REASON_SKB_CSUM, /* sk_buff checksum computation
|
||||
* error
|
||||
*/
|
||||
SKB_DROP_REASON_SKB_GSO_SEG, /* gso segmentation error */
|
||||
SKB_DROP_REASON_SKB_UCOPY_FAULT, /* failed to copy data from
|
||||
* user space, e.g., via
|
||||
* zerocopy_sg_from_iter()
|
||||
* or skb_orphan_frags_rx()
|
||||
*/
|
||||
SKB_DROP_REASON_DEV_HDR, /* device driver specific
|
||||
* header/metadata is invalid
|
||||
*/
|
||||
SKB_DROP_REASON_FULL_RING, /* ring buffer is full */
|
||||
SKB_DROP_REASON_MAX,
|
||||
};
|
||||
|
||||
|
@ -51,6 +51,11 @@
|
||||
EM(SKB_DROP_REASON_XDP, XDP) \
|
||||
EM(SKB_DROP_REASON_TC_INGRESS, TC_INGRESS) \
|
||||
EM(SKB_DROP_REASON_PTYPE_ABSENT, PTYPE_ABSENT) \
|
||||
EM(SKB_DROP_REASON_SKB_CSUM, SKB_CSUM) \
|
||||
EM(SKB_DROP_REASON_SKB_GSO_SEG, SKB_GSO_SEG) \
|
||||
EM(SKB_DROP_REASON_SKB_UCOPY_FAULT, SKB_UCOPY_FAULT) \
|
||||
EM(SKB_DROP_REASON_DEV_HDR, DEV_HDR) \
|
||||
EM(SKB_DROP_REASON_FULL_RING, FULL_RING) \
|
||||
EMe(SKB_DROP_REASON_MAX, MAX)
|
||||
|
||||
#undef EM
|
||||
|
Loading…
Reference in New Issue
Block a user