#include <sys/cdefs.h>
#include "opt_vm.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bitset.h>
#include <sys/domainset.h>
#include <sys/proc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/malloc.h>
#include <sys/rwlock.h>
#include <sys/pctrie.h>
#include <sys/vmmeter.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/vm_domainset.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/vm_phys.h>
#ifdef NUMA
static int vm_domainset_default_stride = 64;
static bool vm_domainset_iter_next(struct vm_domainset_iter *di, int *domain);
static void
vm_domainset_iter_init(struct vm_domainset_iter *di, struct domainset *ds,
int *iter, struct vm_object *obj, vm_pindex_t pindex)
{
di->di_domain = ds;
di->di_iter = iter;
di->di_policy = ds->ds_policy;
DOMAINSET_COPY(&ds->ds_mask, &di->di_valid_mask);
if (di->di_policy == DOMAINSET_POLICY_INTERLEAVE) {
#if VM_NRESERVLEVEL > 0
if (vm_object_reserv(obj)) {
pindex += obj->pg_color;
#if VM_NRESERVLEVEL > 1
pindex >>= VM_LEVEL_1_ORDER;
#endif
pindex >>= VM_LEVEL_0_ORDER;
} else
#endif
pindex /= vm_domainset_default_stride;
if (obj != NULL)
pindex += (((uintptr_t)obj) / sizeof(*obj));
di->di_offset = pindex;
}
}
static void
vm_domainset_iter_rr(struct vm_domainset_iter *di, int *domain)
{
*domain = di->di_domain->ds_order[
(*di->di_iter)++ % di->di_domain->ds_cnt];
}
static void
vm_domainset_iter_interleave(struct vm_domainset_iter *di, int *domain)
{
int d;
d = di->di_offset % di->di_domain->ds_cnt;
*domain = di->di_domain->ds_order[d];
}
static bool
vm_domainset_iter_phase_first(struct vm_domainset_iter *di, int *domain)
{
switch (di->di_policy) {
case DOMAINSET_POLICY_FIRSTTOUCH:
*domain = PCPU_GET(domain);
break;
case DOMAINSET_POLICY_ROUNDROBIN:
vm_domainset_iter_rr(di, domain);
break;
case DOMAINSET_POLICY_PREFER:
*domain = di->di_domain->ds_prefer;
break;
case DOMAINSET_POLICY_INTERLEAVE:
vm_domainset_iter_interleave(di, domain);
break;
default:
panic("%s: Unknown policy %d", __func__, di->di_policy);
}
KASSERT(*domain < vm_ndomains,
("%s: Invalid domain %d", __func__, *domain));
if (!DOMAINSET_ISSET(*domain, &di->di_remain_mask))
return (vm_domainset_iter_next(di, domain));
DOMAINSET_CLR(*domain, &di->di_remain_mask);
if (di->di_minskip && vm_page_count_min_domain(*domain)) {
DOMAINSET_SET(*domain, &di->di_min_mask);
return (vm_domainset_iter_next(di, domain));
}
return (true);
}
static bool
vm_domainset_iter_first(struct vm_domainset_iter *di, int *domain)
{
DOMAINSET_COPY(&di->di_valid_mask, &di->di_remain_mask);
DOMAINSET_ZERO(&di->di_min_mask);
di->di_minskip = true;
return (vm_domainset_iter_phase_first(di, domain));
}
static bool
vm_domainset_iter_next(struct vm_domainset_iter *di, int *domain)
{
while (!DOMAINSET_EMPTY(&di->di_remain_mask)) {
vm_domainset_iter_rr(di, domain);
KASSERT(*domain < vm_ndomains,
("%s: Invalid domain %d", __func__, *domain));
if (DOMAINSET_ISSET(*domain, &di->di_remain_mask)) {
DOMAINSET_CLR(*domain, &di->di_remain_mask);
if (!di->di_minskip || !vm_page_count_min_domain(*domain))
return (true);
DOMAINSET_SET(*domain, &di->di_min_mask);
}
}
if (di->di_minskip) {
di->di_minskip = false;
DOMAINSET_COPY(&di->di_min_mask, &di->di_remain_mask);
return (vm_domainset_iter_phase_first(di, domain));
}
return (false);
}
int
vm_domainset_iter_page_init(struct vm_domainset_iter *di, struct vm_object *obj,
vm_pindex_t pindex, int *domain, int *req)
{
struct domainset_ref *dr;
di->di_flags = *req;
*req = (di->di_flags & ~(VM_ALLOC_WAITOK | VM_ALLOC_WAITFAIL)) |
VM_ALLOC_NOWAIT;
if (obj != NULL && obj->domain.dr_policy != NULL) {
VM_OBJECT_ASSERT_WLOCKED(obj);
dr = &obj->domain;
} else
dr = &curthread->td_domain;
vm_domainset_iter_init(di, dr->dr_policy, &dr->dr_iter, obj, pindex);
return (vm_domainset_iter_first(di, domain) ? 0 : ENOMEM);
}
int
vm_domainset_iter_page(struct vm_domainset_iter *di, struct vm_object *obj,
int *domain, struct pctrie_iter *pages)
{
if (vm_domainset_iter_next(di, domain))
return (0);
if ((di->di_flags & (VM_ALLOC_WAITOK | VM_ALLOC_WAITFAIL)) == 0)
return (ENOMEM);
if (obj != NULL) {
VM_OBJECT_WUNLOCK(obj);
if (pages != NULL)
pctrie_iter_reset(pages);
}
vm_wait_doms(&di->di_valid_mask, 0);
if (obj != NULL)
VM_OBJECT_WLOCK(obj);
if ((di->di_flags & VM_ALLOC_WAITFAIL) != 0)
return (ENOMEM);
return (vm_domainset_iter_first(di, domain) ? 0 : ENOMEM);
}
static int
_vm_domainset_iter_policy_init(struct vm_domainset_iter *di, int *domain,
int *flags)
{
di->di_flags = *flags;
*flags = (di->di_flags & ~M_WAITOK) | M_NOWAIT;
return (vm_domainset_iter_first(di, domain) ? 0 : ENOMEM);
}
int
vm_domainset_iter_policy_init(struct vm_domainset_iter *di,
struct domainset *ds, int *domain, int *flags)
{
vm_domainset_iter_init(di, ds, &curthread->td_domain.dr_iter, NULL, 0);
return (_vm_domainset_iter_policy_init(di, domain, flags));
}
int
vm_domainset_iter_policy_ref_init(struct vm_domainset_iter *di,
struct domainset_ref *dr, int *domain, int *flags)
{
vm_domainset_iter_init(di, dr->dr_policy, &dr->dr_iter, NULL, 0);
return (_vm_domainset_iter_policy_init(di, domain, flags));
}
int
vm_domainset_iter_policy(struct vm_domainset_iter *di, int *domain)
{
if (vm_domainset_iter_next(di, domain))
return (0);
if ((di->di_flags & M_WAITOK) == 0)
return (ENOMEM);
vm_wait_doms(&di->di_valid_mask, 0);
return (vm_domainset_iter_first(di, domain) ? 0 : ENOMEM);
}
void
vm_domainset_iter_ignore(struct vm_domainset_iter *di, int domain)
{
KASSERT(DOMAINSET_ISSET(domain, &di->di_valid_mask),
("%s: domain %d not present in di_valid_mask for di %p",
__func__, domain, di));
DOMAINSET_CLR(domain, &di->di_valid_mask);
}
#else
int
vm_domainset_iter_page(struct vm_domainset_iter *di, struct vm_object *obj,
int *domain, struct pctrie_iter *pages)
{
return (EJUSTRETURN);
}
int
vm_domainset_iter_page_init(struct vm_domainset_iter *di, struct vm_object *obj,
vm_pindex_t pindex, int *domain, int *flags)
{
*domain = 0;
return (0);
}
int
vm_domainset_iter_policy(struct vm_domainset_iter *di, int *domain)
{
return (EJUSTRETURN);
}
int
vm_domainset_iter_policy_init(struct vm_domainset_iter *di,
struct domainset *ds, int *domain, int *flags)
{
*domain = 0;
return (0);
}
int
vm_domainset_iter_policy_ref_init(struct vm_domainset_iter *di,
struct domainset_ref *dr, int *domain, int *flags)
{
*domain = 0;
return (0);
}
void
vm_domainset_iter_ignore(struct vm_domainset_iter *di __unused,
int domain __unused)
{
}
#endif