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 21eb2b9311SLuis Carlos Cobo /* There will be initially 2^INIT_PATHS_SIZE_ORDER buckets */ 22eb2b9311SLuis Carlos Cobo #define INIT_PATHS_SIZE_ORDER 2 23eb2b9311SLuis Carlos Cobo 24eb2b9311SLuis Carlos Cobo /* Keep the mean chain length below this constant */ 25eb2b9311SLuis Carlos Cobo #define MEAN_CHAIN_LEN 2 26eb2b9311SLuis Carlos Cobo 27eb2b9311SLuis Carlos Cobo #define MPATH_EXPIRED(mpath) ((mpath->flags & MESH_PATH_ACTIVE) && \ 28eb2b9311SLuis Carlos Cobo time_after(jiffies, mpath->exp_time) && \ 29eb2b9311SLuis Carlos Cobo !(mpath->flags & MESH_PATH_FIXED)) 30eb2b9311SLuis Carlos Cobo 31eb2b9311SLuis Carlos Cobo struct mpath_node { 32eb2b9311SLuis Carlos Cobo struct hlist_node list; 33eb2b9311SLuis Carlos Cobo struct rcu_head rcu; 34eb2b9311SLuis Carlos Cobo /* This indirection allows two different tables to point to the same 35eb2b9311SLuis Carlos Cobo * mesh_path structure, useful when resizing 36eb2b9311SLuis Carlos Cobo */ 37eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 38eb2b9311SLuis Carlos Cobo }; 39eb2b9311SLuis Carlos Cobo 40349eb8cfSJohannes Berg static struct mesh_table __rcu *mesh_paths; 41349eb8cfSJohannes Berg static struct mesh_table __rcu *mpp_paths; /* Store paths for MPP&MAP */ 42eb2b9311SLuis Carlos Cobo 43f5ea9120SJohannes Berg int mesh_paths_generation; 446b86bd62SJohannes Berg 456b86bd62SJohannes Berg /* This lock will have the grow table function as writer and add / delete nodes 46239289e4SJavier Cardona * as readers. RCU provides sufficient protection only when reading the table 47239289e4SJavier Cardona * (i.e. doing lookups). Adding or adding or removing nodes requires we take 48239289e4SJavier Cardona * the read lock or we risk operating on an old table. The write lock is only 49239289e4SJavier Cardona * needed when modifying the number of buckets a table. 506b86bd62SJohannes Berg */ 516b86bd62SJohannes Berg static DEFINE_RWLOCK(pathtbl_resize_lock); 526b86bd62SJohannes Berg 536b86bd62SJohannes Berg 54349eb8cfSJohannes Berg static inline struct mesh_table *resize_dereference_mesh_paths(void) 55349eb8cfSJohannes Berg { 56349eb8cfSJohannes Berg return rcu_dereference_protected(mesh_paths, 57349eb8cfSJohannes Berg lockdep_is_held(&pathtbl_resize_lock)); 58349eb8cfSJohannes Berg } 59349eb8cfSJohannes Berg 60349eb8cfSJohannes Berg static inline struct mesh_table *resize_dereference_mpp_paths(void) 61349eb8cfSJohannes Berg { 62349eb8cfSJohannes Berg return rcu_dereference_protected(mpp_paths, 63349eb8cfSJohannes Berg lockdep_is_held(&pathtbl_resize_lock)); 64349eb8cfSJohannes Berg } 65349eb8cfSJohannes Berg 66349eb8cfSJohannes Berg /* 67349eb8cfSJohannes Berg * CAREFUL -- "tbl" must not be an expression, 68349eb8cfSJohannes Berg * in particular not an rcu_dereference(), since 69349eb8cfSJohannes Berg * it's used twice. So it is illegal to do 70349eb8cfSJohannes Berg * for_each_mesh_entry(rcu_dereference(...), ...) 71349eb8cfSJohannes Berg */ 72349eb8cfSJohannes Berg #define for_each_mesh_entry(tbl, p, node, i) \ 73349eb8cfSJohannes Berg for (i = 0; i <= tbl->hash_mask; i++) \ 74349eb8cfSJohannes Berg hlist_for_each_entry_rcu(node, p, &tbl->hash_buckets[i], list) 75349eb8cfSJohannes Berg 76349eb8cfSJohannes Berg 776b86bd62SJohannes Berg static struct mesh_table *mesh_table_alloc(int size_order) 786b86bd62SJohannes Berg { 796b86bd62SJohannes Berg int i; 806b86bd62SJohannes Berg struct mesh_table *newtbl; 816b86bd62SJohannes Berg 82d676ff49SJavier Cardona newtbl = kmalloc(sizeof(struct mesh_table), GFP_ATOMIC); 836b86bd62SJohannes Berg if (!newtbl) 846b86bd62SJohannes Berg return NULL; 856b86bd62SJohannes Berg 866b86bd62SJohannes Berg newtbl->hash_buckets = kzalloc(sizeof(struct hlist_head) * 87d676ff49SJavier Cardona (1 << size_order), GFP_ATOMIC); 886b86bd62SJohannes Berg 896b86bd62SJohannes Berg if (!newtbl->hash_buckets) { 906b86bd62SJohannes Berg kfree(newtbl); 916b86bd62SJohannes Berg return NULL; 926b86bd62SJohannes Berg } 936b86bd62SJohannes Berg 946b86bd62SJohannes Berg newtbl->hashwlock = kmalloc(sizeof(spinlock_t) * 95d676ff49SJavier Cardona (1 << size_order), GFP_ATOMIC); 966b86bd62SJohannes Berg if (!newtbl->hashwlock) { 976b86bd62SJohannes Berg kfree(newtbl->hash_buckets); 986b86bd62SJohannes Berg kfree(newtbl); 996b86bd62SJohannes Berg return NULL; 1006b86bd62SJohannes Berg } 1016b86bd62SJohannes Berg 1026b86bd62SJohannes Berg newtbl->size_order = size_order; 1036b86bd62SJohannes Berg newtbl->hash_mask = (1 << size_order) - 1; 1046b86bd62SJohannes Berg atomic_set(&newtbl->entries, 0); 1056b86bd62SJohannes Berg get_random_bytes(&newtbl->hash_rnd, 1066b86bd62SJohannes Berg sizeof(newtbl->hash_rnd)); 1076b86bd62SJohannes Berg for (i = 0; i <= newtbl->hash_mask; i++) 1086b86bd62SJohannes Berg spin_lock_init(&newtbl->hashwlock[i]); 1095ee68e5bSJavier Cardona spin_lock_init(&newtbl->gates_lock); 1106b86bd62SJohannes Berg 1116b86bd62SJohannes Berg return newtbl; 1126b86bd62SJohannes Berg } 1136b86bd62SJohannes Berg 11418889231SJavier Cardona static void __mesh_table_free(struct mesh_table *tbl) 11518889231SJavier Cardona { 11618889231SJavier Cardona kfree(tbl->hash_buckets); 11718889231SJavier Cardona kfree(tbl->hashwlock); 11818889231SJavier Cardona kfree(tbl); 11918889231SJavier Cardona } 12018889231SJavier Cardona 1216b86bd62SJohannes Berg static void mesh_table_free(struct mesh_table *tbl, bool free_leafs) 12218889231SJavier Cardona { 12318889231SJavier Cardona struct hlist_head *mesh_hash; 12418889231SJavier Cardona struct hlist_node *p, *q; 1255ee68e5bSJavier Cardona struct mpath_node *gate; 12618889231SJavier Cardona int i; 12718889231SJavier Cardona 12818889231SJavier Cardona mesh_hash = tbl->hash_buckets; 12918889231SJavier Cardona for (i = 0; i <= tbl->hash_mask; i++) { 1309b84b808SJavier Cardona spin_lock_bh(&tbl->hashwlock[i]); 13118889231SJavier Cardona hlist_for_each_safe(p, q, &mesh_hash[i]) { 13218889231SJavier Cardona tbl->free_node(p, free_leafs); 13318889231SJavier Cardona atomic_dec(&tbl->entries); 13418889231SJavier Cardona } 1359b84b808SJavier Cardona spin_unlock_bh(&tbl->hashwlock[i]); 13618889231SJavier Cardona } 1375ee68e5bSJavier Cardona if (free_leafs) { 1385ee68e5bSJavier Cardona spin_lock_bh(&tbl->gates_lock); 1395ee68e5bSJavier Cardona hlist_for_each_entry_safe(gate, p, q, 1405ee68e5bSJavier Cardona tbl->known_gates, list) { 1415ee68e5bSJavier Cardona hlist_del(&gate->list); 1425ee68e5bSJavier Cardona kfree(gate); 1435ee68e5bSJavier Cardona } 1445ee68e5bSJavier Cardona kfree(tbl->known_gates); 1455ee68e5bSJavier Cardona spin_unlock_bh(&tbl->gates_lock); 1465ee68e5bSJavier Cardona } 1475ee68e5bSJavier Cardona 14818889231SJavier Cardona __mesh_table_free(tbl); 14918889231SJavier Cardona } 15018889231SJavier Cardona 151a3e6b12cScozybit Inc static int mesh_table_grow(struct mesh_table *oldtbl, 152a3e6b12cScozybit Inc struct mesh_table *newtbl) 15318889231SJavier Cardona { 15418889231SJavier Cardona struct hlist_head *oldhash; 15518889231SJavier Cardona struct hlist_node *p, *q; 15618889231SJavier Cardona int i; 15718889231SJavier Cardona 158a3e6b12cScozybit Inc if (atomic_read(&oldtbl->entries) 159a3e6b12cScozybit Inc < oldtbl->mean_chain_len * (oldtbl->hash_mask + 1)) 160a3e6b12cScozybit Inc return -EAGAIN; 16118889231SJavier Cardona 162a3e6b12cScozybit Inc newtbl->free_node = oldtbl->free_node; 163a3e6b12cScozybit Inc newtbl->mean_chain_len = oldtbl->mean_chain_len; 164a3e6b12cScozybit Inc newtbl->copy_node = oldtbl->copy_node; 1655ee68e5bSJavier Cardona newtbl->known_gates = oldtbl->known_gates; 166a3e6b12cScozybit Inc atomic_set(&newtbl->entries, atomic_read(&oldtbl->entries)); 16718889231SJavier Cardona 168a3e6b12cScozybit Inc oldhash = oldtbl->hash_buckets; 169a3e6b12cScozybit Inc for (i = 0; i <= oldtbl->hash_mask; i++) 17018889231SJavier Cardona hlist_for_each(p, &oldhash[i]) 171a3e6b12cScozybit Inc if (oldtbl->copy_node(p, newtbl) < 0) 17218889231SJavier Cardona goto errcopy; 17318889231SJavier Cardona 174a3e6b12cScozybit Inc return 0; 17518889231SJavier Cardona 17618889231SJavier Cardona errcopy: 17718889231SJavier Cardona for (i = 0; i <= newtbl->hash_mask; i++) { 17818889231SJavier Cardona hlist_for_each_safe(p, q, &newtbl->hash_buckets[i]) 179a3e6b12cScozybit Inc oldtbl->free_node(p, 0); 18018889231SJavier Cardona } 181a3e6b12cScozybit Inc return -ENOMEM; 18218889231SJavier Cardona } 18318889231SJavier Cardona 1846b86bd62SJohannes Berg static u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata, 1856b86bd62SJohannes Berg struct mesh_table *tbl) 1866b86bd62SJohannes Berg { 1876b86bd62SJohannes Berg /* Use last four bytes of hw addr and interface index as hash index */ 1886b86bd62SJohannes Berg return jhash_2words(*(u32 *)(addr+2), sdata->dev->ifindex, tbl->hash_rnd) 1896b86bd62SJohannes Berg & tbl->hash_mask; 1906b86bd62SJohannes Berg } 191f5ea9120SJohannes Berg 192eb2b9311SLuis Carlos Cobo 193eb2b9311SLuis Carlos Cobo /** 194eb2b9311SLuis Carlos Cobo * 195eb2b9311SLuis Carlos Cobo * mesh_path_assign_nexthop - update mesh path next hop 196eb2b9311SLuis Carlos Cobo * 197eb2b9311SLuis Carlos Cobo * @mpath: mesh path to update 198eb2b9311SLuis Carlos Cobo * @sta: next hop to assign 199eb2b9311SLuis Carlos Cobo * 200eb2b9311SLuis Carlos Cobo * Locking: mpath->state_lock must be held when calling this function 201eb2b9311SLuis Carlos Cobo */ 202eb2b9311SLuis Carlos Cobo void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) 203eb2b9311SLuis Carlos Cobo { 20410c836d7SJavier Cardona struct sk_buff *skb; 20510c836d7SJavier Cardona struct ieee80211_hdr *hdr; 20610c836d7SJavier Cardona unsigned long flags; 20710c836d7SJavier Cardona 208d0709a65SJohannes Berg rcu_assign_pointer(mpath->next_hop, sta); 20910c836d7SJavier Cardona 21010c836d7SJavier Cardona spin_lock_irqsave(&mpath->frame_queue.lock, flags); 211b22bd522SThomas Pedersen skb_queue_walk(&mpath->frame_queue, skb) { 21210c836d7SJavier Cardona hdr = (struct ieee80211_hdr *) skb->data; 21310c836d7SJavier Cardona memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN); 2147e3c8866SThomas Pedersen memcpy(hdr->addr2, mpath->sdata->vif.addr, ETH_ALEN); 2153f52b7e3SMarco Porsch ieee80211_mps_set_frame_flags(sta->sdata, sta, hdr); 21610c836d7SJavier Cardona } 21710c836d7SJavier Cardona 21810c836d7SJavier Cardona spin_unlock_irqrestore(&mpath->frame_queue.lock, flags); 219eb2b9311SLuis Carlos Cobo } 220eb2b9311SLuis Carlos Cobo 2215ee68e5bSJavier Cardona static void prepare_for_gate(struct sk_buff *skb, char *dst_addr, 2225ee68e5bSJavier Cardona struct mesh_path *gate_mpath) 2235ee68e5bSJavier Cardona { 2245ee68e5bSJavier Cardona struct ieee80211_hdr *hdr; 2255ee68e5bSJavier Cardona struct ieee80211s_hdr *mshdr; 2265ee68e5bSJavier Cardona int mesh_hdrlen, hdrlen; 2275ee68e5bSJavier Cardona char *next_hop; 2285ee68e5bSJavier Cardona 2295ee68e5bSJavier Cardona hdr = (struct ieee80211_hdr *) skb->data; 2305ee68e5bSJavier Cardona hdrlen = ieee80211_hdrlen(hdr->frame_control); 2315ee68e5bSJavier Cardona mshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); 2325ee68e5bSJavier Cardona 2335ee68e5bSJavier Cardona if (!(mshdr->flags & MESH_FLAGS_AE)) { 2345ee68e5bSJavier Cardona /* size of the fixed part of the mesh header */ 2355ee68e5bSJavier Cardona mesh_hdrlen = 6; 2365ee68e5bSJavier Cardona 2375ee68e5bSJavier Cardona /* make room for the two extended addresses */ 2385ee68e5bSJavier Cardona skb_push(skb, 2 * ETH_ALEN); 2395ee68e5bSJavier Cardona memmove(skb->data, hdr, hdrlen + mesh_hdrlen); 2405ee68e5bSJavier Cardona 2415ee68e5bSJavier Cardona hdr = (struct ieee80211_hdr *) skb->data; 2425ee68e5bSJavier Cardona 2435ee68e5bSJavier Cardona /* we preserve the previous mesh header and only add 2445ee68e5bSJavier Cardona * the new addreses */ 2455ee68e5bSJavier Cardona mshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); 2465ee68e5bSJavier Cardona mshdr->flags = MESH_FLAGS_AE_A5_A6; 2475ee68e5bSJavier Cardona memcpy(mshdr->eaddr1, hdr->addr3, ETH_ALEN); 2485ee68e5bSJavier Cardona memcpy(mshdr->eaddr2, hdr->addr4, ETH_ALEN); 2495ee68e5bSJavier Cardona } 2505ee68e5bSJavier Cardona 2515ee68e5bSJavier Cardona /* update next hop */ 2525ee68e5bSJavier Cardona hdr = (struct ieee80211_hdr *) skb->data; 2535ee68e5bSJavier Cardona rcu_read_lock(); 2545ee68e5bSJavier Cardona next_hop = rcu_dereference(gate_mpath->next_hop)->sta.addr; 2555ee68e5bSJavier Cardona memcpy(hdr->addr1, next_hop, ETH_ALEN); 2565ee68e5bSJavier Cardona rcu_read_unlock(); 2577e3c8866SThomas Pedersen memcpy(hdr->addr2, gate_mpath->sdata->vif.addr, ETH_ALEN); 2585ee68e5bSJavier Cardona memcpy(hdr->addr3, dst_addr, ETH_ALEN); 2595ee68e5bSJavier Cardona } 2605ee68e5bSJavier Cardona 2615ee68e5bSJavier Cardona /** 2625ee68e5bSJavier Cardona * 2635ee68e5bSJavier Cardona * mesh_path_move_to_queue - Move or copy frames from one mpath queue to another 2645ee68e5bSJavier Cardona * 2655ee68e5bSJavier Cardona * This function is used to transfer or copy frames from an unresolved mpath to 2665ee68e5bSJavier Cardona * a gate mpath. The function also adds the Address Extension field and 2675ee68e5bSJavier Cardona * updates the next hop. 2685ee68e5bSJavier Cardona * 2695ee68e5bSJavier Cardona * If a frame already has an Address Extension field, only the next hop and 2705ee68e5bSJavier Cardona * destination addresses are updated. 2715ee68e5bSJavier Cardona * 2725ee68e5bSJavier Cardona * The gate mpath must be an active mpath with a valid mpath->next_hop. 2735ee68e5bSJavier Cardona * 2745ee68e5bSJavier Cardona * @mpath: An active mpath the frames will be sent to (i.e. the gate) 2755ee68e5bSJavier Cardona * @from_mpath: The failed mpath 2765ee68e5bSJavier Cardona * @copy: When true, copy all the frames to the new mpath queue. When false, 2775ee68e5bSJavier Cardona * move them. 2785ee68e5bSJavier Cardona */ 2795ee68e5bSJavier Cardona static void mesh_path_move_to_queue(struct mesh_path *gate_mpath, 2805ee68e5bSJavier Cardona struct mesh_path *from_mpath, 2815ee68e5bSJavier Cardona bool copy) 2825ee68e5bSJavier Cardona { 2834bd4c2ddSThomas Pedersen struct sk_buff *skb, *fskb, *tmp; 2844bd4c2ddSThomas Pedersen struct sk_buff_head failq; 2855ee68e5bSJavier Cardona unsigned long flags; 2865ee68e5bSJavier Cardona 2875ee68e5bSJavier Cardona BUG_ON(gate_mpath == from_mpath); 2885ee68e5bSJavier Cardona BUG_ON(!gate_mpath->next_hop); 2895ee68e5bSJavier Cardona 2905ee68e5bSJavier Cardona __skb_queue_head_init(&failq); 2915ee68e5bSJavier Cardona 2925ee68e5bSJavier Cardona spin_lock_irqsave(&from_mpath->frame_queue.lock, flags); 2935ee68e5bSJavier Cardona skb_queue_splice_init(&from_mpath->frame_queue, &failq); 2945ee68e5bSJavier Cardona spin_unlock_irqrestore(&from_mpath->frame_queue.lock, flags); 2955ee68e5bSJavier Cardona 2964bd4c2ddSThomas Pedersen skb_queue_walk_safe(&failq, fskb, tmp) { 2974bd4c2ddSThomas Pedersen if (skb_queue_len(&gate_mpath->frame_queue) >= 2984bd4c2ddSThomas Pedersen MESH_FRAME_QUEUE_LEN) { 2994bd4c2ddSThomas Pedersen mpath_dbg(gate_mpath->sdata, "mpath queue full!\n"); 3004bd4c2ddSThomas Pedersen break; 301817a53d9SJohn W. Linville } 3025ee68e5bSJavier Cardona 3034bd4c2ddSThomas Pedersen skb = skb_copy(fskb, GFP_ATOMIC); 3044bd4c2ddSThomas Pedersen if (WARN_ON(!skb)) 3054bd4c2ddSThomas Pedersen break; 3064bd4c2ddSThomas Pedersen 3075ee68e5bSJavier Cardona prepare_for_gate(skb, gate_mpath->dst, gate_mpath); 3084bd4c2ddSThomas Pedersen skb_queue_tail(&gate_mpath->frame_queue, skb); 3094bd4c2ddSThomas Pedersen 3104bd4c2ddSThomas Pedersen if (copy) 3114bd4c2ddSThomas Pedersen continue; 3124bd4c2ddSThomas Pedersen 3134bd4c2ddSThomas Pedersen __skb_unlink(fskb, &failq); 3144bd4c2ddSThomas Pedersen kfree_skb(fskb); 3155ee68e5bSJavier Cardona } 3165ee68e5bSJavier Cardona 317bdcbd8e0SJohannes Berg mpath_dbg(gate_mpath->sdata, "Mpath queue for gate %pM has %d frames\n", 318bdcbd8e0SJohannes Berg gate_mpath->dst, skb_queue_len(&gate_mpath->frame_queue)); 3195ee68e5bSJavier Cardona 3205ee68e5bSJavier Cardona if (!copy) 3215ee68e5bSJavier Cardona return; 3225ee68e5bSJavier Cardona 3235ee68e5bSJavier Cardona spin_lock_irqsave(&from_mpath->frame_queue.lock, flags); 3245ee68e5bSJavier Cardona skb_queue_splice(&failq, &from_mpath->frame_queue); 3255ee68e5bSJavier Cardona spin_unlock_irqrestore(&from_mpath->frame_queue.lock, flags); 3265ee68e5bSJavier Cardona } 3275ee68e5bSJavier Cardona 328eb2b9311SLuis Carlos Cobo 3295ad20dd1SLuis R. Rodriguez static struct mesh_path *mpath_lookup(struct mesh_table *tbl, u8 *dst, 330239289e4SJavier Cardona struct ieee80211_sub_if_data *sdata) 331239289e4SJavier Cardona { 332239289e4SJavier Cardona struct mesh_path *mpath; 333239289e4SJavier Cardona struct hlist_node *n; 334239289e4SJavier Cardona struct hlist_head *bucket; 335239289e4SJavier Cardona struct mpath_node *node; 336239289e4SJavier Cardona 337239289e4SJavier Cardona bucket = &tbl->hash_buckets[mesh_table_hash(dst, sdata, tbl)]; 338239289e4SJavier Cardona hlist_for_each_entry_rcu(node, n, bucket, list) { 339239289e4SJavier Cardona mpath = node->mpath; 340239289e4SJavier Cardona if (mpath->sdata == sdata && 341b203ca39SJoe Perches ether_addr_equal(dst, mpath->dst)) { 342239289e4SJavier Cardona if (MPATH_EXPIRED(mpath)) { 343239289e4SJavier Cardona spin_lock_bh(&mpath->state_lock); 344239289e4SJavier Cardona mpath->flags &= ~MESH_PATH_ACTIVE; 345239289e4SJavier Cardona spin_unlock_bh(&mpath->state_lock); 346239289e4SJavier Cardona } 347239289e4SJavier Cardona return mpath; 348239289e4SJavier Cardona } 349239289e4SJavier Cardona } 350239289e4SJavier Cardona return NULL; 351239289e4SJavier Cardona } 352239289e4SJavier Cardona 353eb2b9311SLuis Carlos Cobo /** 354eb2b9311SLuis Carlos Cobo * mesh_path_lookup - look up a path in the mesh path table 355eb2b9311SLuis Carlos Cobo * @dst: hardware address (ETH_ALEN length) of destination 356f698d856SJasper Bryant-Greene * @sdata: local subif 357eb2b9311SLuis Carlos Cobo * 358eb2b9311SLuis Carlos Cobo * Returns: pointer to the mesh path structure, or NULL if not found 359eb2b9311SLuis Carlos Cobo * 360eb2b9311SLuis Carlos Cobo * Locking: must be called within a read rcu section. 361eb2b9311SLuis Carlos Cobo */ 362f698d856SJasper Bryant-Greene struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) 363eb2b9311SLuis Carlos Cobo { 3645ad20dd1SLuis R. Rodriguez return mpath_lookup(rcu_dereference(mesh_paths), dst, sdata); 365eb2b9311SLuis Carlos Cobo } 366eb2b9311SLuis Carlos Cobo 36779617deeSYanBo struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) 36879617deeSYanBo { 3695ad20dd1SLuis R. Rodriguez return mpath_lookup(rcu_dereference(mpp_paths), dst, sdata); 37079617deeSYanBo } 37179617deeSYanBo 37279617deeSYanBo 373eb2b9311SLuis Carlos Cobo /** 374eb2b9311SLuis Carlos Cobo * mesh_path_lookup_by_idx - look up a path in the mesh path table by its index 375eb2b9311SLuis Carlos Cobo * @idx: index 376f698d856SJasper Bryant-Greene * @sdata: local subif, or NULL for all entries 377eb2b9311SLuis Carlos Cobo * 378eb2b9311SLuis Carlos Cobo * Returns: pointer to the mesh path structure, or NULL if not found. 379eb2b9311SLuis Carlos Cobo * 380eb2b9311SLuis Carlos Cobo * Locking: must be called within a read rcu section. 381eb2b9311SLuis Carlos Cobo */ 382f698d856SJasper Bryant-Greene struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data *sdata) 383eb2b9311SLuis Carlos Cobo { 384349eb8cfSJohannes Berg struct mesh_table *tbl = rcu_dereference(mesh_paths); 385eb2b9311SLuis Carlos Cobo struct mpath_node *node; 386eb2b9311SLuis Carlos Cobo struct hlist_node *p; 387eb2b9311SLuis Carlos Cobo int i; 388eb2b9311SLuis Carlos Cobo int j = 0; 389eb2b9311SLuis Carlos Cobo 390349eb8cfSJohannes Berg for_each_mesh_entry(tbl, p, node, i) { 391f698d856SJasper Bryant-Greene if (sdata && node->mpath->sdata != sdata) 3922a8ca29aSLuis Carlos Cobo continue; 393eb2b9311SLuis Carlos Cobo if (j++ == idx) { 394eb2b9311SLuis Carlos Cobo if (MPATH_EXPIRED(node->mpath)) { 395eb2b9311SLuis Carlos Cobo spin_lock_bh(&node->mpath->state_lock); 396eb2b9311SLuis Carlos Cobo node->mpath->flags &= ~MESH_PATH_ACTIVE; 397eb2b9311SLuis Carlos Cobo spin_unlock_bh(&node->mpath->state_lock); 398eb2b9311SLuis Carlos Cobo } 399eb2b9311SLuis Carlos Cobo return node->mpath; 400eb2b9311SLuis Carlos Cobo } 4012a8ca29aSLuis Carlos Cobo } 402eb2b9311SLuis Carlos Cobo 403eb2b9311SLuis Carlos Cobo return NULL; 404eb2b9311SLuis Carlos Cobo } 405eb2b9311SLuis Carlos Cobo 4065ee68e5bSJavier Cardona /** 40730be52e4SJohannes Berg * mesh_path_add_gate - add the given mpath to a mesh gate to our path table 40830be52e4SJohannes Berg * @mpath: gate path to add to table 4095ee68e5bSJavier Cardona */ 41030be52e4SJohannes Berg int mesh_path_add_gate(struct mesh_path *mpath) 4115ee68e5bSJavier Cardona { 41230be52e4SJohannes Berg struct mesh_table *tbl; 4135ee68e5bSJavier Cardona struct mpath_node *gate, *new_gate; 4145ee68e5bSJavier Cardona struct hlist_node *n; 4155ee68e5bSJavier Cardona int err; 4165ee68e5bSJavier Cardona 4175ee68e5bSJavier Cardona rcu_read_lock(); 41830be52e4SJohannes Berg tbl = rcu_dereference(mesh_paths); 4195ee68e5bSJavier Cardona 4205ee68e5bSJavier Cardona hlist_for_each_entry_rcu(gate, n, tbl->known_gates, list) 4215ee68e5bSJavier Cardona if (gate->mpath == mpath) { 4225ee68e5bSJavier Cardona err = -EEXIST; 4235ee68e5bSJavier Cardona goto err_rcu; 4245ee68e5bSJavier Cardona } 4255ee68e5bSJavier Cardona 4265ee68e5bSJavier Cardona new_gate = kzalloc(sizeof(struct mpath_node), GFP_ATOMIC); 4275ee68e5bSJavier Cardona if (!new_gate) { 4285ee68e5bSJavier Cardona err = -ENOMEM; 4295ee68e5bSJavier Cardona goto err_rcu; 4305ee68e5bSJavier Cardona } 4315ee68e5bSJavier Cardona 4325ee68e5bSJavier Cardona mpath->is_gate = true; 4335ee68e5bSJavier Cardona mpath->sdata->u.mesh.num_gates++; 4345ee68e5bSJavier Cardona new_gate->mpath = mpath; 4355ee68e5bSJavier Cardona spin_lock_bh(&tbl->gates_lock); 4365ee68e5bSJavier Cardona hlist_add_head_rcu(&new_gate->list, tbl->known_gates); 4375ee68e5bSJavier Cardona spin_unlock_bh(&tbl->gates_lock); 4385ee68e5bSJavier Cardona rcu_read_unlock(); 439bdcbd8e0SJohannes Berg mpath_dbg(mpath->sdata, 440bdcbd8e0SJohannes Berg "Mesh path: Recorded new gate: %pM. %d known gates\n", 441bdcbd8e0SJohannes Berg mpath->dst, mpath->sdata->u.mesh.num_gates); 4425ee68e5bSJavier Cardona return 0; 4435ee68e5bSJavier Cardona err_rcu: 4445ee68e5bSJavier Cardona rcu_read_unlock(); 4455ee68e5bSJavier Cardona return err; 4465ee68e5bSJavier Cardona } 4475ee68e5bSJavier Cardona 4485ee68e5bSJavier Cardona /** 4495ee68e5bSJavier Cardona * mesh_gate_del - remove a mesh gate from the list of known gates 4505ee68e5bSJavier Cardona * @tbl: table which holds our list of known gates 4515ee68e5bSJavier Cardona * @mpath: gate mpath 4525ee68e5bSJavier Cardona * 4535ee68e5bSJavier Cardona * Returns: 0 on success 4545ee68e5bSJavier Cardona * 4555ee68e5bSJavier Cardona * Locking: must be called inside rcu_read_lock() section 4565ee68e5bSJavier Cardona */ 4575ee68e5bSJavier Cardona static int mesh_gate_del(struct mesh_table *tbl, struct mesh_path *mpath) 4585ee68e5bSJavier Cardona { 4595ee68e5bSJavier Cardona struct mpath_node *gate; 4605ee68e5bSJavier Cardona struct hlist_node *p, *q; 4615ee68e5bSJavier Cardona 4625ee68e5bSJavier Cardona hlist_for_each_entry_safe(gate, p, q, tbl->known_gates, list) 4635ee68e5bSJavier Cardona if (gate->mpath == mpath) { 4645ee68e5bSJavier Cardona spin_lock_bh(&tbl->gates_lock); 4655ee68e5bSJavier Cardona hlist_del_rcu(&gate->list); 466ae1f18e4SPaul E. McKenney kfree_rcu(gate, rcu); 4675ee68e5bSJavier Cardona spin_unlock_bh(&tbl->gates_lock); 4685ee68e5bSJavier Cardona mpath->sdata->u.mesh.num_gates--; 4695ee68e5bSJavier Cardona mpath->is_gate = false; 470bdcbd8e0SJohannes Berg mpath_dbg(mpath->sdata, 471bdcbd8e0SJohannes Berg "Mesh path: Deleted gate: %pM. %d known gates\n", 4725ee68e5bSJavier Cardona mpath->dst, mpath->sdata->u.mesh.num_gates); 4735ee68e5bSJavier Cardona break; 4745ee68e5bSJavier Cardona } 4755ee68e5bSJavier Cardona 4765ee68e5bSJavier Cardona return 0; 4775ee68e5bSJavier Cardona } 4785ee68e5bSJavier Cardona 4795ee68e5bSJavier Cardona /** 4805ee68e5bSJavier Cardona * mesh_gate_num - number of gates known to this interface 4815ee68e5bSJavier Cardona * @sdata: subif data 4825ee68e5bSJavier Cardona */ 4835ee68e5bSJavier Cardona int mesh_gate_num(struct ieee80211_sub_if_data *sdata) 4845ee68e5bSJavier Cardona { 4855ee68e5bSJavier Cardona return sdata->u.mesh.num_gates; 4865ee68e5bSJavier Cardona } 4875ee68e5bSJavier Cardona 488eb2b9311SLuis Carlos Cobo /** 489eb2b9311SLuis Carlos Cobo * mesh_path_add - allocate and add a new path to the mesh path table 490eb2b9311SLuis Carlos Cobo * @addr: destination address of the path (ETH_ALEN length) 491f698d856SJasper Bryant-Greene * @sdata: local subif 492eb2b9311SLuis Carlos Cobo * 493af901ca1SAndré Goddard Rosa * Returns: 0 on success 494eb2b9311SLuis Carlos Cobo * 495eb2b9311SLuis Carlos Cobo * State: the initial state of the new path is set to 0 496eb2b9311SLuis Carlos Cobo */ 497f698d856SJasper Bryant-Greene int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) 498eb2b9311SLuis Carlos Cobo { 49918889231SJavier Cardona struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 50018889231SJavier Cardona struct ieee80211_local *local = sdata->local; 501349eb8cfSJohannes Berg struct mesh_table *tbl; 502eb2b9311SLuis Carlos Cobo struct mesh_path *mpath, *new_mpath; 503eb2b9311SLuis Carlos Cobo struct mpath_node *node, *new_node; 504eb2b9311SLuis Carlos Cobo struct hlist_head *bucket; 505eb2b9311SLuis Carlos Cobo struct hlist_node *n; 506eb2b9311SLuis Carlos Cobo int grow = 0; 507eb2b9311SLuis Carlos Cobo int err = 0; 508eb2b9311SLuis Carlos Cobo u32 hash_idx; 509eb2b9311SLuis Carlos Cobo 510b203ca39SJoe Perches if (ether_addr_equal(dst, sdata->vif.addr)) 511eb2b9311SLuis Carlos Cobo /* never add ourselves as neighbours */ 512eb2b9311SLuis Carlos Cobo return -ENOTSUPP; 513eb2b9311SLuis Carlos Cobo 514eb2b9311SLuis Carlos Cobo if (is_multicast_ether_addr(dst)) 515eb2b9311SLuis Carlos Cobo return -ENOTSUPP; 516eb2b9311SLuis Carlos Cobo 517472dbc45SJohannes Berg if (atomic_add_unless(&sdata->u.mesh.mpaths, 1, MESH_MAX_MPATHS) == 0) 518eb2b9311SLuis Carlos Cobo return -ENOSPC; 519eb2b9311SLuis Carlos Cobo 520402d7752SPavel Emelyanov err = -ENOMEM; 52118889231SJavier Cardona new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC); 522402d7752SPavel Emelyanov if (!new_mpath) 523402d7752SPavel Emelyanov goto err_path_alloc; 524402d7752SPavel Emelyanov 52518889231SJavier Cardona new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC); 526402d7752SPavel Emelyanov if (!new_node) 527402d7752SPavel Emelyanov goto err_node_alloc; 528f84e71a9SPavel Emelyanov 5299b84b808SJavier Cardona read_lock_bh(&pathtbl_resize_lock); 530eb2b9311SLuis Carlos Cobo memcpy(new_mpath->dst, dst, ETH_ALEN); 531e83e6541SJohannes Berg eth_broadcast_addr(new_mpath->rann_snd_addr); 53235bcd591SChun-Yeow Yeoh new_mpath->is_root = false; 533f698d856SJasper Bryant-Greene new_mpath->sdata = sdata; 534eb2b9311SLuis Carlos Cobo new_mpath->flags = 0; 535eb2b9311SLuis Carlos Cobo skb_queue_head_init(&new_mpath->frame_queue); 536eb2b9311SLuis Carlos Cobo new_node->mpath = new_mpath; 537eb2b9311SLuis Carlos Cobo new_mpath->timer.data = (unsigned long) new_mpath; 538eb2b9311SLuis Carlos Cobo new_mpath->timer.function = mesh_path_timer; 539eb2b9311SLuis Carlos Cobo new_mpath->exp_time = jiffies; 540eb2b9311SLuis Carlos Cobo spin_lock_init(&new_mpath->state_lock); 541eb2b9311SLuis Carlos Cobo init_timer(&new_mpath->timer); 542eb2b9311SLuis Carlos Cobo 543349eb8cfSJohannes Berg tbl = resize_dereference_mesh_paths(); 544eb2b9311SLuis Carlos Cobo 545349eb8cfSJohannes Berg hash_idx = mesh_table_hash(dst, sdata, tbl); 546349eb8cfSJohannes Berg bucket = &tbl->hash_buckets[hash_idx]; 547349eb8cfSJohannes Berg 548f06c7885SThomas Pedersen spin_lock(&tbl->hashwlock[hash_idx]); 549eb2b9311SLuis Carlos Cobo 550402d7752SPavel Emelyanov err = -EEXIST; 551eb2b9311SLuis Carlos Cobo hlist_for_each_entry(node, n, bucket, list) { 552eb2b9311SLuis Carlos Cobo mpath = node->mpath; 553888d04dfSFelix Fietkau if (mpath->sdata == sdata && 554b203ca39SJoe Perches ether_addr_equal(dst, mpath->dst)) 555402d7752SPavel Emelyanov goto err_exists; 556eb2b9311SLuis Carlos Cobo } 557eb2b9311SLuis Carlos Cobo 558eb2b9311SLuis Carlos Cobo hlist_add_head_rcu(&new_node->list, bucket); 559349eb8cfSJohannes Berg if (atomic_inc_return(&tbl->entries) >= 560349eb8cfSJohannes Berg tbl->mean_chain_len * (tbl->hash_mask + 1)) 561eb2b9311SLuis Carlos Cobo grow = 1; 562eb2b9311SLuis Carlos Cobo 563f5ea9120SJohannes Berg mesh_paths_generation++; 564f5ea9120SJohannes Berg 565f06c7885SThomas Pedersen spin_unlock(&tbl->hashwlock[hash_idx]); 5669b84b808SJavier Cardona read_unlock_bh(&pathtbl_resize_lock); 567402d7752SPavel Emelyanov if (grow) { 56818889231SJavier Cardona set_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags); 56964592c8fSJohannes Berg ieee80211_queue_work(&local->hw, &sdata->work); 570eb2b9311SLuis Carlos Cobo } 571402d7752SPavel Emelyanov return 0; 572402d7752SPavel Emelyanov 573402d7752SPavel Emelyanov err_exists: 574f06c7885SThomas Pedersen spin_unlock(&tbl->hashwlock[hash_idx]); 5759b84b808SJavier Cardona read_unlock_bh(&pathtbl_resize_lock); 576402d7752SPavel Emelyanov kfree(new_node); 577402d7752SPavel Emelyanov err_node_alloc: 578402d7752SPavel Emelyanov kfree(new_mpath); 579402d7752SPavel Emelyanov err_path_alloc: 580472dbc45SJohannes Berg atomic_dec(&sdata->u.mesh.mpaths); 581eb2b9311SLuis Carlos Cobo return err; 582eb2b9311SLuis Carlos Cobo } 583eb2b9311SLuis Carlos Cobo 5841928ecabSJohannes Berg static void mesh_table_free_rcu(struct rcu_head *rcu) 5851928ecabSJohannes Berg { 5861928ecabSJohannes Berg struct mesh_table *tbl = container_of(rcu, struct mesh_table, rcu_head); 5871928ecabSJohannes Berg 5881928ecabSJohannes Berg mesh_table_free(tbl, false); 5891928ecabSJohannes Berg } 5901928ecabSJohannes Berg 59118889231SJavier Cardona void mesh_mpath_table_grow(void) 59218889231SJavier Cardona { 59318889231SJavier Cardona struct mesh_table *oldtbl, *newtbl; 59418889231SJavier Cardona 5959b84b808SJavier Cardona write_lock_bh(&pathtbl_resize_lock); 596349eb8cfSJohannes Berg oldtbl = resize_dereference_mesh_paths(); 597349eb8cfSJohannes Berg newtbl = mesh_table_alloc(oldtbl->size_order + 1); 5981928ecabSJohannes Berg if (!newtbl) 5991928ecabSJohannes Berg goto out; 600349eb8cfSJohannes Berg if (mesh_table_grow(oldtbl, newtbl) < 0) { 601a3e6b12cScozybit Inc __mesh_table_free(newtbl); 6021928ecabSJohannes Berg goto out; 60318889231SJavier Cardona } 60418889231SJavier Cardona rcu_assign_pointer(mesh_paths, newtbl); 60518889231SJavier Cardona 6061928ecabSJohannes Berg call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu); 6071928ecabSJohannes Berg 6081928ecabSJohannes Berg out: 6091928ecabSJohannes Berg write_unlock_bh(&pathtbl_resize_lock); 61018889231SJavier Cardona } 61118889231SJavier Cardona 61218889231SJavier Cardona void mesh_mpp_table_grow(void) 61318889231SJavier Cardona { 61418889231SJavier Cardona struct mesh_table *oldtbl, *newtbl; 61518889231SJavier Cardona 6169b84b808SJavier Cardona write_lock_bh(&pathtbl_resize_lock); 617349eb8cfSJohannes Berg oldtbl = resize_dereference_mpp_paths(); 618349eb8cfSJohannes Berg newtbl = mesh_table_alloc(oldtbl->size_order + 1); 6191928ecabSJohannes Berg if (!newtbl) 6201928ecabSJohannes Berg goto out; 621349eb8cfSJohannes Berg if (mesh_table_grow(oldtbl, newtbl) < 0) { 622a3e6b12cScozybit Inc __mesh_table_free(newtbl); 6231928ecabSJohannes Berg goto out; 62418889231SJavier Cardona } 62518889231SJavier Cardona rcu_assign_pointer(mpp_paths, newtbl); 6261928ecabSJohannes Berg call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu); 62718889231SJavier Cardona 6281928ecabSJohannes Berg out: 6291928ecabSJohannes Berg write_unlock_bh(&pathtbl_resize_lock); 63018889231SJavier Cardona } 631eb2b9311SLuis Carlos Cobo 63279617deeSYanBo int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) 63379617deeSYanBo { 63418889231SJavier Cardona struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 63518889231SJavier Cardona struct ieee80211_local *local = sdata->local; 636349eb8cfSJohannes Berg struct mesh_table *tbl; 63779617deeSYanBo struct mesh_path *mpath, *new_mpath; 63879617deeSYanBo struct mpath_node *node, *new_node; 63979617deeSYanBo struct hlist_head *bucket; 64079617deeSYanBo struct hlist_node *n; 64179617deeSYanBo int grow = 0; 64279617deeSYanBo int err = 0; 64379617deeSYanBo u32 hash_idx; 64479617deeSYanBo 645b203ca39SJoe Perches if (ether_addr_equal(dst, sdata->vif.addr)) 64679617deeSYanBo /* never add ourselves as neighbours */ 64779617deeSYanBo return -ENOTSUPP; 64879617deeSYanBo 64979617deeSYanBo if (is_multicast_ether_addr(dst)) 65079617deeSYanBo return -ENOTSUPP; 65179617deeSYanBo 65279617deeSYanBo err = -ENOMEM; 65318889231SJavier Cardona new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC); 65479617deeSYanBo if (!new_mpath) 65579617deeSYanBo goto err_path_alloc; 65679617deeSYanBo 65718889231SJavier Cardona new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC); 65879617deeSYanBo if (!new_node) 65979617deeSYanBo goto err_node_alloc; 66079617deeSYanBo 6619b84b808SJavier Cardona read_lock_bh(&pathtbl_resize_lock); 66279617deeSYanBo memcpy(new_mpath->dst, dst, ETH_ALEN); 66379617deeSYanBo memcpy(new_mpath->mpp, mpp, ETH_ALEN); 66479617deeSYanBo new_mpath->sdata = sdata; 66579617deeSYanBo new_mpath->flags = 0; 66679617deeSYanBo skb_queue_head_init(&new_mpath->frame_queue); 66779617deeSYanBo new_node->mpath = new_mpath; 668c6133661SThomas Pedersen init_timer(&new_mpath->timer); 66979617deeSYanBo new_mpath->exp_time = jiffies; 67079617deeSYanBo spin_lock_init(&new_mpath->state_lock); 67179617deeSYanBo 672349eb8cfSJohannes Berg tbl = resize_dereference_mpp_paths(); 67379617deeSYanBo 674349eb8cfSJohannes Berg hash_idx = mesh_table_hash(dst, sdata, tbl); 675349eb8cfSJohannes Berg bucket = &tbl->hash_buckets[hash_idx]; 676349eb8cfSJohannes Berg 677f06c7885SThomas Pedersen spin_lock(&tbl->hashwlock[hash_idx]); 67879617deeSYanBo 67979617deeSYanBo err = -EEXIST; 68079617deeSYanBo hlist_for_each_entry(node, n, bucket, list) { 68179617deeSYanBo mpath = node->mpath; 682888d04dfSFelix Fietkau if (mpath->sdata == sdata && 683b203ca39SJoe Perches ether_addr_equal(dst, mpath->dst)) 68479617deeSYanBo goto err_exists; 68579617deeSYanBo } 68679617deeSYanBo 68779617deeSYanBo hlist_add_head_rcu(&new_node->list, bucket); 688349eb8cfSJohannes Berg if (atomic_inc_return(&tbl->entries) >= 689349eb8cfSJohannes Berg tbl->mean_chain_len * (tbl->hash_mask + 1)) 69079617deeSYanBo grow = 1; 69179617deeSYanBo 692f06c7885SThomas Pedersen spin_unlock(&tbl->hashwlock[hash_idx]); 6939b84b808SJavier Cardona read_unlock_bh(&pathtbl_resize_lock); 69479617deeSYanBo if (grow) { 69518889231SJavier Cardona set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags); 69664592c8fSJohannes Berg ieee80211_queue_work(&local->hw, &sdata->work); 69779617deeSYanBo } 69879617deeSYanBo return 0; 69979617deeSYanBo 70079617deeSYanBo err_exists: 701f06c7885SThomas Pedersen spin_unlock(&tbl->hashwlock[hash_idx]); 7029b84b808SJavier Cardona read_unlock_bh(&pathtbl_resize_lock); 70379617deeSYanBo kfree(new_node); 70479617deeSYanBo err_node_alloc: 70579617deeSYanBo kfree(new_mpath); 70679617deeSYanBo err_path_alloc: 70779617deeSYanBo return err; 70879617deeSYanBo } 70979617deeSYanBo 71079617deeSYanBo 711eb2b9311SLuis Carlos Cobo /** 712eb2b9311SLuis Carlos Cobo * mesh_plink_broken - deactivates paths and sends perr when a link breaks 713eb2b9311SLuis Carlos Cobo * 714eb2b9311SLuis Carlos Cobo * @sta: broken peer link 715eb2b9311SLuis Carlos Cobo * 716eb2b9311SLuis Carlos Cobo * This function must be called from the rate control algorithm if enough 717eb2b9311SLuis Carlos Cobo * delivery errors suggest that a peer link is no longer usable. 718eb2b9311SLuis Carlos Cobo */ 719eb2b9311SLuis Carlos Cobo void mesh_plink_broken(struct sta_info *sta) 720eb2b9311SLuis Carlos Cobo { 721349eb8cfSJohannes Berg struct mesh_table *tbl; 72215ff6365SJohannes Berg static const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 723eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 724eb2b9311SLuis Carlos Cobo struct mpath_node *node; 725eb2b9311SLuis Carlos Cobo struct hlist_node *p; 726f698d856SJasper Bryant-Greene struct ieee80211_sub_if_data *sdata = sta->sdata; 727eb2b9311SLuis Carlos Cobo int i; 72825d49e4dSThomas Pedersen __le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_DEST_UNREACHABLE); 729eb2b9311SLuis Carlos Cobo 730eb2b9311SLuis Carlos Cobo rcu_read_lock(); 731349eb8cfSJohannes Berg tbl = rcu_dereference(mesh_paths); 732349eb8cfSJohannes Berg for_each_mesh_entry(tbl, p, node, i) { 733eb2b9311SLuis Carlos Cobo mpath = node->mpath; 734349eb8cfSJohannes Berg if (rcu_dereference(mpath->next_hop) == sta && 735eb2b9311SLuis Carlos Cobo mpath->flags & MESH_PATH_ACTIVE && 736eb2b9311SLuis Carlos Cobo !(mpath->flags & MESH_PATH_FIXED)) { 737f5e50cd0SJavier Cardona spin_lock_bh(&mpath->state_lock); 738eb2b9311SLuis Carlos Cobo mpath->flags &= ~MESH_PATH_ACTIVE; 739d19b3bf6SRui Paulo ++mpath->sn; 740eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 74145904f21SJavier Cardona mesh_path_error_tx(sdata->u.mesh.mshcfg.element_ttl, 74245904f21SJavier Cardona mpath->dst, cpu_to_le32(mpath->sn), 74325d49e4dSThomas Pedersen reason, bcast, sdata); 744f5e50cd0SJavier Cardona } 745eb2b9311SLuis Carlos Cobo } 746eb2b9311SLuis Carlos Cobo rcu_read_unlock(); 747eb2b9311SLuis Carlos Cobo } 748eb2b9311SLuis Carlos Cobo 74919c50b3dSJavier Cardona static void mesh_path_node_reclaim(struct rcu_head *rp) 75019c50b3dSJavier Cardona { 75119c50b3dSJavier Cardona struct mpath_node *node = container_of(rp, struct mpath_node, rcu); 75219c50b3dSJavier Cardona struct ieee80211_sub_if_data *sdata = node->mpath->sdata; 75319c50b3dSJavier Cardona 75419c50b3dSJavier Cardona del_timer_sync(&node->mpath->timer); 75519c50b3dSJavier Cardona atomic_dec(&sdata->u.mesh.mpaths); 75619c50b3dSJavier Cardona kfree(node->mpath); 75719c50b3dSJavier Cardona kfree(node); 75819c50b3dSJavier Cardona } 75919c50b3dSJavier Cardona 76019c50b3dSJavier Cardona /* needs to be called with the corresponding hashwlock taken */ 76119c50b3dSJavier Cardona static void __mesh_path_del(struct mesh_table *tbl, struct mpath_node *node) 76219c50b3dSJavier Cardona { 76319c50b3dSJavier Cardona struct mesh_path *mpath; 76419c50b3dSJavier Cardona mpath = node->mpath; 76519c50b3dSJavier Cardona spin_lock(&mpath->state_lock); 76619c50b3dSJavier Cardona mpath->flags |= MESH_PATH_RESOLVING; 76719c50b3dSJavier Cardona if (mpath->is_gate) 76819c50b3dSJavier Cardona mesh_gate_del(tbl, mpath); 76919c50b3dSJavier Cardona hlist_del_rcu(&node->list); 77019c50b3dSJavier Cardona call_rcu(&node->rcu, mesh_path_node_reclaim); 77119c50b3dSJavier Cardona spin_unlock(&mpath->state_lock); 77219c50b3dSJavier Cardona atomic_dec(&tbl->entries); 77319c50b3dSJavier Cardona } 77419c50b3dSJavier Cardona 775eb2b9311SLuis Carlos Cobo /** 776eb2b9311SLuis Carlos Cobo * mesh_path_flush_by_nexthop - Deletes mesh paths if their next hop matches 777eb2b9311SLuis Carlos Cobo * 7782c53040fSBen Hutchings * @sta: mesh peer to match 779eb2b9311SLuis Carlos Cobo * 780b4e08ea1SLuis Carlos Cobo * RCU notes: this function is called when a mesh plink transitions from 781b4e08ea1SLuis Carlos Cobo * PLINK_ESTAB to any other state, since PLINK_ESTAB state is the only one that 782b4e08ea1SLuis Carlos Cobo * allows path creation. This will happen before the sta can be freed (because 783d0709a65SJohannes Berg * sta_info_destroy() calls this) so any reader in a rcu read block will be 784d0709a65SJohannes Berg * protected against the plink disappearing. 785eb2b9311SLuis Carlos Cobo */ 786eb2b9311SLuis Carlos Cobo void mesh_path_flush_by_nexthop(struct sta_info *sta) 787eb2b9311SLuis Carlos Cobo { 788349eb8cfSJohannes Berg struct mesh_table *tbl; 789eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 790eb2b9311SLuis Carlos Cobo struct mpath_node *node; 791eb2b9311SLuis Carlos Cobo struct hlist_node *p; 792eb2b9311SLuis Carlos Cobo int i; 793eb2b9311SLuis Carlos Cobo 794349eb8cfSJohannes Berg rcu_read_lock(); 795239289e4SJavier Cardona read_lock_bh(&pathtbl_resize_lock); 796239289e4SJavier Cardona tbl = resize_dereference_mesh_paths(); 797349eb8cfSJohannes Berg for_each_mesh_entry(tbl, p, node, i) { 798eb2b9311SLuis Carlos Cobo mpath = node->mpath; 799cd72e817SJavier Cardona if (rcu_dereference(mpath->next_hop) == sta) { 800f06c7885SThomas Pedersen spin_lock(&tbl->hashwlock[i]); 80119c50b3dSJavier Cardona __mesh_path_del(tbl, node); 802f06c7885SThomas Pedersen spin_unlock(&tbl->hashwlock[i]); 803eb2b9311SLuis Carlos Cobo } 804eb2b9311SLuis Carlos Cobo } 805239289e4SJavier Cardona read_unlock_bh(&pathtbl_resize_lock); 806eb2b9311SLuis Carlos Cobo rcu_read_unlock(); 807eb2b9311SLuis Carlos Cobo } 808eb2b9311SLuis Carlos Cobo 809cd72e817SJavier Cardona static void table_flush_by_iface(struct mesh_table *tbl, 810cd72e817SJavier Cardona struct ieee80211_sub_if_data *sdata) 811eb2b9311SLuis Carlos Cobo { 812eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 813eb2b9311SLuis Carlos Cobo struct mpath_node *node; 814eb2b9311SLuis Carlos Cobo struct hlist_node *p; 815eb2b9311SLuis Carlos Cobo int i; 816eb2b9311SLuis Carlos Cobo 817cd72e817SJavier Cardona WARN_ON(!rcu_read_lock_held()); 818eb2b9311SLuis Carlos Cobo for_each_mesh_entry(tbl, p, node, i) { 819eb2b9311SLuis Carlos Cobo mpath = node->mpath; 820cd72e817SJavier Cardona if (mpath->sdata != sdata) 821cd72e817SJavier Cardona continue; 822ece1a2e7SJavier Cardona spin_lock_bh(&tbl->hashwlock[i]); 82319c50b3dSJavier Cardona __mesh_path_del(tbl, node); 824ece1a2e7SJavier Cardona spin_unlock_bh(&tbl->hashwlock[i]); 825eb2b9311SLuis Carlos Cobo } 826eb2b9311SLuis Carlos Cobo } 827eb2b9311SLuis Carlos Cobo 828ece1a2e7SJavier Cardona /** 829ece1a2e7SJavier Cardona * mesh_path_flush_by_iface - Deletes all mesh paths associated with a given iface 830ece1a2e7SJavier Cardona * 831ece1a2e7SJavier Cardona * This function deletes both mesh paths as well as mesh portal paths. 832ece1a2e7SJavier Cardona * 8332c53040fSBen Hutchings * @sdata: interface data to match 834ece1a2e7SJavier Cardona * 835ece1a2e7SJavier Cardona */ 836ece1a2e7SJavier Cardona void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata) 837eb2b9311SLuis Carlos Cobo { 838cd72e817SJavier Cardona struct mesh_table *tbl; 839d0709a65SJohannes Berg 840cd72e817SJavier Cardona rcu_read_lock(); 841239289e4SJavier Cardona read_lock_bh(&pathtbl_resize_lock); 842239289e4SJavier Cardona tbl = resize_dereference_mesh_paths(); 843cd72e817SJavier Cardona table_flush_by_iface(tbl, sdata); 844239289e4SJavier Cardona tbl = resize_dereference_mpp_paths(); 845cd72e817SJavier Cardona table_flush_by_iface(tbl, sdata); 846239289e4SJavier Cardona read_unlock_bh(&pathtbl_resize_lock); 847cd72e817SJavier Cardona rcu_read_unlock(); 848eb2b9311SLuis Carlos Cobo } 849eb2b9311SLuis Carlos Cobo 850eb2b9311SLuis Carlos Cobo /** 851eb2b9311SLuis Carlos Cobo * mesh_path_del - delete a mesh path from the table 852eb2b9311SLuis Carlos Cobo * 853eb2b9311SLuis Carlos Cobo * @addr: dst address (ETH_ALEN length) 854f698d856SJasper Bryant-Greene * @sdata: local subif 855eb2b9311SLuis Carlos Cobo * 856af901ca1SAndré Goddard Rosa * Returns: 0 if successful 857eb2b9311SLuis Carlos Cobo */ 858f698d856SJasper Bryant-Greene int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) 859eb2b9311SLuis Carlos Cobo { 860349eb8cfSJohannes Berg struct mesh_table *tbl; 861eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 862eb2b9311SLuis Carlos Cobo struct mpath_node *node; 863eb2b9311SLuis Carlos Cobo struct hlist_head *bucket; 864eb2b9311SLuis Carlos Cobo struct hlist_node *n; 865eb2b9311SLuis Carlos Cobo int hash_idx; 866eb2b9311SLuis Carlos Cobo int err = 0; 867eb2b9311SLuis Carlos Cobo 8689b84b808SJavier Cardona read_lock_bh(&pathtbl_resize_lock); 869349eb8cfSJohannes Berg tbl = resize_dereference_mesh_paths(); 870349eb8cfSJohannes Berg hash_idx = mesh_table_hash(addr, sdata, tbl); 871349eb8cfSJohannes Berg bucket = &tbl->hash_buckets[hash_idx]; 872eb2b9311SLuis Carlos Cobo 873f06c7885SThomas Pedersen spin_lock(&tbl->hashwlock[hash_idx]); 874eb2b9311SLuis Carlos Cobo hlist_for_each_entry(node, n, bucket, list) { 875eb2b9311SLuis Carlos Cobo mpath = node->mpath; 876f698d856SJasper Bryant-Greene if (mpath->sdata == sdata && 877b203ca39SJoe Perches ether_addr_equal(addr, mpath->dst)) { 87819c50b3dSJavier Cardona __mesh_path_del(tbl, node); 879eb2b9311SLuis Carlos Cobo goto enddel; 880eb2b9311SLuis Carlos Cobo } 881eb2b9311SLuis Carlos Cobo } 882eb2b9311SLuis Carlos Cobo 883eb2b9311SLuis Carlos Cobo err = -ENXIO; 884eb2b9311SLuis Carlos Cobo enddel: 885f5ea9120SJohannes Berg mesh_paths_generation++; 886f06c7885SThomas Pedersen spin_unlock(&tbl->hashwlock[hash_idx]); 8879b84b808SJavier Cardona read_unlock_bh(&pathtbl_resize_lock); 888eb2b9311SLuis Carlos Cobo return err; 889eb2b9311SLuis Carlos Cobo } 890eb2b9311SLuis Carlos Cobo 891eb2b9311SLuis Carlos Cobo /** 892eb2b9311SLuis Carlos Cobo * mesh_path_tx_pending - sends pending frames in a mesh path queue 893eb2b9311SLuis Carlos Cobo * 894eb2b9311SLuis Carlos Cobo * @mpath: mesh path to activate 895eb2b9311SLuis Carlos Cobo * 896eb2b9311SLuis Carlos Cobo * Locking: the state_lock of the mpath structure must NOT be held when calling 897eb2b9311SLuis Carlos Cobo * this function. 898eb2b9311SLuis Carlos Cobo */ 899eb2b9311SLuis Carlos Cobo void mesh_path_tx_pending(struct mesh_path *mpath) 900eb2b9311SLuis Carlos Cobo { 901249b405cSJavier Cardona if (mpath->flags & MESH_PATH_ACTIVE) 902249b405cSJavier Cardona ieee80211_add_pending_skbs(mpath->sdata->local, 903249b405cSJavier Cardona &mpath->frame_queue); 904eb2b9311SLuis Carlos Cobo } 905eb2b9311SLuis Carlos Cobo 906eb2b9311SLuis Carlos Cobo /** 9075ee68e5bSJavier Cardona * mesh_path_send_to_gates - sends pending frames to all known mesh gates 9085ee68e5bSJavier Cardona * 9095ee68e5bSJavier Cardona * @mpath: mesh path whose queue will be emptied 9105ee68e5bSJavier Cardona * 9115ee68e5bSJavier Cardona * If there is only one gate, the frames are transferred from the failed mpath 9125ee68e5bSJavier Cardona * queue to that gate's queue. If there are more than one gates, the frames 9135ee68e5bSJavier Cardona * are copied from each gate to the next. After frames are copied, the 9145ee68e5bSJavier Cardona * mpath queues are emptied onto the transmission queue. 9155ee68e5bSJavier Cardona */ 9165ee68e5bSJavier Cardona int mesh_path_send_to_gates(struct mesh_path *mpath) 9175ee68e5bSJavier Cardona { 9185ee68e5bSJavier Cardona struct ieee80211_sub_if_data *sdata = mpath->sdata; 9195ee68e5bSJavier Cardona struct hlist_node *n; 9205ee68e5bSJavier Cardona struct mesh_table *tbl; 9215ee68e5bSJavier Cardona struct mesh_path *from_mpath = mpath; 9225ee68e5bSJavier Cardona struct mpath_node *gate = NULL; 9235ee68e5bSJavier Cardona bool copy = false; 9245ee68e5bSJavier Cardona struct hlist_head *known_gates; 9255ee68e5bSJavier Cardona 9265ee68e5bSJavier Cardona rcu_read_lock(); 9275ee68e5bSJavier Cardona tbl = rcu_dereference(mesh_paths); 9285ee68e5bSJavier Cardona known_gates = tbl->known_gates; 9295ee68e5bSJavier Cardona rcu_read_unlock(); 9305ee68e5bSJavier Cardona 9315ee68e5bSJavier Cardona if (!known_gates) 9325ee68e5bSJavier Cardona return -EHOSTUNREACH; 9335ee68e5bSJavier Cardona 9345ee68e5bSJavier Cardona hlist_for_each_entry_rcu(gate, n, known_gates, list) { 9355ee68e5bSJavier Cardona if (gate->mpath->sdata != sdata) 9365ee68e5bSJavier Cardona continue; 9375ee68e5bSJavier Cardona 9385ee68e5bSJavier Cardona if (gate->mpath->flags & MESH_PATH_ACTIVE) { 939bdcbd8e0SJohannes Berg mpath_dbg(sdata, "Forwarding to %pM\n", gate->mpath->dst); 9405ee68e5bSJavier Cardona mesh_path_move_to_queue(gate->mpath, from_mpath, copy); 9415ee68e5bSJavier Cardona from_mpath = gate->mpath; 9425ee68e5bSJavier Cardona copy = true; 9435ee68e5bSJavier Cardona } else { 944bdcbd8e0SJohannes Berg mpath_dbg(sdata, 945bdcbd8e0SJohannes Berg "Not forwarding %p (flags %#x)\n", 946bdcbd8e0SJohannes Berg gate->mpath, gate->mpath->flags); 9475ee68e5bSJavier Cardona } 9485ee68e5bSJavier Cardona } 9495ee68e5bSJavier Cardona 9505ee68e5bSJavier Cardona hlist_for_each_entry_rcu(gate, n, known_gates, list) 9515ee68e5bSJavier Cardona if (gate->mpath->sdata == sdata) { 952bdcbd8e0SJohannes Berg mpath_dbg(sdata, "Sending to %pM\n", gate->mpath->dst); 9535ee68e5bSJavier Cardona mesh_path_tx_pending(gate->mpath); 9545ee68e5bSJavier Cardona } 9555ee68e5bSJavier Cardona 9565ee68e5bSJavier Cardona return (from_mpath == mpath) ? -EHOSTUNREACH : 0; 9575ee68e5bSJavier Cardona } 9585ee68e5bSJavier Cardona 9595ee68e5bSJavier Cardona /** 960eb2b9311SLuis Carlos Cobo * mesh_path_discard_frame - discard a frame whose path could not be resolved 961eb2b9311SLuis Carlos Cobo * 962eb2b9311SLuis Carlos Cobo * @skb: frame to discard 963f698d856SJasper Bryant-Greene * @sdata: network subif the frame was to be sent through 964eb2b9311SLuis Carlos Cobo * 965eb2b9311SLuis Carlos Cobo * Locking: the function must me called within a rcu_read_lock region 966eb2b9311SLuis Carlos Cobo */ 967f698d856SJasper Bryant-Greene void mesh_path_discard_frame(struct sk_buff *skb, 968f698d856SJasper Bryant-Greene struct ieee80211_sub_if_data *sdata) 969eb2b9311SLuis Carlos Cobo { 970eb2b9311SLuis Carlos Cobo kfree_skb(skb); 971472dbc45SJohannes Berg sdata->u.mesh.mshstats.dropped_frames_no_route++; 972eb2b9311SLuis Carlos Cobo } 973eb2b9311SLuis Carlos Cobo 974eb2b9311SLuis Carlos Cobo /** 975eb2b9311SLuis Carlos Cobo * mesh_path_flush_pending - free the pending queue of a mesh path 976eb2b9311SLuis Carlos Cobo * 977eb2b9311SLuis Carlos Cobo * @mpath: mesh path whose queue has to be freed 978eb2b9311SLuis Carlos Cobo * 97925985edcSLucas De Marchi * Locking: the function must me called within a rcu_read_lock region 980eb2b9311SLuis Carlos Cobo */ 981eb2b9311SLuis Carlos Cobo void mesh_path_flush_pending(struct mesh_path *mpath) 982eb2b9311SLuis Carlos Cobo { 983eb2b9311SLuis Carlos Cobo struct sk_buff *skb; 984eb2b9311SLuis Carlos Cobo 98500e3f25cSJavier Cardona while ((skb = skb_dequeue(&mpath->frame_queue)) != NULL) 986f698d856SJasper Bryant-Greene mesh_path_discard_frame(skb, mpath->sdata); 987eb2b9311SLuis Carlos Cobo } 988eb2b9311SLuis Carlos Cobo 989eb2b9311SLuis Carlos Cobo /** 990eb2b9311SLuis Carlos Cobo * mesh_path_fix_nexthop - force a specific next hop for a mesh path 991eb2b9311SLuis Carlos Cobo * 992eb2b9311SLuis Carlos Cobo * @mpath: the mesh path to modify 993eb2b9311SLuis Carlos Cobo * @next_hop: the next hop to force 994eb2b9311SLuis Carlos Cobo * 995eb2b9311SLuis Carlos Cobo * Locking: this function must be called holding mpath->state_lock 996eb2b9311SLuis Carlos Cobo */ 997eb2b9311SLuis Carlos Cobo void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop) 998eb2b9311SLuis Carlos Cobo { 999eb2b9311SLuis Carlos Cobo spin_lock_bh(&mpath->state_lock); 1000eb2b9311SLuis Carlos Cobo mesh_path_assign_nexthop(mpath, next_hop); 1001d19b3bf6SRui Paulo mpath->sn = 0xffff; 1002eb2b9311SLuis Carlos Cobo mpath->metric = 0; 1003eb2b9311SLuis Carlos Cobo mpath->hop_count = 0; 1004eb2b9311SLuis Carlos Cobo mpath->exp_time = 0; 1005eb2b9311SLuis Carlos Cobo mpath->flags |= MESH_PATH_FIXED; 1006eb2b9311SLuis Carlos Cobo mesh_path_activate(mpath); 1007eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 1008eb2b9311SLuis Carlos Cobo mesh_path_tx_pending(mpath); 1009eb2b9311SLuis Carlos Cobo } 1010eb2b9311SLuis Carlos Cobo 1011eb2b9311SLuis Carlos Cobo static void mesh_path_node_free(struct hlist_node *p, bool free_leafs) 1012eb2b9311SLuis Carlos Cobo { 1013eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 1014eb2b9311SLuis Carlos Cobo struct mpath_node *node = hlist_entry(p, struct mpath_node, list); 1015eb2b9311SLuis Carlos Cobo mpath = node->mpath; 1016eb2b9311SLuis Carlos Cobo hlist_del_rcu(p); 1017d0df9eecSJavier Cardona if (free_leafs) { 1018d0df9eecSJavier Cardona del_timer_sync(&mpath->timer); 1019eb2b9311SLuis Carlos Cobo kfree(mpath); 1020d0df9eecSJavier Cardona } 1021eb2b9311SLuis Carlos Cobo kfree(node); 1022eb2b9311SLuis Carlos Cobo } 1023eb2b9311SLuis Carlos Cobo 10244caf86c6SPavel Emelyanov static int mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl) 1025eb2b9311SLuis Carlos Cobo { 1026eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 1027eb2b9311SLuis Carlos Cobo struct mpath_node *node, *new_node; 1028eb2b9311SLuis Carlos Cobo u32 hash_idx; 1029eb2b9311SLuis Carlos Cobo 10308566dc3fSPavel Emelyanov new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC); 103100242c40SPavel Emelyanov if (new_node == NULL) 103200242c40SPavel Emelyanov return -ENOMEM; 103300242c40SPavel Emelyanov 1034eb2b9311SLuis Carlos Cobo node = hlist_entry(p, struct mpath_node, list); 1035eb2b9311SLuis Carlos Cobo mpath = node->mpath; 1036eb2b9311SLuis Carlos Cobo new_node->mpath = mpath; 1037f698d856SJasper Bryant-Greene hash_idx = mesh_table_hash(mpath->dst, mpath->sdata, newtbl); 1038eb2b9311SLuis Carlos Cobo hlist_add_head(&new_node->list, 1039eb2b9311SLuis Carlos Cobo &newtbl->hash_buckets[hash_idx]); 10404caf86c6SPavel Emelyanov return 0; 1041eb2b9311SLuis Carlos Cobo } 1042eb2b9311SLuis Carlos Cobo 1043eb2b9311SLuis Carlos Cobo int mesh_pathtbl_init(void) 1044eb2b9311SLuis Carlos Cobo { 1045349eb8cfSJohannes Berg struct mesh_table *tbl_path, *tbl_mpp; 10464c5ade41SDan Carpenter int ret; 104779617deeSYanBo 1048349eb8cfSJohannes Berg tbl_path = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); 1049349eb8cfSJohannes Berg if (!tbl_path) 1050349eb8cfSJohannes Berg return -ENOMEM; 1051349eb8cfSJohannes Berg tbl_path->free_node = &mesh_path_node_free; 1052349eb8cfSJohannes Berg tbl_path->copy_node = &mesh_path_node_copy; 1053349eb8cfSJohannes Berg tbl_path->mean_chain_len = MEAN_CHAIN_LEN; 10545ee68e5bSJavier Cardona tbl_path->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC); 10554c5ade41SDan Carpenter if (!tbl_path->known_gates) { 10564c5ade41SDan Carpenter ret = -ENOMEM; 10574c5ade41SDan Carpenter goto free_path; 10584c5ade41SDan Carpenter } 10595ee68e5bSJavier Cardona INIT_HLIST_HEAD(tbl_path->known_gates); 10605ee68e5bSJavier Cardona 1061349eb8cfSJohannes Berg 1062349eb8cfSJohannes Berg tbl_mpp = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); 1063349eb8cfSJohannes Berg if (!tbl_mpp) { 10644c5ade41SDan Carpenter ret = -ENOMEM; 10654c5ade41SDan Carpenter goto free_path; 106679617deeSYanBo } 1067349eb8cfSJohannes Berg tbl_mpp->free_node = &mesh_path_node_free; 1068349eb8cfSJohannes Berg tbl_mpp->copy_node = &mesh_path_node_copy; 1069349eb8cfSJohannes Berg tbl_mpp->mean_chain_len = MEAN_CHAIN_LEN; 10705ee68e5bSJavier Cardona tbl_mpp->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC); 10714c5ade41SDan Carpenter if (!tbl_mpp->known_gates) { 10724c5ade41SDan Carpenter ret = -ENOMEM; 10734c5ade41SDan Carpenter goto free_mpp; 10744c5ade41SDan Carpenter } 10755ee68e5bSJavier Cardona INIT_HLIST_HEAD(tbl_mpp->known_gates); 1076349eb8cfSJohannes Berg 1077349eb8cfSJohannes Berg /* Need no locking since this is during init */ 1078349eb8cfSJohannes Berg RCU_INIT_POINTER(mesh_paths, tbl_path); 1079349eb8cfSJohannes Berg RCU_INIT_POINTER(mpp_paths, tbl_mpp); 108079617deeSYanBo 1081eb2b9311SLuis Carlos Cobo return 0; 10824c5ade41SDan Carpenter 10834c5ade41SDan Carpenter free_mpp: 10844c5ade41SDan Carpenter mesh_table_free(tbl_mpp, true); 10854c5ade41SDan Carpenter free_path: 10864c5ade41SDan Carpenter mesh_table_free(tbl_path, true); 10874c5ade41SDan Carpenter return ret; 1088eb2b9311SLuis Carlos Cobo } 1089eb2b9311SLuis Carlos Cobo 1090f698d856SJasper Bryant-Greene void mesh_path_expire(struct ieee80211_sub_if_data *sdata) 1091eb2b9311SLuis Carlos Cobo { 1092349eb8cfSJohannes Berg struct mesh_table *tbl; 1093eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 1094eb2b9311SLuis Carlos Cobo struct mpath_node *node; 1095eb2b9311SLuis Carlos Cobo struct hlist_node *p; 1096eb2b9311SLuis Carlos Cobo int i; 1097eb2b9311SLuis Carlos Cobo 1098349eb8cfSJohannes Berg rcu_read_lock(); 1099349eb8cfSJohannes Berg tbl = rcu_dereference(mesh_paths); 1100349eb8cfSJohannes Berg for_each_mesh_entry(tbl, p, node, i) { 1101f698d856SJasper Bryant-Greene if (node->mpath->sdata != sdata) 1102eb2b9311SLuis Carlos Cobo continue; 1103eb2b9311SLuis Carlos Cobo mpath = node->mpath; 1104eb2b9311SLuis Carlos Cobo if ((!(mpath->flags & MESH_PATH_RESOLVING)) && 1105eb2b9311SLuis Carlos Cobo (!(mpath->flags & MESH_PATH_FIXED)) && 1106f5e50cd0SJavier Cardona time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE)) 1107f698d856SJasper Bryant-Greene mesh_path_del(mpath->dst, mpath->sdata); 1108eb2b9311SLuis Carlos Cobo } 1109349eb8cfSJohannes Berg rcu_read_unlock(); 1110eb2b9311SLuis Carlos Cobo } 1111eb2b9311SLuis Carlos Cobo 1112eb2b9311SLuis Carlos Cobo void mesh_pathtbl_unregister(void) 1113eb2b9311SLuis Carlos Cobo { 1114349eb8cfSJohannes Berg /* no need for locking during exit path */ 111533d480ceSEric Dumazet mesh_table_free(rcu_dereference_protected(mesh_paths, 1), true); 111633d480ceSEric Dumazet mesh_table_free(rcu_dereference_protected(mpp_paths, 1), true); 1117eb2b9311SLuis Carlos Cobo } 1118