summaryrefslogtreecommitdiffstats
path: root/src/client/Delegation.cc
blob: 0c0ac1b359f19302cf332d877e9d98c69bc7f14c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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();
  }
}