]> www.infradead.org Git - users/willy/pagecache.git/commitdiff
net/smc: check sndbuf_space again after NOSPACE flag is set in smc_poll
authorGuangguan Wang <guangguan.wang@linux.alibaba.com>
Wed, 11 Dec 2024 09:21:17 +0000 (17:21 +0800)
committerDavid S. Miller <davem@davemloft.net>
Sun, 15 Dec 2024 12:34:59 +0000 (12:34 +0000)
When application sending data more than sndbuf_space, there have chances
application will sleep in epoll_wait, and will never be wakeup again. This
is caused by a race between smc_poll and smc_cdc_tx_handler.

application                                      tasklet
smc_tx_sendmsg(len > sndbuf_space)   |
epoll_wait for EPOLL_OUT,timeout=0   |
  smc_poll                           |
    if (!smc->conn.sndbuf_space)     |
                                     |  smc_cdc_tx_handler
                                     |    atomic_add sndbuf_space
                                     |    smc_tx_sndbuf_nonfull
                                     |      if (!test_bit SOCK_NOSPACE)
                                     |        do not sk_write_space;
      set_bit SOCK_NOSPACE;          |
    return mask=0;                   |

Application will sleep in epoll_wait as smc_poll returns 0. And
smc_cdc_tx_handler will not call sk_write_space because the SOCK_NOSPACE
has not be set. If there is no inflight cdc msg, sk_write_space will not be
called any more, and application will sleep in epoll_wait forever.
So check sndbuf_space again after NOSPACE flag is set to break the race.

Fixes: 8dce2786a290 ("net/smc: smc_poll improvements")
Signed-off-by: Guangguan Wang <guangguan.wang@linux.alibaba.com>
Suggested-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/smc/af_smc.c

index 9e6c69d18581ce6c31d3d357ed2cb29bde6e13a7..92448f2c362cb76ae1bb785c074bc2492d900f80 100644 (file)
@@ -2881,6 +2881,13 @@ __poll_t smc_poll(struct file *file, struct socket *sock,
                        } else {
                                sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
                                set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+
+                               if (sk->sk_state != SMC_INIT) {
+                                       /* Race breaker the same way as tcp_poll(). */
+                                       smp_mb__after_atomic();
+                                       if (atomic_read(&smc->conn.sndbuf_space))
+                                               mask |= EPOLLOUT | EPOLLWRNORM;
+                               }
                        }
                        if (atomic_read(&smc->conn.bytes_to_rcv))
                                mask |= EPOLLIN | EPOLLRDNORM;