--- 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;