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

Commit 0d18a0cb authored by Karsten Graul's avatar Karsten Graul Committed by David S. Miller
Browse files

net/smc: improve delete link processing



Send an orderly DELETE LINK request before termination of a link group,
add support for client triggered DELETE LINK processing. And send a
disorderly DELETE LINK before module is unloaded.

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>
parent 603cc149
Loading
Loading
Loading
Loading
+42 −5
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#define SMC_LGR_NUM_INCR		256
#define SMC_LGR_FREE_DELAY_SERV		(600 * HZ)
#define SMC_LGR_FREE_DELAY_CLNT		(SMC_LGR_FREE_DELAY_SERV + 10 * HZ)
#define SMC_LGR_FREE_DELAY_FAST		(8 * HZ)

static struct smc_lgr_list smc_lgr_list = {	/* established link groups */
	.lock = __SPIN_LOCK_UNLOCKED(smc_lgr_list.lock),
@@ -51,6 +52,11 @@ static void smc_lgr_schedule_free_work(struct smc_link_group *lgr)
			 SMC_LGR_FREE_DELAY_CLNT : SMC_LGR_FREE_DELAY_SERV);
}

void smc_lgr_schedule_free_work_fast(struct smc_link_group *lgr)
{
	mod_delayed_work(system_wq, &lgr->free_work, SMC_LGR_FREE_DELAY_FAST);
}

/* Register connection's alert token in our lookup structure.
 * To use rbtrees we have to implement our own insert core.
 * Requires @conns_lock
@@ -133,6 +139,20 @@ static void smc_lgr_unregister_conn(struct smc_connection *conn)
	smc_lgr_schedule_free_work(lgr);
}

/* Send delete link, either as client to request the initiation
 * of the DELETE LINK sequence from server; or as server to
 * initiate the delete processing. See smc_llc_rx_delete_link().
 */
static int smc_link_send_delete(struct smc_link *lnk)
{
	if (lnk->state == SMC_LNK_ACTIVE &&
	    !smc_llc_send_delete_link(lnk, SMC_LLC_REQ, true)) {
		smc_llc_link_deleting(lnk);
		return 0;
	}
	return -ENOTCONN;
}

static void smc_lgr_free_work(struct work_struct *work)
{
	struct smc_link_group *lgr = container_of(to_delayed_work(work),
@@ -153,10 +173,21 @@ static void smc_lgr_free_work(struct work_struct *work)
	list_del_init(&lgr->list); /* remove from smc_lgr_list */
free:
	spin_unlock_bh(&smc_lgr_list.lock);

	if (!lgr->is_smcd && !lgr->terminating)	{
		/* try to send del link msg, on error free lgr immediately */
		if (!smc_link_send_delete(&lgr->lnk[SMC_SINGLE_LINK])) {
			/* reschedule in case we never receive a response */
			smc_lgr_schedule_free_work(lgr);
			return;
		}
	}

	if (!delayed_work_pending(&lgr->free_work)) {
		if (!lgr->is_smcd &&
		    lgr->lnk[SMC_SINGLE_LINK].state != SMC_LNK_INACTIVE)
			smc_llc_link_inactive(&lgr->lnk[SMC_SINGLE_LINK]);
		struct smc_link *lnk = &lgr->lnk[SMC_SINGLE_LINK];

		if (!lgr->is_smcd && lnk->state != SMC_LNK_INACTIVE)
			smc_llc_link_inactive(lnk);
		smc_lgr_free(lgr);
	}
}
@@ -984,8 +1015,14 @@ void smc_core_exit(void)
	spin_unlock_bh(&smc_lgr_list.lock);
	list_for_each_entry_safe(lgr, lg, &lgr_freeing_list, list) {
		list_del_init(&lgr->list);
		if (!lgr->is_smcd)
			smc_llc_link_inactive(&lgr->lnk[SMC_SINGLE_LINK]);
		if (!lgr->is_smcd) {
			struct smc_link *lnk = &lgr->lnk[SMC_SINGLE_LINK];

			if (lnk->state == SMC_LNK_ACTIVE)
				smc_llc_send_delete_link(lnk, SMC_LLC_REQ,
							 false);
			smc_llc_link_inactive(lnk);
		}
		cancel_delayed_work_sync(&lgr->free_work);
		smc_lgr_free(lgr); /* free link group */
	}
+3 −1
Original line number Diff line number Diff line
@@ -34,7 +34,8 @@ enum smc_lgr_role { /* possible roles of a link group */
enum smc_link_state {			/* possible states of a link */
	SMC_LNK_INACTIVE,	/* link is inactive */
	SMC_LNK_ACTIVATING,	/* link is being activated */
	SMC_LNK_ACTIVE		/* link is active */
	SMC_LNK_ACTIVE,		/* link is active */
	SMC_LNK_DELETING,	/* link is being deleted */
};

#define SMC_WR_BUF_SIZE		48	/* size of work request buffer */
@@ -265,6 +266,7 @@ int smc_conn_create(struct smc_sock *smc, bool is_smcd, int srv_first_contact,
		    struct smc_clc_msg_local *lcl, struct smcd_dev *smcd,
		    u64 peer_gid);
void smcd_conn_free(struct smc_connection *conn);
void smc_lgr_schedule_free_work_fast(struct smc_link_group *lgr);
void smc_core_exit(void);

static inline struct smc_link_group *smc_get_lgr(struct smc_link *link)
+19 −11
Original line number Diff line number Diff line
@@ -278,7 +278,7 @@ int smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[],
/* prepare a delete link message */
static void smc_llc_prep_delete_link(struct smc_llc_msg_del_link *delllc,
				     struct smc_link *link,
				     enum smc_llc_reqresp reqresp)
				     enum smc_llc_reqresp reqresp, bool orderly)
{
	memset(delllc, 0, sizeof(*delllc));
	delllc->hd.common.type = SMC_LLC_DELETE_LINK;
@@ -287,13 +287,14 @@ static void smc_llc_prep_delete_link(struct smc_llc_msg_del_link *delllc,
		delllc->hd.flags |= SMC_LLC_FLAG_RESP;
	/* DEL_LINK_ALL because only 1 link supported */
	delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL;
	if (orderly)
		delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
	delllc->link_num = link->link_id;
}

/* send DELETE LINK request or response */
int smc_llc_send_delete_link(struct smc_link *link,
			     enum smc_llc_reqresp reqresp)
			     enum smc_llc_reqresp reqresp, bool orderly)
{
	struct smc_llc_msg_del_link *delllc;
	struct smc_wr_tx_pend_priv *pend;
@@ -304,7 +305,7 @@ int smc_llc_send_delete_link(struct smc_link *link,
	if (rc)
		return rc;
	delllc = (struct smc_llc_msg_del_link *)wr_buf;
	smc_llc_prep_delete_link(delllc, link, reqresp);
	smc_llc_prep_delete_link(delllc, link, reqresp, orderly);
	/* send llc message */
	rc = smc_wr_tx_send(link, pend);
	return rc;
@@ -438,17 +439,19 @@ static void smc_llc_rx_delete_link(struct smc_link *link,

	if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
		if (lgr->role == SMC_SERV)
			smc_lgr_terminate(lgr);
			smc_lgr_schedule_free_work_fast(lgr);
	} else {
		if (lgr->role == SMC_SERV) {
		smc_lgr_forget(lgr);
			smc_llc_prep_delete_link(llc, link, SMC_LLC_REQ);
			smc_llc_send_message(link, llc, sizeof(*llc));
		smc_llc_link_deleting(link);
		if (lgr->role == SMC_SERV) {
			/* client asks to delete this link, send request */
			smc_llc_prep_delete_link(llc, link, SMC_LLC_REQ, true);
		} else {
			smc_llc_prep_delete_link(llc, link, SMC_LLC_RESP);
			smc_llc_send_message(link, llc, sizeof(*llc));
			smc_lgr_terminate(lgr);
			/* server requests to delete this link, send response */
			smc_llc_prep_delete_link(llc, link, SMC_LLC_RESP, true);
		}
		smc_llc_send_message(link, llc, sizeof(*llc));
		smc_lgr_schedule_free_work_fast(lgr);
	}
}

@@ -622,6 +625,11 @@ void smc_llc_link_active(struct smc_link *link, int testlink_time)
	}
}

void smc_llc_link_deleting(struct smc_link *link)
{
	link->state = SMC_LNK_DELETING;
}

/* called in tasklet context */
void smc_llc_link_inactive(struct smc_link *link)
{
+2 −1
Original line number Diff line number Diff line
@@ -41,9 +41,10 @@ int smc_llc_send_confirm_link(struct smc_link *lnk,
int smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[],
			  enum smc_llc_reqresp reqresp);
int smc_llc_send_delete_link(struct smc_link *link,
			     enum smc_llc_reqresp reqresp);
			     enum smc_llc_reqresp reqresp, bool orderly);
int smc_llc_link_init(struct smc_link *link);
void smc_llc_link_active(struct smc_link *link, int testlink_time);
void smc_llc_link_deleting(struct smc_link *link);
void smc_llc_link_inactive(struct smc_link *link);
void smc_llc_link_clear(struct smc_link *link);
int smc_llc_do_confirm_rkey(struct smc_link *link,
+2 −5
Original line number Diff line number Diff line
@@ -182,17 +182,14 @@ int smc_wr_tx_get_free_slot(struct smc_link *link,
		if (rc)
			return rc;
	} else {
		struct smc_link_group *lgr;

		lgr = smc_get_lgr(link);
		rc = wait_event_timeout(
			link->wr_tx_wait,
			list_empty(&lgr->list) || /* lgr terminated */
			link->state == SMC_LNK_INACTIVE ||
			(smc_wr_tx_get_free_slot_index(link, &idx) != -EBUSY),
			SMC_WR_TX_WAIT_FREE_SLOT_TIME);
		if (!rc) {
			/* timeout - terminate connections */
			smc_lgr_terminate(lgr);
			smc_lgr_terminate(smc_get_lgr(link));
			return -EPIPE;
		}
		if (idx == link->wr_tx_cnt)