Skip to content

Commit

Permalink
net/smc: wait for pending work before clcsock release_sock
Browse files Browse the repository at this point in the history
When the clcsock is already released using sock_release() and a pending
smc_listen_work accesses the clcsock than that will fail. Solve this
by canceling and waiting for the work to complete first. Because the
work holds the sock_lock it must make sure that the lock is not hold
before the new helper smc_clcsock_release() is invoked. And before the
smc_listen_work starts working check if the parent listen socket is
still valid, otherwise stop the work early.

Signed-off-by: Karsten Graul <kgraul@linux.ibm.com>
Signed-off-by: Ursula Braun <ubraun@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
karstengr authored and davem330 committed Apr 11, 2019
1 parent 988dc4a commit fd57770
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 10 deletions.
14 changes: 8 additions & 6 deletions net/smc/af_smc.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,9 @@ static int smc_release(struct socket *sock)

if (sk->sk_state == SMC_CLOSED) {
if (smc->clcsock) {
mutex_lock(&smc->clcsock_release_lock);
sock_release(smc->clcsock);
smc->clcsock = NULL;
mutex_unlock(&smc->clcsock_release_lock);
release_sock(sk);
smc_clcsock_release(smc);
lock_sock(sk);
}
if (!smc->use_fallback)
smc_conn_free(&smc->conn);
Expand Down Expand Up @@ -1037,13 +1036,13 @@ static void smc_listen_out(struct smc_sock *new_smc)
struct smc_sock *lsmc = new_smc->listen_smc;
struct sock *newsmcsk = &new_smc->sk;

lock_sock_nested(&lsmc->sk, SINGLE_DEPTH_NESTING);
if (lsmc->sk.sk_state == SMC_LISTEN) {
lock_sock_nested(&lsmc->sk, SINGLE_DEPTH_NESTING);
smc_accept_enqueue(&lsmc->sk, newsmcsk);
release_sock(&lsmc->sk);
} else { /* no longer listening */
smc_close_non_accepted(newsmcsk);
}
release_sock(&lsmc->sk);

/* Wake up accept */
lsmc->sk.sk_data_ready(&lsmc->sk);
Expand Down Expand Up @@ -1237,6 +1236,9 @@ static void smc_listen_work(struct work_struct *work)
int rc = 0;
u8 ibport;

if (new_smc->listen_smc->sk.sk_state != SMC_LISTEN)
return smc_listen_out_err(new_smc);

if (new_smc->use_fallback) {
smc_listen_out_connected(new_smc);
return;
Expand Down
25 changes: 21 additions & 4 deletions net/smc/smc_close.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@

#define SMC_CLOSE_WAIT_LISTEN_CLCSOCK_TIME (5 * HZ)

/* release the clcsock that is assigned to the smc_sock */
void smc_clcsock_release(struct smc_sock *smc)
{
struct socket *tcp;

if (smc->listen_smc && current_work() != &smc->smc_listen_work)
cancel_work_sync(&smc->smc_listen_work);
mutex_lock(&smc->clcsock_release_lock);
if (smc->clcsock) {
tcp = smc->clcsock;
smc->clcsock = NULL;
sock_release(tcp);
}
mutex_unlock(&smc->clcsock_release_lock);
}

static void smc_close_cleanup_listen(struct sock *parent)
{
struct sock *sk;
Expand Down Expand Up @@ -321,6 +337,7 @@ static void smc_close_passive_work(struct work_struct *work)
close_work);
struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
struct smc_cdc_conn_state_flags *rxflags;
bool release_clcsock = false;
struct sock *sk = &smc->sk;
int old_state;

Expand Down Expand Up @@ -400,13 +417,13 @@ static void smc_close_passive_work(struct work_struct *work)
if ((sk->sk_state == SMC_CLOSED) &&
(sock_flag(sk, SOCK_DEAD) || !sk->sk_socket)) {
smc_conn_free(conn);
if (smc->clcsock) {
sock_release(smc->clcsock);
smc->clcsock = NULL;
}
if (smc->clcsock)
release_clcsock = true;
}
}
release_sock(sk);
if (release_clcsock)
smc_clcsock_release(smc);
sock_put(sk); /* sock_hold done by schedulers of close_work */
}

Expand Down
1 change: 1 addition & 0 deletions net/smc/smc_close.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ void smc_close_wake_tx_prepared(struct smc_sock *smc);
int smc_close_active(struct smc_sock *smc);
int smc_close_shutdown_write(struct smc_sock *smc);
void smc_close_init(struct smc_sock *smc);
void smc_clcsock_release(struct smc_sock *smc);

#endif /* SMC_CLOSE_H */

0 comments on commit fd57770

Please sign in to comment.