Merge git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/v4l-dvb
[pandora-kernel.git] / net / mac80211 / mesh_pathtbl.c
index a17f2b2..5f88a2e 100644 (file)
@@ -98,7 +98,7 @@ struct mesh_path *mesh_path_lookup(u8 *dst, struct net_device *dev)
 /**
  * mesh_path_lookup_by_idx - look up a path in the mesh path table by its index
  * @idx: index
- * @dev: local interface
+ * @dev: local interface, or NULL for all entries
  *
  * Returns: pointer to the mesh path structure, or NULL if not found.
  *
@@ -111,7 +111,9 @@ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct net_device *dev)
        int i;
        int j = 0;
 
-       for_each_mesh_entry(mesh_paths, p, node, i)
+       for_each_mesh_entry(mesh_paths, p, node, i) {
+               if (dev && node->mpath->dev != dev)
+                       continue;
                if (j++ == idx) {
                        if (MPATH_EXPIRED(node->mpath)) {
                                spin_lock_bh(&node->mpath->state_lock);
@@ -121,6 +123,7 @@ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct net_device *dev)
                        }
                        return node->mpath;
                }
+       }
 
        return NULL;
 }
@@ -155,19 +158,20 @@ int mesh_path_add(u8 *dst, struct net_device *dev)
        if (atomic_add_unless(&sdata->u.sta.mpaths, 1, MESH_MAX_MPATHS) == 0)
                return -ENOSPC;
 
-       read_lock(&pathtbl_resize_lock);
-
+       err = -ENOMEM;
        new_mpath = kzalloc(sizeof(struct mesh_path), GFP_KERNEL);
-       if (!new_mpath) {
-               atomic_dec(&sdata->u.sta.mpaths);
-               err = -ENOMEM;
-               goto endadd2;
-       }
+       if (!new_mpath)
+               goto err_path_alloc;
+
+       new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL);
+       if (!new_node)
+               goto err_node_alloc;
+
+       read_lock(&pathtbl_resize_lock);
        memcpy(new_mpath->dst, dst, ETH_ALEN);
        new_mpath->dev = dev;
        new_mpath->flags = 0;
        skb_queue_head_init(&new_mpath->frame_queue);
-       new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL);
        new_node->mpath = new_mpath;
        new_mpath->timer.data = (unsigned long) new_mpath;
        new_mpath->timer.function = mesh_path_timer;
@@ -180,16 +184,11 @@ int mesh_path_add(u8 *dst, struct net_device *dev)
 
        spin_lock(&mesh_paths->hashwlock[hash_idx]);
 
+       err = -EEXIST;
        hlist_for_each_entry(node, n, bucket, list) {
                mpath = node->mpath;
-               if (mpath->dev == dev && memcmp(dst, mpath->dst, ETH_ALEN)
-                               == 0) {
-                       err = -EEXIST;
-                       atomic_dec(&sdata->u.sta.mpaths);
-                       kfree(new_node);
-                       kfree(new_mpath);
-                       goto endadd;
-               }
+               if (mpath->dev == dev && memcmp(dst, mpath->dst, ETH_ALEN) == 0)
+                       goto err_exists;
        }
 
        hlist_add_head_rcu(&new_node->list, bucket);
@@ -197,11 +196,9 @@ int mesh_path_add(u8 *dst, struct net_device *dev)
                mesh_paths->mean_chain_len * (mesh_paths->hash_mask + 1))
                grow = 1;
 
-endadd:
        spin_unlock(&mesh_paths->hashwlock[hash_idx]);
-endadd2:
        read_unlock(&pathtbl_resize_lock);
-       if (!err && grow) {
+       if (grow) {
                struct mesh_table *oldtbl, *newtbl;
 
                write_lock(&pathtbl_resize_lock);
@@ -209,13 +206,24 @@ endadd2:
                newtbl = mesh_table_grow(mesh_paths);
                if (!newtbl) {
                        write_unlock(&pathtbl_resize_lock);
-                       return -ENOMEM;
+                       return 0;
                }
                rcu_assign_pointer(mesh_paths, newtbl);
+               write_unlock(&pathtbl_resize_lock);
+
                synchronize_rcu();
                mesh_table_free(oldtbl, false);
-               write_unlock(&pathtbl_resize_lock);
        }
+       return 0;
+
+err_exists:
+       spin_unlock(&mesh_paths->hashwlock[hash_idx]);
+       read_unlock(&pathtbl_resize_lock);
+       kfree(new_node);
+err_node_alloc:
+       kfree(new_mpath);
+err_path_alloc:
+       atomic_dec(&sdata->u.sta.mpaths);
        return err;
 }
 
@@ -254,16 +262,15 @@ void mesh_plink_broken(struct sta_info *sta)
        }
        rcu_read_unlock();
 }
-EXPORT_SYMBOL(mesh_plink_broken);
 
 /**
  * mesh_path_flush_by_nexthop - Deletes mesh paths if their next hop matches
  *
  * @sta - mesh peer to match
  *
- * RCU notes: this function is called when a mesh plink transitions from ESTAB
- * to any other state, since ESTAB state is the only one that allows path
- * creation. This will happen before the sta can be freed (because
+ * RCU notes: this function is called when a mesh plink transitions from
+ * PLINK_ESTAB to any other state, since PLINK_ESTAB state is the only one that
+ * allows path creation. This will happen before the sta can be freed (because
  * sta_info_destroy() calls this) so any reader in a rcu read block will be
  * protected against the plink disappearing.
  */
@@ -277,7 +284,7 @@ void mesh_path_flush_by_nexthop(struct sta_info *sta)
        for_each_mesh_entry(mesh_paths, p, node, i) {
                mpath = node->mpath;
                if (mpath->next_hop == sta)
-                       mesh_path_del(mpath->dst, mpath->dev, true);
+                       mesh_path_del(mpath->dst, mpath->dev);
        }
 }
 
@@ -291,7 +298,7 @@ void mesh_path_flush(struct net_device *dev)
        for_each_mesh_entry(mesh_paths, p, node, i) {
                mpath = node->mpath;
                if (mpath->dev == dev)
-                       mesh_path_del(mpath->dst, mpath->dev, false);
+                       mesh_path_del(mpath->dst, mpath->dev);
        }
 }
 
@@ -301,7 +308,7 @@ static void mesh_path_node_reclaim(struct rcu_head *rp)
        struct ieee80211_sub_if_data *sdata =
                IEEE80211_DEV_TO_SUB_IF(node->mpath->dev);
 
-       rcu_assign_pointer(node->mpath->next_hop, NULL);
+       del_timer_sync(&node->mpath->timer);
        atomic_dec(&sdata->u.sta.mpaths);
        kfree(node->mpath);
        kfree(node);
@@ -314,12 +321,8 @@ static void mesh_path_node_reclaim(struct rcu_head *rp)
  * @dev: local interface
  *
  * Returns: 0 if succesful
- *
- * State: if the path is being resolved, the deletion will be postponed until
- * the path resolution completes or times out, unless the force parameter
- * is given.
  */
-int mesh_path_del(u8 *addr, struct net_device *dev, bool force)
+int mesh_path_del(u8 *addr, struct net_device *dev)
 {
        struct mesh_path *mpath;
        struct mpath_node *node;
@@ -338,14 +341,10 @@ int mesh_path_del(u8 *addr, struct net_device *dev, bool force)
                if (mpath->dev == dev &&
                                memcmp(addr, mpath->dst, ETH_ALEN) == 0) {
                        spin_lock_bh(&mpath->state_lock);
-                       if (!force && mpath->flags & MESH_PATH_RESOLVING) {
-                               mpath->flags |= MESH_PATH_DELETE;
-                       } else {
-                               mpath->flags |= MESH_PATH_RESOLVING;
-                               hlist_del_rcu(&node->list);
-                               call_rcu(&node->rcu, mesh_path_node_reclaim);
-                               atomic_dec(&mesh_paths->entries);
-                       }
+                       mpath->flags |= MESH_PATH_RESOLVING;
+                       hlist_del_rcu(&node->list);
+                       call_rcu(&node->rcu, mesh_path_node_reclaim);
+                       atomic_dec(&mesh_paths->entries);
                        spin_unlock_bh(&mpath->state_lock);
                        goto enddel;
                }
@@ -458,25 +457,28 @@ static void mesh_path_node_free(struct hlist_node *p, bool free_leafs)
        struct mpath_node *node = hlist_entry(p, struct mpath_node, list);
        mpath = node->mpath;
        hlist_del_rcu(p);
-       synchronize_rcu();
        if (free_leafs)
                kfree(mpath);
        kfree(node);
 }
 
-static void mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl)
+static int mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl)
 {
        struct mesh_path *mpath;
        struct mpath_node *node, *new_node;
        u32 hash_idx;
 
+       new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC);
+       if (new_node == NULL)
+               return -ENOMEM;
+
        node = hlist_entry(p, struct mpath_node, list);
        mpath = node->mpath;
-       new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL);
        new_node->mpath = mpath;
        hash_idx = mesh_table_hash(mpath->dst, mpath->dev, newtbl);
        hlist_add_head(&new_node->list,
                        &newtbl->hash_buckets[hash_idx]);
+       return 0;
 }
 
 int mesh_pathtbl_init(void)
@@ -508,7 +510,7 @@ void mesh_path_expire(struct net_device *dev)
                        time_after(jiffies,
                         mpath->exp_time + MESH_PATH_EXPIRE)) {
                        spin_unlock_bh(&mpath->state_lock);
-                       mesh_path_del(mpath->dst, mpath->dev, false);
+                       mesh_path_del(mpath->dst, mpath->dev);
                } else
                        spin_unlock_bh(&mpath->state_lock);
        }