From 168e4b39d1afb79a7e3ea6c3bb246b4c82c6bdb9 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 30 Oct 2012 17:01:40 -0400 Subject: SUNRPC: add WARN_ON_ONCE for potential deadlock rpc_shutdown_client should never be called from a workqueue context. If it is, it could deadlock looping forever trying to kill tasks that are assigned to the same kworker thread (and will never run rpc_exit_task). Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index cdc7564..dd2532c 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -607,6 +607,13 @@ EXPORT_SYMBOL_GPL(rpc_killall_tasks); */ void rpc_shutdown_client(struct rpc_clnt *clnt) { + /* + * To avoid deadlock, never call rpc_shutdown_client from a + * workqueue context! + */ + WARN_ON_ONCE(current->flags & PF_WQ_WORKER); + might_sleep(); + dprintk_rcu("RPC: shutting down %s client for %s\n", clnt->cl_protname, rcu_dereference(clnt->cl_xprt)->servername); -- cgit v0.10.2 From 1fea73a86527d7ec463af6ff04b0830e1425ff6c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 15 Oct 2012 11:24:57 -0400 Subject: NFS: Get rid of unnecessary asserts If the nfs_client fails to initialise correctly, then it will return an error condition. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 8b39a42..c285e0a 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -277,7 +277,7 @@ void nfs_put_client(struct nfs_client *clp) nfs_cb_idr_remove_locked(clp); spin_unlock(&nn->nfs_client_lock); - BUG_ON(!list_empty(&clp->cl_superblocks)); + WARN_ON_ONCE(!list_empty(&clp->cl_superblocks)); clp->rpc_ops->free_client(clp); } @@ -1061,10 +1061,6 @@ struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info, if (error < 0) goto error; - BUG_ON(!server->nfs_client); - BUG_ON(!server->nfs_client->rpc_ops); - BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); - /* Probe the root fh to retrieve its FSID */ error = nfs_probe_fsinfo(server, mount_info->mntfh, fattr); if (error < 0) diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 6bacfde..72717e6 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -713,10 +713,6 @@ static int nfs4_server_common_setup(struct nfs_server *server, struct nfs_fattr *fattr; int error; - BUG_ON(!server->nfs_client); - BUG_ON(!server->nfs_client->rpc_ops); - BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); - /* data servers support only a subset of NFSv4.1 */ if (is_ds_only_client(server->nfs_client)) return -EPROTONOSUPPORT; -- cgit v0.10.2 From 7fc388460e8479c5b3120cb2fcf0e0daec70b93f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 15 Oct 2012 11:51:21 -0400 Subject: NFS: Remove asserts from the NFS XDR code Convert the ones that are not trivial to check into WARN_ON_ONCE(). Remove checks for things such as NFS2_MAXPATHLEN, which are trivially done by the caller. Add a comment to the case of nfs3_xdr_enc_setacl3args. What is being done there is just wrong... Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index d04f0df..06b9df4 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -195,7 +195,6 @@ static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh) { __be32 *p; - BUG_ON(fh->size != NFS2_FHSIZE); p = xdr_reserve_space(xdr, NFS2_FHSIZE); memcpy(p, fh->data, NFS2_FHSIZE); } @@ -388,7 +387,7 @@ static void encode_filename(struct xdr_stream *xdr, { __be32 *p; - BUG_ON(length > NFS2_MAXNAMLEN); + WARN_ON_ONCE(length > NFS2_MAXNAMLEN); p = xdr_reserve_space(xdr, 4 + length); xdr_encode_opaque(p, name, length); } @@ -428,7 +427,6 @@ static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length) { __be32 *p; - BUG_ON(length > NFS2_MAXPATHLEN); p = xdr_reserve_space(xdr, 4); *p = cpu_to_be32(length); xdr_write_pages(xdr, pages, 0, length); diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index 6cbe894..bffc324 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -198,7 +198,7 @@ static void encode_filename3(struct xdr_stream *xdr, { __be32 *p; - BUG_ON(length > NFS3_MAXNAMLEN); + WARN_ON_ONCE(length > NFS3_MAXNAMLEN); p = xdr_reserve_space(xdr, 4 + length); xdr_encode_opaque(p, name, length); } @@ -238,7 +238,6 @@ out_overflow: static void encode_nfspath3(struct xdr_stream *xdr, struct page **pages, const u32 length) { - BUG_ON(length > NFS3_MAXPATHLEN); encode_uint32(xdr, length); xdr_write_pages(xdr, pages, 0, length); } @@ -388,7 +387,6 @@ out_overflow: */ static void encode_ftype3(struct xdr_stream *xdr, const u32 type) { - BUG_ON(type > NF3FIFO); encode_uint32(xdr, type); } @@ -443,7 +441,7 @@ static void encode_nfs_fh3(struct xdr_stream *xdr, const struct nfs_fh *fh) { __be32 *p; - BUG_ON(fh->size > NFS3_FHSIZE); + WARN_ON_ONCE(fh->size > NFS3_FHSIZE); p = xdr_reserve_space(xdr, 4 + fh->size); xdr_encode_opaque(p, fh->data, fh->size); } @@ -1339,6 +1337,7 @@ static void nfs3_xdr_enc_setacl3args(struct rpc_rqst *req, error = nfsacl_encode(xdr->buf, base, args->inode, (args->mask & NFS_ACL) ? args->acl_access : NULL, 1, 0); + /* FIXME: this is just broken */ BUG_ON(error < 0); error = nfsacl_encode(xdr->buf, base + error, args->inode, (args->mask & NFS_DFACL) ? diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 40836ee..672d9b0 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -936,7 +936,7 @@ static void encode_compound_hdr(struct xdr_stream *xdr, * but this is not required as a MUST for the server to do so. */ hdr->replen = RPC_REPHDRSIZE + auth->au_rslack + 3 + hdr->taglen; - BUG_ON(hdr->taglen > NFS4_MAXTAGLEN); + WARN_ON_ONCE(hdr->taglen > NFS4_MAXTAGLEN); encode_string(xdr, hdr->taglen, hdr->tag); p = reserve_space(xdr, 8); *p++ = cpu_to_be32(hdr->minorversion); @@ -955,7 +955,7 @@ static void encode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 op, static void encode_nops(struct compound_hdr *hdr) { - BUG_ON(hdr->nops > NFS4_MAX_OPS); + WARN_ON_ONCE(hdr->nops > NFS4_MAX_OPS); *hdr->nops_p = htonl(hdr->nops); } @@ -1403,7 +1403,6 @@ static void encode_opentype(struct xdr_stream *xdr, const struct nfs_openargs *a *p = cpu_to_be32(NFS4_OPEN_NOCREATE); break; default: - BUG_ON(arg->claim != NFS4_OPEN_CLAIM_NULL); *p = cpu_to_be32(NFS4_OPEN_CREATE); encode_createmode(xdr, arg); } @@ -1621,7 +1620,6 @@ encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compoun p = reserve_space(xdr, 2*4); *p++ = cpu_to_be32(1); *p = cpu_to_be32(FATTR4_WORD0_ACL); - BUG_ON(arg->acl_len % 4); p = reserve_space(xdr, 4); *p = cpu_to_be32(arg->acl_len); xdr_write_pages(xdr, arg->acl_pages, arg->acl_pgbase, arg->acl_len); -- cgit v0.10.2 From d3edcf96141a7729b12ef5ecab6d5f634e24c61a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 15 Oct 2012 13:14:43 -0400 Subject: NFSv4: Remove the BUG_ON() from nfs4_get_lease_time_prepare()... An EAGAIN return value would be unexpected, but there is no reason to BUG... Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 5eec442..14d86ef 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5581,8 +5581,8 @@ static void nfs4_get_lease_time_prepare(struct rpc_task *task, &data->args->la_seq_args, &data->res->lr_seq_res, task); - BUG_ON(ret == -EAGAIN); - rpc_call_start(task); + if (ret != -EAGAIN) + rpc_call_start(task); dprintk("<-- %s\n", __func__); } -- cgit v0.10.2 From eba24e1fe57df4e4cdee58af940f762eb336a113 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 15 Oct 2012 14:47:33 -0400 Subject: NFSv4.1: Remove unused function last_byte_offset Signed-off-by: Trond Myklebust diff --git a/fs/nfs/objlayout/objlayout.c b/fs/nfs/objlayout/objlayout.c index 8746135..a9ebd81 100644 --- a/fs/nfs/objlayout/objlayout.c +++ b/fs/nfs/objlayout/objlayout.c @@ -148,17 +148,6 @@ end_offset(u64 start, u64 len) return end >= start ? end : NFS4_MAX_UINT64; } -/* last octet in a range */ -static inline u64 -last_byte_offset(u64 start, u64 len) -{ - u64 end; - - BUG_ON(!len); - end = start + len; - return end > start ? end - 1 : NFS4_MAX_UINT64; -} - static void _fix_verify_io_params(struct pnfs_layout_segment *lseg, struct page ***p_pages, unsigned *p_pgbase, u64 offset, unsigned long count) diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 2878f97..dcbc9b2 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -369,17 +369,6 @@ end_offset(u64 start, u64 len) return end >= start ? end : NFS4_MAX_UINT64; } -/* last octet in a range */ -static inline u64 -last_byte_offset(u64 start, u64 len) -{ - u64 end; - - BUG_ON(!len); - end = start + len; - return end > start ? end - 1 : NFS4_MAX_UINT64; -} - /* * is l2 fully contained in l1? * start1 end1 -- cgit v0.10.2 From bc5a89b337ee4b2fa6f577e7e1220d8c1ece71fc Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 15 Oct 2012 14:58:04 -0400 Subject: NFSv4.1: Remove assertion BUG_ON()s from the files and generic layout code Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index 2e45fd9..bfb28fa 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c @@ -512,7 +512,6 @@ filelayout_read_pagelist(struct nfs_read_data *data) loff_t offset = data->args.offset; u32 j, idx; struct nfs_fh *fh; - int status; dprintk("--> %s ino %lu pgbase %u req %Zu@%llu\n", __func__, hdr->inode->i_ino, @@ -538,9 +537,8 @@ filelayout_read_pagelist(struct nfs_read_data *data) data->mds_offset = offset; /* Perform an asynchronous read to ds */ - status = nfs_initiate_read(ds->ds_clp->cl_rpcclient, data, + nfs_initiate_read(ds->ds_clp->cl_rpcclient, data, &filelayout_read_call_ops, RPC_TASK_SOFTCONN); - BUG_ON(status != 0); return PNFS_ATTEMPTED; } @@ -554,7 +552,6 @@ filelayout_write_pagelist(struct nfs_write_data *data, int sync) loff_t offset = data->args.offset; u32 j, idx; struct nfs_fh *fh; - int status; /* Retrieve the correct rpc_client for the byte range */ j = nfs4_fl_calc_j_index(lseg, offset); @@ -579,10 +576,9 @@ filelayout_write_pagelist(struct nfs_write_data *data, int sync) data->args.offset = filelayout_get_dserver_offset(lseg, offset); /* Perform an asynchronous write */ - status = nfs_initiate_write(ds->ds_clp->cl_rpcclient, data, + nfs_initiate_write(ds->ds_clp->cl_rpcclient, data, &filelayout_write_call_ops, sync, RPC_TASK_SOFTCONN); - BUG_ON(status != 0); return PNFS_ATTEMPTED; } @@ -909,7 +905,7 @@ static void filelayout_pg_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *req) { - BUG_ON(pgio->pg_lseg != NULL); + WARN_ON_ONCE(pgio->pg_lseg != NULL); if (req->wb_offset != req->wb_pgbase) { /* @@ -939,7 +935,7 @@ filelayout_pg_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_commit_info cinfo; int status; - BUG_ON(pgio->pg_lseg != NULL); + WARN_ON_ONCE(pgio->pg_lseg != NULL); if (req->wb_offset != req->wb_pgbase) goto out_mds; @@ -1187,7 +1183,6 @@ static void filelayout_recover_commit_reqs(struct list_head *dst, */ for (i = 0, b = cinfo->ds->buckets; i < cinfo->ds->nbuckets; i++, b++) { if (transfer_commit_list(&b->written, dst, cinfo, 0)) { - BUG_ON(!list_empty(&b->written)); pnfs_put_lseg(b->wlseg); b->wlseg = NULL; } diff --git a/fs/nfs/nfs4filelayoutdev.c b/fs/nfs/nfs4filelayoutdev.c index a8eaa9b..93e2530 100644 --- a/fs/nfs/nfs4filelayoutdev.c +++ b/fs/nfs/nfs4filelayoutdev.c @@ -162,8 +162,6 @@ nfs4_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds) dprintk("--> %s DS %s au_flavor %d\n", __func__, ds->ds_remotestr, mds_srv->nfs_client->cl_rpcclient->cl_auth->au_flavor); - BUG_ON(list_empty(&ds->ds_addrs)); - list_for_each_entry(da, &ds->ds_addrs, da_node) { dprintk("%s: DS %s: trying address %s\n", __func__, ds->ds_remotestr, da->da_remotestr); diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index dcbc9b2..e7165d9 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -634,7 +634,6 @@ send_layoutget(struct pnfs_layout_hdr *lo, dprintk("--> %s\n", __func__); - BUG_ON(ctx == NULL); lgp = kzalloc(sizeof(*lgp), gfp_flags); if (lgp == NULL) return NULL; @@ -1115,7 +1114,6 @@ pnfs_update_layout(struct inode *ino, * chance of a CB_LAYOUTRECALL(FILE) coming in. */ spin_lock(&clp->cl_lock); - BUG_ON(!list_empty(&lo->plh_layouts)); list_add_tail(&lo->plh_layouts, &server->layouts); spin_unlock(&clp->cl_lock); } @@ -1211,7 +1209,7 @@ pnfs_generic_pg_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *r { u64 rd_size = req->wb_bytes; - BUG_ON(pgio->pg_lseg != NULL); + WARN_ON_ONCE(pgio->pg_lseg != NULL); if (req->wb_offset != req->wb_pgbase) { nfs_pageio_reset_read_mds(pgio); @@ -1240,7 +1238,7 @@ void pnfs_generic_pg_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *req, u64 wb_size) { - BUG_ON(pgio->pg_lseg != NULL); + WARN_ON_ONCE(pgio->pg_lseg != NULL); if (req->wb_offset != req->wb_pgbase) { nfs_pageio_reset_write_mds(pgio); -- cgit v0.10.2 From deed85e760c8c88cd984c5921dd8cb6b697b6134 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 15 Oct 2012 15:02:01 -0400 Subject: NFS: Remove BUG_ON() calls from the generic writeback code ...and ensure that we set the return value for nfs_page_async_flush() to zero! (Reported-by: Dros Adamson) Signed-off-by: Trond Myklebust diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 9347ab7..f5bc8e1 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -239,21 +239,18 @@ int nfs_congestion_kb; #define NFS_CONGESTION_OFF_THRESH \ (NFS_CONGESTION_ON_THRESH - (NFS_CONGESTION_ON_THRESH >> 2)) -static int nfs_set_page_writeback(struct page *page) +static void nfs_set_page_writeback(struct page *page) { + struct nfs_server *nfss = NFS_SERVER(page_file_mapping(page)->host); int ret = test_set_page_writeback(page); - if (!ret) { - struct inode *inode = page_file_mapping(page)->host; - struct nfs_server *nfss = NFS_SERVER(inode); + WARN_ON_ONCE(ret != 0); - if (atomic_long_inc_return(&nfss->writeback) > - NFS_CONGESTION_ON_THRESH) { - set_bdi_congested(&nfss->backing_dev_info, - BLK_RW_ASYNC); - } + if (atomic_long_inc_return(&nfss->writeback) > + NFS_CONGESTION_ON_THRESH) { + set_bdi_congested(&nfss->backing_dev_info, + BLK_RW_ASYNC); } - return ret; } static void nfs_end_page_writeback(struct page *page) @@ -315,10 +312,10 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, if (IS_ERR(req)) goto out; - ret = nfs_set_page_writeback(page); - BUG_ON(ret != 0); - BUG_ON(test_bit(PG_CLEAN, &req->wb_flags)); + nfs_set_page_writeback(page); + WARN_ON_ONCE(test_bit(PG_CLEAN, &req->wb_flags)); + ret = 0; if (!nfs_pageio_add_request(pgio, req)) { nfs_redirty_request(req); ret = pgio->pg_error; @@ -451,8 +448,6 @@ static void nfs_inode_remove_request(struct nfs_page *req) struct inode *inode = req->wb_context->dentry->d_inode; struct nfs_inode *nfsi = NFS_I(inode); - BUG_ON (!NFS_WBACK_BUSY(req)); - spin_lock(&inode->i_lock); if (likely(!PageSwapCache(req->wb_page))) { set_page_private(req->wb_page, 0); @@ -1727,7 +1722,6 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page) struct nfs_page *req; int ret = 0; - BUG_ON(!PageLocked(page)); for (;;) { wait_on_page_writeback(page); req = nfs_page_find_request(page); -- cgit v0.10.2 From 4ea8fed593218b658927b763f02941cd16c2ed9d Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 15 Oct 2012 15:47:41 -0400 Subject: NFSv4: Get rid of unnecessary BUG_ON()s Signed-off-by: Trond Myklebust diff --git a/fs/nfs/cache_lib.c b/fs/nfs/cache_lib.c index dded263..862a2f1 100644 --- a/fs/nfs/cache_lib.c +++ b/fs/nfs/cache_lib.c @@ -118,7 +118,6 @@ int nfs_cache_register_sb(struct super_block *sb, struct cache_detail *cd) struct dentry *dir; dir = rpc_d_lookup_sb(sb, "cache"); - BUG_ON(dir == NULL); ret = sunrpc_cache_register_pipefs(dir, cd->name, 0600, cd); dput(dir); return ret; diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 76b4a7a..0be08b9 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -216,7 +216,6 @@ static u32 initiate_bulk_draining(struct nfs_client *clp, } pnfs_get_layout_hdr(lo); spin_unlock(&ino->i_lock); - BUG_ON(!list_empty(&lo->plh_bulk_recall)); list_add(&lo->plh_bulk_recall, &recall_list); } } diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c index afddd66..e769930 100644 --- a/fs/nfs/nfs4file.c +++ b/fs/nfs/nfs4file.c @@ -20,7 +20,6 @@ nfs4_file_open(struct inode *inode, struct file *filp) struct iattr attr; int err; - BUG_ON(inode != dentry->d_inode); /* * If no cached dentry exists or if it's negative, NFSv4 handled the * opens in ->lookup() or ->create(). diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 14d86ef..6300cdd 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -206,7 +206,6 @@ static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dent { __be32 *start, *p; - BUG_ON(readdir->count < 80); if (cookie > 2) { readdir->cookie = cookie; memcpy(&readdir->verifier, verifier, sizeof(readdir->verifier)); @@ -415,7 +414,6 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp static void nfs4_free_slot(struct nfs4_slot_table *tbl, u32 slotid) { - BUG_ON(slotid >= NFS4_MAX_SLOT_TABLE); /* clear used bit in bitmap */ __clear_bit(slotid, tbl->used_slots); @@ -2533,7 +2531,8 @@ static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle, rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS]; len = rpcauth_list_flavors(flav_array, ARRAY_SIZE(flav_array)); - BUG_ON(len < 0); + if (len < 0) + return len; for (i = 0; i < len; i++) { /* AUTH_UNIX is the default flavor if none was specified, @@ -3362,9 +3361,6 @@ static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry, int mode = sattr->ia_mode; int status = -ENOMEM; - BUG_ON(!(sattr->ia_valid & ATTR_MODE)); - BUG_ON(!S_ISFIFO(mode) && !S_ISBLK(mode) && !S_ISCHR(mode) && !S_ISSOCK(mode)); - data = nfs4_alloc_createdata(dir, &dentry->d_name, sattr, NF4SOCK); if (data == NULL) goto out; @@ -3380,10 +3376,13 @@ static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry, data->arg.ftype = NF4CHR; data->arg.u.device.specdata1 = MAJOR(rdev); data->arg.u.device.specdata2 = MINOR(rdev); + } else if (!S_ISSOCK(mode)) { + status = -EINVAL; + goto out_free; } status = nfs4_do_create(dir, dentry, data); - +out_free: nfs4_free_createdata(data); out: return status; @@ -5357,7 +5356,6 @@ int nfs4_proc_bind_conn_to_session(struct nfs_client *clp, struct rpc_cred *cred }; dprintk("--> %s\n", __func__); - BUG_ON(clp == NULL); res.session = kzalloc(sizeof(struct nfs4_session), GFP_NOFS); if (unlikely(res.session == NULL)) { diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index c351e6b..e0a28df 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1086,7 +1086,6 @@ void nfs_free_seqid(struct nfs_seqid *seqid) */ static void nfs_increment_seqid(int status, struct nfs_seqid *seqid) { - BUG_ON(list_first_entry(&seqid->sequence->list, struct nfs_seqid, list) != seqid); switch (status) { case 0: break; -- cgit v0.10.2 From f48407ddd46bd215a7b4e1af3940e759a93640c5 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 15 Oct 2012 16:19:30 -0400 Subject: NFS: Remove BUG_ON()s in the fs/nfs/inode.c Signed-off-by: Trond Myklebust diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 6fa01ae..117183b 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -112,8 +112,8 @@ void nfs_clear_inode(struct inode *inode) /* * The following should never happen... */ - BUG_ON(nfs_have_writebacks(inode)); - BUG_ON(!list_empty(&NFS_I(inode)->open_files)); + WARN_ON_ONCE(nfs_have_writebacks(inode)); + WARN_ON_ONCE(!list_empty(&NFS_I(inode)->open_files)); nfs_zap_acl_cache(inode); nfs_access_zap_cache(inode); nfs_fscache_release_inode_cookie(inode); -- cgit v0.10.2 From 28d79ea33f52cae1ea04808e1ec52b8657b5d804 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 15 Oct 2012 16:25:42 -0400 Subject: NFS: Remove the BUG_ON() in the mount code Signed-off-by: Trond Myklebust diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index 015f71f..91a6faf 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -169,6 +169,9 @@ int nfs_mount(struct nfs_mount_request *info) (info->hostname ? info->hostname : "server"), info->dirpath); + if (strlen(info->dirpath) > MNTPATHLEN) + return -ENAMETOOLONG; + if (info->noresvport) args.flags |= RPC_CLNT_CREATE_NONPRIVPORT; @@ -242,6 +245,9 @@ void nfs_umount(const struct nfs_mount_request *info) struct rpc_clnt *clnt; int status; + if (strlen(info->dirpath) > MNTPATHLEN) + return; + if (info->noresvport) args.flags |= RPC_CLNT_CREATE_NONPRIVPORT; @@ -283,7 +289,6 @@ static void encode_mntdirpath(struct xdr_stream *xdr, const char *pathname) const u32 pathname_len = strlen(pathname); __be32 *p; - BUG_ON(pathname_len > MNTPATHLEN); p = xdr_reserve_space(xdr, 4 + pathname_len); xdr_encode_opaque(p, pathname, pathname_len); } -- cgit v0.10.2 From aad56de378b4c675e964a1ab44cf2e55d44d2865 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 15 Oct 2012 17:14:38 -0400 Subject: lockd: Remove unnecessary BUG_ON()s in the xdr client code - Offset bound checks are done in the NFS client code. - So are filehandle size checks - The cookie length is a constant - The utsname()->nodename is already bounded Signed-off-by: Trond Myklebust diff --git a/fs/lockd/clnt4xdr.c b/fs/lockd/clnt4xdr.c index 13ad153..00ec0b9 100644 --- a/fs/lockd/clnt4xdr.c +++ b/fs/lockd/clnt4xdr.c @@ -64,10 +64,6 @@ static void nlm4_compute_offsets(const struct nlm_lock *lock, { const struct file_lock *fl = &lock->fl; - BUG_ON(fl->fl_start > NLM4_OFFSET_MAX); - BUG_ON(fl->fl_end > NLM4_OFFSET_MAX && - fl->fl_end != OFFSET_MAX); - *l_offset = loff_t_to_s64(fl->fl_start); if (fl->fl_end == OFFSET_MAX) *l_len = 0; @@ -122,7 +118,6 @@ static void encode_netobj(struct xdr_stream *xdr, { __be32 *p; - BUG_ON(length > XDR_MAX_NETOBJ); p = xdr_reserve_space(xdr, 4 + length); xdr_encode_opaque(p, data, length); } @@ -156,7 +151,6 @@ out_overflow: static void encode_cookie(struct xdr_stream *xdr, const struct nlm_cookie *cookie) { - BUG_ON(cookie->len > NLM_MAXCOOKIELEN); encode_netobj(xdr, (u8 *)&cookie->data, cookie->len); } @@ -198,7 +192,6 @@ out_overflow: */ static void encode_fh(struct xdr_stream *xdr, const struct nfs_fh *fh) { - BUG_ON(fh->size > NFS3_FHSIZE); encode_netobj(xdr, (u8 *)&fh->data, fh->size); } @@ -336,7 +329,6 @@ static void encode_caller_name(struct xdr_stream *xdr, const char *name) u32 length = strlen(name); __be32 *p; - BUG_ON(length > NLM_MAXSTRLEN); p = xdr_reserve_space(xdr, 4 + length); xdr_encode_opaque(p, name, length); } diff --git a/fs/lockd/clntxdr.c b/fs/lockd/clntxdr.c index 982d267..9a55797 100644 --- a/fs/lockd/clntxdr.c +++ b/fs/lockd/clntxdr.c @@ -60,10 +60,6 @@ static void nlm_compute_offsets(const struct nlm_lock *lock, { const struct file_lock *fl = &lock->fl; - BUG_ON(fl->fl_start > NLM_OFFSET_MAX); - BUG_ON(fl->fl_end > NLM_OFFSET_MAX && - fl->fl_end != OFFSET_MAX); - *l_offset = loff_t_to_s32(fl->fl_start); if (fl->fl_end == OFFSET_MAX) *l_len = 0; @@ -119,7 +115,6 @@ static void encode_netobj(struct xdr_stream *xdr, { __be32 *p; - BUG_ON(length > XDR_MAX_NETOBJ); p = xdr_reserve_space(xdr, 4 + length); xdr_encode_opaque(p, data, length); } @@ -153,7 +148,6 @@ out_overflow: static void encode_cookie(struct xdr_stream *xdr, const struct nlm_cookie *cookie) { - BUG_ON(cookie->len > NLM_MAXCOOKIELEN); encode_netobj(xdr, (u8 *)&cookie->data, cookie->len); } @@ -195,7 +189,6 @@ out_overflow: */ static void encode_fh(struct xdr_stream *xdr, const struct nfs_fh *fh) { - BUG_ON(fh->size != NFS2_FHSIZE); encode_netobj(xdr, (u8 *)&fh->data, NFS2_FHSIZE); } @@ -330,7 +323,6 @@ static void encode_caller_name(struct xdr_stream *xdr, const char *name) u32 length = strlen(name); __be32 *p; - BUG_ON(length > NLM_MAXSTRLEN); p = xdr_reserve_space(xdr, 4 + length); xdr_encode_opaque(p, name, length); } -- cgit v0.10.2 From 326ce0a6da64df3eb8f13a623304ab8033d38c12 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 15 Oct 2012 17:21:04 -0400 Subject: lockd: Remove trivial BUG_ON()s from the NSM code Signed-off-by: Trond Myklebust diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 3d7e09b..3c2cfc6 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -154,8 +154,6 @@ static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res, .rpc_resp = res, }; - BUG_ON(clnt == NULL); - memset(res, 0, sizeof(*res)); msg.rpc_proc = &clnt->cl_procinfo[proc]; @@ -466,7 +464,6 @@ static void encode_nsm_string(struct xdr_stream *xdr, const char *string) const u32 len = strlen(string); __be32 *p; - BUG_ON(len > SM_MAXSTRLEN); p = xdr_reserve_space(xdr, 4 + len); xdr_encode_opaque(p, string, len); } -- cgit v0.10.2 From a2d30a54df968c01fff4a412ac23f55832f45fe6 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 15 Oct 2012 17:26:20 -0400 Subject: lockd: Remove BUG_ON()s in fs/lockd/host.c - Convert the non-trivial ones into WARN_ON_ONCE(). - Remove the trivial refcounting BUGs Signed-off-by: Trond Myklebust diff --git a/fs/lockd/host.c b/fs/lockd/host.c index f9b22e5..0e17090 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -177,9 +177,6 @@ static void nlm_destroy_host_locked(struct nlm_host *host) dprintk("lockd: destroy host %s\n", host->h_name); - BUG_ON(!list_empty(&host->h_lockowners)); - BUG_ON(atomic_read(&host->h_count)); - hlist_del_init(&host->h_hash); nsm_unmonitor(host); @@ -289,13 +286,12 @@ void nlmclnt_release_host(struct nlm_host *host) dprintk("lockd: release client host %s\n", host->h_name); - BUG_ON(atomic_read(&host->h_count) < 0); - BUG_ON(host->h_server); + WARN_ON_ONCE(host->h_server); if (atomic_dec_and_test(&host->h_count)) { - BUG_ON(!list_empty(&host->h_lockowners)); - BUG_ON(!list_empty(&host->h_granted)); - BUG_ON(!list_empty(&host->h_reclaim)); + WARN_ON_ONCE(!list_empty(&host->h_lockowners)); + WARN_ON_ONCE(!list_empty(&host->h_granted)); + WARN_ON_ONCE(!list_empty(&host->h_reclaim)); mutex_lock(&nlm_host_mutex); nlm_destroy_host_locked(host); @@ -412,8 +408,7 @@ void nlmsvc_release_host(struct nlm_host *host) dprintk("lockd: release server host %s\n", host->h_name); - BUG_ON(atomic_read(&host->h_count) < 0); - BUG_ON(!host->h_server); + WARN_ON_ONCE(!host->h_server); atomic_dec(&host->h_count); } -- cgit v0.10.2 From 262693482cd56f887174ad1c0c2bb4f94ffad0ee Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 15 Oct 2012 17:28:45 -0400 Subject: lockd: Remove BUG_ON()s from fs/lockd/clntproc.c Signed-off-by: Trond Myklebust diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 05d2912..54f9e6c 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -141,7 +141,7 @@ static void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) static void nlmclnt_release_lockargs(struct nlm_rqst *req) { - BUG_ON(req->a_args.lock.fl.fl_ops != NULL); + WARN_ON_ONCE(req->a_args.lock.fl.fl_ops != NULL); } /** @@ -465,7 +465,6 @@ static const struct file_lock_operations nlmclnt_lock_ops = { static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host) { - BUG_ON(fl->fl_ops != NULL); fl->fl_u.nfs_fl.state = 0; fl->fl_u.nfs_fl.owner = nlm_find_lockowner(host, fl->fl_owner); INIT_LIST_HEAD(&fl->fl_u.nfs_fl.list); -- cgit v0.10.2 From 922eeac30d8456b8e4462cfb94ddbb6846790ad4 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:25 -0400 Subject: SUNRPC: remove BUG_ON in __rpc_clnt_handle_event Print a KERN_INFO message before rpc_d_lookup_sb returns NULL, like other error paths in that function. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index dd2532c..245de1a 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -132,8 +132,10 @@ static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb, int error; dir = rpc_d_lookup_sb(sb, dir_name); - if (dir == NULL) + if (dir == NULL) { + pr_info("RPC: pipefs directory doesn't exist: %s\n", dir_name); return dir; + } for (;;) { q.len = snprintf(name, sizeof(name), "clnt%x", (unsigned int)clntid++); name[sizeof(name) - 1] = '\0'; @@ -192,7 +194,8 @@ static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event, case RPC_PIPEFS_MOUNT: dentry = rpc_setup_pipedir_sb(sb, clnt, clnt->cl_program->pipe_dir_name); - BUG_ON(dentry == NULL); + if (!dentry) + return -ENOENT; if (IS_ERR(dentry)) return PTR_ERR(dentry); clnt->cl_dentry = dentry; -- cgit v0.10.2 From 9a6478f6cccbd15af30f02368e68b47390360485 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:27 -0400 Subject: SUNRPC: remove BUG_ON from rpc_run_bc_task Replace BUG_ON() with WARN_ON_ONCE() - rpc_run_bc_task calls rpc_init_task() then increments the tk_count, so this is a simple sanity check that if hit once would hit every time this code path is executed. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 245de1a..32aea0b 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -918,7 +918,7 @@ struct rpc_task *rpc_run_bc_task(struct rpc_rqst *req, task->tk_action = call_bc_transmit; atomic_inc(&task->tk_count); - BUG_ON(atomic_read(&task->tk_count) != 2); + WARN_ON_ONCE(atomic_read(&task->tk_count) != 2); rpc_execute(task); out: -- cgit v0.10.2 From 576e613d2111036a075b7c4f7cafdf29a0021c29 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:28 -0400 Subject: SUNRPC: remove BUG_ON from call_transmit Remove unneeded BUG_ON() Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 32aea0b..f1ab4a8 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1664,7 +1664,6 @@ call_transmit(struct rpc_task *task) task->tk_action = call_transmit_status; /* Encode here so that rpcsec_gss can use correct sequence number. */ if (rpc_task_need_encode(task)) { - BUG_ON(task->tk_rqstp->rq_bytes_sent != 0); rpc_xdr_encode(task); /* Did the encode result in an error condition? */ if (task->tk_status != 0) { -- cgit v0.10.2 From 1facf4c4a486d515759edc0b2ece927942dd8167 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:30 -0400 Subject: SUNRPC: remove BUG_ON from call_bc_transmit Replace BUG_ON() with WARN_ON_ONCE(). Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index f1ab4a8..9131401 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1794,7 +1794,7 @@ call_bc_transmit(struct rpc_task *task) * We were unable to reply and will have to drop the * request. The server should reconnect and retransmit. */ - BUG_ON(task->tk_status == -EAGAIN); + WARN_ON_ONCE(task->tk_status == -EAGAIN); printk(KERN_NOTICE "RPC: Could not send backchannel reply " "error: %d\n", task->tk_status); break; -- cgit v0.10.2 From 8b827e1f1e46c45a4c9c389676f622e569d8a8ea Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:31 -0400 Subject: SUNRPC: remove BUG_ON from call_bc_transmit Remove redundant BUG_ON(). Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 9131401..a9dd1e8 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1747,7 +1747,6 @@ call_bc_transmit(struct rpc_task *task) { struct rpc_rqst *req = task->tk_rqstp; - BUG_ON(task->tk_status != 0); task->tk_status = xprt_prepare_transmit(task); if (task->tk_status == -EAGAIN) { /* -- cgit v0.10.2 From e454a7a83d20cc5ec338ad0e3abae85f10d5a0c4 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:32 -0400 Subject: SUNRPC: remove BUG_ON from rpc_sleep_on* Replace BUG_ON() with WARN_ON_ONCE() and clean up after inactive task. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 6357fcb..f494b35 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -343,7 +343,12 @@ void rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, rpc_action action) { /* We shouldn't ever put an inactive task to sleep */ - BUG_ON(!RPC_IS_ACTIVATED(task)); + WARN_ON_ONCE(!RPC_IS_ACTIVATED(task)); + if (!RPC_IS_ACTIVATED(task)) { + task->tk_status = -EIO; + rpc_put_task_async(task); + return; + } /* * Protect the queue operations. @@ -358,7 +363,12 @@ void rpc_sleep_on_priority(struct rpc_wait_queue *q, struct rpc_task *task, rpc_action action, int priority) { /* We shouldn't ever put an inactive task to sleep */ - BUG_ON(!RPC_IS_ACTIVATED(task)); + WARN_ON_ONCE(!RPC_IS_ACTIVATED(task)); + if (!RPC_IS_ACTIVATED(task)) { + task->tk_status = -EIO; + rpc_put_task_async(task); + return; + } /* * Protect the queue operations. -- cgit v0.10.2 From c4ded8d9771c8e8d5d7a202d58af9c70591dd675 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:34 -0400 Subject: SUNRPC: remove BUG_ON from xprt_destroy_backchannel If max_reqs is 0, do nothing besides the usual dprintks. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c index a9c0bbc..80aa881 100644 --- a/net/sunrpc/backchannel_rqst.c +++ b/net/sunrpc/backchannel_rqst.c @@ -191,7 +191,9 @@ void xprt_destroy_backchannel(struct rpc_xprt *xprt, unsigned int max_reqs) dprintk("RPC: destroy backchannel transport\n"); - BUG_ON(max_reqs == 0); + if (max_reqs == 0) + goto out; + spin_lock_bh(&xprt->bc_pa_lock); xprt_dec_alloc_count(xprt, max_reqs); list_for_each_entry_safe(req, tmp, &xprt->bc_pa_list, rq_bc_pa_list) { @@ -202,6 +204,7 @@ void xprt_destroy_backchannel(struct rpc_xprt *xprt, unsigned int max_reqs) } spin_unlock_bh(&xprt->bc_pa_lock); +out: dprintk("RPC: backchannel list empty= %s\n", list_empty(&xprt->bc_pa_list) ? "true" : "false"); } -- cgit v0.10.2 From 4c9c52e479f493ccfc3f513e870912115b49058f Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:35 -0400 Subject: SUNRPC: remove BUG_ON from bc_send Replace BUG_ON() with WARN_ON_ONCE(). The error condition is a simple ref counting sanity check and the following code will not free anything until final put. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/bc_svc.c b/net/sunrpc/bc_svc.c index 0b2eb38..15c7a8a 100644 --- a/net/sunrpc/bc_svc.c +++ b/net/sunrpc/bc_svc.c @@ -53,7 +53,7 @@ int bc_send(struct rpc_rqst *req) if (IS_ERR(task)) ret = PTR_ERR(task); else { - BUG_ON(atomic_read(&task->tk_count) != 1); + WARN_ON_ONCE(atomic_read(&task->tk_count) != 1); ret = task->tk_status; rpc_put_task(task); } -- cgit v0.10.2 From 0db74d9a2d2b312dd2cff066c5b97b8e626e403a Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:36 -0400 Subject: SUNRPC: remove BUG_ON calls from cache_read Replace BUG_ON() with WARN_ON_ONCE() in two parts of cache_read(). Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index fc2f7aa..9afa439 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -775,11 +775,11 @@ static ssize_t cache_read(struct file *filp, char __user *buf, size_t count, if (rp->q.list.next == &cd->queue) { spin_unlock(&queue_lock); mutex_unlock(&inode->i_mutex); - BUG_ON(rp->offset); + WARN_ON_ONCE(rp->offset); return 0; } rq = container_of(rp->q.list.next, struct cache_request, q.list); - BUG_ON(rq->q.reader); + WARN_ON_ONCE(rq->q.reader); if (rp->offset == 0) rq->readers++; spin_unlock(&queue_lock); -- cgit v0.10.2 From 749386e906e76e3244030c35aeccfcb19c3357b8 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:37 -0400 Subject: SUNRPC: remove BUG_ON in rpc_put_sb_net Replace BUG_ON() with WARN_ON() - the condition is definitely a misuse of the API, but shouldn't cause a crash. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 80f5dd2..3128a15 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -1093,7 +1093,7 @@ void rpc_put_sb_net(const struct net *net) { struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); - BUG_ON(sn->pipefs_sb == NULL); + WARN_ON(sn->pipefs_sb == NULL); mutex_unlock(&sn->pipefs_sb_lock); } EXPORT_SYMBOL_GPL(rpc_put_sb_net); -- cgit v0.10.2 From f30dfbba1682fde22f5a728f23d97a7a59a39fa0 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:33 -0400 Subject: SUNRPC: remove two BUG_ON asserts Replace two BUG_ON() calls checking the RPC_BC_PA_IN_USE flag with WARN_ON_ONCE(). Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c index 80aa881..890a299 100644 --- a/net/sunrpc/backchannel_rqst.c +++ b/net/sunrpc/backchannel_rqst.c @@ -59,7 +59,7 @@ static void xprt_free_allocation(struct rpc_rqst *req) struct xdr_buf *xbufp; dprintk("RPC: free allocations for req= %p\n", req); - BUG_ON(test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state)); + WARN_ON_ONCE(test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state)); xbufp = &req->rq_private_buf; free_page((unsigned long)xbufp->head[0].iov_base); xbufp = &req->rq_snd_buf; @@ -258,7 +258,7 @@ void xprt_free_bc_request(struct rpc_rqst *req) dprintk("RPC: free backchannel req=%p\n", req); smp_mb__before_clear_bit(); - BUG_ON(!test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state)); + WARN_ON_ONCE(!test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state)); clear_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state); smp_mb__after_clear_bit(); -- cgit v0.10.2 From 1bd58aaff44485ec9e3640af350f6ba1b33e2bec Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:38 -0400 Subject: SUNRPC: remove BUG_ON from svc_pool_map_set_cpumask Replace BUG_ON() with a WARN() and early return. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 3ee7461..664979b 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -324,7 +324,9 @@ svc_pool_map_set_cpumask(struct task_struct *task, unsigned int pidx) * The caller checks for sv_nrpools > 1, which * implies that we've been initialized. */ - BUG_ON(m->count == 0); + WARN_ON_ONCE(m->count == 0); + if (m->count == 0) + return; switch (m->mode) { case SVC_POOL_PERCPU: -- cgit v0.10.2 From 1b7a1819078c68c4df4bba90f76b8113a08460de Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:39 -0400 Subject: SUNRPC: remove BUG_ONs from *_reclassify_socket* Replace multiple BUG_ON() calls with WARN_ON_ONCE() and early return when sanity checking socket ownership (lock). The bind call will fail if the socket was unsuccessfully reclassified. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 03827ce..cc3020d 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -84,7 +84,11 @@ static struct lock_class_key svc_slock_key[2]; static void svc_reclassify_socket(struct socket *sock) { struct sock *sk = sock->sk; - BUG_ON(sock_owned_by_user(sk)); + + WARN_ON_ONCE(sock_owned_by_user(sk)); + if (sock_owned_by_user(sk)) + return; + switch (sk->sk_family) { case AF_INET: sock_lock_init_class_and_name(sk, "slock-AF_INET-NFSD", diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 75853ca..46c1a879 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -1746,7 +1746,6 @@ static inline void xs_reclassify_socketu(struct socket *sock) { struct sock *sk = sock->sk; - BUG_ON(sock_owned_by_user(sk)); sock_lock_init_class_and_name(sk, "slock-AF_LOCAL-RPC", &xs_slock_key[1], "sk_lock-AF_LOCAL-RPC", &xs_key[1]); } @@ -1755,7 +1754,6 @@ static inline void xs_reclassify_socket4(struct socket *sock) { struct sock *sk = sock->sk; - BUG_ON(sock_owned_by_user(sk)); sock_lock_init_class_and_name(sk, "slock-AF_INET-RPC", &xs_slock_key[0], "sk_lock-AF_INET-RPC", &xs_key[0]); } @@ -1764,13 +1762,16 @@ static inline void xs_reclassify_socket6(struct socket *sock) { struct sock *sk = sock->sk; - BUG_ON(sock_owned_by_user(sk)); sock_lock_init_class_and_name(sk, "slock-AF_INET6-RPC", &xs_slock_key[1], "sk_lock-AF_INET6-RPC", &xs_key[1]); } static inline void xs_reclassify_socket(int family, struct socket *sock) { + WARN_ON_ONCE(sock_owned_by_user(sock->sk)); + if (sock_owned_by_user(sock->sk)) + return; + switch (family) { case AF_LOCAL: xs_reclassify_socketu(sock); -- cgit v0.10.2 From ff1fdb9b805fc03fb51c7b061604360af92d0c9e Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:40 -0400 Subject: SUNRPC: remove BUG_ON in svc_xprt_received Replace BUG_ON() with a WARN_ON_ONCE() and early return. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 194d865..be89bb6 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -218,7 +218,9 @@ static struct svc_xprt *__svc_xpo_create(struct svc_xprt_class *xcl, */ static void svc_xprt_received(struct svc_xprt *xprt) { - BUG_ON(!test_bit(XPT_BUSY, &xprt->xpt_flags)); + WARN_ON_ONCE(!test_bit(XPT_BUSY, &xprt->xpt_flags)); + if (!test_bit(XPT_BUSY, &xprt->xpt_flags)) + return; /* As soon as we clear busy, the xprt could be closed and * 'put', so we need a reference to call svc_xprt_enqueue with: */ -- cgit v0.10.2 From b25cd058f25ea2054351bbe501956002cd8ed4c5 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:41 -0400 Subject: SUNRPC: remove BUG_ONs checking RPCSVC_MAXPAGES Replace two bounds checking BUG_ON() calls with WARN_ON_ONCE() and resetting the requested size to RPCSVC_MAXPAGES. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 664979b..3bf5a54 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -587,7 +587,9 @@ svc_init_buffer(struct svc_rqst *rqstp, unsigned int size, int node) * We assume one is at most one page */ arghi = 0; - BUG_ON(pages > RPCSVC_MAXPAGES); + WARN_ON_ONCE(pages > RPCSVC_MAXPAGES); + if (pages > RPCSVC_MAXPAGES) + pages = RPCSVC_MAXPAGES; while (pages) { struct page *p = alloc_pages_node(node, GFP_KERNEL, 0); if (!p) diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index be89bb6..35fa74b 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -579,7 +579,10 @@ int svc_alloc_arg(struct svc_rqst *rqstp) /* now allocate needed pages. If we get a failure, sleep briefly */ pages = (serv->sv_max_mesg + PAGE_SIZE) / PAGE_SIZE; - BUG_ON(pages >= RPCSVC_MAXPAGES); + WARN_ON_ONCE(pages >= RPCSVC_MAXPAGES); + if (pages >= RPCSVC_MAXPAGES) + /* use as many pages as possible */ + pages = RPCSVC_MAXPAGES - 1; for (i = 0; i < pages ; i++) while (rqstp->rq_pages[i] == NULL) { struct page *p = alloc_page(GFP_KERNEL); -- cgit v0.10.2 From 18e624ad0374f3b1092530f978301611f88e45b3 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:42 -0400 Subject: SUNRPC: remove BUG_ON in xdr_shrink_bufhead Replace bounds checking BUG_ON() with a WARN_ON_ONCE() and resetting the requested len to the max. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 08f50af..5605563 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -318,7 +318,10 @@ xdr_shrink_bufhead(struct xdr_buf *buf, size_t len) tail = buf->tail; head = buf->head; - BUG_ON (len > head->iov_len); + + WARN_ON_ONCE(len > head->iov_len); + if (len > head->iov_len) + len = head->iov_len; /* Shift the tail first */ if (tail->iov_len != 0) { -- cgit v0.10.2 From b8a13d039cbf7aec3b486fc0ae3996a5449afed2 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:43 -0400 Subject: SUNRPC: remove BUG_ON from bc_malloc Replace BUG_ON() with WARN_ON_ONCE() and NULL return - the caller will handle this like a memory allocation failure. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 46c1a879..6db26e5 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2330,9 +2330,11 @@ static void *bc_malloc(struct rpc_task *task, size_t size) struct page *page; struct rpc_buffer *buf; - BUG_ON(size > PAGE_SIZE - sizeof(struct rpc_buffer)); - page = alloc_page(GFP_KERNEL); + WARN_ON_ONCE(size > PAGE_SIZE - sizeof(struct rpc_buffer)); + if (size > PAGE_SIZE - sizeof(struct rpc_buffer)) + return NULL; + page = alloc_page(GFP_KERNEL); if (!page) return NULL; -- cgit v0.10.2 From 332e008a44d1ab6ca85b2214578f2f89af04a2fc Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:44 -0400 Subject: SUNRPC: remove BUG_ON from encode_rpcb_string Replace BUG_ON() with WARN_ON_ONCE() and truncate the encoded string if len > max. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index a70acae..411f332 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -884,7 +884,10 @@ static void encode_rpcb_string(struct xdr_stream *xdr, const char *string, u32 len; len = strlen(string); - BUG_ON(len > maxstrlen); + WARN_ON_ONCE(len > maxstrlen); + if (len > maxstrlen) + /* truncate and hope for the best */ + len = maxstrlen; p = xdr_reserve_space(xdr, 4 + len); xdr_encode_opaque(p, string, len); } -- cgit v0.10.2 From 0af39507f60ee9f98b20f24af09c1a60655417ac Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:45 -0400 Subject: SUNRPC: remove BUG_ON in svc_register Instead of calling BUG_ON(), do a WARN_ON_ONCE() and return -EINVAL. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 3bf5a54..dfa4ba6 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -950,7 +950,9 @@ int svc_register(const struct svc_serv *serv, struct net *net, unsigned int i; int error = 0; - BUG_ON(proto == 0 && port == 0); + WARN_ON_ONCE(proto == 0 && port == 0); + if (proto == 0 && port == 0) + return -EINVAL; for (progp = serv->sv_program; progp; progp = progp->pg_next) { for (i = 0; i < progp->pg_nvers; i++) { -- cgit v0.10.2 From f50ad42837eb874c1a0cd7cca2001364b06f7ac4 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:46 -0400 Subject: SUNRPC: remove BUG_ON from __rpc_sleep_on_priority Replace BUG_ON() with WARN_ON_ONCE(). Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index f494b35..e6db496 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -334,7 +334,7 @@ static void __rpc_sleep_on_priority(struct rpc_wait_queue *q, __rpc_add_wait_queue(q, task, queue_priority); - BUG_ON(task->tk_callback != NULL); + WARN_ON_ONCE(task->tk_callback != NULL); task->tk_callback = action; __rpc_add_timer(q, task); } -- cgit v0.10.2 From 2bd4eef87bc169f1baf5d1518ba939897cc32471 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:47 -0400 Subject: SUNRPC: remove BUG_ONs checking RPC_IS_QUEUED Replace two BUG_ON() calls with WARN_ON_ONCE() and early returns. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index e6db496..6904917 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -133,7 +133,9 @@ static void __rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task, unsigned char queue_priority) { - BUG_ON (RPC_IS_QUEUED(task)); + WARN_ON_ONCE(RPC_IS_QUEUED(task)); + if (RPC_IS_QUEUED(task)) + return; if (RPC_IS_PRIORITY(queue)) __rpc_add_wait_queue_priority(queue, task, queue_priority); @@ -707,7 +709,9 @@ static void __rpc_execute(struct rpc_task *task) dprintk("RPC: %5u __rpc_execute flags=0x%x\n", task->tk_pid, task->tk_flags); - BUG_ON(RPC_IS_QUEUED(task)); + WARN_ON_ONCE(RPC_IS_QUEUED(task)); + if (RPC_IS_QUEUED(task)) + return; for (;;) { void (*do_action)(struct rpc_task *); -- cgit v0.10.2 From 010472980724ed7bbabf818b4232bf3b182b94c5 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:48 -0400 Subject: SUNRPC: remove BUG_ON in svc_delete_xprt Replace BUG_ON() with WARN_ON_ONCE(). Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 35fa74b..b8e47fa 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -931,7 +931,7 @@ static void svc_delete_xprt(struct svc_xprt *xprt) spin_lock_bh(&serv->sv_lock); if (!test_and_set_bit(XPT_DETACHED, &xprt->xpt_flags)) list_del_init(&xprt->xpt_list); - BUG_ON(!list_empty(&xprt->xpt_ready)); + WARN_ON_ONCE(!list_empty(&xprt->xpt_ready)); if (test_bit(XPT_TEMP, &xprt->xpt_flags)) serv->sv_tmpcnt--; spin_unlock_bh(&serv->sv_lock); -- cgit v0.10.2 From 0a0c2a57bc9a47ae876077fdc4678eca33c26ae4 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 23 Oct 2012 10:43:49 -0400 Subject: SUNRPC: remove BUG_ON in rpc_release_task Replace BUG_ON() with WARN_ON_ONCE(). Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 6904917..8529026 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -995,7 +995,7 @@ static void rpc_release_task(struct rpc_task *task) { dprintk("RPC: %5u release task\n", task->tk_pid); - BUG_ON (RPC_IS_QUEUED(task)); + WARN_ON_ONCE(RPC_IS_QUEUED(task)); rpc_release_resources_task(task); -- cgit v0.10.2 From 50d2bdb19734f9e9f21e63881a9b6c8db4cc0eb7 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Thu, 1 Nov 2012 16:04:40 -0400 Subject: SUNRPC: remove BUG_ON from rpc_call_sync Use WARN_ON_ONCE instead of calling BUG_ON and return -EINVAL when RPC_TASK_ASYNC flag is passed to rpc_call_sync. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index a9dd1e8..50bc9db 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -842,7 +842,12 @@ int rpc_call_sync(struct rpc_clnt *clnt, const struct rpc_message *msg, int flag }; int status; - BUG_ON(flags & RPC_TASK_ASYNC); + WARN_ON_ONCE(flags & RPC_TASK_ASYNC); + if (flags & RPC_TASK_ASYNC) { + rpc_release_calldata(task_setup_data.callback_ops, + task_setup_data.callback_data); + return -EINVAL; + } task = rpc_run_task(&task_setup_data); if (IS_ERR(task)) -- cgit v0.10.2 From f994c43d19a9116727d4c228d3f13db595bff562 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 1 Nov 2012 12:14:14 -0400 Subject: SUNRPC: Clean up rpc_bind_new_program We can and should use the rpc_create_args and __rpc_clone_client() to change the program and version number on the resulting rpc_client. Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 50bc9db..c69e199 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -703,21 +703,19 @@ struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *old, const struct rpc_program *program, u32 vers) { + struct rpc_create_args args = { + .program = program, + .prognumber = program->number, + .version = vers, + .authflavor = old->cl_auth->au_flavor, + .client_name = old->cl_principal, + }; struct rpc_clnt *clnt; - const struct rpc_version *version; int err; - BUG_ON(vers >= program->nrvers || !program->version[vers]); - version = program->version[vers]; - clnt = rpc_clone_client(old); + clnt = __rpc_clone_client(&args, old); if (IS_ERR(clnt)) goto out; - clnt->cl_procinfo = version->procs; - clnt->cl_maxproc = version->nrprocs; - clnt->cl_protname = program->name; - clnt->cl_prog = program->number; - clnt->cl_vers = version->number; - clnt->cl_stats = program->stats; err = rpc_ping(clnt); if (err != 0) { rpc_shutdown_client(clnt); -- cgit v0.10.2 From 642fe4d00db56d65060ce2fd4c105884414acb16 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 8 Nov 2012 10:01:26 -0500 Subject: SUNRPC: Fix validity issues with rpc_pipefs sb->s_fs_info rpc_kill_sb() must defer calling put_net() until after the notifier has been called, since most (all?) of the notifier callbacks assume that sb->s_fs_info points to a valid net namespace. It also must not call put_net() if the call to rpc_fill_super was unsuccessful. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=48421 Signed-off-by: Trond Myklebust Cc: Stanislav Kinsbursky Cc: stable@vger.kernel.org [>= v3.4] diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 80f5dd2..e659def 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -1152,14 +1152,19 @@ static void rpc_kill_sb(struct super_block *sb) struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); mutex_lock(&sn->pipefs_sb_lock); + if (sn->pipefs_sb != sb) { + mutex_unlock(&sn->pipefs_sb_lock); + goto out; + } sn->pipefs_sb = NULL; mutex_unlock(&sn->pipefs_sb_lock); - put_net(net); dprintk("RPC: sending pipefs UMOUNT notification for net %p%s\n", net, NET_NAME(net)); blocking_notifier_call_chain(&rpc_pipefs_notifier_list, RPC_PIPEFS_UMOUNT, sb); + put_net(net); +out: kill_litter_super(sb); } -- cgit v0.10.2 From 6bdb5f213c4344324f600dde885f25768fbd14db Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Mon, 12 Nov 2012 16:55:38 -0500 Subject: NFS: Add sequence_priviliged_ops for nfs4_proc_sequence() If I mount an NFS v4.1 server to a single client multiple times and then run xfstests over each mountpoint I usually get the client into a state where recovery deadlocks. The server informs the client of a cb_path_down sequence error, the client then does a bind_connection_to_session and checks the status of the lease. I found that bind_connection_to_session sets the NFS4_SESSION_DRAINING flag on the client, but this flag is never unset before nfs4_check_lease() reaches nfs4_proc_sequence(). This causes the client to deadlock, halting all NFS activity to the server. nfs4_proc_sequence() is only called by the state manager, so I can change it to run in privileged mode to bypass the NFS4_SESSION_DRAINING check and avoid the deadlock. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust Cc: stable@vger.kernel.org diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 6300cdd..a32d953 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6136,13 +6136,26 @@ static void nfs41_sequence_prepare(struct rpc_task *task, void *data) rpc_call_start(task); } +static void nfs41_sequence_prepare_privileged(struct rpc_task *task, void *data) +{ + rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); + nfs41_sequence_prepare(task, data); +} + static const struct rpc_call_ops nfs41_sequence_ops = { .rpc_call_done = nfs41_sequence_call_done, .rpc_call_prepare = nfs41_sequence_prepare, .rpc_release = nfs41_sequence_release, }; -static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred) +static const struct rpc_call_ops nfs41_sequence_privileged_ops = { + .rpc_call_done = nfs41_sequence_call_done, + .rpc_call_prepare = nfs41_sequence_prepare_privileged, + .rpc_release = nfs41_sequence_release, +}; + +static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred, + const struct rpc_call_ops *seq_ops) { struct nfs4_sequence_data *calldata; struct rpc_message msg = { @@ -6152,7 +6165,7 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_ struct rpc_task_setup task_setup_data = { .rpc_client = clp->cl_rpcclient, .rpc_message = &msg, - .callback_ops = &nfs41_sequence_ops, + .callback_ops = seq_ops, .flags = RPC_TASK_ASYNC | RPC_TASK_SOFT, }; @@ -6179,7 +6192,7 @@ static int nfs41_proc_async_sequence(struct nfs_client *clp, struct rpc_cred *cr if ((renew_flags & NFS4_RENEW_TIMEOUT) == 0) return 0; - task = _nfs41_proc_sequence(clp, cred); + task = _nfs41_proc_sequence(clp, cred, &nfs41_sequence_ops); if (IS_ERR(task)) ret = PTR_ERR(task); else @@ -6193,7 +6206,7 @@ static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred) struct rpc_task *task; int ret; - task = _nfs41_proc_sequence(clp, cred); + task = _nfs41_proc_sequence(clp, cred, &nfs41_sequence_privileged_ops); if (IS_ERR(task)) { ret = PTR_ERR(task); goto out; -- cgit v0.10.2 From 5df904aeb0d9baad90e78fc730dfe1afa4996005 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 21 Nov 2012 09:22:14 -0500 Subject: NFSv4.1: Handle session reset and bind_conn_to_session before lease check We can't send a SEQUENCE op unless the session is OK, so it is pointless to handle the CHECK_LEASE state before we've dealt with SESSION_RESET and BIND_CONN_TO_SESSION. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index e0a28df..f3d1bc4 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -2114,15 +2114,6 @@ static void nfs4_state_manager(struct nfs_client *clp) continue; } - if (test_and_clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state)) { - section = "check lease"; - status = nfs4_check_lease(clp); - if (status < 0) - goto out_error; - if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) - continue; - } - /* Initialize or reset the session */ if (test_and_clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)) { section = "reset session"; @@ -2143,6 +2134,14 @@ static void nfs4_state_manager(struct nfs_client *clp) continue; } + if (test_and_clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state)) { + section = "check lease"; + status = nfs4_check_lease(clp); + if (status < 0) + goto out_error; + continue; + } + /* Recall session slots */ if (test_and_clear_bit(NFS4CLNT_RECALL_SLOT, &clp->cl_state)) { section = "recall slot"; -- cgit v0.10.2 From ae72ae676045274c82f3c25159a9dd7cfcf5ffae Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 20 Nov 2012 11:02:55 -0500 Subject: NFSv4.1: Don't confuse CREATE_SESSION arguments and results Don't store the target request and response sizes in the same variables used to store the server's replies to those targets. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a32d953..3e572dc 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5807,8 +5807,8 @@ void nfs4_destroy_session(struct nfs4_session *session) static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args) { struct nfs4_session *session = args->client->cl_session; - unsigned int mxrqst_sz = session->fc_attrs.max_rqst_sz, - mxresp_sz = session->fc_attrs.max_resp_sz; + unsigned int mxrqst_sz = session->fc_target_max_rqst_sz, + mxresp_sz = session->fc_target_max_resp_sz; if (mxrqst_sz == 0) mxrqst_sz = NFS_MAX_FILE_IO_SIZE; @@ -6015,24 +6015,28 @@ int nfs4_init_session(struct nfs_server *server) { struct nfs_client *clp = server->nfs_client; struct nfs4_session *session; - unsigned int rsize, wsize; + unsigned int target_max_rqst_sz = NFS_MAX_FILE_IO_SIZE; + unsigned int target_max_resp_sz = NFS_MAX_FILE_IO_SIZE; if (!nfs4_has_session(clp)) return 0; + if (server->rsize != 0) + target_max_resp_sz = server->rsize; + target_max_resp_sz += nfs41_maxread_overhead; + + if (server->wsize != 0) + target_max_rqst_sz = server->wsize; + target_max_rqst_sz += nfs41_maxwrite_overhead; + session = clp->cl_session; spin_lock(&clp->cl_lock); if (test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) { - - rsize = server->rsize; - if (rsize == 0) - rsize = NFS_MAX_FILE_IO_SIZE; - wsize = server->wsize; - if (wsize == 0) - wsize = NFS_MAX_FILE_IO_SIZE; - - session->fc_attrs.max_rqst_sz = wsize + nfs41_maxwrite_overhead; - session->fc_attrs.max_resp_sz = rsize + nfs41_maxread_overhead; + /* Initialise targets and channel attributes */ + session->fc_target_max_rqst_sz = target_max_rqst_sz; + session->fc_attrs.max_rqst_sz = target_max_rqst_sz; + session->fc_target_max_resp_sz = target_max_resp_sz; + session->fc_attrs.max_resp_sz = target_max_resp_sz; } spin_unlock(&clp->cl_lock); diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index a9e76ee..97c8f91 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -242,6 +242,9 @@ struct nfs4_session { struct nfs4_channel_attrs bc_attrs; struct nfs4_slot_table bc_slot_table; struct nfs_client *clp; + /* Create session arguments */ + unsigned int fc_target_max_rqst_sz; + unsigned int fc_target_max_resp_sz; }; #endif /* CONFIG_NFS_V4 */ -- cgit v0.10.2 From 688a9024e2bc8d07cdc62e287dfb048722cf96df Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 20 Nov 2012 10:53:39 -0500 Subject: NFSv4.1: Adjust CREATE_SESSION arguments when mounting a new filesystem If we're mounting a new filesystem, ensure that the session has negotiated large enough request and reply sizes to match the wsize and rsize mount arguments. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 3e572dc..ee82cdd 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6037,9 +6037,22 @@ int nfs4_init_session(struct nfs_server *server) session->fc_attrs.max_rqst_sz = target_max_rqst_sz; session->fc_target_max_resp_sz = target_max_resp_sz; session->fc_attrs.max_resp_sz = target_max_resp_sz; + } else { + /* Just adjust the targets */ + if (target_max_rqst_sz > session->fc_target_max_rqst_sz) { + session->fc_target_max_rqst_sz = target_max_rqst_sz; + set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); + } + if (target_max_resp_sz > session->fc_target_max_resp_sz) { + session->fc_target_max_resp_sz = target_max_resp_sz; + set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); + } } spin_unlock(&clp->cl_lock); + if (test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)) + nfs4_schedule_lease_recovery(clp); + return nfs41_check_session_ready(clp); } -- cgit v0.10.2 From 43095d397219aa1898db23937b03c1215ef16a37 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 20 Nov 2012 11:13:12 -0500 Subject: NFSv4.1: We must bump the clientid sequence number after CREATE_SESSION We must always bump the clientid sequence number after a successful call to CREATE_SESSION on the server. The result of nfs4_verify_channel_attrs() is irrelevant to that requirement. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index ee82cdd..1ac339b 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5917,10 +5917,9 @@ static int _nfs4_proc_create_session(struct nfs_client *clp, status = rpc_call_sync(session->clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); - if (!status) + if (!status) { /* Verify the session's negotiated channel_attrs values */ status = nfs4_verify_channel_attrs(&args, session); - if (!status) { /* Increment the clientid slot sequence id */ clp->cl_seqid++; } -- cgit v0.10.2 From 2d473d378eb571ad77f9563653639aa35e22d39c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 19 Nov 2012 18:03:22 -0500 Subject: NFSv4.1: nfs4_alloc_slots doesn't need zeroing All that memory is going to be initialised to non-zero by nfs4_add_and_init_slots anyway. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 1ac339b..0402ebb 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5658,7 +5658,7 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo) static struct nfs4_slot *nfs4_alloc_slots(u32 max_slots, gfp_t gfp_flags) { - return kcalloc(max_slots, sizeof(struct nfs4_slot), gfp_flags); + return kmalloc_array(max_slots, sizeof(struct nfs4_slot), gfp_flags); } static void nfs4_add_and_init_slots(struct nfs4_slot_table *tbl, -- cgit v0.10.2 From 9216106a847a53e6d0fe6d11dfd9175f2ca7fccf Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 19 Nov 2012 19:50:45 -0500 Subject: NFSv4.1: clean up nfs4_recall_slot to use nfs4_alloc_slots Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index a525fde..36880b9 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -258,6 +258,8 @@ extern int nfs4_proc_get_lease_time(struct nfs_client *clp, extern int nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, bool sync); +extern struct nfs4_slot *nfs4_alloc_slots(u32 max_slots, gfp_t gfp_flags); + static inline bool is_ds_only_client(struct nfs_client *clp) { diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 0402ebb..5e5cc5a 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5656,7 +5656,7 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo) return status; } -static struct nfs4_slot *nfs4_alloc_slots(u32 max_slots, gfp_t gfp_flags) +struct nfs4_slot *nfs4_alloc_slots(u32 max_slots, gfp_t gfp_flags) { return kmalloc_array(max_slots, sizeof(struct nfs4_slot), gfp_flags); } diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index f3d1bc4..96fcbb9 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -2033,8 +2033,7 @@ static int nfs4_recall_slot(struct nfs_client *clp) return 0; nfs4_begin_drain_session(clp); fc_tbl = &clp->cl_session->fc_slot_table; - new = kmalloc(fc_tbl->target_max_slots * sizeof(struct nfs4_slot), - GFP_NOFS); + new = nfs4_alloc_slots(fc_tbl->target_max_slots, GFP_NOFS); if (!new) return -ENOMEM; -- cgit v0.10.2 From 933602e368c4452260c9bff4fbb3baba35cf987a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 16 Nov 2012 12:12:38 -0500 Subject: NFSv4.1: Shrink struct nfs4_sequence_res by moving sr_renewal_time Store the renewal time inside the session slot instead. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 5e5cc5a..14b3974 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -486,6 +486,7 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) { + struct nfs4_slot *slot; unsigned long timestamp; struct nfs_client *clp; @@ -502,12 +503,14 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * if (!RPC_WAS_SENT(task)) goto out; + slot = res->sr_slot; + /* Check the SEQUENCE operation status */ switch (res->sr_status) { case 0: /* Update the slot's sequence and clientid lease timer */ - ++res->sr_slot->seq_nr; - timestamp = res->sr_renewal_time; + ++slot->seq_nr; + timestamp = slot->renewal_time; clp = res->sr_session->clp; do_renew_lease(clp, timestamp); /* Check sequence flags */ @@ -521,12 +524,12 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * */ dprintk("%s: slot=%td seq=%d: Operation in progress\n", __func__, - res->sr_slot - res->sr_session->fc_slot_table.slots, - res->sr_slot->seq_nr); + slot - res->sr_session->fc_slot_table.slots, + slot->seq_nr); goto out_retry; default: /* Just update the slot sequence no. */ - ++res->sr_slot->seq_nr; + ++slot->seq_nr; } out: /* The session may be reset by one of the error handlers. */ @@ -637,6 +640,7 @@ int nfs41_setup_sequence(struct nfs4_session *session, rpc_task_set_priority(task, RPC_PRIORITY_NORMAL); slot = tbl->slots + slotid; + slot->renewal_time = jiffies; args->sa_session = session; args->sa_slotid = slotid; @@ -644,7 +648,6 @@ int nfs41_setup_sequence(struct nfs4_session *session, res->sr_session = session; res->sr_slot = slot; - res->sr_renewal_time = jiffies; res->sr_status_flags = 0; /* * sr_status is only set in decode_sequence, and so will remain diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index a73ea89..9cb1c63 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -187,6 +187,7 @@ struct nfs4_channel_attrs { /* nfs41 sessions slot seqid */ struct nfs4_slot { + unsigned long renewal_time; u32 seq_nr; }; @@ -200,7 +201,6 @@ struct nfs4_sequence_res { struct nfs4_session *sr_session; struct nfs4_slot *sr_slot; /* slot used to send request */ int sr_status; /* sequence operation status */ - unsigned long sr_renewal_time; u32 sr_status_flags; }; -- cgit v0.10.2 From fe20d7d5eefb218b82033ba5c13cbcbd2a3d874c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 21 Nov 2012 22:49:36 -0500 Subject: NFSv4: Fix a compile time warning when #undef CONFIG_NFS_V4_1 The function nfs4_get_machine_cred_locked is used by NFSv4.0 routines too. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index a525fde..ea4e362 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -321,13 +321,13 @@ extern void nfs4_renew_state(struct work_struct *); /* nfs4state.c */ struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp); +struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp); struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp); int nfs4_discover_server_trunking(struct nfs_client *clp, struct nfs_client **); int nfs40_discover_server_trunking(struct nfs_client *clp, struct nfs_client **, struct rpc_cred *); #if defined(CONFIG_NFS_V4_1) -struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp); struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp); int nfs41_discover_server_trunking(struct nfs_client *clp, struct nfs_client **, struct rpc_cred *); -- cgit v0.10.2 From d751f748b359534d78e2b2e52b59d39f0e0540aa Mon Sep 17 00:00:00 2001 From: Jim Rees Date: Fri, 16 Nov 2012 18:12:06 -0500 Subject: NFS: Reduce stack use in encode_exchange_id() encode_exchange_id() uses more stack space than necessary, giving a compile time warning. Reduce the size of the static buffer for implementation name. Signed-off-by: Jim Rees Reviewed-by: "Adamson, Dros" Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 40836ee..142aacb 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -270,6 +270,8 @@ static int nfs4_stat_to_errno(int); #if defined(CONFIG_NFS_V4_1) #define NFS4_MAX_MACHINE_NAME_LEN (64) +#define IMPL_NAME_LIMIT (sizeof(utsname()->sysname) + sizeof(utsname()->release) + \ + sizeof(utsname()->version) + sizeof(utsname()->machine) + 8) #define encode_exchange_id_maxsz (op_encode_hdr_maxsz + \ encode_verifier_maxsz + \ @@ -282,7 +284,7 @@ static int nfs4_stat_to_errno(int); 1 /* nii_domain */ + \ XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \ 1 /* nii_name */ + \ - XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \ + XDR_QUADLEN(IMPL_NAME_LIMIT) + \ 3 /* nii_date */) #define decode_exchange_id_maxsz (op_decode_hdr_maxsz + \ 2 /* eir_clientid */ + \ @@ -1713,7 +1715,7 @@ static void encode_exchange_id(struct xdr_stream *xdr, struct compound_hdr *hdr) { __be32 *p; - char impl_name[NFS4_OPAQUE_LIMIT]; + char impl_name[IMPL_NAME_LIMIT]; int len = 0; encode_op_hdr(xdr, OP_EXCHANGE_ID, decode_exchange_id_maxsz, hdr); @@ -1728,7 +1730,7 @@ static void encode_exchange_id(struct xdr_stream *xdr, if (send_implementation_id && sizeof(CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN) > 1 && sizeof(CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN) - <= NFS4_OPAQUE_LIMIT + 1) + <= sizeof(impl_name) + 1) len = snprintf(impl_name, sizeof(impl_name), "%s %s %s %s", utsname()->sysname, utsname()->release, utsname()->version, utsname()->machine); -- cgit v0.10.2 From 4c1002100898d03c5c9142ffaf58351c841ab94a Mon Sep 17 00:00:00 2001 From: Yanchuan Nian Date: Mon, 12 Nov 2012 09:27:37 +0800 Subject: nfs: Fix wrong slab cache in nfs_commit_mempool The slab cache in nfs_commit_mempool is wrong, and I think it is just a slip. I tested it on a x86-32 machine, the size of nfs_write_header is 544, and the size of nfs_commit_data is 408, so it works fine. It is also true that sizeof(struct nfs_write_header) > sizeof(struct nfs_commit_data) on other platforms in my opinoin. Just fix it. Signed-off-by: Yanchuan Nian Signed-off-by: Trond Myklebust diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 9347ab7..f710e39 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1829,7 +1829,7 @@ int __init nfs_init_writepagecache(void) goto out_destroy_write_mempool; nfs_commit_mempool = mempool_create_slab_pool(MIN_POOL_COMMIT, - nfs_wdata_cachep); + nfs_cdata_cachep); if (nfs_commit_mempool == NULL) goto out_destroy_commit_cache; -- cgit v0.10.2 From e3725ec015dfbbeb896295cf2b3a995f28b0630e Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 16 Nov 2012 12:25:01 -0500 Subject: NFSv4.1: Shrink struct nfs4_sequence_res by moving the session pointer Move the session pointer into the slot table, then have struct nfs4_slot point to that slot table. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 36880b9..42c5869 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -258,7 +258,8 @@ extern int nfs4_proc_get_lease_time(struct nfs_client *clp, extern int nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, bool sync); -extern struct nfs4_slot *nfs4_alloc_slots(u32 max_slots, gfp_t gfp_flags); +extern struct nfs4_slot *nfs4_alloc_slots(struct nfs4_slot_table *table, + u32 max_slots, gfp_t gfp_flags); static inline bool is_ds_only_client(struct nfs_client *clp) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 14b3974..5b61c4a 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -467,25 +467,28 @@ void nfs4_check_drain_bc_complete(struct nfs4_session *ses) static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) { + struct nfs4_session *session; struct nfs4_slot_table *tbl; - tbl = &res->sr_session->fc_slot_table; if (!res->sr_slot) { /* just wake up the next guy waiting since * we may have not consumed a slot after all */ dprintk("%s: No slot\n", __func__); return; } + tbl = res->sr_slot->table; + session = tbl->session; spin_lock(&tbl->slot_tbl_lock); nfs4_free_slot(tbl, res->sr_slot - tbl->slots); - nfs4_check_drain_fc_complete(res->sr_session); + nfs4_check_drain_fc_complete(session); spin_unlock(&tbl->slot_tbl_lock); res->sr_slot = NULL; } static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) { + struct nfs4_session *session; struct nfs4_slot *slot; unsigned long timestamp; struct nfs_client *clp; @@ -504,6 +507,7 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * goto out; slot = res->sr_slot; + session = slot->table->session; /* Check the SEQUENCE operation status */ switch (res->sr_status) { @@ -511,7 +515,7 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * /* Update the slot's sequence and clientid lease timer */ ++slot->seq_nr; timestamp = slot->renewal_time; - clp = res->sr_session->clp; + clp = session->clp; do_renew_lease(clp, timestamp); /* Check sequence flags */ if (res->sr_status_flags != 0) @@ -524,7 +528,7 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * */ dprintk("%s: slot=%td seq=%d: Operation in progress\n", __func__, - slot - res->sr_session->fc_slot_table.slots, + slot - session->fc_slot_table.slots, slot->seq_nr); goto out_retry; default: @@ -546,7 +550,7 @@ out_retry: static int nfs4_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) { - if (res->sr_session == NULL) + if (res->sr_slot == NULL) return 1; return nfs41_sequence_done(task, res); } @@ -591,7 +595,6 @@ static void nfs41_init_sequence(struct nfs4_sequence_args *args, args->sa_cache_this = 0; if (cache_reply) args->sa_cache_this = 1; - res->sr_session = NULL; res->sr_slot = NULL; } @@ -646,7 +649,6 @@ int nfs41_setup_sequence(struct nfs4_session *session, dprintk("<-- %s slotid=%d seqid=%d\n", __func__, slotid, slot->seq_nr); - res->sr_session = session; res->sr_slot = slot; res->sr_status_flags = 0; /* @@ -5659,9 +5661,18 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo) return status; } -struct nfs4_slot *nfs4_alloc_slots(u32 max_slots, gfp_t gfp_flags) +struct nfs4_slot *nfs4_alloc_slots(struct nfs4_slot_table *table, + u32 max_slots, gfp_t gfp_flags) { - return kmalloc_array(max_slots, sizeof(struct nfs4_slot), gfp_flags); + struct nfs4_slot *tbl; + u32 i; + + tbl = kmalloc_array(max_slots, sizeof(*tbl), gfp_flags); + if (tbl != NULL) { + for (i = 0; i < max_slots; i++) + tbl[i].table = table; + } + return tbl; } static void nfs4_add_and_init_slots(struct nfs4_slot_table *tbl, @@ -5699,7 +5710,7 @@ static int nfs4_realloc_slot_table(struct nfs4_slot_table *tbl, u32 max_reqs, /* Does the newly negotiated max_reqs match the existing slot table? */ if (max_reqs != tbl->max_slots) { - new = nfs4_alloc_slots(max_reqs, GFP_NOFS); + new = nfs4_alloc_slots(tbl, max_reqs, GFP_NOFS); if (!new) goto out; } @@ -5738,11 +5749,13 @@ static int nfs4_setup_session_slot_tables(struct nfs4_session *ses) dprintk("--> %s\n", __func__); /* Fore channel */ tbl = &ses->fc_slot_table; + tbl->session = ses; status = nfs4_realloc_slot_table(tbl, ses->fc_attrs.max_reqs, 1); if (status) /* -ENOMEM */ return status; /* Back channel */ tbl = &ses->bc_slot_table; + tbl->session = ses; status = nfs4_realloc_slot_table(tbl, ses->bc_attrs.max_reqs, 0); if (status && tbl->slots == NULL) /* Fore and back channel share a connection so get diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 96fcbb9..9495789 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -2033,7 +2033,7 @@ static int nfs4_recall_slot(struct nfs_client *clp) return 0; nfs4_begin_drain_session(clp); fc_tbl = &clp->cl_session->fc_slot_table; - new = nfs4_alloc_slots(fc_tbl->target_max_slots, GFP_NOFS); + new = nfs4_alloc_slots(fc_tbl, fc_tbl->target_max_slots, GFP_NOFS); if (!new) return -ENOMEM; diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 672d9b0..4126f05 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -5507,12 +5507,13 @@ static int decode_sequence(struct xdr_stream *xdr, struct rpc_rqst *rqstp) { #if defined(CONFIG_NFS_V4_1) + struct nfs4_session *session; struct nfs4_sessionid id; u32 dummy; int status; __be32 *p; - if (!res->sr_session) + if (res->sr_slot == NULL) return 0; status = decode_op_hdr(xdr, OP_SEQUENCE); @@ -5526,8 +5527,9 @@ static int decode_sequence(struct xdr_stream *xdr, * sequence number, the server is looney tunes. */ status = -EREMOTEIO; + session = res->sr_slot->table->session; - if (memcmp(id.data, res->sr_session->sess_id.data, + if (memcmp(id.data, session->sess_id.data, NFS4_MAX_SESSIONID_LEN)) { dprintk("%s Invalid session id\n", __func__); goto out_err; @@ -5545,7 +5547,7 @@ static int decode_sequence(struct xdr_stream *xdr, } /* slot id */ dummy = be32_to_cpup(p++); - if (dummy != res->sr_slot - res->sr_session->fc_slot_table.slots) { + if (dummy != res->sr_slot - session->fc_slot_table.slots) { dprintk("%s Invalid slot id\n", __func__); goto out_err; } diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 97c8f91..b041287 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -209,6 +209,7 @@ struct nfs_server { /* Sessions */ #define SLOT_TABLE_SZ DIV_ROUND_UP(NFS4_MAX_SLOT_TABLE, 8*sizeof(long)) struct nfs4_slot_table { + struct nfs4_session *session; /* Parent session */ struct nfs4_slot *slots; /* seqid per slot */ unsigned long used_slots[SLOT_TABLE_SZ]; /* used/unused bitmap */ spinlock_t slot_tbl_lock; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 9cb1c63..0fd88ab 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -187,6 +187,7 @@ struct nfs4_channel_attrs { /* nfs41 sessions slot seqid */ struct nfs4_slot { + struct nfs4_slot_table *table; unsigned long renewal_time; u32 seq_nr; }; @@ -198,7 +199,6 @@ struct nfs4_sequence_args { }; struct nfs4_sequence_res { - struct nfs4_session *sr_session; struct nfs4_slot *sr_slot; /* slot used to send request */ int sr_status; /* sequence operation status */ u32 sr_status_flags; -- cgit v0.10.2 From df2fabffbace8988f3265585ec793ff9deccdea7 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 16 Nov 2012 12:45:06 -0500 Subject: NFSv4.1: Label each entry in the session slot tables with its slot number Instead of doing slot table pointer gymnastics every time we want to know which slot we're using. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 5b61c4a..4311dba 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -526,9 +526,9 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * * returned NFS4ERR_DELAY as per Section 2.10.6.2 * of RFC5661. */ - dprintk("%s: slot=%td seq=%d: Operation in progress\n", + dprintk("%s: slot=%u seq=%u: Operation in progress\n", __func__, - slot - session->fc_slot_table.slots, + slot->slot_nr, slot->seq_nr); goto out_retry; default: @@ -671,9 +671,9 @@ int nfs4_setup_sequence(const struct nfs_server *server, if (session == NULL) goto out; - dprintk("--> %s clp %p session %p sr_slot %td\n", + dprintk("--> %s clp %p session %p sr_slot %d\n", __func__, session->clp, session, res->sr_slot ? - res->sr_slot - session->fc_slot_table.slots : -1); + res->sr_slot->slot_nr : -1); ret = nfs41_setup_sequence(session, args, res, task); out: @@ -5669,8 +5669,10 @@ struct nfs4_slot *nfs4_alloc_slots(struct nfs4_slot_table *table, tbl = kmalloc_array(max_slots, sizeof(*tbl), gfp_flags); if (tbl != NULL) { - for (i = 0; i < max_slots; i++) + for (i = 0; i < max_slots; i++) { tbl[i].table = table; + tbl[i].slot_nr = i; + } } return tbl; } diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 4126f05..50bac70 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -5547,7 +5547,7 @@ static int decode_sequence(struct xdr_stream *xdr, } /* slot id */ dummy = be32_to_cpup(p++); - if (dummy != res->sr_slot - session->fc_slot_table.slots) { + if (dummy != res->sr_slot->slot_nr) { dprintk("%s Invalid slot id\n", __func__); goto out_err; } diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 0fd88ab..9c9b76c 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -189,6 +189,7 @@ struct nfs4_channel_attrs { struct nfs4_slot { struct nfs4_slot_table *table; unsigned long renewal_time; + u32 slot_nr; u32 seq_nr; }; -- cgit v0.10.2 From 2b2fa71723f955d5b4a0f4edd99cf3cd69ceafd1 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 16 Nov 2012 12:58:36 -0500 Subject: NFSv4.1: Simplify struct nfs4_sequence_args too Replace the session pointer + slotid with a pointer to the allocated slot. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 4311dba..6c41a34 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -591,7 +591,7 @@ out: static void nfs41_init_sequence(struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, int cache_reply) { - args->sa_session = NULL; + args->sa_slot = NULL; args->sa_cache_this = 0; if (cache_reply) args->sa_cache_this = 1; @@ -644,8 +644,8 @@ int nfs41_setup_sequence(struct nfs4_session *session, rpc_task_set_priority(task, RPC_PRIORITY_NORMAL); slot = tbl->slots + slotid; slot->renewal_time = jiffies; - args->sa_session = session; - args->sa_slotid = slotid; + + args->sa_slot = slot; dprintk("<-- %s slotid=%d seqid=%d\n", __func__, slotid, slot->seq_nr); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 50bac70..27b0fec 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -1833,18 +1833,16 @@ static void encode_sequence(struct xdr_stream *xdr, struct compound_hdr *hdr) { #if defined(CONFIG_NFS_V4_1) - struct nfs4_session *session = args->sa_session; + struct nfs4_session *session; struct nfs4_slot_table *tp; - struct nfs4_slot *slot; + struct nfs4_slot *slot = args->sa_slot; __be32 *p; - if (!session) + if (slot == NULL) return; - tp = &session->fc_slot_table; - - WARN_ON(args->sa_slotid == NFS4_MAX_SLOT_TABLE); - slot = tp->slots + args->sa_slotid; + tp = slot->table; + session = tp->session; encode_op_hdr(xdr, OP_SEQUENCE, decode_sequence_maxsz, hdr); @@ -1858,12 +1856,12 @@ static void encode_sequence(struct xdr_stream *xdr, ((u32 *)session->sess_id.data)[1], ((u32 *)session->sess_id.data)[2], ((u32 *)session->sess_id.data)[3], - slot->seq_nr, args->sa_slotid, + slot->seq_nr, slot->slot_nr, tp->highest_used_slotid, args->sa_cache_this); p = reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 16); p = xdr_encode_opaque_fixed(p, session->sess_id.data, NFS4_MAX_SESSIONID_LEN); *p++ = cpu_to_be32(slot->seq_nr); - *p++ = cpu_to_be32(args->sa_slotid); + *p++ = cpu_to_be32(slot->slot_nr); *p++ = cpu_to_be32(tp->highest_used_slotid); *p = cpu_to_be32(args->sa_cache_this); #endif /* CONFIG_NFS_V4_1 */ @@ -2025,8 +2023,9 @@ static void encode_free_stateid(struct xdr_stream *xdr, static u32 nfs4_xdr_minorversion(const struct nfs4_sequence_args *args) { #if defined(CONFIG_NFS_V4_1) - if (args->sa_session) - return args->sa_session->clp->cl_mvops->minor_version; + + if (args->sa_slot) + return args->sa_slot->table->session->clp->cl_mvops->minor_version; #endif /* CONFIG_NFS_V4_1 */ return 0; } diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 9c9b76c..deb31bb 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -194,8 +194,7 @@ struct nfs4_slot { }; struct nfs4_sequence_args { - struct nfs4_session *sa_session; - u32 sa_slotid; + struct nfs4_slot *sa_slot; u8 sa_cache_this; }; -- cgit v0.10.2 From 2dc03b7f00d7fcd7dbb9302c5ebbd0c2b7fa3557 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 16 Nov 2012 16:10:11 -0500 Subject: NFSv4.1: Simplify slot allocation Clean up the NFSv4.1 slot allocation by replacing nfs_find_slot() with a function nfs_alloc_slot() that returns a pointer to the nfs4_slot instead of an offset into the slot table. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 6c41a34..0789ef1 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -556,20 +556,18 @@ static int nfs4_sequence_done(struct rpc_task *task, } /* - * nfs4_find_slot - efficiently look for a free slot + * nfs4_alloc_slot - efficiently look for a free slot * - * nfs4_find_slot looks for an unset bit in the used_slots bitmap. + * nfs4_alloc_slot looks for an unset bit in the used_slots bitmap. * If found, we mark the slot as used, update the highest_used_slotid, * and respectively set up the sequence operation args. - * The slot number is returned if found, or NFS4_NO_SLOT otherwise. * * Note: must be called with under the slot_tbl_lock. */ -static u32 -nfs4_find_slot(struct nfs4_slot_table *tbl) +static struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl) { + struct nfs4_slot *ret = NULL; u32 slotid; - u32 ret_id = NFS4_NO_SLOT; dprintk("--> %s used_slots=%04lx highest_used=%u max_slots=%u\n", __func__, tbl->used_slots[0], tbl->highest_used_slotid, @@ -581,11 +579,14 @@ nfs4_find_slot(struct nfs4_slot_table *tbl) if (slotid > tbl->highest_used_slotid || tbl->highest_used_slotid == NFS4_NO_SLOT) tbl->highest_used_slotid = slotid; - ret_id = slotid; + ret = &tbl->slots[slotid]; + ret->renewal_time = jiffies; + out: dprintk("<-- %s used_slots=%04lx highest_used=%d slotid=%d \n", - __func__, tbl->used_slots[0], tbl->highest_used_slotid, ret_id); - return ret_id; + __func__, tbl->used_slots[0], tbl->highest_used_slotid, + ret ? ret->slot_nr : -1); + return ret; } static void nfs41_init_sequence(struct nfs4_sequence_args *args, @@ -605,7 +606,6 @@ int nfs41_setup_sequence(struct nfs4_session *session, { struct nfs4_slot *slot; struct nfs4_slot_table *tbl; - u32 slotid; dprintk("--> %s\n", __func__); /* slot already allocated? */ @@ -632,8 +632,8 @@ int nfs41_setup_sequence(struct nfs4_session *session, return -EAGAIN; } - slotid = nfs4_find_slot(tbl); - if (slotid == NFS4_NO_SLOT) { + slot = nfs4_alloc_slot(tbl); + if (slot == NULL) { rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); spin_unlock(&tbl->slot_tbl_lock); dprintk("<-- %s: no free slots\n", __func__); @@ -642,12 +642,11 @@ int nfs41_setup_sequence(struct nfs4_session *session, spin_unlock(&tbl->slot_tbl_lock); rpc_task_set_priority(task, RPC_PRIORITY_NORMAL); - slot = tbl->slots + slotid; - slot->renewal_time = jiffies; args->sa_slot = slot; - dprintk("<-- %s slotid=%d seqid=%d\n", __func__, slotid, slot->seq_nr); + dprintk("<-- %s slotid=%d seqid=%d\n", __func__, + slot->slot_nr, slot->seq_nr); res->sr_slot = slot; res->sr_status_flags = 0; -- cgit v0.10.2 From f4af6e2abc8efb1695203a2b76876edf80f79960 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 20 Nov 2012 14:17:32 -0500 Subject: NFSv4.1: Clean up nfs4_free_slot Change the argument to take the pointer to the slot, instead of just the slotid. We know that the new value of highest_used_slot must be less than the current value. No need to scan the whole table. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 0789ef1..197ef3e 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -412,16 +412,18 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp * Must be called while holding tbl->slot_tbl_lock */ static void -nfs4_free_slot(struct nfs4_slot_table *tbl, u32 slotid) +nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot) { + u32 slotid = slot->slot_nr; + /* clear used bit in bitmap */ __clear_bit(slotid, tbl->used_slots); /* update highest_used_slotid when it is freed */ if (slotid == tbl->highest_used_slotid) { - slotid = find_last_bit(tbl->used_slots, tbl->max_slots); - if (slotid < tbl->max_slots) - tbl->highest_used_slotid = slotid; + u32 new_max = find_last_bit(tbl->used_slots, slotid); + if (new_max < slotid) + tbl->highest_used_slotid = new_max; else tbl->highest_used_slotid = NFS4_NO_SLOT; } @@ -480,7 +482,7 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) session = tbl->session; spin_lock(&tbl->slot_tbl_lock); - nfs4_free_slot(tbl, res->sr_slot - tbl->slots); + nfs4_free_slot(tbl, res->sr_slot); nfs4_check_drain_fc_complete(session); spin_unlock(&tbl->slot_tbl_lock); res->sr_slot = NULL; -- cgit v0.10.2 From 464ee9f966404786ba4c6be35dc8362ee8e6ba4e Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 20 Nov 2012 12:49:27 -0500 Subject: NFSv4.1: Ensure that the client tracks the server target_highest_slotid Dynamic slot allocation in NFSv4.1 depends on the client being able to track the server's target value for the highest slotid in the slot table. See the reference in Section 2.10.6.1 of RFC5661. To avoid ordering problems in the case where 2 SEQUENCE replies contain conflicting updates to this target value, we also introduce a generation counter, to track whether or not an RPC containing a SEQUENCE operation was launched before or after the last update. Also rename the nfs4_slot_table target_max_slots field to 'target_highest_slotid' to avoid confusion with a slot table size or number of slots. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 0be08b9..0ef047b 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -576,7 +576,7 @@ __be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy, if (args->crsa_target_max_slots == fc_tbl->max_slots) goto out; - fc_tbl->target_max_slots = args->crsa_target_max_slots; + fc_tbl->target_highest_slotid = args->crsa_target_max_slots; nfs41_handle_recall_slot(cps->clp); out: dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 197ef3e..d91abaa 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -488,6 +488,28 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) res->sr_slot = NULL; } +/* Update the client's idea of target_highest_slotid */ +static void nfs41_set_target_slotid_locked(struct nfs4_slot_table *tbl, + u32 target_highest_slotid) +{ + if (tbl->target_highest_slotid == target_highest_slotid) + return; + tbl->target_highest_slotid = target_highest_slotid; + tbl->generation++; +} + +static void nfs41_update_target_slotid(struct nfs4_slot_table *tbl, + struct nfs4_slot *slot, + struct nfs4_sequence_res *res) +{ + spin_lock(&tbl->slot_tbl_lock); + if (tbl->generation != slot->generation) + goto out; + nfs41_set_target_slotid_locked(tbl, res->sr_target_highest_slotid); +out: + spin_unlock(&tbl->slot_tbl_lock); +} + static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) { struct nfs4_session *session; @@ -522,6 +544,7 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * /* Check sequence flags */ if (res->sr_status_flags != 0) nfs4_schedule_lease_recovery(clp); + nfs41_update_target_slotid(slot->table, slot, res); break; case -NFS4ERR_DELAY: /* The server detected a resend of the RPC call and @@ -583,6 +606,7 @@ static struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl) tbl->highest_used_slotid = slotid; ret = &tbl->slots[slotid]; ret->renewal_time = jiffies; + ret->generation = tbl->generation; out: dprintk("<-- %s used_slots=%04lx highest_used=%d slotid=%d \n", @@ -5693,6 +5717,7 @@ static void nfs4_add_and_init_slots(struct nfs4_slot_table *tbl, tbl->max_slots = max_slots; } tbl->highest_used_slotid = NFS4_NO_SLOT; + tbl->target_highest_slotid = max_slots - 1; for (i = 0; i < tbl->max_slots; i++) tbl->slots[i].seq_nr = ivalue; spin_unlock(&tbl->slot_tbl_lock); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 9495789..842cb8c 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -2033,17 +2033,16 @@ static int nfs4_recall_slot(struct nfs_client *clp) return 0; nfs4_begin_drain_session(clp); fc_tbl = &clp->cl_session->fc_slot_table; - new = nfs4_alloc_slots(fc_tbl, fc_tbl->target_max_slots, GFP_NOFS); + new = nfs4_alloc_slots(fc_tbl, fc_tbl->target_highest_slotid + 1, GFP_NOFS); if (!new) return -ENOMEM; spin_lock(&fc_tbl->slot_tbl_lock); - for (i = 0; i < fc_tbl->target_max_slots; i++) + for (i = 0; i <= fc_tbl->target_highest_slotid; i++) new[i].seq_nr = fc_tbl->slots[i].seq_nr; old = fc_tbl->slots; fc_tbl->slots = new; - fc_tbl->max_slots = fc_tbl->target_max_slots; - fc_tbl->target_max_slots = 0; + fc_tbl->max_slots = fc_tbl->target_highest_slotid + 1; clp->cl_session->fc_attrs.max_reqs = fc_tbl->max_slots; spin_unlock(&fc_tbl->slot_tbl_lock); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 27b0fec..05d34f1 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -5552,8 +5552,8 @@ static int decode_sequence(struct xdr_stream *xdr, } /* highest slot id - currently not processed */ dummy = be32_to_cpup(p++); - /* target highest slot id - currently not processed */ - dummy = be32_to_cpup(p++); + /* target highest slot id */ + res->sr_target_highest_slotid = be32_to_cpup(p++); /* result flags */ res->sr_status_flags = be32_to_cpup(p); status = 0; diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index b041287..57d4069 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -217,8 +217,9 @@ struct nfs4_slot_table { u32 max_slots; /* # slots in table */ u32 highest_used_slotid; /* sent to server on each SEQ. * op for dynamic resizing */ - u32 target_max_slots; /* Set by CB_RECALL_SLOT as - * the new max_slots */ + u32 target_highest_slotid; /* Server max_slot target */ + unsigned long generation; /* Generation counter for + target_highest_slotid */ struct completion complete; }; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index deb31bb..08c47db 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -188,6 +188,7 @@ struct nfs4_channel_attrs { /* nfs41 sessions slot seqid */ struct nfs4_slot { struct nfs4_slot_table *table; + unsigned long generation; unsigned long renewal_time; u32 slot_nr; u32 seq_nr; @@ -202,6 +203,7 @@ struct nfs4_sequence_res { struct nfs4_slot *sr_slot; /* slot used to send request */ int sr_status; /* sequence operation status */ u32 sr_status_flags; + u32 sr_target_highest_slotid; }; struct nfs4_get_lease_time_args { -- cgit v0.10.2 From da0507b7c95ccd4d9c86394eef42fe076032af30 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 20 Nov 2012 18:10:30 -0500 Subject: NFSv4.1: Reset the sequence number for slots that have been deallocated When the server tells us that it is dynamically resizing the session replay cache, we should reset the sequence number for those slots that have been deallocated. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d91abaa..52435ec 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -498,6 +498,22 @@ static void nfs41_set_target_slotid_locked(struct nfs4_slot_table *tbl, tbl->generation++; } +static void nfs41_set_server_slotid_locked(struct nfs4_slot_table *tbl, + u32 highest_slotid) +{ + unsigned int max_slotid, i; + + if (tbl->server_highest_slotid == highest_slotid) + return; + if (tbl->highest_used_slotid > highest_slotid) + return; + max_slotid = min(tbl->max_slots - 1, highest_slotid); + /* Reset the seq_nr for deallocated slots */ + for (i = tbl->server_highest_slotid + 1; i <= max_slotid; i++) + tbl->slots[i].seq_nr = 1; + tbl->server_highest_slotid = highest_slotid; +} + static void nfs41_update_target_slotid(struct nfs4_slot_table *tbl, struct nfs4_slot *slot, struct nfs4_sequence_res *res) @@ -505,6 +521,7 @@ static void nfs41_update_target_slotid(struct nfs4_slot_table *tbl, spin_lock(&tbl->slot_tbl_lock); if (tbl->generation != slot->generation) goto out; + nfs41_set_server_slotid_locked(tbl, res->sr_highest_slotid); nfs41_set_target_slotid_locked(tbl, res->sr_target_highest_slotid); out: spin_unlock(&tbl->slot_tbl_lock); @@ -5718,6 +5735,7 @@ static void nfs4_add_and_init_slots(struct nfs4_slot_table *tbl, } tbl->highest_used_slotid = NFS4_NO_SLOT; tbl->target_highest_slotid = max_slots - 1; + tbl->server_highest_slotid = max_slots - 1; for (i = 0; i < tbl->max_slots; i++) tbl->slots[i].seq_nr = ivalue; spin_unlock(&tbl->slot_tbl_lock); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 05d34f1..a67040f 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -5550,8 +5550,8 @@ static int decode_sequence(struct xdr_stream *xdr, dprintk("%s Invalid slot id\n", __func__); goto out_err; } - /* highest slot id - currently not processed */ - dummy = be32_to_cpup(p++); + /* highest slot id */ + res->sr_highest_slotid = be32_to_cpup(p++); /* target highest slot id */ res->sr_target_highest_slotid = be32_to_cpup(p++); /* result flags */ diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 57d4069..646e64b 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -218,6 +218,7 @@ struct nfs4_slot_table { u32 highest_used_slotid; /* sent to server on each SEQ. * op for dynamic resizing */ u32 target_highest_slotid; /* Server max_slot target */ + u32 server_highest_slotid; /* Server highest slotid */ unsigned long generation; /* Generation counter for target_highest_slotid */ struct completion complete; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 08c47db..3ddb08f 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -203,6 +203,7 @@ struct nfs4_sequence_res { struct nfs4_slot *sr_slot; /* slot used to send request */ int sr_status; /* sequence operation status */ u32 sr_status_flags; + u32 sr_highest_slotid; u32 sr_target_highest_slotid; }; -- cgit v0.10.2 From ce008c4bb9766bc7eeb02e8299c8baadc25da90b Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 20 Nov 2012 15:16:30 -0500 Subject: NFSv4.1: Fix nfs4_callback_recallslot to work with dynamic slot allocation Ensure that the NFSv4.1 CB_RECALL_SLOT callback updates the slot table target max slotid safely. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 0ef047b..15b9879 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -576,7 +576,7 @@ __be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy, if (args->crsa_target_max_slots == fc_tbl->max_slots) goto out; - fc_tbl->target_highest_slotid = args->crsa_target_max_slots; + nfs41_set_target_slotid(fc_tbl, args->crsa_target_max_slots); nfs41_handle_recall_slot(cps->clp); out: dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 42c5869..5d4e82b 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -260,6 +260,8 @@ extern int nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, extern struct nfs4_slot *nfs4_alloc_slots(struct nfs4_slot_table *table, u32 max_slots, gfp_t gfp_flags); +extern void nfs41_set_target_slotid(struct nfs4_slot_table *tbl, + u32 target_highest_slotid); static inline bool is_ds_only_client(struct nfs_client *clp) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 52435ec..6221223 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -498,6 +498,14 @@ static void nfs41_set_target_slotid_locked(struct nfs4_slot_table *tbl, tbl->generation++; } +void nfs41_set_target_slotid(struct nfs4_slot_table *tbl, + u32 target_highest_slotid) +{ + spin_lock(&tbl->slot_tbl_lock); + nfs41_set_target_slotid_locked(tbl, target_highest_slotid); + spin_unlock(&tbl->slot_tbl_lock); +} + static void nfs41_set_server_slotid_locked(struct nfs4_slot_table *tbl, u32 highest_slotid) { -- cgit v0.10.2 From d5fb4ce33e26e4c1c31c1609b8ffbb24f80bcab8 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 20 Nov 2012 20:24:02 -0500 Subject: NFSv4.1: Don't confuse target_highest_slotid and max_slots in cb_recall_slot Don't confuse the table size and the target_highest_slotid... Signed-off-by: Trond Myklebust diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index 4251c2a..e75631e 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -142,7 +142,7 @@ extern __be32 nfs4_callback_recallany(struct cb_recallanyargs *args, struct cb_recallslotargs { struct sockaddr *crsa_addr; - uint32_t crsa_target_max_slots; + uint32_t crsa_target_highest_slotid; }; extern __be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy, diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 15b9879..ed0b446 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -561,22 +561,20 @@ __be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy, if (!cps->clp) /* set in cb_sequence */ goto out; - dprintk_rcu("NFS: CB_RECALL_SLOT request from %s target max slots %d\n", + dprintk_rcu("NFS: CB_RECALL_SLOT request from %s target highest slotid %d\n", rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR), - args->crsa_target_max_slots); + args->crsa_target_highest_slotid); fc_tbl = &cps->clp->cl_session->fc_slot_table; status = htonl(NFS4ERR_BAD_HIGH_SLOT); - if (args->crsa_target_max_slots > fc_tbl->max_slots || - args->crsa_target_max_slots < 1) + if (args->crsa_target_highest_slotid >= fc_tbl->max_slots || + args->crsa_target_highest_slotid < 1) goto out; status = htonl(NFS4_OK); - if (args->crsa_target_max_slots == fc_tbl->max_slots) - goto out; - nfs41_set_target_slotid(fc_tbl, args->crsa_target_max_slots); + nfs41_set_target_slotid(fc_tbl, args->crsa_target_highest_slotid); nfs41_handle_recall_slot(cps->clp); out: dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 742ff4f..81e8c7d 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -520,7 +520,7 @@ static __be32 decode_recallslot_args(struct svc_rqst *rqstp, p = read_buf(xdr, 4); if (unlikely(p == NULL)) return htonl(NFS4ERR_BADXDR); - args->crsa_target_max_slots = ntohl(*p++); + args->crsa_target_highest_slotid = ntohl(*p++); return 0; } -- cgit v0.10.2 From 1b285ff16ab52fb401aed7ce70abed4bb65b30b5 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 20 Nov 2012 22:32:48 -0500 Subject: NFSv4.1: Allow the server to recall all but one slot If the server wants to leave us with only one slot, or it wants to "shrink" our slot table to something larger than we have now, then so be it. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index ed0b446..a0546ec 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -567,11 +567,6 @@ __be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy, fc_tbl = &cps->clp->cl_session->fc_slot_table; - status = htonl(NFS4ERR_BAD_HIGH_SLOT); - if (args->crsa_target_highest_slotid >= fc_tbl->max_slots || - args->crsa_target_highest_slotid < 1) - goto out; - status = htonl(NFS4_OK); nfs41_set_target_slotid(fc_tbl, args->crsa_target_highest_slotid); -- cgit v0.10.2 From 97e548a93de213b149eea025a97d88e28143b445 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 20 Nov 2012 14:45:48 -0500 Subject: NFSv4.1: Support dynamic resizing of the session slot table Allow the server to control the size of the session slot table by adjusting the value of sr_target_max_slots in the reply to the SEQUENCE operation. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 6221223..1792ece 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -492,10 +492,17 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) static void nfs41_set_target_slotid_locked(struct nfs4_slot_table *tbl, u32 target_highest_slotid) { + unsigned int max_slotid, i; + if (tbl->target_highest_slotid == target_highest_slotid) return; tbl->target_highest_slotid = target_highest_slotid; tbl->generation++; + + max_slotid = min(tbl->max_slots - 1, tbl->target_highest_slotid); + for (i = tbl->max_slotid + 1; i <= max_slotid; i++) + rpc_wake_up_next(&tbl->slot_tbl_waitq); + tbl->max_slotid = max_slotid; } void nfs41_set_target_slotid(struct nfs4_slot_table *tbl, @@ -622,8 +629,8 @@ static struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl) dprintk("--> %s used_slots=%04lx highest_used=%u max_slots=%u\n", __func__, tbl->used_slots[0], tbl->highest_used_slotid, tbl->max_slots); - slotid = find_first_zero_bit(tbl->used_slots, tbl->max_slots); - if (slotid >= tbl->max_slots) + slotid = find_first_zero_bit(tbl->used_slots, tbl->max_slotid + 1); + if (slotid > tbl->max_slotid) goto out; __set_bit(slotid, tbl->used_slots); if (slotid > tbl->highest_used_slotid || @@ -5744,6 +5751,7 @@ static void nfs4_add_and_init_slots(struct nfs4_slot_table *tbl, tbl->highest_used_slotid = NFS4_NO_SLOT; tbl->target_highest_slotid = max_slots - 1; tbl->server_highest_slotid = max_slots - 1; + tbl->max_slotid = max_slots - 1; for (i = 0; i < tbl->max_slots; i++) tbl->slots[i].seq_nr = ivalue; spin_unlock(&tbl->slot_tbl_lock); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 842cb8c..1b7fa73 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -254,15 +254,14 @@ static void nfs4_end_drain_session(struct nfs_client *clp) { struct nfs4_session *ses = clp->cl_session; struct nfs4_slot_table *tbl; - int max_slots; + unsigned int i; if (ses == NULL) return; tbl = &ses->fc_slot_table; if (test_and_clear_bit(NFS4_SESSION_DRAINING, &ses->session_state)) { spin_lock(&tbl->slot_tbl_lock); - max_slots = tbl->max_slots; - while (max_slots--) { + for (i = 0; i <= tbl->max_slotid; i++) { if (rpc_wake_up_first(&tbl->slot_tbl_waitq, nfs4_set_task_privileged, NULL) == NULL) @@ -2043,6 +2042,7 @@ static int nfs4_recall_slot(struct nfs_client *clp) old = fc_tbl->slots; fc_tbl->slots = new; fc_tbl->max_slots = fc_tbl->target_highest_slotid + 1; + fc_tbl->max_slotid = fc_tbl->target_highest_slotid; clp->cl_session->fc_attrs.max_reqs = fc_tbl->max_slots; spin_unlock(&fc_tbl->slot_tbl_lock); diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 646e64b..3071550 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -215,6 +215,7 @@ struct nfs4_slot_table { spinlock_t slot_tbl_lock; struct rpc_wait_queue slot_tbl_waitq; /* allocators may wait here */ u32 max_slots; /* # slots in table */ + u32 max_slotid; /* Max allowed slotid value */ u32 highest_used_slotid; /* sent to server on each SEQ. * op for dynamic resizing */ u32 target_highest_slotid; /* Server max_slot target */ -- cgit v0.10.2 From 87dda67e7386ba7d2164391ea58b34e028d8157b Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 20 Nov 2012 19:49:20 -0500 Subject: NFSv4.1: Allow SEQUENCE to resize the slot table on the fly Instead of an array of slots, use a singly linked list of slots that can be dynamically appended to or shrunk. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 5d4e82b..856bc49 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -258,10 +258,10 @@ extern int nfs4_proc_get_lease_time(struct nfs_client *clp, extern int nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, bool sync); -extern struct nfs4_slot *nfs4_alloc_slots(struct nfs4_slot_table *table, - u32 max_slots, gfp_t gfp_flags); extern void nfs41_set_target_slotid(struct nfs4_slot_table *tbl, u32 target_highest_slotid); +extern int nfs4_resize_slot_table(struct nfs4_slot_table *tbl, + u32 max_reqs, u32 ivalue); static inline bool is_ds_only_client(struct nfs_client *clp) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 1792ece..fc65300 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -397,6 +397,27 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp #if defined(CONFIG_NFS_V4_1) /* + * nfs4_shrink_slot_table - free retired slots from the slot table + */ +static void nfs4_shrink_slot_table(struct nfs4_slot_table *tbl, u32 newsize) +{ + struct nfs4_slot **p; + if (newsize >= tbl->max_slots) + return; + + p = &tbl->slots; + while (newsize--) + p = &(*p)->next; + while (*p) { + struct nfs4_slot *slot = *p; + + *p = slot->next; + kfree(slot); + tbl->max_slots--; + } +} + +/* * nfs4_free_slot - free a slot and efficiently update slot table. * * freeing a slot is trivially done by clearing its respective bit @@ -499,7 +520,7 @@ static void nfs41_set_target_slotid_locked(struct nfs4_slot_table *tbl, tbl->target_highest_slotid = target_highest_slotid; tbl->generation++; - max_slotid = min(tbl->max_slots - 1, tbl->target_highest_slotid); + max_slotid = min(NFS4_MAX_SLOT_TABLE - 1, tbl->target_highest_slotid); for (i = tbl->max_slotid + 1; i <= max_slotid; i++) rpc_wake_up_next(&tbl->slot_tbl_waitq); tbl->max_slotid = max_slotid; @@ -516,16 +537,12 @@ void nfs41_set_target_slotid(struct nfs4_slot_table *tbl, static void nfs41_set_server_slotid_locked(struct nfs4_slot_table *tbl, u32 highest_slotid) { - unsigned int max_slotid, i; - if (tbl->server_highest_slotid == highest_slotid) return; if (tbl->highest_used_slotid > highest_slotid) return; - max_slotid = min(tbl->max_slots - 1, highest_slotid); - /* Reset the seq_nr for deallocated slots */ - for (i = tbl->server_highest_slotid + 1; i <= max_slotid; i++) - tbl->slots[i].seq_nr = 1; + /* Deallocate slots */ + nfs4_shrink_slot_table(tbl, highest_slotid + 1); tbl->server_highest_slotid = highest_slotid; } @@ -612,6 +629,42 @@ static int nfs4_sequence_done(struct rpc_task *task, return nfs41_sequence_done(task, res); } +static struct nfs4_slot *nfs4_new_slot(struct nfs4_slot_table *tbl, + u32 slotid, u32 seq_init, gfp_t gfp_mask) +{ + struct nfs4_slot *slot; + + slot = kzalloc(sizeof(*slot), gfp_mask); + if (slot) { + slot->table = tbl; + slot->slot_nr = slotid; + slot->seq_nr = seq_init; + } + return slot; +} + +static struct nfs4_slot *nfs4_find_or_create_slot(struct nfs4_slot_table *tbl, + u32 slotid, u32 seq_init, gfp_t gfp_mask) +{ + struct nfs4_slot **p, *slot; + + p = &tbl->slots; + for (;;) { + if (*p == NULL) { + *p = nfs4_new_slot(tbl, tbl->max_slots, + seq_init, gfp_mask); + if (*p == NULL) + break; + tbl->max_slots++; + } + slot = *p; + if (slot->slot_nr == slotid) + return slot; + p = &slot->next; + } + return NULL; +} + /* * nfs4_alloc_slot - efficiently look for a free slot * @@ -628,15 +681,17 @@ static struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl) dprintk("--> %s used_slots=%04lx highest_used=%u max_slots=%u\n", __func__, tbl->used_slots[0], tbl->highest_used_slotid, - tbl->max_slots); + tbl->max_slotid + 1); slotid = find_first_zero_bit(tbl->used_slots, tbl->max_slotid + 1); if (slotid > tbl->max_slotid) goto out; + ret = nfs4_find_or_create_slot(tbl, slotid, 1, GFP_NOWAIT); + if (ret == NULL) + goto out; __set_bit(slotid, tbl->used_slots); if (slotid > tbl->highest_used_slotid || tbl->highest_used_slotid == NFS4_NO_SLOT) tbl->highest_used_slotid = slotid; - ret = &tbl->slots[slotid]; ret->renewal_time = jiffies; ret->generation = tbl->generation; @@ -5718,67 +5773,56 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo) return status; } -struct nfs4_slot *nfs4_alloc_slots(struct nfs4_slot_table *table, - u32 max_slots, gfp_t gfp_flags) +static int nfs4_grow_slot_table(struct nfs4_slot_table *tbl, + u32 max_reqs, u32 ivalue) { - struct nfs4_slot *tbl; - u32 i; - - tbl = kmalloc_array(max_slots, sizeof(*tbl), gfp_flags); - if (tbl != NULL) { - for (i = 0; i < max_slots; i++) { - tbl[i].table = table; - tbl[i].slot_nr = i; - } - } - return tbl; + if (max_reqs <= tbl->max_slots) + return 0; + if (nfs4_find_or_create_slot(tbl, max_reqs - 1, ivalue, GFP_NOFS)) + return 0; + return -ENOMEM; } -static void nfs4_add_and_init_slots(struct nfs4_slot_table *tbl, - struct nfs4_slot *new, - u32 max_slots, +static void nfs4_reset_slot_table(struct nfs4_slot_table *tbl, + u32 server_highest_slotid, u32 ivalue) { - struct nfs4_slot *old = NULL; - u32 i; + struct nfs4_slot **p; - spin_lock(&tbl->slot_tbl_lock); - if (new) { - old = tbl->slots; - tbl->slots = new; - tbl->max_slots = max_slots; + nfs4_shrink_slot_table(tbl, server_highest_slotid + 1); + p = &tbl->slots; + while (*p) { + (*p)->seq_nr = ivalue; + p = &(*p)->next; } tbl->highest_used_slotid = NFS4_NO_SLOT; - tbl->target_highest_slotid = max_slots - 1; - tbl->server_highest_slotid = max_slots - 1; - tbl->max_slotid = max_slots - 1; - for (i = 0; i < tbl->max_slots; i++) - tbl->slots[i].seq_nr = ivalue; - spin_unlock(&tbl->slot_tbl_lock); - kfree(old); + tbl->target_highest_slotid = server_highest_slotid; + tbl->server_highest_slotid = server_highest_slotid; + tbl->max_slotid = server_highest_slotid; } /* * (re)Initialise a slot table */ -static int nfs4_realloc_slot_table(struct nfs4_slot_table *tbl, u32 max_reqs, - u32 ivalue) +static int nfs4_realloc_slot_table(struct nfs4_slot_table *tbl, + u32 max_reqs, u32 ivalue) { - struct nfs4_slot *new = NULL; - int ret = -ENOMEM; + int ret; dprintk("--> %s: max_reqs=%u, tbl->max_slots %d\n", __func__, max_reqs, tbl->max_slots); - /* Does the newly negotiated max_reqs match the existing slot table? */ - if (max_reqs != tbl->max_slots) { - new = nfs4_alloc_slots(tbl, max_reqs, GFP_NOFS); - if (!new) - goto out; - } - ret = 0; + if (max_reqs > NFS4_MAX_SLOT_TABLE) + max_reqs = NFS4_MAX_SLOT_TABLE; + + ret = nfs4_grow_slot_table(tbl, max_reqs, ivalue); + if (ret) + goto out; + + spin_lock(&tbl->slot_tbl_lock); + nfs4_reset_slot_table(tbl, max_reqs - 1, ivalue); + spin_unlock(&tbl->slot_tbl_lock); - nfs4_add_and_init_slots(tbl, new, max_reqs, ivalue); dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__, tbl, tbl->slots, tbl->max_slots); out: @@ -5786,18 +5830,28 @@ out: return ret; } +int nfs4_resize_slot_table(struct nfs4_slot_table *tbl, + u32 max_reqs, u32 ivalue) +{ + int ret; + + if (max_reqs > NFS4_MAX_SLOT_TABLE) + max_reqs = NFS4_MAX_SLOT_TABLE; + ret = nfs4_grow_slot_table(tbl, max_reqs, ivalue); + if (ret) + return ret; + spin_lock(&tbl->slot_tbl_lock); + nfs4_shrink_slot_table(tbl, max_reqs); + tbl->max_slotid = max_reqs - 1; + spin_unlock(&tbl->slot_tbl_lock); + return 0; +} + /* Destroy the slot table */ static void nfs4_destroy_slot_tables(struct nfs4_session *session) { - if (session->fc_slot_table.slots != NULL) { - kfree(session->fc_slot_table.slots); - session->fc_slot_table.slots = NULL; - } - if (session->bc_slot_table.slots != NULL) { - kfree(session->bc_slot_table.slots); - session->bc_slot_table.slots = NULL; - } - return; + nfs4_shrink_slot_table(&session->fc_slot_table, 0); + nfs4_shrink_slot_table(&session->bc_slot_table, 0); } /* diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 1b7fa73..c14b2c7 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -2025,29 +2025,15 @@ out: static int nfs4_recall_slot(struct nfs_client *clp) { struct nfs4_slot_table *fc_tbl; - struct nfs4_slot *new, *old; - int i; + u32 new_size; if (!nfs4_has_session(clp)) return 0; nfs4_begin_drain_session(clp); - fc_tbl = &clp->cl_session->fc_slot_table; - new = nfs4_alloc_slots(fc_tbl, fc_tbl->target_highest_slotid + 1, GFP_NOFS); - if (!new) - return -ENOMEM; - spin_lock(&fc_tbl->slot_tbl_lock); - for (i = 0; i <= fc_tbl->target_highest_slotid; i++) - new[i].seq_nr = fc_tbl->slots[i].seq_nr; - old = fc_tbl->slots; - fc_tbl->slots = new; - fc_tbl->max_slots = fc_tbl->target_highest_slotid + 1; - fc_tbl->max_slotid = fc_tbl->target_highest_slotid; - clp->cl_session->fc_attrs.max_reqs = fc_tbl->max_slots; - spin_unlock(&fc_tbl->slot_tbl_lock); - - kfree(old); - return 0; + fc_tbl = &clp->cl_session->fc_slot_table; + new_size = fc_tbl->server_highest_slotid + 1; + return nfs4_resize_slot_table(fc_tbl, new_size, 1); } static int nfs4_bind_conn_to_session(struct nfs_client *clp) diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 3ddb08f..44d256f 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -188,6 +188,7 @@ struct nfs4_channel_attrs { /* nfs41 sessions slot seqid */ struct nfs4_slot { struct nfs4_slot_table *table; + struct nfs4_slot *next; unsigned long generation; unsigned long renewal_time; u32 slot_nr; -- cgit v0.10.2 From afa296103ea3841fdc81d9d66902fe49bb765527 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 20 Nov 2012 20:12:38 -0500 Subject: NFSv4.1: Remove the state manager code to resize the slot table The state manager no longer needs any special machinery to stop the session flow and resize the slot table. It is all done on the fly by the SEQUENCE op code now. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index a0546ec..8610bd1 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -570,7 +570,6 @@ __be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy, status = htonl(NFS4_OK); nfs41_set_target_slotid(fc_tbl, args->crsa_target_highest_slotid); - nfs41_handle_recall_slot(cps->clp); out: dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); return status; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 856bc49..fa1a055 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -21,7 +21,6 @@ enum nfs4_client_state { NFS4CLNT_RECLAIM_NOGRACE, NFS4CLNT_DELEGRETURN, NFS4CLNT_SESSION_RESET, - NFS4CLNT_RECALL_SLOT, NFS4CLNT_LEASE_CONFIRM, NFS4CLNT_SERVER_SCOPE_MISMATCH, NFS4CLNT_PURGE_STATE, @@ -260,8 +259,6 @@ extern int nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, extern void nfs41_set_target_slotid(struct nfs4_slot_table *tbl, u32 target_highest_slotid); -extern int nfs4_resize_slot_table(struct nfs4_slot_table *tbl, - u32 max_reqs, u32 ivalue); static inline bool is_ds_only_client(struct nfs_client *clp) @@ -358,7 +355,6 @@ extern void nfs4_schedule_state_manager(struct nfs_client *); extern void nfs4_schedule_path_down_recovery(struct nfs_client *clp); extern void nfs4_schedule_stateid_recovery(const struct nfs_server *, struct nfs4_state *); extern void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags); -extern void nfs41_handle_recall_slot(struct nfs_client *clp); extern void nfs41_handle_server_scope(struct nfs_client *, struct nfs41_server_scope **); extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index fc65300..0642e28 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5830,23 +5830,6 @@ out: return ret; } -int nfs4_resize_slot_table(struct nfs4_slot_table *tbl, - u32 max_reqs, u32 ivalue) -{ - int ret; - - if (max_reqs > NFS4_MAX_SLOT_TABLE) - max_reqs = NFS4_MAX_SLOT_TABLE; - ret = nfs4_grow_slot_table(tbl, max_reqs, ivalue); - if (ret) - return ret; - spin_lock(&tbl->slot_tbl_lock); - nfs4_shrink_slot_table(tbl, max_reqs); - tbl->max_slotid = max_reqs - 1; - spin_unlock(&tbl->slot_tbl_lock); - return 0; -} - /* Destroy the slot table */ static void nfs4_destroy_slot_tables(struct nfs4_session *session) { diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index c14b2c7..3940cd4 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -302,7 +302,6 @@ static void nfs41_finish_session_reset(struct nfs_client *clp) clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); /* create_session negotiated new slot table */ - clear_bit(NFS4CLNT_RECALL_SLOT, &clp->cl_state); clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state); nfs41_setup_state_renewal(clp); } @@ -1905,14 +1904,6 @@ void nfs4_schedule_session_recovery(struct nfs4_session *session, int err) } EXPORT_SYMBOL_GPL(nfs4_schedule_session_recovery); -void nfs41_handle_recall_slot(struct nfs_client *clp) -{ - set_bit(NFS4CLNT_RECALL_SLOT, &clp->cl_state); - dprintk("%s: scheduling slot recall for server %s\n", __func__, - clp->cl_hostname); - nfs4_schedule_state_manager(clp); -} - static void nfs4_reset_all_state(struct nfs_client *clp) { if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) { @@ -2022,20 +2013,6 @@ out: return status; } -static int nfs4_recall_slot(struct nfs_client *clp) -{ - struct nfs4_slot_table *fc_tbl; - u32 new_size; - - if (!nfs4_has_session(clp)) - return 0; - nfs4_begin_drain_session(clp); - - fc_tbl = &clp->cl_session->fc_slot_table; - new_size = fc_tbl->server_highest_slotid + 1; - return nfs4_resize_slot_table(fc_tbl, new_size, 1); -} - static int nfs4_bind_conn_to_session(struct nfs_client *clp) { struct rpc_cred *cred; @@ -2066,7 +2043,6 @@ static int nfs4_bind_conn_to_session(struct nfs_client *clp) #else /* CONFIG_NFS_V4_1 */ static int nfs4_reset_session(struct nfs_client *clp) { return 0; } static int nfs4_end_drain_session(struct nfs_client *clp) { return 0; } -static int nfs4_recall_slot(struct nfs_client *clp) { return 0; } static int nfs4_bind_conn_to_session(struct nfs_client *clp) { @@ -2126,15 +2102,6 @@ static void nfs4_state_manager(struct nfs_client *clp) continue; } - /* Recall session slots */ - if (test_and_clear_bit(NFS4CLNT_RECALL_SLOT, &clp->cl_state)) { - section = "recall slot"; - status = nfs4_recall_slot(clp); - if (status < 0) - goto out_error; - continue; - } - /* First recover reboot state... */ if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) { section = "reclaim reboot"; -- cgit v0.10.2 From ac0748359a55faf4618f5f0bd9f9bf967c41d218 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 21 Nov 2012 09:06:11 -0500 Subject: NFSv4.1: CB_RECALL_SLOT must schedule a sequence op after updating targets RFC5661 requires us to make sure that the server knows we've updated our slot table size by sending at least one SEQUENCE op containing the new 'highest_slotid' value. We can do so using the 'CHECK_LEASE' functionality of the state manager. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 8610bd1..f99faad 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -570,6 +570,7 @@ __be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy, status = htonl(NFS4_OK); nfs41_set_target_slotid(fc_tbl, args->crsa_target_highest_slotid); + nfs41_server_notify_target_slotid_update(cps->clp); out: dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); return status; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index fa1a055..0a109ec 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -334,6 +334,7 @@ struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp); int nfs41_discover_server_trunking(struct nfs_client *clp, struct nfs_client **, struct rpc_cred *); extern void nfs4_schedule_session_recovery(struct nfs4_session *, int); +extern void nfs41_server_notify_target_slotid_update(struct nfs_client *clp); #else static inline void nfs4_schedule_session_recovery(struct nfs4_session *session, int err) { diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 3940cd4..896be21 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1904,6 +1904,18 @@ void nfs4_schedule_session_recovery(struct nfs4_session *session, int err) } EXPORT_SYMBOL_GPL(nfs4_schedule_session_recovery); +static void nfs41_ping_server(struct nfs_client *clp) +{ + /* Use CHECK_LEASE to ping the server with a SEQUENCE */ + set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state); + nfs4_schedule_state_manager(clp); +} + +void nfs41_server_notify_target_slotid_update(struct nfs_client *clp) +{ + nfs41_ping_server(clp); +} + static void nfs4_reset_all_state(struct nfs_client *clp) { if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) { -- cgit v0.10.2 From 69d206b5b39e298755b60e8e7056cb240182eb95 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 22 Nov 2012 13:21:02 -0500 Subject: NFSv4.1: If slot allocation fails due to OOM, retry more quickly If the NFSv4.1 session slot allocation fails due to an ENOMEM condition, then set the task->tk_timeout to 1/4 second to ensure that we do retry the slot allocation more quickly. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 0642e28..e9e4d63 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -662,7 +662,7 @@ static struct nfs4_slot *nfs4_find_or_create_slot(struct nfs4_slot_table *tbl, return slot; p = &slot->next; } - return NULL; + return ERR_PTR(-ENOMEM); } /* @@ -676,7 +676,7 @@ static struct nfs4_slot *nfs4_find_or_create_slot(struct nfs4_slot_table *tbl, */ static struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl) { - struct nfs4_slot *ret = NULL; + struct nfs4_slot *ret = ERR_PTR(-EBUSY); u32 slotid; dprintk("--> %s used_slots=%04lx highest_used=%u max_slots=%u\n", @@ -686,7 +686,7 @@ static struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl) if (slotid > tbl->max_slotid) goto out; ret = nfs4_find_or_create_slot(tbl, slotid, 1, GFP_NOWAIT); - if (ret == NULL) + if (IS_ERR(ret)) goto out; __set_bit(slotid, tbl->used_slots); if (slotid > tbl->highest_used_slotid || @@ -698,7 +698,7 @@ static struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl) out: dprintk("<-- %s used_slots=%04lx highest_used=%d slotid=%d \n", __func__, tbl->used_slots[0], tbl->highest_used_slotid, - ret ? ret->slot_nr : -1); + !IS_ERR(ret) ? ret->slot_nr : -1); return ret; } @@ -727,6 +727,8 @@ int nfs41_setup_sequence(struct nfs4_session *session, tbl = &session->fc_slot_table; + task->tk_timeout = 0; + spin_lock(&tbl->slot_tbl_lock); if (test_bit(NFS4_SESSION_DRAINING, &session->session_state) && !rpc_task_has_priority(task, RPC_PRIORITY_PRIVILEGED)) { @@ -746,7 +748,10 @@ int nfs41_setup_sequence(struct nfs4_session *session, } slot = nfs4_alloc_slot(tbl); - if (slot == NULL) { + if (IS_ERR(slot)) { + /* If out of memory, try again in 1/4 second */ + if (slot == ERR_PTR(-ENOMEM)) + task->tk_timeout = HZ >> 2; rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); spin_unlock(&tbl->slot_tbl_lock); dprintk("<-- %s: no free slots\n", __func__); @@ -5778,7 +5783,7 @@ static int nfs4_grow_slot_table(struct nfs4_slot_table *tbl, { if (max_reqs <= tbl->max_slots) return 0; - if (nfs4_find_or_create_slot(tbl, max_reqs - 1, ivalue, GFP_NOFS)) + if (!IS_ERR(nfs4_find_or_create_slot(tbl, max_reqs - 1, ivalue, GFP_NOFS))) return 0; return -ENOMEM; } -- cgit v0.10.2 From 5d63360dd8daffc2bc86531e9a44ff9d4881b102 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 23 Nov 2012 13:09:38 -0500 Subject: NFSv4.1: Clean up session draining Coalesce nfs4_check_drain_bc_complete and nfs4_check_drain_fc_complete into a single function that can be called when the slot table is known to be empty, then change nfs4_callback_free_slot() and nfs4_free_slot() to use it. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index e75631e..efd54f0a 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -167,8 +167,6 @@ extern __be32 nfs4_callback_layoutrecall( struct cb_layoutrecallargs *args, void *dummy, struct cb_process_state *cps); -extern void nfs4_check_drain_bc_complete(struct nfs4_session *ses); - struct cb_devicenotifyitem { uint32_t cbd_notify_type; uint32_t cbd_layout_type; diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 81e8c7d..ea6a7b1 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -762,7 +762,7 @@ static void nfs4_callback_free_slot(struct nfs4_session *session) * A single slot, so highest used slotid is either 0 or -1 */ tbl->highest_used_slotid = NFS4_NO_SLOT; - nfs4_check_drain_bc_complete(session); + nfs4_session_drain_complete(session, tbl); spin_unlock(&tbl->slot_tbl_lock); } diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 0a109ec..16b1937 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -335,6 +335,14 @@ int nfs41_discover_server_trunking(struct nfs_client *clp, struct nfs_client **, struct rpc_cred *); extern void nfs4_schedule_session_recovery(struct nfs4_session *, int); extern void nfs41_server_notify_target_slotid_update(struct nfs_client *clp); + +extern void nfs4_session_drain_complete(struct nfs4_session *session, + struct nfs4_slot_table *tbl); + +static inline bool nfs4_session_draining(struct nfs4_session *session) +{ + return !!test_bit(NFS4_SESSION_DRAINING, &session->session_state); +} #else static inline void nfs4_schedule_session_recovery(struct nfs4_session *session, int err) { diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index e9e4d63..0b0f11b 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -445,8 +445,10 @@ nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot) u32 new_max = find_last_bit(tbl->used_slots, slotid); if (new_max < slotid) tbl->highest_used_slotid = new_max; - else + else { tbl->highest_used_slotid = NFS4_NO_SLOT; + nfs4_session_drain_complete(tbl->session, tbl); + } } dprintk("%s: slotid %u highest_used_slotid %d\n", __func__, slotid, tbl->highest_used_slotid); @@ -458,36 +460,6 @@ bool nfs4_set_task_privileged(struct rpc_task *task, void *dummy) return true; } -/* - * Signal state manager thread if session fore channel is drained - */ -static void nfs4_check_drain_fc_complete(struct nfs4_session *ses) -{ - if (!test_bit(NFS4_SESSION_DRAINING, &ses->session_state)) { - rpc_wake_up_first(&ses->fc_slot_table.slot_tbl_waitq, - nfs4_set_task_privileged, NULL); - return; - } - - if (ses->fc_slot_table.highest_used_slotid != NFS4_NO_SLOT) - return; - - dprintk("%s COMPLETE: Session Fore Channel Drained\n", __func__); - complete(&ses->fc_slot_table.complete); -} - -/* - * Signal state manager thread if session back channel is drained - */ -void nfs4_check_drain_bc_complete(struct nfs4_session *ses) -{ - if (!test_bit(NFS4_SESSION_DRAINING, &ses->session_state) || - ses->bc_slot_table.highest_used_slotid != NFS4_NO_SLOT) - return; - dprintk("%s COMPLETE: Session Back Channel Drained\n", __func__); - complete(&ses->bc_slot_table.complete); -} - static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) { struct nfs4_session *session; @@ -504,7 +476,9 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) spin_lock(&tbl->slot_tbl_lock); nfs4_free_slot(tbl, res->sr_slot); - nfs4_check_drain_fc_complete(session); + if (!nfs4_session_draining(session)) + rpc_wake_up_first(&tbl->slot_tbl_waitq, + nfs4_set_task_privileged, NULL); spin_unlock(&tbl->slot_tbl_lock); res->sr_slot = NULL; } diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 896be21..1fb3e6c 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -271,6 +271,16 @@ static void nfs4_end_drain_session(struct nfs_client *clp) } } +/* + * Signal state manager thread if session fore channel is drained + */ +void nfs4_session_drain_complete(struct nfs4_session *session, + struct nfs4_slot_table *tbl) +{ + if (nfs4_session_draining(session)) + complete(&tbl->complete); +} + static int nfs4_wait_on_slot_tbl(struct nfs4_slot_table *tbl) { spin_lock(&tbl->slot_tbl_lock); -- cgit v0.10.2 From 330212796756ca2752b2a70a83860e145b77487c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 26 Nov 2012 13:13:29 -0500 Subject: NFSv4: Move nfs4_wait_clnt_recover and nfs4_client_recover_expired_lease nfs4_wait_clnt_recover and nfs4_client_recover_expired_lease are both generic state related functions. As such, they belong in nfs4state.c, and not nfs4proc.c Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 16b1937..2f6a9f9 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -11,6 +11,8 @@ #if IS_ENABLED(CONFIG_NFS_V4) +#define NFS4_MAX_LOOP_ON_RECOVER (10) + struct idmap; enum nfs4_client_state { @@ -360,6 +362,8 @@ extern void nfs4_state_set_mode_locked(struct nfs4_state *, fmode_t); extern void nfs_inode_find_state_and_recover(struct inode *inode, const nfs4_stateid *stateid); extern void nfs4_schedule_lease_recovery(struct nfs_client *); +extern int nfs4_wait_clnt_recover(struct nfs_client *clp); +extern int nfs4_client_recover_expired_lease(struct nfs_client *clp); extern void nfs4_schedule_state_manager(struct nfs_client *); extern void nfs4_schedule_path_down_recovery(struct nfs_client *clp); extern void nfs4_schedule_stateid_recovery(const struct nfs_server *, struct nfs4_state *); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 0b0f11b..d75e2a2 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -70,8 +70,6 @@ #define NFS4_POLL_RETRY_MIN (HZ/10) #define NFS4_POLL_RETRY_MAX (15*HZ) -#define NFS4_MAX_LOOP_ON_RECOVER (10) - struct nfs4_opendata; static int _nfs4_proc_open(struct nfs4_opendata *data); static int _nfs4_recover_proc_open(struct nfs4_opendata *data); @@ -255,22 +253,6 @@ static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dent kunmap_atomic(start); } -static int nfs4_wait_clnt_recover(struct nfs_client *clp) -{ - int res; - - might_sleep(); - - res = wait_on_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING, - nfs_wait_bit_killable, TASK_KILLABLE); - if (res) - return res; - - if (clp->cl_cons_state < 0) - return clp->cl_cons_state; - return 0; -} - static int nfs4_delay(struct rpc_clnt *clnt, long *timeout) { int res = 0; @@ -1883,24 +1865,6 @@ static int _nfs4_proc_open(struct nfs4_opendata *data) return 0; } -static int nfs4_client_recover_expired_lease(struct nfs_client *clp) -{ - unsigned int loop; - int ret; - - for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) { - ret = nfs4_wait_clnt_recover(clp); - if (ret != 0) - break; - if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) && - !test_bit(NFS4CLNT_CHECK_LEASE,&clp->cl_state)) - break; - nfs4_schedule_state_manager(clp); - ret = -EIO; - } - return ret; -} - static int nfs4_recover_expired_lease(struct nfs_server *server) { return nfs4_client_recover_expired_lease(server->nfs_client); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 1fb3e6c..1077b96 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1216,6 +1216,40 @@ void nfs4_schedule_lease_recovery(struct nfs_client *clp) } EXPORT_SYMBOL_GPL(nfs4_schedule_lease_recovery); +int nfs4_wait_clnt_recover(struct nfs_client *clp) +{ + int res; + + might_sleep(); + + res = wait_on_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING, + nfs_wait_bit_killable, TASK_KILLABLE); + if (res) + return res; + + if (clp->cl_cons_state < 0) + return clp->cl_cons_state; + return 0; +} + +int nfs4_client_recover_expired_lease(struct nfs_client *clp) +{ + unsigned int loop; + int ret; + + for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) { + ret = nfs4_wait_clnt_recover(clp); + if (ret != 0) + break; + if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) && + !test_bit(NFS4CLNT_CHECK_LEASE,&clp->cl_state)) + break; + nfs4_schedule_state_manager(clp); + ret = -EIO; + } + return ret; +} + /* * nfs40_handle_cb_pathdown - return all delegations after NFS4ERR_CB_PATH_DOWN * @clp: client to process -- cgit v0.10.2 From 73e39aaa8366694450cd6034050f542f965e277d Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 26 Nov 2012 12:49:34 -0500 Subject: NFSv4.1: Cleanup move session slot management to fs/nfs/nfs4session.c NFSv4.1 session management is getting complex enough to deserve a separate file. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index b7db608..cce2c05 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -24,7 +24,7 @@ nfsv4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o delegation.o idmap.o callback.o callback_xdr.o callback_proc.o \ nfs4namespace.o nfs4getroot.o nfs4client.o nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o -nfsv4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o +nfsv4-$(CONFIG_NFS_V4_1) += nfs4session.o pnfs.o pnfs_dev.o obj-$(CONFIG_PNFS_FILE_LAYOUT) += nfs_layout_nfsv41_files.o nfs_layout_nfsv41_files-y := nfs4filelayout.o nfs4filelayoutdev.o diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index f99faad..c89b26b 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -14,6 +14,7 @@ #include "delegation.h" #include "internal.h" #include "pnfs.h" +#include "nfs4session.h" #ifdef NFS_DEBUG #define NFSDBG_FACILITY NFSDBG_CALLBACK diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 05521ca..8965a99 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -276,8 +276,6 @@ extern const u32 nfs41_maxwrite_overhead; extern struct rpc_procinfo nfs4_procedures[]; #endif -extern int nfs4_init_ds_session(struct nfs_client *, unsigned long); - /* proc.c */ void nfs_close_context(struct nfs_open_context *ctx, int is_sync); extern struct nfs_client *nfs_init_client(struct nfs_client *clp, diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 2f6a9f9..cd3e309 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -249,19 +249,13 @@ extern int nfs4_setup_sequence(const struct nfs_server *server, extern int nfs41_setup_sequence(struct nfs4_session *session, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, struct rpc_task *task); -extern void nfs4_destroy_session(struct nfs4_session *session); -extern struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp); extern int nfs4_proc_create_session(struct nfs_client *, struct rpc_cred *); extern int nfs4_proc_destroy_session(struct nfs4_session *, struct rpc_cred *); -extern int nfs4_init_session(struct nfs_server *server); extern int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo); extern int nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, bool sync); -extern void nfs41_set_target_slotid(struct nfs4_slot_table *tbl, - u32 target_highest_slotid); - static inline bool is_ds_only_client(struct nfs_client *clp) { @@ -287,11 +281,6 @@ static inline int nfs4_setup_sequence(const struct nfs_server *server, return 0; } -static inline int nfs4_init_session(struct nfs_server *server) -{ - return 0; -} - static inline bool is_ds_only_client(struct nfs_client *clp) { diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 72717e6..acc3472 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -12,6 +12,7 @@ #include "internal.h" #include "callback.h" #include "delegation.h" +#include "nfs4session.h" #include "pnfs.h" #include "netns.h" diff --git a/fs/nfs/nfs4filelayoutdev.c b/fs/nfs/nfs4filelayoutdev.c index 93e2530..b720064 100644 --- a/fs/nfs/nfs4filelayoutdev.c +++ b/fs/nfs/nfs4filelayoutdev.c @@ -33,6 +33,7 @@ #include #include "internal.h" +#include "nfs4session.h" #include "nfs4filelayout.h" #define NFSDBG_FACILITY NFSDBG_PNFS_LD diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d75e2a2..a0c35ab 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -52,7 +52,6 @@ #include #include #include -#include #include #include #include @@ -64,6 +63,8 @@ #include "callback.h" #include "pnfs.h" #include "netns.h" +#include "nfs4session.h" + #define NFSDBG_FACILITY NFSDBG_PROC @@ -378,64 +379,6 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp #if defined(CONFIG_NFS_V4_1) -/* - * nfs4_shrink_slot_table - free retired slots from the slot table - */ -static void nfs4_shrink_slot_table(struct nfs4_slot_table *tbl, u32 newsize) -{ - struct nfs4_slot **p; - if (newsize >= tbl->max_slots) - return; - - p = &tbl->slots; - while (newsize--) - p = &(*p)->next; - while (*p) { - struct nfs4_slot *slot = *p; - - *p = slot->next; - kfree(slot); - tbl->max_slots--; - } -} - -/* - * nfs4_free_slot - free a slot and efficiently update slot table. - * - * freeing a slot is trivially done by clearing its respective bit - * in the bitmap. - * If the freed slotid equals highest_used_slotid we want to update it - * so that the server would be able to size down the slot table if needed, - * otherwise we know that the highest_used_slotid is still in use. - * When updating highest_used_slotid there may be "holes" in the bitmap - * so we need to scan down from highest_used_slotid to 0 looking for the now - * highest slotid in use. - * If none found, highest_used_slotid is set to NFS4_NO_SLOT. - * - * Must be called while holding tbl->slot_tbl_lock - */ -static void -nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot) -{ - u32 slotid = slot->slot_nr; - - /* clear used bit in bitmap */ - __clear_bit(slotid, tbl->used_slots); - - /* update highest_used_slotid when it is freed */ - if (slotid == tbl->highest_used_slotid) { - u32 new_max = find_last_bit(tbl->used_slots, slotid); - if (new_max < slotid) - tbl->highest_used_slotid = new_max; - else { - tbl->highest_used_slotid = NFS4_NO_SLOT; - nfs4_session_drain_complete(tbl->session, tbl); - } - } - dprintk("%s: slotid %u highest_used_slotid %d\n", __func__, - slotid, tbl->highest_used_slotid); -} - bool nfs4_set_task_privileged(struct rpc_task *task, void *dummy) { rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); @@ -465,56 +408,6 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) res->sr_slot = NULL; } -/* Update the client's idea of target_highest_slotid */ -static void nfs41_set_target_slotid_locked(struct nfs4_slot_table *tbl, - u32 target_highest_slotid) -{ - unsigned int max_slotid, i; - - if (tbl->target_highest_slotid == target_highest_slotid) - return; - tbl->target_highest_slotid = target_highest_slotid; - tbl->generation++; - - max_slotid = min(NFS4_MAX_SLOT_TABLE - 1, tbl->target_highest_slotid); - for (i = tbl->max_slotid + 1; i <= max_slotid; i++) - rpc_wake_up_next(&tbl->slot_tbl_waitq); - tbl->max_slotid = max_slotid; -} - -void nfs41_set_target_slotid(struct nfs4_slot_table *tbl, - u32 target_highest_slotid) -{ - spin_lock(&tbl->slot_tbl_lock); - nfs41_set_target_slotid_locked(tbl, target_highest_slotid); - spin_unlock(&tbl->slot_tbl_lock); -} - -static void nfs41_set_server_slotid_locked(struct nfs4_slot_table *tbl, - u32 highest_slotid) -{ - if (tbl->server_highest_slotid == highest_slotid) - return; - if (tbl->highest_used_slotid > highest_slotid) - return; - /* Deallocate slots */ - nfs4_shrink_slot_table(tbl, highest_slotid + 1); - tbl->server_highest_slotid = highest_slotid; -} - -static void nfs41_update_target_slotid(struct nfs4_slot_table *tbl, - struct nfs4_slot *slot, - struct nfs4_sequence_res *res) -{ - spin_lock(&tbl->slot_tbl_lock); - if (tbl->generation != slot->generation) - goto out; - nfs41_set_server_slotid_locked(tbl, res->sr_highest_slotid); - nfs41_set_target_slotid_locked(tbl, res->sr_target_highest_slotid); -out: - spin_unlock(&tbl->slot_tbl_lock); -} - static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) { struct nfs4_session *session; @@ -585,79 +478,6 @@ static int nfs4_sequence_done(struct rpc_task *task, return nfs41_sequence_done(task, res); } -static struct nfs4_slot *nfs4_new_slot(struct nfs4_slot_table *tbl, - u32 slotid, u32 seq_init, gfp_t gfp_mask) -{ - struct nfs4_slot *slot; - - slot = kzalloc(sizeof(*slot), gfp_mask); - if (slot) { - slot->table = tbl; - slot->slot_nr = slotid; - slot->seq_nr = seq_init; - } - return slot; -} - -static struct nfs4_slot *nfs4_find_or_create_slot(struct nfs4_slot_table *tbl, - u32 slotid, u32 seq_init, gfp_t gfp_mask) -{ - struct nfs4_slot **p, *slot; - - p = &tbl->slots; - for (;;) { - if (*p == NULL) { - *p = nfs4_new_slot(tbl, tbl->max_slots, - seq_init, gfp_mask); - if (*p == NULL) - break; - tbl->max_slots++; - } - slot = *p; - if (slot->slot_nr == slotid) - return slot; - p = &slot->next; - } - return ERR_PTR(-ENOMEM); -} - -/* - * nfs4_alloc_slot - efficiently look for a free slot - * - * nfs4_alloc_slot looks for an unset bit in the used_slots bitmap. - * If found, we mark the slot as used, update the highest_used_slotid, - * and respectively set up the sequence operation args. - * - * Note: must be called with under the slot_tbl_lock. - */ -static struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl) -{ - struct nfs4_slot *ret = ERR_PTR(-EBUSY); - u32 slotid; - - dprintk("--> %s used_slots=%04lx highest_used=%u max_slots=%u\n", - __func__, tbl->used_slots[0], tbl->highest_used_slotid, - tbl->max_slotid + 1); - slotid = find_first_zero_bit(tbl->used_slots, tbl->max_slotid + 1); - if (slotid > tbl->max_slotid) - goto out; - ret = nfs4_find_or_create_slot(tbl, slotid, 1, GFP_NOWAIT); - if (IS_ERR(ret)) - goto out; - __set_bit(slotid, tbl->used_slots); - if (slotid > tbl->highest_used_slotid || - tbl->highest_used_slotid == NFS4_NO_SLOT) - tbl->highest_used_slotid = slotid; - ret->renewal_time = jiffies; - ret->generation = tbl->generation; - -out: - dprintk("<-- %s used_slots=%04lx highest_used=%d slotid=%d \n", - __func__, tbl->used_slots[0], tbl->highest_used_slotid, - !IS_ERR(ret) ? ret->slot_nr : -1); - return ret; -} - static void nfs41_init_sequence(struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, int cache_reply) { @@ -5716,143 +5536,6 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo) return status; } -static int nfs4_grow_slot_table(struct nfs4_slot_table *tbl, - u32 max_reqs, u32 ivalue) -{ - if (max_reqs <= tbl->max_slots) - return 0; - if (!IS_ERR(nfs4_find_or_create_slot(tbl, max_reqs - 1, ivalue, GFP_NOFS))) - return 0; - return -ENOMEM; -} - -static void nfs4_reset_slot_table(struct nfs4_slot_table *tbl, - u32 server_highest_slotid, - u32 ivalue) -{ - struct nfs4_slot **p; - - nfs4_shrink_slot_table(tbl, server_highest_slotid + 1); - p = &tbl->slots; - while (*p) { - (*p)->seq_nr = ivalue; - p = &(*p)->next; - } - tbl->highest_used_slotid = NFS4_NO_SLOT; - tbl->target_highest_slotid = server_highest_slotid; - tbl->server_highest_slotid = server_highest_slotid; - tbl->max_slotid = server_highest_slotid; -} - -/* - * (re)Initialise a slot table - */ -static int nfs4_realloc_slot_table(struct nfs4_slot_table *tbl, - u32 max_reqs, u32 ivalue) -{ - int ret; - - dprintk("--> %s: max_reqs=%u, tbl->max_slots %d\n", __func__, - max_reqs, tbl->max_slots); - - if (max_reqs > NFS4_MAX_SLOT_TABLE) - max_reqs = NFS4_MAX_SLOT_TABLE; - - ret = nfs4_grow_slot_table(tbl, max_reqs, ivalue); - if (ret) - goto out; - - spin_lock(&tbl->slot_tbl_lock); - nfs4_reset_slot_table(tbl, max_reqs - 1, ivalue); - spin_unlock(&tbl->slot_tbl_lock); - - dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__, - tbl, tbl->slots, tbl->max_slots); -out: - dprintk("<-- %s: return %d\n", __func__, ret); - return ret; -} - -/* Destroy the slot table */ -static void nfs4_destroy_slot_tables(struct nfs4_session *session) -{ - nfs4_shrink_slot_table(&session->fc_slot_table, 0); - nfs4_shrink_slot_table(&session->bc_slot_table, 0); -} - -/* - * Initialize or reset the forechannel and backchannel tables - */ -static int nfs4_setup_session_slot_tables(struct nfs4_session *ses) -{ - struct nfs4_slot_table *tbl; - int status; - - dprintk("--> %s\n", __func__); - /* Fore channel */ - tbl = &ses->fc_slot_table; - tbl->session = ses; - status = nfs4_realloc_slot_table(tbl, ses->fc_attrs.max_reqs, 1); - if (status) /* -ENOMEM */ - return status; - /* Back channel */ - tbl = &ses->bc_slot_table; - tbl->session = ses; - status = nfs4_realloc_slot_table(tbl, ses->bc_attrs.max_reqs, 0); - if (status && tbl->slots == NULL) - /* Fore and back channel share a connection so get - * both slot tables or neither */ - nfs4_destroy_slot_tables(ses); - return status; -} - -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_NOFS); - if (!session) - return NULL; - - tbl = &session->fc_slot_table; - tbl->highest_used_slotid = NFS4_NO_SLOT; - spin_lock_init(&tbl->slot_tbl_lock); - rpc_init_priority_wait_queue(&tbl->slot_tbl_waitq, "ForeChannel Slot table"); - init_completion(&tbl->complete); - - tbl = &session->bc_slot_table; - tbl->highest_used_slotid = NFS4_NO_SLOT; - spin_lock_init(&tbl->slot_tbl_lock); - rpc_init_wait_queue(&tbl->slot_tbl_waitq, "BackChannel Slot table"); - init_completion(&tbl->complete); - - session->session_state = 1<clp = clp; - return session; -} - -void nfs4_destroy_session(struct nfs4_session *session) -{ - struct rpc_xprt *xprt; - struct rpc_cred *cred; - - cred = nfs4_get_exchange_id_cred(session->clp); - nfs4_proc_destroy_session(session, cred); - if (cred) - put_rpccred(cred); - - rcu_read_lock(); - xprt = rcu_dereference(session->clp->cl_rpcclient->cl_xprt); - rcu_read_unlock(); - dprintk("%s Destroy backchannel for xprt %p\n", - __func__, xprt); - xprt_destroy_backchannel(xprt, NFS41_BC_MIN_CALLBACKS); - nfs4_destroy_slot_tables(session); - kfree(session); -} - /* * Initialize the values to be used by the client in CREATE_SESSION * If nfs4_init_session set the fore channel request and response sizes, @@ -6047,100 +5730,6 @@ int nfs4_proc_destroy_session(struct nfs4_session *session, } /* - * With sessions, the client is not marked ready until after a - * successful EXCHANGE_ID and CREATE_SESSION. - * - * Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate - * other versions of NFS can be tried. - */ -static int nfs41_check_session_ready(struct nfs_client *clp) -{ - int ret; - - if (clp->cl_cons_state == NFS_CS_SESSION_INITING) { - ret = nfs4_client_recover_expired_lease(clp); - if (ret) - return ret; - } - if (clp->cl_cons_state < NFS_CS_READY) - return -EPROTONOSUPPORT; - smp_rmb(); - return 0; -} - -int nfs4_init_session(struct nfs_server *server) -{ - struct nfs_client *clp = server->nfs_client; - struct nfs4_session *session; - unsigned int target_max_rqst_sz = NFS_MAX_FILE_IO_SIZE; - unsigned int target_max_resp_sz = NFS_MAX_FILE_IO_SIZE; - - if (!nfs4_has_session(clp)) - return 0; - - if (server->rsize != 0) - target_max_resp_sz = server->rsize; - target_max_resp_sz += nfs41_maxread_overhead; - - if (server->wsize != 0) - target_max_rqst_sz = server->wsize; - target_max_rqst_sz += nfs41_maxwrite_overhead; - - session = clp->cl_session; - spin_lock(&clp->cl_lock); - if (test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) { - /* Initialise targets and channel attributes */ - session->fc_target_max_rqst_sz = target_max_rqst_sz; - session->fc_attrs.max_rqst_sz = target_max_rqst_sz; - session->fc_target_max_resp_sz = target_max_resp_sz; - session->fc_attrs.max_resp_sz = target_max_resp_sz; - } else { - /* Just adjust the targets */ - if (target_max_rqst_sz > session->fc_target_max_rqst_sz) { - session->fc_target_max_rqst_sz = target_max_rqst_sz; - set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); - } - if (target_max_resp_sz > session->fc_target_max_resp_sz) { - session->fc_target_max_resp_sz = target_max_resp_sz; - set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); - } - } - spin_unlock(&clp->cl_lock); - - if (test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)) - nfs4_schedule_lease_recovery(clp); - - return nfs41_check_session_ready(clp); -} - -int nfs4_init_ds_session(struct nfs_client *clp, unsigned long lease_time) -{ - struct nfs4_session *session = clp->cl_session; - int ret; - - spin_lock(&clp->cl_lock); - if (test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) { - /* - * Do not set NFS_CS_CHECK_LEASE_TIME instead set the - * DS lease to be equal to the MDS lease. - */ - clp->cl_lease_time = lease_time; - clp->cl_last_renewal = jiffies; - } - spin_unlock(&clp->cl_lock); - - ret = nfs41_check_session_ready(clp); - if (ret) - return ret; - /* Test for the DS role */ - if (!is_ds_client(clp)) - return -ENODEV; - return 0; -} -EXPORT_SYMBOL_GPL(nfs4_init_ds_session); - - -/* * Renew the cl_session lease. */ struct nfs4_sequence_data { diff --git a/fs/nfs/nfs4session.c b/fs/nfs/nfs4session.c new file mode 100644 index 0000000..7011702 --- /dev/null +++ b/fs/nfs/nfs4session.c @@ -0,0 +1,436 @@ +/* + * fs/nfs/nfs4session.c + * + * Copyright (c) 2012 Trond Myklebust + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nfs4_fs.h" +#include "internal.h" +#include "nfs4session.h" +#include "callback.h" + +#define NFSDBG_FACILITY NFSDBG_STATE + +/* + * nfs4_shrink_slot_table - free retired slots from the slot table + */ +static void nfs4_shrink_slot_table(struct nfs4_slot_table *tbl, u32 newsize) +{ + struct nfs4_slot **p; + if (newsize >= tbl->max_slots) + return; + + p = &tbl->slots; + while (newsize--) + p = &(*p)->next; + while (*p) { + struct nfs4_slot *slot = *p; + + *p = slot->next; + kfree(slot); + tbl->max_slots--; + } +} + +/* + * nfs4_free_slot - free a slot and efficiently update slot table. + * + * freeing a slot is trivially done by clearing its respective bit + * in the bitmap. + * If the freed slotid equals highest_used_slotid we want to update it + * so that the server would be able to size down the slot table if needed, + * otherwise we know that the highest_used_slotid is still in use. + * When updating highest_used_slotid there may be "holes" in the bitmap + * so we need to scan down from highest_used_slotid to 0 looking for the now + * highest slotid in use. + * If none found, highest_used_slotid is set to NFS4_NO_SLOT. + * + * Must be called while holding tbl->slot_tbl_lock + */ +void nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot) +{ + u32 slotid = slot->slot_nr; + + /* clear used bit in bitmap */ + __clear_bit(slotid, tbl->used_slots); + + /* update highest_used_slotid when it is freed */ + if (slotid == tbl->highest_used_slotid) { + u32 new_max = find_last_bit(tbl->used_slots, slotid); + if (new_max < slotid) + tbl->highest_used_slotid = new_max; + else { + tbl->highest_used_slotid = NFS4_NO_SLOT; + nfs4_session_drain_complete(tbl->session, tbl); + } + } + dprintk("%s: slotid %u highest_used_slotid %d\n", __func__, + slotid, tbl->highest_used_slotid); +} + +static struct nfs4_slot *nfs4_new_slot(struct nfs4_slot_table *tbl, + u32 slotid, u32 seq_init, gfp_t gfp_mask) +{ + struct nfs4_slot *slot; + + slot = kzalloc(sizeof(*slot), gfp_mask); + if (slot) { + slot->table = tbl; + slot->slot_nr = slotid; + slot->seq_nr = seq_init; + } + return slot; +} + +static struct nfs4_slot *nfs4_find_or_create_slot(struct nfs4_slot_table *tbl, + u32 slotid, u32 seq_init, gfp_t gfp_mask) +{ + struct nfs4_slot **p, *slot; + + p = &tbl->slots; + for (;;) { + if (*p == NULL) { + *p = nfs4_new_slot(tbl, tbl->max_slots, + seq_init, gfp_mask); + if (*p == NULL) + break; + tbl->max_slots++; + } + slot = *p; + if (slot->slot_nr == slotid) + return slot; + p = &slot->next; + } + return ERR_PTR(-ENOMEM); +} + +/* + * nfs4_alloc_slot - efficiently look for a free slot + * + * nfs4_alloc_slot looks for an unset bit in the used_slots bitmap. + * If found, we mark the slot as used, update the highest_used_slotid, + * and respectively set up the sequence operation args. + * + * Note: must be called with under the slot_tbl_lock. + */ +struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl) +{ + struct nfs4_slot *ret = ERR_PTR(-EBUSY); + u32 slotid; + + dprintk("--> %s used_slots=%04lx highest_used=%u max_slots=%u\n", + __func__, tbl->used_slots[0], tbl->highest_used_slotid, + tbl->max_slotid + 1); + slotid = find_first_zero_bit(tbl->used_slots, tbl->max_slotid + 1); + if (slotid > tbl->max_slotid) + goto out; + ret = nfs4_find_or_create_slot(tbl, slotid, 1, GFP_NOWAIT); + if (IS_ERR(ret)) + goto out; + __set_bit(slotid, tbl->used_slots); + if (slotid > tbl->highest_used_slotid || + tbl->highest_used_slotid == NFS4_NO_SLOT) + tbl->highest_used_slotid = slotid; + ret->renewal_time = jiffies; + ret->generation = tbl->generation; + +out: + dprintk("<-- %s used_slots=%04lx highest_used=%d slotid=%d \n", + __func__, tbl->used_slots[0], tbl->highest_used_slotid, + !IS_ERR(ret) ? ret->slot_nr : -1); + return ret; +} + +static int nfs4_grow_slot_table(struct nfs4_slot_table *tbl, + u32 max_reqs, u32 ivalue) +{ + if (max_reqs <= tbl->max_slots) + return 0; + if (!IS_ERR(nfs4_find_or_create_slot(tbl, max_reqs - 1, ivalue, GFP_NOFS))) + return 0; + return -ENOMEM; +} + +static void nfs4_reset_slot_table(struct nfs4_slot_table *tbl, + u32 server_highest_slotid, + u32 ivalue) +{ + struct nfs4_slot **p; + + nfs4_shrink_slot_table(tbl, server_highest_slotid + 1); + p = &tbl->slots; + while (*p) { + (*p)->seq_nr = ivalue; + p = &(*p)->next; + } + tbl->highest_used_slotid = NFS4_NO_SLOT; + tbl->target_highest_slotid = server_highest_slotid; + tbl->server_highest_slotid = server_highest_slotid; + tbl->max_slotid = server_highest_slotid; +} + +/* + * (re)Initialise a slot table + */ +static int nfs4_realloc_slot_table(struct nfs4_slot_table *tbl, + u32 max_reqs, u32 ivalue) +{ + int ret; + + dprintk("--> %s: max_reqs=%u, tbl->max_slots %d\n", __func__, + max_reqs, tbl->max_slots); + + if (max_reqs > NFS4_MAX_SLOT_TABLE) + max_reqs = NFS4_MAX_SLOT_TABLE; + + ret = nfs4_grow_slot_table(tbl, max_reqs, ivalue); + if (ret) + goto out; + + spin_lock(&tbl->slot_tbl_lock); + nfs4_reset_slot_table(tbl, max_reqs - 1, ivalue); + spin_unlock(&tbl->slot_tbl_lock); + + dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__, + tbl, tbl->slots, tbl->max_slots); +out: + dprintk("<-- %s: return %d\n", __func__, ret); + return ret; +} + +/* Destroy the slot table */ +static void nfs4_destroy_slot_tables(struct nfs4_session *session) +{ + nfs4_shrink_slot_table(&session->fc_slot_table, 0); + nfs4_shrink_slot_table(&session->bc_slot_table, 0); +} + +/* Update the client's idea of target_highest_slotid */ +static void nfs41_set_target_slotid_locked(struct nfs4_slot_table *tbl, + u32 target_highest_slotid) +{ + unsigned int max_slotid, i; + + if (tbl->target_highest_slotid == target_highest_slotid) + return; + tbl->target_highest_slotid = target_highest_slotid; + tbl->generation++; + + max_slotid = min(NFS4_MAX_SLOT_TABLE - 1, tbl->target_highest_slotid); + for (i = tbl->max_slotid + 1; i <= max_slotid; i++) + rpc_wake_up_next(&tbl->slot_tbl_waitq); + tbl->max_slotid = max_slotid; +} + +void nfs41_set_target_slotid(struct nfs4_slot_table *tbl, + u32 target_highest_slotid) +{ + spin_lock(&tbl->slot_tbl_lock); + nfs41_set_target_slotid_locked(tbl, target_highest_slotid); + spin_unlock(&tbl->slot_tbl_lock); +} + +static void nfs41_set_server_slotid_locked(struct nfs4_slot_table *tbl, + u32 highest_slotid) +{ + if (tbl->server_highest_slotid == highest_slotid) + return; + if (tbl->highest_used_slotid > highest_slotid) + return; + /* Deallocate slots */ + nfs4_shrink_slot_table(tbl, highest_slotid + 1); + tbl->server_highest_slotid = highest_slotid; +} + +void nfs41_update_target_slotid(struct nfs4_slot_table *tbl, + struct nfs4_slot *slot, + struct nfs4_sequence_res *res) +{ + spin_lock(&tbl->slot_tbl_lock); + if (tbl->generation != slot->generation) + goto out; + nfs41_set_server_slotid_locked(tbl, res->sr_highest_slotid); + nfs41_set_target_slotid_locked(tbl, res->sr_target_highest_slotid); +out: + spin_unlock(&tbl->slot_tbl_lock); +} + +/* + * Initialize or reset the forechannel and backchannel tables + */ +int nfs4_setup_session_slot_tables(struct nfs4_session *ses) +{ + struct nfs4_slot_table *tbl; + int status; + + dprintk("--> %s\n", __func__); + /* Fore channel */ + tbl = &ses->fc_slot_table; + tbl->session = ses; + status = nfs4_realloc_slot_table(tbl, ses->fc_attrs.max_reqs, 1); + if (status) /* -ENOMEM */ + return status; + /* Back channel */ + tbl = &ses->bc_slot_table; + tbl->session = ses; + status = nfs4_realloc_slot_table(tbl, ses->bc_attrs.max_reqs, 0); + if (status && tbl->slots == NULL) + /* Fore and back channel share a connection so get + * both slot tables or neither */ + nfs4_destroy_slot_tables(ses); + return status; +} + +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_NOFS); + if (!session) + return NULL; + + tbl = &session->fc_slot_table; + tbl->highest_used_slotid = NFS4_NO_SLOT; + spin_lock_init(&tbl->slot_tbl_lock); + rpc_init_priority_wait_queue(&tbl->slot_tbl_waitq, "ForeChannel Slot table"); + init_completion(&tbl->complete); + + tbl = &session->bc_slot_table; + tbl->highest_used_slotid = NFS4_NO_SLOT; + spin_lock_init(&tbl->slot_tbl_lock); + rpc_init_wait_queue(&tbl->slot_tbl_waitq, "BackChannel Slot table"); + init_completion(&tbl->complete); + + session->session_state = 1<clp = clp; + return session; +} + +void nfs4_destroy_session(struct nfs4_session *session) +{ + struct rpc_xprt *xprt; + struct rpc_cred *cred; + + cred = nfs4_get_exchange_id_cred(session->clp); + nfs4_proc_destroy_session(session, cred); + if (cred) + put_rpccred(cred); + + rcu_read_lock(); + xprt = rcu_dereference(session->clp->cl_rpcclient->cl_xprt); + rcu_read_unlock(); + dprintk("%s Destroy backchannel for xprt %p\n", + __func__, xprt); + xprt_destroy_backchannel(xprt, NFS41_BC_MIN_CALLBACKS); + nfs4_destroy_slot_tables(session); + kfree(session); +} + +/* + * With sessions, the client is not marked ready until after a + * successful EXCHANGE_ID and CREATE_SESSION. + * + * Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate + * other versions of NFS can be tried. + */ +static int nfs41_check_session_ready(struct nfs_client *clp) +{ + int ret; + + if (clp->cl_cons_state == NFS_CS_SESSION_INITING) { + ret = nfs4_client_recover_expired_lease(clp); + if (ret) + return ret; + } + if (clp->cl_cons_state < NFS_CS_READY) + return -EPROTONOSUPPORT; + smp_rmb(); + return 0; +} + +int nfs4_init_session(struct nfs_server *server) +{ + struct nfs_client *clp = server->nfs_client; + struct nfs4_session *session; + unsigned int target_max_rqst_sz = NFS_MAX_FILE_IO_SIZE; + unsigned int target_max_resp_sz = NFS_MAX_FILE_IO_SIZE; + + if (!nfs4_has_session(clp)) + return 0; + + if (server->rsize != 0) + target_max_resp_sz = server->rsize; + target_max_resp_sz += nfs41_maxread_overhead; + + if (server->wsize != 0) + target_max_rqst_sz = server->wsize; + target_max_rqst_sz += nfs41_maxwrite_overhead; + + session = clp->cl_session; + spin_lock(&clp->cl_lock); + if (test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) { + /* Initialise targets and channel attributes */ + session->fc_target_max_rqst_sz = target_max_rqst_sz; + session->fc_attrs.max_rqst_sz = target_max_rqst_sz; + session->fc_target_max_resp_sz = target_max_resp_sz; + session->fc_attrs.max_resp_sz = target_max_resp_sz; + } else { + /* Just adjust the targets */ + if (target_max_rqst_sz > session->fc_target_max_rqst_sz) { + session->fc_target_max_rqst_sz = target_max_rqst_sz; + set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); + } + if (target_max_resp_sz > session->fc_target_max_resp_sz) { + session->fc_target_max_resp_sz = target_max_resp_sz; + set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); + } + } + spin_unlock(&clp->cl_lock); + + if (test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)) + nfs4_schedule_lease_recovery(clp); + + return nfs41_check_session_ready(clp); +} + +int nfs4_init_ds_session(struct nfs_client *clp, unsigned long lease_time) +{ + struct nfs4_session *session = clp->cl_session; + int ret; + + spin_lock(&clp->cl_lock); + if (test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) { + /* + * Do not set NFS_CS_CHECK_LEASE_TIME instead set the + * DS lease to be equal to the MDS lease. + */ + clp->cl_lease_time = lease_time; + clp->cl_last_renewal = jiffies; + } + spin_unlock(&clp->cl_lock); + + ret = nfs41_check_session_ready(clp); + if (ret) + return ret; + /* Test for the DS role */ + if (!is_ds_client(clp)) + return -ENODEV; + return 0; +} +EXPORT_SYMBOL_GPL(nfs4_init_ds_session); + + diff --git a/fs/nfs/nfs4session.h b/fs/nfs/nfs4session.h new file mode 100644 index 0000000..cb47b1e --- /dev/null +++ b/fs/nfs/nfs4session.h @@ -0,0 +1,35 @@ +/* + * fs/nfs/nfs4session.h + * + * Copyright (c) 2012 Trond Myklebust + * + */ +#ifndef __LINUX_FS_NFS_NFS4SESSION_H +#define __LINUX_FS_NFS_NFS4SESSION_H + +#if defined(CONFIG_NFS_V4_1) +extern struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl); +extern void nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot); + +extern void nfs41_set_target_slotid(struct nfs4_slot_table *tbl, + u32 target_highest_slotid); +extern void nfs41_update_target_slotid(struct nfs4_slot_table *tbl, + struct nfs4_slot *slot, + struct nfs4_sequence_res *res); + +extern int nfs4_setup_session_slot_tables(struct nfs4_session *ses); + +extern struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp); +extern void nfs4_destroy_session(struct nfs4_session *session); +extern int nfs4_init_session(struct nfs_server *server); +extern int nfs4_init_ds_session(struct nfs_client *, unsigned long); + +#else /* defined(CONFIG_NFS_V4_1) */ + +static inline int nfs4_init_session(struct nfs_server *server) +{ + return 0; +} + +#endif /* defined(CONFIG_NFS_V4_1) */ +#endif /* __LINUX_FS_NFS_NFS4SESSION_H */ -- cgit v0.10.2 From c34309a45ea491e5f0c0d0af49ccfa018ff35fc1 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 26 Nov 2012 14:33:03 -0500 Subject: NFS: Remove unused function slot_idx Signed-off-by: Trond Myklebust diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 3071550..e707c1b 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -225,11 +225,6 @@ struct nfs4_slot_table { struct completion complete; }; -static inline int slot_idx(struct nfs4_slot_table *tbl, struct nfs4_slot *sp) -{ - return sp - tbl->slots; -} - /* * Session related parameters */ -- cgit v0.10.2 From 76e697ba7e8d187f50e385d21a2b2f1709a62c14 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 26 Nov 2012 14:20:49 -0500 Subject: NFSv4.1: Move slot table and session struct definitions to nfs4session.h Clean up. Gather NFSv4.1 slot definitions in fs/nfs/nfs4session.h. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index f1027b0..4fa788c 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c @@ -40,6 +40,7 @@ #include #include "../pnfs.h" +#include "../nfs4session.h" #include "../internal.h" #include "blocklayout.h" diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index ea6a7b1..59461c9 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -16,6 +16,7 @@ #include "nfs4_fs.h" #include "callback.h" #include "internal.h" +#include "nfs4session.h" #define CB_OP_TAGLEN_MAXSZ (512) #define CB_OP_HDR_RES_MAXSZ (2 + CB_OP_TAGLEN_MAXSZ) diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 8965a99..9bdbfc3 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -18,27 +18,6 @@ struct nfs_string; */ #define NFS_MAX_READAHEAD (RPC_DEF_SLOT_TABLE - 1) -/* - * Determine if sessions are in use. - */ -static inline int nfs4_has_session(const struct nfs_client *clp) -{ -#ifdef CONFIG_NFS_V4_1 - if (clp->cl_session) - return 1; -#endif /* CONFIG_NFS_V4_1 */ - return 0; -} - -static inline int nfs4_has_persistent_session(const struct nfs_client *clp) -{ -#ifdef CONFIG_NFS_V4_1 - if (nfs4_has_session(clp)) - return (clp->cl_session->flags & SESSION4_PERSIST); -#endif /* CONFIG_NFS_V4_1 */ - return 0; -} - static inline void nfs_attr_check_mountpoint(struct super_block *parent, struct nfs_fattr *fattr) { if (!nfs_fsid_equal(&NFS_SB(parent)->fsid, &fattr->fsid)) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index cd3e309..322bd01 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -29,11 +29,6 @@ enum nfs4_client_state { NFS4CLNT_BIND_CONN_TO_SESSION, }; -enum nfs4_session_state { - NFS4_SESSION_INITING, - NFS4_SESSION_DRAINING, -}; - #define NFS4_RENEW_TIMEOUT 0x01 #define NFS4_RENEW_DELEGATION_CB 0x02 @@ -327,13 +322,6 @@ int nfs41_discover_server_trunking(struct nfs_client *clp, extern void nfs4_schedule_session_recovery(struct nfs4_session *, int); extern void nfs41_server_notify_target_slotid_update(struct nfs_client *clp); -extern void nfs4_session_drain_complete(struct nfs4_session *session, - struct nfs4_slot_table *tbl); - -static inline bool nfs4_session_draining(struct nfs4_session *session) -{ - return !!test_bit(NFS4_SESSION_DRAINING, &session->session_state); -} #else static inline void nfs4_schedule_session_recovery(struct nfs4_session *session, int err) { diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index bfb28fa..591a1a7 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c @@ -35,6 +35,7 @@ #include +#include "nfs4session.h" #include "internal.h" #include "delegation.h" #include "nfs4filelayout.h" diff --git a/fs/nfs/nfs4session.h b/fs/nfs/nfs4session.h index cb47b1e..e96323f 100644 --- a/fs/nfs/nfs4session.h +++ b/fs/nfs/nfs4session.h @@ -7,6 +7,68 @@ #ifndef __LINUX_FS_NFS_NFS4SESSION_H #define __LINUX_FS_NFS_NFS4SESSION_H +/* maximum number of slots to use */ +#define NFS4_DEF_SLOT_TABLE_SIZE (16U) +#define NFS4_MAX_SLOT_TABLE (256U) +#define NFS4_NO_SLOT ((u32)-1) + +#if IS_ENABLED(CONFIG_NFS_V4) + +/* Sessions slot seqid */ +struct nfs4_slot { + struct nfs4_slot_table *table; + struct nfs4_slot *next; + unsigned long generation; + unsigned long renewal_time; + u32 slot_nr; + u32 seq_nr; +}; + +/* Sessions */ +#define SLOT_TABLE_SZ DIV_ROUND_UP(NFS4_MAX_SLOT_TABLE, 8*sizeof(long)) +struct nfs4_slot_table { + struct nfs4_session *session; /* Parent session */ + struct nfs4_slot *slots; /* seqid per slot */ + unsigned long used_slots[SLOT_TABLE_SZ]; /* used/unused bitmap */ + spinlock_t slot_tbl_lock; + struct rpc_wait_queue slot_tbl_waitq; /* allocators may wait here */ + u32 max_slots; /* # slots in table */ + u32 max_slotid; /* Max allowed slotid value */ + u32 highest_used_slotid; /* sent to server on each SEQ. + * op for dynamic resizing */ + u32 target_highest_slotid; /* Server max_slot target */ + u32 server_highest_slotid; /* Server highest slotid */ + unsigned long generation; /* Generation counter for + target_highest_slotid */ + struct completion complete; +}; + +/* + * Session related parameters + */ +struct nfs4_session { + struct nfs4_sessionid sess_id; + u32 flags; + unsigned long session_state; + u32 hash_alg; + u32 ssv_len; + + /* The fore and back channel */ + struct nfs4_channel_attrs fc_attrs; + struct nfs4_slot_table fc_slot_table; + struct nfs4_channel_attrs bc_attrs; + struct nfs4_slot_table bc_slot_table; + struct nfs_client *clp; + /* Create session arguments */ + unsigned int fc_target_max_rqst_sz; + unsigned int fc_target_max_resp_sz; +}; + +enum nfs4_session_state { + NFS4_SESSION_INITING, + NFS4_SESSION_DRAINING, +}; + #if defined(CONFIG_NFS_V4_1) extern struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl); extern void nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot); @@ -24,6 +86,31 @@ extern void nfs4_destroy_session(struct nfs4_session *session); extern int nfs4_init_session(struct nfs_server *server); extern int nfs4_init_ds_session(struct nfs_client *, unsigned long); +extern void nfs4_session_drain_complete(struct nfs4_session *session, + struct nfs4_slot_table *tbl); + +static inline bool nfs4_session_draining(struct nfs4_session *session) +{ + return !!test_bit(NFS4_SESSION_DRAINING, &session->session_state); +} + +/* + * Determine if sessions are in use. + */ +static inline int nfs4_has_session(const struct nfs_client *clp) +{ + if (clp->cl_session) + return 1; + return 0; +} + +static inline int nfs4_has_persistent_session(const struct nfs_client *clp) +{ + if (nfs4_has_session(clp)) + return (clp->cl_session->flags & SESSION4_PERSIST); + return 0; +} + #else /* defined(CONFIG_NFS_V4_1) */ static inline int nfs4_init_session(struct nfs_server *server) @@ -31,5 +118,19 @@ static inline int nfs4_init_session(struct nfs_server *server) return 0; } +/* + * Determine if sessions are in use. + */ +static inline int nfs4_has_session(const struct nfs_client *clp) +{ + return 0; +} + +static inline int nfs4_has_persistent_session(const struct nfs_client *clp) +{ + return 0; +} + #endif /* defined(CONFIG_NFS_V4_1) */ +#endif /* IS_ENABLED(CONFIG_NFS_V4) */ #endif /* __LINUX_FS_NFS_NFS4SESSION_H */ diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 1077b96..1402283 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -57,6 +57,7 @@ #include "callback.h" #include "delegation.h" #include "internal.h" +#include "nfs4session.h" #include "pnfs.h" #include "netns.h" diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index a67040f..e786dc7 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -56,6 +56,7 @@ #include "nfs4_fs.h" #include "internal.h" +#include "nfs4session.h" #include "pnfs.h" #include "netns.h" diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 652d3f7..e12cea4 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -64,6 +64,7 @@ #include "iostat.h" #include "internal.h" #include "fscache.h" +#include "nfs4session.h" #include "pnfs.h" #include "nfs.h" diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index e707c1b..6c6ed15 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -198,53 +198,4 @@ struct nfs_server { #define NFS_CAP_POSIX_LOCK (1U << 14) #define NFS_CAP_UIDGID_NOMAP (1U << 15) - -/* maximum number of slots to use */ -#define NFS4_DEF_SLOT_TABLE_SIZE (16U) -#define NFS4_MAX_SLOT_TABLE (256U) -#define NFS4_NO_SLOT ((u32)-1) - -#if IS_ENABLED(CONFIG_NFS_V4) - -/* Sessions */ -#define SLOT_TABLE_SZ DIV_ROUND_UP(NFS4_MAX_SLOT_TABLE, 8*sizeof(long)) -struct nfs4_slot_table { - struct nfs4_session *session; /* Parent session */ - struct nfs4_slot *slots; /* seqid per slot */ - unsigned long used_slots[SLOT_TABLE_SZ]; /* used/unused bitmap */ - spinlock_t slot_tbl_lock; - struct rpc_wait_queue slot_tbl_waitq; /* allocators may wait here */ - u32 max_slots; /* # slots in table */ - u32 max_slotid; /* Max allowed slotid value */ - u32 highest_used_slotid; /* sent to server on each SEQ. - * op for dynamic resizing */ - u32 target_highest_slotid; /* Server max_slot target */ - u32 server_highest_slotid; /* Server highest slotid */ - unsigned long generation; /* Generation counter for - target_highest_slotid */ - struct completion complete; -}; - -/* - * Session related parameters - */ -struct nfs4_session { - struct nfs4_sessionid sess_id; - u32 flags; - unsigned long session_state; - u32 hash_alg; - u32 ssv_len; - - /* The fore and back channel */ - struct nfs4_channel_attrs fc_attrs; - struct nfs4_slot_table fc_slot_table; - struct nfs4_channel_attrs bc_attrs; - struct nfs4_slot_table bc_slot_table; - struct nfs_client *clp; - /* Create session arguments */ - unsigned int fc_target_max_rqst_sz; - unsigned int fc_target_max_resp_sz; -}; - -#endif /* CONFIG_NFS_V4 */ #endif diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 44d256f..2076149 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -185,16 +185,7 @@ struct nfs4_channel_attrs { u32 max_reqs; }; -/* nfs41 sessions slot seqid */ -struct nfs4_slot { - struct nfs4_slot_table *table; - struct nfs4_slot *next; - unsigned long generation; - unsigned long renewal_time; - u32 slot_nr; - u32 seq_nr; -}; - +struct nfs4_slot; struct nfs4_sequence_args { struct nfs4_slot *sa_slot; u8 sa_cache_this; -- cgit v0.10.2 From 0ca3f4825ac92a10aa8f6534f765c44f22778dd3 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 21 Nov 2012 22:34:45 -0500 Subject: NFSv4.1: Set the maximum slot table size to 1024 slots This means that we end up statically allocating 128 bytes for the bitmap on each slot table. For a server that supports 1MB write and read I/O sizes this means that we can completely fill the maximum 1GB TCP send/receive windows. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4session.h b/fs/nfs/nfs4session.h index e96323f..bdd14a6 100644 --- a/fs/nfs/nfs4session.h +++ b/fs/nfs/nfs4session.h @@ -9,7 +9,7 @@ /* maximum number of slots to use */ #define NFS4_DEF_SLOT_TABLE_SIZE (16U) -#define NFS4_MAX_SLOT_TABLE (256U) +#define NFS4_MAX_SLOT_TABLE (1024U) #define NFS4_NO_SLOT ((u32)-1) #if IS_ENABLED(CONFIG_NFS_V4) -- cgit v0.10.2 From c10e449827e6008ef5a4a71c0247c7eb73948e1b Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 26 Nov 2012 16:16:54 -0500 Subject: NFSv4.1: Ping server when our session table limits are too high If the server requests a lower target_highest_slotid, then ensure that we ping it with at least one RPC call containing an appropriate SEQUENCE op. This ensures that the server won't need to send a recall callback in order to shrink the slot table. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 322bd01..8fe155b 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -321,6 +321,7 @@ int nfs41_discover_server_trunking(struct nfs_client *clp, struct nfs_client **, struct rpc_cred *); extern void nfs4_schedule_session_recovery(struct nfs4_session *, int); extern void nfs41_server_notify_target_slotid_update(struct nfs_client *clp); +extern void nfs41_server_notify_highest_slotid_update(struct nfs_client *clp); #else static inline void nfs4_schedule_session_recovery(struct nfs4_session *session, int err) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a0c35ab..ecd4ed3 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -389,6 +389,7 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) { struct nfs4_session *session; struct nfs4_slot_table *tbl; + bool send_new_highest_used_slotid = false; if (!res->sr_slot) { /* just wake up the next guy waiting since @@ -400,12 +401,25 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) session = tbl->session; spin_lock(&tbl->slot_tbl_lock); + /* Be nice to the server: try to ensure that the last transmitted + * value for highest_user_slotid <= target_highest_slotid + */ + if (tbl->highest_used_slotid > tbl->target_highest_slotid) + send_new_highest_used_slotid = true; + nfs4_free_slot(tbl, res->sr_slot); - if (!nfs4_session_draining(session)) - rpc_wake_up_first(&tbl->slot_tbl_waitq, - nfs4_set_task_privileged, NULL); + + if (tbl->highest_used_slotid != NFS4_NO_SLOT) + send_new_highest_used_slotid = false; + if (!nfs4_session_draining(session)) { + if (rpc_wake_up_first(&tbl->slot_tbl_waitq, + nfs4_set_task_privileged, NULL) != NULL) + send_new_highest_used_slotid = false; + } spin_unlock(&tbl->slot_tbl_lock); res->sr_slot = NULL; + if (send_new_highest_used_slotid) + nfs41_server_notify_highest_slotid_update(session->clp); } static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 1402283..c137421 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1961,6 +1961,11 @@ void nfs41_server_notify_target_slotid_update(struct nfs_client *clp) nfs41_ping_server(clp); } +void nfs41_server_notify_highest_slotid_update(struct nfs_client *clp) +{ + nfs41_ping_server(clp); +} + static void nfs4_reset_all_state(struct nfs_client *clp) { if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) { -- cgit v0.10.2 From 6ba7db3420c0dbf3ede16f19a593e6a80edc043f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 22 Oct 2012 20:07:20 -0400 Subject: NFSv4.1: Use nfs41_setup_sequence where appropriate There is no point in using nfs4_setup_sequence or nfs4_sequence_done in pure NFSv4.1 functions. We already know that those have sessions... Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index ecd4ed3..39d2415 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -597,10 +597,11 @@ struct nfs41_call_sync_data { static void nfs41_call_sync_prepare(struct rpc_task *task, void *calldata) { struct nfs41_call_sync_data *data = calldata; + struct nfs4_session *session = nfs4_get_session(data->seq_server); dprintk("--> %s data->seq_server %p\n", __func__, data->seq_server); - if (nfs4_setup_sequence(data->seq_server, data->seq_args, + if (nfs41_setup_sequence(session, data->seq_args, data->seq_res, task)) return; rpc_call_start(task); @@ -6018,6 +6019,7 @@ nfs4_layoutget_prepare(struct rpc_task *task, void *calldata) { struct nfs4_layoutget *lgp = calldata; struct nfs_server *server = NFS_SERVER(lgp->args.inode); + struct nfs4_session *session = nfs4_get_session(server); dprintk("--> %s\n", __func__); /* Note the is a race here, where a CB_LAYOUTRECALL can come in @@ -6025,7 +6027,7 @@ nfs4_layoutget_prepare(struct rpc_task *task, void *calldata) * However, that is not so catastrophic, and there seems * to be no way to prevent it completely. */ - if (nfs4_setup_sequence(server, &lgp->args.seq_args, + if (nfs41_setup_sequence(session, &lgp->args.seq_args, &lgp->res.seq_res, task)) return; if (pnfs_choose_layoutget_stateid(&lgp->args.stateid, @@ -6047,7 +6049,7 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata) dprintk("--> %s\n", __func__); - if (!nfs4_sequence_done(task, &lgp->res.seq_res)) + if (!nfs41_sequence_done(task, &lgp->res.seq_res)) goto out; switch (task->tk_status) { @@ -6211,7 +6213,7 @@ static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata) dprintk("--> %s\n", __func__); - if (!nfs4_sequence_done(task, &lrp->res.seq_res)) + if (!nfs41_sequence_done(task, &lrp->res.seq_res)) return; server = NFS_SERVER(lrp->args.inode); @@ -6360,8 +6362,9 @@ static void nfs4_layoutcommit_prepare(struct rpc_task *task, void *calldata) { struct nfs4_layoutcommit_data *data = calldata; struct nfs_server *server = NFS_SERVER(data->args.inode); + struct nfs4_session *session = nfs4_get_session(server); - if (nfs4_setup_sequence(server, &data->args.seq_args, + if (nfs41_setup_sequence(session, &data->args.seq_args, &data->res.seq_res, task)) return; rpc_call_start(task); @@ -6373,7 +6376,7 @@ nfs4_layoutcommit_done(struct rpc_task *task, void *calldata) struct nfs4_layoutcommit_data *data = calldata; struct nfs_server *server = NFS_SERVER(data->args.inode); - if (!nfs4_sequence_done(task, &data->res.seq_res)) + if (!nfs41_sequence_done(task, &data->res.seq_res)) return; switch (task->tk_status) { /* Just ignore these failures */ -- cgit v0.10.2 From d9afbd1b0889e7da6742e9c67ccc7becc4161f65 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 22 Oct 2012 20:28:44 -0400 Subject: NFSv4.1: Simplify the sequence setup Nobody calls nfs4_setup_sequence or nfs41_setup_sequence without also calling rpc_call_start() on success. This commit therefore folds the rpc_call_start call into nfs41_setup_sequence(). Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 8fe155b..8022ade 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -273,6 +273,7 @@ static inline int nfs4_setup_sequence(const struct nfs_server *server, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, struct rpc_task *task) { + rpc_call_start(task); return 0; } diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index 591a1a7..1e42413 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c @@ -307,12 +307,10 @@ static void filelayout_read_prepare(struct rpc_task *task, void *data) } rdata->read_done_cb = filelayout_read_done_cb; - if (nfs41_setup_sequence(rdata->ds_clp->cl_session, - &rdata->args.seq_args, &rdata->res.seq_res, - task)) - return; - - rpc_call_start(task); + nfs41_setup_sequence(rdata->ds_clp->cl_session, + &rdata->args.seq_args, + &rdata->res.seq_res, + task); } static void filelayout_read_call_done(struct rpc_task *task, void *data) @@ -409,12 +407,10 @@ static void filelayout_write_prepare(struct rpc_task *task, void *data) rpc_exit(task, 0); return; } - if (nfs41_setup_sequence(wdata->ds_clp->cl_session, - &wdata->args.seq_args, &wdata->res.seq_res, - task)) - return; - - rpc_call_start(task); + nfs41_setup_sequence(wdata->ds_clp->cl_session, + &wdata->args.seq_args, + &wdata->res.seq_res, + task); } static void filelayout_write_call_done(struct rpc_task *task, void *data) @@ -450,12 +446,10 @@ static void filelayout_commit_prepare(struct rpc_task *task, void *data) { struct nfs_commit_data *wdata = data; - if (nfs41_setup_sequence(wdata->ds_clp->cl_session, - &wdata->args.seq_args, &wdata->res.seq_res, - task)) - return; - - rpc_call_start(task); + nfs41_setup_sequence(wdata->ds_clp->cl_session, + &wdata->args.seq_args, + &wdata->res.seq_res, + task); } static void filelayout_write_commit_done(struct rpc_task *task, void *data) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 39d2415..23b0c2f 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -513,7 +513,7 @@ int nfs41_setup_sequence(struct nfs4_session *session, dprintk("--> %s\n", __func__); /* slot already allocated? */ if (res->sr_slot != NULL) - return 0; + goto out_success; tbl = &session->fc_slot_table; @@ -563,6 +563,8 @@ int nfs41_setup_sequence(struct nfs4_session *session, * set to 1 if an rpc level failure occurs. */ res->sr_status = 1; +out_success: + rpc_call_start(task); return 0; } EXPORT_SYMBOL_GPL(nfs41_setup_sequence); @@ -575,8 +577,10 @@ int nfs4_setup_sequence(const struct nfs_server *server, struct nfs4_session *session = nfs4_get_session(server); int ret = 0; - if (session == NULL) + if (session == NULL) { + rpc_call_start(task); goto out; + } dprintk("--> %s clp %p session %p sr_slot %d\n", __func__, session->clp, session, res->sr_slot ? @@ -601,10 +605,7 @@ static void nfs41_call_sync_prepare(struct rpc_task *task, void *calldata) dprintk("--> %s data->seq_server %p\n", __func__, data->seq_server); - if (nfs41_setup_sequence(session, data->seq_args, - data->seq_res, task)) - return; - rpc_call_start(task); + nfs41_setup_sequence(session, data->seq_args, data->seq_res, task); } static void nfs41_call_priv_sync_prepare(struct rpc_task *task, void *calldata) @@ -1485,8 +1486,6 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) &data->o_res.seq_res, task) != 0) nfs_release_seqid(data->o_arg.seqid); - else - rpc_call_start(task); return; unlock_no_action: rcu_read_unlock(); @@ -2192,8 +2191,6 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data) &calldata->res.seq_res, task) != 0) nfs_release_seqid(calldata->arg.seqid); - else - rpc_call_start(task); out: dprintk("%s: done!\n", __func__); } @@ -2932,12 +2929,10 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) static void nfs4_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlinkdata *data) { - if (nfs4_setup_sequence(NFS_SERVER(data->dir), - &data->args.seq_args, - &data->res.seq_res, - task)) - return; - rpc_call_start(task); + nfs4_setup_sequence(NFS_SERVER(data->dir), + &data->args.seq_args, + &data->res.seq_res, + task); } static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir) @@ -2965,12 +2960,10 @@ static void nfs4_proc_rename_setup(struct rpc_message *msg, struct inode *dir) static void nfs4_proc_rename_rpc_prepare(struct rpc_task *task, struct nfs_renamedata *data) { - if (nfs4_setup_sequence(NFS_SERVER(data->old_dir), - &data->args.seq_args, - &data->res.seq_res, - task)) - return; - rpc_call_start(task); + nfs4_setup_sequence(NFS_SERVER(data->old_dir), + &data->args.seq_args, + &data->res.seq_res, + task); } static int nfs4_proc_rename_done(struct rpc_task *task, struct inode *old_dir, @@ -3459,12 +3452,10 @@ static void nfs4_proc_read_setup(struct nfs_read_data *data, struct rpc_message static void nfs4_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) { - if (nfs4_setup_sequence(NFS_SERVER(data->header->inode), - &data->args.seq_args, - &data->res.seq_res, - task)) - return; - rpc_call_start(task); + nfs4_setup_sequence(NFS_SERVER(data->header->inode), + &data->args.seq_args, + &data->res.seq_res, + task); } static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_write_data *data) @@ -3525,22 +3516,18 @@ static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_messag static void nfs4_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) { - if (nfs4_setup_sequence(NFS_SERVER(data->header->inode), - &data->args.seq_args, - &data->res.seq_res, - task)) - return; - rpc_call_start(task); + nfs4_setup_sequence(NFS_SERVER(data->header->inode), + &data->args.seq_args, + &data->res.seq_res, + task); } static void nfs4_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data) { - if (nfs4_setup_sequence(NFS_SERVER(data->inode), - &data->args.seq_args, - &data->res.seq_res, - task)) - return; - rpc_call_start(task); + nfs4_setup_sequence(NFS_SERVER(data->inode), + &data->args.seq_args, + &data->res.seq_res, + task); } static int nfs4_commit_done_cb(struct rpc_task *task, struct nfs_commit_data *data) @@ -4187,11 +4174,10 @@ static void nfs4_delegreturn_prepare(struct rpc_task *task, void *data) d_data = (struct nfs4_delegreturndata *)data; - if (nfs4_setup_sequence(d_data->res.server, - &d_data->args.seq_args, - &d_data->res.seq_res, task)) - return; - rpc_call_start(task); + nfs4_setup_sequence(d_data->res.server, + &d_data->args.seq_args, + &d_data->res.seq_res, + task); } #endif /* CONFIG_NFS_V4_1 */ @@ -4445,8 +4431,6 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data) &calldata->res.seq_res, task) != 0) nfs_release_seqid(calldata->arg.seqid); - else - rpc_call_start(task); } static const struct rpc_call_ops nfs4_locku_ops = { @@ -4601,10 +4585,8 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata) if (nfs4_setup_sequence(data->server, &data->arg.seq_args, &data->res.seq_res, - task) == 0) { - rpc_call_start(task); + task) == 0) return; - } nfs_release_seqid(data->arg.open_seqid); out_release_lock_seqid: nfs_release_seqid(data->arg.lock_seqid); @@ -5462,7 +5444,6 @@ struct nfs4_get_lease_time_data { static void nfs4_get_lease_time_prepare(struct rpc_task *task, void *calldata) { - int ret; struct nfs4_get_lease_time_data *data = (struct nfs4_get_lease_time_data *)calldata; @@ -5470,12 +5451,10 @@ static void nfs4_get_lease_time_prepare(struct rpc_task *task, rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); /* just setup sequence, do not trigger session recovery since we're invoked within one */ - ret = nfs41_setup_sequence(data->clp->cl_session, - &data->args->la_seq_args, - &data->res->lr_seq_res, task); - - if (ret != -EAGAIN) - rpc_call_start(task); + nfs41_setup_sequence(data->clp->cl_session, + &data->args->la_seq_args, + &data->res->lr_seq_res, + task); dprintk("<-- %s\n", __func__); } @@ -5809,9 +5788,7 @@ static void nfs41_sequence_prepare(struct rpc_task *task, void *data) args = task->tk_msg.rpc_argp; res = task->tk_msg.rpc_resp; - if (nfs41_setup_sequence(clp->cl_session, args, res, task)) - return; - rpc_call_start(task); + nfs41_setup_sequence(clp->cl_session, args, res, task); } static void nfs41_sequence_prepare_privileged(struct rpc_task *task, void *data) @@ -5914,12 +5891,10 @@ static void nfs4_reclaim_complete_prepare(struct rpc_task *task, void *data) struct nfs4_reclaim_complete_data *calldata = data; rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); - if (nfs41_setup_sequence(calldata->clp->cl_session, - &calldata->arg.seq_args, - &calldata->res.seq_res, task)) - return; - - rpc_call_start(task); + nfs41_setup_sequence(calldata->clp->cl_session, + &calldata->arg.seq_args, + &calldata->res.seq_res, + task); } static int nfs41_reclaim_complete_handle_errors(struct rpc_task *task, struct nfs_client *clp) @@ -6034,9 +6009,7 @@ nfs4_layoutget_prepare(struct rpc_task *task, void *calldata) NFS_I(lgp->args.inode)->layout, lgp->args.ctx->state)) { rpc_exit(task, NFS4_OK); - return; } - rpc_call_start(task); } static void nfs4_layoutget_done(struct rpc_task *task, void *calldata) @@ -6200,10 +6173,10 @@ nfs4_layoutreturn_prepare(struct rpc_task *task, void *calldata) struct nfs4_layoutreturn *lrp = calldata; dprintk("--> %s\n", __func__); - if (nfs41_setup_sequence(lrp->clp->cl_session, &lrp->args.seq_args, - &lrp->res.seq_res, task)) - return; - rpc_call_start(task); + nfs41_setup_sequence(lrp->clp->cl_session, + &lrp->args.seq_args, + &lrp->res.seq_res, + task); } static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata) @@ -6364,10 +6337,10 @@ static void nfs4_layoutcommit_prepare(struct rpc_task *task, void *calldata) struct nfs_server *server = NFS_SERVER(data->args.inode); struct nfs4_session *session = nfs4_get_session(server); - if (nfs41_setup_sequence(session, &data->args.seq_args, - &data->res.seq_res, task)) - return; - rpc_call_start(task); + nfs41_setup_sequence(session, + &data->args.seq_args, + &data->res.seq_res, + task); } static void -- cgit v0.10.2 From fd0c09537a8494e9dccf3856b90058e1f97f1d62 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 1 Nov 2012 14:43:38 -0400 Subject: NFSv4: Simplify the NFSv4/v4.1 synchronous call switch We shouldn't need to pass the 'cache_reply' parameter if we initialise the sequence_args/sequence_res in the caller. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 9bdbfc3..fb99447 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -451,18 +451,6 @@ extern struct nfs_client *nfs4_init_client(struct nfs_client *clp, const struct rpc_timeout *timeparms, const char *ip_addr, rpc_authflavor_t authflavour); -extern int _nfs4_call_sync(struct rpc_clnt *clnt, - struct nfs_server *server, - struct rpc_message *msg, - struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, - int cache_reply); -extern int _nfs4_call_sync_session(struct rpc_clnt *clnt, - struct nfs_server *server, - struct rpc_message *msg, - struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, - int cache_reply); extern int nfs40_walk_client_list(struct nfs_client *clp, struct nfs_client **result, struct rpc_cred *cred); diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 8022ade..4f0cdc1 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -39,8 +39,7 @@ struct nfs4_minor_version_ops { struct nfs_server *server, struct rpc_message *msg, struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, - int cache_reply); + struct nfs4_sequence_res *res); bool (*match_stateid)(const nfs4_stateid *, const nfs4_stateid *); int (*find_root_sec)(struct nfs_server *, struct nfs_fh *, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 23b0c2f..4aaaa3b 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -664,14 +664,13 @@ static int nfs4_call_sync_sequence(struct rpc_clnt *clnt, return ret; } +static int _nfs4_call_sync_session(struct rpc_clnt *clnt, struct nfs_server *server, struct rpc_message *msg, struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, - int cache_reply) + struct nfs4_sequence_res *res) { - nfs41_init_sequence(args, res, cache_reply); return nfs4_call_sync_sequence(clnt, server, msg, args, res, 0); } @@ -689,18 +688,17 @@ static int nfs4_sequence_done(struct rpc_task *task, } #endif /* CONFIG_NFS_V4_1 */ +static int _nfs4_call_sync(struct rpc_clnt *clnt, struct nfs_server *server, struct rpc_message *msg, struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, - int cache_reply) + struct nfs4_sequence_res *res) { - nfs41_init_sequence(args, res, cache_reply); return rpc_call_sync(clnt, msg, 0); } -static inline +static int nfs4_call_sync(struct rpc_clnt *clnt, struct nfs_server *server, struct rpc_message *msg, @@ -708,8 +706,9 @@ int nfs4_call_sync(struct rpc_clnt *clnt, struct nfs4_sequence_res *res, int cache_reply) { + nfs41_init_sequence(args, res, cache_reply); return server->nfs_client->cl_mvops->call_sync(clnt, server, msg, - args, res, cache_reply); + args, res); } static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo) -- cgit v0.10.2 From 7b939a3f44293516c4225f640e8c4b9200beeabc Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 1 Nov 2012 15:19:46 -0400 Subject: NFSv4.1: Clean up nfs41_setup_sequence Move all the sleep-and-exit cases into a single section of code. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 4aaaa3b..87525eb 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -523,18 +523,14 @@ int nfs41_setup_sequence(struct nfs4_session *session, if (test_bit(NFS4_SESSION_DRAINING, &session->session_state) && !rpc_task_has_priority(task, RPC_PRIORITY_PRIVILEGED)) { /* The state manager will wait until the slot table is empty */ - rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); - spin_unlock(&tbl->slot_tbl_lock); dprintk("%s session is draining\n", __func__); - return -EAGAIN; + goto out_sleep; } if (!rpc_queue_empty(&tbl->slot_tbl_waitq) && !rpc_task_has_priority(task, RPC_PRIORITY_PRIVILEGED)) { - rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); - spin_unlock(&tbl->slot_tbl_lock); dprintk("%s enforce FIFO order\n", __func__); - return -EAGAIN; + goto out_sleep; } slot = nfs4_alloc_slot(tbl); @@ -542,10 +538,8 @@ int nfs41_setup_sequence(struct nfs4_session *session, /* If out of memory, try again in 1/4 second */ if (slot == ERR_PTR(-ENOMEM)) task->tk_timeout = HZ >> 2; - rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); - spin_unlock(&tbl->slot_tbl_lock); dprintk("<-- %s: no free slots\n", __func__); - return -EAGAIN; + goto out_sleep; } spin_unlock(&tbl->slot_tbl_lock); @@ -566,6 +560,10 @@ int nfs41_setup_sequence(struct nfs4_session *session, out_success: rpc_call_start(task); return 0; +out_sleep: + rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); + spin_unlock(&tbl->slot_tbl_lock); + return -EAGAIN; } EXPORT_SYMBOL_GPL(nfs41_setup_sequence); -- cgit v0.10.2 From 275e7e20aa8599719729f8ef4c09c9bfc4895642 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 1 Nov 2012 17:07:07 -0400 Subject: NFSv4.1: Remove the 'FIFO' behaviour for nfs41_setup_sequence It is more important to preserve the task priority behaviour, which ensures that things like reclaim writes take precedence over background and kupdate writes. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 4f0cdc1..4635bf5 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -236,7 +236,6 @@ static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *ser return server->nfs_client->cl_session; } -extern bool nfs4_set_task_privileged(struct rpc_task *task, void *dummy); extern int nfs4_setup_sequence(const struct nfs_server *server, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, struct rpc_task *task); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 87525eb..4b1635c 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -379,12 +379,6 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp #if defined(CONFIG_NFS_V4_1) -bool nfs4_set_task_privileged(struct rpc_task *task, void *dummy) -{ - rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); - return true; -} - static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) { struct nfs4_session *session; @@ -412,8 +406,7 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) if (tbl->highest_used_slotid != NFS4_NO_SLOT) send_new_highest_used_slotid = false; if (!nfs4_session_draining(session)) { - if (rpc_wake_up_first(&tbl->slot_tbl_waitq, - nfs4_set_task_privileged, NULL) != NULL) + if (rpc_wake_up_next(&tbl->slot_tbl_waitq) != NULL) send_new_highest_used_slotid = false; } spin_unlock(&tbl->slot_tbl_lock); @@ -527,12 +520,6 @@ int nfs41_setup_sequence(struct nfs4_session *session, goto out_sleep; } - if (!rpc_queue_empty(&tbl->slot_tbl_waitq) && - !rpc_task_has_priority(task, RPC_PRIORITY_PRIVILEGED)) { - dprintk("%s enforce FIFO order\n", __func__); - goto out_sleep; - } - slot = nfs4_alloc_slot(tbl); if (IS_ERR(slot)) { /* If out of memory, try again in 1/4 second */ diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index c137421..7d73df5 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -263,9 +263,7 @@ static void nfs4_end_drain_session(struct nfs_client *clp) if (test_and_clear_bit(NFS4_SESSION_DRAINING, &ses->session_state)) { spin_lock(&tbl->slot_tbl_lock); for (i = 0; i <= tbl->max_slotid; i++) { - if (rpc_wake_up_first(&tbl->slot_tbl_waitq, - nfs4_set_task_privileged, - NULL) == NULL) + if (rpc_wake_up_next(&tbl->slot_tbl_waitq) == NULL) break; } spin_unlock(&tbl->slot_tbl_lock); -- cgit v0.10.2 From 8fe72bac8de784c4059b41a7dd6bb0151a3ae898 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 29 Oct 2012 19:02:20 -0400 Subject: NFSv4: Clean up handling of privileged operations Privileged rpc calls are those that are run by the state recovery thread, in cases where we're trying to recover the system after a server reboot or a network partition. In those cases, we want to fence off all other rpc calls (see nfs4_begin_drain_session()) so that they don't end up using stateids or clientids that are in the process of being recovered. Prior to this patch, we had to set up special callback functions in order to declare an rpc call as being privileged. By adding a new field to the sequence arguments, this patch simplifies things considerably, and allows us to declare the rpc call as privileged before it is run. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 4b1635c..38a709d 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -490,11 +490,17 @@ static void nfs41_init_sequence(struct nfs4_sequence_args *args, { args->sa_slot = NULL; args->sa_cache_this = 0; + args->sa_privileged = 0; if (cache_reply) args->sa_cache_this = 1; res->sr_slot = NULL; } +static void nfs4_set_sequence_privileged(struct nfs4_sequence_args *args) +{ + args->sa_privileged = 1; +} + int nfs41_setup_sequence(struct nfs4_session *session, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, @@ -514,7 +520,7 @@ int nfs41_setup_sequence(struct nfs4_session *session, spin_lock(&tbl->slot_tbl_lock); if (test_bit(NFS4_SESSION_DRAINING, &session->session_state) && - !rpc_task_has_priority(task, RPC_PRIORITY_PRIVILEGED)) { + !args->sa_privileged) { /* The state manager will wait until the slot table is empty */ dprintk("%s session is draining\n", __func__); goto out_sleep; @@ -548,6 +554,9 @@ out_success: rpc_call_start(task); return 0; out_sleep: + /* Privileged tasks are queued with top priority */ + if (args->sa_privileged) + rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); spin_unlock(&tbl->slot_tbl_lock); return -EAGAIN; @@ -593,12 +602,6 @@ static void nfs41_call_sync_prepare(struct rpc_task *task, void *calldata) nfs41_setup_sequence(session, data->seq_args, data->seq_res, task); } -static void nfs41_call_priv_sync_prepare(struct rpc_task *task, void *calldata) -{ - rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); - nfs41_call_sync_prepare(task, calldata); -} - static void nfs41_call_sync_done(struct rpc_task *task, void *calldata) { struct nfs41_call_sync_data *data = calldata; @@ -611,17 +614,11 @@ static const struct rpc_call_ops nfs41_call_sync_ops = { .rpc_call_done = nfs41_call_sync_done, }; -static const struct rpc_call_ops nfs41_call_priv_sync_ops = { - .rpc_call_prepare = nfs41_call_priv_sync_prepare, - .rpc_call_done = nfs41_call_sync_done, -}; - static int nfs4_call_sync_sequence(struct rpc_clnt *clnt, struct nfs_server *server, struct rpc_message *msg, struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, - int privileged) + struct nfs4_sequence_res *res) { int ret; struct rpc_task *task; @@ -637,8 +634,6 @@ static int nfs4_call_sync_sequence(struct rpc_clnt *clnt, .callback_data = &data }; - if (privileged) - task_setup.callback_ops = &nfs41_call_priv_sync_ops; task = rpc_run_task(&task_setup); if (IS_ERR(task)) ret = PTR_ERR(task); @@ -656,16 +651,21 @@ int _nfs4_call_sync_session(struct rpc_clnt *clnt, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res) { - return nfs4_call_sync_sequence(clnt, server, msg, args, res, 0); + return nfs4_call_sync_sequence(clnt, server, msg, args, res); } #else -static inline +static void nfs41_init_sequence(struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, int cache_reply) { } +static void nfs4_set_sequence_privileged(struct nfs4_sequence_args *args) +{ +} + + static int nfs4_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) { @@ -1475,13 +1475,6 @@ unlock_no_action: rcu_read_unlock(); out_no_action: task->tk_action = NULL; - -} - -static void nfs4_recover_open_prepare(struct rpc_task *task, void *calldata) -{ - rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); - nfs4_open_prepare(task, calldata); } static void nfs4_open_done(struct rpc_task *task, void *calldata) @@ -1542,12 +1535,6 @@ static const struct rpc_call_ops nfs4_open_ops = { .rpc_release = nfs4_open_release, }; -static const struct rpc_call_ops nfs4_recover_open_ops = { - .rpc_call_prepare = nfs4_recover_open_prepare, - .rpc_call_done = nfs4_open_done, - .rpc_release = nfs4_open_release, -}; - static int nfs4_run_open_task(struct nfs4_opendata *data, int isrecover) { struct inode *dir = data->dir->d_inode; @@ -1577,7 +1564,7 @@ static int nfs4_run_open_task(struct nfs4_opendata *data, int isrecover) data->rpc_status = 0; data->cancelled = 0; if (isrecover) - task_setup_data.callback_ops = &nfs4_recover_open_ops; + nfs4_set_sequence_privileged(&o_arg->seq_args); task = rpc_run_task(&task_setup_data); if (IS_ERR(task)) return PTR_ERR(task); @@ -4558,8 +4545,9 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata) return; /* Do we need to do an open_to_lock_owner? */ if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) { - if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0) + if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0) { goto out_release_lock_seqid; + } data->arg.open_stateid = &state->stateid; data->arg.new_lock_owner = 1; data->res.open_seqid = data->arg.open_seqid; @@ -4574,13 +4562,7 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata) nfs_release_seqid(data->arg.open_seqid); out_release_lock_seqid: nfs_release_seqid(data->arg.lock_seqid); - dprintk("%s: done!, ret = %d\n", __func__, task->tk_status); -} - -static void nfs4_recover_lock_prepare(struct rpc_task *task, void *calldata) -{ - rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); - nfs4_lock_prepare(task, calldata); + dprintk("%s: done!, ret = %d\n", __func__, data->rpc_status); } static void nfs4_lock_done(struct rpc_task *task, void *calldata) @@ -4635,12 +4617,6 @@ static const struct rpc_call_ops nfs4_lock_ops = { .rpc_release = nfs4_lock_release, }; -static const struct rpc_call_ops nfs4_recover_lock_ops = { - .rpc_call_prepare = nfs4_recover_lock_prepare, - .rpc_call_done = nfs4_lock_done, - .rpc_release = nfs4_lock_release, -}; - static void nfs4_handle_setlk_error(struct nfs_server *server, struct nfs4_lock_state *lsp, int new_lock_owner, int error) { switch (error) { @@ -4683,15 +4659,15 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f return -ENOMEM; if (IS_SETLKW(cmd)) data->arg.block = 1; - if (recovery_type > NFS_LOCK_NEW) { - if (recovery_type == NFS_LOCK_RECLAIM) - data->arg.reclaim = NFS_LOCK_RECLAIM; - task_setup_data.callback_ops = &nfs4_recover_lock_ops; - } nfs41_init_sequence(&data->arg.seq_args, &data->res.seq_res, 1); msg.rpc_argp = &data->arg; msg.rpc_resp = &data->res; task_setup_data.callback_data = data; + if (recovery_type > NFS_LOCK_NEW) { + if (recovery_type == NFS_LOCK_RECLAIM) + data->arg.reclaim = NFS_LOCK_RECLAIM; + nfs4_set_sequence_privileged(&data->arg.seq_args); + } task = rpc_run_task(&task_setup_data); if (IS_ERR(task)) return PTR_ERR(task); @@ -5432,7 +5408,6 @@ static void nfs4_get_lease_time_prepare(struct rpc_task *task, (struct nfs4_get_lease_time_data *)calldata; dprintk("--> %s\n", __func__); - rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); /* just setup sequence, do not trigger session recovery since we're invoked within one */ nfs41_setup_sequence(data->clp->cl_session, @@ -5500,6 +5475,7 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo) int status; nfs41_init_sequence(&args.la_seq_args, &res.lr_seq_res, 0); + nfs4_set_sequence_privileged(&args.la_seq_args); dprintk("--> %s\n", __func__); task = rpc_run_task(&task_setup); @@ -5775,26 +5751,15 @@ static void nfs41_sequence_prepare(struct rpc_task *task, void *data) nfs41_setup_sequence(clp->cl_session, args, res, task); } -static void nfs41_sequence_prepare_privileged(struct rpc_task *task, void *data) -{ - rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); - nfs41_sequence_prepare(task, data); -} - static const struct rpc_call_ops nfs41_sequence_ops = { .rpc_call_done = nfs41_sequence_call_done, .rpc_call_prepare = nfs41_sequence_prepare, .rpc_release = nfs41_sequence_release, }; -static const struct rpc_call_ops nfs41_sequence_privileged_ops = { - .rpc_call_done = nfs41_sequence_call_done, - .rpc_call_prepare = nfs41_sequence_prepare_privileged, - .rpc_release = nfs41_sequence_release, -}; - -static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred, - const struct rpc_call_ops *seq_ops) +static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, + struct rpc_cred *cred, + bool is_privileged) { struct nfs4_sequence_data *calldata; struct rpc_message msg = { @@ -5804,7 +5769,7 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_ struct rpc_task_setup task_setup_data = { .rpc_client = clp->cl_rpcclient, .rpc_message = &msg, - .callback_ops = seq_ops, + .callback_ops = &nfs41_sequence_ops, .flags = RPC_TASK_ASYNC | RPC_TASK_SOFT, }; @@ -5816,6 +5781,8 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_ return ERR_PTR(-ENOMEM); } nfs41_init_sequence(&calldata->args, &calldata->res, 0); + if (is_privileged) + nfs4_set_sequence_privileged(&calldata->args); msg.rpc_argp = &calldata->args; msg.rpc_resp = &calldata->res; calldata->clp = clp; @@ -5831,7 +5798,7 @@ static int nfs41_proc_async_sequence(struct nfs_client *clp, struct rpc_cred *cr if ((renew_flags & NFS4_RENEW_TIMEOUT) == 0) return 0; - task = _nfs41_proc_sequence(clp, cred, &nfs41_sequence_ops); + task = _nfs41_proc_sequence(clp, cred, false); if (IS_ERR(task)) ret = PTR_ERR(task); else @@ -5845,7 +5812,7 @@ static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred) struct rpc_task *task; int ret; - task = _nfs41_proc_sequence(clp, cred, &nfs41_sequence_privileged_ops); + task = _nfs41_proc_sequence(clp, cred, true); if (IS_ERR(task)) { ret = PTR_ERR(task); goto out; @@ -5874,7 +5841,6 @@ static void nfs4_reclaim_complete_prepare(struct rpc_task *task, void *data) { struct nfs4_reclaim_complete_data *calldata = data; - rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); nfs41_setup_sequence(calldata->clp->cl_session, &calldata->arg.seq_args, &calldata->res.seq_res, @@ -5955,6 +5921,7 @@ static int nfs41_proc_reclaim_complete(struct nfs_client *clp) calldata->arg.one_fs = 0; nfs41_init_sequence(&calldata->arg.seq_args, &calldata->res.seq_res, 0); + nfs4_set_sequence_privileged(&calldata->arg.seq_args); msg.rpc_argp = &calldata->arg; msg.rpc_resp = &calldata->res; task_setup_data.callback_data = calldata; @@ -6521,7 +6488,9 @@ static int _nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid) dprintk("NFS call test_stateid %p\n", stateid); nfs41_init_sequence(&args.seq_args, &res.seq_res, 0); - status = nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 1); + nfs4_set_sequence_privileged(&args.seq_args); + status = nfs4_call_sync_sequence(server->client, server, &msg, + &args.seq_args, &res.seq_res); if (status != NFS_OK) { dprintk("NFS reply test_stateid: failed, %d\n", status); return status; @@ -6568,8 +6537,9 @@ static int _nfs4_free_stateid(struct nfs_server *server, nfs4_stateid *stateid) dprintk("NFS call free_stateid %p\n", stateid); nfs41_init_sequence(&args.seq_args, &res.seq_res, 0); + nfs4_set_sequence_privileged(&args.seq_args); status = nfs4_call_sync_sequence(server->client, server, &msg, - &args.seq_args, &res.seq_res, 1); + &args.seq_args, &res.seq_res); dprintk("NFS reply free_stateid: %d\n", status); return status; } diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 2076149..baa673e 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -188,7 +188,8 @@ struct nfs4_channel_attrs { struct nfs4_slot; struct nfs4_sequence_args { struct nfs4_slot *sa_slot; - u8 sa_cache_this; + u8 sa_cache_this : 1, + sa_privileged : 1; }; struct nfs4_sequence_res { -- cgit v0.10.2 From 104287cd4ebb5484c654551c102c25c94227f717 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Mon, 12 Nov 2012 14:13:13 -0500 Subject: NFS: Remove _nfs_call_sync_session All it does is pass its arguments through to another function. Let's cut out the middleman... Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 38a709d..7f8b427 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -644,16 +644,6 @@ static int nfs4_call_sync_sequence(struct rpc_clnt *clnt, return ret; } -static -int _nfs4_call_sync_session(struct rpc_clnt *clnt, - struct nfs_server *server, - struct rpc_message *msg, - struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res) -{ - return nfs4_call_sync_sequence(clnt, server, msg, args, res); -} - #else static void nfs41_init_sequence(struct nfs4_sequence_args *args, @@ -6659,7 +6649,7 @@ static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = { #if defined(CONFIG_NFS_V4_1) static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = { .minor_version = 1, - .call_sync = _nfs4_call_sync_session, + .call_sync = nfs4_call_sync_sequence, .match_stateid = nfs41_match_stateid, .find_root_sec = nfs41_find_root_sec, .reboot_recovery_ops = &nfs41_reboot_recovery_ops, -- cgit v0.10.2 From 1e1093c7fd4951bb4272212c238d09cd7a22f5fc Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 1 Nov 2012 16:44:05 -0400 Subject: NFSv4.1: Don't mess with task priorities in nfs41_setup_sequence We want to preserve the rpc_task priority for things like writebacks, that may have differing levels of urgency. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 7f8b427..99d99a5 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -536,8 +536,6 @@ int nfs41_setup_sequence(struct nfs4_session *session, } spin_unlock(&tbl->slot_tbl_lock); - rpc_task_set_priority(task, RPC_PRIORITY_NORMAL); - args->sa_slot = slot; dprintk("<-- %s slotid=%d seqid=%d\n", __func__, @@ -556,8 +554,10 @@ out_success: out_sleep: /* Privileged tasks are queued with top priority */ if (args->sa_privileged) - rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); - rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); + rpc_sleep_on_priority(&tbl->slot_tbl_waitq, task, + NULL, RPC_PRIORITY_PRIVILEGED); + else + rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); spin_unlock(&tbl->slot_tbl_lock); return -EAGAIN; } diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 8529026..1aefc9f 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -379,6 +379,7 @@ void rpc_sleep_on_priority(struct rpc_wait_queue *q, struct rpc_task *task, __rpc_sleep_on_priority(q, task, action, priority - RPC_PRIORITY_LOW); spin_unlock_bh(&q->lock); } +EXPORT_SYMBOL_GPL(rpc_sleep_on_priority); /** * __rpc_do_wake_up_task - wake up a single rpc_task -- cgit v0.10.2 From 62ae082d883d167cdaa7895cf2972d85e178228a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 29 Nov 2012 17:10:01 -0500 Subject: NFSv4: Reorder the XDR structures to put sequence at the top, not bottom Pre-condition for optimising the slot allocation and reintroducing FIFO behaviour. Signed-off-by: Trond Myklebust diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index baa673e..a55abd4 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -205,8 +205,8 @@ struct nfs4_get_lease_time_args { }; struct nfs4_get_lease_time_res { - struct nfs_fsinfo *lr_fsinfo; struct nfs4_sequence_res lr_seq_res; + struct nfs_fsinfo *lr_fsinfo; }; #define PNFS_LAYOUT_MAXSIZE 4096 @@ -224,23 +224,23 @@ struct pnfs_layout_range { }; struct nfs4_layoutget_args { + struct nfs4_sequence_args seq_args; __u32 type; struct pnfs_layout_range range; __u64 minlength; __u32 maxcount; struct inode *inode; struct nfs_open_context *ctx; - struct nfs4_sequence_args seq_args; nfs4_stateid stateid; struct nfs4_layoutdriver_data layout; }; struct nfs4_layoutget_res { + struct nfs4_sequence_res seq_res; __u32 return_on_close; struct pnfs_layout_range range; __u32 type; nfs4_stateid stateid; - struct nfs4_sequence_res seq_res; struct nfs4_layoutdriver_data *layoutp; }; @@ -251,38 +251,38 @@ struct nfs4_layoutget { }; struct nfs4_getdevicelist_args { + struct nfs4_sequence_args seq_args; const struct nfs_fh *fh; u32 layoutclass; - struct nfs4_sequence_args seq_args; }; struct nfs4_getdevicelist_res { - struct pnfs_devicelist *devlist; struct nfs4_sequence_res seq_res; + struct pnfs_devicelist *devlist; }; struct nfs4_getdeviceinfo_args { - struct pnfs_device *pdev; struct nfs4_sequence_args seq_args; + struct pnfs_device *pdev; }; struct nfs4_getdeviceinfo_res { - struct pnfs_device *pdev; struct nfs4_sequence_res seq_res; + struct pnfs_device *pdev; }; struct nfs4_layoutcommit_args { + struct nfs4_sequence_args seq_args; nfs4_stateid stateid; __u64 lastbytewritten; struct inode *inode; const u32 *bitmask; - struct nfs4_sequence_args seq_args; }; struct nfs4_layoutcommit_res { + struct nfs4_sequence_res seq_res; struct nfs_fattr *fattr; const struct nfs_server *server; - struct nfs4_sequence_res seq_res; int status; }; @@ -296,11 +296,11 @@ struct nfs4_layoutcommit_data { }; struct nfs4_layoutreturn_args { + struct nfs4_sequence_args seq_args; struct pnfs_layout_hdr *layout; struct inode *inode; nfs4_stateid stateid; __u32 layout_type; - struct nfs4_sequence_args seq_args; }; struct nfs4_layoutreturn_res { @@ -326,6 +326,7 @@ struct stateowner_id { * Arguments to the open call. */ struct nfs_openargs { + struct nfs4_sequence_args seq_args; const struct nfs_fh * fh; struct nfs_seqid * seqid; int open_flags; @@ -346,10 +347,10 @@ struct nfs_openargs { const u32 * bitmask; const u32 * open_bitmap; __u32 claim; - struct nfs4_sequence_args seq_args; }; struct nfs_openres { + struct nfs4_sequence_res seq_res; nfs4_stateid stateid; struct nfs_fh fh; struct nfs4_change_info cinfo; @@ -364,7 +365,6 @@ struct nfs_openres { __u32 attrset[NFS4_BITMAP_SIZE]; struct nfs4_string *owner; struct nfs4_string *group_owner; - struct nfs4_sequence_res seq_res; __u32 access_request; __u32 access_supported; __u32 access_result; @@ -388,20 +388,20 @@ struct nfs_open_confirmres { * Arguments to the close call. */ struct nfs_closeargs { + struct nfs4_sequence_args seq_args; struct nfs_fh * fh; nfs4_stateid * stateid; struct nfs_seqid * seqid; fmode_t fmode; const u32 * bitmask; - struct nfs4_sequence_args seq_args; }; struct nfs_closeres { + struct nfs4_sequence_res seq_res; nfs4_stateid stateid; struct nfs_fattr * fattr; struct nfs_seqid * seqid; const struct nfs_server *server; - struct nfs4_sequence_res seq_res; }; /* * * Arguments to the lock,lockt, and locku call. @@ -413,6 +413,7 @@ struct nfs_lowner { }; struct nfs_lock_args { + struct nfs4_sequence_args seq_args; struct nfs_fh * fh; struct file_lock * fl; struct nfs_seqid * lock_seqid; @@ -423,40 +424,39 @@ struct nfs_lock_args { unsigned char block : 1; unsigned char reclaim : 1; unsigned char new_lock_owner : 1; - struct nfs4_sequence_args seq_args; }; struct nfs_lock_res { + struct nfs4_sequence_res seq_res; nfs4_stateid stateid; struct nfs_seqid * lock_seqid; struct nfs_seqid * open_seqid; - struct nfs4_sequence_res seq_res; }; struct nfs_locku_args { + struct nfs4_sequence_args seq_args; struct nfs_fh * fh; struct file_lock * fl; struct nfs_seqid * seqid; nfs4_stateid * stateid; - struct nfs4_sequence_args seq_args; }; struct nfs_locku_res { + struct nfs4_sequence_res seq_res; nfs4_stateid stateid; struct nfs_seqid * seqid; - struct nfs4_sequence_res seq_res; }; struct nfs_lockt_args { + struct nfs4_sequence_args seq_args; struct nfs_fh * fh; struct file_lock * fl; struct nfs_lowner lock_owner; - struct nfs4_sequence_args seq_args; }; struct nfs_lockt_res { - struct file_lock * denied; /* LOCK, LOCKT failed */ struct nfs4_sequence_res seq_res; + struct file_lock * denied; /* LOCK, LOCKT failed */ }; struct nfs_release_lockowner_args { @@ -464,22 +464,23 @@ struct nfs_release_lockowner_args { }; struct nfs4_delegreturnargs { + struct nfs4_sequence_args seq_args; const struct nfs_fh *fhandle; const nfs4_stateid *stateid; const u32 * bitmask; - struct nfs4_sequence_args seq_args; }; struct nfs4_delegreturnres { + struct nfs4_sequence_res seq_res; struct nfs_fattr * fattr; const struct nfs_server *server; - struct nfs4_sequence_res seq_res; }; /* * Arguments to the read call. */ struct nfs_readargs { + struct nfs4_sequence_args seq_args; struct nfs_fh * fh; struct nfs_open_context *context; struct nfs_lock_context *lock_context; @@ -487,20 +488,20 @@ struct nfs_readargs { __u32 count; unsigned int pgbase; struct page ** pages; - struct nfs4_sequence_args seq_args; }; struct nfs_readres { + struct nfs4_sequence_res seq_res; struct nfs_fattr * fattr; __u32 count; int eof; - struct nfs4_sequence_res seq_res; }; /* * Arguments to the write call. */ struct nfs_writeargs { + struct nfs4_sequence_args seq_args; struct nfs_fh * fh; struct nfs_open_context *context; struct nfs_lock_context *lock_context; @@ -510,7 +511,6 @@ struct nfs_writeargs { unsigned int pgbase; struct page ** pages; const u32 * bitmask; - struct nfs4_sequence_args seq_args; }; struct nfs_write_verifier { @@ -523,65 +523,65 @@ struct nfs_writeverf { }; struct nfs_writeres { + struct nfs4_sequence_res seq_res; struct nfs_fattr * fattr; struct nfs_writeverf * verf; __u32 count; const struct nfs_server *server; - struct nfs4_sequence_res seq_res; }; /* * Arguments to the commit call. */ struct nfs_commitargs { + struct nfs4_sequence_args seq_args; struct nfs_fh *fh; __u64 offset; __u32 count; const u32 *bitmask; - struct nfs4_sequence_args seq_args; }; struct nfs_commitres { + struct nfs4_sequence_res seq_res; struct nfs_fattr *fattr; struct nfs_writeverf *verf; const struct nfs_server *server; - struct nfs4_sequence_res seq_res; }; /* * Common arguments to the unlink call */ struct nfs_removeargs { + struct nfs4_sequence_args seq_args; const struct nfs_fh *fh; struct qstr name; - struct nfs4_sequence_args seq_args; }; struct nfs_removeres { + struct nfs4_sequence_res seq_res; const struct nfs_server *server; struct nfs_fattr *dir_attr; struct nfs4_change_info cinfo; - struct nfs4_sequence_res seq_res; }; /* * Common arguments to the rename call */ struct nfs_renameargs { + struct nfs4_sequence_args seq_args; const struct nfs_fh *old_dir; const struct nfs_fh *new_dir; const struct qstr *old_name; const struct qstr *new_name; - struct nfs4_sequence_args seq_args; }; struct nfs_renameres { + struct nfs4_sequence_res seq_res; const struct nfs_server *server; struct nfs4_change_info old_cinfo; struct nfs_fattr *old_fattr; struct nfs4_change_info new_cinfo; struct nfs_fattr *new_fattr; - struct nfs4_sequence_res seq_res; }; /* @@ -622,20 +622,20 @@ struct nfs_createargs { }; struct nfs_setattrargs { + struct nfs4_sequence_args seq_args; struct nfs_fh * fh; nfs4_stateid stateid; struct iattr * iap; const struct nfs_server * server; /* Needed for name mapping */ const u32 * bitmask; - struct nfs4_sequence_args seq_args; }; struct nfs_setaclargs { + struct nfs4_sequence_args seq_args; struct nfs_fh * fh; size_t acl_len; unsigned int acl_pgbase; struct page ** acl_pages; - struct nfs4_sequence_args seq_args; }; struct nfs_setaclres { @@ -643,27 +643,27 @@ struct nfs_setaclres { }; struct nfs_getaclargs { + struct nfs4_sequence_args seq_args; struct nfs_fh * fh; size_t acl_len; unsigned int acl_pgbase; struct page ** acl_pages; - struct nfs4_sequence_args seq_args; }; /* getxattr ACL interface flags */ #define NFS4_ACL_TRUNC 0x0001 /* ACL was truncated */ struct nfs_getaclres { + struct nfs4_sequence_res seq_res; size_t acl_len; size_t acl_data_offset; int acl_flags; struct page * acl_scratch; - struct nfs4_sequence_res seq_res; }; struct nfs_setattrres { + struct nfs4_sequence_res seq_res; struct nfs_fattr * fattr; const struct nfs_server * server; - struct nfs4_sequence_res seq_res; }; struct nfs_linkargs { @@ -828,21 +828,22 @@ struct nfs3_getaclres { typedef u64 clientid4; struct nfs4_accessargs { + struct nfs4_sequence_args seq_args; const struct nfs_fh * fh; const u32 * bitmask; u32 access; - struct nfs4_sequence_args seq_args; }; struct nfs4_accessres { + struct nfs4_sequence_res seq_res; const struct nfs_server * server; struct nfs_fattr * fattr; u32 supported; u32 access; - struct nfs4_sequence_res seq_res; }; struct nfs4_create_arg { + struct nfs4_sequence_args seq_args; u32 ftype; union { struct { @@ -859,88 +860,88 @@ struct nfs4_create_arg { const struct iattr * attrs; const struct nfs_fh * dir_fh; const u32 * bitmask; - struct nfs4_sequence_args seq_args; }; struct nfs4_create_res { + struct nfs4_sequence_res seq_res; const struct nfs_server * server; struct nfs_fh * fh; struct nfs_fattr * fattr; struct nfs4_change_info dir_cinfo; - struct nfs4_sequence_res seq_res; }; struct nfs4_fsinfo_arg { + struct nfs4_sequence_args seq_args; const struct nfs_fh * fh; const u32 * bitmask; - struct nfs4_sequence_args seq_args; }; struct nfs4_fsinfo_res { - struct nfs_fsinfo *fsinfo; struct nfs4_sequence_res seq_res; + struct nfs_fsinfo *fsinfo; }; struct nfs4_getattr_arg { + struct nfs4_sequence_args seq_args; const struct nfs_fh * fh; const u32 * bitmask; - struct nfs4_sequence_args seq_args; }; struct nfs4_getattr_res { + struct nfs4_sequence_res seq_res; const struct nfs_server * server; struct nfs_fattr * fattr; - struct nfs4_sequence_res seq_res; }; struct nfs4_link_arg { + struct nfs4_sequence_args seq_args; const struct nfs_fh * fh; const struct nfs_fh * dir_fh; const struct qstr * name; const u32 * bitmask; - struct nfs4_sequence_args seq_args; }; struct nfs4_link_res { + struct nfs4_sequence_res seq_res; const struct nfs_server * server; struct nfs_fattr * fattr; struct nfs4_change_info cinfo; struct nfs_fattr * dir_attr; - struct nfs4_sequence_res seq_res; }; struct nfs4_lookup_arg { + struct nfs4_sequence_args seq_args; const struct nfs_fh * dir_fh; const struct qstr * name; const u32 * bitmask; - struct nfs4_sequence_args seq_args; }; struct nfs4_lookup_res { + struct nfs4_sequence_res seq_res; const struct nfs_server * server; struct nfs_fattr * fattr; struct nfs_fh * fh; - struct nfs4_sequence_res seq_res; }; struct nfs4_lookup_root_arg { - const u32 * bitmask; struct nfs4_sequence_args seq_args; + const u32 * bitmask; }; struct nfs4_pathconf_arg { + struct nfs4_sequence_args seq_args; const struct nfs_fh * fh; const u32 * bitmask; - struct nfs4_sequence_args seq_args; }; struct nfs4_pathconf_res { - struct nfs_pathconf *pathconf; struct nfs4_sequence_res seq_res; + struct nfs_pathconf *pathconf; }; struct nfs4_readdir_arg { + struct nfs4_sequence_args seq_args; const struct nfs_fh * fh; u64 cookie; nfs4_verifier verifier; @@ -949,21 +950,20 @@ struct nfs4_readdir_arg { unsigned int pgbase; /* zero-copy data */ const u32 * bitmask; int plus; - struct nfs4_sequence_args seq_args; }; struct nfs4_readdir_res { + struct nfs4_sequence_res seq_res; nfs4_verifier verifier; unsigned int pgbase; - struct nfs4_sequence_res seq_res; }; struct nfs4_readlink { + struct nfs4_sequence_args seq_args; const struct nfs_fh * fh; unsigned int pgbase; unsigned int pglen; /* zero-copy data */ struct page ** pages; /* zero-copy data */ - struct nfs4_sequence_args seq_args; }; struct nfs4_readlink_res { @@ -989,28 +989,28 @@ struct nfs4_setclientid_res { }; struct nfs4_statfs_arg { + struct nfs4_sequence_args seq_args; const struct nfs_fh * fh; const u32 * bitmask; - struct nfs4_sequence_args seq_args; }; struct nfs4_statfs_res { - struct nfs_fsstat *fsstat; struct nfs4_sequence_res seq_res; + struct nfs_fsstat *fsstat; }; struct nfs4_server_caps_arg { - struct nfs_fh *fhandle; struct nfs4_sequence_args seq_args; + struct nfs_fh *fhandle; }; struct nfs4_server_caps_res { + struct nfs4_sequence_res seq_res; u32 attr_bitmask[3]; u32 acl_bitmask; u32 has_links; u32 has_symlinks; u32 fh_expire_type; - struct nfs4_sequence_res seq_res; }; #define NFS4_PATHNAME_MAXCOMPONENTS 512 @@ -1036,16 +1036,16 @@ struct nfs4_fs_locations { }; struct nfs4_fs_locations_arg { + struct nfs4_sequence_args seq_args; const struct nfs_fh *dir_fh; const struct qstr *name; struct page *page; const u32 *bitmask; - struct nfs4_sequence_args seq_args; }; struct nfs4_fs_locations_res { - struct nfs4_fs_locations *fs_locations; struct nfs4_sequence_res seq_res; + struct nfs4_fs_locations *fs_locations; }; struct nfs4_secinfo_oid { @@ -1070,14 +1070,14 @@ struct nfs4_secinfo_flavors { }; struct nfs4_secinfo_arg { + struct nfs4_sequence_args seq_args; const struct nfs_fh *dir_fh; const struct qstr *name; - struct nfs4_sequence_args seq_args; }; struct nfs4_secinfo_res { - struct nfs4_secinfo_flavors *flavors; struct nfs4_sequence_res seq_res; + struct nfs4_secinfo_flavors *flavors; }; #endif /* CONFIG_NFS_V4 */ @@ -1157,9 +1157,9 @@ struct nfs41_create_session_res { }; struct nfs41_reclaim_complete_args { + struct nfs4_sequence_args seq_args; /* In the future extend to include curr_fh for use with migration */ unsigned char one_fs:1; - struct nfs4_sequence_args seq_args; }; struct nfs41_reclaim_complete_res { @@ -1169,28 +1169,28 @@ struct nfs41_reclaim_complete_res { #define SECINFO_STYLE_CURRENT_FH 0 #define SECINFO_STYLE_PARENT 1 struct nfs41_secinfo_no_name_args { - int style; struct nfs4_sequence_args seq_args; + int style; }; struct nfs41_test_stateid_args { - nfs4_stateid *stateid; struct nfs4_sequence_args seq_args; + nfs4_stateid *stateid; }; struct nfs41_test_stateid_res { - unsigned int status; struct nfs4_sequence_res seq_res; + unsigned int status; }; struct nfs41_free_stateid_args { - nfs4_stateid *stateid; struct nfs4_sequence_args seq_args; + nfs4_stateid *stateid; }; struct nfs41_free_stateid_res { - unsigned int status; struct nfs4_sequence_res seq_res; + unsigned int status; }; #else -- cgit v0.10.2 From b75ad4cda5a6cd3431b1c65c2739c5ebd2c4b9da Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 29 Nov 2012 17:27:47 -0500 Subject: NFSv4.1: Ensure smooth handover of slots from one task to the next waiting Currently, we see a lot of bouncing for the value of highest_used_slotid due to the fact that slots are getting freed, instead of getting instantly transmitted to the next waiting task. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 99d99a5..9922335 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -401,14 +401,15 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) if (tbl->highest_used_slotid > tbl->target_highest_slotid) send_new_highest_used_slotid = true; + if (nfs41_wake_and_assign_slot(tbl, res->sr_slot)) { + send_new_highest_used_slotid = false; + goto out_unlock; + } nfs4_free_slot(tbl, res->sr_slot); if (tbl->highest_used_slotid != NFS4_NO_SLOT) send_new_highest_used_slotid = false; - if (!nfs4_session_draining(session)) { - if (rpc_wake_up_next(&tbl->slot_tbl_waitq) != NULL) - send_new_highest_used_slotid = false; - } +out_unlock: spin_unlock(&tbl->slot_tbl_lock); res->sr_slot = NULL; if (send_new_highest_used_slotid) @@ -1465,6 +1466,7 @@ unlock_no_action: rcu_read_unlock(); out_no_action: task->tk_action = NULL; + nfs4_sequence_done(task, &data->o_res.seq_res); } static void nfs4_open_done(struct rpc_task *task, void *calldata) @@ -2135,6 +2137,7 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data) if (!call_close) { /* Note: exit _without_ calling nfs4_close_done */ task->tk_action = NULL; + nfs4_sequence_done(task, &calldata->res.seq_res); goto out; } @@ -4384,6 +4387,7 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data) if (test_bit(NFS_LOCK_INITIALIZED, &calldata->lsp->ls_flags) == 0) { /* Note: exit _without_ running nfs4_locku_done */ task->tk_action = NULL; + nfs4_sequence_done(task, &calldata->res.seq_res); return; } calldata->timestamp = jiffies; diff --git a/fs/nfs/nfs4session.c b/fs/nfs/nfs4session.c index 7011702..066cfa1 100644 --- a/fs/nfs/nfs4session.c +++ b/fs/nfs/nfs4session.c @@ -217,11 +217,65 @@ static void nfs4_destroy_slot_tables(struct nfs4_session *session) nfs4_shrink_slot_table(&session->bc_slot_table, 0); } +static bool nfs41_assign_slot(struct rpc_task *task, void *pslot) +{ + struct nfs4_sequence_args *args = task->tk_msg.rpc_argp; + struct nfs4_sequence_res *res = task->tk_msg.rpc_resp; + struct nfs4_slot *slot = pslot; + struct nfs4_slot_table *tbl = slot->table; + + if (nfs4_session_draining(tbl->session) && !args->sa_privileged) + return false; + slot->renewal_time = jiffies; + slot->generation = tbl->generation; + args->sa_slot = slot; + res->sr_slot = slot; + res->sr_status_flags = 0; + res->sr_status = 1; + return true; +} + +static bool __nfs41_wake_and_assign_slot(struct nfs4_slot_table *tbl, + struct nfs4_slot *slot) +{ + if (rpc_wake_up_first(&tbl->slot_tbl_waitq, nfs41_assign_slot, slot)) + return true; + return false; +} + +bool nfs41_wake_and_assign_slot(struct nfs4_slot_table *tbl, + struct nfs4_slot *slot) +{ + if (slot->slot_nr > tbl->max_slotid) + return false; + return __nfs41_wake_and_assign_slot(tbl, slot); +} + +static bool nfs41_try_wake_next_slot_table_entry(struct nfs4_slot_table *tbl) +{ + struct nfs4_slot *slot = nfs4_alloc_slot(tbl); + if (!IS_ERR(slot)) { + bool ret = __nfs41_wake_and_assign_slot(tbl, slot); + if (ret) + return ret; + nfs4_free_slot(tbl, slot); + } + return false; +} + +void nfs41_wake_slot_table(struct nfs4_slot_table *tbl) +{ + for (;;) { + if (!nfs41_try_wake_next_slot_table_entry(tbl)) + break; + } +} + /* Update the client's idea of target_highest_slotid */ static void nfs41_set_target_slotid_locked(struct nfs4_slot_table *tbl, u32 target_highest_slotid) { - unsigned int max_slotid, i; + unsigned int max_slotid; if (tbl->target_highest_slotid == target_highest_slotid) return; @@ -229,9 +283,8 @@ static void nfs41_set_target_slotid_locked(struct nfs4_slot_table *tbl, tbl->generation++; max_slotid = min(NFS4_MAX_SLOT_TABLE - 1, tbl->target_highest_slotid); - for (i = tbl->max_slotid + 1; i <= max_slotid; i++) - rpc_wake_up_next(&tbl->slot_tbl_waitq); tbl->max_slotid = max_slotid; + nfs41_wake_slot_table(tbl); } void nfs41_set_target_slotid(struct nfs4_slot_table *tbl, diff --git a/fs/nfs/nfs4session.h b/fs/nfs/nfs4session.h index bdd14a6..7db7393 100644 --- a/fs/nfs/nfs4session.h +++ b/fs/nfs/nfs4session.h @@ -94,6 +94,10 @@ static inline bool nfs4_session_draining(struct nfs4_session *session) return !!test_bit(NFS4_SESSION_DRAINING, &session->session_state); } +bool nfs41_wake_and_assign_slot(struct nfs4_slot_table *tbl, + struct nfs4_slot *slot); +void nfs41_wake_slot_table(struct nfs4_slot_table *tbl); + /* * Determine if sessions are in use. */ diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 7d73df5..78e90a8 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -255,17 +255,13 @@ static void nfs4_end_drain_session(struct nfs_client *clp) { struct nfs4_session *ses = clp->cl_session; struct nfs4_slot_table *tbl; - unsigned int i; if (ses == NULL) return; tbl = &ses->fc_slot_table; if (test_and_clear_bit(NFS4_SESSION_DRAINING, &ses->session_state)) { spin_lock(&tbl->slot_tbl_lock); - for (i = 0; i <= tbl->max_slotid; i++) { - if (rpc_wake_up_next(&tbl->slot_tbl_waitq) == NULL) - break; - } + nfs41_wake_slot_table(tbl); spin_unlock(&tbl->slot_tbl_lock); } } -- cgit v0.10.2 From c05eecf636101dd4347b2d8fa457626bf0088e0a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 30 Nov 2012 23:59:29 -0500 Subject: SUNRPC: Don't allow low priority tasks to pre-empt higher priority ones Currently, the priority queues attempt to be 'fair' to lower priority tasks by scheduling them after a certain number of higher priority tasks have run. The problem is that both the transport send queue and the NFSv4.1 session slot queue have strong ordering requirements. This patch therefore removes the fairness code in favour of strong ordering of task priorities. Signed-off-by: Trond Myklebust diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index dc0c3cc..b64f8eb 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -192,7 +192,6 @@ struct rpc_wait_queue { pid_t owner; /* process id of last task serviced */ unsigned char maxpriority; /* maximum priority (0 if queue is not a priority queue) */ unsigned char priority; /* current priority */ - unsigned char count; /* # task groups remaining serviced so far */ unsigned char nr; /* # tasks remaining for cookie */ unsigned short qlen; /* total # tasks waiting in queue */ struct rpc_timer timer_list; diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 1aefc9f..d17a704 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -98,6 +98,23 @@ __rpc_add_timer(struct rpc_wait_queue *queue, struct rpc_task *task) list_add(&task->u.tk_wait.timer_list, &queue->timer_list.list); } +static void rpc_set_waitqueue_priority(struct rpc_wait_queue *queue, int priority) +{ + queue->priority = priority; +} + +static void rpc_set_waitqueue_owner(struct rpc_wait_queue *queue, pid_t pid) +{ + queue->owner = pid; + queue->nr = RPC_BATCH_COUNT; +} + +static void rpc_reset_waitqueue_priority(struct rpc_wait_queue *queue) +{ + rpc_set_waitqueue_priority(queue, queue->maxpriority); + rpc_set_waitqueue_owner(queue, 0); +} + /* * Add new request to a priority queue. */ @@ -109,9 +126,11 @@ static void __rpc_add_wait_queue_priority(struct rpc_wait_queue *queue, struct rpc_task *t; INIT_LIST_HEAD(&task->u.tk_wait.links); - q = &queue->tasks[queue_priority]; if (unlikely(queue_priority > queue->maxpriority)) - q = &queue->tasks[queue->maxpriority]; + queue_priority = queue->maxpriority; + if (queue_priority > queue->priority) + rpc_set_waitqueue_priority(queue, queue_priority); + q = &queue->tasks[queue_priority]; list_for_each_entry(t, q, u.tk_wait.list) { if (t->tk_owner == task->tk_owner) { list_add_tail(&task->u.tk_wait.list, &t->u.tk_wait.links); @@ -180,24 +199,6 @@ static void __rpc_remove_wait_queue(struct rpc_wait_queue *queue, struct rpc_tas task->tk_pid, queue, rpc_qname(queue)); } -static inline void rpc_set_waitqueue_priority(struct rpc_wait_queue *queue, int priority) -{ - queue->priority = priority; - queue->count = 1 << (priority * 2); -} - -static inline void rpc_set_waitqueue_owner(struct rpc_wait_queue *queue, pid_t pid) -{ - queue->owner = pid; - queue->nr = RPC_BATCH_COUNT; -} - -static inline void rpc_reset_waitqueue_priority(struct rpc_wait_queue *queue) -{ - rpc_set_waitqueue_priority(queue, queue->maxpriority); - rpc_set_waitqueue_owner(queue, 0); -} - static void __rpc_init_priority_wait_queue(struct rpc_wait_queue *queue, const char *qname, unsigned char nr_queues) { int i; @@ -464,8 +465,7 @@ static struct rpc_task *__rpc_find_next_queued_priority(struct rpc_wait_queue *q /* * Check if we need to switch queues. */ - if (--queue->count) - goto new_owner; + goto new_owner; } /* -- cgit v0.10.2 From 1fa8064429d0acbf5bbf3c8a53f65679fdacc75e Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sun, 2 Dec 2012 13:54:59 -0500 Subject: NFSv4.1: Try to eliminate outliers when updating target_highest_slotid Look for sudden changes in the first and second derivatives in order to eliminate outlier changes to target_highest_slotid (which are due to out-of-order RPC replies). Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4session.c b/fs/nfs/nfs4session.c index 066cfa1..ed5aa9f 100644 --- a/fs/nfs/nfs4session.c +++ b/fs/nfs/nfs4session.c @@ -178,6 +178,8 @@ static void nfs4_reset_slot_table(struct nfs4_slot_table *tbl, tbl->highest_used_slotid = NFS4_NO_SLOT; tbl->target_highest_slotid = server_highest_slotid; tbl->server_highest_slotid = server_highest_slotid; + tbl->d_target_highest_slotid = 0; + tbl->d2_target_highest_slotid = 0; tbl->max_slotid = server_highest_slotid; } @@ -292,6 +294,8 @@ void nfs41_set_target_slotid(struct nfs4_slot_table *tbl, { spin_lock(&tbl->slot_tbl_lock); nfs41_set_target_slotid_locked(tbl, target_highest_slotid); + tbl->d_target_highest_slotid = 0; + tbl->d2_target_highest_slotid = 0; spin_unlock(&tbl->slot_tbl_lock); } @@ -307,16 +311,65 @@ static void nfs41_set_server_slotid_locked(struct nfs4_slot_table *tbl, tbl->server_highest_slotid = highest_slotid; } +static s32 nfs41_derivative_target_slotid(s32 s1, s32 s2) +{ + s1 -= s2; + if (s1 == 0) + return 0; + if (s1 < 0) + return (s1 - 1) >> 1; + return (s1 + 1) >> 1; +} + +static int nfs41_sign_s32(s32 s1) +{ + if (s1 > 0) + return 1; + if (s1 < 0) + return -1; + return 0; +} + +static bool nfs41_same_sign_or_zero_s32(s32 s1, s32 s2) +{ + if (!s1 || !s2) + return true; + return nfs41_sign_s32(s1) == nfs41_sign_s32(s2); +} + +/* Try to eliminate outliers by checking for sharp changes in the + * derivatives and second derivatives + */ +static bool nfs41_is_outlier_target_slotid(struct nfs4_slot_table *tbl, + u32 new_target) +{ + s32 d_target, d2_target; + bool ret = true; + + d_target = nfs41_derivative_target_slotid(new_target, + tbl->target_highest_slotid); + d2_target = nfs41_derivative_target_slotid(d_target, + tbl->d_target_highest_slotid); + /* Is first derivative same sign? */ + if (nfs41_same_sign_or_zero_s32(d_target, tbl->d_target_highest_slotid)) + ret = false; + /* Is second derivative same sign? */ + if (nfs41_same_sign_or_zero_s32(d2_target, tbl->d2_target_highest_slotid)) + ret = false; + tbl->d_target_highest_slotid = d_target; + tbl->d2_target_highest_slotid = d2_target; + return ret; +} + void nfs41_update_target_slotid(struct nfs4_slot_table *tbl, struct nfs4_slot *slot, struct nfs4_sequence_res *res) { spin_lock(&tbl->slot_tbl_lock); - if (tbl->generation != slot->generation) - goto out; - nfs41_set_server_slotid_locked(tbl, res->sr_highest_slotid); - nfs41_set_target_slotid_locked(tbl, res->sr_target_highest_slotid); -out: + if (!nfs41_is_outlier_target_slotid(tbl, res->sr_target_highest_slotid)) + nfs41_set_target_slotid_locked(tbl, res->sr_target_highest_slotid); + if (tbl->generation == slot->generation) + nfs41_set_server_slotid_locked(tbl, res->sr_highest_slotid); spin_unlock(&tbl->slot_tbl_lock); } diff --git a/fs/nfs/nfs4session.h b/fs/nfs/nfs4session.h index 7db7393..04f834c 100644 --- a/fs/nfs/nfs4session.h +++ b/fs/nfs/nfs4session.h @@ -38,6 +38,8 @@ struct nfs4_slot_table { * op for dynamic resizing */ u32 target_highest_slotid; /* Server max_slot target */ u32 server_highest_slotid; /* Server highest slotid */ + s32 d_target_highest_slotid; /* Derivative */ + s32 d2_target_highest_slotid; /* 2nd derivative */ unsigned long generation; /* Generation counter for target_highest_slotid */ struct completion complete; -- cgit v0.10.2 From 7d3e91a89b7adbc2831334def9e494dd9892f9af Mon Sep 17 00:00:00 2001 From: Sven Wegener Date: Sat, 8 Dec 2012 15:30:18 +0100 Subject: NFSv4: Check for buffer length in __nfs4_get_acl_uncached Commit 1f1ea6c "NFSv4: Fix buffer overflow checking in __nfs4_get_acl_uncached" accidently dropped the checking for too small result buffer length. If someone uses getxattr on "system.nfs4_acl" on an NFSv4 mount supporting ACLs, the ACL has not been cached and the buffer suplied is too short, we still copy the complete ACL, resulting in kernel and user space memory corruption. Signed-off-by: Sven Wegener Cc: stable@kernel.org Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 5eec442..05e5f6f 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3937,8 +3937,13 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu goto out_free; } nfs4_write_cached_acl(inode, pages, res.acl_data_offset, res.acl_len); - if (buf) + if (buf) { + if (res.acl_len > buflen) { + ret = -ERANGE; + goto out_free; + } _copy_from_pages(buf, pages, res.acl_data_offset, res.acl_len); + } out_ok: ret = res.acl_len; out_free: -- cgit v0.10.2 From 81d9bce5309288086b58b4d97a644e495fef75f2 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 10 Dec 2012 09:25:48 -0500 Subject: nfs: don't extend writes to cover entire page if pagecache is invalid Jian reported that the following sequence would leave "testfile" with corrupt data: # mount localhost:/export /mnt/nfs/ -o vers=3 # echo abc > /mnt/nfs/testfile; echo def >> /export/testfile; echo ghi >> /mnt/nfs/testfile # cat -v /export/testfile abc ^@^@^@^@ghi While there's no locking involved here, the operations are serialized, so CTO should prevent corruption. The first write to the file is fine and writes 4 bytes. The file is then extended on the server. When it's reopened a GETATTR is issued and the size change is noticed. This causes NFS_INO_INVALID_DATA to be set on the file. Because the file is opened for write only, nfs_want_read_modify_write() returns 0 to nfs_write_begin(). nfs_updatepage then calls nfs_write_pageuptodate() to see if it should extend the nfs_page to cover the whole page. NFS_INO_INVALID_DATA is still set on the file at that point, but that flag is ignored and nfs_pageuptodate erroneously extends the write to cover the whole page, with the write done on the server side filled in with zeroes. This patch just has that function check for NFS_INO_INVALID_DATA in addition to NFS_INO_REVAL_PAGECACHE. This fixes the bug, but looking over the code, I wonder if we might have a similar bug in nfs_revalidate_size(). The difference between those two flags is very subtle, so it seems like we ought to be checking for NFS_INO_INVALID_DATA in most of the places that we look for NFS_INO_REVAL_PAGECACHE. I believe this is regression introduced by commit 8d197a568. The code did check for NFS_INO_INVALID_DATA prior to that patch. Original bug report is here: https://bugzilla.redhat.com/show_bug.cgi?id=885743 Cc: # 3.5+ Reported-by: Jian Li Signed-off-by: Jeff Layton Signed-off-by: Trond Myklebust diff --git a/fs/nfs/write.c b/fs/nfs/write.c index f710e39..eecd8b8 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -884,7 +884,7 @@ static bool nfs_write_pageuptodate(struct page *page, struct inode *inode) { if (nfs_have_delegated_attributes(inode)) goto out; - if (NFS_I(inode)->cache_validity & NFS_INO_REVAL_PAGECACHE) + if (NFS_I(inode)->cache_validity & (NFS_INO_INVALID_DATA|NFS_INO_REVAL_PAGECACHE)) return false; out: return PageUptodate(page) != 0; -- cgit v0.10.2 From 85563073741bd7935a6900d567ddaf907192270d Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 11 Dec 2012 10:31:12 -0500 Subject: NFSv4.1: Handle NFS4ERR_BADSLOT errors correctly Most (all) NFS4ERR_BADSLOT errors are due to the client failing to respect the server's sr_highest_slotid limit. This mainly happens due to reordered RPC requests. The way to handle it is simply to drop the slot that we're using, and retry using the new highest_slotid limits. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 92bd799..a4692e9 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -422,6 +422,7 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * struct nfs4_slot *slot; unsigned long timestamp; struct nfs_client *clp; + int ret = 1; /* * sr_status remains 1 if an RPC level error occurred. The server @@ -462,6 +463,16 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * slot->slot_nr, slot->seq_nr); goto out_retry; + case -NFS4ERR_BADSLOT: + /* + * The slot id we used was probably retired. Try again + * using a different slot id. + */ + if (rpc_restart_call_prepare(task)) { + task->tk_status = 0; + ret = 0; + } + break; default: /* Just update the slot sequence no. */ ++slot->seq_nr; @@ -470,7 +481,7 @@ out: /* The session may be reset by one of the error handlers. */ dprintk("%s: Error %d free the slot \n", __func__, res->sr_status); nfs41_sequence_free_slot(res); - return 1; + return ret; out_retry: if (!rpc_restart_call(task)) goto out; -- cgit v0.10.2 From b0ef9647a0cd6cfd63fed48fbbe6005e4ba92571 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 11 Dec 2012 12:10:14 -0500 Subject: NFSv4.1: Be conservative about the client highest slotid If the server sends us a target that looks like an outlier, but is lower than the existing target, then respect it anyway. However defer actually updating the generation counter until we get a target that doesn't look like an outlier. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4session.c b/fs/nfs/nfs4session.c index ed5aa9f..1e6c87c 100644 --- a/fs/nfs/nfs4session.c +++ b/fs/nfs/nfs4session.c @@ -273,20 +273,28 @@ void nfs41_wake_slot_table(struct nfs4_slot_table *tbl) } } +static void nfs41_set_max_slotid_locked(struct nfs4_slot_table *tbl, + u32 target_highest_slotid) +{ + u32 max_slotid; + + max_slotid = min(NFS4_MAX_SLOT_TABLE - 1, target_highest_slotid); + if (max_slotid > tbl->server_highest_slotid) + max_slotid = tbl->server_highest_slotid; + if (max_slotid > tbl->target_highest_slotid) + max_slotid = tbl->target_highest_slotid; + tbl->max_slotid = max_slotid; + nfs41_wake_slot_table(tbl); +} + /* Update the client's idea of target_highest_slotid */ static void nfs41_set_target_slotid_locked(struct nfs4_slot_table *tbl, u32 target_highest_slotid) { - unsigned int max_slotid; - if (tbl->target_highest_slotid == target_highest_slotid) return; tbl->target_highest_slotid = target_highest_slotid; tbl->generation++; - - max_slotid = min(NFS4_MAX_SLOT_TABLE - 1, tbl->target_highest_slotid); - tbl->max_slotid = max_slotid; - nfs41_wake_slot_table(tbl); } void nfs41_set_target_slotid(struct nfs4_slot_table *tbl, @@ -296,6 +304,7 @@ void nfs41_set_target_slotid(struct nfs4_slot_table *tbl, nfs41_set_target_slotid_locked(tbl, target_highest_slotid); tbl->d_target_highest_slotid = 0; tbl->d2_target_highest_slotid = 0; + nfs41_set_max_slotid_locked(tbl, target_highest_slotid); spin_unlock(&tbl->slot_tbl_lock); } @@ -370,6 +379,7 @@ void nfs41_update_target_slotid(struct nfs4_slot_table *tbl, nfs41_set_target_slotid_locked(tbl, res->sr_target_highest_slotid); if (tbl->generation == slot->generation) nfs41_set_server_slotid_locked(tbl, res->sr_highest_slotid); + nfs41_set_max_slotid_locked(tbl, res->sr_target_highest_slotid); spin_unlock(&tbl->slot_tbl_lock); } -- cgit v0.10.2 From 67fad106a219e083c91c79695bd1807dde1bf7b9 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 12 Dec 2012 11:38:44 -0500 Subject: nfs: don't zero out the rest of the page if we hit the EOF on a DIO READ Eryu provided a test program that would segfault when attempting to read past the EOF on file that was opened O_DIRECT. The buffer given to the read() call was on the stack, and when he attempted to read past it it would scribble over the rest of the stack page. If we hit the end of the file on a DIO READ request, then we don't want to zero out the rest of the buffer. These aren't pagecache pages after all, and there's no guarantee that the buffers that were passed in represent entire pages. Cc: # v3.5+ Cc: Fred Isaman Reported-by: Eryu Guan Signed-off-by: Jeff Layton Signed-off-by: Trond Myklebust diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index cae26cb..594f4e7 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -266,14 +266,6 @@ static void nfs_direct_read_completion(struct nfs_pgio_header *hdr) struct nfs_page *req = nfs_list_entry(hdr->pages.next); struct page *page = req->wb_page; - if (test_bit(NFS_IOHDR_EOF, &hdr->flags)) { - if (bytes > hdr->good_bytes) - zero_user(page, 0, PAGE_SIZE); - else if (hdr->good_bytes - bytes < PAGE_SIZE) - zero_user_segment(page, - hdr->good_bytes & ~PAGE_MASK, - PAGE_SIZE); - } if (!PageCompound(page)) { if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) { if (bytes < hdr->good_bytes) -- cgit v0.10.2 From be7e985804c610fcdcee8730cf42718b8a4e1c41 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 12 Dec 2012 12:36:31 -0500 Subject: nfs: fix page dirtying in NFS DIO read codepath The NFS DIO code will dirty pages that catch read responses in order to handle the case where someone is doing DIO reads into an mmapped buffer. The existing code doesn't really do the right thing though since it doesn't take into account the case where we might be attempting to read past the EOF. Fix the logic in that code to only dirty pages that ended up receiving data from the read. Note too that it really doesn't matter if NFS_IOHDR_ERROR is set or not. All that matters is if the page was altered by the read. Cc: Fred Isaman Signed-off-by: Jeff Layton Signed-off-by: Trond Myklebust diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 594f4e7..0bd7a55 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -266,13 +266,8 @@ static void nfs_direct_read_completion(struct nfs_pgio_header *hdr) struct nfs_page *req = nfs_list_entry(hdr->pages.next); struct page *page = req->wb_page; - if (!PageCompound(page)) { - if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) { - if (bytes < hdr->good_bytes) - set_page_dirty(page); - } else - set_page_dirty(page); - } + if (!PageCompound(page) && bytes < hdr->good_bytes) + set_page_dirty(page); bytes += req->wb_bytes; nfs_list_remove_request(req); nfs_direct_readpage_release(req); -- cgit v0.10.2 From 620038f6d2304475dce800dc5c75fc335a19613a Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Tue, 27 Nov 2012 10:34:20 -0500 Subject: SUNRPC set gss gc_expiry to full lifetime Only use the default GSSD_MIN_TIMEOUT if the gss downcall timeout is zero. Store the full lifetime in gc_expiry (not 3/4 of the lifetime) as subsequent patches will use the gc_expiry to determine buffered WRITE behavior in the face of expired or soon to be expired gss credentials. Signed-off-by: Andy Adamson Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 909dc0c..6e5c824 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -192,17 +192,23 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct const void *q; unsigned int seclen; unsigned int timeout; + unsigned long now = jiffies; u32 window_size; int ret; - /* First unsigned int gives the lifetime (in seconds) of the cred */ + /* First unsigned int gives the remaining lifetime in seconds of the + * credential - e.g. the remaining TGT lifetime for Kerberos or + * the -t value passed to GSSD. + */ p = simple_get_bytes(p, end, &timeout, sizeof(timeout)); if (IS_ERR(p)) goto err; if (timeout == 0) timeout = GSSD_MIN_TIMEOUT; - ctx->gc_expiry = jiffies + (unsigned long)timeout * HZ * 3 / 4; - /* Sequence number window. Determines the maximum number of simultaneous requests */ + ctx->gc_expiry = now + ((unsigned long)timeout * HZ); + /* Sequence number window. Determines the maximum number of + * simultaneous requests + */ p = simple_get_bytes(p, end, &window_size, sizeof(window_size)); if (IS_ERR(p)) goto err; @@ -237,9 +243,12 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct p = ERR_PTR(ret); goto err; } + dprintk("RPC: %s Success. gc_expiry %lu now %lu timeout %u\n", + __func__, ctx->gc_expiry, now, timeout); return q; err: - dprintk("RPC: %s returning %ld\n", __func__, -PTR_ERR(p)); + dprintk("RPC: %s returns %ld gc_expiry %lu now %lu timeout %u\n", + __func__, -PTR_ERR(p), ctx->gc_expiry, now, timeout); return p; } -- cgit v0.10.2 From eb96d5c97b0825d542e9c4ba5e0a22b519355166 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Tue, 27 Nov 2012 10:34:19 -0500 Subject: SUNRPC handle EKEYEXPIRED in call_refreshresult Currently, when an RPCSEC_GSS context has expired or is non-existent and the users (Kerberos) credentials have also expired or are non-existent, the client receives the -EKEYEXPIRED error and tries to refresh the context forever. If an application is performing I/O, or other work against the share, the application hangs, and the user is not prompted to refresh/establish their credentials. This can result in a denial of service for other users. Users are expected to manage their Kerberos credential lifetimes to mitigate this issue. Move the -EKEYEXPIRED handling into the RPC layer. Try tk_cred_retry number of times to refresh the gss_context, and then return -EACCES to the application. Signed-off-by: Andy Adamson Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 6932209..70efb63 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -24,14 +24,14 @@ #define NFSDBG_FACILITY NFSDBG_PROC -/* A wrapper to handle the EJUKEBOX and EKEYEXPIRED error messages */ +/* A wrapper to handle the EJUKEBOX error messages */ static int nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) { int res; do { res = rpc_call_sync(clnt, msg, flags); - if (res != -EJUKEBOX && res != -EKEYEXPIRED) + if (res != -EJUKEBOX) break; freezable_schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME); res = -ERESTARTSYS; @@ -44,7 +44,7 @@ nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) static int nfs3_async_handle_jukebox(struct rpc_task *task, struct inode *inode) { - if (task->tk_status != -EJUKEBOX && task->tk_status != -EKEYEXPIRED) + if (task->tk_status != -EJUKEBOX) return 0; if (task->tk_status == -EJUKEBOX) nfs_inc_stats(inode, NFSIOS_DELAY); diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index 1e42413..194c484 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c @@ -179,7 +179,6 @@ static int filelayout_async_handle_error(struct rpc_task *task, break; case -NFS4ERR_DELAY: case -NFS4ERR_GRACE: - case -EKEYEXPIRED: rpc_delay(task, FILELAYOUT_POLL_RETRY_MAX); break; case -NFS4ERR_RETRY_UNCACHED_REP: diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a4692e9..b0963ae 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -333,7 +333,6 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc } case -NFS4ERR_GRACE: case -NFS4ERR_DELAY: - case -EKEYEXPIRED: ret = nfs4_delay(server->client, &exception->timeout); if (ret != 0) break; @@ -1343,13 +1342,6 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state nfs_inode_find_state_and_recover(state->inode, stateid); nfs4_schedule_stateid_recovery(server, state); - case -EKEYEXPIRED: - /* - * User RPCSEC_GSS context has expired. - * We cannot recover this stateid now, so - * skip it and allow recovery thread to - * proceed. - */ case -ENOMEM: err = 0; goto out; @@ -3946,7 +3938,6 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, case -NFS4ERR_DELAY: nfs_inc_server_stats(server, NFSIOS_DELAY); case -NFS4ERR_GRACE: - case -EKEYEXPIRED: rpc_delay(task, NFS4_POLL_RETRY_MAX); task->tk_status = 0; return -EAGAIN; @@ -4946,15 +4937,6 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl) nfs4_schedule_stateid_recovery(server, state); err = 0; goto out; - case -EKEYEXPIRED: - /* - * User RPCSEC_GSS context has expired. - * We cannot recover this stateid now, so - * skip it and allow recovery thread to - * proceed. - */ - err = 0; - goto out; case -ENOMEM: case -NFS4ERR_DENIED: /* kill_proc(fl->fl_pid, SIGLOST, 1); */ diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 78e90a8..8dcbd9a 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1437,14 +1437,6 @@ restart: /* Mark the file as being 'closed' */ state->state = 0; break; - case -EKEYEXPIRED: - /* - * User RPCSEC_GSS context has expired. - * We cannot recover this stateid now, so - * skip it and allow recovery thread to - * proceed. - */ - break; case -NFS4ERR_ADMIN_REVOKED: case -NFS4ERR_STALE_STATEID: case -NFS4ERR_BAD_STATEID: @@ -1597,14 +1589,6 @@ static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp) nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce); } -static void nfs4_warn_keyexpired(const char *s) -{ - printk_ratelimited(KERN_WARNING "Error: state manager" - " encountered RPCSEC_GSS session" - " expired against NFSv4 server %s.\n", - s); -} - static int nfs4_recovery_handle_error(struct nfs_client *clp, int error) { switch (error) { @@ -1638,10 +1622,6 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error) case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state); break; - case -EKEYEXPIRED: - /* Nothing we can do */ - nfs4_warn_keyexpired(clp->cl_hostname); - break; default: dprintk("%s: failed to handle error %d for server %s\n", __func__, error, clp->cl_hostname); @@ -1758,8 +1738,6 @@ static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status) dprintk("%s: exit with error %d for server %s\n", __func__, -EPROTONOSUPPORT, clp->cl_hostname); return -EPROTONOSUPPORT; - case -EKEYEXPIRED: - nfs4_warn_keyexpired(clp->cl_hostname); case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery * in nfs4_exchange_id */ default: @@ -1912,7 +1890,6 @@ again: break; case -EKEYEXPIRED: - nfs4_warn_keyexpired(clp->cl_hostname); case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery * in nfs4_exchange_id */ status = -EKEYEXPIRED; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 50a88c3..f084dac 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -47,39 +47,6 @@ #define NFSDBG_FACILITY NFSDBG_PROC /* - * wrapper to handle the -EKEYEXPIRED error message. This should generally - * only happen if using krb5 auth and a user's TGT expires. NFSv2 doesn't - * support the NFSERR_JUKEBOX error code, but we handle this situation in the - * same way that we handle that error with NFSv3. - */ -static int -nfs_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) -{ - int res; - do { - res = rpc_call_sync(clnt, msg, flags); - if (res != -EKEYEXPIRED) - break; - freezable_schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME); - res = -ERESTARTSYS; - } while (!fatal_signal_pending(current)); - return res; -} - -#define rpc_call_sync(clnt, msg, flags) nfs_rpc_wrapper(clnt, msg, flags) - -static int -nfs_async_handle_expired_key(struct rpc_task *task) -{ - if (task->tk_status != -EKEYEXPIRED) - return 0; - task->tk_status = 0; - rpc_restart_call(task); - rpc_delay(task, NFS_JUKEBOX_RETRY_TIME); - return 1; -} - -/* * Bare-bones access to getattr: this is for nfs_read_super. */ static int @@ -364,8 +331,6 @@ static void nfs_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlink static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir) { - if (nfs_async_handle_expired_key(task)) - return 0; nfs_mark_for_revalidate(dir); return 1; } @@ -385,8 +350,6 @@ static int nfs_proc_rename_done(struct rpc_task *task, struct inode *old_dir, struct inode *new_dir) { - if (nfs_async_handle_expired_key(task)) - return 0; nfs_mark_for_revalidate(old_dir); nfs_mark_for_revalidate(new_dir); return 1; @@ -642,9 +605,6 @@ static int nfs_read_done(struct rpc_task *task, struct nfs_read_data *data) { struct inode *inode = data->header->inode; - if (nfs_async_handle_expired_key(task)) - return -EAGAIN; - nfs_invalidate_atime(inode); if (task->tk_status >= 0) { nfs_refresh_inode(inode, data->res.fattr); @@ -671,9 +631,6 @@ static int nfs_write_done(struct rpc_task *task, struct nfs_write_data *data) { struct inode *inode = data->header->inode; - if (nfs_async_handle_expired_key(task)) - return -EAGAIN; - if (task->tk_status >= 0) nfs_post_op_update_inode_force_wcc(inode, data->res.fattr); return 0; diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index c69e199..55e174f 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1381,6 +1381,7 @@ call_refreshresult(struct rpc_task *task) return; case -ETIMEDOUT: rpc_delay(task, 3*HZ); + case -EKEYEXPIRED: case -EAGAIN: status = -EACCES; if (!task->tk_cred_retry) -- cgit v0.10.2 From f259613a1e4b44a0cf85a5dafd931be96ee7c9e5 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 13 Dec 2012 15:14:36 +1100 Subject: NFS: avoid NULL dereference in nfs_destroy_server In rare circumstances, nfs_clone_server() of a v2 or v3 server can get an error between setting server->destory (to nfs_destroy_server), and calling nfs_start_lockd (which will set server->nlm_host). If this happens, nfs_clone_server will call nfs_free_server which will call nfs_destroy_server and thence nlmclnt_done(NULL). This causes the NULL to be dereferenced. So add a guard to only call nlmclnt_done() if ->nlm_host is not NULL. The other guards there are irrelevant as nlm_host can only be non-NULL if one of these flags are set - so remove those tests. (Thanks to Trond for this suggestion). This is suitable for any stable kernel since 2.6.25. Cc: stable@vger.kernel.org Signed-off-by: NeilBrown Signed-off-by: Trond Myklebust diff --git a/fs/nfs/client.c b/fs/nfs/client.c index c285e0a..9f3c664 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -615,8 +615,7 @@ EXPORT_SYMBOL_GPL(nfs_create_rpc_client); */ static void nfs_destroy_server(struct nfs_server *server) { - if (!(server->flags & NFS_MOUNT_LOCAL_FLOCK) || - !(server->flags & NFS_MOUNT_LOCAL_FCNTL)) + if (server->nlm_host) nlmclnt_done(server->nlm_host); } -- cgit v0.10.2 From aaea7d2f78d008882524eddff0d78098c8fa9496 Mon Sep 17 00:00:00 2001 From: Yanchuan Nian Date: Thu, 13 Dec 2012 14:37:34 +0800 Subject: nfs: Remove duplicate function declaration in internal.h Remove duplicate function declaration in internal.h Signed-off-by: Yanchuan Nian [Trond: Added nfs_pageio_init_read, which suffered from the same problem] Signed-off-by: Trond Myklebust diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index fb99447..89c1ee4 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -363,9 +363,6 @@ extern int nfs_initiate_read(struct rpc_clnt *clnt, extern void nfs_read_prepare(struct rpc_task *task, void *calldata); extern int nfs_generic_pagein(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr); -extern void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, - struct inode *inode, - const struct nfs_pgio_completion_ops *compl_ops); extern void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio); extern void nfs_readdata_release(struct nfs_read_data *rdata); @@ -388,9 +385,6 @@ extern struct nfs_write_header *nfs_writehdr_alloc(void); extern void nfs_writehdr_free(struct nfs_pgio_header *hdr); extern int nfs_generic_flush(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr); -extern void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, - struct inode *inode, int ioflags, - const struct nfs_pgio_completion_ops *compl_ops); extern void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio); extern void nfs_writedata_release(struct nfs_write_data *wdata); extern void nfs_commit_free(struct nfs_commit_data *p); -- cgit v0.10.2 From 48d7a57693af660666c4afdc54c09b2f9655e260 Mon Sep 17 00:00:00 2001 From: Yanchuan Nian Date: Thu, 13 Dec 2012 14:37:52 +0800 Subject: nfs: Remove unused list nfs4_clientid_list This list was designed to store struct nfs4_client in the client side. But nfs4_client was obsolete and has been removed from the source code. So remove the unused list. Signed-off-by: Yanchuan Nian Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 8dcbd9a..9448c57 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -67,7 +67,6 @@ const nfs4_stateid zero_stateid; static DEFINE_MUTEX(nfs_clid_init_mutex); -static LIST_HEAD(nfs4_clientid_list); int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred) { -- cgit v0.10.2 From eed9935745cc44071043ec8c4cde64c820b5c601 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 14 Dec 2012 14:36:36 -0500 Subject: NFS: Ensure that we always drop inodes that have been marked as stale There is no need to cache stale inodes. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 117183b..2faae14 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -107,6 +107,12 @@ u64 nfs_compat_user_ino64(u64 fileid) return ino; } +int nfs_drop_inode(struct inode *inode) +{ + return NFS_STALE(inode) || generic_drop_inode(inode); +} +EXPORT_SYMBOL_GPL(nfs_drop_inode); + void nfs_clear_inode(struct inode *inode) { /* diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 89c1ee4..f0e6c7d 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -296,6 +296,7 @@ extern struct workqueue_struct *nfsiod_workqueue; extern struct inode *nfs_alloc_inode(struct super_block *sb); extern void nfs_destroy_inode(struct inode *); extern int nfs_write_inode(struct inode *, struct writeback_control *); +extern int nfs_drop_inode(struct inode *); extern void nfs_clear_inode(struct inode *); extern void nfs_evict_inode(struct inode *); void nfs_zap_acl_cache(struct inode *inode); diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c index bd61221..84d2e9e 100644 --- a/fs/nfs/nfs4super.c +++ b/fs/nfs/nfs4super.c @@ -51,6 +51,7 @@ static const struct super_operations nfs4_sops = { .alloc_inode = nfs_alloc_inode, .destroy_inode = nfs_destroy_inode, .write_inode = nfs4_write_inode, + .drop_inode = nfs_drop_inode, .put_super = nfs_put_super, .statfs = nfs_statfs, .evict_inode = nfs4_evict_inode, diff --git a/fs/nfs/super.c b/fs/nfs/super.c index e12cea4..aa5315b 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -308,6 +308,7 @@ const struct super_operations nfs_sops = { .alloc_inode = nfs_alloc_inode, .destroy_inode = nfs_destroy_inode, .write_inode = nfs_write_inode, + .drop_inode = nfs_drop_inode, .put_super = nfs_put_super, .statfs = nfs_statfs, .evict_inode = nfs_evict_inode, -- cgit v0.10.2 From 1f018458b30b0d5c535c94e577aa0acbb92e1395 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 14 Dec 2012 16:38:46 -0500 Subject: NFS: Fix calls to drop_nlink() It is almost always wrong for NFS to call drop_nlink() after removing a file. What we really want is to mark the inode's attributes for revalidation, and we want to ensure that the VFS drops it if we're reasonably sure that this is the final unlink(). Do the former using the usual cache validity flags, and the latter by testing if inode->i_nlink == 1, and clearing it in that case. This also fixes the following warning reported by Neil Brown and Jeff Layton (among others). [634155.004438] WARNING: at /home/abuild/rpmbuild/BUILD/kernel-desktop-3.5.0/lin [634155.004442] Hardware name: Latitude E6510 [634155.004577] crc_itu_t crc32c_intel snd_hwdep snd_pcm snd_timer snd soundcor [634155.004609] Pid: 13402, comm: bash Tainted: G W 3.5.0-36-desktop # [634155.004611] Call Trace: [634155.004630] [] dump_trace+0xaa/0x2b0 [634155.004641] [] dump_stack+0x69/0x6f [634155.004653] [] warn_slowpath_common+0x7b/0xc0 [634155.004662] [] drop_nlink+0x34/0x40 [634155.004687] [] nfs_dentry_iput+0x33/0x70 [nfs] [634155.004714] [] dput+0x12e/0x230 [634155.004726] [] __fput+0x170/0x230 [634155.004735] [] filp_close+0x5f/0x90 [634155.004743] [] sys_close+0x97/0x100 [634155.004754] [] system_call_fastpath+0x16/0x1b [634155.004767] [<00007f2a73a0d110>] 0x7f2a73a0d10f Signed-off-by: Trond Myklebust Cc: stable@vger.kernel.org [3.3+] diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index ce8cb92..a46a746 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1155,11 +1155,14 @@ static int nfs_dentry_delete(const struct dentry *dentry) } +/* Ensure that we revalidate inode->i_nlink */ static void nfs_drop_nlink(struct inode *inode) { spin_lock(&inode->i_lock); - if (inode->i_nlink > 0) - drop_nlink(inode); + /* drop the inode if we're reasonably sure this is the last link */ + if (inode->i_nlink == 1) + clear_nlink(inode); + NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATTR; spin_unlock(&inode->i_lock); } @@ -1174,8 +1177,8 @@ static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA; if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { - drop_nlink(inode); nfs_complete_unlink(dentry, inode); + nfs_drop_nlink(inode); } iput(inode); } @@ -1646,10 +1649,8 @@ static int nfs_safe_remove(struct dentry *dentry) if (inode != NULL) { NFS_PROTO(inode)->return_delegation(inode); error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); - /* The VFS may want to delete this inode */ if (error == 0) nfs_drop_nlink(inode); - nfs_mark_for_revalidate(inode); } else error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); if (error == -ENOENT) -- cgit v0.10.2 From 65a0c14954493802de01968a73b849f9fc4b4d1a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 14 Dec 2012 17:51:40 -0500 Subject: NFS: nfs_lookup_revalidate should not trust an inode with i_nlink == 0 If the inode has no links, then we should force a new lookup. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index a46a746..d8e58ed 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -978,10 +978,11 @@ static int nfs_is_exclusive_create(struct inode *dir, unsigned int flags) * particular file and the "nocto" mount flag is not set. * */ -static inline +static int nfs_lookup_verify_inode(struct inode *inode, unsigned int flags) { struct nfs_server *server = NFS_SERVER(inode); + int ret; if (IS_AUTOMOUNT(inode)) return 0; @@ -992,9 +993,13 @@ int nfs_lookup_verify_inode(struct inode *inode, unsigned int flags) if ((flags & LOOKUP_OPEN) && !(server->flags & NFS_MOUNT_NOCTO) && (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) goto out_force; - return 0; +out: + return (inode->i_nlink == 0) ? -ENOENT : 0; out_force: - return __nfs_revalidate_inode(server, inode); + ret = __nfs_revalidate_inode(server, inode); + if (ret != 0) + return ret; + goto out; } /* -- cgit v0.10.2 From e8794440849d1d15fa11251ef1622e6160614874 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 15 Dec 2012 13:56:18 -0500 Subject: NFSv4.1: Try to deal with NFS4ERR_SEQ_MISORDERED. If the server returns NFS4ERR_SEQ_MISORDERED, it could be a sign that the slot was retired at some point. Retry the attempt after reinitialising the slot sequence number to 1. Also add a handler for NFS4ERR_SEQ_FALSE_RETRY. Just bump the slot sequence number and retry... Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index b0963ae..9003b8f 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -467,11 +467,19 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * * The slot id we used was probably retired. Try again * using a different slot id. */ - if (rpc_restart_call_prepare(task)) { - task->tk_status = 0; - ret = 0; - } - break; + goto retry_nowait; + case -NFS4ERR_SEQ_MISORDERED: + /* + * Could this slot have been previously retired? + * If so, then the server may be expecting seq_nr = 1! + */ + if (slot->seq_nr == 1) + break; + slot->seq_nr = 1; + goto retry_nowait; + case -NFS4ERR_SEQ_FALSE_RETRY: + ++slot->seq_nr; + goto retry_nowait; default: /* Just update the slot sequence no. */ ++slot->seq_nr; @@ -481,6 +489,12 @@ out: dprintk("%s: Error %d free the slot \n", __func__, res->sr_status); nfs41_sequence_free_slot(res); return ret; +retry_nowait: + if (rpc_restart_call_prepare(task)) { + task->tk_status = 0; + ret = 0; + } + goto out; out_retry: if (!rpc_restart_call(task)) goto out; -- cgit v0.10.2 From 8e63b6a8adabb0551124c3b78f7f5f36912c3728 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 15 Dec 2012 15:21:52 -0500 Subject: NFSv4.1: Move the RPC timestamp out of the slot. Shave a few bytes off the slot table size by moving the RPC timestamp into the sequence results. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 9003b8f..afb428e 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -419,7 +419,6 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * { struct nfs4_session *session; struct nfs4_slot *slot; - unsigned long timestamp; struct nfs_client *clp; int ret = 1; @@ -444,9 +443,8 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * case 0: /* Update the slot's sequence and clientid lease timer */ ++slot->seq_nr; - timestamp = slot->renewal_time; clp = session->clp; - do_renew_lease(clp, timestamp); + do_renew_lease(clp, res->sr_timestamp); /* Check sequence flags */ if (res->sr_status_flags != 0) nfs4_schedule_lease_recovery(clp); @@ -473,10 +471,11 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * * Could this slot have been previously retired? * If so, then the server may be expecting seq_nr = 1! */ - if (slot->seq_nr == 1) - break; - slot->seq_nr = 1; - goto retry_nowait; + if (slot->seq_nr != 1) { + slot->seq_nr = 1; + goto retry_nowait; + } + break; case -NFS4ERR_SEQ_FALSE_RETRY: ++slot->seq_nr; goto retry_nowait; @@ -567,6 +566,7 @@ int nfs41_setup_sequence(struct nfs4_session *session, slot->slot_nr, slot->seq_nr); res->sr_slot = slot; + res->sr_timestamp = jiffies; res->sr_status_flags = 0; /* * sr_status is only set in decode_sequence, and so will remain diff --git a/fs/nfs/nfs4session.c b/fs/nfs/nfs4session.c index 1e6c87c..0e1cc1f 100644 --- a/fs/nfs/nfs4session.c +++ b/fs/nfs/nfs4session.c @@ -143,7 +143,6 @@ struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl) if (slotid > tbl->highest_used_slotid || tbl->highest_used_slotid == NFS4_NO_SLOT) tbl->highest_used_slotid = slotid; - ret->renewal_time = jiffies; ret->generation = tbl->generation; out: @@ -228,9 +227,9 @@ static bool nfs41_assign_slot(struct rpc_task *task, void *pslot) if (nfs4_session_draining(tbl->session) && !args->sa_privileged) return false; - slot->renewal_time = jiffies; slot->generation = tbl->generation; args->sa_slot = slot; + res->sr_timestamp = jiffies; res->sr_slot = slot; res->sr_status_flags = 0; res->sr_status = 1; diff --git a/fs/nfs/nfs4session.h b/fs/nfs/nfs4session.h index 04f834c..d17b080 100644 --- a/fs/nfs/nfs4session.h +++ b/fs/nfs/nfs4session.h @@ -19,7 +19,6 @@ struct nfs4_slot { struct nfs4_slot_table *table; struct nfs4_slot *next; unsigned long generation; - unsigned long renewal_time; u32 slot_nr; u32 seq_nr; }; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index a55abd4..29adb12 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -194,6 +194,7 @@ struct nfs4_sequence_args { struct nfs4_sequence_res { struct nfs4_slot *sr_slot; /* slot used to send request */ + unsigned long sr_timestamp; int sr_status; /* sequence operation status */ u32 sr_status_flags; u32 sr_highest_slotid; -- cgit v0.10.2 From ac20d163fccf9fa6acec8b68f127003635e13b72 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 15 Dec 2012 15:36:07 -0500 Subject: NFSv4.1: Deal effectively with interrupted RPC calls. If an RPC call is interrupted, assume that the server hasn't processed the RPC call so that the next time we use the slot, we know that if we get a NFS4ERR_SEQ_MISORDERED or NFS4ERR_SEQ_FALSE_RETRY, we just have to bump the sequence number. Signed-off-by: Trond Myklebust diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index afb428e..493f0f4 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -420,17 +420,9 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * struct nfs4_session *session; struct nfs4_slot *slot; struct nfs_client *clp; + bool interrupted = false; int ret = 1; - /* - * sr_status remains 1 if an RPC level error occurred. The server - * may or may not have processed the sequence operation.. - * Proceed as if the server received and processed the sequence - * operation. - */ - if (res->sr_status == 1) - res->sr_status = NFS_OK; - /* don't increment the sequence number if the task wasn't sent */ if (!RPC_WAS_SENT(task)) goto out; @@ -438,6 +430,11 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * slot = res->sr_slot; session = slot->table->session; + if (slot->interrupted) { + slot->interrupted = 0; + interrupted = true; + } + /* Check the SEQUENCE operation status */ switch (res->sr_status) { case 0: @@ -450,6 +447,15 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * nfs4_schedule_lease_recovery(clp); nfs41_update_target_slotid(slot->table, slot, res); break; + case 1: + /* + * sr_status remains 1 if an RPC level error occurred. + * The server may or may not have processed the sequence + * operation.. + * Mark the slot as having hosted an interrupted RPC call. + */ + slot->interrupted = 1; + goto out; case -NFS4ERR_DELAY: /* The server detected a resend of the RPC call and * returned NFS4ERR_DELAY as per Section 2.10.6.2 @@ -468,6 +474,14 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * goto retry_nowait; case -NFS4ERR_SEQ_MISORDERED: /* + * Was the last operation on this sequence interrupted? + * If so, retry after bumping the sequence number. + */ + if (interrupted) { + ++slot->seq_nr; + goto retry_nowait; + } + /* * Could this slot have been previously retired? * If so, then the server may be expecting seq_nr = 1! */ diff --git a/fs/nfs/nfs4session.c b/fs/nfs/nfs4session.c index 0e1cc1f..ebda5f4 100644 --- a/fs/nfs/nfs4session.c +++ b/fs/nfs/nfs4session.c @@ -172,6 +172,7 @@ static void nfs4_reset_slot_table(struct nfs4_slot_table *tbl, p = &tbl->slots; while (*p) { (*p)->seq_nr = ivalue; + (*p)->interrupted = 0; p = &(*p)->next; } tbl->highest_used_slotid = NFS4_NO_SLOT; diff --git a/fs/nfs/nfs4session.h b/fs/nfs/nfs4session.h index d17b080..6f3cb39 100644 --- a/fs/nfs/nfs4session.h +++ b/fs/nfs/nfs4session.h @@ -21,6 +21,7 @@ struct nfs4_slot { unsigned long generation; u32 slot_nr; u32 seq_nr; + unsigned int interrupted : 1; }; /* Sessions */ -- cgit v0.10.2 From 4a20a988f732caa343257346ebf145ae8fa437e1 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 15 Dec 2012 17:02:29 -0500 Subject: SUNRPC: Handle ECONNREFUSED in xs_local_setup_socket Silence the unnecessary warning "unhandled error (111) connecting to..." and convert it to a dprintk for debugging purposes. Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 6db26e5..7163ef5 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -1902,6 +1902,10 @@ static void xs_local_setup_socket(struct work_struct *work) dprintk("RPC: xprt %p: socket %s does not exist\n", xprt, xprt->address_strings[RPC_DISPLAY_ADDR]); break; + case -ECONNREFUSED: + dprintk("RPC: xprt %p: connection refused for %s\n", + xprt, xprt->address_strings[RPC_DISPLAY_ADDR]); + break; default: printk(KERN_ERR "%s: unhandled error (%d) connecting to %s\n", __func__, -status, -- cgit v0.10.2 From 1efc28780bf84f04dcce4f3f7242e4e6a59bffd4 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 15 Dec 2012 17:05:57 -0500 Subject: SUNRPC: variable 'svsk' is unused in function bc_send_request Silence a compile time warning. Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 7163ef5..68b0a81 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2400,7 +2400,6 @@ static int bc_send_request(struct rpc_task *task) { struct rpc_rqst *req = task->tk_rqstp; struct svc_xprt *xprt; - struct svc_sock *svsk; u32 len; dprintk("sending request with xid: %08x\n", ntohl(req->rq_xid)); @@ -2408,7 +2407,6 @@ static int bc_send_request(struct rpc_task *task) * Get the server socket associated with this callback xprt */ xprt = req->rq_xprt->bc_xprt; - svsk = container_of(xprt, struct svc_sock, sk_xprt); /* * Grab the mutex to serialize data as the connection is shared -- cgit v0.10.2 From ada8e20d044c0fa5610e504ce6fb4578ebd3edd9 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 15 Dec 2012 17:12:14 -0500 Subject: NFS: Don't use SetPageError in the NFS writeback code The writeback code is already capable of passing errors back to user space by means of the open_context->error. In the case of ENOSPC, Neil Brown is reporting seeing 2 errors being returned. Neil writes: "e.g. if /mnt2/ if an nfs mounted filesystem that has no space then strace dd if=/dev/zero conv=fsync >> /mnt2/afile count=1 reported Input/output error and the relevant parts of the strace output are: write(1, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 512) = 512 fsync(1) = -1 EIO (Input/output error) close(1) = -1 ENOSPC (No space left on device)" Neil then shows that the duplication of error messages appears to be due to the use of the PageError() mechanism, which causes filemap_fdatawait_range to return the extra EIO. The regression was introduced by commit 7b281ee026552f10862b617a2a51acf49c829554 (NFS: fsync() must exit with an error if page writeback failed). Fix this by removing the call to SetPageError(), and just relying on open_context->error reporting the ENOSPC back to fsync(). Reported-by: Neil Brown Tested-by: Neil Brown Signed-off-by: Trond Myklebust Cc: stable@vger.kernel.org [3.6+] diff --git a/fs/nfs/write.c b/fs/nfs/write.c index f608ca6..5209916 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -202,7 +202,6 @@ out: /* A writeback failed: mark the page as bad, and invalidate the page cache */ static void nfs_set_pageerror(struct page *page) { - SetPageError(page); nfs_zap_mapping(page_file_mapping(page)->host, page_file_mapping(page)); } -- cgit v0.10.2 From cd6c5968582a273561464fe6b1e8cc8214be02df Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 17 Dec 2012 20:18:52 +0300 Subject: SUNRPC: continue run over clients list on PipeFS event instead of break There are SUNRPC clients, which program doesn't have pipe_dir_name. These clients can be skipped on PipeFS events, because nothing have to be created or destroyed. But instead of breaking in case of such a client was found, search for suitable client over clients list have to be continued. Otherwise some clients could not be covered by PipeFS event handler. Signed-off-by: Stanislav Kinsbursky Cc: stable@vger.kernel.org [>= v3.4] Signed-off-by: Trond Myklebust diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 55e174f..822f020 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -237,7 +237,7 @@ static struct rpc_clnt *rpc_get_client_for_event(struct net *net, int event) spin_lock(&sn->rpc_client_lock); list_for_each_entry(clnt, &sn->all_clients, cl_clients) { if (clnt->cl_program->pipe_dir_name == NULL) - break; + continue; if (rpc_clnt_skip_event(clnt, event)) continue; if (atomic_inc_not_zero(&clnt->cl_count) == 0) -- cgit v0.10.2