/* * \brief Page allocation * \author Christian Helmuth * Bjoern Doebel * \date 2007-01-22 * * In Linux 2.6 this resides in mm/page_alloc.c. * * This implementation is far from complete as it does not cover "struct page" * emulation. In Linux, there's an array of structures for all pages. In * particular, iteration works for this array like: * * struct page *p = alloc_pages(3); // p refers to first page of allocation * ++p; // p refers to second page * * There may be more things to cover and we should have a deep look into the * kernel parts we want to reuse. Candidates for problems may be file systems, * storage (USB, IDE), and video (bttv). */ /* Linux */ #include #include #include #include #include #include "local.h" unsigned long max_low_pfn; unsigned long min_low_pfn; unsigned long max_pfn; /******************* ** Configuration ** *******************/ #define DEBUG_PAGE_ALLOC 0 /* * DDE page cache * * We need to store all pages somewhere (which in the Linux kernel is * performed by the huge VM infrastructure. Purpose for us is: * - make virt_to_phys() work * - enable external clients to hand in memory (e.g., a dm_phys * dataspace and make it accessible as Linux pages to the DDE) */ #define DDE_PAGE_CACHE_SHIFT 10 #define DDE_PAGE_CACHE_SIZE (1 << DDE_PAGE_CACHE_SHIFT) #define DDE_PAGE_CACHE_MASK (DDE_PAGE_CACHE_SIZE - 1) typedef struct { struct hlist_node list; struct page *page; } page_cache_entry; static struct hlist_head dde_page_cache[DDE_PAGE_CACHE_SIZE]; /** Hash function to map virtual addresses to page cache buckets. */ #define VIRT_TO_PAGEHASH(a) ((((unsigned long)a) >> PAGE_SHIFT) & DDE_PAGE_CACHE_MASK) void dde_page_cache_add(struct page *p) { unsigned int hashval = VIRT_TO_PAGEHASH(p->virtual); page_cache_entry *e = kmalloc(sizeof(page_cache_entry), GFP_KERNEL); #if DEBUG_PAGE_ALLOC DEBUG_MSG("virt %p, hash: %x", p->virtual, hashval); #endif e->page = p; INIT_HLIST_NODE(&e->list); hlist_add_head(&e->list, &dde_page_cache[hashval]); } void dde_page_cache_remove(struct page *p) { unsigned int hashval = VIRT_TO_PAGEHASH(p->virtual); struct hlist_node *hn = NULL; struct hlist_head *h = &dde_page_cache[hashval]; page_cache_entry *e = NULL; struct hlist_node *v = NULL; hlist_for_each_entry(e, hn, h, list) { if ((unsigned long)e->page->virtual == ((unsigned long)p->virtual & PAGE_MASK)) v = hn; break; } if (v) { #if DEBUG_PAGE_ALLOC DEBUG_MSG("deleting node %p which contained page %p", v, p); #endif hlist_del(v); } } struct page* dde_page_lookup(unsigned long va) { unsigned int hashval = VIRT_TO_PAGEHASH(va); struct hlist_node *hn = NULL; struct hlist_head *h = &dde_page_cache[hashval]; page_cache_entry *e = NULL; hlist_for_each_entry(e, hn, h, list) { if ((unsigned long)e->page->virtual == (va & PAGE_MASK)) return e->page; } return NULL; } struct page * __alloc_pages_internal(gfp_t gfp_mask, unsigned int order, struct zonelist *zonelist, nodemask_t *nm) { /* XXX: In fact, according to order, we should have one struct page * for every page, not only for the first one. */ struct page *ret = kmalloc(sizeof(*ret), GFP_KERNEL); ret->virtual = (void *)__get_free_pages(gfp_mask, order); dde_page_cache_add(ret); return ret; } unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order) { ddekit_log(DEBUG_PAGE_ALLOC, "gfp_mask=%x order=%d (%d bytes)", gfp_mask, order, PAGE_SIZE << order); Assert(gfp_mask != GFP_DMA); void *p = ddekit_large_malloc(PAGE_SIZE << order); return (unsigned long)p; } unsigned long get_zeroed_page(gfp_t gfp_mask) { unsigned long p = __get_free_pages(gfp_mask, 0); if (p) memset((void *)p, 0, PAGE_SIZE); return (unsigned long)p; } void free_hot_page(struct page *page) { WARN_UNIMPL; } /* * XXX: If alloc_pages() gets fixed to allocate a page struct per page, * this needs to be adapted, too. */ void __free_pages(struct page *page, unsigned int order) { free_pages((unsigned long)page->virtual, order); dde_page_cache_remove(page); } void __pagevec_free(struct pagevec *pvec) { WARN_UNIMPL; } int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, int len, int write, int force, struct page **pages, struct vm_area_struct **vmas) { WARN_UNIMPL; return 0; } /** * ... * * XXX order may be larger than allocation at 'addr' - it may comprise several * allocation via __get_free_pages()! */ void free_pages(unsigned long addr, unsigned int order) { ddekit_log(DEBUG_PAGE_ALLOC, "addr=%p order=%d", (void *)addr, order); ddekit_large_free((void *)addr); } unsigned long __pa(volatile void *addr) { return ddekit_pgtab_get_physaddr((void*)addr); } void *__va(unsigned long addr) { return (void*)ddekit_pgtab_get_virtaddr((ddekit_addr_t) addr); } int set_page_dirty_lock(struct page *page) { WARN_UNIMPL; return 0; } /* * basically copied from linux/mm/page_alloc.c */ void *__init alloc_large_system_hash(const char *tablename, unsigned long bucketsize, unsigned long numentries, int scale, int flags, unsigned int *_hash_shift, unsigned int *_hash_mask, unsigned long limit) { void * table = NULL; unsigned long log2qty; unsigned long size; if (numentries == 0) numentries = 1024; log2qty = ilog2(numentries); size = bucketsize << log2qty; do { unsigned long order; for (order = 0; ((1UL << order) << PAGE_SHIFT) < size; order++); table = (void*) __get_free_pages(GFP_ATOMIC, order); } while (!table && size > PAGE_SIZE && --log2qty); if (!table) panic("Failed to allocate %s hash table\n", tablename); printk("%s hash table entries: %d (order: %d, %lu bytes)\n", tablename, (1U << log2qty), ilog2(size) - PAGE_SHIFT, size); if (_hash_shift) *_hash_shift = log2qty; if (_hash_mask) *_hash_mask = (1 << log2qty) - 1; return table; } static void __init dde_page_cache_init(void) { printk("Initializing DDE page cache\n"); int i=0; for (i; i < DDE_PAGE_CACHE_SIZE; ++i) INIT_HLIST_HEAD(&dde_page_cache[i]); } core_initcall(dde_page_cache_init);