From f1d58c2521eb160178b2151d6326d8dc5d7c8560 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 17 Jun 2009 13:13:00 +0200 Subject: mac80211: push rx status into skb->cb Within mac80211, we often need to copy the rx status into skb->cb. This is wasteful, as drivers could be building it in there to start with. This patch changes the API so that drivers are expected to pass the RX status in skb->cb, now accessible as IEEE80211_SKB_RXCB(skb). It also updates all drivers to pass the rx status in there, but only by making them memcpy() it into place before the call to the receive function (ieee80211_rx(_irqsafe)). Each driver can now be optimised on its own schedule. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8187_dev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/rtl818x/rtl8187_dev.c') diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c index 294250e294d..c9b9dbe584c 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c @@ -380,7 +380,8 @@ static void rtl8187_rx_cb(struct urb *urb) rx_status.flag |= RX_FLAG_TSFT; if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; - ieee80211_rx_irqsafe(dev, skb, &rx_status); + memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); + ieee80211_rx_irqsafe(dev, skb); skb = dev_alloc_skb(RTL8187_MAX_RX); if (unlikely(!skb)) { -- cgit v1.2.3-70-g09d2 From 42935ecaf4e784d0815afa9a7e5fe7e141157ca3 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 29 Jul 2009 20:08:07 -0400 Subject: mac80211: redefine usage of the mac80211 workqueue The mac80211 workqueue exists to enable mac80211 and drivers to queue their own work on a single threaded workqueue. mac80211 takes care to flush the workqueue during suspend but we never really had requirements on drivers for how they should use the workqueue in consideration for suspend. We extend mac80211 to document how the mac80211 workqueue should be used, how it should not be used and finally move raw access to the workqueue to mac80211 only. Drivers and mac80211 use helpers to queue work onto the mac80211 workqueue: * ieee80211_queue_work() * ieee80211_queue_delayed_work() These helpers will now warn if mac80211 already completed its suspend cycle and someone is trying to queue work. mac80211 flushes the mac80211 workqueue prior to suspend a few times, but we haven't taken the care to ensure drivers won't add more work after suspend. To help with this we add a warning when someone tries to add work and mac80211 already completed the suspend cycle. Drivers should ensure they cancel any work or delayed work in the mac80211 stop() callback. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/at76c50x-usb.c | 10 +++--- drivers/net/wireless/ath/ar9170/led.c | 11 ++++--- drivers/net/wireless/ath/ar9170/main.c | 26 ++++++++------- drivers/net/wireless/ath/ath9k/main.c | 15 +++++---- drivers/net/wireless/ath/ath9k/virtual.c | 17 +++++----- drivers/net/wireless/ath/ath9k/xmit.c | 2 +- drivers/net/wireless/b43/main.c | 8 ++--- drivers/net/wireless/b43/phy_common.c | 2 +- drivers/net/wireless/b43/pio.c | 2 +- drivers/net/wireless/b43legacy/main.c | 8 ++--- drivers/net/wireless/p54/led.c | 5 ++- drivers/net/wireless/p54/main.c | 2 +- drivers/net/wireless/p54/p54spi.c | 4 +-- drivers/net/wireless/p54/txrx.c | 2 +- drivers/net/wireless/rt2x00/rt2x00dev.c | 2 +- drivers/net/wireless/rt2x00/rt2x00link.c | 8 ++--- drivers/net/wireless/rt2x00/rt2x00mac.c | 2 +- drivers/net/wireless/rtl818x/rtl8187_dev.c | 2 +- drivers/net/wireless/rtl818x/rtl8187_leds.c | 10 +++--- drivers/net/wireless/zd1211rw/zd_mac.c | 2 +- include/net/mac80211.h | 50 ++++++++++++++++++++++++----- net/mac80211/ibss.c | 6 ++-- net/mac80211/ieee80211_i.h | 6 ++++ net/mac80211/iface.c | 4 +-- net/mac80211/main.c | 8 ++--- net/mac80211/mesh.c | 10 +++--- net/mac80211/mesh_hwmp.c | 4 +-- net/mac80211/mlme.c | 48 +++++++++++++-------------- net/mac80211/pm.c | 4 +-- net/mac80211/scan.c | 8 ++--- net/mac80211/tx.c | 2 +- net/mac80211/util.c | 41 +++++++++++++++++++++++ 32 files changed, 208 insertions(+), 123 deletions(-) (limited to 'drivers/net/wireless/rtl818x/rtl8187_dev.c') diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index aff09a1cf64..7218dbabad3 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -1875,8 +1875,8 @@ static void at76_dwork_hw_scan(struct work_struct *work) /* FIXME: add maximum time for scan to complete */ if (ret != CMD_STATUS_COMPLETE) { - queue_delayed_work(priv->hw->workqueue, &priv->dwork_hw_scan, - SCAN_POLL_INTERVAL); + ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan, + SCAN_POLL_INTERVAL); mutex_unlock(&priv->mtx); return; } @@ -1937,8 +1937,8 @@ static int at76_hw_scan(struct ieee80211_hw *hw, goto exit; } - queue_delayed_work(priv->hw->workqueue, &priv->dwork_hw_scan, - SCAN_POLL_INTERVAL); + ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan, + SCAN_POLL_INTERVAL); exit: mutex_unlock(&priv->mtx); @@ -2027,7 +2027,7 @@ static void at76_configure_filter(struct ieee80211_hw *hw, } else return; - queue_work(hw->workqueue, &priv->work_set_promisc); + ieee80211_queue_work(hw, &priv->work_set_promisc); } static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, diff --git a/drivers/net/wireless/ath/ar9170/led.c b/drivers/net/wireless/ath/ar9170/led.c index 63fda6cd210..86c4e79f6bc 100644 --- a/drivers/net/wireless/ath/ar9170/led.c +++ b/drivers/net/wireless/ath/ar9170/led.c @@ -90,9 +90,12 @@ static void ar9170_update_leds(struct work_struct *work) ar9170_set_leds_state(ar, led_val); mutex_unlock(&ar->mutex); - if (rerun) - queue_delayed_work(ar->hw->workqueue, &ar->led_work, - msecs_to_jiffies(blink_delay)); + if (!rerun) + return; + + ieee80211_queue_delayed_work(ar->hw, + &ar->led_work, + msecs_to_jiffies(blink_delay)); } static void ar9170_led_brightness_set(struct led_classdev *led, @@ -110,7 +113,7 @@ static void ar9170_led_brightness_set(struct led_classdev *led, } if (likely(IS_ACCEPTING_CMD(ar) && arl->toggled)) - queue_delayed_work(ar->hw->workqueue, &ar->led_work, HZ/10); + ieee80211_queue_delayed_work(ar->hw, &ar->led_work, HZ/10); } static int ar9170_register_led(struct ar9170 *ar, int i, char *name, diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c index 099ed3c3ba2..4fc389ae74b 100644 --- a/drivers/net/wireless/ath/ar9170/main.c +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -595,10 +595,12 @@ static void ar9170_tx_janitor(struct work_struct *work) ar9170_tx_fake_ampdu_status(ar); - if (resched) - queue_delayed_work(ar->hw->workqueue, - &ar->tx_janitor, - msecs_to_jiffies(AR9170_JANITOR_DELAY)); + if (!resched) + return; + + ieee80211_queue_delayed_work(ar->hw, + &ar->tx_janitor, + msecs_to_jiffies(AR9170_JANITOR_DELAY)); } void ar9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len) @@ -648,7 +650,7 @@ void ar9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len) * pre-TBTT event */ if (ar->vif && ar->vif->type == NL80211_IFTYPE_AP) - queue_work(ar->hw->workqueue, &ar->beacon_work); + ieee80211_queue_work(ar->hw, &ar->beacon_work); break; case 0xc2: @@ -1825,10 +1827,12 @@ static void ar9170_tx(struct ar9170 *ar) } } - if (schedule_garbagecollector) - queue_delayed_work(ar->hw->workqueue, - &ar->tx_janitor, - msecs_to_jiffies(AR9170_JANITOR_DELAY)); + if (!schedule_garbagecollector) + return; + + ieee80211_queue_delayed_work(ar->hw, + &ar->tx_janitor, + msecs_to_jiffies(AR9170_JANITOR_DELAY)); } static bool ar9170_tx_ampdu_queue(struct ar9170 *ar, struct sk_buff *skb) @@ -2157,7 +2161,7 @@ static void ar9170_op_configure_filter(struct ieee80211_hw *hw, } if (likely(IS_STARTED(ar))) - queue_work(ar->hw->workqueue, &ar->filter_config_work); + ieee80211_queue_work(ar->hw, &ar->filter_config_work); } static void ar9170_op_bss_info_changed(struct ieee80211_hw *hw, @@ -2415,7 +2419,7 @@ static void ar9170_sta_notify(struct ieee80211_hw *hw, } if (IS_STARTED(ar) && ar->filter_changed) - queue_work(ar->hw->workqueue, &ar->filter_config_work); + ieee80211_queue_work(ar->hw, &ar->filter_config_work); } static int ar9170_get_stats(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index cf44623b5cd..292ac2b4189 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -973,10 +973,11 @@ static void ath_led_blink_work(struct work_struct *work) ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, (sc->sc_flags & SC_OP_LED_ON) ? 1 : 0); - queue_delayed_work(sc->hw->workqueue, &sc->ath_led_blink_work, - (sc->sc_flags & SC_OP_LED_ON) ? - msecs_to_jiffies(sc->led_off_duration) : - msecs_to_jiffies(sc->led_on_duration)); + ieee80211_queue_delayed_work(sc->hw, + &sc->ath_led_blink_work, + (sc->sc_flags & SC_OP_LED_ON) ? + msecs_to_jiffies(sc->led_off_duration) : + msecs_to_jiffies(sc->led_on_duration)); sc->led_on_duration = sc->led_on_cnt ? max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25) : @@ -1013,8 +1014,8 @@ static void ath_led_brightness(struct led_classdev *led_cdev, case LED_FULL: if (led->led_type == ATH_LED_ASSOC) { sc->sc_flags |= SC_OP_LED_ASSOCIATED; - queue_delayed_work(sc->hw->workqueue, - &sc->ath_led_blink_work, 0); + ieee80211_queue_delayed_work(sc->hw, + &sc->ath_led_blink_work, 0); } else if (led->led_type == ATH_LED_RADIO) { ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 0); sc->sc_flags |= SC_OP_LED_ON; @@ -1972,7 +1973,7 @@ static int ath9k_start(struct ieee80211_hw *hw) ieee80211_wake_queues(hw); - queue_delayed_work(sc->hw->workqueue, &sc->tx_complete_work, 0); + ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); mutex_unlock: mutex_unlock(&sc->mutex); diff --git a/drivers/net/wireless/ath/ath9k/virtual.c b/drivers/net/wireless/ath/ath9k/virtual.c index e1d419e02b4..19b88f8177f 100644 --- a/drivers/net/wireless/ath/ath9k/virtual.c +++ b/drivers/net/wireless/ath/ath9k/virtual.c @@ -351,7 +351,7 @@ void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) * Drop from tasklet to work to allow mutex for channel * change. */ - queue_work(aphy->sc->hw->workqueue, + ieee80211_queue_work(aphy->sc->hw, &aphy->sc->chan_work); } } @@ -367,7 +367,7 @@ static void ath9k_mark_paused(struct ath_wiphy *aphy) struct ath_softc *sc = aphy->sc; aphy->state = ATH_WIPHY_PAUSED; if (!__ath9k_wiphy_pausing(sc)) - queue_work(sc->hw->workqueue, &sc->chan_work); + ieee80211_queue_work(sc->hw, &sc->chan_work); } static void ath9k_pause_iter(void *data, u8 *mac, struct ieee80211_vif *vif) @@ -521,7 +521,7 @@ int ath9k_wiphy_select(struct ath_wiphy *aphy) spin_unlock_bh(&sc->wiphy_lock); ath_radio_disable(sc); ath_radio_enable(sc); - queue_work(aphy->sc->hw->workqueue, + ieee80211_queue_work(aphy->sc->hw, &aphy->sc->chan_work); return -EBUSY; /* previous select still in progress */ } @@ -541,7 +541,7 @@ int ath9k_wiphy_select(struct ath_wiphy *aphy) if (now) { /* Ready to request channel change immediately */ - queue_work(aphy->sc->hw->workqueue, &aphy->sc->chan_work); + ieee80211_queue_work(aphy->sc->hw, &aphy->sc->chan_work); } /* @@ -648,8 +648,9 @@ try_again: "change\n"); } - queue_delayed_work(sc->hw->workqueue, &sc->wiphy_work, - sc->wiphy_scheduler_int); + ieee80211_queue_delayed_work(sc->hw, + &sc->wiphy_work, + sc->wiphy_scheduler_int); } void ath9k_wiphy_set_scheduler(struct ath_softc *sc, unsigned int msec_int) @@ -657,8 +658,8 @@ void ath9k_wiphy_set_scheduler(struct ath_softc *sc, unsigned int msec_int) cancel_delayed_work_sync(&sc->wiphy_work); sc->wiphy_scheduler_int = msecs_to_jiffies(msec_int); if (sc->wiphy_scheduler_int) - queue_delayed_work(sc->hw->workqueue, &sc->wiphy_work, - sc->wiphy_scheduler_int); + ieee80211_queue_delayed_work(sc->hw, &sc->wiphy_work, + sc->wiphy_scheduler_int); } /* caller must hold wiphy_lock */ diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index b7806e2ca0e..87762da0383 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2063,7 +2063,7 @@ static void ath_tx_complete_poll_work(struct work_struct *work) ath_reset(sc, false); } - queue_delayed_work(sc->hw->workqueue, &sc->tx_complete_work, + ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, msecs_to_jiffies(ATH_TX_COMPLETE_POLL_INT)); } diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 3f4360ad0e4..f985938962e 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -1654,7 +1654,7 @@ static void b43_update_templates(struct b43_wl *wl) wl->current_beacon = beacon; wl->beacon0_uploaded = 0; wl->beacon1_uploaded = 0; - queue_work(wl->hw->workqueue, &wl->beacon_update_trigger); + ieee80211_queue_work(wl->hw, &wl->beacon_update_trigger); } static void b43_set_beacon_int(struct b43_wldev *dev, u16 beacon_int) @@ -2914,7 +2914,7 @@ out_requeue: delay = msecs_to_jiffies(50); else delay = round_jiffies_relative(HZ * 15); - queue_delayed_work(wl->hw->workqueue, &dev->periodic_work, delay); + ieee80211_queue_delayed_work(wl->hw, &dev->periodic_work, delay); out: mutex_unlock(&wl->mutex); } @@ -2925,7 +2925,7 @@ static void b43_periodic_tasks_setup(struct b43_wldev *dev) dev->periodic_state = 0; INIT_DELAYED_WORK(work, b43_periodic_work_handler); - queue_delayed_work(dev->wl->hw->workqueue, work, 0); + ieee80211_queue_delayed_work(dev->wl->hw, work, 0); } /* Check if communication with the device works correctly. */ @@ -4871,7 +4871,7 @@ void b43_controller_restart(struct b43_wldev *dev, const char *reason) if (b43_status(dev) < B43_STAT_INITIALIZED) return; b43info(dev->wl, "Controller RESET (%s) ...\n", reason); - queue_work(dev->wl->hw->workqueue, &dev->restart_work); + ieee80211_queue_work(dev->wl->hw, &dev->restart_work); } #ifdef CONFIG_PM diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index 6d241622210..f537bfef690 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -352,7 +352,7 @@ void b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags) /* We must adjust the transmission power in hardware. * Schedule b43_phy_txpower_adjust_work(). */ - queue_work(dev->wl->hw->workqueue, &dev->wl->txpower_adjust_work); + ieee80211_queue_work(dev->wl->hw, &dev->wl->txpower_adjust_work); } int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset) diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/b43/pio.c index 69138e8c1db..73c047d8de4 100644 --- a/drivers/net/wireless/b43/pio.c +++ b/drivers/net/wireless/b43/pio.c @@ -783,7 +783,7 @@ void b43_pio_rx(struct b43_pio_rxqueue *q) { /* Due to latency issues we must run the RX path in * a workqueue to be able to schedule between packets. */ - queue_work(q->dev->wl->hw->workqueue, &q->rx_work); + ieee80211_queue_work(q->dev->wl->hw, &q->rx_work); } static void b43_pio_tx_suspend_queue(struct b43_pio_txqueue *q) diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index c4973c1942b..b1435594921 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -1252,7 +1252,7 @@ static void b43legacy_update_templates(struct b43legacy_wl *wl) wl->current_beacon = beacon; wl->beacon0_uploaded = 0; wl->beacon1_uploaded = 0; - queue_work(wl->hw->workqueue, &wl->beacon_update_trigger); + ieee80211_queue_work(wl->hw, &wl->beacon_update_trigger); } static void b43legacy_set_beacon_int(struct b43legacy_wldev *dev, @@ -2300,7 +2300,7 @@ out_requeue: delay = msecs_to_jiffies(50); else delay = round_jiffies_relative(HZ * 15); - queue_delayed_work(wl->hw->workqueue, &dev->periodic_work, delay); + ieee80211_queue_delayed_work(wl->hw, &dev->periodic_work, delay); out: mutex_unlock(&wl->mutex); } @@ -2311,7 +2311,7 @@ static void b43legacy_periodic_tasks_setup(struct b43legacy_wldev *dev) dev->periodic_state = 0; INIT_DELAYED_WORK(work, b43legacy_periodic_work_handler); - queue_delayed_work(dev->wl->hw->workqueue, work, 0); + ieee80211_queue_delayed_work(dev->wl->hw, work, 0); } /* Validate access to the chip (SHM) */ @@ -3885,7 +3885,7 @@ void b43legacy_controller_restart(struct b43legacy_wldev *dev, if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) return; b43legacyinfo(dev->wl, "Controller RESET (%s) ...\n", reason); - queue_work(dev->wl->hw->workqueue, &dev->restart_work); + ieee80211_queue_work(dev->wl->hw, &dev->restart_work); } #ifdef CONFIG_PM diff --git a/drivers/net/wireless/p54/led.c b/drivers/net/wireless/p54/led.c index c00115b206d..9575ac03363 100644 --- a/drivers/net/wireless/p54/led.c +++ b/drivers/net/wireless/p54/led.c @@ -61,7 +61,7 @@ static void p54_update_leds(struct work_struct *work) wiphy_name(priv->hw->wiphy), err); if (rerun) - queue_delayed_work(priv->hw->workqueue, &priv->led_work, + ieee80211_queue_delayed_work(priv->hw, &priv->led_work, msecs_to_jiffies(blink_delay)); } @@ -78,8 +78,7 @@ static void p54_led_brightness_set(struct led_classdev *led_dev, if ((brightness) && (led->registered)) { led->toggled++; - queue_delayed_work(priv->hw->workqueue, &priv->led_work, - HZ/10); + ieee80211_queue_delayed_work(priv->hw, &priv->led_work, HZ/10); } } diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 955f6d7ec16..a0d0e726bc4 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -180,7 +180,7 @@ static int p54_start(struct ieee80211_hw *dev) goto out; } - queue_delayed_work(dev->workqueue, &priv->work, 0); + ieee80211_queue_delayed_work(dev, &priv->work, 0); priv->softled_state = 0; err = p54_set_leds(priv); diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index eef532987d0..05458d9249c 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -391,7 +391,7 @@ static irqreturn_t p54spi_interrupt(int irq, void *config) struct spi_device *spi = config; struct p54s_priv *priv = dev_get_drvdata(&spi->dev); - queue_work(priv->hw->workqueue, &priv->work); + ieee80211_queue_work(priv->hw, &priv->work); return IRQ_HANDLED; } @@ -479,7 +479,7 @@ static void p54spi_op_tx(struct ieee80211_hw *dev, struct sk_buff *skb) list_add_tail(&di->tx_list, &priv->tx_pending); spin_unlock_irqrestore(&priv->tx_lock, flags); - queue_work(priv->hw->workqueue, &priv->work); + ieee80211_queue_work(priv->hw, &priv->work); } static void p54spi_work(struct work_struct *work) diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index c32a0d2fa1f..704685fab17 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -380,7 +380,7 @@ static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb) ieee80211_rx_irqsafe(priv->hw, skb); - queue_delayed_work(priv->hw->workqueue, &priv->work, + ieee80211_queue_delayed_work(priv->hw, &priv->work, msecs_to_jiffies(P54_STATISTICS_UPDATE)); return -1; diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 658a63bfb76..b717afbf3f3 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -215,7 +215,7 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) rt2x00lib_beacondone_iter, rt2x00dev); - queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->intf_work); + ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->intf_work); } EXPORT_SYMBOL_GPL(rt2x00lib_beacondone); diff --git a/drivers/net/wireless/rt2x00/rt2x00link.c b/drivers/net/wireless/rt2x00/rt2x00link.c index 79915687e74..917831689cc 100644 --- a/drivers/net/wireless/rt2x00/rt2x00link.c +++ b/drivers/net/wireless/rt2x00/rt2x00link.c @@ -351,8 +351,8 @@ void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev) rt2x00link_reset_tuner(rt2x00dev, false); - queue_delayed_work(rt2x00dev->hw->workqueue, - &link->work, LINK_TUNE_INTERVAL); + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->work, LINK_TUNE_INTERVAL); } void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev) @@ -461,8 +461,8 @@ static void rt2x00link_tuner(struct work_struct *work) * Increase tuner counter, and reschedule the next link tuner run. */ link->count++; - queue_delayed_work(rt2x00dev->hw->workqueue, - &link->work, LINK_TUNE_INTERVAL); + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->work, LINK_TUNE_INTERVAL); } void rt2x00link_register(struct rt2x00_dev *rt2x00dev) diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index e92c8f99d69..81febdfd663 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -431,7 +431,7 @@ void rt2x00mac_configure_filter(struct ieee80211_hw *hw, if (!test_bit(DRIVER_REQUIRE_SCHEDULED, &rt2x00dev->flags)) rt2x00dev->ops->lib->config_filter(rt2x00dev, *total_flags); else - queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->filter_work); + ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->filter_work); } EXPORT_SYMBOL_GPL(rt2x00mac_configure_filter); diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c index c9b9dbe584c..53f57dc5222 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c @@ -220,7 +220,7 @@ static void rtl8187_tx_cb(struct urb *urb) * reading a register in the device. We are in interrupt mode * here, thus queue the skb and finish on a work queue. */ skb_queue_tail(&priv->b_tx_status.queue, skb); - queue_delayed_work(hw->workqueue, &priv->work, 0); + ieee80211_queue_delayed_work(hw, &priv->work, 0); } } diff --git a/drivers/net/wireless/rtl818x/rtl8187_leds.c b/drivers/net/wireless/rtl818x/rtl8187_leds.c index cf9f899fe0e..a6cfb7e7799 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_leds.c +++ b/drivers/net/wireless/rtl818x/rtl8187_leds.c @@ -108,11 +108,11 @@ static void rtl8187_led_brightness_set(struct led_classdev *led_dev, struct rtl8187_priv *priv = hw->priv; if (brightness == LED_OFF) { - queue_delayed_work(hw->workqueue, &priv->led_off, 0); + ieee80211_queue_delayed_work(hw, &priv->led_off, 0); /* The LED is off for 1/20 sec so that it just blinks. */ - queue_delayed_work(hw->workqueue, &priv->led_on, HZ / 20); + ieee80211_queue_delayed_work(hw, &priv->led_on, HZ / 20); } else - queue_delayed_work(hw->workqueue, &priv->led_on, 0); + ieee80211_queue_delayed_work(hw, &priv->led_on, 0); } static int rtl8187_register_led(struct ieee80211_hw *dev, @@ -193,7 +193,7 @@ void rtl8187_leds_init(struct ieee80211_hw *dev, u16 custid) err = rtl8187_register_led(dev, &priv->led_rx, name, ieee80211_get_rx_led_name(dev), ledpin); if (!err) { - queue_delayed_work(dev->workqueue, &priv->led_on, 0); + ieee80211_queue_delayed_work(dev, &priv->led_on, 0); return; } /* registration of RX LED failed - unregister TX */ @@ -209,7 +209,7 @@ void rtl8187_leds_exit(struct ieee80211_hw *dev) struct rtl8187_priv *priv = dev->priv; /* turn the LED off before exiting */ - queue_delayed_work(dev->workqueue, &priv->led_off, 0); + ieee80211_queue_delayed_work(dev, &priv->led_off, 0); cancel_delayed_work_sync(&priv->led_off); cancel_delayed_work_sync(&priv->led_on); rtl8187_unregister_led(&priv->led_rx); diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 9600b72495d..54abdd0c004 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -698,7 +698,7 @@ int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length) && !mac->pass_ctrl) return 0; - fc = *(__le16 *)buffer; + fc = get_unaligned((__le16*)buffer); need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc); skb = dev_alloc_skb(length + (need_padding ? 2 : 0)); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d4e09a06b4a..5ed93f4406a 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -72,6 +72,21 @@ * not do so then mac80211 may add this under certain circumstances. */ +/** + * DOC: mac80211 workqueue + * + * mac80211 provides its own workqueue for drivers and internal mac80211 use. + * The workqueue is a single threaded workqueue and can only be accessed by + * helpers for sanity checking. Drivers must ensure all work added onto the + * mac80211 workqueue should be cancelled on the driver stop() callback. + * + * mac80211 will flushed the workqueue upon interface removal and during + * suspend. + * + * All work performed on the mac80211 workqueue must not acquire the RTNL lock. + * + */ + /** * enum ieee80211_max_queues - maximum number of queues * @@ -913,12 +928,6 @@ enum ieee80211_hw_flags { * * @conf: &struct ieee80211_conf, device configuration, don't use. * - * @workqueue: single threaded workqueue available for driver use, - * allocated by mac80211 on registration and flushed when an - * interface is removed. - * NOTICE: All work performed on this workqueue must not - * acquire the RTNL lock. - * * @priv: pointer to private area that was allocated for driver use * along with this structure. * @@ -954,7 +963,6 @@ enum ieee80211_hw_flags { struct ieee80211_hw { struct ieee80211_conf conf; struct wiphy *wiphy; - struct workqueue_struct *workqueue; const char *rate_control_algorithm; void *priv; u32 flags; @@ -1301,7 +1309,8 @@ enum ieee80211_ampdu_mlme_action { * is disabled. This should turn off the hardware (at least * it must turn off frame reception.) * May be called right after add_interface if that rejects - * an interface. + * an interface. If you added any work onto the mac80211 workqueue + * you should ensure to cancel it on this callback. * Must be implemented. * * @add_interface: Called when a netdevice attached to the hardware is @@ -1927,6 +1936,31 @@ void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw, struct ieee80211_vif *vif), void *data); +/** + * ieee80211_queue_work - add work onto the mac80211 workqueue + * + * Drivers and mac80211 use this to add work onto the mac80211 workqueue. + * This helper ensures drivers are not queueing work when they should not be. + * + * @hw: the hardware struct for the interface we are adding work for + * @work: the work we want to add onto the mac80211 workqueue + */ +void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work); + +/** + * ieee80211_queue_delayed_work - add work onto the mac80211 workqueue + * + * Drivers and mac80211 use this to queue delayed work onto the mac80211 + * workqueue. + * + * @hw: the hardware struct for the interface we are adding work for + * @dwork: delayable work to queue onto the mac80211 workqueue + * @delay: number of jiffies to wait before queueing + */ +void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, + struct delayed_work *dwork, + unsigned long delay); + /** * ieee80211_start_tx_ba_session - Start a tx Block Ack session. * @hw: pointer as obtained from ieee80211_alloc_hw(). diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 6e3cca65c46..920ec8792f4 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -781,7 +781,7 @@ static void ieee80211_ibss_timer(unsigned long data) } set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request); - queue_work(local->hw.workqueue, &ifibss->work); + ieee80211_queue_work(&local->hw, &ifibss->work); } #ifdef CONFIG_PM @@ -853,7 +853,7 @@ ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) case IEEE80211_STYPE_PROBE_REQ: case IEEE80211_STYPE_AUTH: skb_queue_tail(&sdata->u.ibss.skb_queue, skb); - queue_work(local->hw.workqueue, &sdata->u.ibss.work); + ieee80211_queue_work(&local->hw, &sdata->u.ibss.work); return RX_QUEUED; } @@ -912,7 +912,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, ieee80211_recalc_idle(sdata->local); set_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request); - queue_work(sdata->local->hw.workqueue, &sdata->u.ibss.work); + ieee80211_queue_work(&sdata->local->hw, &sdata->u.ibss.work); return 0; } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index aec6853cb43..316825be201 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -614,6 +614,12 @@ struct ieee80211_local { const struct ieee80211_ops *ops; + /* + * private workqueue to mac80211. mac80211 makes this accessible + * via ieee80211_queue_work() + */ + struct workqueue_struct *workqueue; + unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES]; /* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */ spinlock_t queue_stop_reason_lock; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index a83087f4237..8c1284d45e6 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -312,7 +312,7 @@ static int ieee80211_open(struct net_device *dev) * to fix this. */ if (sdata->vif.type == NL80211_IFTYPE_STATION) - queue_work(local->hw.workqueue, &sdata->u.mgd.work); + ieee80211_queue_work(&local->hw, &sdata->u.mgd.work); netif_tx_start_all_queues(dev); @@ -551,7 +551,7 @@ static int ieee80211_stop(struct net_device *dev) ieee80211_led_radio(local, false); - flush_workqueue(local->hw.workqueue); + flush_workqueue(local->workqueue); tasklet_disable(&local->tx_pending_tasklet); tasklet_disable(&local->tasklet); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 5e76dd1daf7..22e07385ff6 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -821,9 +821,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (hw->queues > IEEE80211_MAX_QUEUES) hw->queues = IEEE80211_MAX_QUEUES; - local->hw.workqueue = + local->workqueue = create_singlethread_workqueue(wiphy_name(local->hw.wiphy)); - if (!local->hw.workqueue) { + if (!local->workqueue) { result = -ENOMEM; goto fail_workqueue; } @@ -913,7 +913,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) sta_info_stop(local); fail_sta_info: debugfs_hw_del(local); - destroy_workqueue(local->hw.workqueue); + destroy_workqueue(local->workqueue); fail_workqueue: wiphy_unregister(local->hw.wiphy); fail_wiphy_register: @@ -955,7 +955,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) skb_queue_purge(&local->skb_queue); skb_queue_purge(&local->skb_queue_unreliable); - destroy_workqueue(local->hw.workqueue); + destroy_workqueue(local->workqueue); wiphy_unregister(local->hw.wiphy); ieee80211_wep_free(local); ieee80211_led_exit(local); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 9a3826978b1..2f4f518ab45 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -54,7 +54,7 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data) return; } - queue_work(local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(local->hw.workqueue, &ifmsh->work); } /** @@ -357,7 +357,7 @@ static void ieee80211_mesh_path_timer(unsigned long data) return; } - queue_work(local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(local->hw.workqueue, &ifmsh->work); } struct mesh_table *mesh_table_grow(struct mesh_table *tbl) @@ -471,7 +471,7 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; ifmsh->housekeeping = true; - queue_work(local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(local->hw.workqueue, &ifmsh->work); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED); } @@ -619,7 +619,7 @@ void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) if (ieee80211_vif_is_mesh(&sdata->vif)) - queue_work(local->hw.workqueue, &sdata->u.mesh.work); + ieee80211_queue_work(local->hw.workqueue, &sdata->u.mesh.work); rcu_read_unlock(); } @@ -692,7 +692,7 @@ ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) case IEEE80211_STYPE_PROBE_RESP: case IEEE80211_STYPE_BEACON: skb_queue_tail(&ifmsh->skb_queue, skb); - queue_work(local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(local->hw.workqueue, &ifmsh->work); return RX_QUEUED; } diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index e93c37ef6a4..11ab71a68ff 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -660,14 +660,14 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags) spin_unlock(&ifmsh->mesh_preq_queue_lock); if (time_after(jiffies, ifmsh->last_preq + min_preq_int_jiff(sdata))) - queue_work(sdata->local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(sdata->local->hw.workqueue, &ifmsh->work); else if (time_before(jiffies, ifmsh->last_preq)) { /* avoid long wait if did not send preqs for a long time * and jiffies wrapped around */ ifmsh->last_preq = jiffies - min_preq_int_jiff(sdata) - 1; - queue_work(sdata->local->hw.workqueue, &ifmsh->work); + ieee80211_queue_work(sdata->local->hw.workqueue, &ifmsh->work); } else mod_timer(&ifmsh->mesh_path_timer, ifmsh->last_preq + min_preq_int_jiff(sdata)); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index ee83125ed17..0779ba150b2 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -565,7 +565,7 @@ static void ieee80211_chswitch_timer(unsigned long data) return; } - queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work); + ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); } void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, @@ -597,7 +597,7 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, sdata->local->csa_channel = new_ch; if (sw_elem->count <= 1) { - queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work); + ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); } else { ieee80211_stop_queues_by_reason(&sdata->local->hw, IEEE80211_QUEUE_STOP_REASON_CSA); @@ -763,7 +763,7 @@ void ieee80211_dynamic_ps_timer(unsigned long data) if (local->quiescing || local->suspended) return; - queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work); + ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work); } /* MLME */ @@ -950,7 +950,7 @@ ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata, * due to work needing to be done. Hence, queue the STAs work * again for that. */ - queue_work(local->hw.workqueue, &ifmgd->work); + ieee80211_queue_work(&local->hw, &ifmgd->work); return RX_MGMT_CFG80211_AUTH_TO; } @@ -995,7 +995,7 @@ ieee80211_authenticate(struct ieee80211_sub_if_data *sdata, * due to work needing to be done. Hence, queue the STAs work * again for that. */ - queue_work(local->hw.workqueue, &ifmgd->work); + ieee80211_queue_work(&local->hw, &ifmgd->work); return RX_MGMT_CFG80211_AUTH_TO; } @@ -1124,7 +1124,7 @@ ieee80211_associate(struct ieee80211_sub_if_data *sdata, * due to work needing to be done. Hence, queue the STAs work * again for that. */ - queue_work(local->hw.workqueue, &ifmgd->work); + ieee80211_queue_work(&local->hw, &ifmgd->work); return RX_MGMT_CFG80211_ASSOC_TO; } @@ -1232,8 +1232,7 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); - queue_work(sdata->local->hw.workqueue, - &sdata->u.mgd.beacon_loss_work); + ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work); } EXPORT_SYMBOL(ieee80211_beacon_loss); @@ -1888,7 +1887,7 @@ ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, case IEEE80211_STYPE_DISASSOC: case IEEE80211_STYPE_ACTION: skb_queue_tail(&sdata->u.mgd.skb_queue, skb); - queue_work(local->hw.workqueue, &sdata->u.mgd.work); + ieee80211_queue_work(&local->hw, &sdata->u.mgd.work); return RX_QUEUED; } @@ -2026,7 +2025,7 @@ static void ieee80211_sta_timer(unsigned long data) return; } - queue_work(local->hw.workqueue, &ifmgd->work); + ieee80211_queue_work(&local->hw, &ifmgd->work); } static void ieee80211_sta_work(struct work_struct *work) @@ -2051,13 +2050,11 @@ static void ieee80211_sta_work(struct work_struct *work) return; /* - * Nothing should have been stuffed into the workqueue during - * the suspend->resume cycle. If this WARN is seen then there - * is a bug with either the driver suspend or something in - * mac80211 stuffing into the workqueue which we haven't yet - * cleared during mac80211's suspend cycle. + * ieee80211_queue_work() should have picked up most cases, + * here we'll pick the the rest. */ - if (WARN_ON(local->suspended)) + if (WARN(local->suspended, "STA MLME work scheduled while " + "going to suspend\n")) return; ifmgd = &sdata->u.mgd; @@ -2113,9 +2110,9 @@ static void ieee80211_sta_work(struct work_struct *work) mutex_unlock(&ifmgd->mtx); if (test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request)) - queue_delayed_work(local->hw.workqueue, - &local->scan_work, - round_jiffies_relative(0)); + ieee80211_queue_delayed_work(&local->hw, + &local->scan_work, + round_jiffies_relative(0)); return; } @@ -2196,8 +2193,7 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data) if (local->quiescing) return; - queue_work(sdata->local->hw.workqueue, - &sdata->u.mgd.beacon_loss_work); + ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work); } static void ieee80211_sta_conn_mon_timer(unsigned long data) @@ -2210,7 +2206,7 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data) if (local->quiescing) return; - queue_work(local->hw.workqueue, &ifmgd->monitor_work); + ieee80211_queue_work(&local->hw, &ifmgd->monitor_work); } static void ieee80211_sta_monitor_work(struct work_struct *work) @@ -2229,10 +2225,10 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata) IEEE80211_STA_CONNECTION_POLL); /* let's probe the connection once */ - queue_work(sdata->local->hw.workqueue, + ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.monitor_work); /* and do all the other regular work too */ - queue_work(sdata->local->hw.workqueue, + ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work); } } @@ -2393,7 +2389,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, list_add(&wk->list, &sdata->u.mgd.work_list); mutex_unlock(&ifmgd->mtx); - queue_work(sdata->local->hw.workqueue, &sdata->u.mgd.work); + ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work); return 0; } @@ -2467,7 +2463,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, else ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT; - queue_work(sdata->local->hw.workqueue, &sdata->u.mgd.work); + ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work); err = 0; diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 3320f7daaf2..a5d2f1fb441 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -26,7 +26,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) /* make quiescing visible to timers everywhere */ mb(); - flush_workqueue(local->hw.workqueue); + flush_workqueue(local->workqueue); /* Don't try to run timers while suspended. */ del_timer_sync(&local->sta_cleanup); @@ -117,7 +117,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) * shouldn't be doing (or cancel everything in the * stop callback) that but better safe than sorry. */ - flush_workqueue(local->hw.workqueue); + flush_workqueue(local->workqueue); local->suspended = true; /* need suspended to be visible before quiescing is false */ diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 45731000eb8..244f53f3c8b 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -385,8 +385,9 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) spin_unlock_bh(&local->filter_lock); /* TODO: start scan as soon as all nullfunc frames are ACKed */ - queue_delayed_work(local->hw.workqueue, &local->scan_work, - IEEE80211_CHANNEL_TIME); + ieee80211_queue_delayed_work(&local->hw, + &local->scan_work, + IEEE80211_CHANNEL_TIME); return 0; } @@ -715,8 +716,7 @@ void ieee80211_scan_work(struct work_struct *work) } } while (next_delay == 0); - queue_delayed_work(local->hw.workqueue, &local->scan_work, - next_delay); + ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay); } int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 4e1b2ba122c..7cffaa046b3 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1400,7 +1400,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, if (local->hw.conf.flags & IEEE80211_CONF_PS) { ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_QUEUE_STOP_REASON_PS); - queue_work(local->hw.workqueue, + ieee80211_queue_work(&local->hw, &local->dynamic_ps_disable_work); } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 8502936e531..e55d57f559e 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -511,6 +511,46 @@ void ieee80211_iterate_active_interfaces_atomic( } EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); +/* + * Nothing should have been stuffed into the workqueue during + * the suspend->resume cycle. If this WARN is seen then there + * is a bug with either the driver suspend or something in + * mac80211 stuffing into the workqueue which we haven't yet + * cleared during mac80211's suspend cycle. + */ +static bool ieee80211_can_queue_work(struct ieee80211_local *local) +{ + if (WARN(local->suspended, "queueing ieee80211 work while " + "going to suspend\n")) + return false; + + return true; +} + +void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work) +{ + struct ieee80211_local *local = hw_to_local(hw); + + if (!ieee80211_can_queue_work(local)) + return; + + queue_work(local->workqueue, work); +} +EXPORT_SYMBOL(ieee80211_queue_work); + +void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, + struct delayed_work *dwork, + unsigned long delay) +{ + struct ieee80211_local *local = hw_to_local(hw); + + if (!ieee80211_can_queue_work(local)) + return; + + queue_delayed_work(local->workqueue, dwork, delay); +} +EXPORT_SYMBOL(ieee80211_queue_delayed_work); + void ieee802_11_parse_elems(u8 *start, size_t len, struct ieee802_11_elems *elems) { @@ -1114,3 +1154,4 @@ int ieee80211_reconfig(struct ieee80211_local *local) #endif return 0; } + -- cgit v1.2.3-70-g09d2 From 3ac64beecd27400d12cc7afb4108eef26c499f6a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Aug 2009 16:16:53 +0200 Subject: mac80211: allow configure_filter callback to sleep Over time, a whole bunch of drivers have come up with their own scheme to delay the configure_filter operation to a workqueue. To be able to simplify things, allow configure_filter to sleep, and add a new prepare_multicast callback that drivers that need the multicast address list implement. This new callback must be atomic, but most drivers either don't care or just calculate a hash which can be done atomically and then uploaded to the hardware non-atomically. A cursory look suggests that at76c50x-usb, ar9170, mwl8k (which is actually very broken now), rt2x00, wl1251, wl1271 and zd1211 should make use of this new capability. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/adm8211.c | 42 +++++++++++++------- drivers/net/wireless/at76c50x-usb.c | 7 ++-- drivers/net/wireless/ath/ar9170/main.c | 43 +++++++++++--------- drivers/net/wireless/ath/ath5k/base.c | 64 ++++++++++++++++++------------ drivers/net/wireless/ath/ath9k/main.c | 3 +- drivers/net/wireless/b43/main.c | 2 +- drivers/net/wireless/b43legacy/main.c | 4 +- drivers/net/wireless/iwlwifi/iwl-core.c | 2 +- drivers/net/wireless/iwlwifi/iwl-core.h | 3 +- drivers/net/wireless/libertas_tf/main.c | 37 +++++++++++------ drivers/net/wireless/mac80211_hwsim.c | 4 +- drivers/net/wireless/mwl8k.c | 34 ++++++++++++---- drivers/net/wireless/p54/main.c | 2 +- drivers/net/wireless/rt2x00/rt2x00.h | 2 +- drivers/net/wireless/rt2x00/rt2x00mac.c | 2 +- drivers/net/wireless/rtl818x/rtl8180_dev.c | 11 ++++- drivers/net/wireless/rtl818x/rtl8187_dev.c | 11 ++++- drivers/net/wireless/wl12xx/wl1251_main.c | 4 +- drivers/net/wireless/wl12xx/wl1271_main.c | 4 +- drivers/net/wireless/zd1211rw/zd_mac.c | 44 +++++++++++++------- include/net/mac80211.h | 21 +++++++--- net/mac80211/driver-ops.h | 24 +++++++++-- net/mac80211/driver-trace.h | 36 ++++++++++++++--- net/mac80211/ieee80211_i.h | 3 ++ net/mac80211/iface.c | 15 ++----- net/mac80211/main.c | 24 ++++++++--- net/mac80211/scan.c | 16 +------- net/mac80211/util.c | 2 - 28 files changed, 297 insertions(+), 169 deletions(-) (limited to 'drivers/net/wireless/rtl818x/rtl8187_dev.c') diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index 5695911bc60..b80f514877d 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1328,16 +1328,39 @@ static void adm8211_bss_info_changed(struct ieee80211_hw *dev, } } +static u64 adm8211_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mclist) +{ + unsigned int bit_nr, i; + u32 mc_filter[2]; + + mc_filter[1] = mc_filter[0] = 0; + + for (i = 0; i < mc_count; i++) { + if (!mclist) + break; + bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + + bit_nr &= 0x3F; + mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); + mclist = mclist->next; + } + + return mc_filter[0] | ((u64)(mc_filter[1]) << 32); +} + static void adm8211_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { static const u8 bcast[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; struct adm8211_priv *priv = dev->priv; - unsigned int bit_nr, new_flags; + unsigned int new_flags; u32 mc_filter[2]; - int i; + + mc_filter[0] = multicast; + mc_filter[1] = multicast >> 32; new_flags = 0; @@ -1346,23 +1369,13 @@ static void adm8211_configure_filter(struct ieee80211_hw *dev, priv->nar |= ADM8211_NAR_PR; priv->nar &= ~ADM8211_NAR_MM; mc_filter[1] = mc_filter[0] = ~0; - } else if ((*total_flags & FIF_ALLMULTI) || (mc_count > 32)) { + } else if (*total_flags & FIF_ALLMULTI || multicast == ~(0ULL)) { new_flags |= FIF_ALLMULTI; priv->nar &= ~ADM8211_NAR_PR; priv->nar |= ADM8211_NAR_MM; mc_filter[1] = mc_filter[0] = ~0; } else { priv->nar &= ~(ADM8211_NAR_MM | ADM8211_NAR_PR); - mc_filter[1] = mc_filter[0] = 0; - for (i = 0; i < mc_count; i++) { - if (!mclist) - break; - bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; - - bit_nr &= 0x3F; - mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); - mclist = mclist->next; - } } ADM8211_IDLE_RX(); @@ -1757,6 +1770,7 @@ static const struct ieee80211_ops adm8211_ops = { .remove_interface = adm8211_remove_interface, .config = adm8211_config, .bss_info_changed = adm8211_bss_info_changed, + .prepare_multicast = adm8211_prepare_multicast, .configure_filter = adm8211_configure_filter, .get_stats = adm8211_get_stats, .get_tx_stats = adm8211_get_tx_stats, diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index 7218dbabad3..a6e19545ac6 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -1997,15 +1997,14 @@ static void at76_bss_info_changed(struct ieee80211_hw *hw, /* must be atomic */ static void at76_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, - unsigned int *total_flags, int mc_count, - struct dev_addr_list *mc_list) + unsigned int *total_flags, u64 multicast) { struct at76_priv *priv = hw->priv; int flags; at76_dbg(DBG_MAC80211, "%s(): changed_flags=0x%08x " - "total_flags=0x%08x mc_count=%d", - __func__, changed_flags, *total_flags, mc_count); + "total_flags=0x%08x", + __func__, changed_flags, *total_flags); flags = changed_flags & AT76_SUPPORTED_FILTERS; *total_flags = AT76_SUPPORTED_FILTERS; diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c index ea8c9419336..6a9462e4fd8 100644 --- a/drivers/net/wireless/ath/ar9170/main.c +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -2100,10 +2100,29 @@ unlock: mutex_unlock(&ar->mutex); } +static u64 ar9170_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count, + struct dev_addr_list *mclist) +{ + u64 mchash; + int i; + + /* always get broadcast frames */ + mchash = 1ULL << (0xff >> 2); + + for (i = 0; i < mc_count; i++) { + if (WARN_ON(!mclist)) + break; + mchash |= 1ULL << (mclist->dmi_addr[5] >> 2); + mclist = mclist->next; + } + + return mchash; +} + static void ar9170_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { struct ar9170 *ar = hw->priv; @@ -2116,24 +2135,11 @@ static void ar9170_op_configure_filter(struct ieee80211_hw *hw, * then checking the error flags, later. */ - if (changed_flags & FIF_ALLMULTI) { - if (*new_flags & FIF_ALLMULTI) { - ar->want_mc_hash = ~0ULL; - } else { - u64 mchash; - int i; - - /* always get broadcast frames */ - mchash = 1ULL << (0xff >> 2); + if (changed_flags & FIF_ALLMULTI && *new_flags & FIF_ALLMULTI) + multicast = ~0ULL; - for (i = 0; i < mc_count; i++) { - if (WARN_ON(!mclist)) - break; - mchash |= 1ULL << (mclist->dmi_addr[5] >> 2); - mclist = mclist->next; - } - ar->want_mc_hash = mchash; - } + if (multicast != ar->want_mc_hash) { + ar->want_mc_hash = multicast; set_bit(AR9170_FILTER_CHANGED_MULTICAST, &ar->filter_changed); } @@ -2543,6 +2549,7 @@ static const struct ieee80211_ops ar9170_ops = { .add_interface = ar9170_op_add_interface, .remove_interface = ar9170_op_remove_interface, .config = ar9170_op_config, + .prepare_multicast = ar9170_op_prepare_multicast, .configure_filter = ar9170_op_configure_filter, .conf_tx = ar9170_conf_tx, .bss_info_changed = ar9170_op_bss_info_changed, diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 2b3cf39dd4b..3951b5b1342 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -229,10 +229,12 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, static void ath5k_remove_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf); static int ath5k_config(struct ieee80211_hw *hw, u32 changed); +static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mc_list); static void ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, - int mc_count, struct dev_mc_list *mclist); + u64 multicast); static int ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -260,6 +262,7 @@ static const struct ieee80211_ops ath5k_hw_ops = { .add_interface = ath5k_add_interface, .remove_interface = ath5k_remove_interface, .config = ath5k_config, + .prepare_multicast = ath5k_prepare_multicast, .configure_filter = ath5k_configure_filter, .set_key = ath5k_set_key, .get_stats = ath5k_get_stats, @@ -2853,6 +2856,37 @@ unlock: return ret; } +static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mclist) +{ + u32 mfilt[2], val; + int i; + u8 pos; + + mfilt[0] = 0; + mfilt[1] = 1; + + for (i = 0; i < mc_count; i++) { + if (!mclist) + break; + /* calculate XOR of eight 6-bit values */ + val = get_unaligned_le32(mclist->dmi_addr + 0); + pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; + val = get_unaligned_le32(mclist->dmi_addr + 3); + pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; + pos &= 0x3f; + mfilt[pos / 32] |= (1 << (pos % 32)); + /* XXX: we might be able to just do this instead, + * but not sure, needs testing, if we do use this we'd + * neet to inform below to not reset the mcast */ + /* ath5k_hw_set_mcast_filterindex(ah, + * mclist->dmi_addr[5]); */ + mclist = mclist->next; + } + + return ((u64)(mfilt[1]) << 32) | mfilt[0]; +} + #define SUPPORTED_FIF_FLAGS \ FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | \ FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \ @@ -2878,16 +2912,14 @@ unlock: static void ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { struct ath5k_softc *sc = hw->priv; struct ath5k_hw *ah = sc->ah; - u32 mfilt[2], val, rfilt; - u8 pos; - int i; + u32 mfilt[2], rfilt; - mfilt[0] = 0; - mfilt[1] = 0; + mfilt[0] = multicast; + mfilt[1] = multicast >> 32; /* Only deal with supported flags */ changed_flags &= SUPPORTED_FIF_FLAGS; @@ -2913,24 +2945,6 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw, if (*new_flags & FIF_ALLMULTI) { mfilt[0] = ~0; mfilt[1] = ~0; - } else { - for (i = 0; i < mc_count; i++) { - if (!mclist) - break; - /* calculate XOR of eight 6-bit values */ - val = get_unaligned_le32(mclist->dmi_addr + 0); - pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; - val = get_unaligned_le32(mclist->dmi_addr + 3); - pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; - pos &= 0x3f; - mfilt[pos / 32] |= (1 << (pos % 32)); - /* XXX: we might be able to just do this instead, - * but not sure, needs testing, if we do use this we'd - * neet to inform below to not reset the mcast */ - /* ath5k_hw_set_mcast_filterindex(ah, - * mclist->dmi_addr[5]); */ - mclist = mclist->next; - } } /* This is the best we can do */ diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 3e09b9ac165..2f9c149fd48 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2394,8 +2394,7 @@ skip_chan_change: static void ath9k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, - struct dev_mc_list *mclist) + u64 multicast) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index c5bece09042..78ddbc7f836 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3679,7 +3679,7 @@ out_unlock: static void b43_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, unsigned int *fflags, - int mc_count, struct dev_addr_list *mc_list) + u64 multicast) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev = wl->current_dev; diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index b1435594921..b166a6f9f05 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -2836,9 +2836,7 @@ static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw, static void b43legacy_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, - unsigned int *fflags, - int mc_count, - struct dev_addr_list *mc_list) + unsigned int *fflags,u64 multicast) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); struct b43legacy_wldev *dev = wl->current_dev; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index c0efa6623c0..f1f6dabd8fb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -1514,7 +1514,7 @@ EXPORT_SYMBOL(iwl_irq_handle_error); void iwl_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mc_list) + u64 multicast) { struct iwl_priv *priv = hw->priv; __le32 *filter_flags = &priv->staging_rxon.filter_flags; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index 4ca025a34da..62d90364b61 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -282,8 +282,7 @@ int iwl_set_decrypted_flag(struct iwl_priv *priv, void iwl_irq_handle_error(struct iwl_priv *priv); void iwl_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, struct dev_addr_list *mc_list); + unsigned int *total_flags, u64 multicast); int iwl_hw_nic_init(struct iwl_priv *priv); int iwl_setup_mac(struct iwl_priv *priv); int iwl_set_hw_params(struct iwl_priv *priv); diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c index 4872345a2f6..019431d2f8a 100644 --- a/drivers/net/wireless/libertas_tf/main.c +++ b/drivers/net/wireless/libertas_tf/main.c @@ -366,15 +366,35 @@ static int lbtf_op_config(struct ieee80211_hw *hw, u32 changed) return 0; } +static u64 lbtf_op_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mclist) +{ + struct lbtf_private *priv = hw->priv; + int i; + + if (!mc_count || mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) + return mc_count; + + priv->nr_of_multicastmacaddr = mc_count; + for (i = 0; i < mc_count; i++) { + if (!mclist) + break; + memcpy(&priv->multicastlist[i], mclist->da_addr, + ETH_ALEN); + mclist = mclist->next; + } + + return mc_count; +} + #define SUPPORTED_FIF_FLAGS (FIF_PROMISC_IN_BSS | FIF_ALLMULTI) static void lbtf_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { struct lbtf_private *priv = hw->priv; int old_mac_control = priv->mac_control; - int i; changed_flags &= SUPPORTED_FIF_FLAGS; *new_flags &= SUPPORTED_FIF_FLAGS; @@ -386,20 +406,12 @@ static void lbtf_op_configure_filter(struct ieee80211_hw *hw, else priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE; if (*new_flags & (FIF_ALLMULTI) || - mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) { + multicast > MRVDRV_MAX_MULTICAST_LIST_SIZE) { priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; - } else if (mc_count) { + } else if (multicast) { priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE; - priv->nr_of_multicastmacaddr = mc_count; - for (i = 0; i < mc_count; i++) { - if (!mclist) - break; - memcpy(&priv->multicastlist[i], mclist->da_addr, - ETH_ALEN); - mclist = mclist->next; - } lbtf_cmd_set_mac_multicast_addr(priv); } else { priv->mac_control &= ~(CMD_ACT_MAC_MULTICAST_ENABLE | @@ -461,6 +473,7 @@ static const struct ieee80211_ops lbtf_ops = { .add_interface = lbtf_op_add_interface, .remove_interface = lbtf_op_remove_interface, .config = lbtf_op_config, + .prepare_multicast = lbtf_op_prepare_multicast, .configure_filter = lbtf_op_configure_filter, .bss_info_changed = lbtf_op_bss_info_changed, }; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 930f5c7da4a..6f6cd43592c 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -582,9 +582,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) static void mac80211_hwsim_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, - struct dev_addr_list *mc_list) + unsigned int *total_flags,u64 multicast) { struct mac80211_hwsim_data *data = hw->priv; diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 8a6d3afe412..f84387083e7 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3251,31 +3251,50 @@ mwl8k_configure_filter_exit: return rc; } +static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mclist) +{ + struct mwl8k_configure_filter_worker *worker; + + worker = kzalloc(sizeof(*worker), GFP_ATOMIC); + + if (!worker) + return 0; + + /* + * XXX: This is _HORRIBLY_ broken!! + * + * No locking, the mclist pointer might be invalid as soon as this + * function returns, something in the list might be invalidated + * once we get to the worker, etc... + */ + worker->mc_count = mc_count; + worker->mclist = mclist; + + return (u64)worker; +} + static void mwl8k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, - struct dev_addr_list *mclist) + u64 multicast) { - struct mwl8k_configure_filter_worker *worker; + struct mwl8k_configure_filter_worker *worker = (void *)multicast; struct mwl8k_priv *priv = hw->priv; /* Clear unsupported feature flags */ *total_flags &= MWL8K_SUPPORTED_IF_FLAGS; - if (!(changed_flags & MWL8K_SUPPORTED_IF_FLAGS) && !mc_count) + if (!(changed_flags & MWL8K_SUPPORTED_IF_FLAGS)) return; - worker = kzalloc(sizeof(*worker), GFP_ATOMIC); if (worker == NULL) return; worker->header.options = MWL8K_WQ_QUEUE_ONLY | MWL8K_WQ_TX_WAIT_EMPTY; worker->changed_flags = changed_flags; worker->total_flags = total_flags; - worker->mc_count = mc_count; - worker->mclist = mclist; mwl8k_queue_work(hw, &worker->header, priv->config_wq, mwl8k_configure_filter_wt); @@ -3441,6 +3460,7 @@ static const struct ieee80211_ops mwl8k_ops = { .remove_interface = mwl8k_remove_interface, .config = mwl8k_config, .bss_info_changed = mwl8k_bss_info_changed, + .prepare_multicast = mwl8k_prepare_multicast, .configure_filter = mwl8k_configure_filter, .set_rts_threshold = mwl8k_set_rts_threshold, .conf_tx = mwl8k_conf_tx, diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 77203e346cd..4d486bf9f72 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -302,7 +302,7 @@ out: static void p54_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { struct p54_common *priv = dev->priv; diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 99e89596cef..39d7d9baafd 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -978,7 +978,7 @@ int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed); void rt2x00mac_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mc_list); + u64 multicast); int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set); #ifdef CONFIG_RT2X00_LIB_CRYPTO diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index cb7b6d45933..602f1269971 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -379,7 +379,7 @@ EXPORT_SYMBOL_GPL(rt2x00mac_config); void rt2x00mac_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mc_list) + u64 multicast) { struct rt2x00_dev *rt2x00dev = hw->priv; diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c index 09f46abc730..16429c49139 100644 --- a/drivers/net/wireless/rtl818x/rtl8180_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c @@ -728,10 +728,16 @@ static void rtl8180_bss_info_changed(struct ieee80211_hw *dev, priv->rf->conf_erp(dev, info); } +static u64 rtl8180_prepare_multicast(struct ieee80211_hw *dev, int mc_count, + struct dev_addr_list *mc_list) +{ + return mc_count; +} + static void rtl8180_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mclist) + u64 multicast) { struct rtl8180_priv *priv = dev->priv; @@ -741,7 +747,7 @@ static void rtl8180_configure_filter(struct ieee80211_hw *dev, priv->rx_conf ^= RTL818X_RX_CONF_CTRL; if (changed_flags & FIF_OTHER_BSS) priv->rx_conf ^= RTL818X_RX_CONF_MONITOR; - if (*total_flags & FIF_ALLMULTI || mc_count > 0) + if (*total_flags & FIF_ALLMULTI || multicast > 0) priv->rx_conf |= RTL818X_RX_CONF_MULTICAST; else priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST; @@ -768,6 +774,7 @@ static const struct ieee80211_ops rtl8180_ops = { .remove_interface = rtl8180_remove_interface, .config = rtl8180_config, .bss_info_changed = rtl8180_bss_info_changed, + .prepare_multicast = rtl8180_prepare_multicast, .configure_filter = rtl8180_configure_filter, }; diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c index 53f57dc5222..90f38357393 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c @@ -1192,10 +1192,16 @@ static void rtl8187_bss_info_changed(struct ieee80211_hw *dev, info->use_short_preamble); } +static u64 rtl8187_prepare_multicast(struct ieee80211_hw *dev, + int mc_count, struct dev_addr_list *mc_list) +{ + return mc_count; +} + static void rtl8187_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mclist) + u64 multicast) { struct rtl8187_priv *priv = dev->priv; @@ -1205,7 +1211,7 @@ static void rtl8187_configure_filter(struct ieee80211_hw *dev, priv->rx_conf ^= RTL818X_RX_CONF_CTRL; if (changed_flags & FIF_OTHER_BSS) priv->rx_conf ^= RTL818X_RX_CONF_MONITOR; - if (*total_flags & FIF_ALLMULTI || mc_count > 0) + if (*total_flags & FIF_ALLMULTI || multicast > 0) priv->rx_conf |= RTL818X_RX_CONF_MULTICAST; else priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST; @@ -1268,6 +1274,7 @@ static const struct ieee80211_ops rtl8187_ops = { .remove_interface = rtl8187_remove_interface, .config = rtl8187_config, .bss_info_changed = rtl8187_bss_info_changed, + .prepare_multicast = rtl8187_prepare_multicast, .configure_filter = rtl8187_configure_filter, .conf_tx = rtl8187_conf_tx }; diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index 7148934e464..5809ef5b18f 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c @@ -652,9 +652,7 @@ out: static void wl1251_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, - unsigned int *total, - int mc_count, - struct dev_addr_list *mc_list) + unsigned int *total,u64 multicast) { struct wl1251 *wl = hw->priv; diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 4102d590b79..754be817930 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -793,9 +793,7 @@ out: static void wl1271_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, - unsigned int *total, - int mc_count, - struct dev_addr_list *mc_list) + unsigned int *total,u64 multicast) { struct wl1271 *wl = hw->priv; diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 55b7fbdc85d..6d666359a42 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -796,18 +796,40 @@ static void set_rx_filter_handler(struct work_struct *work) dev_err(zd_mac_dev(mac), "set_rx_filter_handler error %d\n", r); } +static u64 zd_op_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mclist) +{ + struct zd_mac *mac = zd_hw_mac(hw); + struct zd_mc_hash hash; + int i; + + zd_mc_clear(&hash); + + for (i = 0; i < mc_count; i++) { + if (!mclist) + break; + dev_dbg_f(zd_mac_dev(mac), "mc addr %pM\n", mclist->dmi_addr); + zd_mc_add_addr(&hash, mclist->dmi_addr); + mclist = mclist->next; + } + + return hash.low | ((u64)hash.high << 32); +} + #define SUPPORTED_FIF_FLAGS \ (FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \ FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC) static void zd_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { - struct zd_mc_hash hash; + struct zd_mc_hash hash = { + .low = multicast, + .high = multicast >> 32, + }; struct zd_mac *mac = zd_hw_mac(hw); unsigned long flags; - int i; /* Only deal with supported flags */ changed_flags &= SUPPORTED_FIF_FLAGS; @@ -819,25 +841,16 @@ static void zd_op_configure_filter(struct ieee80211_hw *hw, if (!changed_flags) return; - if (*new_flags & (FIF_PROMISC_IN_BSS | FIF_ALLMULTI)) { + if (*new_flags & (FIF_PROMISC_IN_BSS | FIF_ALLMULTI)) zd_mc_add_all(&hash); - } else { - zd_mc_clear(&hash); - for (i = 0; i < mc_count; i++) { - if (!mclist) - break; - dev_dbg_f(zd_mac_dev(mac), "mc addr %pM\n", - mclist->dmi_addr); - zd_mc_add_addr(&hash, mclist->dmi_addr); - mclist = mclist->next; - } - } spin_lock_irqsave(&mac->lock, flags); mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL); mac->pass_ctrl = !!(*new_flags & FIF_CONTROL); mac->multicast_hash = hash; spin_unlock_irqrestore(&mac->lock, flags); + + /* XXX: these can be called here now, can sleep now! */ queue_work(zd_workqueue, &mac->set_multicast_hash_work); if (changed_flags & FIF_CONTROL) @@ -940,6 +953,7 @@ static const struct ieee80211_ops zd_ops = { .add_interface = zd_op_add_interface, .remove_interface = zd_op_remove_interface, .config = zd_op_config, + .prepare_multicast = zd_op_prepare_multicast, .configure_filter = zd_op_configure_filter, .bss_info_changed = zd_op_bss_info_changed, .get_tsf = zd_op_get_tsf, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 76d43e12cc2..bc865206e5f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1219,10 +1219,13 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, * the driver's configure_filter() function which frames should be * passed to mac80211 and which should be filtered out. * - * The configure_filter() callback is invoked with the parameters - * @mc_count and @mc_list for the combined multicast address list - * of all virtual interfaces, @changed_flags telling which flags - * were changed and @total_flags with the new flag states. + * Before configure_filter() is invoked, the prepare_multicast() + * callback is invoked with the parameters @mc_count and @mc_list + * for the combined multicast address list of all virtual interfaces. + * It's use is optional, and it returns a u64 that is passed to + * configure_filter(). Additionally, configure_filter() has the + * arguments @changed_flags telling which flags were changed and + * @total_flags with the new flag states. * * If your device has no multicast address filters your driver will * need to check both the %FIF_ALLMULTI flag and the @mc_count @@ -1375,9 +1378,13 @@ enum ieee80211_ampdu_mlme_action { * for association indication. The @changed parameter indicates which * of the bss parameters has changed when a call is made. * + * @prepare_multicast: Prepare for multicast filter configuration. + * This callback is optional, and its return value is passed + * to configure_filter(). This callback must be atomic. + * * @configure_filter: Configure the device's RX filter. * See the section "Frame filtering" for more information. - * This callback must be implemented and atomic. + * This callback must be implemented. * * @set_tim: Set TIM bit. mac80211 calls this function when a TIM bit * must be set or cleared for a given STA. Must be atomic. @@ -1479,10 +1486,12 @@ struct ieee80211_ops { struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed); + u64 (*prepare_multicast)(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mc_list); void (*configure_filter)(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mc_list); + u64 multicast); int (*set_tim)(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set); int (*set_key)(struct ieee80211_hw *hw, enum set_key_cmd cmd, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 4100c361a99..d231c9323ad 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -55,16 +55,32 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, trace_drv_bss_info_changed(local, vif, info, changed); } +static inline u64 drv_prepare_multicast(struct ieee80211_local *local, + int mc_count, + struct dev_addr_list *mc_list) +{ + u64 ret = 0; + + if (local->ops->prepare_multicast) + ret = local->ops->prepare_multicast(&local->hw, mc_count, + mc_list); + + trace_drv_prepare_multicast(local, mc_count, ret); + + return ret; +} + static inline void drv_configure_filter(struct ieee80211_local *local, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, - struct dev_addr_list *mc_list) + u64 multicast) { + might_sleep(); + local->ops->configure_filter(&local->hw, changed_flags, total_flags, - mc_count, mc_list); + multicast); trace_drv_configure_filter(local, changed_flags, total_flags, - mc_count); + multicast); } static inline int drv_set_tim(struct ieee80211_local *local, diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 5a10da2d70f..37b9051afcf 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -191,31 +191,55 @@ TRACE_EVENT(drv_bss_info_changed, ) ); +TRACE_EVENT(drv_prepare_multicast, + TP_PROTO(struct ieee80211_local *local, int mc_count, u64 ret), + + TP_ARGS(local, mc_count, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(int, mc_count) + __field(u64, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->mc_count = mc_count; + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT " prepare mc (%d): %llx", + LOCAL_PR_ARG, __entry->mc_count, + (unsigned long long) __entry->ret + ) +); + TRACE_EVENT(drv_configure_filter, TP_PROTO(struct ieee80211_local *local, unsigned int changed_flags, unsigned int *total_flags, - int mc_count), + u64 multicast), - TP_ARGS(local, changed_flags, total_flags, mc_count), + TP_ARGS(local, changed_flags, total_flags, multicast), TP_STRUCT__entry( LOCAL_ENTRY __field(unsigned int, changed) __field(unsigned int, total) - __field(int, mc) + __field(u64, multicast) ), TP_fast_assign( LOCAL_ASSIGN; __entry->changed = changed_flags; __entry->total = *total_flags; - __entry->mc = mc_count; + __entry->multicast = multicast; ), TP_printk( - LOCAL_PR_FMT " changed:%#x total:%#x mc:%d", - LOCAL_PR_ARG, __entry->changed, __entry->total, __entry->mc + LOCAL_PR_FMT " changed:%#x total:%#x", + LOCAL_PR_ARG, __entry->changed, __entry->total ) ); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a6abc7dfd90..a07f01736a9 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -636,6 +636,9 @@ struct ieee80211_local { /* protects the aggregated multicast list and filter calls */ spinlock_t filter_lock; + /* used for uploading changed mc list */ + struct work_struct reconfig_filter; + /* aggregated multicast list */ struct dev_addr_list *mc_list; int mc_count; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index e8fb03b91a4..b161301056d 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -227,9 +227,7 @@ static int ieee80211_open(struct net_device *dev) if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS) local->fif_other_bss++; - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); break; default: conf.vif = &sdata->vif; @@ -241,17 +239,13 @@ static int ieee80211_open(struct net_device *dev) if (ieee80211_vif_is_mesh(&sdata->vif)) { local->fif_other_bss++; - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); ieee80211_start_mesh(sdata); } else if (sdata->vif.type == NL80211_IFTYPE_AP) { local->fif_pspoll++; - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); } changed |= ieee80211_reset_erp_info(sdata); @@ -404,10 +398,11 @@ static int ieee80211_stop(struct net_device *dev) spin_lock_bh(&local->filter_lock); __dev_addr_unsync(&local->mc_list, &local->mc_count, &dev->mc_list, &dev->mc_count); - ieee80211_configure_filter(local); spin_unlock_bh(&local->filter_lock); netif_addr_unlock_bh(dev); + ieee80211_configure_filter(local); + del_timer_sync(&local->dynamic_ps_timer); cancel_work_sync(&local->dynamic_ps_enable_work); @@ -458,9 +453,7 @@ static int ieee80211_stop(struct net_device *dev) if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS) local->fif_other_bss--; - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); break; case NL80211_IFTYPE_STATION: del_timer_sync(&sdata->u.mgd.chswitch_timer); @@ -503,9 +496,7 @@ static int ieee80211_stop(struct net_device *dev) local->fif_other_bss--; atomic_dec(&local->iff_allmultis); - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); ieee80211_stop_mesh(sdata); } @@ -622,8 +613,8 @@ static void ieee80211_set_multicast_list(struct net_device *dev) spin_lock_bh(&local->filter_lock); __dev_addr_sync(&local->mc_list, &local->mc_count, &dev->mc_list, &dev->mc_count); - ieee80211_configure_filter(local); spin_unlock_bh(&local->filter_lock); + ieee80211_queue_work(&local->hw, &local->reconfig_filter); } /* diff --git a/net/mac80211/main.c b/net/mac80211/main.c index b03fd84777f..05f923575fe 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -50,9 +50,9 @@ struct ieee80211_tx_status_rtap_hdr { } __attribute__ ((packed)); -/* must be called under mdev tx lock */ void ieee80211_configure_filter(struct ieee80211_local *local) { + u64 mc; unsigned int changed_flags; unsigned int new_flags = 0; @@ -62,7 +62,7 @@ void ieee80211_configure_filter(struct ieee80211_local *local) if (atomic_read(&local->iff_allmultis)) new_flags |= FIF_ALLMULTI; - if (local->monitors) + if (local->monitors || local->scanning) new_flags |= FIF_BCN_PRBRESP_PROMISC; if (local->fif_fcsfail) @@ -80,20 +80,30 @@ void ieee80211_configure_filter(struct ieee80211_local *local) if (local->fif_pspoll) new_flags |= FIF_PSPOLL; + spin_lock_bh(&local->filter_lock); changed_flags = local->filter_flags ^ new_flags; + mc = drv_prepare_multicast(local, local->mc_count, local->mc_list); + spin_unlock_bh(&local->filter_lock); + /* be a bit nasty */ new_flags |= (1<<31); - drv_configure_filter(local, changed_flags, &new_flags, - local->mc_count, - local->mc_list); + drv_configure_filter(local, changed_flags, &new_flags, mc); WARN_ON(new_flags & (1<<31)); local->filter_flags = new_flags & ~(1<<31); } +static void ieee80211_reconfig_filter(struct work_struct *work) +{ + struct ieee80211_local *local = + container_of(work, struct ieee80211_local, reconfig_filter); + + ieee80211_configure_filter(local); +} + int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) { struct ieee80211_channel *chan, *scan_chan; @@ -692,6 +702,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, INIT_WORK(&local->restart_work, ieee80211_restart_work); + INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); + INIT_WORK(&local->dynamic_ps_enable_work, ieee80211_dynamic_ps_enable_work); INIT_WORK(&local->dynamic_ps_disable_work, @@ -946,6 +958,8 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) rtnl_unlock(); + cancel_work_sync(&local->reconfig_filter); + ieee80211_clear_tx_pending(local); sta_info_stop(local); rate_control_deinitialize(local); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index e091cbc3434..1e04be6b912 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -292,13 +292,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) if (was_hw_scan) goto done; - spin_lock_bh(&local->filter_lock); - local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC; - drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC, - &local->filter_flags, - local->mc_count, - local->mc_list); - spin_unlock_bh(&local->filter_lock); + ieee80211_configure_filter(local); drv_sw_scan_complete(local); @@ -376,13 +370,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) local->next_scan_state = SCAN_DECISION; local->scan_channel_idx = 0; - spin_lock_bh(&local->filter_lock); - local->filter_flags |= FIF_BCN_PRBRESP_PROMISC; - drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC, - &local->filter_flags, - local->mc_count, - local->mc_list); - spin_unlock_bh(&local->filter_lock); + ieee80211_configure_filter(local); /* TODO: start scan as soon as all nullfunc frames are ACKed */ ieee80211_queue_delayed_work(&local->hw, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e55d57f559e..5eb306377c6 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1076,9 +1076,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) /* reconfigure hardware */ ieee80211_hw_config(local, ~0); - spin_lock_bh(&local->filter_lock); ieee80211_configure_filter(local); - spin_unlock_bh(&local->filter_lock); /* Finally also reconfigure all the BSS information */ list_for_each_entry(sdata, &local->interfaces, list) { -- cgit v1.2.3-70-g09d2 From 6a8171f261eec3577c2a5985e3a2b51377e48931 Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Wed, 26 Aug 2009 13:54:08 -0300 Subject: rtl8187: fix circular locking (rtl8187_stop/rtl8187_work) Larry Finger reports following lockdep warning: [ INFO: possible circular locking dependency detected ] 2.6.31-rc6-wl #201 ------------------------------------------------------- rfkill/30578 is trying to acquire lock: (&(&priv->work)->work#2){+.+...}, at: [] __cancel_work_timer+0xd9/0x222 but task is already holding lock: (&priv->conf_mutex#2){+.+.+.}, at: [] rtl8187_stop+0x31/0x364 [rtl8187] which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (&priv->conf_mutex#2){+.+.+.}: [] __lock_acquire+0x12d0/0x1614 [] lock_acquire+0xb9/0xdd [] mutex_lock_nested+0x56/0x2a8 [] rtl8187_work+0x3b/0xf2 [rtl8187] [] worker_thread+0x1fa/0x30a [] kthread+0x8f/0x97 [] child_rip+0xa/0x20 [] 0xffffffffffffffff -> #0 (&(&priv->work)->work#2){+.+...}: [] __lock_acquire+0x1005/0x1614 [] lock_acquire+0xb9/0xdd [] __cancel_work_timer+0x112/0x222 [] cancel_delayed_work_sync+0xd/0xf [] rtl8187_stop+0x34c/0x364 [rtl8187] [] ieee80211_stop_device+0x29/0x61 [mac80211] [] ieee80211_stop+0x476/0x530 [mac80211] [] dev_close+0x8a/0xac [] cfg80211_rfkill_set_block+0x4a/0x7a [cfg80211] [] rfkill_set_block+0x84/0xd9 [rfkill] [] rfkill_fop_write+0xda/0x124 [rfkill] [] vfs_write+0xae/0x14a [] sys_write+0x47/0x6e [] system_call_fastpath+0x16/0x1b [] 0xffffffffffffffff The problem here is that rtl8187_stop, while helding priv->conf_mutex, runs cancel_delayed_work_sync on an workqueue that runs rtl8187_work, which also takes priv->conf_mutex lock. Move cancel_delayed_work_sync out of rtl8187_stop priv->conf_mutex locking region. Reported-by: Larry Finger Tested-by: Larry Finger Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8187_dev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/rtl818x/rtl8187_dev.c') diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c index 90f38357393..5573e347680 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c @@ -1014,9 +1014,10 @@ static void rtl8187_stop(struct ieee80211_hw *dev) dev_kfree_skb_any(skb); usb_kill_anchored_urbs(&priv->anchored); + mutex_unlock(&priv->conf_mutex); + if (!priv->is_rtl8187b) cancel_delayed_work_sync(&priv->work); - mutex_unlock(&priv->conf_mutex); } static int rtl8187_add_interface(struct ieee80211_hw *dev, -- cgit v1.2.3-70-g09d2 From ca9152e37f57259ca92486ca5753af16fd9155c6 Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Wed, 26 Aug 2009 13:54:09 -0300 Subject: rtl8187: Implement rfkill support This change implements rfkill support for RTL8187B and RTL8187L devices, using new cfg80211 rfkill API. Acked-by: Larry Finger Tested-by: Hin-Tak Leung Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/Makefile | 2 +- drivers/net/wireless/rtl818x/rtl8187.h | 1 + drivers/net/wireless/rtl818x/rtl8187_dev.c | 28 +++++++----- drivers/net/wireless/rtl818x/rtl8187_leds.c | 4 +- drivers/net/wireless/rtl818x/rtl8187_rfkill.c | 63 +++++++++++++++++++++++++++ drivers/net/wireless/rtl818x/rtl8187_rfkill.h | 8 ++++ drivers/net/wireless/rtl818x/rtl818x.h | 5 ++- 7 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 drivers/net/wireless/rtl818x/rtl8187_rfkill.c create mode 100644 drivers/net/wireless/rtl818x/rtl8187_rfkill.h (limited to 'drivers/net/wireless/rtl818x/rtl8187_dev.c') diff --git a/drivers/net/wireless/rtl818x/Makefile b/drivers/net/wireless/rtl818x/Makefile index 37e3d4db0c4..93cbfbedb46 100644 --- a/drivers/net/wireless/rtl818x/Makefile +++ b/drivers/net/wireless/rtl818x/Makefile @@ -1,5 +1,5 @@ rtl8180-objs := rtl8180_dev.o rtl8180_rtl8225.o rtl8180_sa2400.o rtl8180_max2820.o rtl8180_grf5101.o -rtl8187-objs := rtl8187_dev.o rtl8187_rtl8225.o rtl8187_leds.o +rtl8187-objs := rtl8187_dev.o rtl8187_rtl8225.o rtl8187_leds.o rtl8187_rfkill.o obj-$(CONFIG_RTL8180) += rtl8180.o obj-$(CONFIG_RTL8187) += rtl8187.o diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h index c09bfefc70f..bf9175a8c1f 100644 --- a/drivers/net/wireless/rtl818x/rtl8187.h +++ b/drivers/net/wireless/rtl818x/rtl8187.h @@ -133,6 +133,7 @@ struct rtl8187_priv { __le16 bits16; __le32 bits32; } *io_dmabuf; + bool rfkill_off; }; void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c index 5573e347680..9679b29e1c4 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c @@ -32,6 +32,7 @@ #ifdef CONFIG_RTL8187_LEDS #include "rtl8187_leds.h" #endif +#include "rtl8187_rfkill.h" MODULE_AUTHOR("Michael Wu "); MODULE_AUTHOR("Andrea Merello "); @@ -648,10 +649,10 @@ static int rtl8187_init_hw(struct ieee80211_hw *dev) /* setup card */ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0); - rtl818x_iowrite8(priv, &priv->map->GPIO, 0); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8)); - rtl818x_iowrite8(priv, &priv->map->GPIO, 1); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 1); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); @@ -674,11 +675,11 @@ static int rtl8187_init_hw(struct ieee80211_hw *dev) /* host_usb_init */ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0); - rtl818x_iowrite8(priv, &priv->map->GPIO, 0); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0); reg = rtl818x_ioread8(priv, (u8 *)0xFE53); rtl818x_iowrite8(priv, (u8 *)0xFE53, reg | (1 << 7)); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8)); - rtl818x_iowrite8(priv, &priv->map->GPIO, 0x20); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x20); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x80); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x80); @@ -907,12 +908,12 @@ static int rtl8187_start(struct ieee80211_hw *dev) u32 reg; int ret; + mutex_lock(&priv->conf_mutex); + ret = (!priv->is_rtl8187b) ? rtl8187_init_hw(dev) : rtl8187b_init_hw(dev); if (ret) - return ret; - - mutex_lock(&priv->conf_mutex); + goto rtl8187_start_exit; init_usb_anchor(&priv->anchored); priv->dev = dev; @@ -939,8 +940,7 @@ static int rtl8187_start(struct ieee80211_hw *dev) (7 << 21 /* MAX TX DMA */)); rtl8187_init_urbs(dev); rtl8187b_init_status_urb(dev); - mutex_unlock(&priv->conf_mutex); - return 0; + goto rtl8187_start_exit; } rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); @@ -984,9 +984,10 @@ static int rtl8187_start(struct ieee80211_hw *dev) reg |= RTL818X_CMD_RX_ENABLE; rtl818x_iowrite8(priv, &priv->map->CMD, reg); INIT_DELAYED_WORK(&priv->work, rtl8187_work); - mutex_unlock(&priv->conf_mutex); - return 0; +rtl8187_start_exit: + mutex_unlock(&priv->conf_mutex); + return ret; } static void rtl8187_stop(struct ieee80211_hw *dev) @@ -1277,7 +1278,8 @@ static const struct ieee80211_ops rtl8187_ops = { .bss_info_changed = rtl8187_bss_info_changed, .prepare_multicast = rtl8187_prepare_multicast, .configure_filter = rtl8187_configure_filter, - .conf_tx = rtl8187_conf_tx + .conf_tx = rtl8187_conf_tx, + .rfkill_poll = rtl8187_rfkill_poll }; static void rtl8187_eeprom_register_read(struct eeprom_93cx6 *eeprom) @@ -1517,6 +1519,7 @@ static int __devinit rtl8187_probe(struct usb_interface *intf, reg &= 0xFF; rtl8187_leds_init(dev, reg); #endif + rtl8187_rfkill_init(dev); return 0; @@ -1540,6 +1543,7 @@ static void __devexit rtl8187_disconnect(struct usb_interface *intf) #ifdef CONFIG_RTL8187_LEDS rtl8187_leds_exit(dev); #endif + rtl8187_rfkill_exit(dev); ieee80211_unregister_hw(dev); priv = dev->priv; diff --git a/drivers/net/wireless/rtl818x/rtl8187_leds.c b/drivers/net/wireless/rtl818x/rtl8187_leds.c index a6cfb7e7799..a1c670fc155 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_leds.c +++ b/drivers/net/wireless/rtl818x/rtl8187_leds.c @@ -42,7 +42,7 @@ static void led_turn_on(struct work_struct *work) mutex_lock(&priv->conf_mutex); switch (led->ledpin) { case LED_PIN_GPIO0: - rtl818x_iowrite8(priv, &priv->map->GPIO, 0x01); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x00); break; case LED_PIN_LED0: @@ -80,7 +80,7 @@ static void led_turn_off(struct work_struct *work) mutex_lock(&priv->conf_mutex); switch (led->ledpin) { case LED_PIN_GPIO0: - rtl818x_iowrite8(priv, &priv->map->GPIO, 0x01); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x01); break; case LED_PIN_LED0: diff --git a/drivers/net/wireless/rtl818x/rtl8187_rfkill.c b/drivers/net/wireless/rtl818x/rtl8187_rfkill.c new file mode 100644 index 00000000000..9fab13e4004 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187_rfkill.c @@ -0,0 +1,63 @@ +/* + * Linux RFKILL support for RTL8187 + * + * Copyright (c) 2009 Herton Ronaldo Krzesinski + * + * Based on the RFKILL handling in the r8187 driver, which is: + * Copyright (c) Realtek Semiconductor Corp. All rights reserved. + * + * Thanks to Realtek for their support! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "rtl8187.h" + +static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) +{ + u8 gpio; + + gpio = rtl818x_ioread8(priv, &priv->map->GPIO0); + rtl818x_iowrite8(priv, &priv->map->GPIO0, gpio & ~0x02); + gpio = rtl818x_ioread8(priv, &priv->map->GPIO1); + + return gpio & 0x02; +} + +void rtl8187_rfkill_init(struct ieee80211_hw *hw) +{ + struct rtl8187_priv *priv = hw->priv; + + priv->rfkill_off = rtl8187_is_radio_enabled(priv); + printk(KERN_INFO "rtl8187: wireless switch is %s\n", + priv->rfkill_off ? "on" : "off"); + wiphy_rfkill_set_hw_state(hw->wiphy, !priv->rfkill_off); + wiphy_rfkill_start_polling(hw->wiphy); +} + +void rtl8187_rfkill_poll(struct ieee80211_hw *hw) +{ + bool enabled; + struct rtl8187_priv *priv = hw->priv; + + mutex_lock(&priv->conf_mutex); + enabled = rtl8187_is_radio_enabled(priv); + if (unlikely(enabled != priv->rfkill_off)) { + priv->rfkill_off = enabled; + printk(KERN_INFO "rtl8187: wireless radio switch turned %s\n", + enabled ? "on" : "off"); + wiphy_rfkill_set_hw_state(hw->wiphy, !enabled); + } + mutex_unlock(&priv->conf_mutex); +} + +void rtl8187_rfkill_exit(struct ieee80211_hw *hw) +{ + wiphy_rfkill_stop_polling(hw->wiphy); +} diff --git a/drivers/net/wireless/rtl818x/rtl8187_rfkill.h b/drivers/net/wireless/rtl818x/rtl8187_rfkill.h new file mode 100644 index 00000000000..e12575e96d1 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187_rfkill.h @@ -0,0 +1,8 @@ +#ifndef RTL8187_RFKILL_H +#define RTL8187_RFKILL_H + +void rtl8187_rfkill_init(struct ieee80211_hw *hw); +void rtl8187_rfkill_poll(struct ieee80211_hw *hw); +void rtl8187_rfkill_exit(struct ieee80211_hw *hw); + +#endif /* RTL8187_RFKILL_H */ diff --git a/drivers/net/wireless/rtl818x/rtl818x.h b/drivers/net/wireless/rtl818x/rtl818x.h index 562222e6cf1..8522490d2e2 100644 --- a/drivers/net/wireless/rtl818x/rtl818x.h +++ b/drivers/net/wireless/rtl818x/rtl818x.h @@ -138,8 +138,9 @@ struct rtl818x_csr { __le32 RF_PARA; __le32 RF_TIMING; u8 GP_ENABLE; - u8 GPIO; - u8 reserved_12[2]; + u8 GPIO0; + u8 GPIO1; + u8 reserved_12; __le32 HSSI_PARA; u8 reserved_13[4]; u8 TX_AGC_CTL; -- cgit v1.2.3-70-g09d2