The patch "cfg80211/mac80211: use cfg80211 wdev mutex in mac80211" introduced several deadlocks by converting the ifmsh->mtx to wdev->mtx. Solve these by: 1. drop the cancel_work_sync() in ieee80211_stop_mesh(). Instead make the mesh work conditional on whether the mesh is running or not. 2. lock the mesh work with sdata_lock() to protect beacon updates and prevent races with wdev->mesh_id_len or cfg80211.
Unified · Split
| @@ -579,9 +579,7 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata) · 578 unmodified lines | ||
| 579 | 579 | mesh_path_expire(sdata); |
| 580 | 580 | |
| 581 | 581 | changed = mesh_accept_plinks_update(sdata); |
| 582 | − sdata_lock(sdata); | |
| 583 | 582 | ieee80211_mbss_info_change_notify(sdata, changed); |
| 584 | − sdata_unlock(sdata); | |
| 585 | 583 | |
| 586 | 584 | mod_timer(&ifmsh->housekeeping_timer, |
| 587 | 585 | round_jiffies(jiffies + |
| @@ -788,12 +786,10 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) · 200 unmodified lines | ||
| 788 | 786 | sdata->vif.bss_conf.enable_beacon = false; |
| 789 | 787 | clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); |
| 790 | 788 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); |
| 791 | − sdata_lock(sdata); | |
| 792 | 789 | bcn = rcu_dereference_protected(ifmsh->beacon, |
| 793 | 790 | lockdep_is_held(&sdata->wdev.mtx)); |
| 794 | 791 | rcu_assign_pointer(ifmsh->beacon, NULL); |
| 795 | 792 | kfree_rcu(bcn, rcu_head); |
| 796 | − sdata_unlock(sdata); | |
| 797 | 793 | |
| 798 | 794 | /* flush STAs and mpaths on this iface */ |
| 799 | 795 | sta_info_flush(sdata); |
| @@ -806,14 +802,6 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) · 6 unmodified lines | ||
| 806 | 802 | del_timer_sync(&sdata->u.mesh.housekeeping_timer); |
| 807 | 803 | del_timer_sync(&sdata->u.mesh.mesh_path_root_timer); |
| 808 | 804 | del_timer_sync(&sdata->u.mesh.mesh_path_timer); |
| 809 | − /* | |
| 810 | − * If the timer fired while we waited for it, it will have | |
| 811 | − * requeued the work. Now the work will be running again | |
| 812 | − * but will not rearm the timer again because it checks | |
| 813 | − * whether the interface is running, which, at this point, | |
| 814 | − * it no longer is. | |
| 815 | − */ | |
| 816 | − cancel_work_sync(&sdata->work); | |
| 817 | 805 | |
| 818 | 806 | local->fif_other_bss--; |
| 819 | 807 | atomic_dec(&local->iff_allmultis); |
| @@ -954,6 +942,12 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, · 134 unmodified lines | ||
| 954 | 942 | struct ieee80211_mgmt *mgmt; |
| 955 | 943 | u16 stype; |
| 956 | 944 | |
| 945 | + sdata_lock(sdata); | |
| 946 | + | |
| 947 | + /* mesh already went down */ | |
| 948 | + if (!sdata->wdev.mesh_id_len) | |
| 949 | + goto out; | |
| 950 | + | |
| 957 | 951 | rx_status = IEEE80211_SKB_RXCB(skb); |
| 958 | 952 | mgmt = (struct ieee80211_mgmt *) skb->data; |
| 959 | 953 | stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE; |
| @@ -971,12 +965,20 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, · 11 unmodified lines | ||
| 971 | 965 | ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status); |
| 972 | 966 | break; |
| 973 | 967 | } |
| 968 | +out: | |
| 969 | + sdata_unlock(sdata); | |
| 974 | 970 | } |
| 975 | 971 | |
| 976 | 972 | void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) |
| 977 | 973 | { |
| 978 | 974 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; |
| 979 | 975 | |
| 976 | + sdata_lock(sdata); | |
| 977 | + | |
| 978 | + /* mesh already went down */ | |
| 979 | + if (!sdata->wdev.mesh_id_len) | |
| 980 | + goto out; | |
| 981 | + | |
| 980 | 982 | if (ifmsh->preq_queue_len && |
| 981 | 983 | time_after(jiffies, |
| 982 | 984 | ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval))) |
| @@ -996,6 +998,9 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) · 13 unmodified lines | ||
| 996 | 998 | |
| 997 | 999 | if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags)) |
| 998 | 1000 | mesh_sync_adjust_tbtt(sdata); |
| 1001 | + | |
| 1002 | +out: | |
| 1003 | + sdata_unlock(sdata); | |
| 999 | 1004 | } |
| 1000 | 1005 | |
| 1001 | 1006 | void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) |
| @@ -517,9 +517,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, · 516 unmodified lines | ||
| 517 | 517 | ieee80211_mps_frame_release(sta, elems); |
| 518 | 518 | out: |
| 519 | 519 | rcu_read_unlock(); |
| 520 | − sdata_lock(sdata); | |
| 521 | 520 | ieee80211_mbss_info_change_notify(sdata, changed); |
| 522 | − sdata_unlock(sdata); | |
| 523 | 521 | } |
| 524 | 522 | |
| 525 | 523 | static void mesh_plink_timer(unsigned long data) |
| @@ -1070,9 +1068,6 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, · 544 unmodified lines | ||
| 1070 | 1068 | |
| 1071 | 1069 | rcu_read_unlock(); |
| 1072 | 1070 | |
| 1073 | − if (changed) { | |
| 1074 | − sdata_lock(sdata); | |
| 1071 | + if (changed) | |
| 1075 | 1072 | ieee80211_mbss_info_change_notify(sdata, changed); |
| 1076 | − sdata_unlock(sdata); | |
| 1077 | − } | |
| 1078 | 1073 | } |