18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/fs/nfs/delegation.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004 Trond Myklebust 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * NFS file delegation management 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/completion.h> 118c2ecf20Sopenharmony_ci#include <linux/kthread.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/sched.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 168c2ecf20Sopenharmony_ci#include <linux/iversion.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/nfs4.h> 198c2ecf20Sopenharmony_ci#include <linux/nfs_fs.h> 208c2ecf20Sopenharmony_ci#include <linux/nfs_xdr.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "nfs4_fs.h" 238c2ecf20Sopenharmony_ci#include "nfs4session.h" 248c2ecf20Sopenharmony_ci#include "delegation.h" 258c2ecf20Sopenharmony_ci#include "internal.h" 268c2ecf20Sopenharmony_ci#include "nfs4trace.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define NFS_DEFAULT_DELEGATION_WATERMARK (5000U) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic atomic_long_t nfs_active_delegations; 318c2ecf20Sopenharmony_cistatic unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic void __nfs_free_delegation(struct nfs_delegation *delegation) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci put_cred(delegation->cred); 368c2ecf20Sopenharmony_ci delegation->cred = NULL; 378c2ecf20Sopenharmony_ci kfree_rcu(delegation, rcu); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic void nfs_mark_delegation_revoked(struct nfs_delegation *delegation) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci if (!test_and_set_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { 438c2ecf20Sopenharmony_ci delegation->stateid.type = NFS4_INVALID_STATEID_TYPE; 448c2ecf20Sopenharmony_ci atomic_long_dec(&nfs_active_delegations); 458c2ecf20Sopenharmony_ci if (!test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) 468c2ecf20Sopenharmony_ci nfs_clear_verifier_delegated(delegation->inode); 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic struct nfs_delegation *nfs_get_delegation(struct nfs_delegation *delegation) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci refcount_inc(&delegation->refcount); 538c2ecf20Sopenharmony_ci return delegation; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic void nfs_put_delegation(struct nfs_delegation *delegation) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci if (refcount_dec_and_test(&delegation->refcount)) 598c2ecf20Sopenharmony_ci __nfs_free_delegation(delegation); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic void nfs_free_delegation(struct nfs_delegation *delegation) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci nfs_mark_delegation_revoked(delegation); 658c2ecf20Sopenharmony_ci nfs_put_delegation(delegation); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/** 698c2ecf20Sopenharmony_ci * nfs_mark_delegation_referenced - set delegation's REFERENCED flag 708c2ecf20Sopenharmony_ci * @delegation: delegation to process 718c2ecf20Sopenharmony_ci * 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_civoid nfs_mark_delegation_referenced(struct nfs_delegation *delegation) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void nfs_mark_return_delegation(struct nfs_server *server, 798c2ecf20Sopenharmony_ci struct nfs_delegation *delegation) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci set_bit(NFS_DELEGATION_RETURN, &delegation->flags); 828c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic bool 868c2ecf20Sopenharmony_cinfs4_is_valid_delegation(const struct nfs_delegation *delegation, 878c2ecf20Sopenharmony_ci fmode_t flags) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci if (delegation != NULL && (delegation->type & flags) == flags && 908c2ecf20Sopenharmony_ci !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) && 918c2ecf20Sopenharmony_ci !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) 928c2ecf20Sopenharmony_ci return true; 938c2ecf20Sopenharmony_ci return false; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistruct nfs_delegation *nfs4_get_valid_delegation(const struct inode *inode) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci delegation = rcu_dereference(NFS_I(inode)->delegation); 1018c2ecf20Sopenharmony_ci if (nfs4_is_valid_delegation(delegation, 0)) 1028c2ecf20Sopenharmony_ci return delegation; 1038c2ecf20Sopenharmony_ci return NULL; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int 1078c2ecf20Sopenharmony_cinfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 1108c2ecf20Sopenharmony_ci int ret = 0; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci flags &= FMODE_READ|FMODE_WRITE; 1138c2ecf20Sopenharmony_ci rcu_read_lock(); 1148c2ecf20Sopenharmony_ci delegation = rcu_dereference(NFS_I(inode)->delegation); 1158c2ecf20Sopenharmony_ci if (nfs4_is_valid_delegation(delegation, flags)) { 1168c2ecf20Sopenharmony_ci if (mark) 1178c2ecf20Sopenharmony_ci nfs_mark_delegation_referenced(delegation); 1188c2ecf20Sopenharmony_ci ret = 1; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci rcu_read_unlock(); 1218c2ecf20Sopenharmony_ci return ret; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci/** 1248c2ecf20Sopenharmony_ci * nfs_have_delegation - check if inode has a delegation, mark it 1258c2ecf20Sopenharmony_ci * NFS_DELEGATION_REFERENCED if there is one. 1268c2ecf20Sopenharmony_ci * @inode: inode to check 1278c2ecf20Sopenharmony_ci * @flags: delegation types to check for 1288c2ecf20Sopenharmony_ci * 1298c2ecf20Sopenharmony_ci * Returns one if inode has the indicated delegation, otherwise zero. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ciint nfs4_have_delegation(struct inode *inode, fmode_t flags) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci return nfs4_do_check_delegation(inode, flags, true); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* 1378c2ecf20Sopenharmony_ci * nfs4_check_delegation - check if inode has a delegation, do not mark 1388c2ecf20Sopenharmony_ci * NFS_DELEGATION_REFERENCED if it has one. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ciint nfs4_check_delegation(struct inode *inode, fmode_t flags) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci return nfs4_do_check_delegation(inode, flags, false); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int nfs_delegation_claim_locks(struct nfs4_state *state, const nfs4_stateid *stateid) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct inode *inode = state->inode; 1488c2ecf20Sopenharmony_ci struct file_lock *fl; 1498c2ecf20Sopenharmony_ci struct file_lock_context *flctx = inode->i_flctx; 1508c2ecf20Sopenharmony_ci struct list_head *list; 1518c2ecf20Sopenharmony_ci int status = 0; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (flctx == NULL) 1548c2ecf20Sopenharmony_ci goto out; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci list = &flctx->flc_posix; 1578c2ecf20Sopenharmony_ci spin_lock(&flctx->flc_lock); 1588c2ecf20Sopenharmony_cirestart: 1598c2ecf20Sopenharmony_ci list_for_each_entry(fl, list, fl_list) { 1608c2ecf20Sopenharmony_ci if (nfs_file_open_context(fl->fl_file)->state != state) 1618c2ecf20Sopenharmony_ci continue; 1628c2ecf20Sopenharmony_ci spin_unlock(&flctx->flc_lock); 1638c2ecf20Sopenharmony_ci status = nfs4_lock_delegation_recall(fl, state, stateid); 1648c2ecf20Sopenharmony_ci if (status < 0) 1658c2ecf20Sopenharmony_ci goto out; 1668c2ecf20Sopenharmony_ci spin_lock(&flctx->flc_lock); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci if (list == &flctx->flc_posix) { 1698c2ecf20Sopenharmony_ci list = &flctx->flc_flock; 1708c2ecf20Sopenharmony_ci goto restart; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci spin_unlock(&flctx->flc_lock); 1738c2ecf20Sopenharmony_ciout: 1748c2ecf20Sopenharmony_ci return status; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int nfs_delegation_claim_opens(struct inode *inode, 1788c2ecf20Sopenharmony_ci const nfs4_stateid *stateid, fmode_t type) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 1818c2ecf20Sopenharmony_ci struct nfs_open_context *ctx; 1828c2ecf20Sopenharmony_ci struct nfs4_state_owner *sp; 1838c2ecf20Sopenharmony_ci struct nfs4_state *state; 1848c2ecf20Sopenharmony_ci unsigned int seq; 1858c2ecf20Sopenharmony_ci int err; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ciagain: 1888c2ecf20Sopenharmony_ci rcu_read_lock(); 1898c2ecf20Sopenharmony_ci list_for_each_entry_rcu(ctx, &nfsi->open_files, list) { 1908c2ecf20Sopenharmony_ci state = ctx->state; 1918c2ecf20Sopenharmony_ci if (state == NULL) 1928c2ecf20Sopenharmony_ci continue; 1938c2ecf20Sopenharmony_ci if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) 1948c2ecf20Sopenharmony_ci continue; 1958c2ecf20Sopenharmony_ci if (!nfs4_valid_open_stateid(state)) 1968c2ecf20Sopenharmony_ci continue; 1978c2ecf20Sopenharmony_ci if (!nfs4_stateid_match(&state->stateid, stateid)) 1988c2ecf20Sopenharmony_ci continue; 1998c2ecf20Sopenharmony_ci if (!get_nfs_open_context(ctx)) 2008c2ecf20Sopenharmony_ci continue; 2018c2ecf20Sopenharmony_ci rcu_read_unlock(); 2028c2ecf20Sopenharmony_ci sp = state->owner; 2038c2ecf20Sopenharmony_ci /* Block nfs4_proc_unlck */ 2048c2ecf20Sopenharmony_ci mutex_lock(&sp->so_delegreturn_mutex); 2058c2ecf20Sopenharmony_ci seq = raw_seqcount_begin(&sp->so_reclaim_seqcount); 2068c2ecf20Sopenharmony_ci err = nfs4_open_delegation_recall(ctx, state, stateid); 2078c2ecf20Sopenharmony_ci if (!err) 2088c2ecf20Sopenharmony_ci err = nfs_delegation_claim_locks(state, stateid); 2098c2ecf20Sopenharmony_ci if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) 2108c2ecf20Sopenharmony_ci err = -EAGAIN; 2118c2ecf20Sopenharmony_ci mutex_unlock(&sp->so_delegreturn_mutex); 2128c2ecf20Sopenharmony_ci put_nfs_open_context(ctx); 2138c2ecf20Sopenharmony_ci if (err != 0) 2148c2ecf20Sopenharmony_ci return err; 2158c2ecf20Sopenharmony_ci goto again; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci rcu_read_unlock(); 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/** 2228c2ecf20Sopenharmony_ci * nfs_inode_reclaim_delegation - process a delegation reclaim request 2238c2ecf20Sopenharmony_ci * @inode: inode to process 2248c2ecf20Sopenharmony_ci * @cred: credential to use for request 2258c2ecf20Sopenharmony_ci * @type: delegation type 2268c2ecf20Sopenharmony_ci * @stateid: delegation stateid 2278c2ecf20Sopenharmony_ci * @pagemod_limit: write delegation "space_limit" 2288c2ecf20Sopenharmony_ci * 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_civoid nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred, 2318c2ecf20Sopenharmony_ci fmode_t type, const nfs4_stateid *stateid, 2328c2ecf20Sopenharmony_ci unsigned long pagemod_limit) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 2358c2ecf20Sopenharmony_ci const struct cred *oldcred = NULL; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci rcu_read_lock(); 2388c2ecf20Sopenharmony_ci delegation = rcu_dereference(NFS_I(inode)->delegation); 2398c2ecf20Sopenharmony_ci if (delegation != NULL) { 2408c2ecf20Sopenharmony_ci spin_lock(&delegation->lock); 2418c2ecf20Sopenharmony_ci nfs4_stateid_copy(&delegation->stateid, stateid); 2428c2ecf20Sopenharmony_ci delegation->type = type; 2438c2ecf20Sopenharmony_ci delegation->pagemod_limit = pagemod_limit; 2448c2ecf20Sopenharmony_ci oldcred = delegation->cred; 2458c2ecf20Sopenharmony_ci delegation->cred = get_cred(cred); 2468c2ecf20Sopenharmony_ci clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 2478c2ecf20Sopenharmony_ci if (test_and_clear_bit(NFS_DELEGATION_REVOKED, 2488c2ecf20Sopenharmony_ci &delegation->flags)) 2498c2ecf20Sopenharmony_ci atomic_long_inc(&nfs_active_delegations); 2508c2ecf20Sopenharmony_ci spin_unlock(&delegation->lock); 2518c2ecf20Sopenharmony_ci rcu_read_unlock(); 2528c2ecf20Sopenharmony_ci put_cred(oldcred); 2538c2ecf20Sopenharmony_ci trace_nfs4_reclaim_delegation(inode, type); 2548c2ecf20Sopenharmony_ci } else { 2558c2ecf20Sopenharmony_ci rcu_read_unlock(); 2568c2ecf20Sopenharmony_ci nfs_inode_set_delegation(inode, cred, type, stateid, 2578c2ecf20Sopenharmony_ci pagemod_limit); 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci const struct cred *cred; 2648c2ecf20Sopenharmony_ci int res = 0; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { 2678c2ecf20Sopenharmony_ci spin_lock(&delegation->lock); 2688c2ecf20Sopenharmony_ci cred = get_cred(delegation->cred); 2698c2ecf20Sopenharmony_ci spin_unlock(&delegation->lock); 2708c2ecf20Sopenharmony_ci res = nfs4_proc_delegreturn(inode, cred, 2718c2ecf20Sopenharmony_ci &delegation->stateid, 2728c2ecf20Sopenharmony_ci issync); 2738c2ecf20Sopenharmony_ci put_cred(cred); 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci return res; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct inode *inode = NULL; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci spin_lock(&delegation->lock); 2838c2ecf20Sopenharmony_ci if (delegation->inode != NULL) 2848c2ecf20Sopenharmony_ci inode = igrab(delegation->inode); 2858c2ecf20Sopenharmony_ci if (!inode) 2868c2ecf20Sopenharmony_ci set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags); 2878c2ecf20Sopenharmony_ci spin_unlock(&delegation->lock); 2888c2ecf20Sopenharmony_ci return inode; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic struct nfs_delegation * 2928c2ecf20Sopenharmony_cinfs_start_delegation_return_locked(struct nfs_inode *nfsi) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct nfs_delegation *ret = NULL; 2958c2ecf20Sopenharmony_ci struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (delegation == NULL) 2988c2ecf20Sopenharmony_ci goto out; 2998c2ecf20Sopenharmony_ci spin_lock(&delegation->lock); 3008c2ecf20Sopenharmony_ci if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { 3018c2ecf20Sopenharmony_ci clear_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); 3028c2ecf20Sopenharmony_ci /* Refcount matched in nfs_end_delegation_return() */ 3038c2ecf20Sopenharmony_ci ret = nfs_get_delegation(delegation); 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci spin_unlock(&delegation->lock); 3068c2ecf20Sopenharmony_ci if (ret) 3078c2ecf20Sopenharmony_ci nfs_clear_verifier_delegated(&nfsi->vfs_inode); 3088c2ecf20Sopenharmony_ciout: 3098c2ecf20Sopenharmony_ci return ret; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic struct nfs_delegation * 3138c2ecf20Sopenharmony_cinfs_start_delegation_return(struct nfs_inode *nfsi) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci rcu_read_lock(); 3188c2ecf20Sopenharmony_ci delegation = nfs_start_delegation_return_locked(nfsi); 3198c2ecf20Sopenharmony_ci rcu_read_unlock(); 3208c2ecf20Sopenharmony_ci return delegation; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic void nfs_abort_delegation_return(struct nfs_delegation *delegation, 3248c2ecf20Sopenharmony_ci struct nfs_client *clp, int err) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci spin_lock(&delegation->lock); 3288c2ecf20Sopenharmony_ci clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 3298c2ecf20Sopenharmony_ci if (err == -EAGAIN) { 3308c2ecf20Sopenharmony_ci set_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags); 3318c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_DELEGRETURN_DELAYED, &clp->cl_state); 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci spin_unlock(&delegation->lock); 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic struct nfs_delegation * 3378c2ecf20Sopenharmony_cinfs_detach_delegation_locked(struct nfs_inode *nfsi, 3388c2ecf20Sopenharmony_ci struct nfs_delegation *delegation, 3398c2ecf20Sopenharmony_ci struct nfs_client *clp) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct nfs_delegation *deleg_cur = 3428c2ecf20Sopenharmony_ci rcu_dereference_protected(nfsi->delegation, 3438c2ecf20Sopenharmony_ci lockdep_is_held(&clp->cl_lock)); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (deleg_cur == NULL || delegation != deleg_cur) 3468c2ecf20Sopenharmony_ci return NULL; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci spin_lock(&delegation->lock); 3498c2ecf20Sopenharmony_ci if (!delegation->inode) { 3508c2ecf20Sopenharmony_ci spin_unlock(&delegation->lock); 3518c2ecf20Sopenharmony_ci return NULL; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci list_del_rcu(&delegation->super_list); 3548c2ecf20Sopenharmony_ci delegation->inode = NULL; 3558c2ecf20Sopenharmony_ci rcu_assign_pointer(nfsi->delegation, NULL); 3568c2ecf20Sopenharmony_ci spin_unlock(&delegation->lock); 3578c2ecf20Sopenharmony_ci return delegation; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, 3618c2ecf20Sopenharmony_ci struct nfs_delegation *delegation, 3628c2ecf20Sopenharmony_ci struct nfs_server *server) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci spin_lock(&clp->cl_lock); 3678c2ecf20Sopenharmony_ci delegation = nfs_detach_delegation_locked(nfsi, delegation, clp); 3688c2ecf20Sopenharmony_ci spin_unlock(&clp->cl_lock); 3698c2ecf20Sopenharmony_ci return delegation; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic struct nfs_delegation * 3738c2ecf20Sopenharmony_cinfs_inode_detach_delegation(struct inode *inode) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 3768c2ecf20Sopenharmony_ci struct nfs_server *server = NFS_SERVER(inode); 3778c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci rcu_read_lock(); 3808c2ecf20Sopenharmony_ci delegation = rcu_dereference(nfsi->delegation); 3818c2ecf20Sopenharmony_ci if (delegation != NULL) 3828c2ecf20Sopenharmony_ci delegation = nfs_detach_delegation(nfsi, delegation, server); 3838c2ecf20Sopenharmony_ci rcu_read_unlock(); 3848c2ecf20Sopenharmony_ci return delegation; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic void 3888c2ecf20Sopenharmony_cinfs_update_delegation_cred(struct nfs_delegation *delegation, 3898c2ecf20Sopenharmony_ci const struct cred *cred) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci const struct cred *old; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (cred_fscmp(delegation->cred, cred) != 0) { 3948c2ecf20Sopenharmony_ci old = xchg(&delegation->cred, get_cred(cred)); 3958c2ecf20Sopenharmony_ci put_cred(old); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic void 4008c2ecf20Sopenharmony_cinfs_update_inplace_delegation(struct nfs_delegation *delegation, 4018c2ecf20Sopenharmony_ci const struct nfs_delegation *update) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci if (nfs4_stateid_is_newer(&update->stateid, &delegation->stateid)) { 4048c2ecf20Sopenharmony_ci delegation->stateid.seqid = update->stateid.seqid; 4058c2ecf20Sopenharmony_ci smp_wmb(); 4068c2ecf20Sopenharmony_ci delegation->type = update->type; 4078c2ecf20Sopenharmony_ci delegation->pagemod_limit = update->pagemod_limit; 4088c2ecf20Sopenharmony_ci if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { 4098c2ecf20Sopenharmony_ci delegation->change_attr = update->change_attr; 4108c2ecf20Sopenharmony_ci nfs_update_delegation_cred(delegation, update->cred); 4118c2ecf20Sopenharmony_ci /* smp_mb__before_atomic() is implicit due to xchg() */ 4128c2ecf20Sopenharmony_ci clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags); 4138c2ecf20Sopenharmony_ci atomic_long_inc(&nfs_active_delegations); 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci/** 4198c2ecf20Sopenharmony_ci * nfs_inode_set_delegation - set up a delegation on an inode 4208c2ecf20Sopenharmony_ci * @inode: inode to which delegation applies 4218c2ecf20Sopenharmony_ci * @cred: cred to use for subsequent delegation processing 4228c2ecf20Sopenharmony_ci * @type: delegation type 4238c2ecf20Sopenharmony_ci * @stateid: delegation stateid 4248c2ecf20Sopenharmony_ci * @pagemod_limit: write delegation "space_limit" 4258c2ecf20Sopenharmony_ci * 4268c2ecf20Sopenharmony_ci * Returns zero on success, or a negative errno value. 4278c2ecf20Sopenharmony_ci */ 4288c2ecf20Sopenharmony_ciint nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, 4298c2ecf20Sopenharmony_ci fmode_t type, 4308c2ecf20Sopenharmony_ci const nfs4_stateid *stateid, 4318c2ecf20Sopenharmony_ci unsigned long pagemod_limit) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct nfs_server *server = NFS_SERVER(inode); 4348c2ecf20Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 4358c2ecf20Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 4368c2ecf20Sopenharmony_ci struct nfs_delegation *delegation, *old_delegation; 4378c2ecf20Sopenharmony_ci struct nfs_delegation *freeme = NULL; 4388c2ecf20Sopenharmony_ci int status = 0; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci delegation = kmalloc(sizeof(*delegation), GFP_NOFS); 4418c2ecf20Sopenharmony_ci if (delegation == NULL) 4428c2ecf20Sopenharmony_ci return -ENOMEM; 4438c2ecf20Sopenharmony_ci nfs4_stateid_copy(&delegation->stateid, stateid); 4448c2ecf20Sopenharmony_ci refcount_set(&delegation->refcount, 1); 4458c2ecf20Sopenharmony_ci delegation->type = type; 4468c2ecf20Sopenharmony_ci delegation->pagemod_limit = pagemod_limit; 4478c2ecf20Sopenharmony_ci delegation->change_attr = inode_peek_iversion_raw(inode); 4488c2ecf20Sopenharmony_ci delegation->cred = get_cred(cred); 4498c2ecf20Sopenharmony_ci delegation->inode = inode; 4508c2ecf20Sopenharmony_ci delegation->flags = 1<<NFS_DELEGATION_REFERENCED; 4518c2ecf20Sopenharmony_ci spin_lock_init(&delegation->lock); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci spin_lock(&clp->cl_lock); 4548c2ecf20Sopenharmony_ci old_delegation = rcu_dereference_protected(nfsi->delegation, 4558c2ecf20Sopenharmony_ci lockdep_is_held(&clp->cl_lock)); 4568c2ecf20Sopenharmony_ci if (old_delegation == NULL) 4578c2ecf20Sopenharmony_ci goto add_new; 4588c2ecf20Sopenharmony_ci /* Is this an update of the existing delegation? */ 4598c2ecf20Sopenharmony_ci if (nfs4_stateid_match_other(&old_delegation->stateid, 4608c2ecf20Sopenharmony_ci &delegation->stateid)) { 4618c2ecf20Sopenharmony_ci spin_lock(&old_delegation->lock); 4628c2ecf20Sopenharmony_ci nfs_update_inplace_delegation(old_delegation, 4638c2ecf20Sopenharmony_ci delegation); 4648c2ecf20Sopenharmony_ci spin_unlock(&old_delegation->lock); 4658c2ecf20Sopenharmony_ci goto out; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci if (!test_bit(NFS_DELEGATION_REVOKED, &old_delegation->flags)) { 4688c2ecf20Sopenharmony_ci /* 4698c2ecf20Sopenharmony_ci * Deal with broken servers that hand out two 4708c2ecf20Sopenharmony_ci * delegations for the same file. 4718c2ecf20Sopenharmony_ci * Allow for upgrades to a WRITE delegation, but 4728c2ecf20Sopenharmony_ci * nothing else. 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_ci dfprintk(FILE, "%s: server %s handed out " 4758c2ecf20Sopenharmony_ci "a duplicate delegation!\n", 4768c2ecf20Sopenharmony_ci __func__, clp->cl_hostname); 4778c2ecf20Sopenharmony_ci if (delegation->type == old_delegation->type || 4788c2ecf20Sopenharmony_ci !(delegation->type & FMODE_WRITE)) { 4798c2ecf20Sopenharmony_ci freeme = delegation; 4808c2ecf20Sopenharmony_ci delegation = NULL; 4818c2ecf20Sopenharmony_ci goto out; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci if (test_and_set_bit(NFS_DELEGATION_RETURNING, 4848c2ecf20Sopenharmony_ci &old_delegation->flags)) 4858c2ecf20Sopenharmony_ci goto out; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci freeme = nfs_detach_delegation_locked(nfsi, old_delegation, clp); 4888c2ecf20Sopenharmony_ci if (freeme == NULL) 4898c2ecf20Sopenharmony_ci goto out; 4908c2ecf20Sopenharmony_ciadd_new: 4918c2ecf20Sopenharmony_ci list_add_tail_rcu(&delegation->super_list, &server->delegations); 4928c2ecf20Sopenharmony_ci rcu_assign_pointer(nfsi->delegation, delegation); 4938c2ecf20Sopenharmony_ci delegation = NULL; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci atomic_long_inc(&nfs_active_delegations); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci trace_nfs4_set_delegation(inode, type); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci spin_lock(&inode->i_lock); 5008c2ecf20Sopenharmony_ci if (NFS_I(inode)->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME)) 5018c2ecf20Sopenharmony_ci NFS_I(inode)->cache_validity |= NFS_INO_REVAL_FORCED; 5028c2ecf20Sopenharmony_ci spin_unlock(&inode->i_lock); 5038c2ecf20Sopenharmony_ciout: 5048c2ecf20Sopenharmony_ci spin_unlock(&clp->cl_lock); 5058c2ecf20Sopenharmony_ci if (delegation != NULL) 5068c2ecf20Sopenharmony_ci __nfs_free_delegation(delegation); 5078c2ecf20Sopenharmony_ci if (freeme != NULL) { 5088c2ecf20Sopenharmony_ci nfs_do_return_delegation(inode, freeme, 0); 5098c2ecf20Sopenharmony_ci nfs_free_delegation(freeme); 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci return status; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci/* 5158c2ecf20Sopenharmony_ci * Basic procedure for returning a delegation to the server 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_cistatic int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 5208c2ecf20Sopenharmony_ci int err = 0; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (delegation == NULL) 5238c2ecf20Sopenharmony_ci return 0; 5248c2ecf20Sopenharmony_ci do { 5258c2ecf20Sopenharmony_ci if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) 5268c2ecf20Sopenharmony_ci break; 5278c2ecf20Sopenharmony_ci err = nfs_delegation_claim_opens(inode, &delegation->stateid, 5288c2ecf20Sopenharmony_ci delegation->type); 5298c2ecf20Sopenharmony_ci if (!issync || err != -EAGAIN) 5308c2ecf20Sopenharmony_ci break; 5318c2ecf20Sopenharmony_ci /* 5328c2ecf20Sopenharmony_ci * Guard against state recovery 5338c2ecf20Sopenharmony_ci */ 5348c2ecf20Sopenharmony_ci err = nfs4_wait_clnt_recover(clp); 5358c2ecf20Sopenharmony_ci } while (err == 0); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (err) { 5388c2ecf20Sopenharmony_ci nfs_abort_delegation_return(delegation, clp, err); 5398c2ecf20Sopenharmony_ci goto out; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci err = nfs_do_return_delegation(inode, delegation, issync); 5438c2ecf20Sopenharmony_ciout: 5448c2ecf20Sopenharmony_ci /* Refcount matched in nfs_start_delegation_return_locked() */ 5458c2ecf20Sopenharmony_ci nfs_put_delegation(delegation); 5468c2ecf20Sopenharmony_ci return err; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic bool nfs_delegation_need_return(struct nfs_delegation *delegation) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci bool ret = false; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) 5548c2ecf20Sopenharmony_ci ret = true; 5558c2ecf20Sopenharmony_ci else if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) { 5568c2ecf20Sopenharmony_ci struct inode *inode; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci spin_lock(&delegation->lock); 5598c2ecf20Sopenharmony_ci inode = delegation->inode; 5608c2ecf20Sopenharmony_ci if (inode && list_empty(&NFS_I(inode)->open_files)) 5618c2ecf20Sopenharmony_ci ret = true; 5628c2ecf20Sopenharmony_ci spin_unlock(&delegation->lock); 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci if (ret) 5658c2ecf20Sopenharmony_ci clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); 5668c2ecf20Sopenharmony_ci if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) || 5678c2ecf20Sopenharmony_ci test_bit(NFS_DELEGATION_RETURN_DELAYED, &delegation->flags) || 5688c2ecf20Sopenharmony_ci test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) 5698c2ecf20Sopenharmony_ci ret = false; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci return ret; 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic int nfs_server_return_marked_delegations(struct nfs_server *server, 5758c2ecf20Sopenharmony_ci void __always_unused *data) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 5788c2ecf20Sopenharmony_ci struct nfs_delegation *prev; 5798c2ecf20Sopenharmony_ci struct inode *inode; 5808c2ecf20Sopenharmony_ci struct inode *place_holder = NULL; 5818c2ecf20Sopenharmony_ci struct nfs_delegation *place_holder_deleg = NULL; 5828c2ecf20Sopenharmony_ci int err = 0; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cirestart: 5858c2ecf20Sopenharmony_ci /* 5868c2ecf20Sopenharmony_ci * To avoid quadratic looping we hold a reference 5878c2ecf20Sopenharmony_ci * to an inode place_holder. Each time we restart, we 5888c2ecf20Sopenharmony_ci * list delegation in the server from the delegations 5898c2ecf20Sopenharmony_ci * of that inode. 5908c2ecf20Sopenharmony_ci * prev is an RCU-protected pointer to a delegation which 5918c2ecf20Sopenharmony_ci * wasn't marked for return and might be a good choice for 5928c2ecf20Sopenharmony_ci * the next place_holder. 5938c2ecf20Sopenharmony_ci */ 5948c2ecf20Sopenharmony_ci prev = NULL; 5958c2ecf20Sopenharmony_ci delegation = NULL; 5968c2ecf20Sopenharmony_ci rcu_read_lock(); 5978c2ecf20Sopenharmony_ci if (place_holder) 5988c2ecf20Sopenharmony_ci delegation = rcu_dereference(NFS_I(place_holder)->delegation); 5998c2ecf20Sopenharmony_ci if (!delegation || delegation != place_holder_deleg) 6008c2ecf20Sopenharmony_ci delegation = list_entry_rcu(server->delegations.next, 6018c2ecf20Sopenharmony_ci struct nfs_delegation, super_list); 6028c2ecf20Sopenharmony_ci list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) { 6038c2ecf20Sopenharmony_ci struct inode *to_put = NULL; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (test_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags)) 6068c2ecf20Sopenharmony_ci continue; 6078c2ecf20Sopenharmony_ci if (!nfs_delegation_need_return(delegation)) { 6088c2ecf20Sopenharmony_ci if (nfs4_is_valid_delegation(delegation, 0)) 6098c2ecf20Sopenharmony_ci prev = delegation; 6108c2ecf20Sopenharmony_ci continue; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (prev) { 6148c2ecf20Sopenharmony_ci struct inode *tmp = nfs_delegation_grab_inode(prev); 6158c2ecf20Sopenharmony_ci if (tmp) { 6168c2ecf20Sopenharmony_ci to_put = place_holder; 6178c2ecf20Sopenharmony_ci place_holder = tmp; 6188c2ecf20Sopenharmony_ci place_holder_deleg = prev; 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci inode = nfs_delegation_grab_inode(delegation); 6238c2ecf20Sopenharmony_ci if (inode == NULL) { 6248c2ecf20Sopenharmony_ci rcu_read_unlock(); 6258c2ecf20Sopenharmony_ci iput(to_put); 6268c2ecf20Sopenharmony_ci goto restart; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci delegation = nfs_start_delegation_return_locked(NFS_I(inode)); 6298c2ecf20Sopenharmony_ci rcu_read_unlock(); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci iput(to_put); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci err = nfs_end_delegation_return(inode, delegation, 0); 6348c2ecf20Sopenharmony_ci iput(inode); 6358c2ecf20Sopenharmony_ci cond_resched(); 6368c2ecf20Sopenharmony_ci if (!err) 6378c2ecf20Sopenharmony_ci goto restart; 6388c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 6398c2ecf20Sopenharmony_ci goto out; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci rcu_read_unlock(); 6428c2ecf20Sopenharmony_ciout: 6438c2ecf20Sopenharmony_ci iput(place_holder); 6448c2ecf20Sopenharmony_ci return err; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic bool nfs_server_clear_delayed_delegations(struct nfs_server *server) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct nfs_delegation *d; 6508c2ecf20Sopenharmony_ci bool ret = false; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci list_for_each_entry_rcu (d, &server->delegations, super_list) { 6538c2ecf20Sopenharmony_ci if (!test_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags)) 6548c2ecf20Sopenharmony_ci continue; 6558c2ecf20Sopenharmony_ci nfs_mark_return_delegation(server, d); 6568c2ecf20Sopenharmony_ci clear_bit(NFS_DELEGATION_RETURN_DELAYED, &d->flags); 6578c2ecf20Sopenharmony_ci ret = true; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci return ret; 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_cistatic bool nfs_client_clear_delayed_delegations(struct nfs_client *clp) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci struct nfs_server *server; 6658c2ecf20Sopenharmony_ci bool ret = false; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (!test_and_clear_bit(NFS4CLNT_DELEGRETURN_DELAYED, &clp->cl_state)) 6688c2ecf20Sopenharmony_ci goto out; 6698c2ecf20Sopenharmony_ci rcu_read_lock(); 6708c2ecf20Sopenharmony_ci list_for_each_entry_rcu (server, &clp->cl_superblocks, client_link) { 6718c2ecf20Sopenharmony_ci if (nfs_server_clear_delayed_delegations(server)) 6728c2ecf20Sopenharmony_ci ret = true; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci rcu_read_unlock(); 6758c2ecf20Sopenharmony_ciout: 6768c2ecf20Sopenharmony_ci return ret; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci/** 6808c2ecf20Sopenharmony_ci * nfs_client_return_marked_delegations - return previously marked delegations 6818c2ecf20Sopenharmony_ci * @clp: nfs_client to process 6828c2ecf20Sopenharmony_ci * 6838c2ecf20Sopenharmony_ci * Note that this function is designed to be called by the state 6848c2ecf20Sopenharmony_ci * manager thread. For this reason, it cannot flush the dirty data, 6858c2ecf20Sopenharmony_ci * since that could deadlock in case of a state recovery error. 6868c2ecf20Sopenharmony_ci * 6878c2ecf20Sopenharmony_ci * Returns zero on success, or a negative errno value. 6888c2ecf20Sopenharmony_ci */ 6898c2ecf20Sopenharmony_ciint nfs_client_return_marked_delegations(struct nfs_client *clp) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci int err = nfs_client_for_each_server( 6928c2ecf20Sopenharmony_ci clp, nfs_server_return_marked_delegations, NULL); 6938c2ecf20Sopenharmony_ci if (err) 6948c2ecf20Sopenharmony_ci return err; 6958c2ecf20Sopenharmony_ci /* If a return was delayed, sleep to prevent hard looping */ 6968c2ecf20Sopenharmony_ci if (nfs_client_clear_delayed_delegations(clp)) 6978c2ecf20Sopenharmony_ci ssleep(1); 6988c2ecf20Sopenharmony_ci return 0; 6998c2ecf20Sopenharmony_ci} 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci/** 7028c2ecf20Sopenharmony_ci * nfs_inode_evict_delegation - return delegation, don't reclaim opens 7038c2ecf20Sopenharmony_ci * @inode: inode to process 7048c2ecf20Sopenharmony_ci * 7058c2ecf20Sopenharmony_ci * Does not protect against delegation reclaims, therefore really only safe 7068c2ecf20Sopenharmony_ci * to be called from nfs4_clear_inode(). Guaranteed to always free 7078c2ecf20Sopenharmony_ci * the delegation structure. 7088c2ecf20Sopenharmony_ci */ 7098c2ecf20Sopenharmony_civoid nfs_inode_evict_delegation(struct inode *inode) 7108c2ecf20Sopenharmony_ci{ 7118c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci delegation = nfs_inode_detach_delegation(inode); 7148c2ecf20Sopenharmony_ci if (delegation != NULL) { 7158c2ecf20Sopenharmony_ci set_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 7168c2ecf20Sopenharmony_ci set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags); 7178c2ecf20Sopenharmony_ci nfs_do_return_delegation(inode, delegation, 1); 7188c2ecf20Sopenharmony_ci nfs_free_delegation(delegation); 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci} 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci/** 7238c2ecf20Sopenharmony_ci * nfs_inode_return_delegation - synchronously return a delegation 7248c2ecf20Sopenharmony_ci * @inode: inode to process 7258c2ecf20Sopenharmony_ci * 7268c2ecf20Sopenharmony_ci * This routine will always flush any dirty data to disk on the 7278c2ecf20Sopenharmony_ci * assumption that if we need to return the delegation, then 7288c2ecf20Sopenharmony_ci * we should stop caching. 7298c2ecf20Sopenharmony_ci * 7308c2ecf20Sopenharmony_ci * Returns zero on success, or a negative errno value. 7318c2ecf20Sopenharmony_ci */ 7328c2ecf20Sopenharmony_ciint nfs4_inode_return_delegation(struct inode *inode) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 7358c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 7368c2ecf20Sopenharmony_ci int err = 0; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci nfs_wb_all(inode); 7398c2ecf20Sopenharmony_ci delegation = nfs_start_delegation_return(nfsi); 7408c2ecf20Sopenharmony_ci if (delegation != NULL) 7418c2ecf20Sopenharmony_ci err = nfs_end_delegation_return(inode, delegation, 1); 7428c2ecf20Sopenharmony_ci return err; 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci/** 7468c2ecf20Sopenharmony_ci * nfs_inode_return_delegation_on_close - asynchronously return a delegation 7478c2ecf20Sopenharmony_ci * @inode: inode to process 7488c2ecf20Sopenharmony_ci * 7498c2ecf20Sopenharmony_ci * This routine is called on file close in order to determine if the 7508c2ecf20Sopenharmony_ci * inode delegation needs to be returned immediately. 7518c2ecf20Sopenharmony_ci */ 7528c2ecf20Sopenharmony_civoid nfs4_inode_return_delegation_on_close(struct inode *inode) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 7558c2ecf20Sopenharmony_ci struct nfs_delegation *ret = NULL; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if (!inode) 7588c2ecf20Sopenharmony_ci return; 7598c2ecf20Sopenharmony_ci rcu_read_lock(); 7608c2ecf20Sopenharmony_ci delegation = nfs4_get_valid_delegation(inode); 7618c2ecf20Sopenharmony_ci if (!delegation) 7628c2ecf20Sopenharmony_ci goto out; 7638c2ecf20Sopenharmony_ci if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) || 7648c2ecf20Sopenharmony_ci atomic_long_read(&nfs_active_delegations) >= nfs_delegation_watermark) { 7658c2ecf20Sopenharmony_ci spin_lock(&delegation->lock); 7668c2ecf20Sopenharmony_ci if (delegation->inode && 7678c2ecf20Sopenharmony_ci list_empty(&NFS_I(inode)->open_files) && 7688c2ecf20Sopenharmony_ci !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { 7698c2ecf20Sopenharmony_ci clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); 7708c2ecf20Sopenharmony_ci /* Refcount matched in nfs_end_delegation_return() */ 7718c2ecf20Sopenharmony_ci ret = nfs_get_delegation(delegation); 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci spin_unlock(&delegation->lock); 7748c2ecf20Sopenharmony_ci if (ret) 7758c2ecf20Sopenharmony_ci nfs_clear_verifier_delegated(inode); 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ciout: 7788c2ecf20Sopenharmony_ci rcu_read_unlock(); 7798c2ecf20Sopenharmony_ci nfs_end_delegation_return(inode, ret, 0); 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci/** 7838c2ecf20Sopenharmony_ci * nfs4_inode_make_writeable 7848c2ecf20Sopenharmony_ci * @inode: pointer to inode 7858c2ecf20Sopenharmony_ci * 7868c2ecf20Sopenharmony_ci * Make the inode writeable by returning the delegation if necessary 7878c2ecf20Sopenharmony_ci * 7888c2ecf20Sopenharmony_ci * Returns zero on success, or a negative errno value. 7898c2ecf20Sopenharmony_ci */ 7908c2ecf20Sopenharmony_ciint nfs4_inode_make_writeable(struct inode *inode) 7918c2ecf20Sopenharmony_ci{ 7928c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci rcu_read_lock(); 7958c2ecf20Sopenharmony_ci delegation = nfs4_get_valid_delegation(inode); 7968c2ecf20Sopenharmony_ci if (delegation == NULL || 7978c2ecf20Sopenharmony_ci (nfs4_has_session(NFS_SERVER(inode)->nfs_client) && 7988c2ecf20Sopenharmony_ci (delegation->type & FMODE_WRITE))) { 7998c2ecf20Sopenharmony_ci rcu_read_unlock(); 8008c2ecf20Sopenharmony_ci return 0; 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci rcu_read_unlock(); 8038c2ecf20Sopenharmony_ci return nfs4_inode_return_delegation(inode); 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cistatic void nfs_mark_return_if_closed_delegation(struct nfs_server *server, 8078c2ecf20Sopenharmony_ci struct nfs_delegation *delegation) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); 8108c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic bool nfs_server_mark_return_all_delegations(struct nfs_server *server) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 8168c2ecf20Sopenharmony_ci bool ret = false; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 8198c2ecf20Sopenharmony_ci nfs_mark_return_delegation(server, delegation); 8208c2ecf20Sopenharmony_ci ret = true; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci return ret; 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistatic void nfs_client_mark_return_all_delegations(struct nfs_client *clp) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci struct nfs_server *server; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci rcu_read_lock(); 8308c2ecf20Sopenharmony_ci list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 8318c2ecf20Sopenharmony_ci nfs_server_mark_return_all_delegations(server); 8328c2ecf20Sopenharmony_ci rcu_read_unlock(); 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic void nfs_delegation_run_state_manager(struct nfs_client *clp) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) 8388c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 8398c2ecf20Sopenharmony_ci} 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci/** 8428c2ecf20Sopenharmony_ci * nfs_expire_all_delegations 8438c2ecf20Sopenharmony_ci * @clp: client to process 8448c2ecf20Sopenharmony_ci * 8458c2ecf20Sopenharmony_ci */ 8468c2ecf20Sopenharmony_civoid nfs_expire_all_delegations(struct nfs_client *clp) 8478c2ecf20Sopenharmony_ci{ 8488c2ecf20Sopenharmony_ci nfs_client_mark_return_all_delegations(clp); 8498c2ecf20Sopenharmony_ci nfs_delegation_run_state_manager(clp); 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci/** 8538c2ecf20Sopenharmony_ci * nfs_super_return_all_delegations - return delegations for one superblock 8548c2ecf20Sopenharmony_ci * @server: pointer to nfs_server to process 8558c2ecf20Sopenharmony_ci * 8568c2ecf20Sopenharmony_ci */ 8578c2ecf20Sopenharmony_civoid nfs_server_return_all_delegations(struct nfs_server *server) 8588c2ecf20Sopenharmony_ci{ 8598c2ecf20Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 8608c2ecf20Sopenharmony_ci bool need_wait; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci if (clp == NULL) 8638c2ecf20Sopenharmony_ci return; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci rcu_read_lock(); 8668c2ecf20Sopenharmony_ci need_wait = nfs_server_mark_return_all_delegations(server); 8678c2ecf20Sopenharmony_ci rcu_read_unlock(); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (need_wait) { 8708c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 8718c2ecf20Sopenharmony_ci nfs4_wait_clnt_recover(clp); 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistatic void nfs_mark_return_unused_delegation_types(struct nfs_server *server, 8768c2ecf20Sopenharmony_ci fmode_t flags) 8778c2ecf20Sopenharmony_ci{ 8788c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 8818c2ecf20Sopenharmony_ci if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) 8828c2ecf20Sopenharmony_ci continue; 8838c2ecf20Sopenharmony_ci if (delegation->type & flags) 8848c2ecf20Sopenharmony_ci nfs_mark_return_if_closed_delegation(server, delegation); 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci} 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cistatic void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp, 8898c2ecf20Sopenharmony_ci fmode_t flags) 8908c2ecf20Sopenharmony_ci{ 8918c2ecf20Sopenharmony_ci struct nfs_server *server; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci rcu_read_lock(); 8948c2ecf20Sopenharmony_ci list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 8958c2ecf20Sopenharmony_ci nfs_mark_return_unused_delegation_types(server, flags); 8968c2ecf20Sopenharmony_ci rcu_read_unlock(); 8978c2ecf20Sopenharmony_ci} 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic void nfs_revoke_delegation(struct inode *inode, 9008c2ecf20Sopenharmony_ci const nfs4_stateid *stateid) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 9038c2ecf20Sopenharmony_ci nfs4_stateid tmp; 9048c2ecf20Sopenharmony_ci bool ret = false; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci rcu_read_lock(); 9078c2ecf20Sopenharmony_ci delegation = rcu_dereference(NFS_I(inode)->delegation); 9088c2ecf20Sopenharmony_ci if (delegation == NULL) 9098c2ecf20Sopenharmony_ci goto out; 9108c2ecf20Sopenharmony_ci if (stateid == NULL) { 9118c2ecf20Sopenharmony_ci nfs4_stateid_copy(&tmp, &delegation->stateid); 9128c2ecf20Sopenharmony_ci stateid = &tmp; 9138c2ecf20Sopenharmony_ci } else { 9148c2ecf20Sopenharmony_ci if (!nfs4_stateid_match_other(stateid, &delegation->stateid)) 9158c2ecf20Sopenharmony_ci goto out; 9168c2ecf20Sopenharmony_ci spin_lock(&delegation->lock); 9178c2ecf20Sopenharmony_ci if (stateid->seqid) { 9188c2ecf20Sopenharmony_ci if (nfs4_stateid_is_newer(&delegation->stateid, stateid)) { 9198c2ecf20Sopenharmony_ci spin_unlock(&delegation->lock); 9208c2ecf20Sopenharmony_ci goto out; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci delegation->stateid.seqid = stateid->seqid; 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci spin_unlock(&delegation->lock); 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci nfs_mark_delegation_revoked(delegation); 9278c2ecf20Sopenharmony_ci ret = true; 9288c2ecf20Sopenharmony_ciout: 9298c2ecf20Sopenharmony_ci rcu_read_unlock(); 9308c2ecf20Sopenharmony_ci if (ret) 9318c2ecf20Sopenharmony_ci nfs_inode_find_state_and_recover(inode, stateid); 9328c2ecf20Sopenharmony_ci} 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_civoid nfs_remove_bad_delegation(struct inode *inode, 9358c2ecf20Sopenharmony_ci const nfs4_stateid *stateid) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci nfs_revoke_delegation(inode, stateid); 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_remove_bad_delegation); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_civoid nfs_delegation_mark_returned(struct inode *inode, 9428c2ecf20Sopenharmony_ci const nfs4_stateid *stateid) 9438c2ecf20Sopenharmony_ci{ 9448c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci if (!inode) 9478c2ecf20Sopenharmony_ci return; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci rcu_read_lock(); 9508c2ecf20Sopenharmony_ci delegation = rcu_dereference(NFS_I(inode)->delegation); 9518c2ecf20Sopenharmony_ci if (!delegation) 9528c2ecf20Sopenharmony_ci goto out_rcu_unlock; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci spin_lock(&delegation->lock); 9558c2ecf20Sopenharmony_ci if (!nfs4_stateid_match_other(stateid, &delegation->stateid)) 9568c2ecf20Sopenharmony_ci goto out_spin_unlock; 9578c2ecf20Sopenharmony_ci if (stateid->seqid) { 9588c2ecf20Sopenharmony_ci /* If delegation->stateid is newer, dont mark as returned */ 9598c2ecf20Sopenharmony_ci if (nfs4_stateid_is_newer(&delegation->stateid, stateid)) 9608c2ecf20Sopenharmony_ci goto out_clear_returning; 9618c2ecf20Sopenharmony_ci if (delegation->stateid.seqid != stateid->seqid) 9628c2ecf20Sopenharmony_ci delegation->stateid.seqid = stateid->seqid; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci nfs_mark_delegation_revoked(delegation); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ciout_clear_returning: 9688c2ecf20Sopenharmony_ci clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); 9698c2ecf20Sopenharmony_ciout_spin_unlock: 9708c2ecf20Sopenharmony_ci spin_unlock(&delegation->lock); 9718c2ecf20Sopenharmony_ciout_rcu_unlock: 9728c2ecf20Sopenharmony_ci rcu_read_unlock(); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci nfs_inode_find_state_and_recover(inode, stateid); 9758c2ecf20Sopenharmony_ci} 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci/** 9788c2ecf20Sopenharmony_ci * nfs_expire_unused_delegation_types 9798c2ecf20Sopenharmony_ci * @clp: client to process 9808c2ecf20Sopenharmony_ci * @flags: delegation types to expire 9818c2ecf20Sopenharmony_ci * 9828c2ecf20Sopenharmony_ci */ 9838c2ecf20Sopenharmony_civoid nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags) 9848c2ecf20Sopenharmony_ci{ 9858c2ecf20Sopenharmony_ci nfs_client_mark_return_unused_delegation_types(clp, flags); 9868c2ecf20Sopenharmony_ci nfs_delegation_run_state_manager(clp); 9878c2ecf20Sopenharmony_ci} 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_cistatic void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 9948c2ecf20Sopenharmony_ci if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) 9958c2ecf20Sopenharmony_ci continue; 9968c2ecf20Sopenharmony_ci nfs_mark_return_if_closed_delegation(server, delegation); 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci/** 10018c2ecf20Sopenharmony_ci * nfs_expire_unreferenced_delegations - Eliminate unused delegations 10028c2ecf20Sopenharmony_ci * @clp: nfs_client to process 10038c2ecf20Sopenharmony_ci * 10048c2ecf20Sopenharmony_ci */ 10058c2ecf20Sopenharmony_civoid nfs_expire_unreferenced_delegations(struct nfs_client *clp) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci struct nfs_server *server; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci rcu_read_lock(); 10108c2ecf20Sopenharmony_ci list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 10118c2ecf20Sopenharmony_ci nfs_mark_return_unreferenced_delegations(server); 10128c2ecf20Sopenharmony_ci rcu_read_unlock(); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci nfs_delegation_run_state_manager(clp); 10158c2ecf20Sopenharmony_ci} 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci/** 10188c2ecf20Sopenharmony_ci * nfs_async_inode_return_delegation - asynchronously return a delegation 10198c2ecf20Sopenharmony_ci * @inode: inode to process 10208c2ecf20Sopenharmony_ci * @stateid: state ID information 10218c2ecf20Sopenharmony_ci * 10228c2ecf20Sopenharmony_ci * Returns zero on success, or a negative errno value. 10238c2ecf20Sopenharmony_ci */ 10248c2ecf20Sopenharmony_ciint nfs_async_inode_return_delegation(struct inode *inode, 10258c2ecf20Sopenharmony_ci const nfs4_stateid *stateid) 10268c2ecf20Sopenharmony_ci{ 10278c2ecf20Sopenharmony_ci struct nfs_server *server = NFS_SERVER(inode); 10288c2ecf20Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 10298c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci rcu_read_lock(); 10328c2ecf20Sopenharmony_ci delegation = nfs4_get_valid_delegation(inode); 10338c2ecf20Sopenharmony_ci if (delegation == NULL) 10348c2ecf20Sopenharmony_ci goto out_enoent; 10358c2ecf20Sopenharmony_ci if (stateid != NULL && 10368c2ecf20Sopenharmony_ci !clp->cl_mvops->match_stateid(&delegation->stateid, stateid)) 10378c2ecf20Sopenharmony_ci goto out_enoent; 10388c2ecf20Sopenharmony_ci nfs_mark_return_delegation(server, delegation); 10398c2ecf20Sopenharmony_ci rcu_read_unlock(); 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci nfs_delegation_run_state_manager(clp); 10428c2ecf20Sopenharmony_ci return 0; 10438c2ecf20Sopenharmony_ciout_enoent: 10448c2ecf20Sopenharmony_ci rcu_read_unlock(); 10458c2ecf20Sopenharmony_ci return -ENOENT; 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_cistatic struct inode * 10498c2ecf20Sopenharmony_cinfs_delegation_find_inode_server(struct nfs_server *server, 10508c2ecf20Sopenharmony_ci const struct nfs_fh *fhandle) 10518c2ecf20Sopenharmony_ci{ 10528c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 10538c2ecf20Sopenharmony_ci struct super_block *freeme = NULL; 10548c2ecf20Sopenharmony_ci struct inode *res = NULL; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 10578c2ecf20Sopenharmony_ci spin_lock(&delegation->lock); 10588c2ecf20Sopenharmony_ci if (delegation->inode != NULL && 10598c2ecf20Sopenharmony_ci !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) && 10608c2ecf20Sopenharmony_ci nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { 10618c2ecf20Sopenharmony_ci if (nfs_sb_active(server->super)) { 10628c2ecf20Sopenharmony_ci freeme = server->super; 10638c2ecf20Sopenharmony_ci res = igrab(delegation->inode); 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci spin_unlock(&delegation->lock); 10668c2ecf20Sopenharmony_ci if (res != NULL) 10678c2ecf20Sopenharmony_ci return res; 10688c2ecf20Sopenharmony_ci if (freeme) { 10698c2ecf20Sopenharmony_ci rcu_read_unlock(); 10708c2ecf20Sopenharmony_ci nfs_sb_deactive(freeme); 10718c2ecf20Sopenharmony_ci rcu_read_lock(); 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci return ERR_PTR(-EAGAIN); 10748c2ecf20Sopenharmony_ci } 10758c2ecf20Sopenharmony_ci spin_unlock(&delegation->lock); 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 10788c2ecf20Sopenharmony_ci} 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci/** 10818c2ecf20Sopenharmony_ci * nfs_delegation_find_inode - retrieve the inode associated with a delegation 10828c2ecf20Sopenharmony_ci * @clp: client state handle 10838c2ecf20Sopenharmony_ci * @fhandle: filehandle from a delegation recall 10848c2ecf20Sopenharmony_ci * 10858c2ecf20Sopenharmony_ci * Returns pointer to inode matching "fhandle," or NULL if a matching inode 10868c2ecf20Sopenharmony_ci * cannot be found. 10878c2ecf20Sopenharmony_ci */ 10888c2ecf20Sopenharmony_cistruct inode *nfs_delegation_find_inode(struct nfs_client *clp, 10898c2ecf20Sopenharmony_ci const struct nfs_fh *fhandle) 10908c2ecf20Sopenharmony_ci{ 10918c2ecf20Sopenharmony_ci struct nfs_server *server; 10928c2ecf20Sopenharmony_ci struct inode *res; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci rcu_read_lock(); 10958c2ecf20Sopenharmony_ci list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 10968c2ecf20Sopenharmony_ci res = nfs_delegation_find_inode_server(server, fhandle); 10978c2ecf20Sopenharmony_ci if (res != ERR_PTR(-ENOENT)) { 10988c2ecf20Sopenharmony_ci rcu_read_unlock(); 10998c2ecf20Sopenharmony_ci return res; 11008c2ecf20Sopenharmony_ci } 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci rcu_read_unlock(); 11038c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cistatic void nfs_delegation_mark_reclaim_server(struct nfs_server *server) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 11118c2ecf20Sopenharmony_ci /* 11128c2ecf20Sopenharmony_ci * If the delegation may have been admin revoked, then we 11138c2ecf20Sopenharmony_ci * cannot reclaim it. 11148c2ecf20Sopenharmony_ci */ 11158c2ecf20Sopenharmony_ci if (test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags)) 11168c2ecf20Sopenharmony_ci continue; 11178c2ecf20Sopenharmony_ci set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci/** 11228c2ecf20Sopenharmony_ci * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed 11238c2ecf20Sopenharmony_ci * @clp: nfs_client to process 11248c2ecf20Sopenharmony_ci * 11258c2ecf20Sopenharmony_ci */ 11268c2ecf20Sopenharmony_civoid nfs_delegation_mark_reclaim(struct nfs_client *clp) 11278c2ecf20Sopenharmony_ci{ 11288c2ecf20Sopenharmony_ci struct nfs_server *server; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci rcu_read_lock(); 11318c2ecf20Sopenharmony_ci list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 11328c2ecf20Sopenharmony_ci nfs_delegation_mark_reclaim_server(server); 11338c2ecf20Sopenharmony_ci rcu_read_unlock(); 11348c2ecf20Sopenharmony_ci} 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_cistatic int nfs_server_reap_unclaimed_delegations(struct nfs_server *server, 11378c2ecf20Sopenharmony_ci void __always_unused *data) 11388c2ecf20Sopenharmony_ci{ 11398c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 11408c2ecf20Sopenharmony_ci struct inode *inode; 11418c2ecf20Sopenharmony_cirestart: 11428c2ecf20Sopenharmony_ci rcu_read_lock(); 11438c2ecf20Sopenharmony_cirestart_locked: 11448c2ecf20Sopenharmony_ci list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 11458c2ecf20Sopenharmony_ci if (test_bit(NFS_DELEGATION_INODE_FREEING, 11468c2ecf20Sopenharmony_ci &delegation->flags) || 11478c2ecf20Sopenharmony_ci test_bit(NFS_DELEGATION_RETURNING, 11488c2ecf20Sopenharmony_ci &delegation->flags) || 11498c2ecf20Sopenharmony_ci test_bit(NFS_DELEGATION_NEED_RECLAIM, 11508c2ecf20Sopenharmony_ci &delegation->flags) == 0) 11518c2ecf20Sopenharmony_ci continue; 11528c2ecf20Sopenharmony_ci inode = nfs_delegation_grab_inode(delegation); 11538c2ecf20Sopenharmony_ci if (inode == NULL) 11548c2ecf20Sopenharmony_ci goto restart_locked; 11558c2ecf20Sopenharmony_ci delegation = nfs_start_delegation_return_locked(NFS_I(inode)); 11568c2ecf20Sopenharmony_ci rcu_read_unlock(); 11578c2ecf20Sopenharmony_ci if (delegation != NULL) { 11588c2ecf20Sopenharmony_ci if (nfs_detach_delegation(NFS_I(inode), delegation, 11598c2ecf20Sopenharmony_ci server) != NULL) 11608c2ecf20Sopenharmony_ci nfs_free_delegation(delegation); 11618c2ecf20Sopenharmony_ci /* Match nfs_start_delegation_return_locked */ 11628c2ecf20Sopenharmony_ci nfs_put_delegation(delegation); 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci iput(inode); 11658c2ecf20Sopenharmony_ci cond_resched(); 11668c2ecf20Sopenharmony_ci goto restart; 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci rcu_read_unlock(); 11698c2ecf20Sopenharmony_ci return 0; 11708c2ecf20Sopenharmony_ci} 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci/** 11738c2ecf20Sopenharmony_ci * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done 11748c2ecf20Sopenharmony_ci * @clp: nfs_client to process 11758c2ecf20Sopenharmony_ci * 11768c2ecf20Sopenharmony_ci */ 11778c2ecf20Sopenharmony_civoid nfs_delegation_reap_unclaimed(struct nfs_client *clp) 11788c2ecf20Sopenharmony_ci{ 11798c2ecf20Sopenharmony_ci nfs_client_for_each_server(clp, nfs_server_reap_unclaimed_delegations, 11808c2ecf20Sopenharmony_ci NULL); 11818c2ecf20Sopenharmony_ci} 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_cistatic inline bool nfs4_server_rebooted(const struct nfs_client *clp) 11848c2ecf20Sopenharmony_ci{ 11858c2ecf20Sopenharmony_ci return (clp->cl_state & (BIT(NFS4CLNT_CHECK_LEASE) | 11868c2ecf20Sopenharmony_ci BIT(NFS4CLNT_LEASE_EXPIRED) | 11878c2ecf20Sopenharmony_ci BIT(NFS4CLNT_SESSION_RESET))) != 0; 11888c2ecf20Sopenharmony_ci} 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_cistatic void nfs_mark_test_expired_delegation(struct nfs_server *server, 11918c2ecf20Sopenharmony_ci struct nfs_delegation *delegation) 11928c2ecf20Sopenharmony_ci{ 11938c2ecf20Sopenharmony_ci if (delegation->stateid.type == NFS4_INVALID_STATEID_TYPE) 11948c2ecf20Sopenharmony_ci return; 11958c2ecf20Sopenharmony_ci clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 11968c2ecf20Sopenharmony_ci set_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); 11978c2ecf20Sopenharmony_ci set_bit(NFS4CLNT_DELEGATION_EXPIRED, &server->nfs_client->cl_state); 11988c2ecf20Sopenharmony_ci} 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_cistatic void nfs_inode_mark_test_expired_delegation(struct nfs_server *server, 12018c2ecf20Sopenharmony_ci struct inode *inode) 12028c2ecf20Sopenharmony_ci{ 12038c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci rcu_read_lock(); 12068c2ecf20Sopenharmony_ci delegation = rcu_dereference(NFS_I(inode)->delegation); 12078c2ecf20Sopenharmony_ci if (delegation) 12088c2ecf20Sopenharmony_ci nfs_mark_test_expired_delegation(server, delegation); 12098c2ecf20Sopenharmony_ci rcu_read_unlock(); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci} 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_cistatic void nfs_delegation_mark_test_expired_server(struct nfs_server *server) 12148c2ecf20Sopenharmony_ci{ 12158c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci list_for_each_entry_rcu(delegation, &server->delegations, super_list) 12188c2ecf20Sopenharmony_ci nfs_mark_test_expired_delegation(server, delegation); 12198c2ecf20Sopenharmony_ci} 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci/** 12228c2ecf20Sopenharmony_ci * nfs_mark_test_expired_all_delegations - mark all delegations for testing 12238c2ecf20Sopenharmony_ci * @clp: nfs_client to process 12248c2ecf20Sopenharmony_ci * 12258c2ecf20Sopenharmony_ci * Iterates through all the delegations associated with this server and 12268c2ecf20Sopenharmony_ci * marks them as needing to be checked for validity. 12278c2ecf20Sopenharmony_ci */ 12288c2ecf20Sopenharmony_civoid nfs_mark_test_expired_all_delegations(struct nfs_client *clp) 12298c2ecf20Sopenharmony_ci{ 12308c2ecf20Sopenharmony_ci struct nfs_server *server; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci rcu_read_lock(); 12338c2ecf20Sopenharmony_ci list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 12348c2ecf20Sopenharmony_ci nfs_delegation_mark_test_expired_server(server); 12358c2ecf20Sopenharmony_ci rcu_read_unlock(); 12368c2ecf20Sopenharmony_ci} 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci/** 12398c2ecf20Sopenharmony_ci * nfs_test_expired_all_delegations - test all delegations for a client 12408c2ecf20Sopenharmony_ci * @clp: nfs_client to process 12418c2ecf20Sopenharmony_ci * 12428c2ecf20Sopenharmony_ci * Helper for handling "recallable state revoked" status from server. 12438c2ecf20Sopenharmony_ci */ 12448c2ecf20Sopenharmony_civoid nfs_test_expired_all_delegations(struct nfs_client *clp) 12458c2ecf20Sopenharmony_ci{ 12468c2ecf20Sopenharmony_ci nfs_mark_test_expired_all_delegations(clp); 12478c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 12488c2ecf20Sopenharmony_ci} 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_cistatic void 12518c2ecf20Sopenharmony_cinfs_delegation_test_free_expired(struct inode *inode, 12528c2ecf20Sopenharmony_ci nfs4_stateid *stateid, 12538c2ecf20Sopenharmony_ci const struct cred *cred) 12548c2ecf20Sopenharmony_ci{ 12558c2ecf20Sopenharmony_ci struct nfs_server *server = NFS_SERVER(inode); 12568c2ecf20Sopenharmony_ci const struct nfs4_minor_version_ops *ops = server->nfs_client->cl_mvops; 12578c2ecf20Sopenharmony_ci int status; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci if (!cred) 12608c2ecf20Sopenharmony_ci return; 12618c2ecf20Sopenharmony_ci status = ops->test_and_free_expired(server, stateid, cred); 12628c2ecf20Sopenharmony_ci if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID) 12638c2ecf20Sopenharmony_ci nfs_remove_bad_delegation(inode, stateid); 12648c2ecf20Sopenharmony_ci} 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_cistatic int nfs_server_reap_expired_delegations(struct nfs_server *server, 12678c2ecf20Sopenharmony_ci void __always_unused *data) 12688c2ecf20Sopenharmony_ci{ 12698c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 12708c2ecf20Sopenharmony_ci struct inode *inode; 12718c2ecf20Sopenharmony_ci const struct cred *cred; 12728c2ecf20Sopenharmony_ci nfs4_stateid stateid; 12738c2ecf20Sopenharmony_cirestart: 12748c2ecf20Sopenharmony_ci rcu_read_lock(); 12758c2ecf20Sopenharmony_cirestart_locked: 12768c2ecf20Sopenharmony_ci list_for_each_entry_rcu(delegation, &server->delegations, super_list) { 12778c2ecf20Sopenharmony_ci if (test_bit(NFS_DELEGATION_INODE_FREEING, 12788c2ecf20Sopenharmony_ci &delegation->flags) || 12798c2ecf20Sopenharmony_ci test_bit(NFS_DELEGATION_RETURNING, 12808c2ecf20Sopenharmony_ci &delegation->flags) || 12818c2ecf20Sopenharmony_ci test_bit(NFS_DELEGATION_TEST_EXPIRED, 12828c2ecf20Sopenharmony_ci &delegation->flags) == 0) 12838c2ecf20Sopenharmony_ci continue; 12848c2ecf20Sopenharmony_ci inode = nfs_delegation_grab_inode(delegation); 12858c2ecf20Sopenharmony_ci if (inode == NULL) 12868c2ecf20Sopenharmony_ci goto restart_locked; 12878c2ecf20Sopenharmony_ci spin_lock(&delegation->lock); 12888c2ecf20Sopenharmony_ci cred = get_cred_rcu(delegation->cred); 12898c2ecf20Sopenharmony_ci nfs4_stateid_copy(&stateid, &delegation->stateid); 12908c2ecf20Sopenharmony_ci spin_unlock(&delegation->lock); 12918c2ecf20Sopenharmony_ci clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags); 12928c2ecf20Sopenharmony_ci rcu_read_unlock(); 12938c2ecf20Sopenharmony_ci nfs_delegation_test_free_expired(inode, &stateid, cred); 12948c2ecf20Sopenharmony_ci put_cred(cred); 12958c2ecf20Sopenharmony_ci if (!nfs4_server_rebooted(server->nfs_client)) { 12968c2ecf20Sopenharmony_ci iput(inode); 12978c2ecf20Sopenharmony_ci cond_resched(); 12988c2ecf20Sopenharmony_ci goto restart; 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci nfs_inode_mark_test_expired_delegation(server,inode); 13018c2ecf20Sopenharmony_ci iput(inode); 13028c2ecf20Sopenharmony_ci return -EAGAIN; 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci rcu_read_unlock(); 13058c2ecf20Sopenharmony_ci return 0; 13068c2ecf20Sopenharmony_ci} 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci/** 13098c2ecf20Sopenharmony_ci * nfs_reap_expired_delegations - reap expired delegations 13108c2ecf20Sopenharmony_ci * @clp: nfs_client to process 13118c2ecf20Sopenharmony_ci * 13128c2ecf20Sopenharmony_ci * Iterates through all the delegations associated with this server and 13138c2ecf20Sopenharmony_ci * checks if they have may have been revoked. This function is usually 13148c2ecf20Sopenharmony_ci * expected to be called in cases where the server may have lost its 13158c2ecf20Sopenharmony_ci * lease. 13168c2ecf20Sopenharmony_ci */ 13178c2ecf20Sopenharmony_civoid nfs_reap_expired_delegations(struct nfs_client *clp) 13188c2ecf20Sopenharmony_ci{ 13198c2ecf20Sopenharmony_ci nfs_client_for_each_server(clp, nfs_server_reap_expired_delegations, 13208c2ecf20Sopenharmony_ci NULL); 13218c2ecf20Sopenharmony_ci} 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_civoid nfs_inode_find_delegation_state_and_recover(struct inode *inode, 13248c2ecf20Sopenharmony_ci const nfs4_stateid *stateid) 13258c2ecf20Sopenharmony_ci{ 13268c2ecf20Sopenharmony_ci struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 13278c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 13288c2ecf20Sopenharmony_ci bool found = false; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci rcu_read_lock(); 13318c2ecf20Sopenharmony_ci delegation = rcu_dereference(NFS_I(inode)->delegation); 13328c2ecf20Sopenharmony_ci if (delegation && 13338c2ecf20Sopenharmony_ci nfs4_stateid_match_or_older(&delegation->stateid, stateid) && 13348c2ecf20Sopenharmony_ci !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { 13358c2ecf20Sopenharmony_ci nfs_mark_test_expired_delegation(NFS_SERVER(inode), delegation); 13368c2ecf20Sopenharmony_ci found = true; 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci rcu_read_unlock(); 13398c2ecf20Sopenharmony_ci if (found) 13408c2ecf20Sopenharmony_ci nfs4_schedule_state_manager(clp); 13418c2ecf20Sopenharmony_ci} 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci/** 13448c2ecf20Sopenharmony_ci * nfs_delegations_present - check for existence of delegations 13458c2ecf20Sopenharmony_ci * @clp: client state handle 13468c2ecf20Sopenharmony_ci * 13478c2ecf20Sopenharmony_ci * Returns one if there are any nfs_delegation structures attached 13488c2ecf20Sopenharmony_ci * to this nfs_client. 13498c2ecf20Sopenharmony_ci */ 13508c2ecf20Sopenharmony_ciint nfs_delegations_present(struct nfs_client *clp) 13518c2ecf20Sopenharmony_ci{ 13528c2ecf20Sopenharmony_ci struct nfs_server *server; 13538c2ecf20Sopenharmony_ci int ret = 0; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci rcu_read_lock(); 13568c2ecf20Sopenharmony_ci list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) 13578c2ecf20Sopenharmony_ci if (!list_empty(&server->delegations)) { 13588c2ecf20Sopenharmony_ci ret = 1; 13598c2ecf20Sopenharmony_ci break; 13608c2ecf20Sopenharmony_ci } 13618c2ecf20Sopenharmony_ci rcu_read_unlock(); 13628c2ecf20Sopenharmony_ci return ret; 13638c2ecf20Sopenharmony_ci} 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci/** 13668c2ecf20Sopenharmony_ci * nfs4_refresh_delegation_stateid - Update delegation stateid seqid 13678c2ecf20Sopenharmony_ci * @dst: stateid to refresh 13688c2ecf20Sopenharmony_ci * @inode: inode to check 13698c2ecf20Sopenharmony_ci * 13708c2ecf20Sopenharmony_ci * Returns "true" and updates "dst->seqid" * if inode had a delegation 13718c2ecf20Sopenharmony_ci * that matches our delegation stateid. Otherwise "false" is returned. 13728c2ecf20Sopenharmony_ci */ 13738c2ecf20Sopenharmony_cibool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode) 13748c2ecf20Sopenharmony_ci{ 13758c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 13768c2ecf20Sopenharmony_ci bool ret = false; 13778c2ecf20Sopenharmony_ci if (!inode) 13788c2ecf20Sopenharmony_ci goto out; 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci rcu_read_lock(); 13818c2ecf20Sopenharmony_ci delegation = rcu_dereference(NFS_I(inode)->delegation); 13828c2ecf20Sopenharmony_ci if (delegation != NULL && 13838c2ecf20Sopenharmony_ci nfs4_stateid_match_other(dst, &delegation->stateid) && 13848c2ecf20Sopenharmony_ci nfs4_stateid_is_newer(&delegation->stateid, dst) && 13858c2ecf20Sopenharmony_ci !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { 13868c2ecf20Sopenharmony_ci dst->seqid = delegation->stateid.seqid; 13878c2ecf20Sopenharmony_ci ret = true; 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ci rcu_read_unlock(); 13908c2ecf20Sopenharmony_ciout: 13918c2ecf20Sopenharmony_ci return ret; 13928c2ecf20Sopenharmony_ci} 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci/** 13958c2ecf20Sopenharmony_ci * nfs4_copy_delegation_stateid - Copy inode's state ID information 13968c2ecf20Sopenharmony_ci * @inode: inode to check 13978c2ecf20Sopenharmony_ci * @flags: delegation type requirement 13988c2ecf20Sopenharmony_ci * @dst: stateid data structure to fill in 13998c2ecf20Sopenharmony_ci * @cred: optional argument to retrieve credential 14008c2ecf20Sopenharmony_ci * 14018c2ecf20Sopenharmony_ci * Returns "true" and fills in "dst->data" * if inode had a delegation, 14028c2ecf20Sopenharmony_ci * otherwise "false" is returned. 14038c2ecf20Sopenharmony_ci */ 14048c2ecf20Sopenharmony_cibool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags, 14058c2ecf20Sopenharmony_ci nfs4_stateid *dst, const struct cred **cred) 14068c2ecf20Sopenharmony_ci{ 14078c2ecf20Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 14088c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 14098c2ecf20Sopenharmony_ci bool ret = false; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci flags &= FMODE_READ|FMODE_WRITE; 14128c2ecf20Sopenharmony_ci rcu_read_lock(); 14138c2ecf20Sopenharmony_ci delegation = rcu_dereference(nfsi->delegation); 14148c2ecf20Sopenharmony_ci if (!delegation) 14158c2ecf20Sopenharmony_ci goto out; 14168c2ecf20Sopenharmony_ci spin_lock(&delegation->lock); 14178c2ecf20Sopenharmony_ci ret = nfs4_is_valid_delegation(delegation, flags); 14188c2ecf20Sopenharmony_ci if (ret) { 14198c2ecf20Sopenharmony_ci nfs4_stateid_copy(dst, &delegation->stateid); 14208c2ecf20Sopenharmony_ci nfs_mark_delegation_referenced(delegation); 14218c2ecf20Sopenharmony_ci if (cred) 14228c2ecf20Sopenharmony_ci *cred = get_cred(delegation->cred); 14238c2ecf20Sopenharmony_ci } 14248c2ecf20Sopenharmony_ci spin_unlock(&delegation->lock); 14258c2ecf20Sopenharmony_ciout: 14268c2ecf20Sopenharmony_ci rcu_read_unlock(); 14278c2ecf20Sopenharmony_ci return ret; 14288c2ecf20Sopenharmony_ci} 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci/** 14318c2ecf20Sopenharmony_ci * nfs4_delegation_flush_on_close - Check if we must flush file on close 14328c2ecf20Sopenharmony_ci * @inode: inode to check 14338c2ecf20Sopenharmony_ci * 14348c2ecf20Sopenharmony_ci * This function checks the number of outstanding writes to the file 14358c2ecf20Sopenharmony_ci * against the delegation 'space_limit' field to see if 14368c2ecf20Sopenharmony_ci * the spec requires us to flush the file on close. 14378c2ecf20Sopenharmony_ci */ 14388c2ecf20Sopenharmony_cibool nfs4_delegation_flush_on_close(const struct inode *inode) 14398c2ecf20Sopenharmony_ci{ 14408c2ecf20Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 14418c2ecf20Sopenharmony_ci struct nfs_delegation *delegation; 14428c2ecf20Sopenharmony_ci bool ret = true; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci rcu_read_lock(); 14458c2ecf20Sopenharmony_ci delegation = rcu_dereference(nfsi->delegation); 14468c2ecf20Sopenharmony_ci if (delegation == NULL || !(delegation->type & FMODE_WRITE)) 14478c2ecf20Sopenharmony_ci goto out; 14488c2ecf20Sopenharmony_ci if (atomic_long_read(&nfsi->nrequests) < delegation->pagemod_limit) 14498c2ecf20Sopenharmony_ci ret = false; 14508c2ecf20Sopenharmony_ciout: 14518c2ecf20Sopenharmony_ci rcu_read_unlock(); 14528c2ecf20Sopenharmony_ci return ret; 14538c2ecf20Sopenharmony_ci} 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_cimodule_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644); 1456