aboutsummaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/9p/conv.c6
-rw-r--r--fs/9p/vfs_inode.c6
-rw-r--r--fs/Kconfig1
-rw-r--r--fs/adfs/super.c2
-rw-r--r--fs/befs/linuxvfs.c11
-rw-r--r--fs/binfmt_elf.c17
-rw-r--r--fs/binfmt_elf_fdpic.c980
-rw-r--r--fs/block_dev.c180
-rw-r--r--fs/buffer.c7
-rw-r--r--fs/char_dev.c22
-rw-r--r--fs/cifs/CHANGES10
-rw-r--r--fs/cifs/README2
-rw-r--r--fs/cifs/cifsencrypt.c3
-rw-r--r--fs/cifs/cifsfs.c6
-rw-r--r--fs/cifs/cifsfs.h2
-rw-r--r--fs/cifs/cifsglob.h18
-rw-r--r--fs/cifs/cifsproto.h4
-rw-r--r--fs/cifs/cifssmb.c28
-rw-r--r--fs/cifs/connect.c32
-rw-r--r--fs/cifs/dir.c4
-rw-r--r--fs/cifs/file.c97
-rw-r--r--fs/cifs/netmisc.c1
-rw-r--r--fs/cifs/readdir.c13
-rw-r--r--fs/cifs/sess.c2
-rw-r--r--fs/cifs/smberr.h1
-rw-r--r--fs/cifs/transport.c618
-rw-r--r--fs/cifs/xattr.c6
-rw-r--r--fs/coda/file.c4
-rw-r--r--fs/dcache.c6
-rw-r--r--fs/direct-io.c6
-rw-r--r--fs/efs/symlink.c3
-rw-r--r--fs/eventpoll.c8
-rw-r--r--fs/exec.c10
-rw-r--r--fs/ext2/super.c43
-rw-r--r--fs/ext3/acl.h3
-rw-r--r--fs/ext3/balloc.c6
-rw-r--r--fs/ext3/inode.c32
-rw-r--r--fs/ext3/namei.c15
-rw-r--r--fs/ext3/super.c44
-rw-r--r--fs/file.c14
-rw-r--r--fs/freevxfs/vxfs_lookup.c2
-rw-r--r--fs/fuse/control.c4
-rw-r--r--fs/fuse/dir.c47
-rw-r--r--fs/fuse/file.c10
-rw-r--r--fs/fuse/fuse_i.h2
-rw-r--r--fs/fuse/inode.c2
-rw-r--r--fs/hugetlbfs/inode.c4
-rw-r--r--fs/inotify_user.c2
-rw-r--r--fs/ioprio.c30
-rw-r--r--fs/jbd/commit.c6
-rw-r--r--fs/jbd/journal.c92
-rw-r--r--fs/jbd/transaction.c11
-rw-r--r--fs/jffs2/acl.c4
-rw-r--r--fs/jffs2/acl.h4
-rw-r--r--fs/jffs2/malloc.c2
-rw-r--r--fs/jffs2/nodelist.h2
-rw-r--r--fs/jffs2/readinode.c1
-rw-r--r--fs/jffs2/scan.c4
-rw-r--r--fs/jffs2/summary.c5
-rw-r--r--fs/jffs2/xattr.c45
-rw-r--r--fs/jfs/inode.c16
-rw-r--r--fs/jfs/jfs_inode.h1
-rw-r--r--fs/jfs/jfs_txnmgr.c2
-rw-r--r--fs/jfs/namei.c33
-rw-r--r--fs/jfs/super.c118
-rw-r--r--fs/lockd/clntproc.c26
-rw-r--r--fs/lockd/svclock.c12
-rw-r--r--fs/lockd/svcsubs.c15
-rw-r--r--fs/locks.c29
-rw-r--r--fs/minix/inode.c13
-rw-r--r--fs/namei.c47
-rw-r--r--fs/nfs/dir.c4
-rw-r--r--fs/nfs/direct.c427
-rw-r--r--fs/nfs/file.c8
-rw-r--r--fs/nfs/idmap.c4
-rw-r--r--fs/nfs/namespace.c4
-rw-r--r--fs/nfs/nfs4proc.c103
-rw-r--r--fs/nfs/nfs4xdr.c21
-rw-r--r--fs/nfs/read.c49
-rw-r--r--fs/nfs/write.c59
-rw-r--r--fs/nfsd/nfs4proc.c8
-rw-r--r--fs/nfsd/nfsfh.c20
-rw-r--r--fs/nfsd/stats.c10
-rw-r--r--fs/ntfs/inode.c33
-rw-r--r--fs/ntfs/super.c31
-rw-r--r--fs/ocfs2/dlm/dlmmaster.c1
-rw-r--r--fs/ocfs2/dlm/dlmunlock.c43
-rw-r--r--fs/ocfs2/localalloc.c8
-rw-r--r--fs/ocfs2/ocfs2.h2
-rw-r--r--fs/ocfs2/suballoc.c261
-rw-r--r--fs/ocfs2/suballoc.h2
-rw-r--r--fs/ocfs2/super.c8
-rw-r--r--fs/partitions/Kconfig2
-rw-r--r--fs/partitions/check.c1
-rw-r--r--fs/partitions/sun.c2
-rw-r--r--fs/proc/array.c6
-rw-r--r--fs/proc/base.c33
-rw-r--r--fs/proc/inode.c2
-rw-r--r--fs/proc/kcore.c4
-rw-r--r--fs/proc/proc_misc.c2
-rw-r--r--fs/proc/task_nommu.c2
-rw-r--r--fs/ramfs/file-nommu.c4
-rw-r--r--fs/read_write.c2
-rw-r--r--fs/reiserfs/file.c8
-rw-r--r--fs/reiserfs/inode.c26
-rw-r--r--fs/reiserfs/ioctl.c2
-rw-r--r--fs/reiserfs/procfs.c25
-rw-r--r--fs/reiserfs/super.c2
-rw-r--r--fs/reiserfs/xattr.c2
-rw-r--r--fs/splice.c238
-rw-r--r--fs/super.c12
-rw-r--r--fs/sysfs/inode.c12
-rw-r--r--fs/udf/ialloc.c11
-rw-r--r--fs/udf/super.c9
-rw-r--r--fs/udf/truncate.c64
-rw-r--r--fs/ufs/balloc.c2
-rw-r--r--fs/ufs/inode.c35
-rw-r--r--fs/ufs/namei.c3
-rw-r--r--fs/ufs/super.c2
-rw-r--r--fs/ufs/truncate.c77
-rw-r--r--fs/ufs/util.c17
-rw-r--r--fs/xfs/linux-2.6/xfs_aops.c18
-rw-r--r--fs/xfs/linux-2.6/xfs_buf.h4
-rw-r--r--fs/xfs/linux-2.6/xfs_linux.h1
-rw-r--r--fs/xfs/linux-2.6/xfs_lrw.c27
-rw-r--r--fs/xfs/linux-2.6/xfs_super.c7
-rw-r--r--fs/xfs/quota/xfs_qm_bhv.c21
-rw-r--r--fs/xfs/xfs_alloc.c103
-rw-r--r--fs/xfs/xfs_alloc.h20
-rw-r--r--fs/xfs/xfs_bmap.c2
-rw-r--r--fs/xfs/xfs_fsops.c16
-rw-r--r--fs/xfs/xfs_inode.c17
-rw-r--r--fs/xfs/xfs_log.c12
-rw-r--r--fs/xfs/xfs_mount.c32
-rw-r--r--fs/xfs/xfs_vfsops.c5
135 files changed, 3413 insertions, 1449 deletions
diff --git a/fs/9p/conv.c b/fs/9p/conv.c
index 1e898144eb7..56d88c1a09c 100644
--- a/fs/9p/conv.c
+++ b/fs/9p/conv.c
@@ -673,8 +673,10 @@ struct v9fs_fcall *v9fs_create_tcreate(u32 fid, char *name, u32 perm, u8 mode,
struct cbuf *bufp = &buffer;
size = 4 + 2 + strlen(name) + 4 + 1; /* fid[4] name[s] perm[4] mode[1] */
- if (extended && extension!=NULL)
- size += 2 + strlen(extension); /* extension[s] */
+ if (extended) {
+ size += 2 + /* extension[s] */
+ (extension == NULL ? 0 : strlen(extension));
+ }
fc = v9fs_create_common(bufp, size, TCREATE);
if (IS_ERR(fc))
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index 2f580a197b8..eae50c9d6dc 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -434,11 +434,11 @@ static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir)
result = v9fs_t_remove(v9ses, fid, &fcall);
if (result < 0) {
PRINT_FCALL_ERROR("remove fails", fcall);
- } else {
- v9fs_put_idpool(fid, &v9ses->fidpool);
- v9fs_fid_destroy(v9fid);
}
+ v9fs_put_idpool(fid, &v9ses->fidpool);
+ v9fs_fid_destroy(v9fid);
+
kfree(fcall);
return result;
}
diff --git a/fs/Kconfig b/fs/Kconfig
index 53f5c6d6112..3f00a9faabc 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -1801,6 +1801,7 @@ config CIFS_POSIX
config CIFS_DEBUG2
bool "Enable additional CIFS debugging routines"
+ depends on CIFS
help
Enabling this option adds a few more debugging routines
to the cifs code which slightly increases the size of
diff --git a/fs/adfs/super.c b/fs/adfs/super.c
index ba1c88af49f..82011019494 100644
--- a/fs/adfs/super.c
+++ b/fs/adfs/super.c
@@ -308,7 +308,7 @@ static struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_di
if (adfs_checkmap(sb, dm))
return dm;
- adfs_error(sb, NULL, "map corrupted");
+ adfs_error(sb, "map corrupted");
error_free:
while (--zone >= 0)
diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
index fcaeead9696..50cfca5c7ef 100644
--- a/fs/befs/linuxvfs.c
+++ b/fs/befs/linuxvfs.c
@@ -512,7 +512,11 @@ befs_utf2nls(struct super_block *sb, const char *in,
wchar_t uni;
int unilen, utflen;
char *result;
- int maxlen = in_len; /* The utf8->nls conversion can't make more chars */
+ /* The utf8->nls conversion won't make the final nls string bigger
+ * than the utf one, but if the string is pure ascii they'll have the
+ * same width and an extra char is needed to save the additional \0
+ */
+ int maxlen = in_len + 1;
befs_debug(sb, "---> utf2nls()");
@@ -588,7 +592,10 @@ befs_nls2utf(struct super_block *sb, const char *in,
wchar_t uni;
int unilen, utflen;
char *result;
- int maxlen = 3 * in_len;
+ /* There're nls characters that will translate to 3-chars-wide UTF-8
+ * characters, a additional byte is needed to save the final \0
+ * in special cases */
+ int maxlen = (3 * in_len) + 1;
befs_debug(sb, "---> nls2utf()\n");
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index d0434406eae..672a3b90bc5 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -84,7 +84,7 @@ static struct linux_binfmt elf_format = {
.min_coredump = ELF_EXEC_PAGESIZE
};
-#define BAD_ADDR(x) ((unsigned long)(x) > TASK_SIZE)
+#define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE)
static int set_brk(unsigned long start, unsigned long end)
{
@@ -394,7 +394,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
* <= p_memsize so it's only necessary to check p_memsz.
*/
k = load_addr + eppnt->p_vaddr;
- if (k > TASK_SIZE ||
+ if (BAD_ADDR(k) ||
eppnt->p_filesz > eppnt->p_memsz ||
eppnt->p_memsz > TASK_SIZE ||
TASK_SIZE - eppnt->p_memsz < k) {
@@ -887,7 +887,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
* allowed task size. Note that p_filesz must always be
* <= p_memsz so it is only necessary to check p_memsz.
*/
- if (k > TASK_SIZE || elf_ppnt->p_filesz > elf_ppnt->p_memsz ||
+ if (BAD_ADDR(k) || elf_ppnt->p_filesz > elf_ppnt->p_memsz ||
elf_ppnt->p_memsz > TASK_SIZE ||
TASK_SIZE - elf_ppnt->p_memsz < k) {
/* set_brk can never work. Avoid overflows. */
@@ -941,10 +941,9 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
interpreter,
&interp_load_addr);
if (BAD_ADDR(elf_entry)) {
- printk(KERN_ERR "Unable to load interpreter %.128s\n",
- elf_interpreter);
force_sig(SIGSEGV, current);
- retval = -ENOEXEC; /* Nobody gets to see this, but.. */
+ retval = IS_ERR((void *)elf_entry) ?
+ (int)elf_entry : -EINVAL;
goto out_free_dentry;
}
reloc_func_desc = interp_load_addr;
@@ -955,8 +954,8 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
} else {
elf_entry = loc->elf_ex.e_entry;
if (BAD_ADDR(elf_entry)) {
- send_sig(SIGSEGV, current, 0);
- retval = -ENOEXEC; /* Nobody gets to see this, but.. */
+ force_sig(SIGSEGV, current);
+ retval = -EINVAL;
goto out_free_dentry;
}
}
@@ -1186,8 +1185,6 @@ static int maydump(struct vm_area_struct *vma)
return 1;
}
-#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
-
/* An ELF note in memory */
struct memelfnote
{
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index eba4e23b9ca..2f336582922 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -1,6 +1,6 @@
/* binfmt_elf_fdpic.c: FDPIC ELF binary format
*
- * Copyright (C) 2003, 2004 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2003, 2004, 2006 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
* Derived from binfmt_elf.c
*
@@ -24,7 +24,9 @@
#include <linux/file.h>
#include <linux/fcntl.h>
#include <linux/slab.h>
+#include <linux/pagemap.h>
#include <linux/highmem.h>
+#include <linux/highuid.h>
#include <linux/personality.h>
#include <linux/ptrace.h>
#include <linux/init.h>
@@ -48,45 +50,59 @@ typedef char *elf_caddr_t;
#define kdebug(fmt, ...) do {} while(0)
#endif
+#if 0
+#define kdcore(fmt, ...) printk("FDPIC "fmt"\n" ,##__VA_ARGS__ )
+#else
+#define kdcore(fmt, ...) do {} while(0)
+#endif
+
MODULE_LICENSE("GPL");
-static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs);
-//static int load_elf_fdpic_library(struct file *);
-static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *params, struct file *file);
-static int elf_fdpic_map_file(struct elf_fdpic_params *params,
- struct file *file,
- struct mm_struct *mm,
- const char *what);
+static int load_elf_fdpic_binary(struct linux_binprm *, struct pt_regs *);
+static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *, struct file *);
+static int elf_fdpic_map_file(struct elf_fdpic_params *, struct file *,
+ struct mm_struct *, const char *);
-static int create_elf_fdpic_tables(struct linux_binprm *bprm,
- struct mm_struct *mm,
- struct elf_fdpic_params *exec_params,
- struct elf_fdpic_params *interp_params);
+static int create_elf_fdpic_tables(struct linux_binprm *, struct mm_struct *,
+ struct elf_fdpic_params *,
+ struct elf_fdpic_params *);
#ifndef CONFIG_MMU
-static int elf_fdpic_transfer_args_to_stack(struct linux_binprm *bprm, unsigned long *_sp);
-static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *params,
- struct file *file,
- struct mm_struct *mm);
+static int elf_fdpic_transfer_args_to_stack(struct linux_binprm *,
+ unsigned long *);
+static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *,
+ struct file *,
+ struct mm_struct *);
#endif
-static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
- struct file *file,
- struct mm_struct *mm);
+static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *,
+ struct file *, struct mm_struct *);
+
+#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE)
+static int elf_fdpic_core_dump(long, struct pt_regs *, struct file *);
+#endif
static struct linux_binfmt elf_fdpic_format = {
.module = THIS_MODULE,
.load_binary = load_elf_fdpic_binary,
-// .load_shlib = load_elf_fdpic_library,
-// .core_dump = elf_fdpic_core_dump,
+#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE)
+ .core_dump = elf_fdpic_core_dump,
+#endif
.min_coredump = ELF_EXEC_PAGESIZE,
};
-static int __init init_elf_fdpic_binfmt(void) { return register_binfmt(&elf_fdpic_format); }
-static void __exit exit_elf_fdpic_binfmt(void) { unregister_binfmt(&elf_fdpic_format); }
+static int __init init_elf_fdpic_binfmt(void)
+{
+ return register_binfmt(&elf_fdpic_format);
+}
+
+static void __exit exit_elf_fdpic_binfmt(void)
+{
+ unregister_binfmt(&elf_fdpic_format);
+}
-module_init(init_elf_fdpic_binfmt)
-module_exit(exit_elf_fdpic_binfmt)
+core_initcall(init_elf_fdpic_binfmt);
+module_exit(exit_elf_fdpic_binfmt);
static int is_elf_fdpic(struct elfhdr *hdr, struct file *file)
{
@@ -105,7 +121,8 @@ static int is_elf_fdpic(struct elfhdr *hdr, struct file *file)
/*
* read the program headers table into memory
*/
-static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *params, struct file *file)
+static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *params,
+ struct file *file)
{
struct elf32_phdr *phdr;
unsigned long size;
@@ -121,7 +138,8 @@ static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *params, struct file *f
if (!params->phdrs)
return -ENOMEM;
- retval = kernel_read(file, params->hdr.e_phoff, (char *) params->phdrs, size);
+ retval = kernel_read(file, params->hdr.e_phoff,
+ (char *) params->phdrs, size);
if (retval < 0)
return retval;
@@ -141,17 +159,24 @@ static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *params, struct file *f
}
return 0;
-} /* end elf_fdpic_fetch_phdrs() */
+}
/*****************************************************************************/
/*
* load an fdpic binary into various bits of memory
*/
-static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs)
+static int load_elf_fdpic_binary(struct linux_binprm *bprm,
+ struct pt_regs *regs)
{
struct elf_fdpic_params exec_params, interp_params;
struct elf_phdr *phdr;
- unsigned long stack_size;
+ unsigned long stack_size, entryaddr;
+#ifndef CONFIG_MMU
+ unsigned long fullsize;
+#endif
+#ifdef ELF_FDPIC_PLAT_INIT
+ unsigned long dynaddr;
+#endif
struct file *interpreter = NULL; /* to shut gcc up */
char *interpreter_name = NULL;
int executable_stack;
@@ -212,7 +237,8 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs
goto error;
}
- retval = kernel_read(interpreter, 0, bprm->buf, BINPRM_BUF_SIZE);
+ retval = kernel_read(interpreter, 0, bprm->buf,
+ BINPRM_BUF_SIZE);
if (retval < 0)
goto error;
@@ -295,7 +321,8 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs
&current->mm->start_stack,
&current->mm->start_brk);
- retval = setup_arg_pages(bprm, current->mm->start_stack, executable_stack);
+ retval = setup_arg_pages(bprm, current->mm->start_stack,
+ executable_stack);
if (retval < 0) {
send_sig(SIGKILL, current, 0);
goto error_kill;
@@ -303,7 +330,8 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs
#endif
/* load the executable and interpreter into memory */
- retval = elf_fdpic_map_file(&exec_params, bprm->file, current->mm, "executable");
+ retval = elf_fdpic_map_file(&exec_params, bprm->file, current->mm,
+ "executable");
if (retval < 0)
goto error_kill;
@@ -324,7 +352,8 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs
if (!current->mm->start_brk)
current->mm->start_brk = current->mm->end_data;
- current->mm->brk = current->mm->start_brk = PAGE_ALIGN(current->mm->start_brk);
+ current->mm->brk = current->mm->start_brk =
+ PAGE_ALIGN(current->mm->start_brk);
#else
/* create a stack and brk area big enough for everyone
@@ -336,47 +365,45 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs
stack_size = PAGE_SIZE * 2;
down_write(&current->mm->mmap_sem);
- current->mm->start_brk = do_mmap(NULL,
- 0,
- stack_size,
+ current->mm->start_brk = do_mmap(NULL, 0, stack_size,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANON | MAP_GROWSDOWN,
0);
- if (IS_ERR((void *) current->mm->start_brk)) {
+ if (IS_ERR_VALUE(current->mm->start_brk)) {
up_write(&current->mm->mmap_sem);
retval = current->mm->start_brk;
current->mm->start_brk = 0;
goto error_kill;
}
- if (do_mremap(current->mm->start_brk,
- stack_size,
- ksize((char *) current->mm->start_brk),
- 0, 0
- ) == current->mm->start_brk
- )
- stack_size = ksize((char *) current->mm->start_brk);
+ /* expand the stack mapping to use up the entire allocation granule */
+ fullsize = ksize((char *) current->mm->start_brk);
+ if (!IS_ERR_VALUE(do_mremap(current->mm->start_brk, stack_size,
+ fullsize, 0, 0)))
+ stack_size = fullsize;
up_write(&current->mm->mmap_sem);
current->mm->brk = current->mm->start_brk;
current->mm->context.end_brk = current->mm->start_brk;
- current->mm->context.end_brk += (stack_size > PAGE_SIZE) ? (stack_size - PAGE_SIZE) : 0;
+ current->mm->context.end_brk +=
+ (stack_size > PAGE_SIZE) ? (stack_size - PAGE_SIZE) : 0;
current->mm->start_stack = current->mm->start_brk + stack_size;
#endif
compute_creds(bprm);
current->flags &= ~PF_FORKNOEXEC;
- if (create_elf_fdpic_tables(bprm, current->mm, &exec_params, &interp_params) < 0)
+ if (create_elf_fdpic_tables(bprm, current->mm,
+ &exec_params, &interp_params) < 0)
goto error_kill;
- kdebug("- start_code %lx", (long) current->mm->start_code);
- kdebug("- end_code %lx", (long) current->mm->end_code);
- kdebug("- start_data %lx", (long) current->mm->start_data);
- kdebug("- end_data %lx", (long) current->mm->end_data);
- kdebug("- start_brk %lx", (long) current->mm->start_brk);
- kdebug("- brk %lx", (long) current->mm->brk);
- kdebug("- start_stack %lx", (long) current->mm->start_stack);
+ kdebug("- start_code %lx", current->mm->start_code);
+ kdebug("- end_code %lx", current->mm->end_code);
+ kdebug("- start_data %lx", current->mm->start_data);
+ kdebug("- end_data %lx", current->mm->end_data);
+ kdebug("- start_brk %lx", current->mm->start_brk);
+ kdebug("- brk %lx", current->mm->brk);
+ kdebug("- start_stack %lx", current->mm->start_stack);
#ifdef ELF_FDPIC_PLAT_INIT
/*
@@ -385,21 +412,18 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs
* example. This macro performs whatever initialization to
* the regs structure is required.
*/
- ELF_FDPIC_PLAT_INIT(regs,
- exec_params.map_addr,
- interp_params.map_addr,
- interp_params.dynamic_addr ?: exec_params.dynamic_addr
- );
+ dynaddr = interp_params.dynamic_addr ?: exec_params.dynamic_addr;
+ ELF_FDPIC_PLAT_INIT(regs, exec_params.map_addr, interp_params.map_addr,
+ dynaddr);
#endif
/* everything is now ready... get the userspace context ready to roll */
- start_thread(regs,
- interp_params.entry_addr ?: exec_params.entry_addr,
- current->mm->start_stack);
+ entryaddr = interp_params.entry_addr ?: exec_params.entry_addr;
+ start_thread(regs, entryaddr, current->mm->start_stack);
if (unlikely(current->ptrace & PT_PTRACED)) {
if (current->ptrace & PT_TRACE_EXEC)
- ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP);
+ ptrace_notify((PTRACE_EVENT_EXEC << 8) | SIGTRAP);
else
send_sig(SIGTRAP, current, 0);
}
@@ -419,11 +443,11 @@ error:
return retval;
/* unrecoverable error - kill the process */
- error_kill:
+error_kill:
send_sig(SIGSEGV, current, 0);
goto error;
-} /* end load_elf_fdpic_binary() */
+}
/*****************************************************************************/
/*
@@ -459,6 +483,7 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
*/
hwcap = ELF_HWCAP;
k_platform = ELF_PLATFORM;
+ u_platform = NULL;
if (k_platform) {
platform_len = strlen(k_platform) + 1;
@@ -470,11 +495,11 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
#if defined(__i386__) && defined(CONFIG_SMP)
/* in some cases (e.g. Hyper-Threading), we want to avoid L1 evictions
- * by the processes running on the same package. One thing we can do
- * is to shuffle the initial stack for them.
+ * by the processes running on the same package. One thing we can do is
+ * to shuffle the initial stack for them.
*
- * the conditionals here are unneeded, but kept in to make the
- * code behaviour the same as pre change unless we have hyperthreaded
+ * the conditionals here are unneeded, but kept in to make the code
+ * behaviour the same as pre change unless we have hyperthreaded
* processors. This keeps Mr Marcelo Person happier but should be
* removed for 2.5
*/
@@ -497,11 +522,13 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
if (interp_params->loadmap) {
len = sizeof(struct elf32_fdpic_loadmap);
- len += sizeof(struct elf32_fdpic_loadseg) * interp_params->loadmap->nsegs;
+ len += sizeof(struct elf32_fdpic_loadseg) *
+ interp_params->loadmap->nsegs;
sp = (sp - len) & ~7UL;
interp_params->map_addr = sp;
- if (copy_to_user((void __user *) sp, interp_params->loadmap, len) != 0)
+ if (copy_to_user((void __user *) sp, interp_params->loadmap,
+ len) != 0)
return -EFAULT;
current->mm->context.interp_fdpic_loadmap = (unsigned long) sp;
@@ -525,34 +552,37 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
sp -= sp & 15UL;
/* put the ELF interpreter info on the stack */
-#define NEW_AUX_ENT(nr, id, val) \
- do { \
- struct { unsigned long _id, _val; } __user *ent = (void __user *) csp; \
- __put_user((id), &ent[nr]._id); \
- __put_user((val), &ent[nr]._val); \
+#define NEW_AUX_ENT(nr, id, val) \
+ do { \
+ struct { unsigned long _id, _val; } __user *ent; \
+ \
+ ent = (void __user *) csp; \
+ __put_user((id), &ent[nr]._id); \
+ __put_user((val), &ent[nr]._val); \
} while (0)
csp -= 2 * sizeof(unsigned long);
NEW_AUX_ENT(0, AT_NULL, 0);
if (k_platform) {
csp -= 2 * sizeof(unsigned long);
- NEW_AUX_ENT(0, AT_PLATFORM, (elf_addr_t)(unsigned long) u_platform);
+ NEW_AUX_ENT(0, AT_PLATFORM,
+ (elf_addr_t) (unsigned long) u_platform);
}
csp -= DLINFO_ITEMS * 2 * sizeof(unsigned long);
- NEW_AUX_ENT( 0, AT_HWCAP, hwcap);
- NEW_AUX_ENT( 1, AT_PAGESZ, PAGE_SIZE);
- NEW_AUX_ENT( 2, AT_CLKTCK, CLOCKS_PER_SEC);
- NEW_AUX_ENT( 3, AT_PHDR, exec_params->ph_addr);
- NEW_AUX_ENT( 4, AT_PHENT, sizeof(struct elf_phdr));
- NEW_AUX_ENT( 5, AT_PHNUM, exec_params->hdr.e_phnum);
- NEW_AUX_ENT( 6, AT_BASE, interp_params->elfhdr_addr);
- NEW_AUX_ENT( 7, AT_FLAGS, 0);
- NEW_AUX_ENT( 8, AT_ENTRY, exec_params->entry_addr);
- NEW_AUX_ENT( 9, AT_UID, (elf_addr_t) current->uid);
- NEW_AUX_ENT(10, AT_EUID, (elf_addr_t) current->euid);
- NEW_AUX_ENT(11, AT_GID, (elf_addr_t) current->gid);
- NEW_AUX_ENT(12, AT_EGID, (elf_addr_t) current->egid);
+ NEW_AUX_ENT( 0, AT_HWCAP, hwcap);
+ NEW_AUX_ENT( 1, AT_PAGESZ, PAGE_SIZE);
+ NEW_AUX_ENT( 2, AT_CLKTCK, CLOCKS_PER_SEC);
+ NEW_AUX_ENT( 3, AT_PHDR, exec_params->ph_addr);
+ NEW_AUX_ENT( 4, AT_PHENT, sizeof(struct elf_phdr));
+ NEW_AUX_ENT( 5, AT_PHNUM, exec_params->hdr.e_phnum);
+ NEW_AUX_ENT( 6, AT_BASE, interp_params->elfhdr_addr);
+ NEW_AUX_ENT( 7, AT_FLAGS, 0);
+ NEW_AUX_ENT( 8, AT_ENTRY, exec_params->entry_addr);
+ NEW_AUX_ENT( 9, AT_UID, (elf_addr_t) current->uid);
+ NEW_AUX_ENT(10, AT_EUID, (elf_addr_t) current->euid);
+ NEW_AUX_ENT(11, AT_GID, (elf_addr_t) current->gid);
+ NEW_AUX_ENT(12, AT_EGID, (elf_addr_t) current->egid);
#ifdef ARCH_DLINFO
/* ARCH_DLINFO must come last so platform specific code can enforce
@@ -578,7 +608,8 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
#ifdef CONFIG_MMU
current->mm->arg_start = bprm->p;
#else
- current->mm->arg_start = current->mm->start_stack - (MAX_ARG_PAGES * PAGE_SIZE - bprm->p);
+ current->mm->arg_start = current->mm->start_stack -
+ (MAX_ARG_PAGES * PAGE_SIZE - bprm->p);
#endif
p = (char __user *) current->mm->arg_start;
@@ -606,7 +637,7 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
mm->start_stack = (unsigned long) sp;
return 0;
-} /* end create_elf_fdpic_tables() */
+}
/*****************************************************************************/
/*
@@ -614,7 +645,8 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
* the stack
*/
#ifndef CONFIG_MMU
-static int elf_fdpic_transfer_args_to_stack(struct linux_binprm *bprm, unsigned long *_sp)
+static int elf_fdpic_transfer_args_to_stack(struct linux_binprm *bprm,
+ unsigned long *_sp)
{
unsigned long index, stop, sp;
char *src;
@@ -635,9 +667,9 @@ static int elf_fdpic_transfer_args_to_stack(struct linux_binprm *bprm, unsigned
*_sp = (*_sp - (MAX_ARG_PAGES * PAGE_SIZE - bprm->p)) & ~15;
- out:
+out:
return ret;
-} /* end elf_fdpic_transfer_args_to_stack() */
+}
#endif
/*****************************************************************************/
@@ -712,17 +744,18 @@ static int elf_fdpic_map_file(struct elf_fdpic_params *params,
seg = loadmap->segs;
for (loop = loadmap->nsegs; loop > 0; loop--, seg++) {
if (params->hdr.e_entry >= seg->p_vaddr &&
- params->hdr.e_entry < seg->p_vaddr + seg->p_memsz
- ) {
+ params->hdr.e_entry < seg->p_vaddr + seg->p_memsz) {
params->entry_addr =
- (params->hdr.e_entry - seg->p_vaddr) + seg->addr;
+ (params->hdr.e_entry - seg->p_vaddr) +
+ seg->addr;
break;
}
}
}
/* determine where the program header table has wound up if mapped */
- stop = params->hdr.e_phoff + params->hdr.e_phnum * sizeof (struct elf_phdr);
+ stop = params->hdr.e_phoff;
+ stop += params->hdr.e_phnum * sizeof (struct elf_phdr);
phdr = params->phdrs;
for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) {
@@ -736,9 +769,11 @@ static int elf_fdpic_map_file(struct elf_fdpic_params *params,
seg = loadmap->segs;
for (loop = loadmap->nsegs; loop > 0; loop--, seg++) {
if (phdr->p_vaddr >= seg->p_vaddr &&
- phdr->p_vaddr + phdr->p_filesz <= seg->p_vaddr + seg->p_memsz
- ) {
- params->ph_addr = (phdr->p_vaddr - seg->p_vaddr) + seg->addr +
+ phdr->p_vaddr + phdr->p_filesz <=
+ seg->p_vaddr + seg->p_memsz) {
+ params->ph_addr =
+ (phdr->p_vaddr - seg->p_vaddr) +
+ seg->addr +
params->hdr.e_phoff - phdr->p_offset;
break;
}
@@ -755,18 +790,22 @@ static int elf_fdpic_map_file(struct elf_fdpic_params *params,
seg = loadmap->segs;
for (loop = loadmap->nsegs; loop > 0; loop--, seg++) {
if (phdr->p_vaddr >= seg->p_vaddr &&
- phdr->p_vaddr + phdr->p_memsz <= seg->p_vaddr + seg->p_memsz
- ) {
- params->dynamic_addr = (phdr->p_vaddr - seg->p_vaddr) + seg->addr;
-
- /* check the dynamic section contains at least one item, and that
- * the last item is a NULL entry */
+ phdr->p_vaddr + phdr->p_memsz <=
+ seg->p_vaddr + seg->p_memsz) {
+ params->dynamic_addr =
+ (phdr->p_vaddr - seg->p_vaddr) +
+ seg->addr;
+
+ /* check the dynamic section contains at least
+ * one item, and that the last item is a NULL
+ * entry */
if (phdr->p_memsz == 0 ||
phdr->p_memsz % sizeof(Elf32_Dyn) != 0)
goto dynamic_error;
tmp = phdr->p_memsz / sizeof(Elf32_Dyn);
- if (((Elf32_Dyn *) params->dynamic_addr)[tmp - 1].d_tag != 0)
+ if (((Elf32_Dyn *)
+ params->dynamic_addr)[tmp - 1].d_tag != 0)
goto dynamic_error;
break;
}
@@ -775,8 +814,8 @@ static int elf_fdpic_map_file(struct elf_fdpic_params *params,
}
/* now elide adjacent segments in the load map on MMU linux
- * - on uClinux the holes between may actually be filled with system stuff or stuff from
- * other processes
+ * - on uClinux the holes between may actually be filled with system
+ * stuff or stuff from other processes
*/
#ifdef CONFIG_MMU
nloads = loadmap->nsegs;
@@ -787,7 +826,9 @@ static int elf_fdpic_map_file(struct elf_fdpic_params *params,
if (seg->p_vaddr - mseg->p_vaddr == seg->addr - mseg->addr) {
load_addr = PAGE_ALIGN(mseg->addr + mseg->p_memsz);
if (load_addr == (seg->addr & PAGE_MASK)) {
- mseg->p_memsz += load_addr - (mseg->addr + mseg->p_memsz);
+ mseg->p_memsz +=
+ load_addr -
+ (mseg->addr + mseg->p_memsz);
mseg->p_memsz += seg->addr & ~PAGE_MASK;
mseg->p_memsz += seg->p_memsz;
loadmap->nsegs--;
@@ -815,20 +856,21 @@ static int elf_fdpic_map_file(struct elf_fdpic_params *params,
return 0;
- dynamic_error:
+dynamic_error:
printk("ELF FDPIC %s with invalid DYNAMIC section (inode=%lu)\n",
what, file->f_dentry->d_inode->i_ino);
return -ELIBBAD;
-} /* end elf_fdpic_map_file() */
+}
/*****************************************************************************/
/*
* map a file with constant displacement under uClinux
*/
#ifndef CONFIG_MMU
-static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *params,
- struct file *file,
- struct mm_struct *mm)
+static int elf_fdpic_map_file_constdisp_on_uclinux(
+ struct elf_fdpic_params *params,
+ struct file *file,
+ struct mm_struct *mm)
{
struct elf32_fdpic_loadseg *seg;
struct elf32_phdr *phdr;
@@ -839,7 +881,8 @@ static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *para
load_addr = params->load_addr;
seg = params->loadmap->segs;
- /* determine the bounds of the contiguous overall allocation we must make */
+ /* determine the bounds of the contiguous overall allocation we must
+ * make */
phdr = params->phdrs;
for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) {
if (params->phdrs[loop].p_type != PT_LOAD)
@@ -860,7 +903,7 @@ static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *para
maddr = do_mmap(NULL, load_addr, top - base,
PROT_READ | PROT_WRITE | PROT_EXEC, mflags, 0);
up_write(&mm->mmap_sem);
- if (IS_ERR((void *) maddr))
+ if (IS_ERR_VALUE(maddr))
return (int) maddr;
if (load_addr != 0)
@@ -878,7 +921,8 @@ static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *para
seg->p_vaddr = phdr->p_vaddr;
seg->p_memsz = phdr->p_memsz;
- ret = file->f_op->read(file, (void *) seg->addr, phdr->p_filesz, &fpos);
+ ret = file->f_op->read(file, (void *) seg->addr,
+ phdr->p_filesz, &fpos);
if (ret < 0)
return ret;
@@ -895,8 +939,7 @@ static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *para
if (phdr->p_flags & PF_X) {
mm->start_code = seg->addr;
mm->end_code = seg->addr + phdr->p_memsz;
- }
- else if (!mm->start_data) {
+ } else if (!mm->start_data) {
mm->start_data = seg->addr;
#ifndef CONFIG_MMU
mm->end_data = seg->addr + phdr->p_memsz;
@@ -913,7 +956,7 @@ static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *para
}
return 0;
-} /* end elf_fdpic_map_file_constdisp_on_uclinux() */
+}
#endif
/*****************************************************************************/
@@ -974,14 +1017,14 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
case ELF_FDPIC_FLAG_CONSTDISP:
/* constant displacement
- * - can be mapped anywhere, but must be mapped as a unit
+ * - can be mapped anywhere, but must be mapped as a
+ * unit
*/
if (!dvset) {
maddr = load_addr;
delta_vaddr = phdr->p_vaddr;
dvset = 1;
- }
- else {
+ } else {
maddr = load_addr + phdr->p_vaddr - delta_vaddr;
flags |= MAP_FIXED;
}
@@ -1005,13 +1048,14 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
up_write(&mm->mmap_sem);
kdebug("mmap[%d] <file> sz=%lx pr=%x fl=%x of=%lx --> %08lx",
- loop, phdr->p_memsz + disp, prot, flags, phdr->p_offset - disp,
- maddr);
+ loop, phdr->p_memsz + disp, prot, flags,
+ phdr->p_offset - disp, maddr);
- if (IS_ERR((void *) maddr))
+ if (IS_ERR_VALUE(maddr))
return (int) maddr;
- if ((params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) == ELF_FDPIC_FLAG_CONTIGUOUS)
+ if ((params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) ==
+ ELF_FDPIC_FLAG_CONTIGUOUS)
load_addr += PAGE_ALIGN(phdr->p_memsz + disp);
seg->addr = maddr + disp;
@@ -1022,7 +1066,8 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
if (phdr->p_offset == 0)
params->elfhdr_addr = seg->addr;
- /* clear the bit between beginning of mapping and beginning of PT_LOAD */
+ /* clear the bit between beginning of mapping and beginning of
+ * PT_LOAD */
if (prot & PROT_WRITE && disp > 0) {
kdebug("clear[%d] ad=%lx sz=%lx", loop, maddr, disp);
clear_user((void __user *) maddr, disp);
@@ -1038,19 +1083,20 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
excess1 = PAGE_SIZE - ((maddr + phdr->p_filesz) & ~PAGE_MASK);
#ifdef CONFIG_MMU
-
if (excess > excess1) {
unsigned long xaddr = maddr + phdr->p_filesz + excess1;
unsigned long xmaddr;
flags |= MAP_FIXED | MAP_ANONYMOUS;
down_write(&mm->mmap_sem);
- xmaddr = do_mmap(NULL, xaddr, excess - excess1, prot, flags, 0);
+ xmaddr = do_mmap(NULL, xaddr, excess - excess1,
+ prot, flags, 0);
up_write(&mm->mmap_sem);
kdebug("mmap[%d] <anon>"
" ad=%lx sz=%lx pr=%x fl=%x of=0 --> %08lx",
- loop, xaddr, excess - excess1, prot, flags, xmaddr);
+ loop, xaddr, excess - excess1, prot, flags,
+ xmaddr);
if (xmaddr != xaddr)
return -ENOMEM;
@@ -1059,7 +1105,8 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
if (prot & PROT_WRITE && excess1 > 0) {
kdebug("clear[%d] ad=%lx sz=%lx",
loop, maddr + phdr->p_filesz, excess1);
- clear_user((void __user *) maddr + phdr->p_filesz, excess1);
+ clear_user((void __user *) maddr + phdr->p_filesz,
+ excess1);
}
#else
@@ -1074,8 +1121,7 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
if (phdr->p_flags & PF_X) {
mm->start_code = maddr;
mm->end_code = maddr + phdr->p_memsz;
- }
- else if (!mm->start_data) {
+ } else if (!mm->start_data) {
mm->start_data = maddr;
mm->end_data = maddr + phdr->p_memsz;
}
@@ -1085,4 +1131,662 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
}
return 0;
-} /* end elf_fdpic_map_file_by_direct_mmap() */
+}
+
+/*****************************************************************************/
+/*
+ * ELF-FDPIC core dumper
+ *
+ * Modelled on fs/exec.c:aout_core_dump()
+ * Jeremy Fitzhardinge <jeremy@sw.oz.au>
+ *
+ * Modelled on fs/binfmt_elf.c core dumper
+ */
+#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE)
+
+/*
+ * These are the only things you should do on a core-file: use only these
+ * functions to write out all the necessary info.
+ */
+static int dump_write(struct file *file, const void *addr, int nr)
+{
+ return file->f_op->write(file, addr, nr, &file->f_pos) == nr;
+}
+
+static int dump_seek(struct file *file, loff_t off)
+{
+ if (file->f_op->llseek) {
+ if (file->f_op->llseek(file, off, SEEK_SET) != off)
+ return 0;
+ } else {
+ file->f_pos = off;
+ }
+ return 1;
+}
+
+/*
+ * Decide whether a segment is worth dumping; default is yes to be
+ * sure (missing info is worse than too much; etc).
+ * Personally I'd include everything, and use the coredump limit...
+ *
+ * I think we should skip something. But I am not sure how. H.J.
+ */
+static int maydump(struct vm_area_struct *vma)
+{
+ /* Do not dump I/O mapped devices or special mappings */
+ if (vma->vm_flags & (VM_IO | VM_RESERVED)) {
+ kdcore("%08lx: %08lx: no (IO)", vma->vm_start, vma->vm_flags);
+ return 0;
+ }
+
+ /* If we may not read the contents, don't allow us to dump
+ * them either. "dump_write()" can't handle it anyway.
+ */
+ if (!(vma->vm_flags & VM_READ)) {
+ kdcore("%08lx: %08lx: no (!read)", vma->vm_start, vma->vm_flags);
+ return 0;
+ }
+
+ /* Dump shared memory only if mapped from an anonymous file. */
+ if (vma->vm_flags & VM_SHARED) {
+ if (vma->vm_file->f_dentry->d_inode->i_nlink == 0) {
+ kdcore("%08lx: %08lx: no (share)", vma->vm_start, vma->vm_flags);
+ return 1;
+ }
+
+ kdcore("%08lx: %08lx: no (share)", vma->vm_start, vma->vm_flags);
+ return 0;
+ }
+
+#ifdef CONFIG_MMU
+ /* If it hasn't been written to, don't write it out */
+ if (!vma->anon_vma) {
+ kdcore("%08lx: %08lx: no (!anon)", vma->vm_start, vma->vm_flags);
+ return 0;
+ }
+#endif
+
+ kdcore("%08lx: %08lx: yes", vma->vm_start, vma->vm_flags);
+ return 1;
+}
+
+/* An ELF note in memory */
+struct memelfnote
+{
+ const char *name;
+ int type;
+ unsigned int datasz;
+ void *data;
+};
+
+static int notesize(struct memelfnote *en)
+{
+ int sz;
+
+ sz = sizeof(struct elf_note);
+ sz += roundup(strlen(en->name) + 1, 4);
+ sz += roundup(en->datasz, 4);
+
+ return sz;
+}
+
+/* #define DEBUG */
+
+#define DUMP_WRITE(addr, nr) \
+ do { if (!dump_write(file, (addr), (nr))) return 0; } while(0)
+#define DUMP_SEEK(off) \
+ do { if (!dump_seek(file, (off))) return 0; } while(0)
+
+static int writenote(struct memelfnote *men, struct file *file)
+{
+ struct elf_note en;
+
+ en.n_namesz = strlen(men->name) + 1;
+ en.n_descsz = men->datasz;
+ en.n_type = men->type;
+
+ DUMP_WRITE(&en, sizeof(en));
+ DUMP_WRITE(men->name, en.n_namesz);
+ /* XXX - cast from long long to long to avoid need for libgcc.a */
+ DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX */
+ DUMP_WRITE(men->data, men->datasz);
+ DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX */
+
+ return 1;
+}
+#undef DUMP_WRITE
+#undef DUMP_SEEK
+
+#define DUMP_WRITE(addr, nr) \
+ if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
+ goto end_coredump;
+#define DUMP_SEEK(off) \
+ if (!dump_seek(file, (off))) \
+ goto end_coredump;
+
+static inline void fill_elf_fdpic_header(struct elfhdr *elf, int segs)
+{
+ memcpy(elf->e_ident, ELFMAG, SELFMAG);
+ elf->e_ident[EI_CLASS] = ELF_CLASS;
+ elf->e_ident[EI_DATA] = ELF_DATA;
+ elf->e_ident[EI_VERSION] = EV_CURRENT;
+ elf->e_ident[EI_OSABI] = ELF_OSABI;
+ memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
+
+ elf->e_type = ET_CORE;
+ elf->e_machine = ELF_ARCH;
+ elf->e_version = EV_CURRENT;
+ elf->e_entry = 0;
+ elf->e_phoff = sizeof(struct elfhdr);
+ elf->e_shoff = 0;
+ elf->e_flags = ELF_FDPIC_CORE_EFLAGS;
+ elf->e_ehsize = sizeof(struct elfhdr);
+ elf->e_phentsize = sizeof(struct elf_phdr);
+ elf->e_phnum = segs;
+ elf->e_shentsize = 0;
+ elf->e_shnum = 0;
+ elf->e_shstrndx = 0;
+ return;
+}
+
+static inline void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, loff_t offset)
+{
+ phdr->p_type = PT_NOTE;
+ phdr->p_offset = offset;
+ phdr->p_vaddr = 0;
+ phdr->p_paddr = 0;
+ phdr->p_filesz = sz;
+ phdr->p_memsz = 0;
+ phdr->p_flags = 0;
+ phdr->p_align = 0;
+ return;
+}
+
+static inline void fill_note(struct memelfnote *note, const char *name, int type,
+ unsigned int sz, void *data)
+{
+ note->name = name;
+ note->type = type;
+ note->datasz = sz;
+ note->data = data;
+ return;
+}
+
+/*
+ * fill up all the fields in prstatus from the given task struct, except
+ * registers which need to be filled up seperately.
+ */
+static void fill_prstatus(struct elf_prstatus *prstatus,
+ struct task_struct *p, long signr)
+{
+ prstatus->pr_info.si_signo = prstatus->pr_cursig = signr;
+ prstatus->pr_sigpend = p->pending.signal.sig[0];
+ prstatus->pr_sighold = p->blocked.sig[0];
+ prstatus->pr_pid = p->pid;
+ prstatus->pr_ppid = p->parent->pid;
+ prstatus->pr_pgrp = process_group(p);
+ prstatus->pr_sid = p->signal->session;
+ if (thread_group_leader(p)) {
+ /*
+ * This is the record for the group leader. Add in the
+ * cumulative times of previous dead threads. This total
+ * won't include the time of each live thread whose state
+ * is included in the core dump. The final total reported
+ * to our parent process when it calls wait4 will include
+ * those sums as well as the little bit more time it takes
+ * this and each other thread to finish dying after the
+ * core dump synchronization phase.
+ */
+ cputime_to_timeval(cputime_add(p->utime, p->signal->utime),
+ &prstatus->pr_utime);
+ cputime_to_timeval(cputime_add(p->stime, p->signal->stime),
+ &prstatus->pr_stime);
+ } else {
+ cputime_to_timeval(p->utime, &prstatus->pr_utime);
+ cputime_to_timeval(p->stime, &prstatus->pr_stime);
+ }
+ cputime_to_timeval(p->signal->cutime, &prstatus->pr_cutime);
+ cputime_to_timeval(p->signal->cstime, &prstatus->pr_cstime);
+
+ prstatus->pr_exec_fdpic_loadmap = p->mm->context.exec_fdpic_loadmap;
+ prstatus->pr_interp_fdpic_loadmap = p->mm->context.interp_fdpic_loadmap;
+}
+
+static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
+ struct mm_struct *mm)
+{
+ unsigned int i, len;
+
+ /* first copy the parameters from user space */
+ memset(psinfo, 0, sizeof(struct elf_prpsinfo));
+
+ len = mm->arg_end - mm->arg_start;
+ if (len >= ELF_PRARGSZ)
+ len = ELF_PRARGSZ - 1;
+ if (copy_from_user(&psinfo->pr_psargs,
+ (const char __user *) mm->arg_start, len))
+ return -EFAULT;
+ for (i = 0; i < len; i++)
+ if (psinfo->pr_psargs[i] == 0)
+ psinfo->pr_psargs[i] = ' ';
+ psinfo->pr_psargs[len] = 0;
+
+ psinfo->pr_pid = p->pid;
+ psinfo->pr_ppid = p->parent->pid;
+ psinfo->pr_pgrp = process_group(p);
+ psinfo->pr_sid = p->signal->session;
+
+ i = p->state ? ffz(~p->state) + 1 : 0;
+ psinfo->pr_state = i;
+ psinfo->pr_sname = (i > 5) ? '.' : "RSDTZW"[i];
+ psinfo->pr_zomb = psinfo->pr_sname == 'Z';
+ psinfo->pr_nice = task_nice(p);
+ psinfo->pr_flag = p->flags;
+ SET_UID(psinfo->pr_uid, p->uid);
+ SET_GID(psinfo->pr_gid, p->gid);
+ strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname));
+
+ return 0;
+}
+
+/* Here is the structure in which status of each thread is captured. */
+struct elf_thread_status
+{
+ struct list_head list;
+ struct elf_prstatus prstatus; /* NT_PRSTATUS */
+ elf_fpregset_t fpu; /* NT_PRFPREG */
+ struct task_struct *thread;
+#ifdef ELF_CORE_COPY_XFPREGS
+ elf_fpxregset_t xfpu; /* NT_PRXFPREG */
+#endif
+ struct memelfnote notes[3];
+ int num_notes;
+};
+
+/*
+ * In order to add the specific thread information for the elf file format,
+ * we need to keep a linked list of every thread's pr_status and then create
+ * a single section for them in the final core file.
+ */
+static int elf_dump_thread_status(long signr, struct elf_thread_status *t)
+{
+ struct task_struct *p = t->thread;
+ int sz = 0;
+
+ t->num_notes = 0;
+
+ fill_prstatus(&t->prstatus, p, signr);
+ elf_core_copy_task_regs(p, &t->prstatus.pr_reg);
+
+ fill_note(&t->notes[0], "CORE", NT_PRSTATUS, sizeof(t->prstatus),
+ &t->prstatus);
+ t->num_notes++;
+ sz += notesize(&t->notes[0]);
+
+ t->prstatus.pr_fpvalid = elf_core_copy_task_fpregs(p, NULL, &t->fpu);
+ if (t->prstatus.pr_fpvalid) {
+ fill_note(&t->notes[1], "CORE", NT_PRFPREG, sizeof(t->fpu),
+ &t->fpu);
+ t->num_notes++;
+ sz += notesize(&t->notes[1]);
+ }
+
+#ifdef ELF_CORE_COPY_XFPREGS
+ if (elf_core_copy_task_xfpregs(p, &t->xfpu)) {
+ fill_note(&t->notes[2], "LINUX", NT_PRXFPREG, sizeof(t->xfpu),
+ &t->xfpu);
+ t->num_notes++;
+ sz += notesize(&t->notes[2]);
+ }
+#endif
+ return sz;
+}
+
+/*
+ * dump the segments for an MMU process
+ */
+#ifdef CONFIG_MMU
+static int elf_fdpic_dump_segments(struct file *file, struct mm_struct *mm,
+ size_t *size, unsigned long *limit)
+{
+ struct vm_area_struct *vma;
+
+ for (vma = current->mm->mmap; vma; vma = vma->vm_next) {
+ unsigned long addr;
+
+ if (!maydump(vma))
+ continue;
+
+ for (addr = vma->vm_start;
+ addr < vma->vm_end;
+ addr += PAGE_SIZE
+ ) {
+ struct vm_area_struct *vma;
+ struct page *page;
+
+ if (get_user_pages(current, current->mm, addr, 1, 0, 1,
+ &page, &vma) <= 0) {
+ DUMP_SEEK(file->f_pos + PAGE_SIZE);
+ }
+ else if (page == ZERO_PAGE(addr)) {
+ DUMP_SEEK(file->f_pos + PAGE_SIZE);
+ page_cache_release(page);
+ }
+ else {
+ void *kaddr;
+
+ flush_cache_page(vma, addr, page_to_pfn(page));
+ kaddr = kmap(page);
+ if ((*size += PAGE_SIZE) > *limit ||
+ !dump_write(file, kaddr, PAGE_SIZE)
+ ) {
+ kunmap(page);
+ page_cache_release(page);
+ return -EIO;
+ }
+ kunmap(page);
+ page_cache_release(page);
+ }
+ }
+ }
+
+ return 0;
+
+end_coredump:
+ return -EFBIG;
+}
+#endif
+
+/*
+ * dump the segments for a NOMMU process
+ */
+#ifndef CONFIG_MMU
+static int elf_fdpic_dump_segments(struct file *file, struct mm_struct *mm,
+ size_t *size, unsigned long *limit)
+{
+ struct vm_list_struct *vml;
+
+ for (vml = current->mm->context.vmlist; vml; vml = vml->next) {
+ struct vm_area_struct *vma = vml->vma;
+
+ if (!maydump(vma))
+ continue;
+
+ if ((*size += PAGE_SIZE) > *limit)
+ return -EFBIG;
+
+ if (!dump_write(file, (void *) vma->vm_start,
+ vma->vm_end - vma->vm_start))
+ return -EIO;
+ }
+
+ return 0;
+}
+#endif
+
+/*
+ * Actual dumper
+ *
+ * This is a two-pass process; first we find the offsets of the bits,
+ * and then they are actually written out. If we run out of core limit
+ * we just truncate.
+ */
+static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
+ struct file *file)
+{
+#define NUM_NOTES 6
+ int has_dumped = 0;
+ mm_segment_t fs;
+ int segs;
+ size_t size = 0;
+ int i;
+ struct vm_area_struct *vma;
+ struct elfhdr *elf = NULL;
+ loff_t offset = 0, dataoff;
+ unsigned long limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
+ int numnote;
+ struct memelfnote *notes = NULL;
+ struct elf_prstatus *prstatus = NULL; /* NT_PRSTATUS */
+ struct elf_prpsinfo *psinfo = NULL; /* NT_PRPSINFO */
+ struct task_struct *g, *p;
+ LIST_HEAD(thread_list);
+ struct list_head *t;
+ elf_fpregset_t *fpu = NULL;
+#ifdef ELF_CORE_COPY_XFPREGS
+ elf_fpxregset_t *xfpu = NULL;
+#endif
+ int thread_status_size = 0;
+#ifndef CONFIG_MMU
+ struct vm_list_struct *vml;
+#endif
+ elf_addr_t *auxv;
+
+ /*
+ * We no longer stop all VM operations.
+ *
+ * This is because those proceses that could possibly change map_count
+ * or the mmap / vma pages are now blocked in do_exit on current
+ * finishing this core dump.
+ *
+ * Only ptrace can touch these memory addresses, but it doesn't change
+ * the map_count or the pages allocated. So no possibility of crashing
+ * exists while dumping the mm->vm_next areas to the core file.
+ */
+
+ /* alloc memory for large data structures: too large to be on stack */
+ elf = kmalloc(sizeof(*elf), GFP_KERNEL);
+ if (!elf)
+ goto cleanup;
+ prstatus = kzalloc(sizeof(*prstatus), GFP_KERNEL);
+ if (!prstatus)
+ goto cleanup;
+ psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL);
+ if (!psinfo)
+ goto cleanup;
+ notes = kmalloc(NUM_NOTES * sizeof(struct memelfnote), GFP_KERNEL);
+ if (!notes)
+ goto cleanup;
+ fpu = kmalloc(sizeof(*fpu), GFP_KERNEL);
+ if (!fpu)
+ goto cleanup;
+#ifdef ELF_CORE_COPY_XFPREGS
+ xfpu = kmalloc(sizeof(*xfpu), GFP_KERNEL);
+ if (!xfpu)
+ goto cleanup;
+#endif
+
+ if (signr) {
+ struct elf_thread_status *tmp;
+ read_lock(&tasklist_lock);
+ do_each_thread(g,p)
+ if (current->mm == p->mm && current != p) {
+ tmp = kzalloc(sizeof(*tmp), GFP_ATOMIC);
+ if (!tmp) {
+ read_unlock(&tasklist_lock);
+ goto cleanup;
+ }
+ INIT_LIST_HEAD(&tmp->list);
+ tmp->thread = p;
+ list_add(&tmp->list, &thread_list);
+ }
+ while_each_thread(g,p);
+ read_unlock(&tasklist_lock);
+ list_for_each(t, &thread_list) {
+ struct elf_thread_status *tmp;
+ int sz;
+
+ tmp = list_entry(t, struct elf_thread_status, list);
+ sz = elf_dump_thread_status(signr, tmp);
+ thread_status_size += sz;
+ }
+ }
+
+ /* now collect the dump for the current */
+ fill_prstatus(prstatus, current, signr);
+ elf_core_copy_regs(&prstatus->pr_reg, regs);
+
+#ifdef CONFIG_MMU
+ segs = current->mm->map_count;
+#else
+ segs = 0;
+ for (vml = current->mm->context.vmlist; vml; vml = vml->next)
+ segs++;
+#endif
+#ifdef ELF_CORE_EXTRA_PHDRS
+ segs += ELF_CORE_EXTRA_PHDRS;
+#endif
+
+ /* Set up header */
+ fill_elf_fdpic_header(elf, segs + 1); /* including notes section */
+
+ has_dumped = 1;
+ current->flags |= PF_DUMPCORE;
+
+ /*
+ * Set up the notes in similar form to SVR4 core dumps made
+ * with info from their /proc.
+ */
+
+ fill_note(notes + 0, "CORE", NT_PRSTATUS, sizeof(*prstatus), prstatus);
+ fill_psinfo(psinfo, current->group_leader, current->mm);
+ fill_note(notes + 1, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo);
+
+ numnote = 2;
+
+ auxv = (elf_addr_t *) current->mm->saved_auxv;
+
+ i = 0;
+ do
+ i += 2;
+ while (auxv[i - 2] != AT_NULL);
+ fill_note(&notes[numnote++], "CORE", NT_AUXV,
+ i * sizeof(elf_addr_t), auxv);
+
+ /* Try to dump the FPU. */
+ if ((prstatus->pr_fpvalid =
+ elf_core_copy_task_fpregs(current, regs, fpu)))
+ fill_note(notes + numnote++,
+ "CORE", NT_PRFPREG, sizeof(*fpu), fpu);
+#ifdef ELF_CORE_COPY_XFPREGS
+ if (elf_core_copy_task_xfpregs(current, xfpu))
+ fill_note(notes + numnote++,
+ "LINUX", NT_PRXFPREG, sizeof(*xfpu), xfpu);
+#endif
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ DUMP_WRITE(elf, sizeof(*elf));
+ offset += sizeof(*elf); /* Elf header */
+ offset += (segs+1) * sizeof(struct elf_phdr); /* Program headers */
+
+ /* Write notes phdr entry */
+ {
+ struct elf_phdr phdr;
+ int sz = 0;
+
+ for (i = 0; i < numnote; i++)
+ sz += notesize(notes + i);
+
+ sz += thread_status_size;
+
+ fill_elf_note_phdr(&phdr, sz, offset);
+ offset += sz;
+ DUMP_WRITE(&phdr, sizeof(phdr));
+ }
+
+ /* Page-align dumped data */
+ dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
+
+ /* write program headers for segments dump */
+ for (
+#ifdef CONFIG_MMU
+ vma = current->mm->mmap; vma; vma = vma->vm_next
+#else
+ vml = current->mm->context.vmlist; vml; vml = vml->next
+#endif
+ ) {
+ struct elf_phdr phdr;
+ size_t sz;
+
+#ifndef CONFIG_MMU
+ vma = vml->vma;
+#endif
+
+ sz = vma->vm_end - vma->vm_start;
+
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = offset;
+ phdr.p_vaddr = vma->vm_start;
+ phdr.p_paddr = 0;
+ phdr.p_filesz = maydump(vma) ? sz : 0;
+ phdr.p_memsz = sz;
+ offset += phdr.p_filesz;
+ phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0;
+ if (vma->vm_flags & VM_WRITE)
+ phdr.p_flags |= PF_W;
+ if (vma->vm_flags & VM_EXEC)
+ phdr.p_flags |= PF_X;
+ phdr.p_align = ELF_EXEC_PAGESIZE;
+
+ DUMP_WRITE(&phdr, sizeof(phdr));
+ }
+
+#ifdef ELF_CORE_WRITE_EXTRA_PHDRS
+ ELF_CORE_WRITE_EXTRA_PHDRS;
+#endif
+
+ /* write out the notes section */
+ for (i = 0; i < numnote; i++)
+ if (!writenote(notes + i, file))
+ goto end_coredump;
+
+ /* write out the thread status notes section */
+ list_for_each(t, &thread_list) {
+ struct elf_thread_status *tmp =
+ list_entry(t, struct elf_thread_status, list);
+
+ for (i = 0; i < tmp->num_notes; i++)
+ if (!writenote(&tmp->notes[i], file))
+ goto end_coredump;
+ }
+
+ DUMP_SEEK(dataoff);
+
+ if (elf_fdpic_dump_segments(file, current->mm, &size, &limit) < 0)
+ goto end_coredump;
+
+#ifdef ELF_CORE_WRITE_EXTRA_DATA
+ ELF_CORE_WRITE_EXTRA_DATA;
+#endif
+
+ if (file->f_pos != offset) {
+ /* Sanity check */
+ printk(KERN_WARNING
+ "elf_core_dump: file->f_pos (%lld) != offset (%lld)\n",
+ file->f_pos, offset);
+ }
+
+end_coredump:
+ set_fs(fs);
+
+cleanup:
+ while (!list_empty(&thread_list)) {
+ struct list_head *tmp = thread_list.next;
+ list_del(tmp);
+ kfree(list_entry(tmp, struct elf_thread_status, list));
+ }
+
+ kfree(elf);
+ kfree(prstatus);
+ kfree(psinfo);
+ kfree(notes);
+ kfree(fpu);
+#ifdef ELF_CORE_COPY_XFPREGS
+ kfree(xfpu);
+#endif
+ return has_dumped;
+#undef NUM_NOTES
+}
+
+#endif /* USE_ELF_CORE_DUMP */
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 9633a490dab..045f98854f1 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -739,7 +739,7 @@ static int bd_claim_by_kobject(struct block_device *bdev, void *holder,
if (!bo)
return -ENOMEM;
- mutex_lock(&bdev->bd_mutex);
+ mutex_lock_nested(&bdev->bd_mutex, BD_MUTEX_PARTITION);
res = bd_claim(bdev, holder);
if (res || !add_bd_holder(bdev, bo))
free_bd_holder(bo);
@@ -764,7 +764,7 @@ static void bd_release_from_kobject(struct block_device *bdev,
if (!kobj)
return;
- mutex_lock(&bdev->bd_mutex);
+ mutex_lock_nested(&bdev->bd_mutex, BD_MUTEX_PARTITION);
bd_release(bdev);
if ((bo = del_bd_holder(bdev, kobj)))
free_bd_holder(bo);
@@ -822,6 +822,22 @@ struct block_device *open_by_devnum(dev_t dev, unsigned mode)
EXPORT_SYMBOL(open_by_devnum);
+static int
+blkdev_get_partition(struct block_device *bdev, mode_t mode, unsigned flags);
+
+struct block_device *open_partition_by_devnum(dev_t dev, unsigned mode)
+{
+ struct block_device *bdev = bdget(dev);
+ int err = -ENOMEM;
+ int flags = mode & FMODE_WRITE ? O_RDWR : O_RDONLY;
+ if (bdev)
+ err = blkdev_get_partition(bdev, mode, flags);
+ return err ? ERR_PTR(err) : bdev;
+}
+
+EXPORT_SYMBOL(open_partition_by_devnum);
+
+
/*
* This routine checks whether a removable media has been changed,
* and invalidates all buffer-cache-entries in that case. This
@@ -868,7 +884,66 @@ void bd_set_size(struct block_device *bdev, loff_t size)
}
EXPORT_SYMBOL(bd_set_size);
-static int do_open(struct block_device *bdev, struct file *file)
+static int __blkdev_put(struct block_device *bdev, unsigned int subclass)
+{
+ int ret = 0;
+ struct inode *bd_inode = bdev->bd_inode;
+ struct gendisk *disk = bdev->bd_disk;
+
+ mutex_lock_nested(&bdev->bd_mutex, subclass);
+ lock_kernel();
+ if (!--bdev->bd_openers) {
+ sync_blockdev(bdev);
+ kill_bdev(bdev);
+ }
+ if (bdev->bd_contains == bdev) {
+ if (disk->fops->release)
+ ret = disk->fops->release(bd_inode, NULL);
+ } else {
+ mutex_lock_nested(&bdev->bd_contains->bd_mutex,
+ subclass + 1);
+ bdev->bd_contains->bd_part_count--;
+ mutex_unlock(&bdev->bd_contains->bd_mutex);
+ }
+ if (!bdev->bd_openers) {
+ struct module *owner = disk->fops->owner;
+
+ put_disk(disk);
+ module_put(owner);
+
+ if (bdev->bd_contains != bdev) {
+ kobject_put(&bdev->bd_part->kobj);
+ bdev->bd_part = NULL;
+ }
+ bdev->bd_disk = NULL;
+ bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info;
+ if (bdev != bdev->bd_contains)
+ __blkdev_put(bdev->bd_contains, subclass + 1);
+ bdev->bd_contains = NULL;
+ }
+ unlock_kernel();
+ mutex_unlock(&bdev->bd_mutex);
+ bdput(bdev);
+ return ret;
+}
+
+int blkdev_put(struct block_device *bdev)
+{
+ return __blkdev_put(bdev, BD_MUTEX_NORMAL);
+}
+EXPORT_SYMBOL(blkdev_put);
+
+int blkdev_put_partition(struct block_device *bdev)
+{
+ return __blkdev_put(bdev, BD_MUTEX_PARTITION);
+}
+EXPORT_SYMBOL(blkdev_put_partition);
+
+static int
+blkdev_get_whole(struct block_device *bdev, mode_t mode, unsigned flags);
+
+static int
+do_open(struct block_device *bdev, struct file *file, unsigned int subclass)
{
struct module *owner = NULL;
struct gendisk *disk;
@@ -885,7 +960,8 @@ static int do_open(struct block_device *bdev, struct file *file)
}
owner = disk->fops->owner;
- mutex_lock(&bdev->bd_mutex);
+ mutex_lock_nested(&bdev->bd_mutex, subclass);
+
if (!bdev->bd_openers) {
bdev->bd_disk = disk;
bdev->bd_contains = bdev;
@@ -912,11 +988,11 @@ static int do_open(struct block_device *bdev, struct file *file)
ret = -ENOMEM;
if (!whole)
goto out_first;
- ret = blkdev_get(whole, file->f_mode, file->f_flags);
+ ret = blkdev_get_whole(whole, file->f_mode, file->f_flags);
if (ret)
goto out_first;
bdev->bd_contains = whole;
- mutex_lock(&whole->bd_mutex);
+ mutex_lock_nested(&whole->bd_mutex, BD_MUTEX_WHOLE);
whole->bd_part_count++;
p = disk->part[part - 1];
bdev->bd_inode->i_data.backing_dev_info =
@@ -944,7 +1020,8 @@ static int do_open(struct block_device *bdev, struct file *file)
if (bdev->bd_invalidated)
rescan_partitions(bdev->bd_disk, bdev);
} else {
- mutex_lock(&bdev->bd_contains->bd_mutex);
+ mutex_lock_nested(&bdev->bd_contains->bd_mutex,
+ BD_MUTEX_PARTITION);
bdev->bd_contains->bd_part_count++;
mutex_unlock(&bdev->bd_contains->bd_mutex);
}
@@ -958,7 +1035,7 @@ out_first:
bdev->bd_disk = NULL;
bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info;
if (bdev != bdev->bd_contains)
- blkdev_put(bdev->bd_contains);
+ __blkdev_put(bdev->bd_contains, BD_MUTEX_WHOLE);
bdev->bd_contains = NULL;
put_disk(disk);
module_put(owner);
@@ -985,11 +1062,49 @@ int blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags)
fake_file.f_dentry = &fake_dentry;
fake_dentry.d_inode = bdev->bd_inode;
- return do_open(bdev, &fake_file);
+ return do_open(bdev, &fake_file, BD_MUTEX_NORMAL);
}
EXPORT_SYMBOL(blkdev_get);
+static int
+blkdev_get_whole(struct block_device *bdev, mode_t mode, unsigned flags)
+{
+ /*
+ * This crockload is due to bad choice of ->open() type.
+ * It will go away.
+ * For now, block device ->open() routine must _not_
+ * examine anything in 'inode' argument except ->i_rdev.
+ */
+ struct file fake_file = {};
+ struct dentry fake_dentry = {};
+ fake_file.f_mode = mode;
+ fake_file.f_flags = flags;
+ fake_file.f_dentry = &fake_dentry;
+ fake_dentry.d_inode = bdev->bd_inode;
+
+ return do_open(bdev, &fake_file, BD_MUTEX_WHOLE);
+}
+
+static int
+blkdev_get_partition(struct block_device *bdev, mode_t mode, unsigned flags)
+{
+ /*
+ * This crockload is due to bad choice of ->open() type.
+ * It will go away.
+ * For now, block device ->open() routine must _not_
+ * examine anything in 'inode' argument except ->i_rdev.
+ */
+ struct file fake_file = {};
+ struct dentry fake_dentry = {};
+ fake_file.f_mode = mode;
+ fake_file.f_flags = flags;
+ fake_file.f_dentry = &fake_dentry;
+ fake_dentry.d_inode = bdev->bd_inode;
+
+ return do_open(bdev, &fake_file, BD_MUTEX_PARTITION);
+}
+
static int blkdev_open(struct inode * inode, struct file * filp)
{
struct block_device *bdev;
@@ -1005,7 +1120,7 @@ static int blkdev_open(struct inode * inode, struct file * filp)
bdev = bd_acquire(inode);
- res = do_open(bdev, filp);
+ res = do_open(bdev, filp, BD_MUTEX_NORMAL);
if (res)
return res;
@@ -1019,51 +1134,6 @@ static int blkdev_open(struct inode * inode, struct file * filp)
return res;
}
-int blkdev_put(struct block_device *bdev)
-{
- int ret = 0;
- struct inode *bd_inode = bdev->bd_inode;
- struct gendisk *disk = bdev->bd_disk;
-
- mutex_lock(&bdev->bd_mutex);
- lock_kernel();
- if (!--bdev->bd_openers) {
- sync_blockdev(bdev);
- kill_bdev(bdev);
- }
- if (bdev->bd_contains == bdev) {
- if (disk->fops->release)
- ret = disk->fops->release(bd_inode, NULL);
- } else {
- mutex_lock(&bdev->bd_contains->bd_mutex);
- bdev->bd_contains->bd_part_count--;
- mutex_unlock(&bdev->bd_contains->bd_mutex);
- }
- if (!bdev->bd_openers) {
- struct module *owner = disk->fops->owner;
-
- put_disk(disk);
- module_put(owner);
-
- if (bdev->bd_contains != bdev) {
- kobject_put(&bdev->bd_part->kobj);
- bdev->bd_part = NULL;
- }
- bdev->bd_disk = NULL;
- bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info;
- if (bdev != bdev->bd_contains) {
- blkdev_put(bdev->bd_contains);
- }
- bdev->bd_contains = NULL;
- }
- unlock_kernel();
- mutex_unlock(&bdev->bd_mutex);
- bdput(bdev);
- return ret;
-}
-
-EXPORT_SYMBOL(blkdev_put);
-
static int blkdev_close(struct inode * inode, struct file * filp)
{
struct block_device *bdev = I_BDEV(filp->f_mapping->host);
diff --git a/fs/buffer.c b/fs/buffer.c
index 3660dcb9759..71649ef9b65 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -470,13 +470,18 @@ out:
pass does the actual I/O. */
void invalidate_bdev(struct block_device *bdev, int destroy_dirty_buffers)
{
+ struct address_space *mapping = bdev->bd_inode->i_mapping;
+
+ if (mapping->nrpages == 0)
+ return;
+
invalidate_bh_lrus();
/*
* FIXME: what about destroy_dirty_buffers?
* We really want to use invalidate_inode_pages2() for
* that, but not until that's cleaned up.
*/
- invalidate_inode_pages(bdev->bd_inode->i_mapping);
+ invalidate_inode_pages(mapping);
}
/*
diff --git a/fs/char_dev.c b/fs/char_dev.c
index a4cbc6706ef..3483d3cf808 100644
--- a/fs/char_dev.c
+++ b/fs/char_dev.c
@@ -182,6 +182,28 @@ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
return 0;
}
+/**
+ * register_chrdev() - Register a major number for character devices.
+ * @major: major device number or 0 for dynamic allocation
+ * @name: name of this range of devices
+ * @fops: file operations associated with this devices
+ *
+ * If @major == 0 this functions will dynamically allocate a major and return
+ * its number.
+ *
+ * If @major > 0 this function will attempt to reserve a device with the given
+ * major number and will return zero on success.
+ *
+ * Returns a -ve errno on failure.
+ *
+ * The name of this device has nothing to do with the name of the device in
+ * /dev. It only helps to keep track of the different owners of devices. If
+ * your module name has only one type of devices it's ok to use e.g. the name
+ * of the module here.
+ *
+ * This function registers a range of 256 minor numbers. The first minor number
+ * is 0.
+ */
int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES
index a61d17ed182..0feb3bd49cb 100644
--- a/fs/cifs/CHANGES
+++ b/fs/cifs/CHANGES
@@ -1,3 +1,13 @@
+Version 1.45
+------------
+Do not time out lockw calls when using posix extensions. Do not
+time out requests if server still responding reasonably fast
+on requests on other threads. Improve POSIX locking emulation,
+(lock cancel now works, and unlock of merged range works even
+to Windows servers now). Fix oops on mount to lanman servers
+(win9x, os/2 etc.) when null password. Do not send listxattr
+(SMB to query all EAs) if nouser_xattr specified.
+
Version 1.44
------------
Rewritten sessionsetup support, including support for legacy SMB
diff --git a/fs/cifs/README b/fs/cifs/README
index 7986d0d97ac..5f0e1bd64fe 100644
--- a/fs/cifs/README
+++ b/fs/cifs/README
@@ -408,7 +408,7 @@ A partial list of the supported mount options follows:
user_xattr Allow getting and setting user xattrs as OS/2 EAs (extended
attributes) to the server (default) e.g. via setfattr
and getfattr utilities.
- nouser_xattr Do not allow getfattr/setfattr to get/set xattrs
+ nouser_xattr Do not allow getfattr/setfattr to get/set/list xattrs
mapchars Translate six of the seven reserved characters (not backslash)
*?<>|:
to the remap range (above 0xF000), which also
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index a89efaf78a2..4bc250b2d9f 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -277,7 +277,8 @@ void calc_lanman_hash(struct cifsSesInfo * ses, char * lnm_session_key)
return;
memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
- strncpy(password_with_pad, ses->password, CIFS_ENCPWD_SIZE);
+ if(ses->password)
+ strncpy(password_with_pad, ses->password, CIFS_ENCPWD_SIZE);
if((ses->server->secMode & SECMODE_PW_ENCRYPT) == 0)
if(extended_security & CIFSSEC_MAY_PLNTXT) {
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index c28ede59994..3cd750029be 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -402,7 +402,6 @@ static struct quotactl_ops cifs_quotactl_ops = {
};
#endif
-#ifdef CONFIG_CIFS_EXPERIMENTAL
static void cifs_umount_begin(struct vfsmount * vfsmnt, int flags)
{
struct cifs_sb_info *cifs_sb;
@@ -422,7 +421,7 @@ static void cifs_umount_begin(struct vfsmount * vfsmnt, int flags)
tcon->tidStatus = CifsExiting;
up(&tcon->tconSem);
- /* cancel_brl_requests(tcon); */
+ /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */
/* cancel_notify_requests(tcon); */
if(tcon->ses && tcon->ses->server)
{
@@ -438,7 +437,6 @@ static void cifs_umount_begin(struct vfsmount * vfsmnt, int flags)
return;
}
-#endif
static int cifs_remount(struct super_block *sb, int *flags, char *data)
{
@@ -457,9 +455,7 @@ struct super_operations cifs_super_ops = {
unless later we add lazy close of inodes or unless the kernel forgets to call
us with the same number of releases (closes) as opens */
.show_options = cifs_show_options,
-#ifdef CONFIG_CIFS_EXPERIMENTAL
.umount_begin = cifs_umount_begin,
-#endif
.remount_fs = cifs_remount,
};
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index 8f75c6f2470..39ee8ef3bde 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -100,5 +100,5 @@ extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t);
extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
extern int cifs_ioctl (struct inode * inode, struct file * filep,
unsigned int command, unsigned long arg);
-#define CIFS_VERSION "1.44"
+#define CIFS_VERSION "1.45"
#endif /* _CIFSFS_H */
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 6d7cf5f3bc0..b24006c47df 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -3,6 +3,7 @@
*
* Copyright (C) International Business Machines Corp., 2002,2006
* Author(s): Steve French (sfrench@us.ibm.com)
+ * Jeremy Allison (jra@samba.org)
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
@@ -158,7 +159,8 @@ struct TCP_Server_Info {
/* 16th byte of RFC1001 workstation name is always null */
char workstation_RFC1001_name[SERVER_NAME_LEN_WITH_NULL];
__u32 sequence_number; /* needed for CIFS PDU signature */
- char mac_signing_key[CIFS_SESS_KEY_SIZE + 16];
+ char mac_signing_key[CIFS_SESS_KEY_SIZE + 16];
+ unsigned long lstrp; /* when we got last response from this server */
};
/*
@@ -266,14 +268,14 @@ struct cifsTconInfo {
};
/*
- * This info hangs off the cifsFileInfo structure. This is used to track
- * byte stream locks on the file
+ * This info hangs off the cifsFileInfo structure, pointed to by llist.
+ * This is used to track byte stream locks on the file
*/
struct cifsLockInfo {
- struct cifsLockInfo *next;
- int start;
- int length;
- int type;
+ struct list_head llist; /* pointer to next cifsLockInfo */
+ __u64 offset;
+ __u64 length;
+ __u8 type;
};
/*
@@ -304,6 +306,8 @@ struct cifsFileInfo {
/* lock scope id (0 if none) */
struct file * pfile; /* needed for writepage */
struct inode * pInode; /* needed for oplock break */
+ struct semaphore lock_sem;
+ struct list_head llist; /* list of byte range locks we have. */
unsigned closePend:1; /* file is marked to close */
unsigned invalidHandle:1; /* file closed via session abend */
atomic_t wrtPending; /* handle in use - defer close */
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index a5ddc62d6fe..b35c55c3c8b 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -50,6 +50,10 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *,
extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *,
struct kvec *, int /* nvec to send */,
int * /* type of buf returned */ , const int long_op);
+extern int SendReceiveBlockingLock(const unsigned int /* xid */ , struct cifsTconInfo *,
+ struct smb_hdr * /* input */ ,
+ struct smb_hdr * /* out */ ,
+ int * /* bytes returned */);
extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid);
extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length);
extern int is_valid_oplock_break(struct smb_hdr *smb, struct TCP_Server_Info *);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 19678c575df..075d8fb3d37 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -477,7 +477,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
/* BB get server time for time conversions and add
code to use it and timezone since this is not UTC */
- if (rsp->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
+ if (rsp->EncryptionKeyLength == cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
memcpy(server->cryptKey, rsp->EncryptionKey,
CIFS_CRYPTO_KEY_SIZE);
} else if (server->secMode & SECMODE_PW_ENCRYPT) {
@@ -1460,8 +1460,13 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
pSMB->hdr.smb_buf_length += count;
pSMB->ByteCount = cpu_to_le16(count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ if (waitFlag) {
+ rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned);
+ } else {
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
(struct smb_hdr *) pSMBr, &bytes_returned, timeout);
+ }
cifs_stats_inc(&tcon->num_locks);
if (rc) {
cFYI(1, ("Send error in Lock = %d", rc));
@@ -1484,6 +1489,7 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
char *data_offset;
struct cifs_posix_lock *parm_data;
int rc = 0;
+ int timeout = 0;
int bytes_returned = 0;
__u16 params, param_offset, offset, byte_count, count;
@@ -1503,7 +1509,6 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
pSMB->MaxSetupCount = 0;
pSMB->Reserved = 0;
pSMB->Flags = 0;
- pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
offset = param_offset + params;
@@ -1529,8 +1534,13 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
(((char *) &pSMB->hdr.Protocol) + offset);
parm_data->lock_type = cpu_to_le16(lock_type);
- if(waitFlag)
+ if(waitFlag) {
+ timeout = 3; /* blocking operation, no timeout */
parm_data->lock_flags = cpu_to_le16(1);
+ pSMB->Timeout = cpu_to_le32(-1);
+ } else
+ pSMB->Timeout = 0;
+
parm_data->pid = cpu_to_le32(current->tgid);
parm_data->start = cpu_to_le64(pLockData->fl_start);
parm_data->length = cpu_to_le64(len); /* normalize negative numbers */
@@ -1541,8 +1551,14 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
pSMB->Reserved4 = 0;
pSMB->hdr.smb_buf_length += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
- (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (waitFlag) {
+ rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned);
+ } else {
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, timeout);
+ }
+
if (rc) {
cFYI(1, ("Send error in Posix Lock = %d", rc));
} else if (get_flag) {
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 876eb9ef85f..5d394c72686 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -182,6 +182,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
while ((server->tcpStatus != CifsExiting) && (server->tcpStatus != CifsGood))
{
+ try_to_freeze();
if(server->protocolType == IPV6) {
rc = ipv6_connect(&server->addr.sockAddr6,&server->ssocket);
} else {
@@ -612,6 +613,10 @@ multi_t2_fnd:
#ifdef CONFIG_CIFS_STATS2
mid_entry->when_received = jiffies;
#endif
+ /* so we do not time out requests to server
+ which is still responding (since server could
+ be busy but not dead) */
+ server->lstrp = jiffies;
break;
}
}
@@ -1266,33 +1271,35 @@ find_unc(__be32 new_target_ip_addr, char *uncName, char *userName)
read_lock(&GlobalSMBSeslock);
list_for_each(tmp, &GlobalTreeConnectionList) {
- cFYI(1, ("Next tcon - "));
+ cFYI(1, ("Next tcon"));
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
if (tcon->ses) {
if (tcon->ses->server) {
cFYI(1,
- (" old ip addr: %x == new ip %x ?",
+ ("old ip addr: %x == new ip %x ?",
tcon->ses->server->addr.sockAddr.sin_addr.
s_addr, new_target_ip_addr));
if (tcon->ses->server->addr.sockAddr.sin_addr.
s_addr == new_target_ip_addr) {
- /* BB lock tcon and server and tcp session and increment use count here? */
+ /* BB lock tcon, server and tcp session and increment use count here? */
/* found a match on the TCP session */
/* BB check if reconnection needed */
- cFYI(1,("Matched ip, old UNC: %s == new: %s ?",
+ cFYI(1,("IP match, old UNC: %s new: %s",
tcon->treeName, uncName));
if (strncmp
(tcon->treeName, uncName,
MAX_TREE_SIZE) == 0) {
cFYI(1,
- ("Matched UNC, old user: %s == new: %s ?",
+ ("and old usr: %s new: %s",
tcon->treeName, uncName));
if (strncmp
(tcon->ses->userName,
userName,
MAX_USERNAME_SIZE) == 0) {
read_unlock(&GlobalSMBSeslock);
- return tcon;/* also matched user (smb session)*/
+ /* matched smb session
+ (user name */
+ return tcon;
}
}
}
@@ -1969,7 +1976,18 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
}
cFYI(1,("Negotiate caps 0x%x",(int)cap));
-
+#ifdef CONFIG_CIFS_DEBUG2
+ if(cap & CIFS_UNIX_FCNTL_CAP)
+ cFYI(1,("FCNTL cap"));
+ if(cap & CIFS_UNIX_EXTATTR_CAP)
+ cFYI(1,("EXTATTR cap"));
+ if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
+ cFYI(1,("POSIX path cap"));
+ if(cap & CIFS_UNIX_XATTR_CAP)
+ cFYI(1,("XATTR cap"));
+ if(cap & CIFS_UNIX_POSIX_ACL_CAP)
+ cFYI(1,("POSIX ACL cap"));
+#endif /* CIFS_DEBUG2 */
if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) {
cFYI(1,("setting capabilities failed"));
}
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index ba4cbe9b068..914239d5363 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -267,6 +267,10 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
pCifsFile->invalidHandle = FALSE;
pCifsFile->closePend = FALSE;
init_MUTEX(&pCifsFile->fh_sem);
+ init_MUTEX(&pCifsFile->lock_sem);
+ INIT_LIST_HEAD(&pCifsFile->llist);
+ atomic_set(&pCifsFile->wrtPending,0);
+
/* set the following in open now
pCifsFile->pfile = file; */
write_lock(&GlobalSMBSeslock);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 944d2b9e092..e9c5ba9084f 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -5,6 +5,7 @@
*
* Copyright (C) International Business Machines Corp., 2002,2003
* Author(s): Steve French (sfrench@us.ibm.com)
+ * Jeremy Allison (jra@samba.org)
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
@@ -47,6 +48,8 @@ static inline struct cifsFileInfo *cifs_init_private(
private_data->netfid = netfid;
private_data->pid = current->tgid;
init_MUTEX(&private_data->fh_sem);
+ init_MUTEX(&private_data->lock_sem);
+ INIT_LIST_HEAD(&private_data->llist);
private_data->pfile = file; /* needed for writepage */
private_data->pInode = inode;
private_data->invalidHandle = FALSE;
@@ -473,6 +476,8 @@ int cifs_close(struct inode *inode, struct file *file)
cifs_sb = CIFS_SB(inode->i_sb);
pTcon = cifs_sb->tcon;
if (pSMBFile) {
+ struct cifsLockInfo *li, *tmp;
+
pSMBFile->closePend = TRUE;
if (pTcon) {
/* no sense reconnecting to close a file that is
@@ -496,6 +501,16 @@ int cifs_close(struct inode *inode, struct file *file)
pSMBFile->netfid);
}
}
+
+ /* Delete any outstanding lock records.
+ We'll lose them when the file is closed anyway. */
+ down(&pSMBFile->lock_sem);
+ list_for_each_entry_safe(li, tmp, &pSMBFile->llist, llist) {
+ list_del(&li->llist);
+ kfree(li);
+ }
+ up(&pSMBFile->lock_sem);
+
write_lock(&GlobalSMBSeslock);
list_del(&pSMBFile->flist);
list_del(&pSMBFile->tlist);
@@ -570,6 +585,21 @@ int cifs_closedir(struct inode *inode, struct file *file)
return rc;
}
+static int store_file_lock(struct cifsFileInfo *fid, __u64 len,
+ __u64 offset, __u8 lockType)
+{
+ struct cifsLockInfo *li = kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL);
+ if (li == NULL)
+ return -ENOMEM;
+ li->offset = offset;
+ li->length = len;
+ li->type = lockType;
+ down(&fid->lock_sem);
+ list_add(&li->llist, &fid->llist);
+ up(&fid->lock_sem);
+ return 0;
+}
+
int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
{
int rc, xid;
@@ -581,6 +611,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
struct cifsTconInfo *pTcon;
__u16 netfid;
__u8 lockType = LOCKING_ANDX_LARGE_FILES;
+ int posix_locking;
length = 1 + pfLock->fl_end - pfLock->fl_start;
rc = -EACCES;
@@ -639,15 +670,14 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
}
netfid = ((struct cifsFileInfo *)file->private_data)->netfid;
+ posix_locking = (cifs_sb->tcon->ses->capabilities & CAP_UNIX) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(cifs_sb->tcon->fsUnixInfo.Capability));
/* BB add code here to normalize offset and length to
account for negative length which we can not accept over the
wire */
if (IS_GETLK(cmd)) {
- if(experimEnabled &&
- (cifs_sb->tcon->ses->capabilities & CAP_UNIX) &&
- (CIFS_UNIX_FCNTL_CAP &
- le64_to_cpu(cifs_sb->tcon->fsUnixInfo.Capability))) {
+ if(posix_locking) {
int posix_lock_type;
if(lockType & LOCKING_ANDX_SHARED_LOCK)
posix_lock_type = CIFS_RDLCK;
@@ -683,10 +713,15 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
FreeXid(xid);
return rc;
}
- if (experimEnabled &&
- (cifs_sb->tcon->ses->capabilities & CAP_UNIX) &&
- (CIFS_UNIX_FCNTL_CAP &
- le64_to_cpu(cifs_sb->tcon->fsUnixInfo.Capability))) {
+
+ if (!numLock && !numUnlock) {
+ /* if no lock or unlock then nothing
+ to do since we do not know what it is */
+ FreeXid(xid);
+ return -EOPNOTSUPP;
+ }
+
+ if (posix_locking) {
int posix_lock_type;
if(lockType & LOCKING_ANDX_SHARED_LOCK)
posix_lock_type = CIFS_RDLCK;
@@ -695,18 +730,46 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
if(numUnlock == 1)
posix_lock_type = CIFS_UNLCK;
- else if(numLock == 0) {
- /* if no lock or unlock then nothing
- to do since we do not know what it is */
- FreeXid(xid);
- return -EOPNOTSUPP;
- }
+
rc = CIFSSMBPosixLock(xid, pTcon, netfid, 0 /* set */,
length, pfLock,
posix_lock_type, wait_flag);
- } else
- rc = CIFSSMBLock(xid, pTcon, netfid, length, pfLock->fl_start,
- numUnlock, numLock, lockType, wait_flag);
+ } else {
+ struct cifsFileInfo *fid = (struct cifsFileInfo *)file->private_data;
+
+ if (numLock) {
+ rc = CIFSSMBLock(xid, pTcon, netfid, length, pfLock->fl_start,
+ 0, numLock, lockType, wait_flag);
+
+ if (rc == 0) {
+ /* For Windows locks we must store them. */
+ rc = store_file_lock(fid, length,
+ pfLock->fl_start, lockType);
+ }
+ } else if (numUnlock) {
+ /* For each stored lock that this unlock overlaps
+ completely, unlock it. */
+ int stored_rc = 0;
+ struct cifsLockInfo *li, *tmp;
+
+ down(&fid->lock_sem);
+ list_for_each_entry_safe(li, tmp, &fid->llist, llist) {
+ if (pfLock->fl_start <= li->offset &&
+ length >= li->length) {
+ stored_rc = CIFSSMBLock(xid, pTcon, netfid,
+ li->length, li->offset,
+ 1, 0, li->type, FALSE);
+ if (stored_rc)
+ rc = stored_rc;
+
+ list_del(&li->llist);
+ kfree(li);
+ }
+ }
+ up(&fid->lock_sem);
+ }
+ }
+
if (pfLock->fl_flags & FL_POSIX)
posix_lock_file_wait(file, pfLock);
FreeXid(xid);
diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c
index b66eff5dc62..ce87550e918 100644
--- a/fs/cifs/netmisc.c
+++ b/fs/cifs/netmisc.c
@@ -72,6 +72,7 @@ static const struct smb_to_posix_error mapping_table_ERRDOS[] = {
{ERRinvlevel,-EOPNOTSUPP},
{ERRdirnotempty, -ENOTEMPTY},
{ERRnotlocked, -ENOLCK},
+ {ERRcancelviolation, -ENOLCK},
{ERRalreadyexists, -EEXIST},
{ERRmoredata, -EOVERFLOW},
{ERReasnotsupported,-EOPNOTSUPP},
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 03bbcb37791..9aeb58a7d36 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -82,7 +82,6 @@ static int construct_dentry(struct qstr *qstring, struct file *file,
if(*ptmp_inode == NULL)
return rc;
rc = 1;
- d_instantiate(tmp_dentry, *ptmp_inode);
}
} else {
tmp_dentry = d_alloc(file->f_dentry, qstring);
@@ -99,9 +98,7 @@ static int construct_dentry(struct qstr *qstring, struct file *file,
tmp_dentry->d_op = &cifs_dentry_ops;
if(*ptmp_inode == NULL)
return rc;
- rc = 1;
- d_instantiate(tmp_dentry, *ptmp_inode);
- d_rehash(tmp_dentry);
+ rc = 2;
}
tmp_dentry->d_time = jiffies;
@@ -556,7 +553,7 @@ static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile)
FIND_FILE_STANDARD_INFO * pFindData =
(FIND_FILE_STANDARD_INFO *)current_entry;
filename = &pFindData->FileName[0];
- len = le32_to_cpu(pFindData->FileNameLength);
+ len = pFindData->FileNameLength;
} else {
cFYI(1,("Unknown findfirst level %d",cfile->srch_inf.info_level));
}
@@ -870,6 +867,12 @@ static int cifs_filldir(char *pfindEntry, struct file *file,
pfindEntry, &obj_type, rc);
else
fill_in_inode(tmp_inode, 1 /* NT */, pfindEntry, &obj_type, rc);
+
+ if(rc) /* new inode - needs to be tied to dentry */ {
+ d_instantiate(tmp_dentry, tmp_inode);
+ if(rc == 2)
+ d_rehash(tmp_dentry);
+ }
rc = filldir(direntry,qstring.name,qstring.len,file->f_pos,
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index 7202d534ef0..d1705ab8136 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -372,7 +372,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
/* no capabilities flags in old lanman negotiation */
- pSMB->old_req.PasswordLength = CIFS_SESS_KEY_SIZE;
+ pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE);
/* BB calculate hash with password */
/* and copy into bcc */
diff --git a/fs/cifs/smberr.h b/fs/cifs/smberr.h
index cd41c67ff8d..212c3c29640 100644
--- a/fs/cifs/smberr.h
+++ b/fs/cifs/smberr.h
@@ -95,6 +95,7 @@
#define ERRinvlevel 124
#define ERRdirnotempty 145
#define ERRnotlocked 158
+#define ERRcancelviolation 173
#define ERRalreadyexists 183
#define ERRbadpipe 230
#define ERRpipebusy 231
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 17ba329e2b3..48d47b46b1f 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -3,7 +3,8 @@
*
* Copyright (C) International Business Machines Corp., 2002,2005
* Author(s): Steve French (sfrench@us.ibm.com)
- *
+ * Jeremy Allison (jra@samba.org) 2006.
+ *
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
@@ -36,7 +37,7 @@ extern mempool_t *cifs_mid_poolp;
extern kmem_cache_t *cifs_oplock_cachep;
static struct mid_q_entry *
-AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
+AllocMidQEntry(const struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
{
struct mid_q_entry *temp;
@@ -203,6 +204,10 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
rc = 0;
}
+ /* Don't want to modify the buffer as a
+ side effect of this call. */
+ smb_buffer->smb_buf_length = smb_buf_length;
+
return rc;
}
@@ -217,6 +222,7 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec,
unsigned int len = iov[0].iov_len;
unsigned int total_len;
int first_vec = 0;
+ unsigned int smb_buf_length = smb_buffer->smb_buf_length;
if(ssocket == NULL)
return -ENOTSOCK; /* BB eventually add reconnect code here */
@@ -293,36 +299,15 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec,
} else
rc = 0;
+ /* Don't want to modify the buffer as a
+ side effect of this call. */
+ smb_buffer->smb_buf_length = smb_buf_length;
+
return rc;
}
-int
-SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
- struct kvec *iov, int n_vec, int * pRespBufType /* ret */,
- const int long_op)
+static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op)
{
- int rc = 0;
- unsigned int receive_len;
- unsigned long timeout;
- struct mid_q_entry *midQ;
- struct smb_hdr *in_buf = iov[0].iov_base;
-
- *pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */
-
- if ((ses == NULL) || (ses->server == NULL)) {
- cifs_small_buf_release(in_buf);
- cERROR(1,("Null session"));
- return -EIO;
- }
-
- if(ses->server->tcpStatus == CifsExiting) {
- cifs_small_buf_release(in_buf);
- return -ENOENT;
- }
-
- /* Ensure that we do not send more than 50 overlapping requests
- to the same server. We may make this configurable later or
- use ses->maxReq */
if(long_op == -1) {
/* oplock breaks must not be held up */
atomic_inc(&ses->server->inFlight);
@@ -345,53 +330,140 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
} else {
if(ses->server->tcpStatus == CifsExiting) {
spin_unlock(&GlobalMid_Lock);
- cifs_small_buf_release(in_buf);
return -ENOENT;
}
- /* can not count locking commands against total since
- they are allowed to block on server */
+ /* can not count locking commands against total since
+ they are allowed to block on server */
- if(long_op < 3) {
/* update # of requests on the wire to server */
+ if (long_op < 3)
atomic_inc(&ses->server->inFlight);
- }
spin_unlock(&GlobalMid_Lock);
break;
}
}
}
- /* make sure that we sign in the same order that we send on this socket
- and avoid races inside tcp sendmsg code that could cause corruption
- of smb data */
-
- down(&ses->server->tcpSem);
+ return 0;
+}
+static int allocate_mid(struct cifsSesInfo *ses, struct smb_hdr *in_buf,
+ struct mid_q_entry **ppmidQ)
+{
if (ses->server->tcpStatus == CifsExiting) {
- rc = -ENOENT;
- goto out_unlock2;
+ return -ENOENT;
} else if (ses->server->tcpStatus == CifsNeedReconnect) {
cFYI(1,("tcp session dead - return to caller to retry"));
- rc = -EAGAIN;
- goto out_unlock2;
+ return -EAGAIN;
} else if (ses->status != CifsGood) {
/* check if SMB session is bad because we are setting it up */
if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
(in_buf->Command != SMB_COM_NEGOTIATE)) {
- rc = -EAGAIN;
- goto out_unlock2;
+ return -EAGAIN;
} /* else ok - we are setting up session */
}
- midQ = AllocMidQEntry(in_buf, ses);
- if (midQ == NULL) {
+ *ppmidQ = AllocMidQEntry(in_buf, ses);
+ if (*ppmidQ == NULL) {
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int wait_for_response(struct cifsSesInfo *ses,
+ struct mid_q_entry *midQ,
+ unsigned long timeout,
+ unsigned long time_to_wait)
+{
+ unsigned long curr_timeout;
+
+ for (;;) {
+ curr_timeout = timeout + jiffies;
+ wait_event(ses->server->response_q,
+ (!(midQ->midState == MID_REQUEST_SUBMITTED)) ||
+ time_after(jiffies, curr_timeout) ||
+ ((ses->server->tcpStatus != CifsGood) &&
+ (ses->server->tcpStatus != CifsNew)));
+
+ if (time_after(jiffies, curr_timeout) &&
+ (midQ->midState == MID_REQUEST_SUBMITTED) &&
+ ((ses->server->tcpStatus == CifsGood) ||
+ (ses->server->tcpStatus == CifsNew))) {
+
+ unsigned long lrt;
+
+ /* We timed out. Is the server still
+ sending replies ? */
+ spin_lock(&GlobalMid_Lock);
+ lrt = ses->server->lstrp;
+ spin_unlock(&GlobalMid_Lock);
+
+ /* Calculate time_to_wait past last receive time.
+ Although we prefer not to time out if the
+ server is still responding - we will time
+ out if the server takes more than 15 (or 45
+ or 180) seconds to respond to this request
+ and has not responded to any request from
+ other threads on the client within 10 seconds */
+ lrt += time_to_wait;
+ if (time_after(jiffies, lrt)) {
+ /* No replies for time_to_wait. */
+ cERROR(1,("server not responding"));
+ return -1;
+ }
+ } else {
+ return 0;
+ }
+ }
+}
+
+int
+SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
+ struct kvec *iov, int n_vec, int * pRespBufType /* ret */,
+ const int long_op)
+{
+ int rc = 0;
+ unsigned int receive_len;
+ unsigned long timeout;
+ struct mid_q_entry *midQ;
+ struct smb_hdr *in_buf = iov[0].iov_base;
+
+ *pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */
+
+ if ((ses == NULL) || (ses->server == NULL)) {
+ cifs_small_buf_release(in_buf);
+ cERROR(1,("Null session"));
+ return -EIO;
+ }
+
+ if(ses->server->tcpStatus == CifsExiting) {
+ cifs_small_buf_release(in_buf);
+ return -ENOENT;
+ }
+
+ /* Ensure that we do not send more than 50 overlapping requests
+ to the same server. We may make this configurable later or
+ use ses->maxReq */
+
+ rc = wait_for_free_request(ses, long_op);
+ if (rc) {
+ cifs_small_buf_release(in_buf);
+ return rc;
+ }
+
+ /* make sure that we sign in the same order that we send on this socket
+ and avoid races inside tcp sendmsg code that could cause corruption
+ of smb data */
+
+ down(&ses->server->tcpSem);
+
+ rc = allocate_mid(ses, in_buf, &midQ);
+ if (rc) {
up(&ses->server->tcpSem);
cifs_small_buf_release(in_buf);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
- return -ENOMEM;
+ /* Update # of requests on wire to server */
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
+ return rc;
}
rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number);
@@ -406,32 +478,23 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
atomic_dec(&ses->server->inSend);
midQ->when_sent = jiffies;
#endif
- if(rc < 0) {
- DeleteMidQEntry(midQ);
- up(&ses->server->tcpSem);
- cifs_small_buf_release(in_buf);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
- return rc;
- } else {
- up(&ses->server->tcpSem);
- cifs_small_buf_release(in_buf);
- }
+
+ up(&ses->server->tcpSem);
+ cifs_small_buf_release(in_buf);
+
+ if(rc < 0)
+ goto out;
if (long_op == -1)
- goto cifs_no_response_exit2;
+ goto out;
else if (long_op == 2) /* writes past end of file can take loong time */
timeout = 180 * HZ;
else if (long_op == 1)
timeout = 45 * HZ; /* should be greater than
servers oplock break timeout (about 43 seconds) */
- else if (long_op > 2) {
- timeout = MAX_SCHEDULE_TIMEOUT;
- } else
+ else
timeout = 15 * HZ;
+
/* wait for 15 seconds or until woken up due to response arriving or
due to last connection to this server being unmounted */
if (signal_pending(current)) {
@@ -441,19 +504,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
}
/* No user interrupts in wait - wreaks havoc with performance */
- if(timeout != MAX_SCHEDULE_TIMEOUT) {
- timeout += jiffies;
- wait_event(ses->server->response_q,
- (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
- time_after(jiffies, timeout) ||
- ((ses->server->tcpStatus != CifsGood) &&
- (ses->server->tcpStatus != CifsNew)));
- } else {
- wait_event(ses->server->response_q,
- (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
- ((ses->server->tcpStatus != CifsGood) &&
- (ses->server->tcpStatus != CifsNew)));
- }
+ wait_for_response(ses, midQ, timeout, 10 * HZ);
spin_lock(&GlobalMid_Lock);
if (midQ->resp_buf) {
@@ -481,11 +532,9 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
}
spin_unlock(&GlobalMid_Lock);
DeleteMidQEntry(midQ);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
+ /* Update # of requests on wire to server */
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
return rc;
}
@@ -536,24 +585,12 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
cFYI(1,("Bad MID state?"));
}
}
-cifs_no_response_exit2:
- DeleteMidQEntry(midQ);
-
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
- return rc;
+out:
-out_unlock2:
- up(&ses->server->tcpSem);
- cifs_small_buf_release(in_buf);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
+ DeleteMidQEntry(midQ);
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
return rc;
}
@@ -583,85 +620,34 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
/* Ensure that we do not send more than 50 overlapping requests
to the same server. We may make this configurable later or
use ses->maxReq */
- if(long_op == -1) {
- /* oplock breaks must not be held up */
- atomic_inc(&ses->server->inFlight);
- } else {
- spin_lock(&GlobalMid_Lock);
- while(1) {
- if(atomic_read(&ses->server->inFlight) >=
- cifs_max_pending){
- spin_unlock(&GlobalMid_Lock);
-#ifdef CONFIG_CIFS_STATS2
- atomic_inc(&ses->server->num_waiters);
-#endif
- wait_event(ses->server->request_q,
- atomic_read(&ses->server->inFlight)
- < cifs_max_pending);
-#ifdef CONFIG_CIFS_STATS2
- atomic_dec(&ses->server->num_waiters);
-#endif
- spin_lock(&GlobalMid_Lock);
- } else {
- if(ses->server->tcpStatus == CifsExiting) {
- spin_unlock(&GlobalMid_Lock);
- return -ENOENT;
- }
- /* can not count locking commands against total since
- they are allowed to block on server */
-
- if(long_op < 3) {
- /* update # of requests on the wire to server */
- atomic_inc(&ses->server->inFlight);
- }
- spin_unlock(&GlobalMid_Lock);
- break;
- }
- }
- }
+ rc = wait_for_free_request(ses, long_op);
+ if (rc)
+ return rc;
+
/* make sure that we sign in the same order that we send on this socket
and avoid races inside tcp sendmsg code that could cause corruption
of smb data */
down(&ses->server->tcpSem);
- if (ses->server->tcpStatus == CifsExiting) {
- rc = -ENOENT;
- goto out_unlock;
- } else if (ses->server->tcpStatus == CifsNeedReconnect) {
- cFYI(1,("tcp session dead - return to caller to retry"));
- rc = -EAGAIN;
- goto out_unlock;
- } else if (ses->status != CifsGood) {
- /* check if SMB session is bad because we are setting it up */
- if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
- (in_buf->Command != SMB_COM_NEGOTIATE)) {
- rc = -EAGAIN;
- goto out_unlock;
- } /* else ok - we are setting up session */
- }
- midQ = AllocMidQEntry(in_buf, ses);
- if (midQ == NULL) {
+ rc = allocate_mid(ses, in_buf, &midQ);
+ if (rc) {
up(&ses->server->tcpSem);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
- return -ENOMEM;
+ /* Update # of requests on wire to server */
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
+ return rc;
}
if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
- up(&ses->server->tcpSem);
cERROR(1, ("Illegal length, greater than maximum frame, %d",
in_buf->smb_buf_length));
DeleteMidQEntry(midQ);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
+ up(&ses->server->tcpSem);
+ /* Update # of requests on wire to server */
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
return -EIO;
}
@@ -677,27 +663,19 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
atomic_dec(&ses->server->inSend);
midQ->when_sent = jiffies;
#endif
- if(rc < 0) {
- DeleteMidQEntry(midQ);
- up(&ses->server->tcpSem);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
- return rc;
- } else
- up(&ses->server->tcpSem);
+ up(&ses->server->tcpSem);
+
+ if(rc < 0)
+ goto out;
+
if (long_op == -1)
- goto cifs_no_response_exit;
+ goto out;
else if (long_op == 2) /* writes past end of file can take loong time */
timeout = 180 * HZ;
else if (long_op == 1)
timeout = 45 * HZ; /* should be greater than
servers oplock break timeout (about 43 seconds) */
- else if (long_op > 2) {
- timeout = MAX_SCHEDULE_TIMEOUT;
- } else
+ else
timeout = 15 * HZ;
/* wait for 15 seconds or until woken up due to response arriving or
due to last connection to this server being unmounted */
@@ -708,19 +686,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
}
/* No user interrupts in wait - wreaks havoc with performance */
- if(timeout != MAX_SCHEDULE_TIMEOUT) {
- timeout += jiffies;
- wait_event(ses->server->response_q,
- (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
- time_after(jiffies, timeout) ||
- ((ses->server->tcpStatus != CifsGood) &&
- (ses->server->tcpStatus != CifsNew)));
- } else {
- wait_event(ses->server->response_q,
- (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
- ((ses->server->tcpStatus != CifsGood) &&
- (ses->server->tcpStatus != CifsNew)));
- }
+ wait_for_response(ses, midQ, timeout, 10 * HZ);
spin_lock(&GlobalMid_Lock);
if (midQ->resp_buf) {
@@ -748,11 +714,9 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
}
spin_unlock(&GlobalMid_Lock);
DeleteMidQEntry(midQ);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
+ /* Update # of requests on wire to server */
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
return rc;
}
@@ -799,23 +763,253 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
cERROR(1,("Bad MID state?"));
}
}
-cifs_no_response_exit:
+
+out:
+
DeleteMidQEntry(midQ);
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
+ return rc;
+}
+
+/* Send an NT_CANCEL SMB to cause the POSIX blocking lock to return. */
+
+static int
+send_nt_cancel(struct cifsTconInfo *tcon, struct smb_hdr *in_buf,
+ struct mid_q_entry *midQ)
+{
+ int rc = 0;
+ struct cifsSesInfo *ses = tcon->ses;
+ __u16 mid = in_buf->Mid;
+ header_assemble(in_buf, SMB_COM_NT_CANCEL, tcon, 0);
+ in_buf->Mid = mid;
+ down(&ses->server->tcpSem);
+ rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
+ if (rc) {
+ up(&ses->server->tcpSem);
+ return rc;
+ }
+ rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
+ (struct sockaddr *) &(ses->server->addr.sockAddr));
+ up(&ses->server->tcpSem);
return rc;
+}
+
+/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows
+ blocking lock to return. */
+
+static int
+send_lock_cancel(const unsigned int xid, struct cifsTconInfo *tcon,
+ struct smb_hdr *in_buf,
+ struct smb_hdr *out_buf)
+{
+ int bytes_returned;
+ struct cifsSesInfo *ses = tcon->ses;
+ LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;
+
+ /* We just modify the current in_buf to change
+ the type of lock from LOCKING_ANDX_SHARED_LOCK
+ or LOCKING_ANDX_EXCLUSIVE_LOCK to
+ LOCKING_ANDX_CANCEL_LOCK. */
+
+ pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES;
+ pSMB->Timeout = 0;
+ pSMB->hdr.Mid = GetNextMid(ses->server);
+
+ return SendReceive(xid, ses, in_buf, out_buf,
+ &bytes_returned, 0);
+}
-out_unlock:
+int
+SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
+ struct smb_hdr *in_buf, struct smb_hdr *out_buf,
+ int *pbytes_returned)
+{
+ int rc = 0;
+ int rstart = 0;
+ unsigned int receive_len;
+ struct mid_q_entry *midQ;
+ struct cifsSesInfo *ses;
+
+ if (tcon == NULL || tcon->ses == NULL) {
+ cERROR(1,("Null smb session"));
+ return -EIO;
+ }
+ ses = tcon->ses;
+
+ if(ses->server == NULL) {
+ cERROR(1,("Null tcp session"));
+ return -EIO;
+ }
+
+ if(ses->server->tcpStatus == CifsExiting)
+ return -ENOENT;
+
+ /* Ensure that we do not send more than 50 overlapping requests
+ to the same server. We may make this configurable later or
+ use ses->maxReq */
+
+ rc = wait_for_free_request(ses, 3);
+ if (rc)
+ return rc;
+
+ /* make sure that we sign in the same order that we send on this socket
+ and avoid races inside tcp sendmsg code that could cause corruption
+ of smb data */
+
+ down(&ses->server->tcpSem);
+
+ rc = allocate_mid(ses, in_buf, &midQ);
+ if (rc) {
+ up(&ses->server->tcpSem);
+ return rc;
+ }
+
+ if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+ up(&ses->server->tcpSem);
+ cERROR(1, ("Illegal length, greater than maximum frame, %d",
+ in_buf->smb_buf_length));
+ DeleteMidQEntry(midQ);
+ return -EIO;
+ }
+
+ rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
+
+ midQ->midState = MID_REQUEST_SUBMITTED;
+#ifdef CONFIG_CIFS_STATS2
+ atomic_inc(&ses->server->inSend);
+#endif
+ rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
+ (struct sockaddr *) &(ses->server->addr.sockAddr));
+#ifdef CONFIG_CIFS_STATS2
+ atomic_dec(&ses->server->inSend);
+ midQ->when_sent = jiffies;
+#endif
up(&ses->server->tcpSem);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
+
+ if(rc < 0) {
+ DeleteMidQEntry(midQ);
+ return rc;
+ }
+
+ /* Wait for a reply - allow signals to interrupt. */
+ rc = wait_event_interruptible(ses->server->response_q,
+ (!(midQ->midState == MID_REQUEST_SUBMITTED)) ||
+ ((ses->server->tcpStatus != CifsGood) &&
+ (ses->server->tcpStatus != CifsNew)));
+
+ /* Were we interrupted by a signal ? */
+ if ((rc == -ERESTARTSYS) &&
+ (midQ->midState == MID_REQUEST_SUBMITTED) &&
+ ((ses->server->tcpStatus == CifsGood) ||
+ (ses->server->tcpStatus == CifsNew))) {
+
+ if (in_buf->Command == SMB_COM_TRANSACTION2) {
+ /* POSIX lock. We send a NT_CANCEL SMB to cause the
+ blocking lock to return. */
+
+ rc = send_nt_cancel(tcon, in_buf, midQ);
+ if (rc) {
+ DeleteMidQEntry(midQ);
+ return rc;
+ }
+ } else {
+ /* Windows lock. We send a LOCKINGX_CANCEL_LOCK
+ to cause the blocking lock to return. */
+
+ rc = send_lock_cancel(xid, tcon, in_buf, out_buf);
+
+ /* If we get -ENOLCK back the lock may have
+ already been removed. Don't exit in this case. */
+ if (rc && rc != -ENOLCK) {
+ DeleteMidQEntry(midQ);
+ return rc;
+ }
+ }
+
+ /* Wait 5 seconds for the response. */
+ if (wait_for_response(ses, midQ, 5 * HZ, 5 * HZ)==0) {
+ /* We got the response - restart system call. */
+ rstart = 1;
+ }
+ }
+
+ spin_lock(&GlobalMid_Lock);
+ if (midQ->resp_buf) {
+ spin_unlock(&GlobalMid_Lock);
+ receive_len = midQ->resp_buf->smb_buf_length;
+ } else {
+ cERROR(1,("No response for cmd %d mid %d",
+ midQ->command, midQ->mid));
+ if(midQ->midState == MID_REQUEST_SUBMITTED) {
+ if(ses->server->tcpStatus == CifsExiting)
+ rc = -EHOSTDOWN;
+ else {
+ ses->server->tcpStatus = CifsNeedReconnect;
+ midQ->midState = MID_RETRY_NEEDED;
+ }
+ }
+
+ if (rc != -EHOSTDOWN) {
+ if(midQ->midState == MID_RETRY_NEEDED) {
+ rc = -EAGAIN;
+ cFYI(1,("marking request for retry"));
+ } else {
+ rc = -EIO;
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+ DeleteMidQEntry(midQ);
+ return rc;
}
+
+ if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
+ cERROR(1, ("Frame too large received. Length: %d Xid: %d",
+ receive_len, xid));
+ rc = -EIO;
+ } else { /* rcvd frame is ok */
+
+ if (midQ->resp_buf && out_buf
+ && (midQ->midState == MID_RESPONSE_RECEIVED)) {
+ out_buf->smb_buf_length = receive_len;
+ memcpy((char *)out_buf + 4,
+ (char *)midQ->resp_buf + 4,
+ receive_len);
+
+ dump_smb(out_buf, 92);
+ /* convert the length into a more usable form */
+ if((receive_len > 24) &&
+ (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
+ SECMODE_SIGN_ENABLED))) {
+ rc = cifs_verify_signature(out_buf,
+ ses->server->mac_signing_key,
+ midQ->sequence_number+1);
+ if(rc) {
+ cERROR(1,("Unexpected SMB signature"));
+ /* BB FIXME add code to kill session */
+ }
+ }
+
+ *pbytes_returned = out_buf->smb_buf_length;
+
+ /* BB special case reconnect tid and uid here? */
+ rc = map_smb_to_linux_error(out_buf);
+ /* convert ByteCount if necessary */
+ if (receive_len >=
+ sizeof (struct smb_hdr) -
+ 4 /* do not count RFC1001 header */ +
+ (2 * out_buf->WordCount) + 2 /* bcc */ )
+ BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
+ } else {
+ rc = -EIO;
+ cERROR(1,("Bad MID state?"));
+ }
+ }
+ DeleteMidQEntry(midQ);
+ if (rstart && rc == -EACCES)
+ return -ERESTARTSYS;
return rc;
}
diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c
index 7754d641775..067648b7179 100644
--- a/fs/cifs/xattr.c
+++ b/fs/cifs/xattr.c
@@ -330,11 +330,15 @@ ssize_t cifs_listxattr(struct dentry * direntry, char * data, size_t buf_size)
sb = direntry->d_inode->i_sb;
if(sb == NULL)
return -EIO;
- xid = GetXid();
cifs_sb = CIFS_SB(sb);
pTcon = cifs_sb->tcon;
+ if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
+ return -EOPNOTSUPP;
+
+ xid = GetXid();
+
full_path = build_path_from_dentry(direntry);
if(full_path == NULL) {
FreeXid(xid);
diff --git a/fs/coda/file.c b/fs/coda/file.c
index cc66c681bd1..dbfbcfa5b3c 100644
--- a/fs/coda/file.c
+++ b/fs/coda/file.c
@@ -136,10 +136,8 @@ int coda_open(struct inode *coda_inode, struct file *coda_file)
coda_vfs_stat.open++;
cfi = kmalloc(sizeof(struct coda_file_info), GFP_KERNEL);
- if (!cfi) {
- unlock_kernel();
+ if (!cfi)
return -ENOMEM;
- }
lock_kernel();
diff --git a/fs/dcache.c b/fs/dcache.c
index c6e3535be19..1b4a3a34ec5 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -38,7 +38,7 @@ int sysctl_vfs_cache_pressure __read_mostly = 100;
EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure);
__cacheline_aligned_in_smp DEFINE_SPINLOCK(dcache_lock);
-static seqlock_t rename_lock __cacheline_aligned_in_smp = SEQLOCK_UNLOCKED;
+static __cacheline_aligned_in_smp DEFINE_SEQLOCK(rename_lock);
EXPORT_SYMBOL(dcache_lock);
@@ -1339,10 +1339,10 @@ void d_move(struct dentry * dentry, struct dentry * target)
*/
if (target < dentry) {
spin_lock(&target->d_lock);
- spin_lock(&dentry->d_lock);
+ spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
} else {
spin_lock(&dentry->d_lock);
- spin_lock(&target->d_lock);
+ spin_lock_nested(&target->d_lock, DENTRY_D_LOCK_NESTED);
}
/* Move the dentry to the target hash queue, if on different bucket */
diff --git a/fs/direct-io.c b/fs/direct-io.c
index 538fb0418fb..5981e17f46f 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -220,7 +220,8 @@ static void dio_complete(struct dio *dio, loff_t offset, ssize_t bytes)
if (dio->end_io && dio->result)
dio->end_io(dio->iocb, offset, bytes, dio->map_bh.b_private);
if (dio->lock_type == DIO_LOCKING)
- up_read(&dio->inode->i_alloc_sem);
+ /* lockdep: non-owner release */
+ up_read_non_owner(&dio->inode->i_alloc_sem);
}
/*
@@ -1261,7 +1262,8 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
}
if (dio_lock_type == DIO_LOCKING)
- down_read(&inode->i_alloc_sem);
+ /* lockdep: not the owner will release it */
+ down_read_non_owner(&inode->i_alloc_sem);
}
/*
diff --git a/fs/efs/symlink.c b/fs/efs/symlink.c
index e249cf733a6..1d30d2ff440 100644
--- a/fs/efs/symlink.c
+++ b/fs/efs/symlink.c
@@ -22,7 +22,7 @@ static int efs_symlink_readpage(struct file *file, struct page *page)
err = -ENAMETOOLONG;
if (size > 2 * EFS_BLOCKSIZE)
- goto fail;
+ goto fail_notlocked;
lock_kernel();
/* read first 512 bytes of link target */
@@ -47,6 +47,7 @@ static int efs_symlink_readpage(struct file *file, struct page *page)
return 0;
fail:
unlock_kernel();
+fail_notlocked:
SetPageError(page);
kunmap(page);
unlock_page(page);
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 9c677bbd0b0..3a3567433b9 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -120,7 +120,7 @@ struct epoll_filefd {
*/
struct wake_task_node {
struct list_head llink;
- task_t *task;
+ struct task_struct *task;
wait_queue_head_t *wq;
};
@@ -413,7 +413,7 @@ static void ep_poll_safewake(struct poll_safewake *psw, wait_queue_head_t *wq)
{
int wake_nests = 0;
unsigned long flags;
- task_t *this_task = current;
+ struct task_struct *this_task = current;
struct list_head *lsthead = &psw->wake_task_list, *lnk;
struct wake_task_node *tncur;
struct wake_task_node tnode;
@@ -1168,7 +1168,7 @@ static int ep_unlink(struct eventpoll *ep, struct epitem *epi)
eexit_1:
DNPRINTK(3, (KERN_INFO "[%p] eventpoll: ep_unlink(%p, %p) = %d\n",
- current, ep, epi->file, error));
+ current, ep, epi->ffd.file, error));
return error;
}
@@ -1236,7 +1236,7 @@ static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *k
struct eventpoll *ep = epi->ep;
DNPRINTK(3, (KERN_INFO "[%p] eventpoll: poll_callback(%p) epi=%p ep=%p\n",
- current, epi->file, epi, ep));
+ current, epi->ffd.file, epi, ep));
write_lock_irqsave(&ep->lock, flags);
diff --git a/fs/exec.c b/fs/exec.c
index 8344ba73a2a..54135df2a96 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -486,8 +486,6 @@ struct file *open_exec(const char *name)
if (!(nd.mnt->mnt_flags & MNT_NOEXEC) &&
S_ISREG(inode->i_mode)) {
int err = vfs_permission(&nd, MAY_EXEC);
- if (!err && !(inode->i_mode & 0111))
- err = -EACCES;
file = ERR_PTR(err);
if (!err) {
file = nameidata_to_filp(&nd, O_RDONLY);
@@ -753,7 +751,7 @@ no_thread_group:
write_lock_irq(&tasklist_lock);
spin_lock(&oldsighand->siglock);
- spin_lock(&newsighand->siglock);
+ spin_lock_nested(&newsighand->siglock, SINGLE_DEPTH_NESTING);
rcu_assign_pointer(current->sighand, newsighand);
recalc_sigpending();
@@ -922,12 +920,6 @@ int prepare_binprm(struct linux_binprm *bprm)
int retval;
mode = inode->i_mode;
- /*
- * Check execute perms again - if the caller has CAP_DAC_OVERRIDE,
- * generic_permission lets a non-executable through
- */
- if (!(mode & 0111)) /* with at least _one_ execute bit set */
- return -EACCES;
if (bprm->file->f_op == NULL)
return -EACCES;
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index 9f43879d6d6..ca5bfb6914d 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -251,6 +251,44 @@ static struct super_operations ext2_sops = {
#endif
};
+static struct dentry *ext2_get_dentry(struct super_block *sb, void *vobjp)
+{
+ __u32 *objp = vobjp;
+ unsigned long ino = objp[0];
+ __u32 generation = objp[1];
+ struct inode *inode;
+ struct dentry *result;
+
+ if (ino < EXT2_FIRST_INO(sb) && ino != EXT2_ROOT_INO)
+ return ERR_PTR(-ESTALE);
+ if (ino > le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count))
+ return ERR_PTR(-ESTALE);
+
+ /* iget isn't really right if the inode is currently unallocated!!
+ * ext2_read_inode currently does appropriate checks, but
+ * it might be "neater" to call ext2_get_inode first and check
+ * if the inode is valid.....
+ */
+ inode = iget(sb, ino);
+ if (inode == NULL)
+ return ERR_PTR(-ENOMEM);
+ if (is_bad_inode(inode) ||
+ (generation && inode->i_generation != generation)) {
+ /* we didn't find the right inode.. */
+ iput(inode);
+ return ERR_PTR(-ESTALE);
+ }
+ /* now to find a dentry.
+ * If possible, get a well-connected one
+ */
+ result = d_alloc_anon(inode);
+ if (!result) {
+ iput(inode);
+ return ERR_PTR(-ENOMEM);
+ }
+ return result;
+}
+
/* Yes, most of these are left as NULL!!
* A NULL value implies the default, which works with ext2-like file
* systems, but can be improved upon.
@@ -258,6 +296,7 @@ static struct super_operations ext2_sops = {
*/
static struct export_operations ext2_export_ops = {
.get_parent = ext2_get_parent,
+ .get_dentry = ext2_get_dentry,
};
static unsigned long get_sb_block(void **data)
@@ -775,7 +814,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
if (EXT2_INODE_SIZE(sb) == 0)
goto cantfind_ext2;
sbi->s_inodes_per_block = sb->s_blocksize / EXT2_INODE_SIZE(sb);
- if (sbi->s_inodes_per_block == 0)
+ if (sbi->s_inodes_per_block == 0 || sbi->s_inodes_per_group == 0)
goto cantfind_ext2;
sbi->s_itb_per_group = sbi->s_inodes_per_group /
sbi->s_inodes_per_block;
@@ -1157,7 +1196,7 @@ static ssize_t ext2_quota_write(struct super_block *sb, int type,
struct buffer_head tmp_bh;
struct buffer_head *bh;
- mutex_lock(&inode->i_mutex);
+ mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
while (towrite > 0) {
tocopy = sb->s_blocksize - offset < towrite ?
sb->s_blocksize - offset : towrite;
diff --git a/fs/ext3/acl.h b/fs/ext3/acl.h
index 92d50b53a93..0d1e6279cbf 100644
--- a/fs/ext3/acl.h
+++ b/fs/ext3/acl.h
@@ -62,9 +62,6 @@ extern int ext3_permission (struct inode *, int, struct nameidata *);
extern int ext3_acl_chmod (struct inode *);
extern int ext3_init_acl (handle_t *, struct inode *, struct inode *);
-extern int init_ext3_acl(void);
-extern void exit_ext3_acl(void);
-
#else /* CONFIG_EXT3_FS_POSIX_ACL */
#include <linux/sched.h>
#define ext3_permission NULL
diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c
index a504a40d6d2..063d994bda0 100644
--- a/fs/ext3/balloc.c
+++ b/fs/ext3/balloc.c
@@ -1269,12 +1269,12 @@ ext3_fsblk_t ext3_new_blocks(handle_t *handle, struct inode *inode,
goal = le32_to_cpu(es->s_first_data_block);
group_no = (goal - le32_to_cpu(es->s_first_data_block)) /
EXT3_BLOCKS_PER_GROUP(sb);
+ goal_group = group_no;
+retry_alloc:
gdp = ext3_get_group_desc(sb, group_no, &gdp_bh);
if (!gdp)
goto io_error;
- goal_group = group_no;
-retry:
free_blocks = le16_to_cpu(gdp->bg_free_blocks_count);
/*
* if there is not enough free blocks to make a new resevation
@@ -1349,7 +1349,7 @@ retry:
if (my_rsv) {
my_rsv = NULL;
group_no = goal_group;
- goto retry;
+ goto retry_alloc;
}
/* No space left on the device */
*errp = -ENOSPC;
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index f804d5e9d60..84be02e9365 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -925,7 +925,7 @@ int ext3_get_blocks_handle(handle_t *handle, struct inode *inode,
set_buffer_new(bh_result);
got_it:
map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key));
- if (blocks_to_boundary == 0)
+ if (count > blocks_to_boundary)
set_buffer_boundary(bh_result);
err = count;
/* Clean up and exit */
@@ -1009,11 +1009,14 @@ struct buffer_head *ext3_getblk(handle_t *handle, struct inode *inode,
buffer_trace_init(&dummy.b_history);
err = ext3_get_blocks_handle(handle, inode, block, 1,
&dummy, create, 1);
- if (err == 1) {
+ /*
+ * ext3_get_blocks_handle() returns number of blocks
+ * mapped. 0 in case of a HOLE.
+ */
+ if (err > 0) {
+ if (err > 1)
+ WARN_ON(1);
err = 0;
- } else if (err >= 0) {
- WARN_ON(1);
- err = -EIO;
}
*errp = err;
if (!err && buffer_mapped(&dummy)) {
@@ -1158,7 +1161,7 @@ retry:
ret = PTR_ERR(handle);
goto out;
}
- if (test_opt(inode->i_sb, NOBH))
+ if (test_opt(inode->i_sb, NOBH) && ext3_should_writeback_data(inode))
ret = nobh_prepare_write(page, from, to, ext3_get_block);
else
ret = block_prepare_write(page, from, to, ext3_get_block);
@@ -1244,7 +1247,7 @@ static int ext3_writeback_commit_write(struct file *file, struct page *page,
if (new_i_size > EXT3_I(inode)->i_disksize)
EXT3_I(inode)->i_disksize = new_i_size;
- if (test_opt(inode->i_sb, NOBH))
+ if (test_opt(inode->i_sb, NOBH) && ext3_should_writeback_data(inode))
ret = nobh_commit_write(file, page, from, to);
else
ret = generic_commit_write(file, page, from, to);
@@ -1494,7 +1497,7 @@ static int ext3_writeback_writepage(struct page *page,
goto out_fail;
}
- if (test_opt(inode->i_sb, NOBH))
+ if (test_opt(inode->i_sb, NOBH) && ext3_should_writeback_data(inode))
ret = nobh_writepage(page, ext3_get_block, wbc);
else
ret = block_write_full_page(page, ext3_get_block, wbc);
@@ -2402,14 +2405,15 @@ static ext3_fsblk_t ext3_get_inode_block(struct super_block *sb,
struct buffer_head *bh;
struct ext3_group_desc * gdp;
-
- if ((ino != EXT3_ROOT_INO && ino != EXT3_JOURNAL_INO &&
- ino != EXT3_RESIZE_INO && ino < EXT3_FIRST_INO(sb)) ||
- ino > le32_to_cpu(EXT3_SB(sb)->s_es->s_inodes_count)) {
- ext3_error(sb, "ext3_get_inode_block",
- "bad inode number: %lu", ino);
+ if (!ext3_valid_inum(sb, ino)) {
+ /*
+ * This error is already checked for in namei.c unless we are
+ * looking at an NFS filehandle, in which case no error
+ * report is needed
+ */
return 0;
}
+
block_group = (ino - 1) / EXT3_INODES_PER_GROUP(sb);
if (block_group >= EXT3_SB(sb)->s_groups_count) {
ext3_error(sb,"ext3_get_inode_block","group >= groups count");
diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c
index d9176dba369..2aa7101b27c 100644
--- a/fs/ext3/namei.c
+++ b/fs/ext3/namei.c
@@ -1000,7 +1000,12 @@ static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, str
if (bh) {
unsigned long ino = le32_to_cpu(de->inode);
brelse (bh);
- inode = iget(dir->i_sb, ino);
+ if (!ext3_valid_inum(dir->i_sb, ino)) {
+ ext3_error(dir->i_sb, "ext3_lookup",
+ "bad inode number: %lu", ino);
+ inode = NULL;
+ } else
+ inode = iget(dir->i_sb, ino);
if (!inode)
return ERR_PTR(-EACCES);
@@ -1028,7 +1033,13 @@ struct dentry *ext3_get_parent(struct dentry *child)
return ERR_PTR(-ENOENT);
ino = le32_to_cpu(de->inode);
brelse(bh);
- inode = iget(child->d_inode->i_sb, ino);
+
+ if (!ext3_valid_inum(child->d_inode->i_sb, ino)) {
+ ext3_error(child->d_inode->i_sb, "ext3_get_parent",
+ "bad inode number: %lu", ino);
+ inode = NULL;
+ } else
+ inode = iget(child->d_inode->i_sb, ino);
if (!inode)
return ERR_PTR(-EACCES);
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index f2dd7133661..3559086eee5 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -554,6 +554,47 @@ static int ext3_show_options(struct seq_file *seq, struct vfsmount *vfs)
return 0;
}
+
+static struct dentry *ext3_get_dentry(struct super_block *sb, void *vobjp)
+{
+ __u32 *objp = vobjp;
+ unsigned long ino = objp[0];
+ __u32 generation = objp[1];
+ struct inode *inode;
+ struct dentry *result;
+
+ if (ino < EXT3_FIRST_INO(sb) && ino != EXT3_ROOT_INO)
+ return ERR_PTR(-ESTALE);
+ if (ino > le32_to_cpu(EXT3_SB(sb)->s_es->s_inodes_count))
+ return ERR_PTR(-ESTALE);
+
+ /* iget isn't really right if the inode is currently unallocated!!
+ *
+ * ext3_read_inode will return a bad_inode if the inode had been
+ * deleted, so we should be safe.
+ *
+ * Currently we don't know the generation for parent directory, so
+ * a generation of 0 means "accept any"
+ */
+ inode = iget(sb, ino);
+ if (inode == NULL)
+ return ERR_PTR(-ENOMEM);
+ if (is_bad_inode(inode) ||
+ (generation && inode->i_generation != generation)) {
+ iput(inode);
+ return ERR_PTR(-ESTALE);
+ }
+ /* now to find a dentry.
+ * If possible, get a well-connected one
+ */
+ result = d_alloc_anon(inode);
+ if (!result) {
+ iput(inode);
+ return ERR_PTR(-ENOMEM);
+ }
+ return result;
+}
+
#ifdef CONFIG_QUOTA
#define QTYPE2NAME(t) ((t)==USRQUOTA?"user":"group")
#define QTYPE2MOPT(on, t) ((t)==USRQUOTA?((on)##USRJQUOTA):((on)##GRPJQUOTA))
@@ -622,6 +663,7 @@ static struct super_operations ext3_sops = {
static struct export_operations ext3_export_ops = {
.get_parent = ext3_get_parent,
+ .get_dentry = ext3_get_dentry,
};
enum {
@@ -2614,7 +2656,7 @@ static ssize_t ext3_quota_write(struct super_block *sb, int type,
struct buffer_head *bh;
handle_t *handle = journal_current_handle();
- mutex_lock(&inode->i_mutex);
+ mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
while (towrite > 0) {
tocopy = sb->s_blocksize - offset < towrite ?
sb->s_blocksize - offset : towrite;
diff --git a/fs/file.c b/fs/file.c
index 55f4e702256..b3c6b82e6a9 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -240,13 +240,9 @@ static struct fdtable *alloc_fdtable(int nr)
if (!fdt)
goto out;
- nfds = 8 * L1_CACHE_BYTES;
- /* Expand to the max in easy steps */
- while (nfds <= nr) {
- nfds = nfds * 2;
- if (nfds > NR_OPEN)
- nfds = NR_OPEN;
- }
+ nfds = max_t(int, 8 * L1_CACHE_BYTES, roundup_pow_of_two(nr + 1));
+ if (nfds > NR_OPEN)
+ nfds = NR_OPEN;
new_openset = alloc_fdset(nfds);
new_execset = alloc_fdset(nfds);
@@ -277,11 +273,13 @@ static struct fdtable *alloc_fdtable(int nr)
} while (nfds <= nr);
new_fds = alloc_fd_array(nfds);
if (!new_fds)
- goto out;
+ goto out2;
fdt->fd = new_fds;
fdt->max_fds = nfds;
fdt->free_files = NULL;
return fdt;
+out2:
+ nfds = fdt->max_fdset;
out:
if (new_openset)
free_fdset(new_openset, nfds);
diff --git a/fs/freevxfs/vxfs_lookup.c b/fs/freevxfs/vxfs_lookup.c
index 29cce456c7c..43886fa00a2 100644
--- a/fs/freevxfs/vxfs_lookup.c
+++ b/fs/freevxfs/vxfs_lookup.c
@@ -246,6 +246,8 @@ vxfs_readdir(struct file *fp, void *retp, filldir_t filler)
u_long page, npages, block, pblocks, nblocks, offset;
loff_t pos;
+ lock_kernel();
+
switch ((long)fp->f_pos) {
case 0:
if (filler(retp, ".", 1, fp->f_pos, ip->i_ino, DT_DIR) < 0)
diff --git a/fs/fuse/control.c b/fs/fuse/control.c
index a3bce3a7725..46fe60b2da2 100644
--- a/fs/fuse/control.c
+++ b/fs/fuse/control.c
@@ -105,7 +105,7 @@ static struct dentry *fuse_ctl_add_dentry(struct dentry *parent,
/*
* Add a connection to the control filesystem (if it exists). Caller
- * must host fuse_mutex
+ * must hold fuse_mutex
*/
int fuse_ctl_add_conn(struct fuse_conn *fc)
{
@@ -139,7 +139,7 @@ int fuse_ctl_add_conn(struct fuse_conn *fc)
/*
* Remove a connection from the control filesystem (if it exists).
- * Caller must host fuse_mutex
+ * Caller must hold fuse_mutex
*/
void fuse_ctl_remove_conn(struct fuse_conn *fc)
{
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 72a74cde6de..409ce6a7cca 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -14,6 +14,33 @@
#include <linux/sched.h>
#include <linux/namei.h>
+#if BITS_PER_LONG >= 64
+static inline void fuse_dentry_settime(struct dentry *entry, u64 time)
+{
+ entry->d_time = time;
+}
+
+static inline u64 fuse_dentry_time(struct dentry *entry)
+{
+ return entry->d_time;
+}
+#else
+/*
+ * On 32 bit archs store the high 32 bits of time in d_fsdata
+ */
+static void fuse_dentry_settime(struct dentry *entry, u64 time)
+{
+ entry->d_time = time;
+ entry->d_fsdata = (void *) (unsigned long) (time >> 32);
+}
+
+static u64 fuse_dentry_time(struct dentry *entry)
+{
+ return (u64) entry->d_time +
+ ((u64) (unsigned long) entry->d_fsdata << 32);
+}
+#endif
+
/*
* FUSE caches dentries and attributes with separate timeout. The
* time in jiffies until the dentry/attributes are valid is stored in
@@ -23,10 +50,13 @@
/*
* Calculate the time in jiffies until a dentry/attributes are valid
*/
-static unsigned long time_to_jiffies(unsigned long sec, unsigned long nsec)
+static u64 time_to_jiffies(unsigned long sec, unsigned long nsec)
{
- struct timespec ts = {sec, nsec};
- return jiffies + timespec_to_jiffies(&ts);
+ if (sec || nsec) {
+ struct timespec ts = {sec, nsec};
+ return get_jiffies_64() + timespec_to_jiffies(&ts);
+ } else
+ return 0;
}
/*
@@ -35,7 +65,8 @@ static unsigned long time_to_jiffies(unsigned long sec, unsigned long nsec)
*/
static void fuse_change_timeout(struct dentry *entry, struct fuse_entry_out *o)
{
- entry->d_time = time_to_jiffies(o->entry_valid, o->entry_valid_nsec);
+ fuse_dentry_settime(entry,
+ time_to_jiffies(o->entry_valid, o->entry_valid_nsec));
if (entry->d_inode)
get_fuse_inode(entry->d_inode)->i_time =
time_to_jiffies(o->attr_valid, o->attr_valid_nsec);
@@ -47,7 +78,7 @@ static void fuse_change_timeout(struct dentry *entry, struct fuse_entry_out *o)
*/
void fuse_invalidate_attr(struct inode *inode)
{
- get_fuse_inode(inode)->i_time = jiffies - 1;
+ get_fuse_inode(inode)->i_time = 0;
}
/*
@@ -60,7 +91,7 @@ void fuse_invalidate_attr(struct inode *inode)
*/
static void fuse_invalidate_entry_cache(struct dentry *entry)
{
- entry->d_time = jiffies - 1;
+ fuse_dentry_settime(entry, 0);
}
/*
@@ -102,7 +133,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
if (inode && is_bad_inode(inode))
return 0;
- else if (time_after(jiffies, entry->d_time)) {
+ else if (fuse_dentry_time(entry) < get_jiffies_64()) {
int err;
struct fuse_entry_out outarg;
struct fuse_conn *fc;
@@ -666,7 +697,7 @@ static int fuse_revalidate(struct dentry *entry)
if (!fuse_allow_task(fc, current))
return -EACCES;
if (get_node_id(inode) != FUSE_ROOT_ID &&
- time_before_eq(jiffies, fi->i_time))
+ fi->i_time >= get_jiffies_64())
return 0;
return fuse_do_getattr(inode);
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 63614ed1633..5c4fcd1dbf5 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -395,14 +395,16 @@ static int fuse_readpages(struct file *file, struct address_space *mapping,
struct fuse_readpages_data data;
int err;
+ err = -EIO;
if (is_bad_inode(inode))
- return -EIO;
+ goto clean_pages_up;
data.file = file;
data.inode = inode;
data.req = fuse_get_req(fc);
+ err = PTR_ERR(data.req);
if (IS_ERR(data.req))
- return PTR_ERR(data.req);
+ goto clean_pages_up;
err = read_cache_pages(mapping, pages, fuse_readpages_fill, &data);
if (!err) {
@@ -412,6 +414,10 @@ static int fuse_readpages(struct file *file, struct address_space *mapping,
fuse_put_request(fc, data.req);
}
return err;
+
+clean_pages_up:
+ put_pages_list(pages);
+ return err;
}
static size_t fuse_send_write(struct fuse_req *req, struct file *file,
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 0dbf9662184..69c7750d55b 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -59,7 +59,7 @@ struct fuse_inode {
struct fuse_req *forget_req;
/** Time in jiffies until the file attributes are valid */
- unsigned long i_time;
+ u64 i_time;
};
/** FUSE specific file data */
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index dcaaabd3b9c..7d25092262a 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -51,7 +51,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
return NULL;
fi = get_fuse_inode(inode);
- fi->i_time = jiffies - 1;
+ fi->i_time = 0;
fi->nodeid = 0;
fi->nlookup = 0;
fi->forget_req = fuse_request_alloc();
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 6449cb69796..c3920c96dad 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -83,8 +83,6 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
ret = -ENOMEM;
len = vma_len + ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
- if (!(vma->vm_flags & VM_WRITE) && len > inode->i_size)
- goto out;
if (vma->vm_flags & VM_MAYSHARE &&
hugetlb_reserve_pages(inode, vma->vm_pgoff >> (HPAGE_SHIFT-PAGE_SHIFT),
@@ -93,7 +91,7 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
ret = 0;
hugetlb_prefault_arch_hook(vma->vm_mm);
- if (inode->i_size < len)
+ if (vma->vm_flags & VM_WRITE && inode->i_size < len)
inode->i_size = len;
out:
mutex_unlock(&inode->i_mutex);
diff --git a/fs/inotify_user.c b/fs/inotify_user.c
index f2386442ade..017cb0f134d 100644
--- a/fs/inotify_user.c
+++ b/fs/inotify_user.c
@@ -187,7 +187,7 @@ static struct inotify_kernel_event * kernel_event(s32 wd, u32 mask, u32 cookie,
{
struct inotify_kernel_event *kevent;
- kevent = kmem_cache_alloc(event_cachep, GFP_KERNEL);
+ kevent = kmem_cache_alloc(event_cachep, GFP_NOFS);
if (unlikely(!kevent))
return NULL;
diff --git a/fs/ioprio.c b/fs/ioprio.c
index 93aa5715f22..78b1deae3fa 100644
--- a/fs/ioprio.c
+++ b/fs/ioprio.c
@@ -44,6 +44,9 @@ static int set_task_ioprio(struct task_struct *task, int ioprio)
task->ioprio = ioprio;
ioc = task->io_context;
+ /* see wmb() in current_io_context() */
+ smp_read_barrier_depends();
+
if (ioc && ioc->set_ioprio)
ioc->set_ioprio(ioc, ioprio);
@@ -111,9 +114,9 @@ asmlinkage long sys_ioprio_set(int which, int who, int ioprio)
continue;
ret = set_task_ioprio(p, ioprio);
if (ret)
- break;
+ goto free_uid;
} while_each_thread(g, p);
-
+free_uid:
if (who)
free_uid(user);
break;
@@ -137,6 +140,29 @@ out:
return ret;
}
+int ioprio_best(unsigned short aprio, unsigned short bprio)
+{
+ unsigned short aclass = IOPRIO_PRIO_CLASS(aprio);
+ unsigned short bclass = IOPRIO_PRIO_CLASS(bprio);
+
+ if (!ioprio_valid(aprio))
+ return bprio;
+ if (!ioprio_valid(bprio))
+ return aprio;
+
+ if (aclass == IOPRIO_CLASS_NONE)
+ aclass = IOPRIO_CLASS_BE;
+ if (bclass == IOPRIO_CLASS_NONE)
+ bclass = IOPRIO_CLASS_BE;
+
+ if (aclass == bclass)
+ return min(aprio, bprio);
+ if (aclass > bclass)
+ return bprio;
+ else
+ return aprio;
+}
+
asmlinkage long sys_ioprio_get(int which, int who)
{
struct task_struct *g, *p;
diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c
index 0971814c38b..42da6078431 100644
--- a/fs/jbd/commit.c
+++ b/fs/jbd/commit.c
@@ -261,7 +261,7 @@ void journal_commit_transaction(journal_t *journal)
struct buffer_head *bh = jh2bh(jh);
jbd_lock_bh_state(bh);
- kfree(jh->b_committed_data);
+ jbd_slab_free(jh->b_committed_data, bh->b_size);
jh->b_committed_data = NULL;
jbd_unlock_bh_state(bh);
}
@@ -745,14 +745,14 @@ restart_loop:
* Otherwise, we can just throw away the frozen data now.
*/
if (jh->b_committed_data) {
- kfree(jh->b_committed_data);
+ jbd_slab_free(jh->b_committed_data, bh->b_size);
jh->b_committed_data = NULL;
if (jh->b_frozen_data) {
jh->b_committed_data = jh->b_frozen_data;
jh->b_frozen_data = NULL;
}
} else if (jh->b_frozen_data) {
- kfree(jh->b_frozen_data);
+ jbd_slab_free(jh->b_frozen_data, bh->b_size);
jh->b_frozen_data = NULL;
}
diff --git a/fs/jbd/journal.c b/fs/jbd/journal.c
index 8c9b28dff11..f66724ce443 100644
--- a/fs/jbd/journal.c
+++ b/fs/jbd/journal.c
@@ -84,6 +84,7 @@ EXPORT_SYMBOL(journal_force_commit);
static int journal_convert_superblock_v1(journal_t *, journal_superblock_t *);
static void __journal_abort_soft (journal_t *journal, int errno);
+static int journal_create_jbd_slab(size_t slab_size);
/*
* Helper function used to manage commit timeouts
@@ -328,10 +329,10 @@ repeat:
char *tmp;
jbd_unlock_bh_state(bh_in);
- tmp = jbd_rep_kmalloc(bh_in->b_size, GFP_NOFS);
+ tmp = jbd_slab_alloc(bh_in->b_size, GFP_NOFS);
jbd_lock_bh_state(bh_in);
if (jh_in->b_frozen_data) {
- kfree(tmp);
+ jbd_slab_free(tmp, bh_in->b_size);
goto repeat;
}
@@ -1069,17 +1070,17 @@ static int load_superblock(journal_t *journal)
int journal_load(journal_t *journal)
{
int err;
+ journal_superblock_t *sb;
err = load_superblock(journal);
if (err)
return err;
+ sb = journal->j_superblock;
/* If this is a V2 superblock, then we have to check the
* features flags on it. */
if (journal->j_format_version >= 2) {
- journal_superblock_t *sb = journal->j_superblock;
-
if ((sb->s_feature_ro_compat &
~cpu_to_be32(JFS_KNOWN_ROCOMPAT_FEATURES)) ||
(sb->s_feature_incompat &
@@ -1090,6 +1091,13 @@ int journal_load(journal_t *journal)
}
}
+ /*
+ * Create a slab for this blocksize
+ */
+ err = journal_create_jbd_slab(cpu_to_be32(sb->s_blocksize));
+ if (err)
+ return err;
+
/* Let the recovery code check whether it needs to recover any
* data from the journal. */
if (journal_recover(journal))
@@ -1612,6 +1620,77 @@ void * __jbd_kmalloc (const char *where, size_t size, gfp_t flags, int retry)
}
/*
+ * jbd slab management: create 1k, 2k, 4k, 8k slabs as needed
+ * and allocate frozen and commit buffers from these slabs.
+ *
+ * Reason for doing this is to avoid, SLAB_DEBUG - since it could
+ * cause bh to cross page boundary.
+ */
+
+#define JBD_MAX_SLABS 5
+#define JBD_SLAB_INDEX(size) (size >> 11)
+
+static kmem_cache_t *jbd_slab[JBD_MAX_SLABS];
+static const char *jbd_slab_names[JBD_MAX_SLABS] = {
+ "jbd_1k", "jbd_2k", "jbd_4k", NULL, "jbd_8k"
+};
+
+static void journal_destroy_jbd_slabs(void)
+{
+ int i;
+
+ for (i = 0; i < JBD_MAX_SLABS; i++) {
+ if (jbd_slab[i])
+ kmem_cache_destroy(jbd_slab[i]);
+ jbd_slab[i] = NULL;
+ }
+}
+
+static int journal_create_jbd_slab(size_t slab_size)
+{
+ int i = JBD_SLAB_INDEX(slab_size);
+
+ BUG_ON(i >= JBD_MAX_SLABS);
+
+ /*
+ * Check if we already have a slab created for this size
+ */
+ if (jbd_slab[i])
+ return 0;
+
+ /*
+ * Create a slab and force alignment to be same as slabsize -
+ * this will make sure that allocations won't cross the page
+ * boundary.
+ */
+ jbd_slab[i] = kmem_cache_create(jbd_slab_names[i],
+ slab_size, slab_size, 0, NULL, NULL);
+ if (!jbd_slab[i]) {
+ printk(KERN_EMERG "JBD: no memory for jbd_slab cache\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+void * jbd_slab_alloc(size_t size, gfp_t flags)
+{
+ int idx;
+
+ idx = JBD_SLAB_INDEX(size);
+ BUG_ON(jbd_slab[idx] == NULL);
+ return kmem_cache_alloc(jbd_slab[idx], flags | __GFP_NOFAIL);
+}
+
+void jbd_slab_free(void *ptr, size_t size)
+{
+ int idx;
+
+ idx = JBD_SLAB_INDEX(size);
+ BUG_ON(jbd_slab[idx] == NULL);
+ kmem_cache_free(jbd_slab[idx], ptr);
+}
+
+/*
* Journal_head storage management
*/
static kmem_cache_t *journal_head_cache;
@@ -1799,13 +1878,13 @@ static void __journal_remove_journal_head(struct buffer_head *bh)
printk(KERN_WARNING "%s: freeing "
"b_frozen_data\n",
__FUNCTION__);
- kfree(jh->b_frozen_data);
+ jbd_slab_free(jh->b_frozen_data, bh->b_size);
}
if (jh->b_committed_data) {
printk(KERN_WARNING "%s: freeing "
"b_committed_data\n",
__FUNCTION__);
- kfree(jh->b_committed_data);
+ jbd_slab_free(jh->b_committed_data, bh->b_size);
}
bh->b_private = NULL;
jh->b_bh = NULL; /* debug, really */
@@ -1961,6 +2040,7 @@ static void journal_destroy_caches(void)
journal_destroy_revoke_caches();
journal_destroy_journal_head_cache();
journal_destroy_handle_cache();
+ journal_destroy_jbd_slabs();
}
static int __init journal_init(void)
diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c
index 508b2ea91f4..f5169a96260 100644
--- a/fs/jbd/transaction.c
+++ b/fs/jbd/transaction.c
@@ -666,8 +666,9 @@ repeat:
if (!frozen_buffer) {
JBUFFER_TRACE(jh, "allocate memory for buffer");
jbd_unlock_bh_state(bh);
- frozen_buffer = jbd_kmalloc(jh2bh(jh)->b_size,
- GFP_NOFS);
+ frozen_buffer =
+ jbd_slab_alloc(jh2bh(jh)->b_size,
+ GFP_NOFS);
if (!frozen_buffer) {
printk(KERN_EMERG
"%s: OOM for frozen_buffer\n",
@@ -726,7 +727,7 @@ done:
out:
if (unlikely(frozen_buffer)) /* It's usually NULL */
- kfree(frozen_buffer);
+ jbd_slab_free(frozen_buffer, bh->b_size);
JBUFFER_TRACE(jh, "exit");
return error;
@@ -879,7 +880,7 @@ int journal_get_undo_access(handle_t *handle, struct buffer_head *bh)
repeat:
if (!jh->b_committed_data) {
- committed_data = jbd_kmalloc(jh2bh(jh)->b_size, GFP_NOFS);
+ committed_data = jbd_slab_alloc(jh2bh(jh)->b_size, GFP_NOFS);
if (!committed_data) {
printk(KERN_EMERG "%s: No memory for committed data\n",
__FUNCTION__);
@@ -906,7 +907,7 @@ repeat:
out:
journal_put_journal_head(jh);
if (unlikely(committed_data))
- kfree(committed_data);
+ jbd_slab_free(committed_data, bh->b_size);
return err;
}
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
index 9c2077e7e08..0ae3cd10702 100644
--- a/fs/jffs2/acl.c
+++ b/fs/jffs2/acl.c
@@ -345,10 +345,8 @@ int jffs2_init_acl(struct inode *inode, struct inode *dir)
return rc;
}
-void jffs2_clear_acl(struct inode *inode)
+void jffs2_clear_acl(struct jffs2_inode_info *f)
{
- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
-
if (f->i_acl_access && f->i_acl_access != JFFS2_ACL_NOT_CACHED) {
posix_acl_release(f->i_acl_access);
f->i_acl_access = JFFS2_ACL_NOT_CACHED;
diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h
index 8893bd1a6ba..fa327dbd317 100644
--- a/fs/jffs2/acl.h
+++ b/fs/jffs2/acl.h
@@ -30,7 +30,7 @@ struct jffs2_acl_header {
extern int jffs2_permission(struct inode *, int, struct nameidata *);
extern int jffs2_acl_chmod(struct inode *);
extern int jffs2_init_acl(struct inode *, struct inode *);
-extern void jffs2_clear_acl(struct inode *);
+extern void jffs2_clear_acl(struct jffs2_inode_info *);
extern struct xattr_handler jffs2_acl_access_xattr_handler;
extern struct xattr_handler jffs2_acl_default_xattr_handler;
@@ -40,6 +40,6 @@ extern struct xattr_handler jffs2_acl_default_xattr_handler;
#define jffs2_permission NULL
#define jffs2_acl_chmod(inode) (0)
#define jffs2_init_acl(inode,dir) (0)
-#define jffs2_clear_acl(inode)
+#define jffs2_clear_acl(f)
#endif /* CONFIG_JFFS2_FS_POSIX_ACL */
diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c
index 8310c95478e..33f29100501 100644
--- a/fs/jffs2/malloc.c
+++ b/fs/jffs2/malloc.c
@@ -190,7 +190,7 @@ void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x)
kmem_cache_free(tmp_dnode_info_slab, x);
}
-struct jffs2_raw_node_ref *jffs2_alloc_refblock(void)
+static struct jffs2_raw_node_ref *jffs2_alloc_refblock(void)
{
struct jffs2_raw_node_ref *ret;
diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h
index f752baa8d39..cae92c14116 100644
--- a/fs/jffs2/nodelist.h
+++ b/fs/jffs2/nodelist.h
@@ -426,8 +426,6 @@ char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
/* scan.c */
int jffs2_scan_medium(struct jffs2_sb_info *c);
void jffs2_rotate_lists(struct jffs2_sb_info *c);
-int jffs2_fill_scan_buf(struct jffs2_sb_info *c, void *buf,
- uint32_t ofs, uint32_t len);
struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino);
int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
int jffs2_scan_dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t size);
diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c
index cc1899268c4..266423b2709 100644
--- a/fs/jffs2/readinode.c
+++ b/fs/jffs2/readinode.c
@@ -968,6 +968,7 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
struct jffs2_full_dirent *fd, *fds;
int deleted;
+ jffs2_clear_acl(f);
jffs2_xattr_delete_inode(c, f->inocache);
down(&f->sem);
deleted = f->inocache && !f->inocache->nlink;
diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c
index 2bfdc33752d..e2413466ddd 100644
--- a/fs/jffs2/scan.c
+++ b/fs/jffs2/scan.c
@@ -274,8 +274,8 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
return ret;
}
-int jffs2_fill_scan_buf (struct jffs2_sb_info *c, void *buf,
- uint32_t ofs, uint32_t len)
+static int jffs2_fill_scan_buf(struct jffs2_sb_info *c, void *buf,
+ uint32_t ofs, uint32_t len)
{
int ret;
size_t retlen;
diff --git a/fs/jffs2/summary.c b/fs/jffs2/summary.c
index c19bd476e8e..e52cef526d9 100644
--- a/fs/jffs2/summary.c
+++ b/fs/jffs2/summary.c
@@ -252,6 +252,11 @@ int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs,
union jffs2_node_union *node;
struct jffs2_eraseblock *jeb;
+ if (c->summary->sum_size == JFFS2_SUMMARY_NOSUM_SIZE) {
+ dbg_summary("Summary is disabled for this jeb! Skipping summary info!\n");
+ return 0;
+ }
+
node = invecs[0].iov_base;
jeb = &c->blocks[ofs / c->sector_size];
ofs -= jeb->offset;
diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c
index 18e66dbf23b..25bc1ae0864 100644
--- a/fs/jffs2/xattr.c
+++ b/fs/jffs2/xattr.c
@@ -50,9 +50,10 @@
* is used to write xdatum to medium. xd->version will be incremented.
* create_xattr_datum(c, xprefix, xname, xvalue, xsize)
* is used to create new xdatum and write to medium.
- * delete_xattr_datum(c, xd)
- * is used to delete a xdatum. It marks xd JFFS2_XFLAGS_DEAD, and allows
- * GC to reclaim those physical nodes.
+ * unrefer_xattr_datum(c, xd)
+ * is used to delete a xdatum. When nobody refers this xdatum, JFFS2_XFLAGS_DEAD
+ * is set on xd->flags and chained xattr_dead_list or release it immediately.
+ * In the first case, the garbage collector release it later.
* -------------------------------------------------- */
static uint32_t xattr_datum_hashkey(int xprefix, const char *xname, const char *xvalue, int xsize)
{
@@ -394,22 +395,24 @@ static struct jffs2_xattr_datum *create_xattr_datum(struct jffs2_sb_info *c,
return xd;
}
-static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
+static void unrefer_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
{
/* must be called under down_write(xattr_sem) */
- BUG_ON(atomic_read(&xd->refcnt));
+ if (atomic_dec_and_lock(&xd->refcnt, &c->erase_completion_lock)) {
+ uint32_t xid = xd->xid, version = xd->version;
- unload_xattr_datum(c, xd);
- xd->flags |= JFFS2_XFLAGS_DEAD;
- spin_lock(&c->erase_completion_lock);
- if (xd->node == (void *)xd) {
- BUG_ON(!(xd->flags & JFFS2_XFLAGS_INVALID));
- jffs2_free_xattr_datum(xd);
- } else {
- list_add(&xd->xindex, &c->xattr_dead_list);
+ unload_xattr_datum(c, xd);
+ xd->flags |= JFFS2_XFLAGS_DEAD;
+ if (xd->node == (void *)xd) {
+ BUG_ON(!(xd->flags & JFFS2_XFLAGS_INVALID));
+ jffs2_free_xattr_datum(xd);
+ } else {
+ list_add(&xd->xindex, &c->xattr_dead_list);
+ }
+ spin_unlock(&c->erase_completion_lock);
+
+ dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n", xid, version);
}
- spin_unlock(&c->erase_completion_lock);
- dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n", xd->xid, xd->version);
}
/* -------- xref related functions ------------------
@@ -580,8 +583,7 @@ static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *re
dbg_xattr("xref(ino=%u, xid=%u, xseqno=%u) was removed.\n",
ref->ino, ref->xid, ref->xseqno);
- if (atomic_dec_and_test(&xd->refcnt))
- delete_xattr_datum(c, xd);
+ unrefer_xattr_datum(c, xd);
}
void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
@@ -1119,8 +1121,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname,
ref->next = c->xref_dead_list;
c->xref_dead_list = ref;
spin_unlock(&c->erase_completion_lock);
- if (atomic_dec_and_test(&xd->refcnt))
- delete_xattr_datum(c, xd);
+ unrefer_xattr_datum(c, xd);
} else {
ref->ic = ic;
ref->xd = xd;
@@ -1156,8 +1157,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname,
down_write(&c->xattr_sem);
if (rc) {
JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request);
- if (atomic_dec_and_test(&xd->refcnt))
- delete_xattr_datum(c, xd);
+ unrefer_xattr_datum(c, xd);
up_write(&c->xattr_sem);
return rc;
}
@@ -1170,8 +1170,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname,
ic->xref = ref;
}
rc = PTR_ERR(newref);
- if (atomic_dec_and_test(&xd->refcnt))
- delete_xattr_datum(c, xd);
+ unrefer_xattr_datum(c, xd);
} else if (ref) {
delete_xattr_ref(c, ref);
}
diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c
index 43e3f566aad..a223cf4faa9 100644
--- a/fs/jfs/inode.c
+++ b/fs/jfs/inode.c
@@ -168,16 +168,15 @@ void jfs_dirty_inode(struct inode *inode)
set_cflag(COMMIT_Dirty, inode);
}
-static int
-jfs_get_blocks(struct inode *ip, sector_t lblock, unsigned long max_blocks,
- struct buffer_head *bh_result, int create)
+int jfs_get_block(struct inode *ip, sector_t lblock,
+ struct buffer_head *bh_result, int create)
{
s64 lblock64 = lblock;
int rc = 0;
xad_t xad;
s64 xaddr;
int xflag;
- s32 xlen = max_blocks;
+ s32 xlen = bh_result->b_size >> ip->i_blkbits;
/*
* Take appropriate lock on inode
@@ -188,7 +187,7 @@ jfs_get_blocks(struct inode *ip, sector_t lblock, unsigned long max_blocks,
IREAD_LOCK(ip);
if (((lblock64 << ip->i_sb->s_blocksize_bits) < ip->i_size) &&
- (!xtLookup(ip, lblock64, max_blocks, &xflag, &xaddr, &xlen, 0)) &&
+ (!xtLookup(ip, lblock64, xlen, &xflag, &xaddr, &xlen, 0)) &&
xaddr) {
if (xflag & XAD_NOTRECORDED) {
if (!create)
@@ -255,13 +254,6 @@ jfs_get_blocks(struct inode *ip, sector_t lblock, unsigned long max_blocks,
return rc;
}
-static int jfs_get_block(struct inode *ip, sector_t lblock,
- struct buffer_head *bh_result, int create)
-{
- return jfs_get_blocks(ip, lblock, bh_result->b_size >> ip->i_blkbits,
- bh_result, create);
-}
-
static int jfs_writepage(struct page *page, struct writeback_control *wbc)
{
return nobh_writepage(page, jfs_get_block, wbc);
diff --git a/fs/jfs/jfs_inode.h b/fs/jfs/jfs_inode.h
index b5c7da6190d..1fc48df670c 100644
--- a/fs/jfs/jfs_inode.h
+++ b/fs/jfs/jfs_inode.h
@@ -32,6 +32,7 @@ extern void jfs_truncate_nolock(struct inode *, loff_t);
extern void jfs_free_zero_link(struct inode *);
extern struct dentry *jfs_get_parent(struct dentry *dentry);
extern void jfs_set_inode_flags(struct inode *);
+extern int jfs_get_block(struct inode *, sector_t, struct buffer_head *, int);
extern const struct address_space_operations jfs_aops;
extern struct inode_operations jfs_dir_inode_operations;
diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c
index 10c46231ce1..efbb586bed4 100644
--- a/fs/jfs/jfs_txnmgr.c
+++ b/fs/jfs/jfs_txnmgr.c
@@ -2944,7 +2944,7 @@ int jfs_sync(void *arg)
* Inode is being freed
*/
list_del_init(&jfs_ip->anon_inode_list);
- } else if (! !mutex_trylock(&jfs_ip->commit_mutex)) {
+ } else if (mutex_trylock(&jfs_ip->commit_mutex)) {
/*
* inode will be removed from anonymous list
* when it is committed
diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c
index 09ea03f6227..295268ad231 100644
--- a/fs/jfs/namei.c
+++ b/fs/jfs/namei.c
@@ -165,8 +165,8 @@ static int jfs_create(struct inode *dip, struct dentry *dentry, int mode,
out3:
txEnd(tid);
- mutex_unlock(&JFS_IP(dip)->commit_mutex);
mutex_unlock(&JFS_IP(ip)->commit_mutex);
+ mutex_unlock(&JFS_IP(dip)->commit_mutex);
if (rc) {
free_ea_wmap(ip);
ip->i_nlink = 0;
@@ -300,8 +300,8 @@ static int jfs_mkdir(struct inode *dip, struct dentry *dentry, int mode)
out3:
txEnd(tid);
- mutex_unlock(&JFS_IP(dip)->commit_mutex);
mutex_unlock(&JFS_IP(ip)->commit_mutex);
+ mutex_unlock(&JFS_IP(dip)->commit_mutex);
if (rc) {
free_ea_wmap(ip);
ip->i_nlink = 0;
@@ -384,8 +384,8 @@ static int jfs_rmdir(struct inode *dip, struct dentry *dentry)
if (rc == -EIO)
txAbort(tid, 1);
txEnd(tid);
- mutex_unlock(&JFS_IP(dip)->commit_mutex);
mutex_unlock(&JFS_IP(ip)->commit_mutex);
+ mutex_unlock(&JFS_IP(dip)->commit_mutex);
goto out2;
}
@@ -422,8 +422,8 @@ static int jfs_rmdir(struct inode *dip, struct dentry *dentry)
txEnd(tid);
- mutex_unlock(&JFS_IP(dip)->commit_mutex);
mutex_unlock(&JFS_IP(ip)->commit_mutex);
+ mutex_unlock(&JFS_IP(dip)->commit_mutex);
/*
* Truncating the directory index table is not guaranteed. It
@@ -503,8 +503,8 @@ static int jfs_unlink(struct inode *dip, struct dentry *dentry)
if (rc == -EIO)
txAbort(tid, 1); /* Marks FS Dirty */
txEnd(tid);
- mutex_unlock(&JFS_IP(dip)->commit_mutex);
mutex_unlock(&JFS_IP(ip)->commit_mutex);
+ mutex_unlock(&JFS_IP(dip)->commit_mutex);
IWRITE_UNLOCK(ip);
goto out1;
}
@@ -527,8 +527,8 @@ static int jfs_unlink(struct inode *dip, struct dentry *dentry)
if ((new_size = commitZeroLink(tid, ip)) < 0) {
txAbort(tid, 1); /* Marks FS Dirty */
txEnd(tid);
- mutex_unlock(&JFS_IP(dip)->commit_mutex);
mutex_unlock(&JFS_IP(ip)->commit_mutex);
+ mutex_unlock(&JFS_IP(dip)->commit_mutex);
IWRITE_UNLOCK(ip);
rc = new_size;
goto out1;
@@ -556,9 +556,8 @@ static int jfs_unlink(struct inode *dip, struct dentry *dentry)
txEnd(tid);
- mutex_unlock(&JFS_IP(dip)->commit_mutex);
mutex_unlock(&JFS_IP(ip)->commit_mutex);
-
+ mutex_unlock(&JFS_IP(dip)->commit_mutex);
while (new_size && (rc == 0)) {
tid = txBegin(dip->i_sb, 0);
@@ -847,8 +846,8 @@ static int jfs_link(struct dentry *old_dentry,
out:
txEnd(tid);
- mutex_unlock(&JFS_IP(dir)->commit_mutex);
mutex_unlock(&JFS_IP(ip)->commit_mutex);
+ mutex_unlock(&JFS_IP(dir)->commit_mutex);
jfs_info("jfs_link: rc:%d", rc);
return rc;
@@ -1037,8 +1036,8 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry,
out3:
txEnd(tid);
- mutex_unlock(&JFS_IP(dip)->commit_mutex);
mutex_unlock(&JFS_IP(ip)->commit_mutex);
+ mutex_unlock(&JFS_IP(dip)->commit_mutex);
if (rc) {
free_ea_wmap(ip);
ip->i_nlink = 0;
@@ -1160,10 +1159,11 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (S_ISDIR(new_ip->i_mode)) {
new_ip->i_nlink--;
if (new_ip->i_nlink) {
- mutex_unlock(&JFS_IP(new_dir)->commit_mutex);
- mutex_unlock(&JFS_IP(old_ip)->commit_mutex);
+ mutex_unlock(&JFS_IP(new_ip)->commit_mutex);
if (old_dir != new_dir)
mutex_unlock(&JFS_IP(old_dir)->commit_mutex);
+ mutex_unlock(&JFS_IP(old_ip)->commit_mutex);
+ mutex_unlock(&JFS_IP(new_dir)->commit_mutex);
if (!S_ISDIR(old_ip->i_mode) && new_ip)
IWRITE_UNLOCK(new_ip);
jfs_error(new_ip->i_sb,
@@ -1281,13 +1281,12 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry,
out4:
txEnd(tid);
-
- mutex_unlock(&JFS_IP(new_dir)->commit_mutex);
- mutex_unlock(&JFS_IP(old_ip)->commit_mutex);
- if (old_dir != new_dir)
- mutex_unlock(&JFS_IP(old_dir)->commit_mutex);
if (new_ip)
mutex_unlock(&JFS_IP(new_ip)->commit_mutex);
+ if (old_dir != new_dir)
+ mutex_unlock(&JFS_IP(old_dir)->commit_mutex);
+ mutex_unlock(&JFS_IP(old_ip)->commit_mutex);
+ mutex_unlock(&JFS_IP(new_dir)->commit_mutex);
while (new_size && (rc == 0)) {
tid = txBegin(new_ip->i_sb, 0);
diff --git a/fs/jfs/super.c b/fs/jfs/super.c
index 4f6cfebc82d..143bcd1d5ea 100644
--- a/fs/jfs/super.c
+++ b/fs/jfs/super.c
@@ -26,6 +26,7 @@
#include <linux/moduleparam.h>
#include <linux/kthread.h>
#include <linux/posix_acl.h>
+#include <linux/buffer_head.h>
#include <asm/uaccess.h>
#include <linux/seq_file.h>
@@ -298,7 +299,7 @@ static int parse_options(char *options, struct super_block *sb, s64 *newLVSize,
break;
}
-#if defined(CONFIG_QUOTA)
+#ifdef CONFIG_QUOTA
case Opt_quota:
case Opt_usrquota:
*flag |= JFS_USRQUOTA;
@@ -597,7 +598,7 @@ static int jfs_show_options(struct seq_file *seq, struct vfsmount *vfs)
if (sbi->flag & JFS_NOINTEGRITY)
seq_puts(seq, ",nointegrity");
-#if defined(CONFIG_QUOTA)
+#ifdef CONFIG_QUOTA
if (sbi->flag & JFS_USRQUOTA)
seq_puts(seq, ",usrquota");
@@ -608,6 +609,113 @@ static int jfs_show_options(struct seq_file *seq, struct vfsmount *vfs)
return 0;
}
+#ifdef CONFIG_QUOTA
+
+/* Read data from quotafile - avoid pagecache and such because we cannot afford
+ * acquiring the locks... As quota files are never truncated and quota code
+ * itself serializes the operations (and noone else should touch the files)
+ * we don't have to be afraid of races */
+static ssize_t jfs_quota_read(struct super_block *sb, int type, char *data,
+ size_t len, loff_t off)
+{
+ struct inode *inode = sb_dqopt(sb)->files[type];
+ sector_t blk = off >> sb->s_blocksize_bits;
+ int err = 0;
+ int offset = off & (sb->s_blocksize - 1);
+ int tocopy;
+ size_t toread;
+ struct buffer_head tmp_bh;
+ struct buffer_head *bh;
+ loff_t i_size = i_size_read(inode);
+
+ if (off > i_size)
+ return 0;
+ if (off+len > i_size)
+ len = i_size-off;
+ toread = len;
+ while (toread > 0) {
+ tocopy = sb->s_blocksize - offset < toread ?
+ sb->s_blocksize - offset : toread;
+
+ tmp_bh.b_state = 0;
+ tmp_bh.b_size = 1 << inode->i_blkbits;
+ err = jfs_get_block(inode, blk, &tmp_bh, 0);
+ if (err)
+ return err;
+ if (!buffer_mapped(&tmp_bh)) /* A hole? */
+ memset(data, 0, tocopy);
+ else {
+ bh = sb_bread(sb, tmp_bh.b_blocknr);
+ if (!bh)
+ return -EIO;
+ memcpy(data, bh->b_data+offset, tocopy);
+ brelse(bh);
+ }
+ offset = 0;
+ toread -= tocopy;
+ data += tocopy;
+ blk++;
+ }
+ return len;
+}
+
+/* Write to quotafile */
+static ssize_t jfs_quota_write(struct super_block *sb, int type,
+ const char *data, size_t len, loff_t off)
+{
+ struct inode *inode = sb_dqopt(sb)->files[type];
+ sector_t blk = off >> sb->s_blocksize_bits;
+ int err = 0;
+ int offset = off & (sb->s_blocksize - 1);
+ int tocopy;
+ size_t towrite = len;
+ struct buffer_head tmp_bh;
+ struct buffer_head *bh;
+
+ mutex_lock(&inode->i_mutex);
+ while (towrite > 0) {
+ tocopy = sb->s_blocksize - offset < towrite ?
+ sb->s_blocksize - offset : towrite;
+
+ tmp_bh.b_state = 0;
+ tmp_bh.b_size = 1 << inode->i_blkbits;
+ err = jfs_get_block(inode, blk, &tmp_bh, 1);
+ if (err)
+ goto out;
+ if (offset || tocopy != sb->s_blocksize)
+ bh = sb_bread(sb, tmp_bh.b_blocknr);
+ else
+ bh = sb_getblk(sb, tmp_bh.b_blocknr);
+ if (!bh) {
+ err = -EIO;
+ goto out;
+ }
+ lock_buffer(bh);
+ memcpy(bh->b_data+offset, data, tocopy);
+ flush_dcache_page(bh->b_page);
+ set_buffer_uptodate(bh);
+ mark_buffer_dirty(bh);
+ unlock_buffer(bh);
+ brelse(bh);
+ offset = 0;
+ towrite -= tocopy;
+ data += tocopy;
+ blk++;
+ }
+out:
+ if (len == towrite)
+ return err;
+ if (inode->i_size < off+len-towrite)
+ i_size_write(inode, off+len-towrite);
+ inode->i_version++;
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ mutex_unlock(&inode->i_mutex);
+ return len - towrite;
+}
+
+#endif
+
static struct super_operations jfs_super_operations = {
.alloc_inode = jfs_alloc_inode,
.destroy_inode = jfs_destroy_inode,
@@ -621,7 +729,11 @@ static struct super_operations jfs_super_operations = {
.unlockfs = jfs_unlockfs,
.statfs = jfs_statfs,
.remount_fs = jfs_remount,
- .show_options = jfs_show_options
+ .show_options = jfs_show_options,
+#ifdef CONFIG_QUOTA
+ .quota_read = jfs_quota_read,
+ .quota_write = jfs_quota_write,
+#endif
};
static struct export_operations jfs_export_operations = {
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index 5980c45998c..89ba0df14c2 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -454,7 +454,7 @@ static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *ho
fl->fl_ops = &nlmclnt_lock_ops;
}
-static void do_vfs_lock(struct file_lock *fl)
+static int do_vfs_lock(struct file_lock *fl)
{
int res = 0;
switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) {
@@ -467,9 +467,7 @@ static void do_vfs_lock(struct file_lock *fl)
default:
BUG();
}
- if (res < 0)
- printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n",
- __FUNCTION__);
+ return res;
}
/*
@@ -498,6 +496,7 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
struct nlm_host *host = req->a_host;
struct nlm_res *resp = &req->a_res;
struct nlm_wait *block = NULL;
+ unsigned char fl_flags = fl->fl_flags;
int status = -ENOLCK;
if (!host->h_monitored && nsm_monitor(host) < 0) {
@@ -505,6 +504,10 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
host->h_name);
goto out;
}
+ fl->fl_flags |= FL_ACCESS;
+ status = do_vfs_lock(fl);
+ if (status < 0)
+ goto out;
block = nlmclnt_prepare_block(host, fl);
again:
@@ -539,9 +542,10 @@ again:
up_read(&host->h_rwsem);
goto again;
}
- fl->fl_flags |= FL_SLEEP;
/* Ensure the resulting lock will get added to granted list */
- do_vfs_lock(fl);
+ fl->fl_flags = fl_flags | FL_SLEEP;
+ if (do_vfs_lock(fl) < 0)
+ printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__);
up_read(&host->h_rwsem);
}
status = nlm_stat_to_errno(resp->status);
@@ -552,6 +556,7 @@ out_unblock:
nlmclnt_cancel(host, req->a_args.block, fl);
out:
nlm_release_call(req);
+ fl->fl_flags = fl_flags;
return status;
}
@@ -606,15 +611,19 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
{
struct nlm_host *host = req->a_host;
struct nlm_res *resp = &req->a_res;
- int status;
+ int status = 0;
/*
* Note: the server is supposed to either grant us the unlock
* request, or to deny it with NLM_LCK_DENIED_GRACE_PERIOD. In either
* case, we want to unlock.
*/
+ fl->fl_flags |= FL_EXISTS;
down_read(&host->h_rwsem);
- do_vfs_lock(fl);
+ if (do_vfs_lock(fl) == -ENOENT) {
+ up_read(&host->h_rwsem);
+ goto out;
+ }
up_read(&host->h_rwsem);
if (req->a_flags & RPC_TASK_ASYNC)
@@ -624,7 +633,6 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
if (status < 0)
goto out;
- status = 0;
if (resp->status == NLM_LCK_GRANTED)
goto out;
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index baf5ae51348..c9d419703cf 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -638,9 +638,6 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
if (task->tk_status < 0) {
/* RPC error: Re-insert for retransmission */
timeout = 10 * HZ;
- } else if (block->b_done) {
- /* Block already removed, kill it for real */
- timeout = 0;
} else {
/* Call was successful, now wait for client callback */
timeout = 60 * HZ;
@@ -709,13 +706,10 @@ nlmsvc_retry_blocked(void)
break;
if (time_after(block->b_when,jiffies))
break;
- dprintk("nlmsvc_retry_blocked(%p, when=%ld, done=%d)\n",
- block, block->b_when, block->b_done);
+ dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n",
+ block, block->b_when);
kref_get(&block->b_count);
- if (block->b_done)
- nlmsvc_unlink_block(block);
- else
- nlmsvc_grant_blocked(block);
+ nlmsvc_grant_blocked(block);
nlmsvc_release_block(block);
}
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
index 2a4df9b3779..01b4db9e546 100644
--- a/fs/lockd/svcsubs.c
+++ b/fs/lockd/svcsubs.c
@@ -237,19 +237,22 @@ static int
nlm_traverse_files(struct nlm_host *host, int action)
{
struct nlm_file *file, **fp;
- int i;
+ int i, ret = 0;
mutex_lock(&nlm_file_mutex);
for (i = 0; i < FILE_NRHASH; i++) {
fp = nlm_files + i;
while ((file = *fp) != NULL) {
+ file->f_count++;
+ mutex_unlock(&nlm_file_mutex);
+
/* Traverse locks, blocks and shares of this file
* and update file->f_locks count */
- if (nlm_inspect_file(host, file, action)) {
- mutex_unlock(&nlm_file_mutex);
- return 1;
- }
+ if (nlm_inspect_file(host, file, action))
+ ret = 1;
+ mutex_lock(&nlm_file_mutex);
+ file->f_count--;
/* No more references to this file. Let go of it. */
if (!file->f_blocks && !file->f_locks
&& !file->f_shares && !file->f_count) {
@@ -262,7 +265,7 @@ nlm_traverse_files(struct nlm_host *host, int action)
}
}
mutex_unlock(&nlm_file_mutex);
- return 0;
+ return ret;
}
/*
diff --git a/fs/locks.c b/fs/locks.c
index 1ad29c9b625..d7c53392cac 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -725,6 +725,10 @@ next_task:
/* Try to create a FLOCK lock on filp. We always insert new FLOCK locks
* at the head of the list, but that's secret knowledge known only to
* flock_lock_file and posix_lock_file.
+ *
+ * Note that if called with an FL_EXISTS argument, the caller may determine
+ * whether or not a lock was successfully freed by testing the return
+ * value for -ENOENT.
*/
static int flock_lock_file(struct file *filp, struct file_lock *request)
{
@@ -735,6 +739,8 @@ static int flock_lock_file(struct file *filp, struct file_lock *request)
int found = 0;
lock_kernel();
+ if (request->fl_flags & FL_ACCESS)
+ goto find_conflict;
for_each_lock(inode, before) {
struct file_lock *fl = *before;
if (IS_POSIX(fl))
@@ -750,8 +756,11 @@ static int flock_lock_file(struct file *filp, struct file_lock *request)
break;
}
- if (request->fl_type == F_UNLCK)
+ if (request->fl_type == F_UNLCK) {
+ if ((request->fl_flags & FL_EXISTS) && !found)
+ error = -ENOENT;
goto out;
+ }
error = -ENOMEM;
new_fl = locks_alloc_lock();
@@ -764,6 +773,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request)
if (found)
cond_resched();
+find_conflict:
for_each_lock(inode, before) {
struct file_lock *fl = *before;
if (IS_POSIX(fl))
@@ -777,6 +787,8 @@ static int flock_lock_file(struct file *filp, struct file_lock *request)
locks_insert_block(fl, request);
goto out;
}
+ if (request->fl_flags & FL_ACCESS)
+ goto out;
locks_copy_lock(new_fl, request);
locks_insert_lock(&inode->i_flock, new_fl);
new_fl = NULL;
@@ -948,8 +960,11 @@ static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request
error = 0;
if (!added) {
- if (request->fl_type == F_UNLCK)
+ if (request->fl_type == F_UNLCK) {
+ if (request->fl_flags & FL_EXISTS)
+ error = -ENOENT;
goto out;
+ }
if (!new_fl) {
error = -ENOLCK;
@@ -996,6 +1011,10 @@ static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request
* Add a POSIX style lock to a file.
* We merge adjacent & overlapping locks whenever possible.
* POSIX locks are sorted by owner task, then by starting address
+ *
+ * Note that if called with an FL_EXISTS argument, the caller may determine
+ * whether or not a lock was successfully freed by testing the return
+ * value for -ENOENT.
*/
int posix_lock_file(struct file *filp, struct file_lock *fl)
{
@@ -1402,8 +1421,9 @@ static int __setlease(struct file *filp, long arg, struct file_lock **flp)
if (!leases_enable)
goto out;
- error = lease_alloc(filp, arg, &fl);
- if (error)
+ error = -ENOMEM;
+ fl = locks_alloc_lock();
+ if (fl == NULL)
goto out;
locks_copy_lock(fl, lease);
@@ -1411,6 +1431,7 @@ static int __setlease(struct file *filp, long arg, struct file_lock **flp)
locks_insert_lock(before, fl);
*flp = fl;
+ error = 0;
out:
return error;
}
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 9ea91c5eeb7..330ff9fc7cf 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -204,6 +204,8 @@ static int minix_fill_super(struct super_block *s, void *data, int silent)
/*
* Allocate the buffer map to keep the superblock small.
*/
+ if (sbi->s_imap_blocks == 0 || sbi->s_zmap_blocks == 0)
+ goto out_illegal_sb;
i = (sbi->s_imap_blocks + sbi->s_zmap_blocks) * sizeof(bh);
map = kmalloc(i, GFP_KERNEL);
if (!map)
@@ -263,7 +265,7 @@ out_no_root:
out_no_bitmap:
printk("MINIX-fs: bad superblock or unable to read bitmaps\n");
- out_freemap:
+out_freemap:
for (i = 0; i < sbi->s_imap_blocks; i++)
brelse(sbi->s_imap[i]);
for (i = 0; i < sbi->s_zmap_blocks; i++)
@@ -276,11 +278,16 @@ out_no_map:
printk("MINIX-fs: can't allocate map\n");
goto out_release;
+out_illegal_sb:
+ if (!silent)
+ printk("MINIX-fs: bad superblock\n");
+ goto out_release;
+
out_no_fs:
if (!silent)
printk("VFS: Can't find a Minix or Minix V2 filesystem "
"on device %s\n", s->s_id);
- out_release:
+out_release:
brelse(bh);
goto out;
@@ -290,7 +297,7 @@ out_bad_hblock:
out_bad_sb:
printk("MINIX-fs: unable to read superblock\n");
- out:
+out:
s->s_fs_info = NULL;
kfree(sbi);
return -EINVAL;
diff --git a/fs/namei.c b/fs/namei.c
index c784e8bb57a..432d6bc6fab 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -159,7 +159,7 @@ char * getname(const char __user * filename)
#ifdef CONFIG_AUDITSYSCALL
void putname(const char *name)
{
- if (unlikely(current->audit_context))
+ if (unlikely(!audit_dummy_context()))
audit_putname(name);
else
__putname(name);
@@ -227,10 +227,10 @@ int generic_permission(struct inode *inode, int mask,
int permission(struct inode *inode, int mask, struct nameidata *nd)
{
+ umode_t mode = inode->i_mode;
int retval, submask;
if (mask & MAY_WRITE) {
- umode_t mode = inode->i_mode;
/*
* Nobody gets write access to a read-only fs.
@@ -247,6 +247,13 @@ int permission(struct inode *inode, int mask, struct nameidata *nd)
}
+ /*
+ * MAY_EXEC on regular files requires special handling: We override
+ * filesystem execute permissions if the mode bits aren't set.
+ */
+ if ((mask & MAY_EXEC) && S_ISREG(mode) && !(mode & S_IXUGO))
+ return -EACCES;
+
/* Ordinary permission routines do not understand MAY_APPEND. */
submask = mask & ~MAY_APPEND;
if (inode->i_op && inode->i_op->permission)
@@ -1125,7 +1132,7 @@ static int fastcall do_path_lookup(int dfd, const char *name,
retval = link_path_walk(name, nd);
out:
if (likely(retval == 0)) {
- if (unlikely(current->audit_context && nd && nd->dentry &&
+ if (unlikely(!audit_dummy_context() && nd && nd->dentry &&
nd->dentry->d_inode))
audit_inode(name, nd->dentry->d_inode);
}
@@ -1357,7 +1364,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
return -ENOENT;
BUG_ON(victim->d_parent->d_inode != dir);
- audit_inode_child(victim->d_name.name, victim->d_inode, dir->i_ino);
+ audit_inode_child(victim->d_name.name, victim->d_inode, dir);
error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
if (error)
@@ -1423,7 +1430,7 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
struct dentry *p;
if (p1 == p2) {
- mutex_lock(&p1->d_inode->i_mutex);
+ mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT);
return NULL;
}
@@ -1431,22 +1438,22 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
for (p = p1; p->d_parent != p; p = p->d_parent) {
if (p->d_parent == p2) {
- mutex_lock(&p2->d_inode->i_mutex);
- mutex_lock(&p1->d_inode->i_mutex);
+ mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_PARENT);
+ mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_CHILD);
return p;
}
}
for (p = p2; p->d_parent != p; p = p->d_parent) {
if (p->d_parent == p1) {
- mutex_lock(&p1->d_inode->i_mutex);
- mutex_lock(&p2->d_inode->i_mutex);
+ mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT);
+ mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD);
return p;
}
}
- mutex_lock(&p1->d_inode->i_mutex);
- mutex_lock(&p2->d_inode->i_mutex);
+ mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT);
+ mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD);
return NULL;
}
@@ -1659,6 +1666,7 @@ do_last:
* It already exists.
*/
mutex_unlock(&dir->d_inode->i_mutex);
+ audit_inode_update(path.dentry->d_inode);
error = -EEXIST;
if (flag & O_EXCL)
@@ -1669,6 +1677,7 @@ do_last:
if (flag & O_NOFOLLOW)
goto exit_dput;
}
+
error = -ENOENT;
if (!path.dentry->d_inode)
goto exit_dput;
@@ -1712,8 +1721,14 @@ do_link:
if (error)
goto exit_dput;
error = __do_follow_link(&path, nd);
- if (error)
+ if (error) {
+ /* Does someone understand code flow here? Or it is only
+ * me so stupid? Anathema to whoever designed this non-sense
+ * with "intent.open".
+ */
+ release_open_intent(nd);
return error;
+ }
nd->flags &= ~LOOKUP_PARENT;
if (nd->last_type == LAST_BIND)
goto ok;
@@ -1751,7 +1766,7 @@ struct dentry *lookup_create(struct nameidata *nd, int is_dir)
{
struct dentry *dentry = ERR_PTR(-EEXIST);
- mutex_lock(&nd->dentry->d_inode->i_mutex);
+ mutex_lock_nested(&nd->dentry->d_inode->i_mutex, I_MUTEX_PARENT);
/*
* Yucky last component or no last component at all?
* (foo/., foo/.., /////)
@@ -1759,6 +1774,8 @@ struct dentry *lookup_create(struct nameidata *nd, int is_dir)
if (nd->last_type != LAST_NORM)
goto fail;
nd->flags &= ~LOOKUP_PARENT;
+ nd->flags |= LOOKUP_CREATE;
+ nd->intent.open.flags = O_EXCL;
/*
* Do the final lookup.
@@ -2008,7 +2025,7 @@ static long do_rmdir(int dfd, const char __user *pathname)
error = -EBUSY;
goto exit1;
}
- mutex_lock(&nd.dentry->d_inode->i_mutex);
+ mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
dentry = lookup_hash(&nd);
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
@@ -2082,7 +2099,7 @@ static long do_unlinkat(int dfd, const char __user *pathname)
error = -EISDIR;
if (nd.last_type != LAST_NORM)
goto exit1;
- mutex_lock(&nd.dentry->d_inode->i_mutex);
+ mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
dentry = lookup_hash(&nd);
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 3ddda6f7ecc..e7ffb4deb3e 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -690,7 +690,9 @@ int nfs_lookup_verify_inode(struct inode *inode, struct nameidata *nd)
goto out_force;
/* This is an open(2) */
if (nfs_lookup_check_intent(nd, LOOKUP_OPEN) != 0 &&
- !(server->flags & NFS_MOUNT_NOCTO))
+ !(server->flags & NFS_MOUNT_NOCTO) &&
+ (S_ISREG(inode->i_mode) ||
+ S_ISDIR(inode->i_mode)))
goto out_force;
}
return nfs_revalidate_inode(server, inode);
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 4cdd1b499e3..76ca1cbc38f 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -67,25 +67,19 @@ struct nfs_direct_req {
struct kref kref; /* release manager */
/* I/O parameters */
- struct list_head list, /* nfs_read/write_data structs */
- rewrite_list; /* saved nfs_write_data structs */
struct nfs_open_context *ctx; /* file open context info */
struct kiocb * iocb; /* controlling i/o request */
struct inode * inode; /* target file of i/o */
- unsigned long user_addr; /* location of user's buffer */
- size_t user_count; /* total bytes to move */
- loff_t pos; /* starting offset in file */
- struct page ** pages; /* pages in our buffer */
- unsigned int npages; /* count of pages */
/* completion state */
+ atomic_t io_count; /* i/os we're waiting for */
spinlock_t lock; /* protect completion state */
- int outstanding; /* i/os we're waiting for */
ssize_t count, /* bytes actually processed */
error; /* any reported error */
struct completion completion; /* wait for i/o completion */
/* commit state */
+ struct list_head rewrite_list; /* saved nfs_write_data structs */
struct nfs_write_data * commit_data; /* special write_data for commits */
int flags;
#define NFS_ODIRECT_DO_COMMIT (1) /* an unstable reply was received */
@@ -93,8 +87,18 @@ struct nfs_direct_req {
struct nfs_writeverf verf; /* unstable write verifier */
};
-static void nfs_direct_write_schedule(struct nfs_direct_req *dreq, int sync);
static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode);
+static const struct rpc_call_ops nfs_write_direct_ops;
+
+static inline void get_dreq(struct nfs_direct_req *dreq)
+{
+ atomic_inc(&dreq->io_count);
+}
+
+static inline int put_dreq(struct nfs_direct_req *dreq)
+{
+ return atomic_dec_and_test(&dreq->io_count);
+}
/**
* nfs_direct_IO - NFS address space operation for direct I/O
@@ -118,50 +122,21 @@ ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_
return -EINVAL;
}
-static void nfs_free_user_pages(struct page **pages, int npages, int do_dirty)
+static void nfs_direct_dirty_pages(struct page **pages, int npages)
{
int i;
for (i = 0; i < npages; i++) {
struct page *page = pages[i];
- if (do_dirty && !PageCompound(page))
+ if (!PageCompound(page))
set_page_dirty_lock(page);
- page_cache_release(page);
}
- kfree(pages);
}
-static inline int nfs_get_user_pages(int rw, unsigned long user_addr, size_t size, struct page ***pages)
+static void nfs_direct_release_pages(struct page **pages, int npages)
{
- int result = -ENOMEM;
- unsigned long page_count;
- size_t array_size;
-
- page_count = (user_addr + size + PAGE_SIZE - 1) >> PAGE_SHIFT;
- page_count -= user_addr >> PAGE_SHIFT;
-
- array_size = (page_count * sizeof(struct page *));
- *pages = kmalloc(array_size, GFP_KERNEL);
- if (*pages) {
- down_read(&current->mm->mmap_sem);
- result = get_user_pages(current, current->mm, user_addr,
- page_count, (rw == READ), 0,
- *pages, NULL);
- up_read(&current->mm->mmap_sem);
- if (result != page_count) {
- /*
- * If we got fewer pages than expected from
- * get_user_pages(), the user buffer runs off the
- * end of a mapping; return EFAULT.
- */
- if (result >= 0) {
- nfs_free_user_pages(*pages, result, 0);
- result = -EFAULT;
- } else
- kfree(*pages);
- *pages = NULL;
- }
- }
- return result;
+ int i;
+ for (i = 0; i < npages; i++)
+ page_cache_release(pages[i]);
}
static inline struct nfs_direct_req *nfs_direct_req_alloc(void)
@@ -173,13 +148,13 @@ static inline struct nfs_direct_req *nfs_direct_req_alloc(void)
return NULL;
kref_init(&dreq->kref);
+ kref_get(&dreq->kref);
init_completion(&dreq->completion);
- INIT_LIST_HEAD(&dreq->list);
INIT_LIST_HEAD(&dreq->rewrite_list);
dreq->iocb = NULL;
dreq->ctx = NULL;
spin_lock_init(&dreq->lock);
- dreq->outstanding = 0;
+ atomic_set(&dreq->io_count, 0);
dreq->count = 0;
dreq->error = 0;
dreq->flags = 0;
@@ -220,18 +195,11 @@ out:
}
/*
- * We must hold a reference to all the pages in this direct read request
- * until the RPCs complete. This could be long *after* we are woken up in
- * nfs_direct_wait (for instance, if someone hits ^C on a slow server).
- *
- * In addition, synchronous I/O uses a stack-allocated iocb. Thus we
- * can't trust the iocb is still valid here if this is a synchronous
- * request. If the waiter is woken prematurely, the iocb is long gone.
+ * Synchronous I/O uses a stack-allocated iocb. Thus we can't trust
+ * the iocb is still valid here if this is a synchronous request.
*/
static void nfs_direct_complete(struct nfs_direct_req *dreq)
{
- nfs_free_user_pages(dreq->pages, dreq->npages, 1);
-
if (dreq->iocb) {
long res = (long) dreq->error;
if (!res)
@@ -244,48 +212,10 @@ static void nfs_direct_complete(struct nfs_direct_req *dreq)
}
/*
- * Note we also set the number of requests we have in the dreq when we are
- * done. This prevents races with I/O completion so we will always wait
- * until all requests have been dispatched and completed.
+ * We must hold a reference to all the pages in this direct read request
+ * until the RPCs complete. This could be long *after* we are woken up in
+ * nfs_direct_wait (for instance, if someone hits ^C on a slow server).
*/
-static struct nfs_direct_req *nfs_direct_read_alloc(size_t nbytes, size_t rsize)
-{
- struct list_head *list;
- struct nfs_direct_req *dreq;
- unsigned int rpages = (rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
-
- dreq = nfs_direct_req_alloc();
- if (!dreq)
- return NULL;
-
- list = &dreq->list;
- for(;;) {
- struct nfs_read_data *data = nfs_readdata_alloc(rpages);
-
- if (unlikely(!data)) {
- while (!list_empty(list)) {
- data = list_entry(list->next,
- struct nfs_read_data, pages);
- list_del(&data->pages);
- nfs_readdata_free(data);
- }
- kref_put(&dreq->kref, nfs_direct_req_release);
- return NULL;
- }
-
- INIT_LIST_HEAD(&data->pages);
- list_add(&data->pages, list);
-
- data->req = (struct nfs_page *) dreq;
- dreq->outstanding++;
- if (nbytes <= rsize)
- break;
- nbytes -= rsize;
- }
- kref_get(&dreq->kref);
- return dreq;
-}
-
static void nfs_direct_read_result(struct rpc_task *task, void *calldata)
{
struct nfs_read_data *data = calldata;
@@ -294,6 +224,9 @@ static void nfs_direct_read_result(struct rpc_task *task, void *calldata)
if (nfs_readpage_result(task, data) != 0)
return;
+ nfs_direct_dirty_pages(data->pagevec, data->npages);
+ nfs_direct_release_pages(data->pagevec, data->npages);
+
spin_lock(&dreq->lock);
if (likely(task->tk_status >= 0))
@@ -301,13 +234,10 @@ static void nfs_direct_read_result(struct rpc_task *task, void *calldata)
else
dreq->error = task->tk_status;
- if (--dreq->outstanding) {
- spin_unlock(&dreq->lock);
- return;
- }
-
spin_unlock(&dreq->lock);
- nfs_direct_complete(dreq);
+
+ if (put_dreq(dreq))
+ nfs_direct_complete(dreq);
}
static const struct rpc_call_ops nfs_read_direct_ops = {
@@ -316,41 +246,56 @@ static const struct rpc_call_ops nfs_read_direct_ops = {
};
/*
- * For each nfs_read_data struct that was allocated on the list, dispatch
- * an NFS READ operation
+ * For each rsize'd chunk of the user's buffer, dispatch an NFS READ
+ * operation. If nfs_readdata_alloc() or get_user_pages() fails,
+ * bail and stop sending more reads. Read length accounting is
+ * handled automatically by nfs_direct_read_result(). Otherwise, if
+ * no requests have been sent, just return an error.
*/
-static void nfs_direct_read_schedule(struct nfs_direct_req *dreq)
+static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos)
{
struct nfs_open_context *ctx = dreq->ctx;
struct inode *inode = ctx->dentry->d_inode;
- struct list_head *list = &dreq->list;
- struct page **pages = dreq->pages;
- size_t count = dreq->user_count;
- loff_t pos = dreq->pos;
size_t rsize = NFS_SERVER(inode)->rsize;
- unsigned int curpage, pgbase;
+ unsigned int pgbase;
+ int result;
+ ssize_t started = 0;
+
+ get_dreq(dreq);
- curpage = 0;
- pgbase = dreq->user_addr & ~PAGE_MASK;
do {
struct nfs_read_data *data;
size_t bytes;
- bytes = rsize;
- if (count < rsize)
- bytes = count;
+ pgbase = user_addr & ~PAGE_MASK;
+ bytes = min(rsize,count);
- BUG_ON(list_empty(list));
- data = list_entry(list->next, struct nfs_read_data, pages);
- list_del_init(&data->pages);
+ result = -ENOMEM;
+ data = nfs_readdata_alloc(pgbase + bytes);
+ if (unlikely(!data))
+ break;
+
+ down_read(&current->mm->mmap_sem);
+ result = get_user_pages(current, current->mm, user_addr,
+ data->npages, 1, 0, data->pagevec, NULL);
+ up_read(&current->mm->mmap_sem);
+ if (unlikely(result < data->npages)) {
+ if (result > 0)
+ nfs_direct_release_pages(data->pagevec, result);
+ nfs_readdata_release(data);
+ break;
+ }
+
+ get_dreq(dreq);
+ data->req = (struct nfs_page *) dreq;
data->inode = inode;
data->cred = ctx->cred;
data->args.fh = NFS_FH(inode);
data->args.context = ctx;
data->args.offset = pos;
data->args.pgbase = pgbase;
- data->args.pages = &pages[curpage];
+ data->args.pages = data->pagevec;
data->args.count = bytes;
data->res.fattr = &data->fattr;
data->res.eof = 0;
@@ -373,33 +318,37 @@ static void nfs_direct_read_schedule(struct nfs_direct_req *dreq)
bytes,
(unsigned long long)data->args.offset);
+ started += bytes;
+ user_addr += bytes;
pos += bytes;
+ /* FIXME: Remove this unnecessary math from final patch */
pgbase += bytes;
- curpage += pgbase >> PAGE_SHIFT;
pgbase &= ~PAGE_MASK;
+ BUG_ON(pgbase != (user_addr & ~PAGE_MASK));
count -= bytes;
} while (count != 0);
- BUG_ON(!list_empty(list));
+
+ if (put_dreq(dreq))
+ nfs_direct_complete(dreq);
+
+ if (started)
+ return 0;
+ return result < 0 ? (ssize_t) result : -EFAULT;
}
-static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos, struct page **pages, unsigned int nr_pages)
+static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos)
{
- ssize_t result;
+ ssize_t result = 0;
sigset_t oldset;
struct inode *inode = iocb->ki_filp->f_mapping->host;
struct rpc_clnt *clnt = NFS_CLIENT(inode);
struct nfs_direct_req *dreq;
- dreq = nfs_direct_read_alloc(count, NFS_SERVER(inode)->rsize);
+ dreq = nfs_direct_req_alloc();
if (!dreq)
return -ENOMEM;
- dreq->user_addr = user_addr;
- dreq->user_count = count;
- dreq->pos = pos;
- dreq->pages = pages;
- dreq->npages = nr_pages;
dreq->inode = inode;
dreq->ctx = get_nfs_open_context((struct nfs_open_context *)iocb->ki_filp->private_data);
if (!is_sync_kiocb(iocb))
@@ -407,8 +356,9 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size
nfs_add_stats(inode, NFSIOS_DIRECTREADBYTES, count);
rpc_clnt_sigmask(clnt, &oldset);
- nfs_direct_read_schedule(dreq);
- result = nfs_direct_wait(dreq);
+ result = nfs_direct_read_schedule(dreq, user_addr, count, pos);
+ if (!result)
+ result = nfs_direct_wait(dreq);
rpc_clnt_sigunmask(clnt, &oldset);
return result;
@@ -416,10 +366,10 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size
static void nfs_direct_free_writedata(struct nfs_direct_req *dreq)
{
- list_splice_init(&dreq->rewrite_list, &dreq->list);
- while (!list_empty(&dreq->list)) {
- struct nfs_write_data *data = list_entry(dreq->list.next, struct nfs_write_data, pages);
+ while (!list_empty(&dreq->rewrite_list)) {
+ struct nfs_write_data *data = list_entry(dreq->rewrite_list.next, struct nfs_write_data, pages);
list_del(&data->pages);
+ nfs_direct_release_pages(data->pagevec, data->npages);
nfs_writedata_release(data);
}
}
@@ -427,14 +377,51 @@ static void nfs_direct_free_writedata(struct nfs_direct_req *dreq)
#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
{
- struct list_head *pos;
+ struct inode *inode = dreq->inode;
+ struct list_head *p;
+ struct nfs_write_data *data;
- list_splice_init(&dreq->rewrite_list, &dreq->list);
- list_for_each(pos, &dreq->list)
- dreq->outstanding++;
dreq->count = 0;
+ get_dreq(dreq);
+
+ list_for_each(p, &dreq->rewrite_list) {
+ data = list_entry(p, struct nfs_write_data, pages);
+
+ get_dreq(dreq);
- nfs_direct_write_schedule(dreq, FLUSH_STABLE);
+ /*
+ * Reset data->res.
+ */
+ nfs_fattr_init(&data->fattr);
+ data->res.count = data->args.count;
+ memset(&data->verf, 0, sizeof(data->verf));
+
+ /*
+ * Reuse data->task; data->args should not have changed
+ * since the original request was sent.
+ */
+ rpc_init_task(&data->task, NFS_CLIENT(inode), RPC_TASK_ASYNC,
+ &nfs_write_direct_ops, data);
+ NFS_PROTO(inode)->write_setup(data, FLUSH_STABLE);
+
+ data->task.tk_priority = RPC_PRIORITY_NORMAL;
+ data->task.tk_cookie = (unsigned long) inode;
+
+ /*
+ * We're called via an RPC callback, so BKL is already held.
+ */
+ rpc_execute(&data->task);
+
+ dprintk("NFS: %5u rescheduled direct write call (req %s/%Ld, %u bytes @ offset %Lu)\n",
+ data->task.tk_pid,
+ inode->i_sb->s_id,
+ (long long)NFS_FILEID(inode),
+ data->args.count,
+ (unsigned long long)data->args.offset);
+ }
+
+ if (put_dreq(dreq))
+ nfs_direct_write_complete(dreq, inode);
}
static void nfs_direct_commit_result(struct rpc_task *task, void *calldata)
@@ -471,8 +458,8 @@ static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
data->cred = dreq->ctx->cred;
data->args.fh = NFS_FH(data->inode);
- data->args.offset = dreq->pos;
- data->args.count = dreq->user_count;
+ data->args.offset = 0;
+ data->args.count = 0;
data->res.count = 0;
data->res.fattr = &data->fattr;
data->res.verf = &data->verf;
@@ -516,7 +503,7 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode
static void nfs_alloc_commit_data(struct nfs_direct_req *dreq)
{
- dreq->commit_data = nfs_commit_alloc(0);
+ dreq->commit_data = nfs_commit_alloc();
if (dreq->commit_data != NULL)
dreq->commit_data->req = (struct nfs_page *) dreq;
}
@@ -534,47 +521,6 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode
}
#endif
-static struct nfs_direct_req *nfs_direct_write_alloc(size_t nbytes, size_t wsize)
-{
- struct list_head *list;
- struct nfs_direct_req *dreq;
- unsigned int wpages = (wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
-
- dreq = nfs_direct_req_alloc();
- if (!dreq)
- return NULL;
-
- list = &dreq->list;
- for(;;) {
- struct nfs_write_data *data = nfs_writedata_alloc(wpages);
-
- if (unlikely(!data)) {
- while (!list_empty(list)) {
- data = list_entry(list->next,
- struct nfs_write_data, pages);
- list_del(&data->pages);
- nfs_writedata_free(data);
- }
- kref_put(&dreq->kref, nfs_direct_req_release);
- return NULL;
- }
-
- INIT_LIST_HEAD(&data->pages);
- list_add(&data->pages, list);
-
- data->req = (struct nfs_page *) dreq;
- dreq->outstanding++;
- if (nbytes <= wsize)
- break;
- nbytes -= wsize;
- }
-
- nfs_alloc_commit_data(dreq);
-
- kref_get(&dreq->kref);
- return dreq;
-}
-
static void nfs_direct_write_result(struct rpc_task *task, void *calldata)
{
struct nfs_write_data *data = calldata;
@@ -604,8 +550,6 @@ static void nfs_direct_write_result(struct rpc_task *task, void *calldata)
}
}
}
- /* In case we have to resend */
- data->args.stable = NFS_FILE_SYNC;
spin_unlock(&dreq->lock);
}
@@ -619,14 +563,8 @@ static void nfs_direct_write_release(void *calldata)
struct nfs_write_data *data = calldata;
struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
- spin_lock(&dreq->lock);
- if (--dreq->outstanding) {
- spin_unlock(&dreq->lock);
- return;
- }
- spin_unlock(&dreq->lock);
-
- nfs_direct_write_complete(dreq, data->inode);
+ if (put_dreq(dreq))
+ nfs_direct_write_complete(dreq, data->inode);
}
static const struct rpc_call_ops nfs_write_direct_ops = {
@@ -635,41 +573,58 @@ static const struct rpc_call_ops nfs_write_direct_ops = {
};
/*
- * For each nfs_write_data struct that was allocated on the list, dispatch
- * an NFS WRITE operation
+ * For each wsize'd chunk of the user's buffer, dispatch an NFS WRITE
+ * operation. If nfs_writedata_alloc() or get_user_pages() fails,
+ * bail and stop sending more writes. Write length accounting is
+ * handled automatically by nfs_direct_write_result(). Otherwise, if
+ * no requests have been sent, just return an error.
*/
-static void nfs_direct_write_schedule(struct nfs_direct_req *dreq, int sync)
+static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos, int sync)
{
struct nfs_open_context *ctx = dreq->ctx;
struct inode *inode = ctx->dentry->d_inode;
- struct list_head *list = &dreq->list;
- struct page **pages = dreq->pages;
- size_t count = dreq->user_count;
- loff_t pos = dreq->pos;
size_t wsize = NFS_SERVER(inode)->wsize;
- unsigned int curpage, pgbase;
+ unsigned int pgbase;
+ int result;
+ ssize_t started = 0;
+
+ get_dreq(dreq);
- curpage = 0;
- pgbase = dreq->user_addr & ~PAGE_MASK;
do {
struct nfs_write_data *data;
size_t bytes;
- bytes = wsize;
- if (count < wsize)
- bytes = count;
+ pgbase = user_addr & ~PAGE_MASK;
+ bytes = min(wsize,count);
+
+ result = -ENOMEM;
+ data = nfs_writedata_alloc(pgbase + bytes);
+ if (unlikely(!data))
+ break;
+
+ down_read(&current->mm->mmap_sem);
+ result = get_user_pages(current, current->mm, user_addr,
+ data->npages, 0, 0, data->pagevec, NULL);
+ up_read(&current->mm->mmap_sem);
+ if (unlikely(result < data->npages)) {
+ if (result > 0)
+ nfs_direct_release_pages(data->pagevec, result);
+ nfs_writedata_release(data);
+ break;
+ }
+
+ get_dreq(dreq);
- BUG_ON(list_empty(list));
- data = list_entry(list->next, struct nfs_write_data, pages);
list_move_tail(&data->pages, &dreq->rewrite_list);
+ data->req = (struct nfs_page *) dreq;
data->inode = inode;
data->cred = ctx->cred;
data->args.fh = NFS_FH(inode);
data->args.context = ctx;
data->args.offset = pos;
data->args.pgbase = pgbase;
- data->args.pages = &pages[curpage];
+ data->args.pages = data->pagevec;
data->args.count = bytes;
data->res.fattr = &data->fattr;
data->res.count = bytes;
@@ -693,19 +648,29 @@ static void nfs_direct_write_schedule(struct nfs_direct_req *dreq, int sync)
bytes,
(unsigned long long)data->args.offset);
+ started += bytes;
+ user_addr += bytes;
pos += bytes;
+
+ /* FIXME: Remove this useless math from the final patch */
pgbase += bytes;
- curpage += pgbase >> PAGE_SHIFT;
pgbase &= ~PAGE_MASK;
+ BUG_ON(pgbase != (user_addr & ~PAGE_MASK));
count -= bytes;
} while (count != 0);
- BUG_ON(!list_empty(list));
+
+ if (put_dreq(dreq))
+ nfs_direct_write_complete(dreq, inode);
+
+ if (started)
+ return 0;
+ return result < 0 ? (ssize_t) result : -EFAULT;
}
-static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos, struct page **pages, int nr_pages)
+static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos)
{
- ssize_t result;
+ ssize_t result = 0;
sigset_t oldset;
struct inode *inode = iocb->ki_filp->f_mapping->host;
struct rpc_clnt *clnt = NFS_CLIENT(inode);
@@ -713,17 +678,14 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, siz
size_t wsize = NFS_SERVER(inode)->wsize;
int sync = 0;
- dreq = nfs_direct_write_alloc(count, wsize);
+ dreq = nfs_direct_req_alloc();
if (!dreq)
return -ENOMEM;
+ nfs_alloc_commit_data(dreq);
+
if (dreq->commit_data == NULL || count < wsize)
sync = FLUSH_STABLE;
- dreq->user_addr = user_addr;
- dreq->user_count = count;
- dreq->pos = pos;
- dreq->pages = pages;
- dreq->npages = nr_pages;
dreq->inode = inode;
dreq->ctx = get_nfs_open_context((struct nfs_open_context *)iocb->ki_filp->private_data);
if (!is_sync_kiocb(iocb))
@@ -734,8 +696,9 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, siz
nfs_begin_data_update(inode);
rpc_clnt_sigmask(clnt, &oldset);
- nfs_direct_write_schedule(dreq, sync);
- result = nfs_direct_wait(dreq);
+ result = nfs_direct_write_schedule(dreq, user_addr, count, pos, sync);
+ if (!result)
+ result = nfs_direct_wait(dreq);
rpc_clnt_sigunmask(clnt, &oldset);
return result;
@@ -765,8 +728,6 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, siz
ssize_t nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t pos)
{
ssize_t retval = -EINVAL;
- int page_count;
- struct page **pages;
struct file *file = iocb->ki_filp;
struct address_space *mapping = file->f_mapping;
@@ -788,14 +749,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count,
if (retval)
goto out;
- retval = nfs_get_user_pages(READ, (unsigned long) buf,
- count, &pages);
- if (retval < 0)
- goto out;
- page_count = retval;
-
- retval = nfs_direct_read(iocb, (unsigned long) buf, count, pos,
- pages, page_count);
+ retval = nfs_direct_read(iocb, (unsigned long) buf, count, pos);
if (retval > 0)
iocb->ki_pos = pos + retval;
@@ -831,8 +785,6 @@ out:
ssize_t nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count, loff_t pos)
{
ssize_t retval;
- int page_count;
- struct page **pages;
struct file *file = iocb->ki_filp;
struct address_space *mapping = file->f_mapping;
@@ -860,14 +812,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t
if (retval)
goto out;
- retval = nfs_get_user_pages(WRITE, (unsigned long) buf,
- count, &pages);
- if (retval < 0)
- goto out;
- page_count = retval;
-
- retval = nfs_direct_write(iocb, (unsigned long) buf, count,
- pos, pages, page_count);
+ retval = nfs_direct_write(iocb, (unsigned long) buf, count, pos);
/*
* XXX: nfs_end_data_update() already ensures this file's
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index cc2b874ad5a..48e892880d5 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -312,7 +312,13 @@ static void nfs_invalidate_page(struct page *page, unsigned long offset)
static int nfs_release_page(struct page *page, gfp_t gfp)
{
- return !nfs_wb_page(page->mapping->host, page);
+ if (gfp & __GFP_FS)
+ return !nfs_wb_page(page->mapping->host, page);
+ else
+ /*
+ * Avoid deadlock on nfs_wait_on_request().
+ */
+ return 0;
}
const struct address_space_operations nfs_file_aops = {
diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c
index b81e7ed3c90..07a5dd57646 100644
--- a/fs/nfs/idmap.c
+++ b/fs/nfs/idmap.c
@@ -130,9 +130,7 @@ nfs_idmap_delete(struct nfs4_client *clp)
if (!idmap)
return;
- dput(idmap->idmap_dentry);
- idmap->idmap_dentry = NULL;
- rpc_unlink(idmap->idmap_path);
+ rpc_unlink(idmap->idmap_dentry);
clp->cl_idmap = NULL;
kfree(idmap);
}
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index 19b98ca468e..86b3169c8ca 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -51,7 +51,7 @@ char *nfs_path(const char *base, const struct dentry *dentry,
namelen = dentry->d_name.len;
buflen -= namelen + 1;
if (buflen < 0)
- goto Elong;
+ goto Elong_unlock;
end -= namelen;
memcpy(end, dentry->d_name.name, namelen);
*--end = '/';
@@ -68,6 +68,8 @@ char *nfs_path(const char *base, const struct dentry *dentry,
end -= namelen;
memcpy(end, base, namelen);
return end;
+Elong_unlock:
+ spin_unlock(&dcache_lock);
Elong:
return ERR_PTR(-ENAMETOOLONG);
}
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index b4916b09219..153898e1331 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2668,7 +2668,7 @@ out:
nfs4_set_cached_acl(inode, acl);
}
-static inline ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
+static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
{
struct page *pages[NFS4ACL_MAXPAGES];
struct nfs_getaclargs args = {
@@ -2721,6 +2721,19 @@ out_free:
return ret;
}
+static ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
+{
+ struct nfs4_exception exception = { };
+ ssize_t ret;
+ do {
+ ret = __nfs4_get_acl_uncached(inode, buf, buflen);
+ if (ret >= 0)
+ break;
+ ret = nfs4_handle_exception(NFS_SERVER(inode), ret, &exception);
+ } while (exception.retry);
+ return ret;
+}
+
static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
{
struct nfs_server *server = NFS_SERVER(inode);
@@ -2737,7 +2750,7 @@ static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
return nfs4_get_acl_uncached(inode, buf, buflen);
}
-static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
+static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
{
struct nfs_server *server = NFS_SERVER(inode);
struct page *pages[NFS4ACL_MAXPAGES];
@@ -2763,6 +2776,18 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen
return ret;
}
+static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
+{
+ struct nfs4_exception exception = { };
+ int err;
+ do {
+ err = nfs4_handle_exception(NFS_SERVER(inode),
+ __nfs4_proc_set_acl(inode, buf, buflen),
+ &exception);
+ } while (exception.retry);
+ return err;
+}
+
static int
nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
{
@@ -3144,9 +3169,6 @@ static int do_vfs_lock(struct file *file, struct file_lock *fl)
default:
BUG();
}
- if (res < 0)
- printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n",
- __FUNCTION__);
return res;
}
@@ -3258,8 +3280,6 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
return ERR_PTR(-ENOMEM);
}
- /* Unlock _before_ we do the RPC call */
- do_vfs_lock(fl->fl_file, fl);
return rpc_run_task(NFS_CLIENT(lsp->ls_state->inode), RPC_TASK_ASYNC, &nfs4_locku_ops, data);
}
@@ -3270,30 +3290,28 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *
struct rpc_task *task;
int status = 0;
- /* Is this a delegated lock? */
- if (test_bit(NFS_DELEGATED_STATE, &state->flags))
- goto out_unlock;
- /* Is this open_owner holding any locks on the server? */
- if (test_bit(LK_STATE_IN_USE, &state->flags) == 0)
- goto out_unlock;
-
status = nfs4_set_lock_state(state, request);
+ /* Unlock _before_ we do the RPC call */
+ request->fl_flags |= FL_EXISTS;
+ if (do_vfs_lock(request->fl_file, request) == -ENOENT)
+ goto out;
if (status != 0)
- goto out_unlock;
+ goto out;
+ /* Is this a delegated lock? */
+ if (test_bit(NFS_DELEGATED_STATE, &state->flags))
+ goto out;
lsp = request->fl_u.nfs4_fl.owner;
- status = -ENOMEM;
seqid = nfs_alloc_seqid(&lsp->ls_seqid);
+ status = -ENOMEM;
if (seqid == NULL)
- goto out_unlock;
+ goto out;
task = nfs4_do_unlck(request, request->fl_file->private_data, lsp, seqid);
status = PTR_ERR(task);
if (IS_ERR(task))
- goto out_unlock;
+ goto out;
status = nfs4_wait_for_completion_rpc_task(task);
rpc_release_task(task);
- return status;
-out_unlock:
- do_vfs_lock(request->fl_file, request);
+out:
return status;
}
@@ -3461,10 +3479,10 @@ static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request
struct nfs4_exception exception = { };
int err;
- /* Cache the lock if possible... */
- if (test_bit(NFS_DELEGATED_STATE, &state->flags))
- return 0;
do {
+ /* Cache the lock if possible... */
+ if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
+ return 0;
err = _nfs4_do_setlk(state, F_SETLK, request, 1);
if (err != -NFS4ERR_DELAY)
break;
@@ -3483,6 +3501,8 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request
if (err != 0)
return err;
do {
+ if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
+ return 0;
err = _nfs4_do_setlk(state, F_SETLK, request, 0);
if (err != -NFS4ERR_DELAY)
break;
@@ -3494,29 +3514,42 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request
static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
struct nfs4_client *clp = state->owner->so_client;
+ unsigned char fl_flags = request->fl_flags;
int status;
/* Is this a delegated open? */
- if (NFS_I(state->inode)->delegation_state != 0) {
- /* Yes: cache locks! */
- status = do_vfs_lock(request->fl_file, request);
- /* ...but avoid races with delegation recall... */
- if (status < 0 || test_bit(NFS_DELEGATED_STATE, &state->flags))
- return status;
- }
- down_read(&clp->cl_sem);
status = nfs4_set_lock_state(state, request);
if (status != 0)
goto out;
+ request->fl_flags |= FL_ACCESS;
+ status = do_vfs_lock(request->fl_file, request);
+ if (status < 0)
+ goto out;
+ down_read(&clp->cl_sem);
+ if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
+ struct nfs_inode *nfsi = NFS_I(state->inode);
+ /* Yes: cache locks! */
+ down_read(&nfsi->rwsem);
+ /* ...but avoid races with delegation recall... */
+ if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
+ request->fl_flags = fl_flags & ~FL_SLEEP;
+ status = do_vfs_lock(request->fl_file, request);
+ up_read(&nfsi->rwsem);
+ goto out_unlock;
+ }
+ up_read(&nfsi->rwsem);
+ }
status = _nfs4_do_setlk(state, cmd, request, 0);
if (status != 0)
- goto out;
+ goto out_unlock;
/* Note: we always want to sleep here! */
- request->fl_flags |= FL_SLEEP;
+ request->fl_flags = fl_flags | FL_SLEEP;
if (do_vfs_lock(request->fl_file, request) < 0)
printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__);
-out:
+out_unlock:
up_read(&clp->cl_sem);
+out:
+ request->fl_flags = fl_flags;
return status;
}
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 1750d996f49..730ec8fb31c 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -3355,7 +3355,7 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
struct kvec *iov = rcvbuf->head;
unsigned int nr, pglen = rcvbuf->page_len;
uint32_t *end, *entry, *p, *kaddr;
- uint32_t len, attrlen;
+ uint32_t len, attrlen, xlen;
int hdrlen, recvd, status;
status = decode_op_hdr(xdr, OP_READDIR);
@@ -3377,10 +3377,10 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
BUG_ON(pglen + readdir->pgbase > PAGE_CACHE_SIZE);
kaddr = p = (uint32_t *) kmap_atomic(page, KM_USER0);
- end = (uint32_t *) ((char *)p + pglen + readdir->pgbase);
+ end = p + ((pglen + readdir->pgbase) >> 2);
entry = p;
for (nr = 0; *p++; nr++) {
- if (p + 3 > end)
+ if (end - p < 3)
goto short_pkt;
dprintk("cookie = %Lu, ", *((unsigned long long *)p));
p += 2; /* cookie */
@@ -3389,18 +3389,19 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
printk(KERN_WARNING "NFS: giant filename in readdir (len 0x%x)\n", len);
goto err_unmap;
}
- dprintk("filename = %*s\n", len, (char *)p);
- p += XDR_QUADLEN(len);
- if (p + 1 > end)
+ xlen = XDR_QUADLEN(len);
+ if (end - p < xlen + 1)
goto short_pkt;
+ dprintk("filename = %*s\n", len, (char *)p);
+ p += xlen;
len = ntohl(*p++); /* bitmap length */
- p += len;
- if (p + 1 > end)
+ if (end - p < len + 1)
goto short_pkt;
+ p += len;
attrlen = XDR_QUADLEN(ntohl(*p++));
- p += attrlen; /* attributes */
- if (p + 2 > end)
+ if (end - p < attrlen + 2)
goto short_pkt;
+ p += attrlen; /* attributes */
entry = p;
}
if (!nr && (entry[0] != 0 || entry[1] == 0))
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 52bf634260a..7a9ee00e0c6 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -43,13 +43,15 @@ static mempool_t *nfs_rdata_mempool;
#define MIN_POOL_READ (32)
-struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount)
+struct nfs_read_data *nfs_readdata_alloc(size_t len)
{
+ unsigned int pagecount = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
struct nfs_read_data *p = mempool_alloc(nfs_rdata_mempool, SLAB_NOFS);
if (p) {
memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->pages);
+ p->npages = pagecount;
if (pagecount <= ARRAY_SIZE(p->page_array))
p->pagevec = p->page_array;
else {
@@ -63,7 +65,7 @@ struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount)
return p;
}
-void nfs_readdata_free(struct nfs_read_data *p)
+static void nfs_readdata_free(struct nfs_read_data *p)
{
if (p && (p->pagevec != &p->page_array[0]))
kfree(p->pagevec);
@@ -116,10 +118,17 @@ static void nfs_readpage_truncate_uninitialised_page(struct nfs_read_data *data)
pages = &data->args.pages[base >> PAGE_CACHE_SHIFT];
base &= ~PAGE_CACHE_MASK;
pglen = PAGE_CACHE_SIZE - base;
- if (pglen < remainder)
+ for (;;) {
+ if (remainder <= pglen) {
+ memclear_highpage_flush(*pages, base, remainder);
+ break;
+ }
memclear_highpage_flush(*pages, base, pglen);
- else
- memclear_highpage_flush(*pages, base, remainder);
+ pages++;
+ remainder -= pglen;
+ pglen = PAGE_CACHE_SIZE;
+ base = 0;
+ }
}
/*
@@ -133,7 +142,7 @@ static int nfs_readpage_sync(struct nfs_open_context *ctx, struct inode *inode,
int result;
struct nfs_read_data *rdata;
- rdata = nfs_readdata_alloc(1);
+ rdata = nfs_readdata_alloc(count);
if (!rdata)
return -ENOMEM;
@@ -329,25 +338,25 @@ static int nfs_pagein_multi(struct list_head *head, struct inode *inode)
struct nfs_page *req = nfs_list_entry(head->next);
struct page *page = req->wb_page;
struct nfs_read_data *data;
- unsigned int rsize = NFS_SERVER(inode)->rsize;
- unsigned int nbytes, offset;
+ size_t rsize = NFS_SERVER(inode)->rsize, nbytes;
+ unsigned int offset;
int requests = 0;
LIST_HEAD(list);
nfs_list_remove_request(req);
nbytes = req->wb_bytes;
- for(;;) {
- data = nfs_readdata_alloc(1);
+ do {
+ size_t len = min(nbytes,rsize);
+
+ data = nfs_readdata_alloc(len);
if (!data)
goto out_bad;
INIT_LIST_HEAD(&data->pages);
list_add(&data->pages, &list);
requests++;
- if (nbytes <= rsize)
- break;
- nbytes -= rsize;
- }
+ nbytes -= len;
+ } while(nbytes != 0);
atomic_set(&req->wb_complete, requests);
ClearPageError(page);
@@ -395,7 +404,7 @@ static int nfs_pagein_one(struct list_head *head, struct inode *inode)
if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE)
return nfs_pagein_multi(head, inode);
- data = nfs_readdata_alloc(NFS_SERVER(inode)->rpages);
+ data = nfs_readdata_alloc(NFS_SERVER(inode)->rsize);
if (!data)
goto out_bad;
@@ -476,6 +485,8 @@ static void nfs_readpage_set_pages_uptodate(struct nfs_read_data *data)
unsigned int base = data->args.pgbase;
struct page **pages;
+ if (data->res.eof)
+ count = data->args.count;
if (unlikely(count == 0))
return;
pages = &data->args.pages[base >> PAGE_CACHE_SHIFT];
@@ -483,11 +494,7 @@ static void nfs_readpage_set_pages_uptodate(struct nfs_read_data *data)
count += base;
for (;count >= PAGE_CACHE_SIZE; count -= PAGE_CACHE_SIZE, pages++)
SetPageUptodate(*pages);
- /*
- * Was this an eof or a short read? If the latter, don't mark the page
- * as uptodate yet.
- */
- if (count > 0 && (data->res.eof || data->args.count == data->res.count))
+ if (count != 0)
SetPageUptodate(*pages);
}
@@ -502,6 +509,8 @@ static void nfs_readpage_set_pages_error(struct nfs_read_data *data)
count += base;
for (;count >= PAGE_CACHE_SIZE; count -= PAGE_CACHE_SIZE, pages++)
SetPageError(*pages);
+ if (count != 0)
+ SetPageError(*pages);
}
/*
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index bca5734ca9f..8ab3cf10d79 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -90,22 +90,13 @@ static mempool_t *nfs_commit_mempool;
static DECLARE_WAIT_QUEUE_HEAD(nfs_write_congestion);
-struct nfs_write_data *nfs_commit_alloc(unsigned int pagecount)
+struct nfs_write_data *nfs_commit_alloc(void)
{
struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, SLAB_NOFS);
if (p) {
memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->pages);
- if (pagecount <= ARRAY_SIZE(p->page_array))
- p->pagevec = p->page_array;
- else {
- p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS);
- if (!p->pagevec) {
- mempool_free(p, nfs_commit_mempool);
- p = NULL;
- }
- }
}
return p;
}
@@ -117,13 +108,15 @@ void nfs_commit_free(struct nfs_write_data *p)
mempool_free(p, nfs_commit_mempool);
}
-struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
+struct nfs_write_data *nfs_writedata_alloc(size_t len)
{
+ unsigned int pagecount = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, SLAB_NOFS);
if (p) {
memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->pages);
+ p->npages = pagecount;
if (pagecount <= ARRAY_SIZE(p->page_array))
p->pagevec = p->page_array;
else {
@@ -137,7 +130,7 @@ struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
return p;
}
-void nfs_writedata_free(struct nfs_write_data *p)
+static void nfs_writedata_free(struct nfs_write_data *p)
{
if (p && (p->pagevec != &p->page_array[0]))
kfree(p->pagevec);
@@ -208,7 +201,7 @@ static int nfs_writepage_sync(struct nfs_open_context *ctx, struct inode *inode,
int result, written = 0;
struct nfs_write_data *wdata;
- wdata = nfs_writedata_alloc(1);
+ wdata = nfs_writedata_alloc(wsize);
if (!wdata)
return -ENOMEM;
@@ -578,7 +571,7 @@ static int nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, un
return ret;
}
-static void nfs_cancel_requests(struct list_head *head)
+static void nfs_cancel_dirty_list(struct list_head *head)
{
struct nfs_page *req;
while(!list_empty(head)) {
@@ -589,6 +582,19 @@ static void nfs_cancel_requests(struct list_head *head)
}
}
+static void nfs_cancel_commit_list(struct list_head *head)
+{
+ struct nfs_page *req;
+
+ while(!list_empty(head)) {
+ req = nfs_list_entry(head->next);
+ nfs_list_remove_request(req);
+ nfs_inode_remove_request(req);
+ nfs_clear_page_writeback(req);
+ dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
+ }
+}
+
/*
* nfs_scan_dirty - Scan an inode for dirty requests
* @inode: NFS inode to scan
@@ -986,24 +992,24 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, int how)
struct nfs_page *req = nfs_list_entry(head->next);
struct page *page = req->wb_page;
struct nfs_write_data *data;
- unsigned int wsize = NFS_SERVER(inode)->wsize;
- unsigned int nbytes, offset;
+ size_t wsize = NFS_SERVER(inode)->wsize, nbytes;
+ unsigned int offset;
int requests = 0;
LIST_HEAD(list);
nfs_list_remove_request(req);
nbytes = req->wb_bytes;
- for (;;) {
- data = nfs_writedata_alloc(1);
+ do {
+ size_t len = min(nbytes, wsize);
+
+ data = nfs_writedata_alloc(len);
if (!data)
goto out_bad;
list_add(&data->pages, &list);
requests++;
- if (nbytes <= wsize)
- break;
- nbytes -= wsize;
- }
+ nbytes -= len;
+ } while (nbytes != 0);
atomic_set(&req->wb_complete, requests);
ClearPageError(page);
@@ -1057,7 +1063,7 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, int how)
struct nfs_write_data *data;
unsigned int count;
- data = nfs_writedata_alloc(NFS_SERVER(inode)->wpages);
+ data = nfs_writedata_alloc(NFS_SERVER(inode)->wsize);
if (!data)
goto out_bad;
@@ -1365,7 +1371,7 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how)
struct nfs_write_data *data;
struct nfs_page *req;
- data = nfs_commit_alloc(NFS_SERVER(inode)->wpages);
+ data = nfs_commit_alloc();
if (!data)
goto out_bad;
@@ -1381,6 +1387,7 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how)
nfs_list_remove_request(req);
nfs_mark_request_commit(req);
nfs_clear_page_writeback(req);
+ dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
}
return -ENOMEM;
}
@@ -1499,7 +1506,7 @@ int nfs_sync_inode_wait(struct inode *inode, unsigned long idx_start,
if (pages != 0) {
spin_unlock(&nfsi->req_lock);
if (how & FLUSH_INVALIDATE)
- nfs_cancel_requests(&head);
+ nfs_cancel_dirty_list(&head);
else
ret = nfs_flush_list(inode, &head, pages, how);
spin_lock(&nfsi->req_lock);
@@ -1512,7 +1519,7 @@ int nfs_sync_inode_wait(struct inode *inode, unsigned long idx_start,
break;
if (how & FLUSH_INVALIDATE) {
spin_unlock(&nfsi->req_lock);
- nfs_cancel_requests(&head);
+ nfs_cancel_commit_list(&head);
spin_lock(&nfsi->req_lock);
continue;
}
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index b0e095ea0c0..ee4eff27aed 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -721,6 +721,12 @@ nfsd4_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
return nfs_ok;
}
+static inline void nfsd4_increment_op_stats(u32 opnum)
+{
+ if (opnum >= FIRST_NFS4_OP && opnum <= LAST_NFS4_OP)
+ nfsdstats.nfs4_opcount[opnum]++;
+}
+
/*
* COMPOUND call.
@@ -930,6 +936,8 @@ encode_op:
/* XXX Ugh, we need to get rid of this kind of special case: */
if (op->opnum == OP_READ && op->u.read.rd_filp)
fput(op->u.read.rd_filp);
+
+ nfsd4_increment_op_stats(op->opnum);
}
out:
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index ecc439d2565..501d8388453 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -187,6 +187,11 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
goto out;
}
+ /* Set user creds for this exportpoint */
+ error = nfserrno(nfsd_setuser(rqstp, exp));
+ if (error)
+ goto out;
+
/*
* Look up the dentry using the NFS file handle.
*/
@@ -241,16 +246,17 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
dprintk("nfsd: fh_verify - just checking\n");
dentry = fhp->fh_dentry;
exp = fhp->fh_export;
+ /* Set user creds for this exportpoint; necessary even
+ * in the "just checking" case because this may be a
+ * filehandle that was created by fh_compose, and that
+ * is about to be used in another nfsv4 compound
+ * operation */
+ error = nfserrno(nfsd_setuser(rqstp, exp));
+ if (error)
+ goto out;
}
cache_get(&exp->h);
- /* Set user creds for this exportpoint; necessary even in the "just
- * checking" case because this may be a filehandle that was created by
- * fh_compose, and that is about to be used in another nfsv4 compound
- * operation */
- error = nfserrno(nfsd_setuser(rqstp, exp));
- if (error)
- goto out;
error = nfsd_mode_check(rqstp, dentry->d_inode->i_mode, type);
if (error)
diff --git a/fs/nfsd/stats.c b/fs/nfsd/stats.c
index 57265d56380..71944cddf68 100644
--- a/fs/nfsd/stats.c
+++ b/fs/nfsd/stats.c
@@ -72,6 +72,16 @@ static int nfsd_proc_show(struct seq_file *seq, void *v)
/* show my rpc info */
svc_seq_show(seq, &nfsd_svcstats);
+#ifdef CONFIG_NFSD_V4
+ /* Show count for individual nfsv4 operations */
+ /* Writing operation numbers 0 1 2 also for maintaining uniformity */
+ seq_printf(seq,"proc4ops %u", LAST_NFS4_OP + 1);
+ for (i = 0; i <= LAST_NFS4_OP; i++)
+ seq_printf(seq, " %u", nfsdstats.nfs4_opcount[i]);
+
+ seq_putc(seq, '\n');
+#endif
+
return 0;
}
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index 4c86b7e1d1e..d313f356e66 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -367,6 +367,12 @@ static void ntfs_destroy_extent_inode(ntfs_inode *ni)
kmem_cache_free(ntfs_inode_cache, ni);
}
+/*
+ * The attribute runlist lock has separate locking rules from the
+ * normal runlist lock, so split the two lock-classes:
+ */
+static struct lock_class_key attr_list_rl_lock_class;
+
/**
* __ntfs_init_inode - initialize ntfs specific part of an inode
* @sb: super block of mounted volume
@@ -394,6 +400,8 @@ void __ntfs_init_inode(struct super_block *sb, ntfs_inode *ni)
ni->attr_list_size = 0;
ni->attr_list = NULL;
ntfs_init_runlist(&ni->attr_list_rl);
+ lockdep_set_class(&ni->attr_list_rl.lock,
+ &attr_list_rl_lock_class);
ni->itype.index.bmp_ino = NULL;
ni->itype.index.block_size = 0;
ni->itype.index.vcn_size = 0;
@@ -405,6 +413,13 @@ void __ntfs_init_inode(struct super_block *sb, ntfs_inode *ni)
ni->ext.base_ntfs_ino = NULL;
}
+/*
+ * Extent inodes get MFT-mapped in a nested way, while the base inode
+ * is still mapped. Teach this nesting to the lock validator by creating
+ * a separate class for nested inode's mrec_lock's:
+ */
+static struct lock_class_key extent_inode_mrec_lock_key;
+
inline ntfs_inode *ntfs_new_extent_inode(struct super_block *sb,
unsigned long mft_no)
{
@@ -413,6 +428,7 @@ inline ntfs_inode *ntfs_new_extent_inode(struct super_block *sb,
ntfs_debug("Entering.");
if (likely(ni != NULL)) {
__ntfs_init_inode(sb, ni);
+ lockdep_set_class(&ni->mrec_lock, &extent_inode_mrec_lock_key);
ni->mft_no = mft_no;
ni->type = AT_UNUSED;
ni->name = NULL;
@@ -1722,6 +1738,15 @@ err_out:
return err;
}
+/*
+ * The MFT inode has special locking, so teach the lock validator
+ * about this by splitting off the locking rules of the MFT from
+ * the locking rules of other inodes. The MFT inode can never be
+ * accessed from the VFS side (or even internally), only by the
+ * map_mft functions.
+ */
+static struct lock_class_key mft_ni_runlist_lock_key, mft_ni_mrec_lock_key;
+
/**
* ntfs_read_inode_mount - special read_inode for mount time use only
* @vi: inode to read
@@ -2148,6 +2173,14 @@ int ntfs_read_inode_mount(struct inode *vi)
ntfs_attr_put_search_ctx(ctx);
ntfs_debug("Done.");
ntfs_free(m);
+
+ /*
+ * Split the locking rules of the MFT inode from the
+ * locking rules of other inodes:
+ */
+ lockdep_set_class(&ni->runlist.lock, &mft_ni_runlist_lock_key);
+ lockdep_set_class(&ni->mrec_lock, &mft_ni_mrec_lock_key);
+
return 0;
em_put_err_out:
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index 0e14acea3f8..74e0ee8fce7 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -1724,6 +1724,14 @@ upcase_failed:
return FALSE;
}
+/*
+ * The lcn and mft bitmap inodes are NTFS-internal inodes with
+ * their own special locking rules:
+ */
+static struct lock_class_key
+ lcnbmp_runlist_lock_key, lcnbmp_mrec_lock_key,
+ mftbmp_runlist_lock_key, mftbmp_mrec_lock_key;
+
/**
* load_system_files - open the system files using normal functions
* @vol: ntfs super block describing device whose system files to load
@@ -1780,6 +1788,10 @@ static BOOL load_system_files(ntfs_volume *vol)
ntfs_error(sb, "Failed to load $MFT/$BITMAP attribute.");
goto iput_mirr_err_out;
}
+ lockdep_set_class(&NTFS_I(vol->mftbmp_ino)->runlist.lock,
+ &mftbmp_runlist_lock_key);
+ lockdep_set_class(&NTFS_I(vol->mftbmp_ino)->mrec_lock,
+ &mftbmp_mrec_lock_key);
/* Read upcase table and setup @vol->upcase and @vol->upcase_len. */
if (!load_and_init_upcase(vol))
goto iput_mftbmp_err_out;
@@ -1802,6 +1814,11 @@ static BOOL load_system_files(ntfs_volume *vol)
iput(vol->lcnbmp_ino);
goto bitmap_failed;
}
+ lockdep_set_class(&NTFS_I(vol->lcnbmp_ino)->runlist.lock,
+ &lcnbmp_runlist_lock_key);
+ lockdep_set_class(&NTFS_I(vol->lcnbmp_ino)->mrec_lock,
+ &lcnbmp_mrec_lock_key);
+
NInoSetSparseDisabled(NTFS_I(vol->lcnbmp_ino));
if ((vol->nr_clusters + 7) >> 3 > i_size_read(vol->lcnbmp_ino)) {
iput(vol->lcnbmp_ino);
@@ -2743,6 +2760,17 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
struct inode *tmp_ino;
int blocksize, result;
+ /*
+ * We do a pretty difficult piece of bootstrap by reading the
+ * MFT (and other metadata) from disk into memory. We'll only
+ * release this metadata during umount, so the locking patterns
+ * observed during bootstrap do not count. So turn off the
+ * observation of locking patterns (strictly for this context
+ * only) while mounting NTFS. [The validator is still active
+ * otherwise, even for this context: it will for example record
+ * lock class registrations.]
+ */
+ lockdep_off();
ntfs_debug("Entering.");
#ifndef NTFS_RW
sb->s_flags |= MS_RDONLY;
@@ -2754,6 +2782,7 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
if (!silent)
ntfs_error(sb, "Allocation of NTFS volume structure "
"failed. Aborting mount...");
+ lockdep_on();
return -ENOMEM;
}
/* Initialize ntfs_volume structure. */
@@ -2940,6 +2969,7 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
mutex_unlock(&ntfs_lock);
sb->s_export_op = &ntfs_export_ops;
lock_kernel();
+ lockdep_on();
return 0;
}
ntfs_error(sb, "Failed to allocate root directory.");
@@ -3059,6 +3089,7 @@ err_out_now:
sb->s_fs_info = NULL;
kfree(vol);
ntfs_debug("Failed, returning -EINVAL.");
+ lockdep_on();
return -EINVAL;
}
diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c
index 1b8346dd057..9503240ef0e 100644
--- a/fs/ocfs2/dlm/dlmmaster.c
+++ b/fs/ocfs2/dlm/dlmmaster.c
@@ -2375,7 +2375,6 @@ leave:
mlog(0, "returning %d\n", ret);
return ret;
}
-EXPORT_SYMBOL_GPL(dlm_migrate_lockres);
int dlm_lock_basts_flushed(struct dlm_ctxt *dlm, struct dlm_lock *lock)
{
diff --git a/fs/ocfs2/dlm/dlmunlock.c b/fs/ocfs2/dlm/dlmunlock.c
index b0c3134f4f7..37be4b2e0d4 100644
--- a/fs/ocfs2/dlm/dlmunlock.c
+++ b/fs/ocfs2/dlm/dlmunlock.c
@@ -155,7 +155,7 @@ static enum dlm_status dlmunlock_common(struct dlm_ctxt *dlm,
else
status = dlm_get_unlock_actions(dlm, res, lock, lksb, &actions);
- if (status != DLM_NORMAL)
+ if (status != DLM_NORMAL && (status != DLM_CANCELGRANT || !master_node))
goto leave;
/* By now this has been masked out of cancel requests. */
@@ -183,8 +183,7 @@ static enum dlm_status dlmunlock_common(struct dlm_ctxt *dlm,
spin_lock(&lock->spinlock);
/* if the master told us the lock was already granted,
* let the ast handle all of these actions */
- if (status == DLM_NORMAL &&
- lksb->status == DLM_CANCELGRANT) {
+ if (status == DLM_CANCELGRANT) {
actions &= ~(DLM_UNLOCK_REMOVE_LOCK|
DLM_UNLOCK_REGRANT_LOCK|
DLM_UNLOCK_CLEAR_CONVERT_TYPE);
@@ -349,14 +348,9 @@ static enum dlm_status dlm_send_remote_unlock_request(struct dlm_ctxt *dlm,
vec, veclen, owner, &status);
if (tmpret >= 0) {
// successfully sent and received
- if (status == DLM_CANCELGRANT)
- ret = DLM_NORMAL;
- else if (status == DLM_FORWARD) {
+ if (status == DLM_FORWARD)
mlog(0, "master was in-progress. retry\n");
- ret = DLM_FORWARD;
- } else
- ret = status;
- lksb->status = status;
+ ret = status;
} else {
mlog_errno(tmpret);
if (dlm_is_host_down(tmpret)) {
@@ -372,7 +366,6 @@ static enum dlm_status dlm_send_remote_unlock_request(struct dlm_ctxt *dlm,
/* something bad. this will BUG in ocfs2 */
ret = dlm_err_to_dlm_status(tmpret);
}
- lksb->status = ret;
}
return ret;
@@ -483,6 +476,10 @@ int dlm_unlock_lock_handler(struct o2net_msg *msg, u32 len, void *data)
/* lock was found on queue */
lksb = lock->lksb;
+ if (flags & (LKM_VALBLK|LKM_PUT_LVB) &&
+ lock->ml.type != LKM_EXMODE)
+ flags &= ~(LKM_VALBLK|LKM_PUT_LVB);
+
/* unlockast only called on originating node */
if (flags & LKM_PUT_LVB) {
lksb->flags |= DLM_LKSB_PUT_LVB;
@@ -507,11 +504,8 @@ not_found:
"cookie=%u:%llu\n",
dlm_get_lock_cookie_node(unlock->cookie),
dlm_get_lock_cookie_seq(unlock->cookie));
- else {
- /* send the lksb->status back to the other node */
- status = lksb->status;
+ else
dlm_lock_put(lock);
- }
leave:
if (res)
@@ -533,26 +527,22 @@ static enum dlm_status dlm_get_cancel_actions(struct dlm_ctxt *dlm,
if (dlm_lock_on_list(&res->blocked, lock)) {
/* cancel this outright */
- lksb->status = DLM_NORMAL;
status = DLM_NORMAL;
*actions = (DLM_UNLOCK_CALL_AST |
DLM_UNLOCK_REMOVE_LOCK);
} else if (dlm_lock_on_list(&res->converting, lock)) {
/* cancel the request, put back on granted */
- lksb->status = DLM_NORMAL;
status = DLM_NORMAL;
*actions = (DLM_UNLOCK_CALL_AST |
DLM_UNLOCK_REMOVE_LOCK |
DLM_UNLOCK_REGRANT_LOCK |
DLM_UNLOCK_CLEAR_CONVERT_TYPE);
} else if (dlm_lock_on_list(&res->granted, lock)) {
- /* too late, already granted. DLM_CANCELGRANT */
- lksb->status = DLM_CANCELGRANT;
- status = DLM_NORMAL;
+ /* too late, already granted. */
+ status = DLM_CANCELGRANT;
*actions = DLM_UNLOCK_CALL_AST;
} else {
mlog(ML_ERROR, "lock to cancel is not on any list!\n");
- lksb->status = DLM_IVLOCKID;
status = DLM_IVLOCKID;
*actions = 0;
}
@@ -569,13 +559,11 @@ static enum dlm_status dlm_get_unlock_actions(struct dlm_ctxt *dlm,
/* unlock request */
if (!dlm_lock_on_list(&res->granted, lock)) {
- lksb->status = DLM_DENIED;
status = DLM_DENIED;
dlm_error(status);
*actions = 0;
} else {
/* unlock granted lock */
- lksb->status = DLM_NORMAL;
status = DLM_NORMAL;
*actions = (DLM_UNLOCK_FREE_LOCK |
DLM_UNLOCK_CALL_AST |
@@ -632,6 +620,8 @@ retry:
spin_lock(&res->spinlock);
is_master = (res->owner == dlm->node_num);
+ if (flags & LKM_VALBLK && lock->ml.type != LKM_EXMODE)
+ flags &= ~LKM_VALBLK;
spin_unlock(&res->spinlock);
if (is_master) {
@@ -665,7 +655,7 @@ retry:
}
if (call_ast) {
- mlog(0, "calling unlockast(%p, %d)\n", data, lksb->status);
+ mlog(0, "calling unlockast(%p, %d)\n", data, status);
if (is_master) {
/* it is possible that there is one last bast
* pending. make sure it is flushed, then
@@ -677,9 +667,12 @@ retry:
wait_event(dlm->ast_wq,
dlm_lock_basts_flushed(dlm, lock));
}
- (*unlockast)(data, lksb->status);
+ (*unlockast)(data, status);
}
+ if (status == DLM_CANCELGRANT)
+ status = DLM_NORMAL;
+
if (status == DLM_NORMAL) {
mlog(0, "kicking the thread\n");
dlm_kick_thread(dlm, res);
diff --git a/fs/ocfs2/localalloc.c b/fs/ocfs2/localalloc.c
index 0d1973ea32b..1f17a4d0828 100644
--- a/fs/ocfs2/localalloc.c
+++ b/fs/ocfs2/localalloc.c
@@ -840,6 +840,12 @@ static int ocfs2_local_alloc_new_window(struct ocfs2_super *osb,
mlog(0, "Allocating %u clusters for a new window.\n",
ocfs2_local_alloc_window_bits(osb));
+
+ /* Instruct the allocation code to try the most recently used
+ * cluster group. We'll re-record the group used this pass
+ * below. */
+ ac->ac_last_group = osb->la_last_gd;
+
/* we used the generic suballoc reserve function, but we set
* everything up nicely, so there's no reason why we can't use
* the more specific cluster api to claim bits. */
@@ -852,6 +858,8 @@ static int ocfs2_local_alloc_new_window(struct ocfs2_super *osb,
goto bail;
}
+ osb->la_last_gd = ac->ac_last_group;
+
la->la_bm_off = cpu_to_le32(cluster_off);
alloc->id1.bitmap1.i_total = cpu_to_le32(cluster_count);
/* just in case... In the future when we find space ourselves,
diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h
index cd4a6f253d1..0462a7f4e21 100644
--- a/fs/ocfs2/ocfs2.h
+++ b/fs/ocfs2/ocfs2.h
@@ -197,7 +197,6 @@ struct ocfs2_super
struct ocfs2_node_map recovery_map;
struct ocfs2_node_map umount_map;
- u32 num_clusters;
u64 root_blkno;
u64 system_dir_blkno;
u64 bitmap_blkno;
@@ -237,6 +236,7 @@ struct ocfs2_super
enum ocfs2_local_alloc_state local_alloc_state;
struct buffer_head *local_alloc_bh;
+ u64 la_last_gd;
/* Next two fields are for local node slot recovery during
* mount. */
diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c
index 195523090c8..9d91e66f51a 100644
--- a/fs/ocfs2/suballoc.c
+++ b/fs/ocfs2/suballoc.c
@@ -70,12 +70,6 @@ static int ocfs2_block_group_search(struct inode *inode,
struct buffer_head *group_bh,
u32 bits_wanted, u32 min_bits,
u16 *bit_off, u16 *bits_found);
-static int ocfs2_search_chain(struct ocfs2_alloc_context *ac,
- u32 bits_wanted,
- u32 min_bits,
- u16 *bit_off,
- unsigned int *num_bits,
- u64 *bg_blkno);
static int ocfs2_claim_suballoc_bits(struct ocfs2_super *osb,
struct ocfs2_alloc_context *ac,
u32 bits_wanted,
@@ -85,11 +79,6 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_super *osb,
u64 *bg_blkno);
static int ocfs2_test_bg_bit_allocatable(struct buffer_head *bg_bh,
int nr);
-static int ocfs2_block_group_find_clear_bits(struct ocfs2_super *osb,
- struct buffer_head *bg_bh,
- unsigned int bits_wanted,
- u16 *bit_off,
- u16 *bits_found);
static inline int ocfs2_block_group_set_bits(struct ocfs2_journal_handle *handle,
struct inode *alloc_inode,
struct ocfs2_group_desc *bg,
@@ -143,6 +132,64 @@ static u32 ocfs2_bits_per_group(struct ocfs2_chain_list *cl)
return (u32)le16_to_cpu(cl->cl_cpg) * (u32)le16_to_cpu(cl->cl_bpc);
}
+/* somewhat more expensive than our other checks, so use sparingly. */
+static int ocfs2_check_group_descriptor(struct super_block *sb,
+ struct ocfs2_dinode *di,
+ struct ocfs2_group_desc *gd)
+{
+ unsigned int max_bits;
+
+ if (!OCFS2_IS_VALID_GROUP_DESC(gd)) {
+ OCFS2_RO_ON_INVALID_GROUP_DESC(sb, gd);
+ return -EIO;
+ }
+
+ if (di->i_blkno != gd->bg_parent_dinode) {
+ ocfs2_error(sb, "Group descriptor # %llu has bad parent "
+ "pointer (%llu, expected %llu)",
+ (unsigned long long)le64_to_cpu(gd->bg_blkno),
+ (unsigned long long)le64_to_cpu(gd->bg_parent_dinode),
+ (unsigned long long)le64_to_cpu(di->i_blkno));
+ return -EIO;
+ }
+
+ max_bits = le16_to_cpu(di->id2.i_chain.cl_cpg) * le16_to_cpu(di->id2.i_chain.cl_bpc);
+ if (le16_to_cpu(gd->bg_bits) > max_bits) {
+ ocfs2_error(sb, "Group descriptor # %llu has bit count of %u",
+ (unsigned long long)le64_to_cpu(gd->bg_blkno),
+ le16_to_cpu(gd->bg_bits));
+ return -EIO;
+ }
+
+ if (le16_to_cpu(gd->bg_chain) >=
+ le16_to_cpu(di->id2.i_chain.cl_next_free_rec)) {
+ ocfs2_error(sb, "Group descriptor # %llu has bad chain %u",
+ (unsigned long long)le64_to_cpu(gd->bg_blkno),
+ le16_to_cpu(gd->bg_chain));
+ return -EIO;
+ }
+
+ if (le16_to_cpu(gd->bg_free_bits_count) > le16_to_cpu(gd->bg_bits)) {
+ ocfs2_error(sb, "Group descriptor # %llu has bit count %u but "
+ "claims that %u are free",
+ (unsigned long long)le64_to_cpu(gd->bg_blkno),
+ le16_to_cpu(gd->bg_bits),
+ le16_to_cpu(gd->bg_free_bits_count));
+ return -EIO;
+ }
+
+ if (le16_to_cpu(gd->bg_bits) > (8 * le16_to_cpu(gd->bg_size))) {
+ ocfs2_error(sb, "Group descriptor # %llu has bit count %u but "
+ "max bitmap bits of %u",
+ (unsigned long long)le64_to_cpu(gd->bg_blkno),
+ le16_to_cpu(gd->bg_bits),
+ 8 * le16_to_cpu(gd->bg_size));
+ return -EIO;
+ }
+
+ return 0;
+}
+
static int ocfs2_block_group_fill(struct ocfs2_journal_handle *handle,
struct inode *alloc_inode,
struct buffer_head *bg_bh,
@@ -663,6 +710,7 @@ static int ocfs2_test_bg_bit_allocatable(struct buffer_head *bg_bh,
static int ocfs2_block_group_find_clear_bits(struct ocfs2_super *osb,
struct buffer_head *bg_bh,
unsigned int bits_wanted,
+ unsigned int total_bits,
u16 *bit_off,
u16 *bits_found)
{
@@ -679,10 +727,8 @@ static int ocfs2_block_group_find_clear_bits(struct ocfs2_super *osb,
found = start = best_offset = best_size = 0;
bitmap = bg->bg_bitmap;
- while((offset = ocfs2_find_next_zero_bit(bitmap,
- le16_to_cpu(bg->bg_bits),
- start)) != -1) {
- if (offset == le16_to_cpu(bg->bg_bits))
+ while((offset = ocfs2_find_next_zero_bit(bitmap, total_bits, start)) != -1) {
+ if (offset == total_bits)
break;
if (!ocfs2_test_bg_bit_allocatable(bg_bh, offset)) {
@@ -911,14 +957,35 @@ static int ocfs2_cluster_group_search(struct inode *inode,
{
int search = -ENOSPC;
int ret;
- struct ocfs2_group_desc *bg = (struct ocfs2_group_desc *) group_bh->b_data;
+ struct ocfs2_group_desc *gd = (struct ocfs2_group_desc *) group_bh->b_data;
u16 tmp_off, tmp_found;
+ unsigned int max_bits, gd_cluster_off;
BUG_ON(!ocfs2_is_cluster_bitmap(inode));
- if (bg->bg_free_bits_count) {
+ if (gd->bg_free_bits_count) {
+ max_bits = le16_to_cpu(gd->bg_bits);
+
+ /* Tail groups in cluster bitmaps which aren't cpg
+ * aligned are prone to partial extention by a failed
+ * fs resize. If the file system resize never got to
+ * update the dinode cluster count, then we don't want
+ * to trust any clusters past it, regardless of what
+ * the group descriptor says. */
+ gd_cluster_off = ocfs2_blocks_to_clusters(inode->i_sb,
+ le64_to_cpu(gd->bg_blkno));
+ if ((gd_cluster_off + max_bits) >
+ OCFS2_I(inode)->ip_clusters) {
+ max_bits = OCFS2_I(inode)->ip_clusters - gd_cluster_off;
+ mlog(0, "Desc %llu, bg_bits %u, clusters %u, use %u\n",
+ (unsigned long long)le64_to_cpu(gd->bg_blkno),
+ le16_to_cpu(gd->bg_bits),
+ OCFS2_I(inode)->ip_clusters, max_bits);
+ }
+
ret = ocfs2_block_group_find_clear_bits(OCFS2_SB(inode->i_sb),
group_bh, bits_wanted,
+ max_bits,
&tmp_off, &tmp_found);
if (ret)
return ret;
@@ -951,17 +1018,109 @@ static int ocfs2_block_group_search(struct inode *inode,
if (bg->bg_free_bits_count)
ret = ocfs2_block_group_find_clear_bits(OCFS2_SB(inode->i_sb),
group_bh, bits_wanted,
+ le16_to_cpu(bg->bg_bits),
bit_off, bits_found);
return ret;
}
+static int ocfs2_alloc_dinode_update_counts(struct inode *inode,
+ struct ocfs2_journal_handle *handle,
+ struct buffer_head *di_bh,
+ u32 num_bits,
+ u16 chain)
+{
+ int ret;
+ u32 tmp_used;
+ struct ocfs2_dinode *di = (struct ocfs2_dinode *) di_bh->b_data;
+ struct ocfs2_chain_list *cl = (struct ocfs2_chain_list *) &di->id2.i_chain;
+
+ ret = ocfs2_journal_access(handle, inode, di_bh,
+ OCFS2_JOURNAL_ACCESS_WRITE);
+ if (ret < 0) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ tmp_used = le32_to_cpu(di->id1.bitmap1.i_used);
+ di->id1.bitmap1.i_used = cpu_to_le32(num_bits + tmp_used);
+ le32_add_cpu(&cl->cl_recs[chain].c_free, -num_bits);
+
+ ret = ocfs2_journal_dirty(handle, di_bh);
+ if (ret < 0)
+ mlog_errno(ret);
+
+out:
+ return ret;
+}
+
+static int ocfs2_search_one_group(struct ocfs2_alloc_context *ac,
+ u32 bits_wanted,
+ u32 min_bits,
+ u16 *bit_off,
+ unsigned int *num_bits,
+ u64 gd_blkno,
+ u16 *bits_left)
+{
+ int ret;
+ u16 found;
+ struct buffer_head *group_bh = NULL;
+ struct ocfs2_group_desc *gd;
+ struct inode *alloc_inode = ac->ac_inode;
+ struct ocfs2_journal_handle *handle = ac->ac_handle;
+
+ ret = ocfs2_read_block(OCFS2_SB(alloc_inode->i_sb), gd_blkno,
+ &group_bh, OCFS2_BH_CACHED, alloc_inode);
+ if (ret < 0) {
+ mlog_errno(ret);
+ return ret;
+ }
+
+ gd = (struct ocfs2_group_desc *) group_bh->b_data;
+ if (!OCFS2_IS_VALID_GROUP_DESC(gd)) {
+ OCFS2_RO_ON_INVALID_GROUP_DESC(alloc_inode->i_sb, gd);
+ ret = -EIO;
+ goto out;
+ }
+
+ ret = ac->ac_group_search(alloc_inode, group_bh, bits_wanted, min_bits,
+ bit_off, &found);
+ if (ret < 0) {
+ if (ret != -ENOSPC)
+ mlog_errno(ret);
+ goto out;
+ }
+
+ *num_bits = found;
+
+ ret = ocfs2_alloc_dinode_update_counts(alloc_inode, handle, ac->ac_bh,
+ *num_bits,
+ le16_to_cpu(gd->bg_chain));
+ if (ret < 0) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ ret = ocfs2_block_group_set_bits(handle, alloc_inode, gd, group_bh,
+ *bit_off, *num_bits);
+ if (ret < 0)
+ mlog_errno(ret);
+
+ *bits_left = le16_to_cpu(gd->bg_free_bits_count);
+
+out:
+ brelse(group_bh);
+
+ return ret;
+}
+
static int ocfs2_search_chain(struct ocfs2_alloc_context *ac,
u32 bits_wanted,
u32 min_bits,
u16 *bit_off,
unsigned int *num_bits,
- u64 *bg_blkno)
+ u64 *bg_blkno,
+ u16 *bits_left)
{
int status;
u16 chain, tmp_bits;
@@ -988,9 +1147,9 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac,
goto bail;
}
bg = (struct ocfs2_group_desc *) group_bh->b_data;
- if (!OCFS2_IS_VALID_GROUP_DESC(bg)) {
- OCFS2_RO_ON_INVALID_GROUP_DESC(alloc_inode->i_sb, bg);
- status = -EIO;
+ status = ocfs2_check_group_descriptor(alloc_inode->i_sb, fe, bg);
+ if (status) {
+ mlog_errno(status);
goto bail;
}
@@ -1018,9 +1177,9 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac,
goto bail;
}
bg = (struct ocfs2_group_desc *) group_bh->b_data;
- if (!OCFS2_IS_VALID_GROUP_DESC(bg)) {
- OCFS2_RO_ON_INVALID_GROUP_DESC(alloc_inode->i_sb, bg);
- status = -EIO;
+ status = ocfs2_check_group_descriptor(alloc_inode->i_sb, fe, bg);
+ if (status) {
+ mlog_errno(status);
goto bail;
}
}
@@ -1099,6 +1258,7 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac,
(unsigned long long)fe->i_blkno);
*bg_blkno = le64_to_cpu(bg->bg_blkno);
+ *bits_left = le16_to_cpu(bg->bg_free_bits_count);
bail:
if (group_bh)
brelse(group_bh);
@@ -1120,6 +1280,8 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_super *osb,
{
int status;
u16 victim, i;
+ u16 bits_left = 0;
+ u64 hint_blkno = ac->ac_last_group;
struct ocfs2_chain_list *cl;
struct ocfs2_dinode *fe;
@@ -1146,6 +1308,28 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_super *osb,
goto bail;
}
+ if (hint_blkno) {
+ /* Attempt to short-circuit the usual search mechanism
+ * by jumping straight to the most recently used
+ * allocation group. This helps us mantain some
+ * contiguousness across allocations. */
+ status = ocfs2_search_one_group(ac, bits_wanted, min_bits,
+ bit_off, num_bits,
+ hint_blkno, &bits_left);
+ if (!status) {
+ /* Be careful to update *bg_blkno here as the
+ * caller is expecting it to be filled in, and
+ * ocfs2_search_one_group() won't do that for
+ * us. */
+ *bg_blkno = hint_blkno;
+ goto set_hint;
+ }
+ if (status < 0 && status != -ENOSPC) {
+ mlog_errno(status);
+ goto bail;
+ }
+ }
+
cl = (struct ocfs2_chain_list *) &fe->id2.i_chain;
victim = ocfs2_find_victim_chain(cl);
@@ -1153,9 +1337,9 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_super *osb,
ac->ac_allow_chain_relink = 1;
status = ocfs2_search_chain(ac, bits_wanted, min_bits, bit_off,
- num_bits, bg_blkno);
+ num_bits, bg_blkno, &bits_left);
if (!status)
- goto bail;
+ goto set_hint;
if (status < 0 && status != -ENOSPC) {
mlog_errno(status);
goto bail;
@@ -1177,8 +1361,8 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_super *osb,
ac->ac_chain = i;
status = ocfs2_search_chain(ac, bits_wanted, min_bits,
- bit_off, num_bits,
- bg_blkno);
+ bit_off, num_bits, bg_blkno,
+ &bits_left);
if (!status)
break;
if (status < 0 && status != -ENOSPC) {
@@ -1186,8 +1370,19 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_super *osb,
goto bail;
}
}
-bail:
+set_hint:
+ if (status != -ENOSPC) {
+ /* If the next search of this group is not likely to
+ * yield a suitable extent, then we reset the last
+ * group hint so as to not waste a disk read */
+ if (bits_left < min_bits)
+ ac->ac_last_group = 0;
+ else
+ ac->ac_last_group = *bg_blkno;
+ }
+
+bail:
mlog_exit(status);
return status;
}
@@ -1341,7 +1536,7 @@ int ocfs2_claim_clusters(struct ocfs2_super *osb,
{
int status;
unsigned int bits_wanted = ac->ac_bits_wanted - ac->ac_bits_given;
- u64 bg_blkno;
+ u64 bg_blkno = 0;
u16 bg_bit_off;
mlog_entry_void();
@@ -1494,9 +1689,9 @@ static int ocfs2_free_suballoc_bits(struct ocfs2_journal_handle *handle,
}
group = (struct ocfs2_group_desc *) group_bh->b_data;
- if (!OCFS2_IS_VALID_GROUP_DESC(group)) {
- OCFS2_RO_ON_INVALID_GROUP_DESC(alloc_inode->i_sb, group);
- status = -EIO;
+ status = ocfs2_check_group_descriptor(alloc_inode->i_sb, fe, group);
+ if (status) {
+ mlog_errno(status);
goto bail;
}
BUG_ON((count + start_bit) > le16_to_cpu(group->bg_bits));
diff --git a/fs/ocfs2/suballoc.h b/fs/ocfs2/suballoc.h
index a76c82a7cea..c787838d105 100644
--- a/fs/ocfs2/suballoc.h
+++ b/fs/ocfs2/suballoc.h
@@ -49,6 +49,8 @@ struct ocfs2_alloc_context {
u16 ac_chain;
int ac_allow_chain_relink;
group_search_t *ac_group_search;
+
+ u64 ac_last_group;
};
void ocfs2_free_alloc_context(struct ocfs2_alloc_context *ac);
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 382706a67ff..d17e33e66a1 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -1442,8 +1442,13 @@ static int ocfs2_initialize_super(struct super_block *sb,
osb->bitmap_blkno = OCFS2_I(inode)->ip_blkno;
+ /* We don't have a cluster lock on the bitmap here because
+ * we're only interested in static information and the extra
+ * complexity at mount time isn't worht it. Don't pass the
+ * inode in to the read function though as we don't want it to
+ * be put in the cache. */
status = ocfs2_read_block(osb, osb->bitmap_blkno, &bitmap_bh, 0,
- inode);
+ NULL);
iput(inode);
if (status < 0) {
mlog_errno(status);
@@ -1452,7 +1457,6 @@ static int ocfs2_initialize_super(struct super_block *sb,
di = (struct ocfs2_dinode *) bitmap_bh->b_data;
osb->bitmap_cpg = le16_to_cpu(di->id2.i_chain.cl_cpg);
- osb->num_clusters = le32_to_cpu(di->id1.bitmap1.i_total);
brelse(bitmap_bh);
mlog(0, "cluster bitmap inode: %llu, clusters per group: %u\n",
(unsigned long long)osb->bitmap_blkno, osb->bitmap_cpg);
diff --git a/fs/partitions/Kconfig b/fs/partitions/Kconfig
index c9a47809928..e478f194183 100644
--- a/fs/partitions/Kconfig
+++ b/fs/partitions/Kconfig
@@ -99,7 +99,7 @@ config IBM_PARTITION
config MAC_PARTITION
bool "Macintosh partition map support" if PARTITION_ADVANCED
- default y if MAC
+ default y if (MAC || PPC_PMAC)
help
Say Y here if you would like to use hard disks under Linux which
were partitioned on a Macintosh.
diff --git a/fs/partitions/check.c b/fs/partitions/check.c
index 839634026eb..51c6a748df4 100644
--- a/fs/partitions/check.c
+++ b/fs/partitions/check.c
@@ -339,6 +339,7 @@ void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len)
p->start_sect = start;
p->nr_sects = len;
p->partno = part;
+ p->policy = disk->policy;
if (isdigit(disk->kobj.name[strlen(disk->kobj.name)-1]))
snprintf(p->kobj.name,KOBJ_NAME_LEN,"%sp%d",disk->kobj.name,part);
diff --git a/fs/partitions/sun.c b/fs/partitions/sun.c
index abe91ca03ed..0a5927c806c 100644
--- a/fs/partitions/sun.c
+++ b/fs/partitions/sun.c
@@ -74,7 +74,7 @@ int sun_partition(struct parsed_partitions *state, struct block_device *bdev)
spc = be16_to_cpu(label->ntrks) * be16_to_cpu(label->nsect);
for (i = 0; i < 8; i++, p++) {
unsigned long st_sector;
- int num_sectors;
+ unsigned int num_sectors;
st_sector = be32_to_cpu(p->start_cylinder) * spc;
num_sectors = be32_to_cpu(p->num_sectors);
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 7495d3e2077..0b615d62a15 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -74,6 +74,7 @@
#include <linux/times.h>
#include <linux/cpuset.h>
#include <linux/rcupdate.h>
+#include <linux/delayacct.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
@@ -411,7 +412,7 @@ static int do_task_stat(struct task_struct *task, char * buffer, int whole)
res = sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \
%lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \
-%lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu\n",
+%lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu %llu\n",
task->pid,
tcomm,
state,
@@ -455,7 +456,8 @@ static int do_task_stat(struct task_struct *task, char * buffer, int whole)
task->exit_signal,
task_cpu(task),
task->rt_priority,
- task->policy);
+ task->policy,
+ (unsigned long long)delayacct_blkio_ticks(task));
if(mm)
mmput(mm);
return res;
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 243a94af042..fe8d55fb17c 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -551,6 +551,27 @@ static int proc_fd_access_allowed(struct inode *inode)
return allowed;
}
+static int proc_setattr(struct dentry *dentry, struct iattr *attr)
+{
+ int error;
+ struct inode *inode = dentry->d_inode;
+
+ if (attr->ia_valid & ATTR_MODE)
+ return -EPERM;
+
+ error = inode_change_ok(inode, attr);
+ if (!error) {
+ error = security_inode_setattr(dentry, attr);
+ if (!error)
+ error = inode_setattr(inode, attr);
+ }
+ return error;
+}
+
+static struct inode_operations proc_def_inode_operations = {
+ .setattr = proc_setattr,
+};
+
extern struct seq_operations mounts_op;
struct proc_mounts {
struct seq_file m;
@@ -1111,7 +1132,8 @@ out:
static struct inode_operations proc_pid_link_inode_operations = {
.readlink = proc_pid_readlink,
- .follow_link = proc_pid_follow_link
+ .follow_link = proc_pid_follow_link,
+ .setattr = proc_setattr,
};
static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir)
@@ -1285,6 +1307,7 @@ static struct inode *proc_pid_make_inode(struct super_block * sb, struct task_st
ei = PROC_I(inode);
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
inode->i_ino = fake_ino(task->pid, ino);
+ inode->i_op = &proc_def_inode_operations;
/*
* grab the reference to task.
@@ -1339,6 +1362,7 @@ static int pid_revalidate(struct dentry *dentry, struct nameidata *nd)
inode->i_uid = 0;
inode->i_gid = 0;
}
+ inode->i_mode &= ~(S_ISUID | S_ISGID);
security_task_to_inode(task, inode);
put_task_struct(task);
return 1;
@@ -1389,6 +1413,7 @@ static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd)
inode->i_uid = 0;
inode->i_gid = 0;
}
+ inode->i_mode &= ~(S_ISUID | S_ISGID);
security_task_to_inode(task, inode);
put_task_struct(task);
return 1;
@@ -1527,11 +1552,13 @@ static struct file_operations proc_task_operations = {
*/
static struct inode_operations proc_fd_inode_operations = {
.lookup = proc_lookupfd,
+ .setattr = proc_setattr,
};
static struct inode_operations proc_task_inode_operations = {
.lookup = proc_task_lookup,
.getattr = proc_task_getattr,
+ .setattr = proc_setattr,
};
#ifdef CONFIG_SECURITY
@@ -1845,11 +1872,13 @@ static struct file_operations proc_tid_base_operations = {
static struct inode_operations proc_tgid_base_inode_operations = {
.lookup = proc_tgid_base_lookup,
.getattr = pid_getattr,
+ .setattr = proc_setattr,
};
static struct inode_operations proc_tid_base_inode_operations = {
.lookup = proc_tid_base_lookup,
.getattr = pid_getattr,
+ .setattr = proc_setattr,
};
#ifdef CONFIG_SECURITY
@@ -1892,11 +1921,13 @@ static struct dentry *proc_tid_attr_lookup(struct inode *dir,
static struct inode_operations proc_tgid_attr_inode_operations = {
.lookup = proc_tgid_attr_lookup,
.getattr = pid_getattr,
+ .setattr = proc_setattr,
};
static struct inode_operations proc_tid_attr_inode_operations = {
.lookup = proc_tid_attr_lookup,
.getattr = pid_getattr,
+ .setattr = proc_setattr,
};
#endif
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 6dcef089e18..49dfb2ab783 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -192,7 +192,7 @@ int proc_fill_super(struct super_block *s, void *data, int silent)
{
struct inode * root_inode;
- s->s_flags |= MS_NODIRATIME;
+ s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC;
s->s_blocksize = 1024;
s->s_blocksize_bits = 10;
s->s_magic = PROC_SUPER_MAGIC;
diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c
index 036d14d8362..6a984f64edd 100644
--- a/fs/proc/kcore.c
+++ b/fs/proc/kcore.c
@@ -42,8 +42,6 @@ const struct file_operations proc_kcore_operations = {
#define kc_offset_to_vaddr(o) ((o) + PAGE_OFFSET)
#endif
-#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
-
/* An ELF note in memory */
struct memelfnote
{
@@ -384,7 +382,7 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
*/
if (n) {
if (clear_user(buffer + tsz - n,
- tsz - n))
+ n))
return -EFAULT;
}
} else {
diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c
index 9f2cfc30f9c..94215622544 100644
--- a/fs/proc/proc_misc.c
+++ b/fs/proc/proc_misc.c
@@ -169,7 +169,7 @@ static int meminfo_read_proc(char *page, char **start, off_t off,
"Mapped: %8lu kB\n"
"Slab: %8lu kB\n"
"PageTables: %8lu kB\n"
- "NFS Unstable: %8lu kB\n"
+ "NFS_Unstable: %8lu kB\n"
"Bounce: %8lu kB\n"
"CommitLimit: %8lu kB\n"
"Committed_AS: %8lu kB\n"
diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c
index af69f28277b..4616ed50ffc 100644
--- a/fs/proc/task_nommu.c
+++ b/fs/proc/task_nommu.c
@@ -107,7 +107,7 @@ int proc_exe_link(struct inode *inode, struct dentry **dentry, struct vfsmount *
{
struct vm_list_struct *vml;
struct vm_area_struct *vma;
- struct task_struct *task = proc_task(inode);
+ struct task_struct *task = get_proc_task(inode);
struct mm_struct *mm = get_task_mm(task);
int result = -ENOENT;
diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c
index 99fffc9e1bf..677139b48e0 100644
--- a/fs/ramfs/file-nommu.c
+++ b/fs/ramfs/file-nommu.c
@@ -283,9 +283,9 @@ unsigned long ramfs_nommu_get_unmapped_area(struct file *file,
/*****************************************************************************/
/*
- * set up a mapping
+ * set up a mapping for shared memory segments
*/
int ramfs_nommu_mmap(struct file *file, struct vm_area_struct *vma)
{
- return 0;
+ return vma->vm_flags & VM_SHARED ? 0 : -ENOSYS;
}
diff --git a/fs/read_write.c b/fs/read_write.c
index 5bc0e9234f9..d4cb3183c99 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -436,7 +436,7 @@ unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to)
return seg;
}
-EXPORT_SYMBOL(iov_shorten);
+EXPORT_UNUSED_SYMBOL(iov_shorten); /* June 2006 */
/* A write operation does a read from user space and vice versa */
#define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ)
diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c
index 752cea12e30..1627edd5081 100644
--- a/fs/reiserfs/file.c
+++ b/fs/reiserfs/file.c
@@ -48,8 +48,8 @@ static int reiserfs_file_release(struct inode *inode, struct file *filp)
return 0;
}
- reiserfs_write_lock(inode->i_sb);
mutex_lock(&inode->i_mutex);
+ reiserfs_write_lock(inode->i_sb);
/* freeing preallocation only involves relogging blocks that
* are already in the current transaction. preallocation gets
* freed at the end of each transaction, so it is impossible for
@@ -860,8 +860,12 @@ static int reiserfs_submit_file_region_for_write(struct reiserfs_transaction_han
// this sets the proper flags for O_SYNC to trigger a commit
mark_inode_dirty(inode);
reiserfs_write_unlock(inode->i_sb);
- } else
+ } else {
+ reiserfs_write_lock(inode->i_sb);
+ reiserfs_update_inode_transaction(inode);
mark_inode_dirty(inode);
+ reiserfs_write_unlock(inode->i_sb);
+ }
sd_update = 1;
}
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index 12dfdcfbee3..52f1e213654 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -39,14 +39,10 @@ void reiserfs_delete_inode(struct inode *inode)
/* The = 0 happens when we abort creating a new inode for some reason like lack of space.. */
if (!(inode->i_state & I_NEW) && INODE_PKEY(inode)->k_objectid != 0) { /* also handles bad_inode case */
- mutex_lock(&inode->i_mutex);
-
reiserfs_delete_xattrs(inode);
- if (journal_begin(&th, inode->i_sb, jbegin_count)) {
- mutex_unlock(&inode->i_mutex);
+ if (journal_begin(&th, inode->i_sb, jbegin_count))
goto out;
- }
reiserfs_update_inode_transaction(inode);
err = reiserfs_delete_object(&th, inode);
@@ -57,12 +53,8 @@ void reiserfs_delete_inode(struct inode *inode)
if (!err)
DQUOT_FREE_INODE(inode);
- if (journal_end(&th, inode->i_sb, jbegin_count)) {
- mutex_unlock(&inode->i_mutex);
+ if (journal_end(&th, inode->i_sb, jbegin_count))
goto out;
- }
-
- mutex_unlock(&inode->i_mutex);
/* check return value from reiserfs_delete_object after
* ending the transaction
@@ -2348,6 +2340,7 @@ static int reiserfs_write_full_page(struct page *page,
unsigned long end_index = inode->i_size >> PAGE_CACHE_SHIFT;
int error = 0;
unsigned long block;
+ sector_t last_block;
struct buffer_head *head, *bh;
int partial = 0;
int nr = 0;
@@ -2395,10 +2388,19 @@ static int reiserfs_write_full_page(struct page *page,
}
bh = head;
block = page->index << (PAGE_CACHE_SHIFT - s->s_blocksize_bits);
+ last_block = (i_size_read(inode) - 1) >> inode->i_blkbits;
/* first map all the buffers, logging any direct items we find */
do {
- if ((checked || buffer_dirty(bh)) && (!buffer_mapped(bh) ||
- (buffer_mapped(bh)
+ if (block > last_block) {
+ /*
+ * This can happen when the block size is less than
+ * the page size. The corresponding bytes in the page
+ * were zero filled above
+ */
+ clear_buffer_dirty(bh);
+ set_buffer_uptodate(bh);
+ } else if ((checked || buffer_dirty(bh)) &&
+ (!buffer_mapped(bh) || (buffer_mapped(bh)
&& bh->b_blocknr ==
0))) {
/* not mapped yet, or it points to a direct item, search
diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
index 745c8810089..a986b5e1e28 100644
--- a/fs/reiserfs/ioctl.c
+++ b/fs/reiserfs/ioctl.c
@@ -116,12 +116,12 @@ static int reiserfs_unpack(struct inode *inode, struct file *filp)
if (REISERFS_I(inode)->i_flags & i_nopack_mask) {
return 0;
}
- reiserfs_write_lock(inode->i_sb);
/* we need to make sure nobody is changing the file size beneath
** us
*/
mutex_lock(&inode->i_mutex);
+ reiserfs_write_lock(inode->i_sb);
write_from = inode->i_size & (blocksize - 1);
/* if we are on a block boundary, we are already unpacked. */
diff --git a/fs/reiserfs/procfs.c b/fs/reiserfs/procfs.c
index 5d8a8cfebc7..c533ec1bcae 100644
--- a/fs/reiserfs/procfs.c
+++ b/fs/reiserfs/procfs.c
@@ -492,9 +492,17 @@ static void add_file(struct super_block *sb, char *name,
int reiserfs_proc_info_init(struct super_block *sb)
{
+ char b[BDEVNAME_SIZE];
+ char *s;
+
+ /* Some block devices use /'s */
+ strlcpy(b, reiserfs_bdevname(sb), BDEVNAME_SIZE);
+ s = strchr(b, '/');
+ if (s)
+ *s = '!';
+
spin_lock_init(&__PINFO(sb).lock);
- REISERFS_SB(sb)->procdir =
- proc_mkdir(reiserfs_bdevname(sb), proc_info_root);
+ REISERFS_SB(sb)->procdir = proc_mkdir(b, proc_info_root);
if (REISERFS_SB(sb)->procdir) {
REISERFS_SB(sb)->procdir->owner = THIS_MODULE;
REISERFS_SB(sb)->procdir->data = sb;
@@ -508,13 +516,22 @@ int reiserfs_proc_info_init(struct super_block *sb)
return 0;
}
reiserfs_warning(sb, "reiserfs: cannot create /proc/%s/%s",
- proc_info_root_name, reiserfs_bdevname(sb));
+ proc_info_root_name, b);
return 1;
}
int reiserfs_proc_info_done(struct super_block *sb)
{
struct proc_dir_entry *de = REISERFS_SB(sb)->procdir;
+ char b[BDEVNAME_SIZE];
+ char *s;
+
+ /* Some block devices use /'s */
+ strlcpy(b, reiserfs_bdevname(sb), BDEVNAME_SIZE);
+ s = strchr(b, '/');
+ if (s)
+ *s = '!';
+
if (de) {
remove_proc_entry("journal", de);
remove_proc_entry("oidmap", de);
@@ -528,7 +545,7 @@ int reiserfs_proc_info_done(struct super_block *sb)
__PINFO(sb).exiting = 1;
spin_unlock(&__PINFO(sb).lock);
if (proc_info_root) {
- remove_proc_entry(reiserfs_bdevname(sb), proc_info_root);
+ remove_proc_entry(b, proc_info_root);
REISERFS_SB(sb)->procdir = NULL;
}
return 0;
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
index 28eb3c88603..5567328f104 100644
--- a/fs/reiserfs/super.c
+++ b/fs/reiserfs/super.c
@@ -2203,7 +2203,7 @@ static ssize_t reiserfs_quota_write(struct super_block *sb, int type,
size_t towrite = len;
struct buffer_head tmp_bh, *bh;
- mutex_lock(&inode->i_mutex);
+ mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
while (towrite > 0) {
tocopy = sb->s_blocksize - offset < towrite ?
sb->s_blocksize - offset : towrite;
diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c
index 39fedaa88a0..d935fb9394e 100644
--- a/fs/reiserfs/xattr.c
+++ b/fs/reiserfs/xattr.c
@@ -424,7 +424,7 @@ int xattr_readdir(struct file *file, filldir_t filler, void *buf)
int res = -ENOTDIR;
if (!file->f_op || !file->f_op->readdir)
goto out;
- mutex_lock(&inode->i_mutex);
+ mutex_lock_nested(&inode->i_mutex, I_MUTEX_XATTR);
// down(&inode->i_zombie);
res = -ENOENT;
if (!IS_DEADDIR(inode)) {
diff --git a/fs/splice.c b/fs/splice.c
index 05fd2787be9..684bca3d3a1 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -1307,6 +1307,85 @@ asmlinkage long sys_splice(int fd_in, loff_t __user *off_in,
}
/*
+ * Make sure there's data to read. Wait for input if we can, otherwise
+ * return an appropriate error.
+ */
+static int link_ipipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
+{
+ int ret;
+
+ /*
+ * Check ->nrbufs without the inode lock first. This function
+ * is speculative anyways, so missing one is ok.
+ */
+ if (pipe->nrbufs)
+ return 0;
+
+ ret = 0;
+ mutex_lock(&pipe->inode->i_mutex);
+
+ while (!pipe->nrbufs) {
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ if (!pipe->writers)
+ break;
+ if (!pipe->waiting_writers) {
+ if (flags & SPLICE_F_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
+ }
+ pipe_wait(pipe);
+ }
+
+ mutex_unlock(&pipe->inode->i_mutex);
+ return ret;
+}
+
+/*
+ * Make sure there's writeable room. Wait for room if we can, otherwise
+ * return an appropriate error.
+ */
+static int link_opipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
+{
+ int ret;
+
+ /*
+ * Check ->nrbufs without the inode lock first. This function
+ * is speculative anyways, so missing one is ok.
+ */
+ if (pipe->nrbufs < PIPE_BUFFERS)
+ return 0;
+
+ ret = 0;
+ mutex_lock(&pipe->inode->i_mutex);
+
+ while (pipe->nrbufs >= PIPE_BUFFERS) {
+ if (!pipe->readers) {
+ send_sig(SIGPIPE, current, 0);
+ ret = -EPIPE;
+ break;
+ }
+ if (flags & SPLICE_F_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ pipe->waiting_writers++;
+ pipe_wait(pipe);
+ pipe->waiting_writers--;
+ }
+
+ mutex_unlock(&pipe->inode->i_mutex);
+ return ret;
+}
+
+/*
* Link contents of ipipe to opipe.
*/
static int link_pipe(struct pipe_inode_info *ipipe,
@@ -1314,9 +1393,7 @@ static int link_pipe(struct pipe_inode_info *ipipe,
size_t len, unsigned int flags)
{
struct pipe_buffer *ibuf, *obuf;
- int ret, do_wakeup, i, ipipe_first;
-
- ret = do_wakeup = ipipe_first = 0;
+ int ret = 0, i = 0, nbuf;
/*
* Potential ABBA deadlock, work around it by ordering lock
@@ -1324,126 +1401,62 @@ static int link_pipe(struct pipe_inode_info *ipipe,
* could deadlock (one doing tee from A -> B, the other from B -> A).
*/
if (ipipe->inode < opipe->inode) {
- ipipe_first = 1;
- mutex_lock(&ipipe->inode->i_mutex);
- mutex_lock(&opipe->inode->i_mutex);
+ mutex_lock_nested(&ipipe->inode->i_mutex, I_MUTEX_PARENT);
+ mutex_lock_nested(&opipe->inode->i_mutex, I_MUTEX_CHILD);
} else {
- mutex_lock(&opipe->inode->i_mutex);
- mutex_lock(&ipipe->inode->i_mutex);
+ mutex_lock_nested(&opipe->inode->i_mutex, I_MUTEX_PARENT);
+ mutex_lock_nested(&ipipe->inode->i_mutex, I_MUTEX_CHILD);
}
- for (i = 0;; i++) {
+ do {
if (!opipe->readers) {
send_sig(SIGPIPE, current, 0);
if (!ret)
ret = -EPIPE;
break;
}
- if (ipipe->nrbufs - i) {
- ibuf = ipipe->bufs + ((ipipe->curbuf + i) & (PIPE_BUFFERS - 1));
- /*
- * If we have room, fill this buffer
- */
- if (opipe->nrbufs < PIPE_BUFFERS) {
- int nbuf = (opipe->curbuf + opipe->nrbufs) & (PIPE_BUFFERS - 1);
-
- /*
- * Get a reference to this pipe buffer,
- * so we can copy the contents over.
- */
- ibuf->ops->get(ipipe, ibuf);
-
- obuf = opipe->bufs + nbuf;
- *obuf = *ibuf;
-
- /*
- * Don't inherit the gift flag, we need to
- * prevent multiple steals of this page.
- */
- obuf->flags &= ~PIPE_BUF_FLAG_GIFT;
-
- if (obuf->len > len)
- obuf->len = len;
-
- opipe->nrbufs++;
- do_wakeup = 1;
- ret += obuf->len;
- len -= obuf->len;
-
- if (!len)
- break;
- if (opipe->nrbufs < PIPE_BUFFERS)
- continue;
- }
-
- /*
- * We have input available, but no output room.
- * If we already copied data, return that. If we
- * need to drop the opipe lock, it must be ordered
- * last to avoid deadlocks.
- */
- if ((flags & SPLICE_F_NONBLOCK) || !ipipe_first) {
- if (!ret)
- ret = -EAGAIN;
- break;
- }
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- break;
- }
- if (do_wakeup) {
- smp_mb();
- if (waitqueue_active(&opipe->wait))
- wake_up_interruptible(&opipe->wait);
- kill_fasync(&opipe->fasync_readers, SIGIO, POLL_IN);
- do_wakeup = 0;
- }
+ /*
+ * If we have iterated all input buffers or ran out of
+ * output room, break.
+ */
+ if (i >= ipipe->nrbufs || opipe->nrbufs >= PIPE_BUFFERS)
+ break;
- opipe->waiting_writers++;
- pipe_wait(opipe);
- opipe->waiting_writers--;
- continue;
- }
+ ibuf = ipipe->bufs + ((ipipe->curbuf + i) & (PIPE_BUFFERS - 1));
+ nbuf = (opipe->curbuf + opipe->nrbufs) & (PIPE_BUFFERS - 1);
/*
- * No input buffers, do the usual checks for available
- * writers and blocking and wait if necessary
+ * Get a reference to this pipe buffer,
+ * so we can copy the contents over.
*/
- if (!ipipe->writers)
- break;
- if (!ipipe->waiting_writers) {
- if (ret)
- break;
- }
+ ibuf->ops->get(ipipe, ibuf);
+
+ obuf = opipe->bufs + nbuf;
+ *obuf = *ibuf;
+
/*
- * pipe_wait() drops the ipipe mutex. To avoid deadlocks
- * with another process, we can only safely do that if
- * the ipipe lock is ordered last.
+ * Don't inherit the gift flag, we need to
+ * prevent multiple steals of this page.
*/
- if ((flags & SPLICE_F_NONBLOCK) || ipipe_first) {
- if (!ret)
- ret = -EAGAIN;
- break;
- }
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- break;
- }
+ obuf->flags &= ~PIPE_BUF_FLAG_GIFT;
- if (waitqueue_active(&ipipe->wait))
- wake_up_interruptible_sync(&ipipe->wait);
- kill_fasync(&ipipe->fasync_writers, SIGIO, POLL_OUT);
+ if (obuf->len > len)
+ obuf->len = len;
- pipe_wait(ipipe);
- }
+ opipe->nrbufs++;
+ ret += obuf->len;
+ len -= obuf->len;
+ i++;
+ } while (len);
mutex_unlock(&ipipe->inode->i_mutex);
mutex_unlock(&opipe->inode->i_mutex);
- if (do_wakeup) {
+ /*
+ * If we put data in the output pipe, wakeup any potential readers.
+ */
+ if (ret > 0) {
smp_mb();
if (waitqueue_active(&opipe->wait))
wake_up_interruptible(&opipe->wait);
@@ -1464,14 +1477,29 @@ static long do_tee(struct file *in, struct file *out, size_t len,
{
struct pipe_inode_info *ipipe = in->f_dentry->d_inode->i_pipe;
struct pipe_inode_info *opipe = out->f_dentry->d_inode->i_pipe;
+ int ret = -EINVAL;
/*
- * Link ipipe to the two output pipes, consuming as we go along.
+ * Duplicate the contents of ipipe to opipe without actually
+ * copying the data.
*/
- if (ipipe && opipe)
- return link_pipe(ipipe, opipe, len, flags);
+ if (ipipe && opipe && ipipe != opipe) {
+ /*
+ * Keep going, unless we encounter an error. The ipipe/opipe
+ * ordering doesn't really matter.
+ */
+ ret = link_ipipe_prep(ipipe, flags);
+ if (!ret) {
+ ret = link_opipe_prep(opipe, flags);
+ if (!ret) {
+ ret = link_pipe(ipipe, opipe, len, flags);
+ if (!ret && (flags & SPLICE_F_NONBLOCK))
+ ret = -EAGAIN;
+ }
+ }
+ }
- return -EINVAL;
+ return ret;
}
asmlinkage long sys_tee(int fdin, int fdout, size_t len, unsigned int flags)
diff --git a/fs/super.c b/fs/super.c
index 9b780c42d84..5c4c94d5495 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -49,11 +49,12 @@ DEFINE_SPINLOCK(sb_lock);
/**
* alloc_super - create new superblock
+ * @type: filesystem type superblock should belong to
*
* Allocates and initializes a new &struct super_block. alloc_super()
* returns a pointer new superblock or %NULL if allocation had failed.
*/
-static struct super_block *alloc_super(void)
+static struct super_block *alloc_super(struct file_system_type *type)
{
struct super_block *s = kzalloc(sizeof(struct super_block), GFP_USER);
static struct super_operations default_op;
@@ -72,6 +73,13 @@ static struct super_block *alloc_super(void)
INIT_LIST_HEAD(&s->s_inodes);
init_rwsem(&s->s_umount);
mutex_init(&s->s_lock);
+ lockdep_set_class(&s->s_umount, &type->s_umount_key);
+ /*
+ * The locking rules for s_lock are up to the
+ * filesystem. For example ext3fs has different
+ * lock ordering than usbfs:
+ */
+ lockdep_set_class(&s->s_lock, &type->s_lock_key);
down_write(&s->s_umount);
s->s_count = S_BIAS;
atomic_set(&s->s_active, 1);
@@ -295,7 +303,7 @@ retry:
}
if (!s) {
spin_unlock(&sb_lock);
- s = alloc_super();
+ s = alloc_super(type);
if (!s)
return ERR_PTR(-ENOMEM);
goto retry;
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index 5e0e31cc46f..9889e54e1f1 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -109,6 +109,17 @@ static inline void set_inode_attr(struct inode * inode, struct iattr * iattr)
inode->i_ctime = iattr->ia_ctime;
}
+
+/*
+ * sysfs has a different i_mutex lock order behavior for i_mutex than other
+ * filesystems; sysfs i_mutex is called in many places with subsystem locks
+ * held. At the same time, many of the VFS locking rules do not apply to
+ * sysfs at all (cross directory rename for example). To untangle this mess
+ * (which gives false positives in lockdep), we're giving sysfs inodes their
+ * own class for i_mutex.
+ */
+static struct lock_class_key sysfs_inode_imutex_key;
+
struct inode * sysfs_new_inode(mode_t mode, struct sysfs_dirent * sd)
{
struct inode * inode = new_inode(sysfs_sb);
@@ -118,6 +129,7 @@ struct inode * sysfs_new_inode(mode_t mode, struct sysfs_dirent * sd)
inode->i_mapping->a_ops = &sysfs_aops;
inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
inode->i_op = &sysfs_inode_operations;
+ lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key);
if (sd->s_iattr) {
/* sysfs_dirent has non-default attributes
diff --git a/fs/udf/ialloc.c b/fs/udf/ialloc.c
index 3873c672cb4..33323473e3c 100644
--- a/fs/udf/ialloc.c
+++ b/fs/udf/ialloc.c
@@ -75,6 +75,12 @@ struct inode * udf_new_inode (struct inode *dir, int mode, int * err)
}
*err = -ENOSPC;
+ UDF_I_UNIQUE(inode) = 0;
+ UDF_I_LENEXTENTS(inode) = 0;
+ UDF_I_NEXT_ALLOC_BLOCK(inode) = 0;
+ UDF_I_NEXT_ALLOC_GOAL(inode) = 0;
+ UDF_I_STRAT4096(inode) = 0;
+
block = udf_new_block(dir->i_sb, NULL, UDF_I_LOCATION(dir).partitionReferenceNum,
start, err);
if (*err)
@@ -84,11 +90,6 @@ struct inode * udf_new_inode (struct inode *dir, int mode, int * err)
}
mutex_lock(&sbi->s_alloc_mutex);
- UDF_I_UNIQUE(inode) = 0;
- UDF_I_LENEXTENTS(inode) = 0;
- UDF_I_NEXT_ALLOC_BLOCK(inode) = 0;
- UDF_I_NEXT_ALLOC_GOAL(inode) = 0;
- UDF_I_STRAT4096(inode) = 0;
if (UDF_SB_LVIDBH(sb))
{
struct logicalVolHeaderDesc *lvhd;
diff --git a/fs/udf/super.c b/fs/udf/super.c
index 4df822c881b..fcce1a21a51 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -115,6 +115,13 @@ static struct inode *udf_alloc_inode(struct super_block *sb)
ei = (struct udf_inode_info *)kmem_cache_alloc(udf_inode_cachep, SLAB_KERNEL);
if (!ei)
return NULL;
+
+ ei->i_unique = 0;
+ ei->i_lenExtents = 0;
+ ei->i_next_alloc_block = 0;
+ ei->i_next_alloc_goal = 0;
+ ei->i_strat4096 = 0;
+
return &ei->vfs_inode;
}
@@ -1652,7 +1659,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
iput(inode);
goto error_out;
}
- sb->s_maxbytes = MAX_LFS_FILESIZE;
+ sb->s_maxbytes = 1<<30;
return 0;
error_out:
diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c
index e1b0e8cfecb..0abd66ce36e 100644
--- a/fs/udf/truncate.c
+++ b/fs/udf/truncate.c
@@ -239,37 +239,51 @@ void udf_truncate_extents(struct inode * inode)
{
if (offset)
{
- extoffset -= adsize;
- etype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1);
- if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
- {
- extoffset -= adsize;
- elen = EXT_NOT_RECORDED_NOT_ALLOCATED | (elen + offset);
- udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 0);
+ /*
+ * OK, there is not extent covering inode->i_size and
+ * no extent above inode->i_size => truncate is
+ * extending the file by 'offset'.
+ */
+ if ((!bh && extoffset == udf_file_entry_alloc_offset(inode)) ||
+ (bh && extoffset == sizeof(struct allocExtDesc))) {
+ /* File has no extents at all! */
+ memset(&eloc, 0x00, sizeof(kernel_lb_addr));
+ elen = EXT_NOT_RECORDED_NOT_ALLOCATED | offset;
+ udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1);
}
- else if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
- {
- kernel_lb_addr neloc = { 0, 0 };
+ else {
extoffset -= adsize;
- nelen = EXT_NOT_RECORDED_NOT_ALLOCATED |
- ((elen + offset + inode->i_sb->s_blocksize - 1) &
- ~(inode->i_sb->s_blocksize - 1));
- udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 1);
- udf_add_aext(inode, &bloc, &extoffset, eloc, (etype << 30) | elen, &bh, 1);
- }
- else
- {
- if (elen & (inode->i_sb->s_blocksize - 1))
+ etype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1);
+ if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
+ {
+ extoffset -= adsize;
+ elen = EXT_NOT_RECORDED_NOT_ALLOCATED | (elen + offset);
+ udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 0);
+ }
+ else if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
{
+ kernel_lb_addr neloc = { 0, 0 };
extoffset -= adsize;
- elen = EXT_RECORDED_ALLOCATED |
- ((elen + inode->i_sb->s_blocksize - 1) &
+ nelen = EXT_NOT_RECORDED_NOT_ALLOCATED |
+ ((elen + offset + inode->i_sb->s_blocksize - 1) &
~(inode->i_sb->s_blocksize - 1));
- udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 1);
+ udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 1);
+ udf_add_aext(inode, &bloc, &extoffset, eloc, (etype << 30) | elen, &bh, 1);
+ }
+ else
+ {
+ if (elen & (inode->i_sb->s_blocksize - 1))
+ {
+ extoffset -= adsize;
+ elen = EXT_RECORDED_ALLOCATED |
+ ((elen + inode->i_sb->s_blocksize - 1) &
+ ~(inode->i_sb->s_blocksize - 1));
+ udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 1);
+ }
+ memset(&eloc, 0x00, sizeof(kernel_lb_addr));
+ elen = EXT_NOT_RECORDED_NOT_ALLOCATED | offset;
+ udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1);
}
- memset(&eloc, 0x00, sizeof(kernel_lb_addr));
- elen = EXT_NOT_RECORDED_NOT_ALLOCATED | offset;
- udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1);
}
}
}
diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c
index b01804baa12..b8238147577 100644
--- a/fs/ufs/balloc.c
+++ b/fs/ufs/balloc.c
@@ -248,7 +248,7 @@ static void ufs_change_blocknr(struct inode *inode, unsigned int baseblk,
if (likely(cur_index != index)) {
page = ufs_get_locked_page(mapping, index);
- if (IS_ERR(page))
+ if (!page || IS_ERR(page)) /* it was truncated or EIO */
continue;
} else
page = locked_page;
diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c
index e7c8615beb6..30c6e8a9446 100644
--- a/fs/ufs/inode.c
+++ b/fs/ufs/inode.c
@@ -169,18 +169,20 @@ static void ufs_clear_frag(struct inode *inode, struct buffer_head *bh)
static struct buffer_head *
ufs_clear_frags(struct inode *inode, sector_t beg,
- unsigned int n)
+ unsigned int n, sector_t want)
{
- struct buffer_head *res, *bh;
+ struct buffer_head *res = NULL, *bh;
sector_t end = beg + n;
- res = sb_getblk(inode->i_sb, beg);
- ufs_clear_frag(inode, res);
- for (++beg; beg < end; ++beg) {
+ for (; beg < end; ++beg) {
bh = sb_getblk(inode->i_sb, beg);
ufs_clear_frag(inode, bh);
- brelse(bh);
+ if (want != beg)
+ brelse(bh);
+ else
+ res = bh;
}
+ BUG_ON(!res);
return res;
}
@@ -265,7 +267,9 @@ repeat:
lastfrag = ufsi->i_lastfrag;
}
- goal = fs32_to_cpu(sb, ufsi->i_u1.i_data[lastblock]) + uspi->s_fpb;
+ tmp = fs32_to_cpu(sb, ufsi->i_u1.i_data[lastblock]);
+ if (tmp)
+ goal = tmp + uspi->s_fpb;
tmp = ufs_new_fragments (inode, p, fragment - blockoff,
goal, required + blockoff,
err, locked_page);
@@ -277,13 +281,15 @@ repeat:
tmp = ufs_new_fragments(inode, p, fragment - (blockoff - lastblockoff),
fs32_to_cpu(sb, *p), required + (blockoff - lastblockoff),
err, locked_page);
- }
+ } else /* (lastblock > block) */ {
/*
* We will allocate new block before last allocated block
*/
- else /* (lastblock > block) */ {
- if (lastblock && (tmp = fs32_to_cpu(sb, ufsi->i_u1.i_data[lastblock-1])))
- goal = tmp + uspi->s_fpb;
+ if (block) {
+ tmp = fs32_to_cpu(sb, ufsi->i_u1.i_data[block-1]);
+ if (tmp)
+ goal = tmp + uspi->s_fpb;
+ }
tmp = ufs_new_fragments(inode, p, fragment - blockoff,
goal, uspi->s_fpb, err, locked_page);
}
@@ -296,7 +302,7 @@ repeat:
}
if (!phys) {
- result = ufs_clear_frags(inode, tmp + blockoff, required);
+ result = ufs_clear_frags(inode, tmp, required, tmp + blockoff);
} else {
*phys = tmp + blockoff;
result = NULL;
@@ -383,7 +389,7 @@ repeat:
}
}
- if (block && (tmp = fs32_to_cpu(sb, ((__fs32*)bh->b_data)[block-1]) + uspi->s_fpb))
+ if (block && (tmp = fs32_to_cpu(sb, ((__fs32*)bh->b_data)[block-1])))
goal = tmp + uspi->s_fpb;
else
goal = bh->b_blocknr + uspi->s_fpb;
@@ -397,7 +403,8 @@ repeat:
if (!phys) {
- result = ufs_clear_frags(inode, tmp + blockoff, uspi->s_fpb);
+ result = ufs_clear_frags(inode, tmp, uspi->s_fpb,
+ tmp + blockoff);
} else {
*phys = tmp + blockoff;
*new = 1;
diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c
index abd5f23a426..d344b411e26 100644
--- a/fs/ufs/namei.c
+++ b/fs/ufs/namei.c
@@ -129,7 +129,7 @@ static int ufs_symlink (struct inode * dir, struct dentry * dentry,
struct inode * inode;
if (l > sb->s_blocksize)
- goto out;
+ goto out_notlocked;
lock_kernel();
inode = ufs_new_inode(dir, S_IFLNK | S_IRWXUGO);
@@ -155,6 +155,7 @@ static int ufs_symlink (struct inode * dir, struct dentry * dentry,
err = ufs_add_nondir(dentry, inode);
out:
unlock_kernel();
+out_notlocked:
return err;
out_fail:
diff --git a/fs/ufs/super.c b/fs/ufs/super.c
index 19a99726e58..992ee0b87cc 100644
--- a/fs/ufs/super.c
+++ b/fs/ufs/super.c
@@ -1326,7 +1326,7 @@ static ssize_t ufs_quota_write(struct super_block *sb, int type,
size_t towrite = len;
struct buffer_head *bh;
- mutex_lock(&inode->i_mutex);
+ mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
while (towrite > 0) {
tocopy = sb->s_blocksize - offset < towrite ?
sb->s_blocksize - offset : towrite;
diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c
index c9b55872079..ea11d04c41a 100644
--- a/fs/ufs/truncate.c
+++ b/fs/ufs/truncate.c
@@ -375,17 +375,15 @@ static int ufs_alloc_lastblock(struct inode *inode)
int err = 0;
struct address_space *mapping = inode->i_mapping;
struct ufs_sb_private_info *uspi = UFS_SB(inode->i_sb)->s_uspi;
- struct ufs_inode_info *ufsi = UFS_I(inode);
unsigned lastfrag, i, end;
struct page *lastpage;
struct buffer_head *bh;
lastfrag = (i_size_read(inode) + uspi->s_fsize - 1) >> uspi->s_fshift;
- if (!lastfrag) {
- ufsi->i_lastfrag = 0;
+ if (!lastfrag)
goto out;
- }
+
lastfrag--;
lastpage = ufs_get_locked_page(mapping, lastfrag >>
@@ -400,25 +398,25 @@ static int ufs_alloc_lastblock(struct inode *inode)
for (i = 0; i < end; ++i)
bh = bh->b_this_page;
- if (!buffer_mapped(bh)) {
- err = ufs_getfrag_block(inode, lastfrag, bh, 1);
-
- if (unlikely(err))
- goto out_unlock;
-
- if (buffer_new(bh)) {
- clear_buffer_new(bh);
- unmap_underlying_metadata(bh->b_bdev,
- bh->b_blocknr);
- /*
- * we do not zeroize fragment, because of
- * if it maped to hole, it already contains zeroes
- */
- set_buffer_uptodate(bh);
- mark_buffer_dirty(bh);
- set_page_dirty(lastpage);
- }
+
+ err = ufs_getfrag_block(inode, lastfrag, bh, 1);
+
+ if (unlikely(err))
+ goto out_unlock;
+
+ if (buffer_new(bh)) {
+ clear_buffer_new(bh);
+ unmap_underlying_metadata(bh->b_bdev,
+ bh->b_blocknr);
+ /*
+ * we do not zeroize fragment, because of
+ * if it maped to hole, it already contains zeroes
+ */
+ set_buffer_uptodate(bh);
+ mark_buffer_dirty(bh);
+ set_page_dirty(lastpage);
}
+
out_unlock:
ufs_put_locked_page(lastpage);
out:
@@ -440,23 +438,11 @@ int ufs_truncate(struct inode *inode, loff_t old_i_size)
if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
return -EPERM;
- if (inode->i_size > old_i_size) {
- /*
- * if we expand file we should care about
- * allocation of block for last byte first of all
- */
- err = ufs_alloc_lastblock(inode);
+ err = ufs_alloc_lastblock(inode);
- if (err) {
- i_size_write(inode, old_i_size);
- goto out;
- }
- /*
- * go away, because of we expand file, and we do not
- * need free blocks, and zeroizes page
- */
- lock_kernel();
- goto almost_end;
+ if (err) {
+ i_size_write(inode, old_i_size);
+ goto out;
}
block_truncate_page(inode->i_mapping, inode->i_size, ufs_getfrag_block);
@@ -477,21 +463,8 @@ int ufs_truncate(struct inode *inode, loff_t old_i_size)
yield();
}
- if (inode->i_size < old_i_size) {
- /*
- * now we should have enough space
- * to allocate block for last byte
- */
- err = ufs_alloc_lastblock(inode);
- if (err)
- /*
- * looks like all the same - we have no space,
- * but we truncate file already
- */
- inode->i_size = (ufsi->i_lastfrag - 1) * uspi->s_fsize;
- }
-almost_end:
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
+ ufsi->i_lastfrag = DIRECT_FRAGMENT;
unlock_kernel();
mark_inode_dirty(inode);
out:
diff --git a/fs/ufs/util.c b/fs/ufs/util.c
index 337cf2c46d1..22f820a9b15 100644
--- a/fs/ufs/util.c
+++ b/fs/ufs/util.c
@@ -251,12 +251,12 @@ struct page *ufs_get_locked_page(struct address_space *mapping,
{
struct page *page;
-try_again:
page = find_lock_page(mapping, index);
if (!page) {
page = read_cache_page(mapping, index,
(filler_t*)mapping->a_ops->readpage,
NULL);
+
if (IS_ERR(page)) {
printk(KERN_ERR "ufs_change_blocknr: "
"read_cache_page error: ino %lu, index: %lu\n",
@@ -266,6 +266,14 @@ try_again:
lock_page(page);
+ if (unlikely(page->mapping == NULL)) {
+ /* Truncate got there first */
+ unlock_page(page);
+ page_cache_release(page);
+ page = NULL;
+ goto out;
+ }
+
if (!PageUptodate(page) || PageError(page)) {
unlock_page(page);
page_cache_release(page);
@@ -275,15 +283,8 @@ try_again:
mapping->host->i_ino, index);
page = ERR_PTR(-EIO);
- goto out;
}
}
-
- if (unlikely(!page->mapping || !page_has_buffers(page))) {
- unlock_page(page);
- page_cache_release(page);
- goto try_again;/*we really need these buffers*/
- }
out:
return page;
}
diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c
index c40f81ba9b1..34dcb43a783 100644
--- a/fs/xfs/linux-2.6/xfs_aops.c
+++ b/fs/xfs/linux-2.6/xfs_aops.c
@@ -1390,11 +1390,19 @@ xfs_vm_direct_IO(
iocb->private = xfs_alloc_ioend(inode, IOMAP_UNWRITTEN);
- ret = blockdev_direct_IO_own_locking(rw, iocb, inode,
- iomap.iomap_target->bt_bdev,
- iov, offset, nr_segs,
- xfs_get_blocks_direct,
- xfs_end_io_direct);
+ if (rw == WRITE) {
+ ret = blockdev_direct_IO_own_locking(rw, iocb, inode,
+ iomap.iomap_target->bt_bdev,
+ iov, offset, nr_segs,
+ xfs_get_blocks_direct,
+ xfs_end_io_direct);
+ } else {
+ ret = blockdev_direct_IO_no_locking(rw, iocb, inode,
+ iomap.iomap_target->bt_bdev,
+ iov, offset, nr_segs,
+ xfs_get_blocks_direct,
+ xfs_end_io_direct);
+ }
if (unlikely(ret <= 0 && iocb->private))
xfs_destroy_ioend(iocb->private);
diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h
index ceda3a2859d..7858703ed84 100644
--- a/fs/xfs/linux-2.6/xfs_buf.h
+++ b/fs/xfs/linux-2.6/xfs_buf.h
@@ -246,8 +246,8 @@ extern void xfs_buf_trace(xfs_buf_t *, char *, void *, void *);
#define BUF_BUSY XBF_DONT_BLOCK
#define XFS_BUF_BFLAGS(bp) ((bp)->b_flags)
-#define XFS_BUF_ZEROFLAGS(bp) \
- ((bp)->b_flags &= ~(XBF_READ|XBF_WRITE|XBF_ASYNC|XBF_DELWRI))
+#define XFS_BUF_ZEROFLAGS(bp) ((bp)->b_flags &= \
+ ~(XBF_READ|XBF_WRITE|XBF_ASYNC|XBF_DELWRI|XBF_ORDERED))
#define XFS_BUF_STALE(bp) ((bp)->b_flags |= XFS_B_STALE)
#define XFS_BUF_UNSTALE(bp) ((bp)->b_flags &= ~XFS_B_STALE)
diff --git a/fs/xfs/linux-2.6/xfs_linux.h b/fs/xfs/linux-2.6/xfs_linux.h
index 8c021dc57d1..a13f75c1a93 100644
--- a/fs/xfs/linux-2.6/xfs_linux.h
+++ b/fs/xfs/linux-2.6/xfs_linux.h
@@ -215,7 +215,6 @@ BUFFER_FNS(PrivateStart, unwritten);
#define MIN(a,b) (min(a,b))
#define MAX(a,b) (max(a,b))
#define howmany(x, y) (((x)+((y)-1))/(y))
-#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
/*
* Various platform dependent calls that don't fit anywhere else
diff --git a/fs/xfs/linux-2.6/xfs_lrw.c b/fs/xfs/linux-2.6/xfs_lrw.c
index 5d9cfd91ad0..ee788b1cb36 100644
--- a/fs/xfs/linux-2.6/xfs_lrw.c
+++ b/fs/xfs/linux-2.6/xfs_lrw.c
@@ -264,7 +264,9 @@ xfs_read(
dmflags, &locktype);
if (ret) {
xfs_iunlock(ip, XFS_IOLOCK_SHARED);
- goto unlock_mutex;
+ if (unlikely(ioflags & IO_ISDIRECT))
+ mutex_unlock(&inode->i_mutex);
+ return ret;
}
}
@@ -272,6 +274,9 @@ xfs_read(
bhv_vop_flushinval_pages(vp, ctooff(offtoct(*offset)),
-1, FI_REMAPF_LOCKED);
+ if (unlikely(ioflags & IO_ISDIRECT))
+ mutex_unlock(&inode->i_mutex);
+
xfs_rw_enter_trace(XFS_READ_ENTER, &ip->i_iocore,
(void *)iovp, segs, *offset, ioflags);
ret = __generic_file_aio_read(iocb, iovp, segs, offset);
@@ -281,10 +286,6 @@ xfs_read(
XFS_STATS_ADD(xs_read_bytes, ret);
xfs_iunlock(ip, XFS_IOLOCK_SHARED);
-
-unlock_mutex:
- if (unlikely(ioflags & IO_ISDIRECT))
- mutex_unlock(&inode->i_mutex);
return ret;
}
@@ -390,6 +391,8 @@ xfs_splice_write(
xfs_inode_t *ip = XFS_BHVTOI(bdp);
xfs_mount_t *mp = ip->i_mount;
ssize_t ret;
+ struct inode *inode = outfilp->f_mapping->host;
+ xfs_fsize_t isize;
XFS_STATS_INC(xs_write_calls);
if (XFS_FORCED_SHUTDOWN(ip->i_mount))
@@ -416,6 +419,20 @@ xfs_splice_write(
if (ret > 0)
XFS_STATS_ADD(xs_write_bytes, ret);
+ isize = i_size_read(inode);
+ if (unlikely(ret < 0 && ret != -EFAULT && *ppos > isize))
+ *ppos = isize;
+
+ if (*ppos > ip->i_d.di_size) {
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ if (*ppos > ip->i_d.di_size) {
+ ip->i_d.di_size = *ppos;
+ i_size_write(inode, *ppos);
+ ip->i_update_core = 1;
+ ip->i_update_size = 1;
+ }
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ }
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
return ret;
}
diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c
index 9bdef9d5190..4754f342a5d 100644
--- a/fs/xfs/linux-2.6/xfs_super.c
+++ b/fs/xfs/linux-2.6/xfs_super.c
@@ -314,6 +314,13 @@ xfs_mountfs_check_barriers(xfs_mount_t *mp)
return;
}
+ if (xfs_readonly_buftarg(mp->m_ddev_targp)) {
+ xfs_fs_cmn_err(CE_NOTE, mp,
+ "Disabling barriers, underlying device is readonly");
+ mp->m_flags &= ~XFS_MOUNT_BARRIER;
+ return;
+ }
+
error = xfs_barrier_test(mp);
if (error) {
xfs_fs_cmn_err(CE_NOTE, mp,
diff --git a/fs/xfs/quota/xfs_qm_bhv.c b/fs/xfs/quota/xfs_qm_bhv.c
index e95e99f7168..db8872be8c8 100644
--- a/fs/xfs/quota/xfs_qm_bhv.c
+++ b/fs/xfs/quota/xfs_qm_bhv.c
@@ -203,7 +203,7 @@ xfs_qm_statvfs(
if (error || !vnode)
return error;
- mp = XFS_BHVTOM(bhv);
+ mp = xfs_vfstom(bhvtovfs(bhv));
ip = xfs_vtoi(vnode);
if (!(ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT))
@@ -217,17 +217,24 @@ xfs_qm_statvfs(
return 0;
dp = &dqp->q_core;
- limit = dp->d_blk_softlimit ? dp->d_blk_softlimit : dp->d_blk_hardlimit;
+ limit = dp->d_blk_softlimit ?
+ be64_to_cpu(dp->d_blk_softlimit) :
+ be64_to_cpu(dp->d_blk_hardlimit);
if (limit && statp->f_blocks > limit) {
statp->f_blocks = limit;
- statp->f_bfree = (statp->f_blocks > dp->d_bcount) ?
- (statp->f_blocks - dp->d_bcount) : 0;
+ statp->f_bfree =
+ (statp->f_blocks > be64_to_cpu(dp->d_bcount)) ?
+ (statp->f_blocks - be64_to_cpu(dp->d_bcount)) : 0;
}
- limit = dp->d_ino_softlimit ? dp->d_ino_softlimit : dp->d_ino_hardlimit;
+
+ limit = dp->d_ino_softlimit ?
+ be64_to_cpu(dp->d_ino_softlimit) :
+ be64_to_cpu(dp->d_ino_hardlimit);
if (limit && statp->f_files > limit) {
statp->f_files = limit;
- statp->f_ffree = (statp->f_files > dp->d_icount) ?
- (statp->f_ffree - dp->d_icount) : 0;
+ statp->f_ffree =
+ (statp->f_files > be64_to_cpu(dp->d_icount)) ?
+ (statp->f_ffree - be64_to_cpu(dp->d_icount)) : 0;
}
xfs_qm_dqput(dqp);
diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c
index eef6763f3a6..d2bbcd882a6 100644
--- a/fs/xfs/xfs_alloc.c
+++ b/fs/xfs/xfs_alloc.c
@@ -1835,40 +1835,47 @@ xfs_alloc_fix_freelist(
&agbp)))
return error;
if (!pag->pagf_init) {
+ ASSERT(flags & XFS_ALLOC_FLAG_TRYLOCK);
+ ASSERT(!(flags & XFS_ALLOC_FLAG_FREEING));
args->agbp = NULL;
return 0;
}
} else
agbp = NULL;
- /* If this is a metadata preferred pag and we are user data
+ /*
+ * If this is a metadata preferred pag and we are user data
* then try somewhere else if we are not being asked to
* try harder at this point
*/
- if (pag->pagf_metadata && args->userdata && flags) {
+ if (pag->pagf_metadata && args->userdata &&
+ (flags & XFS_ALLOC_FLAG_TRYLOCK)) {
+ ASSERT(!(flags & XFS_ALLOC_FLAG_FREEING));
args->agbp = NULL;
return 0;
}
- need = XFS_MIN_FREELIST_PAG(pag, mp);
- delta = need > pag->pagf_flcount ? need - pag->pagf_flcount : 0;
- /*
- * If it looks like there isn't a long enough extent, or enough
- * total blocks, reject it.
- */
- longest = (pag->pagf_longest > delta) ?
- (pag->pagf_longest - delta) :
- (pag->pagf_flcount > 0 || pag->pagf_longest > 0);
- if (args->minlen + args->alignment + args->minalignslop - 1 > longest ||
- (!(flags & XFS_ALLOC_FLAG_FREEING) &&
- (int)(pag->pagf_freeblks + pag->pagf_flcount -
- need - args->total) <
- (int)args->minleft)) {
- if (agbp)
- xfs_trans_brelse(tp, agbp);
- args->agbp = NULL;
- return 0;
+ if (!(flags & XFS_ALLOC_FLAG_FREEING)) {
+ need = XFS_MIN_FREELIST_PAG(pag, mp);
+ delta = need > pag->pagf_flcount ? need - pag->pagf_flcount : 0;
+ /*
+ * If it looks like there isn't a long enough extent, or enough
+ * total blocks, reject it.
+ */
+ longest = (pag->pagf_longest > delta) ?
+ (pag->pagf_longest - delta) :
+ (pag->pagf_flcount > 0 || pag->pagf_longest > 0);
+ if ((args->minlen + args->alignment + args->minalignslop - 1) >
+ longest ||
+ ((int)(pag->pagf_freeblks + pag->pagf_flcount -
+ need - args->total) < (int)args->minleft)) {
+ if (agbp)
+ xfs_trans_brelse(tp, agbp);
+ args->agbp = NULL;
+ return 0;
+ }
}
+
/*
* Get the a.g. freespace buffer.
* Can fail if we're not blocking on locks, and it's held.
@@ -1878,6 +1885,8 @@ xfs_alloc_fix_freelist(
&agbp)))
return error;
if (agbp == NULL) {
+ ASSERT(flags & XFS_ALLOC_FLAG_TRYLOCK);
+ ASSERT(!(flags & XFS_ALLOC_FLAG_FREEING));
args->agbp = NULL;
return 0;
}
@@ -1887,22 +1896,24 @@ xfs_alloc_fix_freelist(
*/
agf = XFS_BUF_TO_AGF(agbp);
need = XFS_MIN_FREELIST(agf, mp);
- delta = need > be32_to_cpu(agf->agf_flcount) ?
- (need - be32_to_cpu(agf->agf_flcount)) : 0;
/*
* If there isn't enough total or single-extent, reject it.
*/
- longest = be32_to_cpu(agf->agf_longest);
- longest = (longest > delta) ? (longest - delta) :
- (be32_to_cpu(agf->agf_flcount) > 0 || longest > 0);
- if (args->minlen + args->alignment + args->minalignslop - 1 > longest ||
- (!(flags & XFS_ALLOC_FLAG_FREEING) &&
- (int)(be32_to_cpu(agf->agf_freeblks) +
- be32_to_cpu(agf->agf_flcount) - need - args->total) <
- (int)args->minleft)) {
- xfs_trans_brelse(tp, agbp);
- args->agbp = NULL;
- return 0;
+ if (!(flags & XFS_ALLOC_FLAG_FREEING)) {
+ delta = need > be32_to_cpu(agf->agf_flcount) ?
+ (need - be32_to_cpu(agf->agf_flcount)) : 0;
+ longest = be32_to_cpu(agf->agf_longest);
+ longest = (longest > delta) ? (longest - delta) :
+ (be32_to_cpu(agf->agf_flcount) > 0 || longest > 0);
+ if ((args->minlen + args->alignment + args->minalignslop - 1) >
+ longest ||
+ ((int)(be32_to_cpu(agf->agf_freeblks) +
+ be32_to_cpu(agf->agf_flcount) - need - args->total) <
+ (int)args->minleft)) {
+ xfs_trans_brelse(tp, agbp);
+ args->agbp = NULL;
+ return 0;
+ }
}
/*
* Make the freelist shorter if it's too long.
@@ -1950,12 +1961,11 @@ xfs_alloc_fix_freelist(
* on a completely full ag.
*/
if (targs.agbno == NULLAGBLOCK) {
- if (!(flags & XFS_ALLOC_FLAG_FREEING)) {
- xfs_trans_brelse(tp, agflbp);
- args->agbp = NULL;
- return 0;
- }
- break;
+ if (flags & XFS_ALLOC_FLAG_FREEING)
+ break;
+ xfs_trans_brelse(tp, agflbp);
+ args->agbp = NULL;
+ return 0;
}
/*
* Put each allocated block on the list.
@@ -2442,31 +2452,26 @@ xfs_free_extent(
xfs_fsblock_t bno, /* starting block number of extent */
xfs_extlen_t len) /* length of extent */
{
-#ifdef DEBUG
- xfs_agf_t *agf; /* a.g. freespace header */
-#endif
- xfs_alloc_arg_t args; /* allocation argument structure */
+ xfs_alloc_arg_t args;
int error;
ASSERT(len != 0);
+ memset(&args, 0, sizeof(xfs_alloc_arg_t));
args.tp = tp;
args.mp = tp->t_mountp;
args.agno = XFS_FSB_TO_AGNO(args.mp, bno);
ASSERT(args.agno < args.mp->m_sb.sb_agcount);
args.agbno = XFS_FSB_TO_AGBNO(args.mp, bno);
- args.alignment = 1;
- args.minlen = args.minleft = args.minalignslop = 0;
down_read(&args.mp->m_peraglock);
args.pag = &args.mp->m_perag[args.agno];
if ((error = xfs_alloc_fix_freelist(&args, XFS_ALLOC_FLAG_FREEING)))
goto error0;
#ifdef DEBUG
ASSERT(args.agbp != NULL);
- agf = XFS_BUF_TO_AGF(args.agbp);
- ASSERT(args.agbno + len <= be32_to_cpu(agf->agf_length));
+ ASSERT((args.agbno + len) <=
+ be32_to_cpu(XFS_BUF_TO_AGF(args.agbp)->agf_length));
#endif
- error = xfs_free_ag_extent(tp, args.agbp, args.agno, args.agbno,
- len, 0);
+ error = xfs_free_ag_extent(tp, args.agbp, args.agno, args.agbno, len, 0);
error0:
up_read(&args.mp->m_peraglock);
return error;
diff --git a/fs/xfs/xfs_alloc.h b/fs/xfs/xfs_alloc.h
index 650591f999a..5a4256120cc 100644
--- a/fs/xfs/xfs_alloc.h
+++ b/fs/xfs/xfs_alloc.h
@@ -44,6 +44,26 @@ typedef enum xfs_alloctype
#define XFS_ALLOC_FLAG_FREEING 0x00000002 /* indicate caller is freeing extents*/
/*
+ * In order to avoid ENOSPC-related deadlock caused by
+ * out-of-order locking of AGF buffer (PV 947395), we place
+ * constraints on the relationship among actual allocations for
+ * data blocks, freelist blocks, and potential file data bmap
+ * btree blocks. However, these restrictions may result in no
+ * actual space allocated for a delayed extent, for example, a data
+ * block in a certain AG is allocated but there is no additional
+ * block for the additional bmap btree block due to a split of the
+ * bmap btree of the file. The result of this may lead to an
+ * infinite loop in xfssyncd when the file gets flushed to disk and
+ * all delayed extents need to be actually allocated. To get around
+ * this, we explicitly set aside a few blocks which will not be
+ * reserved in delayed allocation. Considering the minimum number of
+ * needed freelist blocks is 4 fsbs _per AG_, a potential split of file's bmap
+ * btree requires 1 fsb, so we set the number of set-aside blocks
+ * to 4 + 4*agcount.
+ */
+#define XFS_ALLOC_SET_ASIDE(mp) (4 + ((mp)->m_sb.sb_agcount * 4))
+
+/*
* Argument structure for xfs_alloc routines.
* This is turned into a structure to avoid having 20 arguments passed
* down several levels of the stack.
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c
index 3a613753906..bf46fae303a 100644
--- a/fs/xfs/xfs_bmap.c
+++ b/fs/xfs/xfs_bmap.c
@@ -4993,7 +4993,7 @@ xfs_bmapi(
bma.firstblock = *firstblock;
bma.alen = alen;
bma.off = aoff;
- bma.conv = (flags & XFS_BMAPI_CONVERT);
+ bma.conv = !!(flags & XFS_BMAPI_CONVERT);
bma.wasdel = wasdelay;
bma.minlen = minlen;
bma.low = flist->xbf_low;
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 077629bab53..c064e72ada9 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -462,7 +462,7 @@ xfs_fs_counts(
xfs_icsb_sync_counters_lazy(mp);
s = XFS_SB_LOCK(mp);
- cnt->freedata = mp->m_sb.sb_fdblocks;
+ cnt->freedata = mp->m_sb.sb_fdblocks - XFS_ALLOC_SET_ASIDE(mp);
cnt->freertx = mp->m_sb.sb_frextents;
cnt->freeino = mp->m_sb.sb_ifree;
cnt->allocino = mp->m_sb.sb_icount;
@@ -519,15 +519,19 @@ xfs_reserve_blocks(
}
mp->m_resblks = request;
} else {
+ __int64_t free;
+
+ free = mp->m_sb.sb_fdblocks - XFS_ALLOC_SET_ASIDE(mp);
delta = request - mp->m_resblks;
- lcounter = mp->m_sb.sb_fdblocks - delta;
+ lcounter = free - delta;
if (lcounter < 0) {
/* We can't satisfy the request, just get what we can */
- mp->m_resblks += mp->m_sb.sb_fdblocks;
- mp->m_resblks_avail += mp->m_sb.sb_fdblocks;
- mp->m_sb.sb_fdblocks = 0;
+ mp->m_resblks += free;
+ mp->m_resblks_avail += free;
+ mp->m_sb.sb_fdblocks = XFS_ALLOC_SET_ASIDE(mp);
} else {
- mp->m_sb.sb_fdblocks = lcounter;
+ mp->m_sb.sb_fdblocks =
+ lcounter + XFS_ALLOC_SET_ASIDE(mp);
mp->m_resblks = request;
mp->m_resblks_avail += delta;
}
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 86c1bf0bba9..1f8ecff8553 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -334,10 +334,9 @@ xfs_itobp(
#if !defined(__KERNEL__)
ni = 0;
#elif defined(DEBUG)
- ni = (imap_flags & XFS_IMAP_BULKSTAT) ? 0 :
- (BBTOB(imap.im_len) >> mp->m_sb.sb_inodelog);
+ ni = BBTOB(imap.im_len) >> mp->m_sb.sb_inodelog;
#else /* usual case */
- ni = (imap_flags & XFS_IMAP_BULKSTAT) ? 0 : 1;
+ ni = 1;
#endif
for (i = 0; i < ni; i++) {
@@ -348,11 +347,15 @@ xfs_itobp(
(i << mp->m_sb.sb_inodelog));
di_ok = INT_GET(dip->di_core.di_magic, ARCH_CONVERT) == XFS_DINODE_MAGIC &&
XFS_DINODE_GOOD_VERSION(INT_GET(dip->di_core.di_version, ARCH_CONVERT));
- if (unlikely(XFS_TEST_ERROR(!di_ok, mp, XFS_ERRTAG_ITOBP_INOTOBP,
- XFS_RANDOM_ITOBP_INOTOBP))) {
+ if (unlikely(XFS_TEST_ERROR(!di_ok, mp,
+ XFS_ERRTAG_ITOBP_INOTOBP,
+ XFS_RANDOM_ITOBP_INOTOBP))) {
+ if (imap_flags & XFS_IMAP_BULKSTAT) {
+ xfs_trans_brelse(tp, bp);
+ return XFS_ERROR(EINVAL);
+ }
#ifdef DEBUG
- if (!(imap_flags & XFS_IMAP_BULKSTAT))
- cmn_err(CE_ALERT,
+ cmn_err(CE_ALERT,
"Device %s - bad inode magic/vsn "
"daddr %lld #%d (magic=%x)",
XFS_BUFTARG_NAME(mp->m_ddev_targp),
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index e730328636c..21ac1a67e3e 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -1413,7 +1413,7 @@ xlog_sync(xlog_t *log,
ops = iclog->ic_header.h_num_logops;
INT_SET(iclog->ic_header.h_num_logops, ARCH_CONVERT, ops);
- bp = iclog->ic_bp;
+ bp = iclog->ic_bp;
ASSERT(XFS_BUF_FSPRIVATE2(bp, unsigned long) == (unsigned long)1);
XFS_BUF_SET_FSPRIVATE2(bp, (unsigned long)2);
XFS_BUF_SET_ADDR(bp, BLOCK_LSN(INT_GET(iclog->ic_header.h_lsn, ARCH_CONVERT)));
@@ -1430,15 +1430,14 @@ xlog_sync(xlog_t *log,
}
XFS_BUF_SET_PTR(bp, (xfs_caddr_t) &(iclog->ic_header), count);
XFS_BUF_SET_FSPRIVATE(bp, iclog); /* save for later */
+ XFS_BUF_ZEROFLAGS(bp);
XFS_BUF_BUSY(bp);
XFS_BUF_ASYNC(bp);
/*
* Do an ordered write for the log block.
- *
- * It may not be needed to flush the first split block in the log wrap
- * case, but do it anyways to be safe -AK
+ * Its unnecessary to flush the first split block in the log wrap case.
*/
- if (log->l_mp->m_flags & XFS_MOUNT_BARRIER)
+ if (!split && (log->l_mp->m_flags & XFS_MOUNT_BARRIER))
XFS_BUF_ORDERED(bp);
ASSERT(XFS_BUF_ADDR(bp) <= log->l_logBBsize-1);
@@ -1460,7 +1459,7 @@ xlog_sync(xlog_t *log,
return error;
}
if (split) {
- bp = iclog->ic_log->l_xbuf;
+ bp = iclog->ic_log->l_xbuf;
ASSERT(XFS_BUF_FSPRIVATE2(bp, unsigned long) ==
(unsigned long)1);
XFS_BUF_SET_FSPRIVATE2(bp, (unsigned long)2);
@@ -1468,6 +1467,7 @@ xlog_sync(xlog_t *log,
XFS_BUF_SET_PTR(bp, (xfs_caddr_t)((__psint_t)&(iclog->ic_header)+
(__psint_t)count), split);
XFS_BUF_SET_FSPRIVATE(bp, iclog);
+ XFS_BUF_ZEROFLAGS(bp);
XFS_BUF_BUSY(bp);
XFS_BUF_ASYNC(bp);
if (log->l_mp->m_flags & XFS_MOUNT_BARRIER)
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index 4be5c0b2d29..9dfae18d995 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -1243,24 +1243,6 @@ xfs_mod_sb(xfs_trans_t *tp, __int64_t fields)
xfs_trans_log_buf(tp, bp, first, last);
}
-/*
- * In order to avoid ENOSPC-related deadlock caused by
- * out-of-order locking of AGF buffer (PV 947395), we place
- * constraints on the relationship among actual allocations for
- * data blocks, freelist blocks, and potential file data bmap
- * btree blocks. However, these restrictions may result in no
- * actual space allocated for a delayed extent, for example, a data
- * block in a certain AG is allocated but there is no additional
- * block for the additional bmap btree block due to a split of the
- * bmap btree of the file. The result of this may lead to an
- * infinite loop in xfssyncd when the file gets flushed to disk and
- * all delayed extents need to be actually allocated. To get around
- * this, we explicitly set aside a few blocks which will not be
- * reserved in delayed allocation. Considering the minimum number of
- * needed freelist blocks is 4 fsbs, a potential split of file's bmap
- * btree requires 1 fsb, so we set the number of set-aside blocks to 8.
-*/
-#define SET_ASIDE_BLOCKS 8
/*
* xfs_mod_incore_sb_unlocked() is a utility routine common used to apply
@@ -1306,7 +1288,8 @@ xfs_mod_incore_sb_unlocked(xfs_mount_t *mp, xfs_sb_field_t field,
return 0;
case XFS_SBS_FDBLOCKS:
- lcounter = (long long)mp->m_sb.sb_fdblocks - SET_ASIDE_BLOCKS;
+ lcounter = (long long)
+ mp->m_sb.sb_fdblocks - XFS_ALLOC_SET_ASIDE(mp);
res_used = (long long)(mp->m_resblks - mp->m_resblks_avail);
if (delta > 0) { /* Putting blocks back */
@@ -1340,7 +1323,7 @@ xfs_mod_incore_sb_unlocked(xfs_mount_t *mp, xfs_sb_field_t field,
}
}
- mp->m_sb.sb_fdblocks = lcounter + SET_ASIDE_BLOCKS;
+ mp->m_sb.sb_fdblocks = lcounter + XFS_ALLOC_SET_ASIDE(mp);
return 0;
case XFS_SBS_FREXTENTS:
lcounter = (long long)mp->m_sb.sb_frextents;
@@ -2021,7 +2004,8 @@ xfs_icsb_sync_counters_lazy(
* when we get near ENOSPC.
*/
#define XFS_ICSB_INO_CNTR_REENABLE 64
-#define XFS_ICSB_FDBLK_CNTR_REENABLE 512
+#define XFS_ICSB_FDBLK_CNTR_REENABLE(mp) \
+ (512 + XFS_ALLOC_SET_ASIDE(mp))
STATIC void
xfs_icsb_balance_counter(
xfs_mount_t *mp,
@@ -2055,7 +2039,7 @@ xfs_icsb_balance_counter(
case XFS_SBS_FDBLOCKS:
count = mp->m_sb.sb_fdblocks;
resid = do_div(count, weight);
- if (count < XFS_ICSB_FDBLK_CNTR_REENABLE)
+ if (count < XFS_ICSB_FDBLK_CNTR_REENABLE(mp))
goto out;
break;
default:
@@ -2110,11 +2094,11 @@ again:
case XFS_SBS_FDBLOCKS:
BUG_ON((mp->m_resblks - mp->m_resblks_avail) != 0);
- lcounter = icsbp->icsb_fdblocks;
+ lcounter = icsbp->icsb_fdblocks - XFS_ALLOC_SET_ASIDE(mp);
lcounter += delta;
if (unlikely(lcounter < 0))
goto slow_path;
- icsbp->icsb_fdblocks = lcounter;
+ icsbp->icsb_fdblocks = lcounter + XFS_ALLOC_SET_ASIDE(mp);
break;
default:
BUG();
diff --git a/fs/xfs/xfs_vfsops.c b/fs/xfs/xfs_vfsops.c
index 6c96391f3f1..a34796e57af 100644
--- a/fs/xfs/xfs_vfsops.c
+++ b/fs/xfs/xfs_vfsops.c
@@ -515,7 +515,7 @@ xfs_mount(
if (error)
goto error2;
- if ((mp->m_flags & XFS_MOUNT_BARRIER) && !(vfsp->vfs_flag & VFS_RDONLY))
+ if (mp->m_flags & XFS_MOUNT_BARRIER)
xfs_mountfs_check_barriers(mp);
error = XFS_IOINIT(vfsp, args, flags);
@@ -811,7 +811,8 @@ xfs_statvfs(
statp->f_bsize = sbp->sb_blocksize;
lsize = sbp->sb_logstart ? sbp->sb_logblocks : 0;
statp->f_blocks = sbp->sb_dblocks - lsize;
- statp->f_bfree = statp->f_bavail = sbp->sb_fdblocks;
+ statp->f_bfree = statp->f_bavail =
+ sbp->sb_fdblocks - XFS_ALLOC_SET_ASIDE(mp);
fakeinos = statp->f_bfree << sbp->sb_inopblog;
#if XFS_BIG_INUMS
fakeinos += mp->m_inoadd;