* [PATCH] d80211: Fix concurrency issues in ieee80211_sta.c
@ 2007-02-09 5:02 Michael Wu
2007-02-09 5:28 ` Michael Wu
0 siblings, 1 reply; 4+ messages in thread
From: Michael Wu @ 2007-02-09 5:02 UTC (permalink / raw)
To: Jiri Benc; +Cc: linux-wireless
[-- Attachment #1: Type: text/plain, Size: 17108 bytes --]
d80211: Fix concurrency issues in ieee80211_sta.c
This fixes most concurrency issues in ieee80211_sta.c and partially
prevents scans from running over an association in progress and vice versa.
This is achieved by forcing all potentially racy code to run from a
workqueue.
Signed-off-by: Michael Wu <flamingice@sourmilk.net>
---
net/d80211/ieee80211.c | 6 +
net/d80211/ieee80211_i.h | 9 ++
net/d80211/ieee80211_iface.c | 5 +
net/d80211/ieee80211_sta.c | 202 ++++++++++++++++++++++++++++++------------
4 files changed, 162 insertions(+), 60 deletions(-)
diff --git a/net/d80211/ieee80211.c b/net/d80211/ieee80211.c
index 7a92bfe..055e8a2 100644
--- a/net/d80211/ieee80211.c
+++ b/net/d80211/ieee80211.c
@@ -2179,7 +2179,9 @@ void ieee80211_if_shutdown(struct net_de
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS:
sdata->u.sta.state = IEEE80211_DISABLED;
- cancel_delayed_work(&sdata->u.sta.work);
+ del_timer_sync(&sdata->u.sta.timer);
+ flush_scheduled_work();
+ skb_queue_purge(&sdata->u.sta.skb_queue);
if (!local->ops->hw_scan &&
local->scan_dev == sdata->dev) {
local->sta_scanning = 0;
@@ -4023,7 +4025,7 @@ void ieee80211_rx_irqsafe(struct ieee802
skb->dev = local->mdev;
/* copy status into skb->cb for use by tasklet */
- memcpy(skb->cb, status, sizeof(struct ieee80211_rx_status));
+ memcpy(skb->cb, status, sizeof(*status));
skb->pkt_type = ieee80211_rx_msg;
skb_queue_tail(&local->skb_queue, skb);
tasklet_schedule(&local->tasklet);
diff --git a/net/d80211/ieee80211_i.h b/net/d80211/ieee80211_i.h
index bb8fc83..9307882 100644
--- a/net/d80211/ieee80211_i.h
+++ b/net/d80211/ieee80211_i.h
@@ -242,7 +242,8 @@ struct ieee80211_if_sta {
IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED,
IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED
} state;
- struct delayed_work work;
+ struct timer_list timer;
+ struct work_struct work;
u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
u8 ssid[IEEE80211_MAX_SSID_LEN];
size_t ssid_len;
@@ -267,6 +268,11 @@ struct ieee80211_if_sta {
unsigned int create_ibss:1;
unsigned int mixed_cell:1;
unsigned int wmm_enabled:1;
+#define IEEE80211_STA_REQ_SCAN 0
+#define IEEE80211_STA_REQ_AUTH 1
+#define IEEE80211_STA_REQ_RUN 2
+ unsigned long request;
+ struct sk_buff_head skb_queue;
int key_mgmt;
unsigned long last_probe;
@@ -660,6 +666,7 @@ int ieee80211_set_compression(struct iee
struct net_device *dev, struct sta_info *sta);
int ieee80211_init_client(struct net_device *dev);
/* ieee80211_sta.c */
+void ieee80211_sta_timer(unsigned long data);
void ieee80211_sta_work(struct work_struct *work);
void ieee80211_sta_scan_work(struct work_struct *work);
void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb,
diff --git a/net/d80211/ieee80211_iface.c b/net/d80211/ieee80211_iface.c
index 342ad2a..939e289 100644
--- a/net/d80211/ieee80211_iface.c
+++ b/net/d80211/ieee80211_iface.c
@@ -185,7 +185,10 @@ void ieee80211_if_set_type(struct net_de
struct ieee80211_if_sta *ifsta;
ifsta = &sdata->u.sta;
- INIT_DELAYED_WORK(&ifsta->work, ieee80211_sta_work);
+ INIT_WORK(&ifsta->work, ieee80211_sta_work);
+ setup_timer(&ifsta->timer, ieee80211_sta_timer,
+ (unsigned long) ifsta);
+ skb_queue_head_init(&ifsta->skb_queue);
ifsta->capab = WLAN_CAPABILITY_ESS;
ifsta->auth_algs = IEEE80211_AUTH_ALG_OPEN |
diff --git a/net/d80211/ieee80211_sta.c b/net/d80211/ieee80211_sta.c
index 0e45a65..7b6a76b 100644
--- a/net/d80211/ieee80211_sta.c
+++ b/net/d80211/ieee80211_sta.c
@@ -64,6 +64,10 @@ static void ieee80211_rx_bss_put(struct
static int ieee80211_sta_find_ibss(struct net_device *dev,
struct ieee80211_if_sta *ifsta);
static int ieee80211_sta_wep_configured(struct net_device *dev);
+static int ieee80211_sta_start_scan(struct net_device *dev,
+ u8 *ssid, size_t ssid_len);
+static void ieee80211_sta_reset_auth(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta);
/* Parsed Information Elements */
@@ -466,7 +470,7 @@ static void ieee80211_authenticate(struc
ieee80211_send_auth(dev, ifsta, 1, NULL, 0, 0);
- schedule_delayed_work(&ifsta->work, IEEE80211_AUTH_TIMEOUT);
+ mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
}
@@ -694,7 +698,7 @@ static void ieee80211_associate(struct n
ieee80211_send_assoc(dev, ifsta);
- schedule_delayed_work(&ifsta->work, IEEE80211_ASSOC_TIMEOUT);
+ mod_timer(&ifsta->timer, jiffies + IEEE80211_ASSOC_TIMEOUT);
}
@@ -752,10 +756,10 @@ static void ieee80211_associated(struct
memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
- schedule_delayed_work(&ifsta->work,
+ mod_timer(&ifsta->timer, jiffies +
IEEE80211_MONITORING_INTERVAL + 30 * HZ);
} else {
- schedule_delayed_work(&ifsta->work,
+ mod_timer(&ifsta->timer, jiffies +
IEEE80211_MONITORING_INTERVAL);
}
}
@@ -846,8 +850,7 @@ static void ieee80211_auth_completed(str
static void ieee80211_auth_challenge(struct net_device *dev,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
+ size_t len)
{
u8 *pos;
struct ieee802_11_elems elems;
@@ -873,8 +876,7 @@ static void ieee80211_auth_challenge(str
static void ieee80211_rx_mgmt_auth(struct net_device *dev,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
+ size_t len)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
u16 auth_alg, auth_transaction, status_code;
@@ -993,8 +995,7 @@ static void ieee80211_rx_mgmt_auth(struc
if (ifsta->auth_transaction == 4)
ieee80211_auth_completed(dev, ifsta);
else
- ieee80211_auth_challenge(dev, ifsta, mgmt, len,
- rx_status);
+ ieee80211_auth_challenge(dev, ifsta, mgmt, len);
break;
}
}
@@ -1003,8 +1004,7 @@ static void ieee80211_rx_mgmt_auth(struc
static void ieee80211_rx_mgmt_deauth(struct net_device *dev,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
+ size_t len)
{
u16 reason_code;
@@ -1037,7 +1037,7 @@ static void ieee80211_rx_mgmt_deauth(str
ifsta->state == IEEE80211_ASSOCIATE ||
ifsta->state == IEEE80211_ASSOCIATED) {
ifsta->state = IEEE80211_AUTHENTICATE;
- schedule_delayed_work(&ifsta->work,
+ mod_timer(&ifsta->timer, jiffies +
IEEE80211_RETRY_AUTH_INTERVAL);
}
@@ -1049,8 +1049,7 @@ static void ieee80211_rx_mgmt_deauth(str
static void ieee80211_rx_mgmt_disassoc(struct net_device *dev,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
+ size_t len)
{
u16 reason_code;
@@ -1080,7 +1079,7 @@ static void ieee80211_rx_mgmt_disassoc(s
if (ifsta->state == IEEE80211_ASSOCIATED) {
ifsta->state = IEEE80211_ASSOCIATE;
- schedule_delayed_work(&ifsta->work,
+ mod_timer(&ifsta->timer, jiffies +
IEEE80211_RETRY_AUTH_INTERVAL);
}
@@ -1092,7 +1091,6 @@ static void ieee80211_rx_mgmt_assoc_resp
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
size_t len,
- struct ieee80211_rx_status *rx_status,
int reassoc)
{
struct ieee80211_local *local = dev->ieee80211_ptr;
@@ -1735,6 +1733,47 @@ void ieee80211_sta_rx_mgmt(struct net_de
switch (fc & IEEE80211_FCTL_STYPE) {
case IEEE80211_STYPE_PROBE_REQ:
+ case IEEE80211_STYPE_PROBE_RESP:
+ case IEEE80211_STYPE_BEACON:
+ case IEEE80211_STYPE_AUTH:
+ case IEEE80211_STYPE_ASSOC_RESP:
+ case IEEE80211_STYPE_REASSOC_RESP:
+ case IEEE80211_STYPE_DEAUTH:
+ case IEEE80211_STYPE_DISASSOC:
+ memcpy(skb->cb, rx_status, sizeof(*rx_status));
+ skb_queue_tail(&ifsta->skb_queue, skb);
+ schedule_work(&ifsta->work);
+ return;
+ default:
+ printk(KERN_DEBUG "%s: received unknown management frame - "
+ "stype=%d\n", dev->name,
+ (fc & IEEE80211_FCTL_STYPE) >> 4);
+ break;
+ }
+
+ fail:
+ dev_kfree_skb(skb);
+}
+
+
+static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev,
+ struct sk_buff *skb)
+{
+ struct ieee80211_rx_status *rx_status;
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_if_sta *ifsta;
+ struct ieee80211_mgmt *mgmt;
+ u16 fc;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ ifsta = &sdata->u.sta;
+
+ rx_status = (struct ieee80211_rx_status *) skb->cb;
+ mgmt = (struct ieee80211_mgmt *) skb->data;
+ fc = le16_to_cpu(mgmt->frame_control);
+
+ switch (fc & IEEE80211_FCTL_STYPE) {
+ case IEEE80211_STYPE_PROBE_REQ:
ieee80211_rx_mgmt_probe_req(dev, ifsta, mgmt, skb->len,
rx_status);
break;
@@ -1745,33 +1784,21 @@ void ieee80211_sta_rx_mgmt(struct net_de
ieee80211_rx_mgmt_beacon(dev, mgmt, skb->len, rx_status);
break;
case IEEE80211_STYPE_AUTH:
- ieee80211_rx_mgmt_auth(dev, ifsta, mgmt, skb->len, rx_status);
+ ieee80211_rx_mgmt_auth(dev, ifsta, mgmt, skb->len);
break;
case IEEE80211_STYPE_ASSOC_RESP:
- ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len,
- rx_status, 0);
+ ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len, 0);
break;
case IEEE80211_STYPE_REASSOC_RESP:
- ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len,
- rx_status, 1);
+ ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len, 1);
break;
case IEEE80211_STYPE_DEAUTH:
- ieee80211_rx_mgmt_deauth(dev, ifsta, mgmt, skb->len,
- rx_status);
+ ieee80211_rx_mgmt_deauth(dev, ifsta, mgmt, skb->len);
break;
case IEEE80211_STYPE_DISASSOC:
- ieee80211_rx_mgmt_disassoc(dev, ifsta, mgmt, skb->len,
- rx_status);
- break;
- default:
- printk(KERN_DEBUG "%s: received unknown management frame - "
- "stype=%d\n", dev->name,
- (fc & IEEE80211_FCTL_STYPE) >> 4);
+ ieee80211_rx_mgmt_disassoc(dev, ifsta, mgmt, skb->len);
break;
}
-
- fail:
- dev_kfree_skb(skb);
}
@@ -1844,7 +1871,7 @@ static void ieee80211_sta_expire(struct
static void ieee80211_sta_merge_ibss(struct net_device *dev,
struct ieee80211_if_sta *ifsta)
{
- schedule_delayed_work(&ifsta->work, IEEE80211_IBSS_MERGE_INTERVAL);
+ mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL);
ieee80211_sta_expire(dev);
if (ieee80211_sta_active_ibss(dev))
@@ -1856,16 +1883,32 @@ static void ieee80211_sta_merge_ibss(str
}
+void ieee80211_sta_timer(unsigned long data)
+{
+ struct ieee80211_if_sta *ifsta = (struct ieee80211_if_sta *) data;
+ set_bit(IEEE80211_STA_REQ_RUN, &ifsta->request);
+ schedule_work(&ifsta->work);
+}
+
+
void ieee80211_sta_work(struct work_struct *work)
{
struct ieee80211_sub_if_data *sdata =
- container_of(work, struct ieee80211_sub_if_data, u.sta.work.work);
+ container_of(work, struct ieee80211_sub_if_data, u.sta.work);
struct net_device *dev = sdata->dev;
+ struct ieee80211_local *local = dev->ieee80211_ptr;
struct ieee80211_if_sta *ifsta;
+ struct sk_buff *skb;
if (!netif_running(dev))
return;
+ /* TODO: scan_dev check should be removed once scan_completed wakes
+ * every STA interface */
+ if (local->sta_scanning &&
+ local->scan_dev == dev)
+ return;
+
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type != IEEE80211_IF_TYPE_STA &&
sdata->type != IEEE80211_IF_TYPE_IBSS) {
@@ -1875,6 +1918,22 @@ void ieee80211_sta_work(struct work_stru
}
ifsta = &sdata->u.sta;
+ while ((skb = skb_dequeue(&ifsta->skb_queue)))
+ ieee80211_sta_rx_queued_mgmt(dev, skb);
+
+ if (ifsta->state != IEEE80211_AUTHENTICATE &&
+ ifsta->state != IEEE80211_ASSOCIATE &&
+ test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request)) {
+ ieee80211_sta_start_scan(dev, NULL, 0);
+ return;
+ }
+
+ if (test_and_clear_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request)) {
+ ifsta->state = IEEE80211_AUTHENTICATE;
+ ieee80211_sta_reset_auth(dev, ifsta);
+ } else if (!test_and_clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request))
+ return;
+
switch (ifsta->state) {
case IEEE80211_DISABLED:
break;
@@ -1909,14 +1968,10 @@ void ieee80211_sta_work(struct work_stru
}
-static void ieee80211_sta_new_auth(struct net_device *dev,
- struct ieee80211_if_sta *ifsta)
+static void ieee80211_sta_reset_auth(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta)
{
struct ieee80211_local *local = dev->ieee80211_ptr;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- if (sdata->type != IEEE80211_IF_TYPE_STA)
- return;
if (local->ops->reset_tsf) {
/* Reset own TSF to allow time synchronization work. */
@@ -1938,7 +1993,19 @@ static void ieee80211_sta_new_auth(struc
ifsta->auth_alg);
ifsta->auth_transaction = -1;
ifsta->associated = ifsta->auth_tries = ifsta->assoc_tries = 0;
- ieee80211_authenticate(dev, ifsta);
+}
+
+
+static void ieee80211_sta_new_auth(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->type != IEEE80211_IF_TYPE_STA)
+ return;
+
+ set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request);
+ schedule_work(&ifsta->work);
}
@@ -2123,7 +2190,7 @@ static int ieee80211_sta_join_ibss(struc
}
ifsta->state = IEEE80211_IBSS_JOINED;
- schedule_delayed_work(&ifsta->work, IEEE80211_IBSS_MERGE_INTERVAL);
+ mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL);
ieee80211_rx_bss_put(dev, bss);
@@ -2240,7 +2307,7 @@ static int ieee80211_sta_find_ibss(struc
/* Selected IBSS not found in current scan results - try to scan */
if (ifsta->state == IEEE80211_IBSS_JOINED &&
!ieee80211_sta_active_ibss(dev)) {
- schedule_delayed_work(&ifsta->work,
+ mod_timer(&ifsta->timer, jiffies +
IEEE80211_IBSS_MERGE_INTERVAL);
} else if (time_after(jiffies, local->last_scan_completed +
IEEE80211_SCAN_INTERVAL)) {
@@ -2269,7 +2336,7 @@ static int ieee80211_sta_find_ibss(struc
}
ifsta->state = IEEE80211_IBSS_SEARCH;
- schedule_delayed_work(&ifsta->work, interval);
+ mod_timer(&ifsta->timer, jiffies + interval);
return 0;
}
@@ -2432,8 +2499,9 @@ void ieee80211_scan_completed(struct iee
union iwreq_data wrqu;
printk(KERN_DEBUG "%s: scan completed\n", dev->name);
- local->sta_scanning = 0;
local->last_scan_completed = jiffies;
+ wmb();
+ local->sta_scanning = 0;
memset(&wrqu, 0, sizeof(wrqu));
wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
@@ -2444,7 +2512,9 @@ void ieee80211_scan_completed(struct iee
(!ifsta->state == IEEE80211_IBSS_JOINED &&
!ieee80211_sta_active_ibss(dev)))
ieee80211_sta_find_ibss(dev, ifsta);
- }
+ /* TODO: need to wake every sta interface */
+ } else if (sdata->type == IEEE80211_IF_TYPE_STA)
+ ieee80211_sta_timer((unsigned long)&sdata->u.sta);
}
EXPORT_SYMBOL(ieee80211_scan_completed);
@@ -2533,16 +2603,13 @@ void ieee80211_sta_scan_work(struct work
break;
}
- if (local->sta_scanning) {
- if (next_delay)
- schedule_delayed_work(&local->scan_work, next_delay);
- else
- schedule_work(&local->scan_work.work);
- }
+ if (local->sta_scanning)
+ schedule_delayed_work(&local->scan_work, next_delay);
}
-int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len)
+static int ieee80211_sta_start_scan(struct net_device *dev,
+ u8 *ssid, size_t ssid_len)
{
struct ieee80211_local *local = dev->ieee80211_ptr;
@@ -2603,12 +2670,35 @@ int ieee80211_sta_req_scan(struct net_de
list);
local->scan_channel_idx = 0;
local->scan_dev = dev;
- schedule_work(&local->scan_work.work);
+ schedule_delayed_work(&local->scan_work, 0);
return 0;
}
+int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct ieee80211_local *local = dev->ieee80211_ptr;
+
+ if (sdata->type != IEEE80211_IF_TYPE_STA)
+ return ieee80211_sta_start_scan(dev, ssid, ssid_len);
+
+ if (local->sta_scanning) {
+ if (local->scan_dev == dev)
+ return 0;
+ return -EBUSY;
+ }
+
+ if (time_after(local->last_scan_completed + HZ, jiffies))
+ return -EAGAIN;
+
+ set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request);
+ schedule_work(&ifsta->work);
+ return 0;
+}
+
static char *
ieee80211_sta_scan_result(struct net_device *dev,
struct ieee80211_sta_bss *bss,
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] d80211: Fix concurrency issues in ieee80211_sta.c
2007-02-09 5:02 [PATCH] d80211: Fix concurrency issues in ieee80211_sta.c Michael Wu
@ 2007-02-09 5:28 ` Michael Wu
2007-02-09 7:37 ` Michael Wu
0 siblings, 1 reply; 4+ messages in thread
From: Michael Wu @ 2007-02-09 5:28 UTC (permalink / raw)
To: Jiri Benc; +Cc: linux-wireless
[-- Attachment #1.1: Type: text/plain, Size: 595 bytes --]
On Friday 09 February 2007 00:02, Michael Wu wrote:
> @@ -4023,7 +4025,7 @@ void ieee80211_rx_irqsafe(struct ieee802
>
> skb->dev = local->mdev;
> /* copy status into skb->cb for use by tasklet */
> - memcpy(skb->cb, status, sizeof(struct ieee80211_rx_status));
> + memcpy(skb->cb, status, sizeof(*status));
> skb->pkt_type = ieee80211_rx_msg;
> skb_queue_tail(&local->skb_queue, skb);
> tasklet_schedule(&local->tasklet);
Opps.. that was suppose to be part of the previous patch. Fixed patches
attached which just merge that chunk into the previous patch.
-Michael Wu
[-- Attachment #1.2: workqueue-sync --]
[-- Type: text/x-diff, Size: 16780 bytes --]
d80211: Fix concurrency issues in ieee80211_sta.c
From: Michael Wu <flamingice@sourmilk.net>
This fixes most concurrency issues in ieee80211_sta.c and partially
prevents scans from running over an association in progress and vice versa.
This is achieved by forcing all potentially racy code to run from a
workqueue.
Signed-off-by: Michael Wu <flamingice@sourmilk.net>
---
net/d80211/ieee80211.c | 4 +
net/d80211/ieee80211_i.h | 9 ++
net/d80211/ieee80211_iface.c | 5 +
net/d80211/ieee80211_sta.c | 202 ++++++++++++++++++++++++++++++------------
4 files changed, 161 insertions(+), 59 deletions(-)
diff --git a/net/d80211/ieee80211.c b/net/d80211/ieee80211.c
index 2fb66b3..055e8a2 100644
--- a/net/d80211/ieee80211.c
+++ b/net/d80211/ieee80211.c
@@ -2179,7 +2179,9 @@ void ieee80211_if_shutdown(struct net_de
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS:
sdata->u.sta.state = IEEE80211_DISABLED;
- cancel_delayed_work(&sdata->u.sta.work);
+ del_timer_sync(&sdata->u.sta.timer);
+ flush_scheduled_work();
+ skb_queue_purge(&sdata->u.sta.skb_queue);
if (!local->ops->hw_scan &&
local->scan_dev == sdata->dev) {
local->sta_scanning = 0;
diff --git a/net/d80211/ieee80211_i.h b/net/d80211/ieee80211_i.h
index bb8fc83..9307882 100644
--- a/net/d80211/ieee80211_i.h
+++ b/net/d80211/ieee80211_i.h
@@ -242,7 +242,8 @@ struct ieee80211_if_sta {
IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED,
IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED
} state;
- struct delayed_work work;
+ struct timer_list timer;
+ struct work_struct work;
u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
u8 ssid[IEEE80211_MAX_SSID_LEN];
size_t ssid_len;
@@ -267,6 +268,11 @@ struct ieee80211_if_sta {
unsigned int create_ibss:1;
unsigned int mixed_cell:1;
unsigned int wmm_enabled:1;
+#define IEEE80211_STA_REQ_SCAN 0
+#define IEEE80211_STA_REQ_AUTH 1
+#define IEEE80211_STA_REQ_RUN 2
+ unsigned long request;
+ struct sk_buff_head skb_queue;
int key_mgmt;
unsigned long last_probe;
@@ -660,6 +666,7 @@ int ieee80211_set_compression(struct iee
struct net_device *dev, struct sta_info *sta);
int ieee80211_init_client(struct net_device *dev);
/* ieee80211_sta.c */
+void ieee80211_sta_timer(unsigned long data);
void ieee80211_sta_work(struct work_struct *work);
void ieee80211_sta_scan_work(struct work_struct *work);
void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb,
diff --git a/net/d80211/ieee80211_iface.c b/net/d80211/ieee80211_iface.c
index 342ad2a..939e289 100644
--- a/net/d80211/ieee80211_iface.c
+++ b/net/d80211/ieee80211_iface.c
@@ -185,7 +185,10 @@ void ieee80211_if_set_type(struct net_de
struct ieee80211_if_sta *ifsta;
ifsta = &sdata->u.sta;
- INIT_DELAYED_WORK(&ifsta->work, ieee80211_sta_work);
+ INIT_WORK(&ifsta->work, ieee80211_sta_work);
+ setup_timer(&ifsta->timer, ieee80211_sta_timer,
+ (unsigned long) ifsta);
+ skb_queue_head_init(&ifsta->skb_queue);
ifsta->capab = WLAN_CAPABILITY_ESS;
ifsta->auth_algs = IEEE80211_AUTH_ALG_OPEN |
diff --git a/net/d80211/ieee80211_sta.c b/net/d80211/ieee80211_sta.c
index 0e45a65..7b6a76b 100644
--- a/net/d80211/ieee80211_sta.c
+++ b/net/d80211/ieee80211_sta.c
@@ -64,6 +64,10 @@ static void ieee80211_rx_bss_put(struct
static int ieee80211_sta_find_ibss(struct net_device *dev,
struct ieee80211_if_sta *ifsta);
static int ieee80211_sta_wep_configured(struct net_device *dev);
+static int ieee80211_sta_start_scan(struct net_device *dev,
+ u8 *ssid, size_t ssid_len);
+static void ieee80211_sta_reset_auth(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta);
/* Parsed Information Elements */
@@ -466,7 +470,7 @@ static void ieee80211_authenticate(struc
ieee80211_send_auth(dev, ifsta, 1, NULL, 0, 0);
- schedule_delayed_work(&ifsta->work, IEEE80211_AUTH_TIMEOUT);
+ mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
}
@@ -694,7 +698,7 @@ static void ieee80211_associate(struct n
ieee80211_send_assoc(dev, ifsta);
- schedule_delayed_work(&ifsta->work, IEEE80211_ASSOC_TIMEOUT);
+ mod_timer(&ifsta->timer, jiffies + IEEE80211_ASSOC_TIMEOUT);
}
@@ -752,10 +756,10 @@ static void ieee80211_associated(struct
memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
- schedule_delayed_work(&ifsta->work,
+ mod_timer(&ifsta->timer, jiffies +
IEEE80211_MONITORING_INTERVAL + 30 * HZ);
} else {
- schedule_delayed_work(&ifsta->work,
+ mod_timer(&ifsta->timer, jiffies +
IEEE80211_MONITORING_INTERVAL);
}
}
@@ -846,8 +850,7 @@ static void ieee80211_auth_completed(str
static void ieee80211_auth_challenge(struct net_device *dev,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
+ size_t len)
{
u8 *pos;
struct ieee802_11_elems elems;
@@ -873,8 +876,7 @@ static void ieee80211_auth_challenge(str
static void ieee80211_rx_mgmt_auth(struct net_device *dev,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
+ size_t len)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
u16 auth_alg, auth_transaction, status_code;
@@ -993,8 +995,7 @@ static void ieee80211_rx_mgmt_auth(struc
if (ifsta->auth_transaction == 4)
ieee80211_auth_completed(dev, ifsta);
else
- ieee80211_auth_challenge(dev, ifsta, mgmt, len,
- rx_status);
+ ieee80211_auth_challenge(dev, ifsta, mgmt, len);
break;
}
}
@@ -1003,8 +1004,7 @@ static void ieee80211_rx_mgmt_auth(struc
static void ieee80211_rx_mgmt_deauth(struct net_device *dev,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
+ size_t len)
{
u16 reason_code;
@@ -1037,7 +1037,7 @@ static void ieee80211_rx_mgmt_deauth(str
ifsta->state == IEEE80211_ASSOCIATE ||
ifsta->state == IEEE80211_ASSOCIATED) {
ifsta->state = IEEE80211_AUTHENTICATE;
- schedule_delayed_work(&ifsta->work,
+ mod_timer(&ifsta->timer, jiffies +
IEEE80211_RETRY_AUTH_INTERVAL);
}
@@ -1049,8 +1049,7 @@ static void ieee80211_rx_mgmt_deauth(str
static void ieee80211_rx_mgmt_disassoc(struct net_device *dev,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
+ size_t len)
{
u16 reason_code;
@@ -1080,7 +1079,7 @@ static void ieee80211_rx_mgmt_disassoc(s
if (ifsta->state == IEEE80211_ASSOCIATED) {
ifsta->state = IEEE80211_ASSOCIATE;
- schedule_delayed_work(&ifsta->work,
+ mod_timer(&ifsta->timer, jiffies +
IEEE80211_RETRY_AUTH_INTERVAL);
}
@@ -1092,7 +1091,6 @@ static void ieee80211_rx_mgmt_assoc_resp
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
size_t len,
- struct ieee80211_rx_status *rx_status,
int reassoc)
{
struct ieee80211_local *local = dev->ieee80211_ptr;
@@ -1735,6 +1733,47 @@ void ieee80211_sta_rx_mgmt(struct net_de
switch (fc & IEEE80211_FCTL_STYPE) {
case IEEE80211_STYPE_PROBE_REQ:
+ case IEEE80211_STYPE_PROBE_RESP:
+ case IEEE80211_STYPE_BEACON:
+ case IEEE80211_STYPE_AUTH:
+ case IEEE80211_STYPE_ASSOC_RESP:
+ case IEEE80211_STYPE_REASSOC_RESP:
+ case IEEE80211_STYPE_DEAUTH:
+ case IEEE80211_STYPE_DISASSOC:
+ memcpy(skb->cb, rx_status, sizeof(*rx_status));
+ skb_queue_tail(&ifsta->skb_queue, skb);
+ schedule_work(&ifsta->work);
+ return;
+ default:
+ printk(KERN_DEBUG "%s: received unknown management frame - "
+ "stype=%d\n", dev->name,
+ (fc & IEEE80211_FCTL_STYPE) >> 4);
+ break;
+ }
+
+ fail:
+ dev_kfree_skb(skb);
+}
+
+
+static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev,
+ struct sk_buff *skb)
+{
+ struct ieee80211_rx_status *rx_status;
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_if_sta *ifsta;
+ struct ieee80211_mgmt *mgmt;
+ u16 fc;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ ifsta = &sdata->u.sta;
+
+ rx_status = (struct ieee80211_rx_status *) skb->cb;
+ mgmt = (struct ieee80211_mgmt *) skb->data;
+ fc = le16_to_cpu(mgmt->frame_control);
+
+ switch (fc & IEEE80211_FCTL_STYPE) {
+ case IEEE80211_STYPE_PROBE_REQ:
ieee80211_rx_mgmt_probe_req(dev, ifsta, mgmt, skb->len,
rx_status);
break;
@@ -1745,33 +1784,21 @@ void ieee80211_sta_rx_mgmt(struct net_de
ieee80211_rx_mgmt_beacon(dev, mgmt, skb->len, rx_status);
break;
case IEEE80211_STYPE_AUTH:
- ieee80211_rx_mgmt_auth(dev, ifsta, mgmt, skb->len, rx_status);
+ ieee80211_rx_mgmt_auth(dev, ifsta, mgmt, skb->len);
break;
case IEEE80211_STYPE_ASSOC_RESP:
- ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len,
- rx_status, 0);
+ ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len, 0);
break;
case IEEE80211_STYPE_REASSOC_RESP:
- ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len,
- rx_status, 1);
+ ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len, 1);
break;
case IEEE80211_STYPE_DEAUTH:
- ieee80211_rx_mgmt_deauth(dev, ifsta, mgmt, skb->len,
- rx_status);
+ ieee80211_rx_mgmt_deauth(dev, ifsta, mgmt, skb->len);
break;
case IEEE80211_STYPE_DISASSOC:
- ieee80211_rx_mgmt_disassoc(dev, ifsta, mgmt, skb->len,
- rx_status);
- break;
- default:
- printk(KERN_DEBUG "%s: received unknown management frame - "
- "stype=%d\n", dev->name,
- (fc & IEEE80211_FCTL_STYPE) >> 4);
+ ieee80211_rx_mgmt_disassoc(dev, ifsta, mgmt, skb->len);
break;
}
-
- fail:
- dev_kfree_skb(skb);
}
@@ -1844,7 +1871,7 @@ static void ieee80211_sta_expire(struct
static void ieee80211_sta_merge_ibss(struct net_device *dev,
struct ieee80211_if_sta *ifsta)
{
- schedule_delayed_work(&ifsta->work, IEEE80211_IBSS_MERGE_INTERVAL);
+ mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL);
ieee80211_sta_expire(dev);
if (ieee80211_sta_active_ibss(dev))
@@ -1856,16 +1883,32 @@ static void ieee80211_sta_merge_ibss(str
}
+void ieee80211_sta_timer(unsigned long data)
+{
+ struct ieee80211_if_sta *ifsta = (struct ieee80211_if_sta *) data;
+ set_bit(IEEE80211_STA_REQ_RUN, &ifsta->request);
+ schedule_work(&ifsta->work);
+}
+
+
void ieee80211_sta_work(struct work_struct *work)
{
struct ieee80211_sub_if_data *sdata =
- container_of(work, struct ieee80211_sub_if_data, u.sta.work.work);
+ container_of(work, struct ieee80211_sub_if_data, u.sta.work);
struct net_device *dev = sdata->dev;
+ struct ieee80211_local *local = dev->ieee80211_ptr;
struct ieee80211_if_sta *ifsta;
+ struct sk_buff *skb;
if (!netif_running(dev))
return;
+ /* TODO: scan_dev check should be removed once scan_completed wakes
+ * every STA interface */
+ if (local->sta_scanning &&
+ local->scan_dev == dev)
+ return;
+
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type != IEEE80211_IF_TYPE_STA &&
sdata->type != IEEE80211_IF_TYPE_IBSS) {
@@ -1875,6 +1918,22 @@ void ieee80211_sta_work(struct work_stru
}
ifsta = &sdata->u.sta;
+ while ((skb = skb_dequeue(&ifsta->skb_queue)))
+ ieee80211_sta_rx_queued_mgmt(dev, skb);
+
+ if (ifsta->state != IEEE80211_AUTHENTICATE &&
+ ifsta->state != IEEE80211_ASSOCIATE &&
+ test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request)) {
+ ieee80211_sta_start_scan(dev, NULL, 0);
+ return;
+ }
+
+ if (test_and_clear_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request)) {
+ ifsta->state = IEEE80211_AUTHENTICATE;
+ ieee80211_sta_reset_auth(dev, ifsta);
+ } else if (!test_and_clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request))
+ return;
+
switch (ifsta->state) {
case IEEE80211_DISABLED:
break;
@@ -1909,14 +1968,10 @@ void ieee80211_sta_work(struct work_stru
}
-static void ieee80211_sta_new_auth(struct net_device *dev,
- struct ieee80211_if_sta *ifsta)
+static void ieee80211_sta_reset_auth(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta)
{
struct ieee80211_local *local = dev->ieee80211_ptr;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- if (sdata->type != IEEE80211_IF_TYPE_STA)
- return;
if (local->ops->reset_tsf) {
/* Reset own TSF to allow time synchronization work. */
@@ -1938,7 +1993,19 @@ static void ieee80211_sta_new_auth(struc
ifsta->auth_alg);
ifsta->auth_transaction = -1;
ifsta->associated = ifsta->auth_tries = ifsta->assoc_tries = 0;
- ieee80211_authenticate(dev, ifsta);
+}
+
+
+static void ieee80211_sta_new_auth(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->type != IEEE80211_IF_TYPE_STA)
+ return;
+
+ set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request);
+ schedule_work(&ifsta->work);
}
@@ -2123,7 +2190,7 @@ static int ieee80211_sta_join_ibss(struc
}
ifsta->state = IEEE80211_IBSS_JOINED;
- schedule_delayed_work(&ifsta->work, IEEE80211_IBSS_MERGE_INTERVAL);
+ mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL);
ieee80211_rx_bss_put(dev, bss);
@@ -2240,7 +2307,7 @@ static int ieee80211_sta_find_ibss(struc
/* Selected IBSS not found in current scan results - try to scan */
if (ifsta->state == IEEE80211_IBSS_JOINED &&
!ieee80211_sta_active_ibss(dev)) {
- schedule_delayed_work(&ifsta->work,
+ mod_timer(&ifsta->timer, jiffies +
IEEE80211_IBSS_MERGE_INTERVAL);
} else if (time_after(jiffies, local->last_scan_completed +
IEEE80211_SCAN_INTERVAL)) {
@@ -2269,7 +2336,7 @@ static int ieee80211_sta_find_ibss(struc
}
ifsta->state = IEEE80211_IBSS_SEARCH;
- schedule_delayed_work(&ifsta->work, interval);
+ mod_timer(&ifsta->timer, jiffies + interval);
return 0;
}
@@ -2432,8 +2499,9 @@ void ieee80211_scan_completed(struct iee
union iwreq_data wrqu;
printk(KERN_DEBUG "%s: scan completed\n", dev->name);
- local->sta_scanning = 0;
local->last_scan_completed = jiffies;
+ wmb();
+ local->sta_scanning = 0;
memset(&wrqu, 0, sizeof(wrqu));
wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
@@ -2444,7 +2512,9 @@ void ieee80211_scan_completed(struct iee
(!ifsta->state == IEEE80211_IBSS_JOINED &&
!ieee80211_sta_active_ibss(dev)))
ieee80211_sta_find_ibss(dev, ifsta);
- }
+ /* TODO: need to wake every sta interface */
+ } else if (sdata->type == IEEE80211_IF_TYPE_STA)
+ ieee80211_sta_timer((unsigned long)&sdata->u.sta);
}
EXPORT_SYMBOL(ieee80211_scan_completed);
@@ -2533,16 +2603,13 @@ void ieee80211_sta_scan_work(struct work
break;
}
- if (local->sta_scanning) {
- if (next_delay)
- schedule_delayed_work(&local->scan_work, next_delay);
- else
- schedule_work(&local->scan_work.work);
- }
+ if (local->sta_scanning)
+ schedule_delayed_work(&local->scan_work, next_delay);
}
-int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len)
+static int ieee80211_sta_start_scan(struct net_device *dev,
+ u8 *ssid, size_t ssid_len)
{
struct ieee80211_local *local = dev->ieee80211_ptr;
@@ -2603,12 +2670,35 @@ int ieee80211_sta_req_scan(struct net_de
list);
local->scan_channel_idx = 0;
local->scan_dev = dev;
- schedule_work(&local->scan_work.work);
+ schedule_delayed_work(&local->scan_work, 0);
return 0;
}
+int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct ieee80211_local *local = dev->ieee80211_ptr;
+
+ if (sdata->type != IEEE80211_IF_TYPE_STA)
+ return ieee80211_sta_start_scan(dev, ssid, ssid_len);
+
+ if (local->sta_scanning) {
+ if (local->scan_dev == dev)
+ return 0;
+ return -EBUSY;
+ }
+
+ if (time_after(local->last_scan_completed + HZ, jiffies))
+ return -EAGAIN;
+
+ set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request);
+ schedule_work(&ifsta->work);
+ return 0;
+}
+
static char *
ieee80211_sta_scan_result(struct net_device *dev,
struct ieee80211_sta_bss *bss,
[-- Attachment #1.3: kill-hosttime --]
[-- Type: text/x-diff, Size: 3200 bytes --]
d80211: remove hosttime from ieee80211_rx_status
From: Michael Wu <flamingice@sourmilk.net>
Nobody fills hosttime in ieee80211_rx_status. Removing it allows
ieee80211_rx_status to fit in skb->cb.
Signed-off-by: Michael Wu <flamingice@sourmilk.net>
---
include/net/d80211.h | 1 -
net/d80211/ieee80211.c | 25 +++++--------------------
2 files changed, 5 insertions(+), 21 deletions(-)
diff --git a/include/net/d80211.h b/include/net/d80211.h
index 326def5..0b7b963 100644
--- a/include/net/d80211.h
+++ b/include/net/d80211.h
@@ -225,7 +225,6 @@ struct ieee80211_tx_control {
* (the subset supported by hardware) to the 802.11 code with each received
* frame. */
struct ieee80211_rx_status {
- u64 hosttime;
u64 mactime;
int freq; /* receive frequency in Mhz */
int channel;
diff --git a/net/d80211/ieee80211.c b/net/d80211/ieee80211.c
index bbcefa9..2fb66b3 100644
--- a/net/d80211/ieee80211.c
+++ b/net/d80211/ieee80211.c
@@ -2627,7 +2627,7 @@ ieee80211_fill_frame_info(struct ieee802
struct timespec ts;
struct ieee80211_rate *rate;
- jiffies_to_timespec(status->hosttime, &ts);
+ jiffies_to_timespec(jiffies, &ts);
fi->hosttime = cpu_to_be64((u64) ts.tv_sec * 1000000 +
ts.tv_nsec / 1000);
fi->mactime = cpu_to_be64(status->mactime);
@@ -4019,25 +4019,11 @@ static void ieee80211_stat_refresh(unsig
void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ieee80211_rx_status *status)
{
- struct ieee80211_rx_status *saved;
struct ieee80211_local *local = hw_to_local(hw);
skb->dev = local->mdev;
- saved = kmalloc(sizeof(struct ieee80211_rx_status), GFP_ATOMIC);
- if (unlikely(!saved)) {
- if (net_ratelimit())
- printk(KERN_WARNING "%s: Not enough memory, "
- "dropping packet", skb->dev->name);
- /* should be dev_kfree_skb_irq, but due to this function being
- * named _irqsafe instead of just _irq we can't be sure that
- * people won't call it from non-irq contexts */
- dev_kfree_skb_any(skb);
- return;
- }
- memcpy(saved, status, sizeof(struct ieee80211_rx_status));
- /* copy pointer to saved status into skb->cb for use by tasklet */
- memcpy(skb->cb, &saved, sizeof(saved));
-
+ /* copy status into skb->cb for use by tasklet */
+ memcpy(skb->cb, status, sizeof(*status));
skb->pkt_type = ieee80211_rx_msg;
skb_queue_tail(&local->skb_queue, skb);
tasklet_schedule(&local->tasklet);
@@ -4096,13 +4082,12 @@ static void ieee80211_tasklet_handler(un
(skb = skb_dequeue(&local->skb_queue_unreliable))) {
switch (skb->pkt_type) {
case ieee80211_rx_msg:
- /* get pointer to saved status out of skb->cb */
- memcpy(&rx_status, skb->cb, sizeof(rx_status));
+ /* status is in skb->cb */
+ rx_status = (struct ieee80211_rx_status *) skb->cb;
/* Clear skb->type in order to not confuse kernel
* netstack. */
skb->pkt_type = 0;
__ieee80211_rx(local_to_hw(local), skb, rx_status);
- kfree(rx_status);
break;
case ieee80211_tx_status_msg:
/* get pointer to saved status out of skb->cb */
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] d80211: Fix concurrency issues in ieee80211_sta.c
2007-02-09 5:28 ` Michael Wu
@ 2007-02-09 7:37 ` Michael Wu
2007-02-15 21:28 ` Jiri Benc
0 siblings, 1 reply; 4+ messages in thread
From: Michael Wu @ 2007-02-09 7:37 UTC (permalink / raw)
To: Jiri Benc; +Cc: linux-wireless
[-- Attachment #1.1: Type: text/plain, Size: 575 bytes --]
On Friday 09 February 2007 00:28, Michael Wu wrote:
> Opps.. that was suppose to be part of the previous patch. Fixed patches
> attached which just merge that chunk into the previous patch.
>
> -Michael Wu
Release early and release often seems to be my motto. New, hopefully now bug
free version of the patch attached. Previous version leaked and refused to
scan for 5 minutes after boot. The time_after check was removed to fix the
scan issue and the appropriate kfree_skb has been added to stop the leak.
Otherwise, it's mostly the same thing.
-Michael Wu
[-- Attachment #1.2: workqueue-sync --]
[-- Type: text/x-diff, Size: 16711 bytes --]
d80211: Fix concurrency issues in ieee80211_sta.c
From: Michael Wu <flamingice@sourmilk.net>
This fixes most concurrency issues in ieee80211_sta.c and partially
prevents scans from running over an association in progress and vice versa.
This is achieved by forcing all potentially racy code to run from a
workqueue.
Signed-off-by: Michael Wu <flamingice@sourmilk.net>
---
net/d80211/ieee80211.c | 4 +
net/d80211/ieee80211_i.h | 9 ++
net/d80211/ieee80211_iface.c | 5 +
net/d80211/ieee80211_sta.c | 199 ++++++++++++++++++++++++++++++------------
4 files changed, 159 insertions(+), 58 deletions(-)
diff --git a/net/d80211/ieee80211.c b/net/d80211/ieee80211.c
index 2fb66b3..055e8a2 100644
--- a/net/d80211/ieee80211.c
+++ b/net/d80211/ieee80211.c
@@ -2179,7 +2179,9 @@ void ieee80211_if_shutdown(struct net_de
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS:
sdata->u.sta.state = IEEE80211_DISABLED;
- cancel_delayed_work(&sdata->u.sta.work);
+ del_timer_sync(&sdata->u.sta.timer);
+ flush_scheduled_work();
+ skb_queue_purge(&sdata->u.sta.skb_queue);
if (!local->ops->hw_scan &&
local->scan_dev == sdata->dev) {
local->sta_scanning = 0;
diff --git a/net/d80211/ieee80211_i.h b/net/d80211/ieee80211_i.h
index bb8fc83..9307882 100644
--- a/net/d80211/ieee80211_i.h
+++ b/net/d80211/ieee80211_i.h
@@ -242,7 +242,8 @@ struct ieee80211_if_sta {
IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED,
IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED
} state;
- struct delayed_work work;
+ struct timer_list timer;
+ struct work_struct work;
u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
u8 ssid[IEEE80211_MAX_SSID_LEN];
size_t ssid_len;
@@ -267,6 +268,11 @@ struct ieee80211_if_sta {
unsigned int create_ibss:1;
unsigned int mixed_cell:1;
unsigned int wmm_enabled:1;
+#define IEEE80211_STA_REQ_SCAN 0
+#define IEEE80211_STA_REQ_AUTH 1
+#define IEEE80211_STA_REQ_RUN 2
+ unsigned long request;
+ struct sk_buff_head skb_queue;
int key_mgmt;
unsigned long last_probe;
@@ -660,6 +666,7 @@ int ieee80211_set_compression(struct iee
struct net_device *dev, struct sta_info *sta);
int ieee80211_init_client(struct net_device *dev);
/* ieee80211_sta.c */
+void ieee80211_sta_timer(unsigned long data);
void ieee80211_sta_work(struct work_struct *work);
void ieee80211_sta_scan_work(struct work_struct *work);
void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb,
diff --git a/net/d80211/ieee80211_iface.c b/net/d80211/ieee80211_iface.c
index 342ad2a..939e289 100644
--- a/net/d80211/ieee80211_iface.c
+++ b/net/d80211/ieee80211_iface.c
@@ -185,7 +185,10 @@ void ieee80211_if_set_type(struct net_de
struct ieee80211_if_sta *ifsta;
ifsta = &sdata->u.sta;
- INIT_DELAYED_WORK(&ifsta->work, ieee80211_sta_work);
+ INIT_WORK(&ifsta->work, ieee80211_sta_work);
+ setup_timer(&ifsta->timer, ieee80211_sta_timer,
+ (unsigned long) ifsta);
+ skb_queue_head_init(&ifsta->skb_queue);
ifsta->capab = WLAN_CAPABILITY_ESS;
ifsta->auth_algs = IEEE80211_AUTH_ALG_OPEN |
diff --git a/net/d80211/ieee80211_sta.c b/net/d80211/ieee80211_sta.c
index 0e45a65..57e7fa7 100644
--- a/net/d80211/ieee80211_sta.c
+++ b/net/d80211/ieee80211_sta.c
@@ -64,6 +64,10 @@ static void ieee80211_rx_bss_put(struct
static int ieee80211_sta_find_ibss(struct net_device *dev,
struct ieee80211_if_sta *ifsta);
static int ieee80211_sta_wep_configured(struct net_device *dev);
+static int ieee80211_sta_start_scan(struct net_device *dev,
+ u8 *ssid, size_t ssid_len);
+static void ieee80211_sta_reset_auth(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta);
/* Parsed Information Elements */
@@ -466,7 +470,7 @@ static void ieee80211_authenticate(struc
ieee80211_send_auth(dev, ifsta, 1, NULL, 0, 0);
- schedule_delayed_work(&ifsta->work, IEEE80211_AUTH_TIMEOUT);
+ mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
}
@@ -694,7 +698,7 @@ static void ieee80211_associate(struct n
ieee80211_send_assoc(dev, ifsta);
- schedule_delayed_work(&ifsta->work, IEEE80211_ASSOC_TIMEOUT);
+ mod_timer(&ifsta->timer, jiffies + IEEE80211_ASSOC_TIMEOUT);
}
@@ -752,10 +756,10 @@ static void ieee80211_associated(struct
memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
- schedule_delayed_work(&ifsta->work,
+ mod_timer(&ifsta->timer, jiffies +
IEEE80211_MONITORING_INTERVAL + 30 * HZ);
} else {
- schedule_delayed_work(&ifsta->work,
+ mod_timer(&ifsta->timer, jiffies +
IEEE80211_MONITORING_INTERVAL);
}
}
@@ -846,8 +850,7 @@ static void ieee80211_auth_completed(str
static void ieee80211_auth_challenge(struct net_device *dev,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
+ size_t len)
{
u8 *pos;
struct ieee802_11_elems elems;
@@ -873,8 +876,7 @@ static void ieee80211_auth_challenge(str
static void ieee80211_rx_mgmt_auth(struct net_device *dev,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
+ size_t len)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
u16 auth_alg, auth_transaction, status_code;
@@ -993,8 +995,7 @@ static void ieee80211_rx_mgmt_auth(struc
if (ifsta->auth_transaction == 4)
ieee80211_auth_completed(dev, ifsta);
else
- ieee80211_auth_challenge(dev, ifsta, mgmt, len,
- rx_status);
+ ieee80211_auth_challenge(dev, ifsta, mgmt, len);
break;
}
}
@@ -1003,8 +1004,7 @@ static void ieee80211_rx_mgmt_auth(struc
static void ieee80211_rx_mgmt_deauth(struct net_device *dev,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
+ size_t len)
{
u16 reason_code;
@@ -1037,7 +1037,7 @@ static void ieee80211_rx_mgmt_deauth(str
ifsta->state == IEEE80211_ASSOCIATE ||
ifsta->state == IEEE80211_ASSOCIATED) {
ifsta->state = IEEE80211_AUTHENTICATE;
- schedule_delayed_work(&ifsta->work,
+ mod_timer(&ifsta->timer, jiffies +
IEEE80211_RETRY_AUTH_INTERVAL);
}
@@ -1049,8 +1049,7 @@ static void ieee80211_rx_mgmt_deauth(str
static void ieee80211_rx_mgmt_disassoc(struct net_device *dev,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
+ size_t len)
{
u16 reason_code;
@@ -1080,7 +1079,7 @@ static void ieee80211_rx_mgmt_disassoc(s
if (ifsta->state == IEEE80211_ASSOCIATED) {
ifsta->state = IEEE80211_ASSOCIATE;
- schedule_delayed_work(&ifsta->work,
+ mod_timer(&ifsta->timer, jiffies +
IEEE80211_RETRY_AUTH_INTERVAL);
}
@@ -1092,7 +1091,6 @@ static void ieee80211_rx_mgmt_assoc_resp
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
size_t len,
- struct ieee80211_rx_status *rx_status,
int reassoc)
{
struct ieee80211_local *local = dev->ieee80211_ptr;
@@ -1735,6 +1733,47 @@ void ieee80211_sta_rx_mgmt(struct net_de
switch (fc & IEEE80211_FCTL_STYPE) {
case IEEE80211_STYPE_PROBE_REQ:
+ case IEEE80211_STYPE_PROBE_RESP:
+ case IEEE80211_STYPE_BEACON:
+ memcpy(skb->cb, rx_status, sizeof(*rx_status));
+ case IEEE80211_STYPE_AUTH:
+ case IEEE80211_STYPE_ASSOC_RESP:
+ case IEEE80211_STYPE_REASSOC_RESP:
+ case IEEE80211_STYPE_DEAUTH:
+ case IEEE80211_STYPE_DISASSOC:
+ skb_queue_tail(&ifsta->skb_queue, skb);
+ schedule_work(&ifsta->work);
+ return;
+ default:
+ printk(KERN_DEBUG "%s: received unknown management frame - "
+ "stype=%d\n", dev->name,
+ (fc & IEEE80211_FCTL_STYPE) >> 4);
+ break;
+ }
+
+ fail:
+ kfree_skb(skb);
+}
+
+
+static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev,
+ struct sk_buff *skb)
+{
+ struct ieee80211_rx_status *rx_status;
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_if_sta *ifsta;
+ struct ieee80211_mgmt *mgmt;
+ u16 fc;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ ifsta = &sdata->u.sta;
+
+ rx_status = (struct ieee80211_rx_status *) skb->cb;
+ mgmt = (struct ieee80211_mgmt *) skb->data;
+ fc = le16_to_cpu(mgmt->frame_control);
+
+ switch (fc & IEEE80211_FCTL_STYPE) {
+ case IEEE80211_STYPE_PROBE_REQ:
ieee80211_rx_mgmt_probe_req(dev, ifsta, mgmt, skb->len,
rx_status);
break;
@@ -1745,33 +1784,23 @@ void ieee80211_sta_rx_mgmt(struct net_de
ieee80211_rx_mgmt_beacon(dev, mgmt, skb->len, rx_status);
break;
case IEEE80211_STYPE_AUTH:
- ieee80211_rx_mgmt_auth(dev, ifsta, mgmt, skb->len, rx_status);
+ ieee80211_rx_mgmt_auth(dev, ifsta, mgmt, skb->len);
break;
case IEEE80211_STYPE_ASSOC_RESP:
- ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len,
- rx_status, 0);
+ ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len, 0);
break;
case IEEE80211_STYPE_REASSOC_RESP:
- ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len,
- rx_status, 1);
+ ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len, 1);
break;
case IEEE80211_STYPE_DEAUTH:
- ieee80211_rx_mgmt_deauth(dev, ifsta, mgmt, skb->len,
- rx_status);
+ ieee80211_rx_mgmt_deauth(dev, ifsta, mgmt, skb->len);
break;
case IEEE80211_STYPE_DISASSOC:
- ieee80211_rx_mgmt_disassoc(dev, ifsta, mgmt, skb->len,
- rx_status);
- break;
- default:
- printk(KERN_DEBUG "%s: received unknown management frame - "
- "stype=%d\n", dev->name,
- (fc & IEEE80211_FCTL_STYPE) >> 4);
+ ieee80211_rx_mgmt_disassoc(dev, ifsta, mgmt, skb->len);
break;
}
- fail:
- dev_kfree_skb(skb);
+ kfree_skb(skb);
}
@@ -1844,7 +1873,7 @@ static void ieee80211_sta_expire(struct
static void ieee80211_sta_merge_ibss(struct net_device *dev,
struct ieee80211_if_sta *ifsta)
{
- schedule_delayed_work(&ifsta->work, IEEE80211_IBSS_MERGE_INTERVAL);
+ mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL);
ieee80211_sta_expire(dev);
if (ieee80211_sta_active_ibss(dev))
@@ -1856,16 +1885,32 @@ static void ieee80211_sta_merge_ibss(str
}
+void ieee80211_sta_timer(unsigned long data)
+{
+ struct ieee80211_if_sta *ifsta = (struct ieee80211_if_sta *) data;
+ set_bit(IEEE80211_STA_REQ_RUN, &ifsta->request);
+ schedule_work(&ifsta->work);
+}
+
+
void ieee80211_sta_work(struct work_struct *work)
{
struct ieee80211_sub_if_data *sdata =
- container_of(work, struct ieee80211_sub_if_data, u.sta.work.work);
+ container_of(work, struct ieee80211_sub_if_data, u.sta.work);
struct net_device *dev = sdata->dev;
+ struct ieee80211_local *local = dev->ieee80211_ptr;
struct ieee80211_if_sta *ifsta;
+ struct sk_buff *skb;
if (!netif_running(dev))
return;
+ /* TODO: scan_dev check should be removed once scan_completed wakes
+ * every STA interface */
+ if (local->sta_scanning &&
+ local->scan_dev == dev)
+ return;
+
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type != IEEE80211_IF_TYPE_STA &&
sdata->type != IEEE80211_IF_TYPE_IBSS) {
@@ -1875,6 +1920,22 @@ void ieee80211_sta_work(struct work_stru
}
ifsta = &sdata->u.sta;
+ while ((skb = skb_dequeue(&ifsta->skb_queue)))
+ ieee80211_sta_rx_queued_mgmt(dev, skb);
+
+ if (ifsta->state != IEEE80211_AUTHENTICATE &&
+ ifsta->state != IEEE80211_ASSOCIATE &&
+ test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request)) {
+ ieee80211_sta_start_scan(dev, NULL, 0);
+ return;
+ }
+
+ if (test_and_clear_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request)) {
+ ifsta->state = IEEE80211_AUTHENTICATE;
+ ieee80211_sta_reset_auth(dev, ifsta);
+ } else if (!test_and_clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request))
+ return;
+
switch (ifsta->state) {
case IEEE80211_DISABLED:
break;
@@ -1909,14 +1970,10 @@ void ieee80211_sta_work(struct work_stru
}
-static void ieee80211_sta_new_auth(struct net_device *dev,
- struct ieee80211_if_sta *ifsta)
+static void ieee80211_sta_reset_auth(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta)
{
struct ieee80211_local *local = dev->ieee80211_ptr;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- if (sdata->type != IEEE80211_IF_TYPE_STA)
- return;
if (local->ops->reset_tsf) {
/* Reset own TSF to allow time synchronization work. */
@@ -1938,7 +1995,19 @@ static void ieee80211_sta_new_auth(struc
ifsta->auth_alg);
ifsta->auth_transaction = -1;
ifsta->associated = ifsta->auth_tries = ifsta->assoc_tries = 0;
- ieee80211_authenticate(dev, ifsta);
+}
+
+
+static void ieee80211_sta_new_auth(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->type != IEEE80211_IF_TYPE_STA)
+ return;
+
+ set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request);
+ schedule_work(&ifsta->work);
}
@@ -2123,7 +2192,7 @@ static int ieee80211_sta_join_ibss(struc
}
ifsta->state = IEEE80211_IBSS_JOINED;
- schedule_delayed_work(&ifsta->work, IEEE80211_IBSS_MERGE_INTERVAL);
+ mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL);
ieee80211_rx_bss_put(dev, bss);
@@ -2240,7 +2309,7 @@ static int ieee80211_sta_find_ibss(struc
/* Selected IBSS not found in current scan results - try to scan */
if (ifsta->state == IEEE80211_IBSS_JOINED &&
!ieee80211_sta_active_ibss(dev)) {
- schedule_delayed_work(&ifsta->work,
+ mod_timer(&ifsta->timer, jiffies +
IEEE80211_IBSS_MERGE_INTERVAL);
} else if (time_after(jiffies, local->last_scan_completed +
IEEE80211_SCAN_INTERVAL)) {
@@ -2269,7 +2338,7 @@ static int ieee80211_sta_find_ibss(struc
}
ifsta->state = IEEE80211_IBSS_SEARCH;
- schedule_delayed_work(&ifsta->work, interval);
+ mod_timer(&ifsta->timer, jiffies + interval);
return 0;
}
@@ -2432,8 +2501,9 @@ void ieee80211_scan_completed(struct iee
union iwreq_data wrqu;
printk(KERN_DEBUG "%s: scan completed\n", dev->name);
- local->sta_scanning = 0;
local->last_scan_completed = jiffies;
+ wmb();
+ local->sta_scanning = 0;
memset(&wrqu, 0, sizeof(wrqu));
wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
@@ -2444,7 +2514,9 @@ void ieee80211_scan_completed(struct iee
(!ifsta->state == IEEE80211_IBSS_JOINED &&
!ieee80211_sta_active_ibss(dev)))
ieee80211_sta_find_ibss(dev, ifsta);
- }
+ /* TODO: need to wake every sta interface */
+ } else if (sdata->type == IEEE80211_IF_TYPE_STA)
+ ieee80211_sta_timer((unsigned long)&sdata->u.sta);
}
EXPORT_SYMBOL(ieee80211_scan_completed);
@@ -2533,16 +2605,13 @@ void ieee80211_sta_scan_work(struct work
break;
}
- if (local->sta_scanning) {
- if (next_delay)
- schedule_delayed_work(&local->scan_work, next_delay);
- else
- schedule_work(&local->scan_work.work);
- }
+ if (local->sta_scanning)
+ schedule_delayed_work(&local->scan_work, next_delay);
}
-int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len)
+static int ieee80211_sta_start_scan(struct net_device *dev,
+ u8 *ssid, size_t ssid_len)
{
struct ieee80211_local *local = dev->ieee80211_ptr;
@@ -2603,12 +2672,32 @@ int ieee80211_sta_req_scan(struct net_de
list);
local->scan_channel_idx = 0;
local->scan_dev = dev;
- schedule_work(&local->scan_work.work);
+ schedule_delayed_work(&local->scan_work, 0);
return 0;
}
+int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct ieee80211_local *local = dev->ieee80211_ptr;
+
+ if (sdata->type != IEEE80211_IF_TYPE_STA)
+ return ieee80211_sta_start_scan(dev, ssid, ssid_len);
+
+ if (local->sta_scanning) {
+ if (local->scan_dev == dev)
+ return 0;
+ return -EBUSY;
+ }
+
+ set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request);
+ schedule_work(&ifsta->work);
+ return 0;
+}
+
static char *
ieee80211_sta_scan_result(struct net_device *dev,
struct ieee80211_sta_bss *bss,
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] d80211: Fix concurrency issues in ieee80211_sta.c
2007-02-09 7:37 ` Michael Wu
@ 2007-02-15 21:28 ` Jiri Benc
0 siblings, 0 replies; 4+ messages in thread
From: Jiri Benc @ 2007-02-15 21:28 UTC (permalink / raw)
To: Michael Wu; +Cc: linux-wireless
On Fri, 9 Feb 2007 02:37:43 -0500, Michael Wu wrote:
> This fixes most concurrency issues in ieee80211_sta.c and partially
> prevents scans from running over an association in progress and vice versa.
> This is achieved by forcing all potentially racy code to run from a
> workqueue.
Applied to my tree, thanks for the patch!
Jiri
--
Jiri Benc
SUSE Labs
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2007-02-15 21:28 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-02-09 5:02 [PATCH] d80211: Fix concurrency issues in ieee80211_sta.c Michael Wu
2007-02-09 5:28 ` Michael Wu
2007-02-09 7:37 ` Michael Wu
2007-02-15 21:28 ` Jiri Benc
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.