Commit e20aa071 authored by Yinjun Zhang's avatar Yinjun Zhang Committed by Jakub Kicinski
Browse files

nfp: fix schedule in atomic context when sync mc address



The callback `.ndo_set_rx_mode` is called in atomic context, sleep
is not allowed in the implementation. Now use workqueue mechanism
to avoid this issue.

Fixes: de624864 ("nfp: add support for multicast filter")
Signed-off-by: default avatarYinjun Zhang <yinjun.zhang@corigine.com>
Reviewed-by: default avatarLouis Peens <louis.peens@corigine.com>
Signed-off-by: default avatarSimon Horman <simon.horman@corigine.com>
Link: https://lore.kernel.org/r/20221220152100.1042774-1-simon.horman@corigine.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 3d8f2c42
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -617,6 +617,9 @@ struct nfp_net_dp {
 * @vnic_no_name:	For non-port PF vNIC make ndo_get_phys_port_name return
 *			-EOPNOTSUPP to keep backwards compatibility (set by app)
 * @port:		Pointer to nfp_port structure if vNIC is a port
 * @mc_lock:		Protect mc_addrs list
 * @mc_addrs:		List of mc addrs to add/del to HW
 * @mc_work:		Work to update mc addrs
 * @app_priv:		APP private data for this vNIC
 */
struct nfp_net {
@@ -718,6 +721,10 @@ struct nfp_net {

	struct nfp_port *port;

	spinlock_t mc_lock;
	struct list_head mc_addrs;
	struct work_struct mc_work;

	void *app_priv;
};

+57 −4
Original line number Diff line number Diff line
@@ -1334,9 +1334,14 @@ int nfp_ctrl_open(struct nfp_net *nn)
	return err;
}

static int nfp_net_mc_cfg(struct net_device *netdev, const unsigned char *addr, const u32 cmd)
struct nfp_mc_addr_entry {
	u8 addr[ETH_ALEN];
	u32 cmd;
	struct list_head list;
};

static int nfp_net_mc_cfg(struct nfp_net *nn, const unsigned char *addr, const u32 cmd)
{
	struct nfp_net *nn = netdev_priv(netdev);
	int ret;

	ret = nfp_net_mbox_lock(nn, NFP_NET_CFG_MULTICAST_SZ);
@@ -1351,6 +1356,25 @@ static int nfp_net_mc_cfg(struct net_device *netdev, const unsigned char *addr,
	return nfp_net_mbox_reconfig_and_unlock(nn, cmd);
}

static int nfp_net_mc_prep(struct nfp_net *nn, const unsigned char *addr, const u32 cmd)
{
	struct nfp_mc_addr_entry *entry;

	entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
	if (!entry)
		return -ENOMEM;

	ether_addr_copy(entry->addr, addr);
	entry->cmd = cmd;
	spin_lock_bh(&nn->mc_lock);
	list_add_tail(&entry->list, &nn->mc_addrs);
	spin_unlock_bh(&nn->mc_lock);

	schedule_work(&nn->mc_work);

	return 0;
}

static int nfp_net_mc_sync(struct net_device *netdev, const unsigned char *addr)
{
	struct nfp_net *nn = netdev_priv(netdev);
@@ -1361,12 +1385,35 @@ static int nfp_net_mc_sync(struct net_device *netdev, const unsigned char *addr)
		return -EINVAL;
	}

	return nfp_net_mc_cfg(netdev, addr, NFP_NET_CFG_MBOX_CMD_MULTICAST_ADD);
	return nfp_net_mc_prep(nn, addr, NFP_NET_CFG_MBOX_CMD_MULTICAST_ADD);
}

static int nfp_net_mc_unsync(struct net_device *netdev, const unsigned char *addr)
{
	return nfp_net_mc_cfg(netdev, addr, NFP_NET_CFG_MBOX_CMD_MULTICAST_DEL);
	struct nfp_net *nn = netdev_priv(netdev);

	return nfp_net_mc_prep(nn, addr, NFP_NET_CFG_MBOX_CMD_MULTICAST_DEL);
}

static void nfp_net_mc_addr_config(struct work_struct *work)
{
	struct nfp_net *nn = container_of(work, struct nfp_net, mc_work);
	struct nfp_mc_addr_entry *entry, *tmp;
	struct list_head tmp_list;

	INIT_LIST_HEAD(&tmp_list);

	spin_lock_bh(&nn->mc_lock);
	list_splice_init(&nn->mc_addrs, &tmp_list);
	spin_unlock_bh(&nn->mc_lock);

	list_for_each_entry_safe(entry, tmp, &tmp_list, list) {
		if (nfp_net_mc_cfg(nn, entry->addr, entry->cmd))
			nn_err(nn, "Config mc address to HW failed.\n");

		list_del(&entry->list);
		kfree(entry);
	}
}

static void nfp_net_set_rx_mode(struct net_device *netdev)
@@ -2633,6 +2680,11 @@ int nfp_net_init(struct nfp_net *nn)

	if (!nn->dp.netdev)
		return 0;

	spin_lock_init(&nn->mc_lock);
	INIT_LIST_HEAD(&nn->mc_addrs);
	INIT_WORK(&nn->mc_work, nfp_net_mc_addr_config);

	return register_netdev(nn->dp.netdev);

err_clean_mbox:
@@ -2652,5 +2704,6 @@ void nfp_net_clean(struct nfp_net *nn)
	unregister_netdev(nn->dp.netdev);
	nfp_net_ipsec_clean(nn);
	nfp_ccm_mbox_clean(nn);
	flush_work(&nn->mc_work);
	nfp_net_reconfig_wait_posted(nn);
}