1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /*
3  * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
4  * Copyright (c) 2015 System Fabric Works, Inc. All rights reserved.
5  */
6 
7 #include <linux/kernel.h>
8 #include <linux/interrupt.h>
9 #include <linux/hardirq.h>
10 
11 #include "rxe_task.h"
12 
13 int __rxe_do_task(struct rxe_task *task)
14 
15 {
16 	int ret;
17 
18 	while ((ret = task->func(task->arg)) == 0)
19 		;
20 
21 	task->ret = ret;
22 
23 	return ret;
24 }
25 
26 /*
27  * this locking is due to a potential race where
28  * a second caller finds the task already running
29  * but looks just after the last call to func
30  */
31 void rxe_do_task(struct tasklet_struct *t)
32 {
33 	int cont;
34 	int ret;
35 	unsigned long flags;
36 	struct rxe_task *task = from_tasklet(task, t, tasklet);
37 
38 	spin_lock_irqsave(&task->state_lock, flags);
39 	switch (task->state) {
40 	case TASK_STATE_START:
41 		task->state = TASK_STATE_BUSY;
42 		spin_unlock_irqrestore(&task->state_lock, flags);
43 		break;
44 
45 	case TASK_STATE_BUSY:
46 		task->state = TASK_STATE_ARMED;
47 		fallthrough;
48 	case TASK_STATE_ARMED:
49 		spin_unlock_irqrestore(&task->state_lock, flags);
50 		return;
51 
52 	default:
53 		spin_unlock_irqrestore(&task->state_lock, flags);
54 		pr_warn("%s failed with bad state %d\n", __func__, task->state);
55 		return;
56 	}
57 
58 	do {
59 		cont = 0;
60 		ret = task->func(task->arg);
61 
62 		spin_lock_irqsave(&task->state_lock, flags);
63 		switch (task->state) {
64 		case TASK_STATE_BUSY:
65 			if (ret)
66 				task->state = TASK_STATE_START;
67 			else
68 				cont = 1;
69 			break;
70 
71 		/* soneone tried to run the task since the last time we called
72 		 * func, so we will call one more time regardless of the
73 		 * return value
74 		 */
75 		case TASK_STATE_ARMED:
76 			task->state = TASK_STATE_BUSY;
77 			cont = 1;
78 			break;
79 
80 		default:
81 			pr_warn("%s failed with bad state %d\n", __func__,
82 				task->state);
83 		}
84 		spin_unlock_irqrestore(&task->state_lock, flags);
85 	} while (cont);
86 
87 	task->ret = ret;
88 }
89 
90 int rxe_init_task(void *obj, struct rxe_task *task,
91 		  void *arg, int (*func)(void *), char *name)
92 {
93 	task->obj	= obj;
94 	task->arg	= arg;
95 	task->func	= func;
96 	snprintf(task->name, sizeof(task->name), "%s", name);
97 	task->destroyed	= false;
98 
99 	tasklet_setup(&task->tasklet, rxe_do_task);
100 
101 	task->state = TASK_STATE_START;
102 	spin_lock_init(&task->state_lock);
103 
104 	return 0;
105 }
106 
107 void rxe_cleanup_task(struct rxe_task *task)
108 {
109 	unsigned long flags;
110 	bool idle;
111 
112 	/*
113 	 * Mark the task, then wait for it to finish. It might be
114 	 * running in a non-tasklet (direct call) context.
115 	 */
116 	task->destroyed = true;
117 
118 	do {
119 		spin_lock_irqsave(&task->state_lock, flags);
120 		idle = (task->state == TASK_STATE_START);
121 		spin_unlock_irqrestore(&task->state_lock, flags);
122 	} while (!idle);
123 
124 	tasklet_kill(&task->tasklet);
125 }
126 
127 void rxe_run_task(struct rxe_task *task, int sched)
128 {
129 	if (task->destroyed)
130 		return;
131 
132 	if (sched)
133 		tasklet_schedule(&task->tasklet);
134 	else
135 		rxe_do_task(&task->tasklet);
136 }
137 
138 void rxe_disable_task(struct rxe_task *task)
139 {
140 	tasklet_disable(&task->tasklet);
141 }
142 
143 void rxe_enable_task(struct rxe_task *task)
144 {
145 	tasklet_enable(&task->tasklet);
146 }
147