From: Jinliang Zheng crypto_free_aead() can call vunmap() internally (e.g. via dma_free_attrs() in hardware crypto drivers like hisi_sec2), which must not be called from softirq context. free_rxsa() is an RCU callback and therefore runs in softirq context, causing a kernel crash when the underlying AEAD implementation performs DMA unmapping during tfm destruction: vunmap+0x4c/0x70 __iommu_dma_free+0xd0/0x138 dma_free_attrs+0xf4/0x100 sec_aead_exit+0x64/0xb8 [hisi_sec2] crypto_destroy_tfm+0x98/0x110 free_rxsa+0x28/0x50 [macsec] rcu_do_batch+0x184/0x460 rcu_core+0xf4/0x1f8 handle_softirqs+0x118/0x330 Fix this by splitting free_rxsa() into two parts: the RCU callback now only schedules a work item, and the actual resource release (crypto_free_aead, free_percpu, kfree) is done in a workqueue handler running in process context. Add a destroy_work field to struct macsec_rx_sa and initialize it in init_rx_sa(). Signed-off-by: Jinliang Zheng --- drivers/net/macsec.c | 13 +++++++++++-- include/net/macsec.h | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index f6cad0746a02..dabd3d2598ae 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -174,15 +174,23 @@ static void macsec_rxsc_put(struct macsec_rx_sc *sc) call_rcu(&sc->rcu_head, free_rx_sc_rcu); } -static void free_rxsa(struct rcu_head *head) +static void free_rxsa_work(struct work_struct *work) { - struct macsec_rx_sa *sa = container_of(head, struct macsec_rx_sa, rcu); + struct macsec_rx_sa *sa = container_of(work, struct macsec_rx_sa, + destroy_work); crypto_free_aead(sa->key.tfm); free_percpu(sa->stats); kfree(sa); } +static void free_rxsa(struct rcu_head *head) +{ + struct macsec_rx_sa *sa = container_of(head, struct macsec_rx_sa, rcu); + + schedule_work(&sa->destroy_work); +} + static void macsec_rxsa_put(struct macsec_rx_sa *sa) { if (refcount_dec_and_test(&sa->refcnt)) @@ -1407,6 +1415,7 @@ static int init_rx_sa(struct macsec_rx_sa *rx_sa, char *sak, int key_len, rx_sa->next_pn = 1; refcount_set(&rx_sa->refcnt, 1); spin_lock_init(&rx_sa->lock); + INIT_WORK(&rx_sa->destroy_work, free_rxsa_work); return 0; } diff --git a/include/net/macsec.h b/include/net/macsec.h index bc7de5b53e54..aeacd361f686 100644 --- a/include/net/macsec.h +++ b/include/net/macsec.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -137,6 +138,7 @@ struct macsec_rx_sa { struct macsec_rx_sa_stats __percpu *stats; struct macsec_rx_sc *sc; struct rcu_head rcu; + struct work_struct destroy_work; }; struct pcpu_rx_sc_stats { -- 2.39.3