diff options
| author | Jan Kara <jack@suse.cz> | 2019-11-06 10:52:10 +0100 | 
|---|---|---|
| committer | Jan Kara <jack@suse.cz> | 2019-11-06 10:52:10 +0100 | 
| commit | dae82c7fd0926840c832151f3258ba751f73d348 (patch) | |
| tree | 5cdef8359f6cfb964919ffb0bfa36d2de60140b1 /fs/quota | |
| parent | a9913d7eafa74eb2e34e26aa31fe80449b999f8e (diff) | |
| parent | a0828b6ccbdfd46afbbaa9f28df359081c29109b (diff) | |
| download | linux-dae82c7fd0926840c832151f3258ba751f73d348.tar.bz2 | |
Pull series refactoring quota enabling and disabling code.
Diffstat (limited to 'fs/quota')
| -rw-r--r-- | fs/quota/dquot.c | 241 | 
1 files changed, 115 insertions, 126 deletions
| diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 72d24a5451bb..54a6626c2e9b 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -2161,14 +2161,29 @@ int dquot_file_open(struct inode *inode, struct file *file)  }  EXPORT_SYMBOL(dquot_file_open); +static void vfs_cleanup_quota_inode(struct super_block *sb, int type) +{ +	struct quota_info *dqopt = sb_dqopt(sb); +	struct inode *inode = dqopt->files[type]; + +	if (!inode) +		return; +	if (!(dqopt->flags & DQUOT_QUOTA_SYS_FILE)) { +		inode_lock(inode); +		inode->i_flags &= ~S_NOQUOTA; +		inode_unlock(inode); +	} +	dqopt->files[type] = NULL; +	iput(inode); +} +  /*   * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)   */  int dquot_disable(struct super_block *sb, int type, unsigned int flags)  { -	int cnt, ret = 0; +	int cnt;  	struct quota_info *dqopt = sb_dqopt(sb); -	struct inode *toputinode[MAXQUOTAS];  	/* s_umount should be held in exclusive mode */  	if (WARN_ON_ONCE(down_read_trylock(&sb->s_umount))) @@ -2190,7 +2205,6 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags)  		return 0;  	for (cnt = 0; cnt < MAXQUOTAS; cnt++) { -		toputinode[cnt] = NULL;  		if (type != -1 && cnt != type)  			continue;  		if (!sb_has_quota_loaded(sb, cnt)) @@ -2210,8 +2224,7 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags)  				dqopt->flags &=	~dquot_state_flag(  							DQUOT_SUSPENDED, cnt);  				spin_unlock(&dq_state_lock); -				iput(dqopt->files[cnt]); -				dqopt->files[cnt] = NULL; +				vfs_cleanup_quota_inode(sb, cnt);  				continue;  			}  			spin_unlock(&dq_state_lock); @@ -2233,10 +2246,6 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags)  		if (dqopt->ops[cnt]->free_file_info)  			dqopt->ops[cnt]->free_file_info(sb, cnt);  		put_quota_format(dqopt->info[cnt].dqi_format); - -		toputinode[cnt] = dqopt->files[cnt]; -		if (!sb_has_quota_loaded(sb, cnt)) -			dqopt->files[cnt] = NULL;  		dqopt->info[cnt].dqi_flags = 0;  		dqopt->info[cnt].dqi_igrace = 0;  		dqopt->info[cnt].dqi_bgrace = 0; @@ -2258,32 +2267,22 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags)  	 * must also discard the blockdev buffers so that we see the  	 * changes done by userspace on the next quotaon() */  	for (cnt = 0; cnt < MAXQUOTAS; cnt++) -		/* This can happen when suspending quotas on remount-ro... */ -		if (toputinode[cnt] && !sb_has_quota_loaded(sb, cnt)) { -			inode_lock(toputinode[cnt]); -			toputinode[cnt]->i_flags &= ~S_NOQUOTA; -			truncate_inode_pages(&toputinode[cnt]->i_data, 0); -			inode_unlock(toputinode[cnt]); -			mark_inode_dirty_sync(toputinode[cnt]); +		if (!sb_has_quota_loaded(sb, cnt) && dqopt->files[cnt]) { +			inode_lock(dqopt->files[cnt]); +			truncate_inode_pages(&dqopt->files[cnt]->i_data, 0); +			inode_unlock(dqopt->files[cnt]);  		}  	if (sb->s_bdev)  		invalidate_bdev(sb->s_bdev);  put_inodes: +	/* We are done when suspending quotas */ +	if (flags & DQUOT_SUSPENDED) +		return 0; +  	for (cnt = 0; cnt < MAXQUOTAS; cnt++) -		if (toputinode[cnt]) { -			/* On remount RO, we keep the inode pointer so that we -			 * can reenable quota on the subsequent remount RW. We -			 * have to check 'flags' variable and not use sb_has_ -			 * function because another quotaon / quotaoff could -			 * change global state before we got here. We refuse -			 * to suspend quotas when there is pending delete on -			 * the quota file... */ -			if (!(flags & DQUOT_SUSPENDED)) -				iput(toputinode[cnt]); -			else if (!toputinode[cnt]->i_nlink) -				ret = -EBUSY; -		} -	return ret; +		if (!sb_has_quota_loaded(sb, cnt)) +			vfs_cleanup_quota_inode(sb, cnt); +	return 0;  }  EXPORT_SYMBOL(dquot_disable); @@ -2298,28 +2297,52 @@ EXPORT_SYMBOL(dquot_quota_off);   *	Turn quotas on on a device   */ -/* - * Helper function to turn quotas on when we already have the inode of - * quota file and no quota information is loaded. - */ -static int vfs_load_quota_inode(struct inode *inode, int type, int format_id, +static int vfs_setup_quota_inode(struct inode *inode, int type) +{ +	struct super_block *sb = inode->i_sb; +	struct quota_info *dqopt = sb_dqopt(sb); + +	if (!S_ISREG(inode->i_mode)) +		return -EACCES; +	if (IS_RDONLY(inode)) +		return -EROFS; +	if (sb_has_quota_loaded(sb, type)) +		return -EBUSY; + +	dqopt->files[type] = igrab(inode); +	if (!dqopt->files[type]) +		return -EIO; +	if (!(dqopt->flags & DQUOT_QUOTA_SYS_FILE)) { +		/* We don't want quota and atime on quota files (deadlocks +		 * possible) Also nobody should write to the file - we use +		 * special IO operations which ignore the immutable bit. */ +		inode_lock(inode); +		inode->i_flags |= S_NOQUOTA; +		inode_unlock(inode); +		/* +		 * When S_NOQUOTA is set, remove dquot references as no more +		 * references can be added +		 */ +		__dquot_drop(inode); +	} +	return 0; +} + +int dquot_load_quota_sb(struct super_block *sb, int type, int format_id,  	unsigned int flags)  {  	struct quota_format_type *fmt = find_quota_format(format_id); -	struct super_block *sb = inode->i_sb;  	struct quota_info *dqopt = sb_dqopt(sb);  	int error; +	/* Just unsuspend quotas? */ +	BUG_ON(flags & DQUOT_SUSPENDED); +	/* s_umount should be held in exclusive mode */ +	if (WARN_ON_ONCE(down_read_trylock(&sb->s_umount))) +		up_read(&sb->s_umount); +  	if (!fmt)  		return -ESRCH; -	if (!S_ISREG(inode->i_mode)) { -		error = -EACCES; -		goto out_fmt; -	} -	if (IS_RDONLY(inode)) { -		error = -EROFS; -		goto out_fmt; -	}  	if (!sb->s_op->quota_write || !sb->s_op->quota_read ||  	    (type == PRJQUOTA && sb->dq_op->get_projid == NULL)) {  		error = -EINVAL; @@ -2351,27 +2374,9 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,  		invalidate_bdev(sb->s_bdev);  	} -	if (!(dqopt->flags & DQUOT_QUOTA_SYS_FILE)) { -		/* We don't want quota and atime on quota files (deadlocks -		 * possible) Also nobody should write to the file - we use -		 * special IO operations which ignore the immutable bit. */ -		inode_lock(inode); -		inode->i_flags |= S_NOQUOTA; -		inode_unlock(inode); -		/* -		 * When S_NOQUOTA is set, remove dquot references as no more -		 * references can be added -		 */ -		__dquot_drop(inode); -	} - -	error = -EIO; -	dqopt->files[type] = igrab(inode); -	if (!dqopt->files[type]) -		goto out_file_flags;  	error = -EINVAL;  	if (!fmt->qf_ops->check_quota_file(sb, type)) -		goto out_file_init; +		goto out_fmt;  	dqopt->ops[type] = fmt->qf_ops;  	dqopt->info[type].dqi_format = fmt; @@ -2379,7 +2384,7 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,  	INIT_LIST_HEAD(&dqopt->info[type].dqi_dirty_list);  	error = dqopt->ops[type]->read_file_info(sb, type);  	if (error < 0) -		goto out_file_init; +		goto out_fmt;  	if (dqopt->flags & DQUOT_QUOTA_SYS_FILE) {  		spin_lock(&dq_data_lock);  		dqopt->info[type].dqi_flags |= DQF_SYS_FILE; @@ -2394,24 +2399,36 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,  		dquot_disable(sb, type, flags);  	return error; -out_file_init: -	dqopt->files[type] = NULL; -	iput(inode); -out_file_flags: -	inode_lock(inode); -	inode->i_flags &= ~S_NOQUOTA; -	inode_unlock(inode);  out_fmt:  	put_quota_format(fmt);  	return error;  } +EXPORT_SYMBOL(dquot_load_quota_sb); + +/* + * More powerful function for turning on quotas on given quota inode allowing + * setting of individual quota flags + */ +int dquot_load_quota_inode(struct inode *inode, int type, int format_id, +	unsigned int flags) +{ +	int err; + +	err = vfs_setup_quota_inode(inode, type); +	if (err < 0) +		return err; +	err = dquot_load_quota_sb(inode->i_sb, type, format_id, flags); +	if (err < 0) +		vfs_cleanup_quota_inode(inode->i_sb, type); +	return err; +} +EXPORT_SYMBOL(dquot_load_quota_inode);  /* Reenable quotas on remount RW */  int dquot_resume(struct super_block *sb, int type)  {  	struct quota_info *dqopt = sb_dqopt(sb); -	struct inode *inode;  	int ret = 0, cnt;  	unsigned int flags; @@ -2425,8 +2442,6 @@ int dquot_resume(struct super_block *sb, int type)  		if (!sb_has_quota_suspended(sb, cnt))  			continue; -		inode = dqopt->files[cnt]; -		dqopt->files[cnt] = NULL;  		spin_lock(&dq_state_lock);  		flags = dqopt->flags & dquot_state_flag(DQUOT_USAGE_ENABLED |  							DQUOT_LIMITS_ENABLED, @@ -2435,9 +2450,10 @@ int dquot_resume(struct super_block *sb, int type)  		spin_unlock(&dq_state_lock);  		flags = dquot_generic_flag(flags, cnt); -		ret = vfs_load_quota_inode(inode, cnt, -				dqopt->info[cnt].dqi_fmt_id, flags); -		iput(inode); +		ret = dquot_load_quota_sb(sb, cnt, dqopt->info[cnt].dqi_fmt_id, +					  flags); +		if (ret < 0) +			vfs_cleanup_quota_inode(sb, type);  	}  	return ret; @@ -2454,7 +2470,7 @@ int dquot_quota_on(struct super_block *sb, int type, int format_id,  	if (path->dentry->d_sb != sb)  		error = -EXDEV;  	else -		error = vfs_load_quota_inode(d_inode(path->dentry), type, +		error = dquot_load_quota_inode(d_inode(path->dentry), type,  					     format_id, DQUOT_USAGE_ENABLED |  					     DQUOT_LIMITS_ENABLED);  	return error; @@ -2462,41 +2478,6 @@ int dquot_quota_on(struct super_block *sb, int type, int format_id,  EXPORT_SYMBOL(dquot_quota_on);  /* - * More powerful function for turning on quotas allowing setting - * of individual quota flags - */ -int dquot_enable(struct inode *inode, int type, int format_id, -		 unsigned int flags) -{ -	struct super_block *sb = inode->i_sb; - -	/* Just unsuspend quotas? */ -	BUG_ON(flags & DQUOT_SUSPENDED); -	/* s_umount should be held in exclusive mode */ -	if (WARN_ON_ONCE(down_read_trylock(&sb->s_umount))) -		up_read(&sb->s_umount); - -	if (!flags) -		return 0; -	/* Just updating flags needed? */ -	if (sb_has_quota_loaded(sb, type)) { -		if (flags & DQUOT_USAGE_ENABLED && -		    sb_has_quota_usage_enabled(sb, type)) -			return -EBUSY; -		if (flags & DQUOT_LIMITS_ENABLED && -		    sb_has_quota_limits_enabled(sb, type)) -			return -EBUSY; -		spin_lock(&dq_state_lock); -		sb_dqopt(sb)->flags |= dquot_state_flag(flags, type); -		spin_unlock(&dq_state_lock); -		return 0; -	} - -	return vfs_load_quota_inode(inode, type, format_id, flags); -} -EXPORT_SYMBOL(dquot_enable); - -/*   * This function is used when filesystem needs to initialize quotas   * during mount time.   */ @@ -2517,7 +2498,7 @@ int dquot_quota_on_mount(struct super_block *sb, char *qf_name,  	error = security_quota_on(dentry);  	if (!error) -		error = vfs_load_quota_inode(d_inode(dentry), type, format_id, +		error = dquot_load_quota_inode(d_inode(dentry), type, format_id,  				DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED);  out: @@ -2542,13 +2523,17 @@ static int dquot_quota_enable(struct super_block *sb, unsigned int flags)  		if (!(flags & qtype_enforce_flag(type)))  			continue;  		/* Can't enforce without accounting */ -		if (!sb_has_quota_usage_enabled(sb, type)) -			return -EINVAL; -		ret = dquot_enable(dqopt->files[type], type, -				   dqopt->info[type].dqi_fmt_id, -				   DQUOT_LIMITS_ENABLED); -		if (ret < 0) +		if (!sb_has_quota_usage_enabled(sb, type)) { +			ret = -EINVAL; +			goto out_err; +		} +		if (sb_has_quota_limits_enabled(sb, type)) { +			ret = -EBUSY;  			goto out_err; +		} +		spin_lock(&dq_state_lock); +		dqopt->flags |= dquot_state_flag(DQUOT_LIMITS_ENABLED, type); +		spin_unlock(&dq_state_lock);  	}  	return 0;  out_err: @@ -2598,10 +2583,12 @@ static int dquot_quota_disable(struct super_block *sb, unsigned int flags)  out_err:  	/* Backout enforcement disabling we already did */  	for (type--; type >= 0; type--)  { -		if (flags & qtype_enforce_flag(type)) -			dquot_enable(dqopt->files[type], type, -				     dqopt->info[type].dqi_fmt_id, -				     DQUOT_LIMITS_ENABLED); +		if (flags & qtype_enforce_flag(type)) { +			spin_lock(&dq_state_lock); +			dqopt->flags |= +				dquot_state_flag(DQUOT_LIMITS_ENABLED, type); +			spin_unlock(&dq_state_lock); +		}  	}  	return ret;  } @@ -2799,8 +2786,10 @@ int dquot_get_state(struct super_block *sb, struct qc_state *state)  			tstate->flags |= QCI_LIMITS_ENFORCED;  		tstate->spc_timelimit = mi->dqi_bgrace;  		tstate->ino_timelimit = mi->dqi_igrace; -		tstate->ino = dqopt->files[type]->i_ino; -		tstate->blocks = dqopt->files[type]->i_blocks; +		if (dqopt->files[type]) { +			tstate->ino = dqopt->files[type]->i_ino; +			tstate->blocks = dqopt->files[type]->i_blocks; +		}  		tstate->nextents = 1;	/* We don't know... */  		spin_unlock(&dq_data_lock);  	} |