Files
android_kernel_xiaomi_sm8450/drivers/infiniband/sw/rxe/rxe_task.c
Bob Pearson 18f62a453b RDMA/rxe: Limit the number of calls to each tasklet
[ Upstream commit eff6d998ca297cb0b2e53b032a56cf8e04dd8b17 ]

Limit the maximum number of calls to each tasklet from rxe_do_task()
before yielding the cpu. When the limit is reached reschedule the tasklet
and exit the calling loop. This patch prevents one tasklet from consuming
100% of a cpu core and causing a deadlock or soft lockup.

Link: https://lore.kernel.org/r/20220630190425.2251-9-rpearsonhpe@gmail.com
Signed-off-by: Bob Pearson <rpearsonhpe@gmail.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
2022-08-25 11:38:16 +02:00

155 lines
3.2 KiB
C

// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2015 System Fabric Works, Inc. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/hardirq.h>
#include "rxe.h"
int __rxe_do_task(struct rxe_task *task)
{
int ret;
while ((ret = task->func(task->arg)) == 0)
;
task->ret = ret;
return ret;
}
/*
* this locking is due to a potential race where
* a second caller finds the task already running
* but looks just after the last call to func
*/
void rxe_do_task(struct tasklet_struct *t)
{
int cont;
int ret;
unsigned long flags;
struct rxe_task *task = from_tasklet(task, t, tasklet);
unsigned int iterations = RXE_MAX_ITERATIONS;
spin_lock_irqsave(&task->state_lock, flags);
switch (task->state) {
case TASK_STATE_START:
task->state = TASK_STATE_BUSY;
spin_unlock_irqrestore(&task->state_lock, flags);
break;
case TASK_STATE_BUSY:
task->state = TASK_STATE_ARMED;
fallthrough;
case TASK_STATE_ARMED:
spin_unlock_irqrestore(&task->state_lock, flags);
return;
default:
spin_unlock_irqrestore(&task->state_lock, flags);
pr_warn("%s failed with bad state %d\n", __func__, task->state);
return;
}
do {
cont = 0;
ret = task->func(task->arg);
spin_lock_irqsave(&task->state_lock, flags);
switch (task->state) {
case TASK_STATE_BUSY:
if (ret) {
task->state = TASK_STATE_START;
} else if (iterations--) {
cont = 1;
} else {
/* reschedule the tasklet and exit
* the loop to give up the cpu
*/
tasklet_schedule(&task->tasklet);
task->state = TASK_STATE_START;
}
break;
/* someone tried to run the task since the last time we called
* func, so we will call one more time regardless of the
* return value
*/
case TASK_STATE_ARMED:
task->state = TASK_STATE_BUSY;
cont = 1;
break;
default:
pr_warn("%s failed with bad state %d\n", __func__,
task->state);
}
spin_unlock_irqrestore(&task->state_lock, flags);
} while (cont);
task->ret = ret;
}
int rxe_init_task(void *obj, struct rxe_task *task,
void *arg, int (*func)(void *), char *name)
{
task->obj = obj;
task->arg = arg;
task->func = func;
snprintf(task->name, sizeof(task->name), "%s", name);
task->destroyed = false;
tasklet_setup(&task->tasklet, rxe_do_task);
task->state = TASK_STATE_START;
spin_lock_init(&task->state_lock);
return 0;
}
void rxe_cleanup_task(struct rxe_task *task)
{
unsigned long flags;
bool idle;
/*
* Mark the task, then wait for it to finish. It might be
* running in a non-tasklet (direct call) context.
*/
task->destroyed = true;
do {
spin_lock_irqsave(&task->state_lock, flags);
idle = (task->state == TASK_STATE_START);
spin_unlock_irqrestore(&task->state_lock, flags);
} while (!idle);
tasklet_kill(&task->tasklet);
}
void rxe_run_task(struct rxe_task *task, int sched)
{
if (task->destroyed)
return;
if (sched)
tasklet_schedule(&task->tasklet);
else
rxe_do_task(&task->tasklet);
}
void rxe_disable_task(struct rxe_task *task)
{
tasklet_disable(&task->tasklet);
}
void rxe_enable_task(struct rxe_task *task)
{
tasklet_enable(&task->tasklet);
}