Commit 30a916d3 authored by Litao Jiao's avatar Litao Jiao
Browse files

net/smc: Send directly when TCP_CORK is cleared

mainline inclusion
from mainline-v5.18-rc1
commit ea785a1a
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I78OQ2
CVE: NA

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/net/smc?id=ea785a1a573b390a150010b3c5b81e1ccd8c98a8

--------------------------------

According to the man page of TCP_CORK [1], if set, don't send out
partial frames. All queued partial frames are sent when option is
cleared again.

When applications call setsockopt to disable TCP_CORK, this call is
protected by lock_sock(), and tries to mod_delayed_work() to 0, in order
to send pending data right now. However, the delayed work smc_tx_work is
also protected by lock_sock(). There introduces lock contention for
sending data.

To fix it, send pending data directly which acts like TCP, without
lock_sock() protected in the context of setsockopt (already lock_sock()ed),
and cancel unnecessary dealyed work, which is protected by lock.

[1] https://linux.die.net/man/7/tcp



Signed-off-by: default avatarTony Lu <tonylu@linux.alibaba.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarLitao Jiao <jiaolitao@sangfor.com.cn>
parent 22bae0fc
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -2236,9 +2236,10 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
		if (sk->sk_state != SMC_INIT &&
		    sk->sk_state != SMC_LISTEN &&
		    sk->sk_state != SMC_CLOSED) {
			if (!val)
				mod_delayed_work(smc->conn.lgr->tx_wq,
						 &smc->conn.tx_work, 0);
			if (!val) {
				smc_tx_pending(&smc->conn);
				cancel_delayed_work(&smc->conn.tx_work);
			}
		}
		break;
	case TCP_DEFER_ACCEPT:
+15 −10
Original line number Diff line number Diff line
@@ -577,27 +577,32 @@ int smc_tx_sndbuf_nonempty(struct smc_connection *conn)
	return rc;
}

/* Wakeup sndbuf consumers from process context
 * since there is more data to transmit
 */
void smc_tx_work(struct work_struct *work)
void smc_tx_pending(struct smc_connection *conn)
{
	struct smc_connection *conn = container_of(to_delayed_work(work),
						   struct smc_connection,
						   tx_work);
	struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
	int rc;

	lock_sock(&smc->sk);
	if (smc->sk.sk_err)
		goto out;
		return;

	rc = smc_tx_sndbuf_nonempty(conn);
	if (!rc && conn->local_rx_ctrl.prod_flags.write_blocked &&
	    !atomic_read(&conn->bytes_to_rcv))
		conn->local_rx_ctrl.prod_flags.write_blocked = 0;
}

out:
/* Wakeup sndbuf consumers from process context
 * since there is more data to transmit
 */
void smc_tx_work(struct work_struct *work)
{
	struct smc_connection *conn = container_of(to_delayed_work(work),
						   struct smc_connection,
						   tx_work);
	struct smc_sock *smc = container_of(conn, struct smc_sock, conn);

	lock_sock(&smc->sk);
	smc_tx_pending(conn);
	release_sock(&smc->sk);
}

+1 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ static inline int smc_tx_prepared_sends(struct smc_connection *conn)
	return smc_curs_diff(conn->sndbuf_desc->len, &sent, &prep);
}

void smc_tx_pending(struct smc_connection *conn);
void smc_tx_work(struct work_struct *work);
void smc_tx_init(struct smc_sock *smc);
int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len);