All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] mac80211: refactor sta_info_insert_rcu to 3 main stages
@ 2011-07-26 21:29 Guy Eilam
  2011-07-26 21:29 ` [PATCH 2/2] mac80211: fix race condition between assoc_done and first EAP packet Guy Eilam
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Guy Eilam @ 2011-07-26 21:29 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless

Divided the sta_info_insert_rcu function to 3 mini-functions:
sta_info_insert_check - the initial checks done when inserting
a new station
sta_info_insert_adhoc - the function that handles the station
addition in ad-hoc mode
sta_info_insert_mgd - the function that handles the station
addition in the other modes

The outer API was not changed.
The refactoring was done for better usage of the different
stages in the station addition in new scenarios added
in the next commit

Signed-off-by: Guy Eilam <guy@wizery.com>
---
 net/mac80211/sta_info.c |  144 +++++++++++++++++++++++++++++-----------------
 1 files changed, 91 insertions(+), 53 deletions(-)

diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index b83870b..b4549f2 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -371,93 +371,91 @@ static void sta_info_finish_work(struct work_struct *work)
 	mutex_unlock(&local->sta_mtx);
 }
 
-int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
+static int sta_info_insert_check(struct sta_info *sta)
 {
-	struct ieee80211_local *local = sta->local;
 	struct ieee80211_sub_if_data *sdata = sta->sdata;
-	unsigned long flags;
-	int err = 0;
 
 	/*
 	 * Can't be a WARN_ON because it can be triggered through a race:
 	 * something inserts a STA (on one CPU) without holding the RTNL
 	 * and another CPU turns off the net device.
 	 */
-	if (unlikely(!ieee80211_sdata_running(sdata))) {
-		err = -ENETDOWN;
-		rcu_read_lock();
-		goto out_free;
-	}
+	if (unlikely(!ieee80211_sdata_running(sdata)))
+		return -ENETDOWN;
 
 	if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->vif.addr) == 0 ||
-		    is_multicast_ether_addr(sta->sta.addr))) {
-		err = -EINVAL;
+		    is_multicast_ether_addr(sta->sta.addr)))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int sta_info_insert_adhoc(struct sta_info *sta) __acquires(RCU)
+{
+	struct ieee80211_local *local = sta->local;
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
+	unsigned long flags;
+
+	spin_lock_irqsave(&local->sta_lock, flags);
+	/* check if STA exists already */
+	if (sta_info_get_bss(sdata, sta->sta.addr)) {
+		spin_unlock_irqrestore(&local->sta_lock, flags);
 		rcu_read_lock();
-		goto out_free;
+		__sta_info_free(local, sta);
+		return -EEXIST;
 	}
 
-	/*
-	 * In ad-hoc mode, we sometimes need to insert stations
-	 * from tasklet context from the RX path. To avoid races,
-	 * always do so in that case -- see the comment below.
-	 */
-	if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
-		spin_lock_irqsave(&local->sta_lock, flags);
-		/* check if STA exists already */
-		if (sta_info_get_bss(sdata, sta->sta.addr)) {
-			spin_unlock_irqrestore(&local->sta_lock, flags);
-			rcu_read_lock();
-			err = -EEXIST;
-			goto out_free;
-		}
-
-		local->num_sta++;
-		local->sta_generation++;
-		smp_mb();
-		sta_info_hash_add(local, sta);
+	local->num_sta++;
+	local->sta_generation++;
+	smp_mb();
+	sta_info_hash_add(local, sta);
 
-		list_add_tail(&sta->list, &local->sta_pending_list);
+	list_add_tail(&sta->list, &local->sta_pending_list);
 
-		rcu_read_lock();
-		spin_unlock_irqrestore(&local->sta_lock, flags);
+	rcu_read_lock();
+	spin_unlock_irqrestore(&local->sta_lock, flags);
 
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-		wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n",
-			    sta->sta.addr);
+	wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n",
+			sta->sta.addr);
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 
-		ieee80211_queue_work(&local->hw, &local->sta_finish_work);
+	ieee80211_queue_work(&local->hw, &local->sta_finish_work);
 
-		return 0;
-	}
+	return 0;
+}
+
+/*
+ * should be called with sta_mtx locked
+ * this function replaces the mutex lock
+ * with a RCU lock
+ */
+static int sta_info_insert_mgd(struct sta_info *sta) __acquires(RCU)
+{
+	struct ieee80211_local *local = sta->local;
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
+	unsigned long flags;
+	int err = 0;
+
+	lockdep_assert_held(&local->sta_mtx);
 
 	/*
 	 * On first glance, this will look racy, because the code
-	 * below this point, which inserts a station with sleeping,
+	 * in this function, which inserts a station with sleeping,
 	 * unlocks the sta_lock between checking existence in the
 	 * hash table and inserting into it.
 	 *
 	 * However, it is not racy against itself because it keeps
-	 * the mutex locked. It still seems to race against the
-	 * above code that atomically inserts the station... That,
-	 * however, is not true because the above code can only
-	 * be invoked for IBSS interfaces, and the below code will
-	 * not be -- and the two do not race against each other as
-	 * the hash table also keys off the interface.
+	 * the mutex locked.
 	 */
 
-	might_sleep();
-
-	mutex_lock(&local->sta_mtx);
-
 	spin_lock_irqsave(&local->sta_lock, flags);
 	/* check if STA exists already */
 	if (sta_info_get_bss(sdata, sta->sta.addr)) {
 		spin_unlock_irqrestore(&local->sta_lock, flags);
 		mutex_unlock(&local->sta_mtx);
 		rcu_read_lock();
-		err = -EEXIST;
-		goto out_free;
+		return -EEXIST;
 	}
 
 	spin_unlock_irqrestore(&local->sta_lock, flags);
@@ -466,7 +464,7 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
 	if (err) {
 		mutex_unlock(&local->sta_mtx);
 		rcu_read_lock();
-		goto out_free;
+		return err;
 	}
 
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
@@ -481,6 +479,46 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
 		mesh_accept_plinks_update(sdata);
 
 	return 0;
+}
+
+int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
+{
+	struct ieee80211_local *local = sta->local;
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
+	int err = 0;
+
+	err = sta_info_insert_check(sta);
+	if (err) {
+		rcu_read_lock();
+		goto out_free;
+	}
+
+	/*
+	 * In ad-hoc mode, we sometimes need to insert stations
+	 * from tasklet context from the RX path. To avoid races,
+	 * always do so in that case -- see the comment below.
+	 */
+	if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+		return sta_info_insert_adhoc(sta);
+
+	/*
+	 * It might seem that the function called below is in race against
+	 * the function call above that atomically inserts the station... That,
+	 * however, is not true because the above code can only
+	 * be invoked for IBSS interfaces, and the below code will
+	 * not be -- and the two do not race against each other as
+	 * the hash table also keys off the interface.
+	 */
+
+	might_sleep();
+
+	mutex_lock(&local->sta_mtx);
+
+	err = sta_info_insert_mgd(sta);
+	if (err)
+		goto out_free;
+
+	return 0;
  out_free:
 	BUG_ON(!err);
 	__sta_info_free(local, sta);
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/2] mac80211: fix race condition between assoc_done and first EAP packet
  2011-07-26 21:29 [PATCH 1/2] mac80211: refactor sta_info_insert_rcu to 3 main stages Guy Eilam
@ 2011-07-26 21:29 ` Guy Eilam
  2011-08-08 13:35 ` [PATCH 1/2] mac80211: refactor sta_info_insert_rcu to 3 main stages Johannes Berg
  2011-08-09 12:20 ` Johannes Berg
  2 siblings, 0 replies; 7+ messages in thread
From: Guy Eilam @ 2011-07-26 21:29 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless

When associating to an AP, the station might miss the first EAP
packet that the AP sends due to a race condition between the association
success procedure and the rx flow in mac80211.
In such cases, the packet might fall in ieee80211_rx_h_check due to
the fact that the relevant rx->sta wasn't allocated yet.
Allocation of the relevant station info struct before actually
sending the association request and setting it with a new
dummy_sta flag solve this problem.
The station will accept only EAP packets from the AP while it
is in the pre-association/dummy state.
This dummy station entry is not seen by normal sta_info_get()
calls, only by sta_info_get_bss_rx().
The driver is not notified for the first insertion of the
dummy station. The driver is notified only after the association
is complete and the dummy flag is removed from the station entry.
That way, all the rest of the code flow should be untouched by
this change.

Signed-off-by: Guy Eilam <guy@wizery.com>
---
 net/mac80211/mlme.c     |   60 +++++++++++++--
 net/mac80211/rx.c       |   20 ++++-
 net/mac80211/sta_info.c |  192 ++++++++++++++++++++++++++++++++++-------------
 net/mac80211/sta_info.h |   30 +++++++-
 4 files changed, 240 insertions(+), 62 deletions(-)

diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index a497718..8a2fff1 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1495,10 +1495,14 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
 
 	ifmgd->aid = aid;
 
-	sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL);
-	if (!sta) {
-		printk(KERN_DEBUG "%s: failed to alloc STA entry for"
-		       " the AP\n", sdata->name);
+	mutex_lock(&sdata->local->sta_mtx);
+	/*
+	 * station info was already allocated and inserted before
+	 * the association and should be available to us
+	 */
+	sta = sta_info_get_rx(sdata, cbss->bssid);
+	if (WARN_ON(!sta)) {
+		mutex_unlock(&sdata->local->sta_mtx);
 		return false;
 	}
 
@@ -1569,8 +1573,10 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
 	if (elems.wmm_param)
 		set_sta_flags(sta, WLAN_STA_WME);
 
-	err = sta_info_insert(sta);
+	err = sta_info_insert_notify(sta);
 	sta = NULL;
+	/* sta_info_insert_notify changed the mutex lock with rcu lock */
+	rcu_read_unlock();
 	if (err) {
 		printk(KERN_DEBUG "%s: failed to insert STA entry for"
 		       " the AP (error %d)\n", sdata->name, err);
@@ -2387,15 +2393,43 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
 	return 0;
 }
 
+/* create and insert a dummy station entry */
+static int ieee80211_pre_assoc(struct ieee80211_sub_if_data *sdata,
+				u8 *bssid) {
+	struct sta_info *sta;
+	int err;
+
+	sta = sta_info_alloc(sdata, bssid, GFP_KERNEL);
+	if (!sta) {
+		printk(KERN_DEBUG "%s: failed to alloc STA entry for"
+			   " the AP\n", sdata->name);
+		return -ENOMEM;
+	}
+
+	sta->dummy = true;
+
+	err = sta_info_insert(sta);
+	sta = NULL;
+	if (err) {
+		printk(KERN_DEBUG "%s: failed to insert Dummy STA entry for"
+		       " the AP (error %d)\n", sdata->name, err);
+		return err;
+	}
+
+	return 0;
+}
+
 static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
 						  struct sk_buff *skb)
 {
 	struct ieee80211_mgmt *mgmt;
 	struct ieee80211_rx_status *rx_status;
 	struct ieee802_11_elems elems;
+	struct cfg80211_bss *cbss = wk->assoc.bss;
 	u16 status;
 
 	if (!skb) {
+		sta_info_destroy_addr(wk->sdata, cbss->bssid);
 		cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta);
 		return WORK_DONE_DESTROY;
 	}
@@ -2421,12 +2455,16 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
 		if (!ieee80211_assoc_success(wk, mgmt, skb->len)) {
 			mutex_unlock(&wk->sdata->u.mgd.mtx);
 			/* oops -- internal error -- send timeout for now */
+			sta_info_destroy_addr(wk->sdata, cbss->bssid);
 			cfg80211_send_assoc_timeout(wk->sdata->dev,
 						    wk->filter_ta);
 			return WORK_DONE_DESTROY;
 		}
 
 		mutex_unlock(&wk->sdata->u.mgd.mtx);
+	} else {
+		/* assoc failed - destroy the dummy station entry */
+		sta_info_destroy_addr(wk->sdata, cbss->bssid);
 	}
 
 	cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len);
@@ -2440,7 +2478,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 	struct ieee80211_bss *bss = (void *)req->bss->priv;
 	struct ieee80211_work *wk;
 	const u8 *ssid;
-	int i;
+	int i, err;
 
 	mutex_lock(&ifmgd->mtx);
 	if (ifmgd->associated) {
@@ -2465,6 +2503,16 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 	if (!wk)
 		return -ENOMEM;
 
+	/*
+	 * create a dummy station info entry in order
+	 * to start accepting incoming EAPOL packets from the station
+	 */
+	err = ieee80211_pre_assoc(sdata, req->bss->bssid);
+	if (err) {
+		kfree(wk);
+		return err;
+	}
+
 	ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
 	ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
 
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index e8e67dc..3146891 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -842,8 +842,20 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
 		      ieee80211_is_pspoll(hdr->frame_control)) &&
 		     rx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
 		     rx->sdata->vif.type != NL80211_IFTYPE_WDS &&
-		     (!rx->sta || !test_sta_flags(rx->sta, WLAN_STA_ASSOC))))
+		     (!rx->sta || !test_sta_flags(rx->sta, WLAN_STA_ASSOC)))) {
+		if (rx->sta->dummy && ieee80211_is_data(hdr->frame_control)) {
+			u16 ethertype;
+			u8 *payload;
+
+			payload = rx->skb->data +
+				ieee80211_hdrlen(hdr->frame_control);
+			ethertype = (payload[6] << 8) | payload[7];
+			if (cpu_to_be16(ethertype) ==
+				rx->sdata->control_port_protocol)
+				return RX_CONTINUE;
+		}
 		return RX_DROP_MONITOR;
+	}
 
 	return RX_CONTINUE;
 }
@@ -2782,7 +2794,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 	if (ieee80211_is_data(fc)) {
 		prev_sta = NULL;
 
-		for_each_sta_info(local, hdr->addr2, sta, tmp) {
+		for_each_sta_info_rx(local, hdr->addr2, sta, tmp) {
 			if (!prev_sta) {
 				prev_sta = sta;
 				continue;
@@ -2826,7 +2838,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 			continue;
 		}
 
-		rx.sta = sta_info_get_bss(prev, hdr->addr2);
+		rx.sta = sta_info_get_bss_rx(prev, hdr->addr2);
 		rx.sdata = prev;
 		ieee80211_prepare_and_rx_handle(&rx, skb, false);
 
@@ -2834,7 +2846,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 	}
 
 	if (prev) {
-		rx.sta = sta_info_get_bss(prev, hdr->addr2);
+		rx.sta = sta_info_get_bss_rx(prev, hdr->addr2);
 		rx.sdata = prev;
 
 		if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index b4549f2..b27a61b 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -102,6 +102,30 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
 				    lockdep_is_held(&local->sta_mtx));
 	while (sta) {
 		if (sta->sdata == sdata &&
+		    !sta->dummy &&
+		    memcmp(sta->sta.addr, addr, ETH_ALEN) == 0)
+			break;
+		sta = rcu_dereference_check(sta->hnext,
+					    rcu_read_lock_held() ||
+					    lockdep_is_held(&local->sta_lock) ||
+					    lockdep_is_held(&local->sta_mtx));
+	}
+	return sta;
+}
+
+/* get a station info entry even if it is a dummy station*/
+struct sta_info *sta_info_get_rx(struct ieee80211_sub_if_data *sdata,
+			      const u8 *addr)
+{
+	struct ieee80211_local *local = sdata->local;
+	struct sta_info *sta;
+
+	sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
+				    rcu_read_lock_held() ||
+				    lockdep_is_held(&local->sta_lock) ||
+				    lockdep_is_held(&local->sta_mtx));
+	while (sta) {
+		if (sta->sdata == sdata &&
 		    memcmp(sta->sta.addr, addr, ETH_ALEN) == 0)
 			break;
 		sta = rcu_dereference_check(sta->hnext,
@@ -129,6 +153,34 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
 	while (sta) {
 		if ((sta->sdata == sdata ||
 		     (sta->sdata->bss && sta->sdata->bss == sdata->bss)) &&
+		    !sta->dummy &&
+		    memcmp(sta->sta.addr, addr, ETH_ALEN) == 0)
+			break;
+		sta = rcu_dereference_check(sta->hnext,
+					    rcu_read_lock_held() ||
+					    lockdep_is_held(&local->sta_lock) ||
+					    lockdep_is_held(&local->sta_mtx));
+	}
+	return sta;
+}
+
+/*
+ * Get sta info either from the specified interface
+ * or from one of its vlans (including dummy stations)
+ */
+struct sta_info *sta_info_get_bss_rx(struct ieee80211_sub_if_data *sdata,
+				  const u8 *addr)
+{
+	struct ieee80211_local *local = sdata->local;
+	struct sta_info *sta;
+
+	sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
+				    rcu_read_lock_held() ||
+				    lockdep_is_held(&local->sta_lock) ||
+				    lockdep_is_held(&local->sta_mtx));
+	while (sta) {
+		if ((sta->sdata == sdata ||
+		     (sta->sdata->bss && sta->sdata->bss == sdata->bss)) &&
 		    memcmp(sta->sta.addr, addr, ETH_ALEN) == 0)
 			break;
 		sta = rcu_dereference_check(sta->hnext,
@@ -284,7 +336,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 	return sta;
 }
 
-static int sta_info_finish_insert(struct sta_info *sta, bool async)
+static int sta_info_finish_insert(struct sta_info *sta,
+				bool async, bool sta_list_add)
 {
 	struct ieee80211_local *local = sta->local;
 	struct ieee80211_sub_if_data *sdata = sta->sdata;
@@ -294,50 +347,55 @@ static int sta_info_finish_insert(struct sta_info *sta, bool async)
 
 	lockdep_assert_held(&local->sta_mtx);
 
-	/* notify driver */
-	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
-		sdata = container_of(sdata->bss,
-				     struct ieee80211_sub_if_data,
-				     u.ap);
-	err = drv_sta_add(local, sdata, &sta->sta);
-	if (err) {
-		if (!async)
-			return err;
-		printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to driver (%d)"
-				  " - keeping it anyway.\n",
-		       sdata->name, sta->sta.addr, err);
-	} else {
-		sta->uploaded = true;
+	if (!sta->dummy) {
+		/* notify driver */
+		if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+			sdata = container_of(sdata->bss,
+					     struct ieee80211_sub_if_data,
+					     u.ap);
+		err = drv_sta_add(local, sdata, &sta->sta);
+		if (err) {
+			if (!async)
+				return err;
+			printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to "
+					  "driver (%d) - keeping it anyway.\n",
+			       sdata->name, sta->sta.addr, err);
+		} else {
+			sta->uploaded = true;
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-		if (async)
-			wiphy_debug(local->hw.wiphy,
-				    "Finished adding IBSS STA %pM\n",
-				    sta->sta.addr);
+			if (async)
+				wiphy_debug(local->hw.wiphy,
+					    "Finished adding IBSS STA %pM\n",
+					    sta->sta.addr);
 #endif
+		}
+
+		sdata = sta->sdata;
 	}
 
-	sdata = sta->sdata;
+	if (sta_list_add) {
+		if (!async) {
+			local->num_sta++;
+			local->sta_generation++;
+			smp_mb();
 
-	if (!async) {
-		local->num_sta++;
-		local->sta_generation++;
-		smp_mb();
+			/* make the station visible */
+			spin_lock_irqsave(&local->sta_lock, flags);
+			sta_info_hash_add(local, sta);
+			spin_unlock_irqrestore(&local->sta_lock, flags);
+		}
 
-		/* make the station visible */
-		spin_lock_irqsave(&local->sta_lock, flags);
-		sta_info_hash_add(local, sta);
-		spin_unlock_irqrestore(&local->sta_lock, flags);
+		list_add(&sta->list, &local->sta_list);
 	}
 
-	list_add(&sta->list, &local->sta_list);
-
-	ieee80211_sta_debugfs_add(sta);
-	rate_control_add_sta_debugfs(sta);
-
-	sinfo.filled = 0;
-	sinfo.generation = local->sta_generation;
-	cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL);
+	if (!sta->dummy) {
+		ieee80211_sta_debugfs_add(sta);
+		rate_control_add_sta_debugfs(sta);
 
+		sinfo.filled = 0;
+		sinfo.generation = local->sta_generation;
+		cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL);
+	}
 
 	return 0;
 }
@@ -354,7 +412,7 @@ static void sta_info_finish_pending(struct ieee80211_local *local)
 		list_del(&sta->list);
 		spin_unlock_irqrestore(&local->sta_lock, flags);
 
-		sta_info_finish_insert(sta, true);
+		sta_info_finish_insert(sta, true, true);
 
 		spin_lock_irqsave(&local->sta_lock, flags);
 	}
@@ -398,7 +456,7 @@ static int sta_info_insert_adhoc(struct sta_info *sta) __acquires(RCU)
 
 	spin_lock_irqsave(&local->sta_lock, flags);
 	/* check if STA exists already */
-	if (sta_info_get_bss(sdata, sta->sta.addr)) {
+	if (sta_info_get_bss_rx(sdata, sta->sta.addr)) {
 		spin_unlock_irqrestore(&local->sta_lock, flags);
 		rcu_read_lock();
 		__sta_info_free(local, sta);
@@ -430,11 +488,13 @@ static int sta_info_insert_adhoc(struct sta_info *sta) __acquires(RCU)
  * this function replaces the mutex lock
  * with a RCU lock
  */
-static int sta_info_insert_mgd(struct sta_info *sta) __acquires(RCU)
+static int sta_info_insert_mgd(struct sta_info *sta,
+				bool sta_list_add) __acquires(RCU)
 {
 	struct ieee80211_local *local = sta->local;
 	struct ieee80211_sub_if_data *sdata = sta->sdata;
 	unsigned long flags;
+	struct sta_info *exist_sta;
 	int err = 0;
 
 	lockdep_assert_held(&local->sta_mtx);
@@ -450,17 +510,28 @@ static int sta_info_insert_mgd(struct sta_info *sta) __acquires(RCU)
 	 */
 
 	spin_lock_irqsave(&local->sta_lock, flags);
-	/* check if STA exists already */
-	if (sta_info_get_bss(sdata, sta->sta.addr)) {
-		spin_unlock_irqrestore(&local->sta_lock, flags);
-		mutex_unlock(&local->sta_mtx);
-		rcu_read_lock();
-		return -EEXIST;
+	/*
+	 * check if STA exists already.
+	 * only accept a scenario of a second call to sta_info_insert_mgd
+	 * with a dummy station entry that was inserted earlier
+	 * in that case - assume that the dummy station flag should
+	 * be removed.
+	 */
+	exist_sta = sta_info_get_bss_rx(sdata, sta->sta.addr);
+	if (exist_sta) {
+		if (exist_sta == sta && sta->dummy && !sta_list_add) {
+			sta->dummy = false;
+		} else {
+			spin_unlock_irqrestore(&local->sta_lock, flags);
+			mutex_unlock(&local->sta_mtx);
+			rcu_read_lock();
+			return -EEXIST;
+		}
 	}
 
 	spin_unlock_irqrestore(&local->sta_lock, flags);
 
-	err = sta_info_finish_insert(sta, false);
+	err = sta_info_finish_insert(sta, false, sta_list_add);
 	if (err) {
 		mutex_unlock(&local->sta_mtx);
 		rcu_read_lock();
@@ -468,14 +539,15 @@ static int sta_info_insert_mgd(struct sta_info *sta) __acquires(RCU)
 	}
 
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-	wiphy_debug(local->hw.wiphy, "Inserted STA %pM\n", sta->sta.addr);
+	wiphy_debug(local->hw.wiphy, "Inserted %sSTA %pM\n",
+			sta->dummy ? "Dummy " : "", sta->sta.addr);
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 
 	/* move reference to rcu-protected */
 	rcu_read_lock();
 	mutex_unlock(&local->sta_mtx);
 
-	if (ieee80211_vif_is_mesh(&sdata->vif))
+	if (ieee80211_vif_is_mesh(&sdata->vif) && !sta->dummy)
 		mesh_accept_plinks_update(sdata);
 
 	return 0;
@@ -514,7 +586,7 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
 
 	mutex_lock(&local->sta_mtx);
 
-	err = sta_info_insert_mgd(sta);
+	err = sta_info_insert_mgd(sta, true);
 	if (err)
 		goto out_free;
 
@@ -534,6 +606,24 @@ int sta_info_insert(struct sta_info *sta)
 	return err;
 }
 
+/* Caller must hold sta->local->sta_mtx */
+int sta_info_insert_notify(struct sta_info *sta) __acquires(RCU)
+{
+	struct ieee80211_local *local = sta->local;
+	int err = 0;
+
+	err = sta_info_insert_check(sta);
+	if (err) {
+		mutex_unlock(&local->sta_mtx);
+		rcu_read_lock();
+		return err;
+	}
+
+	might_sleep();
+
+	return sta_info_insert_mgd(sta, false);
+}
+
 static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
 {
 	/*
@@ -774,7 +864,7 @@ int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata, const u8 *addr)
 	int ret;
 
 	mutex_lock(&sdata->local->sta_mtx);
-	sta = sta_info_get(sdata, addr);
+	sta = sta_info_get_rx(sdata, addr);
 	ret = __sta_info_destroy(sta);
 	mutex_unlock(&sdata->local->sta_mtx);
 
@@ -788,7 +878,7 @@ int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata,
 	int ret;
 
 	mutex_lock(&sdata->local->sta_mtx);
-	sta = sta_info_get_bss(sdata, addr);
+	sta = sta_info_get_bss_rx(sdata, addr);
 	ret = __sta_info_destroy(sta);
 	mutex_unlock(&sdata->local->sta_mtx);
 
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index a06d64e..b1b104b 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -238,10 +238,12 @@ struct sta_ampdu_mlme {
  * @plink_timer: peer link watch timer
  * @plink_timer_was_running: used by suspend/resume to restore timers
  * @debugfs: debug filesystem info
- * @sta: station information we share with the driver
  * @dead: set to true when sta is unlinked
  * @uploaded: set to true when sta is uploaded to the driver
  * @lost_packets: number of consecutive lost packets
+ * @dummy: indicate a dummy station created for receiving
+ *	EAP frames before association
+ * @sta: station information we share with the driver
  */
 struct sta_info {
 	/* General information, mostly static */
@@ -335,6 +337,9 @@ struct sta_info {
 
 	unsigned int lost_packets;
 
+	/* should be right in front of sta to be in the same cache line */
+	bool dummy;
+
 	/* keep last! */
 	struct ieee80211_sta sta;
 };
@@ -435,9 +440,15 @@ rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid)
 struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
 			      const u8 *addr);
 
+struct sta_info *sta_info_get_rx(struct ieee80211_sub_if_data *sdata,
+			      const u8 *addr);
+
 struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
 				  const u8 *addr);
 
+struct sta_info *sta_info_get_bss_rx(struct ieee80211_sub_if_data *sdata,
+				  const u8 *addr);
+
 static inline
 void for_each_sta_info_type_check(struct ieee80211_local *local,
 				  const u8 *addr,
@@ -458,6 +469,22 @@ void for_each_sta_info_type_check(struct ieee80211_local *local,
 		_sta = nxt,						\
 		nxt = _sta ? rcu_dereference(_sta->hnext) : NULL	\
 	     )								\
+	/* run code only if address matches and it's not a dummy sta */	\
+	if (memcmp(_sta->sta.addr, (_addr), ETH_ALEN) == 0 &&\
+		!_sta->dummy)
+
+#define for_each_sta_info_rx(local, _addr, _sta, nxt)			\
+	for (/* initialise loop */\
+		_sta = rcu_dereference(local->sta_hash[STA_HASH(_addr)]),\
+		nxt = _sta ? rcu_dereference(_sta->hnext) : NULL;	\
+		/* typecheck */						\
+		for_each_sta_info_type_check(local, (_addr), _sta, nxt),\
+		/* continue condition */				\
+		_sta;							\
+		/* advance loop */					\
+		_sta = nxt,						\
+		nxt = _sta ? rcu_dereference(_sta->hnext) : NULL	\
+		)							   \
 	/* compare address and run code only if it matches */		\
 	if (memcmp(_sta->sta.addr, (_addr), ETH_ALEN) == 0)
 
@@ -483,6 +510,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 int sta_info_insert(struct sta_info *sta);
 int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU);
 int sta_info_insert_atomic(struct sta_info *sta);
+int sta_info_insert_notify(struct sta_info *sta)  __acquires(RCU);
 
 int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata,
 			  const u8 *addr);
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] mac80211: refactor sta_info_insert_rcu to 3 main stages
  2011-07-26 21:29 [PATCH 1/2] mac80211: refactor sta_info_insert_rcu to 3 main stages Guy Eilam
  2011-07-26 21:29 ` [PATCH 2/2] mac80211: fix race condition between assoc_done and first EAP packet Guy Eilam
@ 2011-08-08 13:35 ` Johannes Berg
  2011-08-09  9:02   ` Guy Eilam
  2011-08-09 12:20 ` Johannes Berg
  2 siblings, 1 reply; 7+ messages in thread
From: Johannes Berg @ 2011-08-08 13:35 UTC (permalink / raw)
  To: Guy Eilam; +Cc: linux-wireless

Just a cursory look, I'll send a more detailed review later, but I
wanted to ask John to not merge this for now; 

> +static int sta_info_insert_adhoc(struct sta_info *sta) __acquires(RCU)

_ibss would be preferred

> +static int sta_info_insert_mgd(struct sta_info *sta) __acquires(RCU)

_mgd is terribly misleading, this will be done for all modes other than
IBSS.

johannes



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] mac80211: refactor sta_info_insert_rcu to 3 main stages
  2011-08-08 13:35 ` [PATCH 1/2] mac80211: refactor sta_info_insert_rcu to 3 main stages Johannes Berg
@ 2011-08-09  9:02   ` Guy Eilam
  0 siblings, 0 replies; 7+ messages in thread
From: Guy Eilam @ 2011-08-09  9:02 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless

On Mon, Aug 8, 2011 at 4:35 PM, Johannes Berg <johannes@sipsolutions.net> wrote:
> Just a cursory look, I'll send a more detailed review later, but I
> wanted to ask John to not merge this for now;
>
>> +static int sta_info_insert_adhoc(struct sta_info *sta) __acquires(RCU)
>
> _ibss would be preferred
>
>> +static int sta_info_insert_mgd(struct sta_info *sta) __acquires(RCU)
>
> _mgd is terribly misleading, this will be done for all modes other than
> IBSS.

Fair enough. the _ibss change is logical and understood.
The problem with the _mgd function is that it will be silly to call it
_non_ibss (or is it?)
Do you have a suggestion for a better name?

>
> johannes
>
>
>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] mac80211: refactor sta_info_insert_rcu to 3 main stages
  2011-07-26 21:29 [PATCH 1/2] mac80211: refactor sta_info_insert_rcu to 3 main stages Guy Eilam
  2011-07-26 21:29 ` [PATCH 2/2] mac80211: fix race condition between assoc_done and first EAP packet Guy Eilam
  2011-08-08 13:35 ` [PATCH 1/2] mac80211: refactor sta_info_insert_rcu to 3 main stages Johannes Berg
@ 2011-08-09 12:20 ` Johannes Berg
  2011-08-16 17:11   ` Guy Eilam
  2 siblings, 1 reply; 7+ messages in thread
From: Johannes Berg @ 2011-08-09 12:20 UTC (permalink / raw)
  To: Guy Eilam; +Cc: linux-wireless

On Wed, 2011-07-27 at 00:29 +0300, Guy Eilam wrote:

> -int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
> +static int sta_info_insert_check(struct sta_info *sta)
>  {
> -	struct ieee80211_local *local = sta->local;
>  	struct ieee80211_sub_if_data *sdata = sta->sdata;
> -	unsigned long flags;
> -	int err = 0;
>  
>  	/*
>  	 * Can't be a WARN_ON because it can be triggered through a race:
>  	 * something inserts a STA (on one CPU) without holding the RTNL
>  	 * and another CPU turns off the net device.
>  	 */
> -	if (unlikely(!ieee80211_sdata_running(sdata))) {
> -		err = -ENETDOWN;
> -		rcu_read_lock();
> -		goto out_free;
> -	}
> +	if (unlikely(!ieee80211_sdata_running(sdata)))
> +		return -ENETDOWN;
>  
>  	if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->vif.addr) == 0 ||
> -		    is_multicast_ether_addr(sta->sta.addr))) {
> -		err = -EINVAL;
> +		    is_multicast_ether_addr(sta->sta.addr)))
> +		return -EINVAL;
> +
> +	return 0;
> +}

That seems nice, even simplifying the code :)

> +static int sta_info_insert_adhoc(struct sta_info *sta) __acquires(RCU)
> +{
> +	struct ieee80211_local *local = sta->local;
> +	struct ieee80211_sub_if_data *sdata = sta->sdata;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&local->sta_lock, flags);
> +	/* check if STA exists already */
> +	if (sta_info_get_bss(sdata, sta->sta.addr)) {
> +		spin_unlock_irqrestore(&local->sta_lock, flags);
>  		rcu_read_lock();
> -		goto out_free;
> +		__sta_info_free(local, sta);
> +		return -EEXIST;
>  	}
>  
> -	/*
> -	 * In ad-hoc mode, we sometimes need to insert stations
> -	 * from tasklet context from the RX path. To avoid races,
> -	 * always do so in that case -- see the comment below.
> -	 */
> -	if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
> -		spin_lock_irqsave(&local->sta_lock, flags);
> -		/* check if STA exists already */
> -		if (sta_info_get_bss(sdata, sta->sta.addr)) {
> -			spin_unlock_irqrestore(&local->sta_lock, flags);
> -			rcu_read_lock();
> -			err = -EEXIST;
> -			goto out_free;
> -		}
> -
> -		local->num_sta++;
> -		local->sta_generation++;
> -		smp_mb();
> -		sta_info_hash_add(local, sta);
> +	local->num_sta++;
> +	local->sta_generation++;
> +	smp_mb();
> +	sta_info_hash_add(local, sta);
>  
> -		list_add_tail(&sta->list, &local->sta_pending_list);
> +	list_add_tail(&sta->list, &local->sta_pending_list);
>  
> -		rcu_read_lock();
> -		spin_unlock_irqrestore(&local->sta_lock, flags);
> +	rcu_read_lock();
> +	spin_unlock_irqrestore(&local->sta_lock, flags);
>  
>  #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
> -		wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n",
> -			    sta->sta.addr);
> +	wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n",
> +			sta->sta.addr);
>  #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
>  
> -		ieee80211_queue_work(&local->hw, &local->sta_finish_work);
> +	ieee80211_queue_work(&local->hw, &local->sta_finish_work);
>  
> -		return 0;
> -	}
> +	return 0;
> +}

Yup, that looks good too, just the patch is a bit odd due to the code
indentation change.

> +/*
> + * should be called with sta_mtx locked
> + * this function replaces the mutex lock
> + * with a RCU lock
> + */
> +static int sta_info_insert_mgd(struct sta_info *sta) __acquires(RCU)
> +{
> +	struct ieee80211_local *local = sta->local;
> +	struct ieee80211_sub_if_data *sdata = sta->sdata;
> +	unsigned long flags;
> +	int err = 0;
> +
> +	lockdep_assert_held(&local->sta_mtx);
>  
>  	/*
>  	 * On first glance, this will look racy, because the code
> -	 * below this point, which inserts a station with sleeping,
> +	 * in this function, which inserts a station with sleeping,
>  	 * unlocks the sta_lock between checking existence in the
>  	 * hash table and inserting into it.
>  	 *
>  	 * However, it is not racy against itself because it keeps
> -	 * the mutex locked. It still seems to race against the
> -	 * above code that atomically inserts the station... That,
> -	 * however, is not true because the above code can only
> -	 * be invoked for IBSS interfaces, and the below code will
> -	 * not be -- and the two do not race against each other as
> -	 * the hash table also keys off the interface.
> +	 * the mutex locked.
>  	 */
>  
> -	might_sleep();
> -
> -	mutex_lock(&local->sta_mtx);
> -
>  	spin_lock_irqsave(&local->sta_lock, flags);
>  	/* check if STA exists already */
>  	if (sta_info_get_bss(sdata, sta->sta.addr)) {
>  		spin_unlock_irqrestore(&local->sta_lock, flags);
>  		mutex_unlock(&local->sta_mtx);
>  		rcu_read_lock();
> -		err = -EEXIST;
> -		goto out_free;
> +		return -EEXIST;

Isn't that missing the free now?

>  	}
>  
>  	spin_unlock_irqrestore(&local->sta_lock, flags);
> @@ -466,7 +464,7 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
>  	if (err) {
>  		mutex_unlock(&local->sta_mtx);
>  		rcu_read_lock();
> -		goto out_free;
> +		return err;

and here?

> +int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
> +{
> +	struct ieee80211_local *local = sta->local;
> +	struct ieee80211_sub_if_data *sdata = sta->sdata;
> +	int err = 0;
> +
> +	err = sta_info_insert_check(sta);
> +	if (err) {
> +		rcu_read_lock();
> +		goto out_free;
> +	}
> +
> +	/*
> +	 * In ad-hoc mode, we sometimes need to insert stations
> +	 * from tasklet context from the RX path. To avoid races,
> +	 * always do so in that case -- see the comment below.
> +	 */
> +	if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
> +		return sta_info_insert_adhoc(sta);
> +
> +	/*
> +	 * It might seem that the function called below is in race against
> +	 * the function call above that atomically inserts the station... That,
> +	 * however, is not true because the above code can only
> +	 * be invoked for IBSS interfaces, and the below code will
> +	 * not be -- and the two do not race against each other as
> +	 * the hash table also keys off the interface.
> +	 */
> +
> +	might_sleep();
> +
> +	mutex_lock(&local->sta_mtx);
> +
> +	err = sta_info_insert_mgd(sta);
> +	if (err)
> +		goto out_free;
> +
> +	return 0;
>   out_free:
>  	BUG_ON(!err);
>  	__sta_info_free(local, sta);

Ah, ok, I guess not.

Hmm. Can we make the ibss vs. non-IBSS code paths more similar wrt.
freeing? The _ibss one will free it, but the _mgd one won't, that seems
a bit strange to me.

Also, I'd prefer to move the might_sleep/locking into _mgd() if
possible?

johannes


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] mac80211: refactor sta_info_insert_rcu to 3 main stages
  2011-08-09 12:20 ` Johannes Berg
@ 2011-08-16 17:11   ` Guy Eilam
  2011-08-16 17:24     ` Johannes Berg
  0 siblings, 1 reply; 7+ messages in thread
From: Guy Eilam @ 2011-08-16 17:11 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless

On Tue, Aug 9, 2011 at 3:20 PM, Johannes Berg <johannes@sipsolutions.net> wrote:
> On Wed, 2011-07-27 at 00:29 +0300, Guy Eilam wrote:
>
>> -int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
>> +static int sta_info_insert_check(struct sta_info *sta)
>>  {
>> -     struct ieee80211_local *local = sta->local;
>>       struct ieee80211_sub_if_data *sdata = sta->sdata;
>> -     unsigned long flags;
>> -     int err = 0;
>>
>>       /*
>>        * Can't be a WARN_ON because it can be triggered through a race:
>>        * something inserts a STA (on one CPU) without holding the RTNL
>>        * and another CPU turns off the net device.
>>        */
>> -     if (unlikely(!ieee80211_sdata_running(sdata))) {
>> -             err = -ENETDOWN;
>> -             rcu_read_lock();
>> -             goto out_free;
>> -     }
>> +     if (unlikely(!ieee80211_sdata_running(sdata)))
>> +             return -ENETDOWN;
>>
>>       if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->vif.addr) == 0 ||
>> -                 is_multicast_ether_addr(sta->sta.addr))) {
>> -             err = -EINVAL;
>> +                 is_multicast_ether_addr(sta->sta.addr)))
>> +             return -EINVAL;
>> +
>> +     return 0;
>> +}
>
> That seems nice, even simplifying the code :)
>
>> +static int sta_info_insert_adhoc(struct sta_info *sta) __acquires(RCU)
>> +{
>> +     struct ieee80211_local *local = sta->local;
>> +     struct ieee80211_sub_if_data *sdata = sta->sdata;
>> +     unsigned long flags;
>> +
>> +     spin_lock_irqsave(&local->sta_lock, flags);
>> +     /* check if STA exists already */
>> +     if (sta_info_get_bss(sdata, sta->sta.addr)) {
>> +             spin_unlock_irqrestore(&local->sta_lock, flags);
>>               rcu_read_lock();
>> -             goto out_free;
>> +             __sta_info_free(local, sta);
>> +             return -EEXIST;
>>       }
>>
>> -     /*
>> -      * In ad-hoc mode, we sometimes need to insert stations
>> -      * from tasklet context from the RX path. To avoid races,
>> -      * always do so in that case -- see the comment below.
>> -      */
>> -     if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
>> -             spin_lock_irqsave(&local->sta_lock, flags);
>> -             /* check if STA exists already */
>> -             if (sta_info_get_bss(sdata, sta->sta.addr)) {
>> -                     spin_unlock_irqrestore(&local->sta_lock, flags);
>> -                     rcu_read_lock();
>> -                     err = -EEXIST;
>> -                     goto out_free;
>> -             }
>> -
>> -             local->num_sta++;
>> -             local->sta_generation++;
>> -             smp_mb();
>> -             sta_info_hash_add(local, sta);
>> +     local->num_sta++;
>> +     local->sta_generation++;
>> +     smp_mb();
>> +     sta_info_hash_add(local, sta);
>>
>> -             list_add_tail(&sta->list, &local->sta_pending_list);
>> +     list_add_tail(&sta->list, &local->sta_pending_list);
>>
>> -             rcu_read_lock();
>> -             spin_unlock_irqrestore(&local->sta_lock, flags);
>> +     rcu_read_lock();
>> +     spin_unlock_irqrestore(&local->sta_lock, flags);
>>
>>  #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
>> -             wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n",
>> -                         sta->sta.addr);
>> +     wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n",
>> +                     sta->sta.addr);
>>  #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
>>
>> -             ieee80211_queue_work(&local->hw, &local->sta_finish_work);
>> +     ieee80211_queue_work(&local->hw, &local->sta_finish_work);
>>
>> -             return 0;
>> -     }
>> +     return 0;
>> +}
>
> Yup, that looks good too, just the patch is a bit odd due to the code
> indentation change.
>
>> +/*
>> + * should be called with sta_mtx locked
>> + * this function replaces the mutex lock
>> + * with a RCU lock
>> + */
>> +static int sta_info_insert_mgd(struct sta_info *sta) __acquires(RCU)
>> +{
>> +     struct ieee80211_local *local = sta->local;
>> +     struct ieee80211_sub_if_data *sdata = sta->sdata;
>> +     unsigned long flags;
>> +     int err = 0;
>> +
>> +     lockdep_assert_held(&local->sta_mtx);
>>
>>       /*
>>        * On first glance, this will look racy, because the code
>> -      * below this point, which inserts a station with sleeping,
>> +      * in this function, which inserts a station with sleeping,
>>        * unlocks the sta_lock between checking existence in the
>>        * hash table and inserting into it.
>>        *
>>        * However, it is not racy against itself because it keeps
>> -      * the mutex locked. It still seems to race against the
>> -      * above code that atomically inserts the station... That,
>> -      * however, is not true because the above code can only
>> -      * be invoked for IBSS interfaces, and the below code will
>> -      * not be -- and the two do not race against each other as
>> -      * the hash table also keys off the interface.
>> +      * the mutex locked.
>>        */
>>
>> -     might_sleep();
>> -
>> -     mutex_lock(&local->sta_mtx);
>> -
>>       spin_lock_irqsave(&local->sta_lock, flags);
>>       /* check if STA exists already */
>>       if (sta_info_get_bss(sdata, sta->sta.addr)) {
>>               spin_unlock_irqrestore(&local->sta_lock, flags);
>>               mutex_unlock(&local->sta_mtx);
>>               rcu_read_lock();
>> -             err = -EEXIST;
>> -             goto out_free;
>> +             return -EEXIST;
>
> Isn't that missing the free now?
>
>>       }
>>
>>       spin_unlock_irqrestore(&local->sta_lock, flags);
>> @@ -466,7 +464,7 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
>>       if (err) {
>>               mutex_unlock(&local->sta_mtx);
>>               rcu_read_lock();
>> -             goto out_free;
>> +             return err;
>
> and here?
>
>> +int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
>> +{
>> +     struct ieee80211_local *local = sta->local;
>> +     struct ieee80211_sub_if_data *sdata = sta->sdata;
>> +     int err = 0;
>> +
>> +     err = sta_info_insert_check(sta);
>> +     if (err) {
>> +             rcu_read_lock();
>> +             goto out_free;
>> +     }
>> +
>> +     /*
>> +      * In ad-hoc mode, we sometimes need to insert stations
>> +      * from tasklet context from the RX path. To avoid races,
>> +      * always do so in that case -- see the comment below.
>> +      */
>> +     if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
>> +             return sta_info_insert_adhoc(sta);
>> +
>> +     /*
>> +      * It might seem that the function called below is in race against
>> +      * the function call above that atomically inserts the station... That,
>> +      * however, is not true because the above code can only
>> +      * be invoked for IBSS interfaces, and the below code will
>> +      * not be -- and the two do not race against each other as
>> +      * the hash table also keys off the interface.
>> +      */
>> +
>> +     might_sleep();
>> +
>> +     mutex_lock(&local->sta_mtx);
>> +
>> +     err = sta_info_insert_mgd(sta);
>> +     if (err)
>> +             goto out_free;
>> +
>> +     return 0;
>>   out_free:
>>       BUG_ON(!err);
>>       __sta_info_free(local, sta);
>
> Ah, ok, I guess not.
>
> Hmm. Can we make the ibss vs. non-IBSS code paths more similar wrt.
> freeing? The _ibss one will free it, but the _mgd one won't, that seems
> a bit strange to me.
>
> Also, I'd prefer to move the might_sleep/locking into _mgd() if
> possible?

I'll make sure that the _ibss and _non_ibss code paths will have
similar freeing (the freeinng in both cases will be handled outside
the function like it is in the _non_ibss now).
The reason I have the might_sleep/locking outside the _non_ibss
function is because the additions in the next patch. The whole point
of this refactoring from my point of view was the option to have a
similar insert function that won't have the locking inside of it.

>
> johannes
>
>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] mac80211: refactor sta_info_insert_rcu to 3 main stages
  2011-08-16 17:11   ` Guy Eilam
@ 2011-08-16 17:24     ` Johannes Berg
  0 siblings, 0 replies; 7+ messages in thread
From: Johannes Berg @ 2011-08-16 17:24 UTC (permalink / raw)
  To: Guy Eilam; +Cc: linux-wireless

On 8/16/2011 10:11 AM, Guy Eilam wrote:

> I'll make sure that the _ibss and _non_ibss code paths will have
> similar freeing (the freeinng in both cases will be handled outside
> the function like it is in the _non_ibss now).
> The reason I have the might_sleep/locking outside the _non_ibss
> function is because the additions in the next patch. The whole point
> of this refactoring from my point of view was the option to have a
> similar insert function that won't have the locking inside of it.

Ok, fair enough, so it's not possible to move the locking :)

johannes

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2011-08-16 17:24 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-07-26 21:29 [PATCH 1/2] mac80211: refactor sta_info_insert_rcu to 3 main stages Guy Eilam
2011-07-26 21:29 ` [PATCH 2/2] mac80211: fix race condition between assoc_done and first EAP packet Guy Eilam
2011-08-08 13:35 ` [PATCH 1/2] mac80211: refactor sta_info_insert_rcu to 3 main stages Johannes Berg
2011-08-09  9:02   ` Guy Eilam
2011-08-09 12:20 ` Johannes Berg
2011-08-16 17:11   ` Guy Eilam
2011-08-16 17:24     ` Johannes Berg

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.