1eb2b9311SLuis Carlos Cobo /* 2264d9b7dSRui Paulo * Copyright (c) 2008, 2009 open80211s Ltd. 3eb2b9311SLuis Carlos Cobo * Author: Luis Carlos Cobo <luisca@cozybit.com> 4eb2b9311SLuis Carlos Cobo * 5eb2b9311SLuis Carlos Cobo * This program is free software; you can redistribute it and/or modify 6eb2b9311SLuis Carlos Cobo * it under the terms of the GNU General Public License version 2 as 7eb2b9311SLuis Carlos Cobo * published by the Free Software Foundation. 8eb2b9311SLuis Carlos Cobo */ 9eb2b9311SLuis Carlos Cobo 10eb2b9311SLuis Carlos Cobo #include <linux/etherdevice.h> 11eb2b9311SLuis Carlos Cobo #include <linux/list.h> 12eb2b9311SLuis Carlos Cobo #include <linux/random.h> 135a0e3ad6STejun Heo #include <linux/slab.h> 14eb2b9311SLuis Carlos Cobo #include <linux/spinlock.h> 15eb2b9311SLuis Carlos Cobo #include <linux/string.h> 16eb2b9311SLuis Carlos Cobo #include <net/mac80211.h> 17eb2b9311SLuis Carlos Cobo #include "ieee80211_i.h" 18eb2b9311SLuis Carlos Cobo #include "mesh.h" 19eb2b9311SLuis Carlos Cobo 20eb2b9311SLuis Carlos Cobo /* There will be initially 2^INIT_PATHS_SIZE_ORDER buckets */ 21eb2b9311SLuis Carlos Cobo #define INIT_PATHS_SIZE_ORDER 2 22eb2b9311SLuis Carlos Cobo 23eb2b9311SLuis Carlos Cobo /* Keep the mean chain length below this constant */ 24eb2b9311SLuis Carlos Cobo #define MEAN_CHAIN_LEN 2 25eb2b9311SLuis Carlos Cobo 26eb2b9311SLuis Carlos Cobo #define MPATH_EXPIRED(mpath) ((mpath->flags & MESH_PATH_ACTIVE) && \ 27eb2b9311SLuis Carlos Cobo time_after(jiffies, mpath->exp_time) && \ 28eb2b9311SLuis Carlos Cobo !(mpath->flags & MESH_PATH_FIXED)) 29eb2b9311SLuis Carlos Cobo 30eb2b9311SLuis Carlos Cobo struct mpath_node { 31eb2b9311SLuis Carlos Cobo struct hlist_node list; 32eb2b9311SLuis Carlos Cobo struct rcu_head rcu; 33eb2b9311SLuis Carlos Cobo /* This indirection allows two different tables to point to the same 34eb2b9311SLuis Carlos Cobo * mesh_path structure, useful when resizing 35eb2b9311SLuis Carlos Cobo */ 36eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 37eb2b9311SLuis Carlos Cobo }; 38eb2b9311SLuis Carlos Cobo 39eb2b9311SLuis Carlos Cobo static struct mesh_table *mesh_paths; 4079617deeSYanBo static struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */ 41eb2b9311SLuis Carlos Cobo 42f5ea9120SJohannes Berg int mesh_paths_generation; 4318889231SJavier Cardona static void __mesh_table_free(struct mesh_table *tbl) 4418889231SJavier Cardona { 4518889231SJavier Cardona kfree(tbl->hash_buckets); 4618889231SJavier Cardona kfree(tbl->hashwlock); 4718889231SJavier Cardona kfree(tbl); 4818889231SJavier Cardona } 4918889231SJavier Cardona 5018889231SJavier Cardona void mesh_table_free(struct mesh_table *tbl, bool free_leafs) 5118889231SJavier Cardona { 5218889231SJavier Cardona struct hlist_head *mesh_hash; 5318889231SJavier Cardona struct hlist_node *p, *q; 5418889231SJavier Cardona int i; 5518889231SJavier Cardona 5618889231SJavier Cardona mesh_hash = tbl->hash_buckets; 5718889231SJavier Cardona for (i = 0; i <= tbl->hash_mask; i++) { 5818889231SJavier Cardona spin_lock(&tbl->hashwlock[i]); 5918889231SJavier Cardona hlist_for_each_safe(p, q, &mesh_hash[i]) { 6018889231SJavier Cardona tbl->free_node(p, free_leafs); 6118889231SJavier Cardona atomic_dec(&tbl->entries); 6218889231SJavier Cardona } 6318889231SJavier Cardona spin_unlock(&tbl->hashwlock[i]); 6418889231SJavier Cardona } 6518889231SJavier Cardona __mesh_table_free(tbl); 6618889231SJavier Cardona } 6718889231SJavier Cardona 6818889231SJavier Cardona static struct mesh_table *mesh_table_grow(struct mesh_table *tbl) 6918889231SJavier Cardona { 7018889231SJavier Cardona struct mesh_table *newtbl; 7118889231SJavier Cardona struct hlist_head *oldhash; 7218889231SJavier Cardona struct hlist_node *p, *q; 7318889231SJavier Cardona int i; 7418889231SJavier Cardona 7518889231SJavier Cardona if (atomic_read(&tbl->entries) 7618889231SJavier Cardona < tbl->mean_chain_len * (tbl->hash_mask + 1)) 7718889231SJavier Cardona goto endgrow; 7818889231SJavier Cardona 7918889231SJavier Cardona newtbl = mesh_table_alloc(tbl->size_order + 1); 8018889231SJavier Cardona if (!newtbl) 8118889231SJavier Cardona goto endgrow; 8218889231SJavier Cardona 8318889231SJavier Cardona newtbl->free_node = tbl->free_node; 8418889231SJavier Cardona newtbl->mean_chain_len = tbl->mean_chain_len; 8518889231SJavier Cardona newtbl->copy_node = tbl->copy_node; 8618889231SJavier Cardona atomic_set(&newtbl->entries, atomic_read(&tbl->entries)); 8718889231SJavier Cardona 8818889231SJavier Cardona oldhash = tbl->hash_buckets; 8918889231SJavier Cardona for (i = 0; i <= tbl->hash_mask; i++) 9018889231SJavier Cardona hlist_for_each(p, &oldhash[i]) 9118889231SJavier Cardona if (tbl->copy_node(p, newtbl) < 0) 9218889231SJavier Cardona goto errcopy; 9318889231SJavier Cardona 9418889231SJavier Cardona return newtbl; 9518889231SJavier Cardona 9618889231SJavier Cardona errcopy: 9718889231SJavier Cardona for (i = 0; i <= newtbl->hash_mask; i++) { 9818889231SJavier Cardona hlist_for_each_safe(p, q, &newtbl->hash_buckets[i]) 9918889231SJavier Cardona tbl->free_node(p, 0); 10018889231SJavier Cardona } 10118889231SJavier Cardona __mesh_table_free(newtbl); 10218889231SJavier Cardona endgrow: 10318889231SJavier Cardona return NULL; 10418889231SJavier Cardona } 10518889231SJavier Cardona 106f5ea9120SJohannes Berg 107eb2b9311SLuis Carlos Cobo /* This lock will have the grow table function as writer and add / delete nodes 108eb2b9311SLuis Carlos Cobo * as readers. When reading the table (i.e. doing lookups) we are well protected 109eb2b9311SLuis Carlos Cobo * by RCU 110eb2b9311SLuis Carlos Cobo */ 111eb2b9311SLuis Carlos Cobo static DEFINE_RWLOCK(pathtbl_resize_lock); 112eb2b9311SLuis Carlos Cobo 113eb2b9311SLuis Carlos Cobo /** 114eb2b9311SLuis Carlos Cobo * 115eb2b9311SLuis Carlos Cobo * mesh_path_assign_nexthop - update mesh path next hop 116eb2b9311SLuis Carlos Cobo * 117eb2b9311SLuis Carlos Cobo * @mpath: mesh path to update 118eb2b9311SLuis Carlos Cobo * @sta: next hop to assign 119eb2b9311SLuis Carlos Cobo * 120eb2b9311SLuis Carlos Cobo * Locking: mpath->state_lock must be held when calling this function 121eb2b9311SLuis Carlos Cobo */ 122eb2b9311SLuis Carlos Cobo void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) 123eb2b9311SLuis Carlos Cobo { 12410c836d7SJavier Cardona struct sk_buff *skb; 12510c836d7SJavier Cardona struct ieee80211_hdr *hdr; 12610c836d7SJavier Cardona struct sk_buff_head tmpq; 12710c836d7SJavier Cardona unsigned long flags; 12810c836d7SJavier Cardona 129d0709a65SJohannes Berg rcu_assign_pointer(mpath->next_hop, sta); 13010c836d7SJavier Cardona 13110c836d7SJavier Cardona __skb_queue_head_init(&tmpq); 13210c836d7SJavier Cardona 13310c836d7SJavier Cardona spin_lock_irqsave(&mpath->frame_queue.lock, flags); 13410c836d7SJavier Cardona 13510c836d7SJavier Cardona while ((skb = __skb_dequeue(&mpath->frame_queue)) != NULL) { 13610c836d7SJavier Cardona hdr = (struct ieee80211_hdr *) skb->data; 13710c836d7SJavier Cardona memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN); 13810c836d7SJavier Cardona __skb_queue_tail(&tmpq, skb); 13910c836d7SJavier Cardona } 14010c836d7SJavier Cardona 14110c836d7SJavier Cardona skb_queue_splice(&tmpq, &mpath->frame_queue); 14210c836d7SJavier Cardona spin_unlock_irqrestore(&mpath->frame_queue.lock, flags); 143eb2b9311SLuis Carlos Cobo } 144eb2b9311SLuis Carlos Cobo 145eb2b9311SLuis Carlos Cobo 146eb2b9311SLuis Carlos Cobo /** 147eb2b9311SLuis Carlos Cobo * mesh_path_lookup - look up a path in the mesh path table 148eb2b9311SLuis Carlos Cobo * @dst: hardware address (ETH_ALEN length) of destination 149f698d856SJasper Bryant-Greene * @sdata: local subif 150eb2b9311SLuis Carlos Cobo * 151eb2b9311SLuis Carlos Cobo * Returns: pointer to the mesh path structure, or NULL if not found 152eb2b9311SLuis Carlos Cobo * 153eb2b9311SLuis Carlos Cobo * Locking: must be called within a read rcu section. 154eb2b9311SLuis Carlos Cobo */ 155f698d856SJasper Bryant-Greene struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) 156eb2b9311SLuis Carlos Cobo { 157eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 158eb2b9311SLuis Carlos Cobo struct hlist_node *n; 159eb2b9311SLuis Carlos Cobo struct hlist_head *bucket; 160eb2b9311SLuis Carlos Cobo struct mesh_table *tbl; 161eb2b9311SLuis Carlos Cobo struct mpath_node *node; 162eb2b9311SLuis Carlos Cobo 163eb2b9311SLuis Carlos Cobo tbl = rcu_dereference(mesh_paths); 164eb2b9311SLuis Carlos Cobo 165f698d856SJasper Bryant-Greene bucket = &tbl->hash_buckets[mesh_table_hash(dst, sdata, tbl)]; 166eb2b9311SLuis Carlos Cobo hlist_for_each_entry_rcu(node, n, bucket, list) { 167eb2b9311SLuis Carlos Cobo mpath = node->mpath; 168f698d856SJasper Bryant-Greene if (mpath->sdata == sdata && 169eb2b9311SLuis Carlos Cobo memcmp(dst, mpath->dst, ETH_ALEN) == 0) { 170eb2b9311SLuis Carlos Cobo if (MPATH_EXPIRED(mpath)) { 171eb2b9311SLuis Carlos Cobo spin_lock_bh(&mpath->state_lock); 172eb2b9311SLuis Carlos Cobo if (MPATH_EXPIRED(mpath)) 173eb2b9311SLuis Carlos Cobo mpath->flags &= ~MESH_PATH_ACTIVE; 174eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 175eb2b9311SLuis Carlos Cobo } 176eb2b9311SLuis Carlos Cobo return mpath; 177eb2b9311SLuis Carlos Cobo } 178eb2b9311SLuis Carlos Cobo } 179eb2b9311SLuis Carlos Cobo return NULL; 180eb2b9311SLuis Carlos Cobo } 181eb2b9311SLuis Carlos Cobo 18279617deeSYanBo struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) 18379617deeSYanBo { 18479617deeSYanBo struct mesh_path *mpath; 18579617deeSYanBo struct hlist_node *n; 18679617deeSYanBo struct hlist_head *bucket; 18779617deeSYanBo struct mesh_table *tbl; 18879617deeSYanBo struct mpath_node *node; 18979617deeSYanBo 19079617deeSYanBo tbl = rcu_dereference(mpp_paths); 19179617deeSYanBo 19279617deeSYanBo bucket = &tbl->hash_buckets[mesh_table_hash(dst, sdata, tbl)]; 19379617deeSYanBo hlist_for_each_entry_rcu(node, n, bucket, list) { 19479617deeSYanBo mpath = node->mpath; 19579617deeSYanBo if (mpath->sdata == sdata && 19679617deeSYanBo memcmp(dst, mpath->dst, ETH_ALEN) == 0) { 19779617deeSYanBo if (MPATH_EXPIRED(mpath)) { 19879617deeSYanBo spin_lock_bh(&mpath->state_lock); 19979617deeSYanBo if (MPATH_EXPIRED(mpath)) 20079617deeSYanBo mpath->flags &= ~MESH_PATH_ACTIVE; 20179617deeSYanBo spin_unlock_bh(&mpath->state_lock); 20279617deeSYanBo } 20379617deeSYanBo return mpath; 20479617deeSYanBo } 20579617deeSYanBo } 20679617deeSYanBo return NULL; 20779617deeSYanBo } 20879617deeSYanBo 20979617deeSYanBo 210eb2b9311SLuis Carlos Cobo /** 211eb2b9311SLuis Carlos Cobo * mesh_path_lookup_by_idx - look up a path in the mesh path table by its index 212eb2b9311SLuis Carlos Cobo * @idx: index 213f698d856SJasper Bryant-Greene * @sdata: local subif, or NULL for all entries 214eb2b9311SLuis Carlos Cobo * 215eb2b9311SLuis Carlos Cobo * Returns: pointer to the mesh path structure, or NULL if not found. 216eb2b9311SLuis Carlos Cobo * 217eb2b9311SLuis Carlos Cobo * Locking: must be called within a read rcu section. 218eb2b9311SLuis Carlos Cobo */ 219f698d856SJasper Bryant-Greene struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data *sdata) 220eb2b9311SLuis Carlos Cobo { 221eb2b9311SLuis Carlos Cobo struct mpath_node *node; 222eb2b9311SLuis Carlos Cobo struct hlist_node *p; 223eb2b9311SLuis Carlos Cobo int i; 224eb2b9311SLuis Carlos Cobo int j = 0; 225eb2b9311SLuis Carlos Cobo 2262a8ca29aSLuis Carlos Cobo for_each_mesh_entry(mesh_paths, p, node, i) { 227f698d856SJasper Bryant-Greene if (sdata && node->mpath->sdata != sdata) 2282a8ca29aSLuis Carlos Cobo continue; 229eb2b9311SLuis Carlos Cobo if (j++ == idx) { 230eb2b9311SLuis Carlos Cobo if (MPATH_EXPIRED(node->mpath)) { 231eb2b9311SLuis Carlos Cobo spin_lock_bh(&node->mpath->state_lock); 232eb2b9311SLuis Carlos Cobo if (MPATH_EXPIRED(node->mpath)) 233eb2b9311SLuis Carlos Cobo node->mpath->flags &= ~MESH_PATH_ACTIVE; 234eb2b9311SLuis Carlos Cobo spin_unlock_bh(&node->mpath->state_lock); 235eb2b9311SLuis Carlos Cobo } 236eb2b9311SLuis Carlos Cobo return node->mpath; 237eb2b9311SLuis Carlos Cobo } 2382a8ca29aSLuis Carlos Cobo } 239eb2b9311SLuis Carlos Cobo 240eb2b9311SLuis Carlos Cobo return NULL; 241eb2b9311SLuis Carlos Cobo } 242eb2b9311SLuis Carlos Cobo 243eb2b9311SLuis Carlos Cobo /** 244eb2b9311SLuis Carlos Cobo * mesh_path_add - allocate and add a new path to the mesh path table 245eb2b9311SLuis Carlos Cobo * @addr: destination address of the path (ETH_ALEN length) 246f698d856SJasper Bryant-Greene * @sdata: local subif 247eb2b9311SLuis Carlos Cobo * 248af901ca1SAndré Goddard Rosa * Returns: 0 on success 249eb2b9311SLuis Carlos Cobo * 250eb2b9311SLuis Carlos Cobo * State: the initial state of the new path is set to 0 251eb2b9311SLuis Carlos Cobo */ 252f698d856SJasper Bryant-Greene int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) 253eb2b9311SLuis Carlos Cobo { 25418889231SJavier Cardona struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 25518889231SJavier Cardona struct ieee80211_local *local = sdata->local; 256eb2b9311SLuis Carlos Cobo struct mesh_path *mpath, *new_mpath; 257eb2b9311SLuis Carlos Cobo struct mpath_node *node, *new_node; 258eb2b9311SLuis Carlos Cobo struct hlist_head *bucket; 259eb2b9311SLuis Carlos Cobo struct hlist_node *n; 260eb2b9311SLuis Carlos Cobo int grow = 0; 261eb2b9311SLuis Carlos Cobo int err = 0; 262eb2b9311SLuis Carlos Cobo u32 hash_idx; 263eb2b9311SLuis Carlos Cobo 26447846c9bSJohannes Berg if (memcmp(dst, sdata->vif.addr, ETH_ALEN) == 0) 265eb2b9311SLuis Carlos Cobo /* never add ourselves as neighbours */ 266eb2b9311SLuis Carlos Cobo return -ENOTSUPP; 267eb2b9311SLuis Carlos Cobo 268eb2b9311SLuis Carlos Cobo if (is_multicast_ether_addr(dst)) 269eb2b9311SLuis Carlos Cobo return -ENOTSUPP; 270eb2b9311SLuis Carlos Cobo 271472dbc45SJohannes Berg if (atomic_add_unless(&sdata->u.mesh.mpaths, 1, MESH_MAX_MPATHS) == 0) 272eb2b9311SLuis Carlos Cobo return -ENOSPC; 273eb2b9311SLuis Carlos Cobo 274402d7752SPavel Emelyanov err = -ENOMEM; 27518889231SJavier Cardona new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC); 276402d7752SPavel Emelyanov if (!new_mpath) 277402d7752SPavel Emelyanov goto err_path_alloc; 278402d7752SPavel Emelyanov 27918889231SJavier Cardona new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC); 280402d7752SPavel Emelyanov if (!new_node) 281402d7752SPavel Emelyanov goto err_node_alloc; 282f84e71a9SPavel Emelyanov 283f84e71a9SPavel Emelyanov read_lock(&pathtbl_resize_lock); 284eb2b9311SLuis Carlos Cobo memcpy(new_mpath->dst, dst, ETH_ALEN); 285f698d856SJasper Bryant-Greene new_mpath->sdata = sdata; 286eb2b9311SLuis Carlos Cobo new_mpath->flags = 0; 287eb2b9311SLuis Carlos Cobo skb_queue_head_init(&new_mpath->frame_queue); 288eb2b9311SLuis Carlos Cobo new_node->mpath = new_mpath; 289eb2b9311SLuis Carlos Cobo new_mpath->timer.data = (unsigned long) new_mpath; 290eb2b9311SLuis Carlos Cobo new_mpath->timer.function = mesh_path_timer; 291eb2b9311SLuis Carlos Cobo new_mpath->exp_time = jiffies; 292eb2b9311SLuis Carlos Cobo spin_lock_init(&new_mpath->state_lock); 293eb2b9311SLuis Carlos Cobo init_timer(&new_mpath->timer); 294eb2b9311SLuis Carlos Cobo 295f698d856SJasper Bryant-Greene hash_idx = mesh_table_hash(dst, sdata, mesh_paths); 296eb2b9311SLuis Carlos Cobo bucket = &mesh_paths->hash_buckets[hash_idx]; 297eb2b9311SLuis Carlos Cobo 298eb2b9311SLuis Carlos Cobo spin_lock(&mesh_paths->hashwlock[hash_idx]); 299eb2b9311SLuis Carlos Cobo 300402d7752SPavel Emelyanov err = -EEXIST; 301eb2b9311SLuis Carlos Cobo hlist_for_each_entry(node, n, bucket, list) { 302eb2b9311SLuis Carlos Cobo mpath = node->mpath; 303f698d856SJasper Bryant-Greene if (mpath->sdata == sdata && memcmp(dst, mpath->dst, ETH_ALEN) == 0) 304402d7752SPavel Emelyanov goto err_exists; 305eb2b9311SLuis Carlos Cobo } 306eb2b9311SLuis Carlos Cobo 307eb2b9311SLuis Carlos Cobo hlist_add_head_rcu(&new_node->list, bucket); 308eb2b9311SLuis Carlos Cobo if (atomic_inc_return(&mesh_paths->entries) >= 309eb2b9311SLuis Carlos Cobo mesh_paths->mean_chain_len * (mesh_paths->hash_mask + 1)) 310eb2b9311SLuis Carlos Cobo grow = 1; 311eb2b9311SLuis Carlos Cobo 312f5ea9120SJohannes Berg mesh_paths_generation++; 313f5ea9120SJohannes Berg 314eb2b9311SLuis Carlos Cobo spin_unlock(&mesh_paths->hashwlock[hash_idx]); 315eb2b9311SLuis Carlos Cobo read_unlock(&pathtbl_resize_lock); 316402d7752SPavel Emelyanov if (grow) { 31718889231SJavier Cardona set_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags); 31864592c8fSJohannes Berg ieee80211_queue_work(&local->hw, &sdata->work); 319eb2b9311SLuis Carlos Cobo } 320402d7752SPavel Emelyanov return 0; 321402d7752SPavel Emelyanov 322402d7752SPavel Emelyanov err_exists: 323402d7752SPavel Emelyanov spin_unlock(&mesh_paths->hashwlock[hash_idx]); 324402d7752SPavel Emelyanov read_unlock(&pathtbl_resize_lock); 325402d7752SPavel Emelyanov kfree(new_node); 326402d7752SPavel Emelyanov err_node_alloc: 327402d7752SPavel Emelyanov kfree(new_mpath); 328402d7752SPavel Emelyanov err_path_alloc: 329472dbc45SJohannes Berg atomic_dec(&sdata->u.mesh.mpaths); 330eb2b9311SLuis Carlos Cobo return err; 331eb2b9311SLuis Carlos Cobo } 332eb2b9311SLuis Carlos Cobo 33318889231SJavier Cardona void mesh_mpath_table_grow(void) 33418889231SJavier Cardona { 33518889231SJavier Cardona struct mesh_table *oldtbl, *newtbl; 33618889231SJavier Cardona 33718889231SJavier Cardona write_lock(&pathtbl_resize_lock); 33818889231SJavier Cardona oldtbl = mesh_paths; 33918889231SJavier Cardona newtbl = mesh_table_grow(mesh_paths); 34018889231SJavier Cardona if (!newtbl) { 34118889231SJavier Cardona write_unlock(&pathtbl_resize_lock); 34218889231SJavier Cardona return; 34318889231SJavier Cardona } 34418889231SJavier Cardona rcu_assign_pointer(mesh_paths, newtbl); 34518889231SJavier Cardona write_unlock(&pathtbl_resize_lock); 34618889231SJavier Cardona 34718889231SJavier Cardona synchronize_rcu(); 34818889231SJavier Cardona mesh_table_free(oldtbl, false); 34918889231SJavier Cardona } 35018889231SJavier Cardona 35118889231SJavier Cardona void mesh_mpp_table_grow(void) 35218889231SJavier Cardona { 35318889231SJavier Cardona struct mesh_table *oldtbl, *newtbl; 35418889231SJavier Cardona 35518889231SJavier Cardona write_lock(&pathtbl_resize_lock); 35618889231SJavier Cardona oldtbl = mpp_paths; 35718889231SJavier Cardona newtbl = mesh_table_grow(mpp_paths); 35818889231SJavier Cardona if (!newtbl) { 35918889231SJavier Cardona write_unlock(&pathtbl_resize_lock); 36018889231SJavier Cardona return; 36118889231SJavier Cardona } 36218889231SJavier Cardona rcu_assign_pointer(mpp_paths, newtbl); 36318889231SJavier Cardona write_unlock(&pathtbl_resize_lock); 36418889231SJavier Cardona 36518889231SJavier Cardona synchronize_rcu(); 36618889231SJavier Cardona mesh_table_free(oldtbl, false); 36718889231SJavier Cardona } 368eb2b9311SLuis Carlos Cobo 36979617deeSYanBo int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) 37079617deeSYanBo { 37118889231SJavier Cardona struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 37218889231SJavier Cardona struct ieee80211_local *local = sdata->local; 37379617deeSYanBo struct mesh_path *mpath, *new_mpath; 37479617deeSYanBo struct mpath_node *node, *new_node; 37579617deeSYanBo struct hlist_head *bucket; 37679617deeSYanBo struct hlist_node *n; 37779617deeSYanBo int grow = 0; 37879617deeSYanBo int err = 0; 37979617deeSYanBo u32 hash_idx; 38079617deeSYanBo 38147846c9bSJohannes Berg if (memcmp(dst, sdata->vif.addr, ETH_ALEN) == 0) 38279617deeSYanBo /* never add ourselves as neighbours */ 38379617deeSYanBo return -ENOTSUPP; 38479617deeSYanBo 38579617deeSYanBo if (is_multicast_ether_addr(dst)) 38679617deeSYanBo return -ENOTSUPP; 38779617deeSYanBo 38879617deeSYanBo err = -ENOMEM; 38918889231SJavier Cardona new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC); 39079617deeSYanBo if (!new_mpath) 39179617deeSYanBo goto err_path_alloc; 39279617deeSYanBo 39318889231SJavier Cardona new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC); 39479617deeSYanBo if (!new_node) 39579617deeSYanBo goto err_node_alloc; 39679617deeSYanBo 39779617deeSYanBo read_lock(&pathtbl_resize_lock); 39879617deeSYanBo memcpy(new_mpath->dst, dst, ETH_ALEN); 39979617deeSYanBo memcpy(new_mpath->mpp, mpp, ETH_ALEN); 40079617deeSYanBo new_mpath->sdata = sdata; 40179617deeSYanBo new_mpath->flags = 0; 40279617deeSYanBo skb_queue_head_init(&new_mpath->frame_queue); 40379617deeSYanBo new_node->mpath = new_mpath; 40479617deeSYanBo new_mpath->exp_time = jiffies; 40579617deeSYanBo spin_lock_init(&new_mpath->state_lock); 40679617deeSYanBo 40779617deeSYanBo hash_idx = mesh_table_hash(dst, sdata, mpp_paths); 40879617deeSYanBo bucket = &mpp_paths->hash_buckets[hash_idx]; 40979617deeSYanBo 41079617deeSYanBo spin_lock(&mpp_paths->hashwlock[hash_idx]); 41179617deeSYanBo 41279617deeSYanBo err = -EEXIST; 41379617deeSYanBo hlist_for_each_entry(node, n, bucket, list) { 41479617deeSYanBo mpath = node->mpath; 41579617deeSYanBo if (mpath->sdata == sdata && memcmp(dst, mpath->dst, ETH_ALEN) == 0) 41679617deeSYanBo goto err_exists; 41779617deeSYanBo } 41879617deeSYanBo 41979617deeSYanBo hlist_add_head_rcu(&new_node->list, bucket); 42079617deeSYanBo if (atomic_inc_return(&mpp_paths->entries) >= 42179617deeSYanBo mpp_paths->mean_chain_len * (mpp_paths->hash_mask + 1)) 42279617deeSYanBo grow = 1; 42379617deeSYanBo 42479617deeSYanBo spin_unlock(&mpp_paths->hashwlock[hash_idx]); 42579617deeSYanBo read_unlock(&pathtbl_resize_lock); 42679617deeSYanBo if (grow) { 42718889231SJavier Cardona set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags); 42864592c8fSJohannes Berg ieee80211_queue_work(&local->hw, &sdata->work); 42979617deeSYanBo } 43079617deeSYanBo return 0; 43179617deeSYanBo 43279617deeSYanBo err_exists: 43379617deeSYanBo spin_unlock(&mpp_paths->hashwlock[hash_idx]); 43479617deeSYanBo read_unlock(&pathtbl_resize_lock); 43579617deeSYanBo kfree(new_node); 43679617deeSYanBo err_node_alloc: 43779617deeSYanBo kfree(new_mpath); 43879617deeSYanBo err_path_alloc: 43979617deeSYanBo return err; 44079617deeSYanBo } 44179617deeSYanBo 44279617deeSYanBo 443eb2b9311SLuis Carlos Cobo /** 444eb2b9311SLuis Carlos Cobo * mesh_plink_broken - deactivates paths and sends perr when a link breaks 445eb2b9311SLuis Carlos Cobo * 446eb2b9311SLuis Carlos Cobo * @sta: broken peer link 447eb2b9311SLuis Carlos Cobo * 448eb2b9311SLuis Carlos Cobo * This function must be called from the rate control algorithm if enough 449eb2b9311SLuis Carlos Cobo * delivery errors suggest that a peer link is no longer usable. 450eb2b9311SLuis Carlos Cobo */ 451eb2b9311SLuis Carlos Cobo void mesh_plink_broken(struct sta_info *sta) 452eb2b9311SLuis Carlos Cobo { 45315ff6365SJohannes Berg static const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 454eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 455eb2b9311SLuis Carlos Cobo struct mpath_node *node; 456eb2b9311SLuis Carlos Cobo struct hlist_node *p; 457f698d856SJasper Bryant-Greene struct ieee80211_sub_if_data *sdata = sta->sdata; 458eb2b9311SLuis Carlos Cobo int i; 459eb2b9311SLuis Carlos Cobo 460eb2b9311SLuis Carlos Cobo rcu_read_lock(); 461eb2b9311SLuis Carlos Cobo for_each_mesh_entry(mesh_paths, p, node, i) { 462eb2b9311SLuis Carlos Cobo mpath = node->mpath; 463eb2b9311SLuis Carlos Cobo spin_lock_bh(&mpath->state_lock); 464eb2b9311SLuis Carlos Cobo if (mpath->next_hop == sta && 465eb2b9311SLuis Carlos Cobo mpath->flags & MESH_PATH_ACTIVE && 466eb2b9311SLuis Carlos Cobo !(mpath->flags & MESH_PATH_FIXED)) { 467eb2b9311SLuis Carlos Cobo mpath->flags &= ~MESH_PATH_ACTIVE; 468d19b3bf6SRui Paulo ++mpath->sn; 469eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 47045904f21SJavier Cardona mesh_path_error_tx(sdata->u.mesh.mshcfg.element_ttl, 47145904f21SJavier Cardona mpath->dst, cpu_to_le32(mpath->sn), 4729f13084dSRui Paulo cpu_to_le16(PERR_RCODE_DEST_UNREACH), 47315ff6365SJohannes Berg bcast, sdata); 474eb2b9311SLuis Carlos Cobo } else 475eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 476eb2b9311SLuis Carlos Cobo } 477eb2b9311SLuis Carlos Cobo rcu_read_unlock(); 478eb2b9311SLuis Carlos Cobo } 479eb2b9311SLuis Carlos Cobo 480eb2b9311SLuis Carlos Cobo /** 481eb2b9311SLuis Carlos Cobo * mesh_path_flush_by_nexthop - Deletes mesh paths if their next hop matches 482eb2b9311SLuis Carlos Cobo * 483eb2b9311SLuis Carlos Cobo * @sta - mesh peer to match 484eb2b9311SLuis Carlos Cobo * 485b4e08ea1SLuis Carlos Cobo * RCU notes: this function is called when a mesh plink transitions from 486b4e08ea1SLuis Carlos Cobo * PLINK_ESTAB to any other state, since PLINK_ESTAB state is the only one that 487b4e08ea1SLuis Carlos Cobo * allows path creation. This will happen before the sta can be freed (because 488d0709a65SJohannes Berg * sta_info_destroy() calls this) so any reader in a rcu read block will be 489d0709a65SJohannes Berg * protected against the plink disappearing. 490eb2b9311SLuis Carlos Cobo */ 491eb2b9311SLuis Carlos Cobo void mesh_path_flush_by_nexthop(struct sta_info *sta) 492eb2b9311SLuis Carlos Cobo { 493eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 494eb2b9311SLuis Carlos Cobo struct mpath_node *node; 495eb2b9311SLuis Carlos Cobo struct hlist_node *p; 496eb2b9311SLuis Carlos Cobo int i; 497eb2b9311SLuis Carlos Cobo 498eb2b9311SLuis Carlos Cobo for_each_mesh_entry(mesh_paths, p, node, i) { 499eb2b9311SLuis Carlos Cobo mpath = node->mpath; 500eb2b9311SLuis Carlos Cobo if (mpath->next_hop == sta) 501f698d856SJasper Bryant-Greene mesh_path_del(mpath->dst, mpath->sdata); 502eb2b9311SLuis Carlos Cobo } 503eb2b9311SLuis Carlos Cobo } 504eb2b9311SLuis Carlos Cobo 505f698d856SJasper Bryant-Greene void mesh_path_flush(struct ieee80211_sub_if_data *sdata) 506eb2b9311SLuis Carlos Cobo { 507eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 508eb2b9311SLuis Carlos Cobo struct mpath_node *node; 509eb2b9311SLuis Carlos Cobo struct hlist_node *p; 510eb2b9311SLuis Carlos Cobo int i; 511eb2b9311SLuis Carlos Cobo 512eb2b9311SLuis Carlos Cobo for_each_mesh_entry(mesh_paths, p, node, i) { 513eb2b9311SLuis Carlos Cobo mpath = node->mpath; 514f698d856SJasper Bryant-Greene if (mpath->sdata == sdata) 515f698d856SJasper Bryant-Greene mesh_path_del(mpath->dst, mpath->sdata); 516eb2b9311SLuis Carlos Cobo } 517eb2b9311SLuis Carlos Cobo } 518eb2b9311SLuis Carlos Cobo 519eb2b9311SLuis Carlos Cobo static void mesh_path_node_reclaim(struct rcu_head *rp) 520eb2b9311SLuis Carlos Cobo { 521eb2b9311SLuis Carlos Cobo struct mpath_node *node = container_of(rp, struct mpath_node, rcu); 522f698d856SJasper Bryant-Greene struct ieee80211_sub_if_data *sdata = node->mpath->sdata; 523d0709a65SJohannes Berg 52489a1ad69SLuis Carlos Cobo del_timer_sync(&node->mpath->timer); 525472dbc45SJohannes Berg atomic_dec(&sdata->u.mesh.mpaths); 526eb2b9311SLuis Carlos Cobo kfree(node->mpath); 527eb2b9311SLuis Carlos Cobo kfree(node); 528eb2b9311SLuis Carlos Cobo } 529eb2b9311SLuis Carlos Cobo 530eb2b9311SLuis Carlos Cobo /** 531eb2b9311SLuis Carlos Cobo * mesh_path_del - delete a mesh path from the table 532eb2b9311SLuis Carlos Cobo * 533eb2b9311SLuis Carlos Cobo * @addr: dst address (ETH_ALEN length) 534f698d856SJasper Bryant-Greene * @sdata: local subif 535eb2b9311SLuis Carlos Cobo * 536af901ca1SAndré Goddard Rosa * Returns: 0 if successful 537eb2b9311SLuis Carlos Cobo */ 538f698d856SJasper Bryant-Greene int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) 539eb2b9311SLuis Carlos Cobo { 540eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 541eb2b9311SLuis Carlos Cobo struct mpath_node *node; 542eb2b9311SLuis Carlos Cobo struct hlist_head *bucket; 543eb2b9311SLuis Carlos Cobo struct hlist_node *n; 544eb2b9311SLuis Carlos Cobo int hash_idx; 545eb2b9311SLuis Carlos Cobo int err = 0; 546eb2b9311SLuis Carlos Cobo 547eb2b9311SLuis Carlos Cobo read_lock(&pathtbl_resize_lock); 548f698d856SJasper Bryant-Greene hash_idx = mesh_table_hash(addr, sdata, mesh_paths); 549eb2b9311SLuis Carlos Cobo bucket = &mesh_paths->hash_buckets[hash_idx]; 550eb2b9311SLuis Carlos Cobo 551eb2b9311SLuis Carlos Cobo spin_lock(&mesh_paths->hashwlock[hash_idx]); 552eb2b9311SLuis Carlos Cobo hlist_for_each_entry(node, n, bucket, list) { 553eb2b9311SLuis Carlos Cobo mpath = node->mpath; 554f698d856SJasper Bryant-Greene if (mpath->sdata == sdata && 555eb2b9311SLuis Carlos Cobo memcmp(addr, mpath->dst, ETH_ALEN) == 0) { 556eb2b9311SLuis Carlos Cobo spin_lock_bh(&mpath->state_lock); 557eb2b9311SLuis Carlos Cobo mpath->flags |= MESH_PATH_RESOLVING; 558eb2b9311SLuis Carlos Cobo hlist_del_rcu(&node->list); 559eb2b9311SLuis Carlos Cobo call_rcu(&node->rcu, mesh_path_node_reclaim); 560eb2b9311SLuis Carlos Cobo atomic_dec(&mesh_paths->entries); 561eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 562eb2b9311SLuis Carlos Cobo goto enddel; 563eb2b9311SLuis Carlos Cobo } 564eb2b9311SLuis Carlos Cobo } 565eb2b9311SLuis Carlos Cobo 566eb2b9311SLuis Carlos Cobo err = -ENXIO; 567eb2b9311SLuis Carlos Cobo enddel: 568f5ea9120SJohannes Berg mesh_paths_generation++; 569eb2b9311SLuis Carlos Cobo spin_unlock(&mesh_paths->hashwlock[hash_idx]); 570eb2b9311SLuis Carlos Cobo read_unlock(&pathtbl_resize_lock); 571eb2b9311SLuis Carlos Cobo return err; 572eb2b9311SLuis Carlos Cobo } 573eb2b9311SLuis Carlos Cobo 574eb2b9311SLuis Carlos Cobo /** 575eb2b9311SLuis Carlos Cobo * mesh_path_tx_pending - sends pending frames in a mesh path queue 576eb2b9311SLuis Carlos Cobo * 577eb2b9311SLuis Carlos Cobo * @mpath: mesh path to activate 578eb2b9311SLuis Carlos Cobo * 579eb2b9311SLuis Carlos Cobo * Locking: the state_lock of the mpath structure must NOT be held when calling 580eb2b9311SLuis Carlos Cobo * this function. 581eb2b9311SLuis Carlos Cobo */ 582eb2b9311SLuis Carlos Cobo void mesh_path_tx_pending(struct mesh_path *mpath) 583eb2b9311SLuis Carlos Cobo { 584249b405cSJavier Cardona if (mpath->flags & MESH_PATH_ACTIVE) 585249b405cSJavier Cardona ieee80211_add_pending_skbs(mpath->sdata->local, 586249b405cSJavier Cardona &mpath->frame_queue); 587eb2b9311SLuis Carlos Cobo } 588eb2b9311SLuis Carlos Cobo 589eb2b9311SLuis Carlos Cobo /** 590eb2b9311SLuis Carlos Cobo * mesh_path_discard_frame - discard a frame whose path could not be resolved 591eb2b9311SLuis Carlos Cobo * 592eb2b9311SLuis Carlos Cobo * @skb: frame to discard 593f698d856SJasper Bryant-Greene * @sdata: network subif the frame was to be sent through 594eb2b9311SLuis Carlos Cobo * 59535946a57SJavier Cardona * If the frame was being forwarded from another MP, a PERR frame will be sent 59635946a57SJavier Cardona * to the precursor. The precursor's address (i.e. the previous hop) was saved 59735946a57SJavier Cardona * in addr1 of the frame-to-be-forwarded, and would only be overwritten once 59835946a57SJavier Cardona * the destination is successfully resolved. 599eb2b9311SLuis Carlos Cobo * 600eb2b9311SLuis Carlos Cobo * Locking: the function must me called within a rcu_read_lock region 601eb2b9311SLuis Carlos Cobo */ 602f698d856SJasper Bryant-Greene void mesh_path_discard_frame(struct sk_buff *skb, 603f698d856SJasper Bryant-Greene struct ieee80211_sub_if_data *sdata) 604eb2b9311SLuis Carlos Cobo { 605e32f85f7SLuis Carlos Cobo struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; 606eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 607d19b3bf6SRui Paulo u32 sn = 0; 608eb2b9311SLuis Carlos Cobo 60947846c9bSJohannes Berg if (memcmp(hdr->addr4, sdata->vif.addr, ETH_ALEN) != 0) { 610eb2b9311SLuis Carlos Cobo u8 *ra, *da; 611eb2b9311SLuis Carlos Cobo 612e32f85f7SLuis Carlos Cobo da = hdr->addr3; 61335946a57SJavier Cardona ra = hdr->addr1; 614f698d856SJasper Bryant-Greene mpath = mesh_path_lookup(da, sdata); 615eb2b9311SLuis Carlos Cobo if (mpath) 616d19b3bf6SRui Paulo sn = ++mpath->sn; 61745904f21SJavier Cardona mesh_path_error_tx(sdata->u.mesh.mshcfg.element_ttl, skb->data, 61845904f21SJavier Cardona cpu_to_le32(sn), 6199f13084dSRui Paulo cpu_to_le16(PERR_RCODE_NO_ROUTE), ra, sdata); 620eb2b9311SLuis Carlos Cobo } 621eb2b9311SLuis Carlos Cobo 622eb2b9311SLuis Carlos Cobo kfree_skb(skb); 623472dbc45SJohannes Berg sdata->u.mesh.mshstats.dropped_frames_no_route++; 624eb2b9311SLuis Carlos Cobo } 625eb2b9311SLuis Carlos Cobo 626eb2b9311SLuis Carlos Cobo /** 627eb2b9311SLuis Carlos Cobo * mesh_path_flush_pending - free the pending queue of a mesh path 628eb2b9311SLuis Carlos Cobo * 629eb2b9311SLuis Carlos Cobo * @mpath: mesh path whose queue has to be freed 630eb2b9311SLuis Carlos Cobo * 631eb2b9311SLuis Carlos Cobo * Locking: the function must me called withing a rcu_read_lock region 632eb2b9311SLuis Carlos Cobo */ 633eb2b9311SLuis Carlos Cobo void mesh_path_flush_pending(struct mesh_path *mpath) 634eb2b9311SLuis Carlos Cobo { 635eb2b9311SLuis Carlos Cobo struct sk_buff *skb; 636eb2b9311SLuis Carlos Cobo 637eb2b9311SLuis Carlos Cobo while ((skb = skb_dequeue(&mpath->frame_queue)) && 638eb2b9311SLuis Carlos Cobo (mpath->flags & MESH_PATH_ACTIVE)) 639f698d856SJasper Bryant-Greene mesh_path_discard_frame(skb, mpath->sdata); 640eb2b9311SLuis Carlos Cobo } 641eb2b9311SLuis Carlos Cobo 642eb2b9311SLuis Carlos Cobo /** 643eb2b9311SLuis Carlos Cobo * mesh_path_fix_nexthop - force a specific next hop for a mesh path 644eb2b9311SLuis Carlos Cobo * 645eb2b9311SLuis Carlos Cobo * @mpath: the mesh path to modify 646eb2b9311SLuis Carlos Cobo * @next_hop: the next hop to force 647eb2b9311SLuis Carlos Cobo * 648eb2b9311SLuis Carlos Cobo * Locking: this function must be called holding mpath->state_lock 649eb2b9311SLuis Carlos Cobo */ 650eb2b9311SLuis Carlos Cobo void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop) 651eb2b9311SLuis Carlos Cobo { 652eb2b9311SLuis Carlos Cobo spin_lock_bh(&mpath->state_lock); 653eb2b9311SLuis Carlos Cobo mesh_path_assign_nexthop(mpath, next_hop); 654d19b3bf6SRui Paulo mpath->sn = 0xffff; 655eb2b9311SLuis Carlos Cobo mpath->metric = 0; 656eb2b9311SLuis Carlos Cobo mpath->hop_count = 0; 657eb2b9311SLuis Carlos Cobo mpath->exp_time = 0; 658eb2b9311SLuis Carlos Cobo mpath->flags |= MESH_PATH_FIXED; 659eb2b9311SLuis Carlos Cobo mesh_path_activate(mpath); 660eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 661eb2b9311SLuis Carlos Cobo mesh_path_tx_pending(mpath); 662eb2b9311SLuis Carlos Cobo } 663eb2b9311SLuis Carlos Cobo 664eb2b9311SLuis Carlos Cobo static void mesh_path_node_free(struct hlist_node *p, bool free_leafs) 665eb2b9311SLuis Carlos Cobo { 666eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 667eb2b9311SLuis Carlos Cobo struct mpath_node *node = hlist_entry(p, struct mpath_node, list); 668eb2b9311SLuis Carlos Cobo mpath = node->mpath; 669eb2b9311SLuis Carlos Cobo hlist_del_rcu(p); 670eb2b9311SLuis Carlos Cobo if (free_leafs) 671eb2b9311SLuis Carlos Cobo kfree(mpath); 672eb2b9311SLuis Carlos Cobo kfree(node); 673eb2b9311SLuis Carlos Cobo } 674eb2b9311SLuis Carlos Cobo 6754caf86c6SPavel Emelyanov static int mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl) 676eb2b9311SLuis Carlos Cobo { 677eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 678eb2b9311SLuis Carlos Cobo struct mpath_node *node, *new_node; 679eb2b9311SLuis Carlos Cobo u32 hash_idx; 680eb2b9311SLuis Carlos Cobo 6818566dc3fSPavel Emelyanov new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC); 68200242c40SPavel Emelyanov if (new_node == NULL) 68300242c40SPavel Emelyanov return -ENOMEM; 68400242c40SPavel Emelyanov 685eb2b9311SLuis Carlos Cobo node = hlist_entry(p, struct mpath_node, list); 686eb2b9311SLuis Carlos Cobo mpath = node->mpath; 687eb2b9311SLuis Carlos Cobo new_node->mpath = mpath; 688f698d856SJasper Bryant-Greene hash_idx = mesh_table_hash(mpath->dst, mpath->sdata, newtbl); 689eb2b9311SLuis Carlos Cobo hlist_add_head(&new_node->list, 690eb2b9311SLuis Carlos Cobo &newtbl->hash_buckets[hash_idx]); 6914caf86c6SPavel Emelyanov return 0; 692eb2b9311SLuis Carlos Cobo } 693eb2b9311SLuis Carlos Cobo 694eb2b9311SLuis Carlos Cobo int mesh_pathtbl_init(void) 695eb2b9311SLuis Carlos Cobo { 696eb2b9311SLuis Carlos Cobo mesh_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); 69779617deeSYanBo if (!mesh_paths) 69879617deeSYanBo return -ENOMEM; 699eb2b9311SLuis Carlos Cobo mesh_paths->free_node = &mesh_path_node_free; 700eb2b9311SLuis Carlos Cobo mesh_paths->copy_node = &mesh_path_node_copy; 701eb2b9311SLuis Carlos Cobo mesh_paths->mean_chain_len = MEAN_CHAIN_LEN; 70279617deeSYanBo 70379617deeSYanBo mpp_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); 70479617deeSYanBo if (!mpp_paths) { 70579617deeSYanBo mesh_table_free(mesh_paths, true); 706eb2b9311SLuis Carlos Cobo return -ENOMEM; 70779617deeSYanBo } 70879617deeSYanBo mpp_paths->free_node = &mesh_path_node_free; 70979617deeSYanBo mpp_paths->copy_node = &mesh_path_node_copy; 71079617deeSYanBo mpp_paths->mean_chain_len = MEAN_CHAIN_LEN; 71179617deeSYanBo 712eb2b9311SLuis Carlos Cobo return 0; 713eb2b9311SLuis Carlos Cobo } 714eb2b9311SLuis Carlos Cobo 715f698d856SJasper Bryant-Greene void mesh_path_expire(struct ieee80211_sub_if_data *sdata) 716eb2b9311SLuis Carlos Cobo { 717eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 718eb2b9311SLuis Carlos Cobo struct mpath_node *node; 719eb2b9311SLuis Carlos Cobo struct hlist_node *p; 720eb2b9311SLuis Carlos Cobo int i; 721eb2b9311SLuis Carlos Cobo 722eb2b9311SLuis Carlos Cobo read_lock(&pathtbl_resize_lock); 723eb2b9311SLuis Carlos Cobo for_each_mesh_entry(mesh_paths, p, node, i) { 724f698d856SJasper Bryant-Greene if (node->mpath->sdata != sdata) 725eb2b9311SLuis Carlos Cobo continue; 726eb2b9311SLuis Carlos Cobo mpath = node->mpath; 727eb2b9311SLuis Carlos Cobo spin_lock_bh(&mpath->state_lock); 728eb2b9311SLuis Carlos Cobo if ((!(mpath->flags & MESH_PATH_RESOLVING)) && 729eb2b9311SLuis Carlos Cobo (!(mpath->flags & MESH_PATH_FIXED)) && 730eb2b9311SLuis Carlos Cobo time_after(jiffies, 731eb2b9311SLuis Carlos Cobo mpath->exp_time + MESH_PATH_EXPIRE)) { 732eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 733f698d856SJasper Bryant-Greene mesh_path_del(mpath->dst, mpath->sdata); 734eb2b9311SLuis Carlos Cobo } else 735eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 736eb2b9311SLuis Carlos Cobo } 737eb2b9311SLuis Carlos Cobo read_unlock(&pathtbl_resize_lock); 738eb2b9311SLuis Carlos Cobo } 739eb2b9311SLuis Carlos Cobo 740eb2b9311SLuis Carlos Cobo void mesh_pathtbl_unregister(void) 741eb2b9311SLuis Carlos Cobo { 742eb2b9311SLuis Carlos Cobo mesh_table_free(mesh_paths, true); 74379617deeSYanBo mesh_table_free(mpp_paths, true); 744eb2b9311SLuis Carlos Cobo } 745