When do_task() exhausts its RXE_MAX_ITERATIONS budget, it unconditionally sets the task state to TASK_STATE_IDLE to reschedule. This overwrites the TASK_STATE_DRAINING state that may have been concurrently set by rxe_cleanup_task() or rxe_disable_task().
This race condition breaks the cleanup and disable logic, which expects the task to stop processing new work. The cleanup code may proceed while do_task() reschedules itself, leading to a potential use-after-free.
This bug was introduced during the migration from tasklets to workqueues, where the special handling for the draining case was lost.
Fix this by restoring the original behavior. If the state is TASK_STATE_DRAINING when iterations are exhausted, continue the loop by setting cont to 1. This allows new iterations to finish the remaining work and reach the switch statement, which properly transitions the state to TASK_STATE_DRAINED and stops the task as intended.
Fixes: 9b4b7c1f9f54 ("RDMA/rxe: Add workqueue support for rxe tasks") Cc: stable@vger.kernel.org Signed-off-by: Gui-Dong Han hanguidong02@gmail.com --- drivers/infiniband/sw/rxe/rxe_task.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/infiniband/sw/rxe/rxe_task.c b/drivers/infiniband/sw/rxe/rxe_task.c index 6f8f353e9583..f522820b950c 100644 --- a/drivers/infiniband/sw/rxe/rxe_task.c +++ b/drivers/infiniband/sw/rxe/rxe_task.c @@ -132,8 +132,12 @@ static void do_task(struct rxe_task *task) * yield the cpu and reschedule the task */ if (!ret) { - task->state = TASK_STATE_IDLE; - resched = 1; + if (task->state != TASK_STATE_DRAINING) { + task->state = TASK_STATE_IDLE; + resched = 1; + } else { + cont = 1; + } goto exit; }