vm_phys: remove limitation on number of fictitious regions

The number of vm fictitious regions was limited to 8 by default, but
Xen will make heavy usage of those kind of regions in order to map
memory from foreign domains, so instead of increasing the default
number, change the implementation to use a red-black tree to track vm
fictitious ranges.

The public interface remains the same.

Sponsored by: Citrix Systems R&D
Reviewed by: kib, alc
Approved by: gibbs

vm/vm_phys.c:
 - Replace the vm fictitious static array with a red-black tree.
 - Use a rwlock instead of a mutex, since now we also need to take the
   lock in vm_phys_fictitious_to_vm_page, and it can be shared.
This commit is contained in:
Roger Pau Monné 2014-07-09 08:12:58 +00:00
parent fe82cbe85c
commit 38d6b2dcb2

View File

@ -52,8 +52,10 @@ __FBSDID("$FreeBSD$");
#include <sys/proc.h>
#endif
#include <sys/queue.h>
#include <sys/rwlock.h>
#include <sys/sbuf.h>
#include <sys/sysctl.h>
#include <sys/tree.h>
#include <sys/vmmeter.h>
#include <ddb/ddb.h>
@ -75,13 +77,25 @@ int vm_ndomains = 1;
struct vm_phys_seg vm_phys_segs[VM_PHYSSEG_MAX];
int vm_phys_nsegs;
#define VM_PHYS_FICTITIOUS_NSEGS 8
static struct vm_phys_fictitious_seg {
struct vm_phys_fictitious_seg;
static int vm_phys_fictitious_cmp(struct vm_phys_fictitious_seg *,
struct vm_phys_fictitious_seg *);
RB_HEAD(fict_tree, vm_phys_fictitious_seg) vm_phys_fictitious_tree =
RB_INITIALIZER(_vm_phys_fictitious_tree);
struct vm_phys_fictitious_seg {
RB_ENTRY(vm_phys_fictitious_seg) node;
/* Memory region data */
vm_paddr_t start;
vm_paddr_t end;
vm_page_t first_page;
} vm_phys_fictitious_segs[VM_PHYS_FICTITIOUS_NSEGS];
static struct mtx vm_phys_fictitious_reg_mtx;
};
RB_GENERATE_STATIC(fict_tree, vm_phys_fictitious_seg, node,
vm_phys_fictitious_cmp);
static struct rwlock vm_phys_fictitious_reg_lock;
MALLOC_DEFINE(M_FICT_PAGES, "vm_fictitious", "Fictitious VM pages");
static struct vm_freelist
@ -113,6 +127,47 @@ static int vm_phys_paddr_to_segind(vm_paddr_t pa);
static void vm_phys_split_pages(vm_page_t m, int oind, struct vm_freelist *fl,
int order);
/*
* Red-black tree helpers for vm fictitious range management.
*/
static inline int
vm_phys_fictitious_in_range(struct vm_phys_fictitious_seg *p,
struct vm_phys_fictitious_seg *range)
{
KASSERT(range->start != 0 && range->end != 0,
("Invalid range passed on search for vm_fictitious page"));
if (p->start >= range->end)
return (1);
if (p->start < range->start)
return (-1);
return (0);
}
static int
vm_phys_fictitious_cmp(struct vm_phys_fictitious_seg *p1,
struct vm_phys_fictitious_seg *p2)
{
/* Check if this is a search for a page */
if (p1->end == 0)
return (vm_phys_fictitious_in_range(p1, p2));
KASSERT(p2->end != 0,
("Invalid range passed as second parameter to vm fictitious comparison"));
/* Searching to add a new range */
if (p1->end <= p2->start)
return (-1);
if (p1->start >= p2->end)
return (1);
panic("Trying to add overlapping vm fictitious ranges:\n"
"[%#jx:%#jx] and [%#jx:%#jx]", (uintmax_t)p1->start,
(uintmax_t)p1->end, (uintmax_t)p2->start, (uintmax_t)p2->end);
}
static __inline int
vm_rr_selectdomain(void)
{
@ -353,7 +408,7 @@ vm_phys_init(void)
}
}
}
mtx_init(&vm_phys_fictitious_reg_mtx, "vmfctr", NULL, MTX_DEF);
rw_init(&vm_phys_fictitious_reg_lock, "vmfctr");
}
/*
@ -517,20 +572,22 @@ vm_phys_paddr_to_vm_page(vm_paddr_t pa)
vm_page_t
vm_phys_fictitious_to_vm_page(vm_paddr_t pa)
{
struct vm_phys_fictitious_seg *seg;
struct vm_phys_fictitious_seg tmp, *seg;
vm_page_t m;
int segind;
m = NULL;
for (segind = 0; segind < VM_PHYS_FICTITIOUS_NSEGS; segind++) {
seg = &vm_phys_fictitious_segs[segind];
if (pa >= seg->start && pa < seg->end) {
m = &seg->first_page[atop(pa - seg->start)];
KASSERT((m->flags & PG_FICTITIOUS) != 0,
("%p not fictitious", m));
break;
}
}
tmp.start = pa;
tmp.end = 0;
rw_rlock(&vm_phys_fictitious_reg_lock);
seg = RB_FIND(fict_tree, &vm_phys_fictitious_tree, &tmp);
rw_runlock(&vm_phys_fictitious_reg_lock);
if (seg == NULL)
return (NULL);
m = &seg->first_page[atop(pa - seg->start)];
KASSERT((m->flags & PG_FICTITIOUS) != 0, ("%p not fictitious", m));
return (m);
}
@ -541,10 +598,8 @@ vm_phys_fictitious_reg_range(vm_paddr_t start, vm_paddr_t end,
struct vm_phys_fictitious_seg *seg;
vm_page_t fp;
long i, page_count;
int segind;
#ifdef VM_PHYSSEG_DENSE
long pi;
boolean_t malloced;
#endif
page_count = (end - start) / PAGE_SIZE;
@ -555,46 +610,34 @@ vm_phys_fictitious_reg_range(vm_paddr_t start, vm_paddr_t end,
if (atop(end) >= vm_page_array_size + first_page)
return (EINVAL);
fp = &vm_page_array[pi - first_page];
malloced = FALSE;
} else
#endif
{
fp = malloc(page_count * sizeof(struct vm_page), M_FICT_PAGES,
M_WAITOK | M_ZERO);
#ifdef VM_PHYSSEG_DENSE
malloced = TRUE;
#endif
}
for (i = 0; i < page_count; i++) {
vm_page_initfake(&fp[i], start + PAGE_SIZE * i, memattr);
fp[i].oflags &= ~VPO_UNMANAGED;
fp[i].busy_lock = VPB_UNBUSIED;
}
mtx_lock(&vm_phys_fictitious_reg_mtx);
for (segind = 0; segind < VM_PHYS_FICTITIOUS_NSEGS; segind++) {
seg = &vm_phys_fictitious_segs[segind];
if (seg->start == 0 && seg->end == 0) {
seg->start = start;
seg->end = end;
seg->first_page = fp;
mtx_unlock(&vm_phys_fictitious_reg_mtx);
return (0);
}
}
mtx_unlock(&vm_phys_fictitious_reg_mtx);
#ifdef VM_PHYSSEG_DENSE
if (malloced)
#endif
free(fp, M_FICT_PAGES);
return (EBUSY);
seg = malloc(sizeof(*seg), M_FICT_PAGES, M_WAITOK | M_ZERO);
seg->start = start;
seg->end = end;
seg->first_page = fp;
rw_wlock(&vm_phys_fictitious_reg_lock);
RB_INSERT(fict_tree, &vm_phys_fictitious_tree, seg);
rw_wunlock(&vm_phys_fictitious_reg_lock);
return (0);
}
void
vm_phys_fictitious_unreg_range(vm_paddr_t start, vm_paddr_t end)
{
struct vm_phys_fictitious_seg *seg;
vm_page_t fp;
int segind;
struct vm_phys_fictitious_seg *seg, tmp;
#ifdef VM_PHYSSEG_DENSE
long pi;
#endif
@ -602,24 +645,24 @@ vm_phys_fictitious_unreg_range(vm_paddr_t start, vm_paddr_t end)
#ifdef VM_PHYSSEG_DENSE
pi = atop(start);
#endif
tmp.start = start;
tmp.end = 0;
mtx_lock(&vm_phys_fictitious_reg_mtx);
for (segind = 0; segind < VM_PHYS_FICTITIOUS_NSEGS; segind++) {
seg = &vm_phys_fictitious_segs[segind];
if (seg->start == start && seg->end == end) {
seg->start = seg->end = 0;
fp = seg->first_page;
seg->first_page = NULL;
mtx_unlock(&vm_phys_fictitious_reg_mtx);
#ifdef VM_PHYSSEG_DENSE
if (pi < first_page || atop(end) >= vm_page_array_size)
#endif
free(fp, M_FICT_PAGES);
return;
}
rw_wlock(&vm_phys_fictitious_reg_lock);
seg = RB_FIND(fict_tree, &vm_phys_fictitious_tree, &tmp);
if (seg->start != start || seg->end != end) {
rw_wunlock(&vm_phys_fictitious_reg_lock);
panic(
"Unregistering not registered fictitious range [%#jx:%#jx]",
(uintmax_t)start, (uintmax_t)end);
}
mtx_unlock(&vm_phys_fictitious_reg_mtx);
KASSERT(0, ("Unregistering not registered fictitious range"));
RB_REMOVE(fict_tree, &vm_phys_fictitious_tree, seg);
rw_wunlock(&vm_phys_fictitious_reg_lock);
#ifdef VM_PHYSSEG_DENSE
if (pi < first_page || atop(end) >= vm_page_array_size)
#endif
free(seg->first_page, M_FICT_PAGES);
free(seg, M_FICT_PAGES);
}
/*