OpenCores
URL https://opencores.org/ocsvn/c0or1k/c0or1k/trunk

Subversion Repositories c0or1k

[/] [c0or1k/] [trunk/] [conts/] [posix/] [mm0/] [mm/] [pagers.c] - Rev 2

Compare with Previous | Blame | View Log

/*
 * Copyright (C) 2008 Bahadir Balban
 */
#include <l4/macros.h>
#include <l4/lib/list.h>
#include L4LIB_INC_ARCH(syscalls.h)
#include L4LIB_INC_ARCH(syslib.h)
#include <malloc/malloc.h>
#include <mm/alloc_page.h>
#include <vm_area.h>
#include <string.h>
#include <globals.h>
#include <file.h>
#include <init.h>
#include <l4/api/errno.h>
#include <fs.h>
 
struct page *page_init(struct page *page)
{
	/* Reset page */
	memset(page, 0, sizeof(*page));
	page->refcnt = -1;
	spin_lock_init(&page->lock);
	link_init(&page->list);
 
	return page;
}
 
struct page *find_page(struct vm_object *obj, unsigned long pfn)
{
	struct page *p;
 
	list_foreach_struct(p, &obj->page_cache, list)
		if (p->offset == pfn)
			return p;
 
	return 0;
}
 
/*
 * Deletes all pages in a page cache, assumes pages are from the
 * page allocator, and page structs are from the page_array, which
 * is the default situation.
 */
int default_release_pages(struct vm_object *vm_obj)
{
	struct page *p, *n;
 
	list_foreach_removable_struct(p, n, &vm_obj->page_cache, list) {
		list_remove_init(&p->list);
		BUG_ON(p->refcnt);
 
		/* Reinitialise the page */
		page_init(p);
 
		/* Return page back to allocator */
		free_page((void *)page_to_phys(p));
 
		/* Reduce object page count */
		BUG_ON(--vm_obj->npages < 0);
	}
	return 0;
}
 
int file_page_out(struct vm_object *vm_obj, unsigned long page_offset)
{
	struct vm_file *f = vm_object_to_file(vm_obj);
	struct page *page;
	void *paddr;
	int err;
 
	/* Check first if the file has such a page at all */
	if (__pfn(page_align_up(f->length) <= page_offset)) {
		printf("%s: %s: Trying to look up page %lu, but file length "
		       "is %lu bytes.\n", __TASKNAME__, __FUNCTION__,
		       page_offset, f->length);
		BUG();
	}
 
	/* If the page is not in the page cache, simply return. */
	if (!(page = find_page(vm_obj, page_offset)))
		return 0;
 
	/* If the page is not dirty, simply return */
	if (!(page->flags & VM_DIRTY))
		return 0;
 
	paddr = (void *)page_to_phys(page);
 
	//printf("%s/%s: Writing to vnode %lu, at pgoff 0x%lu, %d pages, buf at %p\n",
	//	__TASKNAME__, __FUNCTION__, f->vnode->vnum, page_offset, 1, vaddr);
 
	/* Syscall to vfs to write page back to file. */
	if ((err = vfs_write(f->vnode, page_offset, 1,
			     phys_to_virt(paddr))) < 0)
		return err;
 
	/* Clear dirty flag */
	page->flags &= ~VM_DIRTY;
 
	return 0;
}
 
struct page *file_page_in(struct vm_object *vm_obj, unsigned long page_offset)
{
	struct vm_file *f = vm_object_to_file(vm_obj);
	struct page *page;
	void *paddr;
	int err;
 
	/* Check first if the file has such a page at all */
	if (__pfn(page_align_up(f->length) <= page_offset)) {
		printf("%s: %s: Trying to look up page %lu, but file length "
		       "is %lu bytes.\n", __TASKNAME__, __FUNCTION__,
		       page_offset, f->length);
		BUG();
	}
 
	/* Call vfs only if the page is not resident in page cache. */
	if (!(page = find_page(vm_obj, page_offset))) {
		/* Allocate a new page */
		paddr = alloc_page(1);
		page = phys_to_page(paddr);
 
		/* Call to vfs to read into the page. */
		if ((err = vfs_read(f->vnode, page_offset,
				    1, phys_to_virt(paddr))) < 0) {
 
			free_page(paddr);
			return PTR_ERR(err);
		}
 
	//	printf("%s/%s: Reading into vnode %lu, at pgoff 0x%lu, %d pages, buf at %p\n",
	//	       __TASKNAME__, __FUNCTION__, f->vnode->vnum, page_offset, 1, vaddr);
 
		/* Update vm object details */
		vm_obj->npages++;
 
		/* Update page details */
		page_init(page);
		page->refcnt++;
		page->owner = vm_obj;
		page->offset = page_offset;
		page->virtual = 0;
 
		/* Add the page to owner's list of in-memory pages */
		BUG_ON(!list_empty(&page->list));
		insert_page_olist(page, vm_obj);
	}
 
	return page;
}
 
/*
 * All non-mmapable char devices are handled by this.
 * VFS calls those devices to read their pages
 */
struct vm_pager file_pager = {
	.ops = {
		.page_in = file_page_in,
		.page_out = file_page_out,
		.release_pages = default_release_pages,
	},
};
 
 
/* A proposal for shadow vma container, could be part of vm_file->priv_data */
struct vm_swap_node {
	struct vm_file *swap_file;
	struct task_ids task_ids;
	struct address_pool *pool;
};
 
/*
 * This should save swap_node/page information either in the pte or in a global
 * list of swap descriptors, and then write the page into the possibly one and
 * only swap file.
 */
struct page *swap_page_in(struct vm_object *vm_obj, unsigned long file_offset)
{
	struct page *p;
 
	/* No swapping yet, so the page is either here or not here. */
	if (!(p = find_page(vm_obj, file_offset)))
		return PTR_ERR(-EINVAL);
	else
		return p;
}
 
struct vm_pager swap_pager = {
	.ops = {
		.page_in = swap_page_in,
		.release_pages = default_release_pages,
	},
};
 
/*
 * Just releases the page structures since the actual pages are
 * already in memory as read-only.
 */
int bootfile_release_pages(struct vm_object *vm_obj)
{
	struct page *p, *n;
 
	list_foreach_removable_struct(p, n, &vm_obj->page_cache, list) {
		list_remove(&p->list);
		BUG_ON(p->refcnt);
 
		/* Reinitialise the page */
		page_init(p);
 
		/*
		 * We don't free the page because it doesn't
		 * come from the page allocator
		 */
		// free_page((void *)page_to_phys(p));
 
 
		/* Reduce object page count */
		BUG_ON(--vm_obj->npages < 0);
	}
	return 0;
}
 
#if 0
/* Returns the page with given offset in this vm_object */
struct page *bootfile_page_in(struct vm_object *vm_obj,
			      unsigned long offset)
{
	struct vm_file *boot_file = vm_object_to_file(vm_obj);
	struct svc_image *img = boot_file->priv_data;
	struct page *page;
 
	/* Check first if the file has such a page at all */
	if (__pfn(page_align_up(boot_file->length) <= offset)) {
		printf("%s: %s: Trying to look up page %lu, but file length "
		       "is %lu bytes.\n", __TASKNAME__, __FUNCTION__,
		       offset, boot_file->length);
		BUG();
	}
 
	/* The page is not resident in page cache. */
	if (!(page = find_page(vm_obj, offset))) {
		page = phys_to_page(img->phys_start + __pfn_to_addr(offset));
 
		/* Update page */
		page_init(page);
		page->refcnt++;
		page->owner = vm_obj;
		page->offset = offset;
 
		/* Update object */
		vm_obj->npages++;
 
		/* Add the page to owner's list of in-memory pages */
		BUG_ON(!list_empty(&page->list));
		insert_page_olist(page, vm_obj);
	}
 
	return page;
}
 
struct vm_pager bootfile_pager = {
	.ops = {
		.page_in = bootfile_page_in,
		.release_pages = bootfile_release_pages,
	},
};
 
void bootfile_destroy_priv_data(struct vm_file *bootfile)
{
 
}
 
/* From bare boot images, create mappable device files */
int init_boot_files(struct initdata *initdata)
{
	struct bootdesc *bd = initdata->bootdesc;
	struct vm_file *boot_file;
	struct svc_image *img;
 
	link_init(&initdata->boot_file_list);
 
	for (int i = 0; i < bd->total_images; i++) {
		img = &bd->images[i];
		boot_file = vm_file_create();
 
		/* Allocate private data */
		boot_file->priv_data = kzalloc(sizeof(*img));
		memcpy(boot_file->priv_data, img, sizeof(*img));
 
		boot_file->length = img->phys_end - img->phys_start;
		boot_file->type = VM_FILE_BOOTFILE;
		boot_file->destroy_priv_data =
			bootfile_destroy_priv_data;
 
		/* Initialise the vm object */
		boot_file->vm_obj.flags = VM_OBJ_FILE;
		boot_file->vm_obj.pager = &bootfile_pager;
 
		/* Add the file to initdata's bootfile list */
		list_insert_tail(&boot_file->list, &initdata->boot_file_list);
	}
 
	return 0;
}
#endif
 
/*
 * FIXME:
 * Problem is that devzero is a character device and we don't have a
 * character device subsystem yet.
 *
 * Therefore even though the vm_file for devzero requires a vnode,
 * currently it has no vnode field, and the information (the zero page)
 * that needs to be stored in the dynamic vnode is now stored in the
 * field file_private_data in the vm_file, which really needs to be
 * removed.
 */
 
/* Returns the page with given offset in this vm_object */
struct page *devzero_page_in(struct vm_object *vm_obj,
			     unsigned long page_offset)
{
	struct vm_file *devzero = vm_object_to_file(vm_obj);
	struct page *zpage = devzero->private_file_data;
 
	BUG_ON(!(devzero->type & VM_FILE_DEVZERO));
 
	/* Update zero page struct. */
	spin_lock(&zpage->lock);
	BUG_ON(zpage->refcnt < 0);
	zpage->refcnt++;
	spin_unlock(&zpage->lock);
 
	return zpage;
}
 
struct vm_pager devzero_pager = {
	.ops = {
		.page_in = devzero_page_in,
	},
};
 
struct vm_file *get_devzero(void)
{
	struct vm_file *f;
 
	list_foreach_struct(f, &global_vm_files.list, list)
		if (f->type == VM_FILE_DEVZERO)
			return f;
	return 0;
}
 
int init_devzero(void)
{
	void *zphys, *zvirt;
	struct page *zpage;
	struct vm_file *devzero;
 
	/* Allocate and initialise the zero page */
	zphys = alloc_page(1);
	zpage = phys_to_page(zphys);
	zvirt = (void *)phys_to_virt(zphys);
	memset(zvirt, 0, PAGE_SIZE);
 
	/*
	 * FIXME:
	 * Flush the dcache if virtual data cache
	 */
 
	/* Allocate and initialise devzero file */
	devzero = vm_file_create();
	devzero->type = VM_FILE_DEVZERO;
	devzero->private_file_data = zpage;
	devzero->length = page_align(~0UL); /* So we dont wraparound to 0! */
	devzero->vm_obj.npages = __pfn(devzero->length);
	devzero->vm_obj.pager = &devzero_pager;
	devzero->vm_obj.flags = VM_OBJ_FILE;
 
	/* Initialise zpage */
	zpage->refcnt++;
	zpage->owner = &devzero->vm_obj;
 
	global_add_vm_file(devzero);
	return 0;
}
 
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.