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> 174777be41SJavier Cardona #include "wme.h" 18eb2b9311SLuis Carlos Cobo #include "ieee80211_i.h" 19eb2b9311SLuis Carlos Cobo #include "mesh.h" 20eb2b9311SLuis Carlos Cobo 217646887aSJavier Cardona #ifdef CONFIG_MAC80211_VERBOSE_MPATH_DEBUG 227646887aSJavier Cardona #define mpath_dbg(fmt, args...) printk(KERN_DEBUG fmt, ##args) 237646887aSJavier Cardona #else 247646887aSJavier Cardona #define mpath_dbg(fmt, args...) do { (void)(0); } while (0) 257646887aSJavier Cardona #endif 267646887aSJavier Cardona 27eb2b9311SLuis Carlos Cobo /* There will be initially 2^INIT_PATHS_SIZE_ORDER buckets */ 28eb2b9311SLuis Carlos Cobo #define INIT_PATHS_SIZE_ORDER 2 29eb2b9311SLuis Carlos Cobo 30eb2b9311SLuis Carlos Cobo /* Keep the mean chain length below this constant */ 31eb2b9311SLuis Carlos Cobo #define MEAN_CHAIN_LEN 2 32eb2b9311SLuis Carlos Cobo 33eb2b9311SLuis Carlos Cobo #define MPATH_EXPIRED(mpath) ((mpath->flags & MESH_PATH_ACTIVE) && \ 34eb2b9311SLuis Carlos Cobo time_after(jiffies, mpath->exp_time) && \ 35eb2b9311SLuis Carlos Cobo !(mpath->flags & MESH_PATH_FIXED)) 36eb2b9311SLuis Carlos Cobo 37eb2b9311SLuis Carlos Cobo struct mpath_node { 38eb2b9311SLuis Carlos Cobo struct hlist_node list; 39eb2b9311SLuis Carlos Cobo struct rcu_head rcu; 40eb2b9311SLuis Carlos Cobo /* This indirection allows two different tables to point to the same 41eb2b9311SLuis Carlos Cobo * mesh_path structure, useful when resizing 42eb2b9311SLuis Carlos Cobo */ 43eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 44eb2b9311SLuis Carlos Cobo }; 45eb2b9311SLuis Carlos Cobo 46349eb8cfSJohannes Berg static struct mesh_table __rcu *mesh_paths; 47349eb8cfSJohannes Berg static struct mesh_table __rcu *mpp_paths; /* Store paths for MPP&MAP */ 48eb2b9311SLuis Carlos Cobo 49f5ea9120SJohannes Berg int mesh_paths_generation; 506b86bd62SJohannes Berg 516b86bd62SJohannes Berg /* This lock will have the grow table function as writer and add / delete nodes 52239289e4SJavier Cardona * as readers. RCU provides sufficient protection only when reading the table 53239289e4SJavier Cardona * (i.e. doing lookups). Adding or adding or removing nodes requires we take 54239289e4SJavier Cardona * the read lock or we risk operating on an old table. The write lock is only 55239289e4SJavier Cardona * needed when modifying the number of buckets a table. 566b86bd62SJohannes Berg */ 576b86bd62SJohannes Berg static DEFINE_RWLOCK(pathtbl_resize_lock); 586b86bd62SJohannes Berg 596b86bd62SJohannes Berg 60349eb8cfSJohannes Berg static inline struct mesh_table *resize_dereference_mesh_paths(void) 61349eb8cfSJohannes Berg { 62349eb8cfSJohannes Berg return rcu_dereference_protected(mesh_paths, 63349eb8cfSJohannes Berg lockdep_is_held(&pathtbl_resize_lock)); 64349eb8cfSJohannes Berg } 65349eb8cfSJohannes Berg 66349eb8cfSJohannes Berg static inline struct mesh_table *resize_dereference_mpp_paths(void) 67349eb8cfSJohannes Berg { 68349eb8cfSJohannes Berg return rcu_dereference_protected(mpp_paths, 69349eb8cfSJohannes Berg lockdep_is_held(&pathtbl_resize_lock)); 70349eb8cfSJohannes Berg } 71349eb8cfSJohannes Berg 72349eb8cfSJohannes Berg /* 73349eb8cfSJohannes Berg * CAREFUL -- "tbl" must not be an expression, 74349eb8cfSJohannes Berg * in particular not an rcu_dereference(), since 75349eb8cfSJohannes Berg * it's used twice. So it is illegal to do 76349eb8cfSJohannes Berg * for_each_mesh_entry(rcu_dereference(...), ...) 77349eb8cfSJohannes Berg */ 78349eb8cfSJohannes Berg #define for_each_mesh_entry(tbl, p, node, i) \ 79349eb8cfSJohannes Berg for (i = 0; i <= tbl->hash_mask; i++) \ 80349eb8cfSJohannes Berg hlist_for_each_entry_rcu(node, p, &tbl->hash_buckets[i], list) 81349eb8cfSJohannes Berg 82349eb8cfSJohannes Berg 836b86bd62SJohannes Berg static struct mesh_table *mesh_table_alloc(int size_order) 846b86bd62SJohannes Berg { 856b86bd62SJohannes Berg int i; 866b86bd62SJohannes Berg struct mesh_table *newtbl; 876b86bd62SJohannes Berg 88d676ff49SJavier Cardona newtbl = kmalloc(sizeof(struct mesh_table), GFP_ATOMIC); 896b86bd62SJohannes Berg if (!newtbl) 906b86bd62SJohannes Berg return NULL; 916b86bd62SJohannes Berg 926b86bd62SJohannes Berg newtbl->hash_buckets = kzalloc(sizeof(struct hlist_head) * 93d676ff49SJavier Cardona (1 << size_order), GFP_ATOMIC); 946b86bd62SJohannes Berg 956b86bd62SJohannes Berg if (!newtbl->hash_buckets) { 966b86bd62SJohannes Berg kfree(newtbl); 976b86bd62SJohannes Berg return NULL; 986b86bd62SJohannes Berg } 996b86bd62SJohannes Berg 1006b86bd62SJohannes Berg newtbl->hashwlock = kmalloc(sizeof(spinlock_t) * 101d676ff49SJavier Cardona (1 << size_order), GFP_ATOMIC); 1026b86bd62SJohannes Berg if (!newtbl->hashwlock) { 1036b86bd62SJohannes Berg kfree(newtbl->hash_buckets); 1046b86bd62SJohannes Berg kfree(newtbl); 1056b86bd62SJohannes Berg return NULL; 1066b86bd62SJohannes Berg } 1076b86bd62SJohannes Berg 1086b86bd62SJohannes Berg newtbl->size_order = size_order; 1096b86bd62SJohannes Berg newtbl->hash_mask = (1 << size_order) - 1; 1106b86bd62SJohannes Berg atomic_set(&newtbl->entries, 0); 1116b86bd62SJohannes Berg get_random_bytes(&newtbl->hash_rnd, 1126b86bd62SJohannes Berg sizeof(newtbl->hash_rnd)); 1136b86bd62SJohannes Berg for (i = 0; i <= newtbl->hash_mask; i++) 1146b86bd62SJohannes Berg spin_lock_init(&newtbl->hashwlock[i]); 1155ee68e5bSJavier Cardona spin_lock_init(&newtbl->gates_lock); 1166b86bd62SJohannes Berg 1176b86bd62SJohannes Berg return newtbl; 1186b86bd62SJohannes Berg } 1196b86bd62SJohannes Berg 12018889231SJavier Cardona static void __mesh_table_free(struct mesh_table *tbl) 12118889231SJavier Cardona { 12218889231SJavier Cardona kfree(tbl->hash_buckets); 12318889231SJavier Cardona kfree(tbl->hashwlock); 12418889231SJavier Cardona kfree(tbl); 12518889231SJavier Cardona } 12618889231SJavier Cardona 1276b86bd62SJohannes Berg static void mesh_table_free(struct mesh_table *tbl, bool free_leafs) 12818889231SJavier Cardona { 12918889231SJavier Cardona struct hlist_head *mesh_hash; 13018889231SJavier Cardona struct hlist_node *p, *q; 1315ee68e5bSJavier Cardona struct mpath_node *gate; 13218889231SJavier Cardona int i; 13318889231SJavier Cardona 13418889231SJavier Cardona mesh_hash = tbl->hash_buckets; 13518889231SJavier Cardona for (i = 0; i <= tbl->hash_mask; i++) { 1369b84b808SJavier Cardona spin_lock_bh(&tbl->hashwlock[i]); 13718889231SJavier Cardona hlist_for_each_safe(p, q, &mesh_hash[i]) { 13818889231SJavier Cardona tbl->free_node(p, free_leafs); 13918889231SJavier Cardona atomic_dec(&tbl->entries); 14018889231SJavier Cardona } 1419b84b808SJavier Cardona spin_unlock_bh(&tbl->hashwlock[i]); 14218889231SJavier Cardona } 1435ee68e5bSJavier Cardona if (free_leafs) { 1445ee68e5bSJavier Cardona spin_lock_bh(&tbl->gates_lock); 1455ee68e5bSJavier Cardona hlist_for_each_entry_safe(gate, p, q, 1465ee68e5bSJavier Cardona tbl->known_gates, list) { 1475ee68e5bSJavier Cardona hlist_del(&gate->list); 1485ee68e5bSJavier Cardona kfree(gate); 1495ee68e5bSJavier Cardona } 1505ee68e5bSJavier Cardona kfree(tbl->known_gates); 1515ee68e5bSJavier Cardona spin_unlock_bh(&tbl->gates_lock); 1525ee68e5bSJavier Cardona } 1535ee68e5bSJavier Cardona 15418889231SJavier Cardona __mesh_table_free(tbl); 15518889231SJavier Cardona } 15618889231SJavier Cardona 157a3e6b12cScozybit Inc static int mesh_table_grow(struct mesh_table *oldtbl, 158a3e6b12cScozybit Inc struct mesh_table *newtbl) 15918889231SJavier Cardona { 16018889231SJavier Cardona struct hlist_head *oldhash; 16118889231SJavier Cardona struct hlist_node *p, *q; 16218889231SJavier Cardona int i; 16318889231SJavier Cardona 164a3e6b12cScozybit Inc if (atomic_read(&oldtbl->entries) 165a3e6b12cScozybit Inc < oldtbl->mean_chain_len * (oldtbl->hash_mask + 1)) 166a3e6b12cScozybit Inc return -EAGAIN; 16718889231SJavier Cardona 168a3e6b12cScozybit Inc newtbl->free_node = oldtbl->free_node; 169a3e6b12cScozybit Inc newtbl->mean_chain_len = oldtbl->mean_chain_len; 170a3e6b12cScozybit Inc newtbl->copy_node = oldtbl->copy_node; 1715ee68e5bSJavier Cardona newtbl->known_gates = oldtbl->known_gates; 172a3e6b12cScozybit Inc atomic_set(&newtbl->entries, atomic_read(&oldtbl->entries)); 17318889231SJavier Cardona 174a3e6b12cScozybit Inc oldhash = oldtbl->hash_buckets; 175a3e6b12cScozybit Inc for (i = 0; i <= oldtbl->hash_mask; i++) 17618889231SJavier Cardona hlist_for_each(p, &oldhash[i]) 177a3e6b12cScozybit Inc if (oldtbl->copy_node(p, newtbl) < 0) 17818889231SJavier Cardona goto errcopy; 17918889231SJavier Cardona 180a3e6b12cScozybit Inc return 0; 18118889231SJavier Cardona 18218889231SJavier Cardona errcopy: 18318889231SJavier Cardona for (i = 0; i <= newtbl->hash_mask; i++) { 18418889231SJavier Cardona hlist_for_each_safe(p, q, &newtbl->hash_buckets[i]) 185a3e6b12cScozybit Inc oldtbl->free_node(p, 0); 18618889231SJavier Cardona } 187a3e6b12cScozybit Inc return -ENOMEM; 18818889231SJavier Cardona } 18918889231SJavier Cardona 1906b86bd62SJohannes Berg static u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata, 1916b86bd62SJohannes Berg struct mesh_table *tbl) 1926b86bd62SJohannes Berg { 1936b86bd62SJohannes Berg /* Use last four bytes of hw addr and interface index as hash index */ 1946b86bd62SJohannes Berg return jhash_2words(*(u32 *)(addr+2), sdata->dev->ifindex, tbl->hash_rnd) 1956b86bd62SJohannes Berg & tbl->hash_mask; 1966b86bd62SJohannes Berg } 197f5ea9120SJohannes Berg 198eb2b9311SLuis Carlos Cobo 199eb2b9311SLuis Carlos Cobo /** 200eb2b9311SLuis Carlos Cobo * 201eb2b9311SLuis Carlos Cobo * mesh_path_assign_nexthop - update mesh path next hop 202eb2b9311SLuis Carlos Cobo * 203eb2b9311SLuis Carlos Cobo * @mpath: mesh path to update 204eb2b9311SLuis Carlos Cobo * @sta: next hop to assign 205eb2b9311SLuis Carlos Cobo * 206eb2b9311SLuis Carlos Cobo * Locking: mpath->state_lock must be held when calling this function 207eb2b9311SLuis Carlos Cobo */ 208eb2b9311SLuis Carlos Cobo void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) 209eb2b9311SLuis Carlos Cobo { 21010c836d7SJavier Cardona struct sk_buff *skb; 21110c836d7SJavier Cardona struct ieee80211_hdr *hdr; 21210c836d7SJavier Cardona struct sk_buff_head tmpq; 21310c836d7SJavier Cardona unsigned long flags; 21410c836d7SJavier Cardona 215d0709a65SJohannes Berg rcu_assign_pointer(mpath->next_hop, sta); 21610c836d7SJavier Cardona 21710c836d7SJavier Cardona __skb_queue_head_init(&tmpq); 21810c836d7SJavier Cardona 21910c836d7SJavier Cardona spin_lock_irqsave(&mpath->frame_queue.lock, flags); 22010c836d7SJavier Cardona 22110c836d7SJavier Cardona while ((skb = __skb_dequeue(&mpath->frame_queue)) != NULL) { 22210c836d7SJavier Cardona hdr = (struct ieee80211_hdr *) skb->data; 22310c836d7SJavier Cardona memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN); 22410c836d7SJavier Cardona __skb_queue_tail(&tmpq, skb); 22510c836d7SJavier Cardona } 22610c836d7SJavier Cardona 22710c836d7SJavier Cardona skb_queue_splice(&tmpq, &mpath->frame_queue); 22810c836d7SJavier Cardona spin_unlock_irqrestore(&mpath->frame_queue.lock, flags); 229eb2b9311SLuis Carlos Cobo } 230eb2b9311SLuis Carlos Cobo 2315ee68e5bSJavier Cardona static void prepare_for_gate(struct sk_buff *skb, char *dst_addr, 2325ee68e5bSJavier Cardona struct mesh_path *gate_mpath) 2335ee68e5bSJavier Cardona { 2345ee68e5bSJavier Cardona struct ieee80211_hdr *hdr; 2355ee68e5bSJavier Cardona struct ieee80211s_hdr *mshdr; 2365ee68e5bSJavier Cardona int mesh_hdrlen, hdrlen; 2375ee68e5bSJavier Cardona char *next_hop; 2385ee68e5bSJavier Cardona 2395ee68e5bSJavier Cardona hdr = (struct ieee80211_hdr *) skb->data; 2405ee68e5bSJavier Cardona hdrlen = ieee80211_hdrlen(hdr->frame_control); 2415ee68e5bSJavier Cardona mshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); 2425ee68e5bSJavier Cardona 2435ee68e5bSJavier Cardona if (!(mshdr->flags & MESH_FLAGS_AE)) { 2445ee68e5bSJavier Cardona /* size of the fixed part of the mesh header */ 2455ee68e5bSJavier Cardona mesh_hdrlen = 6; 2465ee68e5bSJavier Cardona 2475ee68e5bSJavier Cardona /* make room for the two extended addresses */ 2485ee68e5bSJavier Cardona skb_push(skb, 2 * ETH_ALEN); 2495ee68e5bSJavier Cardona memmove(skb->data, hdr, hdrlen + mesh_hdrlen); 2505ee68e5bSJavier Cardona 2515ee68e5bSJavier Cardona hdr = (struct ieee80211_hdr *) skb->data; 2525ee68e5bSJavier Cardona 2535ee68e5bSJavier Cardona /* we preserve the previous mesh header and only add 2545ee68e5bSJavier Cardona * the new addreses */ 2555ee68e5bSJavier Cardona mshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); 2565ee68e5bSJavier Cardona mshdr->flags = MESH_FLAGS_AE_A5_A6; 2575ee68e5bSJavier Cardona memcpy(mshdr->eaddr1, hdr->addr3, ETH_ALEN); 2585ee68e5bSJavier Cardona memcpy(mshdr->eaddr2, hdr->addr4, ETH_ALEN); 2595ee68e5bSJavier Cardona } 2605ee68e5bSJavier Cardona 2615ee68e5bSJavier Cardona /* update next hop */ 2625ee68e5bSJavier Cardona hdr = (struct ieee80211_hdr *) skb->data; 2635ee68e5bSJavier Cardona rcu_read_lock(); 2645ee68e5bSJavier Cardona next_hop = rcu_dereference(gate_mpath->next_hop)->sta.addr; 2655ee68e5bSJavier Cardona memcpy(hdr->addr1, next_hop, ETH_ALEN); 2665ee68e5bSJavier Cardona rcu_read_unlock(); 2675ee68e5bSJavier Cardona memcpy(hdr->addr3, dst_addr, ETH_ALEN); 2685ee68e5bSJavier Cardona } 2695ee68e5bSJavier Cardona 2705ee68e5bSJavier Cardona /** 2715ee68e5bSJavier Cardona * 2725ee68e5bSJavier Cardona * mesh_path_move_to_queue - Move or copy frames from one mpath queue to another 2735ee68e5bSJavier Cardona * 2745ee68e5bSJavier Cardona * This function is used to transfer or copy frames from an unresolved mpath to 2755ee68e5bSJavier Cardona * a gate mpath. The function also adds the Address Extension field and 2765ee68e5bSJavier Cardona * updates the next hop. 2775ee68e5bSJavier Cardona * 2785ee68e5bSJavier Cardona * If a frame already has an Address Extension field, only the next hop and 2795ee68e5bSJavier Cardona * destination addresses are updated. 2805ee68e5bSJavier Cardona * 2815ee68e5bSJavier Cardona * The gate mpath must be an active mpath with a valid mpath->next_hop. 2825ee68e5bSJavier Cardona * 2835ee68e5bSJavier Cardona * @mpath: An active mpath the frames will be sent to (i.e. the gate) 2845ee68e5bSJavier Cardona * @from_mpath: The failed mpath 2855ee68e5bSJavier Cardona * @copy: When true, copy all the frames to the new mpath queue. When false, 2865ee68e5bSJavier Cardona * move them. 2875ee68e5bSJavier Cardona */ 2885ee68e5bSJavier Cardona static void mesh_path_move_to_queue(struct mesh_path *gate_mpath, 2895ee68e5bSJavier Cardona struct mesh_path *from_mpath, 2905ee68e5bSJavier Cardona bool copy) 2915ee68e5bSJavier Cardona { 292c6133661SThomas Pedersen struct sk_buff *skb, *cp_skb = NULL; 2935ee68e5bSJavier Cardona struct sk_buff_head gateq, failq; 2945ee68e5bSJavier Cardona unsigned long flags; 2955ee68e5bSJavier Cardona int num_skbs; 2965ee68e5bSJavier Cardona 2975ee68e5bSJavier Cardona BUG_ON(gate_mpath == from_mpath); 2985ee68e5bSJavier Cardona BUG_ON(!gate_mpath->next_hop); 2995ee68e5bSJavier Cardona 3005ee68e5bSJavier Cardona __skb_queue_head_init(&gateq); 3015ee68e5bSJavier Cardona __skb_queue_head_init(&failq); 3025ee68e5bSJavier Cardona 3035ee68e5bSJavier Cardona spin_lock_irqsave(&from_mpath->frame_queue.lock, flags); 3045ee68e5bSJavier Cardona skb_queue_splice_init(&from_mpath->frame_queue, &failq); 3055ee68e5bSJavier Cardona spin_unlock_irqrestore(&from_mpath->frame_queue.lock, flags); 3065ee68e5bSJavier Cardona 3075ee68e5bSJavier Cardona num_skbs = skb_queue_len(&failq); 3085ee68e5bSJavier Cardona 3095ee68e5bSJavier Cardona while (num_skbs--) { 3105ee68e5bSJavier Cardona skb = __skb_dequeue(&failq); 311817a53d9SJohn W. Linville if (copy) { 3125ee68e5bSJavier Cardona cp_skb = skb_copy(skb, GFP_ATOMIC); 313817a53d9SJohn W. Linville if (cp_skb) 314817a53d9SJohn W. Linville __skb_queue_tail(&failq, cp_skb); 315817a53d9SJohn W. Linville } 3165ee68e5bSJavier Cardona 3175ee68e5bSJavier Cardona prepare_for_gate(skb, gate_mpath->dst, gate_mpath); 3185ee68e5bSJavier Cardona __skb_queue_tail(&gateq, skb); 3195ee68e5bSJavier Cardona } 3205ee68e5bSJavier Cardona 3215ee68e5bSJavier Cardona spin_lock_irqsave(&gate_mpath->frame_queue.lock, flags); 3225ee68e5bSJavier Cardona skb_queue_splice(&gateq, &gate_mpath->frame_queue); 3235ee68e5bSJavier Cardona mpath_dbg("Mpath queue for gate %pM has %d frames\n", 3245ee68e5bSJavier Cardona gate_mpath->dst, 3255ee68e5bSJavier Cardona skb_queue_len(&gate_mpath->frame_queue)); 3265ee68e5bSJavier Cardona spin_unlock_irqrestore(&gate_mpath->frame_queue.lock, flags); 3275ee68e5bSJavier Cardona 3285ee68e5bSJavier Cardona if (!copy) 3295ee68e5bSJavier Cardona return; 3305ee68e5bSJavier Cardona 3315ee68e5bSJavier Cardona spin_lock_irqsave(&from_mpath->frame_queue.lock, flags); 3325ee68e5bSJavier Cardona skb_queue_splice(&failq, &from_mpath->frame_queue); 3335ee68e5bSJavier Cardona spin_unlock_irqrestore(&from_mpath->frame_queue.lock, flags); 3345ee68e5bSJavier Cardona } 3355ee68e5bSJavier Cardona 336eb2b9311SLuis Carlos Cobo 337239289e4SJavier Cardona static struct mesh_path *path_lookup(struct mesh_table *tbl, u8 *dst, 338239289e4SJavier Cardona struct ieee80211_sub_if_data *sdata) 339239289e4SJavier Cardona { 340239289e4SJavier Cardona struct mesh_path *mpath; 341239289e4SJavier Cardona struct hlist_node *n; 342239289e4SJavier Cardona struct hlist_head *bucket; 343239289e4SJavier Cardona struct mpath_node *node; 344239289e4SJavier Cardona 345239289e4SJavier Cardona bucket = &tbl->hash_buckets[mesh_table_hash(dst, sdata, tbl)]; 346239289e4SJavier Cardona hlist_for_each_entry_rcu(node, n, bucket, list) { 347239289e4SJavier Cardona mpath = node->mpath; 348239289e4SJavier Cardona if (mpath->sdata == sdata && 349239289e4SJavier Cardona memcmp(dst, mpath->dst, ETH_ALEN) == 0) { 350239289e4SJavier Cardona if (MPATH_EXPIRED(mpath)) { 351239289e4SJavier Cardona spin_lock_bh(&mpath->state_lock); 352239289e4SJavier Cardona mpath->flags &= ~MESH_PATH_ACTIVE; 353239289e4SJavier Cardona spin_unlock_bh(&mpath->state_lock); 354239289e4SJavier Cardona } 355239289e4SJavier Cardona return mpath; 356239289e4SJavier Cardona } 357239289e4SJavier Cardona } 358239289e4SJavier Cardona return NULL; 359239289e4SJavier Cardona } 360239289e4SJavier Cardona 361eb2b9311SLuis Carlos Cobo /** 362eb2b9311SLuis Carlos Cobo * mesh_path_lookup - look up a path in the mesh path table 363eb2b9311SLuis Carlos Cobo * @dst: hardware address (ETH_ALEN length) of destination 364f698d856SJasper Bryant-Greene * @sdata: local subif 365eb2b9311SLuis Carlos Cobo * 366eb2b9311SLuis Carlos Cobo * Returns: pointer to the mesh path structure, or NULL if not found 367eb2b9311SLuis Carlos Cobo * 368eb2b9311SLuis Carlos Cobo * Locking: must be called within a read rcu section. 369eb2b9311SLuis Carlos Cobo */ 370f698d856SJasper Bryant-Greene struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) 371eb2b9311SLuis Carlos Cobo { 372239289e4SJavier Cardona return path_lookup(rcu_dereference(mesh_paths), dst, sdata); 373eb2b9311SLuis Carlos Cobo } 374eb2b9311SLuis Carlos Cobo 37579617deeSYanBo struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) 37679617deeSYanBo { 377239289e4SJavier Cardona return path_lookup(rcu_dereference(mpp_paths), dst, sdata); 37879617deeSYanBo } 37979617deeSYanBo 38079617deeSYanBo 381eb2b9311SLuis Carlos Cobo /** 382eb2b9311SLuis Carlos Cobo * mesh_path_lookup_by_idx - look up a path in the mesh path table by its index 383eb2b9311SLuis Carlos Cobo * @idx: index 384f698d856SJasper Bryant-Greene * @sdata: local subif, or NULL for all entries 385eb2b9311SLuis Carlos Cobo * 386eb2b9311SLuis Carlos Cobo * Returns: pointer to the mesh path structure, or NULL if not found. 387eb2b9311SLuis Carlos Cobo * 388eb2b9311SLuis Carlos Cobo * Locking: must be called within a read rcu section. 389eb2b9311SLuis Carlos Cobo */ 390f698d856SJasper Bryant-Greene struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data *sdata) 391eb2b9311SLuis Carlos Cobo { 392349eb8cfSJohannes Berg struct mesh_table *tbl = rcu_dereference(mesh_paths); 393eb2b9311SLuis Carlos Cobo struct mpath_node *node; 394eb2b9311SLuis Carlos Cobo struct hlist_node *p; 395eb2b9311SLuis Carlos Cobo int i; 396eb2b9311SLuis Carlos Cobo int j = 0; 397eb2b9311SLuis Carlos Cobo 398349eb8cfSJohannes Berg for_each_mesh_entry(tbl, p, node, i) { 399f698d856SJasper Bryant-Greene if (sdata && node->mpath->sdata != sdata) 4002a8ca29aSLuis Carlos Cobo continue; 401eb2b9311SLuis Carlos Cobo if (j++ == idx) { 402eb2b9311SLuis Carlos Cobo if (MPATH_EXPIRED(node->mpath)) { 403eb2b9311SLuis Carlos Cobo spin_lock_bh(&node->mpath->state_lock); 404eb2b9311SLuis Carlos Cobo node->mpath->flags &= ~MESH_PATH_ACTIVE; 405eb2b9311SLuis Carlos Cobo spin_unlock_bh(&node->mpath->state_lock); 406eb2b9311SLuis Carlos Cobo } 407eb2b9311SLuis Carlos Cobo return node->mpath; 408eb2b9311SLuis Carlos Cobo } 4092a8ca29aSLuis Carlos Cobo } 410eb2b9311SLuis Carlos Cobo 411eb2b9311SLuis Carlos Cobo return NULL; 412eb2b9311SLuis Carlos Cobo } 413eb2b9311SLuis Carlos Cobo 4145ee68e5bSJavier Cardona static void mesh_gate_node_reclaim(struct rcu_head *rp) 4155ee68e5bSJavier Cardona { 4165ee68e5bSJavier Cardona struct mpath_node *node = container_of(rp, struct mpath_node, rcu); 4175ee68e5bSJavier Cardona kfree(node); 4185ee68e5bSJavier Cardona } 4195ee68e5bSJavier Cardona 4205ee68e5bSJavier Cardona /** 42130be52e4SJohannes Berg * mesh_path_add_gate - add the given mpath to a mesh gate to our path table 42230be52e4SJohannes Berg * @mpath: gate path to add to table 4235ee68e5bSJavier Cardona */ 42430be52e4SJohannes Berg int mesh_path_add_gate(struct mesh_path *mpath) 4255ee68e5bSJavier Cardona { 42630be52e4SJohannes Berg struct mesh_table *tbl; 4275ee68e5bSJavier Cardona struct mpath_node *gate, *new_gate; 4285ee68e5bSJavier Cardona struct hlist_node *n; 4295ee68e5bSJavier Cardona int err; 4305ee68e5bSJavier Cardona 4315ee68e5bSJavier Cardona rcu_read_lock(); 43230be52e4SJohannes Berg tbl = rcu_dereference(mesh_paths); 4335ee68e5bSJavier Cardona 4345ee68e5bSJavier Cardona hlist_for_each_entry_rcu(gate, n, tbl->known_gates, list) 4355ee68e5bSJavier Cardona if (gate->mpath == mpath) { 4365ee68e5bSJavier Cardona err = -EEXIST; 4375ee68e5bSJavier Cardona goto err_rcu; 4385ee68e5bSJavier Cardona } 4395ee68e5bSJavier Cardona 4405ee68e5bSJavier Cardona new_gate = kzalloc(sizeof(struct mpath_node), GFP_ATOMIC); 4415ee68e5bSJavier Cardona if (!new_gate) { 4425ee68e5bSJavier Cardona err = -ENOMEM; 4435ee68e5bSJavier Cardona goto err_rcu; 4445ee68e5bSJavier Cardona } 4455ee68e5bSJavier Cardona 4465ee68e5bSJavier Cardona mpath->is_gate = true; 4475ee68e5bSJavier Cardona mpath->sdata->u.mesh.num_gates++; 4485ee68e5bSJavier Cardona new_gate->mpath = mpath; 4495ee68e5bSJavier Cardona spin_lock_bh(&tbl->gates_lock); 4505ee68e5bSJavier Cardona hlist_add_head_rcu(&new_gate->list, tbl->known_gates); 4515ee68e5bSJavier Cardona spin_unlock_bh(&tbl->gates_lock); 4525ee68e5bSJavier Cardona rcu_read_unlock(); 4535ee68e5bSJavier Cardona mpath_dbg("Mesh path (%s): Recorded new gate: %pM. %d known gates\n", 4545ee68e5bSJavier Cardona mpath->sdata->name, mpath->dst, 4555ee68e5bSJavier Cardona mpath->sdata->u.mesh.num_gates); 4565ee68e5bSJavier Cardona return 0; 4575ee68e5bSJavier Cardona err_rcu: 4585ee68e5bSJavier Cardona rcu_read_unlock(); 4595ee68e5bSJavier Cardona return err; 4605ee68e5bSJavier Cardona } 4615ee68e5bSJavier Cardona 4625ee68e5bSJavier Cardona /** 4635ee68e5bSJavier Cardona * mesh_gate_del - remove a mesh gate from the list of known gates 4645ee68e5bSJavier Cardona * @tbl: table which holds our list of known gates 4655ee68e5bSJavier Cardona * @mpath: gate mpath 4665ee68e5bSJavier Cardona * 4675ee68e5bSJavier Cardona * Returns: 0 on success 4685ee68e5bSJavier Cardona * 4695ee68e5bSJavier Cardona * Locking: must be called inside rcu_read_lock() section 4705ee68e5bSJavier Cardona */ 4715ee68e5bSJavier Cardona static int mesh_gate_del(struct mesh_table *tbl, struct mesh_path *mpath) 4725ee68e5bSJavier Cardona { 4735ee68e5bSJavier Cardona struct mpath_node *gate; 4745ee68e5bSJavier Cardona struct hlist_node *p, *q; 4755ee68e5bSJavier Cardona 4765ee68e5bSJavier Cardona hlist_for_each_entry_safe(gate, p, q, tbl->known_gates, list) 4775ee68e5bSJavier Cardona if (gate->mpath == mpath) { 4785ee68e5bSJavier Cardona spin_lock_bh(&tbl->gates_lock); 4795ee68e5bSJavier Cardona hlist_del_rcu(&gate->list); 4805ee68e5bSJavier Cardona call_rcu(&gate->rcu, mesh_gate_node_reclaim); 4815ee68e5bSJavier Cardona spin_unlock_bh(&tbl->gates_lock); 4825ee68e5bSJavier Cardona mpath->sdata->u.mesh.num_gates--; 4835ee68e5bSJavier Cardona mpath->is_gate = false; 4845ee68e5bSJavier Cardona mpath_dbg("Mesh path (%s): Deleted gate: %pM. " 4855ee68e5bSJavier Cardona "%d known gates\n", mpath->sdata->name, 4865ee68e5bSJavier Cardona mpath->dst, mpath->sdata->u.mesh.num_gates); 4875ee68e5bSJavier Cardona break; 4885ee68e5bSJavier Cardona } 4895ee68e5bSJavier Cardona 4905ee68e5bSJavier Cardona return 0; 4915ee68e5bSJavier Cardona } 4925ee68e5bSJavier Cardona 4935ee68e5bSJavier Cardona /** 4945ee68e5bSJavier Cardona * mesh_gate_num - number of gates known to this interface 4955ee68e5bSJavier Cardona * @sdata: subif data 4965ee68e5bSJavier Cardona */ 4975ee68e5bSJavier Cardona int mesh_gate_num(struct ieee80211_sub_if_data *sdata) 4985ee68e5bSJavier Cardona { 4995ee68e5bSJavier Cardona return sdata->u.mesh.num_gates; 5005ee68e5bSJavier Cardona } 5015ee68e5bSJavier Cardona 502eb2b9311SLuis Carlos Cobo /** 503eb2b9311SLuis Carlos Cobo * mesh_path_add - allocate and add a new path to the mesh path table 504eb2b9311SLuis Carlos Cobo * @addr: destination address of the path (ETH_ALEN length) 505f698d856SJasper Bryant-Greene * @sdata: local subif 506eb2b9311SLuis Carlos Cobo * 507af901ca1SAndré Goddard Rosa * Returns: 0 on success 508eb2b9311SLuis Carlos Cobo * 509eb2b9311SLuis Carlos Cobo * State: the initial state of the new path is set to 0 510eb2b9311SLuis Carlos Cobo */ 511f698d856SJasper Bryant-Greene int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) 512eb2b9311SLuis Carlos Cobo { 51318889231SJavier Cardona struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 51418889231SJavier Cardona struct ieee80211_local *local = sdata->local; 515349eb8cfSJohannes Berg struct mesh_table *tbl; 516eb2b9311SLuis Carlos Cobo struct mesh_path *mpath, *new_mpath; 517eb2b9311SLuis Carlos Cobo struct mpath_node *node, *new_node; 518eb2b9311SLuis Carlos Cobo struct hlist_head *bucket; 519eb2b9311SLuis Carlos Cobo struct hlist_node *n; 520eb2b9311SLuis Carlos Cobo int grow = 0; 521eb2b9311SLuis Carlos Cobo int err = 0; 522eb2b9311SLuis Carlos Cobo u32 hash_idx; 523eb2b9311SLuis Carlos Cobo 52447846c9bSJohannes Berg if (memcmp(dst, sdata->vif.addr, ETH_ALEN) == 0) 525eb2b9311SLuis Carlos Cobo /* never add ourselves as neighbours */ 526eb2b9311SLuis Carlos Cobo return -ENOTSUPP; 527eb2b9311SLuis Carlos Cobo 528eb2b9311SLuis Carlos Cobo if (is_multicast_ether_addr(dst)) 529eb2b9311SLuis Carlos Cobo return -ENOTSUPP; 530eb2b9311SLuis Carlos Cobo 531472dbc45SJohannes Berg if (atomic_add_unless(&sdata->u.mesh.mpaths, 1, MESH_MAX_MPATHS) == 0) 532eb2b9311SLuis Carlos Cobo return -ENOSPC; 533eb2b9311SLuis Carlos Cobo 534402d7752SPavel Emelyanov err = -ENOMEM; 53518889231SJavier Cardona new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC); 536402d7752SPavel Emelyanov if (!new_mpath) 537402d7752SPavel Emelyanov goto err_path_alloc; 538402d7752SPavel Emelyanov 53918889231SJavier Cardona new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC); 540402d7752SPavel Emelyanov if (!new_node) 541402d7752SPavel Emelyanov goto err_node_alloc; 542f84e71a9SPavel Emelyanov 5439b84b808SJavier Cardona read_lock_bh(&pathtbl_resize_lock); 544eb2b9311SLuis Carlos Cobo memcpy(new_mpath->dst, dst, ETH_ALEN); 545f698d856SJasper Bryant-Greene new_mpath->sdata = sdata; 546eb2b9311SLuis Carlos Cobo new_mpath->flags = 0; 547eb2b9311SLuis Carlos Cobo skb_queue_head_init(&new_mpath->frame_queue); 548eb2b9311SLuis Carlos Cobo new_node->mpath = new_mpath; 549eb2b9311SLuis Carlos Cobo new_mpath->timer.data = (unsigned long) new_mpath; 550eb2b9311SLuis Carlos Cobo new_mpath->timer.function = mesh_path_timer; 551eb2b9311SLuis Carlos Cobo new_mpath->exp_time = jiffies; 552eb2b9311SLuis Carlos Cobo spin_lock_init(&new_mpath->state_lock); 553eb2b9311SLuis Carlos Cobo init_timer(&new_mpath->timer); 554eb2b9311SLuis Carlos Cobo 555349eb8cfSJohannes Berg tbl = resize_dereference_mesh_paths(); 556eb2b9311SLuis Carlos Cobo 557349eb8cfSJohannes Berg hash_idx = mesh_table_hash(dst, sdata, tbl); 558349eb8cfSJohannes Berg bucket = &tbl->hash_buckets[hash_idx]; 559349eb8cfSJohannes Berg 560349eb8cfSJohannes Berg spin_lock_bh(&tbl->hashwlock[hash_idx]); 561eb2b9311SLuis Carlos Cobo 562402d7752SPavel Emelyanov err = -EEXIST; 563eb2b9311SLuis Carlos Cobo hlist_for_each_entry(node, n, bucket, list) { 564eb2b9311SLuis Carlos Cobo mpath = node->mpath; 565f698d856SJasper Bryant-Greene if (mpath->sdata == sdata && memcmp(dst, mpath->dst, ETH_ALEN) == 0) 566402d7752SPavel Emelyanov goto err_exists; 567eb2b9311SLuis Carlos Cobo } 568eb2b9311SLuis Carlos Cobo 569eb2b9311SLuis Carlos Cobo hlist_add_head_rcu(&new_node->list, bucket); 570349eb8cfSJohannes Berg if (atomic_inc_return(&tbl->entries) >= 571349eb8cfSJohannes Berg tbl->mean_chain_len * (tbl->hash_mask + 1)) 572eb2b9311SLuis Carlos Cobo grow = 1; 573eb2b9311SLuis Carlos Cobo 574f5ea9120SJohannes Berg mesh_paths_generation++; 575f5ea9120SJohannes Berg 576349eb8cfSJohannes Berg spin_unlock_bh(&tbl->hashwlock[hash_idx]); 5779b84b808SJavier Cardona read_unlock_bh(&pathtbl_resize_lock); 578402d7752SPavel Emelyanov if (grow) { 57918889231SJavier Cardona set_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags); 58064592c8fSJohannes Berg ieee80211_queue_work(&local->hw, &sdata->work); 581eb2b9311SLuis Carlos Cobo } 582402d7752SPavel Emelyanov return 0; 583402d7752SPavel Emelyanov 584402d7752SPavel Emelyanov err_exists: 585349eb8cfSJohannes Berg spin_unlock_bh(&tbl->hashwlock[hash_idx]); 5869b84b808SJavier Cardona read_unlock_bh(&pathtbl_resize_lock); 587402d7752SPavel Emelyanov kfree(new_node); 588402d7752SPavel Emelyanov err_node_alloc: 589402d7752SPavel Emelyanov kfree(new_mpath); 590402d7752SPavel Emelyanov err_path_alloc: 591472dbc45SJohannes Berg atomic_dec(&sdata->u.mesh.mpaths); 592eb2b9311SLuis Carlos Cobo return err; 593eb2b9311SLuis Carlos Cobo } 594eb2b9311SLuis Carlos Cobo 5951928ecabSJohannes Berg static void mesh_table_free_rcu(struct rcu_head *rcu) 5961928ecabSJohannes Berg { 5971928ecabSJohannes Berg struct mesh_table *tbl = container_of(rcu, struct mesh_table, rcu_head); 5981928ecabSJohannes Berg 5991928ecabSJohannes Berg mesh_table_free(tbl, false); 6001928ecabSJohannes Berg } 6011928ecabSJohannes Berg 60218889231SJavier Cardona void mesh_mpath_table_grow(void) 60318889231SJavier Cardona { 60418889231SJavier Cardona struct mesh_table *oldtbl, *newtbl; 60518889231SJavier Cardona 6069b84b808SJavier Cardona write_lock_bh(&pathtbl_resize_lock); 607349eb8cfSJohannes Berg oldtbl = resize_dereference_mesh_paths(); 608349eb8cfSJohannes Berg newtbl = mesh_table_alloc(oldtbl->size_order + 1); 6091928ecabSJohannes Berg if (!newtbl) 6101928ecabSJohannes Berg goto out; 611349eb8cfSJohannes Berg if (mesh_table_grow(oldtbl, newtbl) < 0) { 612a3e6b12cScozybit Inc __mesh_table_free(newtbl); 6131928ecabSJohannes Berg goto out; 61418889231SJavier Cardona } 61518889231SJavier Cardona rcu_assign_pointer(mesh_paths, newtbl); 61618889231SJavier Cardona 6171928ecabSJohannes Berg call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu); 6181928ecabSJohannes Berg 6191928ecabSJohannes Berg out: 6201928ecabSJohannes Berg write_unlock_bh(&pathtbl_resize_lock); 62118889231SJavier Cardona } 62218889231SJavier Cardona 62318889231SJavier Cardona void mesh_mpp_table_grow(void) 62418889231SJavier Cardona { 62518889231SJavier Cardona struct mesh_table *oldtbl, *newtbl; 62618889231SJavier Cardona 6279b84b808SJavier Cardona write_lock_bh(&pathtbl_resize_lock); 628349eb8cfSJohannes Berg oldtbl = resize_dereference_mpp_paths(); 629349eb8cfSJohannes Berg newtbl = mesh_table_alloc(oldtbl->size_order + 1); 6301928ecabSJohannes Berg if (!newtbl) 6311928ecabSJohannes Berg goto out; 632349eb8cfSJohannes Berg if (mesh_table_grow(oldtbl, newtbl) < 0) { 633a3e6b12cScozybit Inc __mesh_table_free(newtbl); 6341928ecabSJohannes Berg goto out; 63518889231SJavier Cardona } 63618889231SJavier Cardona rcu_assign_pointer(mpp_paths, newtbl); 6371928ecabSJohannes Berg call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu); 63818889231SJavier Cardona 6391928ecabSJohannes Berg out: 6401928ecabSJohannes Berg write_unlock_bh(&pathtbl_resize_lock); 64118889231SJavier Cardona } 642eb2b9311SLuis Carlos Cobo 64379617deeSYanBo int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) 64479617deeSYanBo { 64518889231SJavier Cardona struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 64618889231SJavier Cardona struct ieee80211_local *local = sdata->local; 647349eb8cfSJohannes Berg struct mesh_table *tbl; 64879617deeSYanBo struct mesh_path *mpath, *new_mpath; 64979617deeSYanBo struct mpath_node *node, *new_node; 65079617deeSYanBo struct hlist_head *bucket; 65179617deeSYanBo struct hlist_node *n; 65279617deeSYanBo int grow = 0; 65379617deeSYanBo int err = 0; 65479617deeSYanBo u32 hash_idx; 65579617deeSYanBo 65647846c9bSJohannes Berg if (memcmp(dst, sdata->vif.addr, ETH_ALEN) == 0) 65779617deeSYanBo /* never add ourselves as neighbours */ 65879617deeSYanBo return -ENOTSUPP; 65979617deeSYanBo 66079617deeSYanBo if (is_multicast_ether_addr(dst)) 66179617deeSYanBo return -ENOTSUPP; 66279617deeSYanBo 66379617deeSYanBo err = -ENOMEM; 66418889231SJavier Cardona new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC); 66579617deeSYanBo if (!new_mpath) 66679617deeSYanBo goto err_path_alloc; 66779617deeSYanBo 66818889231SJavier Cardona new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC); 66979617deeSYanBo if (!new_node) 67079617deeSYanBo goto err_node_alloc; 67179617deeSYanBo 6729b84b808SJavier Cardona read_lock_bh(&pathtbl_resize_lock); 67379617deeSYanBo memcpy(new_mpath->dst, dst, ETH_ALEN); 67479617deeSYanBo memcpy(new_mpath->mpp, mpp, ETH_ALEN); 67579617deeSYanBo new_mpath->sdata = sdata; 67679617deeSYanBo new_mpath->flags = 0; 67779617deeSYanBo skb_queue_head_init(&new_mpath->frame_queue); 67879617deeSYanBo new_node->mpath = new_mpath; 679c6133661SThomas Pedersen init_timer(&new_mpath->timer); 68079617deeSYanBo new_mpath->exp_time = jiffies; 68179617deeSYanBo spin_lock_init(&new_mpath->state_lock); 68279617deeSYanBo 683349eb8cfSJohannes Berg tbl = resize_dereference_mpp_paths(); 68479617deeSYanBo 685349eb8cfSJohannes Berg hash_idx = mesh_table_hash(dst, sdata, tbl); 686349eb8cfSJohannes Berg bucket = &tbl->hash_buckets[hash_idx]; 687349eb8cfSJohannes Berg 688349eb8cfSJohannes Berg spin_lock_bh(&tbl->hashwlock[hash_idx]); 68979617deeSYanBo 69079617deeSYanBo err = -EEXIST; 69179617deeSYanBo hlist_for_each_entry(node, n, bucket, list) { 69279617deeSYanBo mpath = node->mpath; 69379617deeSYanBo if (mpath->sdata == sdata && memcmp(dst, mpath->dst, ETH_ALEN) == 0) 69479617deeSYanBo goto err_exists; 69579617deeSYanBo } 69679617deeSYanBo 69779617deeSYanBo hlist_add_head_rcu(&new_node->list, bucket); 698349eb8cfSJohannes Berg if (atomic_inc_return(&tbl->entries) >= 699349eb8cfSJohannes Berg tbl->mean_chain_len * (tbl->hash_mask + 1)) 70079617deeSYanBo grow = 1; 70179617deeSYanBo 702349eb8cfSJohannes Berg spin_unlock_bh(&tbl->hashwlock[hash_idx]); 7039b84b808SJavier Cardona read_unlock_bh(&pathtbl_resize_lock); 70479617deeSYanBo if (grow) { 70518889231SJavier Cardona set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags); 70664592c8fSJohannes Berg ieee80211_queue_work(&local->hw, &sdata->work); 70779617deeSYanBo } 70879617deeSYanBo return 0; 70979617deeSYanBo 71079617deeSYanBo err_exists: 711349eb8cfSJohannes Berg spin_unlock_bh(&tbl->hashwlock[hash_idx]); 7129b84b808SJavier Cardona read_unlock_bh(&pathtbl_resize_lock); 71379617deeSYanBo kfree(new_node); 71479617deeSYanBo err_node_alloc: 71579617deeSYanBo kfree(new_mpath); 71679617deeSYanBo err_path_alloc: 71779617deeSYanBo return err; 71879617deeSYanBo } 71979617deeSYanBo 72079617deeSYanBo 721eb2b9311SLuis Carlos Cobo /** 722eb2b9311SLuis Carlos Cobo * mesh_plink_broken - deactivates paths and sends perr when a link breaks 723eb2b9311SLuis Carlos Cobo * 724eb2b9311SLuis Carlos Cobo * @sta: broken peer link 725eb2b9311SLuis Carlos Cobo * 726eb2b9311SLuis Carlos Cobo * This function must be called from the rate control algorithm if enough 727eb2b9311SLuis Carlos Cobo * delivery errors suggest that a peer link is no longer usable. 728eb2b9311SLuis Carlos Cobo */ 729eb2b9311SLuis Carlos Cobo void mesh_plink_broken(struct sta_info *sta) 730eb2b9311SLuis Carlos Cobo { 731349eb8cfSJohannes Berg struct mesh_table *tbl; 73215ff6365SJohannes Berg static const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 733eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 734eb2b9311SLuis Carlos Cobo struct mpath_node *node; 735eb2b9311SLuis Carlos Cobo struct hlist_node *p; 736f698d856SJasper Bryant-Greene struct ieee80211_sub_if_data *sdata = sta->sdata; 737eb2b9311SLuis Carlos Cobo int i; 73825d49e4dSThomas Pedersen __le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_DEST_UNREACHABLE); 739eb2b9311SLuis Carlos Cobo 740eb2b9311SLuis Carlos Cobo rcu_read_lock(); 741349eb8cfSJohannes Berg tbl = rcu_dereference(mesh_paths); 742349eb8cfSJohannes Berg for_each_mesh_entry(tbl, p, node, i) { 743eb2b9311SLuis Carlos Cobo mpath = node->mpath; 744349eb8cfSJohannes Berg if (rcu_dereference(mpath->next_hop) == sta && 745eb2b9311SLuis Carlos Cobo mpath->flags & MESH_PATH_ACTIVE && 746eb2b9311SLuis Carlos Cobo !(mpath->flags & MESH_PATH_FIXED)) { 747f5e50cd0SJavier Cardona spin_lock_bh(&mpath->state_lock); 748eb2b9311SLuis Carlos Cobo mpath->flags &= ~MESH_PATH_ACTIVE; 749d19b3bf6SRui Paulo ++mpath->sn; 750eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 75145904f21SJavier Cardona mesh_path_error_tx(sdata->u.mesh.mshcfg.element_ttl, 75245904f21SJavier Cardona mpath->dst, cpu_to_le32(mpath->sn), 75325d49e4dSThomas Pedersen reason, bcast, sdata); 754f5e50cd0SJavier Cardona } 755eb2b9311SLuis Carlos Cobo } 756eb2b9311SLuis Carlos Cobo rcu_read_unlock(); 757eb2b9311SLuis Carlos Cobo } 758eb2b9311SLuis Carlos Cobo 75919c50b3dSJavier Cardona static void mesh_path_node_reclaim(struct rcu_head *rp) 76019c50b3dSJavier Cardona { 76119c50b3dSJavier Cardona struct mpath_node *node = container_of(rp, struct mpath_node, rcu); 76219c50b3dSJavier Cardona struct ieee80211_sub_if_data *sdata = node->mpath->sdata; 76319c50b3dSJavier Cardona 76419c50b3dSJavier Cardona del_timer_sync(&node->mpath->timer); 76519c50b3dSJavier Cardona atomic_dec(&sdata->u.mesh.mpaths); 76619c50b3dSJavier Cardona kfree(node->mpath); 76719c50b3dSJavier Cardona kfree(node); 76819c50b3dSJavier Cardona } 76919c50b3dSJavier Cardona 77019c50b3dSJavier Cardona /* needs to be called with the corresponding hashwlock taken */ 77119c50b3dSJavier Cardona static void __mesh_path_del(struct mesh_table *tbl, struct mpath_node *node) 77219c50b3dSJavier Cardona { 77319c50b3dSJavier Cardona struct mesh_path *mpath; 77419c50b3dSJavier Cardona mpath = node->mpath; 77519c50b3dSJavier Cardona spin_lock(&mpath->state_lock); 77619c50b3dSJavier Cardona mpath->flags |= MESH_PATH_RESOLVING; 77719c50b3dSJavier Cardona if (mpath->is_gate) 77819c50b3dSJavier Cardona mesh_gate_del(tbl, mpath); 77919c50b3dSJavier Cardona hlist_del_rcu(&node->list); 78019c50b3dSJavier Cardona call_rcu(&node->rcu, mesh_path_node_reclaim); 78119c50b3dSJavier Cardona spin_unlock(&mpath->state_lock); 78219c50b3dSJavier Cardona atomic_dec(&tbl->entries); 78319c50b3dSJavier Cardona } 78419c50b3dSJavier Cardona 785eb2b9311SLuis Carlos Cobo /** 786eb2b9311SLuis Carlos Cobo * mesh_path_flush_by_nexthop - Deletes mesh paths if their next hop matches 787eb2b9311SLuis Carlos Cobo * 788eb2b9311SLuis Carlos Cobo * @sta - mesh peer to match 789eb2b9311SLuis Carlos Cobo * 790b4e08ea1SLuis Carlos Cobo * RCU notes: this function is called when a mesh plink transitions from 791b4e08ea1SLuis Carlos Cobo * PLINK_ESTAB to any other state, since PLINK_ESTAB state is the only one that 792b4e08ea1SLuis Carlos Cobo * allows path creation. This will happen before the sta can be freed (because 793d0709a65SJohannes Berg * sta_info_destroy() calls this) so any reader in a rcu read block will be 794d0709a65SJohannes Berg * protected against the plink disappearing. 795eb2b9311SLuis Carlos Cobo */ 796eb2b9311SLuis Carlos Cobo void mesh_path_flush_by_nexthop(struct sta_info *sta) 797eb2b9311SLuis Carlos Cobo { 798349eb8cfSJohannes Berg struct mesh_table *tbl; 799eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 800eb2b9311SLuis Carlos Cobo struct mpath_node *node; 801eb2b9311SLuis Carlos Cobo struct hlist_node *p; 802eb2b9311SLuis Carlos Cobo int i; 803eb2b9311SLuis Carlos Cobo 804349eb8cfSJohannes Berg rcu_read_lock(); 805239289e4SJavier Cardona read_lock_bh(&pathtbl_resize_lock); 806239289e4SJavier Cardona tbl = resize_dereference_mesh_paths(); 807349eb8cfSJohannes Berg for_each_mesh_entry(tbl, p, node, i) { 808eb2b9311SLuis Carlos Cobo mpath = node->mpath; 809cd72e817SJavier Cardona if (rcu_dereference(mpath->next_hop) == sta) { 81019c50b3dSJavier Cardona spin_lock_bh(&tbl->hashwlock[i]); 81119c50b3dSJavier Cardona __mesh_path_del(tbl, node); 81219c50b3dSJavier Cardona spin_unlock_bh(&tbl->hashwlock[i]); 813eb2b9311SLuis Carlos Cobo } 814eb2b9311SLuis Carlos Cobo } 815239289e4SJavier Cardona read_unlock_bh(&pathtbl_resize_lock); 816eb2b9311SLuis Carlos Cobo rcu_read_unlock(); 817eb2b9311SLuis Carlos Cobo } 818eb2b9311SLuis Carlos Cobo 819cd72e817SJavier Cardona static void table_flush_by_iface(struct mesh_table *tbl, 820cd72e817SJavier Cardona struct ieee80211_sub_if_data *sdata) 821eb2b9311SLuis Carlos Cobo { 822eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 823eb2b9311SLuis Carlos Cobo struct mpath_node *node; 824eb2b9311SLuis Carlos Cobo struct hlist_node *p; 825eb2b9311SLuis Carlos Cobo int i; 826eb2b9311SLuis Carlos Cobo 827cd72e817SJavier Cardona WARN_ON(!rcu_read_lock_held()); 828eb2b9311SLuis Carlos Cobo for_each_mesh_entry(tbl, p, node, i) { 829eb2b9311SLuis Carlos Cobo mpath = node->mpath; 830cd72e817SJavier Cardona if (mpath->sdata != sdata) 831cd72e817SJavier Cardona continue; 832ece1a2e7SJavier Cardona spin_lock_bh(&tbl->hashwlock[i]); 83319c50b3dSJavier Cardona __mesh_path_del(tbl, node); 834ece1a2e7SJavier Cardona spin_unlock_bh(&tbl->hashwlock[i]); 835eb2b9311SLuis Carlos Cobo } 836eb2b9311SLuis Carlos Cobo } 837eb2b9311SLuis Carlos Cobo 838ece1a2e7SJavier Cardona /** 839ece1a2e7SJavier Cardona * mesh_path_flush_by_iface - Deletes all mesh paths associated with a given iface 840ece1a2e7SJavier Cardona * 841ece1a2e7SJavier Cardona * This function deletes both mesh paths as well as mesh portal paths. 842ece1a2e7SJavier Cardona * 843ece1a2e7SJavier Cardona * @sdata - interface data to match 844ece1a2e7SJavier Cardona * 845ece1a2e7SJavier Cardona */ 846ece1a2e7SJavier Cardona void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata) 847eb2b9311SLuis Carlos Cobo { 848cd72e817SJavier Cardona struct mesh_table *tbl; 849d0709a65SJohannes Berg 850cd72e817SJavier Cardona rcu_read_lock(); 851239289e4SJavier Cardona read_lock_bh(&pathtbl_resize_lock); 852239289e4SJavier Cardona tbl = resize_dereference_mesh_paths(); 853cd72e817SJavier Cardona table_flush_by_iface(tbl, sdata); 854239289e4SJavier Cardona tbl = resize_dereference_mpp_paths(); 855cd72e817SJavier Cardona table_flush_by_iface(tbl, sdata); 856239289e4SJavier Cardona read_unlock_bh(&pathtbl_resize_lock); 857cd72e817SJavier Cardona rcu_read_unlock(); 858eb2b9311SLuis Carlos Cobo } 859eb2b9311SLuis Carlos Cobo 860eb2b9311SLuis Carlos Cobo /** 861eb2b9311SLuis Carlos Cobo * mesh_path_del - delete a mesh path from the table 862eb2b9311SLuis Carlos Cobo * 863eb2b9311SLuis Carlos Cobo * @addr: dst address (ETH_ALEN length) 864f698d856SJasper Bryant-Greene * @sdata: local subif 865eb2b9311SLuis Carlos Cobo * 866af901ca1SAndré Goddard Rosa * Returns: 0 if successful 867eb2b9311SLuis Carlos Cobo */ 868f698d856SJasper Bryant-Greene int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) 869eb2b9311SLuis Carlos Cobo { 870349eb8cfSJohannes Berg struct mesh_table *tbl; 871eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 872eb2b9311SLuis Carlos Cobo struct mpath_node *node; 873eb2b9311SLuis Carlos Cobo struct hlist_head *bucket; 874eb2b9311SLuis Carlos Cobo struct hlist_node *n; 875eb2b9311SLuis Carlos Cobo int hash_idx; 876eb2b9311SLuis Carlos Cobo int err = 0; 877eb2b9311SLuis Carlos Cobo 8789b84b808SJavier Cardona read_lock_bh(&pathtbl_resize_lock); 879349eb8cfSJohannes Berg tbl = resize_dereference_mesh_paths(); 880349eb8cfSJohannes Berg hash_idx = mesh_table_hash(addr, sdata, tbl); 881349eb8cfSJohannes Berg bucket = &tbl->hash_buckets[hash_idx]; 882eb2b9311SLuis Carlos Cobo 883349eb8cfSJohannes Berg spin_lock_bh(&tbl->hashwlock[hash_idx]); 884eb2b9311SLuis Carlos Cobo hlist_for_each_entry(node, n, bucket, list) { 885eb2b9311SLuis Carlos Cobo mpath = node->mpath; 886f698d856SJasper Bryant-Greene if (mpath->sdata == sdata && 887eb2b9311SLuis Carlos Cobo memcmp(addr, mpath->dst, ETH_ALEN) == 0) { 88819c50b3dSJavier Cardona __mesh_path_del(tbl, node); 889eb2b9311SLuis Carlos Cobo goto enddel; 890eb2b9311SLuis Carlos Cobo } 891eb2b9311SLuis Carlos Cobo } 892eb2b9311SLuis Carlos Cobo 893eb2b9311SLuis Carlos Cobo err = -ENXIO; 894eb2b9311SLuis Carlos Cobo enddel: 895f5ea9120SJohannes Berg mesh_paths_generation++; 896349eb8cfSJohannes Berg spin_unlock_bh(&tbl->hashwlock[hash_idx]); 8979b84b808SJavier Cardona read_unlock_bh(&pathtbl_resize_lock); 898eb2b9311SLuis Carlos Cobo return err; 899eb2b9311SLuis Carlos Cobo } 900eb2b9311SLuis Carlos Cobo 901eb2b9311SLuis Carlos Cobo /** 902eb2b9311SLuis Carlos Cobo * mesh_path_tx_pending - sends pending frames in a mesh path queue 903eb2b9311SLuis Carlos Cobo * 904eb2b9311SLuis Carlos Cobo * @mpath: mesh path to activate 905eb2b9311SLuis Carlos Cobo * 906eb2b9311SLuis Carlos Cobo * Locking: the state_lock of the mpath structure must NOT be held when calling 907eb2b9311SLuis Carlos Cobo * this function. 908eb2b9311SLuis Carlos Cobo */ 909eb2b9311SLuis Carlos Cobo void mesh_path_tx_pending(struct mesh_path *mpath) 910eb2b9311SLuis Carlos Cobo { 911249b405cSJavier Cardona if (mpath->flags & MESH_PATH_ACTIVE) 912249b405cSJavier Cardona ieee80211_add_pending_skbs(mpath->sdata->local, 913249b405cSJavier Cardona &mpath->frame_queue); 914eb2b9311SLuis Carlos Cobo } 915eb2b9311SLuis Carlos Cobo 916eb2b9311SLuis Carlos Cobo /** 9175ee68e5bSJavier Cardona * mesh_path_send_to_gates - sends pending frames to all known mesh gates 9185ee68e5bSJavier Cardona * 9195ee68e5bSJavier Cardona * @mpath: mesh path whose queue will be emptied 9205ee68e5bSJavier Cardona * 9215ee68e5bSJavier Cardona * If there is only one gate, the frames are transferred from the failed mpath 9225ee68e5bSJavier Cardona * queue to that gate's queue. If there are more than one gates, the frames 9235ee68e5bSJavier Cardona * are copied from each gate to the next. After frames are copied, the 9245ee68e5bSJavier Cardona * mpath queues are emptied onto the transmission queue. 9255ee68e5bSJavier Cardona */ 9265ee68e5bSJavier Cardona int mesh_path_send_to_gates(struct mesh_path *mpath) 9275ee68e5bSJavier Cardona { 9285ee68e5bSJavier Cardona struct ieee80211_sub_if_data *sdata = mpath->sdata; 9295ee68e5bSJavier Cardona struct hlist_node *n; 9305ee68e5bSJavier Cardona struct mesh_table *tbl; 9315ee68e5bSJavier Cardona struct mesh_path *from_mpath = mpath; 9325ee68e5bSJavier Cardona struct mpath_node *gate = NULL; 9335ee68e5bSJavier Cardona bool copy = false; 9345ee68e5bSJavier Cardona struct hlist_head *known_gates; 9355ee68e5bSJavier Cardona 9365ee68e5bSJavier Cardona rcu_read_lock(); 9375ee68e5bSJavier Cardona tbl = rcu_dereference(mesh_paths); 9385ee68e5bSJavier Cardona known_gates = tbl->known_gates; 9395ee68e5bSJavier Cardona rcu_read_unlock(); 9405ee68e5bSJavier Cardona 9415ee68e5bSJavier Cardona if (!known_gates) 9425ee68e5bSJavier Cardona return -EHOSTUNREACH; 9435ee68e5bSJavier Cardona 9445ee68e5bSJavier Cardona hlist_for_each_entry_rcu(gate, n, known_gates, list) { 9455ee68e5bSJavier Cardona if (gate->mpath->sdata != sdata) 9465ee68e5bSJavier Cardona continue; 9475ee68e5bSJavier Cardona 9485ee68e5bSJavier Cardona if (gate->mpath->flags & MESH_PATH_ACTIVE) { 9495ee68e5bSJavier Cardona mpath_dbg("Forwarding to %pM\n", gate->mpath->dst); 9505ee68e5bSJavier Cardona mesh_path_move_to_queue(gate->mpath, from_mpath, copy); 9515ee68e5bSJavier Cardona from_mpath = gate->mpath; 9525ee68e5bSJavier Cardona copy = true; 9535ee68e5bSJavier Cardona } else { 9545ee68e5bSJavier Cardona mpath_dbg("Not forwarding %p\n", gate->mpath); 9555ee68e5bSJavier Cardona mpath_dbg("flags %x\n", gate->mpath->flags); 9565ee68e5bSJavier Cardona } 9575ee68e5bSJavier Cardona } 9585ee68e5bSJavier Cardona 9595ee68e5bSJavier Cardona hlist_for_each_entry_rcu(gate, n, known_gates, list) 9605ee68e5bSJavier Cardona if (gate->mpath->sdata == sdata) { 9615ee68e5bSJavier Cardona mpath_dbg("Sending to %pM\n", gate->mpath->dst); 9625ee68e5bSJavier Cardona mesh_path_tx_pending(gate->mpath); 9635ee68e5bSJavier Cardona } 9645ee68e5bSJavier Cardona 9655ee68e5bSJavier Cardona return (from_mpath == mpath) ? -EHOSTUNREACH : 0; 9665ee68e5bSJavier Cardona } 9675ee68e5bSJavier Cardona 9685ee68e5bSJavier Cardona /** 969eb2b9311SLuis Carlos Cobo * mesh_path_discard_frame - discard a frame whose path could not be resolved 970eb2b9311SLuis Carlos Cobo * 971eb2b9311SLuis Carlos Cobo * @skb: frame to discard 972f698d856SJasper Bryant-Greene * @sdata: network subif the frame was to be sent through 973eb2b9311SLuis Carlos Cobo * 97435946a57SJavier Cardona * If the frame was being forwarded from another MP, a PERR frame will be sent 97535946a57SJavier Cardona * to the precursor. The precursor's address (i.e. the previous hop) was saved 97635946a57SJavier Cardona * in addr1 of the frame-to-be-forwarded, and would only be overwritten once 97735946a57SJavier Cardona * the destination is successfully resolved. 978eb2b9311SLuis Carlos Cobo * 979eb2b9311SLuis Carlos Cobo * Locking: the function must me called within a rcu_read_lock region 980eb2b9311SLuis Carlos Cobo */ 981f698d856SJasper Bryant-Greene void mesh_path_discard_frame(struct sk_buff *skb, 982f698d856SJasper Bryant-Greene struct ieee80211_sub_if_data *sdata) 983eb2b9311SLuis Carlos Cobo { 984e32f85f7SLuis Carlos Cobo struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; 985eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 986d19b3bf6SRui Paulo u32 sn = 0; 98725d49e4dSThomas Pedersen __le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_NOFORWARD); 988eb2b9311SLuis Carlos Cobo 98947846c9bSJohannes Berg if (memcmp(hdr->addr4, sdata->vif.addr, ETH_ALEN) != 0) { 990eb2b9311SLuis Carlos Cobo u8 *ra, *da; 991eb2b9311SLuis Carlos Cobo 992e32f85f7SLuis Carlos Cobo da = hdr->addr3; 99335946a57SJavier Cardona ra = hdr->addr1; 994af089c15SJavier Cardona rcu_read_lock(); 995f698d856SJasper Bryant-Greene mpath = mesh_path_lookup(da, sdata); 996af089c15SJavier Cardona if (mpath) { 997af089c15SJavier Cardona spin_lock_bh(&mpath->state_lock); 998d19b3bf6SRui Paulo sn = ++mpath->sn; 999af089c15SJavier Cardona spin_unlock_bh(&mpath->state_lock); 1000af089c15SJavier Cardona } 1001af089c15SJavier Cardona rcu_read_unlock(); 100245904f21SJavier Cardona mesh_path_error_tx(sdata->u.mesh.mshcfg.element_ttl, skb->data, 100325d49e4dSThomas Pedersen cpu_to_le32(sn), reason, ra, sdata); 1004eb2b9311SLuis Carlos Cobo } 1005eb2b9311SLuis Carlos Cobo 1006eb2b9311SLuis Carlos Cobo kfree_skb(skb); 1007472dbc45SJohannes Berg sdata->u.mesh.mshstats.dropped_frames_no_route++; 1008eb2b9311SLuis Carlos Cobo } 1009eb2b9311SLuis Carlos Cobo 1010eb2b9311SLuis Carlos Cobo /** 1011eb2b9311SLuis Carlos Cobo * mesh_path_flush_pending - free the pending queue of a mesh path 1012eb2b9311SLuis Carlos Cobo * 1013eb2b9311SLuis Carlos Cobo * @mpath: mesh path whose queue has to be freed 1014eb2b9311SLuis Carlos Cobo * 101525985edcSLucas De Marchi * Locking: the function must me called within a rcu_read_lock region 1016eb2b9311SLuis Carlos Cobo */ 1017eb2b9311SLuis Carlos Cobo void mesh_path_flush_pending(struct mesh_path *mpath) 1018eb2b9311SLuis Carlos Cobo { 1019eb2b9311SLuis Carlos Cobo struct sk_buff *skb; 1020eb2b9311SLuis Carlos Cobo 102100e3f25cSJavier Cardona while ((skb = skb_dequeue(&mpath->frame_queue)) != NULL) 1022f698d856SJasper Bryant-Greene mesh_path_discard_frame(skb, mpath->sdata); 1023eb2b9311SLuis Carlos Cobo } 1024eb2b9311SLuis Carlos Cobo 1025eb2b9311SLuis Carlos Cobo /** 1026eb2b9311SLuis Carlos Cobo * mesh_path_fix_nexthop - force a specific next hop for a mesh path 1027eb2b9311SLuis Carlos Cobo * 1028eb2b9311SLuis Carlos Cobo * @mpath: the mesh path to modify 1029eb2b9311SLuis Carlos Cobo * @next_hop: the next hop to force 1030eb2b9311SLuis Carlos Cobo * 1031eb2b9311SLuis Carlos Cobo * Locking: this function must be called holding mpath->state_lock 1032eb2b9311SLuis Carlos Cobo */ 1033eb2b9311SLuis Carlos Cobo void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop) 1034eb2b9311SLuis Carlos Cobo { 1035eb2b9311SLuis Carlos Cobo spin_lock_bh(&mpath->state_lock); 1036eb2b9311SLuis Carlos Cobo mesh_path_assign_nexthop(mpath, next_hop); 1037d19b3bf6SRui Paulo mpath->sn = 0xffff; 1038eb2b9311SLuis Carlos Cobo mpath->metric = 0; 1039eb2b9311SLuis Carlos Cobo mpath->hop_count = 0; 1040eb2b9311SLuis Carlos Cobo mpath->exp_time = 0; 1041eb2b9311SLuis Carlos Cobo mpath->flags |= MESH_PATH_FIXED; 1042eb2b9311SLuis Carlos Cobo mesh_path_activate(mpath); 1043eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 1044eb2b9311SLuis Carlos Cobo mesh_path_tx_pending(mpath); 1045eb2b9311SLuis Carlos Cobo } 1046eb2b9311SLuis Carlos Cobo 1047eb2b9311SLuis Carlos Cobo static void mesh_path_node_free(struct hlist_node *p, bool free_leafs) 1048eb2b9311SLuis Carlos Cobo { 1049eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 1050eb2b9311SLuis Carlos Cobo struct mpath_node *node = hlist_entry(p, struct mpath_node, list); 1051eb2b9311SLuis Carlos Cobo mpath = node->mpath; 1052eb2b9311SLuis Carlos Cobo hlist_del_rcu(p); 1053d0df9eecSJavier Cardona if (free_leafs) { 1054d0df9eecSJavier Cardona del_timer_sync(&mpath->timer); 1055eb2b9311SLuis Carlos Cobo kfree(mpath); 1056d0df9eecSJavier Cardona } 1057eb2b9311SLuis Carlos Cobo kfree(node); 1058eb2b9311SLuis Carlos Cobo } 1059eb2b9311SLuis Carlos Cobo 10604caf86c6SPavel Emelyanov static int mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl) 1061eb2b9311SLuis Carlos Cobo { 1062eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 1063eb2b9311SLuis Carlos Cobo struct mpath_node *node, *new_node; 1064eb2b9311SLuis Carlos Cobo u32 hash_idx; 1065eb2b9311SLuis Carlos Cobo 10668566dc3fSPavel Emelyanov new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC); 106700242c40SPavel Emelyanov if (new_node == NULL) 106800242c40SPavel Emelyanov return -ENOMEM; 106900242c40SPavel Emelyanov 1070eb2b9311SLuis Carlos Cobo node = hlist_entry(p, struct mpath_node, list); 1071eb2b9311SLuis Carlos Cobo mpath = node->mpath; 1072eb2b9311SLuis Carlos Cobo new_node->mpath = mpath; 1073f698d856SJasper Bryant-Greene hash_idx = mesh_table_hash(mpath->dst, mpath->sdata, newtbl); 1074eb2b9311SLuis Carlos Cobo hlist_add_head(&new_node->list, 1075eb2b9311SLuis Carlos Cobo &newtbl->hash_buckets[hash_idx]); 10764caf86c6SPavel Emelyanov return 0; 1077eb2b9311SLuis Carlos Cobo } 1078eb2b9311SLuis Carlos Cobo 1079eb2b9311SLuis Carlos Cobo int mesh_pathtbl_init(void) 1080eb2b9311SLuis Carlos Cobo { 1081349eb8cfSJohannes Berg struct mesh_table *tbl_path, *tbl_mpp; 10824c5ade41SDan Carpenter int ret; 108379617deeSYanBo 1084349eb8cfSJohannes Berg tbl_path = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); 1085349eb8cfSJohannes Berg if (!tbl_path) 1086349eb8cfSJohannes Berg return -ENOMEM; 1087349eb8cfSJohannes Berg tbl_path->free_node = &mesh_path_node_free; 1088349eb8cfSJohannes Berg tbl_path->copy_node = &mesh_path_node_copy; 1089349eb8cfSJohannes Berg tbl_path->mean_chain_len = MEAN_CHAIN_LEN; 10905ee68e5bSJavier Cardona tbl_path->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC); 10914c5ade41SDan Carpenter if (!tbl_path->known_gates) { 10924c5ade41SDan Carpenter ret = -ENOMEM; 10934c5ade41SDan Carpenter goto free_path; 10944c5ade41SDan Carpenter } 10955ee68e5bSJavier Cardona INIT_HLIST_HEAD(tbl_path->known_gates); 10965ee68e5bSJavier Cardona 1097349eb8cfSJohannes Berg 1098349eb8cfSJohannes Berg tbl_mpp = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); 1099349eb8cfSJohannes Berg if (!tbl_mpp) { 11004c5ade41SDan Carpenter ret = -ENOMEM; 11014c5ade41SDan Carpenter goto free_path; 110279617deeSYanBo } 1103349eb8cfSJohannes Berg tbl_mpp->free_node = &mesh_path_node_free; 1104349eb8cfSJohannes Berg tbl_mpp->copy_node = &mesh_path_node_copy; 1105349eb8cfSJohannes Berg tbl_mpp->mean_chain_len = MEAN_CHAIN_LEN; 11065ee68e5bSJavier Cardona tbl_mpp->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC); 11074c5ade41SDan Carpenter if (!tbl_mpp->known_gates) { 11084c5ade41SDan Carpenter ret = -ENOMEM; 11094c5ade41SDan Carpenter goto free_mpp; 11104c5ade41SDan Carpenter } 11115ee68e5bSJavier Cardona INIT_HLIST_HEAD(tbl_mpp->known_gates); 1112349eb8cfSJohannes Berg 1113349eb8cfSJohannes Berg /* Need no locking since this is during init */ 1114349eb8cfSJohannes Berg RCU_INIT_POINTER(mesh_paths, tbl_path); 1115349eb8cfSJohannes Berg RCU_INIT_POINTER(mpp_paths, tbl_mpp); 111679617deeSYanBo 1117eb2b9311SLuis Carlos Cobo return 0; 11184c5ade41SDan Carpenter 11194c5ade41SDan Carpenter free_mpp: 11204c5ade41SDan Carpenter mesh_table_free(tbl_mpp, true); 11214c5ade41SDan Carpenter free_path: 11224c5ade41SDan Carpenter mesh_table_free(tbl_path, true); 11234c5ade41SDan Carpenter return ret; 1124eb2b9311SLuis Carlos Cobo } 1125eb2b9311SLuis Carlos Cobo 1126f698d856SJasper Bryant-Greene void mesh_path_expire(struct ieee80211_sub_if_data *sdata) 1127eb2b9311SLuis Carlos Cobo { 1128349eb8cfSJohannes Berg struct mesh_table *tbl; 1129eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 1130eb2b9311SLuis Carlos Cobo struct mpath_node *node; 1131eb2b9311SLuis Carlos Cobo struct hlist_node *p; 1132eb2b9311SLuis Carlos Cobo int i; 1133eb2b9311SLuis Carlos Cobo 1134349eb8cfSJohannes Berg rcu_read_lock(); 1135349eb8cfSJohannes Berg tbl = rcu_dereference(mesh_paths); 1136349eb8cfSJohannes Berg for_each_mesh_entry(tbl, p, node, i) { 1137f698d856SJasper Bryant-Greene if (node->mpath->sdata != sdata) 1138eb2b9311SLuis Carlos Cobo continue; 1139eb2b9311SLuis Carlos Cobo mpath = node->mpath; 1140eb2b9311SLuis Carlos Cobo if ((!(mpath->flags & MESH_PATH_RESOLVING)) && 1141eb2b9311SLuis Carlos Cobo (!(mpath->flags & MESH_PATH_FIXED)) && 1142f5e50cd0SJavier Cardona time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE)) 1143f698d856SJasper Bryant-Greene mesh_path_del(mpath->dst, mpath->sdata); 1144eb2b9311SLuis Carlos Cobo } 1145349eb8cfSJohannes Berg rcu_read_unlock(); 1146eb2b9311SLuis Carlos Cobo } 1147eb2b9311SLuis Carlos Cobo 1148eb2b9311SLuis Carlos Cobo void mesh_pathtbl_unregister(void) 1149eb2b9311SLuis Carlos Cobo { 1150349eb8cfSJohannes Berg /* no need for locking during exit path */ 115133d480ceSEric Dumazet mesh_table_free(rcu_dereference_protected(mesh_paths, 1), true); 115233d480ceSEric Dumazet mesh_table_free(rcu_dereference_protected(mpp_paths, 1), true); 1153eb2b9311SLuis Carlos Cobo } 1154