blob: cb03ba99ae51789fd984bb75156fe843e2441a19 [file] [log] [blame]
Thomas Gleixner457c8992019-05-19 13:08:55 +01001// SPDX-License-Identifier: GPL-2.0-only
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * linux/fs/nfs/delegation.c
4 *
5 * Copyright (C) 2004 Trond Myklebust
6 *
7 * NFS file delegation management
8 *
9 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070010#include <linux/completion.h>
Trond Myklebust58d97142006-01-03 09:55:24 +010011#include <linux/kthread.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <linux/module.h>
13#include <linux/sched.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090014#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/spinlock.h>
Jeff Layton1eb5d982018-01-09 08:21:17 -050016#include <linux/iversion.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017
18#include <linux/nfs4.h>
19#include <linux/nfs_fs.h>
20#include <linux/nfs_xdr.h>
21
Trond Myklebust4ce79712005-06-22 17:16:21 +000022#include "nfs4_fs.h"
Trond Myklebustc01d3642018-03-20 16:43:20 -040023#include "nfs4session.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include "delegation.h"
David Howells24c8dbb2006-08-22 20:06:10 -040025#include "internal.h"
Trond Myklebustca8acf82013-08-13 10:36:56 -040026#include "nfs4trace.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070027
Trond Myklebust10717f42020-01-27 09:58:19 -050028#define NFS_DEFAULT_DELEGATION_WATERMARK (5000U)
29
Trond Myklebustd2269ea2020-01-27 09:58:18 -050030static atomic_long_t nfs_active_delegations;
Trond Myklebust10717f42020-01-27 09:58:19 -050031static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK;
Trond Myklebustd2269ea2020-01-27 09:58:18 -050032
33static void __nfs_free_delegation(struct nfs_delegation *delegation)
Trond Myklebust905f8d12007-08-06 12:18:34 -040034{
NeilBrowna52458b2018-12-03 11:30:31 +110035 put_cred(delegation->cred);
36 delegation->cred = NULL;
Lai Jiangshan26f04dd2011-05-01 06:21:54 -070037 kfree_rcu(delegation, rcu);
Trond Myklebust8383e462007-07-06 15:12:04 -040038}
39
Trond Myklebustd2269ea2020-01-27 09:58:18 -050040static 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);
Trond Myklebustefeda802020-02-05 09:01:54 -050045 if (!test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
46 nfs_clear_verifier_delegated(delegation->inode);
Trond Myklebustd2269ea2020-01-27 09:58:18 -050047 }
48}
49
Trond Myklebust8c755932020-02-13 14:51:06 -050050static struct nfs_delegation *nfs_get_delegation(struct nfs_delegation *delegation)
51{
52 refcount_inc(&delegation->refcount);
53 return delegation;
54}
55
56static void nfs_put_delegation(struct nfs_delegation *delegation)
57{
58 if (refcount_dec_and_test(&delegation->refcount))
59 __nfs_free_delegation(delegation);
60}
61
Trond Myklebustd2269ea2020-01-27 09:58:18 -050062static void nfs_free_delegation(struct nfs_delegation *delegation)
63{
64 nfs_mark_delegation_revoked(delegation);
Trond Myklebust8c755932020-02-13 14:51:06 -050065 nfs_put_delegation(delegation);
Trond Myklebustd2269ea2020-01-27 09:58:18 -050066}
67
Chuck Leverd3978bb2010-12-24 01:33:04 +000068/**
69 * nfs_mark_delegation_referenced - set delegation's REFERENCED flag
70 * @delegation: delegation to process
71 *
72 */
Trond Myklebustb7391f42008-12-23 15:21:52 -050073void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
74{
75 set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
76}
77
Trond Myklebustaa05c872016-09-22 13:38:54 -040078static bool
79nfs4_is_valid_delegation(const struct nfs_delegation *delegation,
80 fmode_t flags)
81{
82 if (delegation != NULL && (delegation->type & flags) == flags &&
83 !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) &&
84 !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
85 return true;
86 return false;
87}
88
Trond Myklebustbe3df3d2019-10-31 18:40:32 -040089struct nfs_delegation *nfs4_get_valid_delegation(const struct inode *inode)
90{
91 struct nfs_delegation *delegation;
92
93 delegation = rcu_dereference(NFS_I(inode)->delegation);
94 if (nfs4_is_valid_delegation(delegation, 0))
95 return delegation;
96 return NULL;
97}
98
Peng Tao15bb3af2014-07-03 13:05:00 +080099static int
100nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark)
Trond Myklebustb7391f42008-12-23 15:21:52 -0500101{
102 struct nfs_delegation *delegation;
103 int ret = 0;
104
105 flags &= FMODE_READ|FMODE_WRITE;
106 rcu_read_lock();
107 delegation = rcu_dereference(NFS_I(inode)->delegation);
Trond Myklebustaa05c872016-09-22 13:38:54 -0400108 if (nfs4_is_valid_delegation(delegation, flags)) {
Peng Tao15bb3af2014-07-03 13:05:00 +0800109 if (mark)
110 nfs_mark_delegation_referenced(delegation);
Trond Myklebustb7391f42008-12-23 15:21:52 -0500111 ret = 1;
112 }
113 rcu_read_unlock();
114 return ret;
115}
Peng Tao15bb3af2014-07-03 13:05:00 +0800116/**
117 * nfs_have_delegation - check if inode has a delegation, mark it
118 * NFS_DELEGATION_REFERENCED if there is one.
119 * @inode: inode to check
120 * @flags: delegation types to check for
121 *
122 * Returns one if inode has the indicated delegation, otherwise zero.
123 */
124int nfs4_have_delegation(struct inode *inode, fmode_t flags)
125{
126 return nfs4_do_check_delegation(inode, flags, true);
127}
128
129/*
130 * nfs4_check_delegation - check if inode has a delegation, do not mark
131 * NFS_DELEGATION_REFERENCED if it has one.
132 */
133int nfs4_check_delegation(struct inode *inode, fmode_t flags)
134{
135 return nfs4_do_check_delegation(inode, flags, false);
136}
Trond Myklebustb7391f42008-12-23 15:21:52 -0500137
Olga Kornievskaia44f411c2018-10-04 14:45:00 -0400138static int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_stateid *stateid)
Trond Myklebust888e6942005-11-04 15:38:11 -0500139{
140 struct inode *inode = state->inode;
141 struct file_lock *fl;
Jeff Laytonbd61e0a2015-01-16 15:05:55 -0500142 struct file_lock_context *flctx = inode->i_flctx;
143 struct list_head *list;
Trond Myklebustd5122202009-06-17 13:22:58 -0700144 int status = 0;
Trond Myklebust888e6942005-11-04 15:38:11 -0500145
Jeff Laytonbd61e0a2015-01-16 15:05:55 -0500146 if (flctx == NULL)
Trond Myklebust65b62a22013-02-07 10:54:07 -0500147 goto out;
Jeff Layton314d7cc2013-04-10 15:36:48 -0400148
Jeff Laytonbd61e0a2015-01-16 15:05:55 -0500149 list = &flctx->flc_posix;
Jeff Layton6109c852015-01-16 15:05:57 -0500150 spin_lock(&flctx->flc_lock);
Jeff Laytonbd61e0a2015-01-16 15:05:55 -0500151restart:
152 list_for_each_entry(fl, list, fl_list) {
Olga Kornievskaia44f411c2018-10-04 14:45:00 -0400153 if (nfs_file_open_context(fl->fl_file)->state != state)
Trond Myklebust888e6942005-11-04 15:38:11 -0500154 continue;
Jeff Layton6109c852015-01-16 15:05:57 -0500155 spin_unlock(&flctx->flc_lock);
Trond Myklebustdb4f2e62013-04-01 15:56:46 -0400156 status = nfs4_lock_delegation_recall(fl, state, stateid);
Trond Myklebustd5122202009-06-17 13:22:58 -0700157 if (status < 0)
Trond Myklebust3f09df72009-06-17 13:23:00 -0700158 goto out;
Jeff Layton6109c852015-01-16 15:05:57 -0500159 spin_lock(&flctx->flc_lock);
Trond Myklebust888e6942005-11-04 15:38:11 -0500160 }
Jeff Laytonbd61e0a2015-01-16 15:05:55 -0500161 if (list == &flctx->flc_posix) {
162 list = &flctx->flc_flock;
163 goto restart;
Jeff Layton5263e312015-01-16 15:05:55 -0500164 }
Jeff Layton6109c852015-01-16 15:05:57 -0500165 spin_unlock(&flctx->flc_lock);
Trond Myklebust3f09df72009-06-17 13:23:00 -0700166out:
Trond Myklebust888e6942005-11-04 15:38:11 -0500167 return status;
168}
169
Trond Myklebust24311f82015-09-20 10:50:17 -0400170static int nfs_delegation_claim_opens(struct inode *inode,
171 const nfs4_stateid *stateid, fmode_t type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172{
173 struct nfs_inode *nfsi = NFS_I(inode);
174 struct nfs_open_context *ctx;
Trond Myklebustd25be542013-02-05 11:43:28 -0500175 struct nfs4_state_owner *sp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 struct nfs4_state *state;
Trond Myklebustd25be542013-02-05 11:43:28 -0500177 unsigned int seq;
Trond Myklebust888e6942005-11-04 15:38:11 -0500178 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179
180again:
Trond Myklebust0de43972018-09-02 15:57:01 -0400181 rcu_read_lock();
182 list_for_each_entry_rcu(ctx, &nfsi->open_files, list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 state = ctx->state;
184 if (state == NULL)
185 continue;
186 if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
187 continue;
Trond Myklebustf8ebf7a2014-10-17 23:02:52 +0300188 if (!nfs4_valid_open_stateid(state))
189 continue;
Trond Myklebustf597c532012-03-04 18:13:56 -0500190 if (!nfs4_stateid_match(&state->stateid, stateid))
Trond Myklebust90163022007-07-05 14:55:18 -0400191 continue;
Trond Myklebust0de43972018-09-02 15:57:01 -0400192 if (!get_nfs_open_context(ctx))
193 continue;
194 rcu_read_unlock();
Trond Myklebustd25be542013-02-05 11:43:28 -0500195 sp = state->owner;
Trond Myklebust65b62a22013-02-07 10:54:07 -0500196 /* Block nfs4_proc_unlck */
197 mutex_lock(&sp->so_delegreturn_mutex);
Trond Myklebustd25be542013-02-05 11:43:28 -0500198 seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
Trond Myklebust5eb8d182019-07-19 14:08:37 -0400199 err = nfs4_open_delegation_recall(ctx, state, stateid);
Trond Myklebustd25be542013-02-05 11:43:28 -0500200 if (!err)
Olga Kornievskaia44f411c2018-10-04 14:45:00 -0400201 err = nfs_delegation_claim_locks(state, stateid);
Trond Myklebustd25be542013-02-05 11:43:28 -0500202 if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
203 err = -EAGAIN;
Trond Myklebust65b62a22013-02-07 10:54:07 -0500204 mutex_unlock(&sp->so_delegreturn_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 put_nfs_open_context(ctx);
Trond Myklebust888e6942005-11-04 15:38:11 -0500206 if (err != 0)
Trond Myklebustd18cc1f2009-12-03 08:10:17 -0500207 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 goto again;
209 }
Trond Myklebust0de43972018-09-02 15:57:01 -0400210 rcu_read_unlock();
Trond Myklebustd18cc1f2009-12-03 08:10:17 -0500211 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212}
213
Chuck Leverd3978bb2010-12-24 01:33:04 +0000214/**
215 * nfs_inode_reclaim_delegation - process a delegation reclaim request
216 * @inode: inode to process
217 * @cred: credential to use for request
Trond Myklebust35156bf2018-03-20 17:03:13 -0400218 * @type: delegation type
219 * @stateid: delegation stateid
220 * @pagemod_limit: write delegation "space_limit"
Chuck Leverd3978bb2010-12-24 01:33:04 +0000221 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 */
NeilBrowna52458b2018-12-03 11:30:31 +1100223void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
Trond Myklebust35156bf2018-03-20 17:03:13 -0400224 fmode_t type,
225 const nfs4_stateid *stateid,
226 unsigned long pagemod_limit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227{
Trond Myklebust8f649c32010-05-01 12:36:18 -0400228 struct nfs_delegation *delegation;
NeilBrowna52458b2018-12-03 11:30:31 +1100229 const struct cred *oldcred = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230
Trond Myklebust8f649c32010-05-01 12:36:18 -0400231 rcu_read_lock();
232 delegation = rcu_dereference(NFS_I(inode)->delegation);
233 if (delegation != NULL) {
234 spin_lock(&delegation->lock);
Trond Myklebust1deed572019-10-22 08:52:47 -0400235 if (nfs4_is_valid_delegation(delegation, 0)) {
Trond Myklebust35156bf2018-03-20 17:03:13 -0400236 nfs4_stateid_copy(&delegation->stateid, stateid);
237 delegation->type = type;
238 delegation->pagemod_limit = pagemod_limit;
Trond Myklebust8f649c32010-05-01 12:36:18 -0400239 oldcred = delegation->cred;
NeilBrowna52458b2018-12-03 11:30:31 +1100240 delegation->cred = get_cred(cred);
Trond Myklebust8f649c32010-05-01 12:36:18 -0400241 clear_bit(NFS_DELEGATION_NEED_RECLAIM,
242 &delegation->flags);
Trond Myklebust8f649c32010-05-01 12:36:18 -0400243 spin_unlock(&delegation->lock);
Trond Myklebust8f649c32010-05-01 12:36:18 -0400244 rcu_read_unlock();
NeilBrowna52458b2018-12-03 11:30:31 +1100245 put_cred(oldcred);
Trond Myklebust35156bf2018-03-20 17:03:13 -0400246 trace_nfs4_reclaim_delegation(inode, type);
Trond Myklebustb1a318d2016-09-22 13:39:12 -0400247 return;
Trond Myklebust8f649c32010-05-01 12:36:18 -0400248 }
Trond Myklebustb1a318d2016-09-22 13:39:12 -0400249 /* We appear to have raced with a delegation return. */
250 spin_unlock(&delegation->lock);
Trond Myklebust8f649c32010-05-01 12:36:18 -0400251 }
Trond Myklebustb1a318d2016-09-22 13:39:12 -0400252 rcu_read_unlock();
Trond Myklebust35156bf2018-03-20 17:03:13 -0400253 nfs_inode_set_delegation(inode, cred, type, stateid, pagemod_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254}
255
Trond Myklebust57bfa892008-01-25 16:38:18 -0500256static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
257{
Trond Myklebust5d639442020-02-13 14:51:07 -0500258 const struct cred *cred;
Trond Myklebust57bfa892008-01-25 16:38:18 -0500259 int res = 0;
260
Trond Myklebust5d639442020-02-13 14:51:07 -0500261 if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
262 spin_lock(&delegation->lock);
263 cred = get_cred(delegation->cred);
264 spin_unlock(&delegation->lock);
265 res = nfs4_proc_delegreturn(inode, cred,
Trond Myklebust869f9df2014-11-10 18:43:56 -0500266 &delegation->stateid,
267 issync);
Trond Myklebust5d639442020-02-13 14:51:07 -0500268 put_cred(cred);
269 }
Trond Myklebust57bfa892008-01-25 16:38:18 -0500270 return res;
271}
272
Trond Myklebust86e89482008-12-23 15:21:39 -0500273static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation)
274{
275 struct inode *inode = NULL;
276
277 spin_lock(&delegation->lock);
278 if (delegation->inode != NULL)
279 inode = igrab(delegation->inode);
Trond Myklebust6f9449b2019-02-21 14:51:25 -0500280 if (!inode)
281 set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags);
Trond Myklebust86e89482008-12-23 15:21:39 -0500282 spin_unlock(&delegation->lock);
283 return inode;
284}
285
Chuck Leverdda4b222010-12-24 01:32:54 +0000286static struct nfs_delegation *
Trond Myklebustd25be542013-02-05 11:43:28 -0500287nfs_start_delegation_return_locked(struct nfs_inode *nfsi)
Trond Myklebust57bfa892008-01-25 16:38:18 -0500288{
Trond Myklebustd25be542013-02-05 11:43:28 -0500289 struct nfs_delegation *ret = NULL;
290 struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);
Trond Myklebust57bfa892008-01-25 16:38:18 -0500291
292 if (delegation == NULL)
Trond Myklebustd25be542013-02-05 11:43:28 -0500293 goto out;
294 spin_lock(&delegation->lock);
Trond Myklebust8c755932020-02-13 14:51:06 -0500295 if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
296 /* Refcount matched in nfs_end_delegation_return() */
297 ret = nfs_get_delegation(delegation);
298 }
Trond Myklebustd25be542013-02-05 11:43:28 -0500299 spin_unlock(&delegation->lock);
Trond Myklebustefeda802020-02-05 09:01:54 -0500300 if (ret)
301 nfs_clear_verifier_delegated(&nfsi->vfs_inode);
Trond Myklebustd25be542013-02-05 11:43:28 -0500302out:
303 return ret;
304}
305
306static struct nfs_delegation *
307nfs_start_delegation_return(struct nfs_inode *nfsi)
308{
309 struct nfs_delegation *delegation;
310
311 rcu_read_lock();
312 delegation = nfs_start_delegation_return_locked(nfsi);
313 rcu_read_unlock();
314 return delegation;
315}
316
317static void
318nfs_abort_delegation_return(struct nfs_delegation *delegation,
319 struct nfs_client *clp)
320{
Chuck Leverdda4b222010-12-24 01:32:54 +0000321
Trond Myklebust34310432008-12-23 15:21:38 -0500322 spin_lock(&delegation->lock);
Trond Myklebustd25be542013-02-05 11:43:28 -0500323 clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
324 set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
325 spin_unlock(&delegation->lock);
326 set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
327}
328
329static struct nfs_delegation *
330nfs_detach_delegation_locked(struct nfs_inode *nfsi,
331 struct nfs_delegation *delegation,
332 struct nfs_client *clp)
333{
334 struct nfs_delegation *deleg_cur =
335 rcu_dereference_protected(nfsi->delegation,
336 lockdep_is_held(&clp->cl_lock));
337
338 if (deleg_cur == NULL || delegation != deleg_cur)
339 return NULL;
340
341 spin_lock(&delegation->lock);
Trond Myklebustf9e0cc92019-10-21 14:12:13 -0400342 if (!delegation->inode) {
343 spin_unlock(&delegation->lock);
344 return NULL;
345 }
Trond Myklebust57bfa892008-01-25 16:38:18 -0500346 list_del_rcu(&delegation->super_list);
Trond Myklebust86e89482008-12-23 15:21:39 -0500347 delegation->inode = NULL;
Trond Myklebust57bfa892008-01-25 16:38:18 -0500348 rcu_assign_pointer(nfsi->delegation, NULL);
Trond Myklebust34310432008-12-23 15:21:38 -0500349 spin_unlock(&delegation->lock);
Trond Myklebust57bfa892008-01-25 16:38:18 -0500350 return delegation;
Trond Myklebust57bfa892008-01-25 16:38:18 -0500351}
352
Chuck Leverdda4b222010-12-24 01:32:54 +0000353static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi,
Trond Myklebustd25be542013-02-05 11:43:28 -0500354 struct nfs_delegation *delegation,
355 struct nfs_server *server)
Chuck Leverdda4b222010-12-24 01:32:54 +0000356{
Chuck Leverd3978bb2010-12-24 01:33:04 +0000357 struct nfs_client *clp = server->nfs_client;
Chuck Leverdda4b222010-12-24 01:32:54 +0000358
359 spin_lock(&clp->cl_lock);
Trond Myklebustd25be542013-02-05 11:43:28 -0500360 delegation = nfs_detach_delegation_locked(nfsi, delegation, clp);
Chuck Leverdda4b222010-12-24 01:32:54 +0000361 spin_unlock(&clp->cl_lock);
362 return delegation;
363}
364
Trond Myklebustd25be542013-02-05 11:43:28 -0500365static struct nfs_delegation *
366nfs_inode_detach_delegation(struct inode *inode)
367{
368 struct nfs_inode *nfsi = NFS_I(inode);
369 struct nfs_server *server = NFS_SERVER(inode);
370 struct nfs_delegation *delegation;
371
Trond Myklebustee05f452019-10-21 13:56:59 -0400372 rcu_read_lock();
373 delegation = rcu_dereference(nfsi->delegation);
374 if (delegation != NULL)
375 delegation = nfs_detach_delegation(nfsi, delegation, server);
376 rcu_read_unlock();
377 return delegation;
Trond Myklebustd25be542013-02-05 11:43:28 -0500378}
379
Trond Myklebustcf6726e2014-12-19 11:22:28 -0500380static void
Trond Myklebust57f188e2020-01-27 12:44:41 -0500381nfs_update_delegation_cred(struct nfs_delegation *delegation,
382 const struct cred *cred)
383{
384 const struct cred *old;
385
386 if (cred_fscmp(delegation->cred, cred) != 0) {
387 old = xchg(&delegation->cred, get_cred(cred));
388 put_cred(old);
389 }
390}
391
392static void
Trond Myklebustcf6726e2014-12-19 11:22:28 -0500393nfs_update_inplace_delegation(struct nfs_delegation *delegation,
394 const struct nfs_delegation *update)
395{
396 if (nfs4_stateid_is_newer(&update->stateid, &delegation->stateid)) {
397 delegation->stateid.seqid = update->stateid.seqid;
398 smp_wmb();
399 delegation->type = update->type;
Trond Myklebust57f188e2020-01-27 12:44:41 -0500400 delegation->pagemod_limit = update->pagemod_limit;
401 if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
402 delegation->change_attr = update->change_attr;
403 nfs_update_delegation_cred(delegation, update->cred);
404 /* smp_mb__before_atomic() is implicit due to xchg() */
405 clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
Trond Myklebustd2269ea2020-01-27 09:58:18 -0500406 atomic_long_inc(&nfs_active_delegations);
Trond Myklebust57f188e2020-01-27 12:44:41 -0500407 }
Trond Myklebustcf6726e2014-12-19 11:22:28 -0500408 }
409}
410
Chuck Leverd3978bb2010-12-24 01:33:04 +0000411/**
412 * nfs_inode_set_delegation - set up a delegation on an inode
413 * @inode: inode to which delegation applies
414 * @cred: cred to use for subsequent delegation processing
Trond Myklebust35156bf2018-03-20 17:03:13 -0400415 * @type: delegation type
416 * @stateid: delegation stateid
417 * @pagemod_limit: write delegation "space_limit"
Chuck Leverd3978bb2010-12-24 01:33:04 +0000418 *
419 * Returns zero on success, or a negative errno value.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 */
NeilBrowna52458b2018-12-03 11:30:31 +1100421int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
Trond Myklebust35156bf2018-03-20 17:03:13 -0400422 fmode_t type,
423 const nfs4_stateid *stateid,
424 unsigned long pagemod_limit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425{
Chuck Leverd3978bb2010-12-24 01:33:04 +0000426 struct nfs_server *server = NFS_SERVER(inode);
427 struct nfs_client *clp = server->nfs_client;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 struct nfs_inode *nfsi = NFS_I(inode);
David Howells17d2c0a2010-05-01 12:37:18 -0400429 struct nfs_delegation *delegation, *old_delegation;
Trond Myklebust57bfa892008-01-25 16:38:18 -0500430 struct nfs_delegation *freeme = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 int status = 0;
432
Trond Myklebust8535b2b2010-05-13 12:51:01 -0400433 delegation = kmalloc(sizeof(*delegation), GFP_NOFS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 if (delegation == NULL)
435 return -ENOMEM;
Trond Myklebust35156bf2018-03-20 17:03:13 -0400436 nfs4_stateid_copy(&delegation->stateid, stateid);
Trond Myklebust8c755932020-02-13 14:51:06 -0500437 refcount_set(&delegation->refcount, 1);
Trond Myklebust35156bf2018-03-20 17:03:13 -0400438 delegation->type = type;
439 delegation->pagemod_limit = pagemod_limit;
Jeff Layton1eb5d982018-01-09 08:21:17 -0500440 delegation->change_attr = inode_peek_iversion_raw(inode);
NeilBrowna52458b2018-12-03 11:30:31 +1100441 delegation->cred = get_cred(cred);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 delegation->inode = inode;
Trond Myklebustb7391f42008-12-23 15:21:52 -0500443 delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
Trond Myklebust34310432008-12-23 15:21:38 -0500444 spin_lock_init(&delegation->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445
446 spin_lock(&clp->cl_lock);
David Howells17d2c0a2010-05-01 12:37:18 -0400447 old_delegation = rcu_dereference_protected(nfsi->delegation,
Chuck Leverd3978bb2010-12-24 01:33:04 +0000448 lockdep_is_held(&clp->cl_lock));
Trond Myklebustee05f452019-10-21 13:56:59 -0400449 if (old_delegation == NULL)
450 goto add_new;
451 /* Is this an update of the existing delegation? */
452 if (nfs4_stateid_match_other(&old_delegation->stateid,
453 &delegation->stateid)) {
454 spin_lock(&old_delegation->lock);
455 nfs_update_inplace_delegation(old_delegation,
456 delegation);
457 spin_unlock(&old_delegation->lock);
458 goto out;
459 }
460 if (!test_bit(NFS_DELEGATION_REVOKED, &old_delegation->flags)) {
Trond Myklebust57bfa892008-01-25 16:38:18 -0500461 /*
462 * Deal with broken servers that hand out two
463 * delegations for the same file.
Trond Myklebust17280172012-03-11 13:11:00 -0400464 * Allow for upgrades to a WRITE delegation, but
465 * nothing else.
Trond Myklebust57bfa892008-01-25 16:38:18 -0500466 */
467 dfprintk(FILE, "%s: server %s handed out "
468 "a duplicate delegation!\n",
Harvey Harrison3110ff82008-05-02 13:42:44 -0700469 __func__, clp->cl_hostname);
Trond Myklebust17280172012-03-11 13:11:00 -0400470 if (delegation->type == old_delegation->type ||
471 !(delegation->type & FMODE_WRITE)) {
Trond Myklebust57bfa892008-01-25 16:38:18 -0500472 freeme = delegation;
473 delegation = NULL;
474 goto out;
475 }
Trond Myklebustade04642015-02-27 14:25:50 -0500476 if (test_and_set_bit(NFS_DELEGATION_RETURNING,
477 &old_delegation->flags))
478 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 }
Trond Myklebustee05f452019-10-21 13:56:59 -0400480 freeme = nfs_detach_delegation_locked(nfsi, old_delegation, clp);
481 if (freeme == NULL)
482 goto out;
483add_new:
Trond Myklebust38942ba2015-03-04 15:59:05 -0500484 list_add_tail_rcu(&delegation->super_list, &server->delegations);
Trond Myklebust57bfa892008-01-25 16:38:18 -0500485 rcu_assign_pointer(nfsi->delegation, delegation);
486 delegation = NULL;
Trond Myklebust412c77c2007-07-03 16:10:55 -0400487
Trond Myklebustd2269ea2020-01-27 09:58:18 -0500488 atomic_long_inc(&nfs_active_delegations);
489
Trond Myklebust35156bf2018-03-20 17:03:13 -0400490 trace_nfs4_set_delegation(inode, type);
Trond Myklebust412c77c2007-07-03 16:10:55 -0400491
Trond Myklebust97c2c172018-04-07 18:43:17 -0400492 spin_lock(&inode->i_lock);
493 if (NFS_I(inode)->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME))
494 NFS_I(inode)->cache_validity |= NFS_INO_REVAL_FORCED;
495 spin_unlock(&inode->i_lock);
Trond Myklebust57bfa892008-01-25 16:38:18 -0500496out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 spin_unlock(&clp->cl_lock);
Trond Myklebust603c83d2007-10-18 19:59:20 -0400498 if (delegation != NULL)
Trond Myklebustd2269ea2020-01-27 09:58:18 -0500499 __nfs_free_delegation(delegation);
Trond Myklebustee05f452019-10-21 13:56:59 -0400500 if (freeme != NULL) {
Trond Myklebust57bfa892008-01-25 16:38:18 -0500501 nfs_do_return_delegation(inode, freeme, 0);
Trond Myklebustee05f452019-10-21 13:56:59 -0400502 nfs_free_delegation(freeme);
503 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 return status;
505}
506
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507/*
508 * Basic procedure for returning a delegation to the server
509 */
Trond Myklebustd25be542013-02-05 11:43:28 -0500510static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511{
Trond Myklebustd25be542013-02-05 11:43:28 -0500512 struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
Trond Myklebust869f9df2014-11-10 18:43:56 -0500513 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
Trond Myklebustd25be542013-02-05 11:43:28 -0500515 if (delegation == NULL)
516 return 0;
517 do {
Trond Myklebust869f9df2014-11-10 18:43:56 -0500518 if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
519 break;
Trond Myklebust24311f82015-09-20 10:50:17 -0400520 err = nfs_delegation_claim_opens(inode, &delegation->stateid,
521 delegation->type);
Trond Myklebustd25be542013-02-05 11:43:28 -0500522 if (!issync || err != -EAGAIN)
523 break;
524 /*
525 * Guard against state recovery
526 */
527 err = nfs4_wait_clnt_recover(clp);
528 } while (err == 0);
529
530 if (err) {
531 nfs_abort_delegation_return(delegation, clp);
532 goto out;
533 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534
Trond Myklebustd18cc1f2009-12-03 08:10:17 -0500535 err = nfs_do_return_delegation(inode, delegation, issync);
536out:
Trond Myklebust8c755932020-02-13 14:51:06 -0500537 /* Refcount matched in nfs_start_delegation_return_locked() */
538 nfs_put_delegation(delegation);
Trond Myklebustd18cc1f2009-12-03 08:10:17 -0500539 return err;
Trond Myklebust90163022007-07-05 14:55:18 -0400540}
541
Trond Myklebustb7571442013-04-03 14:33:49 -0400542static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
543{
544 bool ret = false;
545
546 if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
547 ret = true;
Trond Myklebust0d104162020-01-27 09:58:16 -0500548 else if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) {
Trond Myklebustb7571442013-04-03 14:33:49 -0400549 struct inode *inode;
550
551 spin_lock(&delegation->lock);
552 inode = delegation->inode;
553 if (inode && list_empty(&NFS_I(inode)->open_files))
554 ret = true;
555 spin_unlock(&delegation->lock);
556 }
Trond Myklebust0d104162020-01-27 09:58:16 -0500557 if (ret)
558 clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
Trond Myklebustaf20b7b2019-10-22 13:40:47 -0400559 if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) ||
560 test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
561 ret = false;
562
Trond Myklebustb7571442013-04-03 14:33:49 -0400563 return ret;
564}
565
Trond Myklebustaf3b61b2020-02-27 08:29:02 -0500566static int nfs_server_return_marked_delegations(struct nfs_server *server,
567 void __always_unused *data)
568{
569 struct nfs_delegation *delegation;
570 struct nfs_delegation *prev;
571 struct inode *inode;
572 struct inode *place_holder = NULL;
573 struct nfs_delegation *place_holder_deleg = NULL;
574 int err = 0;
575
576restart:
577 /*
578 * To avoid quadratic looping we hold a reference
579 * to an inode place_holder. Each time we restart, we
580 * list delegation in the server from the delegations
581 * of that inode.
582 * prev is an RCU-protected pointer to a delegation which
583 * wasn't marked for return and might be a good choice for
584 * the next place_holder.
585 */
586 prev = NULL;
587 delegation = NULL;
588 rcu_read_lock();
589 if (place_holder)
590 delegation = rcu_dereference(NFS_I(place_holder)->delegation);
591 if (!delegation || delegation != place_holder_deleg)
592 delegation = list_entry_rcu(server->delegations.next,
593 struct nfs_delegation, super_list);
594 list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) {
595 struct inode *to_put = NULL;
596
597 if (test_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags))
598 continue;
599 if (!nfs_delegation_need_return(delegation)) {
600 if (nfs4_is_valid_delegation(delegation, 0))
601 prev = delegation;
602 continue;
603 }
604
605 if (prev) {
606 struct inode *tmp = nfs_delegation_grab_inode(prev);
607 if (tmp) {
608 to_put = place_holder;
609 place_holder = tmp;
610 place_holder_deleg = prev;
611 }
612 }
613
614 inode = nfs_delegation_grab_inode(delegation);
615 if (inode == NULL) {
616 rcu_read_unlock();
617 iput(to_put);
618 goto restart;
619 }
620 delegation = nfs_start_delegation_return_locked(NFS_I(inode));
621 rcu_read_unlock();
622
623 iput(to_put);
624
625 err = nfs_end_delegation_return(inode, delegation, 0);
626 iput(inode);
627 cond_resched();
628 if (!err)
629 goto restart;
630 set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
631 goto out;
632 }
633 rcu_read_unlock();
634out:
635 iput(place_holder);
636 return err;
637}
638
Chuck Leverd3978bb2010-12-24 01:33:04 +0000639/**
640 * nfs_client_return_marked_delegations - return previously marked delegations
641 * @clp: nfs_client to process
642 *
Trond Myklebustdc327ed2012-05-06 19:46:30 -0400643 * Note that this function is designed to be called by the state
644 * manager thread. For this reason, it cannot flush the dirty data,
645 * since that could deadlock in case of a state recovery error.
646 *
Chuck Leverd3978bb2010-12-24 01:33:04 +0000647 * Returns zero on success, or a negative errno value.
Trond Myklebust515d8612008-12-23 15:21:46 -0500648 */
Trond Myklebustd18cc1f2009-12-03 08:10:17 -0500649int nfs_client_return_marked_delegations(struct nfs_client *clp)
Trond Myklebust515d8612008-12-23 15:21:46 -0500650{
Trond Myklebustaf3b61b2020-02-27 08:29:02 -0500651 return nfs_client_for_each_server(clp,
652 nfs_server_return_marked_delegations, NULL);
Trond Myklebust515d8612008-12-23 15:21:46 -0500653}
654
Chuck Leverd3978bb2010-12-24 01:33:04 +0000655/**
Trond Myklebustb47e0e42019-10-21 14:04:00 -0400656 * nfs_inode_evict_delegation - return delegation, don't reclaim opens
Chuck Leverd3978bb2010-12-24 01:33:04 +0000657 * @inode: inode to process
658 *
659 * Does not protect against delegation reclaims, therefore really only safe
Trond Myklebustb47e0e42019-10-21 14:04:00 -0400660 * to be called from nfs4_clear_inode(). Guaranteed to always free
661 * the delegation structure.
Trond Myklebuste6f81072008-01-24 18:14:34 -0500662 */
Trond Myklebustb47e0e42019-10-21 14:04:00 -0400663void nfs_inode_evict_delegation(struct inode *inode)
Trond Myklebuste6f81072008-01-24 18:14:34 -0500664{
Trond Myklebuste6f81072008-01-24 18:14:34 -0500665 struct nfs_delegation *delegation;
666
Trond Myklebustd25be542013-02-05 11:43:28 -0500667 delegation = nfs_inode_detach_delegation(inode);
Trond Myklebustb47e0e42019-10-21 14:04:00 -0400668 if (delegation != NULL) {
Trond Myklebustf885ea62020-01-27 09:58:15 -0500669 set_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
Trond Myklebustb47e0e42019-10-21 14:04:00 -0400670 set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags);
Trond Myklebust5fcdfac2015-03-25 13:19:42 -0400671 nfs_do_return_delegation(inode, delegation, 1);
Trond Myklebustee05f452019-10-21 13:56:59 -0400672 nfs_free_delegation(delegation);
Trond Myklebustb47e0e42019-10-21 14:04:00 -0400673 }
Trond Myklebuste6f81072008-01-24 18:14:34 -0500674}
675
Chuck Leverd3978bb2010-12-24 01:33:04 +0000676/**
677 * nfs_inode_return_delegation - synchronously return a delegation
678 * @inode: inode to process
679 *
Trond Myklebustc57d1bc2012-05-06 19:34:17 -0400680 * This routine will always flush any dirty data to disk on the
681 * assumption that if we need to return the delegation, then
682 * we should stop caching.
683 *
Chuck Leverd3978bb2010-12-24 01:33:04 +0000684 * Returns zero on success, or a negative errno value.
685 */
Bryan Schumaker57ec14c2012-06-20 15:53:44 -0400686int nfs4_inode_return_delegation(struct inode *inode)
Trond Myklebust90163022007-07-05 14:55:18 -0400687{
Trond Myklebust90163022007-07-05 14:55:18 -0400688 struct nfs_inode *nfsi = NFS_I(inode);
689 struct nfs_delegation *delegation;
690 int err = 0;
691
Trond Myklebustc57d1bc2012-05-06 19:34:17 -0400692 nfs_wb_all(inode);
Trond Myklebustd25be542013-02-05 11:43:28 -0500693 delegation = nfs_start_delegation_return(nfsi);
694 if (delegation != NULL)
695 err = nfs_end_delegation_return(inode, delegation, 1);
Trond Myklebust90163022007-07-05 14:55:18 -0400696 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697}
698
Trond Myklebustc01d3642018-03-20 16:43:20 -0400699/**
Trond Myklebustb7b7dac2020-01-27 09:58:17 -0500700 * nfs_inode_return_delegation_on_close - asynchronously return a delegation
701 * @inode: inode to process
702 *
703 * This routine is called on file close in order to determine if the
704 * inode delegation needs to be returned immediately.
705 */
706void nfs4_inode_return_delegation_on_close(struct inode *inode)
707{
708 struct nfs_delegation *delegation;
709 struct nfs_delegation *ret = NULL;
710
711 if (!inode)
712 return;
713 rcu_read_lock();
714 delegation = nfs4_get_valid_delegation(inode);
715 if (!delegation)
716 goto out;
Trond Myklebust10717f42020-01-27 09:58:19 -0500717 if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) ||
718 atomic_long_read(&nfs_active_delegations) >= nfs_delegation_watermark) {
Trond Myklebustb7b7dac2020-01-27 09:58:17 -0500719 spin_lock(&delegation->lock);
720 if (delegation->inode &&
721 list_empty(&NFS_I(inode)->open_files) &&
722 !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
723 clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
Trond Myklebust8c755932020-02-13 14:51:06 -0500724 /* Refcount matched in nfs_end_delegation_return() */
725 ret = nfs_get_delegation(delegation);
Trond Myklebustb7b7dac2020-01-27 09:58:17 -0500726 }
727 spin_unlock(&delegation->lock);
Trond Myklebustefeda802020-02-05 09:01:54 -0500728 if (ret)
729 nfs_clear_verifier_delegated(inode);
Trond Myklebustb7b7dac2020-01-27 09:58:17 -0500730 }
731out:
732 rcu_read_unlock();
733 nfs_end_delegation_return(inode, ret, 0);
734}
735
736/**
Trond Myklebustc01d3642018-03-20 16:43:20 -0400737 * nfs4_inode_make_writeable
738 * @inode: pointer to inode
739 *
740 * Make the inode writeable by returning the delegation if necessary
741 *
742 * Returns zero on success, or a negative errno value.
743 */
744int nfs4_inode_make_writeable(struct inode *inode)
745{
Trond Myklebust3887ce12019-10-27 13:48:18 -0400746 struct nfs_delegation *delegation;
747
748 rcu_read_lock();
749 delegation = nfs4_get_valid_delegation(inode);
750 if (delegation == NULL ||
751 (nfs4_has_session(NFS_SERVER(inode)->nfs_client) &&
752 (delegation->type & FMODE_WRITE))) {
753 rcu_read_unlock();
754 return 0;
755 }
756 rcu_read_unlock();
757 return nfs4_inode_return_delegation(inode);
Trond Myklebustc01d3642018-03-20 16:43:20 -0400758}
759
Trond Myklebustb7571442013-04-03 14:33:49 -0400760static void nfs_mark_return_if_closed_delegation(struct nfs_server *server,
761 struct nfs_delegation *delegation)
762{
763 set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
764 set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
765}
766
Trond Myklebusted1e62112011-07-25 15:37:29 -0400767static void nfs_mark_return_delegation(struct nfs_server *server,
768 struct nfs_delegation *delegation)
Trond Myklebust6411bd42008-12-23 15:21:51 -0500769{
770 set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
Trond Myklebusted1e62112011-07-25 15:37:29 -0400771 set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
Trond Myklebust6411bd42008-12-23 15:21:51 -0500772}
773
Trond Myklebust5c31e232013-04-03 19:04:58 -0400774static bool nfs_server_mark_return_all_delegations(struct nfs_server *server)
775{
776 struct nfs_delegation *delegation;
777 bool ret = false;
778
779 list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
780 nfs_mark_return_delegation(server, delegation);
781 ret = true;
782 }
783 return ret;
784}
785
Trond Myklebustb02ba0b2013-04-03 19:23:58 -0400786static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
787{
788 struct nfs_server *server;
789
790 rcu_read_lock();
791 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
792 nfs_server_mark_return_all_delegations(server);
793 rcu_read_unlock();
794}
795
796static void nfs_delegation_run_state_manager(struct nfs_client *clp)
797{
798 if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
799 nfs4_schedule_state_manager(clp);
800}
801
802/**
803 * nfs_expire_all_delegations
804 * @clp: client to process
805 *
806 */
807void nfs_expire_all_delegations(struct nfs_client *clp)
808{
809 nfs_client_mark_return_all_delegations(clp);
810 nfs_delegation_run_state_manager(clp);
811}
812
Chuck Leverd3978bb2010-12-24 01:33:04 +0000813/**
814 * nfs_super_return_all_delegations - return delegations for one superblock
Trond Myklebust302fad72019-02-18 13:32:38 -0500815 * @server: pointer to nfs_server to process
Chuck Leverd3978bb2010-12-24 01:33:04 +0000816 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 */
Bryan Schumakereeebf912012-06-20 15:53:41 -0400818void nfs_server_return_all_delegations(struct nfs_server *server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819{
Chuck Leverd3978bb2010-12-24 01:33:04 +0000820 struct nfs_client *clp = server->nfs_client;
Trond Myklebust5c31e232013-04-03 19:04:58 -0400821 bool need_wait;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822
823 if (clp == NULL)
824 return;
Chuck Leverd3978bb2010-12-24 01:33:04 +0000825
Trond Myklebust8383e462007-07-06 15:12:04 -0400826 rcu_read_lock();
Trond Myklebust5c31e232013-04-03 19:04:58 -0400827 need_wait = nfs_server_mark_return_all_delegations(server);
Trond Myklebust8383e462007-07-06 15:12:04 -0400828 rcu_read_unlock();
Chuck Leverd3978bb2010-12-24 01:33:04 +0000829
Trond Myklebust5c31e232013-04-03 19:04:58 -0400830 if (need_wait) {
Trond Myklebustd18cc1f2009-12-03 08:10:17 -0500831 nfs4_schedule_state_manager(clp);
Trond Myklebust5c31e232013-04-03 19:04:58 -0400832 nfs4_wait_clnt_recover(clp);
833 }
Trond Myklebust515d8612008-12-23 15:21:46 -0500834}
835
Trond Myklebust826e0012013-04-03 19:27:52 -0400836static void nfs_mark_return_unused_delegation_types(struct nfs_server *server,
Chuck Leverd3978bb2010-12-24 01:33:04 +0000837 fmode_t flags)
Trond Myklebust515d8612008-12-23 15:21:46 -0500838{
839 struct nfs_delegation *delegation;
840
Chuck Leverd3978bb2010-12-24 01:33:04 +0000841 list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
Alexandros Batsakisc79571a2009-12-05 13:20:52 -0500842 if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
843 continue;
844 if (delegation->type & flags)
Trond Myklebust826e0012013-04-03 19:27:52 -0400845 nfs_mark_return_if_closed_delegation(server, delegation);
Trond Myklebust707fb4b2008-12-23 15:21:47 -0500846 }
Chuck Leverd3978bb2010-12-24 01:33:04 +0000847}
848
Trond Myklebust826e0012013-04-03 19:27:52 -0400849static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp,
Chuck Leverd3978bb2010-12-24 01:33:04 +0000850 fmode_t flags)
851{
852 struct nfs_server *server;
853
854 rcu_read_lock();
855 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
Trond Myklebust826e0012013-04-03 19:27:52 -0400856 nfs_mark_return_unused_delegation_types(server, flags);
Trond Myklebust515d8612008-12-23 15:21:46 -0500857 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858}
859
Trond Myklebustee05f452019-10-21 13:56:59 -0400860static void nfs_revoke_delegation(struct inode *inode,
Trond Myklebust41020b62016-09-22 13:38:58 -0400861 const nfs4_stateid *stateid)
862{
863 struct nfs_delegation *delegation;
Trond Myklebust7f048832016-09-22 13:39:14 -0400864 nfs4_stateid tmp;
Trond Myklebust41020b62016-09-22 13:38:58 -0400865 bool ret = false;
866
867 rcu_read_lock();
868 delegation = rcu_dereference(NFS_I(inode)->delegation);
869 if (delegation == NULL)
870 goto out;
Trond Myklebust7f048832016-09-22 13:39:14 -0400871 if (stateid == NULL) {
872 nfs4_stateid_copy(&tmp, &delegation->stateid);
873 stateid = &tmp;
Trond Myklebustf2d47b52019-10-21 14:15:32 -0400874 } else {
875 if (!nfs4_stateid_match_other(stateid, &delegation->stateid))
876 goto out;
877 spin_lock(&delegation->lock);
878 if (stateid->seqid) {
879 if (nfs4_stateid_is_newer(&delegation->stateid, stateid)) {
880 spin_unlock(&delegation->lock);
881 goto out;
882 }
883 delegation->stateid.seqid = stateid->seqid;
884 }
885 spin_unlock(&delegation->lock);
886 }
Trond Myklebustd2269ea2020-01-27 09:58:18 -0500887 nfs_mark_delegation_revoked(delegation);
Trond Myklebust41020b62016-09-22 13:38:58 -0400888 ret = true;
889out:
890 rcu_read_unlock();
Trond Myklebust7f048832016-09-22 13:39:14 -0400891 if (ret)
892 nfs_inode_find_state_and_recover(inode, stateid);
Trond Myklebust41020b62016-09-22 13:38:58 -0400893}
894
895void nfs_remove_bad_delegation(struct inode *inode,
896 const nfs4_stateid *stateid)
Trond Myklebusta1d0b5e2012-03-05 19:56:44 -0500897{
Trond Myklebustee05f452019-10-21 13:56:59 -0400898 nfs_revoke_delegation(inode, stateid);
Trond Myklebusta1d0b5e2012-03-05 19:56:44 -0500899}
Andy Adamson9cb81962012-03-07 10:49:41 -0500900EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
Trond Myklebusta1d0b5e2012-03-05 19:56:44 -0500901
Trond Myklebustd51f91d2019-10-21 14:22:14 -0400902void nfs_delegation_mark_returned(struct inode *inode,
903 const nfs4_stateid *stateid)
904{
905 struct nfs_delegation *delegation;
906
907 if (!inode)
908 return;
909
910 rcu_read_lock();
911 delegation = rcu_dereference(NFS_I(inode)->delegation);
912 if (!delegation)
913 goto out_rcu_unlock;
914
915 spin_lock(&delegation->lock);
916 if (!nfs4_stateid_match_other(stateid, &delegation->stateid))
917 goto out_spin_unlock;
918 if (stateid->seqid) {
919 /* If delegation->stateid is newer, dont mark as returned */
920 if (nfs4_stateid_is_newer(&delegation->stateid, stateid))
921 goto out_clear_returning;
922 if (delegation->stateid.seqid != stateid->seqid)
923 delegation->stateid.seqid = stateid->seqid;
924 }
925
Trond Myklebustd2269ea2020-01-27 09:58:18 -0500926 nfs_mark_delegation_revoked(delegation);
Trond Myklebustd51f91d2019-10-21 14:22:14 -0400927
928out_clear_returning:
929 clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
930out_spin_unlock:
931 spin_unlock(&delegation->lock);
932out_rcu_unlock:
933 rcu_read_unlock();
934
935 nfs_inode_find_state_and_recover(inode, stateid);
936}
937
Chuck Leverd3978bb2010-12-24 01:33:04 +0000938/**
Trond Myklebust826e0012013-04-03 19:27:52 -0400939 * nfs_expire_unused_delegation_types
Chuck Leverd3978bb2010-12-24 01:33:04 +0000940 * @clp: client to process
941 * @flags: delegation types to expire
942 *
943 */
Trond Myklebust826e0012013-04-03 19:27:52 -0400944void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags)
Alexandros Batsakisc79571a2009-12-05 13:20:52 -0500945{
Trond Myklebust826e0012013-04-03 19:27:52 -0400946 nfs_client_mark_return_unused_delegation_types(clp, flags);
Alexandros Batsakisc79571a2009-12-05 13:20:52 -0500947 nfs_delegation_run_state_manager(clp);
948}
949
Chuck Leverd3978bb2010-12-24 01:33:04 +0000950static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
Trond Myklebustb7391f42008-12-23 15:21:52 -0500951{
952 struct nfs_delegation *delegation;
953
Chuck Leverd3978bb2010-12-24 01:33:04 +0000954 list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
Trond Myklebustb7391f42008-12-23 15:21:52 -0500955 if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
956 continue;
Trond Myklebustb7571442013-04-03 14:33:49 -0400957 nfs_mark_return_if_closed_delegation(server, delegation);
Trond Myklebustb7391f42008-12-23 15:21:52 -0500958 }
Trond Myklebustb7391f42008-12-23 15:21:52 -0500959}
960
Chuck Leverd3978bb2010-12-24 01:33:04 +0000961/**
962 * nfs_expire_unreferenced_delegations - Eliminate unused delegations
963 * @clp: nfs_client to process
964 *
965 */
Trond Myklebustb7391f42008-12-23 15:21:52 -0500966void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
967{
Chuck Leverd3978bb2010-12-24 01:33:04 +0000968 struct nfs_server *server;
969
970 rcu_read_lock();
971 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
972 nfs_mark_return_unreferenced_delegations(server);
973 rcu_read_unlock();
974
Trond Myklebustb7391f42008-12-23 15:21:52 -0500975 nfs_delegation_run_state_manager(clp);
976}
977
Chuck Leverd3978bb2010-12-24 01:33:04 +0000978/**
979 * nfs_async_inode_return_delegation - asynchronously return a delegation
980 * @inode: inode to process
Trond Myklebust8e663f02012-03-04 18:13:56 -0500981 * @stateid: state ID information
Chuck Leverd3978bb2010-12-24 01:33:04 +0000982 *
983 * Returns zero on success, or a negative errno value.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 */
Chuck Leverd3978bb2010-12-24 01:33:04 +0000985int nfs_async_inode_return_delegation(struct inode *inode,
986 const nfs4_stateid *stateid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987{
Trond Myklebusted1e62112011-07-25 15:37:29 -0400988 struct nfs_server *server = NFS_SERVER(inode);
989 struct nfs_client *clp = server->nfs_client;
Trond Myklebust6411bd42008-12-23 15:21:51 -0500990 struct nfs_delegation *delegation;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991
Trond Myklebust6411bd42008-12-23 15:21:51 -0500992 rcu_read_lock();
Trond Myklebust457a5042019-10-22 08:46:06 -0400993 delegation = nfs4_get_valid_delegation(inode);
Trond Myklebust755a48a2014-03-02 22:03:12 -0500994 if (delegation == NULL)
995 goto out_enoent;
Trond Myklebust4816fda2015-09-20 14:58:42 -0400996 if (stateid != NULL &&
997 !clp->cl_mvops->match_stateid(&delegation->stateid, stateid))
Trond Myklebust755a48a2014-03-02 22:03:12 -0500998 goto out_enoent;
Trond Myklebusted1e62112011-07-25 15:37:29 -0400999 nfs_mark_return_delegation(server, delegation);
Trond Myklebust6411bd42008-12-23 15:21:51 -05001000 rcu_read_unlock();
Chuck Leverd3978bb2010-12-24 01:33:04 +00001001
Trond Myklebust6411bd42008-12-23 15:21:51 -05001002 nfs_delegation_run_state_manager(clp);
1003 return 0;
Trond Myklebust755a48a2014-03-02 22:03:12 -05001004out_enoent:
1005 rcu_read_unlock();
1006 return -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007}
1008
Chuck Leverd3978bb2010-12-24 01:33:04 +00001009static struct inode *
1010nfs_delegation_find_inode_server(struct nfs_server *server,
1011 const struct nfs_fh *fhandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012{
1013 struct nfs_delegation *delegation;
Trond Myklebuste39d8a12018-11-13 16:37:54 -05001014 struct inode *freeme, *res = NULL;
Chuck Leverd3978bb2010-12-24 01:33:04 +00001015
1016 list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
Trond Myklebust86e89482008-12-23 15:21:39 -05001017 spin_lock(&delegation->lock);
1018 if (delegation->inode != NULL &&
Trond Myklebust457a5042019-10-22 08:46:06 -04001019 !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) &&
Trond Myklebust86e89482008-12-23 15:21:39 -05001020 nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
Trond Myklebuste39d8a12018-11-13 16:37:54 -05001021 freeme = igrab(delegation->inode);
1022 if (freeme && nfs_sb_active(freeme->i_sb))
1023 res = freeme;
Trond Myklebust6c342652018-06-07 14:22:00 -04001024 spin_unlock(&delegation->lock);
1025 if (res != NULL)
1026 return res;
Trond Myklebuste39d8a12018-11-13 16:37:54 -05001027 if (freeme) {
1028 rcu_read_unlock();
1029 iput(freeme);
1030 rcu_read_lock();
1031 }
Trond Myklebust6c342652018-06-07 14:22:00 -04001032 return ERR_PTR(-EAGAIN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 }
Trond Myklebust86e89482008-12-23 15:21:39 -05001034 spin_unlock(&delegation->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 }
Trond Myklebust6c342652018-06-07 14:22:00 -04001036 return ERR_PTR(-ENOENT);
Chuck Leverd3978bb2010-12-24 01:33:04 +00001037}
1038
1039/**
1040 * nfs_delegation_find_inode - retrieve the inode associated with a delegation
1041 * @clp: client state handle
1042 * @fhandle: filehandle from a delegation recall
1043 *
1044 * Returns pointer to inode matching "fhandle," or NULL if a matching inode
1045 * cannot be found.
1046 */
1047struct inode *nfs_delegation_find_inode(struct nfs_client *clp,
1048 const struct nfs_fh *fhandle)
1049{
1050 struct nfs_server *server;
Trond Myklebust6c342652018-06-07 14:22:00 -04001051 struct inode *res;
Chuck Leverd3978bb2010-12-24 01:33:04 +00001052
1053 rcu_read_lock();
1054 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
1055 res = nfs_delegation_find_inode_server(server, fhandle);
Anna Schumakerd5681f52018-06-14 09:39:17 -04001056 if (res != ERR_PTR(-ENOENT)) {
1057 rcu_read_unlock();
Trond Myklebust6c342652018-06-07 14:22:00 -04001058 return res;
Anna Schumakerd5681f52018-06-14 09:39:17 -04001059 }
Chuck Leverd3978bb2010-12-24 01:33:04 +00001060 }
Trond Myklebust8383e462007-07-06 15:12:04 -04001061 rcu_read_unlock();
Trond Myklebust6c342652018-06-07 14:22:00 -04001062 return ERR_PTR(-ENOENT);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063}
1064
Chuck Leverd3978bb2010-12-24 01:33:04 +00001065static void nfs_delegation_mark_reclaim_server(struct nfs_server *server)
1066{
1067 struct nfs_delegation *delegation;
1068
Trond Myklebust45870d62016-09-22 13:38:59 -04001069 list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
1070 /*
1071 * If the delegation may have been admin revoked, then we
1072 * cannot reclaim it.
1073 */
1074 if (test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags))
1075 continue;
Chuck Leverd3978bb2010-12-24 01:33:04 +00001076 set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
Trond Myklebust45870d62016-09-22 13:38:59 -04001077 }
Chuck Leverd3978bb2010-12-24 01:33:04 +00001078}
1079
1080/**
1081 * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed
1082 * @clp: nfs_client to process
1083 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 */
David Howellsadfa6f92006-08-22 20:06:08 -04001085void nfs_delegation_mark_reclaim(struct nfs_client *clp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086{
Chuck Leverd3978bb2010-12-24 01:33:04 +00001087 struct nfs_server *server;
1088
Trond Myklebust8383e462007-07-06 15:12:04 -04001089 rcu_read_lock();
Chuck Leverd3978bb2010-12-24 01:33:04 +00001090 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
1091 nfs_delegation_mark_reclaim_server(server);
Trond Myklebust8383e462007-07-06 15:12:04 -04001092 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093}
1094
Trond Myklebust1bba38b2020-02-27 09:08:25 -05001095static int nfs_server_reap_unclaimed_delegations(struct nfs_server *server,
1096 void __always_unused *data)
1097{
1098 struct nfs_delegation *delegation;
1099 struct inode *inode;
1100restart:
1101 rcu_read_lock();
1102restart_locked:
1103 list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
1104 if (test_bit(NFS_DELEGATION_INODE_FREEING,
1105 &delegation->flags) ||
1106 test_bit(NFS_DELEGATION_RETURNING,
1107 &delegation->flags) ||
1108 test_bit(NFS_DELEGATION_NEED_RECLAIM,
1109 &delegation->flags) == 0)
1110 continue;
1111 inode = nfs_delegation_grab_inode(delegation);
1112 if (inode == NULL)
1113 goto restart_locked;
1114 delegation = nfs_start_delegation_return_locked(NFS_I(inode));
1115 rcu_read_unlock();
1116 if (delegation != NULL) {
1117 if (nfs_detach_delegation(NFS_I(inode), delegation,
1118 server) != NULL)
1119 nfs_free_delegation(delegation);
1120 /* Match nfs_start_delegation_return_locked */
1121 nfs_put_delegation(delegation);
1122 }
1123 iput(inode);
1124 cond_resched();
1125 goto restart;
1126 }
1127 rcu_read_unlock();
1128 return 0;
1129}
1130
Chuck Leverd3978bb2010-12-24 01:33:04 +00001131/**
1132 * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done
1133 * @clp: nfs_client to process
1134 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 */
David Howellsadfa6f92006-08-22 20:06:08 -04001136void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137{
Trond Myklebust1bba38b2020-02-27 09:08:25 -05001138 nfs_client_for_each_server(clp, nfs_server_reap_unclaimed_delegations,
1139 NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140}
Trond Myklebust3e4f6292006-03-20 13:44:46 -05001141
Trond Myklebustbb3d1a32016-09-22 13:39:00 -04001142static inline bool nfs4_server_rebooted(const struct nfs_client *clp)
1143{
1144 return (clp->cl_state & (BIT(NFS4CLNT_CHECK_LEASE) |
1145 BIT(NFS4CLNT_LEASE_EXPIRED) |
1146 BIT(NFS4CLNT_SESSION_RESET))) != 0;
1147}
1148
Trond Myklebust45870d62016-09-22 13:38:59 -04001149static void nfs_mark_test_expired_delegation(struct nfs_server *server,
1150 struct nfs_delegation *delegation)
1151{
Trond Myklebust059b43e2016-09-22 13:39:06 -04001152 if (delegation->stateid.type == NFS4_INVALID_STATEID_TYPE)
1153 return;
Trond Myklebust45870d62016-09-22 13:38:59 -04001154 clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
1155 set_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
1156 set_bit(NFS4CLNT_DELEGATION_EXPIRED, &server->nfs_client->cl_state);
1157}
1158
Trond Myklebustbb3d1a32016-09-22 13:39:00 -04001159static void nfs_inode_mark_test_expired_delegation(struct nfs_server *server,
1160 struct inode *inode)
1161{
1162 struct nfs_delegation *delegation;
1163
1164 rcu_read_lock();
1165 delegation = rcu_dereference(NFS_I(inode)->delegation);
1166 if (delegation)
1167 nfs_mark_test_expired_delegation(server, delegation);
1168 rcu_read_unlock();
1169
1170}
1171
Trond Myklebust45870d62016-09-22 13:38:59 -04001172static void nfs_delegation_mark_test_expired_server(struct nfs_server *server)
1173{
1174 struct nfs_delegation *delegation;
1175
1176 list_for_each_entry_rcu(delegation, &server->delegations, super_list)
1177 nfs_mark_test_expired_delegation(server, delegation);
1178}
1179
1180/**
1181 * nfs_mark_test_expired_all_delegations - mark all delegations for testing
1182 * @clp: nfs_client to process
1183 *
1184 * Iterates through all the delegations associated with this server and
1185 * marks them as needing to be checked for validity.
1186 */
1187void nfs_mark_test_expired_all_delegations(struct nfs_client *clp)
1188{
1189 struct nfs_server *server;
1190
1191 rcu_read_lock();
1192 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
1193 nfs_delegation_mark_test_expired_server(server);
1194 rcu_read_unlock();
1195}
1196
1197/**
Scott Mayhew8ca017c2019-05-06 11:59:05 -04001198 * nfs_test_expired_all_delegations - test all delegations for a client
1199 * @clp: nfs_client to process
1200 *
1201 * Helper for handling "recallable state revoked" status from server.
1202 */
1203void nfs_test_expired_all_delegations(struct nfs_client *clp)
1204{
1205 nfs_mark_test_expired_all_delegations(clp);
1206 nfs4_schedule_state_manager(clp);
1207}
1208
Trond Myklebustad114082019-07-26 14:40:53 +01001209static void
1210nfs_delegation_test_free_expired(struct inode *inode,
1211 nfs4_stateid *stateid,
1212 const struct cred *cred)
1213{
1214 struct nfs_server *server = NFS_SERVER(inode);
1215 const struct nfs4_minor_version_ops *ops = server->nfs_client->cl_mvops;
1216 int status;
1217
1218 if (!cred)
1219 return;
1220 status = ops->test_and_free_expired(server, stateid, cred);
1221 if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID)
1222 nfs_remove_bad_delegation(inode, stateid);
1223}
1224
Scott Mayhew8ca017c2019-05-06 11:59:05 -04001225/**
Trond Myklebust45870d62016-09-22 13:38:59 -04001226 * nfs_reap_expired_delegations - reap expired delegations
1227 * @clp: nfs_client to process
1228 *
1229 * Iterates through all the delegations associated with this server and
1230 * checks if they have may have been revoked. This function is usually
1231 * expected to be called in cases where the server may have lost its
1232 * lease.
1233 */
1234void nfs_reap_expired_delegations(struct nfs_client *clp)
1235{
Trond Myklebust45870d62016-09-22 13:38:59 -04001236 struct nfs_delegation *delegation;
1237 struct nfs_server *server;
1238 struct inode *inode;
NeilBrowna52458b2018-12-03 11:30:31 +11001239 const struct cred *cred;
Trond Myklebust45870d62016-09-22 13:38:59 -04001240 nfs4_stateid stateid;
1241
1242restart:
1243 rcu_read_lock();
1244 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
1245 list_for_each_entry_rcu(delegation, &server->delegations,
1246 super_list) {
Trond Myklebust6f9449b2019-02-21 14:51:25 -05001247 if (test_bit(NFS_DELEGATION_INODE_FREEING,
1248 &delegation->flags) ||
1249 test_bit(NFS_DELEGATION_RETURNING,
1250 &delegation->flags) ||
1251 test_bit(NFS_DELEGATION_TEST_EXPIRED,
Trond Myklebust45870d62016-09-22 13:38:59 -04001252 &delegation->flags) == 0)
1253 continue;
1254 if (!nfs_sb_active(server->super))
NeilBrownf38934912018-05-31 15:23:22 +10001255 break; /* continue in outer loop */
Trond Myklebust45870d62016-09-22 13:38:59 -04001256 inode = nfs_delegation_grab_inode(delegation);
1257 if (inode == NULL) {
1258 rcu_read_unlock();
1259 nfs_sb_deactive(server->super);
1260 goto restart;
1261 }
NeilBrowna52458b2018-12-03 11:30:31 +11001262 cred = get_cred_rcu(delegation->cred);
Trond Myklebust45870d62016-09-22 13:38:59 -04001263 nfs4_stateid_copy(&stateid, &delegation->stateid);
1264 clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
1265 rcu_read_unlock();
Trond Myklebustad114082019-07-26 14:40:53 +01001266 nfs_delegation_test_free_expired(inode, &stateid, cred);
NeilBrowna52458b2018-12-03 11:30:31 +11001267 put_cred(cred);
Trond Myklebustbb3d1a32016-09-22 13:39:00 -04001268 if (nfs4_server_rebooted(clp)) {
1269 nfs_inode_mark_test_expired_delegation(server,inode);
1270 iput(inode);
1271 nfs_sb_deactive(server->super);
1272 return;
1273 }
Trond Myklebust45870d62016-09-22 13:38:59 -04001274 iput(inode);
1275 nfs_sb_deactive(server->super);
NeilBrown3ca951b2018-04-30 14:31:30 +10001276 cond_resched();
Trond Myklebust45870d62016-09-22 13:38:59 -04001277 goto restart;
1278 }
1279 }
1280 rcu_read_unlock();
1281}
1282
Trond Myklebust6c2d8f82016-09-22 13:39:07 -04001283void nfs_inode_find_delegation_state_and_recover(struct inode *inode,
1284 const nfs4_stateid *stateid)
1285{
1286 struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
1287 struct nfs_delegation *delegation;
1288 bool found = false;
1289
1290 rcu_read_lock();
1291 delegation = rcu_dereference(NFS_I(inode)->delegation);
1292 if (delegation &&
Trond Myklebust42c304c2019-10-26 10:16:15 -04001293 nfs4_stateid_match_or_older(&delegation->stateid, stateid) &&
1294 !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
Trond Myklebust6c2d8f82016-09-22 13:39:07 -04001295 nfs_mark_test_expired_delegation(NFS_SERVER(inode), delegation);
1296 found = true;
1297 }
1298 rcu_read_unlock();
1299 if (found)
1300 nfs4_schedule_state_manager(clp);
1301}
1302
Chuck Leverd3978bb2010-12-24 01:33:04 +00001303/**
1304 * nfs_delegations_present - check for existence of delegations
1305 * @clp: client state handle
1306 *
1307 * Returns one if there are any nfs_delegation structures attached
1308 * to this nfs_client.
1309 */
1310int nfs_delegations_present(struct nfs_client *clp)
1311{
1312 struct nfs_server *server;
1313 int ret = 0;
1314
1315 rcu_read_lock();
1316 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
1317 if (!list_empty(&server->delegations)) {
1318 ret = 1;
1319 break;
1320 }
1321 rcu_read_unlock();
1322 return ret;
1323}
1324
1325/**
Trond Myklebust12f275c2017-11-06 15:28:05 -05001326 * nfs4_refresh_delegation_stateid - Update delegation stateid seqid
1327 * @dst: stateid to refresh
1328 * @inode: inode to check
1329 *
1330 * Returns "true" and updates "dst->seqid" * if inode had a delegation
1331 * that matches our delegation stateid. Otherwise "false" is returned.
1332 */
1333bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode)
1334{
1335 struct nfs_delegation *delegation;
1336 bool ret = false;
1337 if (!inode)
1338 goto out;
1339
1340 rcu_read_lock();
1341 delegation = rcu_dereference(NFS_I(inode)->delegation);
1342 if (delegation != NULL &&
Trond Myklebustb5756202019-10-22 13:34:06 -04001343 nfs4_stateid_match_other(dst, &delegation->stateid) &&
Trond Myklebust246afc0a2019-10-24 18:00:35 -04001344 nfs4_stateid_is_newer(&delegation->stateid, dst) &&
Trond Myklebustb5756202019-10-22 13:34:06 -04001345 !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
Trond Myklebust12f275c2017-11-06 15:28:05 -05001346 dst->seqid = delegation->stateid.seqid;
Trond Myklebust79cc5542019-10-31 18:40:33 -04001347 ret = true;
Trond Myklebust12f275c2017-11-06 15:28:05 -05001348 }
1349 rcu_read_unlock();
1350out:
1351 return ret;
1352}
1353
1354/**
Chuck Leverd3978bb2010-12-24 01:33:04 +00001355 * nfs4_copy_delegation_stateid - Copy inode's state ID information
Chuck Leverd3978bb2010-12-24 01:33:04 +00001356 * @inode: inode to check
Trond Myklebust0032a7a2012-03-08 17:16:12 -05001357 * @flags: delegation type requirement
Trond Myklebustabf4e132016-05-16 17:42:44 -04001358 * @dst: stateid data structure to fill in
1359 * @cred: optional argument to retrieve credential
Chuck Leverd3978bb2010-12-24 01:33:04 +00001360 *
Trond Myklebust0032a7a2012-03-08 17:16:12 -05001361 * Returns "true" and fills in "dst->data" * if inode had a delegation,
1362 * otherwise "false" is returned.
Chuck Leverd3978bb2010-12-24 01:33:04 +00001363 */
Trond Myklebustabf4e132016-05-16 17:42:44 -04001364bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags,
NeilBrowna52458b2018-12-03 11:30:31 +11001365 nfs4_stateid *dst, const struct cred **cred)
Trond Myklebust3e4f6292006-03-20 13:44:46 -05001366{
Trond Myklebust3e4f6292006-03-20 13:44:46 -05001367 struct nfs_inode *nfsi = NFS_I(inode);
1368 struct nfs_delegation *delegation;
Trond Myklebust0032a7a2012-03-08 17:16:12 -05001369 bool ret;
Trond Myklebust3e4f6292006-03-20 13:44:46 -05001370
Trond Myklebust0032a7a2012-03-08 17:16:12 -05001371 flags &= FMODE_READ|FMODE_WRITE;
Trond Myklebust8383e462007-07-06 15:12:04 -04001372 rcu_read_lock();
1373 delegation = rcu_dereference(nfsi->delegation);
Trond Myklebustaa05c872016-09-22 13:38:54 -04001374 ret = nfs4_is_valid_delegation(delegation, flags);
Trond Myklebust0032a7a2012-03-08 17:16:12 -05001375 if (ret) {
Trond Myklebustf597c532012-03-04 18:13:56 -05001376 nfs4_stateid_copy(dst, &delegation->stateid);
Trond Myklebust0032a7a2012-03-08 17:16:12 -05001377 nfs_mark_delegation_referenced(delegation);
Trond Myklebustabf4e132016-05-16 17:42:44 -04001378 if (cred)
NeilBrowna52458b2018-12-03 11:30:31 +11001379 *cred = get_cred(delegation->cred);
Trond Myklebust3e4f6292006-03-20 13:44:46 -05001380 }
Trond Myklebust8383e462007-07-06 15:12:04 -04001381 rcu_read_unlock();
1382 return ret;
Trond Myklebust3e4f6292006-03-20 13:44:46 -05001383}
Trond Myklebust5445b1f2015-09-05 19:06:58 -04001384
1385/**
1386 * nfs4_delegation_flush_on_close - Check if we must flush file on close
1387 * @inode: inode to check
1388 *
1389 * This function checks the number of outstanding writes to the file
1390 * against the delegation 'space_limit' field to see if
1391 * the spec requires us to flush the file on close.
1392 */
1393bool nfs4_delegation_flush_on_close(const struct inode *inode)
1394{
1395 struct nfs_inode *nfsi = NFS_I(inode);
1396 struct nfs_delegation *delegation;
1397 bool ret = true;
1398
1399 rcu_read_lock();
1400 delegation = rcu_dereference(nfsi->delegation);
1401 if (delegation == NULL || !(delegation->type & FMODE_WRITE))
1402 goto out;
Trond Myklebusta6b6d5b2017-08-01 15:39:46 -04001403 if (atomic_long_read(&nfsi->nrequests) < delegation->pagemod_limit)
Trond Myklebust5445b1f2015-09-05 19:06:58 -04001404 ret = false;
1405out:
1406 rcu_read_unlock();
1407 return ret;
1408}
Trond Myklebust10717f42020-01-27 09:58:19 -05001409
1410module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644);