root/arch/powerpc/platforms/powernv/opal-async.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. __opal_async_get_token
  2. opal_async_get_token_interruptible
  3. __opal_async_release_token
  4. opal_async_release_token
  5. opal_async_wait_response
  6. opal_async_wait_response_interruptible
  7. opal_async_comp_event
  8. opal_async_comp_init

   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                 /* Fall through */
 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 }

/* [<][>][^][v][top][bottom][index][help] */