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