Sunday, November 25, 2007

INTR_FAST and selwakeup

I stumbled over this problem a couple of years ago, ignored it, was punished for my carelessness, came out with hackerish solution and now I know "The Right Way" to solve this issue. So, the headache starts when you're trying to wakeup userland application polling on a descriptor from INTR_FAST IRQ handler (for 7.X and later that would be interrupt filter). INTR_FAST/interrupt filter routines are usually used for timing-critical tasks and run in IRQ dispatcher context, so no operations that may cause context switch are allowed in this code. Unfortunately selwakeup(9) tries to acquire sellock that leads to a possible context switch and leaves us in a total mess. My solution was handmade kernel thread that has been running through list of channels and performed all dirty work, not the cleanest and easiest to maintain code. "Zaptel-bsd take 2" utilizes taskqueue(9) interface to work around IRQ handler limitations. There is handler routine:


static void
handle_selwakeup(void *context, int pending)
{
struct selinfo *sel = context;
selwakeup(sel);
}


then selinfo/task structures for every channel:

...
/* thingy for select stuff */
struct selinfo sel;
struct task selwakup_task;
...


which should be initialized during channel allocation:

...
TASK_INIT(&chan->selwakup_task, 0,
handle_selwakeup, &chan->sel);
...


and used anytime we'd like to call selwakeup:

...
taskqueue_enqueue_fast(taskqueue_fast,
&timer->selwakup_task);
...

Tuesday, November 20, 2007

Module/kernel parameters

Sometimes it's desirable to pass some arguments to module to customize its behavior, e.g.: to set/unset verbosity level of debug output, set operation mode etc... Linux modules can get this information from insmod utility, but kldload is not capable of doing such kind of things. What a pity. But don't get desperate - tunables to the rescue!

Just like an ordinary command shell (bash, csh, sh) kernel has its own environment, the set of <name, value> pairs. You can get, set, test, unset these variables using getenv, setenv, testenv, unsetenv functions in the kernel and kenv(2) syscall or kenv(1) command in userland. So if you want to set verbosity level for module, you would do something like this:


# kenv zaptel.debug=1
# kldload ./zaptel.ko

and then in module initialization routine:

static int debug = 0; /* Hush-hush */
...
char * value = getenv("zaptel.debug");
if (value) {
debug = strtol(value, NULL, 10);
freeenv(value);
}

Too much code for such a simple task, don't you think? Yeah, like for every common task there are useful macroses defined in kernel headers, you should just find them. Heavy coffeinated kernel hackers knew most of them. In this particular case neat code would look something like that (for statically initialized variable):

static int debug = 0; /* Hush-hush */
TUNABLE_INT("zaptel.debug", &debug);

or, if debug is the member of structure or local variable:

sc->debug = 0;
TUNABLE_INT_FETCH("zaptel.debug", &sc->debug);


TUNABLE_XXX macroses exist for INT, LONG, ULONG and STR types. TUNABLE_STR unlike the others requires third parameter - maximum size of the string. For a dynamic value retrieval every type has TUNABLE_XXX_FETCH macro defined. Nota bene: if there is no environent variable set with requested name, the value of acceptor variable remains untouched.