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
__rxe_do_task(struct rxe_task * task)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 */
rxe_do_task(unsigned long data)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 */
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("%s failed with bad state %d\n", __func__, 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("%s failed with bad state %d\n", __func__,
109 task->state);
110 }
111 spin_unlock_irqrestore(&task->state_lock, flags);
112 } while (cont);
113
114 task->ret = ret;
115 }
116
rxe_init_task(void * obj,struct rxe_task * task,void * arg,int (* func)(void *),char * name)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
rxe_cleanup_task(struct rxe_task * task)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
rxe_run_task(struct rxe_task * task,int sched)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
rxe_disable_task(struct rxe_task * task)165 void rxe_disable_task(struct rxe_task *task)
166 {
167 tasklet_disable(&task->tasklet);
168 }
169
rxe_enable_task(struct rxe_task * task)170 void rxe_enable_task(struct rxe_task *task)
171 {
172 tasklet_enable(&task->tasklet);
173 }
174