diff options
Diffstat (limited to 'mm/filemap.h')
| -rw-r--r-- | mm/filemap.h | 30 | 
1 files changed, 20 insertions, 10 deletions
diff --git a/mm/filemap.h b/mm/filemap.h index 5683cde22055..3f2a343c6015 100644 --- a/mm/filemap.h +++ b/mm/filemap.h  | |||
| @@ -16,15 +16,23 @@ | |||
| 16 | #include <linux/uaccess.h> | 16 | #include <linux/uaccess.h> | 
| 17 | 17 | ||
| 18 | size_t | 18 | size_t | 
| 19 | __filemap_copy_from_user_iovec(char *vaddr, | 19 | __filemap_copy_from_user_iovec_inatomic(char *vaddr, | 
| 20 | const struct iovec *iov, | 20 | const struct iovec *iov, | 
| 21 | size_t base, | 21 | size_t base, | 
| 22 | size_t bytes); | 22 | size_t bytes); | 
| 23 | 23 | ||
| 24 | /* | 24 | /* | 
| 25 | * Copy as much as we can into the page and return the number of bytes which | 25 | * Copy as much as we can into the page and return the number of bytes which | 
| 26 | * were sucessfully copied. If a fault is encountered then clear the page | 26 | * were sucessfully copied. If a fault is encountered then clear the page | 
| 27 | * out to (offset+bytes) and return the number of bytes which were copied. | 27 | * out to (offset+bytes) and return the number of bytes which were copied. | 
| 28 | * | ||
| 29 | * NOTE: For this to work reliably we really want copy_from_user_inatomic_nocache | ||
| 30 | * to *NOT* zero any tail of the buffer that it failed to copy. If it does, | ||
| 31 | * and if the following non-atomic copy succeeds, then there is a small window | ||
| 32 | * where the target page contains neither the data before the write, nor the | ||
| 33 | * data after the write (it contains zero). A read at this time will see | ||
| 34 | * data that is inconsistent with any ordering of the read and the write. | ||
| 35 | * (This has been detected in practice). | ||
| 28 | */ | 36 | */ | 
| 29 | static inline size_t | 37 | static inline size_t | 
| 30 | filemap_copy_from_user(struct page *page, unsigned long offset, | 38 | filemap_copy_from_user(struct page *page, unsigned long offset, | 
| @@ -60,13 +68,15 @@ filemap_copy_from_user_iovec(struct page *page, unsigned long offset, | |||
| 60 | size_t copied; | 68 | size_t copied; | 
| 61 | 69 | ||
| 62 | kaddr = kmap_atomic(page, KM_USER0); | 70 | kaddr = kmap_atomic(page, KM_USER0); | 
| 63 | copied = __filemap_copy_from_user_iovec(kaddr + offset, iov, | 71 | copied = __filemap_copy_from_user_iovec_inatomic(kaddr + offset, iov, | 
| 64 | base, bytes); | 72 | base, bytes); | 
| 65 | kunmap_atomic(kaddr, KM_USER0); | 73 | kunmap_atomic(kaddr, KM_USER0); | 
| 66 | if (copied != bytes) { | 74 | if (copied != bytes) { | 
| 67 | kaddr = kmap(page); | 75 | kaddr = kmap(page); | 
| 68 | copied = __filemap_copy_from_user_iovec(kaddr + offset, iov, | 76 | copied = __filemap_copy_from_user_iovec_inatomic(kaddr + offset, iov, | 
| 69 | base, bytes); | 77 | base, bytes); | 
| 78 | if (bytes - copied) | ||
| 79 | memset(kaddr + offset + copied, 0, bytes - copied); | ||
| 70 | kunmap(page); | 80 | kunmap(page); | 
| 71 | } | 81 | } | 
| 72 | return copied; | 82 | return copied; | 
| @@ -78,7 +88,7 @@ filemap_set_next_iovec(const struct iovec **iovp, size_t *basep, size_t bytes) | |||
| 78 | const struct iovec *iov = *iovp; | 88 | const struct iovec *iov = *iovp; | 
| 79 | size_t base = *basep; | 89 | size_t base = *basep; | 
| 80 | 90 | ||
| 81 | while (bytes) { | 91 | do { | 
| 82 | int copy = min(bytes, iov->iov_len - base); | 92 | int copy = min(bytes, iov->iov_len - base); | 
| 83 | 93 | ||
| 84 | bytes -= copy; | 94 | bytes -= copy; | 
| @@ -87,7 +97,7 @@ filemap_set_next_iovec(const struct iovec **iovp, size_t *basep, size_t bytes) | |||
| 87 | iov++; | 97 | iov++; | 
| 88 | base = 0; | 98 | base = 0; | 
| 89 | } | 99 | } | 
| 90 | } | 100 | } while (bytes); | 
| 91 | *iovp = iov; | 101 | *iovp = iov; | 
| 92 | *basep = base; | 102 | *basep = base; | 
| 93 | } | 103 | } | 
