3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2024-12-22 13:02:44 +01:00

manager: Retry the interface setup if we get an EBUSY

Sometimes, at least with brcmfmac, the default interface apparently
takes a moment to get created after the NEW_WIPHY event.  We didn't
really consider this case in the NEW_WIPHY handler and we've got a race
condition.  It fixes the following bug for me:
https://bugs.archlinux.org/task/63912 -- tested by removing and
re-modprobing the brcmfmac module rather than rebooting.

To work around this wait for the NEW_INTERFACE event and then retry the
setup.  We still do the initial attempt directly after NEW_WIPHY to
handle cases like wiphys with no default interfaces and pre-existing
wiphys.
This commit is contained in:
Andrew Zaborowski 2020-01-28 19:29:35 +01:00 committed by Denis Kenzior
parent aec7c0f39c
commit 55f9639ee3

View File

@ -54,6 +54,7 @@ struct wiphy_setup_state {
struct wiphy *wiphy; struct wiphy *wiphy;
unsigned int pending_cmd_count; unsigned int pending_cmd_count;
bool aborted; bool aborted;
bool retry;
/* /*
* Data we may need if the driver does not seem to support interface * Data we may need if the driver does not seem to support interface
@ -127,15 +128,29 @@ static void manager_new_interface_cb(struct l_genl_msg *msg, void *user_data)
struct wiphy_setup_state *state = user_data; struct wiphy_setup_state *state = user_data;
uint8_t addr_buf[6]; uint8_t addr_buf[6];
uint8_t *addr = NULL; uint8_t *addr = NULL;
int error;
l_debug(""); l_debug("");
if (state->aborted) if (state->aborted)
return; return;
if (l_genl_msg_get_error(msg) < 0) { error = l_genl_msg_get_error(msg);
if (error < 0) {
l_error("NEW_INTERFACE failed: %s", l_error("NEW_INTERFACE failed: %s",
strerror(-l_genl_msg_get_error(msg))); strerror(-l_genl_msg_get_error(msg)));
/*
* If we receive an EBUSY most likely the wiphy is still
* initializing, the default interface has not been created
* yet and the wiphy needs some time. Retry when we
* receive a NEW_INTERFACE event.
*/
if (error == -EBUSY) {
state->retry = true;
return;
}
/* /*
* Nothing we can do to use this wiphy since by now we * Nothing we can do to use this wiphy since by now we
* will have successfully deleted any default interface * will have successfully deleted any default interface
@ -158,7 +173,9 @@ static void manager_new_interface_done(void *user_data)
struct wiphy_setup_state *state = user_data; struct wiphy_setup_state *state = user_data;
state->pending_cmd_count--; state->pending_cmd_count--;
wiphy_setup_state_destroy(state);
if (!state->pending_cmd_count && !state->retry)
wiphy_setup_state_destroy(state);
} }
static void manager_create_interfaces(struct wiphy_setup_state *state) static void manager_create_interfaces(struct wiphy_setup_state *state)
@ -218,18 +235,23 @@ static void manager_create_interfaces(struct wiphy_setup_state *state)
state->pending_cmd_count++; state->pending_cmd_count++;
} }
static bool manager_wiphy_check_setup_done(struct wiphy_setup_state *state)
{
if (state->pending_cmd_count || state->retry)
return false;
manager_create_interfaces(state);
return !state->pending_cmd_count && !state->retry;
}
static void manager_setup_cmd_done(void *user_data) static void manager_setup_cmd_done(void *user_data)
{ {
struct wiphy_setup_state *state = user_data; struct wiphy_setup_state *state = user_data;
state->pending_cmd_count--; state->pending_cmd_count--;
if (state->pending_cmd_count) if (manager_wiphy_check_setup_done(state))
return;
manager_create_interfaces(state);
if (!state->pending_cmd_count)
wiphy_setup_state_destroy(state); wiphy_setup_state_destroy(state);
} }
@ -280,7 +302,6 @@ static void manager_get_interface_cb(struct l_genl_msg *msg, void *user_data)
return; return;
} }
if (nl80211_parse_attrs(msg, NL80211_ATTR_IFINDEX, &ifindex, if (nl80211_parse_attrs(msg, NL80211_ATTR_IFINDEX, &ifindex,
NL80211_ATTR_IFNAME, &ifname, NL80211_ATTR_IFNAME, &ifname,
NL80211_ATTR_UNSPEC) < 0) NL80211_ATTR_UNSPEC) < 0)
@ -476,15 +497,10 @@ static bool manager_check_create_interfaces(void *data, void *user_data)
wiphy_create_complete(state->wiphy); wiphy_create_complete(state->wiphy);
if (state->pending_cmd_count) if (!manager_wiphy_check_setup_done(state))
return false;
/* If we are here, then there are no interfaces for this phy */
manager_create_interfaces(state);
if (state->pending_cmd_count)
return false; return false;
/* If we are here, there were no interfaces for this phy */
wiphy_setup_state_free(state); wiphy_setup_state_free(state);
return true; return true;
} }
@ -561,6 +577,7 @@ static void manager_config_notify(struct l_genl_msg *msg, void *user_data)
{ {
uint8_t cmd; uint8_t cmd;
struct netdev *netdev; struct netdev *netdev;
struct wiphy_setup_state *state;
cmd = l_genl_msg_get_command(msg); cmd = l_genl_msg_get_command(msg);
@ -578,11 +595,24 @@ static void manager_config_notify(struct l_genl_msg *msg, void *user_data)
case NL80211_CMD_NEW_INTERFACE: case NL80211_CMD_NEW_INTERFACE:
/* /*
* TODO: Until NEW_WIPHY contains all required information we * Interfaces are normally dumped on the NEW_WIPHY events and
* are stuck always having to do a full dump. This specific * and we have nothing to do here. But check if by any chance
* interface must also be dumped, and this is taken care of * we've queried this wiphy and it was still busy initialising,
* in manager_new_wiphy_event. * in that case retry the setup now that an interface, likely
* the initial default one, has been added.
*/ */
state = manager_find_pending(manager_parse_wiphy_id(msg));
if (state && state->retry) {
state->retry = false;
l_debug("Retrying setup of wiphy %u", state->id);
manager_get_interface_cb(msg, state);
if (manager_wiphy_check_setup_done(state))
wiphy_setup_state_destroy(state);
}
break; break;
case NL80211_CMD_DEL_INTERFACE: case NL80211_CMD_DEL_INTERFACE: