From 7f163a6fd957a85f7f66a129db1ad243a44399ee Mon Sep 17 00:00:00 2001 From: Jake Oshins Date: Wed, 5 Aug 2015 00:52:36 -0700 Subject: drivers:hv: Modify hv_vmbus to search for all MMIO ranges available. This patch changes the logic in hv_vmbus to record all of the ranges in the VM's firmware (BIOS or UEFI) that offer regions of memory-mapped I/O space for use by paravirtual front-end drivers. The old logic just found one range above 4GB and called it good. This logic will find any ranges above 1MB. It would have been possible with this patch to just use existing resource allocation functions, rather than keep track of the entire set of Hyper-V related MMIO regions in VMBus. This strategy, however, is not sufficient when the resource allocator needs to be aware of the constraints of a Hyper-V virtual machine, which is what happens in the next patch in the series. So this first patch exists to show the first steps in reworking the MMIO allocation paths for Hyper-V front-end drivers. Signed-off-by: Jake Oshins Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/hyperv_fb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/video') diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c index 807ee22ef229..b54ee1c05a5f 100644 --- a/drivers/video/fbdev/hyperv_fb.c +++ b/drivers/video/fbdev/hyperv_fb.c @@ -688,7 +688,7 @@ static int hvfb_getmem(struct fb_info *info) par->mem.name = KBUILD_MODNAME; par->mem.flags = IORESOURCE_MEM | IORESOURCE_BUSY; if (gen2vm) { - ret = allocate_resource(&hyperv_mmio, &par->mem, + ret = allocate_resource(hyperv_mmio, &par->mem, screen_fb_size, 0, -1, screen_fb_size, -- cgit v1.2.3 From 3546448338e76a52d4f86eb3680cb2934e22d89b Mon Sep 17 00:00:00 2001 From: Jake Oshins Date: Wed, 5 Aug 2015 00:52:37 -0700 Subject: drivers:hv: Move MMIO range picking from hyper_fb to hv_vmbus This patch deletes the logic from hyperv_fb which picked a range of MMIO space for the frame buffer and adds new logic to hv_vmbus which picks ranges for child drivers. The new logic isn't quite the same as the old, as it considers more possible ranges. Signed-off-by: Jake Oshins Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/vmbus_drv.c | 88 +++++++++++++++++++++++++++++++++++++++-- drivers/video/fbdev/hyperv_fb.c | 46 ++++++++++----------- include/linux/hyperv.h | 7 +++- 3 files changed, 110 insertions(+), 31 deletions(-) (limited to 'drivers/video') diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index ee59e06c2194..8c3eaee8c54c 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include "hyperv_vmbus.h" @@ -103,7 +104,6 @@ static struct notifier_block hyperv_panic_block = { }; struct resource *hyperv_mmio; -EXPORT_SYMBOL_GPL(hyperv_mmio); static int vmbus_exists(void) { @@ -891,8 +891,8 @@ err_cleanup: } /** - * __vmbus_child_driver_register - Register a vmbus's driver - * @drv: Pointer to driver structure you want to register + * __vmbus_child_driver_register() - Register a vmbus's driver + * @hv_driver: Pointer to driver structure you want to register * @owner: owner module of the drv * @mod_name: module name string * @@ -924,7 +924,8 @@ EXPORT_SYMBOL_GPL(__vmbus_driver_register); /** * vmbus_driver_unregister() - Unregister a vmbus's driver - * @drv: Pointer to driver structure you want to un-register + * @hv_driver: Pointer to driver structure you want to + * un-register * * Un-register the given driver that was previous registered with a call to * vmbus_driver_register() @@ -1104,6 +1105,85 @@ static int vmbus_acpi_remove(struct acpi_device *device) return 0; } +/** + * vmbus_allocate_mmio() - Pick a memory-mapped I/O range. + * @new: If successful, supplied a pointer to the + * allocated MMIO space. + * @device_obj: Identifies the caller + * @min: Minimum guest physical address of the + * allocation + * @max: Maximum guest physical address + * @size: Size of the range to be allocated + * @align: Alignment of the range to be allocated + * @fb_overlap_ok: Whether this allocation can be allowed + * to overlap the video frame buffer. + * + * This function walks the resources granted to VMBus by the + * _CRS object in the ACPI namespace underneath the parent + * "bridge" whether that's a root PCI bus in the Generation 1 + * case or a Module Device in the Generation 2 case. It then + * attempts to allocate from the global MMIO pool in a way that + * matches the constraints supplied in these parameters and by + * that _CRS. + * + * Return: 0 on success, -errno on failure + */ +int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj, + resource_size_t min, resource_size_t max, + resource_size_t size, resource_size_t align, + bool fb_overlap_ok) +{ + struct resource *iter; + resource_size_t range_min, range_max, start, local_min, local_max; + const char *dev_n = dev_name(&device_obj->device); + u32 fb_end = screen_info.lfb_base + (screen_info.lfb_size << 1); + int i; + + for (iter = hyperv_mmio; iter; iter = iter->sibling) { + if ((iter->start >= max) || (iter->end <= min)) + continue; + + range_min = iter->start; + range_max = iter->end; + + /* If this range overlaps the frame buffer, split it into + two tries. */ + for (i = 0; i < 2; i++) { + local_min = range_min; + local_max = range_max; + if (fb_overlap_ok || (range_min >= fb_end) || + (range_max <= screen_info.lfb_base)) { + i++; + } else { + if ((range_min <= screen_info.lfb_base) && + (range_max >= screen_info.lfb_base)) { + /* + * The frame buffer is in this window, + * so trim this into the part that + * preceeds the frame buffer. + */ + local_max = screen_info.lfb_base - 1; + range_min = fb_end; + } else { + range_min = fb_end; + continue; + } + } + + start = (local_min + align - 1) & ~(align - 1); + for (; start + size - 1 <= local_max; start += align) { + *new = request_mem_region_exclusive(start, size, + dev_n); + if (*new) + return 0; + } + } + } + + return -ENXIO; +} +EXPORT_SYMBOL_GPL(vmbus_allocate_mmio); + static int vmbus_acpi_add(struct acpi_device *device) { acpi_status result; diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c index b54ee1c05a5f..e2451bdb4525 100644 --- a/drivers/video/fbdev/hyperv_fb.c +++ b/drivers/video/fbdev/hyperv_fb.c @@ -213,7 +213,7 @@ struct synthvid_msg { struct hvfb_par { struct fb_info *info; - struct resource mem; + struct resource *mem; bool fb_ready; /* fb device is ready */ struct completion wait; u32 synthvid_version; @@ -677,26 +677,18 @@ static void hvfb_get_option(struct fb_info *info) /* Get framebuffer memory from Hyper-V video pci space */ -static int hvfb_getmem(struct fb_info *info) +static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info) { struct hvfb_par *par = info->par; struct pci_dev *pdev = NULL; void __iomem *fb_virt; int gen2vm = efi_enabled(EFI_BOOT); + resource_size_t pot_start, pot_end; int ret; - par->mem.name = KBUILD_MODNAME; - par->mem.flags = IORESOURCE_MEM | IORESOURCE_BUSY; if (gen2vm) { - ret = allocate_resource(hyperv_mmio, &par->mem, - screen_fb_size, - 0, -1, - screen_fb_size, - NULL, NULL); - if (ret != 0) { - pr_err("Unable to allocate framebuffer memory\n"); - return -ENODEV; - } + pot_start = 0; + pot_end = -1; } else { pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT, PCI_DEVICE_ID_HYPERV_VIDEO, NULL); @@ -709,16 +701,18 @@ static int hvfb_getmem(struct fb_info *info) pci_resource_len(pdev, 0) < screen_fb_size) goto err1; - par->mem.end = pci_resource_end(pdev, 0); - par->mem.start = par->mem.end - screen_fb_size + 1; - ret = request_resource(&pdev->resource[0], &par->mem); - if (ret != 0) { - pr_err("Unable to request framebuffer memory\n"); - goto err1; - } + pot_end = pci_resource_end(pdev, 0); + pot_start = pot_end - screen_fb_size + 1; + } + + ret = vmbus_allocate_mmio(&par->mem, hdev, pot_start, pot_end, + screen_fb_size, 0x100000, true); + if (ret != 0) { + pr_err("Unable to allocate framebuffer memory\n"); + goto err1; } - fb_virt = ioremap(par->mem.start, screen_fb_size); + fb_virt = ioremap(par->mem->start, screen_fb_size); if (!fb_virt) goto err2; @@ -736,7 +730,7 @@ static int hvfb_getmem(struct fb_info *info) info->apertures->ranges[0].size = pci_resource_len(pdev, 0); } - info->fix.smem_start = par->mem.start; + info->fix.smem_start = par->mem->start; info->fix.smem_len = screen_fb_size; info->screen_base = fb_virt; info->screen_size = screen_fb_size; @@ -749,7 +743,8 @@ static int hvfb_getmem(struct fb_info *info) err3: iounmap(fb_virt); err2: - release_resource(&par->mem); + release_mem_region(par->mem->start, screen_fb_size); + par->mem = NULL; err1: if (!gen2vm) pci_dev_put(pdev); @@ -763,7 +758,8 @@ static void hvfb_putmem(struct fb_info *info) struct hvfb_par *par = info->par; iounmap(info->screen_base); - release_resource(&par->mem); + release_mem_region(par->mem->start, screen_fb_size); + par->mem = NULL; } @@ -794,7 +790,7 @@ static int hvfb_probe(struct hv_device *hdev, goto error1; } - ret = hvfb_getmem(info); + ret = hvfb_getmem(hdev, info); if (ret) { pr_err("No memory for framebuffer\n"); goto error2; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 217e14be77b9..54733d5b503e 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -977,6 +977,11 @@ int __must_check __vmbus_driver_register(struct hv_driver *hv_driver, const char *mod_name); void vmbus_driver_unregister(struct hv_driver *hv_driver); +int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj, + resource_size_t min, resource_size_t max, + resource_size_t size, resource_size_t align, + bool fb_overlap_ok); + /** * VMBUS_DEVICE - macro used to describe a specific hyperv vmbus device * @@ -1233,8 +1238,6 @@ extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *, void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid); -extern struct resource *hyperv_mmio; - /* * Negotiated version with the Host. */ -- cgit v1.2.3