Skip to content

Commit 372aaab

Browse files
Ben Hillischessturo
authored andcommitted
PCI: hv: Reserve hv_pci swiotlb from buddy and publish via sysfs
The old early_param parsed hv_pci_swiotlb=<base>,<size> and reserved the host-supplied physical address with memblock_reserve(), which does not validate that the range is backed by EPT. Under Hyper-V page-reporting the backing for a nominally usable e820 range can be absent, so the memset() inside swiotlb_init_io_tlb_pool() triple-faulted the guest. Pick the base in the guest instead: * A core_initcall calls alloc_contig_pages(__GFP_DMA32 | __GFP_ZERO) for a kernel-owned, contiguous, below-4G range. __GFP_ZERO faults the pages in, and kernel ownership keeps page reporting away. Gated on CONFIG_CONTIG_ALLOC; without it the dedicated pool is skipped. * The hv_pci_swiotlb= early_param and the alloc core_initcall are only compiled in for built-in builds (#ifndef MODULE), because both early_param and core_initcall are unavailable from modules. Module builds compile cleanly and fall back to the default swiotlb pool. * (base, size) is exposed via DRIVER_ATTR_RO under /sys/bus/vmbus/drivers/hv_pci/swiotlb_{base,size} so userspace can forward the real GPA to the host-side device backend. swiotlb has no destroy_pool() counterpart, so the pages are leaked on driver unload; hv_pci is rarely hot-replaced and the pool is bounded. Signed-off-by: Ben Hillis <benhillis@microsoft.com>
1 parent 8e58006 commit 372aaab

1 file changed

Lines changed: 144 additions & 19 deletions

File tree

drivers/pci/controller/pci-hyperv.c

Lines changed: 144 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
#include <linux/sizes.h>
5454
#include <linux/of_irq.h>
5555
#include <linux/swiotlb.h>
56-
#include <linux/memblock.h>
5756
#include <asm/mshyperv.h>
5857

5958
/*
@@ -472,17 +471,37 @@ static int pci_ring_size = VMBUS_RING_SIZE(SZ_16K);
472471

473472
static phys_addr_t hv_pci_swiotlb_base;
474473
static size_t hv_pci_swiotlb_size;
474+
static bool hv_pci_swiotlb_published;
475475

476+
#ifndef MODULE
477+
/*
478+
* Parse hv_pci_swiotlb=<size>; the base is picked when the driver
479+
* initializes. early_param is only available in built-in builds, so the
480+
* dedicated pool feature is effectively built-in only; module builds fall
481+
* back to the default swiotlb pool.
482+
*/
476483
static int __init early_hv_pci_swiotlb(char *p)
477484
{
478-
hv_pci_swiotlb_base = memparse(p, &p);
479-
if (*p == ',')
480-
hv_pci_swiotlb_size = memparse(p + 1, NULL);
481-
if (hv_pci_swiotlb_base && hv_pci_swiotlb_size)
482-
memblock_reserve(hv_pci_swiotlb_base, hv_pci_swiotlb_size);
483-
return 0;
485+
char *end;
486+
487+
if (!*p)
488+
return 0;
489+
490+
hv_pci_swiotlb_size = memparse(p, &end);
491+
if (*end != '\0') {
492+
pr_warn("hv_pci: ignoring hv_pci_swiotlb=%s; expected hv_pci_swiotlb=<size>\n",
493+
p);
494+
hv_pci_swiotlb_size = 0;
495+
return 0;
496+
}
497+
498+
if (hv_pci_swiotlb_size)
499+
hv_pci_swiotlb_size = ALIGN(hv_pci_swiotlb_size, SZ_2M);
500+
501+
return 0;
484502
}
485503
early_param("hv_pci_swiotlb", early_hv_pci_swiotlb);
504+
#endif /* !MODULE */
486505

487506
static struct io_tlb_mem *hv_pci_swiotlb_pool;
488507

@@ -4233,10 +4252,52 @@ static struct hv_driver hv_pci_drv = {
42334252
.resume = hv_pci_resume,
42344253
};
42354254

4255+
/* Publish swiotlb_{base,size} so userspace can forward the GPA to the host. */
4256+
static ssize_t swiotlb_base_show(struct device_driver *drv, char *buf)
4257+
{
4258+
return sysfs_emit(buf, "0x%llx\n",
4259+
(unsigned long long)hv_pci_swiotlb_base);
4260+
}
4261+
static DRIVER_ATTR_RO(swiotlb_base);
4262+
4263+
static ssize_t swiotlb_size_show(struct device_driver *drv, char *buf)
4264+
{
4265+
return sysfs_emit(buf, "%zu\n", hv_pci_swiotlb_size);
4266+
}
4267+
static DRIVER_ATTR_RO(swiotlb_size);
4268+
4269+
static void hv_pci_swiotlb_unpublish(void)
4270+
{
4271+
if (!hv_pci_swiotlb_published)
4272+
return;
4273+
driver_remove_file(&hv_pci_drv.driver, &driver_attr_swiotlb_size);
4274+
driver_remove_file(&hv_pci_drv.driver, &driver_attr_swiotlb_base);
4275+
hv_pci_swiotlb_published = false;
4276+
}
4277+
4278+
static void hv_pci_swiotlb_publish(void)
4279+
{
4280+
if (driver_create_file(&hv_pci_drv.driver, &driver_attr_swiotlb_base))
4281+
goto warn;
4282+
if (driver_create_file(&hv_pci_drv.driver, &driver_attr_swiotlb_size)) {
4283+
driver_remove_file(&hv_pci_drv.driver, &driver_attr_swiotlb_base);
4284+
goto warn;
4285+
}
4286+
hv_pci_swiotlb_published = true;
4287+
return;
4288+
4289+
warn:
4290+
pr_warn("hv_pci: failed to publish swiotlb range to sysfs\n");
4291+
}
4292+
42364293
static void __exit exit_hv_pci_drv(void)
42374294
{
4295+
hv_pci_swiotlb_unpublish();
4296+
42384297
vmbus_driver_unregister(&hv_pci_drv);
42394298

4299+
/* No swiotlb_destroy_pool() exists, so the backing pages are leaked. */
4300+
42404301
hvpci_block_ops.read_block = NULL;
42414302
hvpci_block_ops.write_block = NULL;
42424303
hvpci_block_ops.reg_blk_invalidate = NULL;
@@ -4252,17 +4313,6 @@ static int __init init_hv_pci_drv(void)
42524313
if (hv_root_partition() && !hv_nested)
42534314
return -ENODEV;
42544315

4255-
if (hv_pci_swiotlb_base && hv_pci_swiotlb_size) {
4256-
hv_pci_swiotlb_pool = swiotlb_create_pool(hv_pci_swiotlb_base,
4257-
hv_pci_swiotlb_size,
4258-
"hv-pci-swiotlb");
4259-
if (IS_ERR(hv_pci_swiotlb_pool)) {
4260-
pr_err("hv_pci: failed to create swiotlb pool: %ld\n",
4261-
PTR_ERR(hv_pci_swiotlb_pool));
4262-
hv_pci_swiotlb_pool = NULL;
4263-
}
4264-
}
4265-
42664316
ret = hv_pci_irqchip_init();
42674317
if (ret)
42684318
return ret;
@@ -4275,8 +4325,83 @@ static int __init init_hv_pci_drv(void)
42754325
hvpci_block_ops.write_block = hv_write_config_block;
42764326
hvpci_block_ops.reg_blk_invalidate = hv_register_block_invalidate;
42774327

4278-
return vmbus_driver_register(&hv_pci_drv);
4328+
ret = vmbus_driver_register(&hv_pci_drv);
4329+
if (ret)
4330+
return ret;
4331+
4332+
if (hv_pci_swiotlb_pool)
4333+
hv_pci_swiotlb_publish();
4334+
4335+
return 0;
4336+
}
4337+
4338+
#ifndef MODULE
4339+
/*
4340+
* The dedicated swiotlb pool feature is built-in only: the cmdline parser
4341+
* uses early_param (unavailable in modules) and the allocation runs as a
4342+
* core_initcall so the buddy allocator hands us a 64 MiB DMA32 contiguous
4343+
* range with minimal fragmentation risk. In module builds
4344+
* hv_pci_swiotlb_size stays 0 and the rest of the file's pool plumbing
4345+
* (probe, publish, exit) is naturally inert.
4346+
*/
4347+
#ifdef CONFIG_CONTIG_ALLOC
4348+
/*
4349+
* Reserve the hv_pci swiotlb pool from the buddy allocator. __GFP_DMA32
4350+
* keeps the range below 4 GiB; kernel ownership keeps Hyper-V page
4351+
* reporting from yanking the backing. Gated on CONFIG_CONTIG_ALLOC.
4352+
*/
4353+
static int __init hv_pci_swiotlb_alloc_pool(void)
4354+
{
4355+
phys_addr_t end;
4356+
unsigned long nr_pages;
4357+
struct page *pages;
4358+
4359+
if (!hv_pci_swiotlb_size)
4360+
return 0;
4361+
4362+
nr_pages = hv_pci_swiotlb_size >> PAGE_SHIFT;
4363+
4364+
/* UMA on WSL; first_online_node biases nothing in practice. */
4365+
pages = alloc_contig_pages(nr_pages,
4366+
GFP_KERNEL | __GFP_DMA32 | __GFP_ZERO,
4367+
first_online_node, &node_online_map);
4368+
if (!pages) {
4369+
pr_warn("hv_pci: failed to allocate %zu-byte swiotlb pool below 4G; feature disabled\n",
4370+
hv_pci_swiotlb_size);
4371+
hv_pci_swiotlb_size = 0;
4372+
return 0;
4373+
}
4374+
4375+
hv_pci_swiotlb_base = page_to_phys(pages);
4376+
end = hv_pci_swiotlb_base + hv_pci_swiotlb_size;
4377+
4378+
pr_info("hv_pci: reserved swiotlb pool [%pa..%pa)\n",
4379+
&hv_pci_swiotlb_base, &end);
4380+
4381+
hv_pci_swiotlb_pool = swiotlb_create_pool(hv_pci_swiotlb_base,
4382+
hv_pci_swiotlb_size,
4383+
"hv-pci-swiotlb");
4384+
if (IS_ERR(hv_pci_swiotlb_pool)) {
4385+
pr_err("hv_pci: failed to create swiotlb pool: %ld\n",
4386+
PTR_ERR(hv_pci_swiotlb_pool));
4387+
hv_pci_swiotlb_pool = NULL;
4388+
free_contig_range(page_to_pfn(pages), nr_pages);
4389+
hv_pci_swiotlb_base = 0;
4390+
hv_pci_swiotlb_size = 0;
4391+
}
4392+
4393+
return 0;
42794394
}
4395+
#else
4396+
static int __init hv_pci_swiotlb_alloc_pool(void)
4397+
{
4398+
if (hv_pci_swiotlb_size)
4399+
pr_warn("hv_pci: CONFIG_CONTIG_ALLOC disabled; hv_pci_swiotlb= ignored\n");
4400+
return 0;
4401+
}
4402+
#endif
4403+
core_initcall(hv_pci_swiotlb_alloc_pool);
4404+
#endif /* !MODULE */
42804405

42814406
module_init(init_hv_pci_drv);
42824407
module_exit(exit_hv_pci_drv);

0 commit comments

Comments
 (0)