ip_rcv_finish_core(), if it does not drop, sets skb->dst by either early demux or route lookup. The last step, calling dst_input(skb), is left to the caller; in the listified case, we split to form sublists with a common dst, but then ip_sublist_rcv_finish() just calls dst_input(skb) in a loop. The next step in listification would thus be to add a list_input() method to struct dst_entry.
Early demux is an indirect call based on iph->protocol; this is another opportunity for listification which is not taken here (it would require slicing up ip_rcv_finish_core() to allow splitting on protocol changes). Signed-off-by: Edward Cree <ec...@solarflare.com> --- net/ipv4/ip_input.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 7a8af8ff3f07..63d4dfdb1766 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -307,7 +307,8 @@ static inline bool ip_rcv_options(struct sk_buff *skb) return true; } -static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb) +static int ip_rcv_finish_core(struct net *net, struct sock *sk, + struct sk_buff *skb) { const struct iphdr *iph = ip_hdr(skb); int (*edemux)(struct sk_buff *skb); @@ -393,7 +394,7 @@ static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb) goto drop; } - return dst_input(skb); + return NET_RX_SUCCESS; drop: kfree_skb(skb); @@ -405,6 +406,15 @@ static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb) goto drop; } +static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb) +{ + int ret = ip_rcv_finish_core(net, sk, skb); + + if (ret != NET_RX_DROP) + ret = dst_input(skb); + return ret; +} + /* * Main IP Receive routine. */ @@ -515,16 +525,54 @@ 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 sk_buff_head *list) +{ + struct sk_buff *skb; + + while ((skb = __skb_dequeue(list)) != NULL) + dst_input(skb); +} + +static void ip_list_rcv_finish(struct net *net, struct sock *sk, + struct sk_buff_head *list) +{ + struct dst_entry *curr_dst = NULL; + struct sk_buff_head sublist; + struct sk_buff *skb; + + __skb_queue_head_init(&sublist); + + while ((skb = __skb_dequeue(list)) != NULL) { + struct dst_entry *dst; + + if (ip_rcv_finish_core(net, sk, skb) == NET_RX_DROP) + continue; + + dst = skb_dst(skb); + if (skb_queue_empty(&sublist)) { + curr_dst = dst; + } else if (curr_dst != dst) { + /* dispatch old sublist */ + ip_sublist_rcv_finish(&sublist); + /* start new sublist */ + __skb_queue_head_init(&sublist); + curr_dst = dst; + } + /* add to current sublist */ + __skb_queue_tail(&sublist, skb); + } + /* dispatch final sublist */ + ip_sublist_rcv_finish(&sublist); +} + static void ip_sublist_rcv(struct sk_buff_head *list, struct net_device *dev, struct net *net) { struct sk_buff_head sublist; - struct sk_buff *skb; NF_HOOK_LIST(NFPROTO_IPV4, NF_INET_PRE_ROUTING, net, NULL, list, &sublist, dev, NULL, ip_rcv_finish); - while ((skb = __skb_dequeue(&sublist)) != NULL) - ip_rcv_finish(net, NULL, skb); + ip_list_rcv_finish(net, NULL, &sublist); } /* Receive a list of IP packets */