--- /dev/null
+From 9417815157557d273325641f7ad2b6dd147276f4 Mon Sep 17 00:00:00 2001
+From: Maxim Patlasov <MPatlasov@parallels.com>
+Date: Fri, 30 Aug 2013 17:06:04 +0400
+Subject: fuse: hotfix truncate_pagecache() issue
+
+From: Maxim Patlasov <MPatlasov@parallels.com>
+
+commit 06a7c3c2781409af95000c60a5df743fd4e2f8b4 upstream.
+
+The way how fuse calls truncate_pagecache() from fuse_change_attributes()
+is completely wrong. Because, w/o i_mutex held, we never sure whether
+'oldsize' and 'attr->size' are valid by the time of execution of
+truncate_pagecache(inode, oldsize, attr->size). In fact, as soon as we
+released fc->lock in the middle of fuse_change_attributes(), we completely
+loose control of actions which may happen with given inode until we reach
+truncate_pagecache. The list of potentially dangerous actions includes
+mmap-ed reads and writes, ftruncate(2) and write(2) extending file size.
+
+The typical outcome of doing truncate_pagecache() with outdated arguments
+is data corruption from user point of view. This is (in some sense)
+acceptable in cases when the issue is triggered by a change of the file on
+the server (i.e. externally wrt fuse operation), but it is absolutely
+intolerable in scenarios when a single fuse client modifies a file without
+any external intervention. A real life case I discovered by fsx-linux
+looked like this:
+
+1. Shrinking ftruncate(2) comes to fuse_do_setattr(). The latter sends
+FUSE_SETATTR to the server synchronously, but before getting fc->lock ...
+2. fuse_dentry_revalidate() is asynchronously called. It sends FUSE_LOOKUP
+to the server synchronously, then calls fuse_change_attributes(). The
+latter updates i_size, releases fc->lock, but before comparing oldsize vs
+attr->size..
+3. fuse_do_setattr() from the first step proceeds by acquiring fc->lock and
+updating attributes and i_size, but now oldsize is equal to
+outarg.attr.size because i_size has just been updated (step 2). Hence,
+fuse_do_setattr() returns w/o calling truncate_pagecache().
+4. As soon as ftruncate(2) completes, the user extends file size by
+write(2) making a hole in the middle of file, then reads data from the hole
+either by read(2) or mmap-ed read. The user expects to get zero data from
+the hole, but gets stale data because truncate_pagecache() is not executed
+yet.
+
+The scenario above illustrates one side of the problem: not truncating the
+page cache even though we should. Another side corresponds to truncating
+page cache too late, when the state of inode changed significantly.
+Theoretically, the following is possible:
+
+1. As in the previous scenario fuse_dentry_revalidate() discovered that
+i_size changed (due to our own fuse_do_setattr()) and is going to call
+truncate_pagecache() for some 'new_size' it believes valid right now. But
+by the time that particular truncate_pagecache() is called ...
+2. fuse_do_setattr() returns (either having called truncate_pagecache() or
+not -- it doesn't matter).
+3. The file is extended either by write(2) or ftruncate(2) or fallocate(2).
+4. mmap-ed write makes a page in the extended region dirty.
+
+The result will be the lost of data user wrote on the fourth step.
+
+The patch is a hotfix resolving the issue in a simplistic way: let's skip
+dangerous i_size update and truncate_pagecache if an operation changing
+file size is in progress. This simplistic approach looks correct for the
+cases w/o external changes. And to handle them properly, more sophisticated
+and intrusive techniques (e.g. NFS-like one) would be required. I'd like to
+postpone it until the issue is well discussed on the mailing list(s).
+
+Changed in v2:
+ - improved patch description to cover both sides of the issue.
+
+Signed-off-by: Maxim Patlasov <mpatlasov@parallels.com>
+Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
+[bwh: Backported to 3.2: add the fuse_inode::state field which we didn't have]
+Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
+Cc: Rui Xiang <rui.xiang@huawei.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/fuse/dir.c | 7 ++++++-
+ fs/fuse/file.c | 8 +++++++-
+ fs/fuse/fuse_i.h | 9 +++++++++
+ fs/fuse/inode.c | 4 +++-
+ 4 files changed, 25 insertions(+), 3 deletions(-)
+
+--- a/fs/fuse/dir.c
++++ b/fs/fuse/dir.c
+@@ -1348,6 +1348,7 @@ static int fuse_do_setattr(struct dentry
+ {
+ struct inode *inode = entry->d_inode;
+ struct fuse_conn *fc = get_fuse_conn(inode);
++ struct fuse_inode *fi = get_fuse_inode(inode);
+ struct fuse_req *req;
+ struct fuse_setattr_in inarg;
+ struct fuse_attr_out outarg;
+@@ -1378,8 +1379,10 @@ static int fuse_do_setattr(struct dentry
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+
+- if (is_truncate)
++ if (is_truncate) {
+ fuse_set_nowrite(inode);
++ set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
++ }
+
+ memset(&inarg, 0, sizeof(inarg));
+ memset(&outarg, 0, sizeof(outarg));
+@@ -1441,12 +1444,14 @@ static int fuse_do_setattr(struct dentry
+ invalidate_inode_pages2(inode->i_mapping);
+ }
+
++ clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
+ return 0;
+
+ error:
+ if (is_truncate)
+ fuse_release_nowrite(inode);
+
++ clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
+ return err;
+ }
+
+--- a/fs/fuse/file.c
++++ b/fs/fuse/file.c
+@@ -515,7 +515,8 @@ static void fuse_read_update_size(struct
+ struct fuse_inode *fi = get_fuse_inode(inode);
+
+ spin_lock(&fc->lock);
+- if (attr_ver == fi->attr_version && size < inode->i_size) {
++ if (attr_ver == fi->attr_version && size < inode->i_size &&
++ !test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) {
+ fi->attr_version = ++fc->attr_version;
+ i_size_write(inode, size);
+ }
+@@ -877,12 +878,16 @@ static ssize_t fuse_perform_write(struct
+ {
+ struct inode *inode = mapping->host;
+ struct fuse_conn *fc = get_fuse_conn(inode);
++ struct fuse_inode *fi = get_fuse_inode(inode);
+ int err = 0;
+ ssize_t res = 0;
+
+ if (is_bad_inode(inode))
+ return -EIO;
+
++ if (inode->i_size < pos + iov_iter_count(ii))
++ set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
++
+ do {
+ struct fuse_req *req;
+ ssize_t count;
+@@ -917,6 +922,7 @@ static ssize_t fuse_perform_write(struct
+ if (res > 0)
+ fuse_write_update_size(inode, pos);
+
++ clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
+ fuse_invalidate_attr(inode);
+
+ return res > 0 ? res : err;
+--- a/fs/fuse/fuse_i.h
++++ b/fs/fuse/fuse_i.h
+@@ -103,6 +103,15 @@ struct fuse_inode {
+
+ /** List of writepage requestst (pending or sent) */
+ struct list_head writepages;
++
++ /** Miscellaneous bits describing inode state */
++ unsigned long state;
++};
++
++/** FUSE inode state bits */
++enum {
++ /** An operation changing file size is in progress */
++ FUSE_I_SIZE_UNSTABLE,
+ };
+
+ struct fuse_conn;
+--- a/fs/fuse/inode.c
++++ b/fs/fuse/inode.c
+@@ -92,6 +92,7 @@ static struct inode *fuse_alloc_inode(st
+ fi->attr_version = 0;
+ fi->writectr = 0;
+ fi->orig_ino = 0;
++ fi->state = 0;
+ INIT_LIST_HEAD(&fi->write_files);
+ INIT_LIST_HEAD(&fi->queued_writes);
+ INIT_LIST_HEAD(&fi->writepages);
+@@ -199,7 +200,8 @@ void fuse_change_attributes(struct inode
+ loff_t oldsize;
+
+ spin_lock(&fc->lock);
+- if (attr_version != 0 && fi->attr_version > attr_version) {
++ if ((attr_version != 0 && fi->attr_version > attr_version) ||
++ test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) {
+ spin_unlock(&fc->lock);
+ return;
+ }
--- /dev/null
+From ccebcc74c81d8399c7b204aea47c1f33b09c2b17 Mon Sep 17 00:00:00 2001
+From: Vyacheslav Dubeyko <slava@dubeyko.com>
+Date: Mon, 30 Sep 2013 13:45:12 -0700
+Subject: nilfs2: fix issue with race condition of competition between segments for dirty blocks
+
+From: Vyacheslav Dubeyko <slava@dubeyko.com>
+
+commit 7f42ec3941560f0902fe3671e36f2c20ffd3af0a upstream.
+
+Many NILFS2 users were reported about strange file system corruption
+(for example):
+
+ NILFS: bad btree node (blocknr=185027): level = 0, flags = 0x0, nchildren = 768
+ NILFS error (device sda4): nilfs_bmap_last_key: broken bmap (inode number=11540)
+
+But such error messages are consequence of file system's issue that takes
+place more earlier. Fortunately, Jerome Poulin <jeromepoulin@gmail.com>
+and Anton Eliasson <devel@antoneliasson.se> were reported about another
+issue not so recently. These reports describe the issue with segctor
+thread's crash:
+
+ BUG: unable to handle kernel paging request at 0000000000004c83
+ IP: nilfs_end_page_io+0x12/0xd0 [nilfs2]
+
+ Call Trace:
+ nilfs_segctor_do_construct+0xf25/0x1b20 [nilfs2]
+ nilfs_segctor_construct+0x17b/0x290 [nilfs2]
+ nilfs_segctor_thread+0x122/0x3b0 [nilfs2]
+ kthread+0xc0/0xd0
+ ret_from_fork+0x7c/0xb0
+
+These two issues have one reason. This reason can raise third issue
+too. Third issue results in hanging of segctor thread with eating of
+100% CPU.
+
+REPRODUCING PATH:
+
+One of the possible way or the issue reproducing was described by
+Jermoe me Poulin <jeromepoulin@gmail.com>:
+
+1. init S to get to single user mode.
+2. sysrq+E to make sure only my shell is running
+3. start network-manager to get my wifi connection up
+4. login as root and launch "screen"
+5. cd /boot/log/nilfs which is a ext3 mount point and can log when NILFS dies.
+6. lscp | xz -9e > lscp.txt.xz
+7. mount my snapshot using mount -o cp=3360839,ro /dev/vgUbuntu/root /mnt/nilfs
+8. start a screen to dump /proc/kmsg to text file since rsyslog is killed
+9. start a screen and launch strace -f -o find-cat.log -t find
+/mnt/nilfs -type f -exec cat {} > /dev/null \;
+10. start a screen and launch strace -f -o apt-get.log -t apt-get update
+11. launch the last command again as it did not crash the first time
+12. apt-get crashes
+13. ps aux > ps-aux-crashed.log
+13. sysrq+W
+14. sysrq+E wait for everything to terminate
+15. sysrq+SUSB
+
+Simplified way of the issue reproducing is starting kernel compilation
+task and "apt-get update" in parallel.
+
+REPRODUCIBILITY:
+
+The issue is reproduced not stable [60% - 80%]. It is very important to
+have proper environment for the issue reproducing. The critical
+conditions for successful reproducing:
+
+(1) It should have big modified file by mmap() way.
+
+(2) This file should have the count of dirty blocks are greater that
+ several segments in size (for example, two or three) from time to time
+ during processing.
+
+(3) It should be intensive background activity of files modification
+ in another thread.
+
+INVESTIGATION:
+
+First of all, it is possible to see that the reason of crash is not valid
+page address:
+
+ NILFS [nilfs_segctor_complete_write]:2100 bh->b_count 0, bh->b_blocknr 13895680, bh->b_size 13897727, bh->b_page 0000000000001a82
+ NILFS [nilfs_segctor_complete_write]:2101 segbuf->sb_segnum 6783
+
+Moreover, value of b_page (0x1a82) is 6786. This value looks like segment
+number. And b_blocknr with b_size values look like block numbers. So,
+buffer_head's pointer points on not proper address value.
+
+Detailed investigation of the issue is discovered such picture:
+
+ [-----------------------------SEGMENT 6783-------------------------------]
+ NILFS [nilfs_segctor_do_construct]:2310 nilfs_segctor_begin_construction
+ NILFS [nilfs_segctor_do_construct]:2321 nilfs_segctor_collect
+ NILFS [nilfs_segctor_do_construct]:2336 nilfs_segctor_assign
+ NILFS [nilfs_segctor_do_construct]:2367 nilfs_segctor_update_segusage
+ NILFS [nilfs_segctor_do_construct]:2371 nilfs_segctor_prepare_write
+ NILFS [nilfs_segctor_do_construct]:2376 nilfs_add_checksums_on_logs
+ NILFS [nilfs_segctor_do_construct]:2381 nilfs_segctor_write
+ NILFS [nilfs_segbuf_submit_bio]:464 bio->bi_sector 111149024, segbuf->sb_segnum 6783
+
+ [-----------------------------SEGMENT 6784-------------------------------]
+ NILFS [nilfs_segctor_do_construct]:2310 nilfs_segctor_begin_construction
+ NILFS [nilfs_segctor_do_construct]:2321 nilfs_segctor_collect
+ NILFS [nilfs_lookup_dirty_data_buffers]:782 bh->b_count 1, bh->b_page ffffea000709b000, page->index 0, i_ino 1033103, i_size 25165824
+ NILFS [nilfs_lookup_dirty_data_buffers]:783 bh->b_assoc_buffers.next ffff8802174a6798, bh->b_assoc_buffers.prev ffff880221cffee8
+ NILFS [nilfs_segctor_do_construct]:2336 nilfs_segctor_assign
+ NILFS [nilfs_segctor_do_construct]:2367 nilfs_segctor_update_segusage
+ NILFS [nilfs_segctor_do_construct]:2371 nilfs_segctor_prepare_write
+ NILFS [nilfs_segctor_do_construct]:2376 nilfs_add_checksums_on_logs
+ NILFS [nilfs_segctor_do_construct]:2381 nilfs_segctor_write
+ NILFS [nilfs_segbuf_submit_bh]:575 bh->b_count 1, bh->b_page ffffea000709b000, page->index 0, i_ino 1033103, i_size 25165824
+ NILFS [nilfs_segbuf_submit_bh]:576 segbuf->sb_segnum 6784
+ NILFS [nilfs_segbuf_submit_bh]:577 bh->b_assoc_buffers.next ffff880218a0d5f8, bh->b_assoc_buffers.prev ffff880218bcdf50
+ NILFS [nilfs_segbuf_submit_bio]:464 bio->bi_sector 111150080, segbuf->sb_segnum 6784, segbuf->sb_nbio 0
+ [----------] ditto
+ NILFS [nilfs_segbuf_submit_bio]:464 bio->bi_sector 111164416, segbuf->sb_segnum 6784, segbuf->sb_nbio 15
+
+ [-----------------------------SEGMENT 6785-------------------------------]
+ NILFS [nilfs_segctor_do_construct]:2310 nilfs_segctor_begin_construction
+ NILFS [nilfs_segctor_do_construct]:2321 nilfs_segctor_collect
+ NILFS [nilfs_lookup_dirty_data_buffers]:782 bh->b_count 2, bh->b_page ffffea000709b000, page->index 0, i_ino 1033103, i_size 25165824
+ NILFS [nilfs_lookup_dirty_data_buffers]:783 bh->b_assoc_buffers.next ffff880219277e80, bh->b_assoc_buffers.prev ffff880221cffc88
+ NILFS [nilfs_segctor_do_construct]:2367 nilfs_segctor_update_segusage
+ NILFS [nilfs_segctor_do_construct]:2371 nilfs_segctor_prepare_write
+ NILFS [nilfs_segctor_do_construct]:2376 nilfs_add_checksums_on_logs
+ NILFS [nilfs_segctor_do_construct]:2381 nilfs_segctor_write
+ NILFS [nilfs_segbuf_submit_bh]:575 bh->b_count 2, bh->b_page ffffea000709b000, page->index 0, i_ino 1033103, i_size 25165824
+ NILFS [nilfs_segbuf_submit_bh]:576 segbuf->sb_segnum 6785
+ NILFS [nilfs_segbuf_submit_bh]:577 bh->b_assoc_buffers.next ffff880218a0d5f8, bh->b_assoc_buffers.prev ffff880222cc7ee8
+ NILFS [nilfs_segbuf_submit_bio]:464 bio->bi_sector 111165440, segbuf->sb_segnum 6785, segbuf->sb_nbio 0
+ [----------] ditto
+ NILFS [nilfs_segbuf_submit_bio]:464 bio->bi_sector 111177728, segbuf->sb_segnum 6785, segbuf->sb_nbio 12
+
+ NILFS [nilfs_segctor_do_construct]:2399 nilfs_segctor_wait
+ NILFS [nilfs_segbuf_wait]:676 segbuf->sb_segnum 6783
+ NILFS [nilfs_segbuf_wait]:676 segbuf->sb_segnum 6784
+ NILFS [nilfs_segbuf_wait]:676 segbuf->sb_segnum 6785
+
+ NILFS [nilfs_segctor_complete_write]:2100 bh->b_count 0, bh->b_blocknr 13895680, bh->b_size 13897727, bh->b_page 0000000000001a82
+
+ BUG: unable to handle kernel paging request at 0000000000001a82
+ IP: [<ffffffffa024d0f2>] nilfs_end_page_io+0x12/0xd0 [nilfs2]
+
+Usually, for every segment we collect dirty files in list. Then, dirty
+blocks are gathered for every dirty file, prepared for write and
+submitted by means of nilfs_segbuf_submit_bh() call. Finally, it takes
+place complete write phase after calling nilfs_end_bio_write() on the
+block layer. Buffers/pages are marked as not dirty on final phase and
+processed files removed from the list of dirty files.
+
+It is possible to see that we had three prepare_write and submit_bio
+phases before segbuf_wait and complete_write phase. Moreover, segments
+compete between each other for dirty blocks because on every iteration
+of segments processing dirty buffer_heads are added in several lists of
+payload_buffers:
+
+ [SEGMENT 6784]: bh->b_assoc_buffers.next ffff880218a0d5f8, bh->b_assoc_buffers.prev ffff880218bcdf50
+ [SEGMENT 6785]: bh->b_assoc_buffers.next ffff880218a0d5f8, bh->b_assoc_buffers.prev ffff880222cc7ee8
+
+The next pointer is the same but prev pointer has changed. It means
+that buffer_head has next pointer from one list but prev pointer from
+another. Such modification can be made several times. And, finally, it
+can be resulted in various issues: (1) segctor hanging, (2) segctor
+crashing, (3) file system metadata corruption.
+
+FIX:
+This patch adds:
+
+(1) setting of BH_Async_Write flag in nilfs_segctor_prepare_write()
+ for every proccessed dirty block;
+
+(2) checking of BH_Async_Write flag in
+ nilfs_lookup_dirty_data_buffers() and
+ nilfs_lookup_dirty_node_buffers();
+
+(3) clearing of BH_Async_Write flag in nilfs_segctor_complete_write(),
+ nilfs_abort_logs(), nilfs_forget_buffer(), nilfs_clear_dirty_page().
+
+Reported-by: Jerome Poulin <jeromepoulin@gmail.com>
+Reported-by: Anton Eliasson <devel@antoneliasson.se>
+Cc: Paul Fertser <fercerpav@gmail.com>
+Cc: ARAI Shun-ichi <hermes@ceres.dti.ne.jp>
+Cc: Piotr Szymaniak <szarpaj@grubelek.pl>
+Cc: Juan Barry Manuel Canham <Linux@riotingpacifist.net>
+Cc: Zahid Chowdhury <zahid.chowdhury@starsolutions.com>
+Cc: Elmer Zhang <freeboy6716@gmail.com>
+Cc: Kenneth Langga <klangga@gmail.com>
+Signed-off-by: Vyacheslav Dubeyko <slava@dubeyko.com>
+Acked-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+[bwh: Backported to 3.2: nilfs_clear_dirty_page() has not been separated
+ from nilfs_clear_dirty_pages()]
+Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
+Cc: Rui Xiang <rui.xiang@huawei.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/nilfs2/page.c | 2 ++
+ fs/nilfs2/segment.c | 11 +++++++++--
+ 2 files changed, 11 insertions(+), 2 deletions(-)
+
+--- a/fs/nilfs2/page.c
++++ b/fs/nilfs2/page.c
+@@ -94,6 +94,7 @@ void nilfs_forget_buffer(struct buffer_h
+ clear_buffer_nilfs_volatile(bh);
+ clear_buffer_nilfs_checked(bh);
+ clear_buffer_nilfs_redirected(bh);
++ clear_buffer_async_write(bh);
+ clear_buffer_dirty(bh);
+ if (nilfs_page_buffers_clean(page))
+ __nilfs_clear_page_dirty(page);
+@@ -390,6 +391,7 @@ void nilfs_clear_dirty_pages(struct addr
+ bh = head = page_buffers(page);
+ do {
+ lock_buffer(bh);
++ clear_buffer_async_write(bh);
+ clear_buffer_dirty(bh);
+ clear_buffer_nilfs_volatile(bh);
+ clear_buffer_nilfs_checked(bh);
+--- a/fs/nilfs2/segment.c
++++ b/fs/nilfs2/segment.c
+@@ -662,7 +662,7 @@ static size_t nilfs_lookup_dirty_data_bu
+
+ bh = head = page_buffers(page);
+ do {
+- if (!buffer_dirty(bh))
++ if (!buffer_dirty(bh) || buffer_async_write(bh))
+ continue;
+ get_bh(bh);
+ list_add_tail(&bh->b_assoc_buffers, listp);
+@@ -696,7 +696,8 @@ static void nilfs_lookup_dirty_node_buff
+ for (i = 0; i < pagevec_count(&pvec); i++) {
+ bh = head = page_buffers(pvec.pages[i]);
+ do {
+- if (buffer_dirty(bh)) {
++ if (buffer_dirty(bh) &&
++ !buffer_async_write(bh)) {
+ get_bh(bh);
+ list_add_tail(&bh->b_assoc_buffers,
+ listp);
+@@ -1578,6 +1579,7 @@ static void nilfs_segctor_prepare_write(
+
+ list_for_each_entry(bh, &segbuf->sb_segsum_buffers,
+ b_assoc_buffers) {
++ set_buffer_async_write(bh);
+ if (bh->b_page != bd_page) {
+ if (bd_page) {
+ lock_page(bd_page);
+@@ -1591,6 +1593,7 @@ static void nilfs_segctor_prepare_write(
+
+ list_for_each_entry(bh, &segbuf->sb_payload_buffers,
+ b_assoc_buffers) {
++ set_buffer_async_write(bh);
+ if (bh == segbuf->sb_super_root) {
+ if (bh->b_page != bd_page) {
+ lock_page(bd_page);
+@@ -1676,6 +1679,7 @@ static void nilfs_abort_logs(struct list
+ list_for_each_entry(segbuf, logs, sb_list) {
+ list_for_each_entry(bh, &segbuf->sb_segsum_buffers,
+ b_assoc_buffers) {
++ clear_buffer_async_write(bh);
+ if (bh->b_page != bd_page) {
+ if (bd_page)
+ end_page_writeback(bd_page);
+@@ -1685,6 +1689,7 @@ static void nilfs_abort_logs(struct list
+
+ list_for_each_entry(bh, &segbuf->sb_payload_buffers,
+ b_assoc_buffers) {
++ clear_buffer_async_write(bh);
+ if (bh == segbuf->sb_super_root) {
+ if (bh->b_page != bd_page) {
+ end_page_writeback(bd_page);
+@@ -1754,6 +1759,7 @@ static void nilfs_segctor_complete_write
+ b_assoc_buffers) {
+ set_buffer_uptodate(bh);
+ clear_buffer_dirty(bh);
++ clear_buffer_async_write(bh);
+ if (bh->b_page != bd_page) {
+ if (bd_page)
+ end_page_writeback(bd_page);
+@@ -1775,6 +1781,7 @@ static void nilfs_segctor_complete_write
+ b_assoc_buffers) {
+ set_buffer_uptodate(bh);
+ clear_buffer_dirty(bh);
++ clear_buffer_async_write(bh);
+ clear_buffer_delay(bh);
+ clear_buffer_nilfs_volatile(bh);
+ clear_buffer_nilfs_redirected(bh);