self.mount_a.run_python(template.format(
fill_mb=self.fill_mb,
file_path=file_path,
- full_wait=full_wait
+ full_wait=full_wait,
+ is_fuse=isinstance(self.mount_a, FuseMount)
))
def test_full_fclose(self):
# Wait long enough for a background flush that should fail
time.sleep(30)
- # ...and check that the failed background flush is reflected in fclose
- try:
- os.close(f)
- except OSError:
- print "close() returned an error as expected"
+ if {is_fuse}:
+ # ...and check that the failed background flush is reflected in fclose
+ try:
+ os.close(f)
+ except OSError:
+ print "close() returned an error as expected"
+ else:
+ raise RuntimeError("close() failed to raise error")
else:
- raise RuntimeError("close() failed to raise error")
+ # The kernel cephfs client does not raise errors on fclose
+ os.close(f)
os.unlink("{file_path}")
""")
if not full:
raise RuntimeError("Failed to reach fullness after writing %d bytes" % bytes)
- # The error sticks to the inode until we dispose of it
- try:
- os.close(f)
- except OSError:
- print "Saw error from close() as expected"
- else:
- raise RuntimeError("Did not see expected error from close()")
+ # close() should not raise an error because we already caught it in
+ # fsync. There shouldn't have been any more writeback errors
+ # since then because all IOs got cancelled on the full flag.
+ print "calling close"
+ os.close(f)
+ print "close() did not raise error"
os.unlink("{file_path}")
""")
set(libclient_srcs
Client.cc
Dentry.cc
+ Fh.cc
Inode.cc
MetaRequest.cc
ClientSnapRealm.cc
ldout(cct, 4) << __func__ << ": FULL: inode 0x" << std::hex << i->first << std::dec
<< " has dirty objects, purging and setting ENOSPC" << dendl;
objectcacher->purge_set(&inode->oset);
- inode->async_err = -ENOSPC;
+ inode->set_async_err(-ENOSPC);
}
}
ldout(client->cct, 1) << "I/O error from flush on inode " << inode
<< " 0x" << std::hex << inode->ino << std::dec
<< ": " << r << "(" << cpp_strerror(r) << ")" << dendl;
- inode->async_err = r;
+ inode->set_async_err(r);
}
}
};
Fh *Client::_create_fh(Inode *in, int flags, int cmode, const UserPerm& perms)
{
- // yay
- Fh *f = new Fh;
+ assert(in);
+ Fh *f = new Fh(in);
f->mode = cmode;
f->flags = flags;
// inode
- assert(in);
- f->inode = in;
f->actor_perms = perms;
ldout(cct, 10) << "_create_fh " << in->ino << " mode " << cmode << dendl;
_release_filelocks(f);
- // Finally, read any async err (i.e. from flushes) from the inode
- int err = in->async_err;
+ // Finally, read any async err (i.e. from flushes)
+ int err = f->take_async_err();
if (err != 0) {
ldout(cct, 1) << "_release_fh " << f << " on inode " << *in << " caught async_err = "
<< cpp_strerror(err) << dendl;
void Client::_put_fh(Fh *f)
{
int left = f->put();
- if (!left)
+ if (!left) {
delete f;
+ }
}
int Client::_open(Inode *in, int flags, mode_t mode, Fh **fhp,
int Client::_flush(Fh *f)
{
Inode *in = f->inode.get();
- int err = in->async_err;
+ int err = f->take_async_err();
if (err != 0) {
ldout(cct, 1) << __func__ << ": " << f << " on inode " << *in << " caught async_err = "
<< cpp_strerror(err) << dendl;
return -EBADF;
#endif
int r = _fsync(f, syncdataonly);
- ldout(cct, 3) << "fsync(" << fd << ", " << syncdataonly << ") = " << r << dendl;
+ if (r == 0) {
+ // The IOs in this fsync were okay, but maybe something happened
+ // in the background that we shoudl be reporting?
+ r = f->take_async_err();
+ ldout(cct, 3) << "fsync(" << fd << ", " << syncdataonly
+ << ") = 0, async_err = " << r << dendl;
+ } else {
+ // Assume that an error we encountered during fsync, even reported
+ // synchronously, would also have applied the error to the Fh, and we
+ // should clear it here to avoid returning the same error again on next
+ // call.
+ ldout(cct, 3) << "fsync(" << fd << ", " << syncdataonly << ") = "
+ << r << dendl;
+ f->take_async_err();
+ }
return r;
}
<< cpp_strerror(-r) << dendl;
}
- if (in->async_err) {
- ldout(cct, 1) << "ino " << in->ino << " marked with error from background flush! "
- << cpp_strerror(in->async_err) << dendl;
- r = in->async_err;
- }
-
return r;
}
tout(cct) << "ll_fsync" << std::endl;
tout(cct) << (unsigned long)fh << std::endl;
- return _fsync(fh, syncdataonly);
+ int r = _fsync(fh, syncdataonly);
+ if (r) {
+ // If we're returning an error, clear it from the FH
+ fh->take_async_err();
+ }
+ return r;
}
#ifdef FALLOC_FL_PUNCH_HOLE
--- /dev/null
+
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 Red Hat Inc
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+
+#include "Inode.h"
+
+#include "Fh.h"
+
+Fh::Fh(Inode *in) : inode(in), _ref(1), pos(0), mds(0), mode(0), flags(0),
+ pos_locked(false), actor_perms(), readahead(),
+ fcntl_locks(NULL), flock_locks(NULL)
+{
+ inode->add_fh(this);
+}
+
+Fh::~Fh()
+{
+ inode->rm_fh(this);
+}
+
#include "common/Readahead.h"
#include "include/types.h"
#include "InodeRef.h"
+#include "UserPerm.h"
class Cond;
class ceph_lock_state_t;
+class Inode;
// file handle for any open file state
// file lock
ceph_lock_state_t *fcntl_locks;
ceph_lock_state_t *flock_locks;
+
+ // IO error encountered by any writeback on this Inode while
+ // this Fh existed (i.e. an fsync on another Fh will still show
+ // up as an async_err here because it could have been the same
+ // bytes we wrote via this Fh).
+ int async_err = {0};
+
+ int take_async_err()
+ {
+ int e = async_err;
+ async_err = 0;
+ return e;
+ }
- Fh() : _ref(1), pos(0), mds(0), mode(0), flags(0), pos_locked(false),
- actor_perms(), readahead(), fcntl_locks(NULL), flock_locks(NULL) {}
+ Fh(Inode *in);
+ ~Fh();
void get() { ++_ref; }
int put() { return --_ref; }
};
#include "Inode.h"
#include "Dentry.h"
#include "Dir.h"
+#include "Fh.h"
#include "MetaSession.h"
#include "ClientSnapRealm.h"
f->dump_int("dirty_data", (int)dirty_data);
f->dump_unsigned("flush_tid", flush_tid);
}
+
+void Inode::set_async_err(int r)
+{
+ for (const auto &fh : fhs) {
+ fh->async_err = r;
+ }
+}
+
class ceph_lock_state_t;
class MetaRequest;
class filepath;
+class Fh;
struct Cap {
MetaSession *session;
xlist<MetaRequest*> unsafe_ops;
+ std::set<Fh*> fhs;
+
Inode(Client *c, vinodeno_t vino, file_layout_t *newlayout)
: client(c), ino(vino.ino), snapid(vino.snapid), faked_ino(0),
rdev(0), mode(0), uid(0), gid(0), nlink(0),
oset((void *)this, newlayout->pool_id, this->ino),
reported_size(0), wanted_max_size(0), requested_max_size(0),
_ref(0), ll_ref(0), dn_set(),
- fcntl_locks(NULL), flock_locks(NULL),
- async_err(0)
+ fcntl_locks(NULL), flock_locks(NULL)
{
memset(&dir_layout, 0, sizeof(dir_layout));
memset("a, 0, sizeof(quota));
bool have_valid_size();
Dir *open_dir();
- // Record errors to be exposed in fclose/fflush
- int async_err;
-
+ void add_fh(Fh *f) {fhs.insert(f);}
+ void rm_fh(Fh *f) {fhs.erase(f);}
+ void set_async_err(int r);
void dump(Formatter *f) const;
};