> 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.
> + 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".
Thanks,
Long