Merge tag 'nfs-for-7.4-1' of git://git.linux-nfs.org/projects/anna/linux-nfs

Pull nfs client updates from Anna Schumaker:
 "New Features:
   - Modernize the direct I/O path
   - Size the LAYOUTGET reply buffer for wide flexfiles layouts
   - Flexfiles device notifications and caching for wide striped layouts
   - isolate mTLS client credentials by network namespace

  Bugfixes:
   - Various localio, flexfiles, xprtrdma, and TLS fixes
   - Merged bugfixes from Trond that he was unable to send during the 7.3-rc cycle

  Other Cleanups and Improvements:
   - Split up pNFS block layout and SCSI layout support"

* tag 'nfs-for-7.4-1' of git://git.linux-nfs.org/projects/anna/linux-nfs: (68 commits)
  nfs/localio: pin UUIDs during global invalidation
  NFSv4/flexfiles: fail I/O to the data server once a lock is lost
  pNFS/flexfiles: don't report an interrupted DS connect as a DS error
  pNFS/flexfiles: make pg_init retry loops killable
  nfsv4/pnfs: use the MDS authflavor for data server connections
  xprtrdma: trim head iovec when the payload arrives via a Write chunk
  nfs: set gencount on protocol-specific remove
  xprtrdma: prevent disconnect feedback during endpoint teardown
  NFS: add a key type that reveals the namespace .nfs keyring serial number
  NFS: name the namespace .nfs keyring in the x509 handshake
  SUNRPC: pass a keyring serial number to the TLS handshake
  NFS: allocate the .nfs keyring per network namespace
  NFS: name the init_nfs_fs() error labels
  NFS: return DENIED in decode_lock_denied if we cannot decode owner
  pNFS/flexfiles: don't reset to MDS for v4 error when previous layout forbid it
  pNFS/flexfiles: don't read through MDS when previous layout forbid it
  pNFS/flexfiles: honor FF_FLAGS_NO_IO_THRU_MDS over the mdsthreshold hint
  NFS/localio: report the stability a DIO WRITE actually has
  NFS/localio: detect a short read or write before the iterator has moved
  NFS/localio: fix nfs_local_dio_misaligned tracepoint
  ...
This commit is contained in:
Linus Torvalds committed 2026-10-25 15:42:16 -10:00
commit 9e089e670e
47 files changed
+2135 -617

No files matched your search

+1
View File
@@ -7,6 +7,7 @@ NFS
:maxdepth: 1
client-identifier
keyring
exporting
localio
pnfs
+69
View File
@@ -0,0 +1,69 @@
.. SPDX-License-Identifier: GPL-2.0
====================================
The per-namespace NFS client keyring
====================================
The NFS client holds one keyring, named ".nfs", per network
namespace. An xprtsec=mtls mount presents the client certificate and
private key named by its cert_serial= and privkey_serial= mount
options, and the handshake links the mount's namespace .nfs keyring
into tlshd before tlshd reads those keys. Keys placed on the .nfs
keyring therefore need not grant user read permission: tlshd reaches
them as a possessor, and a tlshd in another network namespace never
possesses them.
The keyring is owned by the owner of the network namespace's user
namespace, with KEY_POS_ALL and KEY_USR_ALL less SETATTR. It is not
charged to any quota. Keys added to it by userspace are charged to
the user that adds them.
Finding the keyring serial number
=================================
The .nfs keyring is not linked into any keyring that userspace
possesses, so a serial number is the only way to name it. The NFS
client registers a key type, "nfs_keyring", whose sole purpose is to
hand that serial number out. Its request_key handler runs in the
caller's context and does not upcall to /sbin/request-key.
To obtain the serial number, request a key of type "nfs_keyring" with
the description ".nfs" and read its payload::
id=$(keyctl request2 nfs_keyring .nfs "" @s)
serial=$(keyctl print $id)
The contract of the key type is:
* The description is the string ".nfs". Any other description is
rejected with EINVAL before a key is allocated.
* The callout info must be present. request_key() with a NULL
callout_info never invokes a handler and returns ENOKEY when no
matching key exists, so use request_key() with an empty string, or
``keyctl request2`` rather than ``keyctl request``. The content of
the callout info is ignored.
* The payload is the keyring serial number as decimal ASCII digits
with no terminating NUL. The return value of keyctl_read() gives
the length; a buffer of 12 bytes is sufficient.
* add_key() with this type accepts only that payload. A key carrying
any other value is rejected with EINVAL, so a key found in the
caller's keyrings always holds the serial number of the caller's
namespace.
* The key type carries KEY_TYPE_NET_DOMAIN. A key instantiated in one
network namespace does not answer a request made in another, so a
process that keeps its session keyring across setns() receives the
serial number of the namespace it is in at the time of the request.
The serial number is not a secret. The keyring's own permissions
decide what a caller can do with it.
Provisioning credentials
========================
Add the client certificate and private key to the .nfs keyring as
keys that grant no user read permission, then pass their serial
numbers as cert_serial= and privkey_serial= mount options.
+16 -1
View File
@@ -123,8 +123,23 @@ config PNFS_FILE_LAYOUT
config PNFS_BLOCK
tristate
depends on NFS_V4 && BLK_DEV_DM
config PNFS_BLOCK_LAYOUT
bool "NFS client support for pNFS block layouts"
depends on NFS_V4 && BLOCK && BLK_DEV_DM
select PNFS_BLOCK
help
Enable support for the pNFS block-volume layout type (RFC 5663).
If unsure, say N.
config PNFS_SCSI_LAYOUT
bool "NFS client support for pNFS SCSI layouts"
depends on NFS_V4 && BLOCK
default NFS_V4
select PNFS_BLOCK
help
Enable suport for the pNFS SCSI layout type (RFC 8154).
config PNFS_FLEXFILE_LAYOUT
tristate
+2 -1
View File
@@ -4,4 +4,5 @@
#
obj-$(CONFIG_PNFS_BLOCK) += blocklayoutdriver.o
blocklayoutdriver-y += blocklayout.o dev.o extent_tree.o rpc_pipefs.o
blocklayoutdriver-y += blocklayout.o dev.o extent_tree.o
blocklayoutdriver-$(CONFIG_PNFS_BLOCK_LAYOUT) += rpc_pipefs.o
+84 -35
View File
@@ -470,18 +470,6 @@ static struct pnfs_layout_hdr *__bl_alloc_layout_hdr(struct inode *inode,
return &bl->bl_layout;
}
static struct pnfs_layout_hdr *bl_alloc_layout_hdr(struct inode *inode,
gfp_t gfp_flags)
{
return __bl_alloc_layout_hdr(inode, gfp_flags, false);
}
static struct pnfs_layout_hdr *sl_alloc_layout_hdr(struct inode *inode,
gfp_t gfp_flags)
{
return __bl_alloc_layout_hdr(inode, gfp_flags, true);
}
static void bl_free_lseg(struct pnfs_layout_segment *lseg)
{
dprintk("%s enter\n", __func__);
@@ -859,7 +847,8 @@ bl_pg_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)
if (pgio->pg_lseg &&
test_bit(NFS_LSEG_UNAVAILABLE, &pgio->pg_lseg->pls_flags)) {
pnfs_error_mark_layout_for_return(pgio->pg_inode, pgio->pg_lseg);
pnfs_error_mark_layout_for_return(pgio->pg_inode, pgio->pg_lseg,
NULL);
pnfs_set_lo_fail(pgio->pg_lseg);
nfs_pageio_reset_read_mds(pgio);
}
@@ -921,7 +910,8 @@ bl_pg_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)
if (pgio->pg_lseg &&
test_bit(NFS_LSEG_UNAVAILABLE, &pgio->pg_lseg->pls_flags)) {
pnfs_error_mark_layout_for_return(pgio->pg_inode, pgio->pg_lseg);
pnfs_error_mark_layout_for_return(pgio->pg_inode, pgio->pg_lseg,
NULL);
pnfs_set_lo_fail(pgio->pg_lseg);
nfs_pageio_reset_write_mds(pgio);
}
@@ -954,6 +944,13 @@ static const struct nfs_pageio_ops bl_pg_write_ops = {
.pg_cleanup = pnfs_generic_pg_cleanup,
};
#ifdef CONFIG_PNFS_BLOCK_LAYOUT
static struct pnfs_layout_hdr *bl_alloc_layout_hdr(struct inode *inode,
gfp_t gfp_flags)
{
return __bl_alloc_layout_hdr(inode, gfp_flags, false);
}
static struct pnfs_layoutdriver_type blocklayout_type = {
.id = LAYOUT_BLOCK_VOLUME,
.name = "LAYOUT_BLOCK_VOLUME",
@@ -978,6 +975,48 @@ static struct pnfs_layoutdriver_type blocklayout_type = {
.sync = pnfs_generic_sync,
};
static int __init pnfs_register_blocklayout(void)
{
int ret;
ret = bl_init_pipefs();
if (ret)
return ret;
ret = pnfs_register_layoutdriver(&blocklayout_type);
if (ret) {
bl_cleanup_pipefs();
return ret;
}
return 0;
}
static void __exit pnfs_unregister_blocklayout(void)
{
pnfs_unregister_layoutdriver(&blocklayout_type);
bl_cleanup_pipefs();
}
MODULE_ALIAS("nfs-layouttype4-3");
#else
static int __init pnfs_register_blocklayout(void)
{
return 0;
}
static void __exit pnfs_unregister_blocklayout(void)
{
}
#endif /* CONFIG_PNFS_BLOCK_LAYOUT */
#ifdef CONFIG_PNFS_SCSI_LAYOUT
static struct pnfs_layout_hdr *sl_alloc_layout_hdr(struct inode *inode,
gfp_t gfp_flags)
{
return __bl_alloc_layout_hdr(inode, gfp_flags, true);
}
static struct pnfs_layoutdriver_type scsilayout_type = {
.id = LAYOUT_SCSI,
.name = "LAYOUT_SCSI",
@@ -1002,6 +1041,28 @@ static struct pnfs_layoutdriver_type scsilayout_type = {
.sync = pnfs_generic_sync,
};
static int __init pnfs_register_scsilayout(void)
{
return pnfs_register_layoutdriver(&scsilayout_type);
}
static void __exit pnfs_unregister_scsilayout(void)
{
pnfs_unregister_layoutdriver(&scsilayout_type);
}
MODULE_ALIAS("nfs-layouttype4-5");
#else
static int __init pnfs_register_scsilayout(void)
{
return 0;
}
static void __exit pnfs_unregister_scsilayout(void)
{
}
#endif /* CONFIG_PNFS_SCSI_LAYOUT */
static int __init nfs4blocklayout_init(void)
{
@@ -1009,25 +1070,17 @@ static int __init nfs4blocklayout_init(void)
dprintk("%s: NFSv4 Block Layout Driver Registering...\n", __func__);
ret = bl_init_pipefs();
ret = pnfs_register_blocklayout();
if (ret)
goto out;
return ret;
ret = pnfs_register_layoutdriver(&blocklayout_type);
if (ret)
goto out_cleanup_pipe;
ret = pnfs_register_scsilayout();
if (ret) {
pnfs_unregister_blocklayout();
return ret;
}
ret = pnfs_register_layoutdriver(&scsilayout_type);
if (ret)
goto out_unregister_block;
return 0;
out_unregister_block:
pnfs_unregister_layoutdriver(&blocklayout_type);
out_cleanup_pipe:
bl_cleanup_pipefs();
out:
return ret;
}
static void __exit nfs4blocklayout_exit(void)
@@ -1035,13 +1088,9 @@ static void __exit nfs4blocklayout_exit(void)
dprintk("%s: NFSv4 Block Layout Driver Unregistering...\n",
__func__);
pnfs_unregister_layoutdriver(&scsilayout_type);
pnfs_unregister_layoutdriver(&blocklayout_type);
bl_cleanup_pipefs();
pnfs_unregister_scsilayout();
pnfs_unregister_blocklayout();
}
MODULE_ALIAS("nfs-layouttype4-3");
MODULE_ALIAS("nfs-layouttype4-5");
module_init(nfs4blocklayout_init);
module_exit(nfs4blocklayout_exit);
+31 -1
View File
@@ -15,6 +15,7 @@
#define NFSDBG_FACILITY NFSDBG_PNFS_LD
#ifdef CONFIG_PNFS_SCSI_LAYOUT
static void bl_unregister_scsi(struct pnfs_block_dev *dev)
{
struct block_device *bdev = file_bdev(dev->bdev_file);
@@ -45,6 +46,16 @@ static bool bl_register_scsi(struct pnfs_block_dev *dev)
trace_bl_pr_key_reg(bdev, dev->pr_key);
return true;
}
#else
static void bl_unregister_scsi(struct pnfs_block_dev *dev)
{
}
static bool bl_register_scsi(struct pnfs_block_dev *dev)
{
return false;
}
#endif /* CONFIG_PNFS_SCSI_LAYOUT */
static void bl_unregister_dev(struct pnfs_block_dev *dev)
{
@@ -292,7 +303,7 @@ static int
bl_parse_deviceid(struct nfs_server *server, struct pnfs_block_dev *d,
struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask);
#ifdef CONFIG_PNFS_BLOCK_LAYOUT
static int
bl_parse_simple(struct nfs_server *server, struct pnfs_block_dev *d,
struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask)
@@ -320,7 +331,17 @@ bl_parse_simple(struct nfs_server *server, struct pnfs_block_dev *d,
file_bdev(bdev_file)->bd_disk->disk_name);
return 0;
}
#else
static int
bl_parse_simple(struct nfs_server *server, struct pnfs_block_dev *d,
struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask)
{
dprintk("unsupported volume type: %d\n", PNFS_BLOCK_VOLUME_SIMPLE);
return -EIO;
}
#endif /* CONFIG_PNFS_BLOCK_LAYOUT */
#ifdef CONFIG_PNFS_SCSI_LAYOUT
static bool
bl_validate_designator(struct pnfs_block_volume *v)
{
@@ -449,6 +470,15 @@ out_blkdev_put:
d->bdev_file = NULL;
return error;
}
#else
static int
bl_parse_scsi(struct nfs_server *server, struct pnfs_block_dev *d,
struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask)
{
dprintk("unsupported volume type: %d\n", PNFS_BLOCK_VOLUME_SCSI);
return -EIO;
}
#endif /* CONFIG_PNFS_SCSI_LAYOUT */
static int
bl_parse_slice(struct nfs_server *server, struct pnfs_block_dev *d,
+21 -2
View File
@@ -292,7 +292,7 @@ static u32 initiate_file_draining(struct nfs_client *clp,
switch (pnfs_mark_matching_lsegs_return(lo, &free_me_list,
&args->cbl_range,
be32_to_cpu(args->cbl_stateid.seqid),
args->cbl_layoutchanged)) {
args->cbl_layoutchanged, NULL)) {
case 0:
case -EBUSY:
/* There are layout segments that need to be returned */
@@ -391,7 +391,26 @@ __be32 nfs4_callback_devicenotify(void *argp, void *resp,
if (!ld)
continue;
}
nfs4_delete_deviceid(ld, cps->clp, &dev->cbd_dev_id);
/*
* Bump the epoch before touching the cache so a
* GETDEVICEINFO already in flight can detect that it
* predates the notification. A referenced DELETE may be
* racing revocation, so defer it to the state manager --
* this thread cannot issue fore-channel RPCs.
*/
nfs4_deviceid_bump_change_epoch(cps->clp);
if (dev->cbd_notify_type == NOTIFY_DEVICEID4_CHANGE) {
nfs4_delete_deviceid(ld, cps->clp, &dev->cbd_dev_id);
pnfs_layout_reresolve_deviceid_byclid(cps->clp, ld,
&dev->cbd_dev_id,
dev->cbd_immediate);
} else if (pnfs_layout_deviceid_referenced_byclid(cps->clp,
ld, &dev->cbd_dev_id)) {
pnfs_deviceid_delete_mark(cps->clp, ld,
&dev->cbd_dev_id);
} else {
nfs4_delete_deviceid(ld, cps->clp, &dev->cbd_dev_id);
}
}
pnfs_put_layoutdriver(ld);
out:
+8 -1
View File
@@ -271,6 +271,13 @@ __be32 decode_devicenotify_args(struct svc_rqst *rqstp,
if (n == 0)
goto out;
/* sanity check the count against the remaining stream */
if (n > xdr_stream_remaining(xdr) /
((4 * sizeof(uint32_t)) + NFS4_DEVICEID4_SIZE)) {
status = htonl(NFS4ERR_BADXDR);
goto out;
}
args->devs = kmalloc_objs(*args->devs, n);
if (!args->devs) {
status = htonl(NFS4ERR_DELAY);
@@ -312,7 +319,7 @@ __be32 decode_devicenotify_args(struct svc_rqst *rqstp,
memcpy(dev->cbd_dev_id.data, p, NFS4_DEVICEID4_SIZE);
p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE);
if (dev->cbd_layout_type == NOTIFY_DEVICEID4_CHANGE) {
if (dev->cbd_notify_type == NOTIFY_DEVICEID4_CHANGE) {
p = xdr_inline_decode(xdr, sizeof(uint32_t));
if (unlikely(p == NULL)) {
status = htonl(NFS4ERR_BADXDR);
+13 -4
View File
@@ -191,6 +191,13 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
clp->cl_principal = "*";
clp->cl_xprtsec = cl_init->xprtsec;
/*
* Every client in a namespace names the same keyring, so
* nfs_match_client() does not compare keyring_serial.
*/
if (clp->cl_xprtsec.policy == RPC_XPRTSEC_TLS_X509)
clp->cl_xprtsec.keyring_serial =
key_serial(nfs_net_keyring(clp->cl_net));
return clp;
error_cleanup:
@@ -256,7 +263,7 @@ static void pnfs_init_server(struct nfs_server *server)
*/
void nfs_free_client(struct nfs_client *clp)
{
nfs_localio_disable_client(clp);
nfs_localio_free_client(clp);
/* -EIO all pending I/O */
if (!IS_ERR(clp->cl_rpcclient))
@@ -549,7 +556,7 @@ int nfs_create_rpc_client(struct nfs_client *clp,
.version = clp->rpc_ops->version,
.authflavor = flavor,
.cred = cl_init->cred,
.xprtsec = cl_init->xprtsec,
.xprtsec = clp->cl_xprtsec,
.connect_timeout = cl_init->connect_timeout,
.reconnect_timeout = cl_init->reconnect_timeout,
};
@@ -1295,7 +1302,8 @@ void nfs_clients_init(struct net *net)
INIT_LIST_HEAD(&nn->nfs_volume_list);
#if IS_ENABLED(CONFIG_NFS_V4)
idr_init(&nn->cb_ident_idr);
INIT_LIST_HEAD(&nn->nfs4_data_server_cache);
for (int i = 0; i < NFS4_DS_CACHE_HASH_SIZE; i++)
INIT_HLIST_HEAD(&nn->nfs4_data_server_cache[i]);
spin_lock_init(&nn->nfs4_data_server_lock);
#endif /* CONFIG_NFS_V4 */
spin_lock_init(&nn->nfs_client_lock);
@@ -1315,7 +1323,8 @@ void nfs_clients_exit(struct net *net)
WARN_ON_ONCE(!list_empty(&nn->nfs_client_list));
WARN_ON_ONCE(!list_empty(&nn->nfs_volume_list));
#if IS_ENABLED(CONFIG_NFS_V4)
WARN_ON_ONCE(!list_empty(&nn->nfs4_data_server_cache));
for (int i = 0; i < NFS4_DS_CACHE_HASH_SIZE; i++)
WARN_ON_ONCE(!hlist_empty(&nn->nfs4_data_server_cache[i]));
#endif /* CONFIG_NFS_V4 */
}
+1 -2
View File
@@ -934,8 +934,7 @@ nfs_mark_return_if_closed_delegation(struct nfs_server *server,
{
struct inode *inode;
if (!list_empty_careful(&server->delegations_return) ||
test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags))
if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags))
return;
spin_lock(&delegation->lock);
inode = delegation->inode;
+5 -10
View File
@@ -1935,7 +1935,7 @@ static int nfs_dentry_delete(const struct dentry *dentry)
}
/* Ensure that we revalidate inode->i_nlink */
static void nfs_drop_nlink(struct inode *inode, unsigned long gencount)
void nfs_drop_nlink(struct inode *inode, unsigned long gencount)
{
struct nfs_inode *nfsi = NFS_I(inode);
@@ -1949,6 +1949,7 @@ static void nfs_drop_nlink(struct inode *inode, unsigned long gencount)
NFS_INO_INVALID_NLINK);
spin_unlock(&inode->i_lock);
}
EXPORT_SYMBOL_GPL(nfs_drop_nlink);
/*
* Called when the dentry loses inode.
@@ -2490,6 +2491,8 @@ struct dentry *nfs_mkdir(const struct mnt_idmap *idmap, struct inode *dir,
trace_nfs_mkdir_enter(dir, dentry);
ret = NFS_PROTO(dir)->mkdir(dir, dentry, &attr);
trace_nfs_mkdir_exit(dir, dentry, PTR_ERR_OR_ZERO(ret));
if (IS_ERR(ret))
d_drop(dentry);
return ret;
}
EXPORT_SYMBOL_GPL(nfs_mkdir);
@@ -2554,7 +2557,6 @@ EXPORT_SYMBOL_GPL(nfs_rmdir);
static int nfs_safe_remove(struct dentry *dentry)
{
struct inode *dir = d_inode(dentry->d_parent);
struct inode *inode = d_inode(dentry);
int error = -EBUSY;
dfprintk(VFS, "NFS: safe_remove(%pd2)\n", dentry);
@@ -2566,14 +2568,7 @@ static int nfs_safe_remove(struct dentry *dentry)
}
trace_nfs_remove_enter(dir, dentry);
if (inode != NULL) {
unsigned long gencount = READ_ONCE(NFS_I(inode)->attr_gencount);
error = NFS_PROTO(dir)->remove(dir, dentry);
if (error == 0)
nfs_drop_nlink(inode, gencount);
} else
error = NFS_PROTO(dir)->remove(dir, dentry);
error = NFS_PROTO(dir)->remove(dir, dentry);
if (error == -ENOENT)
nfs_dentry_handle_enoent(dentry);
trace_nfs_remove_exit(dir, dentry, error);
+70 -69
View File
@@ -145,11 +145,55 @@ static void nfs_direct_file_adjust_size_locked(struct inode *inode,
}
}
static void nfs_direct_release_pages(struct page **pages, unsigned int npages)
static void nfs_direct_release_pages(struct page **pages, unsigned int npages,
bool pinned)
{
unsigned int i;
for (i = 0; i < npages; i++)
put_page(pages[i]);
if (pinned)
unpin_user_pages(pages, npages);
}
static ssize_t nfs_direct_extract_pages(struct nfs_direct_req *dreq,
struct iov_iter *iter,
size_t size, loff_t *pos,
struct list_head *list)
{
bool pinned = iov_iter_extract_will_pin(iter);
struct page **pagevec = NULL;
ssize_t result, bytes = 0;
int err = 0;
unsigned int npages, i;
size_t pgbase;
result = iov_iter_extract_pages(iter, &pagevec, size, ~0U, 0, &pgbase);
if (result <= 0)
return result;
npages = (result + pgbase + PAGE_SIZE - 1) >> PAGE_SHIFT;
for (i = 0; i < npages; i++) {
struct nfs_page *req;
unsigned int req_len = min_t(size_t, result - bytes, PAGE_SIZE - pgbase);
req = nfs_page_create_from_page(dreq->ctx, pagevec[i],
pinned, pgbase, *pos,
req_len);
if (IS_ERR(req)) {
err = PTR_ERR(req);
break;
}
list_add_tail(&req->wb_list, list);
pgbase = 0;
bytes += req_len;
*pos += req_len;
}
if (i < npages) {
iov_iter_revert(iter, result - bytes);
nfs_direct_release_pages(pagevec + i, npages - i, pinned);
}
kvfree(pagevec);
return bytes ? bytes : err;
}
void nfs_init_cinfo_from_dreq(struct nfs_commit_info *cinfo,
@@ -288,13 +332,7 @@ out_put:
static void nfs_read_sync_pgio_error(struct list_head *head, int error)
{
struct nfs_page *req;
while (!list_empty(head)) {
req = nfs_list_entry(head->next);
nfs_list_remove_request(req);
nfs_release_request(req);
}
nfs_release_request_list(head);
}
static void nfs_direct_pgio_init(struct nfs_pgio_header *hdr)
@@ -326,6 +364,7 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
ssize_t result = -EINVAL;
size_t requested_bytes = 0;
size_t rsize = max_t(size_t, NFS_SERVER(inode)->rsize, PAGE_SIZE);
LIST_HEAD(nfs_page_list);
nfs_pageio_init_read(&desc, dreq->inode, false,
&nfs_direct_read_completion_ops);
@@ -334,40 +373,23 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
inode_dio_begin(inode);
while (iov_iter_count(iter)) {
struct page **pagevec;
size_t bytes;
size_t pgbase;
unsigned npages, i;
result = iov_iter_get_pages_alloc2(iter, &pagevec,
rsize, &pgbase);
result = nfs_direct_extract_pages(dreq, iter, rsize, &pos, &nfs_page_list);
if (result < 0)
break;
bytes = result;
npages = (result + pgbase + PAGE_SIZE - 1) / PAGE_SIZE;
for (i = 0; i < npages; i++) {
struct nfs_page *req;
unsigned int req_len = min_t(size_t, bytes, PAGE_SIZE - pgbase);
/* XXX do we need to do the eof zeroing found in async_filler? */
req = nfs_page_create_from_page(dreq->ctx, pagevec[i],
pgbase, pos, req_len);
if (IS_ERR(req)) {
result = PTR_ERR(req);
break;
}
while (!list_empty(&nfs_page_list)) {
struct nfs_page *req = nfs_list_entry(nfs_page_list.next);
size_t req_len = req->wb_bytes;
nfs_list_remove_request(req);
if (!nfs_pageio_add_request(&desc, req)) {
result = desc.pg_error;
nfs_release_request(req);
nfs_release_request_list(&nfs_page_list);
break;
}
pgbase = 0;
bytes -= req_len;
requested_bytes += req_len;
pos += req_len;
}
nfs_direct_release_pages(pagevec, npages);
kvfree(pagevec);
if (result < 0)
break;
}
@@ -858,6 +880,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
ssize_t result = 0;
size_t requested_bytes = 0;
size_t wsize = max_t(size_t, NFS_SERVER(inode)->wsize, PAGE_SIZE);
LIST_HEAD(nfs_page_list);
bool defer = false;
trace_nfs_direct_write_schedule_iovec(dreq);
@@ -870,53 +893,32 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
NFS_I(inode)->write_io += iov_iter_count(iter);
while (iov_iter_count(iter)) {
struct page **pagevec;
size_t bytes;
size_t pgbase;
unsigned npages, i;
result = iov_iter_get_pages_alloc2(iter, &pagevec,
wsize, &pgbase);
result = nfs_direct_extract_pages(dreq, iter, wsize, &pos, &nfs_page_list);
if (result < 0)
break;
bytes = result;
npages = (result + pgbase + PAGE_SIZE - 1) / PAGE_SIZE;
for (i = 0; i < npages; i++) {
struct nfs_page *req;
unsigned int req_len = min_t(size_t, bytes, PAGE_SIZE - pgbase);
req = nfs_page_create_from_page(dreq->ctx, pagevec[i],
pgbase, pos, req_len);
if (IS_ERR(req)) {
result = PTR_ERR(req);
break;
}
if (desc.pg_error < 0) {
nfs_free_request(req);
result = desc.pg_error;
break;
}
pgbase = 0;
bytes -= req_len;
requested_bytes += req_len;
pos += req_len;
while (!list_empty(&nfs_page_list)) {
struct nfs_page *req = nfs_list_entry(nfs_page_list.next);
size_t req_len = req->wb_bytes;
nfs_list_remove_request(req);
if (defer) {
nfs_mark_request_commit(req, NULL, &cinfo, 0);
requested_bytes += req_len;
continue;
}
nfs_lock_request(req);
if (nfs_pageio_add_request(&desc, req))
if (nfs_pageio_add_request(&desc, req)) {
requested_bytes += req_len;
continue;
}
/* Exit on hard errors */
if (desc.pg_error < 0 && desc.pg_error != -EAGAIN) {
result = desc.pg_error;
nfs_unlock_and_release_request(req);
nfs_release_request_list(&nfs_page_list);
break;
}
@@ -927,11 +929,10 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
spin_unlock(&dreq->lock);
nfs_unlock_request(req);
nfs_mark_request_commit(req, NULL, &cinfo, 0);
requested_bytes += req_len;
desc.pg_error = 0;
defer = true;
}
nfs_direct_release_pages(pagevec, npages);
kvfree(pagevec);
if (result < 0)
break;
}
+9 -8
View File
@@ -55,12 +55,13 @@ static loff_t
filelayout_get_dense_offset(struct nfs4_filelayout_segment *flseg,
loff_t offset)
{
u32 stripe_width = flseg->stripe_unit * flseg->dsaddr->stripe_count;
u64 stripe_width = (u64)flseg->stripe_unit *
flseg->dsaddr->stripe_count;
u64 stripe_no;
u32 rem;
offset -= flseg->pattern_offset;
stripe_no = div_u64(offset, stripe_width);
stripe_no = div64_u64(offset, stripe_width);
div_u64_rem(offset, flseg->stripe_unit, &rem);
return stripe_no * flseg->stripe_unit + rem;
@@ -186,7 +187,7 @@ static int filelayout_async_handle_error(struct rpc_task *task,
dprintk("%s DS connection error %d\n", __func__,
task->tk_status);
nfs4_mark_deviceid_unavailable(devid);
pnfs_error_mark_layout_for_return(inode, lseg);
pnfs_error_mark_layout_for_return(inode, lseg, NULL);
pnfs_set_lo_fail(lseg);
rpc_wake_up(&tbl->slot_tbl_waitq);
fallthrough;
@@ -796,7 +797,7 @@ filelayout_pg_test(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev,
unsigned int size;
u64 p_stripe, r_stripe;
u32 stripe_offset;
u64 segment_offset = pgio->pg_lseg->pls_range.offset;
u64 pattern_offset = FILELAYOUT_LSEG(pgio->pg_lseg)->pattern_offset;
u32 stripe_unit = FILELAYOUT_LSEG(pgio->pg_lseg)->stripe_unit;
/* calls nfs_generic_pg_test */
@@ -808,8 +809,8 @@ filelayout_pg_test(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev,
/* see if req and prev are in the same stripe */
if (prev) {
p_stripe = (u64)req_offset(prev) - segment_offset;
r_stripe = (u64)req_offset(req) - segment_offset;
p_stripe = (u64)req_offset(prev) - pattern_offset;
r_stripe = (u64)req_offset(req) - pattern_offset;
do_div(p_stripe, stripe_unit);
do_div(r_stripe, stripe_unit);
@@ -818,7 +819,7 @@ filelayout_pg_test(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev,
}
/* calculate remaining bytes in the current stripe */
div_u64_rem((u64)req_offset(req) - segment_offset,
div_u64_rem((u64)req_offset(req) - pattern_offset,
stripe_unit,
&stripe_offset);
WARN_ON_ONCE(stripe_offset > stripe_unit);
@@ -856,7 +857,7 @@ fl_pnfs_update_layout(struct inode *ino,
status = filelayout_check_deviceid(lo, fl, gfp_flags);
if (status) {
pnfs_error_mark_layout_for_return(ino, lseg);
pnfs_error_mark_layout_for_return(ino, lseg, NULL);
pnfs_set_lo_fail(lseg);
pnfs_put_lseg(lseg);
lseg = NULL;
+3 -16
View File
@@ -177,27 +177,14 @@ nfs4_fl_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
trace_fl_getdevinfo(server, &pdev->dev_id, dsaddr->ds_list[i]->ds_remotestr);
/* If DS was already in cache, free ds addrs */
while (!list_empty(&dsaddrs)) {
da = list_first_entry(&dsaddrs,
struct nfs4_pnfs_ds_addr,
da_node);
list_del_init(&da->da_node);
kfree(da->da_remotestr);
kfree(da);
}
nfs4_pnfs_ds_addr_list_free(&dsaddrs);
}
folio_put(scratch);
return dsaddr;
out_err_drain_dsaddrs:
while (!list_empty(&dsaddrs)) {
da = list_first_entry(&dsaddrs, struct nfs4_pnfs_ds_addr,
da_node);
list_del_init(&da->da_node);
kfree(da->da_remotestr);
kfree(da);
}
nfs4_pnfs_ds_addr_list_free(&dsaddrs);
out_err_free_deviceid:
nfs4_fl_free_deviceid(dsaddr);
/* stripe_indicies was part of dsaddr */
@@ -280,7 +267,7 @@ nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx)
goto out_test_devid;
status = nfs4_pnfs_ds_connect(s, ds, devid, dataserver_timeo,
dataserver_retrans, 4,
dataserver_retrans, 0, 4,
s->nfs_client->cl_minorversion, true);
if (status) {
nfs4_mark_deviceid_unavailable(devid);
File diff suppressed because it is too large. Load diff
+26 -22
View File
@@ -79,7 +79,7 @@ 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;
struct nfs4_ff_layout_ds __rcu *mirror_ds;
u32 fh_versions_cnt;
struct nfs_fh *fh_versions;
nfs4_stateid stateid;
@@ -124,9 +124,20 @@ struct nfs4_flexfile_layout {
unsigned long flags;
};
/*
* Per-devinfo private data for a layoutstats/layoutreturn encode: the
* stripe the stats describe plus a reference on its device node so the
* node (and its DS addresses) stay valid until the XDR encode runs.
*/
struct nfs4_ff_layoutstat_priv {
struct nfs4_ff_layout_ds_stripe *dss_info;
struct nfs4_ff_layout_ds *mirror_ds;
};
struct nfs4_flexfile_layoutreturn_args {
struct list_head errors;
struct nfs42_layoutstat_devinfo devinfo[FF_LAYOUTSTATS_MAXDEV];
struct nfs4_ff_layoutstat_priv priv[FF_LAYOUTSTATS_MAXDEV];
unsigned int num_errors;
unsigned int num_dev;
struct page *pages[1];
@@ -162,20 +173,6 @@ FF_LAYOUT_COMP(struct pnfs_layout_segment *lseg, u32 idx)
return NULL;
}
static inline struct nfs4_deviceid_node *
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->dss[dss_id].mirror_ds;
if (!IS_ERR_OR_NULL(mirror_ds))
return &mirror_ds->id_node;
}
return NULL;
}
static inline u32
FF_LAYOUT_MIRROR_COUNT(struct pnfs_layout_segment *lseg)
{
@@ -207,9 +204,9 @@ 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, u32 dss_id)
nfs4_ff_layout_ds_version(const struct nfs4_ff_layout_ds *mirror_ds)
{
return mirror->dss[dss_id].mirror_ds->ds_versions[0].version;
return mirror_ds->ds_versions[0].version;
}
static inline u32
@@ -220,7 +217,7 @@ nfs4_ff_layout_calc_dss_id(const u64 stripe_unit, const u32 dss_count, const lof
if (dss_count == 1 || stripe_unit == 0)
return 0;
do_div(tmp, stripe_unit);
tmp = div64_u64(tmp, stripe_unit);
return do_div(tmp, dss_count);
}
@@ -232,6 +229,7 @@ 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,
const struct nfs4_deviceid_node *devid,
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);
@@ -245,23 +243,29 @@ struct nfs_fh *
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,
const struct nfs4_ff_layout_ds *mirror_ds,
u32 dss_id,
nfs4_stateid *stateid);
struct nfs4_ff_layout_ds *
ff_layout_get_mirror_ds(struct pnfs_layout_hdr *lo,
struct nfs4_ff_layout_mirror *mirror,
u32 dss_id);
struct nfs4_pnfs_ds *
nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg,
struct nfs4_ff_layout_mirror *mirror,
struct nfs4_ff_layout_ds *mirror_ds,
u32 dss_id,
bool fail_return);
enum nfs_opnum4 opnum);
struct rpc_clnt *
nfs4_ff_find_or_create_ds_client(struct nfs4_ff_layout_mirror *mirror,
nfs4_ff_find_or_create_ds_client(const struct nfs4_ff_layout_ds *mirror_ds,
struct nfs_client *ds_clp,
struct inode *inode,
u32 dss_id);
struct inode *inode);
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 nfs4_ff_layout_ds *mirror_ds,
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);
+133 -86
View File
@@ -20,6 +20,7 @@
static unsigned int dataserver_timeo = NFS_DEF_TCP_TIMEO;
static unsigned int dataserver_retrans;
static unsigned int dataserver_nconnect;
static bool ff_layout_has_available_ds(struct pnfs_layout_segment *lseg);
@@ -159,26 +160,13 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
goto out_err_drain_dsaddrs;
/* If DS was already in cache, free ds addrs */
while (!list_empty(&dsaddrs)) {
da = list_first_entry(&dsaddrs,
struct nfs4_pnfs_ds_addr,
da_node);
list_del_init(&da->da_node);
kfree(da->da_remotestr);
kfree(da);
}
nfs4_pnfs_ds_addr_list_free(&dsaddrs);
folio_put(scratch);
return new_ds;
out_err_drain_dsaddrs:
while (!list_empty(&dsaddrs)) {
da = list_first_entry(&dsaddrs, struct nfs4_pnfs_ds_addr,
da_node);
list_del_init(&da->da_node);
kfree(da->da_remotestr);
kfree(da);
}
nfs4_pnfs_ds_addr_list_free(&dsaddrs);
kfree(ds_versions);
out_scratch:
@@ -256,6 +244,7 @@ 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,
const struct nfs4_deviceid_node *devid,
u32 dss_id, u64 offset, u64 length, int status,
enum nfs_opnum4 opnum, gfp_t gfp_flags)
{
@@ -264,7 +253,7 @@ int ff_layout_track_ds_error(struct nfs4_flexfile_layout *flo,
if (status == 0)
return 0;
if (IS_ERR_OR_NULL(mirror->dss[dss_id].mirror_ds))
if (devid == NULL)
return -EINVAL;
dserr = kmalloc_obj(*dserr, gfp_flags);
@@ -277,8 +266,7 @@ int ff_layout_track_ds_error(struct nfs4_flexfile_layout *flo,
dserr->status = status;
dserr->opnum = opnum;
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);
memcpy(&dserr->deviceid, &devid->deviceid, NFS4_DEVICEID4_SIZE);
spin_lock(&flo->generic_hdr.plh_inode->i_lock);
ff_layout_add_ds_error_locked(flo, dserr);
@@ -317,50 +305,81 @@ 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,
const struct nfs4_ff_layout_ds *mirror_ds,
u32 dss_id,
nfs4_stateid *stateid)
{
if (nfs4_ff_layout_ds_version(mirror, dss_id) == 4)
if (nfs4_ff_layout_ds_version(mirror_ds) == 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,
u32 dss_id)
/*
* Resolve the stripe's deviceid on first use and pin the node on the
* mirror. Returns a node the caller must put, or an ERR_PTR.
*/
struct nfs4_ff_layout_ds *
ff_layout_get_mirror_ds(struct pnfs_layout_hdr *lo,
struct nfs4_ff_layout_mirror *mirror,
u32 dss_id)
{
struct nfs4_ff_layout_ds *mirror_ds, *old;
struct nfs4_deviceid_node *node;
if (mirror == NULL)
goto outerr;
if (mirror->dss[dss_id].mirror_ds == NULL) {
struct nfs4_deviceid_node *node;
struct nfs4_ff_layout_ds *mirror_ds = ERR_PTR(-ENODEV);
return ERR_PTR(-ENODEV);
node = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode),
&mirror->dss[dss_id].devid, lo->plh_lc_cred,
GFP_KERNEL);
if (node)
mirror_ds = FF_LAYOUT_MIRROR_DS(node);
retry:
rcu_read_lock();
mirror_ds = rcu_dereference(mirror->dss[dss_id].mirror_ds);
if (mirror_ds && !IS_ERR(mirror_ds) &&
atomic_inc_not_zero(&mirror_ds->id_node.ref)) {
rcu_read_unlock();
return mirror_ds;
}
rcu_read_unlock();
if (IS_ERR(mirror_ds))
return mirror_ds;
if (mirror_ds != NULL)
/* raced with a reset; the field is being re-pointed */
goto retry;
/* check for race with another call to this function */
if (cmpxchg(&mirror->dss[dss_id].mirror_ds, NULL, mirror_ds) &&
mirror_ds != ERR_PTR(-ENODEV))
nfs4_put_deviceid_node(node);
node = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode),
&mirror->dss[dss_id].devid, lo->plh_lc_cred,
GFP_KERNEL);
if (node) {
mirror_ds = FF_LAYOUT_MIRROR_DS(node);
/*
* Take the caller's reference before the pointer becomes
* visible below, so a concurrent reset of the installed
* pointer cannot drop the last reference under us.
*/
atomic_inc(&node->ref);
} else {
mirror_ds = ERR_PTR(-ENODEV);
}
if (IS_ERR(mirror->dss[dss_id].mirror_ds))
goto outerr;
/* check for race with another call to this function */
old = unrcu_pointer(cmpxchg(&mirror->dss[dss_id].mirror_ds,
NULL, RCU_INITIALIZER(mirror_ds)));
if (old == NULL)
return mirror_ds;
return true;
outerr:
return false;
/* lost the race; use the winner's node instead */
if (node) {
nfs4_put_deviceid_node(node);
nfs4_put_deviceid_node(node);
}
goto retry;
}
/**
* 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
* @mirror_ds: referenced device node for the stripe, from
* ff_layout_get_mirror_ds() (may be an ERR_PTR)
* @dss_id: DS stripe id to select stripe to use
* @fail_return: return layout on connect failure?
* @opnum: operation this connection is being prepared for
*
* Try to prepare a DS connection to accept an RPC call. This involves
* selecting a mirror to use and connecting the client to it if it's not
@@ -368,16 +387,19 @@ outerr:
*
* Since we only need a single functioning mirror to satisfy a read, we don't
* want to return the layout if there is one. For writes though, any down
* mirror should result in a LAYOUTRETURN. @fail_return is how we distinguish
* between the two cases.
* mirror should result in a LAYOUTRETURN. @opnum is how we distinguish
* between the two cases. On failure, @opnum is also reported in the tracked
* device error so that the server can tell which class of I/O the client
* was unable to send to the mirror.
*
* Returns a pointer to a connected DS object on success or NULL on failure.
* Returns a pointer to a connected DS object on success or an ERR_PTR value on failure.
*/
struct nfs4_pnfs_ds *
nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg,
struct nfs4_ff_layout_mirror *mirror,
struct nfs4_ff_layout_ds *mirror_ds,
u32 dss_id,
bool fail_return)
enum nfs_opnum4 opnum)
{
struct nfs4_pnfs_ds *ds;
struct inode *ino = lseg->pls_layout->plh_inode;
@@ -385,10 +407,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, dss_id))
if (IS_ERR_OR_NULL(mirror_ds))
goto noconnect;
ds = mirror->dss[dss_id].mirror_ds->ds;
ds = mirror_ds->ds;
if (READ_ONCE(ds->ds_clp))
goto out;
/* matching smp_wmb() in _nfs4_pnfs_v3/4_ds_connect */
@@ -397,11 +419,12 @@ 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->dss[dss_id].mirror_ds->id_node,
status = nfs4_pnfs_ds_connect(s, ds, &mirror_ds->id_node,
dataserver_timeo, dataserver_retrans,
mirror->dss[dss_id].mirror_ds->ds_versions[0].version,
mirror->dss[dss_id].mirror_ds->ds_versions[0].minor_version,
mirror->dss[dss_id].mirror_ds->ds_versions[0].tightly_coupled);
dataserver_nconnect,
mirror_ds->ds_versions[0].version,
mirror_ds->ds_versions[0].minor_version,
mirror_ds->ds_versions[0].tightly_coupled);
/* connect success, check rsize/wsize limit */
if (!status) {
@@ -414,20 +437,29 @@ 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->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;
if (mirror_ds->ds_versions[0].rsize > max_payload)
mirror_ds->ds_versions[0].rsize = max_payload;
if (mirror_ds->ds_versions[0].wsize > max_payload)
mirror_ds->ds_versions[0].wsize = max_payload;
goto out;
}
/* The connect was interrupted by a signal: this is not a DS error */
if (status == -ERESTARTSYS || status == -EINTR) {
ds = ERR_PTR(status);
goto out;
}
noconnect:
ff_layout_track_ds_error(FF_LAYOUT_FROM_HDR(lseg->pls_layout),
mirror, dss_id, lseg->pls_range.offset,
mirror,
IS_ERR_OR_NULL(mirror_ds) ?
NULL : &mirror_ds->id_node,
dss_id, lseg->pls_range.offset,
lseg->pls_range.length, NFS4ERR_NXIO,
OP_ILLEGAL, GFP_NOIO);
opnum, GFP_NOIO);
ff_layout_send_layouterror(lseg);
if (fail_return || !ff_layout_has_available_ds(lseg))
pnfs_error_mark_layout_for_return(ino, lseg);
if (opnum != OP_READ || !ff_layout_has_available_ds(lseg))
pnfs_error_mark_layout_for_return(ino, lseg,
&mirror->dss[dss_id].devid);
ds = ERR_PTR(status);
out:
return ds;
@@ -437,11 +469,12 @@ 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 nfs4_ff_layout_ds *mirror_ds,
u32 dss_id)
{
const struct cred *cred;
if (mirror && !mirror->dss[dss_id].mirror_ds->ds_versions[0].tightly_coupled) {
if (mirror && !mirror_ds->ds_versions[0].tightly_coupled) {
cred = ff_layout_get_mirror_cred(mirror, range->iomode, dss_id);
if (!cred)
cred = get_cred(mdscred);
@@ -453,20 +486,18 @@ ff_layout_get_ds_cred(struct nfs4_ff_layout_mirror *mirror,
/**
* nfs4_ff_find_or_create_ds_client - Find or create a DS rpc client
* @mirror: pointer to the mirror
* @mirror_ds: device node for the stripe
* @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,
u32 dss_id)
nfs4_ff_find_or_create_ds_client(const struct nfs4_ff_layout_ds *mirror_ds,
struct nfs_client *ds_clp, struct inode *inode)
{
switch (mirror->dss[dss_id].mirror_ds->ds_versions[0].version) {
switch (mirror_ds->ds_versions[0].version) {
case 3:
/* For NFSv3 DS, flavor is set when creating DS connections */
return ds_clp->cl_rpcclient;
@@ -571,49 +602,60 @@ unsigned int ff_layout_fetch_ds_ioerr(struct pnfs_layout_hdr *lo,
static bool ff_read_layout_has_available_ds(struct pnfs_layout_segment *lseg)
{
struct nfs4_ff_layout_mirror *mirror;
struct nfs4_deviceid_node *devid;
struct nfs4_ff_layout_ds *mirror_ds;
bool ret = false;
u32 idx, dss_id;
rcu_read_lock();
for (idx = 0; idx < FF_LAYOUT_MIRROR_COUNT(lseg); idx++) {
mirror = FF_LAYOUT_COMP(lseg, idx);
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->dss[dss_id].mirror_ds))
mirror_ds = rcu_dereference(mirror->dss[dss_id].mirror_ds);
if (!mirror_ds) {
ret = true;
goto out;
}
if (IS_ERR(mirror_ds))
continue;
devid = &mirror->dss[dss_id].mirror_ds->id_node;
if (!nfs4_test_deviceid_unavailable(devid))
return true;
if (!nfs4_test_deviceid_unavailable(&mirror_ds->id_node)) {
ret = true;
goto out;
}
}
}
return false;
out:
rcu_read_unlock();
return ret;
}
static bool ff_rw_layout_has_available_ds(struct pnfs_layout_segment *lseg)
{
struct nfs4_ff_layout_mirror *mirror;
struct nfs4_deviceid_node *devid;
struct nfs4_ff_layout_ds *mirror_ds;
bool ret = false;
u32 idx, dss_id;
rcu_read_lock();
for (idx = 0; idx < FF_LAYOUT_MIRROR_COUNT(lseg); idx++) {
mirror = FF_LAYOUT_COMP(lseg, idx);
if (!mirror)
return false;
goto out;
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)
mirror_ds = rcu_dereference(mirror->dss[dss_id].mirror_ds);
if (IS_ERR(mirror_ds))
goto out;
if (!mirror_ds)
continue;
devid = &mirror->dss[dss_id].mirror_ds->id_node;
if (nfs4_test_deviceid_unavailable(devid))
return false;
if (nfs4_test_deviceid_unavailable(&mirror_ds->id_node))
goto out;
}
}
return FF_LAYOUT_MIRROR_COUNT(lseg) != 0;
ret = FF_LAYOUT_MIRROR_COUNT(lseg) != 0;
out:
rcu_read_unlock();
return ret;
}
static bool ff_layout_has_available_ds(struct pnfs_layout_segment *lseg)
@@ -644,3 +686,8 @@ module_param(dataserver_timeo, uint, 0644);
MODULE_PARM_DESC(dataserver_timeo, "The time (in tenths of a second) the "
"NFSv4.1 client waits for a response from a "
" data server before it retries an NFS request.");
module_param(dataserver_nconnect, uint, 0644);
MODULE_PARM_DESC(dataserver_nconnect, "The maximum number of connections "
"the NFSv4.1 client opens to each data server, "
"capping the value inherited from the MDS nconnect "
"mount option. 0 (default) applies no cap.");
+2
View File
@@ -402,6 +402,7 @@ static void nfs_set_mount_transport_protocol(struct nfs_fs_context *ctx)
ctx->mount_server.protocol = XPRT_TRANSPORT_UDP;
break;
case XPRT_TRANSPORT_TCP:
case XPRT_TRANSPORT_TCP_TLS:
case XPRT_TRANSPORT_RDMA:
ctx->mount_server.protocol = XPRT_TRANSPORT_TCP;
}
@@ -1750,6 +1751,7 @@ static int nfs_init_fs_context(struct fs_context *fc)
ctx->minorversion = 0;
ctx->need_mount = true;
ctx->xprtsec.policy = RPC_XPRTSEC_NONE;
ctx->xprtsec.keyring_serial = TLS_NO_KEYRING;
ctx->xprtsec.cert_serial = TLS_NO_CERT;
ctx->xprtsec.privkey_serial = TLS_NO_PRIVKEY;
+156 -55
View File
@@ -42,6 +42,12 @@
#include <linux/uaccess.h>
#include <linux/iversion.h>
#include <linux/fileattr.h>
#include <linux/nsproxy.h>
#include <linux/key-type.h>
#include <keys/user-type.h>
#ifdef CONFIG_KEYS
#include <keys/request_key_auth-type.h>
#endif
#include "nfs4_fs.h"
#include "callback.h"
@@ -2641,11 +2647,131 @@ static int nfsiod_start(void)
unsigned int nfs_net_id;
EXPORT_SYMBOL_GPL(nfs_net_id);
#ifdef CONFIG_KEYS
static int nfs_init_keyring(struct net *net)
{
struct nfs_net *nn = net_generic(net, nfs_net_id);
struct cred *cred;
struct key *keyring;
cred = prepare_kernel_cred(&init_task);
if (!cred)
return -ENOMEM;
keyring = keyring_alloc(".nfs", net->user_ns->owner,
net->user_ns->group, cred,
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
(KEY_USR_ALL & ~KEY_USR_SETATTR),
KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
put_cred(cred);
if (IS_ERR(keyring))
return PTR_ERR(keyring);
nn->nfs_keyring = keyring;
return 0;
}
static void nfs_exit_keyring(struct nfs_net *nn)
{
key_put(nn->nfs_keyring);
}
static int nfs_keyring_vet_description(const char *desc)
{
return strcmp(desc, ".nfs") ? -EINVAL : 0;
}
static int nfs_keyring_format_serial(char *buf, size_t len)
{
struct key *keyring = nfs_net_keyring(current->nsproxy->net_ns);
return snprintf(buf, len, "%d", key_serial(keyring));
}
/*
* A key planted by add_key() answers request_key() before the handler
* runs. Accept only the serial number the handler would produce.
*/
static int nfs_keyring_preparse(struct key_preparsed_payload *prep)
{
char serial[12];
int len;
len = nfs_keyring_format_serial(serial, sizeof(serial));
if (prep->datalen != len || !prep->data ||
memcmp(prep->data, serial, len))
return -EINVAL;
return user_preparse(prep);
}
static int nfs_keyring_request_key(struct key *authkey, void *aux)
{
struct request_key_auth *rka = get_request_key_auth(authkey);
char serial[12];
int len, ret;
len = nfs_keyring_format_serial(serial, sizeof(serial));
ret = key_instantiate_and_link(rka->target_key, serial, len,
rka->dest_keyring, authkey);
if (ret < 0)
complete_request_key(authkey, ret);
return ret;
}
/*
* A session keyring survives setns(). Without the net domain tag, a
* key instantiated in one namespace answers a request from another.
*/
static struct key_type key_type_nfs_keyring = {
.name = "nfs_keyring",
.flags = KEY_TYPE_NET_DOMAIN,
.vet_description = nfs_keyring_vet_description,
.preparse = nfs_keyring_preparse,
.free_preparse = user_free_preparse,
.instantiate = generic_key_instantiate,
.revoke = user_revoke,
.destroy = user_destroy,
.describe = user_describe,
.read = user_read,
.request_key = nfs_keyring_request_key,
};
static int __init nfs_register_key_type(void)
{
return register_key_type(&key_type_nfs_keyring);
}
static void nfs_unregister_key_type(void)
{
unregister_key_type(&key_type_nfs_keyring);
}
#else
static inline int nfs_init_keyring(struct net *net)
{
return 0;
}
static inline void nfs_exit_keyring(struct nfs_net *nn)
{
}
static inline int nfs_register_key_type(void)
{
return 0;
}
static inline void nfs_unregister_key_type(void)
{
}
#endif /* CONFIG_KEYS */
static int nfs_net_init(struct net *net)
{
struct nfs_net *nn = net_generic(net, nfs_net_id);
int err;
err = nfs_init_keyring(net);
if (err)
return err;
nfs_clients_init(net);
if (!rpc_proc_register(net, &nn->rpcstats)) {
@@ -2663,14 +2789,18 @@ err_proc_nfs:
rpc_proc_unregister(net, "nfs");
err_proc_rpc:
nfs_clients_exit(net);
nfs_exit_keyring(nn);
return err;
}
static void nfs_net_exit(struct net *net)
{
struct nfs_net *nn = net_generic(net, nfs_net_id);
rpc_proc_unregister(net, "nfs");
nfs_fs_proc_net_exit(net);
nfs_clients_exit(net);
nfs_exit_keyring(nn);
}
static struct pernet_operations nfs_net_ops = {
@@ -2680,35 +2810,6 @@ static struct pernet_operations nfs_net_ops = {
.size = sizeof(struct nfs_net),
};
#ifdef CONFIG_KEYS
static struct key *nfs_keyring;
static int __init nfs_init_keyring(void)
{
nfs_keyring = keyring_alloc(".nfs",
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
current_cred(),
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
(KEY_USR_ALL & ~KEY_USR_SETATTR),
KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
return PTR_ERR_OR_ZERO(nfs_keyring);
}
static void nfs_exit_keyring(void)
{
key_put(nfs_keyring);
}
#else
static inline int nfs_init_keyring(void)
{
return 0;
}
static inline void nfs_exit_keyring(void)
{
}
#endif /* CONFIG_KEYS */
/*
* Initialize NFS
*/
@@ -2716,71 +2817,71 @@ static int __init init_nfs_fs(void)
{
int err;
err = nfs_init_keyring();
if (err)
return err;
err = nfs_sysfs_init();
if (err < 0)
goto out10;
return err;
err = register_pernet_subsys(&nfs_net_ops);
if (err < 0)
goto out9;
goto err_sysfs;
err = nfs_register_key_type();
if (err)
goto err_pernet;
err = nfsiod_start();
if (err)
goto out7;
goto err_keytype;
err = nfs_fs_proc_init();
if (err)
goto out6;
goto err_nfsiod;
err = nfs_init_nfspagecache();
if (err)
goto out5;
goto err_proc;
err = nfs_init_inodecache();
if (err)
goto out4;
goto err_nfspagecache;
err = nfs_init_readpagecache();
if (err)
goto out3;
goto err_inodecache;
err = nfs_init_writepagecache();
if (err)
goto out2;
goto err_readpagecache;
err = nfs_init_directcache();
if (err)
goto out1;
goto err_writepagecache;
err = register_nfs_fs();
if (err)
goto out0;
goto err_directcache;
return 0;
out0:
err_directcache:
nfs_destroy_directcache();
out1:
err_writepagecache:
nfs_destroy_writepagecache();
out2:
err_readpagecache:
nfs_destroy_readpagecache();
out3:
err_inodecache:
nfs_destroy_inodecache();
out4:
err_nfspagecache:
nfs_destroy_nfspagecache();
out5:
err_proc:
nfs_fs_proc_exit();
out6:
err_nfsiod:
nfsiod_stop();
out7:
err_keytype:
nfs_unregister_key_type();
err_pernet:
unregister_pernet_subsys(&nfs_net_ops);
out9:
err_sysfs:
nfs_sysfs_exit();
out10:
nfs_exit_keyring();
return err;
}
@@ -2791,12 +2892,12 @@ static void __exit exit_nfs_fs(void)
nfs_destroy_readpagecache();
nfs_destroy_inodecache();
nfs_destroy_nfspagecache();
nfs_unregister_key_type();
unregister_pernet_subsys(&nfs_net_ops);
unregister_nfs_fs();
nfs_fs_proc_exit();
nfsiod_stop();
nfs_sysfs_exit();
nfs_exit_keyring();
}
/* Not quite true; I just maintain it */
+33 -1
View File
@@ -86,6 +86,7 @@ struct nfs_client_initdata {
struct xprtsec_parms xprtsec;
unsigned long connect_timeout;
unsigned long reconnect_timeout;
rpc_authflavor_t authflavor; /* Auth flavor to use for DS connection */
};
/*
@@ -251,6 +252,7 @@ extern struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv,
int ds_addrlen, int ds_proto,
unsigned int ds_timeo,
unsigned int ds_retrans,
unsigned int ds_nconnect,
u32 minor_version,
bool tightly_coupled);
extern struct rpc_clnt *nfs4_find_or_create_ds_client(struct nfs_client *,
@@ -260,7 +262,7 @@ extern void nfs4_session_limit_xasize(struct nfs_server *server);
extern struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv,
const struct sockaddr_storage *ds_addr, int ds_addrlen,
int ds_proto, unsigned int ds_timeo,
unsigned int ds_retrans);
unsigned int ds_retrans, unsigned int ds_nconnect);
#ifdef CONFIG_PROC_FS
extern int __init nfs_fs_proc_init(void);
extern void nfs_fs_proc_exit(void);
@@ -394,6 +396,7 @@ extern unsigned long nfs_access_cache_count(struct shrinker *shrink,
struct shrink_control *sc);
extern unsigned long nfs_access_cache_scan(struct shrinker *shrink,
struct shrink_control *sc);
extern void nfs_drop_nlink(struct inode *inode, unsigned long gencount);
struct dentry *nfs_lookup(struct inode *, struct dentry *, unsigned int);
void nfs_d_prune_case_insensitive_aliases(struct inode *inode);
int nfs_create(const struct mnt_idmap *, struct inode *, struct dentry *,
@@ -545,6 +548,35 @@ static inline bool nfs_file_io_is_buffered(struct nfs_inode *nfsi)
return test_bit(NFS_INO_ODIRECT, &nfsi->flags) == 0;
}
/*
* Module-private nfs_inode->flags bit (not in <linux/nfs_fs.h>, so no
* exported layout changes): set, and never cleared for the life of the
* in-core inode, once a layout driver has seen the server forbid I/O
* through the MDS for this file (flexfiles sets it from
* FF_FLAGS_NO_IO_THRU_MDS in ff_layout_alloc_lseg()). The RFC 5661
* mdsthreshold hint must not be acted on for such a file, since the only
* thing pnfs_within_mdsthreshold() can ask for is the one thing the
* layout forbids. Servers are assumed to be consistent in their
* no-fallback policy per file, the same assumption
* ff_layout_hdr_no_fallback_to_mds() already makes; if one were not, the
* only effect is that its mdsthreshold hint - a SHOULD - stops being
* honored for an inode that is already in core.
*/
#define NFS_INO_NO_IO_THRU_MDS (30)
static inline void nfs_set_no_io_thru_mds(struct inode *inode)
{
struct nfs_inode *nfsi = NFS_I(inode);
if (!test_bit(NFS_INO_NO_IO_THRU_MDS, &nfsi->flags))
set_bit(NFS_INO_NO_IO_THRU_MDS, &nfsi->flags);
}
static inline bool nfs_no_io_thru_mds(struct inode *inode)
{
return test_bit(NFS_INO_NO_IO_THRU_MDS, &NFS_I(inode)->flags);
}
/* Must be called with exclusively locked inode->i_rwsem */
static inline void nfs_file_block_o_direct(struct nfs_inode *nfsi)
{
+24 -7
View File
@@ -446,7 +446,7 @@ nfs_local_iters_setup_dio(struct nfs_local_kiocb *iocb, int rw,
if (unlikely(!iocb->iter_is_dio_aligned[n_iters])) {
trace_nfs_local_dio_misaligned(iocb->hdr->inode,
local_dio->start_len, local_dio->middle_len, local_dio);
local_dio->middle_offset, local_dio->middle_len, local_dio);
return 0; /* no DIO-aligned IO possible */
}
iocb->end_iter_index = n_iters;
@@ -674,6 +674,8 @@ static void nfs_local_call_read(struct work_struct *work)
n_iters = atomic_read(&iocb->n_iters);
for (int i = 0; i < n_iters ; i++) {
size_t expected;
if (iocb->iter_is_dio_aligned[i]) {
iocb->kiocb.ki_flags |= IOCB_DIRECT;
/* Only use AIO completion if DIO-aligned segment is last */
@@ -684,6 +686,8 @@ static void nfs_local_call_read(struct work_struct *work)
} else
iocb->kiocb.ki_flags &= ~IOCB_DIRECT;
/* read_iter() advances the iterator: measure it beforehand */
expected = iov_iter_count(&iocb->iters[i]);
scoped_with_creds(filp->f_cred)
status = filp->f_op->read_iter(&iocb->kiocb, &iocb->iters[i]);
@@ -691,7 +695,7 @@ static void nfs_local_call_read(struct work_struct *work)
continue;
/* Break on completion, errors, or short reads */
if (nfs_local_pgio_done(iocb, status) || status < 0 ||
(size_t)status < iov_iter_count(&iocb->iters[i])) {
(size_t)status < expected) {
nfs_local_read_iocb_done(iocb);
break;
}
@@ -890,7 +894,7 @@ static void nfs_local_call_write(struct work_struct *work)
file_start_write(filp);
n_iters = atomic_read(&iocb->n_iters);
for (int i = 0; i < n_iters ; i++) {
size_t icount;
size_t expected;
if (iocb->iter_is_dio_aligned[i]) {
iocb->kiocb.ki_flags |= IOCB_DIRECT;
@@ -902,16 +906,17 @@ static void nfs_local_call_write(struct work_struct *work)
} else
iocb->kiocb.ki_flags &= ~IOCB_DIRECT;
/* write_iter() advances the iterator: measure it beforehand */
expected = iov_iter_count(&iocb->iters[i]);
scoped_with_creds(filp->f_cred)
status = filp->f_op->write_iter(&iocb->kiocb, &iocb->iters[i]);
if (status == -EIOCBQUEUED)
continue;
/* Break on completion, errors, or short writes */
icount = iov_iter_count(&iocb->iters[i]);
if (nfs_local_pgio_done(iocb, status) || status < 0 ||
(size_t)status < icount) {
if ((size_t)status < icount) {
(size_t)status < expected) {
if ((size_t)status < expected) {
struct nfs_lock_context *ctx =
iocb->hdr->req->wb_lock_context;
@@ -931,6 +936,7 @@ static void nfs_local_do_write(struct nfs_local_kiocb *iocb,
const struct rpc_call_ops *call_ops)
{
struct nfs_pgio_header *hdr = iocb->hdr;
enum nfs3_stable_how committed = hdr->args.stable;
dprintk("%s: vfs_write count=%u pos=%llu %s\n",
__func__, hdr->args.count, hdr->args.offset,
@@ -949,9 +955,20 @@ static void nfs_local_do_write(struct nfs_local_kiocb *iocb,
iocb->kiocb.ki_flags |= IOCB_DSYNC|IOCB_SYNC;
}
/*
* Report the stability the write will actually have. A DIO WRITE
* is persisted before it completes whatever was asked for, see
* nfs_local_iters_init(), and a caller told so has no reason to
* COMMIT data that is already on stable storage.
*/
if (iocb->kiocb.ki_flags & IOCB_SYNC)
committed = NFS_FILE_SYNC;
else if (iocb->kiocb.ki_flags & IOCB_DSYNC)
committed = NFS_DATA_SYNC;
nfs_local_pgio_init(hdr, call_ops);
nfs_set_local_verifier(hdr->inode, hdr->res.verf, hdr->args.stable);
nfs_set_local_verifier(hdr->inode, hdr->res.verf, committed);
INIT_WORK(&iocb->work, nfs_local_call_write);
if (nfs_local_defer_io())
+13 -1
View File
@@ -18,6 +18,7 @@ struct bl_dev_msg {
uint32_t major, minor;
};
struct key;
struct nfs_netns_client;
struct nfs_net {
@@ -34,11 +35,15 @@ struct nfs_net {
unsigned short nfs_callback_tcpport;
unsigned short nfs_callback_tcpport6;
int cb_users[NFS4_MAX_MINOR_VERSION + 1];
struct list_head nfs4_data_server_cache;
#define NFS4_DS_CACHE_HASH_BITS 8
#define NFS4_DS_CACHE_HASH_SIZE (1 << NFS4_DS_CACHE_HASH_BITS)
/* every entry is still in bucket 0 until the key is added */
struct hlist_head nfs4_data_server_cache[NFS4_DS_CACHE_HASH_SIZE];
spinlock_t nfs4_data_server_lock;
#endif /* CONFIG_NFS_V4 */
struct nfs_netns_client *nfs_client;
spinlock_t nfs_client_lock;
struct key *nfs_keyring;
ktime_t boot_time;
struct rpc_stat rpcstats;
#ifdef CONFIG_PROC_FS
@@ -48,4 +53,11 @@ struct nfs_net {
extern unsigned int nfs_net_id;
static inline struct key *nfs_net_keyring(struct net *net)
{
struct nfs_net *nn = net_generic(net, nfs_net_id);
return nn->nfs_keyring;
}
#endif
+8 -2
View File
@@ -84,7 +84,8 @@ struct nfs_server *nfs3_clone_server(struct nfs_server *source,
*/
struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv,
const struct sockaddr_storage *ds_addr, int ds_addrlen,
int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans)
int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans,
unsigned int ds_nconnect)
{
struct rpc_timeout ds_timeout;
unsigned long connect_timeout = ds_timeo * (ds_retrans + 1) * HZ / 10;
@@ -101,6 +102,7 @@ struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv,
.cred = mds_srv->cred,
.xprtsec = {
.policy = RPC_XPRTSEC_NONE,
.keyring_serial = TLS_NO_KEYRING,
.cert_serial = TLS_NO_CERT,
.privkey_serial = TLS_NO_PRIVKEY,
},
@@ -124,8 +126,12 @@ struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv,
fallthrough;
case XPRT_TRANSPORT_RDMA:
case XPRT_TRANSPORT_TCP:
if (mds_clp->cl_nconnect > 1)
if (mds_clp->cl_nconnect > 1) {
cl_init.nconnect = mds_clp->cl_nconnect;
if (ds_nconnect)
cl_init.nconnect = min(cl_init.nconnect,
ds_nconnect);
}
}
if (mds_srv->flags & NFS_MOUNT_NORESVPORT)
+8
View File
@@ -448,13 +448,21 @@ nfs3_proc_remove(struct inode *dir, struct dentry *dentry)
.rpc_resp = &res,
};
int status = -ENOMEM;
struct inode *inode = d_inode(dentry);
unsigned long gencount;
dprintk("NFS call remove %pd2\n", dentry);
res.dir_attr = nfs_alloc_fattr();
if (res.dir_attr == NULL)
goto out;
if (inode)
gencount = READ_ONCE(NFS_I(inode)->attr_gencount);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
if (status == 0 && inode)
nfs_drop_nlink(inode, gencount);
nfs_post_op_update_inode(dir, res.dir_attr);
nfs_free_fattr(res.dir_attr);
out:
+5
View File
@@ -52,6 +52,7 @@ enum nfs4_client_state {
NFS4CLNT_RECALL_ANY_LAYOUT_READ,
NFS4CLNT_RECALL_ANY_LAYOUT_RW,
NFS4CLNT_DELEGRETURN_DELAYED,
NFS4CLNT_DEVICEID_DELETE,
};
#define NFS4_RENEW_TIMEOUT 0x01
@@ -493,6 +494,7 @@ int nfs41_discover_server_trunking(struct nfs_client *clp,
struct nfs_client **, const struct cred *);
extern void nfs4_schedule_session_recovery(struct nfs4_session *, int);
extern void nfs41_notify_server(struct nfs_client *);
extern void nfs4_deviceid_delete_recover_run(struct nfs_client *clp);
bool nfs4_check_serverowner_major_id(struct nfs41_server_owner *o1,
struct nfs41_server_owner *o2);
@@ -509,6 +511,7 @@ extern void nfs_inode_find_state_and_recover(struct inode *inode,
const nfs4_stateid *stateid);
extern int nfs4_state_mark_reclaim_nograce(struct nfs_client *, struct nfs4_state *);
extern void nfs4_schedule_lease_recovery(struct nfs_client *);
extern void nfs4_reset_all_state(struct nfs_client *);
extern int nfs4_wait_clnt_recover(struct nfs_client *clp);
extern int nfs4_client_recover_expired_lease(struct nfs_client *clp);
extern void nfs4_schedule_state_manager(struct nfs_client *);
@@ -526,6 +529,8 @@ extern int nfs4_select_rw_stateid(struct nfs4_state *, fmode_t,
const struct cred **);
extern bool nfs4_copy_open_stateid(nfs4_stateid *dst,
struct nfs4_state *state);
extern bool nfs4_lock_context_lost(struct nfs4_state *state,
const struct nfs_lock_context *l_ctx);
extern struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask);
extern int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task);
+14 -2
View File
@@ -217,6 +217,7 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
clp->cl_last_renewal = jiffies;
init_waitqueue_head(&clp->cl_lock_waitq);
INIT_LIST_HEAD(&clp->pending_cb_stateids);
INIT_LIST_HEAD(&clp->cl_deviceid_deletes);
if (cl_init->minorversion != 0)
__set_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags);
@@ -229,8 +230,12 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
/*
* Set up the connection to the server before we add add to the
* global list.
*
* Prefer krb5i unless the caller already knows AUTH_UNIX is required.
*/
err = nfs_create_rpc_client(clp, cl_init, RPC_AUTH_GSS_KRB5I);
err = -EINVAL;
if (cl_init->authflavor != RPC_AUTH_UNIX)
err = nfs_create_rpc_client(clp, cl_init, RPC_AUTH_GSS_KRB5I);
if (err == -EINVAL)
err = nfs_create_rpc_client(clp, cl_init, RPC_AUTH_UNIX);
if (err < 0)
@@ -286,6 +291,7 @@ static void nfs4_shutdown_client(struct nfs_client *clp)
nfs4_kill_renewd(clp);
clp->cl_mvops->shutdown_client(clp);
nfs4_destroy_callback(clp);
pnfs_deviceid_delete_queue_free(clp);
if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
nfs_idmap_delete(clp);
@@ -792,7 +798,8 @@ static int nfs4_set_client(struct nfs_server *server,
struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv,
const struct sockaddr_storage *ds_addr, int ds_addrlen,
int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans,
u32 minor_version, bool tightly_coupled)
unsigned int ds_nconnect, u32 minor_version,
bool tightly_coupled)
{
struct rpc_timeout ds_timeout;
struct nfs_client *mds_clp = mds_srv->nfs_client;
@@ -809,9 +816,11 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv,
.cred = mds_srv->cred,
.xprtsec = {
.policy = RPC_XPRTSEC_NONE,
.keyring_serial = TLS_NO_KEYRING,
.cert_serial = TLS_NO_CERT,
.privkey_serial = TLS_NO_PRIVKEY,
},
.authflavor = mds_clp->cl_rpcclient->cl_auth->au_flavor,
};
char buf[INET6_ADDRSTRLEN + 1];
@@ -830,6 +839,9 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv,
case XPRT_TRANSPORT_TCP:
if (mds_clp->cl_nconnect > 1) {
cl_init.nconnect = mds_clp->cl_nconnect;
if (ds_nconnect)
cl_init.nconnect = min(cl_init.nconnect,
ds_nconnect);
cl_init.max_connect = NFS_MAX_TRANSPORTS;
}
}
+141
View File
@@ -4969,12 +4969,14 @@ static int nfs4_proc_remove(struct inode *dir, struct dentry *dentry)
};
struct inode *inode = d_inode(dentry);
int err;
unsigned long gencount;
if (inode) {
if (inode->i_nlink == 1)
nfs4_inode_return_delegation(inode);
else
nfs4_inode_make_writeable(inode);
gencount = READ_ONCE(NFS_I(inode)->attr_gencount);
}
do {
err = _nfs4_proc_remove(dir, &dentry->d_name, NF4REG);
@@ -4982,6 +4984,9 @@ static int nfs4_proc_remove(struct inode *dir, struct dentry *dentry)
err = nfs4_handle_exception(NFS_SERVER(dir), err,
&exception);
} while (exception.retry);
if (err == 0 && inode)
nfs_drop_nlink(inode, gencount);
return err;
}
@@ -9680,6 +9685,14 @@ nfs4_layoutget_handle_exception(struct rpc_task *task,
case -NFS4ERR_BADLAYOUT:
status = -EOVERFLOW;
goto out;
/*
* NFS4ERR_TOOSMALL means the layout for the requested range
* exceeds what the client advertised in loga_maxcount (see
* RFC8881 section 18.43.3).
*/
case -ETOOSMALL:
status = -EMSGSIZE;
goto out;
/*
* NFS4ERR_LAYOUTTRYLATER is a conflict with another client
* (or clients) writing to the same RAID stripe except when
@@ -10498,6 +10511,134 @@ out_put_clp:
return ret;
}
/*
* GETDEVICEINFO surfacing the raw status; nfs4_get_device_info()
* swallows it. A device too large for one page fails with something
* other than -ENOENT, which still proves existence.
*/
static int nfs4_deviceid_validate(struct nfs_server *server,
const struct pnfs_layoutdriver_type *ld,
const struct nfs4_deviceid *id, const struct cred *cred)
{
struct pnfs_device pdev;
struct page *page;
int status;
page = alloc_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
memset(&pdev, 0, sizeof(pdev));
memcpy(&pdev.dev_id, id, sizeof(pdev.dev_id));
pdev.layout_type = ld->id;
pdev.pages = &page;
pdev.pglen = PAGE_SIZE;
pdev.maxcount = PAGE_SIZE - nfs41_maxgetdevinfo_overhead;
status = nfs4_proc_getdeviceinfo(server, &pdev, cred);
__free_page(page);
return status;
}
/*
* A DELETE for a deviceID we still hold layouts on implies the server
* revoked them: run the RFC 8881 Section 18.40.4 recovery. A layout the
* server still calls valid leaves the revocations unable to confirm the
* delete, so verify it with GETDEVICEINFO.
*/
static void nfs4_deviceid_delete_recover(struct nfs_client *clp,
const struct pnfs_layoutdriver_type *ld,
const struct nfs4_deviceid *id)
{
LIST_HEAD(layouts);
struct nfs4_deviceid_ref *ref, *confirm = NULL;
bool revoked = false;
bool inconclusive = false;
int status;
if (pnfs_layout_collect_deviceid_refs(clp, ld, id, &layouts)) {
/* Only a partial list -- an allocation failed, or an inode is
* being evicted. Leave the device cached and recover on a
* later notification.
*/
pnfs_layout_put_deviceid_refs(&layouts);
return;
}
if (list_empty(&layouts)) {
nfs4_delete_deviceid(ld, clp, id);
return;
}
list_for_each_entry(ref, &layouts, node) {
struct pnfs_layout_hdr *lo = ref->lo;
struct inode *inode = ref->inode;
bool invalidated = false;
LIST_HEAD(head);
status = nfs41_test_stateid(NFS_SERVER(inode), &ref->stateid,
ref->cred);
switch (status) {
case NFS_OK:
case -NFS4ERR_OLD_STATEID:
if (!confirm)
confirm = ref;
break;
case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_DELEG_REVOKED:
case -NFS4ERR_EXPIRED:
case -NFS4ERR_BAD_STATEID:
spin_lock(&inode->i_lock);
if (pnfs_layout_is_valid(lo) &&
nfs4_stateid_match_other(&ref->stateid,
&lo->plh_stateid)) {
pnfs_mark_layout_stateid_invalid(lo, &head);
revoked = true;
invalidated = true;
}
spin_unlock(&inode->i_lock);
pnfs_free_lseg_list(&head);
if (invalidated)
nfs_commit_inode(inode, 0);
nfs41_free_stateid(NFS_SERVER(inode), &ref->stateid,
ref->cred, true);
break;
default:
inconclusive = true;
break;
}
}
if (confirm) {
status = nfs4_deviceid_validate(NFS_SERVER(confirm->inode),
ld, id, confirm->cred);
if (status == -ENOENT) {
/* Section 18.40.4 prescribes EXCHANGE_ID here;
* nfs4_schedule_lease_recovery() would only renew
* the existing lease.
*/
pr_warn_ratelimited("NFS: server %s deleted a deviceID referred to by a layout it still considers valid; re-establishing the client ID\n",
clp->cl_hostname);
nfs4_reset_all_state(clp);
nfs4_delete_deviceid(ld, clp, id);
}
} else if (revoked && !inconclusive) {
nfs4_delete_deviceid(ld, clp, id);
}
pnfs_layout_put_deviceid_refs(&layouts);
}
void nfs4_deviceid_delete_recover_run(struct nfs_client *clp)
{
struct nfs4_deviceid_delete *dd;
while ((dd = pnfs_deviceid_delete_dequeue(clp)) != NULL) {
nfs4_deviceid_delete_recover(clp, dd->ld, &dd->id);
pnfs_put_layoutdriver(dd->ld);
kfree(dd);
}
}
static void
nfs41_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp)
{
+40 -5
View File
@@ -812,10 +812,13 @@ void nfs4_close_sync(struct nfs4_state *state, fmode_t fmode)
* be made. Failing that, a "consistent" choice is preferable. The
* consistent choice we make is to prefer the first owner, that of a
* Posix lock.
*
* Called with state->state_lock held. No reference is taken: a lock
* state is freed only after it is unlinked under state->state_lock.
*/
static struct nfs4_lock_state *
__nfs4_find_lock_state(struct nfs4_state *state,
fl_owner_t owner, fl_owner_t owner2)
__nfs4_lookup_lock_state(struct nfs4_state *state,
fl_owner_t owner, fl_owner_t owner2)
{
struct nfs4_lock_state *pos, *ret = NULL;
list_for_each_entry(pos, &state->lock_states, ls_locks) {
@@ -826,6 +829,16 @@ __nfs4_find_lock_state(struct nfs4_state *state,
if (pos->ls_owner == owner2)
ret = pos;
}
return ret;
}
static struct nfs4_lock_state *
__nfs4_find_lock_state(struct nfs4_state *state,
fl_owner_t owner, fl_owner_t owner2)
{
struct nfs4_lock_state *ret;
ret = __nfs4_lookup_lock_state(state, owner, owner2);
if (ret)
refcount_inc(&ret->ls_count);
return ret;
@@ -968,7 +981,7 @@ static int nfs4_copy_lock_stateid(nfs4_stateid *dst,
fl_flock_owner = l_ctx->open_context->flock_owner;
spin_lock(&state->state_lock);
lsp = __nfs4_find_lock_state(state, owner, fl_flock_owner);
lsp = __nfs4_lookup_lock_state(state, owner, fl_flock_owner);
if (lsp && test_bit(NFS_LOCK_LOST, &lsp->ls_flags))
ret = -EIO;
else if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) {
@@ -976,11 +989,28 @@ static int nfs4_copy_lock_stateid(nfs4_stateid *dst,
ret = 0;
}
spin_unlock(&state->state_lock);
nfs4_put_lock_state(lsp);
out:
return ret;
}
bool nfs4_lock_context_lost(struct nfs4_state *state,
const struct nfs_lock_context *l_ctx)
{
struct nfs4_lock_state *lsp;
bool ret;
if (!test_bit(LK_STATE_IN_USE, &state->flags))
return false;
spin_lock(&state->state_lock);
lsp = __nfs4_lookup_lock_state(state, l_ctx->lockowner,
l_ctx->open_context->flock_owner);
ret = lsp && test_bit(NFS_LOCK_LOST, &lsp->ls_flags);
spin_unlock(&state->state_lock);
return ret;
}
EXPORT_SYMBOL_GPL(nfs4_lock_context_lost);
bool nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state)
{
bool ret;
@@ -2354,7 +2384,7 @@ void nfs41_notify_server(struct nfs_client *clp)
nfs4_schedule_state_manager(clp);
}
static void nfs4_reset_all_state(struct nfs_client *clp)
void nfs4_reset_all_state(struct nfs_client *clp)
{
if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) {
set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
@@ -2669,6 +2699,11 @@ static void nfs4_state_manager(struct nfs_client *clp)
set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
}
nfs4_layoutreturn_any_run(clp);
if (test_and_clear_bit(NFS4CLNT_DEVICEID_DELETE,
&clp->cl_state)) {
nfs4_deviceid_delete_recover_run(clp);
set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
}
clear_bit(NFS4CLNT_RECALL_RUNNING, &clp->cl_state);
}
+2 -3
View File
@@ -5169,8 +5169,7 @@ static int decode_lock_denied(struct xdr_stream *xdr, struct file_lock *fl)
p = xdr_decode_hyper(p, &clientid); /* read 8 bytes */
namelen = be32_to_cpup(p); /* read 4 bytes */ /* have read all 32 bytes now */
p = xdr_inline_decode(xdr, namelen); /* variable size field */
if (likely(!p))
return -EIO;
/* We have an owner here, return DENIED */
return -NFS4ERR_DENIED;
}
@@ -6187,7 +6186,7 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
dprintk("NFS: server cheating in layoutget reply: "
"layout len %u > recvd %u\n",
res->layoutp->len, recvd);
status = -EINVAL;
status = -EMSGSIZE;
goto out;
}
+4 -1
View File
@@ -1772,7 +1772,10 @@ DECLARE_EVENT_CLASS(nfs_local_dio_class,
__entry->count = count;
__entry->mem_align = local_dio->mem_align;
__entry->offset_align = local_dio->offset_align;
__entry->start = offset;
if (local_dio->start_len)
__entry->start = local_dio->middle_offset - local_dio->start_len;
else
__entry->start = 0;
__entry->start_len = local_dio->start_len;
__entry->middle = local_dio->middle_offset;
__entry->middle_len = local_dio->middle_len;
+56 -10
View File
@@ -404,20 +404,28 @@ static struct nfs_page *nfs_page_create(struct nfs_lock_context *l_ctx,
return req;
}
static void nfs_page_assign_folio(struct nfs_page *req, struct folio *folio)
static void nfs_page_assign_folio(struct nfs_page *req, struct folio *folio,
bool pinned)
{
if (folio != NULL) {
req->wb_folio = folio;
folio_get(folio);
if (pinned)
set_bit(PG_PINNED, &req->wb_flags);
else
folio_get(folio);
set_bit(PG_FOLIO, &req->wb_flags);
}
}
static void nfs_page_assign_page(struct nfs_page *req, struct page *page)
static void nfs_page_assign_page(struct nfs_page *req, struct page *page,
bool pinned)
{
if (page != NULL) {
req->wb_page = page;
get_page(page);
if (pinned)
set_bit(PG_PINNED, &req->wb_flags);
else
get_page(page);
}
}
@@ -425,6 +433,7 @@ static void nfs_page_assign_page(struct nfs_page *req, struct page *page)
* nfs_page_create_from_page - Create an NFS read/write request.
* @ctx: open context to use
* @page: page to write
* @pinned: true if page is pinned
* @pgbase: starting offset within the page for the write
* @offset: file offset for the write
* @count: number of bytes to read/write
@@ -435,6 +444,7 @@ static void nfs_page_assign_page(struct nfs_page *req, struct page *page)
*/
struct nfs_page *nfs_page_create_from_page(struct nfs_open_context *ctx,
struct page *page,
bool pinned,
unsigned int pgbase, loff_t offset,
unsigned int count)
{
@@ -446,7 +456,9 @@ struct nfs_page *nfs_page_create_from_page(struct nfs_open_context *ctx,
ret = nfs_page_create(l_ctx, pgbase, offset >> PAGE_SHIFT,
offset_in_page(offset), count);
if (!IS_ERR(ret)) {
nfs_page_assign_page(ret, page);
nfs_page_assign_page(ret, page, pinned);
if (pinned)
ret->wb_nr_pinned = 1;
nfs_page_group_init(ret, NULL);
}
nfs_put_lock_context(l_ctx);
@@ -457,6 +469,7 @@ struct nfs_page *nfs_page_create_from_page(struct nfs_open_context *ctx,
* nfs_page_create_from_folio - Create an NFS read/write request.
* @ctx: open context to use
* @folio: folio to write
* @pinned: true if folio is pinned
* @offset: starting offset within the folio for the write
* @count: number of bytes to read/write
*
@@ -466,6 +479,7 @@ struct nfs_page *nfs_page_create_from_page(struct nfs_open_context *ctx,
*/
struct nfs_page *nfs_page_create_from_folio(struct nfs_open_context *ctx,
struct folio *folio,
bool pinned,
unsigned int offset,
unsigned int count)
{
@@ -476,7 +490,10 @@ struct nfs_page *nfs_page_create_from_folio(struct nfs_open_context *ctx,
return ERR_CAST(l_ctx);
ret = nfs_page_create(l_ctx, offset, folio->index, offset, count);
if (!IS_ERR(ret)) {
nfs_page_assign_folio(ret, folio);
nfs_page_assign_folio(ret, folio, pinned);
if (pinned)
ret->wb_nr_pinned = nfs_page_array_len(offset_in_page(offset),
count);
nfs_page_group_init(ret, NULL);
}
nfs_put_lock_context(l_ctx);
@@ -498,9 +515,11 @@ nfs_create_subreq(struct nfs_page *req,
offset, count);
if (!IS_ERR(ret)) {
if (folio)
nfs_page_assign_folio(ret, folio);
nfs_page_assign_folio(ret, folio,
test_bit(PG_PINNED, &req->wb_flags));
else
nfs_page_assign_page(ret, page);
nfs_page_assign_page(ret, page,
test_bit(PG_PINNED, &req->wb_flags));
/* find the last request */
for (last = req->wb_head;
last->wb_this_page != req->wb_head;
@@ -552,11 +571,21 @@ static void nfs_clear_request(struct nfs_page *req)
struct nfs_open_context *ctx;
if (folio != NULL) {
folio_put(folio);
if (test_and_clear_bit(PG_PINNED, &req->wb_flags)) {
if (req->wb_nr_pinned > 0)
unpin_user_folio(folio, req->wb_nr_pinned);
} else {
folio_put(folio);
}
req->wb_folio = NULL;
clear_bit(PG_FOLIO, &req->wb_flags);
} else if (page != NULL) {
put_page(page);
if (test_and_clear_bit(PG_PINNED, &req->wb_flags)) {
if (req->wb_nr_pinned > 0)
unpin_user_pages(&page, req->wb_nr_pinned);
} else {
put_page(page);
}
req->wb_page = NULL;
}
if (l_ctx != NULL) {
@@ -599,6 +628,23 @@ void nfs_release_request(struct nfs_page *req)
}
EXPORT_SYMBOL_GPL(nfs_release_request);
/*
* nfs_release_request_list - Release a list of NFS read/write requests
* @head: list of requests to release
*
* Removes each request from the list and drops it's refcount.
*/
void nfs_release_request_list(struct list_head *head)
{
while (!list_empty(head)) {
struct nfs_page *req = nfs_list_entry(head->next);
nfs_list_remove_request(req);
nfs_release_request(req);
}
}
EXPORT_SYMBOL_GPL(nfs_release_request_list);
/*
* nfs_generic_pg_test - determine if requests can be coalesced
* @desc: pointer to descriptor
+368 -20
View File
@@ -433,7 +433,7 @@ bool nfs4_layout_refresh_old_stateid(nfs4_stateid *dst,
}
/* Try to update the seqid to the most recent */
err = pnfs_mark_matching_lsegs_return(lo, &head, &range, 0,
true);
true, NULL);
if (err != -EBUSY) {
dst->seqid = lo->plh_stateid.seqid;
*dst_range = range;
@@ -487,7 +487,8 @@ static int pnfs_mark_layout_stateid_return(struct pnfs_layout_hdr *lo,
.length = NFS4_MAX_UINT64,
};
return pnfs_mark_matching_lsegs_return(lo, lseg_list, &range, seq, true);
return pnfs_mark_matching_lsegs_return(lo, lseg_list, &range, seq, true,
NULL);
}
static int
@@ -525,7 +526,7 @@ pnfs_layout_io_set_failed(struct pnfs_layout_hdr *lo, u32 iomode)
spin_lock(&inode->i_lock);
pnfs_layout_set_fail_bit(lo, pnfs_iomode_to_fail_bit(iomode));
pnfs_mark_matching_lsegs_return(lo, &head, &range, 0, true);
pnfs_mark_matching_lsegs_return(lo, &head, &range, 0, true, NULL);
spin_unlock(&inode->i_lock);
pnfs_free_lseg_list(&head);
dprintk("%s Setting layout IOMODE_%s fail bit\n", __func__,
@@ -740,7 +741,7 @@ pnfs_mark_matching_lsegs_invalid(struct pnfs_layout_hdr *lo,
if (mark_lseg_invalid(lseg, tmp_list))
continue;
remaining++;
pnfs_lseg_cancel_io(server, lseg);
pnfs_lseg_cancel_io(server, lseg, NULL);
}
dprintk("%s:Return %i\n", __func__, remaining);
return remaining;
@@ -1167,11 +1168,12 @@ pnfs_alloc_init_layoutget_args(struct inode *ino,
struct nfs_open_context *ctx,
const nfs4_stateid *stateid,
const struct pnfs_layout_range *range,
gfp_t gfp_flags)
size_t min_reply_sz, gfp_t gfp_flags)
{
struct nfs_server *server = pnfs_find_server(ino, ctx);
size_t max_reply_sz = server->pnfs_curr_ld->max_layoutget_response;
size_t max_pages = max_response_pages(server);
size_t session_pages = max_response_pages(server);
size_t max_pages = session_pages;
struct nfs4_layoutget *lgp;
dprintk("--> %s\n", __func__);
@@ -1186,6 +1188,19 @@ pnfs_alloc_init_layoutget_args(struct inode *ino,
max_pages = npages;
}
/*
* A previous LAYOUTGET on this layout or on this server did not
* fit the reply buffer: raise the layout driver's default up to
* the session's maximum response size.
*/
if (!min_reply_sz)
min_reply_sz = READ_ONCE(server->lg_reply_sz);
if (min_reply_sz) {
size_t npages = (min_reply_sz + PAGE_SIZE - 1) >> PAGE_SHIFT;
if (npages > max_pages)
max_pages = min(npages, session_pages);
}
lgp->args.layout.pages = nfs4_alloc_pages(max_pages, gfp_flags);
if (!lgp->args.layout.pages) {
kfree(lgp);
@@ -1210,7 +1225,7 @@ pnfs_alloc_init_layoutget_args(struct inode *ino,
lgp->args.minlength = i_size - range->offset;
}
}
lgp->args.maxcount = PNFS_LAYOUT_MAXSIZE;
lgp->args.maxcount = lgp->args.layout.pglen;
pnfs_copy_range(&lgp->args.range, range);
lgp->args.type = server->pnfs_curr_ld->id;
lgp->args.inode = ino;
@@ -1462,7 +1477,7 @@ _pnfs_return_layout(struct inode *ino)
}
valid_layout = pnfs_layout_is_valid(lo);
pnfs_clear_layoutcommit(ino, &tmp_list);
pnfs_mark_matching_lsegs_return(lo, &tmp_list, &range, 0, true);
pnfs_mark_matching_lsegs_return(lo, &tmp_list, &range, 0, true, NULL);
/* Don't send a LAYOUTRETURN if list was initially empty */
@@ -2004,7 +2019,9 @@ pnfs_find_lseg(struct pnfs_layout_hdr *lo,
* is set to IOMODE_READ for a READ request, and set to IOMODE_RW for a
* WRITE request.
*
* A return of true means use MDS I/O.
* A return of true means use MDS I/O, so for a file whose layout forbids
* that (see NFS_INO_NO_IO_THRU_MDS) the hint is not evaluated at all:
* there is no answer it could give that this client may act on.
*
* From rfc 5661:
* If a file's size is smaller than the file size threshold, data accesses
@@ -2025,6 +2042,14 @@ static bool pnfs_within_mdsthreshold(struct nfs_open_context *ctx,
if (t == NULL)
return ret;
/*
* The server has told a layout driver that this file's I/O may not
* go through the MDS (flexfiles FF_FLAGS_NO_IO_THRU_MDS). Honor
* that over its own mdsthreshold hint.
*/
if (nfs_no_io_thru_mds(ino))
return ret;
dprintk("%s bm=0x%x rd_sz=%llu wr_sz=%llu rd_io=%llu wr_io=%llu\n",
__func__, t->bm, t->rd_sz, t->wr_sz, t->rd_io_sz, t->wr_io_sz);
@@ -2145,6 +2170,7 @@ pnfs_update_layout(struct inode *ino,
.inode = ino,
};
unsigned long giveup = jiffies + (clp->cl_lease_time << 1);
size_t reply_sz = 0;
bool first;
if (!pnfs_enabled_sb(NFS_SERVER(ino))) {
@@ -2304,7 +2330,8 @@ lookup_again:
if (arg.length != NFS4_MAX_UINT64)
arg.length = PAGE_ALIGN(arg.length);
lgp = pnfs_alloc_init_layoutget_args(ino, ctx, &stateid, &arg, gfp_flags);
lgp = pnfs_alloc_init_layoutget_args(ino, ctx, &stateid, &arg, reply_sz,
gfp_flags);
if (!lgp) {
lseg = ERR_PTR(-ENOMEM);
trace_pnfs_update_layout(ino, pos, count, iomode, lo, NULL,
@@ -2335,6 +2362,25 @@ lookup_again:
lo, pnfs_iomode_to_fail_bit(iomode));
lseg = NULL;
goto out_put_layout_hdr;
case -EMSGSIZE: {
/*
* The layout exceeded loga_maxcount (NFS4ERR_TOOSMALL):
* retry once with the reply buffer raised to the
* session's maximum response size before falling back
* to I/O through the MDS.
*/
size_t max = max_response_pages(server) << PAGE_SHIFT;
if (reply_sz < max) {
reply_sz = max;
exception.retry = 1;
break;
}
pnfs_layout_set_fail_bit(
lo, pnfs_iomode_to_fail_bit(iomode));
lseg = NULL;
goto out_put_layout_hdr;
}
default:
if (!nfs_error_is_fatal(PTR_ERR(lseg))) {
pnfs_layout_clear_fail_bit(lo, pnfs_iomode_to_fail_bit(iomode));
@@ -2354,6 +2400,13 @@ lookup_again:
goto lookup_again;
}
} else {
/*
* A LAYOUTGET that only succeeded with an escalated reply
* buffer: remember the size so that future LAYOUTGETs to
* this server skip the attempt at the driver's default.
*/
if (reply_sz)
WRITE_ONCE(server->lg_reply_sz, reply_sz);
pnfs_layout_clear_fail_bit(lo, pnfs_iomode_to_fail_bit(iomode));
}
@@ -2448,7 +2501,7 @@ static void _lgopen_prepare_attached(struct nfs4_opendata *data,
lo = _pnfs_grab_empty_layout(ino, ctx);
if (!lo)
return;
lgp = pnfs_alloc_init_layoutget_args(ino, ctx, &current_stateid, &rng,
lgp = pnfs_alloc_init_layoutget_args(ino, ctx, &current_stateid, &rng, 0,
nfs_io_gfp_mask());
if (!lgp) {
clear_and_wake_up_bit(NFS_LAYOUT_FIRST_LAYOUTGET, &lo->plh_flags);
@@ -2474,7 +2527,7 @@ static void _lgopen_prepare_floating(struct nfs4_opendata *data,
};
struct nfs4_layoutget *lgp;
lgp = pnfs_alloc_init_layoutget_args(ino, ctx, &current_stateid, &rng,
lgp = pnfs_alloc_init_layoutget_args(ino, ctx, &current_stateid, &rng, 0,
nfs_io_gfp_mask());
if (!lgp)
return;
@@ -2616,7 +2669,8 @@ pnfs_layout_process(struct nfs4_layoutget *lgp)
.iomode = IOMODE_ANY,
.length = NFS4_MAX_UINT64,
};
pnfs_mark_matching_lsegs_return(lo, &free_me, &range, 0, true);
pnfs_mark_matching_lsegs_return(lo, &free_me, &range, 0, true,
NULL);
goto out_forget;
} else {
/* We have a completely new layout */
@@ -2649,6 +2703,7 @@ out_forget:
* @return_range: describe layout segment ranges to be returned
* @seq: stateid seqid to match
* @cancel_io: signal io be cancelled
* @devid: only cancel io directed at this device (all devices if NULL)
*
* This function is mainly intended for use by layoutrecall. It attempts
* to free the layout segment immediately, or else to mark it for return
@@ -2663,7 +2718,8 @@ int
pnfs_mark_matching_lsegs_return(struct pnfs_layout_hdr *lo,
struct list_head *tmp_list,
const struct pnfs_layout_range *return_range,
u32 seq, bool cancel_io)
u32 seq, bool cancel_io,
const struct nfs4_deviceid *devid)
{
struct pnfs_layout_segment *lseg, *next;
struct nfs_server *server = NFS_SERVER(lo->plh_inode);
@@ -2690,7 +2746,7 @@ pnfs_mark_matching_lsegs_return(struct pnfs_layout_hdr *lo,
remaining++;
set_bit(NFS_LSEG_LAYOUTRETURN, &lseg->pls_flags);
if (cancel_io)
pnfs_lseg_cancel_io(server, lseg);
pnfs_lseg_cancel_io(server, lseg, devid);
}
if (remaining) {
@@ -2708,7 +2764,8 @@ pnfs_mark_matching_lsegs_return(struct pnfs_layout_hdr *lo,
static void
pnfs_mark_layout_for_return(struct inode *inode,
const struct pnfs_layout_range *range)
const struct pnfs_layout_range *range,
const struct nfs4_deviceid *devid)
{
struct pnfs_layout_hdr *lo;
bool return_now = false;
@@ -2726,7 +2783,7 @@ pnfs_mark_layout_for_return(struct inode *inode,
* for how it works.
*/
if (pnfs_mark_matching_lsegs_return(lo, &lo->plh_return_segs, range, 0,
true) != -EBUSY) {
true, devid) != -EBUSY) {
const struct cred *cred;
nfs4_stateid stateid;
enum pnfs_iomode iomode;
@@ -2743,7 +2800,8 @@ pnfs_mark_layout_for_return(struct inode *inode,
}
void pnfs_error_mark_layout_for_return(struct inode *inode,
struct pnfs_layout_segment *lseg)
struct pnfs_layout_segment *lseg,
const struct nfs4_deviceid *devid)
{
struct pnfs_layout_range range = {
.iomode = lseg->pls_range.iomode,
@@ -2751,7 +2809,7 @@ void pnfs_error_mark_layout_for_return(struct inode *inode,
.length = NFS4_MAX_UINT64,
};
pnfs_mark_layout_for_return(inode, &range);
pnfs_mark_layout_for_return(inode, &range, devid);
}
EXPORT_SYMBOL_GPL(pnfs_error_mark_layout_for_return);
@@ -2841,7 +2899,7 @@ restart:
pnfs_get_layout_hdr(lo);
pnfs_set_plh_return_info(lo, range->iomode, 0);
if (pnfs_mark_matching_lsegs_return(lo, &lo->plh_return_segs,
range, 0, true) != 0 ||
range, 0, true, NULL) != 0 ||
!pnfs_prepare_layoutreturn(lo, &stateid, &cred, &iomode)) {
spin_unlock(&inode->i_lock);
rcu_read_unlock();
@@ -2875,6 +2933,294 @@ pnfs_layout_return_unused_byclid(struct nfs_client *clp,
&range);
}
struct pnfs_reresolve_deviceid_args {
const struct pnfs_layoutdriver_type *ld;
const struct nfs4_deviceid *devid;
bool immediate;
struct list_head put_list;
};
static int pnfs_layout_reresolve_deviceid_byserver(struct nfs_server *server,
void *data)
{
struct pnfs_reresolve_deviceid_args *args = data;
struct pnfs_layout_hdr *lo;
struct inode *inode;
if (server->pnfs_curr_ld != args->ld)
return 0;
rcu_read_lock();
list_for_each_entry_rcu(lo, &server->layouts, plh_layouts) {
inode = lo->plh_inode;
if (!inode)
continue;
spin_lock(&inode->i_lock);
args->ld->reresolve_deviceid(lo, args->devid, args->immediate,
&args->put_list);
spin_unlock(&inode->i_lock);
}
rcu_read_unlock();
return 0;
}
/*
* Invoke @ld's reresolve_deviceid hook for @devid on every layout of @clp's
* servers, then drain the put_list once the locks are dropped.
*/
void
pnfs_layout_reresolve_deviceid_byclid(struct nfs_client *clp,
const struct pnfs_layoutdriver_type *ld,
const struct nfs4_deviceid *devid,
bool immediate)
{
struct pnfs_reresolve_deviceid_args args = {
.ld = ld,
.devid = devid,
.immediate = immediate,
.put_list = LIST_HEAD_INIT(args.put_list),
};
struct nfs4_deviceid_put *put, *tmp;
if (!ld->reresolve_deviceid)
return;
nfs_client_for_each_server(clp,
pnfs_layout_reresolve_deviceid_byserver, &args);
list_for_each_entry_safe(put, tmp, &args.put_list, node) {
list_del(&put->node);
nfs4_put_deviceid_node(put->dev);
kfree(put);
}
}
struct pnfs_deviceid_ref_args {
const struct pnfs_layoutdriver_type *ld;
const struct nfs4_deviceid *devid;
struct list_head *result;
bool found;
};
static int pnfs_layout_deviceid_referenced_byserver(
struct nfs_server *server, void *data)
{
struct pnfs_deviceid_ref_args *args = data;
struct pnfs_layout_hdr *lo;
struct inode *inode;
if (server->pnfs_curr_ld != args->ld)
return 0;
rcu_read_lock();
list_for_each_entry_rcu(lo, &server->layouts, plh_layouts) {
inode = lo->plh_inode;
if (!inode)
continue;
spin_lock(&inode->i_lock);
if (NFS_I(inode)->layout == lo && pnfs_layout_is_valid(lo) &&
args->ld->layout_references_deviceid(lo, args->devid))
args->found = true;
spin_unlock(&inode->i_lock);
if (args->found)
break;
}
rcu_read_unlock();
return args->found;
}
/*
* pnfs_layout_deviceid_referenced_byclid - does any live layout of
* @clp's servers using @ld still reference deviceid @devid?
*/
bool
pnfs_layout_deviceid_referenced_byclid(struct nfs_client *clp,
const struct pnfs_layoutdriver_type *ld,
const struct nfs4_deviceid *devid)
{
struct pnfs_deviceid_ref_args args = {
.ld = ld,
.devid = devid,
};
if (!ld->layout_references_deviceid)
return false;
nfs_client_for_each_server(clp,
pnfs_layout_deviceid_referenced_byserver, &args);
return args.found;
}
static int pnfs_layout_collect_deviceid_refs_byserver(
struct nfs_server *server, void *data)
{
struct pnfs_deviceid_ref_args *args = data;
struct nfs4_deviceid_ref *ref, *tmp;
struct pnfs_layout_hdr *lo;
struct inode *inode;
LIST_HEAD(putme);
bool matched;
int ret = 0;
if (server->pnfs_curr_ld != args->ld)
return 0;
rcu_read_lock();
list_for_each_entry_rcu(lo, &server->layouts, plh_layouts) {
inode = lo->plh_inode;
if (!inode)
continue;
spin_lock(&inode->i_lock);
matched = NFS_I(inode)->layout == lo &&
pnfs_layout_is_valid(lo) &&
args->ld->layout_references_deviceid(lo, args->devid);
if (!matched) {
spin_unlock(&inode->i_lock);
continue;
}
ref = kzalloc_obj(*ref, GFP_ATOMIC);
if (!ref) {
spin_unlock(&inode->i_lock);
ret = -ENOMEM;
break;
}
/* NFS_I()->layout == lo under i_lock means the refcount has
* not reached zero: pnfs_put_layout_hdr() decrements to zero
* and detaches in the same critical section.
*/
pnfs_get_layout_hdr(lo);
ref->lo = lo;
nfs4_stateid_copy(&ref->stateid, &lo->plh_stateid);
ref->cred = get_cred(lo->plh_lc_cred);
spin_unlock(&inode->i_lock);
/* the pinned hdr does not hold the inode: grab it (and
* keep the superblock active) for use across RPCs
*/
ref->inode = nfs_igrab_and_active(inode);
if (!ref->inode) {
/* The layout may still name the deviceID, so report a
* partial list rather than silently shortening it.
* Defer the put: it can layoutreturn and sleep.
*/
list_add(&ref->node, &putme);
ret = -EAGAIN;
break;
}
list_add_tail(&ref->node, args->result);
}
rcu_read_unlock();
list_for_each_entry_safe(ref, tmp, &putme, node) {
list_del(&ref->node);
pnfs_put_layout_hdr(ref->lo);
put_cred(ref->cred);
kfree(ref);
}
return ret;
}
/*
* Collect @clp's layouts referencing @devid onto @result as entries usable
* across sleeping RPCs; release with pnfs_layout_put_deviceid_refs().
* A negative return means @result is only a partial set.
*/
int
pnfs_layout_collect_deviceid_refs(struct nfs_client *clp,
const struct pnfs_layoutdriver_type *ld,
const struct nfs4_deviceid *devid,
struct list_head *result)
{
struct pnfs_deviceid_ref_args args = {
.ld = ld,
.devid = devid,
.result = result,
};
if (!ld->layout_references_deviceid)
return 0;
return nfs_client_for_each_server(clp,
pnfs_layout_collect_deviceid_refs_byserver, &args);
}
void
pnfs_layout_put_deviceid_refs(struct list_head *result)
{
struct nfs4_deviceid_ref *ref, *tmp;
list_for_each_entry_safe(ref, tmp, result, node) {
list_del(&ref->node);
put_cred(ref->cred);
pnfs_put_layout_hdr(ref->lo);
nfs_iput_and_deactive(ref->inode);
kfree(ref);
}
}
/*
* Queue @id for the state manager's Section 18.40.4 recovery,
* dropping duplicates of an already-queued suspect.
*/
void pnfs_deviceid_delete_mark(struct nfs_client *clp,
const struct pnfs_layoutdriver_type *ld,
const struct nfs4_deviceid *id)
{
struct nfs4_deviceid_delete *dd, *new;
new = kzalloc_obj(*new, GFP_KERNEL);
if (!new)
return; /* lost notification; recovery waits for the next */
new->ld = pnfs_find_layoutdriver(ld->id);
if (!new->ld) {
kfree(new);
return;
}
memcpy(&new->id, id, sizeof(new->id));
spin_lock(&clp->cl_lock);
list_for_each_entry(dd, &clp->cl_deviceid_deletes, list) {
if (dd->ld == new->ld &&
!memcmp(&dd->id, &new->id, sizeof(dd->id))) {
spin_unlock(&clp->cl_lock);
pnfs_put_layoutdriver(new->ld);
kfree(new);
return;
}
}
list_add_tail(&new->list, &clp->cl_deviceid_deletes);
spin_unlock(&clp->cl_lock);
set_bit(NFS4CLNT_DEVICEID_DELETE, &clp->cl_state);
nfs4_schedule_state_manager(clp);
}
struct nfs4_deviceid_delete *pnfs_deviceid_delete_dequeue(
struct nfs_client *clp)
{
struct nfs4_deviceid_delete *dd = NULL;
spin_lock(&clp->cl_lock);
if (!list_empty(&clp->cl_deviceid_deletes)) {
dd = list_first_entry(&clp->cl_deviceid_deletes,
struct nfs4_deviceid_delete, list);
list_del(&dd->list);
}
spin_unlock(&clp->cl_lock);
return dd;
}
void pnfs_deviceid_delete_queue_free(struct nfs_client *clp)
{
struct nfs4_deviceid_delete *dd;
while ((dd = pnfs_deviceid_delete_dequeue(clp)) != NULL) {
pnfs_put_layoutdriver(dd->ld);
kfree(dd);
}
}
/* Check if we have we have a valid layout but if there isn't an intersection
* between the request and the pgio->pg_lseg, put this pgio->pg_lseg away.
*/
@@ -3103,6 +3449,7 @@ pnfs_do_write(struct nfs_pageio_descriptor *desc,
static void pnfs_writehdr_free(struct nfs_pgio_header *hdr)
{
pnfs_put_ds_dev(hdr->ds_dev);
pnfs_put_lseg(hdr->lseg);
nfs_pgio_header_free(hdr);
}
@@ -3248,6 +3595,7 @@ pnfs_do_read(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr)
static void pnfs_readhdr_free(struct nfs_pgio_header *hdr)
{
pnfs_put_ds_dev(hdr->ds_dev);
pnfs_put_lseg(hdr->lseg);
nfs_pgio_header_free(hdr);
}
+95 -7
View File
@@ -57,7 +57,7 @@ struct nfs4_pnfs_ds_addr {
};
struct nfs4_pnfs_ds {
struct list_head ds_node; /* nfs4_pnfs_dev_hlist dev_dslist */
struct hlist_node ds_node; /* nfs_net nfs4_data_server_cache */
char *ds_remotestr; /* comma sep list of addrs */
struct list_head ds_addrs;
const struct net *ds_net;
@@ -171,6 +171,25 @@ struct pnfs_layoutdriver_type {
struct nfs4_deviceid_node * (*alloc_deviceid_node)
(struct nfs_server *server, struct pnfs_device *pdev,
gfp_t gfp_flags);
/*
* Re-resolve @lo's references to the changed deviceid @id. Called
* under @lo's inode i_lock inside an RCU read-side critical section:
* must not sleep, allocations are GFP_ATOMIC. Rather than put the
* references it gives up (the final put can sleep), the hook
* allocates an nfs4_deviceid_put per reference and queues it on
* @put_list for the caller to put and free. On allocation failure
* it must leave the reference in place.
*/
void (*reresolve_deviceid)(struct pnfs_layout_hdr *lo,
const struct nfs4_deviceid *id,
bool immediate,
struct list_head *put_list);
/*
* Does @lo hold any reference to deviceid @id? Called under
* @lo's inode i_lock; must not sleep.
*/
bool (*layout_references_deviceid)(struct pnfs_layout_hdr *lo,
const struct nfs4_deviceid *id);
int (*prepare_layoutreturn) (struct nfs4_layoutreturn_args *);
@@ -178,7 +197,8 @@ struct pnfs_layoutdriver_type {
int (*prepare_layoutcommit) (struct nfs4_layoutcommit_args *args);
int (*prepare_layoutstats) (struct nfs42_layoutstat_args *args);
void (*cancel_io)(struct pnfs_layout_segment *lseg);
void (*cancel_io)(struct pnfs_layout_segment *lseg,
const struct nfs4_deviceid *devid);
};
struct pnfs_commit_ops {
@@ -301,7 +321,8 @@ int pnfs_mark_matching_lsegs_invalid(struct pnfs_layout_hdr *lo,
int pnfs_mark_matching_lsegs_return(struct pnfs_layout_hdr *lo,
struct list_head *tmp_list,
const struct pnfs_layout_range *recall_range,
u32 seq, bool cancel_io);
u32 seq, bool cancel_io,
const struct nfs4_deviceid *devid);
int pnfs_mark_layout_stateid_invalid(struct pnfs_layout_hdr *lo,
struct list_head *lseg_list);
bool pnfs_roc(struct inode *ino, struct nfs4_layoutreturn_args *args,
@@ -351,9 +372,56 @@ int pnfs_read_done_resend_to_mds(struct nfs_pgio_header *);
int pnfs_write_done_resend_to_mds(struct nfs_pgio_header *);
struct nfs4_threshold *pnfs_mdsthreshold_alloc(void);
void pnfs_error_mark_layout_for_return(struct inode *inode,
struct pnfs_layout_segment *lseg);
struct pnfs_layout_segment *lseg,
const struct nfs4_deviceid *devid);
void pnfs_layout_return_unused_byclid(struct nfs_client *clp,
enum pnfs_iomode iomode);
void pnfs_layout_reresolve_deviceid_byclid(struct nfs_client *clp,
const struct pnfs_layoutdriver_type *ld,
const struct nfs4_deviceid *devid,
bool immediate);
bool pnfs_layout_deviceid_referenced_byclid(struct nfs_client *clp,
const struct pnfs_layoutdriver_type *ld,
const struct nfs4_deviceid *devid);
/*
* One live layout referencing a deviceID, collected for the
* CB_NOTIFY_DEVICEID DELETE recovery: the hdr is pinned, the inode
* igrab'd with its superblock active, and the layout stateid and
* cred snapshotted for TEST_STATEID.
*/
struct nfs4_deviceid_ref {
struct list_head node;
struct pnfs_layout_hdr *lo;
struct inode *inode;
nfs4_stateid stateid;
const struct cred *cred;
};
int pnfs_layout_collect_deviceid_refs(struct nfs_client *clp,
const struct pnfs_layoutdriver_type *ld,
const struct nfs4_deviceid *devid,
struct list_head *result);
void pnfs_layout_put_deviceid_refs(struct list_head *result);
/*
* A CB_NOTIFY_DEVICEID DELETE naming a deviceID that live layouts
* still reference (RFC 8881 Section 18.40.4). Queued on
* nfs_client.cl_deviceid_deletes under cl_lock for the state manager
* to resolve; holds a layoutdriver reference.
*/
struct nfs4_deviceid_delete {
struct list_head list;
const struct pnfs_layoutdriver_type *ld;
struct nfs4_deviceid id;
};
void pnfs_deviceid_delete_mark(struct nfs_client *clp,
const struct pnfs_layoutdriver_type *ld,
const struct nfs4_deviceid *id);
struct nfs4_deviceid_delete *pnfs_deviceid_delete_dequeue(
struct nfs_client *clp);
void pnfs_deviceid_delete_queue_free(struct nfs_client *clp);
int pnfs_layout_handle_reboot(struct nfs_client *clp);
/* nfs4_deviceid_flags */
@@ -376,15 +444,32 @@ struct nfs4_deviceid_node {
atomic_t ref;
};
/* One reference given up by reresolve_deviceid; nodes are shared, so a
* single pass can unpin the same node more than once.
*/
struct nfs4_deviceid_put {
struct list_head node;
struct nfs4_deviceid_node *dev;
};
struct nfs4_deviceid_node *
nfs4_find_get_deviceid(struct nfs_server *server,
const struct nfs4_deviceid *id, const struct cred *cred,
gfp_t gfp_mask);
void nfs4_delete_deviceid(const struct pnfs_layoutdriver_type *, const struct nfs_client *, const struct nfs4_deviceid *);
void nfs4_deviceid_bump_change_epoch(struct nfs_client *clp);
void nfs4_init_deviceid_node(struct nfs4_deviceid_node *, struct nfs_server *,
const struct nfs4_deviceid *);
bool nfs4_put_deviceid_node(struct nfs4_deviceid_node *);
void nfs4_mark_deviceid_available(struct nfs4_deviceid_node *node);
/* Put the device node reference carried by an in-flight I/O, if any */
static inline void pnfs_put_ds_dev(struct nfs4_deviceid_node *dev)
{
if (dev)
nfs4_put_deviceid_node(dev);
}
void nfs4_mark_deviceid_unavailable(struct nfs4_deviceid_node *node);
bool nfs4_test_deviceid_unavailable(struct nfs4_deviceid_node *node);
void nfs4_deviceid_purge_client(const struct nfs_client *);
@@ -422,11 +507,13 @@ struct nfs4_pnfs_ds *nfs4_pnfs_ds_add(const struct net *net,
void nfs4_pnfs_v3_ds_connect_unload(void);
int nfs4_pnfs_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds,
struct nfs4_deviceid_node *devid, unsigned int timeo,
unsigned int retrans, u32 version, u32 minor_version,
unsigned int retrans, unsigned int nconnect,
u32 version, u32 minor_version,
bool tightly_coupled);
struct nfs4_pnfs_ds_addr *nfs4_decode_mp_ds_addr(struct net *net,
struct xdr_stream *xdr,
gfp_t gfp_flags);
void nfs4_pnfs_ds_addr_list_free(struct list_head *dsaddrs);
void pnfs_layout_mark_request_commit(struct nfs_page *req,
struct pnfs_layout_segment *lseg,
struct nfs_commit_info *cinfo,
@@ -690,10 +777,11 @@ pnfs_lseg_request_intersecting(struct pnfs_layout_segment *lseg, struct nfs_page
}
static inline void pnfs_lseg_cancel_io(struct nfs_server *server,
struct pnfs_layout_segment *lseg)
struct pnfs_layout_segment *lseg,
const struct nfs4_deviceid *devid)
{
if (server->pnfs_curr_ld->cancel_io)
server->pnfs_curr_ld->cancel_io(lseg);
server->pnfs_curr_ld->cancel_io(lseg, devid);
}
extern unsigned int layoutstats_timer;
+29 -1
View File
@@ -40,8 +40,11 @@
/*
* Device ID RCU cache. A device ID is unique per server and layout type.
*
* 256 buckets keeps the chains short at the 1024-or-more devices a
* striping deployment expects, for 2KB of BSS on 64-bit.
*/
#define NFS4_DEVICE_ID_HASH_BITS 5
#define NFS4_DEVICE_ID_HASH_BITS 8
#define NFS4_DEVICE_ID_HASH_SIZE (1 << NFS4_DEVICE_ID_HASH_BITS)
#define NFS4_DEVICE_ID_HASH_MASK (NFS4_DEVICE_ID_HASH_SIZE - 1)
@@ -181,6 +184,21 @@ __nfs4_find_get_deviceid(struct nfs_server *server,
return d;
}
/*
* Bumped before the stale entry is unhashed, so an insert serialised
* after the unhash by nfs4_deviceid_lock observes the new epoch.
*/
void
nfs4_deviceid_bump_change_epoch(struct nfs_client *clp)
{
atomic_inc(&clp->cl_deviceid_change_epoch);
}
/* Discarding a raced reply is an optimisation, not a correctness
* requirement, and the epoch moves at the server's rate: bound it.
*/
#define NFS4_DEVICEID_FETCH_RETRIES 3
struct nfs4_deviceid_node *
nfs4_find_get_deviceid(struct nfs_server *server,
const struct nfs4_deviceid *id, const struct cred *cred,
@@ -188,11 +206,14 @@ nfs4_find_get_deviceid(struct nfs_server *server,
{
long hash = nfs4_deviceid_hash(id);
struct nfs4_deviceid_node *d, *new;
int epoch, tries = 0;
retry:
d = __nfs4_find_get_deviceid(server, id, hash);
if (d)
goto found;
epoch = atomic_read(&server->nfs_client->cl_deviceid_change_epoch);
new = nfs4_get_device_info(server, id, cred, gfp_mask);
if (!new) {
trace_nfs4_find_deviceid(server, id, -ENOENT);
@@ -200,6 +221,13 @@ nfs4_find_get_deviceid(struct nfs_server *server,
}
spin_lock(&nfs4_deviceid_lock);
if (atomic_read(&server->nfs_client->cl_deviceid_change_epoch) != epoch &&
++tries <= NFS4_DEVICEID_FETCH_RETRIES) {
/* a mapping changed while we fetched; ours may be stale */
spin_unlock(&nfs4_deviceid_lock);
server->pnfs_curr_ld->free_deviceid_node(new);
goto retry;
}
d = __nfs4_find_get_deviceid(server, id, hash);
if (d) {
spin_unlock(&nfs4_deviceid_lock);
+86 -22
View File
@@ -15,6 +15,8 @@
#include "nfs4session.h"
#include "internal.h"
#include <linux/hash.h>
#include <linux/jhash.h>
#include "pnfs.h"
#include "netns.h"
#include "nfs4trace.h"
@@ -55,6 +57,7 @@ void pnfs_generic_commit_release(void *calldata)
struct nfs_commit_data *data = calldata;
data->completion_ops->completion(data);
pnfs_put_ds_dev(data->ds_dev);
pnfs_put_lseg(data->lseg);
nfs_put_client(data->ds_clp);
nfs_commitdata_release(data);
@@ -576,8 +579,8 @@ same_sockaddr(struct sockaddr *addr1, struct sockaddr *addr2)
}
/*
* Checks if 'dsaddrs1' contains a subset of 'dsaddrs2'. If it does,
* declare a match.
* Checks if 'dsaddrs1' and 'dsaddrs2' hold the same set of addresses.
* If they do, declare a match.
*/
static bool
_same_data_server_addrs_locked(const struct list_head *dsaddrs1,
@@ -587,6 +590,10 @@ _same_data_server_addrs_locked(const struct list_head *dsaddrs1,
struct sockaddr *sa1, *sa2;
bool match = false;
if (list_count_nodes((struct list_head *)dsaddrs1) !=
list_count_nodes((struct list_head *)dsaddrs2))
return false;
list_for_each_entry(da1, dsaddrs1, da_node) {
sa1 = (struct sockaddr *)&da1->da_addr;
match = false;
@@ -602,16 +609,58 @@ _same_data_server_addrs_locked(const struct list_head *dsaddrs1,
return match;
}
/* Hash family, address bytes, and port - as same_sockaddr() */
static u32
nfs4_ds_addr_hash(const struct sockaddr *sa)
{
u32 h = sa->sa_family;
switch (sa->sa_family) {
case AF_INET: {
const struct sockaddr_in *a = (const struct sockaddr_in *)sa;
h = jhash(&a->sin_addr.s_addr, sizeof(a->sin_addr.s_addr), h);
h = jhash(&a->sin_port, sizeof(a->sin_port), h);
break;
}
case AF_INET6: {
const struct sockaddr_in6 *a = (const struct sockaddr_in6 *)sa;
h = jhash(&a->sin6_addr, sizeof(a->sin6_addr), h);
h = jhash(&a->sin6_port, sizeof(a->sin6_port), h);
break;
}
}
return h;
}
/*
* Lookup DS by addresses and NFS version. nfs4_ds_cache_lock is held
* Bucket index for a DS cache key. Per-address hashes combine by
* addition so the multipath list order cannot change the bucket,
* matching the order-independent set comparison above.
*/
static u32
nfs4_ds_cache_hash(const struct list_head *dsaddrs, u32 version)
{
const struct nfs4_pnfs_ds_addr *da;
u32 h = 0;
list_for_each_entry(da, dsaddrs, da_node)
h += nfs4_ds_addr_hash((const struct sockaddr *)&da->da_addr);
return hash_32(jhash_1word(version, h), NFS4_DS_CACHE_HASH_BITS);
}
/*
* Lookup DS by addresses and NFS version. nfs4_data_server_lock is held
*/
static struct nfs4_pnfs_ds *
_data_server_lookup_locked(const struct nfs_net *nn,
const struct list_head *dsaddrs, u32 version)
{
struct nfs4_pnfs_ds *ds;
u32 bucket = nfs4_ds_cache_hash(dsaddrs, version);
list_for_each_entry(ds, &nn->nfs4_data_server_cache, ds_node)
hlist_for_each_entry(ds, &nn->nfs4_data_server_cache[bucket], ds_node)
if (ds->ds_version == version &&
_same_data_server_addrs_locked(&ds->ds_addrs, dsaddrs))
return ds;
@@ -633,23 +682,28 @@ static void nfs4_pnfs_ds_addr_free(struct nfs4_pnfs_ds_addr *da)
kfree(da);
}
static void destroy_ds(struct nfs4_pnfs_ds *ds)
void nfs4_pnfs_ds_addr_list_free(struct list_head *dsaddrs)
{
struct nfs4_pnfs_ds_addr *da;
while (!list_empty(dsaddrs)) {
da = list_first_entry(dsaddrs, struct nfs4_pnfs_ds_addr,
da_node);
list_del_init(&da->da_node);
nfs4_pnfs_ds_addr_free(da);
}
}
EXPORT_SYMBOL_GPL(nfs4_pnfs_ds_addr_list_free);
static void destroy_ds(struct nfs4_pnfs_ds *ds)
{
dprintk("--> %s\n", __func__);
ifdebug(FACILITY)
print_ds(ds);
nfs_put_client(ds->ds_clp);
while (!list_empty(&ds->ds_addrs)) {
da = list_first_entry(&ds->ds_addrs,
struct nfs4_pnfs_ds_addr,
da_node);
list_del_init(&da->da_node);
nfs4_pnfs_ds_addr_free(da);
}
nfs4_pnfs_ds_addr_list_free(&ds->ds_addrs);
kfree(ds->ds_remotestr);
kfree(ds);
@@ -660,7 +714,7 @@ void nfs4_pnfs_ds_put(struct nfs4_pnfs_ds *ds)
struct nfs_net *nn = net_generic(ds->ds_net, nfs_net_id);
if (refcount_dec_and_lock(&ds->ds_count, &nn->nfs4_data_server_lock)) {
list_del_init(&ds->ds_node);
hlist_del_init(&ds->ds_node);
spin_unlock(&nn->nfs4_data_server_lock);
destroy_ds(ds);
}
@@ -726,6 +780,7 @@ nfs4_pnfs_ds_add(const struct net *net, struct list_head *dsaddrs, u32 version,
{
struct nfs_net *nn = net_generic(net, nfs_net_id);
struct nfs4_pnfs_ds *tmp_ds, *ds = NULL;
struct hlist_head *bucket;
char *remotestr;
if (list_empty(dsaddrs)) {
@@ -739,6 +794,8 @@ nfs4_pnfs_ds_add(const struct net *net, struct list_head *dsaddrs, u32 version,
/* this is only used for debugging, so it's ok if its NULL */
remotestr = nfs4_pnfs_remotestr(dsaddrs, gfp_flags);
/* @dsaddrs is empty after the splice below. */
bucket = &nn->nfs4_data_server_cache[nfs4_ds_cache_hash(dsaddrs, version)];
spin_lock(&nn->nfs4_data_server_lock);
tmp_ds = _data_server_lookup_locked(nn, dsaddrs, version);
@@ -747,11 +804,11 @@ nfs4_pnfs_ds_add(const struct net *net, struct list_head *dsaddrs, u32 version,
list_splice_init(dsaddrs, &ds->ds_addrs);
ds->ds_remotestr = remotestr;
refcount_set(&ds->ds_count, 1);
INIT_LIST_HEAD(&ds->ds_node);
INIT_HLIST_NODE(&ds->ds_node);
ds->ds_net = net;
ds->ds_clp = NULL;
ds->ds_version = version;
list_add(&ds->ds_node, &nn->nfs4_data_server_cache);
hlist_add_head(&ds->ds_node, bucket);
dprintk("%s add new data server %s\n", __func__,
ds->ds_remotestr);
} else {
@@ -787,7 +844,8 @@ static struct nfs_client *(*get_v3_ds_connect)(
int ds_addrlen,
int ds_proto,
unsigned int ds_timeo,
unsigned int ds_retrans);
unsigned int ds_retrans,
unsigned int ds_nconnect);
static bool load_v3_ds_connect(void)
{
@@ -810,7 +868,8 @@ void nfs4_pnfs_v3_ds_connect_unload(void)
static int _nfs4_pnfs_v3_ds_connect(struct nfs_server *mds_srv,
struct nfs4_pnfs_ds *ds,
unsigned int timeo,
unsigned int retrans)
unsigned int retrans,
unsigned int nconnect)
{
struct nfs_client *clp = ERR_PTR(-EIO);
struct nfs_client *mds_clp = mds_srv->nfs_client;
@@ -862,7 +921,7 @@ static int _nfs4_pnfs_v3_ds_connect(struct nfs_server *mds_srv,
ds_proto = XPRT_TRANSPORT_TCP_TLS;
clp = get_v3_ds_connect(mds_srv, &da->da_addr, da->da_addrlen,
ds_proto, timeo, retrans);
ds_proto, timeo, retrans, nconnect);
if (IS_ERR(clp))
continue;
clp->cl_rpcclient->cl_softerr = 0;
@@ -885,6 +944,7 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
struct nfs4_pnfs_ds *ds,
unsigned int timeo,
unsigned int retrans,
unsigned int nconnect,
u32 minor_version,
bool tightly_coupled)
{
@@ -976,7 +1036,8 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
clp = nfs4_set_ds_client(mds_srv, &da->da_addr,
da->da_addrlen, ds_proto,
timeo, retrans, minor_version,
timeo, retrans, nconnect,
minor_version,
tightly_coupled);
if (IS_ERR(clp))
continue;
@@ -1011,7 +1072,8 @@ out:
*/
int nfs4_pnfs_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds,
struct nfs4_deviceid_node *devid, unsigned int timeo,
unsigned int retrans, u32 version, u32 minor_version,
unsigned int retrans, unsigned int nconnect,
u32 version, u32 minor_version,
bool tightly_coupled)
{
int err;
@@ -1031,11 +1093,13 @@ int nfs4_pnfs_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds,
switch (version) {
case 3:
err = _nfs4_pnfs_v3_ds_connect(mds_srv, ds, timeo, retrans);
err = _nfs4_pnfs_v3_ds_connect(mds_srv, ds, timeo, retrans,
nconnect);
break;
case 4:
err = _nfs4_pnfs_v4_ds_connect(mds_srv, ds, timeo, retrans,
minor_version, tightly_coupled);
nconnect, minor_version,
tightly_coupled);
break;
default:
dprintk("%s: unsupported DS version %d\n", __func__, version);
+8
View File
@@ -323,9 +323,17 @@ nfs_proc_remove(struct inode *dir, struct dentry *dentry)
.rpc_argp = &arg,
};
int status;
struct inode *inode = d_inode(dentry);
unsigned long gencount;
dprintk("NFS call remove %pd2\n",dentry);
if (inode)
gencount = READ_ONCE(NFS_I(inode)->attr_gencount);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
if (status == 0 && inode)
nfs_drop_nlink(inode, gencount);
nfs_mark_for_revalidate(dir);
dprintk("NFS reply remove: %d\n", status);
+1 -1
View File
@@ -324,7 +324,7 @@ int nfs_read_add_folio(struct nfs_pageio_descriptor *pgio,
aligned_len = min_t(unsigned int, ALIGN(len, rsize), fsize);
new = nfs_page_create_from_folio(ctx, folio, 0, aligned_len);
new = nfs_page_create_from_folio(ctx, folio, false, 0, aligned_len);
if (IS_ERR(new)) {
error = PTR_ERR(new);
if (nfs_netfs_folio_unlock(folio))
+1 -1
View File
@@ -1087,7 +1087,7 @@ static struct nfs_page *nfs_setup_write_request(struct nfs_open_context *ctx,
req = nfs_try_to_update_request(folio, offset, bytes);
if (req != NULL)
goto out;
req = nfs_page_create_from_folio(ctx, folio, offset, bytes);
req = nfs_page_create_from_folio(ctx, folio, false, offset, bytes);
if (IS_ERR(req))
goto out;
nfs_inode_add_request(req);
+85 -16
View File
@@ -24,6 +24,26 @@ static DEFINE_SPINLOCK(nfs_uuids_lock);
* that is protected by nfs_uuids_lock.
*/
static LIST_HEAD(nfs_uuids);
/* Namespace registration order, protected by nfs_uuids_lock. */
static u64 nfs_uuid_seq;
/* The owner waits for temporary users before freeing its nfs_client. */
static void nfs_uuid_get(nfs_uuid_t *nfs_uuid)
{
refcount_inc(&nfs_uuid->refs);
}
static void nfs_uuid_release(nfs_uuid_t *nfs_uuid)
{
bool released;
spin_lock(&nfs_uuid->lock);
released = refcount_dec_and_test(&nfs_uuid->refs);
wake_up_var_locked(&nfs_uuid->refs, &nfs_uuid->lock);
spin_unlock(&nfs_uuid->lock);
if (released)
complete(&nfs_uuid->released);
}
/*
* Lock ordering:
@@ -43,6 +63,8 @@ void nfs_uuid_init(nfs_uuid_t *nfs_uuid)
INIT_LIST_HEAD(&nfs_uuid->list);
INIT_LIST_HEAD(&nfs_uuid->files);
spin_lock_init(&nfs_uuid->lock);
refcount_set(&nfs_uuid->refs, 1);
init_completion(&nfs_uuid->released);
nfs_uuid->nfs3_localio_probe_count = 0;
}
EXPORT_SYMBOL_GPL(nfs_uuid_init);
@@ -50,7 +72,12 @@ EXPORT_SYMBOL_GPL(nfs_uuid_init);
bool nfs_uuid_begin(nfs_uuid_t *nfs_uuid)
{
spin_lock(&nfs_uuid->lock);
if (rcu_access_pointer(nfs_uuid->net)) {
/* The caller retains the client while temporary UUID users drain. */
wait_var_event_spinlock(&nfs_uuid->refs,
refcount_read(&nfs_uuid->refs) <= 1,
&nfs_uuid->lock);
if (rcu_access_pointer(nfs_uuid->net) || nfs_uuid->list_lock ||
refcount_read(&nfs_uuid->refs) != 1) {
/* This nfs_uuid is already in use */
spin_unlock(&nfs_uuid->lock);
return false;
@@ -77,7 +104,7 @@ void nfs_uuid_end(nfs_uuid_t *nfs_uuid)
{
if (!rcu_access_pointer(nfs_uuid->net)) {
spin_lock(&nfs_uuid->lock);
if (!rcu_access_pointer(nfs_uuid->net)) {
if (!rcu_access_pointer(nfs_uuid->net) && !nfs_uuid->list_lock) {
/* Not local, remove from nfs_uuids */
spin_lock(&nfs_uuids_lock);
list_del_init(&nfs_uuid->list);
@@ -113,28 +140,37 @@ void nfs_uuid_is_local(const uuid_t *uuid, struct list_head *list,
spin_unlock(&nfs_uuids_lock);
return;
}
/* Drop the lookup lock before taking nfs_uuid->lock. */
nfs_uuid_get(nfs_uuid);
spin_unlock(&nfs_uuids_lock);
spin_lock(&nfs_uuid->lock);
spin_lock(&nfs_uuids_lock);
/* nfs_uuid_end() may have withdrawn the probe in the meantime. */
if (nfs_uuid_lookup_locked(uuid) != nfs_uuid)
goto out_unlock;
/*
* We don't hold a ref on the net, but instead put
* ourselves on @list (nn->local_clients) so the net
* pointer can be invalidated.
*/
spin_lock(list_lock); /* list_lock is nn->local_clients_lock */
list_move(&nfs_uuid->list, list);
spin_unlock(list_lock);
spin_unlock(&nfs_uuids_lock);
/* Once nfs_uuid is parented to @list, avoid global nfs_uuids_lock */
spin_lock(&nfs_uuid->lock);
__module_get(mod);
nfsd_mod = mod;
spin_lock(list_lock); /* list_lock is nn->local_clients_lock */
nfs_uuid->list_lock = list_lock;
kref_get(&dom->ref);
nfs_uuid->dom = dom;
rcu_assign_pointer(nfs_uuid->net, net);
nfs_uuid->seq = ++nfs_uuid_seq;
list_move_tail(&nfs_uuid->list, list);
spin_unlock(list_lock);
out_unlock:
spin_unlock(&nfs_uuids_lock);
spin_unlock(&nfs_uuid->lock);
nfs_uuid_release(nfs_uuid);
}
EXPORT_SYMBOL_GPL(nfs_uuid_is_local);
@@ -154,6 +190,10 @@ static bool nfs_uuid_put(nfs_uuid_t *nfs_uuid)
struct nfs_file_localio *nfl;
spin_lock(&nfs_uuid->lock);
/* A concurrent disabler can drop the lock while closing files. */
wait_var_event_spinlock(&nfs_uuid->list_lock,
rcu_access_pointer(nfs_uuid->net) ||
!nfs_uuid->list_lock, &nfs_uuid->lock);
if (unlikely(!rcu_access_pointer(nfs_uuid->net))) {
spin_unlock(&nfs_uuid->lock);
return false;
@@ -211,6 +251,7 @@ static bool nfs_uuid_put(nfs_uuid_t *nfs_uuid)
}
module_put(nfsd_mod);
wake_up_var_locked(&nfs_uuid->list_lock, &nfs_uuid->lock);
spin_unlock(&nfs_uuid->lock);
return true;
@@ -223,22 +264,50 @@ void nfs_localio_disable_client(struct nfs_client *clp)
}
EXPORT_SYMBOL_GPL(nfs_localio_disable_client);
void nfs_localio_free_client(struct nfs_client *clp)
{
nfs_uuid_t *nfs_uuid = &clp->cl_uuid;
nfs_uuid_end(nfs_uuid);
nfs_localio_disable_client(clp);
/* Unlink before dropping the owner reference, then drain all users. */
nfs_uuid_release(nfs_uuid);
wait_for_completion(&nfs_uuid->released);
}
EXPORT_SYMBOL_GPL(nfs_localio_free_client);
void nfs_localio_invalidate_clients(struct list_head *nn_local_clients,
spinlock_t *nn_local_clients_lock)
{
LIST_HEAD(local_clients);
nfs_uuid_t *nfs_uuid, *tmp;
nfs_uuid_t *nfs_uuid;
struct nfs_client *clp;
u64 last_seq;
spin_lock(nn_local_clients_lock);
list_splice_init(nn_local_clients, &local_clients);
spin_unlock(nn_local_clients_lock);
list_for_each_entry_safe(nfs_uuid, tmp, &local_clients, list) {
if (WARN_ON(nfs_uuid->list_lock != nn_local_clients_lock))
if (list_empty(nn_local_clients))
goto out_unlock;
/* New registrations go to the tail and belong to a later sweep. */
last_seq = list_last_entry(nn_local_clients, nfs_uuid_t, list)->seq;
for (;;) {
nfs_uuid = list_first_entry_or_null(nn_local_clients,
nfs_uuid_t, list);
/* Use a signed difference to allow the sequence to wrap. */
if (!nfs_uuid || (s64)(nfs_uuid->seq - last_seq) > 0)
break;
if (WARN_ON_ONCE(nfs_uuid->list_lock != nn_local_clients_lock))
break;
/* Teardown unlinks under this lock before dropping its reference. */
nfs_uuid_get(nfs_uuid);
spin_unlock(nn_local_clients_lock);
clp = container_of(nfs_uuid, struct nfs_client, cl_uuid);
nfs_localio_disable_client(clp);
nfs_uuid_release(nfs_uuid);
cond_resched();
spin_lock(nn_local_clients_lock);
}
out_unlock:
spin_unlock(nn_local_clients_lock);
}
EXPORT_SYMBOL_GPL(nfs_localio_invalidate_clients);
+8
View File
@@ -74,6 +74,8 @@ struct nfs_client {
u64 cl_clientid; /* constant */
nfs4_verifier cl_confirm; /* Clientid verifier */
unsigned long cl_state;
/* bumped on each CB_NOTIFY_DEVICEID CHANGE for this client */
atomic_t cl_deviceid_change_epoch;
spinlock_t cl_lock;
@@ -101,6 +103,8 @@ struct nfs_client {
/* The flags used for obtaining the clientid during EXCHANGE_ID */
u32 cl_exchange_flags;
struct nfs4_session *cl_session; /* shared session */
/* CB_NOTIFY_DEVICEID DELETE suspects, protected by cl_lock */
struct list_head cl_deviceid_deletes;
bool cl_preserve_clid;
struct nfs41_server_owner *cl_serverowner;
struct nfs41_server_scope *cl_serverscope;
@@ -248,6 +252,10 @@ struct nfs_server {
that are supported on this
filesystem */
struct pnfs_layoutdriver_type *pnfs_curr_ld; /* Active layout driver */
unsigned int lg_reply_sz; /* Learned LAYOUTGET reply
buffer size, when the layout
driver's default has proved
too small */
struct rpc_wait_queue roc_rpcwaitq;
/* the following fields are protected by nfs_client->cl_lock */
+6 -2
View File
@@ -38,6 +38,7 @@ enum {
PG_REMOVE, /* page group sync bit in write path */
PG_CONTENDED1, /* Is someone waiting for a lock? */
PG_CONTENDED2, /* Is someone waiting for a lock? */
PG_PINNED, /* page is pinned by GUP */
};
struct nfs_inode;
@@ -58,6 +59,7 @@ struct nfs_page {
struct nfs_page *wb_this_page; /* list of reqs for this page */
struct nfs_page *wb_head; /* head pointer for req list */
unsigned short wb_nio; /* Number of I/O attempts */
unsigned int wb_nr_pinned; /* Number of pinned pages */
};
struct nfs_pgio_mirror;
@@ -125,15 +127,17 @@ struct nfs_pageio_descriptor {
extern struct nfs_page *nfs_page_create_from_page(struct nfs_open_context *ctx,
struct page *page,
bool pinned,
unsigned int pgbase,
loff_t offset,
unsigned int count);
extern struct nfs_page *nfs_page_create_from_folio(struct nfs_open_context *ctx,
struct folio *folio,
bool pinned,
unsigned int offset,
unsigned int count);
extern void nfs_release_request(struct nfs_page *);
void nfs_release_request(struct nfs_page *req);
void nfs_release_request_list(struct list_head *head);
extern void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
struct inode *inode,
+2
View File
@@ -1693,6 +1693,7 @@ struct nfs_pgio_header {
struct nfs_client *ds_clp; /* pNFS data server */
u32 ds_commit_idx; /* ds index if ds_clp is set */
u32 pgio_mirror_idx;/* mirror index in pgio layer */
struct nfs4_deviceid_node *ds_dev; /* device node ref held across the I/O */
};
struct nfs_mds_commit_info {
@@ -1731,6 +1732,7 @@ struct nfs_commit_data {
struct nfs_open_context *context;
struct pnfs_layout_segment *lseg;
struct nfs_client *ds_clp; /* pNFS data server */
struct nfs4_deviceid_node *ds_dev; /* device node ref held across the commit */
int ds_commit_index;
loff_t lwb;
const struct rpc_call_ops *mds_ops;
+12
View File
@@ -8,8 +8,10 @@
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
#include <linux/completion.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/refcount.h>
#include <linux/uuid.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svcauth.h>
@@ -37,6 +39,11 @@ struct nfs_file_localio;
typedef struct {
uuid_t uuid;
unsigned nfs3_localio_probe_count;
/* Owner reference plus temporary references held by nfs_common */
refcount_t refs;
struct completion released;
/* Namespace registration order, protected by list_lock */
u64 seq;
/* this struct is over a cacheline, avoid bouncing */
spinlock_t ____cacheline_aligned lock;
struct list_head list;
@@ -55,6 +62,7 @@ void nfs_uuid_is_local(const uuid_t *, struct list_head *, spinlock_t *,
void nfs_localio_enable_client(struct nfs_client *clp);
void nfs_localio_disable_client(struct nfs_client *clp);
void nfs_localio_free_client(struct nfs_client *clp);
void nfs_localio_invalidate_clients(struct list_head *nn_local_clients,
spinlock_t *nn_local_clients_lock);
@@ -127,6 +135,10 @@ static inline void nfs_localio_disable_client(struct nfs_client *clp)
{
}
static inline void nfs_localio_free_client(struct nfs_client *clp)
{
}
#endif /* CONFIG_NFS_LOCALIO */
#endif /* __LINUX_NFSLOCALIO_H */
+1
View File
@@ -145,6 +145,7 @@ struct xprtsec_parms {
enum xprtsec_policies policy;
/* authentication material */
key_serial_t keyring_serial;
key_serial_t cert_serial;
key_serial_t privkey_serial;
};
+34 -5
View File
@@ -984,12 +984,16 @@ void rpcrdma_reset_cwnd(struct rpcrdma_xprt *r_xprt)
* @rqst: controlling RPC request
* @srcp: points to RPC message payload in receive buffer
* @copy_len: remaining length of receive buffer content
* @pad: Write chunk pad bytes needed (zero for pure inline)
* @writelist: bytes conveyed by the Write chunk (zero for pure inline)
*
* The upper layer has set the maximum number of bytes it can
* receive in each component of rq_rcv_buf. These values are set in
* the head.iov_len, page_len, tail.iov_len, and buflen fields.
*
* For READ replies with a Write chunk, the payload is already in
* the page list. An overestimated head length and buflen can be
* reduced without moving reply data.
*
* Unlike the TCP equivalent (xdr_partial_copy_from_skb), in
* many cases this function simply updates iov_base pointers in
* rq_rcv_buf to point directly to the received reply data, to
@@ -998,13 +1002,16 @@ void rpcrdma_reset_cwnd(struct rpcrdma_xprt *r_xprt)
* Returns the count of bytes which had to be memcopied.
*/
static unsigned long
rpcrdma_inline_fixup(struct rpc_rqst *rqst, char *srcp, int copy_len, int pad)
rpcrdma_inline_fixup(struct rpc_rqst *rqst, char *srcp, int copy_len,
u32 writelist)
{
unsigned long fixup_copy_count;
int i, npages, curlen;
char *destp;
struct page **ppages;
int page_base;
int pad = writelist & 3;
unsigned int delta;
/* The head iovec is redirected to the RPC reply message
* in the receive buffer, to avoid a memcopy.
@@ -1016,8 +1023,20 @@ rpcrdma_inline_fixup(struct rpc_rqst *rqst, char *srcp, int copy_len, int pad)
* head.iov_len bytes are copied into the page list.
*/
curlen = rqst->rq_rcv_buf.head[0].iov_len;
if (curlen > copy_len)
if (curlen > copy_len) {
/* These READ replies have no inline fields after the payload.
* Trim the head without moving the directly-placed data.
*/
if (writelist) {
delta = curlen - copy_len;
rqst->rq_rcv_buf.head[0].iov_len = copy_len;
rqst->rq_private_buf.head[0].iov_len = copy_len;
rqst->rq_rcv_buf.buflen -= delta;
rqst->rq_private_buf.buflen -= delta;
}
curlen = copy_len;
}
srcp += curlen;
copy_len -= curlen;
@@ -1243,9 +1262,19 @@ rpcrdma_decode_msg(struct rpcrdma_xprt *r_xprt, struct rpcrdma_rep *rep,
/* Build the RPC reply's Payload stream in rqst->rq_rcv_buf */
base = (char *)xdr_inline_decode(xdr, 0);
rpclen = xdr_stream_remaining(xdr);
rpclen = rep->rr_hdrbuf.len - xdr_stream_pos(xdr);
if (writelist)
rpclen &= ~3;
if (unlikely(!rpclen))
return -EIO;
if (writelist && rpclen < rqst->rq_rcv_buf.head[0].iov_len) {
rqst->rq_rcv_buf.head[0].iov_len = rpclen;
rqst->rq_private_buf.head[0].iov_len = rpclen;
}
r_xprt->rx_stats.fixup_copy_count +=
rpcrdma_inline_fixup(rqst, base, rpclen, writelist & 3);
rpcrdma_inline_fixup(rqst, base, rpclen,
xdr_pad_size(writelist));
r_xprt->rx_stats.total_rdma_reply += writelist;
return rpclen + xdr_align_size(writelist);
+6 -3
View File
@@ -114,9 +114,9 @@ static void rpcrdma_xprt_drain(struct rpcrdma_xprt *r_xprt)
rpcrdma_ep_put(ep);
}
/* Ensure xprt_force_disconnect() is invoked exactly once when a
* connection is closed or lost. (The important thing is it needs
* to be invoked "at least" once).
/* Ensure xprt_force_disconnect() is invoked at most once for an
* unexpected endpoint failure. Local endpoint teardown closes this
* gate before it can generate CM events and flushed completions.
*/
void rpcrdma_force_disconnect(struct rpcrdma_ep *ep)
{
@@ -587,6 +587,9 @@ void rpcrdma_xprt_disconnect(struct rpcrdma_xprt *r_xprt)
if (!ep)
return;
/* Prevent teardown-generated events from requesting another close. */
atomic_xchg(&ep->re_force_disconnect, 1);
id = ep->re_id;
rc = rdma_disconnect(id);
trace_xprtrdma_disconnect(r_xprt, rc);
+36 -34
View File
@@ -356,35 +356,6 @@ xs_alloc_sparse_pages(struct xdr_buf *buf, size_t want, gfp_t gfp)
return want;
}
static int
xs_sock_process_cmsg(struct socket *sock, struct msghdr *msg,
unsigned int *msg_flags, struct cmsghdr *cmsg, int ret)
{
u8 content_type = tls_get_record_type(sock->sk, cmsg);
u8 level, description;
switch (content_type) {
case 0:
break;
case TLS_RECORD_TYPE_DATA:
/* TLS sets EOR at the end of each application data
* record, even though there might be more frames
* waiting to be decrypted.
*/
*msg_flags &= ~MSG_EOR;
break;
case TLS_RECORD_TYPE_ALERT:
tls_alert_recv(sock->sk, msg, &level, &description);
ret = (level == TLS_ALERT_LEVEL_FATAL) ?
-EACCES : -EAGAIN;
break;
default:
/* discard this record type */
ret = -EAGAIN;
}
return ret;
}
static int
xs_sock_recv_cmsg(struct socket *sock, unsigned int *msg_flags, int flags)
{
@@ -402,16 +373,42 @@ xs_sock_recv_cmsg(struct socket *sock, unsigned int *msg_flags, int flags)
.msg_control = &u,
.msg_controllen = sizeof(u),
};
u8 level, description;
int ret;
iov_iter_kvec(&msg.msg_iter, ITER_DEST, &alert_kvec, 1,
alert_kvec.iov_len);
ret = sock_recvmsg(sock, &msg, flags);
if (ret > 0) {
if (tls_get_record_type(sock->sk, &u.cmsg) == TLS_RECORD_TYPE_ALERT)
iov_iter_revert(&msg.msg_iter, ret);
ret = xs_sock_process_cmsg(sock, &msg, msg_flags, &u.cmsg,
-EAGAIN);
/* put_cmsg() shrinks msg_controllen, so a short one means
* kTLS filled in u.cmsg.
*/
if (ret >= 0 && msg.msg_controllen < sizeof(u)) {
if (tls_get_record_type(sock->sk, &u.cmsg) !=
TLS_RECORD_TYPE_ALERT)
return -EAGAIN;
/* RFC 8446 Section 5.1: a record with an Alert type carries
* exactly one message, and an alert is two octets.
* tls_alert_recv() reads both without checking the length.
* alert_kvec caps the count at two, so a longer record
* fills it as well. kTLS sets MSG_EOR only once the
* record has been drained.
*/
if (ret != sizeof(alert) || !(msg.msg_flags & MSG_EOR))
return -EACCES;
iov_iter_revert(&msg.msg_iter, ret);
tls_alert_recv(sock->sk, &msg, &level, &description);
/* RFC 8446 Section 6: every alert but a closure alert is
* an error alert, whatever the legacy AlertLevel octet
* says.
*/
switch (description) {
case TLS_ALERT_DESC_CLOSE_NOTIFY:
case TLS_ALERT_DESC_USER_CANCELED:
ret = -EAGAIN;
break;
default:
ret = -EACCES;
}
}
return ret;
}
@@ -425,6 +422,10 @@ xs_sock_recvmsg(struct socket *sock, struct msghdr *msg, int flags, size_t seek)
ret = sock_recvmsg(sock, msg, flags);
/* Handle TLS inband control message lazily */
if (msg->msg_flags & MSG_CTRUNC) {
/* TLS sets EOR at the end of each application data
* record, even though there might be more frames
* waiting to be decrypted.
*/
msg->msg_flags &= ~(MSG_CTRUNC | MSG_EOR);
if (ret == 0 || ret == -EIO)
ret = xs_sock_recv_cmsg(sock, &msg->msg_flags, flags);
@@ -2636,6 +2637,7 @@ static int xs_tls_handshake_sync(struct rpc_xprt *lower_xprt, struct xprtsec_par
goto out_put_xprt;
break;
case RPC_XPRTSEC_TLS_X509:
args.ta_keyring = xprtsec->keyring_serial;
args.ta_my_cert = xprtsec->cert_serial;
args.ta_my_privkey = xprtsec->privkey_serial;
rc = tls_client_hello_x509(&args, GFP_KERNEL);