]> err.no Git - linux-2.6/blobdiff - net/mac80211/rc80211_pid_algo.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6
[linux-2.6] / net / mac80211 / rc80211_pid_algo.c
index 3fac3a5d7e00eae9f8646e360a18491a8a7e0314..3b77410588e734258706245c8057d741a7916e8d 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright 2002-2005, Instant802 Networks, Inc.
  * Copyright 2005, Devicescape Software, Inc.
  * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
- * Copyright 2007, Stefano Brivio <stefano.brivio@polimi.it>
+ * Copyright 2007-2008, Stefano Brivio <stefano.brivio@polimi.it>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -12,7 +12,7 @@
 #include <linux/netdevice.h>
 #include <linux/types.h>
 #include <linux/skbuff.h>
-
+#include <linux/debugfs.h>
 #include <net/mac80211.h>
 #include "ieee80211_rate.h"
 
  * RC_PID_ARITH_SHIFT.
  */
 
-
-/* Shift the adjustment so that we won't switch to a lower rate if it exhibited
- * a worse failed frames behaviour and we'll choose the highest rate whose
- * failed frames behaviour is not worse than the one of the original rate
- * target. While at it, check that the adjustment is within the ranges. Then,
- * provide the new rate index. */
-static int rate_control_pid_shift_adjust(struct rc_pid_rateinfo *r,
-                                        int adj, int cur, int l)
-{
-       int i, j, k, tmp;
-
-       if (cur + adj < 0)
-               return 0;
-       if (cur + adj >= l)
-               return l - 1;
-
-       i = r[cur + adj].rev_index;
-
-       j = r[cur].rev_index;
-
-       if (adj < 0) {
-                       tmp = i;
-                       for (k = j; k >= i; k--)
-                               if (r[k].diff <= r[j].diff)
-                                       tmp = k;
-                       return r[tmp].index;
-       } else if (adj > 0) {
-                       tmp = i;
-                       for (k = i + 1; k + i < l; k++)
-                               if (r[k].diff <= r[i].diff)
-                                       tmp = k;
-                       return r[tmp].index;
-       }
-       return cur + adj;
-}
-
+/* Adjust the rate while ensuring that we won't switch to a lower rate if it
+ * exhibited a worse failed frames behaviour and we'll choose the highest rate
+ * whose failed frames behaviour is not worse than the one of the original rate
+ * target. While at it, check that the new rate is valid. */
 static void rate_control_pid_adjust_rate(struct ieee80211_local *local,
                                         struct sta_info *sta, int adj,
                                         struct rc_pid_rateinfo *rinfo)
 {
        struct ieee80211_sub_if_data *sdata;
        struct ieee80211_hw_mode *mode;
-       int newidx;
-       int maxrate;
-       int back = (adj > 0) ? 1 : -1;
+       int cur_sorted, new_sorted, probe, tmp, n_bitrates;
+       int cur = sta->txrate;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
-       if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) {
-               /* forced unicast rate - do not change STA rate */
-               return;
-       }
 
        mode = local->oper_hw_mode;
-       maxrate = sdata->bss ? sdata->bss->max_ratectrl_rateidx : -1;
+       n_bitrates = mode->num_rates;
 
-       newidx = rate_control_pid_shift_adjust(rinfo, adj, sta->txrate,
-                                              mode->num_rates);
+       /* Map passed arguments to sorted values. */
+       cur_sorted = rinfo[cur].rev_index;
+       new_sorted = cur_sorted + adj;
 
-       while (newidx != sta->txrate) {
-               if (rate_supported(sta, mode, newidx) &&
-                   (maxrate < 0 || newidx <= maxrate)) {
-                       sta->txrate = newidx;
-                       break;
-               }
+       /* Check limits. */
+       if (new_sorted < 0)
+               new_sorted = rinfo[0].rev_index;
+       else if (new_sorted >= n_bitrates)
+               new_sorted = rinfo[n_bitrates - 1].rev_index;
+
+       tmp = new_sorted;
 
-               newidx += back;
+       if (adj < 0) {
+               /* Ensure that the rate decrease isn't disadvantageous. */
+               for (probe = cur_sorted; probe >= new_sorted; probe--)
+                       if (rinfo[probe].diff <= rinfo[cur_sorted].diff &&
+                           rate_supported(sta, mode, rinfo[probe].index))
+                               tmp = probe;
+       } else {
+               /* Look for rate increase with zero (or below) cost. */
+               for (probe = new_sorted + 1; probe < n_bitrates; probe++)
+                       if (rinfo[probe].diff <= rinfo[new_sorted].diff &&
+                           rate_supported(sta, mode, rinfo[probe].index))
+                               tmp = probe;
        }
 
+       /* Fit the rate found to the nearest supported rate. */
+       do {
+               if (rate_supported(sta, mode, rinfo[tmp].index)) {
+                       sta->txrate = rinfo[tmp].index;
+                       break;
+               }
+               if (adj < 0)
+                       tmp--;
+               else
+                       tmp++;
+       } while (tmp < n_bitrates && tmp >= 0);
+
 #ifdef CONFIG_MAC80211_DEBUGFS
        rate_control_pid_event_rate_change(
                &((struct rc_pid_sta_info *)sta->rate_ctrl_priv)->events,
-               newidx, mode->rates[newidx].rate);
+               cur, mode->rates[cur].rate);
 #endif
 }
 
 /* Normalize the failed frames per-rate differences. */
-static void rate_control_pid_normalize(struct rc_pid_rateinfo *r, int l)
+static void rate_control_pid_normalize(struct rc_pid_info *pinfo, int l)
 {
-       int i;
+       int i, norm_offset = pinfo->norm_offset;
+       struct rc_pid_rateinfo *r = pinfo->rinfo;
 
-       if (r[0].diff > RC_PID_NORM_OFFSET)
-               r[0].diff -= RC_PID_NORM_OFFSET;
-       else if (r[0].diff < -RC_PID_NORM_OFFSET)
-               r[0].diff += RC_PID_NORM_OFFSET;
+       if (r[0].diff > norm_offset)
+               r[0].diff -= norm_offset;
+       else if (r[0].diff < -norm_offset)
+               r[0].diff += norm_offset;
        for (i = 0; i < l - 1; i++)
-               if (r[i + 1].diff > r[i].diff + RC_PID_NORM_OFFSET)
-                       r[i + 1].diff -= RC_PID_NORM_OFFSET;
+               if (r[i + 1].diff > r[i].diff + norm_offset)
+                       r[i + 1].diff -= norm_offset;
                else if (r[i + 1].diff <= r[i].diff)
-                       r[i + 1].diff += RC_PID_NORM_OFFSET;
+                       r[i + 1].diff += norm_offset;
 }
 
 static void rate_control_pid_sample(struct rc_pid_info *pinfo,
@@ -163,18 +152,22 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo,
        struct ieee80211_hw_mode *mode;
        u32 pf;
        s32 err_avg;
-       s32 err_prop;
-       s32 err_int;
-       s32 err_der;
+       u32 err_prop;
+       u32 err_int;
+       u32 err_der;
        int adj, i, j, tmp;
+       unsigned long period;
 
        mode = local->oper_hw_mode;
        spinfo = sta->rate_ctrl_priv;
 
        /* In case nothing happened during the previous control interval, turn
         * the sharpening factor on. */
-       if (jiffies - spinfo->last_sample > 2 * RC_PID_INTERVAL)
-               spinfo->sharp_cnt = RC_PID_SHARPENING_DURATION;
+       period = (HZ * pinfo->sampling_period + 500) / 1000;
+       if (!period)
+               period = 1;
+       if (jiffies - spinfo->last_sample > 2 * period)
+               spinfo->sharp_cnt = pinfo->sharpen_duration;
 
        spinfo->last_sample = jiffies;
 
@@ -202,17 +195,17 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo,
                rinfo[j].diff = rinfo[i].diff + tmp;
                pinfo->oldrate = sta->txrate;
        }
-       rate_control_pid_normalize(rinfo, mode->num_rates);
+       rate_control_pid_normalize(pinfo, mode->num_rates);
 
        /* Compute the proportional, integral and derivative errors. */
-       err_prop = RC_PID_TARGET_PF - pf;
+       err_prop = (pinfo->target << RC_PID_ARITH_SHIFT) - pf;
 
-       err_avg = spinfo->err_avg_sc >> RC_PID_SMOOTHING_SHIFT;
+       err_avg = spinfo->err_avg_sc >> pinfo->smoothing_shift;
        spinfo->err_avg_sc = spinfo->err_avg_sc - err_avg + err_prop;
-       err_int = spinfo->err_avg_sc >> RC_PID_SMOOTHING_SHIFT;
+       err_int = spinfo->err_avg_sc >> pinfo->smoothing_shift;
 
-       err_der = pf - spinfo->last_pf
-                 * (1 + RC_PID_SHARPENING_FACTOR * spinfo->sharp_cnt);
+       err_der = (pf - spinfo->last_pf) *
+                 (1 + pinfo->sharpen_factor * spinfo->sharp_cnt);
        spinfo->last_pf = pf;
        if (spinfo->sharp_cnt)
                        spinfo->sharp_cnt--;
@@ -238,19 +231,28 @@ static void rate_control_pid_tx_status(void *priv, struct net_device *dev,
 {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       struct ieee80211_sub_if_data *sdata;
        struct rc_pid_info *pinfo = priv;
        struct sta_info *sta;
        struct rc_pid_sta_info *spinfo;
+       unsigned long period;
 
        sta = sta_info_get(local, hdr->addr1);
 
        if (!sta)
                return;
 
+       /* Don't update the state if we're not controlling the rate. */
+       sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+       if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) {
+               sta->txrate = sdata->bss->max_ratectrl_rateidx;
+               return;
+       }
+
        /* Ignore all frames that were sent with a different rate than the rate
         * we currently advise mac80211 to use. */
        if (status->control.rate != &local->oper_hw_mode->rates[sta->txrate])
-               return;
+               goto ignore;
 
        spinfo = sta->rate_ctrl_priv;
        spinfo->tx_num_xmit++;
@@ -285,9 +287,13 @@ static void rate_control_pid_tx_status(void *priv, struct net_device *dev,
        sta->tx_num_mpdu_fail += status->retry_count;
 
        /* Update PID controller state. */
-       if (time_after(jiffies, spinfo->last_sample + RC_PID_INTERVAL))
+       period = (HZ * pinfo->sampling_period + 500) / 1000;
+       if (!period)
+               period = 1;
+       if (time_after(jiffies, spinfo->last_sample + period))
                rate_control_pid_sample(pinfo, local, sta);
 
+ignore:
        sta_info_put(sta);
 }
 
@@ -298,22 +304,36 @@ static void rate_control_pid_get_rate(void *priv, struct net_device *dev,
 {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       struct ieee80211_sub_if_data *sdata;
        struct sta_info *sta;
        int rateidx;
+       u16 fc;
 
        sta = sta_info_get(local, hdr->addr1);
 
-       if (!sta) {
-               sel->rate = rate_lowest(local, mode, NULL);
-               sta_info_put(sta);
+       /* Send management frames and broadcast/multicast data using lowest
+        * rate. */
+       fc = le16_to_cpu(hdr->frame_control);
+       if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
+           is_multicast_ether_addr(hdr->addr1) || !sta) {
+               sel->rate = rate_lowest(local, mode, sta);
+               if (sta)
+                       sta_info_put(sta);
                return;
        }
 
+       /* If a forced rate is in effect, select it. */
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       if (sdata->bss && sdata->bss->force_unicast_rateidx > -1)
+               sta->txrate = sdata->bss->force_unicast_rateidx;
+
        rateidx = sta->txrate;
 
        if (rateidx >= mode->num_rates)
                rateidx = mode->num_rates - 1;
 
+       sta->last_txrate = rateidx;
+
        sta_info_put(sta);
 
        sel->rate = &mode->rates[rateidx];
@@ -343,6 +363,9 @@ static void *rate_control_pid_alloc(struct ieee80211_local *local)
        struct ieee80211_hw_mode *mode;
        int i, j, tmp;
        bool s;
+#ifdef CONFIG_MAC80211_DEBUGFS
+       struct rc_pid_debugfs_entries *de;
+#endif
 
        pinfo = kmalloc(sizeof(*pinfo), GFP_ATOMIC);
        if (!pinfo)
@@ -363,10 +386,10 @@ static void *rate_control_pid_alloc(struct ieee80211_local *local)
        for (i = 0; i < mode->num_rates; i++) {
                rinfo[i].index = i;
                rinfo[i].rev_index = i;
-               if (RC_PID_FAST_START)
+               if (pinfo->fast_start)
                        rinfo[i].diff = 0;
                else
-                       rinfo[i].diff = i * RC_PID_NORM_OFFSET;
+                       rinfo[i].diff = i * pinfo->norm_offset;
        }
        for (i = 1; i < mode->num_rates; i++) {
                s = 0;
@@ -385,18 +408,72 @@ static void *rate_control_pid_alloc(struct ieee80211_local *local)
        }
 
        pinfo->target = RC_PID_TARGET_PF;
+       pinfo->sampling_period = RC_PID_INTERVAL;
        pinfo->coeff_p = RC_PID_COEFF_P;
        pinfo->coeff_i = RC_PID_COEFF_I;
        pinfo->coeff_d = RC_PID_COEFF_D;
+       pinfo->smoothing_shift = RC_PID_SMOOTHING_SHIFT;
+       pinfo->sharpen_factor = RC_PID_SHARPENING_FACTOR;
+       pinfo->sharpen_duration = RC_PID_SHARPENING_DURATION;
+       pinfo->norm_offset = RC_PID_NORM_OFFSET;
+       pinfo->fast_start = RC_PID_FAST_START;
        pinfo->rinfo = rinfo;
        pinfo->oldrate = 0;
 
+#ifdef CONFIG_MAC80211_DEBUGFS
+       de = &pinfo->dentries;
+       de->dir = debugfs_create_dir("rc80211_pid",
+                                    local->hw.wiphy->debugfsdir);
+       de->target = debugfs_create_u32("target_pf", S_IRUSR | S_IWUSR,
+                                       de->dir, &pinfo->target);
+       de->sampling_period = debugfs_create_u32("sampling_period",
+                                                S_IRUSR | S_IWUSR, de->dir,
+                                                &pinfo->sampling_period);
+       de->coeff_p = debugfs_create_u32("coeff_p", S_IRUSR | S_IWUSR,
+                                        de->dir, &pinfo->coeff_p);
+       de->coeff_i = debugfs_create_u32("coeff_i", S_IRUSR | S_IWUSR,
+                                        de->dir, &pinfo->coeff_i);
+       de->coeff_d = debugfs_create_u32("coeff_d", S_IRUSR | S_IWUSR,
+                                        de->dir, &pinfo->coeff_d);
+       de->smoothing_shift = debugfs_create_u32("smoothing_shift",
+                                                S_IRUSR | S_IWUSR, de->dir,
+                                                &pinfo->smoothing_shift);
+       de->sharpen_factor = debugfs_create_u32("sharpen_factor",
+                                              S_IRUSR | S_IWUSR, de->dir,
+                                              &pinfo->sharpen_factor);
+       de->sharpen_duration = debugfs_create_u32("sharpen_duration",
+                                                 S_IRUSR | S_IWUSR, de->dir,
+                                                 &pinfo->sharpen_duration);
+       de->norm_offset = debugfs_create_u32("norm_offset",
+                                            S_IRUSR | S_IWUSR, de->dir,
+                                            &pinfo->norm_offset);
+       de->fast_start = debugfs_create_bool("fast_start",
+                                            S_IRUSR | S_IWUSR, de->dir,
+                                            &pinfo->fast_start);
+#endif
+
        return pinfo;
 }
 
 static void rate_control_pid_free(void *priv)
 {
        struct rc_pid_info *pinfo = priv;
+#ifdef CONFIG_MAC80211_DEBUGFS
+       struct rc_pid_debugfs_entries *de = &pinfo->dentries;
+
+       debugfs_remove(de->fast_start);
+       debugfs_remove(de->norm_offset);
+       debugfs_remove(de->sharpen_duration);
+       debugfs_remove(de->sharpen_factor);
+       debugfs_remove(de->smoothing_shift);
+       debugfs_remove(de->coeff_d);
+       debugfs_remove(de->coeff_i);
+       debugfs_remove(de->coeff_p);
+       debugfs_remove(de->sampling_period);
+       debugfs_remove(de->target);
+       debugfs_remove(de->dir);
+#endif
+
        kfree(pinfo->rinfo);
        kfree(pinfo);
 }
@@ -413,6 +490,8 @@ static void *rate_control_pid_alloc_sta(void *priv, gfp_t gfp)
        if (spinfo == NULL)
                return NULL;
 
+       spinfo->last_sample = jiffies;
+
 #ifdef CONFIG_MAC80211_DEBUGFS
        spin_lock_init(&spinfo->events.lock);
        init_waitqueue_head(&spinfo->events.waitqueue);
@@ -427,7 +506,7 @@ static void rate_control_pid_free_sta(void *priv, void *priv_sta)
        kfree(spinfo);
 }
 
-struct rate_control_ops mac80211_rcpid = {
+static struct rate_control_ops mac80211_rcpid = {
        .name = "pid",
        .tx_status = rate_control_pid_tx_status,
        .get_rate = rate_control_pid_get_rate,
@@ -442,3 +521,23 @@ struct rate_control_ops mac80211_rcpid = {
        .remove_sta_debugfs = rate_control_pid_remove_sta_debugfs,
 #endif
 };
+
+MODULE_DESCRIPTION("PID controller based rate control algorithm");
+MODULE_AUTHOR("Stefano Brivio");
+MODULE_AUTHOR("Mattias Nissler");
+MODULE_LICENSE("GPL");
+
+int __init rc80211_pid_init(void)
+{
+       return ieee80211_rate_control_register(&mac80211_rcpid);
+}
+
+void rc80211_pid_exit(void)
+{
+       ieee80211_rate_control_unregister(&mac80211_rcpid);
+}
+
+#ifdef CONFIG_MAC80211_RC_PID_MODULE
+module_init(rc80211_pid_init);
+module_exit(rc80211_pid_exit);
+#endif