Merge remote-tracking branch 'mac80211/master' into HEAD
authorJohannes Berg <johannes.berg@intel.com>
Wed, 19 Jun 2013 16:55:06 +0000 (18:55 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 19 Jun 2013 16:55:12 +0000 (18:55 +0200)
Merge mac80211 to avoid conflicts with the nl80211 attrbuf
changes.

Conflicts:
net/mac80211/iface.c
net/wireless/nl80211.c

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
1  2 
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/mlme.c
net/mac80211/rate.c
net/mac80211/util.c
net/wireless/nl80211.c

diff --combined net/mac80211/cfg.c
@@@ -73,19 -73,16 +73,19 @@@ static int ieee80211_change_iface(struc
                struct ieee80211_local *local = sdata->local;
  
                if (ieee80211_sdata_running(sdata)) {
 +                      u32 mask = MONITOR_FLAG_COOK_FRAMES |
 +                                 MONITOR_FLAG_ACTIVE;
 +
                        /*
 -                       * Prohibit MONITOR_FLAG_COOK_FRAMES to be
 -                       * changed while the interface is up.
 +                       * Prohibit MONITOR_FLAG_COOK_FRAMES and
 +                       * MONITOR_FLAG_ACTIVE to be changed while the
 +                       * interface is up.
                         * Else we would need to add a lot of cruft
                         * to update everything:
                         *      cooked_mntrs, monitor and all fif_* counters
                         *      reconfigure hardware
                         */
 -                      if ((*flags & MONITOR_FLAG_COOK_FRAMES) !=
 -                          (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES))
 +                      if ((*flags & mask) != (sdata->u.mntr_flags & mask))
                                return -EBUSY;
  
                        ieee80211_adjust_monitor_flags(sdata, -1);
@@@ -447,7 -444,7 +447,7 @@@ static void sta_set_sinfo(struct sta_in
        struct ieee80211_local *local = sdata->local;
        struct timespec uptime;
        u64 packets = 0;
 -      int ac;
 +      int i, ac;
  
        sinfo->generation = sdata->local->sta_generation;
  
                        sinfo->signal = (s8)sta->last_signal;
                sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
        }
 +      if (sta->chains) {
 +              sinfo->filled |= STATION_INFO_CHAIN_SIGNAL |
 +                               STATION_INFO_CHAIN_SIGNAL_AVG;
 +
 +              sinfo->chains = sta->chains;
 +              for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
 +                      sinfo->chain_signal[i] = sta->chain_signal_last[i];
 +                      sinfo->chain_signal_avg[i] =
 +                              (s8) -ewma_read(&sta->chain_signal_avg[i]);
 +              }
 +      }
  
        sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
        sta_set_rate_info_rx(sta, &sinfo->rxrate);
@@@ -742,7 -728,7 +742,7 @@@ static void ieee80211_get_et_strings(st
  
        if (sset == ETH_SS_STATS) {
                sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats);
 -              memcpy(data, *ieee80211_gstrings_sta_stats, sz_sta_stats);
 +              memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats);
        }
        drv_get_et_strings(sdata, sset, &(data[sz_sta_stats]));
  }
@@@ -1071,6 -1057,12 +1071,12 @@@ static int ieee80211_stop_ap(struct wip
        clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
  
+       if (sdata->wdev.cac_started) {
+               cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
+               cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_ABORTED,
+                                  GFP_KERNEL);
+       }
        drv_stop_ap(sdata->local, sdata);
  
        /* free all potentially still buffered bcast frames */
@@@ -1749,7 -1741,6 +1755,7 @@@ static int copy_mesh_setup(struct ieee8
        ifmsh->mesh_pp_id = setup->path_sel_proto;
        ifmsh->mesh_pm_id = setup->path_metric;
        ifmsh->user_mpm = setup->user_mpm;
 +      ifmsh->mesh_auth_id = setup->auth_id;
        ifmsh->security = IEEE80211_MESH_SEC_NONE;
        if (setup->is_authenticated)
                ifmsh->security |= IEEE80211_MESH_SEC_AUTHED;
        /* mcast rate setting in Mesh Node */
        memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate,
                                                sizeof(setup->mcast_rate));
 +      sdata->vif.bss_conf.basic_rates = setup->basic_rates;
  
        sdata->vif.bss_conf.beacon_int = setup->beacon_interval;
        sdata->vif.bss_conf.dtim_period = setup->dtim_period;
@@@ -1872,8 -1862,6 +1878,8 @@@ static int ieee80211_update_mesh_config
        if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask))
                conf->dot11MeshAwakeWindowDuration =
                        nconf->dot11MeshAwakeWindowDuration;
 +      if (_chg_mesh_attr(NL80211_MESHCONF_PLINK_TIMEOUT, mask))
 +              conf->plink_timeout = nconf->plink_timeout;
        ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON);
        return 0;
  }
@@@ -2324,7 -2312,7 +2330,7 @@@ int __ieee80211_request_smps(struct iee
        enum ieee80211_smps_mode old_req;
        int err;
  
 -      lockdep_assert_held(&sdata->u.mgd.mtx);
 +      lockdep_assert_held(&sdata->wdev.mtx);
  
        old_req = sdata->u.mgd.req_smps;
        sdata->u.mgd.req_smps = smps_mode;
@@@ -2381,9 -2369,9 +2387,9 @@@ static int ieee80211_set_power_mgmt(str
        local->dynamic_ps_forced_timeout = timeout;
  
        /* no change, but if automatic follow powersave */
 -      mutex_lock(&sdata->u.mgd.mtx);
 +      sdata_lock(sdata);
        __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
 -      mutex_unlock(&sdata->u.mgd.mtx);
 +      sdata_unlock(sdata);
  
        if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
@@@ -2841,12 -2829,6 +2847,12 @@@ static int ieee80211_mgmt_tx(struct wip
                return -EOPNOTSUPP;
        }
  
 +      /* configurations requiring offchan cannot work if no channel has been
 +       * specified
 +       */
 +      if (need_offchan && !chan)
 +              return -EINVAL;
 +
        mutex_lock(&local->mtx);
  
        /* Check if the operating channel is the requested channel */
                rcu_read_lock();
                chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
  
 -              if (chanctx_conf)
 -                      need_offchan = chan != chanctx_conf->def.chan;
 -              else
 +              if (chanctx_conf) {
 +                      need_offchan = chan && (chan != chanctx_conf->def.chan);
 +              } else if (!chan) {
 +                      ret = -EINVAL;
 +                      rcu_read_unlock();
 +                      goto out_unlock;
 +              } else {
                        need_offchan = true;
 +              }
                rcu_read_unlock();
        }
  
@@@ -2924,8 -2901,19 +2930,8 @@@ static void ieee80211_mgmt_frame_regist
                                          u16 frame_type, bool reg)
  {
        struct ieee80211_local *local = wiphy_priv(wiphy);
 -      struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
  
        switch (frame_type) {
 -      case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH:
 -              if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
 -                      struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
 -
 -                      if (reg)
 -                              ifibss->auth_frame_registrations++;
 -                      else
 -                              ifibss->auth_frame_registrations--;
 -              }
 -              break;
        case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ:
                if (reg)
                        local->probe_req_reg++;
@@@ -94,7 -94,6 +94,7 @@@ struct ieee80211_bss 
  #define IEEE80211_MAX_SUPP_RATES 32
        u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
        size_t supp_rates_len;
 +      struct ieee80211_rate *beacon_rate;
  
        /*
         * During association, we save an ERP value from a probe response so
@@@ -367,7 -366,7 +367,7 @@@ struct ieee80211_mgd_assoc_data 
        u8 ssid_len;
        u8 supp_rates_len;
        bool wmm, uapsd;
 -      bool have_beacon, need_beacon;
 +      bool need_beacon;
        bool synced;
        bool timeout_started;
  
@@@ -395,6 -394,7 +395,6 @@@ struct ieee80211_if_managed 
        bool nullfunc_failed;
        bool connection_loss;
  
 -      struct mutex mtx;
        struct cfg80211_bss *associated;
        struct ieee80211_mgd_auth_data *auth_data;
        struct ieee80211_mgd_assoc_data *assoc_data;
  
        bool powersave; /* powersave requested for this iface */
        bool broken_ap; /* AP is broken -- turn off powersave */
 +      bool have_beacon;
        u8 dtim_period;
        enum ieee80211_smps_mode req_smps, /* requested smps mode */
                                 driver_smps_mode; /* smps mode request */
  struct ieee80211_if_ibss {
        struct timer_list timer;
  
 -      struct mutex mtx;
 -
        unsigned long last_scan_completed;
  
        u32 basic_rates;
        bool privacy;
  
        bool control_port;
 -      unsigned int auth_frame_registrations;
  
        u8 bssid[ETH_ALEN] __aligned(2);
        u8 ssid[IEEE80211_MAX_SSID_LEN];
        u8 ssid_len, ie_len;
        u8 *ie;
 -      struct ieee80211_channel *channel;
 -      enum nl80211_channel_type channel_type;
 +      struct cfg80211_chan_def chandef;
  
        unsigned long ibss_join_req;
        /* probe response/beacon for IBSS */
@@@ -542,7 -545,6 +542,7 @@@ struct ieee80211_if_mesh 
        struct timer_list mesh_path_root_timer;
  
        unsigned long wrkq_flags;
 +      unsigned long mbss_changed;
  
        u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
        size_t mesh_id_len;
        bool accepting_plinks;
        int num_gates;
        struct beacon_data __rcu *beacon;
 -      /* just protects beacon updates for now */
 -      struct mutex mtx;
        const u8 *ie;
        u8 ie_len;
        enum {
@@@ -774,26 -778,6 +774,26 @@@ struct ieee80211_sub_if_data *vif_to_sd
        return container_of(p, struct ieee80211_sub_if_data, vif);
  }
  
 +static inline void sdata_lock(struct ieee80211_sub_if_data *sdata)
 +      __acquires(&sdata->wdev.mtx)
 +{
 +      mutex_lock(&sdata->wdev.mtx);
 +      __acquire(&sdata->wdev.mtx);
 +}
 +
 +static inline void sdata_unlock(struct ieee80211_sub_if_data *sdata)
 +      __releases(&sdata->wdev.mtx)
 +{
 +      mutex_unlock(&sdata->wdev.mtx);
 +      __release(&sdata->wdev.mtx);
 +}
 +
 +static inline void
 +sdata_assert_lock(struct ieee80211_sub_if_data *sdata)
 +{
 +      lockdep_assert_held(&sdata->wdev.mtx);
 +}
 +
  static inline enum ieee80211_band
  ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata)
  {
@@@ -1513,15 -1497,19 +1513,16 @@@ static inline void ieee80211_tx_skb(str
        ieee80211_tx_skb_tid(sdata, skb, 7);
  }
  
- u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, bool action,
+ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
                               struct ieee802_11_elems *elems,
                               u64 filter, u32 crc);
- static inline void ieee802_11_parse_elems(u8 *start, size_t len, bool action,
+ static inline void ieee802_11_parse_elems(const u8 *start, size_t len,
+                                         bool action,
                                          struct ieee802_11_elems *elems)
  {
        ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0);
  }
  
 -u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
 -                            enum ieee80211_band band);
 -
  void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
  void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
  void ieee80211_dynamic_ps_timer(unsigned long data);
diff --combined net/mac80211/iface.c
@@@ -159,10 -159,10 +159,11 @@@ static int ieee80211_change_mtu(struct 
        return 0;
  }
  
- static int ieee80211_verify_mac(struct ieee80211_local *local, u8 *addr,
 -static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr)
++static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr,
 +                              bool check_dup)
  {
-       struct ieee80211_sub_if_data *sdata;
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_sub_if_data *iter;
        u64 new, mask, tmp;
        u8 *m;
        int ret = 0;
                ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
                ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
  
 +      if (!check_dup)
 +              return ret;
  
        mutex_lock(&local->iflist_mtx);
-       list_for_each_entry(sdata, &local->interfaces, list) {
-               if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
-                   !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
+       list_for_each_entry(iter, &local->interfaces, list) {
+               if (iter == sdata)
                        continue;
  
-               m = sdata->vif.addr;
 -              if (iter->vif.type == NL80211_IFTYPE_MONITOR)
++              if (iter->vif.type == NL80211_IFTYPE_MONITOR &&
++                  !(iter->u.mntr_flags & MONITOR_FLAG_ACTIVE))
+                       continue;
+               m = iter->vif.addr;
                tmp =   ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
                        ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
                        ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
@@@ -208,17 -208,12 +212,17 @@@ static int ieee80211_change_mac(struct 
  {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct sockaddr *sa = addr;
 +      bool check_dup = true;
        int ret;
  
        if (ieee80211_sdata_running(sdata))
                return -EBUSY;
  
 -      ret = ieee80211_verify_mac(sdata, sa->sa_data);
 +      if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
 +          !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
 +              check_dup = false;
 +
-       ret = ieee80211_verify_mac(sdata->local, sa->sa_data, check_dup);
++      ret = ieee80211_verify_mac(sdata, sa->sa_data, check_dup);
        if (ret)
                return ret;
  
@@@ -550,11 -545,7 +554,11 @@@ int ieee80211_do_open(struct wireless_d
                        break;
                }
  
 -              if (local->monitors == 0 && local->open_count == 0) {
 +              if (sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE) {
 +                      res = drv_add_interface(local, sdata);
 +                      if (res)
 +                              goto err_stop;
 +              } else if (local->monitors == 0 && local->open_count == 0) {
                        res = ieee80211_add_virtual_monitor(local);
                        if (res)
                                goto err_stop;
@@@ -932,11 -923,7 +936,11 @@@ static void ieee80211_do_stop(struct ie
                mutex_lock(&local->mtx);
                ieee80211_recalc_idle(local);
                mutex_unlock(&local->mtx);
 -              break;
 +
 +              if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
 +                      break;
 +
 +              /* fall through */
        default:
                if (going_down)
                        drv_remove_interface(local, sdata);
@@@ -1085,7 -1072,7 +1089,7 @@@ static const struct net_device_ops ieee
        .ndo_start_xmit         = ieee80211_monitor_start_xmit,
        .ndo_set_rx_mode        = ieee80211_set_multicast_list,
        .ndo_change_mtu         = ieee80211_change_mtu,
 -      .ndo_set_mac_address    = eth_mac_addr,
 +      .ndo_set_mac_address    = ieee80211_change_mac,
        .ndo_select_queue       = ieee80211_monitor_select_queue,
  };
  
@@@ -1503,7 -1490,17 +1507,17 @@@ static void ieee80211_assign_perm_addr(
                        break;
                }
  
+               /*
+                * Pick address of existing interface in case user changed
+                * MAC address manually, default to perm_addr.
+                */
                m = local->hw.wiphy->perm_addr;
+               list_for_each_entry(sdata, &local->interfaces, list) {
+                       if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
+                               continue;
+                       m = sdata->vif.addr;
+                       break;
+               }
                start = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
                        ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
                        ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
diff --combined net/mac80211/mlme.c
@@@ -91,6 -91,41 +91,6 @@@ MODULE_PARM_DESC(probe_wait_ms
  #define IEEE80211_SIGNAL_AVE_MIN_COUNT        4
  
  /*
 - * All cfg80211 functions have to be called outside a locked
 - * section so that they can acquire a lock themselves... This
 - * is much simpler than queuing up things in cfg80211, but we
 - * do need some indirection for that here.
 - */
 -enum rx_mgmt_action {
 -      /* no action required */
 -      RX_MGMT_NONE,
 -
 -      /* caller must call cfg80211_send_deauth() */
 -      RX_MGMT_CFG80211_DEAUTH,
 -
 -      /* caller must call cfg80211_send_disassoc() */
 -      RX_MGMT_CFG80211_DISASSOC,
 -
 -      /* caller must call cfg80211_send_rx_auth() */
 -      RX_MGMT_CFG80211_RX_AUTH,
 -
 -      /* caller must call cfg80211_send_rx_assoc() */
 -      RX_MGMT_CFG80211_RX_ASSOC,
 -
 -      /* caller must call cfg80211_send_assoc_timeout() */
 -      RX_MGMT_CFG80211_ASSOC_TIMEOUT,
 -
 -      /* used when a processed beacon causes a deauth */
 -      RX_MGMT_CFG80211_TX_DEAUTH,
 -};
 -
 -/* utils */
 -static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd)
 -{
 -      lockdep_assert_held(&ifmgd->mtx);
 -}
 -
 -/*
   * We can have multiple work items (and connection probing)
   * scheduling this timer, but we need to take care to only
   * reschedule it when it should fire _earlier_ than it was
   * has happened -- the work that runs from this timer will
   * do that.
   */
 -static void run_again(struct ieee80211_if_managed *ifmgd, unsigned long timeout)
 +static void run_again(struct ieee80211_sub_if_data *sdata,
 +                    unsigned long timeout)
  {
 -      ASSERT_MGD_MTX(ifmgd);
 +      sdata_assert_lock(sdata);
  
 -      if (!timer_pending(&ifmgd->timer) ||
 -          time_before(timeout, ifmgd->timer.expires))
 -              mod_timer(&ifmgd->timer, timeout);
 +      if (!timer_pending(&sdata->u.mgd.timer) ||
 +          time_before(timeout, sdata->u.mgd.timer.expires))
 +              mod_timer(&sdata->u.mgd.timer, timeout);
  }
  
  void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata)
@@@ -190,12 -224,6 +190,12 @@@ static u32 chandef_downgrade(struct cfg
                c->width = NL80211_CHAN_WIDTH_20_NOHT;
                ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
                break;
 +      case NL80211_CHAN_WIDTH_5:
 +      case NL80211_CHAN_WIDTH_10:
 +              WARN_ON_ONCE(1);
 +              /* keep c->width */
 +              ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
 +              break;
        }
  
        WARN_ON_ONCE(!cfg80211_chandef_valid(c));
@@@ -624,7 -652,7 +624,7 @@@ static void ieee80211_send_assoc(struc
        struct ieee80211_channel *chan;
        u32 rates = 0;
  
 -      lockdep_assert_held(&ifmgd->mtx);
 +      sdata_assert_lock(sdata);
  
        rcu_read_lock();
        chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
@@@ -886,10 -914,6 +886,10 @@@ void ieee80211_send_nullfunc(struct iee
  
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
                                        IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
 +
 +      if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
 +              IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
 +
        if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
                            IEEE80211_STA_CONNECTION_POLL))
                IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
@@@ -938,7 -962,7 +938,7 @@@ static void ieee80211_chswitch_work(str
        if (!ieee80211_sdata_running(sdata))
                return;
  
 -      mutex_lock(&ifmgd->mtx);
 +      sdata_lock(sdata);
        if (!ifmgd->associated)
                goto out;
  
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
   out:
        ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
 -      mutex_unlock(&ifmgd->mtx);
 +      sdata_unlock(sdata);
  }
  
  void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
@@@ -1012,7 -1036,7 +1012,7 @@@ ieee80211_sta_process_chanswitch(struc
        const struct ieee80211_ht_operation *ht_oper;
        int secondary_channel_offset = -1;
  
 -      ASSERT_MGD_MTX(ifmgd);
 +      sdata_assert_lock(sdata);
  
        if (!cbss)
                return;
@@@ -1366,9 -1390,6 +1366,9 @@@ static bool ieee80211_powersave_allowed
                          IEEE80211_STA_CONNECTION_POLL))
                return false;
  
 +      if (!mgd->have_beacon)
 +              return false;
 +
        rcu_read_lock();
        sta = sta_info_get(sdata, mgd->bssid);
        if (sta)
@@@ -1777,7 -1798,7 +1777,7 @@@ static void ieee80211_set_associated(st
  
        ieee80211_led_assoc(local, 1);
  
 -      if (sdata->u.mgd.assoc_data->have_beacon) {
 +      if (sdata->u.mgd.have_beacon) {
                /*
                 * If the AP is buggy we may get here with no DTIM period
                 * known, so assume it's 1 which is the only safe assumption
                 * probably just won't work at all.
                 */
                bss_conf->dtim_period = sdata->u.mgd.dtim_period ?: 1;
 -              bss_info_changed |= BSS_CHANGED_DTIM_PERIOD;
 +              bss_conf->beacon_rate = bss->beacon_rate;
 +              bss_info_changed |= BSS_CHANGED_BEACON_INFO;
        } else {
 +              bss_conf->beacon_rate = NULL;
                bss_conf->dtim_period = 0;
        }
  
@@@ -1823,7 -1842,7 +1823,7 @@@ static void ieee80211_set_disassoc(stru
        struct ieee80211_local *local = sdata->local;
        u32 changed = 0;
  
 -      ASSERT_MGD_MTX(ifmgd);
 +      sdata_assert_lock(sdata);
  
        if (WARN_ON_ONCE(tx && !frame_buf))
                return;
        del_timer_sync(&sdata->u.mgd.chswitch_timer);
  
        sdata->vif.bss_conf.dtim_period = 0;
 +      sdata->vif.bss_conf.beacon_rate = NULL;
 +
 +      ifmgd->have_beacon = false;
  
        ifmgd->flags = 0;
        ieee80211_vif_release_channel(sdata);
@@@ -2035,7 -2051,7 +2035,7 @@@ static void ieee80211_mgd_probe_ap_send
        }
  
        ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
 -      run_again(ifmgd, ifmgd->probe_timeout);
 +      run_again(sdata, ifmgd->probe_timeout);
        if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
                ieee80211_flush_queues(sdata->local, sdata);
  }
@@@ -2049,7 -2065,7 +2049,7 @@@ static void ieee80211_mgd_probe_ap(stru
        if (!ieee80211_sdata_running(sdata))
                return;
  
 -      mutex_lock(&ifmgd->mtx);
 +      sdata_lock(sdata);
  
        if (!ifmgd->associated)
                goto out;
        ifmgd->probe_send_count = 0;
        ieee80211_mgd_probe_ap_send(sdata);
   out:
 -      mutex_unlock(&ifmgd->mtx);
 +      sdata_unlock(sdata);
  }
  
  struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
        if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
                return NULL;
  
 -      ASSERT_MGD_MTX(ifmgd);
 +      sdata_assert_lock(sdata);
  
        if (ifmgd->associated)
                cbss = ifmgd->associated;
@@@ -2152,9 -2168,9 +2152,9 @@@ static void __ieee80211_disconnect(stru
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
  
 -      mutex_lock(&ifmgd->mtx);
 +      sdata_lock(sdata);
        if (!ifmgd->associated) {
 -              mutex_unlock(&ifmgd->mtx);
 +              sdata_unlock(sdata);
                return;
        }
  
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
 -      mutex_unlock(&ifmgd->mtx);
  
 -      /*
 -       * must be outside lock due to cfg80211,
 -       * but that's not a problem.
 -       */
 -      cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
 +      cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
 +                            IEEE80211_DEAUTH_FRAME_LEN);
 +      sdata_unlock(sdata);
  }
  
  static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
@@@ -2235,7 -2254,7 +2235,7 @@@ static void ieee80211_destroy_auth_data
  {
        struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data;
  
 -      lockdep_assert_held(&sdata->u.mgd.mtx);
 +      sdata_assert_lock(sdata);
  
        if (!assoc) {
                sta_info_destroy_addr(sdata, auth_data->bss->bssid);
@@@ -2276,26 -2295,27 +2276,26 @@@ static void ieee80211_auth_challenge(st
                            auth_data->key_idx, tx_flags);
  }
  
 -static enum rx_mgmt_action __must_check
 -ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
 -                     struct ieee80211_mgmt *mgmt, size_t len)
 +static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
 +                                 struct ieee80211_mgmt *mgmt, size_t len)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u8 bssid[ETH_ALEN];
        u16 auth_alg, auth_transaction, status_code;
        struct sta_info *sta;
  
 -      lockdep_assert_held(&ifmgd->mtx);
 +      sdata_assert_lock(sdata);
  
        if (len < 24 + 6)
 -              return RX_MGMT_NONE;
 +              return;
  
        if (!ifmgd->auth_data || ifmgd->auth_data->done)
 -              return RX_MGMT_NONE;
 +              return;
  
        memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN);
  
        if (!ether_addr_equal(bssid, mgmt->bssid))
 -              return RX_MGMT_NONE;
 +              return;
  
        auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
        auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
                           mgmt->sa, auth_alg, ifmgd->auth_data->algorithm,
                           auth_transaction,
                           ifmgd->auth_data->expected_transaction);
 -              return RX_MGMT_NONE;
 +              return;
        }
  
        if (status_code != WLAN_STATUS_SUCCESS) {
                sdata_info(sdata, "%pM denied authentication (status %d)\n",
                           mgmt->sa, status_code);
                ieee80211_destroy_auth_data(sdata, false);
 -              return RX_MGMT_CFG80211_RX_AUTH;
 +              cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
 +              return;
        }
  
        switch (ifmgd->auth_data->algorithm) {
                if (ifmgd->auth_data->expected_transaction != 4) {
                        ieee80211_auth_challenge(sdata, mgmt, len);
                        /* need another frame */
 -                      return RX_MGMT_NONE;
 +                      return;
                }
                break;
        default:
                WARN_ONCE(1, "invalid auth alg %d",
                          ifmgd->auth_data->algorithm);
 -              return RX_MGMT_NONE;
 +              return;
        }
  
        sdata_info(sdata, "authenticated\n");
        ifmgd->auth_data->done = true;
        ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC;
        ifmgd->auth_data->timeout_started = true;
 -      run_again(ifmgd, ifmgd->auth_data->timeout);
 +      run_again(sdata, ifmgd->auth_data->timeout);
  
        if (ifmgd->auth_data->algorithm == WLAN_AUTH_SAE &&
            ifmgd->auth_data->expected_transaction != 2) {
                 * Report auth frame to user space for processing since another
                 * round of Authentication frames is still needed.
                 */
 -              return RX_MGMT_CFG80211_RX_AUTH;
 +              cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
 +              return;
        }
  
        /* move station state to auth */
        }
        mutex_unlock(&sdata->local->sta_mtx);
  
 -      return RX_MGMT_CFG80211_RX_AUTH;
 +      cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
 +      return;
   out_err:
        mutex_unlock(&sdata->local->sta_mtx);
        /* ignore frame -- wait for timeout */
 -      return RX_MGMT_NONE;
  }
  
  
 -static enum rx_mgmt_action __must_check
 -ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
 -                       struct ieee80211_mgmt *mgmt, size_t len)
 +static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
 +                                   struct ieee80211_mgmt *mgmt, size_t len)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        const u8 *bssid = NULL;
        u16 reason_code;
  
 -      lockdep_assert_held(&ifmgd->mtx);
 +      sdata_assert_lock(sdata);
  
        if (len < 24 + 2)
 -              return RX_MGMT_NONE;
 +              return;
  
        if (!ifmgd->associated ||
            !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
 -              return RX_MGMT_NONE;
 +              return;
  
        bssid = ifmgd->associated->bssid;
  
  
        ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
  
 -      return RX_MGMT_CFG80211_DEAUTH;
 +      cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
  }
  
  
 -static enum rx_mgmt_action __must_check
 -ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
 -                         struct ieee80211_mgmt *mgmt, size_t len)
 +static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
 +                                     struct ieee80211_mgmt *mgmt, size_t len)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u16 reason_code;
  
 -      lockdep_assert_held(&ifmgd->mtx);
 +      sdata_assert_lock(sdata);
  
        if (len < 24 + 2)
 -              return RX_MGMT_NONE;
 +              return;
  
        if (!ifmgd->associated ||
            !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
 -              return RX_MGMT_NONE;
 +              return;
  
        reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
  
  
        ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
  
 -      return RX_MGMT_CFG80211_DISASSOC;
 +      cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
  }
  
  static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
@@@ -2475,7 -2495,7 +2475,7 @@@ static void ieee80211_destroy_assoc_dat
  {
        struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
  
 -      lockdep_assert_held(&sdata->u.mgd.mtx);
 +      sdata_assert_lock(sdata);
  
        if (!assoc) {
                sta_info_destroy_addr(sdata, assoc_data->bss->bssid);
@@@ -2502,8 -2522,11 +2502,11 @@@ static bool ieee80211_assoc_success(str
        u16 capab_info, aid;
        struct ieee802_11_elems elems;
        struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
+       const struct cfg80211_bss_ies *bss_ies = NULL;
+       struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
        u32 changed = 0;
        int err;
+       bool ret;
  
        /* AssocResp and ReassocResp have identical structure */
  
        ifmgd->aid = aid;
  
        /*
+        * Some APs are erroneously not including some information in their
+        * (re)association response frames. Try to recover by using the data
+        * from the beacon or probe response. This seems to afflict mobile
+        * 2G/3G/4G wifi routers, reported models include the "Onda PN51T",
+        * "Vodafone PocketWiFi 2", "ZTE MF60" and a similar T-Mobile device.
+        */
+       if ((assoc_data->wmm && !elems.wmm_param) ||
+           (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
+            (!elems.ht_cap_elem || !elems.ht_operation)) ||
+           (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
+            (!elems.vht_cap_elem || !elems.vht_operation))) {
+               const struct cfg80211_bss_ies *ies;
+               struct ieee802_11_elems bss_elems;
+               rcu_read_lock();
+               ies = rcu_dereference(cbss->ies);
+               if (ies)
+                       bss_ies = kmemdup(ies, sizeof(*ies) + ies->len,
+                                         GFP_ATOMIC);
+               rcu_read_unlock();
+               if (!bss_ies)
+                       return false;
+               ieee802_11_parse_elems(bss_ies->data, bss_ies->len,
+                                      false, &bss_elems);
+               if (assoc_data->wmm &&
+                   !elems.wmm_param && bss_elems.wmm_param) {
+                       elems.wmm_param = bss_elems.wmm_param;
+                       sdata_info(sdata,
+                                  "AP bug: WMM param missing from AssocResp\n");
+               }
+               /*
+                * Also check if we requested HT/VHT, otherwise the AP doesn't
+                * have to include the IEs in the (re)association response.
+                */
+               if (!elems.ht_cap_elem && bss_elems.ht_cap_elem &&
+                   !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
+                       elems.ht_cap_elem = bss_elems.ht_cap_elem;
+                       sdata_info(sdata,
+                                  "AP bug: HT capability missing from AssocResp\n");
+               }
+               if (!elems.ht_operation && bss_elems.ht_operation &&
+                   !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
+                       elems.ht_operation = bss_elems.ht_operation;
+                       sdata_info(sdata,
+                                  "AP bug: HT operation missing from AssocResp\n");
+               }
+               if (!elems.vht_cap_elem && bss_elems.vht_cap_elem &&
+                   !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) {
+                       elems.vht_cap_elem = bss_elems.vht_cap_elem;
+                       sdata_info(sdata,
+                                  "AP bug: VHT capa missing from AssocResp\n");
+               }
+               if (!elems.vht_operation && bss_elems.vht_operation &&
+                   !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) {
+                       elems.vht_operation = bss_elems.vht_operation;
+                       sdata_info(sdata,
+                                  "AP bug: VHT operation missing from AssocResp\n");
+               }
+       }
+       /*
         * We previously checked these in the beacon/probe response, so
         * they should be present here. This is just a safety net.
         */
        if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
            (!elems.wmm_param || !elems.ht_cap_elem || !elems.ht_operation)) {
                sdata_info(sdata,
-                          "HT AP is missing WMM params or HT capability/operation in AssocResp\n");
-               return false;
+                          "HT AP is missing WMM params or HT capability/operation\n");
+               ret = false;
+               goto out;
        }
  
        if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
            (!elems.vht_cap_elem || !elems.vht_operation)) {
                sdata_info(sdata,
-                          "VHT AP is missing VHT capability/operation in AssocResp\n");
-               return false;
+                          "VHT AP is missing VHT capability/operation\n");
+               ret = false;
+               goto out;
        }
  
        mutex_lock(&sdata->local->sta_mtx);
        sta = sta_info_get(sdata, cbss->bssid);
        if (WARN_ON(!sta)) {
                mutex_unlock(&sdata->local->sta_mtx);
-               return false;
+               ret = false;
+               goto out;
        }
  
        sband = local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
                           sta->sta.addr);
                WARN_ON(__sta_info_destroy(sta));
                mutex_unlock(&sdata->local->sta_mtx);
-               return false;
+               ret = false;
+               goto out;
        }
  
        mutex_unlock(&sdata->local->sta_mtx);
        ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt);
        ieee80211_sta_reset_beacon_monitor(sdata);
  
-       return true;
+       ret = true;
+  out:
+       kfree(bss_ies);
+       return ret;
  }
  
 -static enum rx_mgmt_action __must_check
 -ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
 -                           struct ieee80211_mgmt *mgmt, size_t len,
 -                           struct cfg80211_bss **bss)
 +static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
 +                                       struct ieee80211_mgmt *mgmt,
 +                                       size_t len)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
        struct ieee802_11_elems elems;
        u8 *pos;
        bool reassoc;
 +      struct cfg80211_bss *bss;
  
 -      lockdep_assert_held(&ifmgd->mtx);
 +      sdata_assert_lock(sdata);
  
        if (!assoc_data)
 -              return RX_MGMT_NONE;
 +              return;
        if (!ether_addr_equal(assoc_data->bss->bssid, mgmt->bssid))
 -              return RX_MGMT_NONE;
 +              return;
  
        /*
         * AssocResp and ReassocResp have identical structure, so process both
         */
  
        if (len < 24 + 6)
 -              return RX_MGMT_NONE;
 +              return;
  
        reassoc = ieee80211_is_reassoc_req(mgmt->frame_control);
        capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
                assoc_data->timeout = jiffies + msecs_to_jiffies(ms);
                assoc_data->timeout_started = true;
                if (ms > IEEE80211_ASSOC_TIMEOUT)
 -                      run_again(ifmgd, assoc_data->timeout);
 -              return RX_MGMT_NONE;
 +                      run_again(sdata, assoc_data->timeout);
 +              return;
        }
  
 -      *bss = assoc_data->bss;
 +      bss = assoc_data->bss;
  
        if (status_code != WLAN_STATUS_SUCCESS) {
                sdata_info(sdata, "%pM denied association (code=%d)\n",
                           mgmt->sa, status_code);
                ieee80211_destroy_assoc_data(sdata, false);
        } else {
 -              if (!ieee80211_assoc_success(sdata, *bss, mgmt, len)) {
 +              if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) {
                        /* oops -- internal error -- send timeout for now */
                        ieee80211_destroy_assoc_data(sdata, false);
 -                      cfg80211_put_bss(sdata->local->hw.wiphy, *bss);
 -                      return RX_MGMT_CFG80211_ASSOC_TIMEOUT;
 +                      cfg80211_put_bss(sdata->local->hw.wiphy, bss);
 +                      cfg80211_assoc_timeout(sdata->dev, mgmt->bssid);
 +                      return;
                }
                sdata_info(sdata, "associated\n");
  
                ieee80211_destroy_assoc_data(sdata, true);
        }
  
 -      return RX_MGMT_CFG80211_RX_ASSOC;
 +      cfg80211_rx_assoc_resp(sdata->dev, bss, (u8 *)mgmt, len);
  }
  
  static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        int freq;
        struct ieee80211_bss *bss;
        struct ieee80211_channel *channel;
 -      bool need_ps = false;
 -
 -      lockdep_assert_held(&sdata->u.mgd.mtx);
  
 -      if ((sdata->u.mgd.associated &&
 -           ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) ||
 -          (sdata->u.mgd.assoc_data &&
 -           ether_addr_equal(mgmt->bssid,
 -                            sdata->u.mgd.assoc_data->bss->bssid))) {
 -              /* not previously set so we may need to recalc */
 -              need_ps = sdata->u.mgd.associated && !sdata->u.mgd.dtim_period;
 -
 -              if (elems->tim && !elems->parse_error) {
 -                      const struct ieee80211_tim_ie *tim_ie = elems->tim;
 -                      sdata->u.mgd.dtim_period = tim_ie->dtim_period;
 -              }
 -      }
 +      sdata_assert_lock(sdata);
  
        if (elems->ds_params)
                freq = ieee80211_channel_to_frequency(elems->ds_params[0],
  
        bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
                                        channel);
 -      if (bss)
 +      if (bss) {
                ieee80211_rx_bss_put(local, bss);
 +              sdata->vif.bss_conf.beacon_rate = bss->beacon_rate;
 +      }
  
        if (!sdata->u.mgd.associated ||
            !ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid))
                return;
  
 -      if (need_ps) {
 -              mutex_lock(&local->iflist_mtx);
 -              ieee80211_recalc_ps(local, -1);
 -              mutex_unlock(&local->iflist_mtx);
 -      }
 -
        ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
                                         elems, true);
  
@@@ -2790,7 -2901,7 +2863,7 @@@ static void ieee80211_rx_mgmt_probe_res
  
        ifmgd = &sdata->u.mgd;
  
 -      ASSERT_MGD_MTX(ifmgd);
 +      sdata_assert_lock(sdata);
  
        if (!ether_addr_equal(mgmt->da, sdata->vif.addr))
                return; /* ignore ProbeResp to foreign address */
                ifmgd->auth_data->tries = 0;
                ifmgd->auth_data->timeout = jiffies;
                ifmgd->auth_data->timeout_started = true;
 -              run_again(ifmgd, ifmgd->auth_data->timeout);
 +              run_again(sdata, ifmgd->auth_data->timeout);
        }
  }
  
@@@ -2840,9 -2951,10 +2913,9 @@@ static const u64 care_about_ies 
        (1ULL << WLAN_EID_HT_CAPABILITY) |
        (1ULL << WLAN_EID_HT_OPERATION);
  
 -static enum rx_mgmt_action
 -ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 -                       struct ieee80211_mgmt *mgmt, size_t len,
 -                       u8 *deauth_buf, struct ieee80211_rx_status *rx_status)
 +static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 +                                   struct ieee80211_mgmt *mgmt, size_t len,
 +                                   struct ieee80211_rx_status *rx_status)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
        u8 erp_value = 0;
        u32 ncrc;
        u8 *bssid;
 +      u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];
  
 -      lockdep_assert_held(&ifmgd->mtx);
 +      sdata_assert_lock(sdata);
  
        /* Process beacon from the current BSS */
        baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
        if (baselen > len)
 -              return RX_MGMT_NONE;
 +              return;
  
        rcu_read_lock();
        chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
        if (!chanctx_conf) {
                rcu_read_unlock();
 -              return RX_MGMT_NONE;
 +              return;
        }
  
        if (rx_status->freq != chanctx_conf->def.chan->center_freq) {
                rcu_read_unlock();
 -              return RX_MGMT_NONE;
 +              return;
        }
        chan = chanctx_conf->def.chan;
        rcu_read_unlock();
                                       len - baselen, false, &elems);
  
                ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
 -              ifmgd->assoc_data->have_beacon = true;
 +              if (elems.tim && !elems.parse_error) {
 +                      const struct ieee80211_tim_ie *tim_ie = elems.tim;
 +                      ifmgd->dtim_period = tim_ie->dtim_period;
 +              }
 +              ifmgd->have_beacon = true;
                ifmgd->assoc_data->need_beacon = false;
                if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
                        sdata->vif.bss_conf.sync_tsf =
                /* continue assoc process */
                ifmgd->assoc_data->timeout = jiffies;
                ifmgd->assoc_data->timeout_started = true;
 -              run_again(ifmgd, ifmgd->assoc_data->timeout);
 -              return RX_MGMT_NONE;
 +              run_again(sdata, ifmgd->assoc_data->timeout);
 +              return;
        }
  
        if (!ifmgd->associated ||
            !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
 -              return RX_MGMT_NONE;
 +              return;
        bssid = ifmgd->associated->bssid;
  
        /* Track average RSSI from the Beacon frames of the current AP */
        }
  
        if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
 -              return RX_MGMT_NONE;
 +              return;
        ifmgd->beacon_crc = ncrc;
        ifmgd->beacon_crc_valid = true;
  
         * If we haven't had a beacon before, tell the driver about the
         * DTIM period (and beacon timing if desired) now.
         */
 -      if (!bss_conf->dtim_period) {
 +      if (!ifmgd->have_beacon) {
                /* a few bogus AP send dtim_period = 0 or no TIM IE */
                if (elems.tim)
                        bss_conf->dtim_period = elems.tim->dtim_period ?: 1;
                                sdata->vif.bss_conf.sync_dtim_count = 0;
                }
  
 -              changed |= BSS_CHANGED_DTIM_PERIOD;
 +              changed |= BSS_CHANGED_BEACON_INFO;
 +              ifmgd->have_beacon = true;
 +
 +              mutex_lock(&local->iflist_mtx);
 +              ieee80211_recalc_ps(local, -1);
 +              mutex_unlock(&local->iflist_mtx);
 +
 +              ieee80211_recalc_ps_vif(sdata);
        }
  
        if (elems.erp_info) {
                ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                                       WLAN_REASON_DEAUTH_LEAVING,
                                       true, deauth_buf);
 -              return RX_MGMT_CFG80211_TX_DEAUTH;
 +              cfg80211_tx_mlme_mgmt(sdata->dev, deauth_buf,
 +                                    sizeof(deauth_buf));
 +              return;
        }
  
        if (sta && elems.opmode_notif)
                                                       elems.pwr_constr_elem);
  
        ieee80211_bss_info_change_notify(sdata, changed);
 -
 -      return RX_MGMT_NONE;
  }
  
  void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                  struct sk_buff *skb)
  {
 -      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_rx_status *rx_status;
        struct ieee80211_mgmt *mgmt;
 -      struct cfg80211_bss *bss = NULL;
 -      enum rx_mgmt_action rma = RX_MGMT_NONE;
 -      u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];
        u16 fc;
        struct ieee802_11_elems elems;
        int ies_len;
        mgmt = (struct ieee80211_mgmt *) skb->data;
        fc = le16_to_cpu(mgmt->frame_control);
  
 -      mutex_lock(&ifmgd->mtx);
 +      sdata_lock(sdata);
  
        switch (fc & IEEE80211_FCTL_STYPE) {
        case IEEE80211_STYPE_BEACON:
 -              rma = ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
 -                                             deauth_buf, rx_status);
 +              ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status);
                break;
        case IEEE80211_STYPE_PROBE_RESP:
                ieee80211_rx_mgmt_probe_resp(sdata, skb);
                break;
        case IEEE80211_STYPE_AUTH:
 -              rma = ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len);
 +              ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len);
                break;
        case IEEE80211_STYPE_DEAUTH:
 -              rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
 +              ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
                break;
        case IEEE80211_STYPE_DISASSOC:
 -              rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
 +              ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
                break;
        case IEEE80211_STYPE_ASSOC_RESP:
        case IEEE80211_STYPE_REASSOC_RESP:
 -              rma = ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, &bss);
 +              ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len);
                break;
        case IEEE80211_STYPE_ACTION:
                if (mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) {
                }
                break;
        }
 -      mutex_unlock(&ifmgd->mtx);
 -
 -      switch (rma) {
 -      case RX_MGMT_NONE:
 -              /* no action */
 -              break;
 -      case RX_MGMT_CFG80211_DEAUTH:
 -              cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
 -              break;
 -      case RX_MGMT_CFG80211_DISASSOC:
 -              cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
 -              break;
 -      case RX_MGMT_CFG80211_RX_AUTH:
 -              cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, skb->len);
 -              break;
 -      case RX_MGMT_CFG80211_RX_ASSOC:
 -              cfg80211_send_rx_assoc(sdata->dev, bss, (u8 *)mgmt, skb->len);
 -              break;
 -      case RX_MGMT_CFG80211_ASSOC_TIMEOUT:
 -              cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid);
 -              break;
 -      case RX_MGMT_CFG80211_TX_DEAUTH:
 -              cfg80211_send_deauth(sdata->dev, deauth_buf,
 -                                   sizeof(deauth_buf));
 -              break;
 -      default:
 -              WARN(1, "unexpected: %d", rma);
 -      }
 +      sdata_unlock(sdata);
  }
  
  static void ieee80211_sta_timer(unsigned long data)
  static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
                                          u8 *bssid, u8 reason, bool tx)
  {
 -      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
  
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
                               tx, frame_buf);
 -      mutex_unlock(&ifmgd->mtx);
 -
 -      /*
 -       * must be outside lock due to cfg80211,
 -       * but that's not a problem.
 -       */
 -      cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
  
 -      mutex_lock(&ifmgd->mtx);
 +      cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
 +                            IEEE80211_DEAUTH_FRAME_LEN);
  }
  
  static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data;
        u32 tx_flags = 0;
  
 -      lockdep_assert_held(&ifmgd->mtx);
 +      sdata_assert_lock(sdata);
  
        if (WARN_ON_ONCE(!auth_data))
                return -EINVAL;
        if (tx_flags == 0) {
                auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
                ifmgd->auth_data->timeout_started = true;
 -              run_again(ifmgd, auth_data->timeout);
 +              run_again(sdata, auth_data->timeout);
        } else {
                auth_data->timeout_started = false;
        }
@@@ -3336,7 -3475,7 +3409,7 @@@ static int ieee80211_do_assoc(struct ie
        struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
        struct ieee80211_local *local = sdata->local;
  
 -      lockdep_assert_held(&sdata->u.mgd.mtx);
 +      sdata_assert_lock(sdata);
  
        assoc_data->tries++;
        if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) {
        if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
                assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
                assoc_data->timeout_started = true;
 -              run_again(&sdata->u.mgd, assoc_data->timeout);
 +              run_again(sdata, assoc_data->timeout);
        } else {
                assoc_data->timeout_started = false;
        }
@@@ -3385,7 -3524,7 +3458,7 @@@ void ieee80211_sta_work(struct ieee8021
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
  
 -      mutex_lock(&ifmgd->mtx);
 +      sdata_lock(sdata);
  
        if (ifmgd->status_received) {
                __le16 fc = ifmgd->status_fc;
                        if (status_acked) {
                                ifmgd->auth_data->timeout =
                                        jiffies + IEEE80211_AUTH_TIMEOUT_SHORT;
 -                              run_again(ifmgd, ifmgd->auth_data->timeout);
 +                              run_again(sdata, ifmgd->auth_data->timeout);
                        } else {
                                ifmgd->auth_data->timeout = jiffies - 1;
                        }
                        if (status_acked) {
                                ifmgd->assoc_data->timeout =
                                        jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT;
 -                              run_again(ifmgd, ifmgd->assoc_data->timeout);
 +                              run_again(sdata, ifmgd->assoc_data->timeout);
                        } else {
                                ifmgd->assoc_data->timeout = jiffies - 1;
                        }
  
                        ieee80211_destroy_auth_data(sdata, false);
  
 -                      mutex_unlock(&ifmgd->mtx);
 -                      cfg80211_send_auth_timeout(sdata->dev, bssid);
 -                      mutex_lock(&ifmgd->mtx);
 +                      cfg80211_auth_timeout(sdata->dev, bssid);
                }
        } else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started)
 -              run_again(ifmgd, ifmgd->auth_data->timeout);
 +              run_again(sdata, ifmgd->auth_data->timeout);
  
        if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started &&
            time_after(jiffies, ifmgd->assoc_data->timeout)) {
 -              if ((ifmgd->assoc_data->need_beacon &&
 -                   !ifmgd->assoc_data->have_beacon) ||
 +              if ((ifmgd->assoc_data->need_beacon && !ifmgd->have_beacon) ||
                    ieee80211_do_assoc(sdata)) {
                        u8 bssid[ETH_ALEN];
  
  
                        ieee80211_destroy_assoc_data(sdata, false);
  
 -                      mutex_unlock(&ifmgd->mtx);
 -                      cfg80211_send_assoc_timeout(sdata->dev, bssid);
 -                      mutex_lock(&ifmgd->mtx);
 +                      cfg80211_assoc_timeout(sdata->dev, bssid);
                }
        } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
 -              run_again(ifmgd, ifmgd->assoc_data->timeout);
 +              run_again(sdata, ifmgd->assoc_data->timeout);
  
        if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
                            IEEE80211_STA_CONNECTION_POLL) &&
                                        false);
                        }
                } else if (time_is_after_jiffies(ifmgd->probe_timeout))
 -                      run_again(ifmgd, ifmgd->probe_timeout);
 +                      run_again(sdata, ifmgd->probe_timeout);
                else if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
                        mlme_dbg(sdata,
                                 "Failed to send nullfunc to AP %pM after %dms, disconnecting\n",
                }
        }
  
 -      mutex_unlock(&ifmgd->mtx);
 +      sdata_unlock(sdata);
  }
  
  static void ieee80211_sta_bcn_mon_timer(unsigned long data)
@@@ -3573,9 -3717,9 +3646,9 @@@ void ieee80211_sta_restart(struct ieee8
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
  
 -      mutex_lock(&ifmgd->mtx);
 +      sdata_lock(sdata);
        if (!ifmgd->associated) {
 -              mutex_unlock(&ifmgd->mtx);
 +              sdata_unlock(sdata);
                return;
        }
  
                                              ifmgd->associated->bssid,
                                              WLAN_REASON_UNSPECIFIED,
                                              true);
 -              mutex_unlock(&ifmgd->mtx);
 +              sdata_unlock(sdata);
                return;
        }
 -      mutex_unlock(&ifmgd->mtx);
 +      sdata_unlock(sdata);
  }
  #endif
  
@@@ -3621,6 -3765,8 +3694,6 @@@ void ieee80211_sta_setup_sdata(struct i
        ifmgd->uapsd_max_sp_len = sdata->local->hw.uapsd_max_sp_len;
        ifmgd->p2p_noa_index = -1;
  
 -      mutex_init(&ifmgd->mtx);
 -
        if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
                ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC;
        else
@@@ -3777,12 -3923,6 +3850,12 @@@ static int ieee80211_prep_channel(struc
         */
        ret = ieee80211_vif_use_channel(sdata, &chandef,
                                        IEEE80211_CHANCTX_SHARED);
 +
 +      /* don't downgrade for 5 and 10 MHz channels, though. */
 +      if (chandef.width == NL80211_CHAN_WIDTH_5 ||
 +          chandef.width == NL80211_CHAN_WIDTH_10)
 +              return ret;
 +
        while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
                ifmgd->flags |= chandef_downgrade(&chandef);
                ret = ieee80211_vif_use_channel(sdata, &chandef,
@@@ -3982,6 -4122,8 +4055,6 @@@ int ieee80211_mgd_auth(struct ieee80211
  
        /* try to authenticate/probe */
  
 -      mutex_lock(&ifmgd->mtx);
 -
        if ((ifmgd->auth_data && !ifmgd->auth_data->done) ||
            ifmgd->assoc_data) {
                err = -EBUSY;
                                       WLAN_REASON_UNSPECIFIED,
                                       false, frame_buf);
  
 -              __cfg80211_send_deauth(sdata->dev, frame_buf,
 -                                     sizeof(frame_buf));
 +              cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
 +                                    sizeof(frame_buf));
        }
  
        sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid);
  
        /* hold our own reference */
        cfg80211_ref_bss(local->hw.wiphy, auth_data->bss);
 -      err = 0;
 -      goto out_unlock;
 +      return 0;
  
   err_clear:
        memset(ifmgd->bssid, 0, ETH_ALEN);
        ifmgd->auth_data = NULL;
   err_free:
        kfree(auth_data);
 - out_unlock:
 -      mutex_unlock(&ifmgd->mtx);
 -
        return err;
  }
  
@@@ -4057,6 -4203,8 +4130,6 @@@ int ieee80211_mgd_assoc(struct ieee8021
        assoc_data->ssid_len = ssidie[1];
        rcu_read_unlock();
  
 -      mutex_lock(&ifmgd->mtx);
 -
        if (ifmgd->associated) {
                u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
  
                                       WLAN_REASON_UNSPECIFIED,
                                       false, frame_buf);
  
 -              __cfg80211_send_deauth(sdata->dev, frame_buf,
 -                                     sizeof(frame_buf));
 +              cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
 +                                    sizeof(frame_buf));
        }
  
        if (ifmgd->auth_data && !ifmgd->auth_data->done) {
  
        ifmgd->assoc_data = assoc_data;
        ifmgd->dtim_period = 0;
 +      ifmgd->have_beacon = false;
  
        err = ieee80211_prep_connection(sdata, req->bss, true);
        if (err)
                        ifmgd->dtim_period = tim->dtim_period;
                        dtim_count = tim->dtim_count;
                }
 -              assoc_data->have_beacon = true;
 +              ifmgd->have_beacon = true;
                assoc_data->timeout = jiffies;
                assoc_data->timeout_started = true;
  
        }
        rcu_read_unlock();
  
 -      run_again(ifmgd, assoc_data->timeout);
 +      run_again(sdata, assoc_data->timeout);
  
        if (bss->corrupt_data) {
                char *corrupt_type = "data";
                           corrupt_type);
        }
  
 -      err = 0;
 -      goto out;
 +      return 0;
   err_clear:
        memset(ifmgd->bssid, 0, ETH_ALEN);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
        ifmgd->assoc_data = NULL;
   err_free:
        kfree(assoc_data);
 - out:
 -      mutex_unlock(&ifmgd->mtx);
 -
        return err;
  }
  
@@@ -4294,6 -4445,8 +4367,6 @@@ int ieee80211_mgd_deauth(struct ieee802
        bool tx = !req->local_state_change;
        bool report_frame = false;
  
 -      mutex_lock(&ifmgd->mtx);
 -
        sdata_info(sdata,
                   "deauthenticating from %pM by local choice (reason=%d)\n",
                   req->bssid, req->reason_code);
                                               req->reason_code, tx,
                                               frame_buf);
                ieee80211_destroy_auth_data(sdata, false);
 -              mutex_unlock(&ifmgd->mtx);
  
                report_frame = true;
                goto out;
                                       req->reason_code, tx, frame_buf);
                report_frame = true;
        }
 -      mutex_unlock(&ifmgd->mtx);
  
   out:
        if (report_frame)
 -              __cfg80211_send_deauth(sdata->dev, frame_buf,
 -                                     IEEE80211_DEAUTH_FRAME_LEN);
 +              cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
 +                                    IEEE80211_DEAUTH_FRAME_LEN);
  
        return 0;
  }
@@@ -4332,14 -4487,18 +4405,14 @@@ int ieee80211_mgd_disassoc(struct ieee8
        u8 bssid[ETH_ALEN];
        u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
  
 -      mutex_lock(&ifmgd->mtx);
 -
        /*
         * cfg80211 should catch this ... but it's racy since
         * we can receive a disassoc frame, process it, hand it
         * to cfg80211 while that's in a locked section already
         * trying to tell us that the user wants to disconnect.
         */
 -      if (ifmgd->associated != req->bss) {
 -              mutex_unlock(&ifmgd->mtx);
 +      if (ifmgd->associated != req->bss)
                return -ENOLINK;
 -      }
  
        sdata_info(sdata,
                   "disassociating from %pM by local choice (reason=%d)\n",
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DISASSOC,
                               req->reason_code, !req->local_state_change,
                               frame_buf);
 -      mutex_unlock(&ifmgd->mtx);
  
 -      __cfg80211_send_disassoc(sdata->dev, frame_buf,
 -                               IEEE80211_DEAUTH_FRAME_LEN);
 +      cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
 +                            IEEE80211_DEAUTH_FRAME_LEN);
  
        return 0;
  }
@@@ -4371,13 -4531,13 +4444,13 @@@ void ieee80211_mgd_stop(struct ieee8021
        cancel_work_sync(&ifmgd->csa_connection_drop_work);
        cancel_work_sync(&ifmgd->chswitch_work);
  
 -      mutex_lock(&ifmgd->mtx);
 +      sdata_lock(sdata);
        if (ifmgd->assoc_data)
                ieee80211_destroy_assoc_data(sdata, false);
        if (ifmgd->auth_data)
                ieee80211_destroy_auth_data(sdata, false);
        del_timer_sync(&ifmgd->timer);
 -      mutex_unlock(&ifmgd->mtx);
 +      sdata_unlock(sdata);
  }
  
  void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
diff --combined net/mac80211/rate.c
@@@ -397,14 -397,8 +397,14 @@@ static void rate_idx_match_mask(struct 
                        return;
  
                /* if HT BSS, and we handle a data frame, also try HT rates */
 -              if (chan_width == NL80211_CHAN_WIDTH_20_NOHT)
 +              switch (chan_width) {
 +              case NL80211_CHAN_WIDTH_20_NOHT:
 +              case NL80211_CHAN_WIDTH_5:
 +              case NL80211_CHAN_WIDTH_10:
                        return;
 +              default:
 +                      break;
 +              }
  
                alt_rate.idx = 0;
                /* keep protection flags */
@@@ -621,7 -615,7 +621,7 @@@ static void rate_control_apply_mask(str
                if (rates[i].idx < 0)
                        break;
  
-               rate_idx_match_mask(&rates[i], sband, mask, chan_width,
+               rate_idx_match_mask(&rates[i], sband, chan_width, mask,
                                    mcs_mask);
        }
  }
diff --combined net/mac80211/util.c
@@@ -560,9 -560,6 +560,9 @@@ void ieee80211_iterate_active_interface
        list_for_each_entry(sdata, &local->interfaces, list) {
                switch (sdata->vif.type) {
                case NL80211_IFTYPE_MONITOR:
 +                      if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
 +                              continue;
 +                      break;
                case NL80211_IFTYPE_AP_VLAN:
                        continue;
                default:
@@@ -601,9 -598,6 +601,9 @@@ void ieee80211_iterate_active_interface
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
                switch (sdata->vif.type) {
                case NL80211_IFTYPE_MONITOR:
 +                      if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
 +                              continue;
 +                      break;
                case NL80211_IFTYPE_AP_VLAN:
                        continue;
                default:
@@@ -667,12 -661,12 +667,12 @@@ void ieee80211_queue_delayed_work(struc
  }
  EXPORT_SYMBOL(ieee80211_queue_delayed_work);
  
- u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, bool action,
+ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
                               struct ieee802_11_elems *elems,
                               u64 filter, u32 crc)
  {
        size_t left = len;
-       u8 *pos = start;
+       const u8 *pos = start;
        bool calc_crc = filter != 0;
        DECLARE_BITMAP(seen_elems, 256);
        const u8 *ie;
@@@ -1078,6 -1072,32 +1078,6 @@@ void ieee80211_sta_def_wmm_params(struc
        ieee80211_set_wmm_default(sdata, true);
  }
  
 -u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
 -                            enum ieee80211_band band)
 -{
 -      struct ieee80211_supported_band *sband;
 -      struct ieee80211_rate *bitrates;
 -      u32 mandatory_rates;
 -      enum ieee80211_rate_flags mandatory_flag;
 -      int i;
 -
 -      sband = local->hw.wiphy->bands[band];
 -      if (WARN_ON(!sband))
 -              return 1;
 -
 -      if (band == IEEE80211_BAND_2GHZ)
 -              mandatory_flag = IEEE80211_RATE_MANDATORY_B;
 -      else
 -              mandatory_flag = IEEE80211_RATE_MANDATORY_A;
 -
 -      bitrates = sband->bitrates;
 -      mandatory_rates = 0;
 -      for (i = 0; i < sband->n_bitrates; i++)
 -              if (bitrates[i].flags & mandatory_flag)
 -                      mandatory_rates |= BIT(i);
 -      return mandatory_rates;
 -}
 -
  void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
                         u16 transaction, u16 auth_alg, u16 status,
                         const u8 *extra, size_t extra_len, const u8 *da,
@@@ -1584,13 -1604,12 +1584,13 @@@ int ieee80211_reconfig(struct ieee80211
                                   BSS_CHANGED_ARP_FILTER |
                                   BSS_CHANGED_PS;
  
 -                      if (sdata->u.mgd.dtim_period)
 -                              changed |= BSS_CHANGED_DTIM_PERIOD;
 +                      /* Re-send beacon info report to the driver */
 +                      if (sdata->u.mgd.have_beacon)
 +                              changed |= BSS_CHANGED_BEACON_INFO;
  
 -                      mutex_lock(&sdata->u.mgd.mtx);
 +                      sdata_lock(sdata);
                        ieee80211_bss_info_change_notify(sdata, changed);
 -                      mutex_unlock(&sdata->u.mgd.mtx);
 +                      sdata_unlock(sdata);
                        break;
                case NL80211_IFTYPE_ADHOC:
                        changed |= BSS_CHANGED_IBSS;
diff --combined net/wireless/nl80211.c
@@@ -37,10 -37,10 +37,10 @@@ static void nl80211_post_doit(struct ge
  
  /* the netlink family */
  static struct genl_family nl80211_fam = {
 -      .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
 -      .name = "nl80211",      /* have users key off the name instead */
 -      .hdrsize = 0,           /* no private header */
 -      .version = 1,           /* no particular meaning now */
 +      .id = GENL_ID_GENERATE,         /* don't bother with a hardcoded ID */
 +      .name = NL80211_GENL_NAME,      /* have users key off the name instead */
 +      .hdrsize = 0,                   /* no private header */
 +      .version = 1,                   /* no particular meaning now */
        .maxattr = NL80211_ATTR_MAX,
        .netnsok = true,
        .pre_doit = nl80211_pre_doit,
@@@ -59,7 -59,7 +59,7 @@@ __cfg80211_wdev_from_attrs(struct net *
        int wiphy_idx = -1;
        int ifidx = -1;
  
 -      assert_cfg80211_lock();
 +      ASSERT_RTNL();
  
        if (!have_ifidx && !have_wdev_id)
                return ERR_PTR(-EINVAL);
@@@ -80,6 -80,7 +80,6 @@@
                if (have_wdev_id && rdev->wiphy_idx != wiphy_idx)
                        continue;
  
 -              mutex_lock(&rdev->devlist_mtx);
                list_for_each_entry(wdev, &rdev->wdev_list, list) {
                        if (have_ifidx && wdev->netdev &&
                            wdev->netdev->ifindex == ifidx) {
@@@ -91,6 -92,7 +91,6 @@@
                                break;
                        }
                }
 -              mutex_unlock(&rdev->devlist_mtx);
  
                if (result)
                        break;
@@@ -107,7 -109,7 +107,7 @@@ __cfg80211_rdev_from_attrs(struct net *
        struct cfg80211_registered_device *rdev = NULL, *tmp;
        struct net_device *netdev;
  
 -      assert_cfg80211_lock();
 +      ASSERT_RTNL();
  
        if (!attrs[NL80211_ATTR_WIPHY] &&
            !attrs[NL80211_ATTR_IFINDEX] &&
                tmp = cfg80211_rdev_by_wiphy_idx(wdev_id >> 32);
                if (tmp) {
                        /* make sure wdev exists */
 -                      mutex_lock(&tmp->devlist_mtx);
                        list_for_each_entry(wdev, &tmp->wdev_list, list) {
                                if (wdev->identifier != (u32)wdev_id)
                                        continue;
                                found = true;
                                break;
                        }
 -                      mutex_unlock(&tmp->devlist_mtx);
  
                        if (!found)
                                tmp = NULL;
  /*
   * This function returns a pointer to the driver
   * that the genl_info item that is passed refers to.
 - * If successful, it returns non-NULL and also locks
 - * the driver's mutex!
 - *
 - * This means that you need to call cfg80211_unlock_rdev()
 - * before being allowed to acquire &cfg80211_mutex!
 - *
 - * This is necessary because we need to lock the global
 - * mutex to get an item off the list safely, and then
 - * we lock the rdev mutex so it doesn't go away under us.
 - *
 - * We don't want to keep cfg80211_mutex locked
 - * for all the time in order to allow requests on
 - * other interfaces to go through at the same time.
   *
   * The result of this can be a PTR_ERR and hence must
   * be checked with IS_ERR() for errors.
  static struct cfg80211_registered_device *
  cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info)
  {
 -      struct cfg80211_registered_device *rdev;
 -
 -      mutex_lock(&cfg80211_mutex);
 -      rdev = __cfg80211_rdev_from_attrs(netns, info->attrs);
 -
 -      /* if it is not an error we grab the lock on
 -       * it to assure it won't be going away while
 -       * we operate on it */
 -      if (!IS_ERR(rdev))
 -              mutex_lock(&rdev->mtx);
 -
 -      mutex_unlock(&cfg80211_mutex);
 -
 -      return rdev;
 +      return __cfg80211_rdev_from_attrs(netns, info->attrs);
  }
  
  /* policy for the attributes */
@@@ -348,7 -378,6 +348,7 @@@ static const struct nla_policy nl80211_
        [NL80211_ATTR_MDID] = { .type = NLA_U16 },
        [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
                                  .len = IEEE80211_MAX_DATA_LEN },
 +      [NL80211_ATTR_PEER_AID] = { .type = NLA_U16 },
  };
  
  /* policy for the key attributes */
@@@ -426,6 -455,7 +426,6 @@@ static int nl80211_prepare_wdev_dump(st
        int err;
  
        rtnl_lock();
 -      mutex_lock(&cfg80211_mutex);
  
        if (!cb->args[0]) {
                err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
                *rdev = wiphy_to_dev(wiphy);
                *wdev = NULL;
  
 -              mutex_lock(&(*rdev)->devlist_mtx);
                list_for_each_entry(tmp, &(*rdev)->wdev_list, list) {
                        if (tmp->identifier == cb->args[1]) {
                                *wdev = tmp;
                                break;
                        }
                }
 -              mutex_unlock(&(*rdev)->devlist_mtx);
  
                if (!*wdev) {
                        err = -ENODEV;
                }
        }
  
 -      cfg80211_lock_rdev(*rdev);
 -
 -      mutex_unlock(&cfg80211_mutex);
        return 0;
   out_unlock:
 -      mutex_unlock(&cfg80211_mutex);
        rtnl_unlock();
        return err;
  }
  
  static void nl80211_finish_wdev_dump(struct cfg80211_registered_device *rdev)
  {
 -      cfg80211_unlock_rdev(rdev);
        rtnl_unlock();
  }
  
@@@ -800,9 -837,12 +800,9 @@@ static int nl80211_key_allowed(struct w
        case NL80211_IFTYPE_MESH_POINT:
                break;
        case NL80211_IFTYPE_ADHOC:
 -              if (!wdev->current_bss)
 -                      return -ENOLINK;
 -              break;
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
 -              if (wdev->sme_state != CFG80211_SME_CONNECTED)
 +              if (!wdev->current_bss)
                        return -ENOLINK;
                break;
        default:
@@@ -905,7 -945,7 +905,7 @@@ nla_put_failure
  static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,
                                        struct sk_buff *msg)
  {
 -      const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan.tcp;
 +      const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan->tcp;
        struct nlattr *nl_tcp;
  
        if (!tcp)
@@@ -948,37 -988,37 +948,37 @@@ static int nl80211_send_wowlan(struct s
  {
        struct nlattr *nl_wowlan;
  
 -      if (!dev->wiphy.wowlan.flags && !dev->wiphy.wowlan.n_patterns)
 +      if (!dev->wiphy.wowlan)
                return 0;
  
        nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
        if (!nl_wowlan)
                return -ENOBUFS;
  
 -      if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) &&
 +      if (((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_ANY) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
 -          ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) &&
 +          ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_DISCONNECT) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
 -          ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) &&
 +          ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
 -          ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
 +          ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
 -          ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
 +          ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
 -          ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
 +          ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
 -          ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
 +          ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
 -          ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
 +          ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
                return -ENOBUFS;
  
 -      if (dev->wiphy.wowlan.n_patterns) {
 +      if (dev->wiphy.wowlan->n_patterns) {
                struct nl80211_wowlan_pattern_support pat = {
 -                      .max_patterns = dev->wiphy.wowlan.n_patterns,
 -                      .min_pattern_len = dev->wiphy.wowlan.pattern_min_len,
 -                      .max_pattern_len = dev->wiphy.wowlan.pattern_max_len,
 -                      .max_pkt_offset = dev->wiphy.wowlan.max_pkt_offset,
 +                      .max_patterns = dev->wiphy.wowlan->n_patterns,
 +                      .min_pattern_len = dev->wiphy.wowlan->pattern_min_len,
 +                      .max_pattern_len = dev->wiphy.wowlan->pattern_max_len,
 +                      .max_pkt_offset = dev->wiphy.wowlan->max_pkt_offset,
                };
  
                if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
@@@ -1188,9 -1228,6 +1188,9 @@@ static int nl80211_send_wiphy(struct cf
                if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
                    nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
                        goto nla_put_failure;
 +              if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
 +                  nla_put_flag(msg, WIPHY_FLAG_SUPPORTS_5_10_MHZ))
 +                      goto nla_put_failure;
  
                (*split_start)++;
                if (split)
@@@ -1527,12 -1564,17 +1527,17 @@@ static int nl80211_dump_wiphy(struct sk
        struct cfg80211_registered_device *dev;
        s64 filter_wiphy = -1;
        bool split = false;
-       struct nlattr **tb = nl80211_fam.attrbuf;
+       struct nlattr **tb;
        int res;
  
 -      mutex_lock(&cfg80211_mutex);
+       /* will be zeroed in nlmsg_parse() */
+       tb = kmalloc(sizeof(*tb) * (NL80211_ATTR_MAX + 1), GFP_KERNEL);
+       if (!tb)
+               return -ENOMEM;
 +      rtnl_lock();
        res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
-                         tb, nl80211_fam.maxattr, nl80211_policy);
+                         tb, NL80211_ATTR_MAX, nl80211_policy);
        if (res == 0) {
                split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP];
                if (tb[NL80211_ATTR_WIPHY])
  
                        netdev = dev_get_by_index(sock_net(skb->sk), ifidx);
                        if (!netdev) {
 -                              mutex_unlock(&cfg80211_mutex);
 +                              rtnl_unlock();
+                               kfree(tb);
                                return -ENODEV;
                        }
                        if (netdev->ieee80211_ptr) {
                        dev_put(netdev);
                }
        }
+       kfree(tb);
  
        list_for_each_entry(dev, &cfg80211_rdev_list, list) {
                if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
                                    !skb->len &&
                                    cb->min_dump_alloc < 4096) {
                                        cb->min_dump_alloc = 4096;
 -                                      mutex_unlock(&cfg80211_mutex);
 +                                      rtnl_unlock();
                                        return 1;
                                }
                                idx--;
                } while (cb->args[1] > 0);
                break;
        }
 -      mutex_unlock(&cfg80211_mutex);
 +      rtnl_unlock();
  
        cb->args[0] = idx;
  
@@@ -1734,11 -1778,6 +1741,11 @@@ static int nl80211_parse_chandef(struc
                                     IEEE80211_CHAN_DISABLED))
                return -EINVAL;
  
 +      if ((chandef->width == NL80211_CHAN_WIDTH_5 ||
 +           chandef->width == NL80211_CHAN_WIDTH_10) &&
 +          !(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ))
 +              return -EINVAL;
 +
        return 0;
  }
  
@@@ -1760,6 -1799,7 +1767,6 @@@ static int __nl80211_set_channel(struc
        if (result)
                return result;
  
 -      mutex_lock(&rdev->devlist_mtx);
        switch (iftype) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_P2P_GO:
        default:
                result = -EINVAL;
        }
 -      mutex_unlock(&rdev->devlist_mtx);
  
        return result;
  }
@@@ -1831,8 -1872,6 +1838,8 @@@ static int nl80211_set_wiphy(struct sk_
        u32 frag_threshold = 0, rts_threshold = 0;
        u8 coverage_class = 0;
  
 +      ASSERT_RTNL();
 +
        /*
         * Try to find the wiphy and netdev. Normally this
         * function shouldn't need the netdev, but this is
         * also passed a netdev to set_wiphy, so that it is
         * possible to let that go to the right netdev!
         */
 -      mutex_lock(&cfg80211_mutex);
  
        if (info->attrs[NL80211_ATTR_IFINDEX]) {
                int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
  
                netdev = dev_get_by_index(genl_info_net(info), ifindex);
 -              if (netdev && netdev->ieee80211_ptr) {
 +              if (netdev && netdev->ieee80211_ptr)
                        rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
 -                      mutex_lock(&rdev->mtx);
 -              } else
 +              else
                        netdev = NULL;
        }
  
        if (!netdev) {
                rdev = __cfg80211_rdev_from_attrs(genl_info_net(info),
                                                  info->attrs);
 -              if (IS_ERR(rdev)) {
 -                      mutex_unlock(&cfg80211_mutex);
 +              if (IS_ERR(rdev))
                        return PTR_ERR(rdev);
 -              }
                wdev = NULL;
                netdev = NULL;
                result = 0;
 -
 -              mutex_lock(&rdev->mtx);
        } else
                wdev = netdev->ieee80211_ptr;
  
                result = cfg80211_dev_rename(
                        rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
  
 -      mutex_unlock(&cfg80211_mutex);
 -
        if (result)
                goto bad_res;
  
        }
  
   bad_res:
 -      mutex_unlock(&rdev->mtx);
        if (netdev)
                dev_put(netdev);
        return result;
@@@ -2176,7 -2224,7 +2183,7 @@@ static int nl80211_dump_interface(struc
        struct cfg80211_registered_device *rdev;
        struct wireless_dev *wdev;
  
 -      mutex_lock(&cfg80211_mutex);
 +      rtnl_lock();
        list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
                if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
                        continue;
                }
                if_idx = 0;
  
 -              mutex_lock(&rdev->devlist_mtx);
                list_for_each_entry(wdev, &rdev->wdev_list, list) {
                        if (if_idx < if_start) {
                                if_idx++;
                        if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid,
                                               cb->nlh->nlmsg_seq, NLM_F_MULTI,
                                               rdev, wdev) < 0) {
 -                              mutex_unlock(&rdev->devlist_mtx);
                                goto out;
                        }
                        if_idx++;
                }
 -              mutex_unlock(&rdev->devlist_mtx);
  
                wp_idx++;
        }
   out:
 -      mutex_unlock(&cfg80211_mutex);
 +      rtnl_unlock();
  
        cb->args[0] = wp_idx;
        cb->args[1] = if_idx;
@@@ -2235,7 -2286,6 +2242,7 @@@ static const struct nla_policy mntr_fla
        [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
        [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
        [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
 +      [NL80211_MNTR_FLAG_ACTIVE] = { .type = NLA_FLAG },
  };
  
  static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
@@@ -2347,10 -2397,6 +2354,10 @@@ static int nl80211_set_interface(struc
                change = true;
        }
  
 +      if (flags && (*flags & NL80211_MNTR_FLAG_ACTIVE) &&
 +          !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
 +              return -EOPNOTSUPP;
 +
        if (change)
                err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
        else
@@@ -2408,11 -2454,6 +2415,11 @@@ static int nl80211_new_interface(struc
        err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
                                  info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
                                  &flags);
 +
 +      if (!err && (flags & NL80211_MNTR_FLAG_ACTIVE) &&
 +          !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
 +              return -EOPNOTSUPP;
 +
        wdev = rdev_add_virtual_intf(rdev,
                                nla_data(info->attrs[NL80211_ATTR_IFNAME]),
                                type, err ? NULL : &flags, &params);
                INIT_LIST_HEAD(&wdev->mgmt_registrations);
                spin_lock_init(&wdev->mgmt_registrations_lock);
  
 -              mutex_lock(&rdev->devlist_mtx);
                wdev->identifier = ++rdev->wdev_id;
                list_add_rcu(&wdev->list, &rdev->wdev_list);
                rdev->devlist_generation++;
 -              mutex_unlock(&rdev->devlist_mtx);
                break;
        default:
                break;
@@@ -2890,58 -2933,61 +2897,58 @@@ static int nl80211_set_mac_acl(struct s
        return err;
  }
  
 -static int nl80211_parse_beacon(struct genl_info *info,
 +static int nl80211_parse_beacon(struct nlattr *attrs[],
                                struct cfg80211_beacon_data *bcn)
  {
        bool haveinfo = false;
  
 -      if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) ||
 -          !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) ||
 -          !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
 -          !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]))
 +      if (!is_valid_ie_attr(attrs[NL80211_ATTR_BEACON_TAIL]) ||
 +          !is_valid_ie_attr(attrs[NL80211_ATTR_IE]) ||
 +          !is_valid_ie_attr(attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
 +          !is_valid_ie_attr(attrs[NL80211_ATTR_IE_ASSOC_RESP]))
                return -EINVAL;
  
        memset(bcn, 0, sizeof(*bcn));
  
 -      if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
 -              bcn->head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
 -              bcn->head_len = nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
 +      if (attrs[NL80211_ATTR_BEACON_HEAD]) {
 +              bcn->head = nla_data(attrs[NL80211_ATTR_BEACON_HEAD]);
 +              bcn->head_len = nla_len(attrs[NL80211_ATTR_BEACON_HEAD]);
                if (!bcn->head_len)
                        return -EINVAL;
                haveinfo = true;
        }
  
 -      if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
 -              bcn->tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
 -              bcn->tail_len =
 -                  nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
 +      if (attrs[NL80211_ATTR_BEACON_TAIL]) {
 +              bcn->tail = nla_data(attrs[NL80211_ATTR_BEACON_TAIL]);
 +              bcn->tail_len = nla_len(attrs[NL80211_ATTR_BEACON_TAIL]);
                haveinfo = true;
        }
  
        if (!haveinfo)
                return -EINVAL;
  
 -      if (info->attrs[NL80211_ATTR_IE]) {
 -              bcn->beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]);
 -              bcn->beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
 +      if (attrs[NL80211_ATTR_IE]) {
 +              bcn->beacon_ies = nla_data(attrs[NL80211_ATTR_IE]);
 +              bcn->beacon_ies_len = nla_len(attrs[NL80211_ATTR_IE]);
        }
  
 -      if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) {
 +      if (attrs[NL80211_ATTR_IE_PROBE_RESP]) {
                bcn->proberesp_ies =
 -                      nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
 +                      nla_data(attrs[NL80211_ATTR_IE_PROBE_RESP]);
                bcn->proberesp_ies_len =
 -                      nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
 +                      nla_len(attrs[NL80211_ATTR_IE_PROBE_RESP]);
        }
  
 -      if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
 +      if (attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
                bcn->assocresp_ies =
 -                      nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
 +                      nla_data(attrs[NL80211_ATTR_IE_ASSOC_RESP]);
                bcn->assocresp_ies_len =
 -                      nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
 +                      nla_len(attrs[NL80211_ATTR_IE_ASSOC_RESP]);
        }
  
 -      if (info->attrs[NL80211_ATTR_PROBE_RESP]) {
 -              bcn->probe_resp =
 -                      nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]);
 -              bcn->probe_resp_len =
 -                      nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]);
 +      if (attrs[NL80211_ATTR_PROBE_RESP]) {
 +              bcn->probe_resp = nla_data(attrs[NL80211_ATTR_PROBE_RESP]);
 +              bcn->probe_resp_len = nla_len(attrs[NL80211_ATTR_PROBE_RESP]);
        }
  
        return 0;
@@@ -2953,6 -2999,8 +2960,6 @@@ static bool nl80211_get_ap_channel(stru
        struct wireless_dev *wdev;
        bool ret = false;
  
 -      mutex_lock(&rdev->devlist_mtx);
 -
        list_for_each_entry(wdev, &rdev->wdev_list, list) {
                if (wdev->iftype != NL80211_IFTYPE_AP &&
                    wdev->iftype != NL80211_IFTYPE_P2P_GO)
                break;
        }
  
 -      mutex_unlock(&rdev->devlist_mtx);
 -
        return ret;
  }
  
@@@ -3020,7 -3070,7 +3027,7 @@@ static int nl80211_start_ap(struct sk_b
            !info->attrs[NL80211_ATTR_BEACON_HEAD])
                return -EINVAL;
  
 -      err = nl80211_parse_beacon(info, &params.beacon);
 +      err = nl80211_parse_beacon(info->attrs, &params.beacon);
        if (err)
                return err;
  
                params.radar_required = true;
        }
  
 -      mutex_lock(&rdev->devlist_mtx);
        err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
                                           params.chandef.chan,
                                           CHAN_MODE_SHARED,
                                           radar_detect_width);
 -      mutex_unlock(&rdev->devlist_mtx);
 -
        if (err)
                return err;
  
@@@ -3172,7 -3225,7 +3179,7 @@@ static int nl80211_set_beacon(struct sk
        if (!wdev->beacon_interval)
                return -EINVAL;
  
 -      err = nl80211_parse_beacon(info, &params);
 +      err = nl80211_parse_beacon(info->attrs, &params);
        if (err)
                return err;
  
@@@ -3330,32 -3383,6 +3337,32 @@@ static bool nl80211_put_sta_rate(struc
        return true;
  }
  
 +static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal,
 +                             int id)
 +{
 +      void *attr;
 +      int i = 0;
 +
 +      if (!mask)
 +              return true;
 +
 +      attr = nla_nest_start(msg, id);
 +      if (!attr)
 +              return false;
 +
 +      for (i = 0; i < IEEE80211_MAX_CHAINS; i++) {
 +              if (!(mask & BIT(i)))
 +                      continue;
 +
 +              if (nla_put_u8(msg, i, signal[i]))
 +                      return false;
 +      }
 +
 +      nla_nest_end(msg, attr);
 +
 +      return true;
 +}
 +
  static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
                                int flags,
                                struct cfg80211_registered_device *rdev,
        default:
                break;
        }
 +      if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL) {
 +              if (!nl80211_put_signal(msg, sinfo->chains,
 +                                      sinfo->chain_signal,
 +                                      NL80211_STA_INFO_CHAIN_SIGNAL))
 +                      goto nla_put_failure;
 +      }
 +      if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL_AVG) {
 +              if (!nl80211_put_signal(msg, sinfo->chains,
 +                                      sinfo->chain_signal_avg,
 +                                      NL80211_STA_INFO_CHAIN_SIGNAL_AVG))
 +                      goto nla_put_failure;
 +      }
        if (sinfo->filled & STATION_INFO_TX_BITRATE) {
                if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
                                          NL80211_STA_INFO_TX_BITRATE))
@@@ -3826,8 -3841,6 +3833,8 @@@ static int nl80211_set_station_tdls(str
                                    struct station_parameters *params)
  {
        /* Dummy STA entry gets updated once the peer capabilities are known */
 +      if (info->attrs[NL80211_ATTR_PEER_AID])
 +              params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
        if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
                params->ht_capa =
                        nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
@@@ -3968,8 -3981,7 +3975,8 @@@ static int nl80211_new_station(struct s
        if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
                return -EINVAL;
  
 -      if (!info->attrs[NL80211_ATTR_STA_AID])
 +      if (!info->attrs[NL80211_ATTR_STA_AID] &&
 +          !info->attrs[NL80211_ATTR_PEER_AID])
                return -EINVAL;
  
        mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
        params.listen_interval =
                nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
  
 -      params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
 +      if (info->attrs[NL80211_ATTR_PEER_AID])
 +              params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
 +      else
 +              params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
        if (!params.aid || params.aid > IEEE80211_MAX_AID)
                return -EINVAL;
  
                        params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
  
                /* TDLS peers cannot be added */
 -              if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
 +              if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
 +                  info->attrs[NL80211_ATTR_PEER_AID])
                        return -EINVAL;
                /* but don't bother the driver with it */
                params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
                if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
                        return -EINVAL;
                /* TDLS peers cannot be added */
 -              if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
 +              if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
 +                  info->attrs[NL80211_ATTR_PEER_AID])
                        return -EINVAL;
                break;
        case NL80211_IFTYPE_STATION:
@@@ -4585,9 -4592,7 +4592,9 @@@ static int nl80211_get_mesh_config(stru
            nla_put_u32(msg, NL80211_MESHCONF_POWER_MODE,
                        cur_params.power_mode) ||
            nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW,
 -                      cur_params.dot11MeshAwakeWindowDuration))
 +                      cur_params.dot11MeshAwakeWindowDuration) ||
 +          nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
 +                      cur_params.plink_timeout))
                goto nla_put_failure;
        nla_nest_end(msg, pinfoattr);
        genlmsg_end(msg, hdr);
@@@ -4628,7 -4633,6 +4635,7 @@@ static const struct nla_policy nl80211_
        [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 },
        [NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U32 },
        [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 },
 +      [NL80211_MESHCONF_PLINK_TIMEOUT] = { .type = NLA_U32 },
  };
  
  static const struct nla_policy
        [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
        [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
        [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
 +      [NL80211_MESH_SETUP_AUTH_PROTOCOL] = { .type = NLA_U8 },
        [NL80211_MESH_SETUP_USERSPACE_MPM] = { .type = NLA_FLAG },
        [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
                                    .len = IEEE80211_MAX_DATA_LEN },
@@@ -4766,9 -4769,6 +4773,9 @@@ do {                                                                        
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration,
                                  0, 65535, mask,
                                  NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16);
 +      FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 1, 0xffffffff,
 +                                mask, NL80211_MESHCONF_PLINK_TIMEOUT,
 +                                nla_get_u32);
        if (mask_out)
                *mask_out = mask;
  
@@@ -4826,13 -4826,6 +4833,13 @@@ static int nl80211_parse_mesh_setup(str
        if (setup->is_secure)
                setup->user_mpm = true;
  
 +      if (tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]) {
 +              if (!setup->user_mpm)
 +                      return -EINVAL;
 +              setup->auth_id =
 +                      nla_get_u8(tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]);
 +      }
 +
        return 0;
  }
  
@@@ -4875,13 -4868,18 +4882,13 @@@ static int nl80211_get_reg(struct sk_bu
        void *hdr = NULL;
        struct nlattr *nl_reg_rules;
        unsigned int i;
 -      int err = -EINVAL;
 -
 -      mutex_lock(&cfg80211_mutex);
  
        if (!cfg80211_regdomain)
 -              goto out;
 +              return -EINVAL;
  
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 -      if (!msg) {
 -              err = -ENOBUFS;
 -              goto out;
 -      }
 +      if (!msg)
 +              return -ENOBUFS;
  
        hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
                             NL80211_CMD_GET_REG);
        nla_nest_end(msg, nl_reg_rules);
  
        genlmsg_end(msg, hdr);
 -      err = genlmsg_reply(msg, info);
 -      goto out;
 +      return genlmsg_reply(msg, info);
  
  nla_put_failure_rcu:
        rcu_read_unlock();
@@@ -4948,7 -4947,10 +4955,7 @@@ nla_put_failure
        genlmsg_cancel(msg, hdr);
  put_failure:
        nlmsg_free(msg);
 -      err = -EMSGSIZE;
 -out:
 -      mutex_unlock(&cfg80211_mutex);
 -      return err;
 +      return -EMSGSIZE;
  }
  
  static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
                }
        }
  
 -      mutex_lock(&cfg80211_mutex);
 -
        r = set_regdom(rd);
        /* set_regdom took ownership */
        rd = NULL;
 -      mutex_unlock(&cfg80211_mutex);
  
   bad_reg:
        kfree(rd);
@@@ -5066,6 -5071,7 +5073,6 @@@ static int nl80211_trigger_scan(struct 
        if (!rdev->ops->scan)
                return -EOPNOTSUPP;
  
 -      mutex_lock(&rdev->sched_scan_mtx);
        if (rdev->scan_req) {
                err = -EBUSY;
                goto unlock;
        }
  
   unlock:
 -      mutex_unlock(&rdev->sched_scan_mtx);
        return err;
  }
  
@@@ -5322,6 -5329,8 +5329,6 @@@ static int nl80211_start_sched_scan(str
        if (ie_len > wiphy->max_sched_scan_ie_len)
                return -EINVAL;
  
 -      mutex_lock(&rdev->sched_scan_mtx);
 -
        if (rdev->sched_scan_req) {
                err = -EINPROGRESS;
                goto out;
  out_free:
        kfree(request);
  out:
 -      mutex_unlock(&rdev->sched_scan_mtx);
        return err;
  }
  
@@@ -5496,12 -5506,17 +5503,12 @@@ static int nl80211_stop_sched_scan(stru
                                   struct genl_info *info)
  {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
 -      int err;
  
        if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
            !rdev->ops->sched_scan_stop)
                return -EOPNOTSUPP;
  
 -      mutex_lock(&rdev->sched_scan_mtx);
 -      err = __cfg80211_stop_sched_scan(rdev, false);
 -      mutex_unlock(&rdev->sched_scan_mtx);
 -
 -      return err;
 +      return __cfg80211_stop_sched_scan(rdev, false);
  }
  
  static int nl80211_start_radar_detection(struct sk_buff *skb,
        if (!rdev->ops->start_radar_detection)
                return -EOPNOTSUPP;
  
 -      mutex_lock(&rdev->devlist_mtx);
        err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
                                           chandef.chan, CHAN_MODE_SHARED,
                                           BIT(chandef.width));
        if (err)
 -              goto err_locked;
 +              return err;
  
        err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef);
        if (!err) {
                wdev->cac_started = true;
                wdev->cac_start_time = jiffies;
        }
 -err_locked:
 -      mutex_unlock(&rdev->devlist_mtx);
 -
        return err;
  }
  
@@@ -5927,13 -5946,10 +5934,13 @@@ static int nl80211_authenticate(struct 
        if (local_state_change)
                return 0;
  
 -      return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
 -                                ssid, ssid_len, ie, ie_len,
 -                                key.p.key, key.p.key_len, key.idx,
 -                                sae_data, sae_data_len);
 +      wdev_lock(dev->ieee80211_ptr);
 +      err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
 +                               ssid, ssid_len, ie, ie_len,
 +                               key.p.key, key.p.key_len, key.idx,
 +                               sae_data, sae_data_len);
 +      wdev_unlock(dev->ieee80211_ptr);
 +      return err;
  }
  
  static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
@@@ -6100,12 -6116,9 +6107,12 @@@ static int nl80211_associate(struct sk_
        }
  
        err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
 -      if (!err)
 +      if (!err) {
 +              wdev_lock(dev->ieee80211_ptr);
                err = cfg80211_mlme_assoc(rdev, dev, chan, bssid,
                                          ssid, ssid_len, &req);
 +              wdev_unlock(dev->ieee80211_ptr);
 +      }
  
        return err;
  }
@@@ -6115,7 -6128,7 +6122,7 @@@ static int nl80211_deauthenticate(struc
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
        const u8 *ie = NULL, *bssid;
 -      int ie_len = 0;
 +      int ie_len = 0, err;
        u16 reason_code;
        bool local_state_change;
  
  
        local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
  
 -      return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
 -                                  local_state_change);
 +      wdev_lock(dev->ieee80211_ptr);
 +      err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
 +                                 local_state_change);
 +      wdev_unlock(dev->ieee80211_ptr);
 +      return err;
  }
  
  static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
        const u8 *ie = NULL, *bssid;
 -      int ie_len = 0;
 +      int ie_len = 0, err;
        u16 reason_code;
        bool local_state_change;
  
  
        local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
  
 -      return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
 -                                    local_state_change);
 +      wdev_lock(dev->ieee80211_ptr);
 +      err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
 +                                   local_state_change);
 +      wdev_unlock(dev->ieee80211_ptr);
 +      return err;
  }
  
  static bool
@@@ -6288,16 -6295,11 +6295,16 @@@ static int nl80211_join_ibss(struct sk_
        if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef))
                return -EINVAL;
  
 -      if (ibss.chandef.width > NL80211_CHAN_WIDTH_40)
 -              return -EINVAL;
 -      if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
 -          !(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
 +      switch (ibss.chandef.width) {
 +      case NL80211_CHAN_WIDTH_20_NOHT:
 +              break;
 +      case NL80211_CHAN_WIDTH_20:
 +      case NL80211_CHAN_WIDTH_40:
 +              if (rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)
 +                      break;
 +      default:
                return -EINVAL;
 +      }
  
        ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
        ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
@@@ -6424,8 -6426,6 +6431,8 @@@ static int nl80211_testmode_dump(struc
        void *data = NULL;
        int data_len = 0;
  
 +      rtnl_lock();
 +
        if (cb->args[0]) {
                /*
                 * 0 is a valid index, but not valid for args[0],
                                  nl80211_fam.attrbuf, nl80211_fam.maxattr,
                                  nl80211_policy);
                if (err)
 -                      return err;
 +                      goto out_err;
  
 -              mutex_lock(&cfg80211_mutex);
                rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk),
                                                  nl80211_fam.attrbuf);
                if (IS_ERR(rdev)) {
 -                      mutex_unlock(&cfg80211_mutex);
 -                      return PTR_ERR(rdev);
 +                      err = PTR_ERR(rdev);
 +                      goto out_err;
                }
                phy_idx = rdev->wiphy_idx;
                rdev = NULL;
 -              mutex_unlock(&cfg80211_mutex);
  
                if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
                        cb->args[1] =
                data_len = nla_len((void *)cb->args[1]);
        }
  
 -      mutex_lock(&cfg80211_mutex);
        rdev = cfg80211_rdev_by_wiphy_idx(phy_idx);
        if (!rdev) {
 -              mutex_unlock(&cfg80211_mutex);
 -              return -ENOENT;
 +              err = -ENOENT;
 +              goto out_err;
        }
 -      cfg80211_lock_rdev(rdev);
 -      mutex_unlock(&cfg80211_mutex);
  
        if (!rdev->ops->testmode_dump) {
                err = -EOPNOTSUPP;
        /* see above */
        cb->args[0] = phy_idx + 1;
   out_err:
 -      cfg80211_unlock_rdev(rdev);
 +      rtnl_unlock();
        return err;
  }
  
@@@ -6711,9 -6716,7 +6718,9 @@@ static int nl80211_connect(struct sk_bu
                       sizeof(connect.vht_capa));
        }
  
 -      err = cfg80211_connect(rdev, dev, &connect, connkeys);
 +      wdev_lock(dev->ieee80211_ptr);
 +      err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL);
 +      wdev_unlock(dev->ieee80211_ptr);
        if (err)
                kfree(connkeys);
        return err;
@@@ -6724,7 -6727,6 +6731,7 @@@ static int nl80211_disconnect(struct sk
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
        u16 reason;
 +      int ret;
  
        if (!info->attrs[NL80211_ATTR_REASON_CODE])
                reason = WLAN_REASON_DEAUTH_LEAVING;
            dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
                return -EOPNOTSUPP;
  
 -      return cfg80211_disconnect(rdev, dev, reason, true);
 +      wdev_lock(dev->ieee80211_ptr);
 +      ret = cfg80211_disconnect(rdev, dev, reason, true);
 +      wdev_unlock(dev->ieee80211_ptr);
 +      return ret;
  }
  
  static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
@@@ -7160,9 -7159,6 +7167,9 @@@ static int nl80211_tx_mgmt(struct sk_bu
                return -EOPNOTSUPP;
  
        switch (wdev->iftype) {
 +      case NL80211_IFTYPE_P2P_DEVICE:
 +              if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
 +                      return -EINVAL;
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_ADHOC:
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_AP_VLAN:
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_P2P_GO:
 -      case NL80211_IFTYPE_P2P_DEVICE:
                break;
        default:
                return -EOPNOTSUPP;
  
        no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
  
 -      err = nl80211_parse_chandef(rdev, info, &chandef);
 -      if (err)
 -              return err;
 +      /* get the channel if any has been specified, otherwise pass NULL to
 +       * the driver. The latter will use the current one
 +       */
 +      chandef.chan = NULL;
 +      if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
 +              err = nl80211_parse_chandef(rdev, info, &chandef);
 +              if (err)
 +                      return err;
 +      }
 +
 +      if (!chandef.chan && offchan)
 +              return -EINVAL;
  
        if (!dont_wait_for_ack) {
                msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@@ -7513,23 -7501,6 +7520,23 @@@ static int nl80211_join_mesh(struct sk_
                setup.chandef.chan = NULL;
        }
  
 +      if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
 +              u8 *rates = nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
 +              int n_rates =
 +                      nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
 +              struct ieee80211_supported_band *sband;
 +
 +              if (!setup.chandef.chan)
 +                      return -EINVAL;
 +
 +              sband = rdev->wiphy.bands[setup.chandef.chan->band];
 +
 +              err = ieee80211_get_ratemask(sband, rates, n_rates,
 +                                           &setup.basic_rates);
 +              if (err)
 +                      return err;
 +      }
 +
        return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
  }
  
@@@ -7545,29 -7516,28 +7552,29 @@@ static int nl80211_leave_mesh(struct sk
  static int nl80211_send_wowlan_patterns(struct sk_buff *msg,
                                        struct cfg80211_registered_device *rdev)
  {
 +      struct cfg80211_wowlan *wowlan = rdev->wiphy.wowlan_config;
        struct nlattr *nl_pats, *nl_pat;
        int i, pat_len;
  
 -      if (!rdev->wowlan->n_patterns)
 +      if (!wowlan->n_patterns)
                return 0;
  
        nl_pats = nla_nest_start(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN);
        if (!nl_pats)
                return -ENOBUFS;
  
 -      for (i = 0; i < rdev->wowlan->n_patterns; i++) {
 +      for (i = 0; i < wowlan->n_patterns; i++) {
                nl_pat = nla_nest_start(msg, i + 1);
                if (!nl_pat)
                        return -ENOBUFS;
 -              pat_len = rdev->wowlan->patterns[i].pattern_len;
 +              pat_len = wowlan->patterns[i].pattern_len;
                if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK,
                            DIV_ROUND_UP(pat_len, 8),
 -                          rdev->wowlan->patterns[i].mask) ||
 +                          wowlan->patterns[i].mask) ||
                    nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
 -                          pat_len, rdev->wowlan->patterns[i].pattern) ||
 +                          pat_len, wowlan->patterns[i].pattern) ||
                    nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET,
 -                              rdev->wowlan->patterns[i].pkt_offset))
 +                              wowlan->patterns[i].pkt_offset))
                        return -ENOBUFS;
                nla_nest_end(msg, nl_pat);
        }
@@@ -7626,15 -7596,16 +7633,15 @@@ static int nl80211_get_wowlan(struct sk
        void *hdr;
        u32 size = NLMSG_DEFAULT_SIZE;
  
 -      if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns &&
 -          !rdev->wiphy.wowlan.tcp)
 +      if (!rdev->wiphy.wowlan)
                return -EOPNOTSUPP;
  
 -      if (rdev->wowlan && rdev->wowlan->tcp) {
 +      if (rdev->wiphy.wowlan_config && rdev->wiphy.wowlan_config->tcp) {
                /* adjust size to have room for all the data */
 -              size += rdev->wowlan->tcp->tokens_size +
 -                      rdev->wowlan->tcp->payload_len +
 -                      rdev->wowlan->tcp->wake_len +
 -                      rdev->wowlan->tcp->wake_len / 8;
 +              size += rdev->wiphy.wowlan_config->tcp->tokens_size +
 +                      rdev->wiphy.wowlan_config->tcp->payload_len +
 +                      rdev->wiphy.wowlan_config->tcp->wake_len +
 +                      rdev->wiphy.wowlan_config->tcp->wake_len / 8;
        }
  
        msg = nlmsg_new(size, GFP_KERNEL);
        if (!hdr)
                goto nla_put_failure;
  
 -      if (rdev->wowlan) {
 +      if (rdev->wiphy.wowlan_config) {
                struct nlattr *nl_wowlan;
  
                nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
                if (!nl_wowlan)
                        goto nla_put_failure;
  
 -              if ((rdev->wowlan->any &&
 +              if ((rdev->wiphy.wowlan_config->any &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
 -                  (rdev->wowlan->disconnect &&
 +                  (rdev->wiphy.wowlan_config->disconnect &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
 -                  (rdev->wowlan->magic_pkt &&
 +                  (rdev->wiphy.wowlan_config->magic_pkt &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
 -                  (rdev->wowlan->gtk_rekey_failure &&
 +                  (rdev->wiphy.wowlan_config->gtk_rekey_failure &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
 -                  (rdev->wowlan->eap_identity_req &&
 +                  (rdev->wiphy.wowlan_config->eap_identity_req &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
 -                  (rdev->wowlan->four_way_handshake &&
 +                  (rdev->wiphy.wowlan_config->four_way_handshake &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
 -                  (rdev->wowlan->rfkill_release &&
 +                  (rdev->wiphy.wowlan_config->rfkill_release &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
                        goto nla_put_failure;
  
                if (nl80211_send_wowlan_patterns(msg, rdev))
                        goto nla_put_failure;
  
 -              if (nl80211_send_wowlan_tcp(msg, rdev->wowlan->tcp))
 +              if (nl80211_send_wowlan_tcp(msg,
 +                                          rdev->wiphy.wowlan_config->tcp))
                        goto nla_put_failure;
  
                nla_nest_end(msg, nl_wowlan);
@@@ -7699,7 -7669,7 +7706,7 @@@ static int nl80211_parse_wowlan_tcp(str
        u32 data_size, wake_size, tokens_size = 0, wake_mask_size;
        int err, port;
  
 -      if (!rdev->wiphy.wowlan.tcp)
 +      if (!rdev->wiphy.wowlan->tcp)
                return -EINVAL;
  
        err = nla_parse(tb, MAX_NL80211_WOWLAN_TCP,
                return -EINVAL;
  
        data_size = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]);
 -      if (data_size > rdev->wiphy.wowlan.tcp->data_payload_max)
 +      if (data_size > rdev->wiphy.wowlan->tcp->data_payload_max)
                return -EINVAL;
  
        if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) >
 -                      rdev->wiphy.wowlan.tcp->data_interval_max ||
 +                      rdev->wiphy.wowlan->tcp->data_interval_max ||
            nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) == 0)
                return -EINVAL;
  
        wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]);
 -      if (wake_size > rdev->wiphy.wowlan.tcp->wake_payload_max)
 +      if (wake_size > rdev->wiphy.wowlan->tcp->wake_payload_max)
                return -EINVAL;
  
        wake_mask_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_MASK]);
  
                if (!tok->len || tokens_size % tok->len)
                        return -EINVAL;
 -              if (!rdev->wiphy.wowlan.tcp->tok)
 +              if (!rdev->wiphy.wowlan->tcp->tok)
                        return -EINVAL;
 -              if (tok->len > rdev->wiphy.wowlan.tcp->tok->max_len)
 +              if (tok->len > rdev->wiphy.wowlan->tcp->tok->max_len)
                        return -EINVAL;
 -              if (tok->len < rdev->wiphy.wowlan.tcp->tok->min_len)
 +              if (tok->len < rdev->wiphy.wowlan->tcp->tok->min_len)
                        return -EINVAL;
 -              if (tokens_size > rdev->wiphy.wowlan.tcp->tok->bufsize)
 +              if (tokens_size > rdev->wiphy.wowlan->tcp->tok->bufsize)
                        return -EINVAL;
                if (tok->offset + tok->len > data_size)
                        return -EINVAL;
  
        if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]) {
                seq = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]);
 -              if (!rdev->wiphy.wowlan.tcp->seq)
 +              if (!rdev->wiphy.wowlan->tcp->seq)
                        return -EINVAL;
                if (seq->len == 0 || seq->len > 4)
                        return -EINVAL;
@@@ -7838,16 -7808,17 +7845,16 @@@ static int nl80211_set_wowlan(struct sk
        struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
        struct cfg80211_wowlan new_triggers = {};
        struct cfg80211_wowlan *ntrig;
 -      struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
 +      const struct wiphy_wowlan_support *wowlan = rdev->wiphy.wowlan;
        int err, i;
 -      bool prev_enabled = rdev->wowlan;
 +      bool prev_enabled = rdev->wiphy.wowlan_config;
  
 -      if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns &&
 -          !rdev->wiphy.wowlan.tcp)
 +      if (!wowlan)
                return -EOPNOTSUPP;
  
        if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
                cfg80211_rdev_free_wowlan(rdev);
 -              rdev->wowlan = NULL;
 +              rdev->wiphy.wowlan_config = NULL;
                goto set_wakeup;
        }
  
                goto error;
        }
        cfg80211_rdev_free_wowlan(rdev);
 -      rdev->wowlan = ntrig;
 +      rdev->wiphy.wowlan_config = ntrig;
  
   set_wakeup:
 -      if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan)
 -              rdev_set_wakeup(rdev, rdev->wowlan);
 +      if (rdev->ops->set_wakeup &&
 +          prev_enabled != !!rdev->wiphy.wowlan_config)
 +              rdev_set_wakeup(rdev, rdev->wiphy.wowlan_config);
  
        return 0;
   error:
@@@ -8173,7 -8143,9 +8180,7 @@@ static int nl80211_start_p2p_device(str
        if (wdev->p2p_started)
                return 0;
  
 -      mutex_lock(&rdev->devlist_mtx);
        err = cfg80211_can_add_interface(rdev, wdev->iftype);
 -      mutex_unlock(&rdev->devlist_mtx);
        if (err)
                return err;
  
                return err;
  
        wdev->p2p_started = true;
 -      mutex_lock(&rdev->devlist_mtx);
        rdev->opencount++;
 -      mutex_unlock(&rdev->devlist_mtx);
  
        return 0;
  }
@@@ -8198,7 -8172,11 +8205,7 @@@ static int nl80211_stop_p2p_device(stru
        if (!rdev->ops->stop_p2p_device)
                return -EOPNOTSUPP;
  
 -      mutex_lock(&rdev->devlist_mtx);
 -      mutex_lock(&rdev->sched_scan_mtx);
        cfg80211_stop_p2p_device(rdev, wdev);
 -      mutex_unlock(&rdev->sched_scan_mtx);
 -      mutex_unlock(&rdev->devlist_mtx);
  
        return 0;
  }
@@@ -8341,11 -8319,11 +8348,11 @@@ static int nl80211_pre_doit(struct genl
                info->user_ptr[0] = rdev;
        } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV ||
                   ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
 -              mutex_lock(&cfg80211_mutex);
 +              ASSERT_RTNL();
 +
                wdev = __cfg80211_wdev_from_attrs(genl_info_net(info),
                                                  info->attrs);
                if (IS_ERR(wdev)) {
 -                      mutex_unlock(&cfg80211_mutex);
                        if (rtnl)
                                rtnl_unlock();
                        return PTR_ERR(wdev);
  
                if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
                        if (!dev) {
 -                              mutex_unlock(&cfg80211_mutex);
                                if (rtnl)
                                        rtnl_unlock();
                                return -EINVAL;
                if (dev) {
                        if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
                            !netif_running(dev)) {
 -                              mutex_unlock(&cfg80211_mutex);
                                if (rtnl)
                                        rtnl_unlock();
                                return -ENETDOWN;
                        dev_hold(dev);
                } else if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP) {
                        if (!wdev->p2p_started) {
 -                              mutex_unlock(&cfg80211_mutex);
                                if (rtnl)
                                        rtnl_unlock();
                                return -ENETDOWN;
                        }
                }
  
 -              cfg80211_lock_rdev(rdev);
 -
 -              mutex_unlock(&cfg80211_mutex);
 -
                info->user_ptr[0] = rdev;
        }
  
  static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
                              struct genl_info *info)
  {
 -      if (info->user_ptr[0])
 -              cfg80211_unlock_rdev(info->user_ptr[0]);
        if (info->user_ptr[1]) {
                if (ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
                        struct wireless_dev *wdev = info->user_ptr[1];
@@@ -8413,8 -8400,7 +8420,8 @@@ static struct genl_ops nl80211_ops[] = 
                .dumpit = nl80211_dump_wiphy,
                .policy = nl80211_policy,
                /* can be retrieved by unprivileged users */
 -              .internal_flags = NL80211_FLAG_NEED_WIPHY,
 +              .internal_flags = NL80211_FLAG_NEED_WIPHY |
 +                                NL80211_FLAG_NEED_RTNL,
        },
        {
                .cmd = NL80211_CMD_SET_WIPHY,
                .dumpit = nl80211_dump_interface,
                .policy = nl80211_policy,
                /* can be retrieved by unprivileged users */
 -              .internal_flags = NL80211_FLAG_NEED_WDEV,
 +              .internal_flags = NL80211_FLAG_NEED_WDEV |
 +                                NL80211_FLAG_NEED_RTNL,
        },
        {
                .cmd = NL80211_CMD_SET_INTERFACE,
                .cmd = NL80211_CMD_GET_REG,
                .doit = nl80211_get_reg,
                .policy = nl80211_policy,
 +              .internal_flags = NL80211_FLAG_NEED_RTNL,
                /* can be retrieved by unprivileged users */
        },
        {
                .doit = nl80211_set_reg,
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
 +              .internal_flags = NL80211_FLAG_NEED_RTNL,
        },
        {
                .cmd = NL80211_CMD_REQ_SET_REG,
@@@ -9053,6 -9036,8 +9060,6 @@@ static int nl80211_add_scan_req(struct 
        struct nlattr *nest;
        int i;
  
 -      lockdep_assert_held(&rdev->sched_scan_mtx);
 -
        if (WARN_ON(!req))
                return 0;
  
@@@ -9359,27 -9344,31 +9366,27 @@@ void nl80211_send_disassoc(struct cfg80
                                NL80211_CMD_DISASSOCIATE, gfp);
  }
  
 -void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
 -                               size_t len)
 +void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf,
 +                                size_t len)
  {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
 +      const struct ieee80211_mgmt *mgmt = (void *)buf;
 +      u32 cmd;
  
 -      trace_cfg80211_send_unprot_deauth(dev);
 -      nl80211_send_mlme_event(rdev, dev, buf, len,
 -                              NL80211_CMD_UNPROT_DEAUTHENTICATE, GFP_ATOMIC);
 -}
 -EXPORT_SYMBOL(cfg80211_send_unprot_deauth);
 +      if (WARN_ON(len < 2))
 +              return;
  
 -void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
 -                                 size_t len)
 -{
 -      struct wireless_dev *wdev = dev->ieee80211_ptr;
 -      struct wiphy *wiphy = wdev->wiphy;
 -      struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
 +      if (ieee80211_is_deauth(mgmt->frame_control))
 +              cmd = NL80211_CMD_UNPROT_DEAUTHENTICATE;
 +      else
 +              cmd = NL80211_CMD_UNPROT_DISASSOCIATE;
  
 -      trace_cfg80211_send_unprot_disassoc(dev);
 -      nl80211_send_mlme_event(rdev, dev, buf, len,
 -                              NL80211_CMD_UNPROT_DISASSOCIATE, GFP_ATOMIC);
 +      trace_cfg80211_rx_unprot_mlme_mgmt(dev, buf, len);
 +      nl80211_send_mlme_event(rdev, dev, buf, len, cmd, GFP_ATOMIC);
  }
 -EXPORT_SYMBOL(cfg80211_send_unprot_disassoc);
 +EXPORT_SYMBOL(cfg80211_rx_unprot_mlme_mgmt);
  
  static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
                                      struct net_device *netdev, int cmd,
@@@ -9890,6 -9879,7 +9897,6 @@@ static bool __nl80211_unexpected_frame(
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
 -      int err;
        u32 nlportid = ACCESS_ONCE(wdev->ap_unexpected_nlportid);
  
        if (!nlportid)
            nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
                goto nla_put_failure;
  
 -      err = genlmsg_end(msg, hdr);
 -      if (err < 0) {
 -              nlmsg_free(msg);
 -              return true;
 -      }
 -
 +      genlmsg_end(msg, hdr);
        genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
        return true;
  
@@@ -10353,7 -10348,10 +10360,7 @@@ nl80211_radar_notify(struct cfg80211_re
        if (nl80211_send_chandef(msg, chandef))
                goto nla_put_failure;
  
 -      if (genlmsg_end(msg, hdr) < 0) {
 -              nlmsg_free(msg);
 -              return;
 -      }
 +      genlmsg_end(msg, hdr);
  
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@@ -10419,6 -10417,7 +10426,6 @@@ void cfg80211_probe_status(struct net_d
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
 -      int err;
  
        trace_cfg80211_probe_status(dev, addr, cookie, acked);
  
            (acked && nla_put_flag(msg, NL80211_ATTR_ACK)))
                goto nla_put_failure;
  
 -      err = genlmsg_end(msg, hdr);
 -      if (err < 0) {
 -              nlmsg_free(msg);
 -              return;
 -      }
 +      genlmsg_end(msg, hdr);
  
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@@ -10506,7 -10509,7 +10513,7 @@@ void cfg80211_report_wowlan_wakeup(stru
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
 -      int err, size = 200;
 +      int size = 200;
  
        trace_cfg80211_report_wowlan_wakeup(wdev->wiphy, wdev, wakeup);
  
                nla_nest_end(msg, reasons);
        }
  
 -      err = genlmsg_end(msg, hdr);
 -      if (err < 0)
 -              goto free_msg;
 +      genlmsg_end(msg, hdr);
  
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@@ -10612,6 -10617,7 +10619,6 @@@ void cfg80211_tdls_oper_request(struct 
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
 -      int err;
  
        trace_cfg80211_tdls_oper_request(wdev->wiphy, dev, peer, oper,
                                         reason_code);
             nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code)))
                goto nla_put_failure;
  
 -      err = genlmsg_end(msg, hdr);
 -      if (err < 0) {
 -              nlmsg_free(msg);
 -              return;
 -      }
 +      genlmsg_end(msg, hdr);
  
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@@ -10692,6 -10702,7 +10699,6 @@@ void cfg80211_ft_event(struct net_devic
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        void *hdr;
 -      int err;
  
        trace_cfg80211_ft_event(wiphy, netdev, ft_event);
  
                nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len,
                        ft_event->ric_ies);
  
 -      err = genlmsg_end(msg, hdr);
 -      if (err < 0) {
 -              nlmsg_free(msg);
 -              return;
 -      }
 +      genlmsg_end(msg, hdr);
  
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, GFP_KERNEL);