On Tue, Sep 01, 2026 at 11:46:50AM +1000, [email protected] wrote:
> From: Alistair Francis <[email protected]>
> 
> 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.
> 
>  2. We have to pin scsit_pci_epf_poll_cfg_thread() on a CPU in order to
>     respond fast enough to the host. This means we effectivly burn a CPU
>     to read and write some values. But as there are no intterupts
>     generated on these events and we need to be very quick there isn't
>     another option.
> 
> Assisted-by: Devin:claude-opus-4-8
> Signed-off-by: Alistair Francis <[email protected]>
> ---

(snip)

> +static int scsit_pci_epf_dma_transfer(struct scsit_pci_epf *scsi_epf,
> +             struct scsit_pci_epf_segment *seg, enum dma_data_direction dir)
> +{
> +     struct pci_epf *epf = scsi_epf->epf;
> +     struct dma_async_tx_descriptor *desc;
> +     struct dma_slave_config sconf = {};
> +     struct device *dev = &epf->dev;
> +     struct device *dma_dev;
> +     struct dma_chan *chan;
> +     dma_cookie_t cookie;
> +     dma_addr_t dma_addr;
> +     struct mutex *lock;
> +     int ret;
> +
> +     switch (dir) {
> +     case DMA_FROM_DEVICE:
> +             lock = &scsi_epf->dma_rx_lock;
> +             chan = scsi_epf->dma_rx_chan;
> +             sconf.direction = DMA_DEV_TO_MEM;
> +             sconf.src_addr = seg->pci_addr;
> +             break;
> +     case DMA_TO_DEVICE:
> +             lock = &scsi_epf->dma_tx_lock;
> +             chan = scsi_epf->dma_tx_chan;
> +             sconf.direction = DMA_MEM_TO_DEV;
> +             sconf.dst_addr = seg->pci_addr;
> +             break;
> +     default:
> +             return -EINVAL;
> +     }
> +
> +     mutex_lock(lock);

You should not need to take the mutex if you instead use the
dmaengine_prep_config_single_safe() API, see:
a0fba0a49f77 ("nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API")


> +
> +     dma_dev = dmaengine_get_dma_device(chan);
> +     dma_addr = dma_map_single(dma_dev, seg->buf, seg->length, dir);
> +     ret = dma_mapping_error(dma_dev, dma_addr);
> +     if (ret)
> +             goto unlock;
> +
> +     ret = dmaengine_slave_config(chan, &sconf);
> +     if (ret) {
> +             dev_err(dev, "Failed to configure DMA channel\n");
> +             goto unmap;
> +     }
> +
> +     desc = dmaengine_prep_slave_single(chan, dma_addr, seg->length,
> +                                        sconf.direction, DMA_CTRL_ACK);
> +     if (!desc) {
> +             dev_err(dev, "Failed to prepare DMA\n");
> +             ret = -EIO;
> +             goto unmap;
> +     }
> +
> +     cookie = dmaengine_submit(desc);
> +     ret = dma_submit_error(cookie);
> +     if (ret) {
> +             dev_err(dev, "Failed to do DMA submit (err=%d)\n", ret);
> +             goto unmap;
> +     }
> +
> +     if (dma_sync_wait(chan, cookie) != DMA_COMPLETE) {
> +             dev_err(dev, "DMA transfer failed\n");
> +             ret = -EIO;
> +     }
> +
> +     dmaengine_terminate_sync(chan);

I don't think dmaengine_terminate_sync() should be called on each DMA transfer, 
see:
bd00d2c4a1b2 ("nvmet: pci-epf: Remove unnecessary dmaengine_terminate_sync() on 
each DMA transfer")


Kind regards,
Niklas

Reply via email to