]>
Commit | Line | Data |
---|---|---|
b32b8144 FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | #include "common/Clock.h" | |
4 | #include "common/Timer.h" | |
5 | ||
6 | #include "Client.h" | |
7 | #include "Inode.h" | |
8 | #include "Fh.h" | |
9 | #include "Delegation.h" | |
10 | ||
11 | class C_Deleg_Timeout : public Context { | |
12 | Delegation *deleg; | |
13 | public: | |
14 | explicit C_Deleg_Timeout(Delegation *d) : deleg(d) {} | |
15 | void finish(int r) override { | |
16 | Inode *in = deleg->get_fh()->inode.get(); | |
17 | Client *client = in->client; | |
18 | ||
19 | // Called back via Timer, which takes client_lock for us | |
20 | assert(client->client_lock.is_locked_by_me()); | |
21 | ||
22 | lsubdout(client->cct, client, 0) << __func__ << | |
23 | ": delegation return timeout for inode 0x" << | |
24 | std::hex << in->ino << ". Forcibly unmounting client. "<< | |
25 | client << std::dec << dendl; | |
26 | client->_unmount(); | |
27 | } | |
28 | }; | |
29 | ||
30 | /** | |
31 | * ceph_deleg_caps_for_type - what caps are necessary for a delegation? | |
32 | * @type: delegation request type | |
33 | * | |
34 | * Determine what caps are necessary in order to grant a delegation of a given | |
35 | * type. For read delegations, we need whatever we require in order to do | |
36 | * cached reads, plus AsLs to cover metadata changes that should trigger a | |
37 | * recall. We also grab Xs since changing xattrs usually alters the mtime and | |
38 | * so would trigger a recall. | |
39 | * | |
40 | * For write delegations, we need whatever read delegations need plus the | |
41 | * caps to allow writing to the file (Fbwx). | |
42 | */ | |
43 | int ceph_deleg_caps_for_type(unsigned type) | |
44 | { | |
45 | int caps = CEPH_CAP_PIN; | |
46 | ||
47 | switch (type) { | |
48 | case CEPH_DELEGATION_WR: | |
49 | caps |= CEPH_CAP_FILE_EXCL | | |
50 | CEPH_CAP_FILE_WR | CEPH_CAP_FILE_BUFFER; | |
51 | /* Fallthrough */ | |
52 | case CEPH_DELEGATION_RD: | |
53 | caps |= CEPH_CAP_FILE_SHARED | | |
54 | CEPH_CAP_FILE_RD | CEPH_CAP_FILE_CACHE | | |
55 | CEPH_CAP_XATTR_SHARED | | |
56 | CEPH_CAP_LINK_SHARED | CEPH_CAP_AUTH_SHARED; | |
57 | break; | |
58 | default: | |
59 | // Should never happen | |
60 | assert(false); | |
61 | } | |
62 | return caps; | |
63 | } | |
64 | ||
65 | /* | |
66 | * A delegation is a container for holding caps on behalf of a client that | |
67 | * wants to be able to rely on them until recalled. | |
68 | */ | |
69 | Delegation::Delegation(Fh *_fh, unsigned _type, ceph_deleg_cb_t _cb, void *_priv) | |
70 | : fh(_fh), priv(_priv), type(_type), recall_cb(_cb), | |
71 | recall_time(utime_t()), timeout_event(nullptr) | |
72 | { | |
73 | Inode *inode = _fh->inode.get(); | |
74 | inode->client->get_cap_ref(inode, ceph_deleg_caps_for_type(_type)); | |
75 | }; | |
76 | ||
77 | Delegation::~Delegation() | |
78 | { | |
79 | disarm_timeout(); | |
80 | Inode *inode = fh->inode.get(); | |
81 | inode->client->put_cap_ref(inode, ceph_deleg_caps_for_type(type)); | |
82 | } | |
83 | ||
84 | void Delegation::reinit(unsigned _type, ceph_deleg_cb_t _recall_cb, void *_priv) | |
85 | { | |
86 | /* update cap refs -- note that we do a get first to avoid any going to 0 */ | |
87 | if (type != _type) { | |
88 | Inode *inode = fh->inode.get(); | |
89 | ||
90 | inode->client->get_cap_ref(inode, ceph_deleg_caps_for_type(_type)); | |
91 | inode->client->put_cap_ref(inode, ceph_deleg_caps_for_type(type)); | |
92 | type = _type; | |
93 | } | |
94 | ||
95 | recall_cb = _recall_cb; | |
96 | priv = _priv; | |
97 | } | |
98 | ||
99 | void Delegation::arm_timeout() | |
100 | { | |
101 | Client *client = fh->inode.get()->client; | |
102 | ||
103 | if (timeout_event) | |
104 | return; | |
105 | ||
106 | timeout_event = new C_Deleg_Timeout(this); | |
107 | client->timer.add_event_after(client->get_deleg_timeout(), timeout_event); | |
108 | } | |
109 | ||
110 | void Delegation::disarm_timeout() | |
111 | { | |
112 | Client *client = fh->inode.get()->client; | |
113 | ||
114 | if (!timeout_event) | |
115 | return; | |
116 | ||
117 | client->timer.cancel_event(timeout_event); | |
118 | timeout_event = nullptr; | |
119 | } | |
120 | ||
121 | void Delegation::recall(bool skip_read) | |
122 | { | |
123 | /* If skip_read is true, don't break read delegations */ | |
124 | if (skip_read && type == CEPH_DELEGATION_RD) | |
125 | return; | |
126 | ||
127 | if (!is_recalled()) { | |
128 | recall_cb(fh, priv); | |
129 | recall_time = ceph_clock_now(); | |
130 | arm_timeout(); | |
131 | } | |
132 | } |