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

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
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -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);
+46 −0
Original line number Diff line number Diff line
@@ -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)
+14 −176
Original line number Diff line number Diff line
@@ -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,
+2 −2
Original line number Diff line number Diff line
@@ -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;
+32 −15
Original line number Diff line number Diff line
@@ -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,6 +333,7 @@ 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)) {
			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)
Loading