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__);
|
||||
return;
|
||||
}
|
||||
if (hif_ce_state->tasklets[ce_work->id].inited)
|
||||
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
|
||||
* @hif_ce_state: hif_ce_state
|
||||
*
|
||||
* Context: Non-Atomic context
|
||||
* Return: N/A
|
||||
*/
|
||||
void ce_tasklet_kill(struct hif_softc *scn)
|
||||
@@ -235,10 +237,21 @@ void ce_tasklet_kill(struct hif_softc *scn)
|
||||
int i;
|
||||
struct HIF_CE_state *hif_ce_state = HIF_GET_CE_STATE(scn);
|
||||
|
||||
work_initialized = false;
|
||||
|
||||
for (i = 0; i < CE_COUNT_MAX; i++)
|
||||
if (hif_ce_state->tasklets[i].inited) {
|
||||
tasklet_disable(&hif_ce_state->tasklets[i].intr_tq);
|
||||
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);
|
||||
}
|
||||
qdf_atomic_set(&scn->active_tasklet_cnt, 0);
|
||||
|
Reference in New Issue
Block a user