MIPS: bitops: ins start position is always an immediate
[linux-2.6-microblaze.git] / fs / nfsd / vfs.c
index 8593c64..bd0a385 100644 (file)
 
 #define NFSDDBG_FACILITY               NFSDDBG_FILEOP
 
-
-/*
- * This is a cache of readahead params that help us choose the proper
- * readahead strategy. Initially, we set all readahead parameters to 0
- * and let the VFS handle things.
- * If you increase the number of cached files very much, you'll need to
- * add a hash table here.
- */
-struct raparms {
-       struct raparms          *p_next;
-       unsigned int            p_count;
-       ino_t                   p_ino;
-       dev_t                   p_dev;
-       int                     p_set;
-       struct file_ra_state    p_ra;
-       unsigned int            p_hindex;
-};
-
-struct raparm_hbucket {
-       struct raparms          *pb_head;
-       spinlock_t              pb_lock;
-} ____cacheline_aligned_in_smp;
-
-#define RAPARM_HASH_BITS       4
-#define RAPARM_HASH_SIZE       (1<<RAPARM_HASH_BITS)
-#define RAPARM_HASH_MASK       (RAPARM_HASH_SIZE-1)
-static struct raparm_hbucket   raparm_hash[RAPARM_HASH_SIZE];
-
 /* 
  * Called from nfsd_lookup and encode_dirent. Check if we have crossed 
  * a mount point.
@@ -822,67 +794,6 @@ nfsd_open_verified(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
        return err;
 }
 
-
-
-struct raparms *
-nfsd_init_raparms(struct file *file)
-{
-       struct inode *inode = file_inode(file);
-       dev_t dev = inode->i_sb->s_dev;
-       ino_t ino = inode->i_ino;
-       struct raparms  *ra, **rap, **frap = NULL;
-       int depth = 0;
-       unsigned int hash;
-       struct raparm_hbucket *rab;
-
-       hash = jhash_2words(dev, ino, 0xfeedbeef) & RAPARM_HASH_MASK;
-       rab = &raparm_hash[hash];
-
-       spin_lock(&rab->pb_lock);
-       for (rap = &rab->pb_head; (ra = *rap); rap = &ra->p_next) {
-               if (ra->p_ino == ino && ra->p_dev == dev)
-                       goto found;
-               depth++;
-               if (ra->p_count == 0)
-                       frap = rap;
-       }
-       depth = nfsdstats.ra_size;
-       if (!frap) {    
-               spin_unlock(&rab->pb_lock);
-               return NULL;
-       }
-       rap = frap;
-       ra = *frap;
-       ra->p_dev = dev;
-       ra->p_ino = ino;
-       ra->p_set = 0;
-       ra->p_hindex = hash;
-found:
-       if (rap != &rab->pb_head) {
-               *rap = ra->p_next;
-               ra->p_next   = rab->pb_head;
-               rab->pb_head = ra;
-       }
-       ra->p_count++;
-       nfsdstats.ra_depth[depth*10/nfsdstats.ra_size]++;
-       spin_unlock(&rab->pb_lock);
-
-       if (ra->p_set)
-               file->f_ra = ra->p_ra;
-       return ra;
-}
-
-void nfsd_put_raparams(struct file *file, struct raparms *ra)
-{
-       struct raparm_hbucket *rab = &raparm_hash[ra->p_hindex];
-
-       spin_lock(&rab->pb_lock);
-       ra->p_ra = file->f_ra;
-       ra->p_set = 1;
-       ra->p_count--;
-       spin_unlock(&rab->pb_lock);
-}
-
 /*
  * Grab and keep cached pages associated with a file in the svc_rqst
  * so that they can be passed to the network sendmsg/sendpage routines
@@ -923,12 +834,23 @@ static int nfsd_direct_splice_actor(struct pipe_inode_info *pipe,
        return __splice_from_pipe(pipe, sd, nfsd_splice_actor);
 }
 
+static u32 nfsd_eof_on_read(struct file *file, loff_t offset, ssize_t len,
+               size_t expected)
+{
+       if (expected != 0 && len == 0)
+               return 1;
+       if (offset+len >= i_size_read(file_inode(file)))
+               return 1;
+       return 0;
+}
+
 static __be32 nfsd_finish_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
                               struct file *file, loff_t offset,
-                              unsigned long *count, int host_err)
+                              unsigned long *count, u32 *eof, ssize_t host_err)
 {
        if (host_err >= 0) {
                nfsdstats.io_read += host_err;
+               *eof = nfsd_eof_on_read(file, offset, host_err, *count);
                *count = host_err;
                fsnotify_access(file);
                trace_nfsd_read_io_done(rqstp, fhp, offset, *count);
@@ -940,7 +862,8 @@ static __be32 nfsd_finish_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
 }
 
 __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
-                       struct file *file, loff_t offset, unsigned long *count)
+                       struct file *file, loff_t offset, unsigned long *count,
+                       u32 *eof)
 {
        struct splice_desc sd = {
                .len            = 0,
@@ -948,25 +871,27 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
                .pos            = offset,
                .u.data         = rqstp,
        };
-       int host_err;
+       ssize_t host_err;
 
        trace_nfsd_read_splice(rqstp, fhp, offset, *count);
        rqstp->rq_next_page = rqstp->rq_respages + 1;
        host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor);
-       return nfsd_finish_read(rqstp, fhp, file, offset, count, host_err);
+       return nfsd_finish_read(rqstp, fhp, file, offset, count, eof, host_err);
 }
 
 __be32 nfsd_readv(struct svc_rqst *rqstp, struct svc_fh *fhp,
                  struct file *file, loff_t offset,
-                 struct kvec *vec, int vlen, unsigned long *count)
+                 struct kvec *vec, int vlen, unsigned long *count,
+                 u32 *eof)
 {
        struct iov_iter iter;
-       int host_err;
+       loff_t ppos = offset;
+       ssize_t host_err;
 
        trace_nfsd_read_vector(rqstp, fhp, offset, *count);
        iov_iter_kvec(&iter, READ, vec, vlen, *count);
-       host_err = vfs_iter_read(file, &iter, &offset, 0);
-       return nfsd_finish_read(rqstp, fhp, file, offset, count, host_err);
+       host_err = vfs_iter_read(file, &iter, &ppos, 0);
+       return nfsd_finish_read(rqstp, fhp, file, offset, count, eof, host_err);
 }
 
 /*
@@ -1047,8 +972,12 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
        nfsdstats.io_write += *cnt;
        fsnotify_modify(file);
 
-       if (stable && use_wgather)
+       if (stable && use_wgather) {
                host_err = wait_for_concurrent_writes(file);
+               if (host_err < 0)
+                       nfsd_reset_boot_verifier(net_generic(SVC_NET(rqstp),
+                                                nfsd_net_id));
+       }
 
 out_nfserr:
        if (host_err >= 0) {
@@ -1069,7 +998,8 @@ out_nfserr:
  * N.B. After this call fhp needs an fh_put
  */
 __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
-       loff_t offset, struct kvec *vec, int vlen, unsigned long *count)
+       loff_t offset, struct kvec *vec, int vlen, unsigned long *count,
+       u32 *eof)
 {
        struct nfsd_file        *nf;
        struct file *file;
@@ -1082,9 +1012,9 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
 
        file = nf->nf_file;
        if (file->f_op->splice_read && test_bit(RQ_SPLICE_OK, &rqstp->rq_flags))
-               err = nfsd_splice_read(rqstp, fhp, file, offset, count);
+               err = nfsd_splice_read(rqstp, fhp, file, offset, count, eof);
        else
-               err = nfsd_readv(rqstp, fhp, file, offset, vec, vlen, count);
+               err = nfsd_readv(rqstp, fhp, file, offset, vec, vlen, count, eof);
 
        nfsd_file_put(nf);
 
@@ -1133,9 +1063,9 @@ __be32
 nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
                loff_t offset, unsigned long count)
 {
-       struct file     *file;
-       loff_t          end = LLONG_MAX;
-       __be32          err = nfserr_inval;
+       struct nfsd_file        *nf;
+       loff_t                  end = LLONG_MAX;
+       __be32                  err = nfserr_inval;
 
        if (offset < 0)
                goto out;
@@ -1145,20 +1075,27 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
                        goto out;
        }
 
-       err = nfsd_open(rqstp, fhp, S_IFREG,
-                       NFSD_MAY_WRITE|NFSD_MAY_NOT_BREAK_LEASE, &file);
+       err = nfsd_file_acquire(rqstp, fhp,
+                       NFSD_MAY_WRITE|NFSD_MAY_NOT_BREAK_LEASE, &nf);
        if (err)
                goto out;
        if (EX_ISSYNC(fhp->fh_export)) {
-               int err2 = vfs_fsync_range(file, offset, end, 0);
+               int err2 = vfs_fsync_range(nf->nf_file, offset, end, 0);
 
-               if (err2 != -EINVAL)
-                       err = nfserrno(err2);
-               else
+               switch (err2) {
+               case 0:
+                       break;
+               case -EINVAL:
                        err = nfserr_notsupp;
+                       break;
+               default:
+                       err = nfserrno(err2);
+                       nfsd_reset_boot_verifier(net_generic(nf->nf_net,
+                                                nfsd_net_id));
+               }
        }
 
-       fput(file);
+       nfsd_file_put(nf);
 out:
        return err;
 }
@@ -1679,6 +1616,26 @@ out_nfserr:
        goto out_unlock;
 }
 
+static void
+nfsd_close_cached_files(struct dentry *dentry)
+{
+       struct inode *inode = d_inode(dentry);
+
+       if (inode && S_ISREG(inode->i_mode))
+               nfsd_file_close_inode_sync(inode);
+}
+
+static bool
+nfsd_has_cached_files(struct dentry *dentry)
+{
+       bool            ret = false;
+       struct inode *inode = d_inode(dentry);
+
+       if (inode && S_ISREG(inode->i_mode))
+               ret = nfsd_file_is_cached(inode);
+       return ret;
+}
+
 /*
  * Rename a file
  * N.B. After this call _both_ ffhp and tfhp need an fh_put
@@ -1691,6 +1648,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
        struct inode    *fdir, *tdir;
        __be32          err;
        int             host_err;
+       bool            has_cached = false;
 
        err = fh_verify(rqstp, ffhp, S_IFDIR, NFSD_MAY_REMOVE);
        if (err)
@@ -1709,6 +1667,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
        if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen))
                goto out;
 
+retry:
        host_err = fh_want_write(ffhp);
        if (host_err) {
                err = nfserrno(host_err);
@@ -1748,11 +1707,16 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
        if (ffhp->fh_export->ex_path.dentry != tfhp->fh_export->ex_path.dentry)
                goto out_dput_new;
 
-       host_err = vfs_rename(fdir, odentry, tdir, ndentry, NULL, 0);
-       if (!host_err) {
-               host_err = commit_metadata(tfhp);
-               if (!host_err)
-                       host_err = commit_metadata(ffhp);
+       if (nfsd_has_cached_files(ndentry)) {
+               has_cached = true;
+               goto out_dput_old;
+       } else {
+               host_err = vfs_rename(fdir, odentry, tdir, ndentry, NULL, 0);
+               if (!host_err) {
+                       host_err = commit_metadata(tfhp);
+                       if (!host_err)
+                               host_err = commit_metadata(ffhp);
+               }
        }
  out_dput_new:
        dput(ndentry);
@@ -1765,12 +1729,26 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
         * as that would do the wrong thing if the two directories
         * were the same, so again we do it by hand.
         */
-       fill_post_wcc(ffhp);
-       fill_post_wcc(tfhp);
+       if (!has_cached) {
+               fill_post_wcc(ffhp);
+               fill_post_wcc(tfhp);
+       }
        unlock_rename(tdentry, fdentry);
        ffhp->fh_locked = tfhp->fh_locked = false;
        fh_drop_write(ffhp);
 
+       /*
+        * If the target dentry has cached open files, then we need to try to
+        * close them prior to doing the rename. Flushing delayed fput
+        * shouldn't be done with locks held however, so we delay it until this
+        * point and then reattempt the whole shebang.
+        */
+       if (has_cached) {
+               has_cached = false;
+               nfsd_close_cached_files(ndentry);
+               dput(ndentry);
+               goto retry;
+       }
 out:
        return err;
 }
@@ -1817,10 +1795,13 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
        if (!type)
                type = d_inode(rdentry)->i_mode & S_IFMT;
 
-       if (type != S_IFDIR)
+       if (type != S_IFDIR) {
+               nfsd_close_cached_files(rdentry);
                host_err = vfs_unlink(dirp, rdentry, NULL);
-       else
+       } else {
                host_err = vfs_rmdir(dirp, rdentry);
+       }
+
        if (!host_err)
                host_err = commit_metadata(fhp);
        dput(rdentry);
@@ -2094,63 +2075,3 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
 
        return err? nfserrno(err) : 0;
 }
-
-void
-nfsd_racache_shutdown(void)
-{
-       struct raparms *raparm, *last_raparm;
-       unsigned int i;
-
-       dprintk("nfsd: freeing readahead buffers.\n");
-
-       for (i = 0; i < RAPARM_HASH_SIZE; i++) {
-               raparm = raparm_hash[i].pb_head;
-               while(raparm) {
-                       last_raparm = raparm;
-                       raparm = raparm->p_next;
-                       kfree(last_raparm);
-               }
-               raparm_hash[i].pb_head = NULL;
-       }
-}
-/*
- * Initialize readahead param cache
- */
-int
-nfsd_racache_init(int cache_size)
-{
-       int     i;
-       int     j = 0;
-       int     nperbucket;
-       struct raparms **raparm = NULL;
-
-
-       if (raparm_hash[0].pb_head)
-               return 0;
-       nperbucket = DIV_ROUND_UP(cache_size, RAPARM_HASH_SIZE);
-       nperbucket = max(2, nperbucket);
-       cache_size = nperbucket * RAPARM_HASH_SIZE;
-
-       dprintk("nfsd: allocating %d readahead buffers.\n", cache_size);
-
-       for (i = 0; i < RAPARM_HASH_SIZE; i++) {
-               spin_lock_init(&raparm_hash[i].pb_lock);
-
-               raparm = &raparm_hash[i].pb_head;
-               for (j = 0; j < nperbucket; j++) {
-                       *raparm = kzalloc(sizeof(struct raparms), GFP_KERNEL);
-                       if (!*raparm)
-                               goto out_nomem;
-                       raparm = &(*raparm)->p_next;
-               }
-               *raparm = NULL;
-       }
-
-       nfsdstats.ra_size = cache_size;
-       return 0;
-
-out_nomem:
-       dprintk("nfsd: kmalloc failed, freeing readahead buffers\n");
-       nfsd_racache_shutdown();
-       return -ENOMEM;
-}