mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	ceph: quota: support for ceph.quota.max_files
This patch adds support for the max_files quota. It hooks into all the ceph functions that add new filesystem objects that need to be checked against the quota limits. When these limits are hit, -EDQUOT is returned. Note that we're not checking quotas on ceph_link(). ceph_link doesn't really create a new inode, and since the MDS doesn't update the directory statistics when a new (hard) link is created (only with symlinks), they are not accounted as a new file. Signed-off-by: Luis Henriques <lhenriques@suse.com> Reviewed-by: "Yan, Zheng" <zyan@redhat.com> Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
This commit is contained in:
		
							parent
							
								
									fb18a57568
								
							
						
					
					
						commit
						b7a2921765
					
				
					 4 changed files with 95 additions and 1 deletions
				
			
		| 
						 | 
				
			
			@ -828,6 +828,9 @@ static int ceph_mknod(struct inode *dir, struct dentry *dentry,
 | 
			
		|||
	if (ceph_snap(dir) != CEPH_NOSNAP)
 | 
			
		||||
		return -EROFS;
 | 
			
		||||
 | 
			
		||||
	if (ceph_quota_is_max_files_exceeded(dir))
 | 
			
		||||
		return -EDQUOT;
 | 
			
		||||
 | 
			
		||||
	err = ceph_pre_init_acls(dir, &mode, &acls);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
| 
						 | 
				
			
			@ -881,6 +884,9 @@ static int ceph_symlink(struct inode *dir, struct dentry *dentry,
 | 
			
		|||
	if (ceph_snap(dir) != CEPH_NOSNAP)
 | 
			
		||||
		return -EROFS;
 | 
			
		||||
 | 
			
		||||
	if (ceph_quota_is_max_files_exceeded(dir))
 | 
			
		||||
		return -EDQUOT;
 | 
			
		||||
 | 
			
		||||
	dout("symlink in dir %p dentry %p to '%s'\n", dir, dentry, dest);
 | 
			
		||||
	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SYMLINK, USE_AUTH_MDS);
 | 
			
		||||
	if (IS_ERR(req)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -930,6 +936,11 @@ static int ceph_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 | 
			
		|||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ceph_quota_is_max_files_exceeded(dir)) {
 | 
			
		||||
		err = -EDQUOT;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mode |= S_IFDIR;
 | 
			
		||||
	err = ceph_pre_init_acls(dir, &mode, &acls);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -402,7 +402,7 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
 | 
			
		|||
	struct ceph_mds_request *req;
 | 
			
		||||
	struct dentry *dn;
 | 
			
		||||
	struct ceph_acls_info acls = {};
 | 
			
		||||
       int mask;
 | 
			
		||||
	int mask;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	dout("atomic_open %p dentry %p '%pd' %s flags %d mode 0%o\n",
 | 
			
		||||
| 
						 | 
				
			
			@ -413,6 +413,8 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
 | 
			
		|||
		return -ENAMETOOLONG;
 | 
			
		||||
 | 
			
		||||
	if (flags & O_CREAT) {
 | 
			
		||||
		if (ceph_quota_is_max_files_exceeded(dir))
 | 
			
		||||
			return -EDQUOT;
 | 
			
		||||
		err = ceph_pre_init_acls(dir, &mode, &acls);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -63,3 +63,83 @@ void ceph_handle_quota(struct ceph_mds_client *mdsc,
 | 
			
		|||
 | 
			
		||||
	iput(inode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum quota_check_op {
 | 
			
		||||
	QUOTA_CHECK_MAX_FILES_OP	/* check quota max_files limit */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * check_quota_exceeded() will walk up the snaprealm hierarchy and, for each
 | 
			
		||||
 * realm, it will execute quota check operation defined by the 'op' parameter.
 | 
			
		||||
 * The snaprealm walk is interrupted if the quota check detects that the quota
 | 
			
		||||
 * is exceeded or if the root inode is reached.
 | 
			
		||||
 */
 | 
			
		||||
static bool check_quota_exceeded(struct inode *inode, enum quota_check_op op,
 | 
			
		||||
				 loff_t delta)
 | 
			
		||||
{
 | 
			
		||||
	struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
 | 
			
		||||
	struct ceph_inode_info *ci;
 | 
			
		||||
	struct ceph_snap_realm *realm, *next;
 | 
			
		||||
	struct ceph_vino vino;
 | 
			
		||||
	struct inode *in;
 | 
			
		||||
	u64 max, rvalue;
 | 
			
		||||
	bool is_root;
 | 
			
		||||
	bool exceeded = false;
 | 
			
		||||
 | 
			
		||||
	down_read(&mdsc->snap_rwsem);
 | 
			
		||||
	realm = ceph_inode(inode)->i_snap_realm;
 | 
			
		||||
	ceph_get_snap_realm(mdsc, realm);
 | 
			
		||||
	while (realm) {
 | 
			
		||||
		vino.ino = realm->ino;
 | 
			
		||||
		vino.snap = CEPH_NOSNAP;
 | 
			
		||||
		in = ceph_find_inode(inode->i_sb, vino);
 | 
			
		||||
		if (!in) {
 | 
			
		||||
			pr_warn("Failed to find inode for %llu\n", vino.ino);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		ci = ceph_inode(in);
 | 
			
		||||
		spin_lock(&ci->i_ceph_lock);
 | 
			
		||||
		if (op == QUOTA_CHECK_MAX_FILES_OP) {
 | 
			
		||||
			max = ci->i_max_files;
 | 
			
		||||
			rvalue = ci->i_rfiles + ci->i_rsubdirs;
 | 
			
		||||
		}
 | 
			
		||||
		is_root = (ci->i_vino.ino == CEPH_INO_ROOT);
 | 
			
		||||
		spin_unlock(&ci->i_ceph_lock);
 | 
			
		||||
		switch (op) {
 | 
			
		||||
		case QUOTA_CHECK_MAX_FILES_OP:
 | 
			
		||||
			exceeded = (max && (rvalue >= max));
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			/* Shouldn't happen */
 | 
			
		||||
			pr_warn("Invalid quota check op (%d)\n", op);
 | 
			
		||||
			exceeded = true; /* Just break the loop */
 | 
			
		||||
		}
 | 
			
		||||
		iput(in);
 | 
			
		||||
 | 
			
		||||
		if (is_root || exceeded)
 | 
			
		||||
			break;
 | 
			
		||||
		next = realm->parent;
 | 
			
		||||
		ceph_get_snap_realm(mdsc, next);
 | 
			
		||||
		ceph_put_snap_realm(mdsc, realm);
 | 
			
		||||
		realm = next;
 | 
			
		||||
	}
 | 
			
		||||
	ceph_put_snap_realm(mdsc, realm);
 | 
			
		||||
	up_read(&mdsc->snap_rwsem);
 | 
			
		||||
 | 
			
		||||
	return exceeded;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ceph_quota_is_max_files_exceeded - check if we can create a new file
 | 
			
		||||
 * @inode:	directory where a new file is being created
 | 
			
		||||
 *
 | 
			
		||||
 * This functions returns true is max_files quota allows a new file to be
 | 
			
		||||
 * created.  It is necessary to walk through the snaprealm hierarchy (until the
 | 
			
		||||
 * FS root) to check all realms with quotas set.
 | 
			
		||||
 */
 | 
			
		||||
bool ceph_quota_is_max_files_exceeded(struct inode *inode)
 | 
			
		||||
{
 | 
			
		||||
	WARN_ON(!S_ISDIR(inode->i_mode));
 | 
			
		||||
 | 
			
		||||
	return check_quota_exceeded(inode, QUOTA_CHECK_MAX_FILES_OP, 0);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1077,5 +1077,6 @@ extern void ceph_fs_debugfs_cleanup(struct ceph_fs_client *client);
 | 
			
		|||
extern void ceph_handle_quota(struct ceph_mds_client *mdsc,
 | 
			
		||||
			      struct ceph_mds_session *session,
 | 
			
		||||
			      struct ceph_msg *msg);
 | 
			
		||||
extern bool ceph_quota_is_max_files_exceeded(struct inode *inode);
 | 
			
		||||
 | 
			
		||||
#endif /* _FS_CEPH_SUPER_H */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue