| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * System Control and Management Interface (SCMI) Raw mode support
- *
- * Copyright (C) 2022 ARM Ltd.
- */
- /**
- * DOC: Theory of operation
- *
- * When enabled the SCMI Raw mode support exposes a userspace API which allows
- * to send and receive SCMI commands, replies and notifications from a user
- * application through injection and snooping of bare SCMI messages in binary
- * little-endian format.
- *
- * Such injected SCMI transactions will then be routed through the SCMI core
- * stack towards the SCMI backend server using whatever SCMI transport is
- * currently configured on the system under test.
- *
- * It is meant to help in running any sort of SCMI backend server testing, no
- * matter where the server is placed, as long as it is normally reachable via
- * the transport configured on the system.
- *
- * It is activated by a Kernel configuration option since it is NOT meant to
- * be used in production but only during development and in CI deployments.
- *
- * In order to avoid possible interferences between the SCMI Raw transactions
- * originated from a test-suite and the normal operations of the SCMI drivers,
- * when Raw mode is enabled, by default, all the regular SCMI drivers are
- * inhibited, unless CONFIG_ARM_SCMI_RAW_MODE_SUPPORT_COEX is enabled: in this
- * latter case the regular SCMI stack drivers will be loaded as usual and it is
- * up to the user of this interface to take care of manually inhibiting the
- * regular SCMI drivers in order to avoid interferences during the test runs.
- *
- * The exposed API is as follows.
- *
- * All SCMI Raw entries are rooted under a common top /raw debugfs top directory
- * which in turn is rooted under the corresponding underlying SCMI instance.
- *
- * /sys/kernel/debug/scmi/
- * `-- 0
- * |-- atomic_threshold_us
- * |-- instance_name
- * |-- raw
- * | |-- channels
- * | | |-- 0x10
- * | | | |-- message
- * | | | `-- message_async
- * | | `-- 0x13
- * | | |-- message
- * | | `-- message_async
- * | |-- errors
- * | |-- message
- * | |-- message_async
- * | |-- notification
- * | `-- reset
- * `-- transport
- * |-- is_atomic
- * |-- max_msg_size
- * |-- max_rx_timeout_ms
- * |-- rx_max_msg
- * |-- tx_max_msg
- * `-- type
- *
- * where:
- *
- * - errors: used to read back timed-out and unexpected replies
- * - message*: used to send sync/async commands and read back immediate and
- * delayed reponses (if any)
- * - notification: used to read any notification being emitted by the system
- * (if previously enabled by the user app)
- * - reset: used to flush the queues of messages (of any kind) still pending
- * to be read; this is useful at test-suite start/stop to get
- * rid of any unread messages from the previous run.
- *
- * with the per-channel entries rooted at /channels being present only on a
- * system where multiple transport channels have been configured.
- *
- * Such per-channel entries can be used to explicitly choose a specific channel
- * for SCMI bare message injection, in contrast with the general entries above
- * where, instead, the selection of the proper channel to use is automatically
- * performed based the protocol embedded in the injected message and on how the
- * transport is configured on the system.
- *
- * Note that other common general entries are available under transport/ to let
- * the user applications properly make up their expectations in terms of
- * timeouts and message characteristics.
- *
- * Each write to the message* entries causes one command request to be built
- * and sent while the replies or delayed response are read back from those same
- * entries one message at time (receiving an EOF at each message boundary).
- *
- * The user application running the test is in charge of handling timeouts
- * on replies and properly choosing SCMI sequence numbers for the outgoing
- * requests (using the same sequence number is supported but discouraged).
- *
- * Injection of multiple in-flight requests is supported as long as the user
- * application uses properly distinct sequence numbers for concurrent requests
- * and takes care to properly manage all the related issues about concurrency
- * and command/reply pairing. Keep in mind that, anyway, the real level of
- * parallelism attainable in such scenario is dependent on the characteristics
- * of the underlying transport being used.
- *
- * Since the SCMI core regular stack is partially used to deliver and collect
- * the messages, late replies arrived after timeouts and any other sort of
- * unexpected message can be identified by the SCMI core as usual and they will
- * be reported as messages under "errors" for later analysis.
- */
- #include <linux/bitmap.h>
- #include <linux/debugfs.h>
- #include <linux/delay.h>
- #include <linux/device.h>
- #include <linux/export.h>
- #include <linux/io.h>
- #include <linux/kernel.h>
- #include <linux/fs.h>
- #include <linux/list.h>
- #include <linux/module.h>
- #include <linux/poll.h>
- #include <linux/of.h>
- #include <linux/slab.h>
- #include <linux/xarray.h>
- #include "common.h"
- #include "raw_mode.h"
- #include <trace/events/scmi.h>
- #define SCMI_XFER_RAW_MAX_RETRIES 10
- /**
- * struct scmi_raw_queue - Generic Raw queue descriptor
- *
- * @free_bufs: A freelists listhead used to keep unused raw buffers
- * @free_bufs_lock: Spinlock used to protect access to @free_bufs
- * @msg_q: A listhead to a queue of snooped messages waiting to be read out
- * @msg_q_lock: Spinlock used to protect access to @msg_q
- * @wq: A waitqueue used to wait and poll on related @msg_q
- */
- struct scmi_raw_queue {
- struct list_head free_bufs;
- /* Protect free_bufs[] lists */
- spinlock_t free_bufs_lock;
- struct list_head msg_q;
- /* Protect msg_q[] lists */
- spinlock_t msg_q_lock;
- wait_queue_head_t wq;
- };
- /**
- * struct scmi_raw_mode_info - Structure holding SCMI Raw instance data
- *
- * @id: Sequential Raw instance ID.
- * @handle: Pointer to SCMI entity handle to use
- * @desc: Pointer to the transport descriptor to use
- * @tx_max_msg: Maximum number of concurrent TX in-flight messages
- * @q: An array of Raw queue descriptors
- * @chans_q: An XArray mapping optional additional per-channel queues
- * @free_waiters: Head of freelist for unused waiters
- * @free_mtx: A mutex to protect the waiters freelist
- * @active_waiters: Head of list for currently active and used waiters
- * @active_mtx: A mutex to protect the active waiters list
- * @waiters_work: A work descriptor to be used with the workqueue machinery
- * @wait_wq: A workqueue reference to the created workqueue
- * @dentry: Top debugfs root dentry for SCMI Raw
- * @gid: A group ID used for devres accounting
- *
- * Note that this descriptor is passed back to the core after SCMI Raw is
- * initialized as an opaque handle to use by subsequent SCMI Raw call hooks.
- *
- */
- struct scmi_raw_mode_info {
- unsigned int id;
- const struct scmi_handle *handle;
- const struct scmi_desc *desc;
- int tx_max_msg;
- struct scmi_raw_queue *q[SCMI_RAW_MAX_QUEUE];
- struct xarray chans_q;
- struct list_head free_waiters;
- /* Protect free_waiters list */
- struct mutex free_mtx;
- struct list_head active_waiters;
- /* Protect active_waiters list */
- struct mutex active_mtx;
- struct work_struct waiters_work;
- struct workqueue_struct *wait_wq;
- struct dentry *dentry;
- void *gid;
- };
- /**
- * struct scmi_xfer_raw_waiter - Structure to describe an xfer to be waited for
- *
- * @start_jiffies: The timestamp in jiffies of when this structure was queued.
- * @cinfo: A reference to the channel to use for this transaction
- * @xfer: A reference to the xfer to be waited for
- * @async_response: A completion to be, optionally, used for async waits: it
- * will be setup by @scmi_do_xfer_raw_start, if needed, to be
- * pointed at by xfer->async_done.
- * @node: A list node.
- */
- struct scmi_xfer_raw_waiter {
- unsigned long start_jiffies;
- struct scmi_chan_info *cinfo;
- struct scmi_xfer *xfer;
- struct completion async_response;
- struct list_head node;
- };
- /**
- * struct scmi_raw_buffer - Structure to hold a full SCMI message
- *
- * @max_len: The maximum allowed message size (header included) that can be
- * stored into @msg
- * @msg: A message buffer used to collect a full message grabbed from an xfer.
- * @node: A list node.
- */
- struct scmi_raw_buffer {
- size_t max_len;
- struct scmi_msg msg;
- struct list_head node;
- };
- /**
- * struct scmi_dbg_raw_data - Structure holding data needed by the debugfs
- * layer
- *
- * @chan_id: The preferred channel to use: if zero the channel is automatically
- * selected based on protocol.
- * @raw: A reference to the Raw instance.
- * @tx: A message buffer used to collect TX message on write.
- * @tx_size: The effective size of the TX message.
- * @tx_req_size: The final expected size of the complete TX message.
- * @rx: A message buffer to collect RX message on read.
- * @rx_size: The effective size of the RX message.
- */
- struct scmi_dbg_raw_data {
- u8 chan_id;
- struct scmi_raw_mode_info *raw;
- struct scmi_msg tx;
- size_t tx_size;
- size_t tx_req_size;
- struct scmi_msg rx;
- size_t rx_size;
- };
- static struct scmi_raw_queue *
- scmi_raw_queue_select(struct scmi_raw_mode_info *raw, unsigned int idx,
- unsigned int chan_id)
- {
- if (!chan_id)
- return raw->q[idx];
- return xa_load(&raw->chans_q, chan_id);
- }
- static struct scmi_raw_buffer *scmi_raw_buffer_get(struct scmi_raw_queue *q)
- {
- unsigned long flags;
- struct scmi_raw_buffer *rb = NULL;
- struct list_head *head = &q->free_bufs;
- spin_lock_irqsave(&q->free_bufs_lock, flags);
- if (!list_empty(head)) {
- rb = list_first_entry(head, struct scmi_raw_buffer, node);
- list_del_init(&rb->node);
- }
- spin_unlock_irqrestore(&q->free_bufs_lock, flags);
- return rb;
- }
- static void scmi_raw_buffer_put(struct scmi_raw_queue *q,
- struct scmi_raw_buffer *rb)
- {
- unsigned long flags;
- /* Reset to full buffer length */
- rb->msg.len = rb->max_len;
- spin_lock_irqsave(&q->free_bufs_lock, flags);
- list_add_tail(&rb->node, &q->free_bufs);
- spin_unlock_irqrestore(&q->free_bufs_lock, flags);
- }
- static void scmi_raw_buffer_enqueue(struct scmi_raw_queue *q,
- struct scmi_raw_buffer *rb)
- {
- unsigned long flags;
- spin_lock_irqsave(&q->msg_q_lock, flags);
- list_add_tail(&rb->node, &q->msg_q);
- spin_unlock_irqrestore(&q->msg_q_lock, flags);
- wake_up_interruptible(&q->wq);
- }
- static struct scmi_raw_buffer*
- scmi_raw_buffer_dequeue_unlocked(struct scmi_raw_queue *q)
- {
- struct scmi_raw_buffer *rb = NULL;
- if (!list_empty(&q->msg_q)) {
- rb = list_first_entry(&q->msg_q, struct scmi_raw_buffer, node);
- list_del_init(&rb->node);
- }
- return rb;
- }
- static struct scmi_raw_buffer *scmi_raw_buffer_dequeue(struct scmi_raw_queue *q)
- {
- unsigned long flags;
- struct scmi_raw_buffer *rb;
- spin_lock_irqsave(&q->msg_q_lock, flags);
- rb = scmi_raw_buffer_dequeue_unlocked(q);
- spin_unlock_irqrestore(&q->msg_q_lock, flags);
- return rb;
- }
- static void scmi_raw_buffer_queue_flush(struct scmi_raw_queue *q)
- {
- struct scmi_raw_buffer *rb;
- do {
- rb = scmi_raw_buffer_dequeue(q);
- if (rb)
- scmi_raw_buffer_put(q, rb);
- } while (rb);
- }
- static struct scmi_xfer_raw_waiter *
- scmi_xfer_raw_waiter_get(struct scmi_raw_mode_info *raw, struct scmi_xfer *xfer,
- struct scmi_chan_info *cinfo, bool async)
- {
- struct scmi_xfer_raw_waiter *rw = NULL;
- mutex_lock(&raw->free_mtx);
- if (!list_empty(&raw->free_waiters)) {
- rw = list_first_entry(&raw->free_waiters,
- struct scmi_xfer_raw_waiter, node);
- list_del_init(&rw->node);
- if (async) {
- reinit_completion(&rw->async_response);
- xfer->async_done = &rw->async_response;
- }
- rw->cinfo = cinfo;
- rw->xfer = xfer;
- }
- mutex_unlock(&raw->free_mtx);
- return rw;
- }
- static void scmi_xfer_raw_waiter_put(struct scmi_raw_mode_info *raw,
- struct scmi_xfer_raw_waiter *rw)
- {
- if (rw->xfer) {
- rw->xfer->async_done = NULL;
- rw->xfer = NULL;
- }
- mutex_lock(&raw->free_mtx);
- list_add_tail(&rw->node, &raw->free_waiters);
- mutex_unlock(&raw->free_mtx);
- }
- static void scmi_xfer_raw_waiter_enqueue(struct scmi_raw_mode_info *raw,
- struct scmi_xfer_raw_waiter *rw)
- {
- /* A timestamp for the deferred worker to know how much this has aged */
- rw->start_jiffies = jiffies;
- trace_scmi_xfer_response_wait(rw->xfer->transfer_id, rw->xfer->hdr.id,
- rw->xfer->hdr.protocol_id,
- rw->xfer->hdr.seq,
- raw->desc->max_rx_timeout_ms,
- rw->xfer->hdr.poll_completion);
- mutex_lock(&raw->active_mtx);
- list_add_tail(&rw->node, &raw->active_waiters);
- mutex_unlock(&raw->active_mtx);
- /* kick waiter work */
- queue_work(raw->wait_wq, &raw->waiters_work);
- }
- static struct scmi_xfer_raw_waiter *
- scmi_xfer_raw_waiter_dequeue(struct scmi_raw_mode_info *raw)
- {
- struct scmi_xfer_raw_waiter *rw = NULL;
- mutex_lock(&raw->active_mtx);
- if (!list_empty(&raw->active_waiters)) {
- rw = list_first_entry(&raw->active_waiters,
- struct scmi_xfer_raw_waiter, node);
- list_del_init(&rw->node);
- }
- mutex_unlock(&raw->active_mtx);
- return rw;
- }
- /**
- * scmi_xfer_raw_worker - Work function to wait for Raw xfers completions
- *
- * @work: A reference to the work.
- *
- * In SCMI Raw mode, once a user-provided injected SCMI message is sent, we
- * cannot wait to receive its response (if any) in the context of the injection
- * routines so as not to leave the userspace write syscall, which delivered the
- * SCMI message to send, pending till eventually a reply is received.
- * Userspace should and will poll/wait instead on the read syscalls which will
- * be in charge of reading a received reply (if any).
- *
- * Even though reply messages are collected and reported into the SCMI Raw layer
- * on the RX path, nonetheless we have to properly wait for their completion as
- * usual (and async_completion too if needed) in order to properly release the
- * xfer structure at the end: to do this out of the context of the write/send
- * these waiting jobs are delegated to this deferred worker.
- *
- * Any sent xfer, to be waited for, is timestamped and queued for later
- * consumption by this worker: queue aging is accounted for while choosing a
- * timeout for the completion, BUT we do not really care here if we end up
- * accidentally waiting for a bit too long.
- */
- static void scmi_xfer_raw_worker(struct work_struct *work)
- {
- struct scmi_raw_mode_info *raw;
- struct device *dev;
- unsigned long max_tmo;
- raw = container_of(work, struct scmi_raw_mode_info, waiters_work);
- dev = raw->handle->dev;
- max_tmo = msecs_to_jiffies(raw->desc->max_rx_timeout_ms);
- do {
- int ret = 0;
- unsigned int timeout_ms;
- unsigned long aging;
- struct scmi_xfer *xfer;
- struct scmi_xfer_raw_waiter *rw;
- struct scmi_chan_info *cinfo;
- rw = scmi_xfer_raw_waiter_dequeue(raw);
- if (!rw)
- return;
- cinfo = rw->cinfo;
- xfer = rw->xfer;
- /*
- * Waiters are queued by wait-deadline at the end, so some of
- * them could have been already expired when processed, BUT we
- * have to check the completion status anyway just in case a
- * virtually expired (aged) transaction was indeed completed
- * fine and we'll have to wait for the asynchronous part (if
- * any): for this reason a 1 ms timeout is used for already
- * expired/aged xfers.
- */
- aging = jiffies - rw->start_jiffies;
- timeout_ms = max_tmo > aging ?
- jiffies_to_msecs(max_tmo - aging) : 1;
- ret = scmi_xfer_raw_wait_for_message_response(cinfo, xfer,
- timeout_ms);
- if (!ret && xfer->hdr.status)
- ret = scmi_to_linux_errno(xfer->hdr.status);
- if (raw->desc->ops->mark_txdone)
- raw->desc->ops->mark_txdone(rw->cinfo, ret, xfer);
- trace_scmi_xfer_end(xfer->transfer_id, xfer->hdr.id,
- xfer->hdr.protocol_id, xfer->hdr.seq, ret);
- /* Wait also for an async delayed response if needed */
- if (!ret && xfer->async_done) {
- unsigned long tmo = msecs_to_jiffies(SCMI_MAX_RESPONSE_TIMEOUT);
- if (!wait_for_completion_timeout(xfer->async_done, tmo))
- dev_err(dev,
- "timed out in RAW delayed resp - HDR:%08X\n",
- pack_scmi_header(&xfer->hdr));
- }
- /* Release waiter and xfer */
- scmi_xfer_raw_put(raw->handle, xfer);
- scmi_xfer_raw_waiter_put(raw, rw);
- } while (1);
- }
- static void scmi_xfer_raw_reset(struct scmi_raw_mode_info *raw)
- {
- int i;
- dev_info(raw->handle->dev, "Resetting SCMI Raw stack.\n");
- for (i = 0; i < SCMI_RAW_MAX_QUEUE; i++)
- scmi_raw_buffer_queue_flush(raw->q[i]);
- }
- /**
- * scmi_xfer_raw_get_init - An helper to build a valid xfer from the provided
- * bare SCMI message.
- *
- * @raw: A reference to the Raw instance.
- * @buf: A buffer containing the whole SCMI message to send (including the
- * header) in little-endian binary formmat.
- * @len: Length of the message in @buf.
- * @p: A pointer to return the initialized Raw xfer.
- *
- * After an xfer is picked from the TX pool and filled in with the message
- * content, the xfer is registered as pending with the core in the usual way
- * using the original sequence number provided by the user with the message.
- *
- * Note that, in case the testing user application is NOT using distinct
- * sequence-numbers between successive SCMI messages such registration could
- * fail temporarily if the previous message, using the same sequence number,
- * had still not released; in such a case we just wait and retry.
- *
- * Return: 0 on Success
- */
- static int scmi_xfer_raw_get_init(struct scmi_raw_mode_info *raw, void *buf,
- size_t len, struct scmi_xfer **p)
- {
- u32 msg_hdr;
- size_t tx_size;
- struct scmi_xfer *xfer;
- int ret, retry = SCMI_XFER_RAW_MAX_RETRIES;
- struct device *dev = raw->handle->dev;
- if (!buf || len < sizeof(u32))
- return -EINVAL;
- tx_size = len - sizeof(u32);
- /* Ensure we have sane transfer sizes */
- if (tx_size > raw->desc->max_msg_size)
- return -ERANGE;
- xfer = scmi_xfer_raw_get(raw->handle);
- if (IS_ERR(xfer)) {
- dev_warn(dev, "RAW - Cannot get a free RAW xfer !\n");
- return PTR_ERR(xfer);
- }
- /* Build xfer from the provided SCMI bare LE message */
- msg_hdr = le32_to_cpu(*((__le32 *)buf));
- unpack_scmi_header(msg_hdr, &xfer->hdr);
- xfer->hdr.seq = (u16)MSG_XTRACT_TOKEN(msg_hdr);
- /* Polling not supported */
- xfer->hdr.poll_completion = false;
- xfer->hdr.status = SCMI_SUCCESS;
- xfer->tx.len = tx_size;
- xfer->rx.len = raw->desc->max_msg_size;
- /* Clear the whole TX buffer */
- memset(xfer->tx.buf, 0x00, raw->desc->max_msg_size);
- if (xfer->tx.len)
- memcpy(xfer->tx.buf, (u8 *)buf + sizeof(msg_hdr), xfer->tx.len);
- *p = xfer;
- /*
- * In flight registration can temporarily fail in case of Raw messages
- * if the user injects messages without using monotonically increasing
- * sequence numbers since, in Raw mode, the xfer (and the token) is
- * finally released later by a deferred worker. Just retry for a while.
- */
- do {
- ret = scmi_xfer_raw_inflight_register(raw->handle, xfer);
- if (ret) {
- dev_dbg(dev,
- "...retrying[%d] inflight registration\n",
- retry);
- msleep(raw->desc->max_rx_timeout_ms /
- SCMI_XFER_RAW_MAX_RETRIES);
- }
- } while (ret && --retry);
- if (ret) {
- dev_warn(dev,
- "RAW - Could NOT register xfer %d in-flight HDR:0x%08X\n",
- xfer->hdr.seq, msg_hdr);
- scmi_xfer_raw_put(raw->handle, xfer);
- }
- return ret;
- }
- /**
- * scmi_do_xfer_raw_start - An helper to send a valid raw xfer
- *
- * @raw: A reference to the Raw instance.
- * @xfer: The xfer to send
- * @chan_id: The channel ID to use, if zero the channels is automatically
- * selected based on the protocol used.
- * @async: A flag stating if an asynchronous command is required.
- *
- * This function send a previously built raw xfer using an appropriate channel
- * and queues the related waiting work.
- *
- * Note that we need to know explicitly if the required command is meant to be
- * asynchronous in kind since we have to properly setup the waiter.
- * (and deducing this from the payload is weak and do not scale given there is
- * NOT a common header-flag stating if the command is asynchronous or not)
- *
- * Return: 0 on Success
- */
- static int scmi_do_xfer_raw_start(struct scmi_raw_mode_info *raw,
- struct scmi_xfer *xfer, u8 chan_id,
- bool async)
- {
- int ret;
- struct scmi_chan_info *cinfo;
- struct scmi_xfer_raw_waiter *rw;
- struct device *dev = raw->handle->dev;
- if (!chan_id)
- chan_id = xfer->hdr.protocol_id;
- else
- xfer->flags |= SCMI_XFER_FLAG_CHAN_SET;
- cinfo = scmi_xfer_raw_channel_get(raw->handle, chan_id);
- if (IS_ERR(cinfo))
- return PTR_ERR(cinfo);
- rw = scmi_xfer_raw_waiter_get(raw, xfer, cinfo, async);
- if (!rw) {
- dev_warn(dev, "RAW - Cannot get a free waiter !\n");
- return -ENOMEM;
- }
- /* True ONLY if also supported by transport. */
- if (is_polling_enabled(cinfo, raw->desc))
- xfer->hdr.poll_completion = true;
- reinit_completion(&xfer->done);
- /* Make sure xfer state update is visible before sending */
- smp_store_mb(xfer->state, SCMI_XFER_SENT_OK);
- trace_scmi_xfer_begin(xfer->transfer_id, xfer->hdr.id,
- xfer->hdr.protocol_id, xfer->hdr.seq,
- xfer->hdr.poll_completion);
- ret = raw->desc->ops->send_message(rw->cinfo, xfer);
- if (ret) {
- dev_err(dev, "Failed to send RAW message %d\n", ret);
- scmi_xfer_raw_waiter_put(raw, rw);
- return ret;
- }
- trace_scmi_msg_dump(raw->id, cinfo->id, xfer->hdr.protocol_id,
- xfer->hdr.id, "cmnd", xfer->hdr.seq,
- xfer->hdr.status,
- xfer->tx.buf, xfer->tx.len);
- scmi_xfer_raw_waiter_enqueue(raw, rw);
- return ret;
- }
- /**
- * scmi_raw_message_send - An helper to build and send an SCMI command using
- * the provided SCMI bare message buffer
- *
- * @raw: A reference to the Raw instance.
- * @buf: A buffer containing the whole SCMI message to send (including the
- * header) in little-endian binary format.
- * @len: Length of the message in @buf.
- * @chan_id: The channel ID to use.
- * @async: A flag stating if an asynchronous command is required.
- *
- * Return: 0 on Success
- */
- static int scmi_raw_message_send(struct scmi_raw_mode_info *raw,
- void *buf, size_t len, u8 chan_id, bool async)
- {
- int ret;
- struct scmi_xfer *xfer;
- ret = scmi_xfer_raw_get_init(raw, buf, len, &xfer);
- if (ret)
- return ret;
- ret = scmi_do_xfer_raw_start(raw, xfer, chan_id, async);
- if (ret)
- scmi_xfer_raw_put(raw->handle, xfer);
- return ret;
- }
- static struct scmi_raw_buffer *
- scmi_raw_message_dequeue(struct scmi_raw_queue *q, bool o_nonblock)
- {
- unsigned long flags;
- struct scmi_raw_buffer *rb;
- spin_lock_irqsave(&q->msg_q_lock, flags);
- while (list_empty(&q->msg_q)) {
- spin_unlock_irqrestore(&q->msg_q_lock, flags);
- if (o_nonblock)
- return ERR_PTR(-EAGAIN);
- if (wait_event_interruptible(q->wq, !list_empty(&q->msg_q)))
- return ERR_PTR(-ERESTARTSYS);
- spin_lock_irqsave(&q->msg_q_lock, flags);
- }
- rb = scmi_raw_buffer_dequeue_unlocked(q);
- spin_unlock_irqrestore(&q->msg_q_lock, flags);
- return rb;
- }
- /**
- * scmi_raw_message_receive - An helper to dequeue and report the next
- * available enqueued raw message payload that has been collected.
- *
- * @raw: A reference to the Raw instance.
- * @buf: A buffer to get hold of the whole SCMI message received and represented
- * in little-endian binary format.
- * @len: Length of @buf.
- * @size: The effective size of the message copied into @buf
- * @idx: The index of the queue to pick the next queued message from.
- * @chan_id: The channel ID to use.
- * @o_nonblock: A flag to request a non-blocking message dequeue.
- *
- * Return: 0 on Success
- */
- static int scmi_raw_message_receive(struct scmi_raw_mode_info *raw,
- void *buf, size_t len, size_t *size,
- unsigned int idx, unsigned int chan_id,
- bool o_nonblock)
- {
- int ret = 0;
- struct scmi_raw_buffer *rb;
- struct scmi_raw_queue *q;
- q = scmi_raw_queue_select(raw, idx, chan_id);
- if (!q)
- return -ENODEV;
- rb = scmi_raw_message_dequeue(q, o_nonblock);
- if (IS_ERR(rb)) {
- dev_dbg(raw->handle->dev, "RAW - No message available!\n");
- return PTR_ERR(rb);
- }
- if (rb->msg.len <= len) {
- memcpy(buf, rb->msg.buf, rb->msg.len);
- *size = rb->msg.len;
- } else {
- ret = -ENOSPC;
- }
- scmi_raw_buffer_put(q, rb);
- return ret;
- }
- /* SCMI Raw debugfs helpers */
- static ssize_t scmi_dbg_raw_mode_common_read(struct file *filp,
- char __user *buf,
- size_t count, loff_t *ppos,
- unsigned int idx)
- {
- ssize_t cnt;
- struct scmi_dbg_raw_data *rd = filp->private_data;
- if (!rd->rx_size) {
- int ret;
- ret = scmi_raw_message_receive(rd->raw, rd->rx.buf, rd->rx.len,
- &rd->rx_size, idx, rd->chan_id,
- filp->f_flags & O_NONBLOCK);
- if (ret) {
- rd->rx_size = 0;
- return ret;
- }
- /* Reset any previous filepos change, including writes */
- *ppos = 0;
- } else if (*ppos == rd->rx_size) {
- /* Return EOF once all the message has been read-out */
- rd->rx_size = 0;
- return 0;
- }
- cnt = simple_read_from_buffer(buf, count, ppos,
- rd->rx.buf, rd->rx_size);
- return cnt;
- }
- static ssize_t scmi_dbg_raw_mode_common_write(struct file *filp,
- const char __user *buf,
- size_t count, loff_t *ppos,
- bool async)
- {
- int ret;
- struct scmi_dbg_raw_data *rd = filp->private_data;
- if (count > rd->tx.len - rd->tx_size)
- return -ENOSPC;
- /* On first write attempt @count carries the total full message size. */
- if (!rd->tx_size)
- rd->tx_req_size = count;
- /*
- * Gather a full message, possibly across multiple interrupted wrrtes,
- * before sending it with a single RAW xfer.
- */
- if (rd->tx_size < rd->tx_req_size) {
- ssize_t cnt;
- cnt = simple_write_to_buffer(rd->tx.buf, rd->tx.len, ppos,
- buf, count);
- if (cnt < 0)
- return cnt;
- rd->tx_size += cnt;
- if (cnt < count)
- return cnt;
- }
- ret = scmi_raw_message_send(rd->raw, rd->tx.buf, rd->tx_size,
- rd->chan_id, async);
- /* Reset ppos for next message ... */
- rd->tx_size = 0;
- *ppos = 0;
- return ret ?: count;
- }
- static __poll_t scmi_test_dbg_raw_common_poll(struct file *filp,
- struct poll_table_struct *wait,
- unsigned int idx)
- {
- unsigned long flags;
- struct scmi_dbg_raw_data *rd = filp->private_data;
- struct scmi_raw_queue *q;
- __poll_t mask = 0;
- q = scmi_raw_queue_select(rd->raw, idx, rd->chan_id);
- if (!q)
- return mask;
- poll_wait(filp, &q->wq, wait);
- spin_lock_irqsave(&q->msg_q_lock, flags);
- if (!list_empty(&q->msg_q))
- mask = EPOLLIN | EPOLLRDNORM;
- spin_unlock_irqrestore(&q->msg_q_lock, flags);
- return mask;
- }
- static ssize_t scmi_dbg_raw_mode_message_read(struct file *filp,
- char __user *buf,
- size_t count, loff_t *ppos)
- {
- return scmi_dbg_raw_mode_common_read(filp, buf, count, ppos,
- SCMI_RAW_REPLY_QUEUE);
- }
- static ssize_t scmi_dbg_raw_mode_message_write(struct file *filp,
- const char __user *buf,
- size_t count, loff_t *ppos)
- {
- return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos, false);
- }
- static __poll_t scmi_dbg_raw_mode_message_poll(struct file *filp,
- struct poll_table_struct *wait)
- {
- return scmi_test_dbg_raw_common_poll(filp, wait, SCMI_RAW_REPLY_QUEUE);
- }
- static int scmi_dbg_raw_mode_open(struct inode *inode, struct file *filp)
- {
- u8 id;
- struct scmi_raw_mode_info *raw;
- struct scmi_dbg_raw_data *rd;
- const char *id_str = filp->f_path.dentry->d_parent->d_name.name;
- if (!inode->i_private)
- return -ENODEV;
- raw = inode->i_private;
- rd = kzalloc(sizeof(*rd), GFP_KERNEL);
- if (!rd)
- return -ENOMEM;
- rd->rx.len = raw->desc->max_msg_size + sizeof(u32);
- rd->rx.buf = kzalloc(rd->rx.len, GFP_KERNEL);
- if (!rd->rx.buf) {
- kfree(rd);
- return -ENOMEM;
- }
- rd->tx.len = raw->desc->max_msg_size + sizeof(u32);
- rd->tx.buf = kzalloc(rd->tx.len, GFP_KERNEL);
- if (!rd->tx.buf) {
- kfree(rd->rx.buf);
- kfree(rd);
- return -ENOMEM;
- }
- /* Grab channel ID from debugfs entry naming if any */
- if (!kstrtou8(id_str, 16, &id))
- rd->chan_id = id;
- rd->raw = raw;
- filp->private_data = rd;
- return nonseekable_open(inode, filp);
- }
- static int scmi_dbg_raw_mode_release(struct inode *inode, struct file *filp)
- {
- struct scmi_dbg_raw_data *rd = filp->private_data;
- kfree(rd->rx.buf);
- kfree(rd->tx.buf);
- kfree(rd);
- return 0;
- }
- static ssize_t scmi_dbg_raw_mode_reset_write(struct file *filp,
- const char __user *buf,
- size_t count, loff_t *ppos)
- {
- struct scmi_dbg_raw_data *rd = filp->private_data;
- scmi_xfer_raw_reset(rd->raw);
- return count;
- }
- static const struct file_operations scmi_dbg_raw_mode_reset_fops = {
- .open = scmi_dbg_raw_mode_open,
- .release = scmi_dbg_raw_mode_release,
- .write = scmi_dbg_raw_mode_reset_write,
- .owner = THIS_MODULE,
- };
- static const struct file_operations scmi_dbg_raw_mode_message_fops = {
- .open = scmi_dbg_raw_mode_open,
- .release = scmi_dbg_raw_mode_release,
- .read = scmi_dbg_raw_mode_message_read,
- .write = scmi_dbg_raw_mode_message_write,
- .poll = scmi_dbg_raw_mode_message_poll,
- .owner = THIS_MODULE,
- };
- static ssize_t scmi_dbg_raw_mode_message_async_write(struct file *filp,
- const char __user *buf,
- size_t count, loff_t *ppos)
- {
- return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos, true);
- }
- static const struct file_operations scmi_dbg_raw_mode_message_async_fops = {
- .open = scmi_dbg_raw_mode_open,
- .release = scmi_dbg_raw_mode_release,
- .read = scmi_dbg_raw_mode_message_read,
- .write = scmi_dbg_raw_mode_message_async_write,
- .poll = scmi_dbg_raw_mode_message_poll,
- .owner = THIS_MODULE,
- };
- static ssize_t scmi_test_dbg_raw_mode_notif_read(struct file *filp,
- char __user *buf,
- size_t count, loff_t *ppos)
- {
- return scmi_dbg_raw_mode_common_read(filp, buf, count, ppos,
- SCMI_RAW_NOTIF_QUEUE);
- }
- static __poll_t
- scmi_test_dbg_raw_mode_notif_poll(struct file *filp,
- struct poll_table_struct *wait)
- {
- return scmi_test_dbg_raw_common_poll(filp, wait, SCMI_RAW_NOTIF_QUEUE);
- }
- static const struct file_operations scmi_dbg_raw_mode_notification_fops = {
- .open = scmi_dbg_raw_mode_open,
- .release = scmi_dbg_raw_mode_release,
- .read = scmi_test_dbg_raw_mode_notif_read,
- .poll = scmi_test_dbg_raw_mode_notif_poll,
- .owner = THIS_MODULE,
- };
- static ssize_t scmi_test_dbg_raw_mode_errors_read(struct file *filp,
- char __user *buf,
- size_t count, loff_t *ppos)
- {
- return scmi_dbg_raw_mode_common_read(filp, buf, count, ppos,
- SCMI_RAW_ERRS_QUEUE);
- }
- static __poll_t
- scmi_test_dbg_raw_mode_errors_poll(struct file *filp,
- struct poll_table_struct *wait)
- {
- return scmi_test_dbg_raw_common_poll(filp, wait, SCMI_RAW_ERRS_QUEUE);
- }
- static const struct file_operations scmi_dbg_raw_mode_errors_fops = {
- .open = scmi_dbg_raw_mode_open,
- .release = scmi_dbg_raw_mode_release,
- .read = scmi_test_dbg_raw_mode_errors_read,
- .poll = scmi_test_dbg_raw_mode_errors_poll,
- .owner = THIS_MODULE,
- };
- static struct scmi_raw_queue *
- scmi_raw_queue_init(struct scmi_raw_mode_info *raw)
- {
- int i;
- struct scmi_raw_buffer *rb;
- struct device *dev = raw->handle->dev;
- struct scmi_raw_queue *q;
- q = devm_kzalloc(dev, sizeof(*q), GFP_KERNEL);
- if (!q)
- return ERR_PTR(-ENOMEM);
- rb = devm_kcalloc(dev, raw->tx_max_msg, sizeof(*rb), GFP_KERNEL);
- if (!rb)
- return ERR_PTR(-ENOMEM);
- spin_lock_init(&q->free_bufs_lock);
- INIT_LIST_HEAD(&q->free_bufs);
- for (i = 0; i < raw->tx_max_msg; i++, rb++) {
- rb->max_len = raw->desc->max_msg_size + sizeof(u32);
- rb->msg.buf = devm_kzalloc(dev, rb->max_len, GFP_KERNEL);
- if (!rb->msg.buf)
- return ERR_PTR(-ENOMEM);
- scmi_raw_buffer_put(q, rb);
- }
- spin_lock_init(&q->msg_q_lock);
- INIT_LIST_HEAD(&q->msg_q);
- init_waitqueue_head(&q->wq);
- return q;
- }
- static int scmi_xfer_raw_worker_init(struct scmi_raw_mode_info *raw)
- {
- int i;
- struct scmi_xfer_raw_waiter *rw;
- struct device *dev = raw->handle->dev;
- rw = devm_kcalloc(dev, raw->tx_max_msg, sizeof(*rw), GFP_KERNEL);
- if (!rw)
- return -ENOMEM;
- raw->wait_wq = alloc_workqueue("scmi-raw-wait-wq-%d",
- WQ_UNBOUND | WQ_FREEZABLE |
- WQ_HIGHPRI | WQ_SYSFS, 0, raw->id);
- if (!raw->wait_wq)
- return -ENOMEM;
- mutex_init(&raw->free_mtx);
- INIT_LIST_HEAD(&raw->free_waiters);
- mutex_init(&raw->active_mtx);
- INIT_LIST_HEAD(&raw->active_waiters);
- for (i = 0; i < raw->tx_max_msg; i++, rw++) {
- init_completion(&rw->async_response);
- scmi_xfer_raw_waiter_put(raw, rw);
- }
- INIT_WORK(&raw->waiters_work, scmi_xfer_raw_worker);
- return 0;
- }
- static int scmi_raw_mode_setup(struct scmi_raw_mode_info *raw,
- u8 *channels, int num_chans)
- {
- int ret, idx;
- void *gid;
- struct device *dev = raw->handle->dev;
- gid = devres_open_group(dev, NULL, GFP_KERNEL);
- if (!gid)
- return -ENOMEM;
- for (idx = 0; idx < SCMI_RAW_MAX_QUEUE; idx++) {
- raw->q[idx] = scmi_raw_queue_init(raw);
- if (IS_ERR(raw->q[idx])) {
- ret = PTR_ERR(raw->q[idx]);
- goto err;
- }
- }
- xa_init(&raw->chans_q);
- if (num_chans > 1) {
- int i;
- for (i = 0; i < num_chans; i++) {
- struct scmi_raw_queue *q;
- q = scmi_raw_queue_init(raw);
- if (IS_ERR(q)) {
- ret = PTR_ERR(q);
- goto err_xa;
- }
- ret = xa_insert(&raw->chans_q, channels[i], q,
- GFP_KERNEL);
- if (ret) {
- dev_err(dev,
- "Fail to allocate Raw queue 0x%02X\n",
- channels[i]);
- goto err_xa;
- }
- }
- }
- ret = scmi_xfer_raw_worker_init(raw);
- if (ret)
- goto err_xa;
- devres_close_group(dev, gid);
- raw->gid = gid;
- return 0;
- err_xa:
- xa_destroy(&raw->chans_q);
- err:
- devres_release_group(dev, gid);
- return ret;
- }
- /**
- * scmi_raw_mode_init - Function to initialize the SCMI Raw stack
- *
- * @handle: Pointer to SCMI entity handle
- * @top_dentry: A reference to the top Raw debugfs dentry
- * @instance_id: The ID of the underlying SCMI platform instance represented by
- * this Raw instance
- * @channels: The list of the existing channels
- * @num_chans: The number of entries in @channels
- * @desc: Reference to the transport operations
- * @tx_max_msg: Max number of in-flight messages allowed by the transport
- *
- * This function prepare the SCMI Raw stack and creates the debugfs API.
- *
- * Return: An opaque handle to the Raw instance on Success, an ERR_PTR otherwise
- */
- void *scmi_raw_mode_init(const struct scmi_handle *handle,
- struct dentry *top_dentry, int instance_id,
- u8 *channels, int num_chans,
- const struct scmi_desc *desc, int tx_max_msg)
- {
- int ret;
- struct scmi_raw_mode_info *raw;
- struct device *dev;
- if (!handle || !desc)
- return ERR_PTR(-EINVAL);
- dev = handle->dev;
- raw = devm_kzalloc(dev, sizeof(*raw), GFP_KERNEL);
- if (!raw)
- return ERR_PTR(-ENOMEM);
- raw->handle = handle;
- raw->desc = desc;
- raw->tx_max_msg = tx_max_msg;
- raw->id = instance_id;
- ret = scmi_raw_mode_setup(raw, channels, num_chans);
- if (ret) {
- devm_kfree(dev, raw);
- return ERR_PTR(ret);
- }
- raw->dentry = debugfs_create_dir("raw", top_dentry);
- debugfs_create_file("reset", 0200, raw->dentry, raw,
- &scmi_dbg_raw_mode_reset_fops);
- debugfs_create_file("message", 0600, raw->dentry, raw,
- &scmi_dbg_raw_mode_message_fops);
- debugfs_create_file("message_async", 0600, raw->dentry, raw,
- &scmi_dbg_raw_mode_message_async_fops);
- debugfs_create_file("notification", 0400, raw->dentry, raw,
- &scmi_dbg_raw_mode_notification_fops);
- debugfs_create_file("errors", 0400, raw->dentry, raw,
- &scmi_dbg_raw_mode_errors_fops);
- /*
- * Expose per-channel entries if multiple channels available.
- * Just ignore errors while setting up these interfaces since we
- * have anyway already a working core Raw support.
- */
- if (num_chans > 1) {
- int i;
- struct dentry *top_chans;
- top_chans = debugfs_create_dir("channels", raw->dentry);
- for (i = 0; i < num_chans; i++) {
- char cdir[8];
- struct dentry *chd;
- snprintf(cdir, 8, "0x%02X", channels[i]);
- chd = debugfs_create_dir(cdir, top_chans);
- debugfs_create_file("message", 0600, chd, raw,
- &scmi_dbg_raw_mode_message_fops);
- debugfs_create_file("message_async", 0600, chd, raw,
- &scmi_dbg_raw_mode_message_async_fops);
- }
- }
- dev_info(dev, "SCMI RAW Mode initialized for instance %d\n", raw->id);
- return raw;
- }
- /**
- * scmi_raw_mode_cleanup - Function to cleanup the SCMI Raw stack
- *
- * @r: An opaque handle to an initialized SCMI Raw instance
- */
- void scmi_raw_mode_cleanup(void *r)
- {
- struct scmi_raw_mode_info *raw = r;
- if (!raw)
- return;
- debugfs_remove_recursive(raw->dentry);
- cancel_work_sync(&raw->waiters_work);
- destroy_workqueue(raw->wait_wq);
- xa_destroy(&raw->chans_q);
- }
- static int scmi_xfer_raw_collect(void *msg, size_t *msg_len,
- struct scmi_xfer *xfer)
- {
- __le32 *m;
- size_t msg_size;
- if (!xfer || !msg || !msg_len)
- return -EINVAL;
- /* Account for hdr ...*/
- msg_size = xfer->rx.len + sizeof(u32);
- /* ... and status if needed */
- if (xfer->hdr.type != MSG_TYPE_NOTIFICATION)
- msg_size += sizeof(u32);
- if (msg_size > *msg_len)
- return -ENOSPC;
- m = msg;
- *m = cpu_to_le32(pack_scmi_header(&xfer->hdr));
- if (xfer->hdr.type != MSG_TYPE_NOTIFICATION)
- *++m = cpu_to_le32(xfer->hdr.status);
- memcpy(++m, xfer->rx.buf, xfer->rx.len);
- *msg_len = msg_size;
- return 0;
- }
- /**
- * scmi_raw_message_report - Helper to report back valid reponses/notifications
- * to raw message requests.
- *
- * @r: An opaque reference to the raw instance configuration
- * @xfer: The xfer containing the message to be reported
- * @idx: The index of the queue.
- * @chan_id: The channel ID to use.
- *
- * If Raw mode is enabled, this is called from the SCMI core on the regular RX
- * path to save and enqueue the response/notification payload carried by this
- * xfer into a dedicated scmi_raw_buffer for later consumption by the user.
- *
- * This way the caller can free the related xfer immediately afterwards and the
- * user can read back the raw message payload at its own pace (if ever) without
- * holding an xfer for too long.
- */
- void scmi_raw_message_report(void *r, struct scmi_xfer *xfer,
- unsigned int idx, unsigned int chan_id)
- {
- int ret;
- unsigned long flags;
- struct scmi_raw_buffer *rb;
- struct device *dev;
- struct scmi_raw_queue *q;
- struct scmi_raw_mode_info *raw = r;
- if (!raw || (idx == SCMI_RAW_REPLY_QUEUE && !SCMI_XFER_IS_RAW(xfer)))
- return;
- dev = raw->handle->dev;
- q = scmi_raw_queue_select(raw, idx,
- SCMI_XFER_IS_CHAN_SET(xfer) ? chan_id : 0);
- if (!q) {
- dev_warn(dev,
- "RAW[%d] - NO queue for chan 0x%X. Dropping report.\n",
- idx, chan_id);
- return;
- }
- /*
- * Grab the msg_q_lock upfront to avoid a possible race between
- * realizing the free list was empty and effectively picking the next
- * buffer to use from the oldest one enqueued and still unread on this
- * msg_q.
- *
- * Note that nowhere else these locks are taken together, so no risk of
- * deadlocks du eto inversion.
- */
- spin_lock_irqsave(&q->msg_q_lock, flags);
- rb = scmi_raw_buffer_get(q);
- if (!rb) {
- /*
- * Immediate and delayed replies to previously injected Raw
- * commands MUST be read back from userspace to free the buffers:
- * if this is not happening something is seriously broken and
- * must be fixed at the application level: complain loudly.
- */
- if (idx == SCMI_RAW_REPLY_QUEUE) {
- spin_unlock_irqrestore(&q->msg_q_lock, flags);
- dev_warn(dev,
- "RAW[%d] - Buffers exhausted. Dropping report.\n",
- idx);
- return;
- }
- /*
- * Notifications and errors queues are instead handled in a
- * circular manner: unread old buffers are just overwritten by
- * newer ones.
- *
- * The main reason for this is that notifications originated
- * by Raw requests cannot be distinguished from normal ones, so
- * your Raw buffers queues risk to be flooded and depleted by
- * notifications if you left it mistakenly enabled or when in
- * coexistence mode.
- */
- rb = scmi_raw_buffer_dequeue_unlocked(q);
- if (WARN_ON(!rb)) {
- spin_unlock_irqrestore(&q->msg_q_lock, flags);
- return;
- }
- /* Reset to full buffer length */
- rb->msg.len = rb->max_len;
- dev_warn_once(dev,
- "RAW[%d] - Buffers exhausted. Re-using oldest.\n",
- idx);
- }
- spin_unlock_irqrestore(&q->msg_q_lock, flags);
- ret = scmi_xfer_raw_collect(rb->msg.buf, &rb->msg.len, xfer);
- if (ret) {
- dev_warn(dev, "RAW - Cannot collect xfer into buffer !\n");
- scmi_raw_buffer_put(q, rb);
- return;
- }
- scmi_raw_buffer_enqueue(q, rb);
- }
- static void scmi_xfer_raw_fill(struct scmi_raw_mode_info *raw,
- struct scmi_chan_info *cinfo,
- struct scmi_xfer *xfer, u32 msg_hdr)
- {
- /* Unpack received HDR as it is */
- unpack_scmi_header(msg_hdr, &xfer->hdr);
- xfer->hdr.seq = MSG_XTRACT_TOKEN(msg_hdr);
- memset(xfer->rx.buf, 0x00, xfer->rx.len);
- raw->desc->ops->fetch_response(cinfo, xfer);
- }
- /**
- * scmi_raw_error_report - Helper to report back timed-out or generally
- * unexpected replies.
- *
- * @r: An opaque reference to the raw instance configuration
- * @cinfo: A reference to the channel to use to retrieve the broken xfer
- * @msg_hdr: The SCMI message header of the message to fetch and report
- * @priv: Any private data related to the xfer.
- *
- * If Raw mode is enabled, this is called from the SCMI core on the RX path in
- * case of errors to save and enqueue the bad message payload carried by the
- * message that has just been received.
- *
- * Note that we have to manually fetch any available payload into a temporary
- * xfer to be able to save and enqueue the message, since the regular RX error
- * path which had called this would have not fetched the message payload having
- * classified it as an error.
- */
- void scmi_raw_error_report(void *r, struct scmi_chan_info *cinfo,
- u32 msg_hdr, void *priv)
- {
- struct scmi_xfer xfer;
- struct scmi_raw_mode_info *raw = r;
- if (!raw)
- return;
- xfer.rx.len = raw->desc->max_msg_size;
- xfer.rx.buf = kzalloc(xfer.rx.len, GFP_ATOMIC);
- if (!xfer.rx.buf) {
- dev_info(raw->handle->dev,
- "Cannot report Raw error for HDR:0x%X - ENOMEM\n",
- msg_hdr);
- return;
- }
- /* Any transport-provided priv must be passed back down to transport */
- if (priv)
- /* Ensure priv is visible */
- smp_store_mb(xfer.priv, priv);
- scmi_xfer_raw_fill(raw, cinfo, &xfer, msg_hdr);
- scmi_raw_message_report(raw, &xfer, SCMI_RAW_ERRS_QUEUE, 0);
- kfree(xfer.rx.buf);
- }
|