lkml.org 
[lkml]   [2013]   [Sep]   [20]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 60/93] fuse: postpone end_page_writeback() in fuse_writepage_locked()
    Date
    3.8.13.10 -stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Maxim Patlasov <MPatlasov@parallels.com>

    commit 4a4ac4eba1010ef9a804569058ab29e3450c0315 upstream.

    The patch fixes a race between ftruncate(2), mmap-ed write and write(2):

    1) An user makes a page dirty via mmap-ed write.
    2) The user performs shrinking truncate(2) intended to purge the page.
    3) Before fuse_do_setattr calls truncate_pagecache, the page goes to
    writeback. fuse_writepage_locked fills FUSE_WRITE request and releases
    the original page by end_page_writeback.
    4) fuse_do_setattr() completes and successfully returns. Since now, i_mutex
    is free.
    5) Ordinary write(2) extends i_size back to cover the page. Note that
    fuse_send_write_pages do wait for fuse writeback, but for another
    page->index.
    6) fuse_writepage_locked proceeds by queueing FUSE_WRITE request.
    fuse_send_writepage is supposed to crop inarg->size of the request,
    but it doesn't because i_size has already been extended back.

    Moving end_page_writeback to the end of fuse_writepage_locked fixes the
    race because now the fact that truncate_pagecache is successfully returned
    infers that fuse_writepage_locked has already called end_page_writeback.
    And this, in turn, infers that fuse_flush_writepages has already called
    fuse_send_writepage, and the latter used valid (shrunk) i_size. write(2)
    could not extend it because of i_mutex held by ftruncate(2).

    Signed-off-by: Maxim Patlasov <mpatlasov@parallels.com>
    Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
    Signed-off-by: Kamal Mostafa <kamal@canonical.com>
    ---
    fs/fuse/file.c | 3 ++-
    1 file changed, 2 insertions(+), 1 deletion(-)

    diff --git a/fs/fuse/file.c b/fs/fuse/file.c
    index f3ab824..e9c40f3 100644
    --- a/fs/fuse/file.c
    +++ b/fs/fuse/file.c
    @@ -1299,7 +1299,6 @@ static int fuse_writepage_locked(struct page *page)

    inc_bdi_stat(mapping->backing_dev_info, BDI_WRITEBACK);
    inc_zone_page_state(tmp_page, NR_WRITEBACK_TEMP);
    - end_page_writeback(page);

    spin_lock(&fc->lock);
    list_add(&req->writepages_entry, &fi->writepages);
    @@ -1307,6 +1306,8 @@ static int fuse_writepage_locked(struct page *page)
    fuse_flush_writepages(inode);
    spin_unlock(&fc->lock);

    + end_page_writeback(page);
    +
    return 0;

    err_free:
    --
    1.8.1.2


    \
     
     \ /
      Last update: 2013-09-20 22:41    [W:4.136 / U:0.228 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site