Commit 6840e17b authored by Shannon Nelson's avatar Shannon Nelson Committed by David S. Miller
Browse files

ionic: make all rx_mode work threadsafe



Move the bulk of the code from ionic_set_rx_mode(), which
can be called from atomic context, into ionic_lif_rx_mode()
which is a safe context.

A call from the stack will get pushed off into a work thread,
but it is also possible to simultaneously have a call driven
by a queue reconfig request from an ethtool command or fw
recovery event.  We add a mutex around the rx_mode work to be
sure they don't collide.

Fixes: 81dbc241 ("ionic: change set_rx_mode from_ndo to can_sleep")
Signed-off-by: default avatarShannon Nelson <snelson@pensando.io>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0506c93f
Loading
Loading
Loading
Loading
+83 −100
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@ static const u8 ionic_qtype_versions[IONIC_QTYPE_MAX] = {
				      */
};

static void ionic_lif_rx_mode(struct ionic_lif *lif, unsigned int rx_mode);
static void ionic_lif_rx_mode(struct ionic_lif *lif);
static int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr);
static int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr);
static void ionic_link_status_check(struct ionic_lif *lif);
@@ -77,7 +77,7 @@ static void ionic_lif_deferred_work(struct work_struct *work)

		switch (w->type) {
		case IONIC_DW_TYPE_RX_MODE:
			ionic_lif_rx_mode(lif, w->rx_mode);
			ionic_lif_rx_mode(lif);
			break;
		case IONIC_DW_TYPE_RX_ADDR_ADD:
			ionic_lif_addr_add(lif, w->addr);
@@ -1301,10 +1301,8 @@ static int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr)
	return 0;
}

static int ionic_lif_addr(struct ionic_lif *lif, const u8 *addr, bool add,
			  bool can_sleep)
static int ionic_lif_addr(struct ionic_lif *lif, const u8 *addr, bool add)
{
	struct ionic_deferred_work *work;
	unsigned int nmfilters;
	unsigned int nufilters;

@@ -1330,97 +1328,46 @@ static int ionic_lif_addr(struct ionic_lif *lif, const u8 *addr, bool add,
			lif->nucast--;
	}

	if (!can_sleep) {
		work = kzalloc(sizeof(*work), GFP_ATOMIC);
		if (!work)
			return -ENOMEM;
		work->type = add ? IONIC_DW_TYPE_RX_ADDR_ADD :
				   IONIC_DW_TYPE_RX_ADDR_DEL;
		memcpy(work->addr, addr, ETH_ALEN);
		netdev_dbg(lif->netdev, "deferred: rx_filter %s %pM\n",
			   add ? "add" : "del", addr);
		ionic_lif_deferred_enqueue(&lif->deferred, work);
	} else {
	netdev_dbg(lif->netdev, "rx_filter %s %pM\n",
		   add ? "add" : "del", addr);
	if (add)
		return ionic_lif_addr_add(lif, addr);
	else
		return ionic_lif_addr_del(lif, addr);
	}

	return 0;
}

static int ionic_addr_add(struct net_device *netdev, const u8 *addr)
{
	return ionic_lif_addr(netdev_priv(netdev), addr, ADD_ADDR, CAN_SLEEP);
}

static int ionic_ndo_addr_add(struct net_device *netdev, const u8 *addr)
{
	return ionic_lif_addr(netdev_priv(netdev), addr, ADD_ADDR, CAN_NOT_SLEEP);
	return ionic_lif_addr(netdev_priv(netdev), addr, ADD_ADDR);
}

static int ionic_addr_del(struct net_device *netdev, const u8 *addr)
{
	return ionic_lif_addr(netdev_priv(netdev), addr, DEL_ADDR, CAN_SLEEP);
	return ionic_lif_addr(netdev_priv(netdev), addr, DEL_ADDR);
}

static int ionic_ndo_addr_del(struct net_device *netdev, const u8 *addr)
static void ionic_lif_rx_mode(struct ionic_lif *lif)
{
	return ionic_lif_addr(netdev_priv(netdev), addr, DEL_ADDR, CAN_NOT_SLEEP);
}

static void ionic_lif_rx_mode(struct ionic_lif *lif, unsigned int rx_mode)
{
	struct ionic_admin_ctx ctx = {
		.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
		.cmd.rx_mode_set = {
			.opcode = IONIC_CMD_RX_MODE_SET,
			.lif_index = cpu_to_le16(lif->index),
			.rx_mode = cpu_to_le16(rx_mode),
		},
	};
	struct net_device *netdev = lif->netdev;
	unsigned int nfilters;
	unsigned int nd_flags;
	char buf[128];
	int err;
	u16 rx_mode;
	int i;
#define REMAIN(__x) (sizeof(buf) - (__x))

	i = scnprintf(buf, sizeof(buf), "rx_mode 0x%04x -> 0x%04x:",
		      lif->rx_mode, rx_mode);
	if (rx_mode & IONIC_RX_MODE_F_UNICAST)
		i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_UNICAST");
	if (rx_mode & IONIC_RX_MODE_F_MULTICAST)
		i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_MULTICAST");
	if (rx_mode & IONIC_RX_MODE_F_BROADCAST)
		i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_BROADCAST");
	if (rx_mode & IONIC_RX_MODE_F_PROMISC)
		i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_PROMISC");
	if (rx_mode & IONIC_RX_MODE_F_ALLMULTI)
		i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_ALLMULTI");
	netdev_dbg(lif->netdev, "lif%d %s\n", lif->index, buf);
	mutex_lock(&lif->config_lock);

	err = ionic_adminq_post_wait(lif, &ctx);
	if (err)
		netdev_warn(lif->netdev, "set rx_mode 0x%04x failed: %d\n",
			    rx_mode, err);
	else
		lif->rx_mode = rx_mode;
}

static void ionic_set_rx_mode(struct net_device *netdev, bool can_sleep)
{
	struct ionic_lif *lif = netdev_priv(netdev);
	struct ionic_deferred_work *work;
	unsigned int nfilters;
	unsigned int rx_mode;
	/* grab the flags once for local use */
	nd_flags = netdev->flags;

	rx_mode = IONIC_RX_MODE_F_UNICAST;
	rx_mode |= (netdev->flags & IFF_MULTICAST) ? IONIC_RX_MODE_F_MULTICAST : 0;
	rx_mode |= (netdev->flags & IFF_BROADCAST) ? IONIC_RX_MODE_F_BROADCAST : 0;
	rx_mode |= (netdev->flags & IFF_PROMISC) ? IONIC_RX_MODE_F_PROMISC : 0;
	rx_mode |= (netdev->flags & IFF_ALLMULTI) ? IONIC_RX_MODE_F_ALLMULTI : 0;
	rx_mode |= (nd_flags & IFF_MULTICAST) ? IONIC_RX_MODE_F_MULTICAST : 0;
	rx_mode |= (nd_flags & IFF_BROADCAST) ? IONIC_RX_MODE_F_BROADCAST : 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;

	/* sync unicast addresses
	 * next check to see if we're in an overflow state
@@ -1429,36 +1376,72 @@ static void ionic_set_rx_mode(struct net_device *netdev, bool can_sleep)
	 *       we remove our overflow flag and check the netdev flags
	 *       to see if we can disable NIC PROMISC
	 */
	if (can_sleep)
	__dev_uc_sync(netdev, ionic_addr_add, ionic_addr_del);
	else
		__dev_uc_sync(netdev, ionic_ndo_addr_add, ionic_ndo_addr_del);
	nfilters = le32_to_cpu(lif->identity->eth.max_ucast_filters);
	if (netdev_uc_count(netdev) + 1 > nfilters) {
		rx_mode |= IONIC_RX_MODE_F_PROMISC;
		lif->uc_overflow = true;
	} else if (lif->uc_overflow) {
		lif->uc_overflow = false;
		if (!(netdev->flags & IFF_PROMISC))
		if (!(nd_flags & IFF_PROMISC))
			rx_mode &= ~IONIC_RX_MODE_F_PROMISC;
	}

	/* same for multicast */
	if (can_sleep)
	__dev_mc_sync(netdev, ionic_addr_add, ionic_addr_del);
	else
		__dev_mc_sync(netdev, ionic_ndo_addr_add, ionic_ndo_addr_del);
	nfilters = le32_to_cpu(lif->identity->eth.max_mcast_filters);
	if (netdev_mc_count(netdev) > nfilters) {
		rx_mode |= IONIC_RX_MODE_F_ALLMULTI;
		lif->mc_overflow = true;
	} else if (lif->mc_overflow) {
		lif->mc_overflow = false;
		if (!(netdev->flags & IFF_ALLMULTI))
		if (!(nd_flags & IFF_ALLMULTI))
			rx_mode &= ~IONIC_RX_MODE_F_ALLMULTI;
	}

	i = scnprintf(buf, sizeof(buf), "rx_mode 0x%04x -> 0x%04x:",
		      lif->rx_mode, rx_mode);
	if (rx_mode & IONIC_RX_MODE_F_UNICAST)
		i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_UNICAST");
	if (rx_mode & IONIC_RX_MODE_F_MULTICAST)
		i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_MULTICAST");
	if (rx_mode & IONIC_RX_MODE_F_BROADCAST)
		i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_BROADCAST");
	if (rx_mode & IONIC_RX_MODE_F_PROMISC)
		i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_PROMISC");
	if (rx_mode & IONIC_RX_MODE_F_ALLMULTI)
		i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_ALLMULTI");
	if (rx_mode & IONIC_RX_MODE_F_RDMA_SNIFFER)
		i += scnprintf(&buf[i], REMAIN(i), " RX_MODE_F_RDMA_SNIFFER");
	netdev_dbg(netdev, "lif%d %s\n", lif->index, buf);

	if (lif->rx_mode != rx_mode) {
		struct ionic_admin_ctx ctx = {
			.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
			.cmd.rx_mode_set = {
				.opcode = IONIC_CMD_RX_MODE_SET,
				.lif_index = cpu_to_le16(lif->index),
			},
		};
		int err;

		ctx.cmd.rx_mode_set.rx_mode = cpu_to_le16(rx_mode);
		err = ionic_adminq_post_wait(lif, &ctx);
		if (err)
			netdev_warn(netdev, "set rx_mode 0x%04x failed: %d\n",
				    rx_mode, err);
		else
			lif->rx_mode = rx_mode;
	}

	mutex_unlock(&lif->config_lock);
}

static void ionic_set_rx_mode(struct net_device *netdev, bool can_sleep)
{
	struct ionic_lif *lif = netdev_priv(netdev);
	struct ionic_deferred_work *work;

	if (!can_sleep) {
		work = kzalloc(sizeof(*work), GFP_ATOMIC);
		if (!work) {
@@ -1466,12 +1449,10 @@ static void ionic_set_rx_mode(struct net_device *netdev, bool can_sleep)
			return;
		}
		work->type = IONIC_DW_TYPE_RX_MODE;
			work->rx_mode = rx_mode;
		netdev_dbg(lif->netdev, "deferred: rx_mode\n");
		ionic_lif_deferred_enqueue(&lif->deferred, work);
	} else {
			ionic_lif_rx_mode(lif, rx_mode);
		}
		ionic_lif_rx_mode(lif);
	}
}

@@ -3058,6 +3039,7 @@ void ionic_lif_deinit(struct ionic_lif *lif)
	ionic_lif_qcq_deinit(lif, lif->notifyqcq);
	ionic_lif_qcq_deinit(lif, lif->adminqcq);

	mutex_destroy(&lif->config_lock);
	mutex_destroy(&lif->queue_lock);
	ionic_lif_reset(lif);
}
@@ -3185,7 +3167,7 @@ static int ionic_station_set(struct ionic_lif *lif)
		 */
		if (!ether_addr_equal(ctx.comp.lif_getattr.mac,
				      netdev->dev_addr))
			ionic_lif_addr(lif, netdev->dev_addr, ADD_ADDR, CAN_SLEEP);
			ionic_lif_addr(lif, netdev->dev_addr, ADD_ADDR);
	} else {
		/* Update the netdev mac with the device's mac */
		memcpy(addr.sa_data, ctx.comp.lif_getattr.mac, netdev->addr_len);
@@ -3202,7 +3184,7 @@ static int ionic_station_set(struct ionic_lif *lif)

	netdev_dbg(lif->netdev, "adding station MAC addr %pM\n",
		   netdev->dev_addr);
	ionic_lif_addr(lif, netdev->dev_addr, ADD_ADDR, CAN_SLEEP);
	ionic_lif_addr(lif, netdev->dev_addr, ADD_ADDR);

	return 0;
}
@@ -3225,6 +3207,7 @@ int ionic_lif_init(struct ionic_lif *lif)

	lif->hw_index = le16_to_cpu(comp.hw_index);
	mutex_init(&lif->queue_lock);
	mutex_init(&lif->config_lock);

	/* now that we have the hw_index we can figure out our doorbell page */
	lif->dbid_count = le32_to_cpu(lif->ionic->ident.dev.ndbpgs_per_lif);
+2 −2
Original line number Diff line number Diff line
@@ -108,7 +108,6 @@ struct ionic_deferred_work {
	struct list_head list;
	enum ionic_deferred_work_type type;
	union {
		unsigned int rx_mode;
		u8 addr[ETH_ALEN];
		u8 fw_status;
	};
@@ -179,6 +178,7 @@ struct ionic_lif {
	unsigned int index;
	unsigned int hw_index;
	struct mutex queue_lock;	/* lock for queue structures */
	struct mutex config_lock;	/* lock for config actions */
	spinlock_t adminq_lock;		/* lock for AdminQ operations */
	struct ionic_qcq *adminqcq;
	struct ionic_qcq *notifyqcq;
@@ -199,7 +199,7 @@ struct ionic_lif {
	unsigned int nrxq_descs;
	u32 rx_copybreak;
	u64 rxq_features;
	unsigned int rx_mode;
	u16 rx_mode;
	u64 hw_features;
	bool registered;
	bool mc_overflow;