diff options
Diffstat (limited to 'net/smc')
| -rw-r--r-- | net/smc/af_smc.c | 198 | ||||
| -rw-r--r-- | net/smc/smc.h | 21 | ||||
| -rw-r--r-- | net/smc/smc_cdc.c | 3 | ||||
| -rw-r--r-- | net/smc/smc_clc.c | 2 | ||||
| -rw-r--r-- | net/smc/smc_core.c | 137 | ||||
| -rw-r--r-- | net/smc/smc_core.h | 12 | ||||
| -rw-r--r-- | net/smc/smc_diag.c | 8 | ||||
| -rw-r--r-- | net/smc/smc_pnet.c | 7 | ||||
| -rw-r--r-- | net/smc/smc_wr.h | 4 | 
9 files changed, 310 insertions, 82 deletions
diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index aa3bcaaeabf7..8c89d0b0ca18 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -566,11 +566,114 @@ static void smc_stat_fallback(struct smc_sock *smc)  	mutex_unlock(&net->smc.mutex_fback_rsn);  } -static void smc_switch_to_fallback(struct smc_sock *smc, int reason_code) +/* must be called under rcu read lock */ +static void smc_fback_wakeup_waitqueue(struct smc_sock *smc, void *key)  { -	wait_queue_head_t *smc_wait = sk_sleep(&smc->sk); -	wait_queue_head_t *clc_wait = sk_sleep(smc->clcsock->sk); -	unsigned long flags; +	struct socket_wq *wq; +	__poll_t flags; + +	wq = rcu_dereference(smc->sk.sk_wq); +	if (!skwq_has_sleeper(wq)) +		return; + +	/* wake up smc sk->sk_wq */ +	if (!key) { +		/* sk_state_change */ +		wake_up_interruptible_all(&wq->wait); +	} else { +		flags = key_to_poll(key); +		if (flags & (EPOLLIN | EPOLLOUT)) +			/* sk_data_ready or sk_write_space */ +			wake_up_interruptible_sync_poll(&wq->wait, flags); +		else if (flags & EPOLLERR) +			/* sk_error_report */ +			wake_up_interruptible_poll(&wq->wait, flags); +	} +} + +static int smc_fback_mark_woken(wait_queue_entry_t *wait, +				unsigned int mode, int sync, void *key) +{ +	struct smc_mark_woken *mark = +		container_of(wait, struct smc_mark_woken, wait_entry); + +	mark->woken = true; +	mark->key = key; +	return 0; +} + +static void smc_fback_forward_wakeup(struct smc_sock *smc, struct sock *clcsk, +				     void (*clcsock_callback)(struct sock *sk)) +{ +	struct smc_mark_woken mark = { .woken = false }; +	struct socket_wq *wq; + +	init_waitqueue_func_entry(&mark.wait_entry, +				  smc_fback_mark_woken); +	rcu_read_lock(); +	wq = rcu_dereference(clcsk->sk_wq); +	if (!wq) +		goto out; +	add_wait_queue(sk_sleep(clcsk), &mark.wait_entry); +	clcsock_callback(clcsk); +	remove_wait_queue(sk_sleep(clcsk), &mark.wait_entry); + +	if (mark.woken) +		smc_fback_wakeup_waitqueue(smc, mark.key); +out: +	rcu_read_unlock(); +} + +static void smc_fback_state_change(struct sock *clcsk) +{ +	struct smc_sock *smc = +		smc_clcsock_user_data(clcsk); + +	if (!smc) +		return; +	smc_fback_forward_wakeup(smc, clcsk, smc->clcsk_state_change); +} + +static void smc_fback_data_ready(struct sock *clcsk) +{ +	struct smc_sock *smc = +		smc_clcsock_user_data(clcsk); + +	if (!smc) +		return; +	smc_fback_forward_wakeup(smc, clcsk, smc->clcsk_data_ready); +} + +static void smc_fback_write_space(struct sock *clcsk) +{ +	struct smc_sock *smc = +		smc_clcsock_user_data(clcsk); + +	if (!smc) +		return; +	smc_fback_forward_wakeup(smc, clcsk, smc->clcsk_write_space); +} + +static void smc_fback_error_report(struct sock *clcsk) +{ +	struct smc_sock *smc = +		smc_clcsock_user_data(clcsk); + +	if (!smc) +		return; +	smc_fback_forward_wakeup(smc, clcsk, smc->clcsk_error_report); +} + +static int smc_switch_to_fallback(struct smc_sock *smc, int reason_code) +{ +	struct sock *clcsk; + +	mutex_lock(&smc->clcsock_release_lock); +	if (!smc->clcsock) { +		mutex_unlock(&smc->clcsock_release_lock); +		return -EBADF; +	} +	clcsk = smc->clcsock->sk;  	smc->use_fallback = true;  	smc->fallback_rsn = reason_code; @@ -582,22 +685,40 @@ static void smc_switch_to_fallback(struct smc_sock *smc, int reason_code)  		smc->clcsock->wq.fasync_list =  			smc->sk.sk_socket->wq.fasync_list; -		/* There may be some entries remaining in -		 * smc socket->wq, which should be removed -		 * to clcsocket->wq during the fallback. +		/* There might be some wait entries remaining +		 * in smc sk->sk_wq and they should be woken up +		 * as clcsock's wait queue is woken up.  		 */ -		spin_lock_irqsave(&smc_wait->lock, flags); -		spin_lock_nested(&clc_wait->lock, SINGLE_DEPTH_NESTING); -		list_splice_init(&smc_wait->head, &clc_wait->head); -		spin_unlock(&clc_wait->lock); -		spin_unlock_irqrestore(&smc_wait->lock, flags); +		smc->clcsk_state_change = clcsk->sk_state_change; +		smc->clcsk_data_ready = clcsk->sk_data_ready; +		smc->clcsk_write_space = clcsk->sk_write_space; +		smc->clcsk_error_report = clcsk->sk_error_report; + +		clcsk->sk_state_change = smc_fback_state_change; +		clcsk->sk_data_ready = smc_fback_data_ready; +		clcsk->sk_write_space = smc_fback_write_space; +		clcsk->sk_error_report = smc_fback_error_report; + +		smc->clcsock->sk->sk_user_data = +			(void *)((uintptr_t)smc | SK_USER_DATA_NOCOPY);  	} +	mutex_unlock(&smc->clcsock_release_lock); +	return 0;  }  /* fall back during connect */  static int smc_connect_fallback(struct smc_sock *smc, int reason_code)  { -	smc_switch_to_fallback(smc, reason_code); +	struct net *net = sock_net(&smc->sk); +	int rc = 0; + +	rc = smc_switch_to_fallback(smc, reason_code); +	if (rc) { /* fallback fails */ +		this_cpu_inc(net->smc.smc_stats->clnt_hshake_err_cnt); +		if (smc->sk.sk_state == SMC_INIT) +			sock_put(&smc->sk); /* passive closing */ +		return rc; +	}  	smc_copy_sock_settings_to_clc(smc);  	smc->connect_nonblock = 0;  	if (smc->sk.sk_state == SMC_INIT) @@ -634,9 +755,13 @@ static void smc_conn_abort(struct smc_sock *smc, int local_first)  {  	struct smc_connection *conn = &smc->conn;  	struct smc_link_group *lgr = conn->lgr; +	bool lgr_valid = false; + +	if (smc_conn_lgr_valid(conn)) +		lgr_valid = true;  	smc_conn_free(conn); -	if (local_first) +	if (local_first && lgr_valid)  		smc_lgr_cleanup_early(lgr);  } @@ -1514,11 +1639,12 @@ static void smc_listen_decline(struct smc_sock *new_smc, int reason_code,  {  	/* RDMA setup failed, switch back to TCP */  	smc_conn_abort(new_smc, local_first); -	if (reason_code < 0) { /* error, no fallback possible */ +	if (reason_code < 0 || +	    smc_switch_to_fallback(new_smc, reason_code)) { +		/* error, no fallback possible */  		smc_listen_out_err(new_smc);  		return;  	} -	smc_switch_to_fallback(new_smc, reason_code);  	if (reason_code && reason_code != SMC_CLC_DECL_PEERDECL) {  		if (smc_clc_send_decline(new_smc, reason_code, version) < 0) {  			smc_listen_out_err(new_smc); @@ -1960,8 +2086,11 @@ static void smc_listen_work(struct work_struct *work)  	/* check if peer is smc capable */  	if (!tcp_sk(newclcsock->sk)->syn_smc) { -		smc_switch_to_fallback(new_smc, SMC_CLC_DECL_PEERNOSMC); -		smc_listen_out_connected(new_smc); +		rc = smc_switch_to_fallback(new_smc, SMC_CLC_DECL_PEERNOSMC); +		if (rc) +			smc_listen_out_err(new_smc); +		else +			smc_listen_out_connected(new_smc);  		return;  	} @@ -2090,10 +2219,9 @@ out:  static void smc_clcsock_data_ready(struct sock *listen_clcsock)  { -	struct smc_sock *lsmc; +	struct smc_sock *lsmc = +		smc_clcsock_user_data(listen_clcsock); -	lsmc = (struct smc_sock *) -	       ((uintptr_t)listen_clcsock->sk_user_data & ~SK_USER_DATA_NOCOPY);  	if (!lsmc)  		return;  	lsmc->clcsk_data_ready(listen_clcsock); @@ -2250,7 +2378,9 @@ static int smc_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)  	if (msg->msg_flags & MSG_FASTOPEN) {  		if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) { -			smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP); +			rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP); +			if (rc) +				goto out;  		} else {  			rc = -EINVAL;  			goto out; @@ -2443,6 +2573,11 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,  	/* generic setsockopts reaching us here always apply to the  	 * CLC socket  	 */ +	mutex_lock(&smc->clcsock_release_lock); +	if (!smc->clcsock) { +		mutex_unlock(&smc->clcsock_release_lock); +		return -EBADF; +	}  	if (unlikely(!smc->clcsock->ops->setsockopt))  		rc = -EOPNOTSUPP;  	else @@ -2452,6 +2587,7 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,  		sk->sk_err = smc->clcsock->sk->sk_err;  		sk_error_report(sk);  	} +	mutex_unlock(&smc->clcsock_release_lock);  	if (optlen < sizeof(int))  		return -EINVAL; @@ -2468,7 +2604,7 @@ static int smc_setsockopt(struct socket *sock, int level, int optname,  	case TCP_FASTOPEN_NO_COOKIE:  		/* option not supported by SMC */  		if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) { -			smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP); +			rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);  		} else {  			rc = -EINVAL;  		} @@ -2511,13 +2647,23 @@ static int smc_getsockopt(struct socket *sock, int level, int optname,  			  char __user *optval, int __user *optlen)  {  	struct smc_sock *smc; +	int rc;  	smc = smc_sk(sock->sk); +	mutex_lock(&smc->clcsock_release_lock); +	if (!smc->clcsock) { +		mutex_unlock(&smc->clcsock_release_lock); +		return -EBADF; +	}  	/* socket options apply to the CLC socket */ -	if (unlikely(!smc->clcsock->ops->getsockopt)) +	if (unlikely(!smc->clcsock->ops->getsockopt)) { +		mutex_unlock(&smc->clcsock_release_lock);  		return -EOPNOTSUPP; -	return smc->clcsock->ops->getsockopt(smc->clcsock, level, optname, -					     optval, optlen); +	} +	rc = smc->clcsock->ops->getsockopt(smc->clcsock, level, optname, +					   optval, optlen); +	mutex_unlock(&smc->clcsock_release_lock); +	return rc;  }  static int smc_ioctl(struct socket *sock, unsigned int cmd, diff --git a/net/smc/smc.h b/net/smc/smc.h index 1a4fc1c6c4ab..37b2001a0255 100644 --- a/net/smc/smc.h +++ b/net/smc/smc.h @@ -139,6 +139,12 @@ enum smc_urg_state {  	SMC_URG_READ	= 3,			/* data was already read */  }; +struct smc_mark_woken { +	bool woken; +	void *key; +	wait_queue_entry_t wait_entry; +}; +  struct smc_connection {  	struct rb_node		alert_node;  	struct smc_link_group	*lgr;		/* link group of connection */ @@ -221,14 +227,21 @@ struct smc_connection {  						 */  	u64			peer_token;	/* SMC-D token of peer */  	u8			killed : 1;	/* abnormal termination */ +	u8			freed : 1;	/* normal termiation */  	u8			out_of_sync : 1; /* out of sync with peer */  };  struct smc_sock {				/* smc sock container */  	struct sock		sk;  	struct socket		*clcsock;	/* internal tcp socket */ +	void			(*clcsk_state_change)(struct sock *sk); +						/* original stat_change fct. */  	void			(*clcsk_data_ready)(struct sock *sk); -						/* original data_ready fct. **/ +						/* original data_ready fct. */ +	void			(*clcsk_write_space)(struct sock *sk); +						/* original write_space fct. */ +	void			(*clcsk_error_report)(struct sock *sk); +						/* original error_report fct. */  	struct smc_connection	conn;		/* smc connection */  	struct smc_sock		*listen_smc;	/* listen parent */  	struct work_struct	connect_work;	/* handle non-blocking connect*/ @@ -263,6 +276,12 @@ static inline struct smc_sock *smc_sk(const struct sock *sk)  	return (struct smc_sock *)sk;  } +static inline struct smc_sock *smc_clcsock_user_data(struct sock *clcsk) +{ +	return (struct smc_sock *) +	       ((uintptr_t)clcsk->sk_user_data & ~SK_USER_DATA_NOCOPY); +} +  extern struct workqueue_struct	*smc_hs_wq;	/* wq for handshake work */  extern struct workqueue_struct	*smc_close_wq;	/* wq for close work */ diff --git a/net/smc/smc_cdc.c b/net/smc/smc_cdc.c index 84c8a4374fdd..9d5a97168969 100644 --- a/net/smc/smc_cdc.c +++ b/net/smc/smc_cdc.c @@ -197,7 +197,8 @@ int smc_cdc_get_slot_and_msg_send(struct smc_connection *conn)  {  	int rc; -	if (!conn->lgr || (conn->lgr->is_smcd && conn->lgr->peer_shutdown)) +	if (!smc_conn_lgr_valid(conn) || +	    (conn->lgr->is_smcd && conn->lgr->peer_shutdown))  		return -EPIPE;  	if (conn->lgr->is_smcd) { diff --git a/net/smc/smc_clc.c b/net/smc/smc_clc.c index 6be95a2a7b25..ce27399b38b1 100644 --- a/net/smc/smc_clc.c +++ b/net/smc/smc_clc.c @@ -774,7 +774,7 @@ int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info, u8 version)  	dclc.os_type = version == SMC_V1 ? 0 : SMC_CLC_OS_LINUX;  	dclc.hdr.typev2 = (peer_diag_info == SMC_CLC_DECL_SYNCERR) ?  						SMC_FIRST_CONTACT_MASK : 0; -	if ((!smc->conn.lgr || !smc->conn.lgr->is_smcd) && +	if ((!smc_conn_lgr_valid(&smc->conn) || !smc->conn.lgr->is_smcd) &&  	    smc_ib_is_valid_local_systemid())  		memcpy(dclc.id_for_peer, local_systemid,  		       sizeof(local_systemid)); diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index 8935ef4811b0..29525d03b253 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -211,14 +211,13 @@ static void smc_lgr_unregister_conn(struct smc_connection *conn)  {  	struct smc_link_group *lgr = conn->lgr; -	if (!lgr) +	if (!smc_conn_lgr_valid(conn))  		return;  	write_lock_bh(&lgr->conns_lock);  	if (conn->alert_token_local) {  		__smc_lgr_unregister_conn(conn);  	}  	write_unlock_bh(&lgr->conns_lock); -	conn->lgr = NULL;  }  int smc_nl_get_sys_info(struct sk_buff *skb, struct netlink_callback *cb) @@ -749,9 +748,12 @@ int smcr_link_init(struct smc_link_group *lgr, struct smc_link *lnk,  	}  	get_device(&lnk->smcibdev->ibdev->dev);  	atomic_inc(&lnk->smcibdev->lnk_cnt); +	refcount_set(&lnk->refcnt, 1); /* link refcnt is set to 1 */ +	lnk->clearing = 0;  	lnk->path_mtu = lnk->smcibdev->pattr[lnk->ibport - 1].active_mtu;  	lnk->link_id = smcr_next_link_id(lgr);  	lnk->lgr = lgr; +	smc_lgr_hold(lgr); /* lgr_put in smcr_link_clear() */  	lnk->link_idx = link_idx;  	smc_ibdev_cnt_inc(lnk);  	smcr_copy_dev_info_to_link(lnk); @@ -806,6 +808,7 @@ out:  	lnk->state = SMC_LNK_UNUSED;  	if (!atomic_dec_return(&smcibdev->lnk_cnt))  		wake_up(&smcibdev->lnks_deleted); +	smc_lgr_put(lgr); /* lgr_hold above */  	return rc;  } @@ -844,6 +847,7 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini)  	lgr->terminating = 0;  	lgr->freeing = 0;  	lgr->vlan_id = ini->vlan_id; +	refcount_set(&lgr->refcnt, 1); /* set lgr refcnt to 1 */  	mutex_init(&lgr->sndbufs_lock);  	mutex_init(&lgr->rmbs_lock);  	rwlock_init(&lgr->conns_lock); @@ -996,8 +1000,12 @@ void smc_switch_link_and_count(struct smc_connection *conn,  			       struct smc_link *to_lnk)  {  	atomic_dec(&conn->lnk->conn_cnt); +	/* link_hold in smc_conn_create() */ +	smcr_link_put(conn->lnk);  	conn->lnk = to_lnk;  	atomic_inc(&conn->lnk->conn_cnt); +	/* link_put in smc_conn_free() */ +	smcr_link_hold(conn->lnk);  }  struct smc_link *smc_switch_conns(struct smc_link_group *lgr, @@ -1130,8 +1138,19 @@ void smc_conn_free(struct smc_connection *conn)  {  	struct smc_link_group *lgr = conn->lgr; -	if (!lgr) +	if (!lgr || conn->freed) +		/* Connection has never been registered in a +		 * link group, or has already been freed. +		 */  		return; + +	conn->freed = 1; +	if (!smc_conn_lgr_valid(conn)) +		/* Connection has already unregistered from +		 * link group. +		 */ +		goto lgr_put; +  	if (lgr->is_smcd) {  		if (!list_empty(&lgr->list))  			smc_ism_unset_conn(conn); @@ -1148,6 +1167,10 @@ void smc_conn_free(struct smc_connection *conn)  	if (!lgr->conns_num)  		smc_lgr_schedule_free_work(lgr); +lgr_put: +	if (!lgr->is_smcd) +		smcr_link_put(conn->lnk); /* link_hold in smc_conn_create() */ +	smc_lgr_put(lgr); /* lgr_hold in smc_conn_create() */  }  /* unregister a link from a buf_desc */ @@ -1203,13 +1226,29 @@ static void smcr_rtoken_clear_link(struct smc_link *lnk)  	}  } -/* must be called under lgr->llc_conf_mutex lock */ -void smcr_link_clear(struct smc_link *lnk, bool log) +static void __smcr_link_clear(struct smc_link *lnk)  { +	struct smc_link_group *lgr = lnk->lgr;  	struct smc_ib_device *smcibdev; -	if (!lnk->lgr || lnk->state == SMC_LNK_UNUSED) +	smc_wr_free_link_mem(lnk); +	smc_ibdev_cnt_dec(lnk); +	put_device(&lnk->smcibdev->ibdev->dev); +	smcibdev = lnk->smcibdev; +	memset(lnk, 0, sizeof(struct smc_link)); +	lnk->state = SMC_LNK_UNUSED; +	if (!atomic_dec_return(&smcibdev->lnk_cnt)) +		wake_up(&smcibdev->lnks_deleted); +	smc_lgr_put(lgr); /* lgr_hold in smcr_link_init() */ +} + +/* must be called under lgr->llc_conf_mutex lock */ +void smcr_link_clear(struct smc_link *lnk, bool log) +{ +	if (!lnk->lgr || lnk->clearing || +	    lnk->state == SMC_LNK_UNUSED)  		return; +	lnk->clearing = 1;  	lnk->peer_qpn = 0;  	smc_llc_link_clear(lnk, log);  	smcr_buf_unmap_lgr(lnk); @@ -1218,14 +1257,18 @@ void smcr_link_clear(struct smc_link *lnk, bool log)  	smc_wr_free_link(lnk);  	smc_ib_destroy_queue_pair(lnk);  	smc_ib_dealloc_protection_domain(lnk); -	smc_wr_free_link_mem(lnk); -	smc_ibdev_cnt_dec(lnk); -	put_device(&lnk->smcibdev->ibdev->dev); -	smcibdev = lnk->smcibdev; -	memset(lnk, 0, sizeof(struct smc_link)); -	lnk->state = SMC_LNK_UNUSED; -	if (!atomic_dec_return(&smcibdev->lnk_cnt)) -		wake_up(&smcibdev->lnks_deleted); +	smcr_link_put(lnk); /* theoretically last link_put */ +} + +void smcr_link_hold(struct smc_link *lnk) +{ +	refcount_inc(&lnk->refcnt); +} + +void smcr_link_put(struct smc_link *lnk) +{ +	if (refcount_dec_and_test(&lnk->refcnt)) +		__smcr_link_clear(lnk);  }  static void smcr_buf_free(struct smc_link_group *lgr, bool is_rmb, @@ -1290,6 +1333,21 @@ static void smc_lgr_free_bufs(struct smc_link_group *lgr)  	__smc_lgr_free_bufs(lgr, true);  } +/* won't be freed until no one accesses to lgr anymore */ +static void __smc_lgr_free(struct smc_link_group *lgr) +{ +	smc_lgr_free_bufs(lgr); +	if (lgr->is_smcd) { +		if (!atomic_dec_return(&lgr->smcd->lgr_cnt)) +			wake_up(&lgr->smcd->lgrs_deleted); +	} else { +		smc_wr_free_lgr_mem(lgr); +		if (!atomic_dec_return(&lgr_cnt)) +			wake_up(&lgrs_deleted); +	} +	kfree(lgr); +} +  /* remove a link group */  static void smc_lgr_free(struct smc_link_group *lgr)  { @@ -1305,19 +1363,23 @@ static void smc_lgr_free(struct smc_link_group *lgr)  		smc_llc_lgr_clear(lgr);  	} -	smc_lgr_free_bufs(lgr);  	destroy_workqueue(lgr->tx_wq);  	if (lgr->is_smcd) {  		smc_ism_put_vlan(lgr->smcd, lgr->vlan_id);  		put_device(&lgr->smcd->dev); -		if (!atomic_dec_return(&lgr->smcd->lgr_cnt)) -			wake_up(&lgr->smcd->lgrs_deleted); -	} else { -		smc_wr_free_lgr_mem(lgr); -		if (!atomic_dec_return(&lgr_cnt)) -			wake_up(&lgrs_deleted);  	} -	kfree(lgr); +	smc_lgr_put(lgr); /* theoretically last lgr_put */ +} + +void smc_lgr_hold(struct smc_link_group *lgr) +{ +	refcount_inc(&lgr->refcnt); +} + +void smc_lgr_put(struct smc_link_group *lgr) +{ +	if (refcount_dec_and_test(&lgr->refcnt)) +		__smc_lgr_free(lgr);  }  static void smc_sk_wake_ups(struct smc_sock *smc) @@ -1469,16 +1531,11 @@ void smc_smcd_terminate_all(struct smcd_dev *smcd)  /* Called when an SMCR device is removed or the smc module is unloaded.   * If smcibdev is given, all SMCR link groups using this device are terminated.   * If smcibdev is NULL, all SMCR link groups are terminated. - * - * We must wait here for QPs been destroyed before we destroy the CQs, - * or we won't received any CQEs and cdc_pend_tx_wr cannot reach 0 thus - * smc_sock cannot be released.   */  void smc_smcr_terminate_all(struct smc_ib_device *smcibdev)  {  	struct smc_link_group *lgr, *lg;  	LIST_HEAD(lgr_free_list); -	LIST_HEAD(lgr_linkdown_list);  	int i;  	spin_lock_bh(&smc_lgr_list.lock); @@ -1490,7 +1547,7 @@ void smc_smcr_terminate_all(struct smc_ib_device *smcibdev)  		list_for_each_entry_safe(lgr, lg, &smc_lgr_list.list, list) {  			for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {  				if (lgr->lnk[i].smcibdev == smcibdev) -					list_move_tail(&lgr->list, &lgr_linkdown_list); +					smcr_link_down_cond_sched(&lgr->lnk[i]);  			}  		}  	} @@ -1502,16 +1559,6 @@ void smc_smcr_terminate_all(struct smc_ib_device *smcibdev)  		__smc_lgr_terminate(lgr, false);  	} -	list_for_each_entry_safe(lgr, lg, &lgr_linkdown_list, list) { -		for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { -			if (lgr->lnk[i].smcibdev == smcibdev) { -				mutex_lock(&lgr->llc_conf_mutex); -				smcr_link_down_cond(&lgr->lnk[i]); -				mutex_unlock(&lgr->llc_conf_mutex); -			} -		} -	} -  	if (smcibdev) {  		if (atomic_read(&smcibdev->lnk_cnt))  			wait_event(smcibdev->lnks_deleted, @@ -1856,6 +1903,10 @@ create:  			goto out;  		}  	} +	smc_lgr_hold(conn->lgr); /* lgr_put in smc_conn_free() */ +	if (!conn->lgr->is_smcd) +		smcr_link_hold(conn->lnk); /* link_put in smc_conn_free() */ +	conn->freed = 0;  	conn->local_tx_ctrl.common.type = SMC_CDC_MSG_TYPE;  	conn->local_tx_ctrl.len = SMC_WR_TX_SIZE;  	conn->urg_state = SMC_URG_READ; @@ -2240,14 +2291,16 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_smcd, bool is_rmb)  void smc_sndbuf_sync_sg_for_cpu(struct smc_connection *conn)  { -	if (!conn->lgr || conn->lgr->is_smcd || !smc_link_active(conn->lnk)) +	if (!smc_conn_lgr_valid(conn) || conn->lgr->is_smcd || +	    !smc_link_active(conn->lnk))  		return;  	smc_ib_sync_sg_for_cpu(conn->lnk, conn->sndbuf_desc, DMA_TO_DEVICE);  }  void smc_sndbuf_sync_sg_for_device(struct smc_connection *conn)  { -	if (!conn->lgr || conn->lgr->is_smcd || !smc_link_active(conn->lnk)) +	if (!smc_conn_lgr_valid(conn) || conn->lgr->is_smcd || +	    !smc_link_active(conn->lnk))  		return;  	smc_ib_sync_sg_for_device(conn->lnk, conn->sndbuf_desc, DMA_TO_DEVICE);  } @@ -2256,7 +2309,7 @@ void smc_rmb_sync_sg_for_cpu(struct smc_connection *conn)  {  	int i; -	if (!conn->lgr || conn->lgr->is_smcd) +	if (!smc_conn_lgr_valid(conn) || conn->lgr->is_smcd)  		return;  	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {  		if (!smc_link_active(&conn->lgr->lnk[i])) @@ -2270,7 +2323,7 @@ void smc_rmb_sync_sg_for_device(struct smc_connection *conn)  {  	int i; -	if (!conn->lgr || conn->lgr->is_smcd) +	if (!smc_conn_lgr_valid(conn) || conn->lgr->is_smcd)  		return;  	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {  		if (!smc_link_active(&conn->lgr->lnk[i])) diff --git a/net/smc/smc_core.h b/net/smc/smc_core.h index 521c64a3d8d3..4cb03e942364 100644 --- a/net/smc/smc_core.h +++ b/net/smc/smc_core.h @@ -137,6 +137,8 @@ struct smc_link {  	u8			peer_link_uid[SMC_LGR_ID_SIZE]; /* peer uid */  	u8			link_idx;	/* index in lgr link array */  	u8			link_is_asym;	/* is link asymmetric? */ +	u8			clearing : 1;	/* link is being cleared */ +	refcount_t		refcnt;		/* link reference count */  	struct smc_link_group	*lgr;		/* parent link group */  	struct work_struct	link_down_wrk;	/* wrk to bring link down */  	char			ibname[IB_DEVICE_NAME_MAX]; /* ib device name */ @@ -249,6 +251,7 @@ struct smc_link_group {  	u8			terminating : 1;/* lgr is terminating */  	u8			freeing : 1;	/* lgr is being freed */ +	refcount_t		refcnt;		/* lgr reference count */  	bool			is_smcd;	/* SMC-R or SMC-D */  	u8			smc_version;  	u8			negotiated_eid[SMC_MAX_EID_LEN]; @@ -409,6 +412,11 @@ static inline struct smc_connection *smc_lgr_find_conn(  	return res;  } +static inline bool smc_conn_lgr_valid(struct smc_connection *conn) +{ +	return conn->lgr && conn->alert_token_local; +} +  /*   * Returns true if the specified link is usable.   * @@ -487,6 +495,8 @@ struct smc_clc_msg_accept_confirm;  void smc_lgr_cleanup_early(struct smc_link_group *lgr);  void smc_lgr_terminate_sched(struct smc_link_group *lgr); +void smc_lgr_hold(struct smc_link_group *lgr); +void smc_lgr_put(struct smc_link_group *lgr);  void smcr_port_add(struct smc_ib_device *smcibdev, u8 ibport);  void smcr_port_err(struct smc_ib_device *smcibdev, u8 ibport);  void smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid, @@ -518,6 +528,8 @@ void smc_core_exit(void);  int smcr_link_init(struct smc_link_group *lgr, struct smc_link *lnk,  		   u8 link_idx, struct smc_init_info *ini);  void smcr_link_clear(struct smc_link *lnk, bool log); +void smcr_link_hold(struct smc_link *lnk); +void smcr_link_put(struct smc_link *lnk);  void smc_switch_link_and_count(struct smc_connection *conn,  			       struct smc_link *to_lnk);  int smcr_buf_map_lgr(struct smc_link *lnk); diff --git a/net/smc/smc_diag.c b/net/smc/smc_diag.c index 7c8dad28c18d..1fca2f90a9c7 100644 --- a/net/smc/smc_diag.c +++ b/net/smc/smc_diag.c @@ -89,7 +89,7 @@ static int __smc_diag_dump(struct sock *sk, struct sk_buff *skb,  	r->diag_state = sk->sk_state;  	if (smc->use_fallback)  		r->diag_mode = SMC_DIAG_MODE_FALLBACK_TCP; -	else if (smc->conn.lgr && smc->conn.lgr->is_smcd) +	else if (smc_conn_lgr_valid(&smc->conn) && smc->conn.lgr->is_smcd)  		r->diag_mode = SMC_DIAG_MODE_SMCD;  	else  		r->diag_mode = SMC_DIAG_MODE_SMCR; @@ -142,17 +142,15 @@ static int __smc_diag_dump(struct sock *sk, struct sk_buff *skb,  			goto errout;  	} -	if (smc->conn.lgr && !smc->conn.lgr->is_smcd && +	if (smc_conn_lgr_valid(&smc->conn) && !smc->conn.lgr->is_smcd &&  	    (req->diag_ext & (1 << (SMC_DIAG_LGRINFO - 1))) &&  	    !list_empty(&smc->conn.lgr->list)) {  		struct smc_link *link = smc->conn.lnk; -		struct net *net = read_pnet(&link->smcibdev->ibdev->coredev.rdma_net);  		struct smc_diag_lgrinfo linfo = {  			.role = smc->conn.lgr->role,  			.lnk[0].ibport = link->ibport,  			.lnk[0].link_id = link->link_id, -			.lnk[0].net_cookie = net->net_cookie,  		};  		memcpy(linfo.lnk[0].ibname, @@ -164,7 +162,7 @@ static int __smc_diag_dump(struct sock *sk, struct sk_buff *skb,  		if (nla_put(skb, SMC_DIAG_LGRINFO, sizeof(linfo), &linfo) < 0)  			goto errout;  	} -	if (smc->conn.lgr && smc->conn.lgr->is_smcd && +	if (smc_conn_lgr_valid(&smc->conn) && smc->conn.lgr->is_smcd &&  	    (req->diag_ext & (1 << (SMC_DIAG_DMBINFO - 1))) &&  	    !list_empty(&smc->conn.lgr->list)) {  		struct smc_connection *conn = &smc->conn; diff --git a/net/smc/smc_pnet.c b/net/smc/smc_pnet.c index db9825c01e0a..0599246c0376 100644 --- a/net/smc/smc_pnet.c +++ b/net/smc/smc_pnet.c @@ -368,8 +368,6 @@ static int smc_pnet_add_eth(struct smc_pnettable *pnettable, struct net *net,  	new_pe->type = SMC_PNET_ETH;  	memcpy(new_pe->pnet_name, pnet_name, SMC_MAX_PNETID_LEN);  	strncpy(new_pe->eth_name, eth_name, IFNAMSIZ); -	new_pe->ndev = ndev; -	netdev_tracker_alloc(ndev, &new_pe->dev_tracker, GFP_KERNEL);  	rc = -EEXIST;  	new_netdev = true;  	write_lock(&pnettable->lock); @@ -381,6 +379,11 @@ static int smc_pnet_add_eth(struct smc_pnettable *pnettable, struct net *net,  		}  	}  	if (new_netdev) { +		if (ndev) { +			new_pe->ndev = ndev; +			netdev_tracker_alloc(ndev, &new_pe->dev_tracker, +					     GFP_ATOMIC); +		}  		list_add_tail(&new_pe->list, &pnettable->pnetlist);  		write_unlock(&pnettable->lock);  	} else { diff --git a/net/smc/smc_wr.h b/net/smc/smc_wr.h index 47512ccce5ef..a54e90a1110f 100644 --- a/net/smc/smc_wr.h +++ b/net/smc/smc_wr.h @@ -125,10 +125,6 @@ int smc_wr_tx_v2_send(struct smc_link *link,  int smc_wr_tx_send_wait(struct smc_link *link, struct smc_wr_tx_pend_priv *priv,  			unsigned long timeout);  void smc_wr_tx_cq_handler(struct ib_cq *ib_cq, void *cq_context); -void smc_wr_tx_dismiss_slots(struct smc_link *lnk, u8 wr_rx_hdr_type, -			     smc_wr_tx_filter filter, -			     smc_wr_tx_dismisser dismisser, -			     unsigned long data);  void smc_wr_tx_wait_no_pending_sends(struct smc_link *link);  int smc_wr_rx_register_handler(struct smc_wr_rx_handler *handler);  |