From b8954e5734b3c53f90c47fbdbbbdb8a23747ef07 Mon Sep 17 00:00:00 2001 From: Zhi Zhang Date: Wed, 9 Oct 2019 11:37:20 +0800 Subject: [PATCH] client: optimize rename operation under different quota root It took very long time to move a large directory to another directory under different quota root, because rename operation was switched to copy and unlink operations, even if the destination directory didn't enable quota or had enough room for source directory. Fixes: http://tracker.ceph.com/issues/39715 Signed-off-by: Zhi Zhang --- src/client/Client.cc | 69 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/src/client/Client.cc b/src/client/Client.cc index 78771ca987a..4cc8bf0c40e 100644 --- a/src/client/Client.cc +++ b/src/client/Client.cc @@ -12624,15 +12624,6 @@ int Client::_rename(Inode *fromdir, const char *fromname, Inode *todir, const ch else return -EROFS; } - if (fromdir != todir) { - Inode *fromdir_root = - fromdir->quota.is_enable() ? fromdir : get_quota_root(fromdir, perm); - Inode *todir_root = - todir->quota.is_enable() ? todir : get_quota_root(todir, perm); - if (fromdir_root != todir_root) { - return -EXDEV; - } - } InodeRef target; MetaRequest *req = new MetaRequest(op); @@ -12665,7 +12656,32 @@ int Client::_rename(Inode *fromdir, const char *fromname, Inode *todir, const ch req->dentry_unless = CEPH_CAP_FILE_EXCL; InodeRef oldin, otherin; - res = _lookup(fromdir, fromname, 0, &oldin, perm); + Inode *fromdir_root = nullptr; + Inode *todir_root = nullptr; + int mask = 0; + bool quota_check = false; + if (fromdir != todir) { + fromdir_root = + fromdir->quota.is_enable() ? fromdir : get_quota_root(fromdir, perm); + todir_root = + todir->quota.is_enable() ? todir : get_quota_root(todir, perm); + + if (todir_root->quota.is_enable() && fromdir_root != todir_root) { + // use CEPH_STAT_RSTAT mask to force send getattr or lookup request + // to auth MDS to get latest rstat for todir_root and source dir + // even if their dentry caches and inode caps are satisfied. + res = _getattr(todir_root, CEPH_STAT_RSTAT, perm, true); + if (res < 0) + goto fail; + + quota_check = true; + if (oldde->inode && oldde->inode->is_dir()) { + mask |= CEPH_STAT_RSTAT; + } + } + } + + res = _lookup(fromdir, fromname, mask, &oldin, perm); if (res < 0) goto fail; @@ -12674,6 +12690,39 @@ int Client::_rename(Inode *fromdir, const char *fromname, Inode *todir, const ch req->set_old_inode(oldinode); req->old_inode_drop = CEPH_CAP_LINK_SHARED; + if (quota_check) { + int64_t old_bytes, old_files; + if (oldinode->is_dir()) { + old_bytes = oldinode->rstat.rbytes; + old_files = oldinode->rstat.rsize(); + } else { + old_bytes = oldinode->size; + old_files = 1; + } + + bool quota_exceed = false; + if (todir_root && todir_root->quota.max_bytes && + (old_bytes + todir_root->rstat.rbytes) >= todir_root->quota.max_bytes) { + ldout(cct, 10) << "_rename (" << oldinode->ino << " bytes=" + << old_bytes << ") to (" << todir->ino + << ") will exceed quota on " << *todir_root << dendl; + quota_exceed = true; + } + + if (todir_root && todir_root->quota.max_files && + (old_files + todir_root->rstat.rsize()) >= todir_root->quota.max_files) { + ldout(cct, 10) << "_rename (" << oldinode->ino << " files=" + << old_files << ") to (" << todir->ino + << ") will exceed quota on " << *todir_root << dendl; + quota_exceed = true; + } + + if (quota_exceed) { + res = (oldinode->is_dir()) ? -EXDEV : -EDQUOT; + goto fail; + } + } + res = _lookup(todir, toname, 0, &otherin, perm); switch (res) { case 0: -- 2.39.5