2021-04-08 14:11:56

by David Howells

[permalink] [raw]
Subject: [PATCH v6 27/30] afs: Prepare for use of THPs

As a prelude to supporting transparent huge pages, use thp_size() and
similar rather than PAGE_SIZE/SHIFT.

Further, try and frame everything in terms of file positions and lengths
rather than page indices and numbers of pages.

Signed-off-by: David Howells <[email protected]>
cc: [email protected]
cc: [email protected]
cc: [email protected]
Link: https://lore.kernel.org/r/[email protected]rg.uk/ # rfc
Link: https://lore.kernel.org/r/[email protected]g.uk/ # rfc
Link: https://lore.kernel.org/r/[email protected]org.uk/ # v2
Link: https://lore.kernel.org/r/[email protected]rg.uk/ # v3
Link: https://lore.kernel.org/r/[email protected]rg.uk/ # v4
Link: https://lore.kernel.org/r/[email protected]g.uk/ # v5
---

fs/afs/dir.c | 2
fs/afs/file.c | 8 -
fs/afs/internal.h | 2
fs/afs/write.c | 434 +++++++++++++++++++++++++++++------------------------
4 files changed, 244 insertions(+), 202 deletions(-)

diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index 8c093bfff8b6..117df15e5367 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -2083,6 +2083,6 @@ static void afs_dir_invalidatepage(struct page *page, unsigned int offset,
afs_stat_v(dvnode, n_inval);

/* 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);
}
diff --git a/fs/afs/file.c b/fs/afs/file.c
index f1e30b89e41c..edf21c8708a3 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -329,8 +329,8 @@ static int afs_page_filler(struct key *key, struct page *page)
req->vnode = vnode;
req->key = key_get(key);
req->pos = (loff_t)page->index << PAGE_SHIFT;
- req->len = PAGE_SIZE;
- req->nr_pages = 1;
+ req->len = thp_size(page);
+ req->nr_pages = thp_nr_pages(page);
req->done = afs_file_read_done;
req->cleanup = afs_file_read_cleanup;

@@ -574,8 +574,8 @@ static void afs_invalidate_dirty(struct page *page, unsigned int offset,
trace_afs_page_dirty(vnode, tracepoint_string("undirty"), page);
clear_page_dirty_for_io(page);
full_invalidate:
- detach_page_private(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)
#endif

if (PagePrivate(page)) {
- detach_page_private(page);
trace_afs_page_dirty(vnode, tracepoint_string("rel"), page);
+ detach_page_private(page);
}

/* indicate that the page can be released */
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 16020725cc68..9629b6430a52 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -815,8 +815,6 @@ struct afs_operation {
loff_t pos;
loff_t 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 */
} store;
struct {
diff --git a/fs/afs/write.c b/fs/afs/write.c
index 4ccd2c263983..099c7dad09c5 100644
--- a/fs/afs/write.c
+++ b/fs/afs/write.c
@@ -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 page *page;
unsigned long priv;
- unsigned f, from = pos & (PAGE_SIZE - 1);
- unsigned t, to = from + len;
- pgoff_t index = pos >> PAGE_SHIFT;
+ unsigned f, from;
+ unsigned t, to;
+ pgoff_t index;
int ret;

- _enter("{%llx:%llu},{%lx},%u,%u",
- vnode->fid.vid, vnode->fid.vnode, index, from, to);
+ _enter("{%llx:%llu},%llx,%x",
+ 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)
return -ENOMEM;

@@ -121,19 +121,20 @@ int afs_write_begin(struct file *file, struct address_space *mapping,
wait_on_page_fscache(page);
#endif

+ index = page->index;
+ from = pos - index * PAGE_SIZE;
+ to = from + len;
+
try_again:
/* See if this page is already partially written in a way that we can
* merge the new write with.
*/
- t = f = 0;
if (PagePrivate(page)) {
priv = page_private(page);
f = afs_page_dirty_from(page, priv);
t = afs_page_dirty_to(page, priv);
ASSERTCMP(f, <=, t);
- }

- if (f != t) {
if (PageWriteback(page)) {
trace_afs_page_dirty(vnode, tracepoint_string("alrdy"), page);
goto flush_conflicting_write;
@@ -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));
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;
loff_t i_size, maybe_i_size;
int ret = 0;
@@ -233,9 +234,8 @@ int afs_write_end(struct file *file, struct address_space *mapping,
trace_afs_page_dirty(vnode, tracepoint_string("dirty"), page);
}

- set_page_dirty(page);
- if (PageDirty(page))
- _debug("dirtied");
+ if (set_page_dirty(page))
+ _debug("dirtied %lx", page->index);
ret = copied;

out:
@@ -248,40 +248,43 @@ int afs_write_end(struct file *file, struct address_space *mapping,
* kill all the pages in the given range
*/
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 pagevec pv;
- unsigned count, loop;
+ unsigned int loop, psize;

- _enter("{%llx:%llu},%lx-%lx",
- vnode->fid.vid, vnode->fid.vnode, first, last);
+ _enter("{%llx:%llu},%llx @%llx",
+ vnode->fid.vid, vnode->fid.vnode, len, start);

pagevec_init(&pv);

do {
- _debug("kill %lx-%lx", first, last);
+ _debug("kill %llx @%llx", len, start);

- count = last - first + 1;
- if (count > PAGEVEC_SIZE)
- count = PAGEVEC_SIZE;
- pv.nr = find_get_pages_contig(mapping, first, count, pv.pages);
- ASSERTCMP(pv.nr, ==, count);
+ pv.nr = find_get_pages_contig(mapping, start / PAGE_SIZE,
+ PAGEVEC_SIZE, pv.pages);
+ if (pv.nr == 0)
+ break;

- for (loop = 0; loop < count; loop++) {
+ for (loop = 0; loop < pv.nr; 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);
- SetPageError(page);
end_page_writeback(page);
- if (page->index >= first)
- first = page->index + 1;
lock_page(page);
generic_error_remove_page(mapping, page);
unlock_page(page);
}

__pagevec_release(&pv);
- } while (first <= last);
+ } while (len > 0);

_leave("");
}
@@ -291,37 +294,40 @@ static void afs_kill_pages(struct address_space *mapping,
*/
static void afs_redirty_pages(struct writeback_control *wbc,
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 pagevec pv;
- unsigned count, loop;
+ unsigned int loop, psize;

- _enter("{%llx:%llu},%lx-%lx",
- vnode->fid.vid, vnode->fid.vnode, first, last);
+ _enter("{%llx:%llu},%llx @%llx",
+ vnode->fid.vid, vnode->fid.vnode, len, start);

pagevec_init(&pv);

do {
- _debug("redirty %lx-%lx", first, last);
+ _debug("redirty %llx @%llx", len, start);

- count = last - first + 1;
- if (count > PAGEVEC_SIZE)
- count = PAGEVEC_SIZE;
- pv.nr = find_get_pages_contig(mapping, first, count, pv.pages);
- ASSERTCMP(pv.nr, ==, count);
+ pv.nr = find_get_pages_contig(mapping, start / PAGE_SIZE,
+ PAGEVEC_SIZE, pv.pages);
+ if (pv.nr == 0)
+ break;

- for (loop = 0; loop < count; loop++) {
+ for (loop = 0; loop < pv.nr; 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);
end_page_writeback(page);
- if (page->index >= first)
- first = page->index + 1;
}

__pagevec_release(&pv);
- } while (first <= last);
+ } while (len > 0);

_leave("");
}
@@ -329,23 +335,28 @@ static void afs_redirty_pages(struct writeback_control *wbc,
/*
* 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 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}",
- vnode->fid.vid, vnode->fid.vnode, start, last);
+ _enter("{%llx:%llu},{%x @%llx}",
+ vnode->fid.vid, vnode->fid.vnode, len, start);

rcu_read_lock();

- xas_for_each(&xas, page, last) {
- ASSERT(PageWriteback(page));
+ end = (start + len - 1) / PAGE_SIZE;
+ 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);
+ detach_page_private(page);
page_endio(page, true, 0);
}

@@ -404,7 +415,7 @@ static void afs_store_data_success(struct afs_operation *op)
afs_vnode_commit_status(op, &op->file[0]);
if (op->error == 0) {
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);
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 = {
/*
* write to a file
*/
-static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter,
- loff_t pos, pgoff_t first, pgoff_t last,
+static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter, loff_t pos,
bool laundering)
{
struct afs_operation *op;
@@ -453,8 +463,6 @@ static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter,
op->file[0].dv_delta = 1;
op->store.write_iter = iter;
op->store.pos = pos;
- op->store.first = first;
- op->store.last = last;
op->store.size = size;
op->store.i_size = max(pos + size, i_size);
op->store.laundering = laundering;
@@ -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,
struct afs_vnode *vnode,
long *_count,
- pgoff_t start,
- pgoff_t final_page,
- unsigned *_offset,
- unsigned *_to,
- bool new_content)
+ loff_t start,
+ loff_t max_len,
+ bool new_content,
+ unsigned int *_len)
{
- struct page *pages[8], *page;
- unsigned long count = *_count, priv;
- unsigned offset = *_offset, to = *_to, n, f, t;
- int loop;
+ struct pagevec pvec;
+ struct page *page;
+ unsigned long priv;
+ 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 {
- _debug("more %lx [%lx]", start, count);
- n = final_page - start + 1;
- if (n > ARRAY_SIZE(pages))
- n = ARRAY_SIZE(pages);
- n = find_get_pages_contig(mapping, start, ARRAY_SIZE(pages), pages);
- _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;
- }
+ /* Firstly, we gather up a batch of contiguous dirty pages
+ * under the RCU read lock - but we can't clear the dirty flags
+ * there if any of those pages are mapped.
+ */
+ rcu_read_lock();

- for (loop = 0; loop < n; loop++) {
- page = pages[loop];
- if (to != PAGE_SIZE && !new_content)
+ xas_for_each(&xas, page, ULONG_MAX) {
+ stop = true;
+ if (xas_retry(&xas, page))
+ continue;
+ if (xa_is_value(page))
+ break;
+ if (page->index != index)
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;
+
if (!trylock_page(page))
break;
if (!PageDirty(page) || PageWriteback(page)) {
@@ -540,6 +557,7 @@ static void afs_extend_writeback(struct address_space *mapping,
break;
}

+ psize = thp_size(page);
priv = page_private(page);
f = afs_page_dirty_from(page, priv);
t = afs_page_dirty_to(page, priv);
@@ -547,110 +565,126 @@ static void afs_extend_writeback(struct address_space *mapping,
unlock_page(page);
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);

if (!clear_page_dirty_for_io(page))
BUG();
if (test_set_page_writeback(page))
BUG();
+
+ *_count -= thp_nr_pages(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;
- } while (start <= final_page && count < 65536);
+ pagevec_release(&pvec);
+ cond_resched();
+ } while (!stop);

-no_more:
- *_count = count;
- *_offset = offset;
- *_to = to;
+ *_len = len;
}

/*
* Synchronously write back the locked page and any subsequent non-locked dirty
* pages.
*/
-static int afs_write_back_from_locked_page(struct address_space *mapping,
- struct writeback_control *wbc,
- struct page *primary_page,
- pgoff_t final_page)
+static ssize_t afs_write_back_from_locked_page(struct address_space *mapping,
+ struct writeback_control *wbc,
+ struct page *page,
+ loff_t start, loff_t end)
{
struct afs_vnode *vnode = AFS_FS_I(mapping->host);
struct iov_iter iter;
- unsigned long count, priv;
- unsigned offset, to;
- pgoff_t start, first, last;
- loff_t i_size, pos, end;
+ unsigned long priv;
+ unsigned int offset, to, len, max_len;
+ loff_t i_size = i_size_read(&vnode->vfs_inode);
bool new_content = test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
+ long count = wbc->nr_to_write;
int ret;

- _enter(",%lx", primary_page->index);
+ _enter(",%lx,%llx-%llx", page->index, start, end);

- count = 1;
- if (test_set_page_writeback(primary_page))
+ if (test_set_page_writeback(page))
BUG();

+ count -= thp_nr_pages(page);
+
/* Find all consecutive lockable dirty pages that have contiguous
* written regions, stopping when we find a page that is not
* immediately lockable, is not dirty or is missing, or we reach the
* end of the range.
*/
- start = primary_page->index;
- priv = page_private(primary_page);
- offset = afs_page_dirty_from(primary_page, priv);
- to = afs_page_dirty_to(primary_page, priv);
- trace_afs_page_dirty(vnode, tracepoint_string("store"), primary_page);
-
- WARN_ON(offset == to);
- if (offset == to)
- trace_afs_page_dirty(vnode, tracepoint_string("WARN"), primary_page);
-
- if (start < final_page &&
- (to == PAGE_SIZE || new_content))
- afs_extend_writeback(mapping, vnode, &count, start, final_page,
- &offset, &to, new_content);
+ priv = page_private(page);
+ offset = afs_page_dirty_from(page, priv);
+ to = afs_page_dirty_to(page, priv);
+ trace_afs_page_dirty(vnode, tracepoint_string("store"), page);
+
+ len = to - offset;
+ start += offset;
+ if (start < i_size) {
+ /* Trim the write to the EOF; the extra data is ignored. Also
+ * put an upper limit on the size of a single storedata op.
+ */
+ max_len = 65536 * 4096;
+ max_len = min_t(unsigned long long, max_len, end - start + 1);
+ 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
* set; the first page is still locked at this point, but all the rest
* have been unlocked.
*/
- unlock_page(primary_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;
+ unlock_page(page);

- /* Trim the actual write down to the EOF */
- i_size = i_size_read(&vnode->vfs_inode);
- if (end > i_size)
- end = i_size;
+ if (start < i_size) {
+ _debug("write back %x @%llx [%llx]", len, start, i_size);

- if (pos < i_size) {
- iov_iter_xarray(&iter, WRITE, &mapping->i_pages, pos, end - pos);
- ret = afs_store_data(vnode, &iter, pos, first, last, false);
+ iov_iter_xarray(&iter, WRITE, &mapping->i_pages, start, len);
+ ret = afs_store_data(vnode, &iter, start, false);
} else {
+ _debug("write discard %x @%llx [%llx]", len, start, i_size);
+
/* The dirty region was entirely beyond the EOF. */
+ afs_pages_written_back(vnode, start, len);
ret = 0;
}

switch (ret) {
case 0:
- ret = count;
+ wbc->nr_to_write = count;
+ ret = len;
break;

default:
@@ -662,13 +696,13 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
case -EKEYEXPIRED:
case -EKEYREJECTED:
case -EKEYREVOKED:
- afs_redirty_pages(wbc, mapping, first, last);
+ afs_redirty_pages(wbc, mapping, start, len);
mapping_set_error(mapping, ret);
break;

case -EDQUOT:
case -ENOSPC:
- afs_redirty_pages(wbc, mapping, first, last);
+ afs_redirty_pages(wbc, mapping, start, len);
mapping_set_error(mapping, -ENOSPC);
break;

@@ -680,7 +714,7 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
case -ENOMEDIUM:
case -ENXIO:
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);
break;
}
@@ -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 ret;
+ ssize_t ret;
+ loff_t start;

_enter("{%lx},", page->index);

+ start = page->index * PAGE_SIZE;
ret = afs_write_back_from_locked_page(page->mapping, wbc, page,
- wbc->range_end >> PAGE_SHIFT);
+ start, LLONG_MAX - start);
if (ret < 0) {
- _leave(" = %d", ret);
- return 0;
+ _leave(" = %zd", ret);
+ return ret;
}

- wbc->nr_to_write -= ret;
-
_leave(" = 0");
return 0;
}
@@ -717,35 +751,46 @@ int afs_writepage(struct page *page, struct writeback_control *wbc)
*/
static int afs_writepages_region(struct address_space *mapping,
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;
- int ret, n;
+ ssize_t ret;
+ int n;

- _enter(",,%lx,%lx,", index, end);
+ _enter("%llx,%llx,", start, end);

do {
- n = find_get_pages_range_tag(mapping, &index, end,
- PAGECACHE_TAG_DIRTY, 1, &page);
+ pgoff_t index = start / PAGE_SIZE;
+
+ n = find_get_pages_range_tag(mapping, &index, end / PAGE_SIZE,
+ PAGECACHE_TAG_DIRTY, 1, &page);
if (!n)
break;

+ start = (loff_t)page->index * PAGE_SIZE; /* May regress with THPs */
+
_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
* (changing page->mapping to NULL), or even swizzled
* back from swapper_space to tmpfs file mapping
*/
- ret = lock_page_killable(page);
- if (ret < 0) {
- put_page(page);
- _leave(" = %d", ret);
- return ret;
+ if (wbc->sync_mode != WB_SYNC_NONE) {
+ ret = lock_page_killable(page);
+ if (ret < 0) {
+ put_page(page);
+ return ret;
+ }
+ } else {
+ if (!trylock_page(page)) {
+ put_page(page);
+ return 0;
+ }
}

if (page->mapping != mapping || !PageDirty(page)) {
+ start += thp_size(page);
unlock_page(page);
put_page(page);
continue;
@@ -761,20 +806,20 @@ static int afs_writepages_region(struct address_space *mapping,

if (!clear_page_dirty_for_io(page))
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);
if (ret < 0) {
- _leave(" = %d", ret);
+ _leave(" = %zd", ret);
return ret;
}

- wbc->nr_to_write -= ret;
+ start += ret * PAGE_SIZE;

cond_resched();
- } while (index < end && wbc->nr_to_write > 0);
+ } while (wbc->nr_to_write > 0);

- *_next = index;
- _leave(" = 0 [%lx]", *_next);
+ *_next = start;
+ _leave(" = 0 [%llx]", *_next);
return 0;
}

@@ -785,7 +830,7 @@ int afs_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
struct afs_vnode *vnode = AFS_FS_I(mapping->host);
- pgoff_t start, end, next;
+ loff_t start, next;
int ret;

_enter("");
@@ -800,22 +845,19 @@ int afs_writepages(struct address_space *mapping,
return 0;

if (wbc->range_cyclic) {
- start = mapping->writeback_index;
- end = -1;
- ret = afs_writepages_region(mapping, wbc, start, end, &next);
+ start = mapping->writeback_index * PAGE_SIZE;
+ ret = afs_writepages_region(mapping, wbc, start, LLONG_MAX, &next);
if (start > 0 && wbc->nr_to_write > 0 && ret == 0)
ret = afs_writepages_region(mapping, wbc, 0, start,
&next);
- mapping->writeback_index = next;
+ mapping->writeback_index = next / PAGE_SIZE;
} 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, end, &next);
+ ret = afs_writepages_region(mapping, wbc, 0, LLONG_MAX, &next);
if (wbc->nr_to_write > 0)
mapping->writeback_index = next;
} else {
- start = wbc->range_start >> PAGE_SHIFT;
- end = wbc->range_end >> PAGE_SHIFT;
- ret = afs_writepages_region(mapping, wbc, start, end, &next);
+ ret = afs_writepages_region(mapping, wbc,
+ wbc->range_start, wbc->range_end, &next);
}

up_read(&vnode->validate_lock);
@@ -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)
{
+ struct page *page = thp_head(vmf->page);
struct file *file = vmf->vma->vm_file;
struct inode *inode = file_inode(file);
struct afs_vnode *vnode = AFS_FS_I(inode);
unsigned long priv;

- _enter("{{%llx:%llu}},{%lx}",
- vnode->fid.vid, vnode->fid.vnode, vmf->page->index);
+ _enter("{{%llx:%llu}},{%lx}", vnode->fid.vid, vnode->fid.vnode, page->index);

sb_start_pagefault(inode->i_sb);

@@ -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.
*/
#ifdef CONFIG_AFS_FSCACHE
- if (PageFsCache(vmf->page) &&
- wait_on_page_bit_killable(vmf->page, PG_fscache) < 0)
+ if (PageFsCache(page) &&
+ wait_on_page_bit_killable(page, PG_fscache) < 0)
return VM_FAULT_RETRY;
#endif

- if (wait_on_page_writeback_killable(vmf->page))
+ if (wait_on_page_writeback_killable(page))
return VM_FAULT_RETRY;

- if (lock_page_killable(vmf->page) < 0)
+ if (lock_page_killable(page) < 0)
return VM_FAULT_RETRY;

/* 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
* 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);
- if (PagePrivate(vmf->page))
- set_page_private(vmf->page, priv);
- else
- attach_page_private(vmf->page, (void *)priv);
- trace_afs_page_dirty(vnode, tracepoint_string("mkwrite"), vmf->page);
+ if (PagePrivate(page)) {
+ set_page_private(page, priv);
+ trace_afs_page_dirty(vnode, tracepoint_string("mkwrite+"), page);
+ } else {
+ attach_page_private(page, (void *)priv);
+ trace_afs_page_dirty(vnode, tracepoint_string("mkwrite"), page);
+ }
file_update_time(file);

sb_end_pagefault(inode->i_sb);
@@ -963,7 +1007,7 @@ int afs_launder_page(struct page *page)
priv = page_private(page);
if (clear_page_dirty_for_io(page)) {
f = 0;
- t = PAGE_SIZE;
+ t = thp_size(page);
if (PagePrivate(page)) {
f = afs_page_dirty_from(page, priv);
t = afs_page_dirty_to(page, priv);
@@ -975,12 +1019,12 @@ int afs_launder_page(struct page *page)
iov_iter_bvec(&iter, WRITE, bv, 1, bv[0].bv_len);

trace_afs_page_dirty(vnode, tracepoint_string("launder"), page);
- ret = afs_store_data(vnode, &iter, (loff_t)page->index << PAGE_SHIFT,
- page->index, page->index, true);
+ ret = afs_store_data(vnode, &iter, (loff_t)page->index * PAGE_SIZE,
+ true);
}

- detach_page_private(page);
trace_afs_page_dirty(vnode, tracepoint_string("laundered"), page);
+ detach_page_private(page);
wait_on_page_fscache(page);
return ret;
}