So far when receiving event about in-firmware-interface removal we were
notifying our listener and afterwards we were removing Linux interface.
This order was most likely a try to avoid a lockup. Removing in-firmware
interface could be requested by a driver code holding rtnl lock. Such
code waits for a corresponding event (still holding rtnl lock) which
prevents us from calling unregister_netdev. Notifying listener first
allowed it to release rtnl lock and removing interface succesfully.
Please note we couldn't switch to unregister_netdevice as interface
removal event could also occur in other (unpredictable) moments.
Unfortunately above workaround doesn't work in some corner cases. Focus
on the time slot between calling event handler and removing Linux
interface. During that time original caller may leave (unlocking rtnl
semaphore) *and* another call to the same code may be done (locking it
again). If that happens our event handler will stuck at removing Linux
interface, it won't handle another event and will block process holding
rtnl lock.
So the real solution is to remove interface before notifying listener.
We just need to know if rtnl is hold by a caller that triggered our
event.
Moreover this changes makes sure we call unregister_netdevice before
del_virtual_intf leaves which isn't critical but it makes a bit more of
sense to handle it this way.
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
---
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
New firmwares (e.g. 10.10.69.36 for BCM4366) support "interface_remove"
for removing interfaces. Try to use this method on cfg80211 request. In
case of older firmwares (e.g. 7.35.177.56 for BCM43602 as I tested) this
will just result in firmware rejecting command and this won't change any
behavior.
Signed-off-by: Rafał Miłecki <redacted>
---
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 37 +++++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
--
1.8.4.5
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
The reason for doing this first is because we are passing the ifp, which
is netdev_priv(ifp->ndev). In brcmf_remove_interface() we only
unregister the netdev, which will end up (after scheduling) in
brcmf_free_netdev() thus freeing the ifp. By moving the event handler
function ifp may be stale already.
I guess rtnl_locked here means "rtnl_is_locked() by brcmfmac". It
actually does not matter who is holding the rtnl_lock. At least when it
is brcmfmac it is still a different task, ie. hostapd, iw, etc. Also
when brcmf_cfg80211_vif_event_armed() return false there may still be
some task holding the rtnl_lock.
Regards,
Arend
From: Arend van Spriel <arend.vanspriel@broadcom.com> Date: 2016-06-18 19:32:32
On 18-06-16 20:18, Rafał Miłecki wrote:
quoted hunk
New firmwares (e.g. 10.10.69.36 for BCM4366) support "interface_remove"
for removing interfaces. Try to use this method on cfg80211 request. In
case of older firmwares (e.g. 7.35.177.56 for BCM43602 as I tested) this
will just result in firmware rejecting command and this won't change any
behavior.
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
---
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 37 +++++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
ndev can be NULL, ie. for IFTYPE_P2P_DEVICE.
Regards,
Arend
quoted hunk
+ return -ENOTSUPP;
+
/* vif event pending in firmware */
if (brcmf_cfg80211_vif_event_armed(cfg))
return -EBUSY;
@@ -807,12 +841,13 @@ int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_STATION:- case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MESH_POINT: return -EOPNOTSUPP;+ case NL80211_IFTYPE_AP:+ return brcmf_cfg80211_del_ap_iface(wiphy, wdev); case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE:
The reason for doing this first is because we are passing the ifp, which
is netdev_priv(ifp->ndev). In brcmf_remove_interface() we only
unregister the netdev, which will end up (after scheduling) in
brcmf_free_netdev() thus freeing the ifp. By moving the event handler
function ifp may be stale already.
Good catch. What about making brcmf_fweh_call_event_handler work
without ifp? Would that be OK then?
I guess rtnl_locked here means "rtnl_is_locked() by brcmfmac". It
actually does not matter who is holding the rtnl_lock. At least when it
is brcmfmac it is still a different task, ie. hostapd, iw, etc. Also
when brcmf_cfg80211_vif_event_armed() return false there may still be
some task holding the rtnl_lock.
It does matter who holds the lock.
If it's e.g. some other driver (ath, intel, ralink, whatever) we still
should call unregister_netdevice. It'll just wait until rtnl lock gets
released.
If it's brcmfmac holding the lock, we can't expect it to be released
as brcmfmac waits for completion event.
--
Rafał
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
The reason for doing this first is because we are passing the ifp, which
is netdev_priv(ifp->ndev). In brcmf_remove_interface() we only
unregister the netdev, which will end up (after scheduling) in
brcmf_free_netdev() thus freeing the ifp. By moving the event handler
function ifp may be stale already.
Good catch. What about making brcmf_fweh_call_event_handler work
without ifp? Would that be OK then?
Maybe I have even better idea. What about handling Linux interface
removal in the code waiting for BRCMF_E_IF_DEL? We already do
something like that in case of BRCMF_E_IF_ADD (it is brcmf_ap_add_vif
that calls brcmf_net_attach).
New firmwares (e.g. 10.10.69.36 for BCM4366) support "interface_remove"
for removing interfaces. Try to use this method on cfg80211 request. In
case of older firmwares (e.g. 7.35.177.56 for BCM43602 as I tested) this
will just result in firmware rejecting command and this won't change any
behavior.
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
---
V2: Add extra chcek for ndev. Thanks Arend.
---
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 39 +++++++++++++++++++++-
1 file changed, 38 insertions(+), 1 deletion(-)
So far when receiving event about in-firmware-interface removal our
event worker was notifying listener and afterwards it was removing Linux
interface.
First of all it was resulting in slightly unexpected order. The listener
(del_virtual_intf callback) was (usually) returning with success before
we even called unregister_netdev(ice).
Please note this couldn't be simply fixed by changing order of calls in
brcmf_fweh_handle_if_event as unregistering interface earlier could free
struct brcmf_if.
Another problem of current implementation are possible lockups. Focus on
the time slot between calling event handler and removing Linux
interface. During that time original caller may leave (unlocking rtnl
semaphore) *and* another call to the same code may be done (locking it
again). If that happens our event handler will stuck at removing Linux
interface, it won't handle another event and will block process holding
rtnl lock.
This can be simply solved by unregistering interface in a proper
callback, right after receiving confirmation event from firmware. This
only required modifying worker to don't unregister on its own if there
is someone waiting for the event.
Signed-off-by: Rafał Miłecki <redacted>
---
V2: Modification of brcmf_fweh_handle_if_event done in V1 was a wrong
idea as it could result in use-after-free regarding struct brcmf_if.
Thanks Arend for noticing that!
---
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c | 10 ++++++++--
drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c | 3 +--
2 files changed, 9 insertions(+), 4 deletions(-)
@@ -182,8 +183,13 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,err=brcmf_fweh_call_event_handler(ifp,emsg->event_code,emsg,data);-if(ifp&&ifevent->action==BRCMF_E_IF_DEL)-brcmf_remove_interface(ifp,false);+if(ifp&&ifevent->action==BRCMF_E_IF_DEL){+boolarmed=brcmf_cfg80211_vif_event_armed(drvr->config);++/* Default handling in case no-one waits for this event */+if(!armed)+brcmf_remove_interface(ifp,false);+}}/**
--
1.8.4.5
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html