delegation.c 24.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9
/*
 * linux/fs/nfs/delegation.c
 *
 * Copyright (C) 2004 Trond Myklebust
 *
 * NFS file delegation management
 *
 */
#include <linux/completion.h>
10
#include <linux/kthread.h>
L
Linus Torvalds 已提交
11 12
#include <linux/module.h>
#include <linux/sched.h>
13
#include <linux/slab.h>
L
Linus Torvalds 已提交
14 15 16 17 18 19
#include <linux/spinlock.h>

#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_xdr.h>

20
#include "nfs4_fs.h"
L
Linus Torvalds 已提交
21
#include "delegation.h"
22
#include "internal.h"
23
#include "nfs4trace.h"
L
Linus Torvalds 已提交
24

25 26
static void nfs_free_delegation(struct nfs_delegation *delegation)
{
27 28 29 30
	if (delegation->cred) {
		put_rpccred(delegation->cred);
		delegation->cred = NULL;
	}
31
	kfree_rcu(delegation, rcu);
32 33
}

34 35 36 37 38
/**
 * nfs_mark_delegation_referenced - set delegation's REFERENCED flag
 * @delegation: delegation to process
 *
 */
39 40 41 42 43
void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
{
	set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
}

P
Peng Tao 已提交
44 45
static int
nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark)
46 47 48 49 50 51 52
{
	struct nfs_delegation *delegation;
	int ret = 0;

	flags &= FMODE_READ|FMODE_WRITE;
	rcu_read_lock();
	delegation = rcu_dereference(NFS_I(inode)->delegation);
53 54
	if (delegation != NULL && (delegation->type & flags) == flags &&
	    !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
P
Peng Tao 已提交
55 56
		if (mark)
			nfs_mark_delegation_referenced(delegation);
57 58 59 60 61
		ret = 1;
	}
	rcu_read_unlock();
	return ret;
}
P
Peng Tao 已提交
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
/**
 * nfs_have_delegation - check if inode has a delegation, mark it
 * NFS_DELEGATION_REFERENCED if there is one.
 * @inode: inode to check
 * @flags: delegation types to check for
 *
 * Returns one if inode has the indicated delegation, otherwise zero.
 */
int nfs4_have_delegation(struct inode *inode, fmode_t flags)
{
	return nfs4_do_check_delegation(inode, flags, true);
}

/*
 * nfs4_check_delegation - check if inode has a delegation, do not mark
 * NFS_DELEGATION_REFERENCED if it has one.
 */
int nfs4_check_delegation(struct inode *inode, fmode_t flags)
{
	return nfs4_do_check_delegation(inode, flags, false);
}
83

84
static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
85 86 87
{
	struct inode *inode = state->inode;
	struct file_lock *fl;
88 89
	struct file_lock_context *flctx = inode->i_flctx;
	struct list_head *list;
90
	int status = 0;
91

92
	if (flctx == NULL)
93
		goto out;
94

95
	list = &flctx->flc_posix;
96
	spin_lock(&flctx->flc_lock);
97 98
restart:
	list_for_each_entry(fl, list, fl_list) {
99
		if (nfs_file_open_context(fl->fl_file) != ctx)
100
			continue;
101
		spin_unlock(&flctx->flc_lock);
102
		status = nfs4_lock_delegation_recall(fl, state, stateid);
103
		if (status < 0)
104
			goto out;
105
		spin_lock(&flctx->flc_lock);
106
	}
107 108 109
	if (list == &flctx->flc_posix) {
		list = &flctx->flc_flock;
		goto restart;
110
	}
111
	spin_unlock(&flctx->flc_lock);
112
out:
113 114 115
	return status;
}

116 117
static int nfs_delegation_claim_opens(struct inode *inode,
		const nfs4_stateid *stateid, fmode_t type)
L
Linus Torvalds 已提交
118 119 120
{
	struct nfs_inode *nfsi = NFS_I(inode);
	struct nfs_open_context *ctx;
121
	struct nfs4_state_owner *sp;
L
Linus Torvalds 已提交
122
	struct nfs4_state *state;
123
	unsigned int seq;
124
	int err;
L
Linus Torvalds 已提交
125 126 127 128 129 130 131 132 133

again:
	spin_lock(&inode->i_lock);
	list_for_each_entry(ctx, &nfsi->open_files, list) {
		state = ctx->state;
		if (state == NULL)
			continue;
		if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
			continue;
134 135
		if (!nfs4_valid_open_stateid(state))
			continue;
136
		if (!nfs4_stateid_match(&state->stateid, stateid))
137
			continue;
L
Linus Torvalds 已提交
138 139
		get_nfs_open_context(ctx);
		spin_unlock(&inode->i_lock);
140
		sp = state->owner;
141 142
		/* Block nfs4_proc_unlck */
		mutex_lock(&sp->so_delegreturn_mutex);
143
		seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
144
		err = nfs4_open_delegation_recall(ctx, state, stateid, type);
145
		if (!err)
146
			err = nfs_delegation_claim_locks(ctx, state, stateid);
147 148
		if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
			err = -EAGAIN;
149
		mutex_unlock(&sp->so_delegreturn_mutex);
L
Linus Torvalds 已提交
150
		put_nfs_open_context(ctx);
151
		if (err != 0)
152
			return err;
L
Linus Torvalds 已提交
153 154 155
		goto again;
	}
	spin_unlock(&inode->i_lock);
156
	return 0;
L
Linus Torvalds 已提交
157 158
}

159 160 161 162 163 164
/**
 * nfs_inode_reclaim_delegation - process a delegation reclaim request
 * @inode: inode to process
 * @cred: credential to use for request
 * @res: new delegation state from server
 *
L
Linus Torvalds 已提交
165
 */
166 167
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
				  struct nfs_openres *res)
L
Linus Torvalds 已提交
168
{
169 170
	struct nfs_delegation *delegation;
	struct rpc_cred *oldcred = NULL;
L
Linus Torvalds 已提交
171

172 173 174 175 176
	rcu_read_lock();
	delegation = rcu_dereference(NFS_I(inode)->delegation);
	if (delegation != NULL) {
		spin_lock(&delegation->lock);
		if (delegation->inode != NULL) {
177
			nfs4_stateid_copy(&delegation->stateid, &res->delegation);
178
			delegation->type = res->delegation_type;
179
			delegation->pagemod_limit = res->pagemod_limit;
180 181 182 183 184 185
			oldcred = delegation->cred;
			delegation->cred = get_rpccred(cred);
			clear_bit(NFS_DELEGATION_NEED_RECLAIM,
				  &delegation->flags);
			spin_unlock(&delegation->lock);
			rcu_read_unlock();
186
			put_rpccred(oldcred);
187
			trace_nfs4_reclaim_delegation(inode, res->delegation_type);
188 189 190 191 192 193 194 195 196
		} else {
			/* We appear to have raced with a delegation return. */
			spin_unlock(&delegation->lock);
			rcu_read_unlock();
			nfs_inode_set_delegation(inode, cred, res);
		}
	} else {
		rcu_read_unlock();
	}
L
Linus Torvalds 已提交
197 198
}

199 200 201 202
static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
{
	int res = 0;

203 204 205 206 207
	if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
		res = nfs4_proc_delegreturn(inode,
				delegation->cred,
				&delegation->stateid,
				issync);
208 209 210 211
	nfs_free_delegation(delegation);
	return res;
}

212 213 214 215 216 217 218 219 220 221 222
static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation)
{
	struct inode *inode = NULL;

	spin_lock(&delegation->lock);
	if (delegation->inode != NULL)
		inode = igrab(delegation->inode);
	spin_unlock(&delegation->lock);
	return inode;
}

223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
static struct nfs_delegation *
nfs_start_delegation_return_locked(struct nfs_inode *nfsi)
{
	struct nfs_delegation *ret = NULL;
	struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);

	if (delegation == NULL)
		goto out;
	spin_lock(&delegation->lock);
	if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
		ret = delegation;
	spin_unlock(&delegation->lock);
out:
	return ret;
}

static struct nfs_delegation *
nfs_start_delegation_return(struct nfs_inode *nfsi)
{
	struct nfs_delegation *delegation;

	rcu_read_lock();
	delegation = nfs_start_delegation_return_locked(nfsi);
	rcu_read_unlock();
	return delegation;
}

static void
nfs_abort_delegation_return(struct nfs_delegation *delegation,
		struct nfs_client *clp)
{

	spin_lock(&delegation->lock);
	clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
	set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
	spin_unlock(&delegation->lock);
	set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
}

262 263
static struct nfs_delegation *
nfs_detach_delegation_locked(struct nfs_inode *nfsi,
264 265
		struct nfs_delegation *delegation,
		struct nfs_client *clp)
266
{
267
	struct nfs_delegation *deleg_cur =
268
		rcu_dereference_protected(nfsi->delegation,
269
				lockdep_is_held(&clp->cl_lock));
270

271 272
	if (deleg_cur == NULL || delegation != deleg_cur)
		return NULL;
273

274
	spin_lock(&delegation->lock);
275
	set_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
276
	list_del_rcu(&delegation->super_list);
277
	delegation->inode = NULL;
278
	rcu_assign_pointer(nfsi->delegation, NULL);
279
	spin_unlock(&delegation->lock);
280 281 282
	return delegation;
}

283
static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi,
284 285
		struct nfs_delegation *delegation,
		struct nfs_server *server)
286
{
287
	struct nfs_client *clp = server->nfs_client;
288 289

	spin_lock(&clp->cl_lock);
290
	delegation = nfs_detach_delegation_locked(nfsi, delegation, clp);
291 292 293 294
	spin_unlock(&clp->cl_lock);
	return delegation;
}

295 296 297 298 299 300 301 302 303 304 305 306 307
static struct nfs_delegation *
nfs_inode_detach_delegation(struct inode *inode)
{
	struct nfs_inode *nfsi = NFS_I(inode);
	struct nfs_server *server = NFS_SERVER(inode);
	struct nfs_delegation *delegation;

	delegation = nfs_start_delegation_return(nfsi);
	if (delegation == NULL)
		return NULL;
	return nfs_detach_delegation(nfsi, delegation, server);
}

308 309 310 311 312 313 314 315 316 317 318
static void
nfs_update_inplace_delegation(struct nfs_delegation *delegation,
		const struct nfs_delegation *update)
{
	if (nfs4_stateid_is_newer(&update->stateid, &delegation->stateid)) {
		delegation->stateid.seqid = update->stateid.seqid;
		smp_wmb();
		delegation->type = update->type;
	}
}

319 320 321 322 323 324 325
/**
 * nfs_inode_set_delegation - set up a delegation on an inode
 * @inode: inode to which delegation applies
 * @cred: cred to use for subsequent delegation processing
 * @res: new delegation state from server
 *
 * Returns zero on success, or a negative errno value.
L
Linus Torvalds 已提交
326 327 328
 */
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
{
329 330
	struct nfs_server *server = NFS_SERVER(inode);
	struct nfs_client *clp = server->nfs_client;
L
Linus Torvalds 已提交
331
	struct nfs_inode *nfsi = NFS_I(inode);
332
	struct nfs_delegation *delegation, *old_delegation;
333
	struct nfs_delegation *freeme = NULL;
L
Linus Torvalds 已提交
334 335
	int status = 0;

336
	delegation = kmalloc(sizeof(*delegation), GFP_NOFS);
L
Linus Torvalds 已提交
337 338
	if (delegation == NULL)
		return -ENOMEM;
339
	nfs4_stateid_copy(&delegation->stateid, &res->delegation);
L
Linus Torvalds 已提交
340
	delegation->type = res->delegation_type;
341
	delegation->pagemod_limit = res->pagemod_limit;
342
	delegation->change_attr = inode->i_version;
L
Linus Torvalds 已提交
343 344
	delegation->cred = get_rpccred(cred);
	delegation->inode = inode;
345
	delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
346
	spin_lock_init(&delegation->lock);
L
Linus Torvalds 已提交
347 348

	spin_lock(&clp->cl_lock);
349
	old_delegation = rcu_dereference_protected(nfsi->delegation,
350
					lockdep_is_held(&clp->cl_lock));
351
	if (old_delegation != NULL) {
352 353 354 355 356
		/* Is this an update of the existing delegation? */
		if (nfs4_stateid_match_other(&old_delegation->stateid,
					&delegation->stateid)) {
			nfs_update_inplace_delegation(old_delegation,
					delegation);
357
			goto out;
L
Linus Torvalds 已提交
358
		}
359 360 361
		/*
		 * Deal with broken servers that hand out two
		 * delegations for the same file.
362 363
		 * Allow for upgrades to a WRITE delegation, but
		 * nothing else.
364 365 366
		 */
		dfprintk(FILE, "%s: server %s handed out "
				"a duplicate delegation!\n",
367
				__func__, clp->cl_hostname);
368 369
		if (delegation->type == old_delegation->type ||
		    !(delegation->type & FMODE_WRITE)) {
370 371 372 373
			freeme = delegation;
			delegation = NULL;
			goto out;
		}
374 375 376 377
		if (test_and_set_bit(NFS_DELEGATION_RETURNING,
					&old_delegation->flags))
			goto out;
		freeme = nfs_detach_delegation_locked(nfsi,
378 379 380
				old_delegation, clp);
		if (freeme == NULL)
			goto out;
L
Linus Torvalds 已提交
381
	}
382
	list_add_tail_rcu(&delegation->super_list, &server->delegations);
383 384
	rcu_assign_pointer(nfsi->delegation, delegation);
	delegation = NULL;
385 386 387 388 389

	/* Ensure we revalidate the attributes and page cache! */
	spin_lock(&inode->i_lock);
	nfsi->cache_validity |= NFS_INO_REVAL_FORCED;
	spin_unlock(&inode->i_lock);
390
	trace_nfs4_set_delegation(inode, res->delegation_type);
391

392
out:
L
Linus Torvalds 已提交
393
	spin_unlock(&clp->cl_lock);
394 395
	if (delegation != NULL)
		nfs_free_delegation(delegation);
396 397
	if (freeme != NULL)
		nfs_do_return_delegation(inode, freeme, 0);
L
Linus Torvalds 已提交
398 399 400 401 402 403
	return status;
}

/*
 * Basic procedure for returning a delegation to the server
 */
404
static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync)
L
Linus Torvalds 已提交
405
{
406
	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
L
Linus Torvalds 已提交
407
	struct nfs_inode *nfsi = NFS_I(inode);
408
	int err = 0;
L
Linus Torvalds 已提交
409

410 411 412
	if (delegation == NULL)
		return 0;
	do {
413 414
		if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
			break;
415 416
		err = nfs_delegation_claim_opens(inode, &delegation->stateid,
				delegation->type);
417 418 419 420 421 422 423 424 425 426 427 428 429
		if (!issync || err != -EAGAIN)
			break;
		/*
		 * Guard against state recovery
		 */
		err = nfs4_wait_clnt_recover(clp);
	} while (err == 0);

	if (err) {
		nfs_abort_delegation_return(delegation, clp);
		goto out;
	}
	if (!nfs_detach_delegation(nfsi, delegation, NFS_SERVER(inode)))
430
		goto out;
L
Linus Torvalds 已提交
431

432 433 434
	err = nfs_do_return_delegation(inode, delegation, issync);
out:
	return err;
435 436
}

437 438 439 440
static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
{
	bool ret = false;

441 442
	if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
		goto out;
443 444 445 446 447 448 449 450 451 452 453
	if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
		ret = true;
	if (test_and_clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) && !ret) {
		struct inode *inode;

		spin_lock(&delegation->lock);
		inode = delegation->inode;
		if (inode && list_empty(&NFS_I(inode)->open_files))
			ret = true;
		spin_unlock(&delegation->lock);
	}
454
out:
455 456 457
	return ret;
}

458 459 460 461
/**
 * nfs_client_return_marked_delegations - return previously marked delegations
 * @clp: nfs_client to process
 *
462 463 464 465
 * Note that this function is designed to be called by the state
 * manager thread. For this reason, it cannot flush the dirty data,
 * since that could deadlock in case of a state recovery error.
 *
466
 * Returns zero on success, or a negative errno value.
467
 */
468
int nfs_client_return_marked_delegations(struct nfs_client *clp)
469 470
{
	struct nfs_delegation *delegation;
471
	struct nfs_server *server;
472
	struct inode *inode;
473
	int err = 0;
474 475 476

restart:
	rcu_read_lock();
477 478 479
	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
		list_for_each_entry_rcu(delegation, &server->delegations,
								super_list) {
480
			if (!nfs_delegation_need_return(delegation))
481
				continue;
482
			if (!nfs_sb_active(server->super))
483
				continue;
484 485 486 487 488 489
			inode = nfs_delegation_grab_inode(delegation);
			if (inode == NULL) {
				rcu_read_unlock();
				nfs_sb_deactive(server->super);
				goto restart;
			}
490
			delegation = nfs_start_delegation_return_locked(NFS_I(inode));
491 492
			rcu_read_unlock();

493
			err = nfs_end_delegation_return(inode, delegation, 0);
494
			iput(inode);
495
			nfs_sb_deactive(server->super);
496 497 498 499
			if (!err)
				goto restart;
			set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
			return err;
500
		}
501 502
	}
	rcu_read_unlock();
503
	return 0;
504 505
}

506 507 508 509 510 511
/**
 * nfs_inode_return_delegation_noreclaim - return delegation, don't reclaim opens
 * @inode: inode to process
 *
 * Does not protect against delegation reclaims, therefore really only safe
 * to be called from nfs4_clear_inode().
512 513 514 515 516
 */
void nfs_inode_return_delegation_noreclaim(struct inode *inode)
{
	struct nfs_delegation *delegation;

517 518
	delegation = nfs_inode_detach_delegation(inode);
	if (delegation != NULL)
519
		nfs_do_return_delegation(inode, delegation, 1);
520 521
}

522 523 524 525
/**
 * nfs_inode_return_delegation - synchronously return a delegation
 * @inode: inode to process
 *
526 527 528 529
 * This routine will always flush any dirty data to disk on the
 * assumption that if we need to return the delegation, then
 * we should stop caching.
 *
530 531
 * Returns zero on success, or a negative errno value.
 */
532
int nfs4_inode_return_delegation(struct inode *inode)
533 534 535 536 537
{
	struct nfs_inode *nfsi = NFS_I(inode);
	struct nfs_delegation *delegation;
	int err = 0;

538
	nfs_wb_all(inode);
539 540 541
	delegation = nfs_start_delegation_return(nfsi);
	if (delegation != NULL)
		err = nfs_end_delegation_return(inode, delegation, 1);
542
	return err;
L
Linus Torvalds 已提交
543 544
}

545 546 547 548 549 550 551
static void nfs_mark_return_if_closed_delegation(struct nfs_server *server,
		struct nfs_delegation *delegation)
{
	set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
	set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
}

552 553
static void nfs_mark_return_delegation(struct nfs_server *server,
		struct nfs_delegation *delegation)
554 555
{
	set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
556
	set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
557 558
}

559 560 561 562 563 564 565 566 567 568 569 570
static bool nfs_server_mark_return_all_delegations(struct nfs_server *server)
{
	struct nfs_delegation *delegation;
	bool ret = false;

	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
		nfs_mark_return_delegation(server, delegation);
		ret = true;
	}
	return ret;
}

571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
{
	struct nfs_server *server;

	rcu_read_lock();
	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
		nfs_server_mark_return_all_delegations(server);
	rcu_read_unlock();
}

static void nfs_delegation_run_state_manager(struct nfs_client *clp)
{
	if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
		nfs4_schedule_state_manager(clp);
}

/**
 * nfs_expire_all_delegations
 * @clp: client to process
 *
 */
void nfs_expire_all_delegations(struct nfs_client *clp)
{
	nfs_client_mark_return_all_delegations(clp);
	nfs_delegation_run_state_manager(clp);
}

598 599 600 601
/**
 * nfs_super_return_all_delegations - return delegations for one superblock
 * @sb: sb to process
 *
L
Linus Torvalds 已提交
602
 */
603
void nfs_server_return_all_delegations(struct nfs_server *server)
L
Linus Torvalds 已提交
604
{
605
	struct nfs_client *clp = server->nfs_client;
606
	bool need_wait;
L
Linus Torvalds 已提交
607 608 609

	if (clp == NULL)
		return;
610

611
	rcu_read_lock();
612
	need_wait = nfs_server_mark_return_all_delegations(server);
613
	rcu_read_unlock();
614

615
	if (need_wait) {
616
		nfs4_schedule_state_manager(clp);
617 618
		nfs4_wait_clnt_recover(clp);
	}
619 620
}

621
static void nfs_mark_return_unused_delegation_types(struct nfs_server *server,
622
						 fmode_t flags)
623 624 625
{
	struct nfs_delegation *delegation;

626
	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
627 628 629
		if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
			continue;
		if (delegation->type & flags)
630
			nfs_mark_return_if_closed_delegation(server, delegation);
631
	}
632 633
}

634
static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp,
635 636 637 638 639 640
							fmode_t flags)
{
	struct nfs_server *server;

	rcu_read_lock();
	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
641
		nfs_mark_return_unused_delegation_types(server, flags);
642
	rcu_read_unlock();
L
Linus Torvalds 已提交
643 644
}

645 646 647 648 649 650 651 652 653 654 655 656
static void nfs_revoke_delegation(struct inode *inode)
{
	struct nfs_delegation *delegation;
	rcu_read_lock();
	delegation = rcu_dereference(NFS_I(inode)->delegation);
	if (delegation != NULL) {
		set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
		nfs_mark_return_delegation(NFS_SERVER(inode), delegation);
	}
	rcu_read_unlock();
}

657 658 659 660
void nfs_remove_bad_delegation(struct inode *inode)
{
	struct nfs_delegation *delegation;

661
	nfs_revoke_delegation(inode);
662
	delegation = nfs_inode_detach_delegation(inode);
663 664 665 666 667
	if (delegation) {
		nfs_inode_find_state_and_recover(inode, &delegation->stateid);
		nfs_free_delegation(delegation);
	}
}
A
Andy Adamson 已提交
668
EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
669

670
/**
671
 * nfs_expire_unused_delegation_types
672 673 674 675
 * @clp: client to process
 * @flags: delegation types to expire
 *
 */
676
void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags)
677
{
678
	nfs_client_mark_return_unused_delegation_types(clp, flags);
679
	nfs_delegation_run_state_manager(clp);
680 681
}

682
static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
683 684 685
{
	struct nfs_delegation *delegation;

686
	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
687 688
		if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
			continue;
689
		nfs_mark_return_if_closed_delegation(server, delegation);
690 691 692
	}
}

693 694 695 696 697
/**
 * nfs_expire_unreferenced_delegations - Eliminate unused delegations
 * @clp: nfs_client to process
 *
 */
698 699
void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
{
700 701 702 703 704 705 706
	struct nfs_server *server;

	rcu_read_lock();
	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
		nfs_mark_return_unreferenced_delegations(server);
	rcu_read_unlock();

707 708 709
	nfs_delegation_run_state_manager(clp);
}

710 711 712
/**
 * nfs_async_inode_return_delegation - asynchronously return a delegation
 * @inode: inode to process
713
 * @stateid: state ID information
714 715
 *
 * Returns zero on success, or a negative errno value.
L
Linus Torvalds 已提交
716
 */
717 718
int nfs_async_inode_return_delegation(struct inode *inode,
				      const nfs4_stateid *stateid)
L
Linus Torvalds 已提交
719
{
720 721
	struct nfs_server *server = NFS_SERVER(inode);
	struct nfs_client *clp = server->nfs_client;
722
	struct nfs_delegation *delegation;
L
Linus Torvalds 已提交
723

724 725
	rcu_read_lock();
	delegation = rcu_dereference(NFS_I(inode)->delegation);
726 727
	if (delegation == NULL)
		goto out_enoent;
728 729
	if (stateid != NULL &&
	    !clp->cl_mvops->match_stateid(&delegation->stateid, stateid))
730
		goto out_enoent;
731
	nfs_mark_return_delegation(server, delegation);
732
	rcu_read_unlock();
733

734 735
	nfs_delegation_run_state_manager(clp);
	return 0;
736 737 738
out_enoent:
	rcu_read_unlock();
	return -ENOENT;
L
Linus Torvalds 已提交
739 740
}

741 742 743
static struct inode *
nfs_delegation_find_inode_server(struct nfs_server *server,
				 const struct nfs_fh *fhandle)
L
Linus Torvalds 已提交
744 745 746
{
	struct nfs_delegation *delegation;
	struct inode *res = NULL;
747 748

	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
749 750 751
		spin_lock(&delegation->lock);
		if (delegation->inode != NULL &&
		    nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
L
Linus Torvalds 已提交
752 753
			res = igrab(delegation->inode);
		}
754 755 756
		spin_unlock(&delegation->lock);
		if (res != NULL)
			break;
L
Linus Torvalds 已提交
757
	}
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
	return res;
}

/**
 * nfs_delegation_find_inode - retrieve the inode associated with a delegation
 * @clp: client state handle
 * @fhandle: filehandle from a delegation recall
 *
 * Returns pointer to inode matching "fhandle," or NULL if a matching inode
 * cannot be found.
 */
struct inode *nfs_delegation_find_inode(struct nfs_client *clp,
					const struct nfs_fh *fhandle)
{
	struct nfs_server *server;
	struct inode *res = NULL;

	rcu_read_lock();
	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
		res = nfs_delegation_find_inode_server(server, fhandle);
		if (res != NULL)
			break;
	}
781
	rcu_read_unlock();
L
Linus Torvalds 已提交
782 783 784
	return res;
}

785 786 787 788 789 790 791 792 793 794 795 796
static void nfs_delegation_mark_reclaim_server(struct nfs_server *server)
{
	struct nfs_delegation *delegation;

	list_for_each_entry_rcu(delegation, &server->delegations, super_list)
		set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
}

/**
 * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed
 * @clp: nfs_client to process
 *
L
Linus Torvalds 已提交
797
 */
798
void nfs_delegation_mark_reclaim(struct nfs_client *clp)
L
Linus Torvalds 已提交
799
{
800 801
	struct nfs_server *server;

802
	rcu_read_lock();
803 804
	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
		nfs_delegation_mark_reclaim_server(server);
805
	rcu_read_unlock();
L
Linus Torvalds 已提交
806 807
}

808 809 810 811
/**
 * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done
 * @clp: nfs_client to process
 *
L
Linus Torvalds 已提交
812
 */
813
void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
L
Linus Torvalds 已提交
814
{
815
	struct nfs_delegation *delegation;
816
	struct nfs_server *server;
817
	struct inode *inode;
818

819 820
restart:
	rcu_read_lock();
821 822 823
	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
		list_for_each_entry_rcu(delegation, &server->delegations,
								super_list) {
824 825 826
			if (test_bit(NFS_DELEGATION_RETURNING,
						&delegation->flags))
				continue;
827 828 829
			if (test_bit(NFS_DELEGATION_NEED_RECLAIM,
						&delegation->flags) == 0)
				continue;
830
			if (!nfs_sb_active(server->super))
831
				continue;
832 833 834 835 836 837
			inode = nfs_delegation_grab_inode(delegation);
			if (inode == NULL) {
				rcu_read_unlock();
				nfs_sb_deactive(server->super);
				goto restart;
			}
838
			delegation = nfs_start_delegation_return_locked(NFS_I(inode));
839
			rcu_read_unlock();
840 841 842 843 844 845
			if (delegation != NULL) {
				delegation = nfs_detach_delegation(NFS_I(inode),
					delegation, server);
				if (delegation != NULL)
					nfs_free_delegation(delegation);
			}
846
			iput(inode);
847
			nfs_sb_deactive(server->super);
848 849
			goto restart;
		}
L
Linus Torvalds 已提交
850
	}
851
	rcu_read_unlock();
L
Linus Torvalds 已提交
852
}
853

854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878
/**
 * nfs_delegations_present - check for existence of delegations
 * @clp: client state handle
 *
 * Returns one if there are any nfs_delegation structures attached
 * to this nfs_client.
 */
int nfs_delegations_present(struct nfs_client *clp)
{
	struct nfs_server *server;
	int ret = 0;

	rcu_read_lock();
	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
		if (!list_empty(&server->delegations)) {
			ret = 1;
			break;
		}
	rcu_read_unlock();
	return ret;
}

/**
 * nfs4_copy_delegation_stateid - Copy inode's state ID information
 * @inode: inode to check
879
 * @flags: delegation type requirement
880 881
 * @dst: stateid data structure to fill in
 * @cred: optional argument to retrieve credential
882
 *
883 884
 * Returns "true" and fills in "dst->data" * if inode had a delegation,
 * otherwise "false" is returned.
885
 */
886 887
bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags,
		nfs4_stateid *dst, struct rpc_cred **cred)
888 889 890
{
	struct nfs_inode *nfsi = NFS_I(inode);
	struct nfs_delegation *delegation;
891
	bool ret;
892

893
	flags &= FMODE_READ|FMODE_WRITE;
894 895
	rcu_read_lock();
	delegation = rcu_dereference(nfsi->delegation);
896 897
	ret = (delegation != NULL && (delegation->type & flags) == flags);
	if (ret) {
898
		nfs4_stateid_copy(dst, &delegation->stateid);
899
		nfs_mark_delegation_referenced(delegation);
900 901
		if (cred)
			*cred = get_rpccred(delegation->cred);
902
	}
903 904
	rcu_read_unlock();
	return ret;
905
}
906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930

/**
 * nfs4_delegation_flush_on_close - Check if we must flush file on close
 * @inode: inode to check
 *
 * This function checks the number of outstanding writes to the file
 * against the delegation 'space_limit' field to see if
 * the spec requires us to flush the file on close.
 */
bool nfs4_delegation_flush_on_close(const struct inode *inode)
{
	struct nfs_inode *nfsi = NFS_I(inode);
	struct nfs_delegation *delegation;
	bool ret = true;

	rcu_read_lock();
	delegation = rcu_dereference(nfsi->delegation);
	if (delegation == NULL || !(delegation->type & FMODE_WRITE))
		goto out;
	if (nfsi->nrequests < delegation->pagemod_limit)
		ret = false;
out:
	rcu_read_unlock();
	return ret;
}