mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH 1/3] lib: add scatterlist abstraction
@ 2025-02-14 10:12 Ahmad Fatoum
  2025-02-14 10:12 ` [PATCH 2/3] virtio: replace virtio_sg with common scatterlist Ahmad Fatoum
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Ahmad Fatoum @ 2025-02-14 10:12 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

With barebox using 1:1 memory mapping whenever possible, scatter-gather
DMA seems not that important. Many Linux APIs use struct scatterlist
as parameters though, so adding a basic support in barebox will make
easier to port these drivers.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 include/linux/scatterlist.h | 138 +++++++++++++++++++++++++++++++++++
 lib/Makefile                |   1 +
 lib/scatterlist.c           | 142 ++++++++++++++++++++++++++++++++++++
 3 files changed, 281 insertions(+)
 create mode 100644 include/linux/scatterlist.h
 create mode 100644 lib/scatterlist.c

diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
new file mode 100644
index 000000000000..4ac164b6d3f7
--- /dev/null
+++ b/include/linux/scatterlist.h
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_SCATTERLIST_H
+#define _LINUX_SCATTERLIST_H
+
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/bug.h>
+#include <asm/io.h>
+
+
+struct scatterlist {
+	void		*address;
+	unsigned int	flags:2;
+#define SG_CHAIN	0x01UL
+#define SG_END		0x02UL
+	unsigned int	length:30;
+};
+
+struct sg_table {
+	struct scatterlist *sgl;	/* the list */
+	unsigned int nents;		/* number of mapped entries */
+	unsigned int orig_nents;	/* original size of list */
+};
+
+/*
+ * Notes on SG table design.
+ *
+ * We use the the scatterlist struct to place the address AND encode
+ * information about the sg table as well. The two flag bits are reserved
+ * for this information.
+ *
+ * If bit 0 is set, then the address member is a pointer to the next sg
+ * table list. Otherwise the next entry is at sg + 1.
+ *
+ * If bit 1 is set, then this sg entry is the last element in a list.
+ *
+ * See sg_next().
+ *
+ */
+
+static inline unsigned int __sg_flags(struct scatterlist *sg)
+{
+	return sg->flags;
+}
+
+static inline bool sg_is_chain(struct scatterlist *sg)
+{
+	return __sg_flags(sg) & SG_CHAIN;
+}
+
+static inline bool sg_is_last(struct scatterlist *sg)
+{
+	return __sg_flags(sg) & SG_END;
+}
+
+static inline struct scatterlist *sg_chain_ptr(struct scatterlist *sg)
+{
+	return sg->address;
+}
+
+int sg_nents(struct scatterlist *sg);
+int sg_nents_for_len(struct scatterlist *sg, u64 len);
+
+/**
+ * sg_set_buf - Set sg entry to point at given data
+ * @sg:		 SG entry
+ * @buf:	 Data
+ * @buflen:	 Data length
+ *
+ **/
+static inline void sg_set_buf(struct scatterlist *sg, const void *buf,
+			      unsigned int buflen)
+{
+	sg->address = (void *)buf;
+	sg->length = buflen;
+}
+
+/*
+ * Loop over each sg element, following the pointer to a new list if necessary
+ */
+#define for_each_sg(sglist, sg, nr, __i)	\
+	for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg))
+
+/*
+ * Loop over each sg element in the given sg_table object.
+ */
+#define for_each_sgtable_sg(sgt, sg, i)		\
+	for_each_sg((sgt)->sgl, sg, (sgt)->orig_nents, i)
+
+/**
+ * sg_mark_end - Mark the end of the scatterlist
+ * @sg:		 SG entryScatterlist
+ *
+ * Description:
+ *   Marks the passed in sg entry as the termination point for the sg
+ *   table. A call to sg_next() on this entry will return NULL.
+ *
+ **/
+static inline void sg_mark_end(struct scatterlist *sg)
+{
+	/*
+	 * Set termination bit, clear potential chain bit
+	 */
+	sg->flags |= SG_END;
+	sg->flags &= ~SG_CHAIN;
+}
+
+/**
+ * sg_unmark_end - Undo setting the end of the scatterlist
+ * @sg:		 SG entryScatterlist
+ *
+ * Description:
+ *   Removes the termination marker from the given entry of the scatterlist.
+ *
+ **/
+static inline void sg_unmark_end(struct scatterlist *sg)
+{
+	sg->flags &= ~SG_END;
+}
+
+/**
+ * sg_init_marker - Initialize markers in sg table
+ * @sgl:	   The SG table
+ * @nents:	   Number of entries in table
+ *
+ **/
+static inline void sg_init_marker(struct scatterlist *sgl,
+				  unsigned int nents)
+{
+	sg_mark_end(&sgl[nents - 1]);
+}
+
+struct scatterlist *sg_next(struct scatterlist *);
+struct scatterlist *sg_last(struct scatterlist *s, unsigned int);
+void sg_init_table(struct scatterlist *, unsigned int);
+void sg_init_one(struct scatterlist *, const void *, unsigned int);
+
+#endif /* _LINUX_SCATTERLIST_H */
diff --git a/lib/Makefile b/lib/Makefile
index 46b42146cbc9..4e75f2467ac1 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_FIP)	+= fip.o tbbr_config.o
 obj-$(CONFIG_JSMN)	+= jsmn.o
 obj-$(CONFIG_BLOBGEN)	+= blobgen.o
 obj-y			+= stringlist.o
+obj-y			+= scatterlist.o
 obj-y			+= cmdlinepart.o
 obj-y			+= recursive_action.o
 obj-y			+= make_directory.o
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
new file mode 100644
index 000000000000..f7b06013a552
--- /dev/null
+++ b/lib/scatterlist.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2007 Jens Axboe <jens.axboe@oracle.com>
+ *
+ * Scatterlist handling helpers.
+ */
+
+#include <linux/export.h>
+#include <linux/scatterlist.h>
+#include <linux/errno.h>
+
+/**
+ * sg_next - return the next scatterlist entry in a list
+ * @sg:		The current sg entry
+ *
+ * Description:
+ *   Usually the next entry will be @sg@ + 1, but if this sg element is part
+ *   of a chained scatterlist, it could jump to the start of a new
+ *   scatterlist array.
+ *
+ **/
+struct scatterlist *sg_next(struct scatterlist *sg)
+{
+	if (sg_is_last(sg))
+		return NULL;
+
+	sg++;
+	if (unlikely(sg_is_chain(sg)))
+		sg = sg_chain_ptr(sg);
+
+	return sg;
+}
+EXPORT_SYMBOL(sg_next);
+
+/**
+ * sg_nents - return total count of entries in scatterlist
+ * @sg:		The scatterlist
+ *
+ * Description:
+ * Allows to know how many entries are in sg, taking into account
+ * chaining as well
+ *
+ **/
+int sg_nents(struct scatterlist *sg)
+{
+	int nents;
+	for (nents = 0; sg; sg = sg_next(sg))
+		nents++;
+	return nents;
+}
+EXPORT_SYMBOL(sg_nents);
+
+/**
+ * sg_nents_for_len - return total count of entries in scatterlist
+ *                    needed to satisfy the supplied length
+ * @sg:		The scatterlist
+ * @len:	The total required length
+ *
+ * Description:
+ * Determines the number of entries in sg that are required to meet
+ * the supplied length, taking into account chaining as well
+ *
+ * Returns:
+ *   the number of sg entries needed, negative error on failure
+ *
+ **/
+int sg_nents_for_len(struct scatterlist *sg, u64 len)
+{
+	int nents;
+	u64 total;
+
+	if (!len)
+		return 0;
+
+	for (nents = 0, total = 0; sg; sg = sg_next(sg)) {
+		nents++;
+		total += sg->length;
+		if (total >= len)
+			return nents;
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(sg_nents_for_len);
+
+/**
+ * sg_last - return the last scatterlist entry in a list
+ * @sgl:	First entry in the scatterlist
+ * @nents:	Number of entries in the scatterlist
+ *
+ * Description:
+ *   Should only be used casually, it (currently) scans the entire list
+ *   to get the last entry.
+ *
+ *   Note that the @sgl@ pointer passed in need not be the first one,
+ *   the important bit is that @nents@ denotes the number of entries that
+ *   exist from @sgl@.
+ *
+ **/
+struct scatterlist *sg_last(struct scatterlist *sgl, unsigned int nents)
+{
+	struct scatterlist *sg, *ret = NULL;
+	unsigned int i;
+
+	for_each_sg(sgl, sg, nents, i)
+		ret = sg;
+
+	BUG_ON(!sg_is_last(ret));
+	return ret;
+}
+EXPORT_SYMBOL(sg_last);
+
+/**
+ * sg_init_table - Initialize SG table
+ * @sgl:	   The SG table
+ * @nents:	   Number of entries in table
+ *
+ * Notes:
+ *   If this is part of a chained sg table, sg_mark_end() should be
+ *   used only on the last table part.
+ *
+ **/
+void sg_init_table(struct scatterlist *sgl, unsigned int nents)
+{
+	memset(sgl, 0, sizeof(*sgl) * nents);
+	sg_init_marker(sgl, nents);
+}
+EXPORT_SYMBOL(sg_init_table);
+
+/**
+ * sg_init_one - Initialize a single entry sg list
+ * @sg:		 SG entry
+ * @buf:	 Virtual address for IO
+ * @buflen:	 IO length
+ *
+ **/
+void sg_init_one(struct scatterlist *sg, const void *buf, unsigned int buflen)
+{
+	sg_init_table(sg, 1);
+	sg_set_buf(sg, buf, buflen);
+}
+EXPORT_SYMBOL(sg_init_one);
-- 
2.39.5




^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 2/3] virtio: replace virtio_sg with common scatterlist
  2025-02-14 10:12 [PATCH 1/3] lib: add scatterlist abstraction Ahmad Fatoum
@ 2025-02-14 10:12 ` Ahmad Fatoum
  2025-02-14 10:12 ` [PATCH 3/3] virtio: add token data parameter to virtqueue_add_sgs Ahmad Fatoum
  2025-02-17  9:48 ` [PATCH 1/3] lib: add scatterlist abstraction Sascha Hauer
  2 siblings, 0 replies; 4+ messages in thread
From: Ahmad Fatoum @ 2025-02-14 10:12 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Now that we have a basic scatterlist implementation in barebox, let's
switch virtio to use it instead of the virtio_sg knock-off.

This introduces no functional change, but will make porting virtio
driver from Linux easier in future.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/block/virtio_blk.c      | 13 ++++----
 drivers/hw_random/virtio-rng.c  | 11 +++----
 drivers/input/virtio_input.c    | 12 +++----
 drivers/net/virtio.c            | 46 +++++++++++++--------------
 drivers/serial/virtio_console.c | 16 +++++-----
 drivers/virtio/virtio_ring.c    | 55 +++++++++++++++++++--------------
 include/linux/virtio.h          | 18 -----------
 include/linux/virtio_ring.h     | 19 ++++++------
 8 files changed, 87 insertions(+), 103 deletions(-)

diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index cbef500d59b3..3ee7972fe859 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -23,20 +23,20 @@ static int virtio_blk_do_req(struct virtio_blk_priv *priv, void *buffer,
 			     sector_t sector, blkcnt_t blkcnt, u32 type)
 {
 	unsigned int num_out = 0, num_in = 0;
-	struct virtio_sg *sgs[3];
-	u8 status;
+	struct scatterlist hdr_sg, data_sg, status_sg, *sgs[3];
+	u8 status = VIRTIO_BLK_S_IOERR;
 	int ret;
 
 	struct virtio_blk_outhdr out_hdr = {
 		.type = cpu_to_virtio32(priv->vdev, type),
 		.sector = cpu_to_virtio64(priv->vdev, sector),
 	};
-	struct virtio_sg hdr_sg = { &out_hdr, sizeof(out_hdr) };
-	struct virtio_sg data_sg = { buffer, blkcnt * 512 };
-	struct virtio_sg status_sg = { &status, sizeof(status) };
 
+	sg_init_one(&hdr_sg, &out_hdr, sizeof(out_hdr));
 	sgs[num_out++] = &hdr_sg;
 
+	sg_init_one(&data_sg, buffer, blkcnt * 512);
+
 	switch(type) {
 	case VIRTIO_BLK_T_OUT:
 		sgs[num_out++] = &data_sg;
@@ -46,9 +46,10 @@ static int virtio_blk_do_req(struct virtio_blk_priv *priv, void *buffer,
 		break;
 	}
 
+	sg_init_one(&status_sg, &status, sizeof(status));
 	sgs[num_out + num_in++] = &status_sg;
 
-	ret = virtqueue_add(priv->vq, sgs, num_out, num_in);
+	ret = virtqueue_add_sgs(priv->vq, sgs, num_out, num_in);
 	if (ret)
 		return ret;
 
diff --git a/drivers/hw_random/virtio-rng.c b/drivers/hw_random/virtio-rng.c
index f0a3d3cb74e7..6c283c784fb7 100644
--- a/drivers/hw_random/virtio-rng.c
+++ b/drivers/hw_random/virtio-rng.c
@@ -32,19 +32,16 @@ static int virtio_rng_read(struct hwrng *hwrng, void *data, size_t len, bool wai
 {
 	int ret;
 	unsigned int rsize;
-	unsigned char buf[BUFFER_SIZE] __aligned(4);
+	unsigned char buf[BUFFER_SIZE] __aligned(8);
 	unsigned char *ptr = data;
-	struct virtio_sg sg;
-	struct virtio_sg *sgs[1];
+	struct scatterlist sg;
 	struct virtrng_info *vi = to_virtrng_info(hwrng);
 	size_t remaining = len;
 
 	while (remaining) {
-		sg.addr = buf;
-		sg.length = min(remaining, sizeof(buf));
-		sgs[0] = &sg;
+		sg_init_one(&sg, buf, min(remaining, sizeof(buf)));
 
-		ret = virtqueue_add(vi->rng_vq, sgs, 0, 1);
+		ret = virtqueue_add_inbuf(vi->rng_vq, &sg, 1);
 		if (ret)
 			return ret;
 
diff --git a/drivers/input/virtio_input.c b/drivers/input/virtio_input.c
index 655a9051726a..cdcee556a6c7 100644
--- a/drivers/input/virtio_input.c
+++ b/drivers/input/virtio_input.c
@@ -25,9 +25,9 @@ struct virtio_input {
 static void virtinput_queue_evtbuf(struct virtio_input *vi,
 				   struct virtio_input_event *evtbuf)
 {
-	struct virtio_sg sg[1];
-	virtio_sg_init_one(sg, evtbuf, sizeof(*evtbuf));
-	virtqueue_add_inbuf(vi->evt, sg, 1);
+	struct scatterlist sg;
+	sg_init_one(&sg, evtbuf, sizeof(*evtbuf));
+	virtqueue_add_inbuf(vi->evt, &sg, 1);
 }
 
 static int virtinput_recv_events(struct virtio_input *vi)
@@ -63,7 +63,7 @@ static int virtinput_send_status(struct sound_card *beeper, unsigned freq, unsig
 {
 	struct virtio_input *vi = container_of(beeper, struct virtio_input, beeper);
 	struct virtio_input_event *stsbuf;
-	struct virtio_sg sg[1];
+	struct scatterlist sg;
 	u16 code;
 	int rc;
 
@@ -76,9 +76,9 @@ static int virtinput_send_status(struct sound_card *beeper, unsigned freq, unsig
 	stsbuf->type  = cpu_to_le16(EV_SND);
 	stsbuf->code  = cpu_to_le16(code);
 	stsbuf->value = cpu_to_le32(freq);
-	virtio_sg_init_one(sg, stsbuf, sizeof(*stsbuf));
+	sg_init_one(&sg, stsbuf, sizeof(*stsbuf));
 
-	rc = virtqueue_add_outbuf(vi->sts, sg, 1);
+	rc = virtqueue_add_outbuf(vi->sts, &sg, 1);
 	virtqueue_kick(vi->sts);
 
 	if (rc != 0)
diff --git a/drivers/net/virtio.c b/drivers/net/virtio.c
index 0c8a88530f0f..26a1dde5199a 100644
--- a/drivers/net/virtio.c
+++ b/drivers/net/virtio.c
@@ -46,18 +46,18 @@ static inline struct virtio_net_priv *to_priv(struct eth_device *edev)
 static int virtio_net_start(struct eth_device *edev)
 {
 	struct virtio_net_priv *priv = to_priv(edev);
-	struct virtio_sg sg;
-	struct virtio_sg *sgs[] = { &sg };
+	struct scatterlist sg;
 	int i;
 
 	if (!priv->rx_running) {
-		/* receive buffer length is always 1526 */
-		sg.length = VIRTIO_NET_RX_BUF_SIZE;
 
 		/* setup the receive buffer address */
 		for (i = 0; i < VIRTIO_NET_NUM_RX_BUFS; i++) {
-			sg.addr = priv->rx_buff[i];
-			virtqueue_add(priv->rx_vq, sgs, 0, 1);
+			/* receive buffer length is always 1526 */
+			sg.length = VIRTIO_NET_RX_BUF_SIZE;
+
+			sg_init_one(&sg, priv->rx_buff[i], VIRTIO_NET_RX_BUF_SIZE);
+			virtqueue_add_inbuf(priv->rx_vq, &sg, 1);
 		}
 
 		virtqueue_kick(priv->rx_vq);
@@ -72,22 +72,21 @@ static int virtio_net_start(struct eth_device *edev)
 static int virtio_net_send(struct eth_device *edev, void *packet, int length)
 {
 	struct virtio_net_priv *priv = to_priv(edev);
-	struct virtio_net_hdr_v1 hdr_v1;
-	struct virtio_net_hdr hdr;
-	struct virtio_sg hdr_sg;
-	struct virtio_sg data_sg = { packet, length };
-	struct virtio_sg *sgs[] = { &hdr_sg, &data_sg };
+	struct virtio_net_hdr_v1 hdr_v1 = {};
+	struct virtio_net_hdr hdr = {};
+	struct scatterlist sgs[2];
 	int ret;
 
+	sg_init_table(sgs, ARRAY_SIZE(sgs));
+
 	if (priv->net_hdr_len == sizeof(struct virtio_net_hdr))
-		hdr_sg.addr = &hdr;
+		sg_set_buf(&sgs[0], &hdr, priv->net_hdr_len);
 	else
-		hdr_sg.addr = &hdr_v1;
+		sg_set_buf(&sgs[0], &hdr_v1, priv->net_hdr_len);
 
-	hdr_sg.length = priv->net_hdr_len;
-	memset(hdr_sg.addr, 0, priv->net_hdr_len);
+	sg_set_buf(&sgs[1], packet, length);
 
-	ret = virtqueue_add(priv->tx_vq, sgs, 2, 0);
+	ret = virtqueue_add_outbuf(priv->tx_vq, sgs, ARRAY_SIZE(sgs));
 	if (ret)
 		return ret;
 
@@ -104,24 +103,23 @@ static int virtio_net_send(struct eth_device *edev, void *packet, int length)
 static void virtio_net_recv(struct eth_device *edev)
 {
 	struct virtio_net_priv *priv = to_priv(edev);
-	struct virtio_sg sg;
-	struct virtio_sg *sgs[] = { &sg };
+	struct scatterlist sg;
 	unsigned int len;
-	void *buf;
+	void *buf, *addr;
 
-	sg.addr = virtqueue_get_buf(priv->rx_vq, &len);
-	if (!sg.addr)
+	addr = virtqueue_get_buf(priv->rx_vq, &len);
+	if (!addr)
 		return;
 
-	sg.length = VIRTIO_NET_RX_BUF_SIZE;
+	sg_init_one(&sg, addr, VIRTIO_NET_RX_BUF_SIZE);
 
-	buf = sg.addr + priv->net_hdr_len;
+	buf = sg.address + priv->net_hdr_len;
 	len -= priv->net_hdr_len;
 
 	net_receive(edev, buf, len);
 
 	/* Put the buffer back to the rx ring */
-	virtqueue_add(priv->rx_vq, sgs, 0, 1);
+	virtqueue_add_inbuf(priv->rx_vq, &sg, 1);
 }
 
 static void virtio_net_stop(struct eth_device *dev)
diff --git a/drivers/serial/virtio_console.c b/drivers/serial/virtio_console.c
index a4adb77610f6..eb3b1158171d 100644
--- a/drivers/serial/virtio_console.c
+++ b/drivers/serial/virtio_console.c
@@ -41,15 +41,15 @@ static void put_chars(struct virtio_console *virtcons, const char *buf, int coun
 {
 	struct virtqueue *out_vq = virtcons->out_vq;
 	unsigned int len;
-	struct virtio_sg *sgs[1] = {
-		&(struct virtio_sg) { .addr = (void *)buf, .length = count }
-	};
+	struct scatterlist sg;
+
+	sg_init_one(&sg, buf, count);
 
 	/*
 	 * add_buf wants a token to identify this buffer: we hand it
 	 * any non-NULL pointer, since there's only ever one buffer.
 	 */
-	if (virtqueue_add(out_vq, sgs, 1, 0) >= 0) {
+	if (virtqueue_add_outbuf(out_vq, &sg, 1) >= 0) {
 		/* Tell Host to go! */
 		virtqueue_kick(out_vq);
 		/* Chill out until it's done with the buffer. */
@@ -71,12 +71,12 @@ static void virtcons_putc(struct console_device *cdev, char c)
  */
 static void add_inbuf(struct virtio_console *virtcons)
 {
-	struct virtio_sg *sgs[1] = { &(struct virtio_sg) {
-		.addr = virtcons->inbuf, .length = sizeof(virtcons->inbuf) }
-	};
+	struct scatterlist sg;
+
+	sg_init_one(&sg, virtcons->inbuf, sizeof(virtcons->inbuf));
 
 	/* We should always be able to add one buffer to an empty queue. */
-	if (virtqueue_add(virtcons->in_vq, sgs, 0, 1) < 0)
+	if (virtqueue_add_inbuf(virtcons->in_vq, &sg, 1) < 0)
 		BUG();
 	virtqueue_kick(virtcons->in_vq);
 }
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index b0b402834aca..cd3677ff73f9 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -29,10 +29,12 @@ static inline struct device *vring_dma_dev(const struct virtqueue *vq)
 
 /* Map one sg entry. */
 static dma_addr_t vring_map_one_sg(struct virtqueue *vq,
-				   struct virtio_sg *sg,
+				   struct scatterlist *sg,
 				   enum dma_data_direction direction)
 {
-	return dma_map_single(vring_dma_dev(vq), sg->addr, sg->length, direction);
+	return dma_map_single(vring_dma_dev(vq),
+			      sg->address, sg->length,
+			      direction);
 }
 
 static int vring_mapping_error(struct virtqueue *vq,
@@ -55,11 +57,12 @@ static void vring_unmap_one(struct virtqueue *vq,
 		       DMA_FROM_DEVICE : DMA_TO_DEVICE);
 }
 
-int virtqueue_add(struct virtqueue *vq, struct virtio_sg *sgs[],
-		  unsigned int out_sgs, unsigned int in_sgs)
+int virtqueue_add_sgs(struct virtqueue *vq, struct scatterlist *sgs[],
+		      unsigned int out_sgs, unsigned int in_sgs)
 {
 	struct vring_desc *desc;
 	unsigned int total_sg = out_sgs + in_sgs;
+	struct scatterlist *sg;
 	unsigned int i, err_idx, n, avail, descs_used, uninitialized_var(prev);
 	int head;
 
@@ -85,33 +88,37 @@ int virtqueue_add(struct virtqueue *vq, struct virtio_sg *sgs[],
 	}
 
 	for (n = 0; n < out_sgs; n++) {
-		struct virtio_sg *sg = sgs[n];
-		dma_addr_t addr = vring_map_one_sg(vq, sg, DMA_TO_DEVICE);
-		if (vring_mapping_error(vq, addr))
-			goto unmap_release;
+		for (sg = sgs[n]; sg; sg = sg_next(sg)) {
+			dma_addr_t addr = vring_map_one_sg(vq, sg, DMA_TO_DEVICE);
+			if (vring_mapping_error(vq, addr))
+				goto unmap_release;
 
+			desc[i].flags = cpu_to_virtio16(vq->vdev, VRING_DESC_F_NEXT);
+			desc[i].addr = cpu_to_virtio64(vq->vdev, addr);
+			desc[i].len = cpu_to_virtio32(vq->vdev, sg->length);
 
-		desc[i].flags = cpu_to_virtio16(vq->vdev, VRING_DESC_F_NEXT);
-		desc[i].addr = cpu_to_virtio64(vq->vdev, addr);
-		desc[i].len = cpu_to_virtio32(vq->vdev, sg->length);
-
-		prev = i;
-		i = virtio16_to_cpu(vq->vdev, desc[i].next);
+			prev = i;
+			i = virtio16_to_cpu(vq->vdev, desc[i].next);
+		}
 	}
+
 	for (; n < (out_sgs + in_sgs); n++) {
-		struct virtio_sg *sg = sgs[n];
-		dma_addr_t addr = vring_map_one_sg(vq, sg, DMA_FROM_DEVICE);
-		if (vring_mapping_error(vq, addr))
-			goto unmap_release;
+		for (sg = sgs[n]; sg; sg = sg_next(sg)) {
+			struct scatterlist *sg = sgs[n];
+			dma_addr_t addr = vring_map_one_sg(vq, sg, DMA_FROM_DEVICE);
+			if (vring_mapping_error(vq, addr))
+				goto unmap_release;
 
-		desc[i].flags = cpu_to_virtio16(vq->vdev, VRING_DESC_F_NEXT |
-						VRING_DESC_F_WRITE);
-		desc[i].addr = cpu_to_virtio64(vq->vdev, addr);
-		desc[i].len = cpu_to_virtio32(vq->vdev, sg->length);
+			desc[i].flags = cpu_to_virtio16(vq->vdev, VRING_DESC_F_NEXT |
+							VRING_DESC_F_WRITE);
+			desc[i].addr = cpu_to_virtio64(vq->vdev, addr);
+			desc[i].len = cpu_to_virtio32(vq->vdev, sg->length);
 
-		prev = i;
-		i = virtio16_to_cpu(vq->vdev, desc[i].next);
+			prev = i;
+			i = virtio16_to_cpu(vq->vdev, desc[i].next);
+		}
 	}
+
 	/* Last one doesn't continue */
 	desc[prev].flags &= cpu_to_virtio16(vq->vdev, ~VRING_DESC_F_NEXT);
 
diff --git a/include/linux/virtio.h b/include/linux/virtio.h
index a4a54531ca63..3d4c88336055 100644
--- a/include/linux/virtio.h
+++ b/include/linux/virtio.h
@@ -13,24 +13,6 @@ struct virtio_device_id {
 };
 #define VIRTIO_DEV_ANY_ID	0xffffffff
 
-/**
- * virtio scatter-gather struct
- *
- * @addr:		sg buffer address
- * @lengh:		sg buffer length
- */
-struct virtio_sg {
-	void *addr;
-	size_t length;
-};
-
-static inline void virtio_sg_init_one(struct virtio_sg *sg,
-				      void *addr, size_t length)
-{
-	sg[0].addr = addr;
-	sg[0].length = length;
-}
-
 struct virtio_config_ops;
 
 /**
diff --git a/include/linux/virtio_ring.h b/include/linux/virtio_ring.h
index bdef47b0fa6c..1fa9c2ed7428 100644
--- a/include/linux/virtio_ring.h
+++ b/include/linux/virtio_ring.h
@@ -10,6 +10,7 @@
 #define _LINUX_VIRTIO_RING_H
 
 #include <linux/virtio_types.h>
+#include <linux/scatterlist.h>
 
 /* This marks a buffer as continuing via the next field */
 #define VRING_DESC_F_NEXT		1
@@ -161,10 +162,8 @@ static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old)
 	return (__u16)(new_idx - event_idx - 1) < (__u16)(new_idx - old);
 }
 
-struct virtio_sg;
-
 /**
- * virtqueue_add - expose buffers to other end
+ * virtqueue_add_sgs - expose buffers to other end
  *
  * @vq:		the struct virtqueue we're talking about
  * @sgs:	array of terminated scatterlists
@@ -177,8 +176,8 @@ struct virtio_sg;
  *
  * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
  */
-int virtqueue_add(struct virtqueue *vq, struct virtio_sg *sgs[],
-		  unsigned int out_sgs, unsigned int in_sgs);
+int virtqueue_add_sgs(struct virtqueue *vq, struct scatterlist *sgs[],
+		      unsigned int out_sgs, unsigned int in_sgs);
 
 /**
  * virtqueue_add_outbuf - expose output buffers to other end
@@ -192,9 +191,9 @@ int virtqueue_add(struct virtqueue *vq, struct virtio_sg *sgs[],
  * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
  */
 static inline int virtqueue_add_outbuf(struct virtqueue *vq,
-                                      struct virtio_sg *sg, unsigned int num)
+				       struct scatterlist *sg, unsigned int num)
 {
-	return virtqueue_add(vq, &sg, num, 0);
+	return virtqueue_add_sgs(vq, &sg, num, 0);
 }
 
 /**
@@ -209,9 +208,9 @@ static inline int virtqueue_add_outbuf(struct virtqueue *vq,
  * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
  */
 static inline int virtqueue_add_inbuf(struct virtqueue *vq,
-                                     struct virtio_sg *sg, unsigned int num)
+				      struct scatterlist *sg, unsigned int num)
 {
-	return virtqueue_add(vq, &sg, 0, num);
+	return virtqueue_add_sgs(vq, &sg, 0, num);
 }
 
 /**
@@ -219,7 +218,7 @@ static inline int virtqueue_add_inbuf(struct virtqueue *vq,
  *
  * @vq:		the struct virtqueue
  *
- * After one or more virtqueue_add() calls, invoke this to kick
+ * After one or more virtqueue_add_sgs() calls, invoke this to kick
  * the other side.
  *
  * Caller must ensure we don't call this with other virtqueue
-- 
2.39.5




^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 3/3] virtio: add token data parameter to virtqueue_add_sgs
  2025-02-14 10:12 [PATCH 1/3] lib: add scatterlist abstraction Ahmad Fatoum
  2025-02-14 10:12 ` [PATCH 2/3] virtio: replace virtio_sg with common scatterlist Ahmad Fatoum
@ 2025-02-14 10:12 ` Ahmad Fatoum
  2025-02-17  9:48 ` [PATCH 1/3] lib: add scatterlist abstraction Sascha Hauer
  2 siblings, 0 replies; 4+ messages in thread
From: Ahmad Fatoum @ 2025-02-14 10:12 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

In Linux, virtqueue_get_buf doesn't return the buffer, but the cookie
value specified when the entry was first added to the virtqueue via
virtqueue_add_sgs.

To make it easier to port Linux drivers that keep around metadata for
requests, adopt the Linux API.

No functional change expected.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/block/virtio_blk.c      |  2 +-
 drivers/hw_random/virtio-rng.c  |  2 +-
 drivers/input/virtio_input.c    |  4 ++--
 drivers/net/virtio.c            |  6 +++---
 drivers/serial/virtio_console.c |  4 ++--
 drivers/virtio/virtio_ring.c    | 24 +++++++++++++-----------
 include/linux/virtio_ring.h     | 24 ++++++++++++++++++------
 7 files changed, 40 insertions(+), 26 deletions(-)

diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 3ee7972fe859..11111f174142 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -49,7 +49,7 @@ static int virtio_blk_do_req(struct virtio_blk_priv *priv, void *buffer,
 	sg_init_one(&status_sg, &status, sizeof(status));
 	sgs[num_out + num_in++] = &status_sg;
 
-	ret = virtqueue_add_sgs(priv->vq, sgs, num_out, num_in);
+	ret = virtqueue_add_sgs(priv->vq, sgs, num_out, num_in, &out_hdr);
 	if (ret)
 		return ret;
 
diff --git a/drivers/hw_random/virtio-rng.c b/drivers/hw_random/virtio-rng.c
index 6c283c784fb7..1ce321352935 100644
--- a/drivers/hw_random/virtio-rng.c
+++ b/drivers/hw_random/virtio-rng.c
@@ -41,7 +41,7 @@ static int virtio_rng_read(struct hwrng *hwrng, void *data, size_t len, bool wai
 	while (remaining) {
 		sg_init_one(&sg, buf, min(remaining, sizeof(buf)));
 
-		ret = virtqueue_add_inbuf(vi->rng_vq, &sg, 1);
+		ret = virtqueue_add_inbuf(vi->rng_vq, &sg, 1, buf);
 		if (ret)
 			return ret;
 
diff --git a/drivers/input/virtio_input.c b/drivers/input/virtio_input.c
index cdcee556a6c7..6e73e7873986 100644
--- a/drivers/input/virtio_input.c
+++ b/drivers/input/virtio_input.c
@@ -27,7 +27,7 @@ static void virtinput_queue_evtbuf(struct virtio_input *vi,
 {
 	struct scatterlist sg;
 	sg_init_one(&sg, evtbuf, sizeof(*evtbuf));
-	virtqueue_add_inbuf(vi->evt, &sg, 1);
+	virtqueue_add_inbuf(vi->evt, &sg, 1, evtbuf);
 }
 
 static int virtinput_recv_events(struct virtio_input *vi)
@@ -78,7 +78,7 @@ static int virtinput_send_status(struct sound_card *beeper, unsigned freq, unsig
 	stsbuf->value = cpu_to_le32(freq);
 	sg_init_one(&sg, stsbuf, sizeof(*stsbuf));
 
-	rc = virtqueue_add_outbuf(vi->sts, &sg, 1);
+	rc = virtqueue_add_outbuf(vi->sts, &sg, 1, stsbuf);
 	virtqueue_kick(vi->sts);
 
 	if (rc != 0)
diff --git a/drivers/net/virtio.c b/drivers/net/virtio.c
index 26a1dde5199a..73afbc7645bd 100644
--- a/drivers/net/virtio.c
+++ b/drivers/net/virtio.c
@@ -57,7 +57,7 @@ static int virtio_net_start(struct eth_device *edev)
 			sg.length = VIRTIO_NET_RX_BUF_SIZE;
 
 			sg_init_one(&sg, priv->rx_buff[i], VIRTIO_NET_RX_BUF_SIZE);
-			virtqueue_add_inbuf(priv->rx_vq, &sg, 1);
+			virtqueue_add_inbuf(priv->rx_vq, &sg, 1, priv->rx_buff[i]);
 		}
 
 		virtqueue_kick(priv->rx_vq);
@@ -86,7 +86,7 @@ static int virtio_net_send(struct eth_device *edev, void *packet, int length)
 
 	sg_set_buf(&sgs[1], packet, length);
 
-	ret = virtqueue_add_outbuf(priv->tx_vq, sgs, ARRAY_SIZE(sgs));
+	ret = virtqueue_add_outbuf(priv->tx_vq, sgs, ARRAY_SIZE(sgs), &sgs[0].address);
 	if (ret)
 		return ret;
 
@@ -119,7 +119,7 @@ static void virtio_net_recv(struct eth_device *edev)
 	net_receive(edev, buf, len);
 
 	/* Put the buffer back to the rx ring */
-	virtqueue_add_inbuf(priv->rx_vq, &sg, 1);
+	virtqueue_add_inbuf(priv->rx_vq, &sg, 1, addr);
 }
 
 static void virtio_net_stop(struct eth_device *dev)
diff --git a/drivers/serial/virtio_console.c b/drivers/serial/virtio_console.c
index eb3b1158171d..09249aef7a14 100644
--- a/drivers/serial/virtio_console.c
+++ b/drivers/serial/virtio_console.c
@@ -49,7 +49,7 @@ static void put_chars(struct virtio_console *virtcons, const char *buf, int coun
 	 * add_buf wants a token to identify this buffer: we hand it
 	 * any non-NULL pointer, since there's only ever one buffer.
 	 */
-	if (virtqueue_add_outbuf(out_vq, &sg, 1) >= 0) {
+	if (virtqueue_add_outbuf(out_vq, &sg, 1, (void *)buf) >= 0) {
 		/* Tell Host to go! */
 		virtqueue_kick(out_vq);
 		/* Chill out until it's done with the buffer. */
@@ -76,7 +76,7 @@ static void add_inbuf(struct virtio_console *virtcons)
 	sg_init_one(&sg, virtcons->inbuf, sizeof(virtcons->inbuf));
 
 	/* We should always be able to add one buffer to an empty queue. */
-	if (virtqueue_add_inbuf(virtcons->in_vq, &sg, 1) < 0)
+	if (virtqueue_add_inbuf(virtcons->in_vq, &sg, 1, virtcons->inbuf) < 0)
 		BUG();
 	virtqueue_kick(virtcons->in_vq);
 }
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index cd3677ff73f9..29d7bb9ef662 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -58,7 +58,8 @@ static void vring_unmap_one(struct virtqueue *vq,
 }
 
 int virtqueue_add_sgs(struct virtqueue *vq, struct scatterlist *sgs[],
-		      unsigned int out_sgs, unsigned int in_sgs)
+		      unsigned int out_sgs, unsigned int in_sgs,
+		      void *data)
 {
 	struct vring_desc *desc;
 	unsigned int total_sg = out_sgs + in_sgs;
@@ -67,6 +68,7 @@ int virtqueue_add_sgs(struct virtqueue *vq, struct scatterlist *sgs[],
 	int head;
 
 	WARN_ON(total_sg == 0);
+	BUG_ON(data == NULL);
 
 	head = vq->free_head;
 
@@ -128,6 +130,9 @@ int virtqueue_add_sgs(struct virtqueue *vq, struct scatterlist *sgs[],
 	/* Update free pointer */
 	vq->free_head = i;
 
+	/* Store token. */
+	vq->desc_state[head].data = data;
+
 	/*
 	 * Put entry in available array (but don't update avail->idx
 	 * until they do sync).
@@ -204,6 +209,9 @@ static void detach_buf(struct virtqueue *vq, unsigned int head)
 	unsigned int i;
 	__virtio16 nextflag = cpu_to_virtio16(vq->vdev, VRING_DESC_F_NEXT);
 
+	/* Clear data ptr. */
+	vq->desc_state[head].data = NULL;
+
 	/* Put back on free list: unmap first-level descriptors and find end */
 	i = head;
 
@@ -230,6 +238,7 @@ void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len)
 {
 	unsigned int i;
 	u16 last_used;
+	void *ret;
 
 	if (!more_used(vq)) {
 		vq_debug(vq, "No more buffers in queue\n");
@@ -252,6 +261,7 @@ void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len)
 		return NULL;
 	}
 
+	ret = vq->desc_state[i].data;
 	detach_buf(vq, i);
 	vq->last_used_idx++;
 	/*
@@ -263,8 +273,7 @@ void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len)
 		virtio_store_mb(&vring_used_event(&vq->vring),
 				cpu_to_virtio16(vq->vdev, vq->last_used_idx));
 
-	return IOMEM((uintptr_t)virtio64_to_cpu(vq->vdev,
-						  vq->vring.desc[i].addr));
+	return ret;
 }
 
 static struct virtqueue *__vring_new_virtqueue(unsigned int index,
@@ -274,7 +283,7 @@ static struct virtqueue *__vring_new_virtqueue(unsigned int index,
 	unsigned int i;
 	struct virtqueue *vq;
 
-	vq = malloc(sizeof(*vq));
+	vq = calloc(1, struct_size(vq, desc_state, vring.num));
 	if (!vq)
 		return NULL;
 
@@ -282,12 +291,6 @@ static struct virtqueue *__vring_new_virtqueue(unsigned int index,
 	vq->index = index;
 	vq->num_free = vring.num;
 	vq->vring = vring;
-	vq->last_used_idx = 0;
-	vq->avail_flags_shadow = 0;
-	vq->avail_idx_shadow = 0;
-	vq->num_added = 0;
-	vq->queue_dma_addr = 0;
-	vq->queue_size_in_bytes = 0;
 	list_add_tail(&vq->list, &vdev->vqs);
 
 	vq->event = virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX);
@@ -299,7 +302,6 @@ static struct virtqueue *__vring_new_virtqueue(unsigned int index,
 				vq->avail_flags_shadow);
 
 	/* Put everything in free lists */
-	vq->free_head = 0;
 	for (i = 0; i < vring.num - 1; i++)
 		vq->vring.desc[i].next = cpu_to_virtio16(vdev, i + 1);
 
diff --git a/include/linux/virtio_ring.h b/include/linux/virtio_ring.h
index 1fa9c2ed7428..04a2ad0cb1f9 100644
--- a/include/linux/virtio_ring.h
+++ b/include/linux/virtio_ring.h
@@ -82,6 +82,10 @@ struct vring {
 	struct vring_used *used;
 };
 
+struct vring_desc_state_split {
+	void *data;			/* Data for callback. */
+};
+
 /**
  * virtqueue - a queue to register buffers for sending or receiving.
  *
@@ -96,6 +100,7 @@ struct vring {
  * @last_used_idx: last used index we've seen
  * @avail_flags_shadow: last written value to avail->flags
  * @avail_idx_shadow: last written value to avail->idx in guest byte order
+ * @desc_state: cookie data associated with descritptors
  */
 struct virtqueue {
 	struct list_head list;
@@ -111,6 +116,7 @@ struct virtqueue {
 	u16 avail_idx_shadow;
 	dma_addr_t queue_dma_addr;
 	size_t queue_size_in_bytes;
+	struct vring_desc_state_split desc_state[];
 };
 
 /*
@@ -170,6 +176,7 @@ static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old)
  * @out_sgs:	the number of scatterlists readable by other side
  * @in_sgs:	the number of scatterlists which are writable
  *		(after readable ones)
+ * @data:	the token identifying the buffer.
  *
  * Caller must ensure we don't call this with other virtqueue operations
  * at the same time (except where noted).
@@ -177,13 +184,15 @@ static inline int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old)
  * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
  */
 int virtqueue_add_sgs(struct virtqueue *vq, struct scatterlist *sgs[],
-		      unsigned int out_sgs, unsigned int in_sgs);
+		      unsigned int out_sgs, unsigned int in_sgs,
+		      void *data);
 
 /**
  * virtqueue_add_outbuf - expose output buffers to other end
  * @vq: the struct virtqueue we're talking about.
  * @sg: scatterlist (must be well-formed and terminated!)
  * @num: the number of entries in @sg readable by other side
+ * @data: the token identifying the buffer.
  *
  * Caller must ensure we don't call this with other virtqueue operations
  * at the same time (except where noted).
@@ -191,9 +200,10 @@ int virtqueue_add_sgs(struct virtqueue *vq, struct scatterlist *sgs[],
  * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
  */
 static inline int virtqueue_add_outbuf(struct virtqueue *vq,
-				       struct scatterlist *sg, unsigned int num)
+				       struct scatterlist *sg, unsigned int num,
+				       void *data)
 {
-	return virtqueue_add_sgs(vq, &sg, num, 0);
+	return virtqueue_add_sgs(vq, &sg, num, 0, data);
 }
 
 /**
@@ -201,6 +211,7 @@ static inline int virtqueue_add_outbuf(struct virtqueue *vq,
  * @vq: the struct virtqueue we're talking about.
  * @sg: scatterlist (must be well-formed and terminated!)
  * @num: the number of entries in @sg writable by other side
+ * @data: the token identifying the buffer.
  *
  * Caller must ensure we don't call this with other virtqueue operations
  * at the same time (except where noted).
@@ -208,9 +219,10 @@ static inline int virtqueue_add_outbuf(struct virtqueue *vq,
  * Returns zero or a negative error (ie. ENOSPC, ENOMEM, EIO).
  */
 static inline int virtqueue_add_inbuf(struct virtqueue *vq,
-				      struct scatterlist *sg, unsigned int num)
+				      struct scatterlist *sg, unsigned int num,
+				      void *data)
 {
-	return virtqueue_add_sgs(vq, &sg, 0, num);
+	return virtqueue_add_sgs(vq, &sg, 0, num, data);
 }
 
 /**
@@ -240,7 +252,7 @@ void virtqueue_kick(struct virtqueue *vq);
  * Caller must ensure we don't call this with other virtqueue
  * operations at the same time (except where noted).
  *
- * Returns NULL if there are no used buffers, or the memory buffer
+ * Returns NULL if there are no used buffers, or the "data" token
  * handed to virtqueue_add_*().
  */
 void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len);
-- 
2.39.5




^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH 1/3] lib: add scatterlist abstraction
  2025-02-14 10:12 [PATCH 1/3] lib: add scatterlist abstraction Ahmad Fatoum
  2025-02-14 10:12 ` [PATCH 2/3] virtio: replace virtio_sg with common scatterlist Ahmad Fatoum
  2025-02-14 10:12 ` [PATCH 3/3] virtio: add token data parameter to virtqueue_add_sgs Ahmad Fatoum
@ 2025-02-17  9:48 ` Sascha Hauer
  2 siblings, 0 replies; 4+ messages in thread
From: Sascha Hauer @ 2025-02-17  9:48 UTC (permalink / raw)
  To: barebox, Ahmad Fatoum


On Fri, 14 Feb 2025 11:12:04 +0100, Ahmad Fatoum wrote:
> With barebox using 1:1 memory mapping whenever possible, scatter-gather
> DMA seems not that important. Many Linux APIs use struct scatterlist
> as parameters though, so adding a basic support in barebox will make
> easier to port these drivers.
> 
> 

Applied, thanks!

[1/3] lib: add scatterlist abstraction
      https://git.pengutronix.de/cgit/barebox/commit/?id=118ef08339cc (link may not be stable)
[2/3] virtio: replace virtio_sg with common scatterlist
      https://git.pengutronix.de/cgit/barebox/commit/?id=2336406d2b91 (link may not be stable)
[3/3] virtio: add token data parameter to virtqueue_add_sgs
      https://git.pengutronix.de/cgit/barebox/commit/?id=2bafe34d2f68 (link may not be stable)

Best regards,
-- 
Sascha Hauer <s.hauer@pengutronix.de>




^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2025-02-17  9:59 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-02-14 10:12 [PATCH 1/3] lib: add scatterlist abstraction Ahmad Fatoum
2025-02-14 10:12 ` [PATCH 2/3] virtio: replace virtio_sg with common scatterlist Ahmad Fatoum
2025-02-14 10:12 ` [PATCH 3/3] virtio: add token data parameter to virtqueue_add_sgs Ahmad Fatoum
2025-02-17  9:48 ` [PATCH 1/3] lib: add scatterlist abstraction Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox