Skip to content

Commit 349eb8c

Browse files
jmberg-intellinvjw
authored andcommitted
mac80211: annotate and fix RCU in mesh code
This adds proper RCU annotations to the mesh path table code, and fixes a number of bugs in the code that I found while checking the sparse warnings I got as a result of the annotations. Some things like the changes in mesh_path_add() or mesh_pathtbl_init() only serve to shut up sparse, but other changes like the changes surrounding the for_each_mesh_entry() macro fix real RCU bugs in the code. Signed-off-by: Johannes Berg <[email protected]> Signed-off-by: John W. Linville <[email protected]>
1 parent 1928eca commit 349eb8c

File tree

2 files changed

+102
-56
lines changed

2 files changed

+102
-56
lines changed

net/mac80211/mesh.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,6 @@ static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata)
289289
return sdata->u.mesh.mesh_pp_id == IEEE80211_PATH_PROTOCOL_HWMP;
290290
}
291291

292-
#define for_each_mesh_entry(x, p, node, i) \
293-
for (i = 0; i <= x->hash_mask; i++) \
294-
hlist_for_each_entry_rcu(node, p, &x->hash_buckets[i], list)
295-
296292
void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local);
297293

298294
void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata);

net/mac80211/mesh_pathtbl.c

Lines changed: 102 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ struct mpath_node {
3636
struct mesh_path *mpath;
3737
};
3838

39-
static struct mesh_table *mesh_paths;
40-
static struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */
39+
static struct mesh_table __rcu *mesh_paths;
40+
static struct mesh_table __rcu *mpp_paths; /* Store paths for MPP&MAP */
4141

4242
int mesh_paths_generation;
4343

@@ -48,6 +48,29 @@ int mesh_paths_generation;
4848
static DEFINE_RWLOCK(pathtbl_resize_lock);
4949

5050

51+
static inline struct mesh_table *resize_dereference_mesh_paths(void)
52+
{
53+
return rcu_dereference_protected(mesh_paths,
54+
lockdep_is_held(&pathtbl_resize_lock));
55+
}
56+
57+
static inline struct mesh_table *resize_dereference_mpp_paths(void)
58+
{
59+
return rcu_dereference_protected(mpp_paths,
60+
lockdep_is_held(&pathtbl_resize_lock));
61+
}
62+
63+
/*
64+
* CAREFUL -- "tbl" must not be an expression,
65+
* in particular not an rcu_dereference(), since
66+
* it's used twice. So it is illegal to do
67+
* for_each_mesh_entry(rcu_dereference(...), ...)
68+
*/
69+
#define for_each_mesh_entry(tbl, p, node, i) \
70+
for (i = 0; i <= tbl->hash_mask; i++) \
71+
hlist_for_each_entry_rcu(node, p, &tbl->hash_buckets[i], list)
72+
73+
5174
static struct mesh_table *mesh_table_alloc(int size_order)
5275
{
5376
int i;
@@ -258,12 +281,13 @@ struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata)
258281
*/
259282
struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data *sdata)
260283
{
284+
struct mesh_table *tbl = rcu_dereference(mesh_paths);
261285
struct mpath_node *node;
262286
struct hlist_node *p;
263287
int i;
264288
int j = 0;
265289

266-
for_each_mesh_entry(mesh_paths, p, node, i) {
290+
for_each_mesh_entry(tbl, p, node, i) {
267291
if (sdata && node->mpath->sdata != sdata)
268292
continue;
269293
if (j++ == idx) {
@@ -293,6 +317,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
293317
{
294318
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
295319
struct ieee80211_local *local = sdata->local;
320+
struct mesh_table *tbl;
296321
struct mesh_path *mpath, *new_mpath;
297322
struct mpath_node *node, *new_node;
298323
struct hlist_head *bucket;
@@ -332,10 +357,12 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
332357
spin_lock_init(&new_mpath->state_lock);
333358
init_timer(&new_mpath->timer);
334359

335-
hash_idx = mesh_table_hash(dst, sdata, mesh_paths);
336-
bucket = &mesh_paths->hash_buckets[hash_idx];
360+
tbl = resize_dereference_mesh_paths();
337361

338-
spin_lock_bh(&mesh_paths->hashwlock[hash_idx]);
362+
hash_idx = mesh_table_hash(dst, sdata, tbl);
363+
bucket = &tbl->hash_buckets[hash_idx];
364+
365+
spin_lock_bh(&tbl->hashwlock[hash_idx]);
339366

340367
err = -EEXIST;
341368
hlist_for_each_entry(node, n, bucket, list) {
@@ -345,13 +372,13 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
345372
}
346373

347374
hlist_add_head_rcu(&new_node->list, bucket);
348-
if (atomic_inc_return(&mesh_paths->entries) >=
349-
mesh_paths->mean_chain_len * (mesh_paths->hash_mask + 1))
375+
if (atomic_inc_return(&tbl->entries) >=
376+
tbl->mean_chain_len * (tbl->hash_mask + 1))
350377
grow = 1;
351378

352379
mesh_paths_generation++;
353380

354-
spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]);
381+
spin_unlock_bh(&tbl->hashwlock[hash_idx]);
355382
read_unlock_bh(&pathtbl_resize_lock);
356383
if (grow) {
357384
set_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags);
@@ -360,7 +387,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
360387
return 0;
361388

362389
err_exists:
363-
spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]);
390+
spin_unlock_bh(&tbl->hashwlock[hash_idx]);
364391
read_unlock_bh(&pathtbl_resize_lock);
365392
kfree(new_node);
366393
err_node_alloc:
@@ -382,11 +409,11 @@ void mesh_mpath_table_grow(void)
382409
struct mesh_table *oldtbl, *newtbl;
383410

384411
write_lock_bh(&pathtbl_resize_lock);
385-
newtbl = mesh_table_alloc(mesh_paths->size_order + 1);
412+
oldtbl = resize_dereference_mesh_paths();
413+
newtbl = mesh_table_alloc(oldtbl->size_order + 1);
386414
if (!newtbl)
387415
goto out;
388-
oldtbl = mesh_paths;
389-
if (mesh_table_grow(mesh_paths, newtbl) < 0) {
416+
if (mesh_table_grow(oldtbl, newtbl) < 0) {
390417
__mesh_table_free(newtbl);
391418
goto out;
392419
}
@@ -403,11 +430,11 @@ void mesh_mpp_table_grow(void)
403430
struct mesh_table *oldtbl, *newtbl;
404431

405432
write_lock_bh(&pathtbl_resize_lock);
406-
newtbl = mesh_table_alloc(mpp_paths->size_order + 1);
433+
oldtbl = resize_dereference_mpp_paths();
434+
newtbl = mesh_table_alloc(oldtbl->size_order + 1);
407435
if (!newtbl)
408436
goto out;
409-
oldtbl = mpp_paths;
410-
if (mesh_table_grow(mpp_paths, newtbl) < 0) {
437+
if (mesh_table_grow(oldtbl, newtbl) < 0) {
411438
__mesh_table_free(newtbl);
412439
goto out;
413440
}
@@ -422,6 +449,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
422449
{
423450
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
424451
struct ieee80211_local *local = sdata->local;
452+
struct mesh_table *tbl;
425453
struct mesh_path *mpath, *new_mpath;
426454
struct mpath_node *node, *new_node;
427455
struct hlist_head *bucket;
@@ -456,10 +484,12 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
456484
new_mpath->exp_time = jiffies;
457485
spin_lock_init(&new_mpath->state_lock);
458486

459-
hash_idx = mesh_table_hash(dst, sdata, mpp_paths);
460-
bucket = &mpp_paths->hash_buckets[hash_idx];
487+
tbl = resize_dereference_mpp_paths();
461488

462-
spin_lock_bh(&mpp_paths->hashwlock[hash_idx]);
489+
hash_idx = mesh_table_hash(dst, sdata, tbl);
490+
bucket = &tbl->hash_buckets[hash_idx];
491+
492+
spin_lock_bh(&tbl->hashwlock[hash_idx]);
463493

464494
err = -EEXIST;
465495
hlist_for_each_entry(node, n, bucket, list) {
@@ -469,11 +499,11 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
469499
}
470500

471501
hlist_add_head_rcu(&new_node->list, bucket);
472-
if (atomic_inc_return(&mpp_paths->entries) >=
473-
mpp_paths->mean_chain_len * (mpp_paths->hash_mask + 1))
502+
if (atomic_inc_return(&tbl->entries) >=
503+
tbl->mean_chain_len * (tbl->hash_mask + 1))
474504
grow = 1;
475505

476-
spin_unlock_bh(&mpp_paths->hashwlock[hash_idx]);
506+
spin_unlock_bh(&tbl->hashwlock[hash_idx]);
477507
read_unlock_bh(&pathtbl_resize_lock);
478508
if (grow) {
479509
set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags);
@@ -482,7 +512,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
482512
return 0;
483513

484514
err_exists:
485-
spin_unlock_bh(&mpp_paths->hashwlock[hash_idx]);
515+
spin_unlock_bh(&tbl->hashwlock[hash_idx]);
486516
read_unlock_bh(&pathtbl_resize_lock);
487517
kfree(new_node);
488518
err_node_alloc:
@@ -502,6 +532,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
502532
*/
503533
void mesh_plink_broken(struct sta_info *sta)
504534
{
535+
struct mesh_table *tbl;
505536
static const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
506537
struct mesh_path *mpath;
507538
struct mpath_node *node;
@@ -510,10 +541,11 @@ void mesh_plink_broken(struct sta_info *sta)
510541
int i;
511542

512543
rcu_read_lock();
513-
for_each_mesh_entry(mesh_paths, p, node, i) {
544+
tbl = rcu_dereference(mesh_paths);
545+
for_each_mesh_entry(tbl, p, node, i) {
514546
mpath = node->mpath;
515547
spin_lock_bh(&mpath->state_lock);
516-
if (mpath->next_hop == sta &&
548+
if (rcu_dereference(mpath->next_hop) == sta &&
517549
mpath->flags & MESH_PATH_ACTIVE &&
518550
!(mpath->flags & MESH_PATH_FIXED)) {
519551
mpath->flags &= ~MESH_PATH_ACTIVE;
@@ -542,30 +574,38 @@ void mesh_plink_broken(struct sta_info *sta)
542574
*/
543575
void mesh_path_flush_by_nexthop(struct sta_info *sta)
544576
{
577+
struct mesh_table *tbl;
545578
struct mesh_path *mpath;
546579
struct mpath_node *node;
547580
struct hlist_node *p;
548581
int i;
549582

550-
for_each_mesh_entry(mesh_paths, p, node, i) {
583+
rcu_read_lock();
584+
tbl = rcu_dereference(mesh_paths);
585+
for_each_mesh_entry(tbl, p, node, i) {
551586
mpath = node->mpath;
552-
if (mpath->next_hop == sta)
587+
if (rcu_dereference(mpath->next_hop) == sta)
553588
mesh_path_del(mpath->dst, mpath->sdata);
554589
}
590+
rcu_read_unlock();
555591
}
556592

557593
void mesh_path_flush(struct ieee80211_sub_if_data *sdata)
558594
{
595+
struct mesh_table *tbl;
559596
struct mesh_path *mpath;
560597
struct mpath_node *node;
561598
struct hlist_node *p;
562599
int i;
563600

564-
for_each_mesh_entry(mesh_paths, p, node, i) {
601+
rcu_read_lock();
602+
tbl = rcu_dereference(mesh_paths);
603+
for_each_mesh_entry(tbl, p, node, i) {
565604
mpath = node->mpath;
566605
if (mpath->sdata == sdata)
567606
mesh_path_del(mpath->dst, mpath->sdata);
568607
}
608+
rcu_read_unlock();
569609
}
570610

571611
static void mesh_path_node_reclaim(struct rcu_head *rp)
@@ -589,6 +629,7 @@ static void mesh_path_node_reclaim(struct rcu_head *rp)
589629
*/
590630
int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata)
591631
{
632+
struct mesh_table *tbl;
592633
struct mesh_path *mpath;
593634
struct mpath_node *node;
594635
struct hlist_head *bucket;
@@ -597,19 +638,20 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata)
597638
int err = 0;
598639

599640
read_lock_bh(&pathtbl_resize_lock);
600-
hash_idx = mesh_table_hash(addr, sdata, mesh_paths);
601-
bucket = &mesh_paths->hash_buckets[hash_idx];
641+
tbl = resize_dereference_mesh_paths();
642+
hash_idx = mesh_table_hash(addr, sdata, tbl);
643+
bucket = &tbl->hash_buckets[hash_idx];
602644

603-
spin_lock_bh(&mesh_paths->hashwlock[hash_idx]);
645+
spin_lock_bh(&tbl->hashwlock[hash_idx]);
604646
hlist_for_each_entry(node, n, bucket, list) {
605647
mpath = node->mpath;
606648
if (mpath->sdata == sdata &&
607-
memcmp(addr, mpath->dst, ETH_ALEN) == 0) {
649+
memcmp(addr, mpath->dst, ETH_ALEN) == 0) {
608650
spin_lock_bh(&mpath->state_lock);
609651
mpath->flags |= MESH_PATH_RESOLVING;
610652
hlist_del_rcu(&node->list);
611653
call_rcu(&node->rcu, mesh_path_node_reclaim);
612-
atomic_dec(&mesh_paths->entries);
654+
atomic_dec(&tbl->entries);
613655
spin_unlock_bh(&mpath->state_lock);
614656
goto enddel;
615657
}
@@ -618,7 +660,7 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata)
618660
err = -ENXIO;
619661
enddel:
620662
mesh_paths_generation++;
621-
spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]);
663+
spin_unlock_bh(&tbl->hashwlock[hash_idx]);
622664
read_unlock_bh(&pathtbl_resize_lock);
623665
return err;
624666
}
@@ -747,52 +789,60 @@ static int mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl)
747789

748790
int mesh_pathtbl_init(void)
749791
{
750-
mesh_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER);
751-
if (!mesh_paths)
792+
struct mesh_table *tbl_path, *tbl_mpp;
793+
794+
tbl_path = mesh_table_alloc(INIT_PATHS_SIZE_ORDER);
795+
if (!tbl_path)
752796
return -ENOMEM;
753-
mesh_paths->free_node = &mesh_path_node_free;
754-
mesh_paths->copy_node = &mesh_path_node_copy;
755-
mesh_paths->mean_chain_len = MEAN_CHAIN_LEN;
797+
tbl_path->free_node = &mesh_path_node_free;
798+
tbl_path->copy_node = &mesh_path_node_copy;
799+
tbl_path->mean_chain_len = MEAN_CHAIN_LEN;
756800

757-
mpp_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER);
758-
if (!mpp_paths) {
759-
mesh_table_free(mesh_paths, true);
801+
tbl_mpp = mesh_table_alloc(INIT_PATHS_SIZE_ORDER);
802+
if (!tbl_mpp) {
803+
mesh_table_free(tbl_path, true);
760804
return -ENOMEM;
761805
}
762-
mpp_paths->free_node = &mesh_path_node_free;
763-
mpp_paths->copy_node = &mesh_path_node_copy;
764-
mpp_paths->mean_chain_len = MEAN_CHAIN_LEN;
806+
tbl_mpp->free_node = &mesh_path_node_free;
807+
tbl_mpp->copy_node = &mesh_path_node_copy;
808+
tbl_mpp->mean_chain_len = MEAN_CHAIN_LEN;
809+
810+
/* Need no locking since this is during init */
811+
RCU_INIT_POINTER(mesh_paths, tbl_path);
812+
RCU_INIT_POINTER(mpp_paths, tbl_mpp);
765813

766814
return 0;
767815
}
768816

769817
void mesh_path_expire(struct ieee80211_sub_if_data *sdata)
770818
{
819+
struct mesh_table *tbl;
771820
struct mesh_path *mpath;
772821
struct mpath_node *node;
773822
struct hlist_node *p;
774823
int i;
775824

776-
read_lock_bh(&pathtbl_resize_lock);
777-
for_each_mesh_entry(mesh_paths, p, node, i) {
825+
rcu_read_lock();
826+
tbl = rcu_dereference(mesh_paths);
827+
for_each_mesh_entry(tbl, p, node, i) {
778828
if (node->mpath->sdata != sdata)
779829
continue;
780830
mpath = node->mpath;
781831
spin_lock_bh(&mpath->state_lock);
782832
if ((!(mpath->flags & MESH_PATH_RESOLVING)) &&
783833
(!(mpath->flags & MESH_PATH_FIXED)) &&
784-
time_after(jiffies,
785-
mpath->exp_time + MESH_PATH_EXPIRE)) {
834+
time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE)) {
786835
spin_unlock_bh(&mpath->state_lock);
787836
mesh_path_del(mpath->dst, mpath->sdata);
788837
} else
789838
spin_unlock_bh(&mpath->state_lock);
790839
}
791-
read_unlock_bh(&pathtbl_resize_lock);
840+
rcu_read_unlock();
792841
}
793842

794843
void mesh_pathtbl_unregister(void)
795844
{
796-
mesh_table_free(mesh_paths, true);
797-
mesh_table_free(mpp_paths, true);
845+
/* no need for locking during exit path */
846+
mesh_table_free(rcu_dereference_raw(mesh_paths), true);
847+
mesh_table_free(rcu_dereference_raw(mpp_paths), true);
798848
}

0 commit comments

Comments
 (0)