Revert "rndis_wlan: wait association to complete"
Subsystems (5)
PATCH 1/2 Revert "rndis_wlan: wait association to complete"
"rndis_wlan: wait association to complete" would block SIOCSIWESSID to fix broken WPA associations. This is wrong way of fixing the problem, so revert this one.
This reverts commit 1f717679fe4e2c804bab964b60bb01c69ef015c7.
---
drivers/net/wireless/rndis_wlan.c | 47 ++++--------------------------------- 1 files changed, 5 insertions(+), 42 deletions(-)
drivers/net/wireless/rndis_wlan.c +5 -42
@@ -378,7 +378,6 @@ enum wpa_key_mgmt { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE,#define WORK_LINK_UP (1<<0)#define WORK_LINK_DOWN (1<<1)#define WORK_SET_MULTICAST_LIST (1<<2) -#define WORK_ASSOC_WAIT (1<<3)#define COMMAND_BUFFER_SIZE (CONTROL_BUFFER_SIZE + sizeof(struct rndis_set))
@@ -437,7 +436,6 @@ struct rndis_wlan_private {struct work_struct work;struct mutex command_lock;spinlock_t stats_lock; -struct completion assoc_wait;unsigned long work_pending;struct ieee80211_supported_band band;
@@ -938,9 +936,7 @@ static int get_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid) } -#define ASSOC_TIMEOUT_JIFFIES (HZ*10) -static int set_essid (struct usbnet *usbdev,struct ndis_80211_ssid *ssid, -bool wait) +static int set_essid (struct usbnet *usbdev,struct ndis_80211_ssid *ssid) {struct rndis_wlan_private *priv =get_rndis_wlan_priv (usbdev);int ret;
@@ -952,21 +948,6 @@ static int set_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid,devdbg (usbdev,"set_essid: radio_on = 1" ); } -if (!wait) -return ret; - -/* If we return now, userspace would get association events too late -* (after receiving first packets from access point). This causes -* WPA authentication to fail. -*/ -set_bit (WORK_ASSOC_WAIT , &priv->work_pending); -queue_work (priv->workqueue, &priv->work); - -wait_for_completion_interruptible_timeout (&priv->assoc_wait, -ASSOC_TIMEOUT_JIFFIES ); - -clear_bit (WORK_ASSOC_WAIT , &priv->work_pending); -return ret; }
@@ -1028,7 +1009,7 @@ static int disassociate(struct usbnet *usbdev, int reset_ssid) ssid.essid[1 ] =0xff ;for (i =2 ; i <sizeof (ssid.essid); i++) ssid.essid[i] =0x1 + (ssid.essid[i] *0xfe /0xff ); - ret =set_essid (usbdev, &ssid, false); + ret =set_essid (usbdev, &ssid); }return ret; }
@@ -1784,7 +1765,7 @@ static int rndis_iw_set_essid(struct net_device *dev,if (!wrqu->essid.flags || length ==0 )return disassociate (usbdev,1 );else -return set_essid (usbdev, &ssid, true); +return set_essid (usbdev, &ssid); }
@@ -2025,7 +2006,7 @@ static int rndis_iw_set_encode(struct net_device *dev,if (index == priv->encr_tx_key_index)/* ndis drivers want essid to be set after setting encr */ -set_essid (usbdev, &priv->essid, false); +set_essid (usbdev, &priv->essid);return 0 ; }
@@ -2307,20 +2288,7 @@ static void rndis_wlan_worker(struct work_struct *work)unsigned char bssid[ETH_ALEN ];struct ndis_80211_assoc_info *info;int assoc_size =sizeof (*info) +IW_CUSTOM_MAX +32 ; -int ret, offset, len; -__le32 tmp =0 ; - -if (test_bit (WORK_ASSOC_WAIT , &priv->work_pending)) { -/* dummy OID to poll device */ - len =sizeof (tmp); -rndis_query_oid (usbdev,OID_GEN_XMIT_ERROR , &tmp, &len); - -/* requeue work if no link up response from device */ -if (!test_bit (WORK_LINK_UP , &priv->work_pending)) { -msleep (10 ); -queue_work (priv->workqueue, &priv->work); - } - } +int ret, offset;if (test_and_clear_bit (WORK_LINK_UP , &priv->work_pending)) {netif_carrier_on (usbdev->net);
@@ -2360,8 +2328,6 @@ get_bssid:memcpy (evt.ap_addr.sa_data, bssid,ETH_ALEN );wireless_send_event (usbdev->net,SIOCGIWAP , &evt,NULL ); } - -complete_all (&priv->assoc_wait); }if (test_and_clear_bit (WORK_LINK_DOWN , &priv->work_pending)) {
@@ -2864,7 +2830,6 @@ static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf)mutex_init (&priv->command_lock);spin_lock_init (&priv->stats_lock); -init_completion (&priv->assoc_wait);/* because rndis_command() sleeps we need to use workqueue */ priv->workqueue =create_singlethread_workqueue ("rndis_wlan" );
@@ -2967,7 +2932,6 @@ static void rndis_wlan_unbind(struct usbnet *usbdev, struct usb_interface *intf)/* turn radio off */ disassociate (usbdev,0 ); -complete_all (&priv->assoc_wait);cancel_delayed_work_sync (&priv->stats_work);cancel_delayed_work_sync (&priv->scan_work);cancel_work_sync (&priv->work);
@@ -3017,7 +2981,6 @@ static int rndis_wlan_stop(struct usbnet *usbdev) retval =disassociate (usbdev,0 ); priv->work_pending =0 ; -complete_all (&priv->assoc_wait);cancel_delayed_work_sync (&priv->stats_work);cancel_delayed_work_sync (&priv->scan_work);cancel_work_sync (&priv->work);
PATCH 2/2 usbnet: add rx queue pausing
Add rx queue pausing to usbnet. This is needed by rndis_wlan so that it can control rx queue and prevent received packets from being send forward before rndis_wlan receives and handles 'media connect'-indication. Without this establishing WPA connections is hard and fail often.
---
drivers/net/usb/usbnet.c | 52 ++++++++++++++++++++++++++++++++++++- drivers/net/wireless/rndis_wlan.c | 13 +++++++++ include/linux/usb/usbnet.h | 6 ++++ 3 files changed, 69 insertions(+), 2 deletions(-)
drivers/net/usb/usbnet.c +51 -1
@@ -233,6 +233,19 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb) {int status; +if (test_bit (EVENT_RX_PAUSED , &dev->flags)) { +struct sk_buff *skb2; + + skb2 =skb_clone (skb,GFP_ATOMIC ); +if (unlikely (!skb2)) +return ; + +skb_queue_tail (&dev->rxq_pause, skb2); +kfree_skb (skb); + +return ; + } + skb->protocol =eth_type_trans (skb, dev->net); dev->net->stats.rx_packets++; dev->net->stats.rx_bytes += skb->len;
@@ -526,6 +539,41 @@ static void intr_complete (struct urb *urb) }/*-------------------------------------------------------------------------*/ +void usbnet_pause_rx (struct usbnet *dev) +{ +set_bit (EVENT_RX_PAUSED , &dev->flags); + +if (netif_msg_rx_status (dev)) +devdbg (dev,"paused rx queue enabled" ); +} +EXPORT_SYMBOL_GPL (usbnet_pause_rx); + +void usbnet_resume_rx (struct usbnet *dev) +{ +struct sk_buff *skb; +int num =0 ; + +clear_bit (EVENT_RX_PAUSED , &dev->flags); + +while ((skb =skb_dequeue (&dev->rxq_pause)) !=NULL ) { +usbnet_skb_return (dev, skb); + num++; + } + +tasklet_schedule (&dev->bh); + +if (netif_msg_rx_status (dev)) +devdbg (dev,"paused rx queue disabled, %d skbs requeued" , num); +} +EXPORT_SYMBOL_GPL (usbnet_resume_rx); + +void usbnet_purge_paused_rxq (struct usbnet *dev) +{ +skb_queue_purge (&dev->rxq_pause); +} +EXPORT_SYMBOL_GPL (usbnet_purge_paused_rxq); + +/*-------------------------------------------------------------------------*/ // unlink pending rx/tx; completion handlers do all other cleanup
@@ -623,6 +671,8 @@ int usbnet_stop (struct net_device *net)usb_kill_urb (dev->interrupt); +usbnet_purge_paused_rxq (dev); +/* deferred work (task, timer, softirq) must also stop. * can't flush_scheduled_work() until we drop rtnl (later), * else workers could deadlock; so make workers a NOP.
@@ -1113,7 +1163,6 @@ static void usbnet_bh (unsigned long param) } -/*------------------------------------------------------------------------- * * USB Device Driver support
@@ -1210,6 +1259,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)skb_queue_head_init (&dev->rxq);skb_queue_head_init (&dev->txq);skb_queue_head_init (&dev->done); +skb_queue_head_init (&dev->rxq_pause); dev->bh.func = usbnet_bh; dev->bh.data = (unsigned long ) dev;INIT_WORK (&dev->kevent, kevent);
drivers/net/wireless/rndis_wlan.c +12 -1
@@ -1764,8 +1764,15 @@ static int rndis_iw_set_essid(struct net_device *dev,if (!wrqu->essid.flags || length ==0 )return disassociate (usbdev,1 ); -else +else { +/* Pause and purge rx queue, so we don't pass packets before +* 'media connect'-indication. +*/ +usbnet_pause_rx (usbdev); +usbnet_purge_paused_rxq (usbdev); +return set_essid (usbdev, &ssid); + } }
@@ -2328,6 +2335,8 @@ get_bssid:memcpy (evt.ap_addr.sa_data, bssid,ETH_ALEN );wireless_send_event (usbdev->net,SIOCGIWAP , &evt,NULL ); } + +usbnet_resume_rx (usbdev); }if (test_and_clear_bit (WORK_LINK_DOWN , &priv->work_pending)) {
@@ -2541,6 +2550,8 @@ static void rndis_wlan_indication(struct usbnet *usbdev, void *ind, int buflen)switch (msg->status) {case RNDIS_STATUS_MEDIA_CONNECT : +usbnet_pause_rx (usbdev); +devinfo (usbdev,"media connect" );/* queue work to avoid recursive calls into rndis_command */
include/linux/usb/usbnet.h +6 -0
@@ -53,6 +53,7 @@ struct usbnet {struct sk_buff_head rxq;struct sk_buff_head txq;struct sk_buff_head done; +struct sk_buff_head rxq_pause;struct urb *interrupt;struct tasklet_struct bh;
@@ -63,6 +64,7 @@ struct usbnet {# define EVENT_RX_MEMORY 2# define EVENT_STS_SPLIT 3# define EVENT_LINK_RESET 4 +# define EVENT_RX_PAUSED 5 };static inline struct usb_driver *driver_of (struct usb_interface *intf)
@@ -190,6 +192,10 @@ extern void usbnet_defer_kevent (struct usbnet *, int);extern void usbnet_skb_return (struct usbnet *,struct sk_buff *);extern void usbnet_unlink_rx_urbs (struct usbnet *); +extern void usbnet_pause_rx (struct usbnet *); +extern void usbnet_resume_rx (struct usbnet *); +extern void usbnet_purge_paused_rxq (struct usbnet *); +extern int usbnet_get_settings (struct net_device *net,struct ethtool_cmd *cmd);extern int usbnet_set_settings (struct net_device *net,struct ethtool_cmd *cmd);extern u32 usbnet_get_link (struct net_device *net);
Johannes Berg
maintainerRe: [PATCH 2/2] usbnet: add rx queue pausing
drivers/net/usb/usbnet.c void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb) · line 244
Quoted 9 lines · Jussi Kivilinna
drivers/net/usb/usbnet.c +9
-0+if (test_bit (EVENT_RX_PAUSED , &dev->flags)) { +struct sk_buff *skb2; + + skb2 =skb_clone (skb,GFP_ATOMIC ); +if (unlikely (!skb2)) +return ; + +skb_queue_tail (&dev->rxq_pause, skb2); +kfree_skb (skb);
Why clone and then free the original?
johannes
Re: [PATCH 2/2] usbnet: add rx queue pausing
Quoting "Johannes Berg" <[email protected]>:
Quoted 14 lines · Jussi Kivilinna
drivers/net/usb/usbnet.c void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb) · line 244
Quoted 10 lines
drivers/net/usb/usbnet.c +9
-0+if (test_bit (EVENT_RX_PAUSED , &dev->flags)) { +struct sk_buff *skb2; + + skb2 =skb_clone (skb,GFP_ATOMIC ); +if (unlikely (!skb2)) +return ; + +skb_queue_tail (&dev->rxq_pause, skb2); +kfree_skb (skb);Why clone and then free the original?
Ah, I had some problems and let this in. No reason, works fine without cloning. I'll post v2 soon.
-Jussi
Johannes Berg
maintainerRe: [PATCH 2/2] usbnet: add rx queue pausing
In reply to Jussi Kivilinnadrivers/net/usb/usbnet.c void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb) · line 241
Quoted 6 lines · Jussi Kivilinna
drivers/net/usb/usbnet.c +6
-0+if (test_bit (EVENT_RX_PAUSED , &dev->flags)) { +struct sk_buff *skb2; + + skb2 =skb_clone (skb,GFP_ATOMIC ); +if (unlikely (!skb2)) +return ;
that leaks the original afaict.
johannes