diff options
Diffstat (limited to 'fs/cachefiles/rdwr.c')
-rw-r--r-- | fs/cachefiles/rdwr.c | 128 |
1 files changed, 116 insertions, 12 deletions
diff --git a/fs/cachefiles/rdwr.c b/fs/cachefiles/rdwr.c index a69787e7dd96..1d8332563863 100644 --- a/fs/cachefiles/rdwr.c +++ b/fs/cachefiles/rdwr.c | |||
@@ -40,8 +40,10 @@ static int cachefiles_read_waiter(wait_queue_t *wait, unsigned mode, | |||
40 | 40 | ||
41 | _debug("--- monitor %p %lx ---", page, page->flags); | 41 | _debug("--- monitor %p %lx ---", page, page->flags); |
42 | 42 | ||
43 | if (!PageUptodate(page) && !PageError(page)) | 43 | if (!PageUptodate(page) && !PageError(page)) { |
44 | dump_stack(); | 44 | /* unlocked, not uptodate and not erronous? */ |
45 | _debug("page probably truncated"); | ||
46 | } | ||
45 | 47 | ||
46 | /* remove from the waitqueue */ | 48 | /* remove from the waitqueue */ |
47 | list_del(&wait->task_list); | 49 | list_del(&wait->task_list); |
@@ -61,6 +63,84 @@ static int cachefiles_read_waiter(wait_queue_t *wait, unsigned mode, | |||
61 | } | 63 | } |
62 | 64 | ||
63 | /* | 65 | /* |
66 | * handle a probably truncated page | ||
67 | * - check to see if the page is still relevant and reissue the read if | ||
68 | * possible | ||
69 | * - return -EIO on error, -ENODATA if the page is gone, -EINPROGRESS if we | ||
70 | * must wait again and 0 if successful | ||
71 | */ | ||
72 | static int cachefiles_read_reissue(struct cachefiles_object *object, | ||
73 | struct cachefiles_one_read *monitor) | ||
74 | { | ||
75 | struct address_space *bmapping = object->backer->d_inode->i_mapping; | ||
76 | struct page *backpage = monitor->back_page, *backpage2; | ||
77 | int ret; | ||
78 | |||
79 | kenter("{ino=%lx},{%lx,%lx}", | ||
80 | object->backer->d_inode->i_ino, | ||
81 | backpage->index, backpage->flags); | ||
82 | |||
83 | /* skip if the page was truncated away completely */ | ||
84 | if (backpage->mapping != bmapping) { | ||
85 | kleave(" = -ENODATA [mapping]"); | ||
86 | return -ENODATA; | ||
87 | } | ||
88 | |||
89 | backpage2 = find_get_page(bmapping, backpage->index); | ||
90 | if (!backpage2) { | ||
91 | kleave(" = -ENODATA [gone]"); | ||
92 | return -ENODATA; | ||
93 | } | ||
94 | |||
95 | if (backpage != backpage2) { | ||
96 | put_page(backpage2); | ||
97 | kleave(" = -ENODATA [different]"); | ||
98 | return -ENODATA; | ||
99 | } | ||
100 | |||
101 | /* the page is still there and we already have a ref on it, so we don't | ||
102 | * need a second */ | ||
103 | put_page(backpage2); | ||
104 | |||
105 | INIT_LIST_HEAD(&monitor->op_link); | ||
106 | add_page_wait_queue(backpage, &monitor->monitor); | ||
107 | |||
108 | if (trylock_page(backpage)) { | ||
109 | ret = -EIO; | ||
110 | if (PageError(backpage)) | ||
111 | goto unlock_discard; | ||
112 | ret = 0; | ||
113 | if (PageUptodate(backpage)) | ||
114 | goto unlock_discard; | ||
115 | |||
116 | kdebug("reissue read"); | ||
117 | ret = bmapping->a_ops->readpage(NULL, backpage); | ||
118 | if (ret < 0) | ||
119 | goto unlock_discard; | ||
120 | } | ||
121 | |||
122 | /* but the page may have been read before the monitor was installed, so | ||
123 | * the monitor may miss the event - so we have to ensure that we do get | ||
124 | * one in such a case */ | ||
125 | if (trylock_page(backpage)) { | ||
126 | _debug("jumpstart %p {%lx}", backpage, backpage->flags); | ||
127 | unlock_page(backpage); | ||
128 | } | ||
129 | |||
130 | /* it'll reappear on the todo list */ | ||
131 | kleave(" = -EINPROGRESS"); | ||
132 | return -EINPROGRESS; | ||
133 | |||
134 | unlock_discard: | ||
135 | unlock_page(backpage); | ||
136 | spin_lock_irq(&object->work_lock); | ||
137 | list_del(&monitor->op_link); | ||
138 | spin_unlock_irq(&object->work_lock); | ||
139 | kleave(" = %d", ret); | ||
140 | return ret; | ||
141 | } | ||
142 | |||
143 | /* | ||
64 | * copy data from backing pages to netfs pages to complete a read operation | 144 | * copy data from backing pages to netfs pages to complete a read operation |
65 | * - driven by FS-Cache's thread pool | 145 | * - driven by FS-Cache's thread pool |
66 | */ | 146 | */ |
@@ -92,20 +172,26 @@ static void cachefiles_read_copier(struct fscache_operation *_op) | |||
92 | 172 | ||
93 | _debug("- copy {%lu}", monitor->back_page->index); | 173 | _debug("- copy {%lu}", monitor->back_page->index); |
94 | 174 | ||
95 | error = -EIO; | 175 | recheck: |
96 | if (PageUptodate(monitor->back_page)) { | 176 | if (PageUptodate(monitor->back_page)) { |
97 | copy_highpage(monitor->netfs_page, monitor->back_page); | 177 | copy_highpage(monitor->netfs_page, monitor->back_page); |
98 | 178 | ||
99 | pagevec_add(&pagevec, monitor->netfs_page); | 179 | pagevec_add(&pagevec, monitor->netfs_page); |
100 | fscache_mark_pages_cached(monitor->op, &pagevec); | 180 | fscache_mark_pages_cached(monitor->op, &pagevec); |
101 | error = 0; | 181 | error = 0; |
102 | } | 182 | } else if (!PageError(monitor->back_page)) { |
103 | 183 | /* the page has probably been truncated */ | |
104 | if (error) | 184 | error = cachefiles_read_reissue(object, monitor); |
185 | if (error == -EINPROGRESS) | ||
186 | goto next; | ||
187 | goto recheck; | ||
188 | } else { | ||
105 | cachefiles_io_error_obj( | 189 | cachefiles_io_error_obj( |
106 | object, | 190 | object, |
107 | "Readpage failed on backing file %lx", | 191 | "Readpage failed on backing file %lx", |
108 | (unsigned long) monitor->back_page->flags); | 192 | (unsigned long) monitor->back_page->flags); |
193 | error = -EIO; | ||
194 | } | ||
109 | 195 | ||
110 | page_cache_release(monitor->back_page); | 196 | page_cache_release(monitor->back_page); |
111 | 197 | ||
@@ -114,6 +200,7 @@ static void cachefiles_read_copier(struct fscache_operation *_op) | |||
114 | fscache_put_retrieval(op); | 200 | fscache_put_retrieval(op); |
115 | kfree(monitor); | 201 | kfree(monitor); |
116 | 202 | ||
203 | next: | ||
117 | /* let the thread pool have some air occasionally */ | 204 | /* let the thread pool have some air occasionally */ |
118 | max--; | 205 | max--; |
119 | if (max < 0 || need_resched()) { | 206 | if (max < 0 || need_resched()) { |
@@ -333,7 +420,8 @@ int cachefiles_read_or_alloc_page(struct fscache_retrieval *op, | |||
333 | 420 | ||
334 | shift = PAGE_SHIFT - inode->i_sb->s_blocksize_bits; | 421 | shift = PAGE_SHIFT - inode->i_sb->s_blocksize_bits; |
335 | 422 | ||
336 | op->op.flags = FSCACHE_OP_FAST; | 423 | op->op.flags &= FSCACHE_OP_KEEP_FLAGS; |
424 | op->op.flags |= FSCACHE_OP_FAST; | ||
337 | op->op.processor = cachefiles_read_copier; | 425 | op->op.processor = cachefiles_read_copier; |
338 | 426 | ||
339 | pagevec_init(&pagevec, 0); | 427 | pagevec_init(&pagevec, 0); |
@@ -639,7 +727,8 @@ int cachefiles_read_or_alloc_pages(struct fscache_retrieval *op, | |||
639 | 727 | ||
640 | pagevec_init(&pagevec, 0); | 728 | pagevec_init(&pagevec, 0); |
641 | 729 | ||
642 | op->op.flags = FSCACHE_OP_FAST; | 730 | op->op.flags &= FSCACHE_OP_KEEP_FLAGS; |
731 | op->op.flags |= FSCACHE_OP_FAST; | ||
643 | op->op.processor = cachefiles_read_copier; | 732 | op->op.processor = cachefiles_read_copier; |
644 | 733 | ||
645 | INIT_LIST_HEAD(&backpages); | 734 | INIT_LIST_HEAD(&backpages); |
@@ -801,7 +890,8 @@ int cachefiles_write_page(struct fscache_storage *op, struct page *page) | |||
801 | struct cachefiles_cache *cache; | 890 | struct cachefiles_cache *cache; |
802 | mm_segment_t old_fs; | 891 | mm_segment_t old_fs; |
803 | struct file *file; | 892 | struct file *file; |
804 | loff_t pos; | 893 | loff_t pos, eof; |
894 | size_t len; | ||
805 | void *data; | 895 | void *data; |
806 | int ret; | 896 | int ret; |
807 | 897 | ||
@@ -835,15 +925,29 @@ int cachefiles_write_page(struct fscache_storage *op, struct page *page) | |||
835 | ret = -EIO; | 925 | ret = -EIO; |
836 | if (file->f_op->write) { | 926 | if (file->f_op->write) { |
837 | pos = (loff_t) page->index << PAGE_SHIFT; | 927 | pos = (loff_t) page->index << PAGE_SHIFT; |
928 | |||
929 | /* we mustn't write more data than we have, so we have | ||
930 | * to beware of a partial page at EOF */ | ||
931 | eof = object->fscache.store_limit_l; | ||
932 | len = PAGE_SIZE; | ||
933 | if (eof & ~PAGE_MASK) { | ||
934 | ASSERTCMP(pos, <, eof); | ||
935 | if (eof - pos < PAGE_SIZE) { | ||
936 | _debug("cut short %llx to %llx", | ||
937 | pos, eof); | ||
938 | len = eof - pos; | ||
939 | ASSERTCMP(pos + len, ==, eof); | ||
940 | } | ||
941 | } | ||
942 | |||
838 | data = kmap(page); | 943 | data = kmap(page); |
839 | old_fs = get_fs(); | 944 | old_fs = get_fs(); |
840 | set_fs(KERNEL_DS); | 945 | set_fs(KERNEL_DS); |
841 | ret = file->f_op->write( | 946 | ret = file->f_op->write( |
842 | file, (const void __user *) data, PAGE_SIZE, | 947 | file, (const void __user *) data, len, &pos); |
843 | &pos); | ||
844 | set_fs(old_fs); | 948 | set_fs(old_fs); |
845 | kunmap(page); | 949 | kunmap(page); |
846 | if (ret != PAGE_SIZE) | 950 | if (ret != len) |
847 | ret = -EIO; | 951 | ret = -EIO; |
848 | } | 952 | } |
849 | fput(file); | 953 | fput(file); |