1eb2b9311SLuis Carlos Cobo /* 2eb2b9311SLuis Carlos Cobo * Copyright (c) 2008 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/netdevice.h> 13eb2b9311SLuis Carlos Cobo #include <linux/random.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; 40eb2b9311SLuis Carlos Cobo 41eb2b9311SLuis Carlos Cobo /* This lock will have the grow table function as writer and add / delete nodes 42eb2b9311SLuis Carlos Cobo * as readers. When reading the table (i.e. doing lookups) we are well protected 43eb2b9311SLuis Carlos Cobo * by RCU 44eb2b9311SLuis Carlos Cobo */ 45eb2b9311SLuis Carlos Cobo static DEFINE_RWLOCK(pathtbl_resize_lock); 46eb2b9311SLuis Carlos Cobo 47eb2b9311SLuis Carlos Cobo /** 48eb2b9311SLuis Carlos Cobo * 49eb2b9311SLuis Carlos Cobo * mesh_path_assign_nexthop - update mesh path next hop 50eb2b9311SLuis Carlos Cobo * 51eb2b9311SLuis Carlos Cobo * @mpath: mesh path to update 52eb2b9311SLuis Carlos Cobo * @sta: next hop to assign 53eb2b9311SLuis Carlos Cobo * 54eb2b9311SLuis Carlos Cobo * Locking: mpath->state_lock must be held when calling this function 55eb2b9311SLuis Carlos Cobo */ 56eb2b9311SLuis Carlos Cobo void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) 57eb2b9311SLuis Carlos Cobo { 58d0709a65SJohannes Berg rcu_assign_pointer(mpath->next_hop, sta); 59eb2b9311SLuis Carlos Cobo } 60eb2b9311SLuis Carlos Cobo 61eb2b9311SLuis Carlos Cobo 62eb2b9311SLuis Carlos Cobo /** 63eb2b9311SLuis Carlos Cobo * mesh_path_lookup - look up a path in the mesh path table 64eb2b9311SLuis Carlos Cobo * @dst: hardware address (ETH_ALEN length) of destination 65eb2b9311SLuis Carlos Cobo * @dev: local interface 66eb2b9311SLuis Carlos Cobo * 67eb2b9311SLuis Carlos Cobo * Returns: pointer to the mesh path structure, or NULL if not found 68eb2b9311SLuis Carlos Cobo * 69eb2b9311SLuis Carlos Cobo * Locking: must be called within a read rcu section. 70eb2b9311SLuis Carlos Cobo */ 71eb2b9311SLuis Carlos Cobo struct mesh_path *mesh_path_lookup(u8 *dst, struct net_device *dev) 72eb2b9311SLuis Carlos Cobo { 73eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 74eb2b9311SLuis Carlos Cobo struct hlist_node *n; 75eb2b9311SLuis Carlos Cobo struct hlist_head *bucket; 76eb2b9311SLuis Carlos Cobo struct mesh_table *tbl; 77eb2b9311SLuis Carlos Cobo struct mpath_node *node; 78eb2b9311SLuis Carlos Cobo 79eb2b9311SLuis Carlos Cobo tbl = rcu_dereference(mesh_paths); 80eb2b9311SLuis Carlos Cobo 81eb2b9311SLuis Carlos Cobo bucket = &tbl->hash_buckets[mesh_table_hash(dst, dev, tbl)]; 82eb2b9311SLuis Carlos Cobo hlist_for_each_entry_rcu(node, n, bucket, list) { 83eb2b9311SLuis Carlos Cobo mpath = node->mpath; 84eb2b9311SLuis Carlos Cobo if (mpath->dev == dev && 85eb2b9311SLuis Carlos Cobo memcmp(dst, mpath->dst, ETH_ALEN) == 0) { 86eb2b9311SLuis Carlos Cobo if (MPATH_EXPIRED(mpath)) { 87eb2b9311SLuis Carlos Cobo spin_lock_bh(&mpath->state_lock); 88eb2b9311SLuis Carlos Cobo if (MPATH_EXPIRED(mpath)) 89eb2b9311SLuis Carlos Cobo mpath->flags &= ~MESH_PATH_ACTIVE; 90eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 91eb2b9311SLuis Carlos Cobo } 92eb2b9311SLuis Carlos Cobo return mpath; 93eb2b9311SLuis Carlos Cobo } 94eb2b9311SLuis Carlos Cobo } 95eb2b9311SLuis Carlos Cobo return NULL; 96eb2b9311SLuis Carlos Cobo } 97eb2b9311SLuis Carlos Cobo 98eb2b9311SLuis Carlos Cobo /** 99eb2b9311SLuis Carlos Cobo * mesh_path_lookup_by_idx - look up a path in the mesh path table by its index 100eb2b9311SLuis Carlos Cobo * @idx: index 1012a8ca29aSLuis Carlos Cobo * @dev: local interface, or NULL for all entries 102eb2b9311SLuis Carlos Cobo * 103eb2b9311SLuis Carlos Cobo * Returns: pointer to the mesh path structure, or NULL if not found. 104eb2b9311SLuis Carlos Cobo * 105eb2b9311SLuis Carlos Cobo * Locking: must be called within a read rcu section. 106eb2b9311SLuis Carlos Cobo */ 107eb2b9311SLuis Carlos Cobo struct mesh_path *mesh_path_lookup_by_idx(int idx, struct net_device *dev) 108eb2b9311SLuis Carlos Cobo { 109eb2b9311SLuis Carlos Cobo struct mpath_node *node; 110eb2b9311SLuis Carlos Cobo struct hlist_node *p; 111eb2b9311SLuis Carlos Cobo int i; 112eb2b9311SLuis Carlos Cobo int j = 0; 113eb2b9311SLuis Carlos Cobo 1142a8ca29aSLuis Carlos Cobo for_each_mesh_entry(mesh_paths, p, node, i) { 1152a8ca29aSLuis Carlos Cobo if (dev && node->mpath->dev != dev) 1162a8ca29aSLuis Carlos Cobo continue; 117eb2b9311SLuis Carlos Cobo if (j++ == idx) { 118eb2b9311SLuis Carlos Cobo if (MPATH_EXPIRED(node->mpath)) { 119eb2b9311SLuis Carlos Cobo spin_lock_bh(&node->mpath->state_lock); 120eb2b9311SLuis Carlos Cobo if (MPATH_EXPIRED(node->mpath)) 121eb2b9311SLuis Carlos Cobo node->mpath->flags &= ~MESH_PATH_ACTIVE; 122eb2b9311SLuis Carlos Cobo spin_unlock_bh(&node->mpath->state_lock); 123eb2b9311SLuis Carlos Cobo } 124eb2b9311SLuis Carlos Cobo return node->mpath; 125eb2b9311SLuis Carlos Cobo } 1262a8ca29aSLuis Carlos Cobo } 127eb2b9311SLuis Carlos Cobo 128eb2b9311SLuis Carlos Cobo return NULL; 129eb2b9311SLuis Carlos Cobo } 130eb2b9311SLuis Carlos Cobo 131eb2b9311SLuis Carlos Cobo /** 132eb2b9311SLuis Carlos Cobo * mesh_path_add - allocate and add a new path to the mesh path table 133eb2b9311SLuis Carlos Cobo * @addr: destination address of the path (ETH_ALEN length) 134eb2b9311SLuis Carlos Cobo * @dev: local interface 135eb2b9311SLuis Carlos Cobo * 136eb2b9311SLuis Carlos Cobo * Returns: 0 on sucess 137eb2b9311SLuis Carlos Cobo * 138eb2b9311SLuis Carlos Cobo * State: the initial state of the new path is set to 0 139eb2b9311SLuis Carlos Cobo */ 140eb2b9311SLuis Carlos Cobo int mesh_path_add(u8 *dst, struct net_device *dev) 141eb2b9311SLuis Carlos Cobo { 142eb2b9311SLuis Carlos Cobo struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 143eb2b9311SLuis Carlos Cobo struct mesh_path *mpath, *new_mpath; 144eb2b9311SLuis Carlos Cobo struct mpath_node *node, *new_node; 145eb2b9311SLuis Carlos Cobo struct hlist_head *bucket; 146eb2b9311SLuis Carlos Cobo struct hlist_node *n; 147eb2b9311SLuis Carlos Cobo int grow = 0; 148eb2b9311SLuis Carlos Cobo int err = 0; 149eb2b9311SLuis Carlos Cobo u32 hash_idx; 150eb2b9311SLuis Carlos Cobo 151eb2b9311SLuis Carlos Cobo if (memcmp(dst, dev->dev_addr, ETH_ALEN) == 0) 152eb2b9311SLuis Carlos Cobo /* never add ourselves as neighbours */ 153eb2b9311SLuis Carlos Cobo return -ENOTSUPP; 154eb2b9311SLuis Carlos Cobo 155eb2b9311SLuis Carlos Cobo if (is_multicast_ether_addr(dst)) 156eb2b9311SLuis Carlos Cobo return -ENOTSUPP; 157eb2b9311SLuis Carlos Cobo 158eb2b9311SLuis Carlos Cobo if (atomic_add_unless(&sdata->u.sta.mpaths, 1, MESH_MAX_MPATHS) == 0) 159eb2b9311SLuis Carlos Cobo return -ENOSPC; 160eb2b9311SLuis Carlos Cobo 161402d7752SPavel Emelyanov err = -ENOMEM; 162eb2b9311SLuis Carlos Cobo new_mpath = kzalloc(sizeof(struct mesh_path), GFP_KERNEL); 163402d7752SPavel Emelyanov if (!new_mpath) 164402d7752SPavel Emelyanov goto err_path_alloc; 165402d7752SPavel Emelyanov 1660eb03d5aSPavel Emelyanov new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL); 167402d7752SPavel Emelyanov if (!new_node) 168402d7752SPavel Emelyanov goto err_node_alloc; 169f84e71a9SPavel Emelyanov 170f84e71a9SPavel Emelyanov read_lock(&pathtbl_resize_lock); 171eb2b9311SLuis Carlos Cobo memcpy(new_mpath->dst, dst, ETH_ALEN); 172eb2b9311SLuis Carlos Cobo new_mpath->dev = dev; 173eb2b9311SLuis Carlos Cobo new_mpath->flags = 0; 174eb2b9311SLuis Carlos Cobo skb_queue_head_init(&new_mpath->frame_queue); 175eb2b9311SLuis Carlos Cobo new_node->mpath = new_mpath; 176eb2b9311SLuis Carlos Cobo new_mpath->timer.data = (unsigned long) new_mpath; 177eb2b9311SLuis Carlos Cobo new_mpath->timer.function = mesh_path_timer; 178eb2b9311SLuis Carlos Cobo new_mpath->exp_time = jiffies; 179eb2b9311SLuis Carlos Cobo spin_lock_init(&new_mpath->state_lock); 180eb2b9311SLuis Carlos Cobo init_timer(&new_mpath->timer); 181eb2b9311SLuis Carlos Cobo 182eb2b9311SLuis Carlos Cobo hash_idx = mesh_table_hash(dst, dev, mesh_paths); 183eb2b9311SLuis Carlos Cobo bucket = &mesh_paths->hash_buckets[hash_idx]; 184eb2b9311SLuis Carlos Cobo 185eb2b9311SLuis Carlos Cobo spin_lock(&mesh_paths->hashwlock[hash_idx]); 186eb2b9311SLuis Carlos Cobo 187402d7752SPavel Emelyanov err = -EEXIST; 188eb2b9311SLuis Carlos Cobo hlist_for_each_entry(node, n, bucket, list) { 189eb2b9311SLuis Carlos Cobo mpath = node->mpath; 190402d7752SPavel Emelyanov if (mpath->dev == dev && memcmp(dst, mpath->dst, ETH_ALEN) == 0) 191402d7752SPavel Emelyanov goto err_exists; 192eb2b9311SLuis Carlos Cobo } 193eb2b9311SLuis Carlos Cobo 194eb2b9311SLuis Carlos Cobo hlist_add_head_rcu(&new_node->list, bucket); 195eb2b9311SLuis Carlos Cobo if (atomic_inc_return(&mesh_paths->entries) >= 196eb2b9311SLuis Carlos Cobo mesh_paths->mean_chain_len * (mesh_paths->hash_mask + 1)) 197eb2b9311SLuis Carlos Cobo grow = 1; 198eb2b9311SLuis Carlos Cobo 199eb2b9311SLuis Carlos Cobo spin_unlock(&mesh_paths->hashwlock[hash_idx]); 200eb2b9311SLuis Carlos Cobo read_unlock(&pathtbl_resize_lock); 201402d7752SPavel Emelyanov if (grow) { 202eb2b9311SLuis Carlos Cobo struct mesh_table *oldtbl, *newtbl; 203eb2b9311SLuis Carlos Cobo 204eb2b9311SLuis Carlos Cobo write_lock(&pathtbl_resize_lock); 205eb2b9311SLuis Carlos Cobo oldtbl = mesh_paths; 206eb2b9311SLuis Carlos Cobo newtbl = mesh_table_grow(mesh_paths); 207eb2b9311SLuis Carlos Cobo if (!newtbl) { 208eb2b9311SLuis Carlos Cobo write_unlock(&pathtbl_resize_lock); 209eb2b9311SLuis Carlos Cobo return -ENOMEM; 210eb2b9311SLuis Carlos Cobo } 211eb2b9311SLuis Carlos Cobo rcu_assign_pointer(mesh_paths, newtbl); 2126d6936e2SPavel Emelyanov write_unlock(&pathtbl_resize_lock); 2136d6936e2SPavel Emelyanov 214eb2b9311SLuis Carlos Cobo synchronize_rcu(); 215eb2b9311SLuis Carlos Cobo mesh_table_free(oldtbl, false); 216eb2b9311SLuis Carlos Cobo } 217402d7752SPavel Emelyanov return 0; 218402d7752SPavel Emelyanov 219402d7752SPavel Emelyanov err_exists: 220402d7752SPavel Emelyanov spin_unlock(&mesh_paths->hashwlock[hash_idx]); 221402d7752SPavel Emelyanov read_unlock(&pathtbl_resize_lock); 222402d7752SPavel Emelyanov kfree(new_node); 223402d7752SPavel Emelyanov err_node_alloc: 224402d7752SPavel Emelyanov kfree(new_mpath); 225402d7752SPavel Emelyanov err_path_alloc: 226402d7752SPavel Emelyanov atomic_dec(&sdata->u.sta.mpaths); 227eb2b9311SLuis Carlos Cobo return err; 228eb2b9311SLuis Carlos Cobo } 229eb2b9311SLuis Carlos Cobo 230eb2b9311SLuis Carlos Cobo 231eb2b9311SLuis Carlos Cobo /** 232eb2b9311SLuis Carlos Cobo * mesh_plink_broken - deactivates paths and sends perr when a link breaks 233eb2b9311SLuis Carlos Cobo * 234eb2b9311SLuis Carlos Cobo * @sta: broken peer link 235eb2b9311SLuis Carlos Cobo * 236eb2b9311SLuis Carlos Cobo * This function must be called from the rate control algorithm if enough 237eb2b9311SLuis Carlos Cobo * delivery errors suggest that a peer link is no longer usable. 238eb2b9311SLuis Carlos Cobo */ 239eb2b9311SLuis Carlos Cobo void mesh_plink_broken(struct sta_info *sta) 240eb2b9311SLuis Carlos Cobo { 241eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 242eb2b9311SLuis Carlos Cobo struct mpath_node *node; 243eb2b9311SLuis Carlos Cobo struct hlist_node *p; 244d0709a65SJohannes Berg struct net_device *dev = sta->sdata->dev; 245eb2b9311SLuis Carlos Cobo int i; 246eb2b9311SLuis Carlos Cobo 247eb2b9311SLuis Carlos Cobo rcu_read_lock(); 248eb2b9311SLuis Carlos Cobo for_each_mesh_entry(mesh_paths, p, node, i) { 249eb2b9311SLuis Carlos Cobo mpath = node->mpath; 250eb2b9311SLuis Carlos Cobo spin_lock_bh(&mpath->state_lock); 251eb2b9311SLuis Carlos Cobo if (mpath->next_hop == sta && 252eb2b9311SLuis Carlos Cobo mpath->flags & MESH_PATH_ACTIVE && 253eb2b9311SLuis Carlos Cobo !(mpath->flags & MESH_PATH_FIXED)) { 254eb2b9311SLuis Carlos Cobo mpath->flags &= ~MESH_PATH_ACTIVE; 255eb2b9311SLuis Carlos Cobo ++mpath->dsn; 256eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 257eb2b9311SLuis Carlos Cobo mesh_path_error_tx(mpath->dst, 258eb2b9311SLuis Carlos Cobo cpu_to_le32(mpath->dsn), 259eb2b9311SLuis Carlos Cobo dev->broadcast, dev); 260eb2b9311SLuis Carlos Cobo } else 261eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 262eb2b9311SLuis Carlos Cobo } 263eb2b9311SLuis Carlos Cobo rcu_read_unlock(); 264eb2b9311SLuis Carlos Cobo } 265c1edd987SJohannes Berg EXPORT_SYMBOL(mesh_plink_broken); 266eb2b9311SLuis Carlos Cobo 267eb2b9311SLuis Carlos Cobo /** 268eb2b9311SLuis Carlos Cobo * mesh_path_flush_by_nexthop - Deletes mesh paths if their next hop matches 269eb2b9311SLuis Carlos Cobo * 270eb2b9311SLuis Carlos Cobo * @sta - mesh peer to match 271eb2b9311SLuis Carlos Cobo * 272b4e08ea1SLuis Carlos Cobo * RCU notes: this function is called when a mesh plink transitions from 273b4e08ea1SLuis Carlos Cobo * PLINK_ESTAB to any other state, since PLINK_ESTAB state is the only one that 274b4e08ea1SLuis Carlos Cobo * allows path creation. This will happen before the sta can be freed (because 275d0709a65SJohannes Berg * sta_info_destroy() calls this) so any reader in a rcu read block will be 276d0709a65SJohannes Berg * protected against the plink disappearing. 277eb2b9311SLuis Carlos Cobo */ 278eb2b9311SLuis Carlos Cobo void mesh_path_flush_by_nexthop(struct sta_info *sta) 279eb2b9311SLuis Carlos Cobo { 280eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 281eb2b9311SLuis Carlos Cobo struct mpath_node *node; 282eb2b9311SLuis Carlos Cobo struct hlist_node *p; 283eb2b9311SLuis Carlos Cobo int i; 284eb2b9311SLuis Carlos Cobo 285eb2b9311SLuis Carlos Cobo for_each_mesh_entry(mesh_paths, p, node, i) { 286eb2b9311SLuis Carlos Cobo mpath = node->mpath; 287eb2b9311SLuis Carlos Cobo if (mpath->next_hop == sta) 288cfa22c71SLuis Carlos Cobo mesh_path_del(mpath->dst, mpath->dev); 289eb2b9311SLuis Carlos Cobo } 290eb2b9311SLuis Carlos Cobo } 291eb2b9311SLuis Carlos Cobo 292eb2b9311SLuis Carlos Cobo void mesh_path_flush(struct net_device *dev) 293eb2b9311SLuis Carlos Cobo { 294eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 295eb2b9311SLuis Carlos Cobo struct mpath_node *node; 296eb2b9311SLuis Carlos Cobo struct hlist_node *p; 297eb2b9311SLuis Carlos Cobo int i; 298eb2b9311SLuis Carlos Cobo 299eb2b9311SLuis Carlos Cobo for_each_mesh_entry(mesh_paths, p, node, i) { 300eb2b9311SLuis Carlos Cobo mpath = node->mpath; 301eb2b9311SLuis Carlos Cobo if (mpath->dev == dev) 302cfa22c71SLuis Carlos Cobo mesh_path_del(mpath->dst, mpath->dev); 303eb2b9311SLuis Carlos Cobo } 304eb2b9311SLuis Carlos Cobo } 305eb2b9311SLuis Carlos Cobo 306eb2b9311SLuis Carlos Cobo static void mesh_path_node_reclaim(struct rcu_head *rp) 307eb2b9311SLuis Carlos Cobo { 308eb2b9311SLuis Carlos Cobo struct mpath_node *node = container_of(rp, struct mpath_node, rcu); 309eb2b9311SLuis Carlos Cobo struct ieee80211_sub_if_data *sdata = 310eb2b9311SLuis Carlos Cobo IEEE80211_DEV_TO_SUB_IF(node->mpath->dev); 311d0709a65SJohannes Berg 31289a1ad69SLuis Carlos Cobo del_timer_sync(&node->mpath->timer); 313eb2b9311SLuis Carlos Cobo atomic_dec(&sdata->u.sta.mpaths); 314eb2b9311SLuis Carlos Cobo kfree(node->mpath); 315eb2b9311SLuis Carlos Cobo kfree(node); 316eb2b9311SLuis Carlos Cobo } 317eb2b9311SLuis Carlos Cobo 318eb2b9311SLuis Carlos Cobo /** 319eb2b9311SLuis Carlos Cobo * mesh_path_del - delete a mesh path from the table 320eb2b9311SLuis Carlos Cobo * 321eb2b9311SLuis Carlos Cobo * @addr: dst address (ETH_ALEN length) 322eb2b9311SLuis Carlos Cobo * @dev: local interface 323eb2b9311SLuis Carlos Cobo * 324eb2b9311SLuis Carlos Cobo * Returns: 0 if succesful 325eb2b9311SLuis Carlos Cobo */ 326cfa22c71SLuis Carlos Cobo int mesh_path_del(u8 *addr, struct net_device *dev) 327eb2b9311SLuis Carlos Cobo { 328eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 329eb2b9311SLuis Carlos Cobo struct mpath_node *node; 330eb2b9311SLuis Carlos Cobo struct hlist_head *bucket; 331eb2b9311SLuis Carlos Cobo struct hlist_node *n; 332eb2b9311SLuis Carlos Cobo int hash_idx; 333eb2b9311SLuis Carlos Cobo int err = 0; 334eb2b9311SLuis Carlos Cobo 335eb2b9311SLuis Carlos Cobo read_lock(&pathtbl_resize_lock); 336eb2b9311SLuis Carlos Cobo hash_idx = mesh_table_hash(addr, dev, mesh_paths); 337eb2b9311SLuis Carlos Cobo bucket = &mesh_paths->hash_buckets[hash_idx]; 338eb2b9311SLuis Carlos Cobo 339eb2b9311SLuis Carlos Cobo spin_lock(&mesh_paths->hashwlock[hash_idx]); 340eb2b9311SLuis Carlos Cobo hlist_for_each_entry(node, n, bucket, list) { 341eb2b9311SLuis Carlos Cobo mpath = node->mpath; 342eb2b9311SLuis Carlos Cobo if (mpath->dev == dev && 343eb2b9311SLuis Carlos Cobo memcmp(addr, mpath->dst, ETH_ALEN) == 0) { 344eb2b9311SLuis Carlos Cobo spin_lock_bh(&mpath->state_lock); 345eb2b9311SLuis Carlos Cobo mpath->flags |= MESH_PATH_RESOLVING; 346eb2b9311SLuis Carlos Cobo hlist_del_rcu(&node->list); 347eb2b9311SLuis Carlos Cobo call_rcu(&node->rcu, mesh_path_node_reclaim); 348eb2b9311SLuis Carlos Cobo atomic_dec(&mesh_paths->entries); 349eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 350eb2b9311SLuis Carlos Cobo goto enddel; 351eb2b9311SLuis Carlos Cobo } 352eb2b9311SLuis Carlos Cobo } 353eb2b9311SLuis Carlos Cobo 354eb2b9311SLuis Carlos Cobo err = -ENXIO; 355eb2b9311SLuis Carlos Cobo enddel: 356eb2b9311SLuis Carlos Cobo spin_unlock(&mesh_paths->hashwlock[hash_idx]); 357eb2b9311SLuis Carlos Cobo read_unlock(&pathtbl_resize_lock); 358eb2b9311SLuis Carlos Cobo return err; 359eb2b9311SLuis Carlos Cobo } 360eb2b9311SLuis Carlos Cobo 361eb2b9311SLuis Carlos Cobo /** 362eb2b9311SLuis Carlos Cobo * mesh_path_tx_pending - sends pending frames in a mesh path queue 363eb2b9311SLuis Carlos Cobo * 364eb2b9311SLuis Carlos Cobo * @mpath: mesh path to activate 365eb2b9311SLuis Carlos Cobo * 366eb2b9311SLuis Carlos Cobo * Locking: the state_lock of the mpath structure must NOT be held when calling 367eb2b9311SLuis Carlos Cobo * this function. 368eb2b9311SLuis Carlos Cobo */ 369eb2b9311SLuis Carlos Cobo void mesh_path_tx_pending(struct mesh_path *mpath) 370eb2b9311SLuis Carlos Cobo { 371eb2b9311SLuis Carlos Cobo struct sk_buff *skb; 372eb2b9311SLuis Carlos Cobo 373eb2b9311SLuis Carlos Cobo while ((skb = skb_dequeue(&mpath->frame_queue)) && 374eb2b9311SLuis Carlos Cobo (mpath->flags & MESH_PATH_ACTIVE)) 375eb2b9311SLuis Carlos Cobo dev_queue_xmit(skb); 376eb2b9311SLuis Carlos Cobo } 377eb2b9311SLuis Carlos Cobo 378eb2b9311SLuis Carlos Cobo /** 379eb2b9311SLuis Carlos Cobo * mesh_path_discard_frame - discard a frame whose path could not be resolved 380eb2b9311SLuis Carlos Cobo * 381eb2b9311SLuis Carlos Cobo * @skb: frame to discard 382eb2b9311SLuis Carlos Cobo * @dev: network device the frame was to be sent through 383eb2b9311SLuis Carlos Cobo * 384eb2b9311SLuis Carlos Cobo * If the frame was beign forwarded from another MP, a PERR frame will be sent 385eb2b9311SLuis Carlos Cobo * to the precursor. 386eb2b9311SLuis Carlos Cobo * 387eb2b9311SLuis Carlos Cobo * Locking: the function must me called within a rcu_read_lock region 388eb2b9311SLuis Carlos Cobo */ 389eb2b9311SLuis Carlos Cobo void mesh_path_discard_frame(struct sk_buff *skb, struct net_device *dev) 390eb2b9311SLuis Carlos Cobo { 391eb2b9311SLuis Carlos Cobo struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 392eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 393eb2b9311SLuis Carlos Cobo u32 dsn = 0; 394eb2b9311SLuis Carlos Cobo 395eb2b9311SLuis Carlos Cobo if (skb->pkt_type == PACKET_OTHERHOST) { 396eb2b9311SLuis Carlos Cobo struct ieee80211s_hdr *prev_meshhdr; 397eb2b9311SLuis Carlos Cobo int mshhdrlen; 398eb2b9311SLuis Carlos Cobo u8 *ra, *da; 399eb2b9311SLuis Carlos Cobo 400eb2b9311SLuis Carlos Cobo prev_meshhdr = ((struct ieee80211s_hdr *)skb->cb); 401eb2b9311SLuis Carlos Cobo mshhdrlen = ieee80211_get_mesh_hdrlen(prev_meshhdr); 402eb2b9311SLuis Carlos Cobo da = skb->data; 403eb2b9311SLuis Carlos Cobo ra = MESH_PREQ(skb); 404eb2b9311SLuis Carlos Cobo mpath = mesh_path_lookup(da, dev); 405eb2b9311SLuis Carlos Cobo if (mpath) 406eb2b9311SLuis Carlos Cobo dsn = ++mpath->dsn; 407eb2b9311SLuis Carlos Cobo mesh_path_error_tx(skb->data, cpu_to_le32(dsn), ra, dev); 408eb2b9311SLuis Carlos Cobo } 409eb2b9311SLuis Carlos Cobo 410eb2b9311SLuis Carlos Cobo kfree_skb(skb); 411eb2b9311SLuis Carlos Cobo sdata->u.sta.mshstats.dropped_frames_no_route++; 412eb2b9311SLuis Carlos Cobo } 413eb2b9311SLuis Carlos Cobo 414eb2b9311SLuis Carlos Cobo /** 415eb2b9311SLuis Carlos Cobo * mesh_path_flush_pending - free the pending queue of a mesh path 416eb2b9311SLuis Carlos Cobo * 417eb2b9311SLuis Carlos Cobo * @mpath: mesh path whose queue has to be freed 418eb2b9311SLuis Carlos Cobo * 419eb2b9311SLuis Carlos Cobo * Locking: the function must me called withing a rcu_read_lock region 420eb2b9311SLuis Carlos Cobo */ 421eb2b9311SLuis Carlos Cobo void mesh_path_flush_pending(struct mesh_path *mpath) 422eb2b9311SLuis Carlos Cobo { 423eb2b9311SLuis Carlos Cobo struct ieee80211_sub_if_data *sdata; 424eb2b9311SLuis Carlos Cobo struct sk_buff *skb; 425eb2b9311SLuis Carlos Cobo 426eb2b9311SLuis Carlos Cobo sdata = IEEE80211_DEV_TO_SUB_IF(mpath->dev); 427eb2b9311SLuis Carlos Cobo 428eb2b9311SLuis Carlos Cobo while ((skb = skb_dequeue(&mpath->frame_queue)) && 429eb2b9311SLuis Carlos Cobo (mpath->flags & MESH_PATH_ACTIVE)) 430eb2b9311SLuis Carlos Cobo mesh_path_discard_frame(skb, mpath->dev); 431eb2b9311SLuis Carlos Cobo } 432eb2b9311SLuis Carlos Cobo 433eb2b9311SLuis Carlos Cobo /** 434eb2b9311SLuis Carlos Cobo * mesh_path_fix_nexthop - force a specific next hop for a mesh path 435eb2b9311SLuis Carlos Cobo * 436eb2b9311SLuis Carlos Cobo * @mpath: the mesh path to modify 437eb2b9311SLuis Carlos Cobo * @next_hop: the next hop to force 438eb2b9311SLuis Carlos Cobo * 439eb2b9311SLuis Carlos Cobo * Locking: this function must be called holding mpath->state_lock 440eb2b9311SLuis Carlos Cobo */ 441eb2b9311SLuis Carlos Cobo void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop) 442eb2b9311SLuis Carlos Cobo { 443eb2b9311SLuis Carlos Cobo spin_lock_bh(&mpath->state_lock); 444eb2b9311SLuis Carlos Cobo mesh_path_assign_nexthop(mpath, next_hop); 445eb2b9311SLuis Carlos Cobo mpath->dsn = 0xffff; 446eb2b9311SLuis Carlos Cobo mpath->metric = 0; 447eb2b9311SLuis Carlos Cobo mpath->hop_count = 0; 448eb2b9311SLuis Carlos Cobo mpath->exp_time = 0; 449eb2b9311SLuis Carlos Cobo mpath->flags |= MESH_PATH_FIXED; 450eb2b9311SLuis Carlos Cobo mesh_path_activate(mpath); 451eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 452eb2b9311SLuis Carlos Cobo mesh_path_tx_pending(mpath); 453eb2b9311SLuis Carlos Cobo } 454eb2b9311SLuis Carlos Cobo 455eb2b9311SLuis Carlos Cobo static void mesh_path_node_free(struct hlist_node *p, bool free_leafs) 456eb2b9311SLuis Carlos Cobo { 457eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 458eb2b9311SLuis Carlos Cobo struct mpath_node *node = hlist_entry(p, struct mpath_node, list); 459eb2b9311SLuis Carlos Cobo mpath = node->mpath; 460eb2b9311SLuis Carlos Cobo hlist_del_rcu(p); 461eb2b9311SLuis Carlos Cobo if (free_leafs) 462eb2b9311SLuis Carlos Cobo kfree(mpath); 463eb2b9311SLuis Carlos Cobo kfree(node); 464eb2b9311SLuis Carlos Cobo } 465eb2b9311SLuis Carlos Cobo 4664caf86c6SPavel Emelyanov static int mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl) 467eb2b9311SLuis Carlos Cobo { 468eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 469eb2b9311SLuis Carlos Cobo struct mpath_node *node, *new_node; 470eb2b9311SLuis Carlos Cobo u32 hash_idx; 471eb2b9311SLuis Carlos Cobo 4728566dc3fSPavel Emelyanov new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC); 47300242c40SPavel Emelyanov if (new_node == NULL) 47400242c40SPavel Emelyanov return -ENOMEM; 47500242c40SPavel Emelyanov 476eb2b9311SLuis Carlos Cobo node = hlist_entry(p, struct mpath_node, list); 477eb2b9311SLuis Carlos Cobo mpath = node->mpath; 478eb2b9311SLuis Carlos Cobo new_node->mpath = mpath; 479eb2b9311SLuis Carlos Cobo hash_idx = mesh_table_hash(mpath->dst, mpath->dev, newtbl); 480eb2b9311SLuis Carlos Cobo hlist_add_head(&new_node->list, 481eb2b9311SLuis Carlos Cobo &newtbl->hash_buckets[hash_idx]); 4824caf86c6SPavel Emelyanov return 0; 483eb2b9311SLuis Carlos Cobo } 484eb2b9311SLuis Carlos Cobo 485eb2b9311SLuis Carlos Cobo int mesh_pathtbl_init(void) 486eb2b9311SLuis Carlos Cobo { 487eb2b9311SLuis Carlos Cobo mesh_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); 488eb2b9311SLuis Carlos Cobo mesh_paths->free_node = &mesh_path_node_free; 489eb2b9311SLuis Carlos Cobo mesh_paths->copy_node = &mesh_path_node_copy; 490eb2b9311SLuis Carlos Cobo mesh_paths->mean_chain_len = MEAN_CHAIN_LEN; 491eb2b9311SLuis Carlos Cobo if (!mesh_paths) 492eb2b9311SLuis Carlos Cobo return -ENOMEM; 493eb2b9311SLuis Carlos Cobo return 0; 494eb2b9311SLuis Carlos Cobo } 495eb2b9311SLuis Carlos Cobo 496eb2b9311SLuis Carlos Cobo void mesh_path_expire(struct net_device *dev) 497eb2b9311SLuis Carlos Cobo { 498eb2b9311SLuis Carlos Cobo struct mesh_path *mpath; 499eb2b9311SLuis Carlos Cobo struct mpath_node *node; 500eb2b9311SLuis Carlos Cobo struct hlist_node *p; 501eb2b9311SLuis Carlos Cobo int i; 502eb2b9311SLuis Carlos Cobo 503eb2b9311SLuis Carlos Cobo read_lock(&pathtbl_resize_lock); 504eb2b9311SLuis Carlos Cobo for_each_mesh_entry(mesh_paths, p, node, i) { 505eb2b9311SLuis Carlos Cobo if (node->mpath->dev != dev) 506eb2b9311SLuis Carlos Cobo continue; 507eb2b9311SLuis Carlos Cobo mpath = node->mpath; 508eb2b9311SLuis Carlos Cobo spin_lock_bh(&mpath->state_lock); 509eb2b9311SLuis Carlos Cobo if ((!(mpath->flags & MESH_PATH_RESOLVING)) && 510eb2b9311SLuis Carlos Cobo (!(mpath->flags & MESH_PATH_FIXED)) && 511eb2b9311SLuis Carlos Cobo time_after(jiffies, 512eb2b9311SLuis Carlos Cobo mpath->exp_time + MESH_PATH_EXPIRE)) { 513eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 514cfa22c71SLuis Carlos Cobo mesh_path_del(mpath->dst, mpath->dev); 515eb2b9311SLuis Carlos Cobo } else 516eb2b9311SLuis Carlos Cobo spin_unlock_bh(&mpath->state_lock); 517eb2b9311SLuis Carlos Cobo } 518eb2b9311SLuis Carlos Cobo read_unlock(&pathtbl_resize_lock); 519eb2b9311SLuis Carlos Cobo } 520eb2b9311SLuis Carlos Cobo 521eb2b9311SLuis Carlos Cobo void mesh_pathtbl_unregister(void) 522eb2b9311SLuis Carlos Cobo { 523eb2b9311SLuis Carlos Cobo mesh_table_free(mesh_paths, true); 524eb2b9311SLuis Carlos Cobo } 525