Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Cristian Marussi | 5201 | 98.39% | 17 | 68.00% |
Sudeep Holla | 64 | 1.21% | 4 | 16.00% |
Sukrut Bellary | 10 | 0.19% | 1 | 4.00% |
Rishabh Bhatnagar | 5 | 0.09% | 1 | 4.00% |
Lukasz Luba | 3 | 0.06% | 1 | 4.00% |
Tejun Heo | 3 | 0.06% | 1 | 4.00% |
Total | 5286 | 25 |
// 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 0; } 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++) { void *xret; struct scmi_raw_queue *q; q = scmi_raw_queue_init(raw); if (IS_ERR(q)) { ret = PTR_ERR(q); goto err_xa; } xret = xa_store(&raw->chans_q, channels[i], q, GFP_KERNEL); if (xa_err(xret)) { dev_err(dev, "Fail to allocate Raw queue 0x%02X\n", channels[i]); ret = xa_err(xret); 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); /* * 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); }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1