/* * 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 */ #include #include #include #include #include #include #include #include #include #include #include using namespace std; #include "device.h" #include "network.h" #include "../lib.h" #include "../parameters/parameters.h" #include "../process/process.h" extern "C" { #include "../tuning/iw.h" } #include #include #include #include #include static map nics; #ifdef DISABLE_TRYCATCH static inline void ethtool_cmd_speed_set(struct ethtool_cmd *ep, __u32 speed) { ep->speed = (__u16)speed; ep->speed_hi = (__u16)(speed >> 16); } static inline __u32 ethtool_cmd_speed(struct ethtool_cmd *ep) { return (ep->speed_hi << 16) | ep->speed; } #endif 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; pt_strcpy(sysfs_path, path); register_sysfs_path(sysfs_path); pt_strcpy(devname, _name); sprintf(humanname, "nic:%s", _name); pt_strcpy(name, devname); snprintf(devname, sizeof(devname), "%s-up", _name); index_up = get_param_index(devname); rindex_up = get_result_index(devname); snprintf(devname, sizeof(devname), "%s-powerunsave", _name); index_powerunsave = get_param_index(devname); rindex_powerunsave = get_result_index(devname); snprintf(devname, sizeof(devname), "%s-link-100", _name); index_link_100 = get_param_index(devname); rindex_link_100 = get_result_index(devname); snprintf(devname, sizeof(devname), "%s-link-1000", _name); index_link_1000 = get_param_index(devname); rindex_link_1000 = get_result_index(devname); snprintf(devname, sizeof(devname), "%s-link-high", _name); index_link_high = get_param_index(devname); rindex_link_high = get_result_index(devname); snprintf(devname, sizeof(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) { snprintf(humanname, sizeof(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; pt_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; pt_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; pt_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; } static void netdev_callback(const char *d_name) { char devname[128]; std::string f_name("/sys/class/net/"); if (strcmp(d_name, "lo") == 0) return; f_name.append(d_name); snprintf(devname, sizeof(devname), "%s-up", d_name); register_parameter(devname); snprintf(devname, sizeof(devname), "%s-powerunsave", d_name); register_parameter(devname); snprintf(devname, sizeof(devname), "%s-link-100", d_name); register_parameter(devname); snprintf(devname, sizeof(devname), "%s-link-1000", d_name); register_parameter(devname); snprintf(devname, sizeof(devname), "%s-link-high", d_name); register_parameter(devname); snprintf(devname, sizeof(devname), "%s-packets", d_name); register_parameter(devname); 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; }