1 // SPDX-License-Identifier: GPL-2.0-or-later 2 3 #include <linux/export.h> 4 #include <linux/list.h> 5 #include <linux/netdevice.h> 6 #include <linux/spinlock.h> 7 #include <linux/workqueue.h> 8 #include <net/netdev_lock.h> 9 10 #include "dev.h" 11 12 static void netdev_work_proc(struct work_struct *work); 13 14 /* @netdev_work_lock protects: 15 * - @netdev_work_list 16 * - within the list entries (struct net_device fields): 17 * - work_node 18 * - work_tracker 19 * - work_pending 20 * - work_core_pending 21 */ 22 static LIST_HEAD(netdev_work_list); 23 static DEFINE_SPINLOCK(netdev_work_lock); 24 static DECLARE_WORK(netdev_work, netdev_work_proc); 25 26 static void netdev_work_enqueue(struct net_device *dev, unsigned long events, 27 unsigned long core) 28 { 29 if (!events && !core) 30 return; 31 32 spin_lock_bh(&netdev_work_lock); 33 if (!netif_is_alive(dev)) { 34 spin_unlock_bh(&netdev_work_lock); 35 return; 36 } 37 if (list_empty(&dev->work_node)) { 38 list_add_tail(&dev->work_node, &netdev_work_list); 39 netdev_hold(dev, &dev->work_tracker, GFP_ATOMIC); 40 } 41 dev->work_pending |= events; 42 dev->work_core_pending |= core; 43 spin_unlock_bh(&netdev_work_lock); 44 45 schedule_work(&netdev_work); 46 } 47 48 static unsigned long 49 netdev_work_dequeue(struct net_device *dev, unsigned long *pending, 50 unsigned long mask) 51 { 52 unsigned long events; 53 54 spin_lock_bh(&netdev_work_lock); 55 events = *pending & mask; 56 *pending &= ~events; 57 if (!list_empty(&dev->work_node) && 58 !dev->work_pending && !dev->work_core_pending) { 59 list_del_init(&dev->work_node); 60 netdev_put(dev, &dev->work_tracker); 61 } 62 spin_unlock_bh(&netdev_work_lock); 63 64 return events; 65 } 66 67 void netdev_work_cancel_all(struct net_device *dev) 68 { 69 spin_lock_bh(&netdev_work_lock); 70 dev->work_pending = 0; 71 dev->work_core_pending = 0; 72 if (!list_empty(&dev->work_node)) { 73 list_del_init(&dev->work_node); 74 netdev_put(dev, &dev->work_tracker); 75 } 76 spin_unlock_bh(&netdev_work_lock); 77 } 78 79 void netdev_work_sched(struct net_device *dev, unsigned long events) 80 { 81 netdev_work_enqueue(dev, events, 0); 82 } 83 EXPORT_SYMBOL(netdev_work_sched); 84 85 /** 86 * netdev_work_cancel() - cancel selected work for a netdev 87 * @dev: net_device 88 * @mask: events to cancel 89 * 90 * Clear @mask from the device's work pending mask. If no work is left pending 91 * the device is dequeued and its ndo_work won't be called. 92 * 93 * No expectations on locking, but also no guarantees provided. If the caller 94 * wants to touch @dev afterwards (e.g. call the work that got canceled) 95 * they have to ensure @dev does not get freed. 96 * 97 * Returns: the subset of @mask that was actually pending, so the caller can run 98 * those events inline. 99 */ 100 unsigned long netdev_work_cancel(struct net_device *dev, unsigned long mask) 101 { 102 return netdev_work_dequeue(dev, &dev->work_pending, mask); 103 } 104 EXPORT_SYMBOL(netdev_work_cancel); 105 106 void __netdev_work_core_sched(struct net_device *dev, unsigned long events) 107 { 108 netdev_work_enqueue(dev, 0, events); 109 } 110 111 unsigned long 112 __netdev_work_core_cancel(struct net_device *dev, unsigned long mask) 113 { 114 return netdev_work_dequeue(dev, &dev->work_core_pending, mask); 115 } 116 117 static void netdev_work_run(struct net_device *dev, unsigned long events, 118 unsigned long core) 119 { 120 if (!netif_device_present(dev)) 121 return; 122 123 if (core & NETDEV_WORK_RX_MODE) 124 netif_rx_mode_run(dev); 125 if (events && dev->netdev_ops->ndo_work) 126 dev->netdev_ops->ndo_work(dev, events); 127 } 128 129 static void netdev_work_proc(struct work_struct *work) 130 { 131 while (true) { 132 unsigned long events = 0, core = 0; 133 netdevice_tracker tracker; 134 struct net_device *dev; 135 136 spin_lock_bh(&netdev_work_lock); 137 if (list_empty(&netdev_work_list)) { 138 spin_unlock_bh(&netdev_work_lock); 139 break; 140 } 141 dev = list_first_entry(&netdev_work_list, struct net_device, 142 work_node); 143 netdev_hold(dev, &tracker, GFP_ATOMIC); 144 spin_unlock_bh(&netdev_work_lock); 145 146 netdev_lock_ops_compat(dev); 147 spin_lock_bh(&netdev_work_lock); 148 if (!list_empty(&dev->work_node)) { 149 list_del_init(&dev->work_node); 150 core = dev->work_core_pending; 151 dev->work_core_pending = 0; 152 events = dev->work_pending; 153 dev->work_pending = 0; 154 /* We took another ref above */ 155 netdev_put(dev, &dev->work_tracker); 156 157 if (!netif_is_alive(dev)) 158 core = events = 0; 159 } 160 spin_unlock_bh(&netdev_work_lock); 161 162 netdev_work_run(dev, events, core); 163 netdev_unlock_ops_compat(dev); 164 165 netdev_put(dev, &tracker); 166 } 167 } 168