Search Linux Wireless

Re: [PATCH 1/2] mac80211: hardware scan rework (V3)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> The scan code in mac80211 makes the software scan assumption in various
> places. For example, we stop the Tx queue during a software scan so that
> all the Tx packets will be queued by the stack. We also drop frames not
> related to scan in the software scan process. But these are not true for
> hardware scan.
> 
> Some wireless hardwares (for example iwl3945/4965) has the ability to
> perform the whole scan process by hardware and/or firmware. The hardware
> scan is relative powerful in that it tries to maintain normal network
> traffic while doing a scan in the background. Some drivers (i.e iwlwifi)
> do provide a way to tune the hardware scan parameters (for example if the
> STA is associated, what's the max time could the STA leave from the
> associated channel, how long the scans get suspended after returning to
> the service channel, etc). But basically this is transparent to the
> stack. mac80211 should not stop Tx queues or drop Rx packets during a
> hardware scan.
> 
> This patch resolves the above problem by spliting the current scan
> indicator local->sta_scanning into local->sta_sw_scanning and
> local->sta_hw_scanning. It then changes the scan related code to be aware
> of hardware scan or software scan in various places. With this patch,
> iwlwifi performs much better in the scan-while-associated condition and
> disable_hw_scan=1 should never be required.
> 
> Cc: Mohamed Abbas <mohamed.abbas@xxxxxxxxx>
> Cc: Ben Cahill <ben.m.cahill@xxxxxxxxx>
> Signed-off-by: Zhu Yi <yi.zhu@xxxxxxxxx>

Looks good to me.
Acked-by: Johannes Berg <johannes@xxxxxxxxxxxxxxxx>

> 
> diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c
> index 59350b8..acbe717 100644
> --- a/net/mac80211/ieee80211.c
> +++ b/net/mac80211/ieee80211.c
> @@ -351,11 +351,14 @@ static int ieee80211_stop(struct net_device *dev)
>  		synchronize_rcu();
>  		skb_queue_purge(&sdata->u.sta.skb_queue);
>  
> -		if (!local->ops->hw_scan &&
> -		    local->scan_dev == sdata->dev) {
> -			local->sta_scanning = 0;
> -			cancel_delayed_work(&local->scan_work);
> +		if (local->scan_dev == sdata->dev) {
> +			if (!local->ops->hw_scan) {
> +				local->sta_sw_scanning = 0;
> +				cancel_delayed_work(&local->scan_work);
> +			} else
> +				local->sta_hw_scanning = 0;
>  		}
> +
>  		flush_workqueue(local->hw.workqueue);
>  		/* fall through */
>  	default:
> @@ -523,7 +526,7 @@ int ieee80211_hw_config(struct ieee80211_local *local)
>  	struct ieee80211_channel *chan;
>  	int ret = 0;
>  
> -	if (local->sta_scanning) {
> +	if (local->sta_sw_scanning) {
>  		chan = local->scan_channel;
>  		mode = local->scan_hw_mode;
>  	} else {
> diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
> index 72e1c93..35829b1 100644
> --- a/net/mac80211/ieee80211_i.h
> +++ b/net/mac80211/ieee80211_i.h
> @@ -470,7 +470,8 @@ struct ieee80211_local {
>  
>  	struct list_head interfaces;
>  
> -	int sta_scanning;
> +	bool sta_sw_scanning;
> +	bool sta_hw_scanning;
>  	int scan_channel_idx;
>  	enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
>  	unsigned long last_scan_completed;
> @@ -749,7 +750,8 @@ int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len);
>  void ieee80211_sta_req_auth(struct net_device *dev,
>  			    struct ieee80211_if_sta *ifsta);
>  int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len);
> -void ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb,
> +ieee80211_txrx_result ieee80211_sta_rx_scan(struct net_device *dev,
> +					    struct sk_buff *skb,
>  			   struct ieee80211_rx_status *rx_status);
>  void ieee80211_rx_bss_list_init(struct net_device *dev);
>  void ieee80211_rx_bss_list_deinit(struct net_device *dev);
> diff --git a/net/mac80211/ieee80211_ioctl.c b/net/mac80211/ieee80211_ioctl.c
> index 942b9cc..f2d84f3 100644
> --- a/net/mac80211/ieee80211_ioctl.c
> +++ b/net/mac80211/ieee80211_ioctl.c
> @@ -315,7 +315,7 @@ int ieee80211_set_channel(struct ieee80211_local *local, int channel, int freq)
>  	}
>  
>  	if (set) {
> -		if (local->sta_scanning)
> +		if (local->sta_sw_scanning)
>  			ret = 0;
>  		else
>  			ret = ieee80211_hw_config(local);
> @@ -558,8 +558,10 @@ static int ieee80211_ioctl_giwscan(struct net_device *dev,
>  {
>  	int res;
>  	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
> -	if (local->sta_scanning)
> +
> +	if (local->sta_sw_scanning || local->sta_hw_scanning)
>  		return -EAGAIN;
> +
>  	res = ieee80211_sta_scan_results(dev, extra, data->length);
>  	if (res >= 0) {
>  		data->length = res;
> diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c
> index 015b3f8..26f404a 100644
> --- a/net/mac80211/ieee80211_sta.c
> +++ b/net/mac80211/ieee80211_sta.c
> @@ -1487,8 +1487,18 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
>  		u32 supp_rates, prev_rates;
>  		int i, j;
>  
> -		mode = local->sta_scanning ?
> +		mode = local->sta_sw_scanning ?
>  		       local->scan_hw_mode : local->oper_hw_mode;
> +
> +		if (local->sta_hw_scanning) {
> +			/* search for the correct mode matches the beacon */
> +			list_for_each_entry(mode, &local->modes_list, list)
> +				if (mode->mode == rx_status->phymode)
> +					break;
> +
> +			if (mode == NULL)
> +				mode = local->oper_hw_mode;
> +		}
>  		rates = mode->rates;
>  		num_rates = mode->num_rates;
>  
> @@ -1871,31 +1881,39 @@ static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev,
>  }
>  
> 
> -void ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb,
> -			   struct ieee80211_rx_status *rx_status)
> +ieee80211_txrx_result
> +ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb,
> +		      struct ieee80211_rx_status *rx_status)
>  {
>  	struct ieee80211_mgmt *mgmt;
>  	u16 fc;
>  
> -	if (skb->len < 24) {
> -		dev_kfree_skb(skb);
> -		return;
> -	}
> +	if (skb->len < 2)
> +		return TXRX_DROP;
>  
>  	mgmt = (struct ieee80211_mgmt *) skb->data;
>  	fc = le16_to_cpu(mgmt->frame_control);
>  
> +	if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL)
> +		return TXRX_CONTINUE;
> +
> +	if (skb->len < 24)
> +		return TXRX_DROP;
> +
>  	if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
>  		if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP) {
>  			ieee80211_rx_mgmt_probe_resp(dev, mgmt,
>  						     skb->len, rx_status);
> +			dev_kfree_skb(skb);
> +			return TXRX_QUEUED;
>  		} else if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON) {
>  			ieee80211_rx_mgmt_beacon(dev, mgmt, skb->len,
>  						 rx_status);
> +			dev_kfree_skb(skb);
> +			return TXRX_QUEUED;
>  		}
>  	}
> -
> -	dev_kfree_skb(skb);
> +	return TXRX_CONTINUE;
>  }
>  
> 
> @@ -1985,7 +2003,7 @@ void ieee80211_sta_work(struct work_struct *work)
>  	if (!netif_running(dev))
>  		return;
>  
> -	if (local->sta_scanning)
> +	if (local->sta_sw_scanning || local->sta_hw_scanning)
>  		return;
>  
>  	if (sdata->type != IEEE80211_IF_TYPE_STA &&
> @@ -2643,9 +2661,15 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
>  	union iwreq_data wrqu;
>  
>  	local->last_scan_completed = jiffies;
> -	wmb();
> -	local->sta_scanning = 0;
> +	memset(&wrqu, 0, sizeof(wrqu));
> +	wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
>  
> +	if (local->sta_hw_scanning) {
> +		local->sta_hw_scanning = 0;
> +		goto done;
> +	}
> +
> +	local->sta_sw_scanning = 0;
>  	if (ieee80211_hw_config(local))
>  		printk(KERN_DEBUG "%s: failed to restore operational"
>  		       "channel after scan\n", dev->name);
> @@ -2661,9 +2685,6 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
>  
>  	netif_tx_unlock_bh(local->mdev);
>  
> -	memset(&wrqu, 0, sizeof(wrqu));
> -	wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
> -
>  	rcu_read_lock();
>  	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
>  
> @@ -2681,6 +2702,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
>  	}
>  	rcu_read_unlock();
>  
> +done:
>  	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
>  	if (sdata->type == IEEE80211_IF_TYPE_IBSS) {
>  		struct ieee80211_if_sta *ifsta = &sdata->u.sta;
> @@ -2703,7 +2725,7 @@ void ieee80211_sta_scan_work(struct work_struct *work)
>  	int skip;
>  	unsigned long next_delay = 0;
>  
> -	if (!local->sta_scanning)
> +	if (!local->sta_sw_scanning)
>  		return;
>  
>  	switch (local->scan_state) {
> @@ -2766,7 +2788,7 @@ void ieee80211_sta_scan_work(struct work_struct *work)
>  		break;
>  	}
>  
> -	if (local->sta_scanning)
> +	if (local->sta_sw_scanning)
>  		queue_delayed_work(local->hw.workqueue, &local->scan_work,
>  				   next_delay);
>  }
> @@ -2798,7 +2820,7 @@ static int ieee80211_sta_start_scan(struct net_device *dev,
>  	  * ResultCode: SUCCESS, INVALID_PARAMETERS
>  	 */
>  
> -	if (local->sta_scanning) {
> +	if (local->sta_sw_scanning || local->sta_hw_scanning) {
>  		if (local->scan_dev == dev)
>  			return 0;
>  		return -EBUSY;
> @@ -2806,15 +2828,15 @@ static int ieee80211_sta_start_scan(struct net_device *dev,
>  
>  	if (local->ops->hw_scan) {
>  		int rc = local->ops->hw_scan(local_to_hw(local),
> -					    ssid, ssid_len);
> +					     ssid, ssid_len);
>  		if (!rc) {
> -			local->sta_scanning = 1;
> +			local->sta_hw_scanning = 1;
>  			local->scan_dev = dev;
>  		}
>  		return rc;
>  	}
>  
> -	local->sta_scanning = 1;
> +	local->sta_sw_scanning = 1;
>  
>  	rcu_read_lock();
>  	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
> @@ -2869,7 +2891,7 @@ int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len)
>  	if (sdata->type != IEEE80211_IF_TYPE_STA)
>  		return ieee80211_sta_start_scan(dev, ssid, ssid_len);
>  
> -	if (local->sta_scanning) {
> +	if (local->sta_sw_scanning || local->sta_hw_scanning) {
>  		if (local->scan_dev == dev)
>  			return 0;
>  		return -EBUSY;
> diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
> index 428a9fc..596ff71 100644
> --- a/net/mac80211/rx.c
> +++ b/net/mac80211/rx.c
> @@ -338,8 +338,14 @@ ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx)
>  	struct ieee80211_local *local = rx->local;
>  	struct sk_buff *skb = rx->skb;
>  
> -	if (unlikely(local->sta_scanning != 0)) {
> -		ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status);
> +	if (unlikely(local->sta_hw_scanning))
> +		return ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status);
> +
> +	if (unlikely(local->sta_sw_scanning)) {
> +		/* drop all the other packets during a software scan anyway */
> +		if (ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status)
> +		    != TXRX_QUEUED)
> +			dev_kfree_skb(skb);
>  		return TXRX_QUEUED;
>  	}
>  
> @@ -1486,7 +1492,7 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
>  		goto end;
>  	}
>  
> -	if (unlikely(local->sta_scanning))
> +	if (unlikely(local->sta_sw_scanning || local->sta_hw_scanning))
>  		rx.flags |= IEEE80211_TXRXD_RXIN_SCAN;
>  
>  	if (__ieee80211_invoke_rx_handlers(local, local->rx_pre_handlers, &rx,
> diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
> index 1a53154..a6a657f 100644
> --- a/net/mac80211/tx.c
> +++ b/net/mac80211/tx.c
> @@ -225,7 +225,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
>  	if (unlikely(tx->flags & IEEE80211_TXRXD_TX_INJECTED))
>  		return TXRX_CONTINUE;
>  
> -	if (unlikely(tx->local->sta_scanning != 0) &&
> +	if (unlikely(tx->local->sta_sw_scanning) &&
>  	    ((tx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
>  	     (tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ))
>  		return TXRX_DROP;
> -
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

Attachment: signature.asc
Description: This is a digitally signed message part


[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux