mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 05:49:47 -04:00
net: enetc: convert ndo_set_rx_mode() to ndo_set_rx_mode_async()
The current ndo_set_rx_mode() is called under netif_addr_lock spinlock
with BHs disabled, which prevents drivers from sleeping. To work around
this limitation, the enetc driver uses a dedicated workqueue to defer
MAC address list updates to a sleepable context.
Since commit 3554b4345d ("net: introduce ndo_set_rx_mode_async and
netdev_rx_mode_work") introduced the ndo_set_rx_mode_async() callback,
drivers can now handle address list updates directly in a sleepable
context.
Therefore, convert the enetc driver to use ndo_set_rx_mode_async() and
remove the dedicated workqueue and the deferred work item accordingly.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
Link: https://patch.msgid.link/20260720014317.1059359-4-wei.fang@oss.nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -324,8 +324,6 @@ struct enetc_si {
|
||||
const struct enetc_drvdata *drvdata;
|
||||
const struct enetc_si_ops *ops;
|
||||
|
||||
struct workqueue_struct *workqueue;
|
||||
struct work_struct rx_mode_task;
|
||||
struct dentry *debugfs_root;
|
||||
struct enetc_msg_swbd msg; /* Only valid for VSI */
|
||||
};
|
||||
|
||||
@@ -101,24 +101,23 @@ static void enetc4_pf_clear_maft_entries(struct enetc_pf *pf)
|
||||
}
|
||||
|
||||
static int enetc4_pf_add_maft_entries(struct enetc_pf *pf,
|
||||
struct enetc_mac_addr *mac,
|
||||
int mac_cnt)
|
||||
struct netdev_hw_addr_list *uc)
|
||||
{
|
||||
struct maft_entry_data maft = {};
|
||||
struct netdev_hw_addr *ha;
|
||||
u16 si_bit = BIT(0);
|
||||
int i, err;
|
||||
int err;
|
||||
|
||||
maft.cfge.si_bitmap = cpu_to_le16(si_bit);
|
||||
for (i = 0; i < mac_cnt; i++) {
|
||||
ether_addr_copy(maft.keye.mac_addr, mac[i].addr);
|
||||
err = ntmp_maft_add_entry(&pf->si->ntmp_user, i, &maft);
|
||||
if (unlikely(err)) {
|
||||
pf->num_mfe = i;
|
||||
netdev_hw_addr_list_for_each(ha, uc) {
|
||||
ether_addr_copy(maft.keye.mac_addr, ha->addr);
|
||||
err = ntmp_maft_add_entry(&pf->si->ntmp_user, pf->num_mfe,
|
||||
&maft);
|
||||
if (unlikely(err))
|
||||
goto clear_maft_entries;
|
||||
}
|
||||
}
|
||||
|
||||
pf->num_mfe = mac_cnt;
|
||||
pf->num_mfe++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -128,23 +127,29 @@ static int enetc4_pf_add_maft_entries(struct enetc_pf *pf,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int enetc4_pf_set_uc_exact_filter(struct enetc_pf *pf)
|
||||
static void enetc4_pf_set_uc_hash_filter(struct enetc_pf *pf,
|
||||
struct netdev_hw_addr_list *uc)
|
||||
{
|
||||
int max_num_mfe = pf->caps.mac_filter_num;
|
||||
struct enetc_mac_filter mac_filter = {};
|
||||
struct net_device *ndev = pf->si->ndev;
|
||||
struct enetc_mac_addr *mac_tbl;
|
||||
struct enetc_si *si = pf->si;
|
||||
struct enetc_mac_filter *mac_filter = &pf->mac_filter[UC];
|
||||
struct netdev_hw_addr *ha;
|
||||
int i = 0, err;
|
||||
int mac_cnt;
|
||||
u64 hash;
|
||||
|
||||
netif_addr_lock_bh(ndev);
|
||||
enetc_reset_mac_addr_filter(mac_filter);
|
||||
netdev_hw_addr_list_for_each(ha, uc)
|
||||
enetc_add_mac_addr_ht_filter(mac_filter, ha->addr);
|
||||
|
||||
bitmap_to_arr64(&hash, mac_filter->mac_hash_table,
|
||||
ENETC_MADDR_HASH_TBL_SZ);
|
||||
enetc_set_si_uc_hash_filter(pf->si, 0, hash);
|
||||
}
|
||||
|
||||
static int enetc4_pf_set_uc_exact_filter(struct enetc_pf *pf,
|
||||
struct netdev_hw_addr_list *uc)
|
||||
{
|
||||
int mac_cnt = netdev_hw_addr_list_count(uc);
|
||||
struct enetc_si *si = pf->si;
|
||||
|
||||
mac_cnt = netdev_uc_count(ndev);
|
||||
if (!mac_cnt) {
|
||||
netif_addr_unlock_bh(ndev);
|
||||
/* clear both MAC hash and exact filters */
|
||||
enetc_set_si_uc_hash_filter(si, 0, 0);
|
||||
enetc4_pf_clear_maft_entries(pf);
|
||||
@@ -152,79 +157,42 @@ static int enetc4_pf_set_uc_exact_filter(struct enetc_pf *pf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mac_cnt > max_num_mfe) {
|
||||
err = -ENOSPC;
|
||||
goto unlock_netif_addr;
|
||||
}
|
||||
|
||||
mac_tbl = kzalloc_objs(*mac_tbl, mac_cnt, GFP_ATOMIC);
|
||||
if (!mac_tbl) {
|
||||
err = -ENOMEM;
|
||||
goto unlock_netif_addr;
|
||||
}
|
||||
|
||||
netdev_for_each_uc_addr(ha, ndev) {
|
||||
enetc_add_mac_addr_ht_filter(&mac_filter, ha->addr);
|
||||
ether_addr_copy(mac_tbl[i++].addr, ha->addr);
|
||||
}
|
||||
|
||||
netif_addr_unlock_bh(ndev);
|
||||
if (mac_cnt > pf->caps.mac_filter_num)
|
||||
return -ENOSPC;
|
||||
|
||||
/* Set temporary unicast hash filters in case of Rx loss when
|
||||
* updating MAC address filter table
|
||||
*/
|
||||
bitmap_to_arr64(&hash, mac_filter.mac_hash_table,
|
||||
ENETC_MADDR_HASH_TBL_SZ);
|
||||
enetc_set_si_uc_hash_filter(si, 0, hash);
|
||||
enetc4_pf_set_uc_hash_filter(pf, uc);
|
||||
enetc4_pf_clear_maft_entries(pf);
|
||||
|
||||
if (!enetc4_pf_add_maft_entries(pf, mac_tbl, i))
|
||||
if (!enetc4_pf_add_maft_entries(pf, uc)) {
|
||||
enetc_reset_mac_addr_filter(&pf->mac_filter[UC]);
|
||||
enetc_set_si_uc_hash_filter(si, 0, 0);
|
||||
|
||||
kfree(mac_tbl);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unlock_netif_addr:
|
||||
netif_addr_unlock_bh(ndev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void enetc4_pf_set_mac_hash_filter(struct enetc_pf *pf, int type)
|
||||
static void enetc4_pf_set_mc_hash_filter(struct enetc_pf *pf,
|
||||
struct netdev_hw_addr_list *mc)
|
||||
{
|
||||
struct net_device *ndev = pf->si->ndev;
|
||||
struct enetc_mac_filter *mac_filter;
|
||||
struct enetc_si *si = pf->si;
|
||||
struct enetc_mac_filter *mac_filter = &pf->mac_filter[MC];
|
||||
struct netdev_hw_addr *ha;
|
||||
u64 hash;
|
||||
|
||||
netif_addr_lock_bh(ndev);
|
||||
if (type & ENETC_MAC_FILTER_TYPE_UC) {
|
||||
mac_filter = &pf->mac_filter[UC];
|
||||
enetc_reset_mac_addr_filter(mac_filter);
|
||||
netdev_for_each_uc_addr(ha, ndev)
|
||||
enetc_add_mac_addr_ht_filter(mac_filter, ha->addr);
|
||||
enetc_reset_mac_addr_filter(mac_filter);
|
||||
netdev_hw_addr_list_for_each(ha, mc)
|
||||
enetc_add_mac_addr_ht_filter(mac_filter, ha->addr);
|
||||
|
||||
bitmap_to_arr64(&hash, mac_filter->mac_hash_table,
|
||||
ENETC_MADDR_HASH_TBL_SZ);
|
||||
enetc_set_si_uc_hash_filter(si, 0, hash);
|
||||
}
|
||||
|
||||
if (type & ENETC_MAC_FILTER_TYPE_MC) {
|
||||
mac_filter = &pf->mac_filter[MC];
|
||||
enetc_reset_mac_addr_filter(mac_filter);
|
||||
netdev_for_each_mc_addr(ha, ndev)
|
||||
enetc_add_mac_addr_ht_filter(mac_filter, ha->addr);
|
||||
|
||||
bitmap_to_arr64(&hash, mac_filter->mac_hash_table,
|
||||
ENETC_MADDR_HASH_TBL_SZ);
|
||||
enetc_set_si_mc_hash_filter(si, 0, hash);
|
||||
}
|
||||
netif_addr_unlock_bh(ndev);
|
||||
bitmap_to_arr64(&hash, mac_filter->mac_hash_table,
|
||||
ENETC_MADDR_HASH_TBL_SZ);
|
||||
enetc_set_si_mc_hash_filter(pf->si, 0, hash);
|
||||
}
|
||||
|
||||
static void enetc4_pf_set_mac_filter(struct enetc_pf *pf, int type)
|
||||
static void enetc4_pf_set_mac_filter(struct enetc_pf *pf, int type,
|
||||
struct netdev_hw_addr_list *uc,
|
||||
struct netdev_hw_addr_list *mc)
|
||||
{
|
||||
/* Currently, the MAC address filter table (MAFT) only has 4 entries,
|
||||
* and multiple multicast addresses for filtering will be configured
|
||||
@@ -232,15 +200,16 @@ static void enetc4_pf_set_mac_filter(struct enetc_pf *pf, int type)
|
||||
* unicast filtering. If the number of unicast addresses exceeds the
|
||||
* table capacity, the MAC hash filter will be used.
|
||||
*/
|
||||
if (type & ENETC_MAC_FILTER_TYPE_UC && enetc4_pf_set_uc_exact_filter(pf)) {
|
||||
if (type & ENETC_MAC_FILTER_TYPE_UC &&
|
||||
enetc4_pf_set_uc_exact_filter(pf, uc)) {
|
||||
/* Fall back to the MAC hash filter */
|
||||
enetc4_pf_set_mac_hash_filter(pf, ENETC_MAC_FILTER_TYPE_UC);
|
||||
enetc4_pf_set_uc_hash_filter(pf, uc);
|
||||
/* Clear the old MAC exact filter */
|
||||
enetc4_pf_clear_maft_entries(pf);
|
||||
}
|
||||
|
||||
if (type & ENETC_MAC_FILTER_TYPE_MC)
|
||||
enetc4_pf_set_mac_hash_filter(pf, ENETC_MAC_FILTER_TYPE_MC);
|
||||
enetc4_pf_set_mc_hash_filter(pf, mc);
|
||||
}
|
||||
|
||||
static const struct enetc_pf_ops enetc4_pf_ops = {
|
||||
@@ -467,17 +436,17 @@ static void enetc4_pf_free(struct enetc_pf *pf)
|
||||
enetc4_free_ntmp_user(pf->si);
|
||||
}
|
||||
|
||||
static void enetc4_psi_do_set_rx_mode(struct work_struct *work)
|
||||
static int enetc4_pf_set_rx_mode(struct net_device *ndev,
|
||||
struct netdev_hw_addr_list *uc,
|
||||
struct netdev_hw_addr_list *mc)
|
||||
{
|
||||
struct enetc_si *si = container_of(work, struct enetc_si, rx_mode_task);
|
||||
struct enetc_pf *pf = enetc_si_priv(si);
|
||||
struct net_device *ndev = si->ndev;
|
||||
struct enetc_ndev_priv *priv = netdev_priv(ndev);
|
||||
struct enetc_pf *pf = enetc_si_priv(priv->si);
|
||||
struct enetc_si *si = priv->si;
|
||||
bool uc_promisc = false;
|
||||
bool mc_promisc = false;
|
||||
int type = 0;
|
||||
|
||||
rtnl_lock();
|
||||
|
||||
if (ndev->flags & IFF_PROMISC) {
|
||||
uc_promisc = true;
|
||||
mc_promisc = true;
|
||||
@@ -500,17 +469,9 @@ static void enetc4_psi_do_set_rx_mode(struct work_struct *work)
|
||||
enetc_set_si_mc_hash_filter(si, 0, 0);
|
||||
|
||||
/* Set new MAC filter */
|
||||
enetc4_pf_set_mac_filter(pf, type);
|
||||
enetc4_pf_set_mac_filter(pf, type, uc, mc);
|
||||
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
static void enetc4_pf_set_rx_mode(struct net_device *ndev)
|
||||
{
|
||||
struct enetc_ndev_priv *priv = netdev_priv(ndev);
|
||||
struct enetc_si *si = priv->si;
|
||||
|
||||
queue_work(si->workqueue, &si->rx_mode_task);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enetc4_pf_set_features(struct net_device *ndev,
|
||||
@@ -540,7 +501,7 @@ static const struct net_device_ops enetc4_ndev_ops = {
|
||||
.ndo_start_xmit = enetc_xmit,
|
||||
.ndo_get_stats = enetc_get_stats,
|
||||
.ndo_set_mac_address = enetc_pf_set_mac_addr,
|
||||
.ndo_set_rx_mode = enetc4_pf_set_rx_mode,
|
||||
.ndo_set_rx_mode_async = enetc4_pf_set_rx_mode,
|
||||
.ndo_set_features = enetc4_pf_set_features,
|
||||
.ndo_vlan_rx_add_vid = enetc_vlan_rx_add_vid,
|
||||
.ndo_vlan_rx_kill_vid = enetc_vlan_rx_del_vid,
|
||||
@@ -983,19 +944,6 @@ static void enetc4_link_deinit(struct enetc_ndev_priv *priv)
|
||||
enetc_mdiobus_destroy(pf);
|
||||
}
|
||||
|
||||
static int enetc4_psi_wq_task_init(struct enetc_si *si)
|
||||
{
|
||||
char wq_name[24];
|
||||
|
||||
INIT_WORK(&si->rx_mode_task, enetc4_psi_do_set_rx_mode);
|
||||
snprintf(wq_name, sizeof(wq_name), "enetc-%s", pci_name(si->pdev));
|
||||
si->workqueue = create_singlethread_workqueue(wq_name);
|
||||
if (!si->workqueue)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enetc4_pf_netdev_create(struct enetc_si *si)
|
||||
{
|
||||
struct device *dev = &si->pdev->dev;
|
||||
@@ -1036,12 +984,6 @@ static int enetc4_pf_netdev_create(struct enetc_si *si)
|
||||
if (err)
|
||||
goto err_link_init;
|
||||
|
||||
err = enetc4_psi_wq_task_init(si);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to init workqueue\n");
|
||||
goto err_wq_init;
|
||||
}
|
||||
|
||||
err = register_netdev(ndev);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to register netdev\n");
|
||||
@@ -1051,8 +993,6 @@ static int enetc4_pf_netdev_create(struct enetc_si *si)
|
||||
return 0;
|
||||
|
||||
err_reg_netdev:
|
||||
destroy_workqueue(si->workqueue);
|
||||
err_wq_init:
|
||||
enetc4_link_deinit(priv);
|
||||
err_link_init:
|
||||
enetc_free_msix(priv);
|
||||
@@ -1070,8 +1010,6 @@ static void enetc4_pf_netdev_destroy(struct enetc_si *si)
|
||||
struct net_device *ndev = si->ndev;
|
||||
|
||||
unregister_netdev(ndev);
|
||||
cancel_work(&si->rx_mode_task);
|
||||
destroy_workqueue(si->workqueue);
|
||||
enetc4_link_deinit(priv);
|
||||
enetc_free_msix(priv);
|
||||
free_netdev(ndev);
|
||||
|
||||
Reference in New Issue
Block a user