Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d1d00458 authored by Karsten Graul's avatar Karsten Graul Committed by Greg Kroah-Hartman
Browse files

net/smc: wait for pending work before clcsock release_sock



[ Upstream commit fd57770dd198f5b2ddd5b9e6bf282cf98d63adb9 ]

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: default avatarKarsten Graul <kgraul@linux.ibm.com>
Signed-off-by: default avatarUrsula Braun <ubraun@linux.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Stable-dep-of: 5211c9729484 ("net/smc: fix dangling sock under state SMC_APPFINCLOSEWAIT")
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 0b6153d0
Loading
Loading
Loading
Loading
+8 −6
Original line number Diff line number Diff line
@@ -158,10 +158,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);
@@ -1014,13 +1013,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);
@@ -1216,6 +1215,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;
+21 −4
Original line number Diff line number Diff line
@@ -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;
@@ -331,6 +347,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;

@@ -417,13 +434,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 */
}

+1 −0
Original line number Diff line number Diff line
@@ -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 */