--- v2
+++ v4
@@ -1,338 +1,142 @@
From: Tianyu Lan <Tianyu.Lan@microsoft.com>
-hyperv Isolation VM requires bounce buffer support to copy
-data from/to encrypted memory and so enable swiotlb force
-mode to use swiotlb bounce buffer for DMA transaction.
+In Isolation VM, all shared memory with host needs to mark visible
+to host via hvcall. vmbus_establish_gpadl() has already done it for
+storvsc rx/tx ring buffer. The page buffer used by vmbus_sendpacket_
+mpb_desc() still needs to be handled. Use DMA API(scsi_dma_map/unmap)
+to map these memory during sending/receiving packet and return swiotlb
+bounce buffer dma address. In Isolation VM, swiotlb bounce buffer is
+marked to be visible to host and the swiotlb force mode is enabled.
-In Isolation VM with AMD SEV, the bounce buffer needs to be
-accessed via extra address space which is above shared_gpa_boundary
-(E.G 39 bit address line) reported by Hyper-V CPUID ISOLATION_CONFIG.
-The access physical address will be original physical address +
-shared_gpa_boundary. The shared_gpa_boundary in the AMD SEV SNP
-spec is called virtual top of memory(vTOM). Memory addresses below
-vTOM are automatically treated as private while memory above
-vTOM is treated as shared.
-
-Hyper-V initalizes swiotlb bounce buffer and default swiotlb
-needs to be disabled. pci_swiotlb_detect_override() and
-pci_swiotlb_detect_4gb() enable the default one. To override
-the setting, hyperv_swiotlb_detect() needs to run before
-these detect functions which depends on the pci_xen_swiotlb_
-init(). Make pci_xen_swiotlb_init() depends on the hyperv_swiotlb
-_detect() to keep the order.
-
-Swiotlb bounce buffer code calls set_memory_decrypted()
-to mark bounce buffer visible to host and map it in extra
-address space via memremap. Populate the shared_gpa_boundary
-(vTOM) via swiotlb_unencrypted_base variable.
-
-The map function memremap() can't work in the early place
-hyperv_iommu_swiotlb_init() and so call swiotlb_update_mem_attributes()
-in the hyperv_iommu_swiotlb_later_init().
-
-Add Hyper-V dma ops and provide alloc/free and vmap/vunmap noncontiguous
-callback to handle request of allocating and mapping noncontiguous dma
-memory in vmbus device driver. Netvsc driver will use this. Set dma_ops_
-bypass flag for hv device to use dma direct functions during mapping/unmapping
-dma page.
+Set device's dma min align mask to HV_HYP_PAGE_SIZE - 1 in order to
+keep the original data offset in the bounce buffer.
Signed-off-by: Tianyu Lan <Tianyu.Lan@microsoft.com>
---
-Change since v1:
- * Remove hv isolation check in the sev_setup_arch()
+ drivers/hv/vmbus_drv.c | 1 +
+ drivers/scsi/storvsc_drv.c | 37 +++++++++++++++++++++----------------
+ include/linux/hyperv.h | 1 +
+ 3 files changed, 23 insertions(+), 16 deletions(-)
- arch/x86/mm/mem_encrypt.c | 1 +
- arch/x86/xen/pci-swiotlb-xen.c | 3 +-
- drivers/hv/Kconfig | 1 +
- drivers/hv/vmbus_drv.c | 6 ++
- drivers/iommu/hyperv-iommu.c | 164 +++++++++++++++++++++++++++++++++
- include/linux/hyperv.h | 10 ++
- 6 files changed, 184 insertions(+), 1 deletion(-)
-
-diff --git a/arch/x86/mm/mem_encrypt.c b/arch/x86/mm/mem_encrypt.c
-index 35487305d8af..e48c73b3dd41 100644
---- a/arch/x86/mm/mem_encrypt.c
-+++ b/arch/x86/mm/mem_encrypt.c
-@@ -31,6 +31,7 @@
- #include <asm/processor-flags.h>
- #include <asm/msr.h>
- #include <asm/cmdline.h>
-+#include <asm/mshyperv.h>
-
- #include "mm_internal.h"
-
-diff --git a/arch/x86/xen/pci-swiotlb-xen.c b/arch/x86/xen/pci-swiotlb-xen.c
-index 46df59aeaa06..30fd0600b008 100644
---- a/arch/x86/xen/pci-swiotlb-xen.c
-+++ b/arch/x86/xen/pci-swiotlb-xen.c
-@@ -4,6 +4,7 @@
-
- #include <linux/dma-map-ops.h>
- #include <linux/pci.h>
-+#include <linux/hyperv.h>
- #include <xen/swiotlb-xen.h>
-
- #include <asm/xen/hypervisor.h>
-@@ -91,6 +92,6 @@ int pci_xen_swiotlb_init_late(void)
- EXPORT_SYMBOL_GPL(pci_xen_swiotlb_init_late);
-
- IOMMU_INIT_FINISH(pci_xen_swiotlb_detect,
-- NULL,
-+ hyperv_swiotlb_detect,
- pci_xen_swiotlb_init,
- NULL);
-diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig
-index dd12af20e467..d43b4cd88f57 100644
---- a/drivers/hv/Kconfig
-+++ b/drivers/hv/Kconfig
-@@ -9,6 +9,7 @@ config HYPERV
- select PARAVIRT
- select X86_HV_CALLBACK_VECTOR if X86
- select VMAP_PFN
-+ select DMA_OPS_BYPASS
- help
- Select this option to run Linux as a Hyper-V client operating
- system.
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
-index 392c1ac4f819..32dc193e31cd 100644
+index 0a64ccfafb8b..ae6ec503399a 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
-@@ -33,6 +33,7 @@
- #include <linux/random.h>
- #include <linux/kernel.h>
- #include <linux/syscore_ops.h>
-+#include <linux/dma-map-ops.h>
- #include <clocksource/hyperv_timer.h>
- #include "hyperv_vmbus.h"
-
-@@ -2078,6 +2079,7 @@ struct hv_device *vmbus_device_create(const guid_t *type,
- return child_device_obj;
- }
-
-+static u64 vmbus_dma_mask = DMA_BIT_MASK(64);
- /*
- * vmbus_device_register - Register the child device
- */
-@@ -2118,6 +2120,10 @@ int vmbus_device_register(struct hv_device *child_device_obj)
- }
+@@ -2121,6 +2121,7 @@ int vmbus_device_register(struct hv_device *child_device_obj)
hv_debug_add_dev_dir(child_device_obj);
-+ child_device_obj->device.dma_ops_bypass = true;
-+ child_device_obj->device.dma_ops = &hyperv_iommu_dma_ops;
-+ child_device_obj->device.dma_mask = &vmbus_dma_mask;
+ child_device_obj->device.dma_mask = &vmbus_dma_mask;
+ child_device_obj->device.dma_parms = &child_device_obj->dma_parms;
return 0;
err_kset_unregister:
-diff --git a/drivers/iommu/hyperv-iommu.c b/drivers/iommu/hyperv-iommu.c
-index e285a220c913..ebcb628e7e8f 100644
---- a/drivers/iommu/hyperv-iommu.c
-+++ b/drivers/iommu/hyperv-iommu.c
-@@ -13,14 +13,21 @@
- #include <linux/irq.h>
- #include <linux/iommu.h>
- #include <linux/module.h>
-+#include <linux/hyperv.h>
-+#include <linux/io.h>
+diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
+index 20595c0ba0ae..ae293600d799 100644
+--- a/drivers/scsi/storvsc_drv.c
++++ b/drivers/scsi/storvsc_drv.c
+@@ -21,6 +21,8 @@
+ #include <linux/device.h>
+ #include <linux/hyperv.h>
+ #include <linux/blkdev.h>
++#include <linux/dma-mapping.h>
++
+ #include <scsi/scsi.h>
+ #include <scsi/scsi_cmnd.h>
+ #include <scsi/scsi_host.h>
+@@ -1336,6 +1338,7 @@ static void storvsc_on_channel_callback(void *context)
+ continue;
+ }
+ request = (struct storvsc_cmd_request *)scsi_cmd_priv(scmnd);
++ scsi_dma_unmap(scmnd);
+ }
- #include <asm/apic.h>
- #include <asm/cpu.h>
- #include <asm/hw_irq.h>
- #include <asm/io_apic.h>
-+#include <asm/iommu.h>
-+#include <asm/iommu_table.h>
- #include <asm/irq_remapping.h>
- #include <asm/hypervisor.h>
- #include <asm/mshyperv.h>
-+#include <asm/swiotlb.h>
-+#include <linux/dma-map-ops.h>
-+#include <linux/dma-direct.h>
+ storvsc_on_receive(stor_device, packet, request);
+@@ -1749,7 +1752,6 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd)
+ struct hv_host_device *host_dev = shost_priv(host);
+ struct hv_device *dev = host_dev->dev;
+ struct storvsc_cmd_request *cmd_request = scsi_cmd_priv(scmnd);
+- int i;
+ struct scatterlist *sgl;
+ unsigned int sg_count;
+ struct vmscsi_request *vm_srb;
+@@ -1831,10 +1833,11 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd)
+ payload_sz = sizeof(cmd_request->mpb);
- #include "irq_remapping.h"
+ if (sg_count) {
+- unsigned int hvpgoff, hvpfns_to_add;
+ unsigned long offset_in_hvpg = offset_in_hvpage(sgl->offset);
+ unsigned int hvpg_count = HVPFN_UP(offset_in_hvpg + length);
+- u64 hvpfn;
++ struct scatterlist *sg;
++ unsigned long hvpfn, hvpfns_to_add;
++ int j, i = 0;
-@@ -337,4 +344,161 @@ static const struct irq_domain_ops hyperv_root_ir_domain_ops = {
- .free = hyperv_root_irq_remapping_free,
- };
+ if (hvpg_count > MAX_PAGE_BUFFER_COUNT) {
-+static void __init hyperv_iommu_swiotlb_init(void)
-+{
-+ unsigned long hyperv_io_tlb_size;
-+ void *hyperv_io_tlb_start;
-+
-+ /*
-+ * Allocate Hyper-V swiotlb bounce buffer at early place
-+ * to reserve large contiguous memory.
-+ */
-+ hyperv_io_tlb_size = swiotlb_size_or_default();
-+ hyperv_io_tlb_start = memblock_alloc(hyperv_io_tlb_size, PAGE_SIZE);
-+
-+ if (!hyperv_io_tlb_start)
-+ pr_warn("Fail to allocate Hyper-V swiotlb buffer.\n");
-+
-+ swiotlb_init_with_tbl(hyperv_io_tlb_start,
-+ hyperv_io_tlb_size >> IO_TLB_SHIFT, true);
-+}
-+
-+int __init hyperv_swiotlb_detect(void)
-+{
-+ if (!hypervisor_is_type(X86_HYPER_MS_HYPERV))
-+ return 0;
-+
-+ if (!hv_is_isolation_supported())
-+ return 0;
-+
-+ /*
-+ * Enable swiotlb force mode in Isolation VM to
-+ * use swiotlb bounce buffer for dma transaction.
-+ */
-+ if (hv_isolation_type_snp())
-+ swiotlb_unencrypted_base = ms_hyperv.shared_gpa_boundary;
-+ swiotlb_force = SWIOTLB_FORCE;
-+ return 1;
-+}
-+
-+static void __init hyperv_iommu_swiotlb_later_init(void)
-+{
-+ /*
-+ * Swiotlb bounce buffer needs to be mapped in extra address
-+ * space. Map function doesn't work in the early place and so
-+ * call swiotlb_update_mem_attributes() here.
-+ */
-+ swiotlb_update_mem_attributes();
-+}
-+
-+IOMMU_INIT_FINISH(hyperv_swiotlb_detect,
-+ NULL, hyperv_iommu_swiotlb_init,
-+ hyperv_iommu_swiotlb_later_init);
-+
-+static struct sg_table *hyperv_dma_alloc_noncontiguous(struct device *dev,
-+ size_t size, enum dma_data_direction dir, gfp_t gfp,
-+ unsigned long attrs)
-+{
-+ struct dma_sgt_handle *sh;
-+ struct page **pages;
-+ int num_pages = size >> PAGE_SHIFT;
-+ void *vaddr, *ptr;
-+ int rc, i;
-+
-+ if (!hv_isolation_type_snp())
-+ return NULL;
-+
-+ sh = kmalloc(sizeof(*sh), gfp);
-+ if (!sh)
-+ return NULL;
-+
-+ vaddr = vmalloc(size);
-+ if (!vaddr)
-+ goto free_sgt;
-+
-+ pages = kvmalloc_array(num_pages, sizeof(struct page *),
-+ GFP_KERNEL | __GFP_ZERO);
-+ if (!pages)
-+ goto free_mem;
-+
-+ for (i = 0, ptr = vaddr; i < num_pages; ++i, ptr += PAGE_SIZE)
-+ pages[i] = vmalloc_to_page(ptr);
-+
-+ rc = sg_alloc_table_from_pages(&sh->sgt, pages, num_pages, 0, size, GFP_KERNEL);
-+ if (rc)
-+ goto free_pages;
-+
-+ sh->sgt.sgl->dma_address = (dma_addr_t)vaddr;
-+ sh->sgt.sgl->dma_length = size;
-+ sh->pages = pages;
-+
-+ return &sh->sgt;
-+
-+free_pages:
-+ kvfree(pages);
-+free_mem:
-+ vfree(vaddr);
-+free_sgt:
-+ kfree(sh);
-+ return NULL;
-+}
-+
-+static void hyperv_dma_free_noncontiguous(struct device *dev, size_t size,
-+ struct sg_table *sgt, enum dma_data_direction dir)
-+{
-+ struct dma_sgt_handle *sh = sgt_handle(sgt);
-+
-+ if (!hv_isolation_type_snp())
-+ return;
-+
-+ vfree((void *)sh->sgt.sgl->dma_address);
-+ sg_free_table(&sh->sgt);
-+ kvfree(sh->pages);
-+ kfree(sh);
-+}
-+
-+static void *hyperv_dma_vmap_noncontiguous(struct device *dev, size_t size,
-+ struct sg_table *sgt)
-+{
-+ int pg_count = size >> PAGE_SHIFT;
-+ unsigned long *pfns;
-+ struct page **pages = sgt_handle(sgt)->pages;
-+ void *vaddr = NULL;
-+ int i;
-+
-+ if (!hv_isolation_type_snp())
-+ return NULL;
-+
-+ if (!pages)
-+ return NULL;
-+
-+ pfns = kcalloc(pg_count, sizeof(*pfns), GFP_KERNEL);
-+ if (!pfns)
-+ return NULL;
-+
-+ for (i = 0; i < pg_count; i++)
-+ pfns[i] = page_to_pfn(pages[i]) +
-+ (ms_hyperv.shared_gpa_boundary >> PAGE_SHIFT);
-+
-+ vaddr = vmap_pfn(pfns, pg_count, PAGE_KERNEL);
-+ kfree(pfns);
-+ return vaddr;
-+
-+}
-+
-+static void hyperv_dma_vunmap_noncontiguous(struct device *dev, void *addr)
-+{
-+ if (!hv_isolation_type_snp())
-+ return;
-+ vunmap(addr);
-+}
-+
-+const struct dma_map_ops hyperv_iommu_dma_ops = {
-+ .alloc_noncontiguous = hyperv_dma_alloc_noncontiguous,
-+ .free_noncontiguous = hyperv_dma_free_noncontiguous,
-+ .vmap_noncontiguous = hyperv_dma_vmap_noncontiguous,
-+ .vunmap_noncontiguous = hyperv_dma_vunmap_noncontiguous,
-+};
-+EXPORT_SYMBOL_GPL(hyperv_iommu_dma_ops);
-+
- #endif
+@@ -1848,21 +1851,22 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd)
+ payload->range.len = length;
+ payload->range.offset = offset_in_hvpg;
+
++ sg_count = scsi_dma_map(scmnd);
++ if (sg_count < 0)
++ return SCSI_MLQUEUE_DEVICE_BUSY;
+
+- for (i = 0; sgl != NULL; sgl = sg_next(sgl)) {
++ for_each_sg(sgl, sg, sg_count, j) {
+ /*
+- * Init values for the current sgl entry. hvpgoff
+- * and hvpfns_to_add are in units of Hyper-V size
+- * pages. Handling the PAGE_SIZE != HV_HYP_PAGE_SIZE
+- * case also handles values of sgl->offset that are
+- * larger than PAGE_SIZE. Such offsets are handled
+- * even on other than the first sgl entry, provided
+- * they are a multiple of PAGE_SIZE.
++ * Init values for the current sgl entry. hvpfns_to_add
++ * is in units of Hyper-V size pages. Handling the
++ * PAGE_SIZE != HV_HYP_PAGE_SIZE case also handles
++ * values of sgl->offset that are larger than PAGE_SIZE.
++ * Such offsets are handled even on other than the first
++ * sgl entry, provided they are a multiple of PAGE_SIZE.
+ */
+- hvpgoff = HVPFN_DOWN(sgl->offset);
+- hvpfn = page_to_hvpfn(sg_page(sgl)) + hvpgoff;
+- hvpfns_to_add = HVPFN_UP(sgl->offset + sgl->length) -
+- hvpgoff;
++ 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
+@@ -1872,7 +1876,7 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd)
+ * the PFN array is filled.
+ */
+ while (hvpfns_to_add--)
+- payload->range.pfn_array[i++] = hvpfn++;
++ payload->range.pfn_array[i++] = hvpfn++;
+ }
+ }
+
+@@ -2016,6 +2020,7 @@ static int storvsc_probe(struct hv_device *device,
+ stor_device->vmscsi_size_delta = sizeof(struct vmscsi_win8_extension);
+ spin_lock_init(&stor_device->lock);
+ hv_set_drvdata(device, stor_device);
++ dma_set_min_align_mask(&device->device, HV_HYP_PAGE_SIZE - 1);
+
+ stor_device->port_number = host->host_no;
+ ret = storvsc_connect_to_vsp(device, storvsc_ringbuffer_size, is_fc);
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
-index b823311eac79..4d44fb3b3f1c 100644
+index 1f037e114dc8..74f5e92f91a0 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
-@@ -1726,6 +1726,16 @@ int hyperv_write_cfg_blk(struct pci_dev *dev, void *buf, unsigned int len,
- int hyperv_reg_block_invalidate(struct pci_dev *dev, void *context,
- void (*block_invalidate)(void *context,
- u64 block_mask));
-+#ifdef CONFIG_HYPERV
-+int __init hyperv_swiotlb_detect(void);
-+#else
-+static inline int __init hyperv_swiotlb_detect(void)
-+{
-+ return 0;
-+}
-+#endif
-+
-+extern const struct dma_map_ops hyperv_iommu_dma_ops;
+@@ -1261,6 +1261,7 @@ struct hv_device {
- struct hyperv_pci_block_ops {
- int (*read_block)(struct pci_dev *dev, void *buf, unsigned int buf_len,
+ struct vmbus_channel *channel;
+ struct kset *channels_kset;
++ struct device_dma_parameters dma_parms;
+
+ /* place holder to keep track of the dir for hv device in debugfs */
+ struct dentry *debug_dir;
--
2.25.1