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:
Alok Kumar
2018-08-14 16:55:42 +05:30
committed by Amarnath Hullur Subramanyam
parent 87a8e44583
commit 7f4494ffc5

View File

@@ -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);