162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/nfs/callback_proc.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2004 Trond Myklebust 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * NFSv4 callback procedures 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/math.h> 1262306a36Sopenharmony_ci#include <linux/nfs4.h> 1362306a36Sopenharmony_ci#include <linux/nfs_fs.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/rcupdate.h> 1662306a36Sopenharmony_ci#include <linux/types.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "nfs4_fs.h" 1962306a36Sopenharmony_ci#include "callback.h" 2062306a36Sopenharmony_ci#include "delegation.h" 2162306a36Sopenharmony_ci#include "internal.h" 2262306a36Sopenharmony_ci#include "pnfs.h" 2362306a36Sopenharmony_ci#include "nfs4session.h" 2462306a36Sopenharmony_ci#include "nfs4trace.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define NFSDBG_FACILITY NFSDBG_CALLBACK 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci__be32 nfs4_callback_getattr(void *argp, void *resp, 2962306a36Sopenharmony_ci struct cb_process_state *cps) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct cb_getattrargs *args = argp; 3262306a36Sopenharmony_ci struct cb_getattrres *res = resp; 3362306a36Sopenharmony_ci struct nfs_delegation *delegation; 3462306a36Sopenharmony_ci struct inode *inode; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci res->status = htonl(NFS4ERR_OP_NOT_IN_SESSION); 3762306a36Sopenharmony_ci if (!cps->clp) /* Always set for v4.0. Set in cb_sequence for v4.1 */ 3862306a36Sopenharmony_ci goto out; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci res->bitmap[0] = res->bitmap[1] = 0; 4162306a36Sopenharmony_ci res->status = htonl(NFS4ERR_BADHANDLE); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci dprintk_rcu("NFS: GETATTR callback request from %s\n", 4462306a36Sopenharmony_ci rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR)); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci inode = nfs_delegation_find_inode(cps->clp, &args->fh); 4762306a36Sopenharmony_ci if (IS_ERR(inode)) { 4862306a36Sopenharmony_ci if (inode == ERR_PTR(-EAGAIN)) 4962306a36Sopenharmony_ci res->status = htonl(NFS4ERR_DELAY); 5062306a36Sopenharmony_ci trace_nfs4_cb_getattr(cps->clp, &args->fh, NULL, 5162306a36Sopenharmony_ci -ntohl(res->status)); 5262306a36Sopenharmony_ci goto out; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci rcu_read_lock(); 5562306a36Sopenharmony_ci delegation = nfs4_get_valid_delegation(inode); 5662306a36Sopenharmony_ci if (delegation == NULL || (delegation->type & FMODE_WRITE) == 0) 5762306a36Sopenharmony_ci goto out_iput; 5862306a36Sopenharmony_ci res->size = i_size_read(inode); 5962306a36Sopenharmony_ci res->change_attr = delegation->change_attr; 6062306a36Sopenharmony_ci if (nfs_have_writebacks(inode)) 6162306a36Sopenharmony_ci res->change_attr++; 6262306a36Sopenharmony_ci res->ctime = inode_get_ctime(inode); 6362306a36Sopenharmony_ci res->mtime = inode->i_mtime; 6462306a36Sopenharmony_ci res->bitmap[0] = (FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE) & 6562306a36Sopenharmony_ci args->bitmap[0]; 6662306a36Sopenharmony_ci res->bitmap[1] = (FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY) & 6762306a36Sopenharmony_ci args->bitmap[1]; 6862306a36Sopenharmony_ci res->status = 0; 6962306a36Sopenharmony_ciout_iput: 7062306a36Sopenharmony_ci rcu_read_unlock(); 7162306a36Sopenharmony_ci trace_nfs4_cb_getattr(cps->clp, &args->fh, inode, -ntohl(res->status)); 7262306a36Sopenharmony_ci nfs_iput_and_deactive(inode); 7362306a36Sopenharmony_ciout: 7462306a36Sopenharmony_ci dprintk("%s: exit with status = %d\n", __func__, ntohl(res->status)); 7562306a36Sopenharmony_ci return res->status; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci__be32 nfs4_callback_recall(void *argp, void *resp, 7962306a36Sopenharmony_ci struct cb_process_state *cps) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct cb_recallargs *args = argp; 8262306a36Sopenharmony_ci struct inode *inode; 8362306a36Sopenharmony_ci __be32 res; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci res = htonl(NFS4ERR_OP_NOT_IN_SESSION); 8662306a36Sopenharmony_ci if (!cps->clp) /* Always set for v4.0. Set in cb_sequence for v4.1 */ 8762306a36Sopenharmony_ci goto out; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci dprintk_rcu("NFS: RECALL callback request from %s\n", 9062306a36Sopenharmony_ci rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR)); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci res = htonl(NFS4ERR_BADHANDLE); 9362306a36Sopenharmony_ci inode = nfs_delegation_find_inode(cps->clp, &args->fh); 9462306a36Sopenharmony_ci if (IS_ERR(inode)) { 9562306a36Sopenharmony_ci if (inode == ERR_PTR(-EAGAIN)) 9662306a36Sopenharmony_ci res = htonl(NFS4ERR_DELAY); 9762306a36Sopenharmony_ci trace_nfs4_cb_recall(cps->clp, &args->fh, NULL, 9862306a36Sopenharmony_ci &args->stateid, -ntohl(res)); 9962306a36Sopenharmony_ci goto out; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci /* Set up a helper thread to actually return the delegation */ 10262306a36Sopenharmony_ci switch (nfs_async_inode_return_delegation(inode, &args->stateid)) { 10362306a36Sopenharmony_ci case 0: 10462306a36Sopenharmony_ci res = 0; 10562306a36Sopenharmony_ci break; 10662306a36Sopenharmony_ci case -ENOENT: 10762306a36Sopenharmony_ci res = htonl(NFS4ERR_BAD_STATEID); 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci default: 11062306a36Sopenharmony_ci res = htonl(NFS4ERR_RESOURCE); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci trace_nfs4_cb_recall(cps->clp, &args->fh, inode, 11362306a36Sopenharmony_ci &args->stateid, -ntohl(res)); 11462306a36Sopenharmony_ci nfs_iput_and_deactive(inode); 11562306a36Sopenharmony_ciout: 11662306a36Sopenharmony_ci dprintk("%s: exit with status = %d\n", __func__, ntohl(res)); 11762306a36Sopenharmony_ci return res; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#if defined(CONFIG_NFS_V4_1) 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* 12362306a36Sopenharmony_ci * Lookup a layout inode by stateid 12462306a36Sopenharmony_ci * 12562306a36Sopenharmony_ci * Note: returns a refcount on the inode and superblock 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cistatic struct inode *nfs_layout_find_inode_by_stateid(struct nfs_client *clp, 12862306a36Sopenharmony_ci const nfs4_stateid *stateid) 12962306a36Sopenharmony_ci __must_hold(RCU) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct nfs_server *server; 13262306a36Sopenharmony_ci struct inode *inode; 13362306a36Sopenharmony_ci struct pnfs_layout_hdr *lo; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci rcu_read_lock(); 13662306a36Sopenharmony_ci list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 13762306a36Sopenharmony_ci list_for_each_entry_rcu(lo, &server->layouts, plh_layouts) { 13862306a36Sopenharmony_ci if (!pnfs_layout_is_valid(lo)) 13962306a36Sopenharmony_ci continue; 14062306a36Sopenharmony_ci if (!nfs4_stateid_match_other(stateid, &lo->plh_stateid)) 14162306a36Sopenharmony_ci continue; 14262306a36Sopenharmony_ci if (nfs_sb_active(server->super)) 14362306a36Sopenharmony_ci inode = igrab(lo->plh_inode); 14462306a36Sopenharmony_ci else 14562306a36Sopenharmony_ci inode = ERR_PTR(-EAGAIN); 14662306a36Sopenharmony_ci rcu_read_unlock(); 14762306a36Sopenharmony_ci if (inode) 14862306a36Sopenharmony_ci return inode; 14962306a36Sopenharmony_ci nfs_sb_deactive(server->super); 15062306a36Sopenharmony_ci return ERR_PTR(-EAGAIN); 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci rcu_read_unlock(); 15462306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/* 15862306a36Sopenharmony_ci * Lookup a layout inode by filehandle. 15962306a36Sopenharmony_ci * 16062306a36Sopenharmony_ci * Note: returns a refcount on the inode and superblock 16162306a36Sopenharmony_ci * 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_cistatic struct inode *nfs_layout_find_inode_by_fh(struct nfs_client *clp, 16462306a36Sopenharmony_ci const struct nfs_fh *fh) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct nfs_server *server; 16762306a36Sopenharmony_ci struct nfs_inode *nfsi; 16862306a36Sopenharmony_ci struct inode *inode; 16962306a36Sopenharmony_ci struct pnfs_layout_hdr *lo; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci rcu_read_lock(); 17262306a36Sopenharmony_ci list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 17362306a36Sopenharmony_ci list_for_each_entry_rcu(lo, &server->layouts, plh_layouts) { 17462306a36Sopenharmony_ci nfsi = NFS_I(lo->plh_inode); 17562306a36Sopenharmony_ci if (nfs_compare_fh(fh, &nfsi->fh)) 17662306a36Sopenharmony_ci continue; 17762306a36Sopenharmony_ci if (nfsi->layout != lo) 17862306a36Sopenharmony_ci continue; 17962306a36Sopenharmony_ci if (nfs_sb_active(server->super)) 18062306a36Sopenharmony_ci inode = igrab(lo->plh_inode); 18162306a36Sopenharmony_ci else 18262306a36Sopenharmony_ci inode = ERR_PTR(-EAGAIN); 18362306a36Sopenharmony_ci rcu_read_unlock(); 18462306a36Sopenharmony_ci if (inode) 18562306a36Sopenharmony_ci return inode; 18662306a36Sopenharmony_ci nfs_sb_deactive(server->super); 18762306a36Sopenharmony_ci return ERR_PTR(-EAGAIN); 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci rcu_read_unlock(); 19162306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic struct inode *nfs_layout_find_inode(struct nfs_client *clp, 19562306a36Sopenharmony_ci const struct nfs_fh *fh, 19662306a36Sopenharmony_ci const nfs4_stateid *stateid) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct inode *inode; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci inode = nfs_layout_find_inode_by_stateid(clp, stateid); 20162306a36Sopenharmony_ci if (inode == ERR_PTR(-ENOENT)) 20262306a36Sopenharmony_ci inode = nfs_layout_find_inode_by_fh(clp, fh); 20362306a36Sopenharmony_ci return inode; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/* 20762306a36Sopenharmony_ci * Enforce RFC5661 section 12.5.5.2.1. (Layout Recall and Return Sequencing) 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_cistatic u32 pnfs_check_callback_stateid(struct pnfs_layout_hdr *lo, 21062306a36Sopenharmony_ci const nfs4_stateid *new) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci u32 oldseq, newseq; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* Is the stateid not initialised? */ 21562306a36Sopenharmony_ci if (!pnfs_layout_is_valid(lo)) 21662306a36Sopenharmony_ci return NFS4ERR_NOMATCHING_LAYOUT; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* Mismatched stateid? */ 21962306a36Sopenharmony_ci if (!nfs4_stateid_match_other(&lo->plh_stateid, new)) 22062306a36Sopenharmony_ci return NFS4ERR_BAD_STATEID; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci newseq = be32_to_cpu(new->seqid); 22362306a36Sopenharmony_ci /* Are we already in a layout recall situation? */ 22462306a36Sopenharmony_ci if (test_bit(NFS_LAYOUT_RETURN_REQUESTED, &lo->plh_flags) && 22562306a36Sopenharmony_ci lo->plh_return_seq != 0) { 22662306a36Sopenharmony_ci if (newseq < lo->plh_return_seq) 22762306a36Sopenharmony_ci return NFS4ERR_OLD_STATEID; 22862306a36Sopenharmony_ci if (newseq > lo->plh_return_seq) 22962306a36Sopenharmony_ci return NFS4ERR_DELAY; 23062306a36Sopenharmony_ci goto out; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* Check that the stateid matches what we think it should be. */ 23462306a36Sopenharmony_ci oldseq = be32_to_cpu(lo->plh_stateid.seqid); 23562306a36Sopenharmony_ci if (newseq > oldseq + 1) 23662306a36Sopenharmony_ci return NFS4ERR_DELAY; 23762306a36Sopenharmony_ci /* Crazy server! */ 23862306a36Sopenharmony_ci if (newseq <= oldseq) 23962306a36Sopenharmony_ci return NFS4ERR_OLD_STATEID; 24062306a36Sopenharmony_ciout: 24162306a36Sopenharmony_ci return NFS_OK; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic u32 initiate_file_draining(struct nfs_client *clp, 24562306a36Sopenharmony_ci struct cb_layoutrecallargs *args) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct inode *ino; 24862306a36Sopenharmony_ci struct pnfs_layout_hdr *lo; 24962306a36Sopenharmony_ci u32 rv = NFS4ERR_NOMATCHING_LAYOUT; 25062306a36Sopenharmony_ci LIST_HEAD(free_me_list); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci ino = nfs_layout_find_inode(clp, &args->cbl_fh, &args->cbl_stateid); 25362306a36Sopenharmony_ci if (IS_ERR(ino)) { 25462306a36Sopenharmony_ci if (ino == ERR_PTR(-EAGAIN)) 25562306a36Sopenharmony_ci rv = NFS4ERR_DELAY; 25662306a36Sopenharmony_ci goto out_noput; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci pnfs_layoutcommit_inode(ino, false); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci spin_lock(&ino->i_lock); 26362306a36Sopenharmony_ci lo = NFS_I(ino)->layout; 26462306a36Sopenharmony_ci if (!lo) { 26562306a36Sopenharmony_ci spin_unlock(&ino->i_lock); 26662306a36Sopenharmony_ci goto out; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci pnfs_get_layout_hdr(lo); 26962306a36Sopenharmony_ci rv = pnfs_check_callback_stateid(lo, &args->cbl_stateid); 27062306a36Sopenharmony_ci if (rv != NFS_OK) 27162306a36Sopenharmony_ci goto unlock; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* 27462306a36Sopenharmony_ci * Enforce RFC5661 Section 12.5.5.2.1.5 (Bulk Recall and Return) 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_ci if (test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags)) { 27762306a36Sopenharmony_ci rv = NFS4ERR_DELAY; 27862306a36Sopenharmony_ci goto unlock; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci pnfs_set_layout_stateid(lo, &args->cbl_stateid, NULL, true); 28262306a36Sopenharmony_ci switch (pnfs_mark_matching_lsegs_return(lo, &free_me_list, 28362306a36Sopenharmony_ci &args->cbl_range, 28462306a36Sopenharmony_ci be32_to_cpu(args->cbl_stateid.seqid))) { 28562306a36Sopenharmony_ci case 0: 28662306a36Sopenharmony_ci case -EBUSY: 28762306a36Sopenharmony_ci /* There are layout segments that need to be returned */ 28862306a36Sopenharmony_ci rv = NFS4_OK; 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci case -ENOENT: 29162306a36Sopenharmony_ci set_bit(NFS_LAYOUT_DRAIN, &lo->plh_flags); 29262306a36Sopenharmony_ci /* Embrace your forgetfulness! */ 29362306a36Sopenharmony_ci rv = NFS4ERR_NOMATCHING_LAYOUT; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) { 29662306a36Sopenharmony_ci NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo, 29762306a36Sopenharmony_ci &args->cbl_range); 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ciunlock: 30162306a36Sopenharmony_ci spin_unlock(&ino->i_lock); 30262306a36Sopenharmony_ci pnfs_free_lseg_list(&free_me_list); 30362306a36Sopenharmony_ci /* Free all lsegs that are attached to commit buckets */ 30462306a36Sopenharmony_ci nfs_commit_inode(ino, 0); 30562306a36Sopenharmony_ci pnfs_put_layout_hdr(lo); 30662306a36Sopenharmony_ciout: 30762306a36Sopenharmony_ci nfs_iput_and_deactive(ino); 30862306a36Sopenharmony_ciout_noput: 30962306a36Sopenharmony_ci trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, ino, 31062306a36Sopenharmony_ci &args->cbl_stateid, -rv); 31162306a36Sopenharmony_ci return rv; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic u32 initiate_bulk_draining(struct nfs_client *clp, 31562306a36Sopenharmony_ci struct cb_layoutrecallargs *args) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci int stat; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (args->cbl_recall_type == RETURN_FSID) 32062306a36Sopenharmony_ci stat = pnfs_destroy_layouts_byfsid(clp, &args->cbl_fsid, true); 32162306a36Sopenharmony_ci else 32262306a36Sopenharmony_ci stat = pnfs_destroy_layouts_byclid(clp, true); 32362306a36Sopenharmony_ci if (stat != 0) 32462306a36Sopenharmony_ci return NFS4ERR_DELAY; 32562306a36Sopenharmony_ci return NFS4ERR_NOMATCHING_LAYOUT; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic u32 do_callback_layoutrecall(struct nfs_client *clp, 32962306a36Sopenharmony_ci struct cb_layoutrecallargs *args) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci if (args->cbl_recall_type == RETURN_FILE) 33262306a36Sopenharmony_ci return initiate_file_draining(clp, args); 33362306a36Sopenharmony_ci return initiate_bulk_draining(clp, args); 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci__be32 nfs4_callback_layoutrecall(void *argp, void *resp, 33762306a36Sopenharmony_ci struct cb_process_state *cps) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct cb_layoutrecallargs *args = argp; 34062306a36Sopenharmony_ci u32 res = NFS4ERR_OP_NOT_IN_SESSION; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (cps->clp) 34362306a36Sopenharmony_ci res = do_callback_layoutrecall(cps->clp, args); 34462306a36Sopenharmony_ci return cpu_to_be32(res); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic void pnfs_recall_all_layouts(struct nfs_client *clp) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct cb_layoutrecallargs args; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* Pretend we got a CB_LAYOUTRECALL(ALL) */ 35262306a36Sopenharmony_ci memset(&args, 0, sizeof(args)); 35362306a36Sopenharmony_ci args.cbl_recall_type = RETURN_ALL; 35462306a36Sopenharmony_ci /* FIXME we ignore errors, what should we do? */ 35562306a36Sopenharmony_ci do_callback_layoutrecall(clp, &args); 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci__be32 nfs4_callback_devicenotify(void *argp, void *resp, 35962306a36Sopenharmony_ci struct cb_process_state *cps) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct cb_devicenotifyargs *args = argp; 36262306a36Sopenharmony_ci const struct pnfs_layoutdriver_type *ld = NULL; 36362306a36Sopenharmony_ci uint32_t i; 36462306a36Sopenharmony_ci __be32 res = 0; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (!cps->clp) { 36762306a36Sopenharmony_ci res = cpu_to_be32(NFS4ERR_OP_NOT_IN_SESSION); 36862306a36Sopenharmony_ci goto out; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci for (i = 0; i < args->ndevs; i++) { 37262306a36Sopenharmony_ci struct cb_devicenotifyitem *dev = &args->devs[i]; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (!ld || ld->id != dev->cbd_layout_type) { 37562306a36Sopenharmony_ci pnfs_put_layoutdriver(ld); 37662306a36Sopenharmony_ci ld = pnfs_find_layoutdriver(dev->cbd_layout_type); 37762306a36Sopenharmony_ci if (!ld) 37862306a36Sopenharmony_ci continue; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci nfs4_delete_deviceid(ld, cps->clp, &dev->cbd_dev_id); 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci pnfs_put_layoutdriver(ld); 38362306a36Sopenharmony_ciout: 38462306a36Sopenharmony_ci kfree(args->devs); 38562306a36Sopenharmony_ci return res; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci/* 38962306a36Sopenharmony_ci * Validate the sequenceID sent by the server. 39062306a36Sopenharmony_ci * Return success if the sequenceID is one more than what we last saw on 39162306a36Sopenharmony_ci * this slot, accounting for wraparound. Increments the slot's sequence. 39262306a36Sopenharmony_ci * 39362306a36Sopenharmony_ci * We don't yet implement a duplicate request cache, instead we set the 39462306a36Sopenharmony_ci * back channel ca_maxresponsesize_cached to zero. This is OK for now 39562306a36Sopenharmony_ci * since we only currently implement idempotent callbacks anyway. 39662306a36Sopenharmony_ci * 39762306a36Sopenharmony_ci * We have a single slot backchannel at this time, so we don't bother 39862306a36Sopenharmony_ci * checking the used_slots bit array on the table. The lower layer guarantees 39962306a36Sopenharmony_ci * a single outstanding callback request at a time. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_cistatic __be32 40262306a36Sopenharmony_civalidate_seqid(const struct nfs4_slot_table *tbl, const struct nfs4_slot *slot, 40362306a36Sopenharmony_ci const struct cb_sequenceargs * args) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci __be32 ret; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci ret = cpu_to_be32(NFS4ERR_BADSLOT); 40862306a36Sopenharmony_ci if (args->csa_slotid > tbl->server_highest_slotid) 40962306a36Sopenharmony_ci goto out_err; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* Replay */ 41262306a36Sopenharmony_ci if (args->csa_sequenceid == slot->seq_nr) { 41362306a36Sopenharmony_ci ret = cpu_to_be32(NFS4ERR_DELAY); 41462306a36Sopenharmony_ci if (nfs4_test_locked_slot(tbl, slot->slot_nr)) 41562306a36Sopenharmony_ci goto out_err; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* Signal process_op to set this error on next op */ 41862306a36Sopenharmony_ci ret = cpu_to_be32(NFS4ERR_RETRY_UNCACHED_REP); 41962306a36Sopenharmony_ci if (args->csa_cachethis == 0) 42062306a36Sopenharmony_ci goto out_err; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* Liar! We never allowed you to set csa_cachethis != 0 */ 42362306a36Sopenharmony_ci ret = cpu_to_be32(NFS4ERR_SEQ_FALSE_RETRY); 42462306a36Sopenharmony_ci goto out_err; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Note: wraparound relies on seq_nr being of type u32 */ 42862306a36Sopenharmony_ci /* Misordered request */ 42962306a36Sopenharmony_ci ret = cpu_to_be32(NFS4ERR_SEQ_MISORDERED); 43062306a36Sopenharmony_ci if (args->csa_sequenceid != slot->seq_nr + 1) 43162306a36Sopenharmony_ci goto out_err; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci return cpu_to_be32(NFS4_OK); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ciout_err: 43662306a36Sopenharmony_ci trace_nfs4_cb_seqid_err(args, ret); 43762306a36Sopenharmony_ci return ret; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci/* 44162306a36Sopenharmony_ci * For each referring call triple, check the session's slot table for 44262306a36Sopenharmony_ci * a match. If the slot is in use and the sequence numbers match, the 44362306a36Sopenharmony_ci * client is still waiting for a response to the original request. 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_cistatic int referring_call_exists(struct nfs_client *clp, 44662306a36Sopenharmony_ci uint32_t nrclists, 44762306a36Sopenharmony_ci struct referring_call_list *rclists, 44862306a36Sopenharmony_ci spinlock_t *lock) 44962306a36Sopenharmony_ci __releases(lock) 45062306a36Sopenharmony_ci __acquires(lock) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci int status = 0; 45362306a36Sopenharmony_ci int i, j; 45462306a36Sopenharmony_ci struct nfs4_session *session; 45562306a36Sopenharmony_ci struct nfs4_slot_table *tbl; 45662306a36Sopenharmony_ci struct referring_call_list *rclist; 45762306a36Sopenharmony_ci struct referring_call *ref; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* 46062306a36Sopenharmony_ci * XXX When client trunking is implemented, this becomes 46162306a36Sopenharmony_ci * a session lookup from within the loop 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_ci session = clp->cl_session; 46462306a36Sopenharmony_ci tbl = &session->fc_slot_table; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci for (i = 0; i < nrclists; i++) { 46762306a36Sopenharmony_ci rclist = &rclists[i]; 46862306a36Sopenharmony_ci if (memcmp(session->sess_id.data, 46962306a36Sopenharmony_ci rclist->rcl_sessionid.data, 47062306a36Sopenharmony_ci NFS4_MAX_SESSIONID_LEN) != 0) 47162306a36Sopenharmony_ci continue; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci for (j = 0; j < rclist->rcl_nrefcalls; j++) { 47462306a36Sopenharmony_ci ref = &rclist->rcl_refcalls[j]; 47562306a36Sopenharmony_ci spin_unlock(lock); 47662306a36Sopenharmony_ci status = nfs4_slot_wait_on_seqid(tbl, ref->rc_slotid, 47762306a36Sopenharmony_ci ref->rc_sequenceid, HZ >> 1) < 0; 47862306a36Sopenharmony_ci spin_lock(lock); 47962306a36Sopenharmony_ci if (status) 48062306a36Sopenharmony_ci goto out; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ciout: 48562306a36Sopenharmony_ci return status; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci__be32 nfs4_callback_sequence(void *argp, void *resp, 48962306a36Sopenharmony_ci struct cb_process_state *cps) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct cb_sequenceargs *args = argp; 49262306a36Sopenharmony_ci struct cb_sequenceres *res = resp; 49362306a36Sopenharmony_ci struct nfs4_slot_table *tbl; 49462306a36Sopenharmony_ci struct nfs4_slot *slot; 49562306a36Sopenharmony_ci struct nfs_client *clp; 49662306a36Sopenharmony_ci int i; 49762306a36Sopenharmony_ci __be32 status = htonl(NFS4ERR_BADSESSION); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci clp = nfs4_find_client_sessionid(cps->net, args->csa_addr, 50062306a36Sopenharmony_ci &args->csa_sessionid, cps->minorversion); 50162306a36Sopenharmony_ci if (clp == NULL) 50262306a36Sopenharmony_ci goto out; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (!(clp->cl_session->flags & SESSION4_BACK_CHAN)) 50562306a36Sopenharmony_ci goto out; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci tbl = &clp->cl_session->bc_slot_table; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* Set up res before grabbing the spinlock */ 51062306a36Sopenharmony_ci memcpy(&res->csr_sessionid, &args->csa_sessionid, 51162306a36Sopenharmony_ci sizeof(res->csr_sessionid)); 51262306a36Sopenharmony_ci res->csr_sequenceid = args->csa_sequenceid; 51362306a36Sopenharmony_ci res->csr_slotid = args->csa_slotid; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci spin_lock(&tbl->slot_tbl_lock); 51662306a36Sopenharmony_ci /* state manager is resetting the session */ 51762306a36Sopenharmony_ci if (test_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state)) { 51862306a36Sopenharmony_ci status = htonl(NFS4ERR_DELAY); 51962306a36Sopenharmony_ci /* Return NFS4ERR_BADSESSION if we're draining the session 52062306a36Sopenharmony_ci * in order to reset it. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_ci if (test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)) 52362306a36Sopenharmony_ci status = htonl(NFS4ERR_BADSESSION); 52462306a36Sopenharmony_ci goto out_unlock; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci status = htonl(NFS4ERR_BADSLOT); 52862306a36Sopenharmony_ci slot = nfs4_lookup_slot(tbl, args->csa_slotid); 52962306a36Sopenharmony_ci if (IS_ERR(slot)) 53062306a36Sopenharmony_ci goto out_unlock; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci res->csr_highestslotid = tbl->server_highest_slotid; 53362306a36Sopenharmony_ci res->csr_target_highestslotid = tbl->target_highest_slotid; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci status = validate_seqid(tbl, slot, args); 53662306a36Sopenharmony_ci if (status) 53762306a36Sopenharmony_ci goto out_unlock; 53862306a36Sopenharmony_ci if (!nfs4_try_to_lock_slot(tbl, slot)) { 53962306a36Sopenharmony_ci status = htonl(NFS4ERR_DELAY); 54062306a36Sopenharmony_ci goto out_unlock; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci cps->slot = slot; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* The ca_maxresponsesize_cached is 0 with no DRC */ 54562306a36Sopenharmony_ci if (args->csa_cachethis != 0) { 54662306a36Sopenharmony_ci status = htonl(NFS4ERR_REP_TOO_BIG_TO_CACHE); 54762306a36Sopenharmony_ci goto out_unlock; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* 55162306a36Sopenharmony_ci * Check for pending referring calls. If a match is found, a 55262306a36Sopenharmony_ci * related callback was received before the response to the original 55362306a36Sopenharmony_ci * call. 55462306a36Sopenharmony_ci */ 55562306a36Sopenharmony_ci if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists, 55662306a36Sopenharmony_ci &tbl->slot_tbl_lock) < 0) { 55762306a36Sopenharmony_ci status = htonl(NFS4ERR_DELAY); 55862306a36Sopenharmony_ci goto out_unlock; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* 56262306a36Sopenharmony_ci * RFC5661 20.9.3 56362306a36Sopenharmony_ci * If CB_SEQUENCE returns an error, then the state of the slot 56462306a36Sopenharmony_ci * (sequence ID, cached reply) MUST NOT change. 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_ci slot->seq_nr = args->csa_sequenceid; 56762306a36Sopenharmony_ciout_unlock: 56862306a36Sopenharmony_ci spin_unlock(&tbl->slot_tbl_lock); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ciout: 57162306a36Sopenharmony_ci cps->clp = clp; /* put in nfs4_callback_compound */ 57262306a36Sopenharmony_ci for (i = 0; i < args->csa_nrclists; i++) 57362306a36Sopenharmony_ci kfree(args->csa_rclists[i].rcl_refcalls); 57462306a36Sopenharmony_ci kfree(args->csa_rclists); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) { 57762306a36Sopenharmony_ci cps->drc_status = status; 57862306a36Sopenharmony_ci status = 0; 57962306a36Sopenharmony_ci } else 58062306a36Sopenharmony_ci res->csr_status = status; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci trace_nfs4_cb_sequence(args, res, status); 58362306a36Sopenharmony_ci return status; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic bool 58762306a36Sopenharmony_civalidate_bitmap_values(unsigned int mask) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci return (mask & ~RCA4_TYPE_MASK_ALL) == 0; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci__be32 nfs4_callback_recallany(void *argp, void *resp, 59362306a36Sopenharmony_ci struct cb_process_state *cps) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct cb_recallanyargs *args = argp; 59662306a36Sopenharmony_ci __be32 status; 59762306a36Sopenharmony_ci fmode_t flags = 0; 59862306a36Sopenharmony_ci bool schedule_manager = false; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci status = cpu_to_be32(NFS4ERR_OP_NOT_IN_SESSION); 60162306a36Sopenharmony_ci if (!cps->clp) /* set in cb_sequence */ 60262306a36Sopenharmony_ci goto out; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci dprintk_rcu("NFS: RECALL_ANY callback request from %s\n", 60562306a36Sopenharmony_ci rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR)); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci status = cpu_to_be32(NFS4ERR_INVAL); 60862306a36Sopenharmony_ci if (!validate_bitmap_values(args->craa_type_mask)) 60962306a36Sopenharmony_ci goto out; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci status = cpu_to_be32(NFS4_OK); 61262306a36Sopenharmony_ci if (args->craa_type_mask & BIT(RCA4_TYPE_MASK_RDATA_DLG)) 61362306a36Sopenharmony_ci flags = FMODE_READ; 61462306a36Sopenharmony_ci if (args->craa_type_mask & BIT(RCA4_TYPE_MASK_WDATA_DLG)) 61562306a36Sopenharmony_ci flags |= FMODE_WRITE; 61662306a36Sopenharmony_ci if (flags) 61762306a36Sopenharmony_ci nfs_expire_unused_delegation_types(cps->clp, flags); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (args->craa_type_mask & BIT(RCA4_TYPE_MASK_FILE_LAYOUT)) 62062306a36Sopenharmony_ci pnfs_recall_all_layouts(cps->clp); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (args->craa_type_mask & BIT(PNFS_FF_RCA4_TYPE_MASK_READ)) { 62362306a36Sopenharmony_ci set_bit(NFS4CLNT_RECALL_ANY_LAYOUT_READ, &cps->clp->cl_state); 62462306a36Sopenharmony_ci schedule_manager = true; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci if (args->craa_type_mask & BIT(PNFS_FF_RCA4_TYPE_MASK_RW)) { 62762306a36Sopenharmony_ci set_bit(NFS4CLNT_RECALL_ANY_LAYOUT_RW, &cps->clp->cl_state); 62862306a36Sopenharmony_ci schedule_manager = true; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci if (schedule_manager) 63162306a36Sopenharmony_ci nfs4_schedule_state_manager(cps->clp); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ciout: 63462306a36Sopenharmony_ci dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); 63562306a36Sopenharmony_ci return status; 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci/* Reduce the fore channel's max_slots to the target value */ 63962306a36Sopenharmony_ci__be32 nfs4_callback_recallslot(void *argp, void *resp, 64062306a36Sopenharmony_ci struct cb_process_state *cps) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci struct cb_recallslotargs *args = argp; 64362306a36Sopenharmony_ci struct nfs4_slot_table *fc_tbl; 64462306a36Sopenharmony_ci __be32 status; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci status = htonl(NFS4ERR_OP_NOT_IN_SESSION); 64762306a36Sopenharmony_ci if (!cps->clp) /* set in cb_sequence */ 64862306a36Sopenharmony_ci goto out; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci dprintk_rcu("NFS: CB_RECALL_SLOT request from %s target highest slotid %u\n", 65162306a36Sopenharmony_ci rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR), 65262306a36Sopenharmony_ci args->crsa_target_highest_slotid); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci fc_tbl = &cps->clp->cl_session->fc_slot_table; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci status = htonl(NFS4_OK); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci nfs41_set_target_slotid(fc_tbl, args->crsa_target_highest_slotid); 65962306a36Sopenharmony_ci nfs41_notify_server(cps->clp); 66062306a36Sopenharmony_ciout: 66162306a36Sopenharmony_ci dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); 66262306a36Sopenharmony_ci return status; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci__be32 nfs4_callback_notify_lock(void *argp, void *resp, 66662306a36Sopenharmony_ci struct cb_process_state *cps) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci struct cb_notify_lock_args *args = argp; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (!cps->clp) /* set in cb_sequence */ 67162306a36Sopenharmony_ci return htonl(NFS4ERR_OP_NOT_IN_SESSION); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci dprintk_rcu("NFS: CB_NOTIFY_LOCK request from %s\n", 67462306a36Sopenharmony_ci rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR)); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* Don't wake anybody if the string looked bogus */ 67762306a36Sopenharmony_ci if (args->cbnl_valid) 67862306a36Sopenharmony_ci __wake_up(&cps->clp->cl_lock_waitq, TASK_NORMAL, 0, args); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci return htonl(NFS4_OK); 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4_1 */ 68362306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_2 68462306a36Sopenharmony_cistatic void nfs4_copy_cb_args(struct nfs4_copy_state *cp_state, 68562306a36Sopenharmony_ci struct cb_offloadargs *args) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci cp_state->count = args->wr_count; 68862306a36Sopenharmony_ci cp_state->error = args->error; 68962306a36Sopenharmony_ci if (!args->error) { 69062306a36Sopenharmony_ci cp_state->verf.committed = args->wr_writeverf.committed; 69162306a36Sopenharmony_ci memcpy(&cp_state->verf.verifier.data[0], 69262306a36Sopenharmony_ci &args->wr_writeverf.verifier.data[0], 69362306a36Sopenharmony_ci NFS4_VERIFIER_SIZE); 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci__be32 nfs4_callback_offload(void *data, void *dummy, 69862306a36Sopenharmony_ci struct cb_process_state *cps) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci struct cb_offloadargs *args = data; 70162306a36Sopenharmony_ci struct nfs_server *server; 70262306a36Sopenharmony_ci struct nfs4_copy_state *copy, *tmp_copy; 70362306a36Sopenharmony_ci bool found = false; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_KERNEL); 70662306a36Sopenharmony_ci if (!copy) 70762306a36Sopenharmony_ci return htonl(NFS4ERR_SERVERFAULT); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci spin_lock(&cps->clp->cl_lock); 71062306a36Sopenharmony_ci rcu_read_lock(); 71162306a36Sopenharmony_ci list_for_each_entry_rcu(server, &cps->clp->cl_superblocks, 71262306a36Sopenharmony_ci client_link) { 71362306a36Sopenharmony_ci list_for_each_entry(tmp_copy, &server->ss_copies, copies) { 71462306a36Sopenharmony_ci if (memcmp(args->coa_stateid.other, 71562306a36Sopenharmony_ci tmp_copy->stateid.other, 71662306a36Sopenharmony_ci sizeof(args->coa_stateid.other))) 71762306a36Sopenharmony_ci continue; 71862306a36Sopenharmony_ci nfs4_copy_cb_args(tmp_copy, args); 71962306a36Sopenharmony_ci complete(&tmp_copy->completion); 72062306a36Sopenharmony_ci found = true; 72162306a36Sopenharmony_ci goto out; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ciout: 72562306a36Sopenharmony_ci rcu_read_unlock(); 72662306a36Sopenharmony_ci if (!found) { 72762306a36Sopenharmony_ci memcpy(©->stateid, &args->coa_stateid, NFS4_STATEID_SIZE); 72862306a36Sopenharmony_ci nfs4_copy_cb_args(copy, args); 72962306a36Sopenharmony_ci list_add_tail(©->copies, &cps->clp->pending_cb_stateids); 73062306a36Sopenharmony_ci } else 73162306a36Sopenharmony_ci kfree(copy); 73262306a36Sopenharmony_ci spin_unlock(&cps->clp->cl_lock); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci trace_nfs4_cb_offload(&args->coa_fh, &args->coa_stateid, 73562306a36Sopenharmony_ci args->wr_count, args->error, 73662306a36Sopenharmony_ci args->wr_writeverf.committed); 73762306a36Sopenharmony_ci return 0; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4_2 */ 740