summaryrefslogtreecommitdiffstats
path: root/src/client/Delegation.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
commite6918187568dbd01842d8d1d2c808ce16a894239 (patch)
tree64f88b554b444a49f656b6c656111a145cbbaa28 /src/client/Delegation.cc
parentInitial commit. (diff)
downloadceph-upstream/18.2.2.tar.xz
ceph-upstream/18.2.2.zip
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/client/Delegation.cc')
-rw-r--r--src/client/Delegation.cc131
1 files changed, 131 insertions, 0 deletions
diff --git a/src/client/Delegation.cc b/src/client/Delegation.cc
new file mode 100644
index 000000000..0c0ac1b35
--- /dev/null
+++ b/src/client/Delegation.cc
@@ -0,0 +1,131 @@
+// -*- 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();
+ }
+}