0f594a6f19
We need to do this to allow for the label placement on older gcc versions Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
452 lines
11 KiB
C
452 lines
11 KiB
C
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <netdb.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <sys/un.h>
|
|
#include <unistd.h>
|
|
#include <curses.h>
|
|
#include <ncurses.h>
|
|
#include "irqbalance-ui.h"
|
|
#include "ui.h"
|
|
#include "helpers.h"
|
|
|
|
|
|
int irqbalance_pid = -1;
|
|
GList *tree = NULL;
|
|
setup_t setup;
|
|
GMainLoop *main_loop;
|
|
int is_tree = 1;
|
|
static int default_bufsz = 8192;
|
|
|
|
struct msghdr * create_credentials_msg()
|
|
{
|
|
struct ucred *credentials = malloc(sizeof(struct ucred));
|
|
credentials->pid = getpid();
|
|
credentials->uid = geteuid();
|
|
credentials->gid = getegid();
|
|
|
|
struct msghdr *msg = malloc(sizeof(struct msghdr));
|
|
memset(msg, 0, sizeof(struct msghdr));
|
|
msg->msg_iovlen = 1;
|
|
msg->msg_control = malloc(CMSG_SPACE(sizeof(struct ucred)));
|
|
msg->msg_controllen = CMSG_SPACE(sizeof(struct ucred));
|
|
|
|
struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg);
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_CREDENTIALS;
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
|
|
memcpy(CMSG_DATA(cmsg), credentials, sizeof(struct ucred));
|
|
|
|
free(credentials);
|
|
return msg;
|
|
}
|
|
|
|
int init_connection()
|
|
{
|
|
struct sockaddr_un addr;
|
|
memset(&addr, 0, sizeof(struct sockaddr_un));
|
|
|
|
int socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
|
|
if(socket_fd < 0) {
|
|
perror("Error opening socket");
|
|
return 0;
|
|
}
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s%d.sock", SOCKET_TMPFS,
|
|
SOCKET_PATH, irqbalance_pid);
|
|
|
|
if(connect(socket_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
/* Try connect to abstract */
|
|
memset(&addr, 0, sizeof(struct sockaddr_un));
|
|
addr.sun_family = AF_UNIX;
|
|
if (connect(socket_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
close(socket_fd);
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
return socket_fd;
|
|
}
|
|
|
|
void send_settings(char *data)
|
|
{
|
|
/* Send "settings sleep X" to set sleep interval, "settings ban
|
|
* irqs X Y..." to ban IRQs from balancing,
|
|
* "settings cpus <banned_list>" to setup which CPUs are forbidden
|
|
* to handle IRQs
|
|
*/
|
|
int socket_fd = init_connection();
|
|
if(!socket_fd) {
|
|
return;
|
|
}
|
|
|
|
struct msghdr *msg = create_credentials_msg();
|
|
struct iovec iov;
|
|
iov.iov_base = (void *) data;
|
|
iov.iov_len = strlen(data);
|
|
msg->msg_iov = &iov;
|
|
sendmsg(socket_fd, msg, 0);
|
|
|
|
close(socket_fd);
|
|
free(msg->msg_control);
|
|
free(msg);
|
|
}
|
|
|
|
char * get_data(char *string)
|
|
{
|
|
/* Send "setup" to get sleep interval, banned IRQs and banned CPUs,
|
|
* "stats" to get CPU tree statistics
|
|
*/
|
|
int socket_fd;
|
|
|
|
try_again:
|
|
socket_fd = init_connection();
|
|
if(!socket_fd) {
|
|
return NULL;
|
|
}
|
|
|
|
struct msghdr *msg = create_credentials_msg();
|
|
struct iovec iov;
|
|
iov.iov_base = (void *) string;
|
|
iov.iov_len = strlen(string);
|
|
msg->msg_iov = &iov;
|
|
sendmsg(socket_fd, msg, 0);
|
|
|
|
char *data = malloc(default_bufsz);
|
|
int len = recv(socket_fd, data, default_bufsz, MSG_TRUNC);
|
|
close(socket_fd);
|
|
data[len] = '\0';
|
|
free(msg->msg_control);
|
|
free(msg);
|
|
if (len >= default_bufsz) {
|
|
/* msg was truncated, increase bufsz and try again */
|
|
default_bufsz += 8192;
|
|
free(data);
|
|
goto try_again;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
void parse_setup(char *setup_data)
|
|
{
|
|
char *token, *ptr;
|
|
int i,j;
|
|
char *copy;
|
|
irq_t *new_irq = NULL;
|
|
if((setup_data == NULL) || (strlen(setup_data) == 0)) return;
|
|
copy = strdup(setup_data);
|
|
if (!copy)
|
|
return;
|
|
|
|
setup.banned_irqs = NULL;
|
|
setup.banned_cpus = NULL;
|
|
token = strtok_r(copy, " ", &ptr);
|
|
if(strncmp(token, "SLEEP", strlen("SLEEP"))) goto out;
|
|
setup.sleep = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
|
|
token = strtok_r(NULL, " ", &ptr);
|
|
/* Parse banned IRQ data */
|
|
while(!strncmp(token, "IRQ", strlen("IRQ"))) {
|
|
new_irq = malloc(sizeof(irq_t));
|
|
new_irq->vector = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
|
|
token = strtok_r(NULL, " ", &ptr);
|
|
if(strncmp(token, "LOAD", strlen("LOAD"))) goto out;
|
|
new_irq->load = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
|
|
token = strtok_r(NULL, " ", &ptr);
|
|
if(strncmp(token, "DIFF", strlen("DIFF"))) goto out;
|
|
new_irq->diff = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
|
|
token = strtok_r(ptr, " ", &ptr);
|
|
if(strncmp(token, "CLASS", strlen("CLASS"))) goto out;
|
|
new_irq->class = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
|
|
new_irq->is_banned = 1;
|
|
new_irq->assigned_to = NULL;
|
|
setup.banned_irqs = g_list_append(setup.banned_irqs, new_irq);
|
|
token = strtok_r(NULL, " ", &ptr);
|
|
new_irq = NULL;
|
|
}
|
|
|
|
if(strncmp(token, "BANNED", strlen("BANNED"))) goto out;
|
|
token = strtok_r(NULL, " ", &ptr);
|
|
for(i = strlen(token) - 1; i >= 0; i--) {
|
|
char *map = hex_to_bitmap(token[i]);
|
|
for(j = 3; j >= 0; j--) {
|
|
if(map[j] == '1') {
|
|
uint64_t *banned_cpu = malloc(sizeof(uint64_t));
|
|
*banned_cpu = (4 * (strlen(token) - (i + 1)) + (4 - (j + 1)));
|
|
setup.banned_cpus = g_list_append(setup.banned_cpus,
|
|
banned_cpu);
|
|
}
|
|
}
|
|
free(map);
|
|
|
|
}
|
|
free(copy);
|
|
return;
|
|
|
|
out: {
|
|
/* Invalid data presented */
|
|
printf("Invalid data sent. Unexpected token: %s", token);
|
|
if (new_irq) {
|
|
free(new_irq);
|
|
}
|
|
free(copy);
|
|
g_list_free(tree);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
GList * concat_child_lists(cpu_node_t *node)
|
|
{
|
|
GList *new = NULL;
|
|
GList *child_entry = g_list_first(node->children);
|
|
do {
|
|
cpu_node_t *child = (cpu_node_t *)child_entry->data;
|
|
GList *cpu_entry = g_list_first(child->cpu_list);
|
|
do {
|
|
uint64_t *cpu = (uint64_t *)cpu_entry->data;
|
|
new = g_list_append(new, cpu);
|
|
cpu_entry = g_list_next(cpu_entry);
|
|
} while(cpu_entry != NULL);
|
|
child_entry = g_list_next(child_entry);
|
|
} while(child_entry != NULL);
|
|
|
|
return new;
|
|
}
|
|
|
|
void copy_cpu_list_to_irq(irq_t *irq, void *data)
|
|
{
|
|
irq->assigned_to = g_list_copy((GList *)data);
|
|
irq->assigned_to = g_list_sort(irq->assigned_to, sort_ints);
|
|
}
|
|
|
|
void assign_cpu_lists(cpu_node_t *node, void *data __attribute__((unused)))
|
|
{
|
|
if(g_list_length(node->children) > 0) {
|
|
for_each_node(node->children, assign_cpu_lists, NULL);
|
|
node->cpu_list = concat_child_lists(node);
|
|
} else {
|
|
node->cpu_list = g_list_append(node->cpu_list, &(node->number));
|
|
}
|
|
|
|
for_each_irq(node->irqs, copy_cpu_list_to_irq, node->cpu_list);
|
|
}
|
|
|
|
void assign_cpu_mask(cpu_node_t *node, void *data __attribute__((unused)))
|
|
{
|
|
char *mask = malloc(16 * sizeof(char));
|
|
mask[0] = '\0';
|
|
unsigned int sum = 0;
|
|
GList *list_entry = g_list_first(node->cpu_list);
|
|
do {
|
|
int *cpu = list_entry->data;
|
|
sum += 1 << (*cpu);
|
|
list_entry = g_list_next(list_entry);
|
|
} while(list_entry != NULL);
|
|
snprintf(mask, 15, "0x%x", sum);
|
|
node->cpu_mask = mask;
|
|
|
|
if(g_list_length(node->children) > 0) {
|
|
for_each_node(node->children, assign_cpu_mask, NULL);
|
|
}
|
|
}
|
|
|
|
void parse_into_tree(char *data)
|
|
{
|
|
char *token, *ptr;
|
|
cpu_node_t *parent = NULL;
|
|
char *copy;
|
|
tree = NULL;
|
|
irq_t *new_irq = NULL;
|
|
cpu_node_t *new = NULL;
|
|
|
|
if (!data || strlen(data) == 0)
|
|
return;
|
|
|
|
copy = strdup(data);
|
|
if (!copy)
|
|
return;
|
|
|
|
token = strtok_r(copy, " ", &ptr);
|
|
while(token != NULL) {
|
|
/* Parse node data */
|
|
if(strncmp(token, "TYPE", strlen("TYPE"))) {
|
|
free(copy);
|
|
goto out;
|
|
}
|
|
new = malloc(sizeof(cpu_node_t));
|
|
new->irqs = NULL;
|
|
new->children = NULL;
|
|
new->cpu_list = NULL;
|
|
new->cpu_mask = NULL;
|
|
new->type = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
|
|
if(new->type == OBJ_TYPE_NODE) {
|
|
parent = NULL;
|
|
} else if(new->type >= parent->type) {
|
|
parent = parent->parent;
|
|
}
|
|
token = strtok_r(NULL, " ", &ptr);
|
|
if(strncmp(token, "NUMBER", strlen("NUMBER"))) goto out;
|
|
new->number = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
|
|
token = strtok_r(NULL, " ", &ptr);
|
|
if(strncmp(token, "LOAD", strlen("LOAD"))) goto out;
|
|
new->load = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
|
|
token = strtok_r(NULL, " ", &ptr);
|
|
if(strncmp(token, "SAVE_MODE", strlen("SAVE_MODE"))) goto out;
|
|
new->is_powersave = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
|
|
token = strtok_r(NULL, " ", &ptr);
|
|
|
|
/* Parse assigned IRQ data */
|
|
while((token != NULL) && (!strncmp(token, "IRQ", strlen("IRQ")))) {
|
|
new_irq = malloc(sizeof(irq_t));
|
|
new_irq->vector = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
|
|
token = strtok_r(NULL, " ", &ptr);
|
|
if(strncmp(token, "LOAD", strlen("LOAD"))) goto out;
|
|
new_irq->load = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
|
|
token = strtok_r(NULL, " ", &ptr);
|
|
if(strncmp(token, "DIFF", strlen("DIFF"))) goto out;
|
|
new_irq->diff = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
|
|
token = strtok_r(NULL, " ", &ptr);
|
|
if(strncmp(token, "CLASS", strlen("CLASS"))) goto out;
|
|
new_irq->class = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
|
|
new_irq->is_banned = 0;
|
|
new->irqs = g_list_append(new->irqs, new_irq);
|
|
token = strtok_r(NULL, " ", &ptr);
|
|
new_irq = NULL;
|
|
}
|
|
|
|
if((token == NULL) || (strncmp(token, "IRQ", strlen("IRQ")))) {
|
|
new->parent = parent;
|
|
if(parent == NULL) {
|
|
tree = g_list_append(tree, new);
|
|
} else {
|
|
parent->children = g_list_append(parent->children, new);
|
|
}
|
|
if(new->type != OBJ_TYPE_CPU) {
|
|
parent = new;
|
|
}
|
|
}
|
|
|
|
new = NULL;
|
|
}
|
|
free(copy);
|
|
for_each_node(tree, assign_cpu_lists, NULL);
|
|
for_each_node(tree, assign_cpu_mask, NULL);
|
|
return;
|
|
|
|
out: {
|
|
/* Invalid data presented */
|
|
printf("Invalid data sent. Unexpected token: %s\n", token);
|
|
if (new_irq) {
|
|
free(new_irq);
|
|
}
|
|
if (new) {
|
|
free(new);
|
|
}
|
|
g_list_free(tree);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
gboolean rescan_tree(gpointer data __attribute__((unused)))
|
|
{
|
|
char *setup_data = get_data(SETUP);
|
|
parse_setup(setup_data);
|
|
char *irqbalance_data = get_data(STATS);
|
|
parse_into_tree(irqbalance_data);
|
|
if(is_tree) {
|
|
display_tree();
|
|
}
|
|
free(setup_data);
|
|
free(irqbalance_data);
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean key_loop(gpointer data __attribute__((unused)))
|
|
{
|
|
int c = getch();
|
|
switch(c) {
|
|
case 'q':
|
|
close_window(0);
|
|
break;
|
|
case KEY_F(3):
|
|
is_tree = 1;
|
|
display_tree();
|
|
break;
|
|
case KEY_F(4):
|
|
is_tree = 0;
|
|
settings();
|
|
break;
|
|
case KEY_F(5):
|
|
is_tree = 0;
|
|
setup_irqs();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
if(getuid() != 0) {
|
|
printf("This program needs to be executed with root privileges\n");
|
|
return EACCES;
|
|
}
|
|
if(argc > 1) {
|
|
/* PID of irqbalance specified */
|
|
irqbalance_pid = strtol(argv[1], NULL, 10);
|
|
if(!irqbalance_pid) {
|
|
printf("PID must be a number\n");
|
|
return EINVAL;
|
|
}
|
|
} else {
|
|
/* We need to find irqbalance's PID */
|
|
DIR *dir = opendir("/proc");
|
|
if(dir) {
|
|
struct dirent *entry;
|
|
char cmdfile[512];
|
|
char cmdstring[256];
|
|
cmdstring[255] = '\0';
|
|
do {
|
|
entry = readdir(dir);
|
|
if(entry) {
|
|
snprintf(cmdfile, 512, "/proc/%s/cmdline", entry->d_name);
|
|
FILE *f = fopen(cmdfile, "r");
|
|
if(f == NULL) {
|
|
continue;
|
|
}
|
|
fgets(cmdstring, 255, f);
|
|
if((strstr(cmdstring, "irqbalance") != NULL) &&
|
|
(strstr(cmdstring, "irqbalance-ui") == NULL)) {
|
|
irqbalance_pid = strtol(entry->d_name, NULL, 10);
|
|
}
|
|
fclose(f);
|
|
}
|
|
} while((entry) && (irqbalance_pid == -1));
|
|
closedir(dir);
|
|
}
|
|
if(irqbalance_pid == -1) {
|
|
printf("Unable to determine irqbalance PID\n");
|
|
return EINVAL;
|
|
}
|
|
}
|
|
|
|
init();
|
|
|
|
main_loop = g_main_loop_new(NULL, FALSE);
|
|
g_timeout_add_seconds(5, rescan_tree, NULL);
|
|
g_timeout_add_seconds(1, key_loop, NULL);
|
|
g_main_loop_run(main_loop);
|
|
|
|
|
|
g_main_loop_quit(main_loop);
|
|
close_window(0);
|
|
return 0;
|
|
}
|