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(struct rxe_task * task,void * arg,int (* func)(void *))117 int rxe_init_task(struct rxe_task *task, void *arg, int (*func)(void *))
118 {
119 task->arg = arg;
120 task->func = func;
121 task->destroyed = false;
122
123 tasklet_init(&task->tasklet, rxe_do_task, (unsigned long)task);
124
125 task->state = TASK_STATE_START;
126 spin_lock_init(&task->state_lock);
127
128 return 0;
129 }
130
rxe_cleanup_task(struct rxe_task * task)131 void rxe_cleanup_task(struct rxe_task *task)
132 {
133 unsigned long flags;
134 bool idle;
135
136 /*
137 * Mark the task, then wait for it to finish. It might be
138 * running in a non-tasklet (direct call) context.
139 */
140 task->destroyed = true;
141
142 do {
143 spin_lock_irqsave(&task->state_lock, flags);
144 idle = (task->state == TASK_STATE_START);
145 spin_unlock_irqrestore(&task->state_lock, flags);
146 } while (!idle);
147
148 tasklet_kill(&task->tasklet);
149 }
150
rxe_run_task(struct rxe_task * task,int sched)151 void rxe_run_task(struct rxe_task *task, int sched)
152 {
153 if (task->destroyed)
154 return;
155
156 if (sched)
157 tasklet_schedule(&task->tasklet);
158 else
159 rxe_do_task((unsigned long)task);
160 }
161
rxe_disable_task(struct rxe_task * task)162 void rxe_disable_task(struct rxe_task *task)
163 {
164 tasklet_disable(&task->tasklet);
165 }
166
rxe_enable_task(struct rxe_task * task)167 void rxe_enable_task(struct rxe_task *task)
168 {
169 tasklet_enable(&task->tasklet);
170 }
171