Changeset View
Changeset View
Standalone View
Standalone View
sys/arm64/arm64/pmap.c
Show First 20 Lines • Show All 182 Lines • ▼ Show 20 Lines | ||||||||||||
#else | #else | |||||||||||
#define PV_STAT(x) do { } while (0) | #define PV_STAT(x) do { } while (0) | |||||||||||
#endif | #endif | |||||||||||
#define pmap_l0_pindex(v) (NUL2E + NUL1E + ((v) >> L0_SHIFT)) | #define pmap_l0_pindex(v) (NUL2E + NUL1E + ((v) >> L0_SHIFT)) | |||||||||||
#define pmap_l1_pindex(v) (NUL2E + ((v) >> L1_SHIFT)) | #define pmap_l1_pindex(v) (NUL2E + ((v) >> L1_SHIFT)) | |||||||||||
#define pmap_l2_pindex(v) ((v) >> L2_SHIFT) | #define pmap_l2_pindex(v) ((v) >> L2_SHIFT) | |||||||||||
static struct md_page * | struct pmap_large_md_page { | |||||||||||
pa_to_pvh(vm_paddr_t pa) | struct rwlock pv_lock; | |||||||||||
struct md_page pv_page; | ||||||||||||
/* Pad to a power of 2, see pmap_init_pv_table(). */ | ||||||||||||
int pv_pad[2]; | ||||||||||||
markjUnsubmitted Done Inline Actions
markj: | ||||||||||||
}; | ||||||||||||
static struct pmap_large_md_page * | ||||||||||||
_pa_to_pmdp(vm_paddr_t pa) | ||||||||||||
{ | { | |||||||||||
struct vm_phys_seg *seg; | struct vm_phys_seg *seg; | |||||||||||
int segind; | int segind; | |||||||||||
for (segind = 0; segind < vm_phys_nsegs; segind++) { | for (segind = 0; segind < vm_phys_nsegs; segind++) { | |||||||||||
seg = &vm_phys_segs[segind]; | seg = &vm_phys_segs[segind]; | |||||||||||
if (pa >= seg->start && pa < seg->end) | if (pa >= seg->start && pa < seg->end) | |||||||||||
return ((struct md_page *)seg->md_first + | return ((struct pmap_large_md_page *)seg->md_first + | |||||||||||
pmap_l2_pindex(pa) - pmap_l2_pindex(seg->start)); | pmap_l2_pindex(pa) - pmap_l2_pindex(seg->start)); | |||||||||||
} | } | |||||||||||
return (NULL); | ||||||||||||
} | ||||||||||||
static struct pmap_large_md_page * | ||||||||||||
pa_to_pmdp(vm_paddr_t pa) | ||||||||||||
{ | ||||||||||||
struct pmap_large_md_page *pvd; | ||||||||||||
pvd = _pa_to_pmdp(pa); | ||||||||||||
if (pvd == NULL) | ||||||||||||
panic("pa 0x%jx not within vm_phys_segs", (uintmax_t)pa); | panic("pa 0x%jx not within vm_phys_segs", (uintmax_t)pa); | |||||||||||
return (pvd); | ||||||||||||
} | } | |||||||||||
static struct md_page * | static struct pmap_large_md_page * | |||||||||||
page_to_pvh(vm_page_t m) | page_to_pmdp(vm_page_t m) | |||||||||||
{ | { | |||||||||||
struct vm_phys_seg *seg; | struct vm_phys_seg *seg; | |||||||||||
seg = &vm_phys_segs[m->segind]; | seg = &vm_phys_segs[m->segind]; | |||||||||||
return ((struct md_page *)seg->md_first + | return ((struct pmap_large_md_page *)seg->md_first + | |||||||||||
pmap_l2_pindex(VM_PAGE_TO_PHYS(m)) - pmap_l2_pindex(seg->start)); | pmap_l2_pindex(VM_PAGE_TO_PHYS(m)) - pmap_l2_pindex(seg->start)); | |||||||||||
} | } | |||||||||||
#define NPV_LIST_LOCKS MAXCPU | #define pa_to_pvh(pa) (&(pa_to_pmdp(pa)->pv_page)) | |||||||||||
#define page_to_pvh(m) (&(page_to_pmdp(m)->pv_page)) | ||||||||||||
#define PHYS_TO_PV_LIST_LOCK(pa) \ | #define PHYS_TO_PV_LIST_LOCK(pa) ({ \ | |||||||||||
(&pv_list_locks[pa_index(pa) % NPV_LIST_LOCKS]) | struct pmap_large_md_page *_pvd; \ | |||||||||||
struct rwlock *_lock; \ | ||||||||||||
_pvd = _pa_to_pmdp(pa); \ | ||||||||||||
if (__predict_false(_pvd == NULL)) \ | ||||||||||||
_lock = &pv_dummy_large.pv_lock; \ | ||||||||||||
else \ | ||||||||||||
_lock = &(_pvd->pv_lock); \ | ||||||||||||
_lock; \ | ||||||||||||
}) | ||||||||||||
#define CHANGE_PV_LIST_LOCK_TO_PHYS(lockp, pa) do { \ | #define CHANGE_PV_LIST_LOCK_TO_PHYS(lockp, pa) do { \ | |||||||||||
struct rwlock **_lockp = (lockp); \ | struct rwlock **_lockp = (lockp); \ | |||||||||||
struct rwlock *_new_lock; \ | struct rwlock *_new_lock; \ | |||||||||||
\ | \ | |||||||||||
_new_lock = PHYS_TO_PV_LIST_LOCK(pa); \ | _new_lock = PHYS_TO_PV_LIST_LOCK(pa); \ | |||||||||||
if (_new_lock != *_lockp) { \ | if (_new_lock != *_lockp) { \ | |||||||||||
if (*_lockp != NULL) \ | if (*_lockp != NULL) \ | |||||||||||
▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | ||||||||||||
struct pv_chunks_list { | struct pv_chunks_list { | |||||||||||
struct mtx pvc_lock; | struct mtx pvc_lock; | |||||||||||
TAILQ_HEAD(pch, pv_chunk) pvc_list; | TAILQ_HEAD(pch, pv_chunk) pvc_list; | |||||||||||
int active_reclaims; | int active_reclaims; | |||||||||||
} __aligned(CACHE_LINE_SIZE); | } __aligned(CACHE_LINE_SIZE); | |||||||||||
struct pv_chunks_list __exclusive_cache_line pv_chunks[PMAP_MEMDOM]; | struct pv_chunks_list __exclusive_cache_line pv_chunks[PMAP_MEMDOM]; | |||||||||||
static struct rwlock pv_list_locks[NPV_LIST_LOCKS]; | __exclusive_cache_line static struct pmap_large_md_page pv_dummy_large; | |||||||||||
static struct md_page *pv_table; | #define pv_dummy pv_dummy_large.pv_page | |||||||||||
static struct md_page pv_dummy; | __read_mostly static struct pmap_large_md_page *pv_table; | |||||||||||
__read_mostly vm_paddr_t pmap_last_pa; | ||||||||||||
vm_paddr_t dmap_phys_base; /* The start of the dmap region */ | vm_paddr_t dmap_phys_base; /* The start of the dmap region */ | |||||||||||
vm_paddr_t dmap_phys_max; /* The limit of the dmap region */ | vm_paddr_t dmap_phys_max; /* The limit of the dmap region */ | |||||||||||
vm_offset_t dmap_max_addr; /* The virtual address limit of the dmap */ | vm_offset_t dmap_max_addr; /* The virtual address limit of the dmap */ | |||||||||||
extern pt_entry_t pagetable_l0_ttbr1[]; | extern pt_entry_t pagetable_l0_ttbr1[]; | |||||||||||
#define PHYSMAP_SIZE (2 * (VM_PHYSSEG_MAX - 1)) | #define PHYSMAP_SIZE (2 * (VM_PHYSSEG_MAX - 1)) | |||||||||||
▲ Show 20 Lines • Show All 981 Lines • ▼ Show 20 Lines | pmap_init_asids(struct asid_set *set, int bits) | |||||||||||
set->asid_set = kmem_malloc(bitstr_size(set->asid_set_size), | set->asid_set = kmem_malloc(bitstr_size(set->asid_set_size), | |||||||||||
M_WAITOK | M_ZERO); | M_WAITOK | M_ZERO); | |||||||||||
for (i = 0; i < ASID_FIRST_AVAILABLE; i++) | for (i = 0; i < ASID_FIRST_AVAILABLE; i++) | |||||||||||
bit_set(set->asid_set, i); | bit_set(set->asid_set, i); | |||||||||||
set->asid_next = ASID_FIRST_AVAILABLE; | set->asid_next = ASID_FIRST_AVAILABLE; | |||||||||||
mtx_init(&set->asid_set_mutex, "asid set", NULL, MTX_SPIN); | mtx_init(&set->asid_set_mutex, "asid set", NULL, MTX_SPIN); | |||||||||||
} | } | |||||||||||
static void | ||||||||||||
pmap_init_pv_table(void) | ||||||||||||
{ | ||||||||||||
struct vm_phys_seg *seg, *next_seg; | ||||||||||||
struct pmap_large_md_page *pvd; | ||||||||||||
vm_size_t s; | ||||||||||||
long start, end, highest, pv_npg; | ||||||||||||
int domain, i, j, pages; | ||||||||||||
/* | /* | |||||||||||
* We strongly depend on the size being a power of two, so the assert | ||||||||||||
* is overzealous. However, should the struct be resized to a | ||||||||||||
* different power of two, the code below needs to be revisited. | ||||||||||||
*/ | ||||||||||||
CTASSERT((sizeof(*pvd) == 64)); | ||||||||||||
/* | ||||||||||||
* Calculate the size of the array. | ||||||||||||
*/ | ||||||||||||
pv_npg = 0; | ||||||||||||
for (i = 0; i < vm_phys_nsegs; i++) { | ||||||||||||
seg = &vm_phys_segs[i]; | ||||||||||||
pv_npg += pmap_l2_pindex(roundup2(seg->end, L2_SIZE)) - | ||||||||||||
pmap_l2_pindex(seg->start); | ||||||||||||
} | ||||||||||||
s = (vm_size_t)pv_npg * sizeof(struct pmap_large_md_page); | ||||||||||||
s = round_page(s); | ||||||||||||
pv_table = (struct pmap_large_md_page *)kva_alloc(s); | ||||||||||||
if (pv_table == NULL) | ||||||||||||
panic("%s: kva_alloc failed\n", __func__); | ||||||||||||
/* | ||||||||||||
* Iterate physical segments to allocate domain-local memory for PV | ||||||||||||
* list headers. | ||||||||||||
*/ | ||||||||||||
highest = -1; | ||||||||||||
s = 0; | ||||||||||||
for (i = 0; i < vm_phys_nsegs; i++) { | ||||||||||||
seg = &vm_phys_segs[i]; | ||||||||||||
start = highest + 1; | ||||||||||||
end = start + pmap_l2_pindex(roundup2(seg->end, L2_SIZE)) - | ||||||||||||
pmap_l2_pindex(seg->start); | ||||||||||||
domain = seg->domain; | ||||||||||||
if (highest >= end) | ||||||||||||
continue; | ||||||||||||
pvd = &pv_table[start]; | ||||||||||||
pages = end - start + 1; | ||||||||||||
s = round_page(pages * sizeof(*pvd)); | ||||||||||||
highest = start + (s / sizeof(*pvd)) - 1; | ||||||||||||
for (j = 0; j < s; j += PAGE_SIZE) { | ||||||||||||
vm_page_t m = vm_page_alloc_noobj_domain(domain, | ||||||||||||
VM_ALLOC_ZERO); | ||||||||||||
if (m == NULL) | ||||||||||||
panic("failed to allocate PV table page"); | ||||||||||||
pmap_qenter((vm_offset_t)pvd + j, &m, 1); | ||||||||||||
} | ||||||||||||
for (j = 0; j < s / sizeof(*pvd); j++) { | ||||||||||||
rw_init_flags(&pvd->pv_lock, "pmap pv list", RW_NEW); | ||||||||||||
TAILQ_INIT(&pvd->pv_page.pv_list); | ||||||||||||
pvd++; | ||||||||||||
} | ||||||||||||
} | ||||||||||||
pvd = &pv_dummy_large; | ||||||||||||
memset(pvd, 0, sizeof(*pvd)); | ||||||||||||
rw_init_flags(&pvd->pv_lock, "pmap pv list dummy", RW_NEW); | ||||||||||||
TAILQ_INIT(&pvd->pv_page.pv_list); | ||||||||||||
/* | ||||||||||||
* Set pointers from vm_phys_segs to pv_table. | ||||||||||||
*/ | ||||||||||||
for (i = 0, pvd = pv_table; i < vm_phys_nsegs; i++) { | ||||||||||||
seg = &vm_phys_segs[i]; | ||||||||||||
seg->md_first = pvd; | ||||||||||||
pvd += pmap_l2_pindex(roundup2(seg->end, L2_SIZE)) - | ||||||||||||
pmap_l2_pindex(seg->start); | ||||||||||||
/* | ||||||||||||
* If there is a following segment, and the final | ||||||||||||
* superpage of this segment and the initial superpage | ||||||||||||
* of the next segment are the same then adjust the | ||||||||||||
* pv_table entry for that next segment down by one so | ||||||||||||
* that the pv_table entries will be shared. | ||||||||||||
*/ | ||||||||||||
if (i + 1 < vm_phys_nsegs) { | ||||||||||||
next_seg = &vm_phys_segs[i + 1]; | ||||||||||||
if (pmap_l2_pindex(roundup2(seg->end, L2_SIZE)) - 1 == | ||||||||||||
pmap_l2_pindex(next_seg->start)) { | ||||||||||||
pvd--; | ||||||||||||
} | ||||||||||||
} | ||||||||||||
} | ||||||||||||
} | ||||||||||||
/* | ||||||||||||
* Initialize the pmap module. | * Initialize the pmap module. | |||||||||||
* Called by vm_init, to initialize any structures that the pmap | * Called by vm_init, to initialize any structures that the pmap | |||||||||||
* system needs to map virtual memory. | * system needs to map virtual memory. | |||||||||||
*/ | */ | |||||||||||
void | void | |||||||||||
pmap_init(void) | pmap_init(void) | |||||||||||
{ | { | |||||||||||
struct vm_phys_seg *seg, *next_seg; | ||||||||||||
struct md_page *pvh; | ||||||||||||
vm_size_t s; | ||||||||||||
uint64_t mmfr1; | uint64_t mmfr1; | |||||||||||
int i, pv_npg, vmid_bits; | int i, vmid_bits; | |||||||||||
/* | /* | |||||||||||
* Are large page mappings enabled? | * Are large page mappings enabled? | |||||||||||
*/ | */ | |||||||||||
TUNABLE_INT_FETCH("vm.pmap.superpages_enabled", &superpages_enabled); | TUNABLE_INT_FETCH("vm.pmap.superpages_enabled", &superpages_enabled); | |||||||||||
if (superpages_enabled) { | if (superpages_enabled) { | |||||||||||
KASSERT(MAXPAGESIZES > 1 && pagesizes[1] == 0, | KASSERT(MAXPAGESIZES > 1 && pagesizes[1] == 0, | |||||||||||
("pmap_init: can't assign to pagesizes[1]")); | ("pmap_init: can't assign to pagesizes[1]")); | |||||||||||
pagesizes[1] = L2_SIZE; | pagesizes[1] = L2_SIZE; | |||||||||||
if (L1_BLOCKS_SUPPORTED) { | if (L1_BLOCKS_SUPPORTED) { | |||||||||||
KASSERT(MAXPAGESIZES > 2 && pagesizes[2] == 0, | KASSERT(MAXPAGESIZES > 2 && pagesizes[2] == 0, | |||||||||||
("pmap_init: can't assign to pagesizes[2]")); | ("pmap_init: can't assign to pagesizes[2]")); | |||||||||||
pagesizes[2] = L1_SIZE; | pagesizes[2] = L1_SIZE; | |||||||||||
} | } | |||||||||||
} | } | |||||||||||
/* | /* | |||||||||||
* Initialize the ASID allocator. | * Initialize the ASID allocator. | |||||||||||
Done Inline Actions
markj: | ||||||||||||
*/ | */ | |||||||||||
pmap_init_asids(&asids, | pmap_init_asids(&asids, | |||||||||||
(READ_SPECIALREG(tcr_el1) & TCR_ASID_16) != 0 ? 16 : 8); | (READ_SPECIALREG(tcr_el1) & TCR_ASID_16) != 0 ? 16 : 8); | |||||||||||
if (has_hyp()) { | if (has_hyp()) { | |||||||||||
mmfr1 = READ_SPECIALREG(id_aa64mmfr1_el1); | mmfr1 = READ_SPECIALREG(id_aa64mmfr1_el1); | |||||||||||
vmid_bits = 8; | vmid_bits = 8; | |||||||||||
if (ID_AA64MMFR1_VMIDBits_VAL(mmfr1) == | if (ID_AA64MMFR1_VMIDBits_VAL(mmfr1) == | |||||||||||
ID_AA64MMFR1_VMIDBits_16) | ID_AA64MMFR1_VMIDBits_16) | |||||||||||
vmid_bits = 16; | vmid_bits = 16; | |||||||||||
pmap_init_asids(&vmids, vmid_bits); | pmap_init_asids(&vmids, vmid_bits); | |||||||||||
} | } | |||||||||||
/* | /* | |||||||||||
* Initialize pv chunk lists. | * Initialize pv chunk lists. | |||||||||||
*/ | */ | |||||||||||
for (i = 0; i < PMAP_MEMDOM; i++) { | for (i = 0; i < PMAP_MEMDOM; i++) { | |||||||||||
mtx_init(&pv_chunks[i].pvc_lock, "pmap pv chunk list", NULL, | mtx_init(&pv_chunks[i].pvc_lock, "pmap pv chunk list", NULL, | |||||||||||
MTX_DEF); | MTX_DEF); | |||||||||||
TAILQ_INIT(&pv_chunks[i].pvc_list); | TAILQ_INIT(&pv_chunks[i].pvc_list); | |||||||||||
} | } | |||||||||||
Done Inline ActionsPass VM_ALLOC_ZERO to vm_page_alloc_noobj_domain() instead of calling pmap_zero_page(). markj: Pass VM_ALLOC_ZERO to vm_page_alloc_noobj_domain() instead of calling pmap_zero_page(). | ||||||||||||
pmap_init_pv_table(); | ||||||||||||
/* | ||||||||||||
* Initialize the pool of pv list locks. | ||||||||||||
*/ | ||||||||||||
for (i = 0; i < NPV_LIST_LOCKS; i++) | ||||||||||||
rw_init(&pv_list_locks[i], "pmap pv list"); | ||||||||||||
/* | ||||||||||||
* Calculate the size of the pv head table for superpages. | ||||||||||||
*/ | ||||||||||||
pv_npg = 0; | ||||||||||||
for (i = 0; i < vm_phys_nsegs; i++) { | ||||||||||||
seg = &vm_phys_segs[i]; | ||||||||||||
pv_npg += pmap_l2_pindex(roundup2(seg->end, L2_SIZE)) - | ||||||||||||
pmap_l2_pindex(seg->start); | ||||||||||||
} | ||||||||||||
/* | ||||||||||||
* Allocate memory for the pv head table for superpages. | ||||||||||||
*/ | ||||||||||||
s = (vm_size_t)(pv_npg * sizeof(struct md_page)); | ||||||||||||
s = round_page(s); | ||||||||||||
pv_table = kmem_malloc(s, M_WAITOK | M_ZERO); | ||||||||||||
for (i = 0; i < pv_npg; i++) | ||||||||||||
TAILQ_INIT(&pv_table[i].pv_list); | ||||||||||||
TAILQ_INIT(&pv_dummy.pv_list); | ||||||||||||
/* | ||||||||||||
* Set pointers from vm_phys_segs to pv_table. | ||||||||||||
*/ | ||||||||||||
for (i = 0, pvh = pv_table; i < vm_phys_nsegs; i++) { | ||||||||||||
seg = &vm_phys_segs[i]; | ||||||||||||
seg->md_first = pvh; | ||||||||||||
pvh += pmap_l2_pindex(roundup2(seg->end, L2_SIZE)) - | ||||||||||||
pmap_l2_pindex(seg->start); | ||||||||||||
/* | ||||||||||||
* If there is a following segment, and the final | ||||||||||||
* superpage of this segment and the initial superpage | ||||||||||||
* of the next segment are the same then adjust the | ||||||||||||
* pv_table entry for that next segment down by one so | ||||||||||||
* that the pv_table entries will be shared. | ||||||||||||
*/ | ||||||||||||
if (i + 1 < vm_phys_nsegs) { | ||||||||||||
next_seg = &vm_phys_segs[i + 1]; | ||||||||||||
if (pmap_l2_pindex(roundup2(seg->end, L2_SIZE)) - 1 == | ||||||||||||
pmap_l2_pindex(next_seg->start)) { | ||||||||||||
pvh--; | ||||||||||||
} | ||||||||||||
} | ||||||||||||
} | ||||||||||||
vm_initialized = 1; | vm_initialized = 1; | |||||||||||
} | } | |||||||||||
static SYSCTL_NODE(_vm_pmap, OID_AUTO, l2, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, | static SYSCTL_NODE(_vm_pmap, OID_AUTO, l2, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, | |||||||||||
"2MB page mapping counters"); | "2MB page mapping counters"); | |||||||||||
static u_long pmap_l2_demotions; | static u_long pmap_l2_demotions; | |||||||||||
Show All 38 Lines | ||||||||||||
/* | /* | |||||||||||
* Invalidates any cached final- and optionally intermediate-level TLB entries | * Invalidates any cached final- and optionally intermediate-level TLB entries | |||||||||||
* for the specified virtual address in the given virtual address space. | * for the specified virtual address in the given virtual address space. | |||||||||||
*/ | */ | |||||||||||
static __inline void | static __inline void | |||||||||||
pmap_invalidate_page(pmap_t pmap, vm_offset_t va, bool final_only) | pmap_invalidate_page(pmap_t pmap, vm_offset_t va, bool final_only) | |||||||||||
{ | { | |||||||||||
uint64_t r; | uint64_t r; | |||||||||||
Done Inline ActionsExtra newline. markj: Extra newline. | ||||||||||||
PMAP_ASSERT_STAGE1(pmap); | PMAP_ASSERT_STAGE1(pmap); | |||||||||||
dsb(ishst); | dsb(ishst); | |||||||||||
r = TLBI_VA(va); | r = TLBI_VA(va); | |||||||||||
if (pmap == kernel_pmap) { | if (pmap == kernel_pmap) { | |||||||||||
pmap_invalidate_kernel(r, final_only); | pmap_invalidate_kernel(r, final_only); | |||||||||||
} else { | } else { | |||||||||||
▲ Show 20 Lines • Show All 6,184 Lines • Show Last 20 Lines |