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.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 	struct rxe_task *task = from_tasklet(task, t, tasklet);
36 	unsigned int iterations = RXE_MAX_ITERATIONS;
37 
38 	spin_lock_bh(&task->state_lock);
39 	switch (task->state) {
40 	case TASK_STATE_START:
41 		task->state = TASK_STATE_BUSY;
42 		spin_unlock_bh(&task->state_lock);
43 		break;
44 
45 	case TASK_STATE_BUSY:
46 		task->state = TASK_STATE_ARMED;
47 		fallthrough;
48 	case TASK_STATE_ARMED:
49 		spin_unlock_bh(&task->state_lock);
50 		return;
51 
52 	default:
53 		spin_unlock_bh(&task->state_lock);
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_bh(&task->state_lock);
63 		switch (task->state) {
64 		case TASK_STATE_BUSY:
65 			if (ret) {
66 				task->state = TASK_STATE_START;
67 			} else if (iterations--) {
68 				cont = 1;
69 			} else {
70 				/* reschedule the tasklet and exit
71 				 * the loop to give up the cpu
72 				 */
73 				tasklet_schedule(&task->tasklet);
74 				task->state = TASK_STATE_START;
75 			}
76 			break;
77 
78 		/* someone tried to run the task since the last time we called
79 		 * func, so we will call one more time regardless of the
80 		 * return value
81 		 */
82 		case TASK_STATE_ARMED:
83 			task->state = TASK_STATE_BUSY;
84 			cont = 1;
85 			break;
86 
87 		default:
88 			pr_warn("%s failed with bad state %d\n", __func__,
89 				task->state);
90 		}
91 		spin_unlock_bh(&task->state_lock);
92 	} while (cont);
93 
94 	task->ret = ret;
95 }
96 
97 int rxe_init_task(struct rxe_task *task,
98 		  void *arg, int (*func)(void *), char *name)
99 {
100 	task->arg	= arg;
101 	task->func	= func;
102 	snprintf(task->name, sizeof(task->name), "%s", name);
103 	task->destroyed	= false;
104 
105 	tasklet_setup(&task->tasklet, rxe_do_task);
106 
107 	task->state = TASK_STATE_START;
108 	spin_lock_init(&task->state_lock);
109 
110 	return 0;
111 }
112 
113 void rxe_cleanup_task(struct rxe_task *task)
114 {
115 	bool idle;
116 
117 	/*
118 	 * Mark the task, then wait for it to finish. It might be
119 	 * running in a non-tasklet (direct call) context.
120 	 */
121 	task->destroyed = true;
122 
123 	do {
124 		spin_lock_bh(&task->state_lock);
125 		idle = (task->state == TASK_STATE_START);
126 		spin_unlock_bh(&task->state_lock);
127 	} while (!idle);
128 
129 	tasklet_kill(&task->tasklet);
130 }
131 
132 void rxe_run_task(struct rxe_task *task, int sched)
133 {
134 	if (task->destroyed)
135 		return;
136 
137 	if (sched)
138 		tasklet_schedule(&task->tasklet);
139 	else
140 		rxe_do_task(&task->tasklet);
141 }
142 
143 void rxe_disable_task(struct rxe_task *task)
144 {
145 	tasklet_disable(&task->tasklet);
146 }
147 
148 void rxe_enable_task(struct rxe_task *task)
149 {
150 	tasklet_enable(&task->tasklet);
151 }
152