mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 01:49:46 +00:00
Merge tag 'nfs-for-6.18-1' of git://git.linux-nfs.org/projects/anna/linux-nfs
Pull NFS client updates from Anna Schumaker:
"New Features:
- Add a Kconfig option to redirect dfprintk() to the trace buffer
- Enable use of the RWF_DONTCACHE flag on the NFS client
- Add striped layout handling to pNFS flexfiles
- Add proper localio handling for READ and WRITE O_DIRECT
Bugfixes:
- Handle NFS4ERR_GRACE errors during delegation recall
- Fix NFSv4.1 backchannel max_resp_sz verification check
- Fix mount hang after CREATE_SESSION failure
- Fix d_parent->d_inode locking in nfs4_setup_readdir()
Other Cleanups and Improvements:
- Improvements to write handling tracepoints
- Fix a few trivial spelling mistakes
- Cleanups to the rpcbind cleanup call sites
- Convert the SUNRPC xdr_buf to use a scratch folio instead of
scratch page
- Remove unused NFS_WBACK_BUSY() macro
- Remove __GFP_NOWARN flags
- Unexport rpc_malloc() and rpc_free()"
* tag 'nfs-for-6.18-1' of git://git.linux-nfs.org/projects/anna/linux-nfs: (46 commits)
NFS: add basic STATX_DIOALIGN and STATX_DIO_READ_ALIGN support
nfs/localio: add tracepoints for misaligned DIO READ and WRITE support
nfs/localio: add proper O_DIRECT support for READ and WRITE
nfs/localio: refactor iocb initialization
nfs/localio: refactor iocb and iov_iter_bvec initialization
nfs/localio: avoid issuing misaligned IO using O_DIRECT
nfs/localio: make trace_nfs_local_open_fh more useful
NFSD: filecache: add STATX_DIOALIGN and STATX_DIO_READ_ALIGN support
sunrpc: unexport rpc_malloc() and rpc_free()
NFSv4/flexfiles: Add support for striped layouts
NFSv4/flexfiles: Update layout stats & error paths for striped layouts
NFSv4/flexfiles: Write path updates for striped layouts
NFSv4/flexfiles: Commit path updates for striped layouts
NFSv4/flexfiles: Read path updates for striped layouts
NFSv4/flexfiles: Update low level helper functions to be DS stripe aware.
NFSv4/flexfiles: Add data structure support for striped layouts
NFSv4/flexfiles: Use ds_commit_idx when marking a write commit
NFSv4/flexfiles: Remove cred local variable dependency
nfs4_setup_readdir(): insufficient locking for ->d_parent->d_inode dereferencing
NFS: Enable use of the RWF_DONTCACHE flag on the NFS client
...
This commit is contained in:
@@ -216,8 +216,7 @@ out_err:
|
||||
if (warned++ == 0)
|
||||
printk(KERN_WARNING
|
||||
"lockd_up: makesock failed, error=%d\n", err);
|
||||
svc_xprt_destroy_all(serv, net);
|
||||
svc_rpcb_cleanup(serv, net);
|
||||
svc_xprt_destroy_all(serv, net, true);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -255,8 +254,7 @@ static void lockd_down_net(struct svc_serv *serv, struct net *net)
|
||||
nlm_shutdown_hosts_net(net);
|
||||
cancel_delayed_work_sync(&ln->grace_period_end);
|
||||
locks_end_grace(&ln->lockd_manager);
|
||||
svc_xprt_destroy_all(serv, net);
|
||||
svc_rpcb_cleanup(serv, net);
|
||||
svc_xprt_destroy_all(serv, net, true);
|
||||
}
|
||||
} else {
|
||||
pr_err("%s: no users! net=%x\n",
|
||||
|
||||
@@ -676,7 +676,7 @@ bl_alloc_lseg(struct pnfs_layout_hdr *lo, struct nfs4_layoutget_res *lgr,
|
||||
struct pnfs_layout_segment *lseg;
|
||||
struct xdr_buf buf;
|
||||
struct xdr_stream xdr;
|
||||
struct page *scratch;
|
||||
struct folio *scratch;
|
||||
int status, i;
|
||||
uint32_t count;
|
||||
__be32 *p;
|
||||
@@ -689,13 +689,13 @@ bl_alloc_lseg(struct pnfs_layout_hdr *lo, struct nfs4_layoutget_res *lgr,
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
status = -ENOMEM;
|
||||
scratch = alloc_page(gfp_mask);
|
||||
scratch = folio_alloc(gfp_mask, 0);
|
||||
if (!scratch)
|
||||
goto out;
|
||||
|
||||
xdr_init_decode_pages(&xdr, &buf,
|
||||
lgr->layoutp->pages, lgr->layoutp->len);
|
||||
xdr_set_scratch_page(&xdr, scratch);
|
||||
xdr_set_scratch_folio(&xdr, scratch);
|
||||
|
||||
status = -EIO;
|
||||
p = xdr_inline_decode(&xdr, 4);
|
||||
@@ -744,7 +744,7 @@ process_extents:
|
||||
}
|
||||
|
||||
out_free_scratch:
|
||||
__free_page(scratch);
|
||||
folio_put(scratch);
|
||||
out:
|
||||
dprintk("%s returns %d\n", __func__, status);
|
||||
switch (status) {
|
||||
|
||||
@@ -541,16 +541,16 @@ bl_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
|
||||
struct pnfs_block_dev *top;
|
||||
struct xdr_stream xdr;
|
||||
struct xdr_buf buf;
|
||||
struct page *scratch;
|
||||
struct folio *scratch;
|
||||
int nr_volumes, ret, i;
|
||||
__be32 *p;
|
||||
|
||||
scratch = alloc_page(gfp_mask);
|
||||
scratch = folio_alloc(gfp_mask, 0);
|
||||
if (!scratch)
|
||||
goto out;
|
||||
|
||||
xdr_init_decode_pages(&xdr, &buf, pdev->pages, pdev->pglen);
|
||||
xdr_set_scratch_page(&xdr, scratch);
|
||||
xdr_set_scratch_folio(&xdr, scratch);
|
||||
|
||||
p = xdr_inline_decode(&xdr, sizeof(__be32));
|
||||
if (!p)
|
||||
@@ -582,7 +582,7 @@ bl_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
|
||||
out_free_volumes:
|
||||
kfree(volumes);
|
||||
out_free_scratch:
|
||||
__free_page(scratch);
|
||||
folio_put(scratch);
|
||||
out:
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ static void nfs_callback_down_net(u32 minorversion, struct svc_serv *serv, struc
|
||||
return;
|
||||
|
||||
dprintk("NFS: destroy per-net callback data; net=%x\n", net->ns.inum);
|
||||
svc_xprt_destroy_all(serv, net);
|
||||
svc_xprt_destroy_all(serv, net, false);
|
||||
}
|
||||
|
||||
static int nfs_callback_up_net(int minorversion, struct svc_serv *serv,
|
||||
@@ -153,7 +153,7 @@ static int nfs_callback_up_net(int minorversion, struct svc_serv *serv,
|
||||
ret = svc_bind(serv, net);
|
||||
if (ret < 0) {
|
||||
printk(KERN_WARNING "NFS: bind callback service failed\n");
|
||||
goto err_bind;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@@ -166,13 +166,11 @@ static int nfs_callback_up_net(int minorversion, struct svc_serv *serv,
|
||||
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "NFS: callback service start failed\n");
|
||||
goto err_socks;
|
||||
goto err;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_socks:
|
||||
svc_rpcb_cleanup(serv, net);
|
||||
err_bind:
|
||||
err:
|
||||
nn->cb_users[minorversion]--;
|
||||
dprintk("NFS: Couldn't create callback socket: err = %d; "
|
||||
"net = %x\n", ret, net->ns.inum);
|
||||
|
||||
@@ -829,17 +829,17 @@ static int nfs_readdir_folio_filler(struct nfs_readdir_descriptor *desc,
|
||||
struct address_space *mapping = desc->file->f_mapping;
|
||||
struct folio *new, *folio = *arrays;
|
||||
struct xdr_stream stream;
|
||||
struct page *scratch;
|
||||
struct folio *scratch;
|
||||
struct xdr_buf buf;
|
||||
u64 cookie;
|
||||
int status;
|
||||
|
||||
scratch = alloc_page(GFP_KERNEL);
|
||||
scratch = folio_alloc(GFP_KERNEL, 0);
|
||||
if (scratch == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
xdr_init_decode_pages(&stream, &buf, xdr_pages, buflen);
|
||||
xdr_set_scratch_page(&stream, scratch);
|
||||
xdr_set_scratch_folio(&stream, scratch);
|
||||
|
||||
do {
|
||||
status = nfs_readdir_entry_decode(desc, entry, &stream);
|
||||
@@ -891,7 +891,7 @@ static int nfs_readdir_folio_filler(struct nfs_readdir_descriptor *desc,
|
||||
if (folio != *arrays)
|
||||
nfs_readdir_folio_unlock_and_put(folio);
|
||||
|
||||
put_page(scratch);
|
||||
folio_put(scratch);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -161,6 +161,8 @@ nfs_file_read(struct kiocb *iocb, struct iov_iter *to)
|
||||
struct inode *inode = file_inode(iocb->ki_filp);
|
||||
ssize_t result;
|
||||
|
||||
trace_nfs_file_read(iocb, to);
|
||||
|
||||
if (iocb->ki_flags & IOCB_DIRECT)
|
||||
return nfs_file_direct_read(iocb, to, false);
|
||||
|
||||
@@ -361,6 +363,8 @@ static bool nfs_want_read_modify_write(struct file *file, struct folio *folio,
|
||||
|
||||
if (pnfs_ld_read_whole_page(file_inode(file)))
|
||||
return true;
|
||||
if (folio_test_dropbehind(folio))
|
||||
return false;
|
||||
/* Open for reading too? */
|
||||
if (file->f_mode & FMODE_READ)
|
||||
return true;
|
||||
@@ -380,22 +384,23 @@ static int nfs_write_begin(const struct kiocb *iocb,
|
||||
loff_t pos, unsigned len, struct folio **foliop,
|
||||
void **fsdata)
|
||||
{
|
||||
fgf_t fgp = FGP_WRITEBEGIN;
|
||||
struct folio *folio;
|
||||
struct file *file = iocb->ki_filp;
|
||||
int once_thru = 0;
|
||||
int ret;
|
||||
|
||||
trace_nfs_write_begin(file_inode(file), pos, len);
|
||||
|
||||
dfprintk(PAGECACHE, "NFS: write_begin(%pD2(%lu), %u@%lld)\n",
|
||||
file, mapping->host->i_ino, len, (long long) pos);
|
||||
nfs_truncate_last_folio(mapping, i_size_read(mapping->host), pos);
|
||||
|
||||
fgp |= fgf_set_order(len);
|
||||
start:
|
||||
folio = __filemap_get_folio(mapping, pos >> PAGE_SHIFT, fgp,
|
||||
mapping_gfp_mask(mapping));
|
||||
if (IS_ERR(folio))
|
||||
return PTR_ERR(folio);
|
||||
folio = write_begin_get_folio(iocb, mapping, pos >> PAGE_SHIFT, len);
|
||||
if (IS_ERR(folio)) {
|
||||
ret = PTR_ERR(folio);
|
||||
goto out;
|
||||
}
|
||||
*foliop = folio;
|
||||
|
||||
ret = nfs_flush_incompatible(file, folio);
|
||||
@@ -405,11 +410,14 @@ start:
|
||||
} else if (!once_thru &&
|
||||
nfs_want_read_modify_write(file, folio, pos, len)) {
|
||||
once_thru = 1;
|
||||
folio_clear_dropbehind(folio);
|
||||
ret = nfs_read_folio(file, folio);
|
||||
folio_put(folio);
|
||||
if (!ret)
|
||||
goto start;
|
||||
}
|
||||
out:
|
||||
trace_nfs_write_begin_done(file_inode(file), pos, len, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -423,6 +431,7 @@ static int nfs_write_end(const struct kiocb *iocb,
|
||||
unsigned offset = offset_in_folio(folio, pos);
|
||||
int status;
|
||||
|
||||
trace_nfs_write_end(file_inode(file), pos, len);
|
||||
dfprintk(PAGECACHE, "NFS: write_end(%pD2(%lu), %u@%lld)\n",
|
||||
file, mapping->host->i_ino, len, (long long) pos);
|
||||
|
||||
@@ -451,13 +460,16 @@ static int nfs_write_end(const struct kiocb *iocb,
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
|
||||
if (status < 0)
|
||||
if (status < 0) {
|
||||
trace_nfs_write_end_done(file_inode(file), pos, len, status);
|
||||
return status;
|
||||
}
|
||||
NFS_I(mapping->host)->write_io += copied;
|
||||
|
||||
if (nfs_ctx_key_to_expire(ctx, mapping->host))
|
||||
nfs_wb_all(mapping->host);
|
||||
|
||||
trace_nfs_write_end_done(file_inode(file), pos, len, copied);
|
||||
return copied;
|
||||
}
|
||||
|
||||
@@ -690,6 +702,8 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
|
||||
errseq_t since;
|
||||
int error;
|
||||
|
||||
trace_nfs_file_write(iocb, from);
|
||||
|
||||
result = nfs_key_timeout_notify(file, inode);
|
||||
if (result)
|
||||
return result;
|
||||
@@ -949,5 +963,6 @@ const struct file_operations nfs_file_operations = {
|
||||
.splice_write = iter_file_splice_write,
|
||||
.check_flags = nfs_check_flags,
|
||||
.setlease = simple_nosetlease,
|
||||
.fop_flags = FOP_DONTCACHE,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(nfs_file_operations);
|
||||
|
||||
@@ -646,19 +646,19 @@ filelayout_decode_layout(struct pnfs_layout_hdr *flo,
|
||||
{
|
||||
struct xdr_stream stream;
|
||||
struct xdr_buf buf;
|
||||
struct page *scratch;
|
||||
struct folio *scratch;
|
||||
__be32 *p;
|
||||
uint32_t nfl_util;
|
||||
int i;
|
||||
|
||||
dprintk("%s: set_layout_map Begin\n", __func__);
|
||||
|
||||
scratch = alloc_page(gfp_flags);
|
||||
scratch = folio_alloc(gfp_flags, 0);
|
||||
if (!scratch)
|
||||
return -ENOMEM;
|
||||
|
||||
xdr_init_decode_pages(&stream, &buf, lgr->layoutp->pages, lgr->layoutp->len);
|
||||
xdr_set_scratch_page(&stream, scratch);
|
||||
xdr_set_scratch_folio(&stream, scratch);
|
||||
|
||||
/* 20 = ufl_util (4), first_stripe_index (4), pattern_offset (8),
|
||||
* num_fh (4) */
|
||||
@@ -724,11 +724,11 @@ filelayout_decode_layout(struct pnfs_layout_hdr *flo,
|
||||
fl->fh_array[i]->size);
|
||||
}
|
||||
|
||||
__free_page(scratch);
|
||||
folio_put(scratch);
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
__free_page(scratch);
|
||||
folio_put(scratch);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,18 +73,18 @@ nfs4_fl_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
|
||||
struct nfs4_file_layout_dsaddr *dsaddr = NULL;
|
||||
struct xdr_stream stream;
|
||||
struct xdr_buf buf;
|
||||
struct page *scratch;
|
||||
struct folio *scratch;
|
||||
struct list_head dsaddrs;
|
||||
struct nfs4_pnfs_ds_addr *da;
|
||||
struct net *net = server->nfs_client->cl_net;
|
||||
|
||||
/* set up xdr stream */
|
||||
scratch = alloc_page(gfp_flags);
|
||||
scratch = folio_alloc(gfp_flags, 0);
|
||||
if (!scratch)
|
||||
goto out_err;
|
||||
|
||||
xdr_init_decode_pages(&stream, &buf, pdev->pages, pdev->pglen);
|
||||
xdr_set_scratch_page(&stream, scratch);
|
||||
xdr_set_scratch_folio(&stream, scratch);
|
||||
|
||||
/* Get the stripe count (number of stripe index) */
|
||||
p = xdr_inline_decode(&stream, 4);
|
||||
@@ -186,7 +186,7 @@ nfs4_fl_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
|
||||
}
|
||||
}
|
||||
|
||||
__free_page(scratch);
|
||||
folio_put(scratch);
|
||||
return dsaddr;
|
||||
|
||||
out_err_drain_dsaddrs:
|
||||
@@ -204,7 +204,7 @@ out_err_free_deviceid:
|
||||
out_err_free_stripe_indices:
|
||||
kfree(stripe_indices);
|
||||
out_err_free_scratch:
|
||||
__free_page(scratch);
|
||||
folio_put(scratch);
|
||||
out_err:
|
||||
dprintk("%s ERROR: returning NULL\n", __func__);
|
||||
return NULL;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,8 @@
|
||||
* due to network error etc. */
|
||||
#define NFS4_FLEXFILE_LAYOUT_MAX_MIRROR_CNT 4096
|
||||
|
||||
#define NFS4_FLEXFILE_LAYOUT_MAX_STRIPE_CNT 4096
|
||||
|
||||
/* LAYOUTSTATS report interval in ms */
|
||||
#define FF_LAYOUTSTATS_REPORT_INTERVAL (60000L)
|
||||
#define FF_LAYOUTSTATS_MAXDEV 4
|
||||
@@ -71,12 +73,12 @@ struct nfs4_ff_layoutstat {
|
||||
struct nfs4_ff_busy_timer busy_timer;
|
||||
};
|
||||
|
||||
struct nfs4_ff_layout_mirror {
|
||||
struct pnfs_layout_hdr *layout;
|
||||
struct list_head mirrors;
|
||||
u32 ds_count;
|
||||
u32 efficiency;
|
||||
struct nfs4_ff_layout_mirror;
|
||||
|
||||
struct nfs4_ff_layout_ds_stripe {
|
||||
struct nfs4_ff_layout_mirror *mirror;
|
||||
struct nfs4_deviceid devid;
|
||||
u32 efficiency;
|
||||
struct nfs4_ff_layout_ds *mirror_ds;
|
||||
u32 fh_versions_cnt;
|
||||
struct nfs_fh *fh_versions;
|
||||
@@ -84,12 +86,19 @@ struct nfs4_ff_layout_mirror {
|
||||
const struct cred __rcu *ro_cred;
|
||||
const struct cred __rcu *rw_cred;
|
||||
struct nfs_file_localio nfl;
|
||||
refcount_t ref;
|
||||
spinlock_t lock;
|
||||
unsigned long flags;
|
||||
struct nfs4_ff_layoutstat read_stat;
|
||||
struct nfs4_ff_layoutstat write_stat;
|
||||
ktime_t start_time;
|
||||
};
|
||||
|
||||
struct nfs4_ff_layout_mirror {
|
||||
struct pnfs_layout_hdr *layout;
|
||||
struct list_head mirrors;
|
||||
u32 dss_count;
|
||||
struct nfs4_ff_layout_ds_stripe *dss;
|
||||
refcount_t ref;
|
||||
spinlock_t lock;
|
||||
unsigned long flags;
|
||||
u32 report_interval;
|
||||
};
|
||||
|
||||
@@ -150,12 +159,12 @@ FF_LAYOUT_COMP(struct pnfs_layout_segment *lseg, u32 idx)
|
||||
}
|
||||
|
||||
static inline struct nfs4_deviceid_node *
|
||||
FF_LAYOUT_DEVID_NODE(struct pnfs_layout_segment *lseg, u32 idx)
|
||||
FF_LAYOUT_DEVID_NODE(struct pnfs_layout_segment *lseg, u32 idx, u32 dss_id)
|
||||
{
|
||||
struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, idx);
|
||||
|
||||
if (mirror != NULL) {
|
||||
struct nfs4_ff_layout_ds *mirror_ds = mirror->mirror_ds;
|
||||
struct nfs4_ff_layout_ds *mirror_ds = mirror->dss[dss_id].mirror_ds;
|
||||
|
||||
if (!IS_ERR_OR_NULL(mirror_ds))
|
||||
return &mirror_ds->id_node;
|
||||
@@ -182,9 +191,22 @@ ff_layout_no_read_on_rw(struct pnfs_layout_segment *lseg)
|
||||
}
|
||||
|
||||
static inline int
|
||||
nfs4_ff_layout_ds_version(const struct nfs4_ff_layout_mirror *mirror)
|
||||
nfs4_ff_layout_ds_version(const struct nfs4_ff_layout_mirror *mirror, u32 dss_id)
|
||||
{
|
||||
return mirror->mirror_ds->ds_versions[0].version;
|
||||
return mirror->dss[dss_id].mirror_ds->ds_versions[0].version;
|
||||
}
|
||||
|
||||
static inline u32
|
||||
nfs4_ff_layout_calc_dss_id(const u64 stripe_unit, const u32 dss_count, const loff_t offset)
|
||||
{
|
||||
u64 tmp = offset;
|
||||
|
||||
if (dss_count == 1 || stripe_unit == 0)
|
||||
return 0;
|
||||
|
||||
do_div(tmp, stripe_unit);
|
||||
|
||||
return do_div(tmp, dss_count);
|
||||
}
|
||||
|
||||
struct nfs4_ff_layout_ds *
|
||||
@@ -193,9 +215,9 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
|
||||
void nfs4_ff_layout_put_deviceid(struct nfs4_ff_layout_ds *mirror_ds);
|
||||
void nfs4_ff_layout_free_deviceid(struct nfs4_ff_layout_ds *mirror_ds);
|
||||
int ff_layout_track_ds_error(struct nfs4_flexfile_layout *flo,
|
||||
struct nfs4_ff_layout_mirror *mirror, u64 offset,
|
||||
u64 length, int status, enum nfs_opnum4 opnum,
|
||||
gfp_t gfp_flags);
|
||||
struct nfs4_ff_layout_mirror *mirror,
|
||||
u32 dss_id, u64 offset, u64 length, int status,
|
||||
enum nfs_opnum4 opnum, gfp_t gfp_flags);
|
||||
void ff_layout_send_layouterror(struct pnfs_layout_segment *lseg);
|
||||
int ff_layout_encode_ds_ioerr(struct xdr_stream *xdr, const struct list_head *head);
|
||||
void ff_layout_free_ds_ioerr(struct list_head *head);
|
||||
@@ -204,23 +226,27 @@ unsigned int ff_layout_fetch_ds_ioerr(struct pnfs_layout_hdr *lo,
|
||||
struct list_head *head,
|
||||
unsigned int maxnum);
|
||||
struct nfs_fh *
|
||||
nfs4_ff_layout_select_ds_fh(struct nfs4_ff_layout_mirror *mirror);
|
||||
nfs4_ff_layout_select_ds_fh(struct nfs4_ff_layout_mirror *mirror, u32 dss_id);
|
||||
void
|
||||
nfs4_ff_layout_select_ds_stateid(const struct nfs4_ff_layout_mirror *mirror,
|
||||
nfs4_stateid *stateid);
|
||||
u32 dss_id,
|
||||
nfs4_stateid *stateid);
|
||||
|
||||
struct nfs4_pnfs_ds *
|
||||
nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg,
|
||||
struct nfs4_ff_layout_mirror *mirror,
|
||||
u32 dss_id,
|
||||
bool fail_return);
|
||||
|
||||
struct rpc_clnt *
|
||||
nfs4_ff_find_or_create_ds_client(struct nfs4_ff_layout_mirror *mirror,
|
||||
struct nfs_client *ds_clp,
|
||||
struct inode *inode);
|
||||
struct inode *inode,
|
||||
u32 dss_id);
|
||||
const struct cred *ff_layout_get_ds_cred(struct nfs4_ff_layout_mirror *mirror,
|
||||
const struct pnfs_layout_range *range,
|
||||
const struct cred *mdscred);
|
||||
const struct cred *mdscred,
|
||||
u32 dss_id);
|
||||
bool ff_layout_avoid_mds_available_ds(struct pnfs_layout_segment *lseg);
|
||||
bool ff_layout_avoid_read_on_rw(struct pnfs_layout_segment *lseg);
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
|
||||
{
|
||||
struct xdr_stream stream;
|
||||
struct xdr_buf buf;
|
||||
struct page *scratch;
|
||||
struct folio *scratch;
|
||||
struct list_head dsaddrs;
|
||||
struct nfs4_pnfs_ds_addr *da;
|
||||
struct nfs4_ff_layout_ds *new_ds = NULL;
|
||||
@@ -56,7 +56,7 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
|
||||
int i, ret = -ENOMEM;
|
||||
|
||||
/* set up xdr stream */
|
||||
scratch = alloc_page(gfp_flags);
|
||||
scratch = folio_alloc(gfp_flags, 0);
|
||||
if (!scratch)
|
||||
goto out_err;
|
||||
|
||||
@@ -70,7 +70,7 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
|
||||
INIT_LIST_HEAD(&dsaddrs);
|
||||
|
||||
xdr_init_decode_pages(&stream, &buf, pdev->pages, pdev->pglen);
|
||||
xdr_set_scratch_page(&stream, scratch);
|
||||
xdr_set_scratch_folio(&stream, scratch);
|
||||
|
||||
/* multipath count */
|
||||
p = xdr_inline_decode(&stream, 4);
|
||||
@@ -163,7 +163,7 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
|
||||
kfree(da);
|
||||
}
|
||||
|
||||
__free_page(scratch);
|
||||
folio_put(scratch);
|
||||
return new_ds;
|
||||
|
||||
out_err_drain_dsaddrs:
|
||||
@@ -177,7 +177,7 @@ out_err_drain_dsaddrs:
|
||||
|
||||
kfree(ds_versions);
|
||||
out_scratch:
|
||||
__free_page(scratch);
|
||||
folio_put(scratch);
|
||||
out_err:
|
||||
kfree(new_ds);
|
||||
|
||||
@@ -250,16 +250,16 @@ ff_layout_add_ds_error_locked(struct nfs4_flexfile_layout *flo,
|
||||
}
|
||||
|
||||
int ff_layout_track_ds_error(struct nfs4_flexfile_layout *flo,
|
||||
struct nfs4_ff_layout_mirror *mirror, u64 offset,
|
||||
u64 length, int status, enum nfs_opnum4 opnum,
|
||||
gfp_t gfp_flags)
|
||||
struct nfs4_ff_layout_mirror *mirror,
|
||||
u32 dss_id, u64 offset, u64 length, int status,
|
||||
enum nfs_opnum4 opnum, gfp_t gfp_flags)
|
||||
{
|
||||
struct nfs4_ff_layout_ds_err *dserr;
|
||||
|
||||
if (status == 0)
|
||||
return 0;
|
||||
|
||||
if (IS_ERR_OR_NULL(mirror->mirror_ds))
|
||||
if (IS_ERR_OR_NULL(mirror->dss[dss_id].mirror_ds))
|
||||
return -EINVAL;
|
||||
|
||||
dserr = kmalloc(sizeof(*dserr), gfp_flags);
|
||||
@@ -271,8 +271,8 @@ int ff_layout_track_ds_error(struct nfs4_flexfile_layout *flo,
|
||||
dserr->length = length;
|
||||
dserr->status = status;
|
||||
dserr->opnum = opnum;
|
||||
nfs4_stateid_copy(&dserr->stateid, &mirror->stateid);
|
||||
memcpy(&dserr->deviceid, &mirror->mirror_ds->id_node.deviceid,
|
||||
nfs4_stateid_copy(&dserr->stateid, &mirror->dss[dss_id].stateid);
|
||||
memcpy(&dserr->deviceid, &mirror->dss[dss_id].mirror_ds->id_node.deviceid,
|
||||
NFS4_DEVICEID4_SIZE);
|
||||
|
||||
spin_lock(&flo->generic_hdr.plh_inode->i_lock);
|
||||
@@ -282,14 +282,14 @@ int ff_layout_track_ds_error(struct nfs4_flexfile_layout *flo,
|
||||
}
|
||||
|
||||
static const struct cred *
|
||||
ff_layout_get_mirror_cred(struct nfs4_ff_layout_mirror *mirror, u32 iomode)
|
||||
ff_layout_get_mirror_cred(struct nfs4_ff_layout_mirror *mirror, u32 iomode, u32 dss_id)
|
||||
{
|
||||
const struct cred *cred, __rcu **pcred;
|
||||
|
||||
if (iomode == IOMODE_READ)
|
||||
pcred = &mirror->ro_cred;
|
||||
pcred = &mirror->dss[dss_id].ro_cred;
|
||||
else
|
||||
pcred = &mirror->rw_cred;
|
||||
pcred = &mirror->dss[dss_id].rw_cred;
|
||||
|
||||
rcu_read_lock();
|
||||
do {
|
||||
@@ -304,43 +304,45 @@ ff_layout_get_mirror_cred(struct nfs4_ff_layout_mirror *mirror, u32 iomode)
|
||||
}
|
||||
|
||||
struct nfs_fh *
|
||||
nfs4_ff_layout_select_ds_fh(struct nfs4_ff_layout_mirror *mirror)
|
||||
nfs4_ff_layout_select_ds_fh(struct nfs4_ff_layout_mirror *mirror, u32 dss_id)
|
||||
{
|
||||
/* FIXME: For now assume there is only 1 version available for the DS */
|
||||
return &mirror->fh_versions[0];
|
||||
return &mirror->dss[dss_id].fh_versions[0];
|
||||
}
|
||||
|
||||
void
|
||||
nfs4_ff_layout_select_ds_stateid(const struct nfs4_ff_layout_mirror *mirror,
|
||||
nfs4_stateid *stateid)
|
||||
u32 dss_id,
|
||||
nfs4_stateid *stateid)
|
||||
{
|
||||
if (nfs4_ff_layout_ds_version(mirror) == 4)
|
||||
nfs4_stateid_copy(stateid, &mirror->stateid);
|
||||
if (nfs4_ff_layout_ds_version(mirror, dss_id) == 4)
|
||||
nfs4_stateid_copy(stateid, &mirror->dss[dss_id].stateid);
|
||||
}
|
||||
|
||||
static bool
|
||||
ff_layout_init_mirror_ds(struct pnfs_layout_hdr *lo,
|
||||
struct nfs4_ff_layout_mirror *mirror)
|
||||
struct nfs4_ff_layout_mirror *mirror,
|
||||
u32 dss_id)
|
||||
{
|
||||
if (mirror == NULL)
|
||||
goto outerr;
|
||||
if (mirror->mirror_ds == NULL) {
|
||||
if (mirror->dss[dss_id].mirror_ds == NULL) {
|
||||
struct nfs4_deviceid_node *node;
|
||||
struct nfs4_ff_layout_ds *mirror_ds = ERR_PTR(-ENODEV);
|
||||
|
||||
node = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode),
|
||||
&mirror->devid, lo->plh_lc_cred,
|
||||
&mirror->dss[dss_id].devid, lo->plh_lc_cred,
|
||||
GFP_KERNEL);
|
||||
if (node)
|
||||
mirror_ds = FF_LAYOUT_MIRROR_DS(node);
|
||||
|
||||
/* check for race with another call to this function */
|
||||
if (cmpxchg(&mirror->mirror_ds, NULL, mirror_ds) &&
|
||||
if (cmpxchg(&mirror->dss[dss_id].mirror_ds, NULL, mirror_ds) &&
|
||||
mirror_ds != ERR_PTR(-ENODEV))
|
||||
nfs4_put_deviceid_node(node);
|
||||
}
|
||||
|
||||
if (IS_ERR(mirror->mirror_ds))
|
||||
if (IS_ERR(mirror->dss[dss_id].mirror_ds))
|
||||
goto outerr;
|
||||
|
||||
return true;
|
||||
@@ -352,6 +354,7 @@ outerr:
|
||||
* nfs4_ff_layout_prepare_ds - prepare a DS connection for an RPC call
|
||||
* @lseg: the layout segment we're operating on
|
||||
* @mirror: layout mirror describing the DS to use
|
||||
* @dss_id: DS stripe id to select stripe to use
|
||||
* @fail_return: return layout on connect failure?
|
||||
*
|
||||
* Try to prepare a DS connection to accept an RPC call. This involves
|
||||
@@ -368,6 +371,7 @@ outerr:
|
||||
struct nfs4_pnfs_ds *
|
||||
nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg,
|
||||
struct nfs4_ff_layout_mirror *mirror,
|
||||
u32 dss_id,
|
||||
bool fail_return)
|
||||
{
|
||||
struct nfs4_pnfs_ds *ds;
|
||||
@@ -376,10 +380,10 @@ nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg,
|
||||
unsigned int max_payload;
|
||||
int status = -EAGAIN;
|
||||
|
||||
if (!ff_layout_init_mirror_ds(lseg->pls_layout, mirror))
|
||||
if (!ff_layout_init_mirror_ds(lseg->pls_layout, mirror, dss_id))
|
||||
goto noconnect;
|
||||
|
||||
ds = mirror->mirror_ds->ds;
|
||||
ds = mirror->dss[dss_id].mirror_ds->ds;
|
||||
if (READ_ONCE(ds->ds_clp))
|
||||
goto out;
|
||||
/* matching smp_wmb() in _nfs4_pnfs_v3/4_ds_connect */
|
||||
@@ -388,10 +392,10 @@ nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg,
|
||||
/* FIXME: For now we assume the server sent only one version of NFS
|
||||
* to use for the DS.
|
||||
*/
|
||||
status = nfs4_pnfs_ds_connect(s, ds, &mirror->mirror_ds->id_node,
|
||||
status = nfs4_pnfs_ds_connect(s, ds, &mirror->dss[dss_id].mirror_ds->id_node,
|
||||
dataserver_timeo, dataserver_retrans,
|
||||
mirror->mirror_ds->ds_versions[0].version,
|
||||
mirror->mirror_ds->ds_versions[0].minor_version);
|
||||
mirror->dss[dss_id].mirror_ds->ds_versions[0].version,
|
||||
mirror->dss[dss_id].mirror_ds->ds_versions[0].minor_version);
|
||||
|
||||
/* connect success, check rsize/wsize limit */
|
||||
if (!status) {
|
||||
@@ -404,15 +408,15 @@ nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg,
|
||||
max_payload =
|
||||
nfs_block_size(rpc_max_payload(ds->ds_clp->cl_rpcclient),
|
||||
NULL);
|
||||
if (mirror->mirror_ds->ds_versions[0].rsize > max_payload)
|
||||
mirror->mirror_ds->ds_versions[0].rsize = max_payload;
|
||||
if (mirror->mirror_ds->ds_versions[0].wsize > max_payload)
|
||||
mirror->mirror_ds->ds_versions[0].wsize = max_payload;
|
||||
if (mirror->dss[dss_id].mirror_ds->ds_versions[0].rsize > max_payload)
|
||||
mirror->dss[dss_id].mirror_ds->ds_versions[0].rsize = max_payload;
|
||||
if (mirror->dss[dss_id].mirror_ds->ds_versions[0].wsize > max_payload)
|
||||
mirror->dss[dss_id].mirror_ds->ds_versions[0].wsize = max_payload;
|
||||
goto out;
|
||||
}
|
||||
noconnect:
|
||||
ff_layout_track_ds_error(FF_LAYOUT_FROM_HDR(lseg->pls_layout),
|
||||
mirror, lseg->pls_range.offset,
|
||||
mirror, dss_id, lseg->pls_range.offset,
|
||||
lseg->pls_range.length, NFS4ERR_NXIO,
|
||||
OP_ILLEGAL, GFP_NOIO);
|
||||
ff_layout_send_layouterror(lseg);
|
||||
@@ -426,12 +430,13 @@ out:
|
||||
const struct cred *
|
||||
ff_layout_get_ds_cred(struct nfs4_ff_layout_mirror *mirror,
|
||||
const struct pnfs_layout_range *range,
|
||||
const struct cred *mdscred)
|
||||
const struct cred *mdscred,
|
||||
u32 dss_id)
|
||||
{
|
||||
const struct cred *cred;
|
||||
|
||||
if (mirror && !mirror->mirror_ds->ds_versions[0].tightly_coupled) {
|
||||
cred = ff_layout_get_mirror_cred(mirror, range->iomode);
|
||||
if (mirror && !mirror->dss[dss_id].mirror_ds->ds_versions[0].tightly_coupled) {
|
||||
cred = ff_layout_get_mirror_cred(mirror, range->iomode, dss_id);
|
||||
if (!cred)
|
||||
cred = get_cred(mdscred);
|
||||
} else {
|
||||
@@ -445,15 +450,17 @@ ff_layout_get_ds_cred(struct nfs4_ff_layout_mirror *mirror,
|
||||
* @mirror: pointer to the mirror
|
||||
* @ds_clp: nfs_client for the DS
|
||||
* @inode: pointer to inode
|
||||
* @dss_id: DS stripe id
|
||||
*
|
||||
* Find or create a DS rpc client with th MDS server rpc client auth flavor
|
||||
* in the nfs_client cl_ds_clients list.
|
||||
*/
|
||||
struct rpc_clnt *
|
||||
nfs4_ff_find_or_create_ds_client(struct nfs4_ff_layout_mirror *mirror,
|
||||
struct nfs_client *ds_clp, struct inode *inode)
|
||||
struct nfs_client *ds_clp, struct inode *inode,
|
||||
u32 dss_id)
|
||||
{
|
||||
switch (mirror->mirror_ds->ds_versions[0].version) {
|
||||
switch (mirror->dss[dss_id].mirror_ds->ds_versions[0].version) {
|
||||
case 3:
|
||||
/* For NFSv3 DS, flavor is set when creating DS connections */
|
||||
return ds_clp->cl_rpcclient;
|
||||
@@ -559,16 +566,18 @@ static bool ff_read_layout_has_available_ds(struct pnfs_layout_segment *lseg)
|
||||
{
|
||||
struct nfs4_ff_layout_mirror *mirror;
|
||||
struct nfs4_deviceid_node *devid;
|
||||
u32 idx;
|
||||
u32 idx, dss_id;
|
||||
|
||||
for (idx = 0; idx < FF_LAYOUT_MIRROR_COUNT(lseg); idx++) {
|
||||
mirror = FF_LAYOUT_COMP(lseg, idx);
|
||||
if (mirror) {
|
||||
if (!mirror->mirror_ds)
|
||||
if (!mirror)
|
||||
continue;
|
||||
for (dss_id = 0; dss_id < mirror->dss_count; dss_id++) {
|
||||
if (!mirror->dss[dss_id].mirror_ds)
|
||||
return true;
|
||||
if (IS_ERR(mirror->mirror_ds))
|
||||
if (IS_ERR(mirror->dss[dss_id].mirror_ds))
|
||||
continue;
|
||||
devid = &mirror->mirror_ds->id_node;
|
||||
devid = &mirror->dss[dss_id].mirror_ds->id_node;
|
||||
if (!nfs4_test_deviceid_unavailable(devid))
|
||||
return true;
|
||||
}
|
||||
@@ -581,17 +590,21 @@ static bool ff_rw_layout_has_available_ds(struct pnfs_layout_segment *lseg)
|
||||
{
|
||||
struct nfs4_ff_layout_mirror *mirror;
|
||||
struct nfs4_deviceid_node *devid;
|
||||
u32 idx;
|
||||
u32 idx, dss_id;
|
||||
|
||||
for (idx = 0; idx < FF_LAYOUT_MIRROR_COUNT(lseg); idx++) {
|
||||
mirror = FF_LAYOUT_COMP(lseg, idx);
|
||||
if (!mirror || IS_ERR(mirror->mirror_ds))
|
||||
return false;
|
||||
if (!mirror->mirror_ds)
|
||||
continue;
|
||||
devid = &mirror->mirror_ds->id_node;
|
||||
if (nfs4_test_deviceid_unavailable(devid))
|
||||
if (!mirror)
|
||||
return false;
|
||||
for (dss_id = 0; dss_id < mirror->dss_count; dss_id++) {
|
||||
if (IS_ERR(mirror->dss[dss_id].mirror_ds))
|
||||
return false;
|
||||
if (!mirror->dss[dss_id].mirror_ds)
|
||||
continue;
|
||||
devid = &mirror->dss[dss_id].mirror_ds->id_node;
|
||||
if (nfs4_test_deviceid_unavailable(devid))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return FF_LAYOUT_MIRROR_COUNT(lseg) != 0;
|
||||
|
||||
@@ -1073,6 +1073,21 @@ out_no_revalidate:
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
stat->blksize = NFS_SERVER(inode)->dtsize;
|
||||
stat->btime = NFS_I(inode)->btime;
|
||||
|
||||
/* Special handling for STATX_DIOALIGN and STATX_DIO_READ_ALIGN
|
||||
* - NFS doesn't have DIO alignment constraints, avoid getting
|
||||
* these DIO attrs from remote and just respond with most
|
||||
* accommodating limits (so client will issue supported DIO).
|
||||
* - this is unintuitive, but the most coarse-grained
|
||||
* dio_offset_align is the most accommodating.
|
||||
*/
|
||||
if ((request_mask & (STATX_DIOALIGN | STATX_DIO_READ_ALIGN)) &&
|
||||
S_ISREG(inode->i_mode)) {
|
||||
stat->result_mask |= STATX_DIOALIGN | STATX_DIO_READ_ALIGN;
|
||||
stat->dio_mem_align = 4; /* 4-byte alignment */
|
||||
stat->dio_offset_align = PAGE_SIZE;
|
||||
stat->dio_read_offset_align = stat->dio_offset_align;
|
||||
}
|
||||
out:
|
||||
trace_nfs_getattr_exit(inode, err);
|
||||
return err;
|
||||
|
||||
@@ -456,6 +456,16 @@ extern int nfs_wait_bit_killable(struct wait_bit_key *key, int mode);
|
||||
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
/* localio.c */
|
||||
struct nfs_local_dio {
|
||||
u32 mem_align;
|
||||
u32 offset_align;
|
||||
loff_t middle_offset;
|
||||
loff_t end_offset;
|
||||
ssize_t start_len; /* Length for misaligned first extent */
|
||||
ssize_t middle_len; /* Length for DIO-aligned middle extent */
|
||||
ssize_t end_len; /* Length for misaligned last extent */
|
||||
};
|
||||
|
||||
extern void nfs_local_probe_async(struct nfs_client *);
|
||||
extern void nfs_local_probe_async_work(struct work_struct *);
|
||||
extern struct nfsd_file *nfs_local_open_fh(struct nfs_client *,
|
||||
|
||||
403
fs/nfs/localio.c
403
fs/nfs/localio.c
@@ -30,6 +30,8 @@
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_VFS
|
||||
|
||||
#define NFSLOCAL_MAX_IOS 3
|
||||
|
||||
struct nfs_local_kiocb {
|
||||
struct kiocb kiocb;
|
||||
struct bio_vec *bvec;
|
||||
@@ -37,6 +39,14 @@ struct nfs_local_kiocb {
|
||||
struct work_struct work;
|
||||
void (*aio_complete_work)(struct work_struct *);
|
||||
struct nfsd_file *localio;
|
||||
/* Begin mostly DIO-specific members */
|
||||
size_t end_len;
|
||||
short int end_iter_index;
|
||||
short int n_iters;
|
||||
bool iter_is_dio_aligned[NFSLOCAL_MAX_IOS];
|
||||
loff_t offset[NFSLOCAL_MAX_IOS] ____cacheline_aligned;
|
||||
struct iov_iter iters[NFSLOCAL_MAX_IOS];
|
||||
/* End mostly DIO-specific members */
|
||||
};
|
||||
|
||||
struct nfs_local_fsync_ctx {
|
||||
@@ -49,11 +59,6 @@ struct nfs_local_fsync_ctx {
|
||||
static bool localio_enabled __read_mostly = true;
|
||||
module_param(localio_enabled, bool, 0644);
|
||||
|
||||
static bool localio_O_DIRECT_semantics __read_mostly = false;
|
||||
module_param(localio_O_DIRECT_semantics, bool, 0644);
|
||||
MODULE_PARM_DESC(localio_O_DIRECT_semantics,
|
||||
"LOCALIO will use O_DIRECT semantics to filesystem.");
|
||||
|
||||
static inline bool nfs_client_is_local(const struct nfs_client *clp)
|
||||
{
|
||||
return !!rcu_access_pointer(clp->cl_uuid.net);
|
||||
@@ -231,13 +236,13 @@ __nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
|
||||
struct nfsd_file __rcu **pnf,
|
||||
const fmode_t mode)
|
||||
{
|
||||
int status = 0;
|
||||
struct nfsd_file *localio;
|
||||
|
||||
localio = nfs_open_local_fh(&clp->cl_uuid, clp->cl_rpcclient,
|
||||
cred, fh, nfl, pnf, mode);
|
||||
if (IS_ERR(localio)) {
|
||||
int status = PTR_ERR(localio);
|
||||
trace_nfs_local_open_fh(fh, mode, status);
|
||||
status = PTR_ERR(localio);
|
||||
switch (status) {
|
||||
case -ENOMEM:
|
||||
case -ENXIO:
|
||||
@@ -247,6 +252,7 @@ __nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
|
||||
nfs_local_probe(clp);
|
||||
}
|
||||
}
|
||||
trace_nfs_local_open_fh(fh, mode, status);
|
||||
return localio;
|
||||
}
|
||||
|
||||
@@ -281,23 +287,6 @@ nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_local_open_fh);
|
||||
|
||||
static struct bio_vec *
|
||||
nfs_bvec_alloc_and_import_pagevec(struct page **pagevec,
|
||||
unsigned int npages, gfp_t flags)
|
||||
{
|
||||
struct bio_vec *bvec, *p;
|
||||
|
||||
bvec = kmalloc_array(npages, sizeof(*bvec), flags);
|
||||
if (bvec != NULL) {
|
||||
for (p = bvec; npages > 0; p++, pagevec++, npages--) {
|
||||
p->bv_page = *pagevec;
|
||||
p->bv_len = PAGE_SIZE;
|
||||
p->bv_offset = 0;
|
||||
}
|
||||
}
|
||||
return bvec;
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_iocb_free(struct nfs_local_kiocb *iocb)
|
||||
{
|
||||
@@ -311,40 +300,191 @@ nfs_local_iocb_alloc(struct nfs_pgio_header *hdr,
|
||||
{
|
||||
struct nfs_local_kiocb *iocb;
|
||||
|
||||
iocb = kmalloc(sizeof(*iocb), flags);
|
||||
iocb = kzalloc(sizeof(*iocb), flags);
|
||||
if (iocb == NULL)
|
||||
return NULL;
|
||||
iocb->bvec = nfs_bvec_alloc_and_import_pagevec(hdr->page_array.pagevec,
|
||||
hdr->page_array.npages, flags);
|
||||
|
||||
iocb->bvec = kmalloc_array(hdr->page_array.npages,
|
||||
sizeof(struct bio_vec), flags);
|
||||
if (iocb->bvec == NULL) {
|
||||
kfree(iocb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (localio_O_DIRECT_semantics &&
|
||||
test_bit(NFS_IOHDR_ODIRECT, &hdr->flags)) {
|
||||
iocb->kiocb.ki_filp = file;
|
||||
iocb->kiocb.ki_flags = IOCB_DIRECT;
|
||||
} else
|
||||
init_sync_kiocb(&iocb->kiocb, file);
|
||||
init_sync_kiocb(&iocb->kiocb, file);
|
||||
|
||||
iocb->kiocb.ki_pos = hdr->args.offset;
|
||||
iocb->hdr = hdr;
|
||||
iocb->kiocb.ki_flags &= ~IOCB_APPEND;
|
||||
iocb->aio_complete_work = NULL;
|
||||
|
||||
iocb->end_iter_index = -1;
|
||||
|
||||
return iocb;
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_iter_init(struct iov_iter *i, struct nfs_local_kiocb *iocb, int dir)
|
||||
static bool
|
||||
nfs_is_local_dio_possible(struct nfs_local_kiocb *iocb, int rw,
|
||||
size_t len, struct nfs_local_dio *local_dio)
|
||||
{
|
||||
struct nfs_pgio_header *hdr = iocb->hdr;
|
||||
loff_t offset = hdr->args.offset;
|
||||
u32 nf_dio_mem_align, nf_dio_offset_align, nf_dio_read_offset_align;
|
||||
loff_t start_end, orig_end, middle_end;
|
||||
|
||||
iov_iter_bvec(i, dir, iocb->bvec, hdr->page_array.npages,
|
||||
hdr->args.count + hdr->args.pgbase);
|
||||
if (hdr->args.pgbase != 0)
|
||||
iov_iter_advance(i, hdr->args.pgbase);
|
||||
nfs_to->nfsd_file_dio_alignment(iocb->localio, &nf_dio_mem_align,
|
||||
&nf_dio_offset_align, &nf_dio_read_offset_align);
|
||||
if (rw == ITER_DEST)
|
||||
nf_dio_offset_align = nf_dio_read_offset_align;
|
||||
|
||||
if (unlikely(!nf_dio_mem_align || !nf_dio_offset_align))
|
||||
return false;
|
||||
if (unlikely(nf_dio_offset_align > PAGE_SIZE))
|
||||
return false;
|
||||
if (unlikely(len < nf_dio_offset_align))
|
||||
return false;
|
||||
|
||||
local_dio->mem_align = nf_dio_mem_align;
|
||||
local_dio->offset_align = nf_dio_offset_align;
|
||||
|
||||
start_end = round_up(offset, nf_dio_offset_align);
|
||||
orig_end = offset + len;
|
||||
middle_end = round_down(orig_end, nf_dio_offset_align);
|
||||
|
||||
local_dio->middle_offset = start_end;
|
||||
local_dio->end_offset = middle_end;
|
||||
|
||||
local_dio->start_len = start_end - offset;
|
||||
local_dio->middle_len = middle_end - start_end;
|
||||
local_dio->end_len = orig_end - middle_end;
|
||||
|
||||
if (rw == ITER_DEST)
|
||||
trace_nfs_local_dio_read(hdr->inode, offset, len, local_dio);
|
||||
else
|
||||
trace_nfs_local_dio_write(hdr->inode, offset, len, local_dio);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool nfs_iov_iter_aligned_bvec(const struct iov_iter *i,
|
||||
unsigned int addr_mask, unsigned int len_mask)
|
||||
{
|
||||
const struct bio_vec *bvec = i->bvec;
|
||||
size_t skip = i->iov_offset;
|
||||
size_t size = i->count;
|
||||
|
||||
if (size & len_mask)
|
||||
return false;
|
||||
do {
|
||||
size_t len = bvec->bv_len;
|
||||
|
||||
if (len > size)
|
||||
len = size;
|
||||
if ((unsigned long)(bvec->bv_offset + skip) & addr_mask)
|
||||
return false;
|
||||
bvec++;
|
||||
size -= len;
|
||||
skip = 0;
|
||||
} while (size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup as many as 3 iov_iter based on extents described by @local_dio.
|
||||
* Returns the number of iov_iter that were setup.
|
||||
*/
|
||||
static int
|
||||
nfs_local_iters_setup_dio(struct nfs_local_kiocb *iocb, int rw,
|
||||
unsigned int nvecs, size_t len,
|
||||
struct nfs_local_dio *local_dio)
|
||||
{
|
||||
int n_iters = 0;
|
||||
struct iov_iter *iters = iocb->iters;
|
||||
|
||||
/* Setup misaligned start? */
|
||||
if (local_dio->start_len) {
|
||||
iov_iter_bvec(&iters[n_iters], rw, iocb->bvec, nvecs, len);
|
||||
iters[n_iters].count = local_dio->start_len;
|
||||
iocb->offset[n_iters] = iocb->hdr->args.offset;
|
||||
iocb->iter_is_dio_aligned[n_iters] = false;
|
||||
++n_iters;
|
||||
}
|
||||
|
||||
/* Setup misaligned end?
|
||||
* If so, the end is purposely setup to be issued using buffered IO
|
||||
* before the middle (which will use DIO, if DIO-aligned, with AIO).
|
||||
* This creates problems if/when the end results in a partial write.
|
||||
* So must save index and length of end to handle this corner case.
|
||||
*/
|
||||
if (local_dio->end_len) {
|
||||
iov_iter_bvec(&iters[n_iters], rw, iocb->bvec, nvecs, len);
|
||||
iocb->offset[n_iters] = local_dio->end_offset;
|
||||
iov_iter_advance(&iters[n_iters],
|
||||
local_dio->start_len + local_dio->middle_len);
|
||||
iocb->iter_is_dio_aligned[n_iters] = false;
|
||||
/* Save index and length of end */
|
||||
iocb->end_iter_index = n_iters;
|
||||
iocb->end_len = local_dio->end_len;
|
||||
++n_iters;
|
||||
}
|
||||
|
||||
/* Setup DIO-aligned middle to be issued last, to allow for
|
||||
* DIO with AIO completion (see nfs_local_call_{read,write}).
|
||||
*/
|
||||
iov_iter_bvec(&iters[n_iters], rw, iocb->bvec, nvecs, len);
|
||||
if (local_dio->start_len)
|
||||
iov_iter_advance(&iters[n_iters], local_dio->start_len);
|
||||
iters[n_iters].count -= local_dio->end_len;
|
||||
iocb->offset[n_iters] = local_dio->middle_offset;
|
||||
|
||||
iocb->iter_is_dio_aligned[n_iters] =
|
||||
nfs_iov_iter_aligned_bvec(&iters[n_iters],
|
||||
local_dio->mem_align-1, local_dio->offset_align-1);
|
||||
|
||||
if (unlikely(!iocb->iter_is_dio_aligned[n_iters])) {
|
||||
trace_nfs_local_dio_misaligned(iocb->hdr->inode,
|
||||
iocb->hdr->args.offset, len, local_dio);
|
||||
return 0; /* no DIO-aligned IO possible */
|
||||
}
|
||||
++n_iters;
|
||||
|
||||
iocb->n_iters = n_iters;
|
||||
return n_iters;
|
||||
}
|
||||
|
||||
static noinline_for_stack void
|
||||
nfs_local_iters_init(struct nfs_local_kiocb *iocb, int rw)
|
||||
{
|
||||
struct nfs_pgio_header *hdr = iocb->hdr;
|
||||
struct page **pagevec = hdr->page_array.pagevec;
|
||||
unsigned long v, total;
|
||||
unsigned int base;
|
||||
size_t len;
|
||||
|
||||
v = 0;
|
||||
total = hdr->args.count;
|
||||
base = hdr->args.pgbase;
|
||||
while (total && v < hdr->page_array.npages) {
|
||||
len = min_t(size_t, total, PAGE_SIZE - base);
|
||||
bvec_set_page(&iocb->bvec[v], *pagevec, len, base);
|
||||
total -= len;
|
||||
++pagevec;
|
||||
++v;
|
||||
base = 0;
|
||||
}
|
||||
len = hdr->args.count - total;
|
||||
|
||||
if (test_bit(NFS_IOHDR_ODIRECT, &hdr->flags)) {
|
||||
struct nfs_local_dio local_dio;
|
||||
|
||||
if (nfs_is_local_dio_possible(iocb, rw, len, &local_dio) &&
|
||||
nfs_local_iters_setup_dio(iocb, rw, v, len, &local_dio) != 0)
|
||||
return; /* is DIO-aligned */
|
||||
}
|
||||
|
||||
/* Use buffered IO */
|
||||
iocb->offset[0] = hdr->args.offset;
|
||||
iov_iter_bvec(&iocb->iters[0], rw, iocb->bvec, v, len);
|
||||
iocb->n_iters = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -367,23 +507,31 @@ nfs_local_pgio_init(struct nfs_pgio_header *hdr,
|
||||
static void
|
||||
nfs_local_pgio_done(struct nfs_pgio_header *hdr, long status)
|
||||
{
|
||||
/* Must handle partial completions */
|
||||
if (status >= 0) {
|
||||
hdr->res.count = status;
|
||||
hdr->res.op_status = NFS4_OK;
|
||||
hdr->task.tk_status = 0;
|
||||
hdr->res.count += status;
|
||||
/* @hdr was initialized to 0 (zeroed during allocation) */
|
||||
if (hdr->task.tk_status == 0)
|
||||
hdr->res.op_status = NFS4_OK;
|
||||
} else {
|
||||
hdr->res.op_status = nfs_localio_errno_to_nfs4_stat(status);
|
||||
hdr->task.tk_status = status;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_iocb_release(struct nfs_local_kiocb *iocb)
|
||||
{
|
||||
nfs_local_file_put(iocb->localio);
|
||||
nfs_local_iocb_free(iocb);
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_local_pgio_release(struct nfs_local_kiocb *iocb)
|
||||
{
|
||||
struct nfs_pgio_header *hdr = iocb->hdr;
|
||||
|
||||
nfs_local_file_put(iocb->localio);
|
||||
nfs_local_iocb_free(iocb);
|
||||
nfs_local_iocb_release(iocb);
|
||||
nfs_local_hdr_release(hdr, hdr->task.tk_ops);
|
||||
}
|
||||
|
||||
@@ -405,7 +553,10 @@ nfs_local_read_done(struct nfs_local_kiocb *iocb, long status)
|
||||
struct nfs_pgio_header *hdr = iocb->hdr;
|
||||
struct file *filp = iocb->kiocb.ki_filp;
|
||||
|
||||
nfs_local_pgio_done(hdr, status);
|
||||
if ((iocb->kiocb.ki_flags & IOCB_DIRECT) && status == -EINVAL) {
|
||||
/* Underlying FS will return -EINVAL if misaligned DIO is attempted. */
|
||||
pr_info_ratelimited("nfs: Unexpected direct I/O read alignment failure\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Must clear replen otherwise NFSv3 data corruption will occur
|
||||
@@ -434,6 +585,7 @@ static void nfs_local_read_aio_complete(struct kiocb *kiocb, long ret)
|
||||
struct nfs_local_kiocb *iocb =
|
||||
container_of(kiocb, struct nfs_local_kiocb, kiocb);
|
||||
|
||||
nfs_local_pgio_done(iocb->hdr, ret);
|
||||
nfs_local_read_done(iocb, ret);
|
||||
nfs_local_pgio_aio_complete(iocb); /* Calls nfs_local_read_aio_complete_work */
|
||||
}
|
||||
@@ -444,14 +596,25 @@ static void nfs_local_call_read(struct work_struct *work)
|
||||
container_of(work, struct nfs_local_kiocb, work);
|
||||
struct file *filp = iocb->kiocb.ki_filp;
|
||||
const struct cred *save_cred;
|
||||
struct iov_iter iter;
|
||||
ssize_t status;
|
||||
|
||||
save_cred = override_creds(filp->f_cred);
|
||||
|
||||
nfs_local_iter_init(&iter, iocb, READ);
|
||||
for (int i = 0; i < iocb->n_iters ; i++) {
|
||||
if (iocb->iter_is_dio_aligned[i]) {
|
||||
iocb->kiocb.ki_flags |= IOCB_DIRECT;
|
||||
iocb->kiocb.ki_complete = nfs_local_read_aio_complete;
|
||||
iocb->aio_complete_work = nfs_local_read_aio_complete_work;
|
||||
}
|
||||
|
||||
status = filp->f_op->read_iter(&iocb->kiocb, &iter);
|
||||
iocb->kiocb.ki_pos = iocb->offset[i];
|
||||
status = filp->f_op->read_iter(&iocb->kiocb, &iocb->iters[i]);
|
||||
if (status != -EIOCBQUEUED) {
|
||||
nfs_local_pgio_done(iocb->hdr, status);
|
||||
if (iocb->hdr->task.tk_status)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
revert_creds(save_cred);
|
||||
|
||||
@@ -462,33 +625,17 @@ static void nfs_local_call_read(struct work_struct *work)
|
||||
}
|
||||
|
||||
static int
|
||||
nfs_do_local_read(struct nfs_pgio_header *hdr,
|
||||
struct nfsd_file *localio,
|
||||
nfs_local_do_read(struct nfs_local_kiocb *iocb,
|
||||
const struct rpc_call_ops *call_ops)
|
||||
{
|
||||
struct nfs_local_kiocb *iocb;
|
||||
struct file *file = nfs_to->nfsd_file_file(localio);
|
||||
|
||||
/* Don't support filesystems without read_iter */
|
||||
if (!file->f_op->read_iter)
|
||||
return -EAGAIN;
|
||||
struct nfs_pgio_header *hdr = iocb->hdr;
|
||||
|
||||
dprintk("%s: vfs_read count=%u pos=%llu\n",
|
||||
__func__, hdr->args.count, hdr->args.offset);
|
||||
|
||||
iocb = nfs_local_iocb_alloc(hdr, file, GFP_KERNEL);
|
||||
if (iocb == NULL)
|
||||
return -ENOMEM;
|
||||
iocb->localio = localio;
|
||||
|
||||
nfs_local_pgio_init(hdr, call_ops);
|
||||
hdr->res.eof = false;
|
||||
|
||||
if (iocb->kiocb.ki_flags & IOCB_DIRECT) {
|
||||
iocb->kiocb.ki_complete = nfs_local_read_aio_complete;
|
||||
iocb->aio_complete_work = nfs_local_read_aio_complete_work;
|
||||
}
|
||||
|
||||
INIT_WORK(&iocb->work, nfs_local_call_read);
|
||||
queue_work(nfslocaliod_workqueue, &iocb->work);
|
||||
|
||||
@@ -597,7 +744,13 @@ nfs_local_write_done(struct nfs_local_kiocb *iocb, long status)
|
||||
|
||||
dprintk("%s: wrote %ld bytes.\n", __func__, status > 0 ? status : 0);
|
||||
|
||||
if ((iocb->kiocb.ki_flags & IOCB_DIRECT) && status == -EINVAL) {
|
||||
/* Underlying FS will return -EINVAL if misaligned DIO is attempted. */
|
||||
pr_info_ratelimited("nfs: Unexpected direct I/O write alignment failure\n");
|
||||
}
|
||||
|
||||
/* Handle short writes as if they are ENOSPC */
|
||||
status = hdr->res.count;
|
||||
if (status > 0 && status < hdr->args.count) {
|
||||
hdr->mds_offset += status;
|
||||
hdr->args.offset += status;
|
||||
@@ -605,11 +758,11 @@ nfs_local_write_done(struct nfs_local_kiocb *iocb, long status)
|
||||
hdr->args.count -= status;
|
||||
nfs_set_pgio_error(hdr, -ENOSPC, hdr->args.offset);
|
||||
status = -ENOSPC;
|
||||
/* record -ENOSPC in terms of nfs_local_pgio_done */
|
||||
nfs_local_pgio_done(hdr, status);
|
||||
}
|
||||
if (status < 0)
|
||||
if (hdr->task.tk_status < 0)
|
||||
nfs_reset_boot_verifier(inode);
|
||||
|
||||
nfs_local_pgio_done(hdr, status);
|
||||
}
|
||||
|
||||
static void nfs_local_write_aio_complete_work(struct work_struct *work)
|
||||
@@ -626,6 +779,7 @@ static void nfs_local_write_aio_complete(struct kiocb *kiocb, long ret)
|
||||
struct nfs_local_kiocb *iocb =
|
||||
container_of(kiocb, struct nfs_local_kiocb, kiocb);
|
||||
|
||||
nfs_local_pgio_done(iocb->hdr, ret);
|
||||
nfs_local_write_done(iocb, ret);
|
||||
nfs_local_pgio_aio_complete(iocb); /* Calls nfs_local_write_aio_complete_work */
|
||||
}
|
||||
@@ -637,16 +791,53 @@ static void nfs_local_call_write(struct work_struct *work)
|
||||
struct file *filp = iocb->kiocb.ki_filp;
|
||||
unsigned long old_flags = current->flags;
|
||||
const struct cred *save_cred;
|
||||
struct iov_iter iter;
|
||||
ssize_t status;
|
||||
|
||||
current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO;
|
||||
save_cred = override_creds(filp->f_cred);
|
||||
|
||||
nfs_local_iter_init(&iter, iocb, WRITE);
|
||||
|
||||
file_start_write(filp);
|
||||
status = filp->f_op->write_iter(&iocb->kiocb, &iter);
|
||||
for (int i = 0; i < iocb->n_iters ; i++) {
|
||||
if (iocb->iter_is_dio_aligned[i]) {
|
||||
iocb->kiocb.ki_flags |= IOCB_DIRECT;
|
||||
iocb->kiocb.ki_complete = nfs_local_write_aio_complete;
|
||||
iocb->aio_complete_work = nfs_local_write_aio_complete_work;
|
||||
}
|
||||
retry:
|
||||
iocb->kiocb.ki_pos = iocb->offset[i];
|
||||
status = filp->f_op->write_iter(&iocb->kiocb, &iocb->iters[i]);
|
||||
if (status != -EIOCBQUEUED) {
|
||||
if (unlikely(status >= 0 && status < iocb->iters[i].count)) {
|
||||
/* partial write */
|
||||
if (i == iocb->end_iter_index) {
|
||||
/* Must not account partial end, otherwise, due
|
||||
* to end being issued before middle: the partial
|
||||
* write accounting in nfs_local_write_done()
|
||||
* would incorrectly advance hdr->args.offset
|
||||
*/
|
||||
status = 0;
|
||||
} else {
|
||||
/* Partial write at start or buffered middle,
|
||||
* exit early.
|
||||
*/
|
||||
nfs_local_pgio_done(iocb->hdr, status);
|
||||
break;
|
||||
}
|
||||
} else if (unlikely(status == -ENOTBLK &&
|
||||
(iocb->kiocb.ki_flags & IOCB_DIRECT))) {
|
||||
/* VFS will return -ENOTBLK if DIO WRITE fails to
|
||||
* invalidate the page cache. Retry using buffered IO.
|
||||
*/
|
||||
iocb->kiocb.ki_flags &= ~IOCB_DIRECT;
|
||||
iocb->kiocb.ki_complete = NULL;
|
||||
iocb->aio_complete_work = NULL;
|
||||
goto retry;
|
||||
}
|
||||
nfs_local_pgio_done(iocb->hdr, status);
|
||||
if (iocb->hdr->task.tk_status)
|
||||
break;
|
||||
}
|
||||
}
|
||||
file_end_write(filp);
|
||||
|
||||
revert_creds(save_cred);
|
||||
@@ -660,26 +851,15 @@ static void nfs_local_call_write(struct work_struct *work)
|
||||
}
|
||||
|
||||
static int
|
||||
nfs_do_local_write(struct nfs_pgio_header *hdr,
|
||||
struct nfsd_file *localio,
|
||||
nfs_local_do_write(struct nfs_local_kiocb *iocb,
|
||||
const struct rpc_call_ops *call_ops)
|
||||
{
|
||||
struct nfs_local_kiocb *iocb;
|
||||
struct file *file = nfs_to->nfsd_file_file(localio);
|
||||
|
||||
/* Don't support filesystems without write_iter */
|
||||
if (!file->f_op->write_iter)
|
||||
return -EAGAIN;
|
||||
struct nfs_pgio_header *hdr = iocb->hdr;
|
||||
|
||||
dprintk("%s: vfs_write count=%u pos=%llu %s\n",
|
||||
__func__, hdr->args.count, hdr->args.offset,
|
||||
(hdr->args.stable == NFS_UNSTABLE) ? "unstable" : "stable");
|
||||
|
||||
iocb = nfs_local_iocb_alloc(hdr, file, GFP_NOIO);
|
||||
if (iocb == NULL)
|
||||
return -ENOMEM;
|
||||
iocb->localio = localio;
|
||||
|
||||
switch (hdr->args.stable) {
|
||||
default:
|
||||
break;
|
||||
@@ -694,43 +874,74 @@ nfs_do_local_write(struct nfs_pgio_header *hdr,
|
||||
|
||||
nfs_set_local_verifier(hdr->inode, hdr->res.verf, hdr->args.stable);
|
||||
|
||||
if (iocb->kiocb.ki_flags & IOCB_DIRECT) {
|
||||
iocb->kiocb.ki_complete = nfs_local_write_aio_complete;
|
||||
iocb->aio_complete_work = nfs_local_write_aio_complete_work;
|
||||
}
|
||||
|
||||
INIT_WORK(&iocb->work, nfs_local_call_write);
|
||||
queue_work(nfslocaliod_workqueue, &iocb->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct nfs_local_kiocb *
|
||||
nfs_local_iocb_init(struct nfs_pgio_header *hdr, struct nfsd_file *localio)
|
||||
{
|
||||
struct file *file = nfs_to->nfsd_file_file(localio);
|
||||
struct nfs_local_kiocb *iocb;
|
||||
gfp_t gfp_mask;
|
||||
int rw;
|
||||
|
||||
if (hdr->rw_mode & FMODE_READ) {
|
||||
if (!file->f_op->read_iter)
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
gfp_mask = GFP_KERNEL;
|
||||
rw = ITER_DEST;
|
||||
} else {
|
||||
if (!file->f_op->write_iter)
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
gfp_mask = GFP_NOIO;
|
||||
rw = ITER_SOURCE;
|
||||
}
|
||||
|
||||
iocb = nfs_local_iocb_alloc(hdr, file, gfp_mask);
|
||||
if (iocb == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
iocb->hdr = hdr;
|
||||
iocb->localio = localio;
|
||||
|
||||
nfs_local_iters_init(iocb, rw);
|
||||
|
||||
return iocb;
|
||||
}
|
||||
|
||||
int nfs_local_doio(struct nfs_client *clp, struct nfsd_file *localio,
|
||||
struct nfs_pgio_header *hdr,
|
||||
const struct rpc_call_ops *call_ops)
|
||||
{
|
||||
struct nfs_local_kiocb *iocb;
|
||||
int status = 0;
|
||||
|
||||
if (!hdr->args.count)
|
||||
return 0;
|
||||
|
||||
iocb = nfs_local_iocb_init(hdr, localio);
|
||||
if (IS_ERR(iocb))
|
||||
return PTR_ERR(iocb);
|
||||
|
||||
switch (hdr->rw_mode) {
|
||||
case FMODE_READ:
|
||||
status = nfs_do_local_read(hdr, localio, call_ops);
|
||||
status = nfs_local_do_read(iocb, call_ops);
|
||||
break;
|
||||
case FMODE_WRITE:
|
||||
status = nfs_do_local_write(hdr, localio, call_ops);
|
||||
status = nfs_local_do_write(iocb, call_ops);
|
||||
break;
|
||||
default:
|
||||
dprintk("%s: invalid mode: %d\n", __func__,
|
||||
hdr->rw_mode);
|
||||
status = -EINVAL;
|
||||
status = -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (status != 0) {
|
||||
if (status == -EAGAIN)
|
||||
nfs_localio_disable_client(clp);
|
||||
nfs_local_file_put(localio);
|
||||
nfs_local_iocb_release(iocb);
|
||||
hdr->task.tk_status = status;
|
||||
nfs_local_hdr_release(hdr, call_ops);
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
#include <linux/nfs2.h>
|
||||
#include <linux/nfs_fs.h>
|
||||
#include <linux/nfs_common.h>
|
||||
#include "nfstrace.h"
|
||||
#include "internal.h"
|
||||
#include "nfstrace.h"
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_XDR
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
#include <linux/nfsacl.h>
|
||||
#include <linux/nfs_common.h>
|
||||
|
||||
#include "nfstrace.h"
|
||||
#include "internal.h"
|
||||
#include "nfstrace.h"
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_XDR
|
||||
|
||||
|
||||
@@ -1514,7 +1514,7 @@ static ssize_t _nfs42_proc_listxattrs(struct inode *inode, void *buf,
|
||||
|
||||
|
||||
ret = -ENOMEM;
|
||||
res.scratch = alloc_page(GFP_KERNEL);
|
||||
res.scratch = folio_alloc(GFP_KERNEL, 0);
|
||||
if (!res.scratch)
|
||||
goto out;
|
||||
|
||||
@@ -1552,7 +1552,7 @@ out_free_pages:
|
||||
}
|
||||
kfree(pages);
|
||||
out_free_scratch:
|
||||
__free_page(res.scratch);
|
||||
folio_put(res.scratch);
|
||||
out:
|
||||
return ret;
|
||||
|
||||
|
||||
@@ -1781,7 +1781,7 @@ static int nfs4_xdr_dec_listxattrs(struct rpc_rqst *rqstp,
|
||||
struct compound_hdr hdr;
|
||||
int status;
|
||||
|
||||
xdr_set_scratch_page(xdr, res->scratch);
|
||||
xdr_set_scratch_folio(xdr, res->scratch);
|
||||
|
||||
status = decode_compound_hdr(xdr, &hdr);
|
||||
if (status)
|
||||
|
||||
@@ -456,4 +456,5 @@ const struct file_operations nfs4_file_operations = {
|
||||
#else
|
||||
.llseek = nfs_file_llseek,
|
||||
#endif
|
||||
.fop_flags = FOP_DONTCACHE,
|
||||
};
|
||||
|
||||
@@ -391,7 +391,9 @@ static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dent
|
||||
*p++ = htonl(attrs); /* bitmap */
|
||||
*p++ = htonl(12); /* attribute buffer length */
|
||||
*p++ = htonl(NF4DIR);
|
||||
spin_lock(&dentry->d_lock);
|
||||
p = xdr_encode_hyper(p, NFS_FILEID(d_inode(dentry->d_parent)));
|
||||
spin_unlock(&dentry->d_lock);
|
||||
|
||||
readdir->pgbase = (char *)p - (char *)start;
|
||||
readdir->count -= readdir->pgbase;
|
||||
@@ -6160,7 +6162,7 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf,
|
||||
}
|
||||
|
||||
/* for decoding across pages */
|
||||
res.acl_scratch = alloc_page(GFP_KERNEL);
|
||||
res.acl_scratch = folio_alloc(GFP_KERNEL, 0);
|
||||
if (!res.acl_scratch)
|
||||
goto out_free;
|
||||
|
||||
@@ -6196,7 +6198,7 @@ out_free:
|
||||
while (--i >= 0)
|
||||
__free_page(pages[i]);
|
||||
if (res.acl_scratch)
|
||||
__free_page(res.acl_scratch);
|
||||
folio_put(res.acl_scratch);
|
||||
kfree(pages);
|
||||
return ret;
|
||||
}
|
||||
@@ -7872,10 +7874,10 @@ int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state,
|
||||
return err;
|
||||
do {
|
||||
err = _nfs4_do_setlk(state, F_SETLK, fl, NFS_LOCK_NEW);
|
||||
if (err != -NFS4ERR_DELAY)
|
||||
if (err != -NFS4ERR_DELAY && err != -NFS4ERR_GRACE)
|
||||
break;
|
||||
ssleep(1);
|
||||
} while (err == -NFS4ERR_DELAY);
|
||||
} while (err == -NFS4ERR_DELAY || err == -NFSERR_GRACE);
|
||||
return nfs4_handle_delegation_recall_error(server, state, stateid, fl, err);
|
||||
}
|
||||
|
||||
@@ -9442,7 +9444,7 @@ static int nfs4_verify_back_channel_attrs(struct nfs41_create_session_args *args
|
||||
goto out;
|
||||
if (rcvd->max_rqst_sz > sent->max_rqst_sz)
|
||||
return -EINVAL;
|
||||
if (rcvd->max_resp_sz < sent->max_resp_sz)
|
||||
if (rcvd->max_resp_sz > sent->max_resp_sz)
|
||||
return -EINVAL;
|
||||
if (rcvd->max_resp_sz_cached > sent->max_resp_sz_cached)
|
||||
return -EINVAL;
|
||||
|
||||
@@ -2744,6 +2744,9 @@ out_error:
|
||||
case -ENETUNREACH:
|
||||
nfs_mark_client_ready(clp, -EIO);
|
||||
break;
|
||||
case -EINVAL:
|
||||
nfs_mark_client_ready(clp, status);
|
||||
break;
|
||||
default:
|
||||
ssleep(1);
|
||||
break;
|
||||
|
||||
@@ -4930,7 +4930,7 @@ static int decode_attr_pnfstype(struct xdr_stream *xdr, uint32_t *bitmap,
|
||||
}
|
||||
|
||||
/*
|
||||
* The prefered block size for layout directed io
|
||||
* The preferred block size for layout directed io
|
||||
*/
|
||||
static int decode_attr_layout_blksize(struct xdr_stream *xdr, uint32_t *bitmap,
|
||||
uint32_t *res)
|
||||
@@ -6585,7 +6585,7 @@ nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
|
||||
int status;
|
||||
|
||||
if (res->acl_scratch != NULL)
|
||||
xdr_set_scratch_page(xdr, res->acl_scratch);
|
||||
xdr_set_scratch_folio(xdr, res->acl_scratch);
|
||||
status = decode_compound_hdr(xdr, &hdr);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
@@ -45,6 +45,23 @@
|
||||
{ BIT(NFS_INO_LAYOUTSTATS), "LAYOUTSTATS" }, \
|
||||
{ BIT(NFS_INO_ODIRECT), "ODIRECT" })
|
||||
|
||||
#define nfs_show_wb_flags(v) \
|
||||
__print_flags(v, "|", \
|
||||
{ BIT(PG_BUSY), "BUSY" }, \
|
||||
{ BIT(PG_MAPPED), "MAPPED" }, \
|
||||
{ BIT(PG_FOLIO), "FOLIO" }, \
|
||||
{ BIT(PG_CLEAN), "CLEAN" }, \
|
||||
{ BIT(PG_COMMIT_TO_DS), "COMMIT_TO_DS" }, \
|
||||
{ BIT(PG_INODE_REF), "INODE_REF" }, \
|
||||
{ BIT(PG_HEADLOCK), "HEADLOCK" }, \
|
||||
{ BIT(PG_TEARDOWN), "TEARDOWN" }, \
|
||||
{ BIT(PG_UNLOCKPAGE), "UNLOCKPAGE" }, \
|
||||
{ BIT(PG_UPTODATE), "UPTODATE" }, \
|
||||
{ BIT(PG_WB_END), "WB_END" }, \
|
||||
{ BIT(PG_REMOVE), "REMOVE" }, \
|
||||
{ BIT(PG_CONTENDED1), "CONTENDED1" }, \
|
||||
{ BIT(PG_CONTENDED2), "CONTENDED2" })
|
||||
|
||||
DECLARE_EVENT_CLASS(nfs_inode_event,
|
||||
TP_PROTO(
|
||||
const struct inode *inode
|
||||
@@ -967,7 +984,7 @@ DECLARE_EVENT_CLASS(nfs_folio_event,
|
||||
__entry->fileid = nfsi->fileid;
|
||||
__entry->fhandle = nfs_fhandle_hash(&nfsi->fh);
|
||||
__entry->version = inode_peek_iversion_raw(inode);
|
||||
__entry->offset = offset,
|
||||
__entry->offset = offset;
|
||||
__entry->count = count;
|
||||
),
|
||||
|
||||
@@ -1017,8 +1034,8 @@ DECLARE_EVENT_CLASS(nfs_folio_event_done,
|
||||
__entry->fileid = nfsi->fileid;
|
||||
__entry->fhandle = nfs_fhandle_hash(&nfsi->fh);
|
||||
__entry->version = inode_peek_iversion_raw(inode);
|
||||
__entry->offset = offset,
|
||||
__entry->count = count,
|
||||
__entry->offset = offset;
|
||||
__entry->count = count;
|
||||
__entry->ret = ret;
|
||||
),
|
||||
|
||||
@@ -1051,6 +1068,73 @@ DEFINE_NFS_FOLIO_EVENT_DONE(nfs_writeback_folio_done);
|
||||
DEFINE_NFS_FOLIO_EVENT(nfs_invalidate_folio);
|
||||
DEFINE_NFS_FOLIO_EVENT_DONE(nfs_launder_folio_done);
|
||||
|
||||
DEFINE_NFS_FOLIO_EVENT(nfs_try_to_update_request);
|
||||
DEFINE_NFS_FOLIO_EVENT_DONE(nfs_try_to_update_request_done);
|
||||
|
||||
DEFINE_NFS_FOLIO_EVENT(nfs_update_folio);
|
||||
DEFINE_NFS_FOLIO_EVENT_DONE(nfs_update_folio_done);
|
||||
|
||||
DEFINE_NFS_FOLIO_EVENT(nfs_write_begin);
|
||||
DEFINE_NFS_FOLIO_EVENT_DONE(nfs_write_begin_done);
|
||||
|
||||
DEFINE_NFS_FOLIO_EVENT(nfs_write_end);
|
||||
DEFINE_NFS_FOLIO_EVENT_DONE(nfs_write_end_done);
|
||||
|
||||
DEFINE_NFS_FOLIO_EVENT(nfs_writepages);
|
||||
DEFINE_NFS_FOLIO_EVENT_DONE(nfs_writepages_done);
|
||||
|
||||
DECLARE_EVENT_CLASS(nfs_kiocb_event,
|
||||
TP_PROTO(
|
||||
const struct kiocb *iocb,
|
||||
const struct iov_iter *iter
|
||||
),
|
||||
|
||||
TP_ARGS(iocb, iter),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(u32, fhandle)
|
||||
__field(u64, fileid)
|
||||
__field(u64, version)
|
||||
__field(loff_t, offset)
|
||||
__field(size_t, count)
|
||||
__field(int, flags)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
const struct inode *inode = file_inode(iocb->ki_filp);
|
||||
const struct nfs_inode *nfsi = NFS_I(inode);
|
||||
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->fileid = nfsi->fileid;
|
||||
__entry->fhandle = nfs_fhandle_hash(&nfsi->fh);
|
||||
__entry->version = inode_peek_iversion_raw(inode);
|
||||
__entry->offset = iocb->ki_pos;
|
||||
__entry->count = iov_iter_count(iter);
|
||||
__entry->flags = iocb->ki_flags;
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
"fileid=%02x:%02x:%llu fhandle=0x%08x version=%llu offset=%lld count=%zu ki_flags=%s",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
(unsigned long long)__entry->fileid,
|
||||
__entry->fhandle, __entry->version,
|
||||
__entry->offset, __entry->count,
|
||||
__print_flags(__entry->flags, "|", TRACE_IOCB_STRINGS)
|
||||
)
|
||||
);
|
||||
|
||||
#define DEFINE_NFS_KIOCB_EVENT(name) \
|
||||
DEFINE_EVENT(nfs_kiocb_event, name, \
|
||||
TP_PROTO( \
|
||||
const struct kiocb *iocb, \
|
||||
const struct iov_iter *iter \
|
||||
), \
|
||||
TP_ARGS(iocb, iter))
|
||||
|
||||
DEFINE_NFS_KIOCB_EVENT(nfs_file_read);
|
||||
DEFINE_NFS_KIOCB_EVENT(nfs_file_write);
|
||||
|
||||
TRACE_EVENT(nfs_aop_readahead,
|
||||
TP_PROTO(
|
||||
const struct inode *inode,
|
||||
@@ -1398,6 +1482,55 @@ TRACE_EVENT(nfs_writeback_done,
|
||||
)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(nfs_page_class,
|
||||
TP_PROTO(
|
||||
const struct nfs_page *req
|
||||
),
|
||||
|
||||
TP_ARGS(req),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(u32, fhandle)
|
||||
__field(u64, fileid)
|
||||
__field(const struct nfs_page *__private, req)
|
||||
__field(loff_t, offset)
|
||||
__field(unsigned int, count)
|
||||
__field(unsigned long, flags)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
const struct inode *inode = folio_inode(req->wb_folio);
|
||||
const struct nfs_inode *nfsi = NFS_I(inode);
|
||||
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->fileid = nfsi->fileid;
|
||||
__entry->fhandle = nfs_fhandle_hash(&nfsi->fh);
|
||||
__entry->req = req;
|
||||
__entry->offset = req_offset(req);
|
||||
__entry->count = req->wb_bytes;
|
||||
__entry->flags = req->wb_flags;
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
"fileid=%02x:%02x:%llu fhandle=0x%08x req=%p offset=%lld count=%u flags=%s",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
(unsigned long long)__entry->fileid, __entry->fhandle,
|
||||
__entry->req, __entry->offset, __entry->count,
|
||||
nfs_show_wb_flags(__entry->flags)
|
||||
)
|
||||
);
|
||||
|
||||
#define DEFINE_NFS_PAGE_EVENT(name) \
|
||||
DEFINE_EVENT(nfs_page_class, name, \
|
||||
TP_PROTO( \
|
||||
const struct nfs_page *req \
|
||||
), \
|
||||
TP_ARGS(req))
|
||||
|
||||
DEFINE_NFS_PAGE_EVENT(nfs_writepage_setup);
|
||||
DEFINE_NFS_PAGE_EVENT(nfs_do_writepage);
|
||||
|
||||
DECLARE_EVENT_CLASS(nfs_page_error_class,
|
||||
TP_PROTO(
|
||||
const struct inode *inode,
|
||||
@@ -1599,6 +1732,76 @@ DEFINE_NFS_DIRECT_REQ_EVENT(nfs_direct_write_completion);
|
||||
DEFINE_NFS_DIRECT_REQ_EVENT(nfs_direct_write_schedule_iovec);
|
||||
DEFINE_NFS_DIRECT_REQ_EVENT(nfs_direct_write_reschedule_io);
|
||||
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
|
||||
DECLARE_EVENT_CLASS(nfs_local_dio_class,
|
||||
TP_PROTO(
|
||||
const struct inode *inode,
|
||||
loff_t offset,
|
||||
ssize_t count,
|
||||
const struct nfs_local_dio *local_dio
|
||||
),
|
||||
TP_ARGS(inode, offset, count, local_dio),
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(u64, fileid)
|
||||
__field(u32, fhandle)
|
||||
__field(loff_t, offset)
|
||||
__field(ssize_t, count)
|
||||
__field(u32, mem_align)
|
||||
__field(u32, offset_align)
|
||||
__field(loff_t, start)
|
||||
__field(ssize_t, start_len)
|
||||
__field(loff_t, middle)
|
||||
__field(ssize_t, middle_len)
|
||||
__field(loff_t, end)
|
||||
__field(ssize_t, end_len)
|
||||
),
|
||||
TP_fast_assign(
|
||||
const struct nfs_inode *nfsi = NFS_I(inode);
|
||||
const struct nfs_fh *fh = &nfsi->fh;
|
||||
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->fileid = nfsi->fileid;
|
||||
__entry->fhandle = nfs_fhandle_hash(fh);
|
||||
__entry->offset = offset;
|
||||
__entry->count = count;
|
||||
__entry->mem_align = local_dio->mem_align;
|
||||
__entry->offset_align = local_dio->offset_align;
|
||||
__entry->start = offset;
|
||||
__entry->start_len = local_dio->start_len;
|
||||
__entry->middle = local_dio->middle_offset;
|
||||
__entry->middle_len = local_dio->middle_len;
|
||||
__entry->end = local_dio->end_offset;
|
||||
__entry->end_len = local_dio->end_len;
|
||||
),
|
||||
TP_printk("fileid=%02x:%02x:%llu fhandle=0x%08x "
|
||||
"offset=%lld count=%zd "
|
||||
"mem_align=%u offset_align=%u "
|
||||
"start=%llu+%zd middle=%llu+%zd end=%llu+%zd",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
(unsigned long long)__entry->fileid,
|
||||
__entry->fhandle, __entry->offset, __entry->count,
|
||||
__entry->mem_align, __entry->offset_align,
|
||||
__entry->start, __entry->start_len,
|
||||
__entry->middle, __entry->middle_len,
|
||||
__entry->end, __entry->end_len)
|
||||
)
|
||||
|
||||
#define DEFINE_NFS_LOCAL_DIO_EVENT(name) \
|
||||
DEFINE_EVENT(nfs_local_dio_class, nfs_local_dio_##name, \
|
||||
TP_PROTO(const struct inode *inode, \
|
||||
loff_t offset, \
|
||||
ssize_t count, \
|
||||
const struct nfs_local_dio *local_dio),\
|
||||
TP_ARGS(inode, offset, count, local_dio))
|
||||
|
||||
DEFINE_NFS_LOCAL_DIO_EVENT(read);
|
||||
DEFINE_NFS_LOCAL_DIO_EVENT(write);
|
||||
DEFINE_NFS_LOCAL_DIO_EVENT(misaligned);
|
||||
|
||||
#endif /* CONFIG_NFS_LOCALIO */
|
||||
|
||||
TRACE_EVENT(nfs_fh_to_dentry,
|
||||
TP_PROTO(
|
||||
const struct super_block *sb,
|
||||
@@ -1713,10 +1916,10 @@ TRACE_EVENT(nfs_local_open_fh,
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
"error=%d fhandle=0x%08x mode=%s",
|
||||
__entry->error,
|
||||
"fhandle=0x%08x mode=%s result=%d",
|
||||
__entry->fhandle,
|
||||
show_fs_fmode_flags(__entry->fmode)
|
||||
show_fs_fmode_flags(__entry->fmode),
|
||||
__entry->error
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -296,7 +296,7 @@ static void nfs_folio_end_writeback(struct folio *folio)
|
||||
{
|
||||
struct nfs_server *nfss = NFS_SERVER(folio->mapping->host);
|
||||
|
||||
folio_end_writeback(folio);
|
||||
folio_end_writeback_no_dropbehind(folio);
|
||||
if (atomic_long_dec_return(&nfss->writeback) <
|
||||
NFS_CONGESTION_OFF_THRESH) {
|
||||
nfss->write_congested = 0;
|
||||
@@ -593,6 +593,7 @@ static int nfs_do_writepage(struct folio *folio, struct writeback_control *wbc,
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
|
||||
trace_nfs_do_writepage(req);
|
||||
nfs_folio_set_writeback(folio);
|
||||
WARN_ON_ONCE(test_bit(PG_CLEAN, &req->wb_flags));
|
||||
|
||||
@@ -656,12 +657,14 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
|
||||
int priority = 0;
|
||||
int err;
|
||||
|
||||
trace_nfs_writepages(inode, wbc->range_start, wbc->range_end - wbc->range_start);
|
||||
|
||||
/* Wait with writeback until write congestion eases */
|
||||
if (wbc->sync_mode == WB_SYNC_NONE && nfss->write_congested) {
|
||||
err = wait_event_killable(nfss->write_congestion_wait,
|
||||
nfss->write_congested == 0);
|
||||
if (err)
|
||||
return err;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES);
|
||||
@@ -692,10 +695,10 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
|
||||
} while (err < 0 && !nfs_error_is_fatal(err));
|
||||
nfs_io_completion_put(ioc);
|
||||
|
||||
if (err < 0)
|
||||
goto out_err;
|
||||
return 0;
|
||||
if (err > 0)
|
||||
err = 0;
|
||||
out_err:
|
||||
trace_nfs_writepages_done(inode, wbc->range_start, wbc->range_end - wbc->range_start, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -745,6 +748,8 @@ static void nfs_inode_remove_request(struct nfs_page *req)
|
||||
clear_bit(PG_MAPPED, &req->wb_head->wb_flags);
|
||||
}
|
||||
spin_unlock(&mapping->i_private_lock);
|
||||
|
||||
folio_end_dropbehind(folio);
|
||||
}
|
||||
nfs_page_group_unlock(req);
|
||||
|
||||
@@ -926,7 +931,7 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr)
|
||||
req->wb_nio = 0;
|
||||
memcpy(&req->wb_verf, &hdr->verf.verifier, sizeof(req->wb_verf));
|
||||
nfs_mark_request_commit(req, hdr->lseg, &cinfo,
|
||||
hdr->pgio_mirror_idx);
|
||||
hdr->ds_commit_idx);
|
||||
goto next;
|
||||
}
|
||||
remove_req:
|
||||
@@ -1017,11 +1022,12 @@ static struct nfs_page *nfs_try_to_update_request(struct folio *folio,
|
||||
unsigned int end;
|
||||
int error;
|
||||
|
||||
trace_nfs_try_to_update_request(folio_inode(folio), offset, bytes);
|
||||
end = offset + bytes;
|
||||
|
||||
req = nfs_lock_and_join_requests(folio);
|
||||
if (IS_ERR_OR_NULL(req))
|
||||
return req;
|
||||
goto out;
|
||||
|
||||
rqend = req->wb_offset + req->wb_bytes;
|
||||
/*
|
||||
@@ -1043,6 +1049,9 @@ static struct nfs_page *nfs_try_to_update_request(struct folio *folio,
|
||||
else
|
||||
req->wb_bytes = rqend - req->wb_offset;
|
||||
req->wb_nio = 0;
|
||||
out:
|
||||
trace_nfs_try_to_update_request_done(folio_inode(folio), offset, bytes,
|
||||
PTR_ERR_OR_ZERO(req));
|
||||
return req;
|
||||
out_flushme:
|
||||
/*
|
||||
@@ -1053,6 +1062,7 @@ out_flushme:
|
||||
nfs_mark_request_dirty(req);
|
||||
nfs_unlock_and_release_request(req);
|
||||
error = nfs_wb_folio(folio->mapping->host, folio);
|
||||
trace_nfs_try_to_update_request_done(folio_inode(folio), offset, bytes, error);
|
||||
return (error < 0) ? ERR_PTR(error) : NULL;
|
||||
}
|
||||
|
||||
@@ -1090,6 +1100,7 @@ static int nfs_writepage_setup(struct nfs_open_context *ctx,
|
||||
req = nfs_setup_write_request(ctx, folio, offset, count);
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
trace_nfs_writepage_setup(req);
|
||||
/* Update file length */
|
||||
nfs_grow_file(folio, offset, count);
|
||||
nfs_mark_uptodate(req);
|
||||
@@ -1290,6 +1301,8 @@ int nfs_update_folio(struct file *file, struct folio *folio,
|
||||
|
||||
nfs_inc_stats(inode, NFSIOS_VFSUPDATEPAGE);
|
||||
|
||||
trace_nfs_update_folio(inode, offset, count);
|
||||
|
||||
dprintk("NFS: nfs_update_folio(%pD2 %d@%lld)\n", file, count,
|
||||
(long long)(folio_pos(folio) + offset));
|
||||
|
||||
@@ -1309,6 +1322,7 @@ int nfs_update_folio(struct file *file, struct folio *folio,
|
||||
if (status < 0)
|
||||
nfs_set_pageerror(mapping);
|
||||
out:
|
||||
trace_nfs_update_folio_done(inode, offset, count, status);
|
||||
dprintk("NFS: nfs_update_folio returns %d (isize %lld)\n",
|
||||
status, (long long)i_size_read(inode));
|
||||
return status;
|
||||
@@ -1806,7 +1820,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)
|
||||
nfs_mapping_set_error(folio, status);
|
||||
nfs_inode_remove_request(req);
|
||||
}
|
||||
dprintk_cont(", error = %d\n", status);
|
||||
dprintk(", error = %d\n", status);
|
||||
goto next;
|
||||
}
|
||||
|
||||
@@ -1816,11 +1830,11 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)
|
||||
/* We have a match */
|
||||
if (folio)
|
||||
nfs_inode_remove_request(req);
|
||||
dprintk_cont(" OK\n");
|
||||
dprintk(" OK\n");
|
||||
goto next;
|
||||
}
|
||||
/* We have a mismatch. Write the page again */
|
||||
dprintk_cont(" mismatch\n");
|
||||
dprintk(" mismatch\n");
|
||||
nfs_mark_request_dirty(req);
|
||||
atomic_long_inc(&NFS_I(data->inode)->redirtied_pages);
|
||||
next:
|
||||
|
||||
@@ -231,6 +231,9 @@ nfsd_file_alloc(struct net *net, struct inode *inode, unsigned char need,
|
||||
refcount_set(&nf->nf_ref, 1);
|
||||
nf->nf_may = need;
|
||||
nf->nf_mark = NULL;
|
||||
nf->nf_dio_mem_align = 0;
|
||||
nf->nf_dio_offset_align = 0;
|
||||
nf->nf_dio_read_offset_align = 0;
|
||||
return nf;
|
||||
}
|
||||
|
||||
@@ -1069,6 +1072,35 @@ nfsd_file_is_cached(struct inode *inode)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd_file_get_dio_attrs(const struct svc_fh *fhp, struct nfsd_file *nf)
|
||||
{
|
||||
struct inode *inode = file_inode(nf->nf_file);
|
||||
struct kstat stat;
|
||||
__be32 status;
|
||||
|
||||
/* Currently only need to get DIO alignment info for regular files */
|
||||
if (!S_ISREG(inode->i_mode))
|
||||
return nfs_ok;
|
||||
|
||||
status = fh_getattr(fhp, &stat);
|
||||
if (status != nfs_ok)
|
||||
return status;
|
||||
|
||||
trace_nfsd_file_get_dio_attrs(inode, &stat);
|
||||
|
||||
if (stat.result_mask & STATX_DIOALIGN) {
|
||||
nf->nf_dio_mem_align = stat.dio_mem_align;
|
||||
nf->nf_dio_offset_align = stat.dio_offset_align;
|
||||
}
|
||||
if (stat.result_mask & STATX_DIO_READ_ALIGN)
|
||||
nf->nf_dio_read_offset_align = stat.dio_read_offset_align;
|
||||
else
|
||||
nf->nf_dio_read_offset_align = nf->nf_dio_offset_align;
|
||||
|
||||
return nfs_ok;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd_file_do_acquire(struct svc_rqst *rqstp, struct net *net,
|
||||
struct svc_cred *cred,
|
||||
@@ -1187,6 +1219,8 @@ open_file:
|
||||
}
|
||||
status = nfserrno(ret);
|
||||
trace_nfsd_file_open(nf, status);
|
||||
if (status == nfs_ok)
|
||||
status = nfsd_file_get_dio_attrs(fhp, nf);
|
||||
}
|
||||
} else
|
||||
status = nfserr_jukebox;
|
||||
|
||||
@@ -54,6 +54,10 @@ struct nfsd_file {
|
||||
struct list_head nf_gc;
|
||||
struct rcu_head nf_rcu;
|
||||
ktime_t nf_birthtime;
|
||||
|
||||
u32 nf_dio_mem_align;
|
||||
u32 nf_dio_offset_align;
|
||||
u32 nf_dio_read_offset_align;
|
||||
};
|
||||
|
||||
int nfsd_file_cache_init(void);
|
||||
|
||||
@@ -117,6 +117,16 @@ nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
|
||||
return localio;
|
||||
}
|
||||
|
||||
static void nfsd_file_dio_alignment(struct nfsd_file *nf,
|
||||
u32 *nf_dio_mem_align,
|
||||
u32 *nf_dio_offset_align,
|
||||
u32 *nf_dio_read_offset_align)
|
||||
{
|
||||
*nf_dio_mem_align = nf->nf_dio_mem_align;
|
||||
*nf_dio_offset_align = nf->nf_dio_offset_align;
|
||||
*nf_dio_read_offset_align = nf->nf_dio_read_offset_align;
|
||||
}
|
||||
|
||||
static const struct nfsd_localio_operations nfsd_localio_ops = {
|
||||
.nfsd_net_try_get = nfsd_net_try_get,
|
||||
.nfsd_net_put = nfsd_net_put,
|
||||
@@ -124,6 +134,7 @@ static const struct nfsd_localio_operations nfsd_localio_ops = {
|
||||
.nfsd_file_put_local = nfsd_file_put_local,
|
||||
.nfsd_file_get_local = nfsd_file_get_local,
|
||||
.nfsd_file_file = nfsd_file_file,
|
||||
.nfsd_file_dio_alignment = nfsd_file_dio_alignment,
|
||||
};
|
||||
|
||||
void nfsd_localio_ops_init(void)
|
||||
|
||||
@@ -1954,7 +1954,7 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
* remaining listeners and recreate the list.
|
||||
*/
|
||||
if (delete)
|
||||
svc_xprt_destroy_all(serv, net);
|
||||
svc_xprt_destroy_all(serv, net, false);
|
||||
|
||||
/* walk list of addrs again, open any that still don't exist */
|
||||
nlmsg_for_each_attr_type(attr, NFSD_A_SERVER_SOCK_ADDR, info->nlhdr,
|
||||
|
||||
@@ -535,16 +535,13 @@ void nfsd_destroy_serv(struct net *net)
|
||||
#endif
|
||||
}
|
||||
|
||||
svc_xprt_destroy_all(serv, net);
|
||||
|
||||
/*
|
||||
* write_ports can create the server without actually starting
|
||||
* any threads--if we get shut down before any threads are
|
||||
* any threads. If we get shut down before any threads are
|
||||
* started, then nfsd_destroy_serv will be run before any of this
|
||||
* other initialization has been done except the rpcb information.
|
||||
*/
|
||||
svc_rpcb_cleanup(serv, net);
|
||||
|
||||
svc_xprt_destroy_all(serv, net, true);
|
||||
nfsd_shutdown_net(net);
|
||||
svc_destroy(&serv);
|
||||
}
|
||||
|
||||
@@ -1133,6 +1133,33 @@ TRACE_EVENT(nfsd_file_alloc,
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(nfsd_file_get_dio_attrs,
|
||||
TP_PROTO(
|
||||
const struct inode *inode,
|
||||
const struct kstat *stat
|
||||
),
|
||||
TP_ARGS(inode, stat),
|
||||
TP_STRUCT__entry(
|
||||
__field(const void *, inode)
|
||||
__field(unsigned long, mask)
|
||||
__field(u32, mem_align)
|
||||
__field(u32, offset_align)
|
||||
__field(u32, read_offset_align)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->inode = inode;
|
||||
__entry->mask = stat->result_mask;
|
||||
__entry->mem_align = stat->dio_mem_align;
|
||||
__entry->offset_align = stat->dio_offset_align;
|
||||
__entry->read_offset_align = stat->dio_read_offset_align;
|
||||
),
|
||||
TP_printk("inode=%p flags=%s mem_align=%u offset_align=%u read_offset_align=%u",
|
||||
__entry->inode, show_statx_mask(__entry->mask),
|
||||
__entry->mem_align, __entry->offset_align,
|
||||
__entry->read_offset_align
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(nfsd_file_acquire,
|
||||
TP_PROTO(
|
||||
const struct svc_rqst *rqstp,
|
||||
|
||||
@@ -185,6 +185,10 @@ static inline __be32 fh_getattr(const struct svc_fh *fh, struct kstat *stat)
|
||||
u32 request_mask = STATX_BASIC_STATS;
|
||||
struct path p = {.mnt = fh->fh_export->ex_path.mnt,
|
||||
.dentry = fh->fh_dentry};
|
||||
struct inode *inode = d_inode(p.dentry);
|
||||
|
||||
if (S_ISREG(inode->i_mode))
|
||||
request_mask |= (STATX_DIOALIGN | STATX_DIO_READ_ALIGN);
|
||||
|
||||
if (fh->fh_maxsize == NFS4_FHSIZE)
|
||||
request_mask |= (STATX_BTIME | STATX_CHANGE_COOKIE);
|
||||
|
||||
@@ -122,8 +122,6 @@ struct nfs_pageio_descriptor {
|
||||
/* arbitrarily selected limit to number of mirrors */
|
||||
#define NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX 16
|
||||
|
||||
#define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags))
|
||||
|
||||
extern struct nfs_page *nfs_page_create_from_page(struct nfs_open_context *ctx,
|
||||
struct page *page,
|
||||
unsigned int pgbase,
|
||||
|
||||
@@ -862,7 +862,7 @@ struct nfs_getaclres {
|
||||
size_t acl_len;
|
||||
size_t acl_data_offset;
|
||||
int acl_flags;
|
||||
struct page * acl_scratch;
|
||||
struct folio * acl_scratch;
|
||||
};
|
||||
|
||||
struct nfs_setattrres {
|
||||
@@ -1596,7 +1596,7 @@ struct nfs42_listxattrsargs {
|
||||
|
||||
struct nfs42_listxattrsres {
|
||||
struct nfs4_sequence_res seq_res;
|
||||
struct page *scratch;
|
||||
struct folio *scratch;
|
||||
void *xattr_buf;
|
||||
size_t xattr_len;
|
||||
u64 cookie;
|
||||
|
||||
@@ -65,6 +65,8 @@ struct nfsd_localio_operations {
|
||||
struct net *(*nfsd_file_put_local)(struct nfsd_file __rcu **);
|
||||
struct nfsd_file *(*nfsd_file_get_local)(struct nfsd_file *);
|
||||
struct file *(*nfsd_file_file)(struct nfsd_file *);
|
||||
void (*nfsd_file_dio_alignment)(struct nfsd_file *,
|
||||
u32 *, u32 *, u32 *);
|
||||
} ____cacheline_aligned;
|
||||
|
||||
extern void nfsd_localio_ops_init(void);
|
||||
|
||||
@@ -1229,6 +1229,8 @@ void folio_wait_writeback(struct folio *folio);
|
||||
int folio_wait_writeback_killable(struct folio *folio);
|
||||
void end_page_writeback(struct page *page);
|
||||
void folio_end_writeback(struct folio *folio);
|
||||
void folio_end_writeback_no_dropbehind(struct folio *folio);
|
||||
void folio_end_dropbehind(struct folio *folio);
|
||||
void folio_wait_stable(struct folio *folio);
|
||||
void __folio_mark_dirty(struct folio *folio, struct address_space *, int warn);
|
||||
void folio_account_cleaned(struct folio *folio, struct bdi_writeback *wb);
|
||||
|
||||
@@ -23,43 +23,30 @@ extern unsigned int nlm_debug;
|
||||
|
||||
#define dprintk(fmt, ...) \
|
||||
dfprintk(FACILITY, fmt, ##__VA_ARGS__)
|
||||
#define dprintk_cont(fmt, ...) \
|
||||
dfprintk_cont(FACILITY, fmt, ##__VA_ARGS__)
|
||||
#define dprintk_rcu(fmt, ...) \
|
||||
dfprintk_rcu(FACILITY, fmt, ##__VA_ARGS__)
|
||||
#define dprintk_rcu_cont(fmt, ...) \
|
||||
dfprintk_rcu_cont(FACILITY, fmt, ##__VA_ARGS__)
|
||||
|
||||
#undef ifdebug
|
||||
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
|
||||
# define ifdebug(fac) if (unlikely(rpc_debug & RPCDBG_##fac))
|
||||
|
||||
# if IS_ENABLED(CONFIG_SUNRPC_DEBUG_TRACE)
|
||||
# define __sunrpc_printk(fmt, ...) trace_printk(fmt, ##__VA_ARGS__)
|
||||
# else
|
||||
# define __sunrpc_printk(fmt, ...) printk(KERN_DEFAULT fmt, ##__VA_ARGS__)
|
||||
# endif
|
||||
|
||||
# define dfprintk(fac, fmt, ...) \
|
||||
do { \
|
||||
ifdebug(fac) \
|
||||
printk(KERN_DEFAULT fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
# define dfprintk_cont(fac, fmt, ...) \
|
||||
do { \
|
||||
ifdebug(fac) \
|
||||
printk(KERN_CONT fmt, ##__VA_ARGS__); \
|
||||
__sunrpc_printk(fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
# define dfprintk_rcu(fac, fmt, ...) \
|
||||
do { \
|
||||
ifdebug(fac) { \
|
||||
rcu_read_lock(); \
|
||||
printk(KERN_DEFAULT fmt, ##__VA_ARGS__); \
|
||||
rcu_read_unlock(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
# define dfprintk_rcu_cont(fac, fmt, ...) \
|
||||
do { \
|
||||
ifdebug(fac) { \
|
||||
rcu_read_lock(); \
|
||||
printk(KERN_CONT fmt, ##__VA_ARGS__); \
|
||||
__sunrpc_printk(fmt, ##__VA_ARGS__); \
|
||||
rcu_read_unlock(); \
|
||||
} \
|
||||
} while (0)
|
||||
@@ -68,7 +55,6 @@ do { \
|
||||
#else
|
||||
# define ifdebug(fac) if (0)
|
||||
# define dfprintk(fac, fmt, ...) do {} while (0)
|
||||
# define dfprintk_cont(fac, fmt, ...) do {} while (0)
|
||||
# define dfprintk_rcu(fac, fmt, ...) do {} while (0)
|
||||
# define RPC_IFDEBUG(x)
|
||||
#endif
|
||||
|
||||
@@ -196,7 +196,7 @@ struct svc_rqst {
|
||||
struct xdr_buf rq_arg;
|
||||
struct xdr_stream rq_arg_stream;
|
||||
struct xdr_stream rq_res_stream;
|
||||
struct page *rq_scratch_page;
|
||||
struct folio *rq_scratch_folio;
|
||||
struct xdr_buf rq_res;
|
||||
unsigned long rq_maxpages; /* num of entries in rq_pages */
|
||||
struct page * *rq_pages;
|
||||
@@ -503,7 +503,7 @@ static inline void svcxdr_init_decode(struct svc_rqst *rqstp)
|
||||
buf->len = buf->head->iov_len + buf->page_len + buf->tail->iov_len;
|
||||
|
||||
xdr_init_decode(xdr, buf, argv->iov_base, NULL);
|
||||
xdr_set_scratch_page(xdr, rqstp->rq_scratch_page);
|
||||
xdr_set_scratch_folio(xdr, rqstp->rq_scratch_folio);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -165,7 +165,8 @@ int svc_xprt_create(struct svc_serv *serv, const char *xprt_name,
|
||||
struct net *net, const int family,
|
||||
const unsigned short port, int flags,
|
||||
const struct cred *cred);
|
||||
void svc_xprt_destroy_all(struct svc_serv *serv, struct net *net);
|
||||
void svc_xprt_destroy_all(struct svc_serv *serv, struct net *net,
|
||||
bool unregister);
|
||||
void svc_xprt_received(struct svc_xprt *xprt);
|
||||
void svc_xprt_enqueue(struct svc_xprt *xprt);
|
||||
void svc_xprt_put(struct svc_xprt *xprt);
|
||||
|
||||
@@ -288,16 +288,16 @@ xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen)
|
||||
}
|
||||
|
||||
/**
|
||||
* xdr_set_scratch_page - Attach a scratch buffer for decoding data
|
||||
* xdr_set_scratch_folio - Attach a scratch buffer for decoding data
|
||||
* @xdr: pointer to xdr_stream struct
|
||||
* @page: an anonymous page
|
||||
* @page: an anonymous folio
|
||||
*
|
||||
* See xdr_set_scratch_buffer().
|
||||
*/
|
||||
static inline void
|
||||
xdr_set_scratch_page(struct xdr_stream *xdr, struct page *page)
|
||||
xdr_set_scratch_folio(struct xdr_stream *xdr, struct folio *folio)
|
||||
{
|
||||
xdr_set_scratch_buffer(xdr, page_address(page), PAGE_SIZE);
|
||||
xdr_set_scratch_buffer(xdr, folio_address(folio), folio_size(folio));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -141,3 +141,25 @@
|
||||
{ ATTR_TIMES_SET, "TIMES_SET" }, \
|
||||
{ ATTR_TOUCH, "TOUCH"}, \
|
||||
{ ATTR_DELEG, "DELEG"})
|
||||
|
||||
#define show_statx_mask(flags) \
|
||||
__print_flags(flags, "|", \
|
||||
{ STATX_TYPE, "TYPE" }, \
|
||||
{ STATX_MODE, "MODE" }, \
|
||||
{ STATX_NLINK, "NLINK" }, \
|
||||
{ STATX_UID, "UID" }, \
|
||||
{ STATX_GID, "GID" }, \
|
||||
{ STATX_ATIME, "ATIME" }, \
|
||||
{ STATX_MTIME, "MTIME" }, \
|
||||
{ STATX_CTIME, "CTIME" }, \
|
||||
{ STATX_INO, "INO" }, \
|
||||
{ STATX_SIZE, "SIZE" }, \
|
||||
{ STATX_BLOCKS, "BLOCKS" }, \
|
||||
{ STATX_BASIC_STATS, "BASIC_STATS" }, \
|
||||
{ STATX_BTIME, "BTIME" }, \
|
||||
{ STATX_MNT_ID, "MNT_ID" }, \
|
||||
{ STATX_DIOALIGN, "DIOALIGN" }, \
|
||||
{ STATX_MNT_ID_UNIQUE, "MNT_ID_UNIQUE" }, \
|
||||
{ STATX_SUBVOL, "SUBVOL" }, \
|
||||
{ STATX_WRITE_ATOMIC, "WRITE_ATOMIC" }, \
|
||||
{ STATX_DIO_READ_ALIGN, "DIO_READ_ALIGN" })
|
||||
|
||||
34
mm/filemap.c
34
mm/filemap.c
@@ -1621,7 +1621,7 @@ static void filemap_end_dropbehind(struct folio *folio)
|
||||
* completes. Do that now. If we fail, it's likely because of a big folio -
|
||||
* just reset dropbehind for that case and latter completions should invalidate.
|
||||
*/
|
||||
static void filemap_end_dropbehind_write(struct folio *folio)
|
||||
void folio_end_dropbehind(struct folio *folio)
|
||||
{
|
||||
if (!folio_test_dropbehind(folio))
|
||||
return;
|
||||
@@ -1638,16 +1638,18 @@ static void filemap_end_dropbehind_write(struct folio *folio)
|
||||
folio_unlock(folio);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(folio_end_dropbehind);
|
||||
|
||||
/**
|
||||
* folio_end_writeback - End writeback against a folio.
|
||||
* folio_end_writeback_no_dropbehind - End writeback against a folio.
|
||||
* @folio: The folio.
|
||||
*
|
||||
* The folio must actually be under writeback.
|
||||
* This call is intended for filesystems that need to defer dropbehind.
|
||||
*
|
||||
* Context: May be called from process or interrupt context.
|
||||
*/
|
||||
void folio_end_writeback(struct folio *folio)
|
||||
void folio_end_writeback_no_dropbehind(struct folio *folio)
|
||||
{
|
||||
VM_BUG_ON_FOLIO(!folio_test_writeback(folio), folio);
|
||||
|
||||
@@ -1663,6 +1665,25 @@ void folio_end_writeback(struct folio *folio)
|
||||
folio_rotate_reclaimable(folio);
|
||||
}
|
||||
|
||||
if (__folio_end_writeback(folio))
|
||||
folio_wake_bit(folio, PG_writeback);
|
||||
|
||||
acct_reclaim_writeback(folio);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(folio_end_writeback_no_dropbehind);
|
||||
|
||||
/**
|
||||
* folio_end_writeback - End writeback against a folio.
|
||||
* @folio: The folio.
|
||||
*
|
||||
* The folio must actually be under writeback.
|
||||
*
|
||||
* Context: May be called from process or interrupt context.
|
||||
*/
|
||||
void folio_end_writeback(struct folio *folio)
|
||||
{
|
||||
VM_BUG_ON_FOLIO(!folio_test_writeback(folio), folio);
|
||||
|
||||
/*
|
||||
* Writeback does not hold a folio reference of its own, relying
|
||||
* on truncation to wait for the clearing of PG_writeback.
|
||||
@@ -1670,11 +1691,8 @@ void folio_end_writeback(struct folio *folio)
|
||||
* reused before the folio_wake_bit().
|
||||
*/
|
||||
folio_get(folio);
|
||||
if (__folio_end_writeback(folio))
|
||||
folio_wake_bit(folio, PG_writeback);
|
||||
|
||||
filemap_end_dropbehind_write(folio);
|
||||
acct_reclaim_writeback(folio);
|
||||
folio_end_writeback_no_dropbehind(folio);
|
||||
folio_end_dropbehind(folio);
|
||||
folio_put(folio);
|
||||
}
|
||||
EXPORT_SYMBOL(folio_end_writeback);
|
||||
|
||||
@@ -101,6 +101,20 @@ config SUNRPC_DEBUG
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config SUNRPC_DEBUG_TRACE
|
||||
bool "RPC: Send dfprintk() output to the trace buffer"
|
||||
depends on SUNRPC_DEBUG && TRACING
|
||||
default n
|
||||
help
|
||||
dprintk() output can be voluminous, which can overwhelm the
|
||||
kernel's logging facility as it must be sent to the console.
|
||||
This option causes dprintk() output to go to the trace buffer
|
||||
instead of the kernel log.
|
||||
|
||||
This will cause warnings about trace_printk() being used to be
|
||||
logged at boot time, so say N unless you are debugging a problem
|
||||
with sunrpc-based clients or services.
|
||||
|
||||
config SUNRPC_XPRT_RDMA
|
||||
tristate "RPC-over-RDMA transport"
|
||||
depends on SUNRPC && INFINIBAND && INFINIBAND_ADDR_TRANS
|
||||
|
||||
@@ -794,12 +794,12 @@ int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp,
|
||||
struct gssx_res_accept_sec_context *res = data;
|
||||
u32 value_follows;
|
||||
int err;
|
||||
struct page *scratch;
|
||||
struct folio *scratch;
|
||||
|
||||
scratch = alloc_page(GFP_KERNEL);
|
||||
scratch = folio_alloc(GFP_KERNEL, 0);
|
||||
if (!scratch)
|
||||
return -ENOMEM;
|
||||
xdr_set_scratch_page(xdr, scratch);
|
||||
xdr_set_scratch_folio(xdr, scratch);
|
||||
|
||||
/* res->status */
|
||||
err = gssx_dec_status(xdr, &res->status);
|
||||
@@ -844,6 +844,6 @@ int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp,
|
||||
err = gssx_dec_option_array(xdr, &res->options);
|
||||
|
||||
out_free:
|
||||
__free_page(scratch);
|
||||
folio_put(scratch);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1074,7 +1074,6 @@ int rpc_malloc(struct rpc_task *task)
|
||||
rqst->rq_rbuffer = (char *)rqst->rq_buffer + rqst->rq_callsize;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpc_malloc);
|
||||
|
||||
/**
|
||||
* rpc_free - free RPC buffer resources allocated via rpc_malloc
|
||||
@@ -1095,7 +1094,6 @@ void rpc_free(struct rpc_task *task)
|
||||
else
|
||||
kfree(buf);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpc_free);
|
||||
|
||||
/*
|
||||
* Creation and deletion of RPC task structures
|
||||
|
||||
@@ -86,7 +86,7 @@ xdr_partial_copy_from_skb(struct xdr_buf *xdr, struct xdr_skb_reader *desc)
|
||||
/* ACL likes to be lazy in allocating pages - ACLs
|
||||
* are small by default but can get huge. */
|
||||
if ((xdr->flags & XDRBUF_SPARSE_PAGES) && *ppage == NULL) {
|
||||
*ppage = alloc_page(GFP_NOWAIT | __GFP_NOWARN);
|
||||
*ppage = alloc_page(GFP_NOWAIT);
|
||||
if (unlikely(*ppage == NULL)) {
|
||||
if (copied == 0)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -352,7 +352,7 @@ static int svc_pool_map_get_node(unsigned int pidx)
|
||||
if (m->mode == SVC_POOL_PERNODE)
|
||||
return m->pool_to[pidx];
|
||||
}
|
||||
return NUMA_NO_NODE;
|
||||
return numa_mem_id();
|
||||
}
|
||||
/*
|
||||
* Set the given thread's cpus_allowed mask so that it
|
||||
@@ -436,7 +436,6 @@ void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net)
|
||||
svc_unregister(serv, net);
|
||||
rpcb_put_local(net);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_rpcb_cleanup);
|
||||
|
||||
static int svc_uses_rpcbind(struct svc_serv *serv)
|
||||
{
|
||||
@@ -670,8 +669,8 @@ svc_rqst_free(struct svc_rqst *rqstp)
|
||||
folio_batch_release(&rqstp->rq_fbatch);
|
||||
kfree(rqstp->rq_bvec);
|
||||
svc_release_buffer(rqstp);
|
||||
if (rqstp->rq_scratch_page)
|
||||
put_page(rqstp->rq_scratch_page);
|
||||
if (rqstp->rq_scratch_folio)
|
||||
folio_put(rqstp->rq_scratch_folio);
|
||||
kfree(rqstp->rq_resp);
|
||||
kfree(rqstp->rq_argp);
|
||||
kfree(rqstp->rq_auth_data);
|
||||
@@ -692,8 +691,8 @@ svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node)
|
||||
rqstp->rq_server = serv;
|
||||
rqstp->rq_pool = pool;
|
||||
|
||||
rqstp->rq_scratch_page = alloc_pages_node(node, GFP_KERNEL, 0);
|
||||
if (!rqstp->rq_scratch_page)
|
||||
rqstp->rq_scratch_folio = __folio_alloc_node(GFP_KERNEL, 0, node);
|
||||
if (!rqstp->rq_scratch_folio)
|
||||
goto out_enomem;
|
||||
|
||||
rqstp->rq_argp = kmalloc_node(serv->sv_xdrsize, GFP_KERNEL, node);
|
||||
|
||||
@@ -1102,6 +1102,7 @@ static void svc_clean_up_xprts(struct svc_serv *serv, struct net *net)
|
||||
* svc_xprt_destroy_all - Destroy transports associated with @serv
|
||||
* @serv: RPC service to be shut down
|
||||
* @net: target network namespace
|
||||
* @unregister: true if it is OK to unregister the destroyed xprts
|
||||
*
|
||||
* Server threads may still be running (especially in the case where the
|
||||
* service is still running in other network namespaces).
|
||||
@@ -1114,7 +1115,8 @@ static void svc_clean_up_xprts(struct svc_serv *serv, struct net *net)
|
||||
* threads, we may need to wait a little while and then check again to
|
||||
* see if they're done.
|
||||
*/
|
||||
void svc_xprt_destroy_all(struct svc_serv *serv, struct net *net)
|
||||
void svc_xprt_destroy_all(struct svc_serv *serv, struct net *net,
|
||||
bool unregister)
|
||||
{
|
||||
int delay = 0;
|
||||
|
||||
@@ -1124,6 +1126,9 @@ void svc_xprt_destroy_all(struct svc_serv *serv, struct net *net)
|
||||
svc_clean_up_xprts(serv, net);
|
||||
msleep(delay++);
|
||||
}
|
||||
|
||||
if (unregister)
|
||||
svc_rpcb_cleanup(serv, net);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_xprt_destroy_all);
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ rpcrdma_alloc_sparse_pages(struct xdr_buf *buf)
|
||||
ppages = buf->pages + (buf->page_base >> PAGE_SHIFT);
|
||||
while (len > 0) {
|
||||
if (!*ppages)
|
||||
*ppages = alloc_page(GFP_NOWAIT | __GFP_NOWARN);
|
||||
*ppages = alloc_page(GFP_NOWAIT);
|
||||
if (!*ppages)
|
||||
return -ENOBUFS;
|
||||
ppages++;
|
||||
|
||||
Reference in New Issue
Block a user