wifi: wilc1000: add missing read critical sections around vif list traversal
authorAjay Singh <ajay.kathat@microchip.com>
Thu, 15 Feb 2024 15:36:21 +0000 (16:36 +0100)
committerKalle Valo <kvalo@kernel.org>
Mon, 19 Feb 2024 16:21:36 +0000 (18:21 +0200)
Some code manipulating the vif list is still missing some srcu_read_lock /
srcu_read_unlock, and so can trigger RCU warnings:

=============================
WARNING: suspicious RCU usage
6.8.0-rc1+ #37 Not tainted
-----------------------------
drivers/net/wireless/microchip/wilc1000/hif.c:110 RCU-list traversed without holding the required lock!!
[...]
stack backtrace:
CPU: 0 PID: 6 Comm: kworker/0:0 Not tainted 6.8.0-rc1+ #37
Hardware name: Atmel SAMA5
Workqueue: events sdio_irq_work
 unwind_backtrace from show_stack+0x18/0x1c
 show_stack from dump_stack_lvl+0x34/0x58
 dump_stack_lvl from wilc_get_vif_from_idx+0x158/0x180
 wilc_get_vif_from_idx from wilc_network_info_received+0x80/0x48c
 wilc_network_info_received from wilc_handle_isr+0xa10/0xd30
 wilc_handle_isr from wilc_sdio_interrupt+0x44/0x58
 wilc_sdio_interrupt from process_sdio_pending_irqs+0x1c8/0x60c
 process_sdio_pending_irqs from sdio_irq_work+0x6c/0x14c
 sdio_irq_work from process_one_work+0x8d4/0x169c
 process_one_work from worker_thread+0x8cc/0x1340
 worker_thread from kthread+0x448/0x510
 kthread from ret_from_fork+0x14/0x28

Fix those warnings by adding the needed lock around the corresponding
critical sections

Signed-off-by: Ajay Singh <ajay.kathat@microchip.com>
Co-developed-by: Alexis Lothoré <alexis.lothore@bootlin.com>
Signed-off-by: Alexis Lothoré <alexis.lothore@bootlin.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://msgid.link/20240215-wilc_fix_rcu_usage-v1-4-f610e46c6f82@bootlin.com
drivers/net/wireless/microchip/wilc1000/hif.c
drivers/net/wireless/microchip/wilc1000/netdev.c

index c42859a727c3be1db16dac55e3cc460132e7260b..f1085ccb7eedc025aba6a6cf45ecd1bc4edef66c 100644 (file)
@@ -1570,23 +1570,25 @@ void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length)
        struct host_if_drv *hif_drv;
        struct host_if_msg *msg;
        struct wilc_vif *vif;
+       int srcu_idx;
        int result;
        int id;
 
        id = get_unaligned_le32(&buffer[length - 4]);
+       srcu_idx = srcu_read_lock(&wilc->srcu);
        vif = wilc_get_vif_from_idx(wilc, id);
        if (!vif)
-               return;
-       hif_drv = vif->hif_drv;
+               goto out;
 
+       hif_drv = vif->hif_drv;
        if (!hif_drv) {
                netdev_err(vif->ndev, "driver not init[%p]\n", hif_drv);
-               return;
+               goto out;
        }
 
        msg = wilc_alloc_work(vif, handle_rcvd_ntwrk_info, false);
        if (IS_ERR(msg))
-               return;
+               goto out;
 
        msg->body.net_info.frame_len = get_unaligned_le16(&buffer[6]) - 1;
        msg->body.net_info.rssi = buffer[8];
@@ -1595,7 +1597,7 @@ void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length)
                                          GFP_KERNEL);
        if (!msg->body.net_info.mgmt) {
                kfree(msg);
-               return;
+               goto out;
        }
 
        result = wilc_enqueue_work(msg);
@@ -1604,6 +1606,8 @@ void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length)
                kfree(msg->body.net_info.mgmt);
                kfree(msg);
        }
+out:
+       srcu_read_unlock(&wilc->srcu, srcu_idx);
 }
 
 void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length)
@@ -1611,36 +1615,32 @@ void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length)
        struct host_if_drv *hif_drv;
        struct host_if_msg *msg;
        struct wilc_vif *vif;
+       int srcu_idx;
        int result;
        int id;
 
        mutex_lock(&wilc->deinit_lock);
 
        id = get_unaligned_le32(&buffer[length - 4]);
+       srcu_idx = srcu_read_lock(&wilc->srcu);
        vif = wilc_get_vif_from_idx(wilc, id);
-       if (!vif) {
-               mutex_unlock(&wilc->deinit_lock);
-               return;
-       }
+       if (!vif)
+               goto out;
 
        hif_drv = vif->hif_drv;
 
        if (!hif_drv) {
-               mutex_unlock(&wilc->deinit_lock);
-               return;
+               goto out;
        }
 
        if (!hif_drv->conn_info.conn_result) {
                netdev_err(vif->ndev, "%s: conn_result is NULL\n", __func__);
-               mutex_unlock(&wilc->deinit_lock);
-               return;
+               goto out;
        }
 
        msg = wilc_alloc_work(vif, handle_rcvd_gnrl_async_info, false);
-       if (IS_ERR(msg)) {
-               mutex_unlock(&wilc->deinit_lock);
-               return;
-       }
+       if (IS_ERR(msg))
+               goto out;
 
        msg->body.mac_info.status = buffer[7];
        result = wilc_enqueue_work(msg);
@@ -1648,7 +1648,8 @@ void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length)
                netdev_err(vif->ndev, "%s: enqueue work failed\n", __func__);
                kfree(msg);
        }
-
+out:
+       srcu_read_unlock(&wilc->srcu, srcu_idx);
        mutex_unlock(&wilc->deinit_lock);
 }
 
@@ -1656,24 +1657,27 @@ void wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length)
 {
        struct host_if_drv *hif_drv;
        struct wilc_vif *vif;
+       int srcu_idx;
        int result;
        int id;
 
        id = get_unaligned_le32(&buffer[length - 4]);
+       srcu_idx = srcu_read_lock(&wilc->srcu);
        vif = wilc_get_vif_from_idx(wilc, id);
        if (!vif)
-               return;
-       hif_drv = vif->hif_drv;
+               goto out;
 
-       if (!hif_drv)
-               return;
+       hif_drv = vif->hif_drv;
+       if (!hif_drv) {
+               goto out;
+       }
 
        if (hif_drv->usr_scan_req.scan_result) {
                struct host_if_msg *msg;
 
                msg = wilc_alloc_work(vif, handle_scan_complete, false);
                if (IS_ERR(msg))
-                       return;
+                       goto out;
 
                result = wilc_enqueue_work(msg);
                if (result) {
@@ -1682,6 +1686,8 @@ void wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length)
                        kfree(msg);
                }
        }
+out:
+       srcu_read_unlock(&wilc->srcu, srcu_idx);
 }
 
 int wilc_remain_on_channel(struct wilc_vif *vif, u64 cookie, u16 chan,
index 092801d339154ec6bc813d35924cbe0a6b4bf996..710e29bea5605843a8c2e78406de3d8242bbb16c 100644 (file)
@@ -819,14 +819,16 @@ void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size,
        unsigned int frame_len = 0;
        struct wilc_vif *vif;
        struct sk_buff *skb;
+       int srcu_idx;
        int stats;
 
        if (!wilc)
                return;
 
+       srcu_idx = srcu_read_lock(&wilc->srcu);
        wilc_netdev = get_if_handler(wilc, buff);
        if (!wilc_netdev)
-               return;
+               goto out;
 
        buff += pkt_offset;
        vif = netdev_priv(wilc_netdev);
@@ -837,7 +839,7 @@ void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size,
 
                skb = dev_alloc_skb(frame_len);
                if (!skb)
-                       return;
+                       goto out;
 
                skb->dev = wilc_netdev;
 
@@ -850,6 +852,8 @@ void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size,
                stats = netif_rx(skb);
                netdev_dbg(wilc_netdev, "netif_rx ret value is: %d\n", stats);
        }
+out:
+       srcu_read_unlock(&wilc->srcu, srcu_idx);
 }
 
 void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size, bool is_auth)