Skip to content

Commit 718ba5b

Browse files
committed
SUNRPC: Add helpers to prevent socket create from racing
The socket lock is currently held by the task that is requesting the connection be established. While that is efficient in the case where the connection happens quickly, it is racy in the case where it doesn't. What we really want is for the connect helper to be able to block access to the socket while it is being set up. This patch does so by arranging to transfer the socket lock from the task that is requesting the connect attempt, and then releasing that lock once everything is done. This scheme also gives us automatic protection against collisions with the RPC close code, so we can kill the cancel_delayed_work_sync() call in xs_close(). Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
1 parent 6cc7e90 commit 718ba5b

File tree

3 files changed

+41
-6
lines changed

3 files changed

+41
-6
lines changed

include/linux/sunrpc/xprt.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,9 @@ void xprt_force_disconnect(struct rpc_xprt *xprt);
347347
void xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie);
348348
int xs_swapper(struct rpc_xprt *xprt, int enable);
349349

350+
bool xprt_lock_connect(struct rpc_xprt *, struct rpc_task *, void *);
351+
void xprt_unlock_connect(struct rpc_xprt *, void *);
352+
350353
/*
351354
* Reserved bit positions in xprt->state
352355
*/

net/sunrpc/xprt.c

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,37 @@ xprt_init_autodisconnect(unsigned long data)
690690
spin_unlock(&xprt->transport_lock);
691691
}
692692

693+
bool xprt_lock_connect(struct rpc_xprt *xprt,
694+
struct rpc_task *task,
695+
void *cookie)
696+
{
697+
bool ret = false;
698+
699+
spin_lock_bh(&xprt->transport_lock);
700+
if (!test_bit(XPRT_LOCKED, &xprt->state))
701+
goto out;
702+
if (xprt->snd_task != task)
703+
goto out;
704+
xprt->snd_task = cookie;
705+
ret = true;
706+
out:
707+
spin_unlock_bh(&xprt->transport_lock);
708+
return ret;
709+
}
710+
711+
void xprt_unlock_connect(struct rpc_xprt *xprt, void *cookie)
712+
{
713+
spin_lock_bh(&xprt->transport_lock);
714+
if (xprt->snd_task != cookie)
715+
goto out;
716+
if (!test_bit(XPRT_LOCKED, &xprt->state))
717+
goto out;
718+
xprt->snd_task =NULL;
719+
xprt->ops->release_xprt(xprt, NULL);
720+
out:
721+
spin_unlock_bh(&xprt->transport_lock);
722+
}
723+
693724
/**
694725
* xprt_connect - schedule a transport connect operation
695726
* @task: RPC task that is requesting the connect
@@ -712,9 +743,7 @@ void xprt_connect(struct rpc_task *task)
712743
if (test_and_clear_bit(XPRT_CLOSE_WAIT, &xprt->state))
713744
xprt->ops->close(xprt);
714745

715-
if (xprt_connected(xprt))
716-
xprt_release_write(xprt, task);
717-
else {
746+
if (!xprt_connected(xprt)) {
718747
task->tk_rqstp->rq_bytes_sent = 0;
719748
task->tk_timeout = task->tk_rqstp->rq_timeout;
720749
rpc_sleep_on(&xprt->pending, task, xprt_connect_status);
@@ -726,6 +755,7 @@ void xprt_connect(struct rpc_task *task)
726755
xprt->stat.connect_start = jiffies;
727756
xprt->ops->connect(xprt, task);
728757
}
758+
xprt_release_write(xprt, task);
729759
}
730760

731761
static void xprt_connect_status(struct rpc_task *task)
@@ -758,7 +788,6 @@ static void xprt_connect_status(struct rpc_task *task)
758788
dprintk("RPC: %5u xprt_connect_status: error %d connecting to "
759789
"server %s\n", task->tk_pid, -task->tk_status,
760790
xprt->servername);
761-
xprt_release_write(xprt, task);
762791
task->tk_status = -EIO;
763792
}
764793
}

net/sunrpc/xprtsock.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -852,8 +852,6 @@ static void xs_close(struct rpc_xprt *xprt)
852852

853853
dprintk("RPC: xs_close xprt %p\n", xprt);
854854

855-
cancel_delayed_work_sync(&transport->connect_worker);
856-
857855
xs_reset_transport(transport);
858856
xprt->reestablish_timeout = 0;
859857

@@ -2101,6 +2099,7 @@ static void xs_udp_setup_socket(struct work_struct *work)
21012099
trace_rpc_socket_connect(xprt, sock, 0);
21022100
status = 0;
21032101
out:
2102+
xprt_unlock_connect(xprt, transport);
21042103
xprt_clear_connecting(xprt);
21052104
xprt_wake_pending_tasks(xprt, status);
21062105
}
@@ -2286,6 +2285,7 @@ static void xs_tcp_setup_socket(struct work_struct *work)
22862285
case 0:
22872286
case -EINPROGRESS:
22882287
case -EALREADY:
2288+
xprt_unlock_connect(xprt, transport);
22892289
xprt_clear_connecting(xprt);
22902290
return;
22912291
case -EINVAL:
@@ -2303,6 +2303,7 @@ static void xs_tcp_setup_socket(struct work_struct *work)
23032303
out_eagain:
23042304
status = -EAGAIN;
23052305
out:
2306+
xprt_unlock_connect(xprt, transport);
23062307
xprt_clear_connecting(xprt);
23072308
xprt_wake_pending_tasks(xprt, status);
23082309
}
@@ -2325,6 +2326,8 @@ static void xs_connect(struct rpc_xprt *xprt, struct rpc_task *task)
23252326
{
23262327
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
23272328

2329+
WARN_ON_ONCE(!xprt_lock_connect(xprt, task, transport));
2330+
23282331
if (transport->sock != NULL && !RPC_IS_SOFTCONN(task)) {
23292332
dprintk("RPC: xs_connect delayed xprt %p for %lu "
23302333
"seconds\n",

0 commit comments

Comments
 (0)