diff options
Diffstat (limited to 'mm')
-rw-r--r-- | mm/filemap.c | 51 |
1 files changed, 45 insertions, 6 deletions
diff --git a/mm/filemap.c b/mm/filemap.c index 3464b681f844..57faa8d12099 100644 --- a/mm/filemap.c +++ b/mm/filemap.c | |||
@@ -2222,7 +2222,7 @@ __generic_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov, | |||
2222 | unsigned long nr_segs, loff_t *ppos) | 2222 | unsigned long nr_segs, loff_t *ppos) |
2223 | { | 2223 | { |
2224 | struct file *file = iocb->ki_filp; | 2224 | struct file *file = iocb->ki_filp; |
2225 | const struct address_space * mapping = file->f_mapping; | 2225 | struct address_space * mapping = file->f_mapping; |
2226 | size_t ocount; /* original count */ | 2226 | size_t ocount; /* original count */ |
2227 | size_t count; /* after file limit checks */ | 2227 | size_t count; /* after file limit checks */ |
2228 | struct inode *inode = mapping->host; | 2228 | struct inode *inode = mapping->host; |
@@ -2275,8 +2275,11 @@ __generic_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov, | |||
2275 | 2275 | ||
2276 | /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */ | 2276 | /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */ |
2277 | if (unlikely(file->f_flags & O_DIRECT)) { | 2277 | if (unlikely(file->f_flags & O_DIRECT)) { |
2278 | written = generic_file_direct_write(iocb, iov, | 2278 | loff_t endbyte; |
2279 | &nr_segs, pos, ppos, count, ocount); | 2279 | ssize_t written_buffered; |
2280 | |||
2281 | written = generic_file_direct_write(iocb, iov, &nr_segs, pos, | ||
2282 | ppos, count, ocount); | ||
2280 | if (written < 0 || written == count) | 2283 | if (written < 0 || written == count) |
2281 | goto out; | 2284 | goto out; |
2282 | /* | 2285 | /* |
@@ -2285,10 +2288,46 @@ __generic_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov, | |||
2285 | */ | 2288 | */ |
2286 | pos += written; | 2289 | pos += written; |
2287 | count -= written; | 2290 | count -= written; |
2288 | } | 2291 | written_buffered = generic_file_buffered_write(iocb, iov, |
2292 | nr_segs, pos, ppos, count, | ||
2293 | written); | ||
2294 | /* | ||
2295 | * If generic_file_buffered_write() retuned a synchronous error | ||
2296 | * then we want to return the number of bytes which were | ||
2297 | * direct-written, or the error code if that was zero. Note | ||
2298 | * that this differs from normal direct-io semantics, which | ||
2299 | * will return -EFOO even if some bytes were written. | ||
2300 | */ | ||
2301 | if (written_buffered < 0) { | ||
2302 | err = written_buffered; | ||
2303 | goto out; | ||
2304 | } | ||
2289 | 2305 | ||
2290 | written = generic_file_buffered_write(iocb, iov, nr_segs, | 2306 | /* |
2291 | pos, ppos, count, written); | 2307 | * We need to ensure that the page cache pages are written to |
2308 | * disk and invalidated to preserve the expected O_DIRECT | ||
2309 | * semantics. | ||
2310 | */ | ||
2311 | endbyte = pos + written_buffered - written - 1; | ||
2312 | err = do_sync_file_range(file, pos, endbyte, | ||
2313 | SYNC_FILE_RANGE_WAIT_BEFORE| | ||
2314 | SYNC_FILE_RANGE_WRITE| | ||
2315 | SYNC_FILE_RANGE_WAIT_AFTER); | ||
2316 | if (err == 0) { | ||
2317 | written = written_buffered; | ||
2318 | invalidate_mapping_pages(mapping, | ||
2319 | pos >> PAGE_CACHE_SHIFT, | ||
2320 | endbyte >> PAGE_CACHE_SHIFT); | ||
2321 | } else { | ||
2322 | /* | ||
2323 | * We don't know how much we wrote, so just return | ||
2324 | * the number of bytes which were direct-written | ||
2325 | */ | ||
2326 | } | ||
2327 | } else { | ||
2328 | written = generic_file_buffered_write(iocb, iov, nr_segs, | ||
2329 | pos, ppos, count, written); | ||
2330 | } | ||
2292 | out: | 2331 | out: |
2293 | current->backing_dev_info = NULL; | 2332 | current->backing_dev_info = NULL; |
2294 | return written ? written : err; | 2333 | return written ? written : err; |