Skip to content

Commit b4c3fbe

Browse files
herbertxjmberg-intel
authored andcommitted
mac80211: Use linked list instead of rhashtable walk for mesh tables
The mesh table code walks over hash tables for two purposes. First of all it's used as part of a netlink dump process, but it is also used for looking up entries to delete using criteria other than the hash key. The second purpose is directly contrary to the design specification of rhashtable walks. It is only meant for use by netlink dumps. This is because rhashtable is resizable and you cannot obtain a stable walk over it during a resize process. In fact mesh's use of rhashtable for dumping is bogus too. Rather than using rhashtable walk's iterator to keep track of the current position, it always converts the current position to an integer which defeats the purpose of the iterator. Therefore this patch converts all uses of rhashtable walk into a simple linked list. This patch also adds a new spin lock to protect the hash table insertion/removal as well as the walk list modifications. In fact the previous code was buggy as the removals can race with each other, potentially resulting in a double-free. Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
1 parent f9bcc9f commit b4c3fbe

File tree

2 files changed

+43
-101
lines changed

2 files changed

+43
-101
lines changed

net/mac80211/mesh.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ enum mesh_deferred_task_flags {
7070
* @dst: mesh path destination mac address
7171
* @mpp: mesh proxy mac address
7272
* @rhash: rhashtable list pointer
73+
* @walk_list: linked list containing all mesh_path objects.
7374
* @gate_list: list pointer for known gates list
7475
* @sdata: mesh subif
7576
* @next_hop: mesh neighbor to which frames for this destination will be
@@ -105,6 +106,7 @@ struct mesh_path {
105106
u8 dst[ETH_ALEN];
106107
u8 mpp[ETH_ALEN]; /* used for MPP or MAP */
107108
struct rhash_head rhash;
109+
struct hlist_node walk_list;
108110
struct hlist_node gate_list;
109111
struct ieee80211_sub_if_data *sdata;
110112
struct sta_info __rcu *next_hop;
@@ -133,12 +135,16 @@ struct mesh_path {
133135
* gate's mpath may or may not be resolved and active.
134136
* @gates_lock: protects updates to known_gates
135137
* @rhead: the rhashtable containing struct mesh_paths, keyed by dest addr
138+
* @walk_head: linked list containging all mesh_path objects
139+
* @walk_lock: lock protecting walk_head
136140
* @entries: number of entries in the table
137141
*/
138142
struct mesh_table {
139143
struct hlist_head known_gates;
140144
spinlock_t gates_lock;
141145
struct rhashtable rhead;
146+
struct hlist_head walk_head;
147+
spinlock_t walk_lock;
142148
atomic_t entries; /* Up to MAX_MESH_NEIGHBOURS */
143149
};
144150

net/mac80211/mesh_pathtbl.c

Lines changed: 37 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,10 @@ static struct mesh_table *mesh_table_alloc(void)
5959
return NULL;
6060

6161
INIT_HLIST_HEAD(&newtbl->known_gates);
62+
INIT_HLIST_HEAD(&newtbl->walk_head);
6263
atomic_set(&newtbl->entries, 0);
6364
spin_lock_init(&newtbl->gates_lock);
65+
spin_lock_init(&newtbl->walk_lock);
6466

6567
return newtbl;
6668
}
@@ -249,28 +251,15 @@ mpp_path_lookup(struct ieee80211_sub_if_data *sdata, const u8 *dst)
249251
static struct mesh_path *
250252
__mesh_path_lookup_by_idx(struct mesh_table *tbl, int idx)
251253
{
252-
int i = 0, ret;
253-
struct mesh_path *mpath = NULL;
254-
struct rhashtable_iter iter;
255-
256-
ret = rhashtable_walk_init(&tbl->rhead, &iter, GFP_ATOMIC);
257-
if (ret)
258-
return NULL;
259-
260-
rhashtable_walk_start(&iter);
254+
int i = 0;
255+
struct mesh_path *mpath;
261256

262-
while ((mpath = rhashtable_walk_next(&iter))) {
263-
if (IS_ERR(mpath) && PTR_ERR(mpath) == -EAGAIN)
264-
continue;
265-
if (IS_ERR(mpath))
266-
break;
257+
hlist_for_each_entry_rcu(mpath, &tbl->walk_head, walk_list) {
267258
if (i++ == idx)
268259
break;
269260
}
270-
rhashtable_walk_stop(&iter);
271-
rhashtable_walk_exit(&iter);
272261

273-
if (IS_ERR(mpath) || !mpath)
262+
if (!mpath)
274263
return NULL;
275264

276265
if (mpath_expired(mpath)) {
@@ -432,6 +421,7 @@ struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata,
432421
return ERR_PTR(-ENOMEM);
433422

434423
tbl = sdata->u.mesh.mesh_paths;
424+
spin_lock_bh(&tbl->walk_lock);
435425
do {
436426
ret = rhashtable_lookup_insert_fast(&tbl->rhead,
437427
&new_mpath->rhash,
@@ -441,8 +431,10 @@ struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata,
441431
mpath = rhashtable_lookup_fast(&tbl->rhead,
442432
dst,
443433
mesh_rht_params);
444-
434+
else if (!ret)
435+
hlist_add_head(&new_mpath->walk_list, &tbl->walk_head);
445436
} while (unlikely(ret == -EEXIST && !mpath));
437+
spin_unlock_bh(&tbl->walk_lock);
446438

447439
if (ret && ret != -EEXIST)
448440
return ERR_PTR(ret);
@@ -480,9 +472,14 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata,
480472

481473
memcpy(new_mpath->mpp, mpp, ETH_ALEN);
482474
tbl = sdata->u.mesh.mpp_paths;
475+
476+
spin_lock_bh(&tbl->walk_lock);
483477
ret = rhashtable_lookup_insert_fast(&tbl->rhead,
484478
&new_mpath->rhash,
485479
mesh_rht_params);
480+
if (!ret)
481+
hlist_add_head_rcu(&new_mpath->walk_list, &tbl->walk_head);
482+
spin_unlock_bh(&tbl->walk_lock);
486483

487484
sdata->u.mesh.mpp_paths_generation++;
488485
return ret;
@@ -503,20 +500,9 @@ void mesh_plink_broken(struct sta_info *sta)
503500
struct mesh_table *tbl = sdata->u.mesh.mesh_paths;
504501
static const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
505502
struct mesh_path *mpath;
506-
struct rhashtable_iter iter;
507-
int ret;
508-
509-
ret = rhashtable_walk_init(&tbl->rhead, &iter, GFP_ATOMIC);
510-
if (ret)
511-
return;
512-
513-
rhashtable_walk_start(&iter);
514503

515-
while ((mpath = rhashtable_walk_next(&iter))) {
516-
if (IS_ERR(mpath) && PTR_ERR(mpath) == -EAGAIN)
517-
continue;
518-
if (IS_ERR(mpath))
519-
break;
504+
rcu_read_lock();
505+
hlist_for_each_entry_rcu(mpath, &tbl->walk_head, walk_list) {
520506
if (rcu_access_pointer(mpath->next_hop) == sta &&
521507
mpath->flags & MESH_PATH_ACTIVE &&
522508
!(mpath->flags & MESH_PATH_FIXED)) {
@@ -530,8 +516,7 @@ void mesh_plink_broken(struct sta_info *sta)
530516
WLAN_REASON_MESH_PATH_DEST_UNREACHABLE, bcast);
531517
}
532518
}
533-
rhashtable_walk_stop(&iter);
534-
rhashtable_walk_exit(&iter);
519+
rcu_read_unlock();
535520
}
536521

537522
static void mesh_path_free_rcu(struct mesh_table *tbl,
@@ -551,6 +536,7 @@ static void mesh_path_free_rcu(struct mesh_table *tbl,
551536

552537
static void __mesh_path_del(struct mesh_table *tbl, struct mesh_path *mpath)
553538
{
539+
hlist_del_rcu(&mpath->walk_list);
554540
rhashtable_remove_fast(&tbl->rhead, &mpath->rhash, mesh_rht_params);
555541
mesh_path_free_rcu(tbl, mpath);
556542
}
@@ -571,79 +557,41 @@ void mesh_path_flush_by_nexthop(struct sta_info *sta)
571557
struct ieee80211_sub_if_data *sdata = sta->sdata;
572558
struct mesh_table *tbl = sdata->u.mesh.mesh_paths;
573559
struct mesh_path *mpath;
574-
struct rhashtable_iter iter;
575-
int ret;
576-
577-
ret = rhashtable_walk_init(&tbl->rhead, &iter, GFP_ATOMIC);
578-
if (ret)
579-
return;
580-
581-
rhashtable_walk_start(&iter);
582-
583-
while ((mpath = rhashtable_walk_next(&iter))) {
584-
if (IS_ERR(mpath) && PTR_ERR(mpath) == -EAGAIN)
585-
continue;
586-
if (IS_ERR(mpath))
587-
break;
560+
struct hlist_node *n;
588561

562+
spin_lock_bh(&tbl->walk_lock);
563+
hlist_for_each_entry_safe(mpath, n, &tbl->walk_head, walk_list) {
589564
if (rcu_access_pointer(mpath->next_hop) == sta)
590565
__mesh_path_del(tbl, mpath);
591566
}
592-
593-
rhashtable_walk_stop(&iter);
594-
rhashtable_walk_exit(&iter);
567+
spin_unlock_bh(&tbl->walk_lock);
595568
}
596569

597570
static void mpp_flush_by_proxy(struct ieee80211_sub_if_data *sdata,
598571
const u8 *proxy)
599572
{
600573
struct mesh_table *tbl = sdata->u.mesh.mpp_paths;
601574
struct mesh_path *mpath;
602-
struct rhashtable_iter iter;
603-
int ret;
604-
605-
ret = rhashtable_walk_init(&tbl->rhead, &iter, GFP_ATOMIC);
606-
if (ret)
607-
return;
608-
609-
rhashtable_walk_start(&iter);
610-
611-
while ((mpath = rhashtable_walk_next(&iter))) {
612-
if (IS_ERR(mpath) && PTR_ERR(mpath) == -EAGAIN)
613-
continue;
614-
if (IS_ERR(mpath))
615-
break;
575+
struct hlist_node *n;
616576

577+
spin_lock_bh(&tbl->walk_lock);
578+
hlist_for_each_entry_safe(mpath, n, &tbl->walk_head, walk_list) {
617579
if (ether_addr_equal(mpath->mpp, proxy))
618580
__mesh_path_del(tbl, mpath);
619581
}
620-
621-
rhashtable_walk_stop(&iter);
622-
rhashtable_walk_exit(&iter);
582+
spin_unlock_bh(&tbl->walk_lock);
623583
}
624584

625585
static void table_flush_by_iface(struct mesh_table *tbl)
626586
{
627587
struct mesh_path *mpath;
628-
struct rhashtable_iter iter;
629-
int ret;
630-
631-
ret = rhashtable_walk_init(&tbl->rhead, &iter, GFP_ATOMIC);
632-
if (ret)
633-
return;
634-
635-
rhashtable_walk_start(&iter);
588+
struct hlist_node *n;
636589

637-
while ((mpath = rhashtable_walk_next(&iter))) {
638-
if (IS_ERR(mpath) && PTR_ERR(mpath) == -EAGAIN)
639-
continue;
640-
if (IS_ERR(mpath))
641-
break;
590+
spin_lock_bh(&tbl->walk_lock);
591+
hlist_for_each_entry_safe(mpath, n, &tbl->walk_head, walk_list) {
642592
__mesh_path_del(tbl, mpath);
643593
}
644-
645-
rhashtable_walk_stop(&iter);
646-
rhashtable_walk_exit(&iter);
594+
spin_unlock_bh(&tbl->walk_lock);
647595
}
648596

649597
/**
@@ -675,15 +623,15 @@ static int table_path_del(struct mesh_table *tbl,
675623
{
676624
struct mesh_path *mpath;
677625

678-
rcu_read_lock();
626+
spin_lock_bh(&tbl->walk_lock);
679627
mpath = rhashtable_lookup_fast(&tbl->rhead, addr, mesh_rht_params);
680628
if (!mpath) {
681629
rcu_read_unlock();
682630
return -ENXIO;
683631
}
684632

685633
__mesh_path_del(tbl, mpath);
686-
rcu_read_unlock();
634+
spin_unlock_bh(&tbl->walk_lock);
687635
return 0;
688636
}
689637

@@ -854,28 +802,16 @@ void mesh_path_tbl_expire(struct ieee80211_sub_if_data *sdata,
854802
struct mesh_table *tbl)
855803
{
856804
struct mesh_path *mpath;
857-
struct rhashtable_iter iter;
858-
int ret;
805+
struct hlist_node *n;
859806

860-
ret = rhashtable_walk_init(&tbl->rhead, &iter, GFP_KERNEL);
861-
if (ret)
862-
return;
863-
864-
rhashtable_walk_start(&iter);
865-
866-
while ((mpath = rhashtable_walk_next(&iter))) {
867-
if (IS_ERR(mpath) && PTR_ERR(mpath) == -EAGAIN)
868-
continue;
869-
if (IS_ERR(mpath))
870-
break;
807+
spin_lock_bh(&tbl->walk_lock);
808+
hlist_for_each_entry_safe(mpath, n, &tbl->walk_head, walk_list) {
871809
if ((!(mpath->flags & MESH_PATH_RESOLVING)) &&
872810
(!(mpath->flags & MESH_PATH_FIXED)) &&
873811
time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE))
874812
__mesh_path_del(tbl, mpath);
875813
}
876-
877-
rhashtable_walk_stop(&iter);
878-
rhashtable_walk_exit(&iter);
814+
spin_unlock_bh(&tbl->walk_lock);
879815
}
880816

881817
void mesh_path_expire(struct ieee80211_sub_if_data *sdata)

0 commit comments

Comments
 (0)