Home | History | Annotate | Download | only in wifi
      1 //
      2 // Copyright (C) 2014 The Android Open Source Project
      3 //
      4 // Licensed under the Apache License, Version 2.0 (the "License");
      5 // you may not use this file except in compliance with the License.
      6 // You may obtain a copy of the License at
      7 //
      8 //      http://www.apache.org/licenses/LICENSE-2.0
      9 //
     10 // Unless required by applicable law or agreed to in writing, software
     11 // distributed under the License is distributed on an "AS IS" BASIS,
     12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 // See the License for the specific language governing permissions and
     14 // limitations under the License.
     15 //
     16 
     17 #include "shill/wifi/mac80211_monitor.h"
     18 
     19 #include <algorithm>
     20 
     21 #include <base/files/file_util.h>
     22 #include <base/strings/string_number_conversions.h>
     23 #include <base/strings/string_split.h>
     24 #include <base/strings/stringprintf.h>
     25 
     26 #include "shill/logging.h"
     27 #include "shill/metrics.h"
     28 #include "shill/net/shill_time.h"
     29 
     30 namespace shill {
     31 
     32 using std::string;
     33 using std::vector;
     34 
     35 namespace Logging {
     36 static auto kModuleLogScope = ScopeLogger::kWiFi;
     37 static string ObjectID(Mac80211Monitor* m) { return m->link_name(); }
     38 }
     39 
     40 // statics
     41 // At 17-25 bytes per queue, this accommodates 80 queues.
     42 // ath9k has 4 queues, and WP2 has 16 queues.
     43 const size_t Mac80211Monitor::kMaxQueueStateSizeBytes = 2048;
     44 const char Mac80211Monitor::kQueueStatusPathFormat[] =
     45     "/sys/kernel/debug/ieee80211/%s/queues";
     46 const char Mac80211Monitor::kWakeQueuesPathFormat[] =
     47     "/sys/kernel/debug/ieee80211/%s/wake_queues";
     48 const time_t Mac80211Monitor::kQueueStatePollIntervalSeconds = 30;
     49 const time_t Mac80211Monitor::kMinimumTimeBetweenWakesSeconds = 60;
     50 
     51 Mac80211Monitor::Mac80211Monitor(
     52     EventDispatcher* dispatcher,
     53     const string& link_name,
     54     size_t queue_length_limit,
     55     const base::Closure& on_repair_callback,
     56     Metrics* metrics)
     57     : time_(Time::GetInstance()),
     58       dispatcher_(dispatcher),
     59       link_name_(link_name),
     60       queue_length_limit_(queue_length_limit),
     61       on_repair_callback_(on_repair_callback),
     62       metrics_(metrics),
     63       phy_name_("phy-unknown"),
     64       last_woke_queues_monotonic_seconds_(0),
     65       is_running_(false),
     66       have_ever_read_queue_state_file_(false),
     67       is_device_connected_(false),
     68       weak_ptr_factory_(this) {
     69   CHECK(time_);
     70   CHECK(dispatcher_);
     71   CHECK(metrics_);
     72 }
     73 
     74 Mac80211Monitor::~Mac80211Monitor() {
     75   Stop();
     76 }
     77 
     78 void Mac80211Monitor::Start(const string& phy_name) {
     79   SLOG(this, 2) << __func__ << " on " << link_name_ << " (" << phy_name << ")";
     80   CHECK(!is_running_);
     81   phy_name_ = phy_name;
     82   queue_state_file_path_ = base::FilePath(
     83       base::StringPrintf(kQueueStatusPathFormat, phy_name.c_str()));
     84   wake_queues_file_path_ = base::FilePath(
     85       base::StringPrintf(kWakeQueuesPathFormat, phy_name.c_str()));
     86   last_woke_queues_monotonic_seconds_ = 0;
     87   StartTimer();
     88   is_running_ = true;
     89 }
     90 
     91 void Mac80211Monitor::Stop() {
     92   SLOG(this, 2) << __func__ << " on " << link_name_ << " (" << phy_name_ << ")";
     93   StopTimer();
     94   is_running_ = false;
     95 }
     96 
     97 void Mac80211Monitor::UpdateConnectedState(bool new_state) {
     98   SLOG(this, 2) << __func__ << " (new_state=" << new_state << ")";
     99   is_device_connected_ = new_state;
    100 }
    101 
    102 void Mac80211Monitor::StartTimer() {
    103   SLOG(this, 2) << __func__;
    104   if (check_queues_callback_.IsCancelled()) {
    105     check_queues_callback_.Reset(
    106         Bind(&Mac80211Monitor::WakeQueuesIfNeeded,
    107              weak_ptr_factory_.GetWeakPtr()));
    108   }
    109   dispatcher_->PostDelayedTask(check_queues_callback_.callback(),
    110                                 kQueueStatePollIntervalSeconds * 1000);
    111 }
    112 
    113 void Mac80211Monitor::StopTimer() {
    114   SLOG(this, 2) << __func__;
    115   check_queues_callback_.Cancel();
    116 }
    117 
    118 void Mac80211Monitor::WakeQueuesIfNeeded() {
    119   SLOG(this, 2) << __func__ << " on " << link_name_ << " (" << phy_name_ << ")";
    120   CHECK(is_running_);
    121   StartTimer();  // Always re-arm timer.
    122 
    123   if (is_device_connected_) {
    124     SLOG(this, 5) << "Skipping queue check: device is connected.";
    125     return;
    126   }
    127 
    128   string queue_state_string;
    129   if (!base::ReadFileToString(queue_state_file_path_, &queue_state_string,
    130                               kMaxQueueStateSizeBytes)) {
    131     if (have_ever_read_queue_state_file_) {
    132       LOG(WARNING) << __func__ << ": incomplete read on "
    133                    << queue_state_file_path_.value();
    134     }
    135     return;
    136   }
    137   have_ever_read_queue_state_file_ = true;
    138 
    139   uint32_t stuck_flags =
    140       CheckAreQueuesStuck(ParseQueueState(queue_state_string));
    141   SLOG(this, 2) << __func__ << " stuck_flags=" << stuck_flags;
    142   if (!(stuck_flags & kStopFlagPowerSave)) {
    143     if (stuck_flags) {
    144       LOG(INFO) << "Skipping wake: stuck_flags is "
    145                 << std::showbase << std::hex << stuck_flags
    146                 << " (require " << kStopFlagPowerSave << " to wake)."
    147                 << std::dec << std::noshowbase;
    148     }
    149     return;
    150   }
    151 
    152   time_t now_monotonic_seconds = 0;
    153   if (!time_->GetSecondsMonotonic(&now_monotonic_seconds)) {
    154     LOG(WARNING) << "Skipping reset: failed to get monotonic time";
    155     return;
    156   }
    157 
    158   time_t elapsed = now_monotonic_seconds - last_woke_queues_monotonic_seconds_;
    159   if (elapsed < kMinimumTimeBetweenWakesSeconds) {
    160     LOG(WARNING) << "Skipping reset "
    161                  << "(min interval=" << kMinimumTimeBetweenWakesSeconds
    162                  << ", elapsed=" << elapsed << ")";
    163     return;
    164   }
    165 
    166   LOG(WARNING) << "Queues appear stuck; waking.";
    167   if (base::WriteFile(wake_queues_file_path_, "", sizeof("")) < 0) {
    168     PLOG(ERROR) << "Failed to write to " << wake_queues_file_path_.value();
    169     return;
    170   }
    171 
    172   if (!on_repair_callback_.is_null()) {
    173     on_repair_callback_.Run();
    174   }
    175 
    176   last_woke_queues_monotonic_seconds_ = now_monotonic_seconds;
    177 }
    178 
    179 uint32_t Mac80211Monitor::CheckAreQueuesStuck(
    180     const vector<QueueState>& queue_states) {
    181   size_t max_stuck_queue_len = 0;
    182   uint32_t stuck_flags = 0;
    183   for (const auto& queue_state : queue_states) {
    184     if (queue_state.queue_length < queue_length_limit_) {
    185       SLOG(this, 5) << __func__
    186                     << " skipping queue of length " << queue_state.queue_length
    187                     << " (threshold is " << queue_length_limit_ << ")";
    188       continue;
    189     }
    190     if (!queue_state.stop_flags) {
    191       SLOG(this, 5) << __func__
    192                     << " skipping queue of length " << queue_state.queue_length
    193                     << " (not stopped)";
    194       continue;
    195     }
    196     stuck_flags = stuck_flags | queue_state.stop_flags;
    197     max_stuck_queue_len =
    198         std::max(max_stuck_queue_len, queue_state.queue_length);
    199   }
    200 
    201   if (max_stuck_queue_len >= queue_length_limit_) {
    202     LOG(WARNING) << "max queue length is " << max_stuck_queue_len;
    203   }
    204 
    205   if (stuck_flags) {
    206     for (unsigned int i = 0; i < kStopReasonMax; ++i) {
    207       auto stop_reason = static_cast<QueueStopReason>(i);
    208       if (stuck_flags & GetFlagForReason(stop_reason)) {
    209         metrics_->SendEnumToUMA(Metrics::kMetricWifiStoppedTxQueueReason,
    210                                 stop_reason,
    211                                 kStopReasonMax);
    212       }
    213     }
    214 
    215     metrics_->SendToUMA(Metrics::kMetricWifiStoppedTxQueueLength,
    216                         max_stuck_queue_len,
    217                         Metrics::kMetricWifiStoppedTxQueueLengthMin,
    218                         Metrics::kMetricWifiStoppedTxQueueLengthMax,
    219                         Metrics::kMetricWifiStoppedTxQueueLengthNumBuckets);
    220   }
    221 
    222   return stuck_flags;
    223 }
    224 
    225 // example input:
    226 // 01: 0x00000000/0
    227 // ...
    228 // 04: 0x00000000/0
    229 //
    230 // static
    231 vector<Mac80211Monitor::QueueState>
    232 Mac80211Monitor::ParseQueueState(const string& state_string) {
    233   vector<QueueState> queue_states;
    234   vector<string> queue_state_strings = base::SplitString(
    235       state_string, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
    236 
    237   if (queue_state_strings.empty()) {
    238     return queue_states;
    239   }
    240 
    241   // Trailing newline generates empty string as last element.
    242   // Discard that empty string if present.
    243   if (queue_state_strings.back().empty()) {
    244     queue_state_strings.pop_back();
    245   }
    246 
    247   for (const auto& queue_state : queue_state_strings) {
    248     // Example |queue_state|: "00: 0x00000000/10".
    249     vector<string> queuenum_and_state = base::SplitString(
    250         queue_state, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
    251     if (queuenum_and_state.size() != 2) {
    252       LOG(WARNING) << __func__ << ": parse error on " << queue_state;
    253       continue;
    254     }
    255 
    256     // Example |queuenum_and_state|: {"00", "0x00000000/10"}.
    257     vector<string> stopflags_and_length = base::SplitString(
    258         queuenum_and_state[1], "/", base::TRIM_WHITESPACE,
    259         base::SPLIT_WANT_ALL);
    260     if (stopflags_and_length.size() != 2) {
    261       LOG(WARNING) << __func__ << ": parse error on " << queue_state;
    262       continue;
    263     }
    264 
    265     // Example |stopflags_and_length|: {"0x00000000", "10"}.
    266     size_t queue_number;
    267     uint32_t stop_flags;
    268     size_t queue_length;
    269     if (!base::StringToSizeT(queuenum_and_state[0], &queue_number)) {
    270       LOG(WARNING) << __func__ << ": parse error on " << queue_state;
    271       continue;
    272     }
    273     if (!base::HexStringToUInt(stopflags_and_length[0], &stop_flags)) {
    274       LOG(WARNING) << __func__ << ": parse error on " << queue_state;
    275       continue;
    276     }
    277     if (!base::StringToSizeT(stopflags_and_length[1], &queue_length)) {
    278       LOG(WARNING) << __func__ << ": parse error on " << queue_state;
    279       continue;
    280     }
    281     queue_states.emplace_back(queue_number, stop_flags, queue_length);
    282   }
    283 
    284   return queue_states;
    285 }
    286 
    287 // static
    288 Mac80211Monitor::QueueStopFlag Mac80211Monitor::GetFlagForReason(
    289     QueueStopReason reason) {
    290   switch (reason) {
    291     case kStopReasonDriver:
    292       return kStopFlagDriver;
    293     case kStopReasonPowerSave:
    294       return kStopFlagPowerSave;
    295     case kStopReasonChannelSwitch:
    296       return kStopFlagChannelSwitch;
    297     case kStopReasonAggregation:
    298       return kStopFlagAggregation;
    299     case kStopReasonSuspend:
    300       return kStopFlagSuspend;
    301     case kStopReasonBufferAdd:
    302       return kStopFlagBufferAdd;
    303     case kStopReasonChannelTypeChange:
    304       return kStopFlagChannelTypeChange;
    305   }
    306 
    307   // The switch statement above is exhaustive (-Wswitch will complain
    308   // if it is not). But |reason| might be outside the defined range for
    309   // the enum, so we need this to keep the compiler happy.
    310   return kStopFlagInvalid;
    311 }
    312 
    313 }  // namespace shill
    314