Commit 1928ecab620907a0953f811316d05f367f3f4dba

Authored by Johannes Berg
Committed by John W. Linville
1 parent d07c7cf49a

mac80211: fix and simplify mesh locking

The locking in mesh_{mpath,mpp}_table_grow not only
has an rcu_read_unlock() missing, it's also racy
(though really only technically since it's invoked
from a single function only) since it obtains the
new size of the table without any locking, so two
invocations of the function could attempt the same
resize.

Additionally, it uses synchronize_rcu() which is
rather expensive and can be avoided trivially here.

Modify the functions to only use the table lock
and use call_rcu() instead of synchronize_rcu().

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>

Showing 2 changed files with 25 additions and 22 deletions Side-by-side Diff

... ... @@ -120,6 +120,7 @@
120 120 * buckets
121 121 * @mean_chain_len: maximum average length for the hash buckets' list, if it is
122 122 * reached, the table will grow
  123 + * rcu_head: RCU head to free the table
123 124 */
124 125 struct mesh_table {
125 126 /* Number of buckets will be 2^N */
... ... @@ -132,6 +133,8 @@
132 133 int (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl);
133 134 int size_order;
134 135 int mean_chain_len;
  136 +
  137 + struct rcu_head rcu_head;
135 138 };
136 139  
137 140 /* Recent multicast cache */
net/mac80211/mesh_pathtbl.c
... ... @@ -370,52 +370,52 @@
370 370 return err;
371 371 }
372 372  
  373 +static void mesh_table_free_rcu(struct rcu_head *rcu)
  374 +{
  375 + struct mesh_table *tbl = container_of(rcu, struct mesh_table, rcu_head);
  376 +
  377 + mesh_table_free(tbl, false);
  378 +}
  379 +
373 380 void mesh_mpath_table_grow(void)
374 381 {
375 382 struct mesh_table *oldtbl, *newtbl;
376 383  
377   - rcu_read_lock();
378   - newtbl = mesh_table_alloc(rcu_dereference(mesh_paths)->size_order + 1);
379   - if (!newtbl)
380   - return;
381 384 write_lock_bh(&pathtbl_resize_lock);
  385 + newtbl = mesh_table_alloc(mesh_paths->size_order + 1);
  386 + if (!newtbl)
  387 + goto out;
382 388 oldtbl = mesh_paths;
383 389 if (mesh_table_grow(mesh_paths, newtbl) < 0) {
384   - rcu_read_unlock();
385 390 __mesh_table_free(newtbl);
386   - write_unlock_bh(&pathtbl_resize_lock);
387   - return;
  391 + goto out;
388 392 }
389   - rcu_read_unlock();
390 393 rcu_assign_pointer(mesh_paths, newtbl);
391   - write_unlock_bh(&pathtbl_resize_lock);
392 394  
393   - synchronize_rcu();
394   - mesh_table_free(oldtbl, false);
  395 + call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu);
  396 +
  397 + out:
  398 + write_unlock_bh(&pathtbl_resize_lock);
395 399 }
396 400  
397 401 void mesh_mpp_table_grow(void)
398 402 {
399 403 struct mesh_table *oldtbl, *newtbl;
400 404  
401   - rcu_read_lock();
402   - newtbl = mesh_table_alloc(rcu_dereference(mpp_paths)->size_order + 1);
403   - if (!newtbl)
404   - return;
405 405 write_lock_bh(&pathtbl_resize_lock);
  406 + newtbl = mesh_table_alloc(mpp_paths->size_order + 1);
  407 + if (!newtbl)
  408 + goto out;
406 409 oldtbl = mpp_paths;
407 410 if (mesh_table_grow(mpp_paths, newtbl) < 0) {
408   - rcu_read_unlock();
409 411 __mesh_table_free(newtbl);
410   - write_unlock_bh(&pathtbl_resize_lock);
411   - return;
  412 + goto out;
412 413 }
413   - rcu_read_unlock();
414 414 rcu_assign_pointer(mpp_paths, newtbl);
415   - write_unlock_bh(&pathtbl_resize_lock);
  415 + call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu);
416 416  
417   - synchronize_rcu();
418   - mesh_table_free(oldtbl, false);
  417 + out:
  418 + write_unlock_bh(&pathtbl_resize_lock);
419 419 }
420 420  
421 421 int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)