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 125 tasklet_init(&task->tasklet, rxe_do_task, (unsigned long)task); 126 127 task->state = TASK_STATE_START; 128 spin_lock_init(&task->state_lock); 129 130 return 0; 131 } 132 133 void rxe_cleanup_task(struct rxe_task *task) 134 { 135 tasklet_kill(&task->tasklet); 136 } 137 138 void rxe_run_task(struct rxe_task *task, int sched) 139 { 140 if (sched) 141 tasklet_schedule(&task->tasklet); 142 else 143 rxe_do_task((unsigned long)task); 144 } 145 146 void rxe_disable_task(struct rxe_task *task) 147 { 148 tasklet_disable(&task->tasklet); 149 } 150 151 void rxe_enable_task(struct rxe_task *task) 152 { 153 tasklet_enable(&task->tasklet); 154 } 155