RCUify freeing acls, let check_acl() go ahead in RCU mode if acl is cached
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
		
					parent
					
						
							
								951c0d660a
							
						
					
				
			
			
				commit
				
					
						3567866bf2
					
				
			
		
					 2 changed files with 15 additions and 20 deletions
				
			
		
							
								
								
									
										17
									
								
								fs/namei.c
									
										
									
									
									
								
							
							
						
						
									
										17
									
								
								fs/namei.c
									
										
									
									
									
								
							|  | @ -179,19 +179,14 @@ static int check_acl(struct inode *inode, int mask) | |||
| #ifdef CONFIG_FS_POSIX_ACL | ||||
| 	struct posix_acl *acl; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Under RCU walk, we cannot even do a "get_cached_acl()", | ||||
| 	 * because that involves locking and getting a refcount on | ||||
| 	 * a cached ACL. | ||||
| 	 * | ||||
| 	 * So the only case we handle during RCU walking is the | ||||
| 	 * case of a cached "no ACL at all", which needs no locks | ||||
| 	 * or refcounts. | ||||
| 	 */ | ||||
| 	if (mask & MAY_NOT_BLOCK) { | ||||
| 	        if (negative_cached_acl(inode, ACL_TYPE_ACCESS)) | ||||
| 		acl = get_cached_acl_rcu(inode, ACL_TYPE_ACCESS); | ||||
| 	        if (!acl) | ||||
| 	                return -EAGAIN; | ||||
| 	        return -ECHILD; | ||||
| 		/* no ->get_acl() calls in RCU mode... */ | ||||
| 		if (acl == ACL_NOT_CACHED) | ||||
| 			return -ECHILD; | ||||
| 	        return posix_acl_permission(inode, acl, mask); | ||||
| 	} | ||||
| 
 | ||||
| 	acl = get_cached_acl(inode, ACL_TYPE_ACCESS); | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| #define __LINUX_POSIX_ACL_H | ||||
| 
 | ||||
| #include <linux/slab.h> | ||||
| #include <linux/rcupdate.h> | ||||
| 
 | ||||
| #define ACL_UNDEFINED_ID	(-1) | ||||
| 
 | ||||
|  | @ -38,7 +39,10 @@ struct posix_acl_entry { | |||
| }; | ||||
| 
 | ||||
| struct posix_acl { | ||||
| 	atomic_t		a_refcount; | ||||
| 	union { | ||||
| 		atomic_t		a_refcount; | ||||
| 		struct rcu_head		a_rcu; | ||||
| 	}; | ||||
| 	unsigned int		a_count; | ||||
| 	struct posix_acl_entry	a_entries[0]; | ||||
| }; | ||||
|  | @ -65,7 +69,7 @@ static inline void | |||
| posix_acl_release(struct posix_acl *acl) | ||||
| { | ||||
| 	if (acl && atomic_dec_and_test(&acl->a_refcount)) | ||||
| 		kfree(acl); | ||||
| 		kfree_rcu(acl, a_rcu); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -110,13 +114,9 @@ static inline struct posix_acl *get_cached_acl(struct inode *inode, int type) | |||
| 	return acl; | ||||
| } | ||||
| 
 | ||||
| static inline int negative_cached_acl(struct inode *inode, int type) | ||||
| static inline struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type) | ||||
| { | ||||
| 	struct posix_acl **p = acl_by_type(inode, type); | ||||
| 	struct posix_acl *acl = ACCESS_ONCE(*p); | ||||
| 	if (acl) | ||||
| 		return 0; | ||||
| 	return 1; | ||||
| 	return rcu_dereference(*acl_by_type(inode, type)); | ||||
| } | ||||
| 
 | ||||
| static inline void set_cached_acl(struct inode *inode, | ||||
|  | @ -127,7 +127,7 @@ static inline void set_cached_acl(struct inode *inode, | |||
| 	struct posix_acl *old; | ||||
| 	spin_lock(&inode->i_lock); | ||||
| 	old = *p; | ||||
| 	*p = posix_dup_acl(acl); | ||||
| 	rcu_assign_pointer(*p, posix_acl_dup(acl)); | ||||
| 	spin_unlock(&inode->i_lock); | ||||
| 	if (old != ACL_NOT_CACHED) | ||||
| 		posix_acl_release(old); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Al Viro
				Al Viro