Path: blob/master/arch/powerpc/platforms/powernv/opal-async.c
26481 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* PowerNV OPAL asynchronous completion interfaces3*4* Copyright 2013-2017 IBM Corp.5*/67#undef DEBUG89#include <linux/kernel.h>10#include <linux/init.h>11#include <linux/slab.h>12#include <linux/sched.h>13#include <linux/semaphore.h>14#include <linux/spinlock.h>15#include <linux/wait.h>16#include <linux/gfp.h>17#include <linux/of.h>18#include <asm/machdep.h>19#include <asm/opal.h>2021enum opal_async_token_state {22ASYNC_TOKEN_UNALLOCATED = 0,23ASYNC_TOKEN_ALLOCATED,24ASYNC_TOKEN_DISPATCHED,25ASYNC_TOKEN_ABANDONED,26ASYNC_TOKEN_COMPLETED27};2829struct opal_async_token {30enum opal_async_token_state state;31struct opal_msg response;32};3334static DECLARE_WAIT_QUEUE_HEAD(opal_async_wait);35static DEFINE_SPINLOCK(opal_async_comp_lock);36static struct semaphore opal_async_sem;37static unsigned int opal_max_async_tokens;38static struct opal_async_token *opal_async_tokens;3940static int __opal_async_get_token(void)41{42unsigned long flags;43int i, token = -EBUSY;4445spin_lock_irqsave(&opal_async_comp_lock, flags);4647for (i = 0; i < opal_max_async_tokens; i++) {48if (opal_async_tokens[i].state == ASYNC_TOKEN_UNALLOCATED) {49opal_async_tokens[i].state = ASYNC_TOKEN_ALLOCATED;50token = i;51break;52}53}5455spin_unlock_irqrestore(&opal_async_comp_lock, flags);56return token;57}5859/*60* Note: If the returned token is used in an opal call and opal returns61* OPAL_ASYNC_COMPLETION you MUST call one of opal_async_wait_response() or62* opal_async_wait_response_interruptible() at least once before calling another63* opal_async_* function64*/65int opal_async_get_token_interruptible(void)66{67int token;6869/* Wait until a token is available */70if (down_interruptible(&opal_async_sem))71return -ERESTARTSYS;7273token = __opal_async_get_token();74if (token < 0)75up(&opal_async_sem);7677return token;78}79EXPORT_SYMBOL_GPL(opal_async_get_token_interruptible);8081static int __opal_async_release_token(int token)82{83unsigned long flags;84int rc;8586if (token < 0 || token >= opal_max_async_tokens) {87pr_err("%s: Passed token is out of range, token %d\n",88__func__, token);89return -EINVAL;90}9192spin_lock_irqsave(&opal_async_comp_lock, flags);93switch (opal_async_tokens[token].state) {94case ASYNC_TOKEN_COMPLETED:95case ASYNC_TOKEN_ALLOCATED:96opal_async_tokens[token].state = ASYNC_TOKEN_UNALLOCATED;97rc = 0;98break;99/*100* DISPATCHED and ABANDONED tokens must wait for OPAL to respond.101* Mark a DISPATCHED token as ABANDONED so that the response handling102* code knows no one cares and that it can free it then.103*/104case ASYNC_TOKEN_DISPATCHED:105opal_async_tokens[token].state = ASYNC_TOKEN_ABANDONED;106fallthrough;107default:108rc = 1;109}110spin_unlock_irqrestore(&opal_async_comp_lock, flags);111112return rc;113}114115int opal_async_release_token(int token)116{117int ret;118119ret = __opal_async_release_token(token);120if (!ret)121up(&opal_async_sem);122123return ret;124}125EXPORT_SYMBOL_GPL(opal_async_release_token);126127int opal_async_wait_response(uint64_t token, struct opal_msg *msg)128{129if (token >= opal_max_async_tokens) {130pr_err("%s: Invalid token passed\n", __func__);131return -EINVAL;132}133134if (!msg) {135pr_err("%s: Invalid message pointer passed\n", __func__);136return -EINVAL;137}138139/*140* There is no need to mark the token as dispatched, wait_event()141* will block until the token completes.142*143* Wakeup the poller before we wait for events to speed things144* up on platforms or simulators where the interrupts aren't145* functional.146*/147opal_wake_poller();148wait_event(opal_async_wait, opal_async_tokens[token].state149== ASYNC_TOKEN_COMPLETED);150memcpy(msg, &opal_async_tokens[token].response, sizeof(*msg));151152return 0;153}154EXPORT_SYMBOL_GPL(opal_async_wait_response);155156int opal_async_wait_response_interruptible(uint64_t token, struct opal_msg *msg)157{158unsigned long flags;159int ret;160161if (token >= opal_max_async_tokens) {162pr_err("%s: Invalid token passed\n", __func__);163return -EINVAL;164}165166if (!msg) {167pr_err("%s: Invalid message pointer passed\n", __func__);168return -EINVAL;169}170171/*172* The first time this gets called we mark the token as DISPATCHED173* so that if wait_event_interruptible() returns not zero and the174* caller frees the token, we know not to actually free the token175* until the response comes.176*177* Only change if the token is ALLOCATED - it may have been178* completed even before the caller gets around to calling this179* the first time.180*181* There is also a dirty great comment at the token allocation182* function that if the opal call returns OPAL_ASYNC_COMPLETION to183* the caller then the caller *must* call this or the not184* interruptible version before doing anything else with the185* token.186*/187if (opal_async_tokens[token].state == ASYNC_TOKEN_ALLOCATED) {188spin_lock_irqsave(&opal_async_comp_lock, flags);189if (opal_async_tokens[token].state == ASYNC_TOKEN_ALLOCATED)190opal_async_tokens[token].state = ASYNC_TOKEN_DISPATCHED;191spin_unlock_irqrestore(&opal_async_comp_lock, flags);192}193194/*195* Wakeup the poller before we wait for events to speed things196* up on platforms or simulators where the interrupts aren't197* functional.198*/199opal_wake_poller();200ret = wait_event_interruptible(opal_async_wait,201opal_async_tokens[token].state ==202ASYNC_TOKEN_COMPLETED);203if (!ret)204memcpy(msg, &opal_async_tokens[token].response, sizeof(*msg));205206return ret;207}208EXPORT_SYMBOL_GPL(opal_async_wait_response_interruptible);209210/* Called from interrupt context */211static int opal_async_comp_event(struct notifier_block *nb,212unsigned long msg_type, void *msg)213{214struct opal_msg *comp_msg = msg;215enum opal_async_token_state state;216unsigned long flags;217uint64_t token;218219if (msg_type != OPAL_MSG_ASYNC_COMP)220return 0;221222token = be64_to_cpu(comp_msg->params[0]);223spin_lock_irqsave(&opal_async_comp_lock, flags);224state = opal_async_tokens[token].state;225opal_async_tokens[token].state = ASYNC_TOKEN_COMPLETED;226spin_unlock_irqrestore(&opal_async_comp_lock, flags);227228if (state == ASYNC_TOKEN_ABANDONED) {229/* Free the token, no one else will */230opal_async_release_token(token);231return 0;232}233memcpy(&opal_async_tokens[token].response, comp_msg, sizeof(*comp_msg));234wake_up(&opal_async_wait);235236return 0;237}238239static struct notifier_block opal_async_comp_nb = {240.notifier_call = opal_async_comp_event,241.next = NULL,242.priority = 0,243};244245int __init opal_async_comp_init(void)246{247struct device_node *opal_node;248const __be32 *async;249int err;250251opal_node = of_find_node_by_path("/ibm,opal");252if (!opal_node) {253pr_err("%s: Opal node not found\n", __func__);254err = -ENOENT;255goto out;256}257258async = of_get_property(opal_node, "opal-msg-async-num", NULL);259if (!async) {260pr_err("%s: %pOF has no opal-msg-async-num\n",261__func__, opal_node);262err = -ENOENT;263goto out_opal_node;264}265266opal_max_async_tokens = be32_to_cpup(async);267opal_async_tokens = kcalloc(opal_max_async_tokens,268sizeof(*opal_async_tokens), GFP_KERNEL);269if (!opal_async_tokens) {270err = -ENOMEM;271goto out_opal_node;272}273274err = opal_message_notifier_register(OPAL_MSG_ASYNC_COMP,275&opal_async_comp_nb);276if (err) {277pr_err("%s: Can't register OPAL event notifier (%d)\n",278__func__, err);279kfree(opal_async_tokens);280goto out_opal_node;281}282283sema_init(&opal_async_sem, opal_max_async_tokens);284285out_opal_node:286of_node_put(opal_node);287out:288return err;289}290291292