18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * net/tipc/link.c: TIPC link code
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 1996-2007, 2012-2016, Ericsson AB
58c2ecf20Sopenharmony_ci * Copyright (c) 2004-2007, 2010-2013, Wind River Systems
68c2ecf20Sopenharmony_ci * All rights reserved.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
98c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions are met:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
128c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
138c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
148c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
158c2ecf20Sopenharmony_ci *    documentation and/or other materials provided with the distribution.
168c2ecf20Sopenharmony_ci * 3. Neither the names of the copyright holders nor the names of its
178c2ecf20Sopenharmony_ci *    contributors may be used to endorse or promote products derived from
188c2ecf20Sopenharmony_ci *    this software without specific prior written permission.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the
218c2ecf20Sopenharmony_ci * GNU General Public License ("GPL") version 2 as published by the Free
228c2ecf20Sopenharmony_ci * Software Foundation.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
258c2ecf20Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
268c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
278c2ecf20Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
288c2ecf20Sopenharmony_ci * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
298c2ecf20Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
308c2ecf20Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
318c2ecf20Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
328c2ecf20Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
338c2ecf20Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
348c2ecf20Sopenharmony_ci * POSSIBILITY OF SUCH DAMAGE.
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include "core.h"
388c2ecf20Sopenharmony_ci#include "subscr.h"
398c2ecf20Sopenharmony_ci#include "link.h"
408c2ecf20Sopenharmony_ci#include "bcast.h"
418c2ecf20Sopenharmony_ci#include "socket.h"
428c2ecf20Sopenharmony_ci#include "name_distr.h"
438c2ecf20Sopenharmony_ci#include "discover.h"
448c2ecf20Sopenharmony_ci#include "netlink.h"
458c2ecf20Sopenharmony_ci#include "monitor.h"
468c2ecf20Sopenharmony_ci#include "trace.h"
478c2ecf20Sopenharmony_ci#include "crypto.h"
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#include <linux/pkt_sched.h>
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistruct tipc_stats {
528c2ecf20Sopenharmony_ci	u32 sent_pkts;
538c2ecf20Sopenharmony_ci	u32 recv_pkts;
548c2ecf20Sopenharmony_ci	u32 sent_states;
558c2ecf20Sopenharmony_ci	u32 recv_states;
568c2ecf20Sopenharmony_ci	u32 sent_probes;
578c2ecf20Sopenharmony_ci	u32 recv_probes;
588c2ecf20Sopenharmony_ci	u32 sent_nacks;
598c2ecf20Sopenharmony_ci	u32 recv_nacks;
608c2ecf20Sopenharmony_ci	u32 sent_acks;
618c2ecf20Sopenharmony_ci	u32 sent_bundled;
628c2ecf20Sopenharmony_ci	u32 sent_bundles;
638c2ecf20Sopenharmony_ci	u32 recv_bundled;
648c2ecf20Sopenharmony_ci	u32 recv_bundles;
658c2ecf20Sopenharmony_ci	u32 retransmitted;
668c2ecf20Sopenharmony_ci	u32 sent_fragmented;
678c2ecf20Sopenharmony_ci	u32 sent_fragments;
688c2ecf20Sopenharmony_ci	u32 recv_fragmented;
698c2ecf20Sopenharmony_ci	u32 recv_fragments;
708c2ecf20Sopenharmony_ci	u32 link_congs;		/* # port sends blocked by congestion */
718c2ecf20Sopenharmony_ci	u32 deferred_recv;
728c2ecf20Sopenharmony_ci	u32 duplicates;
738c2ecf20Sopenharmony_ci	u32 max_queue_sz;	/* send queue size high water mark */
748c2ecf20Sopenharmony_ci	u32 accu_queue_sz;	/* used for send queue size profiling */
758c2ecf20Sopenharmony_ci	u32 queue_sz_counts;	/* used for send queue size profiling */
768c2ecf20Sopenharmony_ci	u32 msg_length_counts;	/* used for message length profiling */
778c2ecf20Sopenharmony_ci	u32 msg_lengths_total;	/* used for message length profiling */
788c2ecf20Sopenharmony_ci	u32 msg_length_profile[7]; /* used for msg. length profiling */
798c2ecf20Sopenharmony_ci};
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/**
828c2ecf20Sopenharmony_ci * struct tipc_link - TIPC link data structure
838c2ecf20Sopenharmony_ci * @addr: network address of link's peer node
848c2ecf20Sopenharmony_ci * @name: link name character string
858c2ecf20Sopenharmony_ci * @media_addr: media address to use when sending messages over link
868c2ecf20Sopenharmony_ci * @timer: link timer
878c2ecf20Sopenharmony_ci * @net: pointer to namespace struct
888c2ecf20Sopenharmony_ci * @refcnt: reference counter for permanent references (owner node & timer)
898c2ecf20Sopenharmony_ci * @peer_session: link session # being used by peer end of link
908c2ecf20Sopenharmony_ci * @peer_bearer_id: bearer id used by link's peer endpoint
918c2ecf20Sopenharmony_ci * @bearer_id: local bearer id used by link
928c2ecf20Sopenharmony_ci * @tolerance: minimum link continuity loss needed to reset link [in ms]
938c2ecf20Sopenharmony_ci * @abort_limit: # of unacknowledged continuity probes needed to reset link
948c2ecf20Sopenharmony_ci * @state: current state of link FSM
958c2ecf20Sopenharmony_ci * @peer_caps: bitmap describing capabilities of peer node
968c2ecf20Sopenharmony_ci * @silent_intv_cnt: # of timer intervals without any reception from peer
978c2ecf20Sopenharmony_ci * @proto_msg: template for control messages generated by link
988c2ecf20Sopenharmony_ci * @pmsg: convenience pointer to "proto_msg" field
998c2ecf20Sopenharmony_ci * @priority: current link priority
1008c2ecf20Sopenharmony_ci * @net_plane: current link network plane ('A' through 'H')
1018c2ecf20Sopenharmony_ci * @mon_state: cookie with information needed by link monitor
1028c2ecf20Sopenharmony_ci * @backlog_limit: backlog queue congestion thresholds (indexed by importance)
1038c2ecf20Sopenharmony_ci * @exp_msg_count: # of tunnelled messages expected during link changeover
1048c2ecf20Sopenharmony_ci * @reset_rcv_checkpt: seq # of last acknowledged message at time of link reset
1058c2ecf20Sopenharmony_ci * @mtu: current maximum packet size for this link
1068c2ecf20Sopenharmony_ci * @advertised_mtu: advertised own mtu when link is being established
1078c2ecf20Sopenharmony_ci * @transmitq: queue for sent, non-acked messages
1088c2ecf20Sopenharmony_ci * @backlogq: queue for messages waiting to be sent
1098c2ecf20Sopenharmony_ci * @snt_nxt: next sequence number to use for outbound messages
1108c2ecf20Sopenharmony_ci * @ackers: # of peers that needs to ack each packet before it can be released
1118c2ecf20Sopenharmony_ci * @acked: # last packet acked by a certain peer. Used for broadcast.
1128c2ecf20Sopenharmony_ci * @rcv_nxt: next sequence number to expect for inbound messages
1138c2ecf20Sopenharmony_ci * @deferred_queue: deferred queue saved OOS b'cast message received from node
1148c2ecf20Sopenharmony_ci * @unacked_window: # of inbound messages rx'd without ack'ing back to peer
1158c2ecf20Sopenharmony_ci * @inputq: buffer queue for messages to be delivered upwards
1168c2ecf20Sopenharmony_ci * @namedq: buffer queue for name table messages to be delivered upwards
1178c2ecf20Sopenharmony_ci * @next_out: ptr to first unsent outbound message in queue
1188c2ecf20Sopenharmony_ci * @wakeupq: linked list of wakeup msgs waiting for link congestion to abate
1198c2ecf20Sopenharmony_ci * @long_msg_seq_no: next identifier to use for outbound fragmented messages
1208c2ecf20Sopenharmony_ci * @reasm_buf: head of partially reassembled inbound message fragments
1218c2ecf20Sopenharmony_ci * @bc_rcvr: marks that this is a broadcast receiver link
1228c2ecf20Sopenharmony_ci * @stats: collects statistics regarding link activity
1238c2ecf20Sopenharmony_ci */
1248c2ecf20Sopenharmony_cistruct tipc_link {
1258c2ecf20Sopenharmony_ci	u32 addr;
1268c2ecf20Sopenharmony_ci	char name[TIPC_MAX_LINK_NAME];
1278c2ecf20Sopenharmony_ci	struct net *net;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	/* Management and link supervision data */
1308c2ecf20Sopenharmony_ci	u16 peer_session;
1318c2ecf20Sopenharmony_ci	u16 session;
1328c2ecf20Sopenharmony_ci	u16 snd_nxt_state;
1338c2ecf20Sopenharmony_ci	u16 rcv_nxt_state;
1348c2ecf20Sopenharmony_ci	u32 peer_bearer_id;
1358c2ecf20Sopenharmony_ci	u32 bearer_id;
1368c2ecf20Sopenharmony_ci	u32 tolerance;
1378c2ecf20Sopenharmony_ci	u32 abort_limit;
1388c2ecf20Sopenharmony_ci	u32 state;
1398c2ecf20Sopenharmony_ci	u16 peer_caps;
1408c2ecf20Sopenharmony_ci	bool in_session;
1418c2ecf20Sopenharmony_ci	bool active;
1428c2ecf20Sopenharmony_ci	u32 silent_intv_cnt;
1438c2ecf20Sopenharmony_ci	char if_name[TIPC_MAX_IF_NAME];
1448c2ecf20Sopenharmony_ci	u32 priority;
1458c2ecf20Sopenharmony_ci	char net_plane;
1468c2ecf20Sopenharmony_ci	struct tipc_mon_state mon_state;
1478c2ecf20Sopenharmony_ci	u16 rst_cnt;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/* Failover/synch */
1508c2ecf20Sopenharmony_ci	u16 drop_point;
1518c2ecf20Sopenharmony_ci	struct sk_buff *failover_reasm_skb;
1528c2ecf20Sopenharmony_ci	struct sk_buff_head failover_deferdq;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/* Max packet negotiation */
1558c2ecf20Sopenharmony_ci	u16 mtu;
1568c2ecf20Sopenharmony_ci	u16 advertised_mtu;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/* Sending */
1598c2ecf20Sopenharmony_ci	struct sk_buff_head transmq;
1608c2ecf20Sopenharmony_ci	struct sk_buff_head backlogq;
1618c2ecf20Sopenharmony_ci	struct {
1628c2ecf20Sopenharmony_ci		u16 len;
1638c2ecf20Sopenharmony_ci		u16 limit;
1648c2ecf20Sopenharmony_ci		struct sk_buff *target_bskb;
1658c2ecf20Sopenharmony_ci	} backlog[5];
1668c2ecf20Sopenharmony_ci	u16 snd_nxt;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	/* Reception */
1698c2ecf20Sopenharmony_ci	u16 rcv_nxt;
1708c2ecf20Sopenharmony_ci	u32 rcv_unacked;
1718c2ecf20Sopenharmony_ci	struct sk_buff_head deferdq;
1728c2ecf20Sopenharmony_ci	struct sk_buff_head *inputq;
1738c2ecf20Sopenharmony_ci	struct sk_buff_head *namedq;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* Congestion handling */
1768c2ecf20Sopenharmony_ci	struct sk_buff_head wakeupq;
1778c2ecf20Sopenharmony_ci	u16 window;
1788c2ecf20Sopenharmony_ci	u16 min_win;
1798c2ecf20Sopenharmony_ci	u16 ssthresh;
1808c2ecf20Sopenharmony_ci	u16 max_win;
1818c2ecf20Sopenharmony_ci	u16 cong_acks;
1828c2ecf20Sopenharmony_ci	u16 checkpoint;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	/* Fragmentation/reassembly */
1858c2ecf20Sopenharmony_ci	struct sk_buff *reasm_buf;
1868c2ecf20Sopenharmony_ci	struct sk_buff *reasm_tnlmsg;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* Broadcast */
1898c2ecf20Sopenharmony_ci	u16 ackers;
1908c2ecf20Sopenharmony_ci	u16 acked;
1918c2ecf20Sopenharmony_ci	u16 last_gap;
1928c2ecf20Sopenharmony_ci	struct tipc_gap_ack_blks *last_ga;
1938c2ecf20Sopenharmony_ci	struct tipc_link *bc_rcvlink;
1948c2ecf20Sopenharmony_ci	struct tipc_link *bc_sndlink;
1958c2ecf20Sopenharmony_ci	u8 nack_state;
1968c2ecf20Sopenharmony_ci	bool bc_peer_is_up;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	/* Statistics */
1998c2ecf20Sopenharmony_ci	struct tipc_stats stats;
2008c2ecf20Sopenharmony_ci};
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci/*
2038c2ecf20Sopenharmony_ci * Error message prefixes
2048c2ecf20Sopenharmony_ci */
2058c2ecf20Sopenharmony_cistatic const char *link_co_err = "Link tunneling error, ";
2068c2ecf20Sopenharmony_cistatic const char *link_rst_msg = "Resetting link ";
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci/* Send states for broadcast NACKs
2098c2ecf20Sopenharmony_ci */
2108c2ecf20Sopenharmony_cienum {
2118c2ecf20Sopenharmony_ci	BC_NACK_SND_CONDITIONAL,
2128c2ecf20Sopenharmony_ci	BC_NACK_SND_UNCONDITIONAL,
2138c2ecf20Sopenharmony_ci	BC_NACK_SND_SUPPRESS,
2148c2ecf20Sopenharmony_ci};
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci#define TIPC_BC_RETR_LIM  (jiffies + msecs_to_jiffies(10))
2178c2ecf20Sopenharmony_ci#define TIPC_UC_RETR_TIME (jiffies + msecs_to_jiffies(1))
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/* Link FSM states:
2208c2ecf20Sopenharmony_ci */
2218c2ecf20Sopenharmony_cienum {
2228c2ecf20Sopenharmony_ci	LINK_ESTABLISHED     = 0xe,
2238c2ecf20Sopenharmony_ci	LINK_ESTABLISHING    = 0xe  << 4,
2248c2ecf20Sopenharmony_ci	LINK_RESET           = 0x1  << 8,
2258c2ecf20Sopenharmony_ci	LINK_RESETTING       = 0x2  << 12,
2268c2ecf20Sopenharmony_ci	LINK_PEER_RESET      = 0xd  << 16,
2278c2ecf20Sopenharmony_ci	LINK_FAILINGOVER     = 0xf  << 20,
2288c2ecf20Sopenharmony_ci	LINK_SYNCHING        = 0xc  << 24
2298c2ecf20Sopenharmony_ci};
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci/* Link FSM state checking routines
2328c2ecf20Sopenharmony_ci */
2338c2ecf20Sopenharmony_cistatic int link_is_up(struct tipc_link *l)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	return l->state & (LINK_ESTABLISHED | LINK_SYNCHING);
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
2398c2ecf20Sopenharmony_ci			       struct sk_buff_head *xmitq);
2408c2ecf20Sopenharmony_cistatic void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
2418c2ecf20Sopenharmony_ci				      bool probe_reply, u16 rcvgap,
2428c2ecf20Sopenharmony_ci				      int tolerance, int priority,
2438c2ecf20Sopenharmony_ci				      struct sk_buff_head *xmitq);
2448c2ecf20Sopenharmony_cistatic void link_print(struct tipc_link *l, const char *str);
2458c2ecf20Sopenharmony_cistatic int tipc_link_build_nack_msg(struct tipc_link *l,
2468c2ecf20Sopenharmony_ci				    struct sk_buff_head *xmitq);
2478c2ecf20Sopenharmony_cistatic void tipc_link_build_bc_init_msg(struct tipc_link *l,
2488c2ecf20Sopenharmony_ci					struct sk_buff_head *xmitq);
2498c2ecf20Sopenharmony_cistatic u8 __tipc_build_gap_ack_blks(struct tipc_gap_ack_blks *ga,
2508c2ecf20Sopenharmony_ci				    struct tipc_link *l, u8 start_index);
2518c2ecf20Sopenharmony_cistatic u16 tipc_build_gap_ack_blks(struct tipc_link *l, struct tipc_msg *hdr);
2528c2ecf20Sopenharmony_cistatic int tipc_link_advance_transmq(struct tipc_link *l, struct tipc_link *r,
2538c2ecf20Sopenharmony_ci				     u16 acked, u16 gap,
2548c2ecf20Sopenharmony_ci				     struct tipc_gap_ack_blks *ga,
2558c2ecf20Sopenharmony_ci				     struct sk_buff_head *xmitq,
2568c2ecf20Sopenharmony_ci				     bool *retransmitted, int *rc);
2578c2ecf20Sopenharmony_cistatic void tipc_link_update_cwin(struct tipc_link *l, int released,
2588c2ecf20Sopenharmony_ci				  bool retransmitted);
2598c2ecf20Sopenharmony_ci/*
2608c2ecf20Sopenharmony_ci *  Simple non-static link routines (i.e. referenced outside this file)
2618c2ecf20Sopenharmony_ci */
2628c2ecf20Sopenharmony_cibool tipc_link_is_up(struct tipc_link *l)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	return link_is_up(l);
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cibool tipc_link_peer_is_down(struct tipc_link *l)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	return l->state == LINK_PEER_RESET;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cibool tipc_link_is_reset(struct tipc_link *l)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	return l->state & (LINK_RESET | LINK_FAILINGOVER | LINK_ESTABLISHING);
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cibool tipc_link_is_establishing(struct tipc_link *l)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	return l->state == LINK_ESTABLISHING;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cibool tipc_link_is_synching(struct tipc_link *l)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	return l->state == LINK_SYNCHING;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cibool tipc_link_is_failingover(struct tipc_link *l)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	return l->state == LINK_FAILINGOVER;
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cibool tipc_link_is_blocked(struct tipc_link *l)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	return l->state & (LINK_RESETTING | LINK_PEER_RESET | LINK_FAILINGOVER);
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic bool link_is_bc_sndlink(struct tipc_link *l)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	return !l->bc_sndlink;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic bool link_is_bc_rcvlink(struct tipc_link *l)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	return ((l->bc_rcvlink == l) && !link_is_bc_sndlink(l));
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_civoid tipc_link_set_active(struct tipc_link *l, bool active)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	l->active = active;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ciu32 tipc_link_id(struct tipc_link *l)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	return l->peer_bearer_id << 16 | l->bearer_id;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ciint tipc_link_min_win(struct tipc_link *l)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	return l->min_win;
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ciint tipc_link_max_win(struct tipc_link *l)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	return l->max_win;
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ciint tipc_link_prio(struct tipc_link *l)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	return l->priority;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ciunsigned long tipc_link_tolerance(struct tipc_link *l)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	return l->tolerance;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistruct sk_buff_head *tipc_link_inputq(struct tipc_link *l)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	return l->inputq;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cichar tipc_link_plane(struct tipc_link *l)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	return l->net_plane;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistruct net *tipc_link_net(struct tipc_link *l)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	return l->net;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_civoid tipc_link_update_caps(struct tipc_link *l, u16 capabilities)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	l->peer_caps = capabilities;
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_civoid tipc_link_add_bc_peer(struct tipc_link *snd_l,
3588c2ecf20Sopenharmony_ci			   struct tipc_link *uc_l,
3598c2ecf20Sopenharmony_ci			   struct sk_buff_head *xmitq)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct tipc_link *rcv_l = uc_l->bc_rcvlink;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	snd_l->ackers++;
3648c2ecf20Sopenharmony_ci	rcv_l->acked = snd_l->snd_nxt - 1;
3658c2ecf20Sopenharmony_ci	snd_l->state = LINK_ESTABLISHED;
3668c2ecf20Sopenharmony_ci	tipc_link_build_bc_init_msg(uc_l, xmitq);
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_civoid tipc_link_remove_bc_peer(struct tipc_link *snd_l,
3708c2ecf20Sopenharmony_ci			      struct tipc_link *rcv_l,
3718c2ecf20Sopenharmony_ci			      struct sk_buff_head *xmitq)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	u16 ack = snd_l->snd_nxt - 1;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	snd_l->ackers--;
3768c2ecf20Sopenharmony_ci	rcv_l->bc_peer_is_up = true;
3778c2ecf20Sopenharmony_ci	rcv_l->state = LINK_ESTABLISHED;
3788c2ecf20Sopenharmony_ci	tipc_link_bc_ack_rcv(rcv_l, ack, 0, NULL, xmitq, NULL);
3798c2ecf20Sopenharmony_ci	trace_tipc_link_reset(rcv_l, TIPC_DUMP_ALL, "bclink removed!");
3808c2ecf20Sopenharmony_ci	tipc_link_reset(rcv_l);
3818c2ecf20Sopenharmony_ci	rcv_l->state = LINK_RESET;
3828c2ecf20Sopenharmony_ci	if (!snd_l->ackers) {
3838c2ecf20Sopenharmony_ci		trace_tipc_link_reset(snd_l, TIPC_DUMP_ALL, "zero ackers!");
3848c2ecf20Sopenharmony_ci		tipc_link_reset(snd_l);
3858c2ecf20Sopenharmony_ci		snd_l->state = LINK_RESET;
3868c2ecf20Sopenharmony_ci		__skb_queue_purge(xmitq);
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ciint tipc_link_bc_peers(struct tipc_link *l)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	return l->ackers;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic u16 link_bc_rcv_gap(struct tipc_link *l)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	struct sk_buff *skb = skb_peek(&l->deferdq);
3988c2ecf20Sopenharmony_ci	u16 gap = 0;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if (more(l->snd_nxt, l->rcv_nxt))
4018c2ecf20Sopenharmony_ci		gap = l->snd_nxt - l->rcv_nxt;
4028c2ecf20Sopenharmony_ci	if (skb)
4038c2ecf20Sopenharmony_ci		gap = buf_seqno(skb) - l->rcv_nxt;
4048c2ecf20Sopenharmony_ci	return gap;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_civoid tipc_link_set_mtu(struct tipc_link *l, int mtu)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	l->mtu = mtu;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ciint tipc_link_mtu(struct tipc_link *l)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	return l->mtu;
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ciint tipc_link_mss(struct tipc_link *l)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci#ifdef CONFIG_TIPC_CRYPTO
4208c2ecf20Sopenharmony_ci	return l->mtu - INT_H_SIZE - EMSG_OVERHEAD;
4218c2ecf20Sopenharmony_ci#else
4228c2ecf20Sopenharmony_ci	return l->mtu - INT_H_SIZE;
4238c2ecf20Sopenharmony_ci#endif
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ciu16 tipc_link_rcv_nxt(struct tipc_link *l)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	return l->rcv_nxt;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ciu16 tipc_link_acked(struct tipc_link *l)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	return l->acked;
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cichar *tipc_link_name(struct tipc_link *l)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	return l->name;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ciu32 tipc_link_state(struct tipc_link *l)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	return l->state;
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci/**
4478c2ecf20Sopenharmony_ci * tipc_link_create - create a new link
4488c2ecf20Sopenharmony_ci * @net: pointer to associated network namespace
4498c2ecf20Sopenharmony_ci * @if_name: associated interface name
4508c2ecf20Sopenharmony_ci * @bearer_id: id (index) of associated bearer
4518c2ecf20Sopenharmony_ci * @tolerance: link tolerance to be used by link
4528c2ecf20Sopenharmony_ci * @net_plane: network plane (A,B,c..) this link belongs to
4538c2ecf20Sopenharmony_ci * @mtu: mtu to be advertised by link
4548c2ecf20Sopenharmony_ci * @priority: priority to be used by link
4558c2ecf20Sopenharmony_ci * @min_win: minimal send window to be used by link
4568c2ecf20Sopenharmony_ci * @max_win: maximal send window to be used by link
4578c2ecf20Sopenharmony_ci * @session: session to be used by link
4588c2ecf20Sopenharmony_ci * @ownnode: identity of own node
4598c2ecf20Sopenharmony_ci * @peer: node id of peer node
4608c2ecf20Sopenharmony_ci * @peer_caps: bitmap describing peer node capabilities
4618c2ecf20Sopenharmony_ci * @bc_sndlink: the namespace global link used for broadcast sending
4628c2ecf20Sopenharmony_ci * @bc_rcvlink: the peer specific link used for broadcast reception
4638c2ecf20Sopenharmony_ci * @inputq: queue to put messages ready for delivery
4648c2ecf20Sopenharmony_ci * @namedq: queue to put binding table update messages ready for delivery
4658c2ecf20Sopenharmony_ci * @link: return value, pointer to put the created link
4668c2ecf20Sopenharmony_ci *
4678c2ecf20Sopenharmony_ci * Returns true if link was created, otherwise false
4688c2ecf20Sopenharmony_ci */
4698c2ecf20Sopenharmony_cibool tipc_link_create(struct net *net, char *if_name, int bearer_id,
4708c2ecf20Sopenharmony_ci		      int tolerance, char net_plane, u32 mtu, int priority,
4718c2ecf20Sopenharmony_ci		      u32 min_win, u32 max_win, u32 session, u32 self,
4728c2ecf20Sopenharmony_ci		      u32 peer, u8 *peer_id, u16 peer_caps,
4738c2ecf20Sopenharmony_ci		      struct tipc_link *bc_sndlink,
4748c2ecf20Sopenharmony_ci		      struct tipc_link *bc_rcvlink,
4758c2ecf20Sopenharmony_ci		      struct sk_buff_head *inputq,
4768c2ecf20Sopenharmony_ci		      struct sk_buff_head *namedq,
4778c2ecf20Sopenharmony_ci		      struct tipc_link **link)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	char peer_str[NODE_ID_STR_LEN] = {0,};
4808c2ecf20Sopenharmony_ci	char self_str[NODE_ID_STR_LEN] = {0,};
4818c2ecf20Sopenharmony_ci	struct tipc_link *l;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	l = kzalloc(sizeof(*l), GFP_ATOMIC);
4848c2ecf20Sopenharmony_ci	if (!l)
4858c2ecf20Sopenharmony_ci		return false;
4868c2ecf20Sopenharmony_ci	*link = l;
4878c2ecf20Sopenharmony_ci	l->session = session;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	/* Set link name for unicast links only */
4908c2ecf20Sopenharmony_ci	if (peer_id) {
4918c2ecf20Sopenharmony_ci		tipc_nodeid2string(self_str, tipc_own_id(net));
4928c2ecf20Sopenharmony_ci		if (strlen(self_str) > 16)
4938c2ecf20Sopenharmony_ci			sprintf(self_str, "%x", self);
4948c2ecf20Sopenharmony_ci		tipc_nodeid2string(peer_str, peer_id);
4958c2ecf20Sopenharmony_ci		if (strlen(peer_str) > 16)
4968c2ecf20Sopenharmony_ci			sprintf(peer_str, "%x", peer);
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci	/* Peer i/f name will be completed by reset/activate message */
4998c2ecf20Sopenharmony_ci	snprintf(l->name, sizeof(l->name), "%s:%s-%s:unknown",
5008c2ecf20Sopenharmony_ci		 self_str, if_name, peer_str);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	strcpy(l->if_name, if_name);
5038c2ecf20Sopenharmony_ci	l->addr = peer;
5048c2ecf20Sopenharmony_ci	l->peer_caps = peer_caps;
5058c2ecf20Sopenharmony_ci	l->net = net;
5068c2ecf20Sopenharmony_ci	l->in_session = false;
5078c2ecf20Sopenharmony_ci	l->bearer_id = bearer_id;
5088c2ecf20Sopenharmony_ci	l->tolerance = tolerance;
5098c2ecf20Sopenharmony_ci	if (bc_rcvlink)
5108c2ecf20Sopenharmony_ci		bc_rcvlink->tolerance = tolerance;
5118c2ecf20Sopenharmony_ci	l->net_plane = net_plane;
5128c2ecf20Sopenharmony_ci	l->advertised_mtu = mtu;
5138c2ecf20Sopenharmony_ci	l->mtu = mtu;
5148c2ecf20Sopenharmony_ci	l->priority = priority;
5158c2ecf20Sopenharmony_ci	tipc_link_set_queue_limits(l, min_win, max_win);
5168c2ecf20Sopenharmony_ci	l->ackers = 1;
5178c2ecf20Sopenharmony_ci	l->bc_sndlink = bc_sndlink;
5188c2ecf20Sopenharmony_ci	l->bc_rcvlink = bc_rcvlink;
5198c2ecf20Sopenharmony_ci	l->inputq = inputq;
5208c2ecf20Sopenharmony_ci	l->namedq = namedq;
5218c2ecf20Sopenharmony_ci	l->state = LINK_RESETTING;
5228c2ecf20Sopenharmony_ci	__skb_queue_head_init(&l->transmq);
5238c2ecf20Sopenharmony_ci	__skb_queue_head_init(&l->backlogq);
5248c2ecf20Sopenharmony_ci	__skb_queue_head_init(&l->deferdq);
5258c2ecf20Sopenharmony_ci	__skb_queue_head_init(&l->failover_deferdq);
5268c2ecf20Sopenharmony_ci	skb_queue_head_init(&l->wakeupq);
5278c2ecf20Sopenharmony_ci	skb_queue_head_init(l->inputq);
5288c2ecf20Sopenharmony_ci	return true;
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci/**
5328c2ecf20Sopenharmony_ci * tipc_link_bc_create - create new link to be used for broadcast
5338c2ecf20Sopenharmony_ci * @net: pointer to associated network namespace
5348c2ecf20Sopenharmony_ci * @mtu: mtu to be used initially if no peers
5358c2ecf20Sopenharmony_ci * @min_win: minimal send window to be used by link
5368c2ecf20Sopenharmony_ci * @max_win: maximal send window to be used by link
5378c2ecf20Sopenharmony_ci * @inputq: queue to put messages ready for delivery
5388c2ecf20Sopenharmony_ci * @namedq: queue to put binding table update messages ready for delivery
5398c2ecf20Sopenharmony_ci * @link: return value, pointer to put the created link
5408c2ecf20Sopenharmony_ci *
5418c2ecf20Sopenharmony_ci * Returns true if link was created, otherwise false
5428c2ecf20Sopenharmony_ci */
5438c2ecf20Sopenharmony_cibool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer, u8 *peer_id,
5448c2ecf20Sopenharmony_ci			 int mtu, u32 min_win, u32 max_win, u16 peer_caps,
5458c2ecf20Sopenharmony_ci			 struct sk_buff_head *inputq,
5468c2ecf20Sopenharmony_ci			 struct sk_buff_head *namedq,
5478c2ecf20Sopenharmony_ci			 struct tipc_link *bc_sndlink,
5488c2ecf20Sopenharmony_ci			 struct tipc_link **link)
5498c2ecf20Sopenharmony_ci{
5508c2ecf20Sopenharmony_ci	struct tipc_link *l;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	if (!tipc_link_create(net, "", MAX_BEARERS, 0, 'Z', mtu, 0, min_win,
5538c2ecf20Sopenharmony_ci			      max_win, 0, ownnode, peer, NULL, peer_caps,
5548c2ecf20Sopenharmony_ci			      bc_sndlink, NULL, inputq, namedq, link))
5558c2ecf20Sopenharmony_ci		return false;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	l = *link;
5588c2ecf20Sopenharmony_ci	if (peer_id) {
5598c2ecf20Sopenharmony_ci		char peer_str[NODE_ID_STR_LEN] = {0,};
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci		tipc_nodeid2string(peer_str, peer_id);
5628c2ecf20Sopenharmony_ci		if (strlen(peer_str) > 16)
5638c2ecf20Sopenharmony_ci			sprintf(peer_str, "%x", peer);
5648c2ecf20Sopenharmony_ci		/* Broadcast receiver link name: "broadcast-link:<peer>" */
5658c2ecf20Sopenharmony_ci		snprintf(l->name, sizeof(l->name), "%s:%s", tipc_bclink_name,
5668c2ecf20Sopenharmony_ci			 peer_str);
5678c2ecf20Sopenharmony_ci	} else {
5688c2ecf20Sopenharmony_ci		strcpy(l->name, tipc_bclink_name);
5698c2ecf20Sopenharmony_ci	}
5708c2ecf20Sopenharmony_ci	trace_tipc_link_reset(l, TIPC_DUMP_ALL, "bclink created!");
5718c2ecf20Sopenharmony_ci	tipc_link_reset(l);
5728c2ecf20Sopenharmony_ci	l->state = LINK_RESET;
5738c2ecf20Sopenharmony_ci	l->ackers = 0;
5748c2ecf20Sopenharmony_ci	l->bc_rcvlink = l;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	/* Broadcast send link is always up */
5778c2ecf20Sopenharmony_ci	if (link_is_bc_sndlink(l))
5788c2ecf20Sopenharmony_ci		l->state = LINK_ESTABLISHED;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	/* Disable replicast if even a single peer doesn't support it */
5818c2ecf20Sopenharmony_ci	if (link_is_bc_rcvlink(l) && !(peer_caps & TIPC_BCAST_RCAST))
5828c2ecf20Sopenharmony_ci		tipc_bcast_toggle_rcast(net, false);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	return true;
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci/**
5888c2ecf20Sopenharmony_ci * tipc_link_fsm_evt - link finite state machine
5898c2ecf20Sopenharmony_ci * @l: pointer to link
5908c2ecf20Sopenharmony_ci * @evt: state machine event to be processed
5918c2ecf20Sopenharmony_ci */
5928c2ecf20Sopenharmony_ciint tipc_link_fsm_evt(struct tipc_link *l, int evt)
5938c2ecf20Sopenharmony_ci{
5948c2ecf20Sopenharmony_ci	int rc = 0;
5958c2ecf20Sopenharmony_ci	int old_state = l->state;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	switch (l->state) {
5988c2ecf20Sopenharmony_ci	case LINK_RESETTING:
5998c2ecf20Sopenharmony_ci		switch (evt) {
6008c2ecf20Sopenharmony_ci		case LINK_PEER_RESET_EVT:
6018c2ecf20Sopenharmony_ci			l->state = LINK_PEER_RESET;
6028c2ecf20Sopenharmony_ci			break;
6038c2ecf20Sopenharmony_ci		case LINK_RESET_EVT:
6048c2ecf20Sopenharmony_ci			l->state = LINK_RESET;
6058c2ecf20Sopenharmony_ci			break;
6068c2ecf20Sopenharmony_ci		case LINK_FAILURE_EVT:
6078c2ecf20Sopenharmony_ci		case LINK_FAILOVER_BEGIN_EVT:
6088c2ecf20Sopenharmony_ci		case LINK_ESTABLISH_EVT:
6098c2ecf20Sopenharmony_ci		case LINK_FAILOVER_END_EVT:
6108c2ecf20Sopenharmony_ci		case LINK_SYNCH_BEGIN_EVT:
6118c2ecf20Sopenharmony_ci		case LINK_SYNCH_END_EVT:
6128c2ecf20Sopenharmony_ci		default:
6138c2ecf20Sopenharmony_ci			goto illegal_evt;
6148c2ecf20Sopenharmony_ci		}
6158c2ecf20Sopenharmony_ci		break;
6168c2ecf20Sopenharmony_ci	case LINK_RESET:
6178c2ecf20Sopenharmony_ci		switch (evt) {
6188c2ecf20Sopenharmony_ci		case LINK_PEER_RESET_EVT:
6198c2ecf20Sopenharmony_ci			l->state = LINK_ESTABLISHING;
6208c2ecf20Sopenharmony_ci			break;
6218c2ecf20Sopenharmony_ci		case LINK_FAILOVER_BEGIN_EVT:
6228c2ecf20Sopenharmony_ci			l->state = LINK_FAILINGOVER;
6238c2ecf20Sopenharmony_ci		case LINK_FAILURE_EVT:
6248c2ecf20Sopenharmony_ci		case LINK_RESET_EVT:
6258c2ecf20Sopenharmony_ci		case LINK_ESTABLISH_EVT:
6268c2ecf20Sopenharmony_ci		case LINK_FAILOVER_END_EVT:
6278c2ecf20Sopenharmony_ci			break;
6288c2ecf20Sopenharmony_ci		case LINK_SYNCH_BEGIN_EVT:
6298c2ecf20Sopenharmony_ci		case LINK_SYNCH_END_EVT:
6308c2ecf20Sopenharmony_ci		default:
6318c2ecf20Sopenharmony_ci			goto illegal_evt;
6328c2ecf20Sopenharmony_ci		}
6338c2ecf20Sopenharmony_ci		break;
6348c2ecf20Sopenharmony_ci	case LINK_PEER_RESET:
6358c2ecf20Sopenharmony_ci		switch (evt) {
6368c2ecf20Sopenharmony_ci		case LINK_RESET_EVT:
6378c2ecf20Sopenharmony_ci			l->state = LINK_ESTABLISHING;
6388c2ecf20Sopenharmony_ci			break;
6398c2ecf20Sopenharmony_ci		case LINK_PEER_RESET_EVT:
6408c2ecf20Sopenharmony_ci		case LINK_ESTABLISH_EVT:
6418c2ecf20Sopenharmony_ci		case LINK_FAILURE_EVT:
6428c2ecf20Sopenharmony_ci			break;
6438c2ecf20Sopenharmony_ci		case LINK_SYNCH_BEGIN_EVT:
6448c2ecf20Sopenharmony_ci		case LINK_SYNCH_END_EVT:
6458c2ecf20Sopenharmony_ci		case LINK_FAILOVER_BEGIN_EVT:
6468c2ecf20Sopenharmony_ci		case LINK_FAILOVER_END_EVT:
6478c2ecf20Sopenharmony_ci		default:
6488c2ecf20Sopenharmony_ci			goto illegal_evt;
6498c2ecf20Sopenharmony_ci		}
6508c2ecf20Sopenharmony_ci		break;
6518c2ecf20Sopenharmony_ci	case LINK_FAILINGOVER:
6528c2ecf20Sopenharmony_ci		switch (evt) {
6538c2ecf20Sopenharmony_ci		case LINK_FAILOVER_END_EVT:
6548c2ecf20Sopenharmony_ci			l->state = LINK_RESET;
6558c2ecf20Sopenharmony_ci			break;
6568c2ecf20Sopenharmony_ci		case LINK_PEER_RESET_EVT:
6578c2ecf20Sopenharmony_ci		case LINK_RESET_EVT:
6588c2ecf20Sopenharmony_ci		case LINK_ESTABLISH_EVT:
6598c2ecf20Sopenharmony_ci		case LINK_FAILURE_EVT:
6608c2ecf20Sopenharmony_ci			break;
6618c2ecf20Sopenharmony_ci		case LINK_FAILOVER_BEGIN_EVT:
6628c2ecf20Sopenharmony_ci		case LINK_SYNCH_BEGIN_EVT:
6638c2ecf20Sopenharmony_ci		case LINK_SYNCH_END_EVT:
6648c2ecf20Sopenharmony_ci		default:
6658c2ecf20Sopenharmony_ci			goto illegal_evt;
6668c2ecf20Sopenharmony_ci		}
6678c2ecf20Sopenharmony_ci		break;
6688c2ecf20Sopenharmony_ci	case LINK_ESTABLISHING:
6698c2ecf20Sopenharmony_ci		switch (evt) {
6708c2ecf20Sopenharmony_ci		case LINK_ESTABLISH_EVT:
6718c2ecf20Sopenharmony_ci			l->state = LINK_ESTABLISHED;
6728c2ecf20Sopenharmony_ci			break;
6738c2ecf20Sopenharmony_ci		case LINK_FAILOVER_BEGIN_EVT:
6748c2ecf20Sopenharmony_ci			l->state = LINK_FAILINGOVER;
6758c2ecf20Sopenharmony_ci			break;
6768c2ecf20Sopenharmony_ci		case LINK_RESET_EVT:
6778c2ecf20Sopenharmony_ci			l->state = LINK_RESET;
6788c2ecf20Sopenharmony_ci			break;
6798c2ecf20Sopenharmony_ci		case LINK_FAILURE_EVT:
6808c2ecf20Sopenharmony_ci		case LINK_PEER_RESET_EVT:
6818c2ecf20Sopenharmony_ci		case LINK_SYNCH_BEGIN_EVT:
6828c2ecf20Sopenharmony_ci		case LINK_FAILOVER_END_EVT:
6838c2ecf20Sopenharmony_ci			break;
6848c2ecf20Sopenharmony_ci		case LINK_SYNCH_END_EVT:
6858c2ecf20Sopenharmony_ci		default:
6868c2ecf20Sopenharmony_ci			goto illegal_evt;
6878c2ecf20Sopenharmony_ci		}
6888c2ecf20Sopenharmony_ci		break;
6898c2ecf20Sopenharmony_ci	case LINK_ESTABLISHED:
6908c2ecf20Sopenharmony_ci		switch (evt) {
6918c2ecf20Sopenharmony_ci		case LINK_PEER_RESET_EVT:
6928c2ecf20Sopenharmony_ci			l->state = LINK_PEER_RESET;
6938c2ecf20Sopenharmony_ci			rc |= TIPC_LINK_DOWN_EVT;
6948c2ecf20Sopenharmony_ci			break;
6958c2ecf20Sopenharmony_ci		case LINK_FAILURE_EVT:
6968c2ecf20Sopenharmony_ci			l->state = LINK_RESETTING;
6978c2ecf20Sopenharmony_ci			rc |= TIPC_LINK_DOWN_EVT;
6988c2ecf20Sopenharmony_ci			break;
6998c2ecf20Sopenharmony_ci		case LINK_RESET_EVT:
7008c2ecf20Sopenharmony_ci			l->state = LINK_RESET;
7018c2ecf20Sopenharmony_ci			break;
7028c2ecf20Sopenharmony_ci		case LINK_ESTABLISH_EVT:
7038c2ecf20Sopenharmony_ci		case LINK_SYNCH_END_EVT:
7048c2ecf20Sopenharmony_ci			break;
7058c2ecf20Sopenharmony_ci		case LINK_SYNCH_BEGIN_EVT:
7068c2ecf20Sopenharmony_ci			l->state = LINK_SYNCHING;
7078c2ecf20Sopenharmony_ci			break;
7088c2ecf20Sopenharmony_ci		case LINK_FAILOVER_BEGIN_EVT:
7098c2ecf20Sopenharmony_ci		case LINK_FAILOVER_END_EVT:
7108c2ecf20Sopenharmony_ci		default:
7118c2ecf20Sopenharmony_ci			goto illegal_evt;
7128c2ecf20Sopenharmony_ci		}
7138c2ecf20Sopenharmony_ci		break;
7148c2ecf20Sopenharmony_ci	case LINK_SYNCHING:
7158c2ecf20Sopenharmony_ci		switch (evt) {
7168c2ecf20Sopenharmony_ci		case LINK_PEER_RESET_EVT:
7178c2ecf20Sopenharmony_ci			l->state = LINK_PEER_RESET;
7188c2ecf20Sopenharmony_ci			rc |= TIPC_LINK_DOWN_EVT;
7198c2ecf20Sopenharmony_ci			break;
7208c2ecf20Sopenharmony_ci		case LINK_FAILURE_EVT:
7218c2ecf20Sopenharmony_ci			l->state = LINK_RESETTING;
7228c2ecf20Sopenharmony_ci			rc |= TIPC_LINK_DOWN_EVT;
7238c2ecf20Sopenharmony_ci			break;
7248c2ecf20Sopenharmony_ci		case LINK_RESET_EVT:
7258c2ecf20Sopenharmony_ci			l->state = LINK_RESET;
7268c2ecf20Sopenharmony_ci			break;
7278c2ecf20Sopenharmony_ci		case LINK_ESTABLISH_EVT:
7288c2ecf20Sopenharmony_ci		case LINK_SYNCH_BEGIN_EVT:
7298c2ecf20Sopenharmony_ci			break;
7308c2ecf20Sopenharmony_ci		case LINK_SYNCH_END_EVT:
7318c2ecf20Sopenharmony_ci			l->state = LINK_ESTABLISHED;
7328c2ecf20Sopenharmony_ci			break;
7338c2ecf20Sopenharmony_ci		case LINK_FAILOVER_BEGIN_EVT:
7348c2ecf20Sopenharmony_ci		case LINK_FAILOVER_END_EVT:
7358c2ecf20Sopenharmony_ci		default:
7368c2ecf20Sopenharmony_ci			goto illegal_evt;
7378c2ecf20Sopenharmony_ci		}
7388c2ecf20Sopenharmony_ci		break;
7398c2ecf20Sopenharmony_ci	default:
7408c2ecf20Sopenharmony_ci		pr_err("Unknown FSM state %x in %s\n", l->state, l->name);
7418c2ecf20Sopenharmony_ci	}
7428c2ecf20Sopenharmony_ci	trace_tipc_link_fsm(l->name, old_state, l->state, evt);
7438c2ecf20Sopenharmony_ci	return rc;
7448c2ecf20Sopenharmony_ciillegal_evt:
7458c2ecf20Sopenharmony_ci	pr_err("Illegal FSM event %x in state %x on link %s\n",
7468c2ecf20Sopenharmony_ci	       evt, l->state, l->name);
7478c2ecf20Sopenharmony_ci	trace_tipc_link_fsm(l->name, old_state, l->state, evt);
7488c2ecf20Sopenharmony_ci	return rc;
7498c2ecf20Sopenharmony_ci}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci/* link_profile_stats - update statistical profiling of traffic
7528c2ecf20Sopenharmony_ci */
7538c2ecf20Sopenharmony_cistatic void link_profile_stats(struct tipc_link *l)
7548c2ecf20Sopenharmony_ci{
7558c2ecf20Sopenharmony_ci	struct sk_buff *skb;
7568c2ecf20Sopenharmony_ci	struct tipc_msg *msg;
7578c2ecf20Sopenharmony_ci	int length;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	/* Update counters used in statistical profiling of send traffic */
7608c2ecf20Sopenharmony_ci	l->stats.accu_queue_sz += skb_queue_len(&l->transmq);
7618c2ecf20Sopenharmony_ci	l->stats.queue_sz_counts++;
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	skb = skb_peek(&l->transmq);
7648c2ecf20Sopenharmony_ci	if (!skb)
7658c2ecf20Sopenharmony_ci		return;
7668c2ecf20Sopenharmony_ci	msg = buf_msg(skb);
7678c2ecf20Sopenharmony_ci	length = msg_size(msg);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	if (msg_user(msg) == MSG_FRAGMENTER) {
7708c2ecf20Sopenharmony_ci		if (msg_type(msg) != FIRST_FRAGMENT)
7718c2ecf20Sopenharmony_ci			return;
7728c2ecf20Sopenharmony_ci		length = msg_size(msg_inner_hdr(msg));
7738c2ecf20Sopenharmony_ci	}
7748c2ecf20Sopenharmony_ci	l->stats.msg_lengths_total += length;
7758c2ecf20Sopenharmony_ci	l->stats.msg_length_counts++;
7768c2ecf20Sopenharmony_ci	if (length <= 64)
7778c2ecf20Sopenharmony_ci		l->stats.msg_length_profile[0]++;
7788c2ecf20Sopenharmony_ci	else if (length <= 256)
7798c2ecf20Sopenharmony_ci		l->stats.msg_length_profile[1]++;
7808c2ecf20Sopenharmony_ci	else if (length <= 1024)
7818c2ecf20Sopenharmony_ci		l->stats.msg_length_profile[2]++;
7828c2ecf20Sopenharmony_ci	else if (length <= 4096)
7838c2ecf20Sopenharmony_ci		l->stats.msg_length_profile[3]++;
7848c2ecf20Sopenharmony_ci	else if (length <= 16384)
7858c2ecf20Sopenharmony_ci		l->stats.msg_length_profile[4]++;
7868c2ecf20Sopenharmony_ci	else if (length <= 32768)
7878c2ecf20Sopenharmony_ci		l->stats.msg_length_profile[5]++;
7888c2ecf20Sopenharmony_ci	else
7898c2ecf20Sopenharmony_ci		l->stats.msg_length_profile[6]++;
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci/**
7938c2ecf20Sopenharmony_ci * tipc_link_too_silent - check if link is "too silent"
7948c2ecf20Sopenharmony_ci * @l: tipc link to be checked
7958c2ecf20Sopenharmony_ci *
7968c2ecf20Sopenharmony_ci * Returns true if the link 'silent_intv_cnt' is about to reach the
7978c2ecf20Sopenharmony_ci * 'abort_limit' value, otherwise false
7988c2ecf20Sopenharmony_ci */
7998c2ecf20Sopenharmony_cibool tipc_link_too_silent(struct tipc_link *l)
8008c2ecf20Sopenharmony_ci{
8018c2ecf20Sopenharmony_ci	return (l->silent_intv_cnt + 2 > l->abort_limit);
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci/* tipc_link_timeout - perform periodic task as instructed from node timeout
8058c2ecf20Sopenharmony_ci */
8068c2ecf20Sopenharmony_ciint tipc_link_timeout(struct tipc_link *l, struct sk_buff_head *xmitq)
8078c2ecf20Sopenharmony_ci{
8088c2ecf20Sopenharmony_ci	int mtyp = 0;
8098c2ecf20Sopenharmony_ci	int rc = 0;
8108c2ecf20Sopenharmony_ci	bool state = false;
8118c2ecf20Sopenharmony_ci	bool probe = false;
8128c2ecf20Sopenharmony_ci	bool setup = false;
8138c2ecf20Sopenharmony_ci	u16 bc_snt = l->bc_sndlink->snd_nxt - 1;
8148c2ecf20Sopenharmony_ci	u16 bc_acked = l->bc_rcvlink->acked;
8158c2ecf20Sopenharmony_ci	struct tipc_mon_state *mstate = &l->mon_state;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	trace_tipc_link_timeout(l, TIPC_DUMP_NONE, " ");
8188c2ecf20Sopenharmony_ci	trace_tipc_link_too_silent(l, TIPC_DUMP_ALL, " ");
8198c2ecf20Sopenharmony_ci	switch (l->state) {
8208c2ecf20Sopenharmony_ci	case LINK_ESTABLISHED:
8218c2ecf20Sopenharmony_ci	case LINK_SYNCHING:
8228c2ecf20Sopenharmony_ci		mtyp = STATE_MSG;
8238c2ecf20Sopenharmony_ci		link_profile_stats(l);
8248c2ecf20Sopenharmony_ci		tipc_mon_get_state(l->net, l->addr, mstate, l->bearer_id);
8258c2ecf20Sopenharmony_ci		if (mstate->reset || (l->silent_intv_cnt > l->abort_limit))
8268c2ecf20Sopenharmony_ci			return tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
8278c2ecf20Sopenharmony_ci		state = bc_acked != bc_snt;
8288c2ecf20Sopenharmony_ci		state |= l->bc_rcvlink->rcv_unacked;
8298c2ecf20Sopenharmony_ci		state |= l->rcv_unacked;
8308c2ecf20Sopenharmony_ci		state |= !skb_queue_empty(&l->transmq);
8318c2ecf20Sopenharmony_ci		probe = mstate->probing;
8328c2ecf20Sopenharmony_ci		probe |= l->silent_intv_cnt;
8338c2ecf20Sopenharmony_ci		if (probe || mstate->monitoring)
8348c2ecf20Sopenharmony_ci			l->silent_intv_cnt++;
8358c2ecf20Sopenharmony_ci		probe |= !skb_queue_empty(&l->deferdq);
8368c2ecf20Sopenharmony_ci		if (l->snd_nxt == l->checkpoint) {
8378c2ecf20Sopenharmony_ci			tipc_link_update_cwin(l, 0, 0);
8388c2ecf20Sopenharmony_ci			probe = true;
8398c2ecf20Sopenharmony_ci		}
8408c2ecf20Sopenharmony_ci		l->checkpoint = l->snd_nxt;
8418c2ecf20Sopenharmony_ci		break;
8428c2ecf20Sopenharmony_ci	case LINK_RESET:
8438c2ecf20Sopenharmony_ci		setup = l->rst_cnt++ <= 4;
8448c2ecf20Sopenharmony_ci		setup |= !(l->rst_cnt % 16);
8458c2ecf20Sopenharmony_ci		mtyp = RESET_MSG;
8468c2ecf20Sopenharmony_ci		break;
8478c2ecf20Sopenharmony_ci	case LINK_ESTABLISHING:
8488c2ecf20Sopenharmony_ci		setup = true;
8498c2ecf20Sopenharmony_ci		mtyp = ACTIVATE_MSG;
8508c2ecf20Sopenharmony_ci		break;
8518c2ecf20Sopenharmony_ci	case LINK_PEER_RESET:
8528c2ecf20Sopenharmony_ci	case LINK_RESETTING:
8538c2ecf20Sopenharmony_ci	case LINK_FAILINGOVER:
8548c2ecf20Sopenharmony_ci		break;
8558c2ecf20Sopenharmony_ci	default:
8568c2ecf20Sopenharmony_ci		break;
8578c2ecf20Sopenharmony_ci	}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	if (state || probe || setup)
8608c2ecf20Sopenharmony_ci		tipc_link_build_proto_msg(l, mtyp, probe, 0, 0, 0, 0, xmitq);
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	return rc;
8638c2ecf20Sopenharmony_ci}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci/**
8668c2ecf20Sopenharmony_ci * link_schedule_user - schedule a message sender for wakeup after congestion
8678c2ecf20Sopenharmony_ci * @l: congested link
8688c2ecf20Sopenharmony_ci * @hdr: header of message that is being sent
8698c2ecf20Sopenharmony_ci * Create pseudo msg to send back to user when congestion abates
8708c2ecf20Sopenharmony_ci */
8718c2ecf20Sopenharmony_cistatic int link_schedule_user(struct tipc_link *l, struct tipc_msg *hdr)
8728c2ecf20Sopenharmony_ci{
8738c2ecf20Sopenharmony_ci	u32 dnode = tipc_own_addr(l->net);
8748c2ecf20Sopenharmony_ci	u32 dport = msg_origport(hdr);
8758c2ecf20Sopenharmony_ci	struct sk_buff *skb;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	/* Create and schedule wakeup pseudo message */
8788c2ecf20Sopenharmony_ci	skb = tipc_msg_create(SOCK_WAKEUP, 0, INT_H_SIZE, 0,
8798c2ecf20Sopenharmony_ci			      dnode, l->addr, dport, 0, 0);
8808c2ecf20Sopenharmony_ci	if (!skb)
8818c2ecf20Sopenharmony_ci		return -ENOBUFS;
8828c2ecf20Sopenharmony_ci	msg_set_dest_droppable(buf_msg(skb), true);
8838c2ecf20Sopenharmony_ci	TIPC_SKB_CB(skb)->chain_imp = msg_importance(hdr);
8848c2ecf20Sopenharmony_ci	skb_queue_tail(&l->wakeupq, skb);
8858c2ecf20Sopenharmony_ci	l->stats.link_congs++;
8868c2ecf20Sopenharmony_ci	trace_tipc_link_conges(l, TIPC_DUMP_ALL, "wakeup scheduled!");
8878c2ecf20Sopenharmony_ci	return -ELINKCONG;
8888c2ecf20Sopenharmony_ci}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci/**
8918c2ecf20Sopenharmony_ci * link_prepare_wakeup - prepare users for wakeup after congestion
8928c2ecf20Sopenharmony_ci * @l: congested link
8938c2ecf20Sopenharmony_ci * Wake up a number of waiting users, as permitted by available space
8948c2ecf20Sopenharmony_ci * in the send queue
8958c2ecf20Sopenharmony_ci */
8968c2ecf20Sopenharmony_cistatic void link_prepare_wakeup(struct tipc_link *l)
8978c2ecf20Sopenharmony_ci{
8988c2ecf20Sopenharmony_ci	struct sk_buff_head *wakeupq = &l->wakeupq;
8998c2ecf20Sopenharmony_ci	struct sk_buff_head *inputq = l->inputq;
9008c2ecf20Sopenharmony_ci	struct sk_buff *skb, *tmp;
9018c2ecf20Sopenharmony_ci	struct sk_buff_head tmpq;
9028c2ecf20Sopenharmony_ci	int avail[5] = {0,};
9038c2ecf20Sopenharmony_ci	int imp = 0;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	__skb_queue_head_init(&tmpq);
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	for (; imp <= TIPC_SYSTEM_IMPORTANCE; imp++)
9088c2ecf20Sopenharmony_ci		avail[imp] = l->backlog[imp].limit - l->backlog[imp].len;
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	skb_queue_walk_safe(wakeupq, skb, tmp) {
9118c2ecf20Sopenharmony_ci		imp = TIPC_SKB_CB(skb)->chain_imp;
9128c2ecf20Sopenharmony_ci		if (avail[imp] <= 0)
9138c2ecf20Sopenharmony_ci			continue;
9148c2ecf20Sopenharmony_ci		avail[imp]--;
9158c2ecf20Sopenharmony_ci		__skb_unlink(skb, wakeupq);
9168c2ecf20Sopenharmony_ci		__skb_queue_tail(&tmpq, skb);
9178c2ecf20Sopenharmony_ci	}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	spin_lock_bh(&inputq->lock);
9208c2ecf20Sopenharmony_ci	skb_queue_splice_tail(&tmpq, inputq);
9218c2ecf20Sopenharmony_ci	spin_unlock_bh(&inputq->lock);
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci/**
9268c2ecf20Sopenharmony_ci * tipc_link_set_skb_retransmit_time - set the time at which retransmission of
9278c2ecf20Sopenharmony_ci *                                     the given skb should be next attempted
9288c2ecf20Sopenharmony_ci * @skb: skb to set a future retransmission time for
9298c2ecf20Sopenharmony_ci * @l: link the skb will be transmitted on
9308c2ecf20Sopenharmony_ci */
9318c2ecf20Sopenharmony_cistatic void tipc_link_set_skb_retransmit_time(struct sk_buff *skb,
9328c2ecf20Sopenharmony_ci					      struct tipc_link *l)
9338c2ecf20Sopenharmony_ci{
9348c2ecf20Sopenharmony_ci	if (link_is_bc_sndlink(l))
9358c2ecf20Sopenharmony_ci		TIPC_SKB_CB(skb)->nxt_retr = TIPC_BC_RETR_LIM;
9368c2ecf20Sopenharmony_ci	else
9378c2ecf20Sopenharmony_ci		TIPC_SKB_CB(skb)->nxt_retr = TIPC_UC_RETR_TIME;
9388c2ecf20Sopenharmony_ci}
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_civoid tipc_link_reset(struct tipc_link *l)
9418c2ecf20Sopenharmony_ci{
9428c2ecf20Sopenharmony_ci	struct sk_buff_head list;
9438c2ecf20Sopenharmony_ci	u32 imp;
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	__skb_queue_head_init(&list);
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	l->in_session = false;
9488c2ecf20Sopenharmony_ci	/* Force re-synch of peer session number before establishing */
9498c2ecf20Sopenharmony_ci	l->peer_session--;
9508c2ecf20Sopenharmony_ci	l->session++;
9518c2ecf20Sopenharmony_ci	l->mtu = l->advertised_mtu;
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	spin_lock_bh(&l->wakeupq.lock);
9548c2ecf20Sopenharmony_ci	skb_queue_splice_init(&l->wakeupq, &list);
9558c2ecf20Sopenharmony_ci	spin_unlock_bh(&l->wakeupq.lock);
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	spin_lock_bh(&l->inputq->lock);
9588c2ecf20Sopenharmony_ci	skb_queue_splice_init(&list, l->inputq);
9598c2ecf20Sopenharmony_ci	spin_unlock_bh(&l->inputq->lock);
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	__skb_queue_purge(&l->transmq);
9628c2ecf20Sopenharmony_ci	__skb_queue_purge(&l->deferdq);
9638c2ecf20Sopenharmony_ci	__skb_queue_purge(&l->backlogq);
9648c2ecf20Sopenharmony_ci	__skb_queue_purge(&l->failover_deferdq);
9658c2ecf20Sopenharmony_ci	for (imp = 0; imp <= TIPC_SYSTEM_IMPORTANCE; imp++) {
9668c2ecf20Sopenharmony_ci		l->backlog[imp].len = 0;
9678c2ecf20Sopenharmony_ci		l->backlog[imp].target_bskb = NULL;
9688c2ecf20Sopenharmony_ci	}
9698c2ecf20Sopenharmony_ci	kfree_skb(l->reasm_buf);
9708c2ecf20Sopenharmony_ci	kfree_skb(l->reasm_tnlmsg);
9718c2ecf20Sopenharmony_ci	kfree_skb(l->failover_reasm_skb);
9728c2ecf20Sopenharmony_ci	l->reasm_buf = NULL;
9738c2ecf20Sopenharmony_ci	l->reasm_tnlmsg = NULL;
9748c2ecf20Sopenharmony_ci	l->failover_reasm_skb = NULL;
9758c2ecf20Sopenharmony_ci	l->rcv_unacked = 0;
9768c2ecf20Sopenharmony_ci	l->snd_nxt = 1;
9778c2ecf20Sopenharmony_ci	l->rcv_nxt = 1;
9788c2ecf20Sopenharmony_ci	l->snd_nxt_state = 1;
9798c2ecf20Sopenharmony_ci	l->rcv_nxt_state = 1;
9808c2ecf20Sopenharmony_ci	l->acked = 0;
9818c2ecf20Sopenharmony_ci	l->last_gap = 0;
9828c2ecf20Sopenharmony_ci	kfree(l->last_ga);
9838c2ecf20Sopenharmony_ci	l->last_ga = NULL;
9848c2ecf20Sopenharmony_ci	l->silent_intv_cnt = 0;
9858c2ecf20Sopenharmony_ci	l->rst_cnt = 0;
9868c2ecf20Sopenharmony_ci	l->bc_peer_is_up = false;
9878c2ecf20Sopenharmony_ci	memset(&l->mon_state, 0, sizeof(l->mon_state));
9888c2ecf20Sopenharmony_ci	tipc_link_reset_stats(l);
9898c2ecf20Sopenharmony_ci}
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci/**
9928c2ecf20Sopenharmony_ci * tipc_link_xmit(): enqueue buffer list according to queue situation
9938c2ecf20Sopenharmony_ci * @l: link to use
9948c2ecf20Sopenharmony_ci * @list: chain of buffers containing message
9958c2ecf20Sopenharmony_ci * @xmitq: returned list of packets to be sent by caller
9968c2ecf20Sopenharmony_ci *
9978c2ecf20Sopenharmony_ci * Consumes the buffer chain.
9988c2ecf20Sopenharmony_ci * Returns 0 if success, or errno: -ELINKCONG, -EMSGSIZE or -ENOBUFS
9998c2ecf20Sopenharmony_ci * Messages at TIPC_SYSTEM_IMPORTANCE are always accepted
10008c2ecf20Sopenharmony_ci */
10018c2ecf20Sopenharmony_ciint tipc_link_xmit(struct tipc_link *l, struct sk_buff_head *list,
10028c2ecf20Sopenharmony_ci		   struct sk_buff_head *xmitq)
10038c2ecf20Sopenharmony_ci{
10048c2ecf20Sopenharmony_ci	struct sk_buff_head *backlogq = &l->backlogq;
10058c2ecf20Sopenharmony_ci	struct sk_buff_head *transmq = &l->transmq;
10068c2ecf20Sopenharmony_ci	struct sk_buff *skb, *_skb;
10078c2ecf20Sopenharmony_ci	u16 bc_ack = l->bc_rcvlink->rcv_nxt - 1;
10088c2ecf20Sopenharmony_ci	u16 ack = l->rcv_nxt - 1;
10098c2ecf20Sopenharmony_ci	u16 seqno = l->snd_nxt;
10108c2ecf20Sopenharmony_ci	int pkt_cnt = skb_queue_len(list);
10118c2ecf20Sopenharmony_ci	unsigned int mss = tipc_link_mss(l);
10128c2ecf20Sopenharmony_ci	unsigned int cwin = l->window;
10138c2ecf20Sopenharmony_ci	unsigned int mtu = l->mtu;
10148c2ecf20Sopenharmony_ci	struct tipc_msg *hdr;
10158c2ecf20Sopenharmony_ci	bool new_bundle;
10168c2ecf20Sopenharmony_ci	int rc = 0;
10178c2ecf20Sopenharmony_ci	int imp;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	if (pkt_cnt <= 0)
10208c2ecf20Sopenharmony_ci		return 0;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	hdr = buf_msg(skb_peek(list));
10238c2ecf20Sopenharmony_ci	if (unlikely(msg_size(hdr) > mtu)) {
10248c2ecf20Sopenharmony_ci		pr_warn("Too large msg, purging xmit list %d %d %d %d %d!\n",
10258c2ecf20Sopenharmony_ci			skb_queue_len(list), msg_user(hdr),
10268c2ecf20Sopenharmony_ci			msg_type(hdr), msg_size(hdr), mtu);
10278c2ecf20Sopenharmony_ci		__skb_queue_purge(list);
10288c2ecf20Sopenharmony_ci		return -EMSGSIZE;
10298c2ecf20Sopenharmony_ci	}
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	imp = msg_importance(hdr);
10328c2ecf20Sopenharmony_ci	/* Allow oversubscription of one data msg per source at congestion */
10338c2ecf20Sopenharmony_ci	if (unlikely(l->backlog[imp].len >= l->backlog[imp].limit)) {
10348c2ecf20Sopenharmony_ci		if (imp == TIPC_SYSTEM_IMPORTANCE) {
10358c2ecf20Sopenharmony_ci			pr_warn("%s<%s>, link overflow", link_rst_msg, l->name);
10368c2ecf20Sopenharmony_ci			return -ENOBUFS;
10378c2ecf20Sopenharmony_ci		}
10388c2ecf20Sopenharmony_ci		rc = link_schedule_user(l, hdr);
10398c2ecf20Sopenharmony_ci	}
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	if (pkt_cnt > 1) {
10428c2ecf20Sopenharmony_ci		l->stats.sent_fragmented++;
10438c2ecf20Sopenharmony_ci		l->stats.sent_fragments += pkt_cnt;
10448c2ecf20Sopenharmony_ci	}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	/* Prepare each packet for sending, and add to relevant queue: */
10478c2ecf20Sopenharmony_ci	while ((skb = __skb_dequeue(list))) {
10488c2ecf20Sopenharmony_ci		if (likely(skb_queue_len(transmq) < cwin)) {
10498c2ecf20Sopenharmony_ci			hdr = buf_msg(skb);
10508c2ecf20Sopenharmony_ci			msg_set_seqno(hdr, seqno);
10518c2ecf20Sopenharmony_ci			msg_set_ack(hdr, ack);
10528c2ecf20Sopenharmony_ci			msg_set_bcast_ack(hdr, bc_ack);
10538c2ecf20Sopenharmony_ci			_skb = skb_clone(skb, GFP_ATOMIC);
10548c2ecf20Sopenharmony_ci			if (!_skb) {
10558c2ecf20Sopenharmony_ci				kfree_skb(skb);
10568c2ecf20Sopenharmony_ci				__skb_queue_purge(list);
10578c2ecf20Sopenharmony_ci				return -ENOBUFS;
10588c2ecf20Sopenharmony_ci			}
10598c2ecf20Sopenharmony_ci			__skb_queue_tail(transmq, skb);
10608c2ecf20Sopenharmony_ci			tipc_link_set_skb_retransmit_time(skb, l);
10618c2ecf20Sopenharmony_ci			__skb_queue_tail(xmitq, _skb);
10628c2ecf20Sopenharmony_ci			TIPC_SKB_CB(skb)->ackers = l->ackers;
10638c2ecf20Sopenharmony_ci			l->rcv_unacked = 0;
10648c2ecf20Sopenharmony_ci			l->stats.sent_pkts++;
10658c2ecf20Sopenharmony_ci			seqno++;
10668c2ecf20Sopenharmony_ci			continue;
10678c2ecf20Sopenharmony_ci		}
10688c2ecf20Sopenharmony_ci		if (tipc_msg_try_bundle(l->backlog[imp].target_bskb, &skb,
10698c2ecf20Sopenharmony_ci					mss, l->addr, &new_bundle)) {
10708c2ecf20Sopenharmony_ci			if (skb) {
10718c2ecf20Sopenharmony_ci				/* Keep a ref. to the skb for next try */
10728c2ecf20Sopenharmony_ci				l->backlog[imp].target_bskb = skb;
10738c2ecf20Sopenharmony_ci				l->backlog[imp].len++;
10748c2ecf20Sopenharmony_ci				__skb_queue_tail(backlogq, skb);
10758c2ecf20Sopenharmony_ci			} else {
10768c2ecf20Sopenharmony_ci				if (new_bundle) {
10778c2ecf20Sopenharmony_ci					l->stats.sent_bundles++;
10788c2ecf20Sopenharmony_ci					l->stats.sent_bundled++;
10798c2ecf20Sopenharmony_ci				}
10808c2ecf20Sopenharmony_ci				l->stats.sent_bundled++;
10818c2ecf20Sopenharmony_ci			}
10828c2ecf20Sopenharmony_ci			continue;
10838c2ecf20Sopenharmony_ci		}
10848c2ecf20Sopenharmony_ci		l->backlog[imp].target_bskb = NULL;
10858c2ecf20Sopenharmony_ci		l->backlog[imp].len += (1 + skb_queue_len(list));
10868c2ecf20Sopenharmony_ci		__skb_queue_tail(backlogq, skb);
10878c2ecf20Sopenharmony_ci		skb_queue_splice_tail_init(list, backlogq);
10888c2ecf20Sopenharmony_ci	}
10898c2ecf20Sopenharmony_ci	l->snd_nxt = seqno;
10908c2ecf20Sopenharmony_ci	return rc;
10918c2ecf20Sopenharmony_ci}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_cistatic void tipc_link_update_cwin(struct tipc_link *l, int released,
10948c2ecf20Sopenharmony_ci				  bool retransmitted)
10958c2ecf20Sopenharmony_ci{
10968c2ecf20Sopenharmony_ci	int bklog_len = skb_queue_len(&l->backlogq);
10978c2ecf20Sopenharmony_ci	struct sk_buff_head *txq = &l->transmq;
10988c2ecf20Sopenharmony_ci	int txq_len = skb_queue_len(txq);
10998c2ecf20Sopenharmony_ci	u16 cwin = l->window;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	/* Enter fast recovery */
11028c2ecf20Sopenharmony_ci	if (unlikely(retransmitted)) {
11038c2ecf20Sopenharmony_ci		l->ssthresh = max_t(u16, l->window / 2, 300);
11048c2ecf20Sopenharmony_ci		l->window = min_t(u16, l->ssthresh, l->window);
11058c2ecf20Sopenharmony_ci		return;
11068c2ecf20Sopenharmony_ci	}
11078c2ecf20Sopenharmony_ci	/* Enter slow start */
11088c2ecf20Sopenharmony_ci	if (unlikely(!released)) {
11098c2ecf20Sopenharmony_ci		l->ssthresh = max_t(u16, l->window / 2, 300);
11108c2ecf20Sopenharmony_ci		l->window = l->min_win;
11118c2ecf20Sopenharmony_ci		return;
11128c2ecf20Sopenharmony_ci	}
11138c2ecf20Sopenharmony_ci	/* Don't increase window if no pressure on the transmit queue */
11148c2ecf20Sopenharmony_ci	if (txq_len + bklog_len < cwin)
11158c2ecf20Sopenharmony_ci		return;
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	/* Don't increase window if there are holes the transmit queue */
11188c2ecf20Sopenharmony_ci	if (txq_len && l->snd_nxt - buf_seqno(skb_peek(txq)) != txq_len)
11198c2ecf20Sopenharmony_ci		return;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	l->cong_acks += released;
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	/* Slow start  */
11248c2ecf20Sopenharmony_ci	if (cwin <= l->ssthresh) {
11258c2ecf20Sopenharmony_ci		l->window = min_t(u16, cwin + released, l->max_win);
11268c2ecf20Sopenharmony_ci		return;
11278c2ecf20Sopenharmony_ci	}
11288c2ecf20Sopenharmony_ci	/* Congestion avoidance */
11298c2ecf20Sopenharmony_ci	if (l->cong_acks < cwin)
11308c2ecf20Sopenharmony_ci		return;
11318c2ecf20Sopenharmony_ci	l->window = min_t(u16, ++cwin, l->max_win);
11328c2ecf20Sopenharmony_ci	l->cong_acks = 0;
11338c2ecf20Sopenharmony_ci}
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_cistatic void tipc_link_advance_backlog(struct tipc_link *l,
11368c2ecf20Sopenharmony_ci				      struct sk_buff_head *xmitq)
11378c2ecf20Sopenharmony_ci{
11388c2ecf20Sopenharmony_ci	u16 bc_ack = l->bc_rcvlink->rcv_nxt - 1;
11398c2ecf20Sopenharmony_ci	struct sk_buff_head *txq = &l->transmq;
11408c2ecf20Sopenharmony_ci	struct sk_buff *skb, *_skb;
11418c2ecf20Sopenharmony_ci	u16 ack = l->rcv_nxt - 1;
11428c2ecf20Sopenharmony_ci	u16 seqno = l->snd_nxt;
11438c2ecf20Sopenharmony_ci	struct tipc_msg *hdr;
11448c2ecf20Sopenharmony_ci	u16 cwin = l->window;
11458c2ecf20Sopenharmony_ci	u32 imp;
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	while (skb_queue_len(txq) < cwin) {
11488c2ecf20Sopenharmony_ci		skb = skb_peek(&l->backlogq);
11498c2ecf20Sopenharmony_ci		if (!skb)
11508c2ecf20Sopenharmony_ci			break;
11518c2ecf20Sopenharmony_ci		_skb = skb_clone(skb, GFP_ATOMIC);
11528c2ecf20Sopenharmony_ci		if (!_skb)
11538c2ecf20Sopenharmony_ci			break;
11548c2ecf20Sopenharmony_ci		__skb_dequeue(&l->backlogq);
11558c2ecf20Sopenharmony_ci		hdr = buf_msg(skb);
11568c2ecf20Sopenharmony_ci		imp = msg_importance(hdr);
11578c2ecf20Sopenharmony_ci		l->backlog[imp].len--;
11588c2ecf20Sopenharmony_ci		if (unlikely(skb == l->backlog[imp].target_bskb))
11598c2ecf20Sopenharmony_ci			l->backlog[imp].target_bskb = NULL;
11608c2ecf20Sopenharmony_ci		__skb_queue_tail(&l->transmq, skb);
11618c2ecf20Sopenharmony_ci		tipc_link_set_skb_retransmit_time(skb, l);
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci		__skb_queue_tail(xmitq, _skb);
11648c2ecf20Sopenharmony_ci		TIPC_SKB_CB(skb)->ackers = l->ackers;
11658c2ecf20Sopenharmony_ci		msg_set_seqno(hdr, seqno);
11668c2ecf20Sopenharmony_ci		msg_set_ack(hdr, ack);
11678c2ecf20Sopenharmony_ci		msg_set_bcast_ack(hdr, bc_ack);
11688c2ecf20Sopenharmony_ci		l->rcv_unacked = 0;
11698c2ecf20Sopenharmony_ci		l->stats.sent_pkts++;
11708c2ecf20Sopenharmony_ci		seqno++;
11718c2ecf20Sopenharmony_ci	}
11728c2ecf20Sopenharmony_ci	l->snd_nxt = seqno;
11738c2ecf20Sopenharmony_ci}
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci/**
11768c2ecf20Sopenharmony_ci * link_retransmit_failure() - Detect repeated retransmit failures
11778c2ecf20Sopenharmony_ci * @l: tipc link sender
11788c2ecf20Sopenharmony_ci * @r: tipc link receiver (= l in case of unicast)
11798c2ecf20Sopenharmony_ci * @rc: returned code
11808c2ecf20Sopenharmony_ci *
11818c2ecf20Sopenharmony_ci * Return: true if the repeated retransmit failures happens, otherwise
11828c2ecf20Sopenharmony_ci * false
11838c2ecf20Sopenharmony_ci */
11848c2ecf20Sopenharmony_cistatic bool link_retransmit_failure(struct tipc_link *l, struct tipc_link *r,
11858c2ecf20Sopenharmony_ci				    int *rc)
11868c2ecf20Sopenharmony_ci{
11878c2ecf20Sopenharmony_ci	struct sk_buff *skb = skb_peek(&l->transmq);
11888c2ecf20Sopenharmony_ci	struct tipc_msg *hdr;
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	if (!skb)
11918c2ecf20Sopenharmony_ci		return false;
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	if (!TIPC_SKB_CB(skb)->retr_cnt)
11948c2ecf20Sopenharmony_ci		return false;
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	if (!time_after(jiffies, TIPC_SKB_CB(skb)->retr_stamp +
11978c2ecf20Sopenharmony_ci			msecs_to_jiffies(r->tolerance * 10)))
11988c2ecf20Sopenharmony_ci		return false;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	hdr = buf_msg(skb);
12018c2ecf20Sopenharmony_ci	if (link_is_bc_sndlink(l) && !less(r->acked, msg_seqno(hdr)))
12028c2ecf20Sopenharmony_ci		return false;
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	pr_warn("Retransmission failure on link <%s>\n", l->name);
12058c2ecf20Sopenharmony_ci	link_print(l, "State of link ");
12068c2ecf20Sopenharmony_ci	pr_info("Failed msg: usr %u, typ %u, len %u, err %u\n",
12078c2ecf20Sopenharmony_ci		msg_user(hdr), msg_type(hdr), msg_size(hdr), msg_errcode(hdr));
12088c2ecf20Sopenharmony_ci	pr_info("sqno %u, prev: %x, dest: %x\n",
12098c2ecf20Sopenharmony_ci		msg_seqno(hdr), msg_prevnode(hdr), msg_destnode(hdr));
12108c2ecf20Sopenharmony_ci	pr_info("retr_stamp %d, retr_cnt %d\n",
12118c2ecf20Sopenharmony_ci		jiffies_to_msecs(TIPC_SKB_CB(skb)->retr_stamp),
12128c2ecf20Sopenharmony_ci		TIPC_SKB_CB(skb)->retr_cnt);
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	trace_tipc_list_dump(&l->transmq, true, "retrans failure!");
12158c2ecf20Sopenharmony_ci	trace_tipc_link_dump(l, TIPC_DUMP_NONE, "retrans failure!");
12168c2ecf20Sopenharmony_ci	trace_tipc_link_dump(r, TIPC_DUMP_NONE, "retrans failure!");
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	if (link_is_bc_sndlink(l)) {
12198c2ecf20Sopenharmony_ci		r->state = LINK_RESET;
12208c2ecf20Sopenharmony_ci		*rc |= TIPC_LINK_DOWN_EVT;
12218c2ecf20Sopenharmony_ci	} else {
12228c2ecf20Sopenharmony_ci		*rc |= tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
12238c2ecf20Sopenharmony_ci	}
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	return true;
12268c2ecf20Sopenharmony_ci}
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci/* tipc_data_input - deliver data and name distr msgs to upper layer
12298c2ecf20Sopenharmony_ci *
12308c2ecf20Sopenharmony_ci * Consumes buffer if message is of right type
12318c2ecf20Sopenharmony_ci * Node lock must be held
12328c2ecf20Sopenharmony_ci */
12338c2ecf20Sopenharmony_cistatic bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb,
12348c2ecf20Sopenharmony_ci			    struct sk_buff_head *inputq)
12358c2ecf20Sopenharmony_ci{
12368c2ecf20Sopenharmony_ci	struct sk_buff_head *mc_inputq = l->bc_rcvlink->inputq;
12378c2ecf20Sopenharmony_ci	struct tipc_msg *hdr = buf_msg(skb);
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	switch (msg_user(hdr)) {
12408c2ecf20Sopenharmony_ci	case TIPC_LOW_IMPORTANCE:
12418c2ecf20Sopenharmony_ci	case TIPC_MEDIUM_IMPORTANCE:
12428c2ecf20Sopenharmony_ci	case TIPC_HIGH_IMPORTANCE:
12438c2ecf20Sopenharmony_ci	case TIPC_CRITICAL_IMPORTANCE:
12448c2ecf20Sopenharmony_ci		if (unlikely(msg_in_group(hdr) || msg_mcast(hdr))) {
12458c2ecf20Sopenharmony_ci			skb_queue_tail(mc_inputq, skb);
12468c2ecf20Sopenharmony_ci			return true;
12478c2ecf20Sopenharmony_ci		}
12488c2ecf20Sopenharmony_ci		fallthrough;
12498c2ecf20Sopenharmony_ci	case CONN_MANAGER:
12508c2ecf20Sopenharmony_ci		skb_queue_tail(inputq, skb);
12518c2ecf20Sopenharmony_ci		return true;
12528c2ecf20Sopenharmony_ci	case GROUP_PROTOCOL:
12538c2ecf20Sopenharmony_ci		skb_queue_tail(mc_inputq, skb);
12548c2ecf20Sopenharmony_ci		return true;
12558c2ecf20Sopenharmony_ci	case NAME_DISTRIBUTOR:
12568c2ecf20Sopenharmony_ci		l->bc_rcvlink->state = LINK_ESTABLISHED;
12578c2ecf20Sopenharmony_ci		skb_queue_tail(l->namedq, skb);
12588c2ecf20Sopenharmony_ci		return true;
12598c2ecf20Sopenharmony_ci	case MSG_BUNDLER:
12608c2ecf20Sopenharmony_ci	case TUNNEL_PROTOCOL:
12618c2ecf20Sopenharmony_ci	case MSG_FRAGMENTER:
12628c2ecf20Sopenharmony_ci	case BCAST_PROTOCOL:
12638c2ecf20Sopenharmony_ci		return false;
12648c2ecf20Sopenharmony_ci#ifdef CONFIG_TIPC_CRYPTO
12658c2ecf20Sopenharmony_ci	case MSG_CRYPTO:
12668c2ecf20Sopenharmony_ci		if (TIPC_SKB_CB(skb)->decrypted) {
12678c2ecf20Sopenharmony_ci			tipc_crypto_msg_rcv(l->net, skb);
12688c2ecf20Sopenharmony_ci			return true;
12698c2ecf20Sopenharmony_ci		}
12708c2ecf20Sopenharmony_ci		fallthrough;
12718c2ecf20Sopenharmony_ci#endif
12728c2ecf20Sopenharmony_ci	default:
12738c2ecf20Sopenharmony_ci		pr_warn("Dropping received illegal msg type\n");
12748c2ecf20Sopenharmony_ci		kfree_skb(skb);
12758c2ecf20Sopenharmony_ci		return true;
12768c2ecf20Sopenharmony_ci	};
12778c2ecf20Sopenharmony_ci}
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci/* tipc_link_input - process packet that has passed link protocol check
12808c2ecf20Sopenharmony_ci *
12818c2ecf20Sopenharmony_ci * Consumes buffer
12828c2ecf20Sopenharmony_ci */
12838c2ecf20Sopenharmony_cistatic int tipc_link_input(struct tipc_link *l, struct sk_buff *skb,
12848c2ecf20Sopenharmony_ci			   struct sk_buff_head *inputq,
12858c2ecf20Sopenharmony_ci			   struct sk_buff **reasm_skb)
12868c2ecf20Sopenharmony_ci{
12878c2ecf20Sopenharmony_ci	struct tipc_msg *hdr = buf_msg(skb);
12888c2ecf20Sopenharmony_ci	struct sk_buff *iskb;
12898c2ecf20Sopenharmony_ci	struct sk_buff_head tmpq;
12908c2ecf20Sopenharmony_ci	int usr = msg_user(hdr);
12918c2ecf20Sopenharmony_ci	int pos = 0;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	if (usr == MSG_BUNDLER) {
12948c2ecf20Sopenharmony_ci		skb_queue_head_init(&tmpq);
12958c2ecf20Sopenharmony_ci		l->stats.recv_bundles++;
12968c2ecf20Sopenharmony_ci		l->stats.recv_bundled += msg_msgcnt(hdr);
12978c2ecf20Sopenharmony_ci		while (tipc_msg_extract(skb, &iskb, &pos))
12988c2ecf20Sopenharmony_ci			tipc_data_input(l, iskb, &tmpq);
12998c2ecf20Sopenharmony_ci		tipc_skb_queue_splice_tail(&tmpq, inputq);
13008c2ecf20Sopenharmony_ci		return 0;
13018c2ecf20Sopenharmony_ci	} else if (usr == MSG_FRAGMENTER) {
13028c2ecf20Sopenharmony_ci		l->stats.recv_fragments++;
13038c2ecf20Sopenharmony_ci		if (tipc_buf_append(reasm_skb, &skb)) {
13048c2ecf20Sopenharmony_ci			l->stats.recv_fragmented++;
13058c2ecf20Sopenharmony_ci			tipc_data_input(l, skb, inputq);
13068c2ecf20Sopenharmony_ci		} else if (!*reasm_skb && !link_is_bc_rcvlink(l)) {
13078c2ecf20Sopenharmony_ci			pr_warn_ratelimited("Unable to build fragment list\n");
13088c2ecf20Sopenharmony_ci			return tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
13098c2ecf20Sopenharmony_ci		}
13108c2ecf20Sopenharmony_ci		return 0;
13118c2ecf20Sopenharmony_ci	} else if (usr == BCAST_PROTOCOL) {
13128c2ecf20Sopenharmony_ci		tipc_bcast_lock(l->net);
13138c2ecf20Sopenharmony_ci		tipc_link_bc_init_rcv(l->bc_rcvlink, hdr);
13148c2ecf20Sopenharmony_ci		tipc_bcast_unlock(l->net);
13158c2ecf20Sopenharmony_ci	}
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	kfree_skb(skb);
13188c2ecf20Sopenharmony_ci	return 0;
13198c2ecf20Sopenharmony_ci}
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci/* tipc_link_tnl_rcv() - receive TUNNEL_PROTOCOL message, drop or process the
13228c2ecf20Sopenharmony_ci *			 inner message along with the ones in the old link's
13238c2ecf20Sopenharmony_ci *			 deferdq
13248c2ecf20Sopenharmony_ci * @l: tunnel link
13258c2ecf20Sopenharmony_ci * @skb: TUNNEL_PROTOCOL message
13268c2ecf20Sopenharmony_ci * @inputq: queue to put messages ready for delivery
13278c2ecf20Sopenharmony_ci */
13288c2ecf20Sopenharmony_cistatic int tipc_link_tnl_rcv(struct tipc_link *l, struct sk_buff *skb,
13298c2ecf20Sopenharmony_ci			     struct sk_buff_head *inputq)
13308c2ecf20Sopenharmony_ci{
13318c2ecf20Sopenharmony_ci	struct sk_buff **reasm_skb = &l->failover_reasm_skb;
13328c2ecf20Sopenharmony_ci	struct sk_buff **reasm_tnlmsg = &l->reasm_tnlmsg;
13338c2ecf20Sopenharmony_ci	struct sk_buff_head *fdefq = &l->failover_deferdq;
13348c2ecf20Sopenharmony_ci	struct tipc_msg *hdr = buf_msg(skb);
13358c2ecf20Sopenharmony_ci	struct sk_buff *iskb;
13368c2ecf20Sopenharmony_ci	int ipos = 0;
13378c2ecf20Sopenharmony_ci	int rc = 0;
13388c2ecf20Sopenharmony_ci	u16 seqno;
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	if (msg_type(hdr) == SYNCH_MSG) {
13418c2ecf20Sopenharmony_ci		kfree_skb(skb);
13428c2ecf20Sopenharmony_ci		return 0;
13438c2ecf20Sopenharmony_ci	}
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	/* Not a fragment? */
13468c2ecf20Sopenharmony_ci	if (likely(!msg_nof_fragms(hdr))) {
13478c2ecf20Sopenharmony_ci		if (unlikely(!tipc_msg_extract(skb, &iskb, &ipos))) {
13488c2ecf20Sopenharmony_ci			pr_warn_ratelimited("Unable to extract msg, defq: %d\n",
13498c2ecf20Sopenharmony_ci					    skb_queue_len(fdefq));
13508c2ecf20Sopenharmony_ci			return 0;
13518c2ecf20Sopenharmony_ci		}
13528c2ecf20Sopenharmony_ci		kfree_skb(skb);
13538c2ecf20Sopenharmony_ci	} else {
13548c2ecf20Sopenharmony_ci		/* Set fragment type for buf_append */
13558c2ecf20Sopenharmony_ci		if (msg_fragm_no(hdr) == 1)
13568c2ecf20Sopenharmony_ci			msg_set_type(hdr, FIRST_FRAGMENT);
13578c2ecf20Sopenharmony_ci		else if (msg_fragm_no(hdr) < msg_nof_fragms(hdr))
13588c2ecf20Sopenharmony_ci			msg_set_type(hdr, FRAGMENT);
13598c2ecf20Sopenharmony_ci		else
13608c2ecf20Sopenharmony_ci			msg_set_type(hdr, LAST_FRAGMENT);
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci		if (!tipc_buf_append(reasm_tnlmsg, &skb)) {
13638c2ecf20Sopenharmony_ci			/* Successful but non-complete reassembly? */
13648c2ecf20Sopenharmony_ci			if (*reasm_tnlmsg || link_is_bc_rcvlink(l))
13658c2ecf20Sopenharmony_ci				return 0;
13668c2ecf20Sopenharmony_ci			pr_warn_ratelimited("Unable to reassemble tunnel msg\n");
13678c2ecf20Sopenharmony_ci			return tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
13688c2ecf20Sopenharmony_ci		}
13698c2ecf20Sopenharmony_ci		iskb = skb;
13708c2ecf20Sopenharmony_ci	}
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	do {
13738c2ecf20Sopenharmony_ci		seqno = buf_seqno(iskb);
13748c2ecf20Sopenharmony_ci		if (unlikely(less(seqno, l->drop_point))) {
13758c2ecf20Sopenharmony_ci			kfree_skb(iskb);
13768c2ecf20Sopenharmony_ci			continue;
13778c2ecf20Sopenharmony_ci		}
13788c2ecf20Sopenharmony_ci		if (unlikely(seqno != l->drop_point)) {
13798c2ecf20Sopenharmony_ci			__tipc_skb_queue_sorted(fdefq, seqno, iskb);
13808c2ecf20Sopenharmony_ci			continue;
13818c2ecf20Sopenharmony_ci		}
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci		l->drop_point++;
13848c2ecf20Sopenharmony_ci		if (!tipc_data_input(l, iskb, inputq))
13858c2ecf20Sopenharmony_ci			rc |= tipc_link_input(l, iskb, inputq, reasm_skb);
13868c2ecf20Sopenharmony_ci		if (unlikely(rc))
13878c2ecf20Sopenharmony_ci			break;
13888c2ecf20Sopenharmony_ci	} while ((iskb = __tipc_skb_dequeue(fdefq, l->drop_point)));
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	return rc;
13918c2ecf20Sopenharmony_ci}
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci/**
13948c2ecf20Sopenharmony_ci * tipc_get_gap_ack_blks - get Gap ACK blocks from PROTOCOL/STATE_MSG
13958c2ecf20Sopenharmony_ci * @ga: returned pointer to the Gap ACK blocks if any
13968c2ecf20Sopenharmony_ci * @l: the tipc link
13978c2ecf20Sopenharmony_ci * @hdr: the PROTOCOL/STATE_MSG header
13988c2ecf20Sopenharmony_ci * @uc: desired Gap ACK blocks type, i.e. unicast (= 1) or broadcast (= 0)
13998c2ecf20Sopenharmony_ci *
14008c2ecf20Sopenharmony_ci * Return: the total Gap ACK blocks size
14018c2ecf20Sopenharmony_ci */
14028c2ecf20Sopenharmony_ciu16 tipc_get_gap_ack_blks(struct tipc_gap_ack_blks **ga, struct tipc_link *l,
14038c2ecf20Sopenharmony_ci			  struct tipc_msg *hdr, bool uc)
14048c2ecf20Sopenharmony_ci{
14058c2ecf20Sopenharmony_ci	struct tipc_gap_ack_blks *p;
14068c2ecf20Sopenharmony_ci	u16 sz = 0;
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	/* Does peer support the Gap ACK blocks feature? */
14098c2ecf20Sopenharmony_ci	if (l->peer_caps & TIPC_GAP_ACK_BLOCK) {
14108c2ecf20Sopenharmony_ci		p = (struct tipc_gap_ack_blks *)msg_data(hdr);
14118c2ecf20Sopenharmony_ci		sz = ntohs(p->len);
14128c2ecf20Sopenharmony_ci		/* Sanity check */
14138c2ecf20Sopenharmony_ci		if (sz == struct_size(p, gacks, size_add(p->ugack_cnt, p->bgack_cnt))) {
14148c2ecf20Sopenharmony_ci			/* Good, check if the desired type exists */
14158c2ecf20Sopenharmony_ci			if ((uc && p->ugack_cnt) || (!uc && p->bgack_cnt))
14168c2ecf20Sopenharmony_ci				goto ok;
14178c2ecf20Sopenharmony_ci		/* Backward compatible: peer might not support bc, but uc? */
14188c2ecf20Sopenharmony_ci		} else if (uc && sz == struct_size(p, gacks, p->ugack_cnt)) {
14198c2ecf20Sopenharmony_ci			if (p->ugack_cnt) {
14208c2ecf20Sopenharmony_ci				p->bgack_cnt = 0;
14218c2ecf20Sopenharmony_ci				goto ok;
14228c2ecf20Sopenharmony_ci			}
14238c2ecf20Sopenharmony_ci		}
14248c2ecf20Sopenharmony_ci	}
14258c2ecf20Sopenharmony_ci	/* Other cases: ignore! */
14268c2ecf20Sopenharmony_ci	p = NULL;
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ciok:
14298c2ecf20Sopenharmony_ci	*ga = p;
14308c2ecf20Sopenharmony_ci	return sz;
14318c2ecf20Sopenharmony_ci}
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_cistatic u8 __tipc_build_gap_ack_blks(struct tipc_gap_ack_blks *ga,
14348c2ecf20Sopenharmony_ci				    struct tipc_link *l, u8 start_index)
14358c2ecf20Sopenharmony_ci{
14368c2ecf20Sopenharmony_ci	struct tipc_gap_ack *gacks = &ga->gacks[start_index];
14378c2ecf20Sopenharmony_ci	struct sk_buff *skb = skb_peek(&l->deferdq);
14388c2ecf20Sopenharmony_ci	u16 expect, seqno = 0;
14398c2ecf20Sopenharmony_ci	u8 n = 0;
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	if (!skb)
14428c2ecf20Sopenharmony_ci		return 0;
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci	expect = buf_seqno(skb);
14458c2ecf20Sopenharmony_ci	skb_queue_walk(&l->deferdq, skb) {
14468c2ecf20Sopenharmony_ci		seqno = buf_seqno(skb);
14478c2ecf20Sopenharmony_ci		if (unlikely(more(seqno, expect))) {
14488c2ecf20Sopenharmony_ci			gacks[n].ack = htons(expect - 1);
14498c2ecf20Sopenharmony_ci			gacks[n].gap = htons(seqno - expect);
14508c2ecf20Sopenharmony_ci			if (++n >= MAX_GAP_ACK_BLKS / 2) {
14518c2ecf20Sopenharmony_ci				pr_info_ratelimited("Gacks on %s: %d, ql: %d!\n",
14528c2ecf20Sopenharmony_ci						    l->name, n,
14538c2ecf20Sopenharmony_ci						    skb_queue_len(&l->deferdq));
14548c2ecf20Sopenharmony_ci				return n;
14558c2ecf20Sopenharmony_ci			}
14568c2ecf20Sopenharmony_ci		} else if (unlikely(less(seqno, expect))) {
14578c2ecf20Sopenharmony_ci			pr_warn("Unexpected skb in deferdq!\n");
14588c2ecf20Sopenharmony_ci			continue;
14598c2ecf20Sopenharmony_ci		}
14608c2ecf20Sopenharmony_ci		expect = seqno + 1;
14618c2ecf20Sopenharmony_ci	}
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	/* last block */
14648c2ecf20Sopenharmony_ci	gacks[n].ack = htons(seqno);
14658c2ecf20Sopenharmony_ci	gacks[n].gap = 0;
14668c2ecf20Sopenharmony_ci	n++;
14678c2ecf20Sopenharmony_ci	return n;
14688c2ecf20Sopenharmony_ci}
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci/* tipc_build_gap_ack_blks - build Gap ACK blocks
14718c2ecf20Sopenharmony_ci * @l: tipc unicast link
14728c2ecf20Sopenharmony_ci * @hdr: the tipc message buffer to store the Gap ACK blocks after built
14738c2ecf20Sopenharmony_ci *
14748c2ecf20Sopenharmony_ci * The function builds Gap ACK blocks for both the unicast & broadcast receiver
14758c2ecf20Sopenharmony_ci * links of a certain peer, the buffer after built has the network data format
14768c2ecf20Sopenharmony_ci * as found at the struct tipc_gap_ack_blks definition.
14778c2ecf20Sopenharmony_ci *
14788c2ecf20Sopenharmony_ci * returns the actual allocated memory size
14798c2ecf20Sopenharmony_ci */
14808c2ecf20Sopenharmony_cistatic u16 tipc_build_gap_ack_blks(struct tipc_link *l, struct tipc_msg *hdr)
14818c2ecf20Sopenharmony_ci{
14828c2ecf20Sopenharmony_ci	struct tipc_link *bcl = l->bc_rcvlink;
14838c2ecf20Sopenharmony_ci	struct tipc_gap_ack_blks *ga;
14848c2ecf20Sopenharmony_ci	u16 len;
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci	ga = (struct tipc_gap_ack_blks *)msg_data(hdr);
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	/* Start with broadcast link first */
14898c2ecf20Sopenharmony_ci	tipc_bcast_lock(bcl->net);
14908c2ecf20Sopenharmony_ci	msg_set_bcast_ack(hdr, bcl->rcv_nxt - 1);
14918c2ecf20Sopenharmony_ci	msg_set_bc_gap(hdr, link_bc_rcv_gap(bcl));
14928c2ecf20Sopenharmony_ci	ga->bgack_cnt = __tipc_build_gap_ack_blks(ga, bcl, 0);
14938c2ecf20Sopenharmony_ci	tipc_bcast_unlock(bcl->net);
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	/* Now for unicast link, but an explicit NACK only (???) */
14968c2ecf20Sopenharmony_ci	ga->ugack_cnt = (msg_seq_gap(hdr)) ?
14978c2ecf20Sopenharmony_ci			__tipc_build_gap_ack_blks(ga, l, ga->bgack_cnt) : 0;
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci	/* Total len */
15008c2ecf20Sopenharmony_ci	len = struct_size(ga, gacks, size_add(ga->bgack_cnt, ga->ugack_cnt));
15018c2ecf20Sopenharmony_ci	ga->len = htons(len);
15028c2ecf20Sopenharmony_ci	return len;
15038c2ecf20Sopenharmony_ci}
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci/* tipc_link_advance_transmq - advance TIPC link transmq queue by releasing
15068c2ecf20Sopenharmony_ci *			       acked packets, also doing retransmissions if
15078c2ecf20Sopenharmony_ci *			       gaps found
15088c2ecf20Sopenharmony_ci * @l: tipc link with transmq queue to be advanced
15098c2ecf20Sopenharmony_ci * @r: tipc link "receiver" i.e. in case of broadcast (= "l" if unicast)
15108c2ecf20Sopenharmony_ci * @acked: seqno of last packet acked by peer without any gaps before
15118c2ecf20Sopenharmony_ci * @gap: # of gap packets
15128c2ecf20Sopenharmony_ci * @ga: buffer pointer to Gap ACK blocks from peer
15138c2ecf20Sopenharmony_ci * @xmitq: queue for accumulating the retransmitted packets if any
15148c2ecf20Sopenharmony_ci * @retransmitted: returned boolean value if a retransmission is really issued
15158c2ecf20Sopenharmony_ci * @rc: returned code e.g. TIPC_LINK_DOWN_EVT if a repeated retransmit failures
15168c2ecf20Sopenharmony_ci *      happens (- unlikely case)
15178c2ecf20Sopenharmony_ci *
15188c2ecf20Sopenharmony_ci * Return: the number of packets released from the link transmq
15198c2ecf20Sopenharmony_ci */
15208c2ecf20Sopenharmony_cistatic int tipc_link_advance_transmq(struct tipc_link *l, struct tipc_link *r,
15218c2ecf20Sopenharmony_ci				     u16 acked, u16 gap,
15228c2ecf20Sopenharmony_ci				     struct tipc_gap_ack_blks *ga,
15238c2ecf20Sopenharmony_ci				     struct sk_buff_head *xmitq,
15248c2ecf20Sopenharmony_ci				     bool *retransmitted, int *rc)
15258c2ecf20Sopenharmony_ci{
15268c2ecf20Sopenharmony_ci	struct tipc_gap_ack_blks *last_ga = r->last_ga, *this_ga = NULL;
15278c2ecf20Sopenharmony_ci	struct tipc_gap_ack *gacks = NULL;
15288c2ecf20Sopenharmony_ci	struct sk_buff *skb, *_skb, *tmp;
15298c2ecf20Sopenharmony_ci	struct tipc_msg *hdr;
15308c2ecf20Sopenharmony_ci	u32 qlen = skb_queue_len(&l->transmq);
15318c2ecf20Sopenharmony_ci	u16 nacked = acked, ngap = gap, gack_cnt = 0;
15328c2ecf20Sopenharmony_ci	u16 bc_ack = l->bc_rcvlink->rcv_nxt - 1;
15338c2ecf20Sopenharmony_ci	u16 ack = l->rcv_nxt - 1;
15348c2ecf20Sopenharmony_ci	u16 seqno, n = 0;
15358c2ecf20Sopenharmony_ci	u16 end = r->acked, start = end, offset = r->last_gap;
15368c2ecf20Sopenharmony_ci	u16 si = (last_ga) ? last_ga->start_index : 0;
15378c2ecf20Sopenharmony_ci	bool is_uc = !link_is_bc_sndlink(l);
15388c2ecf20Sopenharmony_ci	bool bc_has_acked = false;
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci	trace_tipc_link_retrans(r, acked + 1, acked + gap, &l->transmq);
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	/* Determine Gap ACK blocks if any for the particular link */
15438c2ecf20Sopenharmony_ci	if (ga && is_uc) {
15448c2ecf20Sopenharmony_ci		/* Get the Gap ACKs, uc part */
15458c2ecf20Sopenharmony_ci		gack_cnt = ga->ugack_cnt;
15468c2ecf20Sopenharmony_ci		gacks = &ga->gacks[ga->bgack_cnt];
15478c2ecf20Sopenharmony_ci	} else if (ga) {
15488c2ecf20Sopenharmony_ci		/* Copy the Gap ACKs, bc part, for later renewal if needed */
15498c2ecf20Sopenharmony_ci		this_ga = kmemdup(ga, struct_size(ga, gacks, ga->bgack_cnt),
15508c2ecf20Sopenharmony_ci				  GFP_ATOMIC);
15518c2ecf20Sopenharmony_ci		if (likely(this_ga)) {
15528c2ecf20Sopenharmony_ci			this_ga->start_index = 0;
15538c2ecf20Sopenharmony_ci			/* Start with the bc Gap ACKs */
15548c2ecf20Sopenharmony_ci			gack_cnt = this_ga->bgack_cnt;
15558c2ecf20Sopenharmony_ci			gacks = &this_ga->gacks[0];
15568c2ecf20Sopenharmony_ci		} else {
15578c2ecf20Sopenharmony_ci			/* Hmm, we can get in trouble..., simply ignore it */
15588c2ecf20Sopenharmony_ci			pr_warn_ratelimited("Ignoring bc Gap ACKs, no memory\n");
15598c2ecf20Sopenharmony_ci		}
15608c2ecf20Sopenharmony_ci	}
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	/* Advance the link transmq */
15638c2ecf20Sopenharmony_ci	skb_queue_walk_safe(&l->transmq, skb, tmp) {
15648c2ecf20Sopenharmony_ci		seqno = buf_seqno(skb);
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_cinext_gap_ack:
15678c2ecf20Sopenharmony_ci		if (less_eq(seqno, nacked)) {
15688c2ecf20Sopenharmony_ci			if (is_uc)
15698c2ecf20Sopenharmony_ci				goto release;
15708c2ecf20Sopenharmony_ci			/* Skip packets peer has already acked */
15718c2ecf20Sopenharmony_ci			if (!more(seqno, r->acked))
15728c2ecf20Sopenharmony_ci				continue;
15738c2ecf20Sopenharmony_ci			/* Get the next of last Gap ACK blocks */
15748c2ecf20Sopenharmony_ci			while (more(seqno, end)) {
15758c2ecf20Sopenharmony_ci				if (!last_ga || si >= last_ga->bgack_cnt)
15768c2ecf20Sopenharmony_ci					break;
15778c2ecf20Sopenharmony_ci				start = end + offset + 1;
15788c2ecf20Sopenharmony_ci				end = ntohs(last_ga->gacks[si].ack);
15798c2ecf20Sopenharmony_ci				offset = ntohs(last_ga->gacks[si].gap);
15808c2ecf20Sopenharmony_ci				si++;
15818c2ecf20Sopenharmony_ci				WARN_ONCE(more(start, end) ||
15828c2ecf20Sopenharmony_ci					  (!offset &&
15838c2ecf20Sopenharmony_ci					   si < last_ga->bgack_cnt) ||
15848c2ecf20Sopenharmony_ci					  si > MAX_GAP_ACK_BLKS,
15858c2ecf20Sopenharmony_ci					  "Corrupted Gap ACK: %d %d %d %d %d\n",
15868c2ecf20Sopenharmony_ci					  start, end, offset, si,
15878c2ecf20Sopenharmony_ci					  last_ga->bgack_cnt);
15888c2ecf20Sopenharmony_ci			}
15898c2ecf20Sopenharmony_ci			/* Check against the last Gap ACK block */
15908c2ecf20Sopenharmony_ci			if (in_range(seqno, start, end))
15918c2ecf20Sopenharmony_ci				continue;
15928c2ecf20Sopenharmony_ci			/* Update/release the packet peer is acking */
15938c2ecf20Sopenharmony_ci			bc_has_acked = true;
15948c2ecf20Sopenharmony_ci			if (--TIPC_SKB_CB(skb)->ackers)
15958c2ecf20Sopenharmony_ci				continue;
15968c2ecf20Sopenharmony_cirelease:
15978c2ecf20Sopenharmony_ci			/* release skb */
15988c2ecf20Sopenharmony_ci			__skb_unlink(skb, &l->transmq);
15998c2ecf20Sopenharmony_ci			kfree_skb(skb);
16008c2ecf20Sopenharmony_ci		} else if (less_eq(seqno, nacked + ngap)) {
16018c2ecf20Sopenharmony_ci			/* First gap: check if repeated retrans failures? */
16028c2ecf20Sopenharmony_ci			if (unlikely(seqno == acked + 1 &&
16038c2ecf20Sopenharmony_ci				     link_retransmit_failure(l, r, rc))) {
16048c2ecf20Sopenharmony_ci				/* Ignore this bc Gap ACKs if any */
16058c2ecf20Sopenharmony_ci				kfree(this_ga);
16068c2ecf20Sopenharmony_ci				this_ga = NULL;
16078c2ecf20Sopenharmony_ci				break;
16088c2ecf20Sopenharmony_ci			}
16098c2ecf20Sopenharmony_ci			/* retransmit skb if unrestricted*/
16108c2ecf20Sopenharmony_ci			if (time_before(jiffies, TIPC_SKB_CB(skb)->nxt_retr))
16118c2ecf20Sopenharmony_ci				continue;
16128c2ecf20Sopenharmony_ci			tipc_link_set_skb_retransmit_time(skb, l);
16138c2ecf20Sopenharmony_ci			_skb = pskb_copy(skb, GFP_ATOMIC);
16148c2ecf20Sopenharmony_ci			if (!_skb)
16158c2ecf20Sopenharmony_ci				continue;
16168c2ecf20Sopenharmony_ci			hdr = buf_msg(_skb);
16178c2ecf20Sopenharmony_ci			msg_set_ack(hdr, ack);
16188c2ecf20Sopenharmony_ci			msg_set_bcast_ack(hdr, bc_ack);
16198c2ecf20Sopenharmony_ci			_skb->priority = TC_PRIO_CONTROL;
16208c2ecf20Sopenharmony_ci			__skb_queue_tail(xmitq, _skb);
16218c2ecf20Sopenharmony_ci			l->stats.retransmitted++;
16228c2ecf20Sopenharmony_ci			if (!is_uc)
16238c2ecf20Sopenharmony_ci				r->stats.retransmitted++;
16248c2ecf20Sopenharmony_ci			*retransmitted = true;
16258c2ecf20Sopenharmony_ci			/* Increase actual retrans counter & mark first time */
16268c2ecf20Sopenharmony_ci			if (!TIPC_SKB_CB(skb)->retr_cnt++)
16278c2ecf20Sopenharmony_ci				TIPC_SKB_CB(skb)->retr_stamp = jiffies;
16288c2ecf20Sopenharmony_ci		} else {
16298c2ecf20Sopenharmony_ci			/* retry with Gap ACK blocks if any */
16308c2ecf20Sopenharmony_ci			if (n >= gack_cnt)
16318c2ecf20Sopenharmony_ci				break;
16328c2ecf20Sopenharmony_ci			nacked = ntohs(gacks[n].ack);
16338c2ecf20Sopenharmony_ci			ngap = ntohs(gacks[n].gap);
16348c2ecf20Sopenharmony_ci			n++;
16358c2ecf20Sopenharmony_ci			goto next_gap_ack;
16368c2ecf20Sopenharmony_ci		}
16378c2ecf20Sopenharmony_ci	}
16388c2ecf20Sopenharmony_ci
16398c2ecf20Sopenharmony_ci	/* Renew last Gap ACK blocks for bc if needed */
16408c2ecf20Sopenharmony_ci	if (bc_has_acked) {
16418c2ecf20Sopenharmony_ci		if (this_ga) {
16428c2ecf20Sopenharmony_ci			kfree(last_ga);
16438c2ecf20Sopenharmony_ci			r->last_ga = this_ga;
16448c2ecf20Sopenharmony_ci			r->last_gap = gap;
16458c2ecf20Sopenharmony_ci		} else if (last_ga) {
16468c2ecf20Sopenharmony_ci			if (less(acked, start)) {
16478c2ecf20Sopenharmony_ci				si--;
16488c2ecf20Sopenharmony_ci				offset = start - acked - 1;
16498c2ecf20Sopenharmony_ci			} else if (less(acked, end)) {
16508c2ecf20Sopenharmony_ci				acked = end;
16518c2ecf20Sopenharmony_ci			}
16528c2ecf20Sopenharmony_ci			if (si < last_ga->bgack_cnt) {
16538c2ecf20Sopenharmony_ci				last_ga->start_index = si;
16548c2ecf20Sopenharmony_ci				r->last_gap = offset;
16558c2ecf20Sopenharmony_ci			} else {
16568c2ecf20Sopenharmony_ci				kfree(last_ga);
16578c2ecf20Sopenharmony_ci				r->last_ga = NULL;
16588c2ecf20Sopenharmony_ci				r->last_gap = 0;
16598c2ecf20Sopenharmony_ci			}
16608c2ecf20Sopenharmony_ci		} else {
16618c2ecf20Sopenharmony_ci			r->last_gap = 0;
16628c2ecf20Sopenharmony_ci		}
16638c2ecf20Sopenharmony_ci		r->acked = acked;
16648c2ecf20Sopenharmony_ci	} else {
16658c2ecf20Sopenharmony_ci		kfree(this_ga);
16668c2ecf20Sopenharmony_ci	}
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	return qlen - skb_queue_len(&l->transmq);
16698c2ecf20Sopenharmony_ci}
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci/* tipc_link_build_state_msg: prepare link state message for transmission
16728c2ecf20Sopenharmony_ci *
16738c2ecf20Sopenharmony_ci * Note that sending of broadcast ack is coordinated among nodes, to reduce
16748c2ecf20Sopenharmony_ci * risk of ack storms towards the sender
16758c2ecf20Sopenharmony_ci */
16768c2ecf20Sopenharmony_ciint tipc_link_build_state_msg(struct tipc_link *l, struct sk_buff_head *xmitq)
16778c2ecf20Sopenharmony_ci{
16788c2ecf20Sopenharmony_ci	if (!l)
16798c2ecf20Sopenharmony_ci		return 0;
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	/* Broadcast ACK must be sent via a unicast link => defer to caller */
16828c2ecf20Sopenharmony_ci	if (link_is_bc_rcvlink(l)) {
16838c2ecf20Sopenharmony_ci		if (((l->rcv_nxt ^ tipc_own_addr(l->net)) & 0xf) != 0xf)
16848c2ecf20Sopenharmony_ci			return 0;
16858c2ecf20Sopenharmony_ci		l->rcv_unacked = 0;
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_ci		/* Use snd_nxt to store peer's snd_nxt in broadcast rcv link */
16888c2ecf20Sopenharmony_ci		l->snd_nxt = l->rcv_nxt;
16898c2ecf20Sopenharmony_ci		return TIPC_LINK_SND_STATE;
16908c2ecf20Sopenharmony_ci	}
16918c2ecf20Sopenharmony_ci	/* Unicast ACK */
16928c2ecf20Sopenharmony_ci	l->rcv_unacked = 0;
16938c2ecf20Sopenharmony_ci	l->stats.sent_acks++;
16948c2ecf20Sopenharmony_ci	tipc_link_build_proto_msg(l, STATE_MSG, 0, 0, 0, 0, 0, xmitq);
16958c2ecf20Sopenharmony_ci	return 0;
16968c2ecf20Sopenharmony_ci}
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci/* tipc_link_build_reset_msg: prepare link RESET or ACTIVATE message
16998c2ecf20Sopenharmony_ci */
17008c2ecf20Sopenharmony_civoid tipc_link_build_reset_msg(struct tipc_link *l, struct sk_buff_head *xmitq)
17018c2ecf20Sopenharmony_ci{
17028c2ecf20Sopenharmony_ci	int mtyp = RESET_MSG;
17038c2ecf20Sopenharmony_ci	struct sk_buff *skb;
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci	if (l->state == LINK_ESTABLISHING)
17068c2ecf20Sopenharmony_ci		mtyp = ACTIVATE_MSG;
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci	tipc_link_build_proto_msg(l, mtyp, 0, 0, 0, 0, 0, xmitq);
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci	/* Inform peer that this endpoint is going down if applicable */
17118c2ecf20Sopenharmony_ci	skb = skb_peek_tail(xmitq);
17128c2ecf20Sopenharmony_ci	if (skb && (l->state == LINK_RESET))
17138c2ecf20Sopenharmony_ci		msg_set_peer_stopping(buf_msg(skb), 1);
17148c2ecf20Sopenharmony_ci}
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci/* tipc_link_build_nack_msg: prepare link nack message for transmission
17178c2ecf20Sopenharmony_ci * Note that sending of broadcast NACK is coordinated among nodes, to
17188c2ecf20Sopenharmony_ci * reduce the risk of NACK storms towards the sender
17198c2ecf20Sopenharmony_ci */
17208c2ecf20Sopenharmony_cistatic int tipc_link_build_nack_msg(struct tipc_link *l,
17218c2ecf20Sopenharmony_ci				    struct sk_buff_head *xmitq)
17228c2ecf20Sopenharmony_ci{
17238c2ecf20Sopenharmony_ci	u32 def_cnt = ++l->stats.deferred_recv;
17248c2ecf20Sopenharmony_ci	struct sk_buff_head *dfq = &l->deferdq;
17258c2ecf20Sopenharmony_ci	u32 defq_len = skb_queue_len(dfq);
17268c2ecf20Sopenharmony_ci	int match1, match2;
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	if (link_is_bc_rcvlink(l)) {
17298c2ecf20Sopenharmony_ci		match1 = def_cnt & 0xf;
17308c2ecf20Sopenharmony_ci		match2 = tipc_own_addr(l->net) & 0xf;
17318c2ecf20Sopenharmony_ci		if (match1 == match2)
17328c2ecf20Sopenharmony_ci			return TIPC_LINK_SND_STATE;
17338c2ecf20Sopenharmony_ci		return 0;
17348c2ecf20Sopenharmony_ci	}
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci	if (defq_len >= 3 && !((defq_len - 3) % 16)) {
17378c2ecf20Sopenharmony_ci		u16 rcvgap = buf_seqno(skb_peek(dfq)) - l->rcv_nxt;
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci		tipc_link_build_proto_msg(l, STATE_MSG, 0, 0,
17408c2ecf20Sopenharmony_ci					  rcvgap, 0, 0, xmitq);
17418c2ecf20Sopenharmony_ci	}
17428c2ecf20Sopenharmony_ci	return 0;
17438c2ecf20Sopenharmony_ci}
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci/* tipc_link_rcv - process TIPC packets/messages arriving from off-node
17468c2ecf20Sopenharmony_ci * @l: the link that should handle the message
17478c2ecf20Sopenharmony_ci * @skb: TIPC packet
17488c2ecf20Sopenharmony_ci * @xmitq: queue to place packets to be sent after this call
17498c2ecf20Sopenharmony_ci */
17508c2ecf20Sopenharmony_ciint tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb,
17518c2ecf20Sopenharmony_ci		  struct sk_buff_head *xmitq)
17528c2ecf20Sopenharmony_ci{
17538c2ecf20Sopenharmony_ci	struct sk_buff_head *defq = &l->deferdq;
17548c2ecf20Sopenharmony_ci	struct tipc_msg *hdr = buf_msg(skb);
17558c2ecf20Sopenharmony_ci	u16 seqno, rcv_nxt, win_lim;
17568c2ecf20Sopenharmony_ci	int released = 0;
17578c2ecf20Sopenharmony_ci	int rc = 0;
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci	/* Verify and update link state */
17608c2ecf20Sopenharmony_ci	if (unlikely(msg_user(hdr) == LINK_PROTOCOL))
17618c2ecf20Sopenharmony_ci		return tipc_link_proto_rcv(l, skb, xmitq);
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	/* Don't send probe at next timeout expiration */
17648c2ecf20Sopenharmony_ci	l->silent_intv_cnt = 0;
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci	do {
17678c2ecf20Sopenharmony_ci		hdr = buf_msg(skb);
17688c2ecf20Sopenharmony_ci		seqno = msg_seqno(hdr);
17698c2ecf20Sopenharmony_ci		rcv_nxt = l->rcv_nxt;
17708c2ecf20Sopenharmony_ci		win_lim = rcv_nxt + TIPC_MAX_LINK_WIN;
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci		if (unlikely(!link_is_up(l))) {
17738c2ecf20Sopenharmony_ci			if (l->state == LINK_ESTABLISHING)
17748c2ecf20Sopenharmony_ci				rc = TIPC_LINK_UP_EVT;
17758c2ecf20Sopenharmony_ci			kfree_skb(skb);
17768c2ecf20Sopenharmony_ci			break;
17778c2ecf20Sopenharmony_ci		}
17788c2ecf20Sopenharmony_ci
17798c2ecf20Sopenharmony_ci		/* Drop if outside receive window */
17808c2ecf20Sopenharmony_ci		if (unlikely(less(seqno, rcv_nxt) || more(seqno, win_lim))) {
17818c2ecf20Sopenharmony_ci			l->stats.duplicates++;
17828c2ecf20Sopenharmony_ci			kfree_skb(skb);
17838c2ecf20Sopenharmony_ci			break;
17848c2ecf20Sopenharmony_ci		}
17858c2ecf20Sopenharmony_ci		released += tipc_link_advance_transmq(l, l, msg_ack(hdr), 0,
17868c2ecf20Sopenharmony_ci						      NULL, NULL, NULL, NULL);
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci		/* Defer delivery if sequence gap */
17898c2ecf20Sopenharmony_ci		if (unlikely(seqno != rcv_nxt)) {
17908c2ecf20Sopenharmony_ci			if (!__tipc_skb_queue_sorted(defq, seqno, skb))
17918c2ecf20Sopenharmony_ci				l->stats.duplicates++;
17928c2ecf20Sopenharmony_ci			rc |= tipc_link_build_nack_msg(l, xmitq);
17938c2ecf20Sopenharmony_ci			break;
17948c2ecf20Sopenharmony_ci		}
17958c2ecf20Sopenharmony_ci
17968c2ecf20Sopenharmony_ci		/* Deliver packet */
17978c2ecf20Sopenharmony_ci		l->rcv_nxt++;
17988c2ecf20Sopenharmony_ci		l->stats.recv_pkts++;
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ci		if (unlikely(msg_user(hdr) == TUNNEL_PROTOCOL))
18018c2ecf20Sopenharmony_ci			rc |= tipc_link_tnl_rcv(l, skb, l->inputq);
18028c2ecf20Sopenharmony_ci		else if (!tipc_data_input(l, skb, l->inputq))
18038c2ecf20Sopenharmony_ci			rc |= tipc_link_input(l, skb, l->inputq, &l->reasm_buf);
18048c2ecf20Sopenharmony_ci		if (unlikely(++l->rcv_unacked >= TIPC_MIN_LINK_WIN))
18058c2ecf20Sopenharmony_ci			rc |= tipc_link_build_state_msg(l, xmitq);
18068c2ecf20Sopenharmony_ci		if (unlikely(rc & ~TIPC_LINK_SND_STATE))
18078c2ecf20Sopenharmony_ci			break;
18088c2ecf20Sopenharmony_ci	} while ((skb = __tipc_skb_dequeue(defq, l->rcv_nxt)));
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_ci	/* Forward queues and wake up waiting users */
18118c2ecf20Sopenharmony_ci	if (released) {
18128c2ecf20Sopenharmony_ci		tipc_link_update_cwin(l, released, 0);
18138c2ecf20Sopenharmony_ci		tipc_link_advance_backlog(l, xmitq);
18148c2ecf20Sopenharmony_ci		if (unlikely(!skb_queue_empty(&l->wakeupq)))
18158c2ecf20Sopenharmony_ci			link_prepare_wakeup(l);
18168c2ecf20Sopenharmony_ci	}
18178c2ecf20Sopenharmony_ci	return rc;
18188c2ecf20Sopenharmony_ci}
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_cistatic void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
18218c2ecf20Sopenharmony_ci				      bool probe_reply, u16 rcvgap,
18228c2ecf20Sopenharmony_ci				      int tolerance, int priority,
18238c2ecf20Sopenharmony_ci				      struct sk_buff_head *xmitq)
18248c2ecf20Sopenharmony_ci{
18258c2ecf20Sopenharmony_ci	struct tipc_mon_state *mstate = &l->mon_state;
18268c2ecf20Sopenharmony_ci	struct sk_buff_head *dfq = &l->deferdq;
18278c2ecf20Sopenharmony_ci	struct tipc_link *bcl = l->bc_rcvlink;
18288c2ecf20Sopenharmony_ci	struct tipc_msg *hdr;
18298c2ecf20Sopenharmony_ci	struct sk_buff *skb;
18308c2ecf20Sopenharmony_ci	bool node_up = link_is_up(bcl);
18318c2ecf20Sopenharmony_ci	u16 glen = 0, bc_rcvgap = 0;
18328c2ecf20Sopenharmony_ci	int dlen = 0;
18338c2ecf20Sopenharmony_ci	void *data;
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	/* Don't send protocol message during reset or link failover */
18368c2ecf20Sopenharmony_ci	if (tipc_link_is_blocked(l))
18378c2ecf20Sopenharmony_ci		return;
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci	if (!tipc_link_is_up(l) && (mtyp == STATE_MSG))
18408c2ecf20Sopenharmony_ci		return;
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci	if ((probe || probe_reply) && !skb_queue_empty(dfq))
18438c2ecf20Sopenharmony_ci		rcvgap = buf_seqno(skb_peek(dfq)) - l->rcv_nxt;
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci	skb = tipc_msg_create(LINK_PROTOCOL, mtyp, INT_H_SIZE,
18468c2ecf20Sopenharmony_ci			      tipc_max_domain_size + MAX_GAP_ACK_BLKS_SZ,
18478c2ecf20Sopenharmony_ci			      l->addr, tipc_own_addr(l->net), 0, 0, 0);
18488c2ecf20Sopenharmony_ci	if (!skb)
18498c2ecf20Sopenharmony_ci		return;
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	hdr = buf_msg(skb);
18528c2ecf20Sopenharmony_ci	data = msg_data(hdr);
18538c2ecf20Sopenharmony_ci	msg_set_session(hdr, l->session);
18548c2ecf20Sopenharmony_ci	msg_set_bearer_id(hdr, l->bearer_id);
18558c2ecf20Sopenharmony_ci	msg_set_net_plane(hdr, l->net_plane);
18568c2ecf20Sopenharmony_ci	msg_set_next_sent(hdr, l->snd_nxt);
18578c2ecf20Sopenharmony_ci	msg_set_ack(hdr, l->rcv_nxt - 1);
18588c2ecf20Sopenharmony_ci	msg_set_bcast_ack(hdr, bcl->rcv_nxt - 1);
18598c2ecf20Sopenharmony_ci	msg_set_bc_ack_invalid(hdr, !node_up);
18608c2ecf20Sopenharmony_ci	msg_set_last_bcast(hdr, l->bc_sndlink->snd_nxt - 1);
18618c2ecf20Sopenharmony_ci	msg_set_link_tolerance(hdr, tolerance);
18628c2ecf20Sopenharmony_ci	msg_set_linkprio(hdr, priority);
18638c2ecf20Sopenharmony_ci	msg_set_redundant_link(hdr, node_up);
18648c2ecf20Sopenharmony_ci	msg_set_seq_gap(hdr, 0);
18658c2ecf20Sopenharmony_ci	msg_set_seqno(hdr, l->snd_nxt + U16_MAX / 2);
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci	if (mtyp == STATE_MSG) {
18688c2ecf20Sopenharmony_ci		if (l->peer_caps & TIPC_LINK_PROTO_SEQNO)
18698c2ecf20Sopenharmony_ci			msg_set_seqno(hdr, l->snd_nxt_state++);
18708c2ecf20Sopenharmony_ci		msg_set_seq_gap(hdr, rcvgap);
18718c2ecf20Sopenharmony_ci		bc_rcvgap = link_bc_rcv_gap(bcl);
18728c2ecf20Sopenharmony_ci		msg_set_bc_gap(hdr, bc_rcvgap);
18738c2ecf20Sopenharmony_ci		msg_set_probe(hdr, probe);
18748c2ecf20Sopenharmony_ci		msg_set_is_keepalive(hdr, probe || probe_reply);
18758c2ecf20Sopenharmony_ci		if (l->peer_caps & TIPC_GAP_ACK_BLOCK)
18768c2ecf20Sopenharmony_ci			glen = tipc_build_gap_ack_blks(l, hdr);
18778c2ecf20Sopenharmony_ci		tipc_mon_prep(l->net, data + glen, &dlen, mstate, l->bearer_id);
18788c2ecf20Sopenharmony_ci		msg_set_size(hdr, INT_H_SIZE + glen + dlen);
18798c2ecf20Sopenharmony_ci		skb_trim(skb, INT_H_SIZE + glen + dlen);
18808c2ecf20Sopenharmony_ci		l->stats.sent_states++;
18818c2ecf20Sopenharmony_ci		l->rcv_unacked = 0;
18828c2ecf20Sopenharmony_ci	} else {
18838c2ecf20Sopenharmony_ci		/* RESET_MSG or ACTIVATE_MSG */
18848c2ecf20Sopenharmony_ci		if (mtyp == ACTIVATE_MSG) {
18858c2ecf20Sopenharmony_ci			msg_set_dest_session_valid(hdr, 1);
18868c2ecf20Sopenharmony_ci			msg_set_dest_session(hdr, l->peer_session);
18878c2ecf20Sopenharmony_ci		}
18888c2ecf20Sopenharmony_ci		msg_set_max_pkt(hdr, l->advertised_mtu);
18898c2ecf20Sopenharmony_ci		strcpy(data, l->if_name);
18908c2ecf20Sopenharmony_ci		msg_set_size(hdr, INT_H_SIZE + TIPC_MAX_IF_NAME);
18918c2ecf20Sopenharmony_ci		skb_trim(skb, INT_H_SIZE + TIPC_MAX_IF_NAME);
18928c2ecf20Sopenharmony_ci	}
18938c2ecf20Sopenharmony_ci	if (probe)
18948c2ecf20Sopenharmony_ci		l->stats.sent_probes++;
18958c2ecf20Sopenharmony_ci	if (rcvgap)
18968c2ecf20Sopenharmony_ci		l->stats.sent_nacks++;
18978c2ecf20Sopenharmony_ci	if (bc_rcvgap)
18988c2ecf20Sopenharmony_ci		bcl->stats.sent_nacks++;
18998c2ecf20Sopenharmony_ci	skb->priority = TC_PRIO_CONTROL;
19008c2ecf20Sopenharmony_ci	__skb_queue_tail(xmitq, skb);
19018c2ecf20Sopenharmony_ci	trace_tipc_proto_build(skb, false, l->name);
19028c2ecf20Sopenharmony_ci}
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_civoid tipc_link_create_dummy_tnl_msg(struct tipc_link *l,
19058c2ecf20Sopenharmony_ci				    struct sk_buff_head *xmitq)
19068c2ecf20Sopenharmony_ci{
19078c2ecf20Sopenharmony_ci	u32 onode = tipc_own_addr(l->net);
19088c2ecf20Sopenharmony_ci	struct tipc_msg *hdr, *ihdr;
19098c2ecf20Sopenharmony_ci	struct sk_buff_head tnlq;
19108c2ecf20Sopenharmony_ci	struct sk_buff *skb;
19118c2ecf20Sopenharmony_ci	u32 dnode = l->addr;
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_ci	__skb_queue_head_init(&tnlq);
19148c2ecf20Sopenharmony_ci	skb = tipc_msg_create(TUNNEL_PROTOCOL, FAILOVER_MSG,
19158c2ecf20Sopenharmony_ci			      INT_H_SIZE, BASIC_H_SIZE,
19168c2ecf20Sopenharmony_ci			      dnode, onode, 0, 0, 0);
19178c2ecf20Sopenharmony_ci	if (!skb) {
19188c2ecf20Sopenharmony_ci		pr_warn("%sunable to create tunnel packet\n", link_co_err);
19198c2ecf20Sopenharmony_ci		return;
19208c2ecf20Sopenharmony_ci	}
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci	hdr = buf_msg(skb);
19238c2ecf20Sopenharmony_ci	msg_set_msgcnt(hdr, 1);
19248c2ecf20Sopenharmony_ci	msg_set_bearer_id(hdr, l->peer_bearer_id);
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci	ihdr = (struct tipc_msg *)msg_data(hdr);
19278c2ecf20Sopenharmony_ci	tipc_msg_init(onode, ihdr, TIPC_LOW_IMPORTANCE, TIPC_DIRECT_MSG,
19288c2ecf20Sopenharmony_ci		      BASIC_H_SIZE, dnode);
19298c2ecf20Sopenharmony_ci	msg_set_errcode(ihdr, TIPC_ERR_NO_PORT);
19308c2ecf20Sopenharmony_ci	__skb_queue_tail(&tnlq, skb);
19318c2ecf20Sopenharmony_ci	tipc_link_xmit(l, &tnlq, xmitq);
19328c2ecf20Sopenharmony_ci}
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_ci/* tipc_link_tnl_prepare(): prepare and return a list of tunnel packets
19358c2ecf20Sopenharmony_ci * with contents of the link's transmit and backlog queues.
19368c2ecf20Sopenharmony_ci */
19378c2ecf20Sopenharmony_civoid tipc_link_tnl_prepare(struct tipc_link *l, struct tipc_link *tnl,
19388c2ecf20Sopenharmony_ci			   int mtyp, struct sk_buff_head *xmitq)
19398c2ecf20Sopenharmony_ci{
19408c2ecf20Sopenharmony_ci	struct sk_buff_head *fdefq = &tnl->failover_deferdq;
19418c2ecf20Sopenharmony_ci	struct sk_buff *skb, *tnlskb;
19428c2ecf20Sopenharmony_ci	struct tipc_msg *hdr, tnlhdr;
19438c2ecf20Sopenharmony_ci	struct sk_buff_head *queue = &l->transmq;
19448c2ecf20Sopenharmony_ci	struct sk_buff_head tmpxq, tnlq, frags;
19458c2ecf20Sopenharmony_ci	u16 pktlen, pktcnt, seqno = l->snd_nxt;
19468c2ecf20Sopenharmony_ci	bool pktcnt_need_update = false;
19478c2ecf20Sopenharmony_ci	u16 syncpt;
19488c2ecf20Sopenharmony_ci	int rc;
19498c2ecf20Sopenharmony_ci
19508c2ecf20Sopenharmony_ci	if (!tnl)
19518c2ecf20Sopenharmony_ci		return;
19528c2ecf20Sopenharmony_ci
19538c2ecf20Sopenharmony_ci	__skb_queue_head_init(&tnlq);
19548c2ecf20Sopenharmony_ci	/* Link Synching:
19558c2ecf20Sopenharmony_ci	 * From now on, send only one single ("dummy") SYNCH message
19568c2ecf20Sopenharmony_ci	 * to peer. The SYNCH message does not contain any data, just
19578c2ecf20Sopenharmony_ci	 * a header conveying the synch point to the peer.
19588c2ecf20Sopenharmony_ci	 */
19598c2ecf20Sopenharmony_ci	if (mtyp == SYNCH_MSG && (tnl->peer_caps & TIPC_TUNNEL_ENHANCED)) {
19608c2ecf20Sopenharmony_ci		tnlskb = tipc_msg_create(TUNNEL_PROTOCOL, SYNCH_MSG,
19618c2ecf20Sopenharmony_ci					 INT_H_SIZE, 0, l->addr,
19628c2ecf20Sopenharmony_ci					 tipc_own_addr(l->net),
19638c2ecf20Sopenharmony_ci					 0, 0, 0);
19648c2ecf20Sopenharmony_ci		if (!tnlskb) {
19658c2ecf20Sopenharmony_ci			pr_warn("%sunable to create dummy SYNCH_MSG\n",
19668c2ecf20Sopenharmony_ci				link_co_err);
19678c2ecf20Sopenharmony_ci			return;
19688c2ecf20Sopenharmony_ci		}
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci		hdr = buf_msg(tnlskb);
19718c2ecf20Sopenharmony_ci		syncpt = l->snd_nxt + skb_queue_len(&l->backlogq) - 1;
19728c2ecf20Sopenharmony_ci		msg_set_syncpt(hdr, syncpt);
19738c2ecf20Sopenharmony_ci		msg_set_bearer_id(hdr, l->peer_bearer_id);
19748c2ecf20Sopenharmony_ci		__skb_queue_tail(&tnlq, tnlskb);
19758c2ecf20Sopenharmony_ci		tipc_link_xmit(tnl, &tnlq, xmitq);
19768c2ecf20Sopenharmony_ci		return;
19778c2ecf20Sopenharmony_ci	}
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci	__skb_queue_head_init(&tmpxq);
19808c2ecf20Sopenharmony_ci	__skb_queue_head_init(&frags);
19818c2ecf20Sopenharmony_ci	/* At least one packet required for safe algorithm => add dummy */
19828c2ecf20Sopenharmony_ci	skb = tipc_msg_create(TIPC_LOW_IMPORTANCE, TIPC_DIRECT_MSG,
19838c2ecf20Sopenharmony_ci			      BASIC_H_SIZE, 0, l->addr, tipc_own_addr(l->net),
19848c2ecf20Sopenharmony_ci			      0, 0, TIPC_ERR_NO_PORT);
19858c2ecf20Sopenharmony_ci	if (!skb) {
19868c2ecf20Sopenharmony_ci		pr_warn("%sunable to create tunnel packet\n", link_co_err);
19878c2ecf20Sopenharmony_ci		return;
19888c2ecf20Sopenharmony_ci	}
19898c2ecf20Sopenharmony_ci	__skb_queue_tail(&tnlq, skb);
19908c2ecf20Sopenharmony_ci	tipc_link_xmit(l, &tnlq, &tmpxq);
19918c2ecf20Sopenharmony_ci	__skb_queue_purge(&tmpxq);
19928c2ecf20Sopenharmony_ci
19938c2ecf20Sopenharmony_ci	/* Initialize reusable tunnel packet header */
19948c2ecf20Sopenharmony_ci	tipc_msg_init(tipc_own_addr(l->net), &tnlhdr, TUNNEL_PROTOCOL,
19958c2ecf20Sopenharmony_ci		      mtyp, INT_H_SIZE, l->addr);
19968c2ecf20Sopenharmony_ci	if (mtyp == SYNCH_MSG)
19978c2ecf20Sopenharmony_ci		pktcnt = l->snd_nxt - buf_seqno(skb_peek(&l->transmq));
19988c2ecf20Sopenharmony_ci	else
19998c2ecf20Sopenharmony_ci		pktcnt = skb_queue_len(&l->transmq);
20008c2ecf20Sopenharmony_ci	pktcnt += skb_queue_len(&l->backlogq);
20018c2ecf20Sopenharmony_ci	msg_set_msgcnt(&tnlhdr, pktcnt);
20028c2ecf20Sopenharmony_ci	msg_set_bearer_id(&tnlhdr, l->peer_bearer_id);
20038c2ecf20Sopenharmony_citnl:
20048c2ecf20Sopenharmony_ci	/* Wrap each packet into a tunnel packet */
20058c2ecf20Sopenharmony_ci	skb_queue_walk(queue, skb) {
20068c2ecf20Sopenharmony_ci		hdr = buf_msg(skb);
20078c2ecf20Sopenharmony_ci		if (queue == &l->backlogq)
20088c2ecf20Sopenharmony_ci			msg_set_seqno(hdr, seqno++);
20098c2ecf20Sopenharmony_ci		pktlen = msg_size(hdr);
20108c2ecf20Sopenharmony_ci
20118c2ecf20Sopenharmony_ci		/* Tunnel link MTU is not large enough? This could be
20128c2ecf20Sopenharmony_ci		 * due to:
20138c2ecf20Sopenharmony_ci		 * 1) Link MTU has just changed or set differently;
20148c2ecf20Sopenharmony_ci		 * 2) Or FAILOVER on the top of a SYNCH message
20158c2ecf20Sopenharmony_ci		 *
20168c2ecf20Sopenharmony_ci		 * The 2nd case should not happen if peer supports
20178c2ecf20Sopenharmony_ci		 * TIPC_TUNNEL_ENHANCED
20188c2ecf20Sopenharmony_ci		 */
20198c2ecf20Sopenharmony_ci		if (pktlen > tnl->mtu - INT_H_SIZE) {
20208c2ecf20Sopenharmony_ci			if (mtyp == FAILOVER_MSG &&
20218c2ecf20Sopenharmony_ci			    (tnl->peer_caps & TIPC_TUNNEL_ENHANCED)) {
20228c2ecf20Sopenharmony_ci				rc = tipc_msg_fragment(skb, &tnlhdr, tnl->mtu,
20238c2ecf20Sopenharmony_ci						       &frags);
20248c2ecf20Sopenharmony_ci				if (rc) {
20258c2ecf20Sopenharmony_ci					pr_warn("%sunable to frag msg: rc %d\n",
20268c2ecf20Sopenharmony_ci						link_co_err, rc);
20278c2ecf20Sopenharmony_ci					return;
20288c2ecf20Sopenharmony_ci				}
20298c2ecf20Sopenharmony_ci				pktcnt += skb_queue_len(&frags) - 1;
20308c2ecf20Sopenharmony_ci				pktcnt_need_update = true;
20318c2ecf20Sopenharmony_ci				skb_queue_splice_tail_init(&frags, &tnlq);
20328c2ecf20Sopenharmony_ci				continue;
20338c2ecf20Sopenharmony_ci			}
20348c2ecf20Sopenharmony_ci			/* Unluckily, peer doesn't have TIPC_TUNNEL_ENHANCED
20358c2ecf20Sopenharmony_ci			 * => Just warn it and return!
20368c2ecf20Sopenharmony_ci			 */
20378c2ecf20Sopenharmony_ci			pr_warn_ratelimited("%stoo large msg <%d, %d>: %d!\n",
20388c2ecf20Sopenharmony_ci					    link_co_err, msg_user(hdr),
20398c2ecf20Sopenharmony_ci					    msg_type(hdr), msg_size(hdr));
20408c2ecf20Sopenharmony_ci			return;
20418c2ecf20Sopenharmony_ci		}
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci		msg_set_size(&tnlhdr, pktlen + INT_H_SIZE);
20448c2ecf20Sopenharmony_ci		tnlskb = tipc_buf_acquire(pktlen + INT_H_SIZE, GFP_ATOMIC);
20458c2ecf20Sopenharmony_ci		if (!tnlskb) {
20468c2ecf20Sopenharmony_ci			pr_warn("%sunable to send packet\n", link_co_err);
20478c2ecf20Sopenharmony_ci			return;
20488c2ecf20Sopenharmony_ci		}
20498c2ecf20Sopenharmony_ci		skb_copy_to_linear_data(tnlskb, &tnlhdr, INT_H_SIZE);
20508c2ecf20Sopenharmony_ci		skb_copy_to_linear_data_offset(tnlskb, INT_H_SIZE, hdr, pktlen);
20518c2ecf20Sopenharmony_ci		__skb_queue_tail(&tnlq, tnlskb);
20528c2ecf20Sopenharmony_ci	}
20538c2ecf20Sopenharmony_ci	if (queue != &l->backlogq) {
20548c2ecf20Sopenharmony_ci		queue = &l->backlogq;
20558c2ecf20Sopenharmony_ci		goto tnl;
20568c2ecf20Sopenharmony_ci	}
20578c2ecf20Sopenharmony_ci
20588c2ecf20Sopenharmony_ci	if (pktcnt_need_update)
20598c2ecf20Sopenharmony_ci		skb_queue_walk(&tnlq, skb) {
20608c2ecf20Sopenharmony_ci			hdr = buf_msg(skb);
20618c2ecf20Sopenharmony_ci			msg_set_msgcnt(hdr, pktcnt);
20628c2ecf20Sopenharmony_ci		}
20638c2ecf20Sopenharmony_ci
20648c2ecf20Sopenharmony_ci	tipc_link_xmit(tnl, &tnlq, xmitq);
20658c2ecf20Sopenharmony_ci
20668c2ecf20Sopenharmony_ci	if (mtyp == FAILOVER_MSG) {
20678c2ecf20Sopenharmony_ci		tnl->drop_point = l->rcv_nxt;
20688c2ecf20Sopenharmony_ci		tnl->failover_reasm_skb = l->reasm_buf;
20698c2ecf20Sopenharmony_ci		l->reasm_buf = NULL;
20708c2ecf20Sopenharmony_ci
20718c2ecf20Sopenharmony_ci		/* Failover the link's deferdq */
20728c2ecf20Sopenharmony_ci		if (unlikely(!skb_queue_empty(fdefq))) {
20738c2ecf20Sopenharmony_ci			pr_warn("Link failover deferdq not empty: %d!\n",
20748c2ecf20Sopenharmony_ci				skb_queue_len(fdefq));
20758c2ecf20Sopenharmony_ci			__skb_queue_purge(fdefq);
20768c2ecf20Sopenharmony_ci		}
20778c2ecf20Sopenharmony_ci		skb_queue_splice_init(&l->deferdq, fdefq);
20788c2ecf20Sopenharmony_ci	}
20798c2ecf20Sopenharmony_ci}
20808c2ecf20Sopenharmony_ci
20818c2ecf20Sopenharmony_ci/**
20828c2ecf20Sopenharmony_ci * tipc_link_failover_prepare() - prepare tnl for link failover
20838c2ecf20Sopenharmony_ci *
20848c2ecf20Sopenharmony_ci * This is a special version of the precursor - tipc_link_tnl_prepare(),
20858c2ecf20Sopenharmony_ci * see the tipc_node_link_failover() for details
20868c2ecf20Sopenharmony_ci *
20878c2ecf20Sopenharmony_ci * @l: failover link
20888c2ecf20Sopenharmony_ci * @tnl: tunnel link
20898c2ecf20Sopenharmony_ci * @xmitq: queue for messages to be xmited
20908c2ecf20Sopenharmony_ci */
20918c2ecf20Sopenharmony_civoid tipc_link_failover_prepare(struct tipc_link *l, struct tipc_link *tnl,
20928c2ecf20Sopenharmony_ci				struct sk_buff_head *xmitq)
20938c2ecf20Sopenharmony_ci{
20948c2ecf20Sopenharmony_ci	struct sk_buff_head *fdefq = &tnl->failover_deferdq;
20958c2ecf20Sopenharmony_ci
20968c2ecf20Sopenharmony_ci	tipc_link_create_dummy_tnl_msg(tnl, xmitq);
20978c2ecf20Sopenharmony_ci
20988c2ecf20Sopenharmony_ci	/* This failover link endpoint was never established before,
20998c2ecf20Sopenharmony_ci	 * so it has not received anything from peer.
21008c2ecf20Sopenharmony_ci	 * Otherwise, it must be a normal failover situation or the
21018c2ecf20Sopenharmony_ci	 * node has entered SELF_DOWN_PEER_LEAVING and both peer nodes
21028c2ecf20Sopenharmony_ci	 * would have to start over from scratch instead.
21038c2ecf20Sopenharmony_ci	 */
21048c2ecf20Sopenharmony_ci	tnl->drop_point = 1;
21058c2ecf20Sopenharmony_ci	tnl->failover_reasm_skb = NULL;
21068c2ecf20Sopenharmony_ci
21078c2ecf20Sopenharmony_ci	/* Initiate the link's failover deferdq */
21088c2ecf20Sopenharmony_ci	if (unlikely(!skb_queue_empty(fdefq))) {
21098c2ecf20Sopenharmony_ci		pr_warn("Link failover deferdq not empty: %d!\n",
21108c2ecf20Sopenharmony_ci			skb_queue_len(fdefq));
21118c2ecf20Sopenharmony_ci		__skb_queue_purge(fdefq);
21128c2ecf20Sopenharmony_ci	}
21138c2ecf20Sopenharmony_ci}
21148c2ecf20Sopenharmony_ci
21158c2ecf20Sopenharmony_ci/* tipc_link_validate_msg(): validate message against current link state
21168c2ecf20Sopenharmony_ci * Returns true if message should be accepted, otherwise false
21178c2ecf20Sopenharmony_ci */
21188c2ecf20Sopenharmony_cibool tipc_link_validate_msg(struct tipc_link *l, struct tipc_msg *hdr)
21198c2ecf20Sopenharmony_ci{
21208c2ecf20Sopenharmony_ci	u16 curr_session = l->peer_session;
21218c2ecf20Sopenharmony_ci	u16 session = msg_session(hdr);
21228c2ecf20Sopenharmony_ci	int mtyp = msg_type(hdr);
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_ci	if (msg_user(hdr) != LINK_PROTOCOL)
21258c2ecf20Sopenharmony_ci		return true;
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	switch (mtyp) {
21288c2ecf20Sopenharmony_ci	case RESET_MSG:
21298c2ecf20Sopenharmony_ci		if (!l->in_session)
21308c2ecf20Sopenharmony_ci			return true;
21318c2ecf20Sopenharmony_ci		/* Accept only RESET with new session number */
21328c2ecf20Sopenharmony_ci		return more(session, curr_session);
21338c2ecf20Sopenharmony_ci	case ACTIVATE_MSG:
21348c2ecf20Sopenharmony_ci		if (!l->in_session)
21358c2ecf20Sopenharmony_ci			return true;
21368c2ecf20Sopenharmony_ci		/* Accept only ACTIVATE with new or current session number */
21378c2ecf20Sopenharmony_ci		return !less(session, curr_session);
21388c2ecf20Sopenharmony_ci	case STATE_MSG:
21398c2ecf20Sopenharmony_ci		/* Accept only STATE with current session number */
21408c2ecf20Sopenharmony_ci		if (!l->in_session)
21418c2ecf20Sopenharmony_ci			return false;
21428c2ecf20Sopenharmony_ci		if (session != curr_session)
21438c2ecf20Sopenharmony_ci			return false;
21448c2ecf20Sopenharmony_ci		/* Extra sanity check */
21458c2ecf20Sopenharmony_ci		if (!link_is_up(l) && msg_ack(hdr))
21468c2ecf20Sopenharmony_ci			return false;
21478c2ecf20Sopenharmony_ci		if (!(l->peer_caps & TIPC_LINK_PROTO_SEQNO))
21488c2ecf20Sopenharmony_ci			return true;
21498c2ecf20Sopenharmony_ci		/* Accept only STATE with new sequence number */
21508c2ecf20Sopenharmony_ci		return !less(msg_seqno(hdr), l->rcv_nxt_state);
21518c2ecf20Sopenharmony_ci	default:
21528c2ecf20Sopenharmony_ci		return false;
21538c2ecf20Sopenharmony_ci	}
21548c2ecf20Sopenharmony_ci}
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_ci/* tipc_link_proto_rcv(): receive link level protocol message :
21578c2ecf20Sopenharmony_ci * Note that network plane id propagates through the network, and may
21588c2ecf20Sopenharmony_ci * change at any time. The node with lowest numerical id determines
21598c2ecf20Sopenharmony_ci * network plane
21608c2ecf20Sopenharmony_ci */
21618c2ecf20Sopenharmony_cistatic int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
21628c2ecf20Sopenharmony_ci			       struct sk_buff_head *xmitq)
21638c2ecf20Sopenharmony_ci{
21648c2ecf20Sopenharmony_ci	struct tipc_msg *hdr = buf_msg(skb);
21658c2ecf20Sopenharmony_ci	struct tipc_gap_ack_blks *ga = NULL;
21668c2ecf20Sopenharmony_ci	bool reply = msg_probe(hdr), retransmitted = false;
21678c2ecf20Sopenharmony_ci	u32 dlen = msg_data_sz(hdr), glen = 0, msg_max;
21688c2ecf20Sopenharmony_ci	u16 peers_snd_nxt =  msg_next_sent(hdr);
21698c2ecf20Sopenharmony_ci	u16 peers_tol = msg_link_tolerance(hdr);
21708c2ecf20Sopenharmony_ci	u16 peers_prio = msg_linkprio(hdr);
21718c2ecf20Sopenharmony_ci	u16 gap = msg_seq_gap(hdr);
21728c2ecf20Sopenharmony_ci	u16 ack = msg_ack(hdr);
21738c2ecf20Sopenharmony_ci	u16 rcv_nxt = l->rcv_nxt;
21748c2ecf20Sopenharmony_ci	u16 rcvgap = 0;
21758c2ecf20Sopenharmony_ci	int mtyp = msg_type(hdr);
21768c2ecf20Sopenharmony_ci	int rc = 0, released;
21778c2ecf20Sopenharmony_ci	char *if_name;
21788c2ecf20Sopenharmony_ci	void *data;
21798c2ecf20Sopenharmony_ci
21808c2ecf20Sopenharmony_ci	trace_tipc_proto_rcv(skb, false, l->name);
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_ci	if (dlen > U16_MAX)
21838c2ecf20Sopenharmony_ci		goto exit;
21848c2ecf20Sopenharmony_ci
21858c2ecf20Sopenharmony_ci	if (tipc_link_is_blocked(l) || !xmitq)
21868c2ecf20Sopenharmony_ci		goto exit;
21878c2ecf20Sopenharmony_ci
21888c2ecf20Sopenharmony_ci	if (tipc_own_addr(l->net) > msg_prevnode(hdr))
21898c2ecf20Sopenharmony_ci		l->net_plane = msg_net_plane(hdr);
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_ci	if (skb_linearize(skb))
21928c2ecf20Sopenharmony_ci		goto exit;
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_ci	hdr = buf_msg(skb);
21958c2ecf20Sopenharmony_ci	data = msg_data(hdr);
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci	if (!tipc_link_validate_msg(l, hdr)) {
21988c2ecf20Sopenharmony_ci		trace_tipc_skb_dump(skb, false, "PROTO invalid (1)!");
21998c2ecf20Sopenharmony_ci		trace_tipc_link_dump(l, TIPC_DUMP_NONE, "PROTO invalid (1)!");
22008c2ecf20Sopenharmony_ci		goto exit;
22018c2ecf20Sopenharmony_ci	}
22028c2ecf20Sopenharmony_ci
22038c2ecf20Sopenharmony_ci	switch (mtyp) {
22048c2ecf20Sopenharmony_ci	case RESET_MSG:
22058c2ecf20Sopenharmony_ci	case ACTIVATE_MSG:
22068c2ecf20Sopenharmony_ci		msg_max = msg_max_pkt(hdr);
22078c2ecf20Sopenharmony_ci		if (msg_max < tipc_bearer_min_mtu(l->net, l->bearer_id))
22088c2ecf20Sopenharmony_ci			break;
22098c2ecf20Sopenharmony_ci		/* Complete own link name with peer's interface name */
22108c2ecf20Sopenharmony_ci		if_name =  strrchr(l->name, ':') + 1;
22118c2ecf20Sopenharmony_ci		if (sizeof(l->name) - (if_name - l->name) <= TIPC_MAX_IF_NAME)
22128c2ecf20Sopenharmony_ci			break;
22138c2ecf20Sopenharmony_ci		if (msg_data_sz(hdr) < TIPC_MAX_IF_NAME)
22148c2ecf20Sopenharmony_ci			break;
22158c2ecf20Sopenharmony_ci		strncpy(if_name, data, TIPC_MAX_IF_NAME);
22168c2ecf20Sopenharmony_ci
22178c2ecf20Sopenharmony_ci		/* Update own tolerance if peer indicates a non-zero value */
22188c2ecf20Sopenharmony_ci		if (in_range(peers_tol, TIPC_MIN_LINK_TOL, TIPC_MAX_LINK_TOL)) {
22198c2ecf20Sopenharmony_ci			l->tolerance = peers_tol;
22208c2ecf20Sopenharmony_ci			l->bc_rcvlink->tolerance = peers_tol;
22218c2ecf20Sopenharmony_ci		}
22228c2ecf20Sopenharmony_ci		/* Update own priority if peer's priority is higher */
22238c2ecf20Sopenharmony_ci		if (in_range(peers_prio, l->priority + 1, TIPC_MAX_LINK_PRI))
22248c2ecf20Sopenharmony_ci			l->priority = peers_prio;
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_ci		/* If peer is going down we want full re-establish cycle */
22278c2ecf20Sopenharmony_ci		if (msg_peer_stopping(hdr)) {
22288c2ecf20Sopenharmony_ci			rc = tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
22298c2ecf20Sopenharmony_ci			break;
22308c2ecf20Sopenharmony_ci		}
22318c2ecf20Sopenharmony_ci
22328c2ecf20Sopenharmony_ci		/* If this endpoint was re-created while peer was ESTABLISHING
22338c2ecf20Sopenharmony_ci		 * it doesn't know current session number. Force re-synch.
22348c2ecf20Sopenharmony_ci		 */
22358c2ecf20Sopenharmony_ci		if (mtyp == ACTIVATE_MSG && msg_dest_session_valid(hdr) &&
22368c2ecf20Sopenharmony_ci		    l->session != msg_dest_session(hdr)) {
22378c2ecf20Sopenharmony_ci			if (less(l->session, msg_dest_session(hdr)))
22388c2ecf20Sopenharmony_ci				l->session = msg_dest_session(hdr) + 1;
22398c2ecf20Sopenharmony_ci			break;
22408c2ecf20Sopenharmony_ci		}
22418c2ecf20Sopenharmony_ci
22428c2ecf20Sopenharmony_ci		/* ACTIVATE_MSG serves as PEER_RESET if link is already down */
22438c2ecf20Sopenharmony_ci		if (mtyp == RESET_MSG || !link_is_up(l))
22448c2ecf20Sopenharmony_ci			rc = tipc_link_fsm_evt(l, LINK_PEER_RESET_EVT);
22458c2ecf20Sopenharmony_ci
22468c2ecf20Sopenharmony_ci		/* ACTIVATE_MSG takes up link if it was already locally reset */
22478c2ecf20Sopenharmony_ci		if (mtyp == ACTIVATE_MSG && l->state == LINK_ESTABLISHING)
22488c2ecf20Sopenharmony_ci			rc = TIPC_LINK_UP_EVT;
22498c2ecf20Sopenharmony_ci
22508c2ecf20Sopenharmony_ci		l->peer_session = msg_session(hdr);
22518c2ecf20Sopenharmony_ci		l->in_session = true;
22528c2ecf20Sopenharmony_ci		l->peer_bearer_id = msg_bearer_id(hdr);
22538c2ecf20Sopenharmony_ci		if (l->mtu > msg_max)
22548c2ecf20Sopenharmony_ci			l->mtu = msg_max;
22558c2ecf20Sopenharmony_ci		break;
22568c2ecf20Sopenharmony_ci
22578c2ecf20Sopenharmony_ci	case STATE_MSG:
22588c2ecf20Sopenharmony_ci		/* Validate Gap ACK blocks, drop if invalid */
22598c2ecf20Sopenharmony_ci		glen = tipc_get_gap_ack_blks(&ga, l, hdr, true);
22608c2ecf20Sopenharmony_ci		if (glen > dlen)
22618c2ecf20Sopenharmony_ci			break;
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_ci		l->rcv_nxt_state = msg_seqno(hdr) + 1;
22648c2ecf20Sopenharmony_ci
22658c2ecf20Sopenharmony_ci		/* Update own tolerance if peer indicates a non-zero value */
22668c2ecf20Sopenharmony_ci		if (in_range(peers_tol, TIPC_MIN_LINK_TOL, TIPC_MAX_LINK_TOL)) {
22678c2ecf20Sopenharmony_ci			l->tolerance = peers_tol;
22688c2ecf20Sopenharmony_ci			l->bc_rcvlink->tolerance = peers_tol;
22698c2ecf20Sopenharmony_ci		}
22708c2ecf20Sopenharmony_ci		/* Update own prio if peer indicates a different value */
22718c2ecf20Sopenharmony_ci		if ((peers_prio != l->priority) &&
22728c2ecf20Sopenharmony_ci		    in_range(peers_prio, 1, TIPC_MAX_LINK_PRI)) {
22738c2ecf20Sopenharmony_ci			l->priority = peers_prio;
22748c2ecf20Sopenharmony_ci			rc = tipc_link_fsm_evt(l, LINK_FAILURE_EVT);
22758c2ecf20Sopenharmony_ci		}
22768c2ecf20Sopenharmony_ci
22778c2ecf20Sopenharmony_ci		l->silent_intv_cnt = 0;
22788c2ecf20Sopenharmony_ci		l->stats.recv_states++;
22798c2ecf20Sopenharmony_ci		if (msg_probe(hdr))
22808c2ecf20Sopenharmony_ci			l->stats.recv_probes++;
22818c2ecf20Sopenharmony_ci
22828c2ecf20Sopenharmony_ci		if (!link_is_up(l)) {
22838c2ecf20Sopenharmony_ci			if (l->state == LINK_ESTABLISHING)
22848c2ecf20Sopenharmony_ci				rc = TIPC_LINK_UP_EVT;
22858c2ecf20Sopenharmony_ci			break;
22868c2ecf20Sopenharmony_ci		}
22878c2ecf20Sopenharmony_ci
22888c2ecf20Sopenharmony_ci		tipc_mon_rcv(l->net, data + glen, dlen - glen, l->addr,
22898c2ecf20Sopenharmony_ci			     &l->mon_state, l->bearer_id);
22908c2ecf20Sopenharmony_ci
22918c2ecf20Sopenharmony_ci		/* Send NACK if peer has sent pkts we haven't received yet */
22928c2ecf20Sopenharmony_ci		if ((reply || msg_is_keepalive(hdr)) &&
22938c2ecf20Sopenharmony_ci		    more(peers_snd_nxt, rcv_nxt) &&
22948c2ecf20Sopenharmony_ci		    !tipc_link_is_synching(l) &&
22958c2ecf20Sopenharmony_ci		    skb_queue_empty(&l->deferdq))
22968c2ecf20Sopenharmony_ci			rcvgap = peers_snd_nxt - l->rcv_nxt;
22978c2ecf20Sopenharmony_ci		if (rcvgap || reply)
22988c2ecf20Sopenharmony_ci			tipc_link_build_proto_msg(l, STATE_MSG, 0, reply,
22998c2ecf20Sopenharmony_ci						  rcvgap, 0, 0, xmitq);
23008c2ecf20Sopenharmony_ci
23018c2ecf20Sopenharmony_ci		released = tipc_link_advance_transmq(l, l, ack, gap, ga, xmitq,
23028c2ecf20Sopenharmony_ci						     &retransmitted, &rc);
23038c2ecf20Sopenharmony_ci		if (gap)
23048c2ecf20Sopenharmony_ci			l->stats.recv_nacks++;
23058c2ecf20Sopenharmony_ci		if (released || retransmitted)
23068c2ecf20Sopenharmony_ci			tipc_link_update_cwin(l, released, retransmitted);
23078c2ecf20Sopenharmony_ci		if (released)
23088c2ecf20Sopenharmony_ci			tipc_link_advance_backlog(l, xmitq);
23098c2ecf20Sopenharmony_ci		if (unlikely(!skb_queue_empty(&l->wakeupq)))
23108c2ecf20Sopenharmony_ci			link_prepare_wakeup(l);
23118c2ecf20Sopenharmony_ci	}
23128c2ecf20Sopenharmony_ciexit:
23138c2ecf20Sopenharmony_ci	kfree_skb(skb);
23148c2ecf20Sopenharmony_ci	return rc;
23158c2ecf20Sopenharmony_ci}
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_ci/* tipc_link_build_bc_proto_msg() - create broadcast protocol message
23188c2ecf20Sopenharmony_ci */
23198c2ecf20Sopenharmony_cistatic bool tipc_link_build_bc_proto_msg(struct tipc_link *l, bool bcast,
23208c2ecf20Sopenharmony_ci					 u16 peers_snd_nxt,
23218c2ecf20Sopenharmony_ci					 struct sk_buff_head *xmitq)
23228c2ecf20Sopenharmony_ci{
23238c2ecf20Sopenharmony_ci	struct sk_buff *skb;
23248c2ecf20Sopenharmony_ci	struct tipc_msg *hdr;
23258c2ecf20Sopenharmony_ci	struct sk_buff *dfrd_skb = skb_peek(&l->deferdq);
23268c2ecf20Sopenharmony_ci	u16 ack = l->rcv_nxt - 1;
23278c2ecf20Sopenharmony_ci	u16 gap_to = peers_snd_nxt - 1;
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_ci	skb = tipc_msg_create(BCAST_PROTOCOL, STATE_MSG, INT_H_SIZE,
23308c2ecf20Sopenharmony_ci			      0, l->addr, tipc_own_addr(l->net), 0, 0, 0);
23318c2ecf20Sopenharmony_ci	if (!skb)
23328c2ecf20Sopenharmony_ci		return false;
23338c2ecf20Sopenharmony_ci	hdr = buf_msg(skb);
23348c2ecf20Sopenharmony_ci	msg_set_last_bcast(hdr, l->bc_sndlink->snd_nxt - 1);
23358c2ecf20Sopenharmony_ci	msg_set_bcast_ack(hdr, ack);
23368c2ecf20Sopenharmony_ci	msg_set_bcgap_after(hdr, ack);
23378c2ecf20Sopenharmony_ci	if (dfrd_skb)
23388c2ecf20Sopenharmony_ci		gap_to = buf_seqno(dfrd_skb) - 1;
23398c2ecf20Sopenharmony_ci	msg_set_bcgap_to(hdr, gap_to);
23408c2ecf20Sopenharmony_ci	msg_set_non_seq(hdr, bcast);
23418c2ecf20Sopenharmony_ci	__skb_queue_tail(xmitq, skb);
23428c2ecf20Sopenharmony_ci	return true;
23438c2ecf20Sopenharmony_ci}
23448c2ecf20Sopenharmony_ci
23458c2ecf20Sopenharmony_ci/* tipc_link_build_bc_init_msg() - synchronize broadcast link endpoints.
23468c2ecf20Sopenharmony_ci *
23478c2ecf20Sopenharmony_ci * Give a newly added peer node the sequence number where it should
23488c2ecf20Sopenharmony_ci * start receiving and acking broadcast packets.
23498c2ecf20Sopenharmony_ci */
23508c2ecf20Sopenharmony_cistatic void tipc_link_build_bc_init_msg(struct tipc_link *l,
23518c2ecf20Sopenharmony_ci					struct sk_buff_head *xmitq)
23528c2ecf20Sopenharmony_ci{
23538c2ecf20Sopenharmony_ci	struct sk_buff_head list;
23548c2ecf20Sopenharmony_ci
23558c2ecf20Sopenharmony_ci	__skb_queue_head_init(&list);
23568c2ecf20Sopenharmony_ci	if (!tipc_link_build_bc_proto_msg(l->bc_rcvlink, false, 0, &list))
23578c2ecf20Sopenharmony_ci		return;
23588c2ecf20Sopenharmony_ci	msg_set_bc_ack_invalid(buf_msg(skb_peek(&list)), true);
23598c2ecf20Sopenharmony_ci	tipc_link_xmit(l, &list, xmitq);
23608c2ecf20Sopenharmony_ci}
23618c2ecf20Sopenharmony_ci
23628c2ecf20Sopenharmony_ci/* tipc_link_bc_init_rcv - receive initial broadcast synch data from peer
23638c2ecf20Sopenharmony_ci */
23648c2ecf20Sopenharmony_civoid tipc_link_bc_init_rcv(struct tipc_link *l, struct tipc_msg *hdr)
23658c2ecf20Sopenharmony_ci{
23668c2ecf20Sopenharmony_ci	int mtyp = msg_type(hdr);
23678c2ecf20Sopenharmony_ci	u16 peers_snd_nxt = msg_bc_snd_nxt(hdr);
23688c2ecf20Sopenharmony_ci
23698c2ecf20Sopenharmony_ci	if (link_is_up(l))
23708c2ecf20Sopenharmony_ci		return;
23718c2ecf20Sopenharmony_ci
23728c2ecf20Sopenharmony_ci	if (msg_user(hdr) == BCAST_PROTOCOL) {
23738c2ecf20Sopenharmony_ci		l->rcv_nxt = peers_snd_nxt;
23748c2ecf20Sopenharmony_ci		l->state = LINK_ESTABLISHED;
23758c2ecf20Sopenharmony_ci		return;
23768c2ecf20Sopenharmony_ci	}
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci	if (l->peer_caps & TIPC_BCAST_SYNCH)
23798c2ecf20Sopenharmony_ci		return;
23808c2ecf20Sopenharmony_ci
23818c2ecf20Sopenharmony_ci	if (msg_peer_node_is_up(hdr))
23828c2ecf20Sopenharmony_ci		return;
23838c2ecf20Sopenharmony_ci
23848c2ecf20Sopenharmony_ci	/* Compatibility: accept older, less safe initial synch data */
23858c2ecf20Sopenharmony_ci	if ((mtyp == RESET_MSG) || (mtyp == ACTIVATE_MSG))
23868c2ecf20Sopenharmony_ci		l->rcv_nxt = peers_snd_nxt;
23878c2ecf20Sopenharmony_ci}
23888c2ecf20Sopenharmony_ci
23898c2ecf20Sopenharmony_ci/* tipc_link_bc_sync_rcv - update rcv link according to peer's send state
23908c2ecf20Sopenharmony_ci */
23918c2ecf20Sopenharmony_ciint tipc_link_bc_sync_rcv(struct tipc_link *l, struct tipc_msg *hdr,
23928c2ecf20Sopenharmony_ci			  struct sk_buff_head *xmitq)
23938c2ecf20Sopenharmony_ci{
23948c2ecf20Sopenharmony_ci	u16 peers_snd_nxt = msg_bc_snd_nxt(hdr);
23958c2ecf20Sopenharmony_ci	int rc = 0;
23968c2ecf20Sopenharmony_ci
23978c2ecf20Sopenharmony_ci	if (!link_is_up(l))
23988c2ecf20Sopenharmony_ci		return rc;
23998c2ecf20Sopenharmony_ci
24008c2ecf20Sopenharmony_ci	if (!msg_peer_node_is_up(hdr))
24018c2ecf20Sopenharmony_ci		return rc;
24028c2ecf20Sopenharmony_ci
24038c2ecf20Sopenharmony_ci	/* Open when peer ackowledges our bcast init msg (pkt #1) */
24048c2ecf20Sopenharmony_ci	if (msg_ack(hdr))
24058c2ecf20Sopenharmony_ci		l->bc_peer_is_up = true;
24068c2ecf20Sopenharmony_ci
24078c2ecf20Sopenharmony_ci	if (!l->bc_peer_is_up)
24088c2ecf20Sopenharmony_ci		return rc;
24098c2ecf20Sopenharmony_ci
24108c2ecf20Sopenharmony_ci	/* Ignore if peers_snd_nxt goes beyond receive window */
24118c2ecf20Sopenharmony_ci	if (more(peers_snd_nxt, l->rcv_nxt + l->window))
24128c2ecf20Sopenharmony_ci		return rc;
24138c2ecf20Sopenharmony_ci
24148c2ecf20Sopenharmony_ci	l->snd_nxt = peers_snd_nxt;
24158c2ecf20Sopenharmony_ci	if (link_bc_rcv_gap(l))
24168c2ecf20Sopenharmony_ci		rc |= TIPC_LINK_SND_STATE;
24178c2ecf20Sopenharmony_ci
24188c2ecf20Sopenharmony_ci	/* Return now if sender supports nack via STATE messages */
24198c2ecf20Sopenharmony_ci	if (l->peer_caps & TIPC_BCAST_STATE_NACK)
24208c2ecf20Sopenharmony_ci		return rc;
24218c2ecf20Sopenharmony_ci
24228c2ecf20Sopenharmony_ci	/* Otherwise, be backwards compatible */
24238c2ecf20Sopenharmony_ci
24248c2ecf20Sopenharmony_ci	if (!more(peers_snd_nxt, l->rcv_nxt)) {
24258c2ecf20Sopenharmony_ci		l->nack_state = BC_NACK_SND_CONDITIONAL;
24268c2ecf20Sopenharmony_ci		return 0;
24278c2ecf20Sopenharmony_ci	}
24288c2ecf20Sopenharmony_ci
24298c2ecf20Sopenharmony_ci	/* Don't NACK if one was recently sent or peeked */
24308c2ecf20Sopenharmony_ci	if (l->nack_state == BC_NACK_SND_SUPPRESS) {
24318c2ecf20Sopenharmony_ci		l->nack_state = BC_NACK_SND_UNCONDITIONAL;
24328c2ecf20Sopenharmony_ci		return 0;
24338c2ecf20Sopenharmony_ci	}
24348c2ecf20Sopenharmony_ci
24358c2ecf20Sopenharmony_ci	/* Conditionally delay NACK sending until next synch rcv */
24368c2ecf20Sopenharmony_ci	if (l->nack_state == BC_NACK_SND_CONDITIONAL) {
24378c2ecf20Sopenharmony_ci		l->nack_state = BC_NACK_SND_UNCONDITIONAL;
24388c2ecf20Sopenharmony_ci		if ((peers_snd_nxt - l->rcv_nxt) < TIPC_MIN_LINK_WIN)
24398c2ecf20Sopenharmony_ci			return 0;
24408c2ecf20Sopenharmony_ci	}
24418c2ecf20Sopenharmony_ci
24428c2ecf20Sopenharmony_ci	/* Send NACK now but suppress next one */
24438c2ecf20Sopenharmony_ci	tipc_link_build_bc_proto_msg(l, true, peers_snd_nxt, xmitq);
24448c2ecf20Sopenharmony_ci	l->nack_state = BC_NACK_SND_SUPPRESS;
24458c2ecf20Sopenharmony_ci	return 0;
24468c2ecf20Sopenharmony_ci}
24478c2ecf20Sopenharmony_ci
24488c2ecf20Sopenharmony_ciint tipc_link_bc_ack_rcv(struct tipc_link *r, u16 acked, u16 gap,
24498c2ecf20Sopenharmony_ci			 struct tipc_gap_ack_blks *ga,
24508c2ecf20Sopenharmony_ci			 struct sk_buff_head *xmitq,
24518c2ecf20Sopenharmony_ci			 struct sk_buff_head *retrq)
24528c2ecf20Sopenharmony_ci{
24538c2ecf20Sopenharmony_ci	struct tipc_link *l = r->bc_sndlink;
24548c2ecf20Sopenharmony_ci	bool unused = false;
24558c2ecf20Sopenharmony_ci	int rc = 0;
24568c2ecf20Sopenharmony_ci
24578c2ecf20Sopenharmony_ci	if (!link_is_up(r) || !r->bc_peer_is_up)
24588c2ecf20Sopenharmony_ci		return 0;
24598c2ecf20Sopenharmony_ci
24608c2ecf20Sopenharmony_ci	if (gap) {
24618c2ecf20Sopenharmony_ci		l->stats.recv_nacks++;
24628c2ecf20Sopenharmony_ci		r->stats.recv_nacks++;
24638c2ecf20Sopenharmony_ci	}
24648c2ecf20Sopenharmony_ci
24658c2ecf20Sopenharmony_ci	if (less(acked, r->acked) || (acked == r->acked && !gap && !ga))
24668c2ecf20Sopenharmony_ci		return 0;
24678c2ecf20Sopenharmony_ci
24688c2ecf20Sopenharmony_ci	trace_tipc_link_bc_ack(r, acked, gap, &l->transmq);
24698c2ecf20Sopenharmony_ci	tipc_link_advance_transmq(l, r, acked, gap, ga, retrq, &unused, &rc);
24708c2ecf20Sopenharmony_ci
24718c2ecf20Sopenharmony_ci	tipc_link_advance_backlog(l, xmitq);
24728c2ecf20Sopenharmony_ci	if (unlikely(!skb_queue_empty(&l->wakeupq)))
24738c2ecf20Sopenharmony_ci		link_prepare_wakeup(l);
24748c2ecf20Sopenharmony_ci
24758c2ecf20Sopenharmony_ci	return rc;
24768c2ecf20Sopenharmony_ci}
24778c2ecf20Sopenharmony_ci
24788c2ecf20Sopenharmony_ci/* tipc_link_bc_nack_rcv(): receive broadcast nack message
24798c2ecf20Sopenharmony_ci * This function is here for backwards compatibility, since
24808c2ecf20Sopenharmony_ci * no BCAST_PROTOCOL/STATE messages occur from TIPC v2.5.
24818c2ecf20Sopenharmony_ci */
24828c2ecf20Sopenharmony_ciint tipc_link_bc_nack_rcv(struct tipc_link *l, struct sk_buff *skb,
24838c2ecf20Sopenharmony_ci			  struct sk_buff_head *xmitq)
24848c2ecf20Sopenharmony_ci{
24858c2ecf20Sopenharmony_ci	struct tipc_msg *hdr = buf_msg(skb);
24868c2ecf20Sopenharmony_ci	u32 dnode = msg_destnode(hdr);
24878c2ecf20Sopenharmony_ci	int mtyp = msg_type(hdr);
24888c2ecf20Sopenharmony_ci	u16 acked = msg_bcast_ack(hdr);
24898c2ecf20Sopenharmony_ci	u16 from = acked + 1;
24908c2ecf20Sopenharmony_ci	u16 to = msg_bcgap_to(hdr);
24918c2ecf20Sopenharmony_ci	u16 peers_snd_nxt = to + 1;
24928c2ecf20Sopenharmony_ci	int rc = 0;
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_ci	kfree_skb(skb);
24958c2ecf20Sopenharmony_ci
24968c2ecf20Sopenharmony_ci	if (!tipc_link_is_up(l) || !l->bc_peer_is_up)
24978c2ecf20Sopenharmony_ci		return 0;
24988c2ecf20Sopenharmony_ci
24998c2ecf20Sopenharmony_ci	if (mtyp != STATE_MSG)
25008c2ecf20Sopenharmony_ci		return 0;
25018c2ecf20Sopenharmony_ci
25028c2ecf20Sopenharmony_ci	if (dnode == tipc_own_addr(l->net)) {
25038c2ecf20Sopenharmony_ci		rc = tipc_link_bc_ack_rcv(l, acked, to - acked, NULL, xmitq,
25048c2ecf20Sopenharmony_ci					  xmitq);
25058c2ecf20Sopenharmony_ci		l->stats.recv_nacks++;
25068c2ecf20Sopenharmony_ci		return rc;
25078c2ecf20Sopenharmony_ci	}
25088c2ecf20Sopenharmony_ci
25098c2ecf20Sopenharmony_ci	/* Msg for other node => suppress own NACK at next sync if applicable */
25108c2ecf20Sopenharmony_ci	if (more(peers_snd_nxt, l->rcv_nxt) && !less(l->rcv_nxt, from))
25118c2ecf20Sopenharmony_ci		l->nack_state = BC_NACK_SND_SUPPRESS;
25128c2ecf20Sopenharmony_ci
25138c2ecf20Sopenharmony_ci	return 0;
25148c2ecf20Sopenharmony_ci}
25158c2ecf20Sopenharmony_ci
25168c2ecf20Sopenharmony_civoid tipc_link_set_queue_limits(struct tipc_link *l, u32 min_win, u32 max_win)
25178c2ecf20Sopenharmony_ci{
25188c2ecf20Sopenharmony_ci	int max_bulk = TIPC_MAX_PUBL / (l->mtu / ITEM_SIZE);
25198c2ecf20Sopenharmony_ci
25208c2ecf20Sopenharmony_ci	l->min_win = min_win;
25218c2ecf20Sopenharmony_ci	l->ssthresh = max_win;
25228c2ecf20Sopenharmony_ci	l->max_win = max_win;
25238c2ecf20Sopenharmony_ci	l->window = min_win;
25248c2ecf20Sopenharmony_ci	l->backlog[TIPC_LOW_IMPORTANCE].limit      = min_win * 2;
25258c2ecf20Sopenharmony_ci	l->backlog[TIPC_MEDIUM_IMPORTANCE].limit   = min_win * 4;
25268c2ecf20Sopenharmony_ci	l->backlog[TIPC_HIGH_IMPORTANCE].limit     = min_win * 6;
25278c2ecf20Sopenharmony_ci	l->backlog[TIPC_CRITICAL_IMPORTANCE].limit = min_win * 8;
25288c2ecf20Sopenharmony_ci	l->backlog[TIPC_SYSTEM_IMPORTANCE].limit   = max_bulk;
25298c2ecf20Sopenharmony_ci}
25308c2ecf20Sopenharmony_ci
25318c2ecf20Sopenharmony_ci/**
25328c2ecf20Sopenharmony_ci * link_reset_stats - reset link statistics
25338c2ecf20Sopenharmony_ci * @l: pointer to link
25348c2ecf20Sopenharmony_ci */
25358c2ecf20Sopenharmony_civoid tipc_link_reset_stats(struct tipc_link *l)
25368c2ecf20Sopenharmony_ci{
25378c2ecf20Sopenharmony_ci	memset(&l->stats, 0, sizeof(l->stats));
25388c2ecf20Sopenharmony_ci}
25398c2ecf20Sopenharmony_ci
25408c2ecf20Sopenharmony_cistatic void link_print(struct tipc_link *l, const char *str)
25418c2ecf20Sopenharmony_ci{
25428c2ecf20Sopenharmony_ci	struct sk_buff *hskb = skb_peek(&l->transmq);
25438c2ecf20Sopenharmony_ci	u16 head = hskb ? msg_seqno(buf_msg(hskb)) : l->snd_nxt - 1;
25448c2ecf20Sopenharmony_ci	u16 tail = l->snd_nxt - 1;
25458c2ecf20Sopenharmony_ci
25468c2ecf20Sopenharmony_ci	pr_info("%s Link <%s> state %x\n", str, l->name, l->state);
25478c2ecf20Sopenharmony_ci	pr_info("XMTQ: %u [%u-%u], BKLGQ: %u, SNDNX: %u, RCVNX: %u\n",
25488c2ecf20Sopenharmony_ci		skb_queue_len(&l->transmq), head, tail,
25498c2ecf20Sopenharmony_ci		skb_queue_len(&l->backlogq), l->snd_nxt, l->rcv_nxt);
25508c2ecf20Sopenharmony_ci}
25518c2ecf20Sopenharmony_ci
25528c2ecf20Sopenharmony_ci/* Parse and validate nested (link) properties valid for media, bearer and link
25538c2ecf20Sopenharmony_ci */
25548c2ecf20Sopenharmony_ciint tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[])
25558c2ecf20Sopenharmony_ci{
25568c2ecf20Sopenharmony_ci	int err;
25578c2ecf20Sopenharmony_ci
25588c2ecf20Sopenharmony_ci	err = nla_parse_nested_deprecated(props, TIPC_NLA_PROP_MAX, prop,
25598c2ecf20Sopenharmony_ci					  tipc_nl_prop_policy, NULL);
25608c2ecf20Sopenharmony_ci	if (err)
25618c2ecf20Sopenharmony_ci		return err;
25628c2ecf20Sopenharmony_ci
25638c2ecf20Sopenharmony_ci	if (props[TIPC_NLA_PROP_PRIO]) {
25648c2ecf20Sopenharmony_ci		u32 prio;
25658c2ecf20Sopenharmony_ci
25668c2ecf20Sopenharmony_ci		prio = nla_get_u32(props[TIPC_NLA_PROP_PRIO]);
25678c2ecf20Sopenharmony_ci		if (prio > TIPC_MAX_LINK_PRI)
25688c2ecf20Sopenharmony_ci			return -EINVAL;
25698c2ecf20Sopenharmony_ci	}
25708c2ecf20Sopenharmony_ci
25718c2ecf20Sopenharmony_ci	if (props[TIPC_NLA_PROP_TOL]) {
25728c2ecf20Sopenharmony_ci		u32 tol;
25738c2ecf20Sopenharmony_ci
25748c2ecf20Sopenharmony_ci		tol = nla_get_u32(props[TIPC_NLA_PROP_TOL]);
25758c2ecf20Sopenharmony_ci		if ((tol < TIPC_MIN_LINK_TOL) || (tol > TIPC_MAX_LINK_TOL))
25768c2ecf20Sopenharmony_ci			return -EINVAL;
25778c2ecf20Sopenharmony_ci	}
25788c2ecf20Sopenharmony_ci
25798c2ecf20Sopenharmony_ci	if (props[TIPC_NLA_PROP_WIN]) {
25808c2ecf20Sopenharmony_ci		u32 max_win;
25818c2ecf20Sopenharmony_ci
25828c2ecf20Sopenharmony_ci		max_win = nla_get_u32(props[TIPC_NLA_PROP_WIN]);
25838c2ecf20Sopenharmony_ci		if (max_win < TIPC_DEF_LINK_WIN || max_win > TIPC_MAX_LINK_WIN)
25848c2ecf20Sopenharmony_ci			return -EINVAL;
25858c2ecf20Sopenharmony_ci	}
25868c2ecf20Sopenharmony_ci
25878c2ecf20Sopenharmony_ci	return 0;
25888c2ecf20Sopenharmony_ci}
25898c2ecf20Sopenharmony_ci
25908c2ecf20Sopenharmony_cistatic int __tipc_nl_add_stats(struct sk_buff *skb, struct tipc_stats *s)
25918c2ecf20Sopenharmony_ci{
25928c2ecf20Sopenharmony_ci	int i;
25938c2ecf20Sopenharmony_ci	struct nlattr *stats;
25948c2ecf20Sopenharmony_ci
25958c2ecf20Sopenharmony_ci	struct nla_map {
25968c2ecf20Sopenharmony_ci		u32 key;
25978c2ecf20Sopenharmony_ci		u32 val;
25988c2ecf20Sopenharmony_ci	};
25998c2ecf20Sopenharmony_ci
26008c2ecf20Sopenharmony_ci	struct nla_map map[] = {
26018c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RX_INFO, 0},
26028c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RX_FRAGMENTS, s->recv_fragments},
26038c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RX_FRAGMENTED, s->recv_fragmented},
26048c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RX_BUNDLES, s->recv_bundles},
26058c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RX_BUNDLED, s->recv_bundled},
26068c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_TX_INFO, 0},
26078c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_TX_FRAGMENTS, s->sent_fragments},
26088c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_TX_FRAGMENTED, s->sent_fragmented},
26098c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_TX_BUNDLES, s->sent_bundles},
26108c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_TX_BUNDLED, s->sent_bundled},
26118c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_MSG_PROF_TOT, (s->msg_length_counts) ?
26128c2ecf20Sopenharmony_ci			s->msg_length_counts : 1},
26138c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_CNT, s->msg_length_counts},
26148c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_TOT, s->msg_lengths_total},
26158c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_P0, s->msg_length_profile[0]},
26168c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_P1, s->msg_length_profile[1]},
26178c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_P2, s->msg_length_profile[2]},
26188c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_P3, s->msg_length_profile[3]},
26198c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_P4, s->msg_length_profile[4]},
26208c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_P5, s->msg_length_profile[5]},
26218c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_MSG_LEN_P6, s->msg_length_profile[6]},
26228c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RX_STATES, s->recv_states},
26238c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RX_PROBES, s->recv_probes},
26248c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RX_NACKS, s->recv_nacks},
26258c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RX_DEFERRED, s->deferred_recv},
26268c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_TX_STATES, s->sent_states},
26278c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_TX_PROBES, s->sent_probes},
26288c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_TX_NACKS, s->sent_nacks},
26298c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_TX_ACKS, s->sent_acks},
26308c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RETRANSMITTED, s->retransmitted},
26318c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_DUPLICATES, s->duplicates},
26328c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_LINK_CONGS, s->link_congs},
26338c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_MAX_QUEUE, s->max_queue_sz},
26348c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_AVG_QUEUE, s->queue_sz_counts ?
26358c2ecf20Sopenharmony_ci			(s->accu_queue_sz / s->queue_sz_counts) : 0}
26368c2ecf20Sopenharmony_ci	};
26378c2ecf20Sopenharmony_ci
26388c2ecf20Sopenharmony_ci	stats = nla_nest_start_noflag(skb, TIPC_NLA_LINK_STATS);
26398c2ecf20Sopenharmony_ci	if (!stats)
26408c2ecf20Sopenharmony_ci		return -EMSGSIZE;
26418c2ecf20Sopenharmony_ci
26428c2ecf20Sopenharmony_ci	for (i = 0; i <  ARRAY_SIZE(map); i++)
26438c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, map[i].key, map[i].val))
26448c2ecf20Sopenharmony_ci			goto msg_full;
26458c2ecf20Sopenharmony_ci
26468c2ecf20Sopenharmony_ci	nla_nest_end(skb, stats);
26478c2ecf20Sopenharmony_ci
26488c2ecf20Sopenharmony_ci	return 0;
26498c2ecf20Sopenharmony_cimsg_full:
26508c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, stats);
26518c2ecf20Sopenharmony_ci
26528c2ecf20Sopenharmony_ci	return -EMSGSIZE;
26538c2ecf20Sopenharmony_ci}
26548c2ecf20Sopenharmony_ci
26558c2ecf20Sopenharmony_ci/* Caller should hold appropriate locks to protect the link */
26568c2ecf20Sopenharmony_ciint __tipc_nl_add_link(struct net *net, struct tipc_nl_msg *msg,
26578c2ecf20Sopenharmony_ci		       struct tipc_link *link, int nlflags)
26588c2ecf20Sopenharmony_ci{
26598c2ecf20Sopenharmony_ci	u32 self = tipc_own_addr(net);
26608c2ecf20Sopenharmony_ci	struct nlattr *attrs;
26618c2ecf20Sopenharmony_ci	struct nlattr *prop;
26628c2ecf20Sopenharmony_ci	void *hdr;
26638c2ecf20Sopenharmony_ci	int err;
26648c2ecf20Sopenharmony_ci
26658c2ecf20Sopenharmony_ci	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,
26668c2ecf20Sopenharmony_ci			  nlflags, TIPC_NL_LINK_GET);
26678c2ecf20Sopenharmony_ci	if (!hdr)
26688c2ecf20Sopenharmony_ci		return -EMSGSIZE;
26698c2ecf20Sopenharmony_ci
26708c2ecf20Sopenharmony_ci	attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_LINK);
26718c2ecf20Sopenharmony_ci	if (!attrs)
26728c2ecf20Sopenharmony_ci		goto msg_full;
26738c2ecf20Sopenharmony_ci
26748c2ecf20Sopenharmony_ci	if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, link->name))
26758c2ecf20Sopenharmony_ci		goto attr_msg_full;
26768c2ecf20Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_LINK_DEST, tipc_cluster_mask(self)))
26778c2ecf20Sopenharmony_ci		goto attr_msg_full;
26788c2ecf20Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_LINK_MTU, link->mtu))
26798c2ecf20Sopenharmony_ci		goto attr_msg_full;
26808c2ecf20Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, link->stats.recv_pkts))
26818c2ecf20Sopenharmony_ci		goto attr_msg_full;
26828c2ecf20Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, link->stats.sent_pkts))
26838c2ecf20Sopenharmony_ci		goto attr_msg_full;
26848c2ecf20Sopenharmony_ci
26858c2ecf20Sopenharmony_ci	if (tipc_link_is_up(link))
26868c2ecf20Sopenharmony_ci		if (nla_put_flag(msg->skb, TIPC_NLA_LINK_UP))
26878c2ecf20Sopenharmony_ci			goto attr_msg_full;
26888c2ecf20Sopenharmony_ci	if (link->active)
26898c2ecf20Sopenharmony_ci		if (nla_put_flag(msg->skb, TIPC_NLA_LINK_ACTIVE))
26908c2ecf20Sopenharmony_ci			goto attr_msg_full;
26918c2ecf20Sopenharmony_ci
26928c2ecf20Sopenharmony_ci	prop = nla_nest_start_noflag(msg->skb, TIPC_NLA_LINK_PROP);
26938c2ecf20Sopenharmony_ci	if (!prop)
26948c2ecf20Sopenharmony_ci		goto attr_msg_full;
26958c2ecf20Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, link->priority))
26968c2ecf20Sopenharmony_ci		goto prop_msg_full;
26978c2ecf20Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_PROP_TOL, link->tolerance))
26988c2ecf20Sopenharmony_ci		goto prop_msg_full;
26998c2ecf20Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN,
27008c2ecf20Sopenharmony_ci			link->window))
27018c2ecf20Sopenharmony_ci		goto prop_msg_full;
27028c2ecf20Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, link->priority))
27038c2ecf20Sopenharmony_ci		goto prop_msg_full;
27048c2ecf20Sopenharmony_ci	nla_nest_end(msg->skb, prop);
27058c2ecf20Sopenharmony_ci
27068c2ecf20Sopenharmony_ci	err = __tipc_nl_add_stats(msg->skb, &link->stats);
27078c2ecf20Sopenharmony_ci	if (err)
27088c2ecf20Sopenharmony_ci		goto attr_msg_full;
27098c2ecf20Sopenharmony_ci
27108c2ecf20Sopenharmony_ci	nla_nest_end(msg->skb, attrs);
27118c2ecf20Sopenharmony_ci	genlmsg_end(msg->skb, hdr);
27128c2ecf20Sopenharmony_ci
27138c2ecf20Sopenharmony_ci	return 0;
27148c2ecf20Sopenharmony_ci
27158c2ecf20Sopenharmony_ciprop_msg_full:
27168c2ecf20Sopenharmony_ci	nla_nest_cancel(msg->skb, prop);
27178c2ecf20Sopenharmony_ciattr_msg_full:
27188c2ecf20Sopenharmony_ci	nla_nest_cancel(msg->skb, attrs);
27198c2ecf20Sopenharmony_cimsg_full:
27208c2ecf20Sopenharmony_ci	genlmsg_cancel(msg->skb, hdr);
27218c2ecf20Sopenharmony_ci
27228c2ecf20Sopenharmony_ci	return -EMSGSIZE;
27238c2ecf20Sopenharmony_ci}
27248c2ecf20Sopenharmony_ci
27258c2ecf20Sopenharmony_cistatic int __tipc_nl_add_bc_link_stat(struct sk_buff *skb,
27268c2ecf20Sopenharmony_ci				      struct tipc_stats *stats)
27278c2ecf20Sopenharmony_ci{
27288c2ecf20Sopenharmony_ci	int i;
27298c2ecf20Sopenharmony_ci	struct nlattr *nest;
27308c2ecf20Sopenharmony_ci
27318c2ecf20Sopenharmony_ci	struct nla_map {
27328c2ecf20Sopenharmony_ci		__u32 key;
27338c2ecf20Sopenharmony_ci		__u32 val;
27348c2ecf20Sopenharmony_ci	};
27358c2ecf20Sopenharmony_ci
27368c2ecf20Sopenharmony_ci	struct nla_map map[] = {
27378c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RX_INFO, stats->recv_pkts},
27388c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RX_FRAGMENTS, stats->recv_fragments},
27398c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RX_FRAGMENTED, stats->recv_fragmented},
27408c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RX_BUNDLES, stats->recv_bundles},
27418c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RX_BUNDLED, stats->recv_bundled},
27428c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_TX_INFO, stats->sent_pkts},
27438c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_TX_FRAGMENTS, stats->sent_fragments},
27448c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_TX_FRAGMENTED, stats->sent_fragmented},
27458c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_TX_BUNDLES, stats->sent_bundles},
27468c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_TX_BUNDLED, stats->sent_bundled},
27478c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RX_NACKS, stats->recv_nacks},
27488c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RX_DEFERRED, stats->deferred_recv},
27498c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_TX_NACKS, stats->sent_nacks},
27508c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_TX_ACKS, stats->sent_acks},
27518c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_RETRANSMITTED, stats->retransmitted},
27528c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_DUPLICATES, stats->duplicates},
27538c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_LINK_CONGS, stats->link_congs},
27548c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_MAX_QUEUE, stats->max_queue_sz},
27558c2ecf20Sopenharmony_ci		{TIPC_NLA_STATS_AVG_QUEUE, stats->queue_sz_counts ?
27568c2ecf20Sopenharmony_ci			(stats->accu_queue_sz / stats->queue_sz_counts) : 0}
27578c2ecf20Sopenharmony_ci	};
27588c2ecf20Sopenharmony_ci
27598c2ecf20Sopenharmony_ci	nest = nla_nest_start_noflag(skb, TIPC_NLA_LINK_STATS);
27608c2ecf20Sopenharmony_ci	if (!nest)
27618c2ecf20Sopenharmony_ci		return -EMSGSIZE;
27628c2ecf20Sopenharmony_ci
27638c2ecf20Sopenharmony_ci	for (i = 0; i <  ARRAY_SIZE(map); i++)
27648c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, map[i].key, map[i].val))
27658c2ecf20Sopenharmony_ci			goto msg_full;
27668c2ecf20Sopenharmony_ci
27678c2ecf20Sopenharmony_ci	nla_nest_end(skb, nest);
27688c2ecf20Sopenharmony_ci
27698c2ecf20Sopenharmony_ci	return 0;
27708c2ecf20Sopenharmony_cimsg_full:
27718c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, nest);
27728c2ecf20Sopenharmony_ci
27738c2ecf20Sopenharmony_ci	return -EMSGSIZE;
27748c2ecf20Sopenharmony_ci}
27758c2ecf20Sopenharmony_ci
27768c2ecf20Sopenharmony_ciint tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg,
27778c2ecf20Sopenharmony_ci			struct tipc_link *bcl)
27788c2ecf20Sopenharmony_ci{
27798c2ecf20Sopenharmony_ci	int err;
27808c2ecf20Sopenharmony_ci	void *hdr;
27818c2ecf20Sopenharmony_ci	struct nlattr *attrs;
27828c2ecf20Sopenharmony_ci	struct nlattr *prop;
27838c2ecf20Sopenharmony_ci	u32 bc_mode = tipc_bcast_get_mode(net);
27848c2ecf20Sopenharmony_ci	u32 bc_ratio = tipc_bcast_get_broadcast_ratio(net);
27858c2ecf20Sopenharmony_ci
27868c2ecf20Sopenharmony_ci	if (!bcl)
27878c2ecf20Sopenharmony_ci		return 0;
27888c2ecf20Sopenharmony_ci
27898c2ecf20Sopenharmony_ci	tipc_bcast_lock(net);
27908c2ecf20Sopenharmony_ci
27918c2ecf20Sopenharmony_ci	hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,
27928c2ecf20Sopenharmony_ci			  NLM_F_MULTI, TIPC_NL_LINK_GET);
27938c2ecf20Sopenharmony_ci	if (!hdr) {
27948c2ecf20Sopenharmony_ci		tipc_bcast_unlock(net);
27958c2ecf20Sopenharmony_ci		return -EMSGSIZE;
27968c2ecf20Sopenharmony_ci	}
27978c2ecf20Sopenharmony_ci
27988c2ecf20Sopenharmony_ci	attrs = nla_nest_start_noflag(msg->skb, TIPC_NLA_LINK);
27998c2ecf20Sopenharmony_ci	if (!attrs)
28008c2ecf20Sopenharmony_ci		goto msg_full;
28018c2ecf20Sopenharmony_ci
28028c2ecf20Sopenharmony_ci	/* The broadcast link is always up */
28038c2ecf20Sopenharmony_ci	if (nla_put_flag(msg->skb, TIPC_NLA_LINK_UP))
28048c2ecf20Sopenharmony_ci		goto attr_msg_full;
28058c2ecf20Sopenharmony_ci
28068c2ecf20Sopenharmony_ci	if (nla_put_flag(msg->skb, TIPC_NLA_LINK_BROADCAST))
28078c2ecf20Sopenharmony_ci		goto attr_msg_full;
28088c2ecf20Sopenharmony_ci	if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, bcl->name))
28098c2ecf20Sopenharmony_ci		goto attr_msg_full;
28108c2ecf20Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, 0))
28118c2ecf20Sopenharmony_ci		goto attr_msg_full;
28128c2ecf20Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, 0))
28138c2ecf20Sopenharmony_ci		goto attr_msg_full;
28148c2ecf20Sopenharmony_ci
28158c2ecf20Sopenharmony_ci	prop = nla_nest_start_noflag(msg->skb, TIPC_NLA_LINK_PROP);
28168c2ecf20Sopenharmony_ci	if (!prop)
28178c2ecf20Sopenharmony_ci		goto attr_msg_full;
28188c2ecf20Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN, bcl->max_win))
28198c2ecf20Sopenharmony_ci		goto prop_msg_full;
28208c2ecf20Sopenharmony_ci	if (nla_put_u32(msg->skb, TIPC_NLA_PROP_BROADCAST, bc_mode))
28218c2ecf20Sopenharmony_ci		goto prop_msg_full;
28228c2ecf20Sopenharmony_ci	if (bc_mode & BCLINK_MODE_SEL)
28238c2ecf20Sopenharmony_ci		if (nla_put_u32(msg->skb, TIPC_NLA_PROP_BROADCAST_RATIO,
28248c2ecf20Sopenharmony_ci				bc_ratio))
28258c2ecf20Sopenharmony_ci			goto prop_msg_full;
28268c2ecf20Sopenharmony_ci	nla_nest_end(msg->skb, prop);
28278c2ecf20Sopenharmony_ci
28288c2ecf20Sopenharmony_ci	err = __tipc_nl_add_bc_link_stat(msg->skb, &bcl->stats);
28298c2ecf20Sopenharmony_ci	if (err)
28308c2ecf20Sopenharmony_ci		goto attr_msg_full;
28318c2ecf20Sopenharmony_ci
28328c2ecf20Sopenharmony_ci	tipc_bcast_unlock(net);
28338c2ecf20Sopenharmony_ci	nla_nest_end(msg->skb, attrs);
28348c2ecf20Sopenharmony_ci	genlmsg_end(msg->skb, hdr);
28358c2ecf20Sopenharmony_ci
28368c2ecf20Sopenharmony_ci	return 0;
28378c2ecf20Sopenharmony_ci
28388c2ecf20Sopenharmony_ciprop_msg_full:
28398c2ecf20Sopenharmony_ci	nla_nest_cancel(msg->skb, prop);
28408c2ecf20Sopenharmony_ciattr_msg_full:
28418c2ecf20Sopenharmony_ci	nla_nest_cancel(msg->skb, attrs);
28428c2ecf20Sopenharmony_cimsg_full:
28438c2ecf20Sopenharmony_ci	tipc_bcast_unlock(net);
28448c2ecf20Sopenharmony_ci	genlmsg_cancel(msg->skb, hdr);
28458c2ecf20Sopenharmony_ci
28468c2ecf20Sopenharmony_ci	return -EMSGSIZE;
28478c2ecf20Sopenharmony_ci}
28488c2ecf20Sopenharmony_ci
28498c2ecf20Sopenharmony_civoid tipc_link_set_tolerance(struct tipc_link *l, u32 tol,
28508c2ecf20Sopenharmony_ci			     struct sk_buff_head *xmitq)
28518c2ecf20Sopenharmony_ci{
28528c2ecf20Sopenharmony_ci	l->tolerance = tol;
28538c2ecf20Sopenharmony_ci	if (l->bc_rcvlink)
28548c2ecf20Sopenharmony_ci		l->bc_rcvlink->tolerance = tol;
28558c2ecf20Sopenharmony_ci	if (link_is_up(l))
28568c2ecf20Sopenharmony_ci		tipc_link_build_proto_msg(l, STATE_MSG, 0, 0, 0, tol, 0, xmitq);
28578c2ecf20Sopenharmony_ci}
28588c2ecf20Sopenharmony_ci
28598c2ecf20Sopenharmony_civoid tipc_link_set_prio(struct tipc_link *l, u32 prio,
28608c2ecf20Sopenharmony_ci			struct sk_buff_head *xmitq)
28618c2ecf20Sopenharmony_ci{
28628c2ecf20Sopenharmony_ci	l->priority = prio;
28638c2ecf20Sopenharmony_ci	tipc_link_build_proto_msg(l, STATE_MSG, 0, 0, 0, 0, prio, xmitq);
28648c2ecf20Sopenharmony_ci}
28658c2ecf20Sopenharmony_ci
28668c2ecf20Sopenharmony_civoid tipc_link_set_abort_limit(struct tipc_link *l, u32 limit)
28678c2ecf20Sopenharmony_ci{
28688c2ecf20Sopenharmony_ci	l->abort_limit = limit;
28698c2ecf20Sopenharmony_ci}
28708c2ecf20Sopenharmony_ci
28718c2ecf20Sopenharmony_ci/**
28728c2ecf20Sopenharmony_ci * tipc_link_dump - dump TIPC link data
28738c2ecf20Sopenharmony_ci * @l: tipc link to be dumped
28748c2ecf20Sopenharmony_ci * @dqueues: bitmask to decide if any link queue to be dumped?
28758c2ecf20Sopenharmony_ci *           - TIPC_DUMP_NONE: don't dump link queues
28768c2ecf20Sopenharmony_ci *           - TIPC_DUMP_TRANSMQ: dump link transmq queue
28778c2ecf20Sopenharmony_ci *           - TIPC_DUMP_BACKLOGQ: dump link backlog queue
28788c2ecf20Sopenharmony_ci *           - TIPC_DUMP_DEFERDQ: dump link deferd queue
28798c2ecf20Sopenharmony_ci *           - TIPC_DUMP_INPUTQ: dump link input queue
28808c2ecf20Sopenharmony_ci *           - TIPC_DUMP_WAKEUP: dump link wakeup queue
28818c2ecf20Sopenharmony_ci *           - TIPC_DUMP_ALL: dump all the link queues above
28828c2ecf20Sopenharmony_ci * @buf: returned buffer of dump data in format
28838c2ecf20Sopenharmony_ci */
28848c2ecf20Sopenharmony_ciint tipc_link_dump(struct tipc_link *l, u16 dqueues, char *buf)
28858c2ecf20Sopenharmony_ci{
28868c2ecf20Sopenharmony_ci	int i = 0;
28878c2ecf20Sopenharmony_ci	size_t sz = (dqueues) ? LINK_LMAX : LINK_LMIN;
28888c2ecf20Sopenharmony_ci	struct sk_buff_head *list;
28898c2ecf20Sopenharmony_ci	struct sk_buff *hskb, *tskb;
28908c2ecf20Sopenharmony_ci	u32 len;
28918c2ecf20Sopenharmony_ci
28928c2ecf20Sopenharmony_ci	if (!l) {
28938c2ecf20Sopenharmony_ci		i += scnprintf(buf, sz, "link data: (null)\n");
28948c2ecf20Sopenharmony_ci		return i;
28958c2ecf20Sopenharmony_ci	}
28968c2ecf20Sopenharmony_ci
28978c2ecf20Sopenharmony_ci	i += scnprintf(buf, sz, "link data: %x", l->addr);
28988c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %x", l->state);
28998c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->in_session);
29008c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->session);
29018c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->peer_session);
29028c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->snd_nxt);
29038c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->rcv_nxt);
29048c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->snd_nxt_state);
29058c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->rcv_nxt_state);
29068c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %x", l->peer_caps);
29078c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->silent_intv_cnt);
29088c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->rst_cnt);
29098c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", 0);
29108c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", 0);
29118c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " %u", l->acked);
29128c2ecf20Sopenharmony_ci
29138c2ecf20Sopenharmony_ci	list = &l->transmq;
29148c2ecf20Sopenharmony_ci	len = skb_queue_len(list);
29158c2ecf20Sopenharmony_ci	hskb = skb_peek(list);
29168c2ecf20Sopenharmony_ci	tskb = skb_peek_tail(list);
29178c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " | %u %u %u", len,
29188c2ecf20Sopenharmony_ci		       (hskb) ? msg_seqno(buf_msg(hskb)) : 0,
29198c2ecf20Sopenharmony_ci		       (tskb) ? msg_seqno(buf_msg(tskb)) : 0);
29208c2ecf20Sopenharmony_ci
29218c2ecf20Sopenharmony_ci	list = &l->deferdq;
29228c2ecf20Sopenharmony_ci	len = skb_queue_len(list);
29238c2ecf20Sopenharmony_ci	hskb = skb_peek(list);
29248c2ecf20Sopenharmony_ci	tskb = skb_peek_tail(list);
29258c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " | %u %u %u", len,
29268c2ecf20Sopenharmony_ci		       (hskb) ? msg_seqno(buf_msg(hskb)) : 0,
29278c2ecf20Sopenharmony_ci		       (tskb) ? msg_seqno(buf_msg(tskb)) : 0);
29288c2ecf20Sopenharmony_ci
29298c2ecf20Sopenharmony_ci	list = &l->backlogq;
29308c2ecf20Sopenharmony_ci	len = skb_queue_len(list);
29318c2ecf20Sopenharmony_ci	hskb = skb_peek(list);
29328c2ecf20Sopenharmony_ci	tskb = skb_peek_tail(list);
29338c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " | %u %u %u", len,
29348c2ecf20Sopenharmony_ci		       (hskb) ? msg_seqno(buf_msg(hskb)) : 0,
29358c2ecf20Sopenharmony_ci		       (tskb) ? msg_seqno(buf_msg(tskb)) : 0);
29368c2ecf20Sopenharmony_ci
29378c2ecf20Sopenharmony_ci	list = l->inputq;
29388c2ecf20Sopenharmony_ci	len = skb_queue_len(list);
29398c2ecf20Sopenharmony_ci	hskb = skb_peek(list);
29408c2ecf20Sopenharmony_ci	tskb = skb_peek_tail(list);
29418c2ecf20Sopenharmony_ci	i += scnprintf(buf + i, sz - i, " | %u %u %u\n", len,
29428c2ecf20Sopenharmony_ci		       (hskb) ? msg_seqno(buf_msg(hskb)) : 0,
29438c2ecf20Sopenharmony_ci		       (tskb) ? msg_seqno(buf_msg(tskb)) : 0);
29448c2ecf20Sopenharmony_ci
29458c2ecf20Sopenharmony_ci	if (dqueues & TIPC_DUMP_TRANSMQ) {
29468c2ecf20Sopenharmony_ci		i += scnprintf(buf + i, sz - i, "transmq: ");
29478c2ecf20Sopenharmony_ci		i += tipc_list_dump(&l->transmq, false, buf + i);
29488c2ecf20Sopenharmony_ci	}
29498c2ecf20Sopenharmony_ci	if (dqueues & TIPC_DUMP_BACKLOGQ) {
29508c2ecf20Sopenharmony_ci		i += scnprintf(buf + i, sz - i,
29518c2ecf20Sopenharmony_ci			       "backlogq: <%u %u %u %u %u>, ",
29528c2ecf20Sopenharmony_ci			       l->backlog[TIPC_LOW_IMPORTANCE].len,
29538c2ecf20Sopenharmony_ci			       l->backlog[TIPC_MEDIUM_IMPORTANCE].len,
29548c2ecf20Sopenharmony_ci			       l->backlog[TIPC_HIGH_IMPORTANCE].len,
29558c2ecf20Sopenharmony_ci			       l->backlog[TIPC_CRITICAL_IMPORTANCE].len,
29568c2ecf20Sopenharmony_ci			       l->backlog[TIPC_SYSTEM_IMPORTANCE].len);
29578c2ecf20Sopenharmony_ci		i += tipc_list_dump(&l->backlogq, false, buf + i);
29588c2ecf20Sopenharmony_ci	}
29598c2ecf20Sopenharmony_ci	if (dqueues & TIPC_DUMP_DEFERDQ) {
29608c2ecf20Sopenharmony_ci		i += scnprintf(buf + i, sz - i, "deferdq: ");
29618c2ecf20Sopenharmony_ci		i += tipc_list_dump(&l->deferdq, false, buf + i);
29628c2ecf20Sopenharmony_ci	}
29638c2ecf20Sopenharmony_ci	if (dqueues & TIPC_DUMP_INPUTQ) {
29648c2ecf20Sopenharmony_ci		i += scnprintf(buf + i, sz - i, "inputq: ");
29658c2ecf20Sopenharmony_ci		i += tipc_list_dump(l->inputq, false, buf + i);
29668c2ecf20Sopenharmony_ci	}
29678c2ecf20Sopenharmony_ci	if (dqueues & TIPC_DUMP_WAKEUP) {
29688c2ecf20Sopenharmony_ci		i += scnprintf(buf + i, sz - i, "wakeup: ");
29698c2ecf20Sopenharmony_ci		i += tipc_list_dump(&l->wakeupq, false, buf + i);
29708c2ecf20Sopenharmony_ci	}
29718c2ecf20Sopenharmony_ci
29728c2ecf20Sopenharmony_ci	return i;
29738c2ecf20Sopenharmony_ci}
2974