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:
John Spray 2017-01-13 11:18:59 +00:00 committed by GitHub
commit 2076cda04a
3 changed files with 99 additions and 45 deletions

View File

@ -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']))

View File

@ -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);

View File

@ -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;