Skip to content

Commit 603c05a

Browse files
committed
Merge tag 'nfs-for-6.19-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
Pull NFS client fixes from Trond Myklebust: - Fix another deadlock involving nfs_release_folio() - localio: - Stop I/O upon hitting a fatal error - Deal with page offsets that are > PAGE_SIZE - Fix size read races in truncate, fallocate and copy offload - Several bugfixes for the NFSv4.x directory delegation client code - pNFS: - Fix a deadlock when returning delegations during open - Fix memory leaks in various error paths * tag 'nfs-for-6.19-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: NFS: Fix size read races in truncate, fallocate and copy offload NFS: Don't immediately return directory delegations when disabled NFS/localio: Deal with page bases that are > PAGE_SIZE NFS/localio: Stop further I/O upon hitting an error NFSv4.x: Directory delegations don't require any state recovery NFSv4: Don't free slots prematurely if requesting a directory delegation NFSv4: Fix nfs_clear_verifier_delegated() for delegated directories NFS: Fix directory delegation verifier checks pnfs/blocklayout: Fix memory leak in bl_parse_scsi() pnfs/flexfiles: Fix memory leak in nfs4_ff_alloc_deviceid_node() NFS: Fix a deadlock involving nfs_release_folio() pNFS: Fix a deadlock when returning a delegation during open()
2 parents bc08b65 + d5811e6 commit 603c05a

16 files changed

Lines changed: 239 additions & 101 deletions

File tree

fs/nfs/blocklayout/dev.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,8 +417,10 @@ bl_parse_scsi(struct nfs_server *server, struct pnfs_block_dev *d,
417417
d->map = bl_map_simple;
418418
d->pr_key = v->scsi.pr_key;
419419

420-
if (d->len == 0)
421-
return -ENODEV;
420+
if (d->len == 0) {
421+
error = -ENODEV;
422+
goto out_blkdev_put;
423+
}
422424

423425
ops = bdev->bd_disk->fops->pr_ops;
424426
if (!ops) {

fs/nfs/delegation.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ static int nfs4_do_check_delegation(struct inode *inode, fmode_t type,
149149
int nfs4_have_delegation(struct inode *inode, fmode_t type, int flags)
150150
{
151151
if (S_ISDIR(inode->i_mode) && !directory_delegations)
152-
nfs_inode_evict_delegation(inode);
152+
nfs4_inode_set_return_delegation_on_close(inode);
153153
return nfs4_do_check_delegation(inode, type, flags, true);
154154
}
155155

@@ -581,6 +581,10 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
581581
if (delegation == NULL)
582582
return 0;
583583

584+
/* Directory delegations don't require any state recovery */
585+
if (!S_ISREG(inode->i_mode))
586+
goto out_return;
587+
584588
if (!issync)
585589
mode |= O_NONBLOCK;
586590
/* Recall of any remaining application leases */
@@ -604,6 +608,7 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
604608
goto out;
605609
}
606610

611+
out_return:
607612
err = nfs_do_return_delegation(inode, delegation, issync);
608613
out:
609614
/* Refcount matched in nfs_start_delegation_return_locked() */

fs/nfs/dir.c

Lines changed: 51 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,7 +1440,8 @@ static void nfs_set_verifier_locked(struct dentry *dentry, unsigned long verf)
14401440

14411441
if (!dir || !nfs_verify_change_attribute(dir, verf))
14421442
return;
1443-
if (inode && NFS_PROTO(inode)->have_delegation(inode, FMODE_READ, 0))
1443+
if (NFS_PROTO(dir)->have_delegation(dir, FMODE_READ, 0) ||
1444+
(inode && NFS_PROTO(inode)->have_delegation(inode, FMODE_READ, 0)))
14441445
nfs_set_verifier_delegated(&verf);
14451446
dentry->d_time = verf;
14461447
}
@@ -1465,6 +1466,49 @@ void nfs_set_verifier(struct dentry *dentry, unsigned long verf)
14651466
EXPORT_SYMBOL_GPL(nfs_set_verifier);
14661467

14671468
#if IS_ENABLED(CONFIG_NFS_V4)
1469+
static void nfs_clear_verifier_file(struct inode *inode)
1470+
{
1471+
struct dentry *alias;
1472+
struct inode *dir;
1473+
1474+
hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
1475+
spin_lock(&alias->d_lock);
1476+
dir = d_inode_rcu(alias->d_parent);
1477+
if (!dir ||
1478+
!NFS_PROTO(dir)->have_delegation(dir, FMODE_READ, 0))
1479+
nfs_unset_verifier_delegated(&alias->d_time);
1480+
spin_unlock(&alias->d_lock);
1481+
}
1482+
}
1483+
1484+
static void nfs_clear_verifier_directory(struct inode *dir)
1485+
{
1486+
struct dentry *this_parent;
1487+
struct dentry *dentry;
1488+
struct inode *inode;
1489+
1490+
if (hlist_empty(&dir->i_dentry))
1491+
return;
1492+
this_parent =
1493+
hlist_entry(dir->i_dentry.first, struct dentry, d_u.d_alias);
1494+
1495+
spin_lock(&this_parent->d_lock);
1496+
nfs_unset_verifier_delegated(&this_parent->d_time);
1497+
dentry = d_first_child(this_parent);
1498+
hlist_for_each_entry_from(dentry, d_sib) {
1499+
if (unlikely(dentry->d_flags & DCACHE_DENTRY_CURSOR))
1500+
continue;
1501+
inode = d_inode_rcu(dentry);
1502+
if (inode &&
1503+
NFS_PROTO(inode)->have_delegation(inode, FMODE_READ, 0))
1504+
continue;
1505+
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
1506+
nfs_unset_verifier_delegated(&dentry->d_time);
1507+
spin_unlock(&dentry->d_lock);
1508+
}
1509+
spin_unlock(&this_parent->d_lock);
1510+
}
1511+
14681512
/**
14691513
* nfs_clear_verifier_delegated - clear the dir verifier delegation tag
14701514
* @inode: pointer to inode
@@ -1477,16 +1521,13 @@ EXPORT_SYMBOL_GPL(nfs_set_verifier);
14771521
*/
14781522
void nfs_clear_verifier_delegated(struct inode *inode)
14791523
{
1480-
struct dentry *alias;
1481-
14821524
if (!inode)
14831525
return;
14841526
spin_lock(&inode->i_lock);
1485-
hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
1486-
spin_lock(&alias->d_lock);
1487-
nfs_unset_verifier_delegated(&alias->d_time);
1488-
spin_unlock(&alias->d_lock);
1489-
}
1527+
if (S_ISREG(inode->i_mode))
1528+
nfs_clear_verifier_file(inode);
1529+
else if (S_ISDIR(inode->i_mode))
1530+
nfs_clear_verifier_directory(inode);
14901531
spin_unlock(&inode->i_lock);
14911532
}
14921533
EXPORT_SYMBOL_GPL(nfs_clear_verifier_delegated);
@@ -1516,14 +1557,6 @@ static int nfs_check_verifier(struct inode *dir, struct dentry *dentry,
15161557
if (!nfs_dentry_verify_change(dir, dentry))
15171558
return 0;
15181559

1519-
/*
1520-
* If we have a directory delegation then we don't need to revalidate
1521-
* the directory. The delegation will either get recalled or we will
1522-
* receive a notification when it changes.
1523-
*/
1524-
if (nfs_have_directory_delegation(dir))
1525-
return 0;
1526-
15271560
/* Revalidate nfsi->cache_change_attribute before we declare a match */
15281561
if (nfs_mapping_need_revalidate_inode(dir)) {
15291562
if (rcu_walk)
@@ -2216,13 +2249,6 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
22162249
}
22172250
EXPORT_SYMBOL_GPL(nfs_atomic_open);
22182251

2219-
static int
2220-
nfs_lookup_revalidate_delegated_parent(struct inode *dir, struct dentry *dentry,
2221-
struct inode *inode)
2222-
{
2223-
return nfs_lookup_revalidate_done(dir, dentry, inode, 1);
2224-
}
2225-
22262252
static int
22272253
nfs4_lookup_revalidate(struct inode *dir, const struct qstr *name,
22282254
struct dentry *dentry, unsigned int flags)
@@ -2247,12 +2273,10 @@ nfs4_lookup_revalidate(struct inode *dir, const struct qstr *name,
22472273
if (inode == NULL)
22482274
goto full_reval;
22492275

2250-
if (nfs_verifier_is_delegated(dentry))
2276+
if (nfs_verifier_is_delegated(dentry) ||
2277+
nfs_have_directory_delegation(inode))
22512278
return nfs_lookup_revalidate_delegated(dir, dentry, inode);
22522279

2253-
if (nfs_have_directory_delegation(dir))
2254-
return nfs_lookup_revalidate_delegated_parent(dir, dentry, inode);
2255-
22562280
/* NFS only supports OPEN on regular files */
22572281
if (!S_ISREG(inode->i_mode))
22582282
goto full_reval;

fs/nfs/file.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,8 @@ static bool nfs_release_folio(struct folio *folio, gfp_t gfp)
511511
if ((current_gfp_context(gfp) & GFP_KERNEL) != GFP_KERNEL ||
512512
current_is_kswapd() || current_is_kcompactd())
513513
return false;
514-
if (nfs_wb_folio(folio->mapping->host, folio) < 0)
514+
if (nfs_wb_folio_reclaim(folio->mapping->host, folio) < 0 ||
515+
folio_test_private(folio))
515516
return false;
516517
}
517518
return nfs_fscache_release_folio(folio, gfp);

fs/nfs/flexfilelayout/flexfilelayoutdev.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
103103
sizeof(struct nfs4_ff_ds_version),
104104
gfp_flags);
105105
if (!ds_versions)
106-
goto out_scratch;
106+
goto out_err_drain_dsaddrs;
107107

108108
for (i = 0; i < version_count; i++) {
109109
/* 20 = version(4) + minor_version(4) + rsize(4) + wsize(4) +

fs/nfs/inode.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
716716
{
717717
struct inode *inode = d_inode(dentry);
718718
struct nfs_fattr *fattr;
719-
loff_t oldsize = i_size_read(inode);
719+
loff_t oldsize;
720720
int error = 0;
721721
kuid_t task_uid = current_fsuid();
722722
kuid_t owner_uid = inode->i_uid;
@@ -727,6 +727,10 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
727727
if (attr->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
728728
attr->ia_valid &= ~ATTR_MODE;
729729

730+
if (S_ISREG(inode->i_mode))
731+
nfs_file_block_o_direct(NFS_I(inode));
732+
733+
oldsize = i_size_read(inode);
730734
if (attr->ia_valid & ATTR_SIZE) {
731735
BUG_ON(!S_ISREG(inode->i_mode));
732736

@@ -774,10 +778,8 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
774778
trace_nfs_setattr_enter(inode);
775779

776780
/* Write all dirty data */
777-
if (S_ISREG(inode->i_mode)) {
778-
nfs_file_block_o_direct(NFS_I(inode));
781+
if (S_ISREG(inode->i_mode))
779782
nfs_sync_inode(inode);
780-
}
781783

782784
fattr = nfs_alloc_fattr_with_label(NFS_SERVER(inode));
783785
if (fattr == NULL) {

fs/nfs/io.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ nfs_start_io_write(struct inode *inode)
8484
nfs_file_block_o_direct(NFS_I(inode));
8585
return err;
8686
}
87+
EXPORT_SYMBOL_GPL(nfs_start_io_write);
8788

8889
/**
8990
* nfs_end_io_write - declare that the buffered write operation is done
@@ -97,6 +98,7 @@ nfs_end_io_write(struct inode *inode)
9798
{
9899
up_write(&inode->i_rwsem);
99100
}
101+
EXPORT_SYMBOL_GPL(nfs_end_io_write);
100102

101103
/* Call with exclusively locked inode->i_rwsem */
102104
static void nfs_block_buffered(struct nfs_inode *nfsi, struct inode *inode)

fs/nfs/localio.c

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,8 @@ nfs_local_iters_init(struct nfs_local_kiocb *iocb, int rw)
461461
v = 0;
462462
total = hdr->args.count;
463463
base = hdr->args.pgbase;
464+
pagevec += base >> PAGE_SHIFT;
465+
base &= ~PAGE_MASK;
464466
while (total && v < hdr->page_array.npages) {
465467
len = min_t(size_t, total, PAGE_SIZE - base);
466468
bvec_set_page(&iocb->bvec[v], *pagevec, len, base);
@@ -618,7 +620,6 @@ static void nfs_local_call_read(struct work_struct *work)
618620
struct nfs_local_kiocb *iocb =
619621
container_of(work, struct nfs_local_kiocb, work);
620622
struct file *filp = iocb->kiocb.ki_filp;
621-
bool force_done = false;
622623
ssize_t status;
623624
int n_iters;
624625

@@ -637,13 +638,13 @@ static void nfs_local_call_read(struct work_struct *work)
637638
scoped_with_creds(filp->f_cred)
638639
status = filp->f_op->read_iter(&iocb->kiocb, &iocb->iters[i]);
639640

640-
if (status != -EIOCBQUEUED) {
641-
if (unlikely(status >= 0 && status < iocb->iters[i].count))
642-
force_done = true; /* Partial read */
643-
if (nfs_local_pgio_done(iocb, status, force_done)) {
644-
nfs_local_read_iocb_done(iocb);
645-
break;
646-
}
641+
if (status == -EIOCBQUEUED)
642+
continue;
643+
/* Break on completion, errors, or short reads */
644+
if (nfs_local_pgio_done(iocb, status, false) || status < 0 ||
645+
(size_t)status < iov_iter_count(&iocb->iters[i])) {
646+
nfs_local_read_iocb_done(iocb);
647+
break;
647648
}
648649
}
649650
}
@@ -821,7 +822,6 @@ static void nfs_local_call_write(struct work_struct *work)
821822
container_of(work, struct nfs_local_kiocb, work);
822823
struct file *filp = iocb->kiocb.ki_filp;
823824
unsigned long old_flags = current->flags;
824-
bool force_done = false;
825825
ssize_t status;
826826
int n_iters;
827827

@@ -843,13 +843,13 @@ static void nfs_local_call_write(struct work_struct *work)
843843
scoped_with_creds(filp->f_cred)
844844
status = filp->f_op->write_iter(&iocb->kiocb, &iocb->iters[i]);
845845

846-
if (status != -EIOCBQUEUED) {
847-
if (unlikely(status >= 0 && status < iocb->iters[i].count))
848-
force_done = true; /* Partial write */
849-
if (nfs_local_pgio_done(iocb, status, force_done)) {
850-
nfs_local_write_iocb_done(iocb);
851-
break;
852-
}
846+
if (status == -EIOCBQUEUED)
847+
continue;
848+
/* Break on completion, errors, or short writes */
849+
if (nfs_local_pgio_done(iocb, status, false) || status < 0 ||
850+
(size_t)status < iov_iter_count(&iocb->iters[i])) {
851+
nfs_local_write_iocb_done(iocb);
852+
break;
853853
}
854854
}
855855
file_end_write(filp);

0 commit comments

Comments
 (0)