1 /* 2 * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved. 3 * Copyright (c) 2015 System Fabric Works, Inc. All rights reserved. 4 * 5 * This software is available to you under a choice of one of two 6 * licenses. You may choose to be licensed under the terms of the GNU 7 * General Public License (GPL) Version 2, available from the file 8 * COPYING in the main directory of this source tree, or the 9 * OpenIB.org BSD license below: 10 * 11 * Redistribution and use in source and binary forms, with or 12 * without modification, are permitted provided that the following 13 * conditions are met: 14 * 15 * - Redistributions of source code must retain the above 16 * copyright notice, this list of conditions and the following 17 * disclaimer. 18 * 19 * - Redistributions in binary form must reproduce the above 20 * copyright notice, this list of conditions and the following 21 * disclaimer in the documentation and/or other materials 22 * provided with the distribution. 23 * 24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 * SOFTWARE. 32 */ 33 34 #include <linux/kernel.h> 35 #include <linux/interrupt.h> 36 #include <linux/hardirq.h> 37 38 #include "rxe_task.h" 39 40 int __rxe_do_task(struct rxe_task *task) 41 42 { 43 int ret; 44 45 while ((ret = task->func(task->arg)) == 0) 46 ; 47 48 task->ret = ret; 49 50 return ret; 51 } 52 53 /* 54 * this locking is due to a potential race where 55 * a second caller finds the task already running 56 * but looks just after the last call to func 57 */ 58 void rxe_do_task(unsigned long data) 59 { 60 int cont; 61 int ret; 62 unsigned long flags; 63 struct rxe_task *task = (struct rxe_task *)data; 64 65 spin_lock_irqsave(&task->state_lock, flags); 66 switch (task->state) { 67 case TASK_STATE_START: 68 task->state = TASK_STATE_BUSY; 69 spin_unlock_irqrestore(&task->state_lock, flags); 70 break; 71 72 case TASK_STATE_BUSY: 73 task->state = TASK_STATE_ARMED; 74 /* fall through to */ 75 case TASK_STATE_ARMED: 76 spin_unlock_irqrestore(&task->state_lock, flags); 77 return; 78 79 default: 80 spin_unlock_irqrestore(&task->state_lock, flags); 81 pr_warn("bad state = %d in rxe_do_task\n", task->state); 82 return; 83 } 84 85 do { 86 cont = 0; 87 ret = task->func(task->arg); 88 89 spin_lock_irqsave(&task->state_lock, flags); 90 switch (task->state) { 91 case TASK_STATE_BUSY: 92 if (ret) 93 task->state = TASK_STATE_START; 94 else 95 cont = 1; 96 break; 97 98 /* soneone tried to run the task since the last time we called 99 * func, so we will call one more time regardless of the 100 * return value 101 */ 102 case TASK_STATE_ARMED: 103 task->state = TASK_STATE_BUSY; 104 cont = 1; 105 break; 106 107 default: 108 pr_warn("bad state = %d in rxe_do_task\n", 109 task->state); 110 } 111 spin_unlock_irqrestore(&task->state_lock, flags); 112 } while (cont); 113 114 task->ret = ret; 115 } 116 117 int rxe_init_task(void *obj, struct rxe_task *task, 118 void *arg, int (*func)(void *), char *name) 119 { 120 task->obj = obj; 121 task->arg = arg; 122 task->func = func; 123 snprintf(task->name, sizeof(task->name), "%s", name); 124 task->destroyed = false; 125 126 tasklet_init(&task->tasklet, rxe_do_task, (unsigned long)task); 127 128 task->state = TASK_STATE_START; 129 spin_lock_init(&task->state_lock); 130 131 return 0; 132 } 133 134 void rxe_cleanup_task(struct rxe_task *task) 135 { 136 unsigned long flags; 137 bool idle; 138 139 /* 140 * Mark the task, then wait for it to finish. It might be 141 * running in a non-tasklet (direct call) context. 142 */ 143 task->destroyed = true; 144 145 do { 146 spin_lock_irqsave(&task->state_lock, flags); 147 idle = (task->state == TASK_STATE_START); 148 spin_unlock_irqrestore(&task->state_lock, flags); 149 } while (!idle); 150 151 tasklet_kill(&task->tasklet); 152 } 153 154 void rxe_run_task(struct rxe_task *task, int sched) 155 { 156 if (task->destroyed) 157 return; 158 159 if (sched) 160 tasklet_schedule(&task->tasklet); 161 else 162 rxe_do_task((unsigned long)task); 163 } 164 165 void rxe_disable_task(struct rxe_task *task) 166 { 167 tasklet_disable(&task->tasklet); 168 } 169 170 void rxe_enable_task(struct rxe_task *task) 171 { 172 tasklet_enable(&task->tasklet); 173 } 174