fuse: Implement writepages callback
The .writepages one is required to make each writeback request carry more than one page on it. The patch enables optimized behaviour unconditionally, i.e. mmap-ed writes will benefit from the patch even if fc->writeback_cache=0. [SzM: simplify, add comments] Signed-off-by: Maxim Patlasov <MPatlasov@parallels.com> Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
This commit is contained in:
		
							parent
							
								
									72523425fb
								
							
						
					
					
						commit
						26d614df1d
					
				
							
								
								
									
										150
									
								
								fs/fuse/file.c
									
									
									
									
									
								
							
							
						
						
									
										150
									
								
								fs/fuse/file.c
									
									
									
									
									
								
							| @ -1502,8 +1502,8 @@ static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_req *req) | ||||
| 	fuse_writepage_free(fc, req); | ||||
| } | ||||
| 
 | ||||
| static struct fuse_file *fuse_write_file(struct fuse_conn *fc, | ||||
| 					 struct fuse_inode *fi) | ||||
| static struct fuse_file *fuse_write_file_get(struct fuse_conn *fc, | ||||
| 					     struct fuse_inode *fi) | ||||
| { | ||||
| 	struct fuse_file *ff = NULL; | ||||
| 
 | ||||
| @ -1540,7 +1540,7 @@ static int fuse_writepage_locked(struct page *page) | ||||
| 		goto err_free; | ||||
| 
 | ||||
| 	error = -EIO; | ||||
| 	req->ff = fuse_write_file(fc, fi); | ||||
| 	req->ff = fuse_write_file_get(fc, fi); | ||||
| 	if (!req->ff) | ||||
| 		goto err_free; | ||||
| 
 | ||||
| @ -1586,6 +1586,149 @@ static int fuse_writepage(struct page *page, struct writeback_control *wbc) | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| struct fuse_fill_wb_data { | ||||
| 	struct fuse_req *req; | ||||
| 	struct fuse_file *ff; | ||||
| 	struct inode *inode; | ||||
| }; | ||||
| 
 | ||||
| static void fuse_writepages_send(struct fuse_fill_wb_data *data) | ||||
| { | ||||
| 	struct fuse_req *req = data->req; | ||||
| 	struct inode *inode = data->inode; | ||||
| 	struct fuse_conn *fc = get_fuse_conn(inode); | ||||
| 	struct fuse_inode *fi = get_fuse_inode(inode); | ||||
| 
 | ||||
| 	req->ff = fuse_file_get(data->ff); | ||||
| 	spin_lock(&fc->lock); | ||||
| 	list_add_tail(&req->list, &fi->queued_writes); | ||||
| 	fuse_flush_writepages(inode); | ||||
| 	spin_unlock(&fc->lock); | ||||
| } | ||||
| 
 | ||||
| static int fuse_writepages_fill(struct page *page, | ||||
| 		struct writeback_control *wbc, void *_data) | ||||
| { | ||||
| 	struct fuse_fill_wb_data *data = _data; | ||||
| 	struct fuse_req *req = data->req; | ||||
| 	struct inode *inode = data->inode; | ||||
| 	struct fuse_conn *fc = get_fuse_conn(inode); | ||||
| 	struct page *tmp_page; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (!data->ff) { | ||||
| 		err = -EIO; | ||||
| 		data->ff = fuse_write_file_get(fc, get_fuse_inode(inode)); | ||||
| 		if (!data->ff) | ||||
| 			goto out_unlock; | ||||
| 	} | ||||
| 
 | ||||
| 	if (req) { | ||||
| 		BUG_ON(!req->num_pages); | ||||
| 		if (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) { | ||||
| 
 | ||||
| 			fuse_writepages_send(data); | ||||
| 			data->req = NULL; | ||||
| 		} | ||||
| 	} | ||||
| 	err = -ENOMEM; | ||||
| 	tmp_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); | ||||
| 	if (!tmp_page) | ||||
| 		goto out_unlock; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The page must not be redirtied until the writeout is completed | ||||
| 	 * (i.e. userspace has sent a reply to the write request).  Otherwise | ||||
| 	 * there could be more than one temporary page instance for each real | ||||
| 	 * page. | ||||
| 	 * | ||||
| 	 * This is ensured by holding the page lock in page_mkwrite() while | ||||
| 	 * checking fuse_page_is_writeback().  We already hold the page lock | ||||
| 	 * since clear_page_dirty_for_io() and keep it held until we add the | ||||
| 	 * request to the fi->writepages list and increment req->num_pages. | ||||
| 	 * After this fuse_page_is_writeback() will indicate that the page is | ||||
| 	 * under writeback, so we can release the page lock. | ||||
| 	 */ | ||||
| 	if (data->req == NULL) { | ||||
| 		struct fuse_inode *fi = get_fuse_inode(inode); | ||||
| 
 | ||||
| 		err = -ENOMEM; | ||||
| 		req = fuse_request_alloc_nofs(FUSE_MAX_PAGES_PER_REQ); | ||||
| 		if (!req) { | ||||
| 			__free_page(tmp_page); | ||||
| 			goto out_unlock; | ||||
| 		} | ||||
| 
 | ||||
| 		fuse_write_fill(req, data->ff, page_offset(page), 0); | ||||
| 		req->misc.write.in.write_flags |= FUSE_WRITE_CACHE; | ||||
| 		req->in.argpages = 1; | ||||
| 		req->background = 1; | ||||
| 		req->num_pages = 0; | ||||
| 		req->end = fuse_writepage_end; | ||||
| 		req->inode = inode; | ||||
| 
 | ||||
| 		spin_lock(&fc->lock); | ||||
| 		list_add(&req->writepages_entry, &fi->writepages); | ||||
| 		spin_unlock(&fc->lock); | ||||
| 
 | ||||
| 		data->req = req; | ||||
| 	} | ||||
| 	set_page_writeback(page); | ||||
| 
 | ||||
| 	copy_highpage(tmp_page, page); | ||||
| 	req->pages[req->num_pages] = tmp_page; | ||||
| 	req->page_descs[req->num_pages].offset = 0; | ||||
| 	req->page_descs[req->num_pages].length = PAGE_SIZE; | ||||
| 
 | ||||
| 	inc_bdi_stat(page->mapping->backing_dev_info, BDI_WRITEBACK); | ||||
| 	inc_zone_page_state(tmp_page, NR_WRITEBACK_TEMP); | ||||
| 	end_page_writeback(page); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Protected by fc->lock against concurrent access by | ||||
| 	 * fuse_page_is_writeback(). | ||||
| 	 */ | ||||
| 	spin_lock(&fc->lock); | ||||
| 	req->num_pages++; | ||||
| 	spin_unlock(&fc->lock); | ||||
| 
 | ||||
| 	err = 0; | ||||
| out_unlock: | ||||
| 	unlock_page(page); | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int fuse_writepages(struct address_space *mapping, | ||||
| 			   struct writeback_control *wbc) | ||||
| { | ||||
| 	struct inode *inode = mapping->host; | ||||
| 	struct fuse_fill_wb_data data; | ||||
| 	int err; | ||||
| 
 | ||||
| 	err = -EIO; | ||||
| 	if (is_bad_inode(inode)) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	data.inode = inode; | ||||
| 	data.req = NULL; | ||||
| 	data.ff = NULL; | ||||
| 
 | ||||
| 	err = write_cache_pages(mapping, wbc, fuse_writepages_fill, &data); | ||||
| 	if (data.req) { | ||||
| 		/* Ignore errors if we can write at least one page */ | ||||
| 		BUG_ON(!data.req->num_pages); | ||||
| 		fuse_writepages_send(&data); | ||||
| 		err = 0; | ||||
| 	} | ||||
| 	if (data.ff) | ||||
| 		fuse_file_put(data.ff, false); | ||||
| out: | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int fuse_launder_page(struct page *page) | ||||
| { | ||||
| 	int err = 0; | ||||
| @ -2607,6 +2750,7 @@ 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, | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user