File ifsys.cpp¶
Go to the documentation of this file
/*
*
* Copyright 2016 CUBRID Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/*
* ifsys.cpp
*/
#include <vector>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>
#include <cstring>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
#include "ifsys.hpp"
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"
namespace cubbase
{
bool ifsys::file_exists (const std::string &path)
{
struct stat st;
return ::stat (path.c_str (), &st) == 0;
}
std::vector<std::string> ifsys::list_entries (const std::string &path)
{
std::vector<std::string> out;
struct dirent *e;
DIR *dir;
std::string name;
if ((dir = opendir (path.c_str ())))
{
while ((e = readdir (dir)))
{
name = e->d_name;
if (name == "." || name == "..")
{
continue;
}
out.push_back (name);
}
closedir (dir);
}
sort (out.begin (), out.end ());
return out;
}
std::vector<std::string> ifsys::list_dirs_with_prefix (const std::string &path, const std::string &prefix)
{
std::vector<std::string> out;
struct dirent *e;
DIR *dir;
std::string name;
if ((dir = opendir (path.c_str ())))
{
while ((e = readdir (dir)))
{
if (e->d_type != DT_DIR && e->d_type != DT_LNK)
{
continue;
}
name = e->d_name;
if (name == "." || name == "..")
{
continue;
}
if (!prefix.empty () && name.rfind (prefix, 0) != 0)
{
continue;
}
out.push_back (name);
}
closedir (dir);
}
sort (out.begin (), out.end ());
return out;
}
std::string ifsys::read_one_line (const std::string &path)
{
std::ifstream file (path);
std::string s;
if (file.good ())
{
getline (file, s);
}
return s;
}
unsigned long long ifsys::read_u64 (const std::string &path)
{
std::ifstream file (path);
unsigned long long v;
v = 0;
if (file.good ())
{
file >> v;
}
return v;
}
int ifsys::write_text (const char *path, const char *text)
{
FILE *file;
int success;
file = fopen (path, "w");
if (!file)
{
return -1;
}
success = (fprintf (file, "%s\n", text) >= 0) ? 0 : -1;
fclose (file);
return success;
}
int ifsys::write_int (const char *path, long long int v)
{
char buf[64];
snprintf (buf, sizeof (buf), "%lld", v);
return write_text (path, buf);
}
char *ifsys::cpumask_hex_for_single_cpu (int cpu)
{
unsigned int v;
std::size_t cap;
char tmp[16];
char *buf;
int words;
int i;
words = cpu / 32 + 1;
cap = (size_t) words * 9 + 1;
buf = (char *) malloc (cap);
if (!buf)
{
return NULL;
}
buf[0] = 0;
for (i = words - 1; i >= 0; i--)
{
v = (i == cpu / 32) ? (1u << (cpu % 32)) : 0u;
snprintf (tmp, sizeof (tmp), "%08x", v);
if (buf[0] != '\0')
{
strncat (buf, ",", cap - strlen (buf) - 1);
}
strncat (buf, tmp, cap - strlen (buf) - 1);
}
return buf;
}
bool ifsys::name_blacklisted (const std::string &ifname)
{
return ifname == "lo" ||
ifname.rfind ("docker",0) == 0 ||
ifname.rfind ("veth",0) == 0 ||
ifname.rfind ("br-",0) == 0 ||
ifname.rfind ("virbr",0) == 0 ||
ifname.rfind ("tun",0) == 0 ||
ifname.rfind ("tap",0) == 0 ||
ifname.rfind ("wg",0) == 0 ||
ifname.rfind ("podman",0) == 0 ||
ifname.rfind ("cni",0) == 0 ||
ifname.rfind ("flannel",0) == 0 ||
ifname.rfind ("cilium",0) == 0;
}
bool ifsys::is_physical_iface (const std::string &ifname)
{
return file_exists ("/sys/class/net/" + ifname + "/device");
}
bool ifsys::is_up (const std::string &ifname)
{
std::string s;
s = read_one_line ("/sys/class/net/" + ifname + "/operstate");
return (s == "up");
}
int ifsys::rx_queue_count (const std::string &ifname)
{
return list_dirs_with_prefix ("/sys/class/net/" + ifname + "/queues", "rx-").size ();
}
unsigned long long ifsys::traffic_score (const std::string &ifname)
{
unsigned long long rx, tx;
rx = read_u64 ("/sys/class/net/" + ifname + "/statistics/rx_packets");
tx = read_u64 ("/sys/class/net/" + ifname + "/statistics/tx_packets");
return rx + tx;
}
int ifsys::listdir_count_prefix (const char *path, const char *prefix)
{
struct dirent *e;
DIR *dir;
int length;
int count;
dir = opendir (path);
if (!dir)
{
return 0;
}
count = 0;
length = prefix ? strlen (prefix) : 0;
while ((e = readdir (dir)))
{
if (strcmp (e->d_name, ".") == 0 || strcmp (e->d_name, "..") == 0)
{
continue;
}
if (prefix && strncmp (e->d_name, prefix, length) != 0)
{
continue;
}
if (e->d_type == DT_DIR || e->d_type == DT_LNK)
{
count++;
}
}
closedir (dir);
return count;
}
std::string ifsys::auto_select_primary_iface ()
{
std::vector<std::string> ifs = list_entries ("/sys/class/net");
unsigned long long best_score = 0;
unsigned long long score;
std::string best;
for (auto &ifname : ifs)
{
if (name_blacklisted (ifname))
{
continue;
}
if (!is_physical_iface (ifname))
{
continue;
}
if (!is_up (ifname))
{
continue;
}
if (rx_queue_count (ifname) == 0)
{
continue;
}
/* TODO: use multiple NIC */
score = traffic_score (ifname);
if (score > best_score)
{
best = ifname;
best_score = score;
}
}
if (best.empty())
{
for (auto &ifname : ifs)
{
if (name_blacklisted (ifname))
{
continue;
}
if (!is_physical_iface (ifname))
{
continue;
}
if (!is_up (ifname))
{
continue;
}
best = ifname;
break;
}
}
return best;
}
void ifsys::qirq_push (struct qirq_vec *a, int q, int irq)
{
int ncap;
struct qirq *nv;
if (a->n == a->cap)
{
ncap = a->cap ? a->cap * 2 : 32;
nv = (struct qirq *) realloc (a->v, ncap * sizeof (*nv));
if (!nv)
{
return;
}
a->v = nv;
a->cap = ncap;
}
a->v[a->n].q = q;
a->v[a->n].irq = irq;
a->n++;
}
int ifsys::parse_queue_suffix (const char *s)
{
int length;
int i, j;
length = (int) strlen (s);
i = length - 1;
while (i >= 0 && isspace ((unsigned char) s[i]))
{
i--;
}
if (i < 0)
{
return -1;
}
j = i;
while (j >= 0 && isdigit ((unsigned char) s[j]))
{
j--;
}
if (j == i)
{
return -1;
}
return atoi (s + j + 1);
}
int ifsys::find_irqs_for_iface (const char *ifname, struct qirq_vec *out)
{
FILE *file;
struct qirq tmp;
char line[4096];
char *last, *save, *tok;
char *p, *colon;
int q, w;
int irq;
file = fopen ("/proc/interrupts", "r");
if (!file)
{
return -1;
}
while (fgets (line, sizeof (line), file))
{
p = line;
while (isspace ((unsigned char) *p))
{
p++;
}
if (!isdigit ((unsigned char) *p))
{
continue;
}
irq = atoi (p);
colon = strchr (p, ':');
if (!colon)
{
continue;
}
if (!strstr (line, ifname))
{
continue;
}
last = NULL;
save = NULL;
tok = strtok_r (line, " \t\n", &save);
while (tok)
{
last = tok;
tok = strtok_r (NULL, " \t\n", &save);
}
if (!last)
{
continue;
}
q = parse_queue_suffix (last);
if (q < 0)
{
continue;
}
qirq_push (out, q, irq);
}
fclose (file);
if (out->n > 1)
{
for (int i=0; i<out->n; i++)
{
for (int j=i+1; j<out->n; j++)
{
if (out->v[j].q < out->v[i].q)
{
tmp = out->v[i];
out->v[i]=out->v[j];
out->v[j]=tmp;
}
}
}
w = 0;
for (int i = 0; i < out->n; i++)
{
if (w == 0 || out->v[i].q != out->v[w-1].q)
{
out->v[w++] = out->v[i];
}
}
out->n = w;
}
return 0;
}
int ifsys::set_irq_affinity_list (int irq, int cpu)
{
char path[256], buf[32];
char *mask;
int success;
snprintf (path, sizeof (path), "/proc/irq/%d/smp_affinity_list", irq);
snprintf (buf, sizeof (buf), "%d", cpu);
if (!write_text (path, buf))
{
return 0;
}
mask = cpumask_hex_for_single_cpu (cpu);
if (!mask)
{
return -1;
}
snprintf (path, sizeof (path), "/proc/irq/%d/smp_affinity", irq);
success = write_text (path, mask);
free (mask);
return success;
}
bool ifsys::set_rps_for_queue (const char *ifname, int rxq, int cpu)
{
char p1[320], p2[320];
char base[256];
char *mask;
snprintf (base, sizeof (base), "/sys/class/net/%s/queues/rx-%d", ifname, rxq);
snprintf (p1, sizeof (p1), "%s/rps_cpus", base);
snprintf (p2, sizeof (p2), "%s/rps_flow_cnt", base);
mask = cpumask_hex_for_single_cpu (cpu);
if (!mask)
{
return false;
}
if (write_text (p1, mask) != 0)
{
return false;
}
/* TODO: is it better to turn this off? */
//if (write_text (p2, "4096") != 0)
if (write_text (p2, "0") != 0)
{
return false;
}
free (mask);
return true;
}
bool ifsys::set_xps_for_queue (const char *ifname, int txq, int cpu)
{
char path[256];
char *mask;
mask = cpumask_hex_for_single_cpu (cpu);
snprintf (path, sizeof (path), "/sys/class/net/%s/queues/tx-%d/xps_cpus", ifname, txq);
if (!mask)
{
return false;
}
if (write_text (path, mask) != 0)
{
return false;
}
free (mask);
return true;
}
void ifsys::maybe_set_rps_sock_flow_entries (int ncores)
{
const char *path = "/proc/sys/net/core/rps_sock_flow_entries";
if (access (path, W_OK) != 0)
{
return;
}
/* TODO: is it better to turn this off? */
//write_int (path, (long long int) ncores * 4096);
write_int (path, 0);
}
}