Commit | Line | Data |
---|---|---|
457c8996 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1da177e4 LT |
2 | /* |
3 | * linux/fs/nfs/delegation.c | |
4 | * | |
5 | * Copyright (C) 2004 Trond Myklebust | |
6 | * | |
7 | * NFS file delegation management | |
8 | * | |
9 | */ | |
1da177e4 | 10 | #include <linux/completion.h> |
58d9714a | 11 | #include <linux/kthread.h> |
1da177e4 LT |
12 | #include <linux/module.h> |
13 | #include <linux/sched.h> | |
5a0e3ad6 | 14 | #include <linux/slab.h> |
1da177e4 | 15 | #include <linux/spinlock.h> |
1eb5d98f | 16 | #include <linux/iversion.h> |
1da177e4 LT |
17 | |
18 | #include <linux/nfs4.h> | |
19 | #include <linux/nfs_fs.h> | |
20 | #include <linux/nfs_xdr.h> | |
21 | ||
4ce79717 | 22 | #include "nfs4_fs.h" |
c01d3645 | 23 | #include "nfs4session.h" |
1da177e4 | 24 | #include "delegation.h" |
24c8dbbb | 25 | #include "internal.h" |
ca8acf8d | 26 | #include "nfs4trace.h" |
1da177e4 | 27 | |
10717f45 TM |
28 | #define NFS_DEFAULT_DELEGATION_WATERMARK (5000U) |
29 | ||
d2269ea1 | 30 | static atomic_long_t nfs_active_delegations; |
10717f45 | 31 | static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK; |
d2269ea1 TM |
32 | |
33 | static void __nfs_free_delegation(struct nfs_delegation *delegation) | |
905f8d16 | 34 | { |
a52458b4 N |
35 | put_cred(delegation->cred); |
36 | delegation->cred = NULL; | |
26f04dde | 37 | kfree_rcu(delegation, rcu); |
8383e460 TM |
38 | } |
39 | ||
d2269ea1 TM |
40 | static void nfs_mark_delegation_revoked(struct nfs_delegation *delegation) |
41 | { | |
42 | if (!test_and_set_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { | |
43 | delegation->stateid.type = NFS4_INVALID_STATEID_TYPE; | |
44 | atomic_long_dec(&nfs_active_delegations); | |
efeda80d TM |
45 | if (!test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) |
46 | nfs_clear_verifier_delegated(delegation->inode); | |
d2269ea1 TM |
47 | } |
48 | } | |
49 | ||
8c75593c TM |
50 | static struct nfs_delegation *nfs_get_delegation(struct nfs_delegation *delegation) |
51 | { | |
52 | refcount_inc(&delegation->refcount); | |
53 | return delegation; | |
54 | } | |
55 | ||
56 | static void nfs_put_delegation(struct nfs_delegation *delegation) | |
57 | { | |
58 | if (refcount_dec_and_test(&delegation->refcount)) | |
59 | __nfs_free_delegation(delegation); | |
60 | } | |
61 | ||
d2269ea1 TM |
62 | static void nfs_free_delegation(struct nfs_delegation *delegation) |
63 | { | |
64 | nfs_mark_delegation_revoked(delegation); | |
8c75593c | 65 | nfs_put_delegation(delegation); |
d2269ea1 TM |
66 | } |
67 | ||
d3978bb3 CL |
68 | /** |
69 | * nfs_mark_delegation_referenced - set delegation's REFERENCED flag | |
70 | * @delegation: delegation to process | |
71 | * | |
72 | */ | |
b7391f44 TM |
73 | void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) |
74 | { | |
75 | set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags); | |
76 | } | |
77 | ||
be200377 TM |
78 | static void nfs_mark_return_delegation(struct nfs_server *server, |
79 | struct nfs_delegation *delegation) | |
80 | { | |
81 | set_bit(NFS_DELEGATION_RETURN, &delegation->flags); | |
35a566a2 | 82 | set_bit(NFS4SERV_DELEGRETURN, &server->delegation_flags); |
be200377 TM |
83 | set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); |
84 | } | |
85 | ||
4201916f TM |
86 | static bool nfs4_is_valid_delegation(const struct nfs_delegation *delegation, |
87 | fmode_t type) | |
aa05c87f | 88 | { |
4201916f | 89 | if (delegation != NULL && (delegation->type & type) == type && |
aa05c87f TM |
90 | !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) && |
91 | !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) | |
92 | return true; | |
93 | return false; | |
94 | } | |
95 | ||
be3df3dd TM |
96 | struct nfs_delegation *nfs4_get_valid_delegation(const struct inode *inode) |
97 | { | |
98 | struct nfs_delegation *delegation; | |
99 | ||
100 | delegation = rcu_dereference(NFS_I(inode)->delegation); | |
101 | if (nfs4_is_valid_delegation(delegation, 0)) | |
102 | return delegation; | |
103 | return NULL; | |
104 | } | |
105 | ||
4201916f TM |
106 | static int nfs4_do_check_delegation(struct inode *inode, fmode_t type, |
107 | int flags, bool mark) | |
b7391f44 TM |
108 | { |
109 | struct nfs_delegation *delegation; | |
110 | int ret = 0; | |
111 | ||
4201916f | 112 | type &= FMODE_READ|FMODE_WRITE; |
b7391f44 TM |
113 | rcu_read_lock(); |
114 | delegation = rcu_dereference(NFS_I(inode)->delegation); | |
4201916f | 115 | if (nfs4_is_valid_delegation(delegation, type)) { |
15bb3afe PT |
116 | if (mark) |
117 | nfs_mark_delegation_referenced(delegation); | |
b7391f44 | 118 | ret = 1; |
e12912d9 TM |
119 | if ((flags & NFS_DELEGATION_FLAG_TIME) && |
120 | !test_bit(NFS_DELEGATION_DELEGTIME, &delegation->flags)) | |
121 | ret = 0; | |
b7391f44 TM |
122 | } |
123 | rcu_read_unlock(); | |
124 | return ret; | |
125 | } | |
15bb3afe | 126 | /** |
6453bcd0 | 127 | * nfs4_have_delegation - check if inode has a delegation, mark it |
15bb3afe PT |
128 | * NFS_DELEGATION_REFERENCED if there is one. |
129 | * @inode: inode to check | |
4201916f TM |
130 | * @type: delegation types to check for |
131 | * @flags: various modifiers | |
15bb3afe PT |
132 | * |
133 | * Returns one if inode has the indicated delegation, otherwise zero. | |
134 | */ | |
4201916f | 135 | int nfs4_have_delegation(struct inode *inode, fmode_t type, int flags) |
15bb3afe | 136 | { |
4201916f | 137 | return nfs4_do_check_delegation(inode, type, flags, true); |
15bb3afe PT |
138 | } |
139 | ||
140 | /* | |
141 | * nfs4_check_delegation - check if inode has a delegation, do not mark | |
142 | * NFS_DELEGATION_REFERENCED if it has one. | |
143 | */ | |
4201916f | 144 | int nfs4_check_delegation(struct inode *inode, fmode_t type) |
15bb3afe | 145 | { |
4201916f | 146 | return nfs4_do_check_delegation(inode, type, 0, false); |
15bb3afe | 147 | } |
b7391f44 | 148 | |
44f411c3 | 149 | static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_stateid *stateid) |
888e694c TM |
150 | { |
151 | struct inode *inode = state->inode; | |
152 | struct file_lock *fl; | |
17b985de | 153 | struct file_lock_context *flctx = locks_inode_context(inode); |
bd61e0a9 | 154 | struct list_head *list; |
d5122201 | 155 | int status = 0; |
888e694c | 156 | |
bd61e0a9 | 157 | if (flctx == NULL) |
65b62a29 | 158 | goto out; |
314d7cc0 | 159 | |
bd61e0a9 | 160 | list = &flctx->flc_posix; |
6109c850 | 161 | spin_lock(&flctx->flc_lock); |
bd61e0a9 | 162 | restart: |
d7c9616b | 163 | for_each_file_lock(fl, list) { |
dd1fac6a | 164 | if (nfs_file_open_context(fl->c.flc_file)->state != state) |
888e694c | 165 | continue; |
6109c850 | 166 | spin_unlock(&flctx->flc_lock); |
db4f2e63 | 167 | status = nfs4_lock_delegation_recall(fl, state, stateid); |
d5122201 | 168 | if (status < 0) |
3f09df70 | 169 | goto out; |
6109c850 | 170 | spin_lock(&flctx->flc_lock); |
888e694c | 171 | } |
bd61e0a9 JL |
172 | if (list == &flctx->flc_posix) { |
173 | list = &flctx->flc_flock; | |
174 | goto restart; | |
888e694c | 175 | } |
6109c850 | 176 | spin_unlock(&flctx->flc_lock); |
3f09df70 | 177 | out: |
888e694c TM |
178 | return status; |
179 | } | |
180 | ||
24311f88 TM |
181 | static int nfs_delegation_claim_opens(struct inode *inode, |
182 | const nfs4_stateid *stateid, fmode_t type) | |
1da177e4 LT |
183 | { |
184 | struct nfs_inode *nfsi = NFS_I(inode); | |
185 | struct nfs_open_context *ctx; | |
d25be546 | 186 | struct nfs4_state_owner *sp; |
1da177e4 | 187 | struct nfs4_state *state; |
888e694c | 188 | int err; |
1da177e4 LT |
189 | |
190 | again: | |
0de43976 TM |
191 | rcu_read_lock(); |
192 | list_for_each_entry_rcu(ctx, &nfsi->open_files, list) { | |
1da177e4 LT |
193 | state = ctx->state; |
194 | if (state == NULL) | |
195 | continue; | |
196 | if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) | |
197 | continue; | |
f8ebf7a8 TM |
198 | if (!nfs4_valid_open_stateid(state)) |
199 | continue; | |
f597c537 | 200 | if (!nfs4_stateid_match(&state->stateid, stateid)) |
90163027 | 201 | continue; |
0de43976 TM |
202 | if (!get_nfs_open_context(ctx)) |
203 | continue; | |
204 | rcu_read_unlock(); | |
d25be546 | 205 | sp = state->owner; |
65b62a29 TM |
206 | /* Block nfs4_proc_unlck */ |
207 | mutex_lock(&sp->so_delegreturn_mutex); | |
5eb8d18c | 208 | err = nfs4_open_delegation_recall(ctx, state, stateid); |
d25be546 | 209 | if (!err) |
44f411c3 | 210 | err = nfs_delegation_claim_locks(state, stateid); |
65b62a29 | 211 | mutex_unlock(&sp->so_delegreturn_mutex); |
1da177e4 | 212 | put_nfs_open_context(ctx); |
888e694c | 213 | if (err != 0) |
d18cc1fd | 214 | return err; |
1da177e4 LT |
215 | goto again; |
216 | } | |
0de43976 | 217 | rcu_read_unlock(); |
d18cc1fd | 218 | return 0; |
1da177e4 LT |
219 | } |
220 | ||
d3978bb3 CL |
221 | /** |
222 | * nfs_inode_reclaim_delegation - process a delegation reclaim request | |
223 | * @inode: inode to process | |
224 | * @cred: credential to use for request | |
35156bff TM |
225 | * @type: delegation type |
226 | * @stateid: delegation stateid | |
227 | * @pagemod_limit: write delegation "space_limit" | |
e12912d9 | 228 | * @deleg_type: raw delegation type |
d3978bb3 | 229 | * |
1da177e4 | 230 | */ |
a52458b4 | 231 | void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred, |
1ba04394 | 232 | fmode_t type, const nfs4_stateid *stateid, |
e12912d9 | 233 | unsigned long pagemod_limit, u32 deleg_type) |
1da177e4 | 234 | { |
8f649c37 | 235 | struct nfs_delegation *delegation; |
a52458b4 | 236 | const struct cred *oldcred = NULL; |
1da177e4 | 237 | |
8f649c37 TM |
238 | rcu_read_lock(); |
239 | delegation = rcu_dereference(NFS_I(inode)->delegation); | |
240 | if (delegation != NULL) { | |
241 | spin_lock(&delegation->lock); | |
1ba04394 TM |
242 | nfs4_stateid_copy(&delegation->stateid, stateid); |
243 | delegation->type = type; | |
244 | delegation->pagemod_limit = pagemod_limit; | |
245 | oldcred = delegation->cred; | |
246 | delegation->cred = get_cred(cred); | |
e12912d9 TM |
247 | switch (deleg_type) { |
248 | case NFS4_OPEN_DELEGATE_READ_ATTRS_DELEG: | |
249 | case NFS4_OPEN_DELEGATE_WRITE_ATTRS_DELEG: | |
250 | set_bit(NFS_DELEGATION_DELEGTIME, &delegation->flags); | |
251 | break; | |
252 | default: | |
253 | clear_bit(NFS_DELEGATION_DELEGTIME, &delegation->flags); | |
254 | } | |
1ba04394 TM |
255 | clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); |
256 | if (test_and_clear_bit(NFS_DELEGATION_REVOKED, | |
257 | &delegation->flags)) | |
258 | atomic_long_inc(&nfs_active_delegations); | |
b1a318de | 259 | spin_unlock(&delegation->lock); |
1ba04394 TM |
260 | rcu_read_unlock(); |
261 | put_cred(oldcred); | |
262 | trace_nfs4_reclaim_delegation(inode, type); | |
263 | } else { | |
264 | rcu_read_unlock(); | |
265 | nfs_inode_set_delegation(inode, cred, type, stateid, | |
e12912d9 | 266 | pagemod_limit, deleg_type); |
8f649c37 | 267 | } |
1da177e4 LT |
268 | } |
269 | ||
b81aca09 TM |
270 | static int nfs_do_return_delegation(struct inode *inode, |
271 | struct nfs_delegation *delegation, | |
272 | int issync) | |
57bfa891 | 273 | { |
5d63944f | 274 | const struct cred *cred; |
57bfa891 TM |
275 | int res = 0; |
276 | ||
5d63944f TM |
277 | if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { |
278 | spin_lock(&delegation->lock); | |
279 | cred = get_cred(delegation->cred); | |
280 | spin_unlock(&delegation->lock); | |
b81aca09 TM |
281 | res = nfs4_proc_delegreturn(inode, cred, &delegation->stateid, |
282 | delegation, issync); | |
5d63944f TM |
283 | put_cred(cred); |
284 | } | |
57bfa891 TM |
285 | return res; |
286 | } | |
287 | ||
86e89489 TM |
288 | static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation) |
289 | { | |
290 | struct inode *inode = NULL; | |
291 | ||
292 | spin_lock(&delegation->lock); | |
293 | if (delegation->inode != NULL) | |
294 | inode = igrab(delegation->inode); | |
6f9449be TM |
295 | if (!inode) |
296 | set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags); | |
86e89489 TM |
297 | spin_unlock(&delegation->lock); |
298 | return inode; | |
299 | } | |
300 | ||
d25be546 TM |
301 | static struct nfs_delegation * |
302 | nfs_start_delegation_return_locked(struct nfs_inode *nfsi) | |
303 | { | |
304 | struct nfs_delegation *ret = NULL; | |
305 | struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); | |
306 | ||
307 | if (delegation == NULL) | |
308 | goto out; | |
309 | spin_lock(&delegation->lock); | |
9e8f324b TM |
310 | if (delegation->inode && |
311 | !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { | |
be200377 | 312 | clear_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); |
8c75593c TM |
313 | /* Refcount matched in nfs_end_delegation_return() */ |
314 | ret = nfs_get_delegation(delegation); | |
315 | } | |
d25be546 | 316 | spin_unlock(&delegation->lock); |
efeda80d TM |
317 | if (ret) |
318 | nfs_clear_verifier_delegated(&nfsi->vfs_inode); | |
d25be546 TM |
319 | out: |
320 | return ret; | |
321 | } | |
322 | ||
323 | static struct nfs_delegation * | |
324 | nfs_start_delegation_return(struct nfs_inode *nfsi) | |
325 | { | |
326 | struct nfs_delegation *delegation; | |
327 | ||
328 | rcu_read_lock(); | |
329 | delegation = nfs_start_delegation_return_locked(nfsi); | |
330 | rcu_read_unlock(); | |
331 | return delegation; | |
332 | } | |
333 | ||
be200377 | 334 | static void nfs_abort_delegation_return(struct nfs_delegation *delegation, |
e767b59e | 335 | struct nfs_server *server, int err) |
d25be546 | 336 | { |
d25be546 TM |
337 | spin_lock(&delegation->lock); |
338 | clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); | |
be200377 TM |
339 | if (err == -EAGAIN) { |
340 | set_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); | |
e767b59e TM |
341 | set_bit(NFS4SERV_DELEGRETURN_DELAYED, |
342 | &server->delegation_flags); | |
343 | set_bit(NFS4CLNT_DELEGRETURN_DELAYED, | |
344 | &server->nfs_client->cl_state); | |
be200377 | 345 | } |
d25be546 | 346 | spin_unlock(&delegation->lock); |
d25be546 TM |
347 | } |
348 | ||
dda4b225 CL |
349 | static struct nfs_delegation * |
350 | nfs_detach_delegation_locked(struct nfs_inode *nfsi, | |
d25be546 TM |
351 | struct nfs_delegation *delegation, |
352 | struct nfs_client *clp) | |
57bfa891 | 353 | { |
d25be546 | 354 | struct nfs_delegation *deleg_cur = |
17d2c0a0 | 355 | rcu_dereference_protected(nfsi->delegation, |
d25be546 | 356 | lockdep_is_held(&clp->cl_lock)); |
57bfa891 | 357 | |
d25be546 TM |
358 | if (deleg_cur == NULL || delegation != deleg_cur) |
359 | return NULL; | |
dda4b225 | 360 | |
34310430 | 361 | spin_lock(&delegation->lock); |
f9e0cc9c TM |
362 | if (!delegation->inode) { |
363 | spin_unlock(&delegation->lock); | |
364 | return NULL; | |
365 | } | |
57bfa891 | 366 | list_del_rcu(&delegation->super_list); |
86e89489 | 367 | delegation->inode = NULL; |
57bfa891 | 368 | rcu_assign_pointer(nfsi->delegation, NULL); |
34310430 | 369 | spin_unlock(&delegation->lock); |
57bfa891 | 370 | return delegation; |
57bfa891 TM |
371 | } |
372 | ||
dda4b225 | 373 | static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, |
d25be546 TM |
374 | struct nfs_delegation *delegation, |
375 | struct nfs_server *server) | |
dda4b225 | 376 | { |
d3978bb3 | 377 | struct nfs_client *clp = server->nfs_client; |
dda4b225 CL |
378 | |
379 | spin_lock(&clp->cl_lock); | |
d25be546 | 380 | delegation = nfs_detach_delegation_locked(nfsi, delegation, clp); |
dda4b225 CL |
381 | spin_unlock(&clp->cl_lock); |
382 | return delegation; | |
383 | } | |
384 | ||
d25be546 TM |
385 | static struct nfs_delegation * |
386 | nfs_inode_detach_delegation(struct inode *inode) | |
387 | { | |
388 | struct nfs_inode *nfsi = NFS_I(inode); | |
389 | struct nfs_server *server = NFS_SERVER(inode); | |
390 | struct nfs_delegation *delegation; | |
391 | ||
ee05f456 TM |
392 | rcu_read_lock(); |
393 | delegation = rcu_dereference(nfsi->delegation); | |
394 | if (delegation != NULL) | |
395 | delegation = nfs_detach_delegation(nfsi, delegation, server); | |
396 | rcu_read_unlock(); | |
397 | return delegation; | |
d25be546 TM |
398 | } |
399 | ||
57f188e0 TM |
400 | static void |
401 | nfs_update_delegation_cred(struct nfs_delegation *delegation, | |
402 | const struct cred *cred) | |
403 | { | |
404 | const struct cred *old; | |
405 | ||
406 | if (cred_fscmp(delegation->cred, cred) != 0) { | |
407 | old = xchg(&delegation->cred, get_cred(cred)); | |
408 | put_cred(old); | |
409 | } | |
410 | } | |
411 | ||
cf6726e2 TM |
412 | static void |
413 | nfs_update_inplace_delegation(struct nfs_delegation *delegation, | |
414 | const struct nfs_delegation *update) | |
415 | { | |
416 | if (nfs4_stateid_is_newer(&update->stateid, &delegation->stateid)) { | |
417 | delegation->stateid.seqid = update->stateid.seqid; | |
418 | smp_wmb(); | |
419 | delegation->type = update->type; | |
57f188e0 TM |
420 | delegation->pagemod_limit = update->pagemod_limit; |
421 | if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { | |
422 | delegation->change_attr = update->change_attr; | |
423 | nfs_update_delegation_cred(delegation, update->cred); | |
424 | /* smp_mb__before_atomic() is implicit due to xchg() */ | |
425 | clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags); | |
d2269ea1 | 426 | atomic_long_inc(&nfs_active_delegations); |
57f188e0 | 427 | } |
cf6726e2 TM |
428 | } |
429 | } | |
430 | ||
d3978bb3 CL |
431 | /** |
432 | * nfs_inode_set_delegation - set up a delegation on an inode | |
433 | * @inode: inode to which delegation applies | |
434 | * @cred: cred to use for subsequent delegation processing | |
35156bff TM |
435 | * @type: delegation type |
436 | * @stateid: delegation stateid | |
437 | * @pagemod_limit: write delegation "space_limit" | |
e12912d9 | 438 | * @deleg_type: raw delegation type |
d3978bb3 CL |
439 | * |
440 | * Returns zero on success, or a negative errno value. | |
1da177e4 | 441 | */ |
a52458b4 | 442 | int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, |
e12912d9 TM |
443 | fmode_t type, const nfs4_stateid *stateid, |
444 | unsigned long pagemod_limit, u32 deleg_type) | |
1da177e4 | 445 | { |
d3978bb3 CL |
446 | struct nfs_server *server = NFS_SERVER(inode); |
447 | struct nfs_client *clp = server->nfs_client; | |
1da177e4 | 448 | struct nfs_inode *nfsi = NFS_I(inode); |
17d2c0a0 | 449 | struct nfs_delegation *delegation, *old_delegation; |
57bfa891 | 450 | struct nfs_delegation *freeme = NULL; |
1da177e4 LT |
451 | int status = 0; |
452 | ||
9c00fd9a | 453 | delegation = kmalloc(sizeof(*delegation), GFP_KERNEL_ACCOUNT); |
1da177e4 LT |
454 | if (delegation == NULL) |
455 | return -ENOMEM; | |
35156bff | 456 | nfs4_stateid_copy(&delegation->stateid, stateid); |
8c75593c | 457 | refcount_set(&delegation->refcount, 1); |
35156bff TM |
458 | delegation->type = type; |
459 | delegation->pagemod_limit = pagemod_limit; | |
1eb5d98f | 460 | delegation->change_attr = inode_peek_iversion_raw(inode); |
a52458b4 | 461 | delegation->cred = get_cred(cred); |
1da177e4 | 462 | delegation->inode = inode; |
b7391f44 | 463 | delegation->flags = 1<<NFS_DELEGATION_REFERENCED; |
e12912d9 TM |
464 | switch (deleg_type) { |
465 | case NFS4_OPEN_DELEGATE_READ_ATTRS_DELEG: | |
466 | case NFS4_OPEN_DELEGATE_WRITE_ATTRS_DELEG: | |
467 | delegation->flags |= BIT(NFS_DELEGATION_DELEGTIME); | |
468 | } | |
a9b8d90f | 469 | delegation->test_gen = 0; |
34310430 | 470 | spin_lock_init(&delegation->lock); |
1da177e4 LT |
471 | |
472 | spin_lock(&clp->cl_lock); | |
17d2c0a0 | 473 | old_delegation = rcu_dereference_protected(nfsi->delegation, |
d3978bb3 | 474 | lockdep_is_held(&clp->cl_lock)); |
ee05f456 TM |
475 | if (old_delegation == NULL) |
476 | goto add_new; | |
477 | /* Is this an update of the existing delegation? */ | |
478 | if (nfs4_stateid_match_other(&old_delegation->stateid, | |
479 | &delegation->stateid)) { | |
480 | spin_lock(&old_delegation->lock); | |
481 | nfs_update_inplace_delegation(old_delegation, | |
482 | delegation); | |
483 | spin_unlock(&old_delegation->lock); | |
484 | goto out; | |
485 | } | |
486 | if (!test_bit(NFS_DELEGATION_REVOKED, &old_delegation->flags)) { | |
57bfa891 TM |
487 | /* |
488 | * Deal with broken servers that hand out two | |
489 | * delegations for the same file. | |
17280175 TM |
490 | * Allow for upgrades to a WRITE delegation, but |
491 | * nothing else. | |
57bfa891 TM |
492 | */ |
493 | dfprintk(FILE, "%s: server %s handed out " | |
494 | "a duplicate delegation!\n", | |
3110ff80 | 495 | __func__, clp->cl_hostname); |
17280175 TM |
496 | if (delegation->type == old_delegation->type || |
497 | !(delegation->type & FMODE_WRITE)) { | |
57bfa891 TM |
498 | freeme = delegation; |
499 | delegation = NULL; | |
500 | goto out; | |
501 | } | |
ade04647 TM |
502 | if (test_and_set_bit(NFS_DELEGATION_RETURNING, |
503 | &old_delegation->flags)) | |
504 | goto out; | |
1da177e4 | 505 | } |
ee05f456 TM |
506 | freeme = nfs_detach_delegation_locked(nfsi, old_delegation, clp); |
507 | if (freeme == NULL) | |
508 | goto out; | |
509 | add_new: | |
cc7f2dae TM |
510 | /* |
511 | * If we didn't revalidate the change attribute before setting | |
512 | * the delegation, then pre-emptively ask for a full attribute | |
513 | * cache revalidation. | |
514 | */ | |
515 | spin_lock(&inode->i_lock); | |
516 | if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_CHANGE) | |
517 | nfs_set_cache_invalid(inode, | |
518 | NFS_INO_INVALID_ATIME | NFS_INO_INVALID_CTIME | | |
519 | NFS_INO_INVALID_MTIME | NFS_INO_INVALID_SIZE | | |
520 | NFS_INO_INVALID_BLOCKS | NFS_INO_INVALID_NLINK | | |
521 | NFS_INO_INVALID_OTHER | NFS_INO_INVALID_DATA | | |
522 | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL | | |
523 | NFS_INO_INVALID_XATTR); | |
524 | spin_unlock(&inode->i_lock); | |
525 | ||
38942ba2 | 526 | list_add_tail_rcu(&delegation->super_list, &server->delegations); |
57bfa891 TM |
527 | rcu_assign_pointer(nfsi->delegation, delegation); |
528 | delegation = NULL; | |
412c77ce | 529 | |
d2269ea1 TM |
530 | atomic_long_inc(&nfs_active_delegations); |
531 | ||
35156bff | 532 | trace_nfs4_set_delegation(inode, type); |
e12912d9 TM |
533 | |
534 | /* If we hold writebacks and have delegated mtime then update */ | |
535 | if (deleg_type == NFS4_OPEN_DELEGATE_WRITE_ATTRS_DELEG && | |
536 | nfs_have_writebacks(inode)) | |
537 | nfs_update_delegated_mtime(inode); | |
57bfa891 | 538 | out: |
1da177e4 | 539 | spin_unlock(&clp->cl_lock); |
603c83da | 540 | if (delegation != NULL) |
d2269ea1 | 541 | __nfs_free_delegation(delegation); |
ee05f456 | 542 | if (freeme != NULL) { |
57bfa891 | 543 | nfs_do_return_delegation(inode, freeme, 0); |
ee05f456 TM |
544 | nfs_free_delegation(freeme); |
545 | } | |
1da177e4 LT |
546 | return status; |
547 | } | |
548 | ||
1da177e4 LT |
549 | /* |
550 | * Basic procedure for returning a delegation to the server | |
551 | */ | |
d25be546 | 552 | static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync) |
1da177e4 | 553 | { |
e767b59e | 554 | struct nfs_server *server = NFS_SERVER(inode); |
6b4befc0 | 555 | unsigned int mode = O_WRONLY | O_RDWR; |
869f9dfa | 556 | int err = 0; |
1da177e4 | 557 | |
d25be546 TM |
558 | if (delegation == NULL) |
559 | return 0; | |
6b4befc0 TM |
560 | |
561 | if (!issync) | |
562 | mode |= O_NONBLOCK; | |
563 | /* Recall of any remaining application leases */ | |
564 | err = break_lease(inode, mode); | |
565 | ||
566 | while (err == 0) { | |
869f9dfa TM |
567 | if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) |
568 | break; | |
24311f88 TM |
569 | err = nfs_delegation_claim_opens(inode, &delegation->stateid, |
570 | delegation->type); | |
d25be546 TM |
571 | if (!issync || err != -EAGAIN) |
572 | break; | |
573 | /* | |
574 | * Guard against state recovery | |
575 | */ | |
e767b59e | 576 | err = nfs4_wait_clnt_recover(server->nfs_client); |
6b4befc0 | 577 | } |
d25be546 TM |
578 | |
579 | if (err) { | |
e767b59e | 580 | nfs_abort_delegation_return(delegation, server, err); |
d25be546 TM |
581 | goto out; |
582 | } | |
1da177e4 | 583 | |
d18cc1fd TM |
584 | err = nfs_do_return_delegation(inode, delegation, issync); |
585 | out: | |
8c75593c TM |
586 | /* Refcount matched in nfs_start_delegation_return_locked() */ |
587 | nfs_put_delegation(delegation); | |
d18cc1fd | 588 | return err; |
90163027 TM |
589 | } |
590 | ||
b757144f TM |
591 | static bool nfs_delegation_need_return(struct nfs_delegation *delegation) |
592 | { | |
593 | bool ret = false; | |
594 | ||
595 | if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) | |
596 | ret = true; | |
af20b7b8 | 597 | if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) || |
be200377 | 598 | test_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags) || |
af20b7b8 TM |
599 | test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) |
600 | ret = false; | |
601 | ||
b757144f TM |
602 | return ret; |
603 | } | |
604 | ||
af3b61bf TM |
605 | static int nfs_server_return_marked_delegations(struct nfs_server *server, |
606 | void __always_unused *data) | |
515d8611 TM |
607 | { |
608 | struct nfs_delegation *delegation; | |
e04bbf6b | 609 | struct nfs_delegation *prev; |
515d8611 | 610 | struct inode *inode; |
e04bbf6b N |
611 | struct inode *place_holder = NULL; |
612 | struct nfs_delegation *place_holder_deleg = NULL; | |
d18cc1fd | 613 | int err = 0; |
515d8611 | 614 | |
35a566a2 TM |
615 | if (!test_and_clear_bit(NFS4SERV_DELEGRETURN, |
616 | &server->delegation_flags)) | |
617 | return 0; | |
515d8611 | 618 | restart: |
e04bbf6b N |
619 | /* |
620 | * To avoid quadratic looping we hold a reference | |
621 | * to an inode place_holder. Each time we restart, we | |
af3b61bf TM |
622 | * list delegation in the server from the delegations |
623 | * of that inode. | |
e04bbf6b N |
624 | * prev is an RCU-protected pointer to a delegation which |
625 | * wasn't marked for return and might be a good choice for | |
626 | * the next place_holder. | |
627 | */ | |
e04bbf6b | 628 | prev = NULL; |
af3b61bf TM |
629 | delegation = NULL; |
630 | rcu_read_lock(); | |
e04bbf6b | 631 | if (place_holder) |
af3b61bf TM |
632 | delegation = rcu_dereference(NFS_I(place_holder)->delegation); |
633 | if (!delegation || delegation != place_holder_deleg) | |
634 | delegation = list_entry_rcu(server->delegations.next, | |
635 | struct nfs_delegation, super_list); | |
636 | list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) { | |
637 | struct inode *to_put = NULL; | |
638 | ||
639 | if (test_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags)) | |
640 | continue; | |
641 | if (!nfs_delegation_need_return(delegation)) { | |
642 | if (nfs4_is_valid_delegation(delegation, 0)) | |
e04bbf6b | 643 | prev = delegation; |
af3b61bf TM |
644 | continue; |
645 | } | |
f92214e4 TM |
646 | inode = nfs_delegation_grab_inode(delegation); |
647 | if (inode == NULL) | |
648 | continue; | |
e04bbf6b | 649 | |
af3b61bf TM |
650 | if (prev) { |
651 | struct inode *tmp = nfs_delegation_grab_inode(prev); | |
652 | if (tmp) { | |
653 | to_put = place_holder; | |
654 | place_holder = tmp; | |
655 | place_holder_deleg = prev; | |
9f0f8e12 | 656 | } |
af3b61bf | 657 | } |
d3978bb3 | 658 | |
af3b61bf TM |
659 | delegation = nfs_start_delegation_return_locked(NFS_I(inode)); |
660 | rcu_read_unlock(); | |
661 | ||
662 | iput(to_put); | |
663 | ||
664 | err = nfs_end_delegation_return(inode, delegation, 0); | |
665 | iput(inode); | |
666 | cond_resched(); | |
667 | if (!err) | |
668 | goto restart; | |
35a566a2 | 669 | set_bit(NFS4SERV_DELEGRETURN, &server->delegation_flags); |
af3b61bf TM |
670 | set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); |
671 | goto out; | |
515d8611 TM |
672 | } |
673 | rcu_read_unlock(); | |
af3b61bf TM |
674 | out: |
675 | iput(place_holder); | |
676 | return err; | |
677 | } | |
678 | ||
be200377 TM |
679 | static bool nfs_server_clear_delayed_delegations(struct nfs_server *server) |
680 | { | |
681 | struct nfs_delegation *d; | |
682 | bool ret = false; | |
683 | ||
e767b59e TM |
684 | if (!test_and_clear_bit(NFS4SERV_DELEGRETURN_DELAYED, |
685 | &server->delegation_flags)) | |
686 | goto out; | |
be200377 TM |
687 | list_for_each_entry_rcu (d, &server->delegations, super_list) { |
688 | if (!test_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags)) | |
689 | continue; | |
690 | nfs_mark_return_delegation(server, d); | |
691 | clear_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags); | |
692 | ret = true; | |
693 | } | |
e767b59e | 694 | out: |
be200377 TM |
695 | return ret; |
696 | } | |
697 | ||
698 | static bool nfs_client_clear_delayed_delegations(struct nfs_client *clp) | |
699 | { | |
700 | struct nfs_server *server; | |
701 | bool ret = false; | |
702 | ||
703 | if (!test_and_clear_bit(NFS4CLNT_DELEGRETURN_DELAYED, &clp->cl_state)) | |
704 | goto out; | |
705 | rcu_read_lock(); | |
706 | list_for_each_entry_rcu (server, &clp->cl_superblocks, client_link) { | |
707 | if (nfs_server_clear_delayed_delegations(server)) | |
708 | ret = true; | |
709 | } | |
710 | rcu_read_unlock(); | |
711 | out: | |
712 | return ret; | |
713 | } | |
714 | ||
af3b61bf TM |
715 | /** |
716 | * nfs_client_return_marked_delegations - return previously marked delegations | |
717 | * @clp: nfs_client to process | |
718 | * | |
719 | * Note that this function is designed to be called by the state | |
720 | * manager thread. For this reason, it cannot flush the dirty data, | |
721 | * since that could deadlock in case of a state recovery error. | |
722 | * | |
723 | * Returns zero on success, or a negative errno value. | |
724 | */ | |
725 | int nfs_client_return_marked_delegations(struct nfs_client *clp) | |
726 | { | |
be200377 TM |
727 | int err = nfs_client_for_each_server( |
728 | clp, nfs_server_return_marked_delegations, NULL); | |
729 | if (err) | |
730 | return err; | |
731 | /* If a return was delayed, sleep to prevent hard looping */ | |
732 | if (nfs_client_clear_delayed_delegations(clp)) | |
733 | ssleep(1); | |
734 | return 0; | |
515d8611 TM |
735 | } |
736 | ||
d3978bb3 | 737 | /** |
b47e0e47 | 738 | * nfs_inode_evict_delegation - return delegation, don't reclaim opens |
d3978bb3 CL |
739 | * @inode: inode to process |
740 | * | |
741 | * Does not protect against delegation reclaims, therefore really only safe | |
b47e0e47 TM |
742 | * to be called from nfs4_clear_inode(). Guaranteed to always free |
743 | * the delegation structure. | |
e6f81075 | 744 | */ |
b47e0e47 | 745 | void nfs_inode_evict_delegation(struct inode *inode) |
e6f81075 | 746 | { |
e6f81075 TM |
747 | struct nfs_delegation *delegation; |
748 | ||
d25be546 | 749 | delegation = nfs_inode_detach_delegation(inode); |
b47e0e47 | 750 | if (delegation != NULL) { |
f885ea64 | 751 | set_bit(NFS_DELEGATION_RETURNING, &delegation->flags); |
b47e0e47 | 752 | set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags); |
5fcdfacc | 753 | nfs_do_return_delegation(inode, delegation, 1); |
ee05f456 | 754 | nfs_free_delegation(delegation); |
b47e0e47 | 755 | } |
e6f81075 TM |
756 | } |
757 | ||
d3978bb3 | 758 | /** |
6453bcd0 | 759 | * nfs4_inode_return_delegation - synchronously return a delegation |
d3978bb3 CL |
760 | * @inode: inode to process |
761 | * | |
c57d1bc5 TM |
762 | * This routine will always flush any dirty data to disk on the |
763 | * assumption that if we need to return the delegation, then | |
764 | * we should stop caching. | |
765 | * | |
d3978bb3 CL |
766 | * Returns zero on success, or a negative errno value. |
767 | */ | |
57ec14c5 | 768 | int nfs4_inode_return_delegation(struct inode *inode) |
90163027 | 769 | { |
90163027 TM |
770 | struct nfs_inode *nfsi = NFS_I(inode); |
771 | struct nfs_delegation *delegation; | |
90163027 | 772 | |
d25be546 | 773 | delegation = nfs_start_delegation_return(nfsi); |
6e176d47 TM |
774 | if (delegation != NULL) { |
775 | /* Synchronous recall of any application leases */ | |
776 | break_lease(inode, O_WRONLY | O_RDWR); | |
777 | if (S_ISREG(inode->i_mode)) | |
778 | nfs_wb_all(inode); | |
6b4befc0 | 779 | return nfs_end_delegation_return(inode, delegation, 1); |
6e176d47 | 780 | } |
6b4befc0 | 781 | return 0; |
1da177e4 LT |
782 | } |
783 | ||
8f8df955 TM |
784 | /** |
785 | * nfs4_inode_set_return_delegation_on_close - asynchronously return a delegation | |
786 | * @inode: inode to process | |
787 | * | |
788 | * This routine is called to request that the delegation be returned as soon | |
789 | * as the file is closed. If the file is already closed, the delegation is | |
790 | * immediately returned. | |
791 | */ | |
792 | void nfs4_inode_set_return_delegation_on_close(struct inode *inode) | |
793 | { | |
794 | struct nfs_delegation *delegation; | |
795 | struct nfs_delegation *ret = NULL; | |
796 | ||
797 | if (!inode) | |
798 | return; | |
799 | rcu_read_lock(); | |
800 | delegation = nfs4_get_valid_delegation(inode); | |
801 | if (!delegation) | |
802 | goto out; | |
803 | spin_lock(&delegation->lock); | |
804 | if (!delegation->inode) | |
805 | goto out_unlock; | |
806 | if (list_empty(&NFS_I(inode)->open_files) && | |
807 | !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { | |
808 | /* Refcount matched in nfs_end_delegation_return() */ | |
809 | ret = nfs_get_delegation(delegation); | |
810 | } else | |
811 | set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); | |
812 | out_unlock: | |
813 | spin_unlock(&delegation->lock); | |
814 | if (ret) | |
815 | nfs_clear_verifier_delegated(inode); | |
816 | out: | |
817 | rcu_read_unlock(); | |
818 | nfs_end_delegation_return(inode, ret, 0); | |
819 | } | |
820 | ||
b7b7dac6 | 821 | /** |
6453bcd0 | 822 | * nfs4_inode_return_delegation_on_close - asynchronously return a delegation |
b7b7dac6 TM |
823 | * @inode: inode to process |
824 | * | |
825 | * This routine is called on file close in order to determine if the | |
826 | * inode delegation needs to be returned immediately. | |
827 | */ | |
828 | void nfs4_inode_return_delegation_on_close(struct inode *inode) | |
829 | { | |
830 | struct nfs_delegation *delegation; | |
831 | struct nfs_delegation *ret = NULL; | |
832 | ||
833 | if (!inode) | |
834 | return; | |
835 | rcu_read_lock(); | |
836 | delegation = nfs4_get_valid_delegation(inode); | |
837 | if (!delegation) | |
838 | goto out; | |
10717f45 TM |
839 | if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) || |
840 | atomic_long_read(&nfs_active_delegations) >= nfs_delegation_watermark) { | |
b7b7dac6 TM |
841 | spin_lock(&delegation->lock); |
842 | if (delegation->inode && | |
843 | list_empty(&NFS_I(inode)->open_files) && | |
844 | !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { | |
845 | clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); | |
8c75593c TM |
846 | /* Refcount matched in nfs_end_delegation_return() */ |
847 | ret = nfs_get_delegation(delegation); | |
b7b7dac6 TM |
848 | } |
849 | spin_unlock(&delegation->lock); | |
efeda80d TM |
850 | if (ret) |
851 | nfs_clear_verifier_delegated(inode); | |
b7b7dac6 TM |
852 | } |
853 | out: | |
854 | rcu_read_unlock(); | |
855 | nfs_end_delegation_return(inode, ret, 0); | |
856 | } | |
857 | ||
c01d3645 TM |
858 | /** |
859 | * nfs4_inode_make_writeable | |
860 | * @inode: pointer to inode | |
861 | * | |
862 | * Make the inode writeable by returning the delegation if necessary | |
863 | * | |
864 | * Returns zero on success, or a negative errno value. | |
865 | */ | |
866 | int nfs4_inode_make_writeable(struct inode *inode) | |
867 | { | |
3887ce1a TM |
868 | struct nfs_delegation *delegation; |
869 | ||
870 | rcu_read_lock(); | |
871 | delegation = nfs4_get_valid_delegation(inode); | |
872 | if (delegation == NULL || | |
873 | (nfs4_has_session(NFS_SERVER(inode)->nfs_client) && | |
874 | (delegation->type & FMODE_WRITE))) { | |
875 | rcu_read_unlock(); | |
876 | return 0; | |
877 | } | |
878 | rcu_read_unlock(); | |
879 | return nfs4_inode_return_delegation(inode); | |
c01d3645 TM |
880 | } |
881 | ||
47acca88 TM |
882 | static void |
883 | nfs_mark_return_if_closed_delegation(struct nfs_server *server, | |
884 | struct nfs_delegation *delegation) | |
b757144f | 885 | { |
47acca88 TM |
886 | struct inode *inode; |
887 | ||
888 | if (test_bit(NFS_DELEGATION_RETURN, &delegation->flags) || | |
889 | test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) | |
890 | return; | |
891 | spin_lock(&delegation->lock); | |
892 | inode = delegation->inode; | |
893 | if (!inode) | |
894 | goto out; | |
895 | if (list_empty(&NFS_I(inode)->open_files)) | |
896 | nfs_mark_return_delegation(server, delegation); | |
897 | else | |
898 | set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); | |
899 | out: | |
900 | spin_unlock(&delegation->lock); | |
6411bd4a TM |
901 | } |
902 | ||
5c31e236 TM |
903 | static bool nfs_server_mark_return_all_delegations(struct nfs_server *server) |
904 | { | |
905 | struct nfs_delegation *delegation; | |
906 | bool ret = false; | |
907 | ||
908 | list_for_each_entry_rcu(delegation, &server->delegations, super_list) { | |
909 | nfs_mark_return_delegation(server, delegation); | |
910 | ret = true; | |
911 | } | |
912 | return ret; | |
913 | } | |
914 | ||
b02ba0b6 TM |
915 | static void nfs_client_mark_return_all_delegations(struct nfs_client *clp) |
916 | { | |
917 | struct nfs_server *server; | |
918 | ||
919 | rcu_read_lock(); | |
920 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) | |
921 | nfs_server_mark_return_all_delegations(server); | |
922 | rcu_read_unlock(); | |
923 | } | |
924 | ||
925 | static void nfs_delegation_run_state_manager(struct nfs_client *clp) | |
926 | { | |
927 | if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) | |
928 | nfs4_schedule_state_manager(clp); | |
929 | } | |
930 | ||
931 | /** | |
932 | * nfs_expire_all_delegations | |
933 | * @clp: client to process | |
934 | * | |
935 | */ | |
936 | void nfs_expire_all_delegations(struct nfs_client *clp) | |
937 | { | |
938 | nfs_client_mark_return_all_delegations(clp); | |
939 | nfs_delegation_run_state_manager(clp); | |
940 | } | |
941 | ||
d3978bb3 | 942 | /** |
6453bcd0 | 943 | * nfs_server_return_all_delegations - return delegations for one superblock |
302fad7b | 944 | * @server: pointer to nfs_server to process |
d3978bb3 | 945 | * |
1da177e4 | 946 | */ |
eeebf916 | 947 | void nfs_server_return_all_delegations(struct nfs_server *server) |
1da177e4 | 948 | { |
d3978bb3 | 949 | struct nfs_client *clp = server->nfs_client; |
5c31e236 | 950 | bool need_wait; |
1da177e4 LT |
951 | |
952 | if (clp == NULL) | |
953 | return; | |
d3978bb3 | 954 | |
8383e460 | 955 | rcu_read_lock(); |
5c31e236 | 956 | need_wait = nfs_server_mark_return_all_delegations(server); |
8383e460 | 957 | rcu_read_unlock(); |
d3978bb3 | 958 | |
5c31e236 | 959 | if (need_wait) { |
d18cc1fd | 960 | nfs4_schedule_state_manager(clp); |
5c31e236 TM |
961 | nfs4_wait_clnt_recover(clp); |
962 | } | |
515d8611 TM |
963 | } |
964 | ||
826e0013 | 965 | static void nfs_mark_return_unused_delegation_types(struct nfs_server *server, |
d3978bb3 | 966 | fmode_t flags) |
515d8611 TM |
967 | { |
968 | struct nfs_delegation *delegation; | |
969 | ||
d3978bb3 | 970 | list_for_each_entry_rcu(delegation, &server->delegations, super_list) { |
c79571a5 AB |
971 | if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) |
972 | continue; | |
973 | if (delegation->type & flags) | |
826e0013 | 974 | nfs_mark_return_if_closed_delegation(server, delegation); |
707fb4b3 | 975 | } |
d3978bb3 CL |
976 | } |
977 | ||
826e0013 | 978 | static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp, |
d3978bb3 CL |
979 | fmode_t flags) |
980 | { | |
981 | struct nfs_server *server; | |
982 | ||
983 | rcu_read_lock(); | |
984 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) | |
826e0013 | 985 | nfs_mark_return_unused_delegation_types(server, flags); |
515d8611 | 986 | rcu_read_unlock(); |
1da177e4 LT |
987 | } |
988 | ||
ee05f456 | 989 | static void nfs_revoke_delegation(struct inode *inode, |
41020b67 | 990 | const nfs4_stateid *stateid) |
869f9dfa TM |
991 | { |
992 | struct nfs_delegation *delegation; | |
7f048831 | 993 | nfs4_stateid tmp; |
41020b67 TM |
994 | bool ret = false; |
995 | ||
869f9dfa TM |
996 | rcu_read_lock(); |
997 | delegation = rcu_dereference(NFS_I(inode)->delegation); | |
41020b67 TM |
998 | if (delegation == NULL) |
999 | goto out; | |
7f048831 TM |
1000 | if (stateid == NULL) { |
1001 | nfs4_stateid_copy(&tmp, &delegation->stateid); | |
1002 | stateid = &tmp; | |
f2d47b55 TM |
1003 | } else { |
1004 | if (!nfs4_stateid_match_other(stateid, &delegation->stateid)) | |
1005 | goto out; | |
1006 | spin_lock(&delegation->lock); | |
1007 | if (stateid->seqid) { | |
1008 | if (nfs4_stateid_is_newer(&delegation->stateid, stateid)) { | |
1009 | spin_unlock(&delegation->lock); | |
1010 | goto out; | |
1011 | } | |
1012 | delegation->stateid.seqid = stateid->seqid; | |
1013 | } | |
1014 | spin_unlock(&delegation->lock); | |
1015 | } | |
d2269ea1 | 1016 | nfs_mark_delegation_revoked(delegation); |
41020b67 TM |
1017 | ret = true; |
1018 | out: | |
869f9dfa | 1019 | rcu_read_unlock(); |
7f048831 TM |
1020 | if (ret) |
1021 | nfs_inode_find_state_and_recover(inode, stateid); | |
869f9dfa TM |
1022 | } |
1023 | ||
d51f91d2 TM |
1024 | void nfs_delegation_mark_returned(struct inode *inode, |
1025 | const nfs4_stateid *stateid) | |
1026 | { | |
1027 | struct nfs_delegation *delegation; | |
1028 | ||
1029 | if (!inode) | |
1030 | return; | |
1031 | ||
1032 | rcu_read_lock(); | |
1033 | delegation = rcu_dereference(NFS_I(inode)->delegation); | |
1034 | if (!delegation) | |
1035 | goto out_rcu_unlock; | |
1036 | ||
1037 | spin_lock(&delegation->lock); | |
1038 | if (!nfs4_stateid_match_other(stateid, &delegation->stateid)) | |
1039 | goto out_spin_unlock; | |
1040 | if (stateid->seqid) { | |
1041 | /* If delegation->stateid is newer, dont mark as returned */ | |
1042 | if (nfs4_stateid_is_newer(&delegation->stateid, stateid)) | |
1043 | goto out_clear_returning; | |
1044 | if (delegation->stateid.seqid != stateid->seqid) | |
1045 | delegation->stateid.seqid = stateid->seqid; | |
1046 | } | |
1047 | ||
d2269ea1 | 1048 | nfs_mark_delegation_revoked(delegation); |
7ef60108 DN |
1049 | clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); |
1050 | spin_unlock(&delegation->lock); | |
1051 | if (nfs_detach_delegation(NFS_I(inode), delegation, NFS_SERVER(inode))) | |
1052 | nfs_put_delegation(delegation); | |
1053 | goto out_rcu_unlock; | |
d51f91d2 TM |
1054 | |
1055 | out_clear_returning: | |
1056 | clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); | |
1057 | out_spin_unlock: | |
1058 | spin_unlock(&delegation->lock); | |
1059 | out_rcu_unlock: | |
1060 | rcu_read_unlock(); | |
1061 | ||
1062 | nfs_inode_find_state_and_recover(inode, stateid); | |
1063 | } | |
1064 | ||
77be29b7 BC |
1065 | /** |
1066 | * nfs_remove_bad_delegation - handle delegations that are unusable | |
1067 | * @inode: inode to process | |
1068 | * @stateid: the delegation's stateid | |
1069 | * | |
1070 | * If the server ACK-ed our FREE_STATEID then clean | |
1071 | * up the delegation, else mark and keep the revoked state. | |
1072 | */ | |
1073 | void nfs_remove_bad_delegation(struct inode *inode, | |
1074 | const nfs4_stateid *stateid) | |
1075 | { | |
1076 | if (stateid && stateid->type == NFS4_FREED_STATEID_TYPE) | |
1077 | nfs_delegation_mark_returned(inode, stateid); | |
1078 | else | |
1079 | nfs_revoke_delegation(inode, stateid); | |
1080 | } | |
1081 | EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation); | |
1082 | ||
d3978bb3 | 1083 | /** |
826e0013 | 1084 | * nfs_expire_unused_delegation_types |
d3978bb3 CL |
1085 | * @clp: client to process |
1086 | * @flags: delegation types to expire | |
1087 | * | |
1088 | */ | |
826e0013 | 1089 | void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags) |
58d9714a | 1090 | { |
826e0013 | 1091 | nfs_client_mark_return_unused_delegation_types(clp, flags); |
b0d3ded1 | 1092 | nfs_delegation_run_state_manager(clp); |
58d9714a TM |
1093 | } |
1094 | ||
d3978bb3 | 1095 | static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) |
b7391f44 TM |
1096 | { |
1097 | struct nfs_delegation *delegation; | |
1098 | ||
d3978bb3 | 1099 | list_for_each_entry_rcu(delegation, &server->delegations, super_list) { |
b7391f44 TM |
1100 | if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) |
1101 | continue; | |
b757144f | 1102 | nfs_mark_return_if_closed_delegation(server, delegation); |
b7391f44 | 1103 | } |
b7391f44 TM |
1104 | } |
1105 | ||
d3978bb3 CL |
1106 | /** |
1107 | * nfs_expire_unreferenced_delegations - Eliminate unused delegations | |
1108 | * @clp: nfs_client to process | |
1109 | * | |
1110 | */ | |
b7391f44 TM |
1111 | void nfs_expire_unreferenced_delegations(struct nfs_client *clp) |
1112 | { | |
d3978bb3 CL |
1113 | struct nfs_server *server; |
1114 | ||
1115 | rcu_read_lock(); | |
1116 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) | |
1117 | nfs_mark_return_unreferenced_delegations(server); | |
1118 | rcu_read_unlock(); | |
1119 | ||
b7391f44 TM |
1120 | nfs_delegation_run_state_manager(clp); |
1121 | } | |
1122 | ||
d3978bb3 CL |
1123 | /** |
1124 | * nfs_async_inode_return_delegation - asynchronously return a delegation | |
1125 | * @inode: inode to process | |
8e663f0e | 1126 | * @stateid: state ID information |
d3978bb3 CL |
1127 | * |
1128 | * Returns zero on success, or a negative errno value. | |
1da177e4 | 1129 | */ |
d3978bb3 CL |
1130 | int nfs_async_inode_return_delegation(struct inode *inode, |
1131 | const nfs4_stateid *stateid) | |
1da177e4 | 1132 | { |
ed1e6211 TM |
1133 | struct nfs_server *server = NFS_SERVER(inode); |
1134 | struct nfs_client *clp = server->nfs_client; | |
6411bd4a | 1135 | struct nfs_delegation *delegation; |
1da177e4 | 1136 | |
6411bd4a | 1137 | rcu_read_lock(); |
457a5042 | 1138 | delegation = nfs4_get_valid_delegation(inode); |
755a48a7 TM |
1139 | if (delegation == NULL) |
1140 | goto out_enoent; | |
4816fdad TM |
1141 | if (stateid != NULL && |
1142 | !clp->cl_mvops->match_stateid(&delegation->stateid, stateid)) | |
755a48a7 | 1143 | goto out_enoent; |
ed1e6211 | 1144 | nfs_mark_return_delegation(server, delegation); |
6411bd4a | 1145 | rcu_read_unlock(); |
d3978bb3 | 1146 | |
6b4befc0 TM |
1147 | /* If there are any application leases or delegations, recall them */ |
1148 | break_lease(inode, O_WRONLY | O_RDWR | O_NONBLOCK); | |
1149 | ||
6411bd4a TM |
1150 | nfs_delegation_run_state_manager(clp); |
1151 | return 0; | |
755a48a7 TM |
1152 | out_enoent: |
1153 | rcu_read_unlock(); | |
1154 | return -ENOENT; | |
1da177e4 LT |
1155 | } |
1156 | ||
d3978bb3 CL |
1157 | static struct inode * |
1158 | nfs_delegation_find_inode_server(struct nfs_server *server, | |
1159 | const struct nfs_fh *fhandle) | |
1da177e4 LT |
1160 | { |
1161 | struct nfs_delegation *delegation; | |
113aac6d TM |
1162 | struct super_block *freeme = NULL; |
1163 | struct inode *res = NULL; | |
d3978bb3 CL |
1164 | |
1165 | list_for_each_entry_rcu(delegation, &server->delegations, super_list) { | |
86e89489 TM |
1166 | spin_lock(&delegation->lock); |
1167 | if (delegation->inode != NULL && | |
457a5042 | 1168 | !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) && |
86e89489 | 1169 | nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { |
113aac6d TM |
1170 | if (nfs_sb_active(server->super)) { |
1171 | freeme = server->super; | |
1172 | res = igrab(delegation->inode); | |
1173 | } | |
6c342655 TM |
1174 | spin_unlock(&delegation->lock); |
1175 | if (res != NULL) | |
1176 | return res; | |
e39d8a18 TM |
1177 | if (freeme) { |
1178 | rcu_read_unlock(); | |
113aac6d | 1179 | nfs_sb_deactive(freeme); |
e39d8a18 TM |
1180 | rcu_read_lock(); |
1181 | } | |
6c342655 | 1182 | return ERR_PTR(-EAGAIN); |
1da177e4 | 1183 | } |
86e89489 | 1184 | spin_unlock(&delegation->lock); |
1da177e4 | 1185 | } |
6c342655 | 1186 | return ERR_PTR(-ENOENT); |
d3978bb3 CL |
1187 | } |
1188 | ||
1189 | /** | |
1190 | * nfs_delegation_find_inode - retrieve the inode associated with a delegation | |
1191 | * @clp: client state handle | |
1192 | * @fhandle: filehandle from a delegation recall | |
1193 | * | |
1194 | * Returns pointer to inode matching "fhandle," or NULL if a matching inode | |
1195 | * cannot be found. | |
1196 | */ | |
1197 | struct inode *nfs_delegation_find_inode(struct nfs_client *clp, | |
1198 | const struct nfs_fh *fhandle) | |
1199 | { | |
1200 | struct nfs_server *server; | |
6c342655 | 1201 | struct inode *res; |
d3978bb3 CL |
1202 | |
1203 | rcu_read_lock(); | |
1204 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { | |
1205 | res = nfs_delegation_find_inode_server(server, fhandle); | |
d5681f59 AS |
1206 | if (res != ERR_PTR(-ENOENT)) { |
1207 | rcu_read_unlock(); | |
6c342655 | 1208 | return res; |
d5681f59 | 1209 | } |
d3978bb3 | 1210 | } |
8383e460 | 1211 | rcu_read_unlock(); |
6c342655 | 1212 | return ERR_PTR(-ENOENT); |
1da177e4 LT |
1213 | } |
1214 | ||
d3978bb3 CL |
1215 | static void nfs_delegation_mark_reclaim_server(struct nfs_server *server) |
1216 | { | |
1217 | struct nfs_delegation *delegation; | |
1218 | ||
45870d69 TM |
1219 | list_for_each_entry_rcu(delegation, &server->delegations, super_list) { |
1220 | /* | |
1221 | * If the delegation may have been admin revoked, then we | |
1222 | * cannot reclaim it. | |
1223 | */ | |
1224 | if (test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags)) | |
1225 | continue; | |
d3978bb3 | 1226 | set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); |
45870d69 | 1227 | } |
d3978bb3 CL |
1228 | } |
1229 | ||
1230 | /** | |
1231 | * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed | |
1232 | * @clp: nfs_client to process | |
1233 | * | |
1da177e4 | 1234 | */ |
adfa6f98 | 1235 | void nfs_delegation_mark_reclaim(struct nfs_client *clp) |
1da177e4 | 1236 | { |
d3978bb3 CL |
1237 | struct nfs_server *server; |
1238 | ||
8383e460 | 1239 | rcu_read_lock(); |
d3978bb3 CL |
1240 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) |
1241 | nfs_delegation_mark_reclaim_server(server); | |
8383e460 | 1242 | rcu_read_unlock(); |
1da177e4 LT |
1243 | } |
1244 | ||
1bba38b2 TM |
1245 | static int nfs_server_reap_unclaimed_delegations(struct nfs_server *server, |
1246 | void __always_unused *data) | |
1da177e4 | 1247 | { |
8383e460 | 1248 | struct nfs_delegation *delegation; |
86e89489 | 1249 | struct inode *inode; |
8383e460 TM |
1250 | restart: |
1251 | rcu_read_lock(); | |
1bba38b2 TM |
1252 | list_for_each_entry_rcu(delegation, &server->delegations, super_list) { |
1253 | if (test_bit(NFS_DELEGATION_INODE_FREEING, | |
1254 | &delegation->flags) || | |
1255 | test_bit(NFS_DELEGATION_RETURNING, | |
1256 | &delegation->flags) || | |
1257 | test_bit(NFS_DELEGATION_NEED_RECLAIM, | |
1258 | &delegation->flags) == 0) | |
1259 | continue; | |
1260 | inode = nfs_delegation_grab_inode(delegation); | |
1261 | if (inode == NULL) | |
f92214e4 | 1262 | continue; |
1bba38b2 TM |
1263 | delegation = nfs_start_delegation_return_locked(NFS_I(inode)); |
1264 | rcu_read_unlock(); | |
1265 | if (delegation != NULL) { | |
1266 | if (nfs_detach_delegation(NFS_I(inode), delegation, | |
1267 | server) != NULL) | |
1268 | nfs_free_delegation(delegation); | |
1269 | /* Match nfs_start_delegation_return_locked */ | |
1270 | nfs_put_delegation(delegation); | |
d3978bb3 | 1271 | } |
1bba38b2 TM |
1272 | iput(inode); |
1273 | cond_resched(); | |
1274 | goto restart; | |
1da177e4 | 1275 | } |
8383e460 | 1276 | rcu_read_unlock(); |
1bba38b2 TM |
1277 | return 0; |
1278 | } | |
1279 | ||
1280 | /** | |
1281 | * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done | |
1282 | * @clp: nfs_client to process | |
1283 | * | |
1284 | */ | |
1285 | void nfs_delegation_reap_unclaimed(struct nfs_client *clp) | |
1286 | { | |
1287 | nfs_client_for_each_server(clp, nfs_server_reap_unclaimed_delegations, | |
1288 | NULL); | |
1da177e4 | 1289 | } |
3e4f6290 | 1290 | |
bb3d1a3b TM |
1291 | static inline bool nfs4_server_rebooted(const struct nfs_client *clp) |
1292 | { | |
1293 | return (clp->cl_state & (BIT(NFS4CLNT_CHECK_LEASE) | | |
1294 | BIT(NFS4CLNT_LEASE_EXPIRED) | | |
1295 | BIT(NFS4CLNT_SESSION_RESET))) != 0; | |
1296 | } | |
1297 | ||
45870d69 TM |
1298 | static void nfs_mark_test_expired_delegation(struct nfs_server *server, |
1299 | struct nfs_delegation *delegation) | |
1300 | { | |
059b43e9 TM |
1301 | if (delegation->stateid.type == NFS4_INVALID_STATEID_TYPE) |
1302 | return; | |
45870d69 TM |
1303 | clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); |
1304 | set_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); | |
f163aa81 | 1305 | set_bit(NFS4SERV_DELEGATION_EXPIRED, &server->delegation_flags); |
45870d69 TM |
1306 | set_bit(NFS4CLNT_DELEGATION_EXPIRED, &server->nfs_client->cl_state); |
1307 | } | |
1308 | ||
bb3d1a3b TM |
1309 | static void nfs_inode_mark_test_expired_delegation(struct nfs_server *server, |
1310 | struct inode *inode) | |
1311 | { | |
1312 | struct nfs_delegation *delegation; | |
1313 | ||
1314 | rcu_read_lock(); | |
1315 | delegation = rcu_dereference(NFS_I(inode)->delegation); | |
1316 | if (delegation) | |
1317 | nfs_mark_test_expired_delegation(server, delegation); | |
1318 | rcu_read_unlock(); | |
1319 | ||
1320 | } | |
1321 | ||
45870d69 TM |
1322 | static void nfs_delegation_mark_test_expired_server(struct nfs_server *server) |
1323 | { | |
1324 | struct nfs_delegation *delegation; | |
1325 | ||
1326 | list_for_each_entry_rcu(delegation, &server->delegations, super_list) | |
1327 | nfs_mark_test_expired_delegation(server, delegation); | |
1328 | } | |
1329 | ||
1330 | /** | |
1331 | * nfs_mark_test_expired_all_delegations - mark all delegations for testing | |
1332 | * @clp: nfs_client to process | |
1333 | * | |
1334 | * Iterates through all the delegations associated with this server and | |
1335 | * marks them as needing to be checked for validity. | |
1336 | */ | |
1337 | void nfs_mark_test_expired_all_delegations(struct nfs_client *clp) | |
1338 | { | |
1339 | struct nfs_server *server; | |
1340 | ||
1341 | rcu_read_lock(); | |
1342 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) | |
1343 | nfs_delegation_mark_test_expired_server(server); | |
1344 | rcu_read_unlock(); | |
1345 | } | |
1346 | ||
8ca017c8 SM |
1347 | /** |
1348 | * nfs_test_expired_all_delegations - test all delegations for a client | |
1349 | * @clp: nfs_client to process | |
1350 | * | |
1351 | * Helper for handling "recallable state revoked" status from server. | |
1352 | */ | |
1353 | void nfs_test_expired_all_delegations(struct nfs_client *clp) | |
1354 | { | |
1355 | nfs_mark_test_expired_all_delegations(clp); | |
1356 | nfs4_schedule_state_manager(clp); | |
1357 | } | |
1358 | ||
ad114089 TM |
1359 | static void |
1360 | nfs_delegation_test_free_expired(struct inode *inode, | |
1361 | nfs4_stateid *stateid, | |
1362 | const struct cred *cred) | |
1363 | { | |
1364 | struct nfs_server *server = NFS_SERVER(inode); | |
1365 | const struct nfs4_minor_version_ops *ops = server->nfs_client->cl_mvops; | |
1366 | int status; | |
1367 | ||
1368 | if (!cred) | |
1369 | return; | |
1370 | status = ops->test_and_free_expired(server, stateid, cred); | |
1371 | if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID) | |
1372 | nfs_remove_bad_delegation(inode, stateid); | |
1373 | } | |
1374 | ||
7f156ef0 TM |
1375 | static int nfs_server_reap_expired_delegations(struct nfs_server *server, |
1376 | void __always_unused *data) | |
45870d69 | 1377 | { |
45870d69 | 1378 | struct nfs_delegation *delegation; |
45870d69 | 1379 | struct inode *inode; |
a52458b4 | 1380 | const struct cred *cred; |
45870d69 | 1381 | nfs4_stateid stateid; |
a9b8d90f BC |
1382 | unsigned long gen = ++server->delegation_gen; |
1383 | ||
f163aa81 TM |
1384 | if (!test_and_clear_bit(NFS4SERV_DELEGATION_EXPIRED, |
1385 | &server->delegation_flags)) | |
1386 | return 0; | |
45870d69 TM |
1387 | restart: |
1388 | rcu_read_lock(); | |
7f156ef0 TM |
1389 | list_for_each_entry_rcu(delegation, &server->delegations, super_list) { |
1390 | if (test_bit(NFS_DELEGATION_INODE_FREEING, | |
1391 | &delegation->flags) || | |
1392 | test_bit(NFS_DELEGATION_RETURNING, | |
1393 | &delegation->flags) || | |
1394 | test_bit(NFS_DELEGATION_TEST_EXPIRED, | |
a9b8d90f BC |
1395 | &delegation->flags) == 0 || |
1396 | delegation->test_gen == gen) | |
7f156ef0 TM |
1397 | continue; |
1398 | inode = nfs_delegation_grab_inode(delegation); | |
1399 | if (inode == NULL) | |
f92214e4 | 1400 | continue; |
fc51b1cf | 1401 | spin_lock(&delegation->lock); |
7f156ef0 TM |
1402 | cred = get_cred_rcu(delegation->cred); |
1403 | nfs4_stateid_copy(&stateid, &delegation->stateid); | |
fc51b1cf | 1404 | spin_unlock(&delegation->lock); |
a9b8d90f | 1405 | delegation->test_gen = gen; |
7f156ef0 TM |
1406 | clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); |
1407 | rcu_read_unlock(); | |
1408 | nfs_delegation_test_free_expired(inode, &stateid, cred); | |
1409 | put_cred(cred); | |
1410 | if (!nfs4_server_rebooted(server->nfs_client)) { | |
45870d69 | 1411 | iput(inode); |
3ca951b6 | 1412 | cond_resched(); |
45870d69 TM |
1413 | goto restart; |
1414 | } | |
7f156ef0 | 1415 | nfs_inode_mark_test_expired_delegation(server,inode); |
f163aa81 TM |
1416 | set_bit(NFS4SERV_DELEGATION_EXPIRED, &server->delegation_flags); |
1417 | set_bit(NFS4CLNT_DELEGATION_EXPIRED, | |
1418 | &server->nfs_client->cl_state); | |
7f156ef0 TM |
1419 | iput(inode); |
1420 | return -EAGAIN; | |
45870d69 TM |
1421 | } |
1422 | rcu_read_unlock(); | |
7f156ef0 TM |
1423 | return 0; |
1424 | } | |
1425 | ||
1426 | /** | |
1427 | * nfs_reap_expired_delegations - reap expired delegations | |
1428 | * @clp: nfs_client to process | |
1429 | * | |
1430 | * Iterates through all the delegations associated with this server and | |
1431 | * checks if they have may have been revoked. This function is usually | |
1432 | * expected to be called in cases where the server may have lost its | |
1433 | * lease. | |
1434 | */ | |
1435 | void nfs_reap_expired_delegations(struct nfs_client *clp) | |
1436 | { | |
1437 | nfs_client_for_each_server(clp, nfs_server_reap_expired_delegations, | |
1438 | NULL); | |
45870d69 TM |
1439 | } |
1440 | ||
6c2d8f8d TM |
1441 | void nfs_inode_find_delegation_state_and_recover(struct inode *inode, |
1442 | const nfs4_stateid *stateid) | |
1443 | { | |
1444 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; | |
1445 | struct nfs_delegation *delegation; | |
1446 | bool found = false; | |
1447 | ||
1448 | rcu_read_lock(); | |
1449 | delegation = rcu_dereference(NFS_I(inode)->delegation); | |
1450 | if (delegation && | |
42c304c3 TM |
1451 | nfs4_stateid_match_or_older(&delegation->stateid, stateid) && |
1452 | !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { | |
6c2d8f8d TM |
1453 | nfs_mark_test_expired_delegation(NFS_SERVER(inode), delegation); |
1454 | found = true; | |
1455 | } | |
1456 | rcu_read_unlock(); | |
1457 | if (found) | |
1458 | nfs4_schedule_state_manager(clp); | |
1459 | } | |
1460 | ||
d3978bb3 CL |
1461 | /** |
1462 | * nfs_delegations_present - check for existence of delegations | |
1463 | * @clp: client state handle | |
1464 | * | |
1465 | * Returns one if there are any nfs_delegation structures attached | |
1466 | * to this nfs_client. | |
1467 | */ | |
1468 | int nfs_delegations_present(struct nfs_client *clp) | |
1469 | { | |
1470 | struct nfs_server *server; | |
1471 | int ret = 0; | |
1472 | ||
1473 | rcu_read_lock(); | |
1474 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) | |
1475 | if (!list_empty(&server->delegations)) { | |
1476 | ret = 1; | |
1477 | break; | |
1478 | } | |
1479 | rcu_read_unlock(); | |
1480 | return ret; | |
1481 | } | |
1482 | ||
12f275cd TM |
1483 | /** |
1484 | * nfs4_refresh_delegation_stateid - Update delegation stateid seqid | |
1485 | * @dst: stateid to refresh | |
1486 | * @inode: inode to check | |
1487 | * | |
1488 | * Returns "true" and updates "dst->seqid" * if inode had a delegation | |
1489 | * that matches our delegation stateid. Otherwise "false" is returned. | |
1490 | */ | |
1491 | bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode) | |
1492 | { | |
1493 | struct nfs_delegation *delegation; | |
1494 | bool ret = false; | |
1495 | if (!inode) | |
1496 | goto out; | |
1497 | ||
1498 | rcu_read_lock(); | |
1499 | delegation = rcu_dereference(NFS_I(inode)->delegation); | |
1500 | if (delegation != NULL && | |
b5756208 | 1501 | nfs4_stateid_match_other(dst, &delegation->stateid) && |
246afc0a | 1502 | nfs4_stateid_is_newer(&delegation->stateid, dst) && |
b5756208 | 1503 | !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { |
12f275cd | 1504 | dst->seqid = delegation->stateid.seqid; |
79cc5542 | 1505 | ret = true; |
12f275cd TM |
1506 | } |
1507 | rcu_read_unlock(); | |
1508 | out: | |
1509 | return ret; | |
1510 | } | |
1511 | ||
d3978bb3 CL |
1512 | /** |
1513 | * nfs4_copy_delegation_stateid - Copy inode's state ID information | |
d3978bb3 | 1514 | * @inode: inode to check |
0032a7a7 | 1515 | * @flags: delegation type requirement |
abf4e13c TM |
1516 | * @dst: stateid data structure to fill in |
1517 | * @cred: optional argument to retrieve credential | |
d3978bb3 | 1518 | * |
0032a7a7 TM |
1519 | * Returns "true" and fills in "dst->data" * if inode had a delegation, |
1520 | * otherwise "false" is returned. | |
d3978bb3 | 1521 | */ |
abf4e13c | 1522 | bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags, |
a52458b4 | 1523 | nfs4_stateid *dst, const struct cred **cred) |
3e4f6290 | 1524 | { |
3e4f6290 TM |
1525 | struct nfs_inode *nfsi = NFS_I(inode); |
1526 | struct nfs_delegation *delegation; | |
fc51b1cf | 1527 | bool ret = false; |
3e4f6290 | 1528 | |
0032a7a7 | 1529 | flags &= FMODE_READ|FMODE_WRITE; |
8383e460 TM |
1530 | rcu_read_lock(); |
1531 | delegation = rcu_dereference(nfsi->delegation); | |
fc51b1cf TM |
1532 | if (!delegation) |
1533 | goto out; | |
1534 | spin_lock(&delegation->lock); | |
aa05c87f | 1535 | ret = nfs4_is_valid_delegation(delegation, flags); |
0032a7a7 | 1536 | if (ret) { |
f597c537 | 1537 | nfs4_stateid_copy(dst, &delegation->stateid); |
0032a7a7 | 1538 | nfs_mark_delegation_referenced(delegation); |
abf4e13c | 1539 | if (cred) |
a52458b4 | 1540 | *cred = get_cred(delegation->cred); |
3e4f6290 | 1541 | } |
fc51b1cf TM |
1542 | spin_unlock(&delegation->lock); |
1543 | out: | |
8383e460 TM |
1544 | rcu_read_unlock(); |
1545 | return ret; | |
3e4f6290 | 1546 | } |
5445b1fb TM |
1547 | |
1548 | /** | |
1549 | * nfs4_delegation_flush_on_close - Check if we must flush file on close | |
1550 | * @inode: inode to check | |
1551 | * | |
1552 | * This function checks the number of outstanding writes to the file | |
1553 | * against the delegation 'space_limit' field to see if | |
1554 | * the spec requires us to flush the file on close. | |
1555 | */ | |
1556 | bool nfs4_delegation_flush_on_close(const struct inode *inode) | |
1557 | { | |
1558 | struct nfs_inode *nfsi = NFS_I(inode); | |
1559 | struct nfs_delegation *delegation; | |
1560 | bool ret = true; | |
1561 | ||
1562 | rcu_read_lock(); | |
1563 | delegation = rcu_dereference(nfsi->delegation); | |
1564 | if (delegation == NULL || !(delegation->type & FMODE_WRITE)) | |
1565 | goto out; | |
a6b6d5b8 | 1566 | if (atomic_long_read(&nfsi->nrequests) < delegation->pagemod_limit) |
5445b1fb TM |
1567 | ret = false; |
1568 | out: | |
1569 | rcu_read_unlock(); | |
1570 | return ret; | |
1571 | } | |
10717f45 TM |
1572 | |
1573 | module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644); |