Merge branch 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux...
[sfrench/cifs-2.6.git] / drivers / infiniband / sw / rxe / rxe_task.c
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 }