mac80211: rt2x00: save survey for every channel visited
authorMarkov Mikhail <markov.mikhail@itmh.ru>
Mon, 12 Oct 2020 09:46:43 +0000 (14:46 +0500)
committerDaniel Golle <daniel@makrotopia.org>
Wed, 21 Oct 2020 10:56:26 +0000 (11:56 +0100)
rt2800 olny gives you survey for current channel.

Survey-based ACS algorithms are failing to perform their job when working
with rt2800.

Make rt2800 save survey for every channel visited and be able to give away
that information.

There is a bug registred https://dev.archive.openwrt.org/ticket/19081 and
this patch solves the issue.

Signed-off-by: Markov Mikhail <markov.mikhail@itmh.ru>
package/kernel/mac80211/patches/rt2x00/992-rt2x00-save-survey-for-every-channel-visited.patch [new file with mode: 0644]

diff --git a/package/kernel/mac80211/patches/rt2x00/992-rt2x00-save-survey-for-every-channel-visited.patch b/package/kernel/mac80211/patches/rt2x00/992-rt2x00-save-survey-for-every-channel-visited.patch
new file mode 100644 (file)
index 0000000..31a7bae
--- /dev/null
@@ -0,0 +1,183 @@
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+@@ -1238,6 +1238,8 @@ void rt2800_watchdog(struct rt2x00_dev *
+       if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags))
+               return;
++      rt2800_update_survey(rt2x00dev);
++
+       queue_for_each(rt2x00dev, queue) {
+               switch (queue->qid) {
+               case QID_AC_VO:
+@@ -1274,6 +1276,18 @@ void rt2800_watchdog(struct rt2x00_dev *
+ }
+ EXPORT_SYMBOL_GPL(rt2800_watchdog);
++void rt2800_update_survey(struct rt2x00_dev *rt2x00dev)
++{
++      struct ieee80211_channel *chan = rt2x00dev->hw->conf.chandef.chan;
++      struct rt2x00_chan_survey *chan_survey =
++                 &rt2x00dev->chan_survey[chan->hw_value];
++
++      chan_survey->time_idle += rt2800_register_read(rt2x00dev, CH_IDLE_STA);
++      chan_survey->time_busy += rt2800_register_read(rt2x00dev, CH_BUSY_STA);
++      chan_survey->time_ext_busy += rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC);
++}
++EXPORT_SYMBOL_GPL(rt2800_update_survey);
++
+ static unsigned int rt2800_hw_beacon_base(struct rt2x00_dev *rt2x00dev,
+                                         unsigned int index)
+ {
+@@ -12199,26 +12213,30 @@ int rt2800_get_survey(struct ieee80211_h
+ {
+       struct rt2x00_dev *rt2x00dev = hw->priv;
+       struct ieee80211_conf *conf = &hw->conf;
+-      u32 idle, busy, busy_ext;
++      struct rt2x00_chan_survey *chan_survey =
++                 &rt2x00dev->chan_survey[idx];
++      enum nl80211_band band = NL80211_BAND_2GHZ;
+-      if (idx != 0)
++      if (idx >= rt2x00dev->bands[band].n_channels) {
++              idx -= rt2x00dev->bands[band].n_channels;
++              band = NL80211_BAND_5GHZ;
++      }
++
++      if (idx >= rt2x00dev->bands[band].n_channels)
+               return -ENOENT;
+-      survey->channel = conf->chandef.chan;
++      if (idx == 0)
++              rt2800_update_survey(rt2x00dev);
+-      idle = rt2800_register_read(rt2x00dev, CH_IDLE_STA);
+-      busy = rt2800_register_read(rt2x00dev, CH_BUSY_STA);
+-      busy_ext = rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC);
+-
+-      if (idle || busy) {
+-              survey->filled = SURVEY_INFO_TIME |
+-                               SURVEY_INFO_TIME_BUSY |
+-                               SURVEY_INFO_TIME_EXT_BUSY;
+-
+-              survey->time = (idle + busy) / 1000;
+-              survey->time_busy = busy / 1000;
+-              survey->time_ext_busy = busy_ext / 1000;
+-      }
++      survey->channel = &rt2x00dev->bands[band].channels[idx];
++
++      survey->filled = SURVEY_INFO_TIME |
++                       SURVEY_INFO_TIME_BUSY |
++                       SURVEY_INFO_TIME_EXT_BUSY;
++
++      survey->time = div_u64(chan_survey->time_idle + chan_survey->time_busy, 1000);
++      survey->time_busy = div_u64(chan_survey->time_busy, 1000);
++      survey->time_ext_busy = div_u64(chan_survey->time_ext_busy, 1000);
+       if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
+               survey->filled |= SURVEY_INFO_IN_USE;
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
+@@ -243,6 +243,7 @@ bool rt2800_txstatus_timeout(struct rt2x
+ bool rt2800_txstatus_pending(struct rt2x00_dev *rt2x00dev);
+ void rt2800_watchdog(struct rt2x00_dev *rt2x00dev);
++void rt2800_update_survey(struct rt2x00_dev *rt2x00dev);
+ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc);
+ void rt2800_clear_beacon(struct queue_entry *entry);
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
+@@ -360,6 +360,7 @@ static const struct rt2x00lib_ops rt2800
+       .gain_calibration       = rt2800_gain_calibration,
+       .vco_calibration        = rt2800_vco_calibration,
+       .watchdog               = rt2800_watchdog,
++      .update_survey          = rt2800_update_survey,
+       .start_queue            = rt2800mmio_start_queue,
+       .kick_queue             = rt2800mmio_kick_queue,
+       .stop_queue             = rt2800mmio_stop_queue,
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
+@@ -214,6 +214,7 @@ static const struct rt2x00lib_ops rt2800
+       .gain_calibration       = rt2800_gain_calibration,
+       .vco_calibration        = rt2800_vco_calibration,
+       .watchdog               = rt2800_watchdog,
++      .update_survey          = rt2800_update_survey,
+       .start_queue            = rt2800mmio_start_queue,
+       .kick_queue             = rt2800mmio_kick_queue,
+       .stop_queue             = rt2800mmio_stop_queue,
+--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
+@@ -183,6 +183,15 @@ struct rf_channel {
+ };
+ /*
++ * Information structure for channel survey.
++ */
++struct rt2x00_chan_survey {
++      u64 time_idle;
++      u64 time_busy;
++      u64 time_ext_busy;
++};
++
++/*
+  * Channel information structure
+  */
+ struct channel_info {
+@@ -567,6 +576,7 @@ struct rt2x00lib_ops {
+        * Data queue handlers.
+        */
+       void (*watchdog) (struct rt2x00_dev *rt2x00dev);
++      void (*update_survey) (struct rt2x00_dev *rt2x00dev);
+       void (*start_queue) (struct data_queue *queue);
+       void (*kick_queue) (struct data_queue *queue);
+       void (*stop_queue) (struct data_queue *queue);
+@@ -755,6 +765,7 @@ struct rt2x00_dev {
+        */
+       struct ieee80211_hw *hw;
+       struct ieee80211_supported_band bands[NUM_NL80211_BANDS];
++      struct rt2x00_chan_survey *chan_survey;
+       enum nl80211_band curr_band;
+       int curr_freq;
+--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+@@ -1057,6 +1057,12 @@ static int rt2x00lib_probe_hw_modes(stru
+       if (!rates)
+               goto exit_free_channels;
++      rt2x00dev->chan_survey =
++              kcalloc(spec->num_channels, sizeof(struct rt2x00_chan_survey),
++                      GFP_KERNEL);
++      if (!rt2x00dev->chan_survey)
++              goto exit_free_rates;
++
+       /*
+        * Initialize Rate list.
+        */
+@@ -1108,6 +1114,8 @@ static int rt2x00lib_probe_hw_modes(stru
+       return 0;
++ exit_free_rates:
++      kfree(rates);
+  exit_free_channels:
+       kfree(channels);
+       rt2x00_err(rt2x00dev, "Allocation ieee80211 modes failed\n");
+--- a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
+@@ -317,6 +317,15 @@ int rt2x00mac_config(struct ieee80211_hw
+               return 0;
+       /*
++       * To provide correct survey data for survey-based ACS algorithm
++       * we have to save survey data for current channel before switching.
++       */
++      if (rt2x00dev->ops->lib->update_survey &&
++          (changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
++              rt2x00dev->ops->lib->update_survey(rt2x00dev);
++      }
++
++      /*
+        * Some configuration parameters (e.g. channel and antenna values) can
+        * only be set when the radio is enabled, but do require the RX to
+        * be off. During this period we should keep link tuning enabled,