1007f790cSJiri Pirko /* 2007f790cSJiri Pirko * net/switchdev/switchdev.c - Switch device API 37ea6eb3fSJiri Pirko * Copyright (c) 2014-2015 Jiri Pirko <[email protected]> 4f8f21471SScott Feldman * Copyright (c) 2014-2015 Scott Feldman <[email protected]> 5007f790cSJiri Pirko * 6007f790cSJiri Pirko * This program is free software; you can redistribute it and/or modify 7007f790cSJiri Pirko * it under the terms of the GNU General Public License as published by 8007f790cSJiri Pirko * the Free Software Foundation; either version 2 of the License, or 9007f790cSJiri Pirko * (at your option) any later version. 10007f790cSJiri Pirko */ 11007f790cSJiri Pirko 12007f790cSJiri Pirko #include <linux/kernel.h> 13007f790cSJiri Pirko #include <linux/types.h> 14007f790cSJiri Pirko #include <linux/init.h> 1503bf0c28SJiri Pirko #include <linux/mutex.h> 1603bf0c28SJiri Pirko #include <linux/notifier.h> 17007f790cSJiri Pirko #include <linux/netdevice.h> 18850d0cbcSJiri Pirko #include <linux/etherdevice.h> 1947f8328bSScott Feldman #include <linux/if_bridge.h> 207ea6eb3fSJiri Pirko #include <linux/list.h> 21793f4014SJiri Pirko #include <linux/workqueue.h> 2287aaf2caSNikolay Aleksandrov #include <linux/if_vlan.h> 234f2c6ae5SIdo Schimmel #include <linux/rtnetlink.h> 24007f790cSJiri Pirko #include <net/switchdev.h> 25007f790cSJiri Pirko 26007f790cSJiri Pirko /** 277ea6eb3fSJiri Pirko * switchdev_trans_item_enqueue - Enqueue data item to transaction queue 287ea6eb3fSJiri Pirko * 297ea6eb3fSJiri Pirko * @trans: transaction 307ea6eb3fSJiri Pirko * @data: pointer to data being queued 317ea6eb3fSJiri Pirko * @destructor: data destructor 327ea6eb3fSJiri Pirko * @tritem: transaction item being queued 337ea6eb3fSJiri Pirko * 347ea6eb3fSJiri Pirko * Enqeueue data item to transaction queue. tritem is typically placed in 357ea6eb3fSJiri Pirko * cointainter pointed at by data pointer. Destructor is called on 367ea6eb3fSJiri Pirko * transaction abort and after successful commit phase in case 377ea6eb3fSJiri Pirko * the caller did not dequeue the item before. 387ea6eb3fSJiri Pirko */ 397ea6eb3fSJiri Pirko void switchdev_trans_item_enqueue(struct switchdev_trans *trans, 407ea6eb3fSJiri Pirko void *data, void (*destructor)(void const *), 417ea6eb3fSJiri Pirko struct switchdev_trans_item *tritem) 427ea6eb3fSJiri Pirko { 437ea6eb3fSJiri Pirko tritem->data = data; 447ea6eb3fSJiri Pirko tritem->destructor = destructor; 457ea6eb3fSJiri Pirko list_add_tail(&tritem->list, &trans->item_list); 467ea6eb3fSJiri Pirko } 477ea6eb3fSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_trans_item_enqueue); 487ea6eb3fSJiri Pirko 497ea6eb3fSJiri Pirko static struct switchdev_trans_item * 507ea6eb3fSJiri Pirko __switchdev_trans_item_dequeue(struct switchdev_trans *trans) 517ea6eb3fSJiri Pirko { 527ea6eb3fSJiri Pirko struct switchdev_trans_item *tritem; 537ea6eb3fSJiri Pirko 547ea6eb3fSJiri Pirko if (list_empty(&trans->item_list)) 557ea6eb3fSJiri Pirko return NULL; 567ea6eb3fSJiri Pirko tritem = list_first_entry(&trans->item_list, 577ea6eb3fSJiri Pirko struct switchdev_trans_item, list); 587ea6eb3fSJiri Pirko list_del(&tritem->list); 597ea6eb3fSJiri Pirko return tritem; 607ea6eb3fSJiri Pirko } 617ea6eb3fSJiri Pirko 627ea6eb3fSJiri Pirko /** 637ea6eb3fSJiri Pirko * switchdev_trans_item_dequeue - Dequeue data item from transaction queue 647ea6eb3fSJiri Pirko * 657ea6eb3fSJiri Pirko * @trans: transaction 667ea6eb3fSJiri Pirko */ 677ea6eb3fSJiri Pirko void *switchdev_trans_item_dequeue(struct switchdev_trans *trans) 687ea6eb3fSJiri Pirko { 697ea6eb3fSJiri Pirko struct switchdev_trans_item *tritem; 707ea6eb3fSJiri Pirko 717ea6eb3fSJiri Pirko tritem = __switchdev_trans_item_dequeue(trans); 727ea6eb3fSJiri Pirko BUG_ON(!tritem); 737ea6eb3fSJiri Pirko return tritem->data; 747ea6eb3fSJiri Pirko } 757ea6eb3fSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_trans_item_dequeue); 767ea6eb3fSJiri Pirko 777ea6eb3fSJiri Pirko static void switchdev_trans_init(struct switchdev_trans *trans) 787ea6eb3fSJiri Pirko { 797ea6eb3fSJiri Pirko INIT_LIST_HEAD(&trans->item_list); 807ea6eb3fSJiri Pirko } 817ea6eb3fSJiri Pirko 827ea6eb3fSJiri Pirko static void switchdev_trans_items_destroy(struct switchdev_trans *trans) 837ea6eb3fSJiri Pirko { 847ea6eb3fSJiri Pirko struct switchdev_trans_item *tritem; 857ea6eb3fSJiri Pirko 867ea6eb3fSJiri Pirko while ((tritem = __switchdev_trans_item_dequeue(trans))) 877ea6eb3fSJiri Pirko tritem->destructor(tritem->data); 887ea6eb3fSJiri Pirko } 897ea6eb3fSJiri Pirko 907ea6eb3fSJiri Pirko static void switchdev_trans_items_warn_destroy(struct net_device *dev, 917ea6eb3fSJiri Pirko struct switchdev_trans *trans) 927ea6eb3fSJiri Pirko { 937ea6eb3fSJiri Pirko WARN(!list_empty(&trans->item_list), "%s: transaction item queue is not empty.\n", 947ea6eb3fSJiri Pirko dev->name); 957ea6eb3fSJiri Pirko switchdev_trans_items_destroy(trans); 967ea6eb3fSJiri Pirko } 977ea6eb3fSJiri Pirko 98793f4014SJiri Pirko static LIST_HEAD(deferred); 99793f4014SJiri Pirko static DEFINE_SPINLOCK(deferred_lock); 100793f4014SJiri Pirko 101793f4014SJiri Pirko typedef void switchdev_deferred_func_t(struct net_device *dev, 102793f4014SJiri Pirko const void *data); 103793f4014SJiri Pirko 104793f4014SJiri Pirko struct switchdev_deferred_item { 105793f4014SJiri Pirko struct list_head list; 106793f4014SJiri Pirko struct net_device *dev; 107793f4014SJiri Pirko switchdev_deferred_func_t *func; 108793f4014SJiri Pirko unsigned long data[0]; 109793f4014SJiri Pirko }; 110793f4014SJiri Pirko 111793f4014SJiri Pirko static struct switchdev_deferred_item *switchdev_deferred_dequeue(void) 112793f4014SJiri Pirko { 113793f4014SJiri Pirko struct switchdev_deferred_item *dfitem; 114793f4014SJiri Pirko 115793f4014SJiri Pirko spin_lock_bh(&deferred_lock); 116793f4014SJiri Pirko if (list_empty(&deferred)) { 117793f4014SJiri Pirko dfitem = NULL; 118793f4014SJiri Pirko goto unlock; 119793f4014SJiri Pirko } 120793f4014SJiri Pirko dfitem = list_first_entry(&deferred, 121793f4014SJiri Pirko struct switchdev_deferred_item, list); 122793f4014SJiri Pirko list_del(&dfitem->list); 123793f4014SJiri Pirko unlock: 124793f4014SJiri Pirko spin_unlock_bh(&deferred_lock); 125793f4014SJiri Pirko return dfitem; 126793f4014SJiri Pirko } 127793f4014SJiri Pirko 128793f4014SJiri Pirko /** 129793f4014SJiri Pirko * switchdev_deferred_process - Process ops in deferred queue 130793f4014SJiri Pirko * 131793f4014SJiri Pirko * Called to flush the ops currently queued in deferred ops queue. 132793f4014SJiri Pirko * rtnl_lock must be held. 133793f4014SJiri Pirko */ 134793f4014SJiri Pirko void switchdev_deferred_process(void) 135793f4014SJiri Pirko { 136793f4014SJiri Pirko struct switchdev_deferred_item *dfitem; 137793f4014SJiri Pirko 138793f4014SJiri Pirko ASSERT_RTNL(); 139793f4014SJiri Pirko 140793f4014SJiri Pirko while ((dfitem = switchdev_deferred_dequeue())) { 141793f4014SJiri Pirko dfitem->func(dfitem->dev, dfitem->data); 142793f4014SJiri Pirko dev_put(dfitem->dev); 143793f4014SJiri Pirko kfree(dfitem); 144793f4014SJiri Pirko } 145793f4014SJiri Pirko } 146793f4014SJiri Pirko EXPORT_SYMBOL_GPL(switchdev_deferred_process); 147793f4014SJiri Pirko 148793f4014SJiri Pirko static void switchdev_deferred_process_work(struct work_struct *work) 149793f4014SJiri Pirko { 150793f4014SJiri Pirko rtnl_lock(); 151793f4014SJiri Pirko switchdev_deferred_process(); 152793f4014SJiri Pirko rtnl_unlock(); 153793f4014SJiri Pirko } 154793f4014SJiri Pirko 155793f4014SJiri Pirko static DECLARE_WORK(deferred_process_work, switchdev_deferred_process_work); 156793f4014SJiri Pirko 157793f4014SJiri Pirko static int switchdev_deferred_enqueue(struct net_device *dev, 158793f4014SJiri Pirko const void *data, size_t data_len, 159793f4014SJiri Pirko switchdev_deferred_func_t *func) 160793f4014SJiri Pirko { 161793f4014SJiri Pirko struct switchdev_deferred_item *dfitem; 162793f4014SJiri Pirko 163793f4014SJiri Pirko dfitem = kmalloc(sizeof(*dfitem) + data_len, GFP_ATOMIC); 164793f4014SJiri Pirko if (!dfitem) 165793f4014SJiri Pirko return -ENOMEM; 166793f4014SJiri Pirko dfitem->dev = dev; 167793f4014SJiri Pirko dfitem->func = func; 168793f4014SJiri Pirko memcpy(dfitem->data, data, data_len); 169793f4014SJiri Pirko dev_hold(dev); 170793f4014SJiri Pirko spin_lock_bh(&deferred_lock); 171793f4014SJiri Pirko list_add_tail(&dfitem->list, &deferred); 172793f4014SJiri Pirko spin_unlock_bh(&deferred_lock); 173793f4014SJiri Pirko schedule_work(&deferred_process_work); 174793f4014SJiri Pirko return 0; 175793f4014SJiri Pirko } 176793f4014SJiri Pirko 177*d45224d6SFlorian Fainelli static int switchdev_port_attr_notify(enum switchdev_notifier_type nt, 178*d45224d6SFlorian Fainelli struct net_device *dev, 179f7fadf30SJiri Pirko const struct switchdev_attr *attr, 1807ea6eb3fSJiri Pirko struct switchdev_trans *trans) 1813094333dSScott Feldman { 182*d45224d6SFlorian Fainelli int err; 183*d45224d6SFlorian Fainelli int rc; 1843094333dSScott Feldman 185*d45224d6SFlorian Fainelli struct switchdev_notifier_port_attr_info attr_info = { 186*d45224d6SFlorian Fainelli .attr = attr, 187*d45224d6SFlorian Fainelli .trans = trans, 188*d45224d6SFlorian Fainelli .handled = false, 189*d45224d6SFlorian Fainelli }; 1903094333dSScott Feldman 191*d45224d6SFlorian Fainelli rc = call_switchdev_blocking_notifiers(nt, dev, 192*d45224d6SFlorian Fainelli &attr_info.info, NULL); 193*d45224d6SFlorian Fainelli err = notifier_to_errno(rc); 194*d45224d6SFlorian Fainelli if (err) { 195*d45224d6SFlorian Fainelli WARN_ON(!attr_info.handled); 1963094333dSScott Feldman return err; 1973094333dSScott Feldman } 1983094333dSScott Feldman 199*d45224d6SFlorian Fainelli if (!attr_info.handled) 200*d45224d6SFlorian Fainelli return -EOPNOTSUPP; 201*d45224d6SFlorian Fainelli 202*d45224d6SFlorian Fainelli return 0; 203*d45224d6SFlorian Fainelli } 204*d45224d6SFlorian Fainelli 2050bc05d58SJiri Pirko static int switchdev_port_attr_set_now(struct net_device *dev, 206f7fadf30SJiri Pirko const struct switchdev_attr *attr) 2073094333dSScott Feldman { 2087ea6eb3fSJiri Pirko struct switchdev_trans trans; 2093094333dSScott Feldman int err; 2103094333dSScott Feldman 2117ea6eb3fSJiri Pirko switchdev_trans_init(&trans); 2127ea6eb3fSJiri Pirko 2133094333dSScott Feldman /* Phase I: prepare for attr set. Driver/device should fail 2143094333dSScott Feldman * here if there are going to be issues in the commit phase, 2153094333dSScott Feldman * such as lack of resources or support. The driver/device 2163094333dSScott Feldman * should reserve resources needed for the commit phase here, 2173094333dSScott Feldman * but should not commit the attr. 2183094333dSScott Feldman */ 2193094333dSScott Feldman 220f623ab7fSJiri Pirko trans.ph_prepare = true; 221*d45224d6SFlorian Fainelli err = switchdev_port_attr_notify(SWITCHDEV_PORT_ATTR_SET, dev, attr, 222*d45224d6SFlorian Fainelli &trans); 2233094333dSScott Feldman if (err) { 2243094333dSScott Feldman /* Prepare phase failed: abort the transaction. Any 2253094333dSScott Feldman * resources reserved in the prepare phase are 2263094333dSScott Feldman * released. 2273094333dSScott Feldman */ 2283094333dSScott Feldman 2299f6467cfSJiri Pirko if (err != -EOPNOTSUPP) 2307ea6eb3fSJiri Pirko switchdev_trans_items_destroy(&trans); 2313094333dSScott Feldman 2323094333dSScott Feldman return err; 2333094333dSScott Feldman } 2343094333dSScott Feldman 2353094333dSScott Feldman /* Phase II: commit attr set. This cannot fail as a fault 2363094333dSScott Feldman * of driver/device. If it does, it's a bug in the driver/device 2373094333dSScott Feldman * because the driver said everythings was OK in phase I. 2383094333dSScott Feldman */ 2393094333dSScott Feldman 240f623ab7fSJiri Pirko trans.ph_prepare = false; 241*d45224d6SFlorian Fainelli err = switchdev_port_attr_notify(SWITCHDEV_PORT_ATTR_SET, dev, attr, 242*d45224d6SFlorian Fainelli &trans); 243e9fdaec0SScott Feldman WARN(err, "%s: Commit of attribute (id=%d) failed.\n", 244e9fdaec0SScott Feldman dev->name, attr->id); 2457ea6eb3fSJiri Pirko switchdev_trans_items_warn_destroy(dev, &trans); 2463094333dSScott Feldman 2473094333dSScott Feldman return err; 2483094333dSScott Feldman } 2490bc05d58SJiri Pirko 2500bc05d58SJiri Pirko static void switchdev_port_attr_set_deferred(struct net_device *dev, 2510bc05d58SJiri Pirko const void *data) 2520bc05d58SJiri Pirko { 2530bc05d58SJiri Pirko const struct switchdev_attr *attr = data; 2540bc05d58SJiri Pirko int err; 2550bc05d58SJiri Pirko 2560bc05d58SJiri Pirko err = switchdev_port_attr_set_now(dev, attr); 2570bc05d58SJiri Pirko if (err && err != -EOPNOTSUPP) 2580bc05d58SJiri Pirko netdev_err(dev, "failed (err=%d) to set attribute (id=%d)\n", 2590bc05d58SJiri Pirko err, attr->id); 2607ceb2afbSElad Raz if (attr->complete) 2617ceb2afbSElad Raz attr->complete(dev, err, attr->complete_priv); 2620bc05d58SJiri Pirko } 2630bc05d58SJiri Pirko 2640bc05d58SJiri Pirko static int switchdev_port_attr_set_defer(struct net_device *dev, 2650bc05d58SJiri Pirko const struct switchdev_attr *attr) 2660bc05d58SJiri Pirko { 2670bc05d58SJiri Pirko return switchdev_deferred_enqueue(dev, attr, sizeof(*attr), 2680bc05d58SJiri Pirko switchdev_port_attr_set_deferred); 2690bc05d58SJiri Pirko } 2700bc05d58SJiri Pirko 2710bc05d58SJiri Pirko /** 2720bc05d58SJiri Pirko * switchdev_port_attr_set - Set port attribute 2730bc05d58SJiri Pirko * 2740bc05d58SJiri Pirko * @dev: port device 2750bc05d58SJiri Pirko * @attr: attribute to set 2760bc05d58SJiri Pirko * 2770bc05d58SJiri Pirko * Use a 2-phase prepare-commit transaction model to ensure 2780bc05d58SJiri Pirko * system is not left in a partially updated state due to 2790bc05d58SJiri Pirko * failure from driver/device. 2800bc05d58SJiri Pirko * 2810bc05d58SJiri Pirko * rtnl_lock must be held and must not be in atomic section, 2820bc05d58SJiri Pirko * in case SWITCHDEV_F_DEFER flag is not set. 2830bc05d58SJiri Pirko */ 2840bc05d58SJiri Pirko int switchdev_port_attr_set(struct net_device *dev, 2850bc05d58SJiri Pirko const struct switchdev_attr *attr) 2860bc05d58SJiri Pirko { 2870bc05d58SJiri Pirko if (attr->flags & SWITCHDEV_F_DEFER) 2880bc05d58SJiri Pirko return switchdev_port_attr_set_defer(dev, attr); 2890bc05d58SJiri Pirko ASSERT_RTNL(); 2900bc05d58SJiri Pirko return switchdev_port_attr_set_now(dev, attr); 2910bc05d58SJiri Pirko } 2923094333dSScott Feldman EXPORT_SYMBOL_GPL(switchdev_port_attr_set); 2933094333dSScott Feldman 294e258d919SScott Feldman static size_t switchdev_obj_size(const struct switchdev_obj *obj) 295e258d919SScott Feldman { 296e258d919SScott Feldman switch (obj->id) { 297e258d919SScott Feldman case SWITCHDEV_OBJ_ID_PORT_VLAN: 298e258d919SScott Feldman return sizeof(struct switchdev_obj_port_vlan); 2994d41e125SElad Raz case SWITCHDEV_OBJ_ID_PORT_MDB: 3004d41e125SElad Raz return sizeof(struct switchdev_obj_port_mdb); 30147d5b6dbSAndrew Lunn case SWITCHDEV_OBJ_ID_HOST_MDB: 30247d5b6dbSAndrew Lunn return sizeof(struct switchdev_obj_port_mdb); 303e258d919SScott Feldman default: 304e258d919SScott Feldman BUG(); 305e258d919SScott Feldman } 306e258d919SScott Feldman return 0; 307e258d919SScott Feldman } 308e258d919SScott Feldman 309d17d9f5eSPetr Machata static int switchdev_port_obj_notify(enum switchdev_notifier_type nt, 310d17d9f5eSPetr Machata struct net_device *dev, 311648b4a99SJiri Pirko const struct switchdev_obj *obj, 31269b7320eSPetr Machata struct switchdev_trans *trans, 31369b7320eSPetr Machata struct netlink_ext_ack *extack) 314491d0f15SScott Feldman { 315d17d9f5eSPetr Machata int rc; 316d17d9f5eSPetr Machata int err; 317491d0f15SScott Feldman 318d17d9f5eSPetr Machata struct switchdev_notifier_port_obj_info obj_info = { 319d17d9f5eSPetr Machata .obj = obj, 320d17d9f5eSPetr Machata .trans = trans, 321d17d9f5eSPetr Machata .handled = false, 322d17d9f5eSPetr Machata }; 323491d0f15SScott Feldman 324479c86dcSPetr Machata rc = call_switchdev_blocking_notifiers(nt, dev, &obj_info.info, extack); 325d17d9f5eSPetr Machata err = notifier_to_errno(rc); 326d17d9f5eSPetr Machata if (err) { 327d17d9f5eSPetr Machata WARN_ON(!obj_info.handled); 328491d0f15SScott Feldman return err; 329491d0f15SScott Feldman } 330d17d9f5eSPetr Machata if (!obj_info.handled) 331d17d9f5eSPetr Machata return -EOPNOTSUPP; 332d17d9f5eSPetr Machata return 0; 333d17d9f5eSPetr Machata } 334491d0f15SScott Feldman 3354d429c5dSJiri Pirko static int switchdev_port_obj_add_now(struct net_device *dev, 33669b7320eSPetr Machata const struct switchdev_obj *obj, 33769b7320eSPetr Machata struct netlink_ext_ack *extack) 338491d0f15SScott Feldman { 3397ea6eb3fSJiri Pirko struct switchdev_trans trans; 340491d0f15SScott Feldman int err; 341491d0f15SScott Feldman 342491d0f15SScott Feldman ASSERT_RTNL(); 343491d0f15SScott Feldman 3447ea6eb3fSJiri Pirko switchdev_trans_init(&trans); 3457ea6eb3fSJiri Pirko 346491d0f15SScott Feldman /* Phase I: prepare for obj add. Driver/device should fail 347491d0f15SScott Feldman * here if there are going to be issues in the commit phase, 348491d0f15SScott Feldman * such as lack of resources or support. The driver/device 349491d0f15SScott Feldman * should reserve resources needed for the commit phase here, 350491d0f15SScott Feldman * but should not commit the obj. 351491d0f15SScott Feldman */ 352491d0f15SScott Feldman 353f623ab7fSJiri Pirko trans.ph_prepare = true; 354d17d9f5eSPetr Machata err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD, 35569b7320eSPetr Machata dev, obj, &trans, extack); 356491d0f15SScott Feldman if (err) { 357491d0f15SScott Feldman /* Prepare phase failed: abort the transaction. Any 358491d0f15SScott Feldman * resources reserved in the prepare phase are 359491d0f15SScott Feldman * released. 360491d0f15SScott Feldman */ 361491d0f15SScott Feldman 3629f6467cfSJiri Pirko if (err != -EOPNOTSUPP) 3637ea6eb3fSJiri Pirko switchdev_trans_items_destroy(&trans); 364491d0f15SScott Feldman 365491d0f15SScott Feldman return err; 366491d0f15SScott Feldman } 367491d0f15SScott Feldman 368491d0f15SScott Feldman /* Phase II: commit obj add. This cannot fail as a fault 369491d0f15SScott Feldman * of driver/device. If it does, it's a bug in the driver/device 370491d0f15SScott Feldman * because the driver said everythings was OK in phase I. 371491d0f15SScott Feldman */ 372491d0f15SScott Feldman 373f623ab7fSJiri Pirko trans.ph_prepare = false; 374d17d9f5eSPetr Machata err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD, 37569b7320eSPetr Machata dev, obj, &trans, extack); 3769e8f4a54SJiri Pirko WARN(err, "%s: Commit of object (id=%d) failed.\n", dev->name, obj->id); 3777ea6eb3fSJiri Pirko switchdev_trans_items_warn_destroy(dev, &trans); 378491d0f15SScott Feldman 379491d0f15SScott Feldman return err; 380491d0f15SScott Feldman } 3814d429c5dSJiri Pirko 3824d429c5dSJiri Pirko static void switchdev_port_obj_add_deferred(struct net_device *dev, 3834d429c5dSJiri Pirko const void *data) 3844d429c5dSJiri Pirko { 3854d429c5dSJiri Pirko const struct switchdev_obj *obj = data; 3864d429c5dSJiri Pirko int err; 3874d429c5dSJiri Pirko 38869b7320eSPetr Machata err = switchdev_port_obj_add_now(dev, obj, NULL); 3894d429c5dSJiri Pirko if (err && err != -EOPNOTSUPP) 3904d429c5dSJiri Pirko netdev_err(dev, "failed (err=%d) to add object (id=%d)\n", 3914d429c5dSJiri Pirko err, obj->id); 3927ceb2afbSElad Raz if (obj->complete) 3937ceb2afbSElad Raz obj->complete(dev, err, obj->complete_priv); 3944d429c5dSJiri Pirko } 3954d429c5dSJiri Pirko 3964d429c5dSJiri Pirko static int switchdev_port_obj_add_defer(struct net_device *dev, 3974d429c5dSJiri Pirko const struct switchdev_obj *obj) 3984d429c5dSJiri Pirko { 399e258d919SScott Feldman return switchdev_deferred_enqueue(dev, obj, switchdev_obj_size(obj), 4004d429c5dSJiri Pirko switchdev_port_obj_add_deferred); 4014d429c5dSJiri Pirko } 402491d0f15SScott Feldman 403491d0f15SScott Feldman /** 4044d429c5dSJiri Pirko * switchdev_port_obj_add - Add port object 405491d0f15SScott Feldman * 406491d0f15SScott Feldman * @dev: port device 407ab069002SVivien Didelot * @id: object ID 4084d429c5dSJiri Pirko * @obj: object to add 4094d429c5dSJiri Pirko * 4104d429c5dSJiri Pirko * Use a 2-phase prepare-commit transaction model to ensure 4114d429c5dSJiri Pirko * system is not left in a partially updated state due to 4124d429c5dSJiri Pirko * failure from driver/device. 4134d429c5dSJiri Pirko * 4144d429c5dSJiri Pirko * rtnl_lock must be held and must not be in atomic section, 4154d429c5dSJiri Pirko * in case SWITCHDEV_F_DEFER flag is not set. 416491d0f15SScott Feldman */ 4174d429c5dSJiri Pirko int switchdev_port_obj_add(struct net_device *dev, 41869b7320eSPetr Machata const struct switchdev_obj *obj, 41969b7320eSPetr Machata struct netlink_ext_ack *extack) 4204d429c5dSJiri Pirko { 4214d429c5dSJiri Pirko if (obj->flags & SWITCHDEV_F_DEFER) 4224d429c5dSJiri Pirko return switchdev_port_obj_add_defer(dev, obj); 4234d429c5dSJiri Pirko ASSERT_RTNL(); 42469b7320eSPetr Machata return switchdev_port_obj_add_now(dev, obj, extack); 4254d429c5dSJiri Pirko } 4264d429c5dSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_port_obj_add); 4274d429c5dSJiri Pirko 4284d429c5dSJiri Pirko static int switchdev_port_obj_del_now(struct net_device *dev, 429648b4a99SJiri Pirko const struct switchdev_obj *obj) 430491d0f15SScott Feldman { 431d17d9f5eSPetr Machata return switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_DEL, 43269b7320eSPetr Machata dev, obj, NULL, NULL); 433491d0f15SScott Feldman } 4344d429c5dSJiri Pirko 4354d429c5dSJiri Pirko static void switchdev_port_obj_del_deferred(struct net_device *dev, 4364d429c5dSJiri Pirko const void *data) 4374d429c5dSJiri Pirko { 4384d429c5dSJiri Pirko const struct switchdev_obj *obj = data; 4394d429c5dSJiri Pirko int err; 4404d429c5dSJiri Pirko 4414d429c5dSJiri Pirko err = switchdev_port_obj_del_now(dev, obj); 4424d429c5dSJiri Pirko if (err && err != -EOPNOTSUPP) 4434d429c5dSJiri Pirko netdev_err(dev, "failed (err=%d) to del object (id=%d)\n", 4444d429c5dSJiri Pirko err, obj->id); 4457ceb2afbSElad Raz if (obj->complete) 4467ceb2afbSElad Raz obj->complete(dev, err, obj->complete_priv); 4474d429c5dSJiri Pirko } 4484d429c5dSJiri Pirko 4494d429c5dSJiri Pirko static int switchdev_port_obj_del_defer(struct net_device *dev, 4504d429c5dSJiri Pirko const struct switchdev_obj *obj) 4514d429c5dSJiri Pirko { 452e258d919SScott Feldman return switchdev_deferred_enqueue(dev, obj, switchdev_obj_size(obj), 4534d429c5dSJiri Pirko switchdev_port_obj_del_deferred); 4544d429c5dSJiri Pirko } 4554d429c5dSJiri Pirko 4564d429c5dSJiri Pirko /** 4574d429c5dSJiri Pirko * switchdev_port_obj_del - Delete port object 4584d429c5dSJiri Pirko * 4594d429c5dSJiri Pirko * @dev: port device 4604d429c5dSJiri Pirko * @id: object ID 4614d429c5dSJiri Pirko * @obj: object to delete 4624d429c5dSJiri Pirko * 4634d429c5dSJiri Pirko * rtnl_lock must be held and must not be in atomic section, 4644d429c5dSJiri Pirko * in case SWITCHDEV_F_DEFER flag is not set. 4654d429c5dSJiri Pirko */ 4664d429c5dSJiri Pirko int switchdev_port_obj_del(struct net_device *dev, 4674d429c5dSJiri Pirko const struct switchdev_obj *obj) 4684d429c5dSJiri Pirko { 4694d429c5dSJiri Pirko if (obj->flags & SWITCHDEV_F_DEFER) 4704d429c5dSJiri Pirko return switchdev_port_obj_del_defer(dev, obj); 4714d429c5dSJiri Pirko ASSERT_RTNL(); 4724d429c5dSJiri Pirko return switchdev_port_obj_del_now(dev, obj); 4734d429c5dSJiri Pirko } 474491d0f15SScott Feldman EXPORT_SYMBOL_GPL(switchdev_port_obj_del); 475491d0f15SScott Feldman 476ff5cf100SArkadi Sharshevsky static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain); 477a93e3b17SPetr Machata static BLOCKING_NOTIFIER_HEAD(switchdev_blocking_notif_chain); 47803bf0c28SJiri Pirko 47903bf0c28SJiri Pirko /** 480ebb9a03aSJiri Pirko * register_switchdev_notifier - Register notifier 48103bf0c28SJiri Pirko * @nb: notifier_block 48203bf0c28SJiri Pirko * 483ff5cf100SArkadi Sharshevsky * Register switch device notifier. 48403bf0c28SJiri Pirko */ 485ebb9a03aSJiri Pirko int register_switchdev_notifier(struct notifier_block *nb) 48603bf0c28SJiri Pirko { 487ff5cf100SArkadi Sharshevsky return atomic_notifier_chain_register(&switchdev_notif_chain, nb); 48803bf0c28SJiri Pirko } 489ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(register_switchdev_notifier); 49003bf0c28SJiri Pirko 49103bf0c28SJiri Pirko /** 492ebb9a03aSJiri Pirko * unregister_switchdev_notifier - Unregister notifier 49303bf0c28SJiri Pirko * @nb: notifier_block 49403bf0c28SJiri Pirko * 49503bf0c28SJiri Pirko * Unregister switch device notifier. 49603bf0c28SJiri Pirko */ 497ebb9a03aSJiri Pirko int unregister_switchdev_notifier(struct notifier_block *nb) 49803bf0c28SJiri Pirko { 499ff5cf100SArkadi Sharshevsky return atomic_notifier_chain_unregister(&switchdev_notif_chain, nb); 50003bf0c28SJiri Pirko } 501ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(unregister_switchdev_notifier); 50203bf0c28SJiri Pirko 50303bf0c28SJiri Pirko /** 504ebb9a03aSJiri Pirko * call_switchdev_notifiers - Call notifiers 50503bf0c28SJiri Pirko * @val: value passed unmodified to notifier function 50603bf0c28SJiri Pirko * @dev: port device 50703bf0c28SJiri Pirko * @info: notifier information data 50803bf0c28SJiri Pirko * 509ff5cf100SArkadi Sharshevsky * Call all network notifier blocks. 51003bf0c28SJiri Pirko */ 511ebb9a03aSJiri Pirko int call_switchdev_notifiers(unsigned long val, struct net_device *dev, 5126685987cSPetr Machata struct switchdev_notifier_info *info, 5136685987cSPetr Machata struct netlink_ext_ack *extack) 51403bf0c28SJiri Pirko { 51503bf0c28SJiri Pirko info->dev = dev; 5166685987cSPetr Machata info->extack = extack; 517ff5cf100SArkadi Sharshevsky return atomic_notifier_call_chain(&switchdev_notif_chain, val, info); 51803bf0c28SJiri Pirko } 519ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(call_switchdev_notifiers); 5208a44dbb2SRoopa Prabhu 521a93e3b17SPetr Machata int register_switchdev_blocking_notifier(struct notifier_block *nb) 522a93e3b17SPetr Machata { 523a93e3b17SPetr Machata struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain; 524a93e3b17SPetr Machata 525a93e3b17SPetr Machata return blocking_notifier_chain_register(chain, nb); 526a93e3b17SPetr Machata } 527a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(register_switchdev_blocking_notifier); 528a93e3b17SPetr Machata 529a93e3b17SPetr Machata int unregister_switchdev_blocking_notifier(struct notifier_block *nb) 530a93e3b17SPetr Machata { 531a93e3b17SPetr Machata struct blocking_notifier_head *chain = &switchdev_blocking_notif_chain; 532a93e3b17SPetr Machata 533a93e3b17SPetr Machata return blocking_notifier_chain_unregister(chain, nb); 534a93e3b17SPetr Machata } 535a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(unregister_switchdev_blocking_notifier); 536a93e3b17SPetr Machata 537a93e3b17SPetr Machata int call_switchdev_blocking_notifiers(unsigned long val, struct net_device *dev, 538479c86dcSPetr Machata struct switchdev_notifier_info *info, 539479c86dcSPetr Machata struct netlink_ext_ack *extack) 540a93e3b17SPetr Machata { 541a93e3b17SPetr Machata info->dev = dev; 542479c86dcSPetr Machata info->extack = extack; 543a93e3b17SPetr Machata return blocking_notifier_call_chain(&switchdev_blocking_notif_chain, 544a93e3b17SPetr Machata val, info); 545a93e3b17SPetr Machata } 546a93e3b17SPetr Machata EXPORT_SYMBOL_GPL(call_switchdev_blocking_notifiers); 547a93e3b17SPetr Machata 548f30f0601SPetr Machata static int __switchdev_handle_port_obj_add(struct net_device *dev, 549f30f0601SPetr Machata struct switchdev_notifier_port_obj_info *port_obj_info, 550f30f0601SPetr Machata bool (*check_cb)(const struct net_device *dev), 551f30f0601SPetr Machata int (*add_cb)(struct net_device *dev, 552f30f0601SPetr Machata const struct switchdev_obj *obj, 55369213513SPetr Machata struct switchdev_trans *trans, 55469213513SPetr Machata struct netlink_ext_ack *extack)) 555f30f0601SPetr Machata { 55669213513SPetr Machata struct netlink_ext_ack *extack; 557f30f0601SPetr Machata struct net_device *lower_dev; 558f30f0601SPetr Machata struct list_head *iter; 559f30f0601SPetr Machata int err = -EOPNOTSUPP; 560f30f0601SPetr Machata 56169213513SPetr Machata extack = switchdev_notifier_info_to_extack(&port_obj_info->info); 56269213513SPetr Machata 563f30f0601SPetr Machata if (check_cb(dev)) { 564f30f0601SPetr Machata /* This flag is only checked if the return value is success. */ 565f30f0601SPetr Machata port_obj_info->handled = true; 56669213513SPetr Machata return add_cb(dev, port_obj_info->obj, port_obj_info->trans, 56769213513SPetr Machata extack); 568f30f0601SPetr Machata } 569f30f0601SPetr Machata 570f30f0601SPetr Machata /* Switch ports might be stacked under e.g. a LAG. Ignore the 571f30f0601SPetr Machata * unsupported devices, another driver might be able to handle them. But 572f30f0601SPetr Machata * propagate to the callers any hard errors. 573f30f0601SPetr Machata * 574f30f0601SPetr Machata * If the driver does its own bookkeeping of stacked ports, it's not 575f30f0601SPetr Machata * necessary to go through this helper. 576f30f0601SPetr Machata */ 577f30f0601SPetr Machata netdev_for_each_lower_dev(dev, lower_dev, iter) { 578f30f0601SPetr Machata err = __switchdev_handle_port_obj_add(lower_dev, port_obj_info, 579f30f0601SPetr Machata check_cb, add_cb); 580f30f0601SPetr Machata if (err && err != -EOPNOTSUPP) 581f30f0601SPetr Machata return err; 582f30f0601SPetr Machata } 583f30f0601SPetr Machata 584f30f0601SPetr Machata return err; 585f30f0601SPetr Machata } 586f30f0601SPetr Machata 587f30f0601SPetr Machata int switchdev_handle_port_obj_add(struct net_device *dev, 588f30f0601SPetr Machata struct switchdev_notifier_port_obj_info *port_obj_info, 589f30f0601SPetr Machata bool (*check_cb)(const struct net_device *dev), 590f30f0601SPetr Machata int (*add_cb)(struct net_device *dev, 591f30f0601SPetr Machata const struct switchdev_obj *obj, 59269213513SPetr Machata struct switchdev_trans *trans, 59369213513SPetr Machata struct netlink_ext_ack *extack)) 594f30f0601SPetr Machata { 595f30f0601SPetr Machata int err; 596f30f0601SPetr Machata 597f30f0601SPetr Machata err = __switchdev_handle_port_obj_add(dev, port_obj_info, check_cb, 598f30f0601SPetr Machata add_cb); 599f30f0601SPetr Machata if (err == -EOPNOTSUPP) 600f30f0601SPetr Machata err = 0; 601f30f0601SPetr Machata return err; 602f30f0601SPetr Machata } 603f30f0601SPetr Machata EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add); 604f30f0601SPetr Machata 605f30f0601SPetr Machata static int __switchdev_handle_port_obj_del(struct net_device *dev, 606f30f0601SPetr Machata struct switchdev_notifier_port_obj_info *port_obj_info, 607f30f0601SPetr Machata bool (*check_cb)(const struct net_device *dev), 608f30f0601SPetr Machata int (*del_cb)(struct net_device *dev, 609f30f0601SPetr Machata const struct switchdev_obj *obj)) 610f30f0601SPetr Machata { 611f30f0601SPetr Machata struct net_device *lower_dev; 612f30f0601SPetr Machata struct list_head *iter; 613f30f0601SPetr Machata int err = -EOPNOTSUPP; 614f30f0601SPetr Machata 615f30f0601SPetr Machata if (check_cb(dev)) { 616f30f0601SPetr Machata /* This flag is only checked if the return value is success. */ 617f30f0601SPetr Machata port_obj_info->handled = true; 618f30f0601SPetr Machata return del_cb(dev, port_obj_info->obj); 619f30f0601SPetr Machata } 620f30f0601SPetr Machata 621f30f0601SPetr Machata /* Switch ports might be stacked under e.g. a LAG. Ignore the 622f30f0601SPetr Machata * unsupported devices, another driver might be able to handle them. But 623f30f0601SPetr Machata * propagate to the callers any hard errors. 624f30f0601SPetr Machata * 625f30f0601SPetr Machata * If the driver does its own bookkeeping of stacked ports, it's not 626f30f0601SPetr Machata * necessary to go through this helper. 627f30f0601SPetr Machata */ 628f30f0601SPetr Machata netdev_for_each_lower_dev(dev, lower_dev, iter) { 629f30f0601SPetr Machata err = __switchdev_handle_port_obj_del(lower_dev, port_obj_info, 630f30f0601SPetr Machata check_cb, del_cb); 631f30f0601SPetr Machata if (err && err != -EOPNOTSUPP) 632f30f0601SPetr Machata return err; 633f30f0601SPetr Machata } 634f30f0601SPetr Machata 635f30f0601SPetr Machata return err; 636f30f0601SPetr Machata } 637f30f0601SPetr Machata 638f30f0601SPetr Machata int switchdev_handle_port_obj_del(struct net_device *dev, 639f30f0601SPetr Machata struct switchdev_notifier_port_obj_info *port_obj_info, 640f30f0601SPetr Machata bool (*check_cb)(const struct net_device *dev), 641f30f0601SPetr Machata int (*del_cb)(struct net_device *dev, 642f30f0601SPetr Machata const struct switchdev_obj *obj)) 643f30f0601SPetr Machata { 644f30f0601SPetr Machata int err; 645f30f0601SPetr Machata 646f30f0601SPetr Machata err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb, 647f30f0601SPetr Machata del_cb); 648f30f0601SPetr Machata if (err == -EOPNOTSUPP) 649f30f0601SPetr Machata err = 0; 650f30f0601SPetr Machata return err; 651f30f0601SPetr Machata } 652f30f0601SPetr Machata EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del); 6531cb33af1SFlorian Fainelli 6541cb33af1SFlorian Fainelli static int __switchdev_handle_port_attr_set(struct net_device *dev, 6551cb33af1SFlorian Fainelli struct switchdev_notifier_port_attr_info *port_attr_info, 6561cb33af1SFlorian Fainelli bool (*check_cb)(const struct net_device *dev), 6571cb33af1SFlorian Fainelli int (*set_cb)(struct net_device *dev, 6581cb33af1SFlorian Fainelli const struct switchdev_attr *attr, 6591cb33af1SFlorian Fainelli struct switchdev_trans *trans)) 6601cb33af1SFlorian Fainelli { 6611cb33af1SFlorian Fainelli struct net_device *lower_dev; 6621cb33af1SFlorian Fainelli struct list_head *iter; 6631cb33af1SFlorian Fainelli int err = -EOPNOTSUPP; 6641cb33af1SFlorian Fainelli 6651cb33af1SFlorian Fainelli if (check_cb(dev)) { 6661cb33af1SFlorian Fainelli port_attr_info->handled = true; 6671cb33af1SFlorian Fainelli return set_cb(dev, port_attr_info->attr, 6681cb33af1SFlorian Fainelli port_attr_info->trans); 6691cb33af1SFlorian Fainelli } 6701cb33af1SFlorian Fainelli 6711cb33af1SFlorian Fainelli /* Switch ports might be stacked under e.g. a LAG. Ignore the 6721cb33af1SFlorian Fainelli * unsupported devices, another driver might be able to handle them. But 6731cb33af1SFlorian Fainelli * propagate to the callers any hard errors. 6741cb33af1SFlorian Fainelli * 6751cb33af1SFlorian Fainelli * If the driver does its own bookkeeping of stacked ports, it's not 6761cb33af1SFlorian Fainelli * necessary to go through this helper. 6771cb33af1SFlorian Fainelli */ 6781cb33af1SFlorian Fainelli netdev_for_each_lower_dev(dev, lower_dev, iter) { 6791cb33af1SFlorian Fainelli err = __switchdev_handle_port_attr_set(lower_dev, port_attr_info, 6801cb33af1SFlorian Fainelli check_cb, set_cb); 6811cb33af1SFlorian Fainelli if (err && err != -EOPNOTSUPP) 6821cb33af1SFlorian Fainelli return err; 6831cb33af1SFlorian Fainelli } 6841cb33af1SFlorian Fainelli 6851cb33af1SFlorian Fainelli return err; 6861cb33af1SFlorian Fainelli } 6871cb33af1SFlorian Fainelli 6881cb33af1SFlorian Fainelli int switchdev_handle_port_attr_set(struct net_device *dev, 6891cb33af1SFlorian Fainelli struct switchdev_notifier_port_attr_info *port_attr_info, 6901cb33af1SFlorian Fainelli bool (*check_cb)(const struct net_device *dev), 6911cb33af1SFlorian Fainelli int (*set_cb)(struct net_device *dev, 6921cb33af1SFlorian Fainelli const struct switchdev_attr *attr, 6931cb33af1SFlorian Fainelli struct switchdev_trans *trans)) 6941cb33af1SFlorian Fainelli { 6951cb33af1SFlorian Fainelli int err; 6961cb33af1SFlorian Fainelli 6971cb33af1SFlorian Fainelli err = __switchdev_handle_port_attr_set(dev, port_attr_info, check_cb, 6981cb33af1SFlorian Fainelli set_cb); 6991cb33af1SFlorian Fainelli if (err == -EOPNOTSUPP) 7001cb33af1SFlorian Fainelli err = 0; 7011cb33af1SFlorian Fainelli return err; 7021cb33af1SFlorian Fainelli } 7031cb33af1SFlorian Fainelli EXPORT_SYMBOL_GPL(switchdev_handle_port_attr_set); 704