aboutsummaryrefslogtreecommitdiffstats
path: root/net/switchdev/switchdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/switchdev/switchdev.c')
-rw-r--r--net/switchdev/switchdev.c80
1 files changed, 80 insertions, 0 deletions
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index b8aaf820ef65..5e64b591aff7 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -17,6 +17,7 @@
17#include <linux/netdevice.h> 17#include <linux/netdevice.h>
18#include <linux/if_bridge.h> 18#include <linux/if_bridge.h>
19#include <linux/list.h> 19#include <linux/list.h>
20#include <linux/workqueue.h>
20#include <net/ip_fib.h> 21#include <net/ip_fib.h>
21#include <net/switchdev.h> 22#include <net/switchdev.h>
22 23
@@ -92,6 +93,85 @@ static void switchdev_trans_items_warn_destroy(struct net_device *dev,
92 switchdev_trans_items_destroy(trans); 93 switchdev_trans_items_destroy(trans);
93} 94}
94 95
96static LIST_HEAD(deferred);
97static DEFINE_SPINLOCK(deferred_lock);
98
99typedef void switchdev_deferred_func_t(struct net_device *dev,
100 const void *data);
101
102struct switchdev_deferred_item {
103 struct list_head list;
104 struct net_device *dev;
105 switchdev_deferred_func_t *func;
106 unsigned long data[0];
107};
108
109static struct switchdev_deferred_item *switchdev_deferred_dequeue(void)
110{
111 struct switchdev_deferred_item *dfitem;
112
113 spin_lock_bh(&deferred_lock);
114 if (list_empty(&deferred)) {
115 dfitem = NULL;
116 goto unlock;
117 }
118 dfitem = list_first_entry(&deferred,
119 struct switchdev_deferred_item, list);
120 list_del(&dfitem->list);
121unlock:
122 spin_unlock_bh(&deferred_lock);
123 return dfitem;
124}
125
126/**
127 * switchdev_deferred_process - Process ops in deferred queue
128 *
129 * Called to flush the ops currently queued in deferred ops queue.
130 * rtnl_lock must be held.
131 */
132void switchdev_deferred_process(void)
133{
134 struct switchdev_deferred_item *dfitem;
135
136 ASSERT_RTNL();
137
138 while ((dfitem = switchdev_deferred_dequeue())) {
139 dfitem->func(dfitem->dev, dfitem->data);
140 dev_put(dfitem->dev);
141 kfree(dfitem);
142 }
143}
144EXPORT_SYMBOL_GPL(switchdev_deferred_process);
145
146static void switchdev_deferred_process_work(struct work_struct *work)
147{
148 rtnl_lock();
149 switchdev_deferred_process();
150 rtnl_unlock();
151}
152
153static DECLARE_WORK(deferred_process_work, switchdev_deferred_process_work);
154
155static int switchdev_deferred_enqueue(struct net_device *dev,
156 const void *data, size_t data_len,
157 switchdev_deferred_func_t *func)
158{
159 struct switchdev_deferred_item *dfitem;
160
161 dfitem = kmalloc(sizeof(*dfitem) + data_len, GFP_ATOMIC);
162 if (!dfitem)
163 return -ENOMEM;
164 dfitem->dev = dev;
165 dfitem->func = func;
166 memcpy(dfitem->data, data, data_len);
167 dev_hold(dev);
168 spin_lock_bh(&deferred_lock);
169 list_add_tail(&dfitem->list, &deferred);
170 spin_unlock_bh(&deferred_lock);
171 schedule_work(&deferred_process_work);
172 return 0;
173}
174
95/** 175/**
96 * switchdev_port_attr_get - Get port attribute 176 * switchdev_port_attr_get - Get port attribute
97 * 177 *