Commit e87b03f5 authored by David Howells's avatar David Howells
parent 810caa3e
...@@ -2083,6 +2083,6 @@ static void afs_dir_invalidatepage(struct page *page, unsigned int offset, ...@@ -2083,6 +2083,6 @@ static void afs_dir_invalidatepage(struct page *page, unsigned int offset,
afs_stat_v(dvnode, n_inval); afs_stat_v(dvnode, n_inval);
/* we clean up only if the entire page is being invalidated */ /* we clean up only if the entire page is being invalidated */
if (offset == 0 && length == PAGE_SIZE) if (offset == 0 && length == thp_size(page))
detach_page_private(page); detach_page_private(page);
} }
...@@ -329,8 +329,8 @@ static int afs_page_filler(struct key *key, struct page *page) ...@@ -329,8 +329,8 @@ static int afs_page_filler(struct key *key, struct page *page)
req->vnode = vnode; req->vnode = vnode;
req->key = key_get(key); req->key = key_get(key);
req->pos = (loff_t)page->index << PAGE_SHIFT; req->pos = (loff_t)page->index << PAGE_SHIFT;
req->len = PAGE_SIZE; req->len = thp_size(page);
req->nr_pages = 1; req->nr_pages = thp_nr_pages(page);
req->done = afs_file_read_done; req->done = afs_file_read_done;
req->cleanup = afs_file_read_cleanup; req->cleanup = afs_file_read_cleanup;
...@@ -574,8 +574,8 @@ static void afs_invalidate_dirty(struct page *page, unsigned int offset, ...@@ -574,8 +574,8 @@ static void afs_invalidate_dirty(struct page *page, unsigned int offset,
trace_afs_page_dirty(vnode, tracepoint_string("undirty"), page); trace_afs_page_dirty(vnode, tracepoint_string("undirty"), page);
clear_page_dirty_for_io(page); clear_page_dirty_for_io(page);
full_invalidate: full_invalidate:
detach_page_private(page);
trace_afs_page_dirty(vnode, tracepoint_string("inval"), page); trace_afs_page_dirty(vnode, tracepoint_string("inval"), page);
detach_page_private(page);
} }
/* /*
...@@ -620,8 +620,8 @@ static int afs_releasepage(struct page *page, gfp_t gfp_flags) ...@@ -620,8 +620,8 @@ static int afs_releasepage(struct page *page, gfp_t gfp_flags)
#endif #endif
if (PagePrivate(page)) { if (PagePrivate(page)) {
detach_page_private(page);
trace_afs_page_dirty(vnode, tracepoint_string("rel"), page); trace_afs_page_dirty(vnode, tracepoint_string("rel"), page);
detach_page_private(page);
} }
/* indicate that the page can be released */ /* indicate that the page can be released */
......
...@@ -815,8 +815,6 @@ struct afs_operation { ...@@ -815,8 +815,6 @@ struct afs_operation {
loff_t pos; loff_t pos;
loff_t size; loff_t size;
loff_t i_size; loff_t i_size;
pgoff_t first; /* first page in mapping to deal with */
pgoff_t last; /* last page in mapping to deal with */
bool laundering; /* Laundering page, PG_writeback not set */ bool laundering; /* Laundering page, PG_writeback not set */
} store; } store;
struct { struct {
......
...@@ -94,15 +94,15 @@ int afs_write_begin(struct file *file, struct address_space *mapping, ...@@ -94,15 +94,15 @@ int afs_write_begin(struct file *file, struct address_space *mapping,
struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
struct page *page; struct page *page;
unsigned long priv; unsigned long priv;
unsigned f, from = pos & (PAGE_SIZE - 1); unsigned f, from;
unsigned t, to = from + len; unsigned t, to;
pgoff_t index = pos >> PAGE_SHIFT; pgoff_t index;
int ret; int ret;
_enter("{%llx:%llu},{%lx},%u,%u", _enter("{%llx:%llu},%llx,%x",
vnode->fid.vid, vnode->fid.vnode, index, from, to); vnode->fid.vid, vnode->fid.vnode, pos, len);
page = grab_cache_page_write_begin(mapping, index, flags); page = grab_cache_page_write_begin(mapping, pos / PAGE_SIZE, flags);
if (!page) if (!page)
return -ENOMEM; return -ENOMEM;
...@@ -121,19 +121,20 @@ int afs_write_begin(struct file *file, struct address_space *mapping, ...@@ -121,19 +121,20 @@ int afs_write_begin(struct file *file, struct address_space *mapping,
wait_on_page_fscache(page); wait_on_page_fscache(page);
#endif #endif
index = page->index;
from = pos - index * PAGE_SIZE;
to = from + len;
try_again: try_again:
/* See if this page is already partially written in a way that we can /* See if this page is already partially written in a way that we can
* merge the new write with. * merge the new write with.
*/ */
t = f = 0;
if (PagePrivate(page)) { if (PagePrivate(page)) {
priv = page_private(page); priv = page_private(page);
f = afs_page_dirty_from(page, priv); f = afs_page_dirty_from(page, priv);
t = afs_page_dirty_to(page, priv); t = afs_page_dirty_to(page, priv);
ASSERTCMP(f, <=, t); ASSERTCMP(f, <=, t);
}
if (f != t) {
if (PageWriteback(page)) { if (PageWriteback(page)) {
trace_afs_page_dirty(vnode, tracepoint_string("alrdy"), page); trace_afs_page_dirty(vnode, tracepoint_string("alrdy"), page);
goto flush_conflicting_write; goto flush_conflicting_write;
...@@ -180,7 +181,7 @@ int afs_write_end(struct file *file, struct address_space *mapping, ...@@ -180,7 +181,7 @@ int afs_write_end(struct file *file, struct address_space *mapping,
{ {
struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
unsigned long priv; unsigned long priv;
unsigned int f, from = pos & (PAGE_SIZE - 1); unsigned int f, from = pos & (thp_size(page) - 1);
unsigned int t, to = from + copied; unsigned int t, to = from + copied;
loff_t i_size, maybe_i_size; loff_t i_size, maybe_i_size;
int ret = 0; int ret = 0;
...@@ -233,9 +234,8 @@ int afs_write_end(struct file *file, struct address_space *mapping, ...@@ -233,9 +234,8 @@ int afs_write_end(struct file *file, struct address_space *mapping,
trace_afs_page_dirty(vnode, tracepoint_string("dirty"), page); trace_afs_page_dirty(vnode, tracepoint_string("dirty"), page);
} }
set_page_dirty(page); if (set_page_dirty(page))
if (PageDirty(page)) _debug("dirtied %lx", page->index);
_debug("dirtied");
ret = copied; ret = copied;
out: out:
...@@ -248,40 +248,43 @@ int afs_write_end(struct file *file, struct address_space *mapping, ...@@ -248,40 +248,43 @@ int afs_write_end(struct file *file, struct address_space *mapping,
* kill all the pages in the given range * kill all the pages in the given range
*/ */
static void afs_kill_pages(struct address_space *mapping, static void afs_kill_pages(struct address_space *mapping,
pgoff_t first, pgoff_t last) loff_t start, loff_t len)
{ {
struct afs_vnode *vnode = AFS_FS_I(mapping->host); struct afs_vnode *vnode = AFS_FS_I(mapping->host);
struct pagevec pv; struct pagevec pv;
unsigned count, loop; unsigned int loop, psize;
_enter("{%llx:%llu},%lx-%lx", _enter("{%llx:%llu},%llx @%llx",
vnode->fid.vid, vnode->fid.vnode, first, last); vnode->fid.vid, vnode->fid.vnode, len, start);
pagevec_init(&pv); pagevec_init(&pv);
do { do {
_debug("kill %lx-%lx", first, last); _debug("kill %llx @%llx", len, start);
count = last - first + 1; pv.nr = find_get_pages_contig(mapping, start / PAGE_SIZE,
if (count > PAGEVEC_SIZE) PAGEVEC_SIZE, pv.pages);
count = PAGEVEC_SIZE; if (pv.nr == 0)
pv.nr = find_get_pages_contig(mapping, first, count, pv.pages); break;
ASSERTCMP(pv.nr, ==, count);
for (loop = 0; loop < count; loop++) { for (loop = 0; loop < pv.nr; loop++) {
struct page *page = pv.pages[loop]; struct page *page = pv.pages[loop];
if (page->index * PAGE_SIZE >= start + len)
break;
psize = thp_size(page);
start += psize;
len -= psize;
ClearPageUptodate(page); ClearPageUptodate(page);
SetPageError(page);
end_page_writeback(page); end_page_writeback(page);
if (page->index >= first)
first = page->index + 1;
lock_page(page); lock_page(page);
generic_error_remove_page(mapping, page); generic_error_remove_page(mapping, page);
unlock_page(page); unlock_page(page);
} }
__pagevec_release(&pv); __pagevec_release(&pv);
} while (first <= last); } while (len > 0);
_leave(""); _leave("");
} }
...@@ -291,37 +294,40 @@ static void afs_kill_pages(struct address_space *mapping, ...@@ -291,37 +294,40 @@ static void afs_kill_pages(struct address_space *mapping,
*/ */
static void afs_redirty_pages(struct writeback_control *wbc, static void afs_redirty_pages(struct writeback_control *wbc,
struct address_space *mapping, struct address_space *mapping,
pgoff_t first, pgoff_t last) loff_t start, loff_t len)
{ {
struct afs_vnode *vnode = AFS_FS_I(mapping->host); struct afs_vnode *vnode = AFS_FS_I(mapping->host);
struct pagevec pv; struct pagevec pv;
unsigned count, loop; unsigned int loop, psize;
_enter("{%llx:%llu},%lx-%lx", _enter("{%llx:%llu},%llx @%llx",
vnode->fid.vid, vnode->fid.vnode, first, last); vnode->fid.vid, vnode->fid.vnode, len, start);
pagevec_init(&pv); pagevec_init(&pv);
do { do {
_debug("redirty %lx-%lx", first, last); _debug("redirty %llx @%llx", len, start);
count = last - first + 1; pv.nr = find_get_pages_contig(mapping, start / PAGE_SIZE,
if (count > PAGEVEC_SIZE) PAGEVEC_SIZE, pv.pages);
count = PAGEVEC_SIZE; if (pv.nr == 0)
pv.nr = find_get_pages_contig(mapping, first, count, pv.pages); break;
ASSERTCMP(pv.nr, ==, count);
for (loop = 0; loop < count; loop++) { for (loop = 0; loop < pv.nr; loop++) {
struct page *page = pv.pages[loop]; struct page *page = pv.pages[loop];
if (page->index * PAGE_SIZE >= start + len)
break;
psize = thp_size(page);
start += psize;
len -= psize;
redirty_page_for_writepage(wbc, page); redirty_page_for_writepage(wbc, page);
end_page_writeback(page); end_page_writeback(page);
if (page->index >= first)
first = page->index + 1;
} }
__pagevec_release(&pv); __pagevec_release(&pv);
} while (first <= last); } while (len > 0);
_leave(""); _leave("");
} }
...@@ -329,23 +335,28 @@ static void afs_redirty_pages(struct writeback_control *wbc, ...@@ -329,23 +335,28 @@ static void afs_redirty_pages(struct writeback_control *wbc,
/* /*
* completion of write to server * completion of write to server
*/ */
static void afs_pages_written_back(struct afs_vnode *vnode, pgoff_t start, pgoff_t last) static void afs_pages_written_back(struct afs_vnode *vnode, loff_t start, unsigned int len)
{ {
struct address_space *mapping = vnode->vfs_inode.i_mapping; struct address_space *mapping = vnode->vfs_inode.i_mapping;
struct page *page; struct page *page;
pgoff_t end;
XA_STATE(xas, &mapping->i_pages, start); XA_STATE(xas, &mapping->i_pages, start / PAGE_SIZE);
_enter("{%llx:%llu},{%lx-%lx}", _enter("{%llx:%llu},{%x @%llx}",
vnode->fid.vid, vnode->fid.vnode, start, last); vnode->fid.vid, vnode->fid.vnode, len, start);
rcu_read_lock(); rcu_read_lock();
xas_for_each(&xas, page, last) { end = (start + len - 1) / PAGE_SIZE;
ASSERT(PageWriteback(page)); xas_for_each(&xas, page, end) {
if (!PageWriteback(page)) {
kdebug("bad %x @%llx page %lx %lx", len, start, page->index, end);
ASSERT(PageWriteback(page));
}
detach_page_private(page);
trace_afs_page_dirty(vnode, tracepoint_string("clear"), page); trace_afs_page_dirty(vnode, tracepoint_string("clear"), page);
detach_page_private(page);
page_endio(page, true, 0); page_endio(page, true, 0);
} }
...@@ -404,7 +415,7 @@ static void afs_store_data_success(struct afs_operation *op) ...@@ -404,7 +415,7 @@ static void afs_store_data_success(struct afs_operation *op)
afs_vnode_commit_status(op, &op->file[0]); afs_vnode_commit_status(op, &op->file[0]);
if (op->error == 0) { if (op->error == 0) {
if (!op->store.laundering) if (!op->store.laundering)
afs_pages_written_back(vnode, op->store.first, op->store.last); afs_pages_written_back(vnode, op->store.pos, op->store.size);
afs_stat_v(vnode, n_stores); afs_stat_v(vnode, n_stores);
atomic_long_add(op->store.size, &afs_v2net(vnode)->n_store_bytes); atomic_long_add(op->store.size, &afs_v2net(vnode)->n_store_bytes);
} }
...@@ -419,8 +430,7 @@ static const struct afs_operation_ops afs_store_data_operation = { ...@@ -419,8 +430,7 @@ static const struct afs_operation_ops afs_store_data_operation = {
/* /*
* write to a file * write to a file
*/ */
static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter, static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter, loff_t pos,
loff_t pos, pgoff_t first, pgoff_t last,
bool laundering) bool laundering)
{ {
struct afs_operation *op; struct afs_operation *op;
...@@ -453,8 +463,6 @@ static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter, ...@@ -453,8 +463,6 @@ static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter,
op->file[0].dv_delta = 1; op->file[0].dv_delta = 1;
op->store.write_iter = iter; op->store.write_iter = iter;
op->store.pos = pos; op->store.pos = pos;
op->store.first = first;
op->store.last = last;
op->store.size = size; op->store.size = size;
op->store.i_size = max(pos + size, i_size); op->store.i_size = max(pos + size, i_size);
op->store.laundering = laundering; op->store.laundering = laundering;
...@@ -499,40 +507,49 @@ static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter, ...@@ -499,40 +507,49 @@ static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter,
static void afs_extend_writeback(struct address_space *mapping, static void afs_extend_writeback(struct address_space *mapping,
struct afs_vnode *vnode, struct afs_vnode *vnode,
long *_count, long *_count,
pgoff_t start, loff_t start,
pgoff_t final_page, loff_t max_len,
unsigned *_offset, bool new_content,
unsigned *_to, unsigned int *_len)
bool new_content)
{ {
struct page *pages[8], *page; struct pagevec pvec;
unsigned long count = *_count, priv; struct page *page;
unsigned offset = *_offset, to = *_to, n, f, t; unsigned long priv;
int loop; unsigned int psize, filler = 0;
unsigned int f, t;
loff_t len = *_len;
pgoff_t index = (start + len) / PAGE_SIZE;
bool stop = true;
unsigned int i;
XA_STATE(xas, &mapping->i_pages, index);
pagevec_init(&pvec);
start++;
do { do {
_debug("more %lx [%lx]", start, count); /* Firstly, we gather up a batch of contiguous dirty pages
n = final_page - start + 1; * under the RCU read lock - but we can't clear the dirty flags
if (n > ARRAY_SIZE(pages)) * there if any of those pages are mapped.
n = ARRAY_SIZE(pages); */
n = find_get_pages_contig(mapping, start, ARRAY_SIZE(pages), pages); rcu_read_lock();
_debug("fgpc %u", n);
if (n == 0)
goto no_more;
if (pages[0]->index != start) {
do {
put_page(pages[--n]);
} while (n > 0);
goto no_more;
}
for (loop = 0; loop < n; loop++) { xas_for_each(&xas, page, ULONG_MAX) {
page = pages[loop]; stop = true;
if (to != PAGE_SIZE && !new_content) if (xas_retry(&xas, page))
continue;
if (xa_is_value(page))
break;
if (page->index != index)
break; break;
if (page->index > final_page)
if (!page_cache_get_speculative(page)) {
xas_reset(&xas);
continue;
}
/* Has the page moved or been split? */
if (unlikely(page != xas_reload(&xas)))
break; break;
if (!trylock_page(page)) if (!trylock_page(page))
break; break;
if (!PageDirty(page) || PageWriteback(page)) { if (!PageDirty(page) || PageWriteback(page)) {
...@@ -540,6 +557,7 @@ static void afs_extend_writeback(struct address_space *mapping, ...@@ -540,6 +557,7 @@ static void afs_extend_writeback(struct address_space *mapping,
break; break;
} }
psize = thp_size(page);
priv = page_private(page); priv = page_private(page);
f = afs_page_dirty_from(page, priv); f = afs_page_dirty_from(page, priv);
t = afs_page_dirty_to(page, priv); t = afs_page_dirty_to(page, priv);
...@@ -547,110 +565,126 @@ static void afs_extend_writeback(struct address_space *mapping, ...@@ -547,110 +565,126 @@ static void afs_extend_writeback(struct address_space *mapping,
unlock_page(page); unlock_page(page);
break; break;
} }
to = t;
len += filler + t;
filler = psize - t;
if (len >= max_len || *_count <= 0)
stop = true;
else if (t == psize || new_content)
stop = false;
index += thp_nr_pages(page);
if (!pagevec_add(&pvec, page))
break;
if (stop)
break;
}
if (!stop)
xas_pause(&xas);
rcu_read_unlock();
/* Now, if we obtained any pages, we can shift them to being
* writable and mark them for caching.
*/
if (!pagevec_count(&pvec))
break;
for (i = 0; i < pagevec_count(&pvec); i++) {
page = pvec.pages[i];
trace_afs_page_dirty(vnode, tracepoint_string("store+"), page); trace_afs_page_dirty(vnode, tracepoint_string("store+"), page);
if (!clear_page_dirty_for_io(page)) if (!clear_page_dirty_for_io(page))
BUG(); BUG();
if (test_set_page_writeback(page)) if (test_set_page_writeback(page))
BUG(); BUG();
*_count -= thp_nr_pages(page);
unlock_page(page); unlock_page(page);
put_page(page);
}
count += loop;
if (loop < n) {
for (; loop < n; loop++)
put_page(pages[loop]);
goto no_more;
} }
start += loop; pagevec_release(&pvec);
} while (start <= final_page && count < 65536); cond_resched();
} while (!stop);
no_more: *_len = len;
*_count = count;
*_offset = offset;
*_to = to;
} }
/* /*
* Synchronously write back the locked page and any subsequent non-locked dirty * Synchronously write back the locked page and any subsequent non-locked dirty
* pages. * pages.
*/ */
static int afs_write_back_from_locked_page(struct address_space *mapping, static ssize_t afs_write_back_from_locked_page(struct address_space *mapping,
struct writeback_control *wbc, struct writeback_control *wbc,
struct page *primary_page, struct page *page,
pgoff_t final_page) loff_t start, loff_t end)
{ {
struct afs_vnode *vnode = AFS_FS_I(mapping->host); struct afs_vnode *vnode = AFS_FS_I(mapping->host);
struct iov_iter iter; struct iov_iter iter;
unsigned long count, priv; unsigned long priv;
unsigned offset, to; unsigned int offset, to, len, max_len;
pgoff_t start, first, last; loff_t i_size = i_size_read(&vnode->vfs_inode);
loff_t i_size, pos, end;
bool new_content = test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags); bool new_content = test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
long count = wbc->nr_to_write;
int ret; int ret;
_enter(",%lx", primary_page->index); _enter(",%lx,%llx-%llx", page->index, start, end);
count = 1; if (test_set_page_writeback(page))
if (test_set_page_writeback(primary_page))
BUG(); BUG();
count -= thp_nr_pages(page);
/* Find all consecutive lockable dirty pages that have contiguous /* Find all consecutive lockable dirty pages that have contiguous
* written regions, stopping when we find a page that is not * written regions, stopping when we find a page that is not
* immediately lockable, is not dirty or is missing, or we reach the * immediately lockable, is not dirty or is missing, or we reach the
* end of the range. * end of the range.
*/ */
start = primary_page->index; priv = page_private(page);
priv = page_private(primary_page); offset = afs_page_dirty_from(page, priv);
offset = afs_page_dirty_from(primary_page, priv); to = afs_page_dirty_to(page, priv);
to = afs_page_dirty_to(primary_page, priv); trace_afs_page_dirty(vnode, tracepoint_string("store"), page);
trace_afs_page_dirty(vnode, tracepoint_string("store"), primary_page);
len = to - offset;
WARN_ON(offset == to); start += offset;
if (offset == to) if (start < i_size) {
trace_afs_page_dirty(vnode, tracepoint_string("WARN"), primary_page); /* Trim the write to the EOF; the extra data is ignored. Also
* put an upper limit on the size of a single storedata op.
if (start < final_page && */
(to == PAGE_SIZE || new_content)) max_len = 65536 * 4096;
afs_extend_writeback(mapping, vnode, &count, start, final_page, max_len = min_t(unsigned long long, max_len, end - start + 1);
&offset, &to, new_content); max_len = min_t(unsigned long long, max_len, i_size - start);
if (len < max_len &&
(to == thp_size(page) || new_content))
afs_extend_writeback(mapping, vnode, &count,
start, max_len, new_content, &len);
len = min_t(loff_t, len, max_len);
}
/* We now have a contiguous set of dirty pages, each with writeback /* We now have a contiguous set of dirty pages, each with writeback
* set; the first page is still locked at this point, but all the rest * set; the first page is still locked at this point, but all the rest
* have been unlocked. * have been unlocked.
*/ */
unlock_page(primary_page); unlock_page(page);
first = primary_page->index;
last = first + count - 1;
_debug("write back %lx[%u..] to %lx[..%u]", first, offset, last, to);
pos = first;
pos <<= PAGE_SHIFT;
pos += offset;
end = last;
end <<= PAGE_SHIFT;
end += to;
/* Trim the actual write down to the EOF */ if (start < i_size) {
i_size = i_size_read(&vnode->vfs_inode); _debug("write back %x @%llx [%llx]", len, start, i_size);
if (end > i_size)
end = i_size;
if (pos < i_size) { iov_iter_xarray(&iter, WRITE, &mapping->i_pages, start, len);
iov_iter_xarray(&iter, WRITE, &mapping->i_pages, pos, end - pos); ret = afs_store_data(vnode, &iter, start, false);
ret = afs_store_data(vnode, &iter, pos, first, last, false);
} else { } else {
_debug("write discard %x @%llx [%llx]", len, start, i_size);
/* The dirty region was entirely beyond the EOF. */ /* The dirty region was entirely beyond the EOF. */
afs_pages_written_back(vnode, start, len);
ret = 0; ret = 0;
} }
switch (ret) { switch (ret) {
case 0: case 0:
ret = count; wbc->nr_to_write = count;
ret = len;
break; break;
default: default:
...@@ -662,13 +696,13 @@ static int afs_write_back_from_locked_page(struct address_space *mapping, ...@@ -662,13 +696,13 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
case -EKEYEXPIRED: case -EKEYEXPIRED:
case -EKEYREJECTED: case -EKEYREJECTED:
case -EKEYREVOKED: case -EKEYREVOKED:
afs_redirty_pages(wbc, mapping, first, last); afs_redirty_pages(wbc, mapping, start, len);
mapping_set_error(mapping, ret); mapping_set_error(mapping, ret);
break; break;
case -EDQUOT: case -EDQUOT:
case -ENOSPC: case -ENOSPC:
afs_redirty_pages(wbc, mapping, first, last); afs_redirty_pages(wbc, mapping, start, len);
mapping_set_error(mapping, -ENOSPC); mapping_set_error(mapping, -ENOSPC);
break; break;
...@@ -680,7 +714,7 @@ static int afs_write_back_from_locked_page(struct address_space *mapping, ...@@ -680,7 +714,7 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
case -ENOMEDIUM: case -ENOMEDIUM:
case -ENXIO: case -ENXIO:
trace_afs_file_error(vnode, ret, afs_file_error_writeback_fail); trace_afs_file_error(vnode, ret, afs_file_error_writeback_fail);
afs_kill_pages(mapping, first, last); afs_kill_pages(mapping, start, len);
mapping_set_error(mapping, ret); mapping_set_error(mapping, ret);
break; break;
} }
...@@ -695,19 +729,19 @@ static int afs_write_back_from_locked_page(struct address_space *mapping, ...@@ -695,19 +729,19 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
*/ */
int afs_writepage(struct page *page, struct writeback_control *wbc) int afs_writepage(struct page *page, struct writeback_control *wbc)
{ {
int ret; ssize_t ret;
loff_t start;
_enter("{%lx},", page->index); _enter("{%lx},", page->index);
start = page->index * PAGE_SIZE;
ret = afs_write_back_from_locked_page(page->mapping, wbc, page, ret = afs_write_back_from_locked_page(page->mapping, wbc, page,
wbc->range_end >> PAGE_SHIFT); start, LLONG_MAX - start);
if (ret < 0) { if (ret < 0) {
_leave(" = %d", ret); _leave(" = %zd", ret);
return 0; return ret;
} }
wbc->nr_to_write -= ret;
_leave(" = 0"); _leave(" = 0");
return 0; return 0;
} }
...@@ -717,35 +751,46 @@ int afs_writepage(struct page *page, struct writeback_control *wbc) ...@@ -717,35 +751,46 @@ int afs_writepage(struct page *page, struct writeback_control *wbc)
*/ */
static int afs_writepages_region(struct address_space *mapping, static int afs_writepages_region(struct address_space *mapping,
struct writeback_control *wbc, struct writeback_control *wbc,
pgoff_t index, pgoff_t end, pgoff_t *_next) loff_t start, loff_t end, loff_t *_next)
{ {
struct page *page; struct page *page;
int ret, n; ssize_t ret;
int n;
_enter(",,%lx,%lx,", index, end); _enter("%llx,%llx,", start, end);
do { do {
n = find_get_pages_range_tag(mapping, &index, end, pgoff_t index = start / PAGE_SIZE;
PAGECACHE_TAG_DIRTY, 1, &page);
n = find_get_pages_range_tag(mapping, &index, end / PAGE_SIZE,
PAGECACHE_TAG_DIRTY, 1, &page);
if (!n) if (!n)
break; break;
start = (loff_t)page->index * PAGE_SIZE; /* May regress with THPs */
_debug("wback %lx", page->index); _debug("wback %lx", page->index);
/* /* At this point we hold neither the i_pages lock nor the
* at this point we hold neither the i_pages lock nor the
* page lock: the page may be truncated or invalidated * page lock: the page may be truncated or invalidated
* (changing page->mapping to NULL), or even swizzled * (changing page->mapping to NULL), or even swizzled
* back from swapper_space to tmpfs file mapping * back from swapper_space to tmpfs file mapping
*/ */
ret = lock_page_killable(page); if (wbc->sync_mode != WB_SYNC_NONE) {
if (ret < 0) { ret = lock_page_killable(page);
put_page(page); if (ret < 0) {
_leave(" = %d", ret); put_page(page);
return ret; return ret;
}
} else {
if (!trylock_page(page)) {
put_page(page);
return 0;
}
} }
if (page->mapping != mapping || !PageDirty(page)) { if (page->mapping != mapping || !PageDirty(page)) {
start += thp_size(page);
unlock_page(page); unlock_page(page);
put_page(page); put_page(page);
continue; continue;
...@@ -761,20 +806,20 @@ static int afs_writepages_region(struct address_space *mapping, ...@@ -761,20 +806,20 @@ static int afs_writepages_region(struct address_space *mapping,
if (!clear_page_dirty_for_io(page)) if (!clear_page_dirty_for_io(page))
BUG(); BUG();
ret = afs_write_back_from_locked_page(mapping, wbc, page, end); ret = afs_write_back_from_locked_page(mapping, wbc, page, start, end);
put_page(page); put_page(page);
if (ret < 0) { if (ret < 0) {
_leave(" = %d", ret); _leave(" = %zd", ret);
return ret; return ret;
} }
wbc->nr_to_write -= ret; start += ret * PAGE_SIZE;
cond_resched(); cond_resched();
} while (index < end && wbc->nr_to_write > 0); } while (wbc->nr_to_write > 0);
*_next = index; *_next = start;
_leave(" = 0 [%lx]", *_next); _leave(" = 0 [%llx]", *_next);
return 0; return 0;
} }
...@@ -785,7 +830,7 @@ int afs_writepages(struct address_space *mapping, ...@@ -785,7 +830,7 @@ int afs_writepages(struct address_space *mapping,
struct writeback_control *wbc) struct writeback_control *wbc)
{ {
struct afs_vnode *vnode = AFS_FS_I(mapping->host); struct afs_vnode *vnode = AFS_FS_I(mapping->host);
pgoff_t start, end, next; loff_t start, next;
int ret; int ret;
_enter(""); _enter("");
...@@ -800,22 +845,19 @@ int afs_writepages(struct address_space *mapping, ...@@ -800,22 +845,19 @@ int afs_writepages(struct address_space *mapping,
return 0; return 0;
if (wbc->range_cyclic) { if (wbc->range_cyclic) {
start = mapping->writeback_index; start = mapping->writeback_index * PAGE_SIZE;
end = -1; ret = afs_writepages_region(mapping, wbc, start, LLONG_MAX, &next);
ret = afs_writepages_region(mapping, wbc, start, end, &next);
if (start > 0 && wbc->nr_to_write > 0 && ret == 0) if (start > 0 && wbc->nr_to_write > 0 && ret == 0)
ret = afs_writepages_region(mapping, wbc, 0, start, ret = afs_writepages_region(mapping, wbc, 0, start,
&next); &next);
mapping->writeback_index = next; mapping->writeback_index = next / PAGE_SIZE;
} else if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) { } else if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) {
end = (pgoff_t)(LLONG_MAX >> PAGE_SHIFT); ret = afs_writepages_region(mapping, wbc, 0, LLONG_MAX, &next);
ret = afs_writepages_region(mapping, wbc, 0, end, &next);
if (wbc->nr_to_write > 0) if (wbc->nr_to_write > 0)
mapping->writeback_index = next; mapping->writeback_index = next;
} else { } else {
start = wbc->range_start >> PAGE_SHIFT; ret = afs_writepages_region(mapping, wbc,
end = wbc->range_end >> PAGE_SHIFT; wbc->range_start, wbc->range_end, &next);
ret = afs_writepages_region(mapping, wbc, start, end, &next);
} }
up_read(&vnode->validate_lock); up_read(&vnode->validate_lock);
...@@ -873,13 +915,13 @@ int afs_fsync(struct file *file, loff_t start, loff_t end, int datasync) ...@@ -873,13 +915,13 @@ int afs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
*/ */
vm_fault_t afs_page_mkwrite(struct vm_fault *vmf) vm_fault_t afs_page_mkwrite(struct vm_fault *vmf)
{ {
struct page *page = thp_head(vmf->page);
struct file *file = vmf->vma->vm_file; struct file *file = vmf->vma->vm_file;
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
struct afs_vnode *vnode = AFS_FS_I(inode); struct afs_vnode *vnode = AFS_FS_I(inode);
unsigned long priv; unsigned long priv;
_enter("{{%llx:%llu}},{%lx}", _enter("{{%llx:%llu}},{%lx}", vnode->fid.vid, vnode->fid.vnode, page->index);
vnode->fid.vid, vnode->fid.vnode, vmf->page->index);
sb_start_pagefault(inode->i_sb); sb_start_pagefault(inode->i_sb);
...@@ -887,30 +929,32 @@ vm_fault_t afs_page_mkwrite(struct vm_fault *vmf) ...@@ -887,30 +929,32 @@ vm_fault_t afs_page_mkwrite(struct vm_fault *vmf)
* be modified. We then assume the entire page will need writing back. * be modified. We then assume the entire page will need writing back.
*/ */
#ifdef CONFIG_AFS_FSCACHE #ifdef CONFIG_AFS_FSCACHE
if (PageFsCache(vmf->page) && if (PageFsCache(page) &&
wait_on_page_bit_killable(vmf->page, PG_fscache) < 0) wait_on_page_bit_killable(page, PG_fscache) < 0)
return VM_FAULT_RETRY; return VM_FAULT_RETRY;
#endif #endif
if (wait_on_page_writeback_killable(vmf->page)) if (wait_on_page_writeback_killable(page))
return VM_FAULT_RETRY; return VM_FAULT_RETRY;
if (lock_page_killable(vmf->page) < 0) if (lock_page_killable(page) < 0)
return VM_FAULT_RETRY; return VM_FAULT_RETRY;
/* We mustn't change page->private until writeback is complete as that /* We mustn't change page->private until writeback is complete as that
* details the portion of the page we need to write back and we might * details the portion of the page we need to write back and we might
* need to redirty the page if there's a problem. * need to redirty the page if there's a problem.
*/ */
wait_on_page_writeback(vmf->page); wait_on_page_writeback(page);
priv = afs_page_dirty(vmf->page, 0, PAGE_SIZE); priv = afs_page_dirty(page, 0, thp_size(page));
priv = afs_page_dirty_mmapped(priv); priv = afs_page_dirty_mmapped(priv);
if (PagePrivate(vmf->page)) if (PagePrivate(page)) {
set_page_private(vmf->page, priv); set_page_private(page, priv);
else trace_afs_page_dirty(vnode, tracepoint_string("mkwrite+"), page);
attach_page_private(vmf->page, (void *)priv); } else {
trace_afs_page_dirty(vnode, tracepoint_string("mkwrite"), vmf->page); attach_page_private(page, (void *)priv);
trace_afs_page_dirty(vnode, tracepoint_string("mkwrite"), page);
}
file_update_time(file); file_update_time(file);
sb_end_pagefault(inode->i_sb); sb_end_pagefault(inode->i_sb);
...@@ -963,7 +1007,7 @@ int afs_launder_page(struct page *page) ...@@ -963,7 +1007,7 @@ int afs_launder_page(struct page *page)
priv = page_private(page); priv = page_private(page);
if (clear_page_dirty_for_io(page)) { if (clear_page_dirty_for_io(page)) {
f = 0; f = 0;
t = PAGE_SIZE; t = thp_size(page);
if (PagePrivate(page)) { if (PagePrivate(page)) {
f = afs_page_dirty_from(page, priv); f = afs_page_dirty_from(page, priv);
t = afs_page_dirty_to(page, priv); t = afs_page_dirty_to(page, priv);
...@@ -975,12 +1019,12 @@ int afs_launder_page(struct page *page) ...@@ -975,12 +1019,12 @@ int afs_launder_page(struct page *page)
iov_iter_bvec(&iter, WRITE, bv, 1, bv[0].bv_len); iov_iter_bvec(&iter, WRITE, bv, 1, bv[0].bv_len);
trace_afs_page_dirty(vnode, tracepoint_string("launder"), page); trace_afs_page_dirty(vnode, tracepoint_string("launder"), page);
ret = afs_store_data(vnode, &iter, (loff_t)page->index << PAGE_SHIFT, ret = afs_store_data(vnode, &iter, (loff_t)page->index * PAGE_SIZE,
page->index, page->index, true); true);
} }
detach_page_private(page);
trace_afs_page_dirty(vnode, tracepoint_string("laundered"), page); trace_afs_page_dirty(vnode, tracepoint_string("laundered"), page);
detach_page_private(page);
wait_on_page_fscache(page); wait_on_page_fscache(page);
return ret; return ret;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment