// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "common/Clock.h" #include "common/Timer.h" #include "Client.h" #include "Inode.h" #include "Fh.h" #include "Delegation.h" class C_Deleg_Timeout : public Context { Delegation *deleg; public: explicit C_Deleg_Timeout(Delegation *d) : deleg(d) {} void finish(int r) override { Inode *in = deleg->get_fh()->inode.get(); Client *client = in->client; lsubdout(client->cct, client, 0) << __func__ << ": delegation return timeout for inode 0x" << std::hex << in->ino << ". Forcibly unmounting client. "<< client << std::dec << dendl; client->_unmount(false); } }; /** * ceph_deleg_caps_for_type - what caps are necessary for a delegation? * @type: delegation request type * * Determine what caps are necessary in order to grant a delegation of a given * type. For read delegations, we need whatever we require in order to do * cached reads, plus AsLs to cover metadata changes that should trigger a * recall. We also grab Xs since changing xattrs usually alters the mtime and * so would trigger a recall. * * For write delegations, we need whatever read delegations need plus the * caps to allow writing to the file (Fbwx). */ int ceph_deleg_caps_for_type(unsigned type) { int caps = CEPH_CAP_PIN; switch (type) { case CEPH_DELEGATION_WR: caps |= CEPH_CAP_FILE_EXCL | CEPH_CAP_FILE_WR | CEPH_CAP_FILE_BUFFER; /* Fallthrough */ case CEPH_DELEGATION_RD: caps |= CEPH_CAP_FILE_SHARED | CEPH_CAP_FILE_RD | CEPH_CAP_FILE_CACHE | CEPH_CAP_XATTR_SHARED | CEPH_CAP_LINK_SHARED | CEPH_CAP_AUTH_SHARED; break; default: // Should never happen ceph_abort(); } return caps; } /* * A delegation is a container for holding caps on behalf of a client that * wants to be able to rely on them until recalled. */ Delegation::Delegation(Fh *_fh, unsigned _type, ceph_deleg_cb_t _cb, void *_priv) : fh(_fh), priv(_priv), type(_type), recall_cb(_cb), recall_time(utime_t()), timeout_event(nullptr) { Inode *inode = _fh->inode.get(); inode->client->get_cap_ref(inode, ceph_deleg_caps_for_type(_type)); }; Delegation::~Delegation() { disarm_timeout(); Inode *inode = fh->inode.get(); inode->client->put_cap_ref(inode, ceph_deleg_caps_for_type(type)); } void Delegation::reinit(unsigned _type, ceph_deleg_cb_t _recall_cb, void *_priv) { /* update cap refs -- note that we do a get first to avoid any going to 0 */ if (type != _type) { Inode *inode = fh->inode.get(); inode->client->get_cap_ref(inode, ceph_deleg_caps_for_type(_type)); inode->client->put_cap_ref(inode, ceph_deleg_caps_for_type(type)); type = _type; } recall_cb = _recall_cb; priv = _priv; } void Delegation::arm_timeout() { Client *client = fh->inode.get()->client; std::scoped_lock l(client->timer_lock); if (timeout_event) return; timeout_event = new C_Deleg_Timeout(this); client->timer.add_event_after(client->get_deleg_timeout(), timeout_event); } void Delegation::disarm_timeout() { Client *client = fh->inode.get()->client; std::scoped_lock l(client->timer_lock); if (!timeout_event) return; client->timer.cancel_event(timeout_event); timeout_event = nullptr; } void Delegation::recall(bool skip_read) { /* If skip_read is true, don't break read delegations */ if (skip_read && type == CEPH_DELEGATION_RD) return; if (!is_recalled()) { recall_cb(fh, priv); recall_time = ceph_clock_now(); arm_timeout(); } }