xref: /openbmc/linux/net/mac80211/mesh_pathtbl.c (revision 4c5ade41)
1eb2b9311SLuis Carlos Cobo /*
2264d9b7dSRui Paulo  * Copyright (c) 2008, 2009 open80211s Ltd.
3eb2b9311SLuis Carlos Cobo  * Author:     Luis Carlos Cobo <luisca@cozybit.com>
4eb2b9311SLuis Carlos Cobo  *
5eb2b9311SLuis Carlos Cobo  * This program is free software; you can redistribute it and/or modify
6eb2b9311SLuis Carlos Cobo  * it under the terms of the GNU General Public License version 2 as
7eb2b9311SLuis Carlos Cobo  * published by the Free Software Foundation.
8eb2b9311SLuis Carlos Cobo  */
9eb2b9311SLuis Carlos Cobo 
10eb2b9311SLuis Carlos Cobo #include <linux/etherdevice.h>
11eb2b9311SLuis Carlos Cobo #include <linux/list.h>
12eb2b9311SLuis Carlos Cobo #include <linux/random.h>
135a0e3ad6STejun Heo #include <linux/slab.h>
14eb2b9311SLuis Carlos Cobo #include <linux/spinlock.h>
15eb2b9311SLuis Carlos Cobo #include <linux/string.h>
16eb2b9311SLuis Carlos Cobo #include <net/mac80211.h>
17eb2b9311SLuis Carlos Cobo #include "ieee80211_i.h"
18eb2b9311SLuis Carlos Cobo #include "mesh.h"
19eb2b9311SLuis Carlos Cobo 
207646887aSJavier Cardona #ifdef CONFIG_MAC80211_VERBOSE_MPATH_DEBUG
217646887aSJavier Cardona #define mpath_dbg(fmt, args...)	printk(KERN_DEBUG fmt, ##args)
227646887aSJavier Cardona #else
237646887aSJavier Cardona #define mpath_dbg(fmt, args...)	do { (void)(0); } while (0)
247646887aSJavier Cardona #endif
257646887aSJavier Cardona 
26eb2b9311SLuis Carlos Cobo /* There will be initially 2^INIT_PATHS_SIZE_ORDER buckets */
27eb2b9311SLuis Carlos Cobo #define INIT_PATHS_SIZE_ORDER	2
28eb2b9311SLuis Carlos Cobo 
29eb2b9311SLuis Carlos Cobo /* Keep the mean chain length below this constant */
30eb2b9311SLuis Carlos Cobo #define MEAN_CHAIN_LEN		2
31eb2b9311SLuis Carlos Cobo 
32eb2b9311SLuis Carlos Cobo #define MPATH_EXPIRED(mpath) ((mpath->flags & MESH_PATH_ACTIVE) && \
33eb2b9311SLuis Carlos Cobo 				time_after(jiffies, mpath->exp_time) && \
34eb2b9311SLuis Carlos Cobo 				!(mpath->flags & MESH_PATH_FIXED))
35eb2b9311SLuis Carlos Cobo 
36eb2b9311SLuis Carlos Cobo struct mpath_node {
37eb2b9311SLuis Carlos Cobo 	struct hlist_node list;
38eb2b9311SLuis Carlos Cobo 	struct rcu_head rcu;
39eb2b9311SLuis Carlos Cobo 	/* This indirection allows two different tables to point to the same
40eb2b9311SLuis Carlos Cobo 	 * mesh_path structure, useful when resizing
41eb2b9311SLuis Carlos Cobo 	 */
42eb2b9311SLuis Carlos Cobo 	struct mesh_path *mpath;
43eb2b9311SLuis Carlos Cobo };
44eb2b9311SLuis Carlos Cobo 
45349eb8cfSJohannes Berg static struct mesh_table __rcu *mesh_paths;
46349eb8cfSJohannes Berg static struct mesh_table __rcu *mpp_paths; /* Store paths for MPP&MAP */
47eb2b9311SLuis Carlos Cobo 
48f5ea9120SJohannes Berg int mesh_paths_generation;
496b86bd62SJohannes Berg 
506b86bd62SJohannes Berg /* This lock will have the grow table function as writer and add / delete nodes
51239289e4SJavier Cardona  * as readers. RCU provides sufficient protection only when reading the table
52239289e4SJavier Cardona  * (i.e. doing lookups).  Adding or adding or removing nodes requires we take
53239289e4SJavier Cardona  * the read lock or we risk operating on an old table.  The write lock is only
54239289e4SJavier Cardona  * needed when modifying the number of buckets a table.
556b86bd62SJohannes Berg  */
566b86bd62SJohannes Berg static DEFINE_RWLOCK(pathtbl_resize_lock);
576b86bd62SJohannes Berg 
586b86bd62SJohannes Berg 
59349eb8cfSJohannes Berg static inline struct mesh_table *resize_dereference_mesh_paths(void)
60349eb8cfSJohannes Berg {
61349eb8cfSJohannes Berg 	return rcu_dereference_protected(mesh_paths,
62349eb8cfSJohannes Berg 		lockdep_is_held(&pathtbl_resize_lock));
63349eb8cfSJohannes Berg }
64349eb8cfSJohannes Berg 
65349eb8cfSJohannes Berg static inline struct mesh_table *resize_dereference_mpp_paths(void)
66349eb8cfSJohannes Berg {
67349eb8cfSJohannes Berg 	return rcu_dereference_protected(mpp_paths,
68349eb8cfSJohannes Berg 		lockdep_is_held(&pathtbl_resize_lock));
69349eb8cfSJohannes Berg }
70349eb8cfSJohannes Berg 
715ee68e5bSJavier Cardona static int mesh_gate_add(struct mesh_table *tbl, struct mesh_path *mpath);
725ee68e5bSJavier Cardona 
73349eb8cfSJohannes Berg /*
74349eb8cfSJohannes Berg  * CAREFUL -- "tbl" must not be an expression,
75349eb8cfSJohannes Berg  * in particular not an rcu_dereference(), since
76349eb8cfSJohannes Berg  * it's used twice. So it is illegal to do
77349eb8cfSJohannes Berg  *	for_each_mesh_entry(rcu_dereference(...), ...)
78349eb8cfSJohannes Berg  */
79349eb8cfSJohannes Berg #define for_each_mesh_entry(tbl, p, node, i) \
80349eb8cfSJohannes Berg 	for (i = 0; i <= tbl->hash_mask; i++) \
81349eb8cfSJohannes Berg 		hlist_for_each_entry_rcu(node, p, &tbl->hash_buckets[i], list)
82349eb8cfSJohannes Berg 
83349eb8cfSJohannes Berg 
846b86bd62SJohannes Berg static struct mesh_table *mesh_table_alloc(int size_order)
856b86bd62SJohannes Berg {
866b86bd62SJohannes Berg 	int i;
876b86bd62SJohannes Berg 	struct mesh_table *newtbl;
886b86bd62SJohannes Berg 
89d676ff49SJavier Cardona 	newtbl = kmalloc(sizeof(struct mesh_table), GFP_ATOMIC);
906b86bd62SJohannes Berg 	if (!newtbl)
916b86bd62SJohannes Berg 		return NULL;
926b86bd62SJohannes Berg 
936b86bd62SJohannes Berg 	newtbl->hash_buckets = kzalloc(sizeof(struct hlist_head) *
94d676ff49SJavier Cardona 			(1 << size_order), GFP_ATOMIC);
956b86bd62SJohannes Berg 
966b86bd62SJohannes Berg 	if (!newtbl->hash_buckets) {
976b86bd62SJohannes Berg 		kfree(newtbl);
986b86bd62SJohannes Berg 		return NULL;
996b86bd62SJohannes Berg 	}
1006b86bd62SJohannes Berg 
1016b86bd62SJohannes Berg 	newtbl->hashwlock = kmalloc(sizeof(spinlock_t) *
102d676ff49SJavier Cardona 			(1 << size_order), GFP_ATOMIC);
1036b86bd62SJohannes Berg 	if (!newtbl->hashwlock) {
1046b86bd62SJohannes Berg 		kfree(newtbl->hash_buckets);
1056b86bd62SJohannes Berg 		kfree(newtbl);
1066b86bd62SJohannes Berg 		return NULL;
1076b86bd62SJohannes Berg 	}
1086b86bd62SJohannes Berg 
1096b86bd62SJohannes Berg 	newtbl->size_order = size_order;
1106b86bd62SJohannes Berg 	newtbl->hash_mask = (1 << size_order) - 1;
1116b86bd62SJohannes Berg 	atomic_set(&newtbl->entries,  0);
1126b86bd62SJohannes Berg 	get_random_bytes(&newtbl->hash_rnd,
1136b86bd62SJohannes Berg 			sizeof(newtbl->hash_rnd));
1146b86bd62SJohannes Berg 	for (i = 0; i <= newtbl->hash_mask; i++)
1156b86bd62SJohannes Berg 		spin_lock_init(&newtbl->hashwlock[i]);
1165ee68e5bSJavier Cardona 	spin_lock_init(&newtbl->gates_lock);
1176b86bd62SJohannes Berg 
1186b86bd62SJohannes Berg 	return newtbl;
1196b86bd62SJohannes Berg }
1206b86bd62SJohannes Berg 
12118889231SJavier Cardona static void __mesh_table_free(struct mesh_table *tbl)
12218889231SJavier Cardona {
12318889231SJavier Cardona 	kfree(tbl->hash_buckets);
12418889231SJavier Cardona 	kfree(tbl->hashwlock);
12518889231SJavier Cardona 	kfree(tbl);
12618889231SJavier Cardona }
12718889231SJavier Cardona 
1286b86bd62SJohannes Berg static void mesh_table_free(struct mesh_table *tbl, bool free_leafs)
12918889231SJavier Cardona {
13018889231SJavier Cardona 	struct hlist_head *mesh_hash;
13118889231SJavier Cardona 	struct hlist_node *p, *q;
1325ee68e5bSJavier Cardona 	struct mpath_node *gate;
13318889231SJavier Cardona 	int i;
13418889231SJavier Cardona 
13518889231SJavier Cardona 	mesh_hash = tbl->hash_buckets;
13618889231SJavier Cardona 	for (i = 0; i <= tbl->hash_mask; i++) {
1379b84b808SJavier Cardona 		spin_lock_bh(&tbl->hashwlock[i]);
13818889231SJavier Cardona 		hlist_for_each_safe(p, q, &mesh_hash[i]) {
13918889231SJavier Cardona 			tbl->free_node(p, free_leafs);
14018889231SJavier Cardona 			atomic_dec(&tbl->entries);
14118889231SJavier Cardona 		}
1429b84b808SJavier Cardona 		spin_unlock_bh(&tbl->hashwlock[i]);
14318889231SJavier Cardona 	}
1445ee68e5bSJavier Cardona 	if (free_leafs) {
1455ee68e5bSJavier Cardona 		spin_lock_bh(&tbl->gates_lock);
1465ee68e5bSJavier Cardona 		hlist_for_each_entry_safe(gate, p, q,
1475ee68e5bSJavier Cardona 					 tbl->known_gates, list) {
1485ee68e5bSJavier Cardona 			hlist_del(&gate->list);
1495ee68e5bSJavier Cardona 			kfree(gate);
1505ee68e5bSJavier Cardona 		}
1515ee68e5bSJavier Cardona 		kfree(tbl->known_gates);
1525ee68e5bSJavier Cardona 		spin_unlock_bh(&tbl->gates_lock);
1535ee68e5bSJavier Cardona 	}
1545ee68e5bSJavier Cardona 
15518889231SJavier Cardona 	__mesh_table_free(tbl);
15618889231SJavier Cardona }
15718889231SJavier Cardona 
158a3e6b12cScozybit Inc static int mesh_table_grow(struct mesh_table *oldtbl,
159a3e6b12cScozybit Inc 			   struct mesh_table *newtbl)
16018889231SJavier Cardona {
16118889231SJavier Cardona 	struct hlist_head *oldhash;
16218889231SJavier Cardona 	struct hlist_node *p, *q;
16318889231SJavier Cardona 	int i;
16418889231SJavier Cardona 
165a3e6b12cScozybit Inc 	if (atomic_read(&oldtbl->entries)
166a3e6b12cScozybit Inc 			< oldtbl->mean_chain_len * (oldtbl->hash_mask + 1))
167a3e6b12cScozybit Inc 		return -EAGAIN;
16818889231SJavier Cardona 
169a3e6b12cScozybit Inc 	newtbl->free_node = oldtbl->free_node;
170a3e6b12cScozybit Inc 	newtbl->mean_chain_len = oldtbl->mean_chain_len;
171a3e6b12cScozybit Inc 	newtbl->copy_node = oldtbl->copy_node;
1725ee68e5bSJavier Cardona 	newtbl->known_gates = oldtbl->known_gates;
173a3e6b12cScozybit Inc 	atomic_set(&newtbl->entries, atomic_read(&oldtbl->entries));
17418889231SJavier Cardona 
175a3e6b12cScozybit Inc 	oldhash = oldtbl->hash_buckets;
176a3e6b12cScozybit Inc 	for (i = 0; i <= oldtbl->hash_mask; i++)
17718889231SJavier Cardona 		hlist_for_each(p, &oldhash[i])
178a3e6b12cScozybit Inc 			if (oldtbl->copy_node(p, newtbl) < 0)
17918889231SJavier Cardona 				goto errcopy;
18018889231SJavier Cardona 
181a3e6b12cScozybit Inc 	return 0;
18218889231SJavier Cardona 
18318889231SJavier Cardona errcopy:
18418889231SJavier Cardona 	for (i = 0; i <= newtbl->hash_mask; i++) {
18518889231SJavier Cardona 		hlist_for_each_safe(p, q, &newtbl->hash_buckets[i])
186a3e6b12cScozybit Inc 			oldtbl->free_node(p, 0);
18718889231SJavier Cardona 	}
188a3e6b12cScozybit Inc 	return -ENOMEM;
18918889231SJavier Cardona }
19018889231SJavier Cardona 
1916b86bd62SJohannes Berg static u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata,
1926b86bd62SJohannes Berg 			   struct mesh_table *tbl)
1936b86bd62SJohannes Berg {
1946b86bd62SJohannes Berg 	/* Use last four bytes of hw addr and interface index as hash index */
1956b86bd62SJohannes Berg 	return jhash_2words(*(u32 *)(addr+2), sdata->dev->ifindex, tbl->hash_rnd)
1966b86bd62SJohannes Berg 		& tbl->hash_mask;
1976b86bd62SJohannes Berg }
198f5ea9120SJohannes Berg 
199eb2b9311SLuis Carlos Cobo 
200eb2b9311SLuis Carlos Cobo /**
201eb2b9311SLuis Carlos Cobo  *
202eb2b9311SLuis Carlos Cobo  * mesh_path_assign_nexthop - update mesh path next hop
203eb2b9311SLuis Carlos Cobo  *
204eb2b9311SLuis Carlos Cobo  * @mpath: mesh path to update
205eb2b9311SLuis Carlos Cobo  * @sta: next hop to assign
206eb2b9311SLuis Carlos Cobo  *
207eb2b9311SLuis Carlos Cobo  * Locking: mpath->state_lock must be held when calling this function
208eb2b9311SLuis Carlos Cobo  */
209eb2b9311SLuis Carlos Cobo void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta)
210eb2b9311SLuis Carlos Cobo {
21110c836d7SJavier Cardona 	struct sk_buff *skb;
21210c836d7SJavier Cardona 	struct ieee80211_hdr *hdr;
21310c836d7SJavier Cardona 	struct sk_buff_head tmpq;
21410c836d7SJavier Cardona 	unsigned long flags;
21510c836d7SJavier Cardona 
216d0709a65SJohannes Berg 	rcu_assign_pointer(mpath->next_hop, sta);
21710c836d7SJavier Cardona 
21810c836d7SJavier Cardona 	__skb_queue_head_init(&tmpq);
21910c836d7SJavier Cardona 
22010c836d7SJavier Cardona 	spin_lock_irqsave(&mpath->frame_queue.lock, flags);
22110c836d7SJavier Cardona 
22210c836d7SJavier Cardona 	while ((skb = __skb_dequeue(&mpath->frame_queue)) != NULL) {
22310c836d7SJavier Cardona 		hdr = (struct ieee80211_hdr *) skb->data;
22410c836d7SJavier Cardona 		memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN);
22510c836d7SJavier Cardona 		__skb_queue_tail(&tmpq, skb);
22610c836d7SJavier Cardona 	}
22710c836d7SJavier Cardona 
22810c836d7SJavier Cardona 	skb_queue_splice(&tmpq, &mpath->frame_queue);
22910c836d7SJavier Cardona 	spin_unlock_irqrestore(&mpath->frame_queue.lock, flags);
230eb2b9311SLuis Carlos Cobo }
231eb2b9311SLuis Carlos Cobo 
2325ee68e5bSJavier Cardona static void prepare_for_gate(struct sk_buff *skb, char *dst_addr,
2335ee68e5bSJavier Cardona 			     struct mesh_path *gate_mpath)
2345ee68e5bSJavier Cardona {
2355ee68e5bSJavier Cardona 	struct ieee80211_hdr *hdr;
2365ee68e5bSJavier Cardona 	struct ieee80211s_hdr *mshdr;
2375ee68e5bSJavier Cardona 	int mesh_hdrlen, hdrlen;
2385ee68e5bSJavier Cardona 	char *next_hop;
2395ee68e5bSJavier Cardona 
2405ee68e5bSJavier Cardona 	hdr = (struct ieee80211_hdr *) skb->data;
2415ee68e5bSJavier Cardona 	hdrlen = ieee80211_hdrlen(hdr->frame_control);
2425ee68e5bSJavier Cardona 	mshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
2435ee68e5bSJavier Cardona 
2445ee68e5bSJavier Cardona 	if (!(mshdr->flags & MESH_FLAGS_AE)) {
2455ee68e5bSJavier Cardona 		/* size of the fixed part of the mesh header */
2465ee68e5bSJavier Cardona 		mesh_hdrlen = 6;
2475ee68e5bSJavier Cardona 
2485ee68e5bSJavier Cardona 		/* make room for the two extended addresses */
2495ee68e5bSJavier Cardona 		skb_push(skb, 2 * ETH_ALEN);
2505ee68e5bSJavier Cardona 		memmove(skb->data, hdr, hdrlen + mesh_hdrlen);
2515ee68e5bSJavier Cardona 
2525ee68e5bSJavier Cardona 		hdr = (struct ieee80211_hdr *) skb->data;
2535ee68e5bSJavier Cardona 
2545ee68e5bSJavier Cardona 		/* we preserve the previous mesh header and only add
2555ee68e5bSJavier Cardona 		 * the new addreses */
2565ee68e5bSJavier Cardona 		mshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
2575ee68e5bSJavier Cardona 		mshdr->flags = MESH_FLAGS_AE_A5_A6;
2585ee68e5bSJavier Cardona 		memcpy(mshdr->eaddr1, hdr->addr3, ETH_ALEN);
2595ee68e5bSJavier Cardona 		memcpy(mshdr->eaddr2, hdr->addr4, ETH_ALEN);
2605ee68e5bSJavier Cardona 	}
2615ee68e5bSJavier Cardona 
2625ee68e5bSJavier Cardona 	/* update next hop */
2635ee68e5bSJavier Cardona 	hdr = (struct ieee80211_hdr *) skb->data;
2645ee68e5bSJavier Cardona 	rcu_read_lock();
2655ee68e5bSJavier Cardona 	next_hop = rcu_dereference(gate_mpath->next_hop)->sta.addr;
2665ee68e5bSJavier Cardona 	memcpy(hdr->addr1, next_hop, ETH_ALEN);
2675ee68e5bSJavier Cardona 	rcu_read_unlock();
2685ee68e5bSJavier Cardona 	memcpy(hdr->addr3, dst_addr, ETH_ALEN);
2695ee68e5bSJavier Cardona }
2705ee68e5bSJavier Cardona 
2715ee68e5bSJavier Cardona /**
2725ee68e5bSJavier Cardona  *
2735ee68e5bSJavier Cardona  * mesh_path_move_to_queue - Move or copy frames from one mpath queue to another
2745ee68e5bSJavier Cardona  *
2755ee68e5bSJavier Cardona  * This function is used to transfer or copy frames from an unresolved mpath to
2765ee68e5bSJavier Cardona  * a gate mpath.  The function also adds the Address Extension field and
2775ee68e5bSJavier Cardona  * updates the next hop.
2785ee68e5bSJavier Cardona  *
2795ee68e5bSJavier Cardona  * If a frame already has an Address Extension field, only the next hop and
2805ee68e5bSJavier Cardona  * destination addresses are updated.
2815ee68e5bSJavier Cardona  *
2825ee68e5bSJavier Cardona  * The gate mpath must be an active mpath with a valid mpath->next_hop.
2835ee68e5bSJavier Cardona  *
2845ee68e5bSJavier Cardona  * @mpath: An active mpath the frames will be sent to (i.e. the gate)
2855ee68e5bSJavier Cardona  * @from_mpath: The failed mpath
2865ee68e5bSJavier Cardona  * @copy: When true, copy all the frames to the new mpath queue.  When false,
2875ee68e5bSJavier Cardona  * move them.
2885ee68e5bSJavier Cardona  */
2895ee68e5bSJavier Cardona static void mesh_path_move_to_queue(struct mesh_path *gate_mpath,
2905ee68e5bSJavier Cardona 				    struct mesh_path *from_mpath,
2915ee68e5bSJavier Cardona 				    bool copy)
2925ee68e5bSJavier Cardona {
293c6133661SThomas Pedersen 	struct sk_buff *skb, *cp_skb = NULL;
2945ee68e5bSJavier Cardona 	struct sk_buff_head gateq, failq;
2955ee68e5bSJavier Cardona 	unsigned long flags;
2965ee68e5bSJavier Cardona 	int num_skbs;
2975ee68e5bSJavier Cardona 
2985ee68e5bSJavier Cardona 	BUG_ON(gate_mpath == from_mpath);
2995ee68e5bSJavier Cardona 	BUG_ON(!gate_mpath->next_hop);
3005ee68e5bSJavier Cardona 
3015ee68e5bSJavier Cardona 	__skb_queue_head_init(&gateq);
3025ee68e5bSJavier Cardona 	__skb_queue_head_init(&failq);
3035ee68e5bSJavier Cardona 
3045ee68e5bSJavier Cardona 	spin_lock_irqsave(&from_mpath->frame_queue.lock, flags);
3055ee68e5bSJavier Cardona 	skb_queue_splice_init(&from_mpath->frame_queue, &failq);
3065ee68e5bSJavier Cardona 	spin_unlock_irqrestore(&from_mpath->frame_queue.lock, flags);
3075ee68e5bSJavier Cardona 
3085ee68e5bSJavier Cardona 	num_skbs = skb_queue_len(&failq);
3095ee68e5bSJavier Cardona 
3105ee68e5bSJavier Cardona 	while (num_skbs--) {
3115ee68e5bSJavier Cardona 		skb = __skb_dequeue(&failq);
312817a53d9SJohn W. Linville 		if (copy) {
3135ee68e5bSJavier Cardona 			cp_skb = skb_copy(skb, GFP_ATOMIC);
314817a53d9SJohn W. Linville 			if (cp_skb)
315817a53d9SJohn W. Linville 				__skb_queue_tail(&failq, cp_skb);
316817a53d9SJohn W. Linville 		}
3175ee68e5bSJavier Cardona 
3185ee68e5bSJavier Cardona 		prepare_for_gate(skb, gate_mpath->dst, gate_mpath);
3195ee68e5bSJavier Cardona 		__skb_queue_tail(&gateq, skb);
3205ee68e5bSJavier Cardona 	}
3215ee68e5bSJavier Cardona 
3225ee68e5bSJavier Cardona 	spin_lock_irqsave(&gate_mpath->frame_queue.lock, flags);
3235ee68e5bSJavier Cardona 	skb_queue_splice(&gateq, &gate_mpath->frame_queue);
3245ee68e5bSJavier Cardona 	mpath_dbg("Mpath queue for gate %pM has %d frames\n",
3255ee68e5bSJavier Cardona 			gate_mpath->dst,
3265ee68e5bSJavier Cardona 			skb_queue_len(&gate_mpath->frame_queue));
3275ee68e5bSJavier Cardona 	spin_unlock_irqrestore(&gate_mpath->frame_queue.lock, flags);
3285ee68e5bSJavier Cardona 
3295ee68e5bSJavier Cardona 	if (!copy)
3305ee68e5bSJavier Cardona 		return;
3315ee68e5bSJavier Cardona 
3325ee68e5bSJavier Cardona 	spin_lock_irqsave(&from_mpath->frame_queue.lock, flags);
3335ee68e5bSJavier Cardona 	skb_queue_splice(&failq, &from_mpath->frame_queue);
3345ee68e5bSJavier Cardona 	spin_unlock_irqrestore(&from_mpath->frame_queue.lock, flags);
3355ee68e5bSJavier Cardona }
3365ee68e5bSJavier Cardona 
337eb2b9311SLuis Carlos Cobo 
338239289e4SJavier Cardona static struct mesh_path *path_lookup(struct mesh_table *tbl, u8 *dst,
339239289e4SJavier Cardona 					  struct ieee80211_sub_if_data *sdata)
340239289e4SJavier Cardona {
341239289e4SJavier Cardona 	struct mesh_path *mpath;
342239289e4SJavier Cardona 	struct hlist_node *n;
343239289e4SJavier Cardona 	struct hlist_head *bucket;
344239289e4SJavier Cardona 	struct mpath_node *node;
345239289e4SJavier Cardona 
346239289e4SJavier Cardona 	bucket = &tbl->hash_buckets[mesh_table_hash(dst, sdata, tbl)];
347239289e4SJavier Cardona 	hlist_for_each_entry_rcu(node, n, bucket, list) {
348239289e4SJavier Cardona 		mpath = node->mpath;
349239289e4SJavier Cardona 		if (mpath->sdata == sdata &&
350239289e4SJavier Cardona 				memcmp(dst, mpath->dst, ETH_ALEN) == 0) {
351239289e4SJavier Cardona 			if (MPATH_EXPIRED(mpath)) {
352239289e4SJavier Cardona 				spin_lock_bh(&mpath->state_lock);
353239289e4SJavier Cardona 				mpath->flags &= ~MESH_PATH_ACTIVE;
354239289e4SJavier Cardona 				spin_unlock_bh(&mpath->state_lock);
355239289e4SJavier Cardona 			}
356239289e4SJavier Cardona 			return mpath;
357239289e4SJavier Cardona 		}
358239289e4SJavier Cardona 	}
359239289e4SJavier Cardona 	return NULL;
360239289e4SJavier Cardona }
361239289e4SJavier Cardona 
362eb2b9311SLuis Carlos Cobo /**
363eb2b9311SLuis Carlos Cobo  * mesh_path_lookup - look up a path in the mesh path table
364eb2b9311SLuis Carlos Cobo  * @dst: hardware address (ETH_ALEN length) of destination
365f698d856SJasper Bryant-Greene  * @sdata: local subif
366eb2b9311SLuis Carlos Cobo  *
367eb2b9311SLuis Carlos Cobo  * Returns: pointer to the mesh path structure, or NULL if not found
368eb2b9311SLuis Carlos Cobo  *
369eb2b9311SLuis Carlos Cobo  * Locking: must be called within a read rcu section.
370eb2b9311SLuis Carlos Cobo  */
371f698d856SJasper Bryant-Greene struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata)
372eb2b9311SLuis Carlos Cobo {
373239289e4SJavier Cardona 	return path_lookup(rcu_dereference(mesh_paths), dst, sdata);
374eb2b9311SLuis Carlos Cobo }
375eb2b9311SLuis Carlos Cobo 
37679617deeSYanBo struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata)
37779617deeSYanBo {
378239289e4SJavier Cardona 	return path_lookup(rcu_dereference(mpp_paths), dst, sdata);
37979617deeSYanBo }
38079617deeSYanBo 
38179617deeSYanBo 
382eb2b9311SLuis Carlos Cobo /**
383eb2b9311SLuis Carlos Cobo  * mesh_path_lookup_by_idx - look up a path in the mesh path table by its index
384eb2b9311SLuis Carlos Cobo  * @idx: index
385f698d856SJasper Bryant-Greene  * @sdata: local subif, or NULL for all entries
386eb2b9311SLuis Carlos Cobo  *
387eb2b9311SLuis Carlos Cobo  * Returns: pointer to the mesh path structure, or NULL if not found.
388eb2b9311SLuis Carlos Cobo  *
389eb2b9311SLuis Carlos Cobo  * Locking: must be called within a read rcu section.
390eb2b9311SLuis Carlos Cobo  */
391f698d856SJasper Bryant-Greene struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data *sdata)
392eb2b9311SLuis Carlos Cobo {
393349eb8cfSJohannes Berg 	struct mesh_table *tbl = rcu_dereference(mesh_paths);
394eb2b9311SLuis Carlos Cobo 	struct mpath_node *node;
395eb2b9311SLuis Carlos Cobo 	struct hlist_node *p;
396eb2b9311SLuis Carlos Cobo 	int i;
397eb2b9311SLuis Carlos Cobo 	int j = 0;
398eb2b9311SLuis Carlos Cobo 
399349eb8cfSJohannes Berg 	for_each_mesh_entry(tbl, p, node, i) {
400f698d856SJasper Bryant-Greene 		if (sdata && node->mpath->sdata != sdata)
4012a8ca29aSLuis Carlos Cobo 			continue;
402eb2b9311SLuis Carlos Cobo 		if (j++ == idx) {
403eb2b9311SLuis Carlos Cobo 			if (MPATH_EXPIRED(node->mpath)) {
404eb2b9311SLuis Carlos Cobo 				spin_lock_bh(&node->mpath->state_lock);
405eb2b9311SLuis Carlos Cobo 				node->mpath->flags &= ~MESH_PATH_ACTIVE;
406eb2b9311SLuis Carlos Cobo 				spin_unlock_bh(&node->mpath->state_lock);
407eb2b9311SLuis Carlos Cobo 			}
408eb2b9311SLuis Carlos Cobo 			return node->mpath;
409eb2b9311SLuis Carlos Cobo 		}
4102a8ca29aSLuis Carlos Cobo 	}
411eb2b9311SLuis Carlos Cobo 
412eb2b9311SLuis Carlos Cobo 	return NULL;
413eb2b9311SLuis Carlos Cobo }
414eb2b9311SLuis Carlos Cobo 
4155ee68e5bSJavier Cardona static void mesh_gate_node_reclaim(struct rcu_head *rp)
4165ee68e5bSJavier Cardona {
4175ee68e5bSJavier Cardona 	struct mpath_node *node = container_of(rp, struct mpath_node, rcu);
4185ee68e5bSJavier Cardona 	kfree(node);
4195ee68e5bSJavier Cardona }
4205ee68e5bSJavier Cardona 
4215ee68e5bSJavier Cardona /**
4225ee68e5bSJavier Cardona  * mesh_gate_add - mark mpath as path to a mesh gate and add to known_gates
4235ee68e5bSJavier Cardona  * @mesh_tbl: table which contains known_gates list
4245ee68e5bSJavier Cardona  * @mpath: mpath to known mesh gate
4255ee68e5bSJavier Cardona  *
4265ee68e5bSJavier Cardona  * Returns: 0 on success
4275ee68e5bSJavier Cardona  *
4285ee68e5bSJavier Cardona  */
4295ee68e5bSJavier Cardona static int mesh_gate_add(struct mesh_table *tbl, struct mesh_path *mpath)
4305ee68e5bSJavier Cardona {
4315ee68e5bSJavier Cardona 	struct mpath_node *gate, *new_gate;
4325ee68e5bSJavier Cardona 	struct hlist_node *n;
4335ee68e5bSJavier Cardona 	int err;
4345ee68e5bSJavier Cardona 
4355ee68e5bSJavier Cardona 	rcu_read_lock();
4365ee68e5bSJavier Cardona 	tbl = rcu_dereference(tbl);
4375ee68e5bSJavier Cardona 
4385ee68e5bSJavier Cardona 	hlist_for_each_entry_rcu(gate, n, tbl->known_gates, list)
4395ee68e5bSJavier Cardona 		if (gate->mpath == mpath) {
4405ee68e5bSJavier Cardona 			err = -EEXIST;
4415ee68e5bSJavier Cardona 			goto err_rcu;
4425ee68e5bSJavier Cardona 		}
4435ee68e5bSJavier Cardona 
4445ee68e5bSJavier Cardona 	new_gate = kzalloc(sizeof(struct mpath_node), GFP_ATOMIC);
4455ee68e5bSJavier Cardona 	if (!new_gate) {
4465ee68e5bSJavier Cardona 		err = -ENOMEM;
4475ee68e5bSJavier Cardona 		goto err_rcu;
4485ee68e5bSJavier Cardona 	}
4495ee68e5bSJavier Cardona 
4505ee68e5bSJavier Cardona 	mpath->is_gate = true;
4515ee68e5bSJavier Cardona 	mpath->sdata->u.mesh.num_gates++;
4525ee68e5bSJavier Cardona 	new_gate->mpath = mpath;
4535ee68e5bSJavier Cardona 	spin_lock_bh(&tbl->gates_lock);
4545ee68e5bSJavier Cardona 	hlist_add_head_rcu(&new_gate->list, tbl->known_gates);
4555ee68e5bSJavier Cardona 	spin_unlock_bh(&tbl->gates_lock);
4565ee68e5bSJavier Cardona 	rcu_read_unlock();
4575ee68e5bSJavier Cardona 	mpath_dbg("Mesh path (%s): Recorded new gate: %pM. %d known gates\n",
4585ee68e5bSJavier Cardona 		  mpath->sdata->name, mpath->dst,
4595ee68e5bSJavier Cardona 		  mpath->sdata->u.mesh.num_gates);
4605ee68e5bSJavier Cardona 	return 0;
4615ee68e5bSJavier Cardona err_rcu:
4625ee68e5bSJavier Cardona 	rcu_read_unlock();
4635ee68e5bSJavier Cardona 	return err;
4645ee68e5bSJavier Cardona }
4655ee68e5bSJavier Cardona 
4665ee68e5bSJavier Cardona /**
4675ee68e5bSJavier Cardona  * mesh_gate_del - remove a mesh gate from the list of known gates
4685ee68e5bSJavier Cardona  * @tbl: table which holds our list of known gates
4695ee68e5bSJavier Cardona  * @mpath: gate mpath
4705ee68e5bSJavier Cardona  *
4715ee68e5bSJavier Cardona  * Returns: 0 on success
4725ee68e5bSJavier Cardona  *
4735ee68e5bSJavier Cardona  * Locking: must be called inside rcu_read_lock() section
4745ee68e5bSJavier Cardona  */
4755ee68e5bSJavier Cardona static int mesh_gate_del(struct mesh_table *tbl, struct mesh_path *mpath)
4765ee68e5bSJavier Cardona {
4775ee68e5bSJavier Cardona 	struct mpath_node *gate;
4785ee68e5bSJavier Cardona 	struct hlist_node *p, *q;
4795ee68e5bSJavier Cardona 
4805ee68e5bSJavier Cardona 	tbl = rcu_dereference(tbl);
4815ee68e5bSJavier Cardona 
4825ee68e5bSJavier Cardona 	hlist_for_each_entry_safe(gate, p, q, tbl->known_gates, list)
4835ee68e5bSJavier Cardona 		if (gate->mpath == mpath) {
4845ee68e5bSJavier Cardona 			spin_lock_bh(&tbl->gates_lock);
4855ee68e5bSJavier Cardona 			hlist_del_rcu(&gate->list);
4865ee68e5bSJavier Cardona 			call_rcu(&gate->rcu, mesh_gate_node_reclaim);
4875ee68e5bSJavier Cardona 			spin_unlock_bh(&tbl->gates_lock);
4885ee68e5bSJavier Cardona 			mpath->sdata->u.mesh.num_gates--;
4895ee68e5bSJavier Cardona 			mpath->is_gate = false;
4905ee68e5bSJavier Cardona 			mpath_dbg("Mesh path (%s): Deleted gate: %pM. "
4915ee68e5bSJavier Cardona 				  "%d known gates\n", mpath->sdata->name,
4925ee68e5bSJavier Cardona 				  mpath->dst, mpath->sdata->u.mesh.num_gates);
4935ee68e5bSJavier Cardona 			break;
4945ee68e5bSJavier Cardona 		}
4955ee68e5bSJavier Cardona 
4965ee68e5bSJavier Cardona 	return 0;
4975ee68e5bSJavier Cardona }
4985ee68e5bSJavier Cardona 
4995ee68e5bSJavier Cardona /**
5005ee68e5bSJavier Cardona  *
5015ee68e5bSJavier Cardona  * mesh_path_add_gate - add the given mpath to a mesh gate to our path table
5025ee68e5bSJavier Cardona  * @mpath: gate path to add to table
5035ee68e5bSJavier Cardona  */
5045ee68e5bSJavier Cardona int mesh_path_add_gate(struct mesh_path *mpath)
5055ee68e5bSJavier Cardona {
5065ee68e5bSJavier Cardona 	return mesh_gate_add(mesh_paths, mpath);
5075ee68e5bSJavier Cardona }
5085ee68e5bSJavier Cardona 
5095ee68e5bSJavier Cardona /**
5105ee68e5bSJavier Cardona  * mesh_gate_num - number of gates known to this interface
5115ee68e5bSJavier Cardona  * @sdata: subif data
5125ee68e5bSJavier Cardona  */
5135ee68e5bSJavier Cardona int mesh_gate_num(struct ieee80211_sub_if_data *sdata)
5145ee68e5bSJavier Cardona {
5155ee68e5bSJavier Cardona 	return sdata->u.mesh.num_gates;
5165ee68e5bSJavier Cardona }
5175ee68e5bSJavier Cardona 
518eb2b9311SLuis Carlos Cobo /**
519eb2b9311SLuis Carlos Cobo  * mesh_path_add - allocate and add a new path to the mesh path table
520eb2b9311SLuis Carlos Cobo  * @addr: destination address of the path (ETH_ALEN length)
521f698d856SJasper Bryant-Greene  * @sdata: local subif
522eb2b9311SLuis Carlos Cobo  *
523af901ca1SAndré Goddard Rosa  * Returns: 0 on success
524eb2b9311SLuis Carlos Cobo  *
525eb2b9311SLuis Carlos Cobo  * State: the initial state of the new path is set to 0
526eb2b9311SLuis Carlos Cobo  */
527f698d856SJasper Bryant-Greene int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
528eb2b9311SLuis Carlos Cobo {
52918889231SJavier Cardona 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
53018889231SJavier Cardona 	struct ieee80211_local *local = sdata->local;
531349eb8cfSJohannes Berg 	struct mesh_table *tbl;
532eb2b9311SLuis Carlos Cobo 	struct mesh_path *mpath, *new_mpath;
533eb2b9311SLuis Carlos Cobo 	struct mpath_node *node, *new_node;
534eb2b9311SLuis Carlos Cobo 	struct hlist_head *bucket;
535eb2b9311SLuis Carlos Cobo 	struct hlist_node *n;
536eb2b9311SLuis Carlos Cobo 	int grow = 0;
537eb2b9311SLuis Carlos Cobo 	int err = 0;
538eb2b9311SLuis Carlos Cobo 	u32 hash_idx;
539eb2b9311SLuis Carlos Cobo 
54047846c9bSJohannes Berg 	if (memcmp(dst, sdata->vif.addr, ETH_ALEN) == 0)
541eb2b9311SLuis Carlos Cobo 		/* never add ourselves as neighbours */
542eb2b9311SLuis Carlos Cobo 		return -ENOTSUPP;
543eb2b9311SLuis Carlos Cobo 
544eb2b9311SLuis Carlos Cobo 	if (is_multicast_ether_addr(dst))
545eb2b9311SLuis Carlos Cobo 		return -ENOTSUPP;
546eb2b9311SLuis Carlos Cobo 
547472dbc45SJohannes Berg 	if (atomic_add_unless(&sdata->u.mesh.mpaths, 1, MESH_MAX_MPATHS) == 0)
548eb2b9311SLuis Carlos Cobo 		return -ENOSPC;
549eb2b9311SLuis Carlos Cobo 
550402d7752SPavel Emelyanov 	err = -ENOMEM;
55118889231SJavier Cardona 	new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC);
552402d7752SPavel Emelyanov 	if (!new_mpath)
553402d7752SPavel Emelyanov 		goto err_path_alloc;
554402d7752SPavel Emelyanov 
55518889231SJavier Cardona 	new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC);
556402d7752SPavel Emelyanov 	if (!new_node)
557402d7752SPavel Emelyanov 		goto err_node_alloc;
558f84e71a9SPavel Emelyanov 
5599b84b808SJavier Cardona 	read_lock_bh(&pathtbl_resize_lock);
560eb2b9311SLuis Carlos Cobo 	memcpy(new_mpath->dst, dst, ETH_ALEN);
561f698d856SJasper Bryant-Greene 	new_mpath->sdata = sdata;
562eb2b9311SLuis Carlos Cobo 	new_mpath->flags = 0;
563eb2b9311SLuis Carlos Cobo 	skb_queue_head_init(&new_mpath->frame_queue);
564eb2b9311SLuis Carlos Cobo 	new_node->mpath = new_mpath;
565eb2b9311SLuis Carlos Cobo 	new_mpath->timer.data = (unsigned long) new_mpath;
566eb2b9311SLuis Carlos Cobo 	new_mpath->timer.function = mesh_path_timer;
567eb2b9311SLuis Carlos Cobo 	new_mpath->exp_time = jiffies;
568eb2b9311SLuis Carlos Cobo 	spin_lock_init(&new_mpath->state_lock);
569eb2b9311SLuis Carlos Cobo 	init_timer(&new_mpath->timer);
570eb2b9311SLuis Carlos Cobo 
571349eb8cfSJohannes Berg 	tbl = resize_dereference_mesh_paths();
572eb2b9311SLuis Carlos Cobo 
573349eb8cfSJohannes Berg 	hash_idx = mesh_table_hash(dst, sdata, tbl);
574349eb8cfSJohannes Berg 	bucket = &tbl->hash_buckets[hash_idx];
575349eb8cfSJohannes Berg 
576349eb8cfSJohannes Berg 	spin_lock_bh(&tbl->hashwlock[hash_idx]);
577eb2b9311SLuis Carlos Cobo 
578402d7752SPavel Emelyanov 	err = -EEXIST;
579eb2b9311SLuis Carlos Cobo 	hlist_for_each_entry(node, n, bucket, list) {
580eb2b9311SLuis Carlos Cobo 		mpath = node->mpath;
581f698d856SJasper Bryant-Greene 		if (mpath->sdata == sdata && memcmp(dst, mpath->dst, ETH_ALEN) == 0)
582402d7752SPavel Emelyanov 			goto err_exists;
583eb2b9311SLuis Carlos Cobo 	}
584eb2b9311SLuis Carlos Cobo 
585eb2b9311SLuis Carlos Cobo 	hlist_add_head_rcu(&new_node->list, bucket);
586349eb8cfSJohannes Berg 	if (atomic_inc_return(&tbl->entries) >=
587349eb8cfSJohannes Berg 	    tbl->mean_chain_len * (tbl->hash_mask + 1))
588eb2b9311SLuis Carlos Cobo 		grow = 1;
589eb2b9311SLuis Carlos Cobo 
590f5ea9120SJohannes Berg 	mesh_paths_generation++;
591f5ea9120SJohannes Berg 
592349eb8cfSJohannes Berg 	spin_unlock_bh(&tbl->hashwlock[hash_idx]);
5939b84b808SJavier Cardona 	read_unlock_bh(&pathtbl_resize_lock);
594402d7752SPavel Emelyanov 	if (grow) {
59518889231SJavier Cardona 		set_bit(MESH_WORK_GROW_MPATH_TABLE,  &ifmsh->wrkq_flags);
59664592c8fSJohannes Berg 		ieee80211_queue_work(&local->hw, &sdata->work);
597eb2b9311SLuis Carlos Cobo 	}
598402d7752SPavel Emelyanov 	return 0;
599402d7752SPavel Emelyanov 
600402d7752SPavel Emelyanov err_exists:
601349eb8cfSJohannes Berg 	spin_unlock_bh(&tbl->hashwlock[hash_idx]);
6029b84b808SJavier Cardona 	read_unlock_bh(&pathtbl_resize_lock);
603402d7752SPavel Emelyanov 	kfree(new_node);
604402d7752SPavel Emelyanov err_node_alloc:
605402d7752SPavel Emelyanov 	kfree(new_mpath);
606402d7752SPavel Emelyanov err_path_alloc:
607472dbc45SJohannes Berg 	atomic_dec(&sdata->u.mesh.mpaths);
608eb2b9311SLuis Carlos Cobo 	return err;
609eb2b9311SLuis Carlos Cobo }
610eb2b9311SLuis Carlos Cobo 
6111928ecabSJohannes Berg static void mesh_table_free_rcu(struct rcu_head *rcu)
6121928ecabSJohannes Berg {
6131928ecabSJohannes Berg 	struct mesh_table *tbl = container_of(rcu, struct mesh_table, rcu_head);
6141928ecabSJohannes Berg 
6151928ecabSJohannes Berg 	mesh_table_free(tbl, false);
6161928ecabSJohannes Berg }
6171928ecabSJohannes Berg 
61818889231SJavier Cardona void mesh_mpath_table_grow(void)
61918889231SJavier Cardona {
62018889231SJavier Cardona 	struct mesh_table *oldtbl, *newtbl;
62118889231SJavier Cardona 
6229b84b808SJavier Cardona 	write_lock_bh(&pathtbl_resize_lock);
623349eb8cfSJohannes Berg 	oldtbl = resize_dereference_mesh_paths();
624349eb8cfSJohannes Berg 	newtbl = mesh_table_alloc(oldtbl->size_order + 1);
6251928ecabSJohannes Berg 	if (!newtbl)
6261928ecabSJohannes Berg 		goto out;
627349eb8cfSJohannes Berg 	if (mesh_table_grow(oldtbl, newtbl) < 0) {
628a3e6b12cScozybit Inc 		__mesh_table_free(newtbl);
6291928ecabSJohannes Berg 		goto out;
63018889231SJavier Cardona 	}
63118889231SJavier Cardona 	rcu_assign_pointer(mesh_paths, newtbl);
63218889231SJavier Cardona 
6331928ecabSJohannes Berg 	call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu);
6341928ecabSJohannes Berg 
6351928ecabSJohannes Berg  out:
6361928ecabSJohannes Berg 	write_unlock_bh(&pathtbl_resize_lock);
63718889231SJavier Cardona }
63818889231SJavier Cardona 
63918889231SJavier Cardona void mesh_mpp_table_grow(void)
64018889231SJavier Cardona {
64118889231SJavier Cardona 	struct mesh_table *oldtbl, *newtbl;
64218889231SJavier Cardona 
6439b84b808SJavier Cardona 	write_lock_bh(&pathtbl_resize_lock);
644349eb8cfSJohannes Berg 	oldtbl = resize_dereference_mpp_paths();
645349eb8cfSJohannes Berg 	newtbl = mesh_table_alloc(oldtbl->size_order + 1);
6461928ecabSJohannes Berg 	if (!newtbl)
6471928ecabSJohannes Berg 		goto out;
648349eb8cfSJohannes Berg 	if (mesh_table_grow(oldtbl, newtbl) < 0) {
649a3e6b12cScozybit Inc 		__mesh_table_free(newtbl);
6501928ecabSJohannes Berg 		goto out;
65118889231SJavier Cardona 	}
65218889231SJavier Cardona 	rcu_assign_pointer(mpp_paths, newtbl);
6531928ecabSJohannes Berg 	call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu);
65418889231SJavier Cardona 
6551928ecabSJohannes Berg  out:
6561928ecabSJohannes Berg 	write_unlock_bh(&pathtbl_resize_lock);
65718889231SJavier Cardona }
658eb2b9311SLuis Carlos Cobo 
65979617deeSYanBo int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
66079617deeSYanBo {
66118889231SJavier Cardona 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
66218889231SJavier Cardona 	struct ieee80211_local *local = sdata->local;
663349eb8cfSJohannes Berg 	struct mesh_table *tbl;
66479617deeSYanBo 	struct mesh_path *mpath, *new_mpath;
66579617deeSYanBo 	struct mpath_node *node, *new_node;
66679617deeSYanBo 	struct hlist_head *bucket;
66779617deeSYanBo 	struct hlist_node *n;
66879617deeSYanBo 	int grow = 0;
66979617deeSYanBo 	int err = 0;
67079617deeSYanBo 	u32 hash_idx;
67179617deeSYanBo 
67247846c9bSJohannes Berg 	if (memcmp(dst, sdata->vif.addr, ETH_ALEN) == 0)
67379617deeSYanBo 		/* never add ourselves as neighbours */
67479617deeSYanBo 		return -ENOTSUPP;
67579617deeSYanBo 
67679617deeSYanBo 	if (is_multicast_ether_addr(dst))
67779617deeSYanBo 		return -ENOTSUPP;
67879617deeSYanBo 
67979617deeSYanBo 	err = -ENOMEM;
68018889231SJavier Cardona 	new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC);
68179617deeSYanBo 	if (!new_mpath)
68279617deeSYanBo 		goto err_path_alloc;
68379617deeSYanBo 
68418889231SJavier Cardona 	new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC);
68579617deeSYanBo 	if (!new_node)
68679617deeSYanBo 		goto err_node_alloc;
68779617deeSYanBo 
6889b84b808SJavier Cardona 	read_lock_bh(&pathtbl_resize_lock);
68979617deeSYanBo 	memcpy(new_mpath->dst, dst, ETH_ALEN);
69079617deeSYanBo 	memcpy(new_mpath->mpp, mpp, ETH_ALEN);
69179617deeSYanBo 	new_mpath->sdata = sdata;
69279617deeSYanBo 	new_mpath->flags = 0;
69379617deeSYanBo 	skb_queue_head_init(&new_mpath->frame_queue);
69479617deeSYanBo 	new_node->mpath = new_mpath;
695c6133661SThomas Pedersen 	init_timer(&new_mpath->timer);
69679617deeSYanBo 	new_mpath->exp_time = jiffies;
69779617deeSYanBo 	spin_lock_init(&new_mpath->state_lock);
69879617deeSYanBo 
699349eb8cfSJohannes Berg 	tbl = resize_dereference_mpp_paths();
70079617deeSYanBo 
701349eb8cfSJohannes Berg 	hash_idx = mesh_table_hash(dst, sdata, tbl);
702349eb8cfSJohannes Berg 	bucket = &tbl->hash_buckets[hash_idx];
703349eb8cfSJohannes Berg 
704349eb8cfSJohannes Berg 	spin_lock_bh(&tbl->hashwlock[hash_idx]);
70579617deeSYanBo 
70679617deeSYanBo 	err = -EEXIST;
70779617deeSYanBo 	hlist_for_each_entry(node, n, bucket, list) {
70879617deeSYanBo 		mpath = node->mpath;
70979617deeSYanBo 		if (mpath->sdata == sdata && memcmp(dst, mpath->dst, ETH_ALEN) == 0)
71079617deeSYanBo 			goto err_exists;
71179617deeSYanBo 	}
71279617deeSYanBo 
71379617deeSYanBo 	hlist_add_head_rcu(&new_node->list, bucket);
714349eb8cfSJohannes Berg 	if (atomic_inc_return(&tbl->entries) >=
715349eb8cfSJohannes Berg 	    tbl->mean_chain_len * (tbl->hash_mask + 1))
71679617deeSYanBo 		grow = 1;
71779617deeSYanBo 
718349eb8cfSJohannes Berg 	spin_unlock_bh(&tbl->hashwlock[hash_idx]);
7199b84b808SJavier Cardona 	read_unlock_bh(&pathtbl_resize_lock);
72079617deeSYanBo 	if (grow) {
72118889231SJavier Cardona 		set_bit(MESH_WORK_GROW_MPP_TABLE,  &ifmsh->wrkq_flags);
72264592c8fSJohannes Berg 		ieee80211_queue_work(&local->hw, &sdata->work);
72379617deeSYanBo 	}
72479617deeSYanBo 	return 0;
72579617deeSYanBo 
72679617deeSYanBo err_exists:
727349eb8cfSJohannes Berg 	spin_unlock_bh(&tbl->hashwlock[hash_idx]);
7289b84b808SJavier Cardona 	read_unlock_bh(&pathtbl_resize_lock);
72979617deeSYanBo 	kfree(new_node);
73079617deeSYanBo err_node_alloc:
73179617deeSYanBo 	kfree(new_mpath);
73279617deeSYanBo err_path_alloc:
73379617deeSYanBo 	return err;
73479617deeSYanBo }
73579617deeSYanBo 
73679617deeSYanBo 
737eb2b9311SLuis Carlos Cobo /**
738eb2b9311SLuis Carlos Cobo  * mesh_plink_broken - deactivates paths and sends perr when a link breaks
739eb2b9311SLuis Carlos Cobo  *
740eb2b9311SLuis Carlos Cobo  * @sta: broken peer link
741eb2b9311SLuis Carlos Cobo  *
742eb2b9311SLuis Carlos Cobo  * This function must be called from the rate control algorithm if enough
743eb2b9311SLuis Carlos Cobo  * delivery errors suggest that a peer link is no longer usable.
744eb2b9311SLuis Carlos Cobo  */
745eb2b9311SLuis Carlos Cobo void mesh_plink_broken(struct sta_info *sta)
746eb2b9311SLuis Carlos Cobo {
747349eb8cfSJohannes Berg 	struct mesh_table *tbl;
74815ff6365SJohannes Berg 	static const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
749eb2b9311SLuis Carlos Cobo 	struct mesh_path *mpath;
750eb2b9311SLuis Carlos Cobo 	struct mpath_node *node;
751eb2b9311SLuis Carlos Cobo 	struct hlist_node *p;
752f698d856SJasper Bryant-Greene 	struct ieee80211_sub_if_data *sdata = sta->sdata;
753eb2b9311SLuis Carlos Cobo 	int i;
75425d49e4dSThomas Pedersen 	__le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_DEST_UNREACHABLE);
755eb2b9311SLuis Carlos Cobo 
756eb2b9311SLuis Carlos Cobo 	rcu_read_lock();
757349eb8cfSJohannes Berg 	tbl = rcu_dereference(mesh_paths);
758349eb8cfSJohannes Berg 	for_each_mesh_entry(tbl, p, node, i) {
759eb2b9311SLuis Carlos Cobo 		mpath = node->mpath;
760349eb8cfSJohannes Berg 		if (rcu_dereference(mpath->next_hop) == sta &&
761eb2b9311SLuis Carlos Cobo 		    mpath->flags & MESH_PATH_ACTIVE &&
762eb2b9311SLuis Carlos Cobo 		    !(mpath->flags & MESH_PATH_FIXED)) {
763f5e50cd0SJavier Cardona 			spin_lock_bh(&mpath->state_lock);
764eb2b9311SLuis Carlos Cobo 			mpath->flags &= ~MESH_PATH_ACTIVE;
765d19b3bf6SRui Paulo 			++mpath->sn;
766eb2b9311SLuis Carlos Cobo 			spin_unlock_bh(&mpath->state_lock);
76745904f21SJavier Cardona 			mesh_path_error_tx(sdata->u.mesh.mshcfg.element_ttl,
76845904f21SJavier Cardona 					mpath->dst, cpu_to_le32(mpath->sn),
76925d49e4dSThomas Pedersen 					reason, bcast, sdata);
770f5e50cd0SJavier Cardona 		}
771eb2b9311SLuis Carlos Cobo 	}
772eb2b9311SLuis Carlos Cobo 	rcu_read_unlock();
773eb2b9311SLuis Carlos Cobo }
774eb2b9311SLuis Carlos Cobo 
77519c50b3dSJavier Cardona static void mesh_path_node_reclaim(struct rcu_head *rp)
77619c50b3dSJavier Cardona {
77719c50b3dSJavier Cardona 	struct mpath_node *node = container_of(rp, struct mpath_node, rcu);
77819c50b3dSJavier Cardona 	struct ieee80211_sub_if_data *sdata = node->mpath->sdata;
77919c50b3dSJavier Cardona 
78019c50b3dSJavier Cardona 	del_timer_sync(&node->mpath->timer);
78119c50b3dSJavier Cardona 	atomic_dec(&sdata->u.mesh.mpaths);
78219c50b3dSJavier Cardona 	kfree(node->mpath);
78319c50b3dSJavier Cardona 	kfree(node);
78419c50b3dSJavier Cardona }
78519c50b3dSJavier Cardona 
78619c50b3dSJavier Cardona /* needs to be called with the corresponding hashwlock taken */
78719c50b3dSJavier Cardona static void __mesh_path_del(struct mesh_table *tbl, struct mpath_node *node)
78819c50b3dSJavier Cardona {
78919c50b3dSJavier Cardona 	struct mesh_path *mpath;
79019c50b3dSJavier Cardona 	mpath = node->mpath;
79119c50b3dSJavier Cardona 	spin_lock(&mpath->state_lock);
79219c50b3dSJavier Cardona 	mpath->flags |= MESH_PATH_RESOLVING;
79319c50b3dSJavier Cardona 	if (mpath->is_gate)
79419c50b3dSJavier Cardona 		mesh_gate_del(tbl, mpath);
79519c50b3dSJavier Cardona 	hlist_del_rcu(&node->list);
79619c50b3dSJavier Cardona 	call_rcu(&node->rcu, mesh_path_node_reclaim);
79719c50b3dSJavier Cardona 	spin_unlock(&mpath->state_lock);
79819c50b3dSJavier Cardona 	atomic_dec(&tbl->entries);
79919c50b3dSJavier Cardona }
80019c50b3dSJavier Cardona 
801cd72e817SJavier Cardona /**
802cd72e817SJavier Cardona  * mesh_path_flush_by_nexthop - Deletes mesh paths if their next hop matches
803cd72e817SJavier Cardona  *
804cd72e817SJavier Cardona  * @sta - mesh peer to match
805cd72e817SJavier Cardona  *
806cd72e817SJavier Cardona  * RCU notes: this function is called when a mesh plink transitions from
807cd72e817SJavier Cardona  * PLINK_ESTAB to any other state, since PLINK_ESTAB state is the only one that
808cd72e817SJavier Cardona  * allows path creation. This will happen before the sta can be freed (because
809cd72e817SJavier Cardona  * sta_info_destroy() calls this) so any reader in a rcu read block will be
810cd72e817SJavier Cardona  * protected against the plink disappearing.
811cd72e817SJavier Cardona  */
812cd72e817SJavier Cardona void mesh_path_flush_by_nexthop(struct sta_info *sta)
813eb2b9311SLuis Carlos Cobo {
814349eb8cfSJohannes Berg 	struct mesh_table *tbl;
815eb2b9311SLuis Carlos Cobo 	struct mesh_path *mpath;
816eb2b9311SLuis Carlos Cobo 	struct mpath_node *node;
817eb2b9311SLuis Carlos Cobo 	struct hlist_node *p;
818eb2b9311SLuis Carlos Cobo 	int i;
819eb2b9311SLuis Carlos Cobo 
820349eb8cfSJohannes Berg 	rcu_read_lock();
821239289e4SJavier Cardona 	read_lock_bh(&pathtbl_resize_lock);
822239289e4SJavier Cardona 	tbl = resize_dereference_mesh_paths();
823349eb8cfSJohannes Berg 	for_each_mesh_entry(tbl, p, node, i) {
824eb2b9311SLuis Carlos Cobo 		mpath = node->mpath;
825cd72e817SJavier Cardona 		if (rcu_dereference(mpath->next_hop) == sta) {
82619c50b3dSJavier Cardona 			spin_lock_bh(&tbl->hashwlock[i]);
82719c50b3dSJavier Cardona 			__mesh_path_del(tbl, node);
82819c50b3dSJavier Cardona 			spin_unlock_bh(&tbl->hashwlock[i]);
82919c50b3dSJavier Cardona 		}
830eb2b9311SLuis Carlos Cobo 	}
831239289e4SJavier Cardona 	read_unlock_bh(&pathtbl_resize_lock);
832349eb8cfSJohannes Berg 	rcu_read_unlock();
833eb2b9311SLuis Carlos Cobo }
834eb2b9311SLuis Carlos Cobo 
835cd72e817SJavier Cardona static void table_flush_by_iface(struct mesh_table *tbl,
836cd72e817SJavier Cardona 				 struct ieee80211_sub_if_data *sdata)
837ece1a2e7SJavier Cardona {
838ece1a2e7SJavier Cardona 	struct mesh_path *mpath;
839ece1a2e7SJavier Cardona 	struct mpath_node *node;
840ece1a2e7SJavier Cardona 	struct hlist_node *p;
841ece1a2e7SJavier Cardona 	int i;
842ece1a2e7SJavier Cardona 
843cd72e817SJavier Cardona 	WARN_ON(!rcu_read_lock_held());
844ece1a2e7SJavier Cardona 	for_each_mesh_entry(tbl, p, node, i) {
845ece1a2e7SJavier Cardona 		mpath = node->mpath;
846cd72e817SJavier Cardona 		if (mpath->sdata != sdata)
847cd72e817SJavier Cardona 			continue;
848ece1a2e7SJavier Cardona 		spin_lock_bh(&tbl->hashwlock[i]);
84919c50b3dSJavier Cardona 		__mesh_path_del(tbl, node);
850ece1a2e7SJavier Cardona 		spin_unlock_bh(&tbl->hashwlock[i]);
851ece1a2e7SJavier Cardona 	}
852ece1a2e7SJavier Cardona }
853ece1a2e7SJavier Cardona 
854ece1a2e7SJavier Cardona /**
855ece1a2e7SJavier Cardona  * mesh_path_flush_by_iface - Deletes all mesh paths associated with a given iface
856ece1a2e7SJavier Cardona  *
857ece1a2e7SJavier Cardona  * This function deletes both mesh paths as well as mesh portal paths.
858ece1a2e7SJavier Cardona  *
859ece1a2e7SJavier Cardona  * @sdata - interface data to match
860ece1a2e7SJavier Cardona  *
861ece1a2e7SJavier Cardona  */
862ece1a2e7SJavier Cardona void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata)
863ece1a2e7SJavier Cardona {
864cd72e817SJavier Cardona 	struct mesh_table *tbl;
865cd72e817SJavier Cardona 
866cd72e817SJavier Cardona 	rcu_read_lock();
867239289e4SJavier Cardona 	read_lock_bh(&pathtbl_resize_lock);
868239289e4SJavier Cardona 	tbl = resize_dereference_mesh_paths();
869cd72e817SJavier Cardona 	table_flush_by_iface(tbl, sdata);
870239289e4SJavier Cardona 	tbl = resize_dereference_mpp_paths();
871cd72e817SJavier Cardona 	table_flush_by_iface(tbl, sdata);
872239289e4SJavier Cardona 	read_unlock_bh(&pathtbl_resize_lock);
873cd72e817SJavier Cardona 	rcu_read_unlock();
874ece1a2e7SJavier Cardona }
875ece1a2e7SJavier Cardona 
876eb2b9311SLuis Carlos Cobo /**
877eb2b9311SLuis Carlos Cobo  * mesh_path_del - delete a mesh path from the table
878eb2b9311SLuis Carlos Cobo  *
879eb2b9311SLuis Carlos Cobo  * @addr: dst address (ETH_ALEN length)
880f698d856SJasper Bryant-Greene  * @sdata: local subif
881eb2b9311SLuis Carlos Cobo  *
882af901ca1SAndré Goddard Rosa  * Returns: 0 if successful
883eb2b9311SLuis Carlos Cobo  */
884f698d856SJasper Bryant-Greene int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata)
885eb2b9311SLuis Carlos Cobo {
886349eb8cfSJohannes Berg 	struct mesh_table *tbl;
887eb2b9311SLuis Carlos Cobo 	struct mesh_path *mpath;
888eb2b9311SLuis Carlos Cobo 	struct mpath_node *node;
889eb2b9311SLuis Carlos Cobo 	struct hlist_head *bucket;
890eb2b9311SLuis Carlos Cobo 	struct hlist_node *n;
891eb2b9311SLuis Carlos Cobo 	int hash_idx;
892eb2b9311SLuis Carlos Cobo 	int err = 0;
893eb2b9311SLuis Carlos Cobo 
8949b84b808SJavier Cardona 	read_lock_bh(&pathtbl_resize_lock);
895349eb8cfSJohannes Berg 	tbl = resize_dereference_mesh_paths();
896349eb8cfSJohannes Berg 	hash_idx = mesh_table_hash(addr, sdata, tbl);
897349eb8cfSJohannes Berg 	bucket = &tbl->hash_buckets[hash_idx];
898eb2b9311SLuis Carlos Cobo 
899349eb8cfSJohannes Berg 	spin_lock_bh(&tbl->hashwlock[hash_idx]);
900eb2b9311SLuis Carlos Cobo 	hlist_for_each_entry(node, n, bucket, list) {
901eb2b9311SLuis Carlos Cobo 		mpath = node->mpath;
902f698d856SJasper Bryant-Greene 		if (mpath->sdata == sdata &&
903eb2b9311SLuis Carlos Cobo 		    memcmp(addr, mpath->dst, ETH_ALEN) == 0) {
90419c50b3dSJavier Cardona 			__mesh_path_del(tbl, node);
905eb2b9311SLuis Carlos Cobo 			goto enddel;
906eb2b9311SLuis Carlos Cobo 		}
907eb2b9311SLuis Carlos Cobo 	}
908eb2b9311SLuis Carlos Cobo 
909eb2b9311SLuis Carlos Cobo 	err = -ENXIO;
910eb2b9311SLuis Carlos Cobo enddel:
911f5ea9120SJohannes Berg 	mesh_paths_generation++;
912349eb8cfSJohannes Berg 	spin_unlock_bh(&tbl->hashwlock[hash_idx]);
9139b84b808SJavier Cardona 	read_unlock_bh(&pathtbl_resize_lock);
914eb2b9311SLuis Carlos Cobo 	return err;
915eb2b9311SLuis Carlos Cobo }
916eb2b9311SLuis Carlos Cobo 
917eb2b9311SLuis Carlos Cobo /**
918eb2b9311SLuis Carlos Cobo  * mesh_path_tx_pending - sends pending frames in a mesh path queue
919eb2b9311SLuis Carlos Cobo  *
920eb2b9311SLuis Carlos Cobo  * @mpath: mesh path to activate
921eb2b9311SLuis Carlos Cobo  *
922eb2b9311SLuis Carlos Cobo  * Locking: the state_lock of the mpath structure must NOT be held when calling
923eb2b9311SLuis Carlos Cobo  * this function.
924eb2b9311SLuis Carlos Cobo  */
925eb2b9311SLuis Carlos Cobo void mesh_path_tx_pending(struct mesh_path *mpath)
926eb2b9311SLuis Carlos Cobo {
927249b405cSJavier Cardona 	if (mpath->flags & MESH_PATH_ACTIVE)
928249b405cSJavier Cardona 		ieee80211_add_pending_skbs(mpath->sdata->local,
929249b405cSJavier Cardona 				&mpath->frame_queue);
930eb2b9311SLuis Carlos Cobo }
931eb2b9311SLuis Carlos Cobo 
932eb2b9311SLuis Carlos Cobo /**
9335ee68e5bSJavier Cardona  * mesh_path_send_to_gates - sends pending frames to all known mesh gates
9345ee68e5bSJavier Cardona  *
9355ee68e5bSJavier Cardona  * @mpath: mesh path whose queue will be emptied
9365ee68e5bSJavier Cardona  *
9375ee68e5bSJavier Cardona  * If there is only one gate, the frames are transferred from the failed mpath
9385ee68e5bSJavier Cardona  * queue to that gate's queue.  If there are more than one gates, the frames
9395ee68e5bSJavier Cardona  * are copied from each gate to the next.  After frames are copied, the
9405ee68e5bSJavier Cardona  * mpath queues are emptied onto the transmission queue.
9415ee68e5bSJavier Cardona  */
9425ee68e5bSJavier Cardona int mesh_path_send_to_gates(struct mesh_path *mpath)
9435ee68e5bSJavier Cardona {
9445ee68e5bSJavier Cardona 	struct ieee80211_sub_if_data *sdata = mpath->sdata;
9455ee68e5bSJavier Cardona 	struct hlist_node *n;
9465ee68e5bSJavier Cardona 	struct mesh_table *tbl;
9475ee68e5bSJavier Cardona 	struct mesh_path *from_mpath = mpath;
9485ee68e5bSJavier Cardona 	struct mpath_node *gate = NULL;
9495ee68e5bSJavier Cardona 	bool copy = false;
9505ee68e5bSJavier Cardona 	struct hlist_head *known_gates;
9515ee68e5bSJavier Cardona 
9525ee68e5bSJavier Cardona 	rcu_read_lock();
9535ee68e5bSJavier Cardona 	tbl = rcu_dereference(mesh_paths);
9545ee68e5bSJavier Cardona 	known_gates = tbl->known_gates;
9555ee68e5bSJavier Cardona 	rcu_read_unlock();
9565ee68e5bSJavier Cardona 
9575ee68e5bSJavier Cardona 	if (!known_gates)
9585ee68e5bSJavier Cardona 		return -EHOSTUNREACH;
9595ee68e5bSJavier Cardona 
9605ee68e5bSJavier Cardona 	hlist_for_each_entry_rcu(gate, n, known_gates, list) {
9615ee68e5bSJavier Cardona 		if (gate->mpath->sdata != sdata)
9625ee68e5bSJavier Cardona 			continue;
9635ee68e5bSJavier Cardona 
9645ee68e5bSJavier Cardona 		if (gate->mpath->flags & MESH_PATH_ACTIVE) {
9655ee68e5bSJavier Cardona 			mpath_dbg("Forwarding to %pM\n", gate->mpath->dst);
9665ee68e5bSJavier Cardona 			mesh_path_move_to_queue(gate->mpath, from_mpath, copy);
9675ee68e5bSJavier Cardona 			from_mpath = gate->mpath;
9685ee68e5bSJavier Cardona 			copy = true;
9695ee68e5bSJavier Cardona 		} else {
9705ee68e5bSJavier Cardona 			mpath_dbg("Not forwarding %p\n", gate->mpath);
9715ee68e5bSJavier Cardona 			mpath_dbg("flags %x\n", gate->mpath->flags);
9725ee68e5bSJavier Cardona 		}
9735ee68e5bSJavier Cardona 	}
9745ee68e5bSJavier Cardona 
9755ee68e5bSJavier Cardona 	hlist_for_each_entry_rcu(gate, n, known_gates, list)
9765ee68e5bSJavier Cardona 		if (gate->mpath->sdata == sdata) {
9775ee68e5bSJavier Cardona 			mpath_dbg("Sending to %pM\n", gate->mpath->dst);
9785ee68e5bSJavier Cardona 			mesh_path_tx_pending(gate->mpath);
9795ee68e5bSJavier Cardona 		}
9805ee68e5bSJavier Cardona 
9815ee68e5bSJavier Cardona 	return (from_mpath == mpath) ? -EHOSTUNREACH : 0;
9825ee68e5bSJavier Cardona }
9835ee68e5bSJavier Cardona 
9845ee68e5bSJavier Cardona /**
985eb2b9311SLuis Carlos Cobo  * mesh_path_discard_frame - discard a frame whose path could not be resolved
986eb2b9311SLuis Carlos Cobo  *
987eb2b9311SLuis Carlos Cobo  * @skb: frame to discard
988f698d856SJasper Bryant-Greene  * @sdata: network subif the frame was to be sent through
989eb2b9311SLuis Carlos Cobo  *
99035946a57SJavier Cardona  * If the frame was being forwarded from another MP, a PERR frame will be sent
99135946a57SJavier Cardona  * to the precursor.  The precursor's address (i.e. the previous hop) was saved
99235946a57SJavier Cardona  * in addr1 of the frame-to-be-forwarded, and would only be overwritten once
99335946a57SJavier Cardona  * the destination is successfully resolved.
994eb2b9311SLuis Carlos Cobo  *
995eb2b9311SLuis Carlos Cobo  * Locking: the function must me called within a rcu_read_lock region
996eb2b9311SLuis Carlos Cobo  */
997f698d856SJasper Bryant-Greene void mesh_path_discard_frame(struct sk_buff *skb,
998f698d856SJasper Bryant-Greene 			     struct ieee80211_sub_if_data *sdata)
999eb2b9311SLuis Carlos Cobo {
1000e32f85f7SLuis Carlos Cobo 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
1001eb2b9311SLuis Carlos Cobo 	struct mesh_path *mpath;
1002d19b3bf6SRui Paulo 	u32 sn = 0;
100325d49e4dSThomas Pedersen 	__le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_NOFORWARD);
1004eb2b9311SLuis Carlos Cobo 
100547846c9bSJohannes Berg 	if (memcmp(hdr->addr4, sdata->vif.addr, ETH_ALEN) != 0) {
1006eb2b9311SLuis Carlos Cobo 		u8 *ra, *da;
1007eb2b9311SLuis Carlos Cobo 
1008e32f85f7SLuis Carlos Cobo 		da = hdr->addr3;
100935946a57SJavier Cardona 		ra = hdr->addr1;
1010af089c15SJavier Cardona 		rcu_read_lock();
1011f698d856SJasper Bryant-Greene 		mpath = mesh_path_lookup(da, sdata);
1012af089c15SJavier Cardona 		if (mpath) {
1013af089c15SJavier Cardona 			spin_lock_bh(&mpath->state_lock);
1014d19b3bf6SRui Paulo 			sn = ++mpath->sn;
1015af089c15SJavier Cardona 			spin_unlock_bh(&mpath->state_lock);
1016af089c15SJavier Cardona 		}
1017af089c15SJavier Cardona 		rcu_read_unlock();
101845904f21SJavier Cardona 		mesh_path_error_tx(sdata->u.mesh.mshcfg.element_ttl, skb->data,
101925d49e4dSThomas Pedersen 				   cpu_to_le32(sn), reason, ra, sdata);
1020eb2b9311SLuis Carlos Cobo 	}
1021eb2b9311SLuis Carlos Cobo 
1022eb2b9311SLuis Carlos Cobo 	kfree_skb(skb);
1023472dbc45SJohannes Berg 	sdata->u.mesh.mshstats.dropped_frames_no_route++;
1024eb2b9311SLuis Carlos Cobo }
1025eb2b9311SLuis Carlos Cobo 
1026eb2b9311SLuis Carlos Cobo /**
1027eb2b9311SLuis Carlos Cobo  * mesh_path_flush_pending - free the pending queue of a mesh path
1028eb2b9311SLuis Carlos Cobo  *
1029eb2b9311SLuis Carlos Cobo  * @mpath: mesh path whose queue has to be freed
1030eb2b9311SLuis Carlos Cobo  *
103125985edcSLucas De Marchi  * Locking: the function must me called within a rcu_read_lock region
1032eb2b9311SLuis Carlos Cobo  */
1033eb2b9311SLuis Carlos Cobo void mesh_path_flush_pending(struct mesh_path *mpath)
1034eb2b9311SLuis Carlos Cobo {
1035eb2b9311SLuis Carlos Cobo 	struct sk_buff *skb;
1036eb2b9311SLuis Carlos Cobo 
103700e3f25cSJavier Cardona 	while ((skb = skb_dequeue(&mpath->frame_queue)) != NULL)
1038f698d856SJasper Bryant-Greene 		mesh_path_discard_frame(skb, mpath->sdata);
1039eb2b9311SLuis Carlos Cobo }
1040eb2b9311SLuis Carlos Cobo 
1041eb2b9311SLuis Carlos Cobo /**
1042eb2b9311SLuis Carlos Cobo  * mesh_path_fix_nexthop - force a specific next hop for a mesh path
1043eb2b9311SLuis Carlos Cobo  *
1044eb2b9311SLuis Carlos Cobo  * @mpath: the mesh path to modify
1045eb2b9311SLuis Carlos Cobo  * @next_hop: the next hop to force
1046eb2b9311SLuis Carlos Cobo  *
1047eb2b9311SLuis Carlos Cobo  * Locking: this function must be called holding mpath->state_lock
1048eb2b9311SLuis Carlos Cobo  */
1049eb2b9311SLuis Carlos Cobo void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop)
1050eb2b9311SLuis Carlos Cobo {
1051eb2b9311SLuis Carlos Cobo 	spin_lock_bh(&mpath->state_lock);
1052eb2b9311SLuis Carlos Cobo 	mesh_path_assign_nexthop(mpath, next_hop);
1053d19b3bf6SRui Paulo 	mpath->sn = 0xffff;
1054eb2b9311SLuis Carlos Cobo 	mpath->metric = 0;
1055eb2b9311SLuis Carlos Cobo 	mpath->hop_count = 0;
1056eb2b9311SLuis Carlos Cobo 	mpath->exp_time = 0;
1057eb2b9311SLuis Carlos Cobo 	mpath->flags |= MESH_PATH_FIXED;
1058eb2b9311SLuis Carlos Cobo 	mesh_path_activate(mpath);
1059eb2b9311SLuis Carlos Cobo 	spin_unlock_bh(&mpath->state_lock);
1060eb2b9311SLuis Carlos Cobo 	mesh_path_tx_pending(mpath);
1061eb2b9311SLuis Carlos Cobo }
1062eb2b9311SLuis Carlos Cobo 
1063eb2b9311SLuis Carlos Cobo static void mesh_path_node_free(struct hlist_node *p, bool free_leafs)
1064eb2b9311SLuis Carlos Cobo {
1065eb2b9311SLuis Carlos Cobo 	struct mesh_path *mpath;
1066eb2b9311SLuis Carlos Cobo 	struct mpath_node *node = hlist_entry(p, struct mpath_node, list);
1067eb2b9311SLuis Carlos Cobo 	mpath = node->mpath;
1068eb2b9311SLuis Carlos Cobo 	hlist_del_rcu(p);
1069d0df9eecSJavier Cardona 	if (free_leafs) {
1070d0df9eecSJavier Cardona 		del_timer_sync(&mpath->timer);
1071eb2b9311SLuis Carlos Cobo 		kfree(mpath);
1072d0df9eecSJavier Cardona 	}
1073eb2b9311SLuis Carlos Cobo 	kfree(node);
1074eb2b9311SLuis Carlos Cobo }
1075eb2b9311SLuis Carlos Cobo 
10764caf86c6SPavel Emelyanov static int mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl)
1077eb2b9311SLuis Carlos Cobo {
1078eb2b9311SLuis Carlos Cobo 	struct mesh_path *mpath;
1079eb2b9311SLuis Carlos Cobo 	struct mpath_node *node, *new_node;
1080eb2b9311SLuis Carlos Cobo 	u32 hash_idx;
1081eb2b9311SLuis Carlos Cobo 
10828566dc3fSPavel Emelyanov 	new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC);
108300242c40SPavel Emelyanov 	if (new_node == NULL)
108400242c40SPavel Emelyanov 		return -ENOMEM;
108500242c40SPavel Emelyanov 
1086eb2b9311SLuis Carlos Cobo 	node = hlist_entry(p, struct mpath_node, list);
1087eb2b9311SLuis Carlos Cobo 	mpath = node->mpath;
1088eb2b9311SLuis Carlos Cobo 	new_node->mpath = mpath;
1089f698d856SJasper Bryant-Greene 	hash_idx = mesh_table_hash(mpath->dst, mpath->sdata, newtbl);
1090eb2b9311SLuis Carlos Cobo 	hlist_add_head(&new_node->list,
1091eb2b9311SLuis Carlos Cobo 			&newtbl->hash_buckets[hash_idx]);
10924caf86c6SPavel Emelyanov 	return 0;
1093eb2b9311SLuis Carlos Cobo }
1094eb2b9311SLuis Carlos Cobo 
1095eb2b9311SLuis Carlos Cobo int mesh_pathtbl_init(void)
1096eb2b9311SLuis Carlos Cobo {
1097349eb8cfSJohannes Berg 	struct mesh_table *tbl_path, *tbl_mpp;
10984c5ade41SDan Carpenter 	int ret;
109979617deeSYanBo 
1100349eb8cfSJohannes Berg 	tbl_path = mesh_table_alloc(INIT_PATHS_SIZE_ORDER);
1101349eb8cfSJohannes Berg 	if (!tbl_path)
1102349eb8cfSJohannes Berg 		return -ENOMEM;
1103349eb8cfSJohannes Berg 	tbl_path->free_node = &mesh_path_node_free;
1104349eb8cfSJohannes Berg 	tbl_path->copy_node = &mesh_path_node_copy;
1105349eb8cfSJohannes Berg 	tbl_path->mean_chain_len = MEAN_CHAIN_LEN;
11065ee68e5bSJavier Cardona 	tbl_path->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC);
11074c5ade41SDan Carpenter 	if (!tbl_path->known_gates) {
11084c5ade41SDan Carpenter 		ret = -ENOMEM;
11094c5ade41SDan Carpenter 		goto free_path;
11104c5ade41SDan Carpenter 	}
11115ee68e5bSJavier Cardona 	INIT_HLIST_HEAD(tbl_path->known_gates);
11125ee68e5bSJavier Cardona 
1113349eb8cfSJohannes Berg 
1114349eb8cfSJohannes Berg 	tbl_mpp = mesh_table_alloc(INIT_PATHS_SIZE_ORDER);
1115349eb8cfSJohannes Berg 	if (!tbl_mpp) {
11164c5ade41SDan Carpenter 		ret = -ENOMEM;
11174c5ade41SDan Carpenter 		goto free_path;
111879617deeSYanBo 	}
1119349eb8cfSJohannes Berg 	tbl_mpp->free_node = &mesh_path_node_free;
1120349eb8cfSJohannes Berg 	tbl_mpp->copy_node = &mesh_path_node_copy;
1121349eb8cfSJohannes Berg 	tbl_mpp->mean_chain_len = MEAN_CHAIN_LEN;
11225ee68e5bSJavier Cardona 	tbl_mpp->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC);
11234c5ade41SDan Carpenter 	if (!tbl_mpp->known_gates) {
11244c5ade41SDan Carpenter 		ret = -ENOMEM;
11254c5ade41SDan Carpenter 		goto free_mpp;
11264c5ade41SDan Carpenter 	}
11275ee68e5bSJavier Cardona 	INIT_HLIST_HEAD(tbl_mpp->known_gates);
1128349eb8cfSJohannes Berg 
1129349eb8cfSJohannes Berg 	/* Need no locking since this is during init */
1130349eb8cfSJohannes Berg 	RCU_INIT_POINTER(mesh_paths, tbl_path);
1131349eb8cfSJohannes Berg 	RCU_INIT_POINTER(mpp_paths, tbl_mpp);
113279617deeSYanBo 
1133eb2b9311SLuis Carlos Cobo 	return 0;
11344c5ade41SDan Carpenter 
11354c5ade41SDan Carpenter free_mpp:
11364c5ade41SDan Carpenter 	mesh_table_free(tbl_mpp, true);
11374c5ade41SDan Carpenter free_path:
11384c5ade41SDan Carpenter 	mesh_table_free(tbl_path, true);
11394c5ade41SDan Carpenter 	return ret;
1140eb2b9311SLuis Carlos Cobo }
1141eb2b9311SLuis Carlos Cobo 
1142f698d856SJasper Bryant-Greene void mesh_path_expire(struct ieee80211_sub_if_data *sdata)
1143eb2b9311SLuis Carlos Cobo {
1144349eb8cfSJohannes Berg 	struct mesh_table *tbl;
1145eb2b9311SLuis Carlos Cobo 	struct mesh_path *mpath;
1146eb2b9311SLuis Carlos Cobo 	struct mpath_node *node;
1147eb2b9311SLuis Carlos Cobo 	struct hlist_node *p;
1148eb2b9311SLuis Carlos Cobo 	int i;
1149eb2b9311SLuis Carlos Cobo 
1150349eb8cfSJohannes Berg 	rcu_read_lock();
1151349eb8cfSJohannes Berg 	tbl = rcu_dereference(mesh_paths);
1152349eb8cfSJohannes Berg 	for_each_mesh_entry(tbl, p, node, i) {
1153f698d856SJasper Bryant-Greene 		if (node->mpath->sdata != sdata)
1154eb2b9311SLuis Carlos Cobo 			continue;
1155eb2b9311SLuis Carlos Cobo 		mpath = node->mpath;
1156eb2b9311SLuis Carlos Cobo 		if ((!(mpath->flags & MESH_PATH_RESOLVING)) &&
1157eb2b9311SLuis Carlos Cobo 		    (!(mpath->flags & MESH_PATH_FIXED)) &&
1158f5e50cd0SJavier Cardona 		     time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE))
1159f698d856SJasper Bryant-Greene 			mesh_path_del(mpath->dst, mpath->sdata);
116019c50b3dSJavier Cardona 	}
1161349eb8cfSJohannes Berg 	rcu_read_unlock();
1162eb2b9311SLuis Carlos Cobo }
1163eb2b9311SLuis Carlos Cobo 
1164eb2b9311SLuis Carlos Cobo void mesh_pathtbl_unregister(void)
1165eb2b9311SLuis Carlos Cobo {
1166349eb8cfSJohannes Berg 	/* no need for locking during exit path */
1167349eb8cfSJohannes Berg 	mesh_table_free(rcu_dereference_raw(mesh_paths), true);
1168349eb8cfSJohannes Berg 	mesh_table_free(rcu_dereference_raw(mpp_paths), true);
1169eb2b9311SLuis Carlos Cobo }
1170