On Thu, May 14, 2026 at 2:30 AM Long Li <[email protected]> wrote:
>
> > Hyper-V provides Confidential VMBus to communicate between device model
> > and device guest driver via encrypted/private memory in Confidential VM. The
> > device model is in OpenHCL
> > (https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fopenvm
> > m.dev%2Fguide%2Fuser_guide%2Fopenhcl.html&data=05%7C02%7Clongli%40mi
> > crosoft.com%7C0ccfea7cda8e4500ae9808de9540d01e%7C72f988bf86f141af91a
> > b2d7cd011db47%7C1%7C0%7C639112302777934798%7CUnknown%7CTWFpbG
> > Zsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIk
> > FOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=5Uc%2FM4ZVgJT1
> > NAq08cIlNtfF5oW4n%2FTj%2Bqg3YqBUeZg%3D&reserved=0) that plays the
> > paravisor role.
> >
> > For a VMBus device, there are two communication methods to talk with
> > Host/Hypervisor. 1) VMBUS Ring buffer 2) Dynamic DMA transfer.
> >
> > The Confidential VMBus Ring buffer has been upstreamed by Roman Kisel(commit
> > 6802d8af47d1).
> >
> > The dynamic DMA transition of VMBus device normally goes through DMA core
> > and it uses SWIOTLB as bounce buffer in a CoCo VM.
> >
> > The Confidential VMBus device can do DMA directly to private/encrypted
> > memory. Because the swiotlb is decrypted memory, the DMA transfer must not
> > be bounced through the swiotlb, so as to preserve confidentiality. This is
> > different
> > from the default for Linux CoCo VMs, so not use DMA(SWIOTLB) API in VMBus
> > driver when confidential dynamic DMA transfers capability is present.
> >
> > Signed-off-by: Tianyu Lan <[email protected]>
> > ---
> > drivers/scsi/storvsc_drv.c | 28 +++++++++++++++++++++-------
> > include/linux/hyperv.h | 1 +
> > 2 files changed, 22 insertions(+), 7 deletions(-)
> >
> > diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index
> > ae1abab97835..79b7611518b7 100644
> > --- a/drivers/scsi/storvsc_drv.c
> > +++ b/drivers/scsi/storvsc_drv.c
> > @@ -1316,7 +1316,8 @@ static void storvsc_on_channel_callback(void *context)
> > continue;
> > }
> > request = (struct storvsc_cmd_request
> > *)scsi_cmd_priv(scmnd);
> > - scsi_dma_unmap(scmnd);
> > + if (!device->co_external_memory)
> > + scsi_dma_unmap(scmnd);
> > }
> >
> > storvsc_on_receive(stor_device, packet, request); @@ -
> > 1339,6 +1340,8 @@ static int storvsc_connect_to_vsp(struct hv_device
> > *device,
> > u32 ring_size,
> >
> > device->channel->max_pkt_size = STORVSC_MAX_PKT_SIZE;
> > device->channel->next_request_id_callback = storvsc_next_request_id;
> > + if (device->channel->co_external_memory)
> > + device->co_external_memory = true;
> >
> > ret = vmbus_open(device->channel,
> > ring_size,
> > @@ -1805,7 +1808,7 @@ static enum scsi_qc_status
> > storvsc_queuecommand(struct Scsi_Host *host,
> > unsigned long offset_in_hvpg = offset_in_hvpage(sgl->offset);
> > unsigned int hvpg_count = HVPFN_UP(offset_in_hvpg + length);
> > struct scatterlist *sg;
> > - unsigned long hvpfn, hvpfns_to_add;
> > + unsigned long hvpfn, hvpfns_to_add, hvpgoff;
> > int j, i = 0, sg_count;
> >
> > payload_sz = (hvpg_count * sizeof(u64) + @@ -1821,7 +1824,11
> > @@ static enum scsi_qc_status storvsc_queuecommand(struct Scsi_Host *host,
> > payload->range.len = length;
> > payload->range.offset = offset_in_hvpg;
> >
> > - sg_count = scsi_dma_map(scmnd);
> > + if (dev->co_external_memory)
> > + sg_count = scsi_sg_count(scmnd);
>
> scsi_sg_count() returns unsigned int, sg_count can't be negative. The check
> for sg_count < 0 below becomes dead code. Add a comment to say this is
> expected behavior.
>
Hi Long:
Thanks for your review. Nice catch and will update.
> > + else
> > + sg_count = scsi_dma_map(scmnd);
> > +
> > if (sg_count < 0) {
> > ret = SCSI_MLQUEUE_DEVICE_BUSY;
> > goto err_free_payload;
> > @@ -1836,9 +1843,16 @@ static enum scsi_qc_status
> > storvsc_queuecommand(struct Scsi_Host *host,
> > * Such offsets are handled even on other than the
> > first
> > * sgl entry, provided they are a multiple of
> > PAGE_SIZE.
> > */
> > - hvpfn = HVPFN_DOWN(sg_dma_address(sg));
> > - hvpfns_to_add = HVPFN_UP(sg_dma_address(sg) +
> > - sg_dma_len(sg)) - hvpfn;
> > + if (dev->co_external_memory) {
> > + hvpgoff = HVPFN_DOWN(sg->offset);
> > + hvpfn = page_to_hvpfn(sg_page(sg)) + hvpgoff;
> > + hvpfns_to_add = HVPFN_UP(sg->offset
> > + sg->length) -
> > + hvpgoff;
> > + } else {
> > + hvpfn = HVPFN_DOWN(sg_dma_address(sg));
> > + hvpfns_to_add =
> > HVPFN_UP(sg_dma_address(sg) +
> > + sg_dma_len(sg)) -
> > hvpfn;
> > + }
> >
> > /*
> > * Fill the next portion of the PFN array with @@
> > -1860,7
> > +1874,7 @@ static enum scsi_qc_status storvsc_queuecommand(struct
> > Scsi_Host *host,
> > ret = storvsc_do_io(dev, cmd_request, smp_processor_id());
> > migrate_enable();
> >
> > - if (ret)
> > + if (ret && (!dev->co_external_memory))
> > scsi_dma_unmap(scmnd);
> >
> > if (ret == -EAGAIN) {
> > diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index
> > dfc516c1c719..bcb143766d6e 100644
> > --- a/include/linux/hyperv.h
> > +++ b/include/linux/hyperv.h
> > @@ -1285,6 +1285,7 @@ struct hv_device {
> >
> > /* place holder to keep track of the dir for hv device in debugfs */
> > struct dentry *debug_dir;
> > + bool co_external_memory;
>
> You don't need to introduce co_external_memory in hv_device, vmbus_channel
> already has co_external_memory. Is it possible that you can check the
> vmbus_channel->co_external_memory directly? If you can remove this, you can
> reword this patch to " scsi: storvsc: Confidential VMBus for dynamic DMA
> transfers".
>
Good idea. Will update in the next version.
--
Thanks
Tianyu Lan