blob: 9d20c5c1705a55e638594596c3c2e78053dad922 [file] [log] [blame]
/*
* Copyright 2010, Intel Corporation
*
* This file is part of PowerTOP
*
* This program file is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program in a file named COPYING; if not, write to the
* Free Software Foundation, Inc,
* 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
* or just google for it.
*
* Authors:
* Arjan van de Ven <arjan@linux.intel.com>
*/
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <map>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <libgen.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/ethtool.h>
using namespace std;
#include "device.h"
#include "network.h"
#include "../parameters/parameters.h"
#include "../process/process.h"
extern "C" {
#include "../tuning/iw.h"
}
#include <string.h>
#include <net/if.h>
#include <linux/sockios.h>
#include <sys/ioctl.h>
#include <unistd.h>
static map<string, class network *> nics;
static void do_proc_net_dev(void)
{
static time_t last_time;
class network *dev;
ifstream file;
char line[4096];
char *c, *c2;
if (time(NULL) == last_time)
return;
last_time = time(NULL);
file.open("/proc/net/dev", ios::in);
if (!file)
return;
file.getline(line, 4096);
file.getline(line, 4096);
while (file) {
int i = 0;
unsigned long val = 0;
uint64_t pkt = 0;
file.getline(line, 4096);
c = strchr(line, ':');
if (!c)
continue;
*c = 0;
c2 = c +1;
c = line; while (c && *c == ' ') c++;
/* c now points to the name of the nic */
dev = nics[c];
if (!dev)
continue;
c = c2++;
while (c != c2 && strlen(c) > 0) {
c2 = c;
val = strtoull(c, &c, 10);
i++;
if (i == 2 || i == 10)
pkt += val;
}
dev->pkts = pkt;
}
file.close();
}
network::network(const char *_name, const char *path): device()
{
char line[4096];
std::string filename(path);
char devname[128];
start_up = 0;
end_up = 0;
start_speed = 0;
end_speed = 0;
start_pkts = 0;
end_pkts = 0;
pkts = 0;
valid_100 = -1;
valid_1000 = -1;
valid_high = -1;
valid_powerunsave = -1;
strncpy(sysfs_path, path, sizeof(sysfs_path));
register_sysfs_path(sysfs_path);
sprintf(devname, "%s", _name);
sprintf(humanname, "nic:%s", _name);
strncpy(name, devname, sizeof(name));
sprintf(devname, "%s-up", _name);
index_up = get_param_index(devname);
rindex_up = get_result_index(devname);
sprintf(devname, "%s-powerunsave", _name);
index_powerunsave = get_param_index(devname);
rindex_powerunsave = get_result_index(devname);
sprintf(devname, "%s-link-100", _name);
index_link_100 = get_param_index(devname);
rindex_link_100 = get_result_index(devname);
sprintf(devname, "%s-link-1000", _name);
index_link_1000 = get_param_index(devname);
rindex_link_1000 = get_result_index(devname);
sprintf(devname, "%s-link-high", _name);
index_link_high = get_param_index(devname);
rindex_link_high = get_result_index(devname);
sprintf(devname, "%s-packets", _name);
index_pkts = get_param_index(devname);
rindex_pkts = get_result_index(devname);
memset(line, 0, 4096);
filename.append("/device/driver");
if (readlink(filename.c_str(), line, 4096) > 0) {
sprintf(humanname, _("Network interface: %s (%s)"), _name, basename(line));
};
}
static int net_iface_up(const char *iface)
{
int sock;
struct ifreq ifr;
int ret;
memset(&ifr, 0, sizeof(struct ifreq));
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock<0)
return 0;
strcpy(ifr.ifr_name, iface);
/* Check if the interface is up */
ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
if (ret<0) {
close(sock);
return 0;
}
if (ifr.ifr_flags & (IFF_UP | IFF_RUNNING)) {
close(sock);
return 1;
}
close(sock);
return 0;
}
static int iface_link(const char *name)
{
int sock;
struct ifreq ifr;
struct ethtool_value cmd;
int link;
memset(&ifr, 0, sizeof(struct ifreq));
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock<0)
return 0;
strcpy(ifr.ifr_name, name);
memset(&cmd, 0, sizeof(cmd));
cmd.cmd = ETHTOOL_GLINK;
ifr.ifr_data = (caddr_t)&cmd;
ioctl(sock, SIOCETHTOOL, &ifr);
close(sock);
link = cmd.data;
return link;
}
static int iface_speed(const char *name)
{
int sock;
struct ifreq ifr;
struct ethtool_cmd cmd;
int speed;
memset(&ifr, 0, sizeof(struct ifreq));
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock<0)
return 0;
strcpy(ifr.ifr_name, name);
memset(&cmd, 0, sizeof(cmd));
cmd.cmd = ETHTOOL_GSET;
ifr.ifr_data = (caddr_t)&cmd;
ioctl(sock, SIOCETHTOOL, &ifr);
close(sock);
speed = ethtool_cmd_speed(&cmd);
if (speed > 0 && speed <= 100)
speed = 100;
if (speed > 100 && speed <= 1000)
speed = 1000;
if (speed == 65535 || !iface_link(name))
speed = 0; /* no link */
return speed;
}
void network::start_measurement(void)
{
start_up = 1;
start_speed = 0;
end_up = 1;
end_speed = 0;
start_speed = iface_speed(name);
start_up = net_iface_up(name);
do_proc_net_dev();
start_pkts = pkts;
gettimeofday(&before, NULL);
}
void network::end_measurement(void)
{
int u_100, u_1000, u_high, u_powerunsave;
gettimeofday(&after, NULL);
end_speed = iface_speed(name);
end_up = net_iface_up(name);
do_proc_net_dev();
end_pkts = pkts;
duration = (after.tv_sec - before.tv_sec) + (after.tv_usec - before.tv_usec) / 1000000.0;
u_100 = 0;
u_1000 = 0;
u_high = 0;
if (start_speed == 100)
u_100 += 50;
if (start_speed == 1000)
u_1000 += 50;
if (start_speed > 1000)
u_high += 50;
if (end_speed == 100)
u_100 += 50;
if (end_speed == 1000)
u_1000 += 50;
if (end_speed > 1000)
u_high += 50;
if (start_pkts > end_pkts)
end_pkts = start_pkts;
u_powerunsave = 100 - 100 * get_wifi_power_saving(name);
report_utilization(rindex_link_100, u_100);
report_utilization(rindex_link_1000, u_1000);
report_utilization(rindex_link_high, u_high);
report_utilization(rindex_up, (start_up+end_up) / 2.0);
report_utilization(rindex_pkts, (end_pkts - start_pkts)/(duration + 0.001));
report_utilization(rindex_powerunsave, u_powerunsave);
}
double network::utilization(void)
{
return (end_pkts - start_pkts) / (duration + 0.001);
}
const char * network::device_name(void)
{
return name;
}
void netdev_callback(const char *d_name)
{
std::string f_name("/sys/class/net/");
f_name.append(d_name);
network *bl = new(std::nothrow) class network(d_name, f_name.c_str());
if (bl) {
all_devices.push_back(bl);
nics[d_name] = bl;
}
}
void create_all_nics(callback fn)
{
if (!fn)
fn = &netdev_callback;
process_directory("/sys/class/net/", fn);
}
double network::power_usage(struct result_bundle *result, struct parameter_bundle *bundle)
{
double power;
double factor;
double util;
power = 0;
factor = get_parameter_value(index_up, bundle);
util = get_result_value(rindex_up, result);
power += util * factor;
if (valid_100 == -1) {
valid_100 = utilization_power_valid(rindex_link_100);
valid_1000 = utilization_power_valid(rindex_link_1000);
valid_high = utilization_power_valid(rindex_link_high);
valid_powerunsave = utilization_power_valid(rindex_powerunsave);
}
if (valid_100 > 0) {
factor = get_parameter_value(index_link_100, bundle);
util = get_result_value(rindex_link_100, result);
power += util * factor / 100;
}
if (valid_1000 > 0) {
factor = get_parameter_value(index_link_1000, bundle);
util = get_result_value(rindex_link_1000, result);
power += util * factor / 100;
}
if (valid_high > 0) {
factor = get_parameter_value(index_link_high, bundle);
util = get_result_value(rindex_link_high, result);
power += util * factor / 100;
}
if (valid_powerunsave > 0) {
factor = get_parameter_value(index_powerunsave, bundle);
util = get_result_value(rindex_powerunsave, result);
power += util * factor / 100;
}
factor = get_parameter_value(index_pkts, bundle);
util = get_result_value(rindex_pkts, result);
if (util > 5000)
util = 5000;
power += util * factor / 100;
return power;
}