irqbalance/network.c

207 lines
4.6 KiB
C

/*
* Copyright (C) 2006, Intel Corporation
*
* This file is part of irqbalance
*
* 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
*/
/*
* Due to NAPI, the actual number of interrupts for a network NIC is usually low
* even though the amount of work is high; this file is there to compensate for this
* by adding actual package counts to the calculated amount of work of interrupts
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/types.h>
/* some distros (Debian / SLES) ship a totally broken ethtool.h */
/* work around the breakage some */
#define u32 __u32
#define u16 __u16
#define u8 __u8
#define u64 __u64
#include <linux/ethtool.h>
#undef u8
#undef u16
#undef u32
#undef u64
#include <glib.h>
#include <net/if.h>
#include <linux/sockios.h>
#include <string.h>
#include <sys/ioctl.h>
#include <stdint.h>
#include "irqbalance.h"
struct nic {
char ethname[64];
int irq;
uint64_t prev_pkt;
int counter;
};
static GList *nics;
static int dev_to_irq(char *devname)
{
int sock, ret;
struct ifreq ifr;
struct ethtool_value ethtool;
struct ethtool_drvinfo driver;
FILE *file;
char *line = NULL;
size_t size;
int val;
char buffer[PATH_MAX];
memset(&ifr, 0, sizeof(struct ifreq));
memset(&ethtool, 0, sizeof(struct ethtool_value));
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock<0)
return 0;
strcpy(ifr.ifr_name, devname);
driver.cmd = ETHTOOL_GDRVINFO;
ifr.ifr_data = (void*) &driver;
ret = ioctl(sock, SIOCETHTOOL, &ifr);
close(sock);
if (ret<0)
return 0;
sprintf(buffer,"/sys/bus/pci/devices/%s/irq", driver.bus_info);
file = fopen(buffer, "r");
if (!file)
return 0;
if (getline(&line, &size, file)==0) {
free(line);
fclose(file);
return 0;
}
fclose(file);
val = 0;
if (line)
val = strtoul(line, NULL, 10);
free(line);
return val;
}
static struct nic *new_nic(char *name)
{
struct nic *nic;
nic = malloc(sizeof(struct nic));
if (!nic)
return NULL;
memset(nic, 0, sizeof(struct nic));
strcpy(nic->ethname, name);
nic->irq = dev_to_irq(name);
nics = g_list_append(nics, nic);
return nic;
}
static struct nic *find_nic(char *name)
{
GList *item;
struct nic *nic;
item = g_list_first(nics);
while (item) {
nic = item->data;
item = g_list_next(item);
if (strcmp(nic->ethname, name)==0) {
nic->counter++;
/* refresh irq information once in a while; ifup/down
* can make this info go stale over time
*/
if ((nic->counter % NIC_REFRESH_INTERVAL) == 0)
nic->irq = dev_to_irq(nic->ethname);
return nic;
}
}
nic = new_nic(name);
return nic;
}
void account_for_nic_stats(void)
{
struct nic *nic;
FILE *file;
char *line = NULL;
size_t size = 0;
file = fopen("/proc/net/dev", "r");
if (!file)
return;
/* first two lines are headers */
if (getline(&line, &size, file)==0) {
free(line);
return;
}
if (getline(&line, &size, file)==0) {
free(line);
return;
}
while (!feof(file)) {
uint64_t rxcount;
uint64_t txcount;
uint64_t delta;
int dummy;
char *c, *c2;
if (getline(&line, &size, file)==0)
break;
if (line==NULL)
break;
c = strchr(line, ':');
if (c==NULL) /* header line */
continue;
*c = 0;
c++;
c2 = &line[0];
while (*c2==' ') c2++;
nic = find_nic(c2);
if (!nic)
continue;
dummy = strtoul(c, &c, 10);
rxcount = strtoull(c, &c, 10);
dummy = strtoul(c, &c, 10);
dummy = strtoul(c, &c, 10);
dummy = strtoul(c, &c, 10);
dummy = strtoul(c, &c, 10);
dummy = strtoul(c, &c, 10);
dummy = strtoul(c, &c, 10);
dummy = strtoul(c, &c, 10);
txcount = strtoull(c, &c, 10);
delta = (txcount+rxcount-nic->prev_pkt)/2;
/* add the RX and TX packets to the irq count, but only for 50%;
many packets generate another IRQ anyway and we don't want to
overweigh this too much. Also limit this to 100.000 max */
if (delta>100000)
delta = 100000;
if (delta>0 && nic->prev_pkt != 0)
add_interrupt_count(nic->irq, delta, IRQ_ETH);
nic->prev_pkt = rxcount + txcount;
}
fclose(file);
free(line);
}