diff options
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 44332efa8411..ad65ee01790f 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; |