<feed xmlns='http://www.w3.org/2005/Atom'>
<title>linux.git/fs/afs/write.c, branch v5.10</title>
<subtitle>Linux kernel source tree</subtitle>
<link rel='alternate' type='text/html' href='https://git.tavy.me/linux.git/'/>
<entry>
<title>afs: Fix afs_write_end() when called with copied == 0 [ver #3]</title>
<updated>2020-11-14T19:51:18+00:00</updated>
<author>
<name>David Howells</name>
<email>dhowells@redhat.com</email>
</author>
<published>2020-11-14T17:27:57+00:00</published>
<link rel='alternate' type='text/html' href='https://git.tavy.me/linux.git/commit/?id=3ad216ee73abc554ed8f13f4f8b70845a7bef6da'/>
<id>3ad216ee73abc554ed8f13f4f8b70845a7bef6da</id>
<content type='text'>
When afs_write_end() is called with copied == 0, it tries to set the
dirty region, but there's no way to actually encode a 0-length region in
the encoding in page-&gt;private.

"0,0", for example, indicates a 1-byte region at offset 0.  The maths
miscalculates this and sets it incorrectly.

Fix it to just do nothing but unlock and put the page in this case.  We
don't actually need to mark the page dirty as nothing presumably
changed.

Fixes: 65dd2d6072d3 ("afs: Alter dirty range encoding in page-&gt;private")
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
Signed-off-by: Linus Torvalds &lt;torvalds@linux-foundation.org&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
When afs_write_end() is called with copied == 0, it tries to set the
dirty region, but there's no way to actually encode a 0-length region in
the encoding in page-&gt;private.

"0,0", for example, indicates a 1-byte region at offset 0.  The maths
miscalculates this and sets it incorrectly.

Fix it to just do nothing but unlock and put the page in this case.  We
don't actually need to mark the page dirty as nothing presumably
changed.

Fixes: 65dd2d6072d3 ("afs: Alter dirty range encoding in page-&gt;private")
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
Signed-off-by: Linus Torvalds &lt;torvalds@linux-foundation.org&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>afs: Fix dirty-region encoding on ppc32 with 64K pages</title>
<updated>2020-10-29T13:53:04+00:00</updated>
<author>
<name>David Howells</name>
<email>dhowells@redhat.com</email>
</author>
<published>2020-10-28T12:08:39+00:00</published>
<link rel='alternate' type='text/html' href='https://git.tavy.me/linux.git/commit/?id=2d9900f26ad61e63a34f239bc76c80d2f8a6ff41'/>
<id>2d9900f26ad61e63a34f239bc76c80d2f8a6ff41</id>
<content type='text'>
The dirty region bounds stored in page-&gt;private on an afs page are 15 bits
on a 32-bit box and can, at most, represent a range of up to 32K within a
32K page with a resolution of 1 byte.  This is a problem for powerpc32 with
64K pages enabled.

Further, transparent huge pages may get up to 2M, which will be a problem
for the afs filesystem on all 32-bit arches in the future.

Fix this by decreasing the resolution.  For the moment, a 64K page will
have a resolution determined from PAGE_SIZE.  In the future, the page will
need to be passed in to the helper functions so that the page size can be
assessed and the resolution determined dynamically.

Note that this might not be the ideal way to handle this, since it may
allow some leakage of undirtied zero bytes to the server's copy in the case
of a 3rd-party conflict.  Fixing that would require a separately allocated
record and is a more complicated fix.

Fixes: 4343d00872e1 ("afs: Get rid of the afs_writeback record")
Reported-by: kernel test robot &lt;lkp@intel.com&gt;
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
Reviewed-by: Matthew Wilcox (Oracle) &lt;willy@infradead.org&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The dirty region bounds stored in page-&gt;private on an afs page are 15 bits
on a 32-bit box and can, at most, represent a range of up to 32K within a
32K page with a resolution of 1 byte.  This is a problem for powerpc32 with
64K pages enabled.

Further, transparent huge pages may get up to 2M, which will be a problem
for the afs filesystem on all 32-bit arches in the future.

Fix this by decreasing the resolution.  For the moment, a 64K page will
have a resolution determined from PAGE_SIZE.  In the future, the page will
need to be passed in to the helper functions so that the page size can be
assessed and the resolution determined dynamically.

Note that this might not be the ideal way to handle this, since it may
allow some leakage of undirtied zero bytes to the server's copy in the case
of a 3rd-party conflict.  Fixing that would require a separately allocated
record and is a more complicated fix.

Fixes: 4343d00872e1 ("afs: Get rid of the afs_writeback record")
Reported-by: kernel test robot &lt;lkp@intel.com&gt;
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
Reviewed-by: Matthew Wilcox (Oracle) &lt;willy@infradead.org&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>afs: Fix afs_invalidatepage to adjust the dirty region</title>
<updated>2020-10-29T13:53:04+00:00</updated>
<author>
<name>David Howells</name>
<email>dhowells@redhat.com</email>
</author>
<published>2020-10-22T13:08:23+00:00</published>
<link rel='alternate' type='text/html' href='https://git.tavy.me/linux.git/commit/?id=f86726a69dec5df6ba051baf9265584419478b64'/>
<id>f86726a69dec5df6ba051baf9265584419478b64</id>
<content type='text'>
Fix afs_invalidatepage() to adjust the dirty region recorded in
page-&gt;private when truncating a page.  If the dirty region is entirely
removed, then the private data is cleared and the page dirty state is
cleared.

Without this, if the page is truncated and then expanded again by truncate,
zeros from the expanded, but no-longer dirty region may get written back to
the server if the page gets laundered due to a conflicting 3rd-party write.

It mustn't, however, shorten the dirty region of the page if that page is
still mmapped and has been marked dirty by afs_page_mkwrite(), so a flag is
stored in page-&gt;private to record this.

Fixes: 4343d00872e1 ("afs: Get rid of the afs_writeback record")
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Fix afs_invalidatepage() to adjust the dirty region recorded in
page-&gt;private when truncating a page.  If the dirty region is entirely
removed, then the private data is cleared and the page dirty state is
cleared.

Without this, if the page is truncated and then expanded again by truncate,
zeros from the expanded, but no-longer dirty region may get written back to
the server if the page gets laundered due to a conflicting 3rd-party write.

It mustn't, however, shorten the dirty region of the page if that page is
still mmapped and has been marked dirty by afs_page_mkwrite(), so a flag is
stored in page-&gt;private to record this.

Fixes: 4343d00872e1 ("afs: Get rid of the afs_writeback record")
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>afs: Alter dirty range encoding in page-&gt;private</title>
<updated>2020-10-29T13:53:04+00:00</updated>
<author>
<name>David Howells</name>
<email>dhowells@redhat.com</email>
</author>
<published>2020-10-26T13:57:44+00:00</published>
<link rel='alternate' type='text/html' href='https://git.tavy.me/linux.git/commit/?id=65dd2d6072d393a3aa14ded8afa9a12f27d9c8ad'/>
<id>65dd2d6072d393a3aa14ded8afa9a12f27d9c8ad</id>
<content type='text'>
Currently, page-&gt;private on an afs page is used to store the range of
dirtied data within the page, where the range includes the lower bound, but
excludes the upper bound (e.g. 0-1 is a range covering a single byte).

This, however, requires a superfluous bit for the last-byte bound so that
on a 4KiB page, it can say 0-4096 to indicate the whole page, the idea
being that having both numbers the same would indicate an empty range.
This is unnecessary as the PG_private bit is clear if it's an empty range
(as is PG_dirty).

Alter the way the dirty range is encoded in page-&gt;private such that the
upper bound is reduced by 1 (e.g. 0-0 is then specified the same single
byte range mentioned above).

Applying this to both bounds frees up two bits, one of which can be used in
a future commit.

This allows the afs filesystem to be compiled on ppc32 with 64K pages;
without this, the following warnings are seen:

../fs/afs/internal.h: In function 'afs_page_dirty_to':
../fs/afs/internal.h:881:15: warning: right shift count &gt;= width of type [-Wshift-count-overflow]
  881 |  return (priv &gt;&gt; __AFS_PAGE_PRIV_SHIFT) &amp; __AFS_PAGE_PRIV_MASK;
      |               ^~
../fs/afs/internal.h: In function 'afs_page_dirty':
../fs/afs/internal.h:886:28: warning: left shift count &gt;= width of type [-Wshift-count-overflow]
  886 |  return ((unsigned long)to &lt;&lt; __AFS_PAGE_PRIV_SHIFT) | from;
      |                            ^~

Fixes: 4343d00872e1 ("afs: Get rid of the afs_writeback record")
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Currently, page-&gt;private on an afs page is used to store the range of
dirtied data within the page, where the range includes the lower bound, but
excludes the upper bound (e.g. 0-1 is a range covering a single byte).

This, however, requires a superfluous bit for the last-byte bound so that
on a 4KiB page, it can say 0-4096 to indicate the whole page, the idea
being that having both numbers the same would indicate an empty range.
This is unnecessary as the PG_private bit is clear if it's an empty range
(as is PG_dirty).

Alter the way the dirty range is encoded in page-&gt;private such that the
upper bound is reduced by 1 (e.g. 0-0 is then specified the same single
byte range mentioned above).

Applying this to both bounds frees up two bits, one of which can be used in
a future commit.

This allows the afs filesystem to be compiled on ppc32 with 64K pages;
without this, the following warnings are seen:

../fs/afs/internal.h: In function 'afs_page_dirty_to':
../fs/afs/internal.h:881:15: warning: right shift count &gt;= width of type [-Wshift-count-overflow]
  881 |  return (priv &gt;&gt; __AFS_PAGE_PRIV_SHIFT) &amp; __AFS_PAGE_PRIV_MASK;
      |               ^~
../fs/afs/internal.h: In function 'afs_page_dirty':
../fs/afs/internal.h:886:28: warning: left shift count &gt;= width of type [-Wshift-count-overflow]
  886 |  return ((unsigned long)to &lt;&lt; __AFS_PAGE_PRIV_SHIFT) | from;
      |                            ^~

Fixes: 4343d00872e1 ("afs: Get rid of the afs_writeback record")
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>afs: Wrap page-&gt;private manipulations in inline functions</title>
<updated>2020-10-29T13:53:04+00:00</updated>
<author>
<name>David Howells</name>
<email>dhowells@redhat.com</email>
</author>
<published>2020-10-26T13:22:47+00:00</published>
<link rel='alternate' type='text/html' href='https://git.tavy.me/linux.git/commit/?id=185f0c7073bd5c78f86265f703f5daf1306ab5a7'/>
<id>185f0c7073bd5c78f86265f703f5daf1306ab5a7</id>
<content type='text'>
The afs filesystem uses page-&gt;private to store the dirty range within a
page such that in the event of a conflicting 3rd-party write to the server,
we write back just the bits that got changed locally.

However, there are a couple of problems with this:

 (1) I need a bit to note if the page might be mapped so that partial
     invalidation doesn't shrink the range.

 (2) There aren't necessarily sufficient bits to store the entire range of
     data altered (say it's a 32-bit system with 64KiB pages or transparent
     huge pages are in use).

So wrap the accesses in inline functions so that future commits can change
how this works.

Also move them out of the tracing header into the in-directory header.
There's not really any need for them to be in the tracing header.

Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The afs filesystem uses page-&gt;private to store the dirty range within a
page such that in the event of a conflicting 3rd-party write to the server,
we write back just the bits that got changed locally.

However, there are a couple of problems with this:

 (1) I need a bit to note if the page might be mapped so that partial
     invalidation doesn't shrink the range.

 (2) There aren't necessarily sufficient bits to store the entire range of
     data altered (say it's a 32-bit system with 64KiB pages or transparent
     huge pages are in use).

So wrap the accesses in inline functions so that future commits can change
how this works.

Also move them out of the tracing header into the in-directory header.
There's not really any need for them to be in the tracing header.

Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>afs: Fix where page-&gt;private is set during write</title>
<updated>2020-10-29T13:53:04+00:00</updated>
<author>
<name>David Howells</name>
<email>dhowells@redhat.com</email>
</author>
<published>2020-10-26T14:05:33+00:00</published>
<link rel='alternate' type='text/html' href='https://git.tavy.me/linux.git/commit/?id=f792e3ac82fe2c6c863e93187eb7ddfccab68fa7'/>
<id>f792e3ac82fe2c6c863e93187eb7ddfccab68fa7</id>
<content type='text'>
In afs, page-&gt;private is set to indicate the dirty region of a page.  This
is done in afs_write_begin(), but that can't take account of whether the
copy into the page actually worked.

Fix this by moving the change of page-&gt;private into afs_write_end().

Fixes: 4343d00872e1 ("afs: Get rid of the afs_writeback record")
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
In afs, page-&gt;private is set to indicate the dirty region of a page.  This
is done in afs_write_begin(), but that can't take account of whether the
copy into the page actually worked.

Fix this by moving the change of page-&gt;private into afs_write_end().

Fixes: 4343d00872e1 ("afs: Get rid of the afs_writeback record")
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>afs: Fix page leak on afs_write_begin() failure</title>
<updated>2020-10-29T13:53:04+00:00</updated>
<author>
<name>David Howells</name>
<email>dhowells@redhat.com</email>
</author>
<published>2020-10-22T13:03:03+00:00</published>
<link rel='alternate' type='text/html' href='https://git.tavy.me/linux.git/commit/?id=21db2cdc667f744691a407105b7712bc18d74023'/>
<id>21db2cdc667f744691a407105b7712bc18d74023</id>
<content type='text'>
Fix the leak of the target page in afs_write_begin() when it fails.

Fixes: 15b4650e55e0 ("afs: convert to new aops")
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
cc: Nick Piggin &lt;npiggin@gmail.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Fix the leak of the target page in afs_write_begin() when it fails.

Fixes: 15b4650e55e0 ("afs: convert to new aops")
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
cc: Nick Piggin &lt;npiggin@gmail.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>afs: Fix to take ref on page when PG_private is set</title>
<updated>2020-10-29T13:53:04+00:00</updated>
<author>
<name>David Howells</name>
<email>dhowells@redhat.com</email>
</author>
<published>2020-10-21T12:22:19+00:00</published>
<link rel='alternate' type='text/html' href='https://git.tavy.me/linux.git/commit/?id=fa04a40b169fcee615afbae97f71a09332993f64'/>
<id>fa04a40b169fcee615afbae97f71a09332993f64</id>
<content type='text'>
Fix afs to take a ref on a page when it sets PG_private on it and to drop
the ref when removing the flag.

Note that in afs_write_begin(), a lot of the time, PG_private is already
set on a page to which we're going to add some data.  In such a case, we
leave the bit set and mustn't increment the page count.

As suggested by Matthew Wilcox, use attach/detach_page_private() where
possible.

Fixes: 31143d5d515e ("AFS: implement basic file write support")
Reported-by: Matthew Wilcox (Oracle) &lt;willy@infradead.org&gt;
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
Reviewed-by: Matthew Wilcox (Oracle) &lt;willy@infradead.org&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Fix afs to take a ref on a page when it sets PG_private on it and to drop
the ref when removing the flag.

Note that in afs_write_begin(), a lot of the time, PG_private is already
set on a page to which we're going to add some data.  In such a case, we
leave the bit set and mustn't increment the page count.

As suggested by Matthew Wilcox, use attach/detach_page_private() where
possible.

Fixes: 31143d5d515e ("AFS: implement basic file write support")
Reported-by: Matthew Wilcox (Oracle) &lt;willy@infradead.org&gt;
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
Reviewed-by: Matthew Wilcox (Oracle) &lt;willy@infradead.org&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>afs: Fix afs_launder_page to not clear PG_writeback</title>
<updated>2020-10-27T22:05:56+00:00</updated>
<author>
<name>David Howells</name>
<email>dhowells@redhat.com</email>
</author>
<published>2020-10-22T13:40:31+00:00</published>
<link rel='alternate' type='text/html' href='https://git.tavy.me/linux.git/commit/?id=d383e346f97d6bb0d654bb3d63c44ab106d92d29'/>
<id>d383e346f97d6bb0d654bb3d63c44ab106d92d29</id>
<content type='text'>
Fix afs_launder_page() to not clear PG_writeback on the page it is
laundering as the flag isn't set in this case.

Fixes: 4343d00872e1 ("afs: Get rid of the afs_writeback record")
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Fix afs_launder_page() to not clear PG_writeback on the page it is
laundering as the flag isn't set in this case.

Fixes: 4343d00872e1 ("afs: Get rid of the afs_writeback record")
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>afs: Fix deadlock between writeback and truncate</title>
<updated>2020-10-08T17:50:55+00:00</updated>
<author>
<name>David Howells</name>
<email>dhowells@redhat.com</email>
</author>
<published>2020-10-07T13:22:12+00:00</published>
<link rel='alternate' type='text/html' href='https://git.tavy.me/linux.git/commit/?id=ec0fa0b659144d9c68204d23f627b6a65fa53e50'/>
<id>ec0fa0b659144d9c68204d23f627b6a65fa53e50</id>
<content type='text'>
The afs filesystem has a lock[*] that it uses to serialise I/O operations
going to the server (vnode-&gt;io_lock), as the server will only perform one
modification operation at a time on any given file or directory.  This
prevents the the filesystem from filling up all the call slots to a server
with calls that aren't going to be executed in parallel anyway, thereby
allowing operations on other files to obtain slots.

  [*] Note that is probably redundant for directories at least since
      i_rwsem is used to serialise directory modifications and
      lookup/reading vs modification.  The server does allow parallel
      non-modification ops, however.

When a file truncation op completes, we truncate the in-memory copy of the
file to match - but we do it whilst still holding the io_lock, the idea
being to prevent races with other operations.

However, if writeback starts in a worker thread simultaneously with
truncation (whilst notify_change() is called with i_rwsem locked, writeback
pays it no heed), it may manage to set PG_writeback bits on the pages that
will get truncated before afs_setattr_success() manages to call
truncate_pagecache().  Truncate will then wait for those pages - whilst
still inside io_lock:

    # cat /proc/8837/stack
    [&lt;0&gt;] wait_on_page_bit_common+0x184/0x1e7
    [&lt;0&gt;] truncate_inode_pages_range+0x37f/0x3eb
    [&lt;0&gt;] truncate_pagecache+0x3c/0x53
    [&lt;0&gt;] afs_setattr_success+0x4d/0x6e
    [&lt;0&gt;] afs_wait_for_operation+0xd8/0x169
    [&lt;0&gt;] afs_do_sync_operation+0x16/0x1f
    [&lt;0&gt;] afs_setattr+0x1fb/0x25d
    [&lt;0&gt;] notify_change+0x2cf/0x3c4
    [&lt;0&gt;] do_truncate+0x7f/0xb2
    [&lt;0&gt;] do_sys_ftruncate+0xd1/0x104
    [&lt;0&gt;] do_syscall_64+0x2d/0x3a
    [&lt;0&gt;] entry_SYSCALL_64_after_hwframe+0x44/0xa9

The writeback operation, however, stalls indefinitely because it needs to
get the io_lock to proceed:

    # cat /proc/5940/stack
    [&lt;0&gt;] afs_get_io_locks+0x58/0x1ae
    [&lt;0&gt;] afs_begin_vnode_operation+0xc7/0xd1
    [&lt;0&gt;] afs_store_data+0x1b2/0x2a3
    [&lt;0&gt;] afs_write_back_from_locked_page+0x418/0x57c
    [&lt;0&gt;] afs_writepages_region+0x196/0x224
    [&lt;0&gt;] afs_writepages+0x74/0x156
    [&lt;0&gt;] do_writepages+0x2d/0x56
    [&lt;0&gt;] __writeback_single_inode+0x84/0x207
    [&lt;0&gt;] writeback_sb_inodes+0x238/0x3cf
    [&lt;0&gt;] __writeback_inodes_wb+0x68/0x9f
    [&lt;0&gt;] wb_writeback+0x145/0x26c
    [&lt;0&gt;] wb_do_writeback+0x16a/0x194
    [&lt;0&gt;] wb_workfn+0x74/0x177
    [&lt;0&gt;] process_one_work+0x174/0x264
    [&lt;0&gt;] worker_thread+0x117/0x1b9
    [&lt;0&gt;] kthread+0xec/0xf1
    [&lt;0&gt;] ret_from_fork+0x1f/0x30

and thus deadlock has occurred.

Note that whilst afs_setattr() calls filemap_write_and_wait(), the fact
that the caller is holding i_rwsem doesn't preclude more pages being
dirtied through an mmap'd region.

Fix this by:

 (1) Use the vnode validate_lock to mediate access between afs_setattr()
     and afs_writepages():

     (a) Exclusively lock validate_lock in afs_setattr() around the whole
     	 RPC operation.

     (b) If WB_SYNC_ALL isn't set on entry to afs_writepages(), trying to
     	 shared-lock validate_lock and returning immediately if we couldn't
     	 get it.

     (c) If WB_SYNC_ALL is set, wait for the lock.

     The validate_lock is also used to validate a file and to zap its cache
     if the file was altered by a third party, so it's probably a good fit
     for this.

 (2) Move the truncation outside of the io_lock in setattr, using the same
     hook as is used for local directory editing.

     This requires the old i_size to be retained in the operation record as
     we commit the revised status to the inode members inside the io_lock
     still, but we still need to know if we reduced the file size.

Fixes: d2ddc776a458 ("afs: Overhaul volume and server record caching and fileserver rotation")
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
Signed-off-by: Linus Torvalds &lt;torvalds@linux-foundation.org&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The afs filesystem has a lock[*] that it uses to serialise I/O operations
going to the server (vnode-&gt;io_lock), as the server will only perform one
modification operation at a time on any given file or directory.  This
prevents the the filesystem from filling up all the call slots to a server
with calls that aren't going to be executed in parallel anyway, thereby
allowing operations on other files to obtain slots.

  [*] Note that is probably redundant for directories at least since
      i_rwsem is used to serialise directory modifications and
      lookup/reading vs modification.  The server does allow parallel
      non-modification ops, however.

When a file truncation op completes, we truncate the in-memory copy of the
file to match - but we do it whilst still holding the io_lock, the idea
being to prevent races with other operations.

However, if writeback starts in a worker thread simultaneously with
truncation (whilst notify_change() is called with i_rwsem locked, writeback
pays it no heed), it may manage to set PG_writeback bits on the pages that
will get truncated before afs_setattr_success() manages to call
truncate_pagecache().  Truncate will then wait for those pages - whilst
still inside io_lock:

    # cat /proc/8837/stack
    [&lt;0&gt;] wait_on_page_bit_common+0x184/0x1e7
    [&lt;0&gt;] truncate_inode_pages_range+0x37f/0x3eb
    [&lt;0&gt;] truncate_pagecache+0x3c/0x53
    [&lt;0&gt;] afs_setattr_success+0x4d/0x6e
    [&lt;0&gt;] afs_wait_for_operation+0xd8/0x169
    [&lt;0&gt;] afs_do_sync_operation+0x16/0x1f
    [&lt;0&gt;] afs_setattr+0x1fb/0x25d
    [&lt;0&gt;] notify_change+0x2cf/0x3c4
    [&lt;0&gt;] do_truncate+0x7f/0xb2
    [&lt;0&gt;] do_sys_ftruncate+0xd1/0x104
    [&lt;0&gt;] do_syscall_64+0x2d/0x3a
    [&lt;0&gt;] entry_SYSCALL_64_after_hwframe+0x44/0xa9

The writeback operation, however, stalls indefinitely because it needs to
get the io_lock to proceed:

    # cat /proc/5940/stack
    [&lt;0&gt;] afs_get_io_locks+0x58/0x1ae
    [&lt;0&gt;] afs_begin_vnode_operation+0xc7/0xd1
    [&lt;0&gt;] afs_store_data+0x1b2/0x2a3
    [&lt;0&gt;] afs_write_back_from_locked_page+0x418/0x57c
    [&lt;0&gt;] afs_writepages_region+0x196/0x224
    [&lt;0&gt;] afs_writepages+0x74/0x156
    [&lt;0&gt;] do_writepages+0x2d/0x56
    [&lt;0&gt;] __writeback_single_inode+0x84/0x207
    [&lt;0&gt;] writeback_sb_inodes+0x238/0x3cf
    [&lt;0&gt;] __writeback_inodes_wb+0x68/0x9f
    [&lt;0&gt;] wb_writeback+0x145/0x26c
    [&lt;0&gt;] wb_do_writeback+0x16a/0x194
    [&lt;0&gt;] wb_workfn+0x74/0x177
    [&lt;0&gt;] process_one_work+0x174/0x264
    [&lt;0&gt;] worker_thread+0x117/0x1b9
    [&lt;0&gt;] kthread+0xec/0xf1
    [&lt;0&gt;] ret_from_fork+0x1f/0x30

and thus deadlock has occurred.

Note that whilst afs_setattr() calls filemap_write_and_wait(), the fact
that the caller is holding i_rwsem doesn't preclude more pages being
dirtied through an mmap'd region.

Fix this by:

 (1) Use the vnode validate_lock to mediate access between afs_setattr()
     and afs_writepages():

     (a) Exclusively lock validate_lock in afs_setattr() around the whole
     	 RPC operation.

     (b) If WB_SYNC_ALL isn't set on entry to afs_writepages(), trying to
     	 shared-lock validate_lock and returning immediately if we couldn't
     	 get it.

     (c) If WB_SYNC_ALL is set, wait for the lock.

     The validate_lock is also used to validate a file and to zap its cache
     if the file was altered by a third party, so it's probably a good fit
     for this.

 (2) Move the truncation outside of the io_lock in setattr, using the same
     hook as is used for local directory editing.

     This requires the old i_size to be retained in the operation record as
     we commit the revised status to the inode members inside the io_lock
     still, but we still need to know if we reduced the file size.

Fixes: d2ddc776a458 ("afs: Overhaul volume and server record caching and fileserver rotation")
Signed-off-by: David Howells &lt;dhowells@redhat.com&gt;
Signed-off-by: Linus Torvalds &lt;torvalds@linux-foundation.org&gt;
</pre>
</div>
</content>
</entry>
</feed>
