Commit ea785a1a authored by Tony Lu's avatar Tony Lu Committed by David S. Miller
Browse files

net/smc: Send directly when TCP_CORK is cleared

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>
parent 01b2a995
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -2523,8 +2523,8 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
		    sk->sk_state != SMC_CLOSED) {
			if (!val) {
				SMC_STAT_INC(smc, cork_cnt);
				mod_delayed_work(smc->conn.lgr->tx_wq,
						 &smc->conn.tx_work, 0);
				smc_tx_pending(&smc->conn);
				cancel_delayed_work(&smc->conn.tx_work);
			}
		}
		break;
+15 −10
Original line number Diff line number Diff line
@@ -597,27 +597,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);