]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/nfs/direct.c
Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6
[linux-2.6-omap-h63xx.git] / fs / nfs / direct.c
index 07922881760339f15e907e340c787cf78d888bca..4ae2f3b33fef8b908969c34e07a9e24e2aa06290 100644 (file)
@@ -57,6 +57,7 @@
 #define NFSDBG_FACILITY                NFSDBG_VFS
 #define MAX_DIRECTIO_SIZE      (4096UL << PAGE_SHIFT)
 
+static void nfs_free_user_pages(struct page **pages, int npages, int do_dirty);
 static kmem_cache_t *nfs_direct_cachep;
 
 /*
@@ -107,6 +108,15 @@ nfs_get_user_pages(int rw, unsigned long user_addr, size_t size,
                                        page_count, (rw == READ), 0,
                                        *pages, NULL);
                up_read(&current->mm->mmap_sem);
+               /*
+                * If we got fewer pages than expected from get_user_pages(),
+                * the user buffer runs off the end of a mapping; return EFAULT.
+                */
+               if (result >= 0 && result < page_count) {
+                       nfs_free_user_pages(*pages, result, 0);
+                       *pages = NULL;
+                       result = -EFAULT;
+               }
        }
        return result;
 }
@@ -122,9 +132,10 @@ nfs_free_user_pages(struct page **pages, int npages, int do_dirty)
 {
        int i;
        for (i = 0; i < npages; i++) {
-               if (do_dirty)
-                       set_page_dirty_lock(pages[i]);
-               page_cache_release(pages[i]);
+               struct page *page = pages[i];
+               if (do_dirty && !PageCompound(page))
+                       set_page_dirty_lock(page);
+               page_cache_release(page);
        }
        kfree(pages);
 }
@@ -154,6 +165,7 @@ static struct nfs_direct_req *nfs_direct_read_alloc(size_t nbytes, unsigned int
        struct list_head *list;
        struct nfs_direct_req *dreq;
        unsigned int reads = 0;
+       unsigned int rpages = (rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
 
        dreq = kmem_cache_alloc(nfs_direct_cachep, SLAB_KERNEL);
        if (!dreq)
@@ -167,7 +179,7 @@ static struct nfs_direct_req *nfs_direct_read_alloc(size_t nbytes, unsigned int
 
        list = &dreq->list;
        for(;;) {
-               struct nfs_read_data *data = nfs_readdata_alloc();
+               struct nfs_read_data *data = nfs_readdata_alloc(rpages);
 
                if (unlikely(!data)) {
                        while (!list_empty(list)) {
@@ -268,8 +280,6 @@ static void nfs_direct_read_schedule(struct nfs_direct_req *dreq,
                NFS_PROTO(inode)->read_setup(data);
 
                data->task.tk_cookie = (unsigned long) inode;
-               data->task.tk_calldata = data;
-               data->task.tk_release = nfs_readdata_release;
                data->complete = nfs_direct_read_result;
 
                lock_kernel();
@@ -433,7 +443,7 @@ static ssize_t nfs_direct_write_seg(struct inode *inode,
        struct nfs_writeverf first_verf;
        struct nfs_write_data *wdata;
 
-       wdata = nfs_writedata_alloc();
+       wdata = nfs_writedata_alloc(NFS_SERVER(inode)->wpages);
        if (!wdata)
                return -ENOMEM;
 
@@ -481,7 +491,7 @@ retry:
                if (wdata->verf.committed != NFS_FILE_SYNC) {
                        need_commit = 1;
                        if (memcmp(&first_verf.verifier, &wdata->verf.verifier,
-                                       sizeof(first_verf.verifier)));
+                                       sizeof(first_verf.verifier)))
                                goto sync_retry;
                }
 
@@ -662,10 +672,10 @@ nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t
                .iov_len = count,
        };
 
-       dprintk("nfs: direct read(%s/%s, %lu@%lu)\n",
+       dprintk("nfs: direct read(%s/%s, %lu@%Ld)\n",
                file->f_dentry->d_parent->d_name.name,
                file->f_dentry->d_name.name,
-               (unsigned long) count, (unsigned long) pos);
+               (unsigned long) count, (long long) pos);
 
        if (!is_sync_kiocb(iocb))
                goto out;
@@ -718,9 +728,7 @@ out:
 ssize_t
 nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count, loff_t pos)
 {
-       ssize_t retval = -EINVAL;
-       loff_t *ppos = &iocb->ki_pos;
-       unsigned long limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
+       ssize_t retval;
        struct file *file = iocb->ki_filp;
        struct nfs_open_context *ctx =
                        (struct nfs_open_context *) file->private_data;
@@ -728,35 +736,32 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count,
        struct inode *inode = mapping->host;
        struct iovec iov = {
                .iov_base = (char __user *)buf,
-               .iov_len = count,
        };
 
-       dfprintk(VFS, "nfs: direct write(%s/%s(%ld), %lu@%lu)\n",
+       dfprintk(VFS, "nfs: direct write(%s/%s, %lu@%Ld)\n",
                file->f_dentry->d_parent->d_name.name,
-               file->f_dentry->d_name.name, inode->i_ino,
-               (unsigned long) count, (unsigned long) pos);
+               file->f_dentry->d_name.name,
+               (unsigned long) count, (long long) pos);
 
+       retval = -EINVAL;
        if (!is_sync_kiocb(iocb))
                goto out;
-       if (count < 0)
-               goto out;
-        if (pos < 0)
+
+       retval = generic_write_checks(file, &pos, &count, 0);
+       if (retval)
                goto out;
-       retval = -EFAULT;
-       if (!access_ok(VERIFY_READ, iov.iov_base, iov.iov_len))
+
+       retval = -EINVAL;
+       if ((ssize_t) count < 0)
                goto out;
-       retval = -EFBIG;
-       if (limit != RLIM_INFINITY) {
-               if (pos >= limit) {
-                       send_sig(SIGXFSZ, current, 0);
-                       goto out;
-               }
-               if (count > limit - (unsigned long) pos)
-                       count = limit - (unsigned long) pos;
-       }
        retval = 0;
        if (!count)
                goto out;
+       iov.iov_len = count,
+
+       retval = -EFAULT;
+       if (!access_ok(VERIFY_READ, iov.iov_base, iov.iov_len))
+               goto out;
 
        retval = nfs_sync_mapping(mapping);
        if (retval)
@@ -766,7 +771,7 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count,
        if (mapping->nrpages)
                invalidate_inode_pages2(mapping);
        if (retval > 0)
-               *ppos = pos + retval;
+               iocb->ki_pos = pos + retval;
 
 out:
        return retval;
@@ -776,7 +781,8 @@ int nfs_init_directcache(void)
 {
        nfs_direct_cachep = kmem_cache_create("nfs_direct_cache",
                                                sizeof(struct nfs_direct_req),
-                                               0, SLAB_RECLAIM_ACCOUNT,
+                                               0, (SLAB_RECLAIM_ACCOUNT|
+                                                       SLAB_MEM_SPREAD),
                                                NULL, NULL);
        if (nfs_direct_cachep == NULL)
                return -ENOMEM;