New macro list_for_each_entry_dequeue loops over a list by popping entries from the head, allowing a more concise expression of the dequeue-enqueue model of list processing and avoiding the need for a 'next' pointer (as used in list_for_each_entry_safe).
Signed-off-by: Edward Cree <ec...@solarflare.com> --- include/linux/list.h | 15 +++++++++++++++ include/linux/netfilter.h | 6 ++---- net/core/dev.c | 6 ++---- net/ipv4/ip_input.c | 10 ++++------ net/ipv6/ip6_input.c | 10 ++++------ 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/include/linux/list.h b/include/linux/list.h index de04cc5ed536..150751d03441 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -645,6 +645,21 @@ static inline void list_splice_tail_init(struct list_head *list, #define list_safe_reset_next(pos, n, member) \ n = list_next_entry(pos, member) +/** + * list_for_each_entry_dequeue - iterate over list by removing entries + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate over list of given type, removing each entry from the list before + * running the loop body. The loop will run until the list is empty, so + * adding an entry back onto the list in the loop body will requeue it. + */ +#define list_for_each_entry_dequeue(pos, head, member) \ + while (!list_empty(head) && \ + (pos = list_first_entry(head, typeof(*pos), member)) && \ + (list_del(&pos->member), true)) + /* * Double linked lists with a single pointer list head. * Mostly useful for hash tables where the two pointer list head is diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 23b48de8c2e2..f9a037eb053d 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -293,15 +293,13 @@ NF_HOOK_LIST(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk, struct list_head *head, struct net_device *in, struct net_device *out, int (*okfn)(struct net *, struct sock *, struct sk_buff *)) { - struct sk_buff *skb, *next; struct list_head sublist; + struct sk_buff *skb; INIT_LIST_HEAD(&sublist); - list_for_each_entry_safe(skb, next, head, list) { - list_del(&skb->list); + list_for_each_entry_dequeue(skb, head, list) if (nf_hook(pf, hook, net, sk, skb, in, out, okfn) == 1) list_add_tail(&skb->list, &sublist); - } /* Put passed packets back on main list */ list_splice(&sublist, head); } diff --git a/net/core/dev.c b/net/core/dev.c index ce4583564e00..68055f012d13 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4985,9 +4985,8 @@ static void netif_receive_skb_list_internal(struct list_head *head) struct list_head sublist; INIT_LIST_HEAD(&sublist); - list_for_each_entry_safe(skb, next, head, list) { + list_for_each_entry_dequeue(skb, head, list) { net_timestamp_check(netdev_tstamp_prequeue, skb); - list_del(&skb->list); if (!skb_defer_rx_timestamp(skb)) list_add_tail(&skb->list, &sublist); } @@ -4996,9 +4995,8 @@ static void netif_receive_skb_list_internal(struct list_head *head) if (static_branch_unlikely(&generic_xdp_needed_key)) { preempt_disable(); rcu_read_lock(); - list_for_each_entry_safe(skb, next, head, list) { + list_for_each_entry_dequeue(skb, head, list) { xdp_prog = rcu_dereference(skb->dev->xdp_prog); - list_del(&skb->list); if (do_xdp_generic(xdp_prog, skb) == XDP_PASS) list_add_tail(&skb->list, &sublist); } diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 1a3b6f32b1c9..26285a24c067 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -538,14 +538,13 @@ static void 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; + struct sk_buff *skb; INIT_LIST_HEAD(&sublist); - list_for_each_entry_safe(skb, next, head, list) { + list_for_each_entry_dequeue(skb, head, list) { struct dst_entry *dst; - list_del(&skb->list); /* if ingress device is enslaved to an L3 master device pass the * skb to its handler for processing */ @@ -584,15 +583,14 @@ void ip_list_rcv(struct list_head *head, struct packet_type *pt, { struct net_device *curr_dev = NULL; struct net *curr_net = NULL; - struct sk_buff *skb, *next; struct list_head sublist; + struct sk_buff *skb; INIT_LIST_HEAD(&sublist); - list_for_each_entry_safe(skb, next, head, list) { + list_for_each_entry_dequeue(skb, head, list) { struct net_device *dev = skb->dev; struct net *net = dev_net(dev); - list_del(&skb->list); skb = ip_rcv_core(skb, net); if (skb == NULL) continue; diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 6242682be876..65344cef0edd 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -88,14 +88,13 @@ static void 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; + struct sk_buff *skb; INIT_LIST_HEAD(&sublist); - list_for_each_entry_safe(skb, next, head, list) { + list_for_each_entry_dequeue(skb, head, list) { struct dst_entry *dst; - list_del(&skb->list); /* if ingress device is enslaved to an L3 master device pass the * skb to its handler for processing */ @@ -287,15 +286,14 @@ void ipv6_list_rcv(struct list_head *head, struct packet_type *pt, { struct net_device *curr_dev = NULL; struct net *curr_net = NULL; - struct sk_buff *skb, *next; struct list_head sublist; + struct sk_buff *skb; INIT_LIST_HEAD(&sublist); - list_for_each_entry_safe(skb, next, head, list) { + list_for_each_entry_dequeue(skb, head, list) { struct net_device *dev = skb->dev; struct net *net = dev_net(dev); - list_del(&skb->list); skb = ip6_rcv_core(skb, dev, net); if (skb == NULL) continue;