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_task.h" 12 13 int __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 */ 31 void rxe_do_task(struct tasklet_struct *t) 32 { 33 int cont; 34 int ret; 35 struct rxe_task *task = from_tasklet(task, t, tasklet); 36 37 spin_lock_bh(&task->state_lock); 38 switch (task->state) { 39 case TASK_STATE_START: 40 task->state = TASK_STATE_BUSY; 41 spin_unlock_bh(&task->state_lock); 42 break; 43 44 case TASK_STATE_BUSY: 45 task->state = TASK_STATE_ARMED; 46 fallthrough; 47 case TASK_STATE_ARMED: 48 spin_unlock_bh(&task->state_lock); 49 return; 50 51 default: 52 spin_unlock_bh(&task->state_lock); 53 pr_warn("%s failed with bad state %d\n", __func__, task->state); 54 return; 55 } 56 57 do { 58 cont = 0; 59 ret = task->func(task->arg); 60 61 spin_lock_bh(&task->state_lock); 62 switch (task->state) { 63 case TASK_STATE_BUSY: 64 if (ret) 65 task->state = TASK_STATE_START; 66 else 67 cont = 1; 68 break; 69 70 /* soneone tried to run the task since the last time we called 71 * func, so we will call one more time regardless of the 72 * return value 73 */ 74 case TASK_STATE_ARMED: 75 task->state = TASK_STATE_BUSY; 76 cont = 1; 77 break; 78 79 default: 80 pr_warn("%s failed with bad state %d\n", __func__, 81 task->state); 82 } 83 spin_unlock_bh(&task->state_lock); 84 } while (cont); 85 86 task->ret = ret; 87 } 88 89 int rxe_init_task(void *obj, struct rxe_task *task, 90 void *arg, int (*func)(void *), char *name) 91 { 92 task->obj = obj; 93 task->arg = arg; 94 task->func = func; 95 snprintf(task->name, sizeof(task->name), "%s", name); 96 task->destroyed = false; 97 98 tasklet_setup(&task->tasklet, rxe_do_task); 99 100 task->state = TASK_STATE_START; 101 spin_lock_init(&task->state_lock); 102 103 return 0; 104 } 105 106 void rxe_cleanup_task(struct rxe_task *task) 107 { 108 bool idle; 109 110 /* 111 * Mark the task, then wait for it to finish. It might be 112 * running in a non-tasklet (direct call) context. 113 */ 114 task->destroyed = true; 115 116 do { 117 spin_lock_bh(&task->state_lock); 118 idle = (task->state == TASK_STATE_START); 119 spin_unlock_bh(&task->state_lock); 120 } while (!idle); 121 122 tasklet_kill(&task->tasklet); 123 } 124 125 void rxe_run_task(struct rxe_task *task, int sched) 126 { 127 if (task->destroyed) 128 return; 129 130 if (sched) 131 tasklet_schedule(&task->tasklet); 132 else 133 rxe_do_task(&task->tasklet); 134 } 135 136 void rxe_disable_task(struct rxe_task *task) 137 { 138 tasklet_disable(&task->tasklet); 139 } 140 141 void rxe_enable_task(struct rxe_task *task) 142 { 143 tasklet_enable(&task->tasklet); 144 } 145