diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/sta_info.c | 148 |
1 files changed, 95 insertions, 53 deletions
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 5eaa1673a8f5..d469d9d2b499 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -368,93 +368,90 @@ static void sta_info_finish_work(struct work_struct *work) | |||
368 | mutex_unlock(&local->sta_mtx); | 368 | mutex_unlock(&local->sta_mtx); |
369 | } | 369 | } |
370 | 370 | ||
371 | int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU) | 371 | static int sta_info_insert_check(struct sta_info *sta) |
372 | { | 372 | { |
373 | struct ieee80211_local *local = sta->local; | ||
374 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 373 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
375 | unsigned long flags; | ||
376 | int err = 0; | ||
377 | 374 | ||
378 | /* | 375 | /* |
379 | * Can't be a WARN_ON because it can be triggered through a race: | 376 | * Can't be a WARN_ON because it can be triggered through a race: |
380 | * something inserts a STA (on one CPU) without holding the RTNL | 377 | * something inserts a STA (on one CPU) without holding the RTNL |
381 | * and another CPU turns off the net device. | 378 | * and another CPU turns off the net device. |
382 | */ | 379 | */ |
383 | if (unlikely(!ieee80211_sdata_running(sdata))) { | 380 | if (unlikely(!ieee80211_sdata_running(sdata))) |
384 | err = -ENETDOWN; | 381 | return -ENETDOWN; |
385 | rcu_read_lock(); | ||
386 | goto out_free; | ||
387 | } | ||
388 | 382 | ||
389 | if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->vif.addr) == 0 || | 383 | if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->vif.addr) == 0 || |
390 | is_multicast_ether_addr(sta->sta.addr))) { | 384 | is_multicast_ether_addr(sta->sta.addr))) |
391 | err = -EINVAL; | 385 | return -EINVAL; |
386 | |||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static int sta_info_insert_ibss(struct sta_info *sta) __acquires(RCU) | ||
391 | { | ||
392 | struct ieee80211_local *local = sta->local; | ||
393 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
394 | unsigned long flags; | ||
395 | |||
396 | spin_lock_irqsave(&local->sta_lock, flags); | ||
397 | /* check if STA exists already */ | ||
398 | if (sta_info_get_bss(sdata, sta->sta.addr)) { | ||
399 | spin_unlock_irqrestore(&local->sta_lock, flags); | ||
392 | rcu_read_lock(); | 400 | rcu_read_lock(); |
393 | goto out_free; | 401 | return -EEXIST; |
394 | } | 402 | } |
395 | 403 | ||
396 | /* | 404 | local->num_sta++; |
397 | * In ad-hoc mode, we sometimes need to insert stations | 405 | local->sta_generation++; |
398 | * from tasklet context from the RX path. To avoid races, | 406 | smp_mb(); |
399 | * always do so in that case -- see the comment below. | 407 | sta_info_hash_add(local, sta); |
400 | */ | ||
401 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { | ||
402 | spin_lock_irqsave(&local->sta_lock, flags); | ||
403 | /* check if STA exists already */ | ||
404 | if (sta_info_get_bss(sdata, sta->sta.addr)) { | ||
405 | spin_unlock_irqrestore(&local->sta_lock, flags); | ||
406 | rcu_read_lock(); | ||
407 | err = -EEXIST; | ||
408 | goto out_free; | ||
409 | } | ||
410 | |||
411 | local->num_sta++; | ||
412 | local->sta_generation++; | ||
413 | smp_mb(); | ||
414 | sta_info_hash_add(local, sta); | ||
415 | 408 | ||
416 | list_add_tail(&sta->list, &local->sta_pending_list); | 409 | list_add_tail(&sta->list, &local->sta_pending_list); |
417 | 410 | ||
418 | rcu_read_lock(); | 411 | rcu_read_lock(); |
419 | spin_unlock_irqrestore(&local->sta_lock, flags); | 412 | spin_unlock_irqrestore(&local->sta_lock, flags); |
420 | 413 | ||
421 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG | 414 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG |
422 | wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n", | 415 | wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n", |
423 | sta->sta.addr); | 416 | sta->sta.addr); |
424 | #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ | 417 | #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ |
425 | 418 | ||
426 | ieee80211_queue_work(&local->hw, &local->sta_finish_work); | 419 | ieee80211_queue_work(&local->hw, &local->sta_finish_work); |
427 | 420 | ||
428 | return 0; | 421 | return 0; |
429 | } | 422 | } |
423 | |||
424 | /* | ||
425 | * should be called with sta_mtx locked | ||
426 | * this function replaces the mutex lock | ||
427 | * with a RCU lock | ||
428 | */ | ||
429 | static int sta_info_insert_non_ibss(struct sta_info *sta) __acquires(RCU) | ||
430 | { | ||
431 | struct ieee80211_local *local = sta->local; | ||
432 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
433 | unsigned long flags; | ||
434 | int err = 0; | ||
435 | |||
436 | lockdep_assert_held(&local->sta_mtx); | ||
430 | 437 | ||
431 | /* | 438 | /* |
432 | * On first glance, this will look racy, because the code | 439 | * On first glance, this will look racy, because the code |
433 | * below this point, which inserts a station with sleeping, | 440 | * in this function, which inserts a station with sleeping, |
434 | * unlocks the sta_lock between checking existence in the | 441 | * unlocks the sta_lock between checking existence in the |
435 | * hash table and inserting into it. | 442 | * hash table and inserting into it. |
436 | * | 443 | * |
437 | * However, it is not racy against itself because it keeps | 444 | * However, it is not racy against itself because it keeps |
438 | * the mutex locked. It still seems to race against the | 445 | * the mutex locked. |
439 | * above code that atomically inserts the station... That, | ||
440 | * however, is not true because the above code can only | ||
441 | * be invoked for IBSS interfaces, and the below code will | ||
442 | * not be -- and the two do not race against each other as | ||
443 | * the hash table also keys off the interface. | ||
444 | */ | 446 | */ |
445 | 447 | ||
446 | might_sleep(); | ||
447 | |||
448 | mutex_lock(&local->sta_mtx); | ||
449 | |||
450 | spin_lock_irqsave(&local->sta_lock, flags); | 448 | spin_lock_irqsave(&local->sta_lock, flags); |
451 | /* check if STA exists already */ | 449 | /* check if STA exists already */ |
452 | if (sta_info_get_bss(sdata, sta->sta.addr)) { | 450 | if (sta_info_get_bss(sdata, sta->sta.addr)) { |
453 | spin_unlock_irqrestore(&local->sta_lock, flags); | 451 | spin_unlock_irqrestore(&local->sta_lock, flags); |
454 | mutex_unlock(&local->sta_mtx); | 452 | mutex_unlock(&local->sta_mtx); |
455 | rcu_read_lock(); | 453 | rcu_read_lock(); |
456 | err = -EEXIST; | 454 | return -EEXIST; |
457 | goto out_free; | ||
458 | } | 455 | } |
459 | 456 | ||
460 | spin_unlock_irqrestore(&local->sta_lock, flags); | 457 | spin_unlock_irqrestore(&local->sta_lock, flags); |
@@ -463,7 +460,7 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU) | |||
463 | if (err) { | 460 | if (err) { |
464 | mutex_unlock(&local->sta_mtx); | 461 | mutex_unlock(&local->sta_mtx); |
465 | rcu_read_lock(); | 462 | rcu_read_lock(); |
466 | goto out_free; | 463 | return err; |
467 | } | 464 | } |
468 | 465 | ||
469 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG | 466 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG |
@@ -478,6 +475,51 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU) | |||
478 | mesh_accept_plinks_update(sdata); | 475 | mesh_accept_plinks_update(sdata); |
479 | 476 | ||
480 | return 0; | 477 | return 0; |
478 | } | ||
479 | |||
480 | int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU) | ||
481 | { | ||
482 | struct ieee80211_local *local = sta->local; | ||
483 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
484 | int err = 0; | ||
485 | |||
486 | err = sta_info_insert_check(sta); | ||
487 | if (err) { | ||
488 | rcu_read_lock(); | ||
489 | goto out_free; | ||
490 | } | ||
491 | |||
492 | /* | ||
493 | * In ad-hoc mode, we sometimes need to insert stations | ||
494 | * from tasklet context from the RX path. To avoid races, | ||
495 | * always do so in that case -- see the comment below. | ||
496 | */ | ||
497 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { | ||
498 | err = sta_info_insert_ibss(sta); | ||
499 | if (err) | ||
500 | goto out_free; | ||
501 | |||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | /* | ||
506 | * It might seem that the function called below is in race against | ||
507 | * the function call above that atomically inserts the station... That, | ||
508 | * however, is not true because the above code can only | ||
509 | * be invoked for IBSS interfaces, and the below code will | ||
510 | * not be -- and the two do not race against each other as | ||
511 | * the hash table also keys off the interface. | ||
512 | */ | ||
513 | |||
514 | might_sleep(); | ||
515 | |||
516 | mutex_lock(&local->sta_mtx); | ||
517 | |||
518 | err = sta_info_insert_non_ibss(sta); | ||
519 | if (err) | ||
520 | goto out_free; | ||
521 | |||
522 | return 0; | ||
481 | out_free: | 523 | out_free: |
482 | BUG_ON(!err); | 524 | BUG_ON(!err); |
483 | __sta_info_free(local, sta); | 525 | __sta_info_free(local, sta); |