From b14b7979d70c19b7fa7c4d3244a4ea5916d77492 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 12 Jul 2013 12:31:45 -0400 Subject: NFS: Fix return type of nfs4_end_drain_session() stub Clean up: when NFSv4.1 support is compiled out, nfs4_end_drain_session() becomes a stub. Make the synopsis of the stub match the synopsis of the real version of the function. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/nfs/nfs4state.c') diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index e22862f13564..ad1a7533ce6d 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -2116,7 +2116,7 @@ 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 void nfs4_end_drain_session(struct nfs_client *clp) { } static int nfs4_bind_conn_to_session(struct nfs_client *clp) { -- cgit v1.2.3 From d688f7b8f62857c252b886fa16e8b38b83cfaf7e Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 24 Jul 2013 12:28:28 -0400 Subject: NFS: Use root's credential for lease management when keytab is missing Commit 05f4c350 "NFS: Discover NFSv4 server trunking when mounting" Fri Sep 14 17:24:32 2012 introduced Uniform Client String support, which forces our NFS client to establish a client ID immediately during a mount operation rather than waiting until a user wants to open a file. Normally machine credentials (eg. from a keytab) are used to perform a mount operation that is protected by Kerberos. Before 05fc350, SETCLIENTID used a machine credential, or fell back to a regular user's credential if no keytab is available. On clients that don't have a keytab, performing SETCLIENTID early means there's no user credential to fall back on, since no regular user has kinit'd yet. 05f4c350 seems to have broken the ability to mount with sec=krb5 on clients that don't have a keytab in kernels 3.7 - 3.10. To address this regression, commit 4edaa308 (NFS: Use "krb5i" to establish NFSv4 state whenever possible), Sat Mar 16 15:56:20 2013, was merged in 3.10. This commit forces the NFS client to fall back to AUTH_SYS for lease management operations if no keytab is available. Neil Brown noticed that, since root is required to kinit to do a sec=krb5 mount when a client doesn't have a keytab, we can try to use root's Kerberos credential before AUTH_SYS. Now, when determining a principal and flavor to use for lease management, the NFS client tries in this order: 1. Flavor: AUTH_GSS, krb5i Principal: service principal (via keytab) 2. Flavor: AUTH_GSS, krb5i Principal: user principal established for UID 0 (via kinit) 3. Flavor: AUTH_SYS Principal: UID 0 / GID 0 Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/nfs4state.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'fs/nfs/nfs4state.c') diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index ad1a7533ce6d..f27760b1b06f 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -154,6 +154,19 @@ struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp) return cred; } +static void nfs4_root_machine_cred(struct nfs_client *clp) +{ + struct rpc_cred *cred, *new; + + new = rpc_lookup_machine_cred(NULL); + spin_lock(&clp->cl_lock); + cred = clp->cl_machine_cred; + clp->cl_machine_cred = new; + spin_unlock(&clp->cl_lock); + if (cred != NULL) + put_rpccred(cred); +} + static struct rpc_cred * nfs4_get_renew_cred_server_locked(struct nfs_server *server) { @@ -1896,7 +1909,11 @@ again: __func__, status); goto again; case -EACCES: - if (i++) + if (i++ == 0) { + nfs4_root_machine_cred(clp); + goto again; + } + if (i > 2) break; case -NFS4ERR_CLID_INUSE: case -NFS4ERR_WRONGSEC: -- cgit v1.2.3 From 73d8bde5e4b658948be0d3df51b323ed323997a2 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 24 Jul 2013 12:28:37 -0400 Subject: NFS: Never use user credentials for lease renewal Never try to use a non-UID 0 user credential for lease management, as that credential can change out from under us. The server will block NFSv4 lease recovery with NFS4ERR_CLID_INUSE. Since the mechanism to acquire a credential for lease management is now the same for all minor versions, replace the minor version- specific callout with a single function. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 4 +--- fs/nfs/nfs4proc.c | 6 +----- fs/nfs/nfs4session.c | 2 +- fs/nfs/nfs4state.c | 57 ++++++++-------------------------------------------- 4 files changed, 11 insertions(+), 58 deletions(-) (limited to 'fs/nfs/nfs4state.c') diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index ee81e354bce7..d7bb59d5dd9c 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -193,7 +193,6 @@ struct nfs4_state_recovery_ops { int (*recover_open)(struct nfs4_state_owner *, struct nfs4_state *); int (*recover_lock)(struct nfs4_state *, struct file_lock *); int (*establish_clid)(struct nfs_client *, struct rpc_cred *); - struct rpc_cred * (*get_clid_cred)(struct nfs_client *); int (*reclaim_complete)(struct nfs_client *, struct rpc_cred *); int (*detect_trunking)(struct nfs_client *, struct nfs_client **, struct rpc_cred *); @@ -319,7 +318,7 @@ extern void nfs4_kill_renewd(struct nfs_client *); 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_clid_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, @@ -327,7 +326,6 @@ int nfs4_discover_server_trunking(struct nfs_client *clp, 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_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); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 0e64cccda0ac..a187f4d83e98 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6069,7 +6069,7 @@ int nfs4_destroy_clientid(struct nfs_client *clp) goto out; if (clp->cl_preserve_clid) goto out; - cred = nfs4_get_exchange_id_cred(clp); + cred = nfs4_get_clid_cred(clp); ret = nfs4_proc_destroy_clientid(clp, cred); if (cred) put_rpccred(cred); @@ -7363,7 +7363,6 @@ static const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = { .recover_open = nfs4_open_reclaim, .recover_lock = nfs4_lock_reclaim, .establish_clid = nfs4_init_clientid, - .get_clid_cred = nfs4_get_setclientid_cred, .detect_trunking = nfs40_discover_server_trunking, }; @@ -7374,7 +7373,6 @@ static const struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = { .recover_open = nfs4_open_reclaim, .recover_lock = nfs4_lock_reclaim, .establish_clid = nfs41_init_clientid, - .get_clid_cred = nfs4_get_exchange_id_cred, .reclaim_complete = nfs41_proc_reclaim_complete, .detect_trunking = nfs41_discover_server_trunking, }; @@ -7386,7 +7384,6 @@ static const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = { .recover_open = nfs4_open_expired, .recover_lock = nfs4_lock_expired, .establish_clid = nfs4_init_clientid, - .get_clid_cred = nfs4_get_setclientid_cred, }; #if defined(CONFIG_NFS_V4_1) @@ -7396,7 +7393,6 @@ static const struct nfs4_state_recovery_ops nfs41_nograce_recovery_ops = { .recover_open = nfs41_open_expired, .recover_lock = nfs41_lock_expired, .establish_clid = nfs41_init_clientid, - .get_clid_cred = nfs4_get_exchange_id_cred, }; #endif /* CONFIG_NFS_V4_1 */ diff --git a/fs/nfs/nfs4session.c b/fs/nfs/nfs4session.c index 36e21cb29d65..202e3633d555 100644 --- a/fs/nfs/nfs4session.c +++ b/fs/nfs/nfs4session.c @@ -441,7 +441,7 @@ void nfs4_destroy_session(struct nfs4_session *session) struct rpc_xprt *xprt; struct rpc_cred *cred; - cred = nfs4_get_exchange_id_cred(session->clp); + cred = nfs4_get_clid_cred(session->clp); nfs4_proc_destroy_session(session, cred); if (cred) put_rpccred(cred); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index f27760b1b06f..6818964bb7c0 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -352,62 +352,21 @@ int nfs41_discover_server_trunking(struct nfs_client *clp, return nfs41_walk_client_list(clp, result, cred); } -struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp) -{ - struct rpc_cred *cred; - - spin_lock(&clp->cl_lock); - cred = nfs4_get_machine_cred_locked(clp); - spin_unlock(&clp->cl_lock); - return cred; -} - #endif /* CONFIG_NFS_V4_1 */ -static struct rpc_cred * -nfs4_get_setclientid_cred_server(struct nfs_server *server) -{ - struct nfs_client *clp = server->nfs_client; - struct rpc_cred *cred = NULL; - struct nfs4_state_owner *sp; - struct rb_node *pos; - - spin_lock(&clp->cl_lock); - pos = rb_first(&server->state_owners); - if (pos != NULL) { - sp = rb_entry(pos, struct nfs4_state_owner, so_server_node); - cred = get_rpccred(sp->so_cred); - } - spin_unlock(&clp->cl_lock); - return cred; -} - /** - * nfs4_get_setclientid_cred - Acquire credential for a setclientid operation + * nfs4_get_clid_cred - Acquire credential for a setclientid operation * @clp: client state handle * * Returns an rpc_cred with reference count bumped, or NULL. */ -struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp) +struct rpc_cred *nfs4_get_clid_cred(struct nfs_client *clp) { - struct nfs_server *server; struct rpc_cred *cred; spin_lock(&clp->cl_lock); cred = nfs4_get_machine_cred_locked(clp); spin_unlock(&clp->cl_lock); - if (cred != NULL) - goto out; - - rcu_read_lock(); - list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { - cred = nfs4_get_setclientid_cred_server(server); - if (cred != NULL) - break; - } - rcu_read_unlock(); - -out: return cred; } @@ -1631,7 +1590,7 @@ static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp) if (!nfs4_state_clear_reclaim_reboot(clp)) return; ops = clp->cl_mvops->reboot_recovery_ops; - cred = ops->get_clid_cred(clp); + cred = nfs4_get_clid_cred(clp); nfs4_reclaim_complete(clp, ops, cred); put_rpccred(cred); } @@ -1745,7 +1704,7 @@ static int nfs4_check_lease(struct nfs_client *clp) cred = ops->get_state_renewal_cred_locked(clp); spin_unlock(&clp->cl_lock); if (cred == NULL) { - cred = nfs4_get_setclientid_cred(clp); + cred = nfs4_get_clid_cred(clp); status = -ENOKEY; if (cred == NULL) goto out; @@ -1817,7 +1776,7 @@ static int nfs4_establish_lease(struct nfs_client *clp) clp->cl_mvops->reboot_recovery_ops; int status; - cred = ops->get_clid_cred(clp); + cred = nfs4_get_clid_cred(clp); if (cred == NULL) return -ENOENT; status = ops->establish_clid(clp, cred); @@ -1891,7 +1850,7 @@ int nfs4_discover_server_trunking(struct nfs_client *clp, mutex_lock(&nfs_clid_init_mutex); again: status = -ENOENT; - cred = ops->get_clid_cred(clp); + cred = nfs4_get_clid_cred(clp); if (cred == NULL) goto out_unlock; @@ -2069,7 +2028,7 @@ static int nfs4_reset_session(struct nfs_client *clp) if (!nfs4_has_session(clp)) return 0; nfs4_begin_drain_session(clp); - cred = nfs4_get_exchange_id_cred(clp); + cred = nfs4_get_clid_cred(clp); status = nfs4_proc_destroy_session(clp->cl_session, cred); switch (status) { case 0: @@ -2112,7 +2071,7 @@ static int nfs4_bind_conn_to_session(struct nfs_client *clp) if (!nfs4_has_session(clp)) return 0; nfs4_begin_drain_session(clp); - cred = nfs4_get_exchange_id_cred(clp); + cred = nfs4_get_clid_cred(clp); ret = nfs4_proc_bind_conn_to_session(clp, cred); if (cred) put_rpccred(cred); -- cgit v1.2.3 From 6686390bab6a0e049fa7040631aee08b35a55293 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 12 Aug 2013 16:52:47 +1000 Subject: NFS: remove incorrect "Lock reclaim failed!" warning. After reclaiming state that was lost, the NFS client tries to reclaim any locks, and then checks that each one has NFS_LOCK_INITIALIZED set (which means that the server has confirmed the lock). However if the client holds a delegation, nfs_reclaim_locks() simply aborts (or more accurately it called nfs_lock_reclaim() and that returns without doing anything). This is because when a delegation is held, the server doesn't need to know about locks. So if a delegation is held, NFS_LOCK_INITIALIZED is not expected, and its absence is certainly not an error. So don't print the warnings if NFS_DELGATED_STATE is set. Signed-off-by: NeilBrown Signed-off-by: Trond Myklebust --- fs/nfs/nfs4state.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'fs/nfs/nfs4state.c') diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 6818964bb7c0..25b7cf6a91d9 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1415,14 +1415,16 @@ restart: if (status >= 0) { status = nfs4_reclaim_locks(state, ops); if (status >= 0) { - spin_lock(&state->state_lock); - list_for_each_entry(lock, &state->lock_states, ls_locks) { - if (!test_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags)) - pr_warn_ratelimited("NFS: " - "%s: Lock reclaim " - "failed!\n", __func__); + if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) { + spin_lock(&state->state_lock); + list_for_each_entry(lock, &state->lock_states, ls_locks) { + if (!test_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags)) + pr_warn_ratelimited("NFS: " + "%s: Lock reclaim " + "failed!\n", __func__); + } + spin_unlock(&state->state_lock); } - spin_unlock(&state->state_lock); nfs4_put_open_state(state); spin_lock(&sp->so_lock); goto restart; -- cgit v1.2.3 From 9d33059c1b76d77004753799479af6d359d83217 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 9 Aug 2013 12:48:44 -0400 Subject: NFS: Enable slot table helpers for NFSv4.0 I'd like to re-use NFSv4.1's slot table machinery for NFSv4.0 transport blocking. Re-organize some of nfs4session.c so the slot table code is built even when NFS_V4_1 is disabled. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/Makefile | 6 +++--- fs/nfs/nfs4session.c | 29 +++++++++++++++++++++-------- fs/nfs/nfs4session.h | 28 +++++++++++++--------------- fs/nfs/nfs4state.c | 9 --------- 4 files changed, 37 insertions(+), 35 deletions(-) (limited to 'fs/nfs/nfs4state.c') diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 7238c4b4c70c..03192a66c143 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -23,11 +23,11 @@ obj-$(CONFIG_NFS_V4) += nfsv4.o CFLAGS_nfs4trace.o += -I$(src) 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 dns_resolve.o \ - nfs4trace.o + nfs4namespace.o nfs4getroot.o nfs4client.o nfs4session.o \ + dns_resolve.o nfs4trace.o nfsv4-$(CONFIG_NFS_USE_LEGACY_DNS) += cache_lib.o nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o -nfsv4-$(CONFIG_NFS_V4_1) += nfs4session.o pnfs.o pnfs_dev.o +nfsv4-$(CONFIG_NFS_V4_1) += 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/nfs4session.c b/fs/nfs/nfs4session.c index 0161ad26ee84..746675b74624 100644 --- a/fs/nfs/nfs4session.c +++ b/fs/nfs/nfs4session.c @@ -44,6 +44,17 @@ static void nfs4_shrink_slot_table(struct nfs4_slot_table *tbl, u32 newsize) } } +/** + * nfs4_slot_tbl_drain_complete - wake waiters when drain is complete + * @tbl - controlling slot table + * + */ +void nfs4_slot_tbl_drain_complete(struct nfs4_slot_table *tbl) +{ + if (nfs4_slot_tbl_draining(tbl)) + complete(&tbl->complete); +} + /* * nfs4_free_slot - free a slot and efficiently update slot table. * @@ -212,13 +223,6 @@ out: 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); -} - static bool nfs41_assign_slot(struct rpc_task *task, void *pslot) { struct nfs4_sequence_args *args = task->tk_msg.rpc_argp; @@ -383,6 +387,15 @@ void nfs41_update_target_slotid(struct nfs4_slot_table *tbl, spin_unlock(&tbl->slot_tbl_lock); } +#if defined(CONFIG_NFS_V4_1) + +/* 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 */ @@ -513,4 +526,4 @@ int nfs4_init_ds_session(struct nfs_client *clp, unsigned long lease_time) } EXPORT_SYMBOL_GPL(nfs4_init_ds_session); - +#endif /* defined(CONFIG_NFS_V4_1) */ diff --git a/fs/nfs/nfs4session.h b/fs/nfs/nfs4session.h index e3e6ce357f69..e069cf2d8c5f 100644 --- a/fs/nfs/nfs4session.h +++ b/fs/nfs/nfs4session.h @@ -72,15 +72,24 @@ enum nfs4_session_state { NFS4_SESSION_INITING, }; -#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 nfs4_slot_tbl_drain_complete(struct nfs4_slot_table *tbl); extern void nfs41_update_target_slotid(struct nfs4_slot_table *tbl, struct nfs4_slot *slot, struct nfs4_sequence_res *res); +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); + +static inline bool nfs4_slot_tbl_draining(struct nfs4_slot_table *tbl) +{ + return !!test_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state); +} + +#if defined(CONFIG_NFS_V4_1) +extern void nfs41_set_target_slotid(struct nfs4_slot_table *tbl, + u32 target_highest_slotid); extern int nfs4_setup_session_slot_tables(struct nfs4_session *ses); @@ -89,17 +98,6 @@ extern void nfs4_destroy_session(struct nfs4_session *session); extern int nfs4_init_session(struct nfs_client *clp); extern int nfs4_init_ds_session(struct nfs_client *, unsigned long); -extern void nfs4_slot_tbl_drain_complete(struct nfs4_slot_table *tbl); - -static inline bool nfs4_slot_tbl_draining(struct nfs4_slot_table *tbl) -{ - return !!test_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_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 25b7cf6a91d9..f5b043474c55 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -260,15 +260,6 @@ static void nfs4_end_drain_session(struct nfs_client *clp) } } -/* - * Signal state manager thread if session fore channel is drained - */ -void nfs4_slot_tbl_drain_complete(struct nfs4_slot_table *tbl) -{ - if (nfs4_slot_tbl_draining(tbl)) - complete(&tbl->complete); -} - static int nfs4_drain_slot_tbl(struct nfs4_slot_table *tbl) { set_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state); -- cgit v1.2.3 From 2cf8bca8b9980c58eab9f2f47586eb16a6e63300 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 9 Aug 2013 12:49:56 -0400 Subject: NFS: Update session draining barriers for NFSv4.0 transport blocking Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/nfs4state.c | 61 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 27 deletions(-) (limited to 'fs/nfs/nfs4state.c') diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index f5b043474c55..4e956669d4fb 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -215,32 +215,6 @@ out: return cred; } -#if defined(CONFIG_NFS_V4_1) - -static int nfs41_setup_state_renewal(struct nfs_client *clp) -{ - int status; - struct nfs_fsinfo fsinfo; - - if (!test_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state)) { - nfs4_schedule_state_renewal(clp); - return 0; - } - - status = nfs4_proc_get_lease_time(clp, &fsinfo); - if (status == 0) { - /* Update lease time and schedule renewal */ - spin_lock(&clp->cl_lock); - clp->cl_lease_time = fsinfo.lease_time * HZ; - clp->cl_last_renewal = jiffies; - spin_unlock(&clp->cl_lock); - - nfs4_schedule_state_renewal(clp); - } - - return status; -} - static void nfs4_end_drain_slot_table(struct nfs4_slot_table *tbl) { if (test_and_clear_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state)) { @@ -254,6 +228,11 @@ static void nfs4_end_drain_session(struct nfs_client *clp) { struct nfs4_session *ses = clp->cl_session; + if (clp->cl_slot_tbl) { + nfs4_end_drain_slot_table(clp->cl_slot_tbl); + return; + } + if (ses != NULL) { nfs4_end_drain_slot_table(&ses->bc_slot_table); nfs4_end_drain_slot_table(&ses->fc_slot_table); @@ -278,6 +257,9 @@ static int nfs4_begin_drain_session(struct nfs_client *clp) struct nfs4_session *ses = clp->cl_session; int ret = 0; + if (clp->cl_slot_tbl) + return nfs4_drain_slot_tbl(clp->cl_slot_tbl); + /* back channel */ ret = nfs4_drain_slot_tbl(&ses->bc_slot_table); if (ret) @@ -286,6 +268,32 @@ static int nfs4_begin_drain_session(struct nfs_client *clp) return nfs4_drain_slot_tbl(&ses->fc_slot_table); } +#if defined(CONFIG_NFS_V4_1) + +static int nfs41_setup_state_renewal(struct nfs_client *clp) +{ + int status; + struct nfs_fsinfo fsinfo; + + if (!test_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state)) { + nfs4_schedule_state_renewal(clp); + return 0; + } + + status = nfs4_proc_get_lease_time(clp, &fsinfo); + if (status == 0) { + /* Update lease time and schedule renewal */ + spin_lock(&clp->cl_lock); + clp->cl_lease_time = fsinfo.lease_time * HZ; + clp->cl_last_renewal = jiffies; + spin_unlock(&clp->cl_lock); + + nfs4_schedule_state_renewal(clp); + } + + return status; +} + static void nfs41_finish_session_reset(struct nfs_client *clp) { clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); @@ -2085,7 +2093,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 void nfs4_end_drain_session(struct nfs_client *clp) { } static int nfs4_bind_conn_to_session(struct nfs_client *clp) { -- cgit v1.2.3 From b6a85258d8223a87e7ecf25791e634617a159f79 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 4 Sep 2013 12:26:05 -0400 Subject: NFS: Fix warning introduced by NFSv4.0 transport blocking patches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When CONFIG_NFS_V4_1 is not enabled, gcc emits this warning: linux/fs/nfs/nfs4state.c:255:12: warning: ‘nfs4_begin_drain_session’ defined but not used [-Wunused-function] static int nfs4_begin_drain_session(struct nfs_client *clp) ^ Eventually NFSv4.0 migration recovery will invoke this function, but that has not yet been merged. Hide nfs4_begin_drain_session() behind CONFIG_NFS_V4_1 for now. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/nfs4state.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/nfs/nfs4state.c') diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 4e956669d4fb..da608ee8d5ff 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -239,6 +239,8 @@ static void nfs4_end_drain_session(struct nfs_client *clp) } } +#if defined(CONFIG_NFS_V4_1) + static int nfs4_drain_slot_tbl(struct nfs4_slot_table *tbl) { set_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state); @@ -268,8 +270,6 @@ static int nfs4_begin_drain_session(struct nfs_client *clp) return nfs4_drain_slot_tbl(&ses->fc_slot_table); } -#if defined(CONFIG_NFS_V4_1) - static int nfs41_setup_state_renewal(struct nfs_client *clp) { int status; -- cgit v1.2.3 From ef1820f9be27b6ad158f433ab38002ab8131db4d Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 4 Sep 2013 17:04:49 +1000 Subject: NFSv4: Don't try to recover NFSv4 locks when they are lost. When an NFSv4 client loses contact with the server it can lose any locks that it holds. Currently when it reconnects to the server it simply tries to reclaim those locks. This might succeed even though some other client has held and released a lock in the mean time. So the first client might think the file is unchanged, but it isn't. This isn't good. If, when recovery happens, the locks cannot be claimed because some other client still holds the lock, then we get a message in the kernel logs, but the client can still write. So two clients can both think they have a lock and can both write at the same time. This is equally not good. There was a patch a while ago http://comments.gmane.org/gmane.linux.nfs/41917 which tried to address some of this, but it didn't seem to go anywhere. That patch would also send a signal to the process. That might be useful but for now this patch just causes writes to fail. For NFSv4 (unlike v2/v3) there is a strong link between the lock and the write request so we can fairly easily fail any IO of the lock is gone. While some applications might not expect this, it is still safer than allowing the write to succeed. Because this is a fairly big change in behaviour a module parameter, "recover_locks", is introduced which defaults to true (the current behaviour) but can be set to "false" to tell the client not to try to recover things that were lost. Signed-off-by: NeilBrown Signed-off-by: Trond Myklebust --- fs/nfs/nfs3proc.c | 6 ++++-- fs/nfs/nfs4_fs.h | 1 + fs/nfs/nfs4proc.c | 34 ++++++++++++++++++++++++++-------- fs/nfs/nfs4state.c | 14 +++++++++++--- fs/nfs/proc.c | 6 ++++-- fs/nfs/read.c | 7 ++++--- fs/nfs/write.c | 7 ++++--- include/linux/nfs_xdr.h | 4 ++-- 8 files changed, 56 insertions(+), 23 deletions(-) (limited to 'fs/nfs/nfs4state.c') diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 1db588a3f08b..90cb10d7b693 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -826,9 +826,10 @@ static void nfs3_proc_read_setup(struct nfs_read_data *data, struct rpc_message msg->rpc_proc = &nfs3_procedures[NFS3PROC_READ]; } -static void nfs3_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) +static int nfs3_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) { rpc_call_start(task); + return 0; } static int nfs3_write_done(struct rpc_task *task, struct nfs_write_data *data) @@ -847,9 +848,10 @@ static void nfs3_proc_write_setup(struct nfs_write_data *data, struct rpc_messag msg->rpc_proc = &nfs3_procedures[NFS3PROC_WRITE]; } -static void nfs3_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) +static int nfs3_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) { rpc_call_start(task); + return 0; } static void nfs3_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index af2d5bf043f0..64118316a407 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -133,6 +133,7 @@ struct nfs4_lock_state { struct list_head ls_locks; /* Other lock stateids */ struct nfs4_state * ls_state; /* Pointer to open state */ #define NFS_LOCK_INITIALIZED 0 +#define NFS_LOCK_LOST 1 unsigned long ls_flags; struct nfs_seqid_counter ls_seqid; nfs4_stateid ls_stateid; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 0122919a311a..1eb694e0f305 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4028,15 +4028,19 @@ static void nfs4_proc_read_setup(struct nfs_read_data *data, struct rpc_message nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0); } -static void nfs4_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) +static int 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; - nfs4_set_rw_stateid(&data->args.stateid, data->args.context, - data->args.lock_context, FMODE_READ); + return 0; + if (nfs4_set_rw_stateid(&data->args.stateid, data->args.context, + data->args.lock_context, FMODE_READ) == -EIO) + return -EIO; + if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags))) + return -EIO; + return 0; } static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_write_data *data) @@ -4112,15 +4116,19 @@ static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_messag nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1); } -static void nfs4_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) +static int 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; - nfs4_set_rw_stateid(&data->args.stateid, data->args.context, - data->args.lock_context, FMODE_WRITE); + return 0; + if (nfs4_set_rw_stateid(&data->args.stateid, data->args.context, + data->args.lock_context, FMODE_WRITE) == -EIO) + return -EIO; + if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags))) + return -EIO; + return 0; } static void nfs4_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data) @@ -5515,6 +5523,12 @@ static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request return err; } +bool recover_locks = true; +module_param(recover_locks, bool, 0644); +MODULE_PARM_DESC(recover_locks, + "If the server reports that a lock might be lost, " + "try to recovery it risking corruption."); + static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request) { struct nfs_server *server = NFS_SERVER(state->inode); @@ -5526,6 +5540,10 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request err = nfs4_set_lock_state(state, request); if (err != 0) return err; + if (!recover_locks) { + set_bit(NFS_LOCK_LOST, &request->fl_u.nfs4_fl.owner->ls_flags); + return 0; + } do { if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) return 0; diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index da608ee8d5ff..cc14cbb78b73 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -969,7 +969,9 @@ static int nfs4_copy_lock_stateid(nfs4_stateid *dst, fl_pid = lockowner->l_pid; spin_lock(&state->state_lock); lsp = __nfs4_find_lock_state(state, fl_owner, fl_pid, NFS4_ANY_LOCK_TYPE); - if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) { + if (lsp && test_bit(NFS_LOCK_LOST, &lsp->ls_flags)) + ret = -EIO; + else if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) { nfs4_stateid_copy(dst, &lsp->ls_stateid); ret = 0; smp_rmb(); @@ -1009,11 +1011,17 @@ static int nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state) int nfs4_select_rw_stateid(nfs4_stateid *dst, struct nfs4_state *state, fmode_t fmode, const struct nfs_lockowner *lockowner) { - int ret = 0; + int ret = nfs4_copy_lock_stateid(dst, state, lockowner); + if (ret == -EIO) + /* A lost lock - don't even consider delegations */ + goto out; if (nfs4_copy_delegation_stateid(dst, state->inode, fmode)) goto out; - ret = nfs4_copy_lock_stateid(dst, state, lockowner); if (ret != -ENOENT) + /* nfs4_copy_delegation_stateid() didn't over-write + * dst, so it still has the lock stateid which we now + * choose to use. + */ goto out; ret = nfs4_copy_open_stateid(dst, state); out: diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index c041c41f7a52..a8f57c728df5 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -623,9 +623,10 @@ static void nfs_proc_read_setup(struct nfs_read_data *data, struct rpc_message * msg->rpc_proc = &nfs_procedures[NFSPROC_READ]; } -static void nfs_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) +static int nfs_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data) { rpc_call_start(task); + return 0; } static int nfs_write_done(struct rpc_task *task, struct nfs_write_data *data) @@ -644,9 +645,10 @@ static void nfs_proc_write_setup(struct nfs_write_data *data, struct rpc_message msg->rpc_proc = &nfs_procedures[NFSPROC_WRITE]; } -static void nfs_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) +static int nfs_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data) { rpc_call_start(task); + return 0; } static void nfs_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data) diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 70a26c651f09..31db5c366b81 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -513,9 +513,10 @@ static void nfs_readpage_release_common(void *calldata) void nfs_read_prepare(struct rpc_task *task, void *calldata) { struct nfs_read_data *data = calldata; - NFS_PROTO(data->header->inode)->read_rpc_prepare(task, data); - if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags))) - rpc_exit(task, -EIO); + int err; + err = NFS_PROTO(data->header->inode)->read_rpc_prepare(task, data); + if (err) + rpc_exit(task, err); } static const struct rpc_call_ops nfs_read_common_ops = { diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 94eb4504731a..379450c8d04b 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1294,9 +1294,10 @@ EXPORT_SYMBOL_GPL(nfs_pageio_reset_write_mds); void nfs_write_prepare(struct rpc_task *task, void *calldata) { struct nfs_write_data *data = calldata; - NFS_PROTO(data->header->inode)->write_rpc_prepare(task, data); - if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags))) - rpc_exit(task, -EIO); + int err; + err = NFS_PROTO(data->header->inode)->write_rpc_prepare(task, data); + if (err) + rpc_exit(task, err); } void nfs_commit_prepare(struct rpc_task *task, void *calldata) diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index ddc3e32178c3..7c3956d65b3e 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1419,12 +1419,12 @@ struct nfs_rpc_ops { void (*read_setup) (struct nfs_read_data *, struct rpc_message *); void (*read_pageio_init)(struct nfs_pageio_descriptor *, struct inode *, const struct nfs_pgio_completion_ops *); - void (*read_rpc_prepare)(struct rpc_task *, struct nfs_read_data *); + int (*read_rpc_prepare)(struct rpc_task *, struct nfs_read_data *); int (*read_done) (struct rpc_task *, struct nfs_read_data *); void (*write_setup) (struct nfs_write_data *, struct rpc_message *); void (*write_pageio_init)(struct nfs_pageio_descriptor *, struct inode *, int, const struct nfs_pgio_completion_ops *); - void (*write_rpc_prepare)(struct rpc_task *, struct nfs_write_data *); + int (*write_rpc_prepare)(struct rpc_task *, struct nfs_write_data *); int (*write_done) (struct rpc_task *, struct nfs_write_data *); void (*commit_setup) (struct nfs_commit_data *, struct rpc_message *); void (*commit_rpc_prepare)(struct rpc_task *, struct nfs_commit_data *); -- cgit v1.2.3