Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / iwlwifi / mvm / quota.c
index ce5db6c4ef7e60e3ad557cd0e2d773738419d587..ba68d7b8450508d9c7b123500b654d2195b5613a 100644 (file)
 #include "fw-api.h"
 #include "mvm.h"
 
+#define QUOTA_100      IWL_MVM_MAX_QUOTA
+#define QUOTA_LOWLAT_MIN ((QUOTA_100 * IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT) / 100)
+
 struct iwl_mvm_quota_iterator_data {
        int n_interfaces[MAX_BINDINGS];
        int colors[MAX_BINDINGS];
+       int low_latency[MAX_BINDINGS];
+       int n_low_latency_bindings;
        struct ieee80211_vif *new_vif;
 };
 
@@ -107,22 +112,29 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
                if (vif->bss_conf.assoc)
-                       data->n_interfaces[id]++;
-               break;
+                       break;
+               return;
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_ADHOC:
                if (mvmvif->ap_ibss_active)
-                       data->n_interfaces[id]++;
-               break;
+                       break;
+               return;
        case NL80211_IFTYPE_MONITOR:
                if (mvmvif->monitor_active)
-                       data->n_interfaces[id]++;
-               break;
+                       break;
+               return;
        case NL80211_IFTYPE_P2P_DEVICE:
-               break;
+               return;
        default:
                WARN_ON_ONCE(1);
-               break;
+               return;
+       }
+
+       data->n_interfaces[id]++;
+
+       if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) {
+               data->n_low_latency_bindings++;
+               data->low_latency[id] = true;
        }
 }
 
@@ -162,7 +174,7 @@ static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm,
 int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
 {
        struct iwl_time_quota_cmd cmd = {};
-       int i, idx, ret, num_active_macs, quota, quota_rem;
+       int i, idx, ret, num_active_macs, quota, quota_rem, n_non_lowlat;
        struct iwl_mvm_quota_iterator_data data = {
                .n_interfaces = {},
                .colors = { -1, -1, -1, -1 },
@@ -197,11 +209,39 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
                num_active_macs += data.n_interfaces[i];
        }
 
-       quota = 0;
-       quota_rem = 0;
-       if (num_active_macs) {
-               quota = IWL_MVM_MAX_QUOTA / num_active_macs;
-               quota_rem = IWL_MVM_MAX_QUOTA % num_active_macs;
+       n_non_lowlat = num_active_macs;
+
+       if (data.n_low_latency_bindings == 1) {
+               for (i = 0; i < MAX_BINDINGS; i++) {
+                       if (data.low_latency[i]) {
+                               n_non_lowlat -= data.n_interfaces[i];
+                               break;
+                       }
+               }
+       }
+
+       if (data.n_low_latency_bindings == 1 && n_non_lowlat) {
+               /*
+                * Reserve quota for the low latency binding in case that
+                * there are several data bindings but only a single
+                * low latency one. Split the rest of the quota equally
+                * between the other data interfaces.
+                */
+               quota = (QUOTA_100 - QUOTA_LOWLAT_MIN) / n_non_lowlat;
+               quota_rem = QUOTA_100 - n_non_lowlat * quota -
+                           QUOTA_LOWLAT_MIN;
+       } else if (num_active_macs) {
+               /*
+                * There are 0 or more than 1 low latency bindings, or all the
+                * data interfaces belong to the single low latency binding.
+                * Split the quota equally between the data interfaces.
+                */
+               quota = QUOTA_100 / num_active_macs;
+               quota_rem = QUOTA_100 % num_active_macs;
+       } else {
+               /* values don't really matter - won't be used */
+               quota = 0;
+               quota_rem = 0;
        }
 
        for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {
@@ -211,23 +251,41 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
                cmd.quotas[idx].id_and_color =
                        cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i]));
 
-               if (data.n_interfaces[i] <= 0) {
+               if (data.n_interfaces[i] <= 0)
                        cmd.quotas[idx].quota = cpu_to_le32(0);
-                       cmd.quotas[idx].max_duration = cpu_to_le32(0);
-               } else {
+               else if (data.n_low_latency_bindings == 1 && n_non_lowlat &&
+                        data.low_latency[i])
+                       /*
+                        * There is more than one binding, but only one of the
+                        * bindings is in low latency. For this case, allocate
+                        * the minimal required quota for the low latency
+                        * binding.
+                        */
+                       cmd.quotas[idx].quota = cpu_to_le32(QUOTA_LOWLAT_MIN);
+               else
                        cmd.quotas[idx].quota =
                                cpu_to_le32(quota * data.n_interfaces[i]);
-                       cmd.quotas[idx].max_duration = cpu_to_le32(0);
-               }
+
+               WARN_ONCE(le32_to_cpu(cmd.quotas[idx].quota) > QUOTA_100,
+                         "Binding=%d, quota=%u > max=%u\n",
+                         idx, le32_to_cpu(cmd.quotas[idx].quota), QUOTA_100);
+
+               cmd.quotas[idx].max_duration = cpu_to_le32(0);
+
                idx++;
        }
 
-       /* Give the remainder of the session to the first binding */
-       le32_add_cpu(&cmd.quotas[0].quota, quota_rem);
+       /* Give the remainder of the session to the first data binding */
+       for (i = 0; i < MAX_BINDINGS; i++) {
+               if (le32_to_cpu(cmd.quotas[i].quota) != 0) {
+                       le32_add_cpu(&cmd.quotas[i].quota, quota_rem);
+                       break;
+               }
+       }
 
        iwl_mvm_adjust_quota_for_noa(mvm, &cmd);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC,
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0,
                                   sizeof(cmd), &cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send quota: %d\n", ret);