summaryrefslogtreecommitdiffstats
path: root/sys/amd64
diff options
context:
space:
mode:
authoralc <alc@FreeBSD.org>2008-07-27 17:32:36 +0000
committeralc <alc@FreeBSD.org>2008-07-27 17:32:36 +0000
commit4455d56f31fa7e9832f76eb898382b8cf2d73aca (patch)
tree4a7f48afd1162cf545375899a65e415fbaede608 /sys/amd64
parent3f1807709d7cfe3766d846df960fb657feef0ffb (diff)
downloadFreeBSD-src-4455d56f31fa7e9832f76eb898382b8cf2d73aca.zip
FreeBSD-src-4455d56f31fa7e9832f76eb898382b8cf2d73aca.tar.gz
Enhance pmap_change_attr(). Use pmap_demote_pde() to demote a 2MB page
mapping to 4KB page mappings when the specified attribute change only applies to a portion of the 2MB page. Previously, in such cases, pmap_change_attr() gave up and returned an error. Submitted by: Magesh Dhasayyan
Diffstat (limited to 'sys/amd64')
-rw-r--r--sys/amd64/amd64/pmap.c31
1 files changed, 22 insertions, 9 deletions
diff --git a/sys/amd64/amd64/pmap.c b/sys/amd64/amd64/pmap.c
index 4f8c967..a246668 100644
--- a/sys/amd64/amd64/pmap.c
+++ b/sys/amd64/amd64/pmap.c
@@ -4373,27 +4373,40 @@ pmap_change_attr(va, size, mode)
return (EINVAL);
/*
- * XXX: We have to support tearing 2MB pages down into 4k pages if
- * needed here.
+ * Pages that aren't mapped aren't supported. Also break down 2MB pages
+ * into 4KB pages if required.
*/
- /* Pages that aren't mapped aren't supported. */
- for (tmpva = base; tmpva < (base + size); ) {
+ PMAP_LOCK(kernel_pmap);
+ for (tmpva = base; tmpva < base + size; ) {
pde = pmap_pde(kernel_pmap, tmpva);
- if (*pde == 0)
+ if (*pde == 0) {
+ PMAP_UNLOCK(kernel_pmap);
return (EINVAL);
+ }
if (*pde & PG_PS) {
- /* Handle 2MB pages that are completely contained. */
- if (size >= NBPDR) {
+ /*
+ * If the current offset aligns with a 2MB page frame
+ * and there is at least 2MB left within the range, then
+ * we need not break down this page into 4KB pages.
+ */
+ if ((tmpva & PDRMASK) == 0 &&
+ tmpva + PDRMASK < base + size) {
tmpva += NBPDR;
continue;
}
- return (EINVAL);
+ if (!pmap_demote_pde(kernel_pmap, pde, tmpva)) {
+ PMAP_UNLOCK(kernel_pmap);
+ return (ENOMEM);
+ }
}
pte = vtopte(tmpva);
- if (*pte == 0)
+ if (*pte == 0) {
+ PMAP_UNLOCK(kernel_pmap);
return (EINVAL);
+ }
tmpva += PAGE_SIZE;
}
+ PMAP_UNLOCK(kernel_pmap);
/*
* Ok, all the pages exist, so run through them updating their
OpenPOWER on IntegriCloud