summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/Kconfig6
-rw-r--r--fs/btrfs/disk-io.c19
-rw-r--r--fs/btrfs/fs.h8
-rw-r--r--fs/btrfs/inode.c9
-rw-r--r--fs/btrfs/reflink.c23
-rw-r--r--fs/btrfs/send.c2
-rw-r--r--fs/btrfs/space-info.c8
-rw-r--r--fs/btrfs/sysfs.c52
-rw-r--r--fs/btrfs/tests/extent-map-tests.c3
-rw-r--r--fs/btrfs/tests/qgroup-tests.c6
-rw-r--r--fs/btrfs/tree-log.c2
-rw-r--r--fs/btrfs/volumes.c42
-rw-r--r--fs/btrfs/volumes.h4
-rw-r--r--fs/ext4/move_extent.c2
-rw-r--r--fs/ext4/xattr.c1
-rw-r--r--fs/fs-writeback.c7
-rw-r--r--fs/fuse/file.c4
-rw-r--r--fs/gfs2/lops.c2
-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
-rw-r--r--fs/smb/server/transport_rdma.c16
-rw-r--r--fs/smb/server/vfs.c2
-rw-r--r--fs/xfs/libxfs/xfs_ialloc.c11
-rw-r--r--fs/xfs/libxfs/xfs_rtgroup.c53
-rw-r--r--fs/xfs/libxfs/xfs_rtgroup.h2
-rw-r--r--fs/xfs/xfs_log.c8
-rw-r--r--fs/xfs/xfs_rtalloc.c6
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;
}