2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* hugetlbpage-backed filesystem. Based on ramfs.
|
|
|
|
*
|
|
|
|
* William Irwin, 2002
|
|
|
|
*
|
|
|
|
* Copyright (C) 2002 Linus Torvalds.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/thread_info.h>
|
|
|
|
#include <asm/current.h>
|
|
|
|
#include <linux/sched.h> /* remove ASAP */
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/mount.h>
|
|
|
|
#include <linux/file.h>
|
2007-07-16 10:40:52 +04:00
|
|
|
#include <linux/kernel.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <linux/writeback.h>
|
|
|
|
#include <linux/pagemap.h>
|
|
|
|
#include <linux/highmem.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/string.h>
|
2006-01-11 23:17:46 +03:00
|
|
|
#include <linux/capability.h>
|
2007-07-16 10:40:52 +04:00
|
|
|
#include <linux/ctype.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <linux/backing-dev.h>
|
|
|
|
#include <linux/hugetlb.h>
|
|
|
|
#include <linux/pagevec.h>
|
2007-07-16 10:40:52 +04:00
|
|
|
#include <linux/parser.h>
|
2007-05-07 01:50:12 +04:00
|
|
|
#include <linux/mman.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/dnotify.h>
|
|
|
|
#include <linux/statfs.h>
|
|
|
|
#include <linux/security.h>
|
2009-09-23 03:43:33 +04:00
|
|
|
#include <linux/magic.h>
|
2010-09-08 05:19:35 +04:00
|
|
|
#include <linux/migrate.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
#include <asm/uaccess.h>
|
|
|
|
|
2007-02-12 11:55:41 +03:00
|
|
|
static const struct super_operations hugetlbfs_ops;
|
2006-06-28 15:26:44 +04:00
|
|
|
static const struct address_space_operations hugetlbfs_aops;
|
2006-03-28 13:56:42 +04:00
|
|
|
const struct file_operations hugetlbfs_file_operations;
|
2007-02-12 11:55:39 +03:00
|
|
|
static const struct inode_operations hugetlbfs_dir_inode_operations;
|
|
|
|
static const struct inode_operations hugetlbfs_inode_operations;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
static struct backing_dev_info hugetlbfs_backing_dev_info = {
|
2009-06-12 16:45:52 +04:00
|
|
|
.name = "hugetlbfs",
|
2005-04-17 02:20:36 +04:00
|
|
|
.ra_pages = 0, /* No readahead */
|
2008-04-30 11:54:37 +04:00
|
|
|
.capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK,
|
2005-04-17 02:20:36 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
int sysctl_hugetlb_shm_group;
|
|
|
|
|
2007-07-16 10:40:52 +04:00
|
|
|
enum {
|
|
|
|
Opt_size, Opt_nr_inodes,
|
|
|
|
Opt_mode, Opt_uid, Opt_gid,
|
2008-07-24 08:27:43 +04:00
|
|
|
Opt_pagesize,
|
2007-07-16 10:40:52 +04:00
|
|
|
Opt_err,
|
|
|
|
};
|
|
|
|
|
2008-10-13 13:46:57 +04:00
|
|
|
static const match_table_t tokens = {
|
2007-07-16 10:40:52 +04:00
|
|
|
{Opt_size, "size=%s"},
|
|
|
|
{Opt_nr_inodes, "nr_inodes=%s"},
|
|
|
|
{Opt_mode, "mode=%o"},
|
|
|
|
{Opt_uid, "uid=%u"},
|
|
|
|
{Opt_gid, "gid=%u"},
|
2008-07-24 08:27:43 +04:00
|
|
|
{Opt_pagesize, "pagesize=%s"},
|
2007-07-16 10:40:52 +04:00
|
|
|
{Opt_err, NULL},
|
|
|
|
};
|
|
|
|
|
2005-10-30 04:16:47 +03:00
|
|
|
static void huge_pagevec_release(struct pagevec *pvec)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < pagevec_count(pvec); ++i)
|
|
|
|
put_page(pvec->pages[i]);
|
|
|
|
|
|
|
|
pagevec_reinit(pvec);
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
|
|
|
|
{
|
2006-12-08 13:37:07 +03:00
|
|
|
struct inode *inode = file->f_path.dentry->d_inode;
|
2005-04-17 02:20:36 +04:00
|
|
|
loff_t len, vma_len;
|
|
|
|
int ret;
|
2008-07-24 08:27:41 +04:00
|
|
|
struct hstate *h = hstate_file(file);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
[PATCH] hugetlb: prepare_hugepage_range check offset too
(David:)
If hugetlbfs_file_mmap() returns a failure to do_mmap_pgoff() - for example,
because the given file offset is not hugepage aligned - then do_mmap_pgoff
will go to the unmap_and_free_vma backout path.
But at this stage the vma hasn't been marked as hugepage, and the backout path
will call unmap_region() on it. That will eventually call down to the
non-hugepage version of unmap_page_range(). On ppc64, at least, that will
cause serious problems if there are any existing hugepage pagetable entries in
the vicinity - for example if there are any other hugepage mappings under the
same PUD. unmap_page_range() will trigger a bad_pud() on the hugepage pud
entries. I suspect this will also cause bad problems on ia64, though I don't
have a machine to test it on.
(Hugh:)
prepare_hugepage_range() should check file offset alignment when it checks
virtual address and length, to stop MAP_FIXED with a bad huge offset from
unmapping before it fails further down. PowerPC should apply the same
prepare_hugepage_range alignment checks as ia64 and all the others do.
Then none of the alignment checks in hugetlbfs_file_mmap are required (nor
is the check for too small a mapping); but even so, move up setting of
VM_HUGETLB and add a comment to warn of what David Gibson discovered - if
hugetlbfs_file_mmap fails before setting it, do_mmap_pgoff's unmap_region
when unwinding from error will go the non-huge way, which may cause bad
behaviour on architectures (powerpc and ia64) which segregate their huge
mappings into a separate region of the address space.
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Cc: "Luck, Tony" <tony.luck@intel.com>
Cc: "David S. Miller" <davem@davemloft.net>
Acked-by: Adam Litke <agl@us.ibm.com>
Acked-by: David Gibson <david@gibson.dropbear.id.au>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-11-14 13:03:32 +03:00
|
|
|
/*
|
2007-08-31 10:56:40 +04:00
|
|
|
* vma address alignment (but not the pgoff alignment) has
|
|
|
|
* already been checked by prepare_hugepage_range. If you add
|
|
|
|
* any error returns here, do so after setting VM_HUGETLB, so
|
|
|
|
* is_vm_hugetlb_page tests below unmap_region go the right
|
|
|
|
* way when do_mmap_pgoff unwinds (may be important on powerpc
|
|
|
|
* and ia64).
|
[PATCH] hugetlb: prepare_hugepage_range check offset too
(David:)
If hugetlbfs_file_mmap() returns a failure to do_mmap_pgoff() - for example,
because the given file offset is not hugepage aligned - then do_mmap_pgoff
will go to the unmap_and_free_vma backout path.
But at this stage the vma hasn't been marked as hugepage, and the backout path
will call unmap_region() on it. That will eventually call down to the
non-hugepage version of unmap_page_range(). On ppc64, at least, that will
cause serious problems if there are any existing hugepage pagetable entries in
the vicinity - for example if there are any other hugepage mappings under the
same PUD. unmap_page_range() will trigger a bad_pud() on the hugepage pud
entries. I suspect this will also cause bad problems on ia64, though I don't
have a machine to test it on.
(Hugh:)
prepare_hugepage_range() should check file offset alignment when it checks
virtual address and length, to stop MAP_FIXED with a bad huge offset from
unmapping before it fails further down. PowerPC should apply the same
prepare_hugepage_range alignment checks as ia64 and all the others do.
Then none of the alignment checks in hugetlbfs_file_mmap are required (nor
is the check for too small a mapping); but even so, move up setting of
VM_HUGETLB and add a comment to warn of what David Gibson discovered - if
hugetlbfs_file_mmap fails before setting it, do_mmap_pgoff's unmap_region
when unwinding from error will go the non-huge way, which may cause bad
behaviour on architectures (powerpc and ia64) which segregate their huge
mappings into a separate region of the address space.
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Cc: "Luck, Tony" <tony.luck@intel.com>
Cc: "David S. Miller" <davem@davemloft.net>
Acked-by: Adam Litke <agl@us.ibm.com>
Acked-by: David Gibson <david@gibson.dropbear.id.au>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-11-14 13:03:32 +03:00
|
|
|
*/
|
|
|
|
vma->vm_flags |= VM_HUGETLB | VM_RESERVED;
|
|
|
|
vma->vm_ops = &hugetlb_vm_ops;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2011-07-26 04:11:49 +04:00
|
|
|
if (vma->vm_pgoff & (~huge_page_mask(h) >> PAGE_SHIFT))
|
2007-08-31 10:56:40 +04:00
|
|
|
return -EINVAL;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
vma_len = (loff_t)(vma->vm_end - vma->vm_start);
|
|
|
|
|
2006-01-10 02:59:24 +03:00
|
|
|
mutex_lock(&inode->i_mutex);
|
2005-04-17 02:20:36 +04:00
|
|
|
file_accessed(file);
|
|
|
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
len = vma_len + ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
|
|
|
|
|
2008-07-24 08:27:23 +04:00
|
|
|
if (hugetlb_reserve_pages(inode,
|
2008-07-24 08:27:41 +04:00
|
|
|
vma->vm_pgoff >> huge_page_order(h),
|
2009-02-10 17:02:27 +03:00
|
|
|
len >> huge_page_shift(h), vma,
|
|
|
|
vma->vm_flags))
|
2006-06-23 13:03:15 +04:00
|
|
|
goto out;
|
[PATCH] hugepage: Strict page reservation for hugepage inodes
These days, hugepages are demand-allocated at first fault time. There's a
somewhat dubious (and racy) heuristic when making a new mmap() to check if
there are enough available hugepages to fully satisfy that mapping.
A particularly obvious case where the heuristic breaks down is where a
process maps its hugepages not as a single chunk, but as a bunch of
individually mmap()ed (or shmat()ed) blocks without touching and
instantiating the pages in between allocations. In this case the size of
each block is compared against the total number of available hugepages.
It's thus easy for the process to become overcommitted, because each block
mapping will succeed, although the total number of hugepages required by
all blocks exceeds the number available. In particular, this defeats such
a program which will detect a mapping failure and adjust its hugepage usage
downward accordingly.
The patch below addresses this problem, by strictly reserving a number of
physical hugepages for hugepage inodes which have been mapped, but not
instatiated. MAP_SHARED mappings are thus "safe" - they will fail on
mmap(), not later with an OOM SIGKILL. MAP_PRIVATE mappings can still
trigger an OOM. (Actually SHARED mappings can technically still OOM, but
only if the sysadmin explicitly reduces the hugepage pool between mapping
and instantiation)
This patch appears to address the problem at hand - it allows DB2 to start
correctly, for instance, which previously suffered the failure described
above.
This patch causes no regressions on the libhugetblfs testsuite, and makes a
test (designed to catch this problem) pass which previously failed (ppc64,
POWER5).
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-22 11:08:55 +03:00
|
|
|
|
2005-10-30 04:16:46 +03:00
|
|
|
ret = 0;
|
|
|
|
hugetlb_prefault_arch_hook(vma->vm_mm);
|
[PATCH] mmap zero-length hugetlb file with PROT_NONE to protect a hugetlb virtual area
Sometimes, applications need below call to be successful although
"/mnt/hugepages/file1" doesn't exist.
fd = open("/mnt/hugepages/file1", O_CREAT|O_RDWR, 0755);
*addr = mmap(NULL, 0x1024*1024*256, PROT_NONE, 0, fd, 0);
As for regular pages (or files), above call does work, but as for huge
pages, above call would fail because hugetlbfs_file_mmap would fail if
(!(vma->vm_flags & VM_WRITE) && len > inode->i_size).
This capability on huge page is useful on ia64 when the process wants to
protect one area on region 4, so other threads couldn't read/write this
area. A famous JVM (Java Virtual Machine) implementation on IA64 needs the
capability.
Signed-off-by: Zhang Yanmin <yanmin.zhang@intel.com>
Cc: David Gibson <david@gibson.dropbear.id.au>
Cc: Hugh Dickins <hugh@veritas.com>
[ Expand-on-mmap semantics again... this time matching normal fs's. wli ]
Acked-by: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-07-10 15:44:49 +04:00
|
|
|
if (vma->vm_flags & VM_WRITE && inode->i_size < len)
|
2005-04-17 02:20:36 +04:00
|
|
|
inode->i_size = len;
|
|
|
|
out:
|
2006-01-10 02:59:24 +03:00
|
|
|
mutex_unlock(&inode->i_mutex);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-10-30 04:16:30 +03:00
|
|
|
* Called under down_write(mmap_sem).
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
|
|
|
|
2007-05-07 01:49:00 +04:00
|
|
|
#ifndef HAVE_ARCH_HUGETLB_UNMAPPED_AREA
|
2005-04-17 02:20:36 +04:00
|
|
|
static unsigned long
|
|
|
|
hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
|
|
|
|
unsigned long len, unsigned long pgoff, unsigned long flags)
|
|
|
|
{
|
|
|
|
struct mm_struct *mm = current->mm;
|
|
|
|
struct vm_area_struct *vma;
|
|
|
|
unsigned long start_addr;
|
2008-07-24 08:27:41 +04:00
|
|
|
struct hstate *h = hstate_file(file);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-07-24 08:27:41 +04:00
|
|
|
if (len & ~huge_page_mask(h))
|
2005-04-17 02:20:36 +04:00
|
|
|
return -EINVAL;
|
|
|
|
if (len > TASK_SIZE)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2007-05-07 01:50:12 +04:00
|
|
|
if (flags & MAP_FIXED) {
|
2008-07-24 08:27:41 +04:00
|
|
|
if (prepare_hugepage_range(file, addr, len))
|
2007-05-07 01:50:12 +04:00
|
|
|
return -EINVAL;
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
if (addr) {
|
2008-07-24 08:27:41 +04:00
|
|
|
addr = ALIGN(addr, huge_page_size(h));
|
2005-04-17 02:20:36 +04:00
|
|
|
vma = find_vma(mm, addr);
|
|
|
|
if (TASK_SIZE - len >= addr &&
|
|
|
|
(!vma || addr + len <= vma->vm_start))
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
start_addr = mm->free_area_cache;
|
|
|
|
|
2005-06-22 04:14:49 +04:00
|
|
|
if (len <= mm->cached_hole_size)
|
|
|
|
start_addr = TASK_UNMAPPED_BASE;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
full_search:
|
2008-07-24 08:27:41 +04:00
|
|
|
addr = ALIGN(start_addr, huge_page_size(h));
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
|
|
|
|
/* At this point: (!vma || addr < vma->vm_end). */
|
|
|
|
if (TASK_SIZE - len < addr) {
|
|
|
|
/*
|
|
|
|
* Start a new search - just in case we missed
|
|
|
|
* some holes.
|
|
|
|
*/
|
|
|
|
if (start_addr != TASK_UNMAPPED_BASE) {
|
|
|
|
start_addr = TASK_UNMAPPED_BASE;
|
|
|
|
goto full_search;
|
|
|
|
}
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!vma || addr + len <= vma->vm_start)
|
|
|
|
return addr;
|
2008-07-24 08:27:41 +04:00
|
|
|
addr = ALIGN(vma->vm_end, huge_page_size(h));
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-10-16 12:26:22 +04:00
|
|
|
static int
|
|
|
|
hugetlbfs_read_actor(struct page *page, unsigned long offset,
|
|
|
|
char __user *buf, unsigned long count,
|
|
|
|
unsigned long size)
|
|
|
|
{
|
|
|
|
char *kaddr;
|
|
|
|
unsigned long left, copied = 0;
|
|
|
|
int i, chunksize;
|
|
|
|
|
|
|
|
if (size > count)
|
|
|
|
size = count;
|
|
|
|
|
|
|
|
/* Find which 4k chunk and offset with in that chunk */
|
|
|
|
i = offset >> PAGE_CACHE_SHIFT;
|
|
|
|
offset = offset & ~PAGE_CACHE_MASK;
|
|
|
|
|
|
|
|
while (size) {
|
|
|
|
chunksize = PAGE_CACHE_SIZE;
|
|
|
|
if (offset)
|
|
|
|
chunksize -= offset;
|
|
|
|
if (chunksize > size)
|
|
|
|
chunksize = size;
|
|
|
|
kaddr = kmap(&page[i]);
|
|
|
|
left = __copy_to_user(buf, kaddr + offset, chunksize);
|
|
|
|
kunmap(&page[i]);
|
|
|
|
if (left) {
|
|
|
|
copied += (chunksize - left);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
offset = 0;
|
|
|
|
size -= chunksize;
|
|
|
|
buf += chunksize;
|
|
|
|
copied += chunksize;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
return copied ? copied : -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Support for read() - Find the page attached to f_mapping and copy out the
|
|
|
|
* data. Its *very* similar to do_generic_mapping_read(), we can't use that
|
|
|
|
* since it has PAGE_CACHE_SIZE assumptions.
|
|
|
|
*/
|
|
|
|
static ssize_t hugetlbfs_read(struct file *filp, char __user *buf,
|
|
|
|
size_t len, loff_t *ppos)
|
|
|
|
{
|
2008-07-24 08:27:41 +04:00
|
|
|
struct hstate *h = hstate_file(filp);
|
2007-10-16 12:26:22 +04:00
|
|
|
struct address_space *mapping = filp->f_mapping;
|
|
|
|
struct inode *inode = mapping->host;
|
2008-07-24 08:27:41 +04:00
|
|
|
unsigned long index = *ppos >> huge_page_shift(h);
|
|
|
|
unsigned long offset = *ppos & ~huge_page_mask(h);
|
2007-10-16 12:26:22 +04:00
|
|
|
unsigned long end_index;
|
|
|
|
loff_t isize;
|
|
|
|
ssize_t retval = 0;
|
|
|
|
|
|
|
|
mutex_lock(&inode->i_mutex);
|
|
|
|
|
|
|
|
/* validate length */
|
|
|
|
if (len == 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
isize = i_size_read(inode);
|
|
|
|
if (!isize)
|
|
|
|
goto out;
|
|
|
|
|
2008-07-24 08:27:41 +04:00
|
|
|
end_index = (isize - 1) >> huge_page_shift(h);
|
2007-10-16 12:26:22 +04:00
|
|
|
for (;;) {
|
|
|
|
struct page *page;
|
2008-07-24 08:27:41 +04:00
|
|
|
unsigned long nr, ret;
|
2009-01-07 01:40:14 +03:00
|
|
|
int ra;
|
2007-10-16 12:26:22 +04:00
|
|
|
|
|
|
|
/* nr is the maximum number of bytes to copy from this page */
|
2008-07-24 08:27:41 +04:00
|
|
|
nr = huge_page_size(h);
|
2007-10-16 12:26:22 +04:00
|
|
|
if (index >= end_index) {
|
|
|
|
if (index > end_index)
|
|
|
|
goto out;
|
2008-07-24 08:27:41 +04:00
|
|
|
nr = ((isize - 1) & ~huge_page_mask(h)) + 1;
|
2007-10-16 12:26:22 +04:00
|
|
|
if (nr <= offset) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nr = nr - offset;
|
|
|
|
|
|
|
|
/* Find the page */
|
|
|
|
page = find_get_page(mapping, index);
|
|
|
|
if (unlikely(page == NULL)) {
|
|
|
|
/*
|
|
|
|
* We have a HOLE, zero out the user-buffer for the
|
|
|
|
* length of the hole or request.
|
|
|
|
*/
|
|
|
|
ret = len < nr ? len : nr;
|
|
|
|
if (clear_user(buf, ret))
|
2009-01-07 01:40:14 +03:00
|
|
|
ra = -EFAULT;
|
|
|
|
else
|
|
|
|
ra = 0;
|
2007-10-16 12:26:22 +04:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We have the page, copy it to user space buffer.
|
|
|
|
*/
|
2009-01-07 01:40:14 +03:00
|
|
|
ra = hugetlbfs_read_actor(page, offset, buf, len, nr);
|
|
|
|
ret = ra;
|
2007-10-16 12:26:22 +04:00
|
|
|
}
|
2009-01-07 01:40:14 +03:00
|
|
|
if (ra < 0) {
|
2007-10-16 12:26:22 +04:00
|
|
|
if (retval == 0)
|
2009-01-07 01:40:14 +03:00
|
|
|
retval = ra;
|
2007-10-16 12:26:22 +04:00
|
|
|
if (page)
|
|
|
|
page_cache_release(page);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += ret;
|
|
|
|
retval += ret;
|
|
|
|
len -= ret;
|
2008-07-24 08:27:41 +04:00
|
|
|
index += offset >> huge_page_shift(h);
|
|
|
|
offset &= ~huge_page_mask(h);
|
2007-10-16 12:26:22 +04:00
|
|
|
|
|
|
|
if (page)
|
|
|
|
page_cache_release(page);
|
|
|
|
|
|
|
|
/* short read or no more work */
|
|
|
|
if ((ret != nr) || (len == 0))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
out:
|
2008-07-24 08:27:41 +04:00
|
|
|
*ppos = ((loff_t)index << huge_page_shift(h)) + offset;
|
2007-10-16 12:26:22 +04:00
|
|
|
mutex_unlock(&inode->i_mutex);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2007-10-16 12:25:03 +04:00
|
|
|
static int hugetlbfs_write_begin(struct file *file,
|
|
|
|
struct address_space *mapping,
|
|
|
|
loff_t pos, unsigned len, unsigned flags,
|
|
|
|
struct page **pagep, void **fsdata)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2007-10-16 12:25:03 +04:00
|
|
|
static int hugetlbfs_write_end(struct file *file, struct address_space *mapping,
|
|
|
|
loff_t pos, unsigned len, unsigned copied,
|
|
|
|
struct page *page, void *fsdata)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2007-10-16 12:25:03 +04:00
|
|
|
BUG();
|
2005-04-17 02:20:36 +04:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void truncate_huge_page(struct page *page)
|
|
|
|
{
|
VM: Remove "clear_page_dirty()" and "test_clear_page_dirty()" functions
They were horribly easy to mis-use because of their tempting naming, and
they also did way more than any users of them generally wanted them to
do.
A dirty page can become clean under two circumstances:
(a) when we write it out. We have "clear_page_dirty_for_io()" for
this, and that function remains unchanged.
In the "for IO" case it is not sufficient to just clear the dirty
bit, you also have to mark the page as being under writeback etc.
(b) when we actually remove a page due to it becoming inaccessible to
users, notably because it was truncate()'d away or the file (or
metadata) no longer exists, and we thus want to cancel any
outstanding dirty state.
For the (b) case, we now introduce "cancel_dirty_page()", which only
touches the page state itself, and verifies that the page is not mapped
(since cancelling writes on a mapped page would be actively wrong as it
is still accessible to users).
Some filesystems need to be fixed up for this: CIFS, FUSE, JFS,
ReiserFS, XFS all use the old confusing functions, and will be fixed
separately in subsequent commits (with some of them just removing the
offending logic, and others using clear_page_dirty_for_io()).
This was confirmed by Martin Michlmayr to fix the apt database
corruption on ARM.
Cc: Martin Michlmayr <tbm@cyrius.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Hugh Dickins <hugh@veritas.com>
Cc: Nick Piggin <nickpiggin@yahoo.com.au>
Cc: Arjan van de Ven <arjan@infradead.org>
Cc: Andrei Popa <andrei.popa@i-neo.ro>
Cc: Andrew Morton <akpm@osdl.org>
Cc: Dave Kleikamp <shaggy@linux.vnet.ibm.com>
Cc: Gordon Farquharson <gordonfarquharson@gmail.com>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Trond Myklebust <trond.myklebust@fys.uio.no>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-21 00:46:42 +03:00
|
|
|
cancel_dirty_page(page, /* No IO accounting for huge pages? */0);
|
2005-04-17 02:20:36 +04:00
|
|
|
ClearPageUptodate(page);
|
2011-03-23 02:30:54 +03:00
|
|
|
delete_from_page_cache(page);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
[PATCH] hugepage: Strict page reservation for hugepage inodes
These days, hugepages are demand-allocated at first fault time. There's a
somewhat dubious (and racy) heuristic when making a new mmap() to check if
there are enough available hugepages to fully satisfy that mapping.
A particularly obvious case where the heuristic breaks down is where a
process maps its hugepages not as a single chunk, but as a bunch of
individually mmap()ed (or shmat()ed) blocks without touching and
instantiating the pages in between allocations. In this case the size of
each block is compared against the total number of available hugepages.
It's thus easy for the process to become overcommitted, because each block
mapping will succeed, although the total number of hugepages required by
all blocks exceeds the number available. In particular, this defeats such
a program which will detect a mapping failure and adjust its hugepage usage
downward accordingly.
The patch below addresses this problem, by strictly reserving a number of
physical hugepages for hugepage inodes which have been mapped, but not
instatiated. MAP_SHARED mappings are thus "safe" - they will fail on
mmap(), not later with an OOM SIGKILL. MAP_PRIVATE mappings can still
trigger an OOM. (Actually SHARED mappings can technically still OOM, but
only if the sysadmin explicitly reduces the hugepage pool between mapping
and instantiation)
This patch appears to address the problem at hand - it allows DB2 to start
correctly, for instance, which previously suffered the failure described
above.
This patch causes no regressions on the libhugetblfs testsuite, and makes a
test (designed to catch this problem) pass which previously failed (ppc64,
POWER5).
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-22 11:08:55 +03:00
|
|
|
static void truncate_hugepages(struct inode *inode, loff_t lstart)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-07-24 08:27:41 +04:00
|
|
|
struct hstate *h = hstate_inode(inode);
|
[PATCH] hugepage: Strict page reservation for hugepage inodes
These days, hugepages are demand-allocated at first fault time. There's a
somewhat dubious (and racy) heuristic when making a new mmap() to check if
there are enough available hugepages to fully satisfy that mapping.
A particularly obvious case where the heuristic breaks down is where a
process maps its hugepages not as a single chunk, but as a bunch of
individually mmap()ed (or shmat()ed) blocks without touching and
instantiating the pages in between allocations. In this case the size of
each block is compared against the total number of available hugepages.
It's thus easy for the process to become overcommitted, because each block
mapping will succeed, although the total number of hugepages required by
all blocks exceeds the number available. In particular, this defeats such
a program which will detect a mapping failure and adjust its hugepage usage
downward accordingly.
The patch below addresses this problem, by strictly reserving a number of
physical hugepages for hugepage inodes which have been mapped, but not
instatiated. MAP_SHARED mappings are thus "safe" - they will fail on
mmap(), not later with an OOM SIGKILL. MAP_PRIVATE mappings can still
trigger an OOM. (Actually SHARED mappings can technically still OOM, but
only if the sysadmin explicitly reduces the hugepage pool between mapping
and instantiation)
This patch appears to address the problem at hand - it allows DB2 to start
correctly, for instance, which previously suffered the failure described
above.
This patch causes no regressions on the libhugetblfs testsuite, and makes a
test (designed to catch this problem) pass which previously failed (ppc64,
POWER5).
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-22 11:08:55 +03:00
|
|
|
struct address_space *mapping = &inode->i_data;
|
2008-07-24 08:27:41 +04:00
|
|
|
const pgoff_t start = lstart >> huge_page_shift(h);
|
2005-04-17 02:20:36 +04:00
|
|
|
struct pagevec pvec;
|
|
|
|
pgoff_t next;
|
2006-06-23 13:03:15 +04:00
|
|
|
int i, freed = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
pagevec_init(&pvec, 0);
|
|
|
|
next = start;
|
|
|
|
while (1) {
|
|
|
|
if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
|
|
|
|
if (next == start)
|
|
|
|
break;
|
|
|
|
next = start;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < pagevec_count(&pvec); ++i) {
|
|
|
|
struct page *page = pvec.pages[i];
|
|
|
|
|
|
|
|
lock_page(page);
|
|
|
|
if (page->index > next)
|
|
|
|
next = page->index;
|
|
|
|
++next;
|
|
|
|
truncate_huge_page(page);
|
|
|
|
unlock_page(page);
|
2006-06-23 13:03:15 +04:00
|
|
|
freed++;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
huge_pagevec_release(&pvec);
|
|
|
|
}
|
|
|
|
BUG_ON(!lstart && mapping->nrpages);
|
2006-06-23 13:03:15 +04:00
|
|
|
hugetlb_unreserve_pages(inode, start, freed);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2010-06-05 03:52:12 +04:00
|
|
|
static void hugetlbfs_evict_inode(struct inode *inode)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
[PATCH] hugepage: Strict page reservation for hugepage inodes
These days, hugepages are demand-allocated at first fault time. There's a
somewhat dubious (and racy) heuristic when making a new mmap() to check if
there are enough available hugepages to fully satisfy that mapping.
A particularly obvious case where the heuristic breaks down is where a
process maps its hugepages not as a single chunk, but as a bunch of
individually mmap()ed (or shmat()ed) blocks without touching and
instantiating the pages in between allocations. In this case the size of
each block is compared against the total number of available hugepages.
It's thus easy for the process to become overcommitted, because each block
mapping will succeed, although the total number of hugepages required by
all blocks exceeds the number available. In particular, this defeats such
a program which will detect a mapping failure and adjust its hugepage usage
downward accordingly.
The patch below addresses this problem, by strictly reserving a number of
physical hugepages for hugepage inodes which have been mapped, but not
instatiated. MAP_SHARED mappings are thus "safe" - they will fail on
mmap(), not later with an OOM SIGKILL. MAP_PRIVATE mappings can still
trigger an OOM. (Actually SHARED mappings can technically still OOM, but
only if the sysadmin explicitly reduces the hugepage pool between mapping
and instantiation)
This patch appears to address the problem at hand - it allows DB2 to start
correctly, for instance, which previously suffered the failure described
above.
This patch causes no regressions on the libhugetblfs testsuite, and makes a
test (designed to catch this problem) pass which previously failed (ppc64,
POWER5).
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-22 11:08:55 +03:00
|
|
|
truncate_hugepages(inode, 0);
|
2010-06-05 04:55:25 +04:00
|
|
|
end_writeback(inode);
|
2005-10-30 04:16:43 +03:00
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
static inline void
|
2006-10-28 21:38:43 +04:00
|
|
|
hugetlb_vmtruncate_list(struct prio_tree_root *root, pgoff_t pgoff)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct vm_area_struct *vma;
|
|
|
|
struct prio_tree_iter iter;
|
|
|
|
|
2006-10-28 21:38:43 +04:00
|
|
|
vma_prio_tree_foreach(vma, &iter, root, pgoff, ULONG_MAX) {
|
2005-04-17 02:20:36 +04:00
|
|
|
unsigned long v_offset;
|
|
|
|
|
|
|
|
/*
|
2006-10-28 21:38:43 +04:00
|
|
|
* Can the expression below overflow on 32-bit arches?
|
|
|
|
* No, because the prio_tree returns us only those vmas
|
|
|
|
* which overlap the truncated area starting at pgoff,
|
|
|
|
* and no vma on a 32-bit arch can span beyond the 4GB.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2006-10-28 21:38:43 +04:00
|
|
|
if (vma->vm_pgoff < pgoff)
|
|
|
|
v_offset = (pgoff - vma->vm_pgoff) << PAGE_SHIFT;
|
|
|
|
else
|
2005-04-17 02:20:36 +04:00
|
|
|
v_offset = 0;
|
|
|
|
|
2006-10-11 12:20:46 +04:00
|
|
|
__unmap_hugepage_range(vma,
|
hugetlb: guarantee that COW faults for a process that called mmap(MAP_PRIVATE) on hugetlbfs will succeed
After patch 2 in this series, a process that successfully calls mmap() for
a MAP_PRIVATE mapping will be guaranteed to successfully fault until a
process calls fork(). At that point, the next write fault from the parent
could fail due to COW if the child still has a reference.
We only reserve pages for the parent but a copy must be made to avoid
leaking data from the parent to the child after fork(). Reserves could be
taken for both parent and child at fork time to guarantee faults but if
the mapping is large it is highly likely we will not have sufficient pages
for the reservation, and it is common to fork only to exec() immediatly
after. A failure here would be very undesirable.
Note that the current behaviour of mainline with MAP_PRIVATE pages is
pretty bad. The following situation is allowed to occur today.
1. Process calls mmap(MAP_PRIVATE)
2. Process calls mlock() to fault all pages and makes sure it succeeds
3. Process forks()
4. Process writes to MAP_PRIVATE mapping while child still exists
5. If the COW fails at this point, the process gets SIGKILLed even though it
had taken care to ensure the pages existed
This patch improves the situation by guaranteeing the reliability of the
process that successfully calls mmap(). When the parent performs COW, it
will try to satisfy the allocation without using reserves. If that fails
the parent will steal the page leaving any children without a page.
Faults from the child after that point will result in failure. If the
child COW happens first, an attempt will be made to allocate the page
without reserves and the child will get SIGKILLed on failure.
To summarise the new behaviour:
1. If the original mapper performs COW on a private mapping with multiple
references, it will attempt to allocate a hugepage from the pool or
the buddy allocator without using the existing reserves. On fail, VMAs
mapping the same area are traversed and the page being COW'd is unmapped
where found. It will then steal the original page as the last mapper in
the normal way.
2. The VMAs the pages were unmapped from are flagged to note that pages
with data no longer exist. Future no-page faults on those VMAs will
terminate the process as otherwise it would appear that data was corrupted.
A warning is printed to the console that this situation occured.
2. If the child performs COW first, it will attempt to satisfy the COW
from the pool if there are enough pages or via the buddy allocator if
overcommit is allowed and the buddy allocator can satisfy the request. If
it fails, the child will be killed.
If the pool is large enough, existing applications will not notice that
the reserves were a factor. Existing applications depending on the
no-reserves been set are unlikely to exist as for much of the history of
hugetlbfs, pages were prefaulted at mmap(), allocating the pages at that
point or failing the mmap().
[npiggin@suse.de: fix CONFIG_HUGETLB=n build]
Signed-off-by: Mel Gorman <mel@csn.ul.ie>
Acked-by: Adam Litke <agl@us.ibm.com>
Cc: Andy Whitcroft <apw@shadowen.org>
Cc: William Lee Irwin III <wli@holomorphy.com>
Cc: Hugh Dickins <hugh@veritas.com>
Cc: Nick Piggin <npiggin@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-24 08:27:25 +04:00
|
|
|
vma->vm_start + v_offset, vma->vm_end, NULL);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hugetlb_vmtruncate(struct inode *inode, loff_t offset)
|
|
|
|
{
|
2006-10-28 21:38:43 +04:00
|
|
|
pgoff_t pgoff;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct address_space *mapping = inode->i_mapping;
|
2008-07-24 08:27:41 +04:00
|
|
|
struct hstate *h = hstate_inode(inode);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-07-24 08:27:41 +04:00
|
|
|
BUG_ON(offset & ~huge_page_mask(h));
|
2006-10-28 21:38:43 +04:00
|
|
|
pgoff = offset >> PAGE_SHIFT;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-10-16 12:26:21 +04:00
|
|
|
i_size_write(inode, offset);
|
2011-05-25 04:12:06 +04:00
|
|
|
mutex_lock(&mapping->i_mmap_mutex);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (!prio_tree_empty(&mapping->i_mmap))
|
|
|
|
hugetlb_vmtruncate_list(&mapping->i_mmap, pgoff);
|
2011-05-25 04:12:06 +04:00
|
|
|
mutex_unlock(&mapping->i_mmap_mutex);
|
[PATCH] hugepage: Strict page reservation for hugepage inodes
These days, hugepages are demand-allocated at first fault time. There's a
somewhat dubious (and racy) heuristic when making a new mmap() to check if
there are enough available hugepages to fully satisfy that mapping.
A particularly obvious case where the heuristic breaks down is where a
process maps its hugepages not as a single chunk, but as a bunch of
individually mmap()ed (or shmat()ed) blocks without touching and
instantiating the pages in between allocations. In this case the size of
each block is compared against the total number of available hugepages.
It's thus easy for the process to become overcommitted, because each block
mapping will succeed, although the total number of hugepages required by
all blocks exceeds the number available. In particular, this defeats such
a program which will detect a mapping failure and adjust its hugepage usage
downward accordingly.
The patch below addresses this problem, by strictly reserving a number of
physical hugepages for hugepage inodes which have been mapped, but not
instatiated. MAP_SHARED mappings are thus "safe" - they will fail on
mmap(), not later with an OOM SIGKILL. MAP_PRIVATE mappings can still
trigger an OOM. (Actually SHARED mappings can technically still OOM, but
only if the sysadmin explicitly reduces the hugepage pool between mapping
and instantiation)
This patch appears to address the problem at hand - it allows DB2 to start
correctly, for instance, which previously suffered the failure described
above.
This patch causes no regressions on the libhugetblfs testsuite, and makes a
test (designed to catch this problem) pass which previously failed (ppc64,
POWER5).
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-22 11:08:55 +03:00
|
|
|
truncate_hugepages(inode, offset);
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr)
|
|
|
|
{
|
|
|
|
struct inode *inode = dentry->d_inode;
|
2008-07-24 08:27:41 +04:00
|
|
|
struct hstate *h = hstate_inode(inode);
|
2005-04-17 02:20:36 +04:00
|
|
|
int error;
|
|
|
|
unsigned int ia_valid = attr->ia_valid;
|
|
|
|
|
|
|
|
BUG_ON(!inode);
|
|
|
|
|
|
|
|
error = inode_change_ok(inode, attr);
|
|
|
|
if (error)
|
2010-06-04 13:30:02 +04:00
|
|
|
return error;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (ia_valid & ATTR_SIZE) {
|
|
|
|
error = -EINVAL;
|
2010-06-04 13:30:02 +04:00
|
|
|
if (attr->ia_size & ~huge_page_mask(h))
|
|
|
|
return -EINVAL;
|
|
|
|
error = hugetlb_vmtruncate(inode, attr->ia_size);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (error)
|
2010-06-04 13:30:02 +04:00
|
|
|
return error;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2010-06-04 13:30:02 +04:00
|
|
|
|
|
|
|
setattr_copy(inode, attr);
|
|
|
|
mark_inode_dirty(inode);
|
|
|
|
return 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct inode *hugetlbfs_get_inode(struct super_block *sb, uid_t uid,
|
|
|
|
gid_t gid, int mode, dev_t dev)
|
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
|
|
|
|
inode = new_inode(sb);
|
|
|
|
if (inode) {
|
|
|
|
struct hugetlbfs_inode_info *info;
|
2010-10-23 19:19:54 +04:00
|
|
|
inode->i_ino = get_next_ino();
|
2005-04-17 02:20:36 +04:00
|
|
|
inode->i_mode = mode;
|
|
|
|
inode->i_uid = uid;
|
|
|
|
inode->i_gid = gid;
|
|
|
|
inode->i_mapping->a_ops = &hugetlbfs_aops;
|
|
|
|
inode->i_mapping->backing_dev_info =&hugetlbfs_backing_dev_info;
|
|
|
|
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
2006-06-23 13:03:15 +04:00
|
|
|
INIT_LIST_HEAD(&inode->i_mapping->private_list);
|
2005-04-17 02:20:36 +04:00
|
|
|
info = HUGETLBFS_I(inode);
|
2009-09-22 04:03:43 +04:00
|
|
|
/*
|
|
|
|
* The policy is initialized here even if we are creating a
|
|
|
|
* private inode because initialization simply creates an
|
|
|
|
* an empty rb tree and calls spin_lock_init(), later when we
|
|
|
|
* call mpol_free_shared_policy() it will just return because
|
|
|
|
* the rb tree will still be empty.
|
|
|
|
*/
|
mempolicy: use struct mempolicy pointer in shmem_sb_info
This patch replaces the mempolicy mode, mode_flags, and nodemask in the
shmem_sb_info struct with a struct mempolicy pointer, initialized to NULL.
This removes dependency on the details of mempolicy from shmem.c and hugetlbfs
inode.c and simplifies the interfaces.
mpol_parse_str() in mempolicy.c is changed to return, via a pointer to a
pointer arg, a struct mempolicy pointer on success. For MPOL_DEFAULT, the
returned pointer is NULL. Further, mpol_parse_str() now takes a 'no_context'
argument that causes the input nodemask to be stored in the w.user_nodemask of
the created mempolicy for use when the mempolicy is installed in a tmpfs inode
shared policy tree. At that time, any cpuset contextualization is applied to
the original input nodemask. This preserves the previous behavior where the
input nodemask was stored in the superblock. We can think of the returned
mempolicy as "context free".
Because mpol_parse_str() is now calling mpol_new(), we can remove from
mpol_to_str() the semantic checks that mpol_new() already performs.
Add 'no_context' parameter to mpol_to_str() to specify that it should format
the nodemask in w.user_nodemask for 'bind' and 'interleave' policies.
Change mpol_shared_policy_init() to take a pointer to a "context free" struct
mempolicy and to create a new, "contextualized" mempolicy using the mode,
mode_flags and user_nodemask from the input mempolicy.
Note: we know that the mempolicy passed to mpol_to_str() or
mpol_shared_policy_init() from a tmpfs superblock is "context free". This
is currently the only instance thereof. However, if we found more uses for
this concept, and introduced any ambiguity as to whether a mempolicy was
context free or not, we could add another internal mode flag to identify
context free mempolicies. Then, we could remove the 'no_context' argument
from mpol_to_str().
Added shmem_get_sbmpol() to return a reference counted superblock mempolicy,
if one exists, to pass to mpol_shared_policy_init(). We must add the
reference under the sb stat_lock to prevent races with replacement of the mpol
by remount. This reference is removed in mpol_shared_policy_init().
[akpm@linux-foundation.org: build fix]
[akpm@linux-foundation.org: another build fix]
[akpm@linux-foundation.org: yet another build fix]
Signed-off-by: Lee Schermerhorn <lee.schermerhorn@hp.com>
Cc: Christoph Lameter <clameter@sgi.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Mel Gorman <mel@csn.ul.ie>
Cc: Andi Kleen <ak@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-04-28 13:13:26 +04:00
|
|
|
mpol_shared_policy_init(&info->policy, NULL);
|
2005-04-17 02:20:36 +04:00
|
|
|
switch (mode & S_IFMT) {
|
|
|
|
default:
|
|
|
|
init_special_inode(inode, mode, dev);
|
|
|
|
break;
|
|
|
|
case S_IFREG:
|
|
|
|
inode->i_op = &hugetlbfs_inode_operations;
|
|
|
|
inode->i_fop = &hugetlbfs_file_operations;
|
|
|
|
break;
|
|
|
|
case S_IFDIR:
|
|
|
|
inode->i_op = &hugetlbfs_dir_inode_operations;
|
|
|
|
inode->i_fop = &simple_dir_operations;
|
|
|
|
|
|
|
|
/* directory inodes start off with i_nlink == 2 (for "." entry) */
|
2006-10-01 10:29:04 +04:00
|
|
|
inc_nlink(inode);
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
|
|
|
case S_IFLNK:
|
|
|
|
inode->i_op = &page_symlink_inode_operations;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return inode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* File creation. Allocate an inode, and we're done..
|
|
|
|
*/
|
|
|
|
static int hugetlbfs_mknod(struct inode *dir,
|
|
|
|
struct dentry *dentry, int mode, dev_t dev)
|
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
int error = -ENOSPC;
|
|
|
|
gid_t gid;
|
|
|
|
|
|
|
|
if (dir->i_mode & S_ISGID) {
|
|
|
|
gid = dir->i_gid;
|
|
|
|
if (S_ISDIR(mode))
|
|
|
|
mode |= S_ISGID;
|
|
|
|
} else {
|
2008-11-14 02:38:56 +03:00
|
|
|
gid = current_fsgid();
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2008-11-14 02:38:56 +03:00
|
|
|
inode = hugetlbfs_get_inode(dir->i_sb, current_fsuid(), gid, mode, dev);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (inode) {
|
|
|
|
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
|
|
|
|
d_instantiate(dentry, inode);
|
|
|
|
dget(dentry); /* Extra count - pin the dentry in core */
|
|
|
|
error = 0;
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hugetlbfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
|
|
|
{
|
|
|
|
int retval = hugetlbfs_mknod(dir, dentry, mode | S_IFDIR, 0);
|
|
|
|
if (!retval)
|
2006-10-01 10:29:04 +04:00
|
|
|
inc_nlink(dir);
|
2005-04-17 02:20:36 +04:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hugetlbfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
|
|
|
|
{
|
|
|
|
return hugetlbfs_mknod(dir, dentry, mode | S_IFREG, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hugetlbfs_symlink(struct inode *dir,
|
|
|
|
struct dentry *dentry, const char *symname)
|
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
int error = -ENOSPC;
|
|
|
|
gid_t gid;
|
|
|
|
|
|
|
|
if (dir->i_mode & S_ISGID)
|
|
|
|
gid = dir->i_gid;
|
|
|
|
else
|
2008-11-14 02:38:56 +03:00
|
|
|
gid = current_fsgid();
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-11-14 02:38:56 +03:00
|
|
|
inode = hugetlbfs_get_inode(dir->i_sb, current_fsuid(),
|
2005-04-17 02:20:36 +04:00
|
|
|
gid, S_IFLNK|S_IRWXUGO, 0);
|
|
|
|
if (inode) {
|
|
|
|
int l = strlen(symname)+1;
|
|
|
|
error = page_symlink(inode, symname, l);
|
|
|
|
if (!error) {
|
|
|
|
d_instantiate(dentry, inode);
|
|
|
|
dget(dentry);
|
|
|
|
} else
|
|
|
|
iput(inode);
|
|
|
|
}
|
|
|
|
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2007-02-09 01:20:27 +03:00
|
|
|
* mark the head page dirty
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
|
|
|
static int hugetlbfs_set_page_dirty(struct page *page)
|
|
|
|
{
|
2007-05-07 01:49:39 +04:00
|
|
|
struct page *head = compound_head(page);
|
2007-02-09 01:20:27 +03:00
|
|
|
|
|
|
|
SetPageDirty(head);
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-09-08 05:19:35 +04:00
|
|
|
static int hugetlbfs_migrate_page(struct address_space *mapping,
|
|
|
|
struct page *newpage, struct page *page)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = migrate_huge_page_move_mapping(mapping, newpage, page);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
migrate_page_copy(newpage, page);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-06-23 13:02:58 +04:00
|
|
|
static int hugetlbfs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2006-06-23 13:02:58 +04:00
|
|
|
struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(dentry->d_sb);
|
2008-07-24 08:27:41 +04:00
|
|
|
struct hstate *h = hstate_inode(dentry->d_inode);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
buf->f_type = HUGETLBFS_MAGIC;
|
2008-07-24 08:27:41 +04:00
|
|
|
buf->f_bsize = huge_page_size(h);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (sbinfo) {
|
|
|
|
spin_lock(&sbinfo->stat_lock);
|
2005-11-22 08:32:24 +03:00
|
|
|
/* If no limits set, just report 0 for max/free/used
|
|
|
|
* blocks, like simple_statfs() */
|
|
|
|
if (sbinfo->max_blocks >= 0) {
|
|
|
|
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;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
spin_unlock(&sbinfo->stat_lock);
|
|
|
|
}
|
|
|
|
buf->f_namelen = NAME_MAX;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hugetlbfs_put_super(struct super_block *sb)
|
|
|
|
{
|
|
|
|
struct hugetlbfs_sb_info *sbi = HUGETLBFS_SB(sb);
|
|
|
|
|
|
|
|
if (sbi) {
|
|
|
|
sb->s_fs_info = NULL;
|
|
|
|
kfree(sbi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-10-30 04:16:42 +03:00
|
|
|
static inline int hugetlbfs_dec_free_inodes(struct hugetlbfs_sb_info *sbinfo)
|
|
|
|
{
|
|
|
|
if (sbinfo->free_inodes >= 0) {
|
|
|
|
spin_lock(&sbinfo->stat_lock);
|
|
|
|
if (unlikely(!sbinfo->free_inodes)) {
|
|
|
|
spin_unlock(&sbinfo->stat_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
sbinfo->free_inodes--;
|
|
|
|
spin_unlock(&sbinfo->stat_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hugetlbfs_inc_free_inodes(struct hugetlbfs_sb_info *sbinfo)
|
|
|
|
{
|
|
|
|
if (sbinfo->free_inodes >= 0) {
|
|
|
|
spin_lock(&sbinfo->stat_lock);
|
|
|
|
sbinfo->free_inodes++;
|
|
|
|
spin_unlock(&sbinfo->stat_lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-12-07 07:33:20 +03:00
|
|
|
static struct kmem_cache *hugetlbfs_inode_cachep;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
static struct inode *hugetlbfs_alloc_inode(struct super_block *sb)
|
|
|
|
{
|
2005-10-30 04:16:42 +03:00
|
|
|
struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(sb);
|
2005-04-17 02:20:36 +04:00
|
|
|
struct hugetlbfs_inode_info *p;
|
|
|
|
|
2005-10-30 04:16:42 +03:00
|
|
|
if (unlikely(!hugetlbfs_dec_free_inodes(sbinfo)))
|
|
|
|
return NULL;
|
2006-12-07 07:33:17 +03:00
|
|
|
p = kmem_cache_alloc(hugetlbfs_inode_cachep, GFP_KERNEL);
|
2005-10-30 04:16:42 +03:00
|
|
|
if (unlikely(!p)) {
|
|
|
|
hugetlbfs_inc_free_inodes(sbinfo);
|
2005-04-17 02:20:36 +04:00
|
|
|
return NULL;
|
2005-10-30 04:16:42 +03:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
return &p->vfs_inode;
|
|
|
|
}
|
|
|
|
|
2011-01-07 09:49:49 +03:00
|
|
|
static void hugetlbfs_i_callback(struct rcu_head *head)
|
|
|
|
{
|
|
|
|
struct inode *inode = container_of(head, struct inode, i_rcu);
|
|
|
|
INIT_LIST_HEAD(&inode->i_dentry);
|
|
|
|
kmem_cache_free(hugetlbfs_inode_cachep, HUGETLBFS_I(inode));
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
static void hugetlbfs_destroy_inode(struct inode *inode)
|
|
|
|
{
|
2005-10-30 04:16:42 +03:00
|
|
|
hugetlbfs_inc_free_inodes(HUGETLBFS_SB(inode->i_sb));
|
2005-04-17 02:20:36 +04:00
|
|
|
mpol_free_shared_policy(&HUGETLBFS_I(inode)->policy);
|
2011-01-07 09:49:49 +03:00
|
|
|
call_rcu(&inode->i_rcu, hugetlbfs_i_callback);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2006-06-28 15:26:44 +04:00
|
|
|
static const struct address_space_operations hugetlbfs_aops = {
|
2007-10-16 12:25:03 +04:00
|
|
|
.write_begin = hugetlbfs_write_begin,
|
|
|
|
.write_end = hugetlbfs_write_end,
|
2005-04-17 02:20:36 +04:00
|
|
|
.set_page_dirty = hugetlbfs_set_page_dirty,
|
2010-09-08 05:19:35 +04:00
|
|
|
.migratepage = hugetlbfs_migrate_page,
|
2005-04-17 02:20:36 +04:00
|
|
|
};
|
|
|
|
|
2005-10-30 04:16:42 +03:00
|
|
|
|
2008-07-26 06:45:34 +04:00
|
|
|
static void init_once(void *foo)
|
2005-10-30 04:16:42 +03:00
|
|
|
{
|
|
|
|
struct hugetlbfs_inode_info *ei = (struct hugetlbfs_inode_info *)foo;
|
|
|
|
|
2007-05-17 09:10:57 +04:00
|
|
|
inode_init_once(&ei->vfs_inode);
|
2005-10-30 04:16:42 +03:00
|
|
|
}
|
|
|
|
|
2006-03-28 13:56:42 +04:00
|
|
|
const struct file_operations hugetlbfs_file_operations = {
|
2007-10-16 12:26:22 +04:00
|
|
|
.read = hugetlbfs_read,
|
2005-04-17 02:20:36 +04:00
|
|
|
.mmap = hugetlbfs_file_mmap,
|
2010-05-26 19:53:41 +04:00
|
|
|
.fsync = noop_fsync,
|
2005-04-17 02:20:36 +04:00
|
|
|
.get_unmapped_area = hugetlb_get_unmapped_area,
|
llseek: automatically add .llseek fop
All file_operations should get a .llseek operation so we can make
nonseekable_open the default for future file operations without a
.llseek pointer.
The three cases that we can automatically detect are no_llseek, seq_lseek
and default_llseek. For cases where we can we can automatically prove that
the file offset is always ignored, we use noop_llseek, which maintains
the current behavior of not returning an error from a seek.
New drivers should normally not use noop_llseek but instead use no_llseek
and call nonseekable_open at open time. Existing drivers can be converted
to do the same when the maintainer knows for certain that no user code
relies on calling seek on the device file.
The generated code is often incorrectly indented and right now contains
comments that clarify for each added line why a specific variant was
chosen. In the version that gets submitted upstream, the comments will
be gone and I will manually fix the indentation, because there does not
seem to be a way to do that using coccinelle.
Some amount of new code is currently sitting in linux-next that should get
the same modifications, which I will do at the end of the merge window.
Many thanks to Julia Lawall for helping me learn to write a semantic
patch that does all this.
===== begin semantic patch =====
// This adds an llseek= method to all file operations,
// as a preparation for making no_llseek the default.
//
// The rules are
// - use no_llseek explicitly if we do nonseekable_open
// - use seq_lseek for sequential files
// - use default_llseek if we know we access f_pos
// - use noop_llseek if we know we don't access f_pos,
// but we still want to allow users to call lseek
//
@ open1 exists @
identifier nested_open;
@@
nested_open(...)
{
<+...
nonseekable_open(...)
...+>
}
@ open exists@
identifier open_f;
identifier i, f;
identifier open1.nested_open;
@@
int open_f(struct inode *i, struct file *f)
{
<+...
(
nonseekable_open(...)
|
nested_open(...)
)
...+>
}
@ read disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ read_no_fpos disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
... when != off
}
@ write @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ write_no_fpos @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
... when != off
}
@ fops0 @
identifier fops;
@@
struct file_operations fops = {
...
};
@ has_llseek depends on fops0 @
identifier fops0.fops;
identifier llseek_f;
@@
struct file_operations fops = {
...
.llseek = llseek_f,
...
};
@ has_read depends on fops0 @
identifier fops0.fops;
identifier read_f;
@@
struct file_operations fops = {
...
.read = read_f,
...
};
@ has_write depends on fops0 @
identifier fops0.fops;
identifier write_f;
@@
struct file_operations fops = {
...
.write = write_f,
...
};
@ has_open depends on fops0 @
identifier fops0.fops;
identifier open_f;
@@
struct file_operations fops = {
...
.open = open_f,
...
};
// use no_llseek if we call nonseekable_open
////////////////////////////////////////////
@ nonseekable1 depends on !has_llseek && has_open @
identifier fops0.fops;
identifier nso ~= "nonseekable_open";
@@
struct file_operations fops = {
... .open = nso, ...
+.llseek = no_llseek, /* nonseekable */
};
@ nonseekable2 depends on !has_llseek @
identifier fops0.fops;
identifier open.open_f;
@@
struct file_operations fops = {
... .open = open_f, ...
+.llseek = no_llseek, /* open uses nonseekable */
};
// use seq_lseek for sequential files
/////////////////////////////////////
@ seq depends on !has_llseek @
identifier fops0.fops;
identifier sr ~= "seq_read";
@@
struct file_operations fops = {
... .read = sr, ...
+.llseek = seq_lseek, /* we have seq_read */
};
// use default_llseek if there is a readdir
///////////////////////////////////////////
@ fops1 depends on !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier readdir_e;
@@
// any other fop is used that changes pos
struct file_operations fops = {
... .readdir = readdir_e, ...
+.llseek = default_llseek, /* readdir is present */
};
// use default_llseek if at least one of read/write touches f_pos
/////////////////////////////////////////////////////////////////
@ fops2 depends on !fops1 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read.read_f;
@@
// read fops use offset
struct file_operations fops = {
... .read = read_f, ...
+.llseek = default_llseek, /* read accesses f_pos */
};
@ fops3 depends on !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write.write_f;
@@
// write fops use offset
struct file_operations fops = {
... .write = write_f, ...
+ .llseek = default_llseek, /* write accesses f_pos */
};
// Use noop_llseek if neither read nor write accesses f_pos
///////////////////////////////////////////////////////////
@ fops4 depends on !fops1 && !fops2 && !fops3 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
identifier write_no_fpos.write_f;
@@
// write fops use offset
struct file_operations fops = {
...
.write = write_f,
.read = read_f,
...
+.llseek = noop_llseek, /* read and write both use no f_pos */
};
@ depends on has_write && !has_read && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write_no_fpos.write_f;
@@
struct file_operations fops = {
... .write = write_f, ...
+.llseek = noop_llseek, /* write uses no f_pos */
};
@ depends on has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
@@
struct file_operations fops = {
... .read = read_f, ...
+.llseek = noop_llseek, /* read uses no f_pos */
};
@ depends on !has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
@@
struct file_operations fops = {
...
+.llseek = noop_llseek, /* no read or write fn */
};
===== End semantic patch =====
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Julia Lawall <julia@diku.dk>
Cc: Christoph Hellwig <hch@infradead.org>
2010-08-15 20:52:59 +04:00
|
|
|
.llseek = default_llseek,
|
2005-04-17 02:20:36 +04:00
|
|
|
};
|
|
|
|
|
2007-02-12 11:55:39 +03:00
|
|
|
static const struct inode_operations hugetlbfs_dir_inode_operations = {
|
2005-04-17 02:20:36 +04:00
|
|
|
.create = hugetlbfs_create,
|
|
|
|
.lookup = simple_lookup,
|
|
|
|
.link = simple_link,
|
|
|
|
.unlink = simple_unlink,
|
|
|
|
.symlink = hugetlbfs_symlink,
|
|
|
|
.mkdir = hugetlbfs_mkdir,
|
|
|
|
.rmdir = simple_rmdir,
|
|
|
|
.mknod = hugetlbfs_mknod,
|
|
|
|
.rename = simple_rename,
|
|
|
|
.setattr = hugetlbfs_setattr,
|
|
|
|
};
|
|
|
|
|
2007-02-12 11:55:39 +03:00
|
|
|
static const struct inode_operations hugetlbfs_inode_operations = {
|
2005-04-17 02:20:36 +04:00
|
|
|
.setattr = hugetlbfs_setattr,
|
|
|
|
};
|
|
|
|
|
2007-02-12 11:55:41 +03:00
|
|
|
static const struct super_operations hugetlbfs_ops = {
|
2005-04-17 02:20:36 +04:00
|
|
|
.alloc_inode = hugetlbfs_alloc_inode,
|
|
|
|
.destroy_inode = hugetlbfs_destroy_inode,
|
2010-06-05 03:52:12 +04:00
|
|
|
.evict_inode = hugetlbfs_evict_inode,
|
2005-04-17 02:20:36 +04:00
|
|
|
.statfs = hugetlbfs_statfs,
|
|
|
|
.put_super = hugetlbfs_put_super,
|
2008-02-08 15:21:45 +03:00
|
|
|
.show_options = generic_show_options,
|
2005-04-17 02:20:36 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
hugetlbfs_parse_options(char *options, struct hugetlbfs_config *pconfig)
|
|
|
|
{
|
2007-07-16 10:40:52 +04:00
|
|
|
char *p, *rest;
|
|
|
|
substring_t args[MAX_OPT_ARGS];
|
|
|
|
int option;
|
2008-07-24 08:27:43 +04:00
|
|
|
unsigned long long size = 0;
|
|
|
|
enum { NO_SIZE, SIZE_STD, SIZE_PERCENT } setsize = NO_SIZE;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (!options)
|
|
|
|
return 0;
|
|
|
|
|
2007-07-16 10:40:52 +04:00
|
|
|
while ((p = strsep(&options, ",")) != NULL) {
|
|
|
|
int token;
|
2007-07-16 10:40:54 +04:00
|
|
|
if (!*p)
|
|
|
|
continue;
|
2007-07-16 10:40:52 +04:00
|
|
|
|
|
|
|
token = match_token(p, tokens, args);
|
|
|
|
switch (token) {
|
|
|
|
case Opt_uid:
|
|
|
|
if (match_int(&args[0], &option))
|
|
|
|
goto bad_val;
|
|
|
|
pconfig->uid = option;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Opt_gid:
|
|
|
|
if (match_int(&args[0], &option))
|
|
|
|
goto bad_val;
|
|
|
|
pconfig->gid = option;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Opt_mode:
|
|
|
|
if (match_octal(&args[0], &option))
|
|
|
|
goto bad_val;
|
2008-02-05 09:28:36 +03:00
|
|
|
pconfig->mode = option & 01777U;
|
2007-07-16 10:40:52 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Opt_size: {
|
|
|
|
/* memparse() will accept a K/M/G without a digit */
|
|
|
|
if (!isdigit(*args[0].from))
|
|
|
|
goto bad_val;
|
|
|
|
size = memparse(args[0].from, &rest);
|
2008-07-24 08:27:43 +04:00
|
|
|
setsize = SIZE_STD;
|
|
|
|
if (*rest == '%')
|
|
|
|
setsize = SIZE_PERCENT;
|
2007-07-16 10:40:52 +04:00
|
|
|
break;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-07-16 10:40:52 +04:00
|
|
|
case Opt_nr_inodes:
|
|
|
|
/* memparse() will accept a K/M/G without a digit */
|
|
|
|
if (!isdigit(*args[0].from))
|
|
|
|
goto bad_val;
|
|
|
|
pconfig->nr_inodes = memparse(args[0].from, &rest);
|
|
|
|
break;
|
|
|
|
|
2008-07-24 08:27:43 +04:00
|
|
|
case Opt_pagesize: {
|
|
|
|
unsigned long ps;
|
|
|
|
ps = memparse(args[0].from, &rest);
|
|
|
|
pconfig->hstate = size_to_hstate(ps);
|
|
|
|
if (!pconfig->hstate) {
|
|
|
|
printk(KERN_ERR
|
|
|
|
"hugetlbfs: Unsupported page size %lu MB\n",
|
|
|
|
ps >> 20);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-07-16 10:40:52 +04:00
|
|
|
default:
|
2007-07-16 10:40:54 +04:00
|
|
|
printk(KERN_ERR "hugetlbfs: Bad mount option: \"%s\"\n",
|
|
|
|
p);
|
|
|
|
return -EINVAL;
|
2007-07-16 10:40:52 +04:00
|
|
|
break;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2008-07-24 08:27:43 +04:00
|
|
|
|
|
|
|
/* Do size after hstate is set up */
|
|
|
|
if (setsize > NO_SIZE) {
|
|
|
|
struct hstate *h = pconfig->hstate;
|
|
|
|
if (setsize == SIZE_PERCENT) {
|
|
|
|
size <<= huge_page_shift(h);
|
|
|
|
size *= h->max_huge_pages;
|
|
|
|
do_div(size, 100);
|
|
|
|
}
|
|
|
|
pconfig->nr_blocks = (size >> huge_page_shift(h));
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
2007-07-16 10:40:52 +04:00
|
|
|
|
|
|
|
bad_val:
|
|
|
|
printk(KERN_ERR "hugetlbfs: Bad value '%s' for mount option '%s'\n",
|
|
|
|
args[0].from, p);
|
2009-04-21 23:24:05 +04:00
|
|
|
return -EINVAL;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
hugetlbfs_fill_super(struct super_block *sb, void *data, int silent)
|
|
|
|
{
|
|
|
|
struct inode * inode;
|
|
|
|
struct dentry * root;
|
|
|
|
int ret;
|
|
|
|
struct hugetlbfs_config config;
|
|
|
|
struct hugetlbfs_sb_info *sbinfo;
|
|
|
|
|
2008-02-08 15:21:45 +03:00
|
|
|
save_mount_options(sb, data);
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
config.nr_blocks = -1; /* No limit on size by default */
|
|
|
|
config.nr_inodes = -1; /* No limit on number of inodes by default */
|
2008-11-14 02:38:56 +03:00
|
|
|
config.uid = current_fsuid();
|
|
|
|
config.gid = current_fsgid();
|
2005-04-17 02:20:36 +04:00
|
|
|
config.mode = 0755;
|
2008-07-24 08:27:43 +04:00
|
|
|
config.hstate = &default_hstate;
|
2005-04-17 02:20:36 +04:00
|
|
|
ret = hugetlbfs_parse_options(data, &config);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
sbinfo = kmalloc(sizeof(struct hugetlbfs_sb_info), GFP_KERNEL);
|
|
|
|
if (!sbinfo)
|
|
|
|
return -ENOMEM;
|
|
|
|
sb->s_fs_info = sbinfo;
|
2008-07-24 08:27:43 +04:00
|
|
|
sbinfo->hstate = config.hstate;
|
2005-04-17 02:20:36 +04:00
|
|
|
spin_lock_init(&sbinfo->stat_lock);
|
|
|
|
sbinfo->max_blocks = config.nr_blocks;
|
|
|
|
sbinfo->free_blocks = config.nr_blocks;
|
|
|
|
sbinfo->max_inodes = config.nr_inodes;
|
|
|
|
sbinfo->free_inodes = config.nr_inodes;
|
|
|
|
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
2008-07-24 08:27:43 +04:00
|
|
|
sb->s_blocksize = huge_page_size(config.hstate);
|
|
|
|
sb->s_blocksize_bits = huge_page_shift(config.hstate);
|
2005-04-17 02:20:36 +04:00
|
|
|
sb->s_magic = HUGETLBFS_MAGIC;
|
|
|
|
sb->s_op = &hugetlbfs_ops;
|
|
|
|
sb->s_time_gran = 1;
|
|
|
|
inode = hugetlbfs_get_inode(sb, config.uid, config.gid,
|
|
|
|
S_IFDIR | config.mode, 0);
|
|
|
|
if (!inode)
|
|
|
|
goto out_free;
|
|
|
|
|
|
|
|
root = d_alloc_root(inode);
|
|
|
|
if (!root) {
|
|
|
|
iput(inode);
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
sb->s_root = root;
|
|
|
|
return 0;
|
|
|
|
out_free:
|
|
|
|
kfree(sbinfo);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2007-11-15 03:59:41 +03:00
|
|
|
int hugetlb_get_quota(struct address_space *mapping, long delta)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(mapping->host->i_sb);
|
|
|
|
|
|
|
|
if (sbinfo->free_blocks > -1) {
|
|
|
|
spin_lock(&sbinfo->stat_lock);
|
2007-11-15 03:59:41 +03:00
|
|
|
if (sbinfo->free_blocks - delta >= 0)
|
|
|
|
sbinfo->free_blocks -= delta;
|
2005-04-17 02:20:36 +04:00
|
|
|
else
|
|
|
|
ret = -ENOMEM;
|
|
|
|
spin_unlock(&sbinfo->stat_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-11-15 03:59:41 +03:00
|
|
|
void hugetlb_put_quota(struct address_space *mapping, long delta)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(mapping->host->i_sb);
|
|
|
|
|
|
|
|
if (sbinfo->free_blocks > -1) {
|
|
|
|
spin_lock(&sbinfo->stat_lock);
|
2007-11-15 03:59:41 +03:00
|
|
|
sbinfo->free_blocks += delta;
|
2005-04-17 02:20:36 +04:00
|
|
|
spin_unlock(&sbinfo->stat_lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-25 11:46:36 +04:00
|
|
|
static struct dentry *hugetlbfs_mount(struct file_system_type *fs_type,
|
|
|
|
int flags, const char *dev_name, void *data)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2010-07-25 11:46:36 +04:00
|
|
|
return mount_nodev(fs_type, flags, data, hugetlbfs_fill_super);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct file_system_type hugetlbfs_fs_type = {
|
|
|
|
.name = "hugetlbfs",
|
2010-07-25 11:46:36 +04:00
|
|
|
.mount = hugetlbfs_mount,
|
2005-04-17 02:20:36 +04:00
|
|
|
.kill_sb = kill_litter_super,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct vfsmount *hugetlbfs_vfsmount;
|
|
|
|
|
2009-09-24 02:56:05 +04:00
|
|
|
static int can_do_hugetlb_shm(void)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2009-09-24 02:56:05 +04:00
|
|
|
return capable(CAP_IPC_LOCK) || in_group_p(sysctl_hugetlb_shm_group);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2011-05-26 14:16:19 +04:00
|
|
|
struct file *hugetlb_file_setup(const char *name, size_t size,
|
|
|
|
vm_flags_t acctflag,
|
2009-09-22 04:03:43 +04:00
|
|
|
struct user_struct **user, int creat_flags)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int error = -ENOMEM;
|
|
|
|
struct file *file;
|
|
|
|
struct inode *inode;
|
2009-08-09 00:52:35 +04:00
|
|
|
struct path path;
|
|
|
|
struct dentry *root;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct qstr quick_string;
|
|
|
|
|
2009-08-24 19:30:28 +04:00
|
|
|
*user = NULL;
|
2007-05-07 01:50:18 +04:00
|
|
|
if (!hugetlbfs_vfsmount)
|
|
|
|
return ERR_PTR(-ENOENT);
|
|
|
|
|
2009-09-24 02:56:05 +04:00
|
|
|
if (creat_flags == HUGETLB_SHMFS_INODE && !can_do_hugetlb_shm()) {
|
2009-08-24 19:30:28 +04:00
|
|
|
*user = current_user();
|
|
|
|
if (user_shm_lock(size, *user)) {
|
2010-11-12 01:05:20 +03:00
|
|
|
printk_once(KERN_WARNING "Using mlock ulimits for SHM_HUGETLB is deprecated\n");
|
2009-08-24 19:30:28 +04:00
|
|
|
} else {
|
|
|
|
*user = NULL;
|
2009-04-01 02:21:26 +04:00
|
|
|
return ERR_PTR(-EPERM);
|
2009-08-24 19:30:28 +04:00
|
|
|
}
|
2009-04-01 02:21:26 +04:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
root = hugetlbfs_vfsmount->mnt_root;
|
2007-06-16 21:16:16 +04:00
|
|
|
quick_string.name = name;
|
2005-04-17 02:20:36 +04:00
|
|
|
quick_string.len = strlen(quick_string.name);
|
|
|
|
quick_string.hash = 0;
|
2009-08-09 00:52:35 +04:00
|
|
|
path.dentry = d_alloc(root, &quick_string);
|
|
|
|
if (!path.dentry)
|
2005-04-17 02:20:36 +04:00
|
|
|
goto out_shm_unlock;
|
|
|
|
|
2009-08-09 00:52:35 +04:00
|
|
|
path.mnt = mntget(hugetlbfs_vfsmount);
|
2005-04-17 02:20:36 +04:00
|
|
|
error = -ENOSPC;
|
2008-11-14 02:38:56 +03:00
|
|
|
inode = hugetlbfs_get_inode(root->d_sb, current_fsuid(),
|
|
|
|
current_fsgid(), S_IFREG | S_IRWXUGO, 0);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (!inode)
|
2007-10-17 10:31:13 +04:00
|
|
|
goto out_dentry;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
[PATCH] hugepage: Strict page reservation for hugepage inodes
These days, hugepages are demand-allocated at first fault time. There's a
somewhat dubious (and racy) heuristic when making a new mmap() to check if
there are enough available hugepages to fully satisfy that mapping.
A particularly obvious case where the heuristic breaks down is where a
process maps its hugepages not as a single chunk, but as a bunch of
individually mmap()ed (or shmat()ed) blocks without touching and
instantiating the pages in between allocations. In this case the size of
each block is compared against the total number of available hugepages.
It's thus easy for the process to become overcommitted, because each block
mapping will succeed, although the total number of hugepages required by
all blocks exceeds the number available. In particular, this defeats such
a program which will detect a mapping failure and adjust its hugepage usage
downward accordingly.
The patch below addresses this problem, by strictly reserving a number of
physical hugepages for hugepage inodes which have been mapped, but not
instatiated. MAP_SHARED mappings are thus "safe" - they will fail on
mmap(), not later with an OOM SIGKILL. MAP_PRIVATE mappings can still
trigger an OOM. (Actually SHARED mappings can technically still OOM, but
only if the sysadmin explicitly reduces the hugepage pool between mapping
and instantiation)
This patch appears to address the problem at hand - it allows DB2 to start
correctly, for instance, which previously suffered the failure described
above.
This patch causes no regressions on the libhugetblfs testsuite, and makes a
test (designed to catch this problem) pass which previously failed (ppc64,
POWER5).
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-22 11:08:55 +03:00
|
|
|
error = -ENOMEM;
|
2008-07-24 08:27:41 +04:00
|
|
|
if (hugetlb_reserve_pages(inode, 0,
|
2009-02-10 17:02:27 +03:00
|
|
|
size >> huge_page_shift(hstate_inode(inode)), NULL,
|
|
|
|
acctflag))
|
[PATCH] hugepage: Strict page reservation for hugepage inodes
These days, hugepages are demand-allocated at first fault time. There's a
somewhat dubious (and racy) heuristic when making a new mmap() to check if
there are enough available hugepages to fully satisfy that mapping.
A particularly obvious case where the heuristic breaks down is where a
process maps its hugepages not as a single chunk, but as a bunch of
individually mmap()ed (or shmat()ed) blocks without touching and
instantiating the pages in between allocations. In this case the size of
each block is compared against the total number of available hugepages.
It's thus easy for the process to become overcommitted, because each block
mapping will succeed, although the total number of hugepages required by
all blocks exceeds the number available. In particular, this defeats such
a program which will detect a mapping failure and adjust its hugepage usage
downward accordingly.
The patch below addresses this problem, by strictly reserving a number of
physical hugepages for hugepage inodes which have been mapped, but not
instatiated. MAP_SHARED mappings are thus "safe" - they will fail on
mmap(), not later with an OOM SIGKILL. MAP_PRIVATE mappings can still
trigger an OOM. (Actually SHARED mappings can technically still OOM, but
only if the sysadmin explicitly reduces the hugepage pool between mapping
and instantiation)
This patch appears to address the problem at hand - it allows DB2 to start
correctly, for instance, which previously suffered the failure described
above.
This patch causes no regressions on the libhugetblfs testsuite, and makes a
test (designed to catch this problem) pass which previously failed (ppc64,
POWER5).
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-22 11:08:55 +03:00
|
|
|
goto out_inode;
|
|
|
|
|
2009-08-09 00:52:35 +04:00
|
|
|
d_instantiate(path.dentry, inode);
|
2005-04-17 02:20:36 +04:00
|
|
|
inode->i_size = size;
|
|
|
|
inode->i_nlink = 0;
|
2007-10-17 10:31:13 +04:00
|
|
|
|
|
|
|
error = -ENFILE;
|
2009-08-09 00:52:35 +04:00
|
|
|
file = alloc_file(&path, FMODE_WRITE | FMODE_READ,
|
2007-10-17 10:31:13 +04:00
|
|
|
&hugetlbfs_file_operations);
|
|
|
|
if (!file)
|
2008-02-23 13:59:19 +03:00
|
|
|
goto out_dentry; /* inode is already attached */
|
2007-10-17 10:31:13 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
return file;
|
|
|
|
|
[PATCH] hugepage: Strict page reservation for hugepage inodes
These days, hugepages are demand-allocated at first fault time. There's a
somewhat dubious (and racy) heuristic when making a new mmap() to check if
there are enough available hugepages to fully satisfy that mapping.
A particularly obvious case where the heuristic breaks down is where a
process maps its hugepages not as a single chunk, but as a bunch of
individually mmap()ed (or shmat()ed) blocks without touching and
instantiating the pages in between allocations. In this case the size of
each block is compared against the total number of available hugepages.
It's thus easy for the process to become overcommitted, because each block
mapping will succeed, although the total number of hugepages required by
all blocks exceeds the number available. In particular, this defeats such
a program which will detect a mapping failure and adjust its hugepage usage
downward accordingly.
The patch below addresses this problem, by strictly reserving a number of
physical hugepages for hugepage inodes which have been mapped, but not
instatiated. MAP_SHARED mappings are thus "safe" - they will fail on
mmap(), not later with an OOM SIGKILL. MAP_PRIVATE mappings can still
trigger an OOM. (Actually SHARED mappings can technically still OOM, but
only if the sysadmin explicitly reduces the hugepage pool between mapping
and instantiation)
This patch appears to address the problem at hand - it allows DB2 to start
correctly, for instance, which previously suffered the failure described
above.
This patch causes no regressions on the libhugetblfs testsuite, and makes a
test (designed to catch this problem) pass which previously failed (ppc64,
POWER5).
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-22 11:08:55 +03:00
|
|
|
out_inode:
|
|
|
|
iput(inode);
|
2005-04-17 02:20:36 +04:00
|
|
|
out_dentry:
|
2009-08-09 00:52:35 +04:00
|
|
|
path_put(&path);
|
2005-04-17 02:20:36 +04:00
|
|
|
out_shm_unlock:
|
2009-08-24 19:30:28 +04:00
|
|
|
if (*user) {
|
|
|
|
user_shm_unlock(size, *user);
|
|
|
|
*user = NULL;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
return ERR_PTR(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __init init_hugetlbfs_fs(void)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
struct vfsmount *vfsmount;
|
|
|
|
|
2007-10-17 10:25:46 +04:00
|
|
|
error = bdi_init(&hugetlbfs_backing_dev_info);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
hugetlbfs_inode_cachep = kmem_cache_create("hugetlbfs_inode_cache",
|
|
|
|
sizeof(struct hugetlbfs_inode_info),
|
2007-07-20 05:11:58 +04:00
|
|
|
0, 0, init_once);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (hugetlbfs_inode_cachep == NULL)
|
2007-10-17 10:25:46 +04:00
|
|
|
goto out2;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
error = register_filesystem(&hugetlbfs_fs_type);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
vfsmount = kern_mount(&hugetlbfs_fs_type);
|
|
|
|
|
|
|
|
if (!IS_ERR(vfsmount)) {
|
|
|
|
hugetlbfs_vfsmount = vfsmount;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = PTR_ERR(vfsmount);
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (error)
|
|
|
|
kmem_cache_destroy(hugetlbfs_inode_cachep);
|
2007-10-17 10:25:46 +04:00
|
|
|
out2:
|
|
|
|
bdi_destroy(&hugetlbfs_backing_dev_info);
|
2005-04-17 02:20:36 +04:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit exit_hugetlbfs_fs(void)
|
|
|
|
{
|
|
|
|
kmem_cache_destroy(hugetlbfs_inode_cachep);
|
2011-07-19 20:32:38 +04:00
|
|
|
kern_unmount(hugetlbfs_vfsmount);
|
2005-04-17 02:20:36 +04:00
|
|
|
unregister_filesystem(&hugetlbfs_fs_type);
|
2007-10-17 10:25:46 +04:00
|
|
|
bdi_destroy(&hugetlbfs_backing_dev_info);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(init_hugetlbfs_fs)
|
|
|
|
module_exit(exit_hugetlbfs_fs)
|
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|