policyscript: Add ability to specify a directory for multiple scripts

The policyscript directive allows for the specifcaion of a single script
to define policy for all hardware on a system, which is good as a
site/host specific utility, but it makes for difficult work in the event
that vendors wish to provide guidance for only their own hardware (i.e.
if vendor A wants certain hardware to follow affinity_hinting without
affecting other hardware).  To manage this, lets enhance policyscript to
allow the specification of an entire directory, to which multiple
scripts can be added.  Semantics for this new directory feature are the
same as for the single script case, except that the script exit codes
have additional meaning:

exit code 0 - the script indicates that the referenced irq relates to a
device that this script recognizes and further script processing should
stop

exit code 1 - the script indicates that the referenced irq does not
relate to a device the script recognizes, and script processing should
continue

exit code >2 - the script indicates an error has occured, and any output
from it should be ignored, script processing should continue

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
This commit is contained in:
Neil Horman 2018-07-09 12:54:27 -04:00 committed by Neil Horman
parent d6abbe898b
commit 6e6ac7bc65
2 changed files with 90 additions and 25 deletions

View file

@ -3,6 +3,7 @@
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <assert.h>
#include <errno.h>
@ -493,37 +494,22 @@ static void parse_user_policy_key(char *buf, int irq, struct user_irq_policy *po
}
/*
* Calls out to a possibly user defined script to get user assigned policy
* aspects for a given irq. A value of -1 in a given field indicates no
* policy was given and that system defaults should be used
*/
static void get_irq_user_policy(char *path, int irq, struct user_irq_policy *pol)
static int run_script_for_policy(char *script, char *path, int irq, struct user_irq_policy *pol)
{
char *cmd;
char *brc;
FILE *output;
char buffer[128];
char *brc;
memset(pol, -1, sizeof(struct user_irq_policy));
/* Return defaults if no script was given */
if (!polscript)
return;
/* Use SYSFS_DIR for irq has no sysfs entries */
if (!path)
path = SYSFS_DIR;
cmd = alloca(strlen(path)+strlen(polscript)+64);
cmd = alloca(strlen(path)+strlen(script)+64);
if (!cmd)
return;
return -1;
sprintf(cmd, "exec %s %s %d", polscript, path, irq);
sprintf(cmd, "exec %s %s %d", script, path, irq);
output = popen(cmd, "r");
if (!output) {
log(TO_ALL, LOG_WARNING, "Unable to execute user policy script %s\n", polscript);
return;
log(TO_ALL, LOG_WARNING, "Unable to execute user policy script %s\n", script);
return 1; /* tell caller to ignore this script */
}
while(!feof(output)) {
@ -531,7 +517,69 @@ static void get_irq_user_policy(char *path, int irq, struct user_irq_policy *pol
if (brc)
parse_user_policy_key(brc, irq, pol);
}
pclose(output);
return WEXITSTATUS(pclose(output));
}
/*
* Calls out to a possibly user defined script to get user assigned policy
* aspects for a given irq. A value of -1 in a given field indicates no
* policy was given and that system defaults should be used
*/
static void get_irq_user_policy(char *path, int irq, struct user_irq_policy *pol)
{
struct stat sbuf;
DIR *poldir;
struct dirent *entry;
int ret;
char script[1024];
memset(pol, -1, sizeof(struct user_irq_policy));
/* Return defaults if no script was given */
if (!polscript)
return;
if (stat(polscript, &sbuf))
return;
/* Use SYSFS_DIR for irq has no sysfs entries */
if (!path)
path = SYSFS_DIR;
if (!S_ISDIR(sbuf.st_mode)) {
if (run_script_for_policy(polscript, path, irq, pol) != 0) {
log(TO_CONSOLE, LOG_ERR, "policy script returned non-zero code! skipping user policy\n");
memset(pol, -1, sizeof(struct user_irq_policy));
}
} else {
/* polscript is a directory, user multiple script semantics */
poldir = opendir(polscript);
if (poldir) {
while ((entry = readdir(poldir)) != NULL) {
snprintf(script, sizeof(script), "%s/%s", polscript, entry->d_name);
if (stat(script, &sbuf))
continue;
if (S_ISREG(sbuf.st_mode)) {
memset(pol, -1, sizeof(struct user_irq_policy));
ret = run_script_for_policy(script, path, irq, pol);
if ((ret < 0) || (ret >= 2)) {
log(TO_CONSOLE, LOG_ERR, "Error executing policy script %s : %d\n", script, ret);
continue;
}
/* a ret of 1 means this script isn't
* for this irq
*/
if (ret == 1)
continue;
log(TO_CONSOLE, LOG_DEBUG, "Accepting script %s to define policy for irq %d\n", script, irq);
break;
}
}
}
}
}
static int check_for_module_ban(char *name)

View file

@ -80,7 +80,7 @@ The default value for deepestcache is 2.
.TP
.B -l, --policyscript=<script>
When specified, the referenced script will execute once for each discovered IRQ,
When specified, the referenced script or directory will execute once for each discovered IRQ,
with the sysfs device path and IRQ number passed as arguments. Note that the
device path argument will point to the parent directory from which the IRQ
attributes directory may be directly opened.
@ -90,7 +90,6 @@ and will be captured and interpreted by irqbalance. Irqbalance expects a zero
exit code from the provided utility. Recognized key=value pairs are:
.TP
.I ban=[true | false]
.TP
Directs irqbalance to exclude the passed in IRQ from balancing.
.TP
.I balance_level=[none | package | cache | core]
@ -106,6 +105,24 @@ This option allows for that hardware provided information to be overridden, so
that irqbalance can bias IRQ affinity for these devices toward its most local
node. Note that specifying a -1 here forces irqbalance to consider an interrupt
from a device to be equidistant from all nodes.
.TP
Note that, if a directory is specified rather than a regular file, all files in
the directory will be considered policy scripts, and executed on adding of an
irq to a database. If such a directory is specified, scripts in the directory
must additionally exit with one of the following exit codes:
.TP
.I 0
This indicates the script has a policy for the referenced irq, and that further
script processing should stop
.TP
.I 1
This indicates that the script has no policy for the referenced irq, and that
script processing should continue
.TP
.I 2
This indicates that an error has occured in the script, and it should be skipped
(further processing to continue)
.TP
.B -s, --pid=<file>
Have irqbalance write its process id to the specified file. By default no