diff options
-rw-r--r-- | fs/fuse/file.c | 40 |
1 files changed, 24 insertions, 16 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 927d40dec376..8342df29815d 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1744,6 +1744,13 @@ static void fuse_writepages_send(struct fuse_fill_wb_data *data) end_page_writeback(data->orig_pages[i]); } +/* + * First recheck under fi->lock if the offending offset is still under + * writeback. If yes, then iterate write requests, to see if there's one + * already added for a page at this offset. If there's none, then insert this + * new request onto the auxiliary list, otherwise reuse the existing one by + * copying the new page contents over to the old temporary page. + */ static bool fuse_writepage_in_flight(struct fuse_req *new_req, struct page *page) { @@ -1751,9 +1758,8 @@ static bool fuse_writepage_in_flight(struct fuse_req *new_req, struct fuse_inode *fi = get_fuse_inode(new_req->inode); struct fuse_req *tmp; struct fuse_req *old_req; - pgoff_t curr_index; - BUG_ON(new_req->num_pages != 0); + WARN_ON(new_req->num_pages != 0); spin_lock(&fc->lock); list_del(&new_req->writepages_entry); @@ -1766,32 +1772,34 @@ static bool fuse_writepage_in_flight(struct fuse_req *new_req, new_req->num_pages = 1; for (tmp = old_req; tmp != NULL; tmp = tmp->misc.write.next) { - BUG_ON(tmp->inode != new_req->inode); + pgoff_t curr_index; + + WARN_ON(tmp->inode != new_req->inode); curr_index = tmp->misc.write.in.offset >> PAGE_SHIFT; - if (tmp->num_pages == 1 && - curr_index == page->index) { - old_req = tmp; + if (tmp->num_pages == 1 && curr_index == page->index && + test_bit(FR_PENDING, &tmp->flags)) { + copy_highpage(tmp->pages[0], page); + break; } } - if (old_req->num_pages == 1 && test_bit(FR_PENDING, &old_req->flags)) { - struct backing_dev_info *bdi = inode_to_bdi(page->mapping->host); + if (!tmp) { + new_req->misc.write.next = old_req->misc.write.next; + old_req->misc.write.next = new_req; + } - copy_highpage(old_req->pages[0], page); - spin_unlock(&fc->lock); + spin_unlock(&fc->lock); + + if (tmp) { + struct backing_dev_info *bdi = inode_to_bdi(new_req->inode); dec_wb_stat(&bdi->wb, WB_WRITEBACK); dec_node_page_state(new_req->pages[0], NR_WRITEBACK_TEMP); wb_writeout_inc(&bdi->wb); fuse_writepage_free(fc, new_req); fuse_request_free(new_req); - goto out; - } else { - new_req->misc.write.next = old_req->misc.write.next; - old_req->misc.write.next = new_req; } - spin_unlock(&fc->lock); -out: + return true; } |