2013-08-01 15:39:56

by Sylvain Roger Rieunier

[permalink] [raw]
Subject: [[PATCHv2]rt2800usb: ampdu len] rt2800usb: update aggregation len

From: Sylvain Roger Rieunier <[email protected]>

I tried to read periodically TX_AGG_CNT registers on the rt2800usb driver.
I'had made USB synchronous and asynchronous read. But it's only reduces the
bandwidth.
Does anyone have an idea?


Signed-off-by: Sylvain ROGER RIEUNIER <[email protected]>
---
drivers/net/wireless/rt2x00/rt2800lib.c | 69 ++++++++++++++++++++++
drivers/net/wireless/rt2x00/rt2800usb.c | 92 +++++++++++++++++++++++++++++
drivers/net/wireless/rt2x00/rt2x00.h | 28 +++++++++
drivers/net/wireless/rt2x00/rt2x00debug.c | 66 +++++++++++++++++++++
drivers/net/wireless/rt2x00/rt2x00dev.c | 2 +
drivers/net/wireless/rt2x00/rt2x00usb.c | 2 +
6 files changed, 259 insertions(+)

diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index dedc3d4..0db45fb 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -1813,6 +1813,75 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
}
EXPORT_SYMBOL_GPL(rt2800_config_ant);

+void rt2800_update_aggr_stats(struct rt2x00_dev *rt2x00dev)
+{
+ u32 reg;
+ u32 all;
+ int i;
+
+ rt2800_register_read(rt2x00dev,TX_AGG_CNT, &reg);
+ rt2x00dev->aggr_stats.no_aggr =
+ rt2x00_get_field32(reg,TX_AGG_CNT_NON_AGG_TX_COUNT);
+ rt2x00dev->aggr_stats.all_aggr =
+ rt2x00_get_field32(reg,TX_AGG_CNT_AGG_TX_COUNT);
+
+ rt2800_register_read(rt2x00dev,TX_AGG_CNT0, &reg);
+ rt2x00dev->aggr_stats.ampduCount[0] =
+ rt2x00_get_field32(reg,TX_AGG_CNT0_AGG_SIZE_1_COUNT);
+ rt2x00dev->aggr_stats.ampduCount[1] =
+ rt2x00_get_field32(reg,TX_AGG_CNT0_AGG_SIZE_2_COUNT);
+
+ rt2800_register_read(rt2x00dev,TX_AGG_CNT1, &reg);
+ rt2x00dev->aggr_stats.ampduCount[2] =
+ rt2x00_get_field32(reg,TX_AGG_CNT1_AGG_SIZE_3_COUNT);
+ rt2x00dev->aggr_stats.ampduCount[3] =
+ rt2x00_get_field32(reg,TX_AGG_CNT1_AGG_SIZE_4_COUNT);
+
+ rt2800_register_read(rt2x00dev,TX_AGG_CNT2, &reg);
+ rt2x00dev->aggr_stats.ampduCount[4] =
+ rt2x00_get_field32(reg,TX_AGG_CNT2_AGG_SIZE_5_COUNT);
+ rt2x00dev->aggr_stats.ampduCount[5] =
+ rt2x00_get_field32(reg,TX_AGG_CNT2_AGG_SIZE_6_COUNT);
+
+ rt2800_register_read(rt2x00dev,TX_AGG_CNT3, &reg);
+ rt2x00dev->aggr_stats.ampduCount[6] =
+ rt2x00_get_field32(reg,TX_AGG_CNT3_AGG_SIZE_7_COUNT);
+ rt2x00dev->aggr_stats.ampduCount[7] =
+ rt2x00_get_field32(reg,TX_AGG_CNT3_AGG_SIZE_8_COUNT);
+
+ rt2800_register_read(rt2x00dev,TX_AGG_CNT4, &reg);
+ rt2x00dev->aggr_stats.ampduCount[8] =
+ rt2x00_get_field32(reg,TX_AGG_CNT4_AGG_SIZE_9_COUNT);
+ rt2x00dev->aggr_stats.ampduCount[9] =
+ rt2x00_get_field32(reg,TX_AGG_CNT4_AGG_SIZE_10_COUNT);
+
+ rt2800_register_read(rt2x00dev,TX_AGG_CNT5, &reg);
+ rt2x00dev->aggr_stats.ampduCount[10] =
+ rt2x00_get_field32(reg,TX_AGG_CNT5_AGG_SIZE_11_COUNT);
+ rt2x00dev->aggr_stats.ampduCount[11] =
+ rt2x00_get_field32(reg,TX_AGG_CNT5_AGG_SIZE_12_COUNT);
+
+ rt2800_register_read(rt2x00dev,TX_AGG_CNT6, &reg);
+ rt2x00dev->aggr_stats.ampduCount[12] =
+ rt2x00_get_field32(reg,TX_AGG_CNT6_AGG_SIZE_13_COUNT);
+ rt2x00dev->aggr_stats.ampduCount[13] =
+ rt2x00_get_field32(reg,TX_AGG_CNT6_AGG_SIZE_14_COUNT);
+
+ rt2800_register_read(rt2x00dev,TX_AGG_CNT7, &reg);
+ rt2x00dev->aggr_stats.ampduCount[14] =
+ rt2x00_get_field32(reg,TX_AGG_CNT7_AGG_SIZE_15_COUNT);
+ rt2x00dev->aggr_stats.ampduCount[15] =
+ rt2x00_get_field32(reg,TX_AGG_CNT7_AGG_SIZE_16_COUNT);
+
+ all = rt2x00dev->aggr_stats.no_aggr +
+ rt2x00dev->aggr_stats.all_aggr;
+
+ for( i = 0; i < 16 ; i++)
+ rt2x00dev->aggr_stats.ampduRatio[i] =
+ ((rt2x00dev->aggr_stats.ampduCount[i] * 100) / all);
+}
+EXPORT_SYMBOL_GPL(rt2800_update_aggr_stats);
+
static void rt2800_config_lna_gain(struct rt2x00_dev *rt2x00dev,
struct rt2x00lib_conf *libconf)
{
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index fc9efdf..6ca9cdb 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -225,6 +225,79 @@ static enum hrtimer_restart rt2800usb_tx_sta_fifo_timeout(struct hrtimer *timer)
return HRTIMER_NORESTART;
}

+
+
+static bool rt2800usb_tx_agg_cnt_read_completed(struct rt2x00_dev *rt2x00dev,
+ int urb_status, u32 reg)
+{
+
+ if (urb_status) {
+ WARNING(rt2x00dev, "TX status read failed %d\n", urb_status);
+ return false;
+ }
+
+ if(rt2x00dev->aggr_stats.cnt == 0) {
+ rt2x00dev->aggr_stats.no_aggr =
+ rt2x00_get_field32(reg,TX_AGG_CNT_NON_AGG_TX_COUNT);
+ rt2x00dev->aggr_stats.all_aggr =
+ rt2x00_get_field32(reg,TX_AGG_CNT_AGG_TX_COUNT);
+ } else {
+ unsigned int tmp = (rt2x00dev->aggr_stats.cnt - 1)*2;
+
+ rt2x00dev->aggr_stats.ampduCount[tmp] =
+ rt2x00_get_field32(reg,TX_AGG_CNT0_AGG_SIZE_1_COUNT);
+ rt2x00dev->aggr_stats.ampduCount[tmp+1] =
+ rt2x00_get_field32(reg,TX_AGG_CNT0_AGG_SIZE_2_COUNT);
+ }
+
+ if(rt2x00dev->aggr_stats.cnt == 9) {
+ rt2x00dev->aggr_stats.cnt = 0;
+ clear_bit(TX_AGG_TIMER, &rt2x00dev->aggr_stats.flags);
+ } else {
+ rt2x00dev->aggr_stats.cnt++;
+ /* Read next TX_AGG_CNT register after 1 ms */
+ hrtimer_start(&rt2x00dev->txaggcnt_timer,
+ ktime_set(0, 1000000), HRTIMER_MODE_REL);
+ }
+
+ return true;
+}
+
+static enum hrtimer_restart rt2800usb_tx_agg_cnt_timeout(struct hrtimer *timer)
+{
+ struct rt2x00_dev *rt2x00dev =
+ container_of(timer, struct rt2x00_dev, txaggcnt_timer);
+ unsigned int offset;
+
+ offset = TX_AGG_CNT + rt2x00dev->aggr_stats.cnt*4;
+ rt2x00usb_register_read_async(rt2x00dev,offset,
+ rt2800usb_tx_agg_cnt_read_completed);
+
+ return HRTIMER_NORESTART;
+}
+
+static void rt2800usb_async_read_tx_agg_cnt(struct rt2x00_dev *rt2x00dev)
+{
+
+ if (test_and_set_bit(TX_AGG_TIMER, &rt2x00dev->aggr_stats.flags))
+ return;
+
+ /* Read TX_AGG_CNT register after 1 ms */
+ hrtimer_start(&rt2x00dev->txaggcnt_timer, ktime_set(0, 1000000),
+ HRTIMER_MODE_REL);
+}
+
+static void rt2800usb_timer_txagg(unsigned long data)
+{
+ struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+
+ if (!test_bit(TX_AGG_TIMER, &rt2x00dev->aggr_stats.flags))
+ rt2800usb_async_read_tx_agg_cnt(rt2x00dev);
+
+ rt2x00dev->txagg_timer.expires += 5000;
+ add_timer(&rt2x00dev->txagg_timer);
+}
+
/*
* Firmware functions
*/
@@ -750,6 +823,7 @@ static int rt2800usb_read_eeprom(struct rt2x00_dev *rt2x00dev)
static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
{
int retval;
+ unsigned long j = jiffies;

retval = rt2800_probe_hw(rt2x00dev);
if (retval)
@@ -765,6 +839,24 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
*/
PREPARE_WORK(&rt2x00dev->txdone_work, rt2800usb_work_txdone);

+ /*
+ * Set txaggcnt timer function. for usb async read
+ */
+ rt2x00dev->txaggcnt_timer.function = rt2800usb_tx_agg_cnt_timeout;
+
+ /*
+ * Set txagg timer function. periodic read
+ */
+ clear_bit(TX_AGG_TIMER, &rt2x00dev->aggr_stats.flags);
+ rt2x00dev->aggr_stats.cnt = 0;
+ init_timer(&rt2x00dev->txagg_timer);
+ rt2x00dev->txagg_timer.function = rt2800usb_timer_txagg;
+ rt2x00dev->txagg_timer.data = (unsigned long)rt2x00dev;
+
+ j = jiffies;
+ rt2x00dev->txagg_timer.expires = j + 5000;
+ add_timer(&rt2x00dev->txagg_timer);
+
return 0;
}

diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index fe4c572..b6ae9f3 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -637,6 +637,7 @@ struct rt2x00lib_ops {
struct ieee80211_sta *sta);
int (*sta_remove) (struct rt2x00_dev *rt2x00dev,
int wcid);
+ void (*update_aggr_stats) (struct rt2x00_dev *rt2x00dev);
};

/*
@@ -733,6 +734,24 @@ enum {
NUM_IF_COMB,
};

+#define RT2X00_AGGR_CNT_MAX 16
+
+/*
+ * rt2x00 aggregation state flags
+ */
+enum rt2x00_agg_state_flags {
+ TX_AGG_TIMER,
+};
+struct rt2x00_aggr_stats {
+ unsigned long flags;
+ unsigned int cnt;
+ u32 all_aggr;
+ u32 no_aggr;
+ u32 ampduCount[RT2X00_AGGR_CNT_MAX];
+ u32 ampduRatio[RT2X00_AGGR_CNT_MAX];
+};
+
+
/*
* rt2x00 device structure.
*/
@@ -764,6 +783,8 @@ struct rt2x00_dev {
enum ieee80211_band curr_band;
int curr_freq;

+ struct rt2x00_aggr_stats aggr_stats;
+
/*
* If enabled, the debugfs interface structures
* required for deregistration of debugfs.
@@ -984,6 +1005,13 @@ struct rt2x00_dev {
struct hrtimer txstatus_timer;

/*
+ * Timer to ensure tx aggregation counter reports are read (rt2800usb).
+ */
+ struct hrtimer txaggcnt_timer;
+
+ struct timer_list txagg_timer;
+
+ /*
* Tasklet for processing tx status reports (rt2800pci).
*/
struct tasklet_struct txstatus_tasklet;
diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c
index fe7a7f6..c41d5a8 100644
--- a/drivers/net/wireless/rt2x00/rt2x00debug.c
+++ b/drivers/net/wireless/rt2x00/rt2x00debug.c
@@ -75,6 +75,7 @@ struct rt2x00debug_intf {
* - frame dump file
* - queue stats file
* - crypto stats file
+ * - aggr stats file
*/
struct dentry *driver_folder;
struct dentry *driver_entry;
@@ -96,6 +97,7 @@ struct rt2x00debug_intf {
struct dentry *queue_frame_dump_entry;
struct dentry *queue_stats_entry;
struct dentry *crypto_stats_entry;
+ struct dentry *aggr_stats_entry;

/*
* The frame dump file only allows a single reader,
@@ -589,6 +591,63 @@ static const struct file_operations rt2x00debug_fop_cap_flags = {
.llseek = default_llseek,
};

+
+static ssize_t rt2x00debug_read_aggr_stats(struct file *file,
+ char __user *buf,
+ size_t length,
+ loff_t *offset)
+{
+ struct rt2x00debug_intf *intf = file->private_data;
+ struct rt2x00_dev *rt2x00dev = intf->rt2x00dev;
+ size_t size;
+ char *data;
+ char *temp;
+ int i;
+
+ if (*offset)
+ return 0;
+
+ data = kzalloc(17 * MAX_LINE_LENGTH, GFP_KERNEL);
+ if (!data)
+ return 0;
+
+ temp = data;
+ temp += sprintf(temp, "all aggr(%u) no agg(%u) \n",
+ rt2x00dev->aggr_stats.all_aggr,
+ rt2x00dev->aggr_stats.no_aggr);
+
+ for (i = 0; i < 15 ; i++) {
+ temp += sprintf(temp, "%u AMPDU cnt(%u), ratio(%u)\n", (i+1),
+ rt2x00dev->aggr_stats.ampduCount[i],
+ rt2x00dev->aggr_stats.ampduRatio[i]);
+ }
+
+ temp += sprintf(temp, "16 or up AMPDU cnt(%u), ratio(%u)\n",
+ rt2x00dev->aggr_stats.ampduCount[i],
+ rt2x00dev->aggr_stats.ampduRatio[i]);
+
+ size = strlen(data);
+ size = min(size, length);
+
+ if (copy_to_user(buf, data, size)) {
+ kfree(data);
+ return -EFAULT;
+ }
+
+ kfree(data);
+
+ *offset += size;
+ return size;
+}
+
+static const struct file_operations rt2x00debug_fop_aggr_stats = {
+ .owner = THIS_MODULE,
+ .read = rt2x00debug_read_aggr_stats,
+ .open = rt2x00debug_file_open,
+ .release = rt2x00debug_file_release,
+ .llseek = default_llseek,
+};
+
static struct dentry *rt2x00debug_create_file_driver(const char *name,
struct rt2x00debug_intf
*intf,
@@ -694,6 +753,12 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
if (IS_ERR(intf->cap_flags) || !intf->cap_flags)
goto exit;

+ intf->aggr_stats_entry = debugfs_create_file("aggr_stats", S_IRUSR,
+ intf->driver_folder, intf,
+ &rt2x00debug_fop_aggr_stats);
+ if (IS_ERR(intf->aggr_stats_entry) || !intf->aggr_stats_entry)
+ goto exit;
+
intf->register_folder =
debugfs_create_dir("register", intf->driver_folder);
if (IS_ERR(intf->register_folder) || !intf->register_folder)
@@ -794,6 +859,7 @@ void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev)
debugfs_remove(intf->chipset_entry);
debugfs_remove(intf->driver_entry);
debugfs_remove(intf->driver_folder);
+ debugfs_remove(intf->aggr_stats_entry);
kfree(intf->chipset_blob.data);
kfree(intf->driver_blob.data);
kfree(intf);
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index b16521e..935e8b1 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -1403,7 +1403,9 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)
cancel_delayed_work_sync(&rt2x00dev->autowakeup_work);
cancel_work_sync(&rt2x00dev->sleep_work);
if (rt2x00_is_usb(rt2x00dev)) {
+ hrtimer_cancel(&rt2x00dev->txaggcnt_timer);
hrtimer_cancel(&rt2x00dev->txstatus_timer);
+ del_timer(&rt2x00dev->txagg_timer);
cancel_work_sync(&rt2x00dev->rxdone_work);
cancel_work_sync(&rt2x00dev->txdone_work);
}
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c
index 8828987..0529f69 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.c
@@ -813,6 +813,8 @@ int rt2x00usb_probe(struct usb_interface *usb_intf,
INIT_WORK(&rt2x00dev->txdone_work, rt2x00usb_work_txdone);
hrtimer_init(&rt2x00dev->txstatus_timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL);
+ hrtimer_init(&rt2x00dev->txaggcnt_timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);

retval = rt2x00usb_alloc_reg(rt2x00dev);
if (retval)
--
1.7.10.4



2013-08-05 15:36:07

by Helmut Schaa

[permalink] [raw]
Subject: Re: [[PATCHv2]rt2800usb: ampdu len] rt2800usb: update aggregation len





Sylvain ROGER RIEUNIER <[email protected]> schrieb:
>Dear Helmut,
>
>I already did a test using the Sysfs with an synchronous read. But more
>you
>read TX_AGG_CNT counters and more you reduce the bandwith.
>My idea was to make asynchronous read periodically to allow Sysfs
>reading without
>impact the bandwith performance.
>Do you have an other suggestion?

Maybe just rate limit register reading in the sysfs interface? Something like: we've just updated the stats a second ago, use the old data ...

Or as a different idea, schedule a work from the tx status path (the counters can only change if we transmit something), also rate limited ... This could be used for rt2800pci as well then ...

Helmut

>
>Sylvain
>
>
>
>2013/8/5 Helmut Schaa <[email protected]>
>
>> On Thu, Aug 1, 2013 at 5:39 PM, Sylvain ROGER RIEUNIER
>> <[email protected]> wrote:
>> > From: Sylvain Roger Rieunier <[email protected]>
>> >
>> > I tried to read periodically TX_AGG_CNT registers on the rt2800usb
>> driver.
>> > I'had made USB synchronous and asynchronous read. But it's only
>reduces
>> the
>> > bandwidth.
>> > Does anyone have an idea?
>> >
>> >
>> > Signed-off-by: Sylvain ROGER RIEUNIER
><[email protected]>
>>
>> Instead of updating the statistics from within a timer can't you just
>> update them when reading the sysfs file?
>> Helmut
>>

--
Diese Nachricht wurde von meinem Android-Mobiltelefon mit K-9 Mail gesendet.

2013-08-05 11:33:40

by Helmut Schaa

[permalink] [raw]
Subject: Re: [[PATCHv2]rt2800usb: ampdu len] rt2800usb: update aggregation len

On Thu, Aug 1, 2013 at 5:39 PM, Sylvain ROGER RIEUNIER
<[email protected]> wrote:
> From: Sylvain Roger Rieunier <[email protected]>
>
> I tried to read periodically TX_AGG_CNT registers on the rt2800usb driver.
> I'had made USB synchronous and asynchronous read. But it's only reduces the
> bandwidth.
> Does anyone have an idea?
>
>
> Signed-off-by: Sylvain ROGER RIEUNIER <[email protected]>

Instead of updating the statistics from within a timer can't you just
update them when reading the sysfs file?
Helmut

2013-08-05 20:25:20

by Stanislaw Gruszka

[permalink] [raw]
Subject: Re: [rt2x00-users] [[PATCHv2]rt2800usb: ampdu len] rt2800usb: update aggregation len

On Thu, Aug 01, 2013 at 05:39:42PM +0200, Sylvain ROGER RIEUNIER wrote:
> From: Sylvain Roger Rieunier <[email protected]>
>
> I tried to read periodically TX_AGG_CNT registers on the rt2800usb driver.
> I'had made USB synchronous and asynchronous read. But it's only reduces the
> bandwidth.
> Does anyone have an idea?

Could you elaborate more what you are trying to achieve ?

Stanislaw