Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b7a29217 authored by Luis Henriques's avatar Luis Henriques Committed by Ilya Dryomov
Browse files

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: default avatarLuis Henriques <lhenriques@suse.com>
Reviewed-by: default avatar"Yan, Zheng" <zyan@redhat.com>
Signed-off-by: default avatarIlya Dryomov <idryomov@gmail.com>
parent fb18a575
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -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)
+3 −1
Original line number Diff line number Diff line
@@ -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;
+80 −0
Original line number Diff line number Diff line
@@ -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);
}
+1 −0
Original line number Diff line number Diff line
@@ -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 */