~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/powerpc/platforms/powernv/opal-async.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php