P2P: Recover from successfully requested, but not started, listen
It is possible for the start_listen() callback to be called to request the driver to start a driver operation and stop_listen() called immediately after that (e.g., due to a request to transmit a P2P Public Action frame) before the driver has had time to start ROC and send an event to notify of that. Such a sequence could result in p2p->pending_listen_freq being left to a nonzero value without getting a call to p2p_listen_cb() to clear it. This would stop an ongoing P2P listen operation since no following p2p_listen() call would start the listen due to the pending command being assumed to be in effect. Fix this by detecting this particular sequence and clearing p2p->pending_listen_freq. This was found with the p2p_listen_and_offchannel_tx test case with the new kernel scheduled and UML time-travel. Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
parent
97f7f9ce9b
commit
8e294c3a2c
2 changed files with 23 additions and 0 deletions
|
@ -300,6 +300,8 @@ static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc)
|
|||
ies) < 0) {
|
||||
p2p_dbg(p2p, "Failed to start listen mode");
|
||||
p2p->pending_listen_freq = 0;
|
||||
} else {
|
||||
p2p->pending_listen_wait_drv = true;
|
||||
}
|
||||
wpabuf_free(ies);
|
||||
}
|
||||
|
@ -349,6 +351,7 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout)
|
|||
wpabuf_free(ies);
|
||||
return -1;
|
||||
}
|
||||
p2p->pending_listen_wait_drv = true;
|
||||
wpabuf_free(ies);
|
||||
|
||||
p2p_set_state(p2p, P2P_LISTEN_ONLY);
|
||||
|
@ -1027,6 +1030,7 @@ static void p2p_search(struct p2p_data *p2p)
|
|||
return;
|
||||
}
|
||||
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
|
||||
p2p->pending_listen_wait_drv = false;
|
||||
|
||||
if (p2p->find_pending_full &&
|
||||
(p2p->find_type == P2P_FIND_PROGRESSIVE ||
|
||||
|
@ -1244,6 +1248,7 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
|
|||
p2p->pending_listen_freq = 0;
|
||||
}
|
||||
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
|
||||
p2p->pending_listen_wait_drv = false;
|
||||
p2p->find_pending_full = 0;
|
||||
p2p->find_type = type;
|
||||
if (freq != 2412 && freq != 2437 && freq != 2462 && freq != 60480)
|
||||
|
@ -1338,6 +1343,10 @@ void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq)
|
|||
|
||||
void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq)
|
||||
{
|
||||
p2p_dbg(p2p,
|
||||
"%s(freq=%d) pending_listen_freq=%d in_listen=%d drv_in_listen=%d",
|
||||
__func__, freq, p2p->pending_listen_freq, p2p->in_listen,
|
||||
p2p->drv_in_listen);
|
||||
if (freq > 0 &&
|
||||
((p2p->drv_in_listen == freq && p2p->in_listen) ||
|
||||
p2p->pending_listen_freq == (unsigned int) freq)) {
|
||||
|
@ -1357,7 +1366,15 @@ void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq)
|
|||
p2p_dbg(p2p, "Clear drv_in_listen (%d)", p2p->drv_in_listen);
|
||||
p2p->drv_in_listen = 0;
|
||||
}
|
||||
if (p2p->pending_listen_freq &&
|
||||
p2p->pending_listen_freq != (unsigned int) freq &&
|
||||
!p2p->drv_in_listen && p2p->pending_listen_wait_drv) {
|
||||
p2p_dbg(p2p,
|
||||
"Clear pending_listen_freq since the started listen did not complete before being stopped");
|
||||
p2p->pending_listen_freq = 0;
|
||||
}
|
||||
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
|
||||
p2p->pending_listen_wait_drv = false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2021,6 +2038,7 @@ static void p2p_go_neg_start(void *eloop_ctx, void *timeout_ctx)
|
|||
p2p->pending_listen_freq = 0;
|
||||
}
|
||||
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
|
||||
p2p->pending_listen_wait_drv = false;
|
||||
p2p->go_neg_peer->status = P2P_SC_SUCCESS;
|
||||
/*
|
||||
* Set new timeout to make sure a previously set one does not expire
|
||||
|
@ -2041,6 +2059,7 @@ static void p2p_invite_start(void *eloop_ctx, void *timeout_ctx)
|
|||
p2p->pending_listen_freq = 0;
|
||||
}
|
||||
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
|
||||
p2p->pending_listen_wait_drv = false;
|
||||
p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr,
|
||||
p2p->invite_dev_pw_id);
|
||||
}
|
||||
|
@ -3865,6 +3884,7 @@ void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq,
|
|||
p2p_dbg(p2p, "Starting Listen timeout(%u,%u) on freq=%u based on callback",
|
||||
p2p->pending_listen_sec, p2p->pending_listen_usec,
|
||||
p2p->pending_listen_freq);
|
||||
p2p->pending_listen_wait_drv = false;
|
||||
p2p->in_listen = 1;
|
||||
p2p->drv_in_listen = freq;
|
||||
if (p2p->pending_listen_sec || p2p->pending_listen_usec) {
|
||||
|
@ -4020,6 +4040,7 @@ static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p)
|
|||
|
||||
p2p_dbg(p2p, "Go to Listen state while waiting for the peer to become ready for GO Negotiation");
|
||||
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
|
||||
p2p->pending_listen_wait_drv = false;
|
||||
if (p2p->pending_listen_freq) {
|
||||
p2p_dbg(p2p, "Clear pending_listen_freq for %s", __func__);
|
||||
p2p->pending_listen_freq = 0;
|
||||
|
@ -4147,6 +4168,7 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx)
|
|||
if (p2p->drv_in_listen) {
|
||||
p2p_dbg(p2p, "Driver is still in listen state - stop it");
|
||||
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
|
||||
p2p->pending_listen_wait_drv = false;
|
||||
}
|
||||
|
||||
switch (p2p->state) {
|
||||
|
|
|
@ -389,6 +389,7 @@ struct p2p_data {
|
|||
unsigned int pending_listen_freq;
|
||||
unsigned int pending_listen_sec;
|
||||
unsigned int pending_listen_usec;
|
||||
bool pending_listen_wait_drv;
|
||||
|
||||
u8 dev_capab;
|
||||
|
||||
|
|
Loading…
Reference in a new issue