rewrite sys_mmap() as sys_new_mmap()

This commit is contained in:
NAKAMURA Gou
2013-07-10 13:08:14 +09:00
parent dae884d572
commit 2aa5194958
10 changed files with 651 additions and 195 deletions

View File

@ -6,6 +6,7 @@
#include <string.h>
#include <errno.h>
#include <list.h>
#include <process.h>
static char *last_page;
extern char _head[], _end[];
@ -638,109 +639,57 @@ int ihk_mc_pt_clear_large_page(page_table_t pt, void *virt)
return __clear_pt_page(pt, virt, 1);
}
static int clear_range_l1(struct page_table *pt, uint64_t base, uint64_t start, uint64_t end)
{
int six;
int eix;
int ret;
int i;
six = (start <= base)? 0: (start - base) >> PTL1_SHIFT;
eix = ((base + PTL2_SIZE) <= end)? PT_ENTRIES
: ((end - base) + (PTL1_SIZE - 1)) >> PTL1_SHIFT;
ret = -ENOENT;
for (i = six; i < eix; ++i) {
if (!(pt->entry[i] & PFL1_PRESENT)) {
continue;
}
pt->entry[i] = 0;
ret = 0;
}
return ret;
}
static int clear_range_l2(struct page_table *pt, uint64_t base, uint64_t start, uint64_t end)
typedef int walk_pte_fn_t(void *args, pte_t *ptep, uint64_t base,
uint64_t start, uint64_t end);
static int walk_pte_l1(struct page_table *pt, uint64_t base, uint64_t start,
uint64_t end, walk_pte_fn_t *funcp, void *args)
{
int six;
int eix;
int ret;
int i;
int error;
uint64_t off;
struct page_table *q;
int error;
six = (start <= base)? 0: (start - base) >> PTL2_SHIFT;
eix = ((base + PTL3_SIZE) <= end)? PT_ENTRIES
: ((end - base) + (PTL2_SIZE - 1)) >> PTL2_SHIFT;
six = (start <= base)? 0: ((start - base) >> PTL1_SHIFT);
eix = ((end == 0) || ((base + PTL2_SIZE) <= end))? PT_ENTRIES
: (((end - base) + (PTL1_SIZE - 1)) >> PTL1_SHIFT);
ret = -ENOENT;
for (i = six; i < eix; ++i) {
if (!(pt->entry[i] & PFL2_PRESENT)) {
continue;
off = i * PTL1_SIZE;
error = (*funcp)(args, &pt->entry[i], base+off, start, end);
if (!error) {
ret = 0;
}
else if (error != -ENOENT) {
ret = error;
break;
}
}
return ret;
}
static int walk_pte_l2(struct page_table *pt, uint64_t base, uint64_t start,
uint64_t end, walk_pte_fn_t *funcp, void *args)
{
int six;
int eix;
int ret;
int i;
int error;
uint64_t off;
six = (start <= base)? 0: ((start - base) >> PTL2_SHIFT);
eix = ((end == 0) || ((base + PTL3_SIZE) <= end))? PT_ENTRIES
: (((end - base) + (PTL2_SIZE - 1)) >> PTL2_SHIFT);
ret = -ENOENT;
for (i = six; i < eix; ++i) {
off = i * PTL2_SIZE;
if (pt->entry[i] & PFL2_SIZE) {
if (((base + off) < start) || (end < (base + off + PTL2_SIZE))) {
kprintf("clear_range_l2(%p,%lx,%lx,%lx):"
"not a 2MiB page boundary\n",
pt, base, start, end);
ret = -ERANGE;
break;
}
pt->entry[i] = 0;
ret = 0;
continue;
}
q = phys_to_virt(pt->entry[i] & PT_PHYSMASK);
if ((start <= (base + off)) && ((base + off + PTL2_SIZE) <= end)) {
pt->entry[i] = 0;
ret = 0;
arch_free_page(q);
}
else {
error = clear_range_l1(q, base+off, start, end);
if (!error) {
ret = 0;
}
else if (error != -ENOENT) {
ret = error;
break;
}
}
}
return ret;
}
static int clear_range_l3(struct page_table *pt, uint64_t base, uint64_t start, uint64_t end)
{
int six;
int eix;
int ret;
int i;
int error;
struct page_table *q;
six = (start <= base)? 0: (start - base) >> PTL3_SHIFT;
eix = ((base + PTL4_SIZE) <= end)? PT_ENTRIES
: ((end - base) + (PTL3_SIZE - 1)) >> PTL3_SHIFT;
ret = -ENOENT;
for (i = six; i < eix; ++i) {
if (!(pt->entry[i] & PFL3_PRESENT)) {
continue;
}
q = phys_to_virt(pt->entry[i] & PT_PHYSMASK);
error = clear_range_l2(q, base+(i*PTL3_SIZE), start, end);
error = (*funcp)(args, &pt->entry[i], base+off, start, end);
if (!error) {
ret = 0;
}
@ -753,29 +702,24 @@ static int clear_range_l3(struct page_table *pt, uint64_t base, uint64_t start,
return ret;
}
static int clear_range_l4(struct page_table *pt, uint64_t base, uint64_t start, uint64_t end)
static int walk_pte_l3(struct page_table *pt, uint64_t base, uint64_t start,
uint64_t end, walk_pte_fn_t *funcp, void *args)
{
int six;
int eix;
int ret;
int i;
int error;
struct page_table *q;
uint64_t off;
six = (start <= base)? 0: (start - base) >> PTL4_SHIFT;
eix = ((end - base) + (PTL4_SIZE - 1)) >> PTL4_SHIFT;
if ((eix <= 0) || (PT_ENTRIES < eix)) {
eix = PT_ENTRIES;
}
six = (start <= base)? 0: ((start - base) >> PTL3_SHIFT);
eix = ((end == 0) || ((base + PTL4_SIZE) <= end))? PT_ENTRIES
: (((end - base) + (PTL3_SIZE - 1)) >> PTL3_SHIFT);
ret = -ENOENT;
for (i = six; i < eix; ++i) {
if (!(pt->entry[i] & PFL4_PRESENT)) {
continue;
}
q = phys_to_virt(pt->entry[i] & PT_PHYSMASK);
error = clear_range_l3(q, base+(i*PTL4_SIZE), start, end);
off = i * PTL3_SIZE;
error = (*funcp)(args, &pt->entry[i], base+off, start, end);
if (!error) {
ret = 0;
}
@ -788,6 +732,127 @@ static int clear_range_l4(struct page_table *pt, uint64_t base, uint64_t start,
return ret;
}
static int walk_pte_l4(struct page_table *pt, uint64_t base, uint64_t start,
uint64_t end, walk_pte_fn_t *funcp, void *args)
{
int six;
int eix;
int ret;
int i;
int error;
uint64_t off;
six = (start <= base)? 0: ((start - base) >> PTL4_SHIFT);
eix = (end == 0)? PT_ENTRIES
:(((end - base) + (PTL4_SIZE - 1)) >> PTL4_SHIFT);
ret = -ENOENT;
for (i = six; i < eix; ++i) {
off = i * PTL4_SIZE;
error = (*funcp)(args, &pt->entry[i], base+off, start, end);
if (!error) {
ret = 0;
}
else if (error != -ENOENT) {
ret = error;
break;
}
}
return ret;
}
struct clear_range_args {
int free_physical;
};
static int clear_range_l1(void *args0, pte_t *ptep, uint64_t base, uint64_t start, uint64_t end)
{
struct clear_range_args *args = args0;
uint64_t phys;
if (!(*ptep & PFL1_PRESENT)) {
return -ENOENT;
}
phys = *ptep & PT_PHYSMASK;
*ptep = 0;
if (args->free_physical) {
ihk_mc_free_pages(phys_to_virt(phys), 1);
}
return 0;
}
static int clear_range_l2(void *args0, pte_t *ptep, uint64_t base, uint64_t start, uint64_t end)
{
struct clear_range_args *args = args0;
uint64_t phys;
struct page_table *pt;
int error;
if (!(*ptep & PFL2_PRESENT)) {
return -ENOENT;
}
if (*ptep & PFL2_SIZE) {
if ((base < start) || (end < (base + PTL2_SIZE))) {
kprintf("clear_range_l2(%p,%p,%lx,%lx,%lx):"
"not a 2MiB page boundary\n",
args0, ptep, base, start, end);
return -ERANGE;
}
phys = *ptep & PT_PHYSMASK;
*ptep = 0;
if (args->free_physical) {
ihk_mc_free_pages(phys_to_virt(phys),
LARGE_PAGE_SIZE/PAGE_SIZE);
}
return 0;
}
pt = phys_to_virt(*ptep & PT_PHYSMASK);
error = walk_pte_l1(pt, base, start, end, &clear_range_l1, args0);
if (error && (error != -ENOENT)) {
return error;
}
if ((start <= base) && ((base + PTL2_SIZE) <= end)) {
*ptep = 0;
arch_free_page(pt);
}
return 0;
}
static int clear_range_l3(void *args0, pte_t *ptep, uint64_t base, uint64_t start, uint64_t end)
{
struct page_table *pt;
if (!(*ptep & PFL3_PRESENT)) {
return -ENOENT;
}
pt = phys_to_virt(*ptep & PT_PHYSMASK);
return walk_pte_l2(pt, base, start, end, &clear_range_l2, args0);
}
static int clear_range_l4(void *args0, pte_t *ptep, uint64_t base, uint64_t start, uint64_t end)
{
struct page_table *pt;
if (!(*ptep & PFL4_PRESENT)) {
return -ENOENT;
}
pt = phys_to_virt(*ptep & PT_PHYSMASK);
return walk_pte_l3(pt, base, start, end, &clear_range_l3, args0);
}
static int lookup_pte(struct page_table *pt, void *virt, pte_t **ptep, void **pgbasep, uint64_t *pgsizep)
{
int l4idx, l3idx, l2idx, l1idx;
@ -819,53 +884,91 @@ static int lookup_pte(struct page_table *pt, void *virt, pte_t **ptep, void **pg
return 0;
}
static int is_middle_of_the_page(struct page_table *pt, void *virt)
#ifdef USE_LARGE_PAGES
static int split_large_page(struct page_table *pt, intptr_t virt)
{
int error;
pte_t *pte;
pte_t *ptep;
void *pgbase;
uint64_t pgsize;
struct page_table *q;
uint64_t phys;
pte_t attr;
int i;
error = lookup_pte(pt, virt, &pte, &pgbase, &pgsize);
if (error) {
error = lookup_pte(pt, (void *)virt, &ptep, &pgbase, &pgsize);
if (error || !(*ptep & PF_PRESENT) || (pgsize == PAGE_SIZE)) {
return 0;
}
if (!(*pte & PF_PRESENT)) {
return 0;
q = __alloc_new_pt(IHK_MC_AP_NOWAIT);
if (q == NULL) {
kprintf("split_large_page:__alloc_new_pt failed\n");
return -ENOMEM;
}
return pgbase != virt;
phys = *ptep & PT_PHYSMASK;
attr = *ptep & (PFL2_PRESENT | PFL2_WRITABLE | PFL2_USER | PFL2_PWT | PFL2_PCD);
for (i = 0; i < PT_ENTRIES; ++i) {
q->entry[i] = (phys + (i * PTL1_SIZE)) | attr;
}
*ptep = (virt_to_phys(q) & PT_PHYSMASK) | PFL2_PDIR_ATTR;
return 0;
}
#endif /* USE_LARGE_PAGES */
int ihk_mc_pt_clear_range(page_table_t pt, void *start0, void *end0)
static int clear_range(page_table_t pt, void *start0, void *end0, int free_physical)
{
const uint64_t start = (uint64_t)start0;
const uint64_t end = (uint64_t)end0;
int error;
struct clear_range_args args;
if ((USER_END <= start) || (USER_END < end) || (end <= start)) {
kprintf("ihk_mc_pt_clear_range(%p,%p,%p):invalid start and/or end.\n",
pt, start0, end0);
kprintf("clear_range(%p,%p,%p,%x):invalid start and/or end.\n",
pt, start0, end0, free_physical);
return -EINVAL;
}
if (((start % LARGE_PAGE_SIZE) != 0) && is_middle_of_the_page(pt, start0)) {
kprintf("ihk_mc_pt_clear_range(%p,%p,%p):start0 is not a page boundary\n",
pt, start0, end0);
return -EINVAL;
#ifdef USE_LARGE_PAGES
if (start & (LARGE_PAGE_SIZE - 1)) {
error = split_large_page(pt, start);
if (error) {
kprintf("clear_range(%p,%p,%p,%x):split_large_page(%lx) failed. %d\n",
pt, start0, end0, free_physical, start, error);
return error;
}
}
if (((end % LARGE_PAGE_SIZE) != 0) && is_middle_of_the_page(pt, end0)) {
kprintf("ihk_mc_pt_clear_range(%p,%p,%p):end0 is not a page boundary\n",
pt, start0, end0);
return -EINVAL;
if (end & (LARGE_PAGE_SIZE - 1)) {
error = split_large_page(pt, end);
if (error) {
kprintf("clear_range(%p,%p,%p,%x):split_large_page(%lx) failed. %d\n",
pt, start0, end0, free_physical, end, error);
return error;
}
}
#endif /* USE_LARGE_PAGES */
error = clear_range_l4(pt, 0, start, end);
args.free_physical = free_physical;
error = walk_pte_l4(pt, 0, start, end, &clear_range_l4, &args);
return error;
}
int ihk_mc_pt_clear_range(page_table_t pt, void *start0, void *end0)
{
#define KEEP_PHYSICAL 0
return clear_range(pt, start0, end0, KEEP_PHYSICAL);
}
int ihk_mc_pt_free_range(page_table_t pt, void *start0, void *end0)
{
#define FREE_PHYSICAL 1
return clear_range(pt, start0, end0, FREE_PHYSICAL);
}
void load_page_table(struct page_table *pt)
{
unsigned long pt_addr;