aboutsummaryrefslogtreecommitdiff
path: root/fs/nfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/blocklayout/dev.c6
-rw-r--r--fs/nfs/delegation.c7
-rw-r--r--fs/nfs/dir.c78
-rw-r--r--fs/nfs/file.c3
-rw-r--r--fs/nfs/flexfilelayout/flexfilelayoutdev.c2
-rw-r--r--fs/nfs/inode.c10
-rw-r--r--fs/nfs/io.c2
-rw-r--r--fs/nfs/localio.c32
-rw-r--r--fs/nfs/nfs42proc.c29
-rw-r--r--fs/nfs/nfs4proc.c53
-rw-r--r--fs/nfs/nfs4state.c6
-rw-r--r--fs/nfs/nfstrace.h3
-rw-r--r--fs/nfs/pnfs.c58
-rw-r--r--fs/nfs/pnfs.h17
-rw-r--r--fs/nfs/write.c33
15 files changed, 238 insertions, 101 deletions
diff --git a/fs/nfs/blocklayout/dev.c b/fs/nfs/blocklayout/dev.c
index ab76120705e2..134d7f760a33 100644
--- a/fs/nfs/blocklayout/dev.c
+++ b/fs/nfs/blocklayout/dev.c
@@ -417,8 +417,10 @@ bl_parse_scsi(struct nfs_server *server, struct pnfs_block_dev *d,
d->map = bl_map_simple;
d->pr_key = v->scsi.pr_key;
- if (d->len == 0)
- return -ENODEV;
+ if (d->len == 0) {
+ error = -ENODEV;
+ goto out_blkdev_put;
+ }
ops = bdev->bd_disk->fops->pr_ops;
if (!ops) {
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index 2248e3ad089a..8a3857a49d84 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -149,7 +149,7 @@ static int nfs4_do_check_delegation(struct inode *inode, fmode_t type,
int nfs4_have_delegation(struct inode *inode, fmode_t type, int flags)
{
if (S_ISDIR(inode->i_mode) && !directory_delegations)
- nfs_inode_evict_delegation(inode);
+ nfs4_inode_set_return_delegation_on_close(inode);
return nfs4_do_check_delegation(inode, type, flags, true);
}
@@ -581,6 +581,10 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
if (delegation == NULL)
return 0;
+ /* Directory delegations don't require any state recovery */
+ if (!S_ISREG(inode->i_mode))
+ goto out_return;
+
if (!issync)
mode |= O_NONBLOCK;
/* Recall of any remaining application leases */
@@ -604,6 +608,7 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
goto out;
}
+out_return:
err = nfs_do_return_delegation(inode, delegation, issync);
out:
/* Refcount matched in nfs_start_delegation_return_locked() */
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 23a78a742b61..8f9ea79b7882 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -1440,7 +1440,8 @@ static void nfs_set_verifier_locked(struct dentry *dentry, unsigned long verf)
if (!dir || !nfs_verify_change_attribute(dir, verf))
return;
- if (inode && NFS_PROTO(inode)->have_delegation(inode, FMODE_READ, 0))
+ if (NFS_PROTO(dir)->have_delegation(dir, FMODE_READ, 0) ||
+ (inode && NFS_PROTO(inode)->have_delegation(inode, FMODE_READ, 0)))
nfs_set_verifier_delegated(&verf);
dentry->d_time = verf;
}
@@ -1465,6 +1466,49 @@ void nfs_set_verifier(struct dentry *dentry, unsigned long verf)
EXPORT_SYMBOL_GPL(nfs_set_verifier);
#if IS_ENABLED(CONFIG_NFS_V4)
+static void nfs_clear_verifier_file(struct inode *inode)
+{
+ struct dentry *alias;
+ struct inode *dir;
+
+ hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
+ spin_lock(&alias->d_lock);
+ dir = d_inode_rcu(alias->d_parent);
+ if (!dir ||
+ !NFS_PROTO(dir)->have_delegation(dir, FMODE_READ, 0))
+ nfs_unset_verifier_delegated(&alias->d_time);
+ spin_unlock(&alias->d_lock);
+ }
+}
+
+static void nfs_clear_verifier_directory(struct inode *dir)
+{
+ struct dentry *this_parent;
+ struct dentry *dentry;
+ struct inode *inode;
+
+ if (hlist_empty(&dir->i_dentry))
+ return;
+ this_parent =
+ hlist_entry(dir->i_dentry.first, struct dentry, d_u.d_alias);
+
+ spin_lock(&this_parent->d_lock);
+ nfs_unset_verifier_delegated(&this_parent->d_time);
+ dentry = d_first_child(this_parent);
+ hlist_for_each_entry_from(dentry, d_sib) {
+ if (unlikely(dentry->d_flags & DCACHE_DENTRY_CURSOR))
+ continue;
+ inode = d_inode_rcu(dentry);
+ if (inode &&
+ NFS_PROTO(inode)->have_delegation(inode, FMODE_READ, 0))
+ continue;
+ spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
+ nfs_unset_verifier_delegated(&dentry->d_time);
+ spin_unlock(&dentry->d_lock);
+ }
+ spin_unlock(&this_parent->d_lock);
+}
+
/**
* nfs_clear_verifier_delegated - clear the dir verifier delegation tag
* @inode: pointer to inode
@@ -1477,16 +1521,13 @@ EXPORT_SYMBOL_GPL(nfs_set_verifier);
*/
void nfs_clear_verifier_delegated(struct inode *inode)
{
- struct dentry *alias;
-
if (!inode)
return;
spin_lock(&inode->i_lock);
- hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
- spin_lock(&alias->d_lock);
- nfs_unset_verifier_delegated(&alias->d_time);
- spin_unlock(&alias->d_lock);
- }
+ if (S_ISREG(inode->i_mode))
+ nfs_clear_verifier_file(inode);
+ else if (S_ISDIR(inode->i_mode))
+ nfs_clear_verifier_directory(inode);
spin_unlock(&inode->i_lock);
}
EXPORT_SYMBOL_GPL(nfs_clear_verifier_delegated);
@@ -1516,14 +1557,6 @@ static int nfs_check_verifier(struct inode *dir, struct dentry *dentry,
if (!nfs_dentry_verify_change(dir, dentry))
return 0;
- /*
- * If we have a directory delegation then we don't need to revalidate
- * the directory. The delegation will either get recalled or we will
- * receive a notification when it changes.
- */
- if (nfs_have_directory_delegation(dir))
- return 0;
-
/* Revalidate nfsi->cache_change_attribute before we declare a match */
if (nfs_mapping_need_revalidate_inode(dir)) {
if (rcu_walk)
@@ -2217,13 +2250,6 @@ no_open:
EXPORT_SYMBOL_GPL(nfs_atomic_open);
static int
-nfs_lookup_revalidate_delegated_parent(struct inode *dir, struct dentry *dentry,
- struct inode *inode)
-{
- return nfs_lookup_revalidate_done(dir, dentry, inode, 1);
-}
-
-static int
nfs4_lookup_revalidate(struct inode *dir, const struct qstr *name,
struct dentry *dentry, unsigned int flags)
{
@@ -2247,12 +2273,10 @@ nfs4_lookup_revalidate(struct inode *dir, const struct qstr *name,
if (inode == NULL)
goto full_reval;
- if (nfs_verifier_is_delegated(dentry))
+ if (nfs_verifier_is_delegated(dentry) ||
+ nfs_have_directory_delegation(inode))
return nfs_lookup_revalidate_delegated(dir, dentry, inode);
- if (nfs_have_directory_delegation(dir))
- return nfs_lookup_revalidate_delegated_parent(dir, dentry, inode);
-
/* NFS only supports OPEN on regular files */
if (!S_ISREG(inode->i_mode))
goto full_reval;
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index d020aab40c64..d1c138a416cf 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -511,7 +511,8 @@ static bool nfs_release_folio(struct folio *folio, gfp_t gfp)
if ((current_gfp_context(gfp) & GFP_KERNEL) != GFP_KERNEL ||
current_is_kswapd() || current_is_kcompactd())
return false;
- if (nfs_wb_folio(folio->mapping->host, folio) < 0)
+ if (nfs_wb_folio_reclaim(folio->mapping->host, folio) < 0 ||
+ folio_test_private(folio))
return false;
}
return nfs_fscache_release_folio(folio, gfp);
diff --git a/fs/nfs/flexfilelayout/flexfilelayoutdev.c b/fs/nfs/flexfilelayout/flexfilelayoutdev.c
index c55ea8fa3bfa..c2d8a13a9dbd 100644
--- a/fs/nfs/flexfilelayout/flexfilelayoutdev.c
+++ b/fs/nfs/flexfilelayout/flexfilelayoutdev.c
@@ -103,7 +103,7 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
sizeof(struct nfs4_ff_ds_version),
gfp_flags);
if (!ds_versions)
- goto out_scratch;
+ goto out_err_drain_dsaddrs;
for (i = 0; i < version_count; i++) {
/* 20 = version(4) + minor_version(4) + rsize(4) + wsize(4) +
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 84049f3cd340..de2cce1d08f4 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -716,7 +716,7 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
{
struct inode *inode = d_inode(dentry);
struct nfs_fattr *fattr;
- loff_t oldsize = i_size_read(inode);
+ loff_t oldsize;
int error = 0;
kuid_t task_uid = current_fsuid();
kuid_t owner_uid = inode->i_uid;
@@ -727,6 +727,10 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
if (attr->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
attr->ia_valid &= ~ATTR_MODE;
+ if (S_ISREG(inode->i_mode))
+ nfs_file_block_o_direct(NFS_I(inode));
+
+ oldsize = i_size_read(inode);
if (attr->ia_valid & ATTR_SIZE) {
BUG_ON(!S_ISREG(inode->i_mode));
@@ -774,10 +778,8 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
trace_nfs_setattr_enter(inode);
/* Write all dirty data */
- if (S_ISREG(inode->i_mode)) {
- nfs_file_block_o_direct(NFS_I(inode));
+ if (S_ISREG(inode->i_mode))
nfs_sync_inode(inode);
- }
fattr = nfs_alloc_fattr_with_label(NFS_SERVER(inode));
if (fattr == NULL) {
diff --git a/fs/nfs/io.c b/fs/nfs/io.c
index d275b0a250bf..8337f0ae852d 100644
--- a/fs/nfs/io.c
+++ b/fs/nfs/io.c
@@ -84,6 +84,7 @@ nfs_start_io_write(struct inode *inode)
nfs_file_block_o_direct(NFS_I(inode));
return err;
}
+EXPORT_SYMBOL_GPL(nfs_start_io_write);
/**
* nfs_end_io_write - declare that the buffered write operation is done
@@ -97,6 +98,7 @@ nfs_end_io_write(struct inode *inode)
{
up_write(&inode->i_rwsem);
}
+EXPORT_SYMBOL_GPL(nfs_end_io_write);
/* Call with exclusively locked inode->i_rwsem */
static void nfs_block_buffered(struct nfs_inode *nfsi, struct inode *inode)
diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
index a113bfdacfd6..41fbcb3f9167 100644
--- a/fs/nfs/localio.c
+++ b/fs/nfs/localio.c
@@ -461,6 +461,8 @@ nfs_local_iters_init(struct nfs_local_kiocb *iocb, int rw)
v = 0;
total = hdr->args.count;
base = hdr->args.pgbase;
+ pagevec += base >> PAGE_SHIFT;
+ base &= ~PAGE_MASK;
while (total && v < hdr->page_array.npages) {
len = min_t(size_t, total, PAGE_SIZE - base);
bvec_set_page(&iocb->bvec[v], *pagevec, len, base);
@@ -618,7 +620,6 @@ static void nfs_local_call_read(struct work_struct *work)
struct nfs_local_kiocb *iocb =
container_of(work, struct nfs_local_kiocb, work);
struct file *filp = iocb->kiocb.ki_filp;
- bool force_done = false;
ssize_t status;
int n_iters;
@@ -637,13 +638,13 @@ static void nfs_local_call_read(struct work_struct *work)
scoped_with_creds(filp->f_cred)
status = filp->f_op->read_iter(&iocb->kiocb, &iocb->iters[i]);
- if (status != -EIOCBQUEUED) {
- if (unlikely(status >= 0 && status < iocb->iters[i].count))
- force_done = true; /* Partial read */
- if (nfs_local_pgio_done(iocb, status, force_done)) {
- nfs_local_read_iocb_done(iocb);
- break;
- }
+ if (status == -EIOCBQUEUED)
+ continue;
+ /* Break on completion, errors, or short reads */
+ if (nfs_local_pgio_done(iocb, status, false) || status < 0 ||
+ (size_t)status < iov_iter_count(&iocb->iters[i])) {
+ nfs_local_read_iocb_done(iocb);
+ break;
}
}
}
@@ -821,7 +822,6 @@ static void nfs_local_call_write(struct work_struct *work)
container_of(work, struct nfs_local_kiocb, work);
struct file *filp = iocb->kiocb.ki_filp;
unsigned long old_flags = current->flags;
- bool force_done = false;
ssize_t status;
int n_iters;
@@ -843,13 +843,13 @@ static void nfs_local_call_write(struct work_struct *work)
scoped_with_creds(filp->f_cred)
status = filp->f_op->write_iter(&iocb->kiocb, &iocb->iters[i]);
- if (status != -EIOCBQUEUED) {
- if (unlikely(status >= 0 && status < iocb->iters[i].count))
- force_done = true; /* Partial write */
- if (nfs_local_pgio_done(iocb, status, force_done)) {
- nfs_local_write_iocb_done(iocb);
- break;
- }
+ if (status == -EIOCBQUEUED)
+ continue;
+ /* Break on completion, errors, or short writes */
+ if (nfs_local_pgio_done(iocb, status, false) || status < 0 ||
+ (size_t)status < iov_iter_count(&iocb->iters[i])) {
+ nfs_local_write_iocb_done(iocb);
+ break;
}
}
file_end_write(filp);
diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c
index d537fb0c230e..c08520828708 100644
--- a/fs/nfs/nfs42proc.c
+++ b/fs/nfs/nfs42proc.c
@@ -114,7 +114,6 @@ static int nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep,
exception.inode = inode;
exception.state = lock->open_context->state;
- nfs_file_block_o_direct(NFS_I(inode));
err = nfs_sync_inode(inode);
if (err)
goto out;
@@ -138,13 +137,17 @@ int nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len)
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ALLOCATE],
};
struct inode *inode = file_inode(filep);
- loff_t oldsize = i_size_read(inode);
+ loff_t oldsize;
int err;
if (!nfs_server_capable(inode, NFS_CAP_ALLOCATE))
return -EOPNOTSUPP;
- inode_lock(inode);
+ err = nfs_start_io_write(inode);
+ if (err)
+ return err;
+
+ oldsize = i_size_read(inode);
err = nfs42_proc_fallocate(&msg, filep, offset, len);
@@ -155,7 +158,7 @@ int nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len)
NFS_SERVER(inode)->caps &= ~(NFS_CAP_ALLOCATE |
NFS_CAP_ZERO_RANGE);
- inode_unlock(inode);
+ nfs_end_io_write(inode);
return err;
}
@@ -170,7 +173,9 @@ int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len)
if (!nfs_server_capable(inode, NFS_CAP_DEALLOCATE))
return -EOPNOTSUPP;
- inode_lock(inode);
+ err = nfs_start_io_write(inode);
+ if (err)
+ return err;
err = nfs42_proc_fallocate(&msg, filep, offset, len);
if (err == 0)
@@ -179,7 +184,7 @@ int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len)
NFS_SERVER(inode)->caps &= ~(NFS_CAP_DEALLOCATE |
NFS_CAP_ZERO_RANGE);
- inode_unlock(inode);
+ nfs_end_io_write(inode);
return err;
}
@@ -189,14 +194,17 @@ int nfs42_proc_zero_range(struct file *filep, loff_t offset, loff_t len)
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ZERO_RANGE],
};
struct inode *inode = file_inode(filep);
- loff_t oldsize = i_size_read(inode);
+ loff_t oldsize;
int err;
if (!nfs_server_capable(inode, NFS_CAP_ZERO_RANGE))
return -EOPNOTSUPP;
- inode_lock(inode);
+ err = nfs_start_io_write(inode);
+ if (err)
+ return err;
+ oldsize = i_size_read(inode);
err = nfs42_proc_fallocate(&msg, filep, offset, len);
if (err == 0) {
nfs_truncate_last_folio(inode->i_mapping, oldsize,
@@ -205,7 +213,7 @@ int nfs42_proc_zero_range(struct file *filep, loff_t offset, loff_t len)
} else if (err == -EOPNOTSUPP)
NFS_SERVER(inode)->caps &= ~NFS_CAP_ZERO_RANGE;
- inode_unlock(inode);
+ nfs_end_io_write(inode);
return err;
}
@@ -416,7 +424,7 @@ static ssize_t _nfs42_proc_copy(struct file *src,
struct nfs_server *src_server = NFS_SERVER(src_inode);
loff_t pos_src = args->src_pos;
loff_t pos_dst = args->dst_pos;
- loff_t oldsize_dst = i_size_read(dst_inode);
+ loff_t oldsize_dst;
size_t count = args->count;
ssize_t status;
@@ -461,6 +469,7 @@ static ssize_t _nfs42_proc_copy(struct file *src,
&src_lock->open_context->state->flags);
set_bit(NFS_CLNT_DST_SSC_COPY_STATE,
&dst_lock->open_context->state->flags);
+ oldsize_dst = i_size_read(dst_inode);
status = nfs4_call_sync(dst_server->client, dst_server, &msg,
&args->seq_args, &res->seq_res, 0);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index ec1ce593dea2..a0885ae55abc 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -3894,8 +3894,8 @@ int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait)
calldata->res.seqid = calldata->arg.seqid;
calldata->res.server = server;
calldata->res.lr_ret = -NFS4ERR_NOMATCHING_LAYOUT;
- calldata->lr.roc = pnfs_roc(state->inode,
- &calldata->lr.arg, &calldata->lr.res, msg.rpc_cred);
+ calldata->lr.roc = pnfs_roc(state->inode, &calldata->lr.arg,
+ &calldata->lr.res, msg.rpc_cred, wait);
if (calldata->lr.roc) {
calldata->arg.lr_args = &calldata->lr.arg;
calldata->res.lr_res = &calldata->lr.res;
@@ -4494,6 +4494,25 @@ static bool should_request_dir_deleg(struct inode *inode)
}
#endif /* CONFIG_NFS_V4_1 */
+static void nfs4_call_getattr_prepare(struct rpc_task *task, void *calldata)
+{
+ struct nfs4_call_sync_data *data = calldata;
+ nfs4_setup_sequence(data->seq_server->nfs_client, data->seq_args,
+ data->seq_res, task);
+}
+
+static void nfs4_call_getattr_done(struct rpc_task *task, void *calldata)
+{
+ struct nfs4_call_sync_data *data = calldata;
+
+ nfs4_sequence_process(task, data->seq_res);
+}
+
+static const struct rpc_call_ops nfs4_call_getattr_ops = {
+ .rpc_call_prepare = nfs4_call_getattr_prepare,
+ .rpc_call_done = nfs4_call_getattr_done,
+};
+
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fattr *fattr, struct inode *inode)
{
@@ -4511,16 +4530,26 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
.rpc_argp = &args,
.rpc_resp = &res,
};
+ struct nfs4_call_sync_data data = {
+ .seq_server = server,
+ .seq_args = &args.seq_args,
+ .seq_res = &res.seq_res,
+ };
+ struct rpc_task_setup task_setup = {
+ .rpc_client = server->client,
+ .rpc_message = &msg,
+ .callback_ops = &nfs4_call_getattr_ops,
+ .callback_data = &data,
+ };
struct nfs4_gdd_res gdd_res;
- unsigned short task_flags = 0;
int status;
if (nfs4_has_session(server->nfs_client))
- task_flags = RPC_TASK_MOVEABLE;
+ task_setup.flags = RPC_TASK_MOVEABLE;
/* Is this is an attribute revalidation, subject to softreval? */
if (inode && (server->flags & NFS_MOUNT_SOFTREVAL))
- task_flags |= RPC_TASK_TIMEOUT;
+ task_setup.flags |= RPC_TASK_TIMEOUT;
args.get_dir_deleg = should_request_dir_deleg(inode);
if (args.get_dir_deleg)
@@ -4530,22 +4559,24 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
nfs_fattr_init(fattr);
nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0);
- status = nfs4_do_call_sync(server->client, server, &msg,
- &args.seq_args, &res.seq_res, task_flags);
+ status = nfs4_call_sync_custom(&task_setup);
+
if (args.get_dir_deleg) {
switch (status) {
case 0:
if (gdd_res.status != GDD4_OK)
break;
- status = nfs_inode_set_delegation(
- inode, current_cred(), FMODE_READ,
- &gdd_res.deleg, 0, NFS4_OPEN_DELEGATE_READ);
+ nfs_inode_set_delegation(inode, current_cred(),
+ FMODE_READ, &gdd_res.deleg, 0,
+ NFS4_OPEN_DELEGATE_READ);
break;
case -ENOTSUPP:
case -EOPNOTSUPP:
server->caps &= ~NFS_CAP_DIR_DELEG;
}
}
+
+ nfs4_sequence_free_slot(&res.seq_res);
return status;
}
@@ -7005,7 +7036,7 @@ static int _nfs4_proc_delegreturn(struct inode *inode, const struct cred *cred,
data->inode = nfs_igrab_and_active(inode);
if (data->inode || issync) {
data->lr.roc = pnfs_roc(inode, &data->lr.arg, &data->lr.res,
- cred);
+ cred, issync);
if (data->lr.roc) {
data->args.lr_args = &data->lr.arg;
data->res.lr_res = &data->lr.res;
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 01179f7de322..dba51c622cf3 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -1445,6 +1445,8 @@ void nfs_inode_find_state_and_recover(struct inode *inode,
struct nfs4_state *state;
bool found = false;
+ if (!S_ISREG(inode->i_mode))
+ goto out;
rcu_read_lock();
list_for_each_entry_rcu(ctx, &nfsi->open_files, list) {
state = ctx->state;
@@ -1466,7 +1468,7 @@ void nfs_inode_find_state_and_recover(struct inode *inode,
found = true;
}
rcu_read_unlock();
-
+out:
nfs_inode_find_delegation_state_and_recover(inode, stateid);
if (found)
nfs4_schedule_state_manager(clp);
@@ -1478,6 +1480,8 @@ static void nfs4_state_mark_open_context_bad(struct nfs4_state *state, int err)
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_open_context *ctx;
+ if (!S_ISREG(inode->i_mode))
+ return;
rcu_read_lock();
list_for_each_entry_rcu(ctx, &nfsi->open_files, list) {
if (ctx->state != state)
diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h
index 6ce55e8e6b67..9f9ce4a565ea 100644
--- a/fs/nfs/nfstrace.h
+++ b/fs/nfs/nfstrace.h
@@ -1062,6 +1062,9 @@ DECLARE_EVENT_CLASS(nfs_folio_event_done,
DEFINE_NFS_FOLIO_EVENT(nfs_aop_readpage);
DEFINE_NFS_FOLIO_EVENT_DONE(nfs_aop_readpage_done);
+DEFINE_NFS_FOLIO_EVENT(nfs_writeback_folio_reclaim);
+DEFINE_NFS_FOLIO_EVENT_DONE(nfs_writeback_folio_reclaim_done);
+
DEFINE_NFS_FOLIO_EVENT(nfs_writeback_folio);
DEFINE_NFS_FOLIO_EVENT_DONE(nfs_writeback_folio_done);
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index b72d7cc36766..cff225721d1c 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -1533,10 +1533,9 @@ static int pnfs_layout_return_on_reboot(struct pnfs_layout_hdr *lo)
PNFS_FL_LAYOUTRETURN_PRIVILEGED);
}
-bool pnfs_roc(struct inode *ino,
- struct nfs4_layoutreturn_args *args,
- struct nfs4_layoutreturn_res *res,
- const struct cred *cred)
+bool pnfs_roc(struct inode *ino, struct nfs4_layoutreturn_args *args,
+ struct nfs4_layoutreturn_res *res, const struct cred *cred,
+ bool sync)
{
struct nfs_inode *nfsi = NFS_I(ino);
struct nfs_open_context *ctx;
@@ -1547,7 +1546,7 @@ bool pnfs_roc(struct inode *ino,
nfs4_stateid stateid;
enum pnfs_iomode iomode = 0;
bool layoutreturn = false, roc = false;
- bool skip_read = false;
+ bool skip_read;
if (!nfs_have_layout(ino))
return false;
@@ -1560,20 +1559,14 @@ retry:
lo = NULL;
goto out_noroc;
}
- pnfs_get_layout_hdr(lo);
- if (test_bit(NFS_LAYOUT_RETURN_LOCK, &lo->plh_flags)) {
- spin_unlock(&ino->i_lock);
- rcu_read_unlock();
- wait_on_bit(&lo->plh_flags, NFS_LAYOUT_RETURN,
- TASK_UNINTERRUPTIBLE);
- pnfs_put_layout_hdr(lo);
- goto retry;
- }
/* no roc if we hold a delegation */
+ skip_read = false;
if (nfs4_check_delegation(ino, FMODE_READ)) {
- if (nfs4_check_delegation(ino, FMODE_WRITE))
+ if (nfs4_check_delegation(ino, FMODE_WRITE)) {
+ lo = NULL;
goto out_noroc;
+ }
skip_read = true;
}
@@ -1582,12 +1575,43 @@ retry:
if (state == NULL)
continue;
/* Don't return layout if there is open file state */
- if (state->state & FMODE_WRITE)
+ if (state->state & FMODE_WRITE) {
+ lo = NULL;
goto out_noroc;
+ }
if (state->state & FMODE_READ)
skip_read = true;
}
+ if (skip_read) {
+ bool writes = false;
+
+ list_for_each_entry(lseg, &lo->plh_segs, pls_list) {
+ if (lseg->pls_range.iomode != IOMODE_READ) {
+ writes = true;
+ break;
+ }
+ }
+ if (!writes) {
+ lo = NULL;
+ goto out_noroc;
+ }
+ }
+
+ pnfs_get_layout_hdr(lo);
+ if (test_bit(NFS_LAYOUT_RETURN_LOCK, &lo->plh_flags)) {
+ if (!sync) {
+ pnfs_set_plh_return_info(
+ lo, skip_read ? IOMODE_RW : IOMODE_ANY, 0);
+ goto out_noroc;
+ }
+ spin_unlock(&ino->i_lock);
+ rcu_read_unlock();
+ wait_on_bit(&lo->plh_flags, NFS_LAYOUT_RETURN,
+ TASK_UNINTERRUPTIBLE);
+ pnfs_put_layout_hdr(lo);
+ goto retry;
+ }
list_for_each_entry_safe(lseg, next, &lo->plh_segs, pls_list) {
if (skip_read && lseg->pls_range.iomode == IOMODE_READ)
@@ -1627,7 +1651,7 @@ retry:
out_noroc:
spin_unlock(&ino->i_lock);
rcu_read_unlock();
- pnfs_layoutcommit_inode(ino, true);
+ pnfs_layoutcommit_inode(ino, sync);
if (roc) {
struct pnfs_layoutdriver_type *ld = NFS_SERVER(ino)->pnfs_curr_ld;
if (ld->prepare_layoutreturn)
diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h
index 91ff877185c8..3db8f13d8fe4 100644
--- a/fs/nfs/pnfs.h
+++ b/fs/nfs/pnfs.h
@@ -303,10 +303,9 @@ int pnfs_mark_matching_lsegs_return(struct pnfs_layout_hdr *lo,
u32 seq);
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,
- struct nfs4_layoutreturn_res *res,
- const struct cred *cred);
+bool pnfs_roc(struct inode *ino, struct nfs4_layoutreturn_args *args,
+ struct nfs4_layoutreturn_res *res, const struct cred *cred,
+ bool sync);
int pnfs_roc_done(struct rpc_task *task, struct nfs4_layoutreturn_args **argpp,
struct nfs4_layoutreturn_res **respp, int *ret);
void pnfs_roc_release(struct nfs4_layoutreturn_args *args,
@@ -773,12 +772,10 @@ pnfs_layoutcommit_outstanding(struct inode *inode)
return false;
}
-
-static inline bool
-pnfs_roc(struct inode *ino,
- struct nfs4_layoutreturn_args *args,
- struct nfs4_layoutreturn_res *res,
- const struct cred *cred)
+static inline bool pnfs_roc(struct inode *ino,
+ struct nfs4_layoutreturn_args *args,
+ struct nfs4_layoutreturn_res *res,
+ const struct cred *cred, bool sync)
{
return false;
}
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 336c510f3750..bf412455e8ed 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -2025,6 +2025,39 @@ int nfs_wb_folio_cancel(struct inode *inode, struct folio *folio)
}
/**
+ * nfs_wb_folio_reclaim - Write back all requests on one page
+ * @inode: pointer to page
+ * @folio: pointer to folio
+ *
+ * Assumes that the folio has been locked by the caller
+ */
+int nfs_wb_folio_reclaim(struct inode *inode, struct folio *folio)
+{
+ loff_t range_start = folio_pos(folio);
+ size_t len = folio_size(folio);
+ struct writeback_control wbc = {
+ .sync_mode = WB_SYNC_ALL,
+ .nr_to_write = 0,
+ .range_start = range_start,
+ .range_end = range_start + len - 1,
+ .for_sync = 1,
+ };
+ int ret;
+
+ if (folio_test_writeback(folio))
+ return -EBUSY;
+ if (folio_clear_dirty_for_io(folio)) {
+ trace_nfs_writeback_folio_reclaim(inode, range_start, len);
+ ret = nfs_writepage_locked(folio, &wbc);
+ trace_nfs_writeback_folio_reclaim_done(inode, range_start, len,
+ ret);
+ return ret;
+ }
+ nfs_commit_inode(inode, 0);
+ return 0;
+}
+
+/**
* nfs_wb_folio - Write back all requests on one page
* @inode: pointer to page
* @folio: pointer to folio