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

Commit 662921cd authored by Jon Paul Maloy's avatar Jon Paul Maloy Committed by David S. Miller
Browse files

tipc: merge link->exec_mode and link->state into one FSM



Until now, we have been handling link failover and synchronization
by using an additional link state variable, "exec_mode". This variable
is not independent of the link FSM state, something causing a risk of
inconsistencies, apart from the fact that it clutters the code.

The conditions are now in place to define a new link FSM that covers
all existing use cases, including failover and synchronization, and
eliminate the "exec_mode" field altogether. The FSM must also support
non-atomic resetting of links, which will be introduced later.

The new link FSM is shown below, with 7 states and 8 events.
Only events leading to state change are shown as edges.

+------------------------------------+
|RESET_EVT                           |
|                                    |
|                             +--------------+
|           +-----------------|   SYNCHING   |-----------------+
|           |FAILURE_EVT      +--------------+   PEER_RESET_EVT|
|           |                  A            |                  |
|           |                  |            |                  |
|           |                  |            |                  |
|           |                  |SYNCH_      |SYNCH_            |
|           |                  |BEGIN_EVT   |END_EVT           |
|           |                  |            |                  |
|           V                  |            V                  V
|    +-------------+          +--------------+          +------------+
|    |  RESETTING  |<---------|  ESTABLISHED |--------->| PEER_RESET |
|    +-------------+ FAILURE_ +--------------+ PEER_    +------------+
|           |        EVT        |    A         RESET_EVT       |
|           |                   |    |                         |
|           |                   |    |                         |
|           |    +--------------+    |                         |
|  RESET_EVT|    |RESET_EVT          |ESTABLISH_EVT            |
|           |    |                   |                         |
|           |    |                   |                         |
|           V    V                   |                         |
|    +-------------+          +--------------+        RESET_EVT|
+--->|    RESET    |--------->| ESTABLISHING |<----------------+
     +-------------+ PEER_    +--------------+
      |           A  RESET_EVT       |
      |           |                  |
      |           |                  |
      |FAILOVER_  |FAILOVER_         |FAILOVER_
      |BEGIN_EVT  |END_EVT           |BEGIN_EVT
      |           |                  |
      V           |                  |
     +-------------+                 |
     | FAILINGOVER |<----------------+
     +-------------+

These changes are fully backwards compatible.

Tested-by: default avatarYing Xue <ying.xue@windriver.com>
Signed-off-by: default avatarJon Maloy <jon.maloy@ericsson.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5045f7b9
Loading
Loading
Loading
Loading
+192 −158
Original line number Original line Diff line number Diff line
@@ -50,7 +50,6 @@
 */
 */
static const char *link_co_err = "Link tunneling error, ";
static const char *link_co_err = "Link tunneling error, ";
static const char *link_rst_msg = "Resetting link ";
static const char *link_rst_msg = "Resetting link ";
static const char *link_unk_evt = "Unknown link event ";


static const struct nla_policy tipc_nl_link_policy[TIPC_NLA_LINK_MAX + 1] = {
static const struct nla_policy tipc_nl_link_policy[TIPC_NLA_LINK_MAX + 1] = {
	[TIPC_NLA_LINK_UNSPEC]		= { .type = NLA_UNSPEC },
	[TIPC_NLA_LINK_UNSPEC]		= { .type = NLA_UNSPEC },
@@ -85,46 +84,23 @@ static const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = {
 */
 */
#define WILDCARD_SESSION 0x10000
#define WILDCARD_SESSION 0x10000


/* State value stored in 'failover_pkts'
/* Link FSM states:
 */
 */
#define FIRST_FAILOVER 0xffffu

/* Link FSM states and events:
 */
enum {
	TIPC_LINK_WORKING,
	TIPC_LINK_PROBING,
	TIPC_LINK_RESETTING,
	TIPC_LINK_ESTABLISHING
};

enum {
enum {
	PEER_RESET_EVT    = RESET_MSG,
	LINK_ESTABLISHED     = 0xe,
	ACTIVATE_EVT      = ACTIVATE_MSG,
	LINK_ESTABLISHING    = 0xe  << 4,
	TRAFFIC_EVT,      /* Any other valid msg from peer */
	LINK_RESET           = 0x1  << 8,
	SILENCE_EVT       /* Peer was silent during last timer interval*/
	LINK_RESETTING       = 0x2  << 12,
	LINK_PEER_RESET      = 0xd  << 16,
	LINK_FAILINGOVER     = 0xf  << 20,
	LINK_SYNCHING        = 0xc  << 24
};
};


/* Link FSM state checking routines
/* Link FSM state checking routines
 */
 */
static int link_working(struct tipc_link *l)
static int link_is_up(struct tipc_link *l)
{
	return l->state == TIPC_LINK_WORKING;
}

static int link_probing(struct tipc_link *l)
{
	return l->state == TIPC_LINK_PROBING;
}

static int link_resetting(struct tipc_link *l)
{
{
	return l->state == TIPC_LINK_RESETTING;
	return l->state & (LINK_ESTABLISHED | LINK_SYNCHING);
}

static int link_establishing(struct tipc_link *l)
{
	return l->state == TIPC_LINK_ESTABLISHING;
}
}


static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
@@ -141,11 +117,29 @@ static bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb);
/*
/*
 *  Simple non-static link routines (i.e. referenced outside this file)
 *  Simple non-static link routines (i.e. referenced outside this file)
 */
 */
int tipc_link_is_up(struct tipc_link *l_ptr)
bool tipc_link_is_up(struct tipc_link *l)
{
{
	if (!l_ptr)
	return link_is_up(l);
		return 0;
}
	return link_working(l_ptr) || link_probing(l_ptr);

bool tipc_link_is_reset(struct tipc_link *l)
{
	return l->state & (LINK_RESET | LINK_FAILINGOVER | LINK_ESTABLISHING);
}

bool tipc_link_is_synching(struct tipc_link *l)
{
	return l->state == LINK_SYNCHING;
}

bool tipc_link_is_failingover(struct tipc_link *l)
{
	return l->state == LINK_FAILINGOVER;
}

bool tipc_link_is_blocked(struct tipc_link *l)
{
	return l->state & (LINK_RESETTING | LINK_PEER_RESET | LINK_FAILINGOVER);
}
}


int tipc_link_is_active(struct tipc_link *l)
int tipc_link_is_active(struct tipc_link *l)
@@ -210,7 +204,7 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
	l_ptr->tolerance = b_ptr->tolerance;
	l_ptr->tolerance = b_ptr->tolerance;
	l_ptr->snd_nxt = 1;
	l_ptr->snd_nxt = 1;
	l_ptr->rcv_nxt = 1;
	l_ptr->rcv_nxt = 1;
	l_ptr->state = TIPC_LINK_RESETTING;
	l_ptr->state = LINK_RESET;


	l_ptr->pmsg = (struct tipc_msg *)&l_ptr->proto_msg;
	l_ptr->pmsg = (struct tipc_msg *)&l_ptr->proto_msg;
	msg = l_ptr->pmsg;
	msg = l_ptr->pmsg;
@@ -265,120 +259,159 @@ void tipc_link_build_bcast_sync_msg(struct tipc_link *l,
 * tipc_link_fsm_evt - link finite state machine
 * tipc_link_fsm_evt - link finite state machine
 * @l: pointer to link
 * @l: pointer to link
 * @evt: state machine event to be processed
 * @evt: state machine event to be processed
 * @xmitq: queue to prepend created protocol message, if any
 */
 */
static int tipc_link_fsm_evt(struct tipc_link *l, int evt,
int tipc_link_fsm_evt(struct tipc_link *l, int evt)
			     struct sk_buff_head *xmitq)
{
{
	int rc = 0;
	int rc = 0;
	struct tipc_link *pl;
	enum {
		LINK_RESET     = 1,
		LINK_ACTIVATE  = (1 << 1),
		SND_PROBE      = (1 << 2),
		SND_STATE      = (1 << 3),
		SND_RESET      = (1 << 4),
		SND_ACTIVATE   = (1 << 5),
		SND_BCAST_SYNC = (1 << 6)
	} actions = 0;

	if (l->exec_mode == TIPC_LINK_BLOCKED)
		return rc;


	switch (l->state) {
	switch (l->state) {
	case TIPC_LINK_WORKING:
	case LINK_RESETTING:
		switch (evt) {
		switch (evt) {
		case TRAFFIC_EVT:
		case LINK_PEER_RESET_EVT:
		case ACTIVATE_EVT:
			l->state = LINK_PEER_RESET;
			break;
		case LINK_RESET_EVT:
			l->state = LINK_RESET;
			break;
			break;
		case SILENCE_EVT:
		case LINK_FAILURE_EVT:
			l->state = TIPC_LINK_PROBING;
		case LINK_FAILOVER_BEGIN_EVT:
			actions |= SND_PROBE;
		case LINK_ESTABLISH_EVT:
		case LINK_FAILOVER_END_EVT:
		case LINK_SYNCH_BEGIN_EVT:
		case LINK_SYNCH_END_EVT:
		default:
			goto illegal_evt;
		}
		break;
		break;
		case PEER_RESET_EVT:
	case LINK_RESET:
			actions |= LINK_RESET | SND_ACTIVATE;
		switch (evt) {
		case LINK_PEER_RESET_EVT:
			l->state = LINK_ESTABLISHING;
			break;
			break;
		case LINK_FAILOVER_BEGIN_EVT:
			l->state = LINK_FAILINGOVER;
		case LINK_FAILURE_EVT:
		case LINK_RESET_EVT:
		case LINK_ESTABLISH_EVT:
		case LINK_FAILOVER_END_EVT:
			break;
		case LINK_SYNCH_BEGIN_EVT:
		case LINK_SYNCH_END_EVT:
		default:
		default:
			pr_debug("%s%u WORKING\n", link_unk_evt, evt);
			goto illegal_evt;
		}
		}
		break;
		break;
	case TIPC_LINK_PROBING:
	case LINK_PEER_RESET:
		switch (evt) {
		switch (evt) {
		case TRAFFIC_EVT:
		case LINK_RESET_EVT:
		case ACTIVATE_EVT:
			l->state = LINK_ESTABLISHING;
			l->state = TIPC_LINK_WORKING;
			break;
		case LINK_PEER_RESET_EVT:
		case LINK_ESTABLISH_EVT:
		case LINK_FAILURE_EVT:
			break;
			break;
		case PEER_RESET_EVT:
		case LINK_SYNCH_BEGIN_EVT:
			actions |= LINK_RESET | SND_ACTIVATE;
		case LINK_SYNCH_END_EVT:
		case LINK_FAILOVER_BEGIN_EVT:
		case LINK_FAILOVER_END_EVT:
		default:
			goto illegal_evt;
		}
		break;
		break;
		case SILENCE_EVT:
	case LINK_FAILINGOVER:
			if (l->silent_intv_cnt <= l->abort_limit) {
		switch (evt) {
				actions |= SND_PROBE;
		case LINK_FAILOVER_END_EVT:
			l->state = LINK_RESET;
			break;
			break;
		case LINK_PEER_RESET_EVT:
		case LINK_RESET_EVT:
		case LINK_ESTABLISH_EVT:
		case LINK_FAILURE_EVT:
			break;
		case LINK_FAILOVER_BEGIN_EVT:
		case LINK_SYNCH_BEGIN_EVT:
		case LINK_SYNCH_END_EVT:
		default:
			goto illegal_evt;
		}
		}
			actions |= LINK_RESET | SND_RESET;
		break;
		break;
	case LINK_ESTABLISHING:
		switch (evt) {
		case LINK_ESTABLISH_EVT:
			l->state = LINK_ESTABLISHED;
			rc |= TIPC_LINK_UP_EVT;
			break;
		case LINK_FAILOVER_BEGIN_EVT:
			l->state = LINK_FAILINGOVER;
			break;
		case LINK_PEER_RESET_EVT:
		case LINK_RESET_EVT:
		case LINK_FAILURE_EVT:
		case LINK_SYNCH_BEGIN_EVT:
		case LINK_FAILOVER_END_EVT:
			break;
		case LINK_SYNCH_END_EVT:
		default:
		default:
			pr_err("%s%u PROBING\n", link_unk_evt, evt);
			goto illegal_evt;
		}
		}
		break;
		break;
	case TIPC_LINK_RESETTING:
	case LINK_ESTABLISHED:
		switch (evt) {
		switch (evt) {
		case TRAFFIC_EVT:
		case LINK_PEER_RESET_EVT:
			l->state = LINK_PEER_RESET;
			rc |= TIPC_LINK_DOWN_EVT;
			break;
			break;
		case ACTIVATE_EVT:
		case LINK_FAILURE_EVT:
			pl = node_active_link(l->owner, 0);
			l->state = LINK_RESETTING;
			if (pl && link_probing(pl))
			rc |= TIPC_LINK_DOWN_EVT;
			break;
			break;
			l->state = TIPC_LINK_WORKING;
		case LINK_RESET_EVT:
			actions |= LINK_ACTIVATE;
			l->state = LINK_RESET;
			if (!l->owner->working_links)
				actions |= SND_BCAST_SYNC;
			break;
			break;
		case PEER_RESET_EVT:
		case LINK_ESTABLISH_EVT:
			l->state = TIPC_LINK_ESTABLISHING;
			actions |= SND_ACTIVATE;
			break;
			break;
		case SILENCE_EVT:
		case LINK_SYNCH_BEGIN_EVT:
			actions |= SND_RESET;
			l->state = LINK_SYNCHING;
			break;
			break;
		case LINK_SYNCH_END_EVT:
		case LINK_FAILOVER_BEGIN_EVT:
		case LINK_FAILOVER_END_EVT:
		default:
		default:
			pr_err("%s%u in RESETTING\n", link_unk_evt, evt);
			goto illegal_evt;
		}
		}
		break;
		break;
	case TIPC_LINK_ESTABLISHING:
	case LINK_SYNCHING:
		switch (evt) {
		switch (evt) {
		case TRAFFIC_EVT:
		case LINK_PEER_RESET_EVT:
		case ACTIVATE_EVT:
			l->state = LINK_PEER_RESET;
			pl = node_active_link(l->owner, 0);
			rc |= TIPC_LINK_DOWN_EVT;
			if (pl && link_probing(pl))
			break;
		case LINK_FAILURE_EVT:
			l->state = LINK_RESETTING;
			rc |= TIPC_LINK_DOWN_EVT;
			break;
			break;
			l->state = TIPC_LINK_WORKING;
		case LINK_RESET_EVT:
			actions |= LINK_ACTIVATE;
			l->state = LINK_RESET;
			if (!l->owner->working_links)
				actions |= SND_BCAST_SYNC;
			break;
			break;
		case PEER_RESET_EVT:
		case LINK_ESTABLISH_EVT:
		case LINK_SYNCH_BEGIN_EVT:
			break;
			break;
		case SILENCE_EVT:
		case LINK_SYNCH_END_EVT:
			actions |= SND_ACTIVATE;
			l->state = LINK_ESTABLISHED;
			break;
			break;
		case LINK_FAILOVER_BEGIN_EVT:
		case LINK_FAILOVER_END_EVT:
		default:
		default:
			pr_err("%s%u ESTABLISHING\n", link_unk_evt, evt);
			goto illegal_evt;
		}
		}
		break;
		break;
	default:
	default:
		pr_err("Unknown link state %u/%u\n", l->state, evt);
		pr_err("Unknown FSM state %x in %s\n", l->state, l->name);
	}

	/* Perform actions as decided by FSM */
	if (actions & LINK_RESET) {
		l->exec_mode = TIPC_LINK_BLOCKED;
		rc = TIPC_LINK_DOWN_EVT;
	}
	}
	if (actions & LINK_ACTIVATE)
	return rc;
		rc = TIPC_LINK_UP_EVT;
illegal_evt:

	pr_err("Illegal FSM event %x in state %x on link %s\n",
	       evt, l->state, l->name);
	return rc;
	return rc;
}
}


@@ -432,12 +465,11 @@ int tipc_link_timeout(struct tipc_link *l, struct sk_buff_head *xmitq)
	bool xmit = false;
	bool xmit = false;
	bool prb = false;
	bool prb = false;


	if (l->exec_mode == TIPC_LINK_BLOCKED)
		return rc;

	link_profile_stats(l);
	link_profile_stats(l);


	if (l->state == TIPC_LINK_WORKING) {
	switch (l->state) {
	case LINK_ESTABLISHED:
	case LINK_SYNCHING:
		if (!l->silent_intv_cnt) {
		if (!l->silent_intv_cnt) {
			if (tipc_bclink_acks_missing(l->owner))
			if (tipc_bclink_acks_missing(l->owner))
				xmit = true;
				xmit = true;
@@ -445,17 +477,26 @@ int tipc_link_timeout(struct tipc_link *l, struct sk_buff_head *xmitq)
			xmit = true;
			xmit = true;
			prb = true;
			prb = true;
		} else {
		} else {
			l->exec_mode = TIPC_LINK_BLOCKED;
			rc |= tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
			rc |= TIPC_LINK_DOWN_EVT;
		}
		}
		l->silent_intv_cnt++;
		l->silent_intv_cnt++;
	} else if (l->state == TIPC_LINK_RESETTING) {
		break;
	case LINK_RESET:
		xmit = true;
		xmit = true;
		mtyp = RESET_MSG;
		mtyp = RESET_MSG;
	} else if (l->state == TIPC_LINK_ESTABLISHING) {
		break;
	case LINK_ESTABLISHING:
		xmit = true;
		xmit = true;
		mtyp = ACTIVATE_MSG;
		mtyp = ACTIVATE_MSG;
		break;
	case LINK_RESETTING:
	case LINK_PEER_RESET:
	case LINK_FAILINGOVER:
		break;
	default:
		break;
	}
	}

	if (xmit)
	if (xmit)
		tipc_link_build_proto_msg(l, mtyp, prb, 0, 0, 0, xmitq);
		tipc_link_build_proto_msg(l, mtyp, prb, 0, 0, 0, xmitq);


@@ -559,7 +600,7 @@ void tipc_link_reset(struct tipc_link *l)
{
{
	struct tipc_node *owner = l->owner;
	struct tipc_node *owner = l->owner;


	l->state = TIPC_LINK_RESETTING;
	tipc_link_fsm_evt(l, LINK_RESET_EVT);


	/* Link is down, accept any session */
	/* Link is down, accept any session */
	l->peer_session = WILDCARD_SESSION;
	l->peer_session = WILDCARD_SESSION;
@@ -902,8 +943,7 @@ static int tipc_link_retransm(struct tipc_link *l, int retransm,
		l->stale_count = 1;
		l->stale_count = 1;
	} else if (++l->stale_count > 100) {
	} else if (++l->stale_count > 100) {
		link_retransmit_failure(l, skb);
		link_retransmit_failure(l, skb);
		l->exec_mode = TIPC_LINK_BLOCKED;
		return tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
		return TIPC_LINK_DOWN_EVT;
	}
	}
	skb_queue_walk(&l->transmq, skb) {
	skb_queue_walk(&l->transmq, skb) {
		if (!retransm)
		if (!retransm)
@@ -1002,25 +1042,23 @@ static int tipc_link_input(struct tipc_link *l, struct sk_buff *skb)
		l->stats.recv_bundled += msg_msgcnt(hdr);
		l->stats.recv_bundled += msg_msgcnt(hdr);
		while (tipc_msg_extract(skb, &iskb, &pos))
		while (tipc_msg_extract(skb, &iskb, &pos))
			tipc_data_input(l, iskb);
			tipc_data_input(l, iskb);
		return rc;
		return 0;
	} else if (usr == MSG_FRAGMENTER) {
	} else if (usr == MSG_FRAGMENTER) {
		l->stats.recv_fragments++;
		l->stats.recv_fragments++;
		if (tipc_buf_append(reasm_skb, &skb)) {
		if (tipc_buf_append(reasm_skb, &skb)) {
			l->stats.recv_fragmented++;
			l->stats.recv_fragmented++;
			tipc_data_input(l, skb);
			tipc_data_input(l, skb);
		} else if (!*reasm_skb) {
		} else if (!*reasm_skb) {
			l->exec_mode = TIPC_LINK_BLOCKED;
			return tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
			l->state = TIPC_LINK_RESETTING;
			rc = TIPC_LINK_DOWN_EVT;
		}
		}
		return rc;
		return 0;
	} else if (usr == BCAST_PROTOCOL) {
	} else if (usr == BCAST_PROTOCOL) {
		tipc_link_sync_rcv(node, skb);
		tipc_link_sync_rcv(node, skb);
		return rc;
		return 0;
	}
	}
drop:
drop:
	kfree_skb(skb);
	kfree_skb(skb);
	return rc;
	return 0;
}
}


static bool tipc_link_release_pkts(struct tipc_link *l, u16 acked)
static bool tipc_link_release_pkts(struct tipc_link *l, u16 acked)
@@ -1068,9 +1106,9 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb,
			continue;
			continue;
		}
		}


		if (unlikely(!link_working(l))) {
		if (unlikely(!link_is_up(l))) {
			rc = tipc_link_fsm_evt(l, TRAFFIC_EVT, xmitq);
			rc = tipc_link_fsm_evt(l, LINK_ESTABLISH_EVT);
			if (!link_working(l)) {
			if (!link_is_up(l)) {
				kfree_skb(__skb_dequeue(arrvq));
				kfree_skb(__skb_dequeue(arrvq));
				return rc;
				return rc;
			}
			}
@@ -1192,7 +1230,7 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
	int node_up = l->owner->bclink.recv_permitted;
	int node_up = l->owner->bclink.recv_permitted;


	/* Don't send protocol message during reset or link failover */
	/* Don't send protocol message during reset or link failover */
	if (l->exec_mode == TIPC_LINK_BLOCKED)
	if (tipc_link_is_blocked(l))
		return;
		return;


	msg_set_type(hdr, mtyp);
	msg_set_type(hdr, mtyp);
@@ -1302,7 +1340,6 @@ void tipc_link_tnl_prepare(struct tipc_link *l, struct tipc_link *tnl,
		tnl->drop_point = l->rcv_nxt;
		tnl->drop_point = l->rcv_nxt;
		tnl->failover_reasm_skb = l->reasm_buf;
		tnl->failover_reasm_skb = l->reasm_buf;
		l->reasm_buf = NULL;
		l->reasm_buf = NULL;
		l->exec_mode = TIPC_LINK_BLOCKED;
	}
	}
}
}


@@ -1323,7 +1360,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
	char *if_name;
	char *if_name;
	int rc = 0;
	int rc = 0;


	if (l->exec_mode == TIPC_LINK_BLOCKED)
	if (tipc_link_is_blocked(l))
		goto exit;
		goto exit;


	if (link_own_addr(l) > msg_prevnode(hdr))
	if (link_own_addr(l) > msg_prevnode(hdr))
@@ -1337,6 +1374,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
		    (l->peer_session != WILDCARD_SESSION))
		    (l->peer_session != WILDCARD_SESSION))
			break;
			break;
		/* fall thru' */
		/* fall thru' */

	case ACTIVATE_MSG:
	case ACTIVATE_MSG:


		/* Complete own link name with peer's interface name */
		/* Complete own link name with peer's interface name */
@@ -1355,13 +1393,20 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
		if (in_range(peers_prio, l->priority + 1, TIPC_MAX_LINK_PRI))
		if (in_range(peers_prio, l->priority + 1, TIPC_MAX_LINK_PRI))
			l->priority = peers_prio;
			l->priority = peers_prio;


		if (msg_type(hdr) == RESET_MSG) {
			rc |= tipc_link_fsm_evt(l, LINK_PEER_RESET_EVT);
		} else if (!link_is_up(l)) {
			tipc_link_fsm_evt(l, LINK_PEER_RESET_EVT);
			rc |= tipc_link_fsm_evt(l, LINK_ESTABLISH_EVT);
		}
		l->peer_session = msg_session(hdr);
		l->peer_session = msg_session(hdr);
		l->peer_bearer_id = msg_bearer_id(hdr);
		l->peer_bearer_id = msg_bearer_id(hdr);
		rc = tipc_link_fsm_evt(l, msg_type(hdr), xmitq);
		if (l->mtu > msg_max_pkt(hdr))
		if (l->mtu > msg_max_pkt(hdr))
			l->mtu = msg_max_pkt(hdr);
			l->mtu = msg_max_pkt(hdr);
		break;
		break;

	case STATE_MSG:
	case STATE_MSG:

		/* Update own tolerance if peer indicates a non-zero value */
		/* Update own tolerance if peer indicates a non-zero value */
		if (in_range(peers_tol, TIPC_MIN_LINK_TOL, TIPC_MAX_LINK_TOL))
		if (in_range(peers_tol, TIPC_MIN_LINK_TOL, TIPC_MAX_LINK_TOL))
			l->tolerance = peers_tol;
			l->tolerance = peers_tol;
@@ -1370,11 +1415,11 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
		l->stats.recv_states++;
		l->stats.recv_states++;
		if (msg_probe(hdr))
		if (msg_probe(hdr))
			l->stats.recv_probes++;
			l->stats.recv_probes++;
		rc = tipc_link_fsm_evt(l, TRAFFIC_EVT, xmitq);
		rc = tipc_link_fsm_evt(l, LINK_ESTABLISH_EVT);
		if (!tipc_link_is_up(l))
		if (!link_is_up(l))
			break;
			break;


		/* Has peer sent packets we haven't received yet ? */
		/* Send NACK if peer has sent pkts we haven't received yet */
		if (more(peers_snd_nxt, l->rcv_nxt))
		if (more(peers_snd_nxt, l->rcv_nxt))
			rcvgap = peers_snd_nxt - l->rcv_nxt;
			rcvgap = peers_snd_nxt - l->rcv_nxt;
		if (rcvgap || (msg_probe(hdr)))
		if (rcvgap || (msg_probe(hdr)))
@@ -1387,6 +1432,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
			rc = tipc_link_retransm(l, nacked_gap, xmitq);
			rc = tipc_link_retransm(l, nacked_gap, xmitq);
			l->stats.recv_nacks++;
			l->stats.recv_nacks++;
		}
		}

		tipc_link_advance_backlog(l, xmitq);
		tipc_link_advance_backlog(l, xmitq);
		if (unlikely(!skb_queue_empty(&l->wakeupq)))
		if (unlikely(!skb_queue_empty(&l->wakeupq)))
			link_prepare_wakeup(l);
			link_prepare_wakeup(l);
@@ -1463,19 +1509,7 @@ static void link_print(struct tipc_link *l, const char *str)
	u16 head = hskb ? msg_seqno(buf_msg(hskb)) : l->snd_nxt;
	u16 head = hskb ? msg_seqno(buf_msg(hskb)) : l->snd_nxt;
	u16 tail = l->snd_nxt - 1;
	u16 tail = l->snd_nxt - 1;


	pr_info("%s Link <%s>:", str, l->name);
	pr_info("%s Link <%s> state %x\n", str, l->name, l->state);

	if (link_probing(l))
		pr_cont(":P\n");
	else if (link_establishing(l))
		pr_cont(":E\n");
	else if (link_resetting(l))
		pr_cont(":R\n");
	else if (link_working(l))
		pr_cont(":W\n");
	else
		pr_cont("\n");

	pr_info("XMTQ: %u [%u-%u], BKLGQ: %u, SNDNX: %u, RCVNX: %u\n",
	pr_info("XMTQ: %u [%u-%u], BKLGQ: %u, SNDNX: %u, RCVNX: %u\n",
		skb_queue_len(&l->transmq), head, tail,
		skb_queue_len(&l->transmq), head, tail,
		skb_queue_len(&l->backlogq), l->snd_nxt, l->rcv_nxt);
		skb_queue_len(&l->backlogq), l->snd_nxt, l->rcv_nxt);
+16 −9
Original line number Original line Diff line number Diff line
@@ -49,13 +49,17 @@
 */
 */
#define INVALID_LINK_SEQ 0x10000
#define INVALID_LINK_SEQ 0x10000



/* Link FSM events:
/* Link endpoint receive states
 */
 */
enum {
enum {
	TIPC_LINK_OPEN,
	LINK_ESTABLISH_EVT       = 0xec1ab1e,
	TIPC_LINK_BLOCKED,
	LINK_PEER_RESET_EVT      = 0x9eed0e,
	TIPC_LINK_TUNNEL
	LINK_FAILURE_EVT         = 0xfa110e,
	LINK_RESET_EVT           = 0x10ca1d0e,
	LINK_FAILOVER_BEGIN_EVT  = 0xfa110bee,
	LINK_FAILOVER_END_EVT    = 0xfa110ede,
	LINK_SYNCH_BEGIN_EVT     = 0xc1ccbee,
	LINK_SYNCH_END_EVT       = 0xc1ccede
};
};


/* Events returned from link at packet reception or at timeout
/* Events returned from link at packet reception or at timeout
@@ -120,7 +124,6 @@ struct tipc_stats {
 * @pmsg: convenience pointer to "proto_msg" field
 * @pmsg: convenience pointer to "proto_msg" field
 * @priority: current link priority
 * @priority: current link priority
 * @net_plane: current link network plane ('A' through 'H')
 * @net_plane: current link network plane ('A' through 'H')
 * @exec_mode: transmit/receive mode for link endpoint instance
 * @backlog_limit: backlog queue congestion thresholds (indexed by importance)
 * @backlog_limit: backlog queue congestion thresholds (indexed by importance)
 * @exp_msg_count: # of tunnelled messages expected during link changeover
 * @exp_msg_count: # of tunnelled messages expected during link changeover
 * @reset_rcv_checkpt: seq # of last acknowledged message at time of link reset
 * @reset_rcv_checkpt: seq # of last acknowledged message at time of link reset
@@ -155,7 +158,7 @@ struct tipc_link {
	u32 tolerance;
	u32 tolerance;
	unsigned long keepalive_intv;
	unsigned long keepalive_intv;
	u32 abort_limit;
	u32 abort_limit;
	int state;
	u32 state;
	u32 silent_intv_cnt;
	u32 silent_intv_cnt;
	struct {
	struct {
		unchar hdr[INT_H_SIZE];
		unchar hdr[INT_H_SIZE];
@@ -166,7 +169,6 @@ struct tipc_link {
	char net_plane;
	char net_plane;


	/* Failover/synch */
	/* Failover/synch */
	u8 exec_mode;
	u16 drop_point;
	u16 drop_point;
	struct sk_buff *failover_reasm_skb;
	struct sk_buff *failover_reasm_skb;


@@ -214,8 +216,13 @@ void tipc_link_tnl_prepare(struct tipc_link *l, struct tipc_link *tnl,
			   int mtyp, struct sk_buff_head *xmitq);
			   int mtyp, struct sk_buff_head *xmitq);
void tipc_link_build_bcast_sync_msg(struct tipc_link *l,
void tipc_link_build_bcast_sync_msg(struct tipc_link *l,
				    struct sk_buff_head *xmitq);
				    struct sk_buff_head *xmitq);
int tipc_link_fsm_evt(struct tipc_link *l, int evt);
void tipc_link_reset_fragments(struct tipc_link *l_ptr);
void tipc_link_reset_fragments(struct tipc_link *l_ptr);
int tipc_link_is_up(struct tipc_link *l_ptr);
bool tipc_link_is_up(struct tipc_link *l);
bool tipc_link_is_reset(struct tipc_link *l);
bool tipc_link_is_synching(struct tipc_link *l);
bool tipc_link_is_failingover(struct tipc_link *l);
bool tipc_link_is_blocked(struct tipc_link *l);
int tipc_link_is_active(struct tipc_link *l_ptr);
int tipc_link_is_active(struct tipc_link *l_ptr);
void tipc_link_purge_queues(struct tipc_link *l_ptr);
void tipc_link_purge_queues(struct tipc_link *l_ptr);
void tipc_link_purge_backlog(struct tipc_link *l);
void tipc_link_purge_backlog(struct tipc_link *l);
+18 −13
Original line number Original line Diff line number Diff line
@@ -334,7 +334,6 @@ static void tipc_node_link_up(struct tipc_node *n, int bearer_id,
	if (!ol) {
	if (!ol) {
		*slot0 = bearer_id;
		*slot0 = bearer_id;
		*slot1 = bearer_id;
		*slot1 = bearer_id;
		nl->exec_mode = TIPC_LINK_OPEN;
		tipc_link_build_bcast_sync_msg(nl, xmitq);
		tipc_link_build_bcast_sync_msg(nl, xmitq);
		node_established_contact(n);
		node_established_contact(n);
		return;
		return;
@@ -368,7 +367,7 @@ static void tipc_node_link_down(struct tipc_node *n, int bearer_id)
	struct sk_buff_head xmitq;
	struct sk_buff_head xmitq;


	l = n->links[bearer_id].link;
	l = n->links[bearer_id].link;
	if (!l || !tipc_link_is_up(l))
	if (!l || tipc_link_is_reset(l))
		return;
		return;


	__skb_queue_head_init(&xmitq);
	__skb_queue_head_init(&xmitq);
@@ -414,6 +413,7 @@ static void tipc_node_link_down(struct tipc_node *n, int bearer_id)
	n->sync_point = tnl->rcv_nxt + (U16_MAX / 2 - 1);
	n->sync_point = tnl->rcv_nxt + (U16_MAX / 2 - 1);
	tipc_link_tnl_prepare(l, tnl, FAILOVER_MSG, &xmitq);
	tipc_link_tnl_prepare(l, tnl, FAILOVER_MSG, &xmitq);
	tipc_link_reset(l);
	tipc_link_reset(l);
	tipc_link_fsm_evt(l, LINK_FAILOVER_BEGIN_EVT);
	tipc_bearer_xmit(n->net, tnl->bearer_id, &xmitq, maddr);
	tipc_bearer_xmit(n->net, tnl->bearer_id, &xmitq, maddr);
}
}


@@ -749,7 +749,7 @@ static void node_lost_contact(struct tipc_node *n_ptr)
		struct tipc_link *l_ptr = n_ptr->links[i].link;
		struct tipc_link *l_ptr = n_ptr->links[i].link;
		if (!l_ptr)
		if (!l_ptr)
			continue;
			continue;
		l_ptr->exec_mode = TIPC_LINK_OPEN;
		tipc_link_fsm_evt(l_ptr, LINK_FAILOVER_END_EVT);
		kfree_skb(l_ptr->failover_reasm_skb);
		kfree_skb(l_ptr->failover_reasm_skb);
		l_ptr->failover_reasm_skb = NULL;
		l_ptr->failover_reasm_skb = NULL;
		tipc_link_reset_fragments(l_ptr);
		tipc_link_reset_fragments(l_ptr);
@@ -989,7 +989,7 @@ int tipc_node_xmit_skb(struct net *net, struct sk_buff *skb, u32 dnode,
 * Returns true if state is ok, otherwise consumes buffer and returns false
 * Returns true if state is ok, otherwise consumes buffer and returns false
 */
 */
static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,
static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,
				  int bearer_id)
				  int bearer_id, struct sk_buff_head *xmitq)
{
{
	struct tipc_msg *hdr = buf_msg(skb);
	struct tipc_msg *hdr = buf_msg(skb);
	int usr = msg_user(hdr);
	int usr = msg_user(hdr);
@@ -1042,42 +1042,47 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,
	/* Initiate or update failover mode if applicable */
	/* Initiate or update failover mode if applicable */
	if ((usr == TUNNEL_PROTOCOL) && (mtyp == FAILOVER_MSG)) {
	if ((usr == TUNNEL_PROTOCOL) && (mtyp == FAILOVER_MSG)) {
		syncpt = oseqno + exp_pkts - 1;
		syncpt = oseqno + exp_pkts - 1;
		if (pl && tipc_link_is_up(pl)) {
		if (pl && tipc_link_is_up(pl))
			tipc_node_link_down(n, pl->bearer_id);
			tipc_node_link_down(n, pl->bearer_id);
			pl->exec_mode = TIPC_LINK_BLOCKED;

		}
		/* If pkts arrive out of order, use lowest calculated syncpt */
		/* If pkts arrive out of order, use lowest calculated syncpt */
		if (less(syncpt, n->sync_point))
		if (less(syncpt, n->sync_point))
			n->sync_point = syncpt;
			n->sync_point = syncpt;
	}
	}


	/* Open parallel link when tunnel link reaches synch point */
	/* Open parallel link when tunnel link reaches synch point */
	if ((n->state == NODE_FAILINGOVER) && (more(rcv_nxt, n->sync_point))) {
	if ((n->state == NODE_FAILINGOVER) && !tipc_link_is_failingover(l)) {
		if (!more(rcv_nxt, n->sync_point))
			return true;
		tipc_node_fsm_evt(n, NODE_FAILOVER_END_EVT);
		tipc_node_fsm_evt(n, NODE_FAILOVER_END_EVT);
		if (pl)
		if (pl)
			pl->exec_mode = TIPC_LINK_OPEN;
			tipc_link_fsm_evt(pl, LINK_FAILOVER_END_EVT);
		return true;
		return true;
	}
	}


	/* Initiate or update synch mode if applicable */
	/* Initiate or update synch mode if applicable */
	if ((usr == TUNNEL_PROTOCOL) && (mtyp == SYNCH_MSG)) {
	if ((usr == TUNNEL_PROTOCOL) && (mtyp == SYNCH_MSG)) {
		syncpt = iseqno + exp_pkts - 1;
		syncpt = iseqno + exp_pkts - 1;
		if (!tipc_link_is_up(l)) {
			tipc_link_fsm_evt(l, LINK_ESTABLISH_EVT);
			tipc_node_link_up(n, bearer_id, xmitq);
		}
		if (n->state == SELF_UP_PEER_UP) {
		if (n->state == SELF_UP_PEER_UP) {
			n->sync_point = syncpt;
			n->sync_point = syncpt;
			tipc_link_fsm_evt(l, LINK_SYNCH_BEGIN_EVT);
			tipc_node_fsm_evt(n, NODE_SYNCH_BEGIN_EVT);
			tipc_node_fsm_evt(n, NODE_SYNCH_BEGIN_EVT);
		}
		}
		l->exec_mode = TIPC_LINK_TUNNEL;
		if (less(syncpt, n->sync_point))
		if (less(syncpt, n->sync_point))
			n->sync_point = syncpt;
			n->sync_point = syncpt;
	}
	}


	/* Open tunnel link when parallel link reaches synch point */
	/* Open tunnel link when parallel link reaches synch point */
	if ((n->state == NODE_SYNCHING) && (l->exec_mode == TIPC_LINK_TUNNEL)) {
	if ((n->state == NODE_SYNCHING) && tipc_link_is_synching(l)) {
		if (pl)
		if (pl)
			dlv_nxt = mod(pl->rcv_nxt - skb_queue_len(pl->inputq));
			dlv_nxt = mod(pl->rcv_nxt - skb_queue_len(pl->inputq));
		if (!pl || more(dlv_nxt, n->sync_point)) {
		if (!pl || more(dlv_nxt, n->sync_point)) {
			tipc_link_fsm_evt(l, LINK_SYNCH_END_EVT);
			tipc_node_fsm_evt(n, NODE_SYNCH_END_EVT);
			tipc_node_fsm_evt(n, NODE_SYNCH_END_EVT);
			l->exec_mode = TIPC_LINK_OPEN;
			return true;
			return true;
		}
		}
		if ((usr == TUNNEL_PROTOCOL) && (mtyp == SYNCH_MSG))
		if ((usr == TUNNEL_PROTOCOL) && (mtyp == SYNCH_MSG))
@@ -1143,7 +1148,7 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b)
		tipc_bclink_acknowledge(n, msg_bcast_ack(hdr));
		tipc_bclink_acknowledge(n, msg_bcast_ack(hdr));


	/* Check and if necessary update node state */
	/* Check and if necessary update node state */
	if (likely(tipc_node_check_state(n, skb, bearer_id))) {
	if (likely(tipc_node_check_state(n, skb, bearer_id, &xmitq))) {
		rc = tipc_link_rcv(le->link, skb, &xmitq);
		rc = tipc_link_rcv(le->link, skb, &xmitq);
		skb = NULL;
		skb = NULL;
	}
	}