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

ceph: allow rename operation under different quota realms

Returning -EXDEV when trying to 'mv' files/directories from different
quota realms results in copy+unlink operations instead of the faster
CEPH_MDS_OP_RENAME.  This will occur even when there aren't any quotas
set in the destination directory, or if there's enough space left for
the new file(s).

This patch adds a new helper function to be called on rename operations
which will allow these operations if they can be executed.  This patch
mimics userland fuse client commit b8954e5734b3 ("client:
optimize rename operation under different quota root").

Since ceph_quota_is_same_realm() is now called only from this new
helper, make it static.

URL: https://tracker.ceph.com/issues/44791


Signed-off-by: default avatarLuis Henriques <lhenriques@suse.com>
Reviewed-by: default avatarJeff Layton <jlayton@kernel.org>
Signed-off-by: default avatarIlya Dryomov <idryomov@gmail.com>
parent daa668fb
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -1209,11 +1209,12 @@ static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry,
			op = CEPH_MDS_OP_RENAMESNAP;
		else
			return -EROFS;
	} else if (old_dir != new_dir) {
		err = ceph_quota_check_rename(mdsc, d_inode(old_dentry),
					      new_dir);
		if (err)
			return err;
	}
	/* don't allow cross-quota renames */
	if ((old_dir != new_dir) &&
	    (!ceph_quota_is_same_realm(old_dir, new_dir)))
		return -EXDEV;

	dout("rename dir %p dentry %p to dir %p dentry %p\n",
	     old_dir, old_dentry, new_dir, new_dentry);
+57 −1
Original line number Diff line number Diff line
@@ -264,7 +264,7 @@ restart:
	return NULL;
}

bool ceph_quota_is_same_realm(struct inode *old, struct inode *new)
static bool ceph_quota_is_same_realm(struct inode *old, struct inode *new)
{
	struct ceph_mds_client *mdsc = ceph_inode_to_client(old)->mdsc;
	struct ceph_snap_realm *old_realm, *new_realm;
@@ -516,3 +516,59 @@ bool ceph_quota_update_statfs(struct ceph_fs_client *fsc, struct kstatfs *buf)
	return is_updated;
}

/*
 * ceph_quota_check_rename - check if a rename can be executed
 * @mdsc:	MDS client instance
 * @old:	inode to be copied
 * @new:	destination inode (directory)
 *
 * This function verifies if a rename (e.g. moving a file or directory) can be
 * executed.  It forces an rstat update in the @new target directory (and in the
 * source @old as well, if it's a directory).  The actual check is done both for
 * max_files and max_bytes.
 *
 * This function returns 0 if it's OK to do the rename, or, if quotas are
 * exceeded, -EXDEV (if @old is a directory) or -EDQUOT.
 */
int ceph_quota_check_rename(struct ceph_mds_client *mdsc,
			    struct inode *old, struct inode *new)
{
	struct ceph_inode_info *ci_old = ceph_inode(old);
	int ret = 0;

	if (ceph_quota_is_same_realm(old, new))
		return 0;

	/*
	 * Get the latest rstat for target directory (and for source, if a
	 * directory)
	 */
	ret = ceph_do_getattr(new, CEPH_STAT_RSTAT, false);
	if (ret)
		return ret;

	if (S_ISDIR(old->i_mode)) {
		ret = ceph_do_getattr(old, CEPH_STAT_RSTAT, false);
		if (ret)
			return ret;
		ret = check_quota_exceeded(new, QUOTA_CHECK_MAX_BYTES_OP,
					   ci_old->i_rbytes);
		if (!ret)
			ret = check_quota_exceeded(new,
						   QUOTA_CHECK_MAX_FILES_OP,
						   ci_old->i_rfiles +
						   ci_old->i_rsubdirs);
		if (ret)
			ret = -EXDEV;
	} else {
		ret = check_quota_exceeded(new, QUOTA_CHECK_MAX_BYTES_OP,
					   i_size_read(old));
		if (!ret)
			ret = check_quota_exceeded(new,
						   QUOTA_CHECK_MAX_FILES_OP, 1);
		if (ret)
			ret = -EDQUOT;
	}

	return ret;
}
+2 −1
Original line number Diff line number Diff line
@@ -1210,13 +1210,14 @@ 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);
extern bool ceph_quota_is_same_realm(struct inode *old, struct inode *new);
extern bool ceph_quota_is_max_bytes_exceeded(struct inode *inode,
					     loff_t newlen);
extern bool ceph_quota_is_max_bytes_approaching(struct inode *inode,
						loff_t newlen);
extern bool ceph_quota_update_statfs(struct ceph_fs_client *fsc,
				     struct kstatfs *buf);
extern int ceph_quota_check_rename(struct ceph_mds_client *mdsc,
				   struct inode *old, struct inode *new);
extern void ceph_cleanup_quotarealms_inodes(struct ceph_mds_client *mdsc);

#endif /* _FS_CEPH_SUPER_H */