'Good' packets are defined as skbs for which netif_receive_skb() would have returned %NET_RX_SUCCESS. Thus, drivers can use this number for adaptive interrupt moderation where they previously reacted to the return code from netif_receive_skb().
Signed-off-by: Edward Cree <ec...@solarflare.com> --- include/linux/netdevice.h | 4 +-- include/net/ip.h | 4 +-- include/net/ipv6.h | 4 +-- net/core/dev.c | 63 +++++++++++++++++++++++++++++------------------ net/ipv4/ip_input.c | 39 ++++++++++++++++++----------- net/ipv6/ip6_input.c | 37 +++++++++++++++++----------- 6 files changed, 92 insertions(+), 59 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 2cef1d0fb2b1..76b98386a5dd 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2357,7 +2357,7 @@ struct packet_type { struct net_device *, struct packet_type *, struct net_device *); - void (*list_func) (struct list_head *, + int (*list_func) (struct list_head *, struct packet_type *, struct net_device *); bool (*id_match)(struct packet_type *ptype, @@ -3587,7 +3587,7 @@ int netif_rx(struct sk_buff *skb); int netif_rx_ni(struct sk_buff *skb); int netif_receive_skb(struct sk_buff *skb); int netif_receive_skb_core(struct sk_buff *skb); -void netif_receive_skb_list(struct list_head *head); +int netif_receive_skb_list(struct list_head *head); gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb); int napi_gro_receive_list(struct napi_struct *napi, struct list_head *head); void napi_gro_flush(struct napi_struct *napi, bool flush_old); diff --git a/include/net/ip.h b/include/net/ip.h index 8866bfce6121..33ab464f7a09 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -152,8 +152,8 @@ int ip_build_and_send_pkt(struct sk_buff *skb, const struct sock *sk, struct ip_options_rcu *opt); int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev); -void ip_list_rcv(struct list_head *head, struct packet_type *pt, - struct net_device *orig_dev); +int ip_list_rcv(struct list_head *head, struct packet_type *pt, + struct net_device *orig_dev); int ip_local_deliver(struct sk_buff *skb); void ip_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int proto); int ip_mr_input(struct sk_buff *skb); diff --git a/include/net/ipv6.h b/include/net/ipv6.h index daf80863d3a5..e25920829a94 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -914,8 +914,8 @@ static inline __be32 flowi6_get_flowlabel(const struct flowi6 *fl6) int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev); -void ipv6_list_rcv(struct list_head *head, struct packet_type *pt, - struct net_device *orig_dev); +int ipv6_list_rcv(struct list_head *head, struct packet_type *pt, + struct net_device *orig_dev); int ip6_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb); diff --git a/net/core/dev.c b/net/core/dev.c index 8f0fb56170b3..35427167f6fb 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4969,24 +4969,27 @@ int netif_receive_skb_core(struct sk_buff *skb) } EXPORT_SYMBOL(netif_receive_skb_core); -static inline void __netif_receive_skb_list_ptype(struct list_head *head, - struct packet_type *pt_prev, - struct net_device *orig_dev) +static inline int __netif_receive_skb_list_ptype(struct list_head *head, + struct packet_type *pt_prev, + struct net_device *orig_dev) { struct sk_buff *skb, *next; + int kept = 0; if (!pt_prev) - return; + return 0; if (list_empty(head)) - return; + return 0; if (pt_prev->list_func != NULL) - pt_prev->list_func(head, pt_prev, orig_dev); + kept = pt_prev->list_func(head, pt_prev, orig_dev); else list_for_each_entry_safe(skb, next, head, list) - pt_prev->func(skb, skb->dev, pt_prev, orig_dev); + if (pt_prev->func(skb, skb->dev, pt_prev, orig_dev) == NET_RX_SUCCESS) + kept++; + return kept; } -static void __netif_receive_skb_list_core(struct list_head *head, bool pfmemalloc) +static int __netif_receive_skb_list_core(struct list_head *head, bool pfmemalloc) { /* Fast-path assumptions: * - There is no RX handler. @@ -5003,6 +5006,7 @@ static void __netif_receive_skb_list_core(struct list_head *head, bool pfmemallo struct net_device *od_curr = NULL; struct list_head sublist; struct sk_buff *skb, *next; + int kept = 0, ret; INIT_LIST_HEAD(&sublist); list_for_each_entry_safe(skb, next, head, list) { @@ -5010,12 +5014,15 @@ static void __netif_receive_skb_list_core(struct list_head *head, bool pfmemallo struct packet_type *pt_prev = NULL; list_del(&skb->list); - __netif_receive_skb_core(skb, pfmemalloc, &pt_prev); - if (!pt_prev) + ret = __netif_receive_skb_core(skb, pfmemalloc, &pt_prev); + if (!pt_prev) { + if (ret == NET_RX_SUCCESS) + kept++; continue; + } if (pt_curr != pt_prev || od_curr != orig_dev) { /* dispatch old sublist */ - __netif_receive_skb_list_ptype(&sublist, pt_curr, od_curr); + kept += __netif_receive_skb_list_ptype(&sublist, pt_curr, od_curr); /* start new sublist */ INIT_LIST_HEAD(&sublist); pt_curr = pt_prev; @@ -5025,7 +5032,8 @@ static void __netif_receive_skb_list_core(struct list_head *head, bool pfmemallo } /* dispatch final sublist */ - __netif_receive_skb_list_ptype(&sublist, pt_curr, od_curr); + kept += __netif_receive_skb_list_ptype(&sublist, pt_curr, od_curr); + return kept; } static int __netif_receive_skb(struct sk_buff *skb) @@ -5053,11 +5061,12 @@ static int __netif_receive_skb(struct sk_buff *skb) return ret; } -static void __netif_receive_skb_list(struct list_head *head) +static int __netif_receive_skb_list(struct list_head *head) { unsigned long noreclaim_flag = 0; struct sk_buff *skb, *next; bool pfmemalloc = false; /* Is current sublist PF_MEMALLOC? */ + int kept = 0; list_for_each_entry_safe(skb, next, head, list) { if ((sk_memalloc_socks() && skb_pfmemalloc(skb)) != pfmemalloc) { @@ -5066,7 +5075,7 @@ static void __netif_receive_skb_list(struct list_head *head) /* Handle the previous sublist */ list_cut_before(&sublist, head, &skb->list); if (!list_empty(&sublist)) - __netif_receive_skb_list_core(&sublist, pfmemalloc); + kept += __netif_receive_skb_list_core(&sublist, pfmemalloc); pfmemalloc = !pfmemalloc; /* See comments in __netif_receive_skb */ if (pfmemalloc) @@ -5077,10 +5086,11 @@ static void __netif_receive_skb_list(struct list_head *head) } /* Handle the remaining sublist */ if (!list_empty(head)) - __netif_receive_skb_list_core(head, pfmemalloc); + kept += __netif_receive_skb_list_core(head, pfmemalloc); /* Restore pflags */ if (pfmemalloc) memalloc_noreclaim_restore(noreclaim_flag); + return kept; } static int generic_xdp_install(struct net_device *dev, struct netdev_bpf *xdp) @@ -5156,17 +5166,20 @@ static int netif_receive_skb_internal(struct sk_buff *skb) return ret; } -static void netif_receive_skb_list_internal(struct list_head *head) +static int netif_receive_skb_list_internal(struct list_head *head) { struct bpf_prog *xdp_prog = NULL; struct sk_buff *skb, *next; struct list_head sublist; + int kept = 0; INIT_LIST_HEAD(&sublist); list_for_each_entry_safe(skb, next, head, list) { net_timestamp_check(netdev_tstamp_prequeue, skb); list_del(&skb->list); - if (!skb_defer_rx_timestamp(skb)) + if (skb_defer_rx_timestamp(skb)) + kept++; + else list_add_tail(&skb->list, &sublist); } list_splice_init(&sublist, head); @@ -5196,13 +5209,15 @@ static void netif_receive_skb_list_internal(struct list_head *head) if (cpu >= 0) { /* Will be handled, remove from list */ list_del(&skb->list); - enqueue_to_backlog(skb, cpu, &rflow->last_qtail); + if (enqueue_to_backlog(skb, cpu, &rflow->last_qtail) == NET_RX_SUCCESS) + kept++; } } } #endif - __netif_receive_skb_list(head); + kept += __netif_receive_skb_list(head); rcu_read_unlock(); + return kept; } /** @@ -5232,21 +5247,21 @@ EXPORT_SYMBOL(netif_receive_skb); * netif_receive_skb_list - process many receive buffers from network * @head: list of skbs to process. * - * Since return value of netif_receive_skb() is normally ignored, and - * wouldn't be meaningful for a list, this function returns void. + * Returns the number of skbs for which netif_receive_skb() would have + * returned %NET_RX_SUCCESS. * * This function may only be called from softirq context and interrupts * should be enabled. */ -void netif_receive_skb_list(struct list_head *head) +int netif_receive_skb_list(struct list_head *head) { struct sk_buff *skb; if (list_empty(head)) - return; + return 0; list_for_each_entry(skb, head, list) trace_netif_receive_skb_list_entry(skb); - netif_receive_skb_list_internal(head); + return netif_receive_skb_list_internal(head); } EXPORT_SYMBOL(netif_receive_skb_list); diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 72250b4e466d..fa4eb82f9e55 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -525,22 +525,26 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, ip_rcv_finish); } -static void ip_sublist_rcv_finish(struct list_head *head) +static int ip_sublist_rcv_finish(struct list_head *head) { struct sk_buff *skb, *next; + int kept = 0; list_for_each_entry_safe(skb, next, head, list) { skb_list_del_init(skb); - dst_input(skb); + if (dst_input(skb) == NET_RX_SUCCESS) + kept++; } + return kept; } -static void ip_list_rcv_finish(struct net *net, struct sock *sk, - struct list_head *head) +static int ip_list_rcv_finish(struct net *net, struct sock *sk, + struct list_head *head) { struct dst_entry *curr_dst = NULL; struct sk_buff *skb, *next; struct list_head sublist; + int kept = 0; INIT_LIST_HEAD(&sublist); list_for_each_entry_safe(skb, next, head, list) { @@ -551,8 +555,10 @@ static void ip_list_rcv_finish(struct net *net, struct sock *sk, * skb to its handler for processing */ skb = l3mdev_ip_rcv(skb); - if (!skb) + if (!skb) { + kept++; continue; + } if (ip_rcv_finish_core(net, sk, skb) == NET_RX_DROP) continue; @@ -560,7 +566,7 @@ static void ip_list_rcv_finish(struct net *net, struct sock *sk, if (curr_dst != dst) { /* dispatch old sublist */ if (!list_empty(&sublist)) - ip_sublist_rcv_finish(&sublist); + kept += ip_sublist_rcv_finish(&sublist); /* start new sublist */ INIT_LIST_HEAD(&sublist); curr_dst = dst; @@ -568,25 +574,27 @@ static void ip_list_rcv_finish(struct net *net, struct sock *sk, list_add_tail(&skb->list, &sublist); } /* dispatch final sublist */ - ip_sublist_rcv_finish(&sublist); + kept += ip_sublist_rcv_finish(&sublist); + return kept; } -static void ip_sublist_rcv(struct list_head *head, struct net_device *dev, - struct net *net) +static int ip_sublist_rcv(struct list_head *head, struct net_device *dev, + struct net *net) { NF_HOOK_LIST(NFPROTO_IPV4, NF_INET_PRE_ROUTING, net, NULL, head, dev, NULL, ip_rcv_finish); - ip_list_rcv_finish(net, NULL, head); + return ip_list_rcv_finish(net, NULL, head); } -/* Receive a list of IP packets */ -void ip_list_rcv(struct list_head *head, struct packet_type *pt, - struct net_device *orig_dev) +/* Receive a list of IP packets; return number of successful receives */ +int ip_list_rcv(struct list_head *head, struct packet_type *pt, + struct net_device *orig_dev) { struct net_device *curr_dev = NULL; struct net *curr_net = NULL; struct sk_buff *skb, *next; struct list_head sublist; + int kept = 0; INIT_LIST_HEAD(&sublist); list_for_each_entry_safe(skb, next, head, list) { @@ -601,7 +609,7 @@ void ip_list_rcv(struct list_head *head, struct packet_type *pt, if (curr_dev != dev || curr_net != net) { /* dispatch old sublist */ if (!list_empty(&sublist)) - ip_sublist_rcv(&sublist, curr_dev, curr_net); + kept += ip_sublist_rcv(&sublist, curr_dev, curr_net); /* start new sublist */ INIT_LIST_HEAD(&sublist); curr_dev = dev; @@ -610,5 +618,6 @@ void ip_list_rcv(struct list_head *head, struct packet_type *pt, list_add_tail(&skb->list, &sublist); } /* dispatch final sublist */ - ip_sublist_rcv(&sublist, curr_dev, curr_net); + kept += ip_sublist_rcv(&sublist, curr_dev, curr_net); + return kept; } diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 3c06cc9e9b79..6e013672e1de 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -76,20 +76,24 @@ int ip6_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb) return dst_input(skb); } -static void ip6_sublist_rcv_finish(struct list_head *head) +static int ip6_sublist_rcv_finish(struct list_head *head) { struct sk_buff *skb, *next; + int kept = 0; list_for_each_entry_safe(skb, next, head, list) - dst_input(skb); + if (dst_input(skb) == NET_RX_SUCCESS) + kept++; + return kept; } -static void ip6_list_rcv_finish(struct net *net, struct sock *sk, - struct list_head *head) +static int ip6_list_rcv_finish(struct net *net, struct sock *sk, + struct list_head *head) { struct dst_entry *curr_dst = NULL; struct sk_buff *skb, *next; struct list_head sublist; + int kept = 0; INIT_LIST_HEAD(&sublist); list_for_each_entry_safe(skb, next, head, list) { @@ -100,14 +104,16 @@ static void ip6_list_rcv_finish(struct net *net, struct sock *sk, * skb to its handler for processing */ skb = l3mdev_ip6_rcv(skb); - if (!skb) + if (!skb) { + kept++; continue; + } ip6_rcv_finish_core(net, sk, skb); dst = skb_dst(skb); if (curr_dst != dst) { /* dispatch old sublist */ if (!list_empty(&sublist)) - ip6_sublist_rcv_finish(&sublist); + kept += ip6_sublist_rcv_finish(&sublist); /* start new sublist */ INIT_LIST_HEAD(&sublist); curr_dst = dst; @@ -115,7 +121,8 @@ static void ip6_list_rcv_finish(struct net *net, struct sock *sk, list_add_tail(&skb->list, &sublist); } /* dispatch final sublist */ - ip6_sublist_rcv_finish(&sublist); + kept += ip6_sublist_rcv_finish(&sublist); + return kept; } static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev, @@ -274,22 +281,23 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt ip6_rcv_finish); } -static void ip6_sublist_rcv(struct list_head *head, struct net_device *dev, - struct net *net) +static int ip6_sublist_rcv(struct list_head *head, struct net_device *dev, + struct net *net) { NF_HOOK_LIST(NFPROTO_IPV6, NF_INET_PRE_ROUTING, net, NULL, head, dev, NULL, ip6_rcv_finish); - ip6_list_rcv_finish(net, NULL, head); + return ip6_list_rcv_finish(net, NULL, head); } /* Receive a list of IPv6 packets */ -void ipv6_list_rcv(struct list_head *head, struct packet_type *pt, - struct net_device *orig_dev) +int ipv6_list_rcv(struct list_head *head, struct packet_type *pt, + struct net_device *orig_dev) { struct net_device *curr_dev = NULL; struct net *curr_net = NULL; struct sk_buff *skb, *next; struct list_head sublist; + int kept = 0; INIT_LIST_HEAD(&sublist); list_for_each_entry_safe(skb, next, head, list) { @@ -304,7 +312,7 @@ void ipv6_list_rcv(struct list_head *head, struct packet_type *pt, if (curr_dev != dev || curr_net != net) { /* dispatch old sublist */ if (!list_empty(&sublist)) - ip6_sublist_rcv(&sublist, curr_dev, curr_net); + kept += ip6_sublist_rcv(&sublist, curr_dev, curr_net); /* start new sublist */ INIT_LIST_HEAD(&sublist); curr_dev = dev; @@ -313,7 +321,8 @@ void ipv6_list_rcv(struct list_head *head, struct packet_type *pt, list_add_tail(&skb->list, &sublist); } /* dispatch final sublist */ - ip6_sublist_rcv(&sublist, curr_dev, curr_net); + kept += ip6_sublist_rcv(&sublist, curr_dev, curr_net); + return kept; } /*