1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2/* 3 * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved. 4 * Copyright (c) 2015 System Fabric Works, Inc. All rights reserved. 5 */ 6 7#include <linux/kernel.h> 8#include <linux/interrupt.h> 9#include <linux/hardirq.h> 10 11#include "rxe.h" 12 13int __rxe_do_task(struct rxe_task *task) 14 15{ 16 int ret; 17 18 while ((ret = task->func(task->arg)) == 0) 19 ; 20 21 task->ret = ret; 22 23 return ret; 24} 25 26/* 27 * this locking is due to a potential race where 28 * a second caller finds the task already running 29 * but looks just after the last call to func 30 */ 31void rxe_do_task(struct tasklet_struct *t) 32{ 33 int cont; 34 int ret; 35 unsigned long flags; 36 struct rxe_task *task = from_tasklet(task, t, tasklet); 37 unsigned int iterations = RXE_MAX_ITERATIONS; 38 39 spin_lock_irqsave(&task->state_lock, flags); 40 switch (task->state) { 41 case TASK_STATE_START: 42 task->state = TASK_STATE_BUSY; 43 spin_unlock_irqrestore(&task->state_lock, flags); 44 break; 45 46 case TASK_STATE_BUSY: 47 task->state = TASK_STATE_ARMED; 48 fallthrough; 49 case TASK_STATE_ARMED: 50 spin_unlock_irqrestore(&task->state_lock, flags); 51 return; 52 53 default: 54 spin_unlock_irqrestore(&task->state_lock, flags); 55 pr_warn("%s failed with bad state %d\n", __func__, task->state); 56 return; 57 } 58 59 do { 60 cont = 0; 61 ret = task->func(task->arg); 62 63 spin_lock_irqsave(&task->state_lock, flags); 64 switch (task->state) { 65 case TASK_STATE_BUSY: 66 if (ret) { 67 task->state = TASK_STATE_START; 68 } else if (iterations--) { 69 cont = 1; 70 } else { 71 /* reschedule the tasklet and exit 72 * the loop to give up the cpu 73 */ 74 tasklet_schedule(&task->tasklet); 75 task->state = TASK_STATE_START; 76 } 77 break; 78 79 /* someone tried to run the task since the last time we called 80 * func, so we will call one more time regardless of the 81 * return value 82 */ 83 case TASK_STATE_ARMED: 84 task->state = TASK_STATE_BUSY; 85 cont = 1; 86 break; 87 88 default: 89 pr_warn("%s failed with bad state %d\n", __func__, 90 task->state); 91 } 92 spin_unlock_irqrestore(&task->state_lock, flags); 93 } while (cont); 94 95 task->ret = ret; 96} 97 98int rxe_init_task(struct rxe_task *task, void *arg, int (*func)(void *)) 99{ 100 task->arg = arg; 101 task->func = func; 102 task->destroyed = false; 103 104 tasklet_setup(&task->tasklet, rxe_do_task); 105 106 task->state = TASK_STATE_START; 107 spin_lock_init(&task->state_lock); 108 109 return 0; 110} 111 112void rxe_cleanup_task(struct rxe_task *task) 113{ 114 unsigned long flags; 115 bool idle; 116 117 /* 118 * Mark the task, then wait for it to finish. It might be 119 * running in a non-tasklet (direct call) context. 120 */ 121 task->destroyed = true; 122 123 do { 124 spin_lock_irqsave(&task->state_lock, flags); 125 idle = (task->state == TASK_STATE_START); 126 spin_unlock_irqrestore(&task->state_lock, flags); 127 } while (!idle); 128 129 tasklet_kill(&task->tasklet); 130} 131 132void rxe_run_task(struct rxe_task *task, int sched) 133{ 134 if (task->destroyed) 135 return; 136 137 if (sched) 138 tasklet_schedule(&task->tasklet); 139 else 140 rxe_do_task(&task->tasklet); 141} 142 143void rxe_disable_task(struct rxe_task *task) 144{ 145 tasklet_disable(&task->tasklet); 146} 147 148void rxe_enable_task(struct rxe_task *task) 149{ 150 tasklet_enable(&task->tasklet); 151} 152