diff options
Diffstat (limited to 'net/smc/smc_llc.c')
| -rw-r--r-- | net/smc/smc_llc.c | 212 | 
1 files changed, 138 insertions, 74 deletions
| diff --git a/net/smc/smc_llc.c b/net/smc/smc_llc.c index 391237b601fe..df5b0a6ea848 100644 --- a/net/smc/smc_llc.c +++ b/net/smc/smc_llc.c @@ -186,6 +186,26 @@ static inline void smc_llc_flow_qentry_set(struct smc_llc_flow *flow,  	flow->qentry = qentry;  } +static void smc_llc_flow_parallel(struct smc_link_group *lgr, u8 flow_type, +				  struct smc_llc_qentry *qentry) +{ +	u8 msg_type = qentry->msg.raw.hdr.common.type; + +	if ((msg_type == SMC_LLC_ADD_LINK || msg_type == SMC_LLC_DELETE_LINK) && +	    flow_type != msg_type && !lgr->delayed_event) { +		lgr->delayed_event = qentry; +		return; +	} +	/* drop parallel or already-in-progress llc requests */ +	if (flow_type != msg_type) +		pr_warn_once("smc: SMC-R lg %*phN dropped parallel " +			     "LLC msg: msg %d flow %d role %d\n", +			     SMC_LGR_ID_SIZE, &lgr->id, +			     qentry->msg.raw.hdr.common.type, +			     flow_type, lgr->role); +	kfree(qentry); +} +  /* try to start a new llc flow, initiated by an incoming llc msg */  static bool smc_llc_flow_start(struct smc_llc_flow *flow,  			       struct smc_llc_qentry *qentry) @@ -195,14 +215,7 @@ static bool smc_llc_flow_start(struct smc_llc_flow *flow,  	spin_lock_bh(&lgr->llc_flow_lock);  	if (flow->type) {  		/* a flow is already active */ -		if ((qentry->msg.raw.hdr.common.type == SMC_LLC_ADD_LINK || -		     qentry->msg.raw.hdr.common.type == SMC_LLC_DELETE_LINK) && -		    !lgr->delayed_event) { -			lgr->delayed_event = qentry; -		} else { -			/* forget this llc request */ -			kfree(qentry); -		} +		smc_llc_flow_parallel(lgr, flow->type, qentry);  		spin_unlock_bh(&lgr->llc_flow_lock);  		return false;  	} @@ -222,8 +235,8 @@ static bool smc_llc_flow_start(struct smc_llc_flow *flow,  	}  	if (qentry == lgr->delayed_event)  		lgr->delayed_event = NULL; -	spin_unlock_bh(&lgr->llc_flow_lock);  	smc_llc_flow_qentry_set(flow, qentry); +	spin_unlock_bh(&lgr->llc_flow_lock);  	return true;  } @@ -251,11 +264,11 @@ again:  		return 0;  	}  	spin_unlock_bh(&lgr->llc_flow_lock); -	rc = wait_event_interruptible_timeout(lgr->llc_waiter, -			(lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE && -			 (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE || -			  lgr->llc_flow_rmt.type == allowed_remote)), -			SMC_LLC_WAIT_TIME); +	rc = wait_event_timeout(lgr->llc_flow_waiter, (list_empty(&lgr->list) || +				(lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE && +				 (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE || +				  lgr->llc_flow_rmt.type == allowed_remote))), +				SMC_LLC_WAIT_TIME * 10);  	if (!rc)  		return -ETIMEDOUT;  	goto again; @@ -272,7 +285,7 @@ void smc_llc_flow_stop(struct smc_link_group *lgr, struct smc_llc_flow *flow)  	    flow == &lgr->llc_flow_lcl)  		schedule_work(&lgr->llc_event_work);  	else -		wake_up_interruptible(&lgr->llc_waiter); +		wake_up(&lgr->llc_flow_waiter);  }  /* lnk is optional and used for early wakeup when link goes down, useful in @@ -283,26 +296,32 @@ struct smc_llc_qentry *smc_llc_wait(struct smc_link_group *lgr,  				    int time_out, u8 exp_msg)  {  	struct smc_llc_flow *flow = &lgr->llc_flow_lcl; +	u8 rcv_msg; -	wait_event_interruptible_timeout(lgr->llc_waiter, -					 (flow->qentry || -					  (lnk && !smc_link_usable(lnk)) || -					  list_empty(&lgr->list)), -					 time_out); +	wait_event_timeout(lgr->llc_msg_waiter, +			   (flow->qentry || +			    (lnk && !smc_link_usable(lnk)) || +			    list_empty(&lgr->list)), +			   time_out);  	if (!flow->qentry ||  	    (lnk && !smc_link_usable(lnk)) || list_empty(&lgr->list)) {  		smc_llc_flow_qentry_del(flow);  		goto out;  	} -	if (exp_msg && flow->qentry->msg.raw.hdr.common.type != exp_msg) { +	rcv_msg = flow->qentry->msg.raw.hdr.common.type; +	if (exp_msg && rcv_msg != exp_msg) {  		if (exp_msg == SMC_LLC_ADD_LINK && -		    flow->qentry->msg.raw.hdr.common.type == -		    SMC_LLC_DELETE_LINK) { +		    rcv_msg == SMC_LLC_DELETE_LINK) {  			/* flow_start will delay the unexpected msg */  			smc_llc_flow_start(&lgr->llc_flow_lcl,  					   smc_llc_flow_qentry_clr(flow));  			return NULL;  		} +		pr_warn_once("smc: SMC-R lg %*phN dropped unexpected LLC msg: " +			     "msg %d exp %d flow %d role %d flags %x\n", +			     SMC_LGR_ID_SIZE, &lgr->id, rcv_msg, exp_msg, +			     flow->type, lgr->role, +			     flow->qentry->msg.raw.hdr.flags);  		smc_llc_flow_qentry_del(flow);  	}  out: @@ -409,7 +428,7 @@ static int smc_llc_send_confirm_rkey(struct smc_link *send_link,  	rtok_ix = 1;  	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {  		link = &send_link->lgr->lnk[i]; -		if (link->state == SMC_LNK_ACTIVE && link != send_link) { +		if (smc_link_active(link) && link != send_link) {  			rkeyllc->rtoken[rtok_ix].link_id = link->link_id;  			rkeyllc->rtoken[rtok_ix].rmb_key =  				htonl(rmb_desc->mr_rx[link->link_idx]->rkey); @@ -876,6 +895,36 @@ out:  	return rc;  } +/* as an SMC client, invite server to start the add_link processing */ +static void smc_llc_cli_add_link_invite(struct smc_link *link, +					struct smc_llc_qentry *qentry) +{ +	struct smc_link_group *lgr = smc_get_lgr(link); +	struct smc_init_info ini; + +	if (lgr->type == SMC_LGR_SYMMETRIC || +	    lgr->type == SMC_LGR_ASYMMETRIC_PEER) +		goto out; + +	ini.vlan_id = lgr->vlan_id; +	smc_pnet_find_alt_roce(lgr, &ini, link->smcibdev); +	if (!ini.ib_dev) +		goto out; + +	smc_llc_send_add_link(link, ini.ib_dev->mac[ini.ib_port - 1], +			      ini.ib_gid, NULL, SMC_LLC_REQ); +out: +	kfree(qentry); +} + +static bool smc_llc_is_local_add_link(union smc_llc_msg *llc) +{ +	if (llc->raw.hdr.common.type == SMC_LLC_ADD_LINK && +	    !llc->add_link.qp_mtu && !llc->add_link.link_num) +		return true; +	return false; +} +  static void smc_llc_process_cli_add_link(struct smc_link_group *lgr)  {  	struct smc_llc_qentry *qentry; @@ -883,7 +932,10 @@ static void smc_llc_process_cli_add_link(struct smc_link_group *lgr)  	qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);  	mutex_lock(&lgr->llc_conf_mutex); -	smc_llc_cli_add_link(qentry->link, qentry); +	if (smc_llc_is_local_add_link(&qentry->msg)) +		smc_llc_cli_add_link_invite(qentry->link, qentry); +	else +		smc_llc_cli_add_link(qentry->link, qentry);  	mutex_unlock(&lgr->llc_conf_mutex);  } @@ -892,7 +944,7 @@ static int smc_llc_active_link_count(struct smc_link_group *lgr)  	int i, link_count = 0;  	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { -		if (!smc_link_usable(&lgr->lnk[i])) +		if (!smc_link_active(&lgr->lnk[i]))  			continue;  		link_count++;  	} @@ -1032,12 +1084,14 @@ static int smc_llc_srv_conf_link(struct smc_link *link,  	if (rc)  		return -ENOLINK;  	/* receive CONFIRM LINK response over the RoCE fabric */ -	qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_FIRST_TIME, -			      SMC_LLC_CONFIRM_LINK); -	if (!qentry) { +	qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_FIRST_TIME, 0); +	if (!qentry || +	    qentry->msg.raw.hdr.common.type != SMC_LLC_CONFIRM_LINK) {  		/* send DELETE LINK */  		smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,  					 false, SMC_LLC_DEL_LOST_PATH); +		if (qentry) +			smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);  		return -ENOLINK;  	}  	smc_llc_save_peer_uid(qentry); @@ -1139,14 +1193,14 @@ static void smc_llc_process_srv_add_link(struct smc_link_group *lgr)  	mutex_unlock(&lgr->llc_conf_mutex);  } -/* enqueue a local add_link req to trigger a new add_link flow, only as SERV */ -void smc_llc_srv_add_link_local(struct smc_link *link) +/* enqueue a local add_link req to trigger a new add_link flow */ +void smc_llc_add_link_local(struct smc_link *link)  {  	struct smc_llc_msg_add_link add_llc = {0};  	add_llc.hd.length = sizeof(add_llc);  	add_llc.hd.common.type = SMC_LLC_ADD_LINK; -	/* no dev and port needed, we as server ignore client data anyway */ +	/* no dev and port needed */  	smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc);  } @@ -1222,8 +1276,8 @@ static void smc_llc_process_cli_delete_link(struct smc_link_group *lgr)  	smc_llc_send_message(lnk, &qentry->msg); /* response */  	if (smc_link_downing(&lnk_del->state)) { -		smc_switch_conns(lgr, lnk_del, false); -		smc_wr_tx_wait_no_pending_sends(lnk_del); +		if (smc_switch_conns(lgr, lnk_del, false)) +			smc_wr_tx_wait_no_pending_sends(lnk_del);  	}  	smcr_link_clear(lnk_del, true); @@ -1297,8 +1351,8 @@ static void smc_llc_process_srv_delete_link(struct smc_link_group *lgr)  		goto out; /* asymmetric link already deleted */  	if (smc_link_downing(&lnk_del->state)) { -		smc_switch_conns(lgr, lnk_del, false); -		smc_wr_tx_wait_no_pending_sends(lnk_del); +		if (smc_switch_conns(lgr, lnk_del, false)) +			smc_wr_tx_wait_no_pending_sends(lnk_del);  	}  	if (!list_empty(&lgr->list)) {  		/* qentry is either a request from peer (send it back to @@ -1326,7 +1380,7 @@ static void smc_llc_process_srv_delete_link(struct smc_link_group *lgr)  	if (lgr->type == SMC_LGR_SINGLE && !list_empty(&lgr->list)) {  		/* trigger setup of asymm alt link */ -		smc_llc_srv_add_link_local(lnk); +		smc_llc_add_link_local(lnk);  	}  out:  	mutex_unlock(&lgr->llc_conf_mutex); @@ -1455,11 +1509,22 @@ static void smc_llc_event_handler(struct smc_llc_qentry *qentry)  		if (list_empty(&lgr->list))  			goto out;	/* lgr is terminating */  		if (lgr->role == SMC_CLNT) { -			if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK) { +			if (smc_llc_is_local_add_link(llc)) { +				if (lgr->llc_flow_lcl.type == +				    SMC_LLC_FLOW_ADD_LINK) +					break;	/* add_link in progress */ +				if (smc_llc_flow_start(&lgr->llc_flow_lcl, +						       qentry)) { +					schedule_work(&lgr->llc_add_link_work); +				} +				return; +			} +			if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK && +			    !lgr->llc_flow_lcl.qentry) {  				/* a flow is waiting for this message */  				smc_llc_flow_qentry_set(&lgr->llc_flow_lcl,  							qentry); -				wake_up_interruptible(&lgr->llc_waiter); +				wake_up(&lgr->llc_msg_waiter);  			} else if (smc_llc_flow_start(&lgr->llc_flow_lcl,  						      qentry)) {  				schedule_work(&lgr->llc_add_link_work); @@ -1474,33 +1539,18 @@ static void smc_llc_event_handler(struct smc_llc_qentry *qentry)  		if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) {  			/* a flow is waiting for this message */  			smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry); -			wake_up_interruptible(&lgr->llc_waiter); +			wake_up(&lgr->llc_msg_waiter);  			return;  		}  		break;  	case SMC_LLC_DELETE_LINK: -		if (lgr->role == SMC_CLNT) { -			/* server requests to delete this link, send response */ -			if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) { -				/* DEL LINK REQ during ADD LINK SEQ */ -				smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, -							qentry); -				wake_up_interruptible(&lgr->llc_waiter); -			} else if (smc_llc_flow_start(&lgr->llc_flow_lcl, -						      qentry)) { -				schedule_work(&lgr->llc_del_link_work); -			} -		} else { -			if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK && -			    !lgr->llc_flow_lcl.qentry) { -				/* DEL LINK REQ during ADD LINK SEQ */ -				smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, -							qentry); -				wake_up_interruptible(&lgr->llc_waiter); -			} else if (smc_llc_flow_start(&lgr->llc_flow_lcl, -						      qentry)) { -				schedule_work(&lgr->llc_del_link_work); -			} +		if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK && +		    !lgr->llc_flow_lcl.qentry) { +			/* DEL LINK REQ during ADD LINK SEQ */ +			smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry); +			wake_up(&lgr->llc_msg_waiter); +		} else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) { +			schedule_work(&lgr->llc_del_link_work);  		}  		return;  	case SMC_LLC_CONFIRM_RKEY: @@ -1566,23 +1616,30 @@ again:  static void smc_llc_rx_response(struct smc_link *link,  				struct smc_llc_qentry *qentry)  { +	enum smc_llc_flowtype flowtype = link->lgr->llc_flow_lcl.type; +	struct smc_llc_flow *flow = &link->lgr->llc_flow_lcl;  	u8 llc_type = qentry->msg.raw.hdr.common.type;  	switch (llc_type) {  	case SMC_LLC_TEST_LINK: -		if (link->state == SMC_LNK_ACTIVE) +		if (smc_link_active(link))  			complete(&link->llc_testlink_resp);  		break;  	case SMC_LLC_ADD_LINK: -	case SMC_LLC_DELETE_LINK: -	case SMC_LLC_CONFIRM_LINK:  	case SMC_LLC_ADD_LINK_CONT: +	case SMC_LLC_CONFIRM_LINK: +		if (flowtype != SMC_LLC_FLOW_ADD_LINK || flow->qentry) +			break;	/* drop out-of-flow response */ +		goto assign; +	case SMC_LLC_DELETE_LINK: +		if (flowtype != SMC_LLC_FLOW_DEL_LINK || flow->qentry) +			break;	/* drop out-of-flow response */ +		goto assign;  	case SMC_LLC_CONFIRM_RKEY:  	case SMC_LLC_DELETE_RKEY: -		/* assign responses to the local flow, we requested them */ -		smc_llc_flow_qentry_set(&link->lgr->llc_flow_lcl, qentry); -		wake_up_interruptible(&link->lgr->llc_waiter); -		return; +		if (flowtype != SMC_LLC_FLOW_RKEY || flow->qentry) +			break;	/* drop out-of-flow response */ +		goto assign;  	case SMC_LLC_CONFIRM_RKEY_CONT:  		/* not used because max links is 3 */  		break; @@ -1591,6 +1648,11 @@ static void smc_llc_rx_response(struct smc_link *link,  		break;  	}  	kfree(qentry); +	return; +assign: +	/* assign responses to the local flow, we requested them */ +	smc_llc_flow_qentry_set(&link->lgr->llc_flow_lcl, qentry); +	wake_up(&link->lgr->llc_msg_waiter);  }  static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc) @@ -1616,7 +1678,7 @@ static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc)  	spin_lock_irqsave(&lgr->llc_event_q_lock, flags);  	list_add_tail(&qentry->list, &lgr->llc_event_q);  	spin_unlock_irqrestore(&lgr->llc_event_q_lock, flags); -	schedule_work(&link->lgr->llc_event_work); +	schedule_work(&lgr->llc_event_work);  }  /* copy received msg and add it to the event queue */ @@ -1644,7 +1706,7 @@ static void smc_llc_testlink_work(struct work_struct *work)  	u8 user_data[16] = { 0 };  	int rc; -	if (link->state != SMC_LNK_ACTIVE) +	if (!smc_link_active(link))  		return;		/* don't reschedule worker */  	expire_time = link->wr_rx_tstamp + link->llc_testlink_time;  	if (time_is_after_jiffies(expire_time)) { @@ -1656,7 +1718,7 @@ static void smc_llc_testlink_work(struct work_struct *work)  	/* receive TEST LINK response over RoCE fabric */  	rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp,  						       SMC_LLC_WAIT_TIME); -	if (link->state != SMC_LNK_ACTIVE) +	if (!smc_link_active(link))  		return;		/* link state changed */  	if (rc <= 0) {  		smcr_link_down_cond_sched(link); @@ -1677,7 +1739,8 @@ void smc_llc_lgr_init(struct smc_link_group *lgr, struct smc_sock *smc)  	INIT_LIST_HEAD(&lgr->llc_event_q);  	spin_lock_init(&lgr->llc_event_q_lock);  	spin_lock_init(&lgr->llc_flow_lock); -	init_waitqueue_head(&lgr->llc_waiter); +	init_waitqueue_head(&lgr->llc_flow_waiter); +	init_waitqueue_head(&lgr->llc_msg_waiter);  	mutex_init(&lgr->llc_conf_mutex);  	lgr->llc_testlink_time = net->ipv4.sysctl_tcp_keepalive_time;  } @@ -1686,7 +1749,8 @@ void smc_llc_lgr_init(struct smc_link_group *lgr, struct smc_sock *smc)  void smc_llc_lgr_clear(struct smc_link_group *lgr)  {  	smc_llc_event_flush(lgr); -	wake_up_interruptible_all(&lgr->llc_waiter); +	wake_up_all(&lgr->llc_flow_waiter); +	wake_up_all(&lgr->llc_msg_waiter);  	cancel_work_sync(&lgr->llc_event_work);  	cancel_work_sync(&lgr->llc_add_link_work);  	cancel_work_sync(&lgr->llc_del_link_work); |