Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Luis Henriques <lhenriques@suse.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>

authored by

Luis Henriques and committed by
Ilya Dryomov
dffdcd71 daa668fb

+64 -6
+5 -4
fs/ceph/dir.c
··· 1209 1209 op = CEPH_MDS_OP_RENAMESNAP; 1210 1210 else 1211 1211 return -EROFS; 1212 + } else if (old_dir != new_dir) { 1213 + err = ceph_quota_check_rename(mdsc, d_inode(old_dentry), 1214 + new_dir); 1215 + if (err) 1216 + return err; 1212 1217 } 1213 - /* don't allow cross-quota renames */ 1214 - if ((old_dir != new_dir) && 1215 - (!ceph_quota_is_same_realm(old_dir, new_dir))) 1216 - return -EXDEV; 1217 1218 1218 1219 dout("rename dir %p dentry %p to dir %p dentry %p\n", 1219 1220 old_dir, old_dentry, new_dir, new_dentry);
+57 -1
fs/ceph/quota.c
··· 264 264 return NULL; 265 265 } 266 266 267 - bool ceph_quota_is_same_realm(struct inode *old, struct inode *new) 267 + static bool ceph_quota_is_same_realm(struct inode *old, struct inode *new) 268 268 { 269 269 struct ceph_mds_client *mdsc = ceph_inode_to_client(old)->mdsc; 270 270 struct ceph_snap_realm *old_realm, *new_realm; ··· 516 516 return is_updated; 517 517 } 518 518 519 + /* 520 + * ceph_quota_check_rename - check if a rename can be executed 521 + * @mdsc: MDS client instance 522 + * @old: inode to be copied 523 + * @new: destination inode (directory) 524 + * 525 + * This function verifies if a rename (e.g. moving a file or directory) can be 526 + * executed. It forces an rstat update in the @new target directory (and in the 527 + * source @old as well, if it's a directory). The actual check is done both for 528 + * max_files and max_bytes. 529 + * 530 + * This function returns 0 if it's OK to do the rename, or, if quotas are 531 + * exceeded, -EXDEV (if @old is a directory) or -EDQUOT. 532 + */ 533 + int ceph_quota_check_rename(struct ceph_mds_client *mdsc, 534 + struct inode *old, struct inode *new) 535 + { 536 + struct ceph_inode_info *ci_old = ceph_inode(old); 537 + int ret = 0; 538 + 539 + if (ceph_quota_is_same_realm(old, new)) 540 + return 0; 541 + 542 + /* 543 + * Get the latest rstat for target directory (and for source, if a 544 + * directory) 545 + */ 546 + ret = ceph_do_getattr(new, CEPH_STAT_RSTAT, false); 547 + if (ret) 548 + return ret; 549 + 550 + if (S_ISDIR(old->i_mode)) { 551 + ret = ceph_do_getattr(old, CEPH_STAT_RSTAT, false); 552 + if (ret) 553 + return ret; 554 + ret = check_quota_exceeded(new, QUOTA_CHECK_MAX_BYTES_OP, 555 + ci_old->i_rbytes); 556 + if (!ret) 557 + ret = check_quota_exceeded(new, 558 + QUOTA_CHECK_MAX_FILES_OP, 559 + ci_old->i_rfiles + 560 + ci_old->i_rsubdirs); 561 + if (ret) 562 + ret = -EXDEV; 563 + } else { 564 + ret = check_quota_exceeded(new, QUOTA_CHECK_MAX_BYTES_OP, 565 + i_size_read(old)); 566 + if (!ret) 567 + ret = check_quota_exceeded(new, 568 + QUOTA_CHECK_MAX_FILES_OP, 1); 569 + if (ret) 570 + ret = -EDQUOT; 571 + } 572 + 573 + return ret; 574 + }
+2 -1
fs/ceph/super.h
··· 1210 1210 struct ceph_mds_session *session, 1211 1211 struct ceph_msg *msg); 1212 1212 extern bool ceph_quota_is_max_files_exceeded(struct inode *inode); 1213 - extern bool ceph_quota_is_same_realm(struct inode *old, struct inode *new); 1214 1213 extern bool ceph_quota_is_max_bytes_exceeded(struct inode *inode, 1215 1214 loff_t newlen); 1216 1215 extern bool ceph_quota_is_max_bytes_approaching(struct inode *inode, 1217 1216 loff_t newlen); 1218 1217 extern bool ceph_quota_update_statfs(struct ceph_fs_client *fsc, 1219 1218 struct kstatfs *buf); 1219 + extern int ceph_quota_check_rename(struct ceph_mds_client *mdsc, 1220 + struct inode *old, struct inode *new); 1220 1221 extern void ceph_cleanup_quotarealms_inodes(struct ceph_mds_client *mdsc); 1221 1222 1222 1223 #endif /* _FS_CEPH_SUPER_H */