From: Zhi Zhang Date: Wed, 9 Oct 2019 03:37:20 +0000 (+0800) Subject: client: optimize rename operation under different quota root X-Git-Tag: v15.1.0~1074^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=b8954e5734b3c53f90c47fbdbbbdb8a23747ef07;p=ceph.git 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 --- 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: