summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorAnton Altaparmakov <aia21@cantab.net>2005-10-30 21:00:04 +0000
committerAnton Altaparmakov <aia21@cantab.net>2005-10-30 21:00:04 +0000
commit07b188ab773e183871e57b33ae37bf635c9f12ba (patch)
tree311df8a0dd12fb7bd3e9b5b1a5ca500f0428d679 /fs
parent47c564e10f219f867bdb49225972749a43485a47 (diff)
parent9f75e1eff3edb2bb07349b94c28f4f2a6c66ca43 (diff)
Merge branch 'master' of /usr/src/ntfs-2.6/
Diffstat (limited to 'fs')
-rw-r--r--fs/afs/file.c8
-rw-r--r--fs/binfmt_aout.c1
-rw-r--r--fs/binfmt_elf.c1
-rw-r--r--fs/binfmt_elf_fdpic.c7
-rw-r--r--fs/binfmt_flat.c1
-rw-r--r--fs/binfmt_som.c1
-rw-r--r--fs/bio.c4
-rw-r--r--fs/buffer.c6
-rw-r--r--fs/coda/psdev.c4
-rw-r--r--fs/compat.c1
-rw-r--r--fs/compat_ioctl.c1
-rw-r--r--fs/dcache.c2
-rw-r--r--fs/direct-io.c4
-rw-r--r--fs/dquot.c2
-rw-r--r--fs/exec.c29
-rw-r--r--fs/ext3/inode.c2
-rw-r--r--fs/ext3/super.c26
-rw-r--r--fs/hfs/inode.c2
-rw-r--r--fs/hfsplus/inode.c2
-rw-r--r--fs/hfsplus/super.c1
-rw-r--r--fs/hugetlbfs/inode.c206
-rw-r--r--fs/inode.c2
-rw-r--r--fs/jbd/journal.c2
-rw-r--r--fs/jbd/transaction.c2
-rw-r--r--fs/jfs/jfs_dmap.c20
-rw-r--r--fs/jfs/jfs_imap.c10
-rw-r--r--fs/jfs/jfs_metapage.c22
-rw-r--r--fs/jfs/jfs_txnmgr.c2
-rw-r--r--fs/jfs/jfs_xtree.c18
-rw-r--r--fs/jfs/super.c1
-rw-r--r--fs/lockd/host.c4
-rw-r--r--fs/locks.c48
-rw-r--r--fs/mbcache.c6
-rw-r--r--fs/namei.c95
-rw-r--r--fs/nfs/delegation.c2
-rw-r--r--fs/nfs/delegation.h16
-rw-r--r--fs/nfs/dir.c67
-rw-r--r--fs/nfs/file.c31
-rw-r--r--fs/nfs/inode.c200
-rw-r--r--fs/nfs/nfs2xdr.c1
-rw-r--r--fs/nfs/nfs3proc.c92
-rw-r--r--fs/nfs/nfs3xdr.c1
-rw-r--r--fs/nfs/nfs4_fs.h53
-rw-r--r--fs/nfs/nfs4proc.c735
-rw-r--r--fs/nfs/nfs4state.c181
-rw-r--r--fs/nfs/nfs4xdr.c305
-rw-r--r--fs/nfs/proc.c44
-rw-r--r--fs/nfs/read.c1
-rw-r--r--fs/nfs/write.c2
-rw-r--r--fs/open.c79
-rw-r--r--fs/partitions/check.c29
-rw-r--r--fs/proc/array.c2
-rw-r--r--fs/proc/task_mmu.c51
-rw-r--r--fs/reiserfs/fix_node.c2
-rw-r--r--fs/reiserfs/inode.c2
-rw-r--r--fs/reiserfs/xattr.c2
-rw-r--r--fs/xfs/linux-2.6/kmem.c22
-rw-r--r--fs/xfs/linux-2.6/kmem.h18
-rw-r--r--fs/xfs/linux-2.6/xfs_aops.c2
-rw-r--r--fs/xfs/linux-2.6/xfs_buf.c15
60 files changed, 1637 insertions, 861 deletions
diff --git a/fs/afs/file.c b/fs/afs/file.c
index 23c12512802..4975c9c193d 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -29,7 +29,7 @@ static int afs_file_release(struct inode *inode, struct file *file);
static int afs_file_readpage(struct file *file, struct page *page);
static int afs_file_invalidatepage(struct page *page, unsigned long offset);
-static int afs_file_releasepage(struct page *page, int gfp_flags);
+static int afs_file_releasepage(struct page *page, gfp_t gfp_flags);
static ssize_t afs_file_write(struct file *file, const char __user *buf,
size_t size, loff_t *off);
@@ -279,7 +279,7 @@ static int afs_file_invalidatepage(struct page *page, unsigned long offset)
/*
* release a page and cleanup its private data
*/
-static int afs_file_releasepage(struct page *page, int gfp_flags)
+static int afs_file_releasepage(struct page *page, gfp_t gfp_flags)
{
struct cachefs_page *pageio;
@@ -291,8 +291,8 @@ static int afs_file_releasepage(struct page *page, int gfp_flags)
cachefs_uncache_page(vnode->cache, page);
#endif
- pageio = (struct cachefs_page *) page->private;
- page->private = 0;
+ pageio = (struct cachefs_page *) page_private(page);
+ set_page_private(page, 0);
ClearPagePrivate(page);
if (pageio)
diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c
index dd9baabaf01..72011826f0c 100644
--- a/fs/binfmt_aout.c
+++ b/fs/binfmt_aout.c
@@ -318,7 +318,6 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
current->mm->free_area_cache = current->mm->mmap_base;
current->mm->cached_hole_size = 0;
- set_mm_counter(current->mm, rss, 0);
current->mm->mmap = NULL;
compute_creds(bprm);
current->flags &= ~PF_FORKNOEXEC;
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index d4b15576e58..918ccc267e4 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -773,7 +773,6 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
/* Do this so that we can load the interpreter, if need be. We will
change some of these later */
- set_mm_counter(current->mm, rss, 0);
current->mm->free_area_cache = current->mm->mmap_base;
current->mm->cached_hole_size = 0;
retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP),
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index 134c9c0d1f5..dda87c4c82a 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -294,14 +294,7 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs
&interp_params,
&current->mm->start_stack,
&current->mm->start_brk);
-#endif
-
- /* do this so that we can load the interpreter, if need be
- * - we will change some of these later
- */
- set_mm_counter(current->mm, rss, 0);
-#ifdef CONFIG_MMU
retval = setup_arg_pages(bprm, current->mm->start_stack, executable_stack);
if (retval < 0) {
send_sig(SIGKILL, current, 0);
diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c
index 7974efa107b..9d6625829b9 100644
--- a/fs/binfmt_flat.c
+++ b/fs/binfmt_flat.c
@@ -650,7 +650,6 @@ static int load_flat_file(struct linux_binprm * bprm,
current->mm->start_brk = datapos + data_len + bss_len;
current->mm->brk = (current->mm->start_brk + 3) & ~3;
current->mm->context.end_brk = memp + ksize((void *) memp) - stack_len;
- set_mm_counter(current->mm, rss, 0);
}
if (flags & FLAT_FLAG_KTRACE)
diff --git a/fs/binfmt_som.c b/fs/binfmt_som.c
index 227a2682d2b..00a91dc25d1 100644
--- a/fs/binfmt_som.c
+++ b/fs/binfmt_som.c
@@ -259,7 +259,6 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs)
create_som_tables(bprm);
current->mm->start_stack = bprm->p;
- set_mm_counter(current->mm, rss, 0);
#if 0
printk("(start_brk) %08lx\n" , (unsigned long) current->mm->start_brk);
diff --git a/fs/bio.c b/fs/bio.c
index 7d81a93afd4..460554b07ff 100644
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -778,7 +778,7 @@ static int bio_map_kern_endio(struct bio *bio, unsigned int bytes_done, int err)
static struct bio *__bio_map_kern(request_queue_t *q, void *data,
- unsigned int len, unsigned int gfp_mask)
+ unsigned int len, gfp_t gfp_mask)
{
unsigned long kaddr = (unsigned long)data;
unsigned long end = (kaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
@@ -825,7 +825,7 @@ static struct bio *__bio_map_kern(request_queue_t *q, void *data,
* device. Returns an error pointer in case of error.
*/
struct bio *bio_map_kern(request_queue_t *q, void *data, unsigned int len,
- unsigned int gfp_mask)
+ gfp_t gfp_mask)
{
struct bio *bio;
diff --git a/fs/buffer.c b/fs/buffer.c
index 1216c0d3c8c..2066e4cb700 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -96,7 +96,7 @@ static void
__clear_page_buffers(struct page *page)
{
ClearPagePrivate(page);
- page->private = 0;
+ set_page_private(page, 0);
page_cache_release(page);
}
@@ -502,7 +502,7 @@ static void free_more_memory(void)
yield();
for_each_pgdat(pgdat) {
- zones = pgdat->node_zonelists[GFP_NOFS&GFP_ZONEMASK].zones;
+ zones = pgdat->node_zonelists[gfp_zone(GFP_NOFS)].zones;
if (*zones)
try_to_free_pages(zones, GFP_NOFS);
}
@@ -1571,7 +1571,7 @@ static inline void discard_buffer(struct buffer_head * bh)
*
* NOTE: @gfp_mask may go away, and this function may become non-blocking.
*/
-int try_to_release_page(struct page *page, int gfp_mask)
+int try_to_release_page(struct page *page, gfp_t gfp_mask)
{
struct address_space * const mapping = page->mapping;
diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c
index 3d1cce3653b..6a3df88accf 100644
--- a/fs/coda/psdev.c
+++ b/fs/coda/psdev.c
@@ -370,8 +370,8 @@ static int init_coda_psdev(void)
}
devfs_mk_dir ("coda");
for (i = 0; i < MAX_CODADEVS; i++) {
- class_device_create(coda_psdev_class, MKDEV(CODA_PSDEV_MAJOR,i),
- NULL, "cfs%d", i);
+ class_device_create(coda_psdev_class, NULL,
+ MKDEV(CODA_PSDEV_MAJOR,i), NULL, "cfs%d", i);
err = devfs_mk_cdev(MKDEV(CODA_PSDEV_MAJOR, i),
S_IFCHR|S_IRUSR|S_IWUSR, "coda/%d", i);
if (err)
diff --git a/fs/compat.c b/fs/compat.c
index a719e158e00..8e71cdbecc7 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -1490,7 +1490,6 @@ int compat_do_execve(char * filename,
/* execve success */
security_bprm_free(bprm);
acct_update_integrals(current);
- update_mem_hiwater(current);
kfree(bprm);
return retval;
}
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index e28a74203f3..a327e03753a 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -3050,6 +3050,7 @@ HANDLE_IOCTL(TIOCSSERIAL, serial_struct_ioctl)
HANDLE_IOCTL(USBDEVFS_CONTROL32, do_usbdevfs_control)
HANDLE_IOCTL(USBDEVFS_BULK32, do_usbdevfs_bulk)
HANDLE_IOCTL(USBDEVFS_DISCSIGNAL32, do_usbdevfs_discsignal)
+COMPATIBLE_IOCTL(USBDEVFS_IOCTL32)
/* i2c */
HANDLE_IOCTL(I2C_FUNCS, w_long)
HANDLE_IOCTL(I2C_RDWR, do_i2c_rdwr_ioctl)
diff --git a/fs/dcache.c b/fs/dcache.c
index fb10386c59b..e90512ed35a 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -689,7 +689,7 @@ void shrink_dcache_anon(struct hlist_head *head)
*
* In this case we return -1 to tell the caller that we baled.
*/
-static int shrink_dcache_memory(int nr, unsigned int gfp_mask)
+static int shrink_dcache_memory(int nr, gfp_t gfp_mask)
{
if (nr) {
if (!(gfp_mask & __GFP_FS))
diff --git a/fs/direct-io.c b/fs/direct-io.c
index 0d06097bc99..3931e7f1e6b 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -162,6 +162,7 @@ static int dio_refill_pages(struct dio *dio)
up_read(&current->mm->mmap_sem);
if (ret < 0 && dio->blocks_available && (dio->rw == WRITE)) {
+ struct page *page = ZERO_PAGE(dio->curr_user_address);
/*
* A memory fault, but the filesystem has some outstanding
* mapped blocks. We need to use those blocks up to avoid
@@ -169,7 +170,8 @@ static int dio_refill_pages(struct dio *dio)
*/
if (dio->page_errors == 0)
dio->page_errors = ret;
- dio->pages[0] = ZERO_PAGE(dio->curr_user_address);
+ page_cache_get(page);
+ dio->pages[0] = page;
dio->head = 0;
dio->tail = 1;
ret = 0;
diff --git a/fs/dquot.c b/fs/dquot.c
index b9732335bcd..05f3327d64a 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -500,7 +500,7 @@ static void prune_dqcache(int count)
* more memory
*/
-static int shrink_dqcache_memory(int nr, unsigned int gfp_mask)
+static int shrink_dqcache_memory(int nr, gfp_t gfp_mask)
{
if (nr) {
spin_lock(&dq_list_lock);
diff --git a/fs/exec.c b/fs/exec.c
index a04a575ad43..ba73797eb4c 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -126,8 +126,7 @@ asmlinkage long sys_uselib(const char __user * library)
struct nameidata nd;
int error;
- nd.intent.open.flags = FMODE_READ;
- error = __user_walk(library, LOOKUP_FOLLOW|LOOKUP_OPEN, &nd);
+ error = __user_path_lookup_open(library, LOOKUP_FOLLOW, &nd, FMODE_READ);
if (error)
goto out;
@@ -139,7 +138,7 @@ asmlinkage long sys_uselib(const char __user * library)
if (error)
goto exit;
- file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
+ file = nameidata_to_filp(&nd, O_RDONLY);
error = PTR_ERR(file);
if (IS_ERR(file))
goto out;
@@ -167,6 +166,7 @@ asmlinkage long sys_uselib(const char __user * library)
out:
return error;
exit:
+ release_open_intent(&nd);
path_release(&nd);
goto out;
}
@@ -309,40 +309,36 @@ void install_arg_page(struct vm_area_struct *vma,
pud_t * pud;
pmd_t * pmd;
pte_t * pte;
+ spinlock_t *ptl;
if (unlikely(anon_vma_prepare(vma)))
- goto out_sig;
+ goto out;
flush_dcache_page(page);
pgd = pgd_offset(mm, address);
-
- spin_lock(&mm->page_table_lock);
pud = pud_alloc(mm, pgd, address);
if (!pud)
goto out;
pmd = pmd_alloc(mm, pud, address);
if (!pmd)
goto out;
- pte = pte_alloc_map(mm, pmd, address);
+ pte = pte_alloc_map_lock(mm, pmd, address, &ptl);
if (!pte)
goto out;
if (!pte_none(*pte)) {
- pte_unmap(pte);
+ pte_unmap_unlock(pte, ptl);
goto out;
}
- inc_mm_counter(mm, rss);
+ inc_mm_counter(mm, anon_rss);
lru_cache_add_active(page);
set_pte_at(mm, address, pte, pte_mkdirty(pte_mkwrite(mk_pte(
page, vma->vm_page_prot))));
page_add_anon_rmap(page, vma, address);
- pte_unmap(pte);
- spin_unlock(&mm->page_table_lock);
+ pte_unmap_unlock(pte, ptl);
/* no need for flush_tlb */
return;
out:
- spin_unlock(&mm->page_table_lock);
-out_sig:
__free_page(page);
force_sig(SIGKILL, current);
}
@@ -490,8 +486,7 @@ struct file *open_exec(const char *name)
int err;
struct file *file;
- nd.intent.open.flags = FMODE_READ;
- err = path_lookup(name, LOOKUP_FOLLOW|LOOKUP_OPEN, &nd);
+ err = path_lookup_open(name, LOOKUP_FOLLOW, &nd, FMODE_READ);
file = ERR_PTR(err);
if (!err) {
@@ -504,7 +499,7 @@ struct file *open_exec(const char *name)
err = -EACCES;
file = ERR_PTR(err);
if (!err) {
- file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
+ file = nameidata_to_filp(&nd, O_RDONLY);
if (!IS_ERR(file)) {
err = deny_write_access(file);
if (err) {
@@ -516,6 +511,7 @@ out:
return file;
}
}
+ release_open_intent(&nd);
path_release(&nd);
}
goto out;
@@ -1207,7 +1203,6 @@ int do_execve(char * filename,
/* execve success */
security_bprm_free(bprm);
acct_update_integrals(current);
- update_mem_hiwater(current);
kfree(bprm);
return retval;
}
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index b5177c90d6f..8b38f223279 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -1434,7 +1434,7 @@ static int ext3_invalidatepage(struct page *page, unsigned long offset)
return journal_invalidatepage(journal, page, offset);
}
-static int ext3_releasepage(struct page *page, int wait)
+static int ext3_releasepage(struct page *page, gfp_t wait)
{
journal_t *journal = EXT3_JOURNAL(page->mapping->host);
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index 9e24ceb019f..097383c1115 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -510,19 +510,11 @@ static void ext3_clear_inode(struct inode *inode)
kfree(rsv);
}
-static int ext3_show_options(struct seq_file *seq, struct vfsmount *vfs)
+static inline void ext3_show_quota_options(struct seq_file *seq, struct super_block *sb)
{
- struct super_block *sb = vfs->mnt_sb;
+#if defined(CONFIG_QUOTA)
struct ext3_sb_info *sbi = EXT3_SB(sb);
- if (test_opt(sb, DATA_FLAGS) == EXT3_MOUNT_JOURNAL_DATA)
- seq_puts(seq, ",data=journal");
- else if (test_opt(sb, DATA_FLAGS) == EXT3_MOUNT_ORDERED_DATA)
- seq_puts(seq, ",data=ordered");
- else if (test_opt(sb, DATA_FLAGS) == EXT3_MOUNT_WRITEBACK_DATA)
- seq_puts(seq, ",data=writeback");
-
-#if defined(CONFIG_QUOTA)
if (sbi->s_jquota_fmt)
seq_printf(seq, ",jqfmt=%s",
(sbi->s_jquota_fmt == QFMT_VFS_OLD) ? "vfsold": "vfsv0");
@@ -539,6 +531,20 @@ static int ext3_show_options(struct seq_file *seq, struct vfsmount *vfs)
if (sbi->s_mount_opt & EXT3_MOUNT_GRPQUOTA)
seq_puts(seq, ",grpquota");
#endif
+}
+
+static int ext3_show_options(struct seq_file *seq, struct vfsmount *vfs)
+{
+ struct super_block *sb = vfs->mnt_sb;
+
+ if (test_opt(sb, DATA_FLAGS) == EXT3_MOUNT_JOURNAL_DATA)
+ seq_puts(seq, ",data=journal");
+ else if (test_opt(sb, DATA_FLAGS) == EXT3_MOUNT_ORDERED_DATA)
+ seq_puts(seq, ",data=ordered");
+ else if (test_opt(sb, DATA_FLAGS) == EXT3_MOUNT_WRITEBACK_DATA)
+ seq_puts(seq, ",data=writeback");
+
+ ext3_show_quota_options(seq, sb);
return 0;
}
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index f1570b9f9de..3f680c5675b 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -46,7 +46,7 @@ static sector_t hfs_bmap(struct address_space *mapping, sector_t block)
return generic_block_bmap(mapping, block, hfs_get_block);
}
-static int hfs_releasepage(struct page *page, int mask)
+static int hfs_releasepage(struct page *page, gfp_t mask)
{
struct inode *inode = page->mapping->host;
struct super_block *sb = inode->i_sb;
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index d5642705f63..f205773ddfb 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -40,7 +40,7 @@ static sector_t hfsplus_bmap(struct address_space *mapping, sector_t block)
return generic_block_bmap(mapping, block, hfsplus_get_block);
}
-static int hfsplus_releasepage(struct page *page, int mask)
+static int hfsplus_releasepage(struct page *page, gfp_t mask)
{
struct inode *inode = page->mapping->host;
struct super_block *sb = inode->i_sb;
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index fd0f0f050e1..452fc1fdbd3 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -50,6 +50,7 @@ static void hfsplus_read_inode(struct inode *inode)
init_MUTEX(&HFSPLUS_I(inode).extents_lock);
HFSPLUS_I(inode).flags = 0;
HFSPLUS_I(inode).rsrc_inode = NULL;
+ atomic_set(&HFSPLUS_I(inode).opencnt, 0);
if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) {
read_inode:
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 3a9b6d179cb..e026c807e6b 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -45,10 +45,58 @@ static struct backing_dev_info hugetlbfs_backing_dev_info = {
int sysctl_hugetlb_shm_group;
+static void huge_pagevec_release(struct pagevec *pvec)
+{
+ int i;
+
+ for (i = 0; i < pagevec_count(pvec); ++i)
+ put_page(pvec->pages[i]);
+
+ pagevec_reinit(pvec);
+}
+
+/*
+ * huge_pages_needed tries to determine the number of new huge pages that
+ * will be required to fully populate this VMA. This will be equal to
+ * the size of the VMA in huge pages minus the number of huge pages
+ * (covered by this VMA) that are found in the page cache.
+ *
+ * Result is in bytes to be compatible with is_hugepage_mem_enough()
+ */
+unsigned long
+huge_pages_needed(struct address_space *mapping, struct vm_area_struct *vma)
+{
+ int i;
+ struct pagevec pvec;
+ unsigned long start = vma->vm_start;
+ unsigned long end = vma->vm_end;
+ unsigned long hugepages = (end - start) >> HPAGE_SHIFT;
+ pgoff_t next = vma->vm_pgoff;
+ pgoff_t endpg = next + ((end - start) >> PAGE_SHIFT);
+
+ pagevec_init(&pvec, 0);
+ while (next < endpg) {
+ if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE))
+ break;
+ for (i = 0; i < pagevec_count(&pvec); i++) {
+ struct page *page = pvec.pages[i];
+ if (page->index > next)
+ next = page->index;
+ if (page->index >= endpg)
+ break;
+ next++;
+ hugepages--;
+ }
+ huge_pagevec_release(&pvec);
+ }
+ return hugepages << HPAGE_SHIFT;
+}
+
static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
{
struct inode *inode = file->f_dentry->d_inode;
struct address_space *mapping = inode->i_mapping;
+ unsigned long bytes;
loff_t len, vma_len;
int ret;
@@ -67,6 +115,10 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
if (vma->vm_end - vma->vm_start < HPAGE_SIZE)
return -EINVAL;
+ bytes = huge_pages_needed(mapping, vma);
+ if (!is_hugepage_mem_enough(bytes))
+ return -ENOMEM;
+
vma_len = (loff_t)(vma->vm_end - vma->vm_start);
down(&inode->i_sem);
@@ -79,10 +131,8 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
if (!(vma->vm_flags & VM_WRITE) && len > inode->i_size)
goto out;
- ret = hugetlb_prefault(mapping, vma);
- if (ret)
- goto out;
-
+ ret = 0;
+ hugetlb_prefault_arch_hook(vma->vm_mm);
if (inode->i_size < len)
inode->i_size = len;
out:
@@ -92,7 +142,7 @@ out:
}
/*
- * Called under down_write(mmap_sem), page_table_lock is not held
+ * Called under down_write(mmap_sem).
*/
#ifdef HAVE_ARCH_HUGETLB_UNMAPPED_AREA
@@ -171,16 +221,6 @@ static int hugetlbfs_commit_write(struct file *file,
return -EINVAL;
}
-static void huge_pagevec_release(struct pagevec *pvec)
-{
- int i;
-
- for (i = 0; i < pagevec_count(pvec); ++i)
- put_page(pvec->pages[i]);
-
- pagevec_reinit(pvec);
-}
-
static void truncate_huge_page(struct page *page)
{
clear_page_dirty(page);
@@ -224,52 +264,35 @@ static void truncate_hugepages(struct address_space *mapping, loff_t lstart)
static void hugetlbfs_delete_inode(struct inode *inode)
{
- struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(inode->i_sb);
-
- hlist_del_init(&inode->i_hash);
- list_del_init(&inode->i_list);
- list_del_init(&inode->i_sb_list);
- inode->i_state |= I_FREEING;
- inodes_stat.nr_inodes--;
- spin_unlock(&inode_lock);
-
if (inode->i_data.nrpages)
truncate_hugepages(&inode->i_data, 0);
-
- security_inode_delete(inode);
-
- if (sbinfo->free_inodes >= 0) {
- spin_lock(&sbinfo->stat_lock);
- sbinfo->free_inodes++;
- spin_unlock(&sbinfo->stat_lock);
- }
-
clear_inode(inode);
- destroy_inode(inode);
}
static void hugetlbfs_forget_inode(struct inode *inode)
{
- struct super_block *super_block = inode->i_sb;
- struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(super_block);
+ struct super_block *sb = inode->i_sb;
- if (hlist_unhashed(&inode->i_hash))
- goto out_truncate;
-
- if (!(inode->i_state & (I_DIRTY|I_LOCK))) {
- list_del(&inode->i_list);
- list_add(&inode->i_list, &inode_unused);
- }
- inodes_stat.nr_unused++;
- if (!super_block || (super_block->s_flags & MS_ACTIVE)) {
+ if (!hlist_unhashed(&inode->i_hash)) {
+ if (!(inode->i_state & (I_DIRTY|I_LOCK)))
+ list_move(&inode->i_list, &inode_unused);
+ inodes_stat.nr_unused++;
+ if (!sb || (sb->s_flags & MS_ACTIVE)) {
+ spin_unlock(&inode_lock);
+ return;
+ }
+ inode->i_state |= I_WILL_FREE;
spin_unlock(&inode_lock);
- return;
+ /*
+ * write_inode_now is a noop as we set BDI_CAP_NO_WRITEBACK
+ * in our backing_dev_info.
+ */
+ write_inode_now(inode, 1);
+ spin_lock(&inode_lock);
+ inode->i_state &= ~I_WILL_FREE;
+ inodes_stat.nr_unused--;
+ hlist_del_init(&inode->i_hash);
}
-
- /* write_inode_now() ? */
- inodes_stat.nr_unused--;
- hlist_del_init(&inode->i_hash);
-out_truncate:
list_del_init(&inode->i_list);
list_del_init(&inode->i_sb_list);
inode->i_state |= I_FREEING;
@@ -277,13 +300,6 @@ out_truncate:
spin_unlock(&inode_lock);
if (inode->i_data.nrpages)
truncate_hugepages(&inode->i_data, 0);
-
- if (sbinfo->free_inodes >= 0) {
- spin_lock(&sbinfo->stat_lock);
- sbinfo->free_inodes++;
- spin_unlock(&sbinfo->stat_lock);
- }
-
clear_inode(inode);
destroy_inode(inode);
}
@@ -291,7 +307,7 @@ out_truncate:
static void hugetlbfs_drop_inode(struct inode *inode)
{
if (!inode->i_nlink)
- hugetlbfs_delete_inode(inode);
+ generic_delete_inode(inode);
else
hugetlbfs_forget_inode(inode);
}
@@ -308,7 +324,6 @@ hugetlb_vmtruncate_list(struct prio_tree_root *root, unsigned long h_pgoff)
vma_prio_tree_foreach(vma, &iter, root, h_pgoff, ULONG_MAX) {
unsigned long h_vm_pgoff;
- unsigned long v_length;
unsigned long v_offset;
h_vm_pgoff = vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT);
@@ -319,11 +334,8 @@ hugetlb_vmtruncate_list(struct prio_tree_root *root, unsigned long h_pgoff)
if (h_vm_pgoff >= h_pgoff)
v_offset = 0;
- v_length = vma->vm_end - vma->vm_start;
-
- zap_hugepage_range(vma,
- vma->vm_start + v_offset,
- v_length - v_offset);
+ unmap_hugepage_range(vma,
+ vma->vm_start + v_offset, vma->vm_end);
}
}
@@ -379,17 +391,6 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb, uid_t uid,
gid_t gid, int mode, dev_t dev)
{
struct inode *inode;
- struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(sb);
-
- if (sbinfo->free_inodes >= 0) {
- spin_lock(&sbinfo->stat_lock);
- if (!sbinfo->free_inodes) {
- spin_unlock(&sbinfo->stat_lock);
- return NULL;
- }
- sbinfo->free_inodes--;
- spin_unlock(&sbinfo->stat_lock);
- }
inode = new_inode(sb);
if (inode) {
@@ -531,29 +532,51 @@ static void hugetlbfs_put_super(struct super_block *sb)
}
}
+static inline int hugetlbfs_dec_free_inodes(struct hugetlbfs_sb_info *sbinfo)
+{
+ if (sbinfo->free_inodes >= 0) {
+ spin_lock(&sbinfo->stat_lock);
+ if (unlikely(!sbinfo->free_inodes)) {
+ spin_unlock(&sbinfo->stat_lock);
+ return 0;
+ }
+ sbinfo->free_inodes--;
+ spin_unlock(&sbinfo->stat_lock);
+ }
+
+ return 1;
+}
+
+static void hugetlbfs_inc_free_inodes(struct hugetlbfs_sb_info *sbinfo)
+{
+ if (sbinfo->free_inodes >= 0) {
+ spin_lock(&sbinfo->stat_lock);
+ sbinfo->free_inodes++;
+ spin_unlock(&sbinfo->stat_lock);
+ }
+}
+
+
static kmem_cache_t *hugetlbfs_inode_cachep;
static struct inode *hugetlbfs_alloc_inode(struct super_block *sb)
{
+ struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(sb);
struct hugetlbfs_inode_info *p;
+ if (unlikely(!hugetlbfs_dec_free_inodes(sbinfo)))
+ return NULL;
p = kmem_cache_alloc(hugetlbfs_inode_cachep, SLAB_KERNEL);
- if (!p)
+ if (unlikely(!p)) {
+ hugetlbfs_inc_free_inodes(sbinfo);
return NULL;
+ }
return &p->vfs_inode;
}
-static void init_once(void *foo, kmem_cache_t *cachep, unsigned long flags)
-{
- struct hugetlbfs_inode_info *ei = (struct hugetlbfs_inode_info *)foo;
-
- if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
- SLAB_CTOR_CONSTRUCTOR)
- inode_init_once(&ei->vfs_inode);
-}
-
static void hugetlbfs_destroy_inode(struct inode *inode)
{
+ hugetlbfs_inc_free_inodes(HUGETLBFS_SB(inode->i_sb));
mpol_free_shared_policy(&HUGETLBFS_I(inode)->policy);
kmem_cache_free(hugetlbfs_inode_cachep, HUGETLBFS_I(inode));
}
@@ -565,6 +588,16 @@ static struct address_space_operations hugetlbfs_aops = {
.set_page_dirty = hugetlbfs_set_page_dirty,
};
+
+static void init_once(void *foo, kmem_cache_t *cachep, unsigned long flags)
+{
+ struct hugetlbfs_inode_info *ei = (struct hugetlbfs_inode_info *)foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR)
+ inode_init_once(&ei->vfs_inode);
+}
+
struct file_operations hugetlbfs_file_operations = {
.mmap = hugetlbfs_file_mmap,
.fsync = simple_sync_file,
@@ -592,6 +625,7 @@ static struct super_operations hugetlbfs_ops = {
.alloc_inode = hugetlbfs_alloc_inode,
.destroy_inode = hugetlbfs_destroy_inode,
.statfs = hugetlbfs_statfs,
+ .delete_inode = hugetlbfs_delete_inode,
.drop_inode = hugetlbfs_drop_inode,
.put_super = hugetlbfs_put_super,
};
diff --git a/fs/inode.c b/fs/inode.c
index f80a79ff156..7d331652776 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -475,7 +475,7 @@ static void prune_icache(int nr_to_scan)
* This function is passed the number of inodes to scan, and it returns the
* total number of remaining possibly-reclaimable inodes.
*/
-static int shrink_icache_memory(int nr, unsigned int gfp_mask)
+static int shrink_icache_memory(int nr, gfp_t gfp_mask)
{
if (nr) {
/*
diff --git a/fs/jbd/journal.c b/fs/jbd/journal.c
index 7ae2c4fe506..e4b516ac498 100644
--- a/fs/jbd/journal.c
+++ b/fs/jbd/journal.c
@@ -1606,7 +1606,7 @@ int journal_blocks_per_page(struct inode *inode)
* Simple support for retrying memory allocations. Introduced to help to
* debug different VM deadlock avoidance strategies.
*/
-void * __jbd_kmalloc (const char *where, size_t size, int flags, int retry)
+void * __jbd_kmalloc (const char *where, size_t size, gfp_t flags, int retry)
{
return kmalloc(size, flags | (retry ? __GFP_NOFAIL : 0));
}
diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c
index 49bbc2be3d7..13cb05bf604 100644
--- a/fs/jbd/transaction.c
+++ b/fs/jbd/transaction.c
@@ -1621,7 +1621,7 @@ out:
* while the data is part of a transaction. Yes?
*/
int journal_try_to_free_buffers(journal_t *journal,
- struct page *page, int unused_gfp_mask)
+ struct page *page, gfp_t unused_gfp_mask)
{
struct buffer_head *head;
struct buffer_head *bh;
diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
index eadf319bee2..68000a50ceb 100644
--- a/fs/jfs/jfs_dmap.c
+++ b/fs/jfs/jfs_dmap.c
@@ -74,7 +74,7 @@
static void dbAllocBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static void dbSplit(dmtree_t * tp, int leafno, int splitsz, int newval);
-static void dbBackSplit(dmtree_t * tp, int leafno);
+static int dbBackSplit(dmtree_t * tp, int leafno);
static int dbJoin(dmtree_t * tp, int leafno, int newval);
static void dbAdjTree(dmtree_t * tp, int leafno, int newval);
static int dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc,
@@ -305,7 +305,6 @@ int dbSync(struct inode *ipbmap)
filemap_fdatawrite(ipbmap->i_mapping);
filemap_fdatawait(ipbmap->i_mapping);
- ipbmap->i_state |= I_DIRTY;
diWriteSpecial(ipbmap, 0);
return (0);
@@ -2467,7 +2466,9 @@ dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc, int level)
* that it is at the front of a binary buddy system.
*/
if (oldval == NOFREE) {
- dbBackSplit((dmtree_t *) dcp, leafno);
+ rc = dbBackSplit((dmtree_t *) dcp, leafno);
+ if (rc)
+ return rc;
oldval = dcp->stree[ti];
}
dbSplit((dmtree_t *) dcp, leafno, dcp->budmin, newval);
@@ -2627,7 +2628,7 @@ static void dbSplit(dmtree_t * tp, int leafno, int splitsz, int newval)
*
* serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit;
*/
-static void dbBackSplit(dmtree_t * tp, int leafno)
+static int dbBackSplit(dmtree_t * tp, int leafno)
{
int budsz, bud, w, bsz, size;
int cursz;
@@ -2662,7 +2663,10 @@ static void dbBackSplit(dmtree_t * tp, int leafno)
*/
for (w = leafno, bsz = budsz;; bsz <<= 1,
w = (w < bud) ? w : bud) {
- assert(bsz < le32_to_cpu(tp->dmt_nleafs));
+ if (bsz >= le32_to_cpu(tp->dmt_nleafs)) {
+ jfs_err("JFS: block map error in dbBackSplit");
+ return -EIO;
+ }
/* determine the buddy.
*/
@@ -2681,7 +2685,11 @@ static void dbBackSplit(dmtree_t * tp, int leafno)
}
}
- assert(leaf[leafno] == size);
+ if (leaf[leafno] != size) {
+ jfs_err("JFS: wrong leaf value in dbBackSplit");
+ return -EIO;
+ }
+ return 0;
}
diff --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c
index 4021d46da7e..28201b194f5 100644
--- a/fs/jfs/jfs_imap.c
+++ b/fs/jfs/jfs_imap.c
@@ -57,6 +57,12 @@
#include "jfs_debug.h"
/*
+ * __mark_inode_dirty expects inodes to be hashed. Since we don't want
+ * special inodes in the fileset inode space, we hash them to a dummy head
+ */
+static HLIST_HEAD(aggregate_hash);
+
+/*
* imap locks
*/
/* iag free list lock */
@@ -491,6 +497,8 @@ struct inode *diReadSpecial(struct super_block *sb, ino_t inum, int secondary)
/* release the page */
release_metapage(mp);
+ hlist_add_head(&ip->i_hash, &aggregate_hash);
+
return (ip);
}
@@ -514,8 +522,6 @@ void diWriteSpecial(struct inode *ip, int secondary)
ino_t inum = ip->i_ino;
struct metapage *mp;
- ip->i_state &= ~I_DIRTY;
-
if (secondary)
address = addressPXD(&sbi->ait2) >> sbi->l2nbperpage;
else
diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c
index 13d7e3f1feb..8a53981f9f2 100644
--- a/fs/jfs/jfs_metapage.c
+++ b/fs/jfs/jfs_metapage.c
@@ -86,7 +86,7 @@ struct meta_anchor {
atomic_t io_count;
struct metapage *mp[MPS_PER_PAGE];
};
-#define mp_anchor(page) ((struct meta_anchor *)page->private)
+#define mp_anchor(page) ((struct meta_anchor *)page_private(page))
static inline struct metapage *page_to_mp(struct page *page, uint offset)
{
@@ -108,7 +108,7 @@ static inline int insert_metapage(struct page *page, struct metapage *mp)
if (!a)
return -ENOMEM;
memset(a, 0, sizeof(struct meta_anchor));
- page->private = (unsigned long)a;
+ set_page_private(page, (unsigned long)a);
SetPagePrivate(page);
kmap(page);
}
@@ -136,7 +136,7 @@ static inline void remove_metapage(struct page *page, struct metapage *mp)
a->mp[index] = NULL;
if (--a->mp_count == 0) {
kfree(a);
- page->private = 0;
+ set_page_private(page, 0);
ClearPagePrivate(page);
kunmap(page);
}
@@ -156,13 +156,13 @@ static inline void dec_io(struct page *page, void (*handler) (struct page *))
#else
static inline struct metapage *page_to_mp(struct page *page, uint offset)
{
- return PagePrivate(page) ? (struct metapage *)page->private : NULL;
+ return PagePrivate(page) ? (struct metapage *)page_private(page) : NULL;
}
static inline int insert_metapage(struct page *page, struct metapage *mp)
{
if (mp) {
- page->private = (unsigned long)mp;
+ set_page_private(page, (unsigned long)mp);
SetPagePrivate(page);
kmap(page);
}
@@ -171,7 +171,7 @@ static inline int insert_metapage(struct page *page, struct metapage *mp)
static inline void remove_metapage(struct page *page, struct metapage *mp)
{
- page->private = 0;
+ set_page_private(page, 0);
ClearPagePrivate(page);
kunmap(page);
}
@@ -198,7 +198,7 @@ static void init_once(void *foo, kmem_cache_t *cachep, unsigned long flags)
}
}
-static inline struct metapage *alloc_metapage(unsigned int gfp_mask)
+static inline struct metapage *alloc_metapage(gfp_t gfp_mask)
{
return mempool_alloc(metapage_mempool, gfp_mask);
}
@@ -395,6 +395,12 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc)
if (mp->nohomeok && !test_bit(META_forcewrite, &mp->flag)) {
redirty = 1;
+ /*
+ * Make sure this page isn't blocked indefinitely.
+ * If the journal isn't undergoing I/O, push it
+ */
+ if (mp->log && !(mp->log->cflag & logGC_PAGEOUT))
+ jfs_flush_journal(mp->log, 0);
continue;
}
@@ -534,7 +540,7 @@ add_failed:
return -EIO;
}
-static int metapage_releasepage(struct page *page, int gfp_mask)
+static int metapage_releasepage(struct page *page, gfp_t gfp_mask)
{
struct metapage *mp;
int busy = 0;
diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c
index 9b71ed2674f..b660c93c92d 100644
--- a/fs/jfs/jfs_txnmgr.c
+++ b/fs/jfs/jfs_txnmgr.c
@@ -2396,7 +2396,6 @@ static void txUpdateMap(struct tblock * tblk)
*/
if (tblk->xflag & COMMIT_CREATE) {
diUpdatePMap(ipimap, tblk->ino, FALSE, tblk);
- ipimap->i_state |= I_DIRTY;
/* update persistent block allocation map
* for the allocation of inode extent;
*/
@@ -2407,7 +2406,6 @@ static void txUpdateMap(struct tblock * tblk)
} else if (tblk->xflag & COMMIT_DELETE) {
ip = tblk->u.ip;
diUpdatePMap(ipimap, ip->i_ino, TRUE, tblk);
- ipimap->i_state |= I_DIRTY;
iput(ip);
}
}
diff --git a/fs/jfs/jfs_xtree.c b/fs/jfs/jfs_xtree.c
index a7fe2f2b969..e72f4ebb6e9 100644
--- a/fs/jfs/jfs_xtree.c
+++ b/fs/jfs/jfs_xtree.c
@@ -3516,16 +3516,10 @@ s64 xtTruncate(tid_t tid, struct inode *ip, s64 newsize, int flag)
/* process entries backward from last index */
index = le16_to_cpu(p->header.nextindex) - 1;
- if (p->header.flag & BT_INTERNAL)
- goto getChild;
-
- /*
- * leaf page
- */
- /* Since this is the rightmost leaf, and we may have already freed
- * a page that was formerly to the right, let's make sure that the
- * next pointer is zero.
+ /* Since this is the rightmost page at this level, and we may have
+ * already freed a page that was formerly to the right, let's make
+ * sure that the next pointer is zero.
*/
if (p->header.next) {
if (log)
@@ -3539,6 +3533,12 @@ s64 xtTruncate(tid_t tid, struct inode *ip, s64 newsize, int flag)
p->header.next = 0;
}
+ if (p->header.flag & BT_INTERNAL)
+ goto getChild;
+
+ /*
+ * leaf page
+ */
freed = 0;
/* does region covered by leaf page precede Teof ? */
diff --git a/fs/jfs/super.c b/fs/jfs/super.c
index 71bc34b96b2..4226af3ea91 100644
--- a/fs/jfs/super.c
+++ b/fs/jfs/super.c
@@ -442,6 +442,7 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent)
inode->i_nlink = 1;
inode->i_size = sb->s_bdev->bd_inode->i_size;
inode->i_mapping->a_ops = &jfs_metapage_aops;
+ insert_inode_hash(inode);
mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS);
sbi->direct_inode = inode;
diff --git a/fs/lockd/host.c b/fs/lockd/host.c
index 82c77df81c5..c4c8601096e 100644
--- a/fs/lockd/host.c
+++ b/fs/lockd/host.c
@@ -173,11 +173,10 @@ nlm_bind_host(struct nlm_host *host)
/* If we've already created an RPC client, check whether
* RPC rebind is required
- * Note: why keep rebinding if we're on a tcp connection?
*/
if ((clnt = host->h_rpcclnt) != NULL) {
xprt = clnt->cl_xprt;
- if (!xprt->stream && time_after_eq(jiffies, host->h_nextrebind)) {
+ if (time_after_eq(jiffies, host->h_nextrebind)) {
clnt->cl_port = 0;
host->h_nextrebind = jiffies + NLM_HOST_REBIND;
dprintk("lockd: next rebind in %ld jiffies\n",
@@ -189,7 +188,6 @@ nlm_bind_host(struct nlm_host *host)
goto forgetit;
xprt_set_timeout(&xprt->timeout, 5, nlmsvc_timeout);
- xprt->nocong = 1; /* No congestion control for NLM */
xprt->resvport = 1; /* NLM requires a reserved port */
/* Existing NLM servers accept AUTH_UNIX only */
diff --git a/fs/locks.c b/fs/locks.c
index f7daa5f4894..a1e8b224801 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -316,21 +316,22 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
/* POSIX-1996 leaves the case l->l_len < 0 undefined;
POSIX-2001 defines it. */
start += l->l_start;
- end = start + l->l_len - 1;
- if (l->l_len < 0) {
+ if (start < 0)
+ return -EINVAL;
+ fl->fl_end = OFFSET_MAX;
+ if (l->l_len > 0) {
+ end = start + l->l_len - 1;
+ fl->fl_end = end;
+ } else if (l->l_len < 0) {
end = start - 1;
+ fl->fl_end = end;
start += l->l_len;
+ if (start < 0)
+ return -EINVAL;
}
-
- if (start < 0)
- return -EINVAL;
- if (l->l_len > 0 && end < 0)
- return -EOVERFLOW;
-
fl->fl_start = start; /* we record the absolute position */
- fl->fl_end = end;
- if (l->l_len == 0)
- fl->fl_end = OFFSET_MAX;
+ if (fl->fl_end < fl->fl_start)
+ return -EOVERFLOW;
fl->fl_owner = current->files;
fl->fl_pid = current->tgid;
@@ -362,14 +363,21 @@ static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl,
return -EINVAL;
}
- if (((start += l->l_start) < 0) || (l->l_len < 0))
+ start += l->l_start;
+ if (start < 0)
return -EINVAL;
- fl->fl_end = start + l->l_len - 1;
- if (l->l_len > 0 && fl->fl_end < 0)
- return -EOVERFLOW;
+ fl->fl_end = OFFSET_MAX;
+ if (l->l_len > 0) {
+ fl->fl_end = start + l->l_len - 1;
+ } else if (l->l_len < 0) {
+ fl->fl_end = start - 1;
+ start += l->l_len;
+ if (start < 0)
+ return -EINVAL;
+ }
fl->fl_start = start; /* we record the absolute position */
- if (l->l_len == 0)
- fl->fl_end = OFFSET_MAX;
+ if (fl->fl_end < fl->fl_start)
+ return -EOVERFLOW;
fl->fl_owner = current->files;
fl->fl_pid = current->tgid;
@@ -829,12 +837,16 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request)
/* Detect adjacent or overlapping regions (if same lock type)
*/
if (request->fl_type == fl->fl_type) {
+ /* In all comparisons of start vs end, use
+ * "start - 1" rather than "end + 1". If end
+ * is OFFSET_MAX, end + 1 will become negative.
+ */
if (fl->fl_end < request->fl_start - 1)
goto next_lock;
/* If the next lock in the list has entirely bigger
* addresses than the new one, insert the lock here.
*/
- if (fl->fl_start > request->fl_end + 1)
+ if (fl->fl_start - 1 > request->fl_end)
break;
/* If we come here, the new and old lock are of the
diff --git a/fs/mbcache.c b/fs/mbcache.c
index b002a088857..298997f1747 100644
--- a/fs/mbcache.c
+++ b/fs/mbcache.c
@@ -116,7 +116,7 @@ mb_cache_indexes(struct mb_cache *cache)
* What the mbcache registers as to get shrunk dynamically.
*/
-static int mb_cache_shrink_fn(int nr_to_scan, unsigned int gfp_mask);
+static int mb_cache_shrink_fn(int nr_to_scan, gfp_t gfp_mask);
static inline int
@@ -140,7 +140,7 @@ __mb_cache_entry_unhash(struct mb_cache_entry *ce)
static inline void
-__mb_cache_entry_forget(struct mb_cache_entry *ce, int gfp_mask)
+__mb_cache_entry_forget(struct mb_cache_entry *ce, gfp_t gfp_mask)
{
struct mb_cache *cache = ce->e_cache;
@@ -193,7 +193,7 @@ forget:
* Returns the number of objects which are present in the cache.
*/
static int
-mb_cache_shrink_fn(int nr_to_scan, unsigned int gfp_mask)
+mb_cache_shrink_fn(int nr_to_scan, gfp_t gfp_mask)
{
LIST_HEAD(free_list);
struct list_head *l, *ltmp;
diff --git a/fs/namei.c b/fs/namei.c
index aa62dbda93a..aaaa8103623 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -28,6 +28,7 @@
#include <linux/syscalls.h>
#include <linux/mount.h>
#include <linux/audit.h>
+#include <linux/file.h>
#include <asm/namei.h>
#include <asm/uaccess.h>
@@ -317,6 +318,18 @@ void path_release_on_umount(struct nameidata *nd)
mntput_no_expire(nd->mnt);
}
+/**
+ * release_open_intent - free up open intent resources
+ * @nd: pointer to nameidata
+ */
+void release_open_intent(struct nameidata *nd)
+{
+ if (nd->intent.open.file->f_dentry == NULL)
+ put_filp(nd->intent.open.file);
+ else
+ fput(nd->intent.open.file);
+}
+
/*
* Internal lookup() using the new generic dcache.
* SMP-safe
@@ -750,6 +763,7 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd)
struct qstr this;
unsigned int c;
+ nd->flags |= LOOKUP_CONTINUE;
err = exec_permission_lite(inode, nd);
if (err == -EAGAIN) {
err = permission(inode, MAY_EXEC, nd);
@@ -802,7 +816,6 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd)
if (err < 0)
break;
}
- nd->flags |= LOOKUP_CONTINUE;
/* This does the actual lookups.. */
err = do_lookup(nd, &this, &next);
if (err)
@@ -1052,6 +1065,70 @@ out:
return retval;
}
+static int __path_lookup_intent_open(const char *name, unsigned int lookup_flags,
+ struct nameidata *nd, int open_flags, int create_mode)
+{
+ struct file *filp = get_empty_filp();
+ int err;
+
+ if (filp == NULL)
+ return -ENFILE;
+ nd->intent.open.file = filp;
+ nd->intent.open.flags = open_flags;
+ nd->intent.open.create_mode = create_mode;
+ err = path_lookup(name, lookup_flags|LOOKUP_OPEN, nd);
+ if (IS_ERR(nd->intent.open.file)) {
+ if (err == 0) {
+ err = PTR_ERR(nd->intent.open.file);
+ path_release(nd);
+ }
+ } else if (err != 0)
+ release_open_intent(nd);
+ return err;
+}
+
+/**
+ * path_lookup_open - lookup a file path with open intent
+ * @name: pointer to file name
+ * @lookup_flags: lookup intent flags
+ * @nd: pointer to nameidata
+ * @open_flags: open intent flags
+ */
+int path_lookup_open(const char *name, unsigned int lookup_flags,
+ struct nameidata *nd, int open_flags)
+{
+ return __path_lookup_intent_open(name, lookup_flags, nd,
+ open_flags, 0);
+}
+
+/**
+ * path_lookup_create - lookup a file path with open + create intent
+ * @name: pointer to file name
+ * @lookup_flags: lookup intent flags
+ * @nd: pointer to nameidata
+ * @open_flags: open intent flags
+ * @create_mode: create intent flags
+ */
+int path_lookup_create(const char *name, unsigned int lookup_flags,
+ struct nameidata *nd, int open_flags, int create_mode)
+{
+ return __path_lookup_intent_open(name, lookup_flags|LOOKUP_CREATE, nd,
+ open_flags, create_mode);
+}
+
+int __user_path_lookup_open(const char __user *name, unsigned int lookup_flags,
+ struct nameidata *nd, int open_flags)
+{
+ char *tmp = getname(name);
+ int err = PTR_ERR(tmp);
+
+ if (!IS_ERR(tmp)) {
+ err = __path_lookup_intent_open(tmp, lookup_flags, nd, open_flags, 0);
+ putname(tmp);
+ }
+ return err;
+}
+
/*
* Restricted form of lookup. Doesn't follow links, single-component only,
* needs parent already locked. Doesn't follow mounts.
@@ -1416,27 +1493,27 @@ int may_open(struct nameidata *nd, int acc_mode, int flag)
*/
int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd)
{
- int acc_mode, error = 0;
+ int acc_mode, error;
struct path path;
struct dentry *dir;
int count = 0;
acc_mode = ACC_MODE(flag);
+ /* O_TRUNC implies we need access checks for write permissions */
+ if (flag & O_TRUNC)
+ acc_mode |= MAY_WRITE;
+
/* Allow the LSM permission hook to distinguish append
access from general write access. */
if (flag & O_APPEND)
acc_mode |= MAY_APPEND;
- /* Fill in the open() intent data */
- nd->intent.open.flags = flag;
- nd->intent.open.create_mode = mode;
-
/*
* The simplest case - just a plain lookup.
*/
if (!(flag & O_CREAT)) {
- error = path_lookup(pathname, lookup_flags(flag)|LOOKUP_OPEN, nd);
+ error = path_lookup_open(pathname, lookup_flags(flag), nd, flag);
if (error)
return error;
goto ok;
@@ -1445,7 +1522,7 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd)
/*
* Create - we need to know the parent.
*/
- error = path_lookup(pathname, LOOKUP_PARENT|LOOKUP_OPEN|LOOKUP_CREATE, nd);
+ error = path_lookup_create(pathname, LOOKUP_PARENT, nd, flag, mode);
if (error)
return error;
@@ -1520,6 +1597,8 @@ ok:
exit_dput:
dput_path(&path, nd);
exit:
+ if (!IS_ERR(nd->intent.open.file))
+ release_open_intent(nd);
path_release(nd);
return error;
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index 4a36839f0bb..44135af9894 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -142,7 +142,7 @@ static void nfs_msync_inode(struct inode *inode)
/*
* Basic procedure for returning a delegation to the server
*/
-int nfs_inode_return_delegation(struct inode *inode)
+int __nfs_inode_return_delegation(struct inode *inode)
{
struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
struct nfs_inode *nfsi = NFS_I(inode);
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
index 3f6c45a29d6..8017846b561 100644
--- a/fs/nfs/delegation.h
+++ b/fs/nfs/delegation.h
@@ -25,7 +25,7 @@ struct nfs_delegation {
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
-int nfs_inode_return_delegation(struct inode *inode);
+int __nfs_inode_return_delegation(struct inode *inode);
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid);
struct inode *nfs_delegation_find_inode(struct nfs4_client *clp, const struct nfs_fh *fhandle);
@@ -47,11 +47,25 @@ static inline int nfs_have_delegation(struct inode *inode, int flags)
return 1;
return 0;
}
+
+static inline int nfs_inode_return_delegation(struct inode *inode)
+{
+ int err = 0;
+
+ if (NFS_I(inode)->delegation != NULL)
+ err = __nfs_inode_return_delegation(inode);
+ return err;
+}
#else
static inline int nfs_have_delegation(struct inode *inode, int flags)
{
return 0;
}
+
+static inline int nfs_inode_return_delegation(struct inode *inode)
+{
+ return 0;
+}
#endif
#endif
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 2df639f143e..8272ed3fc70 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -532,6 +532,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
my_entry.eof = 0;
my_entry.fh = &fh;
my_entry.fattr = &fattr;
+ nfs_fattr_init(&fattr);
desc->entry = &my_entry;
while(!desc->entry->eof) {
@@ -565,8 +566,6 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
}
}
unlock_kernel();
- if (desc->error < 0)
- return desc->error;
if (res < 0)
return res;
return 0;
@@ -803,6 +802,7 @@ static int nfs_dentry_delete(struct dentry *dentry)
*/
static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
{
+ nfs_inode_return_delegation(inode);
if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
lock_kernel();
inode->i_nlink--;
@@ -853,12 +853,6 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
dentry->d_op = NFS_PROTO(dir)->dentry_ops;
lock_kernel();
- /* Revalidate parent directory attribute cache */
- error = nfs_revalidate_inode(NFS_SERVER(dir), dir);
- if (error < 0) {
- res = ERR_PTR(error);
- goto out_unlock;
- }
/* If we're doing an exclusive create, optimize away the lookup */
if (nfs_is_exclusive_create(dir, nd))
@@ -916,7 +910,6 @@ static int is_atomic_open(struct inode *dir, struct nameidata *nd)
static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
struct dentry *res = NULL;
- struct inode *inode = NULL;
int error;
/* Check that we are indeed trying to open this file */
@@ -930,8 +923,10 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
dentry->d_op = NFS_PROTO(dir)->dentry_ops;
/* Let vfs_create() deal with O_EXCL */
- if (nd->intent.open.flags & O_EXCL)
- goto no_entry;
+ if (nd->intent.open.flags & O_EXCL) {
+ d_add(dentry, NULL);
+ goto out;
+ }
/* Open the file on the server */
lock_kernel();
@@ -945,32 +940,30 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
if (nd->intent.open.flags & O_CREAT) {
nfs_begin_data_update(dir);
- inode = nfs4_atomic_open(dir, dentry, nd);
+ res = nfs4_atomic_open(dir, dentry, nd);
nfs_end_data_update(dir);
} else
- inode = nfs4_atomic_open(dir, dentry, nd);
+ res = nfs4_atomic_open(dir, dentry, nd);
unlock_kernel();
- if (IS_ERR(inode)) {
- error = PTR_ERR(inode);
+ if (IS_ERR(res)) {
+ error = PTR_ERR(res);
switch (error) {
/* Make a negative dentry */
case -ENOENT:
- inode = NULL;
- break;
+ res = NULL;
+ goto out;
/* This turned out not to be a regular file */
+ case -EISDIR:
+ case -ENOTDIR:
+ goto no_open;
case -ELOOP:
if (!(nd->intent.open.flags & O_NOFOLLOW))
goto no_open;
- /* case -EISDIR: */
/* case -EINVAL: */
default:
- res = ERR_PTR(error);
goto out;
}
- }
-no_entry:
- res = d_add_unique(dentry, inode);
- if (res != NULL)
+ } else if (res != NULL)
dentry = res;
nfs_renew_times(dentry);
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
@@ -1014,7 +1007,7 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
*/
lock_kernel();
verifier = nfs_save_change_attribute(dir);
- ret = nfs4_open_revalidate(dir, dentry, openflags);
+ ret = nfs4_open_revalidate(dir, dentry, openflags, nd);
if (!ret)
nfs_set_verifier(dentry, verifier);
unlock_kernel();
@@ -1137,7 +1130,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
lock_kernel();
nfs_begin_data_update(dir);
- error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags);
+ error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, nd);
nfs_end_data_update(dir);
if (error != 0)
goto out_err;
@@ -1332,6 +1325,7 @@ static int nfs_safe_remove(struct dentry *dentry)
nfs_begin_data_update(dir);
if (inode != NULL) {
+ nfs_inode_return_delegation(inode);
nfs_begin_data_update(inode);
error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
/* The VFS may want to delete this inode */
@@ -1438,17 +1432,14 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
dentry->d_parent->d_name.name, dentry->d_name.name);
- /*
- * Drop the dentry in advance to force a new lookup.
- * Since nfs_proc_link doesn't return a file handle,
- * we can't use the existing dentry.
- */
lock_kernel();
- d_drop(dentry);
-
nfs_begin_data_update(dir);
nfs_begin_data_update(inode);
error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
+ if (error == 0) {
+ atomic_inc(&inode->i_count);
+ d_instantiate(dentry, inode);
+ }
nfs_end_data_update(inode);
nfs_end_data_update(dir);
unlock_kernel();
@@ -1512,9 +1503,11 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
*/
if (!new_inode)
goto go_ahead;
- if (S_ISDIR(new_inode->i_mode))
- goto out;
- else if (atomic_read(&new_dentry->d_count) > 2) {
+ if (S_ISDIR(new_inode->i_mode)) {
+ error = -EISDIR;
+ if (!S_ISDIR(old_inode->i_mode))
+ goto out;
+ } else if (atomic_read(&new_dentry->d_count) > 2) {
int err;
/* copy the target dentry's name */
dentry = d_alloc(new_dentry->d_parent,
@@ -1539,7 +1532,8 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
#endif
goto out;
}
- }
+ } else
+ new_inode->i_nlink--;
go_ahead:
/*
@@ -1549,6 +1543,7 @@ go_ahead:
nfs_wb_all(old_inode);
shrink_dcache_parent(old_dentry);
}
+ nfs_inode_return_delegation(old_inode);
if (new_inode)
d_delete(new_dentry);
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 6bdcfa95de9..57d3e77d97e 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -205,8 +205,8 @@ nfs_file_flush(struct file *file)
if (!status) {
status = ctx->error;
ctx->error = 0;
- if (!status && !nfs_have_delegation(inode, FMODE_READ))
- __nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ if (!status)
+ nfs_revalidate_inode(NFS_SERVER(inode), inode);
}
unlock_kernel();
return status;
@@ -376,22 +376,31 @@ out_swapfile:
static int do_getlk(struct file *filp, int cmd, struct file_lock *fl)
{
+ struct file_lock *cfl;
struct inode *inode = filp->f_mapping->host;
int status = 0;
lock_kernel();
- /* Use local locking if mounted with "-onolock" */
- if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM))
- status = NFS_PROTO(inode)->lock(filp, cmd, fl);
- else {
- struct file_lock *cfl = posix_test_lock(filp, fl);
-
- fl->fl_type = F_UNLCK;
- if (cfl != NULL)
- memcpy(fl, cfl, sizeof(*fl));
+ /* Try local locking first */
+ cfl = posix_test_lock(filp, fl);
+ if (cfl != NULL) {
+ locks_copy_lock(fl, cfl);
+ goto out;
}
+
+ if (nfs_have_delegation(inode, FMODE_READ))
+ goto out_noconflict;
+
+ if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)
+ goto out_noconflict;
+
+ status = NFS_PROTO(inode)->lock(filp, cmd, fl);
+out:
unlock_kernel();
return status;
+out_noconflict:
+ fl->fl_type = F_UNLCK;
+ goto out;
}
static int do_vfs_lock(struct file *file, struct file_lock *fl)
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index d4eadeea128..f2781ca4276 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -358,6 +358,35 @@ out_no_root:
return no_root_error;
}
+static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, unsigned int timeo, unsigned int retrans)
+{
+ to->to_initval = timeo * HZ / 10;
+ to->to_retries = retrans;
+ if (!to->to_retries)
+ to->to_retries = 2;
+
+ switch (proto) {
+ case IPPROTO_TCP:
+ if (!to->to_initval)
+ to->to_initval = 60 * HZ;
+ if (to->to_initval > NFS_MAX_TCP_TIMEOUT)
+ to->to_initval = NFS_MAX_TCP_TIMEOUT;
+ to->to_increment = to->to_initval;
+ to->to_maxval = to->to_initval + (to->to_increment * to->to_retries);
+ to->to_exponential = 0;
+ break;
+ case IPPROTO_UDP:
+ default:
+ if (!to->to_initval)
+ to->to_initval = 11 * HZ / 10;
+ if (to->to_initval > NFS_MAX_UDP_TIMEOUT)
+ to->to_initval = NFS_MAX_UDP_TIMEOUT;
+ to->to_maxval = NFS_MAX_UDP_TIMEOUT;
+ to->to_exponential = 1;
+ break;
+ }
+}
+
/*
* Create an RPC client handle.
*/
@@ -367,22 +396,12 @@ nfs_create_client(struct nfs_server *server, const struct nfs_mount_data *data)
struct rpc_timeout timeparms;
struct rpc_xprt *xprt = NULL;
struct rpc_clnt *clnt = NULL;
- int tcp = (data->flags & NFS_MOUNT_TCP);
-
- /* Initialize timeout values */
- timeparms.to_initval = data->timeo * HZ / 10;
- timeparms.to_retries = data->retrans;
- timeparms.to_maxval = tcp ? RPC_MAX_TCP_TIMEOUT : RPC_MAX_UDP_TIMEOUT;
- timeparms.to_exponential = 1;
+ int proto = (data->flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP;
- if (!timeparms.to_initval)
- timeparms.to_initval = (tcp ? 600 : 11) * HZ / 10;
- if (!timeparms.to_retries)
- timeparms.to_retries = 5;
+ nfs_init_timeout_values(&timeparms, proto, data->timeo, data->retrans);
/* create transport and client */
- xprt = xprt_create_proto(tcp ? IPPROTO_TCP : IPPROTO_UDP,
- &server->addr, &timeparms);
+ xprt = xprt_create_proto(proto, &server->addr, &timeparms);
if (IS_ERR(xprt)) {
dprintk("%s: cannot create RPC transport. Error = %ld\n",
__FUNCTION__, PTR_ERR(xprt));
@@ -576,7 +595,6 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
{ NFS_MOUNT_SOFT, ",soft", ",hard" },
{ NFS_MOUNT_INTR, ",intr", "" },
{ NFS_MOUNT_POSIX, ",posix", "" },
- { NFS_MOUNT_TCP, ",tcp", ",udp" },
{ NFS_MOUNT_NOCTO, ",nocto", "" },
{ NFS_MOUNT_NOAC, ",noac", "" },
{ NFS_MOUNT_NONLM, ",nolock", ",lock" },
@@ -585,6 +603,8 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
};
struct proc_nfs_info *nfs_infop;
struct nfs_server *nfss = NFS_SB(mnt->mnt_sb);
+ char buf[12];
+ char *proto;
seq_printf(m, ",v%d", nfss->rpc_ops->version);
seq_printf(m, ",rsize=%d", nfss->rsize);
@@ -603,6 +623,18 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
else
seq_puts(m, nfs_infop->nostr);
}
+ switch (nfss->client->cl_xprt->prot) {
+ case IPPROTO_TCP:
+ proto = "tcp";
+ break;
+ case IPPROTO_UDP:
+ proto = "udp";
+ break;
+ default:
+ snprintf(buf, sizeof(buf), "%u", nfss->client->cl_xprt->prot);
+ proto = buf;
+ }
+ seq_printf(m, ",proto=%s", proto);
seq_puts(m, ",addr=");
seq_escape(m, nfss->hostname, " \t\n\\");
return 0;
@@ -753,7 +785,8 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
else
init_special_inode(inode, inode->i_mode, fattr->rdev);
- nfsi->read_cache_jiffies = fattr->timestamp;
+ nfsi->read_cache_jiffies = fattr->time_start;
+ nfsi->last_updated = jiffies;
inode->i_atime = fattr->atime;
inode->i_mtime = fattr->mtime;
inode->i_ctime = fattr->ctime;
@@ -821,6 +854,11 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
filemap_fdatawait(inode->i_mapping);
nfs_wb_all(inode);
}
+ /*
+ * Return any delegations if we're going to change ACLs
+ */
+ if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0)
+ nfs_inode_return_delegation(inode);
error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr);
if (error == 0)
nfs_refresh_inode(inode, &fattr);
@@ -1019,15 +1057,11 @@ int nfs_open(struct inode *inode, struct file *filp)
ctx->mode = filp->f_mode;
nfs_file_set_open_context(filp, ctx);
put_nfs_open_context(ctx);
- if ((filp->f_mode & FMODE_WRITE) != 0)
- nfs_begin_data_update(inode);
return 0;
}
int nfs_release(struct inode *inode, struct file *filp)
{
- if ((filp->f_mode & FMODE_WRITE) != 0)
- nfs_end_data_update(inode);
nfs_file_clear_open_context(filp);
return 0;
}
@@ -1083,14 +1117,15 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
goto out;
}
+ spin_lock(&inode->i_lock);
status = nfs_update_inode(inode, &fattr, verifier);
if (status) {
+ spin_unlock(&inode->i_lock);
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n",
inode->i_sb->s_id,
(long long)NFS_FILEID(inode), status);
goto out;
}
- spin_lock(&inode->i_lock);
cache_validity = nfsi->cache_validity;
nfsi->cache_validity &= ~NFS_INO_REVAL_PAGECACHE;
@@ -1098,7 +1133,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
* We may need to keep the attributes marked as invalid if
* we raced with nfs_end_attr_update().
*/
- if (verifier == nfsi->cache_change_attribute)
+ if (time_after_eq(verifier, nfsi->cache_change_attribute))
nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME);
spin_unlock(&inode->i_lock);
@@ -1165,7 +1200,7 @@ void nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
if (S_ISDIR(inode->i_mode)) {
memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
/* This ensures we revalidate child dentries */
- nfsi->cache_change_attribute++;
+ nfsi->cache_change_attribute = jiffies;
}
spin_unlock(&inode->i_lock);
@@ -1197,20 +1232,19 @@ void nfs_end_data_update(struct inode *inode)
struct nfs_inode *nfsi = NFS_I(inode);
if (!nfs_have_delegation(inode, FMODE_READ)) {
- /* Mark the attribute cache for revalidation */
- spin_lock(&inode->i_lock);
- nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
- /* Directories and symlinks: invalidate page cache too */
- if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
+ /* Directories and symlinks: invalidate page cache */
+ if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) {
+ spin_lock(&inode->i_lock);
nfsi->cache_validity |= NFS_INO_INVALID_DATA;
- spin_unlock(&inode->i_lock);
+ spin_unlock(&inode->i_lock);
+ }
}
- nfsi->cache_change_attribute ++;
+ nfsi->cache_change_attribute = jiffies;
atomic_dec(&nfsi->data_updates);
}
/**
- * nfs_refresh_inode - verify consistency of the inode attribute cache
+ * nfs_check_inode_attributes - verify consistency of the inode attribute cache
* @inode - pointer to inode
* @fattr - updated attributes
*
@@ -1218,13 +1252,12 @@ void nfs_end_data_update(struct inode *inode)
* so that fattr carries weak cache consistency data, then it may
* also update the ctime/mtime/change_attribute.
*/
-int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
+static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fattr)
{
struct nfs_inode *nfsi = NFS_I(inode);
loff_t cur_size, new_isize;
int data_unstable;
- spin_lock(&inode->i_lock);
/* Are we in the process of updating data on the server? */
data_unstable = nfs_caches_unstable(inode);
@@ -1288,11 +1321,67 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
if (!timespec_equal(&inode->i_atime, &fattr->atime))
nfsi->cache_validity |= NFS_INO_INVALID_ATIME;
- nfsi->read_cache_jiffies = fattr->timestamp;
- spin_unlock(&inode->i_lock);
+ nfsi->read_cache_jiffies = fattr->time_start;
return 0;
}
+/**
+ * nfs_refresh_inode - try to update the inode attribute cache
+ * @inode - pointer to inode
+ * @fattr - updated attributes
+ *
+ * Check that an RPC call that returned attributes has not overlapped with
+ * other recent updates of the inode metadata, then decide whether it is
+ * safe to do a full update of the inode attributes, or whether just to
+ * call nfs_check_inode_attributes.
+ */
+int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
+{
+ struct nfs_inode *nfsi = NFS_I(inode);
+ int status;
+
+ if ((fattr->valid & NFS_ATTR_FATTR) == 0)
+ return 0;
+ spin_lock(&inode->i_lock);
+ nfsi->cache_validity &= ~NFS_INO_REVAL_PAGECACHE;
+ if (nfs_verify_change_attribute(inode, fattr->time_start))
+ nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME);
+ if (time_after(fattr->time_start, nfsi->last_updated))
+ status = nfs_update_inode(inode, fattr, fattr->time_start);
+ else
+ status = nfs_check_inode_attributes(inode, fattr);
+
+ spin_unlock(&inode->i_lock);
+ return status;
+}
+
+/**
+ * nfs_post_op_update_inode - try to update the inode attribute cache
+ * @inode - pointer to inode
+ * @fattr - updated attributes
+ *
+ * After an operation that has changed the inode metadata, mark the
+ * attribute cache as being invalid, then try to update it.
+ */
+int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr)
+{
+ struct nfs_inode *nfsi = NFS_I(inode);
+ int status = 0;
+
+ spin_lock(&inode->i_lock);
+ if (unlikely((fattr->valid & NFS_ATTR_FATTR) == 0)) {
+ nfsi->cache_validity |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS;
+ goto out;
+ }
+ status = nfs_update_inode(inode, fattr, fattr->time_start);
+ if (time_after_eq(fattr->time_start, nfsi->cache_change_attribute))
+ nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME|NFS_INO_REVAL_PAGECACHE);
+ nfsi->cache_change_attribute = jiffies;
+out:
+ spin_unlock(&inode->i_lock);
+ return status;
+}
+
/*
* Many nfs protocol calls return the new file attributes after
* an operation. Here we update the inode to reflect the state
@@ -1328,20 +1417,17 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign
goto out_err;
}
- spin_lock(&inode->i_lock);
-
/*
* Make sure the inode's type hasn't changed.
*/
- if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) {
- spin_unlock(&inode->i_lock);
+ if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
goto out_changed;
- }
/*
* Update the read time so we don't revalidate too often.
*/
- nfsi->read_cache_jiffies = fattr->timestamp;
+ nfsi->read_cache_jiffies = fattr->time_start;
+ nfsi->last_updated = jiffies;
/* Are we racing with known updates of the metadata on the server? */
data_unstable = ! (nfs_verify_change_attribute(inode, verifier) ||
@@ -1354,7 +1440,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign
/* Do we perhaps have any outstanding writes? */
if (nfsi->npages == 0) {
/* No, but did we race with nfs_end_data_update()? */
- if (verifier == nfsi->cache_change_attribute) {
+ if (time_after_eq(verifier, nfsi->cache_change_attribute)) {
inode->i_size = new_isize;
invalid |= NFS_INO_INVALID_DATA;
}
@@ -1430,7 +1516,6 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign
if (!nfs_have_delegation(inode, FMODE_READ))
nfsi->cache_validity |= invalid;
- spin_unlock(&inode->i_lock);
return 0;
out_changed:
/*
@@ -1639,8 +1724,7 @@ static void nfs4_clear_inode(struct inode *inode)
struct nfs_inode *nfsi = NFS_I(inode);
/* If we are holding a delegation, return it! */
- if (nfsi->delegation != NULL)
- nfs_inode_return_delegation(inode);
+ nfs_inode_return_delegation(inode);
/* First call standard NFS clear_inode() code */
nfs_clear_inode(inode);
/* Now clear out any remaining state */
@@ -1669,7 +1753,7 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
struct rpc_clnt *clnt = NULL;
struct rpc_timeout timeparms;
rpc_authflavor_t authflavour;
- int proto, err = -EIO;
+ int err = -EIO;
sb->s_blocksize_bits = 0;
sb->s_blocksize = 0;
@@ -1687,30 +1771,8 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
server->acdirmax = data->acdirmax*HZ;
server->rpc_ops = &nfs_v4_clientops;
- /* Initialize timeout values */
-
- timeparms.to_initval = data->timeo * HZ / 10;
- timeparms.to_retries = data->retrans;
- timeparms.to_exponential = 1;
- if (!timeparms.to_retries)
- timeparms.to_retries = 5;
- proto = data->proto;
- /* Which IP protocol do we use? */
- switch (proto) {
- case IPPROTO_TCP:
- timeparms.to_maxval = RPC_MAX_TCP_TIMEOUT;
- if (!timeparms.to_initval)
- timeparms.to_initval = 600 * HZ / 10;
- break;
- case IPPROTO_UDP:
- timeparms.to_maxval = RPC_MAX_UDP_TIMEOUT;
- if (!timeparms.to_initval)
- timeparms.to_initval = 11 * HZ / 10;
- break;
- default:
- return -EINVAL;
- }
+ nfs_init_timeout_values(&timeparms, data->proto, data->timeo, data->retrans);
clp = nfs4_get_client(&server->addr.sin_addr);
if (!clp) {
@@ -1735,7 +1797,7 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
down_write(&clp->cl_sem);
if (IS_ERR(clp->cl_rpcclient)) {
- xprt = xprt_create_proto(proto, &server->addr, &timeparms);
+ xprt = xprt_create_proto(data->proto, &server->addr, &timeparms);
if (IS_ERR(xprt)) {
up_write(&clp->cl_sem);
err = PTR_ERR(xprt);
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c
index d91b69044a4..59049e864ca 100644
--- a/fs/nfs/nfs2xdr.c
+++ b/fs/nfs/nfs2xdr.c
@@ -143,7 +143,6 @@ xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
fattr->rdev = 0;
}
- fattr->timestamp = jiffies;
return p;
}
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index edc95514046..92c870d19cc 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -78,7 +78,7 @@ nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
int status;
dprintk("%s: call fsinfo\n", __FUNCTION__);
- info->fattr->valid = 0;
+ nfs_fattr_init(info->fattr);
status = rpc_call(server->client_sys, NFS3PROC_FSINFO, fhandle, info, 0);
dprintk("%s: reply fsinfo: %d\n", __FUNCTION__, status);
if (!(info->fattr->valid & NFS_ATTR_FATTR)) {
@@ -98,7 +98,7 @@ nfs3_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
int status;
dprintk("NFS call getattr\n");
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
status = rpc_call(server->client, NFS3PROC_GETATTR,
fhandle, fattr, 0);
dprintk("NFS reply getattr: %d\n", status);
@@ -117,7 +117,7 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
int status;
dprintk("NFS call setattr\n");
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
status = rpc_call(NFS_CLIENT(inode), NFS3PROC_SETATTR, &arg, fattr, 0);
if (status == 0)
nfs_setattr_update_inode(inode, sattr);
@@ -143,8 +143,8 @@ nfs3_proc_lookup(struct inode *dir, struct qstr *name,
int status;
dprintk("NFS call lookup %s\n", name->name);
- dir_attr.valid = 0;
- fattr->valid = 0;
+ nfs_fattr_init(&dir_attr);
+ nfs_fattr_init(fattr);
status = rpc_call(NFS_CLIENT(dir), NFS3PROC_LOOKUP, &arg, &res, 0);
if (status >= 0 && !(fattr->valid & NFS_ATTR_FATTR))
status = rpc_call(NFS_CLIENT(dir), NFS3PROC_GETATTR,
@@ -174,7 +174,6 @@ static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry)
int status;
dprintk("NFS call access\n");
- fattr.valid = 0;
if (mode & MAY_READ)
arg.access |= NFS3_ACCESS_READ;
@@ -189,6 +188,7 @@ static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry)
if (mode & MAY_EXEC)
arg.access |= NFS3_ACCESS_EXECUTE;
}
+ nfs_fattr_init(&fattr);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
nfs_refresh_inode(inode, &fattr);
if (status == 0) {
@@ -217,7 +217,7 @@ static int nfs3_proc_readlink(struct inode *inode, struct page *page,
int status;
dprintk("NFS call readlink\n");
- fattr.valid = 0;
+ nfs_fattr_init(&fattr);
status = rpc_call(NFS_CLIENT(inode), NFS3PROC_READLINK,
&args, &fattr, 0);
nfs_refresh_inode(inode, &fattr);
@@ -240,7 +240,7 @@ static int nfs3_proc_read(struct nfs_read_data *rdata)
dprintk("NFS call read %d @ %Ld\n", rdata->args.count,
(long long) rdata->args.offset);
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0)
nfs_refresh_inode(inode, fattr);
@@ -263,10 +263,10 @@ static int nfs3_proc_write(struct nfs_write_data *wdata)
dprintk("NFS call write %d @ %Ld\n", wdata->args.count,
(long long) wdata->args.offset);
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags);
if (status >= 0)
- nfs_refresh_inode(inode, fattr);
+ nfs_post_op_update_inode(inode, fattr);
dprintk("NFS reply write: %d\n", status);
return status < 0? status : wdata->res.count;
}
@@ -285,10 +285,10 @@ static int nfs3_proc_commit(struct nfs_write_data *cdata)
dprintk("NFS call commit %d @ %Ld\n", cdata->args.count,
(long long) cdata->args.offset);
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (status >= 0)
- nfs_refresh_inode(inode, fattr);
+ nfs_post_op_update_inode(inode, fattr);
dprintk("NFS reply commit: %d\n", status);
return status;
}
@@ -299,7 +299,7 @@ static int nfs3_proc_commit(struct nfs_write_data *cdata)
*/
static int
nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
- int flags)
+ int flags, struct nameidata *nd)
{
struct nfs_fh fhandle;
struct nfs_fattr fattr;
@@ -329,10 +329,10 @@ nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
sattr->ia_mode &= ~current->fs->umask;
again:
- dir_attr.valid = 0;
- fattr.valid = 0;
+ nfs_fattr_init(&dir_attr);
+ nfs_fattr_init(&fattr);
status = rpc_call(NFS_CLIENT(dir), NFS3PROC_CREATE, &arg, &res, 0);
- nfs_refresh_inode(dir, &dir_attr);
+ nfs_post_op_update_inode(dir, &dir_attr);
/* If the server doesn't support the exclusive creation semantics,
* try again with simple 'guarded' mode. */
@@ -401,9 +401,9 @@ nfs3_proc_remove(struct inode *dir, struct qstr *name)
int status;
dprintk("NFS call remove %s\n", name->name);
- dir_attr.valid = 0;
+ nfs_fattr_init(&dir_attr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
- nfs_refresh_inode(dir, &dir_attr);
+ nfs_post_op_update_inode(dir, &dir_attr);
dprintk("NFS reply remove: %d\n", status);
return status;
}
@@ -422,7 +422,7 @@ nfs3_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr
ptr->arg.fh = NFS_FH(dir->d_inode);
ptr->arg.name = name->name;
ptr->arg.len = name->len;
- ptr->res.valid = 0;
+ nfs_fattr_init(&ptr->res);
msg->rpc_proc = &nfs3_procedures[NFS3PROC_REMOVE];
msg->rpc_argp = &ptr->arg;
msg->rpc_resp = &ptr->res;
@@ -439,7 +439,7 @@ nfs3_proc_unlink_done(struct dentry *dir, struct rpc_task *task)
return 1;
if (msg->rpc_argp) {
dir_attr = (struct nfs_fattr*)msg->rpc_resp;
- nfs_refresh_inode(dir->d_inode, dir_attr);
+ nfs_post_op_update_inode(dir->d_inode, dir_attr);
kfree(msg->rpc_argp);
}
return 0;
@@ -465,11 +465,11 @@ nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name,
int status;
dprintk("NFS call rename %s -> %s\n", old_name->name, new_name->name);
- old_dir_attr.valid = 0;
- new_dir_attr.valid = 0;
+ nfs_fattr_init(&old_dir_attr);
+ nfs_fattr_init(&new_dir_attr);
status = rpc_call(NFS_CLIENT(old_dir), NFS3PROC_RENAME, &arg, &res, 0);
- nfs_refresh_inode(old_dir, &old_dir_attr);
- nfs_refresh_inode(new_dir, &new_dir_attr);
+ nfs_post_op_update_inode(old_dir, &old_dir_attr);
+ nfs_post_op_update_inode(new_dir, &new_dir_attr);
dprintk("NFS reply rename: %d\n", status);
return status;
}
@@ -491,11 +491,11 @@ nfs3_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
int status;
dprintk("NFS call link %s\n", name->name);
- dir_attr.valid = 0;
- fattr.valid = 0;
+ nfs_fattr_init(&dir_attr);
+ nfs_fattr_init(&fattr);
status = rpc_call(NFS_CLIENT(inode), NFS3PROC_LINK, &arg, &res, 0);
- nfs_refresh_inode(dir, &dir_attr);
- nfs_refresh_inode(inode, &fattr);
+ nfs_post_op_update_inode(dir, &dir_attr);
+ nfs_post_op_update_inode(inode, &fattr);
dprintk("NFS reply link: %d\n", status);
return status;
}
@@ -524,10 +524,10 @@ nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
if (path->len > NFS3_MAXPATHLEN)
return -ENAMETOOLONG;
dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
- dir_attr.valid = 0;
- fattr->valid = 0;
+ nfs_fattr_init(&dir_attr);
+ nfs_fattr_init(fattr);
status = rpc_call(NFS_CLIENT(dir), NFS3PROC_SYMLINK, &arg, &res, 0);
- nfs_refresh_inode(dir, &dir_attr);
+ nfs_post_op_update_inode(dir, &dir_attr);
dprintk("NFS reply symlink: %d\n", status);
return status;
}
@@ -552,13 +552,13 @@ nfs3_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr)
int status;
dprintk("NFS call mkdir %s\n", dentry->d_name.name);
- dir_attr.valid = 0;
- fattr.valid = 0;
sattr->ia_mode &= ~current->fs->umask;
+ nfs_fattr_init(&dir_attr);
+ nfs_fattr_init(&fattr);
status = rpc_call(NFS_CLIENT(dir), NFS3PROC_MKDIR, &arg, &res, 0);
- nfs_refresh_inode(dir, &dir_attr);
+ nfs_post_op_update_inode(dir, &dir_attr);
if (status != 0)
goto out;
status = nfs_instantiate(dentry, &fhandle, &fattr);
@@ -582,9 +582,9 @@ nfs3_proc_rmdir(struct inode *dir, struct qstr *name)
int status;
dprintk("NFS call rmdir %s\n", name->name);
- dir_attr.valid = 0;
+ nfs_fattr_init(&dir_attr);
status = rpc_call(NFS_CLIENT(dir), NFS3PROC_RMDIR, &arg, &dir_attr, 0);
- nfs_refresh_inode(dir, &dir_attr);
+ nfs_post_op_update_inode(dir, &dir_attr);
dprintk("NFS reply rmdir: %d\n", status);
return status;
}
@@ -634,7 +634,7 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
dprintk("NFS call readdir%s %d\n",
plus? "plus" : "", (unsigned int) cookie);
- dir_attr.valid = 0;
+ nfs_fattr_init(&dir_attr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
nfs_refresh_inode(dir, &dir_attr);
dprintk("NFS reply readdir: %d\n", status);
@@ -676,10 +676,10 @@ nfs3_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
sattr->ia_mode &= ~current->fs->umask;
- dir_attr.valid = 0;
- fattr.valid = 0;
+ nfs_fattr_init(&dir_attr);
+ nfs_fattr_init(&fattr);
status = rpc_call(NFS_CLIENT(dir), NFS3PROC_MKNOD, &arg, &res, 0);
- nfs_refresh_inode(dir, &dir_attr);
+ nfs_post_op_update_inode(dir, &dir_attr);
if (status != 0)
goto out;
status = nfs_instantiate(dentry, &fh, &fattr);
@@ -698,7 +698,7 @@ nfs3_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
int status;
dprintk("NFS call fsstat\n");
- stat->fattr->valid = 0;
+ nfs_fattr_init(stat->fattr);
status = rpc_call(server->client, NFS3PROC_FSSTAT, fhandle, stat, 0);
dprintk("NFS reply statfs: %d\n", status);
return status;
@@ -711,7 +711,7 @@ nfs3_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,
int status;
dprintk("NFS call fsinfo\n");
- info->fattr->valid = 0;
+ nfs_fattr_init(info->fattr);
status = rpc_call(server->client_sys, NFS3PROC_FSINFO, fhandle, info, 0);
dprintk("NFS reply fsinfo: %d\n", status);
return status;
@@ -724,7 +724,7 @@ nfs3_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
int status;
dprintk("NFS call pathconf\n");
- info->fattr->valid = 0;
+ nfs_fattr_init(info->fattr);
status = rpc_call(server->client, NFS3PROC_PATHCONF, fhandle, info, 0);
dprintk("NFS reply pathconf: %d\n", status);
return status;
@@ -735,7 +735,7 @@ extern u32 *nfs3_decode_dirent(u32 *, struct nfs_entry *, int);
static void
nfs3_read_done(struct rpc_task *task)
{
- struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata;
+ struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata;
if (nfs3_async_handle_jukebox(task))
return;
@@ -775,7 +775,7 @@ nfs3_write_done(struct rpc_task *task)
return;
data = (struct nfs_write_data *)task->tk_calldata;
if (task->tk_status >= 0)
- nfs_refresh_inode(data->inode, data->res.fattr);
+ nfs_post_op_update_inode(data->inode, data->res.fattr);
nfs_writeback_done(task);
}
@@ -819,7 +819,7 @@ nfs3_commit_done(struct rpc_task *task)
return;
data = (struct nfs_write_data *)task->tk_calldata;
if (task->tk_status >= 0)
- nfs_refresh_inode(data->inode, data->res.fattr);
+ nfs_post_op_update_inode(data->inode, data->res.fattr);
nfs_commit_done(task);
}
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index db4a904810a..0498bd36602 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -174,7 +174,6 @@ xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
/* Update the mode bits */
fattr->valid |= (NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3);
- fattr->timestamp = jiffies;
return p;
}
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index ec1a22d7b87..78a53f5a9f1 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -93,25 +93,50 @@ struct nfs4_client {
};
/*
+ * struct rpc_sequence ensures that RPC calls are sent in the exact
+ * order that they appear on the list.
+ */
+struct rpc_sequence {
+ struct rpc_wait_queue wait; /* RPC call delay queue */
+ spinlock_t lock; /* Protects the list */
+ struct list_head list; /* Defines sequence of RPC calls */
+};
+
+#define NFS_SEQID_CONFIRMED 1
+struct nfs_seqid_counter {
+ struct rpc_sequence *sequence;
+ int flags;
+ u32 counter;
+};
+
+struct nfs_seqid {
+ struct nfs_seqid_counter *sequence;
+ struct list_head list;
+};
+
+static inline void nfs_confirm_seqid(struct nfs_seqid_counter *seqid, int status)
+{
+ if (seqid_mutating_err(-status))
+ seqid->flags |= NFS_SEQID_CONFIRMED;
+}
+
+/*
* NFS4 state_owners and lock_owners are simply labels for ordered
* sequences of RPC calls. Their sole purpose is to provide once-only
* semantics by allowing the server to identify replayed requests.
- *
- * The ->so_sema is held during all state_owner seqid-mutating operations:
- * OPEN, OPEN_DOWNGRADE, and CLOSE. Its purpose is to properly serialize
- * so_seqid.
*/
struct nfs4_state_owner {
+ spinlock_t so_lock;
struct list_head so_list; /* per-clientid list of state_owners */
struct nfs4_client *so_client;
u32 so_id; /* 32-bit identifier, unique */
- struct semaphore so_sema;
- u32 so_seqid; /* protected by so_sema */
atomic_t so_count;
struct rpc_cred *so_cred; /* Associated cred */
struct list_head so_states;
struct list_head so_delegations;
+ struct nfs_seqid_counter so_seqid;
+ struct rpc_sequence so_sequence;
};
/*
@@ -132,7 +157,7 @@ struct nfs4_lock_state {
fl_owner_t ls_owner; /* POSIX lock owner */
#define NFS_LOCK_INITIALIZED 1
int ls_flags;
- u32 ls_seqid;
+ struct nfs_seqid_counter ls_seqid;
u32 ls_id;
nfs4_stateid ls_stateid;
atomic_t ls_count;
@@ -153,7 +178,6 @@ struct nfs4_state {
struct inode *inode; /* Pointer to the inode */
unsigned long flags; /* Do we hold any locks? */
- struct semaphore lock_sema; /* Serializes file locking operations */
spinlock_t state_lock; /* Protects the lock_states list */
nfs4_stateid stateid;
@@ -191,8 +215,8 @@ extern int nfs4_proc_setclientid_confirm(struct nfs4_client *);
extern int nfs4_proc_async_renew(struct nfs4_client *);
extern int nfs4_proc_renew(struct nfs4_client *);
extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode);
-extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
-extern int nfs4_open_revalidate(struct inode *, struct dentry *, int);
+extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
+extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *);
extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops;
extern struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops;
@@ -224,12 +248,17 @@ extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state
extern void nfs4_put_open_state(struct nfs4_state *);
extern void nfs4_close_state(struct nfs4_state *, mode_t);
extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mode_t mode);
-extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp);
extern void nfs4_schedule_state_recovery(struct nfs4_client *);
+extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl);
-extern void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *ls);
extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t);
+extern struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter);
+extern int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task);
+extern void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid);
+extern void nfs_increment_lock_seqid(int status, struct nfs_seqid *seqid);
+extern void nfs_free_seqid(struct nfs_seqid *seqid);
+
extern const nfs4_stateid zero_stateid;
/* nfs4xdr.c */
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 9701ca8c942..933e13b383f 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -47,6 +47,7 @@
#include <linux/nfs_page.h>
#include <linux/smp_lock.h>
#include <linux/namei.h>
+#include <linux/mount.h>
#include "nfs4_fs.h"
#include "delegation.h"
@@ -56,10 +57,11 @@
#define NFS4_POLL_RETRY_MIN (1*HZ)
#define NFS4_POLL_RETRY_MAX (15*HZ)
+static int _nfs4_proc_open_confirm(struct rpc_clnt *clnt, const struct nfs_fh *fh, struct nfs4_state_owner *sp, nfs4_stateid *stateid, struct nfs_seqid *seqid);
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
-static int nfs4_async_handle_error(struct rpc_task *, struct nfs_server *);
+static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *);
static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry);
-static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_exception *exception);
+static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception);
extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus);
extern struct rpc_procinfo nfs4_procedures[];
@@ -185,8 +187,26 @@ static void update_changeattr(struct inode *inode, struct nfs4_change_info *cinf
{
struct nfs_inode *nfsi = NFS_I(inode);
+ spin_lock(&inode->i_lock);
+ nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
if (cinfo->before == nfsi->change_attr && cinfo->atomic)
nfsi->change_attr = cinfo->after;
+ spin_unlock(&inode->i_lock);
+}
+
+/* Helper for asynchronous RPC calls */
+static int nfs4_call_async(struct rpc_clnt *clnt, rpc_action tk_begin,
+ rpc_action tk_exit, void *calldata)
+{
+ struct rpc_task *task;
+
+ if (!(task = rpc_new_task(clnt, tk_exit, RPC_TASK_ASYNC)))
+ return -ENOMEM;
+
+ task->tk_calldata = calldata;
+ task->tk_action = tk_begin;
+ rpc_execute(task);
+ return 0;
}
static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags)
@@ -195,6 +215,7 @@ static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid,
open_flags &= (FMODE_READ|FMODE_WRITE);
/* Protect against nfs4_find_state() */
+ spin_lock(&state->owner->so_lock);
spin_lock(&inode->i_lock);
state->state |= open_flags;
/* NB! List reordering - see the reclaim code for why. */
@@ -204,12 +225,12 @@ static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid,
state->nreaders++;
memcpy(&state->stateid, stateid, sizeof(state->stateid));
spin_unlock(&inode->i_lock);
+ spin_unlock(&state->owner->so_lock);
}
/*
* OPEN_RECLAIM:
* reclaim state on the server after a reboot.
- * Assumes caller is holding the sp->so_sem
*/
static int _nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state)
{
@@ -218,7 +239,6 @@ static int _nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *st
struct nfs_delegation *delegation = NFS_I(inode)->delegation;
struct nfs_openargs o_arg = {
.fh = NFS_FH(inode),
- .seqid = sp->so_seqid,
.id = sp->so_id,
.open_flags = state->state,
.clientid = server->nfs4_state->cl_clientid,
@@ -245,8 +265,13 @@ static int _nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *st
}
o_arg.u.delegation_type = delegation->type;
}
+ o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid);
+ if (o_arg.seqid == NULL)
+ return -ENOMEM;
status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR);
- nfs4_increment_seqid(status, sp);
+ /* Confirm the sequence as being established */
+ nfs_confirm_seqid(&sp->so_seqid, status);
+ nfs_increment_open_seqid(status, o_arg.seqid);
if (status == 0) {
memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid));
if (o_res.delegation_type != 0) {
@@ -256,6 +281,7 @@ static int _nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *st
nfs_async_inode_return_delegation(inode, &o_res.stateid);
}
}
+ nfs_free_seqid(o_arg.seqid);
clear_bit(NFS_DELEGATED_STATE, &state->flags);
/* Ensure we update the inode attributes */
NFS_CACHEINV(inode);
@@ -302,23 +328,35 @@ static int _nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state
};
int status = 0;
- down(&sp->so_sema);
if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
goto out;
if (state->state == 0)
goto out;
- arg.seqid = sp->so_seqid;
+ arg.seqid = nfs_alloc_seqid(&sp->so_seqid);
+ status = -ENOMEM;
+ if (arg.seqid == NULL)
+ goto out;
arg.open_flags = state->state;
memcpy(arg.u.delegation.data, state->stateid.data, sizeof(arg.u.delegation.data));
status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR);
- nfs4_increment_seqid(status, sp);
+ nfs_increment_open_seqid(status, arg.seqid);
+ if (status != 0)
+ goto out_free;
+ if(res.rflags & NFS4_OPEN_RESULT_CONFIRM) {
+ status = _nfs4_proc_open_confirm(server->client, NFS_FH(inode),
+ sp, &res.stateid, arg.seqid);
+ if (status != 0)
+ goto out_free;
+ }
+ nfs_confirm_seqid(&sp->so_seqid, 0);
if (status >= 0) {
memcpy(state->stateid.data, res.stateid.data,
sizeof(state->stateid.data));
clear_bit(NFS_DELEGATED_STATE, &state->flags);
}
+out_free:
+ nfs_free_seqid(arg.seqid);
out:
- up(&sp->so_sema);
dput(parent);
return status;
}
@@ -345,11 +383,11 @@ int nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state)
return err;
}
-static inline int _nfs4_proc_open_confirm(struct rpc_clnt *clnt, const struct nfs_fh *fh, struct nfs4_state_owner *sp, nfs4_stateid *stateid)
+static int _nfs4_proc_open_confirm(struct rpc_clnt *clnt, const struct nfs_fh *fh, struct nfs4_state_owner *sp, nfs4_stateid *stateid, struct nfs_seqid *seqid)
{
struct nfs_open_confirmargs arg = {
.fh = fh,
- .seqid = sp->so_seqid,
+ .seqid = seqid,
.stateid = *stateid,
};
struct nfs_open_confirmres res;
@@ -362,7 +400,9 @@ static inline int _nfs4_proc_open_confirm(struct rpc_clnt *clnt, const struct nf
int status;
status = rpc_call_sync(clnt, &msg, RPC_TASK_NOINTR);
- nfs4_increment_seqid(status, sp);
+ /* Confirm the sequence as being established */
+ nfs_confirm_seqid(&sp->so_seqid, status);
+ nfs_increment_open_seqid(status, seqid);
if (status >= 0)
memcpy(stateid, &res.stateid, sizeof(*stateid));
return status;
@@ -380,21 +420,41 @@ static int _nfs4_proc_open(struct inode *dir, struct nfs4_state_owner *sp, stru
int status;
/* Update sequence id. The caller must serialize! */
- o_arg->seqid = sp->so_seqid;
o_arg->id = sp->so_id;
o_arg->clientid = sp->so_client->cl_clientid;
status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR);
- nfs4_increment_seqid(status, sp);
+ if (status == 0) {
+ /* OPEN on anything except a regular file is disallowed in NFSv4 */
+ switch (o_res->f_attr->mode & S_IFMT) {
+ case S_IFREG:
+ break;
+ case S_IFLNK:
+ status = -ELOOP;
+ break;
+ case S_IFDIR:
+ status = -EISDIR;
+ break;
+ default:
+ status = -ENOTDIR;
+ }
+ }
+
+ nfs_increment_open_seqid(status, o_arg->seqid);
if (status != 0)
goto out;
- update_changeattr(dir, &o_res->cinfo);
+ if (o_arg->open_flags & O_CREAT) {
+ update_changeattr(dir, &o_res->cinfo);
+ nfs_post_op_update_inode(dir, o_res->dir_attr);
+ } else
+ nfs_refresh_inode(dir, o_res->dir_attr);
if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) {
status = _nfs4_proc_open_confirm(server->client, &o_res->fh,
- sp, &o_res->stateid);
+ sp, &o_res->stateid, o_arg->seqid);
if (status != 0)
goto out;
}
+ nfs_confirm_seqid(&sp->so_seqid, 0);
if (!(o_res->f_attr->valid & NFS_ATTR_FATTR))
status = server->rpc_ops->getattr(server, &o_res->fh, o_res->f_attr);
out:
@@ -441,9 +501,7 @@ static int _nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
struct inode *inode = state->inode;
struct nfs_server *server = NFS_SERVER(dir);
struct nfs_delegation *delegation = NFS_I(inode)->delegation;
- struct nfs_fattr f_attr = {
- .valid = 0,
- };
+ struct nfs_fattr f_attr, dir_attr;
struct nfs_openargs o_arg = {
.fh = NFS_FH(dir),
.open_flags = state->state,
@@ -453,6 +511,7 @@ static int _nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
};
struct nfs_openres o_res = {
.f_attr = &f_attr,
+ .dir_attr = &dir_attr,
.server = server,
};
int status = 0;
@@ -465,6 +524,12 @@ static int _nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
set_bit(NFS_DELEGATED_STATE, &state->flags);
goto out;
}
+ o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid);
+ status = -ENOMEM;
+ if (o_arg.seqid == NULL)
+ goto out;
+ nfs_fattr_init(&f_attr);
+ nfs_fattr_init(&dir_attr);
status = _nfs4_proc_open(dir, sp, &o_arg, &o_res);
if (status != 0)
goto out_nodeleg;
@@ -490,6 +555,7 @@ static int _nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
nfs_inode_reclaim_delegation(inode, sp->so_cred, &o_res);
}
out_nodeleg:
+ nfs_free_seqid(o_arg.seqid);
clear_bit(NFS_DELEGATED_STATE, &state->flags);
out:
dput(parent);
@@ -564,7 +630,6 @@ static int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred
dprintk("%s: nfs4_get_state_owner failed!\n", __FUNCTION__);
goto out_err;
}
- down(&sp->so_sema);
state = nfs4_get_open_state(inode, sp);
if (state == NULL)
goto out_err;
@@ -589,7 +654,6 @@ static int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred
set_bit(NFS_DELEGATED_STATE, &state->flags);
update_open_stateid(state, &delegation->stateid, open_flags);
out_ok:
- up(&sp->so_sema);
nfs4_put_state_owner(sp);
up_read(&nfsi->rwsem);
up_read(&clp->cl_sem);
@@ -600,11 +664,12 @@ out_err:
if (sp != NULL) {
if (state != NULL)
nfs4_put_open_state(state);
- up(&sp->so_sema);
nfs4_put_state_owner(sp);
}
up_read(&nfsi->rwsem);
up_read(&clp->cl_sem);
+ if (err != -EACCES)
+ nfs_inode_return_delegation(inode);
return err;
}
@@ -635,9 +700,7 @@ static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, st
struct nfs4_client *clp = server->nfs4_state;
struct inode *inode = NULL;
int status;
- struct nfs_fattr f_attr = {
- .valid = 0,
- };
+ struct nfs_fattr f_attr, dir_attr;
struct nfs_openargs o_arg = {
.fh = NFS_FH(dir),
.open_flags = flags,
@@ -648,6 +711,7 @@ static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, st
};
struct nfs_openres o_res = {
.f_attr = &f_attr,
+ .dir_attr = &dir_attr,
.server = server,
};
@@ -665,8 +729,12 @@ static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, st
} else
o_arg.u.attrs = sattr;
/* Serialization for the sequence id */
- down(&sp->so_sema);
+ o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid);
+ if (o_arg.seqid == NULL)
+ return -ENOMEM;
+ nfs_fattr_init(&f_attr);
+ nfs_fattr_init(&dir_attr);
status = _nfs4_proc_open(dir, sp, &o_arg, &o_res);
if (status != 0)
goto out_err;
@@ -681,7 +749,7 @@ static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, st
update_open_stateid(state, &o_res.stateid, flags);
if (o_res.delegation_type != 0)
nfs_inode_set_delegation(inode, cred, &o_res);
- up(&sp->so_sema);
+ nfs_free_seqid(o_arg.seqid);
nfs4_put_state_owner(sp);
up_read(&clp->cl_sem);
*res = state;
@@ -690,7 +758,7 @@ out_err:
if (sp != NULL) {
if (state != NULL)
nfs4_put_open_state(state);
- up(&sp->so_sema);
+ nfs_free_seqid(o_arg.seqid);
nfs4_put_state_owner(sp);
}
/* Note: clp->cl_sem must be released before nfs4_put_open_state()! */
@@ -718,7 +786,7 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry,
* It is actually a sign of a bug on the client or on the server.
*
* If we receive a BAD_SEQID error in the particular case of
- * doing an OPEN, we assume that nfs4_increment_seqid() will
+ * doing an OPEN, we assume that nfs_increment_open_seqid() will
* have unhashed the old state_owner for us, and that we can
* therefore safely retry using a new one. We should still warn
* the user though...
@@ -728,6 +796,16 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry,
exception.retry = 1;
continue;
}
+ /*
+ * BAD_STATEID on OPEN means that the server cancelled our
+ * state before it received the OPEN_CONFIRM.
+ * Recover by retrying the request as per the discussion
+ * on Page 181 of RFC3530.
+ */
+ if (status == -NFS4ERR_BAD_STATEID) {
+ exception.retry = 1;
+ continue;
+ }
res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir),
status, &exception));
} while (exception.retry);
@@ -755,7 +833,7 @@ static int _nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
};
int status;
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
if (state != NULL) {
msg.rpc_cred = state->owner->so_cred;
@@ -787,19 +865,30 @@ struct nfs4_closedata {
struct nfs4_state *state;
struct nfs_closeargs arg;
struct nfs_closeres res;
+ struct nfs_fattr fattr;
};
+static void nfs4_free_closedata(struct nfs4_closedata *calldata)
+{
+ struct nfs4_state *state = calldata->state;
+ struct nfs4_state_owner *sp = state->owner;
+
+ nfs4_put_open_state(calldata->state);
+ nfs_free_seqid(calldata->arg.seqid);
+ nfs4_put_state_owner(sp);
+ kfree(calldata);
+}
+
static void nfs4_close_done(struct rpc_task *task)
{
struct nfs4_closedata *calldata = (struct nfs4_closedata *)task->tk_calldata;
struct nfs4_state *state = calldata->state;
- struct nfs4_state_owner *sp = state->owner;
struct nfs_server *server = NFS_SERVER(calldata->inode);
/* hmm. we are done with the inode, and in the process of freeing
* the state_owner. we keep this around to process errors
*/
- nfs4_increment_seqid(task->tk_status, sp);
+ nfs_increment_open_seqid(task->tk_status, calldata->arg.seqid);
switch (task->tk_status) {
case 0:
memcpy(&state->stateid, &calldata->res.stateid,
@@ -816,25 +905,49 @@ static void nfs4_close_done(struct rpc_task *task)
return;
}
}
+ nfs_refresh_inode(calldata->inode, calldata->res.fattr);
state->state = calldata->arg.open_flags;
- nfs4_put_open_state(state);
- up(&sp->so_sema);
- nfs4_put_state_owner(sp);
- up_read(&server->nfs4_state->cl_sem);
- kfree(calldata);
+ nfs4_free_closedata(calldata);
}
-static inline int nfs4_close_call(struct rpc_clnt *clnt, struct nfs4_closedata *calldata)
+static void nfs4_close_begin(struct rpc_task *task)
{
+ struct nfs4_closedata *calldata = (struct nfs4_closedata *)task->tk_calldata;
+ struct nfs4_state *state = calldata->state;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE],
.rpc_argp = &calldata->arg,
.rpc_resp = &calldata->res,
- .rpc_cred = calldata->state->owner->so_cred,
+ .rpc_cred = state->owner->so_cred,
};
- if (calldata->arg.open_flags != 0)
+ int mode = 0;
+ int status;
+
+ status = nfs_wait_on_sequence(calldata->arg.seqid, task);
+ if (status != 0)
+ return;
+ /* Don't reorder reads */
+ smp_rmb();
+ /* Recalculate the new open mode in case someone reopened the file
+ * while we were waiting in line to be scheduled.
+ */
+ if (state->nreaders != 0)
+ mode |= FMODE_READ;
+ if (state->nwriters != 0)
+ mode |= FMODE_WRITE;
+ if (test_bit(NFS_DELEGATED_STATE, &state->flags))
+ state->state = mode;
+ if (mode == state->state) {
+ nfs4_free_closedata(calldata);
+ task->tk_exit = NULL;
+ rpc_exit(task, 0);
+ return;
+ }
+ nfs_fattr_init(calldata->res.fattr);
+ if (mode != 0)
msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
- return rpc_call_async(clnt, &msg, 0, nfs4_close_done, calldata);
+ calldata->arg.open_flags = mode;
+ rpc_call_setup(task, &msg, 0);
}
/*
@@ -850,40 +963,57 @@ static inline int nfs4_close_call(struct rpc_clnt *clnt, struct nfs4_closedata *
*/
int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode)
{
+ struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_closedata *calldata;
- int status;
+ int status = -ENOMEM;
- /* Tell caller we're done */
- if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
- state->state = mode;
- return 0;
- }
- calldata = (struct nfs4_closedata *)kmalloc(sizeof(*calldata), GFP_KERNEL);
+ calldata = kmalloc(sizeof(*calldata), GFP_KERNEL);
if (calldata == NULL)
- return -ENOMEM;
+ goto out;
calldata->inode = inode;
calldata->state = state;
calldata->arg.fh = NFS_FH(inode);
+ calldata->arg.stateid = &state->stateid;
/* Serialization for the sequence id */
- calldata->arg.seqid = state->owner->so_seqid;
- calldata->arg.open_flags = mode;
- memcpy(&calldata->arg.stateid, &state->stateid,
- sizeof(calldata->arg.stateid));
- status = nfs4_close_call(NFS_SERVER(inode)->client, calldata);
- /*
- * Return -EINPROGRESS on success in order to indicate to the
- * caller that an asynchronous RPC call has been launched, and
- * that it will release the semaphores on completion.
- */
- return (status == 0) ? -EINPROGRESS : status;
+ calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid);
+ if (calldata->arg.seqid == NULL)
+ goto out_free_calldata;
+ calldata->arg.bitmask = server->attr_bitmask;
+ calldata->res.fattr = &calldata->fattr;
+ calldata->res.server = server;
+
+ status = nfs4_call_async(server->client, nfs4_close_begin,
+ nfs4_close_done, calldata);
+ if (status == 0)
+ goto out;
+
+ nfs_free_seqid(calldata->arg.seqid);
+out_free_calldata:
+ kfree(calldata);
+out:
+ return status;
}
-struct inode *
+static void nfs4_intent_set_file(struct nameidata *nd, struct dentry *dentry, struct nfs4_state *state)
+{
+ struct file *filp;
+
+ filp = lookup_instantiate_filp(nd, dentry, NULL);
+ if (!IS_ERR(filp)) {
+ struct nfs_open_context *ctx;
+ ctx = (struct nfs_open_context *)filp->private_data;
+ ctx->state = state;
+ } else
+ nfs4_close_state(state, nd->intent.open.flags);
+}
+
+struct dentry *
nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
struct iattr attr;
struct rpc_cred *cred;
struct nfs4_state *state;
+ struct dentry *res;
if (nd->flags & LOOKUP_CREATE) {
attr.ia_mode = nd->intent.open.create_mode;
@@ -897,16 +1027,23 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
if (IS_ERR(cred))
- return (struct inode *)cred;
+ return (struct dentry *)cred;
state = nfs4_do_open(dir, dentry, nd->intent.open.flags, &attr, cred);
put_rpccred(cred);
- if (IS_ERR(state))
- return (struct inode *)state;
- return state->inode;
+ if (IS_ERR(state)) {
+ if (PTR_ERR(state) == -ENOENT)
+ d_add(dentry, NULL);
+ return (struct dentry *)state;
+ }
+ res = d_add_unique(dentry, state->inode);
+ if (res != NULL)
+ dentry = res;
+ nfs4_intent_set_file(nd, dentry, state);
+ return res;
}
int
-nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags)
+nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, struct nameidata *nd)
{
struct rpc_cred *cred;
struct nfs4_state *state;
@@ -919,18 +1056,30 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags)
if (IS_ERR(state))
state = nfs4_do_open(dir, dentry, openflags, NULL, cred);
put_rpccred(cred);
- if (state == ERR_PTR(-ENOENT) && dentry->d_inode == 0)
- return 1;
- if (IS_ERR(state))
- return 0;
+ if (IS_ERR(state)) {
+ switch (PTR_ERR(state)) {
+ case -EPERM:
+ case -EACCES:
+ case -EDQUOT:
+ case -ENOSPC:
+ case -EROFS:
+ lookup_instantiate_filp(nd, (struct dentry *)state, NULL);
+ return 1;
+ case -ENOENT:
+ if (dentry->d_inode == NULL)
+ return 1;
+ }
+ goto out_drop;
+ }
inode = state->inode;
+ iput(inode);
if (inode == dentry->d_inode) {
- iput(inode);
+ nfs4_intent_set_file(nd, dentry, state);
return 1;
}
- d_drop(dentry);
nfs4_close_state(state, openflags);
- iput(inode);
+out_drop:
+ d_drop(dentry);
return 0;
}
@@ -974,13 +1123,12 @@ static int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fh
static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *info)
{
- struct nfs_fattr * fattr = info->fattr;
struct nfs4_lookup_root_arg args = {
.bitmask = nfs4_fattr_bitmap,
};
struct nfs4_lookup_res res = {
.server = server,
- .fattr = fattr,
+ .fattr = info->fattr,
.fh = fhandle,
};
struct rpc_message msg = {
@@ -988,7 +1136,7 @@ static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
.rpc_argp = &args,
.rpc_resp = &res,
};
- fattr->valid = 0;
+ nfs_fattr_init(info->fattr);
return rpc_call_sync(server->client, &msg, 0);
}
@@ -1051,7 +1199,7 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
q.len = p - q.name;
do {
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
status = nfs4_handle_exception(server,
rpc_call_sync(server->client, &msg, 0),
&exception);
@@ -1088,7 +1236,7 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
.rpc_resp = &res,
};
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
return rpc_call_sync(server->client, &msg, 0);
}
@@ -1130,7 +1278,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
struct nfs4_state *state;
int status;
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
if (IS_ERR(cred))
@@ -1176,7 +1324,7 @@ static int _nfs4_proc_lookup(struct inode *dir, struct qstr *name,
.rpc_resp = &res,
};
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
dprintk("NFS call lookup %s\n", name->name);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
@@ -1325,7 +1473,7 @@ static int _nfs4_proc_read(struct nfs_read_data *rdata)
dprintk("NFS call read %d @ %Ld\n", rdata->args.count,
(long long) rdata->args.offset);
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
status = rpc_call_sync(server->client, &msg, flags);
if (!status)
renew_lease(server, timestamp);
@@ -1362,7 +1510,7 @@ static int _nfs4_proc_write(struct nfs_write_data *wdata)
dprintk("NFS call write %d @ %Ld\n", wdata->args.count,
(long long) wdata->args.offset);
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
status = rpc_call_sync(server->client, &msg, rpcflags);
dprintk("NFS reply write: %d\n", status);
return status;
@@ -1396,7 +1544,7 @@ static int _nfs4_proc_commit(struct nfs_write_data *cdata)
dprintk("NFS call commit %d @ %Ld\n", cdata->args.count,
(long long) cdata->args.offset);
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
status = rpc_call_sync(server->client, &msg, 0);
dprintk("NFS reply commit: %d\n", status);
return status;
@@ -1431,7 +1579,7 @@ static int nfs4_proc_commit(struct nfs_write_data *cdata)
static int
nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
- int flags)
+ int flags, struct nameidata *nd)
{
struct nfs4_state *state;
struct rpc_cred *cred;
@@ -1453,24 +1601,30 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
struct nfs_fattr fattr;
status = nfs4_do_setattr(NFS_SERVER(dir), &fattr,
NFS_FH(state->inode), sattr, state);
- if (status == 0) {
+ if (status == 0)
nfs_setattr_update_inode(state->inode, sattr);
- goto out;
- }
- } else if (flags != 0)
- goto out;
- nfs4_close_state(state, flags);
+ }
+ if (status == 0 && nd != NULL && (nd->flags & LOOKUP_OPEN))
+ nfs4_intent_set_file(nd, dentry, state);
+ else
+ nfs4_close_state(state, flags);
out:
return status;
}
static int _nfs4_proc_remove(struct inode *dir, struct qstr *name)
{
+ struct nfs_server *server = NFS_SERVER(dir);
struct nfs4_remove_arg args = {
.fh = NFS_FH(dir),
.name = name,
+ .bitmask = server->attr_bitmask,
+ };
+ struct nfs_fattr dir_attr;
+ struct nfs4_remove_res res = {
+ .server = server,
+ .dir_attr = &dir_attr,
};
- struct nfs4_change_info res;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE],
.rpc_argp = &args,
@@ -1478,9 +1632,12 @@ static int _nfs4_proc_remove(struct inode *dir, struct qstr *name)
};
int status;
- status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
- if (status == 0)
- update_changeattr(dir, &res);
+ nfs_fattr_init(res.dir_attr);
+ status = rpc_call_sync(server->client, &msg, 0);
+ if (status == 0) {
+ update_changeattr(dir, &res.cinfo);
+ nfs_post_op_update_inode(dir, res.dir_attr);
+ }
return status;
}
@@ -1498,12 +1655,14 @@ static int nfs4_proc_remove(struct inode *dir, struct qstr *name)
struct unlink_desc {
struct nfs4_remove_arg args;
- struct nfs4_change_info res;
+ struct nfs4_remove_res res;
+ struct nfs_fattr dir_attr;
};
static int nfs4_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir,
struct qstr *name)
{
+ struct nfs_server *server = NFS_SERVER(dir->d_inode);
struct unlink_desc *up;
up = (struct unlink_desc *) kmalloc(sizeof(*up), GFP_KERNEL);
@@ -1512,6 +1671,9 @@ static int nfs4_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir,
up->args.fh = NFS_FH(dir->d_inode);
up->args.name = name;
+ up->args.bitmask = server->attr_bitmask;
+ up->res.server = server;
+ up->res.dir_attr = &up->dir_attr;
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE];
msg->rpc_argp = &up->args;
@@ -1526,7 +1688,8 @@ static int nfs4_proc_unlink_done(struct dentry *dir, struct rpc_task *task)
if (msg->rpc_resp != NULL) {
up = container_of(msg->rpc_resp, struct unlink_desc, res);
- update_changeattr(dir->d_inode, &up->res);
+ update_changeattr(dir->d_inode, &up->res.cinfo);
+ nfs_post_op_update_inode(dir->d_inode, up->res.dir_attr);
kfree(up);
msg->rpc_resp = NULL;
msg->rpc_argp = NULL;
@@ -1537,13 +1700,20 @@ static int nfs4_proc_unlink_done(struct dentry *dir, struct rpc_task *task)
static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
struct inode *new_dir, struct qstr *new_name)
{
+ struct nfs_server *server = NFS_SERVER(old_dir);
struct nfs4_rename_arg arg = {
.old_dir = NFS_FH(old_dir),
.new_dir = NFS_FH(new_dir),
.old_name = old_name,
.new_name = new_name,
+ .bitmask = server->attr_bitmask,
+ };
+ struct nfs_fattr old_fattr, new_fattr;
+ struct nfs4_rename_res res = {
+ .server = server,
+ .old_fattr = &old_fattr,
+ .new_fattr = &new_fattr,
};
- struct nfs4_rename_res res = { };
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME],
.rpc_argp = &arg,
@@ -1551,11 +1721,15 @@ static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
};
int status;
- status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0);
+ nfs_fattr_init(res.old_fattr);
+ nfs_fattr_init(res.new_fattr);
+ status = rpc_call_sync(server->client, &msg, 0);
if (!status) {
update_changeattr(old_dir, &res.old_cinfo);
+ nfs_post_op_update_inode(old_dir, res.old_fattr);
update_changeattr(new_dir, &res.new_cinfo);
+ nfs_post_op_update_inode(new_dir, res.new_fattr);
}
return status;
}
@@ -1576,22 +1750,34 @@ static int nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
{
+ struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_link_arg arg = {
.fh = NFS_FH(inode),
.dir_fh = NFS_FH(dir),
.name = name,
+ .bitmask = server->attr_bitmask,
+ };
+ struct nfs_fattr fattr, dir_attr;
+ struct nfs4_link_res res = {
+ .server = server,
+ .fattr = &fattr,
+ .dir_attr = &dir_attr,
};
- struct nfs4_change_info cinfo = { };
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LINK],
.rpc_argp = &arg,
- .rpc_resp = &cinfo,
+ .rpc_resp = &res,
};
int status;
- status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
- if (!status)
- update_changeattr(dir, &cinfo);
+ nfs_fattr_init(res.fattr);
+ nfs_fattr_init(res.dir_attr);
+ status = rpc_call_sync(server->client, &msg, 0);
+ if (!status) {
+ update_changeattr(dir, &res.cinfo);
+ nfs_post_op_update_inode(dir, res.dir_attr);
+ nfs_refresh_inode(inode, res.fattr);
+ }
return status;
}
@@ -1613,6 +1799,7 @@ static int _nfs4_proc_symlink(struct inode *dir, struct qstr *name,
struct nfs_fattr *fattr)
{
struct nfs_server *server = NFS_SERVER(dir);
+ struct nfs_fattr dir_fattr;
struct nfs4_create_arg arg = {
.dir_fh = NFS_FH(dir),
.server = server,
@@ -1625,6 +1812,7 @@ static int _nfs4_proc_symlink(struct inode *dir, struct qstr *name,
.server = server,
.fh = fhandle,
.fattr = fattr,
+ .dir_fattr = &dir_fattr,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SYMLINK],
@@ -1636,11 +1824,13 @@ static int _nfs4_proc_symlink(struct inode *dir, struct qstr *name,
if (path->len > NFS4_MAXPATHLEN)
return -ENAMETOOLONG;
arg.u.symlink = path;
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
+ nfs_fattr_init(&dir_fattr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
if (!status)
update_changeattr(dir, &res.dir_cinfo);
+ nfs_post_op_update_inode(dir, res.dir_fattr);
return status;
}
@@ -1664,7 +1854,7 @@ static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
{
struct nfs_server *server = NFS_SERVER(dir);
struct nfs_fh fhandle;
- struct nfs_fattr fattr;
+ struct nfs_fattr fattr, dir_fattr;
struct nfs4_create_arg arg = {
.dir_fh = NFS_FH(dir),
.server = server,
@@ -1677,6 +1867,7 @@ static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
.server = server,
.fh = &fhandle,
.fattr = &fattr,
+ .dir_fattr = &dir_fattr,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE],
@@ -1685,11 +1876,13 @@ static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
};
int status;
- fattr.valid = 0;
+ nfs_fattr_init(&fattr);
+ nfs_fattr_init(&dir_fattr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
if (!status) {
update_changeattr(dir, &res.dir_cinfo);
+ nfs_post_op_update_inode(dir, res.dir_fattr);
status = nfs_instantiate(dentry, &fhandle, &fattr);
}
return status;
@@ -1762,7 +1955,7 @@ static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
{
struct nfs_server *server = NFS_SERVER(dir);
struct nfs_fh fh;
- struct nfs_fattr fattr;
+ struct nfs_fattr fattr, dir_fattr;
struct nfs4_create_arg arg = {
.dir_fh = NFS_FH(dir),
.server = server,
@@ -1774,6 +1967,7 @@ static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
.server = server,
.fh = &fh,
.fattr = &fattr,
+ .dir_fattr = &dir_fattr,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE],
@@ -1783,7 +1977,8 @@ static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
int status;
int mode = sattr->ia_mode;
- fattr.valid = 0;
+ nfs_fattr_init(&fattr);
+ nfs_fattr_init(&dir_fattr);
BUG_ON(!(sattr->ia_valid & ATTR_MODE));
BUG_ON(!S_ISFIFO(mode) && !S_ISBLK(mode) && !S_ISCHR(mode) && !S_ISSOCK(mode));
@@ -1805,6 +2000,7 @@ static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
if (status == 0) {
update_changeattr(dir, &res.dir_cinfo);
+ nfs_post_op_update_inode(dir, res.dir_fattr);
status = nfs_instantiate(dentry, &fh, &fattr);
}
return status;
@@ -1836,7 +2032,7 @@ static int _nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
.rpc_resp = fsstat,
};
- fsstat->fattr->valid = 0;
+ nfs_fattr_init(fsstat->fattr);
return rpc_call_sync(server->client, &msg, 0);
}
@@ -1883,7 +2079,7 @@ static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, str
static int nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
{
- fsinfo->fattr->valid = 0;
+ nfs_fattr_init(fsinfo->fattr);
return nfs4_do_fsinfo(server, fhandle, fsinfo);
}
@@ -1906,7 +2102,7 @@ static int _nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle
return 0;
}
- pathconf->fattr->valid = 0;
+ nfs_fattr_init(pathconf->fattr);
return rpc_call_sync(server->client, &msg, 0);
}
@@ -1973,8 +2169,10 @@ nfs4_write_done(struct rpc_task *task)
rpc_restart_call(task);
return;
}
- if (task->tk_status >= 0)
+ if (task->tk_status >= 0) {
renew_lease(NFS_SERVER(inode), data->timestamp);
+ nfs_post_op_update_inode(inode, data->res.fattr);
+ }
/* Call back common NFS writeback processing */
nfs_writeback_done(task);
}
@@ -1990,6 +2188,7 @@ nfs4_proc_write_setup(struct nfs_write_data *data, int how)
.rpc_cred = data->cred,
};
struct inode *inode = data->inode;
+ struct nfs_server *server = NFS_SERVER(inode);
int stable;
int flags;
@@ -2001,6 +2200,8 @@ nfs4_proc_write_setup(struct nfs_write_data *data, int how)
} else
stable = NFS_UNSTABLE;
data->args.stable = stable;
+ data->args.bitmask = server->attr_bitmask;
+ data->res.server = server;
data->timestamp = jiffies;
@@ -2022,6 +2223,8 @@ nfs4_commit_done(struct rpc_task *task)
rpc_restart_call(task);
return;
}
+ if (task->tk_status >= 0)
+ nfs_post_op_update_inode(inode, data->res.fattr);
/* Call back common NFS writeback processing */
nfs_commit_done(task);
}
@@ -2037,8 +2240,12 @@ nfs4_proc_commit_setup(struct nfs_write_data *data, int how)
.rpc_cred = data->cred,
};
struct inode *inode = data->inode;
+ struct nfs_server *server = NFS_SERVER(inode);
int flags;
+ data->args.bitmask = server->attr_bitmask;
+ data->res.server = server;
+
/* Set the initial flags for the task. */
flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
@@ -2106,65 +2313,6 @@ nfs4_proc_renew(struct nfs4_client *clp)
return 0;
}
-/*
- * We will need to arrange for the VFS layer to provide an atomic open.
- * Until then, this open method is prone to inefficiency and race conditions
- * due to the lookup, potential create, and open VFS calls from sys_open()
- * placed on the wire.
- */
-static int
-nfs4_proc_file_open(struct inode *inode, struct file *filp)
-{
- struct dentry *dentry = filp->f_dentry;
- struct nfs_open_context *ctx;
- struct nfs4_state *state = NULL;
- struct rpc_cred *cred;
- int status = -ENOMEM;
-
- dprintk("nfs4_proc_file_open: starting on (%.*s/%.*s)\n",
- (int)dentry->d_parent->d_name.len,
- dentry->d_parent->d_name.name,
- (int)dentry->d_name.len, dentry->d_name.name);
-
-
- /* Find our open stateid */
- cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
- if (IS_ERR(cred))
- return PTR_ERR(cred);
- ctx = alloc_nfs_open_context(dentry, cred);
- put_rpccred(cred);
- if (unlikely(ctx == NULL))
- return -ENOMEM;
- status = -EIO; /* ERACE actually */
- state = nfs4_find_state(inode, cred, filp->f_mode);
- if (unlikely(state == NULL))
- goto no_state;
- ctx->state = state;
- nfs4_close_state(state, filp->f_mode);
- ctx->mode = filp->f_mode;
- nfs_file_set_open_context(filp, ctx);
- put_nfs_open_context(ctx);
- if (filp->f_mode & FMODE_WRITE)
- nfs_begin_data_update(inode);
- return 0;
-no_state:
- printk(KERN_WARNING "NFS: v4 raced in function %s\n", __FUNCTION__);
- put_nfs_open_context(ctx);
- return status;
-}
-
-/*
- * Release our state
- */
-static int
-nfs4_proc_file_release(struct inode *inode, struct file *filp)
-{
- if (filp->f_mode & FMODE_WRITE)
- nfs_end_data_update(inode);
- nfs_file_clear_open_context(filp);
- return 0;
-}
-
static inline int nfs4_server_supports_acls(struct nfs_server *server)
{
return (server->caps & NFS_CAP_ACLS)
@@ -2285,7 +2433,7 @@ static inline ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, size
return -ENOMEM;
args.acl_pages[0] = localpage;
args.acl_pgbase = 0;
- args.acl_len = PAGE_SIZE;
+ resp_len = args.acl_len = PAGE_SIZE;
} else {
resp_buf = buf;
buf_to_pages(buf, buflen, args.acl_pages, &args.acl_pgbase);
@@ -2345,6 +2493,7 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen
if (!nfs4_server_supports_acls(server))
return -EOPNOTSUPP;
+ nfs_inode_return_delegation(inode);
buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
ret = rpc_call_sync(NFS_SERVER(inode)->client, &msg, 0);
if (ret == 0)
@@ -2353,7 +2502,7 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen
}
static int
-nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server)
+nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
{
struct nfs4_client *clp = server->nfs4_state;
@@ -2431,7 +2580,7 @@ static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
/* This is the error handling routine for processes that are allowed
* to sleep.
*/
-int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
+int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
{
struct nfs4_client *clp = server->nfs4_state;
int ret = errorcode;
@@ -2632,7 +2781,6 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
down_read(&clp->cl_sem);
nlo.clientid = clp->cl_clientid;
- down(&state->lock_sema);
status = nfs4_set_lock_state(state, request);
if (status != 0)
goto out;
@@ -2659,7 +2807,6 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
status = 0;
}
out:
- up(&state->lock_sema);
up_read(&clp->cl_sem);
return status;
}
@@ -2696,79 +2843,149 @@ static int do_vfs_lock(struct file *file, struct file_lock *fl)
return res;
}
-static int _nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
+struct nfs4_unlockdata {
+ struct nfs_lockargs arg;
+ struct nfs_locku_opargs luargs;
+ struct nfs_lockres res;
+ struct nfs4_lock_state *lsp;
+ struct nfs_open_context *ctx;
+ atomic_t refcount;
+ struct completion completion;
+};
+
+static void nfs4_locku_release_calldata(struct nfs4_unlockdata *calldata)
{
- struct inode *inode = state->inode;
- struct nfs_server *server = NFS_SERVER(inode);
- struct nfs4_client *clp = server->nfs4_state;
- struct nfs_lockargs arg = {
- .fh = NFS_FH(inode),
- .type = nfs4_lck_type(cmd, request),
- .offset = request->fl_start,
- .length = nfs4_lck_length(request),
- };
- struct nfs_lockres res = {
- .server = server,
- };
+ if (atomic_dec_and_test(&calldata->refcount)) {
+ nfs_free_seqid(calldata->luargs.seqid);
+ nfs4_put_lock_state(calldata->lsp);
+ put_nfs_open_context(calldata->ctx);
+ kfree(calldata);
+ }
+}
+
+static void nfs4_locku_complete(struct nfs4_unlockdata *calldata)
+{
+ complete(&calldata->completion);
+ nfs4_locku_release_calldata(calldata);
+}
+
+static void nfs4_locku_done(struct rpc_task *task)
+{
+ struct nfs4_unlockdata *calldata = (struct nfs4_unlockdata *)task->tk_calldata;
+
+ nfs_increment_lock_seqid(task->tk_status, calldata->luargs.seqid);
+ switch (task->tk_status) {
+ case 0:
+ memcpy(calldata->lsp->ls_stateid.data,
+ calldata->res.u.stateid.data,
+ sizeof(calldata->lsp->ls_stateid.data));
+ break;
+ case -NFS4ERR_STALE_STATEID:
+ case -NFS4ERR_EXPIRED:
+ nfs4_schedule_state_recovery(calldata->res.server->nfs4_state);
+ break;
+ default:
+ if (nfs4_async_handle_error(task, calldata->res.server) == -EAGAIN) {
+ rpc_restart_call(task);
+ return;
+ }
+ }
+ nfs4_locku_complete(calldata);
+}
+
+static void nfs4_locku_begin(struct rpc_task *task)
+{
+ struct nfs4_unlockdata *calldata = (struct nfs4_unlockdata *)task->tk_calldata;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKU],
- .rpc_argp = &arg,
- .rpc_resp = &res,
- .rpc_cred = state->owner->so_cred,
+ .rpc_argp = &calldata->arg,
+ .rpc_resp = &calldata->res,
+ .rpc_cred = calldata->lsp->ls_state->owner->so_cred,
};
+ int status;
+
+ status = nfs_wait_on_sequence(calldata->luargs.seqid, task);
+ if (status != 0)
+ return;
+ if ((calldata->lsp->ls_flags & NFS_LOCK_INITIALIZED) == 0) {
+ nfs4_locku_complete(calldata);
+ task->tk_exit = NULL;
+ rpc_exit(task, 0);
+ return;
+ }
+ rpc_call_setup(task, &msg, 0);
+}
+
+static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
+{
+ struct nfs4_unlockdata *calldata;
+ struct inode *inode = state->inode;
+ struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_lock_state *lsp;
- struct nfs_locku_opargs luargs;
int status;
-
- down_read(&clp->cl_sem);
- down(&state->lock_sema);
+
status = nfs4_set_lock_state(state, request);
if (status != 0)
- goto out;
+ return status;
lsp = request->fl_u.nfs4_fl.owner;
/* We might have lost the locks! */
if ((lsp->ls_flags & NFS_LOCK_INITIALIZED) == 0)
- goto out;
- luargs.seqid = lsp->ls_seqid;
- memcpy(&luargs.stateid, &lsp->ls_stateid, sizeof(luargs.stateid));
- arg.u.locku = &luargs;
- status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR);
- nfs4_increment_lock_seqid(status, lsp);
-
- if (status == 0)
- memcpy(&lsp->ls_stateid, &res.u.stateid,
- sizeof(lsp->ls_stateid));
-out:
- up(&state->lock_sema);
+ return 0;
+ calldata = kmalloc(sizeof(*calldata), GFP_KERNEL);
+ if (calldata == NULL)
+ return -ENOMEM;
+ calldata->luargs.seqid = nfs_alloc_seqid(&lsp->ls_seqid);
+ if (calldata->luargs.seqid == NULL) {
+ kfree(calldata);
+ return -ENOMEM;
+ }
+ calldata->luargs.stateid = &lsp->ls_stateid;
+ calldata->arg.fh = NFS_FH(inode);
+ calldata->arg.type = nfs4_lck_type(cmd, request);
+ calldata->arg.offset = request->fl_start;
+ calldata->arg.length = nfs4_lck_length(request);
+ calldata->arg.u.locku = &calldata->luargs;
+ calldata->res.server = server;
+ calldata->lsp = lsp;
+ atomic_inc(&lsp->ls_count);
+
+ /* Ensure we don't close file until we're done freeing locks! */
+ calldata->ctx = get_nfs_open_context((struct nfs_open_context*)request->fl_file->private_data);
+
+ atomic_set(&calldata->refcount, 2);
+ init_completion(&calldata->completion);
+
+ status = nfs4_call_async(NFS_SERVER(inode)->client, nfs4_locku_begin,
+ nfs4_locku_done, calldata);
if (status == 0)
- do_vfs_lock(request->fl_file, request);
- up_read(&clp->cl_sem);
+ wait_for_completion_interruptible(&calldata->completion);
+ do_vfs_lock(request->fl_file, request);
+ nfs4_locku_release_calldata(calldata);
return status;
}
-static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
-{
- struct nfs4_exception exception = { };
- int err;
-
- do {
- err = nfs4_handle_exception(NFS_SERVER(state->inode),
- _nfs4_proc_unlck(state, cmd, request),
- &exception);
- } while (exception.retry);
- return err;
-}
-
static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *request, int reclaim)
{
struct inode *inode = state->inode;
struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_lock_state *lsp = request->fl_u.nfs4_fl.owner;
+ struct nfs_lock_opargs largs = {
+ .lock_stateid = &lsp->ls_stateid,
+ .open_stateid = &state->stateid,
+ .lock_owner = {
+ .clientid = server->nfs4_state->cl_clientid,
+ .id = lsp->ls_id,
+ },
+ .reclaim = reclaim,
+ };
struct nfs_lockargs arg = {
.fh = NFS_FH(inode),
.type = nfs4_lck_type(cmd, request),
.offset = request->fl_start,
.length = nfs4_lck_length(request),
+ .u = {
+ .lock = &largs,
+ },
};
struct nfs_lockres res = {
.server = server,
@@ -2779,53 +2996,39 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *r
.rpc_resp = &res,
.rpc_cred = state->owner->so_cred,
};
- struct nfs_lock_opargs largs = {
- .reclaim = reclaim,
- .new_lock_owner = 0,
- };
- int status;
+ int status = -ENOMEM;
- if (!(lsp->ls_flags & NFS_LOCK_INITIALIZED)) {
+ largs.lock_seqid = nfs_alloc_seqid(&lsp->ls_seqid);
+ if (largs.lock_seqid == NULL)
+ return -ENOMEM;
+ if (!(lsp->ls_seqid.flags & NFS_SEQID_CONFIRMED)) {
struct nfs4_state_owner *owner = state->owner;
- struct nfs_open_to_lock otl = {
- .lock_owner = {
- .clientid = server->nfs4_state->cl_clientid,
- },
- };
-
- otl.lock_seqid = lsp->ls_seqid;
- otl.lock_owner.id = lsp->ls_id;
- memcpy(&otl.open_stateid, &state->stateid, sizeof(otl.open_stateid));
- largs.u.open_lock = &otl;
+
+ largs.open_seqid = nfs_alloc_seqid(&owner->so_seqid);
+ if (largs.open_seqid == NULL)
+ goto out;
largs.new_lock_owner = 1;
- arg.u.lock = &largs;
- down(&owner->so_sema);
- otl.open_seqid = owner->so_seqid;
status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR);
- /* increment open_owner seqid on success, and
- * seqid mutating errors */
- nfs4_increment_seqid(status, owner);
- up(&owner->so_sema);
- if (status == 0) {
- lsp->ls_flags |= NFS_LOCK_INITIALIZED;
- lsp->ls_seqid++;
+ /* increment open seqid on success, and seqid mutating errors */
+ if (largs.new_lock_owner != 0) {
+ nfs_increment_open_seqid(status, largs.open_seqid);
+ if (status == 0)
+ nfs_confirm_seqid(&lsp->ls_seqid, 0);
}
- } else {
- struct nfs_exist_lock el = {
- .seqid = lsp->ls_seqid,
- };
- memcpy(&el.stateid, &lsp->ls_stateid, sizeof(el.stateid));
- largs.u.exist_lock = &el;
- arg.u.lock = &largs;
+ nfs_free_seqid(largs.open_seqid);
+ } else
status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR);
- /* increment seqid on success, and * seqid mutating errors*/
- nfs4_increment_lock_seqid(status, lsp);
- }
+ /* increment lock seqid on success, and seqid mutating errors*/
+ nfs_increment_lock_seqid(status, largs.lock_seqid);
/* save the returned stateid. */
- if (status == 0)
- memcpy(&lsp->ls_stateid, &res.u.stateid, sizeof(nfs4_stateid));
- else if (status == -NFS4ERR_DENIED)
+ if (status == 0) {
+ memcpy(lsp->ls_stateid.data, res.u.stateid.data,
+ sizeof(lsp->ls_stateid.data));
+ lsp->ls_flags |= NFS_LOCK_INITIALIZED;
+ } else if (status == -NFS4ERR_DENIED)
status = -EAGAIN;
+out:
+ nfs_free_seqid(largs.lock_seqid);
return status;
}
@@ -2865,11 +3068,9 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
int status;
down_read(&clp->cl_sem);
- down(&state->lock_sema);
status = nfs4_set_lock_state(state, request);
if (status == 0)
status = _nfs4_do_setlk(state, cmd, request, 0);
- up(&state->lock_sema);
if (status == 0) {
/* Note: we always want to sleep here! */
request->fl_flags |= FL_SLEEP;
@@ -3024,8 +3225,8 @@ struct nfs_rpc_ops nfs_v4_clientops = {
.read_setup = nfs4_proc_read_setup,
.write_setup = nfs4_proc_write_setup,
.commit_setup = nfs4_proc_commit_setup,
- .file_open = nfs4_proc_file_open,
- .file_release = nfs4_proc_file_release,
+ .file_open = nfs_open,
+ .file_release = nfs_release,
.lock = nfs4_proc_lock,
.clear_acl_cache = nfs4_zap_acl_attr,
};
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index afe587d82f1..2d5a6a2b9de 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -264,13 +264,16 @@ nfs4_alloc_state_owner(void)
{
struct nfs4_state_owner *sp;
- sp = kmalloc(sizeof(*sp),GFP_KERNEL);
+ sp = kzalloc(sizeof(*sp),GFP_KERNEL);
if (!sp)
return NULL;
- init_MUTEX(&sp->so_sema);
- sp->so_seqid = 0; /* arbitrary */
+ spin_lock_init(&sp->so_lock);
INIT_LIST_HEAD(&sp->so_states);
INIT_LIST_HEAD(&sp->so_delegations);
+ rpc_init_wait_queue(&sp->so_sequence.wait, "Seqid_waitqueue");
+ sp->so_seqid.sequence = &sp->so_sequence;
+ spin_lock_init(&sp->so_sequence.lock);
+ INIT_LIST_HEAD(&sp->so_sequence.list);
atomic_set(&sp->so_count, 1);
return sp;
}
@@ -359,7 +362,6 @@ nfs4_alloc_open_state(void)
memset(state->stateid.data, 0, sizeof(state->stateid.data));
atomic_set(&state->count, 1);
INIT_LIST_HEAD(&state->lock_states);
- init_MUTEX(&state->lock_sema);
spin_lock_init(&state->state_lock);
return state;
}
@@ -437,21 +439,23 @@ nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner)
if (state)
goto out;
new = nfs4_alloc_open_state();
+ spin_lock(&owner->so_lock);
spin_lock(&inode->i_lock);
state = __nfs4_find_state_byowner(inode, owner);
if (state == NULL && new != NULL) {
state = new;
- /* Caller *must* be holding owner->so_sem */
- /* Note: The reclaim code dictates that we add stateless
- * and read-only stateids to the end of the list */
- list_add_tail(&state->open_states, &owner->so_states);
state->owner = owner;
atomic_inc(&owner->so_count);
list_add(&state->inode_states, &nfsi->open_states);
state->inode = igrab(inode);
spin_unlock(&inode->i_lock);
+ /* Note: The reclaim code dictates that we add stateless
+ * and read-only stateids to the end of the list */
+ list_add_tail(&state->open_states, &owner->so_states);
+ spin_unlock(&owner->so_lock);
} else {
spin_unlock(&inode->i_lock);
+ spin_unlock(&owner->so_lock);
if (new)
nfs4_free_open_state(new);
}
@@ -461,19 +465,21 @@ out:
/*
* Beware! Caller must be holding exactly one
- * reference to clp->cl_sem and owner->so_sema!
+ * reference to clp->cl_sem!
*/
void nfs4_put_open_state(struct nfs4_state *state)
{
struct inode *inode = state->inode;
struct nfs4_state_owner *owner = state->owner;
- if (!atomic_dec_and_lock(&state->count, &inode->i_lock))
+ if (!atomic_dec_and_lock(&state->count, &owner->so_lock))
return;
+ spin_lock(&inode->i_lock);
if (!list_empty(&state->inode_states))
list_del(&state->inode_states);
- spin_unlock(&inode->i_lock);
list_del(&state->open_states);
+ spin_unlock(&inode->i_lock);
+ spin_unlock(&owner->so_lock);
iput(inode);
BUG_ON (state->state != 0);
nfs4_free_open_state(state);
@@ -481,20 +487,17 @@ void nfs4_put_open_state(struct nfs4_state *state)
}
/*
- * Beware! Caller must be holding no references to clp->cl_sem!
- * of owner->so_sema!
+ * Close the current file.
*/
void nfs4_close_state(struct nfs4_state *state, mode_t mode)
{
struct inode *inode = state->inode;
struct nfs4_state_owner *owner = state->owner;
- struct nfs4_client *clp = owner->so_client;
int newstate;
atomic_inc(&owner->so_count);
- down_read(&clp->cl_sem);
- down(&owner->so_sema);
/* Protect against nfs4_find_state() */
+ spin_lock(&owner->so_lock);
spin_lock(&inode->i_lock);
if (mode & FMODE_READ)
state->nreaders--;
@@ -507,6 +510,7 @@ void nfs4_close_state(struct nfs4_state *state, mode_t mode)
list_move_tail(&state->open_states, &owner->so_states);
}
spin_unlock(&inode->i_lock);
+ spin_unlock(&owner->so_lock);
newstate = 0;
if (state->state != 0) {
if (state->nreaders)
@@ -515,14 +519,16 @@ void nfs4_close_state(struct nfs4_state *state, mode_t mode)
newstate |= FMODE_WRITE;
if (state->state == newstate)
goto out;
- if (nfs4_do_close(inode, state, newstate) == -EINPROGRESS)
+ if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
+ state->state = newstate;
+ goto out;
+ }
+ if (nfs4_do_close(inode, state, newstate) == 0)
return;
}
out:
nfs4_put_open_state(state);
- up(&owner->so_sema);
nfs4_put_state_owner(owner);
- up_read(&clp->cl_sem);
}
/*
@@ -546,19 +552,16 @@ __nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
* Return a compatible lock_state. If no initialized lock_state structure
* exists, return an uninitialized one.
*
- * The caller must be holding state->lock_sema
*/
static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
{
struct nfs4_lock_state *lsp;
struct nfs4_client *clp = state->owner->so_client;
- lsp = kmalloc(sizeof(*lsp), GFP_KERNEL);
+ lsp = kzalloc(sizeof(*lsp), GFP_KERNEL);
if (lsp == NULL)
return NULL;
- lsp->ls_flags = 0;
- lsp->ls_seqid = 0; /* arbitrary */
- memset(lsp->ls_stateid.data, 0, sizeof(lsp->ls_stateid.data));
+ lsp->ls_seqid.sequence = &state->owner->so_sequence;
atomic_set(&lsp->ls_count, 1);
lsp->ls_owner = fl_owner;
spin_lock(&clp->cl_lock);
@@ -572,7 +575,7 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f
* Return a compatible lock_state. If no initialized lock_state structure
* exists, return an uninitialized one.
*
- * The caller must be holding state->lock_sema and clp->cl_sem
+ * The caller must be holding clp->cl_sem
*/
static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner)
{
@@ -605,7 +608,7 @@ static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_
* Release reference to lock_state, and free it if we see that
* it is no longer in use
*/
-static void nfs4_put_lock_state(struct nfs4_lock_state *lsp)
+void nfs4_put_lock_state(struct nfs4_lock_state *lsp)
{
struct nfs4_state *state;
@@ -673,29 +676,94 @@ void nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t f
nfs4_put_lock_state(lsp);
}
-/*
-* Called with state->lock_sema and clp->cl_sem held.
-*/
-void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *lsp)
+struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter)
{
- if (status == NFS_OK || seqid_mutating_err(-status))
- lsp->ls_seqid++;
+ struct nfs_seqid *new;
+
+ new = kmalloc(sizeof(*new), GFP_KERNEL);
+ if (new != NULL) {
+ new->sequence = counter;
+ INIT_LIST_HEAD(&new->list);
+ }
+ return new;
+}
+
+void nfs_free_seqid(struct nfs_seqid *seqid)
+{
+ struct rpc_sequence *sequence = seqid->sequence->sequence;
+
+ if (!list_empty(&seqid->list)) {
+ spin_lock(&sequence->lock);
+ list_del(&seqid->list);
+ spin_unlock(&sequence->lock);
+ }
+ rpc_wake_up_next(&sequence->wait);
+ kfree(seqid);
}
/*
-* Called with sp->so_sema and clp->cl_sem held.
-*
-* Increment the seqid if the OPEN/OPEN_DOWNGRADE/CLOSE succeeded, or
-* failed with a seqid incrementing error -
-* see comments nfs_fs.h:seqid_mutating_error()
-*/
-void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp)
-{
- if (status == NFS_OK || seqid_mutating_err(-status))
- sp->so_seqid++;
- /* If the server returns BAD_SEQID, unhash state_owner here */
- if (status == -NFS4ERR_BAD_SEQID)
+ * Increment the seqid if the OPEN/OPEN_DOWNGRADE/CLOSE succeeded, or
+ * failed with a seqid incrementing error -
+ * see comments nfs_fs.h:seqid_mutating_error()
+ */
+static inline void nfs_increment_seqid(int status, struct nfs_seqid *seqid)
+{
+ switch (status) {
+ case 0:
+ break;
+ case -NFS4ERR_BAD_SEQID:
+ case -NFS4ERR_STALE_CLIENTID:
+ case -NFS4ERR_STALE_STATEID:
+ case -NFS4ERR_BAD_STATEID:
+ case -NFS4ERR_BADXDR:
+ case -NFS4ERR_RESOURCE:
+ case -NFS4ERR_NOFILEHANDLE:
+ /* Non-seqid mutating errors */
+ return;
+ };
+ /*
+ * Note: no locking needed as we are guaranteed to be first
+ * on the sequence list
+ */
+ seqid->sequence->counter++;
+}
+
+void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid)
+{
+ if (status == -NFS4ERR_BAD_SEQID) {
+ struct nfs4_state_owner *sp = container_of(seqid->sequence,
+ struct nfs4_state_owner, so_seqid);
nfs4_drop_state_owner(sp);
+ }
+ return nfs_increment_seqid(status, seqid);
+}
+
+/*
+ * Increment the seqid if the LOCK/LOCKU succeeded, or
+ * failed with a seqid incrementing error -
+ * see comments nfs_fs.h:seqid_mutating_error()
+ */
+void nfs_increment_lock_seqid(int status, struct nfs_seqid *seqid)
+{
+ return nfs_increment_seqid(status, seqid);
+}
+
+int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task)
+{
+ struct rpc_sequence *sequence = seqid->sequence->sequence;
+ int status = 0;
+
+ if (sequence->list.next == &seqid->list)
+ goto out;
+ spin_lock(&sequence->lock);
+ if (!list_empty(&sequence->list)) {
+ rpc_sleep_on(&sequence->wait, task, NULL, NULL);
+ status = -EAGAIN;
+ } else
+ list_add(&seqid->list, &sequence->list);
+ spin_unlock(&sequence->lock);
+out:
+ return status;
}
static int reclaimer(void *);
@@ -791,8 +859,6 @@ static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct n
if (state->state == 0)
continue;
status = ops->recover_open(sp, state);
- list_for_each_entry(lock, &state->lock_states, ls_locks)
- lock->ls_flags &= ~NFS_LOCK_INITIALIZED;
if (status >= 0) {
status = nfs4_reclaim_locks(ops, state);
if (status < 0)
@@ -831,6 +897,28 @@ out_err:
return status;
}
+static void nfs4_state_mark_reclaim(struct nfs4_client *clp)
+{
+ struct nfs4_state_owner *sp;
+ struct nfs4_state *state;
+ struct nfs4_lock_state *lock;
+
+ /* Reset all sequence ids to zero */
+ list_for_each_entry(sp, &clp->cl_state_owners, so_list) {
+ sp->so_seqid.counter = 0;
+ sp->so_seqid.flags = 0;
+ spin_lock(&sp->so_lock);
+ list_for_each_entry(state, &sp->so_states, open_states) {
+ list_for_each_entry(lock, &state->lock_states, ls_locks) {
+ lock->ls_seqid.counter = 0;
+ lock->ls_seqid.flags = 0;
+ lock->ls_flags &= ~NFS_LOCK_INITIALIZED;
+ }
+ }
+ spin_unlock(&sp->so_lock);
+ }
+}
+
static int reclaimer(void *ptr)
{
struct reclaimer_args *args = (struct reclaimer_args *)ptr;
@@ -864,6 +952,7 @@ restart_loop:
default:
ops = &nfs4_network_partition_recovery_ops;
};
+ nfs4_state_mark_reclaim(clp);
status = __nfs4_init_client(clp);
if (status)
goto out_error;
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 6c564ef9489..fbbace8a30c 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -95,6 +95,8 @@ static int nfs_stat_to_errno(int);
#define decode_getattr_maxsz (op_decode_hdr_maxsz + nfs4_fattr_maxsz)
#define encode_savefh_maxsz (op_encode_hdr_maxsz)
#define decode_savefh_maxsz (op_decode_hdr_maxsz)
+#define encode_restorefh_maxsz (op_encode_hdr_maxsz)
+#define decode_restorefh_maxsz (op_decode_hdr_maxsz)
#define encode_fsinfo_maxsz (op_encode_hdr_maxsz + 2)
#define decode_fsinfo_maxsz (op_decode_hdr_maxsz + 11)
#define encode_renew_maxsz (op_encode_hdr_maxsz + 3)
@@ -157,16 +159,20 @@ static int nfs_stat_to_errno(int);
op_decode_hdr_maxsz + 2)
#define NFS4_enc_write_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
- op_encode_hdr_maxsz + 8)
+ op_encode_hdr_maxsz + 8 + \
+ encode_getattr_maxsz)
#define NFS4_dec_write_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
- op_decode_hdr_maxsz + 4)
+ op_decode_hdr_maxsz + 4 + \
+ decode_getattr_maxsz)
#define NFS4_enc_commit_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
- op_encode_hdr_maxsz + 3)
+ op_encode_hdr_maxsz + 3 + \
+ encode_getattr_maxsz)
#define NFS4_dec_commit_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
- op_decode_hdr_maxsz + 2)
+ op_decode_hdr_maxsz + 2 + \
+ decode_getattr_maxsz)
#define NFS4_enc_open_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
op_encode_hdr_maxsz + \
@@ -196,17 +202,21 @@ static int nfs_stat_to_errno(int);
#define NFS4_enc_open_downgrade_sz \
(compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
- op_encode_hdr_maxsz + 7)
+ op_encode_hdr_maxsz + 7 + \
+ encode_getattr_maxsz)
#define NFS4_dec_open_downgrade_sz \
(compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
- op_decode_hdr_maxsz + 4)
+ op_decode_hdr_maxsz + 4 + \
+ decode_getattr_maxsz)
#define NFS4_enc_close_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
- op_encode_hdr_maxsz + 5)
+ op_encode_hdr_maxsz + 5 + \
+ encode_getattr_maxsz)
#define NFS4_dec_close_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
- op_decode_hdr_maxsz + 4)
+ op_decode_hdr_maxsz + 4 + \
+ decode_getattr_maxsz)
#define NFS4_enc_setattr_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
op_encode_hdr_maxsz + 4 + \
@@ -300,30 +310,44 @@ static int nfs_stat_to_errno(int);
decode_getfh_maxsz)
#define NFS4_enc_remove_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
- encode_remove_maxsz)
+ encode_remove_maxsz + \
+ encode_getattr_maxsz)
#define NFS4_dec_remove_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
- op_decode_hdr_maxsz + 5)
+ op_decode_hdr_maxsz + 5 + \
+ decode_getattr_maxsz)
#define NFS4_enc_rename_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
encode_savefh_maxsz + \
encode_putfh_maxsz + \
- encode_rename_maxsz)
+ encode_rename_maxsz + \
+ encode_getattr_maxsz + \
+ encode_restorefh_maxsz + \
+ encode_getattr_maxsz)
#define NFS4_dec_rename_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
decode_savefh_maxsz + \
decode_putfh_maxsz + \
- decode_rename_maxsz)
+ decode_rename_maxsz + \
+ decode_getattr_maxsz + \
+ decode_restorefh_maxsz + \
+ decode_getattr_maxsz)
#define NFS4_enc_link_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
encode_savefh_maxsz + \
encode_putfh_maxsz + \
- encode_link_maxsz)
+ encode_link_maxsz + \
+ decode_getattr_maxsz + \
+ encode_restorefh_maxsz + \
+ decode_getattr_maxsz)
#define NFS4_dec_link_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
decode_savefh_maxsz + \
decode_putfh_maxsz + \
- decode_link_maxsz)
+ decode_link_maxsz + \
+ decode_getattr_maxsz + \
+ decode_restorefh_maxsz + \
+ decode_getattr_maxsz)
#define NFS4_enc_symlink_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
encode_symlink_maxsz + \
@@ -336,14 +360,20 @@ static int nfs_stat_to_errno(int);
decode_getfh_maxsz)
#define NFS4_enc_create_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
+ encode_savefh_maxsz + \
encode_create_maxsz + \
+ encode_getfh_maxsz + \
encode_getattr_maxsz + \
- encode_getfh_maxsz)
+ encode_restorefh_maxsz + \
+ encode_getattr_maxsz)
#define NFS4_dec_create_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
+ decode_savefh_maxsz + \
decode_create_maxsz + \
+ decode_getfh_maxsz + \
decode_getattr_maxsz + \
- decode_getfh_maxsz)
+ decode_restorefh_maxsz + \
+ decode_getattr_maxsz)
#define NFS4_enc_pathconf_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
encode_getattr_maxsz)
@@ -602,10 +632,10 @@ static int encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg)
{
uint32_t *p;
- RESERVE_SPACE(8+sizeof(arg->stateid.data));
+ RESERVE_SPACE(8+sizeof(arg->stateid->data));
WRITE32(OP_CLOSE);
- WRITE32(arg->seqid);
- WRITEMEM(arg->stateid.data, sizeof(arg->stateid.data));
+ WRITE32(arg->seqid->sequence->counter);
+ WRITEMEM(arg->stateid->data, sizeof(arg->stateid->data));
return 0;
}
@@ -729,22 +759,18 @@ static int encode_lock(struct xdr_stream *xdr, const struct nfs_lockargs *arg)
WRITE64(arg->length);
WRITE32(opargs->new_lock_owner);
if (opargs->new_lock_owner){
- struct nfs_open_to_lock *ol = opargs->u.open_lock;
-
RESERVE_SPACE(40);
- WRITE32(ol->open_seqid);
- WRITEMEM(&ol->open_stateid, sizeof(ol->open_stateid));
- WRITE32(ol->lock_seqid);
- WRITE64(ol->lock_owner.clientid);
+ WRITE32(opargs->open_seqid->sequence->counter);
+ WRITEMEM(opargs->open_stateid->data, sizeof(opargs->open_stateid->data));
+ WRITE32(opargs->lock_seqid->sequence->counter);
+ WRITE64(opargs->lock_owner.clientid);
WRITE32(4);
- WRITE32(ol->lock_owner.id);
+ WRITE32(opargs->lock_owner.id);
}
else {
- struct nfs_exist_lock *el = opargs->u.exist_lock;
-
RESERVE_SPACE(20);
- WRITEMEM(&el->stateid, sizeof(el->stateid));
- WRITE32(el->seqid);
+ WRITEMEM(opargs->lock_stateid->data, sizeof(opargs->lock_stateid->data));
+ WRITE32(opargs->lock_seqid->sequence->counter);
}
return 0;
@@ -775,8 +801,8 @@ static int encode_locku(struct xdr_stream *xdr, const struct nfs_lockargs *arg)
RESERVE_SPACE(44);
WRITE32(OP_LOCKU);
WRITE32(arg->type);
- WRITE32(opargs->seqid);
- WRITEMEM(&opargs->stateid, sizeof(opargs->stateid));
+ WRITE32(opargs->seqid->sequence->counter);
+ WRITEMEM(opargs->stateid->data, sizeof(opargs->stateid->data));
WRITE64(arg->offset);
WRITE64(arg->length);
@@ -826,7 +852,7 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena
*/
RESERVE_SPACE(8);
WRITE32(OP_OPEN);
- WRITE32(arg->seqid);
+ WRITE32(arg->seqid->sequence->counter);
encode_share_access(xdr, arg->open_flags);
RESERVE_SPACE(16);
WRITE64(arg->clientid);
@@ -941,7 +967,7 @@ static int encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_con
RESERVE_SPACE(8+sizeof(arg->stateid.data));
WRITE32(OP_OPEN_CONFIRM);
WRITEMEM(arg->stateid.data, sizeof(arg->stateid.data));
- WRITE32(arg->seqid);
+ WRITE32(arg->seqid->sequence->counter);
return 0;
}
@@ -950,10 +976,10 @@ static int encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closea
{
uint32_t *p;
- RESERVE_SPACE(8+sizeof(arg->stateid.data));
+ RESERVE_SPACE(8+sizeof(arg->stateid->data));
WRITE32(OP_OPEN_DOWNGRADE);
- WRITEMEM(arg->stateid.data, sizeof(arg->stateid.data));
- WRITE32(arg->seqid);
+ WRITEMEM(arg->stateid->data, sizeof(arg->stateid->data));
+ WRITE32(arg->seqid->sequence->counter);
encode_share_access(xdr, arg->open_flags);
return 0;
}
@@ -1117,6 +1143,17 @@ static int encode_renew(struct xdr_stream *xdr, const struct nfs4_client *client
}
static int
+encode_restorefh(struct xdr_stream *xdr)
+{
+ uint32_t *p;
+
+ RESERVE_SPACE(4);
+ WRITE32(OP_RESTOREFH);
+
+ return 0;
+}
+
+static int
encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg)
{
uint32_t *p;
@@ -1296,14 +1333,18 @@ static int nfs4_xdr_enc_remove(struct rpc_rqst *req, uint32_t *p, const struct n
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 3,
};
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- if ((status = encode_putfh(&xdr, args->fh)) == 0)
- status = encode_remove(&xdr, args->name);
+ if ((status = encode_putfh(&xdr, args->fh)) != 0)
+ goto out;
+ if ((status = encode_remove(&xdr, args->name)) != 0)
+ goto out;
+ status = encode_getfattr(&xdr, args->bitmask);
+out:
return status;
}
@@ -1314,7 +1355,7 @@ static int nfs4_xdr_enc_rename(struct rpc_rqst *req, uint32_t *p, const struct n
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 4,
+ .nops = 7,
};
int status;
@@ -1326,7 +1367,13 @@ static int nfs4_xdr_enc_rename(struct rpc_rqst *req, uint32_t *p, const struct n
goto out;
if ((status = encode_putfh(&xdr, args->new_dir)) != 0)
goto out;
- status = encode_rename(&xdr, args->old_name, args->new_name);
+ if ((status = encode_rename(&xdr, args->old_name, args->new_name)) != 0)
+ goto out;
+ if ((status = encode_getfattr(&xdr, args->bitmask)) != 0)
+ goto out;
+ if ((status = encode_restorefh(&xdr)) != 0)
+ goto out;
+ status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
}
@@ -1338,7 +1385,7 @@ static int nfs4_xdr_enc_link(struct rpc_rqst *req, uint32_t *p, const struct nfs
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 4,
+ .nops = 7,
};
int status;
@@ -1350,7 +1397,13 @@ static int nfs4_xdr_enc_link(struct rpc_rqst *req, uint32_t *p, const struct nfs
goto out;
if ((status = encode_putfh(&xdr, args->dir_fh)) != 0)
goto out;
- status = encode_link(&xdr, args->name);
+ if ((status = encode_link(&xdr, args->name)) != 0)
+ goto out;
+ if ((status = encode_getfattr(&xdr, args->bitmask)) != 0)
+ goto out;
+ if ((status = encode_restorefh(&xdr)) != 0)
+ goto out;
+ status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
}
@@ -1362,7 +1415,7 @@ static int nfs4_xdr_enc_create(struct rpc_rqst *req, uint32_t *p, const struct n
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 4,
+ .nops = 7,
};
int status;
@@ -1370,10 +1423,16 @@ static int nfs4_xdr_enc_create(struct rpc_rqst *req, uint32_t *p, const struct n
encode_compound_hdr(&xdr, &hdr);
if ((status = encode_putfh(&xdr, args->dir_fh)) != 0)
goto out;
+ if ((status = encode_savefh(&xdr)) != 0)
+ goto out;
if ((status = encode_create(&xdr, args)) != 0)
goto out;
if ((status = encode_getfh(&xdr)) != 0)
goto out;
+ if ((status = encode_getfattr(&xdr, args->bitmask)) != 0)
+ goto out;
+ if ((status = encode_restorefh(&xdr)) != 0)
+ goto out;
status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
@@ -1412,7 +1471,7 @@ static int nfs4_xdr_enc_close(struct rpc_rqst *req, uint32_t *p, struct nfs_clos
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 3,
};
int status;
@@ -1422,6 +1481,9 @@ static int nfs4_xdr_enc_close(struct rpc_rqst *req, uint32_t *p, struct nfs_clos
if(status)
goto out;
status = encode_close(&xdr, args);
+ if (status != 0)
+ goto out;
+ status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
}
@@ -1433,15 +1495,21 @@ static int nfs4_xdr_enc_open(struct rpc_rqst *req, uint32_t *p, struct nfs_opena
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 4,
+ .nops = 7,
};
int status;
+ status = nfs_wait_on_sequence(args->seqid, req->rq_task);
+ if (status != 0)
+ goto out;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh);
if (status)
goto out;
+ status = encode_savefh(&xdr);
+ if (status)
+ goto out;
status = encode_open(&xdr, args);
if (status)
goto out;
@@ -1449,6 +1517,12 @@ static int nfs4_xdr_enc_open(struct rpc_rqst *req, uint32_t *p, struct nfs_opena
if (status)
goto out;
status = encode_getfattr(&xdr, args->bitmask);
+ if (status)
+ goto out;
+ status = encode_restorefh(&xdr);
+ if (status)
+ goto out;
+ status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
}
@@ -1464,6 +1538,9 @@ static int nfs4_xdr_enc_open_confirm(struct rpc_rqst *req, uint32_t *p, struct n
};
int status;
+ status = nfs_wait_on_sequence(args->seqid, req->rq_task);
+ if (status != 0)
+ goto out;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh);
@@ -1485,6 +1562,9 @@ static int nfs4_xdr_enc_open_noattr(struct rpc_rqst *req, uint32_t *p, struct nf
};
int status;
+ status = nfs_wait_on_sequence(args->seqid, req->rq_task);
+ if (status != 0)
+ goto out;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh);
@@ -1502,7 +1582,7 @@ static int nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req, uint32_t *p, struct
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 3,
};
int status;
@@ -1512,6 +1592,9 @@ static int nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req, uint32_t *p, struct
if (status)
goto out;
status = encode_open_downgrade(&xdr, args);
+ if (status != 0)
+ goto out;
+ status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
}
@@ -1525,8 +1608,15 @@ static int nfs4_xdr_enc_lock(struct rpc_rqst *req, uint32_t *p, struct nfs_locka
struct compound_hdr hdr = {
.nops = 2,
};
+ struct nfs_lock_opargs *opargs = args->u.lock;
int status;
+ status = nfs_wait_on_sequence(opargs->lock_seqid, req->rq_task);
+ if (status != 0)
+ goto out;
+ /* Do we need to do an open_to_lock_owner? */
+ if (opargs->lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)
+ opargs->new_lock_owner = 0;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, args->fh);
@@ -1713,7 +1803,7 @@ static int nfs4_xdr_enc_write(struct rpc_rqst *req, uint32_t *p, struct nfs_writ
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 3,
};
int status;
@@ -1723,6 +1813,9 @@ static int nfs4_xdr_enc_write(struct rpc_rqst *req, uint32_t *p, struct nfs_writ
if (status)
goto out;
status = encode_write(&xdr, args);
+ if (status)
+ goto out;
+ status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
}
@@ -1734,7 +1827,7 @@ static int nfs4_xdr_enc_commit(struct rpc_rqst *req, uint32_t *p, struct nfs_wri
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 3,
};
int status;
@@ -1744,6 +1837,9 @@ static int nfs4_xdr_enc_commit(struct rpc_rqst *req, uint32_t *p, struct nfs_wri
if (status)
goto out;
status = encode_commit(&xdr, args);
+ if (status)
+ goto out;
+ status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
}
@@ -2670,8 +2766,7 @@ static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_re
goto xdr_error;
status = verify_attr_len(xdr, savep, attrlen);
xdr_error:
- if (status != 0)
- printk(KERN_NOTICE "%s: xdr error %d!\n", __FUNCTION__, -status);
+ dprintk("%s: xdr returned %d!\n", __FUNCTION__, -status);
return status;
}
@@ -2704,8 +2799,7 @@ static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat)
status = verify_attr_len(xdr, savep, attrlen);
xdr_error:
- if (status != 0)
- printk(KERN_NOTICE "%s: xdr error %d!\n", __FUNCTION__, -status);
+ dprintk("%s: xdr returned %d!\n", __FUNCTION__, -status);
return status;
}
@@ -2730,8 +2824,7 @@ static int decode_pathconf(struct xdr_stream *xdr, struct nfs_pathconf *pathconf
status = verify_attr_len(xdr, savep, attrlen);
xdr_error:
- if (status != 0)
- printk(KERN_NOTICE "%s: xdr error %d!\n", __FUNCTION__, -status);
+ dprintk("%s: xdr returned %d!\n", __FUNCTION__, -status);
return status;
}
@@ -2787,13 +2880,10 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, cons
goto xdr_error;
if ((status = decode_attr_time_modify(xdr, bitmap, &fattr->mtime)) != 0)
goto xdr_error;
- if ((status = verify_attr_len(xdr, savep, attrlen)) == 0) {
+ if ((status = verify_attr_len(xdr, savep, attrlen)) == 0)
fattr->valid = NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3 | NFS_ATTR_FATTR_V4;
- fattr->timestamp = jiffies;
- }
xdr_error:
- if (status != 0)
- printk(KERN_NOTICE "%s: xdr error %d!\n", __FUNCTION__, -status);
+ dprintk("%s: xdr returned %d\n", __FUNCTION__, -status);
return status;
}
@@ -2826,8 +2916,7 @@ static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo)
status = verify_attr_len(xdr, savep, attrlen);
xdr_error:
- if (status != 0)
- printk(KERN_NOTICE "%s: xdr error %d!\n", __FUNCTION__, -status);
+ dprintk("%s: xdr returned %d!\n", __FUNCTION__, -status);
return status;
}
@@ -2890,8 +2979,8 @@ static int decode_lock(struct xdr_stream *xdr, struct nfs_lockres *res)
status = decode_op_hdr(xdr, OP_LOCK);
if (status == 0) {
- READ_BUF(sizeof(nfs4_stateid));
- COPYMEM(&res->u.stateid, sizeof(res->u.stateid));
+ READ_BUF(sizeof(res->u.stateid.data));
+ COPYMEM(res->u.stateid.data, sizeof(res->u.stateid.data));
} else if (status == -NFS4ERR_DENIED)
return decode_lock_denied(xdr, &res->u.denied);
return status;
@@ -2913,8 +3002,8 @@ static int decode_locku(struct xdr_stream *xdr, struct nfs_lockres *res)
status = decode_op_hdr(xdr, OP_LOCKU);
if (status == 0) {
- READ_BUF(sizeof(nfs4_stateid));
- COPYMEM(&res->u.stateid, sizeof(res->u.stateid));
+ READ_BUF(sizeof(res->u.stateid.data));
+ COPYMEM(res->u.stateid.data, sizeof(res->u.stateid.data));
}
return status;
}
@@ -2994,7 +3083,7 @@ static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
p += bmlen;
return decode_delegation(xdr, res);
xdr_error:
- printk(KERN_NOTICE "%s: xdr error!\n", __FUNCTION__);
+ dprintk("%s: Bitmap too large! Length = %u\n", __FUNCTION__, bmlen);
return -EIO;
}
@@ -3208,6 +3297,12 @@ static int decode_renew(struct xdr_stream *xdr)
return decode_op_hdr(xdr, OP_RENEW);
}
+static int
+decode_restorefh(struct xdr_stream *xdr)
+{
+ return decode_op_hdr(xdr, OP_RESTOREFH);
+}
+
static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
size_t *acl_len)
{
@@ -3243,7 +3338,8 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
if (attrlen <= *acl_len)
xdr_read_pages(xdr, attrlen);
*acl_len = attrlen;
- }
+ } else
+ status = -EOPNOTSUPP;
out:
return status;
@@ -3352,6 +3448,9 @@ static int nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp, uint32_t *p, stru
if (status)
goto out;
status = decode_open_downgrade(&xdr, res);
+ if (status != 0)
+ goto out;
+ decode_getfattr(&xdr, res->fattr, res->server);
out:
return status;
}
@@ -3424,7 +3523,7 @@ out:
/*
* Decode REMOVE response
*/
-static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_change_info *cinfo)
+static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_remove_res *res)
{
struct xdr_stream xdr;
struct compound_hdr hdr;
@@ -3433,8 +3532,11 @@ static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
goto out;
- if ((status = decode_putfh(&xdr)) == 0)
- status = decode_remove(&xdr, cinfo);
+ if ((status = decode_putfh(&xdr)) != 0)
+ goto out;
+ if ((status = decode_remove(&xdr, &res->cinfo)) != 0)
+ goto out;
+ decode_getfattr(&xdr, res->dir_attr, res->server);
out:
return status;
}
@@ -3457,7 +3559,14 @@ static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_
goto out;
if ((status = decode_putfh(&xdr)) != 0)
goto out;
- status = decode_rename(&xdr, &res->old_cinfo, &res->new_cinfo);
+ if ((status = decode_rename(&xdr, &res->old_cinfo, &res->new_cinfo)) != 0)
+ goto out;
+ /* Current FH is target directory */
+ if (decode_getfattr(&xdr, res->new_fattr, res->server) != 0)
+ goto out;
+ if ((status = decode_restorefh(&xdr)) != 0)
+ goto out;
+ decode_getfattr(&xdr, res->old_fattr, res->server);
out:
return status;
}
@@ -3465,7 +3574,7 @@ out:
/*
* Decode LINK response
*/
-static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_change_info *cinfo)
+static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_link_res *res)
{
struct xdr_stream xdr;
struct compound_hdr hdr;
@@ -3480,7 +3589,17 @@ static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_ch
goto out;
if ((status = decode_putfh(&xdr)) != 0)
goto out;
- status = decode_link(&xdr, cinfo);
+ if ((status = decode_link(&xdr, &res->cinfo)) != 0)
+ goto out;
+ /*
+ * Note order: OP_LINK leaves the directory as the current
+ * filehandle.
+ */
+ if (decode_getfattr(&xdr, res->dir_attr, res->server) != 0)
+ goto out;
+ if ((status = decode_restorefh(&xdr)) != 0)
+ goto out;
+ decode_getfattr(&xdr, res->fattr, res->server);
out:
return status;
}
@@ -3499,13 +3618,17 @@ static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_
goto out;
if ((status = decode_putfh(&xdr)) != 0)
goto out;
+ if ((status = decode_savefh(&xdr)) != 0)
+ goto out;
if ((status = decode_create(&xdr,&res->dir_cinfo)) != 0)
goto out;
if ((status = decode_getfh(&xdr, res->fh)) != 0)
goto out;
- status = decode_getfattr(&xdr, res->fattr, res->server);
- if (status == NFS4ERR_DELAY)
- status = 0;
+ if (decode_getfattr(&xdr, res->fattr, res->server) != 0)
+ goto out;
+ if ((status = decode_restorefh(&xdr)) != 0)
+ goto out;
+ decode_getfattr(&xdr, res->dir_fattr, res->server);
out:
return status;
}
@@ -3623,6 +3746,15 @@ static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_cl
if (status)
goto out;
status = decode_close(&xdr, res);
+ if (status != 0)
+ goto out;
+ /*
+ * Note: Server may do delete on close for this file
+ * in which case the getattr call will fail with
+ * an ESTALE error. Shouldn't be a problem,
+ * though, since fattr->valid will remain unset.
+ */
+ decode_getfattr(&xdr, res->fattr, res->server);
out:
return status;
}
@@ -3643,15 +3775,20 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_ope
status = decode_putfh(&xdr);
if (status)
goto out;
+ status = decode_savefh(&xdr);
+ if (status)
+ goto out;
status = decode_open(&xdr, res);
if (status)
goto out;
status = decode_getfh(&xdr, &res->fh);
if (status)
goto out;
- status = decode_getfattr(&xdr, res->f_attr, res->server);
- if (status == NFS4ERR_DELAY)
- status = 0;
+ if (decode_getfattr(&xdr, res->f_attr, res->server) != 0)
+ goto out;
+ if ((status = decode_restorefh(&xdr)) != 0)
+ goto out;
+ decode_getfattr(&xdr, res->dir_attr, res->server);
out:
return status;
}
@@ -3869,6 +4006,9 @@ static int nfs4_xdr_dec_write(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_wr
if (status)
goto out;
status = decode_write(&xdr, res);
+ if (status)
+ goto out;
+ decode_getfattr(&xdr, res->fattr, res->server);
if (!status)
status = res->count;
out:
@@ -3892,6 +4032,9 @@ static int nfs4_xdr_dec_commit(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_w
if (status)
goto out;
status = decode_commit(&xdr, res);
+ if (status)
+ goto out;
+ decode_getfattr(&xdr, res->fattr, res->server);
out:
return status;
}
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index be23c3fb926..a48a003242c 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -61,7 +61,7 @@ nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
int status;
dprintk("%s: call getattr\n", __FUNCTION__);
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
status = rpc_call(server->client_sys, NFSPROC_GETATTR, fhandle, fattr, 0);
dprintk("%s: reply getattr: %d\n", __FUNCTION__, status);
if (status)
@@ -93,7 +93,7 @@ nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
int status;
dprintk("NFS call getattr\n");
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
status = rpc_call(server->client, NFSPROC_GETATTR,
fhandle, fattr, 0);
dprintk("NFS reply getattr: %d\n", status);
@@ -112,7 +112,7 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
int status;
dprintk("NFS call setattr\n");
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
status = rpc_call(NFS_CLIENT(inode), NFSPROC_SETATTR, &arg, fattr, 0);
if (status == 0)
nfs_setattr_update_inode(inode, sattr);
@@ -136,7 +136,7 @@ nfs_proc_lookup(struct inode *dir, struct qstr *name,
int status;
dprintk("NFS call lookup %s\n", name->name);
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
status = rpc_call(NFS_CLIENT(dir), NFSPROC_LOOKUP, &arg, &res, 0);
dprintk("NFS reply lookup: %d\n", status);
return status;
@@ -174,7 +174,7 @@ static int nfs_proc_read(struct nfs_read_data *rdata)
dprintk("NFS call read %d @ %Ld\n", rdata->args.count,
(long long) rdata->args.offset);
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0) {
nfs_refresh_inode(inode, fattr);
@@ -203,10 +203,10 @@ static int nfs_proc_write(struct nfs_write_data *wdata)
dprintk("NFS call write %d @ %Ld\n", wdata->args.count,
(long long) wdata->args.offset);
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0) {
- nfs_refresh_inode(inode, fattr);
+ nfs_post_op_update_inode(inode, fattr);
wdata->res.count = wdata->args.count;
wdata->verf.committed = NFS_FILE_SYNC;
}
@@ -216,7 +216,7 @@ static int nfs_proc_write(struct nfs_write_data *wdata)
static int
nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
- int flags)
+ int flags, struct nameidata *nd)
{
struct nfs_fh fhandle;
struct nfs_fattr fattr;
@@ -232,7 +232,7 @@ nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
};
int status;
- fattr.valid = 0;
+ nfs_fattr_init(&fattr);
dprintk("NFS call create %s\n", dentry->d_name.name);
status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0);
if (status == 0)
@@ -273,12 +273,13 @@ nfs_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
sattr->ia_size = new_encode_dev(rdev);/* get out your barf bag */
}
- fattr.valid = 0;
+ nfs_fattr_init(&fattr);
status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0);
+ nfs_mark_for_revalidate(dir);
if (status == -EINVAL && S_ISFIFO(mode)) {
sattr->ia_mode = mode;
- fattr.valid = 0;
+ nfs_fattr_init(&fattr);
status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0);
}
if (status == 0)
@@ -305,6 +306,7 @@ nfs_proc_remove(struct inode *dir, struct qstr *name)
dprintk("NFS call remove %s\n", name->name);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+ nfs_mark_for_revalidate(dir);
dprintk("NFS reply remove: %d\n", status);
return status;
@@ -331,8 +333,10 @@ nfs_proc_unlink_done(struct dentry *dir, struct rpc_task *task)
{
struct rpc_message *msg = &task->tk_msg;
- if (msg->rpc_argp)
+ if (msg->rpc_argp) {
+ nfs_mark_for_revalidate(dir->d_inode);
kfree(msg->rpc_argp);
+ }
return 0;
}
@@ -352,6 +356,8 @@ nfs_proc_rename(struct inode *old_dir, struct qstr *old_name,
dprintk("NFS call rename %s -> %s\n", old_name->name, new_name->name);
status = rpc_call(NFS_CLIENT(old_dir), NFSPROC_RENAME, &arg, NULL, 0);
+ nfs_mark_for_revalidate(old_dir);
+ nfs_mark_for_revalidate(new_dir);
dprintk("NFS reply rename: %d\n", status);
return status;
}
@@ -369,6 +375,7 @@ nfs_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
dprintk("NFS call link %s\n", name->name);
status = rpc_call(NFS_CLIENT(inode), NFSPROC_LINK, &arg, NULL, 0);
+ nfs_mark_for_revalidate(dir);
dprintk("NFS reply link: %d\n", status);
return status;
}
@@ -391,9 +398,10 @@ nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
if (path->len > NFS2_MAXPATHLEN)
return -ENAMETOOLONG;
dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
fhandle->size = 0;
status = rpc_call(NFS_CLIENT(dir), NFSPROC_SYMLINK, &arg, NULL, 0);
+ nfs_mark_for_revalidate(dir);
dprintk("NFS reply symlink: %d\n", status);
return status;
}
@@ -416,8 +424,9 @@ nfs_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr)
int status;
dprintk("NFS call mkdir %s\n", dentry->d_name.name);
- fattr.valid = 0;
+ nfs_fattr_init(&fattr);
status = rpc_call(NFS_CLIENT(dir), NFSPROC_MKDIR, &arg, &res, 0);
+ nfs_mark_for_revalidate(dir);
if (status == 0)
status = nfs_instantiate(dentry, &fhandle, &fattr);
dprintk("NFS reply mkdir: %d\n", status);
@@ -436,6 +445,7 @@ nfs_proc_rmdir(struct inode *dir, struct qstr *name)
dprintk("NFS call rmdir %s\n", name->name);
status = rpc_call(NFS_CLIENT(dir), NFSPROC_RMDIR, &arg, NULL, 0);
+ nfs_mark_for_revalidate(dir);
dprintk("NFS reply rmdir: %d\n", status);
return status;
}
@@ -484,7 +494,7 @@ nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
int status;
dprintk("NFS call statfs\n");
- stat->fattr->valid = 0;
+ nfs_fattr_init(stat->fattr);
status = rpc_call(server->client, NFSPROC_STATFS, fhandle, &fsinfo, 0);
dprintk("NFS reply statfs: %d\n", status);
if (status)
@@ -507,7 +517,7 @@ nfs_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,
int status;
dprintk("NFS call fsinfo\n");
- info->fattr->valid = 0;
+ nfs_fattr_init(info->fattr);
status = rpc_call(server->client, NFSPROC_STATFS, fhandle, &fsinfo, 0);
dprintk("NFS reply fsinfo: %d\n", status);
if (status)
@@ -579,7 +589,7 @@ nfs_write_done(struct rpc_task *task)
struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata;
if (task->tk_status >= 0)
- nfs_refresh_inode(data->inode, data->res.fattr);
+ nfs_post_op_update_inode(data->inode, data->res.fattr);
nfs_writeback_done(task);
}
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 9758ebd4990..43b03b19731 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -215,6 +215,7 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
data->res.fattr = &data->fattr;
data->res.count = count;
data->res.eof = 0;
+ nfs_fattr_init(&data->fattr);
NFS_PROTO(inode)->read_setup(data);
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 5130eda231d..819a65f5071 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -870,6 +870,7 @@ static void nfs_write_rpcsetup(struct nfs_page *req,
data->res.fattr = &data->fattr;
data->res.count = count;
data->res.verf = &data->verf;
+ nfs_fattr_init(&data->fattr);
NFS_PROTO(inode)->write_setup(data, how);
@@ -1237,6 +1238,7 @@ static void nfs_commit_rpcsetup(struct list_head *head,
data->res.count = 0;
data->res.fattr = &data->fattr;
data->res.verf = &data->verf;
+ nfs_fattr_init(&data->fattr);
NFS_PROTO(inode)->commit_setup(data, how);
diff --git a/fs/open.c b/fs/open.c
index f0d90cf0495..8d06ec911fd 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -739,7 +739,8 @@ asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group)
}
static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
- int flags, struct file *f)
+ int flags, struct file *f,
+ int (*open)(struct inode *, struct file *))
{
struct inode *inode;
int error;
@@ -761,11 +762,14 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
f->f_op = fops_get(inode->i_fop);
file_move(f, &inode->i_sb->s_files);
- if (f->f_op && f->f_op->open) {
- error = f->f_op->open(inode,f);
+ if (!open && f->f_op)
+ open = f->f_op->open;
+ if (open) {
+ error = open(inode, f);
if (error)
goto cleanup_all;
}
+
f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);
@@ -814,28 +818,75 @@ struct file *filp_open(const char * filename, int flags, int mode)
{
int namei_flags, error;
struct nameidata nd;
- struct file *f;
namei_flags = flags;
if ((namei_flags+1) & O_ACCMODE)
namei_flags++;
- if (namei_flags & O_TRUNC)
- namei_flags |= 2;
-
- error = -ENFILE;
- f = get_empty_filp();
- if (f == NULL)
- return ERR_PTR(error);
error = open_namei(filename, namei_flags, mode, &nd);
if (!error)
- return __dentry_open(nd.dentry, nd.mnt, flags, f);
+ return nameidata_to_filp(&nd, flags);
- put_filp(f);
return ERR_PTR(error);
}
EXPORT_SYMBOL(filp_open);
+/**
+ * lookup_instantiate_filp - instantiates the open intent filp
+ * @nd: pointer to nameidata
+ * @dentry: pointer to dentry
+ * @open: open callback
+ *
+ * Helper for filesystems that want to use lookup open intents and pass back
+ * a fully instantiated struct file to the caller.
+ * This function is meant to be called from within a filesystem's
+ * lookup method.
+ * Note that in case of error, nd->intent.open.file is destroyed, but the
+ * path information remains valid.
+ * If the open callback is set to NULL, then the standard f_op->open()
+ * filesystem callback is substituted.
+ */
+struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry,
+ int (*open)(struct inode *, struct file *))
+{
+ if (IS_ERR(nd->intent.open.file))
+ goto out;
+ if (IS_ERR(dentry))
+ goto out_err;
+ nd->intent.open.file = __dentry_open(dget(dentry), mntget(nd->mnt),
+ nd->intent.open.flags - 1,
+ nd->intent.open.file,
+ open);
+out:
+ return nd->intent.open.file;
+out_err:
+ release_open_intent(nd);
+ nd->intent.open.file = (struct file *)dentry;
+ goto out;
+}
+EXPORT_SYMBOL_GPL(lookup_instantiate_filp);
+
+/**
+ * nameidata_to_filp - convert a nameidata to an open filp.
+ * @nd: pointer to nameidata
+ * @flags: open flags
+ *
+ * Note that this function destroys the original nameidata
+ */
+struct file *nameidata_to_filp(struct nameidata *nd, int flags)
+{
+ struct file *filp;
+
+ /* Pick up the filp from the open intent */
+ filp = nd->intent.open.file;
+ /* Has the filesystem initialised the file for us? */
+ if (filp->f_dentry == NULL)
+ filp = __dentry_open(nd->dentry, nd->mnt, flags, filp, NULL);
+ else
+ path_release(nd);
+ return filp;
+}
+
struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
{
int error;
@@ -846,7 +897,7 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
if (f == NULL)
return ERR_PTR(error);
- return __dentry_open(dentry, mnt, flags, f);
+ return __dentry_open(dentry, mnt, flags, f, NULL);
}
EXPORT_SYMBOL(dentry_open);
diff --git a/fs/partitions/check.c b/fs/partitions/check.c
index 77e178f1316..9c06c5434ec 100644
--- a/fs/partitions/check.c
+++ b/fs/partitions/check.c
@@ -192,6 +192,7 @@ check_partition(struct gendisk *hd, struct block_device *bdev)
struct part_attribute {
struct attribute attr;
ssize_t (*show)(struct hd_struct *,char *);
+ ssize_t (*store)(struct hd_struct *,const char *, size_t);
};
static ssize_t
@@ -201,14 +202,33 @@ part_attr_show(struct kobject * kobj, struct attribute * attr, char * page)
struct part_attribute * part_attr = container_of(attr,struct part_attribute,attr);
ssize_t ret = 0;
if (part_attr->show)
- ret = part_attr->show(p,page);
+ ret = part_attr->show(p, page);
+ return ret;
+}
+static ssize_t
+part_attr_store(struct kobject * kobj, struct attribute * attr,
+ const char *page, size_t count)
+{
+ struct hd_struct * p = container_of(kobj,struct hd_struct,kobj);
+ struct part_attribute * part_attr = container_of(attr,struct part_attribute,attr);
+ ssize_t ret = 0;
+
+ if (part_attr->store)
+ ret = part_attr->store(p, page, count);
return ret;
}
static struct sysfs_ops part_sysfs_ops = {
.show = part_attr_show,
+ .store = part_attr_store,
};
+static ssize_t part_uevent_store(struct hd_struct * p,
+ const char *page, size_t count)
+{
+ kobject_hotplug(&p->kobj, KOBJ_ADD);
+ return count;
+}
static ssize_t part_dev_read(struct hd_struct * p, char *page)
{
struct gendisk *disk = container_of(p->kobj.parent,struct gendisk,kobj);
@@ -229,6 +249,10 @@ static ssize_t part_stat_read(struct hd_struct * p, char *page)
p->reads, (unsigned long long)p->read_sectors,
p->writes, (unsigned long long)p->write_sectors);
}
+static struct part_attribute part_attr_uevent = {
+ .attr = {.name = "uevent", .mode = S_IWUSR },
+ .store = part_uevent_store
+};
static struct part_attribute part_attr_dev = {
.attr = {.name = "dev", .mode = S_IRUGO },
.show = part_dev_read
@@ -247,6 +271,7 @@ static struct part_attribute part_attr_stat = {
};
static struct attribute * default_attrs[] = {
+ &part_attr_uevent.attr,
&part_attr_dev.attr,
&part_attr_start.attr,
&part_attr_size.attr,
@@ -430,7 +455,7 @@ void del_gendisk(struct gendisk *disk)
disk->flags &= ~GENHD_FL_UP;
unlink_gendisk(disk);
disk_stat_set_all(disk, 0);
- disk->stamp = disk->stamp_idle = 0;
+ disk->stamp = 0;
devfs_remove_disk(disk);
diff --git a/fs/proc/array.c b/fs/proc/array.c
index d84eecacbea..3e1239e4b30 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -438,7 +438,7 @@ static int do_task_stat(struct task_struct *task, char * buffer, int whole)
jiffies_to_clock_t(it_real_value),
start_time,
vsize,
- mm ? get_mm_counter(mm, rss) : 0, /* you might want to shift this left 3 */
+ mm ? get_mm_rss(mm) : 0,
rsslim,
mm ? mm->start_code : 0,
mm ? mm->end_code : 0,
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index c7ef3e48e35..d2fa42006d8 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -14,22 +14,41 @@
char *task_mem(struct mm_struct *mm, char *buffer)
{
unsigned long data, text, lib;
+ unsigned long hiwater_vm, total_vm, hiwater_rss, total_rss;
+
+ /*
+ * Note: to minimize their overhead, mm maintains hiwater_vm and
+ * hiwater_rss only when about to *lower* total_vm or rss. Any
+ * collector of these hiwater stats must therefore get total_vm
+ * and rss too, which will usually be the higher. Barriers? not
+ * worth the effort, such snapshots can always be inconsistent.
+ */
+ hiwater_vm = total_vm = mm->total_vm;
+ if (hiwater_vm < mm->hiwater_vm)
+ hiwater_vm = mm->hiwater_vm;
+ hiwater_rss = total_rss = get_mm_rss(mm);
+ if (hiwater_rss < mm->hiwater_rss)
+ hiwater_rss = mm->hiwater_rss;
data = mm->total_vm - mm->shared_vm - mm->stack_vm;
text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK)) >> 10;
lib = (mm->exec_vm << (PAGE_SHIFT-10)) - text;
buffer += sprintf(buffer,
+ "VmPeak:\t%8lu kB\n"
"VmSize:\t%8lu kB\n"
"VmLck:\t%8lu kB\n"
+ "VmHWM:\t%8lu kB\n"
"VmRSS:\t%8lu kB\n"
"VmData:\t%8lu kB\n"
"VmStk:\t%8lu kB\n"
"VmExe:\t%8lu kB\n"
"VmLib:\t%8lu kB\n"
"VmPTE:\t%8lu kB\n",
- (mm->total_vm - mm->reserved_vm) << (PAGE_SHIFT-10),
+ hiwater_vm << (PAGE_SHIFT-10),
+ (total_vm - mm->reserved_vm) << (PAGE_SHIFT-10),
mm->locked_vm << (PAGE_SHIFT-10),
- get_mm_counter(mm, rss) << (PAGE_SHIFT-10),
+ hiwater_rss << (PAGE_SHIFT-10),
+ total_rss << (PAGE_SHIFT-10),
data << (PAGE_SHIFT-10),
mm->stack_vm << (PAGE_SHIFT-10), text, lib,
(PTRS_PER_PTE*sizeof(pte_t)*mm->nr_ptes) >> 10);
@@ -44,13 +63,11 @@ unsigned long task_vsize(struct mm_struct *mm)
int task_statm(struct mm_struct *mm, int *shared, int *text,
int *data, int *resident)
{
- int rss = get_mm_counter(mm, rss);
-
- *shared = rss - get_mm_counter(mm, anon_rss);
+ *shared = get_mm_counter(mm, file_rss);
*text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK))
>> PAGE_SHIFT;
*data = mm->total_vm - mm->shared_vm;
- *resident = rss;
+ *resident = *shared + get_mm_counter(mm, anon_rss);
return mm->total_vm;
}
@@ -186,13 +203,14 @@ static void smaps_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
struct mem_size_stats *mss)
{
pte_t *pte, ptent;
+ spinlock_t *ptl;
unsigned long pfn;
struct page *page;
- pte = pte_offset_map(pmd, addr);
+ pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
do {
ptent = *pte;
- if (pte_none(ptent) || !pte_present(ptent))
+ if (!pte_present(ptent))
continue;
mss->resident += PAGE_SIZE;
@@ -213,8 +231,8 @@ static void smaps_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
mss->private_clean += PAGE_SIZE;
}
} while (pte++, addr += PAGE_SIZE, addr != end);
- pte_unmap(pte - 1);
- cond_resched_lock(&vma->vm_mm->page_table_lock);
+ pte_unmap_unlock(pte - 1, ptl);
+ cond_resched();
}
static inline void smaps_pmd_range(struct vm_area_struct *vma, pud_t *pud,
@@ -268,17 +286,11 @@ static inline void smaps_pgd_range(struct vm_area_struct *vma,
static int show_smap(struct seq_file *m, void *v)
{
struct vm_area_struct *vma = v;
- struct mm_struct *mm = vma->vm_mm;
struct mem_size_stats mss;
memset(&mss, 0, sizeof mss);
-
- if (mm) {
- spin_lock(&mm->page_table_lock);
+ if (vma->vm_mm)
smaps_pgd_range(vma, vma->vm_start, vma->vm_end, &mss);
- spin_unlock(&mm->page_table_lock);
- }
-
return show_map_internal(m, v, &mss);
}
@@ -407,7 +419,6 @@ static struct numa_maps *get_numa_maps(const struct vm_area_struct *vma)
for_each_node(i)
md->node[i] =0;
- spin_lock(&mm->page_table_lock);
for (vaddr = vma->vm_start; vaddr < vma->vm_end; vaddr += PAGE_SIZE) {
page = follow_page(mm, vaddr, 0);
if (page) {
@@ -422,8 +433,8 @@ static struct numa_maps *get_numa_maps(const struct vm_area_struct *vma)
md->anon++;
md->node[page_to_nid(page)]++;
}
+ cond_resched();
}
- spin_unlock(&mm->page_table_lock);
return md;
}
@@ -469,7 +480,7 @@ static int show_numa_map(struct seq_file *m, void *v)
seq_printf(m, " interleave={");
first = 1;
for_each_node(n) {
- if (test_bit(n, pol->v.nodes)) {
+ if (node_isset(n, pol->v.nodes)) {
if (!first)
seq_putc(m,',');
else
diff --git a/fs/reiserfs/fix_node.c b/fs/reiserfs/fix_node.c
index 2706e2adffa..45829889dcd 100644
--- a/fs/reiserfs/fix_node.c
+++ b/fs/reiserfs/fix_node.c
@@ -2022,7 +2022,7 @@ static int get_neighbors(struct tree_balance *p_s_tb, int n_h)
}
#ifdef CONFIG_REISERFS_CHECK
-void *reiserfs_kmalloc(size_t size, int flags, struct super_block *s)
+void *reiserfs_kmalloc(size_t size, gfp_t flags, struct super_block *s)
{
void *vp;
static size_t malloced;
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index d76ee6c4f9b..5f82352b97e 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -2842,7 +2842,7 @@ static int reiserfs_set_page_dirty(struct page *page)
* even in -o notail mode, we can't be sure an old mount without -o notail
* didn't create files with tails.
*/
-static int reiserfs_releasepage(struct page *page, int unused_gfp_flags)
+static int reiserfs_releasepage(struct page *page, gfp_t unused_gfp_flags)
{
struct inode *inode = page->mapping->host;
struct reiserfs_journal *j = SB_JOURNAL(inode->i_sb);
diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c
index 87ac9dc8b38..72e12079867 100644
--- a/fs/reiserfs/xattr.c
+++ b/fs/reiserfs/xattr.c
@@ -453,7 +453,7 @@ static struct page *reiserfs_get_page(struct inode *dir, unsigned long n)
struct page *page;
/* We can deadlock if we try to free dentries,
and an unlink/rmdir has just occured - GFP_NOFS avoids this */
- mapping->flags = (mapping->flags & ~__GFP_BITS_MASK) | GFP_NOFS;
+ mapping_set_gfp_mask(mapping, GFP_NOFS);
page = read_cache_page(mapping, n,
(filler_t *) mapping->a_ops->readpage, NULL);
if (!IS_ERR(page)) {
diff --git a/fs/xfs/linux-2.6/kmem.c b/fs/xfs/linux-2.6/kmem.c
index d2653b589b1..3c92162dc72 100644
--- a/fs/xfs/linux-2.6/kmem.c
+++ b/fs/xfs/linux-2.6/kmem.c
@@ -45,11 +45,11 @@
void *
-kmem_alloc(size_t size, gfp_t flags)
+kmem_alloc(size_t size, unsigned int __nocast flags)
{
- int retries = 0;
- unsigned int lflags = kmem_flags_convert(flags);
- void *ptr;
+ int retries = 0;
+ gfp_t lflags = kmem_flags_convert(flags);
+ void *ptr;
do {
if (size < MAX_SLAB_SIZE || retries > MAX_VMALLOCS)
@@ -67,7 +67,7 @@ kmem_alloc(size_t size, gfp_t flags)
}
void *
-kmem_zalloc(size_t size, gfp_t flags)
+kmem_zalloc(size_t size, unsigned int __nocast flags)
{
void *ptr;
@@ -90,7 +90,7 @@ kmem_free(void *ptr, size_t size)
void *
kmem_realloc(void *ptr, size_t newsize, size_t oldsize,
- gfp_t flags)
+ unsigned int __nocast flags)
{
void *new;
@@ -105,11 +105,11 @@ kmem_realloc(void *ptr, size_t newsize, size_t oldsize,
}
void *
-kmem_zone_alloc(kmem_zone_t *zone, gfp_t flags)
+kmem_zone_alloc(kmem_zone_t *zone, unsigned int __nocast flags)
{
- int retries = 0;
- unsigned int lflags = kmem_flags_convert(flags);
- void *ptr;
+ int retries = 0;
+ gfp_t lflags = kmem_flags_convert(flags);
+ void *ptr;
do {
ptr = kmem_cache_alloc(zone, lflags);
@@ -124,7 +124,7 @@ kmem_zone_alloc(kmem_zone_t *zone, gfp_t flags)
}
void *
-kmem_zone_zalloc(kmem_zone_t *zone, gfp_t flags)
+kmem_zone_zalloc(kmem_zone_t *zone, unsigned int __nocast flags)
{
void *ptr;
diff --git a/fs/xfs/linux-2.6/kmem.h b/fs/xfs/linux-2.6/kmem.h
index ee7010f085b..f4bb78c268c 100644
--- a/fs/xfs/linux-2.6/kmem.h
+++ b/fs/xfs/linux-2.6/kmem.h
@@ -81,9 +81,9 @@ typedef unsigned long xfs_pflags_t;
*(NSTATEP) = *(OSTATEP); \
} while (0)
-static __inline unsigned int kmem_flags_convert(gfp_t flags)
+static __inline gfp_t kmem_flags_convert(unsigned int __nocast flags)
{
- unsigned int lflags = __GFP_NOWARN; /* we'll report problems, if need be */
+ gfp_t lflags = __GFP_NOWARN; /* we'll report problems, if need be */
#ifdef DEBUG
if (unlikely(flags & ~(KM_SLEEP|KM_NOSLEEP|KM_NOFS|KM_MAYFAIL))) {
@@ -125,16 +125,16 @@ kmem_zone_destroy(kmem_zone_t *zone)
BUG();
}
-extern void *kmem_zone_zalloc(kmem_zone_t *, gfp_t);
-extern void *kmem_zone_alloc(kmem_zone_t *, gfp_t);
+extern void *kmem_zone_zalloc(kmem_zone_t *, unsigned int __nocast);
+extern void *kmem_zone_alloc(kmem_zone_t *, unsigned int __nocast);
-extern void *kmem_alloc(size_t, gfp_t);
-extern void *kmem_realloc(void *, size_t, size_t, gfp_t);
-extern void *kmem_zalloc(size_t, gfp_t);
+extern void *kmem_alloc(size_t, unsigned int __nocast);
+extern void *kmem_realloc(void *, size_t, size_t, unsigned int __nocast);
+extern void *kmem_zalloc(size_t, unsigned int __nocast);
extern void kmem_free(void *, size_t);
typedef struct shrinker *kmem_shaker_t;
-typedef int (*kmem_shake_func_t)(int, unsigned int);
+typedef int (*kmem_shake_func_t)(int, gfp_t);
static __inline kmem_shaker_t
kmem_shake_register(kmem_shake_func_t sfunc)
@@ -149,7 +149,7 @@ kmem_shake_deregister(kmem_shaker_t shrinker)
}
static __inline int
-kmem_shake_allow(unsigned int gfp_mask)
+kmem_shake_allow(gfp_t gfp_mask)
{
return (gfp_mask & __GFP_WAIT);
}
diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c
index c6c077978fe..7aa39872470 100644
--- a/fs/xfs/linux-2.6/xfs_aops.c
+++ b/fs/xfs/linux-2.6/xfs_aops.c
@@ -1296,7 +1296,7 @@ linvfs_invalidate_page(
STATIC int
linvfs_release_page(
struct page *page,
- int gfp_mask)
+ gfp_t gfp_mask)
{
struct inode *inode = page->mapping->host;
int dirty, delalloc, unmapped, unwritten;
diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c
index e82cf72ac59..4cd46abe843 100644
--- a/fs/xfs/linux-2.6/xfs_buf.c
+++ b/fs/xfs/linux-2.6/xfs_buf.c
@@ -64,7 +64,7 @@
STATIC kmem_cache_t *pagebuf_zone;
STATIC kmem_shaker_t pagebuf_shake;
-STATIC int xfsbufd_wakeup(int, unsigned int);
+STATIC int xfsbufd_wakeup(int, gfp_t);
STATIC void pagebuf_delwri_queue(xfs_buf_t *, int);
STATIC struct workqueue_struct *xfslogd_workqueue;
@@ -181,8 +181,9 @@ set_page_region(
size_t offset,
size_t length)
{
- page->private |= page_region_mask(offset, length);
- if (page->private == ~0UL)
+ set_page_private(page,
+ page_private(page) | page_region_mask(offset, length));
+ if (page_private(page) == ~0UL)
SetPageUptodate(page);
}
@@ -194,7 +195,7 @@ test_page_region(
{
unsigned long mask = page_region_mask(offset, length);
- return (mask && (page->private & mask) == mask);
+ return (mask && (page_private(page) & mask) == mask);
}
/*
@@ -383,7 +384,7 @@ _pagebuf_lookup_pages(
size_t blocksize = bp->pb_target->pbr_bsize;
size_t size = bp->pb_count_desired;
size_t nbytes, offset;
- int gfp_mask = pb_to_gfp(flags);
+ gfp_t gfp_mask = pb_to_gfp(flags);
unsigned short page_count, i;
pgoff_t first;
loff_t end;
@@ -1749,8 +1750,8 @@ STATIC int xfsbufd_force_sleep;
STATIC int
xfsbufd_wakeup(
- int priority,
- unsigned int mask)
+ int priority,
+ gfp_t mask)
{
if (xfsbufd_force_sleep)
return 0;