Merge branch 'mlxsw-cleanup-neigh-handling'

Jiri Pirko says:

====================
mlxsw: cleanup neigh handling

Ido says:

This series addresses long standing issues in the mlxsw driver
concerning neighbour reflection. It also prepares the code for follow-up
changes dealing with proper resource cleanup and nexthop reflection.

The first two patches convert the neighbour reflection code to use an
ordered workqueue, to prevent re-ordering of NEIGH_UPDATE events that
may happen following subsequent patches.

The third to fifth patches remove the ndo_neigh_{construct,destroy}
entry points from the driver, thereby relying only on NEIGH_UPDATE
events for neighbour reflection. This simplifies the code considerably.

Last patches are fallout and adjust nits in the code I noticed while
going over it.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2017-02-06 11:25:58 -05:00
commit 3bc32d0396
11 changed files with 148 additions and 209 deletions

View File

@ -4145,8 +4145,6 @@ static const struct net_device_ops bond_netdev_ops = {
.ndo_add_slave = bond_enslave, .ndo_add_slave = bond_enslave,
.ndo_del_slave = bond_release, .ndo_del_slave = bond_release,
.ndo_fix_features = bond_fix_features, .ndo_fix_features = bond_fix_features,
.ndo_neigh_construct = netdev_default_l2upper_neigh_construct,
.ndo_neigh_destroy = netdev_default_l2upper_neigh_destroy,
.ndo_bridge_setlink = switchdev_port_bridge_setlink, .ndo_bridge_setlink = switchdev_port_bridge_setlink,
.ndo_bridge_getlink = switchdev_port_bridge_getlink, .ndo_bridge_getlink = switchdev_port_bridge_getlink,
.ndo_bridge_dellink = switchdev_port_bridge_dellink, .ndo_bridge_dellink = switchdev_port_bridge_dellink,

View File

@ -1901,11 +1901,11 @@ int mlxsw_core_schedule_dw(struct delayed_work *dwork, unsigned long delay)
} }
EXPORT_SYMBOL(mlxsw_core_schedule_dw); EXPORT_SYMBOL(mlxsw_core_schedule_dw);
int mlxsw_core_schedule_odw(struct delayed_work *dwork, unsigned long delay) bool mlxsw_core_schedule_work(struct work_struct *work)
{ {
return queue_delayed_work(mlxsw_owq, dwork, delay); return queue_work(mlxsw_owq, work);
} }
EXPORT_SYMBOL(mlxsw_core_schedule_odw); EXPORT_SYMBOL(mlxsw_core_schedule_work);
void mlxsw_core_flush_owq(void) void mlxsw_core_flush_owq(void)
{ {

View File

@ -207,7 +207,7 @@ enum devlink_port_type mlxsw_core_port_type_get(struct mlxsw_core *mlxsw_core,
u8 local_port); u8 local_port);
int mlxsw_core_schedule_dw(struct delayed_work *dwork, unsigned long delay); int mlxsw_core_schedule_dw(struct delayed_work *dwork, unsigned long delay);
int mlxsw_core_schedule_odw(struct delayed_work *dwork, unsigned long delay); bool mlxsw_core_schedule_work(struct work_struct *work);
void mlxsw_core_flush_owq(void); void mlxsw_core_flush_owq(void);
#define MLXSW_CONFIG_PROFILE_SWID_COUNT 8 #define MLXSW_CONFIG_PROFILE_SWID_COUNT 8

View File

@ -1400,8 +1400,6 @@ static const struct net_device_ops mlxsw_sp_port_netdev_ops = {
.ndo_get_offload_stats = mlxsw_sp_port_get_offload_stats, .ndo_get_offload_stats = mlxsw_sp_port_get_offload_stats,
.ndo_vlan_rx_add_vid = mlxsw_sp_port_add_vid, .ndo_vlan_rx_add_vid = mlxsw_sp_port_add_vid,
.ndo_vlan_rx_kill_vid = mlxsw_sp_port_kill_vid, .ndo_vlan_rx_kill_vid = mlxsw_sp_port_kill_vid,
.ndo_neigh_construct = mlxsw_sp_router_neigh_construct,
.ndo_neigh_destroy = mlxsw_sp_router_neigh_destroy,
.ndo_fdb_add = switchdev_port_fdb_add, .ndo_fdb_add = switchdev_port_fdb_add,
.ndo_fdb_del = switchdev_port_fdb_del, .ndo_fdb_del = switchdev_port_fdb_del,
.ndo_fdb_dump = switchdev_port_fdb_dump, .ndo_fdb_dump = switchdev_port_fdb_dump,

View File

@ -599,10 +599,6 @@ static inline void mlxsw_sp_port_dcb_fini(struct mlxsw_sp_port *mlxsw_sp_port)
int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp); int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp);
void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp); void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp);
int mlxsw_sp_router_neigh_construct(struct net_device *dev,
struct neighbour *n);
void mlxsw_sp_router_neigh_destroy(struct net_device *dev,
struct neighbour *n);
int mlxsw_sp_router_netevent_event(struct notifier_block *unused, int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
unsigned long event, void *ptr); unsigned long event, void *ptr);

View File

@ -613,9 +613,7 @@ struct mlxsw_sp_neigh_entry {
struct rhash_head ht_node; struct rhash_head ht_node;
struct mlxsw_sp_neigh_key key; struct mlxsw_sp_neigh_key key;
u16 rif; u16 rif;
bool offloaded; bool connected;
struct delayed_work dw;
struct mlxsw_sp_port *mlxsw_sp_port;
unsigned char ha[ETH_ALEN]; unsigned char ha[ETH_ALEN];
struct list_head nexthop_list; /* list of nexthops using struct list_head nexthop_list; /* list of nexthops using
* this neigh entry * this neigh entry
@ -629,6 +627,28 @@ static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
.key_len = sizeof(struct mlxsw_sp_neigh_key), .key_len = sizeof(struct mlxsw_sp_neigh_key),
}; };
static struct mlxsw_sp_neigh_entry *
mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n,
u16 rif)
{
struct mlxsw_sp_neigh_entry *neigh_entry;
neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_KERNEL);
if (!neigh_entry)
return NULL;
neigh_entry->key.n = n;
neigh_entry->rif = rif;
INIT_LIST_HEAD(&neigh_entry->nexthop_list);
return neigh_entry;
}
static void mlxsw_sp_neigh_entry_free(struct mlxsw_sp_neigh_entry *neigh_entry)
{
kfree(neigh_entry);
}
static int static int
mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_neigh_entry *neigh_entry) struct mlxsw_sp_neigh_entry *neigh_entry)
@ -647,27 +667,38 @@ mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp_neigh_ht_params); mlxsw_sp_neigh_ht_params);
} }
static void mlxsw_sp_router_neigh_update_hw(struct work_struct *work);
static struct mlxsw_sp_neigh_entry * static struct mlxsw_sp_neigh_entry *
mlxsw_sp_neigh_entry_create(struct neighbour *n, u16 rif) mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
{ {
struct mlxsw_sp_neigh_entry *neigh_entry; struct mlxsw_sp_neigh_entry *neigh_entry;
struct mlxsw_sp_rif *r;
int err;
neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_ATOMIC); r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
if (!r)
return ERR_PTR(-EINVAL);
neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, r->rif);
if (!neigh_entry) if (!neigh_entry)
return NULL; return ERR_PTR(-ENOMEM);
neigh_entry->key.n = n;
neigh_entry->rif = rif; err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
INIT_DELAYED_WORK(&neigh_entry->dw, mlxsw_sp_router_neigh_update_hw); if (err)
INIT_LIST_HEAD(&neigh_entry->nexthop_list); goto err_neigh_entry_insert;
return neigh_entry; return neigh_entry;
err_neigh_entry_insert:
mlxsw_sp_neigh_entry_free(neigh_entry);
return ERR_PTR(err);
} }
static void static void
mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp_neigh_entry *neigh_entry) mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_neigh_entry *neigh_entry)
{ {
kfree(neigh_entry); mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
mlxsw_sp_neigh_entry_free(neigh_entry);
} }
static struct mlxsw_sp_neigh_entry * static struct mlxsw_sp_neigh_entry *
@ -680,56 +711,6 @@ mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
&key, mlxsw_sp_neigh_ht_params); &key, mlxsw_sp_neigh_ht_params);
} }
int mlxsw_sp_router_neigh_construct(struct net_device *dev,
struct neighbour *n)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
struct mlxsw_sp_neigh_entry *neigh_entry;
struct mlxsw_sp_rif *r;
int err;
if (n->tbl != &arp_tbl)
return 0;
neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
if (neigh_entry)
return 0;
r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev);
if (WARN_ON(!r))
return -EINVAL;
neigh_entry = mlxsw_sp_neigh_entry_create(n, r->rif);
if (!neigh_entry)
return -ENOMEM;
err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
if (err)
goto err_neigh_entry_insert;
return 0;
err_neigh_entry_insert:
mlxsw_sp_neigh_entry_destroy(neigh_entry);
return err;
}
void mlxsw_sp_router_neigh_destroy(struct net_device *dev,
struct neighbour *n)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
struct mlxsw_sp_neigh_entry *neigh_entry;
if (n->tbl != &arp_tbl)
return;
neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
if (!neigh_entry)
return;
mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
mlxsw_sp_neigh_entry_destroy(neigh_entry);
}
static void static void
mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp) mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
{ {
@ -866,13 +847,11 @@ static void mlxsw_sp_router_neighs_update_nh(struct mlxsw_sp *mlxsw_sp)
/* Take RTNL mutex here to prevent lists from changes */ /* Take RTNL mutex here to prevent lists from changes */
rtnl_lock(); rtnl_lock();
list_for_each_entry(neigh_entry, &mlxsw_sp->router.nexthop_neighs_list, list_for_each_entry(neigh_entry, &mlxsw_sp->router.nexthop_neighs_list,
nexthop_neighs_list_node) { nexthop_neighs_list_node)
/* If this neigh have nexthops, make the kernel think this neigh /* If this neigh have nexthops, make the kernel think this neigh
* is active regardless of the traffic. * is active regardless of the traffic.
*/ */
if (!list_empty(&neigh_entry->nexthop_list)) neigh_event_send(neigh_entry->key.n, NULL);
neigh_event_send(neigh_entry->key.n, NULL);
}
rtnl_unlock(); rtnl_unlock();
} }
@ -916,11 +895,9 @@ static void mlxsw_sp_router_probe_unresolved_nexthops(struct work_struct *work)
*/ */
rtnl_lock(); rtnl_lock();
list_for_each_entry(neigh_entry, &mlxsw_sp->router.nexthop_neighs_list, list_for_each_entry(neigh_entry, &mlxsw_sp->router.nexthop_neighs_list,
nexthop_neighs_list_node) { nexthop_neighs_list_node)
if (!(neigh_entry->key.n->nud_state & NUD_VALID) && if (!neigh_entry->connected)
!list_empty(&neigh_entry->nexthop_list))
neigh_event_send(neigh_entry->key.n, NULL); neigh_event_send(neigh_entry->key.n, NULL);
}
rtnl_unlock(); rtnl_unlock();
mlxsw_core_schedule_dw(&mlxsw_sp->router.nexthop_probe_dw, mlxsw_core_schedule_dw(&mlxsw_sp->router.nexthop_probe_dw,
@ -932,79 +909,101 @@ mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_neigh_entry *neigh_entry, struct mlxsw_sp_neigh_entry *neigh_entry,
bool removing); bool removing);
static void mlxsw_sp_router_neigh_update_hw(struct work_struct *work) static enum mlxsw_reg_rauht_op mlxsw_sp_rauht_op(bool adding)
{
return adding ? MLXSW_REG_RAUHT_OP_WRITE_ADD :
MLXSW_REG_RAUHT_OP_WRITE_DELETE;
}
static void
mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_neigh_entry *neigh_entry,
enum mlxsw_reg_rauht_op op)
{ {
struct mlxsw_sp_neigh_entry *neigh_entry =
container_of(work, struct mlxsw_sp_neigh_entry, dw.work);
struct neighbour *n = neigh_entry->key.n; struct neighbour *n = neigh_entry->key.n;
struct mlxsw_sp_port *mlxsw_sp_port = neigh_entry->mlxsw_sp_port; u32 dip = ntohl(*((__be32 *) n->primary_key));
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
char rauht_pl[MLXSW_REG_RAUHT_LEN]; char rauht_pl[MLXSW_REG_RAUHT_LEN];
struct net_device *dev;
mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha,
dip);
mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl);
}
static void
mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_neigh_entry *neigh_entry,
bool adding)
{
if (!adding && !neigh_entry->connected)
return;
neigh_entry->connected = adding;
if (neigh_entry->key.n->tbl == &arp_tbl)
mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry,
mlxsw_sp_rauht_op(adding));
else
WARN_ON_ONCE(1);
}
struct mlxsw_sp_neigh_event_work {
struct work_struct work;
struct mlxsw_sp *mlxsw_sp;
struct neighbour *n;
};
static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
{
struct mlxsw_sp_neigh_event_work *neigh_work =
container_of(work, struct mlxsw_sp_neigh_event_work, work);
struct mlxsw_sp *mlxsw_sp = neigh_work->mlxsw_sp;
struct mlxsw_sp_neigh_entry *neigh_entry;
struct neighbour *n = neigh_work->n;
unsigned char ha[ETH_ALEN];
bool entry_connected; bool entry_connected;
u8 nud_state, dead; u8 nud_state, dead;
bool updating;
bool removing;
bool adding;
u32 dip;
int err;
/* If these parameters are changed after we release the lock,
* then we are guaranteed to receive another event letting us
* know about it.
*/
read_lock_bh(&n->lock); read_lock_bh(&n->lock);
dip = ntohl(*((__be32 *) n->primary_key)); memcpy(ha, n->ha, ETH_ALEN);
memcpy(neigh_entry->ha, n->ha, sizeof(neigh_entry->ha));
nud_state = n->nud_state; nud_state = n->nud_state;
dead = n->dead; dead = n->dead;
dev = n->dev;
read_unlock_bh(&n->lock); read_unlock_bh(&n->lock);
rtnl_lock();
entry_connected = nud_state & NUD_VALID && !dead; entry_connected = nud_state & NUD_VALID && !dead;
adding = (!neigh_entry->offloaded) && entry_connected; neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
updating = neigh_entry->offloaded && entry_connected; if (!entry_connected && !neigh_entry)
removing = neigh_entry->offloaded && !entry_connected; goto out;
if (!neigh_entry) {
if (adding || updating) { neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
mlxsw_reg_rauht_pack4(rauht_pl, MLXSW_REG_RAUHT_OP_WRITE_ADD, if (IS_ERR(neigh_entry))
neigh_entry->rif, goto out;
neigh_entry->ha, dip);
err = mlxsw_reg_write(mlxsw_sp->core,
MLXSW_REG(rauht), rauht_pl);
if (err) {
netdev_err(dev, "Could not add neigh %pI4h\n", &dip);
neigh_entry->offloaded = false;
} else {
neigh_entry->offloaded = true;
}
mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, false);
} else if (removing) {
mlxsw_reg_rauht_pack4(rauht_pl, MLXSW_REG_RAUHT_OP_WRITE_DELETE,
neigh_entry->rif,
neigh_entry->ha, dip);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht),
rauht_pl);
if (err) {
netdev_err(dev, "Could not delete neigh %pI4h\n", &dip);
neigh_entry->offloaded = true;
} else {
neigh_entry->offloaded = false;
}
mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, true);
} }
memcpy(neigh_entry->ha, ha, ETH_ALEN);
mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected);
if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
out:
rtnl_unlock();
neigh_release(n); neigh_release(n);
mlxsw_sp_port_dev_put(mlxsw_sp_port); kfree(neigh_work);
} }
int mlxsw_sp_router_netevent_event(struct notifier_block *unused, int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
unsigned long event, void *ptr) unsigned long event, void *ptr)
{ {
struct mlxsw_sp_neigh_entry *neigh_entry; struct mlxsw_sp_neigh_event_work *neigh_work;
struct mlxsw_sp_port *mlxsw_sp_port; struct mlxsw_sp_port *mlxsw_sp_port;
struct mlxsw_sp *mlxsw_sp; struct mlxsw_sp *mlxsw_sp;
unsigned long interval; unsigned long interval;
struct net_device *dev;
struct neigh_parms *p; struct neigh_parms *p;
struct neighbour *n; struct neighbour *n;
u32 dip;
switch (event) { switch (event) {
case NETEVENT_DELAY_PROBE_TIME_UPDATE: case NETEVENT_DELAY_PROBE_TIME_UPDATE:
@ -1029,33 +1028,31 @@ int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
break; break;
case NETEVENT_NEIGH_UPDATE: case NETEVENT_NEIGH_UPDATE:
n = ptr; n = ptr;
dev = n->dev;
if (n->tbl != &arp_tbl) if (n->tbl != &arp_tbl)
return NOTIFY_DONE; return NOTIFY_DONE;
mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(dev); mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev);
if (!mlxsw_sp_port) if (!mlxsw_sp_port)
return NOTIFY_DONE; return NOTIFY_DONE;
mlxsw_sp = mlxsw_sp_port->mlxsw_sp; neigh_work = kzalloc(sizeof(*neigh_work), GFP_ATOMIC);
dip = ntohl(*((__be32 *) n->primary_key)); if (!neigh_work) {
neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
if (WARN_ON(!neigh_entry)) {
mlxsw_sp_port_dev_put(mlxsw_sp_port); mlxsw_sp_port_dev_put(mlxsw_sp_port);
return NOTIFY_DONE; return NOTIFY_BAD;
} }
neigh_entry->mlxsw_sp_port = mlxsw_sp_port;
INIT_WORK(&neigh_work->work, mlxsw_sp_router_neigh_event_work);
neigh_work->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
neigh_work->n = n;
/* Take a reference to ensure the neighbour won't be /* Take a reference to ensure the neighbour won't be
* destructed until we drop the reference in delayed * destructed until we drop the reference in delayed
* work. * work.
*/ */
neigh_clone(n); neigh_clone(n);
if (!mlxsw_core_schedule_dw(&neigh_entry->dw, 0)) { mlxsw_core_schedule_work(&neigh_work->work);
neigh_release(n); mlxsw_sp_port_dev_put(mlxsw_sp_port);
mlxsw_sp_port_dev_put(mlxsw_sp_port);
}
break; break;
} }
@ -1336,14 +1333,11 @@ mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
{ {
struct mlxsw_sp_nexthop *nh; struct mlxsw_sp_nexthop *nh;
/* Take RTNL mutex here to prevent lists from changes */
rtnl_lock();
list_for_each_entry(nh, &neigh_entry->nexthop_list, list_for_each_entry(nh, &neigh_entry->nexthop_list,
neigh_list_node) { neigh_list_node) {
__mlxsw_sp_nexthop_neigh_update(nh, removing); __mlxsw_sp_nexthop_neigh_update(nh, removing);
mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp); mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp);
} }
rtnl_unlock();
} }
static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp, static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp,
@ -1359,7 +1353,7 @@ static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp,
/* Take a reference of neigh here ensuring that neigh would /* Take a reference of neigh here ensuring that neigh would
* not be detructed before the nexthop entry is finished. * not be detructed before the nexthop entry is finished.
* The reference is taken either in neigh_lookup() or * The reference is taken either in neigh_lookup() or
* in neith_create() in case n is not found. * in neigh_create() in case n is not found.
*/ */
n = neigh_lookup(&arp_tbl, &fib_nh->nh_gw, dev); n = neigh_lookup(&arp_tbl, &fib_nh->nh_gw, dev);
if (!n) { if (!n) {
@ -1370,8 +1364,11 @@ static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp,
} }
neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n); neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
if (!neigh_entry) { if (!neigh_entry) {
neigh_release(n); neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
return -EINVAL; if (IS_ERR(neigh_entry)) {
neigh_release(n);
return -EINVAL;
}
} }
/* If that is the first nexthop connected to that neigh, add to /* If that is the first nexthop connected to that neigh, add to
@ -1397,6 +1394,7 @@ static void mlxsw_sp_nexthop_fini(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_nexthop *nh) struct mlxsw_sp_nexthop *nh)
{ {
struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry; struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
struct neighbour *n = neigh_entry->key.n;
__mlxsw_sp_nexthop_neigh_update(nh, true); __mlxsw_sp_nexthop_neigh_update(nh, true);
list_del(&nh->neigh_list_node); list_del(&nh->neigh_list_node);
@ -1407,7 +1405,10 @@ static void mlxsw_sp_nexthop_fini(struct mlxsw_sp *mlxsw_sp,
if (list_empty(&nh->neigh_entry->nexthop_list)) if (list_empty(&nh->neigh_entry->nexthop_list))
list_del(&nh->neigh_entry->nexthop_neighs_list_node); list_del(&nh->neigh_entry->nexthop_neighs_list_node);
neigh_release(neigh_entry->key.n); if (!neigh_entry->connected && list_empty(&neigh_entry->nexthop_list))
mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry);
neigh_release(n);
} }
static struct mlxsw_sp_nexthop_group * static struct mlxsw_sp_nexthop_group *
@ -1964,7 +1965,7 @@ static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
} }
struct mlxsw_sp_fib_event_work { struct mlxsw_sp_fib_event_work {
struct delayed_work dw; struct work_struct work;
struct fib_entry_notifier_info fen_info; struct fib_entry_notifier_info fen_info;
struct mlxsw_sp *mlxsw_sp; struct mlxsw_sp *mlxsw_sp;
unsigned long event; unsigned long event;
@ -1973,7 +1974,7 @@ struct mlxsw_sp_fib_event_work {
static void mlxsw_sp_router_fib_event_work(struct work_struct *work) static void mlxsw_sp_router_fib_event_work(struct work_struct *work)
{ {
struct mlxsw_sp_fib_event_work *fib_work = struct mlxsw_sp_fib_event_work *fib_work =
container_of(work, struct mlxsw_sp_fib_event_work, dw.work); container_of(work, struct mlxsw_sp_fib_event_work, work);
struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp; struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
int err; int err;
@ -2014,7 +2015,7 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
if (WARN_ON(!fib_work)) if (WARN_ON(!fib_work))
return NOTIFY_BAD; return NOTIFY_BAD;
INIT_DELAYED_WORK(&fib_work->dw, mlxsw_sp_router_fib_event_work); INIT_WORK(&fib_work->work, mlxsw_sp_router_fib_event_work);
fib_work->mlxsw_sp = mlxsw_sp; fib_work->mlxsw_sp = mlxsw_sp;
fib_work->event = event; fib_work->event = event;
@ -2029,7 +2030,7 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
break; break;
} }
mlxsw_core_schedule_odw(&fib_work->dw, 0); mlxsw_core_schedule_work(&fib_work->work);
return NOTIFY_DONE; return NOTIFY_DONE;
} }

View File

@ -2001,8 +2001,6 @@ static const struct net_device_ops team_netdev_ops = {
.ndo_add_slave = team_add_slave, .ndo_add_slave = team_add_slave,
.ndo_del_slave = team_del_slave, .ndo_del_slave = team_del_slave,
.ndo_fix_features = team_fix_features, .ndo_fix_features = team_fix_features,
.ndo_neigh_construct = netdev_default_l2upper_neigh_construct,
.ndo_neigh_destroy = netdev_default_l2upper_neigh_destroy,
.ndo_change_carrier = team_change_carrier, .ndo_change_carrier = team_change_carrier,
.ndo_bridge_setlink = switchdev_port_bridge_setlink, .ndo_bridge_setlink = switchdev_port_bridge_setlink,
.ndo_bridge_getlink = switchdev_port_bridge_getlink, .ndo_bridge_getlink = switchdev_port_bridge_getlink,

View File

@ -3888,10 +3888,6 @@ void *netdev_lower_dev_get_private(struct net_device *dev,
struct net_device *lower_dev); struct net_device *lower_dev);
void netdev_lower_state_changed(struct net_device *lower_dev, void netdev_lower_state_changed(struct net_device *lower_dev,
void *lower_state_info); void *lower_state_info);
int netdev_default_l2upper_neigh_construct(struct net_device *dev,
struct neighbour *n);
void netdev_default_l2upper_neigh_destroy(struct net_device *dev,
struct neighbour *n);
/* RSS keys are 40 or 52 bytes long */ /* RSS keys are 40 or 52 bytes long */
#define NETDEV_RSS_KEY_LEN 52 #define NETDEV_RSS_KEY_LEN 52

View File

@ -791,8 +791,6 @@ static const struct net_device_ops vlan_netdev_ops = {
.ndo_netpoll_cleanup = vlan_dev_netpoll_cleanup, .ndo_netpoll_cleanup = vlan_dev_netpoll_cleanup,
#endif #endif
.ndo_fix_features = vlan_dev_fix_features, .ndo_fix_features = vlan_dev_fix_features,
.ndo_neigh_construct = netdev_default_l2upper_neigh_construct,
.ndo_neigh_destroy = netdev_default_l2upper_neigh_destroy,
.ndo_fdb_add = switchdev_port_fdb_add, .ndo_fdb_add = switchdev_port_fdb_add,
.ndo_fdb_del = switchdev_port_fdb_del, .ndo_fdb_del = switchdev_port_fdb_del,
.ndo_fdb_dump = switchdev_port_fdb_dump, .ndo_fdb_dump = switchdev_port_fdb_dump,

View File

@ -347,8 +347,6 @@ static const struct net_device_ops br_netdev_ops = {
.ndo_add_slave = br_add_slave, .ndo_add_slave = br_add_slave,
.ndo_del_slave = br_del_slave, .ndo_del_slave = br_del_slave,
.ndo_fix_features = br_fix_features, .ndo_fix_features = br_fix_features,
.ndo_neigh_construct = netdev_default_l2upper_neigh_construct,
.ndo_neigh_destroy = netdev_default_l2upper_neigh_destroy,
.ndo_fdb_add = br_fdb_add, .ndo_fdb_add = br_fdb_add,
.ndo_fdb_del = br_fdb_delete, .ndo_fdb_del = br_fdb_delete,
.ndo_fdb_dump = br_fdb_dump, .ndo_fdb_dump = br_fdb_dump,

View File

@ -6111,50 +6111,6 @@ void netdev_lower_state_changed(struct net_device *lower_dev,
} }
EXPORT_SYMBOL(netdev_lower_state_changed); EXPORT_SYMBOL(netdev_lower_state_changed);
int netdev_default_l2upper_neigh_construct(struct net_device *dev,
struct neighbour *n)
{
struct net_device *lower_dev, *stop_dev;
struct list_head *iter;
int err;
netdev_for_each_lower_dev(dev, lower_dev, iter) {
if (!lower_dev->netdev_ops->ndo_neigh_construct)
continue;
err = lower_dev->netdev_ops->ndo_neigh_construct(lower_dev, n);
if (err) {
stop_dev = lower_dev;
goto rollback;
}
}
return 0;
rollback:
netdev_for_each_lower_dev(dev, lower_dev, iter) {
if (lower_dev == stop_dev)
break;
if (!lower_dev->netdev_ops->ndo_neigh_destroy)
continue;
lower_dev->netdev_ops->ndo_neigh_destroy(lower_dev, n);
}
return err;
}
EXPORT_SYMBOL_GPL(netdev_default_l2upper_neigh_construct);
void netdev_default_l2upper_neigh_destroy(struct net_device *dev,
struct neighbour *n)
{
struct net_device *lower_dev;
struct list_head *iter;
netdev_for_each_lower_dev(dev, lower_dev, iter) {
if (!lower_dev->netdev_ops->ndo_neigh_destroy)
continue;
lower_dev->netdev_ops->ndo_neigh_destroy(lower_dev, n);
}
}
EXPORT_SYMBOL_GPL(netdev_default_l2upper_neigh_destroy);
static void dev_change_rx_flags(struct net_device *dev, int flags) static void dev_change_rx_flags(struct net_device *dev, int flags)
{ {
const struct net_device_ops *ops = dev->netdev_ops; const struct net_device_ops *ops = dev->netdev_ops;