Skip to content

Commit 3a6fb6c

Browse files
whu2014Sasha Levin
authored andcommitted
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 <[email protected]> Signed-off-by: Wei Hu <[email protected]> Acked-by: Bartlomiej Zolnierkiewicz <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent ddc9d35 commit 3a6fb6c

File tree

2 files changed

+144
-39
lines changed

2 files changed

+144
-39
lines changed

drivers/video/fbdev/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2215,6 +2215,7 @@ config FB_HYPERV
22152215
select FB_CFB_COPYAREA
22162216
select FB_CFB_IMAGEBLIT
22172217
select FB_DEFERRED_IO
2218+
select DMA_CMA if HAVE_DMA_CONTIGUOUS && CMA
22182219
help
22192220
This framebuffer driver supports Microsoft Hyper-V Synthetic Video.
22202221

drivers/video/fbdev/hyperv_fb.c

Lines changed: 143 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@
3131
* "set-vmvideo" command. For example
3232
* set-vmvideo -vmname name -horizontalresolution:1920 \
3333
* -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.
3444
*/
3545

3646
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -228,7 +238,6 @@ struct synthvid_msg {
228238
} __packed;
229239

230240

231-
232241
/* FB driver definitions and structures */
233242
#define HVFB_WIDTH 1152 /* default screen width */
234243
#define HVFB_HEIGHT 864 /* default screen height */
@@ -258,12 +267,15 @@ struct hvfb_par {
258267
/* If true, the VSC notifies the VSP on every framebuffer change */
259268
bool synchronous_fb;
260269

270+
/* If true, need to copy from deferred IO mem to framebuffer mem */
271+
bool need_docopy;
272+
261273
struct notifier_block hvfb_panic_nb;
262274

263275
/* Memory for deferred IO and frame buffer itself */
264276
unsigned char *dio_vp;
265277
unsigned char *mmio_vp;
266-
unsigned long mmio_pp;
278+
phys_addr_t mmio_pp;
267279

268280
/* Dirty rectangle, protected by delayed_refresh_lock */
269281
int x1, y1, x2, y2;
@@ -434,7 +446,7 @@ static void synthvid_deferred_io(struct fb_info *p,
434446
maxy = max_t(int, maxy, y2);
435447

436448
/* Copy from dio space to mmio address */
437-
if (par->fb_ready)
449+
if (par->fb_ready && par->need_docopy)
438450
hvfb_docopy(par, start, PAGE_SIZE);
439451
}
440452

@@ -751,12 +763,12 @@ static void hvfb_update_work(struct work_struct *w)
751763
return;
752764

753765
/* Copy the dirty rectangle to frame buffer memory */
754-
for (j = y1; j < y2; j++) {
755-
hvfb_docopy(par,
756-
j * info->fix.line_length +
757-
(x1 * screen_depth / 8),
758-
(x2 - x1) * screen_depth / 8);
759-
}
766+
if (par->need_docopy)
767+
for (j = y1; j < y2; j++)
768+
hvfb_docopy(par,
769+
j * info->fix.line_length +
770+
(x1 * screen_depth / 8),
771+
(x2 - x1) * screen_depth / 8);
760772

761773
/* Refresh */
762774
if (par->fb_ready && par->update)
@@ -801,7 +813,8 @@ static int hvfb_on_panic(struct notifier_block *nb,
801813
par = container_of(nb, struct hvfb_par, hvfb_panic_nb);
802814
par->synchronous_fb = true;
803815
info = par->info;
804-
hvfb_docopy(par, 0, dio_fb_size);
816+
if (par->need_docopy)
817+
hvfb_docopy(par, 0, dio_fb_size);
805818
synthvid_update(info, 0, 0, INT_MAX, INT_MAX);
806819

807820
return NOTIFY_DONE;
@@ -940,6 +953,62 @@ static void hvfb_get_option(struct fb_info *info)
940953
return;
941954
}
942955

956+
/*
957+
* Allocate enough contiguous physical memory.
958+
* Return physical address if succeeded or -1 if failed.
959+
*/
960+
static phys_addr_t hvfb_get_phymem(struct hv_device *hdev,
961+
unsigned int request_size)
962+
{
963+
struct page *page = NULL;
964+
dma_addr_t dma_handle;
965+
void *vmem;
966+
phys_addr_t paddr = 0;
967+
unsigned int order = get_order(request_size);
968+
969+
if (request_size == 0)
970+
return -1;
971+
972+
if (order < MAX_ORDER) {
973+
/* Call alloc_pages if the size is less than 2^MAX_ORDER */
974+
page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
975+
if (!page)
976+
return -1;
977+
978+
paddr = (page_to_pfn(page) << PAGE_SHIFT);
979+
} else {
980+
/* Allocate from CMA */
981+
hdev->device.coherent_dma_mask = DMA_BIT_MASK(64);
982+
983+
vmem = dma_alloc_coherent(&hdev->device,
984+
round_up(request_size, PAGE_SIZE),
985+
&dma_handle,
986+
GFP_KERNEL | __GFP_NOWARN);
987+
988+
if (!vmem)
989+
return -1;
990+
991+
paddr = virt_to_phys(vmem);
992+
}
993+
994+
return paddr;
995+
}
996+
997+
/* Release contiguous physical memory */
998+
static void hvfb_release_phymem(struct hv_device *hdev,
999+
phys_addr_t paddr, unsigned int size)
1000+
{
1001+
unsigned int order = get_order(size);
1002+
1003+
if (order < MAX_ORDER)
1004+
__free_pages(pfn_to_page(paddr >> PAGE_SHIFT), order);
1005+
else
1006+
dma_free_coherent(&hdev->device,
1007+
round_up(size, PAGE_SIZE),
1008+
phys_to_virt(paddr),
1009+
paddr);
1010+
}
1011+
9431012

9441013
/* Get framebuffer memory from Hyper-V video pci space */
9451014
static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
@@ -949,22 +1018,61 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
9491018
void __iomem *fb_virt;
9501019
int gen2vm = efi_enabled(EFI_BOOT);
9511020
resource_size_t pot_start, pot_end;
1021+
phys_addr_t paddr;
9521022
int ret;
9531023

954-
dio_fb_size =
955-
screen_width * screen_height * screen_depth / 8;
1024+
info->apertures = alloc_apertures(1);
1025+
if (!info->apertures)
1026+
return -ENOMEM;
9561027

957-
if (gen2vm) {
958-
pot_start = 0;
959-
pot_end = -1;
960-
} else {
1028+
if (!gen2vm) {
9611029
pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT,
962-
PCI_DEVICE_ID_HYPERV_VIDEO, NULL);
1030+
PCI_DEVICE_ID_HYPERV_VIDEO, NULL);
9631031
if (!pdev) {
9641032
pr_err("Unable to find PCI Hyper-V video\n");
1033+
kfree(info->apertures);
9651034
return -ENODEV;
9661035
}
9671036

1037+
info->apertures->ranges[0].base = pci_resource_start(pdev, 0);
1038+
info->apertures->ranges[0].size = pci_resource_len(pdev, 0);
1039+
1040+
/*
1041+
* For Gen 1 VM, we can directly use the contiguous memory
1042+
* from VM. If we succeed, deferred IO happens directly
1043+
* on this allocated framebuffer memory, avoiding extra
1044+
* memory copy.
1045+
*/
1046+
paddr = hvfb_get_phymem(hdev, screen_fb_size);
1047+
if (paddr != (phys_addr_t) -1) {
1048+
par->mmio_pp = paddr;
1049+
par->mmio_vp = par->dio_vp = __va(paddr);
1050+
1051+
info->fix.smem_start = paddr;
1052+
info->fix.smem_len = screen_fb_size;
1053+
info->screen_base = par->mmio_vp;
1054+
info->screen_size = screen_fb_size;
1055+
1056+
par->need_docopy = false;
1057+
goto getmem_done;
1058+
}
1059+
pr_info("Unable to allocate enough contiguous physical memory on Gen 1 VM. Using MMIO instead.\n");
1060+
} else {
1061+
info->apertures->ranges[0].base = screen_info.lfb_base;
1062+
info->apertures->ranges[0].size = screen_info.lfb_size;
1063+
}
1064+
1065+
/*
1066+
* Cannot use the contiguous physical memory.
1067+
* Allocate mmio space for framebuffer.
1068+
*/
1069+
dio_fb_size =
1070+
screen_width * screen_height * screen_depth / 8;
1071+
1072+
if (gen2vm) {
1073+
pot_start = 0;
1074+
pot_end = -1;
1075+
} else {
9681076
if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) ||
9691077
pci_resource_len(pdev, 0) < screen_fb_size) {
9701078
pr_err("Resource not available or (0x%lx < 0x%lx)\n",
@@ -993,20 +1101,6 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
9931101
if (par->dio_vp == NULL)
9941102
goto err3;
9951103

996-
info->apertures = alloc_apertures(1);
997-
if (!info->apertures)
998-
goto err4;
999-
1000-
if (gen2vm) {
1001-
info->apertures->ranges[0].base = screen_info.lfb_base;
1002-
info->apertures->ranges[0].size = screen_info.lfb_size;
1003-
remove_conflicting_framebuffers(info->apertures,
1004-
KBUILD_MODNAME, false);
1005-
} else {
1006-
info->apertures->ranges[0].base = pci_resource_start(pdev, 0);
1007-
info->apertures->ranges[0].size = pci_resource_len(pdev, 0);
1008-
}
1009-
10101104
/* Physical address of FB device */
10111105
par->mmio_pp = par->mem->start;
10121106
/* Virtual address of FB device */
@@ -1017,13 +1111,15 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
10171111
info->screen_base = par->dio_vp;
10181112
info->screen_size = dio_fb_size;
10191113

1114+
getmem_done:
1115+
remove_conflicting_framebuffers(info->apertures,
1116+
KBUILD_MODNAME, false);
10201117
if (!gen2vm)
10211118
pci_dev_put(pdev);
1119+
kfree(info->apertures);
10221120

10231121
return 0;
10241122

1025-
err4:
1026-
vfree(par->dio_vp);
10271123
err3:
10281124
iounmap(fb_virt);
10291125
err2:
@@ -1032,18 +1128,25 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info)
10321128
err1:
10331129
if (!gen2vm)
10341130
pci_dev_put(pdev);
1131+
kfree(info->apertures);
10351132

10361133
return -ENOMEM;
10371134
}
10381135

10391136
/* Release the framebuffer */
1040-
static void hvfb_putmem(struct fb_info *info)
1137+
static void hvfb_putmem(struct hv_device *hdev, struct fb_info *info)
10411138
{
10421139
struct hvfb_par *par = info->par;
10431140

1044-
vfree(par->dio_vp);
1045-
iounmap(info->screen_base);
1046-
vmbus_free_mmio(par->mem->start, screen_fb_size);
1141+
if (par->need_docopy) {
1142+
vfree(par->dio_vp);
1143+
iounmap(info->screen_base);
1144+
vmbus_free_mmio(par->mem->start, screen_fb_size);
1145+
} else {
1146+
hvfb_release_phymem(hdev, info->fix.smem_start,
1147+
screen_fb_size);
1148+
}
1149+
10471150
par->mem = NULL;
10481151
}
10491152

@@ -1062,6 +1165,7 @@ static int hvfb_probe(struct hv_device *hdev,
10621165
par = info->par;
10631166
par->info = info;
10641167
par->fb_ready = false;
1168+
par->need_docopy = true;
10651169
init_completion(&par->wait);
10661170
INIT_DELAYED_WORK(&par->dwork, hvfb_update_work);
10671171

@@ -1147,7 +1251,7 @@ static int hvfb_probe(struct hv_device *hdev,
11471251

11481252
error:
11491253
fb_deferred_io_cleanup(info);
1150-
hvfb_putmem(info);
1254+
hvfb_putmem(hdev, info);
11511255
error2:
11521256
vmbus_close(hdev->channel);
11531257
error1:
@@ -1177,7 +1281,7 @@ static int hvfb_remove(struct hv_device *hdev)
11771281
vmbus_close(hdev->channel);
11781282
hv_set_drvdata(hdev, NULL);
11791283

1180-
hvfb_putmem(info);
1284+
hvfb_putmem(hdev, info);
11811285
framebuffer_release(info);
11821286

11831287
return 0;

0 commit comments

Comments
 (0)