diff options
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/direct.c | 59 |
1 files changed, 29 insertions, 30 deletions
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index b4bbf6d75923..58830429e42a 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c | |||
@@ -59,7 +59,6 @@ | |||
59 | 59 | ||
60 | #define NFSDBG_FACILITY NFSDBG_VFS | 60 | #define NFSDBG_FACILITY NFSDBG_VFS |
61 | 61 | ||
62 | static void nfs_free_user_pages(struct page **pages, int npages, int do_dirty); | ||
63 | static kmem_cache_t *nfs_direct_cachep; | 62 | static kmem_cache_t *nfs_direct_cachep; |
64 | 63 | ||
65 | /* | 64 | /* |
@@ -121,6 +120,18 @@ ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_ | |||
121 | return -EINVAL; | 120 | return -EINVAL; |
122 | } | 121 | } |
123 | 122 | ||
123 | static void nfs_free_user_pages(struct page **pages, int npages, int do_dirty) | ||
124 | { | ||
125 | int i; | ||
126 | for (i = 0; i < npages; i++) { | ||
127 | struct page *page = pages[i]; | ||
128 | if (do_dirty && !PageCompound(page)) | ||
129 | set_page_dirty_lock(page); | ||
130 | page_cache_release(page); | ||
131 | } | ||
132 | kfree(pages); | ||
133 | } | ||
134 | |||
124 | static inline int nfs_get_user_pages(int rw, unsigned long user_addr, size_t size, struct page ***pages) | 135 | static inline int nfs_get_user_pages(int rw, unsigned long user_addr, size_t size, struct page ***pages) |
125 | { | 136 | { |
126 | int result = -ENOMEM; | 137 | int result = -ENOMEM; |
@@ -138,31 +149,23 @@ static inline int nfs_get_user_pages(int rw, unsigned long user_addr, size_t siz | |||
138 | page_count, (rw == READ), 0, | 149 | page_count, (rw == READ), 0, |
139 | *pages, NULL); | 150 | *pages, NULL); |
140 | up_read(¤t->mm->mmap_sem); | 151 | up_read(¤t->mm->mmap_sem); |
141 | /* | 152 | if (result != page_count) { |
142 | * If we got fewer pages than expected from get_user_pages(), | 153 | /* |
143 | * the user buffer runs off the end of a mapping; return EFAULT. | 154 | * If we got fewer pages than expected from |
144 | */ | 155 | * get_user_pages(), the user buffer runs off the |
145 | if (result >= 0 && result < page_count) { | 156 | * end of a mapping; return EFAULT. |
146 | nfs_free_user_pages(*pages, result, 0); | 157 | */ |
158 | if (result >= 0) { | ||
159 | nfs_free_user_pages(*pages, result, 0); | ||
160 | result = -EFAULT; | ||
161 | } else | ||
162 | kfree(*pages); | ||
147 | *pages = NULL; | 163 | *pages = NULL; |
148 | result = -EFAULT; | ||
149 | } | 164 | } |
150 | } | 165 | } |
151 | return result; | 166 | return result; |
152 | } | 167 | } |
153 | 168 | ||
154 | static void nfs_free_user_pages(struct page **pages, int npages, int do_dirty) | ||
155 | { | ||
156 | int i; | ||
157 | for (i = 0; i < npages; i++) { | ||
158 | struct page *page = pages[i]; | ||
159 | if (do_dirty && !PageCompound(page)) | ||
160 | set_page_dirty_lock(page); | ||
161 | page_cache_release(page); | ||
162 | } | ||
163 | kfree(pages); | ||
164 | } | ||
165 | |||
166 | static inline struct nfs_direct_req *nfs_direct_req_alloc(void) | 169 | static inline struct nfs_direct_req *nfs_direct_req_alloc(void) |
167 | { | 170 | { |
168 | struct nfs_direct_req *dreq; | 171 | struct nfs_direct_req *dreq; |
@@ -788,13 +791,11 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, | |||
788 | if (retval) | 791 | if (retval) |
789 | goto out; | 792 | goto out; |
790 | 793 | ||
791 | page_count = nfs_get_user_pages(READ, (unsigned long) buf, | 794 | retval = nfs_get_user_pages(READ, (unsigned long) buf, |
792 | count, &pages); | 795 | count, &pages); |
793 | if (page_count < 0) { | 796 | if (retval < 0) |
794 | nfs_free_user_pages(pages, 0, 0); | ||
795 | retval = page_count; | ||
796 | goto out; | 797 | goto out; |
797 | } | 798 | page_count = retval; |
798 | 799 | ||
799 | retval = nfs_direct_read(iocb, (unsigned long) buf, count, pos, | 800 | retval = nfs_direct_read(iocb, (unsigned long) buf, count, pos, |
800 | pages, page_count); | 801 | pages, page_count); |
@@ -862,13 +863,11 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t | |||
862 | if (retval) | 863 | if (retval) |
863 | goto out; | 864 | goto out; |
864 | 865 | ||
865 | page_count = nfs_get_user_pages(WRITE, (unsigned long) buf, | 866 | retval = nfs_get_user_pages(WRITE, (unsigned long) buf, |
866 | count, &pages); | 867 | count, &pages); |
867 | if (page_count < 0) { | 868 | if (retval < 0) |
868 | nfs_free_user_pages(pages, 0, 0); | ||
869 | retval = page_count; | ||
870 | goto out; | 869 | goto out; |
871 | } | 870 | page_count = retval; |
872 | 871 | ||
873 | retval = nfs_direct_write(iocb, (unsigned long) buf, count, | 872 | retval = nfs_direct_write(iocb, (unsigned long) buf, count, |
874 | pos, pages, page_count); | 873 | pos, pages, page_count); |