diff options
| author | Mark Brown <broonie@kernel.org> | 2026-09-07 14:47:56 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-09-07 14:47:56 +0100 |
| commit | 34e4cd373019c46645baa12b2ea98c0565757e52 (patch) | |
| tree | b7edf6021280302847bfed1956211c4091ca7fc5 | |
| parent | 865b9b981e69618333fed8f6cf7375a413868f3c (diff) | |
| parent | 70e14d5db3a01a44f6c3217bb3dda2dd5828a507 (diff) | |
| download | linux-next-34e4cd373019c46645baa12b2ea98c0565757e52.tar.gz linux-next-34e4cd373019c46645baa12b2ea98c0565757e52.zip | |
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/remoteproc/linux.git
| -rw-r--r-- | Documentation/devicetree/bindings/remoteproc/ti,am3352-wkup-m3.yaml | 1 | ||||
| -rw-r--r-- | Documentation/staging/rpmsg.rst | 17 | ||||
| -rw-r--r-- | drivers/remoteproc/imx_dsp_rproc.c | 7 | ||||
| -rw-r--r-- | drivers/remoteproc/imx_rproc.c | 51 | ||||
| -rw-r--r-- | drivers/remoteproc/qcom_q6v5_adsp.c | 10 | ||||
| -rw-r--r-- | drivers/remoteproc/qcom_q6v5_mss.c | 11 | ||||
| -rw-r--r-- | drivers/remoteproc/qcom_q6v5_pas.c | 12 | ||||
| -rw-r--r-- | drivers/remoteproc/remoteproc_elf_loader.c | 23 | ||||
| -rw-r--r-- | drivers/remoteproc/stm32_rproc.c | 2 | ||||
| -rw-r--r-- | drivers/remoteproc/ti_k3_dsp_remoteproc.c | 4 | ||||
| -rw-r--r-- | drivers/remoteproc/ti_k3_r5_remoteproc.c | 4 | ||||
| -rw-r--r-- | drivers/remoteproc/xlnx_r5_remoteproc.c | 13 | ||||
| -rw-r--r-- | drivers/rpmsg/virtio_rpmsg_bus.c | 156 | ||||
| -rw-r--r-- | include/linux/rpmsg/virtio_rpmsg.h | 41 | ||||
| -rw-r--r-- | include/uapi/linux/rpmsg.h | 15 | ||||
| -rw-r--r-- | samples/rpmsg/rpmsg_client_sample.c | 20 |
16 files changed, 276 insertions, 111 deletions
diff --git a/Documentation/devicetree/bindings/remoteproc/ti,am3352-wkup-m3.yaml b/Documentation/devicetree/bindings/remoteproc/ti,am3352-wkup-m3.yaml index 9e8a03acdec4..997be06ceeb1 100644 --- a/Documentation/devicetree/bindings/remoteproc/ti,am3352-wkup-m3.yaml +++ b/Documentation/devicetree/bindings/remoteproc/ti,am3352-wkup-m3.yaml @@ -62,7 +62,6 @@ required: dependencies: resets: [reset-names] - reset-names: [resets] additionalProperties: false diff --git a/Documentation/staging/rpmsg.rst b/Documentation/staging/rpmsg.rst index 63612b7ee120..017867383fd9 100644 --- a/Documentation/staging/rpmsg.rst +++ b/Documentation/staging/rpmsg.rst @@ -299,3 +299,20 @@ by the bus, and can then start sending messages to the remote service. The plan is also to add static creation of rpmsg channels via the virtio config space, but it's not implemented yet. + +Configurable buffer sizes +========================= + +By default each rpmsg buffer is 512 bytes, and the same size is used for both +the receive and transmit directions. Remote processors that need a different +size (for example a larger MTU, or different RX and TX queue sizes) can +advertise it through the virtio device config space by setting the +VIRTIO_RPMSG_F_BUFSZ feature bit. + +When this feature is negotiated, the driver reads struct virtio_rpmsg_config +from the config space. This structure provides the size of a single buffer for +each direction (TX and RX). The device should advertise TX/RX buffer sizes +that already satisfy the alignment constraints of its buffers. + +If the feature is not negotiated, the default 512-byte buffers are used for +both directions. diff --git a/drivers/remoteproc/imx_dsp_rproc.c b/drivers/remoteproc/imx_dsp_rproc.c index fd60c67ba8a9..96d619daf3f5 100644 --- a/drivers/remoteproc/imx_dsp_rproc.c +++ b/drivers/remoteproc/imx_dsp_rproc.c @@ -726,14 +726,17 @@ static int imx_dsp_rproc_prepare(struct rproc *rproc) struct device *dev = rproc->dev.parent; int ret; + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + ret = imx_dsp_rproc_add_carveout(priv); if (ret) { dev_err(dev, "failed on imx_dsp_rproc_add_carveout\n"); + pm_runtime_put_sync(dev); return ret; } - pm_runtime_get_sync(dev); - return 0; } diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 745ce52cd822..581bb07f5893 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -24,7 +24,6 @@ #include <linux/regmap.h> #include <linux/remoteproc.h> #include <linux/scmi_imx_protocol.h> -#include <linux/workqueue.h> #include "imx_rproc.h" #include "remoteproc_internal.h" @@ -115,8 +114,6 @@ struct imx_rproc { struct mbox_client cl; struct mbox_chan *tx_ch; struct mbox_chan *rx_ch; - struct work_struct rproc_work; - struct workqueue_struct *workqueue; void __iomem *rsc_table; struct imx_sc_ipc *ipc_handle; struct notifier_block rproc_nb; @@ -540,6 +537,7 @@ static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da, /* parse address translation table */ for (i = 0; i < dcfg->att_size; i++) { const struct imx_rproc_att *att = &dcfg->att[i]; + u64 offset; /* * Ignore entries not belong to current core: @@ -552,9 +550,11 @@ static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da, continue; } - if (da >= att->da && da + len < att->da + att->size) { - unsigned int offset = da - att->da; + if (da < att->da) + continue; + offset = da - att->da; + if (offset <= att->size && len <= att->size - offset) { *sys = att->sa + offset; if (is_iomem) *is_iomem = att->flags & ATT_IOMEM; @@ -585,9 +585,14 @@ static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *i return NULL; for (i = 0; i < IMX_RPROC_MEM_MAX; i++) { - if (sys >= priv->mem[i].sys_addr && sys + len < - priv->mem[i].sys_addr + priv->mem[i].size) { - unsigned int offset = sys - priv->mem[i].sys_addr; + u64 offset; + + if (sys < priv->mem[i].sys_addr) + continue; + + offset = sys - priv->mem[i].sys_addr; + if (offset <= priv->mem[i].size && + len <= priv->mem[i].size - offset) { /* __force to make sparse happy with type conversion */ va = (__force void *)(priv->mem[i].cpu_addr + offset); break; @@ -860,21 +865,11 @@ static int imx_rproc_notified_idr_cb(int id, void *ptr, void *data) return 0; } -static void imx_rproc_vq_work(struct work_struct *work) -{ - struct imx_rproc *priv = container_of(work, struct imx_rproc, - rproc_work); - struct rproc *rproc = priv->rproc; - - idr_for_each(&rproc->notifyids, imx_rproc_notified_idr_cb, rproc); -} - static void imx_rproc_rx_callback(struct mbox_client *cl, void *msg) { struct rproc *rproc = dev_get_drvdata(cl->dev); - struct imx_rproc *priv = rproc->priv; - queue_work(priv->workqueue, &priv->rproc_work); + idr_for_each(&rproc->notifyids, imx_rproc_notified_idr_cb, rproc); } static int imx_rproc_xtr_mbox_init(struct rproc *rproc, bool tx_block) @@ -1239,13 +1234,6 @@ static int imx_rproc_sys_off_handler(struct sys_off_data *data) return NOTIFY_DONE; } -static void imx_rproc_destroy_workqueue(void *data) -{ - struct workqueue_struct *workqueue = data; - - destroy_workqueue(workqueue); -} - static int imx_rproc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1273,17 +1261,6 @@ static int imx_rproc_probe(struct platform_device *pdev) priv->ops = dcfg->ops; dev_set_drvdata(dev, rproc); - priv->workqueue = create_workqueue(dev_name(dev)); - if (!priv->workqueue) { - dev_err(dev, "cannot create workqueue\n"); - return -ENOMEM; - } - - ret = devm_add_action_or_reset(dev, imx_rproc_destroy_workqueue, priv->workqueue); - if (ret) - return dev_err_probe(dev, ret, "Failed to add devm destroy workqueue action\n"); - - INIT_WORK(&priv->rproc_work, imx_rproc_vq_work); ret = imx_rproc_xtr_mbox_init(rproc, true); if (ret) diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index 39654206781d..46d9169a37f6 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -104,6 +104,7 @@ struct qcom_adsp { struct completion stop_done; phys_addr_t mem_phys; + unsigned long iova; phys_addr_t mem_reloc; void __iomem *mem_region; size_t mem_size; @@ -333,7 +334,7 @@ static void adsp_unmap_carveout(struct rproc *rproc) struct qcom_adsp *adsp = rproc->priv; if (adsp->has_iommu) - iommu_unmap(rproc->domain, adsp->mem_phys, adsp->mem_size); + iommu_unmap(rproc->domain, adsp->iova, adsp->mem_size); } static int adsp_map_carveout(struct rproc *rproc) @@ -341,7 +342,6 @@ static int adsp_map_carveout(struct rproc *rproc) struct qcom_adsp *adsp = rproc->priv; struct of_phandle_args args; long long sid; - unsigned long iova; int ret; if (!adsp->has_iommu) @@ -358,9 +358,9 @@ static int adsp_map_carveout(struct rproc *rproc) of_node_put(args.np); /* Add SID configuration for ADSP Firmware to SMMU */ - iova = adsp->mem_phys | (sid << 32); + adsp->iova = adsp->mem_phys | (sid << 32); - ret = iommu_map(rproc->domain, iova, adsp->mem_phys, + ret = iommu_map(rproc->domain, adsp->iova, adsp->mem_phys, adsp->mem_size, IOMMU_READ | IOMMU_WRITE, GFP_KERNEL); if (ret) { @@ -430,7 +430,7 @@ static int adsp_start(struct rproc *rproc) goto disable_adsp_clks; } - ret = qcom_q6v5_wait_for_start(&adsp->q6v5, msecs_to_jiffies(5 * HZ)); + ret = qcom_q6v5_wait_for_start(&adsp->q6v5, msecs_to_jiffies(5000)); if (ret == -ETIMEDOUT) { dev_err(adsp->dev, "start timed out\n"); goto disable_adsp_clks; diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index bef198b9ee63..2f71ed2feff6 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -2079,7 +2079,16 @@ static int q6v5_probe(struct platform_device *pdev) if (!desc) return -EINVAL; - if (desc->need_mem_protection && !qcom_pas_is_available()) + /* + * Memory protection is done through qcom_scm_assign_mem(), which needs + * SCM but not PAS. Only the memory setup path issues PAS calls, so + * requiring PAS for every need_mem_protection platform prevents the + * modem from probing at all on TZ firmware that offers no PAS. + */ + if (desc->need_mem_protection && !qcom_scm_is_available()) + return -EPROBE_DEFER; + + if (desc->need_pas_mem_setup && !qcom_pas_is_available()) return -EPROBE_DEFER; mba_image = desc->hexagon_mba_image; diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index ca8e61254c44..a005546c265d 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -405,6 +405,7 @@ static int qcom_pas_stop(struct rproc *rproc) { struct qcom_pas *pas = rproc->priv; int handover; + int dtb_ret; int ret; ret = qcom_q6v5_request_stop(&pas->q6v5, pas->sysmon); @@ -419,9 +420,12 @@ static int qcom_pas_stop(struct rproc *rproc) dev_err(pas->dev, "failed to shutdown: %d\n", ret); if (pas->dtb_pas_id) { - ret = qcom_pas_shutdown(pas->dtb_pas_id); - if (ret) - dev_err(pas->dev, "failed to shutdown dtb: %d\n", ret); + dtb_ret = qcom_pas_shutdown(pas->dtb_pas_id); + if (dtb_ret) + dev_err(pas->dev, "failed to shutdown dtb: %d\n", dtb_ret); + + if (!ret && dtb_ret) + ret = dtb_ret; qcom_pas_unmap_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size); } @@ -524,7 +528,6 @@ static int qcom_pas_attach(struct rproc *rproc) int ret; pas->q6v5.handover_issued = true; - enable_irq(pas->q6v5.handover_irq); pas->q6v5.running = true; ret = irq_get_irqchip_state(pas->q6v5.fatal_irq, @@ -570,7 +573,6 @@ unroll_attach: pas->rproc->state = RPROC_OFFLINE; ret = -EINVAL; disable_running: - disable_irq(pas->q6v5.handover_irq); pas->q6v5.running = false; return ret; diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c index 94177e416047..da3cddbe7d4c 100644 --- a/drivers/remoteproc/remoteproc_elf_loader.c +++ b/drivers/remoteproc/remoteproc_elf_loader.c @@ -46,8 +46,9 @@ int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw) struct elf32_hdr *ehdr; u32 elf_shdr_get_size; u64 phoff, shoff; + size_t shend; char class; - u16 phnum; + u16 phnum, shnum, shstrndx; if (!fw) { dev_err(dev, "failed to load %s\n", name); @@ -90,9 +91,27 @@ int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw) phoff = elf_hdr_get_e_phoff(class, fw->data); shoff = elf_hdr_get_e_shoff(class, fw->data); phnum = elf_hdr_get_e_phnum(class, fw->data); + shnum = elf_hdr_get_e_shnum(class, fw->data); + shstrndx = elf_hdr_get_e_shstrndx(class, fw->data); elf_shdr_get_size = elf_size_of_shdr(class); - if (fw->size < shoff + elf_shdr_get_size) { + /* keeps shoff in size_t range for the two bounds below */ + if (shoff > fw->size) { + dev_err(dev, "Section header table is out of bounds\n"); + return -EINVAL; + } + + if (shnum) { + shend = size_add(size_mul(elf_shdr_get_size, shnum), shoff); + if (shend > fw->size) { + dev_err(dev, "Section headers are out of bounds\n"); + return -EINVAL; + } + } + + /* find_table() reads the header at shstrndx even with no sections */ + shend = size_add(size_mul(elf_shdr_get_size, (size_t)shstrndx + 1), shoff); + if (shend > fw->size) { dev_err(dev, "Image is too small\n"); return -EINVAL; } diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c index 1fe4cdc0a13a..a47e040c6db8 100644 --- a/drivers/remoteproc/stm32_rproc.c +++ b/drivers/remoteproc/stm32_rproc.c @@ -675,7 +675,7 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev, int err, irq; irq = platform_get_irq_optional(pdev, 0); - if (irq == -EPROBE_DEFER) + if (irq < 0 && irq != -ENXIO) return irq; if (irq > 0) { diff --git a/drivers/remoteproc/ti_k3_dsp_remoteproc.c b/drivers/remoteproc/ti_k3_dsp_remoteproc.c index d6ceea6dc920..c0ccf87dbb6f 100644 --- a/drivers/remoteproc/ti_k3_dsp_remoteproc.c +++ b/drivers/remoteproc/ti_k3_dsp_remoteproc.c @@ -154,6 +154,10 @@ static int k3_dsp_rproc_probe(struct platform_device *pdev) dev_info(dev, "configured DSP for remoteproc mode\n"); } + ret = dma_coerce_mask_and_coherent(&rproc->dev, DMA_BIT_MASK(48)); + if (ret) + dev_warn(dev, "Failed to set DMA mask (%d)\n", ret); + ret = devm_rproc_add(dev, rproc); if (ret) return dev_err_probe(dev, ret, "failed to add register device with remoteproc core\n"); diff --git a/drivers/remoteproc/ti_k3_r5_remoteproc.c b/drivers/remoteproc/ti_k3_r5_remoteproc.c index b1d04d082e44..365b4e4c4b50 100644 --- a/drivers/remoteproc/ti_k3_r5_remoteproc.c +++ b/drivers/remoteproc/ti_k3_r5_remoteproc.c @@ -1047,6 +1047,10 @@ static int k3_r5_cluster_rproc_init(struct platform_device *pdev) goto out; } + ret = dma_coerce_mask_and_coherent(&rproc->dev, DMA_BIT_MASK(48)); + if (ret) + dev_warn(dev, "Failed to set DMA mask (%d)\n", ret); + /* K3 R5s have a Region Address Translator (RAT) but no MMU */ rproc->has_iommu = false; /* error recovery is not supported at present */ diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index c685bb5fa62c..630621288430 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -318,6 +318,8 @@ static struct mbox_info *zynqmp_r5_setup_mbox(struct device *cdev) if (!ipi) return NULL; + INIT_WORK(&ipi->mbox_work, handle_event_notified); + mbox_cl = &ipi->mbox_cl; mbox_cl->rx_callback = zynqmp_r5_mb_rx_cb; mbox_cl->tx_block = false; @@ -329,6 +331,7 @@ static struct mbox_info *zynqmp_r5_setup_mbox(struct device *cdev) ipi->tx_chan = mbox_request_channel_byname(mbox_cl, "tx"); if (IS_ERR(ipi->tx_chan)) { ipi->tx_chan = NULL; + cancel_work_sync(&ipi->mbox_work); kfree(ipi); dev_warn(cdev, "mbox tx channel request failed\n"); return NULL; @@ -339,13 +342,12 @@ static struct mbox_info *zynqmp_r5_setup_mbox(struct device *cdev) mbox_free_channel(ipi->tx_chan); ipi->rx_chan = NULL; ipi->tx_chan = NULL; + cancel_work_sync(&ipi->mbox_work); kfree(ipi); dev_warn(cdev, "mbox rx channel request failed\n"); return NULL; } - INIT_WORK(&ipi->mbox_work, handle_event_notified); - return ipi; } @@ -364,6 +366,8 @@ static void zynqmp_r5_free_mbox(struct mbox_info *ipi) ipi->rx_chan = NULL; } + cancel_work_sync(&ipi->mbox_work); + kfree(ipi); } @@ -381,7 +385,10 @@ static void zynqmp_r5_rproc_kick(struct rproc *rproc, int vqid) int ret; ipi = r5_core->ipi; - if (!ipi) + if (!ipi || !ipi->tx_chan) + return; + + if (mbox_chan_tx_slots_available(ipi->tx_chan) == 0) return; mb_msg = (struct zynqmp_ipi_message *)ipi->tx_mc_buf; diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 1b8bb05924af..40ec93fedde9 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -20,6 +20,7 @@ #include <linux/rpmsg.h> #include <linux/rpmsg/byteorder.h> #include <linux/rpmsg/ns.h> +#include <linux/rpmsg/virtio_rpmsg.h> #include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/sched.h> @@ -35,13 +36,15 @@ * @vdev: the virtio device * @rvq: rx virtqueue * @svq: tx virtqueue - * @rbufs: kernel address of rx buffers - * @sbufs: kernel address of tx buffers - * @num_bufs: total number of buffers for rx and tx - * @buf_size: size of one rx or tx buffer - * @last_sbuf: index of last tx buffer used + * @rx_bufs: kernel address of rx buffers + * @tx_bufs: kernel address of tx buffers + * @num_rx_buf: total number of rx buffers + * @num_tx_buf: total number of tx buffers + * @rx_buf_size: size of one rx buffer + * @tx_buf_size: size of one tx buffer + * @last_tx_buf: index of last tx buffer used * @bufs_dma: dma base addr of the buffers - * @tx_lock: protects svq and sbufs, to allow concurrent senders. + * @tx_lock: protects svq and tx_bufs, to allow concurrent senders. * sending a message might require waking up a dozing remote * processor, which involves sleeping, hence the mutex. * @endpoints: idr of local endpoints, allows fast retrieval @@ -55,10 +58,12 @@ struct virtproc_info { struct virtio_device *vdev; struct virtqueue *rvq, *svq; - void *rbufs, *sbufs; - unsigned int num_bufs; - unsigned int buf_size; - int last_sbuf; + void *rx_bufs, *tx_bufs; + unsigned int num_rx_buf; + unsigned int num_tx_buf; + unsigned int rx_buf_size; + unsigned int tx_buf_size; + int last_tx_buf; dma_addr_t bufs_dma; struct mutex tx_lock; struct idr endpoints; @@ -66,9 +71,6 @@ struct virtproc_info { wait_queue_head_t sendq; }; -/* The feature bitmap for virtio rpmsg */ -#define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */ - /** * struct rpmsg_hdr - common header for all rpmsg messages * @src: source address @@ -110,7 +112,7 @@ struct virtio_rpmsg_channel { /* * We're allocating buffers of 512 bytes each for communications. The * number of buffers will be computed from the number of buffers supported - * by the vring, upto a maximum of 512 buffers (256 in each direction). + * by the vring, up to a maximum of 256 in each direction. * * Each buffer will have 16 bytes for the msg header and 496 bytes for * the payload. @@ -125,8 +127,8 @@ struct virtio_rpmsg_channel { * can change this without changing anything in the firmware of the remote * processor. */ -#define MAX_RPMSG_NUM_BUFS (512) -#define MAX_RPMSG_BUF_SIZE (512) +#define MAX_RPMSG_NUM_BUFS (256) +#define DEFAULT_RPMSG_BUF_SIZE (512) /* * Local addresses are dynamically allocated on-demand. @@ -439,12 +441,9 @@ static void *get_a_tx_buf(struct virtproc_info *vrp) mutex_lock(&vrp->tx_lock); - /* - * either pick the next unused tx buffer - * (half of our buffers are used for sending messages) - */ - if (vrp->last_sbuf < vrp->num_bufs / 2) - ret = vrp->sbufs + vrp->buf_size * vrp->last_sbuf++; + /* either pick the next unused tx buffer */ + if (vrp->last_tx_buf < vrp->num_tx_buf) + ret = vrp->tx_bufs + vrp->tx_buf_size * vrp->last_tx_buf++; /* or recycle a used one */ else ret = virtqueue_get_buf(vrp->svq, &len); @@ -514,7 +513,7 @@ static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev, * messaging), or to improve the buffer allocator, to support * variable-length buffer sizes. */ - if (len > vrp->buf_size - sizeof(struct rpmsg_hdr)) { + if (len > vrp->tx_buf_size - sizeof(struct rpmsg_hdr)) { dev_err(dev, "message is too big (%d)\n", len); return -EMSGSIZE; } @@ -630,11 +629,10 @@ static __poll_t virtio_rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp, /* * check for a free buffer, either: - * - we haven't used all of the available transmit buffers (half of the - * allocated buffers are used for transmit, hence num_bufs / 2), or, + * - we haven't used all of the available transmit buffers or, * - we ask the virtqueue if there's a buffer available */ - if (vrp->last_sbuf < vrp->num_bufs / 2 || + if (vrp->last_tx_buf < vrp->num_tx_buf || !virtqueue_enable_cb(vrp->svq)) mask |= EPOLLOUT; @@ -648,7 +646,7 @@ static ssize_t virtio_rpmsg_get_mtu(struct rpmsg_endpoint *ept) struct rpmsg_device *rpdev = ept->rpdev; struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); - return vch->vrp->buf_size - sizeof(struct rpmsg_hdr); + return vch->vrp->tx_buf_size - sizeof(struct rpmsg_hdr); } static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev, @@ -674,7 +672,7 @@ static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev, * We currently use fixed-sized buffers, so trivially sanitize * the reported payload length. */ - if (len > vrp->buf_size || + if (len > vrp->rx_buf_size || msg_len > (len - sizeof(struct rpmsg_hdr))) { dev_warn(dev, "inbound msg too big: (%d, %d)\n", len, msg_len); return -EINVAL; @@ -707,7 +705,7 @@ static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev, dev_warn_ratelimited(dev, "msg received with no recipient\n"); /* publish the real size of the buffer */ - rpmsg_sg_init(&sg, msg, vrp->buf_size); + rpmsg_sg_init(&sg, msg, vrp->rx_buf_size); /* add the buffer back to the remote processor's virtqueue */ err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, msg, GFP_KERNEL); @@ -811,6 +809,65 @@ static void rpmsg_virtio_del_ctrl_dev(struct rpmsg_device *rpdev_ctrl) device_unregister(&rpdev_ctrl->dev); } +static int rpmsg_virtio_get_buf_size(struct virtproc_info *vrp) +{ + struct virtio_device *vdev; + u8 version; + u16 size; + + vdev = vrp->vdev; + + /* + * If VIRTIO_RPMSG_F_BUFSZ feature is supported, then configure buf + * size from virtio device config space from the resource table. + * If the feature is not supported, then assign default buf size. + */ + if (virtio_has_feature(vdev, VIRTIO_RPMSG_F_BUFSZ)) { + virtio_cread(vdev, struct virtio_rpmsg_config, + version, &version); + + /* for now we support only v1 */ + if (version != RPMSG_VDEV_CONFIG_V1) { + dev_err(&vdev->dev, + "unsupported vdev config version %u\n", version); + return -EINVAL; + } + + /* size of the config space must match */ + virtio_cread(vdev, struct virtio_rpmsg_config, + size, &size); + if (size != sizeof(struct virtio_rpmsg_config)) { + dev_err(&vdev->dev, "invalid size of vdev config %u\n", + size); + return -EINVAL; + } + + /* note: tx and rx are defined from remote view */ + virtio_cread(vdev, struct virtio_rpmsg_config, + txbuf_size, &vrp->rx_buf_size); + virtio_cread(vdev, struct virtio_rpmsg_config, + rxbuf_size, &vrp->tx_buf_size); + + /* The buffers must hold at least the rpmsg header */ + if (vrp->rx_buf_size < sizeof(struct rpmsg_hdr) || + vrp->tx_buf_size < sizeof(struct rpmsg_hdr)) { + dev_err(&vdev->dev, + "bad vdev config: rx buf sz = %u, tx buf sz = %u\n", + vrp->rx_buf_size, vrp->tx_buf_size); + return -EINVAL; + } + + dev_dbg(&vdev->dev, + "vdev config: ver=%u, rx sz = 0x%x, tx sz = 0x%x\n", + version, vrp->rx_buf_size, vrp->tx_buf_size); + } else { + vrp->rx_buf_size = DEFAULT_RPMSG_BUF_SIZE; + vrp->tx_buf_size = DEFAULT_RPMSG_BUF_SIZE; + } + + return 0; +} + static int rpmsg_probe(struct virtio_device *vdev) { struct virtqueue_info vqs_info[] = { @@ -845,19 +902,23 @@ static int rpmsg_probe(struct virtio_device *vdev) vrp->rvq = vqs[0]; vrp->svq = vqs[1]; - /* we expect symmetric tx/rx vrings */ - WARN_ON(virtqueue_get_vring_size(vrp->rvq) != - virtqueue_get_vring_size(vrp->svq)); - /* we need less buffers if vrings are small */ - if (virtqueue_get_vring_size(vrp->rvq) < MAX_RPMSG_NUM_BUFS / 2) - vrp->num_bufs = virtqueue_get_vring_size(vrp->rvq) * 2; + if (virtqueue_get_vring_size(vrp->rvq) < MAX_RPMSG_NUM_BUFS) + vrp->num_rx_buf = virtqueue_get_vring_size(vrp->rvq); + else + vrp->num_rx_buf = MAX_RPMSG_NUM_BUFS; + + if (virtqueue_get_vring_size(vrp->svq) < MAX_RPMSG_NUM_BUFS) + vrp->num_tx_buf = virtqueue_get_vring_size(vrp->svq); else - vrp->num_bufs = MAX_RPMSG_NUM_BUFS; + vrp->num_tx_buf = MAX_RPMSG_NUM_BUFS; - vrp->buf_size = MAX_RPMSG_BUF_SIZE; + err = rpmsg_virtio_get_buf_size(vrp); + if (err) + goto vqs_del; - total_buf_space = vrp->num_bufs * vrp->buf_size; + total_buf_space = (vrp->num_rx_buf * vrp->rx_buf_size) + + (vrp->num_tx_buf * vrp->tx_buf_size); /* allocate coherent memory for the buffers */ bufs_va = dma_alloc_coherent(vdev->dev.parent, @@ -871,18 +932,17 @@ static int rpmsg_probe(struct virtio_device *vdev) dev_dbg(&vdev->dev, "buffers: va %p, dma %pad\n", bufs_va, &vrp->bufs_dma); - /* half of the buffers is dedicated for RX */ - vrp->rbufs = bufs_va; + /* first part of the buffers is dedicated for RX */ + vrp->rx_bufs = bufs_va; - /* and half is dedicated for TX */ - vrp->sbufs = bufs_va + total_buf_space / 2; + vrp->tx_bufs = bufs_va + (vrp->num_rx_buf * vrp->rx_buf_size); /* set up the receive buffers */ - for (i = 0; i < vrp->num_bufs / 2; i++) { + for (i = 0; i < vrp->num_rx_buf; i++) { struct scatterlist sg; - void *cpu_addr = vrp->rbufs + i * vrp->buf_size; + void *cpu_addr = vrp->rx_bufs + i * vrp->rx_buf_size; - rpmsg_sg_init(&sg, cpu_addr, vrp->buf_size); + rpmsg_sg_init(&sg, cpu_addr, vrp->rx_buf_size); err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, cpu_addr, GFP_KERNEL); @@ -965,7 +1025,8 @@ static int rpmsg_remove_device(struct device *dev, void *data) static void rpmsg_remove(struct virtio_device *vdev) { struct virtproc_info *vrp = vdev->priv; - size_t total_buf_space = vrp->num_bufs * vrp->buf_size; + size_t total_buf_space = (vrp->num_rx_buf * vrp->rx_buf_size) + + (vrp->num_tx_buf * vrp->tx_buf_size); int ret; virtio_reset_device(vdev); @@ -979,7 +1040,7 @@ static void rpmsg_remove(struct virtio_device *vdev) vdev->config->del_vqs(vrp->vdev); dma_free_coherent(vdev->dev.parent, total_buf_space, - vrp->rbufs, vrp->bufs_dma); + vrp->rx_bufs, vrp->bufs_dma); kfree(vrp); } @@ -991,6 +1052,7 @@ static struct virtio_device_id id_table[] = { static unsigned int features[] = { VIRTIO_RPMSG_F_NS, + VIRTIO_RPMSG_F_BUFSZ, }; static struct virtio_driver virtio_ipc_driver = { diff --git a/include/linux/rpmsg/virtio_rpmsg.h b/include/linux/rpmsg/virtio_rpmsg.h new file mode 100644 index 000000000000..0872cbca4593 --- /dev/null +++ b/include/linux/rpmsg/virtio_rpmsg.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Pinecone Inc. 2019 + * Copyright (C) Xiang Xiao <xiaoxiang@pinecone.net> + * Copyright (C) Advanced Micro Devices, Inc. 2026 + */ + +#ifndef _LINUX_VIRTIO_RPMSG_H +#define _LINUX_VIRTIO_RPMSG_H + +#include <linux/types.h> +#include <linux/virtio_types.h> + +/* The feature bitmap for virtio rpmsg */ +#define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */ +#define VIRTIO_RPMSG_F_BUFSZ 1 /* RP advertises RX/TX buffer sizes from config space */ + +/* Version of struct virtio_rpmsg_config understood by this driver */ +#define RPMSG_VDEV_CONFIG_V1 1 + +/** + * struct virtio_rpmsg_config - config space for rpmsg virtio device + * + * @version: version of this structure, currently %RPMSG_VDEV_CONFIG_V1. + * @size: size of this structure in bytes. + * @txbuf_size: Tx buf size from remote's view. For Linux this is rx buf size. + * @rxbuf_size: Rx buf size from remote's view. For Linux this is tx buf size. + * + * This is the configuration structure shared by the device and the driver, + * read when %VIRTIO_RPMSG_F_BUFSZ is negotiated. The fields are laid out so + * the structure is naturally 32-bit aligned. + */ +struct virtio_rpmsg_config { + u8 version; + __virtio16 size; + /* The tx/rx individual buffer size (if VIRTIO_RPMSG_F_BUFSZ) */ + __virtio32 txbuf_size; + __virtio32 rxbuf_size; +} __packed; + +#endif /* _LINUX_VIRTIO_RPMSG_H */ diff --git a/include/uapi/linux/rpmsg.h b/include/uapi/linux/rpmsg.h index f0c8da2b185b..d553ebac3181 100644 --- a/include/uapi/linux/rpmsg.h +++ b/include/uapi/linux/rpmsg.h @@ -24,32 +24,35 @@ struct rpmsg_endpoint_info { }; /** - * Instantiate a new rmpsg char device endpoint. + * RPMSG_CREATE_EPT_IOCTL - Instantiate a new rmpsg char device endpoint. */ #define RPMSG_CREATE_EPT_IOCTL _IOW(0xb5, 0x1, struct rpmsg_endpoint_info) /** - * Destroy a rpmsg char device endpoint created by the RPMSG_CREATE_EPT_IOCTL. + * RPMSG_DESTROY_EPT_IOCTL - Destroy a rpmsg char device endpoint created + * by the RPMSG_CREATE_EPT_IOCTL. */ #define RPMSG_DESTROY_EPT_IOCTL _IO(0xb5, 0x2) /** - * Instantiate a new local rpmsg service device. + * RPMSG_CREATE_DEV_IOCTL - Instantiate a new local rpmsg service device. */ #define RPMSG_CREATE_DEV_IOCTL _IOW(0xb5, 0x3, struct rpmsg_endpoint_info) /** - * Release a local rpmsg device. + * RPMSG_RELEASE_DEV_IOCTL - Release a local rpmsg device. */ #define RPMSG_RELEASE_DEV_IOCTL _IOW(0xb5, 0x4, struct rpmsg_endpoint_info) /** - * Get the flow control state of the remote rpmsg char device. + * RPMSG_GET_OUTGOING_FLOWCONTROL - Get the flow control state of the + * remote rpmsg char device. */ #define RPMSG_GET_OUTGOING_FLOWCONTROL _IOR(0xb5, 0x5, int) /** - * Set the flow control state of the local rpmsg char device. + * RPMSG_SET_INCOMING_FLOWCONTROL - Set the flow control state of the + * local rpmsg char device. */ #define RPMSG_SET_INCOMING_FLOWCONTROL _IOR(0xb5, 0x6, int) diff --git a/samples/rpmsg/rpmsg_client_sample.c b/samples/rpmsg/rpmsg_client_sample.c index ae5081662283..4c43436aadb6 100644 --- a/samples/rpmsg/rpmsg_client_sample.c +++ b/samples/rpmsg/rpmsg_client_sample.c @@ -52,6 +52,7 @@ static int rpmsg_sample_probe(struct rpmsg_device *rpdev) { int ret; struct instance_data *idata; + ssize_t mtu, msg_len; dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n", rpdev->src, rpdev->dst); @@ -62,8 +63,25 @@ static int rpmsg_sample_probe(struct rpmsg_device *rpdev) dev_set_drvdata(&rpdev->dev, idata); + mtu = rpmsg_get_mtu(rpdev->ept); + if (mtu < 0) { + dev_warn(&rpdev->dev, "invalid rpmsg MTU size = %ld\n", mtu); + return mtu; + } + + dev_info(&rpdev->dev, "rpmsg MTU size = %ld\n", mtu); + + msg_len = strlen(MSG); + /* make sure our message fits in a single rpmsg buffer */ + if (msg_len > mtu) { + dev_err(&rpdev->dev, + "message size %zu exceeds rpmsg MTU size %ld\n", + strlen(MSG), mtu); + return -EMSGSIZE; + } + /* send a message to our remote processor */ - ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG)); + ret = rpmsg_send(rpdev->ept, MSG, msg_len); if (ret) { dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); return ret; |
