18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *   Robert Olsson <robert.olsson@its.uu.se> Uppsala Universitet
58c2ecf20Sopenharmony_ci *     & Swedish University of Agricultural Sciences.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *   Jens Laas <jens.laas@data.slu.se> Swedish University of
88c2ecf20Sopenharmony_ci *     Agricultural Sciences.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *   Hans Liss <hans.liss@its.uu.se>  Uppsala Universitet
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * This work is based on the LPC-trie which is originally described in:
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * An experimental study of compression methods for dynamic tries
158c2ecf20Sopenharmony_ci * Stefan Nilsson and Matti Tikkanen. Algorithmica, 33(1):19-33, 2002.
168c2ecf20Sopenharmony_ci * https://www.csc.kth.se/~snilsson/software/dyntrie2/
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * IP-address lookup using LC-tries. Stefan Nilsson and Gunnar Karlsson
198c2ecf20Sopenharmony_ci * IEEE Journal on Selected Areas in Communications, 17(6):1083-1092, June 1999
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * Code from fib_hash has been reused which includes the following header:
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * INET		An implementation of the TCP/IP protocol suite for the LINUX
248c2ecf20Sopenharmony_ci *		operating system.  INET is implemented using the  BSD Socket
258c2ecf20Sopenharmony_ci *		interface as the means of communication with the user level.
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci *		IPv4 FIB: lookup engine and maintenance routines.
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * Substantial contributions to this work comes from:
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci *		David S. Miller, <davem@davemloft.net>
348c2ecf20Sopenharmony_ci *		Stephen Hemminger <shemminger@osdl.org>
358c2ecf20Sopenharmony_ci *		Paul E. McKenney <paulmck@us.ibm.com>
368c2ecf20Sopenharmony_ci *		Patrick McHardy <kaber@trash.net>
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_ci#include <linux/cache.h>
398c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
408c2ecf20Sopenharmony_ci#include <linux/bitops.h>
418c2ecf20Sopenharmony_ci#include <linux/types.h>
428c2ecf20Sopenharmony_ci#include <linux/kernel.h>
438c2ecf20Sopenharmony_ci#include <linux/mm.h>
448c2ecf20Sopenharmony_ci#include <linux/string.h>
458c2ecf20Sopenharmony_ci#include <linux/socket.h>
468c2ecf20Sopenharmony_ci#include <linux/sockios.h>
478c2ecf20Sopenharmony_ci#include <linux/errno.h>
488c2ecf20Sopenharmony_ci#include <linux/in.h>
498c2ecf20Sopenharmony_ci#include <linux/inet.h>
508c2ecf20Sopenharmony_ci#include <linux/inetdevice.h>
518c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
528c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
538c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
548c2ecf20Sopenharmony_ci#include <linux/rcupdate.h>
558c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
568c2ecf20Sopenharmony_ci#include <linux/netlink.h>
578c2ecf20Sopenharmony_ci#include <linux/init.h>
588c2ecf20Sopenharmony_ci#include <linux/list.h>
598c2ecf20Sopenharmony_ci#include <linux/slab.h>
608c2ecf20Sopenharmony_ci#include <linux/export.h>
618c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
628c2ecf20Sopenharmony_ci#include <linux/notifier.h>
638c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
648c2ecf20Sopenharmony_ci#include <net/ip.h>
658c2ecf20Sopenharmony_ci#include <net/protocol.h>
668c2ecf20Sopenharmony_ci#include <net/route.h>
678c2ecf20Sopenharmony_ci#include <net/tcp.h>
688c2ecf20Sopenharmony_ci#include <net/sock.h>
698c2ecf20Sopenharmony_ci#include <net/ip_fib.h>
708c2ecf20Sopenharmony_ci#include <net/fib_notifier.h>
718c2ecf20Sopenharmony_ci#include <trace/events/fib.h>
728c2ecf20Sopenharmony_ci#include "fib_lookup.h"
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic int call_fib_entry_notifier(struct notifier_block *nb,
758c2ecf20Sopenharmony_ci				   enum fib_event_type event_type, u32 dst,
768c2ecf20Sopenharmony_ci				   int dst_len, struct fib_alias *fa,
778c2ecf20Sopenharmony_ci				   struct netlink_ext_ack *extack)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct fib_entry_notifier_info info = {
808c2ecf20Sopenharmony_ci		.info.extack = extack,
818c2ecf20Sopenharmony_ci		.dst = dst,
828c2ecf20Sopenharmony_ci		.dst_len = dst_len,
838c2ecf20Sopenharmony_ci		.fi = fa->fa_info,
848c2ecf20Sopenharmony_ci		.tos = fa->fa_tos,
858c2ecf20Sopenharmony_ci		.type = fa->fa_type,
868c2ecf20Sopenharmony_ci		.tb_id = fa->tb_id,
878c2ecf20Sopenharmony_ci	};
888c2ecf20Sopenharmony_ci	return call_fib4_notifier(nb, event_type, &info.info);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int call_fib_entry_notifiers(struct net *net,
928c2ecf20Sopenharmony_ci				    enum fib_event_type event_type, u32 dst,
938c2ecf20Sopenharmony_ci				    int dst_len, struct fib_alias *fa,
948c2ecf20Sopenharmony_ci				    struct netlink_ext_ack *extack)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	struct fib_entry_notifier_info info = {
978c2ecf20Sopenharmony_ci		.info.extack = extack,
988c2ecf20Sopenharmony_ci		.dst = dst,
998c2ecf20Sopenharmony_ci		.dst_len = dst_len,
1008c2ecf20Sopenharmony_ci		.fi = fa->fa_info,
1018c2ecf20Sopenharmony_ci		.tos = fa->fa_tos,
1028c2ecf20Sopenharmony_ci		.type = fa->fa_type,
1038c2ecf20Sopenharmony_ci		.tb_id = fa->tb_id,
1048c2ecf20Sopenharmony_ci	};
1058c2ecf20Sopenharmony_ci	return call_fib4_notifiers(net, event_type, &info.info);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci#define MAX_STAT_DEPTH 32
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci#define KEYLENGTH	(8*sizeof(t_key))
1118c2ecf20Sopenharmony_ci#define KEY_MAX		((t_key)~0)
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_citypedef unsigned int t_key;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci#define IS_TRIE(n)	((n)->pos >= KEYLENGTH)
1168c2ecf20Sopenharmony_ci#define IS_TNODE(n)	((n)->bits)
1178c2ecf20Sopenharmony_ci#define IS_LEAF(n)	(!(n)->bits)
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistruct key_vector {
1208c2ecf20Sopenharmony_ci	t_key key;
1218c2ecf20Sopenharmony_ci	unsigned char pos;		/* 2log(KEYLENGTH) bits needed */
1228c2ecf20Sopenharmony_ci	unsigned char bits;		/* 2log(KEYLENGTH) bits needed */
1238c2ecf20Sopenharmony_ci	unsigned char slen;
1248c2ecf20Sopenharmony_ci	union {
1258c2ecf20Sopenharmony_ci		/* This list pointer if valid if (pos | bits) == 0 (LEAF) */
1268c2ecf20Sopenharmony_ci		struct hlist_head leaf;
1278c2ecf20Sopenharmony_ci		/* This array is valid if (pos | bits) > 0 (TNODE) */
1288c2ecf20Sopenharmony_ci		struct key_vector __rcu *tnode[0];
1298c2ecf20Sopenharmony_ci	};
1308c2ecf20Sopenharmony_ci};
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistruct tnode {
1338c2ecf20Sopenharmony_ci	struct rcu_head rcu;
1348c2ecf20Sopenharmony_ci	t_key empty_children;		/* KEYLENGTH bits needed */
1358c2ecf20Sopenharmony_ci	t_key full_children;		/* KEYLENGTH bits needed */
1368c2ecf20Sopenharmony_ci	struct key_vector __rcu *parent;
1378c2ecf20Sopenharmony_ci	struct key_vector kv[1];
1388c2ecf20Sopenharmony_ci#define tn_bits kv[0].bits
1398c2ecf20Sopenharmony_ci};
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci#define TNODE_SIZE(n)	offsetof(struct tnode, kv[0].tnode[n])
1428c2ecf20Sopenharmony_ci#define LEAF_SIZE	TNODE_SIZE(1)
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS
1458c2ecf20Sopenharmony_cistruct trie_use_stats {
1468c2ecf20Sopenharmony_ci	unsigned int gets;
1478c2ecf20Sopenharmony_ci	unsigned int backtrack;
1488c2ecf20Sopenharmony_ci	unsigned int semantic_match_passed;
1498c2ecf20Sopenharmony_ci	unsigned int semantic_match_miss;
1508c2ecf20Sopenharmony_ci	unsigned int null_node_hit;
1518c2ecf20Sopenharmony_ci	unsigned int resize_node_skipped;
1528c2ecf20Sopenharmony_ci};
1538c2ecf20Sopenharmony_ci#endif
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistruct trie_stat {
1568c2ecf20Sopenharmony_ci	unsigned int totdepth;
1578c2ecf20Sopenharmony_ci	unsigned int maxdepth;
1588c2ecf20Sopenharmony_ci	unsigned int tnodes;
1598c2ecf20Sopenharmony_ci	unsigned int leaves;
1608c2ecf20Sopenharmony_ci	unsigned int nullpointers;
1618c2ecf20Sopenharmony_ci	unsigned int prefixes;
1628c2ecf20Sopenharmony_ci	unsigned int nodesizes[MAX_STAT_DEPTH];
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistruct trie {
1668c2ecf20Sopenharmony_ci	struct key_vector kv[1];
1678c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS
1688c2ecf20Sopenharmony_ci	struct trie_use_stats __percpu *stats;
1698c2ecf20Sopenharmony_ci#endif
1708c2ecf20Sopenharmony_ci};
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic struct key_vector *resize(struct trie *t, struct key_vector *tn);
1738c2ecf20Sopenharmony_cistatic unsigned int tnode_free_size;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/*
1768c2ecf20Sopenharmony_ci * synchronize_rcu after call_rcu for outstanding dirty memory; it should be
1778c2ecf20Sopenharmony_ci * especially useful before resizing the root node with PREEMPT_NONE configs;
1788c2ecf20Sopenharmony_ci * the value was obtained experimentally, aiming to avoid visible slowdown.
1798c2ecf20Sopenharmony_ci */
1808c2ecf20Sopenharmony_ciunsigned int sysctl_fib_sync_mem = 512 * 1024;
1818c2ecf20Sopenharmony_ciunsigned int sysctl_fib_sync_mem_min = 64 * 1024;
1828c2ecf20Sopenharmony_ciunsigned int sysctl_fib_sync_mem_max = 64 * 1024 * 1024;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic struct kmem_cache *fn_alias_kmem __ro_after_init;
1858c2ecf20Sopenharmony_cistatic struct kmem_cache *trie_leaf_kmem __ro_after_init;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic inline struct tnode *tn_info(struct key_vector *kv)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	return container_of(kv, struct tnode, kv[0]);
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/* caller must hold RTNL */
1938c2ecf20Sopenharmony_ci#define node_parent(tn) rtnl_dereference(tn_info(tn)->parent)
1948c2ecf20Sopenharmony_ci#define get_child(tn, i) rtnl_dereference((tn)->tnode[i])
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci/* caller must hold RCU read lock or RTNL */
1978c2ecf20Sopenharmony_ci#define node_parent_rcu(tn) rcu_dereference_rtnl(tn_info(tn)->parent)
1988c2ecf20Sopenharmony_ci#define get_child_rcu(tn, i) rcu_dereference_rtnl((tn)->tnode[i])
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci/* wrapper for rcu_assign_pointer */
2018c2ecf20Sopenharmony_cistatic inline void node_set_parent(struct key_vector *n, struct key_vector *tp)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	if (n)
2048c2ecf20Sopenharmony_ci		rcu_assign_pointer(tn_info(n)->parent, tp);
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci#define NODE_INIT_PARENT(n, p) RCU_INIT_POINTER(tn_info(n)->parent, p)
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci/* This provides us with the number of children in this node, in the case of a
2108c2ecf20Sopenharmony_ci * leaf this will return 0 meaning none of the children are accessible.
2118c2ecf20Sopenharmony_ci */
2128c2ecf20Sopenharmony_cistatic inline unsigned long child_length(const struct key_vector *tn)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	return (1ul << tn->bits) & ~(1ul);
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci#define get_cindex(key, kv) (((key) ^ (kv)->key) >> (kv)->pos)
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic inline unsigned long get_index(t_key key, struct key_vector *kv)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	unsigned long index = key ^ kv->key;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if ((BITS_PER_LONG <= KEYLENGTH) && (KEYLENGTH == kv->pos))
2248c2ecf20Sopenharmony_ci		return 0;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	return index >> kv->pos;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci/* To understand this stuff, an understanding of keys and all their bits is
2308c2ecf20Sopenharmony_ci * necessary. Every node in the trie has a key associated with it, but not
2318c2ecf20Sopenharmony_ci * all of the bits in that key are significant.
2328c2ecf20Sopenharmony_ci *
2338c2ecf20Sopenharmony_ci * Consider a node 'n' and its parent 'tp'.
2348c2ecf20Sopenharmony_ci *
2358c2ecf20Sopenharmony_ci * If n is a leaf, every bit in its key is significant. Its presence is
2368c2ecf20Sopenharmony_ci * necessitated by path compression, since during a tree traversal (when
2378c2ecf20Sopenharmony_ci * searching for a leaf - unless we are doing an insertion) we will completely
2388c2ecf20Sopenharmony_ci * ignore all skipped bits we encounter. Thus we need to verify, at the end of
2398c2ecf20Sopenharmony_ci * a potentially successful search, that we have indeed been walking the
2408c2ecf20Sopenharmony_ci * correct key path.
2418c2ecf20Sopenharmony_ci *
2428c2ecf20Sopenharmony_ci * Note that we can never "miss" the correct key in the tree if present by
2438c2ecf20Sopenharmony_ci * following the wrong path. Path compression ensures that segments of the key
2448c2ecf20Sopenharmony_ci * that are the same for all keys with a given prefix are skipped, but the
2458c2ecf20Sopenharmony_ci * skipped part *is* identical for each node in the subtrie below the skipped
2468c2ecf20Sopenharmony_ci * bit! trie_insert() in this implementation takes care of that.
2478c2ecf20Sopenharmony_ci *
2488c2ecf20Sopenharmony_ci * if n is an internal node - a 'tnode' here, the various parts of its key
2498c2ecf20Sopenharmony_ci * have many different meanings.
2508c2ecf20Sopenharmony_ci *
2518c2ecf20Sopenharmony_ci * Example:
2528c2ecf20Sopenharmony_ci * _________________________________________________________________
2538c2ecf20Sopenharmony_ci * | i | i | i | i | i | i | i | N | N | N | S | S | S | S | S | C |
2548c2ecf20Sopenharmony_ci * -----------------------------------------------------------------
2558c2ecf20Sopenharmony_ci *  31  30  29  28  27  26  25  24  23  22  21  20  19  18  17  16
2568c2ecf20Sopenharmony_ci *
2578c2ecf20Sopenharmony_ci * _________________________________________________________________
2588c2ecf20Sopenharmony_ci * | C | C | C | u | u | u | u | u | u | u | u | u | u | u | u | u |
2598c2ecf20Sopenharmony_ci * -----------------------------------------------------------------
2608c2ecf20Sopenharmony_ci *  15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
2618c2ecf20Sopenharmony_ci *
2628c2ecf20Sopenharmony_ci * tp->pos = 22
2638c2ecf20Sopenharmony_ci * tp->bits = 3
2648c2ecf20Sopenharmony_ci * n->pos = 13
2658c2ecf20Sopenharmony_ci * n->bits = 4
2668c2ecf20Sopenharmony_ci *
2678c2ecf20Sopenharmony_ci * First, let's just ignore the bits that come before the parent tp, that is
2688c2ecf20Sopenharmony_ci * the bits from (tp->pos + tp->bits) to 31. They are *known* but at this
2698c2ecf20Sopenharmony_ci * point we do not use them for anything.
2708c2ecf20Sopenharmony_ci *
2718c2ecf20Sopenharmony_ci * The bits from (tp->pos) to (tp->pos + tp->bits - 1) - "N", above - are the
2728c2ecf20Sopenharmony_ci * index into the parent's child array. That is, they will be used to find
2738c2ecf20Sopenharmony_ci * 'n' among tp's children.
2748c2ecf20Sopenharmony_ci *
2758c2ecf20Sopenharmony_ci * The bits from (n->pos + n->bits) to (tp->pos - 1) - "S" - are skipped bits
2768c2ecf20Sopenharmony_ci * for the node n.
2778c2ecf20Sopenharmony_ci *
2788c2ecf20Sopenharmony_ci * All the bits we have seen so far are significant to the node n. The rest
2798c2ecf20Sopenharmony_ci * of the bits are really not needed or indeed known in n->key.
2808c2ecf20Sopenharmony_ci *
2818c2ecf20Sopenharmony_ci * The bits from (n->pos) to (n->pos + n->bits - 1) - "C" - are the index into
2828c2ecf20Sopenharmony_ci * n's child array, and will of course be different for each child.
2838c2ecf20Sopenharmony_ci *
2848c2ecf20Sopenharmony_ci * The rest of the bits, from 0 to (n->pos -1) - "u" - are completely unknown
2858c2ecf20Sopenharmony_ci * at this point.
2868c2ecf20Sopenharmony_ci */
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic const int halve_threshold = 25;
2898c2ecf20Sopenharmony_cistatic const int inflate_threshold = 50;
2908c2ecf20Sopenharmony_cistatic const int halve_threshold_root = 15;
2918c2ecf20Sopenharmony_cistatic const int inflate_threshold_root = 30;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic void __alias_free_mem(struct rcu_head *head)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	struct fib_alias *fa = container_of(head, struct fib_alias, rcu);
2968c2ecf20Sopenharmony_ci	kmem_cache_free(fn_alias_kmem, fa);
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic inline void alias_free_mem_rcu(struct fib_alias *fa)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	call_rcu(&fa->rcu, __alias_free_mem);
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci#define TNODE_VMALLOC_MAX \
3058c2ecf20Sopenharmony_ci	ilog2((SIZE_MAX - TNODE_SIZE(0)) / sizeof(struct key_vector *))
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic void __node_free_rcu(struct rcu_head *head)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct tnode *n = container_of(head, struct tnode, rcu);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (!n->tn_bits)
3128c2ecf20Sopenharmony_ci		kmem_cache_free(trie_leaf_kmem, n);
3138c2ecf20Sopenharmony_ci	else
3148c2ecf20Sopenharmony_ci		kvfree(n);
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci#define node_free(n) call_rcu(&tn_info(n)->rcu, __node_free_rcu)
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic struct tnode *tnode_alloc(int bits)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	size_t size;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	/* verify bits is within bounds */
3248c2ecf20Sopenharmony_ci	if (bits > TNODE_VMALLOC_MAX)
3258c2ecf20Sopenharmony_ci		return NULL;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	/* determine size and verify it is non-zero and didn't overflow */
3288c2ecf20Sopenharmony_ci	size = TNODE_SIZE(1ul << bits);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (size <= PAGE_SIZE)
3318c2ecf20Sopenharmony_ci		return kzalloc(size, GFP_KERNEL);
3328c2ecf20Sopenharmony_ci	else
3338c2ecf20Sopenharmony_ci		return vzalloc(size);
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic inline void empty_child_inc(struct key_vector *n)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	tn_info(n)->empty_children++;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if (!tn_info(n)->empty_children)
3418c2ecf20Sopenharmony_ci		tn_info(n)->full_children++;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic inline void empty_child_dec(struct key_vector *n)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	if (!tn_info(n)->empty_children)
3478c2ecf20Sopenharmony_ci		tn_info(n)->full_children--;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	tn_info(n)->empty_children--;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic struct key_vector *leaf_new(t_key key, struct fib_alias *fa)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	struct key_vector *l;
3558c2ecf20Sopenharmony_ci	struct tnode *kv;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	kv = kmem_cache_alloc(trie_leaf_kmem, GFP_KERNEL);
3588c2ecf20Sopenharmony_ci	if (!kv)
3598c2ecf20Sopenharmony_ci		return NULL;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	/* initialize key vector */
3628c2ecf20Sopenharmony_ci	l = kv->kv;
3638c2ecf20Sopenharmony_ci	l->key = key;
3648c2ecf20Sopenharmony_ci	l->pos = 0;
3658c2ecf20Sopenharmony_ci	l->bits = 0;
3668c2ecf20Sopenharmony_ci	l->slen = fa->fa_slen;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	/* link leaf to fib alias */
3698c2ecf20Sopenharmony_ci	INIT_HLIST_HEAD(&l->leaf);
3708c2ecf20Sopenharmony_ci	hlist_add_head(&fa->fa_list, &l->leaf);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	return l;
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic struct key_vector *tnode_new(t_key key, int pos, int bits)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	unsigned int shift = pos + bits;
3788c2ecf20Sopenharmony_ci	struct key_vector *tn;
3798c2ecf20Sopenharmony_ci	struct tnode *tnode;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	/* verify bits and pos their msb bits clear and values are valid */
3828c2ecf20Sopenharmony_ci	BUG_ON(!bits || (shift > KEYLENGTH));
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	tnode = tnode_alloc(bits);
3858c2ecf20Sopenharmony_ci	if (!tnode)
3868c2ecf20Sopenharmony_ci		return NULL;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	pr_debug("AT %p s=%zu %zu\n", tnode, TNODE_SIZE(0),
3898c2ecf20Sopenharmony_ci		 sizeof(struct key_vector *) << bits);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (bits == KEYLENGTH)
3928c2ecf20Sopenharmony_ci		tnode->full_children = 1;
3938c2ecf20Sopenharmony_ci	else
3948c2ecf20Sopenharmony_ci		tnode->empty_children = 1ul << bits;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	tn = tnode->kv;
3978c2ecf20Sopenharmony_ci	tn->key = (shift < KEYLENGTH) ? (key >> shift) << shift : 0;
3988c2ecf20Sopenharmony_ci	tn->pos = pos;
3998c2ecf20Sopenharmony_ci	tn->bits = bits;
4008c2ecf20Sopenharmony_ci	tn->slen = pos;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	return tn;
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci/* Check whether a tnode 'n' is "full", i.e. it is an internal node
4068c2ecf20Sopenharmony_ci * and no bits are skipped. See discussion in dyntree paper p. 6
4078c2ecf20Sopenharmony_ci */
4088c2ecf20Sopenharmony_cistatic inline int tnode_full(struct key_vector *tn, struct key_vector *n)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	return n && ((n->pos + n->bits) == tn->pos) && IS_TNODE(n);
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci/* Add a child at position i overwriting the old value.
4148c2ecf20Sopenharmony_ci * Update the value of full_children and empty_children.
4158c2ecf20Sopenharmony_ci */
4168c2ecf20Sopenharmony_cistatic void put_child(struct key_vector *tn, unsigned long i,
4178c2ecf20Sopenharmony_ci		      struct key_vector *n)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	struct key_vector *chi = get_child(tn, i);
4208c2ecf20Sopenharmony_ci	int isfull, wasfull;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	BUG_ON(i >= child_length(tn));
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	/* update emptyChildren, overflow into fullChildren */
4258c2ecf20Sopenharmony_ci	if (!n && chi)
4268c2ecf20Sopenharmony_ci		empty_child_inc(tn);
4278c2ecf20Sopenharmony_ci	if (n && !chi)
4288c2ecf20Sopenharmony_ci		empty_child_dec(tn);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	/* update fullChildren */
4318c2ecf20Sopenharmony_ci	wasfull = tnode_full(tn, chi);
4328c2ecf20Sopenharmony_ci	isfull = tnode_full(tn, n);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	if (wasfull && !isfull)
4358c2ecf20Sopenharmony_ci		tn_info(tn)->full_children--;
4368c2ecf20Sopenharmony_ci	else if (!wasfull && isfull)
4378c2ecf20Sopenharmony_ci		tn_info(tn)->full_children++;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	if (n && (tn->slen < n->slen))
4408c2ecf20Sopenharmony_ci		tn->slen = n->slen;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	rcu_assign_pointer(tn->tnode[i], n);
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cistatic void update_children(struct key_vector *tn)
4468c2ecf20Sopenharmony_ci{
4478c2ecf20Sopenharmony_ci	unsigned long i;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	/* update all of the child parent pointers */
4508c2ecf20Sopenharmony_ci	for (i = child_length(tn); i;) {
4518c2ecf20Sopenharmony_ci		struct key_vector *inode = get_child(tn, --i);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci		if (!inode)
4548c2ecf20Sopenharmony_ci			continue;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci		/* Either update the children of a tnode that
4578c2ecf20Sopenharmony_ci		 * already belongs to us or update the child
4588c2ecf20Sopenharmony_ci		 * to point to ourselves.
4598c2ecf20Sopenharmony_ci		 */
4608c2ecf20Sopenharmony_ci		if (node_parent(inode) == tn)
4618c2ecf20Sopenharmony_ci			update_children(inode);
4628c2ecf20Sopenharmony_ci		else
4638c2ecf20Sopenharmony_ci			node_set_parent(inode, tn);
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic inline void put_child_root(struct key_vector *tp, t_key key,
4688c2ecf20Sopenharmony_ci				  struct key_vector *n)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	if (IS_TRIE(tp))
4718c2ecf20Sopenharmony_ci		rcu_assign_pointer(tp->tnode[0], n);
4728c2ecf20Sopenharmony_ci	else
4738c2ecf20Sopenharmony_ci		put_child(tp, get_index(key, tp), n);
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic inline void tnode_free_init(struct key_vector *tn)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	tn_info(tn)->rcu.next = NULL;
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic inline void tnode_free_append(struct key_vector *tn,
4828c2ecf20Sopenharmony_ci				     struct key_vector *n)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	tn_info(n)->rcu.next = tn_info(tn)->rcu.next;
4858c2ecf20Sopenharmony_ci	tn_info(tn)->rcu.next = &tn_info(n)->rcu;
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cistatic void tnode_free(struct key_vector *tn)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	struct callback_head *head = &tn_info(tn)->rcu;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	while (head) {
4938c2ecf20Sopenharmony_ci		head = head->next;
4948c2ecf20Sopenharmony_ci		tnode_free_size += TNODE_SIZE(1ul << tn->bits);
4958c2ecf20Sopenharmony_ci		node_free(tn);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci		tn = container_of(head, struct tnode, rcu)->kv;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	if (tnode_free_size >= READ_ONCE(sysctl_fib_sync_mem)) {
5018c2ecf20Sopenharmony_ci		tnode_free_size = 0;
5028c2ecf20Sopenharmony_ci		synchronize_rcu();
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_cistatic struct key_vector *replace(struct trie *t,
5078c2ecf20Sopenharmony_ci				  struct key_vector *oldtnode,
5088c2ecf20Sopenharmony_ci				  struct key_vector *tn)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	struct key_vector *tp = node_parent(oldtnode);
5118c2ecf20Sopenharmony_ci	unsigned long i;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	/* setup the parent pointer out of and back into this node */
5148c2ecf20Sopenharmony_ci	NODE_INIT_PARENT(tn, tp);
5158c2ecf20Sopenharmony_ci	put_child_root(tp, tn->key, tn);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/* update all of the child parent pointers */
5188c2ecf20Sopenharmony_ci	update_children(tn);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	/* all pointers should be clean so we are done */
5218c2ecf20Sopenharmony_ci	tnode_free(oldtnode);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	/* resize children now that oldtnode is freed */
5248c2ecf20Sopenharmony_ci	for (i = child_length(tn); i;) {
5258c2ecf20Sopenharmony_ci		struct key_vector *inode = get_child(tn, --i);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci		/* resize child node */
5288c2ecf20Sopenharmony_ci		if (tnode_full(tn, inode))
5298c2ecf20Sopenharmony_ci			tn = resize(t, inode);
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	return tp;
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cistatic struct key_vector *inflate(struct trie *t,
5368c2ecf20Sopenharmony_ci				  struct key_vector *oldtnode)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	struct key_vector *tn;
5398c2ecf20Sopenharmony_ci	unsigned long i;
5408c2ecf20Sopenharmony_ci	t_key m;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	pr_debug("In inflate\n");
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	tn = tnode_new(oldtnode->key, oldtnode->pos - 1, oldtnode->bits + 1);
5458c2ecf20Sopenharmony_ci	if (!tn)
5468c2ecf20Sopenharmony_ci		goto notnode;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	/* prepare oldtnode to be freed */
5498c2ecf20Sopenharmony_ci	tnode_free_init(oldtnode);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	/* Assemble all of the pointers in our cluster, in this case that
5528c2ecf20Sopenharmony_ci	 * represents all of the pointers out of our allocated nodes that
5538c2ecf20Sopenharmony_ci	 * point to existing tnodes and the links between our allocated
5548c2ecf20Sopenharmony_ci	 * nodes.
5558c2ecf20Sopenharmony_ci	 */
5568c2ecf20Sopenharmony_ci	for (i = child_length(oldtnode), m = 1u << tn->pos; i;) {
5578c2ecf20Sopenharmony_ci		struct key_vector *inode = get_child(oldtnode, --i);
5588c2ecf20Sopenharmony_ci		struct key_vector *node0, *node1;
5598c2ecf20Sopenharmony_ci		unsigned long j, k;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci		/* An empty child */
5628c2ecf20Sopenharmony_ci		if (!inode)
5638c2ecf20Sopenharmony_ci			continue;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci		/* A leaf or an internal node with skipped bits */
5668c2ecf20Sopenharmony_ci		if (!tnode_full(oldtnode, inode)) {
5678c2ecf20Sopenharmony_ci			put_child(tn, get_index(inode->key, tn), inode);
5688c2ecf20Sopenharmony_ci			continue;
5698c2ecf20Sopenharmony_ci		}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci		/* drop the node in the old tnode free list */
5728c2ecf20Sopenharmony_ci		tnode_free_append(oldtnode, inode);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci		/* An internal node with two children */
5758c2ecf20Sopenharmony_ci		if (inode->bits == 1) {
5768c2ecf20Sopenharmony_ci			put_child(tn, 2 * i + 1, get_child(inode, 1));
5778c2ecf20Sopenharmony_ci			put_child(tn, 2 * i, get_child(inode, 0));
5788c2ecf20Sopenharmony_ci			continue;
5798c2ecf20Sopenharmony_ci		}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci		/* We will replace this node 'inode' with two new
5828c2ecf20Sopenharmony_ci		 * ones, 'node0' and 'node1', each with half of the
5838c2ecf20Sopenharmony_ci		 * original children. The two new nodes will have
5848c2ecf20Sopenharmony_ci		 * a position one bit further down the key and this
5858c2ecf20Sopenharmony_ci		 * means that the "significant" part of their keys
5868c2ecf20Sopenharmony_ci		 * (see the discussion near the top of this file)
5878c2ecf20Sopenharmony_ci		 * will differ by one bit, which will be "0" in
5888c2ecf20Sopenharmony_ci		 * node0's key and "1" in node1's key. Since we are
5898c2ecf20Sopenharmony_ci		 * moving the key position by one step, the bit that
5908c2ecf20Sopenharmony_ci		 * we are moving away from - the bit at position
5918c2ecf20Sopenharmony_ci		 * (tn->pos) - is the one that will differ between
5928c2ecf20Sopenharmony_ci		 * node0 and node1. So... we synthesize that bit in the
5938c2ecf20Sopenharmony_ci		 * two new keys.
5948c2ecf20Sopenharmony_ci		 */
5958c2ecf20Sopenharmony_ci		node1 = tnode_new(inode->key | m, inode->pos, inode->bits - 1);
5968c2ecf20Sopenharmony_ci		if (!node1)
5978c2ecf20Sopenharmony_ci			goto nomem;
5988c2ecf20Sopenharmony_ci		node0 = tnode_new(inode->key, inode->pos, inode->bits - 1);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci		tnode_free_append(tn, node1);
6018c2ecf20Sopenharmony_ci		if (!node0)
6028c2ecf20Sopenharmony_ci			goto nomem;
6038c2ecf20Sopenharmony_ci		tnode_free_append(tn, node0);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci		/* populate child pointers in new nodes */
6068c2ecf20Sopenharmony_ci		for (k = child_length(inode), j = k / 2; j;) {
6078c2ecf20Sopenharmony_ci			put_child(node1, --j, get_child(inode, --k));
6088c2ecf20Sopenharmony_ci			put_child(node0, j, get_child(inode, j));
6098c2ecf20Sopenharmony_ci			put_child(node1, --j, get_child(inode, --k));
6108c2ecf20Sopenharmony_ci			put_child(node0, j, get_child(inode, j));
6118c2ecf20Sopenharmony_ci		}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci		/* link new nodes to parent */
6148c2ecf20Sopenharmony_ci		NODE_INIT_PARENT(node1, tn);
6158c2ecf20Sopenharmony_ci		NODE_INIT_PARENT(node0, tn);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci		/* link parent to nodes */
6188c2ecf20Sopenharmony_ci		put_child(tn, 2 * i + 1, node1);
6198c2ecf20Sopenharmony_ci		put_child(tn, 2 * i, node0);
6208c2ecf20Sopenharmony_ci	}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	/* setup the parent pointers into and out of this node */
6238c2ecf20Sopenharmony_ci	return replace(t, oldtnode, tn);
6248c2ecf20Sopenharmony_cinomem:
6258c2ecf20Sopenharmony_ci	/* all pointers should be clean so we are done */
6268c2ecf20Sopenharmony_ci	tnode_free(tn);
6278c2ecf20Sopenharmony_cinotnode:
6288c2ecf20Sopenharmony_ci	return NULL;
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cistatic struct key_vector *halve(struct trie *t,
6328c2ecf20Sopenharmony_ci				struct key_vector *oldtnode)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	struct key_vector *tn;
6358c2ecf20Sopenharmony_ci	unsigned long i;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	pr_debug("In halve\n");
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	tn = tnode_new(oldtnode->key, oldtnode->pos + 1, oldtnode->bits - 1);
6408c2ecf20Sopenharmony_ci	if (!tn)
6418c2ecf20Sopenharmony_ci		goto notnode;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	/* prepare oldtnode to be freed */
6448c2ecf20Sopenharmony_ci	tnode_free_init(oldtnode);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	/* Assemble all of the pointers in our cluster, in this case that
6478c2ecf20Sopenharmony_ci	 * represents all of the pointers out of our allocated nodes that
6488c2ecf20Sopenharmony_ci	 * point to existing tnodes and the links between our allocated
6498c2ecf20Sopenharmony_ci	 * nodes.
6508c2ecf20Sopenharmony_ci	 */
6518c2ecf20Sopenharmony_ci	for (i = child_length(oldtnode); i;) {
6528c2ecf20Sopenharmony_ci		struct key_vector *node1 = get_child(oldtnode, --i);
6538c2ecf20Sopenharmony_ci		struct key_vector *node0 = get_child(oldtnode, --i);
6548c2ecf20Sopenharmony_ci		struct key_vector *inode;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci		/* At least one of the children is empty */
6578c2ecf20Sopenharmony_ci		if (!node1 || !node0) {
6588c2ecf20Sopenharmony_ci			put_child(tn, i / 2, node1 ? : node0);
6598c2ecf20Sopenharmony_ci			continue;
6608c2ecf20Sopenharmony_ci		}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci		/* Two nonempty children */
6638c2ecf20Sopenharmony_ci		inode = tnode_new(node0->key, oldtnode->pos, 1);
6648c2ecf20Sopenharmony_ci		if (!inode)
6658c2ecf20Sopenharmony_ci			goto nomem;
6668c2ecf20Sopenharmony_ci		tnode_free_append(tn, inode);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci		/* initialize pointers out of node */
6698c2ecf20Sopenharmony_ci		put_child(inode, 1, node1);
6708c2ecf20Sopenharmony_ci		put_child(inode, 0, node0);
6718c2ecf20Sopenharmony_ci		NODE_INIT_PARENT(inode, tn);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci		/* link parent to node */
6748c2ecf20Sopenharmony_ci		put_child(tn, i / 2, inode);
6758c2ecf20Sopenharmony_ci	}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	/* setup the parent pointers into and out of this node */
6788c2ecf20Sopenharmony_ci	return replace(t, oldtnode, tn);
6798c2ecf20Sopenharmony_cinomem:
6808c2ecf20Sopenharmony_ci	/* all pointers should be clean so we are done */
6818c2ecf20Sopenharmony_ci	tnode_free(tn);
6828c2ecf20Sopenharmony_cinotnode:
6838c2ecf20Sopenharmony_ci	return NULL;
6848c2ecf20Sopenharmony_ci}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_cistatic struct key_vector *collapse(struct trie *t,
6878c2ecf20Sopenharmony_ci				   struct key_vector *oldtnode)
6888c2ecf20Sopenharmony_ci{
6898c2ecf20Sopenharmony_ci	struct key_vector *n, *tp;
6908c2ecf20Sopenharmony_ci	unsigned long i;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	/* scan the tnode looking for that one child that might still exist */
6938c2ecf20Sopenharmony_ci	for (n = NULL, i = child_length(oldtnode); !n && i;)
6948c2ecf20Sopenharmony_ci		n = get_child(oldtnode, --i);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	/* compress one level */
6978c2ecf20Sopenharmony_ci	tp = node_parent(oldtnode);
6988c2ecf20Sopenharmony_ci	put_child_root(tp, oldtnode->key, n);
6998c2ecf20Sopenharmony_ci	node_set_parent(n, tp);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	/* drop dead node */
7028c2ecf20Sopenharmony_ci	node_free(oldtnode);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	return tp;
7058c2ecf20Sopenharmony_ci}
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_cistatic unsigned char update_suffix(struct key_vector *tn)
7088c2ecf20Sopenharmony_ci{
7098c2ecf20Sopenharmony_ci	unsigned char slen = tn->pos;
7108c2ecf20Sopenharmony_ci	unsigned long stride, i;
7118c2ecf20Sopenharmony_ci	unsigned char slen_max;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	/* only vector 0 can have a suffix length greater than or equal to
7148c2ecf20Sopenharmony_ci	 * tn->pos + tn->bits, the second highest node will have a suffix
7158c2ecf20Sopenharmony_ci	 * length at most of tn->pos + tn->bits - 1
7168c2ecf20Sopenharmony_ci	 */
7178c2ecf20Sopenharmony_ci	slen_max = min_t(unsigned char, tn->pos + tn->bits - 1, tn->slen);
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	/* search though the list of children looking for nodes that might
7208c2ecf20Sopenharmony_ci	 * have a suffix greater than the one we currently have.  This is
7218c2ecf20Sopenharmony_ci	 * why we start with a stride of 2 since a stride of 1 would
7228c2ecf20Sopenharmony_ci	 * represent the nodes with suffix length equal to tn->pos
7238c2ecf20Sopenharmony_ci	 */
7248c2ecf20Sopenharmony_ci	for (i = 0, stride = 0x2ul ; i < child_length(tn); i += stride) {
7258c2ecf20Sopenharmony_ci		struct key_vector *n = get_child(tn, i);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci		if (!n || (n->slen <= slen))
7288c2ecf20Sopenharmony_ci			continue;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci		/* update stride and slen based on new value */
7318c2ecf20Sopenharmony_ci		stride <<= (n->slen - slen);
7328c2ecf20Sopenharmony_ci		slen = n->slen;
7338c2ecf20Sopenharmony_ci		i &= ~(stride - 1);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci		/* stop searching if we have hit the maximum possible value */
7368c2ecf20Sopenharmony_ci		if (slen >= slen_max)
7378c2ecf20Sopenharmony_ci			break;
7388c2ecf20Sopenharmony_ci	}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	tn->slen = slen;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	return slen;
7438c2ecf20Sopenharmony_ci}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci/* From "Implementing a dynamic compressed trie" by Stefan Nilsson of
7468c2ecf20Sopenharmony_ci * the Helsinki University of Technology and Matti Tikkanen of Nokia
7478c2ecf20Sopenharmony_ci * Telecommunications, page 6:
7488c2ecf20Sopenharmony_ci * "A node is doubled if the ratio of non-empty children to all
7498c2ecf20Sopenharmony_ci * children in the *doubled* node is at least 'high'."
7508c2ecf20Sopenharmony_ci *
7518c2ecf20Sopenharmony_ci * 'high' in this instance is the variable 'inflate_threshold'. It
7528c2ecf20Sopenharmony_ci * is expressed as a percentage, so we multiply it with
7538c2ecf20Sopenharmony_ci * child_length() and instead of multiplying by 2 (since the
7548c2ecf20Sopenharmony_ci * child array will be doubled by inflate()) and multiplying
7558c2ecf20Sopenharmony_ci * the left-hand side by 100 (to handle the percentage thing) we
7568c2ecf20Sopenharmony_ci * multiply the left-hand side by 50.
7578c2ecf20Sopenharmony_ci *
7588c2ecf20Sopenharmony_ci * The left-hand side may look a bit weird: child_length(tn)
7598c2ecf20Sopenharmony_ci * - tn->empty_children is of course the number of non-null children
7608c2ecf20Sopenharmony_ci * in the current node. tn->full_children is the number of "full"
7618c2ecf20Sopenharmony_ci * children, that is non-null tnodes with a skip value of 0.
7628c2ecf20Sopenharmony_ci * All of those will be doubled in the resulting inflated tnode, so
7638c2ecf20Sopenharmony_ci * we just count them one extra time here.
7648c2ecf20Sopenharmony_ci *
7658c2ecf20Sopenharmony_ci * A clearer way to write this would be:
7668c2ecf20Sopenharmony_ci *
7678c2ecf20Sopenharmony_ci * to_be_doubled = tn->full_children;
7688c2ecf20Sopenharmony_ci * not_to_be_doubled = child_length(tn) - tn->empty_children -
7698c2ecf20Sopenharmony_ci *     tn->full_children;
7708c2ecf20Sopenharmony_ci *
7718c2ecf20Sopenharmony_ci * new_child_length = child_length(tn) * 2;
7728c2ecf20Sopenharmony_ci *
7738c2ecf20Sopenharmony_ci * new_fill_factor = 100 * (not_to_be_doubled + 2*to_be_doubled) /
7748c2ecf20Sopenharmony_ci *      new_child_length;
7758c2ecf20Sopenharmony_ci * if (new_fill_factor >= inflate_threshold)
7768c2ecf20Sopenharmony_ci *
7778c2ecf20Sopenharmony_ci * ...and so on, tho it would mess up the while () loop.
7788c2ecf20Sopenharmony_ci *
7798c2ecf20Sopenharmony_ci * anyway,
7808c2ecf20Sopenharmony_ci * 100 * (not_to_be_doubled + 2*to_be_doubled) / new_child_length >=
7818c2ecf20Sopenharmony_ci *      inflate_threshold
7828c2ecf20Sopenharmony_ci *
7838c2ecf20Sopenharmony_ci * avoid a division:
7848c2ecf20Sopenharmony_ci * 100 * (not_to_be_doubled + 2*to_be_doubled) >=
7858c2ecf20Sopenharmony_ci *      inflate_threshold * new_child_length
7868c2ecf20Sopenharmony_ci *
7878c2ecf20Sopenharmony_ci * expand not_to_be_doubled and to_be_doubled, and shorten:
7888c2ecf20Sopenharmony_ci * 100 * (child_length(tn) - tn->empty_children +
7898c2ecf20Sopenharmony_ci *    tn->full_children) >= inflate_threshold * new_child_length
7908c2ecf20Sopenharmony_ci *
7918c2ecf20Sopenharmony_ci * expand new_child_length:
7928c2ecf20Sopenharmony_ci * 100 * (child_length(tn) - tn->empty_children +
7938c2ecf20Sopenharmony_ci *    tn->full_children) >=
7948c2ecf20Sopenharmony_ci *      inflate_threshold * child_length(tn) * 2
7958c2ecf20Sopenharmony_ci *
7968c2ecf20Sopenharmony_ci * shorten again:
7978c2ecf20Sopenharmony_ci * 50 * (tn->full_children + child_length(tn) -
7988c2ecf20Sopenharmony_ci *    tn->empty_children) >= inflate_threshold *
7998c2ecf20Sopenharmony_ci *    child_length(tn)
8008c2ecf20Sopenharmony_ci *
8018c2ecf20Sopenharmony_ci */
8028c2ecf20Sopenharmony_cistatic inline bool should_inflate(struct key_vector *tp, struct key_vector *tn)
8038c2ecf20Sopenharmony_ci{
8048c2ecf20Sopenharmony_ci	unsigned long used = child_length(tn);
8058c2ecf20Sopenharmony_ci	unsigned long threshold = used;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	/* Keep root node larger */
8088c2ecf20Sopenharmony_ci	threshold *= IS_TRIE(tp) ? inflate_threshold_root : inflate_threshold;
8098c2ecf20Sopenharmony_ci	used -= tn_info(tn)->empty_children;
8108c2ecf20Sopenharmony_ci	used += tn_info(tn)->full_children;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	/* if bits == KEYLENGTH then pos = 0, and will fail below */
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	return (used > 1) && tn->pos && ((50 * used) >= threshold);
8158c2ecf20Sopenharmony_ci}
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_cistatic inline bool should_halve(struct key_vector *tp, struct key_vector *tn)
8188c2ecf20Sopenharmony_ci{
8198c2ecf20Sopenharmony_ci	unsigned long used = child_length(tn);
8208c2ecf20Sopenharmony_ci	unsigned long threshold = used;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	/* Keep root node larger */
8238c2ecf20Sopenharmony_ci	threshold *= IS_TRIE(tp) ? halve_threshold_root : halve_threshold;
8248c2ecf20Sopenharmony_ci	used -= tn_info(tn)->empty_children;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	/* if bits == KEYLENGTH then used = 100% on wrap, and will fail below */
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	return (used > 1) && (tn->bits > 1) && ((100 * used) < threshold);
8298c2ecf20Sopenharmony_ci}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_cistatic inline bool should_collapse(struct key_vector *tn)
8328c2ecf20Sopenharmony_ci{
8338c2ecf20Sopenharmony_ci	unsigned long used = child_length(tn);
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	used -= tn_info(tn)->empty_children;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	/* account for bits == KEYLENGTH case */
8388c2ecf20Sopenharmony_ci	if ((tn->bits == KEYLENGTH) && tn_info(tn)->full_children)
8398c2ecf20Sopenharmony_ci		used -= KEY_MAX;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	/* One child or none, time to drop us from the trie */
8428c2ecf20Sopenharmony_ci	return used < 2;
8438c2ecf20Sopenharmony_ci}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci#define MAX_WORK 10
8468c2ecf20Sopenharmony_cistatic struct key_vector *resize(struct trie *t, struct key_vector *tn)
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS
8498c2ecf20Sopenharmony_ci	struct trie_use_stats __percpu *stats = t->stats;
8508c2ecf20Sopenharmony_ci#endif
8518c2ecf20Sopenharmony_ci	struct key_vector *tp = node_parent(tn);
8528c2ecf20Sopenharmony_ci	unsigned long cindex = get_index(tn->key, tp);
8538c2ecf20Sopenharmony_ci	int max_work = MAX_WORK;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	pr_debug("In tnode_resize %p inflate_threshold=%d threshold=%d\n",
8568c2ecf20Sopenharmony_ci		 tn, inflate_threshold, halve_threshold);
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	/* track the tnode via the pointer from the parent instead of
8598c2ecf20Sopenharmony_ci	 * doing it ourselves.  This way we can let RCU fully do its
8608c2ecf20Sopenharmony_ci	 * thing without us interfering
8618c2ecf20Sopenharmony_ci	 */
8628c2ecf20Sopenharmony_ci	BUG_ON(tn != get_child(tp, cindex));
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	/* Double as long as the resulting node has a number of
8658c2ecf20Sopenharmony_ci	 * nonempty nodes that are above the threshold.
8668c2ecf20Sopenharmony_ci	 */
8678c2ecf20Sopenharmony_ci	while (should_inflate(tp, tn) && max_work) {
8688c2ecf20Sopenharmony_ci		tp = inflate(t, tn);
8698c2ecf20Sopenharmony_ci		if (!tp) {
8708c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS
8718c2ecf20Sopenharmony_ci			this_cpu_inc(stats->resize_node_skipped);
8728c2ecf20Sopenharmony_ci#endif
8738c2ecf20Sopenharmony_ci			break;
8748c2ecf20Sopenharmony_ci		}
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci		max_work--;
8778c2ecf20Sopenharmony_ci		tn = get_child(tp, cindex);
8788c2ecf20Sopenharmony_ci	}
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	/* update parent in case inflate failed */
8818c2ecf20Sopenharmony_ci	tp = node_parent(tn);
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	/* Return if at least one inflate is run */
8848c2ecf20Sopenharmony_ci	if (max_work != MAX_WORK)
8858c2ecf20Sopenharmony_ci		return tp;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	/* Halve as long as the number of empty children in this
8888c2ecf20Sopenharmony_ci	 * node is above threshold.
8898c2ecf20Sopenharmony_ci	 */
8908c2ecf20Sopenharmony_ci	while (should_halve(tp, tn) && max_work) {
8918c2ecf20Sopenharmony_ci		tp = halve(t, tn);
8928c2ecf20Sopenharmony_ci		if (!tp) {
8938c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS
8948c2ecf20Sopenharmony_ci			this_cpu_inc(stats->resize_node_skipped);
8958c2ecf20Sopenharmony_ci#endif
8968c2ecf20Sopenharmony_ci			break;
8978c2ecf20Sopenharmony_ci		}
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci		max_work--;
9008c2ecf20Sopenharmony_ci		tn = get_child(tp, cindex);
9018c2ecf20Sopenharmony_ci	}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	/* Only one child remains */
9048c2ecf20Sopenharmony_ci	if (should_collapse(tn))
9058c2ecf20Sopenharmony_ci		return collapse(t, tn);
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	/* update parent in case halve failed */
9088c2ecf20Sopenharmony_ci	return node_parent(tn);
9098c2ecf20Sopenharmony_ci}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_cistatic void node_pull_suffix(struct key_vector *tn, unsigned char slen)
9128c2ecf20Sopenharmony_ci{
9138c2ecf20Sopenharmony_ci	unsigned char node_slen = tn->slen;
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	while ((node_slen > tn->pos) && (node_slen > slen)) {
9168c2ecf20Sopenharmony_ci		slen = update_suffix(tn);
9178c2ecf20Sopenharmony_ci		if (node_slen == slen)
9188c2ecf20Sopenharmony_ci			break;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci		tn = node_parent(tn);
9218c2ecf20Sopenharmony_ci		node_slen = tn->slen;
9228c2ecf20Sopenharmony_ci	}
9238c2ecf20Sopenharmony_ci}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_cistatic void node_push_suffix(struct key_vector *tn, unsigned char slen)
9268c2ecf20Sopenharmony_ci{
9278c2ecf20Sopenharmony_ci	while (tn->slen < slen) {
9288c2ecf20Sopenharmony_ci		tn->slen = slen;
9298c2ecf20Sopenharmony_ci		tn = node_parent(tn);
9308c2ecf20Sopenharmony_ci	}
9318c2ecf20Sopenharmony_ci}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci/* rcu_read_lock needs to be hold by caller from readside */
9348c2ecf20Sopenharmony_cistatic struct key_vector *fib_find_node(struct trie *t,
9358c2ecf20Sopenharmony_ci					struct key_vector **tp, u32 key)
9368c2ecf20Sopenharmony_ci{
9378c2ecf20Sopenharmony_ci	struct key_vector *pn, *n = t->kv;
9388c2ecf20Sopenharmony_ci	unsigned long index = 0;
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	do {
9418c2ecf20Sopenharmony_ci		pn = n;
9428c2ecf20Sopenharmony_ci		n = get_child_rcu(n, index);
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci		if (!n)
9458c2ecf20Sopenharmony_ci			break;
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci		index = get_cindex(key, n);
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci		/* This bit of code is a bit tricky but it combines multiple
9508c2ecf20Sopenharmony_ci		 * checks into a single check.  The prefix consists of the
9518c2ecf20Sopenharmony_ci		 * prefix plus zeros for the bits in the cindex. The index
9528c2ecf20Sopenharmony_ci		 * is the difference between the key and this value.  From
9538c2ecf20Sopenharmony_ci		 * this we can actually derive several pieces of data.
9548c2ecf20Sopenharmony_ci		 *   if (index >= (1ul << bits))
9558c2ecf20Sopenharmony_ci		 *     we have a mismatch in skip bits and failed
9568c2ecf20Sopenharmony_ci		 *   else
9578c2ecf20Sopenharmony_ci		 *     we know the value is cindex
9588c2ecf20Sopenharmony_ci		 *
9598c2ecf20Sopenharmony_ci		 * This check is safe even if bits == KEYLENGTH due to the
9608c2ecf20Sopenharmony_ci		 * fact that we can only allocate a node with 32 bits if a
9618c2ecf20Sopenharmony_ci		 * long is greater than 32 bits.
9628c2ecf20Sopenharmony_ci		 */
9638c2ecf20Sopenharmony_ci		if (index >= (1ul << n->bits)) {
9648c2ecf20Sopenharmony_ci			n = NULL;
9658c2ecf20Sopenharmony_ci			break;
9668c2ecf20Sopenharmony_ci		}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci		/* keep searching until we find a perfect match leaf or NULL */
9698c2ecf20Sopenharmony_ci	} while (IS_TNODE(n));
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	*tp = pn;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	return n;
9748c2ecf20Sopenharmony_ci}
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci/* Return the first fib alias matching TOS with
9778c2ecf20Sopenharmony_ci * priority less than or equal to PRIO.
9788c2ecf20Sopenharmony_ci * If 'find_first' is set, return the first matching
9798c2ecf20Sopenharmony_ci * fib alias, regardless of TOS and priority.
9808c2ecf20Sopenharmony_ci */
9818c2ecf20Sopenharmony_cistatic struct fib_alias *fib_find_alias(struct hlist_head *fah, u8 slen,
9828c2ecf20Sopenharmony_ci					u8 tos, u32 prio, u32 tb_id,
9838c2ecf20Sopenharmony_ci					bool find_first)
9848c2ecf20Sopenharmony_ci{
9858c2ecf20Sopenharmony_ci	struct fib_alias *fa;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	if (!fah)
9888c2ecf20Sopenharmony_ci		return NULL;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	hlist_for_each_entry(fa, fah, fa_list) {
9918c2ecf20Sopenharmony_ci		if (fa->fa_slen < slen)
9928c2ecf20Sopenharmony_ci			continue;
9938c2ecf20Sopenharmony_ci		if (fa->fa_slen != slen)
9948c2ecf20Sopenharmony_ci			break;
9958c2ecf20Sopenharmony_ci		if (fa->tb_id > tb_id)
9968c2ecf20Sopenharmony_ci			continue;
9978c2ecf20Sopenharmony_ci		if (fa->tb_id != tb_id)
9988c2ecf20Sopenharmony_ci			break;
9998c2ecf20Sopenharmony_ci		if (find_first)
10008c2ecf20Sopenharmony_ci			return fa;
10018c2ecf20Sopenharmony_ci		if (fa->fa_tos > tos)
10028c2ecf20Sopenharmony_ci			continue;
10038c2ecf20Sopenharmony_ci		if (fa->fa_info->fib_priority >= prio || fa->fa_tos < tos)
10048c2ecf20Sopenharmony_ci			return fa;
10058c2ecf20Sopenharmony_ci	}
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	return NULL;
10088c2ecf20Sopenharmony_ci}
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_cistatic struct fib_alias *
10118c2ecf20Sopenharmony_cifib_find_matching_alias(struct net *net, const struct fib_rt_info *fri)
10128c2ecf20Sopenharmony_ci{
10138c2ecf20Sopenharmony_ci	u8 slen = KEYLENGTH - fri->dst_len;
10148c2ecf20Sopenharmony_ci	struct key_vector *l, *tp;
10158c2ecf20Sopenharmony_ci	struct fib_table *tb;
10168c2ecf20Sopenharmony_ci	struct fib_alias *fa;
10178c2ecf20Sopenharmony_ci	struct trie *t;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	tb = fib_get_table(net, fri->tb_id);
10208c2ecf20Sopenharmony_ci	if (!tb)
10218c2ecf20Sopenharmony_ci		return NULL;
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	t = (struct trie *)tb->tb_data;
10248c2ecf20Sopenharmony_ci	l = fib_find_node(t, &tp, be32_to_cpu(fri->dst));
10258c2ecf20Sopenharmony_ci	if (!l)
10268c2ecf20Sopenharmony_ci		return NULL;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) {
10298c2ecf20Sopenharmony_ci		if (fa->fa_slen == slen && fa->tb_id == fri->tb_id &&
10308c2ecf20Sopenharmony_ci		    fa->fa_tos == fri->tos && fa->fa_info == fri->fi &&
10318c2ecf20Sopenharmony_ci		    fa->fa_type == fri->type)
10328c2ecf20Sopenharmony_ci			return fa;
10338c2ecf20Sopenharmony_ci	}
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	return NULL;
10368c2ecf20Sopenharmony_ci}
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_civoid fib_alias_hw_flags_set(struct net *net, const struct fib_rt_info *fri)
10398c2ecf20Sopenharmony_ci{
10408c2ecf20Sopenharmony_ci	struct fib_alias *fa_match;
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	rcu_read_lock();
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	fa_match = fib_find_matching_alias(net, fri);
10458c2ecf20Sopenharmony_ci	if (!fa_match)
10468c2ecf20Sopenharmony_ci		goto out;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	fa_match->offload = fri->offload;
10498c2ecf20Sopenharmony_ci	fa_match->trap = fri->trap;
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ciout:
10528c2ecf20Sopenharmony_ci	rcu_read_unlock();
10538c2ecf20Sopenharmony_ci}
10548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib_alias_hw_flags_set);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_cistatic void trie_rebalance(struct trie *t, struct key_vector *tn)
10578c2ecf20Sopenharmony_ci{
10588c2ecf20Sopenharmony_ci	while (!IS_TRIE(tn))
10598c2ecf20Sopenharmony_ci		tn = resize(t, tn);
10608c2ecf20Sopenharmony_ci}
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_cistatic int fib_insert_node(struct trie *t, struct key_vector *tp,
10638c2ecf20Sopenharmony_ci			   struct fib_alias *new, t_key key)
10648c2ecf20Sopenharmony_ci{
10658c2ecf20Sopenharmony_ci	struct key_vector *n, *l;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	l = leaf_new(key, new);
10688c2ecf20Sopenharmony_ci	if (!l)
10698c2ecf20Sopenharmony_ci		goto noleaf;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	/* retrieve child from parent node */
10728c2ecf20Sopenharmony_ci	n = get_child(tp, get_index(key, tp));
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	/* Case 2: n is a LEAF or a TNODE and the key doesn't match.
10758c2ecf20Sopenharmony_ci	 *
10768c2ecf20Sopenharmony_ci	 *  Add a new tnode here
10778c2ecf20Sopenharmony_ci	 *  first tnode need some special handling
10788c2ecf20Sopenharmony_ci	 *  leaves us in position for handling as case 3
10798c2ecf20Sopenharmony_ci	 */
10808c2ecf20Sopenharmony_ci	if (n) {
10818c2ecf20Sopenharmony_ci		struct key_vector *tn;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci		tn = tnode_new(key, __fls(key ^ n->key), 1);
10848c2ecf20Sopenharmony_ci		if (!tn)
10858c2ecf20Sopenharmony_ci			goto notnode;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci		/* initialize routes out of node */
10888c2ecf20Sopenharmony_ci		NODE_INIT_PARENT(tn, tp);
10898c2ecf20Sopenharmony_ci		put_child(tn, get_index(key, tn) ^ 1, n);
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci		/* start adding routes into the node */
10928c2ecf20Sopenharmony_ci		put_child_root(tp, key, tn);
10938c2ecf20Sopenharmony_ci		node_set_parent(n, tn);
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci		/* parent now has a NULL spot where the leaf can go */
10968c2ecf20Sopenharmony_ci		tp = tn;
10978c2ecf20Sopenharmony_ci	}
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	/* Case 3: n is NULL, and will just insert a new leaf */
11008c2ecf20Sopenharmony_ci	node_push_suffix(tp, new->fa_slen);
11018c2ecf20Sopenharmony_ci	NODE_INIT_PARENT(l, tp);
11028c2ecf20Sopenharmony_ci	put_child_root(tp, key, l);
11038c2ecf20Sopenharmony_ci	trie_rebalance(t, tp);
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	return 0;
11068c2ecf20Sopenharmony_cinotnode:
11078c2ecf20Sopenharmony_ci	node_free(l);
11088c2ecf20Sopenharmony_cinoleaf:
11098c2ecf20Sopenharmony_ci	return -ENOMEM;
11108c2ecf20Sopenharmony_ci}
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_cistatic int fib_insert_alias(struct trie *t, struct key_vector *tp,
11138c2ecf20Sopenharmony_ci			    struct key_vector *l, struct fib_alias *new,
11148c2ecf20Sopenharmony_ci			    struct fib_alias *fa, t_key key)
11158c2ecf20Sopenharmony_ci{
11168c2ecf20Sopenharmony_ci	if (!l)
11178c2ecf20Sopenharmony_ci		return fib_insert_node(t, tp, new, key);
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	if (fa) {
11208c2ecf20Sopenharmony_ci		hlist_add_before_rcu(&new->fa_list, &fa->fa_list);
11218c2ecf20Sopenharmony_ci	} else {
11228c2ecf20Sopenharmony_ci		struct fib_alias *last;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci		hlist_for_each_entry(last, &l->leaf, fa_list) {
11258c2ecf20Sopenharmony_ci			if (new->fa_slen < last->fa_slen)
11268c2ecf20Sopenharmony_ci				break;
11278c2ecf20Sopenharmony_ci			if ((new->fa_slen == last->fa_slen) &&
11288c2ecf20Sopenharmony_ci			    (new->tb_id > last->tb_id))
11298c2ecf20Sopenharmony_ci				break;
11308c2ecf20Sopenharmony_ci			fa = last;
11318c2ecf20Sopenharmony_ci		}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci		if (fa)
11348c2ecf20Sopenharmony_ci			hlist_add_behind_rcu(&new->fa_list, &fa->fa_list);
11358c2ecf20Sopenharmony_ci		else
11368c2ecf20Sopenharmony_ci			hlist_add_head_rcu(&new->fa_list, &l->leaf);
11378c2ecf20Sopenharmony_ci	}
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	/* if we added to the tail node then we need to update slen */
11408c2ecf20Sopenharmony_ci	if (l->slen < new->fa_slen) {
11418c2ecf20Sopenharmony_ci		l->slen = new->fa_slen;
11428c2ecf20Sopenharmony_ci		node_push_suffix(tp, new->fa_slen);
11438c2ecf20Sopenharmony_ci	}
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	return 0;
11468c2ecf20Sopenharmony_ci}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_cistatic bool fib_valid_key_len(u32 key, u8 plen, struct netlink_ext_ack *extack)
11498c2ecf20Sopenharmony_ci{
11508c2ecf20Sopenharmony_ci	if (plen > KEYLENGTH) {
11518c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid prefix length");
11528c2ecf20Sopenharmony_ci		return false;
11538c2ecf20Sopenharmony_ci	}
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	if ((plen < KEYLENGTH) && (key << plen)) {
11568c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack,
11578c2ecf20Sopenharmony_ci			       "Invalid prefix for given prefix length");
11588c2ecf20Sopenharmony_ci		return false;
11598c2ecf20Sopenharmony_ci	}
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	return true;
11628c2ecf20Sopenharmony_ci}
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_cistatic void fib_remove_alias(struct trie *t, struct key_vector *tp,
11658c2ecf20Sopenharmony_ci			     struct key_vector *l, struct fib_alias *old);
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci/* Caller must hold RTNL. */
11688c2ecf20Sopenharmony_ciint fib_table_insert(struct net *net, struct fib_table *tb,
11698c2ecf20Sopenharmony_ci		     struct fib_config *cfg, struct netlink_ext_ack *extack)
11708c2ecf20Sopenharmony_ci{
11718c2ecf20Sopenharmony_ci	struct trie *t = (struct trie *)tb->tb_data;
11728c2ecf20Sopenharmony_ci	struct fib_alias *fa, *new_fa;
11738c2ecf20Sopenharmony_ci	struct key_vector *l, *tp;
11748c2ecf20Sopenharmony_ci	u16 nlflags = NLM_F_EXCL;
11758c2ecf20Sopenharmony_ci	struct fib_info *fi;
11768c2ecf20Sopenharmony_ci	u8 plen = cfg->fc_dst_len;
11778c2ecf20Sopenharmony_ci	u8 slen = KEYLENGTH - plen;
11788c2ecf20Sopenharmony_ci	u8 tos = cfg->fc_tos;
11798c2ecf20Sopenharmony_ci	u32 key;
11808c2ecf20Sopenharmony_ci	int err;
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	key = ntohl(cfg->fc_dst);
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	if (!fib_valid_key_len(key, plen, extack))
11858c2ecf20Sopenharmony_ci		return -EINVAL;
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	pr_debug("Insert table=%u %08x/%d\n", tb->tb_id, key, plen);
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	fi = fib_create_info(cfg, extack);
11908c2ecf20Sopenharmony_ci	if (IS_ERR(fi)) {
11918c2ecf20Sopenharmony_ci		err = PTR_ERR(fi);
11928c2ecf20Sopenharmony_ci		goto err;
11938c2ecf20Sopenharmony_ci	}
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	l = fib_find_node(t, &tp, key);
11968c2ecf20Sopenharmony_ci	fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority,
11978c2ecf20Sopenharmony_ci				tb->tb_id, false) : NULL;
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	/* Now fa, if non-NULL, points to the first fib alias
12008c2ecf20Sopenharmony_ci	 * with the same keys [prefix,tos,priority], if such key already
12018c2ecf20Sopenharmony_ci	 * exists or to the node before which we will insert new one.
12028c2ecf20Sopenharmony_ci	 *
12038c2ecf20Sopenharmony_ci	 * If fa is NULL, we will need to allocate a new one and
12048c2ecf20Sopenharmony_ci	 * insert to the tail of the section matching the suffix length
12058c2ecf20Sopenharmony_ci	 * of the new alias.
12068c2ecf20Sopenharmony_ci	 */
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	if (fa && fa->fa_tos == tos &&
12098c2ecf20Sopenharmony_ci	    fa->fa_info->fib_priority == fi->fib_priority) {
12108c2ecf20Sopenharmony_ci		struct fib_alias *fa_first, *fa_match;
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci		err = -EEXIST;
12138c2ecf20Sopenharmony_ci		if (cfg->fc_nlflags & NLM_F_EXCL)
12148c2ecf20Sopenharmony_ci			goto out;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci		nlflags &= ~NLM_F_EXCL;
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci		/* We have 2 goals:
12198c2ecf20Sopenharmony_ci		 * 1. Find exact match for type, scope, fib_info to avoid
12208c2ecf20Sopenharmony_ci		 * duplicate routes
12218c2ecf20Sopenharmony_ci		 * 2. Find next 'fa' (or head), NLM_F_APPEND inserts before it
12228c2ecf20Sopenharmony_ci		 */
12238c2ecf20Sopenharmony_ci		fa_match = NULL;
12248c2ecf20Sopenharmony_ci		fa_first = fa;
12258c2ecf20Sopenharmony_ci		hlist_for_each_entry_from(fa, fa_list) {
12268c2ecf20Sopenharmony_ci			if ((fa->fa_slen != slen) ||
12278c2ecf20Sopenharmony_ci			    (fa->tb_id != tb->tb_id) ||
12288c2ecf20Sopenharmony_ci			    (fa->fa_tos != tos))
12298c2ecf20Sopenharmony_ci				break;
12308c2ecf20Sopenharmony_ci			if (fa->fa_info->fib_priority != fi->fib_priority)
12318c2ecf20Sopenharmony_ci				break;
12328c2ecf20Sopenharmony_ci			if (fa->fa_type == cfg->fc_type &&
12338c2ecf20Sopenharmony_ci			    fa->fa_info == fi) {
12348c2ecf20Sopenharmony_ci				fa_match = fa;
12358c2ecf20Sopenharmony_ci				break;
12368c2ecf20Sopenharmony_ci			}
12378c2ecf20Sopenharmony_ci		}
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci		if (cfg->fc_nlflags & NLM_F_REPLACE) {
12408c2ecf20Sopenharmony_ci			struct fib_info *fi_drop;
12418c2ecf20Sopenharmony_ci			u8 state;
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci			nlflags |= NLM_F_REPLACE;
12448c2ecf20Sopenharmony_ci			fa = fa_first;
12458c2ecf20Sopenharmony_ci			if (fa_match) {
12468c2ecf20Sopenharmony_ci				if (fa == fa_match)
12478c2ecf20Sopenharmony_ci					err = 0;
12488c2ecf20Sopenharmony_ci				goto out;
12498c2ecf20Sopenharmony_ci			}
12508c2ecf20Sopenharmony_ci			err = -ENOBUFS;
12518c2ecf20Sopenharmony_ci			new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
12528c2ecf20Sopenharmony_ci			if (!new_fa)
12538c2ecf20Sopenharmony_ci				goto out;
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci			fi_drop = fa->fa_info;
12568c2ecf20Sopenharmony_ci			new_fa->fa_tos = fa->fa_tos;
12578c2ecf20Sopenharmony_ci			new_fa->fa_info = fi;
12588c2ecf20Sopenharmony_ci			new_fa->fa_type = cfg->fc_type;
12598c2ecf20Sopenharmony_ci			state = fa->fa_state;
12608c2ecf20Sopenharmony_ci			new_fa->fa_state = state & ~FA_S_ACCESSED;
12618c2ecf20Sopenharmony_ci			new_fa->fa_slen = fa->fa_slen;
12628c2ecf20Sopenharmony_ci			new_fa->tb_id = tb->tb_id;
12638c2ecf20Sopenharmony_ci			new_fa->fa_default = -1;
12648c2ecf20Sopenharmony_ci			new_fa->offload = 0;
12658c2ecf20Sopenharmony_ci			new_fa->trap = 0;
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci			hlist_replace_rcu(&fa->fa_list, &new_fa->fa_list);
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci			if (fib_find_alias(&l->leaf, fa->fa_slen, 0, 0,
12708c2ecf20Sopenharmony_ci					   tb->tb_id, true) == new_fa) {
12718c2ecf20Sopenharmony_ci				enum fib_event_type fib_event;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci				fib_event = FIB_EVENT_ENTRY_REPLACE;
12748c2ecf20Sopenharmony_ci				err = call_fib_entry_notifiers(net, fib_event,
12758c2ecf20Sopenharmony_ci							       key, plen,
12768c2ecf20Sopenharmony_ci							       new_fa, extack);
12778c2ecf20Sopenharmony_ci				if (err) {
12788c2ecf20Sopenharmony_ci					hlist_replace_rcu(&new_fa->fa_list,
12798c2ecf20Sopenharmony_ci							  &fa->fa_list);
12808c2ecf20Sopenharmony_ci					goto out_free_new_fa;
12818c2ecf20Sopenharmony_ci				}
12828c2ecf20Sopenharmony_ci			}
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci			rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen,
12858c2ecf20Sopenharmony_ci				  tb->tb_id, &cfg->fc_nlinfo, nlflags);
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci			alias_free_mem_rcu(fa);
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci			fib_release_info(fi_drop);
12908c2ecf20Sopenharmony_ci			if (state & FA_S_ACCESSED)
12918c2ecf20Sopenharmony_ci				rt_cache_flush(cfg->fc_nlinfo.nl_net);
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci			goto succeeded;
12948c2ecf20Sopenharmony_ci		}
12958c2ecf20Sopenharmony_ci		/* Error if we find a perfect match which
12968c2ecf20Sopenharmony_ci		 * uses the same scope, type, and nexthop
12978c2ecf20Sopenharmony_ci		 * information.
12988c2ecf20Sopenharmony_ci		 */
12998c2ecf20Sopenharmony_ci		if (fa_match)
13008c2ecf20Sopenharmony_ci			goto out;
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci		if (cfg->fc_nlflags & NLM_F_APPEND)
13038c2ecf20Sopenharmony_ci			nlflags |= NLM_F_APPEND;
13048c2ecf20Sopenharmony_ci		else
13058c2ecf20Sopenharmony_ci			fa = fa_first;
13068c2ecf20Sopenharmony_ci	}
13078c2ecf20Sopenharmony_ci	err = -ENOENT;
13088c2ecf20Sopenharmony_ci	if (!(cfg->fc_nlflags & NLM_F_CREATE))
13098c2ecf20Sopenharmony_ci		goto out;
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	nlflags |= NLM_F_CREATE;
13128c2ecf20Sopenharmony_ci	err = -ENOBUFS;
13138c2ecf20Sopenharmony_ci	new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
13148c2ecf20Sopenharmony_ci	if (!new_fa)
13158c2ecf20Sopenharmony_ci		goto out;
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	new_fa->fa_info = fi;
13188c2ecf20Sopenharmony_ci	new_fa->fa_tos = tos;
13198c2ecf20Sopenharmony_ci	new_fa->fa_type = cfg->fc_type;
13208c2ecf20Sopenharmony_ci	new_fa->fa_state = 0;
13218c2ecf20Sopenharmony_ci	new_fa->fa_slen = slen;
13228c2ecf20Sopenharmony_ci	new_fa->tb_id = tb->tb_id;
13238c2ecf20Sopenharmony_ci	new_fa->fa_default = -1;
13248c2ecf20Sopenharmony_ci	new_fa->offload = 0;
13258c2ecf20Sopenharmony_ci	new_fa->trap = 0;
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	/* Insert new entry to the list. */
13288c2ecf20Sopenharmony_ci	err = fib_insert_alias(t, tp, l, new_fa, fa, key);
13298c2ecf20Sopenharmony_ci	if (err)
13308c2ecf20Sopenharmony_ci		goto out_free_new_fa;
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci	/* The alias was already inserted, so the node must exist. */
13338c2ecf20Sopenharmony_ci	l = l ? l : fib_find_node(t, &tp, key);
13348c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(!l)) {
13358c2ecf20Sopenharmony_ci		err = -ENOENT;
13368c2ecf20Sopenharmony_ci		goto out_free_new_fa;
13378c2ecf20Sopenharmony_ci	}
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	if (fib_find_alias(&l->leaf, new_fa->fa_slen, 0, 0, tb->tb_id, true) ==
13408c2ecf20Sopenharmony_ci	    new_fa) {
13418c2ecf20Sopenharmony_ci		enum fib_event_type fib_event;
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci		fib_event = FIB_EVENT_ENTRY_REPLACE;
13448c2ecf20Sopenharmony_ci		err = call_fib_entry_notifiers(net, fib_event, key, plen,
13458c2ecf20Sopenharmony_ci					       new_fa, extack);
13468c2ecf20Sopenharmony_ci		if (err)
13478c2ecf20Sopenharmony_ci			goto out_remove_new_fa;
13488c2ecf20Sopenharmony_ci	}
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	if (!plen)
13518c2ecf20Sopenharmony_ci		tb->tb_num_default++;
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	rt_cache_flush(cfg->fc_nlinfo.nl_net);
13548c2ecf20Sopenharmony_ci	rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, new_fa->tb_id,
13558c2ecf20Sopenharmony_ci		  &cfg->fc_nlinfo, nlflags);
13568c2ecf20Sopenharmony_cisucceeded:
13578c2ecf20Sopenharmony_ci	return 0;
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ciout_remove_new_fa:
13608c2ecf20Sopenharmony_ci	fib_remove_alias(t, tp, l, new_fa);
13618c2ecf20Sopenharmony_ciout_free_new_fa:
13628c2ecf20Sopenharmony_ci	kmem_cache_free(fn_alias_kmem, new_fa);
13638c2ecf20Sopenharmony_ciout:
13648c2ecf20Sopenharmony_ci	fib_release_info(fi);
13658c2ecf20Sopenharmony_cierr:
13668c2ecf20Sopenharmony_ci	return err;
13678c2ecf20Sopenharmony_ci}
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_cistatic inline t_key prefix_mismatch(t_key key, struct key_vector *n)
13708c2ecf20Sopenharmony_ci{
13718c2ecf20Sopenharmony_ci	t_key prefix = n->key;
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	return (key ^ prefix) & (prefix | -prefix);
13748c2ecf20Sopenharmony_ci}
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_cibool fib_lookup_good_nhc(const struct fib_nh_common *nhc, int fib_flags,
13778c2ecf20Sopenharmony_ci			 const struct flowi4 *flp)
13788c2ecf20Sopenharmony_ci{
13798c2ecf20Sopenharmony_ci	if (nhc->nhc_flags & RTNH_F_DEAD)
13808c2ecf20Sopenharmony_ci		return false;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	if (ip_ignore_linkdown(nhc->nhc_dev) &&
13838c2ecf20Sopenharmony_ci	    nhc->nhc_flags & RTNH_F_LINKDOWN &&
13848c2ecf20Sopenharmony_ci	    !(fib_flags & FIB_LOOKUP_IGNORE_LINKSTATE))
13858c2ecf20Sopenharmony_ci		return false;
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci	if (!(flp->flowi4_flags & FLOWI_FLAG_SKIP_NH_OIF)) {
13888c2ecf20Sopenharmony_ci		if (flp->flowi4_oif &&
13898c2ecf20Sopenharmony_ci		    flp->flowi4_oif != nhc->nhc_oif)
13908c2ecf20Sopenharmony_ci			return false;
13918c2ecf20Sopenharmony_ci	}
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	return true;
13948c2ecf20Sopenharmony_ci}
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci/* should be called with rcu_read_lock */
13978c2ecf20Sopenharmony_ciint fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp,
13988c2ecf20Sopenharmony_ci		     struct fib_result *res, int fib_flags)
13998c2ecf20Sopenharmony_ci{
14008c2ecf20Sopenharmony_ci	struct trie *t = (struct trie *) tb->tb_data;
14018c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS
14028c2ecf20Sopenharmony_ci	struct trie_use_stats __percpu *stats = t->stats;
14038c2ecf20Sopenharmony_ci#endif
14048c2ecf20Sopenharmony_ci	const t_key key = ntohl(flp->daddr);
14058c2ecf20Sopenharmony_ci	struct key_vector *n, *pn;
14068c2ecf20Sopenharmony_ci	struct fib_alias *fa;
14078c2ecf20Sopenharmony_ci	unsigned long index;
14088c2ecf20Sopenharmony_ci	t_key cindex;
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	pn = t->kv;
14118c2ecf20Sopenharmony_ci	cindex = 0;
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	n = get_child_rcu(pn, cindex);
14148c2ecf20Sopenharmony_ci	if (!n) {
14158c2ecf20Sopenharmony_ci		trace_fib_table_lookup(tb->tb_id, flp, NULL, -EAGAIN);
14168c2ecf20Sopenharmony_ci		return -EAGAIN;
14178c2ecf20Sopenharmony_ci	}
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS
14208c2ecf20Sopenharmony_ci	this_cpu_inc(stats->gets);
14218c2ecf20Sopenharmony_ci#endif
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	/* Step 1: Travel to the longest prefix match in the trie */
14248c2ecf20Sopenharmony_ci	for (;;) {
14258c2ecf20Sopenharmony_ci		index = get_cindex(key, n);
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci		/* This bit of code is a bit tricky but it combines multiple
14288c2ecf20Sopenharmony_ci		 * checks into a single check.  The prefix consists of the
14298c2ecf20Sopenharmony_ci		 * prefix plus zeros for the "bits" in the prefix. The index
14308c2ecf20Sopenharmony_ci		 * is the difference between the key and this value.  From
14318c2ecf20Sopenharmony_ci		 * this we can actually derive several pieces of data.
14328c2ecf20Sopenharmony_ci		 *   if (index >= (1ul << bits))
14338c2ecf20Sopenharmony_ci		 *     we have a mismatch in skip bits and failed
14348c2ecf20Sopenharmony_ci		 *   else
14358c2ecf20Sopenharmony_ci		 *     we know the value is cindex
14368c2ecf20Sopenharmony_ci		 *
14378c2ecf20Sopenharmony_ci		 * This check is safe even if bits == KEYLENGTH due to the
14388c2ecf20Sopenharmony_ci		 * fact that we can only allocate a node with 32 bits if a
14398c2ecf20Sopenharmony_ci		 * long is greater than 32 bits.
14408c2ecf20Sopenharmony_ci		 */
14418c2ecf20Sopenharmony_ci		if (index >= (1ul << n->bits))
14428c2ecf20Sopenharmony_ci			break;
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci		/* we have found a leaf. Prefixes have already been compared */
14458c2ecf20Sopenharmony_ci		if (IS_LEAF(n))
14468c2ecf20Sopenharmony_ci			goto found;
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci		/* only record pn and cindex if we are going to be chopping
14498c2ecf20Sopenharmony_ci		 * bits later.  Otherwise we are just wasting cycles.
14508c2ecf20Sopenharmony_ci		 */
14518c2ecf20Sopenharmony_ci		if (n->slen > n->pos) {
14528c2ecf20Sopenharmony_ci			pn = n;
14538c2ecf20Sopenharmony_ci			cindex = index;
14548c2ecf20Sopenharmony_ci		}
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci		n = get_child_rcu(n, index);
14578c2ecf20Sopenharmony_ci		if (unlikely(!n))
14588c2ecf20Sopenharmony_ci			goto backtrace;
14598c2ecf20Sopenharmony_ci	}
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	/* Step 2: Sort out leaves and begin backtracing for longest prefix */
14628c2ecf20Sopenharmony_ci	for (;;) {
14638c2ecf20Sopenharmony_ci		/* record the pointer where our next node pointer is stored */
14648c2ecf20Sopenharmony_ci		struct key_vector __rcu **cptr = n->tnode;
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci		/* This test verifies that none of the bits that differ
14678c2ecf20Sopenharmony_ci		 * between the key and the prefix exist in the region of
14688c2ecf20Sopenharmony_ci		 * the lsb and higher in the prefix.
14698c2ecf20Sopenharmony_ci		 */
14708c2ecf20Sopenharmony_ci		if (unlikely(prefix_mismatch(key, n)) || (n->slen == n->pos))
14718c2ecf20Sopenharmony_ci			goto backtrace;
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci		/* exit out and process leaf */
14748c2ecf20Sopenharmony_ci		if (unlikely(IS_LEAF(n)))
14758c2ecf20Sopenharmony_ci			break;
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci		/* Don't bother recording parent info.  Since we are in
14788c2ecf20Sopenharmony_ci		 * prefix match mode we will have to come back to wherever
14798c2ecf20Sopenharmony_ci		 * we started this traversal anyway
14808c2ecf20Sopenharmony_ci		 */
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci		while ((n = rcu_dereference(*cptr)) == NULL) {
14838c2ecf20Sopenharmony_cibacktrace:
14848c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS
14858c2ecf20Sopenharmony_ci			if (!n)
14868c2ecf20Sopenharmony_ci				this_cpu_inc(stats->null_node_hit);
14878c2ecf20Sopenharmony_ci#endif
14888c2ecf20Sopenharmony_ci			/* If we are at cindex 0 there are no more bits for
14898c2ecf20Sopenharmony_ci			 * us to strip at this level so we must ascend back
14908c2ecf20Sopenharmony_ci			 * up one level to see if there are any more bits to
14918c2ecf20Sopenharmony_ci			 * be stripped there.
14928c2ecf20Sopenharmony_ci			 */
14938c2ecf20Sopenharmony_ci			while (!cindex) {
14948c2ecf20Sopenharmony_ci				t_key pkey = pn->key;
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci				/* If we don't have a parent then there is
14978c2ecf20Sopenharmony_ci				 * nothing for us to do as we do not have any
14988c2ecf20Sopenharmony_ci				 * further nodes to parse.
14998c2ecf20Sopenharmony_ci				 */
15008c2ecf20Sopenharmony_ci				if (IS_TRIE(pn)) {
15018c2ecf20Sopenharmony_ci					trace_fib_table_lookup(tb->tb_id, flp,
15028c2ecf20Sopenharmony_ci							       NULL, -EAGAIN);
15038c2ecf20Sopenharmony_ci					return -EAGAIN;
15048c2ecf20Sopenharmony_ci				}
15058c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS
15068c2ecf20Sopenharmony_ci				this_cpu_inc(stats->backtrack);
15078c2ecf20Sopenharmony_ci#endif
15088c2ecf20Sopenharmony_ci				/* Get Child's index */
15098c2ecf20Sopenharmony_ci				pn = node_parent_rcu(pn);
15108c2ecf20Sopenharmony_ci				cindex = get_index(pkey, pn);
15118c2ecf20Sopenharmony_ci			}
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci			/* strip the least significant bit from the cindex */
15148c2ecf20Sopenharmony_ci			cindex &= cindex - 1;
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci			/* grab pointer for next child node */
15178c2ecf20Sopenharmony_ci			cptr = &pn->tnode[cindex];
15188c2ecf20Sopenharmony_ci		}
15198c2ecf20Sopenharmony_ci	}
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_cifound:
15228c2ecf20Sopenharmony_ci	/* this line carries forward the xor from earlier in the function */
15238c2ecf20Sopenharmony_ci	index = key ^ n->key;
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	/* Step 3: Process the leaf, if that fails fall back to backtracing */
15268c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(fa, &n->leaf, fa_list) {
15278c2ecf20Sopenharmony_ci		struct fib_info *fi = fa->fa_info;
15288c2ecf20Sopenharmony_ci		struct fib_nh_common *nhc;
15298c2ecf20Sopenharmony_ci		int nhsel, err;
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci		if ((BITS_PER_LONG > KEYLENGTH) || (fa->fa_slen < KEYLENGTH)) {
15328c2ecf20Sopenharmony_ci			if (index >= (1ul << fa->fa_slen))
15338c2ecf20Sopenharmony_ci				continue;
15348c2ecf20Sopenharmony_ci		}
15358c2ecf20Sopenharmony_ci		if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos)
15368c2ecf20Sopenharmony_ci			continue;
15378c2ecf20Sopenharmony_ci		/* Paired with WRITE_ONCE() in fib_release_info() */
15388c2ecf20Sopenharmony_ci		if (READ_ONCE(fi->fib_dead))
15398c2ecf20Sopenharmony_ci			continue;
15408c2ecf20Sopenharmony_ci		if (fa->fa_info->fib_scope < flp->flowi4_scope)
15418c2ecf20Sopenharmony_ci			continue;
15428c2ecf20Sopenharmony_ci		fib_alias_accessed(fa);
15438c2ecf20Sopenharmony_ci		err = fib_props[fa->fa_type].error;
15448c2ecf20Sopenharmony_ci		if (unlikely(err < 0)) {
15458c2ecf20Sopenharmony_ciout_reject:
15468c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS
15478c2ecf20Sopenharmony_ci			this_cpu_inc(stats->semantic_match_passed);
15488c2ecf20Sopenharmony_ci#endif
15498c2ecf20Sopenharmony_ci			trace_fib_table_lookup(tb->tb_id, flp, NULL, err);
15508c2ecf20Sopenharmony_ci			return err;
15518c2ecf20Sopenharmony_ci		}
15528c2ecf20Sopenharmony_ci		if (fi->fib_flags & RTNH_F_DEAD)
15538c2ecf20Sopenharmony_ci			continue;
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci		if (unlikely(fi->nh)) {
15568c2ecf20Sopenharmony_ci			if (nexthop_is_blackhole(fi->nh)) {
15578c2ecf20Sopenharmony_ci				err = fib_props[RTN_BLACKHOLE].error;
15588c2ecf20Sopenharmony_ci				goto out_reject;
15598c2ecf20Sopenharmony_ci			}
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci			nhc = nexthop_get_nhc_lookup(fi->nh, fib_flags, flp,
15628c2ecf20Sopenharmony_ci						     &nhsel);
15638c2ecf20Sopenharmony_ci			if (nhc)
15648c2ecf20Sopenharmony_ci				goto set_result;
15658c2ecf20Sopenharmony_ci			goto miss;
15668c2ecf20Sopenharmony_ci		}
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci		for (nhsel = 0; nhsel < fib_info_num_path(fi); nhsel++) {
15698c2ecf20Sopenharmony_ci			nhc = fib_info_nhc(fi, nhsel);
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci			if (!fib_lookup_good_nhc(nhc, fib_flags, flp))
15728c2ecf20Sopenharmony_ci				continue;
15738c2ecf20Sopenharmony_ciset_result:
15748c2ecf20Sopenharmony_ci			if (!(fib_flags & FIB_LOOKUP_NOREF))
15758c2ecf20Sopenharmony_ci				refcount_inc(&fi->fib_clntref);
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci			res->prefix = htonl(n->key);
15788c2ecf20Sopenharmony_ci			res->prefixlen = KEYLENGTH - fa->fa_slen;
15798c2ecf20Sopenharmony_ci			res->nh_sel = nhsel;
15808c2ecf20Sopenharmony_ci			res->nhc = nhc;
15818c2ecf20Sopenharmony_ci			res->type = fa->fa_type;
15828c2ecf20Sopenharmony_ci			res->scope = fi->fib_scope;
15838c2ecf20Sopenharmony_ci			res->fi = fi;
15848c2ecf20Sopenharmony_ci			res->table = tb;
15858c2ecf20Sopenharmony_ci			res->fa_head = &n->leaf;
15868c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS
15878c2ecf20Sopenharmony_ci			this_cpu_inc(stats->semantic_match_passed);
15888c2ecf20Sopenharmony_ci#endif
15898c2ecf20Sopenharmony_ci			trace_fib_table_lookup(tb->tb_id, flp, nhc, err);
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci			return err;
15928c2ecf20Sopenharmony_ci		}
15938c2ecf20Sopenharmony_ci	}
15948c2ecf20Sopenharmony_cimiss:
15958c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS
15968c2ecf20Sopenharmony_ci	this_cpu_inc(stats->semantic_match_miss);
15978c2ecf20Sopenharmony_ci#endif
15988c2ecf20Sopenharmony_ci	goto backtrace;
15998c2ecf20Sopenharmony_ci}
16008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib_table_lookup);
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_cistatic void fib_remove_alias(struct trie *t, struct key_vector *tp,
16038c2ecf20Sopenharmony_ci			     struct key_vector *l, struct fib_alias *old)
16048c2ecf20Sopenharmony_ci{
16058c2ecf20Sopenharmony_ci	/* record the location of the previous list_info entry */
16068c2ecf20Sopenharmony_ci	struct hlist_node **pprev = old->fa_list.pprev;
16078c2ecf20Sopenharmony_ci	struct fib_alias *fa = hlist_entry(pprev, typeof(*fa), fa_list.next);
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci	/* remove the fib_alias from the list */
16108c2ecf20Sopenharmony_ci	hlist_del_rcu(&old->fa_list);
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci	/* if we emptied the list this leaf will be freed and we can sort
16138c2ecf20Sopenharmony_ci	 * out parent suffix lengths as a part of trie_rebalance
16148c2ecf20Sopenharmony_ci	 */
16158c2ecf20Sopenharmony_ci	if (hlist_empty(&l->leaf)) {
16168c2ecf20Sopenharmony_ci		if (tp->slen == l->slen)
16178c2ecf20Sopenharmony_ci			node_pull_suffix(tp, tp->pos);
16188c2ecf20Sopenharmony_ci		put_child_root(tp, l->key, NULL);
16198c2ecf20Sopenharmony_ci		node_free(l);
16208c2ecf20Sopenharmony_ci		trie_rebalance(t, tp);
16218c2ecf20Sopenharmony_ci		return;
16228c2ecf20Sopenharmony_ci	}
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci	/* only access fa if it is pointing at the last valid hlist_node */
16258c2ecf20Sopenharmony_ci	if (*pprev)
16268c2ecf20Sopenharmony_ci		return;
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	/* update the trie with the latest suffix length */
16298c2ecf20Sopenharmony_ci	l->slen = fa->fa_slen;
16308c2ecf20Sopenharmony_ci	node_pull_suffix(tp, fa->fa_slen);
16318c2ecf20Sopenharmony_ci}
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_cistatic void fib_notify_alias_delete(struct net *net, u32 key,
16348c2ecf20Sopenharmony_ci				    struct hlist_head *fah,
16358c2ecf20Sopenharmony_ci				    struct fib_alias *fa_to_delete,
16368c2ecf20Sopenharmony_ci				    struct netlink_ext_ack *extack)
16378c2ecf20Sopenharmony_ci{
16388c2ecf20Sopenharmony_ci	struct fib_alias *fa_next, *fa_to_notify;
16398c2ecf20Sopenharmony_ci	u32 tb_id = fa_to_delete->tb_id;
16408c2ecf20Sopenharmony_ci	u8 slen = fa_to_delete->fa_slen;
16418c2ecf20Sopenharmony_ci	enum fib_event_type fib_event;
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	/* Do not notify if we do not care about the route. */
16448c2ecf20Sopenharmony_ci	if (fib_find_alias(fah, slen, 0, 0, tb_id, true) != fa_to_delete)
16458c2ecf20Sopenharmony_ci		return;
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	/* Determine if the route should be replaced by the next route in the
16488c2ecf20Sopenharmony_ci	 * list.
16498c2ecf20Sopenharmony_ci	 */
16508c2ecf20Sopenharmony_ci	fa_next = hlist_entry_safe(fa_to_delete->fa_list.next,
16518c2ecf20Sopenharmony_ci				   struct fib_alias, fa_list);
16528c2ecf20Sopenharmony_ci	if (fa_next && fa_next->fa_slen == slen && fa_next->tb_id == tb_id) {
16538c2ecf20Sopenharmony_ci		fib_event = FIB_EVENT_ENTRY_REPLACE;
16548c2ecf20Sopenharmony_ci		fa_to_notify = fa_next;
16558c2ecf20Sopenharmony_ci	} else {
16568c2ecf20Sopenharmony_ci		fib_event = FIB_EVENT_ENTRY_DEL;
16578c2ecf20Sopenharmony_ci		fa_to_notify = fa_to_delete;
16588c2ecf20Sopenharmony_ci	}
16598c2ecf20Sopenharmony_ci	call_fib_entry_notifiers(net, fib_event, key, KEYLENGTH - slen,
16608c2ecf20Sopenharmony_ci				 fa_to_notify, extack);
16618c2ecf20Sopenharmony_ci}
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci/* Caller must hold RTNL. */
16648c2ecf20Sopenharmony_ciint fib_table_delete(struct net *net, struct fib_table *tb,
16658c2ecf20Sopenharmony_ci		     struct fib_config *cfg, struct netlink_ext_ack *extack)
16668c2ecf20Sopenharmony_ci{
16678c2ecf20Sopenharmony_ci	struct trie *t = (struct trie *) tb->tb_data;
16688c2ecf20Sopenharmony_ci	struct fib_alias *fa, *fa_to_delete;
16698c2ecf20Sopenharmony_ci	struct key_vector *l, *tp;
16708c2ecf20Sopenharmony_ci	u8 plen = cfg->fc_dst_len;
16718c2ecf20Sopenharmony_ci	u8 slen = KEYLENGTH - plen;
16728c2ecf20Sopenharmony_ci	u8 tos = cfg->fc_tos;
16738c2ecf20Sopenharmony_ci	u32 key;
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	key = ntohl(cfg->fc_dst);
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci	if (!fib_valid_key_len(key, plen, extack))
16788c2ecf20Sopenharmony_ci		return -EINVAL;
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci	l = fib_find_node(t, &tp, key);
16818c2ecf20Sopenharmony_ci	if (!l)
16828c2ecf20Sopenharmony_ci		return -ESRCH;
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci	fa = fib_find_alias(&l->leaf, slen, tos, 0, tb->tb_id, false);
16858c2ecf20Sopenharmony_ci	if (!fa)
16868c2ecf20Sopenharmony_ci		return -ESRCH;
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci	pr_debug("Deleting %08x/%d tos=%d t=%p\n", key, plen, tos, t);
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	fa_to_delete = NULL;
16918c2ecf20Sopenharmony_ci	hlist_for_each_entry_from(fa, fa_list) {
16928c2ecf20Sopenharmony_ci		struct fib_info *fi = fa->fa_info;
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_ci		if ((fa->fa_slen != slen) ||
16958c2ecf20Sopenharmony_ci		    (fa->tb_id != tb->tb_id) ||
16968c2ecf20Sopenharmony_ci		    (fa->fa_tos != tos))
16978c2ecf20Sopenharmony_ci			break;
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci		if ((!cfg->fc_type || fa->fa_type == cfg->fc_type) &&
17008c2ecf20Sopenharmony_ci		    (cfg->fc_scope == RT_SCOPE_NOWHERE ||
17018c2ecf20Sopenharmony_ci		     fa->fa_info->fib_scope == cfg->fc_scope) &&
17028c2ecf20Sopenharmony_ci		    (!cfg->fc_prefsrc ||
17038c2ecf20Sopenharmony_ci		     fi->fib_prefsrc == cfg->fc_prefsrc) &&
17048c2ecf20Sopenharmony_ci		    (!cfg->fc_protocol ||
17058c2ecf20Sopenharmony_ci		     fi->fib_protocol == cfg->fc_protocol) &&
17068c2ecf20Sopenharmony_ci		    fib_nh_match(net, cfg, fi, extack) == 0 &&
17078c2ecf20Sopenharmony_ci		    fib_metrics_match(cfg, fi)) {
17088c2ecf20Sopenharmony_ci			fa_to_delete = fa;
17098c2ecf20Sopenharmony_ci			break;
17108c2ecf20Sopenharmony_ci		}
17118c2ecf20Sopenharmony_ci	}
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_ci	if (!fa_to_delete)
17148c2ecf20Sopenharmony_ci		return -ESRCH;
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci	fib_notify_alias_delete(net, key, &l->leaf, fa_to_delete, extack);
17178c2ecf20Sopenharmony_ci	rtmsg_fib(RTM_DELROUTE, htonl(key), fa_to_delete, plen, tb->tb_id,
17188c2ecf20Sopenharmony_ci		  &cfg->fc_nlinfo, 0);
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci	if (!plen)
17218c2ecf20Sopenharmony_ci		tb->tb_num_default--;
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci	fib_remove_alias(t, tp, l, fa_to_delete);
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	if (fa_to_delete->fa_state & FA_S_ACCESSED)
17268c2ecf20Sopenharmony_ci		rt_cache_flush(cfg->fc_nlinfo.nl_net);
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	fib_release_info(fa_to_delete->fa_info);
17298c2ecf20Sopenharmony_ci	alias_free_mem_rcu(fa_to_delete);
17308c2ecf20Sopenharmony_ci	return 0;
17318c2ecf20Sopenharmony_ci}
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci/* Scan for the next leaf starting at the provided key value */
17348c2ecf20Sopenharmony_cistatic struct key_vector *leaf_walk_rcu(struct key_vector **tn, t_key key)
17358c2ecf20Sopenharmony_ci{
17368c2ecf20Sopenharmony_ci	struct key_vector *pn, *n = *tn;
17378c2ecf20Sopenharmony_ci	unsigned long cindex;
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci	/* this loop is meant to try and find the key in the trie */
17408c2ecf20Sopenharmony_ci	do {
17418c2ecf20Sopenharmony_ci		/* record parent and next child index */
17428c2ecf20Sopenharmony_ci		pn = n;
17438c2ecf20Sopenharmony_ci		cindex = (key > pn->key) ? get_index(key, pn) : 0;
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci		if (cindex >> pn->bits)
17468c2ecf20Sopenharmony_ci			break;
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_ci		/* descend into the next child */
17498c2ecf20Sopenharmony_ci		n = get_child_rcu(pn, cindex++);
17508c2ecf20Sopenharmony_ci		if (!n)
17518c2ecf20Sopenharmony_ci			break;
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci		/* guarantee forward progress on the keys */
17548c2ecf20Sopenharmony_ci		if (IS_LEAF(n) && (n->key >= key))
17558c2ecf20Sopenharmony_ci			goto found;
17568c2ecf20Sopenharmony_ci	} while (IS_TNODE(n));
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci	/* this loop will search for the next leaf with a greater key */
17598c2ecf20Sopenharmony_ci	while (!IS_TRIE(pn)) {
17608c2ecf20Sopenharmony_ci		/* if we exhausted the parent node we will need to climb */
17618c2ecf20Sopenharmony_ci		if (cindex >= (1ul << pn->bits)) {
17628c2ecf20Sopenharmony_ci			t_key pkey = pn->key;
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_ci			pn = node_parent_rcu(pn);
17658c2ecf20Sopenharmony_ci			cindex = get_index(pkey, pn) + 1;
17668c2ecf20Sopenharmony_ci			continue;
17678c2ecf20Sopenharmony_ci		}
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_ci		/* grab the next available node */
17708c2ecf20Sopenharmony_ci		n = get_child_rcu(pn, cindex++);
17718c2ecf20Sopenharmony_ci		if (!n)
17728c2ecf20Sopenharmony_ci			continue;
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci		/* no need to compare keys since we bumped the index */
17758c2ecf20Sopenharmony_ci		if (IS_LEAF(n))
17768c2ecf20Sopenharmony_ci			goto found;
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci		/* Rescan start scanning in new node */
17798c2ecf20Sopenharmony_ci		pn = n;
17808c2ecf20Sopenharmony_ci		cindex = 0;
17818c2ecf20Sopenharmony_ci	}
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	*tn = pn;
17848c2ecf20Sopenharmony_ci	return NULL; /* Root of trie */
17858c2ecf20Sopenharmony_cifound:
17868c2ecf20Sopenharmony_ci	/* if we are at the limit for keys just return NULL for the tnode */
17878c2ecf20Sopenharmony_ci	*tn = pn;
17888c2ecf20Sopenharmony_ci	return n;
17898c2ecf20Sopenharmony_ci}
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_cistatic void fib_trie_free(struct fib_table *tb)
17928c2ecf20Sopenharmony_ci{
17938c2ecf20Sopenharmony_ci	struct trie *t = (struct trie *)tb->tb_data;
17948c2ecf20Sopenharmony_ci	struct key_vector *pn = t->kv;
17958c2ecf20Sopenharmony_ci	unsigned long cindex = 1;
17968c2ecf20Sopenharmony_ci	struct hlist_node *tmp;
17978c2ecf20Sopenharmony_ci	struct fib_alias *fa;
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci	/* walk trie in reverse order and free everything */
18008c2ecf20Sopenharmony_ci	for (;;) {
18018c2ecf20Sopenharmony_ci		struct key_vector *n;
18028c2ecf20Sopenharmony_ci
18038c2ecf20Sopenharmony_ci		if (!(cindex--)) {
18048c2ecf20Sopenharmony_ci			t_key pkey = pn->key;
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci			if (IS_TRIE(pn))
18078c2ecf20Sopenharmony_ci				break;
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci			n = pn;
18108c2ecf20Sopenharmony_ci			pn = node_parent(pn);
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci			/* drop emptied tnode */
18138c2ecf20Sopenharmony_ci			put_child_root(pn, n->key, NULL);
18148c2ecf20Sopenharmony_ci			node_free(n);
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci			cindex = get_index(pkey, pn);
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci			continue;
18198c2ecf20Sopenharmony_ci		}
18208c2ecf20Sopenharmony_ci
18218c2ecf20Sopenharmony_ci		/* grab the next available node */
18228c2ecf20Sopenharmony_ci		n = get_child(pn, cindex);
18238c2ecf20Sopenharmony_ci		if (!n)
18248c2ecf20Sopenharmony_ci			continue;
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci		if (IS_TNODE(n)) {
18278c2ecf20Sopenharmony_ci			/* record pn and cindex for leaf walking */
18288c2ecf20Sopenharmony_ci			pn = n;
18298c2ecf20Sopenharmony_ci			cindex = 1ul << n->bits;
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci			continue;
18328c2ecf20Sopenharmony_ci		}
18338c2ecf20Sopenharmony_ci
18348c2ecf20Sopenharmony_ci		hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) {
18358c2ecf20Sopenharmony_ci			hlist_del_rcu(&fa->fa_list);
18368c2ecf20Sopenharmony_ci			alias_free_mem_rcu(fa);
18378c2ecf20Sopenharmony_ci		}
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci		put_child_root(pn, n->key, NULL);
18408c2ecf20Sopenharmony_ci		node_free(n);
18418c2ecf20Sopenharmony_ci	}
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS
18448c2ecf20Sopenharmony_ci	free_percpu(t->stats);
18458c2ecf20Sopenharmony_ci#endif
18468c2ecf20Sopenharmony_ci	kfree(tb);
18478c2ecf20Sopenharmony_ci}
18488c2ecf20Sopenharmony_ci
18498c2ecf20Sopenharmony_cistruct fib_table *fib_trie_unmerge(struct fib_table *oldtb)
18508c2ecf20Sopenharmony_ci{
18518c2ecf20Sopenharmony_ci	struct trie *ot = (struct trie *)oldtb->tb_data;
18528c2ecf20Sopenharmony_ci	struct key_vector *l, *tp = ot->kv;
18538c2ecf20Sopenharmony_ci	struct fib_table *local_tb;
18548c2ecf20Sopenharmony_ci	struct fib_alias *fa;
18558c2ecf20Sopenharmony_ci	struct trie *lt;
18568c2ecf20Sopenharmony_ci	t_key key = 0;
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_ci	if (oldtb->tb_data == oldtb->__data)
18598c2ecf20Sopenharmony_ci		return oldtb;
18608c2ecf20Sopenharmony_ci
18618c2ecf20Sopenharmony_ci	local_tb = fib_trie_table(RT_TABLE_LOCAL, NULL);
18628c2ecf20Sopenharmony_ci	if (!local_tb)
18638c2ecf20Sopenharmony_ci		return NULL;
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	lt = (struct trie *)local_tb->tb_data;
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci	while ((l = leaf_walk_rcu(&tp, key)) != NULL) {
18688c2ecf20Sopenharmony_ci		struct key_vector *local_l = NULL, *local_tp;
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_ci		hlist_for_each_entry(fa, &l->leaf, fa_list) {
18718c2ecf20Sopenharmony_ci			struct fib_alias *new_fa;
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci			if (local_tb->tb_id != fa->tb_id)
18748c2ecf20Sopenharmony_ci				continue;
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_ci			/* clone fa for new local table */
18778c2ecf20Sopenharmony_ci			new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
18788c2ecf20Sopenharmony_ci			if (!new_fa)
18798c2ecf20Sopenharmony_ci				goto out;
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci			memcpy(new_fa, fa, sizeof(*fa));
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci			/* insert clone into table */
18848c2ecf20Sopenharmony_ci			if (!local_l)
18858c2ecf20Sopenharmony_ci				local_l = fib_find_node(lt, &local_tp, l->key);
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_ci			if (fib_insert_alias(lt, local_tp, local_l, new_fa,
18888c2ecf20Sopenharmony_ci					     NULL, l->key)) {
18898c2ecf20Sopenharmony_ci				kmem_cache_free(fn_alias_kmem, new_fa);
18908c2ecf20Sopenharmony_ci				goto out;
18918c2ecf20Sopenharmony_ci			}
18928c2ecf20Sopenharmony_ci		}
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci		/* stop loop if key wrapped back to 0 */
18958c2ecf20Sopenharmony_ci		key = l->key + 1;
18968c2ecf20Sopenharmony_ci		if (key < l->key)
18978c2ecf20Sopenharmony_ci			break;
18988c2ecf20Sopenharmony_ci	}
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_ci	return local_tb;
19018c2ecf20Sopenharmony_ciout:
19028c2ecf20Sopenharmony_ci	fib_trie_free(local_tb);
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci	return NULL;
19058c2ecf20Sopenharmony_ci}
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci/* Caller must hold RTNL */
19088c2ecf20Sopenharmony_civoid fib_table_flush_external(struct fib_table *tb)
19098c2ecf20Sopenharmony_ci{
19108c2ecf20Sopenharmony_ci	struct trie *t = (struct trie *)tb->tb_data;
19118c2ecf20Sopenharmony_ci	struct key_vector *pn = t->kv;
19128c2ecf20Sopenharmony_ci	unsigned long cindex = 1;
19138c2ecf20Sopenharmony_ci	struct hlist_node *tmp;
19148c2ecf20Sopenharmony_ci	struct fib_alias *fa;
19158c2ecf20Sopenharmony_ci
19168c2ecf20Sopenharmony_ci	/* walk trie in reverse order */
19178c2ecf20Sopenharmony_ci	for (;;) {
19188c2ecf20Sopenharmony_ci		unsigned char slen = 0;
19198c2ecf20Sopenharmony_ci		struct key_vector *n;
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci		if (!(cindex--)) {
19228c2ecf20Sopenharmony_ci			t_key pkey = pn->key;
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci			/* cannot resize the trie vector */
19258c2ecf20Sopenharmony_ci			if (IS_TRIE(pn))
19268c2ecf20Sopenharmony_ci				break;
19278c2ecf20Sopenharmony_ci
19288c2ecf20Sopenharmony_ci			/* update the suffix to address pulled leaves */
19298c2ecf20Sopenharmony_ci			if (pn->slen > pn->pos)
19308c2ecf20Sopenharmony_ci				update_suffix(pn);
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_ci			/* resize completed node */
19338c2ecf20Sopenharmony_ci			pn = resize(t, pn);
19348c2ecf20Sopenharmony_ci			cindex = get_index(pkey, pn);
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci			continue;
19378c2ecf20Sopenharmony_ci		}
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_ci		/* grab the next available node */
19408c2ecf20Sopenharmony_ci		n = get_child(pn, cindex);
19418c2ecf20Sopenharmony_ci		if (!n)
19428c2ecf20Sopenharmony_ci			continue;
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_ci		if (IS_TNODE(n)) {
19458c2ecf20Sopenharmony_ci			/* record pn and cindex for leaf walking */
19468c2ecf20Sopenharmony_ci			pn = n;
19478c2ecf20Sopenharmony_ci			cindex = 1ul << n->bits;
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci			continue;
19508c2ecf20Sopenharmony_ci		}
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_ci		hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) {
19538c2ecf20Sopenharmony_ci			/* if alias was cloned to local then we just
19548c2ecf20Sopenharmony_ci			 * need to remove the local copy from main
19558c2ecf20Sopenharmony_ci			 */
19568c2ecf20Sopenharmony_ci			if (tb->tb_id != fa->tb_id) {
19578c2ecf20Sopenharmony_ci				hlist_del_rcu(&fa->fa_list);
19588c2ecf20Sopenharmony_ci				alias_free_mem_rcu(fa);
19598c2ecf20Sopenharmony_ci				continue;
19608c2ecf20Sopenharmony_ci			}
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_ci			/* record local slen */
19638c2ecf20Sopenharmony_ci			slen = fa->fa_slen;
19648c2ecf20Sopenharmony_ci		}
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_ci		/* update leaf slen */
19678c2ecf20Sopenharmony_ci		n->slen = slen;
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_ci		if (hlist_empty(&n->leaf)) {
19708c2ecf20Sopenharmony_ci			put_child_root(pn, n->key, NULL);
19718c2ecf20Sopenharmony_ci			node_free(n);
19728c2ecf20Sopenharmony_ci		}
19738c2ecf20Sopenharmony_ci	}
19748c2ecf20Sopenharmony_ci}
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_ci/* Caller must hold RTNL. */
19778c2ecf20Sopenharmony_ciint fib_table_flush(struct net *net, struct fib_table *tb, bool flush_all)
19788c2ecf20Sopenharmony_ci{
19798c2ecf20Sopenharmony_ci	struct trie *t = (struct trie *)tb->tb_data;
19808c2ecf20Sopenharmony_ci	struct nl_info info = { .nl_net = net };
19818c2ecf20Sopenharmony_ci	struct key_vector *pn = t->kv;
19828c2ecf20Sopenharmony_ci	unsigned long cindex = 1;
19838c2ecf20Sopenharmony_ci	struct hlist_node *tmp;
19848c2ecf20Sopenharmony_ci	struct fib_alias *fa;
19858c2ecf20Sopenharmony_ci	int found = 0;
19868c2ecf20Sopenharmony_ci
19878c2ecf20Sopenharmony_ci	/* walk trie in reverse order */
19888c2ecf20Sopenharmony_ci	for (;;) {
19898c2ecf20Sopenharmony_ci		unsigned char slen = 0;
19908c2ecf20Sopenharmony_ci		struct key_vector *n;
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci		if (!(cindex--)) {
19938c2ecf20Sopenharmony_ci			t_key pkey = pn->key;
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci			/* cannot resize the trie vector */
19968c2ecf20Sopenharmony_ci			if (IS_TRIE(pn))
19978c2ecf20Sopenharmony_ci				break;
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci			/* update the suffix to address pulled leaves */
20008c2ecf20Sopenharmony_ci			if (pn->slen > pn->pos)
20018c2ecf20Sopenharmony_ci				update_suffix(pn);
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci			/* resize completed node */
20048c2ecf20Sopenharmony_ci			pn = resize(t, pn);
20058c2ecf20Sopenharmony_ci			cindex = get_index(pkey, pn);
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci			continue;
20088c2ecf20Sopenharmony_ci		}
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_ci		/* grab the next available node */
20118c2ecf20Sopenharmony_ci		n = get_child(pn, cindex);
20128c2ecf20Sopenharmony_ci		if (!n)
20138c2ecf20Sopenharmony_ci			continue;
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci		if (IS_TNODE(n)) {
20168c2ecf20Sopenharmony_ci			/* record pn and cindex for leaf walking */
20178c2ecf20Sopenharmony_ci			pn = n;
20188c2ecf20Sopenharmony_ci			cindex = 1ul << n->bits;
20198c2ecf20Sopenharmony_ci
20208c2ecf20Sopenharmony_ci			continue;
20218c2ecf20Sopenharmony_ci		}
20228c2ecf20Sopenharmony_ci
20238c2ecf20Sopenharmony_ci		hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) {
20248c2ecf20Sopenharmony_ci			struct fib_info *fi = fa->fa_info;
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_ci			if (!fi || tb->tb_id != fa->tb_id ||
20278c2ecf20Sopenharmony_ci			    (!(fi->fib_flags & RTNH_F_DEAD) &&
20288c2ecf20Sopenharmony_ci			     !fib_props[fa->fa_type].error)) {
20298c2ecf20Sopenharmony_ci				slen = fa->fa_slen;
20308c2ecf20Sopenharmony_ci				continue;
20318c2ecf20Sopenharmony_ci			}
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_ci			/* Do not flush error routes if network namespace is
20348c2ecf20Sopenharmony_ci			 * not being dismantled
20358c2ecf20Sopenharmony_ci			 */
20368c2ecf20Sopenharmony_ci			if (!flush_all && fib_props[fa->fa_type].error) {
20378c2ecf20Sopenharmony_ci				slen = fa->fa_slen;
20388c2ecf20Sopenharmony_ci				continue;
20398c2ecf20Sopenharmony_ci			}
20408c2ecf20Sopenharmony_ci
20418c2ecf20Sopenharmony_ci			fib_notify_alias_delete(net, n->key, &n->leaf, fa,
20428c2ecf20Sopenharmony_ci						NULL);
20438c2ecf20Sopenharmony_ci			if (fi->pfsrc_removed)
20448c2ecf20Sopenharmony_ci				rtmsg_fib(RTM_DELROUTE, htonl(n->key), fa,
20458c2ecf20Sopenharmony_ci					  KEYLENGTH - fa->fa_slen, tb->tb_id, &info, 0);
20468c2ecf20Sopenharmony_ci			hlist_del_rcu(&fa->fa_list);
20478c2ecf20Sopenharmony_ci			fib_release_info(fa->fa_info);
20488c2ecf20Sopenharmony_ci			alias_free_mem_rcu(fa);
20498c2ecf20Sopenharmony_ci			found++;
20508c2ecf20Sopenharmony_ci		}
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_ci		/* update leaf slen */
20538c2ecf20Sopenharmony_ci		n->slen = slen;
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_ci		if (hlist_empty(&n->leaf)) {
20568c2ecf20Sopenharmony_ci			put_child_root(pn, n->key, NULL);
20578c2ecf20Sopenharmony_ci			node_free(n);
20588c2ecf20Sopenharmony_ci		}
20598c2ecf20Sopenharmony_ci	}
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci	pr_debug("trie_flush found=%d\n", found);
20628c2ecf20Sopenharmony_ci	return found;
20638c2ecf20Sopenharmony_ci}
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_ci/* derived from fib_trie_free */
20668c2ecf20Sopenharmony_cistatic void __fib_info_notify_update(struct net *net, struct fib_table *tb,
20678c2ecf20Sopenharmony_ci				     struct nl_info *info)
20688c2ecf20Sopenharmony_ci{
20698c2ecf20Sopenharmony_ci	struct trie *t = (struct trie *)tb->tb_data;
20708c2ecf20Sopenharmony_ci	struct key_vector *pn = t->kv;
20718c2ecf20Sopenharmony_ci	unsigned long cindex = 1;
20728c2ecf20Sopenharmony_ci	struct fib_alias *fa;
20738c2ecf20Sopenharmony_ci
20748c2ecf20Sopenharmony_ci	for (;;) {
20758c2ecf20Sopenharmony_ci		struct key_vector *n;
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci		if (!(cindex--)) {
20788c2ecf20Sopenharmony_ci			t_key pkey = pn->key;
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_ci			if (IS_TRIE(pn))
20818c2ecf20Sopenharmony_ci				break;
20828c2ecf20Sopenharmony_ci
20838c2ecf20Sopenharmony_ci			pn = node_parent(pn);
20848c2ecf20Sopenharmony_ci			cindex = get_index(pkey, pn);
20858c2ecf20Sopenharmony_ci			continue;
20868c2ecf20Sopenharmony_ci		}
20878c2ecf20Sopenharmony_ci
20888c2ecf20Sopenharmony_ci		/* grab the next available node */
20898c2ecf20Sopenharmony_ci		n = get_child(pn, cindex);
20908c2ecf20Sopenharmony_ci		if (!n)
20918c2ecf20Sopenharmony_ci			continue;
20928c2ecf20Sopenharmony_ci
20938c2ecf20Sopenharmony_ci		if (IS_TNODE(n)) {
20948c2ecf20Sopenharmony_ci			/* record pn and cindex for leaf walking */
20958c2ecf20Sopenharmony_ci			pn = n;
20968c2ecf20Sopenharmony_ci			cindex = 1ul << n->bits;
20978c2ecf20Sopenharmony_ci
20988c2ecf20Sopenharmony_ci			continue;
20998c2ecf20Sopenharmony_ci		}
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_ci		hlist_for_each_entry(fa, &n->leaf, fa_list) {
21028c2ecf20Sopenharmony_ci			struct fib_info *fi = fa->fa_info;
21038c2ecf20Sopenharmony_ci
21048c2ecf20Sopenharmony_ci			if (!fi || !fi->nh_updated || fa->tb_id != tb->tb_id)
21058c2ecf20Sopenharmony_ci				continue;
21068c2ecf20Sopenharmony_ci
21078c2ecf20Sopenharmony_ci			rtmsg_fib(RTM_NEWROUTE, htonl(n->key), fa,
21088c2ecf20Sopenharmony_ci				  KEYLENGTH - fa->fa_slen, tb->tb_id,
21098c2ecf20Sopenharmony_ci				  info, NLM_F_REPLACE);
21108c2ecf20Sopenharmony_ci
21118c2ecf20Sopenharmony_ci			/* call_fib_entry_notifiers will be removed when
21128c2ecf20Sopenharmony_ci			 * in-kernel notifier is implemented and supported
21138c2ecf20Sopenharmony_ci			 * for nexthop objects
21148c2ecf20Sopenharmony_ci			 */
21158c2ecf20Sopenharmony_ci			call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE,
21168c2ecf20Sopenharmony_ci						 n->key,
21178c2ecf20Sopenharmony_ci						 KEYLENGTH - fa->fa_slen, fa,
21188c2ecf20Sopenharmony_ci						 NULL);
21198c2ecf20Sopenharmony_ci		}
21208c2ecf20Sopenharmony_ci	}
21218c2ecf20Sopenharmony_ci}
21228c2ecf20Sopenharmony_ci
21238c2ecf20Sopenharmony_civoid fib_info_notify_update(struct net *net, struct nl_info *info)
21248c2ecf20Sopenharmony_ci{
21258c2ecf20Sopenharmony_ci	unsigned int h;
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
21288c2ecf20Sopenharmony_ci		struct hlist_head *head = &net->ipv4.fib_table_hash[h];
21298c2ecf20Sopenharmony_ci		struct fib_table *tb;
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_ci		hlist_for_each_entry_rcu(tb, head, tb_hlist,
21328c2ecf20Sopenharmony_ci					 lockdep_rtnl_is_held())
21338c2ecf20Sopenharmony_ci			__fib_info_notify_update(net, tb, info);
21348c2ecf20Sopenharmony_ci	}
21358c2ecf20Sopenharmony_ci}
21368c2ecf20Sopenharmony_ci
21378c2ecf20Sopenharmony_cistatic int fib_leaf_notify(struct key_vector *l, struct fib_table *tb,
21388c2ecf20Sopenharmony_ci			   struct notifier_block *nb,
21398c2ecf20Sopenharmony_ci			   struct netlink_ext_ack *extack)
21408c2ecf20Sopenharmony_ci{
21418c2ecf20Sopenharmony_ci	struct fib_alias *fa;
21428c2ecf20Sopenharmony_ci	int last_slen = -1;
21438c2ecf20Sopenharmony_ci	int err;
21448c2ecf20Sopenharmony_ci
21458c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) {
21468c2ecf20Sopenharmony_ci		struct fib_info *fi = fa->fa_info;
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci		if (!fi)
21498c2ecf20Sopenharmony_ci			continue;
21508c2ecf20Sopenharmony_ci
21518c2ecf20Sopenharmony_ci		/* local and main table can share the same trie,
21528c2ecf20Sopenharmony_ci		 * so don't notify twice for the same entry.
21538c2ecf20Sopenharmony_ci		 */
21548c2ecf20Sopenharmony_ci		if (tb->tb_id != fa->tb_id)
21558c2ecf20Sopenharmony_ci			continue;
21568c2ecf20Sopenharmony_ci
21578c2ecf20Sopenharmony_ci		if (fa->fa_slen == last_slen)
21588c2ecf20Sopenharmony_ci			continue;
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci		last_slen = fa->fa_slen;
21618c2ecf20Sopenharmony_ci		err = call_fib_entry_notifier(nb, FIB_EVENT_ENTRY_REPLACE,
21628c2ecf20Sopenharmony_ci					      l->key, KEYLENGTH - fa->fa_slen,
21638c2ecf20Sopenharmony_ci					      fa, extack);
21648c2ecf20Sopenharmony_ci		if (err)
21658c2ecf20Sopenharmony_ci			return err;
21668c2ecf20Sopenharmony_ci	}
21678c2ecf20Sopenharmony_ci	return 0;
21688c2ecf20Sopenharmony_ci}
21698c2ecf20Sopenharmony_ci
21708c2ecf20Sopenharmony_cistatic int fib_table_notify(struct fib_table *tb, struct notifier_block *nb,
21718c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack)
21728c2ecf20Sopenharmony_ci{
21738c2ecf20Sopenharmony_ci	struct trie *t = (struct trie *)tb->tb_data;
21748c2ecf20Sopenharmony_ci	struct key_vector *l, *tp = t->kv;
21758c2ecf20Sopenharmony_ci	t_key key = 0;
21768c2ecf20Sopenharmony_ci	int err;
21778c2ecf20Sopenharmony_ci
21788c2ecf20Sopenharmony_ci	while ((l = leaf_walk_rcu(&tp, key)) != NULL) {
21798c2ecf20Sopenharmony_ci		err = fib_leaf_notify(l, tb, nb, extack);
21808c2ecf20Sopenharmony_ci		if (err)
21818c2ecf20Sopenharmony_ci			return err;
21828c2ecf20Sopenharmony_ci
21838c2ecf20Sopenharmony_ci		key = l->key + 1;
21848c2ecf20Sopenharmony_ci		/* stop in case of wrap around */
21858c2ecf20Sopenharmony_ci		if (key < l->key)
21868c2ecf20Sopenharmony_ci			break;
21878c2ecf20Sopenharmony_ci	}
21888c2ecf20Sopenharmony_ci	return 0;
21898c2ecf20Sopenharmony_ci}
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_ciint fib_notify(struct net *net, struct notifier_block *nb,
21928c2ecf20Sopenharmony_ci	       struct netlink_ext_ack *extack)
21938c2ecf20Sopenharmony_ci{
21948c2ecf20Sopenharmony_ci	unsigned int h;
21958c2ecf20Sopenharmony_ci	int err;
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci	for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
21988c2ecf20Sopenharmony_ci		struct hlist_head *head = &net->ipv4.fib_table_hash[h];
21998c2ecf20Sopenharmony_ci		struct fib_table *tb;
22008c2ecf20Sopenharmony_ci
22018c2ecf20Sopenharmony_ci		hlist_for_each_entry_rcu(tb, head, tb_hlist) {
22028c2ecf20Sopenharmony_ci			err = fib_table_notify(tb, nb, extack);
22038c2ecf20Sopenharmony_ci			if (err)
22048c2ecf20Sopenharmony_ci				return err;
22058c2ecf20Sopenharmony_ci		}
22068c2ecf20Sopenharmony_ci	}
22078c2ecf20Sopenharmony_ci	return 0;
22088c2ecf20Sopenharmony_ci}
22098c2ecf20Sopenharmony_ci
22108c2ecf20Sopenharmony_cistatic void __trie_free_rcu(struct rcu_head *head)
22118c2ecf20Sopenharmony_ci{
22128c2ecf20Sopenharmony_ci	struct fib_table *tb = container_of(head, struct fib_table, rcu);
22138c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS
22148c2ecf20Sopenharmony_ci	struct trie *t = (struct trie *)tb->tb_data;
22158c2ecf20Sopenharmony_ci
22168c2ecf20Sopenharmony_ci	if (tb->tb_data == tb->__data)
22178c2ecf20Sopenharmony_ci		free_percpu(t->stats);
22188c2ecf20Sopenharmony_ci#endif /* CONFIG_IP_FIB_TRIE_STATS */
22198c2ecf20Sopenharmony_ci	kfree(tb);
22208c2ecf20Sopenharmony_ci}
22218c2ecf20Sopenharmony_ci
22228c2ecf20Sopenharmony_civoid fib_free_table(struct fib_table *tb)
22238c2ecf20Sopenharmony_ci{
22248c2ecf20Sopenharmony_ci	call_rcu(&tb->rcu, __trie_free_rcu);
22258c2ecf20Sopenharmony_ci}
22268c2ecf20Sopenharmony_ci
22278c2ecf20Sopenharmony_cistatic int fn_trie_dump_leaf(struct key_vector *l, struct fib_table *tb,
22288c2ecf20Sopenharmony_ci			     struct sk_buff *skb, struct netlink_callback *cb,
22298c2ecf20Sopenharmony_ci			     struct fib_dump_filter *filter)
22308c2ecf20Sopenharmony_ci{
22318c2ecf20Sopenharmony_ci	unsigned int flags = NLM_F_MULTI;
22328c2ecf20Sopenharmony_ci	__be32 xkey = htonl(l->key);
22338c2ecf20Sopenharmony_ci	int i, s_i, i_fa, s_fa, err;
22348c2ecf20Sopenharmony_ci	struct fib_alias *fa;
22358c2ecf20Sopenharmony_ci
22368c2ecf20Sopenharmony_ci	if (filter->filter_set ||
22378c2ecf20Sopenharmony_ci	    !filter->dump_exceptions || !filter->dump_routes)
22388c2ecf20Sopenharmony_ci		flags |= NLM_F_DUMP_FILTERED;
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_ci	s_i = cb->args[4];
22418c2ecf20Sopenharmony_ci	s_fa = cb->args[5];
22428c2ecf20Sopenharmony_ci	i = 0;
22438c2ecf20Sopenharmony_ci
22448c2ecf20Sopenharmony_ci	/* rcu_read_lock is hold by caller */
22458c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) {
22468c2ecf20Sopenharmony_ci		struct fib_info *fi = fa->fa_info;
22478c2ecf20Sopenharmony_ci
22488c2ecf20Sopenharmony_ci		if (i < s_i)
22498c2ecf20Sopenharmony_ci			goto next;
22508c2ecf20Sopenharmony_ci
22518c2ecf20Sopenharmony_ci		i_fa = 0;
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_ci		if (tb->tb_id != fa->tb_id)
22548c2ecf20Sopenharmony_ci			goto next;
22558c2ecf20Sopenharmony_ci
22568c2ecf20Sopenharmony_ci		if (filter->filter_set) {
22578c2ecf20Sopenharmony_ci			if (filter->rt_type && fa->fa_type != filter->rt_type)
22588c2ecf20Sopenharmony_ci				goto next;
22598c2ecf20Sopenharmony_ci
22608c2ecf20Sopenharmony_ci			if ((filter->protocol &&
22618c2ecf20Sopenharmony_ci			     fi->fib_protocol != filter->protocol))
22628c2ecf20Sopenharmony_ci				goto next;
22638c2ecf20Sopenharmony_ci
22648c2ecf20Sopenharmony_ci			if (filter->dev &&
22658c2ecf20Sopenharmony_ci			    !fib_info_nh_uses_dev(fi, filter->dev))
22668c2ecf20Sopenharmony_ci				goto next;
22678c2ecf20Sopenharmony_ci		}
22688c2ecf20Sopenharmony_ci
22698c2ecf20Sopenharmony_ci		if (filter->dump_routes) {
22708c2ecf20Sopenharmony_ci			if (!s_fa) {
22718c2ecf20Sopenharmony_ci				struct fib_rt_info fri;
22728c2ecf20Sopenharmony_ci
22738c2ecf20Sopenharmony_ci				fri.fi = fi;
22748c2ecf20Sopenharmony_ci				fri.tb_id = tb->tb_id;
22758c2ecf20Sopenharmony_ci				fri.dst = xkey;
22768c2ecf20Sopenharmony_ci				fri.dst_len = KEYLENGTH - fa->fa_slen;
22778c2ecf20Sopenharmony_ci				fri.tos = fa->fa_tos;
22788c2ecf20Sopenharmony_ci				fri.type = fa->fa_type;
22798c2ecf20Sopenharmony_ci				fri.offload = fa->offload;
22808c2ecf20Sopenharmony_ci				fri.trap = fa->trap;
22818c2ecf20Sopenharmony_ci				err = fib_dump_info(skb,
22828c2ecf20Sopenharmony_ci						    NETLINK_CB(cb->skb).portid,
22838c2ecf20Sopenharmony_ci						    cb->nlh->nlmsg_seq,
22848c2ecf20Sopenharmony_ci						    RTM_NEWROUTE, &fri, flags);
22858c2ecf20Sopenharmony_ci				if (err < 0)
22868c2ecf20Sopenharmony_ci					goto stop;
22878c2ecf20Sopenharmony_ci			}
22888c2ecf20Sopenharmony_ci
22898c2ecf20Sopenharmony_ci			i_fa++;
22908c2ecf20Sopenharmony_ci		}
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci		if (filter->dump_exceptions) {
22938c2ecf20Sopenharmony_ci			err = fib_dump_info_fnhe(skb, cb, tb->tb_id, fi,
22948c2ecf20Sopenharmony_ci						 &i_fa, s_fa, flags);
22958c2ecf20Sopenharmony_ci			if (err < 0)
22968c2ecf20Sopenharmony_ci				goto stop;
22978c2ecf20Sopenharmony_ci		}
22988c2ecf20Sopenharmony_ci
22998c2ecf20Sopenharmony_cinext:
23008c2ecf20Sopenharmony_ci		i++;
23018c2ecf20Sopenharmony_ci	}
23028c2ecf20Sopenharmony_ci
23038c2ecf20Sopenharmony_ci	cb->args[4] = i;
23048c2ecf20Sopenharmony_ci	return skb->len;
23058c2ecf20Sopenharmony_ci
23068c2ecf20Sopenharmony_cistop:
23078c2ecf20Sopenharmony_ci	cb->args[4] = i;
23088c2ecf20Sopenharmony_ci	cb->args[5] = i_fa;
23098c2ecf20Sopenharmony_ci	return err;
23108c2ecf20Sopenharmony_ci}
23118c2ecf20Sopenharmony_ci
23128c2ecf20Sopenharmony_ci/* rcu_read_lock needs to be hold by caller from readside */
23138c2ecf20Sopenharmony_ciint fib_table_dump(struct fib_table *tb, struct sk_buff *skb,
23148c2ecf20Sopenharmony_ci		   struct netlink_callback *cb, struct fib_dump_filter *filter)
23158c2ecf20Sopenharmony_ci{
23168c2ecf20Sopenharmony_ci	struct trie *t = (struct trie *)tb->tb_data;
23178c2ecf20Sopenharmony_ci	struct key_vector *l, *tp = t->kv;
23188c2ecf20Sopenharmony_ci	/* Dump starting at last key.
23198c2ecf20Sopenharmony_ci	 * Note: 0.0.0.0/0 (ie default) is first key.
23208c2ecf20Sopenharmony_ci	 */
23218c2ecf20Sopenharmony_ci	int count = cb->args[2];
23228c2ecf20Sopenharmony_ci	t_key key = cb->args[3];
23238c2ecf20Sopenharmony_ci
23248c2ecf20Sopenharmony_ci	/* First time here, count and key are both always 0. Count > 0
23258c2ecf20Sopenharmony_ci	 * and key == 0 means the dump has wrapped around and we are done.
23268c2ecf20Sopenharmony_ci	 */
23278c2ecf20Sopenharmony_ci	if (count && !key)
23288c2ecf20Sopenharmony_ci		return skb->len;
23298c2ecf20Sopenharmony_ci
23308c2ecf20Sopenharmony_ci	while ((l = leaf_walk_rcu(&tp, key)) != NULL) {
23318c2ecf20Sopenharmony_ci		int err;
23328c2ecf20Sopenharmony_ci
23338c2ecf20Sopenharmony_ci		err = fn_trie_dump_leaf(l, tb, skb, cb, filter);
23348c2ecf20Sopenharmony_ci		if (err < 0) {
23358c2ecf20Sopenharmony_ci			cb->args[3] = key;
23368c2ecf20Sopenharmony_ci			cb->args[2] = count;
23378c2ecf20Sopenharmony_ci			return err;
23388c2ecf20Sopenharmony_ci		}
23398c2ecf20Sopenharmony_ci
23408c2ecf20Sopenharmony_ci		++count;
23418c2ecf20Sopenharmony_ci		key = l->key + 1;
23428c2ecf20Sopenharmony_ci
23438c2ecf20Sopenharmony_ci		memset(&cb->args[4], 0,
23448c2ecf20Sopenharmony_ci		       sizeof(cb->args) - 4*sizeof(cb->args[0]));
23458c2ecf20Sopenharmony_ci
23468c2ecf20Sopenharmony_ci		/* stop loop if key wrapped back to 0 */
23478c2ecf20Sopenharmony_ci		if (key < l->key)
23488c2ecf20Sopenharmony_ci			break;
23498c2ecf20Sopenharmony_ci	}
23508c2ecf20Sopenharmony_ci
23518c2ecf20Sopenharmony_ci	cb->args[3] = key;
23528c2ecf20Sopenharmony_ci	cb->args[2] = count;
23538c2ecf20Sopenharmony_ci
23548c2ecf20Sopenharmony_ci	return skb->len;
23558c2ecf20Sopenharmony_ci}
23568c2ecf20Sopenharmony_ci
23578c2ecf20Sopenharmony_civoid __init fib_trie_init(void)
23588c2ecf20Sopenharmony_ci{
23598c2ecf20Sopenharmony_ci	fn_alias_kmem = kmem_cache_create("ip_fib_alias",
23608c2ecf20Sopenharmony_ci					  sizeof(struct fib_alias),
23618c2ecf20Sopenharmony_ci					  0, SLAB_PANIC, NULL);
23628c2ecf20Sopenharmony_ci
23638c2ecf20Sopenharmony_ci	trie_leaf_kmem = kmem_cache_create("ip_fib_trie",
23648c2ecf20Sopenharmony_ci					   LEAF_SIZE,
23658c2ecf20Sopenharmony_ci					   0, SLAB_PANIC, NULL);
23668c2ecf20Sopenharmony_ci}
23678c2ecf20Sopenharmony_ci
23688c2ecf20Sopenharmony_cistruct fib_table *fib_trie_table(u32 id, struct fib_table *alias)
23698c2ecf20Sopenharmony_ci{
23708c2ecf20Sopenharmony_ci	struct fib_table *tb;
23718c2ecf20Sopenharmony_ci	struct trie *t;
23728c2ecf20Sopenharmony_ci	size_t sz = sizeof(*tb);
23738c2ecf20Sopenharmony_ci
23748c2ecf20Sopenharmony_ci	if (!alias)
23758c2ecf20Sopenharmony_ci		sz += sizeof(struct trie);
23768c2ecf20Sopenharmony_ci
23778c2ecf20Sopenharmony_ci	tb = kzalloc(sz, GFP_KERNEL);
23788c2ecf20Sopenharmony_ci	if (!tb)
23798c2ecf20Sopenharmony_ci		return NULL;
23808c2ecf20Sopenharmony_ci
23818c2ecf20Sopenharmony_ci	tb->tb_id = id;
23828c2ecf20Sopenharmony_ci	tb->tb_num_default = 0;
23838c2ecf20Sopenharmony_ci	tb->tb_data = (alias ? alias->__data : tb->__data);
23848c2ecf20Sopenharmony_ci
23858c2ecf20Sopenharmony_ci	if (alias)
23868c2ecf20Sopenharmony_ci		return tb;
23878c2ecf20Sopenharmony_ci
23888c2ecf20Sopenharmony_ci	t = (struct trie *) tb->tb_data;
23898c2ecf20Sopenharmony_ci	t->kv[0].pos = KEYLENGTH;
23908c2ecf20Sopenharmony_ci	t->kv[0].slen = KEYLENGTH;
23918c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS
23928c2ecf20Sopenharmony_ci	t->stats = alloc_percpu(struct trie_use_stats);
23938c2ecf20Sopenharmony_ci	if (!t->stats) {
23948c2ecf20Sopenharmony_ci		kfree(tb);
23958c2ecf20Sopenharmony_ci		tb = NULL;
23968c2ecf20Sopenharmony_ci	}
23978c2ecf20Sopenharmony_ci#endif
23988c2ecf20Sopenharmony_ci
23998c2ecf20Sopenharmony_ci	return tb;
24008c2ecf20Sopenharmony_ci}
24018c2ecf20Sopenharmony_ci
24028c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS
24038c2ecf20Sopenharmony_ci/* Depth first Trie walk iterator */
24048c2ecf20Sopenharmony_cistruct fib_trie_iter {
24058c2ecf20Sopenharmony_ci	struct seq_net_private p;
24068c2ecf20Sopenharmony_ci	struct fib_table *tb;
24078c2ecf20Sopenharmony_ci	struct key_vector *tnode;
24088c2ecf20Sopenharmony_ci	unsigned int index;
24098c2ecf20Sopenharmony_ci	unsigned int depth;
24108c2ecf20Sopenharmony_ci};
24118c2ecf20Sopenharmony_ci
24128c2ecf20Sopenharmony_cistatic struct key_vector *fib_trie_get_next(struct fib_trie_iter *iter)
24138c2ecf20Sopenharmony_ci{
24148c2ecf20Sopenharmony_ci	unsigned long cindex = iter->index;
24158c2ecf20Sopenharmony_ci	struct key_vector *pn = iter->tnode;
24168c2ecf20Sopenharmony_ci	t_key pkey;
24178c2ecf20Sopenharmony_ci
24188c2ecf20Sopenharmony_ci	pr_debug("get_next iter={node=%p index=%d depth=%d}\n",
24198c2ecf20Sopenharmony_ci		 iter->tnode, iter->index, iter->depth);
24208c2ecf20Sopenharmony_ci
24218c2ecf20Sopenharmony_ci	while (!IS_TRIE(pn)) {
24228c2ecf20Sopenharmony_ci		while (cindex < child_length(pn)) {
24238c2ecf20Sopenharmony_ci			struct key_vector *n = get_child_rcu(pn, cindex++);
24248c2ecf20Sopenharmony_ci
24258c2ecf20Sopenharmony_ci			if (!n)
24268c2ecf20Sopenharmony_ci				continue;
24278c2ecf20Sopenharmony_ci
24288c2ecf20Sopenharmony_ci			if (IS_LEAF(n)) {
24298c2ecf20Sopenharmony_ci				iter->tnode = pn;
24308c2ecf20Sopenharmony_ci				iter->index = cindex;
24318c2ecf20Sopenharmony_ci			} else {
24328c2ecf20Sopenharmony_ci				/* push down one level */
24338c2ecf20Sopenharmony_ci				iter->tnode = n;
24348c2ecf20Sopenharmony_ci				iter->index = 0;
24358c2ecf20Sopenharmony_ci				++iter->depth;
24368c2ecf20Sopenharmony_ci			}
24378c2ecf20Sopenharmony_ci
24388c2ecf20Sopenharmony_ci			return n;
24398c2ecf20Sopenharmony_ci		}
24408c2ecf20Sopenharmony_ci
24418c2ecf20Sopenharmony_ci		/* Current node exhausted, pop back up */
24428c2ecf20Sopenharmony_ci		pkey = pn->key;
24438c2ecf20Sopenharmony_ci		pn = node_parent_rcu(pn);
24448c2ecf20Sopenharmony_ci		cindex = get_index(pkey, pn) + 1;
24458c2ecf20Sopenharmony_ci		--iter->depth;
24468c2ecf20Sopenharmony_ci	}
24478c2ecf20Sopenharmony_ci
24488c2ecf20Sopenharmony_ci	/* record root node so further searches know we are done */
24498c2ecf20Sopenharmony_ci	iter->tnode = pn;
24508c2ecf20Sopenharmony_ci	iter->index = 0;
24518c2ecf20Sopenharmony_ci
24528c2ecf20Sopenharmony_ci	return NULL;
24538c2ecf20Sopenharmony_ci}
24548c2ecf20Sopenharmony_ci
24558c2ecf20Sopenharmony_cistatic struct key_vector *fib_trie_get_first(struct fib_trie_iter *iter,
24568c2ecf20Sopenharmony_ci					     struct trie *t)
24578c2ecf20Sopenharmony_ci{
24588c2ecf20Sopenharmony_ci	struct key_vector *n, *pn;
24598c2ecf20Sopenharmony_ci
24608c2ecf20Sopenharmony_ci	if (!t)
24618c2ecf20Sopenharmony_ci		return NULL;
24628c2ecf20Sopenharmony_ci
24638c2ecf20Sopenharmony_ci	pn = t->kv;
24648c2ecf20Sopenharmony_ci	n = rcu_dereference(pn->tnode[0]);
24658c2ecf20Sopenharmony_ci	if (!n)
24668c2ecf20Sopenharmony_ci		return NULL;
24678c2ecf20Sopenharmony_ci
24688c2ecf20Sopenharmony_ci	if (IS_TNODE(n)) {
24698c2ecf20Sopenharmony_ci		iter->tnode = n;
24708c2ecf20Sopenharmony_ci		iter->index = 0;
24718c2ecf20Sopenharmony_ci		iter->depth = 1;
24728c2ecf20Sopenharmony_ci	} else {
24738c2ecf20Sopenharmony_ci		iter->tnode = pn;
24748c2ecf20Sopenharmony_ci		iter->index = 0;
24758c2ecf20Sopenharmony_ci		iter->depth = 0;
24768c2ecf20Sopenharmony_ci	}
24778c2ecf20Sopenharmony_ci
24788c2ecf20Sopenharmony_ci	return n;
24798c2ecf20Sopenharmony_ci}
24808c2ecf20Sopenharmony_ci
24818c2ecf20Sopenharmony_cistatic void trie_collect_stats(struct trie *t, struct trie_stat *s)
24828c2ecf20Sopenharmony_ci{
24838c2ecf20Sopenharmony_ci	struct key_vector *n;
24848c2ecf20Sopenharmony_ci	struct fib_trie_iter iter;
24858c2ecf20Sopenharmony_ci
24868c2ecf20Sopenharmony_ci	memset(s, 0, sizeof(*s));
24878c2ecf20Sopenharmony_ci
24888c2ecf20Sopenharmony_ci	rcu_read_lock();
24898c2ecf20Sopenharmony_ci	for (n = fib_trie_get_first(&iter, t); n; n = fib_trie_get_next(&iter)) {
24908c2ecf20Sopenharmony_ci		if (IS_LEAF(n)) {
24918c2ecf20Sopenharmony_ci			struct fib_alias *fa;
24928c2ecf20Sopenharmony_ci
24938c2ecf20Sopenharmony_ci			s->leaves++;
24948c2ecf20Sopenharmony_ci			s->totdepth += iter.depth;
24958c2ecf20Sopenharmony_ci			if (iter.depth > s->maxdepth)
24968c2ecf20Sopenharmony_ci				s->maxdepth = iter.depth;
24978c2ecf20Sopenharmony_ci
24988c2ecf20Sopenharmony_ci			hlist_for_each_entry_rcu(fa, &n->leaf, fa_list)
24998c2ecf20Sopenharmony_ci				++s->prefixes;
25008c2ecf20Sopenharmony_ci		} else {
25018c2ecf20Sopenharmony_ci			s->tnodes++;
25028c2ecf20Sopenharmony_ci			if (n->bits < MAX_STAT_DEPTH)
25038c2ecf20Sopenharmony_ci				s->nodesizes[n->bits]++;
25048c2ecf20Sopenharmony_ci			s->nullpointers += tn_info(n)->empty_children;
25058c2ecf20Sopenharmony_ci		}
25068c2ecf20Sopenharmony_ci	}
25078c2ecf20Sopenharmony_ci	rcu_read_unlock();
25088c2ecf20Sopenharmony_ci}
25098c2ecf20Sopenharmony_ci
25108c2ecf20Sopenharmony_ci/*
25118c2ecf20Sopenharmony_ci *	This outputs /proc/net/fib_triestats
25128c2ecf20Sopenharmony_ci */
25138c2ecf20Sopenharmony_cistatic void trie_show_stats(struct seq_file *seq, struct trie_stat *stat)
25148c2ecf20Sopenharmony_ci{
25158c2ecf20Sopenharmony_ci	unsigned int i, max, pointers, bytes, avdepth;
25168c2ecf20Sopenharmony_ci
25178c2ecf20Sopenharmony_ci	if (stat->leaves)
25188c2ecf20Sopenharmony_ci		avdepth = stat->totdepth*100 / stat->leaves;
25198c2ecf20Sopenharmony_ci	else
25208c2ecf20Sopenharmony_ci		avdepth = 0;
25218c2ecf20Sopenharmony_ci
25228c2ecf20Sopenharmony_ci	seq_printf(seq, "\tAver depth:     %u.%02d\n",
25238c2ecf20Sopenharmony_ci		   avdepth / 100, avdepth % 100);
25248c2ecf20Sopenharmony_ci	seq_printf(seq, "\tMax depth:      %u\n", stat->maxdepth);
25258c2ecf20Sopenharmony_ci
25268c2ecf20Sopenharmony_ci	seq_printf(seq, "\tLeaves:         %u\n", stat->leaves);
25278c2ecf20Sopenharmony_ci	bytes = LEAF_SIZE * stat->leaves;
25288c2ecf20Sopenharmony_ci
25298c2ecf20Sopenharmony_ci	seq_printf(seq, "\tPrefixes:       %u\n", stat->prefixes);
25308c2ecf20Sopenharmony_ci	bytes += sizeof(struct fib_alias) * stat->prefixes;
25318c2ecf20Sopenharmony_ci
25328c2ecf20Sopenharmony_ci	seq_printf(seq, "\tInternal nodes: %u\n\t", stat->tnodes);
25338c2ecf20Sopenharmony_ci	bytes += TNODE_SIZE(0) * stat->tnodes;
25348c2ecf20Sopenharmony_ci
25358c2ecf20Sopenharmony_ci	max = MAX_STAT_DEPTH;
25368c2ecf20Sopenharmony_ci	while (max > 0 && stat->nodesizes[max-1] == 0)
25378c2ecf20Sopenharmony_ci		max--;
25388c2ecf20Sopenharmony_ci
25398c2ecf20Sopenharmony_ci	pointers = 0;
25408c2ecf20Sopenharmony_ci	for (i = 1; i < max; i++)
25418c2ecf20Sopenharmony_ci		if (stat->nodesizes[i] != 0) {
25428c2ecf20Sopenharmony_ci			seq_printf(seq, "  %u: %u",  i, stat->nodesizes[i]);
25438c2ecf20Sopenharmony_ci			pointers += (1<<i) * stat->nodesizes[i];
25448c2ecf20Sopenharmony_ci		}
25458c2ecf20Sopenharmony_ci	seq_putc(seq, '\n');
25468c2ecf20Sopenharmony_ci	seq_printf(seq, "\tPointers: %u\n", pointers);
25478c2ecf20Sopenharmony_ci
25488c2ecf20Sopenharmony_ci	bytes += sizeof(struct key_vector *) * pointers;
25498c2ecf20Sopenharmony_ci	seq_printf(seq, "Null ptrs: %u\n", stat->nullpointers);
25508c2ecf20Sopenharmony_ci	seq_printf(seq, "Total size: %u  kB\n", (bytes + 1023) / 1024);
25518c2ecf20Sopenharmony_ci}
25528c2ecf20Sopenharmony_ci
25538c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS
25548c2ecf20Sopenharmony_cistatic void trie_show_usage(struct seq_file *seq,
25558c2ecf20Sopenharmony_ci			    const struct trie_use_stats __percpu *stats)
25568c2ecf20Sopenharmony_ci{
25578c2ecf20Sopenharmony_ci	struct trie_use_stats s = { 0 };
25588c2ecf20Sopenharmony_ci	int cpu;
25598c2ecf20Sopenharmony_ci
25608c2ecf20Sopenharmony_ci	/* loop through all of the CPUs and gather up the stats */
25618c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
25628c2ecf20Sopenharmony_ci		const struct trie_use_stats *pcpu = per_cpu_ptr(stats, cpu);
25638c2ecf20Sopenharmony_ci
25648c2ecf20Sopenharmony_ci		s.gets += pcpu->gets;
25658c2ecf20Sopenharmony_ci		s.backtrack += pcpu->backtrack;
25668c2ecf20Sopenharmony_ci		s.semantic_match_passed += pcpu->semantic_match_passed;
25678c2ecf20Sopenharmony_ci		s.semantic_match_miss += pcpu->semantic_match_miss;
25688c2ecf20Sopenharmony_ci		s.null_node_hit += pcpu->null_node_hit;
25698c2ecf20Sopenharmony_ci		s.resize_node_skipped += pcpu->resize_node_skipped;
25708c2ecf20Sopenharmony_ci	}
25718c2ecf20Sopenharmony_ci
25728c2ecf20Sopenharmony_ci	seq_printf(seq, "\nCounters:\n---------\n");
25738c2ecf20Sopenharmony_ci	seq_printf(seq, "gets = %u\n", s.gets);
25748c2ecf20Sopenharmony_ci	seq_printf(seq, "backtracks = %u\n", s.backtrack);
25758c2ecf20Sopenharmony_ci	seq_printf(seq, "semantic match passed = %u\n",
25768c2ecf20Sopenharmony_ci		   s.semantic_match_passed);
25778c2ecf20Sopenharmony_ci	seq_printf(seq, "semantic match miss = %u\n", s.semantic_match_miss);
25788c2ecf20Sopenharmony_ci	seq_printf(seq, "null node hit= %u\n", s.null_node_hit);
25798c2ecf20Sopenharmony_ci	seq_printf(seq, "skipped node resize = %u\n\n", s.resize_node_skipped);
25808c2ecf20Sopenharmony_ci}
25818c2ecf20Sopenharmony_ci#endif /*  CONFIG_IP_FIB_TRIE_STATS */
25828c2ecf20Sopenharmony_ci
25838c2ecf20Sopenharmony_cistatic void fib_table_print(struct seq_file *seq, struct fib_table *tb)
25848c2ecf20Sopenharmony_ci{
25858c2ecf20Sopenharmony_ci	if (tb->tb_id == RT_TABLE_LOCAL)
25868c2ecf20Sopenharmony_ci		seq_puts(seq, "Local:\n");
25878c2ecf20Sopenharmony_ci	else if (tb->tb_id == RT_TABLE_MAIN)
25888c2ecf20Sopenharmony_ci		seq_puts(seq, "Main:\n");
25898c2ecf20Sopenharmony_ci	else
25908c2ecf20Sopenharmony_ci		seq_printf(seq, "Id %d:\n", tb->tb_id);
25918c2ecf20Sopenharmony_ci}
25928c2ecf20Sopenharmony_ci
25938c2ecf20Sopenharmony_ci
25948c2ecf20Sopenharmony_cistatic int fib_triestat_seq_show(struct seq_file *seq, void *v)
25958c2ecf20Sopenharmony_ci{
25968c2ecf20Sopenharmony_ci	struct net *net = (struct net *)seq->private;
25978c2ecf20Sopenharmony_ci	unsigned int h;
25988c2ecf20Sopenharmony_ci
25998c2ecf20Sopenharmony_ci	seq_printf(seq,
26008c2ecf20Sopenharmony_ci		   "Basic info: size of leaf:"
26018c2ecf20Sopenharmony_ci		   " %zd bytes, size of tnode: %zd bytes.\n",
26028c2ecf20Sopenharmony_ci		   LEAF_SIZE, TNODE_SIZE(0));
26038c2ecf20Sopenharmony_ci
26048c2ecf20Sopenharmony_ci	rcu_read_lock();
26058c2ecf20Sopenharmony_ci	for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
26068c2ecf20Sopenharmony_ci		struct hlist_head *head = &net->ipv4.fib_table_hash[h];
26078c2ecf20Sopenharmony_ci		struct fib_table *tb;
26088c2ecf20Sopenharmony_ci
26098c2ecf20Sopenharmony_ci		hlist_for_each_entry_rcu(tb, head, tb_hlist) {
26108c2ecf20Sopenharmony_ci			struct trie *t = (struct trie *) tb->tb_data;
26118c2ecf20Sopenharmony_ci			struct trie_stat stat;
26128c2ecf20Sopenharmony_ci
26138c2ecf20Sopenharmony_ci			if (!t)
26148c2ecf20Sopenharmony_ci				continue;
26158c2ecf20Sopenharmony_ci
26168c2ecf20Sopenharmony_ci			fib_table_print(seq, tb);
26178c2ecf20Sopenharmony_ci
26188c2ecf20Sopenharmony_ci			trie_collect_stats(t, &stat);
26198c2ecf20Sopenharmony_ci			trie_show_stats(seq, &stat);
26208c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_FIB_TRIE_STATS
26218c2ecf20Sopenharmony_ci			trie_show_usage(seq, t->stats);
26228c2ecf20Sopenharmony_ci#endif
26238c2ecf20Sopenharmony_ci		}
26248c2ecf20Sopenharmony_ci		cond_resched_rcu();
26258c2ecf20Sopenharmony_ci	}
26268c2ecf20Sopenharmony_ci	rcu_read_unlock();
26278c2ecf20Sopenharmony_ci
26288c2ecf20Sopenharmony_ci	return 0;
26298c2ecf20Sopenharmony_ci}
26308c2ecf20Sopenharmony_ci
26318c2ecf20Sopenharmony_cistatic struct key_vector *fib_trie_get_idx(struct seq_file *seq, loff_t pos)
26328c2ecf20Sopenharmony_ci{
26338c2ecf20Sopenharmony_ci	struct fib_trie_iter *iter = seq->private;
26348c2ecf20Sopenharmony_ci	struct net *net = seq_file_net(seq);
26358c2ecf20Sopenharmony_ci	loff_t idx = 0;
26368c2ecf20Sopenharmony_ci	unsigned int h;
26378c2ecf20Sopenharmony_ci
26388c2ecf20Sopenharmony_ci	for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
26398c2ecf20Sopenharmony_ci		struct hlist_head *head = &net->ipv4.fib_table_hash[h];
26408c2ecf20Sopenharmony_ci		struct fib_table *tb;
26418c2ecf20Sopenharmony_ci
26428c2ecf20Sopenharmony_ci		hlist_for_each_entry_rcu(tb, head, tb_hlist) {
26438c2ecf20Sopenharmony_ci			struct key_vector *n;
26448c2ecf20Sopenharmony_ci
26458c2ecf20Sopenharmony_ci			for (n = fib_trie_get_first(iter,
26468c2ecf20Sopenharmony_ci						    (struct trie *) tb->tb_data);
26478c2ecf20Sopenharmony_ci			     n; n = fib_trie_get_next(iter))
26488c2ecf20Sopenharmony_ci				if (pos == idx++) {
26498c2ecf20Sopenharmony_ci					iter->tb = tb;
26508c2ecf20Sopenharmony_ci					return n;
26518c2ecf20Sopenharmony_ci				}
26528c2ecf20Sopenharmony_ci		}
26538c2ecf20Sopenharmony_ci	}
26548c2ecf20Sopenharmony_ci
26558c2ecf20Sopenharmony_ci	return NULL;
26568c2ecf20Sopenharmony_ci}
26578c2ecf20Sopenharmony_ci
26588c2ecf20Sopenharmony_cistatic void *fib_trie_seq_start(struct seq_file *seq, loff_t *pos)
26598c2ecf20Sopenharmony_ci	__acquires(RCU)
26608c2ecf20Sopenharmony_ci{
26618c2ecf20Sopenharmony_ci	rcu_read_lock();
26628c2ecf20Sopenharmony_ci	return fib_trie_get_idx(seq, *pos);
26638c2ecf20Sopenharmony_ci}
26648c2ecf20Sopenharmony_ci
26658c2ecf20Sopenharmony_cistatic void *fib_trie_seq_next(struct seq_file *seq, void *v, loff_t *pos)
26668c2ecf20Sopenharmony_ci{
26678c2ecf20Sopenharmony_ci	struct fib_trie_iter *iter = seq->private;
26688c2ecf20Sopenharmony_ci	struct net *net = seq_file_net(seq);
26698c2ecf20Sopenharmony_ci	struct fib_table *tb = iter->tb;
26708c2ecf20Sopenharmony_ci	struct hlist_node *tb_node;
26718c2ecf20Sopenharmony_ci	unsigned int h;
26728c2ecf20Sopenharmony_ci	struct key_vector *n;
26738c2ecf20Sopenharmony_ci
26748c2ecf20Sopenharmony_ci	++*pos;
26758c2ecf20Sopenharmony_ci	/* next node in same table */
26768c2ecf20Sopenharmony_ci	n = fib_trie_get_next(iter);
26778c2ecf20Sopenharmony_ci	if (n)
26788c2ecf20Sopenharmony_ci		return n;
26798c2ecf20Sopenharmony_ci
26808c2ecf20Sopenharmony_ci	/* walk rest of this hash chain */
26818c2ecf20Sopenharmony_ci	h = tb->tb_id & (FIB_TABLE_HASHSZ - 1);
26828c2ecf20Sopenharmony_ci	while ((tb_node = rcu_dereference(hlist_next_rcu(&tb->tb_hlist)))) {
26838c2ecf20Sopenharmony_ci		tb = hlist_entry(tb_node, struct fib_table, tb_hlist);
26848c2ecf20Sopenharmony_ci		n = fib_trie_get_first(iter, (struct trie *) tb->tb_data);
26858c2ecf20Sopenharmony_ci		if (n)
26868c2ecf20Sopenharmony_ci			goto found;
26878c2ecf20Sopenharmony_ci	}
26888c2ecf20Sopenharmony_ci
26898c2ecf20Sopenharmony_ci	/* new hash chain */
26908c2ecf20Sopenharmony_ci	while (++h < FIB_TABLE_HASHSZ) {
26918c2ecf20Sopenharmony_ci		struct hlist_head *head = &net->ipv4.fib_table_hash[h];
26928c2ecf20Sopenharmony_ci		hlist_for_each_entry_rcu(tb, head, tb_hlist) {
26938c2ecf20Sopenharmony_ci			n = fib_trie_get_first(iter, (struct trie *) tb->tb_data);
26948c2ecf20Sopenharmony_ci			if (n)
26958c2ecf20Sopenharmony_ci				goto found;
26968c2ecf20Sopenharmony_ci		}
26978c2ecf20Sopenharmony_ci	}
26988c2ecf20Sopenharmony_ci	return NULL;
26998c2ecf20Sopenharmony_ci
27008c2ecf20Sopenharmony_cifound:
27018c2ecf20Sopenharmony_ci	iter->tb = tb;
27028c2ecf20Sopenharmony_ci	return n;
27038c2ecf20Sopenharmony_ci}
27048c2ecf20Sopenharmony_ci
27058c2ecf20Sopenharmony_cistatic void fib_trie_seq_stop(struct seq_file *seq, void *v)
27068c2ecf20Sopenharmony_ci	__releases(RCU)
27078c2ecf20Sopenharmony_ci{
27088c2ecf20Sopenharmony_ci	rcu_read_unlock();
27098c2ecf20Sopenharmony_ci}
27108c2ecf20Sopenharmony_ci
27118c2ecf20Sopenharmony_cistatic void seq_indent(struct seq_file *seq, int n)
27128c2ecf20Sopenharmony_ci{
27138c2ecf20Sopenharmony_ci	while (n-- > 0)
27148c2ecf20Sopenharmony_ci		seq_puts(seq, "   ");
27158c2ecf20Sopenharmony_ci}
27168c2ecf20Sopenharmony_ci
27178c2ecf20Sopenharmony_cistatic inline const char *rtn_scope(char *buf, size_t len, enum rt_scope_t s)
27188c2ecf20Sopenharmony_ci{
27198c2ecf20Sopenharmony_ci	switch (s) {
27208c2ecf20Sopenharmony_ci	case RT_SCOPE_UNIVERSE: return "universe";
27218c2ecf20Sopenharmony_ci	case RT_SCOPE_SITE:	return "site";
27228c2ecf20Sopenharmony_ci	case RT_SCOPE_LINK:	return "link";
27238c2ecf20Sopenharmony_ci	case RT_SCOPE_HOST:	return "host";
27248c2ecf20Sopenharmony_ci	case RT_SCOPE_NOWHERE:	return "nowhere";
27258c2ecf20Sopenharmony_ci	default:
27268c2ecf20Sopenharmony_ci		snprintf(buf, len, "scope=%d", s);
27278c2ecf20Sopenharmony_ci		return buf;
27288c2ecf20Sopenharmony_ci	}
27298c2ecf20Sopenharmony_ci}
27308c2ecf20Sopenharmony_ci
27318c2ecf20Sopenharmony_cistatic const char *const rtn_type_names[__RTN_MAX] = {
27328c2ecf20Sopenharmony_ci	[RTN_UNSPEC] = "UNSPEC",
27338c2ecf20Sopenharmony_ci	[RTN_UNICAST] = "UNICAST",
27348c2ecf20Sopenharmony_ci	[RTN_LOCAL] = "LOCAL",
27358c2ecf20Sopenharmony_ci	[RTN_BROADCAST] = "BROADCAST",
27368c2ecf20Sopenharmony_ci	[RTN_ANYCAST] = "ANYCAST",
27378c2ecf20Sopenharmony_ci	[RTN_MULTICAST] = "MULTICAST",
27388c2ecf20Sopenharmony_ci	[RTN_BLACKHOLE] = "BLACKHOLE",
27398c2ecf20Sopenharmony_ci	[RTN_UNREACHABLE] = "UNREACHABLE",
27408c2ecf20Sopenharmony_ci	[RTN_PROHIBIT] = "PROHIBIT",
27418c2ecf20Sopenharmony_ci	[RTN_THROW] = "THROW",
27428c2ecf20Sopenharmony_ci	[RTN_NAT] = "NAT",
27438c2ecf20Sopenharmony_ci	[RTN_XRESOLVE] = "XRESOLVE",
27448c2ecf20Sopenharmony_ci};
27458c2ecf20Sopenharmony_ci
27468c2ecf20Sopenharmony_cistatic inline const char *rtn_type(char *buf, size_t len, unsigned int t)
27478c2ecf20Sopenharmony_ci{
27488c2ecf20Sopenharmony_ci	if (t < __RTN_MAX && rtn_type_names[t])
27498c2ecf20Sopenharmony_ci		return rtn_type_names[t];
27508c2ecf20Sopenharmony_ci	snprintf(buf, len, "type %u", t);
27518c2ecf20Sopenharmony_ci	return buf;
27528c2ecf20Sopenharmony_ci}
27538c2ecf20Sopenharmony_ci
27548c2ecf20Sopenharmony_ci/* Pretty print the trie */
27558c2ecf20Sopenharmony_cistatic int fib_trie_seq_show(struct seq_file *seq, void *v)
27568c2ecf20Sopenharmony_ci{
27578c2ecf20Sopenharmony_ci	const struct fib_trie_iter *iter = seq->private;
27588c2ecf20Sopenharmony_ci	struct key_vector *n = v;
27598c2ecf20Sopenharmony_ci
27608c2ecf20Sopenharmony_ci	if (IS_TRIE(node_parent_rcu(n)))
27618c2ecf20Sopenharmony_ci		fib_table_print(seq, iter->tb);
27628c2ecf20Sopenharmony_ci
27638c2ecf20Sopenharmony_ci	if (IS_TNODE(n)) {
27648c2ecf20Sopenharmony_ci		__be32 prf = htonl(n->key);
27658c2ecf20Sopenharmony_ci
27668c2ecf20Sopenharmony_ci		seq_indent(seq, iter->depth-1);
27678c2ecf20Sopenharmony_ci		seq_printf(seq, "  +-- %pI4/%zu %u %u %u\n",
27688c2ecf20Sopenharmony_ci			   &prf, KEYLENGTH - n->pos - n->bits, n->bits,
27698c2ecf20Sopenharmony_ci			   tn_info(n)->full_children,
27708c2ecf20Sopenharmony_ci			   tn_info(n)->empty_children);
27718c2ecf20Sopenharmony_ci	} else {
27728c2ecf20Sopenharmony_ci		__be32 val = htonl(n->key);
27738c2ecf20Sopenharmony_ci		struct fib_alias *fa;
27748c2ecf20Sopenharmony_ci
27758c2ecf20Sopenharmony_ci		seq_indent(seq, iter->depth);
27768c2ecf20Sopenharmony_ci		seq_printf(seq, "  |-- %pI4\n", &val);
27778c2ecf20Sopenharmony_ci
27788c2ecf20Sopenharmony_ci		hlist_for_each_entry_rcu(fa, &n->leaf, fa_list) {
27798c2ecf20Sopenharmony_ci			char buf1[32], buf2[32];
27808c2ecf20Sopenharmony_ci
27818c2ecf20Sopenharmony_ci			seq_indent(seq, iter->depth + 1);
27828c2ecf20Sopenharmony_ci			seq_printf(seq, "  /%zu %s %s",
27838c2ecf20Sopenharmony_ci				   KEYLENGTH - fa->fa_slen,
27848c2ecf20Sopenharmony_ci				   rtn_scope(buf1, sizeof(buf1),
27858c2ecf20Sopenharmony_ci					     fa->fa_info->fib_scope),
27868c2ecf20Sopenharmony_ci				   rtn_type(buf2, sizeof(buf2),
27878c2ecf20Sopenharmony_ci					    fa->fa_type));
27888c2ecf20Sopenharmony_ci			if (fa->fa_tos)
27898c2ecf20Sopenharmony_ci				seq_printf(seq, " tos=%d", fa->fa_tos);
27908c2ecf20Sopenharmony_ci			seq_putc(seq, '\n');
27918c2ecf20Sopenharmony_ci		}
27928c2ecf20Sopenharmony_ci	}
27938c2ecf20Sopenharmony_ci
27948c2ecf20Sopenharmony_ci	return 0;
27958c2ecf20Sopenharmony_ci}
27968c2ecf20Sopenharmony_ci
27978c2ecf20Sopenharmony_cistatic const struct seq_operations fib_trie_seq_ops = {
27988c2ecf20Sopenharmony_ci	.start  = fib_trie_seq_start,
27998c2ecf20Sopenharmony_ci	.next   = fib_trie_seq_next,
28008c2ecf20Sopenharmony_ci	.stop   = fib_trie_seq_stop,
28018c2ecf20Sopenharmony_ci	.show   = fib_trie_seq_show,
28028c2ecf20Sopenharmony_ci};
28038c2ecf20Sopenharmony_ci
28048c2ecf20Sopenharmony_cistruct fib_route_iter {
28058c2ecf20Sopenharmony_ci	struct seq_net_private p;
28068c2ecf20Sopenharmony_ci	struct fib_table *main_tb;
28078c2ecf20Sopenharmony_ci	struct key_vector *tnode;
28088c2ecf20Sopenharmony_ci	loff_t	pos;
28098c2ecf20Sopenharmony_ci	t_key	key;
28108c2ecf20Sopenharmony_ci};
28118c2ecf20Sopenharmony_ci
28128c2ecf20Sopenharmony_cistatic struct key_vector *fib_route_get_idx(struct fib_route_iter *iter,
28138c2ecf20Sopenharmony_ci					    loff_t pos)
28148c2ecf20Sopenharmony_ci{
28158c2ecf20Sopenharmony_ci	struct key_vector *l, **tp = &iter->tnode;
28168c2ecf20Sopenharmony_ci	t_key key;
28178c2ecf20Sopenharmony_ci
28188c2ecf20Sopenharmony_ci	/* use cached location of previously found key */
28198c2ecf20Sopenharmony_ci	if (iter->pos > 0 && pos >= iter->pos) {
28208c2ecf20Sopenharmony_ci		key = iter->key;
28218c2ecf20Sopenharmony_ci	} else {
28228c2ecf20Sopenharmony_ci		iter->pos = 1;
28238c2ecf20Sopenharmony_ci		key = 0;
28248c2ecf20Sopenharmony_ci	}
28258c2ecf20Sopenharmony_ci
28268c2ecf20Sopenharmony_ci	pos -= iter->pos;
28278c2ecf20Sopenharmony_ci
28288c2ecf20Sopenharmony_ci	while ((l = leaf_walk_rcu(tp, key)) && (pos-- > 0)) {
28298c2ecf20Sopenharmony_ci		key = l->key + 1;
28308c2ecf20Sopenharmony_ci		iter->pos++;
28318c2ecf20Sopenharmony_ci		l = NULL;
28328c2ecf20Sopenharmony_ci
28338c2ecf20Sopenharmony_ci		/* handle unlikely case of a key wrap */
28348c2ecf20Sopenharmony_ci		if (!key)
28358c2ecf20Sopenharmony_ci			break;
28368c2ecf20Sopenharmony_ci	}
28378c2ecf20Sopenharmony_ci
28388c2ecf20Sopenharmony_ci	if (l)
28398c2ecf20Sopenharmony_ci		iter->key = l->key;	/* remember it */
28408c2ecf20Sopenharmony_ci	else
28418c2ecf20Sopenharmony_ci		iter->pos = 0;		/* forget it */
28428c2ecf20Sopenharmony_ci
28438c2ecf20Sopenharmony_ci	return l;
28448c2ecf20Sopenharmony_ci}
28458c2ecf20Sopenharmony_ci
28468c2ecf20Sopenharmony_cistatic void *fib_route_seq_start(struct seq_file *seq, loff_t *pos)
28478c2ecf20Sopenharmony_ci	__acquires(RCU)
28488c2ecf20Sopenharmony_ci{
28498c2ecf20Sopenharmony_ci	struct fib_route_iter *iter = seq->private;
28508c2ecf20Sopenharmony_ci	struct fib_table *tb;
28518c2ecf20Sopenharmony_ci	struct trie *t;
28528c2ecf20Sopenharmony_ci
28538c2ecf20Sopenharmony_ci	rcu_read_lock();
28548c2ecf20Sopenharmony_ci
28558c2ecf20Sopenharmony_ci	tb = fib_get_table(seq_file_net(seq), RT_TABLE_MAIN);
28568c2ecf20Sopenharmony_ci	if (!tb)
28578c2ecf20Sopenharmony_ci		return NULL;
28588c2ecf20Sopenharmony_ci
28598c2ecf20Sopenharmony_ci	iter->main_tb = tb;
28608c2ecf20Sopenharmony_ci	t = (struct trie *)tb->tb_data;
28618c2ecf20Sopenharmony_ci	iter->tnode = t->kv;
28628c2ecf20Sopenharmony_ci
28638c2ecf20Sopenharmony_ci	if (*pos != 0)
28648c2ecf20Sopenharmony_ci		return fib_route_get_idx(iter, *pos);
28658c2ecf20Sopenharmony_ci
28668c2ecf20Sopenharmony_ci	iter->pos = 0;
28678c2ecf20Sopenharmony_ci	iter->key = KEY_MAX;
28688c2ecf20Sopenharmony_ci
28698c2ecf20Sopenharmony_ci	return SEQ_START_TOKEN;
28708c2ecf20Sopenharmony_ci}
28718c2ecf20Sopenharmony_ci
28728c2ecf20Sopenharmony_cistatic void *fib_route_seq_next(struct seq_file *seq, void *v, loff_t *pos)
28738c2ecf20Sopenharmony_ci{
28748c2ecf20Sopenharmony_ci	struct fib_route_iter *iter = seq->private;
28758c2ecf20Sopenharmony_ci	struct key_vector *l = NULL;
28768c2ecf20Sopenharmony_ci	t_key key = iter->key + 1;
28778c2ecf20Sopenharmony_ci
28788c2ecf20Sopenharmony_ci	++*pos;
28798c2ecf20Sopenharmony_ci
28808c2ecf20Sopenharmony_ci	/* only allow key of 0 for start of sequence */
28818c2ecf20Sopenharmony_ci	if ((v == SEQ_START_TOKEN) || key)
28828c2ecf20Sopenharmony_ci		l = leaf_walk_rcu(&iter->tnode, key);
28838c2ecf20Sopenharmony_ci
28848c2ecf20Sopenharmony_ci	if (l) {
28858c2ecf20Sopenharmony_ci		iter->key = l->key;
28868c2ecf20Sopenharmony_ci		iter->pos++;
28878c2ecf20Sopenharmony_ci	} else {
28888c2ecf20Sopenharmony_ci		iter->pos = 0;
28898c2ecf20Sopenharmony_ci	}
28908c2ecf20Sopenharmony_ci
28918c2ecf20Sopenharmony_ci	return l;
28928c2ecf20Sopenharmony_ci}
28938c2ecf20Sopenharmony_ci
28948c2ecf20Sopenharmony_cistatic void fib_route_seq_stop(struct seq_file *seq, void *v)
28958c2ecf20Sopenharmony_ci	__releases(RCU)
28968c2ecf20Sopenharmony_ci{
28978c2ecf20Sopenharmony_ci	rcu_read_unlock();
28988c2ecf20Sopenharmony_ci}
28998c2ecf20Sopenharmony_ci
29008c2ecf20Sopenharmony_cistatic unsigned int fib_flag_trans(int type, __be32 mask, struct fib_info *fi)
29018c2ecf20Sopenharmony_ci{
29028c2ecf20Sopenharmony_ci	unsigned int flags = 0;
29038c2ecf20Sopenharmony_ci
29048c2ecf20Sopenharmony_ci	if (type == RTN_UNREACHABLE || type == RTN_PROHIBIT)
29058c2ecf20Sopenharmony_ci		flags = RTF_REJECT;
29068c2ecf20Sopenharmony_ci	if (fi) {
29078c2ecf20Sopenharmony_ci		const struct fib_nh_common *nhc = fib_info_nhc(fi, 0);
29088c2ecf20Sopenharmony_ci
29098c2ecf20Sopenharmony_ci		if (nhc->nhc_gw.ipv4)
29108c2ecf20Sopenharmony_ci			flags |= RTF_GATEWAY;
29118c2ecf20Sopenharmony_ci	}
29128c2ecf20Sopenharmony_ci	if (mask == htonl(0xFFFFFFFF))
29138c2ecf20Sopenharmony_ci		flags |= RTF_HOST;
29148c2ecf20Sopenharmony_ci	flags |= RTF_UP;
29158c2ecf20Sopenharmony_ci	return flags;
29168c2ecf20Sopenharmony_ci}
29178c2ecf20Sopenharmony_ci
29188c2ecf20Sopenharmony_ci/*
29198c2ecf20Sopenharmony_ci *	This outputs /proc/net/route.
29208c2ecf20Sopenharmony_ci *	The format of the file is not supposed to be changed
29218c2ecf20Sopenharmony_ci *	and needs to be same as fib_hash output to avoid breaking
29228c2ecf20Sopenharmony_ci *	legacy utilities
29238c2ecf20Sopenharmony_ci */
29248c2ecf20Sopenharmony_cistatic int fib_route_seq_show(struct seq_file *seq, void *v)
29258c2ecf20Sopenharmony_ci{
29268c2ecf20Sopenharmony_ci	struct fib_route_iter *iter = seq->private;
29278c2ecf20Sopenharmony_ci	struct fib_table *tb = iter->main_tb;
29288c2ecf20Sopenharmony_ci	struct fib_alias *fa;
29298c2ecf20Sopenharmony_ci	struct key_vector *l = v;
29308c2ecf20Sopenharmony_ci	__be32 prefix;
29318c2ecf20Sopenharmony_ci
29328c2ecf20Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
29338c2ecf20Sopenharmony_ci		seq_printf(seq, "%-127s\n", "Iface\tDestination\tGateway "
29348c2ecf20Sopenharmony_ci			   "\tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU"
29358c2ecf20Sopenharmony_ci			   "\tWindow\tIRTT");
29368c2ecf20Sopenharmony_ci		return 0;
29378c2ecf20Sopenharmony_ci	}
29388c2ecf20Sopenharmony_ci
29398c2ecf20Sopenharmony_ci	prefix = htonl(l->key);
29408c2ecf20Sopenharmony_ci
29418c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) {
29428c2ecf20Sopenharmony_ci		struct fib_info *fi = fa->fa_info;
29438c2ecf20Sopenharmony_ci		__be32 mask = inet_make_mask(KEYLENGTH - fa->fa_slen);
29448c2ecf20Sopenharmony_ci		unsigned int flags = fib_flag_trans(fa->fa_type, mask, fi);
29458c2ecf20Sopenharmony_ci
29468c2ecf20Sopenharmony_ci		if ((fa->fa_type == RTN_BROADCAST) ||
29478c2ecf20Sopenharmony_ci		    (fa->fa_type == RTN_MULTICAST))
29488c2ecf20Sopenharmony_ci			continue;
29498c2ecf20Sopenharmony_ci
29508c2ecf20Sopenharmony_ci		if (fa->tb_id != tb->tb_id)
29518c2ecf20Sopenharmony_ci			continue;
29528c2ecf20Sopenharmony_ci
29538c2ecf20Sopenharmony_ci		seq_setwidth(seq, 127);
29548c2ecf20Sopenharmony_ci
29558c2ecf20Sopenharmony_ci		if (fi) {
29568c2ecf20Sopenharmony_ci			struct fib_nh_common *nhc = fib_info_nhc(fi, 0);
29578c2ecf20Sopenharmony_ci			__be32 gw = 0;
29588c2ecf20Sopenharmony_ci
29598c2ecf20Sopenharmony_ci			if (nhc->nhc_gw_family == AF_INET)
29608c2ecf20Sopenharmony_ci				gw = nhc->nhc_gw.ipv4;
29618c2ecf20Sopenharmony_ci
29628c2ecf20Sopenharmony_ci			seq_printf(seq,
29638c2ecf20Sopenharmony_ci				   "%s\t%08X\t%08X\t%04X\t%d\t%u\t"
29648c2ecf20Sopenharmony_ci				   "%d\t%08X\t%d\t%u\t%u",
29658c2ecf20Sopenharmony_ci				   nhc->nhc_dev ? nhc->nhc_dev->name : "*",
29668c2ecf20Sopenharmony_ci				   prefix, gw, flags, 0, 0,
29678c2ecf20Sopenharmony_ci				   fi->fib_priority,
29688c2ecf20Sopenharmony_ci				   mask,
29698c2ecf20Sopenharmony_ci				   (fi->fib_advmss ?
29708c2ecf20Sopenharmony_ci				    fi->fib_advmss + 40 : 0),
29718c2ecf20Sopenharmony_ci				   fi->fib_window,
29728c2ecf20Sopenharmony_ci				   fi->fib_rtt >> 3);
29738c2ecf20Sopenharmony_ci		} else {
29748c2ecf20Sopenharmony_ci			seq_printf(seq,
29758c2ecf20Sopenharmony_ci				   "*\t%08X\t%08X\t%04X\t%d\t%u\t"
29768c2ecf20Sopenharmony_ci				   "%d\t%08X\t%d\t%u\t%u",
29778c2ecf20Sopenharmony_ci				   prefix, 0, flags, 0, 0, 0,
29788c2ecf20Sopenharmony_ci				   mask, 0, 0, 0);
29798c2ecf20Sopenharmony_ci		}
29808c2ecf20Sopenharmony_ci		seq_pad(seq, '\n');
29818c2ecf20Sopenharmony_ci	}
29828c2ecf20Sopenharmony_ci
29838c2ecf20Sopenharmony_ci	return 0;
29848c2ecf20Sopenharmony_ci}
29858c2ecf20Sopenharmony_ci
29868c2ecf20Sopenharmony_cistatic const struct seq_operations fib_route_seq_ops = {
29878c2ecf20Sopenharmony_ci	.start  = fib_route_seq_start,
29888c2ecf20Sopenharmony_ci	.next   = fib_route_seq_next,
29898c2ecf20Sopenharmony_ci	.stop   = fib_route_seq_stop,
29908c2ecf20Sopenharmony_ci	.show   = fib_route_seq_show,
29918c2ecf20Sopenharmony_ci};
29928c2ecf20Sopenharmony_ci
29938c2ecf20Sopenharmony_ciint __net_init fib_proc_init(struct net *net)
29948c2ecf20Sopenharmony_ci{
29958c2ecf20Sopenharmony_ci	if (!proc_create_net("fib_trie", 0444, net->proc_net, &fib_trie_seq_ops,
29968c2ecf20Sopenharmony_ci			sizeof(struct fib_trie_iter)))
29978c2ecf20Sopenharmony_ci		goto out1;
29988c2ecf20Sopenharmony_ci
29998c2ecf20Sopenharmony_ci	if (!proc_create_net_single("fib_triestat", 0444, net->proc_net,
30008c2ecf20Sopenharmony_ci			fib_triestat_seq_show, NULL))
30018c2ecf20Sopenharmony_ci		goto out2;
30028c2ecf20Sopenharmony_ci
30038c2ecf20Sopenharmony_ci	if (!proc_create_net("route", 0444, net->proc_net, &fib_route_seq_ops,
30048c2ecf20Sopenharmony_ci			sizeof(struct fib_route_iter)))
30058c2ecf20Sopenharmony_ci		goto out3;
30068c2ecf20Sopenharmony_ci
30078c2ecf20Sopenharmony_ci	return 0;
30088c2ecf20Sopenharmony_ci
30098c2ecf20Sopenharmony_ciout3:
30108c2ecf20Sopenharmony_ci	remove_proc_entry("fib_triestat", net->proc_net);
30118c2ecf20Sopenharmony_ciout2:
30128c2ecf20Sopenharmony_ci	remove_proc_entry("fib_trie", net->proc_net);
30138c2ecf20Sopenharmony_ciout1:
30148c2ecf20Sopenharmony_ci	return -ENOMEM;
30158c2ecf20Sopenharmony_ci}
30168c2ecf20Sopenharmony_ci
30178c2ecf20Sopenharmony_civoid __net_exit fib_proc_exit(struct net *net)
30188c2ecf20Sopenharmony_ci{
30198c2ecf20Sopenharmony_ci	remove_proc_entry("fib_trie", net->proc_net);
30208c2ecf20Sopenharmony_ci	remove_proc_entry("fib_triestat", net->proc_net);
30218c2ecf20Sopenharmony_ci	remove_proc_entry("route", net->proc_net);
30228c2ecf20Sopenharmony_ci}
30238c2ecf20Sopenharmony_ci
30248c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */
3025