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:
parent
aec7c0f39c
commit
55f9639ee3
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user