From 948da5eabcbf8d74270e661d0730ba1c203913f5 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 18 Aug 2012 00:02:28 +0000 Subject: Sat Aug 18 00:02:28 UTC 2012 --- testing/irqbalance/PKGBUILD | 40 + testing/irqbalance/irqbalance-2011-08-09.patch | 1584 ++++++++++++++++++++++++ testing/irqbalance/irqbalance.conf.d | 6 + testing/irqbalance/irqbalance.rc.d | 46 + testing/irqbalance/irqbalance.service | 8 + 5 files changed, 1684 insertions(+) create mode 100644 testing/irqbalance/PKGBUILD create mode 100644 testing/irqbalance/irqbalance-2011-08-09.patch create mode 100644 testing/irqbalance/irqbalance.conf.d create mode 100644 testing/irqbalance/irqbalance.rc.d create mode 100644 testing/irqbalance/irqbalance.service (limited to 'testing/irqbalance') diff --git a/testing/irqbalance/PKGBUILD b/testing/irqbalance/PKGBUILD new file mode 100644 index 000000000..52c5ae477 --- /dev/null +++ b/testing/irqbalance/PKGBUILD @@ -0,0 +1,40 @@ +# Maintainer: Dan McGee +# Contributor: Martin Striz + +pkgname=irqbalance +_realver=1.0.3 +pkgver=1.0.3.20110809 +pkgrel=1 +pkgdesc="IRQ balancing daemon for SMP systems" +arch=('i686' 'x86_64') +url="http://code.google.com/p/irqbalance" +license=('GPL') +depends=(glib2 numactl libcap-ng) +makedepends=(pkgconfig) +backup=(etc/conf.d/irqbalance) +source=(http://irqbalance.googlecode.com/files/irqbalance-$_realver.tar.gz + irqbalance-2011-08-09.patch + irqbalance.conf.d + irqbalance.rc.d + irqbalance.service) +md5sums=('6f246481d6295bcb9a79751c03207c96' + '49a5669fc3eb452a5d24abec887f0a6a' + '336c1ee99818f9ecda1687e34c69fd6b' + 'fb82fc5d267d39110baf720d81282a7c' + '9e82dc471128117982a8dd0c4bd5f246') + +build() { + cd "$srcdir/$pkgname-$_realver" + patch -Np1 < ../irqbalance-2011-08-09.patch + autoreconf -fi + ./configure --prefix=/usr + make +} + +package() { + cd "$srcdir/$pkgname-$_realver" + make install DESTDIR="$pkgdir" + install -D -m644 ../irqbalance.conf.d "$pkgdir"/etc/conf.d/irqbalance + install -D -m755 ../irqbalance.rc.d "$pkgdir"/etc/rc.d/irqbalance + install -D -m644 ../irqbalance.service "$pkgdir"/usr/lib/systemd/system/irqbalance.service +} diff --git a/testing/irqbalance/irqbalance-2011-08-09.patch b/testing/irqbalance/irqbalance-2011-08-09.patch new file mode 100644 index 000000000..fcdb8bdbf --- /dev/null +++ b/testing/irqbalance/irqbalance-2011-08-09.patch @@ -0,0 +1,1584 @@ +diff --git a/ChangeLog b/ChangeLog +deleted file mode 100644 +index f5e9428..0000000 +--- a/ChangeLog ++++ /dev/null +@@ -1,3 +0,0 @@ +-This is all tracked in the SVN repo. This file is just here to keep the +-autotools from complaining +- +diff --git a/Makefile.am b/Makefile.am +index 9847232..188e34f 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -22,17 +22,17 @@ + + AUTOMAKE_OPTIONS = no-dependencies + ACLOCAL_AMFLAGS = -I m4 +-EXTRA_DIST = README INSTALL COPYING autogen.sh m4/cap-ng.m4 misc/irqbalance.service +- ++EXTRA_DIST = INSTALL COPYING autogen.sh misc/irqbalance.service ++ + INCLUDES = -I${top_srcdir} +-LIBS = $(CAPNG_LDADD) $(GLIB_LIBS) @LIBS@ +-AM_CFLAGS = $(GLIB_CFLAGS) ++AM_CFLAGS = $(LIBCAP_NG_CFLAGS) $(GLIB_CFLAGS) + AM_CPPFLAGS = -W -Wall -Wshadow -Wformat -Wundef -D_GNU_SOURCE + noinst_HEADERS = bitmap.h constants.h cpumask.h irqbalance.h non-atomic.h \ + types.h + sbin_PROGRAMS = irqbalance + irqbalance_SOURCES = activate.c bitmap.c classify.c cputree.c irqbalance.c \ +- irqlist.c numa.c placement.c powermode.c procinterrupts.c ++ irqlist.c numa.c placement.c procinterrupts.c ++irqbalance_LDADD = $(LIBCAP_NG_LIBS) $(GLIB_LIBS) + dist_man_MANS = irqbalance.1 + + CONFIG_CLEAN_FILES = debug*.list config/* +@@ -40,3 +40,6 @@ clean-generic: + rm -rf autom4te*.cache + rm -f *.rej *.orig *~ + ++if LOCAL_GLIB ++SUBDIRS = glib-local ++endif +diff --git a/NEWS b/NEWS +deleted file mode 100644 +index 7cc0277..0000000 +--- a/NEWS ++++ /dev/null +@@ -1 +0,0 @@ +-No news currently +diff --git a/README b/README +deleted file mode 100644 +index e69de29..0000000 +diff --git a/activate.c b/activate.c +index 292c44a..02fc8dc 100644 +--- a/activate.c ++++ b/activate.c +@@ -1,5 +1,6 @@ + /* + * Copyright (C) 2006, Intel Corporation ++ * Copyright (C) 2012, Neil Horman + * + * This file is part of irqbalance + * +@@ -31,34 +32,63 @@ + + #include "irqbalance.h" + ++static int check_affinity(struct irq_info *info, cpumask_t applied_mask) ++{ ++ cpumask_t current_mask; ++ char buf[PATH_MAX]; ++ char *line = NULL; ++ size_t size = 0; ++ FILE *file; ++ ++ sprintf(buf, "/proc/irq/%i/smp_affinity", info->irq); ++ file = fopen(buf, "r"); ++ if (!file) ++ return 1; ++ if (getline(&line, &size, file)==0) { ++ free(line); ++ fclose(file); ++ return 1; ++ } ++ cpumask_parse_user(line, strlen(line), current_mask); ++ fclose(file); ++ free(line); ++ ++ return cpus_equal(applied_mask, current_mask); ++} + + static void activate_mapping(struct irq_info *info, void *data __attribute__((unused))) + { + char buf[PATH_MAX]; + FILE *file; + cpumask_t applied_mask; ++ int valid_mask = 0; ++ ++ if ((hint_policy == HINT_POLICY_EXACT) && ++ (!cpus_empty(info->affinity_hint))) { ++ applied_mask = info->affinity_hint; ++ valid_mask = 1; ++ } else if (info->assigned_obj) { ++ applied_mask = info->assigned_obj->mask; ++ valid_mask = 1; ++ if ((hint_policy == HINT_POLICY_SUBSET) && ++ (!cpus_empty(info->affinity_hint))) ++ cpus_and(applied_mask, applied_mask, info->affinity_hint); ++ } + + /* + * only activate mappings for irqs that have moved + */ +- if (!info->moved) ++ if (!info->moved && (!valid_mask || check_affinity(info, applied_mask))) + return; + + if (!info->assigned_obj) + return; + +- + sprintf(buf, "/proc/irq/%i/smp_affinity", info->irq); + file = fopen(buf, "w"); + if (!file) + return; + +- if ((hint_policy == HINT_POLICY_EXACT) && +- (!cpus_empty(info->affinity_hint))) +- applied_mask = info->affinity_hint; +- else +- applied_mask = info->assigned_obj->mask; +- + cpumask_scnprintf(buf, PATH_MAX, applied_mask); + fprintf(file, "%s", buf); + fclose(file); +diff --git a/autogen.sh b/autogen.sh +index 5ad9f14..b792e8b 100755 +--- a/autogen.sh ++++ b/autogen.sh +@@ -1,4 +1,5 @@ + #! /bin/sh + set -x -e ++mkdir -p m4 + # --no-recursive is available only in recent autoconf versions + autoreconf -fv --install +diff --git a/classify.c b/classify.c +index 124dab0..05b3bfb 100644 +--- a/classify.c ++++ b/classify.c +@@ -52,6 +52,8 @@ static short class_codes[MAX_CLASS] = { + }; + + static GList *interrupts_db; ++static GList *new_irq_list; ++static GList *banned_irqs; + + #define SYSDEV_DIR "/sys/bus/pci/devices" + +@@ -63,6 +65,30 @@ static gint compare_ints(gconstpointer a, gconstpointer b) + return ai->irq - bi->irq; + } + ++void add_banned_irq(int irq) ++{ ++ struct irq_info find, *new; ++ GList *entry; ++ ++ find.irq = irq; ++ entry = g_list_find_custom(banned_irqs, &find, compare_ints); ++ if (entry) ++ return; ++ ++ new = calloc(sizeof(struct irq_info), 1); ++ if (!new) { ++ if (debug_mode) ++ printf("No memory to ban irq %d\n", irq); ++ return; ++ } ++ ++ new->irq = irq; ++ ++ banned_irqs = g_list_append(banned_irqs, new); ++ return; ++} ++ ++ + /* + * Inserts an irq_info struct into the intterupts_db list + * devpath points to the device directory in sysfs for the +@@ -90,6 +116,13 @@ static struct irq_info *add_one_irq_to_db(const char *devpath, int irq) + return NULL; + } + ++ entry = g_list_find_custom(banned_irqs, &find, compare_ints); ++ if (entry) { ++ if (debug_mode) ++ printf("SKIPPING BANNED IRQ %d\n", irq); ++ return NULL; ++ } ++ + new = calloc(sizeof(struct irq_info), 1); + if (!new) + return NULL; +@@ -175,6 +208,43 @@ out: + return new; + } + ++static int check_for_irq_ban(char *path, int irq) ++{ ++ char *cmd; ++ int rc; ++ ++ if (!banscript) ++ return 0; ++ ++ cmd = alloca(strlen(path)+strlen(banscript)+32); ++ if (!cmd) ++ return 0; ++ ++ sprintf(cmd, "%s %s %d",banscript, path, irq); ++ rc = system(cmd); ++ ++ /* ++ * The system command itself failed ++ */ ++ if (rc == -1) { ++ if (debug_mode) ++ printf("%s failed, please check the --banscript option\n", cmd); ++ else ++ syslog(LOG_INFO, "%s failed, please check the --banscript option\n", cmd); ++ return 0; ++ } ++ ++ if (WEXITSTATUS(rc)) { ++ if (debug_mode) ++ printf("irq %d is baned by %s\n", irq, banscript); ++ else ++ syslog(LOG_INFO, "irq %d is baned by %s\n", irq, banscript); ++ return 1; ++ } ++ return 0; ++ ++} ++ + /* + * Figures out which interrupt(s) relate to the device we're looking at in dirname + */ +@@ -199,6 +269,10 @@ static void build_one_dev_entry(const char *dirname) + irqnum = strtol(entry->d_name, NULL, 10); + if (irqnum) { + sprintf(path, "%s/%s", SYSDEV_DIR, dirname); ++ if (check_for_irq_ban(path, irqnum)) { ++ add_banned_irq(irqnum); ++ continue; ++ } + new = add_one_irq_to_db(path, irqnum); + if (!new) + continue; +@@ -221,6 +295,11 @@ static void build_one_dev_entry(const char *dirname) + */ + if (irqnum) { + sprintf(path, "%s/%s", SYSDEV_DIR, dirname); ++ if (check_for_irq_ban(path, irqnum)) { ++ add_banned_irq(irqnum); ++ goto done; ++ } ++ + new = add_one_irq_to_db(path, irqnum); + if (!new) + goto done; +@@ -248,6 +327,8 @@ void rebuild_irq_db(void) + { + DIR *devdir = opendir(SYSDEV_DIR); + struct dirent *entry; ++ GList *gentry; ++ struct irq_info *ninfo, *iinfo; + + free_irq_db(); + +@@ -263,22 +344,46 @@ void rebuild_irq_db(void) + build_one_dev_entry(entry->d_name); + + } while (entry != NULL); ++ + closedir(devdir); ++ ++ if (!new_irq_list) ++ return; ++ gentry = g_list_first(new_irq_list); ++ while(gentry) { ++ ninfo = gentry->data; ++ iinfo = get_irq_info(ninfo->irq); ++ new_irq_list = g_list_remove(gentry, ninfo); ++ if (!iinfo) { ++ if (debug_mode) ++ printf("Adding untracked IRQ %d to database\n", ninfo->irq); ++ interrupts_db = g_list_append(interrupts_db, ninfo); ++ } else ++ free(ninfo); ++ ++ gentry = g_list_first(new_irq_list); ++ } ++ g_list_free(new_irq_list); ++ new_irq_list = NULL; ++ + } + +-struct irq_info *add_misc_irq(int irq) ++struct irq_info *add_new_irq(int irq) + { +- struct irq_info *new; ++ struct irq_info *new, *nnew; + + new = calloc(sizeof(struct irq_info), 1); +- if (!new) ++ nnew = calloc(sizeof(struct irq_info), 1); ++ if (!new || !nnew) + return NULL; + + new->irq = irq; + new->type = IRQ_TYPE_LEGACY; + new->class = IRQ_OTHER; + new->numa_node = get_numa_node(-1); ++ memcpy(nnew, new, sizeof(struct irq_info)); + interrupts_db = g_list_append(interrupts_db, new); ++ new_irq_list = g_list_append(new_irq_list, nnew); + return new; + } + +@@ -307,7 +412,7 @@ struct irq_info *get_irq_info(int irq) + void migrate_irq(GList **from, GList **to, struct irq_info *info) + { + GList *entry; +- struct irq_info find, *tmp;; ++ struct irq_info find, *tmp; + + find.irq = info->irq; + entry = g_list_find_custom(*from, &find, compare_ints); +@@ -325,18 +430,9 @@ static gint sort_irqs(gconstpointer A, gconstpointer B) + a = (struct irq_info*)A; + b = (struct irq_info*)B; + +- if (a->class < b->class) +- return 1; +- if (a->class > b->class) +- return -1; +- if (a->load < b->load) +- return 1; +- if (a->load > b->load) +- return -1; +- if (aclass < b->class || a->load < b->load || a < b) + return 1; + return -1; +- + } + + void sort_irq_list(GList **list) +diff --git a/configure.ac b/configure.ac +index eed55ba..1230d66 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1,49 +1,12 @@ +-dnl +-define([AC_INIT_NOTICE], +-[### Generated automatically using autoconf version] AC_ACVERSION [ +-### Copyright 2009 Steve Grubb +-### +-### Permission is hereby granted, free of charge, to any person obtaining a +-### copy of this software and associated documentation files (the "Software"), +-### to deal in the Software without restriction, including without limitation +-### the rights to use, copy, modify, merge, publish, distribute, sublicense, +-### and/or sell copies of the Software, and to permit persons to whom the +-### Software is furnished to do so, subject to the following conditions: +-### +-### The above copyright notice and this permission notice shall be included +-### in all copies or substantial portions of the Software. +-### +-### THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-### IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-### FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +-### THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +-### OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +-### ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +-### OTHER DEALINGS IN THE SOFTWARE. +-### +-### For usage, run `./configure --help' +-### For more detailed information on installation, read the file `INSTALL'. +-### +-### If configuration succeeds, status is in the file `config.status'. +-### A log of configuration tests is in `config.log'. +-]) +- +-AC_REVISION($Revision: 1.3 $)dnl + AC_INIT(irqbalance,1.0.3) + AC_PREREQ(2.12)dnl + AM_CONFIG_HEADER(config.h) + +-echo Configuring irqbalance $VERSION +- + AC_CONFIG_MACRO_DIR([m4]) +-AC_CANONICAL_TARGET +-AM_INIT_AUTOMAKE ++AM_INIT_AUTOMAKE([foreign]) + AM_PROG_LIBTOOL + AC_SUBST(LIBTOOL_DEPS) + +-AC_MSG_NOTICE() +-AC_MSG_NOTICE([Checking for programs]) +- + AC_PROG_CC + AC_PROG_INSTALL + AC_PROG_AWK +@@ -55,9 +18,6 @@ AS_IF([test "$enable_numa" = "no"],[ + ac_cv_lib_numa_numa_available=no + ]) + +-AC_MSG_NOTICE +-AC_MSG_NOTICE([echo Checking for header files]) +- + AC_HEADER_STDC + AC_CHECK_HEADERS([numa.h]) + +@@ -70,10 +30,57 @@ AC_C_CONST + AC_C_INLINE + AM_PROG_CC_C_O + +-PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.28]) +-LIBCAP_NG_PATH ++AC_ARG_WITH([glib2], ++ [AS_HELP_STRING([--without-glib2], ++ [Don't use system glib2 library. Use local implementation instead.])], ++ [], ++ [with_glib2=check]) ++ ++local_glib2= ++AS_IF( ++ [test "x$with_glib2" = xyes], ++ [PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.28])], ++ ++ [test "x$with_glib2" = xno], ++ [local_glib2="yes"], ++ ++ [PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.28], [], [local_glib2="yes"])] ++) ++ ++AS_IF( ++ [test "x$local_glib2" = xyes], ++ [ ++ GLIB_CFLAGS=-I./glib-local ++ GLIB_LIBS=glib-local/libglib.a ++ AC_SUBST(GLIB_CFLAGS) ++ AC_SUBST(GLIB_LIBS) ++ AC_MSG_WARN(Using locale implementation of GList functions) ++ ] ++) ++ ++AM_CONDITIONAL([LOCAL_GLIB], [test "x$local_glib2" = "xyes"]) ++ ++AC_ARG_WITH([libcap-ng], ++ AS_HELP_STRING([libcap-ng], [Add libcap-ng-support @<:@default=auto@:>@])) ++ ++AS_IF( ++ [test "x$libcap_ng" != "xno"], ++ [ ++ PKG_CHECK_MODULES([LIBCAP_NG], [libcap-ng], ++ [AC_DEFINE(HAVE_LIBCAP_NG,1,[libcap-ng support])], ++ [ ++ AS_IF( ++ [test "x$libcap_ng" = "xyes"], ++ [ ++ AC_MSG_ERROR([libcap-ng not found]) ++ ] ++ ) ++ ] ++ ) ++ ] ++) + +-AC_OUTPUT(Makefile) ++AC_OUTPUT(Makefile glib-local/Makefile) + + AC_MSG_NOTICE() + AC_MSG_NOTICE([irqbalance Version: $VERSION]) +diff --git a/cputree.c b/cputree.c +index af4fd3a..9568967 100644 +--- a/cputree.c ++++ b/cputree.c +@@ -1,5 +1,6 @@ + /* + * Copyright (C) 2006, Intel Corporation ++ * Copyright (C) 2012, Neil Horman + * + * This file is part of irqbalance + * +diff --git a/glib-local/Makefile.am b/glib-local/Makefile.am +new file mode 100644 +index 0000000..336b56e +--- /dev/null ++++ b/glib-local/Makefile.am +@@ -0,0 +1,8 @@ ++## Process this file with automake to produce Makefile.in ++noinst_LIBRARIES = libglib.a ++ ++libglib_a_SOURCES = glist.c ++ ++libglib_a_CFLAGS = @GLIB_CFLAGS@ ++ ++noinst_HEADERS = glib.h glist.h +diff --git a/glib-local/glib.h b/glib-local/glib.h +new file mode 100644 +index 0000000..5874892 +--- /dev/null ++++ b/glib-local/glib.h +@@ -0,0 +1 @@ ++#include +diff --git a/glib-local/glist.c b/glib-local/glist.c +new file mode 100644 +index 0000000..6fa1761 +--- /dev/null ++++ b/glib-local/glist.c +@@ -0,0 +1,381 @@ ++#include ++ ++#include "glist.h" ++ ++/** ++ * g_list_free: ++ * @list: a #GList ++ * ++ * Frees all of the memory used by a #GList. ++ * The freed elements are returned to the slice allocator. ++ * ++ * ++ * If list elements contain dynamically-allocated memory, ++ * you should either use g_list_free_full() or free them manually ++ * first. ++ * ++ */ ++void ++g_list_free (GList *list) ++{ ++ GList *l = list; ++ ++ while(l) { ++ GList *tmp = l->next; ++ free(l); ++ l = tmp; ++ } ++} ++ ++/** ++ * g_list_last: ++ * @list: a #GList ++ * ++ * Gets the last element in a #GList. ++ * ++ * Returns: the last element in the #GList, ++ * or %NULL if the #GList has no elements ++ */ ++GList* ++g_list_last (GList *list) ++{ ++ if (list) ++ { ++ while (list->next) ++ list = list->next; ++ } ++ ++ return list; ++} ++ ++/** ++ * g_list_append: ++ * @list: a pointer to a #GList ++ * @data: the data for the new element ++ * ++ * Adds a new element on to the end of the list. ++ * ++ * ++ * The return value is the new start of the list, which ++ * may have changed, so make sure you store the new value. ++ * ++ * ++ * ++ * Note that g_list_append() has to traverse the entire list ++ * to find the end, which is inefficient when adding multiple ++ * elements. A common idiom to avoid the inefficiency is to prepend ++ * the elements and reverse the list when all elements have been added. ++ * ++ * ++ * |[ ++ * /* Notice that these are initialized to the empty list. */ ++ * GList *list = NULL, *number_list = NULL; ++ * ++ * /* This is a list of strings. */ ++ * list = g_list_append (list, "first"); ++ * list = g_list_append (list, "second"); ++ * ++ * /* This is a list of integers. */ ++ * number_list = g_list_append (number_list, GINT_TO_POINTER (27)); ++ * number_list = g_list_append (number_list, GINT_TO_POINTER (14)); ++ * ]| ++ * ++ * Returns: the new start of the #GList ++ */ ++GList* ++g_list_append (GList *list, ++ gpointer data) ++{ ++ GList *new_list; ++ GList *last; ++ ++ new_list = malloc(sizeof(*new_list)); ++ new_list->data = data; ++ new_list->next = NULL; ++ ++ if (list) ++ { ++ last = g_list_last (list); ++ /* g_assert (last != NULL); */ ++ last->next = new_list; ++ new_list->prev = last; ++ ++ return list; ++ } ++ else ++ { ++ new_list->prev = NULL; ++ return new_list; ++ } ++} ++ ++static inline GList* ++_g_list_remove_link (GList *list, ++ GList *link) ++{ ++ if (link) ++ { ++ if (link->prev) ++ link->prev->next = link->next; ++ if (link->next) ++ link->next->prev = link->prev; ++ ++ if (link == list) ++ list = list->next; ++ ++ link->next = NULL; ++ link->prev = NULL; ++ } ++ ++ return list; ++} ++ ++/** ++ * g_list_delete_link: ++ * @list: a #GList ++ * @link_: node to delete from @list ++ * ++ * Removes the node link_ from the list and frees it. ++ * Compare this to g_list_remove_link() which removes the node ++ * without freeing it. ++ * ++ * Returns: the new head of @list ++ */ ++GList* ++g_list_delete_link (GList *list, ++ GList *link_) ++{ ++ list = _g_list_remove_link (list, link_); ++ free (link_); ++ ++ return list; ++} ++ ++/** ++ * g_list_first: ++ * @list: a #GList ++ * ++ * Gets the first element in a #GList. ++ * ++ * Returns: the first element in the #GList, ++ * or %NULL if the #GList has no elements ++ */ ++GList* ++g_list_first (GList *list) ++{ ++ if (list) ++ { ++ while (list->prev) ++ list = list->prev; ++ } ++ ++ return list; ++} ++ ++static GList * ++g_list_sort_merge (GList *l1, ++ GList *l2, ++ GFunc compare_func, ++ gpointer user_data) ++{ ++ GList list, *l, *lprev; ++ gint cmp; ++ ++ l = &list; ++ lprev = NULL; ++ ++ while (l1 && l2) ++ { ++ cmp = ((GCompareDataFunc) compare_func) (l1->data, l2->data, user_data); ++ ++ if (cmp <= 0) ++ { ++ l->next = l1; ++ l1 = l1->next; ++ } ++ else ++ { ++ l->next = l2; ++ l2 = l2->next; ++ } ++ l = l->next; ++ l->prev = lprev; ++ lprev = l; ++ } ++ l->next = l1 ? l1 : l2; ++ l->next->prev = l; ++ ++ return list.next; ++} ++ ++static GList* ++g_list_sort_real (GList *list, ++ GFunc compare_func, ++ gpointer user_data) ++{ ++ GList *l1, *l2; ++ ++ if (!list) ++ return NULL; ++ if (!list->next) ++ return list; ++ ++ l1 = list; ++ l2 = list->next; ++ ++ while ((l2 = l2->next) != NULL) ++ { ++ if ((l2 = l2->next) == NULL) ++ break; ++ l1 = l1->next; ++ } ++ l2 = l1->next; ++ l1->next = NULL; ++ ++ return g_list_sort_merge (g_list_sort_real (list, compare_func, user_data), ++ g_list_sort_real (l2, compare_func, user_data), ++ compare_func, ++ user_data); ++} ++ ++/** ++ * g_list_sort: ++ * @list: a #GList ++ * @compare_func: the comparison function used to sort the #GList. ++ * This function is passed the data from 2 elements of the #GList ++ * and should return 0 if they are equal, a negative value if the ++ * first element comes before the second, or a positive value if ++ * the first element comes after the second. ++ * ++ * Sorts a #GList using the given comparison function. ++ * ++ * Returns: the start of the sorted #GList ++ */ ++/** ++ * GCompareFunc: ++ * @a: a value. ++ * @b: a value to compare with. ++ * @Returns: negative value if @a < @b; zero if @a = @b; positive ++ * value if @a > @b. ++ * ++ * Specifies the type of a comparison function used to compare two ++ * values. The function should return a negative integer if the first ++ * value comes before the second, 0 if they are equal, or a positive ++ * integer if the first value comes after the second. ++ **/ ++GList * ++g_list_sort (GList *list, ++ GCompareFunc compare_func) ++{ ++ return g_list_sort_real (list, (GFunc) compare_func, NULL); ++ ++} ++ ++/** ++ * g_list_length: ++ * @list: a #GList ++ * ++ * Gets the number of elements in a #GList. ++ * ++ * ++ * This function iterates over the whole list to ++ * count its elements. ++ * ++ * ++ * Returns: the number of elements in the #GList ++ */ ++guint ++g_list_length (GList *list) ++{ ++ guint length; ++ ++ length = 0; ++ while (list) ++ { ++ length++; ++ list = list->next; ++ } ++ ++ return length; ++} ++ ++/** ++ * g_list_foreach: ++ * @list: a #GList ++ * @func: the function to call with each element's data ++ * @user_data: user data to pass to the function ++ * ++ * Calls a function for each element of a #GList. ++ */ ++/** ++ * GFunc: ++ * @data: the element's data. ++ * @user_data: user data passed to g_list_foreach() or ++ * g_slist_foreach(). ++ * ++ * Specifies the type of functions passed to g_list_foreach() and ++ * g_slist_foreach(). ++ **/ ++void ++g_list_foreach (GList *list, ++ GFunc func, ++ gpointer user_data) ++{ ++ while (list) ++ { ++ GList *next = list->next; ++ (*func) (list->data, user_data); ++ list = next; ++ } ++} ++ ++/** ++ * g_list_free_full: ++ * @list: a pointer to a #GList ++ * @free_func: the function to be called to free each element's data ++ * ++ * Convenience method, which frees all the memory used by a #GList, and ++ * calls the specified destroy function on every element's data. ++ * ++ * Since: 2.28 ++ */ ++void ++g_list_free_full (GList *list, ++ GDestroyNotify free_func) ++{ ++ g_list_foreach (list, (GFunc) free_func, NULL); ++ g_list_free (list); ++} ++ ++/** ++ * g_list_find_custom: ++ * @list: a #GList ++ * @data: user data passed to the function ++ * @func: the function to call for each element. ++ * It should return 0 when the desired element is found ++ * ++ * Finds an element in a #GList, using a supplied function to ++ * find the desired element. It iterates over the list, calling ++ * the given function which should return 0 when the desired ++ * element is found. The function takes two #gconstpointer arguments, ++ * the #GList element's data as the first argument and the ++ * given user data. ++ * ++ * Returns: the found #GList element, or %NULL if it is not found ++ */ ++GList* ++g_list_find_custom (GList *list, ++ gconstpointer data, ++ GCompareFunc func) ++{ ++ g_return_val_if_fail (func != NULL, list); ++ ++ while (list) ++ { ++ if (! func (list->data, data)) ++ return list; ++ list = list->next; ++ } ++ ++ return NULL; ++} +diff --git a/glib-local/glist.h b/glib-local/glist.h +new file mode 100644 +index 0000000..47f2cfe +--- /dev/null ++++ b/glib-local/glist.h +@@ -0,0 +1,56 @@ ++#ifndef __G_LIST_H__ ++#define __G_LIST_H__ ++ ++typedef int gint; ++typedef unsigned int guint; ++typedef void* gpointer; ++typedef const void *gconstpointer; ++typedef gint (*GCompareFunc) (gconstpointer a, ++ gconstpointer b); ++typedef gint (*GCompareDataFunc) (gconstpointer a, ++ gconstpointer b, ++ gpointer user_data); ++typedef void (*GFunc) (gpointer data, ++ gpointer user_data); ++typedef void (*GDestroyNotify) (gpointer data); ++ ++struct _GList; ++typedef struct _GList GList; ++ ++struct _GList ++{ ++ gpointer data; ++ GList *next; ++ GList *prev; ++}; ++ ++/* Doubly linked lists ++ */ ++void g_list_free (GList *list); ++GList* g_list_append (GList *list, ++ gpointer data); ++GList* g_list_delete_link (GList *list, ++ GList *link_); ++GList* g_list_first (GList *list); ++GList* g_list_sort (GList *list, ++ GCompareFunc compare_func); ++guint g_list_length (GList *list); ++void g_list_foreach (GList *list, ++ GFunc func, ++ gpointer user_data); ++void g_list_free_full (GList *list, ++ GDestroyNotify free_func); ++GList* g_list_find_custom (GList *list, ++ gconstpointer data, ++ GCompareFunc func); ++ ++#define g_list_previous(list) ((list) ? (((GList *)(list))->prev) : NULL) ++#define g_list_next(list) ((list) ? (((GList *)(list))->next) : NULL) ++ ++#define g_return_val_if_fail(expr,val) do { \ ++ if (expr) { } else \ ++ { \ ++ return (val); \ ++ } } while(0); ++ ++#endif /* __G_LIST_H__ */ +diff --git a/irqbalance.1 b/irqbalance.1 +index 55fc15f..20105bc 100644 +--- a/irqbalance.1 ++++ b/irqbalance.1 +@@ -39,7 +39,11 @@ Causes irqbalance to be run once, after which the daemon exits + .TP + + .B --debug +-Causes irqbalance to run in the foreground and extra debug information to be printed ++Causes irqbalance to print extra debug information. Implies --foreground ++ ++.TP ++.B --foreground ++Causes irqbalance to run in the foreground (without --debug) + + .TP + .B --hintpolicy=[exact | subset | ignore] +@@ -62,6 +66,30 @@ 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 + powersave mode. In powersave mode, a cpu will not have any irqs balanced to it, + in an effort to prevent that cpu from waking up without need. ++ ++.TP ++.B --banirq= ++Add the specified irq list to the set of banned irqs. irqbalance will not affect ++the affinity of any irqs on the banned list, allowing them to be specified ++manually. This option is addative and can be specified multiple times ++ ++.TP ++.B --banscript=