Re: ipw2100: race between isr_indicate_associated and rx path
Helmut Schaa <[email protected]> Wed, 28 Jan 2009 11:33:23 +0100
| Newsgroups | gmane.linux.drivers.ipw2100.devel |
|---|---|
| Message-ID | <[email protected]> |
Am Mittwoch, 28. Januar 2009 schrieb Zhu, Yi:
> Do you mean the EAP response frame is handed to ieee80211/ipw2100 by
> wpa_supplicant but you didn't see it in the air?
Exactly.
> I assume at this point, the BSSID is already got and the state is
> ASSOCIATED.
Yes.
> Can you confirm wpa_supplicant has already received the wext ASSOCIATED
> event?
Yes.
> Anyway, I couldn't tell without seeing your patch ;-)
Uhh, yes. Here it is :)
The patch is far from being ready, it's just a prototype. So, just ignore
typos, coding style etc.
Helmut
---
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c
index 823c2bf..36b5d52 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/ipw2x00/ipw2100.c
@@ -2393,6 +2393,8 @@ static void isr_rx(struct ipw2100_priv *priv, int i,
{
struct ipw2100_status *status = &priv->status_queue.drv[i];
struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
+ struct ipw2100_skb_buffer *skb_buff;
+
IPW_DEBUG_RX("Handler...\n");
@@ -2413,7 +2415,7 @@ static void isr_rx(struct ipw2100_priv *priv, int i,
}
if (unlikely(priv->ieee->iw_mode != IW_MODE_MONITOR &&
- !(priv->status & STATUS_ASSOCIATED))) {
+ !(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))) {
IPW_DEBUG_DROP("Dropping packet while not associated.\n");
priv->wstats.discard.misc++;
return;
@@ -2433,7 +2435,24 @@ static void isr_rx(struct ipw2100_priv *priv, int i,
IPW_RX_NIC_BUFFER_LENGTH));
#endif
- if (!ieee80211_rx(priv->ieee, packet->skb, stats)) {
+ if (priv->status & STATUS_ASSOCIATING) {
+ /* firmware is already associated and might accept frames
+ * but we the user space does not know about that state
+ * yet. Hence buffer all frames until the status moves to
+ * associated.
+ */
+ skb_buff = (struct ipw2100_skb_buffer*)kmalloc(sizeof(struct ipw2100_skb_buffer), GFP_ATOMIC);
+
+ atomic_inc(&packet->skb->users);
+ INIT_LIST_HEAD(&skb_buff->list);
+ skb_buff->skb = packet->skb;
+ memcpy(&skb_buff->stats, stats, sizeof(struct ieee80211_rx_stats));
+ list_add_tail(&skb_buff->list, &priv->skb_buffer_list);
+
+ IPW_DEBUG_INFO("%s: FW is already associated, buffering packet.\n",
+ priv->net_dev->name);
+
+ } else if (!ieee80211_rx(priv->ieee, packet->skb, stats)) {
#ifdef IPW2100_RX_DEBUG
IPW_DEBUG_DROP("%s: Non consumed packet:\n",
priv->net_dev->name);
@@ -6118,6 +6137,8 @@ static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev,
INIT_LIST_HEAD(&priv->fw_pend_list);
INIT_STAT(&priv->fw_pend_stat);
+ INIT_LIST_HEAD(&priv->skb_buffer_list);
+
priv->workqueue = create_workqueue(DRV_NAME);
INIT_DELAYED_WORK(&priv->reset_work, ipw2100_reset_adapter);
@@ -8298,6 +8319,8 @@ static void ipw2100_wx_event_work(struct work_struct *work)
container_of(work, struct ipw2100_priv, wx_event_work.work);
union iwreq_data wrqu;
int len = ETH_ALEN;
+ bool test = false;
+ struct ipw2100_skb_buffer* skb_buffer;
if (priv->status & STATUS_STOPPING)
return;
@@ -8323,8 +8346,10 @@ static void ipw2100_wx_event_work(struct work_struct *work)
memcpy(priv->ieee->bssid, priv->bssid, ETH_ALEN);
priv->status &= ~STATUS_ASSOCIATING;
priv->status |= STATUS_ASSOCIATED;
+
netif_carrier_on(priv->net_dev);
netif_wake_queue(priv->net_dev);
+ test = true;
}
if (!(priv->status & STATUS_ASSOCIATED)) {
@@ -8341,6 +8366,31 @@ static void ipw2100_wx_event_work(struct work_struct *work)
}
wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
+
+ if (test) {
+ /* now deliver all buffered frames */
+ while ( !list_empty(&priv->skb_buffer_list) ) {
+ IPW_DEBUG_INFO("%s: Delivering queued packet.\n",
+ priv->net_dev->name);
+
+ skb_buffer = list_entry(priv->skb_buffer_list.next, struct ipw2100_skb_buffer, list);
+ list_del(&skb_buffer->list);
+
+ if (!ieee80211_rx(priv->ieee, skb_buffer->skb, &skb_buffer->stats)) {
+/*#ifdef IPW2100_RX_DEBUG
+ IPW_DEBUG_DROP("%s: Non consumed packet:\n",
+ priv->net_dev->name);
+ printk_buf(IPW_DL_DROP, packet_data, status->frame_size);
+#endif*/
+ priv->ieee->stats.rx_errors++;
+
+ /* ieee80211_rx failed, so it didn't free the SKB */
+ dev_kfree_skb_any(skb_buffer->skb);
+ skb_buffer->skb = NULL;
+ }
+ kfree(skb_buffer);
+ }
+ }
}
#define IPW2100_FW_MAJOR_VERSION 1
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.h b/drivers/net/wireless/ipw2x00/ipw2100.h
index bbf1ddc..7c11c6a 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.h
+++ b/drivers/net/wireless/ipw2x00/ipw2100.h
@@ -487,6 +487,12 @@ enum {
#define CAP_SHARED_KEY (1<<0) /* Off = OPEN */
#define CAP_PRIVACY_ON (1<<1) /* Off = No privacy */
+struct ipw2100_skb_buffer {
+ struct list_head list;
+ struct sk_buff* skb;
+ struct ieee80211_rx_stats stats;
+};
+
struct ipw2100_priv {
int stop_hang_check; /* Set 1 when shutting down to kill hang_check */
@@ -601,6 +607,8 @@ struct ipw2100_priv {
struct mutex adapter_mutex;
wait_queue_head_t wait_command_queue;
+
+ struct list_head skb_buffer_list;
};
/*********************************************************
------------------------------------------------------------------------------
This SF.net email is sponsored by:
SourcForge Community
SourceForge wants to tell your story.
http://p.sf.net/sfu/sf-spreadtheword