staging: rtl8192u: Fix sleep in atomic context bug in dm_fsync_timer_callback
authorDuoming Zhou <duoming@zju.edu.cn>
Sun, 10 Jul 2022 10:30:02 +0000 (18:30 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 17 Aug 2022 12:23:50 +0000 (14:23 +0200)
[ Upstream commit 6a0c054930d554ad8f8044ef1fc856d9da391c81 ]

There are sleep in atomic context bugs when dm_fsync_timer_callback is
executing. The root cause is that the memory allocation functions with
GFP_KERNEL or GFP_NOIO parameters are called in dm_fsync_timer_callback
which is a timer handler. The call paths that could trigger bugs are
shown below:

    (interrupt context)
dm_fsync_timer_callback
  write_nic_byte
    kzalloc(sizeof(data), GFP_KERNEL); //may sleep
    usb_control_msg
      kmalloc(.., GFP_NOIO); //may sleep
  write_nic_dword
    kzalloc(sizeof(data), GFP_KERNEL); //may sleep
    usb_control_msg
      kmalloc(.., GFP_NOIO); //may sleep

This patch uses delayed work to replace timer and moves the operations
that may sleep into the delayed work in order to mitigate bugs.

Fixes: 8fc8598e61f6 ("Staging: Added Realtek rtl8192u driver to staging")
Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
Link: https://lore.kernel.org/r/20220710103002.63283-1-duoming@zju.edu.cn
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/staging/rtl8192u/r8192U.h
drivers/staging/rtl8192u/r8192U_dm.c
drivers/staging/rtl8192u/r8192U_dm.h

index 4013107..a23d6d4 100644 (file)
@@ -1013,7 +1013,7 @@ typedef struct r8192_priv {
        bool            bis_any_nonbepkts;
        bool            bcurrent_turbo_EDCA;
        bool            bis_cur_rdlstate;
-       struct timer_list fsync_timer;
+       struct delayed_work fsync_work;
        bool bfsync_processing; /* 500ms Fsync timer is active or not */
        u32     rate_record;
        u32     rateCountDiffRecord;
index 725bf5c..0fcfcaa 100644 (file)
@@ -2578,19 +2578,20 @@ static void dm_init_fsync(struct net_device *dev)
        priv->ieee80211->fsync_seconddiff_ratethreshold = 200;
        priv->ieee80211->fsync_state = Default_Fsync;
        priv->framesyncMonitor = 1;     /* current default 0xc38 monitor on */
-       timer_setup(&priv->fsync_timer, dm_fsync_timer_callback, 0);
+       INIT_DELAYED_WORK(&priv->fsync_work, dm_fsync_work_callback);
 }
 
 static void dm_deInit_fsync(struct net_device *dev)
 {
        struct r8192_priv *priv = ieee80211_priv(dev);
 
-       del_timer_sync(&priv->fsync_timer);
+       cancel_delayed_work_sync(&priv->fsync_work);
 }
 
-void dm_fsync_timer_callback(struct timer_list *t)
+void dm_fsync_work_callback(struct work_struct *work)
 {
-       struct r8192_priv *priv = from_timer(priv, t, fsync_timer);
+       struct r8192_priv *priv =
+           container_of(work, struct r8192_priv, fsync_work.work);
        struct net_device *dev = priv->ieee80211->dev;
        u32 rate_index, rate_count = 0, rate_count_diff = 0;
        bool            bSwitchFromCountDiff = false;
@@ -2657,17 +2658,16 @@ void dm_fsync_timer_callback(struct timer_list *t)
                        }
                }
                if (bDoubleTimeInterval) {
-                       if (timer_pending(&priv->fsync_timer))
-                               del_timer_sync(&priv->fsync_timer);
-                       priv->fsync_timer.expires = jiffies +
-                               msecs_to_jiffies(priv->ieee80211->fsync_time_interval*priv->ieee80211->fsync_multiple_timeinterval);
-                       add_timer(&priv->fsync_timer);
+                       cancel_delayed_work_sync(&priv->fsync_work);
+                       schedule_delayed_work(&priv->fsync_work,
+                                             msecs_to_jiffies(priv
+                                             ->ieee80211->fsync_time_interval *
+                                             priv->ieee80211->fsync_multiple_timeinterval));
                } else {
-                       if (timer_pending(&priv->fsync_timer))
-                               del_timer_sync(&priv->fsync_timer);
-                       priv->fsync_timer.expires = jiffies +
-                               msecs_to_jiffies(priv->ieee80211->fsync_time_interval);
-                       add_timer(&priv->fsync_timer);
+                       cancel_delayed_work_sync(&priv->fsync_work);
+                       schedule_delayed_work(&priv->fsync_work,
+                                             msecs_to_jiffies(priv
+                                             ->ieee80211->fsync_time_interval));
                }
        } else {
                /* Let Register return to default value; */
@@ -2695,7 +2695,7 @@ static void dm_EndSWFsync(struct net_device *dev)
        struct r8192_priv *priv = ieee80211_priv(dev);
 
        RT_TRACE(COMP_HALDM, "%s\n", __func__);
-       del_timer_sync(&(priv->fsync_timer));
+       cancel_delayed_work_sync(&priv->fsync_work);
 
        /* Let Register return to default value; */
        if (priv->bswitch_fsync) {
@@ -2736,11 +2736,9 @@ static void dm_StartSWFsync(struct net_device *dev)
                if (priv->ieee80211->fsync_rate_bitmap &  rateBitmap)
                        priv->rate_record += priv->stats.received_rate_histogram[1][rateIndex];
        }
-       if (timer_pending(&priv->fsync_timer))
-               del_timer_sync(&priv->fsync_timer);
-       priv->fsync_timer.expires = jiffies +
-                       msecs_to_jiffies(priv->ieee80211->fsync_time_interval);
-       add_timer(&priv->fsync_timer);
+       cancel_delayed_work_sync(&priv->fsync_work);
+       schedule_delayed_work(&priv->fsync_work,
+                             msecs_to_jiffies(priv->ieee80211->fsync_time_interval));
 
        write_nic_dword(dev, rOFDM0_RxDetector2, 0x465c12cd);
 }
index 0b2a1c6..2159018 100644 (file)
@@ -166,7 +166,7 @@ void dm_force_tx_fw_info(struct net_device *dev,
 void dm_init_edca_turbo(struct net_device *dev);
 void dm_rf_operation_test_callback(unsigned long data);
 void dm_rf_pathcheck_workitemcallback(struct work_struct *work);
-void dm_fsync_timer_callback(struct timer_list *t);
+void dm_fsync_work_callback(struct work_struct *work);
 void dm_cck_txpower_adjust(struct net_device *dev, bool  binch14);
 void dm_shadow_init(struct net_device *dev);
 void dm_initialize_txpower_tracking(struct net_device *dev);