]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - net/mac80211/iface.c
mac80211: add improved HW queue control
[mirror_ubuntu-jammy-kernel.git] / net / mac80211 / iface.c
index 8e2137bd87e2435d45294ffa192f34688ab61097..ed297649c5773b3db3b0921677e94a9328d64e32 100644 (file)
@@ -149,6 +149,34 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
        return 0;
 }
 
+static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata)
+{
+       int n_queues = sdata->local->hw.queues;
+       int i;
+
+       for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+               if (WARN_ON_ONCE(sdata->vif.hw_queue[i] ==
+                                IEEE80211_INVAL_HW_QUEUE))
+                       return -EINVAL;
+               if (WARN_ON_ONCE(sdata->vif.hw_queue[i] >=
+                                n_queues))
+                       return -EINVAL;
+       }
+
+       if (sdata->vif.type != NL80211_IFTYPE_AP) {
+               sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE;
+               return 0;
+       }
+
+       if (WARN_ON_ONCE(sdata->vif.cab_queue == IEEE80211_INVAL_HW_QUEUE))
+               return -EINVAL;
+
+       if (WARN_ON_ONCE(sdata->vif.cab_queue >= n_queues))
+               return -EINVAL;
+
+       return 0;
+}
+
 void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
                                    const int offset)
 {
@@ -169,6 +197,81 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
 #undef ADJUST
 }
 
+static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       int i;
+
+       for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+               if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)
+                       sdata->vif.hw_queue[i] = IEEE80211_INVAL_HW_QUEUE;
+               else
+                       sdata->vif.hw_queue[i] = i;
+       }
+       sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE;
+}
+
+static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata;
+       int ret;
+
+       if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))
+               return 0;
+
+       if (local->monitor_sdata)
+               return 0;
+
+       sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
+       if (!sdata)
+               return -ENOMEM;
+
+       /* set up data */
+       sdata->local = local;
+       sdata->vif.type = NL80211_IFTYPE_MONITOR;
+       snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
+                wiphy_name(local->hw.wiphy));
+
+       ieee80211_set_default_queues(sdata);
+
+       ret = drv_add_interface(local, sdata);
+       if (WARN_ON(ret)) {
+               /* ok .. stupid driver, it asked for this! */
+               kfree(sdata);
+               return ret;
+       }
+
+       ret = ieee80211_check_queues(sdata);
+       if (ret) {
+               kfree(sdata);
+               return ret;
+       }
+
+       rcu_assign_pointer(local->monitor_sdata, sdata);
+
+       return 0;
+}
+
+static void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))
+               return;
+
+       sdata = rtnl_dereference(local->monitor_sdata);
+
+       if (!sdata)
+               return;
+
+       rcu_assign_pointer(local->monitor_sdata, NULL);
+       synchronize_net();
+
+       drv_remove_interface(local, sdata);
+
+       kfree(sdata);
+}
+
 /*
  * NOTE: Be very careful when changing this function, it must NOT return
  * an error on interface type changes that have been pre-checked, so most
@@ -254,7 +357,11 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP_VLAN:
-               /* no need to tell driver */
+               /* no need to tell driver, but set carrier */
+               if (rtnl_dereference(sdata->bss->beacon))
+                       netif_carrier_on(dev);
+               else
+                       netif_carrier_off(dev);
                break;
        case NL80211_IFTYPE_MONITOR:
                if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) {
@@ -262,6 +369,12 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
                        break;
                }
 
+               if (local->monitors == 0 && local->open_count == 0) {
+                       res = ieee80211_add_virtual_monitor(local);
+                       if (res)
+                               goto err_stop;
+               }
+
                /* must be before the call to ieee80211_configure_filter */
                local->monitors++;
                if (local->monitors == 1) {
@@ -276,9 +389,14 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
                break;
        default:
                if (coming_up) {
+                       ieee80211_del_virtual_monitor(local);
+
                        res = drv_add_interface(local, sdata);
                        if (res)
                                goto err_stop;
+                       res = ieee80211_check_queues(sdata);
+                       if (res)
+                               goto err_del_interface;
                }
 
                if (sdata->vif.type == NL80211_IFTYPE_AP) {
@@ -294,7 +412,8 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
                ieee80211_bss_info_change_notify(sdata, changed);
 
                if (sdata->vif.type == NL80211_IFTYPE_STATION ||
-                   sdata->vif.type == NL80211_IFTYPE_ADHOC)
+                   sdata->vif.type == NL80211_IFTYPE_ADHOC ||
+                   sdata->vif.type == NL80211_IFTYPE_AP)
                        netif_carrier_off(dev);
                else
                        netif_carrier_on(dev);
@@ -304,7 +423,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
                 * need to initialise the hardware if the hardware
                 * doesn't start up with sane defaults
                 */
-               ieee80211_set_wmm_default(sdata);
+               ieee80211_set_wmm_default(sdata, true);
        }
 
        set_bit(SDATA_STATE_RUNNING, &sdata->state);
@@ -318,9 +437,9 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
                        goto err_del_interface;
                }
 
-               sta_info_move_state(sta, IEEE80211_STA_AUTH);
-               sta_info_move_state(sta, IEEE80211_STA_ASSOC);
-               sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
+               sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
+               sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
+               sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
 
                res = sta_info_insert(sta);
                if (res) {
@@ -506,6 +625,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                if (local->monitors == 0) {
                        local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR;
                        hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
+                       ieee80211_del_virtual_monitor(local);
                }
 
                ieee80211_adjust_monitor_flags(sdata, -1);
@@ -579,6 +699,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                }
        }
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+
+       if (local->monitors == local->open_count && local->monitors > 0)
+               ieee80211_add_virtual_monitor(local);
 }
 
 static int ieee80211_stop(struct net_device *dev)
@@ -644,6 +767,8 @@ static void ieee80211_teardown_sdata(struct net_device *dev)
 
        if (ieee80211_vif_is_mesh(&sdata->vif))
                mesh_rmc_free(sdata);
+       else if (sdata->vif.type == NL80211_IFTYPE_STATION)
+               ieee80211_mgd_teardown(sdata);
 
        flushed = sta_info_flush(local, sdata);
        WARN_ON(flushed);
@@ -674,7 +799,7 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev,
        struct ieee80211_hdr *hdr;
        struct ieee80211_radiotap_header *rtap = (void *)skb->data;
 
-       if (local->hw.queues < 4)
+       if (local->hw.queues < IEEE80211_NUM_ACS)
                return 0;
 
        if (skb->len < 4 ||
@@ -968,6 +1093,13 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
        if (ret)
                type = sdata->vif.type;
 
+       /*
+        * Ignore return value here, there's not much we can do since
+        * the driver changed the interface type internally already.
+        * The warnings will hopefully make driver authors fix it :-)
+        */
+       ieee80211_check_queues(sdata);
+
        ieee80211_setup_sdata(sdata, type);
 
        err = ieee80211_do_open(sdata->dev, false);
@@ -1131,11 +1263,15 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
        struct net_device *ndev;
        struct ieee80211_sub_if_data *sdata = NULL;
        int ret, i;
+       int txqs = 1;
 
        ASSERT_RTNL();
 
+       if (local->hw.queues >= IEEE80211_NUM_ACS)
+               txqs = IEEE80211_NUM_ACS;
+
        ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size,
-                               name, ieee80211_if_setup, local->hw.queues, 1);
+                               name, ieee80211_if_setup, txqs, 1);
        if (!ndev)
                return -ENOMEM;
        dev_net_set(ndev, wiphy_net(local->hw.wiphy));
@@ -1181,8 +1317,17 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
                sband = local->hw.wiphy->bands[i];
                sdata->rc_rateidx_mask[i] =
                        sband ? (1 << sband->n_bitrates) - 1 : 0;
+               if (sband)
+                       memcpy(sdata->rc_rateidx_mcs_mask[i],
+                              sband->ht_cap.mcs.rx_mask,
+                              sizeof(sdata->rc_rateidx_mcs_mask[i]));
+               else
+                       memset(sdata->rc_rateidx_mcs_mask[i], 0,
+                              sizeof(sdata->rc_rateidx_mcs_mask[i]));
        }
 
+       ieee80211_set_default_queues(sdata);
+
        /* setup type-dependent data */
        ieee80211_setup_sdata(sdata, type);
 
@@ -1303,7 +1448,9 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
 
                /* do not count disabled managed interfaces */
                if (sdata->vif.type == NL80211_IFTYPE_STATION &&
-                   !sdata->u.mgd.associated) {
+                   !sdata->u.mgd.associated &&
+                   !sdata->u.mgd.auth_data &&
+                   !sdata->u.mgd.assoc_data) {
                        sdata->vif.bss_conf.idle = true;
                        continue;
                }
@@ -1323,7 +1470,8 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
                wk->sdata->vif.bss_conf.idle = false;
        }
 
-       if (local->scan_sdata) {
+       if (local->scan_sdata &&
+           !(local->hw.flags & IEEE80211_HW_SCAN_WHILE_IDLE)) {
                scanning = true;
                local->scan_sdata->vif.bss_conf.idle = false;
        }