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

Merge branch 'reduce-open-coded-skb-next-access-for-gso-segment-walking'

Jason A. Donenfeld says:

====================
reduce open coded skb->next access for gso segment walking

This patchset introduces the skb_list_walk_safe helper macro, in order
to add some sanity to the myrid ways drivers have of walking through gso
segments. The goal is to reduce future bugs commonly caused by open
coding these sorts of things, and to in the future make it easier to
swap out the underlying list representation.

This first patch series addresses the easy uses of drivers iterating
over the returned list of skb_gso_segments, for drivers that live in
drivers/net/*. There are still other use cases to tackle later for
net/*, and after these low-hanging fruits are taken care of, I imagine
there are more subtle cases of gso segment walking that isn't just a
direct return value from skb_gso_segments, and eventually this will have
to be tackled. This series is the first in that direction.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>

+31 -53
+5 -7
drivers/net/ethernet/broadcom/tg3.c
··· 7874 7874 static int tg3_tso_bug(struct tg3 *tp, struct tg3_napi *tnapi, 7875 7875 struct netdev_queue *txq, struct sk_buff *skb) 7876 7876 { 7877 - struct sk_buff *segs, *nskb; 7878 7877 u32 frag_cnt_est = skb_shinfo(skb)->gso_segs * 3; 7878 + struct sk_buff *segs, *seg, *next; 7879 7879 7880 7880 /* Estimate the number of fragments in the worst case */ 7881 7881 if (unlikely(tg3_tx_avail(tnapi) <= frag_cnt_est)) { ··· 7898 7898 if (IS_ERR(segs) || !segs) 7899 7899 goto tg3_tso_bug_end; 7900 7900 7901 - do { 7902 - nskb = segs; 7903 - segs = segs->next; 7904 - nskb->next = NULL; 7905 - tg3_start_xmit(nskb, tp->dev); 7906 - } while (segs); 7901 + skb_list_walk_safe(segs, seg, next) { 7902 + skb_mark_not_on_list(seg); 7903 + tg3_start_xmit(seg, tp->dev); 7904 + } 7907 7905 7908 7906 tg3_tso_bug_end: 7909 7907 dev_consume_skb_any(skb);
+3 -5
drivers/net/ethernet/myricom/myri10ge/myri10ge.c
··· 2892 2892 static netdev_tx_t myri10ge_sw_tso(struct sk_buff *skb, 2893 2893 struct net_device *dev) 2894 2894 { 2895 - struct sk_buff *segs, *curr; 2895 + struct sk_buff *segs, *curr, *next; 2896 2896 struct myri10ge_priv *mgp = netdev_priv(dev); 2897 2897 struct myri10ge_slice_state *ss; 2898 2898 netdev_tx_t status; ··· 2901 2901 if (IS_ERR(segs)) 2902 2902 goto drop; 2903 2903 2904 - while (segs) { 2905 - curr = segs; 2906 - segs = segs->next; 2907 - curr->next = NULL; 2904 + skb_list_walk_safe(segs, curr, next) { 2905 + skb_mark_not_on_list(curr); 2908 2906 status = myri10ge_xmit(curr, dev); 2909 2907 if (status != 0) { 2910 2908 dev_kfree_skb_any(curr);
+2 -5
drivers/net/ethernet/sfc/tx.c
··· 307 307 dev_consume_skb_any(skb); 308 308 skb = segments; 309 309 310 - while (skb) { 311 - next = skb->next; 312 - skb->next = NULL; 313 - 310 + skb_list_walk_safe(skb, skb, next) { 311 + skb_mark_not_on_list(skb); 314 312 efx_enqueue_skb(tx_queue, skb); 315 - skb = next; 316 313 } 317 314 318 315 return 0;
+3 -6
drivers/net/ethernet/sun/sunvnet_common.c
··· 1223 1223 { 1224 1224 struct net_device *dev = VNET_PORT_TO_NET_DEVICE(port); 1225 1225 struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_TX_RING]; 1226 - struct sk_buff *segs; 1226 + struct sk_buff *segs, *curr, *next; 1227 1227 int maclen, datalen; 1228 1228 int status; 1229 1229 int gso_size, gso_type, gso_segs; ··· 1282 1282 skb_reset_mac_header(skb); 1283 1283 1284 1284 status = 0; 1285 - while (segs) { 1286 - struct sk_buff *curr = segs; 1287 - 1288 - segs = segs->next; 1289 - curr->next = NULL; 1285 + skb_list_walk_safe(segs, curr, next) { 1286 + skb_mark_not_on_list(curr); 1290 1287 if (port->tso && curr->len > dev->mtu) { 1291 1288 skb_shinfo(curr)->gso_size = gso_size; 1292 1289 skb_shinfo(curr)->gso_type = gso_type;
+6 -8
drivers/net/tap.c
··· 341 341 features |= tap->tap_features; 342 342 if (netif_needs_gso(skb, features)) { 343 343 struct sk_buff *segs = __skb_gso_segment(skb, features, false); 344 + struct sk_buff *next; 344 345 345 346 if (IS_ERR(segs)) 346 347 goto drop; ··· 353 352 } 354 353 355 354 consume_skb(skb); 356 - while (segs) { 357 - struct sk_buff *nskb = segs->next; 358 - 359 - segs->next = NULL; 360 - if (ptr_ring_produce(&q->ring, segs)) { 361 - kfree_skb(segs); 362 - kfree_skb_list(nskb); 355 + skb_list_walk_safe(segs, skb, next) { 356 + skb_mark_not_on_list(skb); 357 + if (ptr_ring_produce(&q->ring, skb)) { 358 + kfree_skb(skb); 359 + kfree_skb_list(next); 363 360 break; 364 361 } 365 - segs = nskb; 366 362 } 367 363 } else { 368 364 /* If we receive a partial checksum and the tap side
+5 -7
drivers/net/usb/r8152.c
··· 1897 1897 { 1898 1898 if (skb_shinfo(skb)->gso_size) { 1899 1899 netdev_features_t features = tp->netdev->features; 1900 + struct sk_buff *segs, *seg, *next; 1900 1901 struct sk_buff_head seg_list; 1901 - struct sk_buff *segs, *nskb; 1902 1902 1903 1903 features &= ~(NETIF_F_SG | NETIF_F_IPV6_CSUM | NETIF_F_TSO6); 1904 1904 segs = skb_gso_segment(skb, features); ··· 1907 1907 1908 1908 __skb_queue_head_init(&seg_list); 1909 1909 1910 - do { 1911 - nskb = segs; 1912 - segs = segs->next; 1913 - nskb->next = NULL; 1914 - __skb_queue_tail(&seg_list, nskb); 1915 - } while (segs); 1910 + skb_list_walk_safe(segs, seg, next) { 1911 + skb_mark_not_on_list(seg); 1912 + __skb_queue_tail(&seg_list, seg); 1913 + } 1916 1914 1917 1915 skb_queue_splice(&seg_list, list); 1918 1916 dev_kfree_skb(skb);
-8
drivers/net/wireguard/device.h
··· 62 62 int wg_device_init(void); 63 63 void wg_device_uninit(void); 64 64 65 - /* Later after the dust settles, this can be moved into include/linux/skbuff.h, 66 - * where virtually all code that deals with GSO segs can benefit, around ~30 67 - * drivers as of writing. 68 - */ 69 - #define skb_list_walk_safe(first, skb, next) \ 70 - for (skb = first, next = skb->next; skb; \ 71 - skb = next, next = skb ? skb->next : NULL) 72 - 73 65 #endif /* _WG_DEVICE_H */
+2 -7
drivers/net/wireless/intel/iwlwifi/mvm/tx.c
··· 847 847 else if (next) 848 848 consume_skb(skb); 849 849 850 - while (next) { 851 - tmp = next; 852 - next = tmp->next; 853 - 850 + skb_list_walk_safe(next, tmp, next) { 854 851 memcpy(tmp->cb, cb, sizeof(tmp->cb)); 855 852 /* 856 853 * Compute the length of all the data added for the A-MSDU. ··· 877 880 skb_shinfo(tmp)->gso_size = 0; 878 881 } 879 882 880 - tmp->prev = NULL; 881 - tmp->next = NULL; 882 - 883 + skb_mark_not_on_list(tmp); 883 884 __skb_queue_tail(mpdus_skb, tmp); 884 885 i++; 885 886 }
+5
include/linux/skbuff.h
··· 1478 1478 skb->next = NULL; 1479 1479 } 1480 1480 1481 + /* Iterate through singly-linked GSO fragments of an skb. */ 1482 + #define skb_list_walk_safe(first, skb, next) \ 1483 + for ((skb) = (first), (next) = (skb) ? (skb)->next : NULL; (skb); \ 1484 + (skb) = (next), (next) = (skb) ? (skb)->next : NULL) 1485 + 1481 1486 static inline void skb_list_del_init(struct sk_buff *skb) 1482 1487 { 1483 1488 __list_del_entry(&skb->list);