mirror of
https://github.com/ceph/ceph
synced 2025-03-22 10:17:23 +00:00
Merge pull request #12749 from ukernel/wip-18179
mds: propagate error encountered during opening inode by number Reviewed-by: John Spray <john.spray@redhat.com>
This commit is contained in:
commit
2076cda04a
@ -448,38 +448,45 @@ class TestDamage(CephFSTestCase):
|
||||
# damaged guy if I want.
|
||||
self.mount_a.touch("subdir/file_to_be_damaged")
|
||||
|
||||
def test_corrupt_backtrace(self):
|
||||
def test_open_ino_errors(self):
|
||||
"""
|
||||
That an un-decodeable backtrace leads to an appropriate
|
||||
error trying to follow the backtrace to the file.
|
||||
That errors encountered during opening inos are properly propagated
|
||||
"""
|
||||
|
||||
self.mount_a.run_shell(["mkdir", "alpha"])
|
||||
self.mount_a.run_shell(["mkdir", "bravo"])
|
||||
self.mount_a.run_shell(["touch", "alpha/target"])
|
||||
self.mount_a.run_shell(["ln", "alpha/target", "bravo/hardlink"])
|
||||
self.mount_a.run_shell(["mkdir", "dir1"])
|
||||
self.mount_a.run_shell(["touch", "dir1/file1"])
|
||||
self.mount_a.run_shell(["mkdir", "dir2"])
|
||||
self.mount_a.run_shell(["touch", "dir2/file2"])
|
||||
self.mount_a.run_shell(["mkdir", "testdir"])
|
||||
self.mount_a.run_shell(["ln", "dir1/file1", "testdir/hardlink1"])
|
||||
self.mount_a.run_shell(["ln", "dir2/file2", "testdir/hardlink2"])
|
||||
|
||||
alpha_ino = self.mount_a.path_to_ino("alpha/target")
|
||||
file1_ino = self.mount_a.path_to_ino("dir1/file1")
|
||||
file2_ino = self.mount_a.path_to_ino("dir2/file2")
|
||||
dir2_ino = self.mount_a.path_to_ino("dir2")
|
||||
|
||||
# Ensure everything is written to backing store
|
||||
self.mount_a.umount_wait()
|
||||
self.fs.mds_asok(["flush", "journal"])
|
||||
|
||||
# Validate that the backtrace is present and decodable
|
||||
self.fs.read_backtrace(alpha_ino)
|
||||
# Go corrupt the backtrace of alpha/target (used for resolving
|
||||
# bravo/hardlink).
|
||||
self.fs._write_data_xattr(alpha_ino, "parent", "rhubarb")
|
||||
|
||||
# Drop everything from the MDS cache
|
||||
self.mds_cluster.mds_stop()
|
||||
self.fs.journal_tool(['journal', 'reset'])
|
||||
self.mds_cluster.mds_fail_restart()
|
||||
self.fs.wait_for_daemons()
|
||||
|
||||
# Check that touching the hardlink gives EIO
|
||||
self.mount_a.mount()
|
||||
ran = self.mount_a.run_shell(["ls", "-l", "bravo/hardlink"], wait=False)
|
||||
|
||||
# Case 1: un-decodeable backtrace
|
||||
|
||||
# Validate that the backtrace is present and decodable
|
||||
self.fs.read_backtrace(file1_ino)
|
||||
# Go corrupt the backtrace of alpha/target (used for resolving
|
||||
# bravo/hardlink).
|
||||
self.fs._write_data_xattr(file1_ino, "parent", "rhubarb")
|
||||
|
||||
# Check that touching the hardlink gives EIO
|
||||
ran = self.mount_a.run_shell(["stat", "testdir/hardlink1"], wait=False)
|
||||
try:
|
||||
ran.wait()
|
||||
except CommandFailedError:
|
||||
@ -492,4 +499,41 @@ class TestDamage(CephFSTestCase):
|
||||
"damage", "ls", '--format=json-pretty'))
|
||||
self.assertEqual(len(damage), 1)
|
||||
self.assertEqual(damage[0]['damage_type'], "backtrace")
|
||||
self.assertEqual(damage[0]['ino'], alpha_ino)
|
||||
self.assertEqual(damage[0]['ino'], file1_ino)
|
||||
|
||||
self.fs.mon_manager.raw_cluster_cmd(
|
||||
'tell', 'mds.{0}'.format(self.fs.get_active_names()[0]),
|
||||
"damage", "rm", str(damage[0]['id']))
|
||||
|
||||
|
||||
# Case 2: missing dirfrag for the target inode
|
||||
|
||||
self.fs.rados(["rm", "{0:x}.00000000".format(dir2_ino)])
|
||||
|
||||
# Check that touching the hardlink gives EIO
|
||||
ran = self.mount_a.run_shell(["stat", "testdir/hardlink2"], wait=False)
|
||||
try:
|
||||
ran.wait()
|
||||
except CommandFailedError:
|
||||
self.assertTrue("Input/output error" in ran.stderr.getvalue())
|
||||
|
||||
# Check that an entry is created in the damage table
|
||||
damage = json.loads(
|
||||
self.fs.mon_manager.raw_cluster_cmd(
|
||||
'tell', 'mds.{0}'.format(self.fs.get_active_names()[0]),
|
||||
"damage", "ls", '--format=json-pretty'))
|
||||
self.assertEqual(len(damage), 2)
|
||||
if damage[0]['damage_type'] == "backtrace" :
|
||||
self.assertEqual(damage[0]['ino'], file2_ino)
|
||||
self.assertEqual(damage[1]['damage_type'], "dir_frag")
|
||||
self.assertEqual(damage[1]['ino'], dir2_ino)
|
||||
else:
|
||||
self.assertEqual(damage[0]['damage_type'], "dir_frag")
|
||||
self.assertEqual(damage[0]['ino'], dir2_ino)
|
||||
self.assertEqual(damage[1]['damage_type'], "backtrace")
|
||||
self.assertEqual(damage[1]['ino'], file2_ino)
|
||||
|
||||
for entry in damage:
|
||||
self.fs.mon_manager.raw_cluster_cmd(
|
||||
'tell', 'mds.{0}'.format(self.fs.get_active_names()[0]),
|
||||
"damage", "rm", str(entry['id']))
|
||||
|
@ -8256,11 +8256,19 @@ class C_IO_MDC_OpenInoBacktraceFetched : public MDCacheIOContext {
|
||||
|
||||
struct C_MDC_OpenInoTraverseDir : public MDCacheContext {
|
||||
inodeno_t ino;
|
||||
MMDSOpenIno *msg;
|
||||
bool parent;
|
||||
public:
|
||||
C_MDC_OpenInoTraverseDir(MDCache *c, inodeno_t i) : MDCacheContext(c), ino(i) {}
|
||||
C_MDC_OpenInoTraverseDir(MDCache *c, inodeno_t i, MMDSOpenIno *m, bool p) :
|
||||
MDCacheContext(c), ino(i), msg(m), parent(p) {}
|
||||
void finish(int r) {
|
||||
if (r < 0 && !parent)
|
||||
r = -EAGAIN;
|
||||
if (msg) {
|
||||
mdcache->handle_open_ino(msg, r);
|
||||
return;
|
||||
}
|
||||
assert(mdcache->opening_inodes.count(ino));
|
||||
assert(r >= 0);
|
||||
mdcache->_open_ino_traverse_dir(ino, mdcache->opening_inodes[ino], r);
|
||||
}
|
||||
};
|
||||
@ -8320,6 +8328,7 @@ void MDCache::_open_ino_backtrace_fetched(inodeno_t ino, bufferlist& bl, int err
|
||||
new C_OnFinisher(fin, mds->finisher));
|
||||
return;
|
||||
}
|
||||
err = 0; // backtrace.ancestors.empty() is checked below
|
||||
}
|
||||
|
||||
if (err == 0) {
|
||||
@ -8330,11 +8339,15 @@ void MDCache::_open_ino_backtrace_fetched(inodeno_t ino, bufferlist& bl, int err
|
||||
if (info.ancestors[0] == backtrace.ancestors[0]) {
|
||||
dout(10) << " got same parents " << info.ancestors[0] << " 2 times" << dendl;
|
||||
err = -EINVAL;
|
||||
} else {
|
||||
info.last_err = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (err) {
|
||||
dout(10) << " failed to open ino " << ino << dendl;
|
||||
dout(0) << " failed to open ino " << ino << " err " << err << "/" << info.last_err << dendl;
|
||||
if (info.last_err)
|
||||
err = info.last_err;
|
||||
open_ino_finish(ino, info, err);
|
||||
return;
|
||||
}
|
||||
@ -8372,14 +8385,6 @@ void MDCache::_open_ino_parent_opened(inodeno_t ino, int ret)
|
||||
}
|
||||
}
|
||||
|
||||
MDSInternalContextBase* MDCache::_open_ino_get_waiter(inodeno_t ino, MMDSOpenIno *m)
|
||||
{
|
||||
if (m)
|
||||
return new C_MDS_RetryMessage(mds, m);
|
||||
else
|
||||
return new C_MDC_OpenInoTraverseDir(this, ino);
|
||||
}
|
||||
|
||||
void MDCache::_open_ino_traverse_dir(inodeno_t ino, open_ino_info_t& info, int ret)
|
||||
{
|
||||
dout(10) << __func__ << ": ino " << ino << " ret " << ret << dendl;
|
||||
@ -8406,11 +8411,11 @@ void MDCache::_open_ino_traverse_dir(inodeno_t ino, open_ino_info_t& info, int r
|
||||
do_open_ino(ino, info, ret);
|
||||
}
|
||||
|
||||
void MDCache::_open_ino_fetch_dir(inodeno_t ino, MMDSOpenIno *m, CDir *dir)
|
||||
void MDCache::_open_ino_fetch_dir(inodeno_t ino, MMDSOpenIno *m, CDir *dir, bool parent)
|
||||
{
|
||||
if (dir->state_test(CDir::STATE_REJOINUNDEF))
|
||||
assert(dir->get_inode()->dirfragtree.is_leaf(dir->get_frag()));
|
||||
dir->fetch(_open_ino_get_waiter(ino, m));
|
||||
dir->fetch(new C_MDC_OpenInoTraverseDir(this, ino, m, parent));
|
||||
}
|
||||
|
||||
int MDCache::open_ino_traverse_dir(inodeno_t ino, MMDSOpenIno *m,
|
||||
@ -8424,7 +8429,7 @@ int MDCache::open_ino_traverse_dir(inodeno_t ino, MMDSOpenIno *m,
|
||||
|
||||
if (!diri) {
|
||||
if (discover && MDS_INO_IS_MDSDIR(ancestors[i].dirino)) {
|
||||
open_foreign_mdsdir(ancestors[i].dirino, _open_ino_get_waiter(ino, m));
|
||||
open_foreign_mdsdir(ancestors[i].dirino, new C_MDC_OpenInoTraverseDir(this, ino, m, i == 0));
|
||||
return 1;
|
||||
}
|
||||
continue;
|
||||
@ -8435,7 +8440,7 @@ int MDCache::open_ino_traverse_dir(inodeno_t ino, MMDSOpenIno *m,
|
||||
while (dir->state_test(CDir::STATE_REJOINUNDEF) &&
|
||||
dir->get_inode()->state_test(CInode::STATE_REJOINUNDEF))
|
||||
dir = dir->get_inode()->get_parent_dir();
|
||||
_open_ino_fetch_dir(ino, m, dir);
|
||||
_open_ino_fetch_dir(ino, m, dir, i == 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -8453,12 +8458,12 @@ int MDCache::open_ino_traverse_dir(inodeno_t ino, MMDSOpenIno *m,
|
||||
if (diri->is_auth()) {
|
||||
if (diri->is_frozen()) {
|
||||
dout(10) << " " << *diri << " is frozen, waiting " << dendl;
|
||||
diri->add_waiter(CDir::WAIT_UNFREEZE, _open_ino_get_waiter(ino, m));
|
||||
diri->add_waiter(CDir::WAIT_UNFREEZE, new C_MDC_OpenInoTraverseDir(this, ino, m, i == 0));
|
||||
return 1;
|
||||
}
|
||||
dir = diri->get_or_open_dirfrag(this, fg);
|
||||
} else if (discover) {
|
||||
open_remote_dirfrag(diri, fg, _open_ino_get_waiter(ino, m));
|
||||
open_remote_dirfrag(diri, fg, new C_MDC_OpenInoTraverseDir(this, ino, m, i == 0));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@ -8470,14 +8475,14 @@ int MDCache::open_ino_traverse_dir(inodeno_t ino, MMDSOpenIno *m,
|
||||
if (dnl && dnl->is_primary() &&
|
||||
dnl->get_inode()->state_test(CInode::STATE_REJOINUNDEF)) {
|
||||
dout(10) << " fetching undef " << *dnl->get_inode() << dendl;
|
||||
_open_ino_fetch_dir(ino, m, dir);
|
||||
_open_ino_fetch_dir(ino, m, dir, i == 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!dnl && !dir->is_complete() &&
|
||||
(!dir->has_bloom() || dir->is_in_bloom(name))) {
|
||||
dout(10) << " fetching incomplete " << *dir << dendl;
|
||||
_open_ino_fetch_dir(ino, m, dir);
|
||||
_open_ino_fetch_dir(ino, m, dir, i == 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -8487,13 +8492,13 @@ int MDCache::open_ino_traverse_dir(inodeno_t ino, MMDSOpenIno *m,
|
||||
} else if (discover) {
|
||||
if (!dnl) {
|
||||
filepath path(name, 0);
|
||||
discover_path(dir, CEPH_NOSNAP, path, _open_ino_get_waiter(ino, m),
|
||||
discover_path(dir, CEPH_NOSNAP, path, new C_MDC_OpenInoTraverseDir(this, ino, m, i == 0),
|
||||
(i == 0 && want_xlocked));
|
||||
return 1;
|
||||
}
|
||||
if (dnl->is_null() && !dn->lock.can_read(-1)) {
|
||||
dout(10) << " null " << *dn << " is not readable, waiting" << dendl;
|
||||
dn->lock.add_waiter(SimpleLock::WAIT_RD, _open_ino_get_waiter(ino, m));
|
||||
dn->lock.add_waiter(SimpleLock::WAIT_RD, new C_MDC_OpenInoTraverseDir(this, ino, m, i == 0));
|
||||
return 1;
|
||||
}
|
||||
dout(10) << " no ino " << next_ino << " in " << *dir << dendl;
|
||||
@ -8520,7 +8525,7 @@ void MDCache::open_ino_finish(inodeno_t ino, open_ino_info_t& info, int ret)
|
||||
|
||||
void MDCache::do_open_ino(inodeno_t ino, open_ino_info_t& info, int err)
|
||||
{
|
||||
if (err < 0) {
|
||||
if (err < 0 && err != -EAGAIN) {
|
||||
info.checked.clear();
|
||||
info.checked.insert(mds->get_nodeid());
|
||||
info.checking = MDS_RANK_NONE;
|
||||
@ -8530,6 +8535,8 @@ void MDCache::do_open_ino(inodeno_t ino, open_ino_info_t& info, int err)
|
||||
info.discover = false;
|
||||
info.ancestors.clear();
|
||||
}
|
||||
if (err != -ENOENT && err != -ENOTDIR)
|
||||
info.last_err = err;
|
||||
}
|
||||
|
||||
if (info.check_peers) {
|
||||
@ -8591,9 +8598,9 @@ void MDCache::do_open_ino_peer(inodeno_t ino, open_ino_info_t& info)
|
||||
}
|
||||
}
|
||||
|
||||
void MDCache::handle_open_ino(MMDSOpenIno *m)
|
||||
void MDCache::handle_open_ino(MMDSOpenIno *m, int err)
|
||||
{
|
||||
dout(10) << "handle_open_ino " << *m << dendl;
|
||||
dout(10) << "handle_open_ino " << *m << " err " << err << dendl;
|
||||
|
||||
inodeno_t ino = m->ino;
|
||||
MMDSOpenInoReply *reply;
|
||||
@ -8615,6 +8622,8 @@ void MDCache::handle_open_ino(MMDSOpenIno *m)
|
||||
} else {
|
||||
reply->hint = in->authority().first;
|
||||
}
|
||||
} else if (err < 0) {
|
||||
reply = new MMDSOpenInoReply(m->get_tid(), ino, MDS_RANK_NONE, err);
|
||||
} else {
|
||||
mds_rank_t hint = MDS_RANK_NONE;
|
||||
int ret = open_ino_traverse_dir(ino, m, m->ancestors, false, false, &hint);
|
||||
|
@ -912,10 +912,12 @@ protected:
|
||||
bool want_xlocked;
|
||||
version_t tid;
|
||||
int64_t pool;
|
||||
int last_err;
|
||||
list<MDSInternalContextBase*> waiters;
|
||||
open_ino_info_t() : checking(MDS_RANK_NONE), auth_hint(MDS_RANK_NONE),
|
||||
check_peers(true), fetch_backtrace(true), discover(false),
|
||||
want_replica(false), want_xlocked(false), tid(0), pool(-1) {}
|
||||
want_replica(false), want_xlocked(false), tid(0), pool(-1),
|
||||
last_err(0) {}
|
||||
};
|
||||
ceph_tid_t open_ino_last_tid;
|
||||
map<inodeno_t,open_ino_info_t> opening_inodes;
|
||||
@ -923,15 +925,14 @@ protected:
|
||||
void _open_ino_backtrace_fetched(inodeno_t ino, bufferlist& bl, int err);
|
||||
void _open_ino_parent_opened(inodeno_t ino, int ret);
|
||||
void _open_ino_traverse_dir(inodeno_t ino, open_ino_info_t& info, int err);
|
||||
void _open_ino_fetch_dir(inodeno_t ino, MMDSOpenIno *m, CDir *dir);
|
||||
MDSInternalContextBase* _open_ino_get_waiter(inodeno_t ino, MMDSOpenIno *m);
|
||||
void _open_ino_fetch_dir(inodeno_t ino, MMDSOpenIno *m, CDir *dir, bool parent);
|
||||
int open_ino_traverse_dir(inodeno_t ino, MMDSOpenIno *m,
|
||||
vector<inode_backpointer_t>& ancestors,
|
||||
bool discover, bool want_xlocked, mds_rank_t *hint);
|
||||
void open_ino_finish(inodeno_t ino, open_ino_info_t& info, int err);
|
||||
void do_open_ino(inodeno_t ino, open_ino_info_t& info, int err);
|
||||
void do_open_ino_peer(inodeno_t ino, open_ino_info_t& info);
|
||||
void handle_open_ino(MMDSOpenIno *m);
|
||||
void handle_open_ino(MMDSOpenIno *m, int err=0);
|
||||
void handle_open_ino_reply(MMDSOpenInoReply *m);
|
||||
friend class C_IO_MDC_OpenInoBacktraceFetched;
|
||||
friend struct C_MDC_OpenInoTraverseDir;
|
||||
|
Loading…
Reference in New Issue
Block a user