The .writepages one is required to make each writeback request carry more than one page on it. Signed-off-by: Pavel Emelyanov <xemul@xxxxxxxxxx> --- fs/fuse/file.c | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 201 insertions(+), 1 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index d9d566e..058498d 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -699,7 +699,10 @@ static void fuse_send_readpages(struct fuse_req *req, struct file *file) struct fuse_fill_data { struct fuse_req *req; - struct file *file; + union { + struct file *file; + struct fuse_file *ff; + }; struct inode *inode; }; @@ -1400,6 +1403,200 @@ static int fuse_writepage(struct page *page, struct writeback_control *wbc) return err; } +static int fuse_send_writepages(struct fuse_fill_data *data) +{ + int i, all_ok = 1; + struct fuse_req *req = data->req; + struct fuse_conn *fc = get_fuse_conn(data->inode); + struct fuse_inode *fi = get_fuse_inode(data->inode); + loff_t off = -1; + + if (!data->ff) + data->ff = fuse_write_file(fc, fi); + + for (i = 0; i < req->num_pages; i++) { + struct page *page = req->pages[i]; + struct address_space *mapping = page->mapping; + struct page *tmp_page; + + tmp_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); + if (tmp_page) { + copy_highpage(tmp_page, page); + inc_bdi_stat(mapping->backing_dev_info, BDI_WRITEBACK); + inc_zone_page_state(tmp_page, NR_WRITEBACK_TEMP); + } else + all_ok = 0; + req->pages[i] = tmp_page; + if (i == 0) + off = page_offset(page); + + end_page_writeback(page); + } + + if (!all_ok) + return -ENOMEM; + + req->ff = fuse_file_get(data->ff); + fuse_write_fill(req, data->ff, off, 0); + + req->misc.write.in.write_flags |= FUSE_WRITE_CACHE; + req->in.argpages = 1; + req->page_offset = 0; + req->end = fuse_writepage_end; + req->inode = data->inode; + + spin_lock(&fc->lock); + list_add(&req->writepages_entry, &fi->writepages); + list_add_tail(&req->list, &fi->queued_writes); + fuse_flush_writepages(data->inode); + spin_unlock(&fc->lock); + + return 0; +} + +static int fuse_writepages_fill(struct page *page, + struct writeback_control *wbc, void *_data) +{ + struct fuse_fill_data *data = _data; + struct fuse_req *req = data->req; + struct inode *inode = data->inode; + struct fuse_conn *fc = get_fuse_conn(inode); + + if (req->num_pages && + (req->num_pages == FUSE_MAX_PAGES_PER_REQ || + (req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_write || + req->pages[req->num_pages - 1]->index + 1 != page->index)) { + int err; + + err = fuse_send_writepages(data); + if (err) { + unlock_page(page); + return err; + } + + data->req = req = fuse_request_alloc_nofs(); + if (IS_ERR(req)) { + unlock_page(page); + return PTR_ERR(req); + } + } + + req->pages[req->num_pages] = page; + req->num_pages++; + set_page_writeback(page); + unlock_page(page); + + return 0; +} + +static int fuse_writepages(struct address_space *mapping, struct writeback_control *wbc) +{ + struct inode *inode = mapping->host; + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_fill_data data; + int err; + + if (!fc->writeback_cache) + return generic_writepages(mapping, wbc); + + err = -EIO; + if (is_bad_inode(inode)) + goto out; + + data.ff = NULL; + data.inode = inode; + data.req = fuse_request_alloc_nofs(); + err = PTR_ERR(data.req); + if (IS_ERR(data.req)) + goto out_put; + + err = write_cache_pages(mapping, wbc, fuse_writepages_fill, &data); + if (!err) { + if (data.req->num_pages) + err = fuse_send_writepages(&data); + else + fuse_put_request(fc, data.req); + } +out_put: + if (data.ff) + fuse_file_put(data.ff, false); +out: + return err; +} + +static int fuse_prepare_write(struct fuse_conn *fc, struct file *file, + struct page *page, loff_t pos, unsigned len) +{ + struct fuse_req *req; + int err; + + if (PageUptodate(page) || (len == PAGE_CACHE_SIZE)) + return 0; + + req = fuse_get_req(fc); + err = PTR_ERR(req); + if (IS_ERR(req)) + goto out; + + req->out.page_zeroing = 1; + req->out.argpages = 1; + req->num_pages = 1; + req->pages[0] = page; + fuse_send_read(req, file, page_offset(page), PAGE_CACHE_SIZE, NULL); + err = req->out.h.error; + fuse_put_request(fc, req); +out: + if (err) { + unlock_page(page); + page_cache_release(page); + } + return err; +} + +static int fuse_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) +{ + pgoff_t index = pos >> PAGE_CACHE_SHIFT; + struct fuse_conn *fc = get_fuse_conn(file->f_dentry->d_inode); + + BUG_ON(!fc->writeback_cache); + + *pagep = grab_cache_page_write_begin(mapping, index, flags); + if (!*pagep) + return -ENOMEM; + + return fuse_prepare_write(fc, file, *pagep, pos, len); +} + +static int fuse_commit_write(struct file *file, struct page *page, + unsigned from, unsigned to) +{ + struct inode *inode = page->mapping->host; + loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; + + if (!PageUptodate(page)) + SetPageUptodate(page); + + fuse_write_update_size(inode, pos); + set_page_dirty(page); + return 0; +} + +static int fuse_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) +{ + unsigned from = pos & (PAGE_CACHE_SIZE - 1); + + fuse_commit_write(file, page, from, from+copied); + + unlock_page(page); + page_cache_release(page); + + return copied; +} + static int fuse_launder_page(struct page *page) { int err = 0; @@ -2318,11 +2515,14 @@ static const struct file_operations fuse_direct_io_file_operations = { static const struct address_space_operations fuse_file_aops = { .readpage = fuse_readpage, .writepage = fuse_writepage, + .writepages = fuse_writepages, .launder_page = fuse_launder_page, .readpages = fuse_readpages, .set_page_dirty = __set_page_dirty_nobuffers, .bmap = fuse_bmap, .direct_IO = fuse_direct_IO, + .write_begin = fuse_write_begin, + .write_end = fuse_write_end, }; void fuse_init_file_inode(struct inode *inode) -- 1.5.5.6 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html