170ba381eSWeston Andros Adamson // SPDX-License-Identifier: GPL-2.0-only 270ba381eSWeston Andros Adamson /* 370ba381eSWeston Andros Adamson * NFS client support for local clients to bypass network stack 470ba381eSWeston Andros Adamson * 570ba381eSWeston Andros Adamson * Copyright (C) 2014 Weston Andros Adamson <dros@primarydata.com> 670ba381eSWeston Andros Adamson * Copyright (C) 2019 Trond Myklebust <trond.myklebust@hammerspace.com> 770ba381eSWeston Andros Adamson * Copyright (C) 2024 Mike Snitzer <snitzer@hammerspace.com> 870ba381eSWeston Andros Adamson * Copyright (C) 2024 NeilBrown <neilb@suse.de> 970ba381eSWeston Andros Adamson */ 1070ba381eSWeston Andros Adamson 1170ba381eSWeston Andros Adamson #include <linux/module.h> 1270ba381eSWeston Andros Adamson #include <linux/errno.h> 1370ba381eSWeston Andros Adamson #include <linux/vfs.h> 1470ba381eSWeston Andros Adamson #include <linux/file.h> 1570ba381eSWeston Andros Adamson #include <linux/inet.h> 1670ba381eSWeston Andros Adamson #include <linux/sunrpc/addr.h> 1770ba381eSWeston Andros Adamson #include <linux/inetdevice.h> 1870ba381eSWeston Andros Adamson #include <net/addrconf.h> 1970ba381eSWeston Andros Adamson #include <linux/nfs_common.h> 2070ba381eSWeston Andros Adamson #include <linux/nfslocalio.h> 2170ba381eSWeston Andros Adamson #include <linux/module.h> 2270ba381eSWeston Andros Adamson #include <linux/bvec.h> 2370ba381eSWeston Andros Adamson 2470ba381eSWeston Andros Adamson #include <linux/nfs.h> 2570ba381eSWeston Andros Adamson #include <linux/nfs_fs.h> 2670ba381eSWeston Andros Adamson #include <linux/nfs_xdr.h> 2770ba381eSWeston Andros Adamson 2870ba381eSWeston Andros Adamson #include "internal.h" 2970ba381eSWeston Andros Adamson #include "pnfs.h" 3070ba381eSWeston Andros Adamson #include "nfstrace.h" 3170ba381eSWeston Andros Adamson 3270ba381eSWeston Andros Adamson #define NFSDBG_FACILITY NFSDBG_VFS 3370ba381eSWeston Andros Adamson 3470ba381eSWeston Andros Adamson struct nfs_local_kiocb { 3570ba381eSWeston Andros Adamson struct kiocb kiocb; 3670ba381eSWeston Andros Adamson struct bio_vec *bvec; 3770ba381eSWeston Andros Adamson struct nfs_pgio_header *hdr; 3870ba381eSWeston Andros Adamson struct work_struct work; 3970ba381eSWeston Andros Adamson struct nfsd_file *localio; 4070ba381eSWeston Andros Adamson }; 4170ba381eSWeston Andros Adamson 4270ba381eSWeston Andros Adamson struct nfs_local_fsync_ctx { 4370ba381eSWeston Andros Adamson struct nfsd_file *localio; 4470ba381eSWeston Andros Adamson struct nfs_commit_data *data; 4570ba381eSWeston Andros Adamson struct work_struct work; 4670ba381eSWeston Andros Adamson struct kref kref; 4770ba381eSWeston Andros Adamson struct completion *done; 4870ba381eSWeston Andros Adamson }; 4970ba381eSWeston Andros Adamson static void nfs_local_fsync_work(struct work_struct *work); 5070ba381eSWeston Andros Adamson 5170ba381eSWeston Andros Adamson static bool localio_enabled __read_mostly = true; 5270ba381eSWeston Andros Adamson module_param(localio_enabled, bool, 0644); 5370ba381eSWeston Andros Adamson 54*56bcd0f0SMike Snitzer static inline bool nfs_client_is_local(const struct nfs_client *clp) 55*56bcd0f0SMike Snitzer { 56*56bcd0f0SMike Snitzer return !!test_bit(NFS_CS_LOCAL_IO, &clp->cl_flags); 57*56bcd0f0SMike Snitzer } 58*56bcd0f0SMike Snitzer 5970ba381eSWeston Andros Adamson bool nfs_server_is_local(const struct nfs_client *clp) 6070ba381eSWeston Andros Adamson { 61*56bcd0f0SMike Snitzer return nfs_client_is_local(clp) && localio_enabled; 6270ba381eSWeston Andros Adamson } 6370ba381eSWeston Andros Adamson EXPORT_SYMBOL_GPL(nfs_server_is_local); 6470ba381eSWeston Andros Adamson 6570ba381eSWeston Andros Adamson /* 66*56bcd0f0SMike Snitzer * UUID_IS_LOCAL XDR functions 67*56bcd0f0SMike Snitzer */ 68*56bcd0f0SMike Snitzer 69*56bcd0f0SMike Snitzer static void localio_xdr_enc_uuidargs(struct rpc_rqst *req, 70*56bcd0f0SMike Snitzer struct xdr_stream *xdr, 71*56bcd0f0SMike Snitzer const void *data) 72*56bcd0f0SMike Snitzer { 73*56bcd0f0SMike Snitzer const u8 *uuid = data; 74*56bcd0f0SMike Snitzer 75*56bcd0f0SMike Snitzer encode_opaque_fixed(xdr, uuid, UUID_SIZE); 76*56bcd0f0SMike Snitzer } 77*56bcd0f0SMike Snitzer 78*56bcd0f0SMike Snitzer static int localio_xdr_dec_uuidres(struct rpc_rqst *req, 79*56bcd0f0SMike Snitzer struct xdr_stream *xdr, 80*56bcd0f0SMike Snitzer void *result) 81*56bcd0f0SMike Snitzer { 82*56bcd0f0SMike Snitzer /* void return */ 83*56bcd0f0SMike Snitzer return 0; 84*56bcd0f0SMike Snitzer } 85*56bcd0f0SMike Snitzer 86*56bcd0f0SMike Snitzer static const struct rpc_procinfo nfs_localio_procedures[] = { 87*56bcd0f0SMike Snitzer [LOCALIOPROC_UUID_IS_LOCAL] = { 88*56bcd0f0SMike Snitzer .p_proc = LOCALIOPROC_UUID_IS_LOCAL, 89*56bcd0f0SMike Snitzer .p_encode = localio_xdr_enc_uuidargs, 90*56bcd0f0SMike Snitzer .p_decode = localio_xdr_dec_uuidres, 91*56bcd0f0SMike Snitzer .p_arglen = XDR_QUADLEN(UUID_SIZE), 92*56bcd0f0SMike Snitzer .p_replen = 0, 93*56bcd0f0SMike Snitzer .p_statidx = LOCALIOPROC_UUID_IS_LOCAL, 94*56bcd0f0SMike Snitzer .p_name = "UUID_IS_LOCAL", 95*56bcd0f0SMike Snitzer }, 96*56bcd0f0SMike Snitzer }; 97*56bcd0f0SMike Snitzer 98*56bcd0f0SMike Snitzer static unsigned int nfs_localio_counts[ARRAY_SIZE(nfs_localio_procedures)]; 99*56bcd0f0SMike Snitzer static const struct rpc_version nfslocalio_version1 = { 100*56bcd0f0SMike Snitzer .number = 1, 101*56bcd0f0SMike Snitzer .nrprocs = ARRAY_SIZE(nfs_localio_procedures), 102*56bcd0f0SMike Snitzer .procs = nfs_localio_procedures, 103*56bcd0f0SMike Snitzer .counts = nfs_localio_counts, 104*56bcd0f0SMike Snitzer }; 105*56bcd0f0SMike Snitzer 106*56bcd0f0SMike Snitzer static const struct rpc_version *nfslocalio_version[] = { 107*56bcd0f0SMike Snitzer [1] = &nfslocalio_version1, 108*56bcd0f0SMike Snitzer }; 109*56bcd0f0SMike Snitzer 110*56bcd0f0SMike Snitzer extern const struct rpc_program nfslocalio_program; 111*56bcd0f0SMike Snitzer static struct rpc_stat nfslocalio_rpcstat = { &nfslocalio_program }; 112*56bcd0f0SMike Snitzer 113*56bcd0f0SMike Snitzer const struct rpc_program nfslocalio_program = { 114*56bcd0f0SMike Snitzer .name = "nfslocalio", 115*56bcd0f0SMike Snitzer .number = NFS_LOCALIO_PROGRAM, 116*56bcd0f0SMike Snitzer .nrvers = ARRAY_SIZE(nfslocalio_version), 117*56bcd0f0SMike Snitzer .version = nfslocalio_version, 118*56bcd0f0SMike Snitzer .stats = &nfslocalio_rpcstat, 119*56bcd0f0SMike Snitzer }; 120*56bcd0f0SMike Snitzer 121*56bcd0f0SMike Snitzer /* 12270ba381eSWeston Andros Adamson * nfs_local_enable - enable local i/o for an nfs_client 12370ba381eSWeston Andros Adamson */ 124*56bcd0f0SMike Snitzer static void nfs_local_enable(struct nfs_client *clp) 12570ba381eSWeston Andros Adamson { 12670ba381eSWeston Andros Adamson spin_lock(&clp->cl_localio_lock); 12770ba381eSWeston Andros Adamson set_bit(NFS_CS_LOCAL_IO, &clp->cl_flags); 12870ba381eSWeston Andros Adamson trace_nfs_local_enable(clp); 12970ba381eSWeston Andros Adamson spin_unlock(&clp->cl_localio_lock); 13070ba381eSWeston Andros Adamson } 13170ba381eSWeston Andros Adamson 13270ba381eSWeston Andros Adamson /* 13370ba381eSWeston Andros Adamson * nfs_local_disable - disable local i/o for an nfs_client 13470ba381eSWeston Andros Adamson */ 13570ba381eSWeston Andros Adamson void nfs_local_disable(struct nfs_client *clp) 13670ba381eSWeston Andros Adamson { 13770ba381eSWeston Andros Adamson spin_lock(&clp->cl_localio_lock); 13870ba381eSWeston Andros Adamson if (test_and_clear_bit(NFS_CS_LOCAL_IO, &clp->cl_flags)) { 13970ba381eSWeston Andros Adamson trace_nfs_local_disable(clp); 14070ba381eSWeston Andros Adamson nfs_uuid_invalidate_one_client(&clp->cl_uuid); 14170ba381eSWeston Andros Adamson } 14270ba381eSWeston Andros Adamson spin_unlock(&clp->cl_localio_lock); 14370ba381eSWeston Andros Adamson } 14470ba381eSWeston Andros Adamson 14570ba381eSWeston Andros Adamson /* 146*56bcd0f0SMike Snitzer * nfs_init_localioclient - Initialise an NFS localio client connection 147*56bcd0f0SMike Snitzer */ 148*56bcd0f0SMike Snitzer static struct rpc_clnt *nfs_init_localioclient(struct nfs_client *clp) 149*56bcd0f0SMike Snitzer { 150*56bcd0f0SMike Snitzer struct rpc_clnt *rpcclient_localio; 151*56bcd0f0SMike Snitzer 152*56bcd0f0SMike Snitzer rpcclient_localio = rpc_bind_new_program(clp->cl_rpcclient, 153*56bcd0f0SMike Snitzer &nfslocalio_program, 1); 154*56bcd0f0SMike Snitzer 155*56bcd0f0SMike Snitzer dprintk_rcu("%s: server (%s) %s NFS LOCALIO.\n", 156*56bcd0f0SMike Snitzer __func__, rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR), 157*56bcd0f0SMike Snitzer (IS_ERR(rpcclient_localio) ? "does not support" : "supports")); 158*56bcd0f0SMike Snitzer 159*56bcd0f0SMike Snitzer return rpcclient_localio; 160*56bcd0f0SMike Snitzer } 161*56bcd0f0SMike Snitzer 162*56bcd0f0SMike Snitzer static bool nfs_server_uuid_is_local(struct nfs_client *clp) 163*56bcd0f0SMike Snitzer { 164*56bcd0f0SMike Snitzer u8 uuid[UUID_SIZE]; 165*56bcd0f0SMike Snitzer struct rpc_message msg = { 166*56bcd0f0SMike Snitzer .rpc_argp = &uuid, 167*56bcd0f0SMike Snitzer }; 168*56bcd0f0SMike Snitzer struct rpc_clnt *rpcclient_localio; 169*56bcd0f0SMike Snitzer int status; 170*56bcd0f0SMike Snitzer 171*56bcd0f0SMike Snitzer rpcclient_localio = nfs_init_localioclient(clp); 172*56bcd0f0SMike Snitzer if (IS_ERR(rpcclient_localio)) 173*56bcd0f0SMike Snitzer return false; 174*56bcd0f0SMike Snitzer 175*56bcd0f0SMike Snitzer export_uuid(uuid, &clp->cl_uuid.uuid); 176*56bcd0f0SMike Snitzer 177*56bcd0f0SMike Snitzer msg.rpc_proc = &nfs_localio_procedures[LOCALIOPROC_UUID_IS_LOCAL]; 178*56bcd0f0SMike Snitzer status = rpc_call_sync(rpcclient_localio, &msg, 0); 179*56bcd0f0SMike Snitzer dprintk("%s: NFS reply UUID_IS_LOCAL: status=%d\n", 180*56bcd0f0SMike Snitzer __func__, status); 181*56bcd0f0SMike Snitzer rpc_shutdown_client(rpcclient_localio); 182*56bcd0f0SMike Snitzer 183*56bcd0f0SMike Snitzer /* Server is only local if it initialized required struct members */ 184*56bcd0f0SMike Snitzer if (status || !clp->cl_uuid.net || !clp->cl_uuid.dom) 185*56bcd0f0SMike Snitzer return false; 186*56bcd0f0SMike Snitzer 187*56bcd0f0SMike Snitzer return true; 188*56bcd0f0SMike Snitzer } 189*56bcd0f0SMike Snitzer 190*56bcd0f0SMike Snitzer /* 19170ba381eSWeston Andros Adamson * nfs_local_probe - probe local i/o support for an nfs_server and nfs_client 192*56bcd0f0SMike Snitzer * - called after alloc_client and init_client (so cl_rpcclient exists) 193*56bcd0f0SMike Snitzer * - this function is idempotent, it can be called for old or new clients 19470ba381eSWeston Andros Adamson */ 19570ba381eSWeston Andros Adamson void nfs_local_probe(struct nfs_client *clp) 19670ba381eSWeston Andros Adamson { 197*56bcd0f0SMike Snitzer /* Disallow localio if disabled via sysfs or AUTH_SYS isn't used */ 198*56bcd0f0SMike Snitzer if (!localio_enabled || 199*56bcd0f0SMike Snitzer clp->cl_rpcclient->cl_auth->au_flavor != RPC_AUTH_UNIX) { 200*56bcd0f0SMike Snitzer nfs_local_disable(clp); 201*56bcd0f0SMike Snitzer return; 202*56bcd0f0SMike Snitzer } 203*56bcd0f0SMike Snitzer 204*56bcd0f0SMike Snitzer if (nfs_client_is_local(clp)) { 205*56bcd0f0SMike Snitzer /* If already enabled, disable and re-enable */ 206*56bcd0f0SMike Snitzer nfs_local_disable(clp); 207*56bcd0f0SMike Snitzer } 208*56bcd0f0SMike Snitzer 209*56bcd0f0SMike Snitzer nfs_uuid_begin(&clp->cl_uuid); 210*56bcd0f0SMike Snitzer if (nfs_server_uuid_is_local(clp)) 211*56bcd0f0SMike Snitzer nfs_local_enable(clp); 212*56bcd0f0SMike Snitzer nfs_uuid_end(&clp->cl_uuid); 21370ba381eSWeston Andros Adamson } 21470ba381eSWeston Andros Adamson EXPORT_SYMBOL_GPL(nfs_local_probe); 21570ba381eSWeston Andros Adamson 21670ba381eSWeston Andros Adamson /* 21770ba381eSWeston Andros Adamson * nfs_local_open_fh - open a local filehandle in terms of nfsd_file 21870ba381eSWeston Andros Adamson * 21970ba381eSWeston Andros Adamson * Returns a pointer to a struct nfsd_file or NULL 22070ba381eSWeston Andros Adamson */ 22170ba381eSWeston Andros Adamson struct nfsd_file * 22270ba381eSWeston Andros Adamson nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred, 22370ba381eSWeston Andros Adamson struct nfs_fh *fh, const fmode_t mode) 22470ba381eSWeston Andros Adamson { 22570ba381eSWeston Andros Adamson struct nfsd_file *localio; 22670ba381eSWeston Andros Adamson int status; 22770ba381eSWeston Andros Adamson 22870ba381eSWeston Andros Adamson if (!nfs_server_is_local(clp)) 22970ba381eSWeston Andros Adamson return NULL; 23070ba381eSWeston Andros Adamson if (mode & ~(FMODE_READ | FMODE_WRITE)) 23170ba381eSWeston Andros Adamson return NULL; 23270ba381eSWeston Andros Adamson 23370ba381eSWeston Andros Adamson localio = nfs_open_local_fh(&clp->cl_uuid, clp->cl_rpcclient, 23470ba381eSWeston Andros Adamson cred, fh, mode); 23570ba381eSWeston Andros Adamson if (IS_ERR(localio)) { 23670ba381eSWeston Andros Adamson status = PTR_ERR(localio); 23770ba381eSWeston Andros Adamson trace_nfs_local_open_fh(fh, mode, status); 23870ba381eSWeston Andros Adamson switch (status) { 23970ba381eSWeston Andros Adamson case -ENOMEM: 24070ba381eSWeston Andros Adamson case -ENXIO: 24170ba381eSWeston Andros Adamson case -ENOENT: 242*56bcd0f0SMike Snitzer /* Revalidate localio, will disable if unsupported */ 243*56bcd0f0SMike Snitzer nfs_local_probe(clp); 24470ba381eSWeston Andros Adamson } 24570ba381eSWeston Andros Adamson return NULL; 24670ba381eSWeston Andros Adamson } 24770ba381eSWeston Andros Adamson return localio; 24870ba381eSWeston Andros Adamson } 24970ba381eSWeston Andros Adamson EXPORT_SYMBOL_GPL(nfs_local_open_fh); 25070ba381eSWeston Andros Adamson 25170ba381eSWeston Andros Adamson static struct bio_vec * 25270ba381eSWeston Andros Adamson nfs_bvec_alloc_and_import_pagevec(struct page **pagevec, 25370ba381eSWeston Andros Adamson unsigned int npages, gfp_t flags) 25470ba381eSWeston Andros Adamson { 25570ba381eSWeston Andros Adamson struct bio_vec *bvec, *p; 25670ba381eSWeston Andros Adamson 25770ba381eSWeston Andros Adamson bvec = kmalloc_array(npages, sizeof(*bvec), flags); 25870ba381eSWeston Andros Adamson if (bvec != NULL) { 25970ba381eSWeston Andros Adamson for (p = bvec; npages > 0; p++, pagevec++, npages--) { 26070ba381eSWeston Andros Adamson p->bv_page = *pagevec; 26170ba381eSWeston Andros Adamson p->bv_len = PAGE_SIZE; 26270ba381eSWeston Andros Adamson p->bv_offset = 0; 26370ba381eSWeston Andros Adamson } 26470ba381eSWeston Andros Adamson } 26570ba381eSWeston Andros Adamson return bvec; 26670ba381eSWeston Andros Adamson } 26770ba381eSWeston Andros Adamson 26870ba381eSWeston Andros Adamson static void 26970ba381eSWeston Andros Adamson nfs_local_iocb_free(struct nfs_local_kiocb *iocb) 27070ba381eSWeston Andros Adamson { 27170ba381eSWeston Andros Adamson kfree(iocb->bvec); 27270ba381eSWeston Andros Adamson kfree(iocb); 27370ba381eSWeston Andros Adamson } 27470ba381eSWeston Andros Adamson 27570ba381eSWeston Andros Adamson static struct nfs_local_kiocb * 27670ba381eSWeston Andros Adamson nfs_local_iocb_alloc(struct nfs_pgio_header *hdr, 27770ba381eSWeston Andros Adamson struct nfsd_file *localio, gfp_t flags) 27870ba381eSWeston Andros Adamson { 27970ba381eSWeston Andros Adamson struct nfs_local_kiocb *iocb; 28070ba381eSWeston Andros Adamson 28170ba381eSWeston Andros Adamson iocb = kmalloc(sizeof(*iocb), flags); 28270ba381eSWeston Andros Adamson if (iocb == NULL) 28370ba381eSWeston Andros Adamson return NULL; 28470ba381eSWeston Andros Adamson iocb->bvec = nfs_bvec_alloc_and_import_pagevec(hdr->page_array.pagevec, 28570ba381eSWeston Andros Adamson hdr->page_array.npages, flags); 28670ba381eSWeston Andros Adamson if (iocb->bvec == NULL) { 28770ba381eSWeston Andros Adamson kfree(iocb); 28870ba381eSWeston Andros Adamson return NULL; 28970ba381eSWeston Andros Adamson } 29070ba381eSWeston Andros Adamson init_sync_kiocb(&iocb->kiocb, nfs_to->nfsd_file_file(localio)); 29170ba381eSWeston Andros Adamson iocb->kiocb.ki_pos = hdr->args.offset; 29270ba381eSWeston Andros Adamson iocb->localio = localio; 29370ba381eSWeston Andros Adamson iocb->hdr = hdr; 29470ba381eSWeston Andros Adamson iocb->kiocb.ki_flags &= ~IOCB_APPEND; 29570ba381eSWeston Andros Adamson return iocb; 29670ba381eSWeston Andros Adamson } 29770ba381eSWeston Andros Adamson 29870ba381eSWeston Andros Adamson static void 29970ba381eSWeston Andros Adamson nfs_local_iter_init(struct iov_iter *i, struct nfs_local_kiocb *iocb, int dir) 30070ba381eSWeston Andros Adamson { 30170ba381eSWeston Andros Adamson struct nfs_pgio_header *hdr = iocb->hdr; 30270ba381eSWeston Andros Adamson 30370ba381eSWeston Andros Adamson iov_iter_bvec(i, dir, iocb->bvec, hdr->page_array.npages, 30470ba381eSWeston Andros Adamson hdr->args.count + hdr->args.pgbase); 30570ba381eSWeston Andros Adamson if (hdr->args.pgbase != 0) 30670ba381eSWeston Andros Adamson iov_iter_advance(i, hdr->args.pgbase); 30770ba381eSWeston Andros Adamson } 30870ba381eSWeston Andros Adamson 30970ba381eSWeston Andros Adamson static void 31070ba381eSWeston Andros Adamson nfs_local_hdr_release(struct nfs_pgio_header *hdr, 31170ba381eSWeston Andros Adamson const struct rpc_call_ops *call_ops) 31270ba381eSWeston Andros Adamson { 31370ba381eSWeston Andros Adamson call_ops->rpc_call_done(&hdr->task, hdr); 31470ba381eSWeston Andros Adamson call_ops->rpc_release(hdr); 31570ba381eSWeston Andros Adamson } 31670ba381eSWeston Andros Adamson 31770ba381eSWeston Andros Adamson static void 31870ba381eSWeston Andros Adamson nfs_local_pgio_init(struct nfs_pgio_header *hdr, 31970ba381eSWeston Andros Adamson const struct rpc_call_ops *call_ops) 32070ba381eSWeston Andros Adamson { 32170ba381eSWeston Andros Adamson hdr->task.tk_ops = call_ops; 32270ba381eSWeston Andros Adamson if (!hdr->task.tk_start) 32370ba381eSWeston Andros Adamson hdr->task.tk_start = ktime_get(); 32470ba381eSWeston Andros Adamson } 32570ba381eSWeston Andros Adamson 32670ba381eSWeston Andros Adamson static void 32770ba381eSWeston Andros Adamson nfs_local_pgio_done(struct nfs_pgio_header *hdr, long status) 32870ba381eSWeston Andros Adamson { 32970ba381eSWeston Andros Adamson if (status >= 0) { 33070ba381eSWeston Andros Adamson hdr->res.count = status; 33170ba381eSWeston Andros Adamson hdr->res.op_status = NFS4_OK; 33270ba381eSWeston Andros Adamson hdr->task.tk_status = 0; 33370ba381eSWeston Andros Adamson } else { 33470ba381eSWeston Andros Adamson hdr->res.op_status = nfs4_stat_to_errno(status); 33570ba381eSWeston Andros Adamson hdr->task.tk_status = status; 33670ba381eSWeston Andros Adamson } 33770ba381eSWeston Andros Adamson } 33870ba381eSWeston Andros Adamson 33970ba381eSWeston Andros Adamson static void 34070ba381eSWeston Andros Adamson nfs_local_pgio_release(struct nfs_local_kiocb *iocb) 34170ba381eSWeston Andros Adamson { 34270ba381eSWeston Andros Adamson struct nfs_pgio_header *hdr = iocb->hdr; 34370ba381eSWeston Andros Adamson 34470ba381eSWeston Andros Adamson nfs_to->nfsd_file_put_local(iocb->localio); 34570ba381eSWeston Andros Adamson nfs_local_iocb_free(iocb); 34670ba381eSWeston Andros Adamson nfs_local_hdr_release(hdr, hdr->task.tk_ops); 34770ba381eSWeston Andros Adamson } 34870ba381eSWeston Andros Adamson 34970ba381eSWeston Andros Adamson static void 35070ba381eSWeston Andros Adamson nfs_local_read_done(struct nfs_local_kiocb *iocb, long status) 35170ba381eSWeston Andros Adamson { 35270ba381eSWeston Andros Adamson struct nfs_pgio_header *hdr = iocb->hdr; 35370ba381eSWeston Andros Adamson struct file *filp = iocb->kiocb.ki_filp; 35470ba381eSWeston Andros Adamson 35570ba381eSWeston Andros Adamson nfs_local_pgio_done(hdr, status); 35670ba381eSWeston Andros Adamson 35770ba381eSWeston Andros Adamson if (hdr->res.count != hdr->args.count || 35870ba381eSWeston Andros Adamson hdr->args.offset + hdr->res.count >= i_size_read(file_inode(filp))) 35970ba381eSWeston Andros Adamson hdr->res.eof = true; 36070ba381eSWeston Andros Adamson 36170ba381eSWeston Andros Adamson dprintk("%s: read %ld bytes eof %d.\n", __func__, 36270ba381eSWeston Andros Adamson status > 0 ? status : 0, hdr->res.eof); 36370ba381eSWeston Andros Adamson } 36470ba381eSWeston Andros Adamson 365b9f5dd57STrond Myklebust static void nfs_local_call_read(struct work_struct *work) 366b9f5dd57STrond Myklebust { 367b9f5dd57STrond Myklebust struct nfs_local_kiocb *iocb = 368b9f5dd57STrond Myklebust container_of(work, struct nfs_local_kiocb, work); 369b9f5dd57STrond Myklebust struct file *filp = iocb->kiocb.ki_filp; 370b9f5dd57STrond Myklebust const struct cred *save_cred; 371b9f5dd57STrond Myklebust struct iov_iter iter; 372b9f5dd57STrond Myklebust ssize_t status; 373b9f5dd57STrond Myklebust 374b9f5dd57STrond Myklebust save_cred = override_creds(filp->f_cred); 375b9f5dd57STrond Myklebust 376b9f5dd57STrond Myklebust nfs_local_iter_init(&iter, iocb, READ); 377b9f5dd57STrond Myklebust 378b9f5dd57STrond Myklebust status = filp->f_op->read_iter(&iocb->kiocb, &iter); 379b9f5dd57STrond Myklebust WARN_ON_ONCE(status == -EIOCBQUEUED); 380b9f5dd57STrond Myklebust 381b9f5dd57STrond Myklebust nfs_local_read_done(iocb, status); 382b9f5dd57STrond Myklebust nfs_local_pgio_release(iocb); 383b9f5dd57STrond Myklebust 384b9f5dd57STrond Myklebust revert_creds(save_cred); 385b9f5dd57STrond Myklebust } 386b9f5dd57STrond Myklebust 38770ba381eSWeston Andros Adamson static int 38870ba381eSWeston Andros Adamson nfs_do_local_read(struct nfs_pgio_header *hdr, 38970ba381eSWeston Andros Adamson struct nfsd_file *localio, 39070ba381eSWeston Andros Adamson const struct rpc_call_ops *call_ops) 39170ba381eSWeston Andros Adamson { 39270ba381eSWeston Andros Adamson struct nfs_local_kiocb *iocb; 39370ba381eSWeston Andros Adamson 39470ba381eSWeston Andros Adamson dprintk("%s: vfs_read count=%u pos=%llu\n", 39570ba381eSWeston Andros Adamson __func__, hdr->args.count, hdr->args.offset); 39670ba381eSWeston Andros Adamson 39770ba381eSWeston Andros Adamson iocb = nfs_local_iocb_alloc(hdr, localio, GFP_KERNEL); 39870ba381eSWeston Andros Adamson if (iocb == NULL) 39970ba381eSWeston Andros Adamson return -ENOMEM; 40070ba381eSWeston Andros Adamson 40170ba381eSWeston Andros Adamson nfs_local_pgio_init(hdr, call_ops); 40270ba381eSWeston Andros Adamson hdr->res.eof = false; 40370ba381eSWeston Andros Adamson 404b9f5dd57STrond Myklebust INIT_WORK(&iocb->work, nfs_local_call_read); 405b9f5dd57STrond Myklebust queue_work(nfslocaliod_workqueue, &iocb->work); 40670ba381eSWeston Andros Adamson 40770ba381eSWeston Andros Adamson return 0; 40870ba381eSWeston Andros Adamson } 40970ba381eSWeston Andros Adamson 41070ba381eSWeston Andros Adamson static void 41170ba381eSWeston Andros Adamson nfs_copy_boot_verifier(struct nfs_write_verifier *verifier, struct inode *inode) 41270ba381eSWeston Andros Adamson { 41370ba381eSWeston Andros Adamson struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 41470ba381eSWeston Andros Adamson u32 *verf = (u32 *)verifier->data; 41570ba381eSWeston Andros Adamson int seq = 0; 41670ba381eSWeston Andros Adamson 41770ba381eSWeston Andros Adamson do { 41870ba381eSWeston Andros Adamson read_seqbegin_or_lock(&clp->cl_boot_lock, &seq); 41970ba381eSWeston Andros Adamson verf[0] = (u32)clp->cl_nfssvc_boot.tv_sec; 42070ba381eSWeston Andros Adamson verf[1] = (u32)clp->cl_nfssvc_boot.tv_nsec; 42170ba381eSWeston Andros Adamson } while (need_seqretry(&clp->cl_boot_lock, seq)); 42270ba381eSWeston Andros Adamson done_seqretry(&clp->cl_boot_lock, seq); 42370ba381eSWeston Andros Adamson } 42470ba381eSWeston Andros Adamson 42570ba381eSWeston Andros Adamson static void 42670ba381eSWeston Andros Adamson nfs_reset_boot_verifier(struct inode *inode) 42770ba381eSWeston Andros Adamson { 42870ba381eSWeston Andros Adamson struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 42970ba381eSWeston Andros Adamson 43070ba381eSWeston Andros Adamson write_seqlock(&clp->cl_boot_lock); 43170ba381eSWeston Andros Adamson ktime_get_real_ts64(&clp->cl_nfssvc_boot); 43270ba381eSWeston Andros Adamson write_sequnlock(&clp->cl_boot_lock); 43370ba381eSWeston Andros Adamson } 43470ba381eSWeston Andros Adamson 43570ba381eSWeston Andros Adamson static void 43670ba381eSWeston Andros Adamson nfs_set_local_verifier(struct inode *inode, 43770ba381eSWeston Andros Adamson struct nfs_writeverf *verf, 43870ba381eSWeston Andros Adamson enum nfs3_stable_how how) 43970ba381eSWeston Andros Adamson { 44070ba381eSWeston Andros Adamson nfs_copy_boot_verifier(&verf->verifier, inode); 44170ba381eSWeston Andros Adamson verf->committed = how; 44270ba381eSWeston Andros Adamson } 44370ba381eSWeston Andros Adamson 44470ba381eSWeston Andros Adamson /* Factored out from fs/nfsd/vfs.h:fh_getattr() */ 44570ba381eSWeston Andros Adamson static int __vfs_getattr(struct path *p, struct kstat *stat, int version) 44670ba381eSWeston Andros Adamson { 44770ba381eSWeston Andros Adamson u32 request_mask = STATX_BASIC_STATS; 44870ba381eSWeston Andros Adamson 44970ba381eSWeston Andros Adamson if (version == 4) 45070ba381eSWeston Andros Adamson request_mask |= (STATX_BTIME | STATX_CHANGE_COOKIE); 45170ba381eSWeston Andros Adamson return vfs_getattr(p, stat, request_mask, AT_STATX_SYNC_AS_STAT); 45270ba381eSWeston Andros Adamson } 45370ba381eSWeston Andros Adamson 45470ba381eSWeston Andros Adamson /* Copied from fs/nfsd/nfsfh.c:nfsd4_change_attribute() */ 45570ba381eSWeston Andros Adamson static u64 __nfsd4_change_attribute(const struct kstat *stat, 45670ba381eSWeston Andros Adamson const struct inode *inode) 45770ba381eSWeston Andros Adamson { 45870ba381eSWeston Andros Adamson u64 chattr; 45970ba381eSWeston Andros Adamson 46070ba381eSWeston Andros Adamson if (stat->result_mask & STATX_CHANGE_COOKIE) { 46170ba381eSWeston Andros Adamson chattr = stat->change_cookie; 46270ba381eSWeston Andros Adamson if (S_ISREG(inode->i_mode) && 46370ba381eSWeston Andros Adamson !(stat->attributes & STATX_ATTR_CHANGE_MONOTONIC)) { 46470ba381eSWeston Andros Adamson chattr += (u64)stat->ctime.tv_sec << 30; 46570ba381eSWeston Andros Adamson chattr += stat->ctime.tv_nsec; 46670ba381eSWeston Andros Adamson } 46770ba381eSWeston Andros Adamson } else { 46870ba381eSWeston Andros Adamson chattr = time_to_chattr(&stat->ctime); 46970ba381eSWeston Andros Adamson } 47070ba381eSWeston Andros Adamson return chattr; 47170ba381eSWeston Andros Adamson } 47270ba381eSWeston Andros Adamson 47370ba381eSWeston Andros Adamson static void nfs_local_vfs_getattr(struct nfs_local_kiocb *iocb) 47470ba381eSWeston Andros Adamson { 47570ba381eSWeston Andros Adamson struct kstat stat; 47670ba381eSWeston Andros Adamson struct file *filp = iocb->kiocb.ki_filp; 47770ba381eSWeston Andros Adamson struct nfs_pgio_header *hdr = iocb->hdr; 47870ba381eSWeston Andros Adamson struct nfs_fattr *fattr = hdr->res.fattr; 47970ba381eSWeston Andros Adamson int version = NFS_PROTO(hdr->inode)->version; 48070ba381eSWeston Andros Adamson 48170ba381eSWeston Andros Adamson if (unlikely(!fattr) || __vfs_getattr(&filp->f_path, &stat, version)) 48270ba381eSWeston Andros Adamson return; 48370ba381eSWeston Andros Adamson 48470ba381eSWeston Andros Adamson fattr->valid = (NFS_ATTR_FATTR_FILEID | 48570ba381eSWeston Andros Adamson NFS_ATTR_FATTR_CHANGE | 48670ba381eSWeston Andros Adamson NFS_ATTR_FATTR_SIZE | 48770ba381eSWeston Andros Adamson NFS_ATTR_FATTR_ATIME | 48870ba381eSWeston Andros Adamson NFS_ATTR_FATTR_MTIME | 48970ba381eSWeston Andros Adamson NFS_ATTR_FATTR_CTIME | 49070ba381eSWeston Andros Adamson NFS_ATTR_FATTR_SPACE_USED); 49170ba381eSWeston Andros Adamson 49270ba381eSWeston Andros Adamson fattr->fileid = stat.ino; 49370ba381eSWeston Andros Adamson fattr->size = stat.size; 49470ba381eSWeston Andros Adamson fattr->atime = stat.atime; 49570ba381eSWeston Andros Adamson fattr->mtime = stat.mtime; 49670ba381eSWeston Andros Adamson fattr->ctime = stat.ctime; 49770ba381eSWeston Andros Adamson if (version == 4) { 49870ba381eSWeston Andros Adamson fattr->change_attr = 49970ba381eSWeston Andros Adamson __nfsd4_change_attribute(&stat, file_inode(filp)); 50070ba381eSWeston Andros Adamson } else 50170ba381eSWeston Andros Adamson fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime); 50270ba381eSWeston Andros Adamson fattr->du.nfs3.used = stat.blocks << 9; 50370ba381eSWeston Andros Adamson } 50470ba381eSWeston Andros Adamson 50570ba381eSWeston Andros Adamson static void 50670ba381eSWeston Andros Adamson nfs_local_write_done(struct nfs_local_kiocb *iocb, long status) 50770ba381eSWeston Andros Adamson { 50870ba381eSWeston Andros Adamson struct nfs_pgio_header *hdr = iocb->hdr; 50970ba381eSWeston Andros Adamson struct inode *inode = hdr->inode; 51070ba381eSWeston Andros Adamson 51170ba381eSWeston Andros Adamson dprintk("%s: wrote %ld bytes.\n", __func__, status > 0 ? status : 0); 51270ba381eSWeston Andros Adamson 51370ba381eSWeston Andros Adamson /* Handle short writes as if they are ENOSPC */ 51470ba381eSWeston Andros Adamson if (status > 0 && status < hdr->args.count) { 51570ba381eSWeston Andros Adamson hdr->mds_offset += status; 51670ba381eSWeston Andros Adamson hdr->args.offset += status; 51770ba381eSWeston Andros Adamson hdr->args.pgbase += status; 51870ba381eSWeston Andros Adamson hdr->args.count -= status; 51970ba381eSWeston Andros Adamson nfs_set_pgio_error(hdr, -ENOSPC, hdr->args.offset); 52070ba381eSWeston Andros Adamson status = -ENOSPC; 52170ba381eSWeston Andros Adamson } 52270ba381eSWeston Andros Adamson if (status < 0) 52370ba381eSWeston Andros Adamson nfs_reset_boot_verifier(inode); 52470ba381eSWeston Andros Adamson else if (nfs_should_remove_suid(inode)) { 52570ba381eSWeston Andros Adamson /* Deal with the suid/sgid bit corner case */ 52670ba381eSWeston Andros Adamson spin_lock(&inode->i_lock); 52770ba381eSWeston Andros Adamson nfs_set_cache_invalid(inode, NFS_INO_INVALID_MODE); 52870ba381eSWeston Andros Adamson spin_unlock(&inode->i_lock); 52970ba381eSWeston Andros Adamson } 53070ba381eSWeston Andros Adamson nfs_local_pgio_done(hdr, status); 53170ba381eSWeston Andros Adamson } 53270ba381eSWeston Andros Adamson 533b9f5dd57STrond Myklebust static void nfs_local_call_write(struct work_struct *work) 534b9f5dd57STrond Myklebust { 535b9f5dd57STrond Myklebust struct nfs_local_kiocb *iocb = 536b9f5dd57STrond Myklebust container_of(work, struct nfs_local_kiocb, work); 537b9f5dd57STrond Myklebust struct file *filp = iocb->kiocb.ki_filp; 538b9f5dd57STrond Myklebust unsigned long old_flags = current->flags; 539b9f5dd57STrond Myklebust const struct cred *save_cred; 540b9f5dd57STrond Myklebust struct iov_iter iter; 541b9f5dd57STrond Myklebust ssize_t status; 542b9f5dd57STrond Myklebust 543b9f5dd57STrond Myklebust current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO; 544b9f5dd57STrond Myklebust save_cred = override_creds(filp->f_cred); 545b9f5dd57STrond Myklebust 546b9f5dd57STrond Myklebust nfs_local_iter_init(&iter, iocb, WRITE); 547b9f5dd57STrond Myklebust 548b9f5dd57STrond Myklebust file_start_write(filp); 549b9f5dd57STrond Myklebust status = filp->f_op->write_iter(&iocb->kiocb, &iter); 550b9f5dd57STrond Myklebust file_end_write(filp); 551b9f5dd57STrond Myklebust WARN_ON_ONCE(status == -EIOCBQUEUED); 552b9f5dd57STrond Myklebust 553b9f5dd57STrond Myklebust nfs_local_write_done(iocb, status); 554b9f5dd57STrond Myklebust nfs_local_vfs_getattr(iocb); 555b9f5dd57STrond Myklebust nfs_local_pgio_release(iocb); 556b9f5dd57STrond Myklebust 557b9f5dd57STrond Myklebust revert_creds(save_cred); 558b9f5dd57STrond Myklebust current->flags = old_flags; 559b9f5dd57STrond Myklebust } 560b9f5dd57STrond Myklebust 56170ba381eSWeston Andros Adamson static int 56270ba381eSWeston Andros Adamson nfs_do_local_write(struct nfs_pgio_header *hdr, 56370ba381eSWeston Andros Adamson struct nfsd_file *localio, 56470ba381eSWeston Andros Adamson const struct rpc_call_ops *call_ops) 56570ba381eSWeston Andros Adamson { 56670ba381eSWeston Andros Adamson struct nfs_local_kiocb *iocb; 56770ba381eSWeston Andros Adamson 56870ba381eSWeston Andros Adamson dprintk("%s: vfs_write count=%u pos=%llu %s\n", 56970ba381eSWeston Andros Adamson __func__, hdr->args.count, hdr->args.offset, 57070ba381eSWeston Andros Adamson (hdr->args.stable == NFS_UNSTABLE) ? "unstable" : "stable"); 57170ba381eSWeston Andros Adamson 57270ba381eSWeston Andros Adamson iocb = nfs_local_iocb_alloc(hdr, localio, GFP_NOIO); 57370ba381eSWeston Andros Adamson if (iocb == NULL) 57470ba381eSWeston Andros Adamson return -ENOMEM; 57570ba381eSWeston Andros Adamson 57670ba381eSWeston Andros Adamson switch (hdr->args.stable) { 57770ba381eSWeston Andros Adamson default: 57870ba381eSWeston Andros Adamson break; 57970ba381eSWeston Andros Adamson case NFS_DATA_SYNC: 58070ba381eSWeston Andros Adamson iocb->kiocb.ki_flags |= IOCB_DSYNC; 58170ba381eSWeston Andros Adamson break; 58270ba381eSWeston Andros Adamson case NFS_FILE_SYNC: 58370ba381eSWeston Andros Adamson iocb->kiocb.ki_flags |= IOCB_DSYNC|IOCB_SYNC; 58470ba381eSWeston Andros Adamson } 58570ba381eSWeston Andros Adamson nfs_local_pgio_init(hdr, call_ops); 58670ba381eSWeston Andros Adamson 58770ba381eSWeston Andros Adamson nfs_set_local_verifier(hdr->inode, hdr->res.verf, hdr->args.stable); 58870ba381eSWeston Andros Adamson 589b9f5dd57STrond Myklebust INIT_WORK(&iocb->work, nfs_local_call_write); 590b9f5dd57STrond Myklebust queue_work(nfslocaliod_workqueue, &iocb->work); 59170ba381eSWeston Andros Adamson 59270ba381eSWeston Andros Adamson return 0; 59370ba381eSWeston Andros Adamson } 59470ba381eSWeston Andros Adamson 59570ba381eSWeston Andros Adamson int nfs_local_doio(struct nfs_client *clp, struct nfsd_file *localio, 59670ba381eSWeston Andros Adamson struct nfs_pgio_header *hdr, 59770ba381eSWeston Andros Adamson const struct rpc_call_ops *call_ops) 59870ba381eSWeston Andros Adamson { 59970ba381eSWeston Andros Adamson int status = 0; 60070ba381eSWeston Andros Adamson struct file *filp = nfs_to->nfsd_file_file(localio); 60170ba381eSWeston Andros Adamson 60270ba381eSWeston Andros Adamson if (!hdr->args.count) 60370ba381eSWeston Andros Adamson return 0; 60470ba381eSWeston Andros Adamson /* Don't support filesystems without read_iter/write_iter */ 60570ba381eSWeston Andros Adamson if (!filp->f_op->read_iter || !filp->f_op->write_iter) { 60670ba381eSWeston Andros Adamson nfs_local_disable(clp); 60770ba381eSWeston Andros Adamson status = -EAGAIN; 60870ba381eSWeston Andros Adamson goto out; 60970ba381eSWeston Andros Adamson } 61070ba381eSWeston Andros Adamson 61170ba381eSWeston Andros Adamson switch (hdr->rw_mode) { 61270ba381eSWeston Andros Adamson case FMODE_READ: 61370ba381eSWeston Andros Adamson status = nfs_do_local_read(hdr, localio, call_ops); 61470ba381eSWeston Andros Adamson break; 61570ba381eSWeston Andros Adamson case FMODE_WRITE: 61670ba381eSWeston Andros Adamson status = nfs_do_local_write(hdr, localio, call_ops); 61770ba381eSWeston Andros Adamson break; 61870ba381eSWeston Andros Adamson default: 61970ba381eSWeston Andros Adamson dprintk("%s: invalid mode: %d\n", __func__, 62070ba381eSWeston Andros Adamson hdr->rw_mode); 62170ba381eSWeston Andros Adamson status = -EINVAL; 62270ba381eSWeston Andros Adamson } 62370ba381eSWeston Andros Adamson out: 62470ba381eSWeston Andros Adamson if (status != 0) { 62570ba381eSWeston Andros Adamson nfs_to->nfsd_file_put_local(localio); 62670ba381eSWeston Andros Adamson hdr->task.tk_status = status; 62770ba381eSWeston Andros Adamson nfs_local_hdr_release(hdr, call_ops); 62870ba381eSWeston Andros Adamson } 62970ba381eSWeston Andros Adamson return status; 63070ba381eSWeston Andros Adamson } 63170ba381eSWeston Andros Adamson 63270ba381eSWeston Andros Adamson static void 63370ba381eSWeston Andros Adamson nfs_local_init_commit(struct nfs_commit_data *data, 63470ba381eSWeston Andros Adamson const struct rpc_call_ops *call_ops) 63570ba381eSWeston Andros Adamson { 63670ba381eSWeston Andros Adamson data->task.tk_ops = call_ops; 63770ba381eSWeston Andros Adamson } 63870ba381eSWeston Andros Adamson 63970ba381eSWeston Andros Adamson static int 64070ba381eSWeston Andros Adamson nfs_local_run_commit(struct file *filp, struct nfs_commit_data *data) 64170ba381eSWeston Andros Adamson { 64270ba381eSWeston Andros Adamson loff_t start = data->args.offset; 64370ba381eSWeston Andros Adamson loff_t end = LLONG_MAX; 64470ba381eSWeston Andros Adamson 64570ba381eSWeston Andros Adamson if (data->args.count > 0) { 64670ba381eSWeston Andros Adamson end = start + data->args.count - 1; 64770ba381eSWeston Andros Adamson if (end < start) 64870ba381eSWeston Andros Adamson end = LLONG_MAX; 64970ba381eSWeston Andros Adamson } 65070ba381eSWeston Andros Adamson 65170ba381eSWeston Andros Adamson dprintk("%s: commit %llu - %llu\n", __func__, start, end); 65270ba381eSWeston Andros Adamson return vfs_fsync_range(filp, start, end, 0); 65370ba381eSWeston Andros Adamson } 65470ba381eSWeston Andros Adamson 65570ba381eSWeston Andros Adamson static void 65670ba381eSWeston Andros Adamson nfs_local_commit_done(struct nfs_commit_data *data, int status) 65770ba381eSWeston Andros Adamson { 65870ba381eSWeston Andros Adamson if (status >= 0) { 65970ba381eSWeston Andros Adamson nfs_set_local_verifier(data->inode, 66070ba381eSWeston Andros Adamson data->res.verf, 66170ba381eSWeston Andros Adamson NFS_FILE_SYNC); 66270ba381eSWeston Andros Adamson data->res.op_status = NFS4_OK; 66370ba381eSWeston Andros Adamson data->task.tk_status = 0; 66470ba381eSWeston Andros Adamson } else { 66570ba381eSWeston Andros Adamson nfs_reset_boot_verifier(data->inode); 66670ba381eSWeston Andros Adamson data->res.op_status = nfs4_stat_to_errno(status); 66770ba381eSWeston Andros Adamson data->task.tk_status = status; 66870ba381eSWeston Andros Adamson } 66970ba381eSWeston Andros Adamson } 67070ba381eSWeston Andros Adamson 67170ba381eSWeston Andros Adamson static void 67270ba381eSWeston Andros Adamson nfs_local_release_commit_data(struct nfsd_file *localio, 67370ba381eSWeston Andros Adamson struct nfs_commit_data *data, 67470ba381eSWeston Andros Adamson const struct rpc_call_ops *call_ops) 67570ba381eSWeston Andros Adamson { 67670ba381eSWeston Andros Adamson nfs_to->nfsd_file_put_local(localio); 67770ba381eSWeston Andros Adamson call_ops->rpc_call_done(&data->task, data); 67870ba381eSWeston Andros Adamson call_ops->rpc_release(data); 67970ba381eSWeston Andros Adamson } 68070ba381eSWeston Andros Adamson 68170ba381eSWeston Andros Adamson static struct nfs_local_fsync_ctx * 68270ba381eSWeston Andros Adamson nfs_local_fsync_ctx_alloc(struct nfs_commit_data *data, 68370ba381eSWeston Andros Adamson struct nfsd_file *localio, gfp_t flags) 68470ba381eSWeston Andros Adamson { 68570ba381eSWeston Andros Adamson struct nfs_local_fsync_ctx *ctx = kmalloc(sizeof(*ctx), flags); 68670ba381eSWeston Andros Adamson 68770ba381eSWeston Andros Adamson if (ctx != NULL) { 68870ba381eSWeston Andros Adamson ctx->localio = localio; 68970ba381eSWeston Andros Adamson ctx->data = data; 69070ba381eSWeston Andros Adamson INIT_WORK(&ctx->work, nfs_local_fsync_work); 69170ba381eSWeston Andros Adamson kref_init(&ctx->kref); 69270ba381eSWeston Andros Adamson ctx->done = NULL; 69370ba381eSWeston Andros Adamson } 69470ba381eSWeston Andros Adamson return ctx; 69570ba381eSWeston Andros Adamson } 69670ba381eSWeston Andros Adamson 69770ba381eSWeston Andros Adamson static void 69870ba381eSWeston Andros Adamson nfs_local_fsync_ctx_kref_free(struct kref *kref) 69970ba381eSWeston Andros Adamson { 70070ba381eSWeston Andros Adamson kfree(container_of(kref, struct nfs_local_fsync_ctx, kref)); 70170ba381eSWeston Andros Adamson } 70270ba381eSWeston Andros Adamson 70370ba381eSWeston Andros Adamson static void 70470ba381eSWeston Andros Adamson nfs_local_fsync_ctx_put(struct nfs_local_fsync_ctx *ctx) 70570ba381eSWeston Andros Adamson { 70670ba381eSWeston Andros Adamson kref_put(&ctx->kref, nfs_local_fsync_ctx_kref_free); 70770ba381eSWeston Andros Adamson } 70870ba381eSWeston Andros Adamson 70970ba381eSWeston Andros Adamson static void 71070ba381eSWeston Andros Adamson nfs_local_fsync_ctx_free(struct nfs_local_fsync_ctx *ctx) 71170ba381eSWeston Andros Adamson { 71270ba381eSWeston Andros Adamson nfs_local_release_commit_data(ctx->localio, ctx->data, 71370ba381eSWeston Andros Adamson ctx->data->task.tk_ops); 71470ba381eSWeston Andros Adamson nfs_local_fsync_ctx_put(ctx); 71570ba381eSWeston Andros Adamson } 71670ba381eSWeston Andros Adamson 71770ba381eSWeston Andros Adamson static void 71870ba381eSWeston Andros Adamson nfs_local_fsync_work(struct work_struct *work) 71970ba381eSWeston Andros Adamson { 72070ba381eSWeston Andros Adamson struct nfs_local_fsync_ctx *ctx; 72170ba381eSWeston Andros Adamson int status; 72270ba381eSWeston Andros Adamson 72370ba381eSWeston Andros Adamson ctx = container_of(work, struct nfs_local_fsync_ctx, work); 72470ba381eSWeston Andros Adamson 72570ba381eSWeston Andros Adamson status = nfs_local_run_commit(nfs_to->nfsd_file_file(ctx->localio), 72670ba381eSWeston Andros Adamson ctx->data); 72770ba381eSWeston Andros Adamson nfs_local_commit_done(ctx->data, status); 72870ba381eSWeston Andros Adamson if (ctx->done != NULL) 72970ba381eSWeston Andros Adamson complete(ctx->done); 73070ba381eSWeston Andros Adamson nfs_local_fsync_ctx_free(ctx); 73170ba381eSWeston Andros Adamson } 73270ba381eSWeston Andros Adamson 73370ba381eSWeston Andros Adamson int nfs_local_commit(struct nfsd_file *localio, 73470ba381eSWeston Andros Adamson struct nfs_commit_data *data, 73570ba381eSWeston Andros Adamson const struct rpc_call_ops *call_ops, int how) 73670ba381eSWeston Andros Adamson { 73770ba381eSWeston Andros Adamson struct nfs_local_fsync_ctx *ctx; 73870ba381eSWeston Andros Adamson 73970ba381eSWeston Andros Adamson ctx = nfs_local_fsync_ctx_alloc(data, localio, GFP_KERNEL); 74070ba381eSWeston Andros Adamson if (!ctx) { 74170ba381eSWeston Andros Adamson nfs_local_commit_done(data, -ENOMEM); 74270ba381eSWeston Andros Adamson nfs_local_release_commit_data(localio, data, call_ops); 74370ba381eSWeston Andros Adamson return -ENOMEM; 74470ba381eSWeston Andros Adamson } 74570ba381eSWeston Andros Adamson 74670ba381eSWeston Andros Adamson nfs_local_init_commit(data, call_ops); 74770ba381eSWeston Andros Adamson kref_get(&ctx->kref); 74870ba381eSWeston Andros Adamson if (how & FLUSH_SYNC) { 74970ba381eSWeston Andros Adamson DECLARE_COMPLETION_ONSTACK(done); 75070ba381eSWeston Andros Adamson ctx->done = &done; 75170ba381eSWeston Andros Adamson queue_work(nfsiod_workqueue, &ctx->work); 75270ba381eSWeston Andros Adamson wait_for_completion(&done); 75370ba381eSWeston Andros Adamson } else 75470ba381eSWeston Andros Adamson queue_work(nfsiod_workqueue, &ctx->work); 75570ba381eSWeston Andros Adamson nfs_local_fsync_ctx_put(ctx); 75670ba381eSWeston Andros Adamson return 0; 75770ba381eSWeston Andros Adamson } 758