162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * net/tipc/link.c: TIPC link code
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 1996-2007, 2012-2016, Ericsson AB
562306a36Sopenharmony_ci * Copyright (c) 2004-2007, 2010-2013, Wind River Systems
662306a36Sopenharmony_ci * All rights reserved.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
962306a36Sopenharmony_ci * modification, are permitted provided that the following conditions are met:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
1262306a36Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
1362306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
1462306a36Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
1562306a36Sopenharmony_ci *    documentation and/or other materials provided with the distribution.
1662306a36Sopenharmony_ci * 3. Neither the names of the copyright holders nor the names of its
1762306a36Sopenharmony_ci *    contributors may be used to endorse or promote products derived from
1862306a36Sopenharmony_ci *    this software without specific prior written permission.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the
2162306a36Sopenharmony_ci * GNU General Public License ("GPL") version 2 as published by the Free
2262306a36Sopenharmony_ci * Software Foundation.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2562306a36Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2662306a36Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2762306a36Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2862306a36Sopenharmony_ci * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2962306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3062306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3162306a36Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3262306a36Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3362306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3462306a36Sopenharmony_ci * POSSIBILITY OF SUCH DAMAGE.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include "core.h"
3862306a36Sopenharmony_ci#include "subscr.h"
3962306a36Sopenharmony_ci#include "link.h"
4062306a36Sopenharmony_ci#include "bcast.h"
4162306a36Sopenharmony_ci#include "socket.h"
4262306a36Sopenharmony_ci#include "name_distr.h"
4362306a36Sopenharmony_ci#include "discover.h"
4462306a36Sopenharmony_ci#include "netlink.h"
4562306a36Sopenharmony_ci#include "monitor.h"
4662306a36Sopenharmony_ci#include "trace.h"
4762306a36Sopenharmony_ci#include "crypto.h"
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#include <linux/pkt_sched.h>
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistruct tipc_stats {
5262306a36Sopenharmony_ci	u32 sent_pkts;
5362306a36Sopenharmony_ci	u32 recv_pkts;
5462306a36Sopenharmony_ci	u32 sent_states;
5562306a36Sopenharmony_ci	u32 recv_states;
5662306a36Sopenharmony_ci	u32 sent_probes;
5762306a36Sopenharmony_ci	u32 recv_probes;
5862306a36Sopenharmony_ci	u32 sent_nacks;
5962306a36Sopenharmony_ci	u32 recv_nacks;
6062306a36Sopenharmony_ci	u32 sent_acks;
6162306a36Sopenharmony_ci	u32 sent_bundled;
6262306a36Sopenharmony_ci	u32 sent_bundles;
6362306a36Sopenharmony_ci	u32 recv_bundled;
6462306a36Sopenharmony_ci	u32 recv_bundles;
6562306a36Sopenharmony_ci	u32 retransmitted;
6662306a36Sopenharmony_ci	u32 sent_fragmented;
6762306a36Sopenharmony_ci	u32 sent_fragments;
6862306a36Sopenharmony_ci	u32 recv_fragmented;
6962306a36Sopenharmony_ci	u32 recv_fragments;
7062306a36Sopenharmony_ci	u32 link_congs;		/* # port sends blocked by congestion */
7162306a36Sopenharmony_ci	u32 deferred_recv;
7262306a36Sopenharmony_ci	u32 duplicates;
7362306a36Sopenharmony_ci	u32 max_queue_sz;	/* send queue size high water mark */
7462306a36Sopenharmony_ci	u32 accu_queue_sz;	/* used for send queue size profiling */
7562306a36Sopenharmony_ci	u32 queue_sz_counts;	/* used for send queue size profiling */
7662306a36Sopenharmony_ci	u32 msg_length_counts;	/* used for message length profiling */
7762306a36Sopenharmony_ci	u32 msg_lengths_total;	/* used for message length profiling */
7862306a36Sopenharmony_ci	u32 msg_length_profile[7]; /* used for msg. length profiling */
7962306a36Sopenharmony_ci};
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/**
8262306a36Sopenharmony_ci * struct tipc_link - TIPC link data structure
8362306a36Sopenharmony_ci * @addr: network address of link's peer node
8462306a36Sopenharmony_ci * @name: link name character string
8562306a36Sopenharmony_ci * @media_addr: media address to use when sending messages over link
8662306a36Sopenharmony_ci * @timer: link timer
8762306a36Sopenharmony_ci * @net: pointer to namespace struct
8862306a36Sopenharmony_ci * @refcnt: reference counter for permanent references (owner node & timer)
8962306a36Sopenharmony_ci * @peer_session: link session # being used by peer end of link
9062306a36Sopenharmony_ci * @peer_bearer_id: bearer id used by link's peer endpoint
9162306a36Sopenharmony_ci * @bearer_id: local bearer id used by link
9262306a36Sopenharmony_ci * @tolerance: minimum link continuity loss needed to reset link [in ms]
9362306a36Sopenharmony_ci * @abort_limit: # of unacknowledged continuity probes needed to reset link
9462306a36Sopenharmony_ci * @state: current state of link FSM
9562306a36Sopenharmony_ci * @peer_caps: bitmap describing capabilities of peer node
9662306a36Sopenharmony_ci * @silent_intv_cnt: # of timer intervals without any reception from peer
9762306a36Sopenharmony_ci * @proto_msg: template for control messages generated by link
9862306a36Sopenharmony_ci * @pmsg: convenience pointer to "proto_msg" field
9962306a36Sopenharmony_ci * @priority: current link priority
10062306a36Sopenharmony_ci * @net_plane: current link network plane ('A' through 'H')
10162306a36Sopenharmony_ci * @mon_state: cookie with information needed by link monitor
10262306a36Sopenharmony_ci * @backlog_limit: backlog queue congestion thresholds (indexed by importance)
10362306a36Sopenharmony_ci * @exp_msg_count: # of tunnelled messages expected during link changeover
10462306a36Sopenharmony_ci * @reset_rcv_checkpt: seq # of last acknowledged message at time of link reset
10562306a36Sopenharmony_ci * @mtu: current maximum packet size for this link
10662306a36Sopenharmony_ci * @advertised_mtu: advertised own mtu when link is being established
10762306a36Sopenharmony_ci * @transmitq: queue for sent, non-acked messages
10862306a36Sopenharmony_ci * @backlogq: queue for messages waiting to be sent
10962306a36Sopenharmony_ci * @snt_nxt: next sequence number to use for outbound messages
11062306a36Sopenharmony_ci * @ackers: # of peers that needs to ack each packet before it can be released
11162306a36Sopenharmony_ci * @acked: # last packet acked by a certain peer. Used for broadcast.
11262306a36Sopenharmony_ci * @rcv_nxt: next sequence number to expect for inbound messages
11362306a36Sopenharmony_ci * @deferred_queue: deferred queue saved OOS b'cast message received from node
11462306a36Sopenharmony_ci * @unacked_window: # of inbound messages rx'd without ack'ing back to peer
11562306a36Sopenharmony_ci * @inputq: buffer queue for messages to be delivered upwards
11662306a36Sopenharmony_ci * @namedq: buffer queue for name table messages to be delivered upwards
11762306a36Sopenharmony_ci * @next_out: ptr to first unsent outbound message in queue
11862306a36Sopenharmony_ci * @wakeupq: linked list of wakeup msgs waiting for link congestion to abate
11962306a36Sopenharmony_ci * @long_msg_seq_no: next identifier to use for outbound fragmented messages
12062306a36Sopenharmony_ci * @reasm_buf: head of partially reassembled inbound message fragments
12162306a36Sopenharmony_ci * @bc_rcvr: marks that this is a broadcast receiver link
12262306a36Sopenharmony_ci * @stats: collects statistics regarding link activity
12362306a36Sopenharmony_ci * @session: session to be used by link
12462306a36Sopenharmony_ci * @snd_nxt_state: next send seq number
12562306a36Sopenharmony_ci * @rcv_nxt_state: next rcv seq number
12662306a36Sopenharmony_ci * @in_session: have received ACTIVATE_MSG from peer
12762306a36Sopenharmony_ci * @active: link is active
12862306a36Sopenharmony_ci * @if_name: associated interface name
12962306a36Sopenharmony_ci * @rst_cnt: link reset counter
13062306a36Sopenharmony_ci * @drop_point: seq number for failover handling (FIXME)
13162306a36Sopenharmony_ci * @failover_reasm_skb: saved failover msg ptr (FIXME)
13262306a36Sopenharmony_ci * @failover_deferdq: deferred message queue for failover processing (FIXME)
13362306a36Sopenharmony_ci * @transmq: the link's transmit queue
13462306a36Sopenharmony_ci * @backlog: link's backlog by priority (importance)
13562306a36Sopenharmony_ci * @snd_nxt: next sequence number to be used
13662306a36Sopenharmony_ci * @rcv_unacked: # messages read by user, but not yet acked back to peer
13762306a36Sopenharmony_ci * @deferdq: deferred receive queue
13862306a36Sopenharmony_ci * @window: sliding window size for congestion handling
13962306a36Sopenharmony_ci * @min_win: minimal send window to be used by link
14062306a36Sopenharmony_ci * @ssthresh: slow start threshold for congestion handling
14162306a36Sopenharmony_ci * @max_win: maximal send window to be used by link
14262306a36Sopenharmony_ci * @cong_acks: congestion acks for congestion avoidance (FIXME)
14362306a36Sopenharmony_ci * @checkpoint: seq number for congestion window size handling
14462306a36Sopenharmony_ci * @reasm_tnlmsg: fragmentation/reassembly area for tunnel protocol message
14562306a36Sopenharmony_ci * @last_gap: last gap ack blocks for bcast (FIXME)
14662306a36Sopenharmony_ci * @last_ga: ptr to gap ack blocks
14762306a36Sopenharmony_ci * @bc_rcvlink: the peer specific link used for broadcast reception
14862306a36Sopenharmony_ci * @bc_sndlink: the namespace global link used for broadcast sending
14962306a36Sopenharmony_ci * @nack_state: bcast nack state
15062306a36Sopenharmony_ci * @bc_peer_is_up: peer has acked the bcast init msg
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_cistruct tipc_link {
15362306a36Sopenharmony_ci	u32 addr;
15462306a36Sopenharmony_ci	char name[TIPC_MAX_LINK_NAME];
15562306a36Sopenharmony_ci	struct net *net;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/* Management and link supervision data */
15862306a36Sopenharmony_ci	u16 peer_session;
15962306a36Sopenharmony_ci	u16 session;
16062306a36Sopenharmony_ci	u16 snd_nxt_state;
16162306a36Sopenharmony_ci	u16 rcv_nxt_state;
16262306a36Sopenharmony_ci	u32 peer_bearer_id;
16362306a36Sopenharmony_ci	u32 bearer_id;
16462306a36Sopenharmony_ci	u32 tolerance;
16562306a36Sopenharmony_ci	u32 abort_limit;
16662306a36Sopenharmony_ci	u32 state;
16762306a36Sopenharmony_ci	u16 peer_caps;
16862306a36Sopenharmony_ci	bool in_session;
16962306a36Sopenharmony_ci	bool active;
17062306a36Sopenharmony_ci	u32 silent_intv_cnt;
17162306a36Sopenharmony_ci	char if_name[TIPC_MAX_IF_NAME];
17262306a36Sopenharmony_ci	u32 priority;
17362306a36Sopenharmony_ci	char net_plane;
17462306a36Sopenharmony_ci	struct tipc_mon_state mon_state;
17562306a36Sopenharmony_ci	u16 rst_cnt;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/* Failover/synch */
17862306a36Sopenharmony_ci	u16 drop_point;
17962306a36Sopenharmony_ci	struct sk_buff *failover_reasm_skb;
18062306a36Sopenharmony_ci	struct sk_buff_head failover_deferdq;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* Max packet negotiation */
18362306a36Sopenharmony_ci	u16 mtu;
18462306a36Sopenharmony_ci	u16 advertised_mtu;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/* Sending */
18762306a36Sopenharmony_ci	struct sk_buff_head transmq;
18862306a36Sopenharmony_ci	struct sk_buff_head backlogq;
18962306a36Sopenharmony_ci	struct {
19062306a36Sopenharmony_ci		u16 len;
19162306a36Sopenharmony_ci		u16 limit;
19262306a36Sopenharmony_ci		struct sk_buff *target_bskb;
19362306a36Sopenharmony_ci	} backlog[5];
19462306a36Sopenharmony_ci	u16 snd_nxt;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* Reception */
19762306a36Sopenharmony_ci	u16 rcv_nxt;
19862306a36Sopenharmony_ci	u32 rcv_unacked;
19962306a36Sopenharmony_ci	struct sk_buff_head deferdq;
20062306a36Sopenharmony_ci	struct sk_buff_head *inputq;
20162306a36Sopenharmony_ci	struct sk_buff_head *namedq;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* Congestion handling */
20462306a36Sopenharmony_ci	struct sk_buff_head wakeupq;
20562306a36Sopenharmony_ci	u16 window;
20662306a36Sopenharmony_ci	u16 min_win;
20762306a36Sopenharmony_ci	u16 ssthresh;
20862306a36Sopenharmony_ci	u16 max_win;
20962306a36Sopenharmony_ci	u16 cong_acks;
21062306a36Sopenharmony_ci	u16 checkpoint;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	/* Fragmentation/reassembly */
21362306a36Sopenharmony_ci	struct sk_buff *reasm_buf;
21462306a36Sopenharmony_ci	struct sk_buff *reasm_tnlmsg;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/* Broadcast */
21762306a36Sopenharmony_ci	u16 ackers;
21862306a36Sopenharmony_ci	u16 acked;
21962306a36Sopenharmony_ci	u16 last_gap;
22062306a36Sopenharmony_ci	struct tipc_gap_ack_blks *last_ga;
22162306a36Sopenharmony_ci	struct tipc_link *bc_rcvlink;
22262306a36Sopenharmony_ci	struct tipc_link *bc_sndlink;
22362306a36Sopenharmony_ci	u8 nack_state;
22462306a36Sopenharmony_ci	bool bc_peer_is_up;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* Statistics */
22762306a36Sopenharmony_ci	struct tipc_stats stats;
22862306a36Sopenharmony_ci};
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/*
23162306a36Sopenharmony_ci * Error message prefixes
23262306a36Sopenharmony_ci */
23362306a36Sopenharmony_cistatic const char *link_co_err = "Link tunneling error, ";
23462306a36Sopenharmony_cistatic const char *link_rst_msg = "Resetting link ";
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci/* Send states for broadcast NACKs
23762306a36Sopenharmony_ci */
23862306a36Sopenharmony_cienum {
23962306a36Sopenharmony_ci	BC_NACK_SND_CONDITIONAL,
24062306a36Sopenharmony_ci	BC_NACK_SND_UNCONDITIONAL,
24162306a36Sopenharmony_ci	BC_NACK_SND_SUPPRESS,
24262306a36Sopenharmony_ci};
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci#define TIPC_BC_RETR_LIM  (jiffies + msecs_to_jiffies(10))
24562306a36Sopenharmony_ci#define TIPC_UC_RETR_TIME (jiffies + msecs_to_jiffies(1))
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci/* Link FSM states:
24862306a36Sopenharmony_ci */
24962306a36Sopenharmony_cienum {
25062306a36Sopenharmony_ci	LINK_ESTABLISHED     = 0xe,
25162306a36Sopenharmony_ci	LINK_ESTABLISHING    = 0xe  << 4,
25262306a36Sopenharmony_ci	LINK_RESET           = 0x1  << 8,
25362306a36Sopenharmony_ci	LINK_RESETTING       = 0x2  << 12,
25462306a36Sopenharmony_ci	LINK_PEER_RESET      = 0xd  << 16,
25562306a36Sopenharmony_ci	LINK_FAILINGOVER     = 0xf  << 20,
25662306a36Sopenharmony_ci	LINK_SYNCHING        = 0xc  << 24
25762306a36Sopenharmony_ci};
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci/* Link FSM state checking routines
26062306a36Sopenharmony_ci */
26162306a36Sopenharmony_cistatic int link_is_up(struct tipc_link *l)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	return l->state & (LINK_ESTABLISHED | LINK_SYNCHING);
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
26762306a36Sopenharmony_ci			       struct sk_buff_head *xmitq);
26862306a36Sopenharmony_cistatic void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
26962306a36Sopenharmony_ci				      bool probe_reply, u16 rcvgap,
27062306a36Sopenharmony_ci				      int tolerance, int priority,
27162306a36Sopenharmony_ci				      struct sk_buff_head *xmitq);
27262306a36Sopenharmony_cistatic void link_print(struct tipc_link *l, const char *str);
27362306a36Sopenharmony_cistatic int tipc_link_build_nack_msg(struct tipc_link *l,
27462306a36Sopenharmony_ci				    struct sk_buff_head *xmitq);
27562306a36Sopenharmony_cistatic void tipc_link_build_bc_init_msg(struct tipc_link *l,
27662306a36Sopenharmony_ci					struct sk_buff_head *xmitq);
27762306a36Sopenharmony_cistatic u8 __tipc_build_gap_ack_blks(struct tipc_gap_ack_blks *ga,
27862306a36Sopenharmony_ci				    struct tipc_link *l, u8 start_index);
27962306a36Sopenharmony_cistatic u16 tipc_build_gap_ack_blks(struct tipc_link *l, struct tipc_msg *hdr);
28062306a36Sopenharmony_cistatic int tipc_link_advance_transmq(struct tipc_link *l, struct tipc_link *r,
28162306a36Sopenharmony_ci				     u16 acked, u16 gap,
28262306a36Sopenharmony_ci				     struct tipc_gap_ack_blks *ga,
28362306a36Sopenharmony_ci				     struct sk_buff_head *xmitq,
28462306a36Sopenharmony_ci				     bool *retransmitted, int *rc);
28562306a36Sopenharmony_cistatic void tipc_link_update_cwin(struct tipc_link *l, int released,
28662306a36Sopenharmony_ci				  bool retransmitted);
28762306a36Sopenharmony_ci/*
28862306a36Sopenharmony_ci *  Simple non-static link routines (i.e. referenced outside this file)
28962306a36Sopenharmony_ci */
29062306a36Sopenharmony_cibool tipc_link_is_up(struct tipc_link *l)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	return link_is_up(l);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cibool tipc_link_peer_is_down(struct tipc_link *l)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	return l->state == LINK_PEER_RESET;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cibool tipc_link_is_reset(struct tipc_link *l)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	return l->state & (LINK_RESET | LINK_FAILINGOVER | LINK_ESTABLISHING);
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cibool tipc_link_is_establishing(struct tipc_link *l)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	return l->state == LINK_ESTABLISHING;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cibool tipc_link_is_synching(struct tipc_link *l)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	return l->state == LINK_SYNCHING;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cibool tipc_link_is_failingover(struct tipc_link *l)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	return l->state == LINK_FAILINGOVER;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cibool tipc_link_is_blocked(struct tipc_link *l)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	return l->state & (LINK_RESETTING | LINK_PEER_RESET | LINK_FAILINGOVER);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic bool link_is_bc_sndlink(struct tipc_link *l)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	return !l->bc_sndlink;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic bool link_is_bc_rcvlink(struct tipc_link *l)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	return ((l->bc_rcvlink == l) && !link_is_bc_sndlink(l));
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_civoid tipc_link_set_active(struct tipc_link *l, bool active)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	l->active = active;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ciu32 tipc_link_id(struct tipc_link *l)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	return l->peer_bearer_id << 16 | l->bearer_id;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ciint tipc_link_min_win(struct tipc_link *l)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	return l->min_win;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ciint tipc_link_max_win(struct tipc_link *l)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	return l->max_win;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ciint tipc_link_prio(struct tipc_link *l)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	return l->priority;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ciunsigned long tipc_link_tolerance(struct tipc_link *l)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	return l->tolerance;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistruct sk_buff_head *tipc_link_inputq(struct tipc_link *l)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	return l->inputq;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cichar tipc_link_plane(struct tipc_link *l)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	return l->net_plane;
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistruct net *tipc_link_net(struct tipc_link *l)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	return l->net;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_civoid tipc_link_update_caps(struct tipc_link *l, u16 capabilities)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	l->peer_caps = capabilities;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_civoid tipc_link_add_bc_peer(struct tipc_link *snd_l,
38662306a36Sopenharmony_ci			   struct tipc_link *uc_l,
38762306a36Sopenharmony_ci			   struct sk_buff_head *xmitq)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	struct tipc_link *rcv_l = uc_l->bc_rcvlink;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	snd_l->ackers++;
39262306a36Sopenharmony_ci	rcv_l->acked = snd_l->snd_nxt - 1;
39362306a36Sopenharmony_ci	snd_l->state = LINK_ESTABLISHED;
39462306a36Sopenharmony_ci	tipc_link_build_bc_init_msg(uc_l, xmitq);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_civoid tipc_link_remove_bc_peer(struct tipc_link *snd_l,
39862306a36Sopenharmony_ci			      struct tipc_link *rcv_l,
39962306a36Sopenharmony_ci			      struct sk_buff_head *xmitq)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	u16 ack = snd_l->snd_nxt - 1;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	snd_l->ackers--;
40462306a36Sopenharmony_ci	rcv_l->bc_peer_is_up = true;
40562306a36Sopenharmony_ci	rcv_l->state = LINK_ESTABLISHED;
40662306a36Sopenharmony_ci	tipc_link_bc_ack_rcv(rcv_l, ack, 0, NULL, xmitq, NULL);
40762306a36Sopenharmony_ci	trace_tipc_link_reset(rcv_l, TIPC_DUMP_ALL, "bclink removed!");
40862306a36Sopenharmony_ci	tipc_link_reset(rcv_l);
40962306a36Sopenharmony_ci	rcv_l->state = LINK_RESET;
41062306a36Sopenharmony_ci	if (!snd_l->ackers) {
41162306a36Sopenharmony_ci		trace_tipc_link_reset(snd_l, TIPC_DUMP_ALL, "zero ackers!");
41262306a36Sopenharmony_ci		tipc_link_reset(snd_l);
41362306a36Sopenharmony_ci		snd_l->state = LINK_RESET;
41462306a36Sopenharmony_ci		__skb_queue_purge(xmitq);
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ciint tipc_link_bc_peers(struct tipc_link *l)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	return l->ackers;
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic u16 link_bc_rcv_gap(struct tipc_link *l)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	struct sk_buff *skb = skb_peek(&l->deferdq);
42662306a36Sopenharmony_ci	u16 gap = 0;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (more(l->snd_nxt, l->rcv_nxt))
42962306a36Sopenharmony_ci		gap = l->snd_nxt - l->rcv_nxt;
43062306a36Sopenharmony_ci	if (skb)
43162306a36Sopenharmony_ci		gap = buf_seqno(skb) - l->rcv_nxt;
43262306a36Sopenharmony_ci	return gap;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_civoid tipc_link_set_mtu(struct tipc_link *l, int mtu)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	l->mtu = mtu;
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ciint tipc_link_mtu(struct tipc_link *l)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	return l->mtu;
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ciint tipc_link_mss(struct tipc_link *l)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci#ifdef CONFIG_TIPC_CRYPTO
44862306a36Sopenharmony_ci	return l->mtu - INT_H_SIZE - EMSG_OVERHEAD;
44962306a36Sopenharmony_ci#else
45062306a36Sopenharmony_ci	return l->mtu - INT_H_SIZE;
45162306a36Sopenharmony_ci#endif
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ciu16 tipc_link_rcv_nxt(struct tipc_link *l)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	return l->rcv_nxt;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ciu16 tipc_link_acked(struct tipc_link *l)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	return l->acked;
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cichar *tipc_link_name(struct tipc_link *l)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	return l->name;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ciu32 tipc_link_state(struct tipc_link *l)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	return l->state;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci/**
47562306a36Sopenharmony_ci * tipc_link_create - create a new link
47662306a36Sopenharmony_ci * @net: pointer to associated network namespace
47762306a36Sopenharmony_ci * @if_name: associated interface name
47862306a36Sopenharmony_ci * @bearer_id: id (index) of associated bearer
47962306a36Sopenharmony_ci * @tolerance: link tolerance to be used by link
48062306a36Sopenharmony_ci * @net_plane: network plane (A,B,c..) this link belongs to
48162306a36Sopenharmony_ci * @mtu: mtu to be advertised by link
48262306a36Sopenharmony_ci * @priority: priority to be used by link
48362306a36Sopenharmony_ci * @min_win: minimal send window to be used by link
48462306a36Sopenharmony_ci * @max_win: maximal send window to be used by link
48562306a36Sopenharmony_ci * @session: session to be used by link
48662306a36Sopenharmony_ci * @peer: node id of peer node
48762306a36Sopenharmony_ci * @peer_caps: bitmap describing peer node capabilities
48862306a36Sopenharmony_ci * @bc_sndlink: the namespace global link used for broadcast sending
48962306a36Sopenharmony_ci * @bc_rcvlink: the peer specific link used for broadcast reception
49062306a36Sopenharmony_ci * @inputq: queue to put messages ready for delivery
49162306a36Sopenharmony_ci * @namedq: queue to put binding table update messages ready for delivery
49262306a36Sopenharmony_ci * @link: return value, pointer to put the created link
49362306a36Sopenharmony_ci * @self: local unicast link id
49462306a36Sopenharmony_ci * @peer_id: 128-bit ID of peer
49562306a36Sopenharmony_ci *
49662306a36Sopenharmony_ci * Return: true if link was created, otherwise false
49762306a36Sopenharmony_ci */
49862306a36Sopenharmony_cibool tipc_link_create(struct net *net, char *if_name, int bearer_id,
49962306a36Sopenharmony_ci		      int tolerance, char net_plane, u32 mtu, int priority,
50062306a36Sopenharmony_ci		      u32 min_win, u32 max_win, u32 session, u32 self,
50162306a36Sopenharmony_ci		      u32 peer, u8 *peer_id, u16 peer_caps,
50262306a36Sopenharmony_ci		      struct tipc_link *bc_sndlink,
50362306a36Sopenharmony_ci		      struct tipc_link *bc_rcvlink,
50462306a36Sopenharmony_ci		      struct sk_buff_head *inputq,
50562306a36Sopenharmony_ci		      struct sk_buff_head *namedq,
50662306a36Sopenharmony_ci		      struct tipc_link **link)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	char peer_str[NODE_ID_STR_LEN] = {0,};
50962306a36Sopenharmony_ci	char self_str[NODE_ID_STR_LEN] = {0,};
51062306a36Sopenharmony_ci	struct tipc_link *l;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	l = kzalloc(sizeof(*l), GFP_ATOMIC);
51362306a36Sopenharmony_ci	if (!l)
51462306a36Sopenharmony_ci		return false;
51562306a36Sopenharmony_ci	*link = l;
51662306a36Sopenharmony_ci	l->session = session;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/* Set link name for unicast links only */
51962306a36Sopenharmony_ci	if (peer_id) {
52062306a36Sopenharmony_ci		tipc_nodeid2string(self_str, tipc_own_id(net));
52162306a36Sopenharmony_ci		if (strlen(self_str) > 16)
52262306a36Sopenharmony_ci			sprintf(self_str, "%x", self);
52362306a36Sopenharmony_ci		tipc_nodeid2string(peer_str, peer_id);
52462306a36Sopenharmony_ci		if (strlen(peer_str) > 16)
52562306a36Sopenharmony_ci			sprintf(peer_str, "%x", peer);
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci	/* Peer i/f name will be completed by reset/activate message */
52862306a36Sopenharmony_ci	snprintf(l->name, sizeof(l->name), "%s:%s-%s:unknown",
52962306a36Sopenharmony_ci		 self_str, if_name, peer_str);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	strcpy(l->if_name, if_name);
53262306a36Sopenharmony_ci	l->addr = peer;
53362306a36Sopenharmony_ci	l->peer_caps = peer_caps;
53462306a36Sopenharmony_ci	l->net = net;
53562306a36Sopenharmony_ci	l->in_session = false;
53662306a36Sopenharmony_ci	l->bearer_id = bearer_id;
53762306a36Sopenharmony_ci	l->tolerance = tolerance;
53862306a36Sopenharmony_ci	if (bc_rcvlink)
53962306a36Sopenharmony_ci		bc_rcvlink->tolerance = tolerance;
54062306a36Sopenharmony_ci	l->net_plane = net_plane;
54162306a36Sopenharmony_ci	l->advertised_mtu = mtu;
54262306a36Sopenharmony_ci	l->mtu = mtu;
54362306a36Sopenharmony_ci	l->priority = priority;
54462306a36Sopenharmony_ci	tipc_link_set_queue_limits(l, min_win, max_win);
54562306a36Sopenharmony_ci	l->ackers = 1;
54662306a36Sopenharmony_ci	l->bc_sndlink = bc_sndlink;
54762306a36Sopenharmony_ci	l->bc_rcvlink = bc_rcvlink;
54862306a36Sopenharmony_ci	l->inputq = inputq;
54962306a36Sopenharmony_ci	l->namedq = namedq;
55062306a36Sopenharmony_ci	l->state = LINK_RESETTING;
55162306a36Sopenharmony_ci	__skb_queue_head_init(&l->transmq);
55262306a36Sopenharmony_ci	__skb_queue_head_init(&l->backlogq);
55362306a36Sopenharmony_ci	__skb_queue_head_init(&l->deferdq);
55462306a36Sopenharmony_ci	__skb_queue_head_init(&l->failover_deferdq);
55562306a36Sopenharmony_ci	skb_queue_head_init(&l->wakeupq);
55662306a36Sopenharmony_ci	skb_queue_head_init(l->inputq);
55762306a36Sopenharmony_ci	return true;
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci/**
56162306a36Sopenharmony_ci * tipc_link_bc_create - create new link to be used for broadcast
56262306a36Sopenharmony_ci * @net: pointer to associated network namespace
56362306a36Sopenharmony_ci * @mtu: mtu to be used initially if no peers
56462306a36Sopenharmony_ci * @min_win: minimal send window to be used by link
56562306a36Sopenharmony_ci * @max_win: maximal send window to be used by link
56662306a36Sopenharmony_ci * @inputq: queue to put messages ready for delivery
56762306a36Sopenharmony_ci * @namedq: queue to put binding table update messages ready for delivery
56862306a36Sopenharmony_ci * @link: return value, pointer to put the created link
56962306a36Sopenharmony_ci * @ownnode: identity of own node
57062306a36Sopenharmony_ci * @peer: node id of peer node
57162306a36Sopenharmony_ci * @peer_id: 128-bit ID of peer
57262306a36Sopenharmony_ci * @peer_caps: bitmap describing peer node capabilities
57362306a36Sopenharmony_ci * @bc_sndlink: the namespace global link used for broadcast sending
57462306a36Sopenharmony_ci *
57562306a36Sopenharmony_ci * Return: true if link was created, otherwise false
57662306a36Sopenharmony_ci */
57762306a36Sopenharmony_cibool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer, u8 *peer_id,
57862306a36Sopenharmony_ci			 int mtu, u32 min_win, u32 max_win, u16 peer_caps,
57962306a36Sopenharmony_ci			 struct sk_buff_head *inputq,
58062306a36Sopenharmony_ci			 struct sk_buff_head *namedq,
58162306a36Sopenharmony_ci			 struct tipc_link *bc_sndlink,
58262306a36Sopenharmony_ci			 struct tipc_link **link)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	struct tipc_link *l;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	if (!tipc_link_create(net, "", MAX_BEARERS, 0, 'Z', mtu, 0, min_win,
58762306a36Sopenharmony_ci			      max_win, 0, ownnode, peer, NULL, peer_caps,
58862306a36Sopenharmony_ci			      bc_sndlink, NULL, inputq, namedq, link))
58962306a36Sopenharmony_ci		return false;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	l = *link;
59262306a36Sopenharmony_ci	if (peer_id) {
59362306a36Sopenharmony_ci		char peer_str[NODE_ID_STR_LEN] = {0,};
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci		tipc_nodeid2string(peer_str, peer_id);
59662306a36Sopenharmony_ci		if (strlen(peer_str) > 16)
59762306a36Sopenharmony_ci			sprintf(peer_str, "%x", peer);
59862306a36Sopenharmony_ci		/* Broadcast receiver link name: "broadcast-link:<peer>" */
59962306a36Sopenharmony_ci		snprintf(l->name, sizeof(l->name), "%s:%s", tipc_bclink_name,
60062306a36Sopenharmony_ci			 peer_str);
60162306a36Sopenharmony_ci	} else {
60262306a36Sopenharmony_ci		strcpy(l->name, tipc_bclink_name);
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci	trace_tipc_link_reset(l, TIPC_DUMP_ALL, "bclink created!");
60562306a36Sopenharmony_ci	tipc_link_reset(l);
60662306a36Sopenharmony_ci	l->state = LINK_RESET;
60762306a36Sopenharmony_ci	l->ackers = 0;
60862306a36Sopenharmony_ci	l->bc_rcvlink = l;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	/* Broadcast send link is always up */
61162306a36Sopenharmony_ci	if (link_is_bc_sndlink(l))
61262306a36Sopenharmony_ci		l->state = LINK_ESTABLISHED;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	/* Disable replicast if even a single peer doesn't support it */
61562306a36Sopenharmony_ci	if (link_is_bc_rcvlink(l) && !(peer_caps & TIPC_BCAST_RCAST))
61662306a36Sopenharmony_ci		tipc_bcast_toggle_rcast(net, false);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	return true;
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci/**
62262306a36Sopenharmony_ci * tipc_link_fsm_evt - link finite state machine
62362306a36Sopenharmony_ci * @l: pointer to link
62462306a36Sopenharmony_ci * @evt: state machine event to be processed
62562306a36Sopenharmony_ci */
62662306a36Sopenharmony_ciint tipc_link_fsm_evt(struct tipc_link *l, int evt)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	int rc = 0;
62962306a36Sopenharmony_ci	int old_state = l->state;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	switch (l->state) {
63262306a36Sopenharmony_ci	case LINK_RESETTING:
63362306a36Sopenharmony_ci		switch (evt) {
63462306a36Sopenharmony_ci		case LINK_PEER_RESET_EVT:
63562306a36Sopenharmony_ci			l->state = LINK_PEER_RESET;
63662306a36Sopenharmony_ci			break;
63762306a36Sopenharmony_ci		case LINK_RESET_EVT:
63862306a36Sopenharmony_ci			l->state = LINK_RESET;
63962306a36Sopenharmony_ci			break;
64062306a36Sopenharmony_ci		case LINK_FAILURE_EVT:
64162306a36Sopenharmony_ci		case LINK_FAILOVER_BEGIN_EVT:
64262306a36Sopenharmony_ci		case LINK_ESTABLISH_EVT:
64362306a36Sopenharmony_ci		case LINK_FAILOVER_END_EVT:
64462306a36Sopenharmony_ci		case LINK_SYNCH_BEGIN_EVT:
64562306a36Sopenharmony_ci		case LINK_SYNCH_END_EVT:
64662306a36Sopenharmony_ci		default:
64762306a36Sopenharmony_ci			goto illegal_evt;
64862306a36Sopenharmony_ci		}
64962306a36Sopenharmony_ci		break;
65062306a36Sopenharmony_ci	case LINK_RESET:
65162306a36Sopenharmony_ci		switch (evt) {
65262306a36Sopenharmony_ci		case LINK_PEER_RESET_EVT:
65362306a36Sopenharmony_ci			l->state = LINK_ESTABLISHING;
65462306a36Sopenharmony_ci			break;
65562306a36Sopenharmony_ci		case LINK_FAILOVER_BEGIN_EVT:
65662306a36Sopenharmony_ci			l->state = LINK_FAILINGOVER;
65762306a36Sopenharmony_ci			break;
65862306a36Sopenharmony_ci		case LINK_FAILURE_EVT:
65962306a36Sopenharmony_ci		case LINK_RESET_EVT:
66062306a36Sopenharmony_ci		case LINK_ESTABLISH_EVT:
66162306a36Sopenharmony_ci		case LINK_FAILOVER_END_EVT:
66262306a36Sopenharmony_ci			break;
66362306a36Sopenharmony_ci		case LINK_SYNCH_BEGIN_EVT:
66462306a36Sopenharmony_ci		case LINK_SYNCH_END_EVT:
66562306a36Sopenharmony_ci		default:
66662306a36Sopenharmony_ci			goto illegal_evt;
66762306a36Sopenharmony_ci		}
66862306a36Sopenharmony_ci		break;
66962306a36Sopenharmony_ci	case LINK_PEER_RESET:
67062306a36Sopenharmony_ci		switch (evt) {
67162306a36Sopenharmony_ci		case LINK_RESET_EVT:
67262306a36Sopenharmony_ci			l->state = LINK_ESTABLISHING;
67362306a36Sopenharmony_ci			break;
67462306a36Sopenharmony_ci		case LINK_PEER_RESET_EVT:
67562306a36Sopenharmony_ci		case LINK_ESTABLISH_EVT:
67662306a36Sopenharmony_ci		case LINK_FAILURE_EVT:
67762306a36Sopenharmony_ci			break;
67862306a36Sopenharmony_ci		case LINK_SYNCH_BEGIN_EVT:
67962306a36Sopenharmony_ci		case LINK_SYNCH_END_EVT:
68062306a36Sopenharmony_ci		case LINK_FAILOVER_BEGIN_EVT:
68162306a36Sopenharmony_ci		case LINK_FAILOVER_END_EVT:
68262306a36Sopenharmony_ci		default:
68362306a36Sopenharmony_ci			goto illegal_evt;
68462306a36Sopenharmony_ci		}
68562306a36Sopenharmony_ci		break;
68662306a36Sopenharmony_ci	case LINK_FAILINGOVER:
68762306a36Sopenharmony_ci		switch (evt) {
68862306a36Sopenharmony_ci		case LINK_FAILOVER_END_EVT:
68962306a36Sopenharmony_ci			l->state = LINK_RESET;
69062306a36Sopenharmony_ci			break;
69162306a36Sopenharmony_ci		case LINK_PEER_RESET_EVT:
69262306a36Sopenharmony_ci		case LINK_RESET_EVT:
69362306a36Sopenharmony_ci		case LINK_ESTABLISH_EVT:
69462306a36Sopenharmony_ci		case LINK_FAILURE_EVT:
69562306a36Sopenharmony_ci			break;
69662306a36Sopenharmony_ci		case LINK_FAILOVER_BEGIN_EVT:
69762306a36Sopenharmony_ci		case LINK_SYNCH_BEGIN_EVT:
69862306a36Sopenharmony_ci		case LINK_SYNCH_END_EVT:
69962306a36Sopenharmony_ci		default:
70062306a36Sopenharmony_ci			goto illegal_evt;
70162306a36Sopenharmony_ci		}
70262306a36Sopenharmony_ci		break;
70362306a36Sopenharmony_ci	case LINK_ESTABLISHING:
70462306a36Sopenharmony_ci		switch (evt) {
70562306a36Sopenharmony_ci		case LINK_ESTABLISH_EVT:
70662306a36Sopenharmony_ci			l->state = LINK_ESTABLISHED;
70762306a36Sopenharmony_ci			break;
70862306a36Sopenharmony_ci		case LINK_FAILOVER_BEGIN_EVT:
70962306a36Sopenharmony_ci			l->state = LINK_FAILINGOVER;
71062306a36Sopenharmony_ci			break;
71162306a36Sopenharmony_ci		case LINK_RESET_EVT:
71262306a36Sopenharmony_ci			l->state = LINK_RESET;
71362306a36Sopenharmony_ci			break;
71462306a36Sopenharmony_ci		case LINK_FAILURE_EVT:
71562306a36Sopenharmony_ci		case LINK_PEER_RESET_EVT:
71662306a36Sopenharmony_ci		case LINK_SYNCH_BEGIN_EVT:
71762306a36Sopenharmony_ci		case LINK_FAILOVER_END_EVT:
71862306a36Sopenharmony_ci			break;
71962306a36Sopenharmony_ci		case LINK_SYNCH_END_EVT:
72062306a36Sopenharmony_ci		default:
72162306a36Sopenharmony_ci			goto illegal_evt;
72262306a36Sopenharmony_ci		}
72362306a36Sopenharmony_ci		break;
72462306a36Sopenharmony_ci	case LINK_ESTABLISHED:
72562306a36Sopenharmony_ci		switch (evt) {
72662306a36Sopenharmony_ci		case LINK_PEER_RESET_EVT:
72762306a36Sopenharmony_ci			l->state = LINK_PEER_RESET;
72862306a36Sopenharmony_ci			rc |= TIPC_LINK_DOWN_EVT;
72962306a36Sopenharmony_ci			break;
73062306a36Sopenharmony_ci		case LINK_FAILURE_EVT:
73162306a36Sopenharmony_ci			l->state = LINK_RESETTING;
73262306a36Sopenharmony_ci			rc |= TIPC_LINK_DOWN_EVT;
73362306a36Sopenharmony_ci			break;
73462306a36Sopenharmony_ci		case LINK_RESET_EVT:
73562306a36Sopenharmony_ci			l->state = LINK_RESET;
73662306a36Sopenharmony_ci			break;
73762306a36Sopenharmony_ci		case LINK_ESTABLISH_EVT:
73862306a36Sopenharmony_ci		case LINK_SYNCH_END_EVT:
73962306a36Sopenharmony_ci			break;
74062306a36Sopenharmony_ci		case LINK_SYNCH_BEGIN_EVT:
74162306a36Sopenharmony_ci			l->state = LINK_SYNCHING;
74262306a36Sopenharmony_ci			break;
74362306a36Sopenharmony_ci		case LINK_FAILOVER_BEGIN_EVT:
74462306a36Sopenharmony_ci		case LINK_FAILOVER_END_EVT:
74562306a36Sopenharmony_ci		default:
74662306a36Sopenharmony_ci			goto illegal_evt;
74762306a36Sopenharmony_ci		}
74862306a36Sopenharmony_ci		break;
74962306a36Sopenharmony_ci	case LINK_SYNCHING:
75062306a36Sopenharmony_ci		switch (evt) {
75162306a36Sopenharmony_ci		case LINK_PEER_RESET_EVT:
75262306a36Sopenharmony_ci			l->state = LINK_PEER_RESET;
75362306a36Sopenharmony_ci			rc |= TIPC_LINK_DOWN_EVT;
75462306a36Sopenharmony_ci			break;
75562306a36Sopenharmony_ci		case LINK_FAILURE_EVT:
75662306a36Sopenharmony_ci			l->state = LINK_RESETTING;
75762306a36Sopenharmony_ci			rc |= TIPC_LINK_DOWN_EVT;
75862306a36Sopenharmony_ci			break;
75962306a36Sopenharmony_ci		case LINK_RESET_EVT:
76062306a36Sopenharmony_ci			l->state = LINK_RESET;
76162306a36Sopenharmony_ci			break;
76262306a36Sopenharmony_ci		case LINK_ESTABLISH_EVT:
76362306a36Sopenharmony_ci		case LINK_SYNCH_BEGIN_EVT:
76462306a36Sopenharmony_ci			break;
76562306a36Sopenharmony_ci		case LINK_SYNCH_END_EVT:
76662306a36Sopenharmony_ci			l->state = LINK_ESTABLISHED;
76762306a36Sopenharmony_ci			break;
76862306a36Sopenharmony_ci		case LINK_FAILOVER_BEGIN_EVT:
76962306a36Sopenharmony_ci		case LINK_FAILOVER_END_EVT:
77062306a36Sopenharmony_ci		default:
77162306a36Sopenharmony_ci			goto illegal_evt;
77262306a36Sopenharmony_ci		}
77362306a36Sopenharmony_ci		break;
77462306a36Sopenharmony_ci	default:
77562306a36Sopenharmony_ci		pr_err("Unknown FSM state %x in %s\n", l->state, l->name);
77662306a36Sopenharmony_ci	}
77762306a36Sopenharmony_ci	trace_tipc_link_fsm(l->name, old_state, l->state, evt);
77862306a36Sopenharmony_ci	return rc;
77962306a36Sopenharmony_ciillegal_evt:
78062306a36Sopenharmony_ci	pr_err("Illegal FSM event %x in state %x on link %s\n",
78162306a36Sopenharmony_ci	       evt, l->state, l->name);
78262306a36Sopenharmony_ci	trace_tipc_link_fsm(l->name, old_state, l->state, evt);
78362306a36Sopenharmony_ci	return rc;
78462306a36Sopenharmony_ci}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci/* link_profile_stats - update statistical profiling of traffic
78762306a36Sopenharmony_ci */
78862306a36Sopenharmony_cistatic void link_profile_stats(struct tipc_link *l)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	struct sk_buff *skb;
79162306a36Sopenharmony_ci	struct tipc_msg *msg;
79262306a36Sopenharmony_ci	int length;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	/* Update counters used in statistical profiling of send traffic */
79562306a36Sopenharmony_ci	l->stats.accu_queue_sz += skb_queue_len(&l->transmq);
79662306a36Sopenharmony_ci	l->stats.queue_sz_counts++;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	skb = skb_peek(&l->transmq);
79962306a36Sopenharmony_ci	if (!skb)
80062306a36Sopenharmony_ci		return;
80162306a36Sopenharmony_ci	msg = buf_msg(skb);
80262306a36Sopenharmony_ci	length = msg_size(msg);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	if (msg_user(msg) == MSG_FRAGMENTER) {
80562306a36Sopenharmony_ci		if (msg_type(msg) != FIRST_FRAGMENT)
80662306a36Sopenharmony_ci			return;
80762306a36Sopenharmony_ci		length = msg_size(msg_inner_hdr(msg));
80862306a36Sopenharmony_ci	}
80962306a36Sopenharmony_ci	l->stats.msg_lengths_total += length;
81062306a36Sopenharmony_ci	l->stats.msg_length_counts++;
81162306a36Sopenharmony_ci	if (length <= 64)
81262306a36Sopenharmony_ci		l->stats.msg_length_profile[0]++;
81362306a36Sopenharmony_ci	else if (length <= 256)
81462306a36Sopenharmony_ci		l->stats.msg_length_profile[1]++;
81562306a36Sopenharmony_ci	else if (length <= 1024)
81662306a36Sopenharmony_ci		l->stats.msg_length_profile[2]++;
81762306a36Sopenharmony_ci	else if (length <= 4096)
81862306a36Sopenharmony_ci		l->stats.msg_length_profile[3]++;
81962306a36Sopenharmony_ci	else if (length <= 16384)
82062306a36Sopenharmony_ci		l->stats.msg_length_profile[4]++;
82162306a36Sopenharmony_ci	else if (length <= 32768)
82262306a36Sopenharmony_ci		l->stats.msg_length_profile[5]++;
82362306a36Sopenharmony_ci	else
82462306a36Sopenharmony_ci		l->stats.msg_length_profile[6]++;
82562306a36Sopenharmony_ci}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci/**
82862306a36Sopenharmony_ci * tipc_link_too_silent - check if link is "too silent"
82962306a36Sopenharmony_ci * @l: tipc link to be checked
83062306a36Sopenharmony_ci *
83162306a36Sopenharmony_ci * Return: true if the link 'silent_intv_cnt' is about to reach the
83262306a36Sopenharmony_ci * 'abort_limit' value, otherwise false
83362306a36Sopenharmony_ci */
83462306a36Sopenharmony_cibool tipc_link_too_silent(struct tipc_link *l)
83562306a36Sopenharmony_ci{
83662306a36Sopenharmony_ci	return (l->silent_intv_cnt + 2 > l->abort_limit);
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci/* tipc_link_timeout - perform periodic task as instructed from node timeout
84062306a36Sopenharmony_ci */
84162306a36Sopenharmony_ciint tipc_link_timeout(struct tipc_link *l, struct sk_buff_head *xmitq)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	int mtyp = 0;
84462306a36Sopenharmony_ci	int rc = 0;
84562306a36Sopenharmony_ci	bool state = false;
84662306a36Sopenharmony_ci	bool probe = false;
84762306a36Sopenharmony_ci	bool setup = false;
84862306a36Sopenharmony_ci	u16 bc_snt = l->bc_sndlink->snd_nxt - 1;
84962306a36Sopenharmony_ci	u16 bc_acked = l->bc_rcvlink->acked;
85062306a36Sopenharmony_ci	struct tipc_mon_state *mstate = &l->mon_state;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	trace_tipc_link_timeout(l, TIPC_DUMP_NONE, " ");
85362306a36Sopenharmony_ci	trace_tipc_link_too_silent(l, TIPC_DUMP_ALL, " ");
85462306a36Sopenharmony_ci	switch (l->state) {
85562306a36Sopenharmony_ci	case LINK_ESTABLISHED:
85662306a36Sopenharmony_ci	case LINK_SYNCHING:
85762306a36Sopenharmony_ci		mtyp = STATE_MSG;
85862306a36Sopenharmony_ci		link_profile_stats(l);
85962306a36Sopenharmony_ci		tipc_mon_get_state(l->net, l->addr, mstate, l->bearer_id);
86062306a36Sopenharmony_ci		if (mstate->reset || (l->silent_intv_cnt > l->abort_limit))
86162306a36Sopenharmony_ci			return tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
86262306a36Sopenharmony_ci		state = bc_acked != bc_snt;
86362306a36Sopenharmony_ci		state |= l->bc_rcvlink->rcv_unacked;
86462306a36Sopenharmony_ci		state |= l->rcv_unacked;
86562306a36Sopenharmony_ci		state |= !skb_queue_empty(&l->transmq);
86662306a36Sopenharmony_ci		probe = mstate->probing;
86762306a36Sopenharmony_ci		probe |= l->silent_intv_cnt;
86862306a36Sopenharmony_ci		if (probe || mstate->monitoring)
86962306a36Sopenharmony_ci			l->silent_intv_cnt++;
87062306a36Sopenharmony_ci		probe |= !skb_queue_empty(&l->deferdq);
87162306a36Sopenharmony_ci		if (l->snd_nxt == l->checkpoint) {
87262306a36Sopenharmony_ci			tipc_link_update_cwin(l, 0, 0);
87362306a36Sopenharmony_ci			probe = true;
87462306a36Sopenharmony_ci		}
87562306a36Sopenharmony_ci		l->checkpoint = l->snd_nxt;
87662306a36Sopenharmony_ci		break;
87762306a36Sopenharmony_ci	case LINK_RESET:
87862306a36Sopenharmony_ci		setup = l->rst_cnt++ <= 4;
87962306a36Sopenharmony_ci		setup |= !(l->rst_cnt % 16);
88062306a36Sopenharmony_ci		mtyp = RESET_MSG;
88162306a36Sopenharmony_ci		break;
88262306a36Sopenharmony_ci	case LINK_ESTABLISHING:
88362306a36Sopenharmony_ci		setup = true;
88462306a36Sopenharmony_ci		mtyp = ACTIVATE_MSG;
88562306a36Sopenharmony_ci		break;
88662306a36Sopenharmony_ci	case LINK_PEER_RESET:
88762306a36Sopenharmony_ci	case LINK_RESETTING:
88862306a36Sopenharmony_ci	case LINK_FAILINGOVER:
88962306a36Sopenharmony_ci		break;
89062306a36Sopenharmony_ci	default:
89162306a36Sopenharmony_ci		break;
89262306a36Sopenharmony_ci	}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	if (state || probe || setup)
89562306a36Sopenharmony_ci		tipc_link_build_proto_msg(l, mtyp, probe, 0, 0, 0, 0, xmitq);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	return rc;
89862306a36Sopenharmony_ci}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci/**
90162306a36Sopenharmony_ci * link_schedule_user - schedule a message sender for wakeup after congestion
90262306a36Sopenharmony_ci * @l: congested link
90362306a36Sopenharmony_ci * @hdr: header of message that is being sent
90462306a36Sopenharmony_ci * Create pseudo msg to send back to user when congestion abates
90562306a36Sopenharmony_ci */
90662306a36Sopenharmony_cistatic int link_schedule_user(struct tipc_link *l, struct tipc_msg *hdr)
90762306a36Sopenharmony_ci{
90862306a36Sopenharmony_ci	u32 dnode = tipc_own_addr(l->net);
90962306a36Sopenharmony_ci	u32 dport = msg_origport(hdr);
91062306a36Sopenharmony_ci	struct sk_buff *skb;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	/* Create and schedule wakeup pseudo message */
91362306a36Sopenharmony_ci	skb = tipc_msg_create(SOCK_WAKEUP, 0, INT_H_SIZE, 0,
91462306a36Sopenharmony_ci			      dnode, l->addr, dport, 0, 0);
91562306a36Sopenharmony_ci	if (!skb)
91662306a36Sopenharmony_ci		return -ENOBUFS;
91762306a36Sopenharmony_ci	msg_set_dest_droppable(buf_msg(skb), true);
91862306a36Sopenharmony_ci	TIPC_SKB_CB(skb)->chain_imp = msg_importance(hdr);
91962306a36Sopenharmony_ci	skb_queue_tail(&l->wakeupq, skb);
92062306a36Sopenharmony_ci	l->stats.link_congs++;
92162306a36Sopenharmony_ci	trace_tipc_link_conges(l, TIPC_DUMP_ALL, "wakeup scheduled!");
92262306a36Sopenharmony_ci	return -ELINKCONG;
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci/**
92662306a36Sopenharmony_ci * link_prepare_wakeup - prepare users for wakeup after congestion
92762306a36Sopenharmony_ci * @l: congested link
92862306a36Sopenharmony_ci * Wake up a number of waiting users, as permitted by available space
92962306a36Sopenharmony_ci * in the send queue
93062306a36Sopenharmony_ci */
93162306a36Sopenharmony_cistatic void link_prepare_wakeup(struct tipc_link *l)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	struct sk_buff_head *wakeupq = &l->wakeupq;
93462306a36Sopenharmony_ci	struct sk_buff_head *inputq = l->inputq;
93562306a36Sopenharmony_ci	struct sk_buff *skb, *tmp;
93662306a36Sopenharmony_ci	struct sk_buff_head tmpq;
93762306a36Sopenharmony_ci	int avail[5] = {0,};
93862306a36Sopenharmony_ci	int imp = 0;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	__skb_queue_head_init(&tmpq);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	for (; imp <= TIPC_SYSTEM_IMPORTANCE; imp++)
94362306a36Sopenharmony_ci		avail[imp] = l->backlog[imp].limit - l->backlog[imp].len;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	skb_queue_walk_safe(wakeupq, skb, tmp) {
94662306a36Sopenharmony_ci		imp = TIPC_SKB_CB(skb)->chain_imp;
94762306a36Sopenharmony_ci		if (avail[imp] <= 0)
94862306a36Sopenharmony_ci			continue;
94962306a36Sopenharmony_ci		avail[imp]--;
95062306a36Sopenharmony_ci		__skb_unlink(skb, wakeupq);
95162306a36Sopenharmony_ci		__skb_queue_tail(&tmpq, skb);
95262306a36Sopenharmony_ci	}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	spin_lock_bh(&inputq->lock);
95562306a36Sopenharmony_ci	skb_queue_splice_tail(&tmpq, inputq);
95662306a36Sopenharmony_ci	spin_unlock_bh(&inputq->lock);
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci/**
96162306a36Sopenharmony_ci * tipc_link_set_skb_retransmit_time - set the time at which retransmission of
96262306a36Sopenharmony_ci *                                     the given skb should be next attempted
96362306a36Sopenharmony_ci * @skb: skb to set a future retransmission time for
96462306a36Sopenharmony_ci * @l: link the skb will be transmitted on
96562306a36Sopenharmony_ci */
96662306a36Sopenharmony_cistatic void tipc_link_set_skb_retransmit_time(struct sk_buff *skb,
96762306a36Sopenharmony_ci					      struct tipc_link *l)
96862306a36Sopenharmony_ci{
96962306a36Sopenharmony_ci	if (link_is_bc_sndlink(l))
97062306a36Sopenharmony_ci		TIPC_SKB_CB(skb)->nxt_retr = TIPC_BC_RETR_LIM;
97162306a36Sopenharmony_ci	else
97262306a36Sopenharmony_ci		TIPC_SKB_CB(skb)->nxt_retr = TIPC_UC_RETR_TIME;
97362306a36Sopenharmony_ci}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_civoid tipc_link_reset(struct tipc_link *l)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	struct sk_buff_head list;
97862306a36Sopenharmony_ci	u32 imp;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	__skb_queue_head_init(&list);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	l->in_session = false;
98362306a36Sopenharmony_ci	/* Force re-synch of peer session number before establishing */
98462306a36Sopenharmony_ci	l->peer_session--;
98562306a36Sopenharmony_ci	l->session++;
98662306a36Sopenharmony_ci	l->mtu = l->advertised_mtu;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	spin_lock_bh(&l->wakeupq.lock);
98962306a36Sopenharmony_ci	skb_queue_splice_init(&l->wakeupq, &list);
99062306a36Sopenharmony_ci	spin_unlock_bh(&l->wakeupq.lock);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	spin_lock_bh(&l->inputq->lock);
99362306a36Sopenharmony_ci	skb_queue_splice_init(&list, l->inputq);
99462306a36Sopenharmony_ci	spin_unlock_bh(&l->inputq->lock);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	__skb_queue_purge(&l->transmq);
99762306a36Sopenharmony_ci	__skb_queue_purge(&l->deferdq);
99862306a36Sopenharmony_ci	__skb_queue_purge(&l->backlogq);
99962306a36Sopenharmony_ci	__skb_queue_purge(&l->failover_deferdq);
100062306a36Sopenharmony_ci	for (imp = 0; imp <= TIPC_SYSTEM_IMPORTANCE; imp++) {
100162306a36Sopenharmony_ci		l->backlog[imp].len = 0;
100262306a36Sopenharmony_ci		l->backlog[imp].target_bskb = NULL;
100362306a36Sopenharmony_ci	}
100462306a36Sopenharmony_ci	kfree_skb(l->reasm_buf);
100562306a36Sopenharmony_ci	kfree_skb(l->reasm_tnlmsg);
100662306a36Sopenharmony_ci	kfree_skb(l->failover_reasm_skb);
100762306a36Sopenharmony_ci	l->reasm_buf = NULL;
100862306a36Sopenharmony_ci	l->reasm_tnlmsg = NULL;
100962306a36Sopenharmony_ci	l->failover_reasm_skb = NULL;
101062306a36Sopenharmony_ci	l->rcv_unacked = 0;
101162306a36Sopenharmony_ci	l->snd_nxt = 1;
101262306a36Sopenharmony_ci	l->rcv_nxt = 1;
101362306a36Sopenharmony_ci	l->snd_nxt_state = 1;
101462306a36Sopenharmony_ci	l->rcv_nxt_state = 1;
101562306a36Sopenharmony_ci	l->acked = 0;
101662306a36Sopenharmony_ci	l->last_gap = 0;
101762306a36Sopenharmony_ci	kfree(l->last_ga);
101862306a36Sopenharmony_ci	l->last_ga = NULL;
101962306a36Sopenharmony_ci	l->silent_intv_cnt = 0;
102062306a36Sopenharmony_ci	l->rst_cnt = 0;
102162306a36Sopenharmony_ci	l->bc_peer_is_up = false;
102262306a36Sopenharmony_ci	memset(&l->mon_state, 0, sizeof(l->mon_state));
102362306a36Sopenharmony_ci	tipc_link_reset_stats(l);
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci/**
102762306a36Sopenharmony_ci * tipc_link_xmit(): enqueue buffer list according to queue situation
102862306a36Sopenharmony_ci * @l: link to use
102962306a36Sopenharmony_ci * @list: chain of buffers containing message
103062306a36Sopenharmony_ci * @xmitq: returned list of packets to be sent by caller
103162306a36Sopenharmony_ci *
103262306a36Sopenharmony_ci * Consumes the buffer chain.
103362306a36Sopenharmony_ci * Messages at TIPC_SYSTEM_IMPORTANCE are always accepted
103462306a36Sopenharmony_ci * Return: 0 if success, or errno: -ELINKCONG, -EMSGSIZE or -ENOBUFS
103562306a36Sopenharmony_ci */
103662306a36Sopenharmony_ciint tipc_link_xmit(struct tipc_link *l, struct sk_buff_head *list,
103762306a36Sopenharmony_ci		   struct sk_buff_head *xmitq)
103862306a36Sopenharmony_ci{
103962306a36Sopenharmony_ci	struct sk_buff_head *backlogq = &l->backlogq;
104062306a36Sopenharmony_ci	struct sk_buff_head *transmq = &l->transmq;
104162306a36Sopenharmony_ci	struct sk_buff *skb, *_skb;
104262306a36Sopenharmony_ci	u16 bc_ack = l->bc_rcvlink->rcv_nxt - 1;
104362306a36Sopenharmony_ci	u16 ack = l->rcv_nxt - 1;
104462306a36Sopenharmony_ci	u16 seqno = l->snd_nxt;
104562306a36Sopenharmony_ci	int pkt_cnt = skb_queue_len(list);
104662306a36Sopenharmony_ci	unsigned int mss = tipc_link_mss(l);
104762306a36Sopenharmony_ci	unsigned int cwin = l->window;
104862306a36Sopenharmony_ci	unsigned int mtu = l->mtu;
104962306a36Sopenharmony_ci	struct tipc_msg *hdr;
105062306a36Sopenharmony_ci	bool new_bundle;
105162306a36Sopenharmony_ci	int rc = 0;
105262306a36Sopenharmony_ci	int imp;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	if (pkt_cnt <= 0)
105562306a36Sopenharmony_ci		return 0;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	hdr = buf_msg(skb_peek(list));
105862306a36Sopenharmony_ci	if (unlikely(msg_size(hdr) > mtu)) {
105962306a36Sopenharmony_ci		pr_warn("Too large msg, purging xmit list %d %d %d %d %d!\n",
106062306a36Sopenharmony_ci			skb_queue_len(list), msg_user(hdr),
106162306a36Sopenharmony_ci			msg_type(hdr), msg_size(hdr), mtu);
106262306a36Sopenharmony_ci		__skb_queue_purge(list);
106362306a36Sopenharmony_ci		return -EMSGSIZE;
106462306a36Sopenharmony_ci	}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	imp = msg_importance(hdr);
106762306a36Sopenharmony_ci	/* Allow oversubscription of one data msg per source at congestion */
106862306a36Sopenharmony_ci	if (unlikely(l->backlog[imp].len >= l->backlog[imp].limit)) {
106962306a36Sopenharmony_ci		if (imp == TIPC_SYSTEM_IMPORTANCE) {
107062306a36Sopenharmony_ci			pr_warn("%s<%s>, link overflow", link_rst_msg, l->name);
107162306a36Sopenharmony_ci			return -ENOBUFS;
107262306a36Sopenharmony_ci		}
107362306a36Sopenharmony_ci		rc = link_schedule_user(l, hdr);
107462306a36Sopenharmony_ci	}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if (pkt_cnt > 1) {
107762306a36Sopenharmony_ci		l->stats.sent_fragmented++;
107862306a36Sopenharmony_ci		l->stats.sent_fragments += pkt_cnt;
107962306a36Sopenharmony_ci	}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	/* Prepare each packet for sending, and add to relevant queue: */
108262306a36Sopenharmony_ci	while ((skb = __skb_dequeue(list))) {
108362306a36Sopenharmony_ci		if (likely(skb_queue_len(transmq) < cwin)) {
108462306a36Sopenharmony_ci			hdr = buf_msg(skb);
108562306a36Sopenharmony_ci			msg_set_seqno(hdr, seqno);
108662306a36Sopenharmony_ci			msg_set_ack(hdr, ack);
108762306a36Sopenharmony_ci			msg_set_bcast_ack(hdr, bc_ack);
108862306a36Sopenharmony_ci			_skb = skb_clone(skb, GFP_ATOMIC);
108962306a36Sopenharmony_ci			if (!_skb) {
109062306a36Sopenharmony_ci				kfree_skb(skb);
109162306a36Sopenharmony_ci				__skb_queue_purge(list);
109262306a36Sopenharmony_ci				return -ENOBUFS;
109362306a36Sopenharmony_ci			}
109462306a36Sopenharmony_ci			__skb_queue_tail(transmq, skb);
109562306a36Sopenharmony_ci			tipc_link_set_skb_retransmit_time(skb, l);
109662306a36Sopenharmony_ci			__skb_queue_tail(xmitq, _skb);
109762306a36Sopenharmony_ci			TIPC_SKB_CB(skb)->ackers = l->ackers;
109862306a36Sopenharmony_ci			l->rcv_unacked = 0;
109962306a36Sopenharmony_ci			l->stats.sent_pkts++;
110062306a36Sopenharmony_ci			seqno++;
110162306a36Sopenharmony_ci			continue;
110262306a36Sopenharmony_ci		}
110362306a36Sopenharmony_ci		if (tipc_msg_try_bundle(l->backlog[imp].target_bskb, &skb,
110462306a36Sopenharmony_ci					mss, l->addr, &new_bundle)) {
110562306a36Sopenharmony_ci			if (skb) {
110662306a36Sopenharmony_ci				/* Keep a ref. to the skb for next try */
110762306a36Sopenharmony_ci				l->backlog[imp].target_bskb = skb;
110862306a36Sopenharmony_ci				l->backlog[imp].len++;
110962306a36Sopenharmony_ci				__skb_queue_tail(backlogq, skb);
111062306a36Sopenharmony_ci			} else {
111162306a36Sopenharmony_ci				if (new_bundle) {
111262306a36Sopenharmony_ci					l->stats.sent_bundles++;
111362306a36Sopenharmony_ci					l->stats.sent_bundled++;
111462306a36Sopenharmony_ci				}
111562306a36Sopenharmony_ci				l->stats.sent_bundled++;
111662306a36Sopenharmony_ci			}
111762306a36Sopenharmony_ci			continue;
111862306a36Sopenharmony_ci		}
111962306a36Sopenharmony_ci		l->backlog[imp].target_bskb = NULL;
112062306a36Sopenharmony_ci		l->backlog[imp].len += (1 + skb_queue_len(list));
112162306a36Sopenharmony_ci		__skb_queue_tail(backlogq, skb);
112262306a36Sopenharmony_ci		skb_queue_splice_tail_init(list, backlogq);
112362306a36Sopenharmony_ci	}
112462306a36Sopenharmony_ci	l->snd_nxt = seqno;
112562306a36Sopenharmony_ci	return rc;
112662306a36Sopenharmony_ci}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_cistatic void tipc_link_update_cwin(struct tipc_link *l, int released,
112962306a36Sopenharmony_ci				  bool retransmitted)
113062306a36Sopenharmony_ci{
113162306a36Sopenharmony_ci	int bklog_len = skb_queue_len(&l->backlogq);
113262306a36Sopenharmony_ci	struct sk_buff_head *txq = &l->transmq;
113362306a36Sopenharmony_ci	int txq_len = skb_queue_len(txq);
113462306a36Sopenharmony_ci	u16 cwin = l->window;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	/* Enter fast recovery */
113762306a36Sopenharmony_ci	if (unlikely(retransmitted)) {
113862306a36Sopenharmony_ci		l->ssthresh = max_t(u16, l->window / 2, 300);
113962306a36Sopenharmony_ci		l->window = min_t(u16, l->ssthresh, l->window);
114062306a36Sopenharmony_ci		return;
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci	/* Enter slow start */
114362306a36Sopenharmony_ci	if (unlikely(!released)) {
114462306a36Sopenharmony_ci		l->ssthresh = max_t(u16, l->window / 2, 300);
114562306a36Sopenharmony_ci		l->window = l->min_win;
114662306a36Sopenharmony_ci		return;
114762306a36Sopenharmony_ci	}
114862306a36Sopenharmony_ci	/* Don't increase window if no pressure on the transmit queue */
114962306a36Sopenharmony_ci	if (txq_len + bklog_len < cwin)
115062306a36Sopenharmony_ci		return;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	/* Don't increase window if there are holes the transmit queue */
115362306a36Sopenharmony_ci	if (txq_len && l->snd_nxt - buf_seqno(skb_peek(txq)) != txq_len)
115462306a36Sopenharmony_ci		return;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	l->cong_acks += released;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	/* Slow start  */
115962306a36Sopenharmony_ci	if (cwin <= l->ssthresh) {
116062306a36Sopenharmony_ci		l->window = min_t(u16, cwin + released, l->max_win);
116162306a36Sopenharmony_ci		return;
116262306a36Sopenharmony_ci	}
116362306a36Sopenharmony_ci	/* Congestion avoidance */
116462306a36Sopenharmony_ci	if (l->cong_acks < cwin)
116562306a36Sopenharmony_ci		return;
116662306a36Sopenharmony_ci	l->window = min_t(u16, ++cwin, l->max_win);
116762306a36Sopenharmony_ci	l->cong_acks = 0;
116862306a36Sopenharmony_ci}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_cistatic void tipc_link_advance_backlog(struct tipc_link *l,
117162306a36Sopenharmony_ci				      struct sk_buff_head *xmitq)
117262306a36Sopenharmony_ci{
117362306a36Sopenharmony_ci	u16 bc_ack = l->bc_rcvlink->rcv_nxt - 1;
117462306a36Sopenharmony_ci	struct sk_buff_head *txq = &l->transmq;
117562306a36Sopenharmony_ci	struct sk_buff *skb, *_skb;
117662306a36Sopenharmony_ci	u16 ack = l->rcv_nxt - 1;
117762306a36Sopenharmony_ci	u16 seqno = l->snd_nxt;
117862306a36Sopenharmony_ci	struct tipc_msg *hdr;
117962306a36Sopenharmony_ci	u16 cwin = l->window;
118062306a36Sopenharmony_ci	u32 imp;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	while (skb_queue_len(txq) < cwin) {
118362306a36Sopenharmony_ci		skb = skb_peek(&l->backlogq);
118462306a36Sopenharmony_ci		if (!skb)
118562306a36Sopenharmony_ci			break;
118662306a36Sopenharmony_ci		_skb = skb_clone(skb, GFP_ATOMIC);
118762306a36Sopenharmony_ci		if (!_skb)
118862306a36Sopenharmony_ci			break;
118962306a36Sopenharmony_ci		__skb_dequeue(&l->backlogq);
119062306a36Sopenharmony_ci		hdr = buf_msg(skb);
119162306a36Sopenharmony_ci		imp = msg_importance(hdr);
119262306a36Sopenharmony_ci		l->backlog[imp].len--;
119362306a36Sopenharmony_ci		if (unlikely(skb == l->backlog[imp].target_bskb))
119462306a36Sopenharmony_ci			l->backlog[imp].target_bskb = NULL;
119562306a36Sopenharmony_ci		__skb_queue_tail(&l->transmq, skb);
119662306a36Sopenharmony_ci		tipc_link_set_skb_retransmit_time(skb, l);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci		__skb_queue_tail(xmitq, _skb);
119962306a36Sopenharmony_ci		TIPC_SKB_CB(skb)->ackers = l->ackers;
120062306a36Sopenharmony_ci		msg_set_seqno(hdr, seqno);
120162306a36Sopenharmony_ci		msg_set_ack(hdr, ack);
120262306a36Sopenharmony_ci		msg_set_bcast_ack(hdr, bc_ack);
120362306a36Sopenharmony_ci		l->rcv_unacked = 0;
120462306a36Sopenharmony_ci		l->stats.sent_pkts++;
120562306a36Sopenharmony_ci		seqno++;
120662306a36Sopenharmony_ci	}
120762306a36Sopenharmony_ci	l->snd_nxt = seqno;
120862306a36Sopenharmony_ci}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci/**
121162306a36Sopenharmony_ci * link_retransmit_failure() - Detect repeated retransmit failures
121262306a36Sopenharmony_ci * @l: tipc link sender
121362306a36Sopenharmony_ci * @r: tipc link receiver (= l in case of unicast)
121462306a36Sopenharmony_ci * @rc: returned code
121562306a36Sopenharmony_ci *
121662306a36Sopenharmony_ci * Return: true if the repeated retransmit failures happens, otherwise
121762306a36Sopenharmony_ci * false
121862306a36Sopenharmony_ci */
121962306a36Sopenharmony_cistatic bool link_retransmit_failure(struct tipc_link *l, struct tipc_link *r,
122062306a36Sopenharmony_ci				    int *rc)
122162306a36Sopenharmony_ci{
122262306a36Sopenharmony_ci	struct sk_buff *skb = skb_peek(&l->transmq);
122362306a36Sopenharmony_ci	struct tipc_msg *hdr;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	if (!skb)
122662306a36Sopenharmony_ci		return false;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	if (!TIPC_SKB_CB(skb)->retr_cnt)
122962306a36Sopenharmony_ci		return false;
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	if (!time_after(jiffies, TIPC_SKB_CB(skb)->retr_stamp +
123262306a36Sopenharmony_ci			msecs_to_jiffies(r->tolerance * 10)))
123362306a36Sopenharmony_ci		return false;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	hdr = buf_msg(skb);
123662306a36Sopenharmony_ci	if (link_is_bc_sndlink(l) && !less(r->acked, msg_seqno(hdr)))
123762306a36Sopenharmony_ci		return false;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	pr_warn("Retransmission failure on link <%s>\n", l->name);
124062306a36Sopenharmony_ci	link_print(l, "State of link ");
124162306a36Sopenharmony_ci	pr_info("Failed msg: usr %u, typ %u, len %u, err %u\n",
124262306a36Sopenharmony_ci		msg_user(hdr), msg_type(hdr), msg_size(hdr), msg_errcode(hdr));
124362306a36Sopenharmony_ci	pr_info("sqno %u, prev: %x, dest: %x\n",
124462306a36Sopenharmony_ci		msg_seqno(hdr), msg_prevnode(hdr), msg_destnode(hdr));
124562306a36Sopenharmony_ci	pr_info("retr_stamp %d, retr_cnt %d\n",
124662306a36Sopenharmony_ci		jiffies_to_msecs(TIPC_SKB_CB(skb)->retr_stamp),
124762306a36Sopenharmony_ci		TIPC_SKB_CB(skb)->retr_cnt);
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	trace_tipc_list_dump(&l->transmq, true, "retrans failure!");
125062306a36Sopenharmony_ci	trace_tipc_link_dump(l, TIPC_DUMP_NONE, "retrans failure!");
125162306a36Sopenharmony_ci	trace_tipc_link_dump(r, TIPC_DUMP_NONE, "retrans failure!");
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	if (link_is_bc_sndlink(l)) {
125462306a36Sopenharmony_ci		r->state = LINK_RESET;
125562306a36Sopenharmony_ci		*rc |= TIPC_LINK_DOWN_EVT;
125662306a36Sopenharmony_ci	} else {
125762306a36Sopenharmony_ci		*rc |= tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
125862306a36Sopenharmony_ci	}
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	return true;
126162306a36Sopenharmony_ci}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci/* tipc_data_input - deliver data and name distr msgs to upper layer
126462306a36Sopenharmony_ci *
126562306a36Sopenharmony_ci * Consumes buffer if message is of right type
126662306a36Sopenharmony_ci * Node lock must be held
126762306a36Sopenharmony_ci */
126862306a36Sopenharmony_cistatic bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb,
126962306a36Sopenharmony_ci			    struct sk_buff_head *inputq)
127062306a36Sopenharmony_ci{
127162306a36Sopenharmony_ci	struct sk_buff_head *mc_inputq = l->bc_rcvlink->inputq;
127262306a36Sopenharmony_ci	struct tipc_msg *hdr = buf_msg(skb);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	switch (msg_user(hdr)) {
127562306a36Sopenharmony_ci	case TIPC_LOW_IMPORTANCE:
127662306a36Sopenharmony_ci	case TIPC_MEDIUM_IMPORTANCE:
127762306a36Sopenharmony_ci	case TIPC_HIGH_IMPORTANCE:
127862306a36Sopenharmony_ci	case TIPC_CRITICAL_IMPORTANCE:
127962306a36Sopenharmony_ci		if (unlikely(msg_in_group(hdr) || msg_mcast(hdr))) {
128062306a36Sopenharmony_ci			skb_queue_tail(mc_inputq, skb);
128162306a36Sopenharmony_ci			return true;
128262306a36Sopenharmony_ci		}
128362306a36Sopenharmony_ci		fallthrough;
128462306a36Sopenharmony_ci	case CONN_MANAGER:
128562306a36Sopenharmony_ci		skb_queue_tail(inputq, skb);
128662306a36Sopenharmony_ci		return true;
128762306a36Sopenharmony_ci	case GROUP_PROTOCOL:
128862306a36Sopenharmony_ci		skb_queue_tail(mc_inputq, skb);
128962306a36Sopenharmony_ci		return true;
129062306a36Sopenharmony_ci	case NAME_DISTRIBUTOR:
129162306a36Sopenharmony_ci		l->bc_rcvlink->state = LINK_ESTABLISHED;
129262306a36Sopenharmony_ci		skb_queue_tail(l->namedq, skb);
129362306a36Sopenharmony_ci		return true;
129462306a36Sopenharmony_ci	case MSG_BUNDLER:
129562306a36Sopenharmony_ci	case TUNNEL_PROTOCOL:
129662306a36Sopenharmony_ci	case MSG_FRAGMENTER:
129762306a36Sopenharmony_ci	case BCAST_PROTOCOL:
129862306a36Sopenharmony_ci		return false;
129962306a36Sopenharmony_ci#ifdef CONFIG_TIPC_CRYPTO
130062306a36Sopenharmony_ci	case MSG_CRYPTO:
130162306a36Sopenharmony_ci		if (sysctl_tipc_key_exchange_enabled &&
130262306a36Sopenharmony_ci		    TIPC_SKB_CB(skb)->decrypted) {
130362306a36Sopenharmony_ci			tipc_crypto_msg_rcv(l->net, skb);
130462306a36Sopenharmony_ci			return true;
130562306a36Sopenharmony_ci		}
130662306a36Sopenharmony_ci		fallthrough;
130762306a36Sopenharmony_ci#endif
130862306a36Sopenharmony_ci	default:
130962306a36Sopenharmony_ci		pr_warn("Dropping received illegal msg type\n");
131062306a36Sopenharmony_ci		kfree_skb(skb);
131162306a36Sopenharmony_ci		return true;
131262306a36Sopenharmony_ci	}
131362306a36Sopenharmony_ci}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci/* tipc_link_input - process packet that has passed link protocol check
131662306a36Sopenharmony_ci *
131762306a36Sopenharmony_ci * Consumes buffer
131862306a36Sopenharmony_ci */
131962306a36Sopenharmony_cistatic int tipc_link_input(struct tipc_link *l, struct sk_buff *skb,
132062306a36Sopenharmony_ci			   struct sk_buff_head *inputq,
132162306a36Sopenharmony_ci			   struct sk_buff **reasm_skb)
132262306a36Sopenharmony_ci{
132362306a36Sopenharmony_ci	struct tipc_msg *hdr = buf_msg(skb);
132462306a36Sopenharmony_ci	struct sk_buff *iskb;
132562306a36Sopenharmony_ci	struct sk_buff_head tmpq;
132662306a36Sopenharmony_ci	int usr = msg_user(hdr);
132762306a36Sopenharmony_ci	int pos = 0;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	if (usr == MSG_BUNDLER) {
133062306a36Sopenharmony_ci		skb_queue_head_init(&tmpq);
133162306a36Sopenharmony_ci		l->stats.recv_bundles++;
133262306a36Sopenharmony_ci		l->stats.recv_bundled += msg_msgcnt(hdr);
133362306a36Sopenharmony_ci		while (tipc_msg_extract(skb, &iskb, &pos))
133462306a36Sopenharmony_ci			tipc_data_input(l, iskb, &tmpq);
133562306a36Sopenharmony_ci		tipc_skb_queue_splice_tail(&tmpq, inputq);
133662306a36Sopenharmony_ci		return 0;
133762306a36Sopenharmony_ci	} else if (usr == MSG_FRAGMENTER) {
133862306a36Sopenharmony_ci		l->stats.recv_fragments++;
133962306a36Sopenharmony_ci		if (tipc_buf_append(reasm_skb, &skb)) {
134062306a36Sopenharmony_ci			l->stats.recv_fragmented++;
134162306a36Sopenharmony_ci			tipc_data_input(l, skb, inputq);
134262306a36Sopenharmony_ci		} else if (!*reasm_skb && !link_is_bc_rcvlink(l)) {
134362306a36Sopenharmony_ci			pr_warn_ratelimited("Unable to build fragment list\n");
134462306a36Sopenharmony_ci			return tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
134562306a36Sopenharmony_ci		}
134662306a36Sopenharmony_ci		return 0;
134762306a36Sopenharmony_ci	} else if (usr == BCAST_PROTOCOL) {
134862306a36Sopenharmony_ci		tipc_bcast_lock(l->net);
134962306a36Sopenharmony_ci		tipc_link_bc_init_rcv(l->bc_rcvlink, hdr);
135062306a36Sopenharmony_ci		tipc_bcast_unlock(l->net);
135162306a36Sopenharmony_ci	}
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	kfree_skb(skb);
135462306a36Sopenharmony_ci	return 0;
135562306a36Sopenharmony_ci}
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci/* tipc_link_tnl_rcv() - receive TUNNEL_PROTOCOL message, drop or process the
135862306a36Sopenharmony_ci *			 inner message along with the ones in the old link's
135962306a36Sopenharmony_ci *			 deferdq
136062306a36Sopenharmony_ci * @l: tunnel link
136162306a36Sopenharmony_ci * @skb: TUNNEL_PROTOCOL message
136262306a36Sopenharmony_ci * @inputq: queue to put messages ready for delivery
136362306a36Sopenharmony_ci */
136462306a36Sopenharmony_cistatic int tipc_link_tnl_rcv(struct tipc_link *l, struct sk_buff *skb,
136562306a36Sopenharmony_ci			     struct sk_buff_head *inputq)
136662306a36Sopenharmony_ci{
136762306a36Sopenharmony_ci	struct sk_buff **reasm_skb = &l->failover_reasm_skb;
136862306a36Sopenharmony_ci	struct sk_buff **reasm_tnlmsg = &l->reasm_tnlmsg;
136962306a36Sopenharmony_ci	struct sk_buff_head *fdefq = &l->failover_deferdq;
137062306a36Sopenharmony_ci	struct tipc_msg *hdr = buf_msg(skb);
137162306a36Sopenharmony_ci	struct sk_buff *iskb;
137262306a36Sopenharmony_ci	int ipos = 0;
137362306a36Sopenharmony_ci	int rc = 0;
137462306a36Sopenharmony_ci	u16 seqno;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	if (msg_type(hdr) == SYNCH_MSG) {
137762306a36Sopenharmony_ci		kfree_skb(skb);
137862306a36Sopenharmony_ci		return 0;
137962306a36Sopenharmony_ci	}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	/* Not a fragment? */
138262306a36Sopenharmony_ci	if (likely(!msg_nof_fragms(hdr))) {
138362306a36Sopenharmony_ci		if (unlikely(!tipc_msg_extract(skb, &iskb, &ipos))) {
138462306a36Sopenharmony_ci			pr_warn_ratelimited("Unable to extract msg, defq: %d\n",
138562306a36Sopenharmony_ci					    skb_queue_len(fdefq));
138662306a36Sopenharmony_ci			return 0;
138762306a36Sopenharmony_ci		}
138862306a36Sopenharmony_ci		kfree_skb(skb);
138962306a36Sopenharmony_ci	} else {
139062306a36Sopenharmony_ci		/* Set fragment type for buf_append */
139162306a36Sopenharmony_ci		if (msg_fragm_no(hdr) == 1)
139262306a36Sopenharmony_ci			msg_set_type(hdr, FIRST_FRAGMENT);
139362306a36Sopenharmony_ci		else if (msg_fragm_no(hdr) < msg_nof_fragms(hdr))
139462306a36Sopenharmony_ci			msg_set_type(hdr, FRAGMENT);
139562306a36Sopenharmony_ci		else
139662306a36Sopenharmony_ci			msg_set_type(hdr, LAST_FRAGMENT);
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci		if (!tipc_buf_append(reasm_tnlmsg, &skb)) {
139962306a36Sopenharmony_ci			/* Successful but non-complete reassembly? */
140062306a36Sopenharmony_ci			if (*reasm_tnlmsg || link_is_bc_rcvlink(l))
140162306a36Sopenharmony_ci				return 0;
140262306a36Sopenharmony_ci			pr_warn_ratelimited("Unable to reassemble tunnel msg\n");
140362306a36Sopenharmony_ci			return tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
140462306a36Sopenharmony_ci		}
140562306a36Sopenharmony_ci		iskb = skb;
140662306a36Sopenharmony_ci	}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	do {
140962306a36Sopenharmony_ci		seqno = buf_seqno(iskb);
141062306a36Sopenharmony_ci		if (unlikely(less(seqno, l->drop_point))) {
141162306a36Sopenharmony_ci			kfree_skb(iskb);
141262306a36Sopenharmony_ci			continue;
141362306a36Sopenharmony_ci		}
141462306a36Sopenharmony_ci		if (unlikely(seqno != l->drop_point)) {
141562306a36Sopenharmony_ci			__tipc_skb_queue_sorted(fdefq, seqno, iskb);
141662306a36Sopenharmony_ci			continue;
141762306a36Sopenharmony_ci		}
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci		l->drop_point++;
142062306a36Sopenharmony_ci		if (!tipc_data_input(l, iskb, inputq))
142162306a36Sopenharmony_ci			rc |= tipc_link_input(l, iskb, inputq, reasm_skb);
142262306a36Sopenharmony_ci		if (unlikely(rc))
142362306a36Sopenharmony_ci			break;
142462306a36Sopenharmony_ci	} while ((iskb = __tipc_skb_dequeue(fdefq, l->drop_point)));
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	return rc;
142762306a36Sopenharmony_ci}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci/**
143062306a36Sopenharmony_ci * tipc_get_gap_ack_blks - get Gap ACK blocks from PROTOCOL/STATE_MSG
143162306a36Sopenharmony_ci * @ga: returned pointer to the Gap ACK blocks if any
143262306a36Sopenharmony_ci * @l: the tipc link
143362306a36Sopenharmony_ci * @hdr: the PROTOCOL/STATE_MSG header
143462306a36Sopenharmony_ci * @uc: desired Gap ACK blocks type, i.e. unicast (= 1) or broadcast (= 0)
143562306a36Sopenharmony_ci *
143662306a36Sopenharmony_ci * Return: the total Gap ACK blocks size
143762306a36Sopenharmony_ci */
143862306a36Sopenharmony_ciu16 tipc_get_gap_ack_blks(struct tipc_gap_ack_blks **ga, struct tipc_link *l,
143962306a36Sopenharmony_ci			  struct tipc_msg *hdr, bool uc)
144062306a36Sopenharmony_ci{
144162306a36Sopenharmony_ci	struct tipc_gap_ack_blks *p;
144262306a36Sopenharmony_ci	u16 sz = 0;
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	/* Does peer support the Gap ACK blocks feature? */
144562306a36Sopenharmony_ci	if (l->peer_caps & TIPC_GAP_ACK_BLOCK) {
144662306a36Sopenharmony_ci		p = (struct tipc_gap_ack_blks *)msg_data(hdr);
144762306a36Sopenharmony_ci		sz = ntohs(p->len);
144862306a36Sopenharmony_ci		/* Sanity check */
144962306a36Sopenharmony_ci		if (sz == struct_size(p, gacks, size_add(p->ugack_cnt, p->bgack_cnt))) {
145062306a36Sopenharmony_ci			/* Good, check if the desired type exists */
145162306a36Sopenharmony_ci			if ((uc && p->ugack_cnt) || (!uc && p->bgack_cnt))
145262306a36Sopenharmony_ci				goto ok;
145362306a36Sopenharmony_ci		/* Backward compatible: peer might not support bc, but uc? */
145462306a36Sopenharmony_ci		} else if (uc && sz == struct_size(p, gacks, p->ugack_cnt)) {
145562306a36Sopenharmony_ci			if (p->ugack_cnt) {
145662306a36Sopenharmony_ci				p->bgack_cnt = 0;
145762306a36Sopenharmony_ci				goto ok;
145862306a36Sopenharmony_ci			}
145962306a36Sopenharmony_ci		}
146062306a36Sopenharmony_ci	}
146162306a36Sopenharmony_ci	/* Other cases: ignore! */
146262306a36Sopenharmony_ci	p = NULL;
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ciok:
146562306a36Sopenharmony_ci	*ga = p;
146662306a36Sopenharmony_ci	return sz;
146762306a36Sopenharmony_ci}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_cistatic u8 __tipc_build_gap_ack_blks(struct tipc_gap_ack_blks *ga,
147062306a36Sopenharmony_ci				    struct tipc_link *l, u8 start_index)
147162306a36Sopenharmony_ci{
147262306a36Sopenharmony_ci	struct tipc_gap_ack *gacks = &ga->gacks[start_index];
147362306a36Sopenharmony_ci	struct sk_buff *skb = skb_peek(&l->deferdq);
147462306a36Sopenharmony_ci	u16 expect, seqno = 0;
147562306a36Sopenharmony_ci	u8 n = 0;
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	if (!skb)
147862306a36Sopenharmony_ci		return 0;
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	expect = buf_seqno(skb);
148162306a36Sopenharmony_ci	skb_queue_walk(&l->deferdq, skb) {
148262306a36Sopenharmony_ci		seqno = buf_seqno(skb);
148362306a36Sopenharmony_ci		if (unlikely(more(seqno, expect))) {
148462306a36Sopenharmony_ci			gacks[n].ack = htons(expect - 1);
148562306a36Sopenharmony_ci			gacks[n].gap = htons(seqno - expect);
148662306a36Sopenharmony_ci			if (++n >= MAX_GAP_ACK_BLKS / 2) {
148762306a36Sopenharmony_ci				pr_info_ratelimited("Gacks on %s: %d, ql: %d!\n",
148862306a36Sopenharmony_ci						    l->name, n,
148962306a36Sopenharmony_ci						    skb_queue_len(&l->deferdq));
149062306a36Sopenharmony_ci				return n;
149162306a36Sopenharmony_ci			}
149262306a36Sopenharmony_ci		} else if (unlikely(less(seqno, expect))) {
149362306a36Sopenharmony_ci			pr_warn("Unexpected skb in deferdq!\n");
149462306a36Sopenharmony_ci			continue;
149562306a36Sopenharmony_ci		}
149662306a36Sopenharmony_ci		expect = seqno + 1;
149762306a36Sopenharmony_ci	}
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci	/* last block */
150062306a36Sopenharmony_ci	gacks[n].ack = htons(seqno);
150162306a36Sopenharmony_ci	gacks[n].gap = 0;
150262306a36Sopenharmony_ci	n++;
150362306a36Sopenharmony_ci	return n;
150462306a36Sopenharmony_ci}
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci/* tipc_build_gap_ack_blks - build Gap ACK blocks
150762306a36Sopenharmony_ci * @l: tipc unicast link
150862306a36Sopenharmony_ci * @hdr: the tipc message buffer to store the Gap ACK blocks after built
150962306a36Sopenharmony_ci *
151062306a36Sopenharmony_ci * The function builds Gap ACK blocks for both the unicast & broadcast receiver
151162306a36Sopenharmony_ci * links of a certain peer, the buffer after built has the network data format
151262306a36Sopenharmony_ci * as found at the struct tipc_gap_ack_blks definition.
151362306a36Sopenharmony_ci *
151462306a36Sopenharmony_ci * returns the actual allocated memory size
151562306a36Sopenharmony_ci */
151662306a36Sopenharmony_cistatic u16 tipc_build_gap_ack_blks(struct tipc_link *l, struct tipc_msg *hdr)
151762306a36Sopenharmony_ci{
151862306a36Sopenharmony_ci	struct tipc_link *bcl = l->bc_rcvlink;
151962306a36Sopenharmony_ci	struct tipc_gap_ack_blks *ga;
152062306a36Sopenharmony_ci	u16 len;
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	ga = (struct tipc_gap_ack_blks *)msg_data(hdr);
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	/* Start with broadcast link first */
152562306a36Sopenharmony_ci	tipc_bcast_lock(bcl->net);
152662306a36Sopenharmony_ci	msg_set_bcast_ack(hdr, bcl->rcv_nxt - 1);
152762306a36Sopenharmony_ci	msg_set_bc_gap(hdr, link_bc_rcv_gap(bcl));
152862306a36Sopenharmony_ci	ga->bgack_cnt = __tipc_build_gap_ack_blks(ga, bcl, 0);
152962306a36Sopenharmony_ci	tipc_bcast_unlock(bcl->net);
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	/* Now for unicast link, but an explicit NACK only (???) */
153262306a36Sopenharmony_ci	ga->ugack_cnt = (msg_seq_gap(hdr)) ?
153362306a36Sopenharmony_ci			__tipc_build_gap_ack_blks(ga, l, ga->bgack_cnt) : 0;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	/* Total len */
153662306a36Sopenharmony_ci	len = struct_size(ga, gacks, size_add(ga->bgack_cnt, ga->ugack_cnt));
153762306a36Sopenharmony_ci	ga->len = htons(len);
153862306a36Sopenharmony_ci	return len;
153962306a36Sopenharmony_ci}
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci/* tipc_link_advance_transmq - advance TIPC link transmq queue by releasing
154262306a36Sopenharmony_ci *			       acked packets, also doing retransmissions if
154362306a36Sopenharmony_ci *			       gaps found
154462306a36Sopenharmony_ci * @l: tipc link with transmq queue to be advanced
154562306a36Sopenharmony_ci * @r: tipc link "receiver" i.e. in case of broadcast (= "l" if unicast)
154662306a36Sopenharmony_ci * @acked: seqno of last packet acked by peer without any gaps before
154762306a36Sopenharmony_ci * @gap: # of gap packets
154862306a36Sopenharmony_ci * @ga: buffer pointer to Gap ACK blocks from peer
154962306a36Sopenharmony_ci * @xmitq: queue for accumulating the retransmitted packets if any
155062306a36Sopenharmony_ci * @retransmitted: returned boolean value if a retransmission is really issued
155162306a36Sopenharmony_ci * @rc: returned code e.g. TIPC_LINK_DOWN_EVT if a repeated retransmit failures
155262306a36Sopenharmony_ci *      happens (- unlikely case)
155362306a36Sopenharmony_ci *
155462306a36Sopenharmony_ci * Return: the number of packets released from the link transmq
155562306a36Sopenharmony_ci */
155662306a36Sopenharmony_cistatic int tipc_link_advance_transmq(struct tipc_link *l, struct tipc_link *r,
155762306a36Sopenharmony_ci				     u16 acked, u16 gap,
155862306a36Sopenharmony_ci				     struct tipc_gap_ack_blks *ga,
155962306a36Sopenharmony_ci				     struct sk_buff_head *xmitq,
156062306a36Sopenharmony_ci				     bool *retransmitted, int *rc)
156162306a36Sopenharmony_ci{
156262306a36Sopenharmony_ci	struct tipc_gap_ack_blks *last_ga = r->last_ga, *this_ga = NULL;
156362306a36Sopenharmony_ci	struct tipc_gap_ack *gacks = NULL;
156462306a36Sopenharmony_ci	struct sk_buff *skb, *_skb, *tmp;
156562306a36Sopenharmony_ci	struct tipc_msg *hdr;
156662306a36Sopenharmony_ci	u32 qlen = skb_queue_len(&l->transmq);
156762306a36Sopenharmony_ci	u16 nacked = acked, ngap = gap, gack_cnt = 0;
156862306a36Sopenharmony_ci	u16 bc_ack = l->bc_rcvlink->rcv_nxt - 1;
156962306a36Sopenharmony_ci	u16 ack = l->rcv_nxt - 1;
157062306a36Sopenharmony_ci	u16 seqno, n = 0;
157162306a36Sopenharmony_ci	u16 end = r->acked, start = end, offset = r->last_gap;
157262306a36Sopenharmony_ci	u16 si = (last_ga) ? last_ga->start_index : 0;
157362306a36Sopenharmony_ci	bool is_uc = !link_is_bc_sndlink(l);
157462306a36Sopenharmony_ci	bool bc_has_acked = false;
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	trace_tipc_link_retrans(r, acked + 1, acked + gap, &l->transmq);
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	/* Determine Gap ACK blocks if any for the particular link */
157962306a36Sopenharmony_ci	if (ga && is_uc) {
158062306a36Sopenharmony_ci		/* Get the Gap ACKs, uc part */
158162306a36Sopenharmony_ci		gack_cnt = ga->ugack_cnt;
158262306a36Sopenharmony_ci		gacks = &ga->gacks[ga->bgack_cnt];
158362306a36Sopenharmony_ci	} else if (ga) {
158462306a36Sopenharmony_ci		/* Copy the Gap ACKs, bc part, for later renewal if needed */
158562306a36Sopenharmony_ci		this_ga = kmemdup(ga, struct_size(ga, gacks, ga->bgack_cnt),
158662306a36Sopenharmony_ci				  GFP_ATOMIC);
158762306a36Sopenharmony_ci		if (likely(this_ga)) {
158862306a36Sopenharmony_ci			this_ga->start_index = 0;
158962306a36Sopenharmony_ci			/* Start with the bc Gap ACKs */
159062306a36Sopenharmony_ci			gack_cnt = this_ga->bgack_cnt;
159162306a36Sopenharmony_ci			gacks = &this_ga->gacks[0];
159262306a36Sopenharmony_ci		} else {
159362306a36Sopenharmony_ci			/* Hmm, we can get in trouble..., simply ignore it */
159462306a36Sopenharmony_ci			pr_warn_ratelimited("Ignoring bc Gap ACKs, no memory\n");
159562306a36Sopenharmony_ci		}
159662306a36Sopenharmony_ci	}
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	/* Advance the link transmq */
159962306a36Sopenharmony_ci	skb_queue_walk_safe(&l->transmq, skb, tmp) {
160062306a36Sopenharmony_ci		seqno = buf_seqno(skb);
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_cinext_gap_ack:
160362306a36Sopenharmony_ci		if (less_eq(seqno, nacked)) {
160462306a36Sopenharmony_ci			if (is_uc)
160562306a36Sopenharmony_ci				goto release;
160662306a36Sopenharmony_ci			/* Skip packets peer has already acked */
160762306a36Sopenharmony_ci			if (!more(seqno, r->acked))
160862306a36Sopenharmony_ci				continue;
160962306a36Sopenharmony_ci			/* Get the next of last Gap ACK blocks */
161062306a36Sopenharmony_ci			while (more(seqno, end)) {
161162306a36Sopenharmony_ci				if (!last_ga || si >= last_ga->bgack_cnt)
161262306a36Sopenharmony_ci					break;
161362306a36Sopenharmony_ci				start = end + offset + 1;
161462306a36Sopenharmony_ci				end = ntohs(last_ga->gacks[si].ack);
161562306a36Sopenharmony_ci				offset = ntohs(last_ga->gacks[si].gap);
161662306a36Sopenharmony_ci				si++;
161762306a36Sopenharmony_ci				WARN_ONCE(more(start, end) ||
161862306a36Sopenharmony_ci					  (!offset &&
161962306a36Sopenharmony_ci					   si < last_ga->bgack_cnt) ||
162062306a36Sopenharmony_ci					  si > MAX_GAP_ACK_BLKS,
162162306a36Sopenharmony_ci					  "Corrupted Gap ACK: %d %d %d %d %d\n",
162262306a36Sopenharmony_ci					  start, end, offset, si,
162362306a36Sopenharmony_ci					  last_ga->bgack_cnt);
162462306a36Sopenharmony_ci			}
162562306a36Sopenharmony_ci			/* Check against the last Gap ACK block */
162662306a36Sopenharmony_ci			if (tipc_in_range(seqno, start, end))
162762306a36Sopenharmony_ci				continue;
162862306a36Sopenharmony_ci			/* Update/release the packet peer is acking */
162962306a36Sopenharmony_ci			bc_has_acked = true;
163062306a36Sopenharmony_ci			if (--TIPC_SKB_CB(skb)->ackers)
163162306a36Sopenharmony_ci				continue;
163262306a36Sopenharmony_cirelease:
163362306a36Sopenharmony_ci			/* release skb */
163462306a36Sopenharmony_ci			__skb_unlink(skb, &l->transmq);
163562306a36Sopenharmony_ci			kfree_skb(skb);
163662306a36Sopenharmony_ci		} else if (less_eq(seqno, nacked + ngap)) {
163762306a36Sopenharmony_ci			/* First gap: check if repeated retrans failures? */
163862306a36Sopenharmony_ci			if (unlikely(seqno == acked + 1 &&
163962306a36Sopenharmony_ci				     link_retransmit_failure(l, r, rc))) {
164062306a36Sopenharmony_ci				/* Ignore this bc Gap ACKs if any */
164162306a36Sopenharmony_ci				kfree(this_ga);
164262306a36Sopenharmony_ci				this_ga = NULL;
164362306a36Sopenharmony_ci				break;
164462306a36Sopenharmony_ci			}
164562306a36Sopenharmony_ci			/* retransmit skb if unrestricted*/
164662306a36Sopenharmony_ci			if (time_before(jiffies, TIPC_SKB_CB(skb)->nxt_retr))
164762306a36Sopenharmony_ci				continue;
164862306a36Sopenharmony_ci			tipc_link_set_skb_retransmit_time(skb, l);
164962306a36Sopenharmony_ci			_skb = pskb_copy(skb, GFP_ATOMIC);
165062306a36Sopenharmony_ci			if (!_skb)
165162306a36Sopenharmony_ci				continue;
165262306a36Sopenharmony_ci			hdr = buf_msg(_skb);
165362306a36Sopenharmony_ci			msg_set_ack(hdr, ack);
165462306a36Sopenharmony_ci			msg_set_bcast_ack(hdr, bc_ack);
165562306a36Sopenharmony_ci			_skb->priority = TC_PRIO_CONTROL;
165662306a36Sopenharmony_ci			__skb_queue_tail(xmitq, _skb);
165762306a36Sopenharmony_ci			l->stats.retransmitted++;
165862306a36Sopenharmony_ci			if (!is_uc)
165962306a36Sopenharmony_ci				r->stats.retransmitted++;
166062306a36Sopenharmony_ci			*retransmitted = true;
166162306a36Sopenharmony_ci			/* Increase actual retrans counter & mark first time */
166262306a36Sopenharmony_ci			if (!TIPC_SKB_CB(skb)->retr_cnt++)
166362306a36Sopenharmony_ci				TIPC_SKB_CB(skb)->retr_stamp = jiffies;
166462306a36Sopenharmony_ci		} else {
166562306a36Sopenharmony_ci			/* retry with Gap ACK blocks if any */
166662306a36Sopenharmony_ci			if (n >= gack_cnt)
166762306a36Sopenharmony_ci				break;
166862306a36Sopenharmony_ci			nacked = ntohs(gacks[n].ack);
166962306a36Sopenharmony_ci			ngap = ntohs(gacks[n].gap);
167062306a36Sopenharmony_ci			n++;
167162306a36Sopenharmony_ci			goto next_gap_ack;
167262306a36Sopenharmony_ci		}
167362306a36Sopenharmony_ci	}
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	/* Renew last Gap ACK blocks for bc if needed */
167662306a36Sopenharmony_ci	if (bc_has_acked) {
167762306a36Sopenharmony_ci		if (this_ga) {
167862306a36Sopenharmony_ci			kfree(last_ga);
167962306a36Sopenharmony_ci			r->last_ga = this_ga;
168062306a36Sopenharmony_ci			r->last_gap = gap;
168162306a36Sopenharmony_ci		} else if (last_ga) {
168262306a36Sopenharmony_ci			if (less(acked, start)) {
168362306a36Sopenharmony_ci				si--;
168462306a36Sopenharmony_ci				offset = start - acked - 1;
168562306a36Sopenharmony_ci			} else if (less(acked, end)) {
168662306a36Sopenharmony_ci				acked = end;
168762306a36Sopenharmony_ci			}
168862306a36Sopenharmony_ci			if (si < last_ga->bgack_cnt) {
168962306a36Sopenharmony_ci				last_ga->start_index = si;
169062306a36Sopenharmony_ci				r->last_gap = offset;
169162306a36Sopenharmony_ci			} else {
169262306a36Sopenharmony_ci				kfree(last_ga);
169362306a36Sopenharmony_ci				r->last_ga = NULL;
169462306a36Sopenharmony_ci				r->last_gap = 0;
169562306a36Sopenharmony_ci			}
169662306a36Sopenharmony_ci		} else {
169762306a36Sopenharmony_ci			r->last_gap = 0;
169862306a36Sopenharmony_ci		}
169962306a36Sopenharmony_ci		r->acked = acked;
170062306a36Sopenharmony_ci	} else {
170162306a36Sopenharmony_ci		kfree(this_ga);
170262306a36Sopenharmony_ci	}
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	return qlen - skb_queue_len(&l->transmq);
170562306a36Sopenharmony_ci}
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci/* tipc_link_build_state_msg: prepare link state message for transmission
170862306a36Sopenharmony_ci *
170962306a36Sopenharmony_ci * Note that sending of broadcast ack is coordinated among nodes, to reduce
171062306a36Sopenharmony_ci * risk of ack storms towards the sender
171162306a36Sopenharmony_ci */
171262306a36Sopenharmony_ciint tipc_link_build_state_msg(struct tipc_link *l, struct sk_buff_head *xmitq)
171362306a36Sopenharmony_ci{
171462306a36Sopenharmony_ci	if (!l)
171562306a36Sopenharmony_ci		return 0;
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	/* Broadcast ACK must be sent via a unicast link => defer to caller */
171862306a36Sopenharmony_ci	if (link_is_bc_rcvlink(l)) {
171962306a36Sopenharmony_ci		if (((l->rcv_nxt ^ tipc_own_addr(l->net)) & 0xf) != 0xf)
172062306a36Sopenharmony_ci			return 0;
172162306a36Sopenharmony_ci		l->rcv_unacked = 0;
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci		/* Use snd_nxt to store peer's snd_nxt in broadcast rcv link */
172462306a36Sopenharmony_ci		l->snd_nxt = l->rcv_nxt;
172562306a36Sopenharmony_ci		return TIPC_LINK_SND_STATE;
172662306a36Sopenharmony_ci	}
172762306a36Sopenharmony_ci	/* Unicast ACK */
172862306a36Sopenharmony_ci	l->rcv_unacked = 0;
172962306a36Sopenharmony_ci	l->stats.sent_acks++;
173062306a36Sopenharmony_ci	tipc_link_build_proto_msg(l, STATE_MSG, 0, 0, 0, 0, 0, xmitq);
173162306a36Sopenharmony_ci	return 0;
173262306a36Sopenharmony_ci}
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci/* tipc_link_build_reset_msg: prepare link RESET or ACTIVATE message
173562306a36Sopenharmony_ci */
173662306a36Sopenharmony_civoid tipc_link_build_reset_msg(struct tipc_link *l, struct sk_buff_head *xmitq)
173762306a36Sopenharmony_ci{
173862306a36Sopenharmony_ci	int mtyp = RESET_MSG;
173962306a36Sopenharmony_ci	struct sk_buff *skb;
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	if (l->state == LINK_ESTABLISHING)
174262306a36Sopenharmony_ci		mtyp = ACTIVATE_MSG;
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	tipc_link_build_proto_msg(l, mtyp, 0, 0, 0, 0, 0, xmitq);
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	/* Inform peer that this endpoint is going down if applicable */
174762306a36Sopenharmony_ci	skb = skb_peek_tail(xmitq);
174862306a36Sopenharmony_ci	if (skb && (l->state == LINK_RESET))
174962306a36Sopenharmony_ci		msg_set_peer_stopping(buf_msg(skb), 1);
175062306a36Sopenharmony_ci}
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci/* tipc_link_build_nack_msg: prepare link nack message for transmission
175362306a36Sopenharmony_ci * Note that sending of broadcast NACK is coordinated among nodes, to
175462306a36Sopenharmony_ci * reduce the risk of NACK storms towards the sender
175562306a36Sopenharmony_ci */
175662306a36Sopenharmony_cistatic int tipc_link_build_nack_msg(struct tipc_link *l,
175762306a36Sopenharmony_ci				    struct sk_buff_head *xmitq)
175862306a36Sopenharmony_ci{
175962306a36Sopenharmony_ci	u32 def_cnt = ++l->stats.deferred_recv;
176062306a36Sopenharmony_ci	struct sk_buff_head *dfq = &l->deferdq;
176162306a36Sopenharmony_ci	u32 defq_len = skb_queue_len(dfq);
176262306a36Sopenharmony_ci	int match1, match2;
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	if (link_is_bc_rcvlink(l)) {
176562306a36Sopenharmony_ci		match1 = def_cnt & 0xf;
176662306a36Sopenharmony_ci		match2 = tipc_own_addr(l->net) & 0xf;
176762306a36Sopenharmony_ci		if (match1 == match2)
176862306a36Sopenharmony_ci			return TIPC_LINK_SND_STATE;
176962306a36Sopenharmony_ci		return 0;
177062306a36Sopenharmony_ci	}
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	if (defq_len >= 3 && !((defq_len - 3) % 16)) {
177362306a36Sopenharmony_ci		u16 rcvgap = buf_seqno(skb_peek(dfq)) - l->rcv_nxt;
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci		tipc_link_build_proto_msg(l, STATE_MSG, 0, 0,
177662306a36Sopenharmony_ci					  rcvgap, 0, 0, xmitq);
177762306a36Sopenharmony_ci	}
177862306a36Sopenharmony_ci	return 0;
177962306a36Sopenharmony_ci}
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci/* tipc_link_rcv - process TIPC packets/messages arriving from off-node
178262306a36Sopenharmony_ci * @l: the link that should handle the message
178362306a36Sopenharmony_ci * @skb: TIPC packet
178462306a36Sopenharmony_ci * @xmitq: queue to place packets to be sent after this call
178562306a36Sopenharmony_ci */
178662306a36Sopenharmony_ciint tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb,
178762306a36Sopenharmony_ci		  struct sk_buff_head *xmitq)
178862306a36Sopenharmony_ci{
178962306a36Sopenharmony_ci	struct sk_buff_head *defq = &l->deferdq;
179062306a36Sopenharmony_ci	struct tipc_msg *hdr = buf_msg(skb);
179162306a36Sopenharmony_ci	u16 seqno, rcv_nxt, win_lim;
179262306a36Sopenharmony_ci	int released = 0;
179362306a36Sopenharmony_ci	int rc = 0;
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	/* Verify and update link state */
179662306a36Sopenharmony_ci	if (unlikely(msg_user(hdr) == LINK_PROTOCOL))
179762306a36Sopenharmony_ci		return tipc_link_proto_rcv(l, skb, xmitq);
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci	/* Don't send probe at next timeout expiration */
180062306a36Sopenharmony_ci	l->silent_intv_cnt = 0;
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	do {
180362306a36Sopenharmony_ci		hdr = buf_msg(skb);
180462306a36Sopenharmony_ci		seqno = msg_seqno(hdr);
180562306a36Sopenharmony_ci		rcv_nxt = l->rcv_nxt;
180662306a36Sopenharmony_ci		win_lim = rcv_nxt + TIPC_MAX_LINK_WIN;
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci		if (unlikely(!link_is_up(l))) {
180962306a36Sopenharmony_ci			if (l->state == LINK_ESTABLISHING)
181062306a36Sopenharmony_ci				rc = TIPC_LINK_UP_EVT;
181162306a36Sopenharmony_ci			kfree_skb(skb);
181262306a36Sopenharmony_ci			break;
181362306a36Sopenharmony_ci		}
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci		/* Drop if outside receive window */
181662306a36Sopenharmony_ci		if (unlikely(less(seqno, rcv_nxt) || more(seqno, win_lim))) {
181762306a36Sopenharmony_ci			l->stats.duplicates++;
181862306a36Sopenharmony_ci			kfree_skb(skb);
181962306a36Sopenharmony_ci			break;
182062306a36Sopenharmony_ci		}
182162306a36Sopenharmony_ci		released += tipc_link_advance_transmq(l, l, msg_ack(hdr), 0,
182262306a36Sopenharmony_ci						      NULL, NULL, NULL, NULL);
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci		/* Defer delivery if sequence gap */
182562306a36Sopenharmony_ci		if (unlikely(seqno != rcv_nxt)) {
182662306a36Sopenharmony_ci			if (!__tipc_skb_queue_sorted(defq, seqno, skb))
182762306a36Sopenharmony_ci				l->stats.duplicates++;
182862306a36Sopenharmony_ci			rc |= tipc_link_build_nack_msg(l, xmitq);
182962306a36Sopenharmony_ci			break;
183062306a36Sopenharmony_ci		}
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ci		/* Deliver packet */
183362306a36Sopenharmony_ci		l->rcv_nxt++;
183462306a36Sopenharmony_ci		l->stats.recv_pkts++;
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci		if (unlikely(msg_user(hdr) == TUNNEL_PROTOCOL))
183762306a36Sopenharmony_ci			rc |= tipc_link_tnl_rcv(l, skb, l->inputq);
183862306a36Sopenharmony_ci		else if (!tipc_data_input(l, skb, l->inputq))
183962306a36Sopenharmony_ci			rc |= tipc_link_input(l, skb, l->inputq, &l->reasm_buf);
184062306a36Sopenharmony_ci		if (unlikely(++l->rcv_unacked >= TIPC_MIN_LINK_WIN))
184162306a36Sopenharmony_ci			rc |= tipc_link_build_state_msg(l, xmitq);
184262306a36Sopenharmony_ci		if (unlikely(rc & ~TIPC_LINK_SND_STATE))
184362306a36Sopenharmony_ci			break;
184462306a36Sopenharmony_ci	} while ((skb = __tipc_skb_dequeue(defq, l->rcv_nxt)));
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	/* Forward queues and wake up waiting users */
184762306a36Sopenharmony_ci	if (released) {
184862306a36Sopenharmony_ci		tipc_link_update_cwin(l, released, 0);
184962306a36Sopenharmony_ci		tipc_link_advance_backlog(l, xmitq);
185062306a36Sopenharmony_ci		if (unlikely(!skb_queue_empty(&l->wakeupq)))
185162306a36Sopenharmony_ci			link_prepare_wakeup(l);
185262306a36Sopenharmony_ci	}
185362306a36Sopenharmony_ci	return rc;
185462306a36Sopenharmony_ci}
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_cistatic void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
185762306a36Sopenharmony_ci				      bool probe_reply, u16 rcvgap,
185862306a36Sopenharmony_ci				      int tolerance, int priority,
185962306a36Sopenharmony_ci				      struct sk_buff_head *xmitq)
186062306a36Sopenharmony_ci{
186162306a36Sopenharmony_ci	struct tipc_mon_state *mstate = &l->mon_state;
186262306a36Sopenharmony_ci	struct sk_buff_head *dfq = &l->deferdq;
186362306a36Sopenharmony_ci	struct tipc_link *bcl = l->bc_rcvlink;
186462306a36Sopenharmony_ci	struct tipc_msg *hdr;
186562306a36Sopenharmony_ci	struct sk_buff *skb;
186662306a36Sopenharmony_ci	bool node_up = link_is_up(bcl);
186762306a36Sopenharmony_ci	u16 glen = 0, bc_rcvgap = 0;
186862306a36Sopenharmony_ci	int dlen = 0;
186962306a36Sopenharmony_ci	void *data;
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	/* Don't send protocol message during reset or link failover */
187262306a36Sopenharmony_ci	if (tipc_link_is_blocked(l))
187362306a36Sopenharmony_ci		return;
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	if (!tipc_link_is_up(l) && (mtyp == STATE_MSG))
187662306a36Sopenharmony_ci		return;
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci	if ((probe || probe_reply) && !skb_queue_empty(dfq))
187962306a36Sopenharmony_ci		rcvgap = buf_seqno(skb_peek(dfq)) - l->rcv_nxt;
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	skb = tipc_msg_create(LINK_PROTOCOL, mtyp, INT_H_SIZE,
188262306a36Sopenharmony_ci			      tipc_max_domain_size + MAX_GAP_ACK_BLKS_SZ,
188362306a36Sopenharmony_ci			      l->addr, tipc_own_addr(l->net), 0, 0, 0);
188462306a36Sopenharmony_ci	if (!skb)
188562306a36Sopenharmony_ci		return;
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	hdr = buf_msg(skb);
188862306a36Sopenharmony_ci	data = msg_data(hdr);
188962306a36Sopenharmony_ci	msg_set_session(hdr, l->session);
189062306a36Sopenharmony_ci	msg_set_bearer_id(hdr, l->bearer_id);
189162306a36Sopenharmony_ci	msg_set_net_plane(hdr, l->net_plane);
189262306a36Sopenharmony_ci	msg_set_next_sent(hdr, l->snd_nxt);
189362306a36Sopenharmony_ci	msg_set_ack(hdr, l->rcv_nxt - 1);
189462306a36Sopenharmony_ci	msg_set_bcast_ack(hdr, bcl->rcv_nxt - 1);
189562306a36Sopenharmony_ci	msg_set_bc_ack_invalid(hdr, !node_up);
189662306a36Sopenharmony_ci	msg_set_last_bcast(hdr, l->bc_sndlink->snd_nxt - 1);
189762306a36Sopenharmony_ci	msg_set_link_tolerance(hdr, tolerance);
189862306a36Sopenharmony_ci	msg_set_linkprio(hdr, priority);
189962306a36Sopenharmony_ci	msg_set_redundant_link(hdr, node_up);
190062306a36Sopenharmony_ci	msg_set_seq_gap(hdr, 0);
190162306a36Sopenharmony_ci	msg_set_seqno(hdr, l->snd_nxt + U16_MAX / 2);
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	if (mtyp == STATE_MSG) {
190462306a36Sopenharmony_ci		if (l->peer_caps & TIPC_LINK_PROTO_SEQNO)
190562306a36Sopenharmony_ci			msg_set_seqno(hdr, l->snd_nxt_state++);
190662306a36Sopenharmony_ci		msg_set_seq_gap(hdr, rcvgap);
190762306a36Sopenharmony_ci		bc_rcvgap = link_bc_rcv_gap(bcl);
190862306a36Sopenharmony_ci		msg_set_bc_gap(hdr, bc_rcvgap);
190962306a36Sopenharmony_ci		msg_set_probe(hdr, probe);
191062306a36Sopenharmony_ci		msg_set_is_keepalive(hdr, probe || probe_reply);
191162306a36Sopenharmony_ci		if (l->peer_caps & TIPC_GAP_ACK_BLOCK)
191262306a36Sopenharmony_ci			glen = tipc_build_gap_ack_blks(l, hdr);
191362306a36Sopenharmony_ci		tipc_mon_prep(l->net, data + glen, &dlen, mstate, l->bearer_id);
191462306a36Sopenharmony_ci		msg_set_size(hdr, INT_H_SIZE + glen + dlen);
191562306a36Sopenharmony_ci		skb_trim(skb, INT_H_SIZE + glen + dlen);
191662306a36Sopenharmony_ci		l->stats.sent_states++;
191762306a36Sopenharmony_ci		l->rcv_unacked = 0;
191862306a36Sopenharmony_ci	} else {
191962306a36Sopenharmony_ci		/* RESET_MSG or ACTIVATE_MSG */
192062306a36Sopenharmony_ci		if (mtyp == ACTIVATE_MSG) {
192162306a36Sopenharmony_ci			msg_set_dest_session_valid(hdr, 1);
192262306a36Sopenharmony_ci			msg_set_dest_session(hdr, l->peer_session);
192362306a36Sopenharmony_ci		}
192462306a36Sopenharmony_ci		msg_set_max_pkt(hdr, l->advertised_mtu);
192562306a36Sopenharmony_ci		strcpy(data, l->if_name);
192662306a36Sopenharmony_ci		msg_set_size(hdr, INT_H_SIZE + TIPC_MAX_IF_NAME);
192762306a36Sopenharmony_ci		skb_trim(skb, INT_H_SIZE + TIPC_MAX_IF_NAME);
192862306a36Sopenharmony_ci	}
192962306a36Sopenharmony_ci	if (probe)
193062306a36Sopenharmony_ci		l->stats.sent_probes++;
193162306a36Sopenharmony_ci	if (rcvgap)
193262306a36Sopenharmony_ci		l->stats.sent_nacks++;
193362306a36Sopenharmony_ci	if (bc_rcvgap)
193462306a36Sopenharmony_ci		bcl->stats.sent_nacks++;
193562306a36Sopenharmony_ci	skb->priority = TC_PRIO_CONTROL;
193662306a36Sopenharmony_ci	__skb_queue_tail(xmitq, skb);
193762306a36Sopenharmony_ci	trace_tipc_proto_build(skb, false, l->name);
193862306a36Sopenharmony_ci}
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_civoid tipc_link_create_dummy_tnl_msg(struct tipc_link *l,
194162306a36Sopenharmony_ci				    struct sk_buff_head *xmitq)
194262306a36Sopenharmony_ci{
194362306a36Sopenharmony_ci	u32 onode = tipc_own_addr(l->net);
194462306a36Sopenharmony_ci	struct tipc_msg *hdr, *ihdr;
194562306a36Sopenharmony_ci	struct sk_buff_head tnlq;
194662306a36Sopenharmony_ci	struct sk_buff *skb;
194762306a36Sopenharmony_ci	u32 dnode = l->addr;
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	__skb_queue_head_init(&tnlq);
195062306a36Sopenharmony_ci	skb = tipc_msg_create(TUNNEL_PROTOCOL, FAILOVER_MSG,
195162306a36Sopenharmony_ci			      INT_H_SIZE, BASIC_H_SIZE,
195262306a36Sopenharmony_ci			      dnode, onode, 0, 0, 0);
195362306a36Sopenharmony_ci	if (!skb) {
195462306a36Sopenharmony_ci		pr_warn("%sunable to create tunnel packet\n", link_co_err);
195562306a36Sopenharmony_ci		return;
195662306a36Sopenharmony_ci	}
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	hdr = buf_msg(skb);
195962306a36Sopenharmony_ci	msg_set_msgcnt(hdr, 1);
196062306a36Sopenharmony_ci	msg_set_bearer_id(hdr, l->peer_bearer_id);
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	ihdr = (struct tipc_msg *)msg_data(hdr);
196362306a36Sopenharmony_ci	tipc_msg_init(onode, ihdr, TIPC_LOW_IMPORTANCE, TIPC_DIRECT_MSG,
196462306a36Sopenharmony_ci		      BASIC_H_SIZE, dnode);
196562306a36Sopenharmony_ci	msg_set_errcode(ihdr, TIPC_ERR_NO_PORT);
196662306a36Sopenharmony_ci	__skb_queue_tail(&tnlq, skb);
196762306a36Sopenharmony_ci	tipc_link_xmit(l, &tnlq, xmitq);
196862306a36Sopenharmony_ci}
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci/* tipc_link_tnl_prepare(): prepare and return a list of tunnel packets
197162306a36Sopenharmony_ci * with contents of the link's transmit and backlog queues.
197262306a36Sopenharmony_ci */
197362306a36Sopenharmony_civoid tipc_link_tnl_prepare(struct tipc_link *l, struct tipc_link *tnl,
197462306a36Sopenharmony_ci			   int mtyp, struct sk_buff_head *xmitq)
197562306a36Sopenharmony_ci{
197662306a36Sopenharmony_ci	struct sk_buff_head *fdefq = &tnl->failover_deferdq;
197762306a36Sopenharmony_ci	struct sk_buff *skb, *tnlskb;
197862306a36Sopenharmony_ci	struct tipc_msg *hdr, tnlhdr;
197962306a36Sopenharmony_ci	struct sk_buff_head *queue = &l->transmq;
198062306a36Sopenharmony_ci	struct sk_buff_head tmpxq, tnlq, frags;
198162306a36Sopenharmony_ci	u16 pktlen, pktcnt, seqno = l->snd_nxt;
198262306a36Sopenharmony_ci	bool pktcnt_need_update = false;
198362306a36Sopenharmony_ci	u16 syncpt;
198462306a36Sopenharmony_ci	int rc;
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci	if (!tnl)
198762306a36Sopenharmony_ci		return;
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	__skb_queue_head_init(&tnlq);
199062306a36Sopenharmony_ci	/* Link Synching:
199162306a36Sopenharmony_ci	 * From now on, send only one single ("dummy") SYNCH message
199262306a36Sopenharmony_ci	 * to peer. The SYNCH message does not contain any data, just
199362306a36Sopenharmony_ci	 * a header conveying the synch point to the peer.
199462306a36Sopenharmony_ci	 */
199562306a36Sopenharmony_ci	if (mtyp == SYNCH_MSG && (tnl->peer_caps & TIPC_TUNNEL_ENHANCED)) {
199662306a36Sopenharmony_ci		tnlskb = tipc_msg_create(TUNNEL_PROTOCOL, SYNCH_MSG,
199762306a36Sopenharmony_ci					 INT_H_SIZE, 0, l->addr,
199862306a36Sopenharmony_ci					 tipc_own_addr(l->net),
199962306a36Sopenharmony_ci					 0, 0, 0);
200062306a36Sopenharmony_ci		if (!tnlskb) {
200162306a36Sopenharmony_ci			pr_warn("%sunable to create dummy SYNCH_MSG\n",
200262306a36Sopenharmony_ci				link_co_err);
200362306a36Sopenharmony_ci			return;
200462306a36Sopenharmony_ci		}
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci		hdr = buf_msg(tnlskb);
200762306a36Sopenharmony_ci		syncpt = l->snd_nxt + skb_queue_len(&l->backlogq) - 1;
200862306a36Sopenharmony_ci		msg_set_syncpt(hdr, syncpt);
200962306a36Sopenharmony_ci		msg_set_bearer_id(hdr, l->peer_bearer_id);
201062306a36Sopenharmony_ci		__skb_queue_tail(&tnlq, tnlskb);
201162306a36Sopenharmony_ci		tipc_link_xmit(tnl, &tnlq, xmitq);
201262306a36Sopenharmony_ci		return;
201362306a36Sopenharmony_ci	}
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci	__skb_queue_head_init(&tmpxq);
201662306a36Sopenharmony_ci	__skb_queue_head_init(&frags);
201762306a36Sopenharmony_ci	/* At least one packet required for safe algorithm => add dummy */
201862306a36Sopenharmony_ci	skb = tipc_msg_create(TIPC_LOW_IMPORTANCE, TIPC_DIRECT_MSG,
201962306a36Sopenharmony_ci			      BASIC_H_SIZE, 0, l->addr, tipc_own_addr(l->net),
202062306a36Sopenharmony_ci			      0, 0, TIPC_ERR_NO_PORT);
202162306a36Sopenharmony_ci	if (!skb) {
202262306a36Sopenharmony_ci		pr_warn("%sunable to create tunnel packet\n", link_co_err);
202362306a36Sopenharmony_ci		return;
202462306a36Sopenharmony_ci	}
202562306a36Sopenharmony_ci	__skb_queue_tail(&tnlq, skb);
202662306a36Sopenharmony_ci	tipc_link_xmit(l, &tnlq, &tmpxq);
202762306a36Sopenharmony_ci	__skb_queue_purge(&tmpxq);
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	/* Initialize reusable tunnel packet header */
203062306a36Sopenharmony_ci	tipc_msg_init(tipc_own_addr(l->net), &tnlhdr, TUNNEL_PROTOCOL,
203162306a36Sopenharmony_ci		      mtyp, INT_H_SIZE, l->addr);
203262306a36Sopenharmony_ci	if (mtyp == SYNCH_MSG)
203362306a36Sopenharmony_ci		pktcnt = l->snd_nxt - buf_seqno(skb_peek(&l->transmq));
203462306a36Sopenharmony_ci	else
203562306a36Sopenharmony_ci		pktcnt = skb_queue_len(&l->transmq);
203662306a36Sopenharmony_ci	pktcnt += skb_queue_len(&l->backlogq);
203762306a36Sopenharmony_ci	msg_set_msgcnt(&tnlhdr, pktcnt);
203862306a36Sopenharmony_ci	msg_set_bearer_id(&tnlhdr, l->peer_bearer_id);
203962306a36Sopenharmony_citnl:
204062306a36Sopenharmony_ci	/* Wrap each packet into a tunnel packet */
204162306a36Sopenharmony_ci	skb_queue_walk(queue, skb) {
204262306a36Sopenharmony_ci		hdr = buf_msg(skb);
204362306a36Sopenharmony_ci		if (queue == &l->backlogq)
204462306a36Sopenharmony_ci			msg_set_seqno(hdr, seqno++);
204562306a36Sopenharmony_ci		pktlen = msg_size(hdr);
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci		/* Tunnel link MTU is not large enough? This could be
204862306a36Sopenharmony_ci		 * due to:
204962306a36Sopenharmony_ci		 * 1) Link MTU has just changed or set differently;
205062306a36Sopenharmony_ci		 * 2) Or FAILOVER on the top of a SYNCH message
205162306a36Sopenharmony_ci		 *
205262306a36Sopenharmony_ci		 * The 2nd case should not happen if peer supports
205362306a36Sopenharmony_ci		 * TIPC_TUNNEL_ENHANCED
205462306a36Sopenharmony_ci		 */
205562306a36Sopenharmony_ci		if (pktlen > tnl->mtu - INT_H_SIZE) {
205662306a36Sopenharmony_ci			if (mtyp == FAILOVER_MSG &&
205762306a36Sopenharmony_ci			    (tnl->peer_caps & TIPC_TUNNEL_ENHANCED)) {
205862306a36Sopenharmony_ci				rc = tipc_msg_fragment(skb, &tnlhdr, tnl->mtu,
205962306a36Sopenharmony_ci						       &frags);
206062306a36Sopenharmony_ci				if (rc) {
206162306a36Sopenharmony_ci					pr_warn("%sunable to frag msg: rc %d\n",
206262306a36Sopenharmony_ci						link_co_err, rc);
206362306a36Sopenharmony_ci					return;
206462306a36Sopenharmony_ci				}
206562306a36Sopenharmony_ci				pktcnt += skb_queue_len(&frags) - 1;
206662306a36Sopenharmony_ci				pktcnt_need_update = true;
206762306a36Sopenharmony_ci				skb_queue_splice_tail_init(&frags, &tnlq);
206862306a36Sopenharmony_ci				continue;
206962306a36Sopenharmony_ci			}
207062306a36Sopenharmony_ci			/* Unluckily, peer doesn't have TIPC_TUNNEL_ENHANCED
207162306a36Sopenharmony_ci			 * => Just warn it and return!
207262306a36Sopenharmony_ci			 */
207362306a36Sopenharmony_ci			pr_warn_ratelimited("%stoo large msg <%d, %d>: %d!\n",
207462306a36Sopenharmony_ci					    link_co_err, msg_user(hdr),
207562306a36Sopenharmony_ci					    msg_type(hdr), msg_size(hdr));
207662306a36Sopenharmony_ci			return;
207762306a36Sopenharmony_ci		}
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci		msg_set_size(&tnlhdr, pktlen + INT_H_SIZE);
208062306a36Sopenharmony_ci		tnlskb = tipc_buf_acquire(pktlen + INT_H_SIZE, GFP_ATOMIC);
208162306a36Sopenharmony_ci		if (!tnlskb) {
208262306a36Sopenharmony_ci			pr_warn("%sunable to send packet\n", link_co_err);
208362306a36Sopenharmony_ci			return;
208462306a36Sopenharmony_ci		}
208562306a36Sopenharmony_ci		skb_copy_to_linear_data(tnlskb, &tnlhdr, INT_H_SIZE);
208662306a36Sopenharmony_ci		skb_copy_to_linear_data_offset(tnlskb, INT_H_SIZE, hdr, pktlen);
208762306a36Sopenharmony_ci		__skb_queue_tail(&tnlq, tnlskb);
208862306a36Sopenharmony_ci	}
208962306a36Sopenharmony_ci	if (queue != &l->backlogq) {
209062306a36Sopenharmony_ci		queue = &l->backlogq;
209162306a36Sopenharmony_ci		goto tnl;
209262306a36Sopenharmony_ci	}
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci	if (pktcnt_need_update)
209562306a36Sopenharmony_ci		skb_queue_walk(&tnlq, skb) {
209662306a36Sopenharmony_ci			hdr = buf_msg(skb);
209762306a36Sopenharmony_ci			msg_set_msgcnt(hdr, pktcnt);
209862306a36Sopenharmony_ci		}
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci	tipc_link_xmit(tnl, &tnlq, xmitq);
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_ci	if (mtyp == FAILOVER_MSG) {
210362306a36Sopenharmony_ci		tnl->drop_point = l->rcv_nxt;
210462306a36Sopenharmony_ci		tnl->failover_reasm_skb = l->reasm_buf;
210562306a36Sopenharmony_ci		l->reasm_buf = NULL;
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci		/* Failover the link's deferdq */
210862306a36Sopenharmony_ci		if (unlikely(!skb_queue_empty(fdefq))) {
210962306a36Sopenharmony_ci			pr_warn("Link failover deferdq not empty: %d!\n",
211062306a36Sopenharmony_ci				skb_queue_len(fdefq));
211162306a36Sopenharmony_ci			__skb_queue_purge(fdefq);
211262306a36Sopenharmony_ci		}
211362306a36Sopenharmony_ci		skb_queue_splice_init(&l->deferdq, fdefq);
211462306a36Sopenharmony_ci	}
211562306a36Sopenharmony_ci}
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci/**
211862306a36Sopenharmony_ci * tipc_link_failover_prepare() - prepare tnl for link failover
211962306a36Sopenharmony_ci *
212062306a36Sopenharmony_ci * This is a special version of the precursor - tipc_link_tnl_prepare(),
212162306a36Sopenharmony_ci * see the tipc_node_link_failover() for details
212262306a36Sopenharmony_ci *
212362306a36Sopenharmony_ci * @l: failover link
212462306a36Sopenharmony_ci * @tnl: tunnel link
212562306a36Sopenharmony_ci * @xmitq: queue for messages to be xmited
212662306a36Sopenharmony_ci */
212762306a36Sopenharmony_civoid tipc_link_failover_prepare(struct tipc_link *l, struct tipc_link *tnl,
212862306a36Sopenharmony_ci				struct sk_buff_head *xmitq)
212962306a36Sopenharmony_ci{
213062306a36Sopenharmony_ci	struct sk_buff_head *fdefq = &tnl->failover_deferdq;
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	tipc_link_create_dummy_tnl_msg(tnl, xmitq);
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci	/* This failover link endpoint was never established before,
213562306a36Sopenharmony_ci	 * so it has not received anything from peer.
213662306a36Sopenharmony_ci	 * Otherwise, it must be a normal failover situation or the
213762306a36Sopenharmony_ci	 * node has entered SELF_DOWN_PEER_LEAVING and both peer nodes
213862306a36Sopenharmony_ci	 * would have to start over from scratch instead.
213962306a36Sopenharmony_ci	 */
214062306a36Sopenharmony_ci	tnl->drop_point = 1;
214162306a36Sopenharmony_ci	tnl->failover_reasm_skb = NULL;
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	/* Initiate the link's failover deferdq */
214462306a36Sopenharmony_ci	if (unlikely(!skb_queue_empty(fdefq))) {
214562306a36Sopenharmony_ci		pr_warn("Link failover deferdq not empty: %d!\n",
214662306a36Sopenharmony_ci			skb_queue_len(fdefq));
214762306a36Sopenharmony_ci		__skb_queue_purge(fdefq);
214862306a36Sopenharmony_ci	}
214962306a36Sopenharmony_ci}
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci/* tipc_link_validate_msg(): validate message against current link state
215262306a36Sopenharmony_ci * Returns true if message should be accepted, otherwise false
215362306a36Sopenharmony_ci */
215462306a36Sopenharmony_cibool tipc_link_validate_msg(struct tipc_link *l, struct tipc_msg *hdr)
215562306a36Sopenharmony_ci{
215662306a36Sopenharmony_ci	u16 curr_session = l->peer_session;
215762306a36Sopenharmony_ci	u16 session = msg_session(hdr);
215862306a36Sopenharmony_ci	int mtyp = msg_type(hdr);
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci	if (msg_user(hdr) != LINK_PROTOCOL)
216162306a36Sopenharmony_ci		return true;
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci	switch (mtyp) {
216462306a36Sopenharmony_ci	case RESET_MSG:
216562306a36Sopenharmony_ci		if (!l->in_session)
216662306a36Sopenharmony_ci			return true;
216762306a36Sopenharmony_ci		/* Accept only RESET with new session number */
216862306a36Sopenharmony_ci		return more(session, curr_session);
216962306a36Sopenharmony_ci	case ACTIVATE_MSG:
217062306a36Sopenharmony_ci		if (!l->in_session)
217162306a36Sopenharmony_ci			return true;
217262306a36Sopenharmony_ci		/* Accept only ACTIVATE with new or current session number */
217362306a36Sopenharmony_ci		return !less(session, curr_session);
217462306a36Sopenharmony_ci	case STATE_MSG:
217562306a36Sopenharmony_ci		/* Accept only STATE with current session number */
217662306a36Sopenharmony_ci		if (!l->in_session)
217762306a36Sopenharmony_ci			return false;
217862306a36Sopenharmony_ci		if (session != curr_session)
217962306a36Sopenharmony_ci			return false;
218062306a36Sopenharmony_ci		/* Extra sanity check */
218162306a36Sopenharmony_ci		if (!link_is_up(l) && msg_ack(hdr))
218262306a36Sopenharmony_ci			return false;
218362306a36Sopenharmony_ci		if (!(l->peer_caps & TIPC_LINK_PROTO_SEQNO))
218462306a36Sopenharmony_ci			return true;
218562306a36Sopenharmony_ci		/* Accept only STATE with new sequence number */
218662306a36Sopenharmony_ci		return !less(msg_seqno(hdr), l->rcv_nxt_state);
218762306a36Sopenharmony_ci	default:
218862306a36Sopenharmony_ci		return false;
218962306a36Sopenharmony_ci	}
219062306a36Sopenharmony_ci}
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci/* tipc_link_proto_rcv(): receive link level protocol message :
219362306a36Sopenharmony_ci * Note that network plane id propagates through the network, and may
219462306a36Sopenharmony_ci * change at any time. The node with lowest numerical id determines
219562306a36Sopenharmony_ci * network plane
219662306a36Sopenharmony_ci */
219762306a36Sopenharmony_cistatic int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
219862306a36Sopenharmony_ci			       struct sk_buff_head *xmitq)
219962306a36Sopenharmony_ci{
220062306a36Sopenharmony_ci	struct tipc_msg *hdr = buf_msg(skb);
220162306a36Sopenharmony_ci	struct tipc_gap_ack_blks *ga = NULL;
220262306a36Sopenharmony_ci	bool reply = msg_probe(hdr), retransmitted = false;
220362306a36Sopenharmony_ci	u32 dlen = msg_data_sz(hdr), glen = 0, msg_max;
220462306a36Sopenharmony_ci	u16 peers_snd_nxt =  msg_next_sent(hdr);
220562306a36Sopenharmony_ci	u16 peers_tol = msg_link_tolerance(hdr);
220662306a36Sopenharmony_ci	u16 peers_prio = msg_linkprio(hdr);
220762306a36Sopenharmony_ci	u16 gap = msg_seq_gap(hdr);
220862306a36Sopenharmony_ci	u16 ack = msg_ack(hdr);
220962306a36Sopenharmony_ci	u16 rcv_nxt = l->rcv_nxt;
221062306a36Sopenharmony_ci	u16 rcvgap = 0;
221162306a36Sopenharmony_ci	int mtyp = msg_type(hdr);
221262306a36Sopenharmony_ci	int rc = 0, released;
221362306a36Sopenharmony_ci	char *if_name;
221462306a36Sopenharmony_ci	void *data;
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	trace_tipc_proto_rcv(skb, false, l->name);
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci	if (dlen > U16_MAX)
221962306a36Sopenharmony_ci		goto exit;
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	if (tipc_link_is_blocked(l) || !xmitq)
222262306a36Sopenharmony_ci		goto exit;
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci	if (tipc_own_addr(l->net) > msg_prevnode(hdr))
222562306a36Sopenharmony_ci		l->net_plane = msg_net_plane(hdr);
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	if (skb_linearize(skb))
222862306a36Sopenharmony_ci		goto exit;
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci	hdr = buf_msg(skb);
223162306a36Sopenharmony_ci	data = msg_data(hdr);
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_ci	if (!tipc_link_validate_msg(l, hdr)) {
223462306a36Sopenharmony_ci		trace_tipc_skb_dump(skb, false, "PROTO invalid (1)!");
223562306a36Sopenharmony_ci		trace_tipc_link_dump(l, TIPC_DUMP_NONE, "PROTO invalid (1)!");
223662306a36Sopenharmony_ci		goto exit;
223762306a36Sopenharmony_ci	}
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_ci	switch (mtyp) {
224062306a36Sopenharmony_ci	case RESET_MSG:
224162306a36Sopenharmony_ci	case ACTIVATE_MSG:
224262306a36Sopenharmony_ci		msg_max = msg_max_pkt(hdr);
224362306a36Sopenharmony_ci		if (msg_max < tipc_bearer_min_mtu(l->net, l->bearer_id))
224462306a36Sopenharmony_ci			break;
224562306a36Sopenharmony_ci		/* Complete own link name with peer's interface name */
224662306a36Sopenharmony_ci		if_name =  strrchr(l->name, ':') + 1;
224762306a36Sopenharmony_ci		if (sizeof(l->name) - (if_name - l->name) <= TIPC_MAX_IF_NAME)
224862306a36Sopenharmony_ci			break;
224962306a36Sopenharmony_ci		if (msg_data_sz(hdr) < TIPC_MAX_IF_NAME)
225062306a36Sopenharmony_ci			break;
225162306a36Sopenharmony_ci		strncpy(if_name, data, TIPC_MAX_IF_NAME);
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci		/* Update own tolerance if peer indicates a non-zero value */
225462306a36Sopenharmony_ci		if (tipc_in_range(peers_tol, TIPC_MIN_LINK_TOL, TIPC_MAX_LINK_TOL)) {
225562306a36Sopenharmony_ci			l->tolerance = peers_tol;
225662306a36Sopenharmony_ci			l->bc_rcvlink->tolerance = peers_tol;
225762306a36Sopenharmony_ci		}
225862306a36Sopenharmony_ci		/* Update own priority if peer's priority is higher */
225962306a36Sopenharmony_ci		if (tipc_in_range(peers_prio, l->priority + 1, TIPC_MAX_LINK_PRI))
226062306a36Sopenharmony_ci			l->priority = peers_prio;
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci		/* If peer is going down we want full re-establish cycle */
226362306a36Sopenharmony_ci		if (msg_peer_stopping(hdr)) {
226462306a36Sopenharmony_ci			rc = tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
226562306a36Sopenharmony_ci			break;
226662306a36Sopenharmony_ci		}
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci		/* If this endpoint was re-created while peer was ESTABLISHING
226962306a36Sopenharmony_ci		 * it doesn't know current session number. Force re-synch.
227062306a36Sopenharmony_ci		 */
227162306a36Sopenharmony_ci		if (mtyp == ACTIVATE_MSG && msg_dest_session_valid(hdr) &&
227262306a36Sopenharmony_ci		    l->session != msg_dest_session(hdr)) {
227362306a36Sopenharmony_ci			if (less(l->session, msg_dest_session(hdr)))
227462306a36Sopenharmony_ci				l->session = msg_dest_session(hdr) + 1;
227562306a36Sopenharmony_ci			break;
227662306a36Sopenharmony_ci		}
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci		/* ACTIVATE_MSG serves as PEER_RESET if link is already down */
227962306a36Sopenharmony_ci		if (mtyp == RESET_MSG || !link_is_up(l))
228062306a36Sopenharmony_ci			rc = tipc_link_fsm_evt(l, LINK_PEER_RESET_EVT);
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ci		/* ACTIVATE_MSG takes up link if it was already locally reset */
228362306a36Sopenharmony_ci		if (mtyp == ACTIVATE_MSG && l->state == LINK_ESTABLISHING)
228462306a36Sopenharmony_ci			rc = TIPC_LINK_UP_EVT;
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci		l->peer_session = msg_session(hdr);
228762306a36Sopenharmony_ci		l->in_session = true;
228862306a36Sopenharmony_ci		l->peer_bearer_id = msg_bearer_id(hdr);
228962306a36Sopenharmony_ci		if (l->mtu > msg_max)
229062306a36Sopenharmony_ci			l->mtu = msg_max;
229162306a36Sopenharmony_ci		break;
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci	case STATE_MSG:
229462306a36Sopenharmony_ci		/* Validate Gap ACK blocks, drop if invalid */
229562306a36Sopenharmony_ci		glen = tipc_get_gap_ack_blks(&ga, l, hdr, true);
229662306a36Sopenharmony_ci		if (glen > dlen)
229762306a36Sopenharmony_ci			break;
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci		l->rcv_nxt_state = msg_seqno(hdr) + 1;
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci		/* Update own tolerance if peer indicates a non-zero value */
230262306a36Sopenharmony_ci		if (tipc_in_range(peers_tol, TIPC_MIN_LINK_TOL, TIPC_MAX_LINK_TOL)) {
230362306a36Sopenharmony_ci			l->tolerance = peers_tol;
230462306a36Sopenharmony_ci			l->bc_rcvlink->tolerance = peers_tol;
230562306a36Sopenharmony_ci		}
230662306a36Sopenharmony_ci		/* Update own prio if peer indicates a different value */
230762306a36Sopenharmony_ci		if ((peers_prio != l->priority) &&
230862306a36Sopenharmony_ci		    tipc_in_range(peers_prio, 1, TIPC_MAX_LINK_PRI)) {
230962306a36Sopenharmony_ci			l->priority = peers_prio;
231062306a36Sopenharmony_ci			rc = tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
231162306a36Sopenharmony_ci		}
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci		l->silent_intv_cnt = 0;
231462306a36Sopenharmony_ci		l->stats.recv_states++;
231562306a36Sopenharmony_ci		if (msg_probe(hdr))
231662306a36Sopenharmony_ci			l->stats.recv_probes++;
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci		if (!link_is_up(l)) {
231962306a36Sopenharmony_ci			if (l->state == LINK_ESTABLISHING)
232062306a36Sopenharmony_ci				rc = TIPC_LINK_UP_EVT;
232162306a36Sopenharmony_ci			break;
232262306a36Sopenharmony_ci		}
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci		tipc_mon_rcv(l->net, data + glen, dlen - glen, l->addr,
232562306a36Sopenharmony_ci			     &l->mon_state, l->bearer_id);
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_ci		/* Send NACK if peer has sent pkts we haven't received yet */
232862306a36Sopenharmony_ci		if ((reply || msg_is_keepalive(hdr)) &&
232962306a36Sopenharmony_ci		    more(peers_snd_nxt, rcv_nxt) &&
233062306a36Sopenharmony_ci		    !tipc_link_is_synching(l) &&
233162306a36Sopenharmony_ci		    skb_queue_empty(&l->deferdq))
233262306a36Sopenharmony_ci			rcvgap = peers_snd_nxt - l->rcv_nxt;
233362306a36Sopenharmony_ci		if (rcvgap || reply)
233462306a36Sopenharmony_ci			tipc_link_build_proto_msg(l, STATE_MSG, 0, reply,
233562306a36Sopenharmony_ci						  rcvgap, 0, 0, xmitq);
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_ci		released = tipc_link_advance_transmq(l, l, ack, gap, ga, xmitq,
233862306a36Sopenharmony_ci						     &retransmitted, &rc);
233962306a36Sopenharmony_ci		if (gap)
234062306a36Sopenharmony_ci			l->stats.recv_nacks++;
234162306a36Sopenharmony_ci		if (released || retransmitted)
234262306a36Sopenharmony_ci			tipc_link_update_cwin(l, released, retransmitted);
234362306a36Sopenharmony_ci		if (released)
234462306a36Sopenharmony_ci			tipc_link_advance_backlog(l, xmitq);
234562306a36Sopenharmony_ci		if (unlikely(!skb_queue_empty(&l->wakeupq)))
234662306a36Sopenharmony_ci			link_prepare_wakeup(l);
234762306a36Sopenharmony_ci	}
234862306a36Sopenharmony_ciexit:
234962306a36Sopenharmony_ci	kfree_skb(skb);
235062306a36Sopenharmony_ci	return rc;
235162306a36Sopenharmony_ci}
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci/* tipc_link_build_bc_proto_msg() - create broadcast protocol message
235462306a36Sopenharmony_ci */
235562306a36Sopenharmony_cistatic bool tipc_link_build_bc_proto_msg(struct tipc_link *l, bool bcast,
235662306a36Sopenharmony_ci					 u16 peers_snd_nxt,
235762306a36Sopenharmony_ci					 struct sk_buff_head *xmitq)
235862306a36Sopenharmony_ci{
235962306a36Sopenharmony_ci	struct sk_buff *skb;
236062306a36Sopenharmony_ci	struct tipc_msg *hdr;
236162306a36Sopenharmony_ci	struct sk_buff *dfrd_skb = skb_peek(&l->deferdq);
236262306a36Sopenharmony_ci	u16 ack = l->rcv_nxt - 1;
236362306a36Sopenharmony_ci	u16 gap_to = peers_snd_nxt - 1;
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	skb = tipc_msg_create(BCAST_PROTOCOL, STATE_MSG, INT_H_SIZE,
236662306a36Sopenharmony_ci			      0, l->addr, tipc_own_addr(l->net), 0, 0, 0);
236762306a36Sopenharmony_ci	if (!skb)
236862306a36Sopenharmony_ci		return false;
236962306a36Sopenharmony_ci	hdr = buf_msg(skb);
237062306a36Sopenharmony_ci	msg_set_last_bcast(hdr, l->bc_sndlink->snd_nxt - 1);
237162306a36Sopenharmony_ci	msg_set_bcast_ack(hdr, ack);
237262306a36Sopenharmony_ci	msg_set_bcgap_after(hdr, ack);
237362306a36Sopenharmony_ci	if (dfrd_skb)
237462306a36Sopenharmony_ci		gap_to = buf_seqno(dfrd_skb) - 1;
237562306a36Sopenharmony_ci	msg_set_bcgap_to(hdr, gap_to);
237662306a36Sopenharmony_ci	msg_set_non_seq(hdr, bcast);
237762306a36Sopenharmony_ci	__skb_queue_tail(xmitq, skb);
237862306a36Sopenharmony_ci	return true;
237962306a36Sopenharmony_ci}
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci/* tipc_link_build_bc_init_msg() - synchronize broadcast link endpoints.
238262306a36Sopenharmony_ci *
238362306a36Sopenharmony_ci * Give a newly added peer node the sequence number where it should
238462306a36Sopenharmony_ci * start receiving and acking broadcast packets.
238562306a36Sopenharmony_ci */
238662306a36Sopenharmony_cistatic void tipc_link_build_bc_init_msg(struct tipc_link *l,
238762306a36Sopenharmony_ci					struct sk_buff_head *xmitq)
238862306a36Sopenharmony_ci{
238962306a36Sopenharmony_ci	struct sk_buff_head list;
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci	__skb_queue_head_init(&list);
239262306a36Sopenharmony_ci	if (!tipc_link_build_bc_proto_msg(l->bc_rcvlink, false, 0, &list))
239362306a36Sopenharmony_ci		return;
239462306a36Sopenharmony_ci	msg_set_bc_ack_invalid(buf_msg(skb_peek(&list)), true);
239562306a36Sopenharmony_ci	tipc_link_xmit(l, &list, xmitq);
239662306a36Sopenharmony_ci}
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci/* tipc_link_bc_init_rcv - receive initial broadcast synch data from peer
239962306a36Sopenharmony_ci */
240062306a36Sopenharmony_civoid tipc_link_bc_init_rcv(struct tipc_link *l, struct tipc_msg *hdr)
240162306a36Sopenharmony_ci{
240262306a36Sopenharmony_ci	int mtyp = msg_type(hdr);
240362306a36Sopenharmony_ci	u16 peers_snd_nxt = msg_bc_snd_nxt(hdr);
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_ci	if (link_is_up(l))
240662306a36Sopenharmony_ci		return;
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_ci	if (msg_user(hdr) == BCAST_PROTOCOL) {
240962306a36Sopenharmony_ci		l->rcv_nxt = peers_snd_nxt;
241062306a36Sopenharmony_ci		l->state = LINK_ESTABLISHED;
241162306a36Sopenharmony_ci		return;
241262306a36Sopenharmony_ci	}
241362306a36Sopenharmony_ci
241462306a36Sopenharmony_ci	if (l->peer_caps & TIPC_BCAST_SYNCH)
241562306a36Sopenharmony_ci		return;
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci	if (msg_peer_node_is_up(hdr))
241862306a36Sopenharmony_ci		return;
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	/* Compatibility: accept older, less safe initial synch data */
242162306a36Sopenharmony_ci	if ((mtyp == RESET_MSG) || (mtyp == ACTIVATE_MSG))
242262306a36Sopenharmony_ci		l->rcv_nxt = peers_snd_nxt;
242362306a36Sopenharmony_ci}
242462306a36Sopenharmony_ci
242562306a36Sopenharmony_ci/* tipc_link_bc_sync_rcv - update rcv link according to peer's send state
242662306a36Sopenharmony_ci */
242762306a36Sopenharmony_ciint tipc_link_bc_sync_rcv(struct tipc_link *l, struct tipc_msg *hdr,
242862306a36Sopenharmony_ci			  struct sk_buff_head *xmitq)
242962306a36Sopenharmony_ci{
243062306a36Sopenharmony_ci	u16 peers_snd_nxt = msg_bc_snd_nxt(hdr);
243162306a36Sopenharmony_ci	int rc = 0;
243262306a36Sopenharmony_ci
243362306a36Sopenharmony_ci	if (!link_is_up(l))
243462306a36Sopenharmony_ci		return rc;
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_ci	if (!msg_peer_node_is_up(hdr))
243762306a36Sopenharmony_ci		return rc;
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci	/* Open when peer acknowledges our bcast init msg (pkt #1) */
244062306a36Sopenharmony_ci	if (msg_ack(hdr))
244162306a36Sopenharmony_ci		l->bc_peer_is_up = true;
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci	if (!l->bc_peer_is_up)
244462306a36Sopenharmony_ci		return rc;
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_ci	/* Ignore if peers_snd_nxt goes beyond receive window */
244762306a36Sopenharmony_ci	if (more(peers_snd_nxt, l->rcv_nxt + l->window))
244862306a36Sopenharmony_ci		return rc;
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	l->snd_nxt = peers_snd_nxt;
245162306a36Sopenharmony_ci	if (link_bc_rcv_gap(l))
245262306a36Sopenharmony_ci		rc |= TIPC_LINK_SND_STATE;
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci	/* Return now if sender supports nack via STATE messages */
245562306a36Sopenharmony_ci	if (l->peer_caps & TIPC_BCAST_STATE_NACK)
245662306a36Sopenharmony_ci		return rc;
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	/* Otherwise, be backwards compatible */
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	if (!more(peers_snd_nxt, l->rcv_nxt)) {
246162306a36Sopenharmony_ci		l->nack_state = BC_NACK_SND_CONDITIONAL;
246262306a36Sopenharmony_ci		return 0;
246362306a36Sopenharmony_ci	}
246462306a36Sopenharmony_ci
246562306a36Sopenharmony_ci	/* Don't NACK if one was recently sent or peeked */
246662306a36Sopenharmony_ci	if (l->nack_state == BC_NACK_SND_SUPPRESS) {
246762306a36Sopenharmony_ci		l->nack_state = BC_NACK_SND_UNCONDITIONAL;
246862306a36Sopenharmony_ci		return 0;
246962306a36Sopenharmony_ci	}
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci	/* Conditionally delay NACK sending until next synch rcv */
247262306a36Sopenharmony_ci	if (l->nack_state == BC_NACK_SND_CONDITIONAL) {
247362306a36Sopenharmony_ci		l->nack_state = BC_NACK_SND_UNCONDITIONAL;
247462306a36Sopenharmony_ci		if ((peers_snd_nxt - l->rcv_nxt) < TIPC_MIN_LINK_WIN)
247562306a36Sopenharmony_ci			return 0;
247662306a36Sopenharmony_ci	}
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci	/* Send NACK now but suppress next one */
247962306a36Sopenharmony_ci	tipc_link_build_bc_proto_msg(l, true, peers_snd_nxt, xmitq);
248062306a36Sopenharmony_ci	l->nack_state = BC_NACK_SND_SUPPRESS;
248162306a36Sopenharmony_ci	return 0;
248262306a36Sopenharmony_ci}
248362306a36Sopenharmony_ci
248462306a36Sopenharmony_ciint tipc_link_bc_ack_rcv(struct tipc_link *r, u16 acked, u16 gap,
248562306a36Sopenharmony_ci			 struct tipc_gap_ack_blks *ga,
248662306a36Sopenharmony_ci			 struct sk_buff_head *xmitq,
248762306a36Sopenharmony_ci			 struct sk_buff_head *retrq)
248862306a36Sopenharmony_ci{
248962306a36Sopenharmony_ci	struct tipc_link *l = r->bc_sndlink;
249062306a36Sopenharmony_ci	bool unused = false;
249162306a36Sopenharmony_ci	int rc = 0;
249262306a36Sopenharmony_ci
249362306a36Sopenharmony_ci	if (!link_is_up(r) || !r->bc_peer_is_up)
249462306a36Sopenharmony_ci		return 0;
249562306a36Sopenharmony_ci
249662306a36Sopenharmony_ci	if (gap) {
249762306a36Sopenharmony_ci		l->stats.recv_nacks++;
249862306a36Sopenharmony_ci		r->stats.recv_nacks++;
249962306a36Sopenharmony_ci	}
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_ci	if (less(acked, r->acked) || (acked == r->acked && !gap && !ga))
250262306a36Sopenharmony_ci		return 0;
250362306a36Sopenharmony_ci
250462306a36Sopenharmony_ci	trace_tipc_link_bc_ack(r, acked, gap, &l->transmq);
250562306a36Sopenharmony_ci	tipc_link_advance_transmq(l, r, acked, gap, ga, retrq, &unused, &rc);
250662306a36Sopenharmony_ci
250762306a36Sopenharmony_ci	tipc_link_advance_backlog(l, xmitq);
250862306a36Sopenharmony_ci	if (unlikely(!skb_queue_empty(&l->wakeupq)))
250962306a36Sopenharmony_ci		link_prepare_wakeup(l);
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_ci	return rc;
251262306a36Sopenharmony_ci}
251362306a36Sopenharmony_ci
251462306a36Sopenharmony_ci/* tipc_link_bc_nack_rcv(): receive broadcast nack message
251562306a36Sopenharmony_ci * This function is here for backwards compatibility, since
251662306a36Sopenharmony_ci * no BCAST_PROTOCOL/STATE messages occur from TIPC v2.5.
251762306a36Sopenharmony_ci */
251862306a36Sopenharmony_ciint tipc_link_bc_nack_rcv(struct tipc_link *l, struct sk_buff *skb,
251962306a36Sopenharmony_ci			  struct sk_buff_head *xmitq)
252062306a36Sopenharmony_ci{
252162306a36Sopenharmony_ci	struct tipc_msg *hdr = buf_msg(skb);
252262306a36Sopenharmony_ci	u32 dnode = msg_destnode(hdr);
252362306a36Sopenharmony_ci	int mtyp = msg_type(hdr);
252462306a36Sopenharmony_ci	u16 acked = msg_bcast_ack(hdr);
252562306a36Sopenharmony_ci	u16 from = acked + 1;
252662306a36Sopenharmony_ci	u16 to = msg_bcgap_to(hdr);
252762306a36Sopenharmony_ci	u16 peers_snd_nxt = to + 1;
252862306a36Sopenharmony_ci	int rc = 0;
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci	kfree_skb(skb);
253162306a36Sopenharmony_ci
253262306a36Sopenharmony_ci	if (!tipc_link_is_up(l) || !l->bc_peer_is_up)
253362306a36Sopenharmony_ci		return 0;
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci	if (mtyp != STATE_MSG)
253662306a36Sopenharmony_ci		return 0;
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	if (dnode == tipc_own_addr(l->net)) {
253962306a36Sopenharmony_ci		rc = tipc_link_bc_ack_rcv(l, acked, to - acked, NULL, xmitq,
254062306a36Sopenharmony_ci					  xmitq);
254162306a36Sopenharmony_ci		l->stats.recv_nacks++;
254262306a36Sopenharmony_ci		return rc;
254362306a36Sopenharmony_ci	}
254462306a36Sopenharmony_ci
254562306a36Sopenharmony_ci	/* Msg for other node => suppress own NACK at next sync if applicable */
254662306a36Sopenharmony_ci	if (more(peers_snd_nxt, l->rcv_nxt) && !less(l->rcv_nxt, from))
254762306a36Sopenharmony_ci		l->nack_state = BC_NACK_SND_SUPPRESS;
254862306a36Sopenharmony_ci
254962306a36Sopenharmony_ci	return 0;
255062306a36Sopenharmony_ci}
255162306a36Sopenharmony_ci
255262306a36Sopenharmony_civoid tipc_link_set_queue_limits(struct tipc_link *l, u32 min_win, u32 max_win)
255362306a36Sopenharmony_ci{
255462306a36Sopenharmony_ci	int max_bulk = TIPC_MAX_PUBL / (l->mtu / ITEM_SIZE);
255562306a36Sopenharmony_ci
255662306a36Sopenharmony_ci	l->min_win = min_win;
255762306a36Sopenharmony_ci	l->ssthresh = max_win;
255862306a36Sopenharmony_ci	l->max_win = max_win;
255962306a36Sopenharmony_ci	l->window = min_win;
256062306a36Sopenharmony_ci	l->backlog[TIPC_LOW_IMPORTANCE].limit      = min_win * 2;
256162306a36Sopenharmony_ci	l->backlog[TIPC_MEDIUM_IMPORTANCE].limit   = min_win * 4;
256262306a36Sopenharmony_ci	l->backlog[TIPC_HIGH_IMPORTANCE].limit     = min_win * 6;
256362306a36Sopenharmony_ci	l->backlog[TIPC_CRITICAL_IMPORTANCE].limit = min_win * 8;
256462306a36Sopenharmony_ci	l->backlog[TIPC_SYSTEM_IMPORTANCE].limit   = max_bulk;
256562306a36Sopenharmony_ci}
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_ci/**
256862306a36Sopenharmony_ci * tipc_link_reset_stats - reset link statistics
256962306a36Sopenharmony_ci * @l: pointer to link
257062306a36Sopenharmony_ci */
257162306a36Sopenharmony_civoid tipc_link_reset_stats(struct tipc_link *l)
257262306a36Sopenharmony_ci{
257362306a36Sopenharmony_ci	memset(&l->stats, 0, sizeof(l->stats));
257462306a36Sopenharmony_ci}
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_cistatic void link_print(struct tipc_link *l, const char *str)
257762306a36Sopenharmony_ci{
257862306a36Sopenharmony_ci	struct sk_buff *hskb = skb_peek(&l->transmq);
257962306a36Sopenharmony_ci	u16 head = hskb ? msg_seqno(buf_msg(hskb)) : l->snd_nxt - 1;
258062306a36Sopenharmony_ci	u16 tail = l->snd_nxt - 1;
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci	pr_info("%s Link <%s> state %x\n", str, l->name, l->state);
258362306a36Sopenharmony_ci	pr_info("XMTQ: %u [%u-%u], BKLGQ: %u, SNDNX: %u, RCVNX: %u\n",
258462306a36Sopenharmony_ci		skb_queue_len(&l->transmq), head, tail,
258562306a36Sopenharmony_ci		skb_queue_len(&l->backlogq), l->snd_nxt, l->rcv_nxt);
258662306a36Sopenharmony_ci}
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci/* Parse and validate nested (link) properties valid for media, bearer and link
258962306a36Sopenharmony_ci */
259062306a36Sopenharmony_ciint tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[])
259162306a36Sopenharmony_ci{
259262306a36Sopenharmony_ci	int err;
259362306a36Sopenharmony_ci
259462306a36Sopenharmony_ci	err = nla_parse_nested_deprecated(props, TIPC_NLA_PROP_MAX, prop,
259562306a36Sopenharmony_ci					  tipc_nl_prop_policy, NULL);
259662306a36Sopenharmony_ci	if (err)
259762306a36Sopenharmony_ci		return err;
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_ci	if (props[TIPC_NLA_PROP_PRIO]) {
260062306a36Sopenharmony_ci		u32 prio;
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci		prio = nla_get_u32(props[TIPC_NLA_PROP_PRIO]);
260362306a36Sopenharmony_ci		if (prio > TIPC_MAX_LINK_PRI)
260462306a36Sopenharmony_ci			return -EINVAL;
260562306a36Sopenharmony_ci	}
260662306a36Sopenharmony_ci
260762306a36Sopenharmony_ci	if (props[TIPC_NLA_PROP_TOL]) {
260862306a36Sopenharmony_ci		u32 tol;
260962306a36Sopenharmony_ci
261062306a36Sopenharmony_ci		tol = nla_get_u32(props[TIPC_NLA_PROP_TOL]);
261162306a36Sopenharmony_ci		if ((tol < TIPC_MIN_LINK_TOL) || (tol > TIPC_MAX_LINK_TOL))
261262306a36Sopenharmony_ci			return -EINVAL;
261362306a36Sopenharmony_ci	}
261462306a36Sopenharmony_ci
261562306a36Sopenharmony_ci	if (props[TIPC_NLA_PROP_WIN]) {
261662306a36Sopenharmony_ci		u32 max_win;
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ci		max_win = nla_get_u32(props[TIPC_NLA_PROP_WIN]);
261962306a36Sopenharmony_ci		if (max_win < TIPC_DEF_LINK_WIN || max_win > TIPC_MAX_LINK_WIN)
262062306a36Sopenharmony_ci			return -EINVAL;
262162306a36Sopenharmony_ci	}
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_ci	return 0;
262462306a36Sopenharmony_ci}
262562306a36Sopenharmony_ci
262662306a36Sopenharmony_cistatic int __tipc_nl_add_stats(struct sk_buff *skb, struct tipc_stats *s)
262762306a36Sopenharmony_ci{
262862306a36Sopenharmony_ci	int i;
262962306a36Sopenharmony_ci	struct nlattr *stats;
263062306a36Sopenharmony_ci
263162306a36Sopenharmony_ci	struct nla_map {
263262306a36Sopenharmony_ci		u32 key;
263362306a36Sopenharmony_ci		u32 val;
263462306a36Sopenharmony_ci	};
263562306a36Sopenharmony_ci
263662306a36Sopenharmony_ci	struct nla_map map[] = {
263762306a36Sopenharmony_ci		{TIPC_NLA_STATS_RX_INFO, 0},
263862306a36Sopenharmony_ci		{TIPC_NLA_STATS_RX_FRAGMENTS, s->recv_fragments},
263962306a36Sopenharmony_ci		{TIPC_NLA_STATS_RX_FRAGMENTED, s->recv_fragmented},
264062306a36Sopenharmony_ci		{TIPC_NLA_STATS_RX_BUNDLES, s->recv_bundles},
264162306a36Sopenharmony_ci		{TIPC_NLA_STATS_RX_BUNDLED, s->recv_bundled},
264262306a36Sopenharmony_ci		{TIPC_NLA_STATS_TX_INFO, 0},
264362306a36Sopenharmony_ci		{TIPC_NLA_STATS_TX_FRAGMENTS, s->sent_fragments},
264462306a36Sopenharmony_ci		{TIPC_NLA_STATS_TX_FRAGMENTED, s->sent_fragmented},
264562306a36Sopenharmony_ci		{TIPC_NLA_STATS_TX_BUNDLES, s->sent_bundles},
264662306a36Sopenharmony_ci		{TIPC_NLA_STATS_TX_BUNDLED, s->sent_bundled},
264762306a36Sopenharmony_ci		{TIPC_NLA_STATS_MSG_PROF_TOT, (s->msg_length_counts) ?
264862306a36Sopenharmony_ci			s->msg_length_counts : 1},
264962306a36Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_CNT, s->msg_length_counts},
265062306a36Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_TOT, s->msg_lengths_total},
265162306a36Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_P0, s->msg_length_profile[0]},
265262306a36Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_P1, s->msg_length_profile[1]},
265362306a36Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_P2, s->msg_length_profile[2]},
265462306a36Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_P3, s->msg_length_profile[3]},
265562306a36Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_P4, s->msg_length_profile[4]},
265662306a36Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_P5, s->msg_length_profile[5]},
265762306a36Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_P6, s->msg_length_profile[6]},
265862306a36Sopenharmony_ci		{TIPC_NLA_STATS_RX_STATES, s->recv_states},
265962306a36Sopenharmony_ci		{TIPC_NLA_STATS_RX_PROBES, s->recv_probes},
266062306a36Sopenharmony_ci		{TIPC_NLA_STATS_RX_NACKS, s->recv_nacks},
266162306a36Sopenharmony_ci		{TIPC_NLA_STATS_RX_DEFERRED, s->deferred_recv},
266262306a36Sopenharmony_ci		{TIPC_NLA_STATS_TX_STATES, s->sent_states},
266362306a36Sopenharmony_ci		{TIPC_NLA_STATS_TX_PROBES, s->sent_probes},
266462306a36Sopenharmony_ci		{TIPC_NLA_STATS_TX_NACKS, s->sent_nacks},
266562306a36Sopenharmony_ci		{TIPC_NLA_STATS_TX_ACKS, s->sent_acks},
266662306a36Sopenharmony_ci		{TIPC_NLA_STATS_RETRANSMITTED, s->retransmitted},
266762306a36Sopenharmony_ci		{TIPC_NLA_STATS_DUPLICATES, s->duplicates},
266862306a36Sopenharmony_ci		{TIPC_NLA_STATS_LINK_CONGS, s->link_congs},
266962306a36Sopenharmony_ci		{TIPC_NLA_STATS_MAX_QUEUE, s->max_queue_sz},
267062306a36Sopenharmony_ci		{TIPC_NLA_STATS_AVG_QUEUE, s->queue_sz_counts ?
267162306a36Sopenharmony_ci			(s->accu_queue_sz / s->queue_sz_counts) : 0}
267262306a36Sopenharmony_ci	};
267362306a36Sopenharmony_ci
267462306a36Sopenharmony_ci	stats = nla_nest_start_noflag(skb, TIPC_NLA_LINK_STATS);
267562306a36Sopenharmony_ci	if (!stats)
267662306a36Sopenharmony_ci		return -EMSGSIZE;
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci	for (i = 0; i <  ARRAY_SIZE(map); i++)
267962306a36Sopenharmony_ci		if (nla_put_u32(skb, map[i].key, map[i].val))
268062306a36Sopenharmony_ci			goto msg_full;
268162306a36Sopenharmony_ci
268262306a36Sopenharmony_ci	nla_nest_end(skb, stats);
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci	return 0;
268562306a36Sopenharmony_cimsg_full:
268662306a36Sopenharmony_ci	nla_nest_cancel(skb, stats);
268762306a36Sopenharmony_ci
268862306a36Sopenharmony_ci	return -EMSGSIZE;
268962306a36Sopenharmony_ci}
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_ci/* Caller should hold appropriate locks to protect the link */
269262306a36Sopenharmony_ciint __tipc_nl_add_link(struct net *net, struct tipc_nl_msg *msg,
269362306a36Sopenharmony_ci		       struct tipc_link *link, int nlflags)
269462306a36Sopenharmony_ci{
269562306a36Sopenharmony_ci	u32 self = tipc_own_addr(net);
269662306a36Sopenharmony_ci	struct nlattr *attrs;
269762306a36Sopenharmony_ci	struct nlattr *prop;
269862306a36Sopenharmony_ci	void *hdr;
269962306a36Sopenharmony_ci	int err;
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ci	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,
270262306a36Sopenharmony_ci			  nlflags, TIPC_NL_LINK_GET);
270362306a36Sopenharmony_ci	if (!hdr)
270462306a36Sopenharmony_ci		return -EMSGSIZE;
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci	attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_LINK);
270762306a36Sopenharmony_ci	if (!attrs)
270862306a36Sopenharmony_ci		goto msg_full;
270962306a36Sopenharmony_ci
271062306a36Sopenharmony_ci	if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, link->name))
271162306a36Sopenharmony_ci		goto attr_msg_full;
271262306a36Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_LINK_DEST, tipc_cluster_mask(self)))
271362306a36Sopenharmony_ci		goto attr_msg_full;
271462306a36Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_LINK_MTU, link->mtu))
271562306a36Sopenharmony_ci		goto attr_msg_full;
271662306a36Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, link->stats.recv_pkts))
271762306a36Sopenharmony_ci		goto attr_msg_full;
271862306a36Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, link->stats.sent_pkts))
271962306a36Sopenharmony_ci		goto attr_msg_full;
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_ci	if (tipc_link_is_up(link))
272262306a36Sopenharmony_ci		if (nla_put_flag(msg->skb, TIPC_NLA_LINK_UP))
272362306a36Sopenharmony_ci			goto attr_msg_full;
272462306a36Sopenharmony_ci	if (link->active)
272562306a36Sopenharmony_ci		if (nla_put_flag(msg->skb, TIPC_NLA_LINK_ACTIVE))
272662306a36Sopenharmony_ci			goto attr_msg_full;
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_ci	prop = nla_nest_start_noflag(msg->skb, TIPC_NLA_LINK_PROP);
272962306a36Sopenharmony_ci	if (!prop)
273062306a36Sopenharmony_ci		goto attr_msg_full;
273162306a36Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, link->priority))
273262306a36Sopenharmony_ci		goto prop_msg_full;
273362306a36Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_PROP_TOL, link->tolerance))
273462306a36Sopenharmony_ci		goto prop_msg_full;
273562306a36Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN,
273662306a36Sopenharmony_ci			link->window))
273762306a36Sopenharmony_ci		goto prop_msg_full;
273862306a36Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, link->priority))
273962306a36Sopenharmony_ci		goto prop_msg_full;
274062306a36Sopenharmony_ci	nla_nest_end(msg->skb, prop);
274162306a36Sopenharmony_ci
274262306a36Sopenharmony_ci	err = __tipc_nl_add_stats(msg->skb, &link->stats);
274362306a36Sopenharmony_ci	if (err)
274462306a36Sopenharmony_ci		goto attr_msg_full;
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	nla_nest_end(msg->skb, attrs);
274762306a36Sopenharmony_ci	genlmsg_end(msg->skb, hdr);
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_ci	return 0;
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ciprop_msg_full:
275262306a36Sopenharmony_ci	nla_nest_cancel(msg->skb, prop);
275362306a36Sopenharmony_ciattr_msg_full:
275462306a36Sopenharmony_ci	nla_nest_cancel(msg->skb, attrs);
275562306a36Sopenharmony_cimsg_full:
275662306a36Sopenharmony_ci	genlmsg_cancel(msg->skb, hdr);
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci	return -EMSGSIZE;
275962306a36Sopenharmony_ci}
276062306a36Sopenharmony_ci
276162306a36Sopenharmony_cistatic int __tipc_nl_add_bc_link_stat(struct sk_buff *skb,
276262306a36Sopenharmony_ci				      struct tipc_stats *stats)
276362306a36Sopenharmony_ci{
276462306a36Sopenharmony_ci	int i;
276562306a36Sopenharmony_ci	struct nlattr *nest;
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci	struct nla_map {
276862306a36Sopenharmony_ci		__u32 key;
276962306a36Sopenharmony_ci		__u32 val;
277062306a36Sopenharmony_ci	};
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci	struct nla_map map[] = {
277362306a36Sopenharmony_ci		{TIPC_NLA_STATS_RX_INFO, stats->recv_pkts},
277462306a36Sopenharmony_ci		{TIPC_NLA_STATS_RX_FRAGMENTS, stats->recv_fragments},
277562306a36Sopenharmony_ci		{TIPC_NLA_STATS_RX_FRAGMENTED, stats->recv_fragmented},
277662306a36Sopenharmony_ci		{TIPC_NLA_STATS_RX_BUNDLES, stats->recv_bundles},
277762306a36Sopenharmony_ci		{TIPC_NLA_STATS_RX_BUNDLED, stats->recv_bundled},
277862306a36Sopenharmony_ci		{TIPC_NLA_STATS_TX_INFO, stats->sent_pkts},
277962306a36Sopenharmony_ci		{TIPC_NLA_STATS_TX_FRAGMENTS, stats->sent_fragments},
278062306a36Sopenharmony_ci		{TIPC_NLA_STATS_TX_FRAGMENTED, stats->sent_fragmented},
278162306a36Sopenharmony_ci		{TIPC_NLA_STATS_TX_BUNDLES, stats->sent_bundles},
278262306a36Sopenharmony_ci		{TIPC_NLA_STATS_TX_BUNDLED, stats->sent_bundled},
278362306a36Sopenharmony_ci		{TIPC_NLA_STATS_RX_NACKS, stats->recv_nacks},
278462306a36Sopenharmony_ci		{TIPC_NLA_STATS_RX_DEFERRED, stats->deferred_recv},
278562306a36Sopenharmony_ci		{TIPC_NLA_STATS_TX_NACKS, stats->sent_nacks},
278662306a36Sopenharmony_ci		{TIPC_NLA_STATS_TX_ACKS, stats->sent_acks},
278762306a36Sopenharmony_ci		{TIPC_NLA_STATS_RETRANSMITTED, stats->retransmitted},
278862306a36Sopenharmony_ci		{TIPC_NLA_STATS_DUPLICATES, stats->duplicates},
278962306a36Sopenharmony_ci		{TIPC_NLA_STATS_LINK_CONGS, stats->link_congs},
279062306a36Sopenharmony_ci		{TIPC_NLA_STATS_MAX_QUEUE, stats->max_queue_sz},
279162306a36Sopenharmony_ci		{TIPC_NLA_STATS_AVG_QUEUE, stats->queue_sz_counts ?
279262306a36Sopenharmony_ci			(stats->accu_queue_sz / stats->queue_sz_counts) : 0}
279362306a36Sopenharmony_ci	};
279462306a36Sopenharmony_ci
279562306a36Sopenharmony_ci	nest = nla_nest_start_noflag(skb, TIPC_NLA_LINK_STATS);
279662306a36Sopenharmony_ci	if (!nest)
279762306a36Sopenharmony_ci		return -EMSGSIZE;
279862306a36Sopenharmony_ci
279962306a36Sopenharmony_ci	for (i = 0; i <  ARRAY_SIZE(map); i++)
280062306a36Sopenharmony_ci		if (nla_put_u32(skb, map[i].key, map[i].val))
280162306a36Sopenharmony_ci			goto msg_full;
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_ci	nla_nest_end(skb, nest);
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_ci	return 0;
280662306a36Sopenharmony_cimsg_full:
280762306a36Sopenharmony_ci	nla_nest_cancel(skb, nest);
280862306a36Sopenharmony_ci
280962306a36Sopenharmony_ci	return -EMSGSIZE;
281062306a36Sopenharmony_ci}
281162306a36Sopenharmony_ci
281262306a36Sopenharmony_ciint tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg,
281362306a36Sopenharmony_ci			struct tipc_link *bcl)
281462306a36Sopenharmony_ci{
281562306a36Sopenharmony_ci	int err;
281662306a36Sopenharmony_ci	void *hdr;
281762306a36Sopenharmony_ci	struct nlattr *attrs;
281862306a36Sopenharmony_ci	struct nlattr *prop;
281962306a36Sopenharmony_ci	u32 bc_mode = tipc_bcast_get_mode(net);
282062306a36Sopenharmony_ci	u32 bc_ratio = tipc_bcast_get_broadcast_ratio(net);
282162306a36Sopenharmony_ci
282262306a36Sopenharmony_ci	if (!bcl)
282362306a36Sopenharmony_ci		return 0;
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci	tipc_bcast_lock(net);
282662306a36Sopenharmony_ci
282762306a36Sopenharmony_ci	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,
282862306a36Sopenharmony_ci			  NLM_F_MULTI, TIPC_NL_LINK_GET);
282962306a36Sopenharmony_ci	if (!hdr) {
283062306a36Sopenharmony_ci		tipc_bcast_unlock(net);
283162306a36Sopenharmony_ci		return -EMSGSIZE;
283262306a36Sopenharmony_ci	}
283362306a36Sopenharmony_ci
283462306a36Sopenharmony_ci	attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_LINK);
283562306a36Sopenharmony_ci	if (!attrs)
283662306a36Sopenharmony_ci		goto msg_full;
283762306a36Sopenharmony_ci
283862306a36Sopenharmony_ci	/* The broadcast link is always up */
283962306a36Sopenharmony_ci	if (nla_put_flag(msg->skb, TIPC_NLA_LINK_UP))
284062306a36Sopenharmony_ci		goto attr_msg_full;
284162306a36Sopenharmony_ci
284262306a36Sopenharmony_ci	if (nla_put_flag(msg->skb, TIPC_NLA_LINK_BROADCAST))
284362306a36Sopenharmony_ci		goto attr_msg_full;
284462306a36Sopenharmony_ci	if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, bcl->name))
284562306a36Sopenharmony_ci		goto attr_msg_full;
284662306a36Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, 0))
284762306a36Sopenharmony_ci		goto attr_msg_full;
284862306a36Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, 0))
284962306a36Sopenharmony_ci		goto attr_msg_full;
285062306a36Sopenharmony_ci
285162306a36Sopenharmony_ci	prop = nla_nest_start_noflag(msg->skb, TIPC_NLA_LINK_PROP);
285262306a36Sopenharmony_ci	if (!prop)
285362306a36Sopenharmony_ci		goto attr_msg_full;
285462306a36Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN, bcl->max_win))
285562306a36Sopenharmony_ci		goto prop_msg_full;
285662306a36Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_PROP_BROADCAST, bc_mode))
285762306a36Sopenharmony_ci		goto prop_msg_full;
285862306a36Sopenharmony_ci	if (bc_mode & BCLINK_MODE_SEL)
285962306a36Sopenharmony_ci		if (nla_put_u32(msg->skb, TIPC_NLA_PROP_BROADCAST_RATIO,
286062306a36Sopenharmony_ci				bc_ratio))
286162306a36Sopenharmony_ci			goto prop_msg_full;
286262306a36Sopenharmony_ci	nla_nest_end(msg->skb, prop);
286362306a36Sopenharmony_ci
286462306a36Sopenharmony_ci	err = __tipc_nl_add_bc_link_stat(msg->skb, &bcl->stats);
286562306a36Sopenharmony_ci	if (err)
286662306a36Sopenharmony_ci		goto attr_msg_full;
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_ci	tipc_bcast_unlock(net);
286962306a36Sopenharmony_ci	nla_nest_end(msg->skb, attrs);
287062306a36Sopenharmony_ci	genlmsg_end(msg->skb, hdr);
287162306a36Sopenharmony_ci
287262306a36Sopenharmony_ci	return 0;
287362306a36Sopenharmony_ci
287462306a36Sopenharmony_ciprop_msg_full:
287562306a36Sopenharmony_ci	nla_nest_cancel(msg->skb, prop);
287662306a36Sopenharmony_ciattr_msg_full:
287762306a36Sopenharmony_ci	nla_nest_cancel(msg->skb, attrs);
287862306a36Sopenharmony_cimsg_full:
287962306a36Sopenharmony_ci	tipc_bcast_unlock(net);
288062306a36Sopenharmony_ci	genlmsg_cancel(msg->skb, hdr);
288162306a36Sopenharmony_ci
288262306a36Sopenharmony_ci	return -EMSGSIZE;
288362306a36Sopenharmony_ci}
288462306a36Sopenharmony_ci
288562306a36Sopenharmony_civoid tipc_link_set_tolerance(struct tipc_link *l, u32 tol,
288662306a36Sopenharmony_ci			     struct sk_buff_head *xmitq)
288762306a36Sopenharmony_ci{
288862306a36Sopenharmony_ci	l->tolerance = tol;
288962306a36Sopenharmony_ci	if (l->bc_rcvlink)
289062306a36Sopenharmony_ci		l->bc_rcvlink->tolerance = tol;
289162306a36Sopenharmony_ci	if (link_is_up(l))
289262306a36Sopenharmony_ci		tipc_link_build_proto_msg(l, STATE_MSG, 0, 0, 0, tol, 0, xmitq);
289362306a36Sopenharmony_ci}
289462306a36Sopenharmony_ci
289562306a36Sopenharmony_civoid tipc_link_set_prio(struct tipc_link *l, u32 prio,
289662306a36Sopenharmony_ci			struct sk_buff_head *xmitq)
289762306a36Sopenharmony_ci{
289862306a36Sopenharmony_ci	l->priority = prio;
289962306a36Sopenharmony_ci	tipc_link_build_proto_msg(l, STATE_MSG, 0, 0, 0, 0, prio, xmitq);
290062306a36Sopenharmony_ci}
290162306a36Sopenharmony_ci
290262306a36Sopenharmony_civoid tipc_link_set_abort_limit(struct tipc_link *l, u32 limit)
290362306a36Sopenharmony_ci{
290462306a36Sopenharmony_ci	l->abort_limit = limit;
290562306a36Sopenharmony_ci}
290662306a36Sopenharmony_ci
290762306a36Sopenharmony_ci/**
290862306a36Sopenharmony_ci * tipc_link_dump - dump TIPC link data
290962306a36Sopenharmony_ci * @l: tipc link to be dumped
291062306a36Sopenharmony_ci * @dqueues: bitmask to decide if any link queue to be dumped?
291162306a36Sopenharmony_ci *           - TIPC_DUMP_NONE: don't dump link queues
291262306a36Sopenharmony_ci *           - TIPC_DUMP_TRANSMQ: dump link transmq queue
291362306a36Sopenharmony_ci *           - TIPC_DUMP_BACKLOGQ: dump link backlog queue
291462306a36Sopenharmony_ci *           - TIPC_DUMP_DEFERDQ: dump link deferd queue
291562306a36Sopenharmony_ci *           - TIPC_DUMP_INPUTQ: dump link input queue
291662306a36Sopenharmony_ci *           - TIPC_DUMP_WAKEUP: dump link wakeup queue
291762306a36Sopenharmony_ci *           - TIPC_DUMP_ALL: dump all the link queues above
291862306a36Sopenharmony_ci * @buf: returned buffer of dump data in format
291962306a36Sopenharmony_ci */
292062306a36Sopenharmony_ciint tipc_link_dump(struct tipc_link *l, u16 dqueues, char *buf)
292162306a36Sopenharmony_ci{
292262306a36Sopenharmony_ci	int i = 0;
292362306a36Sopenharmony_ci	size_t sz = (dqueues) ? LINK_LMAX : LINK_LMIN;
292462306a36Sopenharmony_ci	struct sk_buff_head *list;
292562306a36Sopenharmony_ci	struct sk_buff *hskb, *tskb;
292662306a36Sopenharmony_ci	u32 len;
292762306a36Sopenharmony_ci
292862306a36Sopenharmony_ci	if (!l) {
292962306a36Sopenharmony_ci		i += scnprintf(buf, sz, "link data: (null)\n");
293062306a36Sopenharmony_ci		return i;
293162306a36Sopenharmony_ci	}
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci	i += scnprintf(buf, sz, "link data: %x", l->addr);
293462306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %x", l->state);
293562306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->in_session);
293662306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->session);
293762306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->peer_session);
293862306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->snd_nxt);
293962306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->rcv_nxt);
294062306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->snd_nxt_state);
294162306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->rcv_nxt_state);
294262306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %x", l->peer_caps);
294362306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->silent_intv_cnt);
294462306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->rst_cnt);
294562306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", 0);
294662306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", 0);
294762306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->acked);
294862306a36Sopenharmony_ci
294962306a36Sopenharmony_ci	list = &l->transmq;
295062306a36Sopenharmony_ci	len = skb_queue_len(list);
295162306a36Sopenharmony_ci	hskb = skb_peek(list);
295262306a36Sopenharmony_ci	tskb = skb_peek_tail(list);
295362306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " | %u %u %u", len,
295462306a36Sopenharmony_ci		       (hskb) ? msg_seqno(buf_msg(hskb)) : 0,
295562306a36Sopenharmony_ci		       (tskb) ? msg_seqno(buf_msg(tskb)) : 0);
295662306a36Sopenharmony_ci
295762306a36Sopenharmony_ci	list = &l->deferdq;
295862306a36Sopenharmony_ci	len = skb_queue_len(list);
295962306a36Sopenharmony_ci	hskb = skb_peek(list);
296062306a36Sopenharmony_ci	tskb = skb_peek_tail(list);
296162306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " | %u %u %u", len,
296262306a36Sopenharmony_ci		       (hskb) ? msg_seqno(buf_msg(hskb)) : 0,
296362306a36Sopenharmony_ci		       (tskb) ? msg_seqno(buf_msg(tskb)) : 0);
296462306a36Sopenharmony_ci
296562306a36Sopenharmony_ci	list = &l->backlogq;
296662306a36Sopenharmony_ci	len = skb_queue_len(list);
296762306a36Sopenharmony_ci	hskb = skb_peek(list);
296862306a36Sopenharmony_ci	tskb = skb_peek_tail(list);
296962306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " | %u %u %u", len,
297062306a36Sopenharmony_ci		       (hskb) ? msg_seqno(buf_msg(hskb)) : 0,
297162306a36Sopenharmony_ci		       (tskb) ? msg_seqno(buf_msg(tskb)) : 0);
297262306a36Sopenharmony_ci
297362306a36Sopenharmony_ci	list = l->inputq;
297462306a36Sopenharmony_ci	len = skb_queue_len(list);
297562306a36Sopenharmony_ci	hskb = skb_peek(list);
297662306a36Sopenharmony_ci	tskb = skb_peek_tail(list);
297762306a36Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " | %u %u %u\n", len,
297862306a36Sopenharmony_ci		       (hskb) ? msg_seqno(buf_msg(hskb)) : 0,
297962306a36Sopenharmony_ci		       (tskb) ? msg_seqno(buf_msg(tskb)) : 0);
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_ci	if (dqueues & TIPC_DUMP_TRANSMQ) {
298262306a36Sopenharmony_ci		i += scnprintf(buf + i, sz - i, "transmq: ");
298362306a36Sopenharmony_ci		i += tipc_list_dump(&l->transmq, false, buf + i);
298462306a36Sopenharmony_ci	}
298562306a36Sopenharmony_ci	if (dqueues & TIPC_DUMP_BACKLOGQ) {
298662306a36Sopenharmony_ci		i += scnprintf(buf + i, sz - i,
298762306a36Sopenharmony_ci			       "backlogq: <%u %u %u %u %u>, ",
298862306a36Sopenharmony_ci			       l->backlog[TIPC_LOW_IMPORTANCE].len,
298962306a36Sopenharmony_ci			       l->backlog[TIPC_MEDIUM_IMPORTANCE].len,
299062306a36Sopenharmony_ci			       l->backlog[TIPC_HIGH_IMPORTANCE].len,
299162306a36Sopenharmony_ci			       l->backlog[TIPC_CRITICAL_IMPORTANCE].len,
299262306a36Sopenharmony_ci			       l->backlog[TIPC_SYSTEM_IMPORTANCE].len);
299362306a36Sopenharmony_ci		i += tipc_list_dump(&l->backlogq, false, buf + i);
299462306a36Sopenharmony_ci	}
299562306a36Sopenharmony_ci	if (dqueues & TIPC_DUMP_DEFERDQ) {
299662306a36Sopenharmony_ci		i += scnprintf(buf + i, sz - i, "deferdq: ");
299762306a36Sopenharmony_ci		i += tipc_list_dump(&l->deferdq, false, buf + i);
299862306a36Sopenharmony_ci	}
299962306a36Sopenharmony_ci	if (dqueues & TIPC_DUMP_INPUTQ) {
300062306a36Sopenharmony_ci		i += scnprintf(buf + i, sz - i, "inputq: ");
300162306a36Sopenharmony_ci		i += tipc_list_dump(l->inputq, false, buf + i);
300262306a36Sopenharmony_ci	}
300362306a36Sopenharmony_ci	if (dqueues & TIPC_DUMP_WAKEUP) {
300462306a36Sopenharmony_ci		i += scnprintf(buf + i, sz - i, "wakeup: ");
300562306a36Sopenharmony_ci		i += tipc_list_dump(&l->wakeupq, false, buf + i);
300662306a36Sopenharmony_ci	}
300762306a36Sopenharmony_ci
300862306a36Sopenharmony_ci	return i;
300962306a36Sopenharmony_ci}
3010