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

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/tcpSigned-off-by: default avatarTony Lu <tonylu@linux.alibaba.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 01b2a995
...@@ -2523,8 +2523,8 @@ static int smc_setsockopt(struct socket *sock, int level, int optname, ...@@ -2523,8 +2523,8 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,
sk->sk_state != SMC_CLOSED) { sk->sk_state != SMC_CLOSED) {
if (!val) { if (!val) {
SMC_STAT_INC(smc, cork_cnt); SMC_STAT_INC(smc, cork_cnt);
mod_delayed_work(smc->conn.lgr->tx_wq, smc_tx_pending(&smc->conn);
&smc->conn.tx_work, 0); cancel_delayed_work(&smc->conn.tx_work);
} }
} }
break; break;
......
...@@ -597,27 +597,32 @@ int smc_tx_sndbuf_nonempty(struct smc_connection *conn) ...@@ -597,27 +597,32 @@ int smc_tx_sndbuf_nonempty(struct smc_connection *conn)
return rc; return rc;
} }
/* Wakeup sndbuf consumers from process context void smc_tx_pending(struct smc_connection *conn)
* 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); struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
int rc; int rc;
lock_sock(&smc->sk);
if (smc->sk.sk_err) if (smc->sk.sk_err)
goto out; return;
rc = smc_tx_sndbuf_nonempty(conn); rc = smc_tx_sndbuf_nonempty(conn);
if (!rc && conn->local_rx_ctrl.prod_flags.write_blocked && if (!rc && conn->local_rx_ctrl.prod_flags.write_blocked &&
!atomic_read(&conn->bytes_to_rcv)) !atomic_read(&conn->bytes_to_rcv))
conn->local_rx_ctrl.prod_flags.write_blocked = 0; conn->local_rx_ctrl.prod_flags.write_blocked = 0;
}
/* 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);
out: lock_sock(&smc->sk);
smc_tx_pending(conn);
release_sock(&smc->sk); release_sock(&smc->sk);
} }
......
...@@ -27,6 +27,7 @@ static inline int smc_tx_prepared_sends(struct smc_connection *conn) ...@@ -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); 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_work(struct work_struct *work);
void smc_tx_init(struct smc_sock *smc); void smc_tx_init(struct smc_sock *smc);
int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len); int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment