Whamcloud - gitweb
- using TASK_INTERRUPTIBLE in OBD_SLEEP_ON at Andreas' suggestion.
[fs/lustre-release.git] / lustre / llite / llite_gns.c
index 5c711b2..ab076db 100644 (file)
@@ -40,6 +40,8 @@ static spinlock_t gns_lock = SPIN_LOCK_UNLOCKED;
 static struct ptlrpc_thread gns_thread;
 static struct ll_gns_ctl gns_ctl;
 
+#define CONCUR_GNS_RESTART_APPROACH 0
+
 /*
  * waits until passed dentry gets mountpoint or timeout and attempts are
  * exhausted. Returns 1 if dentry became mountpoint and 0 otherwise.
@@ -69,6 +71,26 @@ ll_gns_wait_for_mount(struct dentry *dentry,
         RETURN(-ETIME);
 }
 
+#if (CONCUR_GNS_RESTART_APPROACH == 1)
+/* 
+ * sending a signal known to be ignored to cause restarting syscall if GNS mount
+ * function returns -ERESTARTSYS.
+ */
+static void
+ll_gns_send_signal(void)
+{
+        struct task_struct *task = current;
+        int signal = SIGCONT;
+
+        read_lock(&tasklist_lock);
+        spin_lock_irq(&task->sighand->siglock);
+        sigaddset(&task->pending.signal, signal);
+        spin_unlock_irq(&task->sighand->siglock);
+        read_unlock(&tasklist_lock);
+        set_tsk_thread_flag(task, TIF_SIGPENDING);
+}
+#endif
+
 /*
  * tries to mount the mount object under passed @dentry. In the case of success
  * @dentry will become mount point and 0 will be returned. Error code will be
@@ -77,7 +99,6 @@ ll_gns_wait_for_mount(struct dentry *dentry,
 int
 ll_gns_mount_object(struct dentry *dentry, struct vfsmount *mnt)
 {
-        struct ll_dentry_data *lld = dentry->d_fsdata;
         char *path, *pathpage, *datapage, *argv[4];
         struct file *mntinfo_fd = NULL;
         int cleanup_phase = 0, rc = 0;
@@ -85,36 +106,64 @@ ll_gns_mount_object(struct dentry *dentry, struct vfsmount *mnt)
         struct dentry *dchild;
         ENTRY;
 
+        LASSERT(dentry->d_inode != NULL);
+
+        if (!S_ISDIR(dentry->d_inode->i_mode))
+                RETURN(-EINVAL);
+
+        sbi = ll_i2sbi(dentry->d_inode);
+        
         if (mnt == NULL) {
                 CERROR("suid directory found, but no "
                        "vfsmount available.\n");
                 RETURN(-EINVAL);
         }
 
-        CDEBUG(D_INODE, "mounting dentry %p\n", dentry);
+        if (atomic_read(&sbi->ll_gns_enabled) == 0)
+                RETURN(-EINVAL);
 
-        LASSERT(dentry->d_inode != NULL);
-        LASSERT(S_ISDIR(dentry->d_inode->i_mode));
-        LASSERT(lld != NULL);
-        
-        sbi = ll_i2sbi(dentry->d_inode);
-        LASSERT(sbi != NULL);
+        spin_lock(&sbi->ll_gns_lock);
 
         /* 
          * another thead is in progress or just finished mounting the
          * dentry. Handling that.
          */
-        spin_lock(&sbi->ll_gns_lock);
         if (sbi->ll_gns_state == LL_GNS_MOUNTING ||
             sbi->ll_gns_state == LL_GNS_FINISHED) {
+                /* 
+                 * another thread is trying to mount GNS dentry. We'd like to
+                 * handling that.
+                 */
                 spin_unlock(&sbi->ll_gns_lock);
-                CDEBUG(D_INODE, "GNS is in progress now, throwing "
-                       "-ERESTARTSYS to restart syscall and let "
-                       "it finish.\n");
+
+                /* 
+                 * check if dentry is mount point already, if so, do not restart
+                 * syscal.
+                 */
+                if (d_mountpoint(dentry))
+                        RETURN(0);
+
+#if (CONCUR_GNS_RESTART_APPROACH == 1)
+                /* 
+                 * causing syscall to restart and possibly find this dentry
+                 * already mounted.
+                 */
+                ll_gns_send_signal();
                 RETURN(-ERESTARTSYS);
+#else
+                /* 
+                 * waiting for GNS complete and check dentry again, it may be
+                 * mounted already.
+                 */
+                wait_for_completion(&sbi->ll_gns_mount_finished);
+                if (d_mountpoint(dentry))
+                        RETURN(0);
+#endif
         }
         LASSERT(sbi->ll_gns_state == LL_GNS_IDLE);
 
+        CDEBUG(D_INODE, "mounting dentry %p\n", dentry);
+
         /* mounting started */
         sbi->ll_gns_state = LL_GNS_MOUNTING;
         spin_unlock(&sbi->ll_gns_lock);
@@ -140,22 +189,21 @@ ll_gns_mount_object(struct dentry *dentry, struct vfsmount *mnt)
          * mount object name is taken from sbi, where it is set in mount time or
          * via /proc/fs... tunable. It may be ".mntinfo" or so.
          */
-        dchild = lookup_one_len(sbi->ll_gns_oname, dentry,
-                                strlen(sbi->ll_gns_oname));
+
+        /* 
+         * recursive lookup with trying to mount SUID bit marked directories on
+         * the way is not possible here, as lookup_one_len() does not pass @nd
+         * to ->lookup() and this is checked in ll_lookup_it(). So, do not
+         * handle possible -EAGAIN here.
+         */
+        dchild = ll_lookup_one_len(sbi->ll_gns_oname, dentry,
+                                   strlen(sbi->ll_gns_oname));
         up(&sbi->ll_gns_sem);
 
         cleanup_phase = 2;
         
         if (IS_ERR(dchild)) {
                 rc = PTR_ERR(dchild);
-                
-                if (rc == -ERESTARTSYS) {
-                        CDEBUG(D_INODE, "possible endless loop is detected "
-                               "due to mount object is directory marked by "
-                               "SUID bit.\n");
-                        GOTO(cleanup, rc = -ELOOP);
-                }
-
                 CERROR("can't find mount object %*s/%*s err = %d.\n",
                        (int)dentry->d_name.len, dentry->d_name.name,
                        strlen(sbi->ll_gns_oname), sbi->ll_gns_oname,
@@ -169,7 +217,7 @@ ll_gns_mount_object(struct dentry *dentry, struct vfsmount *mnt)
 
         /* check if found child is regular file */
         if (!S_ISREG(dchild->d_inode->i_mode))
-                GOTO(cleanup, rc = -EOPNOTSUPP);
+                GOTO(cleanup, rc = -EBADF);
 
         mntget(mnt);
 
@@ -209,6 +257,14 @@ ll_gns_mount_object(struct dentry *dentry, struct vfsmount *mnt)
                 GOTO(cleanup, rc);
         }
 
+        /* no data in mount object? */
+        if (rc == 0) {
+                CERROR("mount object %*s/%*s is empty?\n",
+                       (int)dentry->d_name.len, dentry->d_name.name,
+                       strlen(sbi->ll_gns_oname), sbi->ll_gns_oname);
+                GOTO(cleanup, rc);
+        }
+
         datapage[rc] = '\0';
         fput(mntinfo_fd);
         mntinfo_fd = NULL;
@@ -228,27 +284,22 @@ ll_gns_mount_object(struct dentry *dentry, struct vfsmount *mnt)
         
         up(&sbi->ll_gns_sem);
 
-        rc = USERMODEHELPER(argv[0], argv, NULL);
+        /* do not wait for helper complete here. */
+        rc = call_usermodehelper(argv[0], argv, NULL, 1);
         if (rc) {
-                CERROR("failed to call GNS upcall %s, err = %d\n",
-                       sbi->ll_gns_upcall, rc);
-                GOTO(cleanup, rc);
+                CWARN("failed to call GNS upcall %s, err = %d, "
+                      "checking for mount anyway\n", sbi->ll_gns_upcall, rc);
         }
 
         /*
-         * wait for mount completion. This is actually not need, because
-         * USERMODEHELPER() returns only when usermode process finishes. But we
-         * doing this just for case USERMODEHELPER() semantics will be changed
-         * or usermode upcall program will start mounting in backgound and
-         * return instantly. --umka
+         * waiting for dentry become mount point GNS_WAIT_ATTEMPTS times by 1
+         * second.
          */
         rc = ll_gns_wait_for_mount(dentry, 1, GNS_WAIT_ATTEMPTS);
-        complete_all(&sbi->ll_gns_mount_finished);
         if (rc == 0) {
                 struct dentry *rdentry;
                 struct vfsmount *rmnt;
-                
-                /* mount is successful */
+               
                 LASSERT(sbi->ll_gns_state == LL_GNS_FINISHED);
 
                 rmnt = mntget(mnt);
@@ -289,16 +340,11 @@ cleanup:
                         dput(dchild);
         case 1:
                 free_page((unsigned long)pathpage);
-                
-                /* 
-                 * waking up all waiters after gns state is set to
-                 * LL_GNS_MOUNTING
-                 */
-                complete_all(&sbi->ll_gns_mount_finished);
         case 0:
                 spin_lock(&sbi->ll_gns_lock);
                 sbi->ll_gns_state = LL_GNS_IDLE;
                 spin_unlock(&sbi->ll_gns_lock);
+                complete_all(&sbi->ll_gns_mount_finished);
         }
         return rc;
 }