forked from mirrors/linux
		
	net/smc: wait for pending work before clcsock release_sock
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>
This commit is contained in:
		
							parent
							
								
									988dc4a9a3
								
							
						
					
					
						commit
						fd57770dd1
					
				
					 3 changed files with 30 additions and 10 deletions
				
			
		| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue