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>
diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c
index 44332ef..ad65ee0 100644
--- a/fs/coda/upcall.c
+++ b/fs/coda/upcall.c
@@ -638,42 +638,83 @@
/*
* coda_upcall and coda_downcall routines.
- *
*/
+static void block_signals(sigset_t *old)
+{
+ spin_lock_irq(¤t->sighand->siglock);
+ *old = current->blocked;
-static inline void coda_waitfor_upcall(struct upc_req *vmp)
+ 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 *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();
- }
- remove_wait_queue(&vmp->uc_sleep, &wait);
- set_current_state(TASK_RUNNING);
- return;
+ if (signal_pending(current)) {
+ list_del(&req->uc_chain);
+ break;
+ }
+
+ if (blocked)
+ schedule_timeout(HZ);
+ else
+ schedule();
+ }
+ if (blocked)
+ unblock_signals(&old);
+
+ remove_wait_queue(&req->uc_sleep, &wait);
+ set_current_state(TASK_RUNNING);
}
@@ -750,8 +791,6 @@
goto exit;
}
- list_del(&(req->uc_chain));
-
/* Interrupted before venus read it. */
if (!(req->uc_flags & REQ_READ))
goto exit;
diff --git a/include/linux/coda_psdev.h b/include/linux/coda_psdev.h
index b541bb3..f28c2f7 100644
--- a/include/linux/coda_psdev.h
+++ b/include/linux/coda_psdev.h
@@ -85,7 +85,6 @@
u_short uc_opcode; /* copied from data to save lookup */
int uc_unique;
wait_queue_head_t uc_sleep; /* process' wait queue */
- unsigned long uc_posttime;
};
#define REQ_ASYNC 0x1