Linux SCSI subsystem development
 help / color / mirror / Atom feed
From: "Michael S. Tsirkin" <mst@redhat.com>
To: alistair23@gmail.com
Cc: eperezma@redhat.com, linux-kernel@vger.kernel.org,
	xuanzhuo@linux.alibaba.com, jasowangio@gmail.com,
	linux-scsi@vger.kernel.org, mkp@kernel.org,
	virtualization@lists.linux.dev,
	James.Bottomley@hansenpartnership.com, alistair@alistair23.me,
	Alistair Francis <alistair.francis@wdc.com>,
	graf@amazon.com
Subject: Re: [PATCH 2/2] scsi: Initial commit of VirtIO PCIe Endpoint Driver
Date: Tue, 1 Sep 2026 07:04:41 -0400	[thread overview]
Message-ID: <20260901044553-mutt-send-email-mst@kernel.org> (raw)
In-Reply-To: <20260901014650.2728658-3-alistair.francis@wdc.com>

On Tue, Sep 01, 2026 at 11:46:50AM +1000, alistair23@gmail.com wrote:
> From: Alistair Francis <alistair.francis@wdc.com>
> 
> This patch adds a VirtIO SCSI endpoint build on top of the VirtIO PCIe
> endpoint. This is a similar approach to the NVMe PCIe Endpoint
> (drivers/nvme/target/pci-epf.c) but for SCSI.
> 
> This does end up being somewhat similar to the pci-epf.c code, but
> re-written for SCSI.
> 
> This approach allows a PCIe Endpoint device (tested on a
> radxa-rock5b) to setup what appears to be a SCSI device, using an
> existing SCSI backend (tested using scsi_debug).
> 
> At this point a host can connect over PCIe, ensure virtio_pci and
> virtio_scsi is loaded and on PCIe rescan will see a scsi device.
> 
> There are two main pain points with this approach though:
>  1. We have to use the Legacy SCSI VirtIO driver. This is because the
>     Raxda Rock5b (and AFAIK all PCIe Endpoint hardware) can't add
>     capabilities. So we can't advertise the VirtIO Common configuration
>     capability, which means we can't be a modern VirtIO SCSI device.
> 
>     This is unfortunate, but there doesn't seem to be any way around
>     this, at least with the current hardware.

This was discussed on the virtio list. The way around this is
to change virtio spec to allow common configuration
in memory. I don't think a patch like this was ever posted -
want to try?


....



> +
> +static int scsit_pci_epf_read_desc(struct scsit_pci_epf_vq *vq,
> +				   u16 idx, struct vring_desc *desc)
> +{
> +	void __iomem *p;
> +
> +	if (idx >= vq->depth)
> +		return -EINVAL;
> +
> +	p = vq->desc_map.virt_addr + (size_t)idx * sizeof(struct vring_desc);
> +	desc->addr = cpu_to_le64(readq(p + offsetof(struct vring_desc, addr)));
> +	desc->len = cpu_to_le32(readl(p + offsetof(struct vring_desc, len)));
> +	desc->flags = cpu_to_le16(readw(p + offsetof(struct vring_desc, flags)));
> +	desc->next = cpu_to_le16(readw(p + offsetof(struct vring_desc, next)));
> +
> +	return 0;
> +}
> +
> +static int scsit_pci_epf_read_indirect(struct scsit_pci_epf_ctrl *ctrl,
> +				       const struct vring_desc *parent,
> +				       struct vring_desc **out_descs,
> +				       unsigned int *out_n)
> +{
> +	struct vring_desc *descs;
> +	u32 len = le32_to_cpu(parent->len);
> +	unsigned int n;
> +	int ret;
> +
> +	if (len % sizeof(struct vring_desc) || !len)
> +		return -EINVAL;
> +
> +	n = len / sizeof(struct vring_desc);
> +	descs = kmalloc_array(n, sizeof(*descs), GFP_KERNEL);
> +	if (!descs)
> +		return -ENOMEM;
> +
> +	ret = scsit_pci_epf_transfer(ctrl, descs, le64_to_cpu(parent->addr),
> +				     len, DMA_FROM_DEVICE);
> +	if (ret) {
> +		kfree(descs);
> +		return ret;
> +	}
> +
> +	*out_descs = descs;
> +	*out_n = n;
> +	return 0;
> +}
> +
> +struct scsit_pci_epf_chain {
> +	struct vring_desc	*ro;
> +	unsigned int		nr_ro;
> +	u32			ro_len;
> +	struct vring_desc	*wo;
> +	unsigned int		nr_wo;
> +	u32			wo_len;
> +
> +	struct vring_desc	*indirect;
> +};
> +
> +static void scsit_pci_epf_chain_free(struct scsit_pci_epf_chain *c)
> +{
> +	kfree(c->ro);
> +	c->ro = NULL;
> +	kfree(c->wo);
> +	c->wo = NULL;
> +	kfree(c->indirect);
> +	c->indirect = NULL;
> +}
> +
> +static int scsit_pci_epf_walk_chain(struct scsit_pci_epf_ctrl *ctrl,
> +				    struct scsit_pci_epf_vq *vq,
> +				    u16 head_idx,
> +				    struct scsit_pci_epf_chain *out)
> +{
> +	struct vring_desc desc, *descs = NULL;
> +	unsigned int n = 0, i = 0, cap_ro = 8, cap_wo = 8;
> +	bool in_indirect = false;
> +	int ret;
> +
> +	memset(out, 0, sizeof(*out));
> +
> +	out->ro = kmalloc_array(cap_ro, sizeof(*out->ro), GFP_KERNEL);
> +	out->wo = kmalloc_array(cap_wo, sizeof(*out->wo), GFP_KERNEL);
> +	if (!out->ro || !out->wo) {
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	ret = scsit_pci_epf_read_desc(vq, head_idx, &desc);
> +	if (ret)
> +		goto err;
> +
> +	/* Reject obviously-invalid head descriptors */
> +	if (!le64_to_cpu(desc.addr) && !le32_to_cpu(desc.len) &&
> +	    !le16_to_cpu(desc.flags)) {
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	for (;;) {
> +		u16 flags = le16_to_cpu(desc.flags);
> +
> +		if (!in_indirect && (flags & VRING_DESC_F_INDIRECT)) {
> +			ret = scsit_pci_epf_read_indirect(ctrl, &desc,
> +							  &descs, &n);
> +			if (ret)
> +				goto err;
> +			out->indirect = descs;
> +			in_indirect = true;
> +			i = 0;
> +			desc = descs[0];
> +			continue;
> +		}
> +
> +		if (flags & VRING_DESC_F_WRITE) {
> +			if (out->nr_wo == cap_wo) {
> +				cap_wo *= 2;
> +				out->wo = krealloc_array(out->wo, cap_wo,
> +						sizeof(*out->wo), GFP_KERNEL);
> +				if (!out->wo) {
> +					ret = -ENOMEM;
> +					goto err;
> +				}
> +			}
> +			out->wo[out->nr_wo++] = desc;
> +			out->wo_len += le32_to_cpu(desc.len);
> +		} else {
> +			/* Read-only descriptors must precede write-only. */
> +			if (out->nr_wo) {
> +				ret = -EINVAL;
> +				goto err;
> +			}
> +			if (out->nr_ro == cap_ro) {
> +				cap_ro *= 2;
> +				out->ro = krealloc_array(out->ro, cap_ro,
> +						sizeof(*out->ro), GFP_KERNEL);
> +				if (!out->ro) {
> +					ret = -ENOMEM;
> +					goto err;
> +				}
> +			}
> +			out->ro[out->nr_ro++] = desc;
> +			out->ro_len += le32_to_cpu(desc.len);
> +		}
> +
> +		if (!(flags & VRING_DESC_F_NEXT))
> +			break;
> +
> +		if (in_indirect) {
> +			i = le16_to_cpu(desc.next);
> +			if (i >= n) {
> +				ret = -EINVAL;
> +				goto err;
> +			}
> +			desc = descs[i];
> +		} else {
> +			u16 next = le16_to_cpu(desc.next);
> +
> +			ret = scsit_pci_epf_read_desc(vq, next, &desc);
> +			if (ret)
> +				goto err;
> +		}
> +	}
> +
> +	return 0;
> +
> +err:
> +	scsit_pci_epf_chain_free(out);
> +	return ret;
> +}
> +





> +static int scsit_pci_epf_chain_to_segs(struct scsit_pci_epf_cmd *cmd,
> +				       const struct vring_desc *descs,
> +				       unsigned int n)
> +{
> +	struct scsit_pci_epf_segment *seg;
> +	unsigned int i;
> +	int ret;
> +
> +	if (n == 0)
> +		return 0;
> +
> +	if (n == 1) {
> +		cmd->nr_data_segs = 1;
> +		cmd->data_segs = &cmd->data_seg;
> +		seg = &cmd->data_segs[0];
> +		seg->pci_addr = le64_to_cpu(descs[0].addr);
> +		seg->length = le32_to_cpu(descs[0].len);
> +		return 0;
> +	}
> +
> +	ret = scsit_pci_epf_alloc_cmd_data_segs(cmd, n);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < n; i++) {
> +		seg = &cmd->data_segs[i];
> +		seg->pci_addr = le64_to_cpu(descs[i].addr);
> +		seg->length = le32_to_cpu(descs[i].len);
> +	}
> +
> +	return 0;
> +}
> +
> +static int scsit_pci_epf_alloc_cmd_data_buf(struct scsit_pci_epf_cmd *cmd)
> +{
> +	struct scsit_pci_epf_ctrl *ctrl = cmd->ctrl;
> +	struct scsit_pci_epf_segment *seg;
> +	struct scatterlist *sg;
> +	int ret, i;
> +
> +	if (cmd->se_cmd.data_length > ctrl->mdts)
> +		return -EINVAL;
> +
> +	if (cmd->nr_data_segs == 1) {
> +		sg_init_table(&cmd->data_sgl, 1);
> +		cmd->data_sgt.sgl = &cmd->data_sgl;
> +		cmd->data_sgt.nents = 1;
> +		cmd->data_sgt.orig_nents = 1;
> +	} else {
> +		ret = sg_alloc_table(&cmd->data_sgt, cmd->nr_data_segs,
> +				     GFP_KERNEL);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	for_each_sgtable_sg(&cmd->data_sgt, sg, i) {
> +		seg = &cmd->data_segs[i];
> +		seg->buf = kmalloc(seg->length, GFP_KERNEL);
> +		if (!seg->buf)
> +			return -ENOMEM;
> +		sg_set_buf(sg, seg->buf, seg->length);
> +	}
> +
> +	return 0;
> +}
> +
> +static void scsit_pci_epf_post_used(struct scsit_pci_epf_vq *vq,
> +				    u16 head_idx, u32 len)
> +{
> +	void __iomem *ring = vq->used_map.virt_addr +
> +			     offsetof(struct vring_used, ring) +
> +			     (size_t)(vq->next_used_idx & (vq->depth - 1)) *
> +			     sizeof(struct vring_used_elem);
> +
> +	writel(head_idx, ring + offsetof(struct vring_used_elem, id));
> +	writel(len, ring + offsetof(struct vring_used_elem, len));
> +
> +	vq->next_used_idx++;
> +	writew(vq->next_used_idx,
> +	       vq->used_map.virt_addr + offsetof(struct vring_used, idx));
> +}



So this is a reimplementation of virtio, and a weird one, for example
features are legacy but format is LE like modern. Packed ring is
not implemented.  Uses strongly ordered ops for everything.

I don't have specific advice but there is some similarity here
to what Alex Graf is doing.


And I want to see a spec decumenting how all this is supposed
to work, please, and not just an ad hoc implementation.



-- 
MST


  parent reply	other threads:[~2026-09-01 11:04 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-01  1:46 [PATCH 0/2] scsi: Initial commit of VirtIO PCIe Endpoint Driver alistair23
2026-09-01  1:46 ` [PATCH 1/2] virtio_pci: Add a quirk to force DMA Map API for certain legacy devices alistair23
2026-09-01  8:43   ` Michael S. Tsirkin
2026-09-01  1:46 ` [PATCH 2/2] scsi: Initial commit of VirtIO PCIe Endpoint Driver alistair23
2026-09-01  2:06   ` sashiko-bot
2026-09-01  9:03   ` Niklas Cassel
2026-09-01 11:04   ` Michael S. Tsirkin [this message]
2026-09-01  8:35 ` [PATCH 0/2] " Niklas Cassel
2026-09-01  9:20 ` Damien Le Moal

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260901044553-mutt-send-email-mst@kernel.org \
    --to=mst@redhat.com \
    --cc=James.Bottomley@hansenpartnership.com \
    --cc=alistair.francis@wdc.com \
    --cc=alistair23@gmail.com \
    --cc=alistair@alistair23.me \
    --cc=eperezma@redhat.com \
    --cc=graf@amazon.com \
    --cc=jasowangio@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-scsi@vger.kernel.org \
    --cc=mkp@kernel.org \
    --cc=virtualization@lists.linux.dev \
    --cc=xuanzhuo@linux.alibaba.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox