Whamcloud - gitweb
- added revalidating inode as Alex adviced before checking its size in ll_gns_mount_o...
[fs/lustre-release.git] / lustre / llite / llite_gns.c
index 7b8c3b9..d9affef 100644 (file)
@@ -50,6 +50,7 @@ ll_gns_wait_for_mount(struct dentry *dentry,
 {
         struct l_wait_info lwi;
         struct ll_sb_info *sbi;
+       int rc = 0;
         ENTRY;
 
         LASSERT(dentry != NULL);
@@ -57,16 +58,21 @@ ll_gns_wait_for_mount(struct dentry *dentry,
         sbi = ll_s2sbi(dentry->d_sb);
         
         lwi = LWI_TIMEOUT(timeout * HZ, NULL, NULL);
-        for (; !d_mountpoint(dentry) && tries > 0; tries--)
+        for (; !d_mountpoint(dentry) && tries > 0; tries--) {
                 l_wait_event(sbi->ll_gns_waitq, d_mountpoint(dentry), &lwi);
-
-        if (d_mountpoint(dentry)) {
-                spin_lock(&sbi->ll_gns_lock);
-                sbi->ll_gns_state = LL_GNS_FINISHED;
-                spin_unlock(&sbi->ll_gns_lock);
-                RETURN(0);
-        }
-        RETURN(-ETIME);
+               if (signal_pending(current))
+                       GOTO(out, rc = -EINTR);
+       }
+
+        if (!d_mountpoint(dentry))
+               rc = -ETIME;
+       
+       EXIT;
+out:   
+        spin_lock(&sbi->ll_gns_lock);
+        sbi->ll_gns_state = LL_GNS_FINISHED;
+        spin_unlock(&sbi->ll_gns_lock);
+        return rc;
 }
 
 /*
@@ -77,11 +83,11 @@ ll_gns_wait_for_mount(struct dentry *dentry,
 int
 ll_gns_mount_object(struct dentry *dentry, struct vfsmount *mnt)
 {
-        char *path, *pathpage, *datapage, *argv[4];
+        char *path, *pathpage, *datapage = NULL, *argv[4];
         struct file *mntinfo_fd = NULL;
         int cleanup_phase = 0, rc = 0;
         struct ll_sb_info *sbi;
-        struct dentry *dchild;
+        struct dentry *dchild = NULL;
         ENTRY;
 
         LASSERT(dentry->d_inode != NULL);
@@ -90,41 +96,71 @@ ll_gns_mount_object(struct dentry *dentry, struct vfsmount *mnt)
                 RETURN(-EINVAL);
 
         sbi = ll_i2sbi(dentry->d_inode);
-        LASSERT(sbi != NULL);
-
-        spin_lock(&sbi->ll_gns_lock);
-
-        if (sbi->ll_gns_state == LL_GNS_DISABLED) {
-                spin_unlock(&sbi->ll_gns_lock);
+        
+        if (mnt == NULL) {
+                CERROR("suid directory found, but no "
+                       "vfsmount available.\n");
                 RETURN(-EINVAL);
         }
-        
+
+        if (atomic_read(&sbi->ll_gns_enabled) == 0)
+                RETURN(-EINVAL);
+
+        spin_lock(&sbi->ll_gns_lock);
+       
         /* 
          * another thead is in progress or just finished mounting the
          * dentry. Handling that.
          */
-        if (sbi->ll_gns_state == LL_GNS_MOUNTING ||
-            sbi->ll_gns_state == LL_GNS_FINISHED) {
+        if (sbi->ll_gns_state != LL_GNS_IDLE) {
                 /* 
-                 * check if another thread is trying to mount some GNS dentry
-                 * too. Letting it know that we busy and make ll_lookup_it() to
-                 * restart syscall and try again later.
+                 * another thread is trying to mount GNS dentry. We'd like to
+                 * handling that.
                  */
                 spin_unlock(&sbi->ll_gns_lock);
-                RETURN(-EAGAIN);
-        }
-        LASSERT(sbi->ll_gns_state == LL_GNS_IDLE);
 
-        if (mnt == NULL) {
-                CERROR("suid directory found, but no "
-                       "vfsmount available.\n");
-                RETURN(-EINVAL);
-        }
+        restart:
+                /* 
+                 * check if dentry is mount point already, if so, do not restart
+                 * syscal.
+                 */
+                if (d_mountpoint(dentry))
+                        RETURN(0);
+
+                spin_lock(&sbi->ll_gns_lock);
+               if (sbi->ll_gns_pending_dentry && 
+                   is_subdir(sbi->ll_gns_pending_dentry, dentry)) {
+                       spin_unlock(&sbi->ll_gns_lock);
+                       RETURN(-EAGAIN);
+               }
+                spin_unlock(&sbi->ll_gns_lock);
 
+                /* 
+                 * 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);
+
+                /* 
+                 * check for he case when there are few waiters and all they are
+                 * awakened, but only one will find GNS state LL_GNS_IDLE, and
+                 * the rest will face with LL_GNS_MOUNTING.  --umka
+                 */
+                spin_lock(&sbi->ll_gns_lock);
+                if (sbi->ll_gns_state != LL_GNS_IDLE) {
+                        spin_unlock(&sbi->ll_gns_lock);
+                        goto restart;
+                }
+                spin_unlock(&sbi->ll_gns_lock);
+        }
+        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;
+       sbi->ll_gns_pending_dentry = dentry;
         spin_unlock(&sbi->ll_gns_lock);
 
         /* we need to build an absolute pathname to pass to mount */
@@ -137,8 +173,8 @@ ll_gns_mount_object(struct dentry *dentry, struct vfsmount *mnt)
         path = d_path(dentry, mnt, pathpage, PAGE_SIZE);
         if (IS_ERR(path)) {
                 CERROR("can't build mount object path, err %d\n",
-                       (int)PTR_ERR(dchild));
-                GOTO(cleanup, rc = PTR_ERR(dchild));
+                       (int)PTR_ERR(path));
+                GOTO(cleanup, rc = PTR_ERR(path));
         }
 
         /* synchronizing with possible /proc/fs/...write */
@@ -152,15 +188,12 @@ ll_gns_mount_object(struct dentry *dentry, struct vfsmount *mnt)
         /* 
          * 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.
+         * to ->lookup() and this is checked in ll_lookup_it().
          */
         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);
                 CERROR("can't find mount object %*s/%*s err = %d.\n",
@@ -171,26 +204,39 @@ ll_gns_mount_object(struct dentry *dentry, struct vfsmount *mnt)
         }
 
         /* mount object is not found */
-        if (!dchild->d_inode)
+        if (!dchild->d_inode) {
+                dput(dchild);
                 GOTO(cleanup, rc = -ENOENT);
+        }
 
         /* check if found child is regular file */
-        if (!S_ISREG(dchild->d_inode->i_mode))
-                GOTO(cleanup, rc = -EOPNOTSUPP);
-
-        mntget(mnt);
+        if (!S_ISREG(dchild->d_inode->i_mode)) {
+                dput(dchild);
+                GOTO(cleanup, rc = -EBADF);
+        }
 
         /* ok, mount object if found, opening it. */
-        mntinfo_fd = dentry_open(dchild, mnt, 0);
+        mntinfo_fd = dentry_open(dchild, mntget(mnt), 0);
         if (IS_ERR(mntinfo_fd)) {
                 CERROR("can't open 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,
                        (int)PTR_ERR(mntinfo_fd));
                 mntput(mnt);
+                dput(dchild);
                 GOTO(cleanup, rc = PTR_ERR(mntinfo_fd));
         }
-        cleanup_phase = 3;
+        cleanup_phase = 2;
+
+        /* make sure that inode size is up-to-date */
+        rc = ll_inode_revalidate_it(mntinfo_fd->f_dentry);
+        if (rc < 0) {
+                CERROR("can't revalidate 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,
+                       rc);
+                GOTO(cleanup, rc);
+        }
 
         if (mntinfo_fd->f_dentry->d_inode->i_size > PAGE_SIZE - 1) {
                 CERROR("mount object %*s/%*s is too big (%Ld)\n",
@@ -204,7 +250,7 @@ ll_gns_mount_object(struct dentry *dentry, struct vfsmount *mnt)
         if (!datapage)
                 GOTO(cleanup, rc = -ENOMEM);
 
-        cleanup_phase = 4;
+        cleanup_phase = 3;
         
         /* read data from mount object. */
         rc = kernel_read(mntinfo_fd, 0, datapage, PAGE_SIZE - 1);
@@ -243,28 +289,25 @@ 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 needed, 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);
-        if (rc == 0) {
+        LASSERT(sbi->ll_gns_state == LL_GNS_FINISHED);
+       
+       /* checking for mount point anyway to not loss mounts */
+        if (d_mountpoint(dentry)) {
                 struct dentry *rdentry;
                 struct vfsmount *rmnt;
-                
-                /* mount is successful */
-                LASSERT(sbi->ll_gns_state == LL_GNS_FINISHED);
-
+               
                 rmnt = mntget(mnt);
                 rdentry = dget(dentry);
                 
@@ -285,6 +328,8 @@ ll_gns_mount_object(struct dentry *dentry, struct vfsmount *mnt)
                         mntput(mnt);
                         dput(dentry);
                 }
+               
+               rc = 0;
         } else {
                 CERROR("usermode upcall %s failed to mount %s, err %d\n",
                        sbi->ll_gns_upcall, path, rc);
@@ -293,20 +338,24 @@ ll_gns_mount_object(struct dentry *dentry, struct vfsmount *mnt)
         EXIT;
 cleanup:
         switch (cleanup_phase) {
-        case 4:
-                free_page((unsigned long)datapage);
         case 3:
-                if (mntinfo_fd != NULL)
-                        fput(mntinfo_fd);
+                free_page((unsigned long)datapage);
         case 2:
-                if (dchild != NULL)
-                        dput(dchild);
+                if (mntinfo_fd != NULL) {
+                        fput(mntinfo_fd);
+                        dchild = NULL;
+                }
         case 1:
                 free_page((unsigned long)pathpage);
         case 0:
                 spin_lock(&sbi->ll_gns_lock);
                 sbi->ll_gns_state = LL_GNS_IDLE;
+               sbi->ll_gns_pending_dentry = NULL;
                 spin_unlock(&sbi->ll_gns_lock);
+
+                /* waking up all waiters after GNS state is LL_GNS_IDLE */
+                complete_all(&sbi->ll_gns_mount_finished);
+                init_completion(&sbi->ll_gns_mount_finished);
         }
         return rc;
 }
@@ -414,7 +463,7 @@ static int inline ll_gns_check_stop(void)
 }
 
 /* GNS control thread function. */
-static int ll_gns_thread_main(void *arg)
+static int ll_gns_thread(void *arg)
 {
         struct ll_gns_ctl *ctl = arg;
         unsigned long flags;
@@ -483,7 +532,7 @@ void ll_gns_del_timer(struct ll_sb_info *sbi)
  * starts GNS control thread and waits for a signal it is up and work may be
  * continued.
  */
-int ll_gns_start_thread(void)
+int ll_gns_thread_start(void)
 {
         int rc;
         ENTRY;
@@ -493,7 +542,7 @@ int ll_gns_start_thread(void)
         init_completion(&gns_ctl.gc_finishing);
         init_waitqueue_head(&gns_thread.t_ctl_waitq);
         
-        rc = kernel_thread(ll_gns_thread_main, &gns_ctl,
+        rc = kernel_thread(ll_gns_thread, &gns_ctl,
                            (CLONE_VM | CLONE_FILES));
         if (rc < 0) {
                 CERROR("cannot start GNS control thread, "
@@ -506,7 +555,7 @@ int ll_gns_start_thread(void)
 }
 
 /* stops GNS control thread and waits its actual stop. */
-void ll_gns_stop_thread(void)
+void ll_gns_thread_stop(void)
 {
         ENTRY;
         gns_thread.t_flags = SVC_STOPPING;