nfsd: track last inode only in use_wgather case
[safe/jmp/linux-2.6] / fs / nfsd / vfs.c
index 46e6bd2..ebf56c6 100644 (file)
@@ -975,6 +975,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
        __be32                  err = 0;
        int                     host_err;
        int                     stable = *stablep;
+       int                     use_wgather;
 
 #ifdef MSNFS
        err = nfserr_perm;
@@ -993,9 +994,10 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
         *  -   the sync export option has been set, or
         *  -   the client requested O_SYNC behavior (NFSv3 feature).
         *  -   The file system doesn't support fsync().
-        * When gathered writes have been configured for this volume,
+        * When NFSv2 gathered writes have been configured for this volume,
         * flushing the data to disk is handled separately below.
         */
+       use_wgather = (rqstp->rq_vers == 2) && EX_WGATHER(exp);
 
        if (!file->f_op->fsync) {/* COMMIT3 cannot work */
               stable = 2;
@@ -1004,7 +1006,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
 
        if (!EX_ISSYNC(exp))
                stable = 0;
-       if (stable && !EX_WGATHER(exp)) {
+       if (stable && !use_wgather) {
                spin_lock(&file->f_lock);
                file->f_flags |= O_SYNC;
                spin_unlock(&file->f_lock);
@@ -1015,6 +1017,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
        host_err = vfs_writev(file, (struct iovec __user *)vec, vlen, &offset);
        set_fs(oldfs);
        if (host_err >= 0) {
+               *cnt = host_err;
                nfsdstats.io_write += host_err;
                fsnotify_modify(file->f_path.dentry);
        }
@@ -1023,7 +1026,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
        if (host_err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID)))
                kill_suid(dentry);
 
-       if (host_err >= 0 && stable) {
+       if (host_err >= 0 && stable && use_wgather) {
                static ino_t    last_ino;
                static dev_t    last_dev;
 
@@ -1039,31 +1042,25 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
                 * nice and simple solution (IMHO), and it seems to
                 * work:-)
                 */
-               if (EX_WGATHER(exp)) {
-                       if (atomic_read(&inode->i_writecount) > 1
-                           || (last_ino == inode->i_ino && last_dev == inode->i_sb->s_dev)) {
-                               dprintk("nfsd: write defer %d\n", task_pid_nr(current));
-                               msleep(10);
-                               dprintk("nfsd: write resume %d\n", task_pid_nr(current));
-                       }
+               if (atomic_read(&inode->i_writecount) > 1
+                   || (last_ino == inode->i_ino && last_dev == inode->i_sb->s_dev)) {
+                       dprintk("nfsd: write defer %d\n", task_pid_nr(current));
+                       msleep(10);
+                       dprintk("nfsd: write resume %d\n", task_pid_nr(current));
+               }
 
-                       if (inode->i_state & I_DIRTY) {
-                               dprintk("nfsd: write sync %d\n", task_pid_nr(current));
-                               host_err=nfsd_sync(file);
-                       }
-#if 0
-                       wake_up(&inode->i_wait);
-#endif
+               if (inode->i_state & I_DIRTY) {
+                       dprintk("nfsd: write sync %d\n", task_pid_nr(current));
+                       host_err=nfsd_sync(file);
                }
                last_ino = inode->i_ino;
                last_dev = inode->i_sb->s_dev;
        }
 
        dprintk("nfsd: write complete host_err=%d\n", host_err);
-       if (host_err >= 0) {
+       if (host_err >= 0)
                err = 0;
-               *cnt = host_err;
-       } else
+       else
                err = nfserrno(host_err);
 out:
        return err;
@@ -1890,8 +1887,8 @@ static int nfsd_buffered_filldir(void *__buf, const char *name, int namlen,
        return 0;
 }
 
-static int nfsd_buffered_readdir(struct file *file, filldir_t func,
-                                struct readdir_cd *cdp, loff_t *offsetp)
+static __be32 nfsd_buffered_readdir(struct file *file, filldir_t func,
+                                   struct readdir_cd *cdp, loff_t *offsetp)
 {
        struct readdir_data buf;
        struct buffered_dirent *de;
@@ -1901,11 +1898,12 @@ static int nfsd_buffered_readdir(struct file *file, filldir_t func,
 
        buf.dirent = (void *)__get_free_page(GFP_KERNEL);
        if (!buf.dirent)
-               return -ENOMEM;
+               return nfserrno(-ENOMEM);
 
        offset = *offsetp;
 
        while (1) {
+               struct inode *dir_inode = file->f_path.dentry->d_inode;
                unsigned int reclen;
 
                cdp->err = nfserr_eof; /* will be cleared on successful read */
@@ -1924,26 +1922,38 @@ static int nfsd_buffered_readdir(struct file *file, filldir_t func,
                if (!size)
                        break;
 
+               /*
+                * Various filldir functions may end up calling back into
+                * lookup_one_len() and the file system's ->lookup() method.
+                * These expect i_mutex to be held, as it would within readdir.
+                */
+               host_err = mutex_lock_killable(&dir_inode->i_mutex);
+               if (host_err)
+                       break;
+
                de = (struct buffered_dirent *)buf.dirent;
                while (size > 0) {
                        offset = de->offset;
 
                        if (func(cdp, de->name, de->namlen, de->offset,
                                 de->ino, de->d_type))
-                               goto done;
+                               break;
 
                        if (cdp->err != nfs_ok)
-                               goto done;
+                               break;
 
                        reclen = ALIGN(sizeof(*de) + de->namlen,
                                       sizeof(u64));
                        size -= reclen;
                        de = (struct buffered_dirent *)((char *)de + reclen);
                }
+               mutex_unlock(&dir_inode->i_mutex);
+               if (size > 0) /* We bailed out early */
+                       break;
+
                offset = vfs_llseek(file, 0, SEEK_CUR);
        }
 
- done:
        free_page((unsigned long)(buf.dirent));
 
        if (host_err)