Skip to content

Commit

Permalink
Merge tag 'nfs-for-6.6-3' of git://git.linux-nfs.org/projects/anna/li…
Browse files Browse the repository at this point in the history
…nux-nfs

Pull NFS client fixes from Anna Schumaker:
 "Stable fixes:
   - Revert "SUNRPC dont update timeout value on connection reset"
   - NFSv4: Fix a state manager thread deadlock regression

  Fixes:
   - Fix potential NULL pointer dereference in nfs_inode_remove_request()
   - Fix rare NULL pointer dereference in xs_tcp_tls_setup_socket()
   - Fix long delay before failing a TLS mount when server does not
     support TLS
   - Fix various NFS state manager issues"

* tag 'nfs-for-6.6-3' of git://git.linux-nfs.org/projects/anna/linux-nfs:
  nfs: decrement nrequests counter before releasing the req
  SUNRPC/TLS: Lock the lower_xprt during the tls handshake
  Revert "SUNRPC dont update timeout value on connection reset"
  NFSv4: Fix a state manager thread deadlock regression
  NFSv4: Fix a nfs4_state_manager() race
  SUNRPC: Fail quickly when server does not recognize TLS
  • Loading branch information
torvalds committed Oct 3, 2023
2 parents 7cccbec + dd1b202 commit cbf3a2c
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 22 deletions.
4 changes: 3 additions & 1 deletion fs/nfs/nfs4proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -10622,7 +10622,9 @@ static void nfs4_disable_swap(struct inode *inode)
*/
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;

nfs4_schedule_state_manager(clp);
set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
wake_up_var(&clp->cl_state);
}

static const struct inode_operations nfs4_dir_inode_operations = {
Expand Down
45 changes: 33 additions & 12 deletions fs/nfs/nfs4state.c
Original file line number Diff line number Diff line change
Expand Up @@ -1209,16 +1209,26 @@ void nfs4_schedule_state_manager(struct nfs_client *clp)
{
struct task_struct *task;
char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1];
struct rpc_clnt *clnt = clp->cl_rpcclient;
bool swapon = false;

if (clp->cl_rpcclient->cl_shutdown)
if (clnt->cl_shutdown)
return;

set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
if (test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state) != 0) {
wake_up_var(&clp->cl_state);
return;

if (atomic_read(&clnt->cl_swapper)) {
swapon = !test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE,
&clp->cl_state);
if (!swapon) {
wake_up_var(&clp->cl_state);
return;
}
}
set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state);

if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
return;

__module_get(THIS_MODULE);
refcount_inc(&clp->cl_count);

Expand All @@ -1235,8 +1245,9 @@ void nfs4_schedule_state_manager(struct nfs_client *clp)
__func__, PTR_ERR(task));
if (!nfs_client_init_is_complete(clp))
nfs_mark_client_ready(clp, PTR_ERR(task));
if (swapon)
clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
nfs4_clear_state_manager_bit(clp);
clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
nfs_put_client(clp);
module_put(THIS_MODULE);
}
Expand Down Expand Up @@ -2703,6 +2714,13 @@ static void nfs4_state_manager(struct nfs_client *clp)
nfs4_end_drain_session(clp);
nfs4_clear_state_manager_bit(clp);

if (test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state) &&
!test_and_set_bit(NFS4CLNT_MANAGER_RUNNING,
&clp->cl_state)) {
memflags = memalloc_nofs_save();
continue;
}

if (!test_and_set_bit(NFS4CLNT_RECALL_RUNNING, &clp->cl_state)) {
if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) {
nfs_client_return_marked_delegations(clp);
Expand Down Expand Up @@ -2741,22 +2759,25 @@ static int nfs4_run_state_manager(void *ptr)

allow_signal(SIGKILL);
again:
set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state);
nfs4_state_manager(clp);
if (atomic_read(&cl->cl_swapper)) {

if (test_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state) &&
!test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state)) {
wait_var_event_interruptible(&clp->cl_state,
test_bit(NFS4CLNT_RUN_MANAGER,
&clp->cl_state));
if (atomic_read(&cl->cl_swapper) &&
test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state))
if (!atomic_read(&cl->cl_swapper))
clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
if (refcount_read(&clp->cl_count) > 1 && !signalled() &&
!test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state))
goto again;
/* Either no longer a swapper, or were signalled */
clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);
}
clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state);

if (refcount_read(&clp->cl_count) > 1 && !signalled() &&
test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state) &&
!test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state))
!test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state))
goto again;

nfs_put_client(clp);
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -802,8 +802,8 @@ static void nfs_inode_remove_request(struct nfs_page *req)
}

if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags)) {
nfs_release_request(req);
atomic_long_dec(&NFS_I(nfs_page_to_inode(req))->nrequests);
nfs_release_request(req);
}
}

Expand Down
11 changes: 8 additions & 3 deletions net/sunrpc/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -769,9 +769,14 @@ int rpcauth_wrap_req(struct rpc_task *task, struct xdr_stream *xdr)
* @task: controlling RPC task
* @xdr: xdr_stream containing RPC Reply header
*
* On success, @xdr is updated to point past the verifier and
* zero is returned. Otherwise, @xdr is in an undefined state
* and a negative errno is returned.
* Return values:
* %0: Verifier is valid. @xdr now points past the verifier.
* %-EIO: Verifier is corrupted or message ended early.
* %-EACCES: Verifier is intact but not valid.
* %-EPROTONOSUPPORT: Server does not support the requested auth type.
*
* When a negative errno is returned, @xdr is left in an undefined
* state.
*/
int
rpcauth_checkverf(struct rpc_task *task, struct xdr_stream *xdr)
Expand Down
4 changes: 2 additions & 2 deletions net/sunrpc/auth_tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ static int tls_validate(struct rpc_task *task, struct xdr_stream *xdr)
if (*p != rpc_auth_null)
return -EIO;
if (xdr_stream_decode_opaque_inline(xdr, &str, starttls_len) != starttls_len)
return -EIO;
return -EPROTONOSUPPORT;
if (memcmp(str, starttls_token, starttls_len))
return -EIO;
return -EPROTONOSUPPORT;
return 0;
}

Expand Down
13 changes: 10 additions & 3 deletions net/sunrpc/clnt.c
Original file line number Diff line number Diff line change
Expand Up @@ -2476,8 +2476,7 @@ call_status(struct rpc_task *task)
goto out_exit;
}
task->tk_action = call_encode;
if (status != -ECONNRESET && status != -ECONNABORTED)
rpc_check_timeout(task);
rpc_check_timeout(task);
return;
out_exit:
rpc_call_rpcerror(task, status);
Expand Down Expand Up @@ -2725,7 +2724,15 @@ rpc_decode_header(struct rpc_task *task, struct xdr_stream *xdr)

out_verifier:
trace_rpc_bad_verifier(task);
goto out_garbage;
switch (error) {
case -EPROTONOSUPPORT:
goto out_err;
case -EACCES:
/* Re-encode with a fresh cred */
fallthrough;
default:
goto out_garbage;
}

out_msg_denied:
error = -EACCES;
Expand Down
6 changes: 6 additions & 0 deletions net/sunrpc/xprtsock.c
Original file line number Diff line number Diff line change
Expand Up @@ -2672,6 +2672,10 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work)
rcu_read_lock();
lower_xprt = rcu_dereference(lower_clnt->cl_xprt);
rcu_read_unlock();

if (wait_on_bit_lock(&lower_xprt->state, XPRT_LOCKED, TASK_KILLABLE))
goto out_unlock;

status = xs_tls_handshake_sync(lower_xprt, &upper_xprt->xprtsec);
if (status) {
trace_rpc_tls_not_started(upper_clnt, upper_xprt);
Expand All @@ -2681,6 +2685,7 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work)
status = xs_tcp_tls_finish_connecting(lower_xprt, upper_transport);
if (status)
goto out_close;
xprt_release_write(lower_xprt, NULL);

trace_rpc_socket_connect(upper_xprt, upper_transport->sock, 0);
if (!xprt_test_and_set_connected(upper_xprt)) {
Expand All @@ -2702,6 +2707,7 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work)
return;

out_close:
xprt_release_write(lower_xprt, NULL);
rpc_shutdown_client(lower_clnt);

/* xprt_force_disconnect() wakes tasks with a fixed tk_status code.
Expand Down

0 comments on commit cbf3a2c

Please sign in to comment.