ath9k_htc: avoid scheduling while atomic on sta_rc_update
authorStanislaw Gruszka <sgruszka@redhat.com>
Mon, 3 Feb 2014 10:45:51 +0000 (11:45 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 4 Feb 2014 20:30:07 +0000 (15:30 -0500)
mac80211 ->sta_rc_update() callback must be atomic. Since we have to
take mutex and do other operations that can sleep when sending fimrware
commands to device, the only option to satisfy atomicity requirement of
->sta_rc_update(), that I can see, is introduce work_struct and defer
uploading new rates to that work.

Tested-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/htc.h
drivers/net/wireless/ath/ath9k/htc_drv_main.c

index 58da3468d1f0ac1b3de6522b7118640f2c80da76..99a203174f45a04b50248a370ca4113e256e75ca 100644 (file)
@@ -262,6 +262,8 @@ enum tid_aggr_state {
 struct ath9k_htc_sta {
        u8 index;
        enum tid_aggr_state tid_state[ATH9K_HTC_MAX_TID];
+       struct work_struct rc_update_work;
+       struct ath9k_htc_priv *htc_priv;
 };
 
 #define ATH9K_HTC_RXBUF 256
index a57af9b96a3931f2863dfaf91d1a59682cd46526..c9254a61ca52d0984c23efdf4cb01aeb6b40ae23 100644 (file)
@@ -1270,18 +1270,50 @@ static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
        mutex_unlock(&priv->mutex);
 }
 
+static void ath9k_htc_sta_rc_update_work(struct work_struct *work)
+{
+       struct ath9k_htc_sta *ista =
+           container_of(work, struct ath9k_htc_sta, rc_update_work);
+       struct ieee80211_sta *sta =
+           container_of((void *)ista, struct ieee80211_sta, drv_priv);
+       struct ath9k_htc_priv *priv = ista->htc_priv;
+       struct ath_common *common = ath9k_hw_common(priv->ah);
+       struct ath9k_htc_target_rate trate;
+
+       mutex_lock(&priv->mutex);
+       ath9k_htc_ps_wakeup(priv);
+
+       memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
+       ath9k_htc_setup_rate(priv, sta, &trate);
+       if (!ath9k_htc_send_rate_cmd(priv, &trate))
+               ath_dbg(common, CONFIG,
+                       "Supported rates for sta: %pM updated, rate caps: 0x%X\n",
+                       sta->addr, be32_to_cpu(trate.capflags));
+       else
+               ath_dbg(common, CONFIG,
+                       "Unable to update supported rates for sta: %pM\n",
+                       sta->addr);
+
+       ath9k_htc_ps_restore(priv);
+       mutex_unlock(&priv->mutex);
+}
+
 static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
                             struct ieee80211_sta *sta)
 {
        struct ath9k_htc_priv *priv = hw->priv;
+       struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
        int ret;
 
        mutex_lock(&priv->mutex);
        ath9k_htc_ps_wakeup(priv);
        ret = ath9k_htc_add_station(priv, vif, sta);
-       if (!ret)
+       if (!ret) {
+               INIT_WORK(&ista->rc_update_work, ath9k_htc_sta_rc_update_work);
+               ista->htc_priv = priv;
                ath9k_htc_init_rate(priv, sta);
+       }
        ath9k_htc_ps_restore(priv);
        mutex_unlock(&priv->mutex);
 
@@ -1293,12 +1325,13 @@ static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
                                struct ieee80211_sta *sta)
 {
        struct ath9k_htc_priv *priv = hw->priv;
-       struct ath9k_htc_sta *ista;
+       struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
        int ret;
 
+       cancel_work_sync(&ista->rc_update_work);
+
        mutex_lock(&priv->mutex);
        ath9k_htc_ps_wakeup(priv);
-       ista = (struct ath9k_htc_sta *) sta->drv_priv;
        htc_sta_drain(priv->htc, ista->index);
        ret = ath9k_htc_remove_station(priv, vif, sta);
        ath9k_htc_ps_restore(priv);
@@ -1311,29 +1344,12 @@ static void ath9k_htc_sta_rc_update(struct ieee80211_hw *hw,
                                    struct ieee80211_vif *vif,
                                    struct ieee80211_sta *sta, u32 changed)
 {
-       struct ath9k_htc_priv *priv = hw->priv;
-       struct ath_common *common = ath9k_hw_common(priv->ah);
-       struct ath9k_htc_target_rate trate;
+       struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
 
        if (!(changed & IEEE80211_RC_SUPP_RATES_CHANGED))
                return;
 
-       mutex_lock(&priv->mutex);
-       ath9k_htc_ps_wakeup(priv);
-
-       memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
-       ath9k_htc_setup_rate(priv, sta, &trate);
-       if (!ath9k_htc_send_rate_cmd(priv, &trate))
-               ath_dbg(common, CONFIG,
-                       "Supported rates for sta: %pM updated, rate caps: 0x%X\n",
-                       sta->addr, be32_to_cpu(trate.capflags));
-       else
-               ath_dbg(common, CONFIG,
-                       "Unable to update supported rates for sta: %pM\n",
-                       sta->addr);
-
-       ath9k_htc_ps_restore(priv);
-       mutex_unlock(&priv->mutex);
+       schedule_work(&ista->rc_update_work);
 }
 
 static int ath9k_htc_conf_tx(struct ieee80211_hw *hw,