Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/platforms/powernv/opal-async.c
26481 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* PowerNV OPAL asynchronous completion interfaces
4
*
5
* Copyright 2013-2017 IBM Corp.
6
*/
7
8
#undef DEBUG
9
10
#include <linux/kernel.h>
11
#include <linux/init.h>
12
#include <linux/slab.h>
13
#include <linux/sched.h>
14
#include <linux/semaphore.h>
15
#include <linux/spinlock.h>
16
#include <linux/wait.h>
17
#include <linux/gfp.h>
18
#include <linux/of.h>
19
#include <asm/machdep.h>
20
#include <asm/opal.h>
21
22
enum opal_async_token_state {
23
ASYNC_TOKEN_UNALLOCATED = 0,
24
ASYNC_TOKEN_ALLOCATED,
25
ASYNC_TOKEN_DISPATCHED,
26
ASYNC_TOKEN_ABANDONED,
27
ASYNC_TOKEN_COMPLETED
28
};
29
30
struct opal_async_token {
31
enum opal_async_token_state state;
32
struct opal_msg response;
33
};
34
35
static DECLARE_WAIT_QUEUE_HEAD(opal_async_wait);
36
static DEFINE_SPINLOCK(opal_async_comp_lock);
37
static struct semaphore opal_async_sem;
38
static unsigned int opal_max_async_tokens;
39
static struct opal_async_token *opal_async_tokens;
40
41
static int __opal_async_get_token(void)
42
{
43
unsigned long flags;
44
int i, token = -EBUSY;
45
46
spin_lock_irqsave(&opal_async_comp_lock, flags);
47
48
for (i = 0; i < opal_max_async_tokens; i++) {
49
if (opal_async_tokens[i].state == ASYNC_TOKEN_UNALLOCATED) {
50
opal_async_tokens[i].state = ASYNC_TOKEN_ALLOCATED;
51
token = i;
52
break;
53
}
54
}
55
56
spin_unlock_irqrestore(&opal_async_comp_lock, flags);
57
return token;
58
}
59
60
/*
61
* Note: If the returned token is used in an opal call and opal returns
62
* OPAL_ASYNC_COMPLETION you MUST call one of opal_async_wait_response() or
63
* opal_async_wait_response_interruptible() at least once before calling another
64
* opal_async_* function
65
*/
66
int opal_async_get_token_interruptible(void)
67
{
68
int token;
69
70
/* Wait until a token is available */
71
if (down_interruptible(&opal_async_sem))
72
return -ERESTARTSYS;
73
74
token = __opal_async_get_token();
75
if (token < 0)
76
up(&opal_async_sem);
77
78
return token;
79
}
80
EXPORT_SYMBOL_GPL(opal_async_get_token_interruptible);
81
82
static int __opal_async_release_token(int token)
83
{
84
unsigned long flags;
85
int rc;
86
87
if (token < 0 || token >= opal_max_async_tokens) {
88
pr_err("%s: Passed token is out of range, token %d\n",
89
__func__, token);
90
return -EINVAL;
91
}
92
93
spin_lock_irqsave(&opal_async_comp_lock, flags);
94
switch (opal_async_tokens[token].state) {
95
case ASYNC_TOKEN_COMPLETED:
96
case ASYNC_TOKEN_ALLOCATED:
97
opal_async_tokens[token].state = ASYNC_TOKEN_UNALLOCATED;
98
rc = 0;
99
break;
100
/*
101
* DISPATCHED and ABANDONED tokens must wait for OPAL to respond.
102
* Mark a DISPATCHED token as ABANDONED so that the response handling
103
* code knows no one cares and that it can free it then.
104
*/
105
case ASYNC_TOKEN_DISPATCHED:
106
opal_async_tokens[token].state = ASYNC_TOKEN_ABANDONED;
107
fallthrough;
108
default:
109
rc = 1;
110
}
111
spin_unlock_irqrestore(&opal_async_comp_lock, flags);
112
113
return rc;
114
}
115
116
int opal_async_release_token(int token)
117
{
118
int ret;
119
120
ret = __opal_async_release_token(token);
121
if (!ret)
122
up(&opal_async_sem);
123
124
return ret;
125
}
126
EXPORT_SYMBOL_GPL(opal_async_release_token);
127
128
int opal_async_wait_response(uint64_t token, struct opal_msg *msg)
129
{
130
if (token >= opal_max_async_tokens) {
131
pr_err("%s: Invalid token passed\n", __func__);
132
return -EINVAL;
133
}
134
135
if (!msg) {
136
pr_err("%s: Invalid message pointer passed\n", __func__);
137
return -EINVAL;
138
}
139
140
/*
141
* There is no need to mark the token as dispatched, wait_event()
142
* will block until the token completes.
143
*
144
* Wakeup the poller before we wait for events to speed things
145
* up on platforms or simulators where the interrupts aren't
146
* functional.
147
*/
148
opal_wake_poller();
149
wait_event(opal_async_wait, opal_async_tokens[token].state
150
== ASYNC_TOKEN_COMPLETED);
151
memcpy(msg, &opal_async_tokens[token].response, sizeof(*msg));
152
153
return 0;
154
}
155
EXPORT_SYMBOL_GPL(opal_async_wait_response);
156
157
int opal_async_wait_response_interruptible(uint64_t token, struct opal_msg *msg)
158
{
159
unsigned long flags;
160
int ret;
161
162
if (token >= opal_max_async_tokens) {
163
pr_err("%s: Invalid token passed\n", __func__);
164
return -EINVAL;
165
}
166
167
if (!msg) {
168
pr_err("%s: Invalid message pointer passed\n", __func__);
169
return -EINVAL;
170
}
171
172
/*
173
* The first time this gets called we mark the token as DISPATCHED
174
* so that if wait_event_interruptible() returns not zero and the
175
* caller frees the token, we know not to actually free the token
176
* until the response comes.
177
*
178
* Only change if the token is ALLOCATED - it may have been
179
* completed even before the caller gets around to calling this
180
* the first time.
181
*
182
* There is also a dirty great comment at the token allocation
183
* function that if the opal call returns OPAL_ASYNC_COMPLETION to
184
* the caller then the caller *must* call this or the not
185
* interruptible version before doing anything else with the
186
* token.
187
*/
188
if (opal_async_tokens[token].state == ASYNC_TOKEN_ALLOCATED) {
189
spin_lock_irqsave(&opal_async_comp_lock, flags);
190
if (opal_async_tokens[token].state == ASYNC_TOKEN_ALLOCATED)
191
opal_async_tokens[token].state = ASYNC_TOKEN_DISPATCHED;
192
spin_unlock_irqrestore(&opal_async_comp_lock, flags);
193
}
194
195
/*
196
* Wakeup the poller before we wait for events to speed things
197
* up on platforms or simulators where the interrupts aren't
198
* functional.
199
*/
200
opal_wake_poller();
201
ret = wait_event_interruptible(opal_async_wait,
202
opal_async_tokens[token].state ==
203
ASYNC_TOKEN_COMPLETED);
204
if (!ret)
205
memcpy(msg, &opal_async_tokens[token].response, sizeof(*msg));
206
207
return ret;
208
}
209
EXPORT_SYMBOL_GPL(opal_async_wait_response_interruptible);
210
211
/* Called from interrupt context */
212
static int opal_async_comp_event(struct notifier_block *nb,
213
unsigned long msg_type, void *msg)
214
{
215
struct opal_msg *comp_msg = msg;
216
enum opal_async_token_state state;
217
unsigned long flags;
218
uint64_t token;
219
220
if (msg_type != OPAL_MSG_ASYNC_COMP)
221
return 0;
222
223
token = be64_to_cpu(comp_msg->params[0]);
224
spin_lock_irqsave(&opal_async_comp_lock, flags);
225
state = opal_async_tokens[token].state;
226
opal_async_tokens[token].state = ASYNC_TOKEN_COMPLETED;
227
spin_unlock_irqrestore(&opal_async_comp_lock, flags);
228
229
if (state == ASYNC_TOKEN_ABANDONED) {
230
/* Free the token, no one else will */
231
opal_async_release_token(token);
232
return 0;
233
}
234
memcpy(&opal_async_tokens[token].response, comp_msg, sizeof(*comp_msg));
235
wake_up(&opal_async_wait);
236
237
return 0;
238
}
239
240
static struct notifier_block opal_async_comp_nb = {
241
.notifier_call = opal_async_comp_event,
242
.next = NULL,
243
.priority = 0,
244
};
245
246
int __init opal_async_comp_init(void)
247
{
248
struct device_node *opal_node;
249
const __be32 *async;
250
int err;
251
252
opal_node = of_find_node_by_path("/ibm,opal");
253
if (!opal_node) {
254
pr_err("%s: Opal node not found\n", __func__);
255
err = -ENOENT;
256
goto out;
257
}
258
259
async = of_get_property(opal_node, "opal-msg-async-num", NULL);
260
if (!async) {
261
pr_err("%s: %pOF has no opal-msg-async-num\n",
262
__func__, opal_node);
263
err = -ENOENT;
264
goto out_opal_node;
265
}
266
267
opal_max_async_tokens = be32_to_cpup(async);
268
opal_async_tokens = kcalloc(opal_max_async_tokens,
269
sizeof(*opal_async_tokens), GFP_KERNEL);
270
if (!opal_async_tokens) {
271
err = -ENOMEM;
272
goto out_opal_node;
273
}
274
275
err = opal_message_notifier_register(OPAL_MSG_ASYNC_COMP,
276
&opal_async_comp_nb);
277
if (err) {
278
pr_err("%s: Can't register OPAL event notifier (%d)\n",
279
__func__, err);
280
kfree(opal_async_tokens);
281
goto out_opal_node;
282
}
283
284
sema_init(&opal_async_sem, opal_max_async_tokens);
285
286
out_opal_node:
287
of_node_put(opal_node);
288
out:
289
return err;
290
}
291
292