]> err.no Git - linux-2.6/commitdiff
coda: block signals during upcall processing
authorJan Harkes <jaharkes@cs.cmu.edu>
Thu, 19 Jul 2007 08:48:46 +0000 (01:48 -0700)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Thu, 19 Jul 2007 17:04:48 +0000 (10:04 -0700)
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>
fs/coda/upcall.c
include/linux/coda_psdev.h

index 44332efa841171155afe21a4fd127a09102ffe47..ad65ee01790f172e4fb50720c592814b1a06e876 100644 (file)
@@ -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(&current->sighand->siglock);
+       *old = current->blocked;
+
+       sigfillset(&current->blocked);
+       sigdelset(&current->blocked, SIGKILL);
+       sigdelset(&current->blocked, SIGSTOP);
+       sigdelset(&current->blocked, SIGINT);
+
+       recalc_sigpending();
+       spin_unlock_irq(&current->sighand->siglock);
+}
+
+static void unblock_signals(sigset_t *old)
+{
+       spin_lock_irq(&current->sighand->siglock);
+       current->blocked = *old;
+       recalc_sigpending();
+       spin_unlock_irq(&current->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;
index b541bb3d1f4bcf880d1244fc000a9d2a20c80c83..f28c2f7fd454cb950769845b7e84678e8c37fbd0 100644 (file)
@@ -85,7 +85,6 @@ struct upc_req {
        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