irqbalance/bugfix-force-irq-into-rebalance-list-when-irq-removed-and-reinserted.patch
2019-12-25 22:08:07 +08:00

472 lines
13 KiB
Diff

From 1ca314651ddc31cd52ef67893fdd7aac43ea5201 Mon Sep 17 00:00:00 2001
From: zhengshaoyu <zhengshaoyu@huawei.com>
Date: Thu, 22 Jun 2017 04:39:00 +0000
Subject: [PATCH] irqbalance: bugfix popen and pclose
[Changelog]: bugfix popen and pclose
[Author]:zhengshaoyu
Date: Sun, 17 Mar 2019 20:07:28 -0400
---
classify.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++-------
irqbalance.c | 2 +-
irqbalance.h | 5 +++
procinterrupts.c | 112 +++++++++++++++++++++++++++++++------------------
types.h | 1 +
5 files changed, 190 insertions(+), 56 deletions(-)
diff --git a/classify.c b/classify.c
index 9868633..30a8d2a 100644
--- a/classify.c
+++ b/classify.c
@@ -66,6 +66,8 @@ struct pci_info {
#define PCI_SUB_DEVICE_EMC_0568 0x0568
#define PCI_SUB_DEVICE_EMC_dd00 0xdd00
+extern void force_rebalance_irq(struct irq_info *info, void *data __attribute__((unused)));
+
/*
* Apply software workarounds for some special devices
*
@@ -562,11 +564,13 @@ static int check_for_irq_ban(char *path __attribute__((unused)), int irq, GList
/*
* Check to see if we banned module which the irq belongs to.
*/
- entry = g_list_find_custom(proc_interrupts, &find, compare_ints);
- if (entry) {
- res = entry->data;
- if (check_for_module_ban(res->name))
- return 1;
+ if (proc_interrupts) {
+ entry = g_list_find_custom(proc_interrupts, &find, compare_ints);
+ if (entry) {
+ res = entry->data;
+ if (check_for_module_ban(res->name))
+ return 1;
+ }
}
#ifdef INCLUDE_BANSCRIPT
@@ -605,13 +609,14 @@ static int check_for_irq_ban(char *path __attribute__((unused)), int irq, GList
/*
* Figures out which interrupt(s) relate to the device we"re looking at in dirname
*/
-static void build_one_dev_entry(const char *dirname, GList *tmp_irqs)
+struct irq_info *build_one_dev_entry(const char *dirname, GList *tmp_list)
{
struct dirent *entry;
DIR *msidir;
FILE *fd;
int irqnum;
- struct irq_info *new, hint;
+ struct irq_info *new = NULL;
+ struct irq_info hint;
char path[PATH_MAX];
char devpath[PATH_MAX];
struct user_irq_policy pol;
@@ -635,7 +640,7 @@ static void build_one_dev_entry(const char *dirname, GList *tmp_irqs)
if (new)
continue;
get_irq_user_policy(devpath, irqnum, &pol);
- if ((pol.ban == 1) || (check_for_irq_ban(devpath, irqnum, tmp_irqs))) {
+ if ((pol.ban == 1) || (check_for_irq_ban(devpath, irqnum, tmp_list))) {
add_banned_irq(irqnum, &banned_irqs);
continue;
}
@@ -647,13 +652,13 @@ static void build_one_dev_entry(const char *dirname, GList *tmp_irqs)
}
} while (entry != NULL);
closedir(msidir);
- return;
+ return new;
}
sprintf(path, "%s/%s/irq", SYSPCI_DIR, dirname);
fd = fopen(path, "r");
if (!fd)
- return;
+ return new;
if (fscanf(fd, "%d", &irqnum) < 0)
goto done;
@@ -670,7 +675,7 @@ static void build_one_dev_entry(const char *dirname, GList *tmp_irqs)
if (new)
goto done;
get_irq_user_policy(devpath, irqnum, &pol);
- if ((pol.ban == 1) || (check_for_irq_ban(path, irqnum, tmp_irqs))) {
+ if ((pol.ban == 1) || (check_for_irq_ban(path, irqnum, tmp_list))) {
add_banned_irq(irqnum, &banned_irqs);
goto done;
}
@@ -684,7 +689,56 @@ static void build_one_dev_entry(const char *dirname, GList *tmp_irqs)
done:
fclose(fd);
- return;
+ return new;
+}
+
+void find_irq_dev_path(int irq, char *dirname, int length)
+{
+ char cmd[PATH_MAX + 128];
+ FILE *output = NULL;
+ char path[PATH_MAX];
+ char buffer[128];
+ char *brc = NULL;
+
+ memset(dirname, 0, length);
+ /* Return defaults if irq is 0 */
+ if (!irq)
+ return;
+
+ sprintf(path, "%s/*/msi_irqs", SYSPCI_DIR);
+ sprintf(cmd, "exec find %s -type f -name %d | awk -F '/' '{print $6}' ", path, irq);
+ output = popen(cmd, "r");
+ if (!output) {
+ log(TO_CONSOLE, LOG_WARNING, "Unable to execute IRQ %d path %s\n", irq, path);
+ return;
+ }
+
+ brc = fgets(buffer, 128, output);
+ if (brc) {
+ log(TO_CONSOLE, LOG_INFO, "msi_irqs IRQ %d dirname is %s\n", irq, brc);
+ strncpy(dirname, brc, length);
+ pclose(output);
+ return;
+ }
+ pclose(output);
+
+ sprintf(path, "%s/*/irq", SYSPCI_DIR);
+ sprintf(cmd, "exec grep -w %d %s | awk -F '/' '{print $6}' ", irq, path);
+ output = popen(cmd, "r");
+ if (!output) {
+ log(TO_CONSOLE, LOG_WARNING, "Unable to execute IRQ %d path %s\n", irq, path);
+ return;
+ }
+
+ brc = fgets(buffer, 128, output);
+ if (brc) {
+ log(TO_CONSOLE, LOG_INFO, "IRQ %d dirname is %s\n", irq, brc);
+ strncpy(dirname, brc, length);
+ pclose(output);
+ return;
+ }
+ pclose(output);
+
}
static void free_irq(struct irq_info *info, void *data __attribute__((unused)))
@@ -692,6 +750,42 @@ static void free_irq(struct irq_info *info, void *data __attribute__((unused)))
free(info);
}
+static void remove_no_existing_irq(struct irq_info *info, void *data __attribute__((unused)))
+{
+ GList *entry = NULL;
+
+ if (info->existing) {
+ info->existing = 0;
+ return;
+ }
+
+ entry = g_list_find_custom(interrupts_db, info, compare_ints);
+ if (entry)
+ interrupts_db = g_list_delete_link(interrupts_db, entry);
+
+ entry = g_list_find_custom(banned_irqs, info, compare_ints);
+ if (entry)
+ banned_irqs = g_list_delete_link(banned_irqs, entry);
+
+ entry = g_list_find_custom(rebalance_irq_list, info, compare_ints);
+ if (entry)
+ rebalance_irq_list = g_list_delete_link(rebalance_irq_list, entry);
+
+ if(info->assigned_obj) {
+ entry = g_list_find_custom(info->assigned_obj->interrupts, info, compare_ints);
+ if (entry) {
+ info->assigned_obj->interrupts = g_list_delete_link(info->assigned_obj->interrupts, entry);
+ }
+ }
+ log(TO_CONSOLE, LOG_INFO, "IRQ %d is removed from interrupts_db.\n", info->irq);
+ free_irq(info, NULL);
+}
+
+void clear_no_existing_irqs(void)
+{
+ for_each_irq(NULL, remove_no_existing_irq, NULL);
+}
+
void free_irq_db(void)
{
for_each_irq(NULL, free_irq, NULL);
@@ -711,14 +805,14 @@ void free_cl_opts(void)
g_list_free(banned_irqs);
}
-static void add_new_irq(int irq, struct irq_info *hint, GList *proc_interrupts)
+struct irq_info *add_new_irq(int irq, struct irq_info *hint, GList *proc_interrupts)
{
- struct irq_info *new;
+ struct irq_info *new = NULL;
struct user_irq_policy pol;
new = get_irq_info(irq);
if (new)
- return;
+ return new;
/* Set NULL devpath for the irq has no sysfs entries */
get_irq_user_policy(NULL, irq, &pol);
@@ -730,6 +824,8 @@ static void add_new_irq(int irq, struct irq_info *hint, GList *proc_interrupts)
if (!new)
log(TO_CONSOLE, LOG_WARNING, "add_new_irq: Failed to add irq %d\n", irq);
+
+ return new;
}
static void add_missing_irq(struct irq_info *info, void *attr)
diff --git a/irqbalance.c b/irqbalance.c
index 2f699b8..e375a1a 100644
--- a/irqbalance.c
+++ b/irqbalance.c
@@ -238,7 +238,7 @@ static void dump_object_tree(void)
for_each_object(numa_nodes, dump_numa_node_info, NULL);
}
-static void force_rebalance_irq(struct irq_info *info, void *data __attribute__((unused)))
+void force_rebalance_irq(struct irq_info *info, void *data __attribute__((unused)))
{
if (info->level == BALANCE_NONE)
return;
diff --git a/irqbalance.h b/irqbalance.h
index 8d5b329..73737ed 100644
--- a/irqbalance.h
+++ b/irqbalance.h
@@ -107,6 +107,10 @@ extern void free_cl_opts(void);
extern void add_cl_banned_module(char *modname);
#define irq_numa_node(irq) ((irq)->numa_node)
+extern struct irq_info *build_one_dev_entry(const char *dirname, GList *tmp_list);
+extern void find_irq_dev_path(int irq, char *dirname, int length);
+extern struct irq_info *add_new_irq(int irq, struct irq_info *hint, GList *proc_interrupts);
+extern void clear_no_existing_irqs(void);
/*
* Generic object functions
diff --git a/procinterrupts.c b/procinterrupts.c
index eb84a1c..d384860 100644
--- a/procinterrupts.c
+++ b/procinterrupts.c
@@ -142,6 +142,52 @@ static void guess_arm_irq_hints(char *name, struct irq_info *info)
}
#endif
+static void init_irq_class_and_type(char *savedline, struct irq_info *info, int irq) {
+ char *irq_name = NULL;
+ char *irq_mod = NULL;
+ char *savedptr = NULL;
+ char *last_token = NULL;
+ char *p = NULL;
+ int is_xen_dyn = 0;
+#ifdef AARCH64
+ char *tmp = NULL;
+#endif
+
+ irq_name = strtok_r(savedline, " ", &savedptr);
+ if (strstr(irq_name, "xen-dyn") != NULL)
+ is_xen_dyn = 1;
+ last_token = strtok_r(NULL, " ", &savedptr);
+ while ((p = strtok_r(NULL, " ", &savedptr))) {
+ irq_name = last_token;
+ if (strstr(irq_name, "xen-dyn") != NULL)
+ is_xen_dyn = 1;
+ last_token = p;
+ }
+
+#ifdef AARCH64
+ /* Of course the formatting for /proc/interrupts is different on different arches */
+ irq_name = last_token;
+ tmp = strchr(irq_name, '\n');
+ if (tmp)
+ *tmp = 0;
+#endif
+ irq_mod = last_token;
+ info->irq = irq;
+
+ if (strstr(irq_name, "-event") != NULL && is_xen_dyn == 1) {
+ info->type = IRQ_TYPE_VIRT_EVENT;
+ info->class = IRQ_VIRT_EVENT;
+ } else {
+#ifdef AARCH64
+ guess_arm_irq_hints(irq_name, info);
+#else
+ info->type = IRQ_TYPE_LEGACY;
+ info->class = IRQ_OTHER;
+#endif
+ }
+ info->name = strdupa(irq_mod);
+}
+
GList* collect_full_irq_list()
{
@@ -149,10 +187,6 @@ GList* collect_full_irq_list()
FILE *file;
char *line = NULL;
size_t size = 0;
- char *irq_name, *irq_mod, *savedptr, *last_token, *p;
-#ifdef AARCH64
- char *tmp;
-#endif
file = fopen("/proc/interrupts", "r");
if (!file)
@@ -168,7 +206,6 @@ GList* collect_full_irq_list()
while (!feof(file)) {
int number;
- int is_xen_dyn = 0;
struct irq_info *info;
char *c;
char *savedline = NULL;
@@ -188,45 +222,13 @@ GList* collect_full_irq_list()
savedline = strdup(line);
if (!savedline)
break;
- irq_name = strtok_r(savedline, " ", &savedptr);
- if (strstr(irq_name, "xen-dyn") != NULL)
- is_xen_dyn = 1;
- last_token = strtok_r(NULL, " ", &savedptr);
- while ((p = strtok_r(NULL, " ", &savedptr))) {
- irq_name = last_token;
- if (strstr(irq_name, "xen-dyn") != NULL)
- is_xen_dyn = 1;
- last_token = p;
- }
-
-#ifdef AARCH64
- /* Of course the formatting for /proc/interrupts is different on different arches */
- irq_name = last_token;
- tmp = strchr(irq_name, '\n');
- if (tmp)
- *tmp = 0;
-#endif
- irq_mod = last_token;
-
*c = 0;
c++;
number = strtoul(line, NULL, 10);
info = calloc(sizeof(struct irq_info), 1);
if (info) {
- info->irq = number;
- if (strstr(irq_name, "-event") != NULL && is_xen_dyn == 1) {
- info->type = IRQ_TYPE_VIRT_EVENT;
- info->class = IRQ_VIRT_EVENT;
- } else {
-#ifdef AARCH64
- guess_arm_irq_hints(irq_name, info);
-#else
- info->type = IRQ_TYPE_LEGACY;
- info->class = IRQ_OTHER;
-#endif
- }
- info->name = strdup(irq_mod);
+ init_irq_class_and_type(savedline, info, number);
tmp_list = g_list_append(tmp_list, info);
}
free(savedline);
@@ -230,6 +235,14 @@ GList* collect_full_irq_list()
return tmp_list;
}
+/* parsing /proc/interrrupts to detect whether removed and reinserted IRQ
+* device happened or not. If yes, then IRQs have to be rescanning again;
+* However, in order to keep no impact on online running IRQs performance stable,
+* removed and reinserted IRQ added back into rebalance_irq_list,
+* for irq load re-calculation instead of trigger rescanning all IRQs.
+* specially, when a new IRQ is detected, it has to be checked what devpath is,
+* then it is added into database accordingly.
+*/
void parse_proc_interrupts(void)
{
FILE *file;
@@ -253,7 +266,9 @@ void parse_proc_interrupts(void)
uint64_t count;
char *c, *c2;
struct irq_info *info;
+ struct irq_info tmp_info;
char savedline[1024];
+ char dirname[PATH_MAX] = {'\0'};
if (getline(&line, &size, file)==0)
break;
@@ -281,9 +296,24 @@ void parse_proc_interrupts(void)
info = get_irq_info(number);
if (!info) {
- need_rescan = 1;
- break;
+ find_irq_dev_path(number, dirname, PATH_MAX);
+ if (strlen(dirname) > 0) {
+ info = build_one_dev_entry(dirname, NULL);
+ log(TO_CONSOLE, LOG_INFO, "new IRQ %d added into database, dirname %s\n", number, dirname);
+ } else {
+ init_irq_class_and_type(savedline, &tmp_info, number);
+ info = add_new_irq(number, &tmp_info, NULL);
+ }
+
+ if (info) {
+ force_rebalance_irq(info, NULL);
+ log(TO_CONSOLE, LOG_INFO, "new IRQ %d added into rebalance list\n", number);
+ } else {
+ need_rescan = 1;
+ break;
+ }
}
+ info->existing = 1;
count = 0;
cpunr = 0;
@@ -307,17 +337,19 @@ void parse_proc_interrupts(void)
* cause an overflow and IRQ won't be rebalanced again
*/
if (count < info->irq_count) {
- need_rescan = 1;
- break;
+ log(TO_CONSOLE, LOG_INFO, "Removed and reinserted IRQ %d added into rebalance list\n", number);
+ force_rebalance_irq(info, NULL);
}
- info->last_irq_count = info->irq_count;
+ info->last_irq_count = info->irq_count;
info->irq_count = count;
/* is interrupt MSI based? */
if ((info->type == IRQ_TYPE_MSI) || (info->type == IRQ_TYPE_MSIX))
msi_found_in_sysfs = 1;
- }
+ }
+ clear_no_existing_irqs();
+
if ((proc_int_has_msi) && (!msi_found_in_sysfs) && (!need_rescan)) {
log(TO_ALL, LOG_WARNING, "WARNING: MSI interrupts found in /proc/interrupts\n");
log(TO_ALL, LOG_WARNING, "But none found in sysfs, you need to update your kernel\n");
diff --git a/types.h b/types.h
index a01d649..9693cf4 100644
--- a/types.h
+++ b/types.h
@@ -70,6 +70,7 @@ struct irq_info {
uint64_t last_irq_count;
uint64_t load;
int moved;
+ int existing;
struct topo_obj *assigned_obj;
char *name;
};
--
1.8.3.1