xref: /linux/net/core/netdev_work.c (revision 0e59523b440c90ed1899476f32ad4e949282f43e)
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