--- linux/fs/proc/proc_misc.c.orig Sun Feb 24 15:36:55 2002 +++ linux/fs/proc/proc_misc.c Wed Feb 27 09:09:02 2002 @@ -179,7 +179,8 @@ "LowTotal: %8lu kB\n" "LowFree: %8lu kB\n" "SwapTotal: %8lu kB\n" - "SwapFree: %8lu kB\n", + "SwapFree: %8lu kB\n" + "BigPagesFree: %8lu kB\n", K(i.totalram), K(i.freeram), K(i.sharedram), @@ -195,7 +196,8 @@ K(i.totalram-i.totalhigh), K(i.freeram-i.freehigh), K(i.totalswap), - K(i.freeswap)); + K(i.freeswap), + nr_bigpages << (PMD_SHIFT-10)); return proc_calc_metrics(page, start, off, count, eof, len); #undef B --- linux/fs/proc/array.c.orig Sun Feb 24 19:40:00 2002 +++ linux/fs/proc/array.c Tue Mar 5 23:02:48 2002 @@ -397,6 +397,13 @@ if (pmd_none(*pmd)) return; + if (pmd_bigpage(*pmd)) { + *total += BIGPAGE_PAGES; + *pages += BIGPAGE_PAGES; + *shared += BIGPAGE_PAGES; + *dirty += BIGPAGE_PAGES; + return; + } if (pmd_bad(*pmd)) { pmd_ERROR(*pmd); pmd_clear(pmd); @@ -557,6 +564,12 @@ str[1] = flags & VM_WRITE ? 'w' : '-'; str[2] = flags & VM_EXEC ? 'x' : '-'; str[3] = flags & VM_MAYSHARE ? 's' : 'p'; + if (flags & VM_BIGPAGE) + str[3] = 'B'; + else + if (map->vm_ops && (map->vm_ops->nopage == shmem_nopage) && + I_BIGPAGE(map->vm_file->f_dentry->d_inode)) + str[3] = 'b'; str[4] = 0; dev = 0; --- linux/fs/binfmt_elf.c.orig Sun Feb 24 19:39:14 2002 +++ linux/fs/binfmt_elf.c Wed Feb 27 09:03:34 2002 @@ -1212,12 +1212,12 @@ if (pgd_none(*pgd)) goto nextpage_coredump; pmd = pmd_offset(pgd, addr); - if (pmd_none(*pmd)) + if (pmd_none(*pmd) || pmd_bigpage(*pmd)) goto nextpage_coredump; pte = pte_offset_map(pmd, addr); none = pte_none(*pte); pte_unmap(pte); - if (pte_none(*pte)) { + if (none) { nextpage_coredump: DUMP_SEEK (file->f_pos + PAGE_SIZE); } else { --- linux/kernel/ptrace.c.orig Sun Feb 24 19:41:59 2002 +++ linux/kernel/ptrace.c Sun Feb 24 20:26:07 2002 @@ -105,15 +105,24 @@ pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) goto fault_in_page; - if (pmd_bad(*pgmiddle)) - goto bad_pmd; - pgtable = pte_offset_map(pgmiddle, addr); - if (!pte_present(*pgtable)) - goto fault_in_page_unmap; - if (write && (!pte_write(*pgtable) || !pte_dirty(*pgtable))) - goto fault_in_page_unmap; - page = pte_page(*pgtable); - pte_unmap(pgtable); + if (pmd_bigpage(*pgmiddle)) { + unsigned long idx = (addr & BIGPAGE_MASK) / PAGE_SIZE; + + page = pmd_page(*pgmiddle); + if (!BigPage(page)) + BUG(); + page += idx; + } else { + if (pmd_bad(*pgmiddle)) + goto bad_pmd; + pgtable = pte_offset_map(pgmiddle, addr); + if (!pte_present(*pgtable)) + goto fault_in_page_unmap; + if (write && (!pte_write(*pgtable) || !pte_dirty(*pgtable))) + goto fault_in_page_unmap; + page = pte_page(*pgtable); + pte_unmap(pgtable); + } /* ZERO_PAGE is special: reads from it are ok even though it's marked reserved */ if (page != ZERO_PAGE(addr) || write) { --- linux/kernel/fork.c.orig Sun Feb 24 21:16:40 2002 +++ linux/kernel/fork.c Wed Mar 6 20:49:40 2002 @@ -176,7 +176,8 @@ if (!tmp) goto fail_nomem; *tmp = *mpnt; - tmp->vm_flags &= ~VM_LOCKED; + if (!(tmp->vm_flags & VM_BIGPAGE)) + tmp->vm_flags &= ~VM_LOCKED; tmp->vm_mm = mm; tmp->vm_next = NULL; file = tmp->vm_file; --- linux/kernel/sysctl.c.orig Tue Feb 26 09:33:03 2002 +++ linux/kernel/sysctl.c Tue Feb 26 10:08:18 2002 @@ -222,6 +222,8 @@ 0444, NULL, &proc_dointvec}, {KERN_RTSIGMAX, "rtsig-max", &max_queued_signals, sizeof(int), 0644, NULL, &proc_dointvec}, + {KERN_SHMUSEBIGPAGES, "shm-use-bigpages", &shm_use_bigpages, sizeof(int), + 0644, NULL, &proc_dointvec}, #ifdef CONFIG_SYSVIPC {KERN_SHMMAX, "shmmax", &shm_ctlmax, sizeof (size_t), 0644, NULL, &proc_doulongvec_minmax}, --- linux/mm/bigpages.c.orig Tue Mar 5 17:50:34 2002 +++ linux/mm/bigpages.c Sat Mar 2 11:54:40 2002 @@ -0,0 +1,96 @@ +/* + * linux/mm/bigpages.c + * + * Copyright (C) 2002 Ingo Molnar + */ + +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/spinlock.h> +#include <linux/highmem.h> +#include <linux/smp_lock.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <asm/uaccess.h> +#include <asm/pgalloc.h> + +static spinlock_t bigpages_lock = SPIN_LOCK_UNLOCKED; +long nr_bigpages; +static LIST_HEAD(bigpages_list); + +#define ORDER_BIGPAGE (PMD_SHIFT - PAGE_SHIFT) + +struct page *alloc_bigpage(void) +{ + list_t *head = &bigpages_list; + struct page *page = NULL; + + spin_lock(&bigpages_lock); + if (nr_bigpages) { + page = list_entry(head->next, struct page, list); + list_del_init(head->next); + nr_bigpages--; + } + spin_unlock(&bigpages_lock); + + return page; +} + +void free_bigpage(struct page *page) +{ + struct page *p; + int i; + + if ((page - mem_map) % BIGPAGE_PAGES) + BUG(); + for (i = 0 ; i < (1 << ORDER_BIGPAGE); i++) { + p = page + i; + set_page_count(p, 2); + set_bit(PG_bigpage, &p->flags); + clear_highpage(p); + } + spin_lock(&bigpages_lock); + nr_bigpages++; + list_add(&page->list, &bigpages_list); + spin_unlock(&bigpages_lock); +} + +static int grow_bigpages_pool(int pages) +{ + struct page *page; + int allocated = 0; + + while (pages) { + page = alloc_pages(__GFP_HIGHMEM, ORDER_BIGPAGE); + if (!page) + break; + free_bigpage(page); + pages--; + allocated++; + } + printk("bigpage subsystem: allocated %ld bigpages (=%ldMB).\n", + nr_bigpages, nr_bigpages << (BIGPAGE_SHIFT - 20)); + return allocated; +} + +static __initdata int boot_bigpages; + +static __init int reserve_bigpages(char *str) +{ + unsigned long pages = memparse(str, &str) >> PAGE_SHIFT; + + pages >>= ORDER_BIGPAGE; + boot_bigpages = pages; + + return 0; +} + +static __init int init_bigpage_pool(void) +{ + grow_bigpages_pool(boot_bigpages); + return 0; +} + +__setup("bigpages=", reserve_bigpages); +__initcall(init_bigpage_pool); + --- linux/mm/page_alloc.c.orig Sun Feb 24 11:49:51 2002 +++ linux/mm/page_alloc.c Wed Mar 6 20:52:15 2002 @@ -72,13 +72,8 @@ per_cpu_t *per_cpu; zone_t *zone; - /* - * This late check is safe because reserved pages do not - * have a valid page->count. This trick avoids overhead - * in __free_pages(). - */ - if (PageReserved(page)) - return; + if (BigPage(page)) + BUG(); if (page->buffers) BUG(); if (page->mapping) @@ -209,6 +204,8 @@ __restore_flags(flags); set_page_count(page, 1); + if (BigPage(page)) + BUG(); return page; } @@ -236,6 +233,8 @@ if (BAD_RANGE(zone,page)) BUG(); DEBUG_ADD_PAGE + if (BigPage(page)) + BUG(); return page; } curr_order++; @@ -594,6 +595,8 @@ void __free_pages(struct page *page, unsigned long order) { + if (PageReserved(page)) + return; if (put_page_testzero(page)) __free_pages_ok(page, order); } --- linux/mm/shmem.c.orig Sun Feb 24 13:08:00 2002 +++ linux/mm/shmem.c Wed Mar 6 20:55:39 2002 @@ -5,6 +5,7 @@ * 2000 Transmeta Corp. * 2000-2001 Christoph Rohland * 2000-2001 SAP AG + * 2002 Ingo Molnar, Red Hat Inc. * * This file is released under the GPL. */ @@ -21,6 +22,7 @@ #include <linux/devfs_fs_kernel.h> #include <linux/fs.h> #include <linux/mm.h> +#include <linux/slab.h> #include <linux/file.h> #include <linux/swap.h> #include <linux/pagemap.h> @@ -49,6 +51,8 @@ static spinlock_t shmem_ilock = SPIN_LOCK_UNLOCKED; atomic_t shmem_nrpages = ATOMIC_INIT(0); +int shm_use_bigpages; + #define BLOCKS_PER_PAGE (PAGE_CACHE_SIZE/512) static void shmem_removepage(struct page *page) @@ -76,19 +80,40 @@ * It has to be called with the spinlock held. */ +static int shm_alloc_space(struct inode * inode, unsigned long space) +{ + struct shmem_sb_info * sbinfo = SHMEM_SB(inode->i_sb); + + spin_lock(&sbinfo->stat_lock); + if (sbinfo->free_blocks < space) { + spin_unlock(&sbinfo->stat_lock); + return -ENOSPC; + } + sbinfo->free_blocks -= space; + inode->i_blocks += space*BLOCKS_PER_PAGE; + spin_unlock(&sbinfo->stat_lock); + + return 0; +} + +static void shm_free_space(struct inode * inode, unsigned long freed) +{ + struct shmem_sb_info * sbinfo = SHMEM_SB(inode->i_sb); + + spin_lock(&sbinfo->stat_lock); + sbinfo->free_blocks += freed; + inode->i_blocks -= freed*BLOCKS_PER_PAGE; + spin_unlock(&sbinfo->stat_lock); +} + static void shmem_recalc_inode(struct inode * inode) { unsigned long freed; freed = (inode->i_blocks/BLOCKS_PER_PAGE) - (inode->i_mapping->nrpages + SHMEM_I(inode)->swapped); - if (freed){ - struct shmem_sb_info * sbinfo = SHMEM_SB(inode->i_sb); - inode->i_blocks -= freed*BLOCKS_PER_PAGE; - spin_lock (&sbinfo->stat_lock); - sbinfo->free_blocks += freed; - spin_unlock (&sbinfo->stat_lock); - } + if (freed) + shm_free_space(inode, freed); } /* @@ -134,7 +159,7 @@ #define SHMEM_MAX_BLOCKS (SHMEM_NR_DIRECT + ENTRIES_PER_PAGE * ENTRIES_PER_PAGE/2*(ENTRIES_PER_PAGE+1)) -static swp_entry_t * shmem_swp_entry (struct shmem_inode_info *info, unsigned long index, unsigned long page) +static swp_entry_t * shmem_swp_entry (shmem_info_t *info, unsigned long index, unsigned long page) { unsigned long offset; void **dir; @@ -181,7 +206,7 @@ * @info: info structure for the inode * @index: index of the page to find */ -static inline swp_entry_t * shmem_alloc_entry (struct shmem_inode_info *info, unsigned long index) +static inline swp_entry_t * shmem_alloc_entry (shmem_info_t *info, unsigned long index) { unsigned long page = 0; swp_entry_t * res; @@ -290,7 +315,7 @@ * then shmem_truncate_direct to do the real work */ static inline unsigned long -shmem_truncate_indirect(struct shmem_inode_info *info, unsigned long index) +shmem_truncate_indirect(shmem_info_t *info, unsigned long index) { swp_entry_t ***base; unsigned long baseidx, len, start; @@ -328,40 +353,61 @@ return shmem_truncate_direct(base, start, len); } -static void shmem_truncate (struct inode * inode) +static void shmem_truncate(struct inode *inode) { - unsigned long index; - unsigned long freed = 0; - struct shmem_inode_info * info = SHMEM_I(inode); + shmem_info_t * info = SHMEM_I(inode); + unsigned long freed = 0, index, i; + struct page *page; down(&info->sem); inode->i_ctime = inode->i_mtime = CURRENT_TIME; - spin_lock (&info->lock); - index = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + spin_lock(&info->lock); + if (!info->bigpages) { + index = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - while (index < info->next_index) - freed += shmem_truncate_indirect(info, index); + while (index < info->next_index) + freed += shmem_truncate_indirect(info, index); - info->swapped -= freed; - shmem_recalc_inode(inode); - spin_unlock (&info->lock); + info->swapped -= freed; + shmem_recalc_inode(inode); + } else { + index = (inode->i_size + BIGPAGE_SIZE - 1) >> BIGPAGE_SHIFT; + + for (i = index; i < info->max_bigpages; i++) { + page = info->bigpages[i]; + if (page) { + info->bigpages[i] = NULL; + free_bigpage(page); + shm_free_space(inode, BIGPAGE_PAGES); + } + } + } + spin_unlock(&info->lock); up(&info->sem); } static void shmem_delete_inode(struct inode * inode) { struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); + shmem_info_t *info = SHMEM_I(inode); inode->i_size = 0; - if (inode->i_op->truncate == shmem_truncate){ - spin_lock (&shmem_ilock); - list_del (&SHMEM_I(inode)->list); - spin_unlock (&shmem_ilock); - shmem_truncate (inode); + if (inode->i_op->truncate == shmem_truncate) { + spin_lock(&shmem_ilock); + list_del(&info->list); + spin_unlock(&shmem_ilock); + shmem_truncate(inode); + if (info->bigpages) { + kfree(info->bigpages); + info->bigpages = NULL; + info->max_bigpages = 0; + } } - spin_lock (&sbinfo->stat_lock); + if (info->bigpages) + BUG(); + spin_lock(&sbinfo->stat_lock); sbinfo->free_inodes++; - spin_unlock (&sbinfo->stat_lock); + spin_unlock(&sbinfo->stat_lock); clear_inode(inode); } @@ -378,14 +424,14 @@ return -1; } -static int shmem_unuse_inode (struct shmem_inode_info *info, swp_entry_t entry, struct page *page) +static int shmem_unuse_inode (shmem_info_t *info, swp_entry_t entry, struct page *page) { swp_entry_t *ptr; unsigned long idx; int offset; idx = 0; - spin_lock (&info->lock); + spin_lock(&info->lock); offset = shmem_clear_swp (entry, info->i_direct, SHMEM_NR_DIRECT); if (offset >= 0) goto found; @@ -399,7 +445,7 @@ if (offset >= 0) goto found; } - spin_unlock (&info->lock); + spin_unlock(&info->lock); return 0; found: add_to_page_cache(page, info->inode->i_mapping, offset + idx); @@ -418,16 +464,18 @@ void shmem_unuse(swp_entry_t entry, struct page *page) { struct list_head *p; - struct shmem_inode_info * info; + shmem_info_t * info; - spin_lock (&shmem_ilock); + if (BigPage(page)) + BUG(); + spin_lock(&shmem_ilock); list_for_each(p, &shmem_inodes) { - info = list_entry(p, struct shmem_inode_info, list); + info = list_entry(p, shmem_info_t, list); if (shmem_unuse_inode(info, entry, page)) break; } - spin_unlock (&shmem_ilock); + spin_unlock(&shmem_ilock); } /* @@ -440,12 +488,14 @@ static int shmem_writepage(struct page * page) { int error; - struct shmem_inode_info *info; + shmem_info_t *info; swp_entry_t *entry, swap; struct inode *inode; if (!PageLocked(page)) BUG(); + if (BigPage(page)) + BUG(); inode = page->mapping->host; info = SHMEM_I(inode); @@ -494,13 +544,14 @@ * still need to guard against racing with shm_writepage(), which might * be trying to move the page to the swap cache as we run. */ -static struct page * shmem_getpage_locked(struct shmem_inode_info *info, struct inode * inode, unsigned long idx) +static struct page * shmem_getpage_locked(shmem_info_t *info, struct inode * inode, unsigned long idx) { struct address_space * mapping = inode->i_mapping; - struct shmem_sb_info *sbinfo; struct page * page; swp_entry_t *entry; + if (info->bigpages) + BUG(); repeat: page = find_lock_page(mapping, idx); if (page) @@ -510,7 +561,7 @@ if (IS_ERR(entry)) return (void *)entry; - spin_lock (&info->lock); + spin_lock(&info->lock); /* The shmem_alloc_entry() call may have blocked, and * shmem_writepage may have been moving a page between the page @@ -521,7 +572,7 @@ if (page) { if (TryLockPage(page)) goto wait_retry; - spin_unlock (&info->lock); + spin_unlock(&info->lock); return page; } @@ -533,7 +584,7 @@ page = __find_get_page(&swapper_space, entry->val, page_hash(&swapper_space, entry->val)); if (!page) { - spin_unlock (&info->lock); + spin_unlock(&info->lock); lock_kernel(); swapin_readahead(*entry); page = read_swap_cache_async(*entry); @@ -563,15 +614,12 @@ page->flags = flags | (1 << PG_dirty); add_to_page_cache_locked(page, mapping, idx); info->swapped--; - spin_unlock (&info->lock); + spin_unlock(&info->lock); } else { - sbinfo = SHMEM_SB(inode->i_sb); - spin_unlock (&info->lock); - spin_lock (&sbinfo->stat_lock); - if (sbinfo->free_blocks == 0) - goto no_space; - sbinfo->free_blocks--; - spin_unlock (&sbinfo->stat_lock); + spin_unlock(&info->lock); + + if (shm_alloc_space(inode, 1)) + return ERR_PTR(-ENOSPC); /* Ok, get a new page. We don't have to worry about the * info->lock spinlock here: we cannot race against @@ -581,10 +629,11 @@ * new shm entry. The inode semaphore we already hold * is enough to make this atomic. */ page = page_cache_alloc(mapping); - if (!page) + if (!page) { + shm_free_space(inode, 1); return ERR_PTR(-ENOMEM); + } clear_highpage(page); - inode->i_blocks += BLOCKS_PER_PAGE; add_to_page_cache (page, mapping, idx); } @@ -594,23 +643,63 @@ if (info->locked) page_cache_get(page); return page; -no_space: - spin_unlock (&sbinfo->stat_lock); - return ERR_PTR(-ENOSPC); wait_retry: - spin_unlock (&info->lock); + spin_unlock(&info->lock); wait_on_page(page); page_cache_release(page); goto repeat; } +static struct page * shmem_getbigpage_locked(shmem_info_t *info, struct inode * inode, unsigned long idx) +{ + unsigned long bigidx, offset; + struct page *page; + + bigidx = idx / BIGPAGE_PAGES; + offset = idx % BIGPAGE_PAGES; + + if (bigidx >= info->max_bigpages) + return ERR_PTR(-ENOSPC); +got_bigpage: + page = info->bigpages[bigidx]; + if (page) { + page += offset; + get_page(page); + if (!BigPage(page)) + BUG(); + lock_page(page); + return page; + } + + if (shm_alloc_space(inode, BIGPAGE_PAGES)) + return ERR_PTR(-ENOSPC); + + page = alloc_bigpage(); + if (!page) { + shm_free_space(inode, BIGPAGE_PAGES); + return ERR_PTR(-ENOSPC); + } + + spin_lock(&info->lock); + if (info->bigpages[bigidx]) { + spin_unlock(&info->lock); + free_bigpage(page); + shm_free_space(inode, BIGPAGE_PAGES); + goto got_bigpage; + } + info->bigpages[bigidx] = page; + spin_unlock(&info->lock); + + goto got_bigpage; +} + static int shmem_getpage(struct inode * inode, unsigned long idx, struct page **ptr) { - struct shmem_inode_info *info = SHMEM_I(inode); + shmem_info_t *info = SHMEM_I(inode); int error; - down (&info->sem); + down(&info->sem); *ptr = ERR_PTR(-EFAULT); if (inode->i_size <= (loff_t) idx * PAGE_CACHE_SIZE) goto failed; @@ -620,10 +709,36 @@ goto failed; UnlockPage(*ptr); - up (&info->sem); + up(&info->sem); + return 0; +failed: + up(&info->sem); + error = PTR_ERR(*ptr); + *ptr = NOPAGE_SIGBUS; + if (error == -ENOMEM) + *ptr = NOPAGE_OOM; + return error; +} + +static int shmem_getbigpage(struct inode * inode, unsigned long idx, struct page **ptr) +{ + shmem_info_t *info = SHMEM_I(inode); + int error; + + down(&info->sem); + *ptr = ERR_PTR(-EFAULT); + if (inode->i_size <= (loff_t) idx * PAGE_SIZE) + goto failed; + + *ptr = shmem_getbigpage_locked(info, inode, idx); + if (IS_ERR (*ptr)) + goto failed; + + UnlockPage(*ptr); + up(&info->sem); return 0; failed: - up (&info->sem); + up(&info->sem); error = PTR_ERR(*ptr); *ptr = NOPAGE_SIGBUS; if (error == -ENOMEM) @@ -633,10 +748,31 @@ struct page * shmem_nopage(struct vm_area_struct * vma, unsigned long address, int no_share) { - struct page * page; - unsigned int idx; struct inode * inode = vma->vm_file->f_dentry->d_inode; + int bigpage = vma->vm_flags & VM_BIGPAGE; + unsigned long idx, bigidx; + struct page * page = NULL; + + if (I_BIGPAGE(inode)) { + if (no_share) + BUG(); + idx = (address - vma->vm_start) >> PAGE_SHIFT; + idx += vma->vm_pgoff; + + if (shmem_getbigpage(inode, idx, &page)) + return page; + + if (bigpage) { + put_page(page); + bigidx = idx / BIGPAGE_PAGES; + if (bigidx >= SHMEM_I(inode)->max_bigpages) + BUG(); + page = SHMEM_I(inode)->bigpages[bigidx]; + get_page(page); + } + return page; + } idx = (address - vma->vm_start) >> PAGE_CACHE_SHIFT; idx += vma->vm_pgoff; @@ -662,7 +798,7 @@ void shmem_lock(struct file * file, int lock) { struct inode * inode = file->f_dentry->d_inode; - struct shmem_inode_info * info = SHMEM_I(inode); + shmem_info_t * info = SHMEM_I(inode); struct page * page; unsigned long idx, size; @@ -670,6 +806,8 @@ if (info->locked == lock) goto out; info->locked = lock; + if (SHMEM_I(inode)->bigpages) + goto out; size = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; for (idx = 0; idx < size; idx++) { page = find_lock_page(inode->i_mapping, idx); @@ -686,32 +824,97 @@ up(&info->sem); } + +int shmem_make_bigpage_mmap(struct file * file, struct vm_area_struct * vma) +{ + struct inode *inode = file->f_dentry->d_inode; + unsigned long pages; + shmem_info_t *info; + int bigpage; + + /* + * COW of 4MB/2MB pages is ... an interesting concept. Disallow it. + */ + if (!(vma->vm_flags & VM_SHARED)) + return -EINVAL; + + info = SHMEM_I(inode); + /* + * Make sure the bigpage area is properly aligned and + * properly sized, both on the virtual and on the + * physical side. + */ + bigpage = 0; + if ((vma->vm_flags & VM_BIGMAP) || shm_use_bigpages) + bigpage = 1; + if (vma->vm_start & BIGPAGE_MASK) + bigpage = 0; + if (vma->vm_end & BIGPAGE_MASK) + bigpage = 0; + if (vma->vm_pgoff % BIGPAGE_PAGES) + bigpage = 0; + if (!bigpage && (vma->vm_flags & VM_BIGMAP)) + return -EINVAL; + + pages = (vma->vm_end - vma->vm_start) / PAGE_SIZE + vma->vm_pgoff; + pages >>= (BIGPAGE_SHIFT - PAGE_SHIFT); + if (pages > info->max_bigpages) + return -ENOSPC; + + vma->vm_flags |= VM_LOCKED; + if (bigpage) + vma->vm_flags |= VM_BIGPAGE; + return 0; +} + static int shmem_mmap(struct file * file, struct vm_area_struct * vma) { - struct vm_operations_struct * ops; + struct vm_operations_struct * ops = &shmem_vm_ops; struct inode *inode = file->f_dentry->d_inode; - ops = &shmem_vm_ops; if (!inode->i_sb || !S_ISREG(inode->i_mode)) return -EACCES; + if (I_BIGPAGE(inode)) { + int error = shmem_make_bigpage_mmap(file, vma); + if (error) + return error; + } UPDATE_ATIME(inode); vma->vm_ops = ops; + vma->vm_flags &= ~VM_BIGMAP; + return 0; +} + +int shmem_munmap(struct vm_area_struct * vma, unsigned long addr, size_t size) +{ + int bigpage = vma->vm_flags & VM_BIGPAGE; + + /* + * Make sure the unmapped bigpage area is properly aligned and + * properly sized: + */ + if (bigpage) { + if (addr & BIGPAGE_MASK) + return -EINVAL; + if (size & BIGPAGE_MASK) + return -EINVAL; + } return 0; } struct inode *shmem_get_inode(struct super_block *sb, int mode, int dev) { struct inode * inode; - struct shmem_inode_info *info; + shmem_info_t *info; struct shmem_sb_info *sbinfo = SHMEM_SB(sb); - spin_lock (&sbinfo->stat_lock); + spin_lock(&sbinfo->stat_lock); if (!sbinfo->free_inodes) { - spin_unlock (&sbinfo->stat_lock); + spin_unlock(&sbinfo->stat_lock); return NULL; } sbinfo->free_inodes--; - spin_unlock (&sbinfo->stat_lock); + spin_unlock(&sbinfo->stat_lock); inode = new_inode(sb); if (inode) { @@ -727,6 +930,8 @@ info->inode = inode; spin_lock_init (&info->lock); sema_init (&info->sem, 1); + if (info->bigpages) + BUG(); switch (mode & S_IFMT) { default: init_special_inode(inode, mode, dev); @@ -734,9 +939,9 @@ case S_IFREG: inode->i_op = &shmem_inode_operations; inode->i_fop = &shmem_file_operations; - spin_lock (&shmem_ilock); - list_add (&SHMEM_I(inode)->list, &shmem_inodes); - spin_unlock (&shmem_ilock); + spin_lock(&shmem_ilock); + list_add(&SHMEM_I(inode)->list, &shmem_inodes); + spin_unlock(&shmem_ilock); break; case S_IFDIR: inode->i_nlink++; @@ -783,14 +988,23 @@ shmem_file_write(struct file *file,const char *buf,size_t count,loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; - struct shmem_inode_info *info; + shmem_info_t *info = SHMEM_I(inode); unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur; + unsigned int page_size, page_shift; loff_t pos; struct page *page; unsigned long written; long status; int err; + if (I_BIGPAGE(inode)) { + page_size = PAGE_SIZE; + page_shift = PAGE_SHIFT; + } else { + page_size = PAGE_CACHE_SIZE; + page_shift = PAGE_CACHE_SHIFT; + } + if ((ssize_t) count < 0) return -EINVAL; @@ -845,9 +1059,9 @@ * Try to find the page in the cache. If it isn't there, * allocate a free page. */ - offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */ - index = pos >> PAGE_CACHE_SHIFT; - bytes = PAGE_CACHE_SIZE - offset; + offset = (pos & (page_size -1)); /* Within page */ + index = pos >> page_shift; + bytes = page_size - offset; if (bytes > count) { bytes = count; deactivate = 0; @@ -859,24 +1073,26 @@ * same page as we're writing to, without it being marked * up-to-date. */ - { volatile unsigned char dummy; + { + volatile unsigned char dummy; __get_user(dummy, buf); __get_user(dummy, buf+bytes-1); } - info = SHMEM_I(inode); - down (&info->sem); - page = shmem_getpage_locked(info, inode, index); - up (&info->sem); + down(&info->sem); + if (I_BIGPAGE(inode)) + page = shmem_getbigpage_locked(info, inode, index); + else + page = shmem_getpage_locked(info, inode, index); + up(&info->sem); status = PTR_ERR(page); if (IS_ERR(page)) break; /* We have exclusive IO access to the page.. */ - if (!PageLocked(page)) { + if (!PageLocked(page)) PAGE_BUG(page); - } kaddr = kmap(page); status = copy_from_user(kaddr+offset, buf, bytes); @@ -899,7 +1115,10 @@ UnlockPage(page); if (deactivate) deactivate_page(page); - page_cache_release(page); + if (I_BIGPAGE(inode)) + __free_page(page); + else + page_cache_release(page); if (status < 0) break; @@ -921,29 +1140,43 @@ { struct inode *inode = filp->f_dentry->d_inode; struct address_space *mapping = inode->i_mapping; + unsigned int page_size, page_shift, page_mask; unsigned long index, offset; int nr = 1; - index = *ppos >> PAGE_CACHE_SHIFT; - offset = *ppos & ~PAGE_CACHE_MASK; + if (I_BIGPAGE(inode)) { + page_size = PAGE_SIZE; + page_shift = PAGE_SHIFT; + page_mask = PAGE_MASK; + } else { + page_size = PAGE_CACHE_SIZE; + page_shift = PAGE_CACHE_SHIFT; + page_mask = PAGE_CACHE_MASK; + } + index = *ppos >> page_shift; + offset = *ppos & ~page_mask; while (nr && desc->count) { struct page *page; unsigned long end_index, nr; - end_index = inode->i_size >> PAGE_CACHE_SHIFT; + end_index = inode->i_size >> page_shift; if (index > end_index) break; - nr = PAGE_CACHE_SIZE; + nr = page_size; if (index == end_index) { - nr = inode->i_size & ~PAGE_CACHE_MASK; + nr = inode->i_size & ~page_mask; if (nr <= offset) break; } nr = nr - offset; - if ((desc->error = shmem_getpage(inode, index, &page))) + if (I_BIGPAGE(inode)) + desc->error = shmem_getbigpage(inode, index, &page); + else + desc->error = shmem_getpage(inode, index, &page); + if (desc->error) break; if (mapping->i_mmap_shared != NULL) @@ -961,13 +1194,16 @@ */ nr = file_read_actor(desc, page, offset, nr); offset += nr; - index += offset >> PAGE_CACHE_SHIFT; - offset &= ~PAGE_CACHE_MASK; - - page_cache_release(page); + index += offset >> page_shift; + offset &= ~page_mask; + + if (I_BIGPAGE(inode)) + __free_page(page); + else + page_cache_release(page); } - *ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset; + *ppos = ((loff_t) index << page_shift) + offset; UPDATE_ATIME(inode); } @@ -1002,12 +1238,12 @@ buf->f_type = TMPFS_MAGIC; buf->f_bsize = PAGE_CACHE_SIZE; - spin_lock (&sbinfo->stat_lock); + spin_lock(&sbinfo->stat_lock); buf->f_blocks = sbinfo->max_blocks; buf->f_bavail = buf->f_bfree = sbinfo->free_blocks; buf->f_files = sbinfo->max_inodes; buf->f_ffree = sbinfo->free_inodes; - spin_unlock (&sbinfo->stat_lock); + spin_unlock(&sbinfo->stat_lock); buf->f_namelen = 255; return 0; } @@ -1035,6 +1271,8 @@ d_instantiate(dentry, inode); dget(dentry); /* Extra count - pin the dentry in core */ error = 0; + if (shm_use_bigpages > 1) + shm_enable_bigpages(dentry->d_inode, NULL, BIGPAGE_SIZE, 0); } return error; } @@ -1153,7 +1391,7 @@ struct inode *inode; struct page *page; char *kaddr; - struct shmem_inode_info * info; + shmem_info_t * info; error = shmem_mknod(dir, dentry, S_IFLNK | S_IRWXUGO, 0); if (error) @@ -1166,14 +1404,14 @@ inode = dentry->d_inode; info = SHMEM_I(inode); inode->i_size = len; - if (len <= sizeof(struct shmem_inode_info)) { + if (len <= sizeof(shmem_info_t)) { /* do it inline */ memcpy(info, symname, len); inode->i_op = &shmem_symlink_inline_operations; } else { - spin_lock (&shmem_ilock); - list_add (&info->list, &shmem_inodes); - spin_unlock (&shmem_ilock); + spin_lock(&shmem_ilock); + list_add(&info->list, &shmem_inodes); + spin_unlock(&shmem_ilock); down(&info->sem); page = shmem_getpage_locked(info, inode, 0); if (IS_ERR(page)) { @@ -1299,6 +1537,7 @@ { return 0; } + #endif static struct super_block *shmem_read_super(struct super_block * sb, void * data, int silent) @@ -1347,7 +1586,70 @@ return sb; } +/* + * Limit kmalloc() size. + */ +#define MAX_BIGPAGES (16000/sizeof(void *)) + +void shm_enable_bigpages(struct inode *inode, struct file *filp, size_t size, int prealloc) +{ + struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); + shmem_info_t * info = SHMEM_I(inode); + unsigned long pages, i, j; + + if (!S_ISREG(inode->i_mode)) + return; + if ((size < 1 /*BIGPAGE_SIZE*/) || info->bigpages || !nr_bigpages) + return; + info->max_bigpages = sbinfo->max_blocks >> (BIGPAGE_SHIFT - PAGE_CACHE_SHIFT); + if (info->max_bigpages > MAX_BIGPAGES) + info->max_bigpages = MAX_BIGPAGES; + if (!info->max_bigpages) + return; + info->bigpages = (struct page **) kmalloc(info->max_bigpages * sizeof(struct page *), GFP_KERNEL); + if (!info->bigpages) + return; + memset(info->bigpages, 0, info->max_bigpages * sizeof(struct page *)); + if (prealloc) { + pages = size / BIGPAGE_SIZE; + for (i = 0; i < pages; i++) { + if (!shm_alloc_space(inode, BIGPAGE_PAGES)) { + info->bigpages[i] = alloc_bigpage(); + if (!info->bigpages[i]) + shm_free_space(inode, BIGPAGE_PAGES); + } + if (!info->bigpages[i]) { + for (j = 0; j < i; j++) { + shm_free_space(inode, BIGPAGE_PAGES); + free_bigpage(info->bigpages[j]); + info->bigpages[j] = NULL; + } + kfree(info->bigpages); + info->max_bigpages = 0; + info->bigpages = NULL; + return; + } + } + } +} +static int shmem_ioctl(struct inode * inode, struct file * filp, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case SHMEM_IOC_SETBIGPAGES: + if (!HAVE_ARCH_BIGPAGES || !nr_bigpages) + return -EINVAL; + if (inode->i_size || SHMEM_I(inode)->swapped) + return -EINVAL; + down(&inode->i_sem); + shm_enable_bigpages(inode, filp, BIGPAGE_SIZE, 0); + up(&inode->i_sem); + break; + default: + } + return 0; +} static struct address_space_operations shmem_aops = { removepage: shmem_removepage, @@ -1356,10 +1658,12 @@ static struct file_operations shmem_file_operations = { mmap: shmem_mmap, + munmap: shmem_munmap, #ifdef CONFIG_TMPFS read: shmem_file_read, write: shmem_file_write, fsync: shmem_sync_file, + ioctl: shmem_ioctl, #endif }; @@ -1523,6 +1827,8 @@ file = shmem_file_setup("dev/zero", size); if (IS_ERR(file)) return PTR_ERR(file); + if (shm_use_bigpages) + shm_enable_bigpages(file->f_dentry->d_inode, file, BIGPAGE_SIZE, 0); if (vma->vm_file) fput (vma->vm_file); --- linux/mm/mlock.c.orig Sun Feb 24 21:18:24 2002 +++ linux/mm/mlock.c Sun Feb 24 21:18:51 2002 @@ -162,6 +162,8 @@ vma = find_vma(current->mm, start); if (!vma || vma->vm_start > start) return -ENOMEM; + if (vma->vm_flags & VM_BIGPAGE) + return -EINVAL; for (nstart = start ; ; ) { unsigned int newflags; --- linux/mm/mmap.c.orig Sun Feb 24 17:10:07 2002 +++ linux/mm/mmap.c Wed Mar 6 20:47:33 2002 @@ -199,6 +199,7 @@ flag_bits = _trans(flags, MAP_GROWSDOWN, VM_GROWSDOWN) | _trans(flags, MAP_DENYWRITE, VM_DENYWRITE) | + _trans(flags, MAP_BIGPAGE, VM_BIGMAP) | _trans(flags, MAP_EXECUTABLE, VM_EXECUTABLE); return prot_bits | flag_bits; #undef _trans @@ -329,7 +330,7 @@ vma->vm_start = addr; vma->vm_end = addr + len; vma->vm_flags = vm_flags; - vma->vm_page_prot = protection_map[vm_flags & 0x0f]; + vma->vm_page_prot = protection_map[vm_flags & MAP_TYPE]; vma->vm_ops = NULL; vma->vm_pgoff = pgoff; vma->vm_file = NULL; @@ -348,9 +349,18 @@ error = file->f_op->mmap(file, vma); if (error) goto unmap_and_free_vma; - } else if (flags & MAP_SHARED) { - error = shmem_zero_setup(vma); - if (error) + if (vma->vm_flags & VM_BIGMAP) { + error = -EINVAL; + goto unmap_and_free_vma; + } + } else { + if (flags & MAP_SHARED) { + error = shmem_zero_setup(vma); + if (error) + goto free_vma; + } + error = -EINVAL; + if (vma->vm_flags & VM_BIGMAP) goto free_vma; } @@ -751,6 +761,10 @@ if ((mpnt->vm_start < addr && mpnt->vm_end > addr+len) && mm->map_count >= max_map_count) return -ENOMEM; + if (mpnt->vm_file && mpnt->vm_file->f_op && + mpnt->vm_file->f_op->munmap) + if (mpnt->vm_file->f_op->munmap(mpnt, addr, len)) + return 0; // FIXME: -EINVAL; /* * We may need one additional vma to fix up the mappings ... @@ -1056,8 +1070,13 @@ mm->map_count--; __remove_shared_vm_struct(next); spin_unlock(&mm->page_table_lock); + if (next->vm_ops && next->vm_ops->close) + next->vm_ops->close(vma); unlock_vma_mappings(vma); - + if (next->vm_file) { + fput(next->vm_file); + next->vm_file = NULL; + } kmem_cache_free(vm_area_cachep, next); } --- linux/mm/memory.c.orig Sun Feb 24 18:05:08 2002 +++ linux/mm/memory.c Thu Mar 7 03:23:03 2002 @@ -82,6 +82,8 @@ if (pmd_none(*dir)) return; + if (pmd_bigpage(*dir)) + return; if (pmd_bad(*dir)) { pmd_ERROR(*dir); pmd_clear(dir); @@ -179,6 +181,7 @@ unsigned long address = vma->vm_start; unsigned long end = vma->vm_end; unsigned long cow = (vma->vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE; + int bigpage = (vma->vm_flags & VM_BIGPAGE); src_pgd = pgd_offset(src, address)-1; dst_pgd = pgd_offset(dst, address)-1; @@ -213,6 +216,18 @@ if (pmd_none(*src_pmd)) goto skip_copy_pte_range; + if (bigpage) { + if (!pmd_bigpage(*src_pmd)) + pmd_clear(dst_pmd); + else + *dst_pmd = *src_pmd; + address += PMD_SIZE; + if (address >= end) + goto out; + goto cont_copy_pmd_range; + } + if (pmd_bigpage(*src_pmd)) + BUG(); if (pmd_bad(*src_pmd)) { pmd_ERROR(*src_pmd); pmd_clear(src_pmd); @@ -297,11 +312,13 @@ static inline int zap_pte_range(mmu_gather_t *tlb, pmd_t * pmd, unsigned long address, unsigned long size) { unsigned long offset; - pte_t *mapped, *ptep; int freed = 0; + pte_t *ptep; if (pmd_none(*pmd)) return 0; + if (pmd_bigpage(*pmd)) + BUG(); if (pmd_bad(*pmd)) { pmd_ERROR(*pmd); pmd_clear(pmd); @@ -351,7 +368,10 @@ end = ((address + PGDIR_SIZE) & PGDIR_MASK); freed = 0; do { - freed += zap_pte_range(tlb, pmd, address, end - address); + if (pmd_bigpage(*pmd)) + pmd_clear(pmd); + else + freed += zap_pte_range(tlb, pmd, address, end - address); address = (address + PMD_SIZE) & PMD_MASK; pmd++; } while (address < end); @@ -403,11 +423,10 @@ spin_unlock(&mm->page_table_lock); } - /* * Do a quick page-table lookup for a single page. */ -static struct page * follow_page(unsigned long address, int write) +struct page * follow_page(unsigned long address, int write) { pgd_t *pgd; pmd_t *pmd; @@ -418,6 +437,8 @@ goto out; pmd = pmd_offset(pgd, address); + if (pmd_bigpage(*pmd)) + return pmd_page(*pmd) + (address & BIGPAGE_MASK) / PAGE_SIZE; if (pmd_none(*pmd) || pmd_bad(*pmd)) goto out; @@ -1381,6 +1402,40 @@ return 1; } +static int handle_bigpage_fault(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long long address, int write_access, pmd_t *pmd) +{ + struct inode *inode = vma->vm_file->f_dentry->d_inode; + struct page *new_page; + + spin_unlock(&mm->page_table_lock); + down_read(&inode->i_truncate_sem); + new_page = vma->vm_ops->nopage(vma, address & PAGE_MASK, (vma->vm_flags & VM_SHARED)?0:write_access); + up_read(&inode->i_truncate_sem); + spin_lock(&mm->page_table_lock); + + if (!new_page) + return 0; + if (new_page == NOPAGE_OOM) + return -1; + if (!BigPage(new_page)) + BUG(); + /* + * Another context was faster. + */ + if (pmd_present(*pmd)) { + if (pmd_bigpage(*pmd)) + return 1; + free_one_pmd(pmd); + } + /* + * Major fault. + */ + pmd_populate_bigpage(mm, pmd, new_page); + flush_tlb_page(vma, address & PMD_MASK); + update_mmu_cache(vma, address, entry); + return 2; +} + /* * By the time we get here, we already hold the mm semaphore */ @@ -1390,6 +1445,7 @@ int ret = -1; pgd_t *pgd; pmd_t *pmd; + int bigpage = (vma->vm_flags & VM_BIGPAGE); current->state = TASK_RUNNING; pgd = pgd_offset(mm, address); @@ -1401,13 +1457,15 @@ spin_lock(&mm->page_table_lock); pmd = pmd_alloc(mm, pgd, address); - if (pmd) { + if (pmd && !bigpage) { pte_t * pte = pte_alloc_map(mm, pmd, address); if (pte) { ret = handle_pte_fault(mm, vma, address, write_access, &pte, pmd); pte_unmap(pte); } - } + } else + if (pmd) + ret = handle_bigpage_fault(mm, vma, address, write_access, pmd); spin_unlock(&mm->page_table_lock); return ret; } @@ -1712,6 +1770,8 @@ if (!pgd_none(*pgd)) { pmd = pmd_offset(pgd, addr); if (!pmd_none(*pmd)) { + if (pmd_bigpage(*pmd)) + BUG(); ptep = pte_offset_map(pmd, addr); pte = *ptep; if (pte_present(pte)) --- linux/mm/mremap.c.orig Sun Feb 24 19:59:04 2002 +++ linux/mm/mremap.c Mon Feb 25 18:17:48 2002 @@ -245,6 +245,11 @@ vma = find_vma(current->mm, addr); if (!vma || vma->vm_start > addr) goto out; + /* + * Do not remap bigpages, yet. + */ + if (vma->vm_flags & VM_BIGPAGE) + goto out; /* We can't remap across vm area boundaries */ if (old_len > vma->vm_end - addr) goto out; --- linux/mm/mprotect.c.orig Sun Feb 24 20:04:35 2002 +++ linux/mm/mprotect.c Wed Mar 6 00:55:20 2002 @@ -21,6 +21,9 @@ if (pmd_none(*pmd)) return; + // FIXME: we now silently 'succeed', we should split up the bigpage. + if (pmd_bigpage(*pmd)) + return; if (pmd_bad(*pmd)) { pmd_ERROR(*pmd); pmd_clear(pmd); @@ -250,6 +253,10 @@ vma = find_vma(current->mm, start); error = -EFAULT; if (!vma || vma->vm_start > start) + goto out; + + error = 0; + if (vma->vm_flags & VM_BIGPAGE) goto out; for (nstart = start ; ; ) { --- linux/mm/Makefile.orig Sun Feb 24 22:05:59 2002 +++ linux/mm/Makefile Sun Feb 24 13:04:26 2002 @@ -14,7 +14,7 @@ obj-y := memory.o mmap.o filemap.o mprotect.o mlock.o mremap.o \ vmalloc.o slab.o bootmem.o swap.o vmscan.o page_io.o \ page_alloc.o swap_state.o swapfile.o numa.o oom_kill.o \ - shmem.o + shmem.o bigpages.o obj-$(CONFIG_HIGHMEM) += highmem.o obj-y += wtd.o --- linux/include/linux/mm.h.orig Sun Feb 24 13:57:11 2002 +++ linux/include/linux/mm.h Thu Mar 7 04:48:18 2002 @@ -102,6 +102,8 @@ #define VM_DONTCOPY 0x00020000 /* Do not copy this vma on fork */ #define VM_DONTEXPAND 0x00040000 /* Cannot expand with mremap() */ #define VM_RESERVED 0x00080000 /* Don't unmap it from swap_out */ +#define VM_BIGPAGE 0x00100000 /* bigpage mappings, no pte's */ +#define VM_BIGMAP 0x00200000 /* user wants bigpage mapping */ #define VM_STACK_FLAGS 0x00000177 @@ -286,6 +288,7 @@ #define PG_inactive_clean 11 #define PG_highmem 12 #define PG_checked 13 /* kill me in 2.5.<early>. */ +#define PG_bigpage 14 /* bits 21-29 unused */ #define PG_arch_1 30 #define PG_reserved 31 @@ -302,6 +305,7 @@ #define TryLockPage(page) test_and_set_bit(PG_locked, &(page)->flags) #define PageChecked(page) test_bit(PG_checked, &(page)->flags) #define SetPageChecked(page) set_bit(PG_checked, &(page)->flags) +#define BigPage(page) test_bit(PG_bigpage, &(page)->flags) extern void __set_page_dirty(struct page *); @@ -424,6 +428,10 @@ * The old interface name will be removed in 2.5: */ #define get_free_page get_zeroed_page + +extern long nr_bigpages; +extern struct page * FASTCALL(alloc_bigpage(void)); +extern void FASTCALL(free_bigpage(struct page *page)); /* * There is only one 'core' page-freeing function. --- linux/include/linux/shmem_fs.h.orig Sun Feb 24 13:06:31 2002 +++ linux/include/linux/shmem_fs.h Tue Mar 5 22:34:13 2002 @@ -19,7 +19,7 @@ extern atomic_t shmem_nrpages; -struct shmem_inode_info { +typedef struct shmem_inode_info { spinlock_t lock; struct semaphore sem; unsigned long next_index; @@ -29,7 +29,9 @@ int locked; /* into memory */ struct list_head list; struct inode *inode; -}; + unsigned long max_bigpages; + struct page **bigpages; +} shmem_info_t; struct shmem_sb_info { unsigned long max_blocks; /* How many blocks are allowed */ @@ -39,6 +41,19 @@ spinlock_t stat_lock; }; -#define SHMEM_I(inode) (&inode->u.shmem_i) +#define SHMEM_I(inode) (&inode->u.shmem_i) +#define I_BIGPAGE(inode) (SHMEM_I(inode)->bigpages) + +#define SHMEM_IOC_SETBIGPAGES _IO('f', 1) + +extern size_t shm_ctlmax; +extern void shm_enable_bigpages(struct inode *inode, struct file *filp, size_t size, int prealloc); +extern int shm_use_bigpages; +extern struct page * shmem_nopage(struct vm_area_struct * vma, unsigned long address, int no_share); +extern int shmem_make_bigpage_mmap(struct file * file, struct vm_area_struct * vma); + +extern void check_fault_page(struct vm_area_struct *vma, unsigned long addr); +extern struct page * follow_page(unsigned long address, int write); +extern int shmem_munmap(struct vm_area_struct * vma, unsigned long addr, size_t size); #endif --- linux/include/linux/fs.h.orig Mon Feb 25 13:22:41 2002 +++ linux/include/linux/fs.h Thu Mar 7 04:48:15 2002 @@ -855,6 +855,7 @@ ssize_t (*aio_readx)(struct file *, struct kiocb *, struct iocb); ssize_t (*aio_write)(struct file *, struct kiocb *, struct iocb); ssize_t (*aio_fsync)(struct file *, struct kiocb *, struct iocb); + int (*munmap) (struct vm_area_struct *, unsigned long, size_t); }; struct inode_operations { --- linux/include/linux/sysctl.h.orig Tue Feb 26 09:36:17 2002 +++ linux/include/linux/sysctl.h Tue Mar 5 19:41:47 2002 @@ -125,6 +125,7 @@ KERN_CADPID=54, /* int: PID of the process to notify on CAD */ KERN_TAINTED=55, /* int: various kernel tainted flags */ KERN_CHILD_RUNS_FIRST=56, /* int: child-runs-first forking */ + KERN_SHMUSEBIGPAGES=57, /* int: use bigpages wherever possible */ }; --- linux/include/asm-i386/pgtable.h.orig Sun Feb 24 19:36:09 2002 +++ linux/include/asm-i386/pgtable.h Thu Mar 7 04:48:15 2002 @@ -262,6 +262,14 @@ #define pmd_present(x) (pmd_val(x) & _PAGE_PRESENT) #define pmd_clear(xp) do { set_pmd(xp, __pmd(0)); } while (0) #define pmd_bad(x) ((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE) +#define pmd_bigpage(x) (pmd_val(x) & _PAGE_PSE) + +#define BIGPAGE_SHIFT (PMD_SHIFT) +#define BIGPAGE_SIZE (1UL << BIGPAGE_SHIFT) +#define BIGPAGE_MASK (BIGPAGE_SIZE - 1) +#define BIGPAGE_PAGES (BIGPAGE_SIZE / PAGE_SIZE) + +#define HAVE_ARCH_BIGPAGES cpu_has_pse /* * Permanent address of a page. Obviously must never be --- linux/include/asm-i386/pgalloc.h.orig Sun Feb 24 18:12:48 2002 +++ linux/include/asm-i386/pgalloc.h Thu Mar 7 04:48:15 2002 @@ -16,6 +16,18 @@ ((unsigned long long)(pte - mem_map) << (unsigned long long) PAGE_SHIFT))); } + +static inline void pmd_populate_bigpage(struct mm_struct *mm, pmd_t *pmd, struct page *page) +{ + unsigned int idx = page - mem_map; + + if (idx & ((1 << (BIGPAGE_SHIFT - PAGE_SHIFT)) -1)) { + printk("ugh, page idx %d (%p) cannot be PSE page!\n", idx, page); + BUG(); + } + set_pmd(pmd, __pmd(_PAGE_TABLE + _PAGE_PSE + + ((unsigned long long)idx << (unsigned long long) PAGE_SHIFT))); +} /* * Allocate and free page tables. */ --- linux/include/asm-i386/mman.h.orig Mon Feb 25 12:16:26 2002 +++ linux/include/asm-i386/mman.h Mon Feb 25 12:32:19 2002 @@ -11,6 +11,7 @@ #define MAP_TYPE 0x0f /* Mask for type of mapping */ #define MAP_FIXED 0x10 /* Interpret addr exactly */ #define MAP_ANONYMOUS 0x20 /* don't use a file */ +#define MAP_BIGPAGE 0x40 /* bigpage mapping */ #define MAP_GROWSDOWN 0x0100 /* stack-like segment */ #define MAP_DENYWRITE 0x0800 /* ETXTBSY */ --- linux/include/asm-i386/atomic.h.orig Thu Feb 28 11:43:34 2002 +++ linux/include/asm-i386/atomic.h Thu Mar 7 04:48:15 2002 @@ -2,6 +2,7 @@ #define __ARCH_I386_ATOMIC__ #include <linux/config.h> +#include <asm/page.h> /* * Atomic operations that C can't guarantee us. Useful for --- linux/ipc/shm.c.orig Tue Feb 26 08:39:46 2002 +++ linux/ipc/shm.c Wed Mar 6 11:15:40 2002 @@ -124,6 +124,10 @@ shm_tot -= (shp->shm_segsz + PAGE_SIZE - 1) >> PAGE_SHIFT; shm_rmid (shp->id); shmem_lock(shp->shm_file, 0); + /* + * Unlock the spinlock first, fput() might sleep. + */ + shm_unlock(shp->id); fput (shp->shm_file); kfree (shp); } @@ -149,22 +153,30 @@ shp->shm_nattch--; if(shp->shm_nattch == 0 && shp->shm_flags & SHM_DEST) - shm_destroy (shp); - - shm_unlock(id); + shm_destroy(shp); /* unlocks */ + else + shm_unlock(id); up (&shm_ids.sem); } static int shm_mmap(struct file * file, struct vm_area_struct * vma) { + int error; + UPDATE_ATIME(file->f_dentry->d_inode); + if (SHMEM_I(file->f_dentry->d_inode)->bigpages) { + error = shmem_make_bigpage_mmap(file, vma); + if (error) + return -EINVAL; + } vma->vm_ops = &shm_vm_ops; shm_inc(file->f_dentry->d_inode->i_ino); return 0; } static struct file_operations shm_file_operations = { - mmap: shm_mmap + mmap: shm_mmap, + munmap: shmem_munmap }; static struct vm_operations_struct shm_vm_ops = { @@ -197,10 +209,16 @@ if (IS_ERR(file)) goto no_file; + file->f_op = &shm_file_operations; + error = -ENOMEM; + if (shm_use_bigpages) + shm_enable_bigpages(file->f_dentry->d_inode, file, size, 1); + error = -ENOSPC; id = shm_addid(shp); if(id == -1) goto no_id; + shp->shm_perm.key = key; shp->shm_flags = (shmflg & S_IRWXUGO); shp->shm_cprid = current->pid; @@ -212,7 +230,6 @@ shp->id = shm_buildid(id,shp->shm_perm.seq); shp->shm_file = file; file->f_dentry->d_inode->i_ino = shp->id; - file->f_op = &shm_file_operations; shm_tot += numpages; shm_unlock (id); return shp->id; @@ -511,11 +528,11 @@ shp->shm_flags |= SHM_DEST; /* Do not find it any more */ shp->shm_perm.key = IPC_PRIVATE; + /* Unlock */ + shm_unlock(shmid); } else - shm_destroy (shp); + shm_destroy (shp); /* unlocks */ - /* Unlock */ - shm_unlock(shmid); up(&shm_ids.sem); return err; } @@ -631,8 +648,9 @@ shp->shm_nattch--; if(shp->shm_nattch == 0 && shp->shm_flags & SHM_DEST) - shm_destroy (shp); - shm_unlock(shmid); + shm_destroy (shp); /* unlocks */ + else + shm_unlock(shmid); up (&shm_ids.sem); *raddr = (unsigned long) user_addr; @@ -649,18 +667,19 @@ */ asmlinkage long sys_shmdt (char *shmaddr) { + int error = -EINVAL; struct mm_struct *mm = current->mm; struct vm_area_struct *shmd, *shmdnext; down_write(&mm->mmap_sem); for (shmd = mm->mmap; shmd; shmd = shmdnext) { shmdnext = shmd->vm_next; - if (shmd->vm_ops == &shm_vm_ops - && shmd->vm_start - (shmd->vm_pgoff << PAGE_SHIFT) == (ulong) shmaddr) - do_munmap(mm, shmd->vm_start, shmd->vm_end - shmd->vm_start); + if ((shmd->vm_ops == &shm_vm_ops) && + (shmd->vm_start == (ulong) shmaddr)) + error = do_munmap(mm, shmd->vm_start, shmd->vm_end - shmd->vm_start); } up_write(&mm->mmap_sem); - return 0; + return error; } #ifdef CONFIG_PROC_FS --- linux/mm/mmap.c~ Tue Mar 26 10:59:01 2002 +++ linux/mm/mmap.c Tue Mar 26 17:00:18 2002 @@ -1070,9 +1070,9 @@ mm->map_count--; __remove_shared_vm_struct(next); spin_unlock(&mm->page_table_lock); - if (next->vm_ops && next->vm_ops->close) - next->vm_ops->close(vma); unlock_vma_mappings(vma); + if (next->vm_ops && next->vm_ops->close) + next->vm_ops->close(next); if (next->vm_file) { fput(next->vm_file); next->vm_file = NULL;
