page allocation/attributes question (i386/x86_64 specific)

From: Stuart_Hayes
Date: Wed Jun 22 2005 - 12:22:30 EST



I have a question regarding page attributes on i386/x86_64 platforms:

Supopse a function requests a page with __get_free_pages(), and the page
that's returned is actually part of a large (2MB) page that has a single
pte (or pmd, I guess) to control it.

Suppose this function then calls change_page_attr() to, say, modify the
caching policy for that page. Since the page is part of a large 2MB
page (PAGE_PSE is set), change_page_attr() calls split_large_page() to
create 512 new PTEs, since the caching policy is only being changed on
the single 4K page, and not for the entire 2MB large page.

When split_large_page() creates the 512 new PTEs, it assigns the
requested attributes to the page in question, but it sets all of the
other 511 PTEs the PAGE_KERNEL attributes. It never checks what
attributes were set for the large page--it just assumes that the other
511 pages should have PAGE_KERNEL attributes.

My question is this: when split_large_page() is called, should it make
the other 511 PTEs inherit the page attributes from the large page (with
the exception of PAGE_PSE, obviously)?

There appears to be a bug (at least in certain 2.6 kernel versions)
where __get_free_pages() returns a page that's in a large page that is
executable (it doesn't have the PAGE_NX bit set)... but, after
change_page_attr() is called, the other 511 pages, which contain kernel
code, are no longer executable (because they were set to PAGE_KERNEL,
which has PAGE_NX set).

I've copied the split_large_page() code below for reference.

Thanks,
Stuart


static struct page *split_large_page(unsigned long address, pgprot_t
prot)
{
int i;
unsigned long addr;
struct page *base;
pte_t *pbase;

spin_unlock_irq(&cpa_lock);
base = alloc_pages(GFP_KERNEL, 0);
spin_lock_irq(&cpa_lock);
if (!base)
return NULL;

address = __pa(address);
addr = address & LARGE_PAGE_MASK;
pbase = (pte_t *)page_address(base);
for (i = 0; i < PTRS_PER_PTE; i++, addr += PAGE_SIZE) {
pbase[i] = pfn_pte(addr >> PAGE_SHIFT,
addr == address ? prot :
PAGE_KERNEL);
}
return base;
}
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/