Blame view

net/switchdev/switchdev.c 28.4 KB
007f790c8   Jiri Pirko   net: introduce ge...
1
2
  /*
   * net/switchdev/switchdev.c - Switch device API
7ea6eb3f5   Jiri Pirko   switchdev: introd...
3
   * Copyright (c) 2014-2015 Jiri Pirko <jiri@resnulli.us>
f8f214715   Scott Feldman   switchdev: add ne...
4
   * Copyright (c) 2014-2015 Scott Feldman <sfeldma@gmail.com>
007f790c8   Jiri Pirko   net: introduce ge...
5
6
7
8
9
10
11
12
13
14
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   */
  
  #include <linux/kernel.h>
  #include <linux/types.h>
  #include <linux/init.h>
03bf0c281   Jiri Pirko   switchdev: introd...
15
16
  #include <linux/mutex.h>
  #include <linux/notifier.h>
007f790c8   Jiri Pirko   net: introduce ge...
17
  #include <linux/netdevice.h>
850d0cbc9   Jiri Pirko   switchdev: remove...
18
  #include <linux/etherdevice.h>
47f8328bb   Scott Feldman   switchdev: add ne...
19
  #include <linux/if_bridge.h>
7ea6eb3f5   Jiri Pirko   switchdev: introd...
20
  #include <linux/list.h>
793f40147   Jiri Pirko   switchdev: introd...
21
  #include <linux/workqueue.h>
87aaf2cae   Nikolay Aleksandrov   switchdev: check ...
22
  #include <linux/if_vlan.h>
4f2c6ae5c   Ido Schimmel   switchdev: Requir...
23
  #include <linux/rtnetlink.h>
007f790c8   Jiri Pirko   net: introduce ge...
24
25
26
  #include <net/switchdev.h>
  
  /**
7ea6eb3f5   Jiri Pirko   switchdev: introd...
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
   *	switchdev_trans_item_enqueue - Enqueue data item to transaction queue
   *
   *	@trans: transaction
   *	@data: pointer to data being queued
   *	@destructor: data destructor
   *	@tritem: transaction item being queued
   *
   *	Enqeueue data item to transaction queue. tritem is typically placed in
   *	cointainter pointed at by data pointer. Destructor is called on
   *	transaction abort and after successful commit phase in case
   *	the caller did not dequeue the item before.
   */
  void switchdev_trans_item_enqueue(struct switchdev_trans *trans,
  				  void *data, void (*destructor)(void const *),
  				  struct switchdev_trans_item *tritem)
  {
  	tritem->data = data;
  	tritem->destructor = destructor;
  	list_add_tail(&tritem->list, &trans->item_list);
  }
  EXPORT_SYMBOL_GPL(switchdev_trans_item_enqueue);
  
  static struct switchdev_trans_item *
  __switchdev_trans_item_dequeue(struct switchdev_trans *trans)
  {
  	struct switchdev_trans_item *tritem;
  
  	if (list_empty(&trans->item_list))
  		return NULL;
  	tritem = list_first_entry(&trans->item_list,
  				  struct switchdev_trans_item, list);
  	list_del(&tritem->list);
  	return tritem;
  }
  
  /**
   *	switchdev_trans_item_dequeue - Dequeue data item from transaction queue
   *
   *	@trans: transaction
   */
  void *switchdev_trans_item_dequeue(struct switchdev_trans *trans)
  {
  	struct switchdev_trans_item *tritem;
  
  	tritem = __switchdev_trans_item_dequeue(trans);
  	BUG_ON(!tritem);
  	return tritem->data;
  }
  EXPORT_SYMBOL_GPL(switchdev_trans_item_dequeue);
  
  static void switchdev_trans_init(struct switchdev_trans *trans)
  {
  	INIT_LIST_HEAD(&trans->item_list);
  }
  
  static void switchdev_trans_items_destroy(struct switchdev_trans *trans)
  {
  	struct switchdev_trans_item *tritem;
  
  	while ((tritem = __switchdev_trans_item_dequeue(trans)))
  		tritem->destructor(tritem->data);
  }
  
  static void switchdev_trans_items_warn_destroy(struct net_device *dev,
  					       struct switchdev_trans *trans)
  {
  	WARN(!list_empty(&trans->item_list), "%s: transaction item queue is not empty.
  ",
  	     dev->name);
  	switchdev_trans_items_destroy(trans);
  }
793f40147   Jiri Pirko   switchdev: introd...
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
  static LIST_HEAD(deferred);
  static DEFINE_SPINLOCK(deferred_lock);
  
  typedef void switchdev_deferred_func_t(struct net_device *dev,
  				       const void *data);
  
  struct switchdev_deferred_item {
  	struct list_head list;
  	struct net_device *dev;
  	switchdev_deferred_func_t *func;
  	unsigned long data[0];
  };
  
  static struct switchdev_deferred_item *switchdev_deferred_dequeue(void)
  {
  	struct switchdev_deferred_item *dfitem;
  
  	spin_lock_bh(&deferred_lock);
  	if (list_empty(&deferred)) {
  		dfitem = NULL;
  		goto unlock;
  	}
  	dfitem = list_first_entry(&deferred,
  				  struct switchdev_deferred_item, list);
  	list_del(&dfitem->list);
  unlock:
  	spin_unlock_bh(&deferred_lock);
  	return dfitem;
  }
  
  /**
   *	switchdev_deferred_process - Process ops in deferred queue
   *
   *	Called to flush the ops currently queued in deferred ops queue.
   *	rtnl_lock must be held.
   */
  void switchdev_deferred_process(void)
  {
  	struct switchdev_deferred_item *dfitem;
  
  	ASSERT_RTNL();
  
  	while ((dfitem = switchdev_deferred_dequeue())) {
  		dfitem->func(dfitem->dev, dfitem->data);
  		dev_put(dfitem->dev);
  		kfree(dfitem);
  	}
  }
  EXPORT_SYMBOL_GPL(switchdev_deferred_process);
  
  static void switchdev_deferred_process_work(struct work_struct *work)
  {
  	rtnl_lock();
  	switchdev_deferred_process();
  	rtnl_unlock();
  }
  
  static DECLARE_WORK(deferred_process_work, switchdev_deferred_process_work);
  
  static int switchdev_deferred_enqueue(struct net_device *dev,
  				      const void *data, size_t data_len,
  				      switchdev_deferred_func_t *func)
  {
  	struct switchdev_deferred_item *dfitem;
  
  	dfitem = kmalloc(sizeof(*dfitem) + data_len, GFP_ATOMIC);
  	if (!dfitem)
  		return -ENOMEM;
  	dfitem->dev = dev;
  	dfitem->func = func;
  	memcpy(dfitem->data, data, data_len);
  	dev_hold(dev);
  	spin_lock_bh(&deferred_lock);
  	list_add_tail(&dfitem->list, &deferred);
  	spin_unlock_bh(&deferred_lock);
  	schedule_work(&deferred_process_work);
  	return 0;
  }
7ea6eb3f5   Jiri Pirko   switchdev: introd...
176
  /**
3094333d9   Scott Feldman   switchdev: introd...
177
178
179
180
181
182
183
184
185
186
187
   *	switchdev_port_attr_get - Get port attribute
   *
   *	@dev: port device
   *	@attr: attribute to get
   */
  int switchdev_port_attr_get(struct net_device *dev, struct switchdev_attr *attr)
  {
  	const struct switchdev_ops *ops = dev->switchdev_ops;
  	struct net_device *lower_dev;
  	struct list_head *iter;
  	struct switchdev_attr first = {
1f8683987   Jiri Pirko   switchdev: rename...
188
  		.id = SWITCHDEV_ATTR_ID_UNDEFINED
3094333d9   Scott Feldman   switchdev: introd...
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
  	};
  	int err = -EOPNOTSUPP;
  
  	if (ops && ops->switchdev_port_attr_get)
  		return ops->switchdev_port_attr_get(dev, attr);
  
  	if (attr->flags & SWITCHDEV_F_NO_RECURSE)
  		return err;
  
  	/* Switch device port(s) may be stacked under
  	 * bond/team/vlan dev, so recurse down to get attr on
  	 * each port.  Return -ENODATA if attr values don't
  	 * compare across ports.
  	 */
  
  	netdev_for_each_lower_dev(dev, lower_dev, iter) {
  		err = switchdev_port_attr_get(lower_dev, attr);
  		if (err)
  			break;
1f8683987   Jiri Pirko   switchdev: rename...
208
  		if (first.id == SWITCHDEV_ATTR_ID_UNDEFINED)
3094333d9   Scott Feldman   switchdev: introd...
209
210
211
212
213
214
215
216
217
218
  			first = *attr;
  		else if (memcmp(&first, attr, sizeof(*attr)))
  			return -ENODATA;
  	}
  
  	return err;
  }
  EXPORT_SYMBOL_GPL(switchdev_port_attr_get);
  
  static int __switchdev_port_attr_set(struct net_device *dev,
f7fadf304   Jiri Pirko   switchdev: make s...
219
  				     const struct switchdev_attr *attr,
7ea6eb3f5   Jiri Pirko   switchdev: introd...
220
  				     struct switchdev_trans *trans)
3094333d9   Scott Feldman   switchdev: introd...
221
222
223
224
225
  {
  	const struct switchdev_ops *ops = dev->switchdev_ops;
  	struct net_device *lower_dev;
  	struct list_head *iter;
  	int err = -EOPNOTSUPP;
0c63d80c3   Jiri Pirko   switchdev: respec...
226
227
228
229
  	if (ops && ops->switchdev_port_attr_set) {
  		err = ops->switchdev_port_attr_set(dev, attr, trans);
  		goto done;
  	}
3094333d9   Scott Feldman   switchdev: introd...
230
231
  
  	if (attr->flags & SWITCHDEV_F_NO_RECURSE)
464314ea6   Scott Feldman   switchdev: skip o...
232
  		goto done;
3094333d9   Scott Feldman   switchdev: introd...
233
234
235
236
237
238
239
  
  	/* Switch device port(s) may be stacked under
  	 * bond/team/vlan dev, so recurse down to set attr on
  	 * each port.
  	 */
  
  	netdev_for_each_lower_dev(dev, lower_dev, iter) {
7ea6eb3f5   Jiri Pirko   switchdev: introd...
240
  		err = __switchdev_port_attr_set(lower_dev, attr, trans);
3094333d9   Scott Feldman   switchdev: introd...
241
242
243
  		if (err)
  			break;
  	}
464314ea6   Scott Feldman   switchdev: skip o...
244
245
246
  done:
  	if (err == -EOPNOTSUPP && attr->flags & SWITCHDEV_F_SKIP_EOPNOTSUPP)
  		err = 0;
3094333d9   Scott Feldman   switchdev: introd...
247
248
  	return err;
  }
0bc05d585   Jiri Pirko   switchdev: allow ...
249
250
  static int switchdev_port_attr_set_now(struct net_device *dev,
  				       const struct switchdev_attr *attr)
3094333d9   Scott Feldman   switchdev: introd...
251
  {
7ea6eb3f5   Jiri Pirko   switchdev: introd...
252
  	struct switchdev_trans trans;
3094333d9   Scott Feldman   switchdev: introd...
253
  	int err;
7ea6eb3f5   Jiri Pirko   switchdev: introd...
254
  	switchdev_trans_init(&trans);
3094333d9   Scott Feldman   switchdev: introd...
255
256
257
258
259
260
  	/* Phase I: prepare for attr set. Driver/device should fail
  	 * here if there are going to be issues in the commit phase,
  	 * such as lack of resources or support.  The driver/device
  	 * should reserve resources needed for the commit phase here,
  	 * but should not commit the attr.
  	 */
f623ab7f5   Jiri Pirko   switchdev: reduce...
261
  	trans.ph_prepare = true;
7ea6eb3f5   Jiri Pirko   switchdev: introd...
262
  	err = __switchdev_port_attr_set(dev, attr, &trans);
3094333d9   Scott Feldman   switchdev: introd...
263
264
265
266
267
  	if (err) {
  		/* Prepare phase failed: abort the transaction.  Any
  		 * resources reserved in the prepare phase are
  		 * released.
  		 */
9f6467cf2   Jiri Pirko   switchdev: remove...
268
  		if (err != -EOPNOTSUPP)
7ea6eb3f5   Jiri Pirko   switchdev: introd...
269
  			switchdev_trans_items_destroy(&trans);
3094333d9   Scott Feldman   switchdev: introd...
270
271
272
273
274
275
276
277
  
  		return err;
  	}
  
  	/* Phase II: commit attr set.  This cannot fail as a fault
  	 * of driver/device.  If it does, it's a bug in the driver/device
  	 * because the driver said everythings was OK in phase I.
  	 */
f623ab7f5   Jiri Pirko   switchdev: reduce...
278
  	trans.ph_prepare = false;
7ea6eb3f5   Jiri Pirko   switchdev: introd...
279
  	err = __switchdev_port_attr_set(dev, attr, &trans);
e9fdaec0e   Scott Feldman   switchdev: change...
280
281
282
  	WARN(err, "%s: Commit of attribute (id=%d) failed.
  ",
  	     dev->name, attr->id);
7ea6eb3f5   Jiri Pirko   switchdev: introd...
283
  	switchdev_trans_items_warn_destroy(dev, &trans);
3094333d9   Scott Feldman   switchdev: introd...
284
285
286
  
  	return err;
  }
0bc05d585   Jiri Pirko   switchdev: allow ...
287
288
289
290
291
292
293
294
295
296
297
298
  
  static void switchdev_port_attr_set_deferred(struct net_device *dev,
  					     const void *data)
  {
  	const struct switchdev_attr *attr = data;
  	int err;
  
  	err = switchdev_port_attr_set_now(dev, attr);
  	if (err && err != -EOPNOTSUPP)
  		netdev_err(dev, "failed (err=%d) to set attribute (id=%d)
  ",
  			   err, attr->id);
7ceb2afbd   Elad Raz   switchdev: Adding...
299
300
  	if (attr->complete)
  		attr->complete(dev, err, attr->complete_priv);
0bc05d585   Jiri Pirko   switchdev: allow ...
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
  }
  
  static int switchdev_port_attr_set_defer(struct net_device *dev,
  					 const struct switchdev_attr *attr)
  {
  	return switchdev_deferred_enqueue(dev, attr, sizeof(*attr),
  					  switchdev_port_attr_set_deferred);
  }
  
  /**
   *	switchdev_port_attr_set - Set port attribute
   *
   *	@dev: port device
   *	@attr: attribute to set
   *
   *	Use a 2-phase prepare-commit transaction model to ensure
   *	system is not left in a partially updated state due to
   *	failure from driver/device.
   *
   *	rtnl_lock must be held and must not be in atomic section,
   *	in case SWITCHDEV_F_DEFER flag is not set.
   */
  int switchdev_port_attr_set(struct net_device *dev,
  			    const struct switchdev_attr *attr)
  {
  	if (attr->flags & SWITCHDEV_F_DEFER)
  		return switchdev_port_attr_set_defer(dev, attr);
  	ASSERT_RTNL();
  	return switchdev_port_attr_set_now(dev, attr);
  }
3094333d9   Scott Feldman   switchdev: introd...
331
  EXPORT_SYMBOL_GPL(switchdev_port_attr_set);
e258d919b   Scott Feldman   switchdev: fix: p...
332
333
334
335
336
  static size_t switchdev_obj_size(const struct switchdev_obj *obj)
  {
  	switch (obj->id) {
  	case SWITCHDEV_OBJ_ID_PORT_VLAN:
  		return sizeof(struct switchdev_obj_port_vlan);
e258d919b   Scott Feldman   switchdev: fix: p...
337
338
  	case SWITCHDEV_OBJ_ID_PORT_FDB:
  		return sizeof(struct switchdev_obj_port_fdb);
4d41e1259   Elad Raz   switchdev: Adding...
339
340
  	case SWITCHDEV_OBJ_ID_PORT_MDB:
  		return sizeof(struct switchdev_obj_port_mdb);
e258d919b   Scott Feldman   switchdev: fix: p...
341
342
343
344
345
  	default:
  		BUG();
  	}
  	return 0;
  }
22c1f67ea   Scott Feldman   switchdev: sparse...
346
  static int __switchdev_port_obj_add(struct net_device *dev,
648b4a995   Jiri Pirko   switchdev: bring ...
347
  				    const struct switchdev_obj *obj,
7ea6eb3f5   Jiri Pirko   switchdev: introd...
348
  				    struct switchdev_trans *trans)
491d0f153   Scott Feldman   switchdev: introd...
349
350
351
352
353
354
355
  {
  	const struct switchdev_ops *ops = dev->switchdev_ops;
  	struct net_device *lower_dev;
  	struct list_head *iter;
  	int err = -EOPNOTSUPP;
  
  	if (ops && ops->switchdev_port_obj_add)
9e8f4a548   Jiri Pirko   switchdev: push o...
356
  		return ops->switchdev_port_obj_add(dev, obj, trans);
491d0f153   Scott Feldman   switchdev: introd...
357
358
359
360
361
362
363
  
  	/* Switch device port(s) may be stacked under
  	 * bond/team/vlan dev, so recurse down to add object on
  	 * each port.
  	 */
  
  	netdev_for_each_lower_dev(dev, lower_dev, iter) {
9e8f4a548   Jiri Pirko   switchdev: push o...
364
  		err = __switchdev_port_obj_add(lower_dev, obj, trans);
491d0f153   Scott Feldman   switchdev: introd...
365
366
367
368
369
370
  		if (err)
  			break;
  	}
  
  	return err;
  }
4d429c5dd   Jiri Pirko   switchdev: introd...
371
372
  static int switchdev_port_obj_add_now(struct net_device *dev,
  				      const struct switchdev_obj *obj)
491d0f153   Scott Feldman   switchdev: introd...
373
  {
7ea6eb3f5   Jiri Pirko   switchdev: introd...
374
  	struct switchdev_trans trans;
491d0f153   Scott Feldman   switchdev: introd...
375
376
377
  	int err;
  
  	ASSERT_RTNL();
7ea6eb3f5   Jiri Pirko   switchdev: introd...
378
  	switchdev_trans_init(&trans);
491d0f153   Scott Feldman   switchdev: introd...
379
380
381
382
383
384
  	/* Phase I: prepare for obj add. Driver/device should fail
  	 * here if there are going to be issues in the commit phase,
  	 * such as lack of resources or support.  The driver/device
  	 * should reserve resources needed for the commit phase here,
  	 * but should not commit the obj.
  	 */
f623ab7f5   Jiri Pirko   switchdev: reduce...
385
  	trans.ph_prepare = true;
9e8f4a548   Jiri Pirko   switchdev: push o...
386
  	err = __switchdev_port_obj_add(dev, obj, &trans);
491d0f153   Scott Feldman   switchdev: introd...
387
388
389
390
391
  	if (err) {
  		/* Prepare phase failed: abort the transaction.  Any
  		 * resources reserved in the prepare phase are
  		 * released.
  		 */
9f6467cf2   Jiri Pirko   switchdev: remove...
392
  		if (err != -EOPNOTSUPP)
7ea6eb3f5   Jiri Pirko   switchdev: introd...
393
  			switchdev_trans_items_destroy(&trans);
491d0f153   Scott Feldman   switchdev: introd...
394
395
396
397
398
399
400
401
  
  		return err;
  	}
  
  	/* Phase II: commit obj add.  This cannot fail as a fault
  	 * of driver/device.  If it does, it's a bug in the driver/device
  	 * because the driver said everythings was OK in phase I.
  	 */
f623ab7f5   Jiri Pirko   switchdev: reduce...
402
  	trans.ph_prepare = false;
9e8f4a548   Jiri Pirko   switchdev: push o...
403
404
405
  	err = __switchdev_port_obj_add(dev, obj, &trans);
  	WARN(err, "%s: Commit of object (id=%d) failed.
  ", dev->name, obj->id);
7ea6eb3f5   Jiri Pirko   switchdev: introd...
406
  	switchdev_trans_items_warn_destroy(dev, &trans);
491d0f153   Scott Feldman   switchdev: introd...
407
408
409
  
  	return err;
  }
4d429c5dd   Jiri Pirko   switchdev: introd...
410
411
412
413
414
415
416
417
418
419
420
421
  
  static void switchdev_port_obj_add_deferred(struct net_device *dev,
  					    const void *data)
  {
  	const struct switchdev_obj *obj = data;
  	int err;
  
  	err = switchdev_port_obj_add_now(dev, obj);
  	if (err && err != -EOPNOTSUPP)
  		netdev_err(dev, "failed (err=%d) to add object (id=%d)
  ",
  			   err, obj->id);
7ceb2afbd   Elad Raz   switchdev: Adding...
422
423
  	if (obj->complete)
  		obj->complete(dev, err, obj->complete_priv);
4d429c5dd   Jiri Pirko   switchdev: introd...
424
425
426
427
428
  }
  
  static int switchdev_port_obj_add_defer(struct net_device *dev,
  					const struct switchdev_obj *obj)
  {
e258d919b   Scott Feldman   switchdev: fix: p...
429
  	return switchdev_deferred_enqueue(dev, obj, switchdev_obj_size(obj),
4d429c5dd   Jiri Pirko   switchdev: introd...
430
431
  					  switchdev_port_obj_add_deferred);
  }
491d0f153   Scott Feldman   switchdev: introd...
432
433
  
  /**
4d429c5dd   Jiri Pirko   switchdev: introd...
434
   *	switchdev_port_obj_add - Add port object
491d0f153   Scott Feldman   switchdev: introd...
435
436
   *
   *	@dev: port device
ab0690023   Vivien Didelot   net: switchdev: a...
437
   *	@id: object ID
4d429c5dd   Jiri Pirko   switchdev: introd...
438
439
440
441
442
443
444
445
   *	@obj: object to add
   *
   *	Use a 2-phase prepare-commit transaction model to ensure
   *	system is not left in a partially updated state due to
   *	failure from driver/device.
   *
   *	rtnl_lock must be held and must not be in atomic section,
   *	in case SWITCHDEV_F_DEFER flag is not set.
491d0f153   Scott Feldman   switchdev: introd...
446
   */
4d429c5dd   Jiri Pirko   switchdev: introd...
447
  int switchdev_port_obj_add(struct net_device *dev,
648b4a995   Jiri Pirko   switchdev: bring ...
448
  			   const struct switchdev_obj *obj)
491d0f153   Scott Feldman   switchdev: introd...
449
  {
4d429c5dd   Jiri Pirko   switchdev: introd...
450
451
452
453
454
455
456
457
458
459
  	if (obj->flags & SWITCHDEV_F_DEFER)
  		return switchdev_port_obj_add_defer(dev, obj);
  	ASSERT_RTNL();
  	return switchdev_port_obj_add_now(dev, obj);
  }
  EXPORT_SYMBOL_GPL(switchdev_port_obj_add);
  
  static int switchdev_port_obj_del_now(struct net_device *dev,
  				      const struct switchdev_obj *obj)
  {
491d0f153   Scott Feldman   switchdev: introd...
460
461
462
463
464
465
  	const struct switchdev_ops *ops = dev->switchdev_ops;
  	struct net_device *lower_dev;
  	struct list_head *iter;
  	int err = -EOPNOTSUPP;
  
  	if (ops && ops->switchdev_port_obj_del)
9e8f4a548   Jiri Pirko   switchdev: push o...
466
  		return ops->switchdev_port_obj_del(dev, obj);
491d0f153   Scott Feldman   switchdev: introd...
467
468
469
470
471
472
473
  
  	/* Switch device port(s) may be stacked under
  	 * bond/team/vlan dev, so recurse down to delete object on
  	 * each port.
  	 */
  
  	netdev_for_each_lower_dev(dev, lower_dev, iter) {
4d429c5dd   Jiri Pirko   switchdev: introd...
474
  		err = switchdev_port_obj_del_now(lower_dev, obj);
491d0f153   Scott Feldman   switchdev: introd...
475
476
477
478
479
480
  		if (err)
  			break;
  	}
  
  	return err;
  }
4d429c5dd   Jiri Pirko   switchdev: introd...
481
482
483
484
485
486
487
488
489
490
491
492
  
  static void switchdev_port_obj_del_deferred(struct net_device *dev,
  					    const void *data)
  {
  	const struct switchdev_obj *obj = data;
  	int err;
  
  	err = switchdev_port_obj_del_now(dev, obj);
  	if (err && err != -EOPNOTSUPP)
  		netdev_err(dev, "failed (err=%d) to del object (id=%d)
  ",
  			   err, obj->id);
7ceb2afbd   Elad Raz   switchdev: Adding...
493
494
  	if (obj->complete)
  		obj->complete(dev, err, obj->complete_priv);
4d429c5dd   Jiri Pirko   switchdev: introd...
495
496
497
498
499
  }
  
  static int switchdev_port_obj_del_defer(struct net_device *dev,
  					const struct switchdev_obj *obj)
  {
e258d919b   Scott Feldman   switchdev: fix: p...
500
  	return switchdev_deferred_enqueue(dev, obj, switchdev_obj_size(obj),
4d429c5dd   Jiri Pirko   switchdev: introd...
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
  					  switchdev_port_obj_del_deferred);
  }
  
  /**
   *	switchdev_port_obj_del - Delete port object
   *
   *	@dev: port device
   *	@id: object ID
   *	@obj: object to delete
   *
   *	rtnl_lock must be held and must not be in atomic section,
   *	in case SWITCHDEV_F_DEFER flag is not set.
   */
  int switchdev_port_obj_del(struct net_device *dev,
  			   const struct switchdev_obj *obj)
  {
  	if (obj->flags & SWITCHDEV_F_DEFER)
  		return switchdev_port_obj_del_defer(dev, obj);
  	ASSERT_RTNL();
  	return switchdev_port_obj_del_now(dev, obj);
  }
491d0f153   Scott Feldman   switchdev: introd...
522
  EXPORT_SYMBOL_GPL(switchdev_port_obj_del);
45d4122ca   Samudrala, Sridhar   switchdev: add su...
523
524
525
526
  /**
   *	switchdev_port_obj_dump - Dump port objects
   *
   *	@dev: port device
25f07adc4   Vivien Didelot   net: switchdev: p...
527
   *	@id: object ID
45d4122ca   Samudrala, Sridhar   switchdev: add su...
528
   *	@obj: object to dump
25f07adc4   Vivien Didelot   net: switchdev: p...
529
   *	@cb: function to call with a filled object
771acac2f   Jiri Pirko   switchdev: assert...
530
531
   *
   *	rtnl_lock must be held.
45d4122ca   Samudrala, Sridhar   switchdev: add su...
532
   */
9e8f4a548   Jiri Pirko   switchdev: push o...
533
  int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj,
648b4a995   Jiri Pirko   switchdev: bring ...
534
  			    switchdev_obj_dump_cb_t *cb)
45d4122ca   Samudrala, Sridhar   switchdev: add su...
535
536
537
538
539
  {
  	const struct switchdev_ops *ops = dev->switchdev_ops;
  	struct net_device *lower_dev;
  	struct list_head *iter;
  	int err = -EOPNOTSUPP;
771acac2f   Jiri Pirko   switchdev: assert...
540
  	ASSERT_RTNL();
45d4122ca   Samudrala, Sridhar   switchdev: add su...
541
  	if (ops && ops->switchdev_port_obj_dump)
9e8f4a548   Jiri Pirko   switchdev: push o...
542
  		return ops->switchdev_port_obj_dump(dev, obj, cb);
45d4122ca   Samudrala, Sridhar   switchdev: add su...
543
544
545
546
547
548
549
  
  	/* Switch device port(s) may be stacked under
  	 * bond/team/vlan dev, so recurse down to dump objects on
  	 * first port at bottom of stack.
  	 */
  
  	netdev_for_each_lower_dev(dev, lower_dev, iter) {
9e8f4a548   Jiri Pirko   switchdev: push o...
550
  		err = switchdev_port_obj_dump(lower_dev, obj, cb);
45d4122ca   Samudrala, Sridhar   switchdev: add su...
551
552
553
554
555
556
  		break;
  	}
  
  	return err;
  }
  EXPORT_SYMBOL_GPL(switchdev_port_obj_dump);
ebb9a03a5   Jiri Pirko   switchdev: s/netd...
557
  static RAW_NOTIFIER_HEAD(switchdev_notif_chain);
03bf0c281   Jiri Pirko   switchdev: introd...
558
559
  
  /**
ebb9a03a5   Jiri Pirko   switchdev: s/netd...
560
   *	register_switchdev_notifier - Register notifier
03bf0c281   Jiri Pirko   switchdev: introd...
561
562
563
564
565
566
   *	@nb: notifier_block
   *
   *	Register switch device notifier. This should be used by code
   *	which needs to monitor events happening in particular device.
   *	Return values are same as for atomic_notifier_chain_register().
   */
ebb9a03a5   Jiri Pirko   switchdev: s/netd...
567
  int register_switchdev_notifier(struct notifier_block *nb)
03bf0c281   Jiri Pirko   switchdev: introd...
568
569
  {
  	int err;
4f2c6ae5c   Ido Schimmel   switchdev: Requir...
570
  	rtnl_lock();
ebb9a03a5   Jiri Pirko   switchdev: s/netd...
571
  	err = raw_notifier_chain_register(&switchdev_notif_chain, nb);
4f2c6ae5c   Ido Schimmel   switchdev: Requir...
572
  	rtnl_unlock();
03bf0c281   Jiri Pirko   switchdev: introd...
573
574
  	return err;
  }
ebb9a03a5   Jiri Pirko   switchdev: s/netd...
575
  EXPORT_SYMBOL_GPL(register_switchdev_notifier);
03bf0c281   Jiri Pirko   switchdev: introd...
576
577
  
  /**
ebb9a03a5   Jiri Pirko   switchdev: s/netd...
578
   *	unregister_switchdev_notifier - Unregister notifier
03bf0c281   Jiri Pirko   switchdev: introd...
579
580
581
582
583
   *	@nb: notifier_block
   *
   *	Unregister switch device notifier.
   *	Return values are same as for atomic_notifier_chain_unregister().
   */
ebb9a03a5   Jiri Pirko   switchdev: s/netd...
584
  int unregister_switchdev_notifier(struct notifier_block *nb)
03bf0c281   Jiri Pirko   switchdev: introd...
585
586
  {
  	int err;
4f2c6ae5c   Ido Schimmel   switchdev: Requir...
587
  	rtnl_lock();
ebb9a03a5   Jiri Pirko   switchdev: s/netd...
588
  	err = raw_notifier_chain_unregister(&switchdev_notif_chain, nb);
4f2c6ae5c   Ido Schimmel   switchdev: Requir...
589
  	rtnl_unlock();
03bf0c281   Jiri Pirko   switchdev: introd...
590
591
  	return err;
  }
ebb9a03a5   Jiri Pirko   switchdev: s/netd...
592
  EXPORT_SYMBOL_GPL(unregister_switchdev_notifier);
03bf0c281   Jiri Pirko   switchdev: introd...
593
594
  
  /**
ebb9a03a5   Jiri Pirko   switchdev: s/netd...
595
   *	call_switchdev_notifiers - Call notifiers
03bf0c281   Jiri Pirko   switchdev: introd...
596
597
598
599
600
601
602
   *	@val: value passed unmodified to notifier function
   *	@dev: port device
   *	@info: notifier information data
   *
   *	Call all network notifier blocks. This should be called by driver
   *	when it needs to propagate hardware event.
   *	Return values are same as for atomic_notifier_call_chain().
4f2c6ae5c   Ido Schimmel   switchdev: Requir...
603
   *	rtnl_lock must be held.
03bf0c281   Jiri Pirko   switchdev: introd...
604
   */
ebb9a03a5   Jiri Pirko   switchdev: s/netd...
605
606
  int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
  			     struct switchdev_notifier_info *info)
03bf0c281   Jiri Pirko   switchdev: introd...
607
608
  {
  	int err;
4f2c6ae5c   Ido Schimmel   switchdev: Requir...
609
  	ASSERT_RTNL();
03bf0c281   Jiri Pirko   switchdev: introd...
610
  	info->dev = dev;
ebb9a03a5   Jiri Pirko   switchdev: s/netd...
611
  	err = raw_notifier_call_chain(&switchdev_notif_chain, val, info);
03bf0c281   Jiri Pirko   switchdev: introd...
612
613
  	return err;
  }
ebb9a03a5   Jiri Pirko   switchdev: s/netd...
614
  EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
8a44dbb20   Roopa Prabhu   swdevice: add new...
615

7d4f8d871   Scott Feldman   switchdev; add VL...
616
  struct switchdev_vlan_dump {
8f24f3095   Jiri Pirko   switchdev: rename...
617
  	struct switchdev_obj_port_vlan vlan;
7d4f8d871   Scott Feldman   switchdev; add VL...
618
619
620
621
622
623
  	struct sk_buff *skb;
  	u32 filter_mask;
  	u16 flags;
  	u16 begin;
  	u16 end;
  };
e23b002b2   Vivien Didelot   net: switchdev: r...
624
  static int switchdev_port_vlan_dump_put(struct switchdev_vlan_dump *dump)
7d4f8d871   Scott Feldman   switchdev; add VL...
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
  {
  	struct bridge_vlan_info vinfo;
  
  	vinfo.flags = dump->flags;
  
  	if (dump->begin == 0 && dump->end == 0) {
  		return 0;
  	} else if (dump->begin == dump->end) {
  		vinfo.vid = dump->begin;
  		if (nla_put(dump->skb, IFLA_BRIDGE_VLAN_INFO,
  			    sizeof(vinfo), &vinfo))
  			return -EMSGSIZE;
  	} else {
  		vinfo.vid = dump->begin;
  		vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
  		if (nla_put(dump->skb, IFLA_BRIDGE_VLAN_INFO,
  			    sizeof(vinfo), &vinfo))
  			return -EMSGSIZE;
  		vinfo.vid = dump->end;
  		vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
  		vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END;
  		if (nla_put(dump->skb, IFLA_BRIDGE_VLAN_INFO,
  			    sizeof(vinfo), &vinfo))
  			return -EMSGSIZE;
  	}
  
  	return 0;
  }
648b4a995   Jiri Pirko   switchdev: bring ...
653
  static int switchdev_port_vlan_dump_cb(struct switchdev_obj *obj)
7d4f8d871   Scott Feldman   switchdev; add VL...
654
  {
648b4a995   Jiri Pirko   switchdev: bring ...
655
  	struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
7d4f8d871   Scott Feldman   switchdev; add VL...
656
  	struct switchdev_vlan_dump *dump =
25f07adc4   Vivien Didelot   net: switchdev: p...
657
  		container_of(vlan, struct switchdev_vlan_dump, vlan);
7d4f8d871   Scott Feldman   switchdev; add VL...
658
659
660
661
662
663
664
665
666
667
  	int err = 0;
  
  	if (vlan->vid_begin > vlan->vid_end)
  		return -EINVAL;
  
  	if (dump->filter_mask & RTEXT_FILTER_BRVLAN) {
  		dump->flags = vlan->flags;
  		for (dump->begin = dump->end = vlan->vid_begin;
  		     dump->begin <= vlan->vid_end;
  		     dump->begin++, dump->end++) {
e23b002b2   Vivien Didelot   net: switchdev: r...
668
  			err = switchdev_port_vlan_dump_put(dump);
7d4f8d871   Scott Feldman   switchdev; add VL...
669
670
671
672
673
674
675
676
677
678
679
  			if (err)
  				return err;
  		}
  	} else if (dump->filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED) {
  		if (dump->begin > vlan->vid_begin &&
  		    dump->begin >= vlan->vid_end) {
  			if ((dump->begin - 1) == vlan->vid_end &&
  			    dump->flags == vlan->flags) {
  				/* prepend */
  				dump->begin = vlan->vid_begin;
  			} else {
e23b002b2   Vivien Didelot   net: switchdev: r...
680
  				err = switchdev_port_vlan_dump_put(dump);
7d4f8d871   Scott Feldman   switchdev; add VL...
681
682
683
684
685
686
687
688
689
690
691
  				dump->flags = vlan->flags;
  				dump->begin = vlan->vid_begin;
  				dump->end = vlan->vid_end;
  			}
  		} else if (dump->end <= vlan->vid_begin &&
  		           dump->end < vlan->vid_end) {
  			if ((dump->end  + 1) == vlan->vid_begin &&
  			    dump->flags == vlan->flags) {
  				/* append */
  				dump->end = vlan->vid_end;
  			} else {
e23b002b2   Vivien Didelot   net: switchdev: r...
692
  				err = switchdev_port_vlan_dump_put(dump);
7d4f8d871   Scott Feldman   switchdev; add VL...
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
  				dump->flags = vlan->flags;
  				dump->begin = vlan->vid_begin;
  				dump->end = vlan->vid_end;
  			}
  		} else {
  			err = -EINVAL;
  		}
  	}
  
  	return err;
  }
  
  static int switchdev_port_vlan_fill(struct sk_buff *skb, struct net_device *dev,
  				    u32 filter_mask)
  {
  	struct switchdev_vlan_dump dump = {
6ff64f6f9   Ido Schimmel   switchdev: Pass o...
709
  		.vlan.obj.orig_dev = dev,
9e8f4a548   Jiri Pirko   switchdev: push o...
710
  		.vlan.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
7d4f8d871   Scott Feldman   switchdev; add VL...
711
712
713
714
715
716
717
  		.skb = skb,
  		.filter_mask = filter_mask,
  	};
  	int err = 0;
  
  	if ((filter_mask & RTEXT_FILTER_BRVLAN) ||
  	    (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)) {
9e8f4a548   Jiri Pirko   switchdev: push o...
718
  		err = switchdev_port_obj_dump(dev, &dump.vlan.obj,
25f07adc4   Vivien Didelot   net: switchdev: p...
719
  					      switchdev_port_vlan_dump_cb);
7d4f8d871   Scott Feldman   switchdev; add VL...
720
721
722
723
  		if (err)
  			goto err_out;
  		if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)
  			/* last one */
e23b002b2   Vivien Didelot   net: switchdev: r...
724
  			err = switchdev_port_vlan_dump_put(&dump);
7d4f8d871   Scott Feldman   switchdev; add VL...
725
726
727
728
729
  	}
  
  err_out:
  	return err == -EOPNOTSUPP ? 0 : err;
  }
8793d0a66   Scott Feldman   switchdev: add ne...
730
731
732
733
734
735
736
737
738
739
740
741
742
  /**
   *	switchdev_port_bridge_getlink - Get bridge port attributes
   *
   *	@dev: port device
   *
   *	Called for SELF on rtnl_bridge_getlink to get bridge port
   *	attributes.
   */
  int switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
  				  struct net_device *dev, u32 filter_mask,
  				  int nlflags)
  {
  	struct switchdev_attr attr = {
6ff64f6f9   Ido Schimmel   switchdev: Pass o...
743
  		.orig_dev = dev,
1f8683987   Jiri Pirko   switchdev: rename...
744
  		.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
8793d0a66   Scott Feldman   switchdev: add ne...
745
746
  	};
  	u16 mode = BRIDGE_MODE_UNDEF;
741af0053   Ido Schimmel   switchdev: Add su...
747
  	u32 mask = BR_LEARNING | BR_LEARNING_SYNC | BR_FLOOD;
8793d0a66   Scott Feldman   switchdev: add ne...
748
  	int err;
97c242902   Ido Schimmel   switchdev: Execut...
749
750
  	if (!netif_is_bridge_port(dev))
  		return -EOPNOTSUPP;
8793d0a66   Scott Feldman   switchdev: add ne...
751
  	err = switchdev_port_attr_get(dev, &attr);
5c8079d04   Vivien Didelot   net: switchdev: i...
752
  	if (err && err != -EOPNOTSUPP)
8793d0a66   Scott Feldman   switchdev: add ne...
753
754
755
  		return err;
  
  	return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode,
7d4f8d871   Scott Feldman   switchdev; add VL...
756
757
  				       attr.u.brport_flags, mask, nlflags,
  				       filter_mask, switchdev_port_vlan_fill);
8793d0a66   Scott Feldman   switchdev: add ne...
758
759
  }
  EXPORT_SYMBOL_GPL(switchdev_port_bridge_getlink);
47f8328bb   Scott Feldman   switchdev: add ne...
760
761
762
763
764
  static int switchdev_port_br_setflag(struct net_device *dev,
  				     struct nlattr *nlattr,
  				     unsigned long brport_flag)
  {
  	struct switchdev_attr attr = {
6ff64f6f9   Ido Schimmel   switchdev: Pass o...
765
  		.orig_dev = dev,
1f8683987   Jiri Pirko   switchdev: rename...
766
  		.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
47f8328bb   Scott Feldman   switchdev: add ne...
767
768
769
770
771
772
773
774
775
  	};
  	u8 flag = nla_get_u8(nlattr);
  	int err;
  
  	err = switchdev_port_attr_get(dev, &attr);
  	if (err)
  		return err;
  
  	if (flag)
42275bd8f   Scott Feldman   switchdev: don't ...
776
  		attr.u.brport_flags |= brport_flag;
47f8328bb   Scott Feldman   switchdev: add ne...
777
  	else
42275bd8f   Scott Feldman   switchdev: don't ...
778
  		attr.u.brport_flags &= ~brport_flag;
47f8328bb   Scott Feldman   switchdev: add ne...
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
  
  	return switchdev_port_attr_set(dev, &attr);
  }
  
  static const struct nla_policy
  switchdev_port_bridge_policy[IFLA_BRPORT_MAX + 1] = {
  	[IFLA_BRPORT_STATE]		= { .type = NLA_U8 },
  	[IFLA_BRPORT_COST]		= { .type = NLA_U32 },
  	[IFLA_BRPORT_PRIORITY]		= { .type = NLA_U16 },
  	[IFLA_BRPORT_MODE]		= { .type = NLA_U8 },
  	[IFLA_BRPORT_GUARD]		= { .type = NLA_U8 },
  	[IFLA_BRPORT_PROTECT]		= { .type = NLA_U8 },
  	[IFLA_BRPORT_FAST_LEAVE]	= { .type = NLA_U8 },
  	[IFLA_BRPORT_LEARNING]		= { .type = NLA_U8 },
  	[IFLA_BRPORT_LEARNING_SYNC]	= { .type = NLA_U8 },
  	[IFLA_BRPORT_UNICAST_FLOOD]	= { .type = NLA_U8 },
  };
  
  static int switchdev_port_br_setlink_protinfo(struct net_device *dev,
  					      struct nlattr *protinfo)
  {
  	struct nlattr *attr;
  	int rem;
  	int err;
  
  	err = nla_validate_nested(protinfo, IFLA_BRPORT_MAX,
  				  switchdev_port_bridge_policy);
  	if (err)
  		return err;
  
  	nla_for_each_nested(attr, protinfo, rem) {
  		switch (nla_type(attr)) {
  		case IFLA_BRPORT_LEARNING:
  			err = switchdev_port_br_setflag(dev, attr,
  							BR_LEARNING);
  			break;
  		case IFLA_BRPORT_LEARNING_SYNC:
  			err = switchdev_port_br_setflag(dev, attr,
  							BR_LEARNING_SYNC);
  			break;
741af0053   Ido Schimmel   switchdev: Add su...
819
820
821
  		case IFLA_BRPORT_UNICAST_FLOOD:
  			err = switchdev_port_br_setflag(dev, attr, BR_FLOOD);
  			break;
47f8328bb   Scott Feldman   switchdev: add ne...
822
823
824
825
826
827
828
829
830
831
832
833
834
835
  		default:
  			err = -EOPNOTSUPP;
  			break;
  		}
  		if (err)
  			return err;
  	}
  
  	return 0;
  }
  
  static int switchdev_port_br_afspec(struct net_device *dev,
  				    struct nlattr *afspec,
  				    int (*f)(struct net_device *dev,
648b4a995   Jiri Pirko   switchdev: bring ...
836
  					     const struct switchdev_obj *obj))
47f8328bb   Scott Feldman   switchdev: add ne...
837
838
839
  {
  	struct nlattr *attr;
  	struct bridge_vlan_info *vinfo;
9e8f4a548   Jiri Pirko   switchdev: push o...
840
  	struct switchdev_obj_port_vlan vlan = {
6ff64f6f9   Ido Schimmel   switchdev: Pass o...
841
  		.obj.orig_dev = dev,
9e8f4a548   Jiri Pirko   switchdev: push o...
842
843
  		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
  	};
47f8328bb   Scott Feldman   switchdev: add ne...
844
845
846
847
848
849
850
851
852
  	int rem;
  	int err;
  
  	nla_for_each_nested(attr, afspec, rem) {
  		if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO)
  			continue;
  		if (nla_len(attr) != sizeof(struct bridge_vlan_info))
  			return -EINVAL;
  		vinfo = nla_data(attr);
87aaf2cae   Nikolay Aleksandrov   switchdev: check ...
853
854
  		if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK)
  			return -EINVAL;
ab0690023   Vivien Didelot   net: switchdev: a...
855
  		vlan.flags = vinfo->flags;
47f8328bb   Scott Feldman   switchdev: add ne...
856
  		if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
ab0690023   Vivien Didelot   net: switchdev: a...
857
  			if (vlan.vid_begin)
47f8328bb   Scott Feldman   switchdev: add ne...
858
  				return -EINVAL;
ab0690023   Vivien Didelot   net: switchdev: a...
859
  			vlan.vid_begin = vinfo->vid;
cc02aa8e4   Nikolay Aleksandrov   switchdev: enforc...
860
861
862
  			/* don't allow range of pvids */
  			if (vlan.flags & BRIDGE_VLAN_INFO_PVID)
  				return -EINVAL;
47f8328bb   Scott Feldman   switchdev: add ne...
863
  		} else if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END) {
ab0690023   Vivien Didelot   net: switchdev: a...
864
  			if (!vlan.vid_begin)
47f8328bb   Scott Feldman   switchdev: add ne...
865
  				return -EINVAL;
ab0690023   Vivien Didelot   net: switchdev: a...
866
867
  			vlan.vid_end = vinfo->vid;
  			if (vlan.vid_end <= vlan.vid_begin)
47f8328bb   Scott Feldman   switchdev: add ne...
868
  				return -EINVAL;
9e8f4a548   Jiri Pirko   switchdev: push o...
869
  			err = f(dev, &vlan.obj);
47f8328bb   Scott Feldman   switchdev: add ne...
870
871
  			if (err)
  				return err;
3a7bde55a   Scott Feldman   switchdev: fix: e...
872
  			vlan.vid_begin = 0;
47f8328bb   Scott Feldman   switchdev: add ne...
873
  		} else {
ab0690023   Vivien Didelot   net: switchdev: a...
874
  			if (vlan.vid_begin)
47f8328bb   Scott Feldman   switchdev: add ne...
875
  				return -EINVAL;
ab0690023   Vivien Didelot   net: switchdev: a...
876
877
  			vlan.vid_begin = vinfo->vid;
  			vlan.vid_end = vinfo->vid;
9e8f4a548   Jiri Pirko   switchdev: push o...
878
  			err = f(dev, &vlan.obj);
47f8328bb   Scott Feldman   switchdev: add ne...
879
880
  			if (err)
  				return err;
3a7bde55a   Scott Feldman   switchdev: fix: e...
881
  			vlan.vid_begin = 0;
47f8328bb   Scott Feldman   switchdev: add ne...
882
883
884
885
886
  		}
  	}
  
  	return 0;
  }
8a44dbb20   Roopa Prabhu   swdevice: add new...
887
  /**
47f8328bb   Scott Feldman   switchdev: add ne...
888
   *	switchdev_port_bridge_setlink - Set bridge port attributes
8a44dbb20   Roopa Prabhu   swdevice: add new...
889
890
   *
   *	@dev: port device
47f8328bb   Scott Feldman   switchdev: add ne...
891
892
   *	@nlh: netlink header
   *	@flags: netlink flags
8a44dbb20   Roopa Prabhu   swdevice: add new...
893
   *
47f8328bb   Scott Feldman   switchdev: add ne...
894
895
   *	Called for SELF on rtnl_bridge_setlink to set bridge port
   *	attributes.
8a44dbb20   Roopa Prabhu   swdevice: add new...
896
   */
ebb9a03a5   Jiri Pirko   switchdev: s/netd...
897
898
  int switchdev_port_bridge_setlink(struct net_device *dev,
  				  struct nlmsghdr *nlh, u16 flags)
8a44dbb20   Roopa Prabhu   swdevice: add new...
899
  {
47f8328bb   Scott Feldman   switchdev: add ne...
900
901
902
  	struct nlattr *protinfo;
  	struct nlattr *afspec;
  	int err = 0;
97c242902   Ido Schimmel   switchdev: Execut...
903
904
  	if (!netif_is_bridge_port(dev))
  		return -EOPNOTSUPP;
47f8328bb   Scott Feldman   switchdev: add ne...
905
906
907
908
909
910
911
  	protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
  				   IFLA_PROTINFO);
  	if (protinfo) {
  		err = switchdev_port_br_setlink_protinfo(dev, protinfo);
  		if (err)
  			return err;
  	}
8a44dbb20   Roopa Prabhu   swdevice: add new...
912

47f8328bb   Scott Feldman   switchdev: add ne...
913
914
915
916
917
  	afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
  				 IFLA_AF_SPEC);
  	if (afspec)
  		err = switchdev_port_br_afspec(dev, afspec,
  					       switchdev_port_obj_add);
8a44dbb20   Roopa Prabhu   swdevice: add new...
918

47f8328bb   Scott Feldman   switchdev: add ne...
919
  	return err;
8a44dbb20   Roopa Prabhu   swdevice: add new...
920
  }
ebb9a03a5   Jiri Pirko   switchdev: s/netd...
921
  EXPORT_SYMBOL_GPL(switchdev_port_bridge_setlink);
8a44dbb20   Roopa Prabhu   swdevice: add new...
922
923
  
  /**
5c34e0221   Scott Feldman   switchdev: add ne...
924
   *	switchdev_port_bridge_dellink - Set bridge port attributes
8a44dbb20   Roopa Prabhu   swdevice: add new...
925
926
   *
   *	@dev: port device
5c34e0221   Scott Feldman   switchdev: add ne...
927
928
   *	@nlh: netlink header
   *	@flags: netlink flags
8a44dbb20   Roopa Prabhu   swdevice: add new...
929
   *
5c34e0221   Scott Feldman   switchdev: add ne...
930
931
   *	Called for SELF on rtnl_bridge_dellink to set bridge port
   *	attributes.
8a44dbb20   Roopa Prabhu   swdevice: add new...
932
   */
ebb9a03a5   Jiri Pirko   switchdev: s/netd...
933
934
  int switchdev_port_bridge_dellink(struct net_device *dev,
  				  struct nlmsghdr *nlh, u16 flags)
8a44dbb20   Roopa Prabhu   swdevice: add new...
935
  {
5c34e0221   Scott Feldman   switchdev: add ne...
936
  	struct nlattr *afspec;
8a44dbb20   Roopa Prabhu   swdevice: add new...
937

97c242902   Ido Schimmel   switchdev: Execut...
938
939
  	if (!netif_is_bridge_port(dev))
  		return -EOPNOTSUPP;
5c34e0221   Scott Feldman   switchdev: add ne...
940
941
942
943
944
  	afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg),
  				 IFLA_AF_SPEC);
  	if (afspec)
  		return switchdev_port_br_afspec(dev, afspec,
  						switchdev_port_obj_del);
8a44dbb20   Roopa Prabhu   swdevice: add new...
945

5c34e0221   Scott Feldman   switchdev: add ne...
946
  	return 0;
8a44dbb20   Roopa Prabhu   swdevice: add new...
947
  }
ebb9a03a5   Jiri Pirko   switchdev: s/netd...
948
  EXPORT_SYMBOL_GPL(switchdev_port_bridge_dellink);
8a44dbb20   Roopa Prabhu   swdevice: add new...
949

45d4122ca   Samudrala, Sridhar   switchdev: add su...
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
  /**
   *	switchdev_port_fdb_add - Add FDB (MAC/VLAN) entry to port
   *
   *	@ndmsg: netlink hdr
   *	@nlattr: netlink attributes
   *	@dev: port device
   *	@addr: MAC address to add
   *	@vid: VLAN to add
   *
   *	Add FDB entry to switch device.
   */
  int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
  			   struct net_device *dev, const unsigned char *addr,
  			   u16 vid, u16 nlm_flags)
  {
52ba57cfd   Jiri Pirko   switchdev: rename...
965
  	struct switchdev_obj_port_fdb fdb = {
6ff64f6f9   Ido Schimmel   switchdev: Pass o...
966
  		.obj.orig_dev = dev,
9e8f4a548   Jiri Pirko   switchdev: push o...
967
  		.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
ab0690023   Vivien Didelot   net: switchdev: a...
968
  		.vid = vid,
45d4122ca   Samudrala, Sridhar   switchdev: add su...
969
  	};
850d0cbc9   Jiri Pirko   switchdev: remove...
970
  	ether_addr_copy(fdb.addr, addr);
9e8f4a548   Jiri Pirko   switchdev: push o...
971
  	return switchdev_port_obj_add(dev, &fdb.obj);
45d4122ca   Samudrala, Sridhar   switchdev: add su...
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
  }
  EXPORT_SYMBOL_GPL(switchdev_port_fdb_add);
  
  /**
   *	switchdev_port_fdb_del - Delete FDB (MAC/VLAN) entry from port
   *
   *	@ndmsg: netlink hdr
   *	@nlattr: netlink attributes
   *	@dev: port device
   *	@addr: MAC address to delete
   *	@vid: VLAN to delete
   *
   *	Delete FDB entry from switch device.
   */
  int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
  			   struct net_device *dev, const unsigned char *addr,
  			   u16 vid)
  {
52ba57cfd   Jiri Pirko   switchdev: rename...
990
  	struct switchdev_obj_port_fdb fdb = {
6ff64f6f9   Ido Schimmel   switchdev: Pass o...
991
  		.obj.orig_dev = dev,
9e8f4a548   Jiri Pirko   switchdev: push o...
992
  		.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
ab0690023   Vivien Didelot   net: switchdev: a...
993
  		.vid = vid,
45d4122ca   Samudrala, Sridhar   switchdev: add su...
994
  	};
850d0cbc9   Jiri Pirko   switchdev: remove...
995
  	ether_addr_copy(fdb.addr, addr);
9e8f4a548   Jiri Pirko   switchdev: push o...
996
  	return switchdev_port_obj_del(dev, &fdb.obj);
45d4122ca   Samudrala, Sridhar   switchdev: add su...
997
998
999
1000
  }
  EXPORT_SYMBOL_GPL(switchdev_port_fdb_del);
  
  struct switchdev_fdb_dump {
52ba57cfd   Jiri Pirko   switchdev: rename...
1001
  	struct switchdev_obj_port_fdb fdb;
e02a06b2a   Vivien Didelot   net: switchdev: m...
1002
  	struct net_device *dev;
45d4122ca   Samudrala, Sridhar   switchdev: add su...
1003
1004
  	struct sk_buff *skb;
  	struct netlink_callback *cb;
45d4122ca   Samudrala, Sridhar   switchdev: add su...
1005
1006
  	int idx;
  };
648b4a995   Jiri Pirko   switchdev: bring ...
1007
  static int switchdev_port_fdb_dump_cb(struct switchdev_obj *obj)
45d4122ca   Samudrala, Sridhar   switchdev: add su...
1008
  {
648b4a995   Jiri Pirko   switchdev: bring ...
1009
  	struct switchdev_obj_port_fdb *fdb = SWITCHDEV_OBJ_PORT_FDB(obj);
45d4122ca   Samudrala, Sridhar   switchdev: add su...
1010
  	struct switchdev_fdb_dump *dump =
25f07adc4   Vivien Didelot   net: switchdev: p...
1011
  		container_of(fdb, struct switchdev_fdb_dump, fdb);
45d4122ca   Samudrala, Sridhar   switchdev: add su...
1012
1013
1014
1015
  	u32 portid = NETLINK_CB(dump->cb->skb).portid;
  	u32 seq = dump->cb->nlh->nlmsg_seq;
  	struct nlmsghdr *nlh;
  	struct ndmsg *ndm;
45d4122ca   Samudrala, Sridhar   switchdev: add su...
1016

d297653dd   Roopa Prabhu   rtnetlink: fdb du...
1017
  	if (dump->idx < dump->cb->args[2])
45d4122ca   Samudrala, Sridhar   switchdev: add su...
1018
  		goto skip;
45d4122ca   Samudrala, Sridhar   switchdev: add su...
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
  	nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
  			sizeof(*ndm), NLM_F_MULTI);
  	if (!nlh)
  		return -EMSGSIZE;
  
  	ndm = nlmsg_data(nlh);
  	ndm->ndm_family  = AF_BRIDGE;
  	ndm->ndm_pad1    = 0;
  	ndm->ndm_pad2    = 0;
  	ndm->ndm_flags   = NTF_SELF;
  	ndm->ndm_type    = 0;
e02a06b2a   Vivien Didelot   net: switchdev: m...
1030
  	ndm->ndm_ifindex = dump->dev->ifindex;
25f07adc4   Vivien Didelot   net: switchdev: p...
1031
  	ndm->ndm_state   = fdb->ndm_state;
45d4122ca   Samudrala, Sridhar   switchdev: add su...
1032

25f07adc4   Vivien Didelot   net: switchdev: p...
1033
  	if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, fdb->addr))
45d4122ca   Samudrala, Sridhar   switchdev: add su...
1034
  		goto nla_put_failure;
25f07adc4   Vivien Didelot   net: switchdev: p...
1035
  	if (fdb->vid && nla_put_u16(dump->skb, NDA_VLAN, fdb->vid))
45d4122ca   Samudrala, Sridhar   switchdev: add su...
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
  		goto nla_put_failure;
  
  	nlmsg_end(dump->skb, nlh);
  
  skip:
  	dump->idx++;
  	return 0;
  
  nla_put_failure:
  	nlmsg_cancel(dump->skb, nlh);
  	return -EMSGSIZE;
  }
  
  /**
   *	switchdev_port_fdb_dump - Dump port FDB (MAC/VLAN) entries
   *
   *	@skb: netlink skb
   *	@cb: netlink callback
   *	@dev: port device
   *	@filter_dev: filter device
   *	@idx:
   *
3e3476604   Nicolas Dichtel   switchdev: fix ty...
1058
   *	Dump FDB entries from switch device.
45d4122ca   Samudrala, Sridhar   switchdev: add su...
1059
1060
1061
   */
  int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
  			    struct net_device *dev,
d297653dd   Roopa Prabhu   rtnetlink: fdb du...
1062
  			    struct net_device *filter_dev, int *idx)
45d4122ca   Samudrala, Sridhar   switchdev: add su...
1063
1064
  {
  	struct switchdev_fdb_dump dump = {
6ff64f6f9   Ido Schimmel   switchdev: Pass o...
1065
  		.fdb.obj.orig_dev = dev,
9e8f4a548   Jiri Pirko   switchdev: push o...
1066
  		.fdb.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
e02a06b2a   Vivien Didelot   net: switchdev: m...
1067
  		.dev = dev,
45d4122ca   Samudrala, Sridhar   switchdev: add su...
1068
1069
  		.skb = skb,
  		.cb = cb,
d297653dd   Roopa Prabhu   rtnetlink: fdb du...
1070
  		.idx = *idx,
45d4122ca   Samudrala, Sridhar   switchdev: add su...
1071
  	};
472681d57   MINOURA Makoto / 箕浦 真   net: ndo_fdb_dump...
1072
  	int err;
45d4122ca   Samudrala, Sridhar   switchdev: add su...
1073

472681d57   MINOURA Makoto / 箕浦 真   net: ndo_fdb_dump...
1074
1075
  	err = switchdev_port_obj_dump(dev, &dump.fdb.obj,
  				      switchdev_port_fdb_dump_cb);
d297653dd   Roopa Prabhu   rtnetlink: fdb du...
1076
1077
  	*idx = dump.idx;
  	return err;
45d4122ca   Samudrala, Sridhar   switchdev: add su...
1078
1079
  }
  EXPORT_SYMBOL_GPL(switchdev_port_fdb_dump);
8438884d4   Or Gerlitz   net/switchdev: Ex...
1080
1081
  bool switchdev_port_same_parent_id(struct net_device *a,
  				   struct net_device *b)
1a3b2ec93   Scott Feldman   switchdev: add of...
1082
1083
  {
  	struct switchdev_attr a_attr = {
6ff64f6f9   Ido Schimmel   switchdev: Pass o...
1084
  		.orig_dev = a,
1f8683987   Jiri Pirko   switchdev: rename...
1085
  		.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
1a3b2ec93   Scott Feldman   switchdev: add of...
1086
1087
  	};
  	struct switchdev_attr b_attr = {
6ff64f6f9   Ido Schimmel   switchdev: Pass o...
1088
  		.orig_dev = b,
1f8683987   Jiri Pirko   switchdev: rename...
1089
  		.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
1a3b2ec93   Scott Feldman   switchdev: add of...
1090
1091
1092
1093
1094
1095
1096
1097
  	};
  
  	if (switchdev_port_attr_get(a, &a_attr) ||
  	    switchdev_port_attr_get(b, &b_attr))
  		return false;
  
  	return netdev_phys_item_id_same(&a_attr.u.ppid, &b_attr.u.ppid);
  }
2eb03e6c4   Or Gerlitz   switchdev: Put ex...
1098
  EXPORT_SYMBOL_GPL(switchdev_port_same_parent_id);