Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

video: hyperv: hyperv_fb: Use physical memory for fb on HyperV Gen 1 VMs.

On Hyper-V, Generation 1 VMs can directly use VM's physical memory for
their framebuffers. This can improve the efficiency of framebuffer and
overall performence for VM. The physical memory assigned to framebuffer
must be contiguous. We use CMA allocator to get contiguouse physicial
memory when the framebuffer size is greater than 4MB. For size under
4MB, we use alloc_pages to achieve this.

To enable framebuffer memory allocation from CMA, supply a kernel
parameter to give enough space to CMA allocator at boot time. For
example:
cma=130m
This gives 130MB memory to CAM allocator that can be allocated to
framebuffer. If this fails, we fall back to the old way of using
mmio for framebuffer.

Reported-by: kbuild test robot <lkp@intel.com>
Signed-off-by: Wei Hu <weh@microsoft.com>
Acked-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>

authored by

Wei Hu and committed by
Sasha Levin
3a6fb6c4 ddc9d357

+144 -39
+1
drivers/video/fbdev/Kconfig
··· 2215 2215 select FB_CFB_COPYAREA 2216 2216 select FB_CFB_IMAGEBLIT 2217 2217 select FB_DEFERRED_IO 2218 + select DMA_CMA if HAVE_DMA_CONTIGUOUS && CMA 2218 2219 help 2219 2220 This framebuffer driver supports Microsoft Hyper-V Synthetic Video. 2220 2221
+143 -39
drivers/video/fbdev/hyperv_fb.c
··· 31 31 * "set-vmvideo" command. For example 32 32 * set-vmvideo -vmname name -horizontalresolution:1920 \ 33 33 * -verticalresolution:1200 -resolutiontype single 34 + * 35 + * Gen 1 VMs also support direct using VM's physical memory for framebuffer. 36 + * It could improve the efficiency and performance for framebuffer and VM. 37 + * This requires to allocate contiguous physical memory from Linux kernel's 38 + * CMA memory allocator. To enable this, supply a kernel parameter to give 39 + * enough memory space to CMA allocator for framebuffer. For example: 40 + * cma=130m 41 + * This gives 130MB memory to CMA allocator that can be allocated to 42 + * framebuffer. For reference, 8K resolution (7680x4320) takes about 43 + * 127MB memory. 34 44 */ 35 45 36 46 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ··· 238 228 } __packed; 239 229 240 230 241 - 242 231 /* FB driver definitions and structures */ 243 232 #define HVFB_WIDTH 1152 /* default screen width */ 244 233 #define HVFB_HEIGHT 864 /* default screen height */ ··· 267 258 /* If true, the VSC notifies the VSP on every framebuffer change */ 268 259 bool synchronous_fb; 269 260 261 + /* If true, need to copy from deferred IO mem to framebuffer mem */ 262 + bool need_docopy; 263 + 270 264 struct notifier_block hvfb_panic_nb; 271 265 272 266 /* Memory for deferred IO and frame buffer itself */ 273 267 unsigned char *dio_vp; 274 268 unsigned char *mmio_vp; 275 - unsigned long mmio_pp; 269 + phys_addr_t mmio_pp; 276 270 277 271 /* Dirty rectangle, protected by delayed_refresh_lock */ 278 272 int x1, y1, x2, y2; ··· 446 434 maxy = max_t(int, maxy, y2); 447 435 448 436 /* Copy from dio space to mmio address */ 449 - if (par->fb_ready) 437 + if (par->fb_ready && par->need_docopy) 450 438 hvfb_docopy(par, start, PAGE_SIZE); 451 439 } 452 440 ··· 763 751 return; 764 752 765 753 /* Copy the dirty rectangle to frame buffer memory */ 766 - for (j = y1; j < y2; j++) { 767 - hvfb_docopy(par, 768 - j * info->fix.line_length + 769 - (x1 * screen_depth / 8), 770 - (x2 - x1) * screen_depth / 8); 771 - } 754 + if (par->need_docopy) 755 + for (j = y1; j < y2; j++) 756 + hvfb_docopy(par, 757 + j * info->fix.line_length + 758 + (x1 * screen_depth / 8), 759 + (x2 - x1) * screen_depth / 8); 772 760 773 761 /* Refresh */ 774 762 if (par->fb_ready && par->update) ··· 813 801 par = container_of(nb, struct hvfb_par, hvfb_panic_nb); 814 802 par->synchronous_fb = true; 815 803 info = par->info; 816 - hvfb_docopy(par, 0, dio_fb_size); 804 + if (par->need_docopy) 805 + hvfb_docopy(par, 0, dio_fb_size); 817 806 synthvid_update(info, 0, 0, INT_MAX, INT_MAX); 818 807 819 808 return NOTIFY_DONE; ··· 953 940 return; 954 941 } 955 942 943 + /* 944 + * Allocate enough contiguous physical memory. 945 + * Return physical address if succeeded or -1 if failed. 946 + */ 947 + static phys_addr_t hvfb_get_phymem(struct hv_device *hdev, 948 + unsigned int request_size) 949 + { 950 + struct page *page = NULL; 951 + dma_addr_t dma_handle; 952 + void *vmem; 953 + phys_addr_t paddr = 0; 954 + unsigned int order = get_order(request_size); 955 + 956 + if (request_size == 0) 957 + return -1; 958 + 959 + if (order < MAX_ORDER) { 960 + /* Call alloc_pages if the size is less than 2^MAX_ORDER */ 961 + page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); 962 + if (!page) 963 + return -1; 964 + 965 + paddr = (page_to_pfn(page) << PAGE_SHIFT); 966 + } else { 967 + /* Allocate from CMA */ 968 + hdev->device.coherent_dma_mask = DMA_BIT_MASK(64); 969 + 970 + vmem = dma_alloc_coherent(&hdev->device, 971 + round_up(request_size, PAGE_SIZE), 972 + &dma_handle, 973 + GFP_KERNEL | __GFP_NOWARN); 974 + 975 + if (!vmem) 976 + return -1; 977 + 978 + paddr = virt_to_phys(vmem); 979 + } 980 + 981 + return paddr; 982 + } 983 + 984 + /* Release contiguous physical memory */ 985 + static void hvfb_release_phymem(struct hv_device *hdev, 986 + phys_addr_t paddr, unsigned int size) 987 + { 988 + unsigned int order = get_order(size); 989 + 990 + if (order < MAX_ORDER) 991 + __free_pages(pfn_to_page(paddr >> PAGE_SHIFT), order); 992 + else 993 + dma_free_coherent(&hdev->device, 994 + round_up(size, PAGE_SIZE), 995 + phys_to_virt(paddr), 996 + paddr); 997 + } 998 + 956 999 957 1000 /* Get framebuffer memory from Hyper-V video pci space */ 958 1001 static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info) ··· 1018 949 void __iomem *fb_virt; 1019 950 int gen2vm = efi_enabled(EFI_BOOT); 1020 951 resource_size_t pot_start, pot_end; 952 + phys_addr_t paddr; 1021 953 int ret; 1022 954 955 + info->apertures = alloc_apertures(1); 956 + if (!info->apertures) 957 + return -ENOMEM; 958 + 959 + if (!gen2vm) { 960 + pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT, 961 + PCI_DEVICE_ID_HYPERV_VIDEO, NULL); 962 + if (!pdev) { 963 + pr_err("Unable to find PCI Hyper-V video\n"); 964 + kfree(info->apertures); 965 + return -ENODEV; 966 + } 967 + 968 + info->apertures->ranges[0].base = pci_resource_start(pdev, 0); 969 + info->apertures->ranges[0].size = pci_resource_len(pdev, 0); 970 + 971 + /* 972 + * For Gen 1 VM, we can directly use the contiguous memory 973 + * from VM. If we succeed, deferred IO happens directly 974 + * on this allocated framebuffer memory, avoiding extra 975 + * memory copy. 976 + */ 977 + paddr = hvfb_get_phymem(hdev, screen_fb_size); 978 + if (paddr != (phys_addr_t) -1) { 979 + par->mmio_pp = paddr; 980 + par->mmio_vp = par->dio_vp = __va(paddr); 981 + 982 + info->fix.smem_start = paddr; 983 + info->fix.smem_len = screen_fb_size; 984 + info->screen_base = par->mmio_vp; 985 + info->screen_size = screen_fb_size; 986 + 987 + par->need_docopy = false; 988 + goto getmem_done; 989 + } 990 + pr_info("Unable to allocate enough contiguous physical memory on Gen 1 VM. Using MMIO instead.\n"); 991 + } else { 992 + info->apertures->ranges[0].base = screen_info.lfb_base; 993 + info->apertures->ranges[0].size = screen_info.lfb_size; 994 + } 995 + 996 + /* 997 + * Cannot use the contiguous physical memory. 998 + * Allocate mmio space for framebuffer. 999 + */ 1023 1000 dio_fb_size = 1024 1001 screen_width * screen_height * screen_depth / 8; 1025 1002 ··· 1073 958 pot_start = 0; 1074 959 pot_end = -1; 1075 960 } else { 1076 - pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT, 1077 - PCI_DEVICE_ID_HYPERV_VIDEO, NULL); 1078 - if (!pdev) { 1079 - pr_err("Unable to find PCI Hyper-V video\n"); 1080 - return -ENODEV; 1081 - } 1082 - 1083 961 if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) || 1084 962 pci_resource_len(pdev, 0) < screen_fb_size) { 1085 963 pr_err("Resource not available or (0x%lx < 0x%lx)\n", ··· 1101 993 if (par->dio_vp == NULL) 1102 994 goto err3; 1103 995 1104 - info->apertures = alloc_apertures(1); 1105 - if (!info->apertures) 1106 - goto err4; 1107 - 1108 - if (gen2vm) { 1109 - info->apertures->ranges[0].base = screen_info.lfb_base; 1110 - info->apertures->ranges[0].size = screen_info.lfb_size; 1111 - remove_conflicting_framebuffers(info->apertures, 1112 - KBUILD_MODNAME, false); 1113 - } else { 1114 - info->apertures->ranges[0].base = pci_resource_start(pdev, 0); 1115 - info->apertures->ranges[0].size = pci_resource_len(pdev, 0); 1116 - } 1117 - 1118 996 /* Physical address of FB device */ 1119 997 par->mmio_pp = par->mem->start; 1120 998 /* Virtual address of FB device */ ··· 1111 1017 info->screen_base = par->dio_vp; 1112 1018 info->screen_size = dio_fb_size; 1113 1019 1020 + getmem_done: 1021 + remove_conflicting_framebuffers(info->apertures, 1022 + KBUILD_MODNAME, false); 1114 1023 if (!gen2vm) 1115 1024 pci_dev_put(pdev); 1025 + kfree(info->apertures); 1116 1026 1117 1027 return 0; 1118 1028 1119 - err4: 1120 - vfree(par->dio_vp); 1121 1029 err3: 1122 1030 iounmap(fb_virt); 1123 1031 err2: ··· 1128 1032 err1: 1129 1033 if (!gen2vm) 1130 1034 pci_dev_put(pdev); 1035 + kfree(info->apertures); 1131 1036 1132 1037 return -ENOMEM; 1133 1038 } 1134 1039 1135 1040 /* Release the framebuffer */ 1136 - static void hvfb_putmem(struct fb_info *info) 1041 + static void hvfb_putmem(struct hv_device *hdev, struct fb_info *info) 1137 1042 { 1138 1043 struct hvfb_par *par = info->par; 1139 1044 1140 - vfree(par->dio_vp); 1141 - iounmap(info->screen_base); 1142 - vmbus_free_mmio(par->mem->start, screen_fb_size); 1045 + if (par->need_docopy) { 1046 + vfree(par->dio_vp); 1047 + iounmap(info->screen_base); 1048 + vmbus_free_mmio(par->mem->start, screen_fb_size); 1049 + } else { 1050 + hvfb_release_phymem(hdev, info->fix.smem_start, 1051 + screen_fb_size); 1052 + } 1053 + 1143 1054 par->mem = NULL; 1144 1055 } 1145 1056 ··· 1165 1062 par = info->par; 1166 1063 par->info = info; 1167 1064 par->fb_ready = false; 1065 + par->need_docopy = true; 1168 1066 init_completion(&par->wait); 1169 1067 INIT_DELAYED_WORK(&par->dwork, hvfb_update_work); 1170 1068 ··· 1251 1147 1252 1148 error: 1253 1149 fb_deferred_io_cleanup(info); 1254 - hvfb_putmem(info); 1150 + hvfb_putmem(hdev, info); 1255 1151 error2: 1256 1152 vmbus_close(hdev->channel); 1257 1153 error1: ··· 1281 1177 vmbus_close(hdev->channel); 1282 1178 hv_set_drvdata(hdev, NULL); 1283 1179 1284 - hvfb_putmem(info); 1180 + hvfb_putmem(hdev, info); 1285 1181 framebuffer_release(info); 1286 1182 1287 1183 return 0;