提交 20edb50e 编写于 作者: J John W. Linville 提交者: Johannes Berg

mac80211: remove PID rate control

Minstrel has long since proven its worth.
Signed-off-by: NJohn W. Linville <linville@tuxdriver.com>
Signed-off-by: NJohannes Berg <johannes.berg@intel.com>
上级 7171511e
......@@ -5629,16 +5629,6 @@ F: Documentation/networking/mac80211-injection.txt
F: include/net/mac80211.h
F: net/mac80211/
MAC80211 PID RATE CONTROL
M: Stefano Brivio <stefano.brivio@polimi.it>
M: Mattias Nissler <mattias.nissler@gmx.de>
L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/en/developers/Documentation/mac80211/RateControl/PID
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git
S: Maintained
F: net/mac80211/rc80211_pid*
MACVLAN DRIVER
M: Patrick McHardy <kaber@trash.net>
L: netdev@vger.kernel.org
......
......@@ -19,14 +19,6 @@ if MAC80211 != n
config MAC80211_HAS_RC
bool
config MAC80211_RC_PID
bool "PID controller based rate control algorithm" if EXPERT
select MAC80211_HAS_RC
---help---
This option enables a TX rate control algorithm for
mac80211 that uses a PID controller to select the TX
rate.
config MAC80211_RC_MINSTREL
bool "Minstrel" if EXPERT
select MAC80211_HAS_RC
......@@ -51,14 +43,6 @@ choice
overridden through the ieee80211_default_rc_algo module
parameter if different algorithms are available.
config MAC80211_RC_DEFAULT_PID
bool "PID controller based rate control algorithm"
depends on MAC80211_RC_PID
---help---
Select the PID controller based rate control as the
default rate control algorithm. You should choose
this unless you know what you are doing.
config MAC80211_RC_DEFAULT_MINSTREL
bool "Minstrel"
depends on MAC80211_RC_MINSTREL
......@@ -72,7 +56,6 @@ config MAC80211_RC_DEFAULT
string
default "minstrel_ht" if MAC80211_RC_DEFAULT_MINSTREL && MAC80211_RC_MINSTREL_HT
default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL
default "pid" if MAC80211_RC_DEFAULT_PID
default ""
endif
......
......@@ -47,17 +47,12 @@ mac80211-$(CONFIG_PM) += pm.o
CFLAGS_trace.o := -I$(src)
# objects for PID algorithm
rc80211_pid-y := rc80211_pid_algo.o
rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o
rc80211_minstrel-y := rc80211_minstrel.o
rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o
rc80211_minstrel_ht-y := rc80211_minstrel_ht.o
rc80211_minstrel_ht-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_ht_debugfs.o
mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc80211_pid-y)
mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y)
mac80211-$(CONFIG_MAC80211_RC_MINSTREL_HT) += $(rc80211_minstrel_ht-y)
......
......@@ -1187,18 +1187,12 @@ static int __init ieee80211_init(void)
if (ret)
goto err_minstrel;
ret = rc80211_pid_init();
if (ret)
goto err_pid;
ret = ieee80211_iface_init();
if (ret)
goto err_netdev;
return 0;
err_netdev:
rc80211_pid_exit();
err_pid:
rc80211_minstrel_ht_exit();
err_minstrel:
rc80211_minstrel_exit();
......@@ -1208,7 +1202,6 @@ static int __init ieee80211_init(void)
static void __exit ieee80211_exit(void)
{
rc80211_pid_exit();
rc80211_minstrel_ht_exit();
rc80211_minstrel_exit();
......
......@@ -143,19 +143,6 @@ void rate_control_deinitialize(struct ieee80211_local *local);
/* Rate control algorithms */
#ifdef CONFIG_MAC80211_RC_PID
int rc80211_pid_init(void);
void rc80211_pid_exit(void);
#else
static inline int rc80211_pid_init(void)
{
return 0;
}
static inline void rc80211_pid_exit(void)
{
}
#endif
#ifdef CONFIG_MAC80211_RC_MINSTREL
int rc80211_minstrel_init(void);
void rc80211_minstrel_exit(void);
......
/*
* Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
* Copyright 2007, 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
* published by the Free Software Foundation.
*/
#ifndef RC80211_PID_H
#define RC80211_PID_H
/* Sampling period for measuring percentage of failed frames in ms. */
#define RC_PID_INTERVAL 125
/* Exponential averaging smoothness (used for I part of PID controller) */
#define RC_PID_SMOOTHING_SHIFT 3
#define RC_PID_SMOOTHING (1 << RC_PID_SMOOTHING_SHIFT)
/* Sharpening factor (used for D part of PID controller) */
#define RC_PID_SHARPENING_FACTOR 0
#define RC_PID_SHARPENING_DURATION 0
/* Fixed point arithmetic shifting amount. */
#define RC_PID_ARITH_SHIFT 8
/* Proportional PID component coefficient. */
#define RC_PID_COEFF_P 15
/* Integral PID component coefficient. */
#define RC_PID_COEFF_I 9
/* Derivative PID component coefficient. */
#define RC_PID_COEFF_D 15
/* Target failed frames rate for the PID controller. NB: This effectively gives
* maximum failed frames percentage we're willing to accept. If the wireless
* link quality is good, the controller will fail to adjust failed frames
* percentage to the target. This is intentional.
*/
#define RC_PID_TARGET_PF 14
/* Rate behaviour normalization quantity over time. */
#define RC_PID_NORM_OFFSET 3
/* Push high rates right after loading. */
#define RC_PID_FAST_START 0
/* Arithmetic right shift for positive and negative values for ISO C. */
#define RC_PID_DO_ARITH_RIGHT_SHIFT(x, y) \
((x) < 0 ? -((-(x)) >> (y)) : (x) >> (y))
enum rc_pid_event_type {
RC_PID_EVENT_TYPE_TX_STATUS,
RC_PID_EVENT_TYPE_RATE_CHANGE,
RC_PID_EVENT_TYPE_TX_RATE,
RC_PID_EVENT_TYPE_PF_SAMPLE,
};
union rc_pid_event_data {
/* RC_PID_EVENT_TX_STATUS */
struct {
u32 flags;
struct ieee80211_tx_info tx_status;
};
/* RC_PID_EVENT_TYPE_RATE_CHANGE */
/* RC_PID_EVENT_TYPE_TX_RATE */
struct {
int index;
int rate;
};
/* RC_PID_EVENT_TYPE_PF_SAMPLE */
struct {
s32 pf_sample;
s32 prop_err;
s32 int_err;
s32 der_err;
};
};
struct rc_pid_event {
/* The time when the event occurred */
unsigned long timestamp;
/* Event ID number */
unsigned int id;
/* Type of event */
enum rc_pid_event_type type;
/* type specific data */
union rc_pid_event_data data;
};
/* Size of the event ring buffer. */
#define RC_PID_EVENT_RING_SIZE 32
struct rc_pid_event_buffer {
/* Counter that generates event IDs */
unsigned int ev_count;
/* Ring buffer of events */
struct rc_pid_event ring[RC_PID_EVENT_RING_SIZE];
/* Index to the entry in events_buf to be reused */
unsigned int next_entry;
/* Lock that guards against concurrent access to this buffer struct */
spinlock_t lock;
/* Wait queue for poll/select and blocking I/O */
wait_queue_head_t waitqueue;
};
struct rc_pid_events_file_info {
/* The event buffer we read */
struct rc_pid_event_buffer *events;
/* The entry we have should read next */
unsigned int next_entry;
};
/**
* struct rc_pid_debugfs_entries - tunable parameters
*
* Algorithm parameters, tunable via debugfs.
* @target: target percentage for failed frames
* @sampling_period: error sampling interval in milliseconds
* @coeff_p: absolute value of the proportional coefficient
* @coeff_i: absolute value of the integral coefficient
* @coeff_d: absolute value of the derivative coefficient
* @smoothing_shift: absolute value of the integral smoothing factor (i.e.
* amount of smoothing introduced by the exponential moving average)
* @sharpen_factor: absolute value of the derivative sharpening factor (i.e.
* amount of emphasis given to the derivative term after low activity
* events)
* @sharpen_duration: duration of the sharpening effect after the detected low
* activity event, relative to sampling_period
* @norm_offset: amount of normalization periodically performed on the learnt
* rate behaviour values (lower means we should trust more what we learnt
* about behaviour of rates, higher means we should trust more the natural
* ordering of rates)
*/
struct rc_pid_debugfs_entries {
struct dentry *target;
struct dentry *sampling_period;
struct dentry *coeff_p;
struct dentry *coeff_i;
struct dentry *coeff_d;
struct dentry *smoothing_shift;
struct dentry *sharpen_factor;
struct dentry *sharpen_duration;
struct dentry *norm_offset;
};
void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf,
struct ieee80211_tx_info *stat);
void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf,
int index, int rate);
void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf,
int index, int rate);
void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf,
s32 pf_sample, s32 prop_err,
s32 int_err, s32 der_err);
void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta,
struct dentry *dir);
void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta);
struct rc_pid_sta_info {
unsigned long last_change;
unsigned long last_sample;
u32 tx_num_failed;
u32 tx_num_xmit;
int txrate_idx;
/* Average failed frames percentage error (i.e. actual vs. target
* percentage), scaled by RC_PID_SMOOTHING. This value is computed
* using using an exponential weighted average technique:
*
* (RC_PID_SMOOTHING - 1) * err_avg_old + err
* err_avg = ------------------------------------------
* RC_PID_SMOOTHING
*
* where err_avg is the new approximation, err_avg_old the previous one
* and err is the error w.r.t. to the current failed frames percentage
* sample. Note that the bigger RC_PID_SMOOTHING the more weight is
* given to the previous estimate, resulting in smoother behavior (i.e.
* corresponding to a longer integration window).
*
* For computation, we actually don't use the above formula, but this
* one:
*
* err_avg_scaled = err_avg_old_scaled - err_avg_old + err
*
* where:
* err_avg_scaled = err * RC_PID_SMOOTHING
* err_avg_old_scaled = err_avg_old * RC_PID_SMOOTHING
*
* This avoids floating point numbers and the per_failed_old value can
* easily be obtained by shifting per_failed_old_scaled right by
* RC_PID_SMOOTHING_SHIFT.
*/
s32 err_avg_sc;
/* Last framed failes percentage sample. */
u32 last_pf;
/* Sharpening needed. */
u8 sharp_cnt;
#ifdef CONFIG_MAC80211_DEBUGFS
/* Event buffer */
struct rc_pid_event_buffer events;
/* Events debugfs file entry */
struct dentry *events_entry;
#endif
};
/* Algorithm parameters. We keep them on a per-algorithm approach, so they can
* be tuned individually for each interface.
*/
struct rc_pid_rateinfo {
/* Map sorted rates to rates in ieee80211_hw_mode. */
int index;
/* Map rates in ieee80211_hw_mode to sorted rates. */
int rev_index;
/* Did we do any measurement on this rate? */
bool valid;
/* Comparison with the lowest rate. */
int diff;
};
struct rc_pid_info {
/* The failed frames percentage target. */
unsigned int target;
/* Rate at which failed frames percentage is sampled in 0.001s. */
unsigned int sampling_period;
/* P, I and D coefficients. */
int coeff_p;
int coeff_i;
int coeff_d;
/* Exponential averaging shift. */
unsigned int smoothing_shift;
/* Sharpening factor and duration. */
unsigned int sharpen_factor;
unsigned int sharpen_duration;
/* Normalization offset. */
unsigned int norm_offset;
/* Rates information. */
struct rc_pid_rateinfo *rinfo;
/* Index of the last used rate. */
int oldrate;
#ifdef CONFIG_MAC80211_DEBUGFS
/* Debugfs entries created for the parameters above. */
struct rc_pid_debugfs_entries dentries;
#endif
};
#endif /* RC80211_PID_H */
/*
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2005, Devicescape Software, Inc.
* Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
* 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
* published by the Free Software Foundation.
*/
#include <linux/netdevice.h>
#include <linux/types.h>
#include <linux/skbuff.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <net/mac80211.h>
#include "rate.h"
#include "mesh.h"
#include "rc80211_pid.h"
/* This is an implementation of a TX rate control algorithm that uses a PID
* controller. Given a target failed frames rate, the controller decides about
* TX rate changes to meet the target failed frames rate.
*
* The controller basically computes the following:
*
* adj = CP * err + CI * err_avg + CD * (err - last_err) * (1 + sharpening)
*
* where
* adj adjustment value that is used to switch TX rate (see below)
* err current error: target vs. current failed frames percentage
* last_err last error
* err_avg average (i.e. poor man's integral) of recent errors
* sharpening non-zero when fast response is needed (i.e. right after
* association or no frames sent for a long time), heading
* to zero over time
* CP Proportional coefficient
* CI Integral coefficient
* CD Derivative coefficient
*
* CP, CI, CD are subject to careful tuning.
*
* The integral component uses a exponential moving average approach instead of
* an actual sliding window. The advantage is that we don't need to keep an
* array of the last N error values and computation is easier.
*
* Once we have the adj value, we map it to a rate by means of a learning
* algorithm. This algorithm keeps the state of the percentual failed frames
* difference between rates. The behaviour of the lowest available rate is kept
* as a reference value, and every time we switch between two rates, we compute
* the difference between the failed frames each rate exhibited. By doing so,
* we compare behaviours which different rates exhibited in adjacent timeslices,
* thus the comparison is minimally affected by external conditions. This
* difference gets propagated to the whole set of measurements, so that the
* reference is always the same. Periodically, we normalize this set so that
* recent events weigh the most. By comparing the adj value with this set, we
* avoid pejorative switches to lower rates and allow for switches to higher
* rates if they behaved well.
*
* Note that for the computations we use a fixed-point representation to avoid
* floating point arithmetic. Hence, all values are shifted left by
* RC_PID_ARITH_SHIFT.
*/
/* 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_supported_band *sband,
struct ieee80211_sta *sta,
struct rc_pid_sta_info *spinfo, int adj,
struct rc_pid_rateinfo *rinfo)
{
int cur_sorted, new_sorted, probe, tmp, n_bitrates, band;
int cur = spinfo->txrate_idx;
band = sband->band;
n_bitrates = sband->n_bitrates;
/* Map passed arguments to sorted values. */
cur_sorted = rinfo[cur].rev_index;
new_sorted = cur_sorted + adj;
/* 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;
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, band, 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, band, rinfo[probe].index))
tmp = probe;
}
/* Fit the rate found to the nearest supported rate. */
do {
if (rate_supported(sta, band, rinfo[tmp].index)) {
spinfo->txrate_idx = 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(&spinfo->events,
spinfo->txrate_idx,
sband->bitrates[spinfo->txrate_idx].bitrate);
#endif
}
/* Normalize the failed frames per-rate differences. */
static void rate_control_pid_normalize(struct rc_pid_info *pinfo, int l)
{
int i, norm_offset = pinfo->norm_offset;
struct rc_pid_rateinfo *r = pinfo->rinfo;
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 + norm_offset)
r[i + 1].diff -= norm_offset;
else if (r[i + 1].diff <= r[i].diff)
r[i + 1].diff += norm_offset;
}
static void rate_control_pid_sample(struct rc_pid_info *pinfo,
struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta,
struct rc_pid_sta_info *spinfo)
{
struct rc_pid_rateinfo *rinfo = pinfo->rinfo;
u32 pf;
s32 err_avg;
u32 err_prop;
u32 err_int;
u32 err_der;
int adj, i, j, tmp;
unsigned long period;
/* In case nothing happened during the previous control interval, turn
* the sharpening factor on. */
period = msecs_to_jiffies(pinfo->sampling_period);
if (jiffies - spinfo->last_sample > 2 * period)
spinfo->sharp_cnt = pinfo->sharpen_duration;
spinfo->last_sample = jiffies;
/* This should never happen, but in case, we assume the old sample is
* still a good measurement and copy it. */
if (unlikely(spinfo->tx_num_xmit == 0))
pf = spinfo->last_pf;
else
pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit;
spinfo->tx_num_xmit = 0;
spinfo->tx_num_failed = 0;
/* If we just switched rate, update the rate behaviour info. */
if (pinfo->oldrate != spinfo->txrate_idx) {
i = rinfo[pinfo->oldrate].rev_index;
j = rinfo[spinfo->txrate_idx].rev_index;
tmp = (pf - spinfo->last_pf);
tmp = RC_PID_DO_ARITH_RIGHT_SHIFT(tmp, RC_PID_ARITH_SHIFT);
rinfo[j].diff = rinfo[i].diff + tmp;
pinfo->oldrate = spinfo->txrate_idx;
}
rate_control_pid_normalize(pinfo, sband->n_bitrates);
/* Compute the proportional, integral and derivative errors. */
err_prop = (pinfo->target - pf) << RC_PID_ARITH_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 >> pinfo->smoothing_shift;
err_der = (pf - spinfo->last_pf) *
(1 + pinfo->sharpen_factor * spinfo->sharp_cnt);
spinfo->last_pf = pf;
if (spinfo->sharp_cnt)
spinfo->sharp_cnt--;
#ifdef CONFIG_MAC80211_DEBUGFS
rate_control_pid_event_pf_sample(&spinfo->events, pf, err_prop, err_int,
err_der);
#endif
/* Compute the controller output. */
adj = (err_prop * pinfo->coeff_p + err_int * pinfo->coeff_i
+ err_der * pinfo->coeff_d);
adj = RC_PID_DO_ARITH_RIGHT_SHIFT(adj, 2 * RC_PID_ARITH_SHIFT);
/* Change rate. */
if (adj)
rate_control_pid_adjust_rate(sband, sta, spinfo, adj, rinfo);
}
static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
struct sk_buff *skb)
{
struct rc_pid_info *pinfo = priv;
struct rc_pid_sta_info *spinfo = priv_sta;
unsigned long period;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
if (!spinfo)
return;
/* Ignore all frames that were sent with a different rate than the rate
* we currently advise mac80211 to use. */
if (info->status.rates[0].idx != spinfo->txrate_idx)
return;
spinfo->tx_num_xmit++;
#ifdef CONFIG_MAC80211_DEBUGFS
rate_control_pid_event_tx_status(&spinfo->events, info);
#endif
/* We count frames that totally failed to be transmitted as two bad
* frames, those that made it out but had some retries as one good and
* one bad frame. */
if (!(info->flags & IEEE80211_TX_STAT_ACK)) {
spinfo->tx_num_failed += 2;
spinfo->tx_num_xmit++;
} else if (info->status.rates[0].count > 1) {
spinfo->tx_num_failed++;
spinfo->tx_num_xmit++;
}
/* Update PID controller state. */
period = msecs_to_jiffies(pinfo->sampling_period);
if (time_after(jiffies, spinfo->last_sample + period))
rate_control_pid_sample(pinfo, sband, sta, spinfo);
}
static void
rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
void *priv_sta,
struct ieee80211_tx_rate_control *txrc)
{
struct sk_buff *skb = txrc->skb;
struct ieee80211_supported_band *sband = txrc->sband;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct rc_pid_sta_info *spinfo = priv_sta;
int rateidx;
if (txrc->rts)
info->control.rates[0].count =
txrc->hw->conf.long_frame_max_tx_count;
else
info->control.rates[0].count =
txrc->hw->conf.short_frame_max_tx_count;
/* Send management frames and NO_ACK data using lowest rate. */
if (rate_control_send_low(sta, priv_sta, txrc))
return;
rateidx = spinfo->txrate_idx;
if (rateidx >= sband->n_bitrates)
rateidx = sband->n_bitrates - 1;
info->control.rates[0].idx = rateidx;
#ifdef CONFIG_MAC80211_DEBUGFS
rate_control_pid_event_tx_rate(&spinfo->events,
rateidx, sband->bitrates[rateidx].bitrate);
#endif
}
static void
rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,
struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta)
{
struct rc_pid_sta_info *spinfo = priv_sta;
struct rc_pid_info *pinfo = priv;
struct rc_pid_rateinfo *rinfo = pinfo->rinfo;
int i, j, tmp;
bool s;
/* TODO: This routine should consider using RSSI from previous packets
* as we need to have IEEE 802.1X auth succeed immediately after assoc..
* Until that method is implemented, we will use the lowest supported
* rate as a workaround. */
/* Sort the rates. This is optimized for the most common case (i.e.
* almost-sorted CCK+OFDM rates). Kind of bubble-sort with reversed
* mapping too. */
for (i = 0; i < sband->n_bitrates; i++) {
rinfo[i].index = i;
rinfo[i].rev_index = i;
if (RC_PID_FAST_START)
rinfo[i].diff = 0;
else
rinfo[i].diff = i * pinfo->norm_offset;
}
for (i = 1; i < sband->n_bitrates; i++) {
s = false;
for (j = 0; j < sband->n_bitrates - i; j++)
if (unlikely(sband->bitrates[rinfo[j].index].bitrate >
sband->bitrates[rinfo[j + 1].index].bitrate)) {
tmp = rinfo[j].index;
rinfo[j].index = rinfo[j + 1].index;
rinfo[j + 1].index = tmp;
rinfo[rinfo[j].index].rev_index = j;
rinfo[rinfo[j + 1].index].rev_index = j + 1;
s = true;
}
if (!s)
break;
}
spinfo->txrate_idx = rate_lowest_index(sband, sta);
}
static void *rate_control_pid_alloc(struct ieee80211_hw *hw,
struct dentry *debugfsdir)
{
struct rc_pid_info *pinfo;
struct rc_pid_rateinfo *rinfo;
struct ieee80211_supported_band *sband;
int i, max_rates = 0;
#ifdef CONFIG_MAC80211_DEBUGFS
struct rc_pid_debugfs_entries *de;
#endif
pinfo = kmalloc(sizeof(*pinfo), GFP_ATOMIC);
if (!pinfo)
return NULL;
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
sband = hw->wiphy->bands[i];
if (sband && sband->n_bitrates > max_rates)
max_rates = sband->n_bitrates;
}
rinfo = kmalloc(sizeof(*rinfo) * max_rates, GFP_ATOMIC);
if (!rinfo) {
kfree(pinfo);
return NULL;
}
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->rinfo = rinfo;
pinfo->oldrate = 0;
#ifdef CONFIG_MAC80211_DEBUGFS
de = &pinfo->dentries;
de->target = debugfs_create_u32("target_pf", S_IRUSR | S_IWUSR,
debugfsdir, &pinfo->target);
de->sampling_period = debugfs_create_u32("sampling_period",
S_IRUSR | S_IWUSR, debugfsdir,
&pinfo->sampling_period);
de->coeff_p = debugfs_create_u32("coeff_p", S_IRUSR | S_IWUSR,
debugfsdir, (u32 *)&pinfo->coeff_p);
de->coeff_i = debugfs_create_u32("coeff_i", S_IRUSR | S_IWUSR,
debugfsdir, (u32 *)&pinfo->coeff_i);
de->coeff_d = debugfs_create_u32("coeff_d", S_IRUSR | S_IWUSR,
debugfsdir, (u32 *)&pinfo->coeff_d);
de->smoothing_shift = debugfs_create_u32("smoothing_shift",
S_IRUSR | S_IWUSR, debugfsdir,
&pinfo->smoothing_shift);
de->sharpen_factor = debugfs_create_u32("sharpen_factor",
S_IRUSR | S_IWUSR, debugfsdir,
&pinfo->sharpen_factor);
de->sharpen_duration = debugfs_create_u32("sharpen_duration",
S_IRUSR | S_IWUSR, debugfsdir,
&pinfo->sharpen_duration);
de->norm_offset = debugfs_create_u32("norm_offset",
S_IRUSR | S_IWUSR, debugfsdir,
&pinfo->norm_offset);
#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->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);
#endif
kfree(pinfo->rinfo);
kfree(pinfo);
}
static void *rate_control_pid_alloc_sta(void *priv, struct ieee80211_sta *sta,
gfp_t gfp)
{
struct rc_pid_sta_info *spinfo;
spinfo = kzalloc(sizeof(*spinfo), 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);
#endif
return spinfo;
}
static void rate_control_pid_free_sta(void *priv, struct ieee80211_sta *sta,
void *priv_sta)
{
kfree(priv_sta);
}
static const struct rate_control_ops mac80211_rcpid = {
.name = "pid",
.tx_status = rate_control_pid_tx_status,
.get_rate = rate_control_pid_get_rate,
.rate_init = rate_control_pid_rate_init,
.alloc = rate_control_pid_alloc,
.free = rate_control_pid_free,
.alloc_sta = rate_control_pid_alloc_sta,
.free_sta = rate_control_pid_free_sta,
#ifdef CONFIG_MAC80211_DEBUGFS
.add_sta_debugfs = rate_control_pid_add_sta_debugfs,
.remove_sta_debugfs = rate_control_pid_remove_sta_debugfs,
#endif
};
int __init rc80211_pid_init(void)
{
return ieee80211_rate_control_register(&mac80211_rcpid);
}
void rc80211_pid_exit(void)
{
ieee80211_rate_control_unregister(&mac80211_rcpid);
}
/*
* Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
*
* 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
* published by the Free Software Foundation.
*/
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/poll.h>
#include <linux/netdevice.h>
#include <linux/types.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/export.h>
#include <net/mac80211.h>
#include "rate.h"
#include "rc80211_pid.h"
static void rate_control_pid_event(struct rc_pid_event_buffer *buf,
enum rc_pid_event_type type,
union rc_pid_event_data *data)
{
struct rc_pid_event *ev;
unsigned long status;
spin_lock_irqsave(&buf->lock, status);
ev = &(buf->ring[buf->next_entry]);
buf->next_entry = (buf->next_entry + 1) % RC_PID_EVENT_RING_SIZE;
ev->timestamp = jiffies;
ev->id = buf->ev_count++;
ev->type = type;
ev->data = *data;
spin_unlock_irqrestore(&buf->lock, status);
wake_up_all(&buf->waitqueue);
}
void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf,
struct ieee80211_tx_info *stat)
{
union rc_pid_event_data evd;
evd.flags = stat->flags;
memcpy(&evd.tx_status, stat, sizeof(struct ieee80211_tx_info));
rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_STATUS, &evd);
}
void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf,
int index, int rate)
{
union rc_pid_event_data evd;
evd.index = index;
evd.rate = rate;
rate_control_pid_event(buf, RC_PID_EVENT_TYPE_RATE_CHANGE, &evd);
}
void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf,
int index, int rate)
{
union rc_pid_event_data evd;
evd.index = index;
evd.rate = rate;
rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_RATE, &evd);
}
void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf,
s32 pf_sample, s32 prop_err,
s32 int_err, s32 der_err)
{
union rc_pid_event_data evd;
evd.pf_sample = pf_sample;
evd.prop_err = prop_err;
evd.int_err = int_err;
evd.der_err = der_err;
rate_control_pid_event(buf, RC_PID_EVENT_TYPE_PF_SAMPLE, &evd);
}
static int rate_control_pid_events_open(struct inode *inode, struct file *file)
{
struct rc_pid_sta_info *sinfo = inode->i_private;
struct rc_pid_event_buffer *events = &sinfo->events;
struct rc_pid_events_file_info *file_info;
unsigned long status;
/* Allocate a state struct */
file_info = kmalloc(sizeof(*file_info), GFP_KERNEL);
if (file_info == NULL)
return -ENOMEM;
spin_lock_irqsave(&events->lock, status);
file_info->next_entry = events->next_entry;
file_info->events = events;
spin_unlock_irqrestore(&events->lock, status);
file->private_data = file_info;
return 0;
}
static int rate_control_pid_events_release(struct inode *inode,
struct file *file)
{
struct rc_pid_events_file_info *file_info = file->private_data;
kfree(file_info);
return 0;
}
static unsigned int rate_control_pid_events_poll(struct file *file,
poll_table *wait)
{
struct rc_pid_events_file_info *file_info = file->private_data;
poll_wait(file, &file_info->events->waitqueue, wait);
return POLLIN | POLLRDNORM;
}
#define RC_PID_PRINT_BUF_SIZE 64
static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf,
size_t length, loff_t *offset)
{
struct rc_pid_events_file_info *file_info = file->private_data;
struct rc_pid_event_buffer *events = file_info->events;
struct rc_pid_event *ev;
char pb[RC_PID_PRINT_BUF_SIZE];
int ret;
int p;
unsigned long status;
/* Check if there is something to read. */
if (events->next_entry == file_info->next_entry) {
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
/* Wait */
ret = wait_event_interruptible(events->waitqueue,
events->next_entry != file_info->next_entry);
if (ret)
return ret;
}
/* Write out one event per call. I don't care whether it's a little
* inefficient, this is debugging code anyway. */
spin_lock_irqsave(&events->lock, status);
/* Get an event */
ev = &(events->ring[file_info->next_entry]);
file_info->next_entry = (file_info->next_entry + 1) %
RC_PID_EVENT_RING_SIZE;
/* Print information about the event. Note that userspace needs to
* provide large enough buffers. */
length = length < RC_PID_PRINT_BUF_SIZE ?
length : RC_PID_PRINT_BUF_SIZE;
p = scnprintf(pb, length, "%u %lu ", ev->id, ev->timestamp);
switch (ev->type) {
case RC_PID_EVENT_TYPE_TX_STATUS:
p += scnprintf(pb + p, length - p, "tx_status %u %u",
!(ev->data.flags & IEEE80211_TX_STAT_ACK),
ev->data.tx_status.status.rates[0].idx);
break;
case RC_PID_EVENT_TYPE_RATE_CHANGE:
p += scnprintf(pb + p, length - p, "rate_change %d %d",
ev->data.index, ev->data.rate);
break;
case RC_PID_EVENT_TYPE_TX_RATE:
p += scnprintf(pb + p, length - p, "tx_rate %d %d",
ev->data.index, ev->data.rate);
break;
case RC_PID_EVENT_TYPE_PF_SAMPLE:
p += scnprintf(pb + p, length - p,
"pf_sample %d %d %d %d",
ev->data.pf_sample, ev->data.prop_err,
ev->data.int_err, ev->data.der_err);
break;
}
p += scnprintf(pb + p, length - p, "\n");
spin_unlock_irqrestore(&events->lock, status);
if (copy_to_user(buf, pb, p))
return -EFAULT;
return p;
}
#undef RC_PID_PRINT_BUF_SIZE
static const struct file_operations rc_pid_fop_events = {
.owner = THIS_MODULE,
.read = rate_control_pid_events_read,
.poll = rate_control_pid_events_poll,
.open = rate_control_pid_events_open,
.release = rate_control_pid_events_release,
.llseek = noop_llseek,
};
void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta,
struct dentry *dir)
{
struct rc_pid_sta_info *spinfo = priv_sta;
spinfo->events_entry = debugfs_create_file("rc_pid_events", S_IRUGO,
dir, spinfo,
&rc_pid_fop_events);
}
void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta)
{
struct rc_pid_sta_info *spinfo = priv_sta;
debugfs_remove(spinfo->events_entry);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册