diff options
Diffstat (limited to 'fs')
40 files changed, 415 insertions, 222 deletions
diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig index 4438637c8900..6d6fc85835d4 100644 --- a/fs/btrfs/Kconfig +++ b/fs/btrfs/Kconfig @@ -115,6 +115,10 @@ config BTRFS_EXPERIMENTAL - extent tree v2 - complex rework of extent tracking - - large folio support + - large folio and block size (> page size) support + + - shutdown ioctl and auto-degradation support + + - asynchronous checksum generation for data writes If unsure, say N. diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index d8ca5b6e88e0..89022e9f393b 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1661,7 +1661,7 @@ static void backup_super_roots(struct btrfs_fs_info *info) btrfs_set_backup_chunk_root_level(root_backup, btrfs_header_level(info->chunk_root->node)); - if (!btrfs_fs_compat_ro(info, BLOCK_GROUP_TREE)) { + if (!btrfs_fs_incompat(info, EXTENT_TREE_V2)) { struct btrfs_root *extent_root = btrfs_extent_root(info, 0); struct btrfs_root *csum_root = btrfs_csum_root(info, 0); @@ -3255,6 +3255,15 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount) return 0; } +static bool fs_is_full_ro(const struct btrfs_fs_info *fs_info) +{ + if (!sb_rdonly(fs_info->sb)) + return false; + if (unlikely(fs_info->mount_opt & BTRFS_MOUNT_FULL_RO_MASK)) + return true; + return false; +} + int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_devices) { u32 sectorsize; @@ -3363,6 +3372,10 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_ERROR) WRITE_ONCE(fs_info->fs_error, -EUCLEAN); + /* If the fs has any rescue options, no transaction is allowed. */ + if (fs_is_full_ro(fs_info)) + WRITE_ONCE(fs_info->fs_error, -EROFS); + /* Set up fs_info before parsing mount options */ nodesize = btrfs_super_nodesize(disk_super); sectorsize = btrfs_super_sectorsize(disk_super); @@ -3489,6 +3502,10 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device fs_info->generation == btrfs_super_uuid_tree_generation(disk_super)) set_bit(BTRFS_FS_UPDATE_UUID_TREE_GEN, &fs_info->flags); + if (unlikely(btrfs_verify_dev_items(fs_info))) { + ret = -EUCLEAN; + goto fail_block_groups; + } ret = btrfs_verify_dev_extents(fs_info); if (ret) { btrfs_err(fs_info, diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h index 0f7e1ef27891..8ffbc40ebe45 100644 --- a/fs/btrfs/fs.h +++ b/fs/btrfs/fs.h @@ -264,6 +264,14 @@ enum { BTRFS_MOUNT_REF_TRACKER = (1ULL << 33), }; +/* These mount options require a full read-only fs, no new transaction is allowed. */ +#define BTRFS_MOUNT_FULL_RO_MASK \ + (BTRFS_MOUNT_NOLOGREPLAY | \ + BTRFS_MOUNT_IGNOREBADROOTS | \ + BTRFS_MOUNT_IGNOREDATACSUMS | \ + BTRFS_MOUNT_IGNOREMETACSUMS | \ + BTRFS_MOUNT_IGNORESUPERFLAGS) + /* * Compat flags that we support. If any incompat flags are set other than the * ones specified below then we will fail to mount diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index d2b302ac6af9..a2b5b440637e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4180,6 +4180,15 @@ cache_acl: return 0; out: + /* + * We may have a read locked leaf and iget_failed() triggers inode + * eviction which needs to release the delayed inode and that needs + * to lock the delayed inode's mutex. This can cause a ABBA deadlock + * with a task running delayed items, as that require first locking + * the delayed inode's mutex and then modifying its subvolume btree. + * So release the path before iget_failed(). + */ + btrfs_release_path(path); iget_failed(vfs_inode); return ret; } diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c index b5fe95baf92e..58dc3e5057ce 100644 --- a/fs/btrfs/reflink.c +++ b/fs/btrfs/reflink.c @@ -705,7 +705,6 @@ static noinline int btrfs_clone_files(struct file *file, struct file *file_src, struct inode *src = file_inode(file_src); struct btrfs_fs_info *fs_info = inode_to_fs_info(inode); int ret; - int wb_ret; u64 len = olen; u64 bs = fs_info->sectorsize; u64 end; @@ -750,25 +749,29 @@ static noinline int btrfs_clone_files(struct file *file, struct file *file_src, btrfs_lock_extent(&BTRFS_I(inode)->io_tree, destoff, end, &cached_state); ret = btrfs_clone(src, inode, off, olen, len, destoff, 0); btrfs_unlock_extent(&BTRFS_I(inode)->io_tree, destoff, end, &cached_state); + if (ret < 0) + return ret; /* * We may have copied an inline extent into a page of the destination - * range, so wait for writeback to complete before truncating pages + * range, so wait for writeback to complete before invalidating pages * from the page cache. This is a rare case. */ - wb_ret = btrfs_wait_ordered_range(BTRFS_I(inode), destoff, len); - ret = ret ? ret : wb_ret; + ret = btrfs_wait_ordered_range(BTRFS_I(inode), destoff, len); + if (ret < 0) + return ret; + /* - * Truncate page cache pages so that future reads will see the cloned - * data immediately and not the previous data. + * Invalidate page cache so that future reads will see the cloned data + * immediately and not the previous data. */ - truncate_inode_pages_range(&inode->i_data, - round_down(destoff, PAGE_SIZE), - round_up(destoff + len, PAGE_SIZE) - 1); + ret = filemap_invalidate_inode(inode, false, destoff, end); + if (ret < 0) + return ret; btrfs_btree_balance_dirty(fs_info); - return ret; + return 0; } static int btrfs_remap_file_range_prep(struct file *file_in, loff_t pos_in, diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 2522faa97478..d8127a7120c2 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -6383,6 +6383,8 @@ static int range_is_hole_in_parent(struct send_ctx *sctx, extent_end = btrfs_file_extent_end(path); if (extent_end <= start) goto next; + if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) + return 0; if (btrfs_file_extent_disk_bytenr(leaf, fi) == 0) { search_start = extent_end; goto next; diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c index 6babbe333741..3f08e450f796 100644 --- a/fs/btrfs/space-info.c +++ b/fs/btrfs/space-info.c @@ -306,18 +306,22 @@ static int create_space_info(struct btrfs_fs_info *info, u64 flags) 0); if (ret) - return ret; + goto out_free; } ret = btrfs_sysfs_add_space_info_type(space_info); if (ret) - return ret; + goto out_free; list_add(&space_info->list, &info->space_info); if (flags & BTRFS_BLOCK_GROUP_DATA) info->data_sinfo = space_info; return ret; + +out_free: + kfree(space_info); + return ret; } int btrfs_init_space_info(struct btrfs_fs_info *fs_info) diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index 1f64c132b387..4b3c2acac51a 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -26,7 +26,6 @@ #include "misc.h" #include "fs.h" #include "accessors.h" -#include "zoned.h" /* * Structure name Path @@ -1189,56 +1188,6 @@ static ssize_t btrfs_commit_stats_store(struct kobject *kobj, } BTRFS_ATTR_RW(, commit_stats, btrfs_commit_stats_show, btrfs_commit_stats_store); -static ssize_t btrfs_zoned_stats_show(struct kobject *kobj, - struct kobj_attribute *a, char *buf) -{ - struct btrfs_fs_info *fs_info = to_fs_info(kobj); - struct btrfs_block_group *bg; - size_t ret = 0; - - - if (!btrfs_is_zoned(fs_info)) - return ret; - - spin_lock(&fs_info->zone_active_bgs_lock); - ret += sysfs_emit_at(buf, ret, "active block-groups: %zu\n", - list_count_nodes(&fs_info->zone_active_bgs)); - spin_unlock(&fs_info->zone_active_bgs_lock); - - mutex_lock(&fs_info->reclaim_bgs_lock); - spin_lock(&fs_info->unused_bgs_lock); - ret += sysfs_emit_at(buf, ret, "\treclaimable: %zu\n", - list_count_nodes(&fs_info->reclaim_bgs)); - ret += sysfs_emit_at(buf, ret, "\tunused: %zu\n", - list_count_nodes(&fs_info->unused_bgs)); - spin_unlock(&fs_info->unused_bgs_lock); - mutex_unlock(&fs_info->reclaim_bgs_lock); - - ret += sysfs_emit_at(buf, ret, "\tneed reclaim: %s\n", - str_true_false(btrfs_zoned_should_reclaim(fs_info))); - - if (fs_info->data_reloc_bg) - ret += sysfs_emit_at(buf, ret, - "data relocation block-group: %llu\n", - fs_info->data_reloc_bg); - if (fs_info->treelog_bg) - ret += sysfs_emit_at(buf, ret, - "tree-log block-group: %llu\n", - fs_info->treelog_bg); - - spin_lock(&fs_info->zone_active_bgs_lock); - ret += sysfs_emit_at(buf, ret, "active zones:\n"); - list_for_each_entry(bg, &fs_info->zone_active_bgs, active_bg_list) { - ret += sysfs_emit_at(buf, ret, - "\tstart: %llu, wp: %llu used: %llu, reserved: %llu, unusable: %llu\n", - bg->start, bg->alloc_offset, bg->used, - bg->reserved, bg->zone_unusable); - } - spin_unlock(&fs_info->zone_active_bgs_lock); - return ret; -} -BTRFS_ATTR(, zoned_stats, btrfs_zoned_stats_show); - static ssize_t btrfs_clone_alignment_show(struct kobject *kobj, struct kobj_attribute *a, char *buf) { @@ -1651,7 +1600,6 @@ static const struct attribute *btrfs_attrs[] = { BTRFS_ATTR_PTR(, bg_reclaim_threshold), BTRFS_ATTR_PTR(, commit_stats), BTRFS_ATTR_PTR(, temp_fsid), - BTRFS_ATTR_PTR(, zoned_stats), #ifdef CONFIG_BTRFS_EXPERIMENTAL BTRFS_ATTR_PTR(, offload_csum), #endif diff --git a/fs/btrfs/tests/extent-map-tests.c b/fs/btrfs/tests/extent-map-tests.c index 0b9f25dd1a68..aabf825e8d7b 100644 --- a/fs/btrfs/tests/extent-map-tests.c +++ b/fs/btrfs/tests/extent-map-tests.c @@ -1059,6 +1059,7 @@ static int test_rmap_block(struct btrfs_fs_info *fs_info, if (out_stripe_len != BTRFS_STRIPE_LEN) { test_err("calculated stripe length doesn't match"); + ret = -EINVAL; goto out; } @@ -1066,12 +1067,14 @@ static int test_rmap_block(struct btrfs_fs_info *fs_info, for (i = 0; i < out_ndaddrs; i++) test_msg("mapped %llu", logical[i]); test_err("unexpected number of mapped addresses: %d", out_ndaddrs); + ret = -EINVAL; goto out; } for (i = 0; i < out_ndaddrs; i++) { if (logical[i] != test->mapped_logical[i]) { test_err("unexpected logical address mapped"); + ret = -EINVAL; goto out; } } diff --git a/fs/btrfs/tests/qgroup-tests.c b/fs/btrfs/tests/qgroup-tests.c index e9124605974b..0fcc31beeffe 100644 --- a/fs/btrfs/tests/qgroup-tests.c +++ b/fs/btrfs/tests/qgroup-tests.c @@ -517,11 +517,11 @@ int btrfs_test_qgroups(u32 sectorsize, u32 nodesize) tmp_root->root_key.objectid = BTRFS_FS_TREE_OBJECTID; root->fs_info->fs_root = tmp_root; ret = btrfs_insert_fs_root(root->fs_info, tmp_root); + btrfs_put_root(tmp_root); if (ret) { test_err("couldn't insert fs root %d", ret); goto out; } - btrfs_put_root(tmp_root); tmp_root = btrfs_alloc_dummy_root(fs_info); if (IS_ERR(tmp_root)) { @@ -532,11 +532,11 @@ int btrfs_test_qgroups(u32 sectorsize, u32 nodesize) tmp_root->root_key.objectid = BTRFS_FIRST_FREE_OBJECTID; ret = btrfs_insert_fs_root(root->fs_info, tmp_root); + btrfs_put_root(tmp_root); if (ret) { - test_err("couldn't insert fs root %d", ret); + test_err("couldn't insert subvolume root %d", ret); goto out; } - btrfs_put_root(tmp_root); test_msg("running qgroup tests"); ret = test_no_shared_qgroup(root, sectorsize, nodesize); diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 2d9d38b82daa..6cffcf0c3e7a 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -2798,7 +2798,7 @@ static int replay_one_buffer(struct extent_buffer *eb, nritems = btrfs_header_nritems(eb); for (wc->log_slot = 0; wc->log_slot < nritems; wc->log_slot++) { - struct btrfs_inode_item *inode_item; + struct btrfs_inode_item *inode_item = NULL; btrfs_item_key_to_cpu(eb, &wc->log_key, wc->log_slot); diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 13c514684cfb..8a08412f3529 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1364,7 +1364,9 @@ struct btrfs_super_block *btrfs_read_disk_super(struct block_device *bdev, (bytenr + BTRFS_SUPER_INFO_SIZE) >> PAGE_SHIFT); } + filemap_invalidate_lock(mapping); page = read_cache_page_gfp(mapping, bytenr >> PAGE_SHIFT, GFP_NOFS); + filemap_invalidate_unlock(mapping); if (IS_ERR(page)) return ERR_CAST(page); @@ -7257,6 +7259,7 @@ static int read_one_dev(struct extent_buffer *leaf, return -EINVAL; } } + set_bit(BTRFS_DEV_STATE_ITEM_FOUND, &device->dev_state); set_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &device->dev_state); if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state) && !test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)) { @@ -8083,6 +8086,45 @@ int btrfs_verify_dev_extents(struct btrfs_fs_info *fs_info) } /* + * Ensure that all devices registered in the fs have their device items in the + * chunk tree. + * + * Return true if unexpected device is found. + * Return false otherwise. + */ +bool btrfs_verify_dev_items(const struct btrfs_fs_info *fs_info) +{ + struct btrfs_fs_devices *seed_devs; + struct btrfs_device *dev; + bool ret = false; + + mutex_lock(&uuid_mutex); + list_for_each_entry(dev, &fs_info->fs_devices->devices, dev_list) { + if (!test_bit(BTRFS_DEV_STATE_ITEM_FOUND, &dev->dev_state)) { + btrfs_err(fs_info, + "devid %llu path %s is registered but not found in chunk tree", + dev->devid, btrfs_dev_name(dev)); + ret = true; + } + } + list_for_each_entry(seed_devs, &fs_info->fs_devices->seed_list, seed_list) { + list_for_each_entry(dev, &seed_devs->devices, dev_list) { + if (!test_bit(BTRFS_DEV_STATE_ITEM_FOUND, &dev->dev_state)) { + btrfs_err(fs_info, + "devid %llu path %s is registered but not found in chunk tree", + dev->devid, btrfs_dev_name(dev)); + ret = true; + } + } + } + mutex_unlock(&uuid_mutex); + if (ret) + btrfs_err(fs_info, +"remove the above devices or use 'btrfs device scan --forget <dev>' to unregister them before mount"); + return ret; +} + +/* * Check whether the given block group or device is pinned by any inode being * used as a swapfile. */ diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 34b854c1a303..f20abeb16bce 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -100,6 +100,9 @@ enum btrfs_raid_types { #define BTRFS_DEV_STATE_FLUSH_SENT (4) #define BTRFS_DEV_STATE_NO_READA (5) +/* Set when the device item is found in chunk tree, used to catch unexpected registered device. */ +#define BTRFS_DEV_STATE_ITEM_FOUND (7) + /* Special value encoding failure to write primary super block. */ #define BTRFS_SUPER_PRIMARY_WRITE_ERROR (INT_MAX / 2) @@ -893,6 +896,7 @@ enum btrfs_raid_types __attribute_const__ btrfs_bg_flags_to_raid_index(u64 flags int btrfs_bg_type_to_factor(u64 flags); const char *btrfs_bg_type_to_raid_name(u64 flags); int btrfs_verify_dev_extents(struct btrfs_fs_info *fs_info); +bool btrfs_verify_dev_items(const struct btrfs_fs_info *fs_info); bool btrfs_repair_one_zone(struct btrfs_fs_info *fs_info, u64 logical); bool btrfs_pinned_by_swapfile(struct btrfs_fs_info *fs_info, void *ptr); diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index 0550fd30fd10..635fb8a52e0c 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c @@ -393,9 +393,11 @@ out: repair_branches: ret2 = 0; + ext4_double_down_write_data_sem(orig_inode, donor_inode); r_len = ext4_swap_extents(handle, donor_inode, orig_inode, mext->donor_lblk, orig_map->m_lblk, *m_len, 0, &ret2); + ext4_double_up_write_data_sem(orig_inode, donor_inode); if (ret2 || r_len != *m_len) { ext4_error_inode_block(orig_inode, (sector_t)(orig_map->m_lblk), EIO, "Unable to copy data block, data will be lost!"); diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 2e02efbddaac..4ed8ddf2a60b 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -1037,6 +1037,7 @@ static int ext4_xattr_inode_update_ref(handle_t *handle, struct inode *ea_inode, ext4_error_inode(ea_inode, __func__, __LINE__, 0, "EA inode %lu ref wraparound: ref_count=%lld ref_change=%d", ea_inode->i_ino, ref_count, ref_change); + brelse(iloc.bh); ret = -EFSCORRUPTED; goto out; } diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 6800886c4d10..baa2f2141146 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -2750,8 +2750,13 @@ static void wait_sb_inodes(struct super_block *sb) * The mapping can appear untagged while still on-list since we * do not have the mapping lock. Skip it here, wb completion * will remove it. + * + * If the mapping does not have data integrity semantics, + * there's no need to wait for the writeout to complete, as the + * mapping cannot guarantee that data is persistently stored. */ - if (!mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK)) + if (!mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK) || + mapping_no_data_integrity(mapping)) continue; spin_unlock_irq(&sb->s_inode_wblist_lock); diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 01bc894e9c2b..3b2a171e652f 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -3200,8 +3200,10 @@ void fuse_init_file_inode(struct inode *inode, unsigned int flags) inode->i_fop = &fuse_file_operations; inode->i_data.a_ops = &fuse_file_aops; - if (fc->writeback_cache) + if (fc->writeback_cache) { mapping_set_writeback_may_deadlock_on_reclaim(&inode->i_data); + mapping_set_no_data_integrity(&inode->i_data); + } INIT_LIST_HEAD(&fi->write_files); INIT_LIST_HEAD(&fi->queued_writes); diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c index 97ebe457c00a..d27a0b1080a9 100644 --- a/fs/gfs2/lops.c +++ b/fs/gfs2/lops.c @@ -484,7 +484,7 @@ static struct bio *gfs2_chain_bio(struct bio *prev, unsigned int nr_iovecs) new = bio_alloc(prev->bi_bdev, nr_iovecs, prev->bi_opf, GFP_NOIO); bio_clone_blkg_association(new, prev); new->bi_iter.bi_sector = bio_end_sector(prev); - bio_chain(prev, new); + bio_chain(new, prev); submit_bio(prev); return new; } 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 diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c index f585359684d4..e4273932e7e4 100644 --- a/fs/smb/server/transport_rdma.c +++ b/fs/smb/server/transport_rdma.c @@ -1353,14 +1353,12 @@ static int get_sg_list(void *buf, int size, struct scatterlist *sg_list, int nen static int get_mapped_sg_list(struct ib_device *device, void *buf, int size, struct scatterlist *sg_list, int nentries, - enum dma_data_direction dir) + enum dma_data_direction dir, int *npages) { - int npages; - - npages = get_sg_list(buf, size, sg_list, nentries); - if (npages < 0) + *npages = get_sg_list(buf, size, sg_list, nentries); + if (*npages < 0) return -EINVAL; - return ib_dma_map_sg(device, sg_list, npages, dir); + return ib_dma_map_sg(device, sg_list, *npages, dir); } static int post_sendmsg(struct smbdirect_socket *sc, @@ -1431,12 +1429,13 @@ static int smb_direct_post_send_data(struct smbdirect_socket *sc, for (i = 0; i < niov; i++) { struct ib_sge *sge; int sg_cnt; + int npages; sg_init_table(sg, SMBDIRECT_SEND_IO_MAX_SGE - 1); sg_cnt = get_mapped_sg_list(sc->ib.dev, iov[i].iov_base, iov[i].iov_len, sg, SMBDIRECT_SEND_IO_MAX_SGE - 1, - DMA_TO_DEVICE); + DMA_TO_DEVICE, &npages); if (sg_cnt <= 0) { pr_err("failed to map buffer\n"); ret = -ENOMEM; @@ -1444,7 +1443,7 @@ static int smb_direct_post_send_data(struct smbdirect_socket *sc, } else if (sg_cnt + msg->num_sge > SMBDIRECT_SEND_IO_MAX_SGE) { pr_err("buffer not fitted into sges\n"); ret = -E2BIG; - ib_dma_unmap_sg(sc->ib.dev, sg, sg_cnt, + ib_dma_unmap_sg(sc->ib.dev, sg, npages, DMA_TO_DEVICE); goto err; } @@ -2708,6 +2707,7 @@ int ksmbd_rdma_init(void) { int ret; + smb_direct_port = SMB_DIRECT_PORT_INFINIBAND; smb_direct_listener.cm_id = NULL; ret = ib_register_client(&smb_direct_ib_client); diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index f891344bd76b..b8e648b8300f 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -1227,7 +1227,7 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath, } /** - * ksmbd_vfs_kern_path_start_remove() - lookup a file and get path info prior to removal + * ksmbd_vfs_kern_path_start_removing() - lookup a file and get path info prior to removal * @work: work * @filepath: file path that is relative to share * @flags: lookup flags diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c index d97295eaebe6..c19d6d713780 100644 --- a/fs/xfs/libxfs/xfs_ialloc.c +++ b/fs/xfs/libxfs/xfs_ialloc.c @@ -848,15 +848,16 @@ sparse_alloc: * invalid inode records, such as records that start at agbno 0 * or extend beyond the AG. * - * Set min agbno to the first aligned, non-zero agbno and max to - * the last aligned agbno that is at least one full chunk from - * the end of the AG. + * Set min agbno to the first chunk aligned, non-zero agbno and + * max to one less than the last chunk aligned agbno from the + * end of the AG. We subtract 1 from max so that the cluster + * allocation alignment takes over and allows allocation within + * the last full inode chunk in the AG. */ args.min_agbno = args.mp->m_sb.sb_inoalignmt; args.max_agbno = round_down(xfs_ag_block_count(args.mp, pag_agno(pag)), - args.mp->m_sb.sb_inoalignmt) - - igeo->ialloc_blks; + args.mp->m_sb.sb_inoalignmt) - 1; error = xfs_alloc_vextent_near_bno(&args, xfs_agbno_to_fsb(pag, diff --git a/fs/xfs/libxfs/xfs_rtgroup.c b/fs/xfs/libxfs/xfs_rtgroup.c index 9186c58e83d5..be16efaa6925 100644 --- a/fs/xfs/libxfs/xfs_rtgroup.c +++ b/fs/xfs/libxfs/xfs_rtgroup.c @@ -48,6 +48,31 @@ xfs_rtgroup_min_block( return 0; } +/* Compute the number of rt extents in this realtime group. */ +static xfs_rtxnum_t +__xfs_rtgroup_extents( + struct xfs_mount *mp, + xfs_rgnumber_t rgno, + xfs_rgnumber_t rgcount, + xfs_rtbxlen_t rextents) +{ + ASSERT(rgno < rgcount); + if (rgno == rgcount - 1) + return rextents - ((xfs_rtxnum_t)rgno * mp->m_sb.sb_rgextents); + + ASSERT(xfs_has_rtgroups(mp)); + return mp->m_sb.sb_rgextents; +} + +xfs_rtxnum_t +xfs_rtgroup_extents( + struct xfs_mount *mp, + xfs_rgnumber_t rgno) +{ + return __xfs_rtgroup_extents(mp, rgno, mp->m_sb.sb_rgcount, + mp->m_sb.sb_rextents); +} + /* Precompute this group's geometry */ void xfs_rtgroup_calc_geometry( @@ -58,7 +83,8 @@ xfs_rtgroup_calc_geometry( xfs_rtbxlen_t rextents) { rtg->rtg_extents = __xfs_rtgroup_extents(mp, rgno, rgcount, rextents); - rtg_group(rtg)->xg_block_count = rtg->rtg_extents * mp->m_sb.sb_rextsize; + rtg_group(rtg)->xg_block_count = + rtg->rtg_extents * mp->m_sb.sb_rextsize; rtg_group(rtg)->xg_min_gbno = xfs_rtgroup_min_block(mp, rgno); } @@ -136,31 +162,6 @@ out_unwind_new_rtgs: return error; } -/* Compute the number of rt extents in this realtime group. */ -xfs_rtxnum_t -__xfs_rtgroup_extents( - struct xfs_mount *mp, - xfs_rgnumber_t rgno, - xfs_rgnumber_t rgcount, - xfs_rtbxlen_t rextents) -{ - ASSERT(rgno < rgcount); - if (rgno == rgcount - 1) - return rextents - ((xfs_rtxnum_t)rgno * mp->m_sb.sb_rgextents); - - ASSERT(xfs_has_rtgroups(mp)); - return mp->m_sb.sb_rgextents; -} - -xfs_rtxnum_t -xfs_rtgroup_extents( - struct xfs_mount *mp, - xfs_rgnumber_t rgno) -{ - return __xfs_rtgroup_extents(mp, rgno, mp->m_sb.sb_rgcount, - mp->m_sb.sb_rextents); -} - /* * Update the rt extent count of the previous tail rtgroup if it changed during * recovery (i.e. recovery of a growfs). diff --git a/fs/xfs/libxfs/xfs_rtgroup.h b/fs/xfs/libxfs/xfs_rtgroup.h index 03f1e2493334..73cace4d25c7 100644 --- a/fs/xfs/libxfs/xfs_rtgroup.h +++ b/fs/xfs/libxfs/xfs_rtgroup.h @@ -285,8 +285,6 @@ void xfs_free_rtgroups(struct xfs_mount *mp, xfs_rgnumber_t first_rgno, int xfs_initialize_rtgroups(struct xfs_mount *mp, xfs_rgnumber_t first_rgno, xfs_rgnumber_t end_rgno, xfs_rtbxlen_t rextents); -xfs_rtxnum_t __xfs_rtgroup_extents(struct xfs_mount *mp, xfs_rgnumber_t rgno, - xfs_rgnumber_t rgcount, xfs_rtbxlen_t rextents); xfs_rtxnum_t xfs_rtgroup_extents(struct xfs_mount *mp, xfs_rgnumber_t rgno); void xfs_rtgroup_calc_geometry(struct xfs_mount *mp, struct xfs_rtgroup *rtg, xfs_rgnumber_t rgno, xfs_rgnumber_t rgcount, diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index a311385b23d8..d4544ccafea5 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -1180,9 +1180,11 @@ xfs_log_cover( int error = 0; bool need_covered; - ASSERT((xlog_cil_empty(mp->m_log) && xlog_iclogs_empty(mp->m_log) && - !xfs_ail_min_lsn(mp->m_log->l_ailp)) || - xlog_is_shutdown(mp->m_log)); + if (!xlog_is_shutdown(mp->m_log)) { + ASSERT(xlog_cil_empty(mp->m_log)); + ASSERT(xlog_iclogs_empty(mp->m_log)); + ASSERT(!xfs_ail_min_lsn(mp->m_log->l_ailp)); + } if (!xfs_log_writable(mp)) return 0; diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index e063f4f2f2e6..a12ffed12391 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -126,7 +126,7 @@ xfs_rtcopy_summary( error = 0; out: xfs_rtbuf_cache_relse(oargs); - return 0; + return error; } /* * Mark an extent specified by start and len allocated. @@ -1265,7 +1265,7 @@ xfs_growfs_check_rtgeom( uint32_t rem; if (rextsize != 1) - return -EINVAL; + goto out_inval; div_u64_rem(nmp->m_sb.sb_rblocks, gblocks, &rem); if (rem) { xfs_warn(mp, @@ -1326,7 +1326,7 @@ xfs_grow_last_rtg( return true; if (mp->m_sb.sb_rgcount == 0) return false; - return xfs_rtgroup_extents(mp, mp->m_sb.sb_rgcount - 1) <= + return xfs_rtgroup_extents(mp, mp->m_sb.sb_rgcount - 1) < mp->m_sb.sb_rgextents; } |
