]> err.no Git - linux-2.6/blobdiff - fs/nfs/inode.c
Merge master.kernel.org:/home/rmk/linux-2.6-arm
[linux-2.6] / fs / nfs / inode.c
index 469387920dd987d2cfc04b56f95a9c9131e91cee..5cb3345eb6940a7532457f447d7ea77d89b86113 100644 (file)
 
 #define NFSDBG_FACILITY                NFSDBG_VFS
 
+#define NFS_64_BIT_INODE_NUMBERS_ENABLED       1
+
+/* Default is to see 64-bit inode numbers */
+static int enable_ino64 = NFS_64_BIT_INODE_NUMBERS_ENABLED;
+
 static void nfs_invalidate_inode(struct inode *);
 static int nfs_update_inode(struct inode *, struct nfs_fattr *);
 
@@ -62,6 +67,25 @@ nfs_fattr_to_ino_t(struct nfs_fattr *fattr)
        return nfs_fileid_to_ino_t(fattr->fileid);
 }
 
+/**
+ * nfs_compat_user_ino64 - returns the user-visible inode number
+ * @fileid: 64-bit fileid
+ *
+ * This function returns a 32-bit inode number if the boot parameter
+ * nfs.enable_ino64 is zero.
+ */
+u64 nfs_compat_user_ino64(u64 fileid)
+{
+       int ino;
+
+       if (enable_ino64)
+               return fileid;
+       ino = fileid;
+       if (sizeof(ino) < sizeof(fileid))
+               ino ^= fileid >> (sizeof(fileid)-sizeof(ino)) * 8;
+       return ino;
+}
+
 int nfs_write_inode(struct inode *inode, int sync)
 {
        int ret;
@@ -168,7 +192,7 @@ void nfs_invalidate_atime(struct inode *inode)
  */
 static void nfs_invalidate_inode(struct inode *inode)
 {
-       set_bit(NFS_INO_STALE, &NFS_FLAGS(inode));
+       set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);
        nfs_zap_caches_locked(inode);
 }
 
@@ -205,7 +229,7 @@ nfs_init_locked(struct inode *inode, void *opaque)
        struct nfs_find_desc    *desc = (struct nfs_find_desc *)opaque;
        struct nfs_fattr        *fattr = desc->fattr;
 
-       NFS_FILEID(inode) = fattr->fileid;
+       set_nfs_fileid(inode, fattr->fileid);
        nfs_copy_fh(NFS_FH(inode), desc->fh);
        return 0;
 }
@@ -267,7 +291,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                        inode->i_fop = &nfs_dir_operations;
                        if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS)
                            && fattr->size <= NFS_LIMIT_READDIRPLUS)
-                               set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_FLAGS(inode));
+                               set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
                        /* Deal with crossing mountpoints */
                        if (!nfs_fsid_equal(&NFS_SB(sb)->fsid, &fattr->fsid)) {
                                if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
@@ -275,6 +299,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                                else
                                        inode->i_op = &nfs_mountpoint_inode_operations;
                                inode->i_fop = NULL;
+                               set_bit(NFS_INO_MOUNTPOINT, &nfsi->flags);
                        }
                } else if (S_ISLNK(inode->i_mode))
                        inode->i_op = &nfs_symlink_inode_operations;
@@ -333,6 +358,10 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
 
        nfs_inc_stats(inode, NFSIOS_VFSSETATTR);
 
+       /* skip mode change if it's just for clearing setuid/setgid */
+       if (attr->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
+               attr->ia_valid &= ~ATTR_MODE;
+
        if (attr->ia_valid & ATTR_SIZE) {
                if (!S_ISREG(inode->i_mode) || attr->ia_size == i_size_read(inode))
                        attr->ia_valid &= ~ATTR_SIZE;
@@ -405,15 +434,11 @@ static int nfs_wait_schedule(void *word)
  */
 static int nfs_wait_on_inode(struct inode *inode)
 {
-       struct rpc_clnt *clnt = NFS_CLIENT(inode);
        struct nfs_inode *nfsi = NFS_I(inode);
-       sigset_t oldmask;
        int error;
 
-       rpc_clnt_sigmask(clnt, &oldmask);
        error = wait_on_bit_lock(&nfsi->flags, NFS_INO_REVALIDATING,
-                                       nfs_wait_schedule, TASK_INTERRUPTIBLE);
-       rpc_clnt_sigunmask(clnt, &oldmask);
+                                       nfs_wait_schedule, TASK_KILLABLE);
 
        return error;
 }
@@ -433,9 +458,18 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
        int need_atime = NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATIME;
        int err;
 
-       /* Flush out writes to the server in order to update c/mtime */
-       if (S_ISREG(inode->i_mode))
+       /*
+        * Flush out writes to the server in order to update c/mtime.
+        *
+        * Hold the i_mutex to suspend application writes temporarily;
+        * this prevents long-running writing applications from blocking
+        * nfs_wb_nocommit.
+        */
+       if (S_ISREG(inode->i_mode)) {
+               mutex_lock(&inode->i_mutex);
                nfs_wb_nocommit(inode);
+               mutex_unlock(&inode->i_mutex);
+       }
 
        /*
         * We may force a getattr if the user cares about atime.
@@ -456,7 +490,7 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
                err = nfs_revalidate_inode(NFS_SERVER(inode), inode);
        if (!err) {
                generic_fillattr(inode, stat);
-               stat->ino = NFS_FILEID(inode);
+               stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode));
        }
        return err;
 }
@@ -472,6 +506,7 @@ static struct nfs_open_context *alloc_nfs_open_context(struct vfsmount *mnt, str
                ctx->cred = get_rpccred(cred);
                ctx->state = NULL;
                ctx->lockowner = current->files;
+               ctx->flags = 0;
                ctx->error = 0;
                ctx->dir_cookie = 0;
                atomic_set(&ctx->count, 1);
@@ -486,16 +521,24 @@ struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx)
        return ctx;
 }
 
-void put_nfs_open_context(struct nfs_open_context *ctx)
+static void __put_nfs_open_context(struct nfs_open_context *ctx, int wait)
 {
-       struct inode *inode = ctx->path.dentry->d_inode;
+       struct inode *inode;
+
+       if (ctx == NULL)
+               return;
 
+       inode = ctx->path.dentry->d_inode;
        if (!atomic_dec_and_lock(&ctx->count, &inode->i_lock))
                return;
        list_del(&ctx->list);
        spin_unlock(&inode->i_lock);
-       if (ctx->state != NULL)
-               nfs4_close_state(&ctx->path, ctx->state, ctx->mode);
+       if (ctx->state != NULL) {
+               if (wait)
+                       nfs4_close_sync(&ctx->path, ctx->state, ctx->mode);
+               else
+                       nfs4_close_state(&ctx->path, ctx->state, ctx->mode);
+       }
        if (ctx->cred != NULL)
                put_rpccred(ctx->cred);
        dput(ctx->path.dentry);
@@ -503,6 +546,16 @@ void put_nfs_open_context(struct nfs_open_context *ctx)
        kfree(ctx);
 }
 
+void put_nfs_open_context(struct nfs_open_context *ctx)
+{
+       __put_nfs_open_context(ctx, 0);
+}
+
+static void put_nfs_open_context_sync(struct nfs_open_context *ctx)
+{
+       __put_nfs_open_context(ctx, 1);
+}
+
 /*
  * Ensure that mmap has a recent RPC credential for use when writing out
  * shared pages
@@ -549,7 +602,7 @@ static void nfs_file_clear_open_context(struct file *filp)
                spin_lock(&inode->i_lock);
                list_move_tail(&ctx->list, &NFS_I(inode)->open_files);
                spin_unlock(&inode->i_lock);
-               put_nfs_open_context(ctx);
+               put_nfs_open_context_sync(ctx);
        }
 }
 
@@ -561,7 +614,7 @@ int nfs_open(struct inode *inode, struct file *filp)
        struct nfs_open_context *ctx;
        struct rpc_cred *cred;
 
-       cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
+       cred = rpc_lookup_cred();
        if (IS_ERR(cred))
                return PTR_ERR(cred);
        ctx = alloc_nfs_open_context(filp->f_path.mnt, filp->f_path.dentry, cred);
@@ -617,7 +670,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
                if (status == -ESTALE) {
                        nfs_zap_caches(inode);
                        if (!S_ISDIR(inode->i_mode))
-                               set_bit(NFS_INO_STALE, &NFS_FLAGS(inode));
+                               set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);
                }
                goto out;
        }
@@ -772,8 +825,9 @@ static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                        if (S_ISDIR(inode->i_mode))
                                nfsi->cache_validity |= NFS_INO_INVALID_DATA;
                }
-               if (inode->i_size == fattr->pre_size && nfsi->npages == 0)
-                       inode->i_size = fattr->size;
+               if (inode->i_size == nfs_size_to_loff_t(fattr->pre_size) &&
+                   nfsi->npages == 0)
+                       inode->i_size = nfs_size_to_loff_t(fattr->size);
        }
 }
 
@@ -790,6 +844,7 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat
 {
        struct nfs_inode *nfsi = NFS_I(inode);
        loff_t cur_size, new_isize;
+       unsigned long invalid = 0;
 
 
        /* Has the inode gone and changed behind our back? */
@@ -803,29 +858,36 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat
 
        if ((fattr->valid & NFS_ATTR_FATTR_V4) != 0 &&
                        nfsi->change_attr != fattr->change_attr)
-               nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
+               invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
 
        /* Verify a few of the more important attributes */
        if (!timespec_equal(&inode->i_mtime, &fattr->mtime))
-               nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
+               invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
 
        cur_size = i_size_read(inode);
        new_isize = nfs_size_to_loff_t(fattr->size);
        if (cur_size != new_isize && nfsi->npages == 0)
-               nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
+               invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
 
        /* Have any file permissions changed? */
        if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)
                        || inode->i_uid != fattr->uid
                        || inode->i_gid != fattr->gid)
-               nfsi->cache_validity |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL;
+               invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL;
 
        /* Has the link count changed? */
        if (inode->i_nlink != fattr->nlink)
-               nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
+               invalid |= NFS_INO_INVALID_ATTR;
 
        if (!timespec_equal(&inode->i_atime, &fattr->atime))
-               nfsi->cache_validity |= NFS_INO_INVALID_ATIME;
+               invalid |= NFS_INO_INVALID_ATIME;
+
+       if (invalid != 0)
+               nfsi->cache_validity |= invalid;
+       else
+               nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_ATIME
+                               | NFS_INO_REVAL_PAGECACHE);
 
        nfsi->read_cache_jiffies = fattr->time_start;
        return 0;
@@ -876,13 +938,12 @@ int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
 
-       if (fattr->valid & NFS_ATTR_FATTR)
-               return nfs_refresh_inode(inode, fattr);
-
        spin_lock(&inode->i_lock);
-       nfsi->cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
+       nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
+       if (S_ISDIR(inode->i_mode))
+               nfsi->cache_validity |= NFS_INO_INVALID_DATA;
        spin_unlock(&inode->i_lock);
-       return 0;
+       return nfs_refresh_inode(inode, fattr);
 }
 
 /**
@@ -930,7 +991,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
        struct nfs_server *server;
        struct nfs_inode *nfsi = NFS_I(inode);
        loff_t cur_isize, new_isize;
-       unsigned int    invalid = 0;
+       unsigned long invalid = 0;
        unsigned long now = jiffies;
 
        dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n",
@@ -948,8 +1009,9 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 
        server = NFS_SERVER(inode);
        /* Update the fsid? */
-       if (S_ISDIR(inode->i_mode)
-                       && !nfs_fsid_equal(&server->fsid, &fattr->fsid))
+       if (S_ISDIR(inode->i_mode) &&
+                       !nfs_fsid_equal(&server->fsid, &fattr->fsid) &&
+                       !test_bit(NFS_INO_MOUNTPOINT, &nfsi->flags))
                server->fsid = fattr->fsid;
 
        /*
@@ -970,7 +1032,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                        dprintk("NFS: mtime change on server for file %s/%ld\n",
                                        inode->i_sb->s_id, inode->i_ino);
                        invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
-                       nfsi->cache_change_attribute = now;
+                       if (S_ISDIR(inode->i_mode))
+                               nfs_force_lookup_revalidate(inode);
                }
                /* If ctime has changed we should definitely clear access+acl caches */
                if (!timespec_equal(&inode->i_ctime, &fattr->ctime))
@@ -979,7 +1042,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                dprintk("NFS: change_attr change on server for file %s/%ld\n",
                                inode->i_sb->s_id, inode->i_ino);
                invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
-               nfsi->cache_change_attribute = now;
+               if (S_ISDIR(inode->i_mode))
+                       nfs_force_lookup_revalidate(inode);
        }
 
        /* Check if our cached file size is stale */
@@ -1084,7 +1148,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 void nfs4_clear_inode(struct inode *inode)
 {
        /* If we are holding a delegation, return it! */
-       nfs_inode_return_delegation(inode);
+       nfs_inode_return_delegation_noreclaim(inode);
        /* First call standard NFS clear_inode() code */
        nfs_clear_inode(inode);
 }
@@ -1123,7 +1187,7 @@ static inline void nfs4_init_once(struct nfs_inode *nfsi)
 #endif
 }
 
-static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flags)
+static void init_once(struct kmem_cache * cachep, void *foo)
 {
        struct nfs_inode *nfsi = (struct nfs_inode *) foo;
 
@@ -1134,6 +1198,9 @@ static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flag
        INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC);
        nfsi->ncommit = 0;
        nfsi->npages = 0;
+       atomic_set(&nfsi->silly_count, 1);
+       INIT_HLIST_HEAD(&nfsi->silly_list);
+       init_waitqueue_head(&nfsi->waitqueue);
        nfs4_init_once(nfsi);
 }
 
@@ -1155,6 +1222,36 @@ static void nfs_destroy_inodecache(void)
        kmem_cache_destroy(nfs_inode_cachep);
 }
 
+struct workqueue_struct *nfsiod_workqueue;
+
+/*
+ * start up the nfsiod workqueue
+ */
+static int nfsiod_start(void)
+{
+       struct workqueue_struct *wq;
+       dprintk("RPC:       creating workqueue nfsiod\n");
+       wq = create_singlethread_workqueue("nfsiod");
+       if (wq == NULL)
+               return -ENOMEM;
+       nfsiod_workqueue = wq;
+       return 0;
+}
+
+/*
+ * Destroy the nfsiod workqueue
+ */
+static void nfsiod_stop(void)
+{
+       struct workqueue_struct *wq;
+
+       wq = nfsiod_workqueue;
+       if (wq == NULL)
+               return;
+       nfsiod_workqueue = NULL;
+       destroy_workqueue(wq);
+}
+
 /*
  * Initialize NFS
  */
@@ -1162,6 +1259,10 @@ static int __init init_nfs_fs(void)
 {
        int err;
 
+       err = nfsiod_start();
+       if (err)
+               goto out6;
+
        err = nfs_fs_proc_init();
        if (err)
                goto out5;
@@ -1208,6 +1309,8 @@ out3:
 out4:
        nfs_fs_proc_exit();
 out5:
+       nfsiod_stop();
+out6:
        return err;
 }
 
@@ -1223,11 +1326,13 @@ static void __exit exit_nfs_fs(void)
 #endif
        unregister_nfs_fs();
        nfs_fs_proc_exit();
+       nfsiod_stop();
 }
 
 /* Not quite true; I just maintain it */
 MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
 MODULE_LICENSE("GPL");
+module_param(enable_ino64, bool, 0644);
 
 module_init(init_nfs_fs)
 module_exit(exit_nfs_fs)