Subject: Re: uses of CLRSIG()
To: None <tech-kern@netbsd.org>
From: Christos Zoulas <christos@astron.com>
List: tech-kern
Date: 04/02/2006 23:20:00
In article <20060402114818.GL29748@snowdrop.l8s.co.uk>,
David Laight  <david@l8s.co.uk> wrote:
>sys/signalvar.h has:
>#define CLRSIG(p, sig) sigdelset(&p->p_sigctx.ps_siglist, sig)
>sys/signal.h has:
>#define sigdelset(s, n) __sigdelset(s, n)
>sys/sigtypes.h has:
>#define __sigdelset(s, n) ((s)->__bits[__sigword(n)] &= ~__sigmask(n))
>#define __sigword(n) (((unsigned int)(n) - 1) >> 5)
>
>Note that this means that the 'sig' arg to CLRSIG() must not be zero, and
>is evaluted twice.
>
>CLRSIG() is used exactly twice [1], both as:
>	CLRSIG(p, CURSIG(l));
>CURSIG is defined in sys/signalvar.h as:
>#define CURSIG(l) (l->l_proc->p_sigctx.ps_sigcheck ? issignal(l) : 0)
>
>So it can easily return 0, and issignal() might return different values
>when called twice - it is quite a complex function.
>
>I don't know enough about the signal code to know what CLRSIG should
>actually do...
>
>	David
>
>[1] nfs/nfs_nqlease.c & ufs/mfs/mfs_vfsops.c

My understanding is that the CLRSIG() is supposed to clear the signal
that was sent to the syncer process to prevent it from being delivered
to the syncer process in case unmounting fails, so that the syncer process
does not die while the filesystem is still mounted. The typical scenario
is, the syncher process is tsleep()ing in the kernel, and waking up when
it needs to do work. If someone sends a signal to it, eg. kill -TERM
the mfs process, then the kernel will try to unmount the mfs filesystem
before delivering the signal to the process. If that unmount fails, then
we should not really kill the process because that will hang the mount.
So we call CLRSIG() to stop the signal from being delivered.

So the first call to issignal() will return the signal number that was
sent to the syncer process (unless someone malicious was able to send
a lower numbered signal between the time tsleep() returned and we called
issignal()... something that is not really easy to do). But you are
right, we should not be calling it many times as a side effect of this
macro. I would re-write CLRSIG(l) as:

#define CLRSIG(l)							\
    do {								\
	    int _sg;							\
	    while ((_sg = CURSIG(l)) != 0)				\	
		    sigdelset(&(l)->l_proc->p_sigctx.ps_siglist, _sg);	\
    } while (/*CONSTCOND*/0)

to clear all the signals, and make sure I am calling issignal() the appropriate
number of times.

christos