FUSE: Adapt readdirplus to application usage patterns
Use the same adaptive readdirplus mechanism as NFS: http://permalink.gmane.org/gmane.linux.nfs/49299 If the user space implementation wants to disable readdirplus temporarily, it could just return ENOTSUPP. Then kernel will recall it with readdir. Signed-off-by: Feng Shuo <steve.shuo.feng@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
This commit is contained in:
		
							parent
							
								
									c2132c1bc7
								
							
						
					
					
						commit
						4582a4ab2a
					
				| @ -14,6 +14,27 @@ | |||||||
| #include <linux/namei.h> | #include <linux/namei.h> | ||||||
| #include <linux/slab.h> | #include <linux/slab.h> | ||||||
| 
 | 
 | ||||||
|  | static bool fuse_use_readdirplus(struct inode *dir, struct file *filp) | ||||||
|  | { | ||||||
|  | 	struct fuse_conn *fc = get_fuse_conn(dir); | ||||||
|  | 	struct fuse_inode *fi = get_fuse_inode(dir); | ||||||
|  | 
 | ||||||
|  | 	if (!fc->do_readdirplus) | ||||||
|  | 		return false; | ||||||
|  | 	if (test_and_clear_bit(FUSE_I_ADVISE_RDPLUS, &fi->state)) | ||||||
|  | 		return true; | ||||||
|  | 	if (filp->f_pos == 0) | ||||||
|  | 		return true; | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void fuse_advise_use_readdirplus(struct inode *dir) | ||||||
|  | { | ||||||
|  | 	struct fuse_inode *fi = get_fuse_inode(dir); | ||||||
|  | 
 | ||||||
|  | 	set_bit(FUSE_I_ADVISE_RDPLUS, &fi->state); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #if BITS_PER_LONG >= 64 | #if BITS_PER_LONG >= 64 | ||||||
| static inline void fuse_dentry_settime(struct dentry *entry, u64 time) | static inline void fuse_dentry_settime(struct dentry *entry, u64 time) | ||||||
| { | { | ||||||
| @ -219,6 +240,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) | |||||||
| 				       attr_version); | 				       attr_version); | ||||||
| 		fuse_change_entry_timeout(entry, &outarg); | 		fuse_change_entry_timeout(entry, &outarg); | ||||||
| 	} | 	} | ||||||
|  | 	fuse_advise_use_readdirplus(inode); | ||||||
| 	return 1; | 	return 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -355,6 +377,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | |||||||
| 	else | 	else | ||||||
| 		fuse_invalidate_entry_cache(entry); | 		fuse_invalidate_entry_cache(entry); | ||||||
| 
 | 
 | ||||||
|  | 	fuse_advise_use_readdirplus(dir); | ||||||
| 	return newent; | 	return newent; | ||||||
| 
 | 
 | ||||||
|  out_iput: |  out_iput: | ||||||
| @ -1290,7 +1313,7 @@ static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file, | |||||||
| 
 | 
 | ||||||
| static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) | static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) | ||||||
| { | { | ||||||
| 	int err; | 	int plus, err; | ||||||
| 	size_t nbytes; | 	size_t nbytes; | ||||||
| 	struct page *page; | 	struct page *page; | ||||||
| 	struct inode *inode = file->f_path.dentry->d_inode; | 	struct inode *inode = file->f_path.dentry->d_inode; | ||||||
| @ -1310,11 +1333,13 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) | |||||||
| 		fuse_put_request(fc, req); | 		fuse_put_request(fc, req); | ||||||
| 		return -ENOMEM; | 		return -ENOMEM; | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	plus = fuse_use_readdirplus(inode, file); | ||||||
| 	req->out.argpages = 1; | 	req->out.argpages = 1; | ||||||
| 	req->num_pages = 1; | 	req->num_pages = 1; | ||||||
| 	req->pages[0] = page; | 	req->pages[0] = page; | ||||||
| 	req->page_descs[0].length = PAGE_SIZE; | 	req->page_descs[0].length = PAGE_SIZE; | ||||||
| 	if (fc->do_readdirplus) { | 	if (plus) { | ||||||
| 		attr_version = fuse_get_attr_version(fc); | 		attr_version = fuse_get_attr_version(fc); | ||||||
| 		fuse_read_fill(req, file, file->f_pos, PAGE_SIZE, | 		fuse_read_fill(req, file, file->f_pos, PAGE_SIZE, | ||||||
| 			       FUSE_READDIRPLUS); | 			       FUSE_READDIRPLUS); | ||||||
| @ -1327,7 +1352,7 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) | |||||||
| 	err = req->out.h.error; | 	err = req->out.h.error; | ||||||
| 	fuse_put_request(fc, req); | 	fuse_put_request(fc, req); | ||||||
| 	if (!err) { | 	if (!err) { | ||||||
| 		if (fc->do_readdirplus) { | 		if (plus) { | ||||||
| 			err = parse_dirplusfile(page_address(page), nbytes, | 			err = parse_dirplusfile(page_address(page), nbytes, | ||||||
| 						file, dstbuf, filldir, | 						file, dstbuf, filldir, | ||||||
| 						attr_version); | 						attr_version); | ||||||
|  | |||||||
| @ -106,6 +106,15 @@ struct fuse_inode { | |||||||
| 
 | 
 | ||||||
| 	/** List of writepage requestst (pending or sent) */ | 	/** List of writepage requestst (pending or sent) */ | ||||||
| 	struct list_head writepages; | 	struct list_head writepages; | ||||||
|  | 
 | ||||||
|  | 	/** Miscellaneous bits describing inode state */ | ||||||
|  | 	unsigned long state; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /** FUSE inode state bits */ | ||||||
|  | enum { | ||||||
|  | 	/** Advise readdirplus  */ | ||||||
|  | 	FUSE_I_ADVISE_RDPLUS, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct fuse_conn; | struct fuse_conn; | ||||||
|  | |||||||
| @ -92,6 +92,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb) | |||||||
| 	fi->attr_version = 0; | 	fi->attr_version = 0; | ||||||
| 	fi->writectr = 0; | 	fi->writectr = 0; | ||||||
| 	fi->orig_ino = 0; | 	fi->orig_ino = 0; | ||||||
|  | 	fi->state = 0; | ||||||
| 	INIT_LIST_HEAD(&fi->write_files); | 	INIT_LIST_HEAD(&fi->write_files); | ||||||
| 	INIT_LIST_HEAD(&fi->queued_writes); | 	INIT_LIST_HEAD(&fi->queued_writes); | ||||||
| 	INIT_LIST_HEAD(&fi->writepages); | 	INIT_LIST_HEAD(&fi->writepages); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user