An Oracle blog about Solaris

ISSIG() How do I use it?

Chris Beal
Senior Principal Software Engineer
As indicated in this blog entry a fix I did for a bug showed up some interesting and rather hard to diagnose problems. These showed up as the getting EACCES (permission denied) errors when trying to cd or read an automounted directory.
Now when you use dtrace to work back through what has happened you find that Using truss and dtrace it was possible to see that the program that was getting EACCES was doing an open on the directory which ended up in auto_wait4mount(). It is this call that returns EACCES.
At around the same time the automountd process gets an interrupted system call (through a variety of calling paths but usually from nfs4_mount() > eventually calling > nfs4secinfo_otw() > nfs4_rfscall() which returns EINTR indicating it's getting something like a signal. This includes watchpoint activity and fork1 requests. In this case it is a fork1 request so the thread requesting the stop is in the same processes, so ISSIG(JUSTLOOKING) returns true even if lwp_nostop is set. (this was the fix for 4522909). If we look in nfs4_rfscall() at the following section
   1334                 /\*
1335 \* If there is a current signal, then don't bother
1336 \* even trying to send out the request because we
1337 \* won't be able to block waiting for the response.
1338 \* Simply assume RPC_INTR and get on with it.
1339 \*/
1340 if (ttolwp(curthread) != NULL && ISSIG(curthread, JUSTLOOKING))
1341 status = RPC_INTR;
1342 else {
1343 status = CLNT_CALL(client, which, xdrargs, argsp,
1344 xdrres, resp, wait);
1345 }

Here we look to see if there is a signal pending using ISSIG(curthread, JUSTLOOKING) to optimise out a CLNT_CALL() if its not needed. If so we return RPC_INTR (a little further down).
The assumption is that if you have any signal like activity you need to return to userland to handle the signal. This is not the case for fork1() you can simply wait till you start running again and carry on. ISSIG(t, FORREAL) could be used to check if there is a real need to return to userland. The trouble is you need to drop all your locks before calling it. So you then have to reaquire the locks later. This may require you restart the rfscall operation.
Also if you do a forkall() (ie a normal fork() systemcall) you do need to return to userland with EINTR, same with some /proc activity. So it's probably worth checking for that prior to calling the issig(FORREAL). A good example of how to do this is in cv_wait_sig().
So an example of how to correctley check for signal delivery in a system call (say if you are going to do something that takes a long time and don'r want to waste that activity if it's going to be interrupted) would be.
 .        if (lwp != NULL &&
. ... drop all of your locks! ...
. if (ISSIG(t, FORREAL) ||
. lwp->lwp_sysabort ||
. MUSTRETURN(p, t)) {
. lwp->lwp_sysabort = 0;
. return (set_errno(EINTR));
. }
. return (set_errno(ERESTART));
. }

I'll be applying this approach to nfs shortly
Technorati Tags:

Be the first to comment

Comments ( 0 )