Commit 154ee116 authored by David S. Miller's avatar David S. Miller

Merge branch 'ionic-vlanid-mgmt'

Shannon Nelson says:

====================
ionic: add vlanid overflow management

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 driver 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.

The first 6 patches rework the existing filter management to
make it flexible enough for additional filter types.  Next
we add the vlan ids into the management.  The last 2 patches
allow us to catch the max vlan -ENOSPC error without adding
an unnecessary error message to the kernel log.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 7932d531 f91958cc
......@@ -70,8 +70,13 @@ struct ionic_admin_ctx {
};
int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx);
int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, int err);
int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx,
const int err, const bool do_msg);
int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx);
int ionic_adminq_post_wait_nomsg(struct ionic_lif *lif, struct ionic_admin_ctx *ctx);
void ionic_adminq_netdev_err_print(struct ionic_lif *lif, u8 opcode,
u8 status, int err);
int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_wait);
int ionic_set_dma_mask(struct ionic *ionic);
int ionic_setup(struct ionic *ionic);
......
......@@ -226,6 +226,50 @@ static int netdev_show(struct seq_file *seq, void *v)
}
DEFINE_SHOW_ATTRIBUTE(netdev);
static int lif_filters_show(struct seq_file *seq, void *v)
{
struct ionic_lif *lif = seq->private;
struct ionic_rx_filter *f;
struct hlist_head *head;
struct hlist_node *tmp;
unsigned int i;
seq_puts(seq, "id flow state type filter\n");
spin_lock_bh(&lif->rx_filters.lock);
for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
head = &lif->rx_filters.by_id[i];
hlist_for_each_entry_safe(f, tmp, head, by_id) {
switch (le16_to_cpu(f->cmd.match)) {
case IONIC_RX_FILTER_MATCH_VLAN:
seq_printf(seq, "0x%04x 0x%08x 0x%02x vlan 0x%04x\n",
f->filter_id, f->flow_id, f->state,
le16_to_cpu(f->cmd.vlan.vlan));
break;
case IONIC_RX_FILTER_MATCH_MAC:
seq_printf(seq, "0x%04x 0x%08x 0x%02x mac %pM\n",
f->filter_id, f->flow_id, f->state,
f->cmd.mac.addr);
break;
case IONIC_RX_FILTER_MATCH_MAC_VLAN:
seq_printf(seq, "0x%04x 0x%08x 0x%02x macvl 0x%04x %pM\n",
f->filter_id, f->flow_id, f->state,
le16_to_cpu(f->cmd.vlan.vlan),
f->cmd.mac.addr);
break;
case IONIC_RX_FILTER_STEER_PKTCLASS:
seq_printf(seq, "0x%04x 0x%08x 0x%02x rxstr 0x%llx\n",
f->filter_id, f->flow_id, f->state,
le64_to_cpu(f->cmd.pkt_class));
break;
}
}
}
spin_unlock_bh(&lif->rx_filters.lock);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(lif_filters);
void ionic_debugfs_add_lif(struct ionic_lif *lif)
{
struct dentry *lif_dentry;
......@@ -237,6 +281,8 @@ void ionic_debugfs_add_lif(struct ionic_lif *lif)
debugfs_create_file("netdev", 0400, lif->dentry,
lif->netdev, &netdev_fops);
debugfs_create_file("filters", 0400, lif->dentry,
lif, &lif_filters_fops);
}
void ionic_debugfs_del_lif(struct ionic_lif *lif)
......
......@@ -1242,137 +1242,6 @@ void ionic_get_stats64(struct net_device *netdev,
ns->tx_errors = ns->tx_aborted_errors;
}
int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr)
{
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_MAC),
},
};
int nfilters = le32_to_cpu(lif->identity->eth.max_ucast_filters);
bool mc = is_multicast_ether_addr(addr);
struct ionic_rx_filter *f;
int err = 0;
memcpy(ctx.cmd.rx_filter_add.mac.addr, addr, ETH_ALEN);
spin_lock_bh(&lif->rx_filters.lock);
f = ionic_rx_filter_by_addr(lif, addr);
if (f) {
/* don't bother if we already have it and it is sync'd */
if (f->state == IONIC_FILTER_STATE_SYNCED) {
spin_unlock_bh(&lif->rx_filters.lock);
return 0;
}
/* mark preemptively as sync'd to block any parallel attempts */
f->state = IONIC_FILTER_STATE_SYNCED;
} else {
/* save as SYNCED to catch any DEL requests while processing */
err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
IONIC_FILTER_STATE_SYNCED);
}
spin_unlock_bh(&lif->rx_filters.lock);
if (err)
return err;
netdev_dbg(lif->netdev, "rx_filter add ADDR %pM\n", addr);
/* Don't bother with the write to FW if we know there's no room,
* we can try again on the next sync attempt.
*/
if ((lif->nucast + lif->nmcast) >= nfilters)
err = -ENOSPC;
else
err = ionic_adminq_post_wait(lif, &ctx);
spin_lock_bh(&lif->rx_filters.lock);
if (err && err != -EEXIST) {
/* set the state back to NEW so we can try again later */
f = ionic_rx_filter_by_addr(lif, addr);
if (f && f->state == IONIC_FILTER_STATE_SYNCED) {
f->state = IONIC_FILTER_STATE_NEW;
set_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state);
}
spin_unlock_bh(&lif->rx_filters.lock);
if (err == -ENOSPC)
return 0;
else
return err;
}
if (mc)
lif->nmcast++;
else
lif->nucast++;
f = ionic_rx_filter_by_addr(lif, addr);
if (f && f->state == IONIC_FILTER_STATE_OLD) {
/* Someone requested a delete while we were adding
* so update the filter info with the results from the add
* and the data will be there for the delete on the next
* sync cycle.
*/
err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
IONIC_FILTER_STATE_OLD);
} else {
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;
}
int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr)
{
struct ionic_admin_ctx ctx = {
.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;
int state;
int err;
spin_lock_bh(&lif->rx_filters.lock);
f = ionic_rx_filter_by_addr(lif, addr);
if (!f) {
spin_unlock_bh(&lif->rx_filters.lock);
return -ENOENT;
}
netdev_dbg(lif->netdev, "rx_filter del ADDR %pM (id %d)\n",
addr, f->filter_id);
state = f->state;
ctx.cmd.rx_filter_del.filter_id = cpu_to_le32(f->filter_id);
ionic_rx_filter_free(lif, f);
if (is_multicast_ether_addr(addr) && lif->nmcast)
lif->nmcast--;
else if (!is_multicast_ether_addr(addr) && lif->nucast)
lif->nucast--;
spin_unlock_bh(&lif->rx_filters.lock);
if (state != IONIC_FILTER_STATE_NEW) {
err = ionic_adminq_post_wait(lif, &ctx);
if (err && err != -EEXIST)
return err;
}
return 0;
}
static int ionic_addr_add(struct net_device *netdev, const u8 *addr)
{
return ionic_lif_list_addr(netdev_priv(netdev), addr, ADD_ADDR);
......@@ -1404,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_ALLMULTI) ? IONIC_RX_MODE_F_ALLMULTI : 0;
/* sync the mac filters */
/* sync the filters */
ionic_rx_filter_sync(lif);
/* check for overflow state
......@@ -1414,14 +1283,12 @@ void ionic_lif_rx_mode(struct ionic_lif *lif)
* to see if we can disable NIC PROMISC
*/
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_ALLMULTI;
lif->uc_overflow = true;
lif->mc_overflow = true;
} else if (lif->uc_overflow) {
lif->uc_overflow = false;
lif->mc_overflow = false;
} else {
if (!(nd_flags & IFF_PROMISC))
rx_mode &= ~IONIC_RX_MODE_F_PROMISC;
if (!(nd_flags & IFF_ALLMULTI))
......@@ -1806,59 +1673,30 @@ static int ionic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto,
u16 vid)
{
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;
netdev_dbg(netdev, "rx_filter add VLAN %d\n", vid);
err = ionic_adminq_post_wait(lif, &ctx);
err = ionic_lif_vlan_add(lif, vid);
if (err)
return err;
spin_lock_bh(&lif->rx_filters.lock);
err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
IONIC_FILTER_STATE_SYNCED);
spin_unlock_bh(&lif->rx_filters.lock);
ionic_lif_rx_mode(lif);
return err;
return 0;
}
static int ionic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto,
u16 vid)
{
struct ionic_lif *lif = netdev_priv(netdev);
struct ionic_admin_ctx ctx = {
.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;
}
int err;
netdev_dbg(netdev, "rx_filter del VLAN %d (id %d)\n",
vid, f->filter_id);
err = ionic_lif_vlan_del(lif, vid);
if (err)
return err;
ctx.cmd.rx_filter_del.filter_id = cpu_to_le32(f->filter_id);
ionic_rx_filter_free(lif, f);
spin_unlock_bh(&lif->rx_filters.lock);
ionic_lif_rx_mode(lif);
return ionic_adminq_post_wait(lif, &ctx);
return 0;
}
int ionic_lif_rss_config(struct ionic_lif *lif, const u16 types,
......
......@@ -189,11 +189,11 @@ struct ionic_lif {
u16 rx_mode;
u64 hw_features;
bool registered;
bool mc_overflow;
bool uc_overflow;
u16 lif_type;
unsigned int nmcast;
unsigned int nucast;
unsigned int nvlans;
unsigned int max_vlans;
char name[IONIC_LIF_NAME_MAX_SZ];
union ionic_lif_identity *identity;
......
......@@ -212,24 +212,28 @@ static void ionic_adminq_flush(struct ionic_lif *lif)
spin_unlock_irqrestore(&lif->adminq_lock, irqflags);
}
void ionic_adminq_netdev_err_print(struct ionic_lif *lif, u8 opcode,
u8 status, int err)
{
netdev_err(lif->netdev, "%s (%d) failed: %s (%d)\n",
ionic_opcode_to_str(opcode), opcode,
ionic_error_to_str(status), err);
}
static int ionic_adminq_check_err(struct ionic_lif *lif,
struct ionic_admin_ctx *ctx,
bool timeout)
const bool timeout,
const bool do_msg)
{
struct net_device *netdev = lif->netdev;
const char *opcode_str;
const char *status_str;
int err = 0;
if (ctx->comp.comp.status || timeout) {
opcode_str = ionic_opcode_to_str(ctx->cmd.cmd.opcode);
status_str = ionic_error_to_str(ctx->comp.comp.status);
err = timeout ? -ETIMEDOUT :
ionic_error_to_errno(ctx->comp.comp.status);
netdev_err(netdev, "%s (%d) failed: %s (%d)\n",
opcode_str, ctx->cmd.cmd.opcode,
timeout ? "TIMEOUT" : status_str, err);
if (do_msg)
ionic_adminq_netdev_err_print(lif, ctx->cmd.cmd.opcode,
ctx->comp.comp.status, err);
if (timeout)
ionic_adminq_flush(lif);
......@@ -298,7 +302,8 @@ int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
return err;
}
int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, int err)
int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx,
const int err, const bool do_msg)
{
struct net_device *netdev = lif->netdev;
unsigned long time_limit;
......@@ -310,7 +315,7 @@ int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, int er
name = ionic_opcode_to_str(ctx->cmd.cmd.opcode);
if (err) {
if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state))
if (do_msg && !test_bit(IONIC_LIF_F_FW_RESET, lif->state))
netdev_err(netdev, "Posting of %s (%d) failed: %d\n",
name, ctx->cmd.cmd.opcode, err);
return err;
......@@ -328,8 +333,9 @@ int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, int er
/* interrupt the wait if FW stopped */
if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) {
netdev_err(netdev, "%s (%d) interrupted, FW in reset\n",
name, ctx->cmd.cmd.opcode);
if (do_msg)
netdev_err(netdev, "%s (%d) interrupted, FW in reset\n",
name, ctx->cmd.cmd.opcode);
return -ENXIO;
}
......@@ -339,7 +345,9 @@ int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx, int er
dev_dbg(lif->ionic->dev, "%s: elapsed %d msecs\n",
__func__, jiffies_to_msecs(time_done - time_start));
return ionic_adminq_check_err(lif, ctx, time_after_eq(time_done, time_limit));
return ionic_adminq_check_err(lif, ctx,
time_after_eq(time_done, time_limit),
do_msg);
}
int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
......@@ -348,7 +356,16 @@ int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
err = ionic_adminq_post(lif, ctx);
return ionic_adminq_wait(lif, ctx, err);
return ionic_adminq_wait(lif, ctx, err, true);
}
int ionic_adminq_post_wait_nomsg(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
{
int err;
err = ionic_adminq_post(lif, ctx);
return ionic_adminq_wait(lif, ctx, err, false);
}
static void ionic_dev_cmd_clean(struct ionic *ionic)
......
......@@ -348,7 +348,7 @@ static int ionic_phc_adjfine(struct ptp_clock_info *info, long scaled_ppm)
spin_unlock_irqrestore(&phc->lock, irqflags);
return ionic_adminq_wait(phc->lif, &ctx, err);
return ionic_adminq_wait(phc->lif, &ctx, err, true);
}
static int ionic_phc_adjtime(struct ptp_clock_info *info, s64 delta)
......@@ -373,7 +373,7 @@ static int ionic_phc_adjtime(struct ptp_clock_info *info, s64 delta)
spin_unlock_irqrestore(&phc->lock, irqflags);
return ionic_adminq_wait(phc->lif, &ctx, err);
return ionic_adminq_wait(phc->lif, &ctx, err, true);
}
static int ionic_phc_settime64(struct ptp_clock_info *info,
......@@ -402,7 +402,7 @@ static int ionic_phc_settime64(struct ptp_clock_info *info,
spin_unlock_irqrestore(&phc->lock, irqflags);
return ionic_adminq_wait(phc->lif, &ctx, err);
return ionic_adminq_wait(phc->lif, &ctx, err, true);
}
static int ionic_phc_gettimex64(struct ptp_clock_info *info,
......@@ -459,7 +459,7 @@ static long ionic_phc_aux_work(struct ptp_clock_info *info)
spin_unlock_irqrestore(&phc->lock, irqflags);
ionic_adminq_wait(phc->lif, &ctx, err);
ionic_adminq_wait(phc->lif, &ctx, err, true);
return phc->aux_work_delay;
}
......
......@@ -239,6 +239,21 @@ struct ionic_rx_filter *ionic_rx_filter_rxsteer(struct ionic_lif *lif)
return NULL;
}
static struct ionic_rx_filter *ionic_rx_filter_find(struct ionic_lif *lif,
struct ionic_rx_filter_add_cmd *ac)
{
switch (le16_to_cpu(ac->match)) {
case IONIC_RX_FILTER_MATCH_VLAN:
return ionic_rx_filter_by_vlan(lif, le16_to_cpu(ac->vlan.vlan));
case IONIC_RX_FILTER_MATCH_MAC:
return ionic_rx_filter_by_addr(lif, ac->mac.addr);
default:
netdev_err(lif->netdev, "unsupported filter match %d",
le16_to_cpu(ac->match));
return NULL;
}
}
int ionic_lif_list_addr(struct ionic_lif *lif, const u8 *addr, bool mode)
{
struct ionic_rx_filter *f;
......@@ -286,6 +301,228 @@ int ionic_lif_list_addr(struct ionic_lif *lif, const u8 *addr, bool mode)
return 0;
}
static int ionic_lif_filter_add(struct ionic_lif *lif,
struct ionic_rx_filter_add_cmd *ac)
{
struct ionic_admin_ctx ctx = {
.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
};
struct ionic_rx_filter *f;
int nfilters;
int err = 0;
ctx.cmd.rx_filter_add = *ac;
ctx.cmd.rx_filter_add.opcode = IONIC_CMD_RX_FILTER_ADD,
ctx.cmd.rx_filter_add.lif_index = cpu_to_le16(lif->index),
spin_lock_bh(&lif->rx_filters.lock);
f = ionic_rx_filter_find(lif, &ctx.cmd.rx_filter_add);
if (f) {
/* don't bother if we already have it and it is sync'd */
if (f->state == IONIC_FILTER_STATE_SYNCED) {
spin_unlock_bh(&lif->rx_filters.lock);
return 0;
}
/* mark preemptively as sync'd to block any parallel attempts */
f->state = IONIC_FILTER_STATE_SYNCED;
} else {
/* save as SYNCED to catch any DEL requests while processing */
err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
IONIC_FILTER_STATE_SYNCED);
}
spin_unlock_bh(&lif->rx_filters.lock);
if (err)
return err;
/* Don't bother with the write to FW if we know there's no room,
* 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)) {
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:
netdev_dbg(lif->netdev, "%s: rx_filter add ADDR %pM\n",
__func__, ctx.cmd.rx_filter_add.mac.addr);
nfilters = le32_to_cpu(lif->identity->eth.max_ucast_filters);
if ((lif->nucast + lif->nmcast) >= nfilters)
err = -ENOSPC;
break;
}
if (err != -ENOSPC)
err = ionic_adminq_post_wait_nomsg(lif, &ctx);
spin_lock_bh(&lif->rx_filters.lock);
if (err && err != -EEXIST) {
/* set the state back to NEW so we can try again later */
f = ionic_rx_filter_find(lif, &ctx.cmd.rx_filter_add);
if (f && f->state == IONIC_FILTER_STATE_SYNCED) {
f->state = IONIC_FILTER_STATE_NEW;
/* If -ENOSPC we won't waste time trying to sync again
* until there is a delete that might make room
*/
if (err != -ENOSPC)
set_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state);
}
spin_unlock_bh(&lif->rx_filters.lock);
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;
}
ionic_adminq_netdev_err_print(lif, ctx.cmd.cmd.opcode,
ctx.comp.comp.status, err);
switch (le16_to_cpu(ctx.cmd.rx_filter_add.match)) {
case IONIC_RX_FILTER_MATCH_VLAN:
netdev_info(lif->netdev, "rx_filter add failed: VLAN %d\n",
ctx.cmd.rx_filter_add.vlan.vlan);
break;
case IONIC_RX_FILTER_MATCH_MAC:
netdev_info(lif->netdev, "rx_filter add failed: ADDR %pM\n",
ctx.cmd.rx_filter_add.mac.addr);
break;
}
return err;
}
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:
if (is_multicast_ether_addr(ctx.cmd.rx_filter_add.mac.addr))
lif->nmcast++;
else
lif->nucast++;
break;
}
f = ionic_rx_filter_find(lif, &ctx.cmd.rx_filter_add);
if (f && f->state == IONIC_FILTER_STATE_OLD) {
/* Someone requested a delete while we were adding
* so update the filter info with the results from the add
* and the data will be there for the delete on the next
* sync cycle.
*/
err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
IONIC_FILTER_STATE_OLD);
} else {
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;
}
int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr)
{
struct ionic_rx_filter_add_cmd ac = {
.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_MAC),
};
memcpy(&ac.mac.addr, addr, ETH_ALEN);
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,
struct ionic_rx_filter_add_cmd *ac)
{
struct ionic_admin_ctx ctx = {
.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;
int state;
int err;
spin_lock_bh(&lif->rx_filters.lock);
f = ionic_rx_filter_find(lif, ac);
if (!f) {
spin_unlock_bh(&lif->rx_filters.lock);
return -ENOENT;
}
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:
netdev_dbg(lif->netdev, "%s: rx_filter del ADDR %pM id %d\n",
__func__, ac->mac.addr, f->filter_id);
if (is_multicast_ether_addr(ac->mac.addr) && lif->nmcast)
lif->nmcast--;
else if (!is_multicast_ether_addr(ac->mac.addr) && lif->nucast)
lif->nucast--;
break;
}
state = f->state;
ctx.cmd.rx_filter_del.filter_id = cpu_to_le32(f->filter_id);
ionic_rx_filter_free(lif, f);
spin_unlock_bh(&lif->rx_filters.lock);
if (state != IONIC_FILTER_STATE_NEW) {
err = ionic_adminq_post_wait(lif, &ctx);
if (err && err != -EEXIST)
return err;
}
return 0;
}
int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr)
{
struct ionic_rx_filter_add_cmd ac = {
.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_MAC),
};
memcpy(&ac.mac.addr, addr, ETH_ALEN);
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 list_head list;
struct ionic_rx_filter f;
......@@ -340,14 +577,14 @@ void ionic_rx_filter_sync(struct ionic_lif *lif)
* they can clear room for some new filters
*/
list_for_each_entry_safe(sync_item, spos, &sync_del_list, list) {
(void)ionic_lif_addr_del(lif, sync_item->f.cmd.mac.addr);
(void)ionic_lif_filter_del(lif, &sync_item->f.cmd);
list_del(&sync_item->list);
devm_kfree(dev, sync_item);
}
list_for_each_entry_safe(sync_item, spos, &sync_add_list, list) {
(void)ionic_lif_addr_add(lif, sync_item->f.cmd.mac.addr);
(void)ionic_lif_filter_add(lif, &sync_item->f.cmd);
list_del(&sync_item->list);
devm_kfree(dev, sync_item);
......
......@@ -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);
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_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_ */
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