Skip to content

Commit 9e2369c

Browse files
roygerjgross1
authored andcommitted
xen: add helpers to allocate unpopulated memory
To be used in order to create foreign mappings. This is based on the ZONE_DEVICE facility which is used by persistent memory devices in order to create struct pages and kernel virtual mappings for the IOMEM areas of such devices. Note that on kernels without support for ZONE_DEVICE Xen will fallback to use ballooned pages in order to create foreign mappings. The newly added helpers use the same parameters as the existing {alloc/free}_xenballooned_pages functions, which allows for in-place replacement of the callers. Once a memory region has been added to be used as scratch mapping space it will no longer be released, and pages returned are kept in a linked list. This allows to have a buffer of pages and prevents resorting to frequent additions and removals of regions. If enabled (because ZONE_DEVICE is supported) the usage of the new functionality untangles Xen balloon and RAM hotplug from the usage of unpopulated physical memory ranges to map foreign pages, which is the correct thing to do in order to avoid mappings of foreign pages depend on memory hotplug. Note the driver is currently not enabled on Arm platforms because it would interfere with the identity mapping required on some platforms. Signed-off-by: Roger Pau Monné <[email protected]> Reviewed-by: Juergen Gross <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Juergen Gross <[email protected]>
1 parent 4533d3a commit 9e2369c

File tree

10 files changed

+219
-15
lines changed

10 files changed

+219
-15
lines changed

drivers/gpu/drm/xen/xen_drm_front_gem.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <drm/drm_probe_helper.h>
1919

2020
#include <xen/balloon.h>
21+
#include <xen/xen.h>
2122

2223
#include "xen_drm_front.h"
2324
#include "xen_drm_front_gem.h"
@@ -99,8 +100,8 @@ static struct xen_gem_object *gem_create(struct drm_device *dev, size_t size)
99100
* allocate ballooned pages which will be used to map
100101
* grant references provided by the backend
101102
*/
102-
ret = alloc_xenballooned_pages(xen_obj->num_pages,
103-
xen_obj->pages);
103+
ret = xen_alloc_unpopulated_pages(xen_obj->num_pages,
104+
xen_obj->pages);
104105
if (ret < 0) {
105106
DRM_ERROR("Cannot allocate %zu ballooned pages: %d\n",
106107
xen_obj->num_pages, ret);
@@ -152,8 +153,8 @@ void xen_drm_front_gem_free_object_unlocked(struct drm_gem_object *gem_obj)
152153
} else {
153154
if (xen_obj->pages) {
154155
if (xen_obj->be_alloc) {
155-
free_xenballooned_pages(xen_obj->num_pages,
156-
xen_obj->pages);
156+
xen_free_unpopulated_pages(xen_obj->num_pages,
157+
xen_obj->pages);
157158
gem_free_pages_array(xen_obj);
158159
} else {
159160
drm_gem_put_pages(&xen_obj->base,

drivers/xen/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,4 +324,14 @@ config XEN_HAVE_VPMU
324324
config XEN_FRONT_PGDIR_SHBUF
325325
tristate
326326

327+
config XEN_UNPOPULATED_ALLOC
328+
bool "Use unpopulated memory ranges for guest mappings"
329+
depends on X86 && ZONE_DEVICE
330+
default XEN_BACKEND || XEN_GNTDEV || XEN_DOM0
331+
help
332+
Use unpopulated memory ranges in order to create mappings for guest
333+
memory regions, including grant maps and foreign pages. This avoids
334+
having to balloon out RAM regions in order to obtain physical memory
335+
space to create such mappings.
336+
327337
endmenu

drivers/xen/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,4 @@ xen-gntdev-$(CONFIG_XEN_GNTDEV_DMABUF) += gntdev-dmabuf.o
4242
xen-gntalloc-y := gntalloc.o
4343
xen-privcmd-y := privcmd.o privcmd-buf.o
4444
obj-$(CONFIG_XEN_FRONT_PGDIR_SHBUF) += xen-front-pgdir-shbuf.o
45+
obj-$(CONFIG_XEN_UNPOPULATED_ALLOC) += unpopulated-alloc.o

drivers/xen/balloon.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,7 @@ void free_xenballooned_pages(int nr_pages, struct page **pages)
654654
}
655655
EXPORT_SYMBOL(free_xenballooned_pages);
656656

657-
#ifdef CONFIG_XEN_PV
657+
#if defined(CONFIG_XEN_PV) && !defined(CONFIG_XEN_UNPOPULATED_ALLOC)
658658
static void __init balloon_add_region(unsigned long start_pfn,
659659
unsigned long pages)
660660
{
@@ -708,7 +708,7 @@ static int __init balloon_init(void)
708708
register_sysctl_table(xen_root);
709709
#endif
710710

711-
#ifdef CONFIG_XEN_PV
711+
#if defined(CONFIG_XEN_PV) && !defined(CONFIG_XEN_UNPOPULATED_ALLOC)
712712
{
713713
int i;
714714

drivers/xen/grant-table.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ int gnttab_alloc_pages(int nr_pages, struct page **pages)
801801
{
802802
int ret;
803803

804-
ret = alloc_xenballooned_pages(nr_pages, pages);
804+
ret = xen_alloc_unpopulated_pages(nr_pages, pages);
805805
if (ret < 0)
806806
return ret;
807807

@@ -836,7 +836,7 @@ EXPORT_SYMBOL_GPL(gnttab_pages_clear_private);
836836
void gnttab_free_pages(int nr_pages, struct page **pages)
837837
{
838838
gnttab_pages_clear_private(nr_pages, pages);
839-
free_xenballooned_pages(nr_pages, pages);
839+
xen_free_unpopulated_pages(nr_pages, pages);
840840
}
841841
EXPORT_SYMBOL_GPL(gnttab_free_pages);
842842

drivers/xen/privcmd.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ static int alloc_empty_pages(struct vm_area_struct *vma, int numpgs)
425425
if (pages == NULL)
426426
return -ENOMEM;
427427

428-
rc = alloc_xenballooned_pages(numpgs, pages);
428+
rc = xen_alloc_unpopulated_pages(numpgs, pages);
429429
if (rc != 0) {
430430
pr_warn("%s Could not alloc %d pfns rc:%d\n", __func__,
431431
numpgs, rc);
@@ -896,7 +896,7 @@ static void privcmd_close(struct vm_area_struct *vma)
896896

897897
rc = xen_unmap_domain_gfn_range(vma, numgfns, pages);
898898
if (rc == 0)
899-
free_xenballooned_pages(numpgs, pages);
899+
xen_free_unpopulated_pages(numpgs, pages);
900900
else
901901
pr_crit("unable to unmap MFN range: leaking %d pages. rc=%d\n",
902902
numpgs, rc);

drivers/xen/unpopulated-alloc.c

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <linux/errno.h>
3+
#include <linux/gfp.h>
4+
#include <linux/kernel.h>
5+
#include <linux/mm.h>
6+
#include <linux/memremap.h>
7+
#include <linux/slab.h>
8+
9+
#include <asm/page.h>
10+
11+
#include <xen/page.h>
12+
#include <xen/xen.h>
13+
14+
static DEFINE_MUTEX(list_lock);
15+
static LIST_HEAD(page_list);
16+
static unsigned int list_count;
17+
18+
static int fill_list(unsigned int nr_pages)
19+
{
20+
struct dev_pagemap *pgmap;
21+
void *vaddr;
22+
unsigned int i, alloc_pages = round_up(nr_pages, PAGES_PER_SECTION);
23+
int ret;
24+
25+
pgmap = kzalloc(sizeof(*pgmap), GFP_KERNEL);
26+
if (!pgmap)
27+
return -ENOMEM;
28+
29+
pgmap->type = MEMORY_DEVICE_GENERIC;
30+
pgmap->res.name = "Xen scratch";
31+
pgmap->res.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
32+
33+
ret = allocate_resource(&iomem_resource, &pgmap->res,
34+
alloc_pages * PAGE_SIZE, 0, -1,
35+
PAGES_PER_SECTION * PAGE_SIZE, NULL, NULL);
36+
if (ret < 0) {
37+
pr_err("Cannot allocate new IOMEM resource\n");
38+
kfree(pgmap);
39+
return ret;
40+
}
41+
42+
#ifdef CONFIG_XEN_HAVE_PVMMU
43+
/*
44+
* memremap will build page tables for the new memory so
45+
* the p2m must contain invalid entries so the correct
46+
* non-present PTEs will be written.
47+
*
48+
* If a failure occurs, the original (identity) p2m entries
49+
* are not restored since this region is now known not to
50+
* conflict with any devices.
51+
*/
52+
if (!xen_feature(XENFEAT_auto_translated_physmap)) {
53+
xen_pfn_t pfn = PFN_DOWN(pgmap->res.start);
54+
55+
for (i = 0; i < alloc_pages; i++) {
56+
if (!set_phys_to_machine(pfn + i, INVALID_P2M_ENTRY)) {
57+
pr_warn("set_phys_to_machine() failed, no memory added\n");
58+
release_resource(&pgmap->res);
59+
kfree(pgmap);
60+
return -ENOMEM;
61+
}
62+
}
63+
}
64+
#endif
65+
66+
vaddr = memremap_pages(pgmap, NUMA_NO_NODE);
67+
if (IS_ERR(vaddr)) {
68+
pr_err("Cannot remap memory range\n");
69+
release_resource(&pgmap->res);
70+
kfree(pgmap);
71+
return PTR_ERR(vaddr);
72+
}
73+
74+
for (i = 0; i < alloc_pages; i++) {
75+
struct page *pg = virt_to_page(vaddr + PAGE_SIZE * i);
76+
77+
BUG_ON(!virt_addr_valid(vaddr + PAGE_SIZE * i));
78+
list_add(&pg->lru, &page_list);
79+
list_count++;
80+
}
81+
82+
return 0;
83+
}
84+
85+
/**
86+
* xen_alloc_unpopulated_pages - alloc unpopulated pages
87+
* @nr_pages: Number of pages
88+
* @pages: pages returned
89+
* @return 0 on success, error otherwise
90+
*/
91+
int xen_alloc_unpopulated_pages(unsigned int nr_pages, struct page **pages)
92+
{
93+
unsigned int i;
94+
int ret = 0;
95+
96+
mutex_lock(&list_lock);
97+
if (list_count < nr_pages) {
98+
ret = fill_list(nr_pages - list_count);
99+
if (ret)
100+
goto out;
101+
}
102+
103+
for (i = 0; i < nr_pages; i++) {
104+
struct page *pg = list_first_entry_or_null(&page_list,
105+
struct page,
106+
lru);
107+
108+
BUG_ON(!pg);
109+
list_del(&pg->lru);
110+
list_count--;
111+
pages[i] = pg;
112+
113+
#ifdef CONFIG_XEN_HAVE_PVMMU
114+
if (!xen_feature(XENFEAT_auto_translated_physmap)) {
115+
ret = xen_alloc_p2m_entry(page_to_pfn(pg));
116+
if (ret < 0) {
117+
unsigned int j;
118+
119+
for (j = 0; j <= i; j++) {
120+
list_add(&pages[j]->lru, &page_list);
121+
list_count++;
122+
}
123+
goto out;
124+
}
125+
}
126+
#endif
127+
}
128+
129+
out:
130+
mutex_unlock(&list_lock);
131+
return ret;
132+
}
133+
EXPORT_SYMBOL(xen_alloc_unpopulated_pages);
134+
135+
/**
136+
* xen_free_unpopulated_pages - return unpopulated pages
137+
* @nr_pages: Number of pages
138+
* @pages: pages to return
139+
*/
140+
void xen_free_unpopulated_pages(unsigned int nr_pages, struct page **pages)
141+
{
142+
unsigned int i;
143+
144+
mutex_lock(&list_lock);
145+
for (i = 0; i < nr_pages; i++) {
146+
list_add(&pages[i]->lru, &page_list);
147+
list_count++;
148+
}
149+
mutex_unlock(&list_lock);
150+
}
151+
EXPORT_SYMBOL(xen_free_unpopulated_pages);
152+
153+
#ifdef CONFIG_XEN_PV
154+
static int __init init(void)
155+
{
156+
unsigned int i;
157+
158+
if (!xen_domain())
159+
return -ENODEV;
160+
161+
if (!xen_pv_domain())
162+
return 0;
163+
164+
/*
165+
* Initialize with pages from the extra memory regions (see
166+
* arch/x86/xen/setup.c).
167+
*/
168+
for (i = 0; i < XEN_EXTRA_MEM_MAX_REGIONS; i++) {
169+
unsigned int j;
170+
171+
for (j = 0; j < xen_extra_mem[i].n_pfns; j++) {
172+
struct page *pg =
173+
pfn_to_page(xen_extra_mem[i].start_pfn + j);
174+
175+
list_add(&pg->lru, &page_list);
176+
list_count++;
177+
}
178+
}
179+
180+
return 0;
181+
}
182+
subsys_initcall(init);
183+
#endif

drivers/xen/xenbus/xenbus_client.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ static int xenbus_map_ring_hvm(struct xenbus_device *dev,
621621
bool leaked = false;
622622
unsigned int nr_pages = XENBUS_PAGES(nr_grefs);
623623

624-
err = alloc_xenballooned_pages(nr_pages, node->hvm.pages);
624+
err = xen_alloc_unpopulated_pages(nr_pages, node->hvm.pages);
625625
if (err)
626626
goto out_err;
627627

@@ -662,7 +662,7 @@ static int xenbus_map_ring_hvm(struct xenbus_device *dev,
662662
addr, nr_pages);
663663
out_free_ballooned_pages:
664664
if (!leaked)
665-
free_xenballooned_pages(nr_pages, node->hvm.pages);
665+
xen_free_unpopulated_pages(nr_pages, node->hvm.pages);
666666
out_err:
667667
return err;
668668
}
@@ -858,7 +858,7 @@ static int xenbus_unmap_ring_hvm(struct xenbus_device *dev, void *vaddr)
858858
info.addrs);
859859
if (!rv) {
860860
vunmap(vaddr);
861-
free_xenballooned_pages(nr_pages, node->hvm.pages);
861+
xen_free_unpopulated_pages(nr_pages, node->hvm.pages);
862862
}
863863
else
864864
WARN(1, "Leaking %p, size %u page(s)\n", vaddr, nr_pages);

drivers/xen/xlate_mmu.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ int __init xen_xlate_map_ballooned_pages(xen_pfn_t **gfns, void **virt,
232232
kfree(pages);
233233
return -ENOMEM;
234234
}
235-
rc = alloc_xenballooned_pages(nr_pages, pages);
235+
rc = xen_alloc_unpopulated_pages(nr_pages, pages);
236236
if (rc) {
237237
pr_warn("%s Couldn't balloon alloc %ld pages rc:%d\n", __func__,
238238
nr_pages, rc);
@@ -249,7 +249,7 @@ int __init xen_xlate_map_ballooned_pages(xen_pfn_t **gfns, void **virt,
249249
if (!vaddr) {
250250
pr_warn("%s Couldn't map %ld pages rc:%d\n", __func__,
251251
nr_pages, rc);
252-
free_xenballooned_pages(nr_pages, pages);
252+
xen_free_unpopulated_pages(nr_pages, pages);
253253
kfree(pages);
254254
kfree(pfns);
255255
return -ENOMEM;

include/xen/xen.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,13 @@ bool xen_biovec_phys_mergeable(const struct bio_vec *vec1,
5252
extern u64 xen_saved_max_mem_size;
5353
#endif
5454

55+
#ifdef CONFIG_XEN_UNPOPULATED_ALLOC
56+
int xen_alloc_unpopulated_pages(unsigned int nr_pages, struct page **pages);
57+
void xen_free_unpopulated_pages(unsigned int nr_pages, struct page **pages);
58+
#else
59+
#define xen_alloc_unpopulated_pages alloc_xenballooned_pages
60+
#define xen_free_unpopulated_pages free_xenballooned_pages
61+
#include <xen/balloon.h>
62+
#endif
63+
5564
#endif /* _XEN_XEN_H */

0 commit comments

Comments
 (0)