qcacmn: Avoid race condition between tasklet schedule and kill
Tasklet get stuck when cds_is_driver_recovering and the driver state is unloading. It happens due to race between schedule of tasklet and tasklet_kill. Here one thread is trying to disable & kill the tasklet while another thread is trying to concurrently schedule it. Cancel the tasklet work and make tasklets[i].inited = false before disabling the tasklet in ce_tasklet_kill() and validate if tasket is inited before rescheduling the tasklet. Change-Id: Ib6f4013a73bca8558da097b73535bb8a8f80354d CRs-Fixed: 2290951
This commit is contained in:

committed by
Amarnath Hullur Subramanyam

parent
87a8e44583
commit
7f4494ffc5
@@ -73,6 +73,7 @@ static void reschedule_ce_tasklet_work_handler(struct work_struct *work)
|
|||||||
HIF_ERROR("%s: wlan driver is unloaded", __func__);
|
HIF_ERROR("%s: wlan driver is unloaded", __func__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (hif_ce_state->tasklets[ce_work->id].inited)
|
||||||
tasklet_schedule(&hif_ce_state->tasklets[ce_work->id].intr_tq);
|
tasklet_schedule(&hif_ce_state->tasklets[ce_work->id].intr_tq);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,6 +229,7 @@ void ce_tasklet_init(struct HIF_CE_state *hif_ce_state, uint32_t mask)
|
|||||||
* ce_tasklet_kill() - ce_tasklet_kill
|
* ce_tasklet_kill() - ce_tasklet_kill
|
||||||
* @hif_ce_state: hif_ce_state
|
* @hif_ce_state: hif_ce_state
|
||||||
*
|
*
|
||||||
|
* Context: Non-Atomic context
|
||||||
* Return: N/A
|
* Return: N/A
|
||||||
*/
|
*/
|
||||||
void ce_tasklet_kill(struct hif_softc *scn)
|
void ce_tasklet_kill(struct hif_softc *scn)
|
||||||
@@ -235,10 +237,21 @@ void ce_tasklet_kill(struct hif_softc *scn)
|
|||||||
int i;
|
int i;
|
||||||
struct HIF_CE_state *hif_ce_state = HIF_GET_CE_STATE(scn);
|
struct HIF_CE_state *hif_ce_state = HIF_GET_CE_STATE(scn);
|
||||||
|
|
||||||
|
work_initialized = false;
|
||||||
|
|
||||||
for (i = 0; i < CE_COUNT_MAX; i++)
|
for (i = 0; i < CE_COUNT_MAX; i++)
|
||||||
if (hif_ce_state->tasklets[i].inited) {
|
if (hif_ce_state->tasklets[i].inited) {
|
||||||
tasklet_disable(&hif_ce_state->tasklets[i].intr_tq);
|
|
||||||
hif_ce_state->tasklets[i].inited = false;
|
hif_ce_state->tasklets[i].inited = false;
|
||||||
|
/*
|
||||||
|
* Cancel the tasklet work before tasklet_disable
|
||||||
|
* to avoid race between tasklet_schedule and
|
||||||
|
* tasklet_kill. Here cancel_work_sync() won't
|
||||||
|
* return before reschedule_ce_tasklet_work_handler()
|
||||||
|
* completes. Even if tasklet_schedule() happens
|
||||||
|
* tasklet_disable() will take care of that.
|
||||||
|
*/
|
||||||
|
cancel_work_sync(&tasklet_workers[i].work);
|
||||||
|
tasklet_disable(&hif_ce_state->tasklets[i].intr_tq);
|
||||||
tasklet_kill(&hif_ce_state->tasklets[i].intr_tq);
|
tasklet_kill(&hif_ce_state->tasklets[i].intr_tq);
|
||||||
}
|
}
|
||||||
qdf_atomic_set(&scn->active_tasklet_cnt, 0);
|
qdf_atomic_set(&scn->active_tasklet_cnt, 0);
|
||||||
|
Reference in New Issue
Block a user