blob: 84717a3577ccf3fdbfa2b00dd9c0d36ab59b5c65 [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "shill/traffic_monitor.h"
#include <base/bind.h>
#include <base/stringprintf.h>
#include "shill/device.h"
#include "shill/device_info.h"
#include "shill/event_dispatcher.h"
#include "shill/logging.h"
#include "shill/socket_info_reader.h"
using base::StringPrintf;
using std::string;
using std::vector;
namespace shill {
// static
const int TrafficMonitor::kMinimumFailedSamplesToTrigger = 2;
const int64 TrafficMonitor::kSamplingIntervalMilliseconds = 5000;
TrafficMonitor::TrafficMonitor(const DeviceRefPtr &device,
EventDispatcher *dispatcher)
: device_(device),
dispatcher_(dispatcher),
socket_info_reader_(new SocketInfoReader),
accummulated_failure_samples_(0) {
}
TrafficMonitor::~TrafficMonitor() {
Stop();
}
void TrafficMonitor::Start() {
SLOG(Link, 2) << __func__;
Stop();
sample_traffic_callback_.Reset(base::Bind(&TrafficMonitor::SampleTraffic,
base::Unretained(this)));
dispatcher_->PostDelayedTask(sample_traffic_callback_.callback(),
kSamplingIntervalMilliseconds);
}
void TrafficMonitor::Stop() {
SLOG(Link, 2) << __func__;
sample_traffic_callback_.Cancel();
accummulated_failure_samples_ = 0;
}
void TrafficMonitor::BuildIPPortToTxQueueLength(
const vector<SocketInfo> &socket_infos,
IPPortToTxQueueLengthMap *tx_queue_lengths) {
SLOG(Link, 3) << __func__;
string device_ip_address = device_->ipconfig()->properties().address;
vector<SocketInfo>::const_iterator it;
for (it = socket_infos.begin(); it != socket_infos.end(); ++it) {
SLOG(Link, 4) << "SocketInfo(IP=" << it->local_ip_address().ToString()
<< ", TX=" << it->transmit_queue_value()
<< ", State=" << it->connection_state()
<< ", TimerState=" << it->timer_state();
if (it->local_ip_address().ToString() != device_ip_address ||
it->transmit_queue_value() == 0 ||
it->connection_state() != SocketInfo::kConnectionStateEstablished ||
(it->timer_state() != SocketInfo::kTimerStateRetransmitTimerPending &&
it->timer_state() !=
SocketInfo::kTimerStateZeroWindowProbeTimerPending)) {
SLOG(Link, 4) << "Connection Filtered.";
continue;
}
SLOG(Link, 3) << "Monitoring connection: TX=" << it->transmit_queue_value()
<< " TimerState=" << it->timer_state();
string local_ip_port =
StringPrintf("%s:%d",
it->local_ip_address().ToString().c_str(),
it->local_port());
(*tx_queue_lengths)[local_ip_port] = it->transmit_queue_value();
}
}
void TrafficMonitor::SampleTraffic() {
SLOG(Link, 2) << __func__;
vector<SocketInfo> socket_infos;
if (!socket_info_reader_->LoadTcpSocketInfo(&socket_infos) ||
socket_infos.empty()) {
SLOG(Link, 2) << __func__ << ": Empty socket info";
accummulated_failure_samples_ = 0;
return;
}
IPPortToTxQueueLengthMap curr_tx_queue_lengths;
BuildIPPortToTxQueueLength(socket_infos, &curr_tx_queue_lengths);
if (curr_tx_queue_lengths.empty()) {
SLOG(Link, 2) << __func__ << ": No interesting socket info";
accummulated_failure_samples_ = 0;
} else {
bool congested_tx_queues = true;
IPPortToTxQueueLengthMap::iterator old_tx_queue_it;
for (old_tx_queue_it = old_tx_queue_lengths_.begin();
old_tx_queue_it != old_tx_queue_lengths_.end();
++old_tx_queue_it) {
IPPortToTxQueueLengthMap::iterator curr_tx_queue_it =
curr_tx_queue_lengths.find(old_tx_queue_it->first);
if (curr_tx_queue_it == curr_tx_queue_lengths.end() ||
curr_tx_queue_it->second < old_tx_queue_it->second) {
congested_tx_queues = false;
break;
}
}
if (congested_tx_queues &&
++accummulated_failure_samples_ == kMinimumFailedSamplesToTrigger) {
LOG(WARNING) << "Congested tx queues detected, out-of-credits?";
outgoing_tcp_packets_not_routed_callback_.Run();
}
}
old_tx_queue_lengths_ = curr_tx_queue_lengths;
dispatcher_->PostDelayedTask(sample_traffic_callback_.callback(),
kSamplingIntervalMilliseconds);
}
} // namespace shill