mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 08:19:28 +00:00
net/smc: Send directly when TCP_CORK is cleared
[ Upstream commit ea785a1a57
]
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: Tony Lu <tonylu@linux.alibaba.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
646d533af2
commit
eb5202cb83
3 changed files with 18 additions and 12 deletions
|
@ -2636,8 +2636,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;
|
||||
|
|
|
@ -597,6 +597,20 @@ int smc_tx_sndbuf_nonempty(struct smc_connection *conn)
|
|||
return rc;
|
||||
}
|
||||
|
||||
void smc_tx_pending(struct smc_connection *conn)
|
||||
{
|
||||
struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
|
||||
int rc;
|
||||
|
||||
if (smc->sk.sk_err)
|
||||
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;
|
||||
}
|
||||
|
||||
/* Wakeup sndbuf consumers from process context
|
||||
* since there is more data to transmit
|
||||
*/
|
||||
|
@ -606,18 +620,9 @@ void smc_tx_work(struct work_struct *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;
|
||||
|
||||
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:
|
||||
smc_tx_pending(conn);
|
||||
release_sock(&smc->sk);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue