Commit 9b0b6ba6 authored by Shannon Nelson's avatar Shannon Nelson Committed by David S. Miller

ionic: handle vlan id overflow

Add vlans to the existing rx_filter_sync mechanics currently
used for managing mac filters.

Older versions of our firmware had no enforced limits on the
number of vlans that the LIF could request, but requesting large
numbers of vlans caused issues in FW memory management, so an
arbitrary limit was added in the FW.  The FW now returns -ENOSPC
when it hits that limit, which the driver needs to handle.

Unfortunately, the FW doesn't advertise the vlan id limit,
as it does with mac filters, so the driver won't know the
limit until it bumps into it.  We'll grab the current vlan id
count and use that as the limit from there on and thus prevent
getting any more -ENOSPC errors.

Just as is done for the mac filters, the device puts the device
into promiscuous mode when -ENOSPC is seen for vlan ids, and
the driver will track the vlans that aren't synced to the FW.
When vlans are removed, the driver will retry the un-synced
vlans.  If all outstanding vlans are synced, the promiscuous
mode will be disabled.
Signed-off-by: default avatarShannon Nelson <snelson@pensando.io>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c2b63d34
...@@ -1273,7 +1273,7 @@ void ionic_lif_rx_mode(struct ionic_lif *lif) ...@@ -1273,7 +1273,7 @@ void ionic_lif_rx_mode(struct ionic_lif *lif)
rx_mode |= (nd_flags & IFF_PROMISC) ? IONIC_RX_MODE_F_PROMISC : 0; rx_mode |= (nd_flags & IFF_PROMISC) ? IONIC_RX_MODE_F_PROMISC : 0;
rx_mode |= (nd_flags & IFF_ALLMULTI) ? IONIC_RX_MODE_F_ALLMULTI : 0; rx_mode |= (nd_flags & IFF_ALLMULTI) ? IONIC_RX_MODE_F_ALLMULTI : 0;
/* sync the mac filters */ /* sync the filters */
ionic_rx_filter_sync(lif); ionic_rx_filter_sync(lif);
/* check for overflow state /* check for overflow state
...@@ -1284,7 +1284,8 @@ void ionic_lif_rx_mode(struct ionic_lif *lif) ...@@ -1284,7 +1284,8 @@ void ionic_lif_rx_mode(struct ionic_lif *lif)
*/ */
nfilters = le32_to_cpu(lif->identity->eth.max_ucast_filters); nfilters = le32_to_cpu(lif->identity->eth.max_ucast_filters);
if ((lif->nucast + lif->nmcast) >= nfilters) { if (((lif->nucast + lif->nmcast) >= nfilters) ||
(lif->max_vlans && lif->nvlans >= lif->max_vlans)) {
rx_mode |= IONIC_RX_MODE_F_PROMISC; rx_mode |= IONIC_RX_MODE_F_PROMISC;
rx_mode |= IONIC_RX_MODE_F_ALLMULTI; rx_mode |= IONIC_RX_MODE_F_ALLMULTI;
} else { } else {
...@@ -1672,59 +1673,30 @@ static int ionic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, ...@@ -1672,59 +1673,30 @@ static int ionic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto,
u16 vid) u16 vid)
{ {
struct ionic_lif *lif = netdev_priv(netdev); struct ionic_lif *lif = netdev_priv(netdev);
struct ionic_admin_ctx ctx = {
.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
.cmd.rx_filter_add = {
.opcode = IONIC_CMD_RX_FILTER_ADD,
.lif_index = cpu_to_le16(lif->index),
.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_VLAN),
.vlan.vlan = cpu_to_le16(vid),
},
};
int err; int err;
netdev_dbg(netdev, "rx_filter add VLAN %d\n", vid); err = ionic_lif_vlan_add(lif, vid);
err = ionic_adminq_post_wait(lif, &ctx);
if (err) if (err)
return err; return err;
spin_lock_bh(&lif->rx_filters.lock); ionic_lif_rx_mode(lif);
err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
IONIC_FILTER_STATE_SYNCED);
spin_unlock_bh(&lif->rx_filters.lock);
return err; return 0;
} }
static int ionic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, static int ionic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto,
u16 vid) u16 vid)
{ {
struct ionic_lif *lif = netdev_priv(netdev); struct ionic_lif *lif = netdev_priv(netdev);
struct ionic_admin_ctx ctx = { int err;
.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
.cmd.rx_filter_del = {
.opcode = IONIC_CMD_RX_FILTER_DEL,
.lif_index = cpu_to_le16(lif->index),
},
};
struct ionic_rx_filter *f;
spin_lock_bh(&lif->rx_filters.lock);
f = ionic_rx_filter_by_vlan(lif, vid);
if (!f) {
spin_unlock_bh(&lif->rx_filters.lock);
return -ENOENT;
}
netdev_dbg(netdev, "rx_filter del VLAN %d (id %d)\n", err = ionic_lif_vlan_del(lif, vid);
vid, f->filter_id); if (err)
return err;
ctx.cmd.rx_filter_del.filter_id = cpu_to_le32(f->filter_id); ionic_lif_rx_mode(lif);
ionic_rx_filter_free(lif, f);
spin_unlock_bh(&lif->rx_filters.lock);
return ionic_adminq_post_wait(lif, &ctx); return 0;
} }
int ionic_lif_rss_config(struct ionic_lif *lif, const u16 types, int ionic_lif_rss_config(struct ionic_lif *lif, const u16 types,
......
...@@ -192,6 +192,8 @@ struct ionic_lif { ...@@ -192,6 +192,8 @@ struct ionic_lif {
u16 lif_type; u16 lif_type;
unsigned int nmcast; unsigned int nmcast;
unsigned int nucast; unsigned int nucast;
unsigned int nvlans;
unsigned int max_vlans;
char name[IONIC_LIF_NAME_MAX_SZ]; char name[IONIC_LIF_NAME_MAX_SZ];
union ionic_lif_identity *identity; union ionic_lif_identity *identity;
......
...@@ -337,8 +337,16 @@ static int ionic_lif_filter_add(struct ionic_lif *lif, ...@@ -337,8 +337,16 @@ static int ionic_lif_filter_add(struct ionic_lif *lif,
/* Don't bother with the write to FW if we know there's no room, /* Don't bother with the write to FW if we know there's no room,
* we can try again on the next sync attempt. * we can try again on the next sync attempt.
* Since the FW doesn't have a way to tell us the vlan limit,
* we start max_vlans at 0 until we hit the ENOSPC error.
*/ */
switch (le16_to_cpu(ctx.cmd.rx_filter_add.match)) { switch (le16_to_cpu(ctx.cmd.rx_filter_add.match)) {
case IONIC_RX_FILTER_MATCH_VLAN:
netdev_dbg(lif->netdev, "%s: rx_filter add VLAN %d\n",
__func__, ctx.cmd.rx_filter_add.vlan.vlan);
if (lif->max_vlans && lif->nvlans >= lif->max_vlans)
err = -ENOSPC;
break;
case IONIC_RX_FILTER_MATCH_MAC: case IONIC_RX_FILTER_MATCH_MAC:
netdev_dbg(lif->netdev, "%s: rx_filter add ADDR %pM\n", netdev_dbg(lif->netdev, "%s: rx_filter add ADDR %pM\n",
__func__, ctx.cmd.rx_filter_add.mac.addr); __func__, ctx.cmd.rx_filter_add.mac.addr);
...@@ -368,13 +376,19 @@ static int ionic_lif_filter_add(struct ionic_lif *lif, ...@@ -368,13 +376,19 @@ static int ionic_lif_filter_add(struct ionic_lif *lif,
spin_unlock_bh(&lif->rx_filters.lock); spin_unlock_bh(&lif->rx_filters.lock);
if (err == -ENOSPC) if (err == -ENOSPC) {
if (le16_to_cpu(ctx.cmd.rx_filter_add.match) == IONIC_RX_FILTER_MATCH_VLAN)
lif->max_vlans = lif->nvlans;
return 0; return 0;
}
return err; return err;
} }
switch (le16_to_cpu(ctx.cmd.rx_filter_add.match)) { switch (le16_to_cpu(ctx.cmd.rx_filter_add.match)) {
case IONIC_RX_FILTER_MATCH_VLAN:
lif->nvlans++;
break;
case IONIC_RX_FILTER_MATCH_MAC: case IONIC_RX_FILTER_MATCH_MAC:
if (is_multicast_ether_addr(ctx.cmd.rx_filter_add.mac.addr)) if (is_multicast_ether_addr(ctx.cmd.rx_filter_add.mac.addr))
lif->nmcast++; lif->nmcast++;
...@@ -413,6 +427,16 @@ int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr) ...@@ -413,6 +427,16 @@ int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr)
return ionic_lif_filter_add(lif, &ac); return ionic_lif_filter_add(lif, &ac);
} }
int ionic_lif_vlan_add(struct ionic_lif *lif, const u16 vid)
{
struct ionic_rx_filter_add_cmd ac = {
.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_VLAN),
.vlan.vlan = cpu_to_le16(vid),
};
return ionic_lif_filter_add(lif, &ac);
}
static int ionic_lif_filter_del(struct ionic_lif *lif, static int ionic_lif_filter_del(struct ionic_lif *lif,
struct ionic_rx_filter_add_cmd *ac) struct ionic_rx_filter_add_cmd *ac)
{ {
...@@ -435,6 +459,11 @@ static int ionic_lif_filter_del(struct ionic_lif *lif, ...@@ -435,6 +459,11 @@ static int ionic_lif_filter_del(struct ionic_lif *lif,
} }
switch (le16_to_cpu(ac->match)) { switch (le16_to_cpu(ac->match)) {
case IONIC_RX_FILTER_MATCH_VLAN:
netdev_dbg(lif->netdev, "%s: rx_filter del VLAN %d id %d\n",
__func__, ac->vlan.vlan, f->filter_id);
lif->nvlans--;
break;
case IONIC_RX_FILTER_MATCH_MAC: case IONIC_RX_FILTER_MATCH_MAC:
netdev_dbg(lif->netdev, "%s: rx_filter del ADDR %pM id %d\n", netdev_dbg(lif->netdev, "%s: rx_filter del ADDR %pM id %d\n",
__func__, ac->mac.addr, f->filter_id); __func__, ac->mac.addr, f->filter_id);
...@@ -471,6 +500,16 @@ int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr) ...@@ -471,6 +500,16 @@ int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr)
return ionic_lif_filter_del(lif, &ac); return ionic_lif_filter_del(lif, &ac);
} }
int ionic_lif_vlan_del(struct ionic_lif *lif, const u16 vid)
{
struct ionic_rx_filter_add_cmd ac = {
.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_VLAN),
.vlan.vlan = cpu_to_le16(vid),
};
return ionic_lif_filter_del(lif, &ac);
}
struct sync_item { struct sync_item {
struct list_head list; struct list_head list;
struct ionic_rx_filter f; struct ionic_rx_filter f;
......
...@@ -44,5 +44,7 @@ struct ionic_rx_filter *ionic_rx_filter_rxsteer(struct ionic_lif *lif); ...@@ -44,5 +44,7 @@ struct ionic_rx_filter *ionic_rx_filter_rxsteer(struct ionic_lif *lif);
void ionic_rx_filter_sync(struct ionic_lif *lif); void ionic_rx_filter_sync(struct ionic_lif *lif);
int ionic_lif_list_addr(struct ionic_lif *lif, const u8 *addr, bool mode); int ionic_lif_list_addr(struct ionic_lif *lif, const u8 *addr, bool mode);
int ionic_rx_filters_need_sync(struct ionic_lif *lif); int ionic_rx_filters_need_sync(struct ionic_lif *lif);
int ionic_lif_vlan_add(struct ionic_lif *lif, const u16 vid);
int ionic_lif_vlan_del(struct ionic_lif *lif, const u16 vid);
#endif /* _IONIC_RX_FILTER_H_ */ #endif /* _IONIC_RX_FILTER_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment