netdev_set_tc_queue() (and related helpers/drivers such as netdev_bind_sb_channel_queue(), netdev_reset_tc(), and netdev_unbind_sb_channel()) perform separate 16-bit writes to dev->tc_to_txq[tc].count and dev->tc_to_txq[tc].offset. Furthermore, memset() in netdev_reset_tc() and netdev_unbind_sb_channel() provides no guarantee of performing full 32-bit word stores. Concurrent lockless readers (e.g. skb_tx_hash(), netdev_txq_to_tc(), ixgbe_select_queue(), taprio, mqprio, FPE drivers) can observe torn values where offset and count belong to inconsistent configurations. Redefine struct netdev_tc_txq to embed count and offset inside a union with a u32 combined field, allowing atomic manipulation via READ_ONCE() and WRITE_ONCE(). Update all lockless readers and writers across the kernel to use READ_ONCE() and WRITE_ONCE() on the combined field.
Showing the changes to include (1 of 11 files). Show all
Unified · Split
| @@ -832,8 +832,13 @@ struct xps_dev_maps { · 831 unmodified lines | |||
| 832 | #define TC_BITMASK 15 | 832 | #define TC_BITMASK 15 |
| 833 | /* HW offloaded queuing disciplines txq count and offset maps */ | 833 | /* HW offloaded queuing disciplines txq count and offset maps */ |
| 834 | struct netdev_tc_txq { | 834 | struct netdev_tc_txq { |
| 835 | u16 count; | 835 | union { |
| 836 | u16 offset; | 836 | struct { |
| 837 | u16 count; | ||
| 838 | u16 offset; | ||
| 839 | }; | ||
| 840 | u32 combined; | ||
| 841 | }; | ||
| 837 | }; | 842 | }; |
| 838 | 843 | ||
| 839 | #if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) | 844 | #if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) |