summaryrefslogtreecommitdiffstats
path: root/net/switchdev/switchdev.c
diff options
context:
space:
mode:
authorJiri Pirko <jiri@mellanox.com>2015-10-14 13:40:48 -0400
committerDavid S. Miller <davem@davemloft.net>2015-10-15 09:09:46 -0400
commit793f40147e82cdedc80971fa7f5596d6ed1e555e (patch)
tree2bb1f4e5265077009ab346ea8d0f2e12c70d7ab6 /net/switchdev/switchdev.c
parentadc9048c607a08320e87befc940955d6ffe51fac (diff)
switchdev: introduce switchdev deferred ops infrastructure
Introduce infrastructure which will be used internally to defer ops. Note that the deferred ops are queued up and either are processed by scheduled work or explicitly by user calling deferred_process function. Signed-off-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
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 *