diff options
Diffstat (limited to 'fs')
50 files changed, 1262 insertions, 827 deletions
diff --git a/fs/inode.c b/fs/inode.c index 407bf392e20..258ec22bb29 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1205,8 +1205,6 @@ void generic_delete_inode(struct inode *inode) inodes_stat.nr_inodes--; spin_unlock(&inode_lock); - security_inode_delete(inode); - if (op->delete_inode) { void (*delete)(struct inode *) = op->delete_inode; /* Filesystems implementing their own diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index c03d4dce4d7..bc2ff593276 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -1889,7 +1889,7 @@ static struct kmem_cache *get_slab(size_t size) BUG_ON(i >= JBD2_MAX_SLABS); if (unlikely(i < 0)) i = 0; - BUG_ON(jbd2_slab[i] == 0); + BUG_ON(jbd2_slab[i] == NULL); return jbd2_slab[i]; } diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c index 9e2f6a72166..c92ea3b3ea5 100644 --- a/fs/jfs/jfs_dmap.c +++ b/fs/jfs/jfs_dmap.c @@ -2438,7 +2438,7 @@ dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc, int level) /* check if this is a control page update for an allocation. * if so, update the leaf to reflect the new leaf value using - * dbSplit(); otherwise (deallocation), use dbJoin() to udpate + * dbSplit(); otherwise (deallocation), use dbJoin() to update * the leaf with the new value. in addition to updating the * leaf, dbSplit() will also split the binary buddy system of * the leaves, if required, and bubble new values within the diff --git a/fs/libfs.c b/fs/libfs.c index ea9a6cc9b35..232bea425b0 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -547,6 +547,40 @@ ssize_t simple_read_from_buffer(void __user *to, size_t count, loff_t *ppos, } /** + * simple_write_to_buffer - copy data from user space to the buffer + * @to: the buffer to write to + * @available: the size of the buffer + * @ppos: the current position in the buffer + * @from: the user space buffer to read from + * @count: the maximum number of bytes to read + * + * The simple_write_to_buffer() function reads up to @count bytes from the user + * space address starting at @from into the buffer @to at offset @ppos. + * + * On success, the number of bytes written is returned and the offset @ppos is + * advanced by this number, or negative value is returned on error. + **/ +ssize_t simple_write_to_buffer(void *to, size_t available, loff_t *ppos, + const void __user *from, size_t count) +{ + loff_t pos = *ppos; + size_t res; + + if (pos < 0) + return -EINVAL; + if (pos >= available || !count) + return 0; + if (count > available - pos) + count = available - pos; + res = copy_from_user(to + pos, from, count); + if (res == count) + return -EFAULT; + count -= res; + *ppos = pos + count; + return count; +} + +/** * memory_read_from_buffer - copy data from the buffer * @to: the kernel space buffer to read to * @count: the maximum number of bytes to read @@ -864,6 +898,7 @@ EXPORT_SYMBOL(simple_statfs); EXPORT_SYMBOL(simple_sync_file); EXPORT_SYMBOL(simple_unlink); EXPORT_SYMBOL(simple_read_from_buffer); +EXPORT_SYMBOL(simple_write_to_buffer); EXPORT_SYMBOL(memory_read_from_buffer); EXPORT_SYMBOL(simple_transaction_set); EXPORT_SYMBOL(simple_transaction_get); diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c index 2396a85c0f5..72d1893ddd3 100644 --- a/fs/logfs/dir.c +++ b/fs/logfs/dir.c @@ -12,7 +12,7 @@ * Atomic dir operations * * Directory operations are by default not atomic. Dentries and Inodes are - * created/removed/altered in seperate operations. Therefore we need to do + * created/removed/altered in separate operations. Therefore we need to do * a small amount of journaling. * * Create, link, mkdir, mknod and symlink all share the same function to do diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h index 93b55f33724..1a9db84f8d8 100644 --- a/fs/logfs/logfs.h +++ b/fs/logfs/logfs.h @@ -707,7 +707,7 @@ static inline gc_level_t expand_level(u64 ino, level_t __level) u8 level = (__force u8)__level; if (ino == LOGFS_INO_MASTER) { - /* ifile has seperate areas */ + /* ifile has separate areas */ level += LOGFS_MAX_LEVELS; } return (__force gc_level_t)level; diff --git a/fs/logfs/logfs_abi.h b/fs/logfs/logfs_abi.h index f674725663f..ae960519c54 100644 --- a/fs/logfs/logfs_abi.h +++ b/fs/logfs/logfs_abi.h @@ -50,9 +50,9 @@ static inline void check_##type(void) \ * 12 - gc recycled blocks, long-lived data * 13 - replacement blocks, short-lived data * - * Levels 1-11 are necessary for robust gc operations and help seperate + * Levels 1-11 are necessary for robust gc operations and help separate * short-lived metadata from longer-lived file data. In the future, - * file data should get seperated into several segments based on simple + * file data should get separated into several segments based on simple * heuristics. Old data recycled during gc operation is expected to be * long-lived. New data is of uncertain life expectancy. New data * used to replace older blocks in existing files is expected to be @@ -117,7 +117,7 @@ static inline void check_##type(void) \ #define pure_ofs(ofs) (ofs & ~LOGFS_FULLY_POPULATED) /* - * LogFS needs to seperate data into levels. Each level is defined as the + * LogFS needs to separate data into levels. Each level is defined as the * maximal possible distance from the master inode (inode of the inode file). * Data blocks reside on level 0, 1x indirect block on level 1, etc. * Inodes reside on level 6, indirect blocks for the inode file on levels 7-11. @@ -204,7 +204,7 @@ SIZE_CHECK(logfs_segment_header, LOGFS_SEGMENT_HEADERSIZE); * @ds_crc: crc32 of structure starting with the next field * @ds_ifile_levels: maximum number of levels for ifile * @ds_iblock_levels: maximum number of levels for regular files - * @ds_data_levels: number of seperate levels for data + * @ds_data_levels: number of separate levels for data * @pad0: reserved, must be 0 * @ds_feature_incompat: incompatible filesystem features * @ds_feature_ro_compat: read-only compatible filesystem features @@ -456,7 +456,7 @@ enum logfs_vim { * @vim: life expectancy of data * * "Areas" are segments currently being used for writing. There is at least - * one area per GC level. Several may be used to seperate long-living from + * one area per GC level. Several may be used to separate long-living from * short-living data. If an area with unknown vim is encountered, it can * simply be closed. * The write buffer immediately follow this header. diff --git a/fs/namespace.c b/fs/namespace.c index f20cb57d106..88058de59c7 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -628,7 +628,6 @@ repeat: mnt->mnt_pinned = 0; spin_unlock(&vfsmount_lock); acct_auto_close_mnt(mnt); - security_sb_umount_close(mnt); goto repeat; } } @@ -1117,8 +1116,6 @@ static int do_umount(struct vfsmount *mnt, int flags) retval = 0; } spin_unlock(&vfsmount_lock); - if (retval) - security_sb_umount_busy(mnt); up_write(&namespace_sem); release_mounts(&umount_list); return retval; @@ -1435,17 +1432,10 @@ static int graft_tree(struct vfsmount *mnt, struct path *path) if (cant_mount(path->dentry)) goto out_unlock; - err = security_sb_check_sb(mnt, path); - if (err) - goto out_unlock; - - err = -ENOENT; if (!d_unlinked(path->dentry)) err = attach_recursive_mnt(mnt, path, NULL); out_unlock: mutex_unlock(&path->dentry->d_inode->i_mutex); - if (!err) - security_sb_post_addmount(mnt, path); return err; } @@ -1581,8 +1571,6 @@ static int do_remount(struct path *path, int flags, int mnt_flags, } up_write(&sb->s_umount); if (!err) { - security_sb_post_remount(path->mnt, flags, data); - spin_lock(&vfsmount_lock); touch_mnt_namespace(path->mnt->mnt_ns); spin_unlock(&vfsmount_lock); @@ -2277,7 +2265,6 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, touch_mnt_namespace(current->nsproxy->mnt_ns); spin_unlock(&vfsmount_lock); chroot_fs_refs(&root, &new); - security_sb_post_pivotroot(&root, &new); error = 0; path_put(&root_parent); path_put(&parent_path); diff --git a/fs/nfs/client.c b/fs/nfs/client.c index acc9c4943b8..7ec9b34a59f 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -934,7 +934,6 @@ static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, str } fsinfo.fattr = fattr; - nfs_fattr_init(fattr); error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo); if (error < 0) goto out_error; @@ -1047,13 +1046,18 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, struct nfs_fh *mntfh) { struct nfs_server *server; - struct nfs_fattr fattr; + struct nfs_fattr *fattr; int error; server = nfs_alloc_server(); if (!server) return ERR_PTR(-ENOMEM); + error = -ENOMEM; + fattr = nfs_alloc_fattr(); + if (fattr == NULL) + goto error; + /* Get a client representation */ error = nfs_init_server(server, data); if (error < 0) @@ -1064,7 +1068,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); /* Probe the root fh to retrieve its FSID */ - error = nfs_probe_fsinfo(server, mntfh, &fattr); + error = nfs_probe_fsinfo(server, mntfh, fattr); if (error < 0) goto error; if (server->nfs_client->rpc_ops->version == 3) { @@ -1077,14 +1081,14 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, server->namelen = NFS2_MAXNAMLEN; } - if (!(fattr.valid & NFS_ATTR_FATTR)) { - error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr); + if (!(fattr->valid & NFS_ATTR_FATTR)) { + error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr); if (error < 0) { dprintk("nfs_create_server: getattr error = %d\n", -error); goto error; } } - memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid)); + memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid)); dprintk("Server FSID: %llx:%llx\n", (unsigned long long) server->fsid.major, @@ -1096,9 +1100,11 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, spin_unlock(&nfs_client_lock); server->mount_time = jiffies; + nfs_free_fattr(fattr); return server; error: + nfs_free_fattr(fattr); nfs_free_server(server); return ERR_PTR(error); } @@ -1340,7 +1346,7 @@ error: struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, struct nfs_fh *mntfh) { - struct nfs_fattr fattr; + struct nfs_fattr *fattr; struct nfs_server *server; int error; @@ -1350,6 +1356,11 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, if (!server) return ERR_PTR(-ENOMEM); + error = -ENOMEM; + fattr = nfs_alloc_fattr(); + if (fattr == NULL) + goto error; + /* set up the general RPC client */ error = nfs4_init_server(server, data); if (error < 0) @@ -1364,7 +1375,7 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, goto error; /* Probe the root fh to retrieve its FSID */ - error = nfs4_path_walk(server, mntfh, data->nfs_server.export_path); + error = nfs4_get_rootfh(server, mntfh); if (error < 0) goto error; @@ -1375,7 +1386,7 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, nfs4_session_set_rwsize(server); - error = nfs_probe_fsinfo(server, mntfh, &fattr); + error = nfs_probe_fsinfo(server, mntfh, fattr); if (error < 0) goto error; @@ -1389,9 +1400,11 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, server->mount_time = jiffies; dprintk("<-- nfs4_create_server() = %p\n", server); + nfs_free_fattr(fattr); return server; error: + nfs_free_fattr(fattr); nfs_free_server(server); dprintk("<-- nfs4_create_server() = error %d\n", error); return ERR_PTR(error); @@ -1405,7 +1418,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, { struct nfs_client *parent_client; struct nfs_server *server, *parent_server; - struct nfs_fattr fattr; + struct nfs_fattr *fattr; int error; dprintk("--> nfs4_create_referral_server()\n"); @@ -1414,6 +1427,11 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, if (!server) return ERR_PTR(-ENOMEM); + error = -ENOMEM; + fattr = nfs_alloc_fattr(); + if (fattr == NULL) + goto error; + parent_server = NFS_SB(data->sb); parent_client = parent_server->nfs_client; @@ -1443,12 +1461,12 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); /* Probe the root fh to retrieve its FSID and filehandle */ - error = nfs4_path_walk(server, mntfh, data->mnt_path); + error = nfs4_get_rootfh(server, mntfh); if (error < 0) goto error; /* probe the filesystem info for this server filesystem */ - error = nfs_probe_fsinfo(server, mntfh, &fattr); + error = nfs_probe_fsinfo(server, mntfh, fattr); if (error < 0) goto error; @@ -1466,10 +1484,12 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, server->mount_time = jiffies; + nfs_free_fattr(fattr); dprintk("<-- nfs_create_referral_server() = %p\n", server); return server; error: + nfs_free_fattr(fattr); nfs_free_server(server); dprintk("<-- nfs4_create_referral_server() = error %d\n", error); return ERR_PTR(error); @@ -1485,7 +1505,7 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source, struct nfs_fattr *fattr) { struct nfs_server *server; - struct nfs_fattr fattr_fsinfo; + struct nfs_fattr *fattr_fsinfo; int error; dprintk("--> nfs_clone_server(,%llx:%llx,)\n", @@ -1496,6 +1516,11 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source, if (!server) return ERR_PTR(-ENOMEM); + error = -ENOMEM; + fattr_fsinfo = nfs_alloc_fattr(); + if (fattr_fsinfo == NULL) + goto out_free_server; + /* Copy data from the source */ server->nfs_client = source->nfs_client; atomic_inc(&server->nfs_client->cl_count); @@ -1512,7 +1537,7 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source, nfs_init_server_aclclient(server); /* probe the filesystem info for this server filesystem */ - error = nfs_probe_fsinfo(server, fh, &fattr_fsinfo); + error = nfs_probe_fsinfo(server, fh, fattr_fsinfo); if (error < 0) goto out_free_server; @@ -1534,10 +1559,12 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source, server->mount_time = jiffies; + nfs_free_fattr(fattr_fsinfo); dprintk("<-- nfs_clone_server() = %p\n", server); return server; out_free_server: + nfs_free_fattr(fattr_fsinfo); nfs_free_server(server); dprintk("<-- nfs_clone_server() = error %d\n", error); return ERR_PTR(error); diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index ea61d26e787..30163454397 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -213,7 +213,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct struct nfs_delegation *freeme = NULL; int status = 0; - delegation = kmalloc(sizeof(*delegation), GFP_KERNEL); + delegation = kmalloc(sizeof(*delegation), GFP_NOFS); if (delegation == NULL) return -ENOMEM; memcpy(delegation->stateid.data, res->delegation.data, diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index a7bb5c694aa..ee9a179ebdf 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -530,9 +530,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) nfs_readdir_descriptor_t my_desc, *desc = &my_desc; struct nfs_entry my_entry; - struct nfs_fh fh; - struct nfs_fattr fattr; - long res; + int res = -ENOMEM; dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n", dentry->d_parent->d_name.name, dentry->d_name.name, @@ -554,9 +552,11 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) my_entry.cookie = my_entry.prev_cookie = 0; my_entry.eof = 0; - my_entry.fh = &fh; - my_entry.fattr = &fattr; - nfs_fattr_init(&fattr); + my_entry.fh = nfs_alloc_fhandle(); + my_entry.fattr = nfs_alloc_fattr(); + if (my_entry.fh == NULL || my_entry.fattr == NULL) + goto out_alloc_failed; + desc->entry = &my_entry; nfs_block_sillyrename(dentry); @@ -598,7 +598,10 @@ out: nfs_unblock_sillyrename(dentry); if (res > 0) res = 0; - dfprintk(FILE, "NFS: readdir(%s/%s) returns %ld\n", +out_alloc_failed: + nfs_free_fattr(my_entry.fattr); + nfs_free_fhandle(my_entry.fh); + dfprintk(FILE, "NFS: readdir(%s/%s) returns %d\n", dentry->d_parent->d_name.name, dentry->d_name.name, res); return res; @@ -776,9 +779,9 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) struct inode *dir; struct inode *inode; struct dentry *parent; + struct nfs_fh *fhandle = NULL; + struct nfs_fattr *fattr = NULL; int error; - struct nfs_fh fhandle; - struct nfs_fattr fattr; parent = dget_parent(dentry); dir = parent->d_inode; @@ -811,14 +814,22 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) if (NFS_STALE(inode)) goto out_bad; - error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); + error = -ENOMEM; + fhandle = nfs_alloc_fhandle(); + fattr = nfs_alloc_fattr(); + if (fhandle == NULL || fattr == NULL) + goto out_error; + + error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr); if (error) goto out_bad; - if (nfs_compare_fh(NFS_FH(inode), &fhandle)) + if (nfs_compare_fh(NFS_FH(inode), fhandle)) goto out_bad; - if ((error = nfs_refresh_inode(inode, &fattr)) != 0) + if ((error = nfs_refresh_inode(inode, fattr)) != 0) goto out_bad; + nfs_free_fattr(fattr); + nfs_free_fhandle(fhandle); out_set_verifier: nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); out_valid: @@ -842,11 +853,21 @@ out_zap_parent: shrink_dcache_parent(dentry); } d_drop(dentry); + nfs_free_fattr(fattr); + nfs_free_fhandle(fhandle); dput(parent); dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is invalid\n", __func__, dentry->d_parent->d_name.name, dentry->d_name.name); return 0; +out_error: + nfs_free_fattr(fattr); + nfs_free_fhandle(fhandle); + dput(parent); + dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) lookup returned error %d\n", + __func__, dentry->d_parent->d_name.name, + dentry->d_name.name, error); + return error; } /* @@ -911,9 +932,9 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru struct dentry *res; struct dentry *parent; struct inode *inode = NULL; + struct nfs_fh *fhandle = NULL; + struct nfs_fattr *fattr = NULL; int error; - struct nfs_fh fhandle; - struct nfs_fattr fattr; dfprintk(VFS, "NFS: lookup(%s/%s)\n", dentry->d_parent->d_name.name, dentry->d_name.name); @@ -923,7 +944,6 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru if (dentry->d_name.len > NFS_SERVER(dir)->namelen) goto out; - res = ERR_PTR(-ENOMEM); dentry->d_op = NFS_PROTO(dir)->dentry_ops; /* @@ -936,17 +956,23 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru goto out; } + res = ERR_PTR(-ENOMEM); + fhandle = nfs_alloc_fhandle(); + fattr = nfs_alloc_fattr(); + if (fhandle == NULL || fattr == NULL) + goto out; + parent = dentry->d_parent; /* Protect against concurrent sillydeletes */ nfs_block_sillyrename(parent); - error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); + error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr); if (error == -ENOENT) goto no_entry; if (error < 0) { res = ERR_PTR(error); goto out_unblock_sillyrename; } - inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr); + inode = nfs_fhget(dentry->d_sb, fhandle, fattr); res = (struct dentry *)inode; if (IS_ERR(res)) goto out_unblock_sillyrename; @@ -962,6 +988,8 @@ no_entry: out_unblock_sillyrename: nfs_unblock_sillyrename(parent); out: + nfs_free_fattr(fattr); + nfs_free_fhandle(fhandle); return res; } @@ -1669,28 +1697,33 @@ static void nfs_access_free_entry(struct nfs_access_entry *entry) smp_mb__after_atomic_dec(); } +static void nfs_access_free_list(struct list_head *head) +{ + struct nfs_access_entry *cache; + + while (!list_empty(head)) { + cache = list_entry(head->next, struct nfs_access_entry, lru); + list_del(&cache->lru); + nfs_access_free_entry(cache); + } +} + int nfs_access_cache_shrinker(int nr_to_scan, gfp_t gfp_mask) { LIST_HEAD(head); struct nfs_inode *nfsi; struct nfs_access_entry *cache; -restart: + if ((gfp_mask & GFP_KERNEL) != GFP_KERNEL) + return (nr_to_scan == 0) ? 0 : -1; + spin_lock(&nfs_access_lru_lock); list_for_each_entry(nfsi, &nfs_access_lru_list, access_cache_inode_lru) { - struct rw_semaphore *s_umount; struct inode *inode; if (nr_to_scan-- == 0) break; - s_umount = &nfsi->vfs_inode.i_sb->s_umount; - if (!down_read_trylock(s_umount)) - continue; - inode = igrab(&nfsi->vfs_inode); - if (inode == NULL) { - up_read(s_umount); - continue; - } + inode = &nfsi->vfs_inode; spin_lock(&inode->i_lock); if (list_empty(&nfsi->access_cache_entry_lru)) goto remove_lru_entry; @@ -1704,61 +1737,47 @@ restart: else { remove_lru_entry: list_del_init(&nfsi->access_cache_inode_lru); + smp_mb__before_clear_bit(); clear_bit(NFS_INO_ACL_LRU_SET, &nfsi->flags); + smp_mb__after_clear_bit(); } - spin_unlock(&inode->i_lock); - spin_unlock(&nfs_access_lru_lock); - iput(inode); - up_read(s_umount); - goto restart; } spin_unlock(&nfs_access_lru_lock); - while (!list_empty(&head)) { - cache = list_entry(head.next, struct nfs_access_entry, lru); - list_del(&cache->lru); - nfs_access_free_entry(cache); - } + nfs_access_free_list(&head); return (atomic_long_read(&nfs_access_nr_entries) / 100) * sysctl_vfs_cache_pressure; } -static void __nfs_access_zap_cache(struct inode *inode) +static void __nfs_access_zap_cache(struct nfs_inode *nfsi, struct list_head *head) { - struct nfs_inode *nfsi = NFS_I(inode); struct rb_root *root_node = &nfsi->access_cache; - struct rb_node *n, *dispose = NULL; + struct rb_node *n; struct nfs_access_entry *entry; /* Unhook entries from the cache */ while ((n = rb_first(root_node)) != NULL) { entry = rb_entry(n, struct nfs_access_entry, rb_node); rb_erase(n, root_node); - list_del(&entry->lru); - n->rb_left = dispose; - dispose = n; + list_move(&entry->lru, head); } nfsi->cache_validity &= ~NFS_INO_INVALID_ACCESS; - spin_unlock(&inode->i_lock); - - /* Now kill them all! */ - while (dispose != NULL) { - n = dispose; - dispose = n->rb_left; - nfs_access_free_entry(rb_entry(n, struct nfs_access_entry, rb_node)); - } } void nfs_access_zap_cache(struct inode *inode) { + LIST_HEAD(head); + + if (test_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags) == 0) + return; /* Remove from global LRU init */ - if (test_and_clear_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags)) { - spin_lock(&nfs_access_lru_lock); + spin_lock(&nfs_access_lru_lock); + if (test_and_clear_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags)) list_del_init(&NFS_I(inode)->access_cache_inode_lru); - spin_unlock(&nfs_access_lru_lock); - } spin_lock(&inode->i_lock); - /* This will release the spinlock */ - __nfs_access_zap_cache(inode); + __nfs_access_zap_cache(NFS_I(inode), &head); + spin_unlock(&inode->i_lock); + spin_unlock(&nfs_access_lru_lock); + nfs_access_free_list(&head); } static struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, struct rpc_cred *cred) @@ -1809,8 +1828,8 @@ out_stale: nfs_access_free_entry(cache); return -ENOENT; out_zap: - /* This will release the spinlock */ - __nfs_access_zap_cache(inode); + spin_unlock(&inode->i_lock); + nfs_access_zap_cache(inode); return -ENOENT; } @@ -1865,9 +1884,11 @@ static void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *s smp_mb__after_atomic_inc(); /* Add inode to global LRU list */ - if (!test_and_set_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags)) { + if (!test_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags)) { spin_lock(&nfs_access_lru_lock); - list_add_tail(&NFS_I(inode)->access_cache_inode_lru, &nfs_access_lru_list); + if (!test_and_set_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags)) + list_add_tail(&NFS_I(inode)->access_cache_inode_lru, + &nfs_access_lru_list); spin_unlock(&nfs_access_lru_lock); } } diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 8d965bddb87..cac96bcc91e 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -161,14 +161,17 @@ static int nfs_revalidate_file_size(struct inode *inode, struct file *filp) struct nfs_server *server = NFS_SERVER(inode); struct nfs_inode *nfsi = NFS_I(inode); - if (server->flags & NFS_MOUNT_NOAC) - goto force_reval; + if (nfs_have_delegated_attributes(inode)) + goto out_noreval; + if (filp->f_flags & O_DIRECT) goto force_reval; - if (nfsi->npages != 0) - return 0; - if (!(nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE) && !nfs_attribute_timeout(inode)) - return 0; + if (nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE) + goto force_reval; + if (nfs_attribute_timeout(inode)) + goto force_reval; +out_noreval: + return 0; force_reval: return __nfs_revalidate_inode(server, inode); } diff --git a/fs/nfs/fscache.c b/fs/nfs/fscache.c index a6b16ed9322..ce153a6b3ae 100644 --- a/fs/nfs/fscache.c +++ b/fs/nfs/fscache.c @@ -467,7 +467,8 @@ int __nfs_readpages_from_fscache(struct nfs_open_context *ctx, struct list_head *pages, unsigned *nr_pages) { - int ret, npages = *nr_pages; + unsigned npages = *nr_pages; + int ret; dfprintk(FSCACHE, "NFS: nfs_getpages_from_fscache (0x%p/%u/0x%p)\n", NFS_I(inode)->fscache, npages, inode); diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c index b35d2a61606..7428f7d6273 100644 --- a/fs/nfs/getroot.c +++ b/fs/nfs/getroot.c @@ -78,159 +78,94 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh) { struct nfs_server *server = NFS_SB(sb); struct nfs_fsinfo fsinfo; - struct nfs_fattr fattr; - struct dentry *mntroot; + struct dentry *ret; struct inode *inode; int error; /* get the actual root for this mount */ - fsinfo.fattr = &fattr; + fsinfo.fattr = nfs_alloc_fattr(); + if (fsinfo.fattr == NULL) + return ERR_PTR(-ENOMEM); error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); if (error < 0) { dprintk("nfs_get_root: getattr error = %d\n", -error); - return ERR_PTR(error); + ret = ERR_PTR(error); + goto out; } inode = nfs_fhget(sb, mntfh, fsinfo.fattr); if (IS_ERR(inode)) { dprintk("nfs_get_root: get root inode failed\n"); - return ERR_CAST(inode); + ret = ERR_CAST(inode); + goto out; } error = nfs_superblock_set_dummy_root(sb, inode); - if (error != 0) - return ERR_PTR(error); + if (error != 0) { + ret = ERR_PTR(error); + goto out; + } /* root dentries normally start off anonymous and get spliced in later * if the dentry tree reaches them; however if the dentry already * exists, we'll pick it up at this point and use it as the root */ - mntroot = d_obtain_alias(inode); - if (IS_ERR(mntroot)) { + ret = d_obtain_alias(inode); + if (IS_ERR(ret)) { dprintk("nfs_get_root: get root dentry failed\n"); - return mntroot; + goto out; } - security_d_instantiate(mntroot, inode); - - if (!mntroot->d_op) - mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops; + security_d_instantiate(ret, inode); - return mntroot; + if (ret->d_op == NULL) + ret->d_op = server->nfs_client->rpc_ops->dentry_ops; +out: + nfs_free_fattr(fsinfo.fattr); + return ret; } #ifdef CONFIG_NFS_V4 -/* - * Do a simple pathwalk from the root FH of the server to the nominated target - * of the mountpoint - * - give error on symlinks - * - give error on ".." occurring in the path - * - follow traversals - */ -int nfs4_path_walk(struct nfs_server *server, - struct nfs_fh *mntfh, - const char *path) +int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh) { struct nfs_fsinfo fsinfo; - struct nfs_fattr fattr; - struct nfs_fh lastfh; - struct qstr name; - int ret; + int ret = -ENOMEM; - dprintk("--> nfs4_path_walk(,,%s)\n", path); + dprintk("--> nfs4_get_rootfh()\n"); - fsinfo.fattr = &fattr; - nfs_fattr_init(&fattr); - - /* Eat leading slashes */ - while (*path == '/') - path++; + fsinfo.fattr = nfs_alloc_fattr(); + if (fsinfo.fattr == NULL) + goto out; /* Start by getting the root filehandle from the server */ ret = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); if (ret < 0) { - dprintk("nfs4_get_root: getroot error = %d\n", -ret); - return ret; + dprintk("nfs4_get_rootfh: getroot error = %d\n", -ret); + goto out; } - if (!S_ISDIR(fattr.mode)) { - printk(KERN_ERR "nfs4_get_root:" + if (!(fsinfo.fattr->valid & NFS_ATTR_FATTR_MODE) + || !S_ISDIR(fsinfo.fattr->mode)) { + printk(KERN_ERR "nfs4_get_rootfh:" " getroot encountered non-directory\n"); - return -ENOTDIR; + ret = -ENOTDIR; + goto out; } - /* FIXME: It is quite valid for the server to return a referral here */ - if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) { - printk(KERN_ERR "nfs4_get_root:" + if (fsinfo.fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) { + printk(KERN_ERR "nfs4_get_rootfh:" " getroot obtained referral\n"); - return -EREMOTE; - } - -next_component: - dprintk("Next: %s\n", path); - - /* extract the next bit of the path */ - if (!*path) - goto path_walk_complete; - - name.name = path; - while (*path && *path != '/') - path++; - name.len = path - (const char *) name.name; - - if (name.len > NFS4_MAXNAMLEN) - return -ENAMETOOLONG; - -eat_dot_dir: - while (*path == '/') - path++; - - if (path[0] == '.' && (path[1] == '/' || !path[1])) { - path += 2; - goto eat_dot_dir; - } - - /* FIXME: Why shouldn't the user be able to use ".." in the path? */ - if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || !path[2]) - ) { - printk(KERN_ERR "nfs4_get_root:" - " Mount path contains reference to \"..\"\n"); - return -EINVAL; + ret = -EREMOTE; + goto out; } - /* lookup the next FH in the sequence */ - memcpy(&lastfh, mntfh, sizeof(lastfh)); - - dprintk("LookupFH: %*.*s [%s]\n", name.len, name.len, name.name, path); - - ret = server->nfs_client->rpc_ops->lookupfh(server, &lastfh, &name, - mntfh, &fattr); - if (ret < 0) { - dprintk("nfs4_get_root: getroot error = %d\n", -ret); - return ret; - } - - if (!S_ISDIR(fattr.mode)) { - printk(KERN_ERR "nfs4_get_root:" - " lookupfh encountered non-directory\n"); - return -ENOTDIR; - } - - /* FIXME: Referrals are quite valid here too */ - if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) { - printk(KERN_ERR "nfs4_get_root:" - " lookupfh obtained referral\n"); - return -EREMOTE; - } - - goto next_component; - -path_walk_complete: - memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid)); - dprintk("<-- nfs4_path_walk() = 0\n"); - return 0; + memcpy(&server->fsid, &fsinfo.fattr->fsid, sizeof(server->fsid)); +out: + nfs_free_fattr(fsinfo.fattr); + dprintk("<-- nfs4_get_rootfh() = %d\n", ret); + return ret; } /* @@ -239,8 +174,8 @@ path_walk_complete: struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh) { struct nfs_server *server = NFS_SB(sb); - struct nfs_fattr fattr; - struct dentry *mntroot; + struct nfs_fattr *fattr = NULL; + struct dentry *ret; struct inode *inode; int error; @@ -254,40 +189,50 @@ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh) return ERR_PTR(error); } + fattr = nfs_alloc_fattr(); + if (fattr == NULL) + return ERR_PTR(-ENOMEM);; + /* get the actual root for this mount */ - error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr); + error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr); if (error < 0) { dprintk("nfs_get_root: getattr error = %d\n", -error); - return ERR_PTR(error); + ret = ERR_PTR(error); + goto out; } - inode = nfs_fhget(sb, mntfh, &fattr); + inode = nfs_fhget(sb, mntfh, fattr); if (IS_ERR(inode)) { dprintk("nfs_get_root: get root inode failed\n"); - return ERR_CAST(inode); + ret = ERR_CAST(inode); + goto out; } error = nfs_superblock_set_dummy_root(sb, inode); - if (error != 0) - return ERR_PTR(error); + if (error != 0) { + ret = ERR_PTR(error); + goto out; + } /* root dentries normally start off anonymous and get spliced in later * if the dentry tree reaches them; however if the dentry already * exists, we'll pick it up at this point and use it as the root */ - mntroot = d_obtain_alias(inode); - if (IS_ERR(mntroot)) { + ret = d_obtain_alias(inode); + if (IS_ERR(ret)) { dprintk("nfs_get_root: get root dentry failed\n"); - return mntroot; + goto out; } - security_d_instantiate(mntroot, inode); + security_d_instantiate(ret, inode); - if (!mntroot->d_op) - mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops; + if (ret->d_op == NULL) + ret->d_op = server->nfs_client->rpc_ops->dentry_ops; +out: + nfs_free_fattr(fattr); dprintk("<-- nfs4_get_root()\n"); - return mntroot; + return ret; } #endif /* CONFIG_NFS_V4 */ diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 50a56edca0b..099b3518fee 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -393,8 +393,8 @@ int nfs_setattr(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; - struct nfs_fattr fattr; - int error; + struct nfs_fattr *fattr; + int error = -ENOMEM; nfs_inc_stats(inode, NFSIOS_VFSSETATTR); @@ -417,14 +417,20 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr) filemap_write_and_wait(inode->i_mapping); nfs_wb_all(inode); } + + fattr = nfs_alloc_fattr(); + if (fattr == NULL) + goto out; /* * 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); + error = NFS_PROTO(inode)->setattr(dentry, fattr, attr); if (error == 0) - nfs_refresh_inode(inode, &fattr); + nfs_refresh_inode(inode, fattr); + nfs_free_fattr(fattr); +out: return error; } @@ -682,7 +688,7 @@ int __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) { int status = -ESTALE; - struct nfs_fattr fattr; + struct nfs_fattr *fattr = NULL; struct nfs_inode *nfsi = NFS_I(inode); dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n", @@ -693,8 +699,13 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) if (NFS_STALE(inode)) goto out; + status = -ENOMEM; + fattr = nfs_alloc_fattr(); + if (fattr == NULL) + goto out; + nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE); - status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), &fattr); + status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), fattr); if (status != 0) { dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n", inode->i_sb->s_id, @@ -707,7 +718,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) goto out; } - status = nfs_refresh_inode(inode, &fattr); + status = nfs_refresh_inode(inode, fattr); if (status) { dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n", inode->i_sb->s_id, @@ -723,6 +734,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) (long long)NFS_FILEID(inode)); out: + nfs_free_fattr(fattr); return status; } @@ -730,9 +742,14 @@ int nfs_attribute_timeout(struct inode *inode) { struct nfs_inode *nfsi = NFS_I(inode); + return !time_in_range_open(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo); +} + +static int nfs_attribute_cache_expired(struct inode *inode) +{ if (nfs_have_delegated_attributes(inode)) return 0; - return !time_in_range_open(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo); + return nfs_attribute_timeout(inode); } /** @@ -745,7 +762,7 @@ int nfs_attribute_timeout(struct inode *inode) int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) { if (!(NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATTR) - && !nfs_attribute_timeout(inode)) + && !nfs_attribute_cache_expired(inode)) return NFS_STALE(inode) ? -ESTALE : 0; return __nfs_revalidate_inode(server, inode); } @@ -782,7 +799,8 @@ int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) int ret = 0; if ((nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE) - || nfs_attribute_timeout(inode) || NFS_STALE(inode)) { + || nfs_attribute_cache_expired(inode) + || NFS_STALE(inode)) { ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode); if (ret < 0) goto out; @@ -916,6 +934,26 @@ void nfs_fattr_init(struct nfs_fattr *fattr) fattr->gencount = nfs_inc_attr_generation_counter(); } +struct nfs_fattr *nfs_alloc_fattr(void) +{ + struct nfs_fattr *fattr; + + fattr = kmalloc(sizeof(*fattr), GFP_NOFS); + if (fattr != NULL) + nfs_fattr_init(fattr); + return fattr; +} + +struct nfs_fh *nfs_alloc_fhandle(void) +{ + struct nfs_fh *fh; + + fh = kmalloc(sizeof(struct nfs_fh), GFP_NOFS); + if (fh != NULL) + fh->size = 0; + return fh; +} + /** * nfs_inode_attrs_need_update - check if the inode attributes need updating * @inode - pointer to inode diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 11f82f03c5d..d8bd619e386 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -244,9 +244,7 @@ extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *); #ifdef CONFIG_NFS_V4 extern struct dentry *nfs4_get_root(struct super_block *, struct nfs_fh *); -extern int nfs4_path_walk(struct nfs_server *server, - struct nfs_fh *mntfh, - const char *path); +extern int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh); #endif /* read.c */ diff --git a/fs/nfs/iostat.h b/fs/nfs/iostat.h index 1d8d5c813b0..c5832487c45 100644 --- a/fs/nfs/iostat.h +++ b/fs/nfs/iostat.h @@ -36,14 +36,14 @@ static inline void nfs_inc_stats(const struct inode *inode, static inline void nfs_add_server_stats(const struct nfs_server *server, enum nfs_stat_bytecounters stat, - unsigned long addend) + long addend) { this_cpu_add(server->io_stats->bytes[stat], addend); } static inline void nfs_add_stats(const struct inode *inode, enum nfs_stat_bytecounters stat, - unsigned long addend) + long addend) { nfs_add_server_stats(NFS_SERVER(inode), stat, addend); } @@ -51,7 +51,7 @@ static inline void nfs_add_stats(const struct inode *inode, #ifdef CONFIG_NFS_FSCACHE static inline void nfs_add_fscache_stats(struct inode *inode, enum nfs_stat_fscachecounters stat, - unsigned long addend) + long addend) { this_cpu_add(NFS_SERVER(inode)->io_stats->fscache[stat], addend); } diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index 7888cf36022..db6aa3673cf 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c @@ -105,8 +105,8 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) struct vfsmount *mnt; struct nfs_server *server = NFS_SERVER(dentry->d_inode); struct dentry *parent; - struct nfs_fh fh; - struct nfs_fattr fattr; + struct nfs_fh *fh = NULL; + struct nfs_fattr *fattr = NULL; int err; dprintk("--> nfs_follow_mountpoint()\n"); @@ -115,6 +115,12 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) if (IS_ROOT(dentry)) goto out_err; + err = -ENOMEM; + fh = nfs_alloc_fhandle(); + fattr = nfs_alloc_fattr(); + if (fh == NULL || fattr == NULL) + goto out_err; + dprintk("%s: enter\n", __func__); dput(nd->path.dentry); nd->path.dentry = dget(dentry); @@ -123,16 +129,16 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) parent = dget_parent(nd->path.dentry); err = server->nfs_client->rpc_ops->lookup(parent->d_inode, &nd->path.dentry->d_name, - &fh, &fattr); + fh, fattr); dput(parent); if (err != 0) goto out_err; - if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) + if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) mnt = nfs_do_refmount(nd->path.mnt, nd->path.dentry); else - mnt = nfs_do_submount(nd->path.mnt, nd->path.dentry, &fh, - &fattr); + mnt = nfs_do_submount(nd->path.mnt, nd->path.dentry, fh, + fattr); err = PTR_ERR(mnt); if (IS_ERR(mnt)) goto out_err; @@ -151,6 +157,8 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) nd->path.dentry = dget(mnt->mnt_root); schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); out: + nfs_free_fattr(fattr); + nfs_free_fhandle(fh); dprintk("%s: done, returned %d\n", __func__, err); dprintk("<-- nfs_follow_mountpoint() = %d\n", err); diff --git a/fs/nfs/nfs3acl.c b/fs/nfs/nfs3acl.c index d150ae0c5ec..9f88c5f4c7e 100644 --- a/fs/nfs/nfs3acl.c +++ b/fs/nfs/nfs3acl.c @@ -185,7 +185,6 @@ static void nfs3_cache_acls(struct inode *inode, struct posix_acl *acl, struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type) { struct nfs_server *server = NFS_SERVER(inode); - struct nfs_fattr fattr; struct page *pages[NFSACL_MAXPAGES] = { }; struct nfs3_getaclargs args = { .fh = NFS_FH(inode), @@ -193,7 +192,7 @@ struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type) .pages = pages, }; struct nfs3_getaclres res = { - .fattr = &fattr, + 0 }; struct rpc_message msg = { .rpc_argp = &args, @@ -228,7 +227,10 @@ struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type) dprintk("NFS call getacl\n"); msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_GETACL]; - nfs_fattr_init(&fattr); + res.fattr = nfs_alloc_fattr(); + if (res.fattr == NULL) + return ERR_PTR(-ENOMEM); + status = rpc_call_sync(server->client_acl, &msg, 0); dprintk("NFS reply getacl: %d\n", status); @@ -238,7 +240,7 @@ struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type) switch (status) { case 0: - status = nfs_refresh_inode(inode, &fattr); + status = nfs_refresh_inode(inode, res.fattr); break; case -EPFNOSUPPORT: case -EPROTONOSUPPORT: @@ -278,6 +280,7 @@ struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type) getout: posix_acl_release(res.acl_access); posix_acl_release(res.acl_default); + nfs_free_fattr(res.fattr); if (status != 0) { posix_acl_release(acl); @@ -290,7 +293,7 @@ static int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl, struct posix_acl *dfacl) { struct nfs_server *server = NFS_SERVER(inode); - struct nfs_fattr fattr; + struct nfs_fattr *fattr; struct page *pages[NFSACL_MAXPAGES]; struct nfs3_setaclargs args = { .inode = inode, @@ -335,8 +338,13 @@ static int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl, } dprintk("NFS call setacl\n"); + status = -ENOMEM; + fattr = nfs_alloc_fattr(); + if (fattr == NULL) + goto out_freepages; + msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_SETACL]; - nfs_fattr_init(&fattr); + msg.rpc_resp = fattr; status = rpc_call_sync(server->client_acl, &msg, 0); nfs_access_zap_cache(inode); nfs_zap_acl_cache(inode); @@ -344,7 +352,7 @@ static int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl, switch (status) { case 0: - status = nfs_refresh_inode(inode, &fattr); + status = nfs_refresh_inode(inode, fattr); nfs3_cache_acls(inode, acl, dfacl); break; case -EPFNOSUPPORT: @@ -355,6 +363,7 @@ static int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl, case -ENOTSUPP: status = -EOPNOTSUPP; } + nfs_free_fattr(fattr); out_freepages: while (args.npages != 0) { args.npages--; diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index e701002694e..fabb4f2849a 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -144,14 +144,12 @@ static int nfs3_proc_lookup(struct inode *dir, struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr) { - struct nfs_fattr dir_attr; struct nfs3_diropargs arg = { .fh = NFS_FH(dir), .name = name->name, .len = name->len }; struct nfs3_diropres res = { - .dir_attr = &dir_attr, .fh = fhandle, .fattr = fattr }; @@ -163,29 +161,30 @@ nfs3_proc_lookup(struct inode *dir, struct qstr *name, int status; dprintk("NFS call lookup %s\n", name->name); - nfs_fattr_init(&dir_attr); + res.dir_attr = nfs_alloc_fattr(); + if (res.dir_attr == NULL) + return -ENOMEM; + nfs_fattr_init(fattr); status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - nfs_refresh_inode(dir, &dir_attr); + nfs_refresh_inode(dir, res.dir_attr); if (status >= 0 && !(fattr->valid & NFS_ATTR_FATTR)) { msg.rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR]; msg.rpc_argp = fhandle; msg.rpc_resp = fattr; status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); } + nfs_free_fattr(res.dir_attr); dprintk("NFS reply lookup: %d\n", status); return status; } static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry) { - struct nfs_fattr fattr; struct nfs3_accessargs arg = { .fh = NFS_FH(inode), }; - struct nfs3_accessres res = { - .fattr = &fattr, - }; + struct nfs3_accessres res; struct rpc_message msg = { .rpc_proc = &nfs3_procedures[NFS3PROC_ACCESS], .rpc_argp = &arg, @@ -193,7 +192,7 @@ static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry) .rpc_cred = entry->cred, }; int mode = entry->mask; - int status; + int status = -ENOMEM; dprintk("NFS call access\n"); @@ -210,9 +209,13 @@ 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); + + res.fattr = nfs_alloc_fattr(); + if (res.fattr == NULL) + goto out; + status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); - nfs_refresh_inode(inode, &fattr); + nfs_refresh_inode(inode, res.fattr); if (status == 0) { entry->mask = 0; if (res.access & NFS3_ACCESS_READ) @@ -222,6 +225,8 @@ static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry) if (res.access & (NFS3_ACCESS_LOOKUP|NFS3_ACCESS_EXECUTE)) entry->mask |= MAY_EXEC; } + nfs_free_fattr(res.fattr); +out: dprintk("NFS reply access: %d\n", status); return status; } @@ -229,7 +234,7 @@ static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry) static int nfs3_proc_readlink(struct inode *inode, struct page *page, unsigned int pgbase, unsigned int pglen) { - struct nfs_fattr fattr; + struct nfs_fattr *fattr; struct nfs3_readlinkargs args = { .fh = NFS_FH(inode), .pgbase = pgbase, @@ -239,14 +244,19 @@ static int nfs3_proc_readlink(struct inode *inode, struct page *page, struct rpc_message msg = { .rpc_proc = &nfs3_procedures[NFS3PROC_READLINK], .rpc_argp = &args, - .rpc_resp = &fattr, }; - int status; + int status = -ENOMEM; dprintk("NFS call readlink\n"); - nfs_fattr_init(&fattr); + fattr = nfs_alloc_fattr(); + if (fattr == NULL) + goto out; + msg.rpc_resp = fattr; + status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); - nfs_refresh_inode(inode, &fattr); + nfs_refresh_inode(inode, fattr); + nfs_free_fattr(fattr); +out: dprintk("NFS reply readlink: %d\n", status); return status; } @@ -396,12 +406,17 @@ nfs3_proc_remove(struct inode *dir, struct qstr *name) .rpc_argp = &arg, .rpc_resp = &res, }; - int status; + int status = -ENOMEM; dprintk("NFS call remove %s\n", name->name); - nfs_fattr_init(&res.dir_attr); + res.dir_attr = nfs_alloc_fattr(); + if (res.dir_attr == NULL) + goto out; + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - nfs_post_op_update_inode(dir, &res.dir_attr); + nfs_post_op_update_inode(dir, res.dir_attr); + nfs_free_fattr(res.dir_attr); +out: dprintk("NFS reply remove: %d\n", status); return status; } @@ -419,7 +434,7 @@ nfs3_proc_unlink_done(struct rpc_task *task, struct inode *dir) if (nfs3_async_handle_jukebox(task, dir)) return 0; res = task->tk_msg.rpc_resp; - nfs_post_op_update_inode(dir, &res->dir_attr); + nfs_post_op_update_inode(dir, res->dir_attr); return 1; } @@ -427,7 +442,6 @@ static int nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name, struct inode *new_dir, struct qstr *new_name) { - struct nfs_fattr old_dir_attr, new_dir_attr; struct nfs3_renameargs arg = { .fromfh = NFS_FH(old_dir), .fromname = old_name->name, @@ -436,23 +450,27 @@ nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name, .toname = new_name->name, .tolen = new_name->len }; - struct nfs3_renameres res = { - .fromattr = &old_dir_attr, - .toattr = &new_dir_attr - }; + struct nfs3_renameres res; struct rpc_message msg = { .rpc_proc = &nfs3_procedures[NFS3PROC_RENAME], .rpc_argp = &arg, .rpc_resp = &res, }; - int status; + int status = -ENOMEM; dprintk("NFS call rename %s -> %s\n", old_name->name, new_name->name); - nfs_fattr_init(&old_dir_attr); - nfs_fattr_init(&new_dir_attr); + + res.fromattr = nfs_alloc_fattr(); + res.toattr = nfs_alloc_fattr(); + if (res.fromattr == NULL || res.toattr == NULL) + goto out; + status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0); - nfs_post_op_update_inode(old_dir, &old_dir_attr); - nfs_post_op_update_inode(new_dir, &new_dir_attr); + nfs_post_op_update_inode(old_dir, res.fromattr); + nfs_post_op_update_inode(new_dir, res.toattr); +out: + nfs_free_fattr(res.toattr); + nfs_free_fattr(res.fromattr); dprintk("NFS reply rename: %d\n", status); return status; } @@ -460,30 +478,32 @@ nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name, static int nfs3_proc_link(struct inode *inode, struct inode *dir, struct qstr *name) { - struct nfs_fattr dir_attr, fattr; struct nfs3_linkargs arg = { .fromfh = NFS_FH(inode), .tofh = NFS_FH(dir), .toname = name->name, .tolen = name->len }; - struct nfs3_linkres res = { - .dir_attr = &dir_attr, - .fattr = &fattr - }; + struct nfs3_linkres res; struct rpc_message msg = { .rpc_proc = &nfs3_procedures[NFS3PROC_LINK], .rpc_argp = &arg, .rpc_resp = &res, }; - int status; + int status = -ENOMEM; dprintk("NFS call link %s\n", name->name); - nfs_fattr_init(&dir_attr); - nfs_fattr_init(&fattr); + res.fattr = nfs_alloc_fattr(); + res.dir_attr = nfs_alloc_fattr(); + if (res.fattr == NULL || res.dir_attr == NULL) + goto out; + status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); - nfs_post_op_update_inode(dir, &dir_attr); - nfs_post_op_update_inode(inode, &fattr); + nfs_post_op_update_inode(dir, res.dir_attr); + nfs_post_op_update_inode(inode, res.fattr); +out: + nfs_free_fattr(res.dir_attr); + nfs_free_fattr(res.fattr); dprintk("NFS reply link: %d\n", status); return status; } @@ -554,7 +574,7 @@ out: static int nfs3_proc_rmdir(struct inode *dir, struct qstr *name) { - struct nfs_fattr dir_attr; + struct nfs_fattr *dir_attr; struct nfs3_diropargs arg = { .fh = NFS_FH(dir), .name = name->name, @@ -563,14 +583,19 @@ nfs3_proc_rmdir(struct inode *dir, struct qstr *name) struct rpc_message msg = { .rpc_proc = &nfs3_procedures[NFS3PROC_RMDIR], .rpc_argp = &arg, - .rpc_resp = &dir_attr, }; - int status; + int status = -ENOMEM; dprintk("NFS call rmdir %s\n", name->name); - nfs_fattr_init(&dir_attr); + dir_attr = nfs_alloc_fattr(); + if (dir_attr == NULL) + goto out; + + msg.rpc_resp = dir_attr; status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - nfs_post_op_update_inode(dir, &dir_attr); + nfs_post_op_update_inode(dir, dir_attr); + nfs_free_fattr(dir_attr); +out: dprintk("NFS reply rmdir: %d\n", status); return status; } @@ -589,7 +614,6 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, u64 cookie, struct page *page, unsigned int count, int plus) { struct inode *dir = dentry->d_inode; - struct nfs_fattr dir_attr; __be32 *verf = NFS_COOKIEVERF(dir); struct nfs3_readdirargs arg = { .fh = NFS_FH(dir), @@ -600,7 +624,6 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, .pages = &page }; struct nfs3_readdirres res = { - .dir_attr = &dir_attr, .verf = verf, .plus = plus }; @@ -610,7 +633,7 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, .rpc_resp = &res, .rpc_cred = cred }; - int status; + int status = -ENOMEM; if (plus) msg.rpc_proc = &nfs3_procedures[NFS3PROC_READDIRPLUS]; @@ -618,12 +641,17 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, dprintk("NFS call readdir%s %d\n", plus? "plus" : "", (unsigned int) cookie); - nfs_fattr_init(&dir_attr); + res.dir_attr = nfs_alloc_fattr(); + if (res.dir_attr == NULL) + goto out; + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); nfs_invalidate_atime(dir); + nfs_refresh_inode(dir, res.dir_attr); - nfs_refresh_inode(dir, &dir_attr); + nfs_free_fattr(res.dir_attr); +out: dprintk("NFS reply readdir: %d\n", status); return status; } diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index 56a86f6ac8b..75dcfc7da36 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -762,7 +762,7 @@ nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr) static int nfs3_xdr_removeres(struct rpc_rqst *req, __be32 *p, struct nfs_removeres *res) { - return nfs3_xdr_wccstat(req, p, &res->dir_attr); + return nfs3_xdr_wccstat(req, p, res->dir_attr); } /* diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index a187200a7aa..c538c6106e1 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -206,14 +206,14 @@ extern ssize_t nfs4_listxattr(struct dentry *, char *, size_t); /* nfs4proc.c */ -extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *); -extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct rpc_cred *); +extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *, struct nfs4_setclientid_res *); +extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct nfs4_setclientid_res *arg, struct rpc_cred *); extern int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred); extern int nfs4_proc_async_renew(struct nfs_client *, struct rpc_cred *); extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *); extern int nfs4_init_clientid(struct nfs_client *, struct rpc_cred *); extern int nfs41_init_clientid(struct nfs_client *, struct rpc_cred *); -extern int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait); +extern int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait); 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 int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); @@ -286,7 +286,7 @@ 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_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t); -extern struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter); +extern struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask); 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); diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c index f071d12c613..3c2a1724fbd 100644 --- a/fs/nfs/nfs4namespace.c +++ b/fs/nfs/nfs4namespace.c @@ -115,6 +115,7 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, char *page, char *page2, const struct nfs4_fs_location *location) { + const size_t addr_bufsize = sizeof(struct sockaddr_storage); struct vfsmount *mnt = ERR_PTR(-ENOENT); char *mnt_path; unsigned int maxbuflen; @@ -126,9 +127,12 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, mountdata->mnt_path = mnt_path; maxbuflen = mnt_path - 1 - page2; + mountdata->addr = kmalloc(addr_bufsize, GFP_KERNEL); + if (mountdata->addr == NULL) + return ERR_PTR(-ENOMEM); + for (s = 0; s < location->nservers; s++) { const struct nfs4_string *buf = &location->servers[s]; - struct sockaddr_storage addr; if (buf->len <= 0 || buf->len >= maxbuflen) continue; @@ -137,11 +141,10 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, continue; mountdata->addrlen = nfs_parse_server_name(buf->data, buf->len, - (struct sockaddr *)&addr, sizeof(addr)); + mountdata->addr, addr_bufsize); if (mountdata->addrlen == 0) continue; - mountdata->addr = (struct sockaddr *)&addr; rpc_set_port(mountdata->addr, NFS_PORT); memcpy(page2, buf->data, buf->len); @@ -156,6 +159,7 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, if (!IS_ERR(mnt)) break; } + kfree(mountdata->addr); return mnt; } @@ -221,8 +225,8 @@ out: /* * nfs_do_refmount - handle crossing a referral on server + * @mnt_parent - mountpoint of referral * @dentry - dentry of referral - * @nd - nameidata info * */ struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 071fcedd517..70015dd60a9 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -70,6 +70,9 @@ static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinf static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *); static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr); static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr); +static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, + struct nfs_fattr *fattr, struct iattr *sattr, + struct nfs4_state *state); /* Prevent leaks of NFSv4 errors into userland */ static int nfs4_map_errors(int err) @@ -714,17 +717,18 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p) static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path, struct nfs4_state_owner *sp, fmode_t fmode, int flags, - const struct iattr *attrs) + const struct iattr *attrs, + gfp_t gfp_mask) { struct dentry *parent = dget_parent(path->dentry); struct inode *dir = parent->d_inode; struct nfs_server *server = NFS_SERVER(dir); struct nfs4_opendata *p; - p = kzalloc(sizeof(*p), GFP_KERNEL); + p = kzalloc(sizeof(*p), gfp_mask); if (p == NULL) goto err; - p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid); + p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid, gfp_mask); if (p->o_arg.seqid == NULL) goto err_free; path_get(path); @@ -1060,7 +1064,7 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context { struct nfs4_opendata *opendata; - opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, 0, NULL); + opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, 0, NULL, GFP_NOFS); if (opendata == NULL) return ERR_PTR(-ENOMEM); opendata->state = state; @@ -1648,7 +1652,7 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, fmode_t fmode, in if (path->dentry->d_inode != NULL) nfs4_return_incompatible_delegation(path->dentry->d_inode, fmode); status = -ENOMEM; - opendata = nfs4_opendata_alloc(path, sp, fmode, flags, sattr); + opendata = nfs4_opendata_alloc(path, sp, fmode, flags, sattr, GFP_KERNEL); if (opendata == NULL) goto err_put_state_owner; @@ -1659,15 +1663,24 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, fmode_t fmode, in if (status != 0) goto err_opendata_put; - if (opendata->o_arg.open_flags & O_EXCL) - nfs4_exclusive_attrset(opendata, sattr); - state = nfs4_opendata_to_nfs4_state(opendata); status = PTR_ERR(state); if (IS_ERR(state)) goto err_opendata_put; if (server->caps & NFS_CAP_POSIX_LOCK) set_bit(NFS_STATE_POSIX_LOCKS, &state->flags); + + if (opendata->o_arg.open_flags & O_EXCL) { + nfs4_exclusive_attrset(opendata, sattr); + + nfs_fattr_init(opendata->o_res.f_attr); + status = nfs4_do_setattr(state->inode, cred, + opendata->o_res.f_attr, sattr, + state); + if (status == 0) + nfs_setattr_update_inode(state->inode, sattr); + nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr); + } nfs4_opendata_put(opendata); nfs4_put_state_owner(sp); *res = state; @@ -1914,7 +1927,7 @@ static const struct rpc_call_ops nfs4_close_ops = { * * NOTE: Caller must be holding the sp->so_owner semaphore! */ -int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait) +int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait) { struct nfs_server *server = NFS_SERVER(state->inode); struct nfs4_closedata *calldata; @@ -1933,7 +1946,7 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait) }; int status = -ENOMEM; - calldata = kzalloc(sizeof(*calldata), GFP_KERNEL); + calldata = kzalloc(sizeof(*calldata), gfp_mask); if (calldata == NULL) goto out; calldata->inode = state->inode; @@ -1941,7 +1954,7 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait) calldata->arg.fh = NFS_FH(state->inode); calldata->arg.stateid = &state->open_stateid; /* Serialization for the sequence id */ - calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid); + calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid, gfp_mask); if (calldata->arg.seqid == NULL) goto out_free_calldata; calldata->arg.fmode = 0; @@ -2404,14 +2417,12 @@ static int nfs4_proc_lookup(struct inode *dir, struct qstr *name, struct nfs_fh static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry) { struct nfs_server *server = NFS_SERVER(inode); - struct nfs_fattr fattr; struct nfs4_accessargs args = { .fh = NFS_FH(inode), .bitmask = server->attr_bitmask, }; struct nfs4_accessres res = { .server = server, - .fattr = &fattr, }; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ACCESS], @@ -2438,7 +2449,11 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry if (mode & MAY_EXEC) args.access |= NFS4_ACCESS_EXECUTE; } - nfs_fattr_init(&fattr); + + res.fattr = nfs_alloc_fattr(); + if (res.fattr == NULL) + return -ENOMEM; + status = nfs4_call_sync(server, &msg, &args, &res, 0); if (!status) { entry->mask = 0; @@ -2448,8 +2463,9 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry entry->mask |= MAY_WRITE; if (res.access & (NFS4_ACCESS_LOOKUP|NFS4_ACCESS_EXECUTE)) entry->mask |= MAY_EXEC; - nfs_refresh_inode(inode, &fattr); + nfs_refresh_inode(inode, res.fattr); } + nfs_free_fattr(res.fattr); return status; } @@ -2562,13 +2578,6 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, } d_add(dentry, igrab(state->inode)); nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); - if (flags & O_EXCL) { - struct nfs_fattr fattr; - status = nfs4_do_setattr(state->inode, cred, &fattr, sattr, state); - if (status == 0) - nfs_setattr_update_inode(state->inode, sattr); - nfs_post_op_update_inode(state->inode, &fattr); - } if (status == 0 && (nd->flags & LOOKUP_OPEN) != 0) status = nfs4_intent_set_file(nd, &path, state, fmode); else @@ -2596,14 +2605,19 @@ static int _nfs4_proc_remove(struct inode *dir, struct qstr *name) .rpc_argp = &args, .rpc_resp = &res, }; - int status; + int status = -ENOMEM; + + res.dir_attr = nfs_alloc_fattr(); + if (res.dir_attr == NULL) + goto out; - nfs_fattr_init(&res.dir_attr); status = nfs4_call_sync(server, &msg, &args, &res, 1); if (status == 0) { update_changeattr(dir, &res.cinfo); - nfs_post_op_update_inode(dir, &res.dir_attr); + nfs_post_op_update_inode(dir, res.dir_attr); } + nfs_free_fattr(res.dir_attr); +out: return status; } @@ -2638,7 +2652,7 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir) if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN) return 0; update_changeattr(dir, &res->cinfo); - nfs_post_op_update_inode(dir, &res->dir_attr); + nfs_post_op_update_inode(dir, res->dir_attr); return 1; } @@ -2653,29 +2667,31 @@ static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *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 rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME], .rpc_argp = &arg, .rpc_resp = &res, }; - int status; + int status = -ENOMEM; - nfs_fattr_init(res.old_fattr); - nfs_fattr_init(res.new_fattr); - status = nfs4_call_sync(server, &msg, &arg, &res, 1); + res.old_fattr = nfs_alloc_fattr(); + res.new_fattr = nfs_alloc_fattr(); + if (res.old_fattr == NULL || res.new_fattr == NULL) + goto out; + status = nfs4_call_sync(server, &msg, &arg, &res, 1); 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); } +out: + nfs_free_fattr(res.new_fattr); + nfs_free_fattr(res.old_fattr); return status; } @@ -2702,28 +2718,30 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr * .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 rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LINK], .rpc_argp = &arg, .rpc_resp = &res, }; - int status; + int status = -ENOMEM; + + res.fattr = nfs_alloc_fattr(); + res.dir_attr = nfs_alloc_fattr(); + if (res.fattr == NULL || res.dir_attr == NULL) + goto out; - nfs_fattr_init(res.fattr); - nfs_fattr_init(res.dir_attr); status = nfs4_call_sync(server, &msg, &arg, &res, 1); if (!status) { update_changeattr(dir, &res.cinfo); nfs_post_op_update_inode(dir, res.dir_attr); nfs_post_op_update_inode(inode, res.fattr); } - +out: + nfs_free_fattr(res.dir_attr); + nfs_free_fattr(res.fattr); return status; } @@ -3146,23 +3164,31 @@ static void nfs4_proc_commit_setup(struct nfs_write_data *data, struct rpc_messa msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT]; } +struct nfs4_renewdata { + struct nfs_client *client; + unsigned long timestamp; +}; + /* * nfs4_proc_async_renew(): This is not one of the nfs_rpc_ops; it is a special * standalone procedure for queueing an asynchronous RENEW. */ -static void nfs4_renew_release(void *data) +static void nfs4_renew_release(void *calldata) { - struct nfs_client *clp = data; + struct nfs4_renewdata *data = calldata; + struct nfs_client *clp = data->client; if (atomic_read(&clp->cl_count) > 1) nfs4_schedule_state_renewal(clp); nfs_put_client(clp); + kfree(data); } -static void nfs4_renew_done(struct rpc_task *task, void *data) +static void nfs4_renew_done(struct rpc_task *task, void *calldata) { - struct nfs_client *clp = data; - unsigned long timestamp = task->tk_start; + struct nfs4_renewdata *data = calldata; + struct nfs_client *clp = data->client; + unsigned long timestamp = data->timestamp; if (task->tk_status < 0) { /* Unless we're shutting down, schedule state recovery! */ @@ -3188,11 +3214,17 @@ int nfs4_proc_async_renew(struct nfs_client *clp, struct rpc_cred *cred) .rpc_argp = clp, .rpc_cred = cred, }; + struct nfs4_renewdata *data; if (!atomic_inc_not_zero(&clp->cl_count)) return -EIO; + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + data->client = clp; + data->timestamp = jiffies; return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT, - &nfs4_renew_ops, clp); + &nfs4_renew_ops, data); } int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred) @@ -3494,7 +3526,9 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, return _nfs4_async_handle_error(task, server, server->nfs_client, state); } -int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short port, struct rpc_cred *cred) +int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, + unsigned short port, struct rpc_cred *cred, + struct nfs4_setclientid_res *res) { nfs4_verifier sc_verifier; struct nfs4_setclientid setclientid = { @@ -3504,7 +3538,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short po struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID], .rpc_argp = &setclientid, - .rpc_resp = clp, + .rpc_resp = res, .rpc_cred = cred, }; __be32 *p; @@ -3547,12 +3581,14 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short po return status; } -static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cred *cred) +static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp, + struct nfs4_setclientid_res *arg, + struct rpc_cred *cred) { struct nfs_fsinfo fsinfo; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID_CONFIRM], - .rpc_argp = clp, + .rpc_argp = arg, .rpc_resp = &fsinfo, .rpc_cred = cred, }; @@ -3570,12 +3606,14 @@ static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cre return status; } -int nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cred *cred) +int nfs4_proc_setclientid_confirm(struct nfs_client *clp, + struct nfs4_setclientid_res *arg, + struct rpc_cred *cred) { long timeout = 0; int err; do { - err = _nfs4_proc_setclientid_confirm(clp, cred); + err = _nfs4_proc_setclientid_confirm(clp, arg, cred); switch (err) { case 0: return err; @@ -3667,7 +3705,7 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co }; int status = 0; - data = kzalloc(sizeof(*data), GFP_KERNEL); + data = kzalloc(sizeof(*data), GFP_NOFS); if (data == NULL) return -ENOMEM; data->args.fhandle = &data->fh; @@ -3823,7 +3861,7 @@ static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl, struct nfs4_unlockdata *p; struct inode *inode = lsp->ls_state->inode; - p = kzalloc(sizeof(*p), GFP_KERNEL); + p = kzalloc(sizeof(*p), GFP_NOFS); if (p == NULL) return NULL; p->arg.fh = NFS_FH(inode); @@ -3961,7 +3999,7 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock * if (test_bit(NFS_DELEGATED_STATE, &state->flags)) goto out; lsp = request->fl_u.nfs4_fl.owner; - seqid = nfs_alloc_seqid(&lsp->ls_seqid); + seqid = nfs_alloc_seqid(&lsp->ls_seqid, GFP_KERNEL); status = -ENOMEM; if (seqid == NULL) goto out; @@ -3989,22 +4027,23 @@ struct nfs4_lockdata { }; static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl, - struct nfs_open_context *ctx, struct nfs4_lock_state *lsp) + struct nfs_open_context *ctx, struct nfs4_lock_state *lsp, + gfp_t gfp_mask) { struct nfs4_lockdata *p; struct inode *inode = lsp->ls_state->inode; struct nfs_server *server = NFS_SERVER(inode); - p = kzalloc(sizeof(*p), GFP_KERNEL); + p = kzalloc(sizeof(*p), gfp_mask); if (p == NULL) return NULL; p->arg.fh = NFS_FH(inode); p->arg.fl = &p->fl; - p->arg.open_seqid = nfs_alloc_seqid(&lsp->ls_state->owner->so_seqid); + p->arg.open_seqid = nfs_alloc_seqid(&lsp->ls_state->owner->so_seqid, gfp_mask); if (p->arg.open_seqid == NULL) goto out_free; - p->arg.lock_seqid = nfs_alloc_seqid(&lsp->ls_seqid); + p->arg.lock_seqid = nfs_alloc_seqid(&lsp->ls_seqid, gfp_mask); if (p->arg.lock_seqid == NULL) goto out_free_seqid; p->arg.lock_stateid = &lsp->ls_stateid; @@ -4158,7 +4197,8 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f dprintk("%s: begin!\n", __func__); data = nfs4_alloc_lockdata(fl, nfs_file_open_context(fl->fl_file), - fl->fl_u.nfs4_fl.owner); + fl->fl_u.nfs4_fl.owner, + recovery_type == NFS_LOCK_NEW ? GFP_KERNEL : GFP_NOFS); if (data == NULL) return -ENOMEM; if (IS_SETLKW(cmd)) @@ -4647,7 +4687,7 @@ static int nfs4_reset_slot_table(struct nfs4_slot_table *tbl, u32 max_reqs, if (max_reqs != tbl->max_slots) { ret = -ENOMEM; new = kmalloc(max_reqs * sizeof(struct nfs4_slot), - GFP_KERNEL); + GFP_NOFS); if (!new) goto out; ret = 0; @@ -4712,7 +4752,7 @@ static int nfs4_init_slot_table(struct nfs4_slot_table *tbl, dprintk("--> %s: max_reqs=%u\n", __func__, max_slots); - slot = kcalloc(max_slots, sizeof(struct nfs4_slot), GFP_KERNEL); + slot = kcalloc(max_slots, sizeof(struct nfs4_slot), GFP_NOFS); if (!slot) goto out; ret = 0; @@ -4761,7 +4801,7 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp) struct nfs4_session *session; struct nfs4_slot_table *tbl; - session = kzalloc(sizeof(struct nfs4_session), GFP_KERNEL); + session = kzalloc(sizeof(struct nfs4_session), GFP_NOFS); if (!session) return NULL; @@ -5105,8 +5145,8 @@ static int nfs41_proc_async_sequence(struct nfs_client *clp, if (!atomic_inc_not_zero(&clp->cl_count)) return -EIO; - args = kzalloc(sizeof(*args), GFP_KERNEL); - res = kzalloc(sizeof(*res), GFP_KERNEL); + args = kzalloc(sizeof(*args), GFP_NOFS); + res = kzalloc(sizeof(*res), GFP_NOFS); if (!args || !res) { kfree(args); kfree(res); @@ -5207,7 +5247,7 @@ static int nfs41_proc_reclaim_complete(struct nfs_client *clp) int status = -ENOMEM; dprintk("--> %s\n", __func__); - calldata = kzalloc(sizeof(*calldata), GFP_KERNEL); + calldata = kzalloc(sizeof(*calldata), GFP_NOFS); if (calldata == NULL) goto out; calldata->clp = clp; diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 6c5ed51f105..34acf5926fd 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -62,6 +62,7 @@ static LIST_HEAD(nfs4_clientid_list); int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred) { + struct nfs4_setclientid_res clid; unsigned short port; int status; @@ -69,11 +70,15 @@ int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred) if (clp->cl_addr.ss_family == AF_INET6) port = nfs_callback_tcpport6; - status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred); - if (status == 0) - status = nfs4_proc_setclientid_confirm(clp, cred); - if (status == 0) - nfs4_schedule_state_renewal(clp); + status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid); + if (status != 0) + goto out; + status = nfs4_proc_setclientid_confirm(clp, &clid, cred); + if (status != 0) + goto out; + clp->cl_clientid = clid.clientid; + nfs4_schedule_state_renewal(clp); +out: return status; } @@ -361,7 +366,7 @@ nfs4_alloc_state_owner(void) { struct nfs4_state_owner *sp; - sp = kzalloc(sizeof(*sp),GFP_KERNEL); + sp = kzalloc(sizeof(*sp),GFP_NOFS); if (!sp) return NULL; spin_lock_init(&sp->so_lock); @@ -435,7 +440,7 @@ nfs4_alloc_open_state(void) { struct nfs4_state *state; - state = kzalloc(sizeof(*state), GFP_KERNEL); + state = kzalloc(sizeof(*state), GFP_NOFS); if (!state) return NULL; atomic_set(&state->count, 1); @@ -537,7 +542,8 @@ void nfs4_put_open_state(struct nfs4_state *state) /* * Close the current file. */ -static void __nfs4_close(struct path *path, struct nfs4_state *state, fmode_t fmode, int wait) +static void __nfs4_close(struct path *path, struct nfs4_state *state, + fmode_t fmode, gfp_t gfp_mask, int wait) { struct nfs4_state_owner *owner = state->owner; int call_close = 0; @@ -578,17 +584,17 @@ static void __nfs4_close(struct path *path, struct nfs4_state *state, fmode_t fm nfs4_put_open_state(state); nfs4_put_state_owner(owner); } else - nfs4_do_close(path, state, wait); + nfs4_do_close(path, state, gfp_mask, wait); } void nfs4_close_state(struct path *path, struct nfs4_state *state, fmode_t fmode) { - __nfs4_close(path, state, fmode, 0); + __nfs4_close(path, state, fmode, GFP_NOFS, 0); } void nfs4_close_sync(struct path *path, struct nfs4_state *state, fmode_t fmode) { - __nfs4_close(path, state, fmode, 1); + __nfs4_close(path, state, fmode, GFP_KERNEL, 1); } /* @@ -618,7 +624,7 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f struct nfs4_lock_state *lsp; struct nfs_client *clp = state->owner->so_client; - lsp = kzalloc(sizeof(*lsp), GFP_KERNEL); + lsp = kzalloc(sizeof(*lsp), GFP_NOFS); if (lsp == NULL) return NULL; rpc_init_wait_queue(&lsp->ls_sequence.wait, "lock_seqid_waitqueue"); @@ -754,11 +760,11 @@ void nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t f nfs4_put_lock_state(lsp); } -struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter) +struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask) { struct nfs_seqid *new; - new = kmalloc(sizeof(*new), GFP_KERNEL); + new = kmalloc(sizeof(*new), gfp_mask); if (new != NULL) { new->sequence = counter; INIT_LIST_HEAD(&new->list); @@ -1347,7 +1353,7 @@ static int nfs4_recall_slot(struct nfs_client *clp) nfs4_begin_drain_session(clp); new = kmalloc(fc_tbl->target_max_slots * sizeof(struct nfs4_slot), - GFP_KERNEL); + GFP_NOFS); if (!new) return -ENOMEM; diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 38f3b582e7c..6bdef28efa3 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -1504,14 +1504,14 @@ static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclie hdr->replen += decode_setclientid_maxsz; } -static void encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs_client *client_state, struct compound_hdr *hdr) +static void encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs4_setclientid_res *arg, struct compound_hdr *hdr) { __be32 *p; p = reserve_space(xdr, 12 + NFS4_VERIFIER_SIZE); *p++ = cpu_to_be32(OP_SETCLIENTID_CONFIRM); - p = xdr_encode_hyper(p, client_state->cl_clientid); - xdr_encode_opaque_fixed(p, client_state->cl_confirm.data, NFS4_VERIFIER_SIZE); + p = xdr_encode_hyper(p, arg->clientid); + xdr_encode_opaque_fixed(p, arg->confirm.data, NFS4_VERIFIER_SIZE); hdr->nops++; hdr->replen += decode_setclientid_confirm_maxsz; } @@ -2324,7 +2324,7 @@ static int nfs4_xdr_enc_setclientid(struct rpc_rqst *req, __be32 *p, struct nfs4 /* * a SETCLIENTID_CONFIRM request */ -static int nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, __be32 *p, struct nfs_client *clp) +static int nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, __be32 *p, struct nfs4_setclientid_res *arg) { struct xdr_stream xdr; struct compound_hdr hdr = { @@ -2334,7 +2334,7 @@ static int nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, __be32 *p, str xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, req, &hdr); - encode_setclientid_confirm(&xdr, clp, &hdr); + encode_setclientid_confirm(&xdr, arg, &hdr); encode_putrootfh(&xdr, &hdr); encode_fsinfo(&xdr, lease_bitmap, &hdr); encode_nops(&hdr); @@ -4397,7 +4397,7 @@ out_overflow: return -EIO; } -static int decode_setclientid(struct xdr_stream *xdr, struct nfs_client *clp) +static int decode_setclientid(struct xdr_stream *xdr, struct nfs4_setclientid_res *res) { __be32 *p; uint32_t opnum; @@ -4417,8 +4417,8 @@ static int decode_setclientid(struct xdr_stream *xdr, struct nfs_client *clp) p = xdr_inline_decode(xdr, 8 + NFS4_VERIFIER_SIZE); if (unlikely(!p)) goto out_overflow; - p = xdr_decode_hyper(p, &clp->cl_clientid); - memcpy(clp->cl_confirm.data, p, NFS4_VERIFIER_SIZE); + p = xdr_decode_hyper(p, &res->clientid); + memcpy(res->confirm.data, p, NFS4_VERIFIER_SIZE); } else if (nfserr == NFSERR_CLID_INUSE) { uint32_t len; @@ -4815,7 +4815,7 @@ static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, __be32 *p, struct nfs_rem goto out; if ((status = decode_remove(&xdr, &res->cinfo)) != 0) goto out; - decode_getfattr(&xdr, &res->dir_attr, res->server, + decode_getfattr(&xdr, res->dir_attr, res->server, !RPC_IS_ASYNC(rqstp->rq_task)); out: return status; @@ -5498,7 +5498,7 @@ static int nfs4_xdr_dec_renew(struct rpc_rqst *rqstp, __be32 *p, void *dummy) * Decode SETCLIENTID response */ static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req, __be32 *p, - struct nfs_client *clp) + struct nfs4_setclientid_res *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -5507,7 +5507,7 @@ static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req, __be32 *p, xdr_init_decode(&xdr, &req->rq_rcv_buf, p); status = decode_compound_hdr(&xdr, &hdr); if (!status) - status = decode_setclientid(&xdr, clp); + status = decode_setclientid(&xdr, res); return status; } diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index 8c55b27c0de..6bd19d843af 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -488,7 +488,6 @@ static int __init root_nfs_ports(void) */ static int __init root_nfs_get_handle(void) { - struct nfs_fh fh; struct sockaddr_in sin; unsigned int auth_flav_len = 0; struct nfs_mount_request request = { @@ -499,21 +498,24 @@ static int __init root_nfs_get_handle(void) NFS_MNT3_VERSION : NFS_MNT_VERSION, .protocol = (nfs_data.flags & NFS_MOUNT_TCP) ? XPRT_TRANSPORT_TCP : XPRT_TRANSPORT_UDP, - .fh = &fh, .auth_flav_len = &auth_flav_len, }; - int status; + int status = -ENOMEM; + request.fh = nfs_alloc_fhandle(); + if (!request.fh) + goto out; set_sockaddr(&sin, servaddr, htons(mount_port)); status = nfs_mount(&request); if (status < 0) printk(KERN_ERR "Root-NFS: Server returned error %d " "while mounting %s\n", status, nfs_export_path); else { - nfs_data.root.size = fh.size; - memcpy(nfs_data.root.data, fh.data, fh.size); + nfs_data.root.size = request.fh->size; + memcpy(&nfs_data.root.data, request.fh->data, request.fh->size); } - + nfs_free_fhandle(request.fh); +out: return status; } diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index 29d9d36cd5f..a3654e57b58 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -60,16 +60,10 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode, { struct nfs_page *req; - for (;;) { - /* try to allocate the request struct */ - req = nfs_page_alloc(); - if (req != NULL) - break; - - if (fatal_signal_pending(current)) - return ERR_PTR(-ERESTARTSYS); - yield(); - } + /* try to allocate the request struct */ + req = nfs_page_alloc(); + if (req == NULL) + return ERR_PTR(-ENOMEM); /* Initialize the request struct. Initially, we assume a * long write-back delay. This will be adjusted in diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 0288be80444..611bec22f55 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -224,35 +224,60 @@ static int nfs_proc_readlink(struct inode *inode, struct page *page, return status; } +struct nfs_createdata { + struct nfs_createargs arg; + struct nfs_diropok res; + struct nfs_fh fhandle; + struct nfs_fattr fattr; +}; + +static struct nfs_createdata *nfs_alloc_createdata(struct inode *dir, + struct dentry *dentry, struct iattr *sattr) +{ + struct nfs_createdata *data; + + data = kmalloc(sizeof(*data), GFP_KERNEL); + + if (data != NULL) { + data->arg.fh = NFS_FH(dir); + data->arg.name = dentry->d_name.name; + data->arg.len = dentry->d_name.len; + data->arg.sattr = sattr; + nfs_fattr_init(&data->fattr); + data->fhandle.size = 0; + data->res.fh = &data->fhandle; + data->res.fattr = &data->fattr; + } + return data; +}; + +static void nfs_free_createdata(const struct nfs_createdata *data) +{ + kfree(data); +} + static int nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, int flags, struct nameidata *nd) { - struct nfs_fh fhandle; - struct nfs_fattr fattr; - struct nfs_createargs arg = { - .fh = NFS_FH(dir), - .name = dentry->d_name.name, - .len = dentry->d_name.len, - .sattr = sattr - }; - struct nfs_diropok res = { - .fh = &fhandle, - .fattr = &fattr - }; + struct nfs_createdata *data; struct rpc_message msg = { .rpc_proc = &nfs_procedures[NFSPROC_CREATE], - .rpc_argp = &arg, - .rpc_resp = &res, }; - int status; + int status = -ENOMEM; - nfs_fattr_init(&fattr); dprintk("NFS call create %s\n", dentry->d_name.name); + data = nfs_alloc_createdata(dir, dentry, sattr); + if (data == NULL) + goto out; + msg.rpc_argp = &data->arg; + msg.rpc_resp = &data->res; status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); nfs_mark_for_revalidate(dir); if (status == 0) - status = nfs_instantiate(dentry, &fhandle, &fattr); + status = nfs_instantiate(dentry, data->res.fh, data->res.fattr); + nfs_free_createdata(data); +out: dprintk("NFS reply create: %d\n", status); return status; } @@ -264,24 +289,12 @@ static int nfs_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr, dev_t rdev) { - struct nfs_fh fhandle; - struct nfs_fattr fattr; - struct nfs_createargs arg = { - .fh = NFS_FH(dir), - .name = dentry->d_name.name, - .len = dentry->d_name.len, - .sattr = sattr - }; - struct nfs_diropok res = { - .fh = &fhandle, - .fattr = &fattr - }; + struct nfs_createdata *data; struct rpc_message msg = { .rpc_proc = &nfs_procedures[NFSPROC_CREATE], - .rpc_argp = &arg, - .rpc_resp = &res, }; - int status, mode; + umode_t mode; + int status = -ENOMEM; dprintk("NFS call mknod %s\n", dentry->d_name.name); @@ -294,17 +307,24 @@ nfs_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr, sattr->ia_size = new_encode_dev(rdev);/* get out your barf bag */ } - nfs_fattr_init(&fattr); + data = nfs_alloc_createdata(dir, dentry, sattr); + if (data == NULL) + goto out; + msg.rpc_argp = &data->arg; + msg.rpc_resp = &data->res; + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); nfs_mark_for_revalidate(dir); if (status == -EINVAL && S_ISFIFO(mode)) { sattr->ia_mode = mode; - nfs_fattr_init(&fattr); + nfs_fattr_init(data->res.fattr); status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); } if (status == 0) - status = nfs_instantiate(dentry, &fhandle, &fattr); + status = nfs_instantiate(dentry, data->res.fh, data->res.fattr); + nfs_free_createdata(data); +out: dprintk("NFS reply mknod: %d\n", status); return status; } @@ -398,8 +418,8 @@ static int nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page, unsigned int len, struct iattr *sattr) { - struct nfs_fh fhandle; - struct nfs_fattr fattr; + struct nfs_fh *fh; + struct nfs_fattr *fattr; struct nfs_symlinkargs arg = { .fromfh = NFS_FH(dir), .fromname = dentry->d_name.name, @@ -412,12 +432,18 @@ nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page, .rpc_proc = &nfs_procedures[NFSPROC_SYMLINK], .rpc_argp = &arg, }; - int status; + int status = -ENAMETOOLONG; + + dprintk("NFS call symlink %s\n", dentry->d_name.name); if (len > NFS2_MAXPATHLEN) - return -ENAMETOOLONG; + goto out; - dprintk("NFS call symlink %s\n", dentry->d_name.name); + fh = nfs_alloc_fhandle(); + fattr = nfs_alloc_fattr(); + status = -ENOMEM; + if (fh == NULL || fattr == NULL) + goto out; status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); nfs_mark_for_revalidate(dir); @@ -427,12 +453,12 @@ nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page, * filehandle size to zero indicates to nfs_instantiate that it * should fill in the data with a LOOKUP call on the wire. */ - if (status == 0) { - nfs_fattr_init(&fattr); - fhandle.size = 0; - status = nfs_instantiate(dentry, &fhandle, &fattr); - } + if (status == 0) + status = nfs_instantiate(dentry, fh, fattr); + nfs_free_fattr(fattr); + nfs_free_fhandle(fh); +out: dprintk("NFS reply symlink: %d\n", status); return status; } @@ -440,31 +466,25 @@ nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page, static int nfs_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr) { - struct nfs_fh fhandle; - struct nfs_fattr fattr; - struct nfs_createargs arg = { - .fh = NFS_FH(dir), - .name = dentry->d_name.name, - .len = dentry->d_name.len, - .sattr = sattr - }; - struct nfs_diropok res = { - .fh = &fhandle, - .fattr = &fattr - }; + struct nfs_createdata *data; struct rpc_message msg = { .rpc_proc = &nfs_procedures[NFSPROC_MKDIR], - .rpc_argp = &arg, - .rpc_resp = &res, }; - int status; + int status = -ENOMEM; dprintk("NFS call mkdir %s\n", dentry->d_name.name); - nfs_fattr_init(&fattr); + data = nfs_alloc_createdata(dir, dentry, sattr); + if (data == NULL) + goto out; + msg.rpc_argp = &data->arg; + msg.rpc_resp = &data->res; + status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); nfs_mark_for_revalidate(dir); if (status == 0) - status = nfs_instantiate(dentry, &fhandle, &fattr); + status = nfs_instantiate(dentry, data->res.fh, data->res.fattr); + nfs_free_createdata(data); +out: dprintk("NFS reply mkdir: %d\n", status); return status; } diff --git a/fs/nfs/read.c b/fs/nfs/read.c index db9b360ae19..6e2b06e6ca7 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -40,7 +40,7 @@ static mempool_t *nfs_rdata_mempool; struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount) { - struct nfs_read_data *p = mempool_alloc(nfs_rdata_mempool, GFP_NOFS); + struct nfs_read_data *p = mempool_alloc(nfs_rdata_mempool, GFP_KERNEL); if (p) { memset(p, 0, sizeof(*p)); @@ -50,7 +50,7 @@ struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount) if (pagecount <= ARRAY_SIZE(p->page_array)) p->pagevec = p->page_array; else { - p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS); + p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_KERNEL); if (!p->pagevec) { mempool_free(p, nfs_rdata_mempool); p = NULL; diff --git a/fs/nfs/super.c b/fs/nfs/super.c index b4148fc00f9..2f8b1157daa 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -141,7 +141,6 @@ static const match_table_t nfs_mount_option_tokens = { { Opt_resvport, "resvport" }, { Opt_noresvport, "noresvport" }, { Opt_fscache, "fsc" }, - { Opt_fscache_uniq, "fsc=%s" }, { Opt_nofscache, "nofsc" }, { Opt_port, "port=%s" }, @@ -171,6 +170,7 @@ static const match_table_t nfs_mount_option_tokens = { { Opt_mountaddr, "mountaddr=%s" }, { Opt_lookupcache, "lookupcache=%s" }, + { Opt_fscache_uniq, "fsc=%s" }, { Opt_err, NULL } }; @@ -423,15 +423,19 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf) unsigned char blockbits; unsigned long blockres; struct nfs_fh *fh = NFS_FH(dentry->d_inode); - struct nfs_fattr fattr; - struct nfs_fsstat res = { - .fattr = &fattr, - }; - int error; + struct nfs_fsstat res; + int error = -ENOMEM; + + res.fattr = nfs_alloc_fattr(); + if (res.fattr == NULL) + goto out_err; error = server->nfs_client->rpc_ops->statfs(server, fh, &res); + + nfs_free_fattr(res.fattr); if (error < 0) goto out_err; + buf->f_type = NFS_SUPER_MAGIC; /* @@ -1046,14 +1050,6 @@ static int nfs_parse_mount_options(char *raw, kfree(mnt->fscache_uniq); mnt->fscache_uniq = NULL; break; - case Opt_fscache_uniq: - string = match_strdup(args); - if (!string) - goto out_nomem; - kfree(mnt->fscache_uniq); - mnt->fscache_uniq = string; - mnt->options |= NFS_OPTION_FSCACHE; - break; /* * options that take numeric values @@ -1384,6 +1380,14 @@ static int nfs_parse_mount_options(char *raw, return 0; }; break; + case Opt_fscache_uniq: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + kfree(mnt->fscache_uniq); + mnt->fscache_uniq = string; + mnt->options |= NFS_OPTION_FSCACHE; + break; /* * Special options @@ -2172,7 +2176,7 @@ static int nfs_get_sb(struct file_system_type *fs_type, int error = -ENOMEM; data = nfs_alloc_parsed_mount_data(3); - mntfh = kzalloc(sizeof(*mntfh), GFP_KERNEL); + mntfh = nfs_alloc_fhandle(); if (data == NULL || mntfh == NULL) goto out_free_fh; @@ -2247,7 +2251,7 @@ out: kfree(data->fscache_uniq); security_free_mnt_opts(&data->lsm_opts); out_free_fh: - kfree(mntfh); + nfs_free_fhandle(mntfh); kfree(data); return error; @@ -2556,7 +2560,7 @@ static int nfs4_remote_get_sb(struct file_system_type *fs_type, }; int error = -ENOMEM; - mntfh = kzalloc(sizeof(*mntfh), GFP_KERNEL); + mntfh = nfs_alloc_fhandle(); if (data == NULL || mntfh == NULL) goto out_free_fh; @@ -2614,7 +2618,7 @@ static int nfs4_remote_get_sb(struct file_system_type *fs_type, out: security_free_mnt_opts(&data->lsm_opts); out_free_fh: - kfree(mntfh); + nfs_free_fhandle(mntfh); return error; out_free: @@ -2669,41 +2673,120 @@ out_freepage: free_page((unsigned long)page); } +struct nfs_referral_count { + struct list_head list; + const struct task_struct *task; + unsigned int referral_count; +}; + +static LIST_HEAD(nfs_referral_count_list); +static DEFINE_SPINLOCK(nfs_referral_count_list_lock); + +static struct nfs_referral_count *nfs_find_referral_count(void) +{ + struct nfs_referral_count *p; + + list_for_each_entry(p, &nfs_referral_count_list, list) { + if (p->task == current) + return p; + } + return NULL; +} + +#define NFS_MAX_NESTED_REFERRALS 2 + +static int nfs_referral_loop_protect(void) +{ + struct nfs_referral_count *p, *new; + int ret = -ENOMEM; + + new = kmalloc(sizeof(*new), GFP_KERNEL); + if (!new) + goto out; + new->task = current; + new->referral_count = 1; + + ret = 0; + spin_lock(&nfs_referral_count_list_lock); + p = nfs_find_referral_count(); + if (p != NULL) { + if (p->referral_count >= NFS_MAX_NESTED_REFERRALS) + ret = -ELOOP; + else + p->referral_count++; + } else { + list_add(&new->list, &nfs_referral_count_list); + new = NULL; + } + spin_unlock(&nfs_referral_count_list_lock); + kfree(new); +out: + return ret; +} + +static void nfs_referral_loop_unprotect(void) +{ + struct nfs_referral_count *p; + + spin_lock(&nfs_referral_count_list_lock); + p = nfs_find_referral_count(); + p->referral_count--; + if (p->referral_count == 0) + list_del(&p->list); + else + p = NULL; + spin_unlock(&nfs_referral_count_list_lock); + kfree(p); +} + static int nfs_follow_remote_path(struct vfsmount *root_mnt, const char *export_path, struct vfsmount *mnt_target) { + struct nameidata *nd = NULL; struct mnt_namespace *ns_private; - struct nameidata nd; struct super_block *s; int ret; + nd = kmalloc(sizeof(*nd), GFP_KERNEL); + if (nd == NULL) + return -ENOMEM; + ns_private = create_mnt_ns(root_mnt); ret = PTR_ERR(ns_private); if (IS_ERR(ns_private)) goto out_mntput; + ret = nfs_referral_loop_protect(); + if (ret != 0) + goto out_put_mnt_ns; + ret = vfs_path_lookup(root_mnt->mnt_root, root_mnt, - export_path, LOOKUP_FOLLOW, &nd); + export_path, LOOKUP_FOLLOW, nd); + nfs_referral_loop_unprotect(); put_mnt_ns(ns_private); if (ret != 0) goto out_err; - s = nd.path.mnt->mnt_sb; + s = nd->path.mnt->mnt_sb; atomic_inc(&s->s_active); mnt_target->mnt_sb = s; - mnt_target->mnt_root = dget(nd.path.dentry); + mnt_target->mnt_root = dget(nd->path.dentry); /* Correct the device pathname */ - nfs_fix_devname(&nd.path, mnt_target); + nfs_fix_devname(&nd->path, mnt_target); - path_put(&nd.path); + path_put(&nd->path); + kfree(nd); down_write(&s->s_umount); return 0; +out_put_mnt_ns: + put_mnt_ns(ns_private); out_mntput: mntput(root_mnt); out_err: + kfree(nd); return ret; } @@ -2874,17 +2957,21 @@ static int nfs4_remote_referral_get_sb(struct file_system_type *fs_type, struct super_block *s; struct nfs_server *server; struct dentry *mntroot; - struct nfs_fh mntfh; + struct nfs_fh *mntfh; int (*compare_super)(struct super_block *, void *) = nfs_compare_super; struct nfs_sb_mountdata sb_mntdata = { .mntflags = flags, }; - int error; + int error = -ENOMEM; dprintk("--> nfs4_referral_get_sb()\n"); + mntfh = nfs_alloc_fhandle(); + if (mntfh == NULL) + goto out_err_nofh; + /* create a new volume representation */ - server = nfs4_create_referral_server(data, &mntfh); + server = nfs4_create_referral_server(data, mntfh); if (IS_ERR(server)) { error = PTR_ERR(server); goto out_err_noserver; @@ -2916,7 +3003,7 @@ static int nfs4_remote_referral_get_sb(struct file_system_type *fs_type, nfs_fscache_get_super_cookie(s, NULL, data); } - mntroot = nfs4_get_root(s, &mntfh); + mntroot = nfs4_get_root(s, mntfh); if (IS_ERR(mntroot)) { error = PTR_ERR(mntroot); goto error_splat_super; @@ -2933,12 +3020,15 @@ static int nfs4_remote_referral_get_sb(struct file_system_type *fs_type, security_sb_clone_mnt_opts(data->sb, s); + nfs_free_fhandle(mntfh); dprintk("<-- nfs4_referral_get_sb() = 0\n"); return 0; out_err_nosb: nfs_free_server(server); out_err_noserver: + nfs_free_fhandle(mntfh); +out_err_nofh: dprintk("<-- nfs4_referral_get_sb() = %d [error]\n", error); return error; @@ -2947,6 +3037,7 @@ error_splat_super: bdi_unregister(&server->backing_dev_info); error_splat_bdi: deactivate_locked_super(s); + nfs_free_fhandle(mntfh); dprintk("<-- nfs4_referral_get_sb() = %d [splat]\n", error); return error; } diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 6da3d3ff6ed..a2242af6a17 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -23,6 +23,7 @@ struct nfs_unlinkdata { struct nfs_removeres res; struct inode *dir; struct rpc_cred *cred; + struct nfs_fattr dir_attr; }; /** @@ -169,7 +170,7 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n } nfs_sb_active(dir->i_sb); data->args.fh = NFS_FH(dir); - nfs_fattr_init(&data->res.dir_attr); + nfs_fattr_init(data->res.dir_attr); NFS_PROTO(dir)->unlink_setup(&msg, dir); @@ -259,6 +260,7 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry) goto out_free; } data->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE; + data->res.dir_attr = &data->dir_attr; status = -EBUSY; spin_lock(&dentry->d_lock); diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 872a5ef550c..c2a4f71d87d 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -259,10 +259,9 @@ static struct cache_detail svc_expkey_cache = { .alloc = expkey_alloc, }; -static struct svc_expkey * -svc_expkey_lookup(struct svc_expkey *item) +static int +svc_expkey_hash(struct svc_expkey *item) { - struct cache_head *ch; int hash = item->ek_fsidtype; char * cp = (char*)item->ek_fsid; int len = key_len(item->ek_fsidtype); @@ -270,6 +269,14 @@ svc_expkey_lookup(struct svc_expkey *item) hash ^= hash_mem(cp, len, EXPKEY_HASHBITS); hash ^= hash_ptr(item->ek_client, EXPKEY_HASHBITS); hash &= EXPKEY_HASHMASK; + return hash; +} + +static struct svc_expkey * +svc_expkey_lookup(struct svc_expkey *item) +{ + struct cache_head *ch; + int hash = svc_expkey_hash(item); ch = sunrpc_cache_lookup(&svc_expkey_cache, &item->h, hash); @@ -283,13 +290,7 @@ static struct svc_expkey * svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old) { struct cache_head *ch; - int hash = new->ek_fsidtype; - char * cp = (char*)new->ek_fsid; - int len = key_len(new->ek_fsidtype); - - hash ^= hash_mem(cp, len, EXPKEY_HASHBITS); - hash ^= hash_ptr(new->ek_client, EXPKEY_HASHBITS); - hash &= EXPKEY_HASHMASK; + int hash = svc_expkey_hash(new); ch = sunrpc_cache_update(&svc_expkey_cache, &new->h, &old->h, hash); @@ -738,14 +739,22 @@ struct cache_detail svc_export_cache = { .alloc = svc_export_alloc, }; -static struct svc_export * -svc_export_lookup(struct svc_export *exp) +static int +svc_export_hash(struct svc_export *exp) { - struct cache_head *ch; int hash; + hash = hash_ptr(exp->ex_client, EXPORT_HASHBITS); hash ^= hash_ptr(exp->ex_path.dentry, EXPORT_HASHBITS); hash ^= hash_ptr(exp->ex_path.mnt, EXPORT_HASHBITS); + return hash; +} + +static struct svc_export * +svc_export_lookup(struct svc_export *exp) +{ + struct cache_head *ch; + int hash = svc_export_hash(exp); ch = sunrpc_cache_lookup(&svc_export_cache, &exp->h, hash); @@ -759,10 +768,7 @@ static struct svc_export * svc_export_update(struct svc_export *new, struct svc_export *old) { struct cache_head *ch; - int hash; - hash = hash_ptr(old->ex_client, EXPORT_HASHBITS); - hash ^= hash_ptr(old->ex_path.dentry, EXPORT_HASHBITS); - hash ^= hash_ptr(old->ex_path.mnt, EXPORT_HASHBITS); + int hash = svc_export_hash(old); ch = sunrpc_cache_update(&svc_export_cache, &new->h, &old->h, @@ -1071,9 +1077,9 @@ exp_export(struct nfsctl_export *nxp) err = 0; finish: kfree(new.ex_pathname); - if (exp) + if (!IS_ERR_OR_NULL(exp)) exp_put(exp); - if (fsid_key && !IS_ERR(fsid_key)) + if (!IS_ERR_OR_NULL(fsid_key)) cache_put(&fsid_key->h, &svc_expkey_cache); path_put(&path); out_put_clp: diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 7e32bd394e8..eb78e7e2207 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -32,6 +32,7 @@ */ #include <linux/sunrpc/clnt.h> +#include <linux/sunrpc/svc_xprt.h> #include <linux/slab.h> #include "nfsd.h" #include "state.h" @@ -79,11 +80,6 @@ enum nfs_cb_opnum4 { cb_sequence_dec_sz + \ op_dec_sz) -struct nfs4_rpc_args { - void *args_op; - struct nfsd4_cb_sequence args_seq; -}; - /* * Generic encode routines from fs/nfs/nfs4xdr.c */ @@ -428,13 +424,19 @@ static struct rpc_procinfo nfs4_cb_procedures[] = { }; static struct rpc_version nfs_cb_version4 = { +/* + * Note on the callback rpc program version number: despite language in rfc + * 5661 section 18.36.3 requiring servers to use 4 in this field, the + * official xdr descriptions for both 4.0 and 4.1 specify version 1, and + * in practice that appears to be what implementations use. The section + * 18.36.3 language is expected to be fixed in an erratum. + */ .number = 1, .nrprocs = ARRAY_SIZE(nfs4_cb_procedures), .procs = nfs4_cb_procedures }; static struct rpc_version * nfs_cb_version[] = { - NULL, &nfs_cb_version4, }; @@ -456,15 +458,14 @@ static struct rpc_program cb_program = { static int max_cb_time(void) { - return max(NFSD_LEASE_TIME/10, (time_t)1) * HZ; + return max(nfsd4_lease/10, (time_t)1) * HZ; } /* Reference counting, callback cleanup, etc., all look racy as heck. - * And why is cb_set an atomic? */ + * And why is cl_cb_set an atomic? */ -int setup_callback_client(struct nfs4_client *clp) +int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb) { - struct nfs4_cb_conn *cb = &clp->cl_cb_conn; struct rpc_timeout timeparms = { .to_initval = max_cb_time(), .to_retries = 0, @@ -476,7 +477,7 @@ int setup_callback_client(struct nfs4_client *clp) .timeout = &timeparms, .program = &cb_program, .prognumber = cb->cb_prog, - .version = nfs_cb_version[1]->number, + .version = 0, .authflavor = clp->cl_flavor, .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), .client_name = clp->cl_principal, @@ -486,7 +487,7 @@ int setup_callback_client(struct nfs4_client *clp) if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5)) return -EINVAL; if (cb->cb_minorversion) { - args.bc_xprt = clp->cl_cb_xprt; + args.bc_xprt = cb->cb_xprt; args.protocol = XPRT_TRANSPORT_BC_TCP; } /* Create RPC client */ @@ -496,7 +497,7 @@ int setup_callback_client(struct nfs4_client *clp) PTR_ERR(client)); return PTR_ERR(client); } - cb->cb_client = client; + nfsd4_set_callback_client(clp, client); return 0; } @@ -514,8 +515,7 @@ static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata) if (task->tk_status) warn_no_callback_path(clp, task->tk_status); else - atomic_set(&clp->cl_cb_conn.cb_set, 1); - put_nfs4_client(clp); + atomic_set(&clp->cl_cb_set, 1); } static const struct rpc_call_ops nfsd4_cb_probe_ops = { @@ -537,7 +537,6 @@ int set_callback_cred(void) void do_probe_callback(struct nfs4_client *clp) { - struct nfs4_cb_conn *cb = &clp->cl_cb_conn; struct rpc_message msg = { .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL], .rpc_argp = clp, @@ -545,34 +544,27 @@ void do_probe_callback(struct nfs4_client *clp) }; int status; - status = rpc_call_async(cb->cb_client, &msg, + status = rpc_call_async(clp->cl_cb_client, &msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN, &nfsd4_cb_probe_ops, (void *)clp); - if (status) { + if (status) warn_no_callback_path(clp, status); - put_nfs4_client(clp); - } } /* * Set up the callback client and put a NFSPROC4_CB_NULL on the wire... */ -void -nfsd4_probe_callback(struct nfs4_client *clp) +void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *cb) { int status; - BUG_ON(atomic_read(&clp->cl_cb_conn.cb_set)); + BUG_ON(atomic_read(&clp->cl_cb_set)); - status = setup_callback_client(clp); + status = setup_callback_client(clp, cb); if (status) { warn_no_callback_path(clp, status); return; } - - /* the task holds a reference to the nfs4_client struct */ - atomic_inc(&clp->cl_count); - do_probe_callback(clp); } @@ -658,18 +650,32 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata) } } + static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) { struct nfs4_delegation *dp = calldata; struct nfs4_client *clp = dp->dl_client; + struct rpc_clnt *current_rpc_client = clp->cl_cb_client; nfsd4_cb_done(task, calldata); + if (current_rpc_client == NULL) { + /* We're shutting down; give up. */ + /* XXX: err, or is it ok just to fall through + * and rpc_restart_call? */ + return; + } + switch (task->tk_status) { case -EIO: /* Network partition? */ - atomic_set(&clp->cl_cb_conn.cb_set, 0); + atomic_set(&clp->cl_cb_set, 0); warn_no_callback_path(clp, task->tk_status); + if (current_rpc_client != task->tk_client) { + /* queue a callback on the new connection: */ + nfsd4_cb_recall(dp); + return; + } case -EBADHANDLE: case -NFS4ERR_BAD_STATEID: /* Race: client probably got cb_recall @@ -677,7 +683,7 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) break; default: /* success, or error we can't handle */ - goto done; + return; } if (dp->dl_retries--) { rpc_delay(task, 2*HZ); @@ -685,20 +691,16 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) rpc_restart_call(task); return; } else { - atomic_set(&clp->cl_cb_conn.cb_set, 0); + atomic_set(&clp->cl_cb_set, 0); warn_no_callback_path(clp, task->tk_status); } -done: - kfree(task->tk_msg.rpc_argp); } static void nfsd4_cb_recall_release(void *calldata) { struct nfs4_delegation *dp = calldata; - struct nfs4_client *clp = dp->dl_client; nfs4_put_delegation(dp); - put_nfs4_client(clp); } static const struct rpc_call_ops nfsd4_cb_recall_ops = { @@ -707,33 +709,75 @@ static const struct rpc_call_ops nfsd4_cb_recall_ops = { .rpc_release = nfsd4_cb_recall_release, }; +static struct workqueue_struct *callback_wq; + +int nfsd4_create_callback_queue(void) +{ + callback_wq = create_singlethread_workqueue("nfsd4_callbacks"); + if (!callback_wq) + return -ENOMEM; + return 0; +} + +void nfsd4_destroy_callback_queue(void) +{ + destroy_workqueue(callback_wq); +} + +/* must be called under the state lock */ +void nfsd4_set_callback_client(struct nfs4_client *clp, struct rpc_clnt *new) +{ + struct rpc_clnt *old = clp->cl_cb_client; + + clp->cl_cb_client = new; + /* + * After this, any work that saw the old value of cl_cb_client will + * be gone: + */ + flush_workqueue(callback_wq); + /* So we can safely shut it down: */ + if (old) + rpc_shutdown_client(old); +} + /* * called with dp->dl_count inc'ed. */ -void -nfsd4_cb_recall(struct nfs4_delegation *dp) +static void _nfsd4_cb_recall(struct nfs4_delegation *dp) { struct nfs4_client *clp = dp->dl_client; - struct rpc_clnt *clnt = clp->cl_cb_conn.cb_client; - struct nfs4_rpc_args *args; + struct rpc_clnt *clnt = clp->cl_cb_client; + struct nfs4_rpc_args *args = &dp->dl_recall.cb_args; struct rpc_message msg = { .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL], .rpc_cred = callback_cred }; - int status = -ENOMEM; + int status; + + if (clnt == NULL) + return; /* Client is shutting down; give up. */ - args = kzalloc(sizeof(*args), GFP_KERNEL); - if (!args) - goto out; args->args_op = dp; msg.rpc_argp = args; dp->dl_retries = 1; status = rpc_call_async(clnt, &msg, RPC_TASK_SOFT, &nfsd4_cb_recall_ops, dp); -out: - if (status) { - kfree(args); - put_nfs4_client(clp); + if (status) nfs4_put_delegation(dp); - } +} + +void nfsd4_do_callback_rpc(struct work_struct *w) +{ + /* XXX: for now, just send off delegation recall. */ + /* In future, generalize to handle any sort of callback. */ + struct nfsd4_callback *c = container_of(w, struct nfsd4_callback, cb_work); + struct nfs4_delegation *dp = container_of(c, struct nfs4_delegation, dl_recall); + + _nfsd4_cb_recall(dp); +} + + +void nfsd4_cb_recall(struct nfs4_delegation *dp) +{ + queue_work(callback_wq, &dp->dl_recall.cb_work); } diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 2ab9e8501bf..59ec449b0c7 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -969,20 +969,36 @@ static struct nfsd4_operation nfsd4_ops[]; static const char *nfsd4_op_name(unsigned opnum); /* - * Enforce NFSv4.1 COMPOUND ordering rules. + * Enforce NFSv4.1 COMPOUND ordering rules: * - * TODO: - * - enforce NFS4ERR_NOT_ONLY_OP, - * - DESTROY_SESSION MUST be the final operation in the COMPOUND request. + * Also note, enforced elsewhere: + * - SEQUENCE other than as first op results in + * NFS4ERR_SEQUENCE_POS. (Enforced in nfsd4_sequence().) + * - BIND_CONN_TO_SESSION must be the only op in its compound + * (Will be enforced in nfsd4_bind_conn_to_session().) + * - DESTROY_SESSION must be the final operation in a compound, if + * sessionid's in SEQUENCE and DESTROY_SESSION are the same. + * (Enforced in nfsd4_destroy_session().) */ -static bool nfs41_op_ordering_ok(struct nfsd4_compoundargs *args) +static __be32 nfs41_check_op_ordering(struct nfsd4_compoundargs *args) { - if (args->minorversion && args->opcnt > 0) { - struct nfsd4_op *op = &args->ops[0]; - return (op->status == nfserr_op_illegal) || - (nfsd4_ops[op->opnum].op_flags & ALLOWED_AS_FIRST_OP); - } - return true; + struct nfsd4_op *op = &args->ops[0]; + + /* These ordering requirements don't apply to NFSv4.0: */ + if (args->minorversion == 0) + return nfs_ok; + /* This is weird, but OK, not our problem: */ + if (args->opcnt == 0) + return nfs_ok; + if (op->status == nfserr_op_illegal) + return nfs_ok; + if (!(nfsd4_ops[op->opnum].op_flags & ALLOWED_AS_FIRST_OP)) + return nfserr_op_not_in_session; + if (op->opnum == OP_SEQUENCE) + return nfs_ok; + if (args->opcnt != 1) + return nfserr_not_only_op; + return nfs_ok; } /* @@ -1012,6 +1028,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, resp->rqstp = rqstp; resp->cstate.minorversion = args->minorversion; resp->cstate.replay_owner = NULL; + resp->cstate.session = NULL; fh_init(&resp->cstate.current_fh, NFS4_FHSIZE); fh_init(&resp->cstate.save_fh, NFS4_FHSIZE); /* Use the deferral mechanism only for NFSv4.0 compounds */ @@ -1024,13 +1041,13 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, if (args->minorversion > nfsd_supported_minorversion) goto out; - if (!nfs41_op_ordering_ok(args)) { + status = nfs41_check_op_ordering(args); + if (status) { op = &args->ops[0]; - op->status = nfserr_sequence_pos; + op->status = status; goto encode_op; } - status = nfs_ok; while (!status && resp->opcnt < args->opcnt) { op = &args->ops[resp->opcnt++]; @@ -1295,6 +1312,11 @@ static struct nfsd4_operation nfsd4_ops[] = { .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, .op_name = "OP_SEQUENCE", }, + [OP_RECLAIM_COMPLETE] = { + .op_func = (nfsd4op_func)nfsd4_reclaim_complete, + .op_flags = ALLOWED_WITHOUT_FH, + .op_name = "OP_RECLAIM_COMPLETE", + }, }; static const char *nfsd4_op_name(unsigned opnum) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 6a8fedaa4f5..12f7109720c 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -45,8 +45,8 @@ #define NFSDDBG_FACILITY NFSDDBG_PROC /* Globals */ -static time_t lease_time = 90; /* default lease time */ -static time_t user_lease_time = 90; +time_t nfsd4_lease = 90; /* default lease time */ +time_t nfsd4_grace = 90; static time_t boot_time; static u32 current_ownerid = 1; static u32 current_fileid = 1; @@ -190,7 +190,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f dp->dl_vfs_file = stp->st_vfs_file; dp->dl_type = type; dp->dl_ident = cb->cb_ident; - dp->dl_stateid.si_boot = get_seconds(); + dp->dl_stateid.si_boot = boot_time; dp->dl_stateid.si_stateownerid = current_delegid++; dp->dl_stateid.si_fileid = 0; dp->dl_stateid.si_generation = 0; @@ -199,6 +199,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f atomic_set(&dp->dl_count, 1); list_add(&dp->dl_perfile, &fp->fi_delegations); list_add(&dp->dl_perclnt, &clp->cl_delegations); + INIT_WORK(&dp->dl_recall.cb_work, nfsd4_do_callback_rpc); return dp; } @@ -249,6 +250,9 @@ unhash_delegation(struct nfs4_delegation *dp) * SETCLIENTID state */ +/* client_lock protects the client lru list and session hash table */ +static DEFINE_SPINLOCK(client_lock); + /* Hash tables for nfs4_clientid state */ #define CLIENT_HASH_BITS 4 #define CLIENT_HASH_SIZE (1 << CLIENT_HASH_BITS) @@ -367,7 +371,6 @@ static void release_openowner(struct nfs4_stateowner *sop) nfs4_put_stateowner(sop); } -static DEFINE_SPINLOCK(sessionid_lock); #define SESSION_HASH_SIZE 512 static struct list_head sessionid_hashtbl[SESSION_HASH_SIZE]; @@ -565,10 +568,10 @@ alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, new->se_flags = cses->flags; kref_init(&new->se_ref); - spin_lock(&sessionid_lock); + spin_lock(&client_lock); list_add(&new->se_hash, &sessionid_hashtbl[idx]); list_add(&new->se_perclnt, &clp->cl_sessions); - spin_unlock(&sessionid_lock); + spin_unlock(&client_lock); status = nfs_ok; out: @@ -579,7 +582,7 @@ out_free: goto out; } -/* caller must hold sessionid_lock */ +/* caller must hold client_lock */ static struct nfsd4_session * find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid) { @@ -602,7 +605,7 @@ find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid) return NULL; } -/* caller must hold sessionid_lock */ +/* caller must hold client_lock */ static void unhash_session(struct nfsd4_session *ses) { @@ -610,15 +613,6 @@ unhash_session(struct nfsd4_session *ses) list_del(&ses->se_perclnt); } -static void -release_session(struct nfsd4_session *ses) -{ - spin_lock(&sessionid_lock); - unhash_session(ses); - spin_unlock(&sessionid_lock); - nfsd4_put_session(ses); -} - void free_session(struct kref *kref) { @@ -634,9 +628,18 @@ free_session(struct kref *kref) kfree(ses); } +/* must be called under the client_lock */ static inline void -renew_client(struct nfs4_client *clp) +renew_client_locked(struct nfs4_client *clp) { + if (is_client_expired(clp)) { + dprintk("%s: client (clientid %08x/%08x) already expired\n", + __func__, + clp->cl_clientid.cl_boot, + clp->cl_clientid.cl_id); + return; + } + /* * Move client to the end to the LRU list. */ @@ -647,6 +650,14 @@ renew_client(struct nfs4_client *clp) clp->cl_time = get_seconds(); } +static inline void +renew_client(struct nfs4_client *clp) +{ + spin_lock(&client_lock); + renew_client_locked(clp); + spin_unlock(&client_lock); +} + /* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */ static int STALE_CLIENTID(clientid_t *clid) @@ -680,27 +691,9 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name) return clp; } -static void -shutdown_callback_client(struct nfs4_client *clp) -{ - struct rpc_clnt *clnt = clp->cl_cb_conn.cb_client; - - if (clnt) { - /* - * Callback threads take a reference on the client, so there - * should be no outstanding callbacks at this point. - */ - clp->cl_cb_conn.cb_client = NULL; - rpc_shutdown_client(clnt); - } -} - static inline void free_client(struct nfs4_client *clp) { - shutdown_callback_client(clp); - if (clp->cl_cb_xprt) - svc_xprt_put(clp->cl_cb_xprt); if (clp->cl_cred.cr_group_info) put_group_info(clp->cl_cred.cr_group_info); kfree(clp->cl_principal); @@ -709,10 +702,34 @@ free_client(struct nfs4_client *clp) } void -put_nfs4_client(struct nfs4_client *clp) +release_session_client(struct nfsd4_session *session) { - if (atomic_dec_and_test(&clp->cl_count)) + struct nfs4_client *clp = session->se_client; + + if (!atomic_dec_and_lock(&clp->cl_refcount, &client_lock)) + return; + if (is_client_expired(clp)) { free_client(clp); + session->se_client = NULL; + } else + renew_client_locked(clp); + spin_unlock(&client_lock); + nfsd4_put_session(session); +} + +/* must be called under the client_lock */ +static inline void +unhash_client_locked(struct nfs4_client *clp) +{ + mark_client_expired(clp); + list_del(&clp->cl_lru); + while (!list_empty(&clp->cl_sessions)) { + struct nfsd4_session *ses; + ses = list_entry(clp->cl_sessions.next, struct nfsd4_session, + se_perclnt); + unhash_session(ses); + nfsd4_put_session(ses); + } } static void @@ -722,9 +739,6 @@ expire_client(struct nfs4_client *clp) struct nfs4_delegation *dp; struct list_head reaplist; - dprintk("NFSD: expire_client cl_count %d\n", - atomic_read(&clp->cl_count)); - INIT_LIST_HEAD(&reaplist); spin_lock(&recall_lock); while (!list_empty(&clp->cl_delegations)) { @@ -740,20 +754,20 @@ expire_client(struct nfs4_client *clp) list_del_init(&dp->dl_recall_lru); unhash_delegation(dp); } - list_del(&clp->cl_idhash); - list_del(&clp->cl_strhash); - list_del(&clp->cl_lru); while (!list_empty(&clp->cl_openowners)) { sop = list_entry(clp->cl_openowners.next, struct nfs4_stateowner, so_perclient); release_openowner(sop); } - while (!list_empty(&clp->cl_sessions)) { - struct nfsd4_session *ses; - ses = list_entry(clp->cl_sessions.next, struct nfsd4_session, - se_perclnt); - release_session(ses); - } - put_nfs4_client(clp); + nfsd4_set_callback_client(clp, NULL); + if (clp->cl_cb_conn.cb_xprt) + svc_xprt_put(clp->cl_cb_conn.cb_xprt); + list_del(&clp->cl_idhash); + list_del(&clp->cl_strhash); + spin_lock(&client_lock); + unhash_client_locked(clp); + if (atomic_read(&clp->cl_refcount) == 0) + free_client(clp); + spin_unlock(&client_lock); } static void copy_verf(struct nfs4_client *target, nfs4_verifier *source) @@ -839,14 +853,15 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, } memcpy(clp->cl_recdir, recdir, HEXDIR_LEN); - atomic_set(&clp->cl_count, 1); - atomic_set(&clp->cl_cb_conn.cb_set, 0); + atomic_set(&clp->cl_refcount, 0); + atomic_set(&clp->cl_cb_set, 0); INIT_LIST_HEAD(&clp->cl_idhash); INIT_LIST_HEAD(&clp->cl_strhash); INIT_LIST_HEAD(&clp->cl_openowners); INIT_LIST_HEAD(&clp->cl_delegations); INIT_LIST_HEAD(&clp->cl_sessions); INIT_LIST_HEAD(&clp->cl_lru); + clp->cl_time = get_seconds(); clear_bit(0, &clp->cl_cb_slot_busy); rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table"); copy_verf(clp, verf); @@ -877,8 +892,7 @@ add_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval) list_add(&clp->cl_strhash, &unconf_str_hashtbl[strhashval]); idhashval = clientid_hashval(clp->cl_clientid.cl_id); list_add(&clp->cl_idhash, &unconf_id_hashtbl[idhashval]); - list_add_tail(&clp->cl_lru, &client_lru); - clp->cl_time = get_seconds(); + renew_client(clp); } static void @@ -888,10 +902,9 @@ move_to_confirmed(struct nfs4_client *clp) unsigned int strhashval; dprintk("NFSD: move_to_confirm nfs4_client %p\n", clp); - list_del_init(&clp->cl_strhash); list_move(&clp->cl_idhash, &conf_id_hashtbl[idhashval]); strhashval = clientstr_hashval(clp->cl_recdir); - list_add(&clp->cl_strhash, &conf_str_hashtbl[strhashval]); + list_move(&clp->cl_strhash, &conf_str_hashtbl[strhashval]); renew_client(clp); } @@ -1327,15 +1340,9 @@ nfsd4_create_session(struct svc_rqst *rqstp, cs_slot->sl_seqid++; /* from 0 to 1 */ move_to_confirmed(unconf); - /* - * We do not support RDMA or persistent sessions - */ - cr_ses->flags &= ~SESSION4_PERSIST; - cr_ses->flags &= ~SESSION4_RDMA; - if (cr_ses->flags & SESSION4_BACK_CHAN) { - unconf->cl_cb_xprt = rqstp->rq_xprt; - svc_xprt_get(unconf->cl_cb_xprt); + unconf->cl_cb_conn.cb_xprt = rqstp->rq_xprt; + svc_xprt_get(rqstp->rq_xprt); rpc_copy_addr( (struct sockaddr *)&unconf->cl_cb_conn.cb_addr, sa); @@ -1344,7 +1351,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, cstate->minorversion; unconf->cl_cb_conn.cb_prog = cr_ses->callback_prog; unconf->cl_cb_seq_nr = 1; - nfsd4_probe_callback(unconf); + nfsd4_probe_callback(unconf, &unconf->cl_cb_conn); } conf = unconf; } else { @@ -1352,6 +1359,12 @@ nfsd4_create_session(struct svc_rqst *rqstp, goto out; } + /* + * We do not support RDMA or persistent sessions + */ + cr_ses->flags &= ~SESSION4_PERSIST; + cr_ses->flags &= ~SESSION4_RDMA; + status = alloc_init_session(rqstp, conf, cr_ses); if (status) goto out; @@ -1369,6 +1382,21 @@ out: return status; } +static bool nfsd4_last_compound_op(struct svc_rqst *rqstp) +{ + struct nfsd4_compoundres *resp = rqstp->rq_resp; + struct nfsd4_compoundargs *argp = rqstp->rq_argp; + + return argp->opcnt == resp->opcnt; +} + +static bool nfsd4_compound_in_session(struct nfsd4_session *session, struct nfs4_sessionid *sid) +{ + if (!session) + return 0; + return !memcmp(sid, &session->se_sessionid, sizeof(*sid)); +} + __be32 nfsd4_destroy_session(struct svc_rqst *r, struct nfsd4_compound_state *cstate, @@ -1384,19 +1412,25 @@ nfsd4_destroy_session(struct svc_rqst *r, * - Do we need to clear any callback info from previous session? */ + if (nfsd4_compound_in_session(cstate->session, &sessionid->sessionid)) { + if (!nfsd4_last_compound_op(r)) + return nfserr_not_only_op; + } dump_sessionid(__func__, &sessionid->sessionid); - spin_lock(&sessionid_lock); + spin_lock(&client_lock); ses = find_in_sessionid_hashtbl(&sessionid->sessionid); if (!ses) { - spin_unlock(&sessionid_lock); + spin_unlock(&client_lock); goto out; } unhash_session(ses); - spin_unlock(&sessionid_lock); + spin_unlock(&client_lock); + nfs4_lock_state(); /* wait for callbacks */ - shutdown_callback_client(ses->se_client); + nfsd4_set_callback_client(ses->se_client, NULL); + nfs4_unlock_state(); nfsd4_put_session(ses); status = nfs_ok; out: @@ -1417,7 +1451,7 @@ nfsd4_sequence(struct svc_rqst *rqstp, if (resp->opcnt != 1) return nfserr_sequence_pos; - spin_lock(&sessionid_lock); + spin_lock(&client_lock); status = nfserr_badsession; session = find_in_sessionid_hashtbl(&seq->sessionid); if (!session) @@ -1456,23 +1490,47 @@ nfsd4_sequence(struct svc_rqst *rqstp, cstate->slot = slot; cstate->session = session; - /* Hold a session reference until done processing the compound: - * nfsd4_put_session called only if the cstate slot is set. - */ - nfsd4_get_session(session); out: - spin_unlock(&sessionid_lock); - /* Renew the clientid on success and on replay */ + /* Hold a session reference until done processing the compound. */ if (cstate->session) { - nfs4_lock_state(); - renew_client(session->se_client); - nfs4_unlock_state(); + nfsd4_get_session(cstate->session); + atomic_inc(&session->se_client->cl_refcount); } + spin_unlock(&client_lock); dprintk("%s: return %d\n", __func__, ntohl(status)); return status; } __be32 +nfsd4_reclaim_complete(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_reclaim_complete *rc) +{ + if (rc->rca_one_fs) { + if (!cstate->current_fh.fh_dentry) + return nfserr_nofilehandle; + /* + * We don't take advantage of the rca_one_fs case. + * That's OK, it's optional, we can safely ignore it. + */ + return nfs_ok; + } + nfs4_lock_state(); + if (is_client_expired(cstate->session->se_client)) { + nfs4_unlock_state(); + /* + * The following error isn't really legal. + * But we only get here if the client just explicitly + * destroyed the client. Surely it no longer cares what + * error it gets back on an operation for the dead + * client. + */ + return nfserr_stale_clientid; + } + nfsd4_create_clid_dir(cstate->session->se_client); + nfs4_unlock_state(); + return nfs_ok; +} + +__be32 nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_setclientid *setclid) { @@ -1631,9 +1689,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, if (!same_creds(&conf->cl_cred, &unconf->cl_cred)) status = nfserr_clid_inuse; else { - /* XXX: We just turn off callbacks until we can handle - * change request correctly. */ - atomic_set(&conf->cl_cb_conn.cb_set, 0); + atomic_set(&conf->cl_cb_set, 0); + nfsd4_probe_callback(conf, &unconf->cl_cb_conn); expire_client(unconf); status = nfs_ok; @@ -1667,7 +1724,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, } move_to_confirmed(unconf); conf = unconf; - nfsd4_probe_callback(conf); + nfsd4_probe_callback(conf, &conf->cl_cb_conn); status = nfs_ok; } } else if ((!conf || (conf && !same_verf(&conf->cl_confirm, &confirm))) @@ -1700,12 +1757,12 @@ alloc_init_file(struct inode *ino) INIT_LIST_HEAD(&fp->fi_hash); INIT_LIST_HEAD(&fp->fi_stateids); INIT_LIST_HEAD(&fp->fi_delegations); - spin_lock(&recall_lock); - list_add(&fp->fi_hash, &file_hashtbl[hashval]); - spin_unlock(&recall_lock); fp->fi_inode = igrab(ino); fp->fi_id = current_fileid++; fp->fi_had_conflict = false; + spin_lock(&recall_lock); + list_add(&fp->fi_hash, &file_hashtbl[hashval]); + spin_unlock(&recall_lock); return fp; } return NULL; @@ -1827,7 +1884,7 @@ init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open * stp->st_stateowner = sop; get_nfs4_file(fp); stp->st_file = fp; - stp->st_stateid.si_boot = get_seconds(); + stp->st_stateid.si_boot = boot_time; stp->st_stateid.si_stateownerid = sop->so_id; stp->st_stateid.si_fileid = fp->fi_id; stp->st_stateid.si_generation = 0; @@ -2028,7 +2085,6 @@ void nfsd_break_deleg_cb(struct file_lock *fl) * lock) we know the server hasn't removed the lease yet, we know * it's safe to take a reference: */ atomic_inc(&dp->dl_count); - atomic_inc(&dp->dl_client->cl_count); spin_lock(&recall_lock); list_add_tail(&dp->dl_recall_lru, &del_recall_lru); @@ -2347,7 +2403,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta { struct nfs4_delegation *dp; struct nfs4_stateowner *sop = stp->st_stateowner; - struct nfs4_cb_conn *cb = &sop->so_client->cl_cb_conn; + int cb_up = atomic_read(&sop->so_client->cl_cb_set); struct file_lock fl, *flp = &fl; int status, flag = 0; @@ -2355,7 +2411,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta open->op_recall = 0; switch (open->op_claim_type) { case NFS4_OPEN_CLAIM_PREVIOUS: - if (!atomic_read(&cb->cb_set)) + if (!cb_up) open->op_recall = 1; flag = open->op_delegate_type; if (flag == NFS4_OPEN_DELEGATE_NONE) @@ -2366,7 +2422,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta * had the chance to reclaim theirs.... */ if (locks_in_grace()) goto out; - if (!atomic_read(&cb->cb_set) || !sop->so_confirmed) + if (!cb_up || !sop->so_confirmed) goto out; if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) flag = NFS4_OPEN_DELEGATE_WRITE; @@ -2483,10 +2539,8 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf } memcpy(&open->op_stateid, &stp->st_stateid, sizeof(stateid_t)); - if (nfsd4_has_session(&resp->cstate)) { + if (nfsd4_has_session(&resp->cstate)) open->op_stateowner->so_confirmed = 1; - nfsd4_create_clid_dir(open->op_stateowner->so_client); - } /* * Attempt to hand out a delegation. No error return, because the @@ -2537,7 +2591,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, renew_client(clp); status = nfserr_cb_path_down; if (!list_empty(&clp->cl_delegations) - && !atomic_read(&clp->cl_cb_conn.cb_set)) + && !atomic_read(&clp->cl_cb_set)) goto out; status = nfs_ok; out: @@ -2554,6 +2608,12 @@ nfsd4_end_grace(void) dprintk("NFSD: end of grace period\n"); nfsd4_recdir_purge_old(); locks_end_grace(&nfsd4_manager); + /* + * Now that every NFSv4 client has had the chance to recover and + * to see the (possibly new, possibly shorter) lease time, we + * can safely set the next grace time to the current lease time: + */ + nfsd4_grace = nfsd4_lease; } static time_t @@ -2563,15 +2623,17 @@ nfs4_laundromat(void) struct nfs4_stateowner *sop; struct nfs4_delegation *dp; struct list_head *pos, *next, reaplist; - time_t cutoff = get_seconds() - NFSD_LEASE_TIME; - time_t t, clientid_val = NFSD_LEASE_TIME; - time_t u, test_val = NFSD_LEASE_TIME; + time_t cutoff = get_seconds() - nfsd4_lease; + time_t t, clientid_val = nfsd4_lease; + time_t u, test_val = nfsd4_lease; nfs4_lock_state(); dprintk("NFSD: laundromat service - starting\n"); if (locks_in_grace()) nfsd4_end_grace(); + INIT_LIST_HEAD(&reaplist); + spin_lock(&client_lock); list_for_each_safe(pos, next, &client_lru) { clp = list_entry(pos, struct nfs4_client, cl_lru); if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { @@ -2580,12 +2642,22 @@ nfs4_laundromat(void) clientid_val = t; break; } + if (atomic_read(&clp->cl_refcount)) { + dprintk("NFSD: client in use (clientid %08x)\n", + clp->cl_clientid.cl_id); + continue; + } + unhash_client_locked(clp); + list_add(&clp->cl_lru, &reaplist); + } + spin_unlock(&client_lock); + list_for_each_safe(pos, next, &reaplist) { + clp = list_entry(pos, struct nfs4_client, cl_lru); dprintk("NFSD: purging unused client (clientid %08x)\n", clp->cl_clientid.cl_id); nfsd4_remove_clid_dir(clp); expire_client(clp); } - INIT_LIST_HEAD(&reaplist); spin_lock(&recall_lock); list_for_each_safe(pos, next, &del_recall_lru) { dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); @@ -2605,7 +2677,7 @@ nfs4_laundromat(void) list_del_init(&dp->dl_recall_lru); unhash_delegation(dp); } - test_val = NFSD_LEASE_TIME; + test_val = nfsd4_lease; list_for_each_safe(pos, next, &close_lru) { sop = list_entry(pos, struct nfs4_stateowner, so_close_lru); if (time_after((unsigned long)sop->so_time, (unsigned long)cutoff)) { @@ -2661,39 +2733,11 @@ nfs4_check_fh(struct svc_fh *fhp, struct nfs4_stateid *stp) static int STALE_STATEID(stateid_t *stateid) { - if (time_after((unsigned long)boot_time, - (unsigned long)stateid->si_boot)) { - dprintk("NFSD: stale stateid " STATEID_FMT "!\n", - STATEID_VAL(stateid)); - return 1; - } - return 0; -} - -static int -EXPIRED_STATEID(stateid_t *stateid) -{ - if (time_before((unsigned long)boot_time, - ((unsigned long)stateid->si_boot)) && - time_before((unsigned long)(stateid->si_boot + lease_time), get_seconds())) { - dprintk("NFSD: expired stateid " STATEID_FMT "!\n", - STATEID_VAL(stateid)); - return 1; - } - return 0; -} - -static __be32 -stateid_error_map(stateid_t *stateid) -{ - if (STALE_STATEID(stateid)) - return nfserr_stale_stateid; - if (EXPIRED_STATEID(stateid)) - return nfserr_expired; - - dprintk("NFSD: bad stateid " STATEID_FMT "!\n", + if (stateid->si_boot == boot_time) + return 0; + dprintk("NFSD: stale stateid " STATEID_FMT "!\n", STATEID_VAL(stateid)); - return nfserr_bad_stateid; + return 1; } static inline int @@ -2817,10 +2861,8 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, status = nfserr_bad_stateid; if (is_delegation_stateid(stateid)) { dp = find_delegation_stateid(ino, stateid); - if (!dp) { - status = stateid_error_map(stateid); + if (!dp) goto out; - } status = check_stateid_generation(stateid, &dp->dl_stateid, flags); if (status) @@ -2833,10 +2875,8 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, *filpp = dp->dl_vfs_file; } else { /* open or lock stateid */ stp = find_stateid(stateid, flags); - if (!stp) { - status = stateid_error_map(stateid); + if (!stp) goto out; - } if (nfs4_check_fh(current_fh, stp)) goto out; if (!stp->st_stateowner->so_confirmed) @@ -2908,7 +2948,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, */ sop = search_close_lru(stateid->si_stateownerid, flags); if (sop == NULL) - return stateid_error_map(stateid); + return nfserr_bad_stateid; *sopp = sop; goto check_replay; } @@ -3175,10 +3215,8 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (!is_delegation_stateid(stateid)) goto out; dp = find_delegation_stateid(inode, stateid); - if (!dp) { - status = stateid_error_map(stateid); + if (!dp) goto out; - } status = check_stateid_generation(stateid, &dp->dl_stateid, flags); if (status) goto out; @@ -3404,7 +3442,7 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc stp->st_stateowner = sop; get_nfs4_file(fp); stp->st_file = fp; - stp->st_stateid.si_boot = get_seconds(); + stp->st_stateid.si_boot = boot_time; stp->st_stateid.si_stateownerid = sop->so_id; stp->st_stateid.si_fileid = fp->fi_id; stp->st_stateid.si_generation = 0; @@ -3976,12 +4014,6 @@ nfsd4_load_reboot_recovery_data(void) printk("NFSD: Failure reading reboot recovery data\n"); } -unsigned long -get_nfs4_grace_period(void) -{ - return max(user_lease_time, lease_time) * HZ; -} - /* * Since the lifetime of a delegation isn't limited to that of an open, a * client may quite reasonably hang on to a delegation as long as it has @@ -4008,20 +4040,27 @@ set_max_delegations(void) static int __nfs4_state_start(void) { - unsigned long grace_time; + int ret; boot_time = get_seconds(); - grace_time = get_nfs4_grace_period(); - lease_time = user_lease_time; locks_start_grace(&nfsd4_manager); printk(KERN_INFO "NFSD: starting %ld-second grace period\n", - grace_time/HZ); + nfsd4_grace); + ret = set_callback_cred(); + if (ret) + return -ENOMEM; laundry_wq = create_singlethread_workqueue("nfsd4"); if (laundry_wq == NULL) return -ENOMEM; - queue_delayed_work(laundry_wq, &laundromat_work, grace_time); + ret = nfsd4_create_callback_queue(); + if (ret) + goto out_free_laundry; + queue_delayed_work(laundry_wq, &laundromat_work, nfsd4_grace * HZ); set_max_delegations(); - return set_callback_cred(); + return 0; +out_free_laundry: + destroy_workqueue(laundry_wq); + return ret; } int @@ -4039,12 +4078,6 @@ nfs4_state_start(void) return 0; } -time_t -nfs4_lease_time(void) -{ - return lease_time; -} - static void __nfs4_state_shutdown(void) { @@ -4089,6 +4122,7 @@ nfs4_state_shutdown(void) nfs4_lock_state(); nfs4_release_reclaim(); __nfs4_state_shutdown(); + nfsd4_destroy_callback_queue(); nfs4_unlock_state(); } @@ -4128,21 +4162,3 @@ nfs4_recoverydir(void) { return user_recovery_dirname; } - -/* - * Called when leasetime is changed. - * - * The only way the protocol gives us to handle on-the-fly lease changes is to - * simulate a reboot. Instead of doing that, we just wait till the next time - * we start to register any changes in lease time. If the administrator - * really wants to change the lease time *now*, they can go ahead and bring - * nfsd down and then back up again after changing the lease time. - * - * user_lease_time is protected by nfsd_mutex since it's only really accessed - * when nfsd is starting - */ -void -nfs4_reset_lease(time_t leasetime) -{ - user_lease_time = leasetime; -} diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 34ccf815ea8..ac17a708023 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1234,6 +1234,16 @@ nfsd4_decode_sequence(struct nfsd4_compoundargs *argp, DECODE_TAIL; } +static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc) +{ + DECODE_HEAD; + + READ_BUF(4); + READ32(rc->rca_one_fs); + + DECODE_TAIL; +} + static __be32 nfsd4_decode_noop(struct nfsd4_compoundargs *argp, void *p) { @@ -1346,7 +1356,7 @@ static nfsd4_dec nfsd41_dec_ops[] = { [OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp, [OP_WANT_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp, [OP_DESTROY_CLIENTID] = (nfsd4_dec)nfsd4_decode_notsupp, - [OP_RECLAIM_COMPLETE] = (nfsd4_dec)nfsd4_decode_notsupp, + [OP_RECLAIM_COMPLETE] = (nfsd4_dec)nfsd4_decode_reclaim_complete, }; struct nfsd4_minorversion_ops { @@ -1900,7 +1910,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, if (bmval0 & FATTR4_WORD0_LEASE_TIME) { if ((buflen -= 4) < 0) goto out_resource; - WRITE32(NFSD_LEASE_TIME); + WRITE32(nfsd4_lease); } if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) { if ((buflen -= 4) < 0) @@ -3307,11 +3317,14 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo iov = &rqstp->rq_res.head[0]; iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base; BUG_ON(iov->iov_len > PAGE_SIZE); - if (nfsd4_has_session(cs) && cs->status != nfserr_replay_cache) { - nfsd4_store_cache_entry(resp); - dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__); - resp->cstate.slot->sl_inuse = false; - nfsd4_put_session(resp->cstate.session); + if (nfsd4_has_session(cs)) { + if (cs->status != nfserr_replay_cache) { + nfsd4_store_cache_entry(resp); + dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__); + cs->slot->sl_inuse = false; + } + /* Renew the clientid on success and on replay */ + release_session_client(cs->session); } return 1; } diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index e3591073098..bc3194ea01f 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -46,6 +46,7 @@ enum { */ #ifdef CONFIG_NFSD_V4 NFSD_Leasetime, + NFSD_Gracetime, NFSD_RecoveryDir, #endif }; @@ -70,6 +71,7 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size); static ssize_t write_maxblksize(struct file *file, char *buf, size_t size); #ifdef CONFIG_NFSD_V4 static ssize_t write_leasetime(struct file *file, char *buf, size_t size); +static ssize_t write_gracetime(struct file *file, char *buf, size_t size); static ssize_t write_recoverydir(struct file *file, char *buf, size_t size); #endif @@ -91,6 +93,7 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = { [NFSD_MaxBlkSize] = write_maxblksize, #ifdef CONFIG_NFSD_V4 [NFSD_Leasetime] = write_leasetime, + [NFSD_Gracetime] = write_gracetime, [NFSD_RecoveryDir] = write_recoverydir, #endif }; @@ -1204,29 +1207,45 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) } #ifdef CONFIG_NFSD_V4 -extern time_t nfs4_leasetime(void); - -static ssize_t __write_leasetime(struct file *file, char *buf, size_t size) +static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, time_t *time) { - /* if size > 10 seconds, call - * nfs4_reset_lease() then write out the new lease (seconds) as reply - */ char *mesg = buf; - int rv, lease; + int rv, i; if (size > 0) { if (nfsd_serv) return -EBUSY; - rv = get_int(&mesg, &lease); + rv = get_int(&mesg, &i); if (rv) return rv; - if (lease < 10 || lease > 3600) + /* + * Some sanity checking. We don't have a reason for + * these particular numbers, but problems with the + * extremes are: + * - Too short: the briefest network outage may + * cause clients to lose all their locks. Also, + * the frequent polling may be wasteful. + * - Too long: do you really want reboot recovery + * to take more than an hour? Or to make other + * clients wait an hour before being able to + * revoke a dead client's locks? + */ + if (i < 10 || i > 3600) return -EINVAL; - nfs4_reset_lease(lease); + *time = i; } - return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n", - nfs4_lease_time()); + return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n", *time); +} + +static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, time_t *time) +{ + ssize_t rv; + + mutex_lock(&nfsd_mutex); + rv = __nfsd4_write_time(file, buf, size, time); + mutex_unlock(&nfsd_mutex); + return rv; } /** @@ -1252,12 +1271,22 @@ static ssize_t __write_leasetime(struct file *file, char *buf, size_t size) */ static ssize_t write_leasetime(struct file *file, char *buf, size_t size) { - ssize_t rv; + return nfsd4_write_time(file, buf, size, &nfsd4_lease); +} - mutex_lock(&nfsd_mutex); - rv = __write_leasetime(file, buf, size); - mutex_unlock(&nfsd_mutex); - return rv; +/** + * write_gracetime - Set or report current NFSv4 grace period time + * + * As above, but sets the time of the NFSv4 grace period. + * + * Note this should never be set to less than the *previous* + * lease-period time, but we don't try to enforce this. (In the common + * case (a new boot), we don't know what the previous lease time was + * anyway.) + */ +static ssize_t write_gracetime(struct file *file, char *buf, size_t size) +{ + return nfsd4_write_time(file, buf, size, &nfsd4_grace); } extern char *nfs4_recoverydir(void); @@ -1351,6 +1380,7 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent) [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO}, #ifdef CONFIG_NFSD_V4 [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR}, + [NFSD_Gracetime] = {"nfsv4gracetime", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR}, #endif /* last one */ {""} diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index e942a1aaac9..72377761270 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -82,7 +82,6 @@ int nfs4_state_init(void); void nfsd4_free_slabs(void); int nfs4_state_start(void); void nfs4_state_shutdown(void); -time_t nfs4_lease_time(void); void nfs4_reset_lease(time_t leasetime); int nfs4_reset_recoverydir(char *recdir); #else @@ -90,7 +89,6 @@ static inline int nfs4_state_init(void) { return 0; } static inline void nfsd4_free_slabs(void) { } static inline int nfs4_state_start(void) { return 0; } static inline void nfs4_state_shutdown(void) { } -static inline time_t nfs4_lease_time(void) { return 0; } static inline void nfs4_reset_lease(time_t leasetime) { } static inline int nfs4_reset_recoverydir(char *recdir) { return 0; } #endif @@ -229,6 +227,9 @@ extern struct timeval nfssvc_boot; #ifdef CONFIG_NFSD_V4 +extern time_t nfsd4_lease; +extern time_t nfsd4_grace; + /* before processing a COMPOUND operation, we have to check that there * is enough space in the buffer for XDR encode to succeed. otherwise, * we might process an operation with side effects, and be unable to @@ -247,7 +248,6 @@ extern struct timeval nfssvc_boot; #define COMPOUND_SLACK_SPACE 140 /* OP_GETFH */ #define COMPOUND_ERR_SLACK_SPACE 12 /* OP_SETATTR */ -#define NFSD_LEASE_TIME (nfs4_lease_time()) #define NFSD_LAUNDROMAT_MINTIMEOUT 10 /* seconds */ /* diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 171699eb07c..06b2a26edfe 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -120,7 +120,7 @@ u32 nfsd_supported_minorversion; int nfsd_vers(int vers, enum vers_op change) { if (vers < NFSD_MINVERS || vers >= NFSD_NRVERS) - return -1; + return 0; switch(change) { case NFSD_SET: nfsd_versions[vers] = nfsd_version[vers]; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index fefeae27f25..006c84230c7 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -70,6 +70,16 @@ struct nfsd4_cb_sequence { struct nfs4_client *cbs_clp; }; +struct nfs4_rpc_args { + void *args_op; + struct nfsd4_cb_sequence args_seq; +}; + +struct nfsd4_callback { + struct nfs4_rpc_args cb_args; + struct work_struct cb_work; +}; + struct nfs4_delegation { struct list_head dl_perfile; struct list_head dl_perclnt; @@ -86,6 +96,7 @@ struct nfs4_delegation { stateid_t dl_stateid; struct knfsd_fh dl_fh; int dl_retries; + struct nfsd4_callback dl_recall; }; /* client delegation callback info */ @@ -96,9 +107,7 @@ struct nfs4_cb_conn { u32 cb_prog; u32 cb_minorversion; u32 cb_ident; /* minorversion 0 only */ - /* RPC client info */ - atomic_t cb_set; /* successful CB_NULL call */ - struct rpc_clnt * cb_client; + struct svc_xprt *cb_xprt; /* minorversion 1 only */ }; /* Maximum number of slots per session. 160 is useful for long haul TCP */ @@ -157,7 +166,7 @@ struct nfsd4_session { struct list_head se_hash; /* hash by sessionid */ struct list_head se_perclnt; u32 se_flags; - struct nfs4_client *se_client; /* for expire_client */ + struct nfs4_client *se_client; struct nfs4_sessionid se_sessionid; struct nfsd4_channel_attrs se_fchannel; struct nfsd4_channel_attrs se_bchannel; @@ -212,25 +221,41 @@ struct nfs4_client { struct svc_cred cl_cred; /* setclientid principal */ clientid_t cl_clientid; /* generated by server */ nfs4_verifier cl_confirm; /* generated by server */ - struct nfs4_cb_conn cl_cb_conn; /* callback info */ - atomic_t cl_count; /* ref count */ u32 cl_firststate; /* recovery dir creation */ + /* for v4.0 and v4.1 callbacks: */ + struct nfs4_cb_conn cl_cb_conn; + struct rpc_clnt *cl_cb_client; + atomic_t cl_cb_set; + /* for nfs41 */ struct list_head cl_sessions; struct nfsd4_clid_slot cl_cs_slot; /* create_session slot */ u32 cl_exchange_flags; struct nfs4_sessionid cl_sessionid; + /* number of rpc's in progress over an associated session: */ + atomic_t cl_refcount; /* for nfs41 callbacks */ /* We currently support a single back channel with a single slot */ unsigned long cl_cb_slot_busy; u32 cl_cb_seq_nr; - struct svc_xprt *cl_cb_xprt; /* 4.1 callback transport */ struct rpc_wait_queue cl_cb_waitq; /* backchannel callers may */ /* wait here for slots */ }; +static inline void +mark_client_expired(struct nfs4_client *clp) +{ + clp->cl_time = 0; +} + +static inline bool +is_client_expired(struct nfs4_client *clp) +{ + return clp->cl_time == 0; +} + /* struct nfs4_client_reset * one per old client. Populates reset_str_hashtbl. Filled from conf_id_hashtbl * upon lease reset, or from upcall to state_daemon (to read in state @@ -377,11 +402,14 @@ extern void nfs4_lock_state(void); extern void nfs4_unlock_state(void); extern int nfs4_in_grace(void); extern __be32 nfs4_check_open_reclaim(clientid_t *clid); -extern void put_nfs4_client(struct nfs4_client *clp); extern void nfs4_free_stateowner(struct kref *kref); extern int set_callback_cred(void); -extern void nfsd4_probe_callback(struct nfs4_client *clp); +extern void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *); +extern void nfsd4_do_callback_rpc(struct work_struct *); extern void nfsd4_cb_recall(struct nfs4_delegation *dp); +extern int nfsd4_create_callback_queue(void); +extern void nfsd4_destroy_callback_queue(void); +extern void nfsd4_set_callback_client(struct nfs4_client *, struct rpc_clnt *); extern void nfs4_put_delegation(struct nfs4_delegation *dp); extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname); extern void nfsd4_init_recdir(char *recdir_name); @@ -392,6 +420,7 @@ extern int nfs4_has_reclaimed_state(const char *name, bool use_exchange_id); extern void nfsd4_recdir_purge_old(void); extern int nfsd4_create_clid_dir(struct nfs4_client *clp); extern void nfsd4_remove_clid_dir(struct nfs4_client *clp); +extern void release_session_client(struct nfsd4_session *); static inline void nfs4_put_stateowner(struct nfs4_stateowner *so) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 6dd5f1970e0..23c06f77f4c 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -724,7 +724,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, struct inode *inode; int flags = O_RDONLY|O_LARGEFILE; __be32 err; - int host_err; + int host_err = 0; validate_process_creds(); @@ -761,7 +761,8 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, * Check to see if there are any leases on this file. * This may block while leases are broken. */ - host_err = break_lease(inode, O_NONBLOCK | ((access & NFSD_MAY_WRITE) ? O_WRONLY : 0)); + if (!(access & NFSD_MAY_NOT_BREAK_LEASE)) + host_err = break_lease(inode, O_NONBLOCK | ((access & NFSD_MAY_WRITE) ? O_WRONLY : 0)); if (host_err == -EWOULDBLOCK) host_err = -ETIMEDOUT; if (host_err) /* NOMEM or WOULDBLOCK */ @@ -1169,7 +1170,8 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, goto out; } - err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_WRITE, &file); + err = nfsd_open(rqstp, fhp, S_IFREG, + NFSD_MAY_WRITE|NFSD_MAY_NOT_BREAK_LEASE, &file); if (err) goto out; if (EX_ISSYNC(fhp->fh_export)) { diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h index 4b1de0a9ea7..217a62c2a35 100644 --- a/fs/nfsd/vfs.h +++ b/fs/nfsd/vfs.h @@ -20,6 +20,7 @@ #define NFSD_MAY_OWNER_OVERRIDE 64 #define NFSD_MAY_LOCAL_ACCESS 128 /* IRIX doing local access check on device special file*/ #define NFSD_MAY_BYPASS_GSS_ON_ROOT 256 +#define NFSD_MAY_NOT_BREAK_LEASE 512 #define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE) #define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC) diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index efa33773953..4d476ff08ae 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -381,6 +381,10 @@ struct nfsd4_destroy_session { struct nfs4_sessionid sessionid; }; +struct nfsd4_reclaim_complete { + u32 rca_one_fs; +}; + struct nfsd4_op { int opnum; __be32 status; @@ -421,6 +425,7 @@ struct nfsd4_op { struct nfsd4_create_session create_session; struct nfsd4_destroy_session destroy_session; struct nfsd4_sequence sequence; + struct nfsd4_reclaim_complete reclaim_complete; } u; struct nfs4_replay * replay; }; @@ -513,9 +518,8 @@ extern void nfsd4_store_cache_entry(struct nfsd4_compoundres *resp); extern __be32 nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp, struct nfsd4_sequence *seq); extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp, - struct nfsd4_compound_state *, -struct nfsd4_exchange_id *); - extern __be32 nfsd4_create_session(struct svc_rqst *, + struct nfsd4_compound_state *, struct nfsd4_exchange_id *); +extern __be32 nfsd4_create_session(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_create_session *); extern __be32 nfsd4_sequence(struct svc_rqst *, @@ -524,6 +528,7 @@ extern __be32 nfsd4_sequence(struct svc_rqst *, extern __be32 nfsd4_destroy_session(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_destroy_session *); +__be32 nfsd4_reclaim_complete(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_reclaim_complete *); extern __be32 nfsd4_process_open1(struct nfsd4_compound_state *, struct nfsd4_open *open); extern __be32 nfsd4_process_open2(struct svc_rqst *rqstp, diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c index c82af6acc2e..b44bb835e8e 100644 --- a/fs/omfs/inode.c +++ b/fs/omfs/inode.c @@ -3,7 +3,6 @@ * Copyright (C) 2006 Bob Copeland <me@bobcopeland.com> * Released under GPL v2. */ -#include <linux/version.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/slab.h> diff --git a/fs/proc/base.c b/fs/proc/base.c index 8418fcc0a6a..c7f9f23449d 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -730,6 +730,7 @@ out_no_task: static const struct file_operations proc_info_file_operations = { .read = proc_info_read, + .llseek = generic_file_llseek, }; static int proc_single_show(struct seq_file *m, void *v) @@ -987,6 +988,7 @@ out_no_task: static const struct file_operations proc_environ_operations = { .read = environ_read, + .llseek = generic_file_llseek, }; static ssize_t oom_adjust_read(struct file *file, char __user *buf, @@ -1060,6 +1062,7 @@ static ssize_t oom_adjust_write(struct file *file, const char __user *buf, static const struct file_operations proc_oom_adjust_operations = { .read = oom_adjust_read, .write = oom_adjust_write, + .llseek = generic_file_llseek, }; #ifdef CONFIG_AUDITSYSCALL @@ -1131,6 +1134,7 @@ out_free_page: static const struct file_operations proc_loginuid_operations = { .read = proc_loginuid_read, .write = proc_loginuid_write, + .llseek = generic_file_llseek, }; static ssize_t proc_sessionid_read(struct file * file, char __user * buf, @@ -1151,6 +1155,7 @@ static ssize_t proc_sessionid_read(struct file * file, char __user * buf, static const struct file_operations proc_sessionid_operations = { .read = proc_sessionid_read, + .llseek = generic_file_llseek, }; #endif @@ -1202,6 +1207,7 @@ static ssize_t proc_fault_inject_write(struct file * file, static const struct file_operations proc_fault_inject_operations = { .read = proc_fault_inject_read, .write = proc_fault_inject_write, + .llseek = generic_file_llseek, }; #endif @@ -1943,7 +1949,7 @@ static ssize_t proc_fdinfo_read(struct file *file, char __user *buf, } static const struct file_operations proc_fdinfo_file_operations = { - .open = nonseekable_open, + .open = nonseekable_open, .read = proc_fdinfo_read, }; @@ -2227,6 +2233,7 @@ out_no_task: static const struct file_operations proc_pid_attr_operations = { .read = proc_pid_attr_read, .write = proc_pid_attr_write, + .llseek = generic_file_llseek, }; static const struct pid_entry attr_dir_stuff[] = { @@ -2347,6 +2354,7 @@ static ssize_t proc_coredump_filter_write(struct file *file, static const struct file_operations proc_coredump_filter_operations = { .read = proc_coredump_filter_read, .write = proc_coredump_filter_write, + .llseek = generic_file_llseek, }; #endif diff --git a/fs/proc/inode.c b/fs/proc/inode.c index d35b23238fb..aea8502e58a 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -232,9 +232,9 @@ static long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigne if (rv == -ENOIOCTLCMD) rv = -EINVAL; } else if (ioctl) { - lock_kernel(); + WARN_ONCE(1, "Procfs ioctl handlers must use unlocked_ioctl, " + "%pf will be called without the Bkl held\n", ioctl); rv = ioctl(file->f_path.dentry->d_inode, file, cmd, arg); - unlock_kernel(); } pde_users_dec(pde); diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c index 19979a2ce27..c837a77351b 100644 --- a/fs/proc/kcore.c +++ b/fs/proc/kcore.c @@ -558,6 +558,7 @@ static int open_kcore(struct inode *inode, struct file *filp) static const struct file_operations proc_kcore_operations = { .read = read_kcore, .open = open_kcore, + .llseek = generic_file_llseek, }; #ifdef CONFIG_MEMORY_HOTPLUG diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c index cfe90a48a6e..bd4b5a740ff 100644 --- a/fs/proc/kmsg.c +++ b/fs/proc/kmsg.c @@ -53,6 +53,7 @@ static const struct file_operations proc_kmsg_operations = { .poll = kmsg_poll, .open = kmsg_open, .release = kmsg_release, + .llseek = generic_file_llseek, }; static int __init proc_kmsg_init(void) diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c index 9fbc99ec799..91c817ff02c 100644 --- a/fs/proc/vmcore.c +++ b/fs/proc/vmcore.c @@ -163,6 +163,7 @@ static ssize_t read_vmcore(struct file *file, char __user *buffer, static const struct file_operations proc_vmcore_operations = { .read = read_vmcore, + .llseek = generic_file_llseek, }; static struct vmcore* __init get_new_element(void) |