From 6e2f297d49fd12d61915320564425a71776ec43d Mon Sep 17 00:00:00 2001
From: Tao Liu <ltao@redhat.com>
Date: Thu, 7 Nov 2024 10:03:21 +1300
Subject: [PATCH] Rebase to upstream commit (b4b6f194da)

Resolves: RHEL-58318

Signed-off-by: Tao Liu <ltao@redhat.com>
---
 ...eck-if-using-a-negative-index-of-buf.patch |   5 +-
 0002-Check-fflush-return-value.patch          |   4 +-
 0003-fix-32-bit-formats.patch                 |  69 ++++
 0004-add-void-to-fix-strict-prototypes.patch  | 320 ++++++++++++++++++
 0005-cast-void-pointer-to-actual-type.patch   |  28 ++
 ...-move-build-files-to-repository-root.patch | 113 +++++++
 ...-meson-bump-project-version-to-1.9.4.patch |  29 ++
 ...n-drop-redundant-install_man-options.patch |  50 +++
 ...eson-add-a-minimum-version-decorator.patch |  37 ++
 ...h => 0010-Drop-ProtectKernelTunables.patch |   4 +-
 ...son-replace-generic-array-with-files.patch |  44 +++
 0012-meson-use-find_library-for-numa.patch    |  26 ++
 0013-ui-change-void-to-char.patch             | 104 ++++++
 0014-clang-tidy-don-t-assign-in-if.patch      |  71 ++++
 0015-clang-tidy-properly-use-strncmp.patch    | 106 ++++++
 0016-replace-malloc-with-g_malloc0.patch      |  32 ++
 ...ang-tidy-don-t-use-else-after-return.patch | 151 +++++++++
 ...tidy-remove-return-in-void-functions.patch |  36 ++
 ...g-tidy-remove-redundant-declarations.patch |  27 ++
 ...-clang-tidy-remove-duplicate-include.patch |  27 ++
 0021-CI-add-meson-CI.patch                    |  92 +++++
 ...q-in-a-higher-level-utility-function.patch | 173 ++++++++++
 ...s-count-per-CPU-to-avoid-overflowing.patch | 195 +++++++++++
 ...to-curses-if-without-irqbalance-ui-i.patch |  43 +++
 ...s-space-causing-with-systemd-not-be-.patch |  26 ++
 0026-direct-initialize-msghdr-members.patch   |  38 +++
 0027-direct-initialize-iovec.patch            |  31 ++
 0028-clang-tidy-add-missing-free.patch        |  27 ++
 0029-clang-tidy-don-t-assign-in-if.patch      |  43 +++
 0030-clang-tidy-remove-pointless-casts.patch  |  37 ++
 0031-use-g_malloc-and-friends.patch           | 314 +++++++++++++++++
 0032-remove-malloc-from-ucred.patch           |  46 +++
 0033-gcc-analyzer-add-NULL-checks.patch       |  42 +++
 ...c-analyzer-increase-socket_name-size.patch |  30 ++
 0035-use-g_strdup_printf.patch                |  66 ++++
 ...d-malloc-with-create_credentials_msg.patch | 111 ++++++
 0037-conver-strncmp-to-g_str_has_prefix.patch | 268 +++++++++++++++
 ...E_ARGS-as-empty-string-to-squelch-sy.patch |  23 ++
 0039-Minor-punctuation-fix.patch              |  25 ++
 ...option-should-return-0-rather-than-1.patch |  41 +++
 ...to-CapabilityBoundingSet-in-irqbalan.patch |  46 +++
 0042-Check-info-moved-before-updating.patch   |  31 ++
 ...balance.1-a-b-a-b-it-s-type-its-type.patch |  38 +++
 ...d-of-EIO-when-try-setting-irq-affini.patch |  39 +++
 ...nce-1.9.0-environment-file-sysconfig.patch |   6 +-
 irqbalance.spec                               |  51 ++-
 46 files changed, 3151 insertions(+), 14 deletions(-)
 create mode 100644 0003-fix-32-bit-formats.patch
 create mode 100644 0004-add-void-to-fix-strict-prototypes.patch
 create mode 100644 0005-cast-void-pointer-to-actual-type.patch
 create mode 100644 0006-meson-move-build-files-to-repository-root.patch
 create mode 100644 0007-meson-bump-project-version-to-1.9.4.patch
 create mode 100644 0008-meson-drop-redundant-install_man-options.patch
 create mode 100644 0009-meson-add-a-minimum-version-decorator.patch
 rename 0003-Drop-ProtectKernelTunables.patch => 0010-Drop-ProtectKernelTunables.patch (91%)
 create mode 100644 0011-meson-replace-generic-array-with-files.patch
 create mode 100644 0012-meson-use-find_library-for-numa.patch
 create mode 100644 0013-ui-change-void-to-char.patch
 create mode 100644 0014-clang-tidy-don-t-assign-in-if.patch
 create mode 100644 0015-clang-tidy-properly-use-strncmp.patch
 create mode 100644 0016-replace-malloc-with-g_malloc0.patch
 create mode 100644 0017-clang-tidy-don-t-use-else-after-return.patch
 create mode 100644 0018-clang-tidy-remove-return-in-void-functions.patch
 create mode 100644 0019-clang-tidy-remove-redundant-declarations.patch
 create mode 100644 0020-clang-tidy-remove-duplicate-include.patch
 create mode 100644 0021-CI-add-meson-CI.patch
 create mode 100644 0022-Wrap-migrate_irq-in-a-higher-level-utility-function.patch
 create mode 100644 0023-Track-IRQ-slots-count-per-CPU-to-avoid-overflowing.patch
 create mode 100644 0024-Disable-linking-to-curses-if-without-irqbalance-ui-i.patch
 create mode 100644 0025-Remove-extraneous-space-causing-with-systemd-not-be-.patch
 create mode 100644 0026-direct-initialize-msghdr-members.patch
 create mode 100644 0027-direct-initialize-iovec.patch
 create mode 100644 0028-clang-tidy-add-missing-free.patch
 create mode 100644 0029-clang-tidy-don-t-assign-in-if.patch
 create mode 100644 0030-clang-tidy-remove-pointless-casts.patch
 create mode 100644 0031-use-g_malloc-and-friends.patch
 create mode 100644 0032-remove-malloc-from-ucred.patch
 create mode 100644 0033-gcc-analyzer-add-NULL-checks.patch
 create mode 100644 0034-gcc-analyzer-increase-socket_name-size.patch
 create mode 100644 0035-use-g_strdup_printf.patch
 create mode 100644 0036-avoid-malloc-with-create_credentials_msg.patch
 create mode 100644 0037-conver-strncmp-to-g_str_has_prefix.patch
 create mode 100644 0038-define-IRQBALANCE_ARGS-as-empty-string-to-squelch-sy.patch
 create mode 100644 0039-Minor-punctuation-fix.patch
 create mode 100644 0040-Version-option-should-return-0-rather-than-1.patch
 create mode 100644 0041-Add-CAP_SETPCAP-to-CapabilityBoundingSet-in-irqbalan.patch
 create mode 100644 0042-Check-info-moved-before-updating.patch
 create mode 100644 0043-irqbalance.1-a-b-a-b-it-s-type-its-type.patch
 create mode 100644 0044-Use-EPERM-instead-of-EIO-when-try-setting-irq-affini.patch

diff --git a/0001-irqbalance-ui-check-if-using-a-negative-index-of-buf.patch b/0001-irqbalance-ui-check-if-using-a-negative-index-of-buf.patch
index f1125bc..d7a7cf2 100644
--- a/0001-irqbalance-ui-check-if-using-a-negative-index-of-buf.patch
+++ b/0001-irqbalance-ui-check-if-using-a-negative-index-of-buf.patch
@@ -1,7 +1,8 @@
 From c0cd6149722ca525cf31a363dbe724689bef4d87 Mon Sep 17 00:00:00 2001
 From: Tao Liu <ltao@redhat.com>
 Date: Wed, 13 Mar 2024 14:30:48 +0800
-Subject: [PATCH 1/3] irqbalance-ui: check if using a negative index of buffer
+Subject: [PATCH 01/44] irqbalance-ui: check if using a negative index of
+ buffer
 
 A negative index will be used when recv() fails, which is unexpected for
 the data buffer. The issue was found by Static Application Security
@@ -34,5 +35,5 @@ index b7f9b62..c26eff6 100644
  		/* msg was truncated, increase bufsz and try again */
  		default_bufsz += 8192;
 -- 
-2.40.1
+2.47.0
 
diff --git a/0002-Check-fflush-return-value.patch b/0002-Check-fflush-return-value.patch
index baebfc8..1bf16b4 100644
--- a/0002-Check-fflush-return-value.patch
+++ b/0002-Check-fflush-return-value.patch
@@ -1,7 +1,7 @@
 From 8301666f3029ff4d9089a273a45ec47671d964c1 Mon Sep 17 00:00:00 2001
 From: Andrew Zaborowski <andrew.zaborowski@intel.com>
 Date: Fri, 29 Mar 2024 18:43:55 -0700
-Subject: [PATCH 2/3] Check fflush() return value
+Subject: [PATCH 02/44] Check fflush() return value
 
 Since fprintf() may buffer output, as noted in 470a64b19062, fclose()'s
 error value was also being checked for the write errors.  However in
@@ -37,5 +37,5 @@ index e30d0f0..0c1e7a1 100644
  	if (ret < 0)
  		goto error;
 -- 
-2.40.1
+2.47.0
 
diff --git a/0003-fix-32-bit-formats.patch b/0003-fix-32-bit-formats.patch
new file mode 100644
index 0000000..b689cf6
--- /dev/null
+++ b/0003-fix-32-bit-formats.patch
@@ -0,0 +1,69 @@
+From 1277ea524354fa628b4189e699af8d62f8be7021 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Sun, 31 Mar 2024 14:18:24 -0700
+Subject: [PATCH 03/44] fix 32-bit formats
+
+exposed with -Wformat when building on 32-bit systems
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ placement.c | 2 +-
+ ui/ui.c     | 8 ++++----
+ 2 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/placement.c b/placement.c
+index 9fde8cb..dea7c23 100644
+--- a/placement.c
++++ b/placement.c
+@@ -172,7 +172,7 @@ static void validate_irq(struct irq_info *info, void *data)
+ {
+ 	if (info->assigned_obj != data)
+ 		log(TO_CONSOLE, LOG_INFO, "object validation error: irq %d is wrong, points to %p, should be %p\n",
+-			info->irq, info->assigned_obj, data);
++			info->irq, (void*)info->assigned_obj, data);
+ }
+ 
+ static void validate_object(struct topo_obj *d, void *data __attribute__((unused)))
+diff --git a/ui/ui.c b/ui/ui.c
+index bee6868..8354fc6 100644
+--- a/ui/ui.c
++++ b/ui/ui.c
+@@ -101,7 +101,7 @@ int get_valid_sleep_input(int column_offest)
+ 		if(input == NULL) {
+ 			curs_set(0);
+ 			attrset(COLOR_PAIR(1));
+-			mvprintw(2, column_offest, "%lu			", new_sleep);
++			mvprintw(2, column_offest, "%" PRIu64 "			", new_sleep);
+ 			move(LINES, COLS);
+ 			break;
+ 		}
+@@ -125,7 +125,7 @@ int get_valid_sleep_input(int column_offest)
+ 	}
+ 
+ 	attrset(COLOR_PAIR(1));
+-	mvprintw(2, column_offest, "%lu				", new_sleep);
++	mvprintw(2, column_offest, "%" PRIu64 "				", new_sleep);
+ 
+ 	return new_sleep;
+ }
+@@ -296,7 +296,7 @@ void handle_cpu_banning()
+ 		case '\r': {
+ 			attrset(COLOR_PAIR(3));
+ 			int banned = toggle_cpu(tmp, position + offset - 6);
+-			mvprintw(position, 3, "CPU %d     ", position + offset - 6);
++			mvprintw(position, 3, "CPU %zu     ", position + offset - 6);
+ 			if(banned) {
+ 				mvprintw(position, 19, "YES");
+ 			} else {
+@@ -770,7 +770,7 @@ void display_tree_node_irqs(irq_t *irq, void *data)
+ 	if (max_offset >= offset && max_offset - offset < LINES - 5) {
+ 		snprintf(indent + strlen(indent), 32 - strlen(indent), "%s", (char *)data);
+ 		attrset(COLOR_PAIR(3));
+-		printw("%sIRQ %u, IRQs since last rebalance %lu\n",
++		printw("%sIRQ %u, IRQs since last rebalance %" PRIu64 "\n",
+ 			indent, irq->vector, irq->diff);
+ 	}
+ 	max_offset++;
+-- 
+2.47.0
+
diff --git a/0004-add-void-to-fix-strict-prototypes.patch b/0004-add-void-to-fix-strict-prototypes.patch
new file mode 100644
index 0000000..a3a679b
--- /dev/null
+++ b/0004-add-void-to-fix-strict-prototypes.patch
@@ -0,0 +1,320 @@
+From b6a831d692ed7e12db7748db49b3b39516d151d2 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Sun, 31 Mar 2024 14:31:22 -0700
+Subject: [PATCH 04/44] add void to fix strict-prototypes
+
+This becomes a hard error with C23
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ irqbalance.c       |  2 +-
+ irqbalance.h       |  2 +-
+ procinterrupts.c   |  2 +-
+ ui/helpers.c       |  2 +-
+ ui/helpers.h       |  2 +-
+ ui/irqbalance-ui.c |  6 +++---
+ ui/irqbalance-ui.h |  5 ++---
+ ui/ui.c            | 26 +++++++++++++-------------
+ ui/ui.h            | 28 ++++++++++++++--------------
+ 9 files changed, 37 insertions(+), 38 deletions(-)
+
+diff --git a/irqbalance.c b/irqbalance.c
+index 12302d7..373161f 100644
+--- a/irqbalance.c
++++ b/irqbalance.c
+@@ -544,7 +544,7 @@ out:
+ 	return TRUE;
+ }
+ 
+-int init_socket()
++int init_socket(void)
+ {
+ 	struct sockaddr_un addr;
+ 	memset(&addr, 0, sizeof(struct sockaddr_un));
+diff --git a/irqbalance.h b/irqbalance.h
+index 7b47cd1..09daa3d 100644
+--- a/irqbalance.h
++++ b/irqbalance.h
+@@ -36,7 +36,7 @@ extern char *classes[];
+ extern void parse_cpu_tree(void);
+ extern void clear_work_stats(void);
+ extern void parse_proc_interrupts(void);
+-extern GList* collect_full_irq_list();
++extern GList* collect_full_irq_list(void);
+ extern void parse_proc_stat(void);
+ extern void set_interrupt_count(int number, uint64_t count);
+ extern void set_msi_interrupt_numa(int number);
+diff --git a/procinterrupts.c b/procinterrupts.c
+index dfa95c6..e7ba653 100644
+--- a/procinterrupts.c
++++ b/procinterrupts.c
+@@ -206,7 +206,7 @@ void init_irq_class_and_type(char *savedline, struct irq_info *info, int irq)
+ 	info->name = strdup(irq_fullname);
+ }
+ 
+-GList* collect_full_irq_list()
++GList* collect_full_irq_list(void)
+ {
+ 	GList *tmp_list = NULL;
+ 	FILE *file;
+diff --git a/ui/helpers.c b/ui/helpers.c
+index 0e9f76c..247f826 100644
+--- a/ui/helpers.c
++++ b/ui/helpers.c
+@@ -165,7 +165,7 @@ void dump_node(cpu_node_t *node, void *data __attribute__((unused)))
+ 	}
+ }
+ 
+-void dump_tree()
++void dump_tree(void)
+ {
+ 	for_each_node(tree, dump_node, NULL);
+ }
+diff --git a/ui/helpers.h b/ui/helpers.h
+index b8d9fcc..922914b 100644
+--- a/ui/helpers.h
++++ b/ui/helpers.h
+@@ -25,7 +25,7 @@ void for_each_node(GList *list, void (*fp)(cpu_node_t *node, void *data), void *
+ 
+ void dump_irq(irq_t *irq, void *data __attribute__((unused)));
+ void dump_node(cpu_node_t *node, void *data __attribute__((unused)));
+-void dump_tree();
++void dump_tree(void);
+ 
+ 
+ #endif /* HELPERS_H */
+diff --git a/ui/irqbalance-ui.c b/ui/irqbalance-ui.c
+index c26eff6..f5122ee 100644
+--- a/ui/irqbalance-ui.c
++++ b/ui/irqbalance-ui.c
+@@ -28,7 +28,7 @@ setup_t setup;
+ GMainLoop *main_loop;
+ static int default_bufsz = 8192;
+ 
+-struct msghdr * create_credentials_msg()
++struct msghdr * create_credentials_msg(void)
+ {
+ 	struct ucred *credentials = malloc(sizeof(struct ucred));
+ 	credentials->pid = getpid();
+@@ -51,7 +51,7 @@ struct msghdr * create_credentials_msg()
+ 	return msg;
+ }
+ 
+-int init_connection()
++int init_connection(void)
+ {
+ 	struct sockaddr_un addr;
+ 	memset(&addr, 0, sizeof(struct sockaddr_un));
+@@ -378,7 +378,7 @@ gboolean rescan_tree(gpointer data __attribute__((unused)))
+ 	free(irqbalance_data);
+ 	return TRUE;
+ }
+-void scroll_window() {
++void scroll_window(void) {
+ 	switch(state) {
+ 	case STATE_TREE:
+ 		display_tree();
+diff --git a/ui/irqbalance-ui.h b/ui/irqbalance-ui.h
+index dc24083..178be4b 100644
+--- a/ui/irqbalance-ui.h
++++ b/ui/irqbalance-ui.h
+@@ -72,8 +72,8 @@ typedef struct setup {
+ 
+ /* Function prototypes */
+ 
+-struct msghdr * create_credentials_msg();
+-int init_connection();
++struct msghdr * create_credentials_msg(void);
++int init_connection(void);
+ void send_settings(char *data);
+ char * get_data(char *string);
+ void parse_setup(char *setup_data);
+@@ -83,7 +83,6 @@ void assign_cpu_lists(cpu_node_t *node, void *data);
+ void assign_cpu_mask(cpu_node_t *node, void *data);
+ void parse_into_tree(char *data);
+ gboolean rescan_tree(gpointer data);
+-int main();
+ 
+ 
+ #endif /* IRQBALANCE_UI_H */
+diff --git a/ui/ui.c b/ui/ui.c
+index 8354fc6..9fa990a 100644
+--- a/ui/ui.c
++++ b/ui/ui.c
+@@ -21,7 +21,7 @@ char *IRQ_CLASS_TO_STR[] = {
+ 			"10-Gigabit Ethernet",
+ 			"Virt Event"};
+ 
+-void show_frame()
++void show_frame(void)
+ {
+ 	int i;
+ 	attrset(COLOR_PAIR(4));
+@@ -37,7 +37,7 @@ void show_frame()
+ 	}
+ }
+ 
+-void show_footer()
++void show_footer(void)
+ {
+ 	char footer[COLS];
+ 	snprintf(footer, COLS - 1,
+@@ -172,7 +172,7 @@ void print_cpu_line(cpu_ban_t *cpu, void *data __attribute__((unused)))
+ 	max_offset++;
+ }
+ 
+-void print_all_cpus()
++void print_all_cpus(void)
+ {
+ 	max_offset = 0;
+ 	if(all_cpus == NULL) {
+@@ -193,7 +193,7 @@ void add_banned_cpu(int *banned_cpu, void *data)
+ 	snprintf(data + strlen(data), 1024 - strlen(data), "%d, ", *banned_cpu);
+ }
+ 
+-void display_banned_cpus()
++void display_banned_cpus(void)
+ {
+ 	char banned_cpus[1024] = "Banned CPU numbers: \0";
+ 	if(g_list_length(setup.banned_cpus) > 0) {
+@@ -247,7 +247,7 @@ void get_cpu(cpu_node_t *node, void *data __attribute__((unused)))
+ 	}
+ }
+ 
+-void handle_cpu_banning()
++void handle_cpu_banning(void)
+ {
+ 	GList *tmp = g_list_copy_deep(all_cpus, copy_cpu_ban, NULL);
+ 	attrset(COLOR_PAIR(5));
+@@ -504,7 +504,7 @@ void print_irq_line(irq_t *irq, void *data __attribute__((unused)))
+ 	mvprintw(line, 120, "%s", irq_name[line]);
+ }
+ 
+-void print_all_irqs()
++void print_all_irqs(void)
+ {
+ 	max_offset = 0;
+ 	attrset(COLOR_PAIR(0));
+@@ -555,13 +555,13 @@ void copy_irqs_from_nodes(cpu_node_t *node, void *data __attribute__((unused)))
+ 	}
+ }
+ 
+-void get_all_irqs()
++void get_all_irqs(void)
+ {
+ 	all_irqs = g_list_copy_deep(setup.banned_irqs, copy_irq, NULL);
+ 	for_each_node(tree, copy_irqs_from_nodes, NULL);
+ }
+ 
+-void handle_irq_banning()
++void handle_irq_banning(void)
+ {
+ 	GList *tmp = g_list_copy_deep(all_irqs, copy_irq, NULL);
+ 	attrset(COLOR_PAIR(5));
+@@ -670,7 +670,7 @@ void handle_irq_banning()
+ 	}
+ }
+ 
+-void handle_sleep_setting()
++void handle_sleep_setting(void)
+ {
+ 	char info[128] = "Current sleep interval between rebalancing: \0";
+ 	uint8_t sleep_input_offset = strlen(info) + 3;
+@@ -693,7 +693,7 @@ void handle_sleep_setting()
+ 	refresh();
+ }
+ 
+-void init()
++void init(void)
+ {
+ 	signal(SIGINT, close_window);
+ 	initscr();
+@@ -732,7 +732,7 @@ void close_window(int sig __attribute__((unused)))
+ 	exit(EXIT_SUCCESS);
+ }
+ 
+-void settings()
++void settings(void)
+ {
+ 	clear();
+ 	char *setup_data = get_data(SETUP);
+@@ -751,7 +751,7 @@ void settings()
+ 	free(setup_data);
+ }
+ 
+-void setup_irqs()
++void setup_irqs(void)
+ {
+ 	clear();
+ 	get_all_irqs();
+@@ -830,7 +830,7 @@ void display_tree_node(cpu_node_t *node, void *data)
+ 	}
+ }
+ 
+-void display_tree()
++void display_tree(void)
+ {
+ 	clear();
+ 	char *setup_data = get_data(SETUP);
+diff --git a/ui/ui.h b/ui/ui.h
+index da5b4b9..f3485d4 100644
+--- a/ui/ui.h
++++ b/ui/ui.h
+@@ -17,40 +17,40 @@ extern setup_t setup;
+ extern int offset;
+ extern int max_offset;
+ 
+-void show_frame();
+-void show_footer();
++void show_frame(void);
++void show_footer(void);
+ 
+ char * check_control_in_sleep_input(int max_len, int column_offest, int line_offset);
+ int get_valid_sleep_input(int column_offest);
+ 
+ void get_banned_cpu(int *cpu, void *data);
+ void print_cpu_line(cpu_ban_t *cpu, void *data);
+-void print_all_cpus();
++void print_all_cpus(void);
+ void add_banned_cpu(int *banned_cpu, void *data);
+-void display_banned_cpus();
++void display_banned_cpus(void);
+ int toggle_cpu(GList *cpu_list, int cpu_number);
+ void get_new_cpu_ban_values(cpu_ban_t *cpu, void *data);
+-void get_cpu();
+-void handle_sleep_setting();
+-void handle_cpu_banning();
++void get_cpu(cpu_node_t *node, void *data);
++void handle_sleep_setting(void);
++void handle_cpu_banning(void);
+ 
+ void copy_assigned_obj(int *number, void *data);
+ void print_assigned_objects_string(irq_t *irq, int *line_offset);
+ void print_irq_line(irq_t *irq, void *data);
+-void print_all_irqs();
++void print_all_irqs(void);
+ int toggle_irq(GList *irq_list, int position);
+ void get_new_irq_ban_values(irq_t *irq, void *data);
+ void copy_irqs_from_nodes(cpu_node_t *node, void *data);
+-void get_all_irqs();
+-void handle_irq_banning();
++void get_all_irqs(void);
++void handle_irq_banning(void);
+ 
+-void init();
++void init(void);
+ void close_window(int sig);
+-void settings();
+-void setup_irqs();
++void settings(void);
++void setup_irqs(void);
+ void display_tree_node_irqs(irq_t *irq, void *data);
+ void display_tree_node(cpu_node_t *node, void *data);
+-void display_tree();
++void display_tree(void);
+ 
+ 
+ #endif /* UI_H */
+-- 
+2.47.0
+
diff --git a/0005-cast-void-pointer-to-actual-type.patch b/0005-cast-void-pointer-to-actual-type.patch
new file mode 100644
index 0000000..6685bd8
--- /dev/null
+++ b/0005-cast-void-pointer-to-actual-type.patch
@@ -0,0 +1,28 @@
+From f2aebffef0ad990daca4b04eab4900d757b85364 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Sun, 31 Mar 2024 15:01:38 -0700
+Subject: [PATCH 05/44] cast void pointer to actual type
+
+pointer arithmetic on void is a GNU extension.
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ ui/ui.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/ui/ui.c b/ui/ui.c
+index 9fa990a..a107fb9 100644
+--- a/ui/ui.c
++++ b/ui/ui.c
+@@ -190,7 +190,7 @@ void print_all_cpus(void)
+ 
+ void add_banned_cpu(int *banned_cpu, void *data)
+ {
+-	snprintf(data + strlen(data), 1024 - strlen(data), "%d, ", *banned_cpu);
++	snprintf((char *)data + strlen(data), 1024 - strlen(data), "%d, ", *banned_cpu);
+ }
+ 
+ void display_banned_cpus(void)
+-- 
+2.47.0
+
diff --git a/0006-meson-move-build-files-to-repository-root.patch b/0006-meson-move-build-files-to-repository-root.patch
new file mode 100644
index 0000000..7ca5e42
--- /dev/null
+++ b/0006-meson-move-build-files-to-repository-root.patch
@@ -0,0 +1,113 @@
+From 922ee47bbfb44a1ee7346f434092028adf4dffcd Mon Sep 17 00:00:00 2001
+From: Eli Schwartz <eschwartz93@gmail.com>
+Date: Mon, 1 Apr 2024 00:15:44 -0400
+Subject: [PATCH 06/44] meson: move build files to repository root
+
+Per the README and github ticket discussion, the purpose of having it in
+contrib/ is to gauge community interest. This interest has now been
+expressed by several parties, so that interest is certainly there.
+
+Let's move it to a more discoverable location. This has two benefits:
+
+- usability. If various parties intend to use it, then it is easier to
+  do so from the repository root rather than cd'ing into a subdir.
+
+- discoverability. At least Gentoo didn't initially realize it existed,
+  since it wasn't in the repository root, but switched after realizing.
+  It seems reasonable that other redistributors, too, would be
+  interested in evaluating its use but hadn't previously noticed the
+  option.
+
+Addresses: https://github.com/Irqbalance/irqbalance/pull/211#issuecomment-2028891561
+Signed-off-by: Eli Schwartz <eschwartz93@gmail.com>
+---
+ contrib/README                                |  2 --
+ contrib/meson.build => meson.build            | 30 +++++++++----------
+ .../meson_options.txt => meson_options.txt    |  0
+ 3 files changed, 15 insertions(+), 17 deletions(-)
+ delete mode 100644 contrib/README
+ rename contrib/meson.build => meson.build (85%)
+ rename contrib/meson_options.txt => meson_options.txt (100%)
+
+diff --git a/contrib/README b/contrib/README
+deleted file mode 100644
+index 2158dac..0000000
+--- a/contrib/README
++++ /dev/null
+@@ -1,2 +0,0 @@
+-This directory contains meson build instructions for irqbalance. This is here to see if there is any interest from
+-the general community.
+diff --git a/contrib/meson.build b/meson.build
+similarity index 85%
+rename from contrib/meson.build
+rename to meson.build
+index 0c74702..707baed 100644
+--- a/contrib/meson.build
++++ b/meson.build
+@@ -25,15 +25,15 @@ if cdata.get('HAVE_IRQBALANCEUI')
+ 
+   executable(
+     'irqbalance-ui',
+-    '../ui/helpers.c',
+-    '../ui/irqbalance-ui.c',
+-    '../ui/ui.c',
++    'ui/helpers.c',
++    'ui/irqbalance-ui.c',
++    'ui/ui.c',
+     dependencies: [glib_dep, ncurses_dep],
+     install: true,
+   )
+ 
+   install_man(
+-    '../irqbalance-ui.1',
++    'irqbalance-ui.1',
+     install_dir: get_option('mandir') + '/man1',
+     install_mode: 'rw-r--r--',
+     locale: 'en',
+@@ -41,20 +41,20 @@ if cdata.get('HAVE_IRQBALANCEUI')
+ endif
+ 
+ irqbalance_sources = [
+-  '../activate.c',
+-  '../bitmap.c',
+-  '../classify.c',
+-  '../cputree.c',
+-  '../irqbalance.c',
+-  '../irqlist.c',
+-  '../numa.c',
+-  '../placement.c',
+-  '../procinterrupts.c',
++  'activate.c',
++  'bitmap.c',
++  'classify.c',
++  'cputree.c',
++  'irqbalance.c',
++  'irqlist.c',
++  'numa.c',
++  'placement.c',
++  'procinterrupts.c',
+ ]
+ 
+ if libnl_3_dep.found() and libnl_genl_3_dep.found()
+   irqbalance_sources += [
+-    '../thermal.c',
++    'thermal.c',
+   ]
+ endif
+ 
+@@ -66,7 +66,7 @@ executable(
+ )
+ 
+ install_man(
+-  '../irqbalance.1',
++  'irqbalance.1',
+   install_dir: get_option('mandir') + '/man1',
+   install_mode: 'rw-r--r--',
+   locale: 'en',
+diff --git a/contrib/meson_options.txt b/meson_options.txt
+similarity index 100%
+rename from contrib/meson_options.txt
+rename to meson_options.txt
+-- 
+2.47.0
+
diff --git a/0007-meson-bump-project-version-to-1.9.4.patch b/0007-meson-bump-project-version-to-1.9.4.patch
new file mode 100644
index 0000000..8b2d889
--- /dev/null
+++ b/0007-meson-bump-project-version-to-1.9.4.patch
@@ -0,0 +1,29 @@
+From 21e1ae87a832eeeb3971180aade4dfde5c259acf Mon Sep 17 00:00:00 2001
+From: Eli Schwartz <eschwartz93@gmail.com>
+Date: Mon, 1 Apr 2024 00:17:52 -0400
+Subject: [PATCH 07/44] meson: bump project version to 1.9.4
+
+This was not done in commit f8b8cddfb54516308cd484c883d930f97c9e12ed.
+
+Signed-off-by: Eli Schwartz <eschwartz93@gmail.com>
+---
+ meson.build | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/meson.build b/meson.build
+index 707baed..9abf2d1 100644
+--- a/meson.build
++++ b/meson.build
+@@ -1,4 +1,8 @@
+-project('irqbalance', 'c', version: '1.9.3', default_options: ['warning_level=1'])
++project('irqbalance', 'c',
++    version: '1.9.4',
++    default_options: ['warning_level=1'],
++)
++
+ cc = meson.get_compiler('c')
+ 
+ glib_dep = dependency('glib-2.0')
+-- 
+2.47.0
+
diff --git a/0008-meson-drop-redundant-install_man-options.patch b/0008-meson-drop-redundant-install_man-options.patch
new file mode 100644
index 0000000..da18f8a
--- /dev/null
+++ b/0008-meson-drop-redundant-install_man-options.patch
@@ -0,0 +1,50 @@
+From ea733f46545116acd3693a76efb6713ba75c7d79 Mon Sep 17 00:00:00 2001
+From: Eli Schwartz <eschwartz93@gmail.com>
+Date: Mon, 1 Apr 2024 00:19:59 -0400
+Subject: [PATCH 08/44] meson: drop redundant install_man() options
+
+It is unnecessary to set the install directory for manpages, since meson
+infers this from the file extension and installs *.1 manpages to mandir
+in the man1 section for you.
+
+It is also unnecessary to tag them as "en" because that is the
+default... it is likewise unnecessary to tag them with manual file
+permissions since the default 644 permission is also the overridden one.
+
+Signed-off-by: Eli Schwartz <eschwartz93@gmail.com>
+---
+ meson.build | 14 ++------------
+ 1 file changed, 2 insertions(+), 12 deletions(-)
+
+diff --git a/meson.build b/meson.build
+index 9abf2d1..2cd0a9e 100644
+--- a/meson.build
++++ b/meson.build
+@@ -36,12 +36,7 @@ if cdata.get('HAVE_IRQBALANCEUI')
+     install: true,
+   )
+ 
+-  install_man(
+-    'irqbalance-ui.1',
+-    install_dir: get_option('mandir') + '/man1',
+-    install_mode: 'rw-r--r--',
+-    locale: 'en',
+-  )
++  install_man('irqbalance-ui.1')
+ endif
+ 
+ irqbalance_sources = [
+@@ -69,9 +64,4 @@ executable(
+   install: true,
+ )
+ 
+-install_man(
+-  'irqbalance.1',
+-  install_dir: get_option('mandir') + '/man1',
+-  install_mode: 'rw-r--r--',
+-  locale: 'en',
+-)
++install_man('irqbalance.1')
+-- 
+2.47.0
+
diff --git a/0009-meson-add-a-minimum-version-decorator.patch b/0009-meson-add-a-minimum-version-decorator.patch
new file mode 100644
index 0000000..1dd7cad
--- /dev/null
+++ b/0009-meson-add-a-minimum-version-decorator.patch
@@ -0,0 +1,37 @@
+From 23c6da76e05bdd042e76c3df2b531190797760c4 Mon Sep 17 00:00:00 2001
+From: Eli Schwartz <eschwartz93@gmail.com>
+Date: Mon, 1 Apr 2024 00:28:35 -0400
+Subject: [PATCH 09/44] meson: add a minimum version decorator
+
+Indicate the minimum required version of meson. Version 0.47 (released
+March 2020) is needed regardless, since it provides the cross-platform
+curses dependency wrapper. This just lets meson know that, so it
+can:
+
+- warn you if you use features from newer versions you didn't intend to
+  use, which is a sign to either require a higher minimum or avoid the
+  new feature for maximal compatibility
+
+- error out if you have an older version of meson too old to build
+  correctly with
+
+Signed-off-by: Eli Schwartz <eschwartz93@gmail.com>
+---
+ meson.build | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/meson.build b/meson.build
+index 2cd0a9e..026c4c4 100644
+--- a/meson.build
++++ b/meson.build
+@@ -1,6 +1,7 @@
+ project('irqbalance', 'c',
+     version: '1.9.4',
+     default_options: ['warning_level=1'],
++    meson_version: '>=0.54.0',
+ )
+ 
+ cc = meson.get_compiler('c')
+-- 
+2.47.0
+
diff --git a/0003-Drop-ProtectKernelTunables.patch b/0010-Drop-ProtectKernelTunables.patch
similarity index 91%
rename from 0003-Drop-ProtectKernelTunables.patch
rename to 0010-Drop-ProtectKernelTunables.patch
index f6fa428..33e608e 100644
--- a/0003-Drop-ProtectKernelTunables.patch
+++ b/0010-Drop-ProtectKernelTunables.patch
@@ -1,7 +1,7 @@
 From f2c8309a4198d8f51069a783905049c5b7eb7600 Mon Sep 17 00:00:00 2001
 From: Neil Horman <nhorman@openssl.org>
 Date: Mon, 1 Apr 2024 08:05:14 -0400
-Subject: [PATCH 3/3] Drop ProtectKernelTunables
+Subject: [PATCH 10/44] Drop ProtectKernelTunables
 
 It makes /proc/irq read only
 ---
@@ -21,5 +21,5 @@ index 87e19c1..b731cc6 100644
  ProtectKernelLogs=yes 
  ProtectControlGroups=yes 
 -- 
-2.40.1
+2.47.0
 
diff --git a/0011-meson-replace-generic-array-with-files.patch b/0011-meson-replace-generic-array-with-files.patch
new file mode 100644
index 0000000..4f9e117
--- /dev/null
+++ b/0011-meson-replace-generic-array-with-files.patch
@@ -0,0 +1,44 @@
+From 280b89d44c64d561ce091d3bc1a7145ad3a77167 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Sun, 31 Mar 2024 13:31:26 -0700
+Subject: [PATCH 11/44] meson: replace generic array with files()
+
+The latter is more restrictive.
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ meson.build | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/meson.build b/meson.build
+index 026c4c4..90d6aac 100644
+--- a/meson.build
++++ b/meson.build
+@@ -40,7 +40,7 @@ if cdata.get('HAVE_IRQBALANCEUI')
+   install_man('irqbalance-ui.1')
+ endif
+ 
+-irqbalance_sources = [
++irqbalance_sources = files(
+   'activate.c',
+   'bitmap.c',
+   'classify.c',
+@@ -50,12 +50,12 @@ irqbalance_sources = [
+   'numa.c',
+   'placement.c',
+   'procinterrupts.c',
+-]
++)
+ 
+ if libnl_3_dep.found() and libnl_genl_3_dep.found()
+-  irqbalance_sources += [
++  irqbalance_sources += files(
+     'thermal.c',
+-  ]
++  )
+ endif
+ 
+ executable(
+-- 
+2.47.0
+
diff --git a/0012-meson-use-find_library-for-numa.patch b/0012-meson-use-find_library-for-numa.patch
new file mode 100644
index 0000000..b8442ee
--- /dev/null
+++ b/0012-meson-use-find_library-for-numa.patch
@@ -0,0 +1,26 @@
+From 2e1bf9022276fc8753595c2db24d184382ad4a7c Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Sun, 31 Mar 2024 15:00:44 -0700
+Subject: [PATCH 12/44] meson: use find_library for numa
+
+Older versions of numa do not come with a pkgconfig file.
+---
+ meson.build | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/meson.build b/meson.build
+index 90d6aac..d26ef42 100644
+--- a/meson.build
++++ b/meson.build
+@@ -10,7 +10,7 @@ glib_dep = dependency('glib-2.0')
+ m_dep = cc.find_library('m', required: false)
+ capng_dep = dependency('libcap-ng', required: get_option('capng'))
+ ncurses_dep = dependency('curses', required: get_option('ui'))
+-numa_dep = dependency('numa', required: get_option('numa'))
++numa_dep = cc.find_library('numa', required: get_option('numa'))
+ libnl_3_dep = dependency('libnl-3.0', required: get_option('thermal'))
+ libnl_genl_3_dep = dependency('libnl-genl-3.0', required: get_option('thermal'))
+ systemd_dep = dependency('libsystemd', required: get_option('systemd'))
+-- 
+2.47.0
+
diff --git a/0013-ui-change-void-to-char.patch b/0013-ui-change-void-to-char.patch
new file mode 100644
index 0000000..0a463c9
--- /dev/null
+++ b/0013-ui-change-void-to-char.patch
@@ -0,0 +1,104 @@
+From 80a8aeb1dbb055bc1daa30e3bf8d80d5b07b33c7 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Sun, 31 Mar 2024 15:27:43 -0700
+Subject: [PATCH 13/44] ui: change void to char
+
+It ends up being treated as a char anyway.
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ ui/helpers.c | 2 +-
+ ui/helpers.h | 2 +-
+ ui/ui.c      | 8 ++++----
+ ui/ui.h      | 6 +++---
+ 4 files changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/ui/helpers.c b/ui/helpers.c
+index 247f826..3b4c51f 100644
+--- a/ui/helpers.c
++++ b/ui/helpers.c
+@@ -117,7 +117,7 @@ void for_each_cpu(GList *list, void (*fp)(cpu_ban_t *cpu, void *data), void *dat
+ 	}
+ }
+ 
+-void for_each_int(GList *list, void (*fp)(int *number, void *data), void *data)
++void for_each_int(GList *list, void (*fp)(int *number, char *data), void *data)
+ {
+ 	GList *entry;
+ 	entry = g_list_first(list);
+diff --git a/ui/helpers.h b/ui/helpers.h
+index 922914b..176a8d3 100644
+--- a/ui/helpers.h
++++ b/ui/helpers.h
+@@ -16,7 +16,7 @@ char * hex_to_bitmap(char hex_digit);
+ gpointer copy_cpu_ban(gconstpointer src, gpointer data);
+ gpointer copy_irq(gconstpointer src, gpointer data);
+ void for_each_cpu(GList *list, void (*fp)(cpu_ban_t *cpu, void *data), void *data);
+-void for_each_int(GList *list, void (*fp)(int *number, void *data), void *data);
++void for_each_int(GList *list, void (*fp)(int *number, char *data), void *data);
+ void for_each_irq(GList *list, void (*fp)(irq_t *irq, void *data), void *data);
+ void for_each_node(GList *list, void (*fp)(cpu_node_t *node, void *data), void *data);
+ 
+diff --git a/ui/ui.c b/ui/ui.c
+index a107fb9..2695ef0 100644
+--- a/ui/ui.c
++++ b/ui/ui.c
+@@ -130,7 +130,7 @@ int get_valid_sleep_input(int column_offest)
+ 	return new_sleep;
+ }
+ 
+-void get_banned_cpu(int *cpu, void *data __attribute__((unused)))
++void get_banned_cpu(int *cpu, char *data __attribute__((unused)))
+ {
+ 	cpu_ban_t *new = malloc(sizeof(cpu_ban_t));
+ 	new->number = *cpu;
+@@ -188,9 +188,9 @@ void print_all_cpus(void)
+ 		max_offset = 0;
+ }
+ 
+-void add_banned_cpu(int *banned_cpu, void *data)
++void add_banned_cpu(int *banned_cpu, char *data)
+ {
+-	snprintf((char *)data + strlen(data), 1024 - strlen(data), "%d, ", *banned_cpu);
++	snprintf(data + strlen(data), 1024 - strlen(data), "%d, ", *banned_cpu);
+ }
+ 
+ void display_banned_cpus(void)
+@@ -369,7 +369,7 @@ static inline void bsnl_emit(char *buf, int buflen)
+ 		snprintf(buf + len, buflen - len, "%d-%d", rbot, rtop);
+ }
+ 
+-void copy_assigned_obj(int *number, void *data)
++void copy_assigned_obj(int *number, char *data)
+ {
+ 	if (rtop == -1) {
+ 		rbot = rtop = *number;
+diff --git a/ui/ui.h b/ui/ui.h
+index f3485d4..5fbdd7c 100644
+--- a/ui/ui.h
++++ b/ui/ui.h
+@@ -23,10 +23,10 @@ void show_footer(void);
+ char * check_control_in_sleep_input(int max_len, int column_offest, int line_offset);
+ int get_valid_sleep_input(int column_offest);
+ 
+-void get_banned_cpu(int *cpu, void *data);
++void get_banned_cpu(int *cpu, char *data);
+ void print_cpu_line(cpu_ban_t *cpu, void *data);
+ void print_all_cpus(void);
+-void add_banned_cpu(int *banned_cpu, void *data);
++void add_banned_cpu(int *banned_cpu, char *data);
+ void display_banned_cpus(void);
+ int toggle_cpu(GList *cpu_list, int cpu_number);
+ void get_new_cpu_ban_values(cpu_ban_t *cpu, void *data);
+@@ -34,7 +34,7 @@ void get_cpu(cpu_node_t *node, void *data);
+ void handle_sleep_setting(void);
+ void handle_cpu_banning(void);
+ 
+-void copy_assigned_obj(int *number, void *data);
++void copy_assigned_obj(int *number, char *data);
+ void print_assigned_objects_string(irq_t *irq, int *line_offset);
+ void print_irq_line(irq_t *irq, void *data);
+ void print_all_irqs(void);
+-- 
+2.47.0
+
diff --git a/0014-clang-tidy-don-t-assign-in-if.patch b/0014-clang-tidy-don-t-assign-in-if.patch
new file mode 100644
index 0000000..cb12203
--- /dev/null
+++ b/0014-clang-tidy-don-t-assign-in-if.patch
@@ -0,0 +1,71 @@
+From 122ae9e27078585e5c618bee6741f723c03f1aef Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Mon, 1 Apr 2024 13:56:46 -0700
+Subject: [PATCH 14/44] clang-tidy: don't assign in if
+
+Found with bugprone-assignment-in-if-condition
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ classify.c   | 17 +++++++++++------
+ irqbalance.c |  3 ++-
+ 2 files changed, 13 insertions(+), 7 deletions(-)
+
+diff --git a/classify.c b/classify.c
+index 1601a1e..df9c13c 100644
+--- a/classify.c
++++ b/classify.c
+@@ -200,25 +200,30 @@ static unsigned int read_pci_data(const char *devpath, const char* file)
+ /* Get pci information for IRQ classification */
+ static int get_pci_info(const char *devpath, struct pci_info *pci)
+ {
+-	unsigned int data = PCI_INVAL_DATA;
++	unsigned int data;
+ 
+-	if ((data = read_pci_data(devpath, "vendor")) == PCI_INVAL_DATA)
++	data = read_pci_data(devpath, "vendor");
++	if (data == PCI_INVAL_DATA)
+ 		return -ENODEV;
+ 	pci->vendor = (unsigned short)data;
+ 
+-	if ((data = read_pci_data(devpath, "device")) == PCI_INVAL_DATA)
++	data = read_pci_data(devpath, "device");
++	if (data == PCI_INVAL_DATA)
+ 		return -ENODEV;
+ 	pci->device = (unsigned short)data;
+ 
+-	if ((data = read_pci_data(devpath, "subsystem_vendor")) == PCI_INVAL_DATA)
++	data = read_pci_data(devpath, "subsystem_vendor");
++	if (data == PCI_INVAL_DATA)
+ 		return -ENODEV;
+ 	pci->sub_vendor = (unsigned short)data;
+ 
+-	if ((data = read_pci_data(devpath, "subsystem_device")) == PCI_INVAL_DATA)
++	data = read_pci_data(devpath, "subsystem_device");
++	if (data == PCI_INVAL_DATA)
+ 		return -ENODEV;
+ 	pci->sub_device = (unsigned short)data;
+ 
+-	if ((data = read_pci_data(devpath, "class")) == PCI_INVAL_DATA)
++	data = read_pci_data(devpath, "class");
++	if (data == PCI_INVAL_DATA)
+ 		return -ENODEV;
+ 	pci->class = data;
+ 
+diff --git a/irqbalance.c b/irqbalance.c
+index 373161f..a8c56c2 100644
+--- a/irqbalance.c
++++ b/irqbalance.c
+@@ -422,7 +422,8 @@ gboolean sock_handle(gint fd, GIOCondition condition, gpointer user_data __attri
+ 			log(TO_ALL, LOG_WARNING, "Connection couldn't be accepted.\n");
+ 			goto out;
+ 		}
+-		if ((recv_size = recvmsg(sock, &msg, 0)) < 0) {
++		recv_size = recvmsg(sock, &msg, 0);
++		if (recv_size < 0) {
+ 			log(TO_ALL, LOG_WARNING, "Error while receiving data.\n");
+ 			goto out_close;
+ 		}
+-- 
+2.47.0
+
diff --git a/0015-clang-tidy-properly-use-strncmp.patch b/0015-clang-tidy-properly-use-strncmp.patch
new file mode 100644
index 0000000..6aa998d
--- /dev/null
+++ b/0015-clang-tidy-properly-use-strncmp.patch
@@ -0,0 +1,106 @@
+From 4c1b0a09bf78365c88e2fdf9713540e59f0375fc Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Mon, 1 Apr 2024 13:58:40 -0700
+Subject: [PATCH 15/44] clang-tidy: properly use strncmp
+
+Found with bugprone-suspicious-string-compare
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ ui/irqbalance-ui.c | 26 +++++++++++++-------------
+ 1 file changed, 13 insertions(+), 13 deletions(-)
+
+diff --git a/ui/irqbalance-ui.c b/ui/irqbalance-ui.c
+index f5122ee..581c110 100644
+--- a/ui/irqbalance-ui.c
++++ b/ui/irqbalance-ui.c
+@@ -157,7 +157,7 @@ void parse_setup(char *setup_data)
+ 	setup.banned_irqs = NULL;
+ 	setup.banned_cpus = NULL;
+ 	token = strtok_r(copy, " ", &ptr);
+-	if(strncmp(token, "SLEEP", strlen("SLEEP"))) goto out;
++	if(strncmp(token, "SLEEP", strlen("SLEEP")) != 0) goto out;
+ 	setup.sleep = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
+ 	token = strtok_r(NULL, " ", &ptr);
+ 	/* Parse banned IRQ data */
+@@ -165,13 +165,13 @@ void parse_setup(char *setup_data)
+ 		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;
++		if(strncmp(token, "LOAD", strlen("LOAD")) != 0) 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;
++		if(strncmp(token, "DIFF", strlen("DIFF")) != 0) 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;
++		if(strncmp(token, "CLASS", strlen("CLASS")) != 0) goto out;
+ 		new_irq->class = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
+ 		new_irq->is_banned = 1;
+ 		new_irq->assigned_to = NULL;
+@@ -180,7 +180,7 @@ void parse_setup(char *setup_data)
+ 		new_irq = NULL;
+ 	}
+ 
+-	if(strncmp(token, "BANNED", strlen("BANNED"))) goto out;
++	if(strncmp(token, "BANNED", strlen("BANNED")) != 0) goto out;
+ 	token = strtok_r(NULL, " ", &ptr);
+ 	for(i = strlen(token) - 1; i >= 0; i--) {
+ 		if (token[i] == ',')
+@@ -287,7 +287,7 @@ void parse_into_tree(char *data)
+ 	token = strtok_r(copy, " ", &ptr);
+ 	while(token != NULL) {
+ 		/* Parse node data */
+-		if(strncmp(token, "TYPE", strlen("TYPE"))) {
++		if(strncmp(token, "TYPE", strlen("TYPE")) != 0) {
+ 			free(copy);
+ 			 goto out;
+ 		}
+@@ -303,13 +303,13 @@ void parse_into_tree(char *data)
+ 			parent = parent->parent;
+ 		}
+ 		token = strtok_r(NULL, " ", &ptr);
+-		if(strncmp(token, "NUMBER", strlen("NUMBER"))) goto out;
++		if(strncmp(token, "NUMBER", strlen("NUMBER")) != 0) goto out;
+ 		new->number = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
+ 		token = strtok_r(NULL, " ", &ptr);
+-		if(strncmp(token, "LOAD", strlen("LOAD"))) goto out;
++		if(strncmp(token, "LOAD", strlen("LOAD")) != 0) 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;
++		if(strncmp(token, "SAVE_MODE", strlen("SAVE_MODE")) != 0) goto out;
+ 		new->is_powersave = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
+ 		token = strtok_r(NULL, " ", &ptr);
+ 
+@@ -318,13 +318,13 @@ void parse_into_tree(char *data)
+ 			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;
++			if(strncmp(token, "LOAD", strlen("LOAD")) != 0) 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;
++			if(strncmp(token, "DIFF", strlen("DIFF")) != 0) 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;
++			if(strncmp(token, "CLASS", strlen("CLASS")) != 0) 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);
+@@ -332,7 +332,7 @@ void parse_into_tree(char *data)
+ 			new_irq = NULL;
+ 		}
+ 
+-		if((token == NULL) || (strncmp(token, "IRQ", strlen("IRQ")))) {
++		if((token == NULL) || (strncmp(token, "IRQ", strlen("IRQ")) != 0)) {
+ 			new->parent = parent;
+ 			if(parent == NULL) {
+ 				tree = g_list_append(tree, new);
+-- 
+2.47.0
+
diff --git a/0016-replace-malloc-with-g_malloc0.patch b/0016-replace-malloc-with-g_malloc0.patch
new file mode 100644
index 0000000..a6d0c24
--- /dev/null
+++ b/0016-replace-malloc-with-g_malloc0.patch
@@ -0,0 +1,32 @@
+From 8f575ec8bd70bd6895fc876d203ae28b7ab8c495 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Mon, 1 Apr 2024 14:04:46 -0700
+Subject: [PATCH 16/44] replace malloc with g_malloc0
+
+Aborts on failure. There's no null check.
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ ui/irqbalance-ui.c | 6 +-----
+ 1 file changed, 1 insertion(+), 5 deletions(-)
+
+diff --git a/ui/irqbalance-ui.c b/ui/irqbalance-ui.c
+index 581c110..06f4602 100644
+--- a/ui/irqbalance-ui.c
++++ b/ui/irqbalance-ui.c
+@@ -291,11 +291,7 @@ void parse_into_tree(char *data)
+ 			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 = g_malloc0(sizeof(cpu_node_t));
+ 		new->type = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
+ 		if(new->type == OBJ_TYPE_NODE) {
+ 			parent = NULL;
+-- 
+2.47.0
+
diff --git a/0017-clang-tidy-don-t-use-else-after-return.patch b/0017-clang-tidy-don-t-use-else-after-return.patch
new file mode 100644
index 0000000..a7e4682
--- /dev/null
+++ b/0017-clang-tidy-don-t-use-else-after-return.patch
@@ -0,0 +1,151 @@
+From 2c3cbb5713e86199a10c8624eaf188609635d443 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Mon, 1 Apr 2024 14:11:56 -0700
+Subject: [PATCH 17/44] clang-tidy: don't use else after return
+
+Found with readability-else-after-return
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ bitmap.h     | 15 +++++----------
+ classify.c   |  6 ++----
+ irqbalance.c |  6 +++---
+ numa.c       |  2 +-
+ ui/ui.c      | 13 ++++++-------
+ 5 files changed, 17 insertions(+), 25 deletions(-)
+
+diff --git a/bitmap.h b/bitmap.h
+index 7afce59..9eca1af 100644
+--- a/bitmap.h
++++ b/bitmap.h
+@@ -282,8 +282,7 @@ static inline int bitmap_equal(const unsigned long *src1,
+ {
+ 	if (nbits <= BITS_PER_LONG)
+ 		return ! ((*src1 ^ *src2) & BITMAP_LAST_WORD_MASK(nbits));
+-	else
+-		return __bitmap_equal(src1, src2, nbits);
++	return __bitmap_equal(src1, src2, nbits);
+ }
+ 
+ static inline int bitmap_intersects(const unsigned long *src1,
+@@ -291,8 +290,7 @@ static inline int bitmap_intersects(const unsigned long *src1,
+ {
+ 	if (nbits <= BITS_PER_LONG)
+ 		return ((*src1 & *src2) & BITMAP_LAST_WORD_MASK(nbits)) != 0;
+-	else
+-		return __bitmap_intersects(src1, src2, nbits);
++	return __bitmap_intersects(src1, src2, nbits);
+ }
+ 
+ static inline int bitmap_subset(const unsigned long *src1,
+@@ -300,24 +298,21 @@ static inline int bitmap_subset(const unsigned long *src1,
+ {
+ 	if (nbits <= BITS_PER_LONG)
+ 		return ! ((*src1 & ~(*src2)) & BITMAP_LAST_WORD_MASK(nbits));
+-	else
+-		return __bitmap_subset(src1, src2, nbits);
++	return __bitmap_subset(src1, src2, nbits);
+ }
+ 
+ static inline int bitmap_empty(const unsigned long *src, int nbits)
+ {
+ 	if (nbits <= BITS_PER_LONG)
+ 		return ! (*src & BITMAP_LAST_WORD_MASK(nbits));
+-	else
+-		return __bitmap_empty(src, nbits);
++	return __bitmap_empty(src, nbits);
+ }
+ 
+ static inline int bitmap_full(const unsigned long *src, int nbits)
+ {
+ 	if (nbits <= BITS_PER_LONG)
+ 		return ! (~(*src) & BITMAP_LAST_WORD_MASK(nbits));
+-	else
+-		return __bitmap_full(src, nbits);
++	return __bitmap_full(src, nbits);
+ }
+ 
+ static inline int bitmap_weight(const unsigned long *src, int nbits)
+diff --git a/classify.c b/classify.c
+index df9c13c..cc0200d 100644
+--- a/classify.c
++++ b/classify.c
+@@ -295,8 +295,7 @@ gint substr_find(gconstpointer a, gconstpointer b)
+ {
+ 	if (strstr(b, a))
+ 		return 0;
+-	else
+-		return 1;
++	return 1;
+ }
+ 
+ static void add_banned_module(char *modname, GList **modlist)
+@@ -558,8 +557,7 @@ static int check_for_module_ban(char *name)
+ 
+ 	if (entry)
+ 		return 1;
+-	else
+-		return 0;
++	return 0;
+ }
+ 
+ static int check_for_irq_ban(struct irq_info *irq, char *mod)
+diff --git a/irqbalance.c b/irqbalance.c
+index a8c56c2..ab4e448 100644
+--- a/irqbalance.c
++++ b/irqbalance.c
+@@ -330,10 +330,10 @@ out:
+ 
+ 	if (keep_going) {
+ 		return TRUE;
+-	} else {
+-		g_main_loop_quit(main_loop);
+-		return FALSE;
+ 	}
++
++	g_main_loop_quit(main_loop);
++	return FALSE;
+ }
+ 
+ void get_irq_data(struct irq_info *irq, void *data)
+diff --git a/numa.c b/numa.c
+index 13d7ebd..5143ea0 100644
+--- a/numa.c
++++ b/numa.c
+@@ -116,7 +116,7 @@ void connect_cpu_mem_topo(struct topo_obj *p, void *data __attribute__((unused))
+ 
+ 	if (len == 0) {
+ 		return;
+-	} else if (len > 1) {
++	} if (len > 1) {
+ 		for_each_object(p->children, connect_cpu_mem_topo, NULL);
+ 		return;
+ 	}
+diff --git a/ui/ui.c b/ui/ui.c
+index 2695ef0..c580f85 100644
+--- a/ui/ui.c
++++ b/ui/ui.c
+@@ -113,14 +113,13 @@ int get_valid_sleep_input(int column_offest)
+ 		new_sleep = strtol(input, &error, 10);
+ 		if((*error == '\0') && (new_sleep >= 1)) {
+ 			break;
+-		} else {
+-			new_sleep = setup.sleep;
+-			attrset(COLOR_PAIR(4) | A_BOLD);
+-			mvprintw(LINES - 2, 1,
+-				"Invalid input: %s								",
+-				input);
+-			refresh();
+ 		}
++		new_sleep = setup.sleep;
++		attrset(COLOR_PAIR(4) | A_BOLD);
++		mvprintw(LINES - 2, 1,
++			"Invalid input: %s								",
++			input);
++		refresh();
+ 		free(input);
+ 	}
+ 
+-- 
+2.47.0
+
diff --git a/0018-clang-tidy-remove-return-in-void-functions.patch b/0018-clang-tidy-remove-return-in-void-functions.patch
new file mode 100644
index 0000000..31e78b9
--- /dev/null
+++ b/0018-clang-tidy-remove-return-in-void-functions.patch
@@ -0,0 +1,36 @@
+From 9b1ced291f14debdae735723978ed5b44da9deee Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Mon, 1 Apr 2024 14:14:55 -0700
+Subject: [PATCH 18/44] clang-tidy: remove return in void functions
+
+Found with readability-redundant-control-flow
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ classify.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/classify.c b/classify.c
+index cc0200d..08340db 100644
+--- a/classify.c
++++ b/classify.c
+@@ -102,8 +102,6 @@ static void apply_pci_quirks(const struct pci_info *pci, int *irq_class)
+ 				break;
+ 		}
+ 	}
+-
+-	return;
+ }
+ 
+ /* Determin IRQ class based on PCI class code */
+@@ -283,7 +281,6 @@ static void add_banned_irq(int irq, GList **list)
+ 
+ 	*list = g_list_append(*list, new);
+ 	log(TO_CONSOLE, LOG_INFO, "IRQ %d was BANNED.\n", irq);
+-	return;
+ }
+ 
+ void add_cl_banned_irq(int irq)
+-- 
+2.47.0
+
diff --git a/0019-clang-tidy-remove-redundant-declarations.patch b/0019-clang-tidy-remove-redundant-declarations.patch
new file mode 100644
index 0000000..a3a60ae
--- /dev/null
+++ b/0019-clang-tidy-remove-redundant-declarations.patch
@@ -0,0 +1,27 @@
+From 02f7c174f1dfb0699cec4078d90a93901556a0c1 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Mon, 1 Apr 2024 14:16:26 -0700
+Subject: [PATCH 19/44] clang-tidy: remove redundant declarations
+
+Found with readability-redundant-declaration
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ ui/ui.h | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/ui/ui.h b/ui/ui.h
+index 5fbdd7c..e41ca24 100644
+--- a/ui/ui.h
++++ b/ui/ui.h
+@@ -11,7 +11,6 @@
+ #include "irqbalance-ui.h"
+ #include "helpers.h"
+ 
+-extern GList *tree;
+ extern setup_t setup;
+ 
+ extern int offset;
+-- 
+2.47.0
+
diff --git a/0020-clang-tidy-remove-duplicate-include.patch b/0020-clang-tidy-remove-duplicate-include.patch
new file mode 100644
index 0000000..dde209f
--- /dev/null
+++ b/0020-clang-tidy-remove-duplicate-include.patch
@@ -0,0 +1,27 @@
+From e78ea2676fdb2754b58ddec3c88b185f1b0b9949 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Mon, 1 Apr 2024 14:18:35 -0700
+Subject: [PATCH 20/44] clang-tidy: remove duplicate include
+
+Found with readability-duplicate-include
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ irqbalance.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/irqbalance.c b/irqbalance.c
+index ab4e448..870d7c0 100644
+--- a/irqbalance.c
++++ b/irqbalance.c
+@@ -26,7 +26,6 @@
+ #include <malloc.h>
+ #include <sys/time.h>
+ #include <syslog.h>
+-#include <unistd.h>
+ #include <signal.h>
+ #include <time.h>
+ #include <sys/types.h>
+-- 
+2.47.0
+
diff --git a/0021-CI-add-meson-CI.patch b/0021-CI-add-meson-CI.patch
new file mode 100644
index 0000000..9d8b032
--- /dev/null
+++ b/0021-CI-add-meson-CI.patch
@@ -0,0 +1,92 @@
+From 44795adcf03c951efb92bed69af7d68d230d6d69 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Sun, 31 Mar 2024 13:59:01 -0700
+Subject: [PATCH 21/44] CI: add meson CI
+
+tests old and new Clang/GCC on x64 and Alpine Linux on various platforms.
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ .github/workflows/meson.yml | 70 +++++++++++++++++++++++++++++++++++++
+ 1 file changed, 70 insertions(+)
+ create mode 100644 .github/workflows/meson.yml
+
+diff --git a/.github/workflows/meson.yml b/.github/workflows/meson.yml
+new file mode 100644
+index 0000000..df2d826
+--- /dev/null
++++ b/.github/workflows/meson.yml
+@@ -0,0 +1,70 @@
++name: meson
++
++on: [pull_request, push]
++
++concurrency:
++  group: ${{github.workflow}}-${{github.head_ref}}
++  cancel-in-progress: true
++
++jobs:
++  Linux-GCC:
++    runs-on: ubuntu-20.04
++    strategy:
++      matrix:
++        cc: ['7', '13']
++    steps:
++      - uses: actions/checkout@v4
++      - uses: egor-tensin/setup-gcc@v1
++        with:
++          version: ${{matrix.cc}}
++      - name: Install Packages
++        run: |
++          python3 -m pip install meson ninja
++          sudo apt install -y libcap-ng-dev libnl-3-dev libnl-genl-3-dev libnuma-dev libsystemd-dev
++      - name: Compile and Test
++        run: |
++          meson setup "${{github.workspace}}/build" -Dwarning_level=3
++          meson compile -C "${{github.workspace}}/build"
++          meson test -C "${{github.workspace}}/build"
++  Linux-Clang:
++    runs-on: ubuntu-20.04
++    strategy:
++      matrix:
++        cc: ['6.0', '18']
++    steps:
++      - uses: actions/checkout@v4
++      - uses: egor-tensin/setup-clang@v1
++        with:
++          version: ${{matrix.cc}}
++      - name: Install Packages
++        run: |
++          python3 -m pip install meson ninja
++          sudo apt install -y libcap-ng-dev libnl-3-dev libnl-genl-3-dev libnuma-dev libsystemd-dev
++      - name: Compile and Test
++        run: |
++          meson setup "${{github.workspace}}/build" -Dwarning_level=3
++          meson compile -C "${{github.workspace}}/build" --verbose
++          meson test -C "${{github.workspace}}/build" --verbose
++  Alpine:
++    runs-on: ubuntu-20.04
++    strategy:
++      matrix:
++        platform: ['aarch64', 'armhf', 'armv7', 'ppc64le', 'riscv64', 's390x']
++    defaults:
++      run:
++        shell: alpine.sh {0}
++    steps:
++      - name: Get pushed code
++        uses: actions/checkout@v4
++
++      - uses: jirutka/setup-alpine@v1
++        with:
++          branch: edge
++          arch: ${{matrix.platform}}
++          packages: >
++            build-base glib-dev libcap-ng-dev libnl3-dev meson numactl-dev
++      - name: Compile and Test
++        run: |
++          meson setup "${{github.workspace}}/build" -Dwarning_level=3 -Dsystemd=disabled
++          meson compile -C "${{github.workspace}}/build"
++          meson test -C "${{github.workspace}}/build"
+-- 
+2.47.0
+
diff --git a/0022-Wrap-migrate_irq-in-a-higher-level-utility-function.patch b/0022-Wrap-migrate_irq-in-a-higher-level-utility-function.patch
new file mode 100644
index 0000000..2c6dae7
--- /dev/null
+++ b/0022-Wrap-migrate_irq-in-a-higher-level-utility-function.patch
@@ -0,0 +1,173 @@
+From d16ad5df4cd14b148513ab9d5cf0ebcf09d023e0 Mon Sep 17 00:00:00 2001
+From: Andrew Zaborowski <andrew.zaborowski@intel.com>
+Date: Fri, 10 May 2024 18:38:52 -0700
+Subject: [PATCH 22/44] Wrap migrate_irq in a higher level utility function
+
+Add migrate_irq_obj and replace existing migrate_irq calls with calls to
+the new function.  migrate_irq_obj takes source and destination
+topo_obj's instead of interrupt lists so as to factor out updating of
+the load on the destination cpu and of info->asssigned_obj.
+
+Pass NULL as destination to move irq to rebalance_irq_list.
+
+Drop the unneeded force_irq_migration.
+---
+ irqbalance.c |  4 +---
+ irqbalance.h |  1 +
+ irqlist.c    | 31 +++++++++++++++++++++----------
+ placement.c  | 24 +++++-------------------
+ 4 files changed, 28 insertions(+), 32 deletions(-)
+
+diff --git a/irqbalance.c b/irqbalance.c
+index 870d7c0..7efbc98 100644
+--- a/irqbalance.c
++++ b/irqbalance.c
+@@ -253,9 +253,7 @@ void force_rebalance_irq(struct irq_info *info, void *data __attribute__((unused
+ 	if (info->assigned_obj == NULL)
+ 		rebalance_irq_list = g_list_append(rebalance_irq_list, info);
+ 	else
+-		migrate_irq(&info->assigned_obj->interrupts, &rebalance_irq_list, info);
+-
+-	info->assigned_obj = NULL;
++		migrate_irq_obj(info->assigned_obj, NULL, info);
+ }
+ 
+ gboolean handler(gpointer data __attribute__((unused)))
+diff --git a/irqbalance.h b/irqbalance.h
+index 09daa3d..76640dd 100644
+--- a/irqbalance.h
++++ b/irqbalance.h
+@@ -52,6 +52,7 @@ void dump_workloads(void);
+ void sort_irq_list(GList **list);
+ void calculate_placement(void);
+ void dump_tree(void);
++void migrate_irq_obj(struct topo_obj *from, struct topo_obj *to, struct irq_info *info);
+ 
+ void activate_mappings(void);
+ void clear_cpu_tree(void);
+diff --git a/irqlist.c b/irqlist.c
+index 0ba411e..304b1c6 100644
+--- a/irqlist.c
++++ b/irqlist.c
+@@ -108,9 +108,7 @@ static void move_candidate_irqs(struct irq_info *info, void *data)
+ 
+ 	log(TO_CONSOLE, LOG_INFO, "Selecting irq %d for rebalancing\n", info->irq);
+ 
+-	migrate_irq(&info->assigned_obj->interrupts, &rebalance_irq_list, info);
+-
+-	info->assigned_obj = NULL;
++	force_rebalance_irq(info, NULL);
+ }
+ 
+ static void migrate_overloaded_irqs(struct topo_obj *obj, void *data)
+@@ -146,12 +144,6 @@ static void migrate_overloaded_irqs(struct topo_obj *obj, void *data)
+ 	}
+ }
+ 
+-static void force_irq_migration(struct irq_info *info, void *data __attribute__((unused)))
+-{
+-	migrate_irq(&info->assigned_obj->interrupts, &rebalance_irq_list, info);
+-	info->assigned_obj = NULL;
+-}
+-
+ static void clear_powersave_mode(struct topo_obj *obj, void *data __attribute__((unused)))
+ {
+ 	obj->powersave_mode = 0;
+@@ -183,7 +175,7 @@ void update_migration_status(void)
+ 			log(TO_ALL, LOG_INFO, "cpu %d entering powersave mode\n", info.powersave->number);
+ 			info.powersave->powersave_mode = 1;
+ 			if (g_list_length(info.powersave->interrupts) > 0)
+-				for_each_irq(info.powersave->interrupts, force_irq_migration, NULL);
++				for_each_irq(info.powersave->interrupts, force_rebalance_irq, NULL);
+ 		} else if ((info.num_over) && (info.num_powersave)) {
+ 			log(TO_ALL, LOG_INFO, "Load average increasing, re-enabling all cpus for irq balancing\n");
+ 			for_each_object(cpus, clear_powersave_mode, NULL);
+@@ -205,3 +197,22 @@ void dump_workloads(void)
+ 	for_each_irq(NULL, dump_workload, NULL);
+ }
+ 
++void migrate_irq_obj(struct topo_obj *from, struct topo_obj *to, struct irq_info *info)
++{
++
++	GList **from_list;
++	GList **to_list;
++
++	if (!from)
++		from = info->assigned_obj;
++
++	from_list = from ? &from->interrupts : &rebalance_irq_list;
++	to_list = to ? &to->interrupts : &rebalance_irq_list;
++
++	migrate_irq(from_list, to_list, info);
++
++	if (to)
++		to->load += info->load + 1;
++
++	info->assigned_obj = to;
++}
+diff --git a/placement.c b/placement.c
+index dea7c23..f156e0e 100644
+--- a/placement.c
++++ b/placement.c
+@@ -74,7 +74,6 @@ static void find_best_object_for_irq(struct irq_info *info, void *data)
+ {
+ 	struct obj_placement place;
+ 	struct topo_obj *d = data;
+-	struct topo_obj *asign;
+ 
+ 	if (!info->moved)
+ 		return;
+@@ -107,13 +106,8 @@ static void find_best_object_for_irq(struct irq_info *info, void *data)
+ 
+ 	for_each_object(d->children, find_best_object, &place);
+ 
+-	asign = place.best;
+-
+-	if (asign) {
+-		migrate_irq(&d->interrupts, &asign->interrupts, info);
+-		info->assigned_obj = asign;
+-		asign->load += info->load;
+-	}
++	if (place.best)
++		migrate_irq_obj(d, place.best, info);
+ }
+ 
+ static void place_irq_in_object(struct topo_obj *d, void *data __attribute__((unused)))
+@@ -125,7 +119,6 @@ static void place_irq_in_object(struct topo_obj *d, void *data __attribute__((un
+ static void place_irq_in_node(struct irq_info *info, void *data __attribute__((unused)))
+ {
+ 	struct obj_placement place;
+-	struct topo_obj *asign;
+ 
+ 	if ((info->level == BALANCE_NONE) && cpus_empty(banned_cpus))
+ 		return;
+@@ -145,9 +138,7 @@ static void place_irq_in_node(struct irq_info *info, void *data __attribute__((u
+ 		 * This irq belongs to a device with a preferred numa node
+ 		 * put it on that node
+ 		 */
+-		migrate_irq(&rebalance_irq_list, &irq_numa_node(info)->interrupts, info);
+-		info->assigned_obj = irq_numa_node(info);
+-		irq_numa_node(info)->load += info->load + 1;
++		migrate_irq_obj(NULL, irq_numa_node(info), info);
+ 
+ 		return;
+ 	}
+@@ -159,13 +150,8 @@ find_placement:
+ 
+ 	for_each_object(numa_nodes, find_best_object, &place);
+ 
+-	asign = place.best;
+-
+-	if (asign) {
+-		migrate_irq(&rebalance_irq_list, &asign->interrupts, info);
+-		info->assigned_obj = asign;
+-		asign->load += info->load;
+-	}
++	if (place.best)
++		migrate_irq_obj(NULL, place.best, info);
+ }
+ 
+ static void validate_irq(struct irq_info *info, void *data)
+-- 
+2.47.0
+
diff --git a/0023-Track-IRQ-slots-count-per-CPU-to-avoid-overflowing.patch b/0023-Track-IRQ-slots-count-per-CPU-to-avoid-overflowing.patch
new file mode 100644
index 0000000..96e57c6
--- /dev/null
+++ b/0023-Track-IRQ-slots-count-per-CPU-to-avoid-overflowing.patch
@@ -0,0 +1,195 @@
+From 54051449030cb3c1642f9a6110316d3705eb3a23 Mon Sep 17 00:00:00 2001
+From: Andrew Zaborowski <andrew.zaborowski@intel.com>
+Date: Fri, 10 May 2024 18:57:34 -0700
+Subject: [PATCH 23/44] Track IRQ "slots" count per CPU to avoid overflowing
+
+There are situations where irqbalance may try to migrate large numbers of
+IRQs to a topo_obj, there's no upper bound on the number as the
+placement logic is based on load mainly.  The kernel's irq bitmasks limit
+the number of IRQs on each cpu and if more are tried to be migrated, the
+write to smp_affinity returns -ENOSPC.  This confuses irqbalance's
+logic, the topo_obj.interrupts list no longer matches the irqs actually
+on that CPU or cache domain, and results in floods of error messages.
+See https://github.com/Irqbalance/irqbalance/issues/303 for details.
+
+For an easy fix, track the number of IRQ slots still free on each CPU.
+We start with INT_MAX meaning "unknown" and when we first get a -ENOSPC,
+we know we have no slots left.  From there update the slots count each
+time we migrate IRQs to/from the CPU core topo_obj.  We may never see an
+-ENOSPC and in that case there's no change in current logic, we never
+start tracking.
+
+This way we don't need to know ahead of time how many slots the kernel
+has for each CPU.  The number may be arch specific (it is about 200 on
+x86-64) and is dependent on the number managed IRQs kernel has
+registered, so we don't want to guess.  This is also more tolerant to
+the topo_obj.interrupts lists not matching exactly the kernel's idea of
+each irq's current affinity, e.g. due to -EIO errors in the smp_affinity
+writes.
+
+For now only do the tracking at OBJ_TYPE_CPU level so we don't have to
+update slots_left for all parent objs.
+
+Th commit doesn't try to stop an ongoing activation of all the IRQs
+already scheduled for moving to one cpu, when that cpu starts returning
+ENOSPC.  We'll still see a bunch of those errors in that iteration.
+But in subsequent calculate_placement() iterations we avoid assigning
+more IRQs to that cpu than we were able to successfully move before.
+---
+ activate.c   | 13 ++++++++++++-
+ classify.c   |  2 ++
+ cputree.c    | 10 ++++++++++
+ irqbalance.c |  3 +++
+ irqbalance.h |  1 +
+ irqlist.c    | 11 ++++++++++-
+ placement.c  |  3 +++
+ types.h      |  1 +
+ 8 files changed, 42 insertions(+), 2 deletions(-)
+
+diff --git a/activate.c b/activate.c
+index 0c1e7a1..10ad57d 100644
+--- a/activate.c
++++ b/activate.c
+@@ -99,7 +99,6 @@ error:
+ 		"Cannot change IRQ %i affinity: %s\n",
+ 		info->irq, strerror(errsave));
+ 	switch (errsave) {
+-	case ENOSPC: /* Specified CPU APIC is full. */
+ 	case EAGAIN: /* Interrupted by signal. */
+ 	case EBUSY: /* Affinity change already in progress. */
+ 	case EINVAL: /* IRQ would be bound to no CPU. */
+@@ -107,6 +106,18 @@ error:
+ 	case ENOMEM: /* Kernel cannot allocate CPU mask. */
+ 		/* Do not blacklist the IRQ on transient errors. */
+ 		break;
++	case ENOSPC: /* Specified CPU APIC is full. */
++		if (info->assigned_obj->obj_type != OBJ_TYPE_CPU)
++			break;
++
++		if (info->assigned_obj->slots_left > 0)
++			info->assigned_obj->slots_left = -1;
++		else
++			/* Negative slots to count how many we need to free */
++			info->assigned_obj->slots_left--;
++
++		force_rebalance_irq(info, NULL);
++		break;
+ 	default:
+ 		/* Any other error is considered permanent. */
+ 		info->level = BALANCE_NONE;
+diff --git a/classify.c b/classify.c
+index 08340db..69d72ac 100644
+--- a/classify.c
++++ b/classify.c
+@@ -883,6 +883,8 @@ static void remove_no_existing_irq(struct irq_info *info, void *data __attribute
+ 		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);
++			/* Probe number of slots again, don't guess whether the IRQ left a free slot */
++			info->assigned_obj->slots_left = INT_MAX;
+ 		}
+ 	}
+ 	free_irq(info, NULL);
+diff --git a/cputree.c b/cputree.c
+index d66be55..6c7b3b4 100644
+--- a/cputree.c
++++ b/cputree.c
+@@ -595,3 +595,13 @@ int get_cpu_count(void)
+ 	return g_list_length(cpus);
+ }
+ 
++static void clear_obj_slots(struct topo_obj *d, void *data __attribute__((unused)))
++{
++	d->slots_left = INT_MAX;
++	for_each_object(d->children, clear_obj_slots, NULL);
++}
++
++void clear_slots(void)
++{
++	for_each_object(numa_nodes, clear_obj_slots, NULL);
++}
+diff --git a/irqbalance.c b/irqbalance.c
+index 7efbc98..1490336 100644
+--- a/irqbalance.c
++++ b/irqbalance.c
+@@ -298,6 +298,7 @@ gboolean scan(gpointer data __attribute__((unused)))
+ 		} while (need_rebuild);
+ 
+ 		for_each_irq(NULL, force_rebalance_irq, NULL);
++		clear_slots();
+ 		parse_proc_interrupts();
+ 		parse_proc_stat();
+ 		return TRUE;
+@@ -695,6 +696,8 @@ int main(int argc, char** argv)
+ 	parse_proc_interrupts();
+ 	parse_proc_stat();
+ 
++	clear_slots();
++
+ #ifdef HAVE_IRQBALANCEUI
+ 	if (init_socket()) {
+ 		ret = EXIT_FAILURE;
+diff --git a/irqbalance.h b/irqbalance.h
+index 76640dd..47e40cc 100644
+--- a/irqbalance.h
++++ b/irqbalance.h
+@@ -98,6 +98,7 @@ extern struct topo_obj *get_numa_node(int nodeid);
+ #define cpu_numa_node(cpu) ((cpu)->parent->numa_nodes)
+ extern struct topo_obj *find_cpu_core(int cpunr);
+ extern int get_cpu_count(void);
++extern void clear_slots(void);
+ 
+ /*
+  * irq db functions
+diff --git a/irqlist.c b/irqlist.c
+index 304b1c6..9483a11 100644
+--- a/irqlist.c
++++ b/irqlist.c
+@@ -211,8 +211,17 @@ void migrate_irq_obj(struct topo_obj *from, struct topo_obj *to, struct irq_info
+ 
+ 	migrate_irq(from_list, to_list, info);
+ 
+-	if (to)
++	if (from) {
++		if (from->slots_left != INT_MAX)
++			from->slots_left++;
++	}
++
++	if (to) {
++		if (to->slots_left != INT_MAX)
++			to->slots_left--;
++
+ 		to->load += info->load + 1;
++	}
+ 
+ 	info->assigned_obj = to;
+ }
+diff --git a/placement.c b/placement.c
+index f156e0e..3276dea 100644
+--- a/placement.c
++++ b/placement.c
+@@ -59,6 +59,9 @@ static void find_best_object(struct topo_obj *d, void *data)
+ 	if (d->powersave_mode)
+ 		return;
+ 
++	if (d->slots_left <= 0)
++		return;
++
+ 	newload = d->load;
+ 	if (newload < best->best_cost) {
+ 		best->best = d;
+diff --git a/types.h b/types.h
+index ea1fae8..5c66bf9 100644
+--- a/types.h
++++ b/types.h
+@@ -56,6 +56,7 @@ struct topo_obj {
+ 	GList *children;
+ 	GList *numa_nodes;
+ 	GList **obj_type_list;
++	int slots_left;
+ };
+ 
+ struct irq_info {
+-- 
+2.47.0
+
diff --git a/0024-Disable-linking-to-curses-if-without-irqbalance-ui-i.patch b/0024-Disable-linking-to-curses-if-without-irqbalance-ui-i.patch
new file mode 100644
index 0000000..3fe3275
--- /dev/null
+++ b/0024-Disable-linking-to-curses-if-without-irqbalance-ui-i.patch
@@ -0,0 +1,43 @@
+From c90599b15e847d019f8d3058b5c9010d8919a786 Mon Sep 17 00:00:00 2001
+From: Andrew Soknacki <6068404+Sout@users.noreply.github.com>
+Date: Thu, 20 Jun 2024 20:38:45 -0400
+Subject: [PATCH 24/44] Disable linking to curses if --without-irqbalance-ui is
+ specified.
+
+---
+ configure.ac | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index 7ec6060..4718444 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -38,12 +38,6 @@ AC_CHECK_LIB(m, floor)
+ PKG_CHECK_MODULES([GLIB2], [glib-2.0], [], [AC_MSG_ERROR([glib-2.0 is required])])
+ 
+ PKG_CHECK_MODULES([NCURSESW], [ncursesw], [has_ncursesw=yes], [AC_CHECK_LIB(curses, mvprintw)])
+-AS_IF([test "x$has_ncursesw" = "xyes"], [
+-  AC_SUBST([NCURSESW_CFLAGS])
+-  AC_SUBST([NCURSESW_LIBS])
+-  LIBS="$LIBS $NCURSESW_LIBS"
+-  AC_SUBST([LIBS])
+-])
+ 
+ AC_CANONICAL_HOST
+ 
+@@ -78,6 +72,12 @@ AC_ARG_WITH([irqbalance-ui],
+ AS_IF(
+   [test "x$with_irqbalanceui" = "xyes"], [
+     AC_DEFINE([HAVE_IRQBALANCEUI], 1, [Build irqbalance ui component.])
++    AS_IF([test "x$has_ncursesw" = "xyes"], [
++        AC_SUBST([NCURSESW_CFLAGS])
++        AC_SUBST([NCURSESW_LIBS])
++        LIBS="$LIBS $NCURSESW_LIBS"
++        AC_SUBST([LIBS])
++   ])
+ ])
+ AM_CONDITIONAL([IRQBALANCEUI], [test x$with_irqbalanceui = xyes])
+ 
+-- 
+2.47.0
+
diff --git a/0025-Remove-extraneous-space-causing-with-systemd-not-be-.patch b/0025-Remove-extraneous-space-causing-with-systemd-not-be-.patch
new file mode 100644
index 0000000..e6e1619
--- /dev/null
+++ b/0025-Remove-extraneous-space-causing-with-systemd-not-be-.patch
@@ -0,0 +1,26 @@
+From 9851c8c9bf8de26295e633475d35e36ccbe93714 Mon Sep 17 00:00:00 2001
+From: Andrew Soknacki <6068404+Sout@users.noreply.github.com>
+Date: Thu, 20 Jun 2024 20:38:45 -0400
+Subject: [PATCH 25/44] Remove extraneous space causing --with-systemd not be
+ aligned
+
+---
+ configure.ac | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/configure.ac b/configure.ac
+index 4718444..678f66e 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -82,7 +82,7 @@ AS_IF(
+ AM_CONDITIONAL([IRQBALANCEUI], [test x$with_irqbalanceui = xyes])
+ 
+ AC_ARG_WITH([systemd],
+-  [ AS_HELP_STRING([--with-systemd],[Add systemd-lib support])]
++  [AS_HELP_STRING([--with-systemd],[Add systemd-lib support])]
+ )
+ AS_IF(
+   [test "x$with_systemd" = xyes], [
+-- 
+2.47.0
+
diff --git a/0026-direct-initialize-msghdr-members.patch b/0026-direct-initialize-msghdr-members.patch
new file mode 100644
index 0000000..acddbef
--- /dev/null
+++ b/0026-direct-initialize-msghdr-members.patch
@@ -0,0 +1,38 @@
+From 5010a9766aff8a0c8e8644acef40d025ec2e0a48 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Sun, 30 Jun 2024 16:50:50 -0700
+Subject: [PATCH 26/44] direct initialize msghdr members
+
+Standard C99. { 0 } is somewhat interesting as some compilers warn about
+uninitialized members, which is bogus.
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ irqbalance.c | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/irqbalance.c b/irqbalance.c
+index 1490336..64b41f7 100644
+--- a/irqbalance.c
++++ b/irqbalance.c
+@@ -406,11 +406,12 @@ gboolean sock_handle(gint fd, GIOCondition condition, gpointer user_data __attri
+ 	int valid_user = 0;
+ 
+ 	struct iovec iov = { buff, 500 };
+-	struct msghdr msg = { 0 };
+-	msg.msg_iov = &iov;
+-	msg.msg_iovlen = 1;
+-	msg.msg_control = malloc(CMSG_SPACE(sizeof(struct ucred)));
+-	msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred));
++	struct msghdr msg = {
++		.msg_iov = &iov,
++		.msg_iovlen = 1,
++		.msg_control = malloc(CMSG_SPACE(sizeof(struct ucred))),
++		.msg_controllen = CMSG_SPACE(sizeof(struct ucred)),
++	};
+ 
+ 	struct cmsghdr *cmsg;
+ 
+-- 
+2.47.0
+
diff --git a/0027-direct-initialize-iovec.patch b/0027-direct-initialize-iovec.patch
new file mode 100644
index 0000000..d172e6d
--- /dev/null
+++ b/0027-direct-initialize-iovec.patch
@@ -0,0 +1,31 @@
+From d36ae562c668c25d737b90c4ca1a84d8e9079712 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Thu, 4 Jul 2024 14:04:14 -0700
+Subject: [PATCH 27/44] direct initialize iovec
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ ui/irqbalance-ui.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/ui/irqbalance-ui.c b/ui/irqbalance-ui.c
+index 06f4602..2e6b7d3 100644
+--- a/ui/irqbalance-ui.c
++++ b/ui/irqbalance-ui.c
+@@ -118,9 +118,10 @@ try_again:
+ 	}
+ 
+ 	struct msghdr *msg = create_credentials_msg();
+-	struct iovec iov;
+-	iov.iov_base = (void *) string;
+-	iov.iov_len = strlen(string);
++	struct iovec iov = {
++		.iov_base = (void *) string,
++		.iov_len = strlen(string),
++	};
+ 	msg->msg_iov = &iov;
+ 	sendmsg(socket_fd, msg, 0);
+ 
+-- 
+2.47.0
+
diff --git a/0028-clang-tidy-add-missing-free.patch b/0028-clang-tidy-add-missing-free.patch
new file mode 100644
index 0000000..db490a8
--- /dev/null
+++ b/0028-clang-tidy-add-missing-free.patch
@@ -0,0 +1,27 @@
+From 16564e3ced6517ddd15bbcfb84df704f7f614c3b Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Sun, 30 Jun 2024 17:26:11 -0700
+Subject: [PATCH 28/44] clang-tidy: add missing free
+
+Found with clang-analyzer-unix.Malloc
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ ui/ui.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/ui/ui.c b/ui/ui.c
+index c580f85..5401d02 100644
+--- a/ui/ui.c
++++ b/ui/ui.c
+@@ -112,6 +112,7 @@ int get_valid_sleep_input(int column_offest)
+ 		char *error;
+ 		new_sleep = strtol(input, &error, 10);
+ 		if((*error == '\0') && (new_sleep >= 1)) {
++			free(input);
+ 			break;
+ 		}
+ 		new_sleep = setup.sleep;
+-- 
+2.47.0
+
diff --git a/0029-clang-tidy-don-t-assign-in-if.patch b/0029-clang-tidy-don-t-assign-in-if.patch
new file mode 100644
index 0000000..4bd98d3
--- /dev/null
+++ b/0029-clang-tidy-don-t-assign-in-if.patch
@@ -0,0 +1,43 @@
+From 2ed4dd0233a6e070c7ad128594892510d28f2ea9 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Sun, 30 Jun 2024 17:14:55 -0700
+Subject: [PATCH 29/44] clang-tidy: don't assign in if
+
+Found with bugprone-assignment-in-if-condition
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ irqbalance.c | 17 ++++++++++-------
+ 1 file changed, 10 insertions(+), 7 deletions(-)
+
+diff --git a/irqbalance.c b/irqbalance.c
+index 64b41f7..dfd5d2d 100644
+--- a/irqbalance.c
++++ b/irqbalance.c
+@@ -654,13 +654,16 @@ int main(int argc, char** argv)
+ 		if (daemon(0,0))
+ 			exit(EXIT_FAILURE);
+ 		/* Write pidfile which can be used to avoid starting multiple instances */
+-		if (pidfile && (pidfd = open(pidfile,
+-			O_WRONLY | O_CREAT | O_EXCL | O_TRUNC,
+-			S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) >= 0) {
+-			char str[16];
+-			snprintf(str, sizeof(str), "%u\n", getpid());
+-			write(pidfd, str, strlen(str));
+-			close(pidfd);
++		if (pidfile) {
++			pidfd = open(pidfile,
++				O_WRONLY | O_CREAT | O_EXCL | O_TRUNC,
++				S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
++			if (pidfd >= 0) {
++				char str[16];
++				snprintf(str, sizeof(str), "%u\n", getpid());
++				write(pidfd, str, strlen(str));
++				close(pidfd);
++			}
+ 		}
+ 	}
+ 
+-- 
+2.47.0
+
diff --git a/0030-clang-tidy-remove-pointless-casts.patch b/0030-clang-tidy-remove-pointless-casts.patch
new file mode 100644
index 0000000..6dd2131
--- /dev/null
+++ b/0030-clang-tidy-remove-pointless-casts.patch
@@ -0,0 +1,37 @@
+From 13916f7e14a2e6270e40519aa2f59319136dad08 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Sun, 30 Jun 2024 17:16:30 -0700
+Subject: [PATCH 30/44] clang-tidy: remove pointless casts
+
+Found with readability-redundant-casting
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ cputree.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/cputree.c b/cputree.c
+index 6c7b3b4..4820bdf 100644
+--- a/cputree.c
++++ b/cputree.c
+@@ -271,7 +271,7 @@ static struct topo_obj* add_cpu_to_cache_domain(struct topo_obj *cpu,
+ 	entry = g_list_find(cache->children, cpu);
+ 	if (!entry) {
+ 		cache->children = g_list_append(cache->children, cpu);
+-		cpu->parent = (struct topo_obj *)cache;
++		cpu->parent = cache;
+ 	}
+ 
+ 	if (!numa_avail || (nodeid > NUMA_NO_NODE))
+@@ -441,7 +441,7 @@ static void dump_numa_node_num(struct topo_obj *p, void *data __attribute__((unu
+ 
+ static void dump_balance_obj(struct topo_obj *d, void *data __attribute__((unused)))
+ {
+-	struct topo_obj *c = (struct topo_obj *)d;
++	struct topo_obj *c = d;
+ 	log(TO_CONSOLE, LOG_INFO, "%s%s%s%sCPU number %i  numa_node is ",
+ 	    log_indent, log_indent, log_indent, log_indent, c->number);
+ 	for_each_object(cpu_numa_node(c), dump_numa_node_num, NULL);
+-- 
+2.47.0
+
diff --git a/0031-use-g_malloc-and-friends.patch b/0031-use-g_malloc-and-friends.patch
new file mode 100644
index 0000000..0ec328e
--- /dev/null
+++ b/0031-use-g_malloc-and-friends.patch
@@ -0,0 +1,314 @@
+From a7cfbeb0a6609dd56507c129ea2163816c4a4a5f Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Sun, 30 Jun 2024 16:49:20 -0700
+Subject: [PATCH 31/44] use g_malloc and friends
+
+These should not fail. There are no null checks.
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ irqbalance.c       |  4 ++--
+ ui/helpers.c       |  6 ++---
+ ui/irqbalance-ui.c | 57 ++++++++++++++++++++--------------------------
+ ui/ui.c            | 17 +++++++-------
+ 4 files changed, 38 insertions(+), 46 deletions(-)
+
+diff --git a/irqbalance.c b/irqbalance.c
+index dfd5d2d..3875d5d 100644
+--- a/irqbalance.c
++++ b/irqbalance.c
+@@ -409,7 +409,7 @@ gboolean sock_handle(gint fd, GIOCondition condition, gpointer user_data __attri
+ 	struct msghdr msg = {
+ 		.msg_iov = &iov,
+ 		.msg_iovlen = 1,
+-		.msg_control = malloc(CMSG_SPACE(sizeof(struct ucred))),
++		.msg_control = g_malloc(CMSG_SPACE(sizeof(struct ucred))),
+ 		.msg_controllen = CMSG_SPACE(sizeof(struct ucred)),
+ 	};
+ 
+@@ -540,7 +540,7 @@ out_close:
+ 	}
+ 
+ out:
+-	free(msg.msg_control);
++	g_free(msg.msg_control);
+ 	return TRUE;
+ }
+ 
+diff --git a/ui/helpers.c b/ui/helpers.c
+index 3b4c51f..529b1b3 100644
+--- a/ui/helpers.c
++++ b/ui/helpers.c
+@@ -73,7 +73,7 @@ char * hex_to_bitmap(char hex_digit) {
+ 		return "0000\0";
+ 	}
+ 
+-	char *bitmap = malloc(5 * sizeof(char));
++	char *bitmap = g_malloc_n(5, sizeof(char));
+ 	bitmap[4] = '\0';
+ 	int i;
+ 	for(i = 3; i >= 0; i--) {
+@@ -86,7 +86,7 @@ char * hex_to_bitmap(char hex_digit) {
+ gpointer copy_cpu_ban(gconstpointer src, gpointer data __attribute__((unused)))
+ {
+ 	cpu_ban_t *old = (cpu_ban_t *)src; 
+-	cpu_ban_t *new = malloc(sizeof(cpu_ban_t));
++	cpu_ban_t *new = g_malloc(sizeof(cpu_ban_t));
+ 	new->number = old->number;
+ 	new->is_banned = old->is_banned;
+ 	new->is_changed = 0;
+@@ -96,7 +96,7 @@ gpointer copy_cpu_ban(gconstpointer src, gpointer data __attribute__((unused)))
+ gpointer copy_irq(gconstpointer src, gpointer data __attribute__((unused)))
+ {
+ 	irq_t *old = (irq_t *)src; 
+-	irq_t *new = malloc(sizeof(irq_t));
++	irq_t *new = g_malloc(sizeof(irq_t));
+ 	new->vector = old->vector;
+ 	new->load = old->load;
+ 	new->diff = old->diff;
+diff --git a/ui/irqbalance-ui.c b/ui/irqbalance-ui.c
+index 2e6b7d3..9bb76fd 100644
+--- a/ui/irqbalance-ui.c
++++ b/ui/irqbalance-ui.c
+@@ -30,15 +30,14 @@ static int default_bufsz = 8192;
+ 
+ struct msghdr * create_credentials_msg(void)
+ {
+-	struct ucred *credentials = malloc(sizeof(struct ucred));
++	struct ucred *credentials = g_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));
++	struct msghdr *msg = g_malloc0(sizeof(struct msghdr));
+ 	msg->msg_iovlen = 1;
+-	msg->msg_control = malloc(CMSG_SPACE(sizeof(struct ucred)));
++	msg->msg_control = g_malloc(CMSG_SPACE(sizeof(struct ucred)));
+ 	msg->msg_controllen = CMSG_SPACE(sizeof(struct ucred));
+ 
+ 	struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg);
+@@ -47,7 +46,7 @@ struct msghdr * create_credentials_msg(void)
+ 	cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
+ 	memcpy(CMSG_DATA(cmsg), credentials, sizeof(struct ucred));
+ 
+-	free(credentials);
++	g_free(credentials);
+ 	return msg;
+ }
+ 
+@@ -100,8 +99,8 @@ void send_settings(char *data)
+ 	sendmsg(socket_fd, msg, 0);
+ 
+ 	close(socket_fd);
+-	free(msg->msg_control);
+-	free(msg);
++	g_free(msg->msg_control);
++	g_free(msg);
+ }
+ 
+ char * get_data(char *string)
+@@ -125,20 +124,20 @@ try_again:
+ 	msg->msg_iov = &iov;
+ 	sendmsg(socket_fd, msg, 0);
+ 
+-	char *data = malloc(default_bufsz);
++	char *data = g_malloc(default_bufsz);
+ 	int len = recv(socket_fd, data, default_bufsz, MSG_TRUNC);
+ 	close(socket_fd);
+-	free(msg->msg_control);
+-	free(msg);
++	g_free(msg->msg_control);
++	g_free(msg);
+ 	if (len < 0) {
+-		free(data);
++		g_free(data);
+ 		return NULL;
+ 	}
+ 	data[len] = '\0';
+ 	if (len >= default_bufsz) {
+ 		/* msg was truncated, increase bufsz and try again */
+ 		default_bufsz += 8192;
+-		free(data);
++		g_free(data);
+ 		goto try_again;
+ 	}
+ 	return data;
+@@ -163,7 +162,7 @@ void parse_setup(char *setup_data)
+ 	token = strtok_r(NULL, " ", &ptr);
+ 	/* Parse banned IRQ data */
+ 	while(!strncmp(token, "IRQ", strlen("IRQ"))) {
+-		new_irq = malloc(sizeof(irq_t));
++		new_irq = g_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")) != 0) goto out;
+@@ -189,26 +188,24 @@ void parse_setup(char *setup_data)
+ 		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));
++				uint64_t *banned_cpu = g_malloc(sizeof(uint64_t));
+ 				*banned_cpu = cpu;
+ 				setup.banned_cpus = g_list_append(setup.banned_cpus,
+ 								banned_cpu);
+ 			}
+ 			cpu++;
+ 		}
+-		free(map);
++		g_free(map);
+ 	
+ 	}
+-	free(copy);
++	g_free(copy);
+ 	return;
+ 
+ out: {
+ 	/* Invalid data presented */
+ 	printf("Invalid data sent.  Unexpected token: %s", token);
+-	if (new_irq) {
+-		free(new_irq);
+-	}
+-	free(copy);
++	g_free(new_irq);
++	g_free(copy);
+ 	g_list_free(tree);
+ 	exit(1);
+ }
+@@ -252,7 +249,7 @@ void assign_cpu_lists(cpu_node_t *node, void *data __attribute__((unused)))
+ 
+ void assign_cpu_mask(cpu_node_t *node, void *data __attribute__((unused)))
+ {
+-	char *mask = malloc(16 * sizeof(char));
++	char *mask = g_malloc_n(16, sizeof(char));
+ 	mask[0] = '\0';
+ 	unsigned int sum = 0;
+ 	GList *list_entry = g_list_first(node->cpu_list);
+@@ -281,7 +278,7 @@ void parse_into_tree(char *data)
+ 	if (!data || strlen(data) == 0)
+ 		return;
+ 
+-	copy = strdup(data);
++	copy = g_strdup(data);
+ 	if (!copy)
+ 		return;
+ 
+@@ -289,8 +286,8 @@ void parse_into_tree(char *data)
+ 	while(token != NULL) {
+ 		/* Parse node data */
+ 		if(strncmp(token, "TYPE", strlen("TYPE")) != 0) {
+-			free(copy);
+-			 goto out;
++			g_free(copy);
++			goto out;
+ 		}
+ 		new = g_malloc0(sizeof(cpu_node_t));
+ 		new->type = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
+@@ -312,7 +309,7 @@ void parse_into_tree(char *data)
+ 
+ 		/* Parse assigned IRQ data */
+ 		while((token != NULL) && (!strncmp(token, "IRQ", strlen("IRQ")))) {
+-			new_irq = malloc(sizeof(irq_t));
++			new_irq = g_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")) != 0) goto out;
+@@ -343,7 +340,7 @@ void parse_into_tree(char *data)
+ 
+ 		new = NULL;
+ 	}
+-	free(copy);
++	g_free(copy);
+ 	for_each_node(tree, assign_cpu_lists, NULL);
+ 	for_each_node(tree, assign_cpu_mask, NULL);
+ 	return;
+@@ -351,12 +348,8 @@ void parse_into_tree(char *data)
+ out: {
+ 	/* Invalid data presented */
+ 	printf("Invalid data sent.  Unexpected token: %s\n", token);
+-	if (new_irq) {
+-		free(new_irq);
+-	}
+-	if (new) {
+-		free(new);
+-	}
++	g_free(new_irq);
++	g_free(new);
+ 	g_list_free(tree);
+ 	exit(1);
+ }
+diff --git a/ui/ui.c b/ui/ui.c
+index 5401d02..be5df5e 100644
+--- a/ui/ui.c
++++ b/ui/ui.c
+@@ -51,7 +51,7 @@ void show_footer(void)
+ 
+ char * check_control_in_sleep_input(int max_len, int column_offest, int line_offset)
+ {
+-	char *input_to = malloc(max_len * sizeof(char));
++	char *input_to = g_malloc_n(max_len, sizeof(char));
+ 	int iteration = 0;
+ 	while(iteration < max_len) {
+ 		int new = getch();
+@@ -76,7 +76,7 @@ char * check_control_in_sleep_input(int max_len, int column_offest, int line_off
+ 			attrset(COLOR_PAIR(5) | A_REVERSE | A_BOLD);
+ 			break;
+ 		case 27:
+-			free(input_to);
++			g_free(input_to);
+ 			return NULL;
+ 		default:
+ 			input_to[iteration] = new;
+@@ -112,7 +112,7 @@ int get_valid_sleep_input(int column_offest)
+ 		char *error;
+ 		new_sleep = strtol(input, &error, 10);
+ 		if((*error == '\0') && (new_sleep >= 1)) {
+-			free(input);
++			g_free(input);
+ 			break;
+ 		}
+ 		new_sleep = setup.sleep;
+@@ -121,7 +121,7 @@ int get_valid_sleep_input(int column_offest)
+ 			"Invalid input: %s								",
+ 			input);
+ 		refresh();
+-		free(input);
++		g_free(input);
+ 	}
+ 
+ 	attrset(COLOR_PAIR(1));
+@@ -132,7 +132,7 @@ int get_valid_sleep_input(int column_offest)
+ 
+ void get_banned_cpu(int *cpu, char *data __attribute__((unused)))
+ {
+-	cpu_ban_t *new = malloc(sizeof(cpu_ban_t));
++	cpu_ban_t *new = g_malloc(sizeof(cpu_ban_t));
+ 	new->number = *cpu;
+ 	new->is_banned = 1;
+ 	all_cpus = g_list_append(all_cpus, new);
+@@ -237,7 +237,7 @@ void get_new_cpu_ban_values(cpu_ban_t *cpu, void *data)
+ void get_cpu(cpu_node_t *node, void *data __attribute__((unused)))
+ {
+ 	if(node->type == OBJ_TYPE_CPU) {
+-		cpu_ban_t *new = malloc(sizeof(cpu_ban_t));
++		cpu_ban_t *new = g_malloc(sizeof(cpu_ban_t));
+ 		new->number = node->number;
+ 		new->is_banned = 0;
+ 		all_cpus = g_list_append(all_cpus, new);
+@@ -402,10 +402,9 @@ void get_irq_name(int end)
+ 	char buffer[128];
+ 
+ 	if (irq_name == NULL) {
+-		irq_name = malloc(sizeof(char *) * LINES);
++		irq_name = g_malloc_n(LINES, sizeof(char *));
+ 		for (i = 4; i < LINES; i++) {
+-			irq_name[i] = malloc(sizeof(char) * 50);
+-			memset(irq_name[i], 0, sizeof(char) * 50);
++			irq_name[i] = g_malloc0_n(50, sizeof(char));
+ 		}
+ 	}
+ 
+-- 
+2.47.0
+
diff --git a/0032-remove-malloc-from-ucred.patch b/0032-remove-malloc-from-ucred.patch
new file mode 100644
index 0000000..e84ac44
--- /dev/null
+++ b/0032-remove-malloc-from-ucred.patch
@@ -0,0 +1,46 @@
+From 7622883bad5b75d28d8e1cb3a2c48b9589aa82b4 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Thu, 4 Jul 2024 13:54:12 -0700
+Subject: [PATCH 32/44] remove malloc from ucred
+
+This just gets freed at the end after getting copied.
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ ui/irqbalance-ui.c | 13 ++++++-------
+ 1 file changed, 6 insertions(+), 7 deletions(-)
+
+diff --git a/ui/irqbalance-ui.c b/ui/irqbalance-ui.c
+index 9bb76fd..d281f47 100644
+--- a/ui/irqbalance-ui.c
++++ b/ui/irqbalance-ui.c
+@@ -30,10 +30,11 @@ static int default_bufsz = 8192;
+ 
+ struct msghdr * create_credentials_msg(void)
+ {
+-	struct ucred *credentials = g_malloc(sizeof(struct ucred));
+-	credentials->pid = getpid();
+-	credentials->uid = geteuid();
+-	credentials->gid = getegid();
++	struct ucred credentials = {
++		.pid = getpid(),
++		.uid = geteuid(),
++		.gid = getegid(),
++	};
+ 
+ 	struct msghdr *msg = g_malloc0(sizeof(struct msghdr));
+ 	msg->msg_iovlen = 1;
+@@ -44,9 +45,7 @@ struct msghdr * create_credentials_msg(void)
+ 	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));
+-
+-	g_free(credentials);
++	memcpy(CMSG_DATA(cmsg), &credentials, sizeof(struct ucred));
+ 	return msg;
+ }
+ 
+-- 
+2.47.0
+
diff --git a/0033-gcc-analyzer-add-NULL-checks.patch b/0033-gcc-analyzer-add-NULL-checks.patch
new file mode 100644
index 0000000..f2049c6
--- /dev/null
+++ b/0033-gcc-analyzer-add-NULL-checks.patch
@@ -0,0 +1,42 @@
+From e39848bfe45ea580bc0fd3b966a4199fb2fcd5b8 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Thu, 4 Jul 2024 14:13:54 -0700
+Subject: [PATCH 33/44] gcc analyzer: add NULL checks
+
+Found with -Wanalyzer-null-argument
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ irqbalance.c | 2 +-
+ ui/ui.c      | 2 ++
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/irqbalance.c b/irqbalance.c
+index 3875d5d..0b77376 100644
+--- a/irqbalance.c
++++ b/irqbalance.c
+@@ -503,7 +503,7 @@ gboolean sock_handle(gint fd, GIOCondition condition, gpointer user_data __attri
+ 						recv_size - strlen("settings cpus "));
+ 				cpu_ban_string[recv_size - strlen("settings cpus ")] = '\0';
+ 				banned_cpumask_from_ui = strtok(cpu_ban_string, " ");
+-				if (!strncmp(banned_cpumask_from_ui, "NULL", strlen("NULL"))) {
++				if (banned_cpumask_from_ui && !strncmp(banned_cpumask_from_ui, "NULL", strlen("NULL"))) {
+ 					banned_cpumask_from_ui = NULL;
+ 					free(cpu_ban_string);
+ 					cpu_ban_string = NULL;
+diff --git a/ui/ui.c b/ui/ui.c
+index be5df5e..8d7c493 100644
+--- a/ui/ui.c
++++ b/ui/ui.c
+@@ -418,6 +418,8 @@ void get_irq_name(int end)
+ 	cmd = alloca(sizeof(char) * (len + 1));
+ 	snprintf(cmd, len + 1, "cat /proc/interrupts | awk '{for (i=%d;i<=NF;i++)printf(\"%%s \", $i);print \"\"}' | cut -c-49", cpunr + 2);
+ 	output = popen(cmd, "r");
++	if (!output)
++		return;
+ 	for (i = 0; i <= offset; i++)
+ 		fgets(buffer, 50, output);
+ 	for (i = 4; i < end; i++)
+-- 
+2.47.0
+
diff --git a/0034-gcc-analyzer-increase-socket_name-size.patch b/0034-gcc-analyzer-increase-socket_name-size.patch
new file mode 100644
index 0000000..48de8ec
--- /dev/null
+++ b/0034-gcc-analyzer-increase-socket_name-size.patch
@@ -0,0 +1,30 @@
+From 4d537284c8d272cf05a64ee8ba226dd54c496a55 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Thu, 4 Jul 2024 14:24:30 -0700
+Subject: [PATCH 34/44] gcc analyzer: increase socket_name size
+
+Now matches sun_path size.
+
+Found with -Wanalyzer-out-of-bounds
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ irqbalance.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/irqbalance.c b/irqbalance.c
+index 0b77376..4f3b97d 100644
+--- a/irqbalance.c
++++ b/irqbalance.c
+@@ -70,7 +70,7 @@ unsigned long migrate_ratio = 0;
+ 
+ #ifdef HAVE_IRQBALANCEUI
+ int socket_fd;
+-char socket_name[64];
++char socket_name[108];
+ char *banned_cpumask_from_ui = NULL;
+ #endif
+ 
+-- 
+2.47.0
+
diff --git a/0035-use-g_strdup_printf.patch b/0035-use-g_strdup_printf.patch
new file mode 100644
index 0000000..37d6a4d
--- /dev/null
+++ b/0035-use-g_strdup_printf.patch
@@ -0,0 +1,66 @@
+From 434180846db9b1516e1533664b56a253800f88aa Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Thu, 4 Jul 2024 16:11:10 -0700
+Subject: [PATCH 35/44] use g_strdup_printf
+
+Simplifies the code.
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ ui/ui.c | 15 +++++++--------
+ 1 file changed, 7 insertions(+), 8 deletions(-)
+
+diff --git a/ui/ui.c b/ui/ui.c
+index 8d7c493..752744a 100644
+--- a/ui/ui.c
++++ b/ui/ui.c
+@@ -396,7 +396,7 @@ void print_assigned_objects_string(irq_t *irq, int *line_offset)
+ 
+ void get_irq_name(int end)
+ {
+-	int i, cpunr, len;
++	int i, cpunr;
+ 	FILE *output;
+ 	char *cmd;
+ 	char buffer[128];
+@@ -414,10 +414,9 @@ void get_irq_name(int end)
+ 	fscanf(output, "%d", &cpunr);
+ 	pclose(output);
+ 
+-	len = snprintf(NULL, 0, "cat /proc/interrupts | awk '{for (i=%d;i<=NF;i++)printf(\"%%s \", $i);print \"\"}' | cut -c-49", cpunr + 2);
+-	cmd = alloca(sizeof(char) * (len + 1));
+-	snprintf(cmd, len + 1, "cat /proc/interrupts | awk '{for (i=%d;i<=NF;i++)printf(\"%%s \", $i);print \"\"}' | cut -c-49", cpunr + 2);
++	cmd = g_strdup_printf("cat /proc/interrupts | awk '{for (i=%d;i<=NF;i++)printf(\"%%s \", $i);print \"\"}' | cut -c-49", cpunr + 2);
+ 	output = popen(cmd, "r");
++	g_free(cmd);
+ 	if (!output)
+ 		return;
+ 	for (i = 0; i <= offset; i++)
+@@ -683,9 +682,9 @@ void handle_sleep_setting(void)
+ 	uint64_t new_sleep = get_valid_sleep_input(sleep_input_offset);
+ 	if(new_sleep != setup.sleep) {
+ 		setup.sleep = new_sleep;
+-		char settings_data[128];
+-		snprintf(settings_data, 128, "%s %" PRIu64, SET_SLEEP, new_sleep);
++		char *settings_data = g_strdup_printf("%s %" PRIu64, SET_SLEEP, new_sleep);
+ 		send_settings(settings_data);
++		g_free(settings_data);
+ 	}
+ 	attrset(COLOR_PAIR(5));
+ 	mvprintw(LINES - 2, 1, "Press <S> for changing sleep setup, <C> for CPU ban setup. ");
+@@ -739,10 +738,10 @@ void settings(void)
+ 	char *setup_data = get_data(SETUP);
+ 	parse_setup(setup_data);
+ 
+-	char info[128] = "Current sleep interval between rebalancing: \0";
+-	snprintf(info + strlen(info), 128 - strlen(info), "%" PRIu64 "\n", setup.sleep);
++	char *info = g_strdup_printf("Current sleep interval between rebalancing: %" PRIu64 "\n", setup.sleep);
+ 	attrset(COLOR_PAIR(1));
+ 	mvprintw(2, 3, "%s", info);
++	g_free(info);
+ 	print_all_cpus();
+ 	attrset(COLOR_PAIR(5));
+ 	mvprintw(LINES - 2, 1, "Press <S> for changing sleep setup, <C> for CPU ban setup. ");
+-- 
+2.47.0
+
diff --git a/0036-avoid-malloc-with-create_credentials_msg.patch b/0036-avoid-malloc-with-create_credentials_msg.patch
new file mode 100644
index 0000000..9ceff7b
--- /dev/null
+++ b/0036-avoid-malloc-with-create_credentials_msg.patch
@@ -0,0 +1,111 @@
+From 6f9f9e9556db8d4ec5dc5f662a5fb13167dfa111 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Sun, 7 Jul 2024 18:02:12 -0700
+Subject: [PATCH 36/44] avoid malloc with create_credentials_msg
+
+Not really needed.
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ ui/irqbalance-ui.c | 39 ++++++++++++++++++++-------------------
+ ui/irqbalance-ui.h |  2 +-
+ 2 files changed, 21 insertions(+), 20 deletions(-)
+
+diff --git a/ui/irqbalance-ui.c b/ui/irqbalance-ui.c
+index d281f47..f4cc48f 100644
+--- a/ui/irqbalance-ui.c
++++ b/ui/irqbalance-ui.c
+@@ -28,7 +28,7 @@ setup_t setup;
+ GMainLoop *main_loop;
+ static int default_bufsz = 8192;
+ 
+-struct msghdr * create_credentials_msg(void)
++struct msghdr create_credentials_msg(void)
+ {
+ 	struct ucred credentials = {
+ 		.pid = getpid(),
+@@ -36,12 +36,13 @@ struct msghdr * create_credentials_msg(void)
+ 		.gid = getegid(),
+ 	};
+ 
+-	struct msghdr *msg = g_malloc0(sizeof(struct msghdr));
+-	msg->msg_iovlen = 1;
+-	msg->msg_control = g_malloc(CMSG_SPACE(sizeof(struct ucred)));
+-	msg->msg_controllen = CMSG_SPACE(sizeof(struct ucred));
++	struct msghdr msg = {
++		.msg_iovlen = 1,
++		.msg_control = g_malloc(CMSG_SPACE(sizeof(struct ucred))),
++		.msg_controllen = CMSG_SPACE(sizeof(struct ucred)),
++	};
+ 
+-	struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg);
++	struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+ 	cmsg->cmsg_level = SOL_SOCKET;
+ 	cmsg->cmsg_type = SCM_CREDENTIALS;
+ 	cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
+@@ -90,16 +91,17 @@ void send_settings(char *data)
+ 		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);
++	struct iovec iov = {
++		.iov_base = (void *) data,
++		.iov_len = strlen(data),
++	};
++
++	struct msghdr msg = create_credentials_msg();
++	msg.msg_iov = &iov;
++	sendmsg(socket_fd, &msg, 0);
+ 
+ 	close(socket_fd);
+-	g_free(msg->msg_control);
+-	g_free(msg);
++	g_free(msg.msg_control);
+ }
+ 
+ char * get_data(char *string)
+@@ -115,19 +117,18 @@ try_again:
+ 		return NULL;
+ 	}
+ 
+-	struct msghdr *msg = create_credentials_msg();
++	struct msghdr msg = create_credentials_msg();
+ 	struct iovec iov = {
+ 		.iov_base = (void *) string,
+ 		.iov_len = strlen(string),
+ 	};
+-	msg->msg_iov = &iov;
+-	sendmsg(socket_fd, msg, 0);
++	msg.msg_iov = &iov;
++	sendmsg(socket_fd, &msg, 0);
+ 
+ 	char *data = g_malloc(default_bufsz);
+ 	int len = recv(socket_fd, data, default_bufsz, MSG_TRUNC);
+ 	close(socket_fd);
+-	g_free(msg->msg_control);
+-	g_free(msg);
++	g_free(msg.msg_control);
+ 	if (len < 0) {
+ 		g_free(data);
+ 		return NULL;
+diff --git a/ui/irqbalance-ui.h b/ui/irqbalance-ui.h
+index 178be4b..4a2982d 100644
+--- a/ui/irqbalance-ui.h
++++ b/ui/irqbalance-ui.h
+@@ -72,7 +72,7 @@ typedef struct setup {
+ 
+ /* Function prototypes */
+ 
+-struct msghdr * create_credentials_msg(void);
++struct msghdr create_credentials_msg(void);
+ int init_connection(void);
+ void send_settings(char *data);
+ char * get_data(char *string);
+-- 
+2.47.0
+
diff --git a/0037-conver-strncmp-to-g_str_has_prefix.patch b/0037-conver-strncmp-to-g_str_has_prefix.patch
new file mode 100644
index 0000000..052158a
--- /dev/null
+++ b/0037-conver-strncmp-to-g_str_has_prefix.patch
@@ -0,0 +1,268 @@
+From da75aae4effd6bc276bb1b882d68617cd02aa8e0 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Sun, 7 Jul 2024 17:17:33 -0700
+Subject: [PATCH 37/44] conver strncmp to g_str_has_prefix
+
+Avoids implicit bool conversions.
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
+ cputree.c          |  2 +-
+ irqbalance.c       | 21 +++++++++------------
+ numa.c             |  2 +-
+ procinterrupts.c   |  6 +++---
+ ui/irqbalance-ui.c | 30 +++++++++++++++---------------
+ ui/ui.c            |  6 +++---
+ 6 files changed, 32 insertions(+), 35 deletions(-)
+
+diff --git a/cputree.c b/cputree.c
+index 4820bdf..49b2f37 100644
+--- a/cputree.c
++++ b/cputree.c
+@@ -380,7 +380,7 @@ static void do_one_cpu(char *path)
+ 			entry = readdir(dir);
+ 			if (!entry)
+ 				break;
+-			if (strncmp(entry->d_name, "node", 4) == 0) {
++			if (g_str_has_prefix(entry->d_name, "node")) {
+ 				char *end;
+ 				int num;
+ 				num = strtol(entry->d_name + 4, &end, 10);
+diff --git a/irqbalance.c b/irqbalance.c
+index 4f3b97d..491a912 100644
+--- a/irqbalance.c
++++ b/irqbalance.c
+@@ -154,7 +154,7 @@ static void parse_command_line(int argc, char **argv)
+ 				add_cl_banned_module(optarg);
+ 				break;
+ 			case 'p':
+-				if (!strncmp(optarg, "off", strlen(optarg)))
++				if (g_str_has_prefix(optarg, "off"))
+ 					power_thresh = ULONG_MAX;
+ 				else {
+ 					power_thresh = strtoull(optarg, &endptr, 10);
+@@ -443,15 +443,14 @@ gboolean sock_handle(gint fd, GIOCondition condition, gpointer user_data __attri
+ 			goto out_close;
+ 		}
+ 
+-		if (!strncmp(buff, "stats", strlen("stats"))) {
++		if (g_str_has_prefix(buff, "stats")) {
+ 			char *stats = NULL;
+ 			for_each_object(numa_nodes, get_object_stat, &stats);
+ 			send(sock, stats, strlen(stats), 0);
+ 			free(stats);
+ 		}
+-		if (!strncmp(buff, "settings ", strlen("settings "))) {
+-			if (!(strncmp(buff + strlen("settings "), "sleep ",
+-							strlen("sleep ")))) {
++		if (g_str_has_prefix(buff, "settings ")) {
++			if (g_str_has_prefix(buff + strlen("settings "), "sleep ")) {
+ 				char *sleep_string = malloc(
+ 						sizeof(char) * (recv_size - strlen("settings sleep ") + 1));
+ 
+@@ -465,8 +464,7 @@ gboolean sock_handle(gint fd, GIOCondition condition, gpointer user_data __attri
+ 					sleep_interval = new_iterval;
+ 				}
+ 				free(sleep_string);
+-			} else if (!(strncmp(buff + strlen("settings "), "ban irqs ",
+-							strlen("ban irqs ")))) {
++			} else if (g_str_has_prefix(buff + strlen("settings "), "ban irqs ")) {
+ 				char *end;
+ 				char *irq_string = malloc(
+ 						sizeof(char) * (recv_size - strlen("settings ban irqs ") + 1));
+@@ -479,7 +477,7 @@ gboolean sock_handle(gint fd, GIOCondition condition, gpointer user_data __attri
+ 				g_list_free_full(cl_banned_irqs, free);
+ 				cl_banned_irqs = NULL;
+ 				need_rescan = 1;
+-				if (!strncmp(irq_string, "NONE", strlen("NONE"))) {
++				if (g_str_has_prefix(irq_string, "NONE")) {
+ 					free(irq_string);
+ 					goto out_close;
+ 				}
+@@ -488,8 +486,7 @@ gboolean sock_handle(gint fd, GIOCondition condition, gpointer user_data __attri
+ 					add_cl_banned_irq(irq);
+ 				} while((irq = strtoul(end, &end, 10)));
+ 				free(irq_string);
+-			} else if (!(strncmp(buff + strlen("settings "), "cpus ",
+-							strlen("cpus")))) {
++			} else if (g_str_has_prefix(buff + strlen("settings "), "cpus ")) {
+ 				banned_cpumask_from_ui = NULL;
+ 				free(cpu_ban_string);
+ 				cpu_ban_string = NULL;
+@@ -503,7 +500,7 @@ gboolean sock_handle(gint fd, GIOCondition condition, gpointer user_data __attri
+ 						recv_size - strlen("settings cpus "));
+ 				cpu_ban_string[recv_size - strlen("settings cpus ")] = '\0';
+ 				banned_cpumask_from_ui = strtok(cpu_ban_string, " ");
+-				if (banned_cpumask_from_ui && !strncmp(banned_cpumask_from_ui, "NULL", strlen("NULL"))) {
++				if (banned_cpumask_from_ui && g_str_has_prefix(banned_cpumask_from_ui, "NULL")) {
+ 					banned_cpumask_from_ui = NULL;
+ 					free(cpu_ban_string);
+ 					cpu_ban_string = NULL;
+@@ -511,7 +508,7 @@ gboolean sock_handle(gint fd, GIOCondition condition, gpointer user_data __attri
+ 				need_rescan = 1;
+ 			}
+ 		}
+-		if (!strncmp(buff, "setup", strlen("setup"))) {
++		if (g_str_has_prefix(buff, "setup")) {
+ 			char banned[512];
+ 			char *setup = calloc(strlen("SLEEP  ") + 11 + 1, 1);
+ 			char *newptr = NULL;
+diff --git a/numa.c b/numa.c
+index 5143ea0..a79f16e 100644
+--- a/numa.c
++++ b/numa.c
+@@ -84,7 +84,7 @@ void build_numa_node_list(void)
+ 		if (!entry)
+ 			break;
+ 		if ((entry->d_type == DT_DIR) &&
+-		    (strncmp(entry->d_name, "node", 4) == 0) &&
++		    g_str_has_prefix(entry->d_name, "node") &&
+ 		    isdigit(entry->d_name[4])) {
+ 			add_one_node(strtoul(&entry->d_name[4], NULL, 10));
+ 		}
+diff --git a/procinterrupts.c b/procinterrupts.c
+index e7ba653..4d04bf2 100644
+--- a/procinterrupts.c
++++ b/procinterrupts.c
+@@ -85,7 +85,7 @@ static int check_platform_device(char *name, struct irq_info *info)
+ 
+ 		log(TO_ALL, LOG_DEBUG, "Checking entry %s\n", ent->d_name);
+ 		for (i = 0; pdev_irq_info[i].d_name != NULL; i++) {
+-			if (!strncmp(ent->d_name, pdev_irq_info[i].d_name, strlen(pdev_irq_info[i].d_name))) {
++			if (g_str_has_prefix(ent->d_name, pdev_irq_info[i].d_name)) {
+ 				info->type = pdev_irq_info[i].type;
+ 				info->class = pdev_irq_info[i].class;
+ 				rc = 0;
+@@ -171,8 +171,8 @@ void init_irq_class_and_type(char *savedline, struct irq_info *info, int irq)
+ 		 * /proc/interrupts format defined, after of interrupt type
+ 		 * the reset string is mark the irq desc name.
+ 		 */
+-		if (strncmp(irq_name, "Level", strlen("Level")) == 0 ||
+-                                strncmp(irq_name, "Edge", strlen("Edge")) == 0)
++		if (!g_str_has_prefix(irq_name, "Level") ||
++                                !g_str_has_prefix(irq_name, "Edge"))
+                         break;
+ #endif
+ 	}
+diff --git a/ui/irqbalance-ui.c b/ui/irqbalance-ui.c
+index f4cc48f..4a6832a 100644
+--- a/ui/irqbalance-ui.c
++++ b/ui/irqbalance-ui.c
+@@ -157,21 +157,21 @@ void parse_setup(char *setup_data)
+ 	setup.banned_irqs = NULL;
+ 	setup.banned_cpus = NULL;
+ 	token = strtok_r(copy, " ", &ptr);
+-	if(strncmp(token, "SLEEP", strlen("SLEEP")) != 0) goto out;
++	if(!g_str_has_prefix(token, "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"))) {
++	while(g_str_has_prefix(token, "IRQ")) {
+ 		new_irq = g_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")) != 0) goto out;
++		if(!g_str_has_prefix(token, "LOAD")) goto out;
+ 		new_irq->load = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
+ 		token = strtok_r(NULL, " ", &ptr);
+-		if(strncmp(token, "DIFF", strlen("DIFF")) != 0) goto out;
++		if(!g_str_has_prefix(token, "DIFF")) goto out;
+ 		new_irq->diff = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
+ 		token = strtok_r(ptr, " ", &ptr);
+-		if(strncmp(token, "CLASS", strlen("CLASS")) != 0) goto out;
++		if(!g_str_has_prefix(token, "CLASS")) goto out;
+ 		new_irq->class = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
+ 		new_irq->is_banned = 1;
+ 		new_irq->assigned_to = NULL;
+@@ -180,7 +180,7 @@ void parse_setup(char *setup_data)
+ 		new_irq = NULL;
+ 	}
+ 
+-	if(strncmp(token, "BANNED", strlen("BANNED")) != 0) goto out;
++	if(!g_str_has_prefix(token, "BANNED")) goto out;
+ 	token = strtok_r(NULL, " ", &ptr);
+ 	for(i = strlen(token) - 1; i >= 0; i--) {
+ 		if (token[i] == ',')
+@@ -285,7 +285,7 @@ void parse_into_tree(char *data)
+ 	token = strtok_r(copy, " ", &ptr);
+ 	while(token != NULL) {
+ 		/* Parse node data */
+-		if(strncmp(token, "TYPE", strlen("TYPE")) != 0) {
++		if(!g_str_has_prefix(token, "TYPE")) {
+ 			g_free(copy);
+ 			goto out;
+ 		}
+@@ -297,28 +297,28 @@ void parse_into_tree(char *data)
+ 			parent = parent->parent;
+ 		}
+ 		token = strtok_r(NULL, " ", &ptr);
+-		if(strncmp(token, "NUMBER", strlen("NUMBER")) != 0) goto out;
++		if(!g_str_has_prefix(token, "NUMBER")) goto out;
+ 		new->number = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
+ 		token = strtok_r(NULL, " ", &ptr);
+-		if(strncmp(token, "LOAD", strlen("LOAD")) != 0) goto out;
++		if(!g_str_has_prefix(token, "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")) != 0) goto out;
++		if(!g_str_has_prefix(token, "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")))) {
++		while(token && g_str_has_prefix(token, "IRQ")) {
+ 			new_irq = g_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")) != 0) goto out;
++			if(!g_str_has_prefix(token, "LOAD")) goto out;
+ 			new_irq->load = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
+ 			token = strtok_r(NULL, " ", &ptr);
+-			if(strncmp(token, "DIFF", strlen("DIFF")) != 0) goto out;
++			if(!g_str_has_prefix(token, "DIFF")) goto out;
+ 			new_irq->diff = strtol(strtok_r(NULL, " ", &ptr), NULL, 10);
+ 			token = strtok_r(NULL, " ", &ptr);
+-			if(strncmp(token, "CLASS", strlen("CLASS")) != 0) goto out;
++			if(!g_str_has_prefix(token, "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);
+@@ -326,7 +326,7 @@ void parse_into_tree(char *data)
+ 			new_irq = NULL;
+ 		}
+ 
+-		if((token == NULL) || (strncmp(token, "IRQ", strlen("IRQ")) != 0)) {
++		if(!token || !g_str_has_prefix(token, "IRQ")) {
+ 			new->parent = parent;
+ 			if(parent == NULL) {
+ 				tree = g_list_append(tree, new);
+diff --git a/ui/ui.c b/ui/ui.c
+index 752744a..8325bcd 100644
+--- a/ui/ui.c
++++ b/ui/ui.c
+@@ -336,9 +336,9 @@ void handle_cpu_banning(void)
+ 			show_frame();
+ 			show_footer();
+ 			refresh();
+-			char settings_string[1024] = "settings cpus \0";
++			char settings_string[1024] = "settings cpus ";
+ 			for_each_cpu(all_cpus, get_new_cpu_ban_values, settings_string);
+-			if(!strcmp("settings cpus \0", settings_string)) {
++			if(g_str_has_prefix(settings_string, "settings cpus ")) {
+ 				strncpy(settings_string + strlen(settings_string),
+ 						"NULL", 1024 - strlen(settings_string));
+ 			}
+@@ -654,7 +654,7 @@ void handle_irq_banning(void)
+ 			refresh();
+ 			char settings_string[1024] = BAN_IRQS;
+ 			for_each_irq(all_irqs, get_new_irq_ban_values, settings_string);
+-			if(!strcmp(BAN_IRQS, settings_string)) {
++			if(g_str_has_prefix(settings_string, BAN_IRQS)) {
+ 				strncpy(settings_string + strlen(settings_string),
+ 						" NONE", 1024 - strlen(settings_string));
+ 			}
+-- 
+2.47.0
+
diff --git a/0038-define-IRQBALANCE_ARGS-as-empty-string-to-squelch-sy.patch b/0038-define-IRQBALANCE_ARGS-as-empty-string-to-squelch-sy.patch
new file mode 100644
index 0000000..c44be8d
--- /dev/null
+++ b/0038-define-IRQBALANCE_ARGS-as-empty-string-to-squelch-sy.patch
@@ -0,0 +1,23 @@
+From 42115bda75d9c49156a2799bc178ea105daf5003 Mon Sep 17 00:00:00 2001
+From: Cameron Baird <cameronbaird@microsoft.com>
+Date: Wed, 10 Jul 2024 23:09:32 +0000
+Subject: [PATCH 38/44] define IRQBALANCE_ARGS as empty string to squelch
+ systemd warning
+
+---
+ misc/irqbalance.env | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/misc/irqbalance.env b/misc/irqbalance.env
+index 96acb39..84cb843 100644
+--- a/misc/irqbalance.env
++++ b/misc/irqbalance.env
+@@ -41,4 +41,4 @@
+ #    Append any args here to the irqbalance daemon as documented in the man
+ #    page.
+ #
+-#IRQBALANCE_ARGS=
++IRQBALANCE_ARGS=""
+-- 
+2.47.0
+
diff --git a/0039-Minor-punctuation-fix.patch b/0039-Minor-punctuation-fix.patch
new file mode 100644
index 0000000..238808e
--- /dev/null
+++ b/0039-Minor-punctuation-fix.patch
@@ -0,0 +1,25 @@
+From 2860da404dea0fcfd404f56e546b90f8432176cf Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Torsten=20St=C3=B6ter?= <torsten.stoeter@lin-magdeburg.de>
+Date: Fri, 12 Jul 2024 14:24:01 +0200
+Subject: [PATCH 39/44] Minor punctuation fix.
+
+---
+ irqbalance.1 | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/irqbalance.1 b/irqbalance.1
+index 4c75362..a15fb63 100644
+--- a/irqbalance.1
++++ b/irqbalance.1
+@@ -51,7 +51,7 @@ Enables log output optimized for systemd-journal.
+ 
+ .TP
+ .B -p, --powerthresh=<threshold>
+-Set the threshold at which we attempt to move a CPU into powersave mode
++Set the threshold at which we attempt to move a CPU into powersave mode.
+ If more than <threshold> CPUs are more than 1 standard deviation below the
+ average CPU softirq workload, and no CPUs are more than 1 standard deviation
+ above (and have more than 1 IRQ assigned to them), attempt to place 1 CPU in
+-- 
+2.47.0
+
diff --git a/0040-Version-option-should-return-0-rather-than-1.patch b/0040-Version-option-should-return-0-rather-than-1.patch
new file mode 100644
index 0000000..7958a20
--- /dev/null
+++ b/0040-Version-option-should-return-0-rather-than-1.patch
@@ -0,0 +1,41 @@
+From 5d9eb1987ca8ae895bd2e2cf585954f50d3544ae Mon Sep 17 00:00:00 2001
+From: Tao Liu <ltao@redhat.com>
+Date: Tue, 13 Aug 2024 10:30:56 +1200
+Subject: [PATCH 40/44] Version option should return 0 rather than 1
+
+Previously invoke irqbalance with --version options, the return value
+is 1 instead of 0:
+
+$ irqbalance --version
+irqbalance version 1.9.4
+$ echo $?
+1
+
+It is unexpected because irqbalance have successfully returned the
+version string with no errors, so 0 should be returned instead of 1.
+This will confuse some automation tests.
+
+This patch will make irqbalance return the correct value for version
+option.
+
+Signed-off-by: Tao Liu <ltao@redhat.com>
+---
+ irqbalance.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/irqbalance.c b/irqbalance.c
+index 491a912..6422a7b 100644
+--- a/irqbalance.c
++++ b/irqbalance.c
+@@ -122,7 +122,7 @@ static void parse_command_line(int argc, char **argv)
+ 				break;
+ 			case 'V':
+ 				version();
+-				exit(1);
++				exit(0);
+ 				break;
+ 			case 'c':
+ 				deepest_cache = strtoul(optarg, &endptr, 10);
+-- 
+2.47.0
+
diff --git a/0041-Add-CAP_SETPCAP-to-CapabilityBoundingSet-in-irqbalan.patch b/0041-Add-CAP_SETPCAP-to-CapabilityBoundingSet-in-irqbalan.patch
new file mode 100644
index 0000000..0d32619
--- /dev/null
+++ b/0041-Add-CAP_SETPCAP-to-CapabilityBoundingSet-in-irqbalan.patch
@@ -0,0 +1,46 @@
+From 196385b63d1e4ac1431f39f7b02978c969f4ff21 Mon Sep 17 00:00:00 2001
+From: Tao Liu <ltao@redhat.com>
+Date: Tue, 20 Aug 2024 16:46:38 +1200
+Subject: [PATCH 41/44] Add CAP_SETPCAP to CapabilityBoundingSet in
+ irqbalance.service
+
+A error message of 'libcap-ng used by "/usr/sbin/irqbalance" failed dropping
+bounding set due to not having CAP_SETPCAP in capng_apply' is noticed.
+
+Previously a similar issue[1] has been fixed by the following commit:
+
+    efab272 Drop CapabilityBoundingSet from irqbalance service
+    43751df drop NoNewPrivs from irqbalance service
+
+in which, CapabilityBoundingSet and NoNewPrivs parameters are dropped
+from the irqbalance.service, and get restored by the following commit
+later:
+
+    a99b604 Set additional systemd options for service
+
+So this patch will not do the dropping again, but add CAP_SETPCAP to
+CapabilityBoundingSet instead.
+
+[1]: https://github.com/Irqbalance/irqbalance/issues/182
+
+Signed-off-by: Tao Liu <ltao@redhat.com>
+---
+ misc/irqbalance.service | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/misc/irqbalance.service b/misc/irqbalance.service
+index b731cc6..7e0c5d8 100644
+--- a/misc/irqbalance.service
++++ b/misc/irqbalance.service
+@@ -9,7 +9,7 @@ ConditionCPUs=>1
+ EnvironmentFile=-/usr/lib/irqbalance/defaults.env
+ EnvironmentFile=-/path/to/irqbalance.env
+ ExecStart=/usr/sbin/irqbalance $IRQBALANCE_ARGS
+-CapabilityBoundingSet=
++CapabilityBoundingSet=CAP_SETPCAP
+ NoNewPrivileges=yes
+ ProtectSystem=strict
+ ReadOnlyPaths=/
+-- 
+2.47.0
+
diff --git a/0042-Check-info-moved-before-updating.patch b/0042-Check-info-moved-before-updating.patch
new file mode 100644
index 0000000..7b63d6b
--- /dev/null
+++ b/0042-Check-info-moved-before-updating.patch
@@ -0,0 +1,31 @@
+From 25fa38022bc349cd2d3fdb41fcdad6e7193a73a7 Mon Sep 17 00:00:00 2001
+From: lvgenggeng <lvgenggeng@uniontech.com>
+Date: Tue, 3 Sep 2024 23:31:49 +0800
+Subject: [PATCH 42/44] Check info->moved before updating
+
+In migrate_irq(), the list will not be changed if info not found.
+
+Signed-off-by: lvgenggeng <lvgenggeng@uniontech.com>
+---
+ irqlist.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/irqlist.c b/irqlist.c
+index 9483a11..5ad2faf 100644
+--- a/irqlist.c
++++ b/irqlist.c
+@@ -210,6 +210,11 @@ void migrate_irq_obj(struct topo_obj *from, struct topo_obj *to, struct irq_info
+ 	to_list = to ? &to->interrupts : &rebalance_irq_list;
+ 
+ 	migrate_irq(from_list, to_list, info);
++	/*
++	 * only update list after info found
++	 */
++	if (!info->moved)
++		return;
+ 
+ 	if (from) {
+ 		if (from->slots_left != INT_MAX)
+-- 
+2.47.0
+
diff --git a/0043-irqbalance.1-a-b-a-b-it-s-type-its-type.patch b/0043-irqbalance.1-a-b-a-b-it-s-type-its-type.patch
new file mode 100644
index 0000000..a37da64
--- /dev/null
+++ b/0043-irqbalance.1-a-b-a-b-it-s-type-its-type.patch
@@ -0,0 +1,38 @@
+From 1489ae342e07f2537e60456d2c639f90fd2ec87f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= <nabijaczleweli@nabijaczleweli.xyz>
+Date: Wed, 2 Oct 2024 15:21:08 +0200
+Subject: [PATCH 43/44] irqbalance.1: a , b => a, b; it's type => its type
+
+---
+ irqbalance.1 | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/irqbalance.1 b/irqbalance.1
+index a15fb63..05f7b48 100644
+--- a/irqbalance.1
++++ b/irqbalance.1
+@@ -193,13 +193,13 @@ Forces a rescan of the available IRQs and system topology.
+ irqbalance is able to communicate via socket and return it's current assignment
+ tree and setup, as well as set new settings based on sent values. Socket is abstract,
+ with a name in form of
+-.B irqbalance<PID>.sock
+-, where <PID> is the process ID of irqbalance instance to communicate with.
++.BR irqbalance<PID>.sock ,
++where <PID> is the process ID of irqbalance instance to communicate with.
+ Possible values to send:
+ .TP
+ .B stats
+ Retrieve assignment tree of IRQs to CPUs, in recursive manner. For each CPU node
+-in tree, it's type, number, load and whether the save mode is active are sent. For
++in tree, its type, number, load and whether the save mode is active are sent. For
+ each assigned IRQ type, it's number, load, number of IRQs since last rebalancing
+ and it's class are sent. Refer to types.h file for explanation of defines.
+ .TP
+@@ -220,4 +220,3 @@ Based on chosen tools, ancillary message with credentials needs to be sent with
+ 
+ .SH "HOMEPAGE"
+ https://github.com/Irqbalance/irqbalance
+-
+-- 
+2.47.0
+
diff --git a/0044-Use-EPERM-instead-of-EIO-when-try-setting-irq-affini.patch b/0044-Use-EPERM-instead-of-EIO-when-try-setting-irq-affini.patch
new file mode 100644
index 0000000..0afbe7f
--- /dev/null
+++ b/0044-Use-EPERM-instead-of-EIO-when-try-setting-irq-affini.patch
@@ -0,0 +1,39 @@
+From 3685d33ea8b3fecb832efc646ac1d4ed3945ed71 Mon Sep 17 00:00:00 2001
+From: Tao Liu <ltao@redhat.com>
+Date: Fri, 18 Oct 2024 11:25:44 +1300
+Subject: [PATCH 44/44] Use EPERM instead of EIO when try setting irq affinity
+ fails
+
+Kernel commit eb29369fa543e ("genirq/proc: Change the return value for
+set affinity permission error") changed the error number from EIO to
+EPERM when a specific irq failed to set affinity from userspace.
+
+In addition, users have complained about EIO is misleading when
+setting affinity fails, however it just meaning "user cannot set
+affinity", so EPERM is better for that.
+
+This patch will follow the kernel change, to unify the behaviour of
+irqbalance from the kernels which have not integrated the kernel
+commit mentioned above.
+
+Signed-off-by: Tao Liu <ltao@redhat.com>
+---
+ activate.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/activate.c b/activate.c
+index 10ad57d..59a2fce 100644
+--- a/activate.c
++++ b/activate.c
+@@ -95,6 +95,8 @@ static void activate_mapping(struct irq_info *info, void *data __attribute__((un
+ 	info->moved = 0; /*migration is done*/
+ 	return;
+ error:
++	/* Use EPERM as the explaination for EIO */
++	errsave = (errsave == EIO) ? EPERM : errsave;
+ 	log(TO_ALL, LOG_WARNING,
+ 		"Cannot change IRQ %i affinity: %s\n",
+ 		info->irq, strerror(errsave));
+-- 
+2.47.0
+
diff --git a/irqbalance-1.9.0-environment-file-sysconfig.patch b/irqbalance-1.9.0-environment-file-sysconfig.patch
index db997bc..0850e77 100644
--- a/irqbalance-1.9.0-environment-file-sysconfig.patch
+++ b/irqbalance-1.9.0-environment-file-sysconfig.patch
@@ -8,7 +8,7 @@ Subject: [PATCH] misc/irqbalance.service: Use sysconfig for EnvironmentFile
  1 file changed, 1 insertion(+), 2 deletions(-)
 
 diff --git a/misc/irqbalance.service b/misc/irqbalance.service
-index 0f79c3e..9bc63b6 100644
+index 7e0c5d8..9ac430f 100644
 --- a/misc/irqbalance.service
 +++ b/misc/irqbalance.service
 @@ -6,8 +6,7 @@ ConditionVirtualization=!container
@@ -19,7 +19,5 @@ index 0f79c3e..9bc63b6 100644
 -EnvironmentFile=-/path/to/irqbalance.env
 +EnvironmentFile=-/etc/sysconfig/irqbalance
  ExecStart=/usr/sbin/irqbalance $IRQBALANCE_ARGS
- CapabilityBoundingSet=
+ CapabilityBoundingSet=CAP_SETPCAP
  NoNewPrivileges=yes
--- 
-2.37.1
diff --git a/irqbalance.spec b/irqbalance.spec
index 4feb051..c367135 100644
--- a/irqbalance.spec
+++ b/irqbalance.spec
@@ -6,10 +6,53 @@ Summary:        IRQ balancing daemon
 License:        GPL-2.0-only
 URL:            https://github.com/Irqbalance/irqbalance
 Source0:        %{url}/archive/v%{version}/irqbalance-%{version}.tar.gz
-Patch1:         irqbalance-1.9.0-environment-file-sysconfig.patch
-Patch2: 	0001-irqbalance-ui-check-if-using-a-negative-index-of-buf.patch
-Patch3: 	0002-Check-fflush-return-value.patch
-Patch4: 	0003-Drop-ProtectKernelTunables.patch
+
+Patch2: 0001-irqbalance-ui-check-if-using-a-negative-index-of-buf.patch
+Patch3: 0002-Check-fflush-return-value.patch
+Patch4: 0003-fix-32-bit-formats.patch
+Patch5: 0004-add-void-to-fix-strict-prototypes.patch
+Patch6: 0005-cast-void-pointer-to-actual-type.patch
+Patch7: 0006-meson-move-build-files-to-repository-root.patch
+Patch8: 0007-meson-bump-project-version-to-1.9.4.patch
+Patch9: 0008-meson-drop-redundant-install_man-options.patch
+Patch10: 0009-meson-add-a-minimum-version-decorator.patch
+Patch11: 0010-Drop-ProtectKernelTunables.patch
+Patch12: 0011-meson-replace-generic-array-with-files.patch
+Patch13: 0012-meson-use-find_library-for-numa.patch
+Patch14: 0013-ui-change-void-to-char.patch
+Patch15: 0014-clang-tidy-don-t-assign-in-if.patch
+Patch16: 0015-clang-tidy-properly-use-strncmp.patch
+Patch17: 0016-replace-malloc-with-g_malloc0.patch
+Patch18: 0017-clang-tidy-don-t-use-else-after-return.patch
+Patch19: 0018-clang-tidy-remove-return-in-void-functions.patch
+Patch20: 0019-clang-tidy-remove-redundant-declarations.patch
+Patch21: 0020-clang-tidy-remove-duplicate-include.patch
+Patch22: 0021-CI-add-meson-CI.patch
+Patch23: 0022-Wrap-migrate_irq-in-a-higher-level-utility-function.patch
+Patch24: 0023-Track-IRQ-slots-count-per-CPU-to-avoid-overflowing.patch
+Patch25: 0024-Disable-linking-to-curses-if-without-irqbalance-ui-i.patch
+Patch26: 0025-Remove-extraneous-space-causing-with-systemd-not-be-.patch
+Patch27: 0026-direct-initialize-msghdr-members.patch
+Patch28: 0027-direct-initialize-iovec.patch
+Patch29: 0028-clang-tidy-add-missing-free.patch
+Patch30: 0029-clang-tidy-don-t-assign-in-if.patch
+Patch31: 0030-clang-tidy-remove-pointless-casts.patch
+Patch32: 0031-use-g_malloc-and-friends.patch
+Patch33: 0032-remove-malloc-from-ucred.patch
+Patch34: 0033-gcc-analyzer-add-NULL-checks.patch
+Patch35: 0034-gcc-analyzer-increase-socket_name-size.patch
+Patch36: 0035-use-g_strdup_printf.patch
+Patch37: 0036-avoid-malloc-with-create_credentials_msg.patch
+Patch38: 0037-conver-strncmp-to-g_str_has_prefix.patch
+Patch39: 0038-define-IRQBALANCE_ARGS-as-empty-string-to-squelch-sy.patch
+Patch40: 0039-Minor-punctuation-fix.patch
+Patch41: 0040-Version-option-should-return-0-rather-than-1.patch
+Patch42: 0041-Add-CAP_SETPCAP-to-CapabilityBoundingSet-in-irqbalan.patch
+Patch43: 0042-Check-info-moved-before-updating.patch
+Patch44: 0043-irqbalance.1-a-b-a-b-it-s-type-its-type.patch
+Patch45: 0044-Use-EPERM-instead-of-EIO-when-try-setting-irq-affini.patch
+
+Patch46: irqbalance-1.9.0-environment-file-sysconfig.patch
 
 BuildRequires:  autoconf automake libtool libcap-ng
 BuildRequires:  glib2-devel pkgconf libcap-ng-devel