This is an automated email from the ASF dual-hosted git repository.
xiaoxiang781216 pushed a commit to branch releases/13.1
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/releases/13.1 by this push:
new 7e827485b2c fs/v9fs: Split virtio buffers where their physical pages
are not adjacent.
7e827485b2c is described below
commit 7e827485b2cd81608b3285fb83b17645d30f8b67
Author: Marco Casaroli <[email protected]>
AuthorDate: Fri Oct 9 00:17:03 2026 +0200
fs/v9fs: Split virtio buffers where their physical pages are not adjacent.
virtio_9p_request() gave each buffer to the device as one descriptor,
and libmetal translates only its start address. In a kernel build a
buffer in a user address environment is contiguous only in virtual
memory, so a read that crossed a page wrote into whatever physical page
followed the first one. QEMU stops the device with "virtio: bogus
descriptor or out of resources" when that range leaves guest memory.
With CONFIG_ARCH_ADDRENV, split each buffer at the pages that are not
physically adjacent.
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <[email protected]>
---
fs/v9fs/virtio_9p.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 107 insertions(+), 6 deletions(-)
diff --git a/fs/v9fs/virtio_9p.c b/fs/v9fs/virtio_9p.c
index 9f0c4881467..9a02b500e58 100644
--- a/fs/v9fs/virtio_9p.c
+++ b/fs/v9fs/virtio_9p.c
@@ -29,6 +29,10 @@
#include <nuttx/nuttx.h>
#include <nuttx/spinlock.h>
+#ifdef CONFIG_ARCH_ADDRENV
+# include <nuttx/arch.h>
+# include <nuttx/pgalloc.h>
+#endif
#include <nuttx/virtio/virtio.h>
#include "client.h"
@@ -89,6 +93,67 @@ const struct v9fs_transport_ops_s g_virtio_9p_transport_ops =
* Private Functions
****************************************************************************/
+#ifdef CONFIG_ARCH_ADDRENV
+
+/****************************************************************************
+ * Name: virtio_9p_split
+ *
+ * Description:
+ * A buffer in a user address environment is contiguous only in virtual
+ * memory, but the device reads and writes physical memory, and one
+ * descriptor covers one physical range. Split the buffer where its
+ * physical pages are not adjacent. With vb NULL, only count.
+ *
+ * Returned Value:
+ * The number of descriptors the buffer needs.
+ *
+ ****************************************************************************/
+
+static size_t virtio_9p_split(FAR void *buf, size_t len,
+ FAR struct virtqueue_buf *vb)
+{
+ uintptr_t va = (uintptr_t)buf;
+ uintptr_t next = 0;
+ size_t n = 0;
+
+ while (len > 0)
+ {
+ size_t chunk = MM_PGSIZE - (va & MM_PGMASK);
+ uintptr_t pa;
+
+ if (chunk > len)
+ {
+ chunk = len;
+ }
+
+ pa = up_addrenv_va_to_pa((FAR void *)va);
+ if (n > 0 && pa == next)
+ {
+ if (vb != NULL)
+ {
+ vb[n - 1].len += chunk;
+ }
+ }
+ else
+ {
+ if (vb != NULL)
+ {
+ vb[n].buf = (FAR void *)va;
+ vb[n].len = chunk;
+ }
+
+ n++;
+ }
+
+ next = pa + chunk;
+ va += chunk;
+ len -= chunk;
+ }
+
+ return n;
+}
+#endif
+
/****************************************************************************
* Name: virtio_9p_create
****************************************************************************/
@@ -165,26 +230,62 @@ static int virtio_9p_request(FAR struct v9fs_transport_s
*transport,
FAR struct virtio_9p_priv_s *priv =
container_of(transport, struct virtio_9p_priv_s, transport);
FAR struct virtqueue *vq = priv->vdev->vrings_info[0].vq;
- struct virtqueue_buf vb[payload->wcount + payload->rcount];
+#ifdef CONFIG_ARCH_ADDRENV
+ size_t wcount = 0;
+ size_t rcount = 0;
+ size_t n = 0;
+#else
+ size_t wcount = payload->wcount;
+ size_t rcount = payload->rcount;
+#endif
irqstate_t flags;
size_t i;
int ret;
+#ifdef CONFIG_ARCH_ADDRENV
+ for (i = 0; i < payload->wcount; i++)
+ {
+ wcount += virtio_9p_split(payload->wiov[i].iov_base,
+ payload->wiov[i].iov_len, NULL);
+ }
+
+ for (i = 0; i < payload->rcount; i++)
+ {
+ rcount += virtio_9p_split(payload->riov[i].iov_base,
+ payload->riov[i].iov_len, NULL);
+ }
+#endif
+
+ struct virtqueue_buf vb[wcount + rcount];
+
+#ifdef CONFIG_ARCH_ADDRENV
for (i = 0; i < payload->wcount; i++)
+ {
+ n += virtio_9p_split(payload->wiov[i].iov_base,
+ payload->wiov[i].iov_len, &vb[n]);
+ }
+
+ for (i = 0; i < payload->rcount; i++)
+ {
+ n += virtio_9p_split(payload->riov[i].iov_base,
+ payload->riov[i].iov_len, &vb[n]);
+ }
+#else
+ for (i = 0; i < wcount; i++)
{
vb[i].buf = payload->wiov[i].iov_base;
vb[i].len = payload->wiov[i].iov_len;
}
- for (i = 0; i < payload->rcount; i++)
+ for (i = 0; i < rcount; i++)
{
- vb[payload->wcount + i].buf = payload->riov[i].iov_base;
- vb[payload->wcount + i].len = payload->riov[i].iov_len;
+ vb[wcount + i].buf = payload->riov[i].iov_base;
+ vb[wcount + i].len = payload->riov[i].iov_len;
}
+#endif
flags = spin_lock_irqsave(&priv->lock);
- ret = virtqueue_add_buffer(vq, vb, payload->wcount, payload->rcount,
- payload);
+ ret = virtqueue_add_buffer(vq, vb, wcount, rcount, payload);
if (ret < 0)
{
vrterr("virtqueue_add_buffer failed, ret=%d\n", ret);