From mboxrd@z Thu Jan 1 00:00:00 1970 From: Pavel Emelyanov Subject: [PATCH 8/10] fuse: Implement writepages and write_begin/write_end callbacks Date: Tue, 03 Jul 2012 19:56:46 +0400 Message-ID: <4FF3163E.3020806@parallels.com> References: <4FF3156E.8030109@parallels.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Cc: Kirill Korotaev , James Bottomley To: fuse-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org, Miklos Szeredi , Alexander Viro , linux-fsdevel Return-path: In-Reply-To: <4FF3156E.8030109-bzQdu9zFT3WakBO8gow8eQ@public.gmane.org> List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: fuse-devel-bounces-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org List-Id: linux-fsdevel.vger.kernel.org The .writepages one is required to make each writeback request carry more than one page on it. Signed-off-by: Pavel Emelyanov --- 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 ------------------------------------------------------------------------------ Live Security Virtual Conference Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/