From: Jiri Pirko <j...@mellanox.com> This is another step on the way to per-world clean cut. Introduce world ops hooks which each world can implement in world-specific way. Also introduce world infrastructure including function for port worlds change.
Signed-off-by: Jiri Pirko <j...@mellanox.com> --- drivers/net/ethernet/rocker/rocker.h | 56 ++++ drivers/net/ethernet/rocker/rocker_main.c | 508 +++++++++++++++++++++++++++++- 2 files changed, 563 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/rocker/rocker.h b/drivers/net/ethernet/rocker/rocker.h index 650caa0..d49bc5d 100644 --- a/drivers/net/ethernet/rocker/rocker.h +++ b/drivers/net/ethernet/rocker/rocker.h @@ -12,7 +12,11 @@ #ifndef _ROCKER_H #define _ROCKER_H +#include <linux/kernel.h> #include <linux/types.h> +#include <linux/netdevice.h> +#include <net/neighbour.h> +#include <net/switchdev.h> #include "rocker_hw.h" @@ -24,4 +28,56 @@ struct rocker_desc_info { dma_addr_t mapaddr; }; +struct rocker; +struct rocker_port; + +struct rocker_world_ops { + const char *kind; + size_t priv_size; + size_t port_priv_size; + u8 mode; + int (*init)(struct rocker *rocker, void *priv); + void (*fini)(void *priv); + int (*port_init)(struct rocker_port *rocker_port, void *priv, + void *port_priv); + void (*port_fini)(void *port_priv); + int (*port_open)(void *port_priv); + void (*port_stop)(void *port_priv); + int (*port_attr_stp_state_set)(void *port_priv, u8 state, + struct switchdev_trans *trans); + int (*port_attr_bridge_flags_set)(void *port_priv, + unsigned long brport_flags, + struct switchdev_trans *trans); + int (*port_attr_bridge_flags_get)(void *port_priv, + unsigned long *p_brport_flags); + int (*port_obj_vlan_add)(void *port_priv, + const struct switchdev_obj_port_vlan *vlan, + struct switchdev_trans *trans); + int (*port_obj_vlan_del)(void *port_priv, + const struct switchdev_obj_port_vlan *vlan); + int (*port_obj_vlan_dump)(void *port_priv, + struct switchdev_obj_port_vlan *vlan, + switchdev_obj_dump_cb_t *cb); + int (*port_obj_fib4_add)(void *port_priv, + const struct switchdev_obj_ipv4_fib *fib4, + struct switchdev_trans *trans); + int (*port_obj_fib4_del)(void *port_priv, + const struct switchdev_obj_ipv4_fib *fib4); + int (*port_obj_fdb_add)(void *port_priv, + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans); + int (*port_obj_fdb_del)(void *port_priv, + const struct switchdev_obj_port_fdb *fdb); + int (*port_obj_fdb_dump)(void *port_priv, + struct switchdev_obj_port_fdb *fdb, + switchdev_obj_dump_cb_t *cb); + int (*port_master_linked)(void *port_priv, struct net_device *master); + int (*port_master_unlinked)(void *port_priv, struct net_device *master); + int (*port_neigh_update)(void *port_priv, struct neighbour *n); + int (*port_neigh_destroy)(void *port_priv, struct neighbour *n); + int (*port_ev_mac_vlan_seen)(void *port_priv, + const unsigned char *addr, + __be16 vlan_id); +}; + #endif diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index 54d2f46..b9b3c9d 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -210,6 +210,8 @@ struct rocker_port { struct net_device *dev; struct net_device *bridge_dev; struct rocker *rocker; + struct rocker_world *world; + void *world_port_priv; unsigned int port_number; u32 pport; __be16 internal_vlan_id; @@ -236,6 +238,7 @@ struct rocker { spinlock_t cmd_ring_lock; /* for cmd ring accesses */ struct rocker_dma_ring_info cmd_ring; struct rocker_dma_ring_info event_ring; + struct list_head worlds; DECLARE_HASHTABLE(flow_tbl, 16); spinlock_t flow_tbl_lock; /* for flow tbl accesses */ u64 flow_tbl_next_cookie; @@ -1236,6 +1239,9 @@ static int rocker_port_fdb(struct rocker_port *rocker_port, struct switchdev_trans *trans, const unsigned char *addr, __be16 vlan_id, int flags); +static int rocker_world_port_ev_mac_vlan_seen(struct rocker_port *rocker_port, + const unsigned char *addr, + __be16 vlan_id); static int rocker_event_mac_vlan_seen(const struct rocker *rocker, const struct rocker_tlv *info) @@ -1246,6 +1252,7 @@ static int rocker_event_mac_vlan_seen(const struct rocker *rocker, const unsigned char *addr; int flags = ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_LEARNED; __be16 vlan_id; + int err; rocker_tlv_parse_nested(attrs, ROCKER_TLV_EVENT_MAC_VLAN_MAX, info); if (!attrs[ROCKER_TLV_EVENT_MAC_VLAN_PPORT] || @@ -1262,6 +1269,10 @@ static int rocker_event_mac_vlan_seen(const struct rocker *rocker, rocker_port = rocker->ports[port_number]; + err = rocker_world_port_ev_mac_vlan_seen(rocker_port, addr, vlan_id); + if (err) + return err; + if (rocker_port->stp_state != BR_STATE_LEARNING && rocker_port->stp_state != BR_STATE_FORWARDING) return 0; @@ -2155,6 +2166,416 @@ static int rocker_cmd_group_tbl_del(const struct rocker_port *rocker_port, return 0; } +/********************** + * Worlds manipulation + **********************/ + +static struct rocker_world_ops *rocker_world_ops[] = { +}; + +#define ROCKER_WORLD_OPS_LEN ARRAY_SIZE(rocker_world_ops) + +static const struct rocker_world_ops *__rocker_world_ops_find(const char *kind) +{ + struct rocker_world_ops *ops; + int i; + + for (i = 0; i < ROCKER_WORLD_OPS_LEN; i++) { + ops = rocker_world_ops[i]; + if (!strcmp(ops->kind, kind)) + return ops; + } + return NULL; +} + +struct rocker_world { + struct list_head list; + const struct rocker_world_ops *ops; + unsigned int ref; + unsigned long priv[0]; +}; + +static struct rocker_world *__rocker_world_find(struct rocker *rocker, + const struct rocker_world_ops *ops) +{ + struct rocker_world *world; + + list_for_each_entry(world, &rocker->worlds, list) { + if (world->ops == ops) + return world; + } + return NULL; +} + +static int rocker_world_init(struct rocker *rocker, struct rocker_world *world) +{ + if (!world->ops->init) + return 0; + return world->ops->init(rocker, world->priv); +} + +static void rocker_world_fini(struct rocker *rocker, struct rocker_world *world) +{ + if (!world->ops->fini) + return; + return world->ops->fini(world->priv); +} + +static int rocker_world_get(struct rocker_port *rocker_port, + const struct rocker_world_ops *ops) +{ + struct rocker *rocker = rocker_port->rocker; + struct rocker_world *world = __rocker_world_find(rocker, ops); + int err; + + if (world) + goto ref_inc; + world = kzalloc(sizeof(*world) + ops->priv_size, GFP_KERNEL); + if (!world) + return -ENOMEM; + world->ops = ops; + + err = rocker_world_init(rocker, world); + if (err) + goto err_world_init; + + list_add_tail(&world->list, &rocker->worlds); +ref_inc: + world->ref++; + rocker_port->world = world; + return 0; + +err_world_init: + kfree(world); + return err; +} + +static void rocker_world_put(struct rocker_port *rocker_port) +{ + struct rocker_world *world = rocker_port->world; + struct rocker *rocker = rocker_port->rocker; + + world->ref--; + if (world->ref) + return; + list_del(&world->list); + rocker_world_fini(rocker, world); + kfree(world); + rocker_port->world = NULL; +} + +static int rocker_world_port_init(struct rocker_port *rocker_port) +{ + struct rocker_world *world = rocker_port->world; + + if (!world->ops->port_init) + return 0; + return world->ops->port_init(rocker_port, world->priv, + rocker_port->world_port_priv); +} + +static void rocker_world_port_fini(struct rocker_port *rocker_port) +{ + struct rocker_world *world = rocker_port->world; + + if (!world->ops->port_fini) + return; + world->ops->port_fini(rocker_port->world_port_priv); +} + +static int rocker_world_port_join(struct rocker_port *rocker_port, + const struct rocker_world_ops *ops) +{ + int err; + + err = rocker_world_get(rocker_port, ops); + if (err) + return err; + rocker_port->world_port_priv = kzalloc(ops->port_priv_size, GFP_KERNEL); + if (!rocker_port->world_port_priv) { + err = -ENOMEM; + goto err_alloc_port_priv; + } + err = rocker_cmd_set_port_settings_mode(rocker_port, ops->mode); + if (err) + goto err_set_mode; + err = rocker_world_port_init(rocker_port); + if (err) + goto err_port_init; + return 0; + +err_port_init: +err_set_mode: + kfree(rocker_port->world_port_priv); +err_alloc_port_priv: + rocker_world_put(rocker_port); + return err; +} + +static void rocker_world_port_leave(struct rocker_port *rocker_port) +{ + rocker_world_port_fini(rocker_port); + kfree(rocker_port->world_port_priv); + rocker_world_put(rocker_port); +} + +static int __rocker_port_change_world(struct rocker_port *rocker_port, + const struct rocker_world_ops *ops) +{ + int err; + + /* Check if mode was previously set and do cleanup if so */ + if (rocker_port->world) + rocker_world_port_leave(rocker_port); + if (ops) { + err = rocker_world_port_join(rocker_port, ops); + if (err) + return err; + } + return 0; +} + +static int rocker_port_change_world(struct rocker_port *rocker_port, + const char *kind) +{ + const struct rocker_world_ops *ops; + int err; + + if (rocker_port->dev->flags & IFF_UP) + return -EBUSY; + + if (rocker_port->world && + strcmp(rocker_port->world->ops->kind, kind) == 0) { + netdev_err(rocker_port->dev, "Unable to change to the same world the port is in\n"); + return -EINVAL; + } + + ops = __rocker_world_ops_find(kind); + if (!ops) { + netdev_err(rocker_port->dev, "World \"%s\" not found\n", kind); + return -EINVAL; + } + + err = __rocker_port_change_world(rocker_port, ops); + if (err) { + netdev_err(rocker_port->dev, "Failed to change to world \"%s\"\n", kind); + return err; + } + + netdev_info(rocker_port->dev, "World changed to \"%s\"\n", kind); + return 0; +} + +static int rocker_world_port_open(struct rocker_port *rocker_port) +{ + struct rocker_world *world = rocker_port->world; + + if (world->ops->port_open) + return 0; + return world->ops->port_open(rocker_port->world_port_priv); +} + +static void rocker_world_port_stop(struct rocker_port *rocker_port) +{ + struct rocker_world *world = rocker_port->world; + + if (world->ops->port_stop) + return; + world->ops->port_stop(rocker_port->world_port_priv); +} + +static int rocker_world_port_attr_stp_state_set(struct rocker_port *rocker_port, + u8 state, + struct switchdev_trans *trans) +{ + struct rocker_world *world = rocker_port->world; + + if (!world->ops->port_attr_stp_state_set) + return 0; + return world->ops->port_attr_stp_state_set(rocker_port->world_port_priv, + state, trans); +} + +static int +rocker_world_port_attr_bridge_flags_set(struct rocker_port *rocker_port, + unsigned long brport_flags, + struct switchdev_trans *trans) +{ + struct rocker_world *world = rocker_port->world; + + if (!world->ops->port_attr_bridge_flags_set) + return 0; + return world->ops->port_attr_bridge_flags_set(rocker_port->world_port_priv, + brport_flags, trans); +} + +static int +rocker_world_port_attr_bridge_flags_get(const struct rocker_port *rocker_port, + unsigned long *p_brport_flags) +{ + struct rocker_world *world = rocker_port->world; + + if (!world->ops->port_attr_bridge_flags_get) + return 0; + return world->ops->port_attr_bridge_flags_get(rocker_port->world_port_priv, + p_brport_flags); +} + +static int +rocker_world_port_obj_vlan_add(struct rocker_port *rocker_port, + const struct switchdev_obj_port_vlan *vlan, + struct switchdev_trans *trans) +{ + struct rocker_world *world = rocker_port->world; + + if (!world->ops->port_obj_vlan_add) + return 0; + return world->ops->port_obj_vlan_add(rocker_port->world_port_priv, + vlan, trans); +} + +static int +rocker_world_port_obj_vlan_del(struct rocker_port *rocker_port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct rocker_world *world = rocker_port->world; + + if (!world->ops->port_obj_vlan_del) + return 0; + return world->ops->port_obj_vlan_del(rocker_port->world_port_priv, + vlan); +} + +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 *world = rocker_port->world; + + if (!world->ops->port_obj_vlan_dump) + return 0; + return world->ops->port_obj_vlan_dump(rocker_port->world_port_priv, + vlan, cb); +} + +static int +rocker_world_port_obj_fib4_add(struct rocker_port *rocker_port, + const struct switchdev_obj_ipv4_fib *fib4, + struct switchdev_trans *trans) +{ + struct rocker_world *world = rocker_port->world; + + if (!world->ops->port_obj_fib4_add) + return 0; + return world->ops->port_obj_fib4_add(rocker_port->world_port_priv, + fib4, trans); +} + +static int +rocker_world_port_obj_fib4_del(struct rocker_port *rocker_port, + const struct switchdev_obj_ipv4_fib *fib4) +{ + struct rocker_world *world = rocker_port->world; + + if (!world->ops->port_obj_fib4_del) + return 0; + return world->ops->port_obj_fib4_del(rocker_port->world_port_priv, + fib4); +} + +static int +rocker_world_port_obj_fdb_add(struct rocker_port *rocker_port, + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans) +{ + struct rocker_world *world = rocker_port->world; + + if (!world->ops->port_obj_fdb_add) + return 0; + return world->ops->port_obj_fdb_add(rocker_port->world_port_priv, + 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 *world = rocker_port->world; + + if (!world->ops->port_obj_fdb_del) + return 0; + return world->ops->port_obj_fdb_del(rocker_port->world_port_priv, + fdb); +} + +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) +{ + struct rocker_world *world = rocker_port->world; + + if (!world->ops->port_obj_fdb_dump) + return 0; + return world->ops->port_obj_fdb_dump(rocker_port->world_port_priv, + fdb, cb); +} + +static int rocker_world_port_master_linked(struct rocker_port *rocker_port, + struct net_device *master) +{ + struct rocker_world *world = rocker_port->world; + + if (!world->ops->port_master_linked) + return 0; + return world->ops->port_master_linked(rocker_port->world_port_priv, + master); +} + +static int rocker_world_port_master_unlinked(struct rocker_port *rocker_port, + struct net_device *master) +{ + struct rocker_world *world = rocker_port->world; + + if (!world->ops->port_master_unlinked) + return 0; + return world->ops->port_master_unlinked(rocker_port->world_port_priv, + master); +} + +static int rocker_world_port_neigh_update(struct rocker_port *rocker_port, + struct neighbour *n) +{ + struct rocker_world *world = rocker_port->world; + + if (!world->ops->port_neigh_update) + return 0; + return world->ops->port_neigh_update(rocker_port->world_port_priv, n); +} + +static int rocker_world_port_neigh_destroy(struct rocker_port *rocker_port, + struct neighbour *n) +{ + struct rocker_world *world = rocker_port->world; + + if (!world->ops->port_neigh_destroy) + return 0; + return world->ops->port_neigh_destroy(rocker_port->world_port_priv, n); +} + +static int rocker_world_port_ev_mac_vlan_seen(struct rocker_port *rocker_port, + const unsigned char *addr, + __be16 vlan_id) +{ + struct rocker_world *world = rocker_port->world; + + if (!world->ops->port_ev_mac_vlan_seen) + return 0; + return world->ops->port_ev_mac_vlan_seen(rocker_port->world_port_priv, + addr, vlan_id); +} + /*************************************************** * Flow, group, FDB, internal VLAN and neigh tables ***************************************************/ @@ -3849,6 +4270,11 @@ static int rocker_port_open(struct net_device *dev) struct rocker_port *rocker_port = netdev_priv(dev); int err; + if (!rocker_port->world) { + netdev_err(rocker_port->dev, "World not set\n"); + return -EINVAL; + } + err = rocker_port_dma_rings_init(rocker_port); if (err) return err; @@ -3869,6 +4295,12 @@ static int rocker_port_open(struct net_device *dev) goto err_request_rx_irq; } + err = rocker_world_port_open(rocker_port); + if (err) { + netdev_err(rocker_port->dev, "cannot open port in world\n"); + goto err_world_port_open; + } + err = rocker_port_fwd_enable(rocker_port, NULL, 0); if (err) goto err_fwd_enable; @@ -3881,6 +4313,7 @@ static int rocker_port_open(struct net_device *dev) return 0; err_fwd_enable: +err_world_port_open: free_irq(rocker_msix_rx_vector(rocker_port), rocker_port); err_request_rx_irq: free_irq(rocker_msix_tx_vector(rocker_port), rocker_port); @@ -3897,6 +4330,7 @@ static int rocker_port_stop(struct net_device *dev) rocker_port_set_enable(rocker_port, false); napi_disable(&rocker_port->napi_rx); napi_disable(&rocker_port->napi_tx); + rocker_world_port_stop(rocker_port); rocker_port_fwd_disable(rocker_port, NULL, ROCKER_OP_FLAG_NOWAIT); free_irq(rocker_msix_rx_vector(rocker_port), rocker_port); @@ -4107,9 +4541,14 @@ static void rocker_port_neigh_destroy(struct neighbour *n) struct rocker_port *rocker_port = netdev_priv(n->dev); int flags = ROCKER_OP_FLAG_REMOVE | ROCKER_OP_FLAG_NOWAIT; __be32 ip_addr = *(__be32 *)n->primary_key; + int err; rocker_port_ipv4_neigh(rocker_port, NULL, flags, ip_addr, n->ha); + err = rocker_world_port_neigh_destroy(rocker_port, n); + if (err) + netdev_warn(rocker_port->dev, "failed to handle neigh destroy (err %d)\n", + err); } static const struct net_device_ops rocker_port_netdev_ops = { @@ -4138,6 +4577,7 @@ static int rocker_port_attr_get(struct net_device *dev, { const struct rocker_port *rocker_port = netdev_priv(dev); const struct rocker *rocker = rocker_port->rocker; + int err = 0; switch (attr->id) { case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: @@ -4146,12 +4586,14 @@ static int rocker_port_attr_get(struct net_device *dev, break; case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: attr->u.brport_flags = rocker_port->brport_flags; + err = rocker_world_port_attr_bridge_flags_get(rocker_port, + &attr->u.brport_flags); break; default: return -EOPNOTSUPP; } - return 0; + return err; } static int rocker_port_brport_flags_set(struct rocker_port *rocker_port, @@ -4184,10 +4626,20 @@ static int rocker_port_attr_set(struct net_device *dev, err = rocker_port_stp_update(rocker_port, trans, ROCKER_OP_FLAG_NOWAIT, attr->u.stp_state); + if (err) + break; + err = rocker_world_port_attr_stp_state_set(rocker_port, + attr->u.stp_state, + trans); break; case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: err = rocker_port_brport_flags_set(rocker_port, trans, attr->u.brport_flags); + if (err) + break; + err = rocker_world_port_attr_bridge_flags_set(rocker_port, + attr->u.brport_flags, + trans); break; default: err = -EOPNOTSUPP; @@ -4259,16 +4711,31 @@ static int rocker_port_obj_add(struct net_device *dev, case SWITCHDEV_OBJ_ID_PORT_VLAN: err = rocker_port_vlans_add(rocker_port, trans, SWITCHDEV_OBJ_PORT_VLAN(obj)); + if (err) + break; + err = rocker_world_port_obj_vlan_add(rocker_port, + SWITCHDEV_OBJ_PORT_VLAN(obj), + trans); break; case SWITCHDEV_OBJ_ID_IPV4_FIB: fib4 = SWITCHDEV_OBJ_IPV4_FIB(obj); err = rocker_port_fib_ipv4(rocker_port, trans, htonl(fib4->dst), fib4->dst_len, fib4->fi, fib4->tb_id, 0); + if (err) + break; + err = rocker_world_port_obj_fib4_add(rocker_port, + SWITCHDEV_OBJ_IPV4_FIB(obj), + trans); break; case SWITCHDEV_OBJ_ID_PORT_FDB: err = rocker_port_fdb_add(rocker_port, trans, SWITCHDEV_OBJ_PORT_FDB(obj)); + if (err) + break; + err = rocker_world_port_obj_fdb_add(rocker_port, + SWITCHDEV_OBJ_PORT_FDB(obj), + trans); break; default: err = -EOPNOTSUPP; @@ -4331,6 +4798,10 @@ static int rocker_port_obj_del(struct net_device *dev, case SWITCHDEV_OBJ_ID_PORT_VLAN: err = rocker_port_vlans_del(rocker_port, SWITCHDEV_OBJ_PORT_VLAN(obj)); + if (err) + break; + err = rocker_world_port_obj_vlan_del(rocker_port, + SWITCHDEV_OBJ_PORT_VLAN(obj)); break; case SWITCHDEV_OBJ_ID_IPV4_FIB: fib4 = SWITCHDEV_OBJ_IPV4_FIB(obj); @@ -4338,10 +4809,18 @@ static int rocker_port_obj_del(struct net_device *dev, htonl(fib4->dst), fib4->dst_len, fib4->fi, fib4->tb_id, ROCKER_OP_FLAG_REMOVE); + if (err) + break; + err = rocker_world_port_obj_fib4_del(rocker_port, + SWITCHDEV_OBJ_IPV4_FIB(obj)); break; case SWITCHDEV_OBJ_ID_PORT_FDB: err = rocker_port_fdb_del(rocker_port, NULL, SWITCHDEV_OBJ_PORT_FDB(obj)); + if (err) + break; + err = rocker_world_port_obj_fdb_del(rocker_port, + SWITCHDEV_OBJ_PORT_FDB(obj)); break; default: err = -EOPNOTSUPP; @@ -4412,10 +4891,20 @@ static int rocker_port_obj_dump(struct net_device *dev, case SWITCHDEV_OBJ_ID_PORT_FDB: err = rocker_port_fdb_dump(rocker_port, SWITCHDEV_OBJ_PORT_FDB(obj), cb); + if (err) + break; + err = rocker_world_port_obj_fdb_dump(rocker_port, + SWITCHDEV_OBJ_PORT_FDB(obj), + cb); break; case SWITCHDEV_OBJ_ID_PORT_VLAN: err = rocker_port_vlan_dump(rocker_port, SWITCHDEV_OBJ_PORT_VLAN(obj), cb); + if (err) + break; + err = rocker_world_port_obj_vlan_dump(rocker_port, + SWITCHDEV_OBJ_PORT_VLAN(obj), + cb); break; default: err = -EOPNOTSUPP; @@ -4910,6 +5399,7 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) rocker = kzalloc(sizeof(*rocker), GFP_KERNEL); if (!rocker) return -ENOMEM; + INIT_LIST_HEAD(&rocker->worlds); err = pci_enable_device(pdev); if (err) { @@ -5187,12 +5677,22 @@ static int rocker_netdevice_event(struct notifier_block *unused, goto out; rocker_port = netdev_priv(dev); if (info->linking) { + err = rocker_world_port_master_linked(rocker_port, + info->upper_dev); + if (err) + netdev_warn(dev, "failed to reflect master linked (err %d)\n", + err); err = rocker_port_master_linked(rocker_port, info->upper_dev); if (err) netdev_warn(dev, "failed to reflect master linked (err %d)\n", err); } else { + err = rocker_world_port_master_unlinked(rocker_port, + info->upper_dev); + if (err) + netdev_warn(dev, "failed to reflect master unlinked (err %d)\n", + err); err = rocker_port_master_unlinked(rocker_port); if (err) netdev_warn(dev, "failed to reflect master unlinked (err %d)\n", @@ -5225,6 +5725,7 @@ static int rocker_neigh_update(struct net_device *dev, struct neighbour *n) static int rocker_netevent_event(struct notifier_block *unused, unsigned long event, void *ptr) { + struct rocker_port *rocker_port; struct net_device *dev; struct neighbour *n = ptr; int err; @@ -5236,6 +5737,11 @@ static int rocker_netevent_event(struct notifier_block *unused, dev = n->dev; if (!rocker_port_dev_check(dev)) return NOTIFY_DONE; + rocker_port = netdev_priv(dev); + err = rocker_world_port_neigh_update(rocker_port, n); + if (err) + netdev_warn(dev, "failed to handle neigh update (err %d)\n", + err); err = rocker_neigh_update(dev, n); if (err) netdev_warn(dev, -- 1.9.3 -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html