mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
net/smc: don't spin in smc_rx_recvmsg() while a pipe holds spliced data
While bytes spliced off the RMB are still in a pipe, recvmsg() waits
for the pipe to release them before it copies the data behind them.
The path to that wait skips the timeout and signal checks, and
smc_rx_wait() returns at once on RCV_SHUTDOWN. With a signal pending,
after SHUT_RD, or once the peer closes or shuts down writing,
recvmsg() spins on a CPU until the pipe is drained, and SIGKILL cannot
stop it. MSG_DONTWAIT and an expired SO_RCVTIMEO wait there too
instead of returning -EAGAIN.
Check the timeout and signals before that wait, and end it when the
pipe releases the data or the connection is killed, not on RCV_SHUTDOWN.
A zero-length read returns 0 without waiting for the pipe; a
non-blocking one returns -EAGAIN while poll() still reports the socket
readable (splice() can still take those bytes). The wait sets
SOCKWQ_ASYNC_WAITDATA like smc_rx_wait(), so a byte arriving behind
the pipe raises no SIGIO on a reader already asleep for it, and a full
barrier orders its wait queue entry before its read of splice_pending,
so no wake is lost.
Fixes: 9014db202c ("smc: add support for splice()")
Cc: stable@vger.kernel.org
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
Reviewed-by: Dust Li <dust.li@linux.alibaba.com>
Link: https://patch.msgid.link/20261029-b4-disp-da7f8e6a-v2-1-9052904adf72@proton.me
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
parent
6c1c41effa
commit
8288ba942c
|
|
@ -286,6 +286,27 @@ int smc_rx_wait(struct smc_sock *smc, long *timeo, size_t peeked,
|
|||
return rc;
|
||||
}
|
||||
|
||||
/* Wait for the pipe to release the spliced data. Unlike smc_rx_wait()
|
||||
* it does not end on shutdown or sk_err, the data is already in the
|
||||
* RMB; smc_conn_kill() wakes it and the caller checks conn->killed.
|
||||
*/
|
||||
static void smc_rx_wait_splice(struct smc_sock *smc, long *timeo)
|
||||
{
|
||||
DEFINE_WAIT_FUNC(wait, woken_wake_function);
|
||||
struct smc_connection *conn = &smc->conn;
|
||||
struct sock *sk = &smc->sk;
|
||||
|
||||
sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
|
||||
add_wait_queue(sk_sleep(sk), &wait);
|
||||
/* pairs with the smp_mb() in wq_has_sleeper(): the release drops
|
||||
* splice_pending, then looks for a sleeper
|
||||
*/
|
||||
smp_mb();
|
||||
sk_wait_event(sk, timeo, !atomic_read(&conn->splice_pending), &wait);
|
||||
remove_wait_queue(sk_sleep(sk), &wait);
|
||||
sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
|
||||
}
|
||||
|
||||
static int smc_rx_recv_urg(struct smc_sock *smc, struct msghdr *msg, int len,
|
||||
int flags)
|
||||
{
|
||||
|
|
@ -443,7 +464,23 @@ int smc_rx_recvmsg(struct smc_sock *smc, struct msghdr *msg,
|
|||
/* could be just 1 byte, even after waiting on data above */
|
||||
readable = smc_rx_data_available(conn, peeked_bytes);
|
||||
splbytes = atomic_read(&conn->splice_pending);
|
||||
if (!readable || (msg && splbytes)) {
|
||||
if (msg && splbytes) {
|
||||
/* a pipe holds bytes ahead of these; the copy path
|
||||
* skips the timeout and signal checks
|
||||
*/
|
||||
if (!read_remaining)
|
||||
break;
|
||||
if (!timeo || signal_pending(current)) {
|
||||
if (!read_done)
|
||||
read_done = timeo ?
|
||||
sock_intr_errno(timeo) :
|
||||
-EAGAIN;
|
||||
break;
|
||||
}
|
||||
smc_rx_wait_splice(smc, &timeo);
|
||||
continue;
|
||||
}
|
||||
if (!readable) {
|
||||
if (splbytes)
|
||||
func = smc_rx_data_available_and_no_splice_pend;
|
||||
else
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user