Search Linux Wireless

Re: [PATCH 2/3] cfg80211: fix processing world regdomain when non modular

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

 



Hey Collen, Luis,

On Sun, Mar 16, 2014 at 9:04 PM, Colleen T <colleen@xxxxxxxxxxx> wrote:
> On Sat, Mar 15, 2014 at 9:42 PM, Luis R. Rodriguez
> <mcgrof@xxxxxxxxxxxxxxxx> wrote:
>> On Sat, Mar 15, 2014 at 8:59 AM, Janusz Dziedzic
>> <janusz.dziedzic@xxxxxxxxx> wrote:
>>> 2014-03-15 2:03 GMT+01:00 Luis R. Rodriguez <mcgrof@xxxxxxxxxxxxxxxx>:
>>>> On Fri, Mar 14, 2014 at 3:12 PM, Colleen T <colleen@xxxxxxxxxxx> wrote:
>>>>> I'm on mac80211-next/master, sha is
>>>>> 5a970df8990d173e7e4092952f2e3da1de69b27d
>>>>
>>>> I tried to reproduce by just merging the fixes in question onto Linus'
>>>> tree and using radios=3 but no go. Can you provide the full kernel
>>>> log, I'm particularly interested in what happened before. The
>>>> COUNTRY=US on debian, which I believe you're on, should just trigger a
>>>> regulatory domain setting to US upon initialization. Depending on how
>>>> Debian does it this could either trigger as a module parameter or as a
>>>> userspace event *after* the world regdom get set.
>
> I'm attaching a full kernel log that shows the warning being
> triggered.  You are correct, I'm on debian.  In the attached case I
> ran:
> $ modprobe mac80211_hwsim radios=4
>  I pulled the fixes onto Linus' tree and I end up with the same result.
>

Seems I might have found the culprit - reg_todo is called while the
request to CRDA is in flight and the patch in question causes the
already-in-process user-request to be handled again. Since it's the
same regdomain as the last request (it's the last request itself), we
get this:

treatment = __reg_process_hint_user(user_request);
if (treatment == REG_REQ_IGNORE ||
   treatment == REG_REQ_ALREADY_SET) {
kfree(user_request); <------
return treatment;
}

Can you try adding the attached patch? It just replaced relevant
kfree-s with a function that avoids freeing the last request.

Also, in the current scheme of things, CRDA might be called twice for
the same request in some corner cases, but that's not a problem, since
reg_is_valid_request() will block the second set_regdom().

Regards,
Arik
From c05c4e5eefc56a1dd1d74d7326a599deb44c6844 Mon Sep 17 00:00:00 2001
From: Arik Nemtsov <arik@xxxxxxxxxx>
Date: Wed, 9 Apr 2014 19:24:16 +0300
Subject: [PATCH] cfg80211: avoid freeing last_request while in flight

Avoid freeing the last request while it is being processed. This can
happen in some cases if reg_work is kicked for some reason while the
currently pending request is in flight.

Signed-off-by: Arik Nemtsov <arik@xxxxxxxxxx>
---
 net/wireless/reg.c | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index ba04c63..bad094b 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -240,7 +240,13 @@ static char user_alpha2[2];
 module_param(ieee80211_regdom, charp, 0444);
 MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
 
-static void reg_free_request(struct regulatory_request *lr)
+static void reg_free_request(struct regulatory_request *request)
+{
+	if (request != get_last_request())
+		kfree(request);
+}
+
+static void reg_free_last_request(struct regulatory_request *lr)
 {
 	if (lr != &core_request_world && lr)
 		kfree_rcu(lr, rcu_head);
@@ -254,7 +260,7 @@ static void reg_update_last_request(struct regulatory_request *request)
 	if (lr == request)
 		return;
 
-	reg_free_request(lr);
+	reg_free_last_request(lr);
 	rcu_assign_pointer(last_request, request);
 }
 
@@ -1616,7 +1622,7 @@ reg_process_hint_user(struct regulatory_request *user_request)
 	treatment = __reg_process_hint_user(user_request);
 	if (treatment == REG_REQ_IGNORE ||
 	    treatment == REG_REQ_ALREADY_SET) {
-		kfree(user_request);
+		reg_free_request(user_request);
 		return treatment;
 	}
 
@@ -1676,14 +1682,14 @@ reg_process_hint_driver(struct wiphy *wiphy,
 	case REG_REQ_OK:
 		break;
 	case REG_REQ_IGNORE:
-		kfree(driver_request);
+		reg_free_request(driver_request);
 		return treatment;
 	case REG_REQ_INTERSECT:
 		/* fall through */
 	case REG_REQ_ALREADY_SET:
 		regd = reg_copy_regd(get_cfg80211_regdom());
 		if (IS_ERR(regd)) {
-			kfree(driver_request);
+			reg_free_request(driver_request);
 			return REG_REQ_IGNORE;
 		}
 		rcu_assign_pointer(wiphy->regd, regd);
@@ -1777,10 +1783,10 @@ reg_process_hint_country_ie(struct wiphy *wiphy,
 	case REG_REQ_IGNORE:
 		/* fall through */
 	case REG_REQ_ALREADY_SET:
-		kfree(country_ie_request);
+		reg_free_request(country_ie_request);
 		return treatment;
 	case REG_REQ_INTERSECT:
-		kfree(country_ie_request);
+		reg_free_request(country_ie_request);
 		/*
 		 * This doesn't happen yet, not sure we
 		 * ever want to support it for this case.
@@ -1841,7 +1847,7 @@ static void reg_process_hint(struct regulatory_request *reg_request)
 	return;
 
 out_free:
-	kfree(reg_request);
+	reg_free_request(reg_request);
 }
 
 /*
-- 
1.8.1.2


[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux