diff options
author | Jan Harkes <jaharkes@cs.cmu.edu> | 2007-07-19 01:48:46 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-19 10:04:48 -0700 |
commit | d9664c95afe5baa92ea56eff6a1c18e7b7a2cbe7 (patch) | |
tree | 642019723c5ec027aabca8ce872468babc9cfacd /fs/coda/upcall.c | |
parent | fe71b5f3871af2c281a08acd4bedd2da25e46bc3 (diff) |
coda: block signals during upcall processing
We ignore signals for about 30 seconds to give userspace a chance to see the
upcall. As we did not block signals we ended up in a busy loop for the
remainder of the period when a signal is received.
Signed-off-by: Jan Harkes <jaharkes@cs.cmu.edu>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/coda/upcall.c')
-rw-r--r-- | fs/coda/upcall.c | 81 |
1 files changed, 60 insertions, 21 deletions
diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 44332efa841..ad65ee01790 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -638,42 +638,83 @@ int venus_statfs(struct dentry *dentry, struct kstatfs *sfs) /* * coda_upcall and coda_downcall routines. - * */ +static void block_signals(sigset_t *old) +{ + spin_lock_irq(¤t->sighand->siglock); + *old = current->blocked; + + sigfillset(¤t->blocked); + sigdelset(¤t->blocked, SIGKILL); + sigdelset(¤t->blocked, SIGSTOP); + sigdelset(¤t->blocked, SIGINT); + + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); +} + +static void unblock_signals(sigset_t *old) +{ + spin_lock_irq(¤t->sighand->siglock); + current->blocked = *old; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); +} + +/* Don't allow signals to interrupt the following upcalls before venus + * has seen them, + * - CODA_CLOSE or CODA_RELEASE upcall (to avoid reference count problems) + * - CODA_STORE (to avoid data loss) + */ +#define CODA_INTERRUPTIBLE(r) (!coda_hard && \ + (((r)->uc_opcode != CODA_CLOSE && \ + (r)->uc_opcode != CODA_STORE && \ + (r)->uc_opcode != CODA_RELEASE) || \ + (r)->uc_flags & REQ_READ)) -static inline void coda_waitfor_upcall(struct upc_req *vmp) +static inline void coda_waitfor_upcall(struct upc_req *req) { DECLARE_WAITQUEUE(wait, current); + unsigned long timeout = jiffies + coda_timeout * HZ; + sigset_t old; + int blocked; - vmp->uc_posttime = jiffies; + block_signals(&old); + blocked = 1; - add_wait_queue(&vmp->uc_sleep, &wait); + add_wait_queue(&req->uc_sleep, &wait); for (;;) { - if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE ) + if (CODA_INTERRUPTIBLE(req)) set_current_state(TASK_INTERRUPTIBLE); else set_current_state(TASK_UNINTERRUPTIBLE); /* got a reply */ - if ( vmp->uc_flags & ( REQ_WRITE | REQ_ABORT ) ) + if (req->uc_flags & (REQ_WRITE | REQ_ABORT)) break; - if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE && signal_pending(current) ) { - /* if this process really wants to die, let it go */ - if ( sigismember(&(current->pending.signal), SIGKILL) || - sigismember(&(current->pending.signal), SIGINT) ) - break; - /* signal is present: after timeout always return - really smart idea, probably useless ... */ - if ( jiffies - vmp->uc_posttime > coda_timeout * HZ ) - break; + if (blocked && time_after(jiffies, timeout) && + CODA_INTERRUPTIBLE(req)) + { + unblock_signals(&old); + blocked = 0; } - schedule(); + + if (signal_pending(current)) { + list_del(&req->uc_chain); + break; + } + + if (blocked) + schedule_timeout(HZ); + else + schedule(); } - remove_wait_queue(&vmp->uc_sleep, &wait); - set_current_state(TASK_RUNNING); + if (blocked) + unblock_signals(&old); - return; + remove_wait_queue(&req->uc_sleep, &wait); + set_current_state(TASK_RUNNING); } @@ -750,8 +791,6 @@ static int coda_upcall(struct coda_sb_info *sbi, goto exit; } - list_del(&(req->uc_chain)); - /* Interrupted before venus read it. */ if (!(req->uc_flags & REQ_READ)) goto exit; |