pkg://hwtools_0.8.orig.tar.gz:245593/
hwtools-0.8/
irqtune/
src/irqtune.c
downloads
/* irqtune -- load IRQ tuning module */
#pragma member irqtune.h
#include <irqtune.h>
#include <lib/cmd.c>
#include <lib/file.c>
#include <lib/opt.c>
#include <lib/xstr.c>
#include <lib/sys.c>
#include <lib/zprt.c>
#include <lib/zprtx.c>
/* main -- main program */
void
main(int argc,char **argv)
{
char *cp;
int idx;
int val;
pgmname = *argv;
--argc;
++argv;
proc_ints = "/proc/interrupts";
setbuf(stderr,NULL);
sprintf(tmpf,"/tmp/irqtune.%d",getpid());
/* insure we got "/sbin/irqtune" and not just "irqtune" */
cp = strchr(pgmname,'/');
if (cp != NULL)
dynstat |= DYN_FULL_PATH;
/* locate the top directory */
strcpy(dirtop,pgmname);
filehead(dirtop);
/* decode options */
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
if (! optget(opthelp,cp + 1))
usage();
}
/* do install and stop */
if (mstopt & OPTINSTALL)
install(argc,argv);
/* do version and stop */
if (mstopt & (OPTVERSION | OPTERR)) {
zprtx("irqtune: version is %s\n",IRQTUNE_VERSION);
if (mstopt & OPTVERSION)
exit(0);
}
/* display help and stop */
if (mstopt & OPTHELP) {
_usage();
exit(0);
}
/* get IRQ priority arguments */
idx = 0;
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (! RNGE(*cp,'0','9'))
break;
if (idx >= sizeof(irqhigh_list))
usage();
val = atoi(cp);
if (! RNGE(val,0,15))
usage();
irqhigh_list[idx++] = val & 0x0F;
}
/* set bad defaults */
if (mstopt & OPTRESET) {
irqhigh_list[0] = IRQHIGH_MST_BAD;
irqhigh_list[1] = IRQHIGH_SLV_BAD;
}
/* check the sanity */
cp = irqhigh_insane(irqhigh_list[0],irqhigh_list[1]);
if (cp != NULL)
sysfault("irqtune: unable to use values %u/%u -- %s\n",
irqhigh_list[0],irqhigh_list[1],cp);
/* fake it */
irqhigh_set(0,irqhigh_list[0],irqhigh_list[1]);
/* probe the configuration and find any trouble */
probectl();
/* load the module by whatever means necessary */
loader();
/* send the table */
if ((mstopt & OPTSORT) || (mstopt & OPTNOGO))
mstopt &= ~OPTQUIET;
if (bigcode)
mstopt |= OPTQUIET;
if (! (mstopt & OPTQUIET)) {
tblinit();
tblread(1,proc_ints);
tblshow();
}
/* do final exit */
sysexit(bigcode);
}
/* install -- install program */
void
install(int argc,char **argv)
{
char **modtail;
int idx;
int err;
int ok;
int unit;
FILE *xf;
char srcfile[1024];
char dstfile[1024];
bigcode = 1;
xf = NULL;
unit = -1;
do {
/* insure we're invoked by full path */
if (! (dynstat & DYN_FULL_PATH)) {
zprtx("install: must be invoked via full path\n");
break;
}
/* check number of arguments */
if (argc != 3) {
zprtx("install: wrong number of arguments\n");
break;
}
/* get SBIN directory */
strcpy(pathtmp,argv[1]);
/* do simple installation */
if (strcmp(argv[0],"simple") == 0) {
zprtx("irqtune: simple installation ...\n");
/* copy over self */
err = -1;
for (idx = 0; idx <= 1; ++idx) {
err = filecpy(idx,pathtmp,dirtop,"irqtune");
if (err < 0)
break;
}
if (err < 0)
break;
/* copy over our modules */
err = -1;
ok = 0;
for (modtail = modnames; *modtail != NULL; ++modtail) {
for (idx = 0; idx <= 1; ++idx) {
sprintf(srcfile,"%s.o",*modtail);
err = filecpy(idx,pathtmp,dirtop,srcfile);
if (err < 0)
continue;
ok = 1;
}
}
if (! ok) {
zprtx("install: no modules found\n");
break;
}
bigcode = 0;
break;
}
/* do stub installation */
if (strcmp(argv[0],"sh") == 0) {
zprtx("install: stub installation ...\n");
/* open/create the stub file */
unit = filecpy(1,pathtmp,NULL,"irqtune");
if (unit < 0)
break;
/* attach a stream */
xf = fdopen(unit,"a");
if (xf == NULL) {
zprtx("install: unable to fdopen -- %s\n",
strerror(errno));
break;
}
unit = -1;
fprintf(xf,"#!/bin/sh -\n");
fprintf(xf,"IRQTUNE_HOME='%s'\n",argv[2]);
fprintf(xf,"exec $IRQTUNE_HOME/sbin/irqtune $*\n");
fclose(xf);
xf = NULL;
/* remove .o files */
for (modtail = modnames; *modtail != NULL; ++modtail) {
sprintf(dstfile,"%s/%s.o",pathtmp,*modtail);
err = unlink(dstfile);
if (err >= 0)
zprtx("install: removing %s ...\n",dstfile);
}
bigcode = 0;
break;
}
zprtx("install: unknown installation mode -- '%s'\n",
argv[0]);
} while (0);
if (xf != NULL)
fclose(xf);
if (unit >= 0)
close(unit);
sysexit(bigcode);
}
/* loader -- load module */
void
loader(void)
{
char *cp;
int err;
int ldcode;
int rmcode;
char **modtail;
int modcnt;
char *logf;
struct stat mystat;
bigcode = 1;
/* force unloading if requested (do this silently as it's likely to */
/* fail in the normal case) we will unload anything (even stuff loaded */
/* manually without the "-o" option) */
rmcode = 0;
for (modtail = modnames; *modtail != NULL; ++modtail) {
if (mstopt & OPTNOGO)
break;
if (! (mstopt & OPTUNLOAD))
break;
sprintf(bigbf,"%s %s",rmmod_file,*modtail);
rmcode = vsystem(0,"pre-unloading",*modtail,NULL,bigbf);
} while (0);
/* load the module (fallback to simpler versions to insure it loads) */
modcnt = 0;
/* don't try something that is likely to fail (unless forced) */
modtail = modnames;
if (! (mstopt & OPTFORCE)) {
if (dynstat & DYN_NPRONLY)
++modtail;
}
for (; *modtail != NULL; ++modtail) {
sprintf(modcur,"%s/%s.o",dirtop,*modtail);
/* check for module existence -- don't spew unless full errors as */
/* known useless modules may be removed to speed things up */
err = stat(modcur,&mystat);
if (err < 0) {
if (mstopt & OPTERR)
zprtx("irqtune: module %s not found -- skipping\n",
modcur);
continue;
}
++modcnt;
/* NOTE: we are version independent */
/* insmod complains even with the force option */
logf = "/dev/null";
cp = bigbf;
cp += sprintf(cp,"%s -x -o %s -f %s",
insmod_file,modtag,modcur);
/* tell insmod to do verbose output */
#ifdef NEVERWAS
if (mstopt & OPTERR)
cp += sprintf(cp," -v");
#endif /*NEVERWAS*/
/* add the desired priority */
cp += sprintf(cp," priority=%d,%d",irqhigh_list[0],irqhigh_list[1]);
/* we don't need to check system log (we really should to be sure) */
zprtx("irqtune: %s %u/%u\n",
(mstopt & OPTNOGO) ? "simulating IRQ priority of" : "setting system IRQ priority to",
irqhigh_list[0],irqhigh_list[1]);
fflush(stderr);
/* perform module load */
ldcode = 0;
do {
if (mstopt & OPTNOGO)
break;
/* sync the disks, just in case */
sync();
/* install it */
ldcode = vsystem(mstopt & OPTERR,"loading",
modcur,modtag,bigbf);
} while (0);
/* check for errors -- the normal presumption is if insmod failed, */
/* there is no module to unload */
if (ldcode) {
if (! (mstopt & OPTUNLOAD))
continue;
}
/* the mere act of installing does the job so uninstall immediately */
rmcode = 0;
do {
if (mstopt & OPTNOGO)
break;
sprintf(bigbf,"%s %s",rmmod_file,modtag);
rmcode = vsystem(1,"unloading",modcur,modtag,bigbf);
} while (0);
/*
// normally, failure to unload is bad because it's so simple.
// if we're forcing unload, we may attempt to unload a module that
// never got loaded. this isn't an immediate killer, it will be
// detected in the final sweep (e.g. bigcode will stay set).
// it could also be some nasty, genuine failure of rmmod to unload.
// we can't tell the difference without some arcane version dependent
// digesting of rmmod stderr output, so we'll just go on and hope for
// the best.
*/
if (rmcode) {
if (! (mstopt & OPTUNLOAD))
break;
}
/* stop loading if we found one that works */
if ((ldcode == 0) && (rmcode == 0)) {
bigcode = 0;
break;
}
}
/* complain about no modules found whatsoever */
do {
if (mstopt & OPTNOGO)
break;
if (modcnt <= 0) {
zprtx("irqtune: no irqtune modules found\n");
bigcode = 1;
}
} while (0);
}
/* probechk -- check dynamic configuration */
void
probechk(mskof_t actmsk,const char *fmt,...)
{
int ok;
int errflg;
mskof_t errmsk;
const char *tag;
va_list ap;
/* decide if we're missing something required */
ok = BOOL(dynstat & actmsk);
if (actmsk & DYN_NEG)
ok = ! ok;
/* decide on fatal error */
errmsk = DYN_FATAL;
if (mstopt & OPTWARN)
errmsk |= DYN_WARN;
errflg = 0;
if (actmsk & errmsk)
errflg = (! ok);
if ((mstopt & OPTERR) || errflg) {
zprtx("probe: ");
va_start(ap,fmt);
zprtvx(0,fmt,ap);
va_end(ap);
do {
if (actmsk & DYN_FATAL) {
tag = ok ? "OK" : "ERROR";
break;
}
if (actmsk & DYN_WARN) {
tag = ok ? "OK" : "WARNING";
break;
}
tag = ok ? "YES" : "NO";
} while (0);
zprtx(" -- %s\n",tag);
}
/* mark fatal error */
if (errflg)
dynerr |= actmsk;
}
/* probectl -- probe control (to probe 1) */
void
probectl(void)
{
/* do the probes */
probeknl();
probeksyms();
probeins();
tblread(0,proc_ints);
/* check for insmod conflicts against kernel */
do {
/* check for the first nicely working version */
dynstat &= ~DYN_INSMOD_STRONG;
if (insmod_version >= INSMOD_VERGOOD) {
dynstat |= DYN_INSMOD_STRONG;
break;
}
/* both insmod and kernel are 2.0.X */
if ((insmod_version < VERMK(2,1,0)) &&
(kvers_dynamic < VERMK(2,1,0)))
break;
/* the killer a bad 2.1.X insmod on a 2.0.X kernel */
if (RNGEM1(insmod_version,VERMK(2,1,0),INSMOD_VERGOOD) &&
(kvers_dynamic < VERMK(2,1,0))) {
dynstat |= DYN_INSMOD_CRASH;
break;
}
} while (0);
/* avert possible insmod crash */
if ((dynstat & DYN_INSMOD_CRASH) &&
(dynstat & (DYN_KSYMS_USING | DYN_KSYMS_CSUM)))
dynstat |= DYN_NPRONLY;
/* mention a 1.X insmod on a 2.X kernel */
if (kvers_dynamic >= VERMK(2,0,0)) {
if (VERMAJ(kvers_dynamic) > VERMAJ(insmod_version))
dynstat |= DYN_INSMOD_OLD;
}
/* detect and show errors */
probectl2();
/* abort on detected errors */
if (dynerr) {
zprtx("irqtune: probe detected errors\n");
if (mstopt & OPTFORCE)
zprtx("irqtune: loading forced\n");
else
sysfault("irqtune: loading not performed (may be overriden by -f)\n");
}
}
/* probectl2 -- probe control (to probe 2) */
void
probectl2(void)
{
probechk(DYN_FATAL | DYN_FULL_PATH,
"irqtune must be invoked via the full path");
/* show basic insmod operation */
probechk(DYN_SBIN_PATH,"/sbin in $PATH");
probechk(DYN_FATAL | DYN_INSMOD_PATH,"insmod found in $PATH (%s)",
insmod_dir);
probechk(DYN_FATAL | DYN_INSMOD_EXEC,"insmod simple execution");
probechk(DYN_INSMOD_VERSION,"insmod has version (" VERFMT ")",
VERPRT(insmod_version));
probechk(DYN_FATAL | DYN_RMMOD_PATH,"rmmod found in insmod directory");
/* show an unusable insmod (it accepts no dash options) */
/* FIXME -- we could support this at some cost, but really this is likely */
/* some early beta copy of insmod. sanity beats flexibility here */
probechk(DYN_FATAL | DYN_NEG | DYN_INSMOD_ANCIENT,
"insmod version supports command line options");
/* show a downrev insmod */
probechk(DYN_FATAL | DYN_NEG | DYN_INSMOD_OLD,
"insmod version (" VERFMT ") compatible with kernel version (" VERFMT ")",
VERPRT(insmod_version),VERPRT(kvers_dynamic));
/* show a no-problems insmod */
probechk(DYN_WARN | DYN_INSMOD_STRONG,
"insmod version should be " VERFMT " (or better)",
VERPRT(INSMOD_VERGOOD));
/* show possible insmod crash (we can compensate) */
probechk(DYN_WARN | DYN_NEG | DYN_INSMOD_CRASH,
"insmod and kernel compatible with CONFIG_MODVERSIONS");
/* show that we're doing crash compensation */
probechk(DYN_WARN | DYN_NEG | DYN_NPRONLY,"%s loading will be tried",
modnames[0]);
probechk(DYN_KNL_CPLDIFF | DYN_NEG,
"kernel version irqtune built under (" VERFMT ") matches current system",
VERPRT(kvers_compile));
/* complain about kernels where IRQ handling renders us ineffective */
probechk(DYN_FATAL | DYN_KNL_VEROK,"kernel IRQ handling is compatible");
/* show kernels built without modules support */
probechk(DYN_FATAL | DYN_KNL_GSYM,"kernel has module support (CONFIG_MODULES)");
probechk(DYN_FATAL | DYN_KSYMS_FOUND,"kernel has symbols");
/* show modversions information */
probechk(DYN_KSYMS_USING,"kernel is using versions (CONFIG_MODVERSIONS)");
probechk(DYN_KSYMS_CSUM,"kernel symbols are checksummed (CONFIG_MODVERSIONS)");
/* require /proc/interrupts */
probechk(DYN_FATAL | DYN_PROC_INTS,"kernel has /proc/interrupts");
}
/* probeins -- probe for insmod */
void
probeins(void)
{
char *cp;
int nextflg;
int err;
struct cmdblk *cmd;
char **av;
char *argv[10];
char bf[1024];
/* add most probable place for insmod */
cp = pathdir("/sbin");
if (cp != NULL)
dynstat |= DYN_SBIN_PATH;
cmd = NULL;
do {
strcpy(insmod_file,"insmod");
strcpy(rmmod_file,"rmmod");
/* probe for insmod in $PATH */
cp = pathlook("insmod",insmod_path);
if (cp == NULL) {
sprintf(insmod_dir,"???");
break;
}
/* directory where insmod was found */
dynstat |= DYN_INSMOD_PATH;
strcpy(insmod_dir,cp);
if (insmod_path != NULL)
sprintf(insmod_file,"%s/insmod",insmod_dir);
/* NOTE: we require that rmmod be in the same directory as insmod */
/* flexibility is one thing, but probable sanity is more important */
cp = pathlook("rmmod",insmod_dir);
if (cp != NULL) {
dynstat |= DYN_RMMOD_PATH;
if (insmod_path != NULL)
sprintf(rmmod_file,"%s/rmmod",insmod_dir);
}
/* capture output of simple insmod execution (and get version) */
/* WARNING: don't change this without change to ANCIENT string below */
sprintf(bf,"%s -V",insmod_file);
cmd = cmdopen(bf);
if (cmd == NULL)
break;
dynstat |= DYN_INSMOD_EXEC;
/* find the version */
while (1) {
/* read next line */
err = cmdread(cmd,bigbf,sizeof(bigbf));
if (err < 0)
break;
/* look for ancient insmod that supports no command line options */
if (strcmp(bigbf,"Cannot open -V") == 0)
dynstat |= DYN_INSMOD_ANCIENT;
/* look for "x.y.z" */
xstrargv(argv,Cntof(argv),bigbf);
nextflg = 0;
for (av = argv; *av != NULL; ++av) {
cp = *av;
if (strcasecmp(cp,"version") == 0)
nextflg = 1;
/* bug out if we've found a version number */
insmod_version = verdcd(cp);
if (insmod_version)
break;
}
if (insmod_version)
break;
}
/* insure that we got a version */
if (insmod_version != 0)
dynstat |= DYN_INSMOD_VERSION;
} while (0);
/* remove temp file */
cmdclose(cmd);
}
/* probeknl -- probe the kernel */
void
probeknl(void)
{
int err;
/* decide what kernel we were compiled under */
kvers_compile = verdcd(kernel_version);
/* decide what kernel we're running under */
uname(&uts);
kvers_dynamic = verdcd(uts.release);
if (mstopt & OPTERR)
zprtx("irqtune: kernel version " VERFMT "\n",
VERPRT(kvers_dynamic));
/* show the version discrepancy (it _should_ not matter because of -f) */
if (kvers_compile != kvers_dynamic)
dynstat |= DYN_KNL_CPLDIFF;
/* check for incompatible kernel IRQ handling */
if (! RNGE(kvers_dynamic,KNL_VERBADLO,KNL_VERBADHI))
dynstat |= DYN_KNL_VEROK;
/* check for module support compiled into kernel (if kernel has */
/* CONFIG_MODULES off, this will return ENOSYS). since we're only using */
/* this to probe, it should still work even with 2.1.18 or later */
err = get_kernel_syms(NULL);
if (err >= 0)
dynstat |= DYN_KNL_GSYM;
}
/* probeksyms -- probe /proc/ksyms */
void
probeksyms(void)
{
char *cp;
FILE *xf;
int argc;
int len;
char *argv[10];
dynstat &= ~DYN_KSYMS_FOUND;
do {
xf = fopen("/proc/ksyms","r");
if (xf == NULL)
break;
/* scan the symbols */
while (1) {
cp = fgets(bigbf,sizeof(bigbf),xf);
if (cp == NULL)
break;
/* clean the line */
cp = strrchr(bigbf,'\n');
if (cp != NULL)
*cp = 0;
argc = xstrargv(argv,Cntof(argv),bigbf);
if (argc < 2)
continue;
/* look for <value> <symbol> */
dynstat |= DYN_KSYMS_FOUND;
cp = argv[1];
/* decide if version numbers are being used */
if (! (dynstat & DYN_KSYMS_USING)) {
if (strcmp(cp,"Using_Versions") == 0)
dynstat |= DYN_KSYMS_USING;
}
/* decide if symbol has been checksummed */
len = strlen(cp);
if (len > 10) {
if ((cp[len - 10] == '_') && (cp[len - 9] == 'R')) {
dynstat |= DYN_KSYMS_CSUM;
len -= 10;
}
}
}
fclose(xf);
} while (0);
}
/* verdcd -- decode version number */
/* RETURNS: decoded version number */
u_long
verdcd(char *bf)
{
char *bp;
u_long valmaj;
u_long valmin;
u_long valsub;
valmaj = 0;
valmin = 0;
valsub = 0;
do {
for (bp = bf; *bp != 0; ++bp) {
if (RNGE(*bp,'0','9'))
continue;
if (*bp != '.')
break;
}
if (*bp != 0)
break;
bp = bf;
valmaj = strtol(bp,&bp,10);
++bp;
valmin = strtol(bp,&bp,10);
++bp;
valsub = strtol(bp,&bp,10);
} while (0);
valmaj = VERMK(valmaj,valmin,valsub);
return valmaj;
}
/* tblfind -- find table entry */
/* RETURNS: pointer to /proc/interrupts table entry */
struct tblblk *
tblfind(u_char irq_no)
{
struct tblblk *tbl;
/* NOTE: we can do a sort cheaply by indexing here by priority */
if (mstopt & OPTSORT)
irq_no = irq_prior_list[irq_no];
tbl = &tblbase[irq_no];
return tbl;
}
/* tblinit -- initialize to default */
void
tblinit(void)
{
u_char irq_no;
struct tblblk *tbl;
/* calculate priorities */
irqhigh_calc();
/* initialize the table (and calculate priorities) */
for (irq_no = 0; irq_no < IRQHIGH_MAX; ++irq_no) {
tbl = tblfind(irq_no);
tbl->tbl_irq = irq_no;
tbl->tbl_prior = irq_prior_list[irq_no];
tbl->tbl_bias = irq_prior_bias[irq_no];
strcpy(tbl->tbl_bf," 0 ? Inactive");
}
}
/* tblread -- read in /proc/interrupts table */
void
tblread(int goflg,char *file)
{
FILE *xf;
int err;
char *cp;
u_long irq_no;
struct tblblk *tbl;
char bf[80];
xf = fopen(file,"r");
while (xf != NULL) {
/* read a line */
cp = fgets(bf,sizeof(bf),xf);
if (cp == NULL)
break;
/* clean the line */
cp = strchr(bf,'\n');
if (cp != NULL)
*cp = 0;
/* get the irq number */
irq_no = strtol(bf,&cp,10);
/* do a modest syntax check */
err = 1;
do {
if (*cp != ':')
break;
++cp;
if (irq_no > IRQHIGH_SLV_HI)
break;
err = 0;
} while (0);
/* skip entry on error */
if (err) {
zprtx("tblread: SYNTAX '%s'\n",bf);
continue;
}
dynstat |= DYN_PROC_INTS;
if (! goflg)
break;
/* copy into table */
tbl = tblfind(irq_no);
strcpy(tbl->tbl_bf,cp);
tbl->tbl_active = 1;
}
if (xf != NULL)
fclose(xf);
}
/* tblshow -- show table */
void
tblshow(void)
{
u_char irq_no;
struct tblblk *tbl;
/* NOTE: do _not_ use tblfind here (and this completes the sort) */
for (irq_no = 0; irq_no < IRQHIGH_MAX; ++irq_no) {
tbl = &tblbase[irq_no];
if ((mstopt & OPTALL) || tbl->tbl_active)
zprtx("I%2.2u/P%2.2u: %s\n",
tbl->tbl_irq,tbl->tbl_prior - tbl->tbl_bias,tbl->tbl_bf);
}
}
/* _usage -- show program usage */
void
_usage(void)
{
zprtx("usage: irqtune [options] [master] [slave]\n");
zprtx("version: %s\n",IRQTUNE_VERSION);
zprtx("arguments:\n");
zprtx(" master -- high priority IRQ on PIC master (DEFAULT: %d)\n",
IRQHIGH_MST_DFT);
zprtx(" slave -- high priority IRQ on PIC slave (DEFAULT: %d)\n",
IRQHIGH_SLV_DFT);
optusage(opthelp);
}
/* usage -- show program usage and exit */
void
usage(void)
{
_usage();
sysexit(1);
}