diff options
Diffstat (limited to 'drivers/net/ethernet/rocker/rocker_main.c')
-rw-r--r-- | drivers/net/ethernet/rocker/rocker_main.c | 235 |
1 files changed, 155 insertions, 80 deletions
diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index bab13613b138..b1e5c07099fa 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -1557,7 +1557,11 @@ static int rocker_world_port_attr_stp_state_set(struct rocker_port *rocker_port, if (!wops->port_attr_stp_state_set) return -EOPNOTSUPP; - return wops->port_attr_stp_state_set(rocker_port, state, trans); + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + return wops->port_attr_stp_state_set(rocker_port, state); } static int @@ -1569,6 +1573,10 @@ rocker_world_port_attr_bridge_flags_set(struct rocker_port *rocker_port, if (!wops->port_attr_bridge_flags_set) return -EOPNOTSUPP; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + return wops->port_attr_bridge_flags_set(rocker_port, brport_flags, trans); } @@ -1585,6 +1593,20 @@ rocker_world_port_attr_bridge_flags_get(const struct rocker_port *rocker_port, } static int +rocker_world_port_attr_bridge_flags_support_get(const struct rocker_port * + rocker_port, + unsigned long * + p_brport_flags_support) +{ + struct rocker_world_ops *wops = rocker_port->rocker->wops; + + if (!wops->port_attr_bridge_flags_support_get) + return -EOPNOTSUPP; + return wops->port_attr_bridge_flags_support_get(rocker_port, + p_brport_flags_support); +} + +static int rocker_world_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port, u32 ageing_time, struct switchdev_trans *trans) @@ -1594,6 +1616,10 @@ rocker_world_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port, if (!wops->port_attr_bridge_ageing_time_set) return -EOPNOTSUPP; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + return wops->port_attr_bridge_ageing_time_set(rocker_port, ageing_time, trans); } @@ -1607,7 +1633,11 @@ rocker_world_port_obj_vlan_add(struct rocker_port *rocker_port, if (!wops->port_obj_vlan_add) return -EOPNOTSUPP; - return wops->port_obj_vlan_add(rocker_port, vlan, trans); + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + return wops->port_obj_vlan_add(rocker_port, vlan); } static int @@ -1622,50 +1652,26 @@ rocker_world_port_obj_vlan_del(struct rocker_port *rocker_port, } static int -rocker_world_port_obj_vlan_dump(const struct rocker_port *rocker_port, - struct switchdev_obj_port_vlan *vlan, - switchdev_obj_dump_cb_t *cb) -{ - struct rocker_world_ops *wops = rocker_port->rocker->wops; - - if (!wops->port_obj_vlan_dump) - return -EOPNOTSUPP; - return wops->port_obj_vlan_dump(rocker_port, vlan, cb); -} - -static int -rocker_world_port_obj_fdb_add(struct rocker_port *rocker_port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) +rocker_world_port_fdb_add(struct rocker_port *rocker_port, + struct switchdev_notifier_fdb_info *info) { struct rocker_world_ops *wops = rocker_port->rocker->wops; if (!wops->port_obj_fdb_add) return -EOPNOTSUPP; - return wops->port_obj_fdb_add(rocker_port, fdb, trans); -} - -static int -rocker_world_port_obj_fdb_del(struct rocker_port *rocker_port, - const struct switchdev_obj_port_fdb *fdb) -{ - struct rocker_world_ops *wops = rocker_port->rocker->wops; - if (!wops->port_obj_fdb_del) - return -EOPNOTSUPP; - return wops->port_obj_fdb_del(rocker_port, fdb); + return wops->port_obj_fdb_add(rocker_port, info->vid, info->addr); } static int -rocker_world_port_obj_fdb_dump(const struct rocker_port *rocker_port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) +rocker_world_port_fdb_del(struct rocker_port *rocker_port, + struct switchdev_notifier_fdb_info *info) { struct rocker_world_ops *wops = rocker_port->rocker->wops; - if (!wops->port_obj_fdb_dump) + if (!wops->port_obj_fdb_del) return -EOPNOTSUPP; - return wops->port_obj_fdb_dump(rocker_port, fdb, cb); + return wops->port_obj_fdb_del(rocker_port, info->vid, info->addr); } static int rocker_world_port_master_linked(struct rocker_port *rocker_port, @@ -2022,12 +2028,6 @@ static const struct net_device_ops rocker_port_netdev_ops = { .ndo_start_xmit = rocker_port_xmit, .ndo_set_mac_address = rocker_port_set_mac_address, .ndo_change_mtu = rocker_port_change_mtu, - .ndo_bridge_getlink = switchdev_port_bridge_getlink, - .ndo_bridge_setlink = switchdev_port_bridge_setlink, - .ndo_bridge_dellink = switchdev_port_bridge_dellink, - .ndo_fdb_add = switchdev_port_fdb_add, - .ndo_fdb_del = switchdev_port_fdb_del, - .ndo_fdb_dump = switchdev_port_fdb_dump, .ndo_get_phys_port_name = rocker_port_get_phys_port_name, .ndo_change_proto_down = rocker_port_change_proto_down, .ndo_neigh_destroy = rocker_port_neigh_destroy, @@ -2053,6 +2053,10 @@ static int rocker_port_attr_get(struct net_device *dev, err = rocker_world_port_attr_bridge_flags_get(rocker_port, &attr->u.brport_flags); break; + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT: + err = rocker_world_port_attr_bridge_flags_support_get(rocker_port, + &attr->u.brport_flags_support); + break; default: return -EOPNOTSUPP; } @@ -2104,11 +2108,6 @@ static int rocker_port_obj_add(struct net_device *dev, SWITCHDEV_OBJ_PORT_VLAN(obj), trans); break; - case SWITCHDEV_OBJ_ID_PORT_FDB: - err = rocker_world_port_obj_fdb_add(rocker_port, - SWITCHDEV_OBJ_PORT_FDB(obj), - trans); - break; default: err = -EOPNOTSUPP; break; @@ -2128,36 +2127,6 @@ static int rocker_port_obj_del(struct net_device *dev, err = rocker_world_port_obj_vlan_del(rocker_port, SWITCHDEV_OBJ_PORT_VLAN(obj)); break; - case SWITCHDEV_OBJ_ID_PORT_FDB: - err = rocker_world_port_obj_fdb_del(rocker_port, - SWITCHDEV_OBJ_PORT_FDB(obj)); - break; - default: - err = -EOPNOTSUPP; - break; - } - - return err; -} - -static int rocker_port_obj_dump(struct net_device *dev, - struct switchdev_obj *obj, - switchdev_obj_dump_cb_t *cb) -{ - const struct rocker_port *rocker_port = netdev_priv(dev); - int err = 0; - - switch (obj->id) { - case SWITCHDEV_OBJ_ID_PORT_FDB: - err = rocker_world_port_obj_fdb_dump(rocker_port, - SWITCHDEV_OBJ_PORT_FDB(obj), - cb); - break; - case SWITCHDEV_OBJ_ID_PORT_VLAN: - err = rocker_world_port_obj_vlan_dump(rocker_port, - SWITCHDEV_OBJ_PORT_VLAN(obj), - cb); - break; default: err = -EOPNOTSUPP; break; @@ -2171,7 +2140,6 @@ static const struct switchdev_ops rocker_port_switchdev_ops = { .switchdev_port_attr_set = rocker_port_attr_set, .switchdev_port_obj_add = rocker_port_obj_add, .switchdev_port_obj_del = rocker_port_obj_del, - .switchdev_port_obj_dump = rocker_port_obj_dump, }; struct rocker_fib_event_work { @@ -2729,6 +2697,109 @@ static void rocker_msix_fini(const struct rocker *rocker) kfree(rocker->msix_entries); } +static bool rocker_port_dev_check(const struct net_device *dev) +{ + return dev->netdev_ops == &rocker_port_netdev_ops; +} + +struct rocker_switchdev_event_work { + struct work_struct work; + struct switchdev_notifier_fdb_info fdb_info; + struct rocker_port *rocker_port; + unsigned long event; +}; + +static void +rocker_fdb_offload_notify(struct rocker_port *rocker_port, + struct switchdev_notifier_fdb_info *recv_info) +{ + struct switchdev_notifier_fdb_info info; + + info.addr = recv_info->addr; + info.vid = recv_info->vid; + call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, + rocker_port->dev, &info.info); +} + +static void rocker_switchdev_event_work(struct work_struct *work) +{ + struct rocker_switchdev_event_work *switchdev_work = + container_of(work, struct rocker_switchdev_event_work, work); + struct rocker_port *rocker_port = switchdev_work->rocker_port; + struct switchdev_notifier_fdb_info *fdb_info; + int err; + + rtnl_lock(); + switch (switchdev_work->event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + fdb_info = &switchdev_work->fdb_info; + err = rocker_world_port_fdb_add(rocker_port, fdb_info); + if (err) { + netdev_dbg(rocker_port->dev, "fdb add failed err=%d\n", err); + break; + } + rocker_fdb_offload_notify(rocker_port, fdb_info); + break; + case SWITCHDEV_FDB_DEL_TO_DEVICE: + fdb_info = &switchdev_work->fdb_info; + err = rocker_world_port_fdb_del(rocker_port, fdb_info); + if (err) + netdev_dbg(rocker_port->dev, "fdb add failed err=%d\n", err); + break; + } + rtnl_unlock(); + + kfree(switchdev_work->fdb_info.addr); + kfree(switchdev_work); + dev_put(rocker_port->dev); +} + +/* called under rcu_read_lock() */ +static int rocker_switchdev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev = switchdev_notifier_info_to_dev(ptr); + struct rocker_switchdev_event_work *switchdev_work; + struct switchdev_notifier_fdb_info *fdb_info = ptr; + struct rocker_port *rocker_port; + + if (!rocker_port_dev_check(dev)) + return NOTIFY_DONE; + + rocker_port = netdev_priv(dev); + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); + if (WARN_ON(!switchdev_work)) + return NOTIFY_BAD; + + INIT_WORK(&switchdev_work->work, rocker_switchdev_event_work); + switchdev_work->rocker_port = rocker_port; + switchdev_work->event = event; + + switch (event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */ + case SWITCHDEV_FDB_DEL_TO_DEVICE: + memcpy(&switchdev_work->fdb_info, ptr, + sizeof(switchdev_work->fdb_info)); + switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); + ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, + fdb_info->addr); + /* Take a reference on the rocker device */ + dev_hold(dev); + break; + default: + kfree(switchdev_work); + return NOTIFY_DONE; + } + + queue_work(rocker_port->rocker->rocker_owq, + &switchdev_work->work); + return NOTIFY_DONE; +} + +static struct notifier_block rocker_switchdev_notifier = { + .notifier_call = rocker_switchdev_event, +}; + static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct rocker *rocker; @@ -2834,6 +2905,12 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err) goto err_register_fib_notifier; + err = register_switchdev_notifier(&rocker_switchdev_notifier); + if (err) { + dev_err(&pdev->dev, "Failed to register switchdev notifier\n"); + goto err_register_switchdev_notifier; + } + rocker->hw.id = rocker_read64(rocker, SWITCH_ID); err = rocker_probe_ports(rocker); @@ -2848,6 +2925,8 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; err_probe_ports: + unregister_switchdev_notifier(&rocker_switchdev_notifier); +err_register_switchdev_notifier: unregister_fib_notifier(&rocker->fib_nb); err_register_fib_notifier: destroy_workqueue(rocker->rocker_owq); @@ -2878,6 +2957,7 @@ static void rocker_remove(struct pci_dev *pdev) struct rocker *rocker = pci_get_drvdata(pdev); rocker_remove_ports(rocker); + unregister_switchdev_notifier(&rocker_switchdev_notifier); unregister_fib_notifier(&rocker->fib_nb); rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET); destroy_workqueue(rocker->rocker_owq); @@ -2902,11 +2982,6 @@ static struct pci_driver rocker_pci_driver = { * Net device notifier event handler ************************************/ -static bool rocker_port_dev_check(const struct net_device *dev) -{ - return dev->netdev_ops == &rocker_port_netdev_ops; -} - static bool rocker_port_dev_check_under(const struct net_device *dev, struct rocker *rocker) { |