diff options
Diffstat (limited to 'net/smc/smc_core.c')
| -rw-r--r-- | net/smc/smc_core.c | 193 | 
1 files changed, 133 insertions, 60 deletions
| diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index a6849362f4dd..29525d03b253 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -171,8 +171,10 @@ static int smc_lgr_register_conn(struct smc_connection *conn, bool first)  	if (!conn->lgr->is_smcd) {  		rc = smcr_lgr_conn_assign_link(conn, first); -		if (rc) +		if (rc) { +			conn->lgr = NULL;  			return rc; +		}  	}  	/* find a new alert_token_local value not yet used by some connection  	 * in this link group @@ -209,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) @@ -348,6 +349,9 @@ static int smc_nl_fill_lgr(struct smc_link_group *lgr,  		goto errattr;  	if (nla_put_u8(skb, SMC_NLA_LGR_R_VLAN_ID, lgr->vlan_id))  		goto errattr; +	if (nla_put_u64_64bit(skb, SMC_NLA_LGR_R_NET_COOKIE, +			      lgr->net->net_cookie, SMC_NLA_LGR_R_PAD)) +		goto errattr;  	memcpy(smc_target, lgr->pnet_id, SMC_MAX_PNETID_LEN);  	smc_target[SMC_MAX_PNETID_LEN] = 0;  	if (nla_put_string(skb, SMC_NLA_LGR_R_PNETID, smc_target)) @@ -622,15 +626,13 @@ int smcd_nl_get_lgr(struct sk_buff *skb, struct netlink_callback *cb)  	return skb->len;  } -void smc_lgr_cleanup_early(struct smc_connection *conn) +void smc_lgr_cleanup_early(struct smc_link_group *lgr)  { -	struct smc_link_group *lgr = conn->lgr;  	spinlock_t *lgr_lock;  	if (!lgr)  		return; -	smc_conn_free(conn);  	smc_lgr_list_head(lgr, &lgr_lock);  	spin_lock_bh(lgr_lock);  	/* do not use this link group for new connections */ @@ -746,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); @@ -803,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;  } @@ -841,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); @@ -897,6 +904,7 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini)  			smc_wr_free_lgr_mem(lgr);  			goto free_wq;  		} +		lgr->net = smc_ib_net(lnk->smcibdev);  		lgr_list = &smc_lgr_list.list;  		lgr_lock = &smc_lgr_list.lock;  		atomic_inc(&lgr_cnt); @@ -992,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, @@ -1101,18 +1113,24 @@ static void smcr_buf_unuse(struct smc_buf_desc *rmb_desc,  		smc_buf_free(lgr, true, rmb_desc);  	} else {  		rmb_desc->used = 0; +		memset(rmb_desc->cpu_addr, 0, rmb_desc->len);  	}  }  static void smc_buf_unuse(struct smc_connection *conn,  			  struct smc_link_group *lgr)  { -	if (conn->sndbuf_desc) +	if (conn->sndbuf_desc) {  		conn->sndbuf_desc->used = 0; -	if (conn->rmb_desc && lgr->is_smcd) +		memset(conn->sndbuf_desc->cpu_addr, 0, conn->sndbuf_desc->len); +	} +	if (conn->rmb_desc && lgr->is_smcd) {  		conn->rmb_desc->used = 0; -	else if (conn->rmb_desc) +		memset(conn->rmb_desc->cpu_addr, 0, conn->rmb_desc->len + +		       sizeof(struct smcd_cdc_msg)); +	} else if (conn->rmb_desc) {  		smcr_buf_unuse(conn->rmb_desc, lgr); +	}  }  /* remove a finished connection from its link group */ @@ -1120,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); @@ -1138,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 */ @@ -1193,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); @@ -1208,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, @@ -1280,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)  { @@ -1295,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) @@ -1459,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); @@ -1480,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]);  			}  		}  	} @@ -1492,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, @@ -1542,9 +1599,9 @@ void smcr_lgr_set_type(struct smc_link_group *lgr, enum smc_lgr_type new_type)  		lgr_type = "ASYMMETRIC_LOCAL";  		break;  	} -	pr_warn_ratelimited("smc: SMC-R lg %*phN state changed: " +	pr_warn_ratelimited("smc: SMC-R lg %*phN net %llu state changed: "  			    "%s, pnetid %.16s\n", SMC_LGR_ID_SIZE, &lgr->id, -			    lgr_type, lgr->pnet_id); +			    lgr->net->net_cookie, lgr_type, lgr->pnet_id);  }  /* set new lgr type and tag a link as asymmetric */ @@ -1579,7 +1636,8 @@ void smcr_port_add(struct smc_ib_device *smcibdev, u8 ibport)  		if (strncmp(smcibdev->pnetid[ibport - 1], lgr->pnet_id,  			    SMC_MAX_PNETID_LEN) ||  		    lgr->type == SMC_LGR_SYMMETRIC || -		    lgr->type == SMC_LGR_ASYMMETRIC_PEER) +		    lgr->type == SMC_LGR_ASYMMETRIC_PEER || +		    !rdma_dev_access_netns(smcibdev->ibdev, lgr->net))  			continue;  		/* trigger local add link processing */ @@ -1737,8 +1795,10 @@ static bool smcr_lgr_match(struct smc_link_group *lgr, u8 smcr_version,  			   u8 peer_systemid[],  			   u8 peer_gid[],  			   u8 peer_mac_v1[], -			   enum smc_lgr_role role, u32 clcqpn) +			   enum smc_lgr_role role, u32 clcqpn, +			   struct net *net)  { +	struct smc_link *lnk;  	int i;  	if (memcmp(lgr->peer_systemid, peer_systemid, SMC_SYSTEMID_LEN) || @@ -1746,12 +1806,17 @@ static bool smcr_lgr_match(struct smc_link_group *lgr, u8 smcr_version,  		return false;  	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { -		if (!smc_link_active(&lgr->lnk[i])) +		lnk = &lgr->lnk[i]; + +		if (!smc_link_active(lnk))  			continue; -		if ((lgr->role == SMC_SERV || lgr->lnk[i].peer_qpn == clcqpn) && -		    !memcmp(lgr->lnk[i].peer_gid, peer_gid, SMC_GID_SIZE) && +		/* use verbs API to check netns, instead of lgr->net */ +		if (!rdma_dev_access_netns(lnk->smcibdev->ibdev, net)) +			return false; +		if ((lgr->role == SMC_SERV || lnk->peer_qpn == clcqpn) && +		    !memcmp(lnk->peer_gid, peer_gid, SMC_GID_SIZE) &&  		    (smcr_version == SMC_V2 || -		     !memcmp(lgr->lnk[i].peer_mac, peer_mac_v1, ETH_ALEN))) +		     !memcmp(lnk->peer_mac, peer_mac_v1, ETH_ALEN)))  			return true;  	}  	return false; @@ -1767,6 +1832,7 @@ static bool smcd_lgr_match(struct smc_link_group *lgr,  int smc_conn_create(struct smc_sock *smc, struct smc_init_info *ini)  {  	struct smc_connection *conn = &smc->conn; +	struct net *net = sock_net(&smc->sk);  	struct list_head *lgr_list;  	struct smc_link_group *lgr;  	enum smc_lgr_role role; @@ -1793,7 +1859,7 @@ int smc_conn_create(struct smc_sock *smc, struct smc_init_info *ini)  		     smcr_lgr_match(lgr, ini->smcr_version,  				    ini->peer_systemid,  				    ini->peer_gid, ini->peer_mac, role, -				    ini->ib_clcqpn)) && +				    ini->ib_clcqpn, net)) &&  		    !lgr->sync_err &&  		    (ini->smcd_version == SMC_V2 ||  		     lgr->vlan_id == ini->vlan_id) && @@ -1832,9 +1898,15 @@ create:  		write_lock_bh(&lgr->conns_lock);  		rc = smc_lgr_register_conn(conn, true);  		write_unlock_bh(&lgr->conns_lock); -		if (rc) +		if (rc) { +			smc_lgr_cleanup_early(lgr);  			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; @@ -2163,7 +2235,6 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_smcd, bool is_rmb)  		if (buf_desc) {  			SMC_STAT_RMB_SIZE(smc, is_smcd, is_rmb, bufsize);  			SMC_STAT_BUF_REUSE(smc, is_smcd, is_rmb); -			memset(buf_desc->cpu_addr, 0, bufsize);  			break; /* found reusable slot */  		} @@ -2220,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);  } @@ -2236,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])) @@ -2250,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])) |