Commit 6960d133 authored by Íñigo Huguet's avatar Íñigo Huguet Committed by David S. Miller

atlantic: fix deadlock at aq_nic_stop

NIC is stopped with rtnl_lock held, and during the stop it cancels the
'service_task' work and free irqs.

However, if CONFIG_MACSEC is set, rtnl_lock is acquired both from
aq_nic_service_task and aq_linkstate_threaded_isr. Then a deadlock
happens if aq_nic_stop tries to cancel/disable them when they've already
started their execution.

As the deadlock is caused by rtnl_lock, it causes many other processes
to stall, not only atlantic related stuff.

Fix it by introducing a mutex that protects each NIC's macsec related
data, and locking it instead of the rtnl_lock from the service task and
the threaded IRQ.

Before this patch, all macsec data was protected with rtnl_lock, but
maybe not all of it needs to be protected. With this new mutex, further
efforts can be made to limit the protected data only to that which
requires it. However, probably it doesn't worth it because all macsec's
data accesses are infrequent, and almost all are done from macsec_ops
or ethtool callbacks, called holding rtnl_lock, so macsec_mutex won't
never be much contended.

The issue appeared repeteadly attaching and deattaching the NIC to a
bond interface. Doing that after this patch I cannot reproduce the bug.

Fixes: 62c1c2e6 ("net: atlantic: MACSec offload skeleton")
Reported-by: default avatarLi Liang <liali@redhat.com>
Suggested-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarÍñigo Huguet <ihuguet@redhat.com>
Reviewed-by: default avatarIgor Russkikh <irusskikh@marvell.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0bda0362
...@@ -1394,26 +1394,57 @@ static void aq_check_txsa_expiration(struct aq_nic_s *nic) ...@@ -1394,26 +1394,57 @@ static void aq_check_txsa_expiration(struct aq_nic_s *nic)
egress_sa_threshold_expired); egress_sa_threshold_expired);
} }
#define AQ_LOCKED_MDO_DEF(mdo) \
static int aq_locked_mdo_##mdo(struct macsec_context *ctx) \
{ \
struct aq_nic_s *nic = netdev_priv(ctx->netdev); \
int ret; \
mutex_lock(&nic->macsec_mutex); \
ret = aq_mdo_##mdo(ctx); \
mutex_unlock(&nic->macsec_mutex); \
return ret; \
}
AQ_LOCKED_MDO_DEF(dev_open)
AQ_LOCKED_MDO_DEF(dev_stop)
AQ_LOCKED_MDO_DEF(add_secy)
AQ_LOCKED_MDO_DEF(upd_secy)
AQ_LOCKED_MDO_DEF(del_secy)
AQ_LOCKED_MDO_DEF(add_rxsc)
AQ_LOCKED_MDO_DEF(upd_rxsc)
AQ_LOCKED_MDO_DEF(del_rxsc)
AQ_LOCKED_MDO_DEF(add_rxsa)
AQ_LOCKED_MDO_DEF(upd_rxsa)
AQ_LOCKED_MDO_DEF(del_rxsa)
AQ_LOCKED_MDO_DEF(add_txsa)
AQ_LOCKED_MDO_DEF(upd_txsa)
AQ_LOCKED_MDO_DEF(del_txsa)
AQ_LOCKED_MDO_DEF(get_dev_stats)
AQ_LOCKED_MDO_DEF(get_tx_sc_stats)
AQ_LOCKED_MDO_DEF(get_tx_sa_stats)
AQ_LOCKED_MDO_DEF(get_rx_sc_stats)
AQ_LOCKED_MDO_DEF(get_rx_sa_stats)
const struct macsec_ops aq_macsec_ops = { const struct macsec_ops aq_macsec_ops = {
.mdo_dev_open = aq_mdo_dev_open, .mdo_dev_open = aq_locked_mdo_dev_open,
.mdo_dev_stop = aq_mdo_dev_stop, .mdo_dev_stop = aq_locked_mdo_dev_stop,
.mdo_add_secy = aq_mdo_add_secy, .mdo_add_secy = aq_locked_mdo_add_secy,
.mdo_upd_secy = aq_mdo_upd_secy, .mdo_upd_secy = aq_locked_mdo_upd_secy,
.mdo_del_secy = aq_mdo_del_secy, .mdo_del_secy = aq_locked_mdo_del_secy,
.mdo_add_rxsc = aq_mdo_add_rxsc, .mdo_add_rxsc = aq_locked_mdo_add_rxsc,
.mdo_upd_rxsc = aq_mdo_upd_rxsc, .mdo_upd_rxsc = aq_locked_mdo_upd_rxsc,
.mdo_del_rxsc = aq_mdo_del_rxsc, .mdo_del_rxsc = aq_locked_mdo_del_rxsc,
.mdo_add_rxsa = aq_mdo_add_rxsa, .mdo_add_rxsa = aq_locked_mdo_add_rxsa,
.mdo_upd_rxsa = aq_mdo_upd_rxsa, .mdo_upd_rxsa = aq_locked_mdo_upd_rxsa,
.mdo_del_rxsa = aq_mdo_del_rxsa, .mdo_del_rxsa = aq_locked_mdo_del_rxsa,
.mdo_add_txsa = aq_mdo_add_txsa, .mdo_add_txsa = aq_locked_mdo_add_txsa,
.mdo_upd_txsa = aq_mdo_upd_txsa, .mdo_upd_txsa = aq_locked_mdo_upd_txsa,
.mdo_del_txsa = aq_mdo_del_txsa, .mdo_del_txsa = aq_locked_mdo_del_txsa,
.mdo_get_dev_stats = aq_mdo_get_dev_stats, .mdo_get_dev_stats = aq_locked_mdo_get_dev_stats,
.mdo_get_tx_sc_stats = aq_mdo_get_tx_sc_stats, .mdo_get_tx_sc_stats = aq_locked_mdo_get_tx_sc_stats,
.mdo_get_tx_sa_stats = aq_mdo_get_tx_sa_stats, .mdo_get_tx_sa_stats = aq_locked_mdo_get_tx_sa_stats,
.mdo_get_rx_sc_stats = aq_mdo_get_rx_sc_stats, .mdo_get_rx_sc_stats = aq_locked_mdo_get_rx_sc_stats,
.mdo_get_rx_sa_stats = aq_mdo_get_rx_sa_stats, .mdo_get_rx_sa_stats = aq_locked_mdo_get_rx_sa_stats,
}; };
int aq_macsec_init(struct aq_nic_s *nic) int aq_macsec_init(struct aq_nic_s *nic)
...@@ -1435,6 +1466,7 @@ int aq_macsec_init(struct aq_nic_s *nic) ...@@ -1435,6 +1466,7 @@ int aq_macsec_init(struct aq_nic_s *nic)
nic->ndev->features |= NETIF_F_HW_MACSEC; nic->ndev->features |= NETIF_F_HW_MACSEC;
nic->ndev->macsec_ops = &aq_macsec_ops; nic->ndev->macsec_ops = &aq_macsec_ops;
mutex_init(&nic->macsec_mutex);
return 0; return 0;
} }
...@@ -1458,7 +1490,7 @@ int aq_macsec_enable(struct aq_nic_s *nic) ...@@ -1458,7 +1490,7 @@ int aq_macsec_enable(struct aq_nic_s *nic)
if (!nic->macsec_cfg) if (!nic->macsec_cfg)
return 0; return 0;
rtnl_lock(); mutex_lock(&nic->macsec_mutex);
if (nic->aq_fw_ops->send_macsec_req) { if (nic->aq_fw_ops->send_macsec_req) {
struct macsec_cfg_request cfg = { 0 }; struct macsec_cfg_request cfg = { 0 };
...@@ -1507,7 +1539,7 @@ int aq_macsec_enable(struct aq_nic_s *nic) ...@@ -1507,7 +1539,7 @@ int aq_macsec_enable(struct aq_nic_s *nic)
ret = aq_apply_macsec_cfg(nic); ret = aq_apply_macsec_cfg(nic);
unlock: unlock:
rtnl_unlock(); mutex_unlock(&nic->macsec_mutex);
return ret; return ret;
} }
...@@ -1519,9 +1551,9 @@ void aq_macsec_work(struct aq_nic_s *nic) ...@@ -1519,9 +1551,9 @@ void aq_macsec_work(struct aq_nic_s *nic)
if (!netif_carrier_ok(nic->ndev)) if (!netif_carrier_ok(nic->ndev))
return; return;
rtnl_lock(); mutex_lock(&nic->macsec_mutex);
aq_check_txsa_expiration(nic); aq_check_txsa_expiration(nic);
rtnl_unlock(); mutex_unlock(&nic->macsec_mutex);
} }
int aq_macsec_rx_sa_cnt(struct aq_nic_s *nic) int aq_macsec_rx_sa_cnt(struct aq_nic_s *nic)
...@@ -1532,21 +1564,30 @@ int aq_macsec_rx_sa_cnt(struct aq_nic_s *nic) ...@@ -1532,21 +1564,30 @@ int aq_macsec_rx_sa_cnt(struct aq_nic_s *nic)
if (!cfg) if (!cfg)
return 0; return 0;
mutex_lock(&nic->macsec_mutex);
for (i = 0; i < AQ_MACSEC_MAX_SC; i++) { for (i = 0; i < AQ_MACSEC_MAX_SC; i++) {
if (!test_bit(i, &cfg->rxsc_idx_busy)) if (!test_bit(i, &cfg->rxsc_idx_busy))
continue; continue;
cnt += hweight_long(cfg->aq_rxsc[i].rx_sa_idx_busy); cnt += hweight_long(cfg->aq_rxsc[i].rx_sa_idx_busy);
} }
mutex_unlock(&nic->macsec_mutex);
return cnt; return cnt;
} }
int aq_macsec_tx_sc_cnt(struct aq_nic_s *nic) int aq_macsec_tx_sc_cnt(struct aq_nic_s *nic)
{ {
int cnt;
if (!nic->macsec_cfg) if (!nic->macsec_cfg)
return 0; return 0;
return hweight_long(nic->macsec_cfg->txsc_idx_busy); mutex_lock(&nic->macsec_mutex);
cnt = hweight_long(nic->macsec_cfg->txsc_idx_busy);
mutex_unlock(&nic->macsec_mutex);
return cnt;
} }
int aq_macsec_tx_sa_cnt(struct aq_nic_s *nic) int aq_macsec_tx_sa_cnt(struct aq_nic_s *nic)
...@@ -1557,12 +1598,15 @@ int aq_macsec_tx_sa_cnt(struct aq_nic_s *nic) ...@@ -1557,12 +1598,15 @@ int aq_macsec_tx_sa_cnt(struct aq_nic_s *nic)
if (!cfg) if (!cfg)
return 0; return 0;
mutex_lock(&nic->macsec_mutex);
for (i = 0; i < AQ_MACSEC_MAX_SC; i++) { for (i = 0; i < AQ_MACSEC_MAX_SC; i++) {
if (!test_bit(i, &cfg->txsc_idx_busy)) if (!test_bit(i, &cfg->txsc_idx_busy))
continue; continue;
cnt += hweight_long(cfg->aq_txsc[i].tx_sa_idx_busy); cnt += hweight_long(cfg->aq_txsc[i].tx_sa_idx_busy);
} }
mutex_unlock(&nic->macsec_mutex);
return cnt; return cnt;
} }
...@@ -1634,6 +1678,8 @@ u64 *aq_macsec_get_stats(struct aq_nic_s *nic, u64 *data) ...@@ -1634,6 +1678,8 @@ u64 *aq_macsec_get_stats(struct aq_nic_s *nic, u64 *data)
if (!cfg) if (!cfg)
return data; return data;
mutex_lock(&nic->macsec_mutex);
aq_macsec_update_stats(nic); aq_macsec_update_stats(nic);
common_stats = &cfg->stats; common_stats = &cfg->stats;
...@@ -1716,5 +1762,7 @@ u64 *aq_macsec_get_stats(struct aq_nic_s *nic, u64 *data) ...@@ -1716,5 +1762,7 @@ u64 *aq_macsec_get_stats(struct aq_nic_s *nic, u64 *data)
data += i; data += i;
mutex_unlock(&nic->macsec_mutex);
return data; return data;
} }
...@@ -157,6 +157,8 @@ struct aq_nic_s { ...@@ -157,6 +157,8 @@ struct aq_nic_s {
struct mutex fwreq_mutex; struct mutex fwreq_mutex;
#if IS_ENABLED(CONFIG_MACSEC) #if IS_ENABLED(CONFIG_MACSEC)
struct aq_macsec_cfg *macsec_cfg; struct aq_macsec_cfg *macsec_cfg;
/* mutex to protect data in macsec_cfg */
struct mutex macsec_mutex;
#endif #endif
/* PTP support */ /* PTP support */
struct aq_ptp_s *aq_ptp; struct aq_ptp_s *aq_ptp;
......
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