summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/afs/rxrpc.c6
-rw-r--r--fs/binfmt_elf.c2
-rw-r--r--fs/btrfs/accessors.h1
-rw-r--r--fs/btrfs/acl.c25
-rw-r--r--fs/btrfs/backref.c37
-rw-r--r--fs/btrfs/backref.h7
-rw-r--r--fs/btrfs/bio.c290
-rw-r--r--fs/btrfs/bio.h39
-rw-r--r--fs/btrfs/block-group.c79
-rw-r--r--fs/btrfs/block-group.h2
-rw-r--r--fs/btrfs/block-rsv.c14
-rw-r--r--fs/btrfs/btrfs_inode.h20
-rw-r--r--fs/btrfs/compression.c47
-rw-r--r--fs/btrfs/compression.h15
-rw-r--r--fs/btrfs/ctree.c238
-rw-r--r--fs/btrfs/ctree.h18
-rw-r--r--fs/btrfs/defrag.c5
-rw-r--r--fs/btrfs/delalloc-space.c4
-rw-r--r--fs/btrfs/delayed-inode.c26
-rw-r--r--fs/btrfs/delayed-ref.c45
-rw-r--r--fs/btrfs/dev-replace.c4
-rw-r--r--fs/btrfs/dir-item.c4
-rw-r--r--fs/btrfs/direct-io.c10
-rw-r--r--fs/btrfs/disk-io.c66
-rw-r--r--fs/btrfs/disk-io.h3
-rw-r--r--fs/btrfs/extent-tree.c172
-rw-r--r--fs/btrfs/extent-tree.h27
-rw-r--r--fs/btrfs/extent_io.c57
-rw-r--r--fs/btrfs/extent_io.h1
-rw-r--r--fs/btrfs/extent_map.h3
-rw-r--r--fs/btrfs/file-item.c85
-rw-r--r--fs/btrfs/file-item.h4
-rw-r--r--fs/btrfs/file.c33
-rw-r--r--fs/btrfs/free-space-cache.c24
-rw-r--r--fs/btrfs/free-space-tree.c55
-rw-r--r--fs/btrfs/fs.h36
-rw-r--r--fs/btrfs/inode-item.c5
-rw-r--r--fs/btrfs/inode.c192
-rw-r--r--fs/btrfs/ioctl.c178
-rw-r--r--fs/btrfs/messages.c1
-rw-r--r--fs/btrfs/messages.h3
-rw-r--r--fs/btrfs/misc.h7
-rw-r--r--fs/btrfs/ordered-data.c74
-rw-r--r--fs/btrfs/print-tree.c16
-rw-r--r--fs/btrfs/qgroup.c182
-rw-r--r--fs/btrfs/raid-stripe-tree.c18
-rw-r--r--fs/btrfs/raid56.c839
-rw-r--r--fs/btrfs/raid56.h103
-rw-r--r--fs/btrfs/reflink.c15
-rw-r--r--fs/btrfs/relocation.c85
-rw-r--r--fs/btrfs/root-tree.c4
-rw-r--r--fs/btrfs/scrub.c270
-rw-r--r--fs/btrfs/send.c152
-rw-r--r--fs/btrfs/space-info.c464
-rw-r--r--fs/btrfs/space-info.h43
-rw-r--r--fs/btrfs/subpage.c67
-rw-r--r--fs/btrfs/subpage.h1
-rw-r--r--fs/btrfs/super.c77
-rw-r--r--fs/btrfs/sysfs.c58
-rw-r--r--fs/btrfs/sysfs.h3
-rw-r--r--fs/btrfs/tests/extent-io-tests.c3
-rw-r--r--fs/btrfs/tests/extent-map-tests.c6
-rw-r--r--fs/btrfs/tests/qgroup-tests.c16
-rw-r--r--fs/btrfs/transaction.c48
-rw-r--r--fs/btrfs/transaction.h4
-rw-r--r--fs/btrfs/tree-checker.c23
-rw-r--r--fs/btrfs/tree-log.c183
-rw-r--r--fs/btrfs/tree-log.h8
-rw-r--r--fs/btrfs/uuid-tree.c120
-rw-r--r--fs/btrfs/verity.c32
-rw-r--r--fs/btrfs/volumes.c199
-rw-r--r--fs/btrfs/volumes.h10
-rw-r--r--fs/btrfs/xattr.c41
-rw-r--r--fs/btrfs/zoned.c64
-rw-r--r--fs/btrfs/zoned.h7
-rw-r--r--fs/ceph/dir.c5
-rw-r--r--fs/ceph/inode.c49
-rw-r--r--fs/ceph/xattr.c6
-rw-r--r--fs/coredump.c2
-rw-r--r--fs/dlm/lowcomms.c8
-rw-r--r--fs/erofs/compress.h12
-rw-r--r--fs/erofs/decompressor.c149
-rw-r--r--fs/erofs/decompressor_crypto.c7
-rw-r--r--fs/erofs/decompressor_deflate.c37
-rw-r--r--fs/erofs/decompressor_lzma.c26
-rw-r--r--fs/erofs/decompressor_zstd.c28
-rw-r--r--fs/erofs/erofs_fs.h2
-rw-r--r--fs/erofs/fileio.c2
-rw-r--r--fs/erofs/fscache.c4
-rw-r--r--fs/erofs/super.c38
-rw-r--r--fs/erofs/zdata.c21
-rw-r--r--fs/exec.c2
-rw-r--r--fs/ext4/balloc.c2
-rw-r--r--fs/ext4/dir.c8
-rw-r--r--fs/ext4/ext4.h50
-rw-r--r--fs/ext4/ext4_jbd2.c3
-rw-r--r--fs/ext4/extents.c28
-rw-r--r--fs/ext4/extents_status.c31
-rw-r--r--fs/ext4/extents_status.h2
-rw-r--r--fs/ext4/hash.c2
-rw-r--r--fs/ext4/ialloc.c1
-rw-r--r--fs/ext4/inline.c14
-rw-r--r--fs/ext4/inode.c165
-rw-r--r--fs/ext4/ioctl.c14
-rw-r--r--fs/ext4/mballoc.c188
-rw-r--r--fs/ext4/move_extent.c786
-rw-r--r--fs/ext4/namei.c18
-rw-r--r--fs/ext4/orphan.c4
-rw-r--r--fs/ext4/readpage.c7
-rw-r--r--fs/ext4/super.c72
-rw-r--r--fs/ext4/sysfs.c6
-rw-r--r--fs/ext4/verity.c2
-rw-r--r--fs/ext4/xattr.c6
-rw-r--r--fs/fuse/dev_uring.c7
-rw-r--r--fs/gfs2/aops.c2
-rw-r--r--fs/gfs2/file.c2
-rw-r--r--fs/gfs2/glock.c227
-rw-r--r--fs/gfs2/glock.h12
-rw-r--r--fs/gfs2/glops.c98
-rw-r--r--fs/gfs2/incore.h22
-rw-r--r--fs/gfs2/inode.c15
-rw-r--r--fs/gfs2/inode.h1
-rw-r--r--fs/gfs2/lock_dlm.c57
-rw-r--r--fs/gfs2/log.c59
-rw-r--r--fs/gfs2/lops.c12
-rw-r--r--fs/gfs2/meta_io.c13
-rw-r--r--fs/gfs2/ops_fstype.c37
-rw-r--r--fs/gfs2/quota.c66
-rw-r--r--fs/gfs2/recovery.c8
-rw-r--r--fs/gfs2/super.c35
-rw-r--r--fs/gfs2/super.h1
-rw-r--r--fs/gfs2/sys.c64
-rw-r--r--fs/gfs2/trace_gfs2.h1
-rw-r--r--fs/gfs2/trans.c30
-rw-r--r--fs/gfs2/util.c328
-rw-r--r--fs/gfs2/util.h56
-rw-r--r--fs/hfs/.kunitconfig7
-rw-r--r--fs/hfs/Kconfig15
-rw-r--r--fs/hfs/Makefile2
-rw-r--r--fs/hfs/bfind.c2
-rw-r--r--fs/hfs/bnode.c52
-rw-r--r--fs/hfs/brec.c2
-rw-r--r--fs/hfs/btree.c2
-rw-r--r--fs/hfs/btree.h113
-rw-r--r--fs/hfs/catalog.c2
-rw-r--r--fs/hfs/hfs.h269
-rw-r--r--fs/hfs/hfs_fs.h89
-rw-r--r--fs/hfs/inode.c3
-rw-r--r--fs/hfs/string.c5
-rw-r--r--fs/hfs/string_test.c133
-rw-r--r--fs/hfsplus/.kunitconfig8
-rw-r--r--fs/hfsplus/Kconfig15
-rw-r--r--fs/hfsplus/Makefile3
-rw-r--r--fs/hfsplus/bfind.c2
-rw-r--r--fs/hfsplus/bnode.c64
-rw-r--r--fs/hfsplus/brec.c2
-rw-r--r--fs/hfsplus/btree.c2
-rw-r--r--fs/hfsplus/dir.c7
-rw-r--r--fs/hfsplus/hfsplus_fs.h41
-rw-r--r--fs/hfsplus/hfsplus_raw.h394
-rw-r--r--fs/hfsplus/inode.c41
-rw-r--r--fs/hfsplus/super.c87
-rw-r--r--fs/hfsplus/unicode.c16
-rw-r--r--fs/hfsplus/unicode_test.c1579
-rw-r--r--fs/hfsplus/xattr.c22
-rw-r--r--fs/jbd2/checkpoint.c2
-rw-r--r--fs/jbd2/journal.c35
-rw-r--r--fs/jbd2/transaction.c26
-rw-r--r--fs/lockd/netlink.c1
-rw-r--r--fs/lockd/netlink.h1
-rw-r--r--fs/nfsd/netlink.c1
-rw-r--r--fs/nfsd/netlink.h1
-rw-r--r--fs/ntfs3/attrib.c88
-rw-r--r--fs/ntfs3/dir.c3
-rw-r--r--fs/ntfs3/file.c109
-rw-r--r--fs/ntfs3/frecord.c219
-rw-r--r--fs/ntfs3/fsntfs.c132
-rw-r--r--fs/ntfs3/index.c3
-rw-r--r--fs/ntfs3/inode.c27
-rw-r--r--fs/ntfs3/namei.c6
-rw-r--r--fs/ntfs3/ntfs_fs.h40
-rw-r--r--fs/ntfs3/record.c2
-rw-r--r--fs/ntfs3/run.c17
-rw-r--r--fs/ntfs3/super.c88
-rw-r--r--fs/ntfs3/xattr.c18
-rw-r--r--fs/ocfs2/cluster/tcp.c6
-rw-r--r--fs/resctrl/ctrlmondata.c309
-rw-r--r--fs/resctrl/internal.h17
-rw-r--r--fs/resctrl/rdtgroup.c82
-rw-r--r--fs/select.c12
-rw-r--r--fs/smb/client/cifs_debug.c23
-rw-r--r--fs/smb/client/cifsglob.h31
-rw-r--r--fs/smb/client/cifspdu.h601
-rw-r--r--fs/smb/client/cifssmb.c18
-rw-r--r--fs/smb/client/cifstransport.c8
-rw-r--r--fs/smb/client/connect.c10
-rw-r--r--fs/smb/client/inode.c4
-rw-r--r--fs/smb/client/misc.c2
-rw-r--r--fs/smb/client/ntlmssp.h8
-rw-r--r--fs/smb/client/readdir.c12
-rw-r--r--fs/smb/client/reparse.h8
-rw-r--r--fs/smb/client/rfc1002pdu.h8
-rw-r--r--fs/smb/client/smb1ops.c2
-rw-r--r--fs/smb/client/smb2inode.c2
-rw-r--r--fs/smb/client/smb2ops.c91
-rw-r--r--fs/smb/client/smb2pdu.c45
-rw-r--r--fs/smb/client/smb2pdu.h112
-rw-r--r--fs/smb/client/smbdirect.c28
-rw-r--r--fs/smb/common/cifsglob.h30
-rw-r--r--fs/smb/common/fscc.h174
-rw-r--r--fs/smb/common/smb2pdu.h279
-rw-r--r--fs/smb/common/smbacl.h8
-rw-r--r--fs/smb/common/smbdirect/smbdirect_socket.h51
-rw-r--r--fs/smb/common/smbglob.h72
-rw-r--r--fs/smb/server/Kconfig6
-rw-r--r--fs/smb/server/auth.c390
-rw-r--r--fs/smb/server/auth.h10
-rw-r--r--fs/smb/server/crypto_ctx.c24
-rw-r--r--fs/smb/server/crypto_ctx.h15
-rw-r--r--fs/smb/server/mgmt/tree_connect.c18
-rw-r--r--fs/smb/server/mgmt/tree_connect.h1
-rw-r--r--fs/smb/server/misc.c15
-rw-r--r--fs/smb/server/oplock.c8
-rw-r--r--fs/smb/server/server.c4
-rw-r--r--fs/smb/server/smb2misc.c2
-rw-r--r--fs/smb/server/smb2ops.c38
-rw-r--r--fs/smb/server/smb2pdu.c217
-rw-r--r--fs/smb/server/smb2pdu.h107
-rw-r--r--fs/smb/server/smb_common.h276
-rw-r--r--fs/smb/server/transport_ipc.c7
-rw-r--r--fs/smb/server/transport_rdma.c40
-rw-r--r--fs/smb/server/transport_tcp.c45
-rw-r--r--fs/smb/server/vfs.c7
-rw-r--r--fs/smb/server/vfs_cache.c88
-rw-r--r--fs/xfs/libxfs/xfs_group.h9
-rw-r--r--fs/xfs/libxfs/xfs_log_format.h38
-rw-r--r--fs/xfs/libxfs/xfs_ondisk.h6
-rw-r--r--fs/xfs/libxfs/xfs_quota_defs.h4
-rw-r--r--fs/xfs/libxfs/xfs_rtgroup.h14
-rw-r--r--fs/xfs/libxfs/xfs_zones.c1
-rw-r--r--fs/xfs/scrub/quota.c8
-rw-r--r--fs/xfs/scrub/quota_repair.c18
-rw-r--r--fs/xfs/scrub/quotacheck.c11
-rw-r--r--fs/xfs/scrub/quotacheck_repair.c21
-rw-r--r--fs/xfs/xfs_dquot.c143
-rw-r--r--fs/xfs/xfs_dquot.h22
-rw-r--r--fs/xfs/xfs_dquot_item.c6
-rw-r--r--fs/xfs/xfs_icache.c31
-rw-r--r--fs/xfs/xfs_log.c206
-rw-r--r--fs/xfs/xfs_log_cil.c6
-rw-r--r--fs/xfs/xfs_log_priv.h33
-rw-r--r--fs/xfs/xfs_log_recover.c45
-rw-r--r--fs/xfs/xfs_qm.c154
-rw-r--r--fs/xfs/xfs_qm.h2
-rw-r--r--fs/xfs/xfs_qm_bhv.c4
-rw-r--r--fs/xfs/xfs_qm_syscalls.c10
-rw-r--r--fs/xfs/xfs_quotaops.c2
-rw-r--r--fs/xfs/xfs_trace.h8
-rw-r--r--fs/xfs/xfs_trans_dquot.c18
-rw-r--r--fs/xfs/xfs_zone_alloc.c28
-rw-r--r--fs/xfs/xfs_zone_gc.c14
-rw-r--r--fs/xfs/xfs_zone_priv.h1
-rw-r--r--fs/xfs/xfs_zone_space_resv.c10
263 files changed, 8599 insertions, 7528 deletions
diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c
index c1cadf8fb346..bf0e4ea0aafd 100644
--- a/fs/afs/rxrpc.c
+++ b/fs/afs/rxrpc.c
@@ -82,16 +82,16 @@ int afs_open_socket(struct afs_net *net)
if (ret < 0)
pr_err("Couldn't create RxGK CM key: %d\n", ret);
- ret = kernel_bind(socket, (struct sockaddr *) &srx, sizeof(srx));
+ ret = kernel_bind(socket, (struct sockaddr_unsized *) &srx, sizeof(srx));
if (ret == -EADDRINUSE) {
srx.transport.sin6.sin6_port = 0;
- ret = kernel_bind(socket, (struct sockaddr *) &srx, sizeof(srx));
+ ret = kernel_bind(socket, (struct sockaddr_unsized *) &srx, sizeof(srx));
}
if (ret < 0)
goto error_2;
srx.srx_service = YFS_CM_SERVICE;
- ret = kernel_bind(socket, (struct sockaddr *) &srx, sizeof(srx));
+ ret = kernel_bind(socket, (struct sockaddr_unsized *) &srx, sizeof(srx));
if (ret < 0)
goto error_2;
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index e4653bb99946..3eb734c192e9 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -46,7 +46,7 @@
#include <linux/cred.h>
#include <linux/dax.h>
#include <linux/uaccess.h>
-#include <linux/rseq.h>
+#include <uapi/linux/rseq.h>
#include <asm/param.h>
#include <asm/page.h>
diff --git a/fs/btrfs/accessors.h b/fs/btrfs/accessors.h
index 99b3ced12805..78721412951c 100644
--- a/fs/btrfs/accessors.h
+++ b/fs/btrfs/accessors.h
@@ -12,6 +12,7 @@
#include <linux/string.h>
#include <linux/mm.h>
#include <uapi/linux/btrfs_tree.h>
+#include "fs.h"
#include "extent_io.h"
struct extent_buffer;
diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c
index e0ba00d64ea0..c336e2ab7f8a 100644
--- a/fs/btrfs/acl.c
+++ b/fs/btrfs/acl.c
@@ -14,12 +14,13 @@
#include "ctree.h"
#include "xattr.h"
#include "acl.h"
+#include "misc.h"
struct posix_acl *btrfs_get_acl(struct inode *inode, int type, bool rcu)
{
int size;
const char *name;
- char *value = NULL;
+ char AUTO_KFREE(value);
struct posix_acl *acl;
if (rcu)
@@ -49,7 +50,6 @@ struct posix_acl *btrfs_get_acl(struct inode *inode, int type, bool rcu)
acl = NULL;
else
acl = ERR_PTR(size);
- kfree(value);
return acl;
}
@@ -59,7 +59,7 @@ int __btrfs_set_acl(struct btrfs_trans_handle *trans, struct inode *inode,
{
int ret, size = 0;
const char *name;
- char *value = NULL;
+ char AUTO_KFREE(value);
switch (type) {
case ACL_TYPE_ACCESS:
@@ -85,28 +85,23 @@ int __btrfs_set_acl(struct btrfs_trans_handle *trans, struct inode *inode,
nofs_flag = memalloc_nofs_save();
value = kmalloc(size, GFP_KERNEL);
memalloc_nofs_restore(nofs_flag);
- if (!value) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!value)
+ return -ENOMEM;
ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
if (ret < 0)
- goto out;
+ return ret;
}
if (trans)
ret = btrfs_setxattr(trans, inode, name, value, size, 0);
else
ret = btrfs_setxattr_trans(inode, name, value, size, 0);
+ if (ret < 0)
+ return ret;
-out:
- kfree(value);
-
- if (!ret)
- set_cached_acl(inode, type, acl);
-
- return ret;
+ set_cached_acl(inode, type, acl);
+ return 0;
}
int btrfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index 2ab550a1e715..78da47a3d00e 100644
--- a/fs/btrfs/backref.c
+++ b/fs/btrfs/backref.c
@@ -666,10 +666,9 @@ static int resolve_indirect_ref(struct btrfs_backref_walk_ctx *ctx,
ret = btrfs_search_old_slot(root, &search_key, path, ctx->time_seq);
btrfs_debug(ctx->fs_info,
- "search slot in root %llu (level %d, ref count %d) returned %d for key (%llu %u %llu)",
- ref->root_id, level, ref->count, ret,
- ref->key_for_search.objectid, ref->key_for_search.type,
- ref->key_for_search.offset);
+"search slot in root %llu (level %d, ref count %d) returned %d for key " BTRFS_KEY_FMT,
+ ref->root_id, level, ref->count, ret,
+ BTRFS_KEY_FMT_VALUE(&ref->key_for_search));
if (ret < 0)
goto out;
@@ -1409,12 +1408,12 @@ static int find_parent_nodes(struct btrfs_backref_walk_ctx *ctx,
if (!path)
return -ENOMEM;
if (!ctx->trans) {
- path->search_commit_root = 1;
- path->skip_locking = 1;
+ path->search_commit_root = true;
+ path->skip_locking = true;
}
if (ctx->time_seq == BTRFS_SEQ_LAST)
- path->skip_locking = 1;
+ path->skip_locking = true;
again:
head = NULL;
@@ -1561,7 +1560,7 @@ again:
btrfs_release_path(path);
- ret = add_missing_keys(ctx->fs_info, &preftrees, path->skip_locking == 0);
+ ret = add_missing_keys(ctx->fs_info, &preftrees, !path->skip_locking);
if (ret)
goto out;
@@ -2786,7 +2785,7 @@ struct btrfs_data_container *init_data_container(u32 total_bytes)
* allocates space to return multiple file system paths for an inode.
* total_bytes to allocate are passed, note that space usable for actual path
* information will be total_bytes - sizeof(struct inode_fs_paths).
- * the returned pointer must be freed with free_ipath() in the end.
+ * the returned pointer must be freed with __free_inode_fs_paths() in the end.
*/
struct inode_fs_paths *init_ipath(s32 total_bytes, struct btrfs_root *fs_root,
struct btrfs_path *path)
@@ -2811,14 +2810,6 @@ struct inode_fs_paths *init_ipath(s32 total_bytes, struct btrfs_root *fs_root,
return ifp;
}
-void free_ipath(struct inode_fs_paths *ipath)
-{
- if (!ipath)
- return;
- kvfree(ipath->fspath);
- kfree(ipath);
-}
-
struct btrfs_backref_iter *btrfs_backref_iter_alloc(struct btrfs_fs_info *fs_info)
{
struct btrfs_backref_iter *ret;
@@ -2834,8 +2825,8 @@ struct btrfs_backref_iter *btrfs_backref_iter_alloc(struct btrfs_fs_info *fs_inf
}
/* Current backref iterator only supports iteration in commit root */
- ret->path->search_commit_root = 1;
- ret->path->skip_locking = 1;
+ ret->path->search_commit_root = true;
+ ret->path->skip_locking = true;
ret->fs_info = fs_info;
return ret;
@@ -3308,8 +3299,8 @@ static int handle_indirect_tree_backref(struct btrfs_trans_handle *trans,
level = cur->level + 1;
/* Search the tree to find parent blocks referring to the block */
- path->search_commit_root = 1;
- path->skip_locking = 1;
+ path->search_commit_root = true;
+ path->skip_locking = true;
path->lowest_level = level;
ret = btrfs_search_slot(NULL, root, tree_key, path, 0, 0);
path->lowest_level = 0;
@@ -3323,9 +3314,9 @@ static int handle_indirect_tree_backref(struct btrfs_trans_handle *trans,
eb = path->nodes[level];
if (btrfs_node_blockptr(eb, path->slots[level]) != cur->bytenr) {
btrfs_err(fs_info,
-"couldn't find block (%llu) (level %d) in tree (%llu) with key (%llu %u %llu)",
+"couldn't find block (%llu) (level %d) in tree (%llu) with key " BTRFS_KEY_FMT,
cur->bytenr, level - 1, btrfs_root_id(root),
- tree_key->objectid, tree_key->type, tree_key->offset);
+ BTRFS_KEY_FMT_VALUE(tree_key));
btrfs_put_root(root);
ret = -ENOENT;
goto out;
diff --git a/fs/btrfs/backref.h b/fs/btrfs/backref.h
index 25d51c246070..1d009b0f4c69 100644
--- a/fs/btrfs/backref.h
+++ b/fs/btrfs/backref.h
@@ -241,7 +241,12 @@ char *btrfs_ref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
struct btrfs_data_container *init_data_container(u32 total_bytes);
struct inode_fs_paths *init_ipath(s32 total_bytes, struct btrfs_root *fs_root,
struct btrfs_path *path);
-void free_ipath(struct inode_fs_paths *ipath);
+
+DEFINE_FREE(inode_fs_paths, struct inode_fs_paths *,
+ if (_T) {
+ kvfree(_T->fspath);
+ kfree(_T);
+ })
int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid,
u64 start_off, struct btrfs_path *path,
diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c
index 21df48e6c4fa..fa1d321a2fb8 100644
--- a/fs/btrfs/bio.c
+++ b/fs/btrfs/bio.c
@@ -41,13 +41,17 @@ static bool bbio_has_ordered_extent(const struct btrfs_bio *bbio)
* Initialize a btrfs_bio structure. This skips the embedded bio itself as it
* is already initialized by the block layer.
*/
-void btrfs_bio_init(struct btrfs_bio *bbio, struct btrfs_fs_info *fs_info,
+void btrfs_bio_init(struct btrfs_bio *bbio, struct btrfs_inode *inode, u64 file_offset,
btrfs_bio_end_io_t end_io, void *private)
{
+ /* @inode parameter is mandatory. */
+ ASSERT(inode);
+
memset(bbio, 0, offsetof(struct btrfs_bio, bio));
- bbio->fs_info = fs_info;
+ bbio->inode = inode;
bbio->end_io = end_io;
bbio->private = private;
+ bbio->file_offset = file_offset;
atomic_set(&bbio->pending_ios, 1);
WRITE_ONCE(bbio->status, BLK_STS_OK);
}
@@ -60,7 +64,7 @@ void btrfs_bio_init(struct btrfs_bio *bbio, struct btrfs_fs_info *fs_info,
* a mempool.
*/
struct btrfs_bio *btrfs_bio_alloc(unsigned int nr_vecs, blk_opf_t opf,
- struct btrfs_fs_info *fs_info,
+ struct btrfs_inode *inode, u64 file_offset,
btrfs_bio_end_io_t end_io, void *private)
{
struct btrfs_bio *bbio;
@@ -68,7 +72,7 @@ struct btrfs_bio *btrfs_bio_alloc(unsigned int nr_vecs, blk_opf_t opf,
bio = bio_alloc_bioset(NULL, nr_vecs, opf, GFP_NOFS, &btrfs_bioset);
bbio = btrfs_bio(bio);
- btrfs_bio_init(bbio, fs_info, end_io, private);
+ btrfs_bio_init(bbio, inode, file_offset, end_io, private);
return bbio;
}
@@ -85,13 +89,13 @@ static struct btrfs_bio *btrfs_split_bio(struct btrfs_fs_info *fs_info,
return ERR_CAST(bio);
bbio = btrfs_bio(bio);
- btrfs_bio_init(bbio, fs_info, NULL, orig_bbio);
- bbio->inode = orig_bbio->inode;
- bbio->file_offset = orig_bbio->file_offset;
+ btrfs_bio_init(bbio, orig_bbio->inode, orig_bbio->file_offset, NULL, orig_bbio);
orig_bbio->file_offset += map_length;
if (bbio_has_ordered_extent(bbio)) {
refcount_inc(&orig_bbio->ordered->refs);
bbio->ordered = orig_bbio->ordered;
+ bbio->orig_logical = orig_bbio->orig_logical;
+ orig_bbio->orig_logical += map_length;
}
bbio->csum_search_commit_root = orig_bbio->csum_search_commit_root;
atomic_inc(&orig_bbio->pending_ios);
@@ -100,6 +104,12 @@ static struct btrfs_bio *btrfs_split_bio(struct btrfs_fs_info *fs_info,
void btrfs_bio_end_io(struct btrfs_bio *bbio, blk_status_t status)
{
+ /* Make sure we're already in task context. */
+ ASSERT(in_task());
+
+ if (bbio->async_csum)
+ wait_for_completion(&bbio->csum_done);
+
bbio->bio.bi_status = status;
if (bbio->bio.bi_pool == &btrfs_clone_bioset) {
struct btrfs_bio *orig_bbio = bbio->private;
@@ -163,11 +173,30 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio,
struct btrfs_failed_bio *fbio = repair_bbio->private;
struct btrfs_inode *inode = repair_bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
- struct bio_vec *bv = bio_first_bvec_all(&repair_bbio->bio);
+ /*
+ * We can not move forward the saved_iter, as it will be later
+ * utilized by repair_bbio again.
+ */
+ struct bvec_iter saved_iter = repair_bbio->saved_iter;
+ const u32 step = min(fs_info->sectorsize, PAGE_SIZE);
+ const u64 logical = repair_bbio->saved_iter.bi_sector << SECTOR_SHIFT;
+ const u32 nr_steps = repair_bbio->saved_iter.bi_size / step;
int mirror = repair_bbio->mirror_num;
+ phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
+ phys_addr_t paddr;
+ unsigned int slot = 0;
+
+ /* Repair bbio should be eaxctly one block sized. */
+ ASSERT(repair_bbio->saved_iter.bi_size == fs_info->sectorsize);
+
+ btrfs_bio_for_each_block(paddr, &repair_bbio->bio, &saved_iter, step) {
+ ASSERT(slot < nr_steps);
+ paddrs[slot] = paddr;
+ slot++;
+ }
if (repair_bbio->bio.bi_status ||
- !btrfs_data_csum_ok(repair_bbio, dev, 0, bvec_phys(bv))) {
+ !btrfs_data_csum_ok(repair_bbio, dev, 0, paddrs)) {
bio_reset(&repair_bbio->bio, NULL, REQ_OP_READ);
repair_bbio->bio.bi_iter = repair_bbio->saved_iter;
@@ -186,8 +215,7 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio,
mirror = prev_repair_mirror(fbio, mirror);
btrfs_repair_io_failure(fs_info, btrfs_ino(inode),
repair_bbio->file_offset, fs_info->sectorsize,
- repair_bbio->saved_iter.bi_sector << SECTOR_SHIFT,
- bvec_phys(bv), mirror);
+ logical, paddrs, step, mirror);
} while (mirror != fbio->bbio->mirror_num);
done:
@@ -204,21 +232,25 @@ done:
*/
static struct btrfs_failed_bio *repair_one_sector(struct btrfs_bio *failed_bbio,
u32 bio_offset,
- phys_addr_t paddr,
+ phys_addr_t paddrs[],
struct btrfs_failed_bio *fbio)
{
struct btrfs_inode *inode = failed_bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
- struct folio *folio = page_folio(phys_to_page(paddr));
const u32 sectorsize = fs_info->sectorsize;
- const u32 foff = offset_in_folio(folio, paddr);
- const u64 logical = (failed_bbio->saved_iter.bi_sector << SECTOR_SHIFT);
+ const u32 step = min(fs_info->sectorsize, PAGE_SIZE);
+ const u32 nr_steps = sectorsize / step;
+ /*
+ * For bs > ps cases, the saved_iter can be partially moved forward.
+ * In that case we should round it down to the block boundary.
+ */
+ const u64 logical = round_down(failed_bbio->saved_iter.bi_sector << SECTOR_SHIFT,
+ sectorsize);
struct btrfs_bio *repair_bbio;
struct bio *repair_bio;
int num_copies;
int mirror;
- ASSERT(foff + sectorsize <= folio_size(folio));
btrfs_debug(fs_info, "repair read error: read error at %llu",
failed_bbio->file_offset + bio_offset);
@@ -238,15 +270,22 @@ static struct btrfs_failed_bio *repair_one_sector(struct btrfs_bio *failed_bbio,
atomic_inc(&fbio->repair_count);
- repair_bio = bio_alloc_bioset(NULL, 1, REQ_OP_READ, GFP_NOFS,
+ repair_bio = bio_alloc_bioset(NULL, nr_steps, REQ_OP_READ, GFP_NOFS,
&btrfs_repair_bioset);
- repair_bio->bi_iter.bi_sector = failed_bbio->saved_iter.bi_sector;
- bio_add_folio_nofail(repair_bio, folio, sectorsize, foff);
+ repair_bio->bi_iter.bi_sector = logical >> SECTOR_SHIFT;
+ for (int i = 0; i < nr_steps; i++) {
+ int ret;
+
+ ASSERT(offset_in_page(paddrs[i]) + step <= PAGE_SIZE);
+
+ ret = bio_add_page(repair_bio, phys_to_page(paddrs[i]), step,
+ offset_in_page(paddrs[i]));
+ ASSERT(ret == step);
+ }
repair_bbio = btrfs_bio(repair_bio);
- btrfs_bio_init(repair_bbio, fs_info, NULL, fbio);
- repair_bbio->inode = failed_bbio->inode;
- repair_bbio->file_offset = failed_bbio->file_offset + bio_offset;
+ btrfs_bio_init(repair_bbio, failed_bbio->inode, failed_bbio->file_offset + bio_offset,
+ NULL, fbio);
mirror = next_repair_mirror(fbio, failed_bbio->mirror_num);
btrfs_debug(fs_info, "submitting repair read to mirror %d", mirror);
@@ -258,10 +297,13 @@ static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_device *de
{
struct btrfs_inode *inode = bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
- u32 sectorsize = fs_info->sectorsize;
+ const u32 sectorsize = fs_info->sectorsize;
+ const u32 step = min(sectorsize, PAGE_SIZE);
+ const u32 nr_steps = sectorsize / step;
struct bvec_iter *iter = &bbio->saved_iter;
blk_status_t status = bbio->bio.bi_status;
struct btrfs_failed_bio *fbio = NULL;
+ phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
phys_addr_t paddr;
u32 offset = 0;
@@ -280,13 +322,19 @@ static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_device *de
/* Clear the I/O error. A failed repair will reset it. */
bbio->bio.bi_status = BLK_STS_OK;
- btrfs_bio_for_each_block(paddr, &bbio->bio, iter, fs_info->sectorsize) {
- if (status || !btrfs_data_csum_ok(bbio, dev, offset, paddr))
- fbio = repair_one_sector(bbio, offset, paddr, fbio);
- offset += sectorsize;
+ btrfs_bio_for_each_block(paddr, &bbio->bio, iter, step) {
+ paddrs[(offset / step) % nr_steps] = paddr;
+ offset += step;
+
+ if (IS_ALIGNED(offset, sectorsize)) {
+ if (status ||
+ !btrfs_data_csum_ok(bbio, dev, offset - sectorsize, paddrs))
+ fbio = repair_one_sector(bbio, offset - sectorsize,
+ paddrs, fbio);
+ }
}
if (bbio->csum != bbio->csum_inline)
- kfree(bbio->csum);
+ kvfree(bbio->csum);
if (fbio)
btrfs_repair_done(fbio);
@@ -317,36 +365,35 @@ static struct workqueue_struct *btrfs_end_io_wq(const struct btrfs_fs_info *fs_i
return fs_info->endio_workers;
}
-static void btrfs_end_bio_work(struct work_struct *work)
+static void simple_end_io_work(struct work_struct *work)
{
struct btrfs_bio *bbio = container_of(work, struct btrfs_bio, end_io_work);
+ struct bio *bio = &bbio->bio;
- /* Metadata reads are checked and repaired by the submitter. */
- if (is_data_bbio(bbio))
- btrfs_check_read_bio(bbio, bbio->bio.bi_private);
- else
- btrfs_bio_end_io(bbio, bbio->bio.bi_status);
+ if (bio_op(bio) == REQ_OP_READ) {
+ /* Metadata reads are checked and repaired by the submitter. */
+ if (is_data_bbio(bbio))
+ return btrfs_check_read_bio(bbio, bbio->bio.bi_private);
+ return btrfs_bio_end_io(bbio, bbio->bio.bi_status);
+ }
+ if (bio_is_zone_append(bio) && !bio->bi_status)
+ btrfs_record_physical_zoned(bbio);
+ btrfs_bio_end_io(bbio, bbio->bio.bi_status);
}
static void btrfs_simple_end_io(struct bio *bio)
{
struct btrfs_bio *bbio = btrfs_bio(bio);
struct btrfs_device *dev = bio->bi_private;
- struct btrfs_fs_info *fs_info = bbio->fs_info;
+ struct btrfs_fs_info *fs_info = bbio->inode->root->fs_info;
btrfs_bio_counter_dec(fs_info);
if (bio->bi_status)
btrfs_log_dev_io_error(bio, dev);
- if (bio_op(bio) == REQ_OP_READ) {
- INIT_WORK(&bbio->end_io_work, btrfs_end_bio_work);
- queue_work(btrfs_end_io_wq(fs_info, bio), &bbio->end_io_work);
- } else {
- if (bio_is_zone_append(bio) && !bio->bi_status)
- btrfs_record_physical_zoned(bbio);
- btrfs_bio_end_io(bbio, bbio->bio.bi_status);
- }
+ INIT_WORK(&bbio->end_io_work, simple_end_io_work);
+ queue_work(btrfs_end_io_wq(fs_info, bio), &bbio->end_io_work);
}
static void btrfs_raid56_end_io(struct bio *bio)
@@ -354,6 +401,9 @@ static void btrfs_raid56_end_io(struct bio *bio)
struct btrfs_io_context *bioc = bio->bi_private;
struct btrfs_bio *bbio = btrfs_bio(bio);
+ /* RAID56 endio is always handled in workqueue. */
+ ASSERT(in_task());
+
btrfs_bio_counter_dec(bioc->fs_info);
bbio->mirror_num = bioc->mirror_num;
if (bio_op(bio) == REQ_OP_READ && is_data_bbio(bbio))
@@ -364,11 +414,12 @@ static void btrfs_raid56_end_io(struct bio *bio)
btrfs_put_bioc(bioc);
}
-static void btrfs_orig_write_end_io(struct bio *bio)
+static void orig_write_end_io_work(struct work_struct *work)
{
+ struct btrfs_bio *bbio = container_of(work, struct btrfs_bio, end_io_work);
+ struct bio *bio = &bbio->bio;
struct btrfs_io_stripe *stripe = bio->bi_private;
struct btrfs_io_context *bioc = stripe->bioc;
- struct btrfs_bio *bbio = btrfs_bio(bio);
btrfs_bio_counter_dec(bioc->fs_info);
@@ -393,8 +444,18 @@ static void btrfs_orig_write_end_io(struct bio *bio)
btrfs_put_bioc(bioc);
}
-static void btrfs_clone_write_end_io(struct bio *bio)
+static void btrfs_orig_write_end_io(struct bio *bio)
{
+ struct btrfs_bio *bbio = btrfs_bio(bio);
+
+ INIT_WORK(&bbio->end_io_work, orig_write_end_io_work);
+ queue_work(btrfs_end_io_wq(bbio->inode->root->fs_info, bio), &bbio->end_io_work);
+}
+
+static void clone_write_end_io_work(struct work_struct *work)
+{
+ struct btrfs_bio *bbio = container_of(work, struct btrfs_bio, end_io_work);
+ struct bio *bio = &bbio->bio;
struct btrfs_io_stripe *stripe = bio->bi_private;
if (bio->bi_status) {
@@ -409,6 +470,14 @@ static void btrfs_clone_write_end_io(struct bio *bio)
bio_put(bio);
}
+static void btrfs_clone_write_end_io(struct bio *bio)
+{
+ struct btrfs_bio *bbio = btrfs_bio(bio);
+
+ INIT_WORK(&bbio->end_io_work, clone_write_end_io_work);
+ queue_work(btrfs_end_io_wq(bbio->inode->root->fs_info, bio), &bbio->end_io_work);
+}
+
static void btrfs_submit_dev_bio(struct btrfs_device *dev, struct bio *bio)
{
if (!dev || !dev->bdev ||
@@ -455,6 +524,7 @@ static void btrfs_submit_dev_bio(struct btrfs_device *dev, struct bio *bio)
static void btrfs_submit_mirrored_bio(struct btrfs_io_context *bioc, int dev_nr)
{
struct bio *orig_bio = bioc->orig_bio, *bio;
+ struct btrfs_bio *orig_bbio = btrfs_bio(orig_bio);
ASSERT(bio_op(orig_bio) != REQ_OP_READ);
@@ -463,8 +533,11 @@ static void btrfs_submit_mirrored_bio(struct btrfs_io_context *bioc, int dev_nr)
bio = orig_bio;
bio->bi_end_io = btrfs_orig_write_end_io;
} else {
- bio = bio_alloc_clone(NULL, orig_bio, GFP_NOFS, &fs_bio_set);
+ /* We need to use endio_work to run end_io in task context. */
+ bio = bio_alloc_clone(NULL, orig_bio, GFP_NOFS, &btrfs_bioset);
bio_inc_remaining(orig_bio);
+ btrfs_bio_init(btrfs_bio(bio), orig_bbio->inode,
+ orig_bbio->file_offset, NULL, NULL);
bio->bi_end_io = btrfs_clone_write_end_io;
}
@@ -509,7 +582,11 @@ static int btrfs_bio_csum(struct btrfs_bio *bbio)
{
if (bbio->bio.bi_opf & REQ_META)
return btree_csum_one_bio(bbio);
- return btrfs_csum_one_bio(bbio);
+#ifdef CONFIG_BTRFS_EXPERIMENTAL
+ return btrfs_csum_one_bio(bbio, true);
+#else
+ return btrfs_csum_one_bio(bbio, false);
+#endif
}
/*
@@ -581,20 +658,25 @@ static void run_one_async_done(struct btrfs_work *work, bool do_free)
static bool should_async_write(struct btrfs_bio *bbio)
{
+ struct btrfs_fs_info *fs_info = bbio->inode->root->fs_info;
bool auto_csum_mode = true;
#ifdef CONFIG_BTRFS_EXPERIMENTAL
- struct btrfs_fs_devices *fs_devices = bbio->fs_info->fs_devices;
+ struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
enum btrfs_offload_csum_mode csum_mode = READ_ONCE(fs_devices->offload_csum_mode);
- if (csum_mode == BTRFS_OFFLOAD_CSUM_FORCE_OFF)
- return false;
-
- auto_csum_mode = (csum_mode == BTRFS_OFFLOAD_CSUM_AUTO);
+ if (csum_mode == BTRFS_OFFLOAD_CSUM_FORCE_ON)
+ return true;
+ /*
+ * Write bios will calculate checksum and submit bio at the same time.
+ * Unless explicitly required don't offload serial csum calculate and bio
+ * submit into a workqueue.
+ */
+ return false;
#endif
/* Submit synchronously if the checksum implementation is fast. */
- if (auto_csum_mode && test_bit(BTRFS_FS_CSUM_IMPL_FAST, &bbio->fs_info->flags))
+ if (auto_csum_mode && test_bit(BTRFS_FS_CSUM_IMPL_FAST, &fs_info->flags))
return false;
/*
@@ -605,7 +687,7 @@ static bool should_async_write(struct btrfs_bio *bbio)
return false;
/* Zoned devices require I/O to be submitted in order. */
- if ((bbio->bio.bi_opf & REQ_META) && btrfs_is_zoned(bbio->fs_info))
+ if ((bbio->bio.bi_opf & REQ_META) && btrfs_is_zoned(fs_info))
return false;
return true;
@@ -620,7 +702,7 @@ static bool btrfs_wq_submit_bio(struct btrfs_bio *bbio,
struct btrfs_io_context *bioc,
struct btrfs_io_stripe *smap, int mirror_num)
{
- struct btrfs_fs_info *fs_info = bbio->fs_info;
+ struct btrfs_fs_info *fs_info = bbio->inode->root->fs_info;
struct async_submit_bio *async;
async = kmalloc(sizeof(*async), GFP_NOFS);
@@ -639,11 +721,12 @@ static bool btrfs_wq_submit_bio(struct btrfs_bio *bbio,
static u64 btrfs_append_map_length(struct btrfs_bio *bbio, u64 map_length)
{
+ struct btrfs_fs_info *fs_info = bbio->inode->root->fs_info;
unsigned int nr_segs;
int sector_offset;
- map_length = min(map_length, bbio->fs_info->max_zone_append_size);
- sector_offset = bio_split_rw_at(&bbio->bio, &bbio->fs_info->limits,
+ map_length = min(map_length, fs_info->max_zone_append_size);
+ sector_offset = bio_split_rw_at(&bbio->bio, &fs_info->limits,
&nr_segs, map_length);
if (sector_offset) {
/*
@@ -651,7 +734,7 @@ static u64 btrfs_append_map_length(struct btrfs_bio *bbio, u64 map_length)
* sectorsize and thus cause unaligned I/Os. Fix that by
* always rounding down to the nearest boundary.
*/
- return ALIGN_DOWN(sector_offset << SECTOR_SHIFT, bbio->fs_info->sectorsize);
+ return ALIGN_DOWN(sector_offset << SECTOR_SHIFT, fs_info->sectorsize);
}
return map_length;
}
@@ -659,7 +742,7 @@ static u64 btrfs_append_map_length(struct btrfs_bio *bbio, u64 map_length)
static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num)
{
struct btrfs_inode *inode = bbio->inode;
- struct btrfs_fs_info *fs_info = bbio->fs_info;
+ struct btrfs_fs_info *fs_info = inode->root->fs_info;
struct bio *bio = &bbio->bio;
u64 logical = bio->bi_iter.bi_sector << SECTOR_SHIFT;
u64 length = bio->bi_iter.bi_size;
@@ -670,7 +753,7 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num)
blk_status_t status;
int ret;
- if (!bbio->inode || btrfs_is_data_reloc_root(inode->root))
+ if (bbio->is_scrub || btrfs_is_data_reloc_root(inode->root))
smap.rst_search_commit_root = true;
else
smap.rst_search_commit_root = false;
@@ -684,6 +767,14 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num)
goto end_bbio;
}
+ /*
+ * For fscrypt writes we will get the encrypted bio after we've remapped
+ * our bio to the physical disk location, so we need to save the
+ * original bytenr so we know what we're checksumming.
+ */
+ if (bio_op(bio) == REQ_OP_WRITE && is_data_bbio(bbio))
+ bbio->orig_logical = logical;
+
map_length = min(map_length, length);
if (use_append)
map_length = btrfs_append_map_length(bbio, map_length);
@@ -734,7 +825,7 @@ static bool btrfs_submit_chunk(struct btrfs_bio *bbio, int mirror_num)
* Csum items for reloc roots have already been cloned at this
* point, so they are handled as part of the no-checksum case.
*/
- if (inode && !(inode->flags & BTRFS_INODE_NODATASUM) &&
+ if (!(inode->flags & BTRFS_INODE_NODATASUM) &&
!test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state) &&
!btrfs_is_data_reloc_root(inode->root)) {
if (should_async_write(bbio) &&
@@ -782,25 +873,27 @@ end_bbio:
static void assert_bbio_alignment(struct btrfs_bio *bbio)
{
#ifdef CONFIG_BTRFS_ASSERT
- struct btrfs_fs_info *fs_info = bbio->fs_info;
+ struct btrfs_fs_info *fs_info = bbio->inode->root->fs_info;
struct bio_vec bvec;
struct bvec_iter iter;
const u32 blocksize = fs_info->sectorsize;
+ const u32 alignment = min(blocksize, PAGE_SIZE);
+ const u64 logical = bbio->bio.bi_iter.bi_sector << SECTOR_SHIFT;
+ const u32 length = bbio->bio.bi_iter.bi_size;
- /* Metadata has no extra bs > ps alignment requirement. */
- if (!is_data_bbio(bbio))
- return;
+ /* The logical and length should still be aligned to blocksize. */
+ ASSERT(IS_ALIGNED(logical, blocksize) && IS_ALIGNED(length, blocksize) &&
+ length != 0, "root=%llu inode=%llu logical=%llu length=%u",
+ btrfs_root_id(bbio->inode->root),
+ btrfs_ino(bbio->inode), logical, length);
bio_for_each_bvec(bvec, &bbio->bio, iter)
- ASSERT(IS_ALIGNED(bvec.bv_offset, blocksize) &&
- IS_ALIGNED(bvec.bv_len, blocksize),
+ ASSERT(IS_ALIGNED(bvec.bv_offset, alignment) &&
+ IS_ALIGNED(bvec.bv_len, alignment),
"root=%llu inode=%llu logical=%llu length=%u index=%u bv_offset=%u bv_len=%u",
btrfs_root_id(bbio->inode->root),
- btrfs_ino(bbio->inode),
- bbio->bio.bi_iter.bi_sector << SECTOR_SHIFT,
- bbio->bio.bi_iter.bi_size, iter.bi_idx,
- bvec.bv_offset,
- bvec.bv_len);
+ btrfs_ino(bbio->inode), logical, length, iter.bi_idx,
+ bvec.bv_offset, bvec.bv_len);
#endif
}
@@ -824,18 +917,36 @@ void btrfs_submit_bbio(struct btrfs_bio *bbio, int mirror_num)
*
* The I/O is issued synchronously to block the repair read completion from
* freeing the bio.
+ *
+ * @ino: Offending inode number
+ * @fileoff: File offset inside the inode
+ * @length: Length of the repair write
+ * @logical: Logical address of the range
+ * @paddrs: Physical address array of the content
+ * @step: Length of for each paddrs
+ * @mirror_num: Mirror number to write to. Must not be zero
*/
-int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 start,
- u64 length, u64 logical, phys_addr_t paddr, int mirror_num)
+int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 fileoff,
+ u32 length, u64 logical, const phys_addr_t paddrs[],
+ unsigned int step, int mirror_num)
{
+ const u32 nr_steps = DIV_ROUND_UP_POW2(length, step);
struct btrfs_io_stripe smap = { 0 };
- struct bio_vec bvec;
- struct bio bio;
+ struct bio *bio = NULL;
int ret = 0;
ASSERT(!(fs_info->sb->s_flags & SB_RDONLY));
BUG_ON(!mirror_num);
+ /* Basic alignment checks. */
+ ASSERT(IS_ALIGNED(logical, fs_info->sectorsize));
+ ASSERT(IS_ALIGNED(length, fs_info->sectorsize));
+ ASSERT(IS_ALIGNED(fileoff, fs_info->sectorsize));
+ /* Either it's a single data or metadata block. */
+ ASSERT(length <= BTRFS_MAX_BLOCKSIZE);
+ ASSERT(step <= length);
+ ASSERT(is_power_of_2(step));
+
if (btrfs_repair_one_zone(fs_info, logical))
return 0;
@@ -855,24 +966,27 @@ int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 start,
goto out_counter_dec;
}
- bio_init(&bio, smap.dev->bdev, &bvec, 1, REQ_OP_WRITE | REQ_SYNC);
- bio.bi_iter.bi_sector = smap.physical >> SECTOR_SHIFT;
- __bio_add_page(&bio, phys_to_page(paddr), length, offset_in_page(paddr));
- ret = submit_bio_wait(&bio);
+ bio = bio_alloc(smap.dev->bdev, nr_steps, REQ_OP_WRITE | REQ_SYNC, GFP_NOFS);
+ bio->bi_iter.bi_sector = smap.physical >> SECTOR_SHIFT;
+ for (int i = 0; i < nr_steps; i++) {
+ ret = bio_add_page(bio, phys_to_page(paddrs[i]), step, offset_in_page(paddrs[i]));
+ /* We should have allocated enough slots to contain all the different pages. */
+ ASSERT(ret == step);
+ }
+ ret = submit_bio_wait(bio);
+ bio_put(bio);
if (ret) {
/* try to remap that extent elsewhere? */
btrfs_dev_stat_inc_and_print(smap.dev, BTRFS_DEV_STAT_WRITE_ERRS);
- goto out_bio_uninit;
+ goto out_counter_dec;
}
btrfs_info_rl(fs_info,
"read error corrected: ino %llu off %llu (dev %s sector %llu)",
- ino, start, btrfs_dev_name(smap.dev),
+ ino, fileoff, btrfs_dev_name(smap.dev),
smap.physical >> SECTOR_SHIFT);
ret = 0;
-out_bio_uninit:
- bio_uninit(&bio);
out_counter_dec:
btrfs_bio_counter_dec(fs_info);
return ret;
@@ -885,16 +999,16 @@ out_counter_dec:
*/
void btrfs_submit_repair_write(struct btrfs_bio *bbio, int mirror_num, bool dev_replace)
{
- struct btrfs_fs_info *fs_info = bbio->fs_info;
+ struct btrfs_fs_info *fs_info = bbio->inode->root->fs_info;
u64 logical = bbio->bio.bi_iter.bi_sector << SECTOR_SHIFT;
u64 length = bbio->bio.bi_iter.bi_size;
struct btrfs_io_stripe smap = { 0 };
int ret;
- ASSERT(fs_info);
ASSERT(mirror_num > 0);
ASSERT(btrfs_op(&bbio->bio) == BTRFS_MAP_WRITE);
- ASSERT(!bbio->inode);
+ ASSERT(!is_data_inode(bbio->inode));
+ ASSERT(bbio->is_scrub);
btrfs_bio_counter_inc_blocked(fs_info);
ret = btrfs_map_repair_block(fs_info, &smap, logical, length, mirror_num);
diff --git a/fs/btrfs/bio.h b/fs/btrfs/bio.h
index 00883aea55d7..1be74209f0b8 100644
--- a/fs/btrfs/bio.h
+++ b/fs/btrfs/bio.h
@@ -18,13 +18,6 @@ struct btrfs_inode;
#define BTRFS_BIO_INLINE_CSUM_SIZE 64
-/*
- * Maximum number of sectors for a single bio to limit the size of the
- * checksum array. This matches the number of bio_vecs per bio and thus the
- * I/O size for buffered I/O.
- */
-#define BTRFS_MAX_BIO_SECTORS (256)
-
typedef void (*btrfs_bio_end_io_t)(struct btrfs_bio *bbio);
/*
@@ -34,7 +27,10 @@ typedef void (*btrfs_bio_end_io_t)(struct btrfs_bio *bbio);
struct btrfs_bio {
/*
* Inode and offset into it that this I/O operates on.
- * Only set for data I/O.
+ *
+ * If the inode is a data one, csum verification and read-repair
+ * will be done automatically.
+ * If the inode is a metadata one, everything is handled by the caller.
*/
struct btrfs_inode *inode;
u64 file_offset;
@@ -56,11 +52,16 @@ struct btrfs_bio {
* - pointer to the checksums for this bio
* - original physical address from the allocator
* (for zone append only)
+ * - original logical address, used for checksumming fscrypt bios
*/
struct {
struct btrfs_ordered_extent *ordered;
struct btrfs_ordered_sum *sums;
+ struct work_struct csum_work;
+ struct completion csum_done;
+ struct bvec_iter csum_saved_iter;
u64 orig_physical;
+ u64 orig_logical;
};
/* For metadata reads: parentness verification. */
@@ -76,14 +77,21 @@ struct btrfs_bio {
atomic_t pending_ios;
struct work_struct end_io_work;
- /* File system that this I/O operates on. */
- struct btrfs_fs_info *fs_info;
-
/* Save the first error status of split bio. */
blk_status_t status;
/* Use the commit root to look up csums (data read bio only). */
bool csum_search_commit_root;
+
+ /*
+ * Since scrub will reuse btree inode, we need this flag to distinguish
+ * scrub bios.
+ */
+ bool is_scrub;
+
+ /* Whether the csum generation for data write is async. */
+ bool async_csum;
+
/*
* This member must come last, bio_alloc_bioset will allocate enough
* bytes for entire btrfs_bio but relies on bio being last.
@@ -99,10 +107,10 @@ static inline struct btrfs_bio *btrfs_bio(struct bio *bio)
int __init btrfs_bioset_init(void);
void __cold btrfs_bioset_exit(void);
-void btrfs_bio_init(struct btrfs_bio *bbio, struct btrfs_fs_info *fs_info,
+void btrfs_bio_init(struct btrfs_bio *bbio, struct btrfs_inode *inode, u64 file_offset,
btrfs_bio_end_io_t end_io, void *private);
struct btrfs_bio *btrfs_bio_alloc(unsigned int nr_vecs, blk_opf_t opf,
- struct btrfs_fs_info *fs_info,
+ struct btrfs_inode *inode, u64 file_offset,
btrfs_bio_end_io_t end_io, void *private);
void btrfs_bio_end_io(struct btrfs_bio *bbio, blk_status_t status);
@@ -111,7 +119,8 @@ void btrfs_bio_end_io(struct btrfs_bio *bbio, blk_status_t status);
void btrfs_submit_bbio(struct btrfs_bio *bbio, int mirror_num);
void btrfs_submit_repair_write(struct btrfs_bio *bbio, int mirror_num, bool dev_replace);
-int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 start,
- u64 length, u64 logical, phys_addr_t paddr, int mirror_num);
+int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 fileoff,
+ u32 length, u64 logical, const phys_addr_t paddrs[],
+ unsigned int step, int mirror_num);
#endif
diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c
index 08cdda47509f..08b14449fabe 100644
--- a/fs/btrfs/block-group.c
+++ b/fs/btrfs/block-group.c
@@ -613,8 +613,8 @@ static int sample_block_group_extent_item(struct btrfs_caching_control *caching_
extent_root = btrfs_extent_root(fs_info, max_t(u64, block_group->start,
BTRFS_SUPER_INFO_OFFSET));
- path->skip_locking = 1;
- path->search_commit_root = 1;
+ path->skip_locking = true;
+ path->search_commit_root = true;
path->reada = READA_FORWARD;
search_offset = index * div_u64(block_group->length, max_index);
@@ -744,8 +744,8 @@ static int load_extent_tree_free(struct btrfs_caching_control *caching_ctl)
* root to add free space. So we skip locking and search the commit
* root, since its read-only
*/
- path->skip_locking = 1;
- path->search_commit_root = 1;
+ path->skip_locking = true;
+ path->search_commit_root = true;
path->reada = READA_FORWARD;
key.objectid = last;
@@ -1065,7 +1065,7 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
struct btrfs_chunk_map *map)
{
struct btrfs_fs_info *fs_info = trans->fs_info;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_block_group *block_group;
struct btrfs_free_cluster *cluster;
struct inode *inode;
@@ -1305,7 +1305,6 @@ out:
btrfs_put_block_group(block_group);
if (remove_rsv)
btrfs_dec_delayed_refs_rsv_bg_updates(fs_info);
- btrfs_free_path(path);
return ret;
}
@@ -1403,8 +1402,7 @@ static int inc_block_group_ro(struct btrfs_block_group *cache, bool force)
* BTRFS_RESERVE_NO_FLUSH to give ourselves the most amount of
* leeway to allow us to mark this block group as read only.
*/
- if (btrfs_can_overcommit(cache->fs_info, sinfo, num_bytes,
- BTRFS_RESERVE_NO_FLUSH))
+ if (btrfs_can_overcommit(sinfo, num_bytes, BTRFS_RESERVE_NO_FLUSH))
ret = 0;
}
@@ -1425,7 +1423,7 @@ out:
if (ret == -ENOSPC && btrfs_test_opt(cache->fs_info, ENOSPC_DEBUG)) {
btrfs_info(cache->fs_info,
"unable to make block group %llu ro", cache->start);
- btrfs_dump_space_info(cache->fs_info, cache->space_info, 0, false);
+ btrfs_dump_space_info(cache->space_info, 0, false);
}
return ret;
}
@@ -3068,7 +3066,7 @@ int btrfs_inc_block_group_ro(struct btrfs_block_group *cache,
* We have allocated a new chunk. We also need to activate that chunk to
* grant metadata tickets for zoned filesystem.
*/
- ret = btrfs_zoned_activate_one_bg(fs_info, space_info, true);
+ ret = btrfs_zoned_activate_one_bg(space_info, true);
if (ret < 0)
goto out;
@@ -3799,7 +3797,7 @@ int btrfs_update_block_group(struct btrfs_trans_handle *trans,
* reservation and return -EAGAIN, otherwise this function always succeeds.
*/
int btrfs_add_reserved_bytes(struct btrfs_block_group *cache,
- u64 ram_bytes, u64 num_bytes, int delalloc,
+ u64 ram_bytes, u64 num_bytes, bool delalloc,
bool force_wrong_size_class)
{
struct btrfs_space_info *space_info = cache->space_info;
@@ -3810,30 +3808,38 @@ int btrfs_add_reserved_bytes(struct btrfs_block_group *cache,
spin_lock(&cache->lock);
if (cache->ro) {
ret = -EAGAIN;
- goto out;
+ goto out_error;
}
if (btrfs_block_group_should_use_size_class(cache)) {
size_class = btrfs_calc_block_group_size_class(num_bytes);
ret = btrfs_use_block_group_size_class(cache, size_class, force_wrong_size_class);
if (ret)
- goto out;
+ goto out_error;
}
+
cache->reserved += num_bytes;
- space_info->bytes_reserved += num_bytes;
+ if (delalloc)
+ cache->delalloc_bytes += num_bytes;
+
trace_btrfs_space_reservation(cache->fs_info, "space_info",
space_info->flags, num_bytes, 1);
+ spin_unlock(&cache->lock);
+
+ space_info->bytes_reserved += num_bytes;
btrfs_space_info_update_bytes_may_use(space_info, -ram_bytes);
- if (delalloc)
- cache->delalloc_bytes += num_bytes;
/*
* Compression can use less space than we reserved, so wake tickets if
* that happens.
*/
if (num_bytes < ram_bytes)
- btrfs_try_granting_tickets(cache->fs_info, space_info);
-out:
+ btrfs_try_granting_tickets(space_info);
+ spin_unlock(&space_info->lock);
+
+ return 0;
+
+out_error:
spin_unlock(&cache->lock);
spin_unlock(&space_info->lock);
return ret;
@@ -3855,22 +3861,25 @@ void btrfs_free_reserved_bytes(struct btrfs_block_group *cache, u64 num_bytes,
bool is_delalloc)
{
struct btrfs_space_info *space_info = cache->space_info;
+ bool bg_ro;
spin_lock(&space_info->lock);
spin_lock(&cache->lock);
- if (cache->ro)
+ bg_ro = cache->ro;
+ cache->reserved -= num_bytes;
+ if (is_delalloc)
+ cache->delalloc_bytes -= num_bytes;
+ spin_unlock(&cache->lock);
+
+ if (bg_ro)
space_info->bytes_readonly += num_bytes;
else if (btrfs_is_zoned(cache->fs_info))
space_info->bytes_zone_unusable += num_bytes;
- cache->reserved -= num_bytes;
+
space_info->bytes_reserved -= num_bytes;
space_info->max_extent_size = 0;
- if (is_delalloc)
- cache->delalloc_bytes -= num_bytes;
- spin_unlock(&cache->lock);
-
- btrfs_try_granting_tickets(cache->fs_info, space_info);
+ btrfs_try_granting_tickets(space_info);
spin_unlock(&space_info->lock);
}
@@ -4188,11 +4197,11 @@ int btrfs_chunk_alloc(struct btrfs_trans_handle *trans,
should_alloc = should_alloc_chunk(fs_info, space_info, force);
if (space_info->full) {
/* No more free physical space */
+ spin_unlock(&space_info->lock);
if (should_alloc)
ret = -ENOSPC;
else
ret = 0;
- spin_unlock(&space_info->lock);
return ret;
} else if (!should_alloc) {
spin_unlock(&space_info->lock);
@@ -4204,16 +4213,16 @@ int btrfs_chunk_alloc(struct btrfs_trans_handle *trans,
* recheck if we should continue with our allocation
* attempt.
*/
+ spin_unlock(&space_info->lock);
wait_for_alloc = true;
force = CHUNK_ALLOC_NO_FORCE;
- spin_unlock(&space_info->lock);
mutex_lock(&fs_info->chunk_mutex);
mutex_unlock(&fs_info->chunk_mutex);
} else {
/* Proceed with allocation */
- space_info->chunk_alloc = 1;
- wait_for_alloc = false;
+ space_info->chunk_alloc = true;
spin_unlock(&space_info->lock);
+ wait_for_alloc = false;
}
cond_resched();
@@ -4260,7 +4269,7 @@ int btrfs_chunk_alloc(struct btrfs_trans_handle *trans,
spin_lock(&space_info->lock);
if (ret < 0) {
if (ret == -ENOSPC)
- space_info->full = 1;
+ space_info->full = true;
else
goto out;
} else {
@@ -4270,7 +4279,7 @@ int btrfs_chunk_alloc(struct btrfs_trans_handle *trans,
space_info->force_alloc = CHUNK_ALLOC_NO_FORCE;
out:
- space_info->chunk_alloc = 0;
+ space_info->chunk_alloc = false;
spin_unlock(&space_info->lock);
mutex_unlock(&fs_info->chunk_mutex);
@@ -4311,7 +4320,7 @@ static void reserve_chunk_space(struct btrfs_trans_handle *trans,
if (left < bytes && btrfs_test_opt(fs_info, ENOSPC_DEBUG)) {
btrfs_info(fs_info, "left=%llu, need=%llu, flags=%llu",
left, bytes, type);
- btrfs_dump_space_info(fs_info, info, 0, false);
+ btrfs_dump_space_info(info, 0, false);
}
if (left < bytes) {
@@ -4336,7 +4345,7 @@ static void reserve_chunk_space(struct btrfs_trans_handle *trans,
* We have a new chunk. We also need to activate it for
* zoned filesystem.
*/
- ret = btrfs_zoned_activate_one_bg(fs_info, info, true);
+ ret = btrfs_zoned_activate_one_bg(info, true);
if (ret < 0)
return;
@@ -4456,7 +4465,7 @@ static void check_removing_space_info(struct btrfs_space_info *space_info)
* indicates a real bug if this happens.
*/
if (WARN_ON(space_info->bytes_pinned > 0 || space_info->bytes_may_use > 0))
- btrfs_dump_space_info(info, space_info, 0, false);
+ btrfs_dump_space_info(space_info, 0, false);
/*
* If there was a failure to cleanup a log tree, very likely due to an
@@ -4467,7 +4476,7 @@ static void check_removing_space_info(struct btrfs_space_info *space_info)
if (!(space_info->flags & BTRFS_BLOCK_GROUP_METADATA) ||
!BTRFS_FS_LOG_CLEANUP_ERROR(info)) {
if (WARN_ON(space_info->bytes_reserved > 0))
- btrfs_dump_space_info(info, space_info, 0, false);
+ btrfs_dump_space_info(space_info, 0, false);
}
WARN_ON(space_info->reclaim_size > 0);
diff --git a/fs/btrfs/block-group.h b/fs/btrfs/block-group.h
index 9172104a5889..5f933455118c 100644
--- a/fs/btrfs/block-group.h
+++ b/fs/btrfs/block-group.h
@@ -345,7 +345,7 @@ int btrfs_setup_space_cache(struct btrfs_trans_handle *trans);
int btrfs_update_block_group(struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes, bool alloc);
int btrfs_add_reserved_bytes(struct btrfs_block_group *cache,
- u64 ram_bytes, u64 num_bytes, int delalloc,
+ u64 ram_bytes, u64 num_bytes, bool delalloc,
bool force_wrong_size_class);
void btrfs_free_reserved_bytes(struct btrfs_block_group *cache, u64 num_bytes,
bool is_delalloc);
diff --git a/fs/btrfs/block-rsv.c b/fs/btrfs/block-rsv.c
index 5ad6de738aee..96cf7a162987 100644
--- a/fs/btrfs/block-rsv.c
+++ b/fs/btrfs/block-rsv.c
@@ -218,8 +218,7 @@ int btrfs_block_rsv_add(struct btrfs_fs_info *fs_info,
if (num_bytes == 0)
return 0;
- ret = btrfs_reserve_metadata_bytes(fs_info, block_rsv->space_info,
- num_bytes, flush);
+ ret = btrfs_reserve_metadata_bytes(block_rsv->space_info, num_bytes, flush);
if (!ret)
btrfs_block_rsv_add_bytes(block_rsv, num_bytes, true);
@@ -259,8 +258,7 @@ int btrfs_block_rsv_refill(struct btrfs_fs_info *fs_info,
if (!ret)
return 0;
- ret = btrfs_reserve_metadata_bytes(fs_info, block_rsv->space_info,
- num_bytes, flush);
+ ret = btrfs_reserve_metadata_bytes(block_rsv->space_info, num_bytes, flush);
if (!ret) {
btrfs_block_rsv_add_bytes(block_rsv, num_bytes, false);
return 0;
@@ -387,7 +385,7 @@ void btrfs_update_global_block_rsv(struct btrfs_fs_info *fs_info)
num_bytes = block_rsv->reserved - block_rsv->size;
btrfs_space_info_update_bytes_may_use(sinfo, -num_bytes);
block_rsv->reserved = block_rsv->size;
- btrfs_try_granting_tickets(fs_info, sinfo);
+ btrfs_try_granting_tickets(sinfo);
}
block_rsv->full = (block_rsv->reserved == block_rsv->size);
@@ -530,8 +528,8 @@ again:
block_rsv->type, ret);
}
try_reserve:
- ret = btrfs_reserve_metadata_bytes(fs_info, block_rsv->space_info,
- blocksize, BTRFS_RESERVE_NO_FLUSH);
+ ret = btrfs_reserve_metadata_bytes(block_rsv->space_info, blocksize,
+ BTRFS_RESERVE_NO_FLUSH);
if (!ret)
return block_rsv;
/*
@@ -552,7 +550,7 @@ try_reserve:
* one last time to force a reservation if there's enough actual space
* on disk to make the reservation.
*/
- ret = btrfs_reserve_metadata_bytes(fs_info, block_rsv->space_info, blocksize,
+ ret = btrfs_reserve_metadata_bytes(block_rsv->space_info, blocksize,
BTRFS_RESERVE_FLUSH_EMERGENCY);
if (!ret)
return block_rsv;
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index af373d50a901..73602ee8de3f 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -18,20 +18,20 @@
#include <linux/lockdep.h>
#include <uapi/linux/btrfs_tree.h>
#include <trace/events/btrfs.h>
+#include "ctree.h"
#include "block-rsv.h"
#include "extent_map.h"
-#include "extent_io.h"
#include "extent-io-tree.h"
-#include "ordered-data.h"
-#include "delayed-inode.h"
-struct extent_state;
struct posix_acl;
struct iov_iter;
struct writeback_control;
struct btrfs_root;
struct btrfs_fs_info;
struct btrfs_trans_handle;
+struct btrfs_bio;
+struct btrfs_file_extent;
+struct btrfs_delayed_node;
/*
* Since we search a directory based on f_pos (struct dir_context::pos) we have
@@ -543,16 +543,14 @@ static inline void btrfs_set_inode_mapping_order(struct btrfs_inode *inode)
#endif
}
-/* Array of bytes with variable length, hexadecimal format 0x1234 */
-#define CSUM_FMT "0x%*phN"
-#define CSUM_FMT_VALUE(size, bytes) size, bytes
-
-void btrfs_calculate_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr,
- u8 *dest);
+void btrfs_calculate_block_csum_folio(struct btrfs_fs_info *fs_info,
+ const phys_addr_t paddr, u8 *dest);
+void btrfs_calculate_block_csum_pages(struct btrfs_fs_info *fs_info,
+ const phys_addr_t paddrs[], u8 *dest);
int btrfs_check_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr, u8 *csum,
const u8 * const csum_expected);
bool btrfs_data_csum_ok(struct btrfs_bio *bbio, struct btrfs_device *dev,
- u32 bio_offset, phys_addr_t paddr);
+ u32 bio_offset, const phys_addr_t paddrs[]);
noinline int can_nocow_extent(struct btrfs_inode *inode, u64 offset, u64 *len,
struct btrfs_file_extent *file_extent,
bool nowait);
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index bacad18357b3..7dda6cc68379 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -67,9 +67,7 @@ static struct compressed_bio *alloc_compressed_bio(struct btrfs_inode *inode,
bbio = btrfs_bio(bio_alloc_bioset(NULL, BTRFS_MAX_COMPRESSED_PAGES, op,
GFP_NOFS, &btrfs_compressed_bioset));
- btrfs_bio_init(bbio, inode->root->fs_info, end_io, NULL);
- bbio->inode = inode;
- bbio->file_offset = start;
+ btrfs_bio_init(bbio, inode, start, end_io, NULL);
return to_compressed_bio(bbio);
}
@@ -194,15 +192,13 @@ static unsigned long btrfs_compr_pool_count(struct shrinker *sh, struct shrink_c
static unsigned long btrfs_compr_pool_scan(struct shrinker *sh, struct shrink_control *sc)
{
- struct list_head remove;
+ LIST_HEAD(remove);
struct list_head *tmp, *next;
int freed;
if (compr_pool.count == 0)
return SHRINK_STOP;
- INIT_LIST_HEAD(&remove);
-
/* For now, just simply drain the whole list. */
spin_lock(&compr_pool.lock);
list_splice_init(&compr_pool.list, &remove);
@@ -321,22 +317,6 @@ static noinline void end_compressed_writeback(const struct compressed_bio *cb)
/* the inode may be gone now */
}
-static void btrfs_finish_compressed_write_work(struct work_struct *work)
-{
- struct compressed_bio *cb =
- container_of(work, struct compressed_bio, write_end_work);
-
- btrfs_finish_ordered_extent(cb->bbio.ordered, NULL, cb->start, cb->len,
- cb->bbio.bio.bi_status == BLK_STS_OK);
-
- if (cb->writeback)
- end_compressed_writeback(cb);
- /* Note, our inode could be gone now */
-
- btrfs_free_compressed_folios(cb);
- bio_put(&cb->bbio.bio);
-}
-
/*
* Do the cleanup once all the compressed pages hit the disk. This will clear
* writeback on the file pages and free the compressed pages.
@@ -347,28 +327,33 @@ static void btrfs_finish_compressed_write_work(struct work_struct *work)
static void end_bbio_compressed_write(struct btrfs_bio *bbio)
{
struct compressed_bio *cb = to_compressed_bio(bbio);
- struct btrfs_fs_info *fs_info = bbio->inode->root->fs_info;
- queue_work(fs_info->compressed_write_workers, &cb->write_end_work);
+ btrfs_finish_ordered_extent(cb->bbio.ordered, NULL, cb->start, cb->len,
+ cb->bbio.bio.bi_status == BLK_STS_OK);
+
+ if (cb->writeback)
+ end_compressed_writeback(cb);
+ /* Note, our inode could be gone now. */
+ btrfs_free_compressed_folios(cb);
+ bio_put(&cb->bbio.bio);
}
static void btrfs_add_compressed_bio_folios(struct compressed_bio *cb)
{
- struct btrfs_fs_info *fs_info = cb->bbio.fs_info;
struct bio *bio = &cb->bbio.bio;
u32 offset = 0;
+ unsigned int findex = 0;
while (offset < cb->compressed_len) {
- struct folio *folio;
+ struct folio *folio = cb->compressed_folios[findex];
+ u32 len = min_t(u32, cb->compressed_len - offset, folio_size(folio));
int ret;
- u32 len = min_t(u32, cb->compressed_len - offset,
- btrfs_min_folio_size(fs_info));
- folio = cb->compressed_folios[offset >> (PAGE_SHIFT + fs_info->block_min_order)];
/* Maximum compressed extent is smaller than bio size limit. */
ret = bio_add_folio(bio, folio, len, 0);
ASSERT(ret);
offset += len;
+ findex++;
}
}
@@ -402,7 +387,6 @@ void btrfs_submit_compressed_write(struct btrfs_ordered_extent *ordered,
cb->compressed_folios = compressed_folios;
cb->compressed_len = ordered->disk_num_bytes;
cb->writeback = writeback;
- INIT_WORK(&cb->write_end_work, btrfs_finish_compressed_write_work);
cb->nr_folios = nr_folios;
cb->bbio.bio.bi_iter.bi_sector = ordered->disk_bytenr >> SECTOR_SHIFT;
cb->bbio.ordered = ordered;
@@ -1100,7 +1084,8 @@ static int btrfs_decompress_bio(struct compressed_bio *cb)
/*
* a less complex decompression routine. Our compressed data fits in a
* single page, and we want to read a single page out of it.
- * start_byte tells us the offset into the compressed data we're interested in
+ * dest_pgoff tells us the offset into the destination folio where we write the
+ * decompressed data.
*/
int btrfs_decompress(int type, const u8 *data_in, struct folio *dest_folio,
unsigned long dest_pgoff, size_t srclen, size_t destlen)
diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h
index aee1fd21cdd6..e0228017e861 100644
--- a/fs/btrfs/compression.h
+++ b/fs/btrfs/compression.h
@@ -14,14 +14,12 @@
#include <linux/pagemap.h>
#include "bio.h"
#include "fs.h"
-#include "messages.h"
+#include "btrfs_inode.h"
struct address_space;
-struct page;
struct inode;
struct btrfs_inode;
struct btrfs_ordered_extent;
-struct btrfs_bio;
/*
* We want to make sure that amount of RAM required to uncompress an extent is
@@ -65,11 +63,8 @@ struct compressed_bio {
/* Whether this is a write for writeback. */
bool writeback;
- union {
- /* For reads, this is the bio we are copying the data into */
- struct btrfs_bio *orig_bbio;
- struct work_struct write_end_work;
- };
+ /* For reads, this is the bio we are copying the data into. */
+ struct btrfs_bio *orig_bbio;
/* Must be last. */
struct btrfs_bio bbio;
@@ -77,7 +72,7 @@ struct compressed_bio {
static inline struct btrfs_fs_info *cb_to_fs_info(const struct compressed_bio *cb)
{
- return cb->bbio.fs_info;
+ return cb->bbio.inode->root->fs_info;
}
/* @range_end must be exclusive. */
@@ -100,7 +95,7 @@ int btrfs_compress_folios(unsigned int type, int level, struct btrfs_inode *inod
u64 start, struct folio **folios, unsigned long *out_folios,
unsigned long *total_in, unsigned long *total_out);
int btrfs_decompress(int type, const u8 *data_in, struct folio *dest_folio,
- unsigned long start_byte, size_t srclen, size_t destlen);
+ unsigned long dest_pgoff, size_t srclen, size_t destlen);
int btrfs_decompress_buf2page(const char *buf, u32 buf_len,
struct compressed_bio *cb, u32 decompressed);
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 561658aca018..a48b4befbee7 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -862,6 +862,75 @@ struct extent_buffer *btrfs_read_node_slot(struct extent_buffer *parent,
}
/*
+ * Promote a child node to become the new tree root.
+ *
+ * @trans: Transaction handle
+ * @root: Tree root structure to update
+ * @path: Path holding nodes and locks
+ * @level: Level of the parent (old root)
+ * @parent: The parent (old root) with exactly one item
+ *
+ * This helper is called during rebalancing when the root node contains only
+ * a single item (nritems == 1). We can reduce the tree height by promoting
+ * that child to become the new root and freeing the old root node. The path
+ * locks and references are updated accordingly.
+ *
+ * Return: 0 on success, negative errno on failure. The transaction is aborted
+ * on critical errors.
+ */
+static int promote_child_to_root(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, struct btrfs_path *path,
+ int level, struct extent_buffer *parent)
+{
+ struct extent_buffer *child;
+ int ret;
+
+ ASSERT(btrfs_header_nritems(parent) == 1);
+
+ child = btrfs_read_node_slot(parent, 0);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ btrfs_tree_lock(child);
+ ret = btrfs_cow_block(trans, root, child, parent, 0, &child, BTRFS_NESTING_COW);
+ if (ret) {
+ btrfs_tree_unlock(child);
+ free_extent_buffer(child);
+ return ret;
+ }
+
+ ret = btrfs_tree_mod_log_insert_root(root->node, child, true);
+ if (unlikely(ret < 0)) {
+ btrfs_tree_unlock(child);
+ free_extent_buffer(child);
+ btrfs_abort_transaction(trans, ret);
+ return ret;
+ }
+ rcu_assign_pointer(root->node, child);
+
+ add_root_to_dirty_list(root);
+ btrfs_tree_unlock(child);
+
+ path->locks[level] = 0;
+ path->nodes[level] = NULL;
+ btrfs_clear_buffer_dirty(trans, parent);
+ btrfs_tree_unlock(parent);
+ /* Once for the path. */
+ free_extent_buffer(parent);
+
+ root_sub_used_bytes(root);
+ ret = btrfs_free_tree_block(trans, btrfs_root_id(root), parent, 0, 1);
+ /* Once for the root ptr. */
+ free_extent_buffer_stale(parent);
+ if (unlikely(ret < 0)) {
+ btrfs_abort_transaction(trans, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
* node level balancing, used to make sure nodes are in proper order for
* item deletion. We balance from the top down, so we have to make sure
* that a deletion won't leave an node completely empty later on.
@@ -900,55 +969,10 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
* by promoting the node below to a root
*/
if (!parent) {
- struct extent_buffer *child;
-
if (btrfs_header_nritems(mid) != 1)
return 0;
- /* promote the child to a root */
- child = btrfs_read_node_slot(mid, 0);
- if (IS_ERR(child)) {
- ret = PTR_ERR(child);
- goto out;
- }
-
- btrfs_tree_lock(child);
- ret = btrfs_cow_block(trans, root, child, mid, 0, &child,
- BTRFS_NESTING_COW);
- if (ret) {
- btrfs_tree_unlock(child);
- free_extent_buffer(child);
- goto out;
- }
-
- ret = btrfs_tree_mod_log_insert_root(root->node, child, true);
- if (unlikely(ret < 0)) {
- btrfs_tree_unlock(child);
- free_extent_buffer(child);
- btrfs_abort_transaction(trans, ret);
- goto out;
- }
- rcu_assign_pointer(root->node, child);
-
- add_root_to_dirty_list(root);
- btrfs_tree_unlock(child);
-
- path->locks[level] = 0;
- path->nodes[level] = NULL;
- btrfs_clear_buffer_dirty(trans, mid);
- btrfs_tree_unlock(mid);
- /* once for the path */
- free_extent_buffer(mid);
-
- root_sub_used_bytes(root);
- ret = btrfs_free_tree_block(trans, btrfs_root_id(root), mid, 0, 1);
- /* once for the root ptr */
- free_extent_buffer_stale(mid);
- if (unlikely(ret < 0)) {
- btrfs_abort_transaction(trans, ret);
- goto out;
- }
- return 0;
+ return promote_child_to_root(trans, root, path, level, mid);
}
if (btrfs_header_nritems(mid) >
BTRFS_NODEPTRS_PER_BLOCK(fs_info) / 4)
@@ -1101,11 +1125,12 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
/* update the path */
if (left) {
if (btrfs_header_nritems(left) > orig_slot) {
- refcount_inc(&left->refs);
/* left was locked after cow */
path->nodes[level] = left;
path->slots[level + 1] -= 1;
path->slots[level] = orig_slot;
+ /* Left is now owned by path. */
+ left = NULL;
if (mid) {
btrfs_tree_unlock(mid);
free_extent_buffer(mid);
@@ -1125,8 +1150,7 @@ out:
free_extent_buffer(right);
}
if (left) {
- if (path->nodes[level] != left)
- btrfs_tree_unlock(left);
+ btrfs_tree_unlock(left);
free_extent_buffer(left);
}
return ret;
@@ -1435,8 +1459,8 @@ static noinline void unlock_up(struct btrfs_path *path, int level,
}
if (i >= lowest_unlock && i > skip_level) {
- check_skip = false;
btrfs_tree_unlock_rw(path->nodes[i], path->locks[i]);
+ check_skip = false;
path->locks[i] = 0;
if (write_lock_level &&
i > min_write_lock_level &&
@@ -1709,9 +1733,9 @@ static struct extent_buffer *btrfs_search_slot_get_root(struct btrfs_root *root,
level = btrfs_header_level(b);
/*
* Ensure that all callers have set skip_locking when
- * p->search_commit_root = 1.
+ * p->search_commit_root is true.
*/
- ASSERT(p->skip_locking == 1);
+ ASSERT(p->skip_locking);
goto out;
}
@@ -2599,12 +2623,11 @@ void btrfs_set_item_key_safe(struct btrfs_trans_handle *trans,
if (unlikely(btrfs_comp_keys(&disk_key, new_key) >= 0)) {
btrfs_print_leaf(eb);
btrfs_crit(fs_info,
- "slot %u key (%llu %u %llu) new key (%llu %u %llu)",
+ "slot %u key " BTRFS_KEY_FMT " new key " BTRFS_KEY_FMT,
slot, btrfs_disk_key_objectid(&disk_key),
btrfs_disk_key_type(&disk_key),
btrfs_disk_key_offset(&disk_key),
- new_key->objectid, new_key->type,
- new_key->offset);
+ BTRFS_KEY_FMT_VALUE(new_key));
BUG();
}
}
@@ -2613,12 +2636,11 @@ void btrfs_set_item_key_safe(struct btrfs_trans_handle *trans,
if (unlikely(btrfs_comp_keys(&disk_key, new_key) <= 0)) {
btrfs_print_leaf(eb);
btrfs_crit(fs_info,
- "slot %u key (%llu %u %llu) new key (%llu %u %llu)",
+ "slot %u key " BTRFS_KEY_FMT " new key " BTRFS_KEY_FMT,
slot, btrfs_disk_key_objectid(&disk_key),
btrfs_disk_key_type(&disk_key),
btrfs_disk_key_offset(&disk_key),
- new_key->objectid, new_key->type,
- new_key->offset);
+ BTRFS_KEY_FMT_VALUE(new_key));
BUG();
}
}
@@ -2677,10 +2699,9 @@ static bool check_sibling_keys(const struct extent_buffer *left,
btrfs_crit(left->fs_info, "right extent buffer:");
btrfs_print_tree(right, false);
btrfs_crit(left->fs_info,
-"bad key order, sibling blocks, left last (%llu %u %llu) right first (%llu %u %llu)",
- left_last.objectid, left_last.type,
- left_last.offset, right_first.objectid,
- right_first.type, right_first.offset);
+"bad key order, sibling blocks, left last " BTRFS_KEY_FMT " right first " BTRFS_KEY_FMT,
+ BTRFS_KEY_FMT_VALUE(&left_last),
+ BTRFS_KEY_FMT_VALUE(&right_first));
return true;
}
return false;
@@ -3217,10 +3238,8 @@ static noinline int __push_leaf_right(struct btrfs_trans_handle *trans,
/* then fixup the leaf pointer in the path */
if (path->slots[0] >= left_nritems) {
path->slots[0] -= left_nritems;
- if (btrfs_header_nritems(path->nodes[0]) == 0)
- btrfs_clear_buffer_dirty(trans, path->nodes[0]);
- btrfs_tree_unlock(path->nodes[0]);
- free_extent_buffer(path->nodes[0]);
+ btrfs_tree_unlock(left);
+ free_extent_buffer(left);
path->nodes[0] = right;
path->slots[1] += 1;
} else {
@@ -3398,9 +3417,13 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans,
btrfs_set_header_nritems(left, old_left_nritems + push_items);
/* fixup right node */
- if (push_items > right_nritems)
- WARN(1, KERN_CRIT "push items %d nr %u\n", push_items,
- right_nritems);
+ if (unlikely(push_items > right_nritems)) {
+ ret = -EUCLEAN;
+ btrfs_abort_transaction(trans, ret);
+ btrfs_crit(fs_info, "push items (%d) > right leaf items (%u)",
+ push_items, right_nritems);
+ goto out;
+ }
if (push_items < right_nritems) {
push_space = btrfs_item_offset(right, push_items - 1) -
@@ -3433,8 +3456,8 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans,
/* then fixup the leaf pointer in the path */
if (path->slots[0] < push_items) {
path->slots[0] += old_left_nritems;
- btrfs_tree_unlock(path->nodes[0]);
- free_extent_buffer(path->nodes[0]);
+ btrfs_tree_unlock(right);
+ free_extent_buffer(right);
path->nodes[0] = left;
path->slots[1] -= 1;
} else {
@@ -3861,10 +3884,10 @@ static noinline int setup_leaf_for_split(struct btrfs_trans_handle *trans,
}
btrfs_release_path(path);
- path->keep_locks = 1;
- path->search_for_split = 1;
+ path->keep_locks = true;
+ path->search_for_split = true;
ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
- path->search_for_split = 0;
+ path->search_for_split = false;
if (ret > 0)
ret = -EAGAIN;
if (ret < 0)
@@ -3891,11 +3914,11 @@ static noinline int setup_leaf_for_split(struct btrfs_trans_handle *trans,
if (ret)
goto err;
- path->keep_locks = 0;
+ path->keep_locks = false;
btrfs_unlock_up_safe(path, 1);
return 0;
err:
- path->keep_locks = 0;
+ path->keep_locks = false;
return ret;
}
@@ -4109,7 +4132,7 @@ void btrfs_extend_item(struct btrfs_trans_handle *trans,
nritems = btrfs_header_nritems(leaf);
data_end = leaf_data_end(leaf);
- if (btrfs_leaf_free_space(leaf) < data_size) {
+ if (unlikely(btrfs_leaf_free_space(leaf) < data_size)) {
btrfs_print_leaf(leaf);
BUG();
}
@@ -4139,7 +4162,6 @@ void btrfs_extend_item(struct btrfs_trans_handle *trans,
memmove_leaf_data(leaf, data_end - data_size, data_end,
old_data - data_end);
- data_end = old_data;
old_size = btrfs_item_size(leaf, slot);
btrfs_set_item_size(leaf, slot, old_size + data_size);
btrfs_mark_buffer_dirty(trans, leaf);
@@ -4498,9 +4520,7 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
/* delete the leaf if we've emptied it */
if (nritems == 0) {
- if (leaf == root->node) {
- btrfs_set_header_level(leaf, 0);
- } else {
+ if (leaf != root->node) {
btrfs_clear_buffer_dirty(trans, leaf);
ret = btrfs_del_leaf(trans, root, path, leaf);
if (ret < 0)
@@ -4566,10 +4586,9 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root,
if (btrfs_header_nritems(leaf) == 0) {
path->slots[1] = slot;
ret = btrfs_del_leaf(trans, root, path, leaf);
+ free_extent_buffer(leaf);
if (ret < 0)
return ret;
- free_extent_buffer(leaf);
- ret = 0;
} else {
/* if we're still in the path, make sure
* we're dirty. Otherwise, one of the
@@ -4613,11 +4632,11 @@ int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key,
u32 nritems;
int level;
int ret = 1;
- int keep_locks = path->keep_locks;
+ const bool keep_locks = path->keep_locks;
ASSERT(!path->nowait);
ASSERT(path->lowest_level == 0);
- path->keep_locks = 1;
+ path->keep_locks = true;
again:
cur = btrfs_read_lock_root_node(root);
level = btrfs_header_level(cur);
@@ -4707,7 +4726,7 @@ out:
* 0 is returned if another key is found, < 0 if there are any errors
* and 1 is returned if there are no higher keys in the tree
*
- * path->keep_locks should be set to 1 on the search made before
+ * path->keep_locks should be set to true on the search made before
* calling this function.
*/
int btrfs_find_next_key(struct btrfs_root *root, struct btrfs_path *path,
@@ -4806,13 +4825,13 @@ again:
next = NULL;
btrfs_release_path(path);
- path->keep_locks = 1;
+ path->keep_locks = true;
if (time_seq) {
ret = btrfs_search_old_slot(root, &key, path, time_seq);
} else {
if (path->need_commit_sem) {
- path->need_commit_sem = 0;
+ path->need_commit_sem = false;
need_commit_sem = true;
if (path->nowait) {
if (!down_read_trylock(&fs_info->commit_root_sem)) {
@@ -4825,41 +4844,30 @@ again:
}
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
}
- path->keep_locks = 0;
+ path->keep_locks = false;
if (ret < 0)
goto done;
nritems = btrfs_header_nritems(path->nodes[0]);
/*
- * by releasing the path above we dropped all our locks. A balance
- * could have added more items next to the key that used to be
- * at the very end of the block. So, check again here and
- * advance the path if there are now more items available.
- */
- if (nritems > 0 && path->slots[0] < nritems - 1) {
- if (ret == 0)
- path->slots[0]++;
- ret = 0;
- goto done;
- }
- /*
- * So the above check misses one case:
- * - after releasing the path above, someone has removed the item that
- * used to be at the very end of the block, and balance between leafs
- * gets another one with bigger key.offset to replace it.
+ * By releasing the path above we dropped all our locks. A balance
+ * could have happened and
*
- * This one should be returned as well, or we can get leaf corruption
- * later(esp. in __btrfs_drop_extents()).
+ * 1. added more items after the previous last item
+ * 2. deleted the previous last item
*
- * And a bit more explanation about this check,
- * with ret > 0, the key isn't found, the path points to the slot
- * where it should be inserted, so the path->slots[0] item must be the
- * bigger one.
+ * So, check again here and advance the path if there are now more
+ * items available.
*/
- if (nritems > 0 && ret > 0 && path->slots[0] == nritems - 1) {
- ret = 0;
- goto done;
+ if (nritems > 0 && path->slots[0] <= nritems - 1) {
+ if (ret == 0 && path->slots[0] != nritems - 1) {
+ path->slots[0]++;
+ goto done;
+ } else if (ret > 0) {
+ ret = 0;
+ goto done;
+ }
}
while (level < BTRFS_MAX_LEVEL) {
@@ -4964,7 +4972,7 @@ done:
if (need_commit_sem) {
int ret2;
- path->need_commit_sem = 1;
+ path->need_commit_sem = true;
ret2 = finish_need_commit_sem_search(path);
up_read(&fs_info->commit_root_sem);
if (ret2)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index fe70b593c7cd..692370fc07b2 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -17,9 +17,7 @@
#include <linux/refcount.h>
#include <uapi/linux/btrfs_tree.h>
#include "locking.h"
-#include "fs.h"
#include "accessors.h"
-#include "extent-io-tree.h"
struct extent_buffer;
struct btrfs_block_rsv;
@@ -67,21 +65,21 @@ struct btrfs_path {
* set by btrfs_split_item, tells search_slot to keep all locks
* and to force calls to keep space in the nodes
*/
- unsigned int search_for_split:1;
+ bool search_for_split:1;
/* Keep some upper locks as we walk down. */
- unsigned int keep_locks:1;
- unsigned int skip_locking:1;
- unsigned int search_commit_root:1;
- unsigned int need_commit_sem:1;
- unsigned int skip_release_on_error:1;
+ bool keep_locks:1;
+ bool skip_locking:1;
+ bool search_commit_root:1;
+ bool need_commit_sem:1;
+ bool skip_release_on_error:1;
/*
* Indicate that new item (btrfs_search_slot) is extending already
* existing item and ins_len contains only the data size and not item
* header (ie. sizeof(struct btrfs_item) is not included).
*/
- unsigned int search_for_extension:1;
+ bool search_for_extension:1;
/* Stop search if any locks need to be taken (for read) */
- unsigned int nowait:1;
+ bool nowait:1;
};
#define BTRFS_PATH_AUTO_FREE(path_name) \
diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c
index a7f20f048398..b81e224d4a27 100644
--- a/fs/btrfs/defrag.c
+++ b/fs/btrfs/defrag.c
@@ -15,6 +15,7 @@
#include "defrag.h"
#include "file-item.h"
#include "super.h"
+#include "compression.h"
static struct kmem_cache *btrfs_inode_defrag_cachep;
@@ -470,7 +471,7 @@ static int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
memcpy(&key, &root->defrag_progress, sizeof(key));
}
- path->keep_locks = 1;
+ path->keep_locks = true;
ret = btrfs_search_forward(root, &key, path, BTRFS_OLDEST_GENERATION);
if (ret < 0)
@@ -513,7 +514,7 @@ static int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
/*
* Now that we reallocated the node we can find the next key. Note that
* btrfs_find_next_key() can release our path and do another search
- * without COWing, this is because even with path->keep_locks = 1,
+ * without COWing, this is because even with path->keep_locks == true,
* btrfs_search_slot() / ctree.c:unlock_up() does not keeps a lock on a
* node when path->slots[node_level - 1] does not point to the last
* item or a slot beyond the last item (ctree.c:unlock_up()). Therefore
diff --git a/fs/btrfs/delalloc-space.c b/fs/btrfs/delalloc-space.c
index 288e1776c02d..0970799d0aa4 100644
--- a/fs/btrfs/delalloc-space.c
+++ b/fs/btrfs/delalloc-space.c
@@ -358,8 +358,8 @@ int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes,
noflush);
if (ret)
return ret;
- ret = btrfs_reserve_metadata_bytes(fs_info, block_rsv->space_info,
- meta_reserve, flush);
+ ret = btrfs_reserve_metadata_bytes(block_rsv->space_info, meta_reserve,
+ flush);
if (ret) {
btrfs_qgroup_free_meta_prealloc(root, qgroup_reserve);
return ret;
diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c
index 3df7b9d7fbe8..ce6e9f8812e0 100644
--- a/fs/btrfs/delayed-inode.c
+++ b/fs/btrfs/delayed-inode.c
@@ -668,7 +668,7 @@ static int btrfs_insert_delayed_item(struct btrfs_trans_handle *trans,
struct btrfs_key first_key;
const u32 first_data_size = first_item->data_len;
int total_size;
- char *ins_data = NULL;
+ char AUTO_KFREE(ins_data);
int ret;
bool continuous_keys_only = false;
@@ -740,10 +740,8 @@ static int btrfs_insert_delayed_item(struct btrfs_trans_handle *trans,
ins_data = kmalloc_array(batch.nr,
sizeof(u32) + sizeof(struct btrfs_key), GFP_NOFS);
- if (!ins_data) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!ins_data)
+ return -ENOMEM;
ins_sizes = (u32 *)ins_data;
ins_keys = (struct btrfs_key *)(ins_data + batch.nr * sizeof(u32));
batch.keys = ins_keys;
@@ -759,7 +757,7 @@ static int btrfs_insert_delayed_item(struct btrfs_trans_handle *trans,
ret = btrfs_insert_empty_items(trans, root, path, &batch);
if (ret)
- goto out;
+ return ret;
list_for_each_entry(curr, &item_list, tree_list) {
char *data_ptr;
@@ -814,9 +812,8 @@ static int btrfs_insert_delayed_item(struct btrfs_trans_handle *trans,
list_del(&curr->tree_list);
btrfs_release_delayed_item(curr);
}
-out:
- kfree(ins_data);
- return ret;
+
+ return 0;
}
static int btrfs_insert_delayed_items(struct btrfs_trans_handle *trans,
@@ -2011,13 +2008,10 @@ int btrfs_delayed_delete_inode_ref(struct btrfs_inode *inode)
* It is very rare.
*/
mutex_lock(&delayed_node->mutex);
- if (test_bit(BTRFS_DELAYED_NODE_DEL_IREF, &delayed_node->flags))
- goto release_node;
-
- set_bit(BTRFS_DELAYED_NODE_DEL_IREF, &delayed_node->flags);
- delayed_node->count++;
- atomic_inc(&fs_info->delayed_root->items);
-release_node:
+ if (!test_and_set_bit(BTRFS_DELAYED_NODE_DEL_IREF, &delayed_node->flags)) {
+ delayed_node->count++;
+ atomic_inc(&fs_info->delayed_root->items);
+ }
mutex_unlock(&delayed_node->mutex);
btrfs_release_delayed_node(delayed_node, &delayed_node_tracker);
return 0;
diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c
index 481802efaa14..e8bc37453336 100644
--- a/fs/btrfs/delayed-ref.c
+++ b/fs/btrfs/delayed-ref.c
@@ -228,7 +228,7 @@ int btrfs_delayed_refs_rsv_refill(struct btrfs_fs_info *fs_info,
if (!num_bytes)
return 0;
- ret = btrfs_reserve_metadata_bytes(fs_info, space_info, num_bytes, flush);
+ ret = btrfs_reserve_metadata_bytes(space_info, num_bytes, flush);
if (ret)
return ret;
@@ -798,9 +798,13 @@ static void init_delayed_ref_head(struct btrfs_delayed_ref_head *head_ref,
}
/*
- * helper function to actually insert a head node into the rbtree.
- * this does all the dirty work in terms of maintaining the correct
- * overall modification count.
+ * Helper function to actually insert a head node into the xarray. This does all
+ * the dirty work in terms of maintaining the correct overall modification
+ * count.
+ *
+ * The caller is responsible for calling kfree() on @qrecord. More specifically,
+ * if this function reports that it did not insert it as noted in
+ * @qrecord_inserted_ret, then it's safe to call kfree() on it.
*
* Returns an error pointer in case of an error.
*/
@@ -814,7 +818,14 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans,
struct btrfs_delayed_ref_head *existing;
struct btrfs_delayed_ref_root *delayed_refs;
const unsigned long index = (head_ref->bytenr >> fs_info->sectorsize_bits);
- bool qrecord_inserted = false;
+
+ /*
+ * If 'qrecord_inserted_ret' is provided, then the first thing we need
+ * to do is to initialize it to false just in case we have an exit
+ * before trying to insert the record.
+ */
+ if (qrecord_inserted_ret)
+ *qrecord_inserted_ret = false;
delayed_refs = &trans->transaction->delayed_refs;
lockdep_assert_held(&delayed_refs->lock);
@@ -833,6 +844,12 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans,
/* Record qgroup extent info if provided */
if (qrecord) {
+ /*
+ * Setting 'qrecord' but not 'qrecord_inserted_ret' will likely
+ * result in a memory leakage.
+ */
+ ASSERT(qrecord_inserted_ret != NULL);
+
int ret;
ret = btrfs_qgroup_trace_extent_nolock(fs_info, delayed_refs, qrecord,
@@ -840,12 +857,10 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans,
if (ret) {
/* Clean up if insertion fails or item exists. */
xa_release(&delayed_refs->dirty_extents, index);
- /* Caller responsible for freeing qrecord on error. */
if (ret < 0)
return ERR_PTR(ret);
- kfree(qrecord);
- } else {
- qrecord_inserted = true;
+ } else if (qrecord_inserted_ret) {
+ *qrecord_inserted_ret = true;
}
}
@@ -888,8 +903,6 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans,
delayed_refs->num_heads++;
delayed_refs->num_heads_ready++;
}
- if (qrecord_inserted_ret)
- *qrecord_inserted_ret = qrecord_inserted;
return head_ref;
}
@@ -1049,6 +1062,14 @@ static int add_delayed_ref(struct btrfs_trans_handle *trans,
xa_release(&delayed_refs->head_refs, index);
spin_unlock(&delayed_refs->lock);
ret = PTR_ERR(new_head_ref);
+
+ /*
+ * It's only safe to call kfree() on 'qrecord' if
+ * add_delayed_ref_head() has _not_ inserted it for
+ * tracing. Otherwise we need to handle this here.
+ */
+ if (!qrecord_reserved || qrecord_inserted)
+ goto free_head_ref;
goto free_record;
}
head_ref = new_head_ref;
@@ -1071,6 +1092,8 @@ static int add_delayed_ref(struct btrfs_trans_handle *trans,
if (qrecord_inserted)
return btrfs_qgroup_trace_extent_post(trans, record, generic_ref->bytenr);
+
+ kfree(record);
return 0;
free_record:
diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c
index a4eaef60549e..b6c7da8e1bc8 100644
--- a/fs/btrfs/dev-replace.c
+++ b/fs/btrfs/dev-replace.c
@@ -489,8 +489,8 @@ static int mark_block_group_to_copy(struct btrfs_fs_info *fs_info,
}
path->reada = READA_FORWARD;
- path->search_commit_root = 1;
- path->skip_locking = 1;
+ path->search_commit_root = true;
+ path->skip_locking = true;
key.objectid = src_dev->devid;
key.type = BTRFS_DEV_EXTENT_KEY;
diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
index 69863e398e22..085a83ae9e62 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -9,6 +9,7 @@
#include "transaction.h"
#include "accessors.h"
#include "dir-item.h"
+#include "delayed-inode.h"
/*
* insert a name into a directory, doing overflow properly if there is a hash
@@ -111,7 +112,7 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans,
int ret = 0;
int ret2 = 0;
struct btrfs_root *root = dir->root;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_dir_item *dir_item;
struct extent_buffer *leaf;
unsigned long name_ptr;
@@ -163,7 +164,6 @@ second_insert:
ret2 = btrfs_insert_delayed_dir_index(trans, name->name, name->len, dir,
&disk_key, type, index);
out_free:
- btrfs_free_path(path);
if (ret)
return ret;
if (ret2)
diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c
index 802d4dbe5b38..07e19e88ba4b 100644
--- a/fs/btrfs/direct-io.c
+++ b/fs/btrfs/direct-io.c
@@ -10,6 +10,8 @@
#include "fs.h"
#include "transaction.h"
#include "volumes.h"
+#include "bio.h"
+#include "ordered-data.h"
struct btrfs_dio_data {
ssize_t submitted;
@@ -184,7 +186,7 @@ static struct extent_map *btrfs_new_extent_direct(struct btrfs_inode *inode,
alloc_hint = btrfs_get_extent_allocation_hint(inode, start, len);
again:
ret = btrfs_reserve_extent(root, len, len, fs_info->sectorsize,
- 0, alloc_hint, &ins, 1, 1);
+ 0, alloc_hint, &ins, true, true);
if (ret == -EAGAIN) {
ASSERT(btrfs_is_zoned(fs_info));
wait_on_bit_io(&inode->root->fs_info->flags, BTRFS_FS_NEED_ZONE_FINISH,
@@ -385,7 +387,7 @@ static int btrfs_dio_iomap_begin(struct inode *inode, loff_t start,
* to allocate a contiguous array for the checksums.
*/
if (!write)
- len = min_t(u64, len, fs_info->sectorsize * BTRFS_MAX_BIO_SECTORS);
+ len = min_t(u64, len, fs_info->sectorsize * BIO_MAX_VECS);
lockstart = start;
lockend = start + len - 1;
@@ -713,10 +715,8 @@ static void btrfs_dio_submit_io(const struct iomap_iter *iter, struct bio *bio,
container_of(bbio, struct btrfs_dio_private, bbio);
struct btrfs_dio_data *dio_data = iter->private;
- btrfs_bio_init(bbio, BTRFS_I(iter->inode)->root->fs_info,
+ btrfs_bio_init(bbio, BTRFS_I(iter->inode), file_offset,
btrfs_dio_end_io, bio->bi_private);
- bbio->inode = BTRFS_I(iter->inode);
- bbio->file_offset = file_offset;
dip->file_offset = file_offset;
dip->bytes = bio->bi_iter.bi_size;
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 0aa7e5d1b05f..89149fac804c 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -50,6 +50,7 @@
#include "relocation.h"
#include "scrub.h"
#include "super.h"
+#include "delayed-inode.h"
#define BTRFS_SUPER_FLAG_SUPP (BTRFS_HEADER_FLAG_WRITTEN |\
BTRFS_HEADER_FLAG_RELOC |\
@@ -182,26 +183,33 @@ static int btrfs_repair_eb_io_failure(const struct extent_buffer *eb,
int mirror_num)
{
struct btrfs_fs_info *fs_info = eb->fs_info;
+ const u32 step = min(fs_info->nodesize, PAGE_SIZE);
+ const u32 nr_steps = eb->len / step;
+ phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
int ret = 0;
if (sb_rdonly(fs_info->sb))
return -EROFS;
- for (int i = 0; i < num_extent_folios(eb); i++) {
+ for (int i = 0; i < num_extent_pages(eb); i++) {
struct folio *folio = eb->folios[i];
- u64 start = max_t(u64, eb->start, folio_pos(folio));
- u64 end = min_t(u64, eb->start + eb->len,
- folio_pos(folio) + eb->folio_size);
- u32 len = end - start;
- phys_addr_t paddr = PFN_PHYS(folio_pfn(folio)) +
- offset_in_folio(folio, start);
-
- ret = btrfs_repair_io_failure(fs_info, 0, start, len, start,
- paddr, mirror_num);
- if (ret)
- break;
+
+ /* No large folio support yet. */
+ ASSERT(folio_order(folio) == 0);
+ ASSERT(i < nr_steps);
+
+ /*
+ * For nodesize < page size, there is just one paddr, with some
+ * offset inside the page.
+ *
+ * For nodesize >= page size, it's one or more paddrs, and eb->start
+ * must be aligned to page boundary.
+ */
+ paddrs[i] = page_to_phys(&folio->page) + offset_in_page(eb->start);
}
+ ret = btrfs_repair_io_failure(fs_info, 0, eb->start, eb->len, eb->start,
+ paddrs, step, mirror_num);
return ret;
}
@@ -398,10 +406,10 @@ int btrfs_validate_extent_buffer(struct extent_buffer *eb,
if (memcmp(result, header_csum, csum_size) != 0) {
btrfs_warn_rl(fs_info,
-"checksum verify failed on logical %llu mirror %u wanted " CSUM_FMT " found " CSUM_FMT " level %d%s",
+"checksum verify failed on logical %llu mirror %u wanted " BTRFS_CSUM_FMT " found " BTRFS_CSUM_FMT " level %d%s",
eb->start, eb->read_mirror,
- CSUM_FMT_VALUE(csum_size, header_csum),
- CSUM_FMT_VALUE(csum_size, result),
+ BTRFS_CSUM_FMT_VALUE(csum_size, header_csum),
+ BTRFS_CSUM_FMT_VALUE(csum_size, result),
btrfs_header_level(eb),
ignore_csum ? ", ignored" : "");
if (unlikely(!ignore_csum)) {
@@ -644,20 +652,10 @@ static struct btrfs_root *btrfs_alloc_root(struct btrfs_fs_info *fs_info,
if (!root)
return NULL;
- memset(&root->root_key, 0, sizeof(root->root_key));
- memset(&root->root_item, 0, sizeof(root->root_item));
- memset(&root->defrag_progress, 0, sizeof(root->defrag_progress));
root->fs_info = fs_info;
root->root_key.objectid = objectid;
- root->node = NULL;
- root->commit_root = NULL;
- root->state = 0;
RB_CLEAR_NODE(&root->rb_node);
- btrfs_set_root_last_trans(root, 0);
- root->free_objectid = 0;
- root->nr_delalloc_inodes = 0;
- root->nr_ordered_extents = 0;
xa_init(&root->inodes);
xa_init(&root->delayed_nodes);
@@ -691,10 +689,7 @@ static struct btrfs_root *btrfs_alloc_root(struct btrfs_fs_info *fs_info,
refcount_set(&root->refs, 1);
atomic_set(&root->snapshot_force_cow, 0);
atomic_set(&root->nr_swapfiles, 0);
- btrfs_set_root_log_transid(root, 0);
root->log_transid_committed = -1;
- btrfs_set_root_last_log_commit(root, 0);
- root->anon_dev = 0;
if (!btrfs_is_testing(fs_info)) {
btrfs_extent_io_tree_init(fs_info, &root->dirty_log_pages,
IO_TREE_ROOT_DIRTY_LOG_PAGES);
@@ -1773,8 +1768,6 @@ static void btrfs_stop_all_workers(struct btrfs_fs_info *fs_info)
destroy_workqueue(fs_info->endio_workers);
if (fs_info->rmw_workers)
destroy_workqueue(fs_info->rmw_workers);
- if (fs_info->compressed_write_workers)
- destroy_workqueue(fs_info->compressed_write_workers);
btrfs_destroy_workqueue(fs_info->endio_write_workers);
btrfs_destroy_workqueue(fs_info->endio_freespace_worker);
btrfs_destroy_workqueue(fs_info->delayed_workers);
@@ -1986,8 +1979,6 @@ static int btrfs_init_workqueues(struct btrfs_fs_info *fs_info)
fs_info->endio_write_workers =
btrfs_alloc_workqueue(fs_info, "endio-write", flags,
max_active, 2);
- fs_info->compressed_write_workers =
- alloc_workqueue("btrfs-compressed-write", flags, max_active);
fs_info->endio_freespace_worker =
btrfs_alloc_workqueue(fs_info, "freespace-write", flags,
max_active, 0);
@@ -2003,7 +1994,6 @@ static int btrfs_init_workqueues(struct btrfs_fs_info *fs_info)
if (!(fs_info->workers &&
fs_info->delalloc_workers && fs_info->flush_workers &&
fs_info->endio_workers && fs_info->endio_meta_workers &&
- fs_info->compressed_write_workers &&
fs_info->endio_write_workers &&
fs_info->endio_freespace_worker && fs_info->rmw_workers &&
fs_info->caching_workers && fs_info->fixup_workers &&
@@ -3255,12 +3245,6 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount)
PAGE_SIZE, fs_info->sectorsize);
return -EINVAL;
}
- if (fs_info->sectorsize > PAGE_SIZE && btrfs_fs_incompat(fs_info, RAID56)) {
- btrfs_err(fs_info,
- "RAID56 is not supported for page size %lu with sectorsize %u",
- PAGE_SIZE, fs_info->sectorsize);
- return -EINVAL;
- }
/* This can be called by remount, we need to protect the super block. */
spin_lock(&fs_info->super_lock);
@@ -4290,7 +4274,7 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info)
/*
* When finishing a compressed write bio we schedule a work queue item
- * to finish an ordered extent - btrfs_finish_compressed_write_work()
+ * to finish an ordered extent - end_bbio_compressed_write()
* calls btrfs_finish_ordered_extent() which in turns does a call to
* btrfs_queue_ordered_fn(), and that queues the ordered extent
* completion either in the endio_write_workers work queue or in the
@@ -4298,7 +4282,7 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info)
* below, so before we flush them we must flush this queue for the
* workers of compressed writes.
*/
- flush_workqueue(fs_info->compressed_write_workers);
+ flush_workqueue(fs_info->endio_workers);
/*
* After we parked the cleaner kthread, ordered extents may have
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index 57920f2c6fe4..5320da83d0cf 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -9,7 +9,8 @@
#include <linux/sizes.h>
#include <linux/compiler_types.h>
#include "ctree.h"
-#include "fs.h"
+#include "bio.h"
+#include "ordered-data.h"
struct block_device;
struct super_block;
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index dc4ca98c3780..e4cae34620d1 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -40,6 +40,7 @@
#include "orphan.h"
#include "tree-checker.h"
#include "raid-stripe-tree.h"
+#include "delayed-inode.h"
#undef SCRAMBLE_DELAYED_REFS
@@ -164,8 +165,8 @@ search_again:
if (unlikely(num_refs == 0)) {
ret = -EUCLEAN;
btrfs_err(fs_info,
- "unexpected zero reference count for extent item (%llu %u %llu)",
- key.objectid, key.type, key.offset);
+ "unexpected zero reference count for extent item " BTRFS_KEY_FMT,
+ BTRFS_KEY_FMT_VALUE(&key));
btrfs_abort_transaction(trans, ret);
return ret;
}
@@ -597,8 +598,8 @@ static noinline int remove_extent_data_ref(struct btrfs_trans_handle *trans,
num_refs = btrfs_shared_data_ref_count(leaf, ref2);
} else {
btrfs_err(trans->fs_info,
- "unrecognized backref key (%llu %u %llu)",
- key.objectid, key.type, key.offset);
+ "unrecognized backref key " BTRFS_KEY_FMT,
+ BTRFS_KEY_FMT_VALUE(&key));
btrfs_abort_transaction(trans, -EUCLEAN);
return -EUCLEAN;
}
@@ -788,7 +789,7 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
want = extent_ref_type(parent, owner);
if (insert) {
extra_size = btrfs_extent_inline_ref_size(want);
- path->search_for_extension = 1;
+ path->search_for_extension = true;
} else
extra_size = -1;
@@ -954,7 +955,7 @@ again:
if (!path->keep_locks) {
btrfs_release_path(path);
- path->keep_locks = 1;
+ path->keep_locks = true;
goto again;
}
@@ -975,11 +976,11 @@ out_no_entry:
*ref_ret = (struct btrfs_extent_inline_ref *)ptr;
out:
if (path->keep_locks) {
- path->keep_locks = 0;
+ path->keep_locks = false;
btrfs_unlock_up_safe(path, 1);
}
if (insert)
- path->search_for_extension = 0;
+ path->search_for_extension = false;
return ret;
}
@@ -1764,7 +1765,7 @@ static int run_one_delayed_ref(struct btrfs_trans_handle *trans,
if (TRANS_ABORTED(trans)) {
if (insert_reserved) {
- btrfs_pin_extent(trans, node->bytenr, node->num_bytes, 1);
+ btrfs_pin_extent(trans, node->bytenr, node->num_bytes);
free_head_ref_squota_rsv(trans->fs_info, href);
}
return 0;
@@ -1783,7 +1784,7 @@ static int run_one_delayed_ref(struct btrfs_trans_handle *trans,
else
BUG();
if (ret && insert_reserved)
- btrfs_pin_extent(trans, node->bytenr, node->num_bytes, 1);
+ btrfs_pin_extent(trans, node->bytenr, node->num_bytes);
if (ret < 0)
btrfs_err(trans->fs_info,
"failed to run delayed ref for logical %llu num_bytes %llu type %u action %u ref_mod %d: %d",
@@ -1890,7 +1891,7 @@ static int cleanup_ref_head(struct btrfs_trans_handle *trans,
spin_unlock(&delayed_refs->lock);
if (head->must_insert_reserved) {
- btrfs_pin_extent(trans, head->bytenr, head->num_bytes, 1);
+ btrfs_pin_extent(trans, head->bytenr, head->num_bytes);
if (head->is_data) {
struct btrfs_root *csum_root;
@@ -2591,34 +2592,34 @@ static u64 first_logical_byte(struct btrfs_fs_info *fs_info)
}
static int pin_down_extent(struct btrfs_trans_handle *trans,
- struct btrfs_block_group *cache,
- u64 bytenr, u64 num_bytes, int reserved)
+ struct btrfs_block_group *bg,
+ u64 bytenr, u64 num_bytes, bool reserved)
{
- spin_lock(&cache->space_info->lock);
- spin_lock(&cache->lock);
- cache->pinned += num_bytes;
- btrfs_space_info_update_bytes_pinned(cache->space_info, num_bytes);
- if (reserved) {
- cache->reserved -= num_bytes;
- cache->space_info->bytes_reserved -= num_bytes;
- }
- spin_unlock(&cache->lock);
- spin_unlock(&cache->space_info->lock);
+ struct btrfs_space_info *space_info = bg->space_info;
+ const u64 reserved_bytes = (reserved ? num_bytes : 0);
+
+ spin_lock(&space_info->lock);
+ spin_lock(&bg->lock);
+ bg->pinned += num_bytes;
+ bg->reserved -= reserved_bytes;
+ spin_unlock(&bg->lock);
+ space_info->bytes_reserved -= reserved_bytes;
+ btrfs_space_info_update_bytes_pinned(space_info, num_bytes);
+ spin_unlock(&space_info->lock);
btrfs_set_extent_bit(&trans->transaction->pinned_extents, bytenr,
bytenr + num_bytes - 1, EXTENT_DIRTY, NULL);
return 0;
}
-int btrfs_pin_extent(struct btrfs_trans_handle *trans,
- u64 bytenr, u64 num_bytes, int reserved)
+int btrfs_pin_extent(struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes)
{
struct btrfs_block_group *cache;
cache = btrfs_lookup_block_group(trans->fs_info, bytenr);
BUG_ON(!cache); /* Logic error */
- pin_down_extent(trans, cache, bytenr, num_bytes, reserved);
+ pin_down_extent(trans, cache, bytenr, num_bytes, true);
btrfs_put_block_group(cache);
return 0;
@@ -2642,7 +2643,7 @@ int btrfs_pin_extent_for_log_replay(struct btrfs_trans_handle *trans,
if (ret)
goto out;
- pin_down_extent(trans, cache, eb->start, eb->len, 0);
+ pin_down_extent(trans, cache, eb->start, eb->len, false);
/* remove us from the free space cache (if we're there at all) */
ret = btrfs_remove_free_space(cache, eb->start, eb->len);
@@ -2747,13 +2748,11 @@ static int unpin_extent_range(struct btrfs_fs_info *fs_info,
struct btrfs_free_cluster *cluster = NULL;
u64 total_unpinned = 0;
u64 empty_cluster = 0;
- bool readonly;
- int ret = 0;
while (start <= end) {
u64 len;
+ bool readonly;
- readonly = false;
if (!cache ||
start >= cache->start + cache->length) {
if (cache)
@@ -2762,8 +2761,7 @@ static int unpin_extent_range(struct btrfs_fs_info *fs_info,
cache = btrfs_lookup_block_group(fs_info, start);
if (unlikely(cache == NULL)) {
/* Logic error, something removed the block group. */
- ret = -EUCLEAN;
- goto out;
+ return -EUCLEAN;
}
cluster = fetch_cluster_info(fs_info,
@@ -2797,27 +2795,28 @@ static int unpin_extent_range(struct btrfs_fs_info *fs_info,
spin_lock(&space_info->lock);
spin_lock(&cache->lock);
+ readonly = cache->ro;
cache->pinned -= len;
+ spin_unlock(&cache->lock);
+
btrfs_space_info_update_bytes_pinned(space_info, -len);
space_info->max_extent_size = 0;
- if (cache->ro) {
+
+ if (readonly) {
space_info->bytes_readonly += len;
- readonly = true;
} else if (btrfs_is_zoned(fs_info)) {
/* Need reset before reusing in a zoned block group */
btrfs_space_info_update_bytes_zone_unusable(space_info, len);
- readonly = true;
- }
- spin_unlock(&cache->lock);
- if (!readonly && return_free_space)
+ } else if (return_free_space) {
btrfs_return_free_space(space_info, len);
+ }
spin_unlock(&space_info->lock);
}
if (cache)
btrfs_put_block_group(cache);
-out:
- return ret;
+
+ return 0;
}
int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans)
@@ -3086,7 +3085,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
{
struct btrfs_fs_info *info = trans->fs_info;
struct btrfs_key key;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_root *extent_root;
struct extent_buffer *leaf;
struct btrfs_extent_item *ei;
@@ -3121,7 +3120,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
node->bytenr, refs_to_drop);
ret = -EINVAL;
btrfs_abort_transaction(trans, ret);
- goto out;
+ return ret;
}
if (is_data)
@@ -3166,15 +3165,14 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
abort_and_dump(trans, path,
"invalid iref slot %u, no EXTENT/METADATA_ITEM found but has inline extent ref",
path->slots[0]);
- ret = -EUCLEAN;
- goto out;
+ return -EUCLEAN;
}
/* Must be SHARED_* item, remove the backref first */
ret = remove_extent_backref(trans, extent_root, path,
NULL, refs_to_drop, is_data);
if (unlikely(ret)) {
btrfs_abort_transaction(trans, ret);
- goto out;
+ return ret;
}
btrfs_release_path(path);
@@ -3223,7 +3221,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
}
if (unlikely(ret < 0)) {
btrfs_abort_transaction(trans, ret);
- goto out;
+ return ret;
}
extent_slot = path->slots[0];
}
@@ -3232,10 +3230,10 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
"unable to find ref byte nr %llu parent %llu root %llu owner %llu offset %llu slot %d",
bytenr, node->parent, node->ref_root, owner_objectid,
owner_offset, path->slots[0]);
- goto out;
+ return ret;
} else {
btrfs_abort_transaction(trans, ret);
- goto out;
+ return ret;
}
leaf = path->nodes[0];
@@ -3246,7 +3244,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
"unexpected extent item size, has %u expect >= %zu",
item_size, sizeof(*ei));
btrfs_abort_transaction(trans, ret);
- goto out;
+ return ret;
}
ei = btrfs_item_ptr(leaf, extent_slot,
struct btrfs_extent_item);
@@ -3260,8 +3258,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
key.objectid, key.type, key.offset,
path->slots[0], owner_objectid, item_size,
sizeof(*ei) + sizeof(*bi));
- ret = -EUCLEAN;
- goto out;
+ return -EUCLEAN;
}
bi = (struct btrfs_tree_block_info *)(ei + 1);
WARN_ON(owner_objectid != btrfs_tree_block_level(leaf, bi));
@@ -3272,8 +3269,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
abort_and_dump(trans, path,
"trying to drop %d refs but we only have %llu for bytenr %llu slot %u",
refs_to_drop, refs, bytenr, path->slots[0]);
- ret = -EUCLEAN;
- goto out;
+ return -EUCLEAN;
}
refs -= refs_to_drop;
@@ -3289,8 +3285,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
abort_and_dump(trans, path,
"invalid iref, got inlined extent ref but no EXTENT/METADATA_ITEM found, slot %u",
path->slots[0]);
- ret = -EUCLEAN;
- goto out;
+ return -EUCLEAN;
}
} else {
btrfs_set_extent_refs(leaf, ei, refs);
@@ -3300,7 +3295,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
iref, refs_to_drop, is_data);
if (unlikely(ret)) {
btrfs_abort_transaction(trans, ret);
- goto out;
+ return ret;
}
}
} else {
@@ -3320,17 +3315,15 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
"invalid refs_to_drop, current refs %u refs_to_drop %u slot %u",
extent_data_ref_count(path, iref),
refs_to_drop, path->slots[0]);
- ret = -EUCLEAN;
- goto out;
+ return -EUCLEAN;
}
if (iref) {
if (unlikely(path->slots[0] != extent_slot)) {
abort_and_dump(trans, path,
-"invalid iref, extent item key (%llu %u %llu) slot %u doesn't have wanted iref",
- key.objectid, key.type,
- key.offset, path->slots[0]);
- ret = -EUCLEAN;
- goto out;
+"invalid iref, extent item key " BTRFS_KEY_FMT " slot %u doesn't have wanted iref",
+ BTRFS_KEY_FMT_VALUE(&key),
+ path->slots[0]);
+ return -EUCLEAN;
}
} else {
/*
@@ -3343,8 +3336,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
abort_and_dump(trans, path,
"invalid SHARED_* item slot %u, previous item is not EXTENT/METADATA_ITEM",
path->slots[0]);
- ret = -EUCLEAN;
- goto out;
+ return -EUCLEAN;
}
path->slots[0] = extent_slot;
num_to_del = 2;
@@ -3365,7 +3357,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
num_to_del);
if (unlikely(ret)) {
btrfs_abort_transaction(trans, ret);
- goto out;
+ return ret;
}
btrfs_release_path(path);
@@ -3373,8 +3365,6 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
}
btrfs_release_path(path);
-out:
- btrfs_free_path(path);
return ret;
}
@@ -3483,7 +3473,7 @@ int btrfs_free_tree_block(struct btrfs_trans_handle *trans,
bg = btrfs_lookup_block_group(fs_info, buf->start);
if (btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN)) {
- pin_down_extent(trans, bg, buf->start, buf->len, 1);
+ pin_down_extent(trans, bg, buf->start, buf->len, true);
btrfs_put_block_group(bg);
goto out;
}
@@ -3507,7 +3497,7 @@ int btrfs_free_tree_block(struct btrfs_trans_handle *trans,
if (test_bit(BTRFS_FS_TREE_MOD_LOG_USERS, &fs_info->flags)
|| btrfs_is_zoned(fs_info)) {
- pin_down_extent(trans, bg, buf->start, buf->len, 1);
+ pin_down_extent(trans, bg, buf->start, buf->len, true);
btrfs_put_block_group(bg);
goto out;
}
@@ -3537,7 +3527,7 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_ref *ref)
* tree, just update pinning info and exit early.
*/
if (ref->ref_root == BTRFS_TREE_LOG_OBJECTID) {
- btrfs_pin_extent(trans, ref->bytenr, ref->num_bytes, 1);
+ btrfs_pin_extent(trans, ref->bytenr, ref->num_bytes);
ret = 0;
} else if (ref->type == BTRFS_REF_METADATA) {
ret = btrfs_add_delayed_tree_ref(trans, ref, NULL);
@@ -3588,15 +3578,14 @@ enum btrfs_loop_type {
};
static inline void
-btrfs_lock_block_group(struct btrfs_block_group *cache,
- int delalloc)
+btrfs_lock_block_group(struct btrfs_block_group *cache, bool delalloc)
{
if (delalloc)
down_read(&cache->data_rwsem);
}
static inline void btrfs_grab_block_group(struct btrfs_block_group *cache,
- int delalloc)
+ bool delalloc)
{
btrfs_get_block_group(cache);
if (delalloc)
@@ -3606,7 +3595,7 @@ static inline void btrfs_grab_block_group(struct btrfs_block_group *cache,
static struct btrfs_block_group *btrfs_lock_cluster(
struct btrfs_block_group *block_group,
struct btrfs_free_cluster *cluster,
- int delalloc)
+ bool delalloc)
__acquires(&cluster->refill_lock)
{
struct btrfs_block_group *used_bg = NULL;
@@ -3643,8 +3632,7 @@ static struct btrfs_block_group *btrfs_lock_cluster(
}
static inline void
-btrfs_release_block_group(struct btrfs_block_group *cache,
- int delalloc)
+btrfs_release_block_group(struct btrfs_block_group *cache, bool delalloc)
{
if (delalloc)
up_read(&cache->data_rwsem);
@@ -4034,7 +4022,7 @@ static int do_allocation(struct btrfs_block_group *block_group,
static void release_block_group(struct btrfs_block_group *block_group,
struct find_free_extent_ctl *ffe_ctl,
- int delalloc)
+ bool delalloc)
{
switch (ffe_ctl->policy) {
case BTRFS_EXTENT_ALLOC_CLUSTERED:
@@ -4690,7 +4678,7 @@ loop:
int btrfs_reserve_extent(struct btrfs_root *root, u64 ram_bytes,
u64 num_bytes, u64 min_alloc_size,
u64 empty_size, u64 hint_byte,
- struct btrfs_key *ins, int is_data, int delalloc)
+ struct btrfs_key *ins, bool is_data, bool delalloc)
{
struct btrfs_fs_info *fs_info = root->fs_info;
struct find_free_extent_ctl ffe_ctl = {};
@@ -4735,8 +4723,7 @@ again:
"allocation failed flags %llu, wanted %llu tree-log %d, relocation: %d",
flags, num_bytes, for_treelog, for_data_reloc);
if (sinfo)
- btrfs_dump_space_info(fs_info, sinfo,
- num_bytes, 1);
+ btrfs_dump_space_info(sinfo, num_bytes, 1);
}
}
@@ -4776,7 +4763,7 @@ int btrfs_pin_reserved_extent(struct btrfs_trans_handle *trans,
return -ENOSPC;
}
- ret = pin_down_extent(trans, cache, eb->start, eb->len, 1);
+ ret = pin_down_extent(trans, cache, eb->start, eb->len, true);
btrfs_put_block_group(cache);
return ret;
}
@@ -5022,7 +5009,7 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
ret = alloc_reserved_file_extent(trans, 0, root_objectid, 0, owner,
offset, ins, 1, root_objectid);
if (ret)
- btrfs_pin_extent(trans, ins->objectid, ins->offset, 1);
+ btrfs_pin_extent(trans, ins->objectid, ins->offset);
ret = btrfs_record_squota_delta(fs_info, &delta);
btrfs_put_block_group(block_group);
return ret;
@@ -5168,7 +5155,7 @@ struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans,
return ERR_CAST(block_rsv);
ret = btrfs_reserve_extent(root, blocksize, blocksize, blocksize,
- empty_size, hint, &ins, 0, 0);
+ empty_size, hint, &ins, false, false);
if (ret)
goto out_unuse;
@@ -6061,7 +6048,7 @@ int btrfs_drop_snapshot(struct btrfs_root *root, bool update_ref, bool for_reloc
struct btrfs_trans_handle *trans;
struct btrfs_root *tree_root = fs_info->tree_root;
struct btrfs_root_item *root_item = &root->root_item;
- struct walk_control *wc;
+ struct walk_control AUTO_KFREE(wc);
struct btrfs_key key;
const u64 rootid = btrfs_root_id(root);
int ret = 0;
@@ -6079,9 +6066,8 @@ int btrfs_drop_snapshot(struct btrfs_root *root, bool update_ref, bool for_reloc
wc = kzalloc(sizeof(*wc), GFP_NOFS);
if (!wc) {
- btrfs_free_path(path);
ret = -ENOMEM;
- goto out;
+ goto out_free;
}
/*
@@ -6291,7 +6277,6 @@ out_end_trans:
btrfs_end_transaction_throttle(trans);
out_free:
- kfree(wc);
btrfs_free_path(path);
out:
if (!ret && root_dropped) {
@@ -6334,7 +6319,7 @@ int btrfs_drop_subtree(struct btrfs_trans_handle *trans,
{
struct btrfs_fs_info *fs_info = root->fs_info;
BTRFS_PATH_AUTO_FREE(path);
- struct walk_control *wc;
+ struct walk_control AUTO_KFREE(wc);
int level;
int parent_level;
int ret = 0;
@@ -6373,18 +6358,17 @@ int btrfs_drop_subtree(struct btrfs_trans_handle *trans,
while (1) {
ret = walk_down_tree(trans, root, path, wc);
if (ret < 0)
- break;
+ return ret;
ret = walk_up_tree(trans, root, path, wc, parent_level);
if (ret) {
- if (ret > 0)
- ret = 0;
+ if (ret < 0)
+ return ret;
break;
}
}
- kfree(wc);
- return ret;
+ return 0;
}
/*
diff --git a/fs/btrfs/extent-tree.h b/fs/btrfs/extent-tree.h
index e970ac42a871..71bb8109c969 100644
--- a/fs/btrfs/extent-tree.h
+++ b/fs/btrfs/extent-tree.h
@@ -30,7 +30,6 @@ struct find_free_extent_ctl {
u64 min_alloc_size;
u64 empty_size;
u64 flags;
- int delalloc;
/* Where to start the search inside the bg */
u64 search_start;
@@ -40,6 +39,7 @@ struct find_free_extent_ctl {
struct btrfs_free_cluster *last_ptr;
bool use_cluster;
+ bool delalloc;
bool have_caching_bg;
bool orig_have_caching_bg;
@@ -49,6 +49,16 @@ struct find_free_extent_ctl {
/* Allocation is called for data relocation */
bool for_data_reloc;
+ /*
+ * Set to true if we're retrying the allocation on this block group
+ * after waiting for caching progress, this is so that we retry only
+ * once before moving on to another block group.
+ */
+ bool retry_uncached;
+
+ /* Whether or not the allocator is currently following a hint. */
+ bool hinted;
+
/* RAID index, converted from flags */
int index;
@@ -57,13 +67,6 @@ struct find_free_extent_ctl {
*/
int loop;
- /*
- * Set to true if we're retrying the allocation on this block group
- * after waiting for caching progress, this is so that we retry only
- * once before moving on to another block group.
- */
- bool retry_uncached;
-
/* If current block group is cached */
int cached;
@@ -82,9 +85,6 @@ struct find_free_extent_ctl {
/* Allocation policy */
enum btrfs_extent_allocation_policy policy;
- /* Whether or not the allocator is currently following a hint */
- bool hinted;
-
/* Size class of block groups to prefer in early loops */
enum btrfs_block_group_size_class size_class;
};
@@ -110,8 +110,7 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, u64 bytenr,
u64 offset, int metadata, u64 *refs, u64 *flags,
u64 *owner_root);
-int btrfs_pin_extent(struct btrfs_trans_handle *trans, u64 bytenr, u64 num,
- int reserved);
+int btrfs_pin_extent(struct btrfs_trans_handle *trans, u64 bytenr, u64 num);
int btrfs_pin_extent_for_log_replay(struct btrfs_trans_handle *trans,
const struct extent_buffer *eb);
int btrfs_exclude_logged_extents(struct extent_buffer *eb);
@@ -138,7 +137,7 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
struct btrfs_key *ins);
int btrfs_reserve_extent(struct btrfs_root *root, u64 ram_bytes, u64 num_bytes,
u64 min_alloc_size, u64 empty_size, u64 hint_byte,
- struct btrfs_key *ins, int is_data, int delalloc);
+ struct btrfs_key *ins, bool is_data, bool delalloc);
int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct extent_buffer *buf, bool full_backref);
int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 7361d5d890d2..629fd5af4286 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -374,8 +374,7 @@ noinline_for_stack bool find_lock_delalloc_range(struct inode *inode,
struct extent_io_tree *tree = &BTRFS_I(inode)->io_tree;
const u64 orig_start = *start;
const u64 orig_end = *end;
- /* The sanity tests may not set a valid fs_info. */
- u64 max_bytes = fs_info ? fs_info->max_extent_size : BTRFS_MAX_EXTENT_SIZE;
+ u64 max_bytes = fs_info->max_extent_size;
u64 delalloc_start;
u64 delalloc_end;
bool found;
@@ -518,7 +517,7 @@ static void end_folio_read(struct folio *folio, bool uptodate, u64 start, u32 le
*/
static void end_bbio_data_write(struct btrfs_bio *bbio)
{
- struct btrfs_fs_info *fs_info = bbio->fs_info;
+ struct btrfs_fs_info *fs_info = bbio->inode->root->fs_info;
struct bio *bio = &bbio->bio;
int error = blk_status_to_errno(bio->bi_status);
struct folio_iter fi;
@@ -574,7 +573,7 @@ static void begin_folio_read(struct btrfs_fs_info *fs_info, struct folio *folio)
*/
static void end_bbio_data_read(struct btrfs_bio *bbio)
{
- struct btrfs_fs_info *fs_info = bbio->fs_info;
+ struct btrfs_fs_info *fs_info = bbio->inode->root->fs_info;
struct bio *bio = &bbio->bio;
struct folio_iter fi;
@@ -739,12 +738,10 @@ static void alloc_new_bio(struct btrfs_inode *inode,
struct btrfs_fs_info *fs_info = inode->root->fs_info;
struct btrfs_bio *bbio;
- bbio = btrfs_bio_alloc(BIO_MAX_VECS, bio_ctrl->opf, fs_info,
- bio_ctrl->end_io_func, NULL);
+ bbio = btrfs_bio_alloc(BIO_MAX_VECS, bio_ctrl->opf, inode,
+ file_offset, bio_ctrl->end_io_func, NULL);
bbio->bio.bi_iter.bi_sector = disk_bytenr >> SECTOR_SHIFT;
bbio->bio.bi_write_hint = inode->vfs_inode.i_write_hint;
- bbio->inode = inode;
- bbio->file_offset = file_offset;
bio_ctrl->bbio = bbio;
bio_ctrl->len_to_oe_boundary = U32_MAX;
bio_ctrl->next_file_offset = file_offset;
@@ -1691,14 +1688,17 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode,
unsigned long range_bitmap = 0;
bool submitted_io = false;
int found_error = 0;
+ const u64 end = start + len;
const u64 folio_start = folio_pos(folio);
+ const u64 folio_end = folio_start + folio_size(folio);
const unsigned int blocks_per_folio = btrfs_blocks_per_folio(fs_info, folio);
u64 cur;
int bit;
int ret = 0;
- ASSERT(start >= folio_start &&
- start + len <= folio_start + folio_size(folio));
+ ASSERT(start >= folio_start, "start=%llu folio_start=%llu", start, folio_start);
+ ASSERT(end <= folio_end, "start=%llu len=%u folio_start=%llu folio_size=%zu",
+ start, len, folio_start, folio_size(folio));
ret = btrfs_writepage_cow_fixup(folio);
if (ret == -EAGAIN) {
@@ -1714,7 +1714,7 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode,
return ret;
}
- for (cur = start; cur < start + len; cur += fs_info->sectorsize)
+ for (cur = start; cur < end; cur += fs_info->sectorsize)
set_bit((cur - folio_start) >> fs_info->sectorsize_bits, &range_bitmap);
bitmap_and(&bio_ctrl->submit_bitmap, &bio_ctrl->submit_bitmap, &range_bitmap,
blocks_per_folio);
@@ -1725,8 +1725,24 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode,
cur = folio_pos(folio) + (bit << fs_info->sectorsize_bits);
if (cur >= i_size) {
+ struct btrfs_ordered_extent *ordered;
+
+ ordered = btrfs_lookup_first_ordered_range(inode, cur,
+ folio_end - cur);
+ /*
+ * We have just run delalloc before getting here, so
+ * there must be an ordered extent.
+ */
+ ASSERT(ordered != NULL);
+ spin_lock(&inode->ordered_tree_lock);
+ set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags);
+ ordered->truncated_len = min(ordered->truncated_len,
+ cur - ordered->file_offset);
+ spin_unlock(&inode->ordered_tree_lock);
+ btrfs_put_ordered_extent(ordered);
+
btrfs_mark_ordered_io_finished(inode, folio, cur,
- start + len - cur, true);
+ end - cur, true);
/*
* This range is beyond i_size, thus we don't need to
* bother writing back.
@@ -1735,8 +1751,7 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode,
* writeback the sectors with subpage dirty bits,
* causing writeback without ordered extent.
*/
- btrfs_folio_clear_dirty(fs_info, folio, cur,
- start + len - cur);
+ btrfs_folio_clear_dirty(fs_info, folio, cur, end - cur);
break;
}
ret = submit_one_sector(inode, folio, cur, bio_ctrl, i_size);
@@ -1856,7 +1871,7 @@ static int extent_writepage(struct folio *folio, struct btrfs_bio_ctrl *bio_ctrl
folio_size(folio), bio_ctrl, i_size);
if (ret == 1)
return 0;
- if (ret < 0)
+ if (unlikely(ret < 0))
btrfs_err_rl(fs_info,
"failed to submit blocks, root=%lld inode=%llu folio=%llu submit_bitmap=%*pbl: %d",
btrfs_root_id(inode->root), btrfs_ino(inode),
@@ -2206,12 +2221,11 @@ static noinline_for_stack void write_one_eb(struct extent_buffer *eb,
bbio = btrfs_bio_alloc(INLINE_EXTENT_BUFFER_PAGES,
REQ_OP_WRITE | REQ_META | wbc_to_write_flags(wbc),
- eb->fs_info, end_bbio_meta_write, eb);
+ BTRFS_I(fs_info->btree_inode), eb->start,
+ end_bbio_meta_write, eb);
bbio->bio.bi_iter.bi_sector = eb->start >> SECTOR_SHIFT;
bio_set_dev(&bbio->bio, fs_info->fs_devices->latest_dev->bdev);
wbc_init_bio(wbc, &bbio->bio);
- bbio->inode = BTRFS_I(eb->fs_info->btree_inode);
- bbio->file_offset = eb->start;
for (int i = 0; i < num_extent_folios(eb); i++) {
struct folio *folio = eb->folios[i];
u64 range_start = max_t(u64, eb->start, folio_pos(folio));
@@ -3823,6 +3837,7 @@ static void end_bbio_meta_read(struct btrfs_bio *bbio)
int read_extent_buffer_pages_nowait(struct extent_buffer *eb, int mirror_num,
const struct btrfs_tree_parent_check *check)
{
+ struct btrfs_fs_info *fs_info = eb->fs_info;
struct btrfs_bio *bbio;
if (test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags))
@@ -3856,11 +3871,9 @@ int read_extent_buffer_pages_nowait(struct extent_buffer *eb, int mirror_num,
refcount_inc(&eb->refs);
bbio = btrfs_bio_alloc(INLINE_EXTENT_BUFFER_PAGES,
- REQ_OP_READ | REQ_META, eb->fs_info,
- end_bbio_meta_read, eb);
+ REQ_OP_READ | REQ_META, BTRFS_I(fs_info->btree_inode),
+ eb->start, end_bbio_meta_read, eb);
bbio->bio.bi_iter.bi_sector = eb->start >> SECTOR_SHIFT;
- bbio->inode = BTRFS_I(eb->fs_info->btree_inode);
- bbio->file_offset = eb->start;
memcpy(&bbio->parent_check, check, sizeof(*check));
for (int i = 0; i < num_extent_folios(eb); i++) {
struct folio *folio = eb->folios[i];
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index 5fcbfe44218c..02ebb2f238af 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -12,7 +12,6 @@
#include <linux/rwsem.h>
#include <linux/list.h>
#include <linux/slab.h>
-#include "compression.h"
#include "messages.h"
#include "ulist.h"
#include "misc.h"
diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h
index d4b81ee4d97b..6f685f3c9327 100644
--- a/fs/btrfs/extent_map.h
+++ b/fs/btrfs/extent_map.h
@@ -8,8 +8,7 @@
#include <linux/rbtree.h>
#include <linux/list.h>
#include <linux/refcount.h>
-#include "misc.h"
-#include "compression.h"
+#include "fs.h"
struct btrfs_inode;
struct btrfs_fs_info;
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index a42e6d54e7cd..14e5257f0f04 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -18,6 +18,7 @@
#include "fs.h"
#include "accessors.h"
#include "file-item.h"
+#include "volumes.h"
#define __MAX_CSUM_ITEMS(r, size) ((unsigned long)(((BTRFS_LEAF_DATA_SIZE(r) - \
sizeof(struct btrfs_item) * 2) / \
@@ -372,7 +373,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
return -ENOMEM;
if (nblocks * csum_size > BTRFS_BIO_INLINE_CSUM_SIZE) {
- bbio->csum = kmalloc_array(nblocks, csum_size, GFP_NOFS);
+ bbio->csum = kvcalloc(nblocks, csum_size, GFP_NOFS);
if (!bbio->csum)
return -ENOMEM;
} else {
@@ -393,8 +394,8 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
* between reading the free space cache and updating the csum tree.
*/
if (btrfs_is_free_space_inode(inode)) {
- path->search_commit_root = 1;
- path->skip_locking = 1;
+ path->search_commit_root = true;
+ path->skip_locking = true;
}
/*
@@ -422,8 +423,8 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
* from across transactions.
*/
if (bbio->csum_search_commit_root) {
- path->search_commit_root = 1;
- path->skip_locking = 1;
+ path->search_commit_root = true;
+ path->skip_locking = true;
down_read(&fs_info->commit_root_sem);
}
@@ -438,7 +439,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
if (count < 0) {
ret = count;
if (bbio->csum != bbio->csum_inline)
- kfree(bbio->csum);
+ kvfree(bbio->csum);
bbio->csum = NULL;
break;
}
@@ -764,21 +765,55 @@ fail:
return ret;
}
+static void csum_one_bio(struct btrfs_bio *bbio, struct bvec_iter *src)
+{
+ struct btrfs_inode *inode = bbio->inode;
+ struct btrfs_fs_info *fs_info = inode->root->fs_info;
+ SHASH_DESC_ON_STACK(shash, fs_info->csum_shash);
+ struct bio *bio = &bbio->bio;
+ struct btrfs_ordered_sum *sums = bbio->sums;
+ struct bvec_iter iter = *src;
+ phys_addr_t paddr;
+ const u32 blocksize = fs_info->sectorsize;
+ const u32 step = min(blocksize, PAGE_SIZE);
+ const u32 nr_steps = blocksize / step;
+ phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
+ u32 offset = 0;
+ int index = 0;
+
+ shash->tfm = fs_info->csum_shash;
+
+ btrfs_bio_for_each_block(paddr, bio, &iter, step) {
+ paddrs[(offset / step) % nr_steps] = paddr;
+ offset += step;
+
+ if (IS_ALIGNED(offset, blocksize)) {
+ btrfs_calculate_block_csum_pages(fs_info, paddrs, sums->sums + index);
+ index += fs_info->csum_size;
+ }
+ }
+}
+
+static void csum_one_bio_work(struct work_struct *work)
+{
+ struct btrfs_bio *bbio = container_of(work, struct btrfs_bio, csum_work);
+
+ ASSERT(btrfs_op(&bbio->bio) == BTRFS_MAP_WRITE);
+ ASSERT(bbio->async_csum == true);
+ csum_one_bio(bbio, &bbio->csum_saved_iter);
+ complete(&bbio->csum_done);
+}
+
/*
* Calculate checksums of the data contained inside a bio.
*/
-int btrfs_csum_one_bio(struct btrfs_bio *bbio)
+int btrfs_csum_one_bio(struct btrfs_bio *bbio, bool async)
{
struct btrfs_ordered_extent *ordered = bbio->ordered;
struct btrfs_inode *inode = bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
- SHASH_DESC_ON_STACK(shash, fs_info->csum_shash);
struct bio *bio = &bbio->bio;
struct btrfs_ordered_sum *sums;
- struct bvec_iter iter = bio->bi_iter;
- phys_addr_t paddr;
- const u32 blocksize = fs_info->sectorsize;
- int index;
unsigned nofs_flag;
nofs_flag = memalloc_nofs_save();
@@ -789,21 +824,21 @@ int btrfs_csum_one_bio(struct btrfs_bio *bbio)
if (!sums)
return -ENOMEM;
+ sums->logical = bbio->orig_logical;
sums->len = bio->bi_iter.bi_size;
INIT_LIST_HEAD(&sums->list);
-
- sums->logical = bio->bi_iter.bi_sector << SECTOR_SHIFT;
- index = 0;
-
- shash->tfm = fs_info->csum_shash;
-
- btrfs_bio_for_each_block(paddr, bio, &iter, blocksize) {
- btrfs_calculate_block_csum(fs_info, paddr, sums->sums + index);
- index += fs_info->csum_size;
- }
-
bbio->sums = sums;
btrfs_add_ordered_sum(ordered, sums);
+
+ if (!async) {
+ csum_one_bio(bbio, &bbio->bio.bi_iter);
+ return 0;
+ }
+ init_completion(&bbio->csum_done);
+ bbio->async_csum = true;
+ bbio->csum_saved_iter = bbio->bio.bi_iter;
+ INIT_WORK(&bbio->csum_work, csum_one_bio_work);
+ schedule_work(&bbio->csum_work);
return 0;
}
@@ -1142,10 +1177,10 @@ again:
}
btrfs_release_path(path);
- path->search_for_extension = 1;
+ path->search_for_extension = true;
ret = btrfs_search_slot(trans, root, &file_key, path,
csum_size, 1);
- path->search_for_extension = 0;
+ path->search_for_extension = false;
if (ret < 0)
goto out;
diff --git a/fs/btrfs/file-item.h b/fs/btrfs/file-item.h
index 63216c43676d..5645c5e3abdb 100644
--- a/fs/btrfs/file-item.h
+++ b/fs/btrfs/file-item.h
@@ -7,7 +7,7 @@
#include <linux/list.h>
#include <uapi/linux/btrfs_tree.h>
#include "ctree.h"
-#include "accessors.h"
+#include "ordered-data.h"
struct extent_map;
struct btrfs_file_extent_item;
@@ -64,7 +64,7 @@ int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans,
int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_ordered_sum *sums);
-int btrfs_csum_one_bio(struct btrfs_bio *bbio);
+int btrfs_csum_one_bio(struct btrfs_bio *bbio, bool async);
int btrfs_alloc_dummy_sum(struct btrfs_bio *bbio);
int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
struct list_head *list, int search_commit,
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index e7453f992e1e..7a501e73d880 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -75,7 +75,7 @@ int btrfs_dirty_folio(struct btrfs_inode *inode, struct folio *folio, loff_t pos
u64 num_bytes;
u64 start_pos;
u64 end_of_last_block;
- u64 end_pos = pos + write_bytes;
+ const u64 end_pos = pos + write_bytes;
loff_t isize = i_size_read(&inode->vfs_inode);
unsigned int extra_bits = 0;
@@ -86,11 +86,9 @@ int btrfs_dirty_folio(struct btrfs_inode *inode, struct folio *folio, loff_t pos
extra_bits |= EXTENT_NORESERVE;
start_pos = round_down(pos, fs_info->sectorsize);
- num_bytes = round_up(write_bytes + pos - start_pos,
- fs_info->sectorsize);
+ num_bytes = round_up(end_pos - start_pos, fs_info->sectorsize);
ASSERT(num_bytes <= U32_MAX);
- ASSERT(folio_pos(folio) <= pos &&
- folio_next_pos(folio) >= pos + write_bytes);
+ ASSERT(folio_pos(folio) <= pos && folio_next_pos(folio) >= end_pos);
end_of_last_block = start_pos + num_bytes - 1;
@@ -1442,6 +1440,8 @@ ssize_t btrfs_do_write_iter(struct kiocb *iocb, struct iov_iter *from,
struct btrfs_inode *inode = BTRFS_I(file_inode(file));
ssize_t num_written, num_sync;
+ if (unlikely(btrfs_is_shutdown(inode->root->fs_info)))
+ return -EIO;
/*
* If the fs flips readonly due to some impossible error, although we
* have opened a file as writable, we have to stop this write operation
@@ -2044,6 +2044,8 @@ static int btrfs_file_mmap_prepare(struct vm_area_desc *desc)
struct file *filp = desc->file;
struct address_space *mapping = filp->f_mapping;
+ if (unlikely(btrfs_is_shutdown(inode_to_fs_info(file_inode(filp)))))
+ return -EIO;
if (!mapping->a_ops->read_folio)
return -ENOEXEC;
@@ -3113,6 +3115,9 @@ static long btrfs_fallocate(struct file *file, int mode,
int blocksize = BTRFS_I(inode)->root->fs_info->sectorsize;
int ret;
+ if (unlikely(btrfs_is_shutdown(inode_to_fs_info(inode))))
+ return -EIO;
+
/* Do not allow fallocate in ZONED mode */
if (btrfs_is_zoned(inode_to_fs_info(inode)))
return -EOPNOTSUPP;
@@ -3804,6 +3809,9 @@ static int btrfs_file_open(struct inode *inode, struct file *filp)
{
int ret;
+ if (unlikely(btrfs_is_shutdown(inode_to_fs_info(inode))))
+ return -EIO;
+
filp->f_mode |= FMODE_NOWAIT | FMODE_CAN_ODIRECT;
ret = fsverity_file_open(inode, filp);
@@ -3816,6 +3824,9 @@ static ssize_t btrfs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
ssize_t ret = 0;
+ if (unlikely(btrfs_is_shutdown(inode_to_fs_info(file_inode(iocb->ki_filp)))))
+ return -EIO;
+
if (iocb->ki_flags & IOCB_DIRECT) {
ret = btrfs_direct_read(iocb, to);
if (ret < 0 || !iov_iter_count(to) ||
@@ -3826,10 +3837,20 @@ static ssize_t btrfs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
return filemap_read(iocb, to, ret);
}
+static ssize_t btrfs_file_splice_read(struct file *in, loff_t *ppos,
+ struct pipe_inode_info *pipe,
+ size_t len, unsigned int flags)
+{
+ if (unlikely(btrfs_is_shutdown(inode_to_fs_info(file_inode(in)))))
+ return -EIO;
+
+ return filemap_splice_read(in, ppos, pipe, len, flags);
+}
+
const struct file_operations btrfs_file_operations = {
.llseek = btrfs_file_llseek,
.read_iter = btrfs_file_read_iter,
- .splice_read = filemap_splice_read,
+ .splice_read = btrfs_file_splice_read,
.write_iter = btrfs_file_write_iter,
.splice_write = iter_file_splice_write,
.mmap_prepare = btrfs_file_mmap_prepare,
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index ab873bd67192..f0f72850fab2 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -968,8 +968,8 @@ int load_free_space_cache(struct btrfs_block_group *block_group)
path = btrfs_alloc_path();
if (!path)
return 0;
- path->search_commit_root = 1;
- path->skip_locking = 1;
+ path->search_commit_root = true;
+ path->skip_locking = true;
/*
* We must pass a path with search_commit_root set to btrfs_iget in
@@ -3656,7 +3656,7 @@ static int do_trimming(struct btrfs_block_group *block_group,
struct btrfs_fs_info *fs_info = block_group->fs_info;
struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl;
int ret;
- int update = 0;
+ bool bg_ro;
const u64 end = start + bytes;
const u64 reserved_end = reserved_start + reserved_bytes;
enum btrfs_trim_state trim_state = BTRFS_TRIM_STATE_UNTRIMMED;
@@ -3664,12 +3664,14 @@ static int do_trimming(struct btrfs_block_group *block_group,
spin_lock(&space_info->lock);
spin_lock(&block_group->lock);
- if (!block_group->ro) {
+ bg_ro = block_group->ro;
+ if (!bg_ro) {
block_group->reserved += reserved_bytes;
+ spin_unlock(&block_group->lock);
space_info->bytes_reserved += reserved_bytes;
- update = 1;
+ } else {
+ spin_unlock(&block_group->lock);
}
- spin_unlock(&block_group->lock);
spin_unlock(&space_info->lock);
ret = btrfs_discard_extent(fs_info, start, bytes, &trimmed);
@@ -3690,14 +3692,16 @@ static int do_trimming(struct btrfs_block_group *block_group,
list_del(&trim_entry->list);
mutex_unlock(&ctl->cache_writeout_mutex);
- if (update) {
+ if (!bg_ro) {
spin_lock(&space_info->lock);
spin_lock(&block_group->lock);
- if (block_group->ro)
- space_info->bytes_readonly += reserved_bytes;
+ bg_ro = block_group->ro;
block_group->reserved -= reserved_bytes;
- space_info->bytes_reserved -= reserved_bytes;
spin_unlock(&block_group->lock);
+
+ space_info->bytes_reserved -= reserved_bytes;
+ if (bg_ro)
+ space_info->bytes_readonly += reserved_bytes;
spin_unlock(&space_info->lock);
}
diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c
index d86541073d42..1ad2ad384b9e 100644
--- a/fs/btrfs/free-space-tree.c
+++ b/fs/btrfs/free-space-tree.c
@@ -165,11 +165,9 @@ static unsigned long *alloc_bitmap(u32 bitmap_size)
/*
* GFP_NOFS doesn't work with kvmalloc(), but we really can't recurse
- * into the filesystem as the free space bitmap can be modified in the
- * critical section of a transaction commit.
- *
- * TODO: push the memalloc_nofs_{save,restore}() to the caller where we
- * know that recursion is unsafe.
+ * into the filesystem here. All callers hold a transaction handle
+ * open, so if a GFP_KERNEL allocation recurses into the filesystem
+ * and triggers a transaction commit, we would deadlock.
*/
nofs_flag = memalloc_nofs_save();
ret = kvzalloc(bitmap_rounded_size, GFP_KERNEL);
@@ -218,11 +216,8 @@ int btrfs_convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans,
bitmap_size = free_space_bitmap_size(fs_info, block_group->length);
bitmap = alloc_bitmap(bitmap_size);
- if (unlikely(!bitmap)) {
- ret = -ENOMEM;
- btrfs_abort_transaction(trans, ret);
- goto out;
- }
+ if (unlikely(!bitmap))
+ return 0;
start = block_group->start;
end = block_group->start + block_group->length;
@@ -361,11 +356,8 @@ int btrfs_convert_free_space_to_extents(struct btrfs_trans_handle *trans,
bitmap_size = free_space_bitmap_size(fs_info, block_group->length);
bitmap = alloc_bitmap(bitmap_size);
- if (unlikely(!bitmap)) {
- ret = -ENOMEM;
- btrfs_abort_transaction(trans, ret);
- goto out;
- }
+ if (unlikely(!bitmap))
+ return 0;
start = block_group->start;
end = block_group->start + block_group->length;
@@ -841,7 +833,7 @@ int btrfs_remove_from_free_space_tree(struct btrfs_trans_handle *trans,
u64 start, u64 size)
{
struct btrfs_block_group *block_group;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
int ret;
if (!btrfs_fs_compat_ro(trans->fs_info, FREE_SPACE_TREE))
@@ -851,7 +843,7 @@ int btrfs_remove_from_free_space_tree(struct btrfs_trans_handle *trans,
if (unlikely(!path)) {
ret = -ENOMEM;
btrfs_abort_transaction(trans, ret);
- goto out;
+ return ret;
}
block_group = btrfs_lookup_block_group(trans->fs_info, start);
@@ -859,7 +851,7 @@ int btrfs_remove_from_free_space_tree(struct btrfs_trans_handle *trans,
DEBUG_WARN("no block group found for start=%llu", start);
ret = -ENOENT;
btrfs_abort_transaction(trans, ret);
- goto out;
+ return ret;
}
mutex_lock(&block_group->free_space_lock);
@@ -869,8 +861,7 @@ int btrfs_remove_from_free_space_tree(struct btrfs_trans_handle *trans,
btrfs_abort_transaction(trans, ret);
btrfs_put_block_group(block_group);
-out:
- btrfs_free_path(path);
+
return ret;
}
@@ -1023,7 +1014,7 @@ int btrfs_add_to_free_space_tree(struct btrfs_trans_handle *trans,
u64 start, u64 size)
{
struct btrfs_block_group *block_group;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
int ret;
if (!btrfs_fs_compat_ro(trans->fs_info, FREE_SPACE_TREE))
@@ -1033,7 +1024,7 @@ int btrfs_add_to_free_space_tree(struct btrfs_trans_handle *trans,
if (unlikely(!path)) {
ret = -ENOMEM;
btrfs_abort_transaction(trans, ret);
- goto out;
+ return ret;
}
block_group = btrfs_lookup_block_group(trans->fs_info, start);
@@ -1041,7 +1032,7 @@ int btrfs_add_to_free_space_tree(struct btrfs_trans_handle *trans,
DEBUG_WARN("no block group found for start=%llu", start);
ret = -ENOENT;
btrfs_abort_transaction(trans, ret);
- goto out;
+ return ret;
}
mutex_lock(&block_group->free_space_lock);
@@ -1051,8 +1042,7 @@ int btrfs_add_to_free_space_tree(struct btrfs_trans_handle *trans,
btrfs_abort_transaction(trans, ret);
btrfs_put_block_group(block_group);
-out:
- btrfs_free_path(path);
+
return ret;
}
@@ -1466,7 +1456,7 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans,
struct btrfs_block_group *block_group)
{
struct btrfs_root *root = btrfs_free_space_root(block_group);
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key, found_key;
struct extent_buffer *leaf;
u64 start, end;
@@ -1485,7 +1475,7 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans,
if (unlikely(!path)) {
ret = -ENOMEM;
btrfs_abort_transaction(trans, ret);
- goto out;
+ return ret;
}
start = block_group->start;
@@ -1499,7 +1489,7 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans,
ret = btrfs_search_prev_slot(trans, root, &key, path, -1, 1);
if (unlikely(ret)) {
btrfs_abort_transaction(trans, ret);
- goto out;
+ return ret;
}
leaf = path->nodes[0];
@@ -1530,14 +1520,13 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans,
ret = btrfs_del_items(trans, root, path, path->slots[0], nr);
if (unlikely(ret)) {
btrfs_abort_transaction(trans, ret);
- goto out;
+ return ret;
}
btrfs_release_path(path);
}
ret = 0;
-out:
- btrfs_free_path(path);
+
return ret;
}
@@ -1702,8 +1691,8 @@ int btrfs_load_free_space_tree(struct btrfs_caching_control *caching_ctl)
* Just like caching_thread() doesn't want to deadlock on the extent
* tree, we don't want to deadlock on the free space tree.
*/
- path->skip_locking = 1;
- path->search_commit_root = 1;
+ path->skip_locking = true;
+ path->search_commit_root = true;
path->reada = READA_FORWARD;
info = btrfs_search_free_space_info(NULL, block_group, path, 0);
diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index 814bbc9417d2..0f7e1ef27891 100644
--- a/fs/btrfs/fs.h
+++ b/fs/btrfs/fs.h
@@ -29,6 +29,7 @@
#include "extent-io-tree.h"
#include "async-thread.h"
#include "block-rsv.h"
+#include "messages.h"
struct inode;
struct super_block;
@@ -73,6 +74,13 @@ struct btrfs_space_info;
#define BTRFS_SUPER_INFO_SIZE 4096
static_assert(sizeof(struct btrfs_super_block) == BTRFS_SUPER_INFO_SIZE);
+/* Array of bytes with variable length, hexadecimal format 0x1234 */
+#define BTRFS_CSUM_FMT "0x%*phN"
+#define BTRFS_CSUM_FMT_VALUE(size, bytes) size, bytes
+
+#define BTRFS_KEY_FMT "(%llu %u %llu)"
+#define BTRFS_KEY_FMT_VALUE(key) (key)->objectid, (key)->type, (key)->offset
+
/*
* Number of metadata items necessary for an unlink operation:
*
@@ -124,6 +132,12 @@ enum {
/* No more delayed iput can be queued. */
BTRFS_FS_STATE_NO_DELAYED_IPUT,
+ /*
+ * Emergency shutdown, a step further than transaction aborted by
+ * rejecting all operations.
+ */
+ BTRFS_FS_STATE_EMERGENCY_SHUTDOWN,
+
BTRFS_FS_STATE_COUNT
};
@@ -644,7 +658,6 @@ struct btrfs_fs_info {
struct workqueue_struct *endio_workers;
struct workqueue_struct *endio_meta_workers;
struct workqueue_struct *rmw_workers;
- struct workqueue_struct *compressed_write_workers;
struct btrfs_workqueue *endio_write_workers;
struct btrfs_workqueue *endio_freespace_worker;
struct btrfs_workqueue *caching_workers;
@@ -1120,6 +1133,27 @@ static inline void btrfs_wake_unfinished_drop(struct btrfs_fs_info *fs_info)
(unlikely(test_bit(BTRFS_FS_STATE_LOG_CLEANUP_ERROR, \
&(fs_info)->fs_state)))
+static inline bool btrfs_is_shutdown(struct btrfs_fs_info *fs_info)
+{
+ return test_bit(BTRFS_FS_STATE_EMERGENCY_SHUTDOWN, &fs_info->fs_state);
+}
+
+static inline void btrfs_force_shutdown(struct btrfs_fs_info *fs_info)
+{
+ /*
+ * Here we do not want to use handle_fs_error(), which will mark the fs
+ * read-only.
+ * Some call sites like shutdown ioctl will mark the fs shutdown when
+ * the fs is frozen. But thaw path will handle RO and RW fs
+ * differently.
+ *
+ * So here we only mark the fs error without flipping it RO.
+ */
+ WRITE_ONCE(fs_info->fs_error, -EIO);
+ if (!test_and_set_bit(BTRFS_FS_STATE_EMERGENCY_SHUTDOWN, &fs_info->fs_state))
+ btrfs_crit(fs_info, "emergency shutdown");
+}
+
/*
* We use folio flag owner_2 to indicate there is an ordered extent with
* unfinished IO.
diff --git a/fs/btrfs/inode-item.c b/fs/btrfs/inode-item.c
index 1bd73b80f9fa..b73e1dd97208 100644
--- a/fs/btrfs/inode-item.c
+++ b/fs/btrfs/inode-item.c
@@ -312,7 +312,7 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
if (!path)
return -ENOMEM;
- path->skip_release_on_error = 1;
+ path->skip_release_on_error = true;
ret = btrfs_insert_empty_item(trans, root, path, &key,
ins_len);
if (ret == -EEXIST) {
@@ -444,7 +444,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
struct btrfs_truncate_control *control)
{
struct btrfs_fs_info *fs_info = root->fs_info;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct extent_buffer *leaf;
struct btrfs_file_extent_item *fi;
struct btrfs_key key;
@@ -730,6 +730,5 @@ out:
if (!ret && control->last_size > new_size)
control->last_size = new_size;
- btrfs_free_path(path);
return ret;
}
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 9c6ca87b3d56..c4bee47829ed 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -72,6 +72,7 @@
#include "backref.h"
#include "raid-stripe-tree.h"
#include "fiemap.h"
+#include "delayed-inode.h"
#define COW_FILE_RANGE_KEEP_LOCKED (1UL << 0)
#define COW_FILE_RANGE_NO_INLINE (1UL << 1)
@@ -131,7 +132,7 @@ static int data_reloc_print_warning_inode(u64 inum, u64 offset, u64 num_bytes,
struct btrfs_fs_info *fs_info = warn->fs_info;
struct extent_buffer *eb;
struct btrfs_inode_item *inode_item;
- struct inode_fs_paths *ipath = NULL;
+ struct inode_fs_paths *ipath __free(inode_fs_paths) = NULL;
struct btrfs_root *local_root;
struct btrfs_key key;
unsigned int nofs_flag;
@@ -196,7 +197,6 @@ static int data_reloc_print_warning_inode(u64 inum, u64 offset, u64 num_bytes,
}
btrfs_put_root(local_root);
- free_ipath(ipath);
return 0;
err:
@@ -204,7 +204,6 @@ err:
"checksum error at logical %llu mirror %u root %llu inode %llu offset %llu, path resolving failed with ret=%d",
warn->logical, warn->mirror_num, root, inum, offset, ret);
- free_ipath(ipath);
return ret;
}
@@ -236,21 +235,21 @@ static void print_data_reloc_error(const struct btrfs_inode *inode, u64 file_off
if (logical == U64_MAX) {
btrfs_warn_rl(fs_info, "has data reloc tree but no running relocation");
btrfs_warn_rl(fs_info,
-"csum failed root %lld ino %llu off %llu csum " CSUM_FMT " expected csum " CSUM_FMT " mirror %d",
+"csum failed root %lld ino %llu off %llu csum " BTRFS_CSUM_FMT " expected csum " BTRFS_CSUM_FMT " mirror %d",
btrfs_root_id(inode->root), btrfs_ino(inode), file_off,
- CSUM_FMT_VALUE(csum_size, csum),
- CSUM_FMT_VALUE(csum_size, csum_expected),
+ BTRFS_CSUM_FMT_VALUE(csum_size, csum),
+ BTRFS_CSUM_FMT_VALUE(csum_size, csum_expected),
mirror_num);
return;
}
logical += file_off;
btrfs_warn_rl(fs_info,
-"csum failed root %lld ino %llu off %llu logical %llu csum " CSUM_FMT " expected csum " CSUM_FMT " mirror %d",
+"csum failed root %lld ino %llu off %llu logical %llu csum " BTRFS_CSUM_FMT " expected csum " BTRFS_CSUM_FMT " mirror %d",
btrfs_root_id(inode->root),
btrfs_ino(inode), file_off, logical,
- CSUM_FMT_VALUE(csum_size, csum),
- CSUM_FMT_VALUE(csum_size, csum_expected),
+ BTRFS_CSUM_FMT_VALUE(csum_size, csum),
+ BTRFS_CSUM_FMT_VALUE(csum_size, csum_expected),
mirror_num);
ret = extent_from_logical(fs_info, logical, &path, &found_key, &flags);
@@ -321,19 +320,19 @@ static void __cold btrfs_print_data_csum_error(struct btrfs_inode *inode,
/* Output without objectid, which is more meaningful */
if (btrfs_root_id(root) >= BTRFS_LAST_FREE_OBJECTID) {
btrfs_warn_rl(root->fs_info,
-"csum failed root %lld ino %lld off %llu csum " CSUM_FMT " expected csum " CSUM_FMT " mirror %d",
+"csum failed root %lld ino %lld off %llu csum " BTRFS_CSUM_FMT " expected csum " BTRFS_CSUM_FMT " mirror %d",
btrfs_root_id(root), btrfs_ino(inode),
logical_start,
- CSUM_FMT_VALUE(csum_size, csum),
- CSUM_FMT_VALUE(csum_size, csum_expected),
+ BTRFS_CSUM_FMT_VALUE(csum_size, csum),
+ BTRFS_CSUM_FMT_VALUE(csum_size, csum_expected),
mirror_num);
} else {
btrfs_warn_rl(root->fs_info,
-"csum failed root %llu ino %llu off %llu csum " CSUM_FMT " expected csum " CSUM_FMT " mirror %d",
+"csum failed root %llu ino %llu off %llu csum " BTRFS_CSUM_FMT " expected csum " BTRFS_CSUM_FMT " mirror %d",
btrfs_root_id(root), btrfs_ino(inode),
logical_start,
- CSUM_FMT_VALUE(csum_size, csum),
- CSUM_FMT_VALUE(csum_size, csum_expected),
+ BTRFS_CSUM_FMT_VALUE(csum_size, csum),
+ BTRFS_CSUM_FMT_VALUE(csum_size, csum_expected),
mirror_num);
}
}
@@ -594,6 +593,10 @@ static bool can_cow_file_range_inline(struct btrfs_inode *inode,
if (size < i_size_read(&inode->vfs_inode))
return false;
+ /* Encrypted file cannot be inlined. */
+ if (IS_ENCRYPTED(&inode->vfs_inode))
+ return false;
+
return true;
}
@@ -865,7 +868,7 @@ static void compress_file_range(struct btrfs_work *work)
u64 actual_end;
u64 i_size;
int ret = 0;
- struct folio **folios;
+ struct folio **folios = NULL;
unsigned long nr_folios;
unsigned long total_compressed = 0;
unsigned long total_in = 0;
@@ -874,6 +877,9 @@ static void compress_file_range(struct btrfs_work *work)
int compress_type = fs_info->compress_type;
int compress_level = fs_info->compress_level;
+ if (unlikely(btrfs_is_shutdown(fs_info)))
+ goto cleanup_and_bail_uncompressed;
+
inode_should_defrag(inode, start, end, end - start + 1, SZ_16K);
/*
@@ -1135,7 +1141,7 @@ static void submit_one_async_extent(struct async_chunk *async_chunk,
ret = btrfs_reserve_extent(root, async_extent->ram_size,
async_extent->compressed_size,
async_extent->compressed_size,
- 0, *alloc_hint, &ins, 1, 1);
+ 0, *alloc_hint, &ins, true, true);
if (ret) {
/*
* We can't reserve contiguous space for the compressed size.
@@ -1289,6 +1295,11 @@ static noinline int cow_file_range(struct btrfs_inode *inode,
unsigned long page_ops;
int ret = 0;
+ if (unlikely(btrfs_is_shutdown(fs_info))) {
+ ret = -EIO;
+ goto out_unlock;
+ }
+
if (btrfs_is_free_space_inode(inode)) {
ret = -EINVAL;
goto out_unlock;
@@ -1353,7 +1364,7 @@ static noinline int cow_file_range(struct btrfs_inode *inode,
ret = btrfs_reserve_extent(root, num_bytes, num_bytes,
min_alloc_size, 0, alloc_hint,
- &ins, 1, 1);
+ &ins, true, true);
if (ret == -EAGAIN) {
/*
* btrfs_reserve_extent only returns -EAGAIN for zoned
@@ -2007,7 +2018,7 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
{
struct btrfs_fs_info *fs_info = inode->root->fs_info;
struct btrfs_root *root = inode->root;
- struct btrfs_path *path;
+ struct btrfs_path *path = NULL;
u64 cow_start = (u64)-1;
/*
* If not 0, represents the inclusive end of the last fallback_to_cow()
@@ -2037,6 +2048,10 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
*/
ASSERT(!btrfs_is_zoned(fs_info) || btrfs_is_data_reloc_root(root));
+ if (unlikely(btrfs_is_shutdown(fs_info))) {
+ ret = -EIO;
+ goto error;
+ }
path = btrfs_alloc_path();
if (!path) {
ret = -ENOMEM;
@@ -3334,36 +3349,67 @@ int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered)
return btrfs_finish_one_ordered(ordered);
}
-void btrfs_calculate_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr,
- u8 *dest)
+/*
+ * Calculate the checksum of an fs block at physical memory address @paddr,
+ * and save the result to @dest.
+ *
+ * The folio containing @paddr must be large enough to contain a full fs block.
+ */
+void btrfs_calculate_block_csum_folio(struct btrfs_fs_info *fs_info,
+ const phys_addr_t paddr, u8 *dest)
{
struct folio *folio = page_folio(phys_to_page(paddr));
const u32 blocksize = fs_info->sectorsize;
- SHASH_DESC_ON_STACK(shash, fs_info->csum_shash);
+ const u32 step = min(blocksize, PAGE_SIZE);
+ const u32 nr_steps = blocksize / step;
+ phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
- shash->tfm = fs_info->csum_shash;
/* The full block must be inside the folio. */
ASSERT(offset_in_folio(folio, paddr) + blocksize <= folio_size(folio));
- if (folio_test_partial_kmap(folio)) {
- size_t cur = paddr;
+ for (int i = 0; i < nr_steps; i++) {
+ u32 pindex = offset_in_folio(folio, paddr + i * step) >> PAGE_SHIFT;
- crypto_shash_init(shash);
- while (cur < paddr + blocksize) {
- void *kaddr;
- size_t len = min(paddr + blocksize - cur,
- PAGE_SIZE - offset_in_page(cur));
+ /*
+ * For bs <= ps cases, we will only run the loop once, so the offset
+ * inside the page will only added to paddrs[0].
+ *
+ * For bs > ps cases, the block must be page aligned, thus offset
+ * inside the page will always be 0.
+ */
+ paddrs[i] = page_to_phys(folio_page(folio, pindex)) + offset_in_page(paddr);
+ }
+ return btrfs_calculate_block_csum_pages(fs_info, paddrs, dest);
+}
- kaddr = kmap_local_folio(folio, offset_in_folio(folio, cur));
- crypto_shash_update(shash, kaddr, len);
- kunmap_local(kaddr);
- cur += len;
- }
- crypto_shash_final(shash, dest);
- } else {
- crypto_shash_digest(shash, phys_to_virt(paddr), blocksize, dest);
+/*
+ * Calculate the checksum of a fs block backed by multiple noncontiguous pages
+ * at @paddrs[] and save the result to @dest.
+ *
+ * The folio containing @paddr must be large enough to contain a full fs block.
+ */
+void btrfs_calculate_block_csum_pages(struct btrfs_fs_info *fs_info,
+ const phys_addr_t paddrs[], u8 *dest)
+{
+ const u32 blocksize = fs_info->sectorsize;
+ const u32 step = min(blocksize, PAGE_SIZE);
+ const u32 nr_steps = blocksize / step;
+ SHASH_DESC_ON_STACK(shash, fs_info->csum_shash);
+
+ shash->tfm = fs_info->csum_shash;
+ crypto_shash_init(shash);
+ for (int i = 0; i < nr_steps; i++) {
+ const phys_addr_t paddr = paddrs[i];
+ void *kaddr;
+
+ ASSERT(offset_in_page(paddr) + step <= PAGE_SIZE);
+ kaddr = kmap_local_page(phys_to_page(paddr)) + offset_in_page(paddr);
+ crypto_shash_update(shash, kaddr, step);
+ kunmap_local(kaddr);
}
+ crypto_shash_final(shash, dest);
}
+
/*
* Verify the checksum for a single sector without any extra action that depend
* on the type of I/O.
@@ -3373,19 +3419,20 @@ void btrfs_calculate_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr
int btrfs_check_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr, u8 *csum,
const u8 * const csum_expected)
{
- btrfs_calculate_block_csum(fs_info, paddr, csum);
+ btrfs_calculate_block_csum_folio(fs_info, paddr, csum);
if (unlikely(memcmp(csum, csum_expected, fs_info->csum_size) != 0))
return -EIO;
return 0;
}
/*
- * Verify the checksum of a single data sector.
+ * Verify the checksum of a single data sector, which can be scattered at
+ * different noncontiguous pages.
*
* @bbio: btrfs_io_bio which contains the csum
* @dev: device the sector is on
* @bio_offset: offset to the beginning of the bio (in bytes)
- * @bv: bio_vec to check
+ * @paddrs: physical addresses which back the fs block
*
* Check if the checksum on a data block is valid. When a checksum mismatch is
* detected, report the error and fill the corrupted range with zero.
@@ -3393,12 +3440,13 @@ int btrfs_check_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr, u8
* Return %true if the sector is ok or had no checksum to start with, else %false.
*/
bool btrfs_data_csum_ok(struct btrfs_bio *bbio, struct btrfs_device *dev,
- u32 bio_offset, phys_addr_t paddr)
+ u32 bio_offset, const phys_addr_t paddrs[])
{
struct btrfs_inode *inode = bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
const u32 blocksize = fs_info->sectorsize;
- struct folio *folio;
+ const u32 step = min(blocksize, PAGE_SIZE);
+ const u32 nr_steps = blocksize / step;
u64 file_offset = bbio->file_offset + bio_offset;
u64 end = file_offset + blocksize - 1;
u8 *csum_expected;
@@ -3418,7 +3466,8 @@ bool btrfs_data_csum_ok(struct btrfs_bio *bbio, struct btrfs_device *dev,
csum_expected = bbio->csum + (bio_offset >> fs_info->sectorsize_bits) *
fs_info->csum_size;
- if (btrfs_check_block_csum(fs_info, paddr, csum, csum_expected))
+ btrfs_calculate_block_csum_pages(fs_info, paddrs, csum);
+ if (unlikely(memcmp(csum, csum_expected, fs_info->csum_size) != 0))
goto zeroit;
return true;
@@ -3427,9 +3476,8 @@ zeroit:
bbio->mirror_num);
if (dev)
btrfs_dev_stat_inc_and_print(dev, BTRFS_DEV_STAT_CORRUPTION_ERRS);
- folio = page_folio(phys_to_page(paddr));
- ASSERT(offset_in_folio(folio, paddr) + blocksize <= folio_size(folio));
- folio_zero_range(folio, offset_in_folio(folio, paddr), blocksize);
+ for (int i = 0; i < nr_steps; i++)
+ memzero_page(phys_to_page(paddrs[i]), offset_in_page(paddrs[i]), step);
return false;
}
@@ -4316,8 +4364,8 @@ skip_backref:
* operations on the log tree, increasing latency for applications.
*/
if (!rename_ctx) {
- btrfs_del_inode_ref_in_log(trans, root, name, inode, dir_ino);
- btrfs_del_dir_entries_in_log(trans, root, name, dir, index);
+ btrfs_del_inode_ref_in_log(trans, name, inode, dir);
+ btrfs_del_dir_entries_in_log(trans, name, dir, index);
}
/*
@@ -4416,7 +4464,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
{
struct btrfs_root *root = dir->root;
struct btrfs_inode *inode = BTRFS_I(d_inode(dentry));
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct extent_buffer *leaf;
struct btrfs_dir_item *di;
struct btrfs_key key;
@@ -4509,7 +4557,6 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
if (ret)
btrfs_abort_transaction(trans, ret);
out:
- btrfs_free_path(path);
fscrypt_free_filename(&fname);
return ret;
}
@@ -5634,9 +5681,9 @@ static int btrfs_inode_by_name(struct btrfs_inode *dir, struct dentry *dentry,
location->type != BTRFS_ROOT_ITEM_KEY)) {
ret = -EUCLEAN;
btrfs_warn(root->fs_info,
-"%s gets something invalid in DIR_ITEM (name %s, directory ino %llu, location(%llu %u %llu))",
+"%s gets something invalid in DIR_ITEM (name %s, directory ino %llu, location " BTRFS_KEY_FMT ")",
__func__, fname.disk_name.name, btrfs_ino(dir),
- location->objectid, location->type, location->offset);
+ BTRFS_KEY_FMT_VALUE(location));
}
if (!ret)
*type = btrfs_dir_ftype(path->nodes[0], di);
@@ -7074,8 +7121,8 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode,
* point the commit_root has everything we need.
*/
if (btrfs_is_free_space_inode(inode)) {
- path->search_commit_root = 1;
- path->skip_locking = 1;
+ path->search_commit_root = true;
+ path->skip_locking = true;
}
ret = btrfs_lookup_file_extent(NULL, root, path, objectid, start, 0);
@@ -7585,11 +7632,11 @@ static void btrfs_invalidate_folio(struct folio *folio, size_t offset,
EXTENT_LOCKED | EXTENT_DO_ACCOUNTING |
EXTENT_DEFRAG, &cached_state);
- spin_lock_irq(&inode->ordered_tree_lock);
+ spin_lock(&inode->ordered_tree_lock);
set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags);
ordered->truncated_len = min(ordered->truncated_len,
cur - ordered->file_offset);
- spin_unlock_irq(&inode->ordered_tree_lock);
+ spin_unlock(&inode->ordered_tree_lock);
/*
* If the ordered extent has finished, we're safe to delete all
@@ -7651,19 +7698,22 @@ static int btrfs_truncate(struct btrfs_inode *inode, bool skip_writeback)
.ino = btrfs_ino(inode),
.min_type = BTRFS_EXTENT_DATA_KEY,
.clear_extent_range = true,
+ .new_size = inode->vfs_inode.i_size,
};
struct btrfs_root *root = inode->root;
struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_block_rsv rsv;
int ret;
struct btrfs_trans_handle *trans;
- u64 mask = fs_info->sectorsize - 1;
const u64 min_size = btrfs_calc_metadata_size(fs_info, 1);
+ const u64 lock_start = round_down(inode->vfs_inode.i_size, fs_info->sectorsize);
+ const u64 i_size_up = round_up(inode->vfs_inode.i_size, fs_info->sectorsize);
+
+ /* Our inode is locked and the i_size can't be changed concurrently. */
+ btrfs_assert_inode_locked(inode);
if (!skip_writeback) {
- ret = btrfs_wait_ordered_range(inode,
- inode->vfs_inode.i_size & (~mask),
- (u64)-1);
+ ret = btrfs_wait_ordered_range(inode, lock_start, (u64)-1);
if (ret)
return ret;
}
@@ -7727,19 +7777,14 @@ static int btrfs_truncate(struct btrfs_inode *inode, bool skip_writeback)
while (1) {
struct extent_state *cached_state = NULL;
- const u64 new_size = inode->vfs_inode.i_size;
- const u64 lock_start = ALIGN_DOWN(new_size, fs_info->sectorsize);
- control.new_size = new_size;
btrfs_lock_extent(&inode->io_tree, lock_start, (u64)-1, &cached_state);
/*
* We want to drop from the next block forward in case this new
* size is not block aligned since we will be keeping the last
* block of the extent just the way it is.
*/
- btrfs_drop_extent_map_range(inode,
- ALIGN(new_size, fs_info->sectorsize),
- (u64)-1, false);
+ btrfs_drop_extent_map_range(inode, i_size_up, (u64)-1, false);
ret = btrfs_truncate_inode_items(trans, root, &control);
@@ -9053,7 +9098,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
*/
cur_bytes = min(cur_bytes, last_alloc);
ret = btrfs_reserve_extent(root, cur_bytes, cur_bytes,
- min_size, 0, *alloc_hint, &ins, 1, 0);
+ min_size, 0, *alloc_hint, &ins, true, false);
if (ret)
break;
@@ -9389,7 +9434,6 @@ int btrfs_encoded_read_regular_fill_pages(struct btrfs_inode *inode,
u64 disk_bytenr, u64 disk_io_size,
struct page **pages, void *uring_ctx)
{
- struct btrfs_fs_info *fs_info = inode->root->fs_info;
struct btrfs_encoded_read_private *priv, sync_priv;
struct completion sync_reads;
unsigned long i = 0;
@@ -9414,10 +9458,9 @@ int btrfs_encoded_read_regular_fill_pages(struct btrfs_inode *inode,
priv->status = 0;
priv->uring_ctx = uring_ctx;
- bbio = btrfs_bio_alloc(BIO_MAX_VECS, REQ_OP_READ, fs_info,
+ bbio = btrfs_bio_alloc(BIO_MAX_VECS, REQ_OP_READ, inode, 0,
btrfs_encoded_read_endio, priv);
bbio->bio.bi_iter.bi_sector = disk_bytenr >> SECTOR_SHIFT;
- bbio->inode = inode;
do {
size_t bytes = min_t(u64, disk_io_size, PAGE_SIZE);
@@ -9426,10 +9469,9 @@ int btrfs_encoded_read_regular_fill_pages(struct btrfs_inode *inode,
refcount_inc(&priv->pending_refs);
btrfs_submit_bbio(bbio, 0);
- bbio = btrfs_bio_alloc(BIO_MAX_VECS, REQ_OP_READ, fs_info,
+ bbio = btrfs_bio_alloc(BIO_MAX_VECS, REQ_OP_READ, inode, 0,
btrfs_encoded_read_endio, priv);
bbio->bio.bi_iter.bi_sector = disk_bytenr >> SECTOR_SHIFT;
- bbio->inode = inode;
continue;
}
@@ -9820,8 +9862,6 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
}
for (;;) {
- struct btrfs_ordered_extent *ordered;
-
ret = btrfs_wait_ordered_range(inode, start, num_bytes);
if (ret)
goto out_folios;
@@ -9871,7 +9911,7 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
}
ret = btrfs_reserve_extent(root, disk_num_bytes, disk_num_bytes,
- disk_num_bytes, 0, 0, &ins, 1, 1);
+ disk_num_bytes, 0, 0, &ins, true, true);
if (ret)
goto out_delalloc_release;
extent_reserved = true;
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index b138120feba3..acb484546b1d 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -503,7 +503,7 @@ static noinline int create_subvol(struct mnt_idmap *idmap,
struct btrfs_fs_info *fs_info = inode_to_fs_info(dir);
struct btrfs_trans_handle *trans;
struct btrfs_key key;
- struct btrfs_root_item *root_item;
+ struct btrfs_root_item AUTO_KFREE(root_item);
struct btrfs_inode_item *inode_item;
struct extent_buffer *leaf;
struct btrfs_root *root = BTRFS_I(dir)->root;
@@ -527,20 +527,18 @@ static noinline int create_subvol(struct mnt_idmap *idmap,
ret = btrfs_get_free_objectid(fs_info->tree_root, &objectid);
if (ret)
- goto out_root_item;
+ return ret;
/*
* Don't create subvolume whose level is not zero. Or qgroup will be
* screwed up since it assumes subvolume qgroup's level to be 0.
*/
- if (btrfs_qgroup_level(objectid)) {
- ret = -ENOSPC;
- goto out_root_item;
- }
+ if (btrfs_qgroup_level(objectid))
+ return -ENOSPC;
ret = get_anon_bdev(&anon_dev);
if (ret < 0)
- goto out_root_item;
+ return ret;
new_inode_args.inode = btrfs_new_subvol_inode(idmap, dir);
if (!new_inode_args.inode) {
@@ -692,8 +690,7 @@ out_inode:
out_anon_dev:
if (anon_dev)
free_anon_bdev(anon_dev);
-out_root_item:
- kfree(root_item);
+
return ret;
}
@@ -1599,7 +1596,7 @@ static noinline int search_ioctl(struct btrfs_root *root,
{
struct btrfs_fs_info *info = root->fs_info;
struct btrfs_key key;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
int ret;
int num_found = 0;
unsigned long sk_offset = 0;
@@ -1619,10 +1616,8 @@ static noinline int search_ioctl(struct btrfs_root *root,
} else {
/* Look up the root from the arguments. */
root = btrfs_get_fs_root(info, sk->tree_id, true);
- if (IS_ERR(root)) {
- btrfs_free_path(path);
+ if (IS_ERR(root))
return PTR_ERR(root);
- }
}
key.objectid = sk->min_objectid;
@@ -1656,7 +1651,6 @@ static noinline int search_ioctl(struct btrfs_root *root,
sk->nr_items = num_found;
btrfs_put_root(root);
- btrfs_free_path(path);
return ret;
}
@@ -1739,7 +1733,7 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info,
int total_len = 0;
struct btrfs_inode_ref *iref;
struct extent_buffer *l;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
if (dirid == BTRFS_FIRST_FREE_OBJECTID) {
name[0]='\0';
@@ -1800,7 +1794,6 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info,
ret = 0;
out:
btrfs_put_root(root);
- btrfs_free_path(path);
return ret;
}
@@ -1817,8 +1810,8 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap,
struct btrfs_inode_ref *iref;
struct btrfs_root_ref *rref;
struct btrfs_root *root = NULL;
- struct btrfs_path *path;
- struct btrfs_key key, key2;
+ BTRFS_PATH_AUTO_FREE(path);
+ struct btrfs_key key;
struct extent_buffer *leaf;
char *ptr;
int slot;
@@ -1838,10 +1831,8 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap,
ptr = &args->path[BTRFS_INO_LOOKUP_USER_PATH_MAX - 1];
root = btrfs_get_fs_root(fs_info, treeid, true);
- if (IS_ERR(root)) {
- ret = PTR_ERR(root);
- goto out;
- }
+ if (IS_ERR(root))
+ return PTR_ERR(root);
key.objectid = dirid;
key.type = BTRFS_INODE_REF_KEY;
@@ -1873,24 +1864,6 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap,
read_extent_buffer(leaf, ptr,
(unsigned long)(iref + 1), len);
- /* Check the read+exec permission of this directory */
- ret = btrfs_previous_item(root, path, dirid,
- BTRFS_INODE_ITEM_KEY);
- if (ret < 0) {
- goto out_put;
- } else if (ret > 0) {
- ret = -ENOENT;
- goto out_put;
- }
-
- leaf = path->nodes[0];
- slot = path->slots[0];
- btrfs_item_key_to_cpu(leaf, &key2, slot);
- if (key2.objectid != dirid) {
- ret = -ENOENT;
- goto out_put;
- }
-
/*
* We don't need the path anymore, so release it and
* avoid deadlocks and lockdep warnings in case
@@ -1898,18 +1871,17 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap,
* btree and lock the same leaf.
*/
btrfs_release_path(path);
- temp_inode = btrfs_iget(key2.objectid, root);
+ temp_inode = btrfs_iget(key.offset, root);
if (IS_ERR(temp_inode)) {
ret = PTR_ERR(temp_inode);
goto out_put;
}
+ /* Check the read+exec permission of this directory. */
ret = inode_permission(idmap, &temp_inode->vfs_inode,
MAY_READ | MAY_EXEC);
iput(&temp_inode->vfs_inode);
- if (ret) {
- ret = -EACCES;
+ if (ret)
goto out_put;
- }
if (key.offset == upper_limit)
break;
@@ -1935,12 +1907,10 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap,
key.type = BTRFS_ROOT_REF_KEY;
key.offset = args->treeid;
ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, path, 0, 0);
- if (ret < 0) {
- goto out;
- } else if (ret > 0) {
- ret = -ENOENT;
- goto out;
- }
+ if (ret < 0)
+ return ret;
+ else if (ret > 0)
+ return -ENOENT;
leaf = path->nodes[0];
slot = path->slots[0];
@@ -1950,10 +1920,8 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap,
item_len = btrfs_item_size(leaf, slot);
/* Check if dirid in ROOT_REF corresponds to passed dirid */
rref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref);
- if (args->dirid != btrfs_root_ref_dirid(leaf, rref)) {
- ret = -EINVAL;
- goto out;
- }
+ if (args->dirid != btrfs_root_ref_dirid(leaf, rref))
+ return -EINVAL;
/* Copy subvolume's name */
item_off += sizeof(struct btrfs_root_ref);
@@ -1963,8 +1931,7 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap,
out_put:
btrfs_put_root(root);
-out:
- btrfs_free_path(path);
+
return ret;
}
@@ -2939,7 +2906,7 @@ static long btrfs_ioctl_space_info(struct btrfs_fs_info *fs_info,
struct btrfs_ioctl_space_args space_args = { 0 };
struct btrfs_ioctl_space_info space;
struct btrfs_ioctl_space_info *dest;
- struct btrfs_ioctl_space_info *dest_orig;
+ struct btrfs_ioctl_space_info AUTO_KFREE(dest_orig);
struct btrfs_ioctl_space_info __user *user_dest;
struct btrfs_space_info *info;
static const u64 types[] = {
@@ -3060,9 +3027,8 @@ static long btrfs_ioctl_space_info(struct btrfs_fs_info *fs_info,
(arg + sizeof(struct btrfs_ioctl_space_args));
if (copy_to_user(user_dest, dest_orig, alloc_size))
- ret = -EFAULT;
+ return -EFAULT;
- kfree(dest_orig);
out:
if (ret == 0 && copy_to_user(arg, &space_args, sizeof(space_args)))
ret = -EFAULT;
@@ -3281,7 +3247,7 @@ static long btrfs_ioctl_ino_to_path(struct btrfs_root *root, void __user *arg)
u64 rel_ptr;
int size;
struct btrfs_ioctl_ino_path_args *ipa = NULL;
- struct inode_fs_paths *ipath = NULL;
+ struct inode_fs_paths *ipath __free(inode_fs_paths) = NULL;
struct btrfs_path *path;
if (!capable(CAP_DAC_READ_SEARCH))
@@ -3329,7 +3295,6 @@ static long btrfs_ioctl_ino_to_path(struct btrfs_root *root, void __user *arg)
out:
btrfs_free_path(path);
- free_ipath(ipath);
kfree(ipa);
return ret;
@@ -3594,7 +3559,7 @@ static long btrfs_ioctl_balance_ctl(struct btrfs_fs_info *fs_info, int cmd)
static long btrfs_ioctl_balance_progress(struct btrfs_fs_info *fs_info,
void __user *arg)
{
- struct btrfs_ioctl_balance_args *bargs;
+ struct btrfs_ioctl_balance_args AUTO_KFREE(bargs);
int ret = 0;
if (!capable(CAP_SYS_ADMIN))
@@ -3616,8 +3581,6 @@ static long btrfs_ioctl_balance_progress(struct btrfs_fs_info *fs_info,
if (copy_to_user(arg, bargs, sizeof(*bargs)))
ret = -EFAULT;
-
- kfree(bargs);
out:
mutex_unlock(&fs_info->balance_mutex);
return ret;
@@ -4211,7 +4174,7 @@ static int check_feature_bits(const struct btrfs_fs_info *fs_info,
u64 safe_set, u64 safe_clear)
{
const char *type = btrfs_feature_set_name(set);
- char *names;
+ const char AUTO_KFREE(names);
u64 disallowed, unsupported;
u64 set_mask = flags & change_mask;
u64 clear_mask = ~flags & change_mask;
@@ -4219,12 +4182,11 @@ static int check_feature_bits(const struct btrfs_fs_info *fs_info,
unsupported = set_mask & ~supported_flags;
if (unsupported) {
names = btrfs_printable_features(set, unsupported);
- if (names) {
+ if (names)
btrfs_warn(fs_info,
"this kernel does not support the %s feature bit%s",
names, strchr(names, ',') ? "s" : "");
- kfree(names);
- } else
+ else
btrfs_warn(fs_info,
"this kernel does not support %s bits 0x%llx",
type, unsupported);
@@ -4234,12 +4196,11 @@ static int check_feature_bits(const struct btrfs_fs_info *fs_info,
disallowed = set_mask & ~safe_set;
if (disallowed) {
names = btrfs_printable_features(set, disallowed);
- if (names) {
+ if (names)
btrfs_warn(fs_info,
"can't set the %s feature bit%s while mounted",
names, strchr(names, ',') ? "s" : "");
- kfree(names);
- } else
+ else
btrfs_warn(fs_info,
"can't set %s bits 0x%llx while mounted",
type, disallowed);
@@ -4249,12 +4210,11 @@ static int check_feature_bits(const struct btrfs_fs_info *fs_info,
disallowed = clear_mask & ~safe_clear;
if (disallowed) {
names = btrfs_printable_features(set, disallowed);
- if (names) {
+ if (names)
btrfs_warn(fs_info,
"can't clear the %s feature bit%s while mounted",
names, strchr(names, ',') ? "s" : "");
- kfree(names);
- } else
+ else
btrfs_warn(fs_info,
"can't clear %s bits 0x%llx while mounted",
type, disallowed);
@@ -4401,10 +4361,6 @@ static int btrfs_ioctl_encoded_read(struct file *file, void __user *argp,
goto out_acct;
}
- if (fs_info->sectorsize > PAGE_SIZE) {
- ret = -ENOTTY;
- goto out_acct;
- }
if (compat) {
#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT)
struct btrfs_ioctl_encoded_io_args_32 args32;
@@ -4496,7 +4452,6 @@ out_acct:
static int btrfs_ioctl_encoded_write(struct file *file, void __user *argp, bool compat)
{
- struct btrfs_fs_info *fs_info = inode_to_fs_info(file->f_inode);
struct btrfs_ioctl_encoded_io_args args;
struct iovec iovstack[UIO_FASTIOV];
struct iovec *iov = iovstack;
@@ -4510,11 +4465,6 @@ static int btrfs_ioctl_encoded_write(struct file *file, void __user *argp, bool
goto out_acct;
}
- if (fs_info->sectorsize > PAGE_SIZE) {
- ret = -ENOTTY;
- goto out_acct;
- }
-
if (!(file->f_mode & FMODE_WRITE)) {
ret = -EBADF;
goto out_acct;
@@ -4632,8 +4582,9 @@ struct io_btrfs_cmd {
struct btrfs_uring_priv *priv;
};
-static void btrfs_uring_read_finished(struct io_uring_cmd *cmd, unsigned int issue_flags)
+static void btrfs_uring_read_finished(struct io_tw_req tw_req, io_tw_token_t tw)
{
+ struct io_uring_cmd *cmd = io_uring_cmd_from_tw(tw_req);
struct io_btrfs_cmd *bc = io_uring_cmd_to_pdu(cmd, struct io_btrfs_cmd);
struct btrfs_uring_priv *priv = bc->priv;
struct btrfs_inode *inode = BTRFS_I(file_inode(priv->iocb.ki_filp));
@@ -4678,7 +4629,7 @@ out:
btrfs_unlock_extent(io_tree, priv->start, priv->lockend, &priv->cached_state);
btrfs_inode_unlock(inode, BTRFS_ILOCK_SHARED);
- io_uring_cmd_done(cmd, ret, issue_flags);
+ io_uring_cmd_done(cmd, ret, IO_URING_CMD_TASK_WORK_ISSUE_FLAGS);
add_rchar(current, ret);
for (index = 0; index < priv->nr_pages; index++)
@@ -4796,11 +4747,6 @@ static int btrfs_uring_encoded_read(struct io_uring_cmd *cmd, unsigned int issue
ret = -EPERM;
goto out_acct;
}
- if (fs_info->sectorsize > PAGE_SIZE) {
- ret = -ENOTTY;
- goto out_acct;
- }
-
sqe_addr = u64_to_user_ptr(READ_ONCE(cmd->sqe->addr));
if (issue_flags & IO_URING_F_COMPAT) {
@@ -4928,7 +4874,6 @@ out_acct:
static int btrfs_uring_encoded_write(struct io_uring_cmd *cmd, unsigned int issue_flags)
{
struct file *file = cmd->file;
- struct btrfs_fs_info *fs_info = inode_to_fs_info(file->f_inode);
loff_t pos;
struct kiocb kiocb;
ssize_t ret;
@@ -4943,11 +4888,6 @@ static int btrfs_uring_encoded_write(struct io_uring_cmd *cmd, unsigned int issu
ret = -EPERM;
goto out_acct;
}
- if (fs_info->sectorsize > PAGE_SIZE) {
- ret = -ENOTTY;
- goto out_acct;
- }
-
sqe_addr = u64_to_user_ptr(READ_ONCE(cmd->sqe->addr));
if (!(file->f_mode & FMODE_WRITE)) {
@@ -5060,6 +5000,9 @@ out_acct:
int btrfs_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags)
{
+ if (unlikely(btrfs_is_shutdown(inode_to_fs_info(file_inode(cmd->file)))))
+ return -EIO;
+
switch (cmd->cmd_op) {
case BTRFS_IOC_ENCODED_READ:
#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT)
@@ -5203,6 +5146,43 @@ static int btrfs_ioctl_subvol_sync(struct btrfs_fs_info *fs_info, void __user *a
return 0;
}
+#ifdef CONFIG_BTRFS_EXPERIMENTAL
+static int btrfs_ioctl_shutdown(struct btrfs_fs_info *fs_info, unsigned long arg)
+{
+ int ret = 0;
+ u32 flags;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (get_user(flags, (u32 __user *)arg))
+ return -EFAULT;
+
+ if (flags >= BTRFS_SHUTDOWN_FLAGS_LAST)
+ return -EINVAL;
+
+ if (btrfs_is_shutdown(fs_info))
+ return 0;
+
+ switch (flags) {
+ case BTRFS_SHUTDOWN_FLAGS_LOGFLUSH:
+ case BTRFS_SHUTDOWN_FLAGS_DEFAULT:
+ ret = freeze_super(fs_info->sb, FREEZE_HOLDER_KERNEL, NULL);
+ if (ret)
+ return ret;
+ btrfs_force_shutdown(fs_info);
+ ret = thaw_super(fs_info->sb, FREEZE_HOLDER_KERNEL, NULL);
+ if (ret)
+ return ret;
+ break;
+ case BTRFS_SHUTDOWN_FLAGS_NOLOGFLUSH:
+ btrfs_force_shutdown(fs_info);
+ break;
+ }
+ return ret;
+}
+#endif
+
long btrfs_ioctl(struct file *file, unsigned int
cmd, unsigned long arg)
{
@@ -5358,6 +5338,10 @@ long btrfs_ioctl(struct file *file, unsigned int
#endif
case BTRFS_IOC_SUBVOL_SYNC_WAIT:
return btrfs_ioctl_subvol_sync(fs_info, argp);
+#ifdef CONFIG_BTRFS_EXPERIMENTAL
+ case BTRFS_IOC_SHUTDOWN:
+ return btrfs_ioctl_shutdown(fs_info, arg);
+#endif
}
return -ENOTTY;
diff --git a/fs/btrfs/messages.c b/fs/btrfs/messages.c
index a0cf8effe008..2f853de44473 100644
--- a/fs/btrfs/messages.c
+++ b/fs/btrfs/messages.c
@@ -24,6 +24,7 @@ static const char fs_state_chars[] = {
[BTRFS_FS_STATE_NO_DATA_CSUMS] = 'C',
[BTRFS_FS_STATE_SKIP_META_CSUMS] = 'S',
[BTRFS_FS_STATE_LOG_CLEANUP_ERROR] = 'L',
+ [BTRFS_FS_STATE_EMERGENCY_SHUTDOWN] = 'E',
};
static void btrfs_state_to_string(const struct btrfs_fs_info *info, char *buf)
diff --git a/fs/btrfs/messages.h b/fs/btrfs/messages.h
index 4416c165644f..d8c0bd17dcda 100644
--- a/fs/btrfs/messages.h
+++ b/fs/btrfs/messages.h
@@ -168,7 +168,8 @@ do { \
#endif
#else
-#define ASSERT(cond, args...) (void)(cond)
+/* Compile check the @cond expression but don't generate any code. */
+#define ASSERT(cond, args...) BUILD_BUG_ON_INVALID(cond)
#endif
#ifdef CONFIG_BTRFS_DEBUG
diff --git a/fs/btrfs/misc.h b/fs/btrfs/misc.h
index 17b71e1285e5..12c5a9d6564f 100644
--- a/fs/btrfs/misc.h
+++ b/fs/btrfs/misc.h
@@ -14,6 +14,13 @@
#include <linux/bio.h>
/*
+ * Convenience macros to define a pointer with the __free(kfree) and
+ * __free(kvfree) cleanup attributes and initialized to NULL.
+ */
+#define AUTO_KFREE(name) *name __free(kfree) = NULL
+#define AUTO_KVFREE(name) *name __free(kvfree) = NULL
+
+/*
* Enumerate bits using enum autoincrement. Define the @name as the n-th bit.
*/
#define ENUM_BIT(name) \
diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c
index 7fedebbee558..5df02c707aee 100644
--- a/fs/btrfs/ordered-data.c
+++ b/fs/btrfs/ordered-data.c
@@ -237,14 +237,14 @@ static void insert_ordered_extent(struct btrfs_ordered_extent *entry)
/* One ref for the tree. */
refcount_inc(&entry->refs);
- spin_lock_irq(&inode->ordered_tree_lock);
+ spin_lock(&inode->ordered_tree_lock);
node = tree_insert(&inode->ordered_tree, entry->file_offset,
&entry->rb_node);
if (unlikely(node))
btrfs_panic(fs_info, -EEXIST,
"inconsistency in ordered tree at offset %llu",
entry->file_offset);
- spin_unlock_irq(&inode->ordered_tree_lock);
+ spin_unlock(&inode->ordered_tree_lock);
spin_lock(&root->ordered_extent_lock);
list_add_tail(&entry->root_extent_list,
@@ -328,9 +328,9 @@ void btrfs_add_ordered_sum(struct btrfs_ordered_extent *entry,
{
struct btrfs_inode *inode = entry->inode;
- spin_lock_irq(&inode->ordered_tree_lock);
+ spin_lock(&inode->ordered_tree_lock);
list_add_tail(&sum->list, &entry->list);
- spin_unlock_irq(&inode->ordered_tree_lock);
+ spin_unlock(&inode->ordered_tree_lock);
}
void btrfs_mark_ordered_extent_error(struct btrfs_ordered_extent *ordered)
@@ -417,15 +417,14 @@ void btrfs_finish_ordered_extent(struct btrfs_ordered_extent *ordered,
bool uptodate)
{
struct btrfs_inode *inode = ordered->inode;
- unsigned long flags;
bool ret;
trace_btrfs_finish_ordered_extent(inode, file_offset, len, uptodate);
- spin_lock_irqsave(&inode->ordered_tree_lock, flags);
+ spin_lock(&inode->ordered_tree_lock);
ret = can_finish_ordered_extent(ordered, folio, file_offset, len,
uptodate);
- spin_unlock_irqrestore(&inode->ordered_tree_lock, flags);
+ spin_unlock(&inode->ordered_tree_lock);
/*
* If this is a COW write it means we created new extent maps for the
@@ -481,18 +480,16 @@ void btrfs_mark_ordered_io_finished(struct btrfs_inode *inode,
{
struct rb_node *node;
struct btrfs_ordered_extent *entry = NULL;
- unsigned long flags;
u64 cur = file_offset;
+ const u64 end = file_offset + num_bytes;
- trace_btrfs_writepage_end_io_hook(inode, file_offset,
- file_offset + num_bytes - 1,
- uptodate);
+ trace_btrfs_writepage_end_io_hook(inode, file_offset, end - 1, uptodate);
- spin_lock_irqsave(&inode->ordered_tree_lock, flags);
- while (cur < file_offset + num_bytes) {
+ spin_lock(&inode->ordered_tree_lock);
+ while (cur < end) {
u64 entry_end;
- u64 end;
- u32 len;
+ u64 this_end;
+ u64 len;
node = ordered_tree_search(inode, cur);
/* No ordered extents at all */
@@ -535,19 +532,18 @@ void btrfs_mark_ordered_io_finished(struct btrfs_inode *inode,
* |
* cur
*/
- end = min(entry->file_offset + entry->num_bytes,
- file_offset + num_bytes) - 1;
- ASSERT(end + 1 - cur < U32_MAX);
- len = end + 1 - cur;
+ this_end = min(entry_end, end);
+ len = this_end - cur;
+ ASSERT(len < U32_MAX);
if (can_finish_ordered_extent(entry, folio, cur, len, uptodate)) {
- spin_unlock_irqrestore(&inode->ordered_tree_lock, flags);
+ spin_unlock(&inode->ordered_tree_lock);
btrfs_queue_ordered_fn(entry);
- spin_lock_irqsave(&inode->ordered_tree_lock, flags);
+ spin_lock(&inode->ordered_tree_lock);
}
cur += len;
}
- spin_unlock_irqrestore(&inode->ordered_tree_lock, flags);
+ spin_unlock(&inode->ordered_tree_lock);
}
/*
@@ -573,10 +569,9 @@ bool btrfs_dec_test_ordered_pending(struct btrfs_inode *inode,
{
struct rb_node *node;
struct btrfs_ordered_extent *entry = NULL;
- unsigned long flags;
bool finished = false;
- spin_lock_irqsave(&inode->ordered_tree_lock, flags);
+ spin_lock(&inode->ordered_tree_lock);
if (cached && *cached) {
entry = *cached;
goto have_entry;
@@ -613,7 +608,7 @@ out:
refcount_inc(&entry->refs);
trace_btrfs_ordered_extent_dec_test_pending(inode, entry);
}
- spin_unlock_irqrestore(&inode->ordered_tree_lock, flags);
+ spin_unlock(&inode->ordered_tree_lock);
return finished;
}
@@ -678,7 +673,7 @@ void btrfs_remove_ordered_extent(struct btrfs_inode *btrfs_inode,
percpu_counter_add_batch(&fs_info->ordered_bytes, -entry->num_bytes,
fs_info->delalloc_batch);
- spin_lock_irq(&btrfs_inode->ordered_tree_lock);
+ spin_lock(&btrfs_inode->ordered_tree_lock);
node = &entry->rb_node;
rb_erase(node, &btrfs_inode->ordered_tree);
RB_CLEAR_NODE(node);
@@ -686,7 +681,7 @@ void btrfs_remove_ordered_extent(struct btrfs_inode *btrfs_inode,
btrfs_inode->ordered_tree_last = NULL;
set_bit(BTRFS_ORDERED_COMPLETE, &entry->flags);
pending = test_and_clear_bit(BTRFS_ORDERED_PENDING, &entry->flags);
- spin_unlock_irq(&btrfs_inode->ordered_tree_lock);
+ spin_unlock(&btrfs_inode->ordered_tree_lock);
/*
* The current running transaction is waiting on us, we need to let it
@@ -971,9 +966,8 @@ struct btrfs_ordered_extent *btrfs_lookup_ordered_extent(struct btrfs_inode *ino
{
struct rb_node *node;
struct btrfs_ordered_extent *entry = NULL;
- unsigned long flags;
- spin_lock_irqsave(&inode->ordered_tree_lock, flags);
+ spin_lock(&inode->ordered_tree_lock);
node = ordered_tree_search(inode, file_offset);
if (!node)
goto out;
@@ -986,7 +980,7 @@ struct btrfs_ordered_extent *btrfs_lookup_ordered_extent(struct btrfs_inode *ino
trace_btrfs_ordered_extent_lookup(inode, entry);
}
out:
- spin_unlock_irqrestore(&inode->ordered_tree_lock, flags);
+ spin_unlock(&inode->ordered_tree_lock);
return entry;
}
@@ -999,7 +993,7 @@ struct btrfs_ordered_extent *btrfs_lookup_ordered_range(
struct rb_node *node;
struct btrfs_ordered_extent *entry = NULL;
- spin_lock_irq(&inode->ordered_tree_lock);
+ spin_lock(&inode->ordered_tree_lock);
node = ordered_tree_search(inode, file_offset);
if (!node) {
node = ordered_tree_search(inode, file_offset + len);
@@ -1026,7 +1020,7 @@ out:
refcount_inc(&entry->refs);
trace_btrfs_ordered_extent_lookup_range(inode, entry);
}
- spin_unlock_irq(&inode->ordered_tree_lock);
+ spin_unlock(&inode->ordered_tree_lock);
return entry;
}
@@ -1041,7 +1035,7 @@ void btrfs_get_ordered_extents_for_logging(struct btrfs_inode *inode,
btrfs_assert_inode_locked(inode);
- spin_lock_irq(&inode->ordered_tree_lock);
+ spin_lock(&inode->ordered_tree_lock);
for (n = rb_first(&inode->ordered_tree); n; n = rb_next(n)) {
struct btrfs_ordered_extent *ordered;
@@ -1055,7 +1049,7 @@ void btrfs_get_ordered_extents_for_logging(struct btrfs_inode *inode,
refcount_inc(&ordered->refs);
trace_btrfs_ordered_extent_lookup_for_logging(inode, ordered);
}
- spin_unlock_irq(&inode->ordered_tree_lock);
+ spin_unlock(&inode->ordered_tree_lock);
}
/*
@@ -1068,7 +1062,7 @@ btrfs_lookup_first_ordered_extent(struct btrfs_inode *inode, u64 file_offset)
struct rb_node *node;
struct btrfs_ordered_extent *entry = NULL;
- spin_lock_irq(&inode->ordered_tree_lock);
+ spin_lock(&inode->ordered_tree_lock);
node = ordered_tree_search(inode, file_offset);
if (!node)
goto out;
@@ -1077,7 +1071,7 @@ btrfs_lookup_first_ordered_extent(struct btrfs_inode *inode, u64 file_offset)
refcount_inc(&entry->refs);
trace_btrfs_ordered_extent_lookup_first(inode, entry);
out:
- spin_unlock_irq(&inode->ordered_tree_lock);
+ spin_unlock(&inode->ordered_tree_lock);
return entry;
}
@@ -1099,7 +1093,7 @@ struct btrfs_ordered_extent *btrfs_lookup_first_ordered_range(
struct rb_node *next;
struct btrfs_ordered_extent *entry = NULL;
- spin_lock_irq(&inode->ordered_tree_lock);
+ spin_lock(&inode->ordered_tree_lock);
node = inode->ordered_tree.rb_node;
/*
* Here we don't want to use tree_search() which will use tree->last
@@ -1154,7 +1148,7 @@ out:
trace_btrfs_ordered_extent_lookup_first_range(inode, entry);
}
- spin_unlock_irq(&inode->ordered_tree_lock);
+ spin_unlock(&inode->ordered_tree_lock);
return entry;
}
@@ -1286,9 +1280,7 @@ struct btrfs_ordered_extent *btrfs_split_ordered_extent(
/*
* Take the root's ordered_extent_lock to avoid a race with
* btrfs_wait_ordered_extents() when updating the disk_bytenr and
- * disk_num_bytes fields of the ordered extent below. And we disable
- * IRQs because the inode's ordered_tree_lock is used in IRQ context
- * elsewhere.
+ * disk_num_bytes fields of the ordered extent below.
*
* There's no concern about a previous caller of
* btrfs_wait_ordered_extents() getting the trimmed ordered extent
diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c
index 62b993fae54f..f189bf09ce6a 100644
--- a/fs/btrfs/print-tree.c
+++ b/fs/btrfs/print-tree.c
@@ -131,7 +131,7 @@ static void print_extent_item(const struct extent_buffer *eb, int slot, int type
struct btrfs_tree_block_info *info;
info = (struct btrfs_tree_block_info *)(ei + 1);
btrfs_tree_block_key(eb, info, &key);
- pr_info("\t\ttree block key (%llu %u %llu) level %d\n",
+ pr_info("\t\ttree block key " BTRFS_KEY_FMT " level %d\n",
btrfs_disk_key_objectid(&key), key.type,
btrfs_disk_key_offset(&key),
btrfs_tree_block_level(eb, info));
@@ -277,9 +277,8 @@ static void print_dir_item(const struct extent_buffer *eb, int i)
struct btrfs_key location;
btrfs_dir_item_key_to_cpu(eb, di, &location);
- pr_info("\t\tlocation key (%llu %u %llu) type %d\n",
- location.objectid, location.type, location.offset,
- btrfs_dir_ftype(eb, di));
+ pr_info("\t\tlocation key " BTRFS_KEY_FMT " type %d\n",
+ BTRFS_KEY_FMT_VALUE(&location), btrfs_dir_ftype(eb, di));
pr_info("\t\ttransid %llu data_len %u name_len %u\n",
btrfs_dir_transid(eb, di), data_len, name_len);
di = (struct btrfs_dir_item *)((char *)di + len);
@@ -421,7 +420,7 @@ static void key_type_string(const struct btrfs_key *key, char *buf, int buf_size
if (key->type == 0 && key->objectid == BTRFS_FREE_SPACE_OBJECTID)
scnprintf(buf, buf_size, "UNTYPED");
else if (key_to_str[key->type])
- scnprintf(buf, buf_size, key_to_str[key->type]);
+ scnprintf(buf, buf_size, "%s", key_to_str[key->type]);
else
scnprintf(buf, buf_size, "UNKNOWN.%d", key->type);
}
@@ -598,10 +597,9 @@ void btrfs_print_tree(const struct extent_buffer *c, bool follow)
print_eb_refs_lock(c);
for (i = 0; i < nr; i++) {
btrfs_node_key_to_cpu(c, &key, i);
- pr_info("\tkey %d (%llu %u %llu) block %llu gen %llu\n",
- i, key.objectid, key.type, key.offset,
- btrfs_node_blockptr(c, i),
- btrfs_node_ptr_generation(c, i));
+ pr_info("\tkey %d " BTRFS_KEY_FMT " block %llu gen %llu\n",
+ i, BTRFS_KEY_FMT_VALUE(&key), btrfs_node_blockptr(c, i),
+ btrfs_node_ptr_generation(c, i));
}
if (!follow)
return;
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 31ad8580322a..9e2b53e90dcb 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -660,7 +660,7 @@ static int add_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src,
{
int ret;
struct btrfs_root *quota_root = trans->fs_info->quota_root;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
path = btrfs_alloc_path();
@@ -672,7 +672,6 @@ static int add_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src,
key.offset = dst;
ret = btrfs_insert_empty_item(trans, quota_root, path, &key, 0);
- btrfs_free_path(path);
return ret;
}
@@ -681,7 +680,7 @@ static int del_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src,
{
int ret;
struct btrfs_root *quota_root = trans->fs_info->quota_root;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
path = btrfs_alloc_path();
@@ -694,24 +693,19 @@ static int del_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src,
ret = btrfs_search_slot(trans, quota_root, &key, path, -1, 1);
if (ret < 0)
- goto out;
+ return ret;
- if (ret > 0) {
- ret = -ENOENT;
- goto out;
- }
+ if (ret > 0)
+ return -ENOENT;
- ret = btrfs_del_item(trans, quota_root, path);
-out:
- btrfs_free_path(path);
- return ret;
+ return btrfs_del_item(trans, quota_root, path);
}
static int add_qgroup_item(struct btrfs_trans_handle *trans,
struct btrfs_root *quota_root, u64 qgroupid)
{
int ret;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_qgroup_info_item *qgroup_info;
struct btrfs_qgroup_limit_item *qgroup_limit;
struct extent_buffer *leaf;
@@ -737,7 +731,7 @@ static int add_qgroup_item(struct btrfs_trans_handle *trans,
ret = btrfs_insert_empty_item(trans, quota_root, path, &key,
sizeof(*qgroup_info));
if (ret && ret != -EEXIST)
- goto out;
+ return ret;
leaf = path->nodes[0];
qgroup_info = btrfs_item_ptr(leaf, path->slots[0],
@@ -754,7 +748,7 @@ static int add_qgroup_item(struct btrfs_trans_handle *trans,
ret = btrfs_insert_empty_item(trans, quota_root, path, &key,
sizeof(*qgroup_limit));
if (ret && ret != -EEXIST)
- goto out;
+ return ret;
leaf = path->nodes[0];
qgroup_limit = btrfs_item_ptr(leaf, path->slots[0],
@@ -765,17 +759,14 @@ static int add_qgroup_item(struct btrfs_trans_handle *trans,
btrfs_set_qgroup_limit_rsv_rfer(leaf, qgroup_limit, 0);
btrfs_set_qgroup_limit_rsv_excl(leaf, qgroup_limit, 0);
- ret = 0;
-out:
- btrfs_free_path(path);
- return ret;
+ return 0;
}
static int del_qgroup_item(struct btrfs_trans_handle *trans, u64 qgroupid)
{
int ret;
struct btrfs_root *quota_root = trans->fs_info->quota_root;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
path = btrfs_alloc_path();
@@ -787,33 +778,27 @@ static int del_qgroup_item(struct btrfs_trans_handle *trans, u64 qgroupid)
key.offset = qgroupid;
ret = btrfs_search_slot(trans, quota_root, &key, path, -1, 1);
if (ret < 0)
- goto out;
+ return ret;
- if (ret > 0) {
- ret = -ENOENT;
- goto out;
- }
+ if (ret > 0)
+ return -ENOENT;
ret = btrfs_del_item(trans, quota_root, path);
if (ret)
- goto out;
+ return ret;
btrfs_release_path(path);
key.type = BTRFS_QGROUP_LIMIT_KEY;
ret = btrfs_search_slot(trans, quota_root, &key, path, -1, 1);
if (ret < 0)
- goto out;
+ return ret;
- if (ret > 0) {
- ret = -ENOENT;
- goto out;
- }
+ if (ret > 0)
+ return -ENOENT;
ret = btrfs_del_item(trans, quota_root, path);
-out:
- btrfs_free_path(path);
return ret;
}
@@ -821,7 +806,7 @@ static int update_qgroup_limit_item(struct btrfs_trans_handle *trans,
struct btrfs_qgroup *qgroup)
{
struct btrfs_root *quota_root = trans->fs_info->quota_root;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
struct extent_buffer *l;
struct btrfs_qgroup_limit_item *qgroup_limit;
@@ -841,7 +826,7 @@ static int update_qgroup_limit_item(struct btrfs_trans_handle *trans,
ret = -ENOENT;
if (ret)
- goto out;
+ return ret;
l = path->nodes[0];
slot = path->slots[0];
@@ -851,8 +836,7 @@ static int update_qgroup_limit_item(struct btrfs_trans_handle *trans,
btrfs_set_qgroup_limit_max_excl(l, qgroup_limit, qgroup->max_excl);
btrfs_set_qgroup_limit_rsv_rfer(l, qgroup_limit, qgroup->rsv_rfer);
btrfs_set_qgroup_limit_rsv_excl(l, qgroup_limit, qgroup->rsv_excl);
-out:
- btrfs_free_path(path);
+
return ret;
}
@@ -861,7 +845,7 @@ static int update_qgroup_info_item(struct btrfs_trans_handle *trans,
{
struct btrfs_fs_info *fs_info = trans->fs_info;
struct btrfs_root *quota_root = fs_info->quota_root;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
struct extent_buffer *l;
struct btrfs_qgroup_info_item *qgroup_info;
@@ -884,7 +868,7 @@ static int update_qgroup_info_item(struct btrfs_trans_handle *trans,
ret = -ENOENT;
if (ret)
- goto out;
+ return ret;
l = path->nodes[0];
slot = path->slots[0];
@@ -894,8 +878,7 @@ static int update_qgroup_info_item(struct btrfs_trans_handle *trans,
btrfs_set_qgroup_info_rfer_cmpr(l, qgroup_info, qgroup->rfer_cmpr);
btrfs_set_qgroup_info_excl(l, qgroup_info, qgroup->excl);
btrfs_set_qgroup_info_excl_cmpr(l, qgroup_info, qgroup->excl_cmpr);
-out:
- btrfs_free_path(path);
+
return ret;
}
@@ -903,7 +886,7 @@ static int update_qgroup_status_item(struct btrfs_trans_handle *trans)
{
struct btrfs_fs_info *fs_info = trans->fs_info;
struct btrfs_root *quota_root = fs_info->quota_root;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
struct extent_buffer *l;
struct btrfs_qgroup_status_item *ptr;
@@ -923,7 +906,7 @@ static int update_qgroup_status_item(struct btrfs_trans_handle *trans)
ret = -ENOENT;
if (ret)
- goto out;
+ return ret;
l = path->nodes[0];
slot = path->slots[0];
@@ -933,8 +916,7 @@ static int update_qgroup_status_item(struct btrfs_trans_handle *trans)
btrfs_set_qgroup_status_generation(l, ptr, trans->transid);
btrfs_set_qgroup_status_rescan(l, ptr,
fs_info->qgroup_rescan_progress.objectid);
-out:
- btrfs_free_path(path);
+
return ret;
}
@@ -944,7 +926,7 @@ out:
static int btrfs_clean_quota_tree(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
struct extent_buffer *leaf = NULL;
int ret;
@@ -961,7 +943,7 @@ static int btrfs_clean_quota_tree(struct btrfs_trans_handle *trans,
while (1) {
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
if (ret < 0)
- goto out;
+ return ret;
leaf = path->nodes[0];
nr = btrfs_header_nritems(leaf);
if (!nr)
@@ -974,14 +956,12 @@ static int btrfs_clean_quota_tree(struct btrfs_trans_handle *trans,
path->slots[0] = 0;
ret = btrfs_del_items(trans, root, path, 0, nr);
if (ret)
- goto out;
+ return ret;
btrfs_release_path(path);
}
- ret = 0;
-out:
- btrfs_free_path(path);
- return ret;
+
+ return 0;
}
int btrfs_quota_enable(struct btrfs_fs_info *fs_info,
@@ -1263,7 +1243,14 @@ out:
btrfs_end_transaction(trans);
else if (trans)
ret = btrfs_end_transaction(trans);
- kfree(prealloc);
+
+ /*
+ * At this point we either failed at allocating prealloc, or we
+ * succeeded and passed the ownership to it to add_qgroup_rb(). In any
+ * case, this needs to be NULL or there is something wrong.
+ */
+ ASSERT(prealloc == NULL);
+
return ret;
}
@@ -1695,7 +1682,12 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid)
ret = btrfs_sysfs_add_one_qgroup(fs_info, qgroup);
out:
mutex_unlock(&fs_info->qgroup_ioctl_lock);
- kfree(prealloc);
+ /*
+ * At this point we either failed at allocating prealloc, or we
+ * succeeded and passed the ownership to it to add_qgroup_rb(). In any
+ * case, this needs to be NULL or there is something wrong.
+ */
+ ASSERT(prealloc == NULL);
return ret;
}
@@ -1707,8 +1699,7 @@ out:
static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *qgroup)
{
struct btrfs_key key;
- struct btrfs_path *path;
- int ret;
+ BTRFS_PATH_AUTO_FREE(path);
/*
* Squota would never be inconsistent, but there can still be case
@@ -1741,13 +1732,11 @@ static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup
if (!path)
return -ENOMEM;
- ret = btrfs_find_root(fs_info->tree_root, &key, path, NULL, NULL);
- btrfs_free_path(path);
/*
* The @ret from btrfs_find_root() exactly matches our definition for
* the return value, thus can be returned directly.
*/
- return ret;
+ return btrfs_find_root(fs_info->tree_root, &key, path, NULL, NULL);
}
int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid)
@@ -2296,7 +2285,7 @@ static int qgroup_trace_extent_swap(struct btrfs_trans_handle* trans,
bool trace_leaf)
{
struct btrfs_key key;
- struct btrfs_path *src_path;
+ BTRFS_PATH_AUTO_FREE(src_path);
struct btrfs_fs_info *fs_info = trans->fs_info;
u32 nodesize = fs_info->nodesize;
int cur_level = root_level;
@@ -2308,10 +2297,8 @@ static int qgroup_trace_extent_swap(struct btrfs_trans_handle* trans,
return -EINVAL;
src_path = btrfs_alloc_path();
- if (!src_path) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!src_path)
+ return -ENOMEM;
if (dst_level)
btrfs_node_key_to_cpu(dst_path->nodes[dst_level], &key, 0);
@@ -2337,10 +2324,8 @@ static int qgroup_trace_extent_swap(struct btrfs_trans_handle* trans,
parent_slot = src_path->slots[cur_level + 1];
eb = btrfs_read_node_slot(eb, parent_slot);
- if (IS_ERR(eb)) {
- ret = PTR_ERR(eb);
- goto out;
- }
+ if (IS_ERR(eb))
+ return PTR_ERR(eb);
src_path->nodes[cur_level] = eb;
@@ -2361,10 +2346,8 @@ static int qgroup_trace_extent_swap(struct btrfs_trans_handle* trans,
&src_key, src_path->slots[cur_level]);
}
/* Content mismatch, something went wrong */
- if (btrfs_comp_cpu_keys(&dst_key, &src_key)) {
- ret = -ENOENT;
- goto out;
- }
+ if (btrfs_comp_cpu_keys(&dst_key, &src_key))
+ return -ENOENT;
cur_level--;
}
@@ -2375,21 +2358,20 @@ static int qgroup_trace_extent_swap(struct btrfs_trans_handle* trans,
ret = btrfs_qgroup_trace_extent(trans, src_path->nodes[dst_level]->start,
nodesize);
if (ret < 0)
- goto out;
+ return ret;
ret = btrfs_qgroup_trace_extent(trans, dst_path->nodes[dst_level]->start,
nodesize);
if (ret < 0)
- goto out;
+ return ret;
/* Record leaf file extents */
if (dst_level == 0 && trace_leaf) {
ret = btrfs_qgroup_trace_leaf_items(trans, src_path->nodes[0]);
if (ret < 0)
- goto out;
+ return ret;
ret = btrfs_qgroup_trace_leaf_items(trans, dst_path->nodes[0]);
}
-out:
- btrfs_free_path(src_path);
+
return ret;
}
@@ -2590,7 +2572,7 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans,
int level;
u8 drop_subptree_thres;
struct extent_buffer *eb = root_eb;
- struct btrfs_path *path = NULL;
+ BTRFS_PATH_AUTO_FREE(path);
ASSERT(0 <= root_level && root_level < BTRFS_MAX_LEVEL);
ASSERT(root_eb != NULL);
@@ -2623,12 +2605,12 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans,
ret = btrfs_read_extent_buffer(root_eb, &check);
if (ret)
- goto out;
+ return ret;
}
if (root_level == 0) {
ret = btrfs_qgroup_trace_leaf_items(trans, root_eb);
- goto out;
+ return ret;
}
path = btrfs_alloc_path();
@@ -2664,10 +2646,8 @@ walk_down:
child_bytenr = btrfs_node_blockptr(eb, parent_slot);
eb = btrfs_read_node_slot(eb, parent_slot);
- if (IS_ERR(eb)) {
- ret = PTR_ERR(eb);
- goto out;
- }
+ if (IS_ERR(eb))
+ return PTR_ERR(eb);
path->nodes[level] = eb;
path->slots[level] = 0;
@@ -2678,14 +2658,14 @@ walk_down:
ret = btrfs_qgroup_trace_extent(trans, child_bytenr,
fs_info->nodesize);
if (ret)
- goto out;
+ return ret;
}
if (level == 0) {
ret = btrfs_qgroup_trace_leaf_items(trans,
path->nodes[level]);
if (ret)
- goto out;
+ return ret;
/* Nonzero return here means we completed our search */
ret = adjust_slots_upwards(path, root_level);
@@ -2699,11 +2679,7 @@ walk_down:
level--;
}
- ret = 0;
-out:
- btrfs_free_path(path);
-
- return ret;
+ return 0;
}
static void qgroup_iterator_nested_add(struct list_head *head, struct btrfs_qgroup *qgroup)
@@ -3303,7 +3279,7 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
struct btrfs_root *quota_root;
struct btrfs_qgroup *srcgroup;
struct btrfs_qgroup *dstgroup;
- struct btrfs_qgroup *prealloc;
+ struct btrfs_qgroup *prealloc = NULL;
struct btrfs_qgroup_list **qlist_prealloc = NULL;
bool free_inherit = false;
bool need_rescan = false;
@@ -3544,7 +3520,14 @@ out:
}
if (free_inherit)
kfree(inherit);
- kfree(prealloc);
+
+ /*
+ * At this point we either failed at allocating prealloc, or we
+ * succeeded and passed the ownership to it to add_qgroup_rb(). In any
+ * case, this needs to be NULL or there is something wrong.
+ */
+ ASSERT(prealloc == NULL);
+
return ret;
}
@@ -3712,10 +3695,8 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans,
path, 1, 0);
btrfs_debug(fs_info,
- "current progress key (%llu %u %llu), search_slot ret %d",
- fs_info->qgroup_rescan_progress.objectid,
- fs_info->qgroup_rescan_progress.type,
- fs_info->qgroup_rescan_progress.offset, ret);
+ "current progress key " BTRFS_KEY_FMT ", search_slot ret %d",
+ BTRFS_KEY_FMT_VALUE(&fs_info->qgroup_rescan_progress), ret);
if (ret) {
/*
@@ -3817,8 +3798,8 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
* Rescan should only search for commit root, and any later difference
* should be recorded by qgroup
*/
- path->search_commit_root = 1;
- path->skip_locking = 1;
+ path->search_commit_root = true;
+ path->skip_locking = true;
while (!ret && !(stopped = rescan_should_stop(fs_info))) {
trans = btrfs_start_transaction(fs_info->fs_root, 0);
@@ -4796,7 +4777,7 @@ int btrfs_qgroup_trace_subtree_after_cow(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_tree_parent_check check = { 0 };
struct btrfs_qgroup_swapped_blocks *blocks = &root->swapped_blocks;
- struct btrfs_qgroup_swapped_block *block;
+ struct btrfs_qgroup_swapped_block AUTO_KFREE(block);
struct extent_buffer *reloc_eb = NULL;
struct rb_node *node;
bool swapped = false;
@@ -4853,7 +4834,6 @@ int btrfs_qgroup_trace_subtree_after_cow(struct btrfs_trans_handle *trans,
ret = qgroup_trace_subtree_swap(trans, reloc_eb, subvol_eb,
block->last_snapshot, block->trace_leaf);
free_out:
- kfree(block);
free_extent_buffer(reloc_eb);
out:
if (ret < 0) {
diff --git a/fs/btrfs/raid-stripe-tree.c b/fs/btrfs/raid-stripe-tree.c
index cc6f6095cc9f..2987cb7c686e 100644
--- a/fs/btrfs/raid-stripe-tree.c
+++ b/fs/btrfs/raid-stripe-tree.c
@@ -19,7 +19,7 @@ static int btrfs_partially_delete_raid_extent(struct btrfs_trans_handle *trans,
u64 newlen, u64 frontpad)
{
struct btrfs_root *stripe_root = trans->fs_info->stripe_root;
- struct btrfs_stripe_extent *extent, *newitem;
+ struct btrfs_stripe_extent *extent, AUTO_KFREE(newitem);
struct extent_buffer *leaf;
int slot;
size_t item_size;
@@ -53,14 +53,10 @@ static int btrfs_partially_delete_raid_extent(struct btrfs_trans_handle *trans,
ret = btrfs_del_item(trans, stripe_root, path);
if (ret)
- goto out;
+ return ret;
btrfs_release_path(path);
- ret = btrfs_insert_item(trans, stripe_root, &newkey, newitem, item_size);
-
-out:
- kfree(newitem);
- return ret;
+ return btrfs_insert_item(trans, stripe_root, &newkey, newitem, item_size);
}
int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 length)
@@ -299,7 +295,7 @@ int btrfs_insert_one_raid_extent(struct btrfs_trans_handle *trans,
struct btrfs_key stripe_key;
struct btrfs_root *stripe_root = fs_info->stripe_root;
const int num_stripes = btrfs_bg_type_to_factor(bioc->map_type);
- struct btrfs_stripe_extent *stripe_extent;
+ struct btrfs_stripe_extent AUTO_KFREE(stripe_extent);
const size_t item_size = struct_size(stripe_extent, strides, num_stripes);
int ret;
@@ -336,8 +332,6 @@ int btrfs_insert_one_raid_extent(struct btrfs_trans_handle *trans,
btrfs_abort_transaction(trans, ret);
}
- kfree(stripe_extent);
-
return ret;
}
@@ -394,8 +388,8 @@ int btrfs_get_raid_extent_offset(struct btrfs_fs_info *fs_info,
return -ENOMEM;
if (stripe->rst_search_commit_root) {
- path->skip_locking = 1;
- path->search_commit_root = 1;
+ path->skip_locking = true;
+ path->search_commit_root = true;
}
ret = btrfs_search_slot(NULL, stripe_root, &stripe_key, path, 0, 0);
diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index 0135dceb7baa..f38d8305e46d 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -66,10 +66,10 @@ static void btrfs_dump_rbio(const struct btrfs_fs_info *fs_info,
dump_bioc(fs_info, rbio->bioc);
btrfs_crit(fs_info,
-"rbio flags=0x%lx nr_sectors=%u nr_data=%u real_stripes=%u stripe_nsectors=%u scrubp=%u dbitmap=0x%lx",
+"rbio flags=0x%lx nr_sectors=%u nr_data=%u real_stripes=%u stripe_nsectors=%u sector_nsteps=%u scrubp=%u dbitmap=0x%lx",
rbio->flags, rbio->nr_sectors, rbio->nr_data,
rbio->real_stripes, rbio->stripe_nsectors,
- rbio->scrubp, rbio->dbitmap);
+ rbio->sector_nsteps, rbio->scrubp, rbio->dbitmap);
}
#define ASSERT_RBIO(expr, rbio) \
@@ -134,18 +134,10 @@ struct btrfs_stripe_hash_table {
};
/*
- * A structure to present a sector inside a page, the length is fixed to
- * sectorsize;
+ * The PFN may still be valid, but our paddrs should always be block size
+ * aligned, thus such -1 paddr is definitely not a valid one.
*/
-struct sector_ptr {
- /*
- * Blocks from the bio list can still be highmem.
- * So here we use physical address to present a page and the offset inside it.
- */
- phys_addr_t paddr;
- bool has_paddr;
- bool uptodate;
-};
+#define INVALID_PADDR (~(phys_addr_t)0)
static void rmw_rbio_work(struct work_struct *work);
static void rmw_rbio_work_locked(struct work_struct *work);
@@ -159,8 +151,8 @@ static void free_raid_bio_pointers(struct btrfs_raid_bio *rbio)
{
bitmap_free(rbio->error_bitmap);
kfree(rbio->stripe_pages);
- kfree(rbio->bio_sectors);
- kfree(rbio->stripe_sectors);
+ kfree(rbio->bio_paddrs);
+ kfree(rbio->stripe_paddrs);
kfree(rbio->finish_pointers);
}
@@ -235,12 +227,22 @@ int btrfs_alloc_stripe_hash_table(struct btrfs_fs_info *info)
return 0;
}
-static void memcpy_sectors(const struct sector_ptr *dst,
- const struct sector_ptr *src, u32 blocksize)
+static void memcpy_from_bio_to_stripe(struct btrfs_raid_bio *rbio, unsigned int sector_nr)
{
- memcpy_page(phys_to_page(dst->paddr), offset_in_page(dst->paddr),
- phys_to_page(src->paddr), offset_in_page(src->paddr),
- blocksize);
+ const u32 step = min(rbio->bioc->fs_info->sectorsize, PAGE_SIZE);
+
+ ASSERT(sector_nr < rbio->nr_sectors);
+ for (int i = 0; i < rbio->sector_nsteps; i++) {
+ unsigned int index = sector_nr * rbio->sector_nsteps + i;
+ phys_addr_t dst = rbio->stripe_paddrs[index];
+ phys_addr_t src = rbio->bio_paddrs[index];
+
+ ASSERT(dst != INVALID_PADDR);
+ ASSERT(src != INVALID_PADDR);
+
+ memcpy_page(phys_to_page(dst), offset_in_page(dst),
+ phys_to_page(src), offset_in_page(src), step);
+ }
}
/*
@@ -263,20 +265,19 @@ static void cache_rbio_pages(struct btrfs_raid_bio *rbio)
for (i = 0; i < rbio->nr_sectors; i++) {
/* Some range not covered by bio (partial write), skip it */
- if (!rbio->bio_sectors[i].has_paddr) {
+ if (rbio->bio_paddrs[i * rbio->sector_nsteps] == INVALID_PADDR) {
/*
* Even if the sector is not covered by bio, if it is
* a data sector it should still be uptodate as it is
* read from disk.
*/
if (i < rbio->nr_data * rbio->stripe_nsectors)
- ASSERT(rbio->stripe_sectors[i].uptodate);
+ ASSERT(test_bit(i, rbio->stripe_uptodate_bitmap));
continue;
}
- memcpy_sectors(&rbio->stripe_sectors[i], &rbio->bio_sectors[i],
- rbio->bioc->fs_info->sectorsize);
- rbio->stripe_sectors[i].uptodate = 1;
+ memcpy_from_bio_to_stripe(rbio, i);
+ set_bit(i, rbio->stripe_uptodate_bitmap);
}
set_bit(RBIO_CACHE_READY_BIT, &rbio->flags);
}
@@ -299,19 +300,48 @@ static int rbio_bucket(struct btrfs_raid_bio *rbio)
return hash_64(num >> 16, BTRFS_STRIPE_HASH_TABLE_BITS);
}
-static bool full_page_sectors_uptodate(struct btrfs_raid_bio *rbio,
- unsigned int page_nr)
+/* Get the sector number of the first sector covered by @page_nr. */
+static u32 page_nr_to_sector_nr(struct btrfs_raid_bio *rbio, unsigned int page_nr)
{
- const u32 sectorsize = rbio->bioc->fs_info->sectorsize;
- const u32 sectors_per_page = PAGE_SIZE / sectorsize;
+ u32 sector_nr;
+
+ ASSERT(page_nr < rbio->nr_pages);
+
+ sector_nr = (page_nr << PAGE_SHIFT) >> rbio->bioc->fs_info->sectorsize_bits;
+ ASSERT(sector_nr < rbio->nr_sectors);
+ return sector_nr;
+}
+
+/*
+ * Get the number of sectors covered by @page_nr.
+ *
+ * For bs > ps cases, the result will always be 1.
+ * For bs <= ps cases, the result will be ps / bs.
+ */
+static u32 page_nr_to_num_sectors(struct btrfs_raid_bio *rbio, unsigned int page_nr)
+{
+ struct btrfs_fs_info *fs_info = rbio->bioc->fs_info;
+ u32 nr_sectors;
+
+ ASSERT(page_nr < rbio->nr_pages);
+
+ nr_sectors = round_up(PAGE_SIZE, fs_info->sectorsize) >> fs_info->sectorsize_bits;
+ ASSERT(nr_sectors > 0);
+ return nr_sectors;
+}
+
+static __maybe_unused bool full_page_sectors_uptodate(struct btrfs_raid_bio *rbio,
+ unsigned int page_nr)
+{
+ const u32 sector_nr = page_nr_to_sector_nr(rbio, page_nr);
+ const u32 nr_bits = page_nr_to_num_sectors(rbio, page_nr);
int i;
ASSERT(page_nr < rbio->nr_pages);
+ ASSERT(sector_nr + nr_bits < rbio->nr_sectors);
- for (i = sectors_per_page * page_nr;
- i < sectors_per_page * page_nr + sectors_per_page;
- i++) {
- if (!rbio->stripe_sectors[i].uptodate)
+ for (i = sector_nr; i < sector_nr + nr_bits; i++) {
+ if (!test_bit(i, rbio->stripe_uptodate_bitmap))
return false;
}
return true;
@@ -324,46 +354,44 @@ static bool full_page_sectors_uptodate(struct btrfs_raid_bio *rbio,
*/
static void index_stripe_sectors(struct btrfs_raid_bio *rbio)
{
- const u32 sectorsize = rbio->bioc->fs_info->sectorsize;
+ const u32 step = min(rbio->bioc->fs_info->sectorsize, PAGE_SIZE);
u32 offset;
int i;
- for (i = 0, offset = 0; i < rbio->nr_sectors; i++, offset += sectorsize) {
+ for (i = 0, offset = 0; i < rbio->nr_sectors * rbio->sector_nsteps;
+ i++, offset += step) {
int page_index = offset >> PAGE_SHIFT;
ASSERT(page_index < rbio->nr_pages);
if (!rbio->stripe_pages[page_index])
continue;
- rbio->stripe_sectors[i].has_paddr = true;
- rbio->stripe_sectors[i].paddr =
- page_to_phys(rbio->stripe_pages[page_index]) +
- offset_in_page(offset);
+ rbio->stripe_paddrs[i] = page_to_phys(rbio->stripe_pages[page_index]) +
+ offset_in_page(offset);
}
}
static void steal_rbio_page(struct btrfs_raid_bio *src,
struct btrfs_raid_bio *dest, int page_nr)
{
- const u32 sectorsize = src->bioc->fs_info->sectorsize;
- const u32 sectors_per_page = PAGE_SIZE / sectorsize;
- int i;
+ const u32 sector_nr = page_nr_to_sector_nr(src, page_nr);
+ const u32 nr_bits = page_nr_to_num_sectors(src, page_nr);
+
+ ASSERT(page_nr < src->nr_pages);
+ ASSERT(sector_nr + nr_bits < src->nr_sectors);
if (dest->stripe_pages[page_nr])
__free_page(dest->stripe_pages[page_nr]);
dest->stripe_pages[page_nr] = src->stripe_pages[page_nr];
src->stripe_pages[page_nr] = NULL;
- /* Also update the sector->uptodate bits. */
- for (i = sectors_per_page * page_nr;
- i < sectors_per_page * page_nr + sectors_per_page; i++)
- dest->stripe_sectors[i].uptodate = true;
+ /* Also update the stripe_uptodate_bitmap bits. */
+ bitmap_set(dest->stripe_uptodate_bitmap, sector_nr, nr_bits);
}
static bool is_data_stripe_page(struct btrfs_raid_bio *rbio, int page_nr)
{
- const int sector_nr = (page_nr << PAGE_SHIFT) >>
- rbio->bioc->fs_info->sectorsize_bits;
+ const int sector_nr = page_nr_to_sector_nr(rbio, page_nr);
/*
* We have ensured PAGE_SIZE is aligned with sectorsize, thus
@@ -677,39 +705,62 @@ static int rbio_can_merge(struct btrfs_raid_bio *last,
return 1;
}
-static unsigned int rbio_stripe_sector_index(const struct btrfs_raid_bio *rbio,
- unsigned int stripe_nr,
- unsigned int sector_nr)
+/* Return the sector index for @stripe_nr and @sector_nr. */
+static unsigned int rbio_sector_index(const struct btrfs_raid_bio *rbio,
+ unsigned int stripe_nr,
+ unsigned int sector_nr)
{
+ unsigned int ret;
+
ASSERT_RBIO_STRIPE(stripe_nr < rbio->real_stripes, rbio, stripe_nr);
ASSERT_RBIO_SECTOR(sector_nr < rbio->stripe_nsectors, rbio, sector_nr);
- return stripe_nr * rbio->stripe_nsectors + sector_nr;
+ ret = stripe_nr * rbio->stripe_nsectors + sector_nr;
+ ASSERT(ret < rbio->nr_sectors);
+ return ret;
+}
+
+/* Return the paddr array index for @stripe_nr, @sector_nr and @step_nr. */
+static unsigned int rbio_paddr_index(const struct btrfs_raid_bio *rbio,
+ unsigned int stripe_nr,
+ unsigned int sector_nr,
+ unsigned int step_nr)
+{
+ unsigned int ret;
+
+ ASSERT_RBIO_SECTOR(step_nr < rbio->sector_nsteps, rbio, step_nr);
+
+ ret = rbio_sector_index(rbio, stripe_nr, sector_nr) * rbio->sector_nsteps + step_nr;
+ ASSERT(ret < rbio->nr_sectors * rbio->sector_nsteps);
+ return ret;
}
-/* Return a sector from rbio->stripe_sectors, not from the bio list */
-static struct sector_ptr *rbio_stripe_sector(const struct btrfs_raid_bio *rbio,
- unsigned int stripe_nr,
- unsigned int sector_nr)
+static phys_addr_t rbio_stripe_paddr(const struct btrfs_raid_bio *rbio,
+ unsigned int stripe_nr, unsigned int sector_nr,
+ unsigned int step_nr)
{
- return &rbio->stripe_sectors[rbio_stripe_sector_index(rbio, stripe_nr,
- sector_nr)];
+ return rbio->stripe_paddrs[rbio_paddr_index(rbio, stripe_nr, sector_nr, step_nr)];
}
-/* Grab a sector inside P stripe */
-static struct sector_ptr *rbio_pstripe_sector(const struct btrfs_raid_bio *rbio,
- unsigned int sector_nr)
+static phys_addr_t rbio_pstripe_paddr(const struct btrfs_raid_bio *rbio,
+ unsigned int sector_nr, unsigned int step_nr)
{
- return rbio_stripe_sector(rbio, rbio->nr_data, sector_nr);
+ return rbio_stripe_paddr(rbio, rbio->nr_data, sector_nr, step_nr);
}
-/* Grab a sector inside Q stripe, return NULL if not RAID6 */
-static struct sector_ptr *rbio_qstripe_sector(const struct btrfs_raid_bio *rbio,
- unsigned int sector_nr)
+static phys_addr_t rbio_qstripe_paddr(const struct btrfs_raid_bio *rbio,
+ unsigned int sector_nr, unsigned int step_nr)
{
if (rbio->nr_data + 1 == rbio->real_stripes)
- return NULL;
- return rbio_stripe_sector(rbio, rbio->nr_data + 1, sector_nr);
+ return INVALID_PADDR;
+ return rbio_stripe_paddr(rbio, rbio->nr_data + 1, sector_nr, step_nr);
+}
+
+/* Return a paddr pointer into the rbio::stripe_paddrs[] for the specified sector. */
+static phys_addr_t *rbio_stripe_paddrs(const struct btrfs_raid_bio *rbio,
+ unsigned int stripe_nr, unsigned int sector_nr)
+{
+ return &rbio->stripe_paddrs[rbio_paddr_index(rbio, stripe_nr, sector_nr, 0)];
}
/*
@@ -944,7 +995,7 @@ static void rbio_orig_end_io(struct btrfs_raid_bio *rbio, blk_status_t status)
}
/*
- * Get a sector pointer specified by its @stripe_nr and @sector_nr.
+ * Get paddr pointer for the sector specified by its @stripe_nr and @sector_nr.
*
* @rbio: The raid bio
* @stripe_nr: Stripe number, valid range [0, real_stripe)
@@ -954,34 +1005,52 @@ static void rbio_orig_end_io(struct btrfs_raid_bio *rbio, blk_status_t status)
*
* The read/modify/write code wants to reuse the original bio page as much
* as possible, and only use stripe_sectors as fallback.
+ *
+ * Return NULL if bio_list_only is set but the specified sector has no
+ * coresponding bio.
*/
-static struct sector_ptr *sector_in_rbio(struct btrfs_raid_bio *rbio,
- int stripe_nr, int sector_nr,
- bool bio_list_only)
+static phys_addr_t *sector_paddrs_in_rbio(struct btrfs_raid_bio *rbio,
+ int stripe_nr, int sector_nr,
+ bool bio_list_only)
{
- struct sector_ptr *sector;
- int index;
+ phys_addr_t *ret = NULL;
+ const int index = rbio_paddr_index(rbio, stripe_nr, sector_nr, 0);
- ASSERT_RBIO_STRIPE(stripe_nr >= 0 && stripe_nr < rbio->real_stripes,
- rbio, stripe_nr);
- ASSERT_RBIO_SECTOR(sector_nr >= 0 && sector_nr < rbio->stripe_nsectors,
- rbio, sector_nr);
+ ASSERT(index >= 0 && index < rbio->nr_sectors * rbio->sector_nsteps);
- index = stripe_nr * rbio->stripe_nsectors + sector_nr;
- ASSERT(index >= 0 && index < rbio->nr_sectors);
-
- spin_lock(&rbio->bio_list_lock);
- sector = &rbio->bio_sectors[index];
- if (sector->has_paddr || bio_list_only) {
- /* Don't return sector without a valid page pointer */
- if (!sector->has_paddr)
- sector = NULL;
- spin_unlock(&rbio->bio_list_lock);
- return sector;
+ scoped_guard(spinlock, &rbio->bio_list_lock) {
+ if (rbio->bio_paddrs[index] != INVALID_PADDR || bio_list_only) {
+ /* Don't return sector without a valid page pointer */
+ if (rbio->bio_paddrs[index] != INVALID_PADDR)
+ ret = &rbio->bio_paddrs[index];
+ return ret;
+ }
}
- spin_unlock(&rbio->bio_list_lock);
+ return &rbio->stripe_paddrs[index];
+}
- return &rbio->stripe_sectors[index];
+/*
+ * Similar to sector_paddr_in_rbio(), but with extra consideration for
+ * bs > ps cases, where we can have multiple steps for a fs block.
+ */
+static phys_addr_t sector_paddr_in_rbio(struct btrfs_raid_bio *rbio,
+ int stripe_nr, int sector_nr, int step_nr,
+ bool bio_list_only)
+{
+ phys_addr_t ret = INVALID_PADDR;
+ const int index = rbio_paddr_index(rbio, stripe_nr, sector_nr, step_nr);
+
+ ASSERT(index >= 0 && index < rbio->nr_sectors * rbio->sector_nsteps);
+
+ scoped_guard(spinlock, &rbio->bio_list_lock) {
+ if (rbio->bio_paddrs[index] != INVALID_PADDR || bio_list_only) {
+ /* Don't return sector without a valid page pointer */
+ if (rbio->bio_paddrs[index] != INVALID_PADDR)
+ ret = rbio->bio_paddrs[index];
+ return ret;
+ }
+ }
+ return rbio->stripe_paddrs[index];
}
/*
@@ -997,10 +1066,16 @@ static struct btrfs_raid_bio *alloc_rbio(struct btrfs_fs_info *fs_info,
const unsigned int stripe_nsectors =
BTRFS_STRIPE_LEN >> fs_info->sectorsize_bits;
const unsigned int num_sectors = stripe_nsectors * real_stripes;
+ const unsigned int step = min(fs_info->sectorsize, PAGE_SIZE);
+ const unsigned int sector_nsteps = fs_info->sectorsize / step;
struct btrfs_raid_bio *rbio;
- /* PAGE_SIZE must also be aligned to sectorsize for subpage support */
- ASSERT(IS_ALIGNED(PAGE_SIZE, fs_info->sectorsize));
+ /*
+ * For bs <= ps cases, ps must be aligned to bs.
+ * For bs > ps cases, bs must be aligned to ps.
+ */
+ ASSERT(IS_ALIGNED(PAGE_SIZE, fs_info->sectorsize) ||
+ IS_ALIGNED(fs_info->sectorsize, PAGE_SIZE));
/*
* Our current stripe len should be fixed to 64k thus stripe_nsectors
* (at most 16) should be no larger than BITS_PER_LONG.
@@ -1019,19 +1094,22 @@ static struct btrfs_raid_bio *alloc_rbio(struct btrfs_fs_info *fs_info,
return ERR_PTR(-ENOMEM);
rbio->stripe_pages = kcalloc(num_pages, sizeof(struct page *),
GFP_NOFS);
- rbio->bio_sectors = kcalloc(num_sectors, sizeof(struct sector_ptr),
- GFP_NOFS);
- rbio->stripe_sectors = kcalloc(num_sectors, sizeof(struct sector_ptr),
- GFP_NOFS);
+ rbio->bio_paddrs = kcalloc(num_sectors * sector_nsteps, sizeof(phys_addr_t), GFP_NOFS);
+ rbio->stripe_paddrs = kcalloc(num_sectors * sector_nsteps, sizeof(phys_addr_t), GFP_NOFS);
rbio->finish_pointers = kcalloc(real_stripes, sizeof(void *), GFP_NOFS);
rbio->error_bitmap = bitmap_zalloc(num_sectors, GFP_NOFS);
+ rbio->stripe_uptodate_bitmap = bitmap_zalloc(num_sectors, GFP_NOFS);
- if (!rbio->stripe_pages || !rbio->bio_sectors || !rbio->stripe_sectors ||
- !rbio->finish_pointers || !rbio->error_bitmap) {
+ if (!rbio->stripe_pages || !rbio->bio_paddrs || !rbio->stripe_paddrs ||
+ !rbio->finish_pointers || !rbio->error_bitmap || !rbio->stripe_uptodate_bitmap) {
free_raid_bio_pointers(rbio);
kfree(rbio);
return ERR_PTR(-ENOMEM);
}
+ for (int i = 0; i < num_sectors * sector_nsteps; i++) {
+ rbio->stripe_paddrs[i] = INVALID_PADDR;
+ rbio->bio_paddrs[i] = INVALID_PADDR;
+ }
bio_list_init(&rbio->bio_list);
init_waitqueue_head(&rbio->io_wait);
@@ -1046,6 +1124,7 @@ static struct btrfs_raid_bio *alloc_rbio(struct btrfs_fs_info *fs_info,
rbio->real_stripes = real_stripes;
rbio->stripe_npages = stripe_npages;
rbio->stripe_nsectors = stripe_nsectors;
+ rbio->sector_nsteps = sector_nsteps;
refcount_set(&rbio->refs, 1);
atomic_set(&rbio->stripes_pending, 0);
@@ -1090,8 +1169,8 @@ static int alloc_rbio_parity_pages(struct btrfs_raid_bio *rbio)
* @faila and @failb will also be updated to the first and second stripe
* number of the errors.
*/
-static int get_rbio_veritical_errors(struct btrfs_raid_bio *rbio, int sector_nr,
- int *faila, int *failb)
+static int get_rbio_vertical_errors(struct btrfs_raid_bio *rbio, int sector_nr,
+ int *faila, int *failb)
{
int stripe_nr;
int found_errors = 0;
@@ -1123,20 +1202,41 @@ static int get_rbio_veritical_errors(struct btrfs_raid_bio *rbio, int sector_nr,
return found_errors;
}
+static int bio_add_paddrs(struct bio *bio, phys_addr_t *paddrs, unsigned int nr_steps,
+ unsigned int step)
+{
+ int added = 0;
+ int ret;
+
+ for (int i = 0; i < nr_steps; i++) {
+ ret = bio_add_page(bio, phys_to_page(paddrs[i]), step,
+ offset_in_page(paddrs[i]));
+ if (ret != step)
+ goto revert;
+ added += ret;
+ }
+ return added;
+revert:
+ /*
+ * We don't need to revert the bvec, as the bio will be submitted immediately,
+ * as long as the size is reduced the extra bvec will not be accessed.
+ */
+ bio->bi_iter.bi_size -= added;
+ return 0;
+}
+
/*
* Add a single sector @sector into our list of bios for IO.
*
* Return 0 if everything went well.
- * Return <0 for error.
+ * Return <0 for error, and no byte will be added to @rbio.
*/
-static int rbio_add_io_sector(struct btrfs_raid_bio *rbio,
- struct bio_list *bio_list,
- struct sector_ptr *sector,
- unsigned int stripe_nr,
- unsigned int sector_nr,
- enum req_op op)
+static int rbio_add_io_paddrs(struct btrfs_raid_bio *rbio, struct bio_list *bio_list,
+ phys_addr_t *paddrs, unsigned int stripe_nr,
+ unsigned int sector_nr, enum req_op op)
{
const u32 sectorsize = rbio->bioc->fs_info->sectorsize;
+ const u32 step = min(sectorsize, PAGE_SIZE);
struct bio *last = bio_list->tail;
int ret;
struct bio *bio;
@@ -1152,7 +1252,7 @@ static int rbio_add_io_sector(struct btrfs_raid_bio *rbio,
rbio, stripe_nr);
ASSERT_RBIO_SECTOR(sector_nr >= 0 && sector_nr < rbio->stripe_nsectors,
rbio, sector_nr);
- ASSERT(sector->has_paddr);
+ ASSERT(paddrs != NULL);
stripe = &rbio->bioc->stripes[stripe_nr];
disk_start = stripe->physical + sector_nr * sectorsize;
@@ -1165,8 +1265,8 @@ static int rbio_add_io_sector(struct btrfs_raid_bio *rbio,
rbio->error_bitmap);
/* Check if we have reached tolerance early. */
- found_errors = get_rbio_veritical_errors(rbio, sector_nr,
- NULL, NULL);
+ found_errors = get_rbio_vertical_errors(rbio, sector_nr,
+ NULL, NULL);
if (unlikely(found_errors > rbio->bioc->max_errors))
return -EIO;
return 0;
@@ -1183,8 +1283,7 @@ static int rbio_add_io_sector(struct btrfs_raid_bio *rbio,
*/
if (last_end == disk_start && !last->bi_status &&
last->bi_bdev == stripe->dev->bdev) {
- ret = bio_add_page(last, phys_to_page(sector->paddr),
- sectorsize, offset_in_page(sector->paddr));
+ ret = bio_add_paddrs(last, paddrs, rbio->sector_nsteps, step);
if (ret == sectorsize)
return 0;
}
@@ -1197,28 +1296,27 @@ static int rbio_add_io_sector(struct btrfs_raid_bio *rbio,
bio->bi_iter.bi_sector = disk_start >> SECTOR_SHIFT;
bio->bi_private = rbio;
- __bio_add_page(bio, phys_to_page(sector->paddr), sectorsize,
- offset_in_page(sector->paddr));
+ ret = bio_add_paddrs(bio, paddrs, rbio->sector_nsteps, step);
+ ASSERT(ret == sectorsize);
bio_list_add(bio_list, bio);
return 0;
}
static void index_one_bio(struct btrfs_raid_bio *rbio, struct bio *bio)
{
- const u32 sectorsize = rbio->bioc->fs_info->sectorsize;
- const u32 sectorsize_bits = rbio->bioc->fs_info->sectorsize_bits;
+ struct btrfs_fs_info *fs_info = rbio->bioc->fs_info;
+ const u32 step = min(fs_info->sectorsize, PAGE_SIZE);
+ const u32 step_bits = min(fs_info->sectorsize_bits, PAGE_SHIFT);
struct bvec_iter iter = bio->bi_iter;
phys_addr_t paddr;
u32 offset = (bio->bi_iter.bi_sector << SECTOR_SHIFT) -
rbio->bioc->full_stripe_logical;
- btrfs_bio_for_each_block(paddr, bio, &iter, sectorsize) {
- unsigned int index = (offset >> sectorsize_bits);
- struct sector_ptr *sector = &rbio->bio_sectors[index];
+ btrfs_bio_for_each_block(paddr, bio, &iter, step) {
+ unsigned int index = (offset >> step_bits);
- sector->has_paddr = true;
- sector->paddr = paddr;
- offset += sectorsize;
+ rbio->bio_paddrs[index] = paddr;
+ offset += step;
}
}
@@ -1296,56 +1394,64 @@ static void assert_rbio(struct btrfs_raid_bio *rbio)
ASSERT_RBIO(rbio->nr_data < rbio->real_stripes, rbio);
}
-static inline void *kmap_local_sector(const struct sector_ptr *sector)
+static inline void *kmap_local_paddr(phys_addr_t paddr)
{
/* The sector pointer must have a page mapped to it. */
- ASSERT(sector->has_paddr);
+ ASSERT(paddr != INVALID_PADDR);
- return kmap_local_page(phys_to_page(sector->paddr)) +
- offset_in_page(sector->paddr);
+ return kmap_local_page(phys_to_page(paddr)) + offset_in_page(paddr);
}
-/* Generate PQ for one vertical stripe. */
-static void generate_pq_vertical(struct btrfs_raid_bio *rbio, int sectornr)
+static void generate_pq_vertical_step(struct btrfs_raid_bio *rbio, unsigned int sector_nr,
+ unsigned int step_nr)
{
void **pointers = rbio->finish_pointers;
- const u32 sectorsize = rbio->bioc->fs_info->sectorsize;
- struct sector_ptr *sector;
+ const u32 step = min(rbio->bioc->fs_info->sectorsize, PAGE_SIZE);
int stripe;
const bool has_qstripe = rbio->bioc->map_type & BTRFS_BLOCK_GROUP_RAID6;
/* First collect one sector from each data stripe */
- for (stripe = 0; stripe < rbio->nr_data; stripe++) {
- sector = sector_in_rbio(rbio, stripe, sectornr, 0);
- pointers[stripe] = kmap_local_sector(sector);
- }
+ for (stripe = 0; stripe < rbio->nr_data; stripe++)
+ pointers[stripe] = kmap_local_paddr(
+ sector_paddr_in_rbio(rbio, stripe, sector_nr, step_nr, 0));
/* Then add the parity stripe */
- sector = rbio_pstripe_sector(rbio, sectornr);
- sector->uptodate = 1;
- pointers[stripe++] = kmap_local_sector(sector);
+ pointers[stripe++] = kmap_local_paddr(rbio_pstripe_paddr(rbio, sector_nr, step_nr));
if (has_qstripe) {
/*
* RAID6, add the qstripe and call the library function
* to fill in our p/q
*/
- sector = rbio_qstripe_sector(rbio, sectornr);
- sector->uptodate = 1;
- pointers[stripe++] = kmap_local_sector(sector);
+ pointers[stripe++] = kmap_local_paddr(
+ rbio_qstripe_paddr(rbio, sector_nr, step_nr));
assert_rbio(rbio);
- raid6_call.gen_syndrome(rbio->real_stripes, sectorsize,
- pointers);
+ raid6_call.gen_syndrome(rbio->real_stripes, step, pointers);
} else {
/* raid5 */
- memcpy(pointers[rbio->nr_data], pointers[0], sectorsize);
- run_xor(pointers + 1, rbio->nr_data - 1, sectorsize);
+ memcpy(pointers[rbio->nr_data], pointers[0], step);
+ run_xor(pointers + 1, rbio->nr_data - 1, step);
}
for (stripe = stripe - 1; stripe >= 0; stripe--)
kunmap_local(pointers[stripe]);
}
+/* Generate PQ for one vertical stripe. */
+static void generate_pq_vertical(struct btrfs_raid_bio *rbio, int sectornr)
+{
+ const bool has_qstripe = (rbio->bioc->map_type & BTRFS_BLOCK_GROUP_RAID6);
+
+ for (int i = 0; i < rbio->sector_nsteps; i++)
+ generate_pq_vertical_step(rbio, sectornr, i);
+
+ set_bit(rbio_sector_index(rbio, rbio->nr_data, sectornr),
+ rbio->stripe_uptodate_bitmap);
+ if (has_qstripe)
+ set_bit(rbio_sector_index(rbio, rbio->nr_data + 1, sectornr),
+ rbio->stripe_uptodate_bitmap);
+}
+
static int rmw_assemble_write_bios(struct btrfs_raid_bio *rbio,
struct bio_list *bio_list)
{
@@ -1372,7 +1478,7 @@ static int rmw_assemble_write_bios(struct btrfs_raid_bio *rbio,
*/
for (total_sector_nr = 0; total_sector_nr < rbio->nr_sectors;
total_sector_nr++) {
- struct sector_ptr *sector;
+ phys_addr_t *paddrs;
stripe = total_sector_nr / rbio->stripe_nsectors;
sectornr = total_sector_nr % rbio->stripe_nsectors;
@@ -1382,14 +1488,14 @@ static int rmw_assemble_write_bios(struct btrfs_raid_bio *rbio,
continue;
if (stripe < rbio->nr_data) {
- sector = sector_in_rbio(rbio, stripe, sectornr, 1);
- if (!sector)
+ paddrs = sector_paddrs_in_rbio(rbio, stripe, sectornr, 1);
+ if (paddrs == NULL)
continue;
} else {
- sector = rbio_stripe_sector(rbio, stripe, sectornr);
+ paddrs = rbio_stripe_paddrs(rbio, stripe, sectornr);
}
- ret = rbio_add_io_sector(rbio, bio_list, sector, stripe,
+ ret = rbio_add_io_paddrs(rbio, bio_list, paddrs, stripe,
sectornr, REQ_OP_WRITE);
if (ret)
goto error;
@@ -1407,7 +1513,7 @@ static int rmw_assemble_write_bios(struct btrfs_raid_bio *rbio,
for (total_sector_nr = 0; total_sector_nr < rbio->nr_sectors;
total_sector_nr++) {
- struct sector_ptr *sector;
+ phys_addr_t *paddrs;
stripe = total_sector_nr / rbio->stripe_nsectors;
sectornr = total_sector_nr % rbio->stripe_nsectors;
@@ -1432,14 +1538,14 @@ static int rmw_assemble_write_bios(struct btrfs_raid_bio *rbio,
continue;
if (stripe < rbio->nr_data) {
- sector = sector_in_rbio(rbio, stripe, sectornr, 1);
- if (!sector)
+ paddrs = sector_paddrs_in_rbio(rbio, stripe, sectornr, 1);
+ if (paddrs == NULL)
continue;
} else {
- sector = rbio_stripe_sector(rbio, stripe, sectornr);
+ paddrs = rbio_stripe_paddrs(rbio, stripe, sectornr);
}
- ret = rbio_add_io_sector(rbio, bio_list, sector,
+ ret = rbio_add_io_paddrs(rbio, bio_list, paddrs,
rbio->real_stripes,
sectornr, REQ_OP_WRITE);
if (ret)
@@ -1487,21 +1593,17 @@ static void set_rbio_range_error(struct btrfs_raid_bio *rbio, struct bio *bio)
}
/*
- * For subpage case, we can no longer set page Up-to-date directly for
- * stripe_pages[], thus we need to locate the sector.
+ * Return the index inside the rbio->stripe_sectors[] array.
+ *
+ * Return -1 if not found.
*/
-static struct sector_ptr *find_stripe_sector(struct btrfs_raid_bio *rbio,
- phys_addr_t paddr)
+static int find_stripe_sector_nr(struct btrfs_raid_bio *rbio, phys_addr_t paddr)
{
- int i;
-
- for (i = 0; i < rbio->nr_sectors; i++) {
- struct sector_ptr *sector = &rbio->stripe_sectors[i];
-
- if (sector->has_paddr && sector->paddr == paddr)
- return sector;
+ for (int i = 0; i < rbio->nr_sectors; i++) {
+ if (rbio->stripe_paddrs[i * rbio->sector_nsteps] == paddr)
+ return i;
}
- return NULL;
+ return -1;
}
/*
@@ -1510,17 +1612,23 @@ static struct sector_ptr *find_stripe_sector(struct btrfs_raid_bio *rbio,
*/
static void set_bio_pages_uptodate(struct btrfs_raid_bio *rbio, struct bio *bio)
{
- const u32 blocksize = rbio->bioc->fs_info->sectorsize;
+ const u32 sectorsize = rbio->bioc->fs_info->sectorsize;
+ const u32 step = min(sectorsize, PAGE_SIZE);
+ u32 offset = 0;
phys_addr_t paddr;
ASSERT(!bio_flagged(bio, BIO_CLONED));
- btrfs_bio_for_each_block_all(paddr, bio, blocksize) {
- struct sector_ptr *sector = find_stripe_sector(rbio, paddr);
+ btrfs_bio_for_each_block_all(paddr, bio, step) {
+ /* Hitting the first step of a sector. */
+ if (IS_ALIGNED(offset, sectorsize)) {
+ int sector_nr = find_stripe_sector_nr(rbio, paddr);
- ASSERT(sector);
- if (sector)
- sector->uptodate = 1;
+ ASSERT(sector_nr >= 0);
+ if (sector_nr >= 0)
+ set_bit(sector_nr, rbio->stripe_uptodate_bitmap);
+ }
+ offset += step;
}
}
@@ -1530,10 +1638,9 @@ static int get_bio_sector_nr(struct btrfs_raid_bio *rbio, struct bio *bio)
int i;
for (i = 0; i < rbio->nr_sectors; i++) {
- if (rbio->stripe_sectors[i].paddr == bvec_paddr)
+ if (rbio->stripe_paddrs[i * rbio->sector_nsteps] == bvec_paddr)
break;
- if (rbio->bio_sectors[i].has_paddr &&
- rbio->bio_sectors[i].paddr == bvec_paddr)
+ if (rbio->bio_paddrs[i * rbio->sector_nsteps] == bvec_paddr)
break;
}
ASSERT(i < rbio->nr_sectors);
@@ -1566,7 +1673,11 @@ static void verify_bio_data_sectors(struct btrfs_raid_bio *rbio,
struct bio *bio)
{
struct btrfs_fs_info *fs_info = rbio->bioc->fs_info;
+ const u32 step = min(fs_info->sectorsize, PAGE_SIZE);
+ const u32 nr_steps = rbio->sector_nsteps;
int total_sector_nr = get_bio_sector_nr(rbio, bio);
+ u32 offset = 0;
+ phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
phys_addr_t paddr;
/* No data csum for the whole stripe, no need to verify. */
@@ -1577,18 +1688,24 @@ static void verify_bio_data_sectors(struct btrfs_raid_bio *rbio,
if (total_sector_nr >= rbio->nr_data * rbio->stripe_nsectors)
return;
- btrfs_bio_for_each_block_all(paddr, bio, fs_info->sectorsize) {
+ btrfs_bio_for_each_block_all(paddr, bio, step) {
u8 csum_buf[BTRFS_CSUM_SIZE];
- u8 *expected_csum = rbio->csum_buf + total_sector_nr * fs_info->csum_size;
- int ret;
+ u8 *expected_csum;
+
+ paddrs[(offset / step) % nr_steps] = paddr;
+ offset += step;
+
+ /* Not yet covering the full fs block, continue to the next step. */
+ if (!IS_ALIGNED(offset, fs_info->sectorsize))
+ continue;
/* No csum for this sector, skip to the next sector. */
if (!test_bit(total_sector_nr, rbio->csum_bitmap))
continue;
- ret = btrfs_check_block_csum(fs_info, paddr,
- csum_buf, expected_csum);
- if (ret < 0)
+ expected_csum = rbio->csum_buf + total_sector_nr * fs_info->csum_size;
+ btrfs_calculate_block_csum_pages(fs_info, paddrs, csum_buf);
+ if (unlikely(memcmp(csum_buf, expected_csum, fs_info->csum_size) != 0))
set_bit(total_sector_nr, rbio->error_bitmap);
total_sector_nr++;
}
@@ -1785,10 +1902,9 @@ static int verify_one_sector(struct btrfs_raid_bio *rbio,
int stripe_nr, int sector_nr)
{
struct btrfs_fs_info *fs_info = rbio->bioc->fs_info;
- struct sector_ptr *sector;
+ phys_addr_t *paddrs;
u8 csum_buf[BTRFS_CSUM_SIZE];
u8 *csum_expected;
- int ret;
if (!rbio->csum_bitmap || !rbio->csum_buf)
return 0;
@@ -1801,54 +1917,32 @@ static int verify_one_sector(struct btrfs_raid_bio *rbio,
* bio list if possible.
*/
if (rbio->operation == BTRFS_RBIO_READ_REBUILD) {
- sector = sector_in_rbio(rbio, stripe_nr, sector_nr, 0);
+ paddrs = sector_paddrs_in_rbio(rbio, stripe_nr, sector_nr, 0);
} else {
- sector = rbio_stripe_sector(rbio, stripe_nr, sector_nr);
+ paddrs = rbio_stripe_paddrs(rbio, stripe_nr, sector_nr);
}
csum_expected = rbio->csum_buf +
(stripe_nr * rbio->stripe_nsectors + sector_nr) *
fs_info->csum_size;
- ret = btrfs_check_block_csum(fs_info, sector->paddr, csum_buf, csum_expected);
- return ret;
+ btrfs_calculate_block_csum_pages(fs_info, paddrs, csum_buf);
+ if (unlikely(memcmp(csum_buf, csum_expected, fs_info->csum_size) != 0))
+ return -EIO;
+ return 0;
}
-/*
- * Recover a vertical stripe specified by @sector_nr.
- * @*pointers are the pre-allocated pointers by the caller, so we don't
- * need to allocate/free the pointers again and again.
- */
-static int recover_vertical(struct btrfs_raid_bio *rbio, int sector_nr,
- void **pointers, void **unmap_array)
+static void recover_vertical_step(struct btrfs_raid_bio *rbio,
+ unsigned int sector_nr,
+ unsigned int step_nr,
+ int faila, int failb,
+ void **pointers, void **unmap_array)
{
struct btrfs_fs_info *fs_info = rbio->bioc->fs_info;
- struct sector_ptr *sector;
- const u32 sectorsize = fs_info->sectorsize;
- int found_errors;
- int faila;
- int failb;
+ const u32 step = min(fs_info->sectorsize, PAGE_SIZE);
int stripe_nr;
- int ret = 0;
- /*
- * Now we just use bitmap to mark the horizontal stripes in
- * which we have data when doing parity scrub.
- */
- if (rbio->operation == BTRFS_RBIO_PARITY_SCRUB &&
- !test_bit(sector_nr, &rbio->dbitmap))
- return 0;
-
- found_errors = get_rbio_veritical_errors(rbio, sector_nr, &faila,
- &failb);
- /*
- * No errors in the vertical stripe, skip it. Can happen for recovery
- * which only part of a stripe failed csum check.
- */
- if (!found_errors)
- return 0;
-
- if (unlikely(found_errors > rbio->bioc->max_errors))
- return -EIO;
+ ASSERT(step_nr < rbio->sector_nsteps);
+ ASSERT(sector_nr < rbio->stripe_nsectors);
/*
* Setup our array of pointers with sectors from each stripe
@@ -1857,16 +1951,18 @@ static int recover_vertical(struct btrfs_raid_bio *rbio, int sector_nr,
* pointer order.
*/
for (stripe_nr = 0; stripe_nr < rbio->real_stripes; stripe_nr++) {
+ phys_addr_t paddr;
+
/*
* If we're rebuilding a read, we have to use pages from the
* bio list if possible.
*/
if (rbio->operation == BTRFS_RBIO_READ_REBUILD) {
- sector = sector_in_rbio(rbio, stripe_nr, sector_nr, 0);
+ paddr = sector_paddr_in_rbio(rbio, stripe_nr, sector_nr, step_nr, 0);
} else {
- sector = rbio_stripe_sector(rbio, stripe_nr, sector_nr);
+ paddr = rbio_stripe_paddr(rbio, stripe_nr, sector_nr, step_nr);
}
- pointers[stripe_nr] = kmap_local_sector(sector);
+ pointers[stripe_nr] = kmap_local_paddr(paddr);
unmap_array[stripe_nr] = pointers[stripe_nr];
}
@@ -1912,10 +2008,10 @@ static int recover_vertical(struct btrfs_raid_bio *rbio, int sector_nr,
}
if (failb == rbio->real_stripes - 2) {
- raid6_datap_recov(rbio->real_stripes, sectorsize,
+ raid6_datap_recov(rbio->real_stripes, step,
faila, pointers);
} else {
- raid6_2data_recov(rbio->real_stripes, sectorsize,
+ raid6_2data_recov(rbio->real_stripes, step,
faila, failb, pointers);
}
} else {
@@ -1925,7 +2021,7 @@ static int recover_vertical(struct btrfs_raid_bio *rbio, int sector_nr,
ASSERT(failb == -1);
pstripe:
/* Copy parity block into failed block to start with */
- memcpy(pointers[faila], pointers[rbio->nr_data], sectorsize);
+ memcpy(pointers[faila], pointers[rbio->nr_data], step);
/* Rearrange the pointer array */
p = pointers[faila];
@@ -1935,40 +2031,66 @@ pstripe:
pointers[rbio->nr_data - 1] = p;
/* Xor in the rest */
- run_xor(pointers, rbio->nr_data - 1, sectorsize);
-
+ run_xor(pointers, rbio->nr_data - 1, step);
}
+cleanup:
+ for (stripe_nr = rbio->real_stripes - 1; stripe_nr >= 0; stripe_nr--)
+ kunmap_local(unmap_array[stripe_nr]);
+}
+
+/*
+ * Recover a vertical stripe specified by @sector_nr.
+ * @*pointers are the pre-allocated pointers by the caller, so we don't
+ * need to allocate/free the pointers again and again.
+ */
+static int recover_vertical(struct btrfs_raid_bio *rbio, int sector_nr,
+ void **pointers, void **unmap_array)
+{
+ int found_errors;
+ int faila;
+ int failb;
+ int ret = 0;
+
/*
- * No matter if this is a RMW or recovery, we should have all
- * failed sectors repaired in the vertical stripe, thus they are now
- * uptodate.
- * Especially if we determine to cache the rbio, we need to
- * have at least all data sectors uptodate.
- *
- * If possible, also check if the repaired sector matches its data
- * checksum.
+ * Now we just use bitmap to mark the horizontal stripes in
+ * which we have data when doing parity scrub.
+ */
+ if (rbio->operation == BTRFS_RBIO_PARITY_SCRUB &&
+ !test_bit(sector_nr, &rbio->dbitmap))
+ return 0;
+
+ found_errors = get_rbio_vertical_errors(rbio, sector_nr, &faila,
+ &failb);
+ /*
+ * No errors in the vertical stripe, skip it. Can happen for recovery
+ * which only part of a stripe failed csum check.
*/
+ if (!found_errors)
+ return 0;
+
+ if (unlikely(found_errors > rbio->bioc->max_errors))
+ return -EIO;
+
+ for (int i = 0; i < rbio->sector_nsteps; i++)
+ recover_vertical_step(rbio, sector_nr, i, faila, failb,
+ pointers, unmap_array);
if (faila >= 0) {
ret = verify_one_sector(rbio, faila, sector_nr);
if (ret < 0)
- goto cleanup;
+ return ret;
- sector = rbio_stripe_sector(rbio, faila, sector_nr);
- sector->uptodate = 1;
+ set_bit(rbio_sector_index(rbio, faila, sector_nr),
+ rbio->stripe_uptodate_bitmap);
}
if (failb >= 0) {
ret = verify_one_sector(rbio, failb, sector_nr);
if (ret < 0)
- goto cleanup;
+ return ret;
- sector = rbio_stripe_sector(rbio, failb, sector_nr);
- sector->uptodate = 1;
+ set_bit(rbio_sector_index(rbio, failb, sector_nr),
+ rbio->stripe_uptodate_bitmap);
}
-
-cleanup:
- for (stripe_nr = rbio->real_stripes - 1; stripe_nr >= 0; stripe_nr--)
- kunmap_local(unmap_array[stripe_nr]);
return ret;
}
@@ -2043,7 +2165,7 @@ static void recover_rbio(struct btrfs_raid_bio *rbio)
total_sector_nr++) {
int stripe = total_sector_nr / rbio->stripe_nsectors;
int sectornr = total_sector_nr % rbio->stripe_nsectors;
- struct sector_ptr *sector;
+ phys_addr_t *paddrs;
/*
* Skip the range which has error. It can be a range which is
@@ -2060,8 +2182,8 @@ static void recover_rbio(struct btrfs_raid_bio *rbio)
continue;
}
- sector = rbio_stripe_sector(rbio, stripe, sectornr);
- ret = rbio_add_io_sector(rbio, &bio_list, sector, stripe,
+ paddrs = rbio_stripe_paddrs(rbio, stripe, sectornr);
+ ret = rbio_add_io_paddrs(rbio, &bio_list, paddrs, stripe,
sectornr, REQ_OP_READ);
if (ret < 0) {
bio_list_put(&bio_list);
@@ -2106,7 +2228,7 @@ static void set_rbio_raid6_extra_error(struct btrfs_raid_bio *rbio, int mirror_n
int faila;
int failb;
- found_errors = get_rbio_veritical_errors(rbio, sector_nr,
+ found_errors = get_rbio_vertical_errors(rbio, sector_nr,
&faila, &failb);
/* This vertical stripe doesn't have errors. */
if (!found_errors)
@@ -2250,13 +2372,13 @@ static int rmw_read_wait_recover(struct btrfs_raid_bio *rbio)
*/
for (total_sector_nr = 0; total_sector_nr < rbio->nr_sectors;
total_sector_nr++) {
- struct sector_ptr *sector;
int stripe = total_sector_nr / rbio->stripe_nsectors;
int sectornr = total_sector_nr % rbio->stripe_nsectors;
+ phys_addr_t *paddrs;
- sector = rbio_stripe_sector(rbio, stripe, sectornr);
- ret = rbio_add_io_sector(rbio, &bio_list, sector,
- stripe, sectornr, REQ_OP_READ);
+ paddrs = rbio_stripe_paddrs(rbio, stripe, sectornr);
+ ret = rbio_add_io_paddrs(rbio, &bio_list, paddrs, stripe,
+ sectornr, REQ_OP_READ);
if (ret) {
bio_list_put(&bio_list);
return ret;
@@ -2310,14 +2432,15 @@ static bool need_read_stripe_sectors(struct btrfs_raid_bio *rbio)
int i;
for (i = 0; i < rbio->nr_data * rbio->stripe_nsectors; i++) {
- struct sector_ptr *sector = &rbio->stripe_sectors[i];
+ phys_addr_t paddr = rbio->stripe_paddrs[i * rbio->sector_nsteps];
/*
* We have a sector which doesn't have page nor uptodate,
* thus this rbio can not be cached one, as cached one must
* have all its data sectors present and uptodate.
*/
- if (!sector->has_paddr || !sector->uptodate)
+ if (paddr == INVALID_PADDR ||
+ !test_bit(i, rbio->stripe_uptodate_bitmap))
return true;
}
return false;
@@ -2398,7 +2521,7 @@ static void rmw_rbio(struct btrfs_raid_bio *rbio)
for (sectornr = 0; sectornr < rbio->stripe_nsectors; sectornr++) {
int found_errors;
- found_errors = get_rbio_veritical_errors(rbio, sectornr, NULL, NULL);
+ found_errors = get_rbio_vertical_errors(rbio, sectornr, NULL, NULL);
if (unlikely(found_errors > rbio->bioc->max_errors)) {
ret = -EIO;
break;
@@ -2469,47 +2592,121 @@ struct btrfs_raid_bio *raid56_parity_alloc_scrub_rbio(struct bio *bio,
return rbio;
}
+static int alloc_rbio_sector_pages(struct btrfs_raid_bio *rbio,
+ int sector_nr)
+{
+ const u32 step = min(PAGE_SIZE, rbio->bioc->fs_info->sectorsize);
+ const u32 base = sector_nr * rbio->sector_nsteps;
+
+ for (int i = base; i < base + rbio->sector_nsteps; i++) {
+ const unsigned int page_index = (i * step) >> PAGE_SHIFT;
+ struct page *page;
+
+ if (rbio->stripe_pages[page_index])
+ continue;
+ page = alloc_page(GFP_NOFS);
+ if (!page)
+ return -ENOMEM;
+ rbio->stripe_pages[page_index] = page;
+ }
+ return 0;
+}
+
/*
* We just scrub the parity that we have correct data on the same horizontal,
* so we needn't allocate all pages for all the stripes.
*/
static int alloc_rbio_essential_pages(struct btrfs_raid_bio *rbio)
{
- const u32 sectorsize = rbio->bioc->fs_info->sectorsize;
int total_sector_nr;
for (total_sector_nr = 0; total_sector_nr < rbio->nr_sectors;
total_sector_nr++) {
- struct page *page;
int sectornr = total_sector_nr % rbio->stripe_nsectors;
- int index = (total_sector_nr * sectorsize) >> PAGE_SHIFT;
+ int ret;
if (!test_bit(sectornr, &rbio->dbitmap))
continue;
- if (rbio->stripe_pages[index])
- continue;
- page = alloc_page(GFP_NOFS);
- if (!page)
- return -ENOMEM;
- rbio->stripe_pages[index] = page;
+ ret = alloc_rbio_sector_pages(rbio, total_sector_nr);
+ if (ret < 0)
+ return ret;
}
index_stripe_sectors(rbio);
return 0;
}
+/* Return true if the content of the step matches the caclulated one. */
+static bool verify_one_parity_step(struct btrfs_raid_bio *rbio,
+ void *pointers[], unsigned int sector_nr,
+ unsigned int step_nr)
+{
+ const unsigned int nr_data = rbio->nr_data;
+ const bool has_qstripe = (rbio->real_stripes - rbio->nr_data == 2);
+ const u32 step = min(rbio->bioc->fs_info->sectorsize, PAGE_SIZE);
+ void *parity;
+ bool ret = false;
+
+ ASSERT(step_nr < rbio->sector_nsteps);
+
+ /* First collect one page from each data stripe. */
+ for (int stripe = 0; stripe < nr_data; stripe++)
+ pointers[stripe] = kmap_local_paddr(
+ sector_paddr_in_rbio(rbio, stripe, sector_nr,
+ step_nr, 0));
+
+ if (has_qstripe) {
+ assert_rbio(rbio);
+ /* RAID6, call the library function to fill in our P/Q. */
+ raid6_call.gen_syndrome(rbio->real_stripes, step, pointers);
+ } else {
+ /* RAID5. */
+ memcpy(pointers[nr_data], pointers[0], step);
+ run_xor(pointers + 1, nr_data - 1, step);
+ }
+
+ /* Check scrubbing parity and repair it. */
+ parity = kmap_local_paddr(rbio_stripe_paddr(rbio, rbio->scrubp, sector_nr, step_nr));
+ if (memcmp(parity, pointers[rbio->scrubp], step) != 0)
+ memcpy(parity, pointers[rbio->scrubp], step);
+ else
+ ret = true;
+ kunmap_local(parity);
+
+ for (int stripe = nr_data - 1; stripe >= 0; stripe--)
+ kunmap_local(pointers[stripe]);
+ return ret;
+}
+
+/*
+ * The @pointers array should have the P/Q parity already mapped.
+ */
+static void verify_one_parity_sector(struct btrfs_raid_bio *rbio,
+ void *pointers[], unsigned int sector_nr)
+{
+ bool found_error = false;
+
+ for (int step_nr = 0; step_nr < rbio->sector_nsteps; step_nr++) {
+ bool match;
+
+ match = verify_one_parity_step(rbio, pointers, sector_nr, step_nr);
+ if (!match)
+ found_error = true;
+ }
+ if (!found_error)
+ bitmap_clear(&rbio->dbitmap, sector_nr, 1);
+}
+
static int finish_parity_scrub(struct btrfs_raid_bio *rbio)
{
struct btrfs_io_context *bioc = rbio->bioc;
- const u32 sectorsize = bioc->fs_info->sectorsize;
void **pointers = rbio->finish_pointers;
unsigned long *pbitmap = &rbio->finish_pbitmap;
int nr_data = rbio->nr_data;
- int stripe;
int sectornr;
bool has_qstripe;
struct page *page;
- struct sector_ptr p_sector = { 0 };
- struct sector_ptr q_sector = { 0 };
+ phys_addr_t p_paddr = INVALID_PADDR;
+ phys_addr_t q_paddr = INVALID_PADDR;
struct bio_list bio_list;
int is_replace = 0;
int ret;
@@ -2542,72 +2739,36 @@ static int finish_parity_scrub(struct btrfs_raid_bio *rbio)
page = alloc_page(GFP_NOFS);
if (!page)
return -ENOMEM;
- p_sector.has_paddr = true;
- p_sector.paddr = page_to_phys(page);
- p_sector.uptodate = 1;
+ p_paddr = page_to_phys(page);
page = NULL;
+ pointers[nr_data] = kmap_local_paddr(p_paddr);
if (has_qstripe) {
/* RAID6, allocate and map temp space for the Q stripe */
page = alloc_page(GFP_NOFS);
if (!page) {
- __free_page(phys_to_page(p_sector.paddr));
- p_sector.has_paddr = false;
+ __free_page(phys_to_page(p_paddr));
+ p_paddr = INVALID_PADDR;
return -ENOMEM;
}
- q_sector.has_paddr = true;
- q_sector.paddr = page_to_phys(page);
- q_sector.uptodate = 1;
+ q_paddr = page_to_phys(page);
page = NULL;
- pointers[rbio->real_stripes - 1] = kmap_local_sector(&q_sector);
+ pointers[rbio->real_stripes - 1] = kmap_local_paddr(q_paddr);
}
bitmap_clear(rbio->error_bitmap, 0, rbio->nr_sectors);
/* Map the parity stripe just once */
- pointers[nr_data] = kmap_local_sector(&p_sector);
-
- for_each_set_bit(sectornr, &rbio->dbitmap, rbio->stripe_nsectors) {
- struct sector_ptr *sector;
- void *parity;
-
- /* first collect one page from each data stripe */
- for (stripe = 0; stripe < nr_data; stripe++) {
- sector = sector_in_rbio(rbio, stripe, sectornr, 0);
- pointers[stripe] = kmap_local_sector(sector);
- }
- if (has_qstripe) {
- assert_rbio(rbio);
- /* RAID6, call the library function to fill in our P/Q */
- raid6_call.gen_syndrome(rbio->real_stripes, sectorsize,
- pointers);
- } else {
- /* raid5 */
- memcpy(pointers[nr_data], pointers[0], sectorsize);
- run_xor(pointers + 1, nr_data - 1, sectorsize);
- }
-
- /* Check scrubbing parity and repair it */
- sector = rbio_stripe_sector(rbio, rbio->scrubp, sectornr);
- parity = kmap_local_sector(sector);
- if (memcmp(parity, pointers[rbio->scrubp], sectorsize) != 0)
- memcpy(parity, pointers[rbio->scrubp], sectorsize);
- else
- /* Parity is right, needn't writeback */
- bitmap_clear(&rbio->dbitmap, sectornr, 1);
- kunmap_local(parity);
-
- for (stripe = nr_data - 1; stripe >= 0; stripe--)
- kunmap_local(pointers[stripe]);
- }
+ for_each_set_bit(sectornr, &rbio->dbitmap, rbio->stripe_nsectors)
+ verify_one_parity_sector(rbio, pointers, sectornr);
kunmap_local(pointers[nr_data]);
- __free_page(phys_to_page(p_sector.paddr));
- p_sector.has_paddr = false;
- if (q_sector.has_paddr) {
- __free_page(phys_to_page(q_sector.paddr));
- q_sector.has_paddr = false;
+ __free_page(phys_to_page(p_paddr));
+ p_paddr = INVALID_PADDR;
+ if (q_paddr != INVALID_PADDR) {
+ __free_page(phys_to_page(q_paddr));
+ q_paddr = INVALID_PADDR;
}
/*
@@ -2616,10 +2777,10 @@ static int finish_parity_scrub(struct btrfs_raid_bio *rbio)
* everything else.
*/
for_each_set_bit(sectornr, &rbio->dbitmap, rbio->stripe_nsectors) {
- struct sector_ptr *sector;
+ phys_addr_t *paddrs;
- sector = rbio_stripe_sector(rbio, rbio->scrubp, sectornr);
- ret = rbio_add_io_sector(rbio, &bio_list, sector, rbio->scrubp,
+ paddrs = rbio_stripe_paddrs(rbio, rbio->scrubp, sectornr);
+ ret = rbio_add_io_paddrs(rbio, &bio_list, paddrs, rbio->scrubp,
sectornr, REQ_OP_WRITE);
if (ret)
goto cleanup;
@@ -2634,11 +2795,10 @@ static int finish_parity_scrub(struct btrfs_raid_bio *rbio)
*/
ASSERT_RBIO(rbio->bioc->replace_stripe_src >= 0, rbio);
for_each_set_bit(sectornr, pbitmap, rbio->stripe_nsectors) {
- struct sector_ptr *sector;
+ phys_addr_t *paddrs;
- sector = rbio_stripe_sector(rbio, rbio->scrubp, sectornr);
- ret = rbio_add_io_sector(rbio, &bio_list, sector,
- rbio->real_stripes,
+ paddrs = rbio_stripe_paddrs(rbio, rbio->scrubp, sectornr);
+ ret = rbio_add_io_paddrs(rbio, &bio_list, paddrs, rbio->real_stripes,
sectornr, REQ_OP_WRITE);
if (ret)
goto cleanup;
@@ -2686,7 +2846,7 @@ static int recover_scrub_rbio(struct btrfs_raid_bio *rbio)
int failb;
int found_errors;
- found_errors = get_rbio_veritical_errors(rbio, sector_nr,
+ found_errors = get_rbio_vertical_errors(rbio, sector_nr,
&faila, &failb);
if (unlikely(found_errors > rbio->bioc->max_errors)) {
ret = -EIO;
@@ -2755,7 +2915,7 @@ static int scrub_assemble_read_bios(struct btrfs_raid_bio *rbio)
total_sector_nr++) {
int sectornr = total_sector_nr % rbio->stripe_nsectors;
int stripe = total_sector_nr / rbio->stripe_nsectors;
- struct sector_ptr *sector;
+ phys_addr_t *paddrs;
/* No data in the vertical stripe, no need to read. */
if (!test_bit(sectornr, &rbio->dbitmap))
@@ -2763,22 +2923,23 @@ static int scrub_assemble_read_bios(struct btrfs_raid_bio *rbio)
/*
* We want to find all the sectors missing from the rbio and
- * read them from the disk. If sector_in_rbio() finds a sector
+ * read them from the disk. If sector_paddr_in_rbio() finds a sector
* in the bio list we don't need to read it off the stripe.
*/
- sector = sector_in_rbio(rbio, stripe, sectornr, 1);
- if (sector)
+ paddrs = sector_paddrs_in_rbio(rbio, stripe, sectornr, 1);
+ if (paddrs == NULL)
continue;
- sector = rbio_stripe_sector(rbio, stripe, sectornr);
+ paddrs = rbio_stripe_paddrs(rbio, stripe, sectornr);
/*
* The bio cache may have handed us an uptodate sector. If so,
* use it.
*/
- if (sector->uptodate)
+ if (test_bit(rbio_sector_index(rbio, stripe, sectornr),
+ rbio->stripe_uptodate_bitmap))
continue;
- ret = rbio_add_io_sector(rbio, &bio_list, sector, stripe,
+ ret = rbio_add_io_paddrs(rbio, &bio_list, paddrs, stripe,
sectornr, REQ_OP_READ);
if (ret) {
bio_list_put(&bio_list);
@@ -2819,7 +2980,7 @@ static void scrub_rbio(struct btrfs_raid_bio *rbio)
for (sector_nr = 0; sector_nr < rbio->stripe_nsectors; sector_nr++) {
int found_errors;
- found_errors = get_rbio_veritical_errors(rbio, sector_nr, NULL, NULL);
+ found_errors = get_rbio_vertical_errors(rbio, sector_nr, NULL, NULL);
if (unlikely(found_errors > rbio->bioc->max_errors)) {
ret = -EIO;
break;
@@ -2857,9 +3018,6 @@ void raid56_parity_cache_data_folios(struct btrfs_raid_bio *rbio,
unsigned int foffset = 0;
int ret;
- /* We shouldn't hit RAID56 for bs > ps cases for now. */
- ASSERT(fs_info->sectorsize <= PAGE_SIZE);
-
/*
* If we hit ENOMEM temporarily, but later at
* raid56_parity_submit_scrub_rbio() time it succeeded, we just do
@@ -2893,8 +3051,7 @@ void raid56_parity_cache_data_folios(struct btrfs_raid_bio *rbio,
foffset = 0;
}
}
- for (unsigned int sector_nr = offset_in_full_stripe >> fs_info->sectorsize_bits;
- sector_nr < (offset_in_full_stripe + BTRFS_STRIPE_LEN) >> fs_info->sectorsize_bits;
- sector_nr++)
- rbio->stripe_sectors[sector_nr].uptodate = true;
+ bitmap_set(rbio->stripe_uptodate_bitmap,
+ offset_in_full_stripe >> fs_info->sectorsize_bits,
+ BTRFS_STRIPE_LEN >> fs_info->sectorsize_bits);
}
diff --git a/fs/btrfs/raid56.h b/fs/btrfs/raid56.h
index 84c4d1d29c7a..1f463ecf7e41 100644
--- a/fs/btrfs/raid56.h
+++ b/fs/btrfs/raid56.h
@@ -16,7 +16,6 @@
#include "volumes.h"
struct page;
-struct sector_ptr;
struct btrfs_fs_info;
enum btrfs_rbio_ops {
@@ -25,6 +24,84 @@ enum btrfs_rbio_ops {
BTRFS_RBIO_PARITY_SCRUB,
};
+/*
+ * Overview of btrfs_raid_bio.
+ *
+ * One btrfs_raid_bio represents a full stripe of RAID56, including both data
+ * and P/Q stripes. For now, each data and P/Q stripe is of a fixed length (64K).
+ *
+ * One btrfs_raid_bio can have one or more bios from higher layer, covering
+ * part or all of the data stripes.
+ *
+ * [PAGES FROM HIGHER LAYER BIOS]
+ * Higher layer bios are in the btrfs_raid_bio::bio_list.
+ *
+ * Pages from the bio_list are represented like the following:
+ *
+ * bio_list: |<- Bio 1 ->| |<- Bio 2 ->| ...
+ * bio_paddrs: [0] [1] [2] [3] [4] [5] ...
+ *
+ * If there is a bio covering a sector (one btrfs fs block), the corresponding
+ * pointer in btrfs_raid_bio::bio_paddrs[] will point to the physical address
+ * (with the offset inside the page) of the corresponding bio.
+ *
+ * If there is no bio covering a sector, then btrfs_raid_bio::bio_paddrs[i] will
+ * be INVALID_PADDR.
+ *
+ * The length of each entry in bio_paddrs[] is a step (aka, min(sectorsize, PAGE_SIZE)).
+ *
+ * [PAGES FOR INTERNAL USAGES]
+ * Pages not covered by any bio or belonging to P/Q stripes are stored in
+ * btrfs_raid_bio::stripe_pages[] and stripe_paddrs[], like the following:
+ *
+ * stripe_pages: |<- Page 0 ->|<- Page 1 ->| ...
+ * stripe_paddrs: [0] [1] [2] [3] [4] ...
+ *
+ * stripe_pages[] array stores all the pages covering the full stripe, including
+ * data and P/Q pages.
+ * stripe_pages[0] is the first page of the first data stripe.
+ * stripe_pages[BTRFS_STRIPE_LEN / PAGE_SIZE] is the first page of the second
+ * data stripe.
+ *
+ * Some pointers inside stripe_pages[] can be NULL, e.g. for a full stripe write
+ * (the bio covers all data stripes) there is no need to allocate pages for
+ * data stripes (can grab from bio_paddrs[]).
+ *
+ * If the corresponding page of stripe_paddrs[i] is not allocated, the value of
+ * stripe_paddrs[i] will be INVALID_PADDR.
+ *
+ * The length of each entry in stripe_paddrs[] is a step.
+ *
+ * [LOCATING A SECTOR]
+ * To locate a sector for IO, we need the following info:
+ *
+ * - stripe_nr
+ * Starts from 0 (representing the first data stripe), ends at
+ * @nr_data (RAID5, P stripe) or @nr_data + 1 (RAID6, Q stripe).
+ *
+ * - sector_nr
+ * Starts from 0 (representing the first sector of the stripe), ends
+ * at BTRFS_STRIPE_LEN / sectorsize - 1.
+ *
+ * - step_nr
+ * A step is min(sector_size, PAGE_SIZE).
+ *
+ * Starts from 0 (representing the first step of the sector), ends
+ * at @sector_nsteps - 1.
+ *
+ * For most call sites they do not need to bother this parameter.
+ * It is for bs > ps support and only for vertical stripe related works.
+ * (e.g. RMW/recover)
+ *
+ * - from which array
+ * Whether grabbing from stripe_paddrs[] (aka, internal pages) or from the
+ * bio_paddrs[] (aka, from the higher layer bios).
+ *
+ * For IO, a physical address is returned, so that we can extract the page and
+ * the offset inside the page for IO.
+ * A special value INVALID_PADDR represents when the physical address is invalid,
+ * normally meaning there is no page allocated for the specified sector.
+ */
struct btrfs_raid_bio {
struct btrfs_io_context *bioc;
@@ -82,6 +159,14 @@ struct btrfs_raid_bio {
/* How many sectors there are for each stripe */
u8 stripe_nsectors;
+ /*
+ * How many steps there are for one sector.
+ *
+ * For bs > ps cases, it's sectorsize / PAGE_SIZE.
+ * For bs <= ps cases, it's always 1.
+ */
+ u8 sector_nsteps;
+
/* Stripe number that we're scrubbing */
u8 scrubp;
@@ -116,13 +201,13 @@ struct btrfs_raid_bio {
struct page **stripe_pages;
/* Pointers to the sectors in the bio_list, for faster lookup */
- struct sector_ptr *bio_sectors;
+ phys_addr_t *bio_paddrs;
- /*
- * For subpage support, we need to map each sector to above
- * stripe_pages.
- */
- struct sector_ptr *stripe_sectors;
+ /* Pointers to the sectors in the stripe_pages[]. */
+ phys_addr_t *stripe_paddrs;
+
+ /* Each set bit means the corresponding sector in stripe_sectors[] is uptodate. */
+ unsigned long *stripe_uptodate_bitmap;
/* Allocated with real_stripes-many pointers for finish_*() calls */
void **finish_pointers;
@@ -131,10 +216,6 @@ struct btrfs_raid_bio {
* The bitmap recording where IO errors happened.
* Each bit is corresponding to one sector in either bio_sectors[] or
* stripe_sectors[] array.
- *
- * The reason we don't use another bit in sector_ptr is, we have two
- * arrays of sectors, and a lot of IO can use sectors in both arrays.
- * Thus making it much harder to iterate.
*/
unsigned long *error_bitmap;
diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c
index 5465a5eae9b2..b5fe95baf92e 100644
--- a/fs/btrfs/reflink.c
+++ b/fs/btrfs/reflink.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/blkdev.h>
+#include <linux/fscrypt.h>
#include <linux/iversion.h>
#include "ctree.h"
#include "fs.h"
@@ -343,7 +344,7 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
BTRFS_PATH_AUTO_FREE(path);
struct extent_buffer *leaf;
struct btrfs_trans_handle *trans;
- char *buf = NULL;
+ char AUTO_KVFREE(buf);
struct btrfs_key key;
u32 nritems;
int slot;
@@ -358,10 +359,8 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
return ret;
path = btrfs_alloc_path();
- if (!path) {
- kvfree(buf);
+ if (!path)
return ret;
- }
path->reada = READA_FORWARD;
/* Clone data */
@@ -611,7 +610,6 @@ process_slot:
}
out:
- kvfree(buf);
clear_bit(BTRFS_INODE_NO_DELALLOC_FLUSH, &BTRFS_I(inode)->runtime_flags);
return ret;
@@ -792,6 +790,10 @@ static int btrfs_remap_file_range_prep(struct file *file_in, loff_t pos_in,
ASSERT(inode_in->vfs_inode.i_sb == inode_out->vfs_inode.i_sb);
}
+ /* Can only reflink encrypted files if both files are encrypted. */
+ if (IS_ENCRYPTED(&inode_in->vfs_inode) != IS_ENCRYPTED(&inode_out->vfs_inode))
+ return -EINVAL;
+
/* Don't make the dst file partly checksummed */
if ((inode_in->flags & BTRFS_INODE_NODATASUM) !=
(inode_out->flags & BTRFS_INODE_NODATASUM)) {
@@ -868,6 +870,9 @@ loff_t btrfs_remap_file_range(struct file *src_file, loff_t off,
bool same_inode = dst_inode == src_inode;
int ret;
+ if (unlikely(btrfs_is_shutdown(inode_to_fs_info(file_inode(src_file)))))
+ return -EIO;
+
if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY))
return -EINVAL;
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 0765e06d00b8..5bfefc3e9c06 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -511,7 +511,7 @@ static void __del_reloc_root(struct btrfs_root *root)
{
struct btrfs_fs_info *fs_info = root->fs_info;
struct rb_node *rb_node;
- struct mapping_node *node = NULL;
+ struct mapping_node AUTO_KFREE(node);
struct reloc_control *rc = fs_info->reloc_ctl;
bool put_ref = false;
@@ -544,7 +544,6 @@ static void __del_reloc_root(struct btrfs_root *root)
spin_unlock(&fs_info->trans_lock);
if (put_ref)
btrfs_put_root(root);
- kfree(node);
}
/*
@@ -586,10 +585,9 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_root *reloc_root;
struct extent_buffer *eb;
- struct btrfs_root_item *root_item;
+ struct btrfs_root_item AUTO_KFREE(root_item);
struct btrfs_key root_key;
int ret = 0;
- bool must_abort = false;
root_item = kmalloc(sizeof(*root_item), GFP_NOFS);
if (!root_item)
@@ -615,17 +613,16 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
btrfs_disk_key_to_cpu(&cpu_key, &root->root_item.drop_progress);
btrfs_err(fs_info,
- "cannot relocate partially dropped subvolume %llu, drop progress key (%llu %u %llu)",
- objectid, cpu_key.objectid, cpu_key.type, cpu_key.offset);
- ret = -EUCLEAN;
- goto fail;
+ "cannot relocate partially dropped subvolume %llu, drop progress key " BTRFS_KEY_FMT,
+ objectid, BTRFS_KEY_FMT_VALUE(&cpu_key));
+ return ERR_PTR(-EUCLEAN);
}
/* called by btrfs_init_reloc_root */
ret = btrfs_copy_root(trans, root, root->commit_root, &eb,
BTRFS_TREE_RELOC_OBJECTID);
if (ret)
- goto fail;
+ return ERR_PTR(ret);
/*
* Set the last_snapshot field to the generation of the commit
@@ -648,14 +645,13 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
ret = btrfs_copy_root(trans, root, root->node, &eb,
BTRFS_TREE_RELOC_OBJECTID);
if (ret)
- goto fail;
+ return ERR_PTR(ret);
}
/*
* We have changed references at this point, we must abort the
- * transaction if anything fails.
+ * transaction if anything fails (i.e. 'goto abort').
*/
- must_abort = true;
memcpy(root_item, &root->root_item, sizeof(*root_item));
btrfs_set_root_bytenr(root_item, eb->start);
@@ -675,9 +671,7 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
ret = btrfs_insert_root(trans, fs_info->tree_root,
&root_key, root_item);
if (ret)
- goto fail;
-
- kfree(root_item);
+ goto abort;
reloc_root = btrfs_read_tree_root(fs_info->tree_root, &root_key);
if (IS_ERR(reloc_root)) {
@@ -687,11 +681,9 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
set_bit(BTRFS_ROOT_SHAREABLE, &reloc_root->state);
btrfs_set_root_last_trans(reloc_root, trans->transid);
return reloc_root;
-fail:
- kfree(root_item);
+
abort:
- if (must_abort)
- btrfs_abort_transaction(trans, ret);
+ btrfs_abort_transaction(trans, ret);
return ERR_PTR(ret);
}
@@ -2947,7 +2939,7 @@ static int relocate_file_extent_cluster(struct reloc_control *rc)
const struct file_extent_cluster *cluster = &rc->cluster;
u64 offset = BTRFS_I(inode)->reloc_block_group_start;
u64 cur_file_offset = cluster->start - offset;
- struct file_ra_state *ra;
+ struct file_ra_state AUTO_KFREE(ra);
int cluster_nr = 0;
int ret = 0;
@@ -2960,13 +2952,13 @@ static int relocate_file_extent_cluster(struct reloc_control *rc)
ret = prealloc_file_extent_cluster(rc);
if (ret)
- goto out;
+ return ret;
file_ra_state_init(ra, inode->i_mapping);
ret = setup_relocation_extent_mapping(rc);
if (ret)
- goto out;
+ return ret;
while (cur_file_offset < cluster->end - offset) {
ret = relocate_one_folio(rc, ra, &cluster_nr, &cur_file_offset);
@@ -2975,8 +2967,6 @@ static int relocate_file_extent_cluster(struct reloc_control *rc)
}
if (ret == 0)
WARN_ON(cluster_nr != cluster->nr);
-out:
- kfree(ra);
return ret;
}
@@ -3175,8 +3165,8 @@ again:
key.offset = blocksize;
}
- path->search_commit_root = 1;
- path->skip_locking = 1;
+ path->search_commit_root = true;
+ path->skip_locking = true;
ret = btrfs_search_slot(NULL, rc->extent_root, &key, path, 0, 0);
if (ret < 0)
return ret;
@@ -3368,8 +3358,8 @@ int find_next_extent(struct reloc_control *rc, struct btrfs_path *path,
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = 0;
- path->search_commit_root = 1;
- path->skip_locking = 1;
+ path->search_commit_root = true;
+ path->skip_locking = true;
ret = btrfs_search_slot(NULL, rc->extent_root, &key, path,
0, 0);
if (ret < 0)
@@ -3882,8 +3872,7 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start,
struct inode *inode;
struct btrfs_path *path;
int ret;
- int rw = 0;
- int err = 0;
+ bool bg_is_ro = false;
/*
* This only gets set if we had a half-deleted snapshot on mount. We
@@ -3925,24 +3914,20 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start,
}
ret = reloc_chunk_start(fs_info);
- if (ret < 0) {
- err = ret;
+ if (ret < 0)
goto out_put_bg;
- }
rc->extent_root = extent_root;
rc->block_group = bg;
ret = btrfs_inc_block_group_ro(rc->block_group, true);
- if (ret) {
- err = ret;
+ if (ret)
goto out;
- }
- rw = 1;
+ bg_is_ro = true;
path = btrfs_alloc_path();
if (!path) {
- err = -ENOMEM;
+ ret = -ENOMEM;
goto out;
}
@@ -3954,14 +3939,12 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start,
else
ret = PTR_ERR(inode);
- if (ret && ret != -ENOENT) {
- err = ret;
+ if (ret && ret != -ENOENT)
goto out;
- }
rc->data_inode = create_reloc_inode(rc->block_group);
if (IS_ERR(rc->data_inode)) {
- err = PTR_ERR(rc->data_inode);
+ ret = PTR_ERR(rc->data_inode);
rc->data_inode = NULL;
goto out;
}
@@ -3982,8 +3965,6 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start,
mutex_lock(&fs_info->cleaner_mutex);
ret = relocate_block_group(rc);
mutex_unlock(&fs_info->cleaner_mutex);
- if (ret < 0)
- err = ret;
finishes_stage = rc->stage;
/*
@@ -3996,16 +3977,18 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start,
* out of the loop if we hit an error.
*/
if (rc->stage == MOVE_DATA_EXTENTS && rc->found_file_extent) {
- ret = btrfs_wait_ordered_range(BTRFS_I(rc->data_inode), 0,
- (u64)-1);
- if (ret)
- err = ret;
+ int wb_ret;
+
+ wb_ret = btrfs_wait_ordered_range(BTRFS_I(rc->data_inode), 0,
+ (u64)-1);
+ if (wb_ret && ret == 0)
+ ret = wb_ret;
invalidate_mapping_pages(rc->data_inode->i_mapping,
0, -1);
rc->stage = UPDATE_DATA_PTRS;
}
- if (err < 0)
+ if (ret < 0)
goto out;
if (rc->extents_found == 0)
@@ -4021,14 +4004,14 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start,
WARN_ON(rc->block_group->reserved > 0);
WARN_ON(rc->block_group->used > 0);
out:
- if (err && rw)
+ if (ret && bg_is_ro)
btrfs_dec_block_group_ro(rc->block_group);
iput(rc->data_inode);
reloc_chunk_end(fs_info);
out_put_bg:
btrfs_put_block_group(bg);
free_reloc_control(rc);
- return err;
+ return ret;
}
static noinline_for_stack int mark_garbage_root(struct btrfs_root *root)
diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c
index d07eab70f759..6a7e297ab0a7 100644
--- a/fs/btrfs/root-tree.c
+++ b/fs/btrfs/root-tree.c
@@ -147,8 +147,8 @@ int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root
if (unlikely(ret > 0)) {
btrfs_crit(fs_info,
- "unable to find root key (%llu %u %llu) in tree %llu",
- key->objectid, key->type, key->offset, btrfs_root_id(root));
+ "unable to find root key " BTRFS_KEY_FMT " in tree %llu",
+ BTRFS_KEY_FMT_VALUE(key), btrfs_root_id(root));
ret = -EUCLEAN;
btrfs_abort_transaction(trans, ret);
return ret;
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
index ba20d9286a34..a40ee41f42c6 100644
--- a/fs/btrfs/scrub.c
+++ b/fs/btrfs/scrub.c
@@ -463,10 +463,10 @@ static noinline_for_stack struct scrub_ctx *scrub_setup_ctx(
refcount_set(&sctx->refs, 1);
sctx->is_dev_replace = is_dev_replace;
sctx->fs_info = fs_info;
- sctx->extent_path.search_commit_root = 1;
- sctx->extent_path.skip_locking = 1;
- sctx->csum_path.search_commit_root = 1;
- sctx->csum_path.skip_locking = 1;
+ sctx->extent_path.search_commit_root = true;
+ sctx->extent_path.skip_locking = true;
+ sctx->csum_path.search_commit_root = true;
+ sctx->csum_path.skip_locking = true;
for (i = 0; i < SCRUB_TOTAL_STRIPES; i++) {
int ret;
@@ -505,7 +505,7 @@ static int scrub_print_warning_inode(u64 inum, u64 offset, u64 num_bytes,
struct btrfs_inode_item *inode_item;
struct scrub_warning *swarn = warn_ctx;
struct btrfs_fs_info *fs_info = swarn->dev->fs_info;
- struct inode_fs_paths *ipath = NULL;
+ struct inode_fs_paths *ipath __free(inode_fs_paths) = NULL;
struct btrfs_root *local_root;
struct btrfs_key key;
@@ -569,7 +569,6 @@ static int scrub_print_warning_inode(u64 inum, u64 offset, u64 num_bytes,
(char *)(unsigned long)ipath->fspath->val[i]);
btrfs_put_root(local_root);
- free_ipath(ipath);
return 0;
err:
@@ -580,7 +579,6 @@ err:
swarn->physical,
root, inum, offset, ret);
- free_ipath(ipath);
return 0;
}
@@ -777,10 +775,10 @@ static void scrub_verify_one_metadata(struct scrub_stripe *stripe, int sector_nr
scrub_bitmap_set_meta_error(stripe, sector_nr, sectors_per_tree);
scrub_bitmap_set_error(stripe, sector_nr, sectors_per_tree);
btrfs_warn_rl(fs_info,
-"scrub: tree block %llu mirror %u has bad csum, has " CSUM_FMT " want " CSUM_FMT,
+"scrub: tree block %llu mirror %u has bad csum, has " BTRFS_CSUM_FMT " want " BTRFS_CSUM_FMT,
logical, stripe->mirror_num,
- CSUM_FMT_VALUE(fs_info->csum_size, on_disk_csum),
- CSUM_FMT_VALUE(fs_info->csum_size, calculated_csum));
+ BTRFS_CSUM_FMT_VALUE(fs_info->csum_size, on_disk_csum),
+ BTRFS_CSUM_FMT_VALUE(fs_info->csum_size, calculated_csum));
return;
}
if (stripe->sectors[sector_nr].generation !=
@@ -929,10 +927,11 @@ static int calc_next_mirror(int mirror, int num_copies)
static void scrub_bio_add_sector(struct btrfs_bio *bbio, struct scrub_stripe *stripe,
int sector_nr)
{
+ struct btrfs_fs_info *fs_info = bbio->inode->root->fs_info;
void *kaddr = scrub_stripe_get_kaddr(stripe, sector_nr);
int ret;
- ret = bio_add_page(&bbio->bio, virt_to_page(kaddr), bbio->fs_info->sectorsize,
+ ret = bio_add_page(&bbio->bio, virt_to_page(kaddr), fs_info->sectorsize,
offset_in_page(kaddr));
/*
* Caller should ensure the bbio has enough size.
@@ -942,7 +941,21 @@ static void scrub_bio_add_sector(struct btrfs_bio *bbio, struct scrub_stripe *st
* to create the minimal amount of bio vectors, for fs block size < page
* size cases.
*/
- ASSERT(ret == bbio->fs_info->sectorsize);
+ ASSERT(ret == fs_info->sectorsize);
+}
+
+static struct btrfs_bio *alloc_scrub_bbio(struct btrfs_fs_info *fs_info,
+ unsigned int nr_vecs, blk_opf_t opf,
+ u64 logical,
+ btrfs_bio_end_io_t end_io, void *private)
+{
+ struct btrfs_bio *bbio;
+
+ bbio = btrfs_bio_alloc(nr_vecs, opf, BTRFS_I(fs_info->btree_inode),
+ logical, end_io, private);
+ bbio->is_scrub = true;
+ bbio->bio.bi_iter.bi_sector = logical >> SECTOR_SHIFT;
+ return bbio;
}
static void scrub_stripe_submit_repair_read(struct scrub_stripe *stripe,
@@ -953,8 +966,9 @@ static void scrub_stripe_submit_repair_read(struct scrub_stripe *stripe,
const unsigned long old_error_bitmap = scrub_bitmap_read_error(stripe);
int i;
- ASSERT(stripe->mirror_num >= 1);
- ASSERT(atomic_read(&stripe->pending_io) == 0);
+ ASSERT(stripe->mirror_num >= 1, "stripe->mirror_num=%d", stripe->mirror_num);
+ ASSERT(atomic_read(&stripe->pending_io) == 0,
+ "atomic_read(&stripe->pending_io)=%d", atomic_read(&stripe->pending_io));
for_each_set_bit(i, &old_error_bitmap, stripe->nr_sectors) {
/* The current sector cannot be merged, submit the bio. */
@@ -968,12 +982,10 @@ static void scrub_stripe_submit_repair_read(struct scrub_stripe *stripe,
bbio = NULL;
}
- if (!bbio) {
- bbio = btrfs_bio_alloc(stripe->nr_sectors, REQ_OP_READ,
- fs_info, scrub_repair_read_endio, stripe);
- bbio->bio.bi_iter.bi_sector = (stripe->logical +
- (i << fs_info->sectorsize_bits)) >> SECTOR_SHIFT;
- }
+ if (!bbio)
+ bbio = alloc_scrub_bbio(fs_info, stripe->nr_sectors, REQ_OP_READ,
+ stripe->logical + (i << fs_info->sectorsize_bits),
+ scrub_repair_read_endio, stripe);
scrub_bio_add_sector(bbio, stripe, i);
}
@@ -1019,7 +1031,7 @@ static void scrub_stripe_report_errors(struct scrub_ctx *sctx,
int ret;
/* For scrub, our mirror_num should always start at 1. */
- ASSERT(stripe->mirror_num >= 1);
+ ASSERT(stripe->mirror_num >= 1, "stripe->mirror_num=%d", stripe->mirror_num);
ret = btrfs_map_block(fs_info, BTRFS_MAP_GET_READ_MIRRORS,
stripe->logical, &mapped_len, &bioc,
NULL, NULL);
@@ -1159,7 +1171,7 @@ static void scrub_stripe_read_repair_worker(struct work_struct *work)
int mirror;
int i;
- ASSERT(stripe->mirror_num > 0);
+ ASSERT(stripe->mirror_num >= 1, "stripe->mirror_num=%d", stripe->mirror_num);
wait_scrub_stripe_io(stripe);
scrub_verify_one_stripe(stripe, scrub_bitmap_read_has_extent(stripe));
@@ -1284,7 +1296,7 @@ static void scrub_write_endio(struct btrfs_bio *bbio)
bitmap_set(&stripe->write_error_bitmap, sector_nr,
bio_size >> fs_info->sectorsize_bits);
spin_unlock_irqrestore(&stripe->write_error_lock, flags);
- for (int i = 0; i < (bio_size >> fs_info->sectorsize_bits); i++)
+ for (i = 0; i < (bio_size >> fs_info->sectorsize_bits); i++)
btrfs_dev_stat_inc_and_print(stripe->dev,
BTRFS_DEV_STAT_WRITE_ERRS);
}
@@ -1352,13 +1364,10 @@ static void scrub_write_sectors(struct scrub_ctx *sctx, struct scrub_stripe *str
scrub_submit_write_bio(sctx, stripe, bbio, dev_replace);
bbio = NULL;
}
- if (!bbio) {
- bbio = btrfs_bio_alloc(stripe->nr_sectors, REQ_OP_WRITE,
- fs_info, scrub_write_endio, stripe);
- bbio->bio.bi_iter.bi_sector = (stripe->logical +
- (sector_nr << fs_info->sectorsize_bits)) >>
- SECTOR_SHIFT;
- }
+ if (!bbio)
+ bbio = alloc_scrub_bbio(fs_info, stripe->nr_sectors, REQ_OP_WRITE,
+ stripe->logical + (sector_nr << fs_info->sectorsize_bits),
+ scrub_write_endio, stripe);
scrub_bio_add_sector(bbio, stripe, sector_nr);
}
if (bbio)
@@ -1478,7 +1487,7 @@ static int compare_extent_item_range(struct btrfs_path *path,
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
ASSERT(key.type == BTRFS_EXTENT_ITEM_KEY ||
- key.type == BTRFS_METADATA_ITEM_KEY);
+ key.type == BTRFS_METADATA_ITEM_KEY, "key.type=%u", key.type);
if (key.type == BTRFS_METADATA_ITEM_KEY)
len = fs_info->nodesize;
else
@@ -1583,7 +1592,7 @@ static void get_extent_info(struct btrfs_path *path, u64 *extent_start_ret,
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
ASSERT(key.type == BTRFS_METADATA_ITEM_KEY ||
- key.type == BTRFS_EXTENT_ITEM_KEY);
+ key.type == BTRFS_EXTENT_ITEM_KEY, "key.type=%u", key.type);
*extent_start_ret = key.objectid;
if (key.type == BTRFS_METADATA_ITEM_KEY)
*size_ret = path->nodes[0]->fs_info->nodesize;
@@ -1681,7 +1690,9 @@ static int scrub_find_fill_first_stripe(struct btrfs_block_group *bg,
scrub_stripe_reset_bitmaps(stripe);
/* The range must be inside the bg. */
- ASSERT(logical_start >= bg->start && logical_end <= bg->start + bg->length);
+ ASSERT(logical_start >= bg->start && logical_end <= bg->start + bg->length,
+ "bg->start=%llu logical_start=%llu logical_end=%llu end=%llu",
+ bg->start, logical_start, logical_end, bg->start + bg->length);
ret = find_first_extent_item(extent_root, extent_path, logical_start,
logical_len);
@@ -1849,9 +1860,8 @@ static void scrub_submit_extent_sector_read(struct scrub_stripe *stripe)
continue;
}
- bbio = btrfs_bio_alloc(stripe->nr_sectors, REQ_OP_READ,
- fs_info, scrub_read_endio, stripe);
- bbio->bio.bi_iter.bi_sector = logical >> SECTOR_SHIFT;
+ bbio = alloc_scrub_bbio(fs_info, stripe->nr_sectors, REQ_OP_READ,
+ logical, scrub_read_endio, stripe);
}
scrub_bio_add_sector(bbio, stripe, i);
@@ -1888,10 +1898,8 @@ static void scrub_submit_initial_read(struct scrub_ctx *sctx,
return;
}
- bbio = btrfs_bio_alloc(BTRFS_STRIPE_LEN >> min_folio_shift, REQ_OP_READ, fs_info,
- scrub_read_endio, stripe);
-
- bbio->bio.bi_iter.bi_sector = stripe->logical >> SECTOR_SHIFT;
+ bbio = alloc_scrub_bbio(fs_info, BTRFS_STRIPE_LEN >> min_folio_shift, REQ_OP_READ,
+ stripe->logical, scrub_read_endio, stripe);
/* Read the whole range inside the chunk boundary. */
for (unsigned int cur = 0; cur < nr_sectors; cur++)
scrub_bio_add_sector(bbio, stripe, cur);
@@ -2069,37 +2077,135 @@ static int queue_scrub_stripe(struct scrub_ctx *sctx, struct btrfs_block_group *
return 0;
}
+/*
+ * Return 0 if we should not cancel the scrub.
+ * Return <0 if we need to cancel the scrub, returned value will
+ * indicate the reason:
+ * - -ECANCELED - Being explicitly canceled through ioctl.
+ * - -EINTR - Being interrupted by signal or fs/process freezing.
+ */
+static int should_cancel_scrub(const struct scrub_ctx *sctx)
+{
+ struct btrfs_fs_info *fs_info = sctx->fs_info;
+
+ if (atomic_read(&fs_info->scrub_cancel_req) ||
+ atomic_read(&sctx->cancel_req))
+ return -ECANCELED;
+
+ /*
+ * The user (e.g. fsfreeze command) or power management (PM)
+ * suspend/hibernate can freeze the fs. And PM suspend/hibernate will
+ * also freeze all user processes.
+ *
+ * A user process can only be frozen when it is in user space, thus we
+ * have to cancel the run so that the process can return to the user
+ * space.
+ *
+ * Furthermore we have to check both filesystem and process freezing,
+ * as PM can be configured to freeze the filesystems before processes.
+ *
+ * If we only check fs freezing, then suspend without fs freezing
+ * will timeout, as the process is still in kernel space.
+ *
+ * If we only check process freezing, then suspend with fs freezing
+ * will timeout, as the running scrub will prevent the fs from being frozen.
+ */
+ if (fs_info->sb->s_writers.frozen > SB_UNFROZEN ||
+ freezing(current) || signal_pending(current))
+ return -EINTR;
+ return 0;
+}
+
+static int scrub_raid56_cached_parity(struct scrub_ctx *sctx,
+ struct btrfs_device *scrub_dev,
+ struct btrfs_chunk_map *map,
+ u64 full_stripe_start,
+ unsigned long *extent_bitmap)
+{
+ DECLARE_COMPLETION_ONSTACK(io_done);
+ struct btrfs_fs_info *fs_info = sctx->fs_info;
+ struct btrfs_io_context *bioc = NULL;
+ struct btrfs_raid_bio *rbio;
+ struct bio bio;
+ const int data_stripes = nr_data_stripes(map);
+ u64 length = btrfs_stripe_nr_to_offset(data_stripes);
+ int ret;
+
+ bio_init(&bio, NULL, NULL, 0, REQ_OP_READ);
+ bio.bi_iter.bi_sector = full_stripe_start >> SECTOR_SHIFT;
+ bio.bi_private = &io_done;
+ bio.bi_end_io = raid56_scrub_wait_endio;
+
+ btrfs_bio_counter_inc_blocked(fs_info);
+ ret = btrfs_map_block(fs_info, BTRFS_MAP_WRITE, full_stripe_start,
+ &length, &bioc, NULL, NULL);
+ if (ret < 0)
+ goto out;
+ /* For RAID56 write there must be an @bioc allocated. */
+ ASSERT(bioc);
+ rbio = raid56_parity_alloc_scrub_rbio(&bio, bioc, scrub_dev, extent_bitmap,
+ BTRFS_STRIPE_LEN >> fs_info->sectorsize_bits);
+ btrfs_put_bioc(bioc);
+ if (!rbio) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ /* Use the recovered stripes as cache to avoid read them from disk again. */
+ for (int i = 0; i < data_stripes; i++) {
+ struct scrub_stripe *stripe = &sctx->raid56_data_stripes[i];
+
+ raid56_parity_cache_data_folios(rbio, stripe->folios,
+ full_stripe_start + (i << BTRFS_STRIPE_LEN_SHIFT));
+ }
+ raid56_parity_submit_scrub_rbio(rbio);
+ wait_for_completion_io(&io_done);
+ ret = blk_status_to_errno(bio.bi_status);
+out:
+ btrfs_bio_counter_dec(fs_info);
+ bio_uninit(&bio);
+ return ret;
+}
+
static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx,
struct btrfs_device *scrub_dev,
struct btrfs_block_group *bg,
struct btrfs_chunk_map *map,
u64 full_stripe_start)
{
- DECLARE_COMPLETION_ONSTACK(io_done);
struct btrfs_fs_info *fs_info = sctx->fs_info;
- struct btrfs_raid_bio *rbio;
- struct btrfs_io_context *bioc = NULL;
struct btrfs_path extent_path = { 0 };
struct btrfs_path csum_path = { 0 };
- struct bio *bio;
struct scrub_stripe *stripe;
bool all_empty = true;
const int data_stripes = nr_data_stripes(map);
unsigned long extent_bitmap = 0;
- u64 length = btrfs_stripe_nr_to_offset(data_stripes);
int ret;
ASSERT(sctx->raid56_data_stripes);
+ ret = should_cancel_scrub(sctx);
+ if (ret < 0)
+ return ret;
+
+ if (atomic_read(&fs_info->scrub_pause_req))
+ scrub_blocked_if_needed(fs_info);
+
+ spin_lock(&bg->lock);
+ if (test_bit(BLOCK_GROUP_FLAG_REMOVED, &bg->runtime_flags)) {
+ spin_unlock(&bg->lock);
+ return 0;
+ }
+ spin_unlock(&bg->lock);
+
/*
* For data stripe search, we cannot reuse the same extent/csum paths,
* as the data stripe bytenr may be smaller than previous extent. Thus
* we have to use our own extent/csum paths.
*/
- extent_path.search_commit_root = 1;
- extent_path.skip_locking = 1;
- csum_path.search_commit_root = 1;
- csum_path.skip_locking = 1;
+ extent_path.search_commit_root = true;
+ extent_path.skip_locking = true;
+ csum_path.search_commit_root = true;
+ csum_path.skip_locking = true;
for (int i = 0; i < data_stripes; i++) {
int stripe_index;
@@ -2194,45 +2300,11 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx,
}
/* Now we can check and regenerate the P/Q stripe. */
- bio = bio_alloc(NULL, 1, REQ_OP_READ, GFP_NOFS);
- bio->bi_iter.bi_sector = full_stripe_start >> SECTOR_SHIFT;
- bio->bi_private = &io_done;
- bio->bi_end_io = raid56_scrub_wait_endio;
-
- btrfs_bio_counter_inc_blocked(fs_info);
- ret = btrfs_map_block(fs_info, BTRFS_MAP_WRITE, full_stripe_start,
- &length, &bioc, NULL, NULL);
- if (ret < 0) {
- bio_put(bio);
- btrfs_put_bioc(bioc);
- btrfs_bio_counter_dec(fs_info);
- goto out;
- }
- rbio = raid56_parity_alloc_scrub_rbio(bio, bioc, scrub_dev, &extent_bitmap,
- BTRFS_STRIPE_LEN >> fs_info->sectorsize_bits);
- btrfs_put_bioc(bioc);
- if (!rbio) {
- ret = -ENOMEM;
- bio_put(bio);
- btrfs_bio_counter_dec(fs_info);
- goto out;
- }
- /* Use the recovered stripes as cache to avoid read them from disk again. */
- for (int i = 0; i < data_stripes; i++) {
- stripe = &sctx->raid56_data_stripes[i];
-
- raid56_parity_cache_data_folios(rbio, stripe->folios,
- full_stripe_start + (i << BTRFS_STRIPE_LEN_SHIFT));
- }
- raid56_parity_submit_scrub_rbio(rbio);
- wait_for_completion_io(&io_done);
- ret = blk_status_to_errno(bio->bi_status);
- bio_put(bio);
- btrfs_bio_counter_dec(fs_info);
-
+ ret = scrub_raid56_cached_parity(sctx, scrub_dev, map, full_stripe_start,
+ &extent_bitmap);
+out:
btrfs_release_path(&extent_path);
btrfs_release_path(&csum_path);
-out:
return ret;
}
@@ -2263,18 +2335,13 @@ static int scrub_simple_mirror(struct scrub_ctx *sctx,
u64 found_logical = U64_MAX;
u64 cur_physical = physical + cur_logical - logical_start;
- /* Canceled? */
- if (atomic_read(&fs_info->scrub_cancel_req) ||
- atomic_read(&sctx->cancel_req)) {
- ret = -ECANCELED;
+ ret = should_cancel_scrub(sctx);
+ if (ret < 0)
break;
- }
- /* Paused? */
- if (atomic_read(&fs_info->scrub_pause_req)) {
- /* Push queued extents */
+
+ if (atomic_read(&fs_info->scrub_pause_req))
scrub_blocked_if_needed(fs_info);
- }
- /* Block group removed? */
+
spin_lock(&bg->lock);
if (test_bit(BLOCK_GROUP_FLAG_REMOVED, &bg->runtime_flags)) {
spin_unlock(&bg->lock);
@@ -2529,8 +2596,6 @@ out:
}
if (sctx->is_dev_replace && ret >= 0) {
- int ret2;
-
ret2 = sync_write_pointer_for_zoned(sctx,
chunk_logical + offset,
map->stripes[stripe_index].physical,
@@ -2623,8 +2688,8 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx,
return -ENOMEM;
path->reada = READA_FORWARD;
- path->search_commit_root = 1;
- path->skip_locking = 1;
+ path->search_commit_root = true;
+ path->skip_locking = true;
key.objectid = scrub_dev->devid;
key.type = BTRFS_DEV_EXTENT_KEY;
@@ -3039,6 +3104,10 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
unsigned int nofs_flag;
bool need_commit = false;
+ /* Set the basic fallback @last_physical before we got a sctx. */
+ if (progress)
+ progress->last_physical = start;
+
if (btrfs_fs_closing(fs_info))
return -EAGAIN;
@@ -3057,6 +3126,7 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
sctx = scrub_setup_ctx(fs_info, is_dev_replace);
if (IS_ERR(sctx))
return PTR_ERR(sctx);
+ sctx->stat.last_physical = start;
ret = scrub_workers_get(fs_info);
if (ret)
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index 96a030d28e09..2522faa97478 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -47,28 +47,30 @@
* It allows fast adding of path elements on the right side (normal path) and
* fast adding to the left side (reversed path). A reversed path can also be
* unreversed if needed.
+ *
+ * The definition of struct fs_path relies on -fms-extensions to allow
+ * including a tagged struct as an anonymous member.
*/
+struct __fs_path {
+ char *start;
+ char *end;
+
+ char *buf;
+ unsigned short buf_len:15;
+ unsigned short reversed:1;
+};
+static_assert(sizeof(struct __fs_path) < 256);
struct fs_path {
- union {
- struct {
- char *start;
- char *end;
-
- char *buf;
- unsigned short buf_len:15;
- unsigned short reversed:1;
- char inline_buf[];
- };
- /*
- * Average path length does not exceed 200 bytes, we'll have
- * better packing in the slab and higher chance to satisfy
- * an allocation later during send.
- */
- char pad[256];
- };
+ struct __fs_path;
+ /*
+ * Average path length does not exceed 200 bytes, we'll have
+ * better packing in the slab and higher chance to satisfy
+ * an allocation later during send.
+ */
+ char inline_buf[256 - sizeof(struct __fs_path)];
};
#define FS_PATH_INLINE_SIZE \
- (sizeof(struct fs_path) - offsetof(struct fs_path, inline_buf))
+ sizeof_field(struct fs_path, inline_buf)
/* reused for each extent */
@@ -305,7 +307,6 @@ struct send_ctx {
struct btrfs_lru_cache dir_created_cache;
struct btrfs_lru_cache dir_utimes_cache;
- /* Must be last as it ends in a flexible-array member. */
struct fs_path cur_inode_path;
};
@@ -633,9 +634,9 @@ static struct btrfs_path *alloc_path_for_send(void)
path = btrfs_alloc_path();
if (!path)
return NULL;
- path->search_commit_root = 1;
- path->skip_locking = 1;
- path->need_commit_sem = 1;
+ path->search_commit_root = true;
+ path->skip_locking = true;
+ path->need_commit_sem = true;
return path;
}
@@ -1053,10 +1054,8 @@ static int iterate_inode_ref(struct btrfs_root *root, struct btrfs_path *path,
}
if (unlikely(start < p->buf)) {
btrfs_err(root->fs_info,
- "send: path ref buffer underflow for key (%llu %u %llu)",
- found_key->objectid,
- found_key->type,
- found_key->offset);
+ "send: path ref buffer underflow for key " BTRFS_KEY_FMT,
+ BTRFS_KEY_FMT_VALUE(found_key));
ret = -EINVAL;
goto out;
}
@@ -1136,12 +1135,12 @@ static int iterate_dir_item(struct btrfs_root *root, struct btrfs_path *path,
btrfs_dir_item_key_to_cpu(eb, di, &di_key);
if (btrfs_dir_ftype(eb, di) == BTRFS_FT_XATTR) {
- if (name_len > XATTR_NAME_MAX) {
+ if (unlikely(name_len > XATTR_NAME_MAX)) {
ret = -ENAMETOOLONG;
goto out;
}
- if (name_len + data_len >
- BTRFS_MAX_XATTR_SIZE(root->fs_info)) {
+ if (unlikely(name_len + data_len >
+ BTRFS_MAX_XATTR_SIZE(root->fs_info))) {
ret = -E2BIG;
goto out;
}
@@ -1149,7 +1148,7 @@ static int iterate_dir_item(struct btrfs_root *root, struct btrfs_path *path,
/*
* Path too long
*/
- if (name_len + data_len > PATH_MAX) {
+ if (unlikely(name_len + data_len > PATH_MAX)) {
ret = -ENAMETOOLONG;
goto out;
}
@@ -2460,7 +2459,7 @@ static int send_subvol_begin(struct send_ctx *sctx)
struct btrfs_key key;
struct btrfs_root_ref *ref;
struct extent_buffer *leaf;
- char *name = NULL;
+ char AUTO_KFREE(name);
int namelen;
path = btrfs_alloc_path();
@@ -2478,18 +2477,15 @@ static int send_subvol_begin(struct send_ctx *sctx)
ret = btrfs_search_slot_for_read(send_root->fs_info->tree_root,
&key, path, 1, 0);
if (ret < 0)
- goto out;
- if (ret) {
- ret = -ENOENT;
- goto out;
- }
+ return ret;
+ if (ret)
+ return -ENOENT;
leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
if (key.type != BTRFS_ROOT_BACKREF_KEY ||
key.objectid != btrfs_root_id(send_root)) {
- ret = -ENOENT;
- goto out;
+ return -ENOENT;
}
ref = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_ref);
namelen = btrfs_root_ref_name_len(leaf, ref);
@@ -2499,11 +2495,11 @@ static int send_subvol_begin(struct send_ctx *sctx)
if (parent_root) {
ret = begin_cmd(sctx, BTRFS_SEND_C_SNAPSHOT);
if (ret < 0)
- goto out;
+ return ret;
} else {
ret = begin_cmd(sctx, BTRFS_SEND_C_SUBVOL);
if (ret < 0)
- goto out;
+ return ret;
}
TLV_PUT_STRING(sctx, BTRFS_SEND_A_PATH, name, namelen);
@@ -2531,8 +2527,6 @@ static int send_subvol_begin(struct send_ctx *sctx)
ret = send_cmd(sctx);
tlv_put_failure:
-out:
- kfree(name);
return ret;
}
@@ -4079,7 +4073,7 @@ static int update_ref_path(struct send_ctx *sctx, struct recorded_ref *ref)
*/
static int refresh_ref_path(struct send_ctx *sctx, struct recorded_ref *ref)
{
- char *name;
+ char AUTO_KFREE(name);
int ret;
name = kmemdup(ref->name, ref->name_len, GFP_KERNEL);
@@ -4089,17 +4083,16 @@ static int refresh_ref_path(struct send_ctx *sctx, struct recorded_ref *ref)
fs_path_reset(ref->full_path);
ret = get_cur_path(sctx, ref->dir, ref->dir_gen, ref->full_path);
if (ret < 0)
- goto out;
+ return ret;
ret = fs_path_add(ref->full_path, name, ref->name_len);
if (ret < 0)
- goto out;
+ return ret;
/* Update the reference's base name pointer. */
set_ref_path(ref, ref->full_path);
-out:
- kfree(name);
- return ret;
+
+ return 0;
}
static int rbtree_check_dir_ref_comp(const void *k, const struct rb_node *node)
@@ -4951,6 +4944,7 @@ struct find_xattr_ctx {
int found_idx;
char *found_data;
int found_data_len;
+ bool copy_data;
};
static int __find_xattr(int num, struct btrfs_key *di_key, const char *name,
@@ -4962,9 +4956,11 @@ static int __find_xattr(int num, struct btrfs_key *di_key, const char *name,
strncmp(name, ctx->name, name_len) == 0) {
ctx->found_idx = num;
ctx->found_data_len = data_len;
- ctx->found_data = kmemdup(data, data_len, GFP_KERNEL);
- if (!ctx->found_data)
- return -ENOMEM;
+ if (ctx->copy_data) {
+ ctx->found_data = kmemdup(data, data_len, GFP_KERNEL);
+ if (!ctx->found_data)
+ return -ENOMEM;
+ }
return 1;
}
return 0;
@@ -4984,6 +4980,7 @@ static int find_xattr(struct btrfs_root *root,
ctx.found_idx = -1;
ctx.found_data = NULL;
ctx.found_data_len = 0;
+ ctx.copy_data = (data != NULL);
ret = iterate_dir_item(root, path, __find_xattr, &ctx);
if (ret < 0)
@@ -4995,7 +4992,7 @@ static int find_xattr(struct btrfs_root *root,
*data = ctx.found_data;
*data_len = ctx.found_data_len;
} else {
- kfree(ctx.found_data);
+ ASSERT(ctx.found_data == NULL);
}
return ctx.found_idx;
}
@@ -5008,8 +5005,8 @@ static int __process_changed_new_xattr(int num, struct btrfs_key *di_key,
{
int ret;
struct send_ctx *sctx = ctx;
- char *found_data = NULL;
- int found_data_len = 0;
+ char AUTO_KFREE(found_data);
+ int found_data_len = 0;
ret = find_xattr(sctx->parent_root, sctx->right_path,
sctx->cmp_key, name, name_len, &found_data,
@@ -5027,7 +5024,6 @@ static int __process_changed_new_xattr(int num, struct btrfs_key *di_key,
}
}
- kfree(found_data);
return ret;
}
@@ -5138,7 +5134,7 @@ static int process_verity(struct send_ctx *sctx)
if (ret < 0)
goto iput;
- if (ret > FS_VERITY_MAX_DESCRIPTOR_SIZE) {
+ if (unlikely(ret > FS_VERITY_MAX_DESCRIPTOR_SIZE)) {
ret = -EMSGSIZE;
goto iput;
}
@@ -5182,14 +5178,14 @@ static int put_data_header(struct send_ctx *sctx, u32 len)
* Since v2, the data attribute header doesn't include a length,
* it is implicitly to the end of the command.
*/
- if (sctx->send_max_size - sctx->send_size < sizeof(__le16) + len)
+ if (unlikely(sctx->send_max_size - sctx->send_size < sizeof(__le16) + len))
return -EOVERFLOW;
put_unaligned_le16(BTRFS_SEND_A_DATA, sctx->send_buf + sctx->send_size);
sctx->send_size += sizeof(__le16);
} else {
struct btrfs_tlv_header *hdr;
- if (sctx->send_max_size - sctx->send_size < sizeof(*hdr) + len)
+ if (unlikely(sctx->send_max_size - sctx->send_size < sizeof(*hdr) + len))
return -EOVERFLOW;
hdr = (struct btrfs_tlv_header *)(sctx->send_buf + sctx->send_size);
put_unaligned_le16(BTRFS_SEND_A_DATA, &hdr->tlv_type);
@@ -5589,8 +5585,8 @@ static int send_encoded_extent(struct send_ctx *sctx, struct btrfs_path *path,
* between the beginning of the command and the file data.
*/
data_offset = PAGE_ALIGN(sctx->send_size);
- if (data_offset > sctx->send_max_size ||
- sctx->send_max_size - data_offset < disk_num_bytes) {
+ if (unlikely(data_offset > sctx->send_max_size ||
+ sctx->send_max_size - data_offset < disk_num_bytes)) {
ret = -EOVERFLOW;
goto out;
}
@@ -5643,14 +5639,7 @@ static int send_extent_data(struct send_ctx *sctx, struct btrfs_path *path,
ei = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
- /*
- * Do not go through encoded read for bs > ps cases.
- *
- * Encoded send is using vmallocated pages as buffer, which we can
- * not ensure every folio is large enough to contain a block.
- */
- if (sctx->send_root->fs_info->sectorsize <= PAGE_SIZE &&
- (sctx->flags & BTRFS_SEND_FLAG_COMPRESSED) &&
+ if ((sctx->flags & BTRFS_SEND_FLAG_COMPRESSED) &&
btrfs_file_extent_compression(leaf, ei) != BTRFS_COMPRESS_NONE) {
bool is_inline = (btrfs_file_extent_type(leaf, ei) ==
BTRFS_FILE_EXTENT_INLINE);
@@ -5764,7 +5753,7 @@ static int send_capabilities(struct send_ctx *sctx)
struct btrfs_dir_item *di;
struct extent_buffer *leaf;
unsigned long data_ptr;
- char *buf = NULL;
+ char AUTO_KFREE(buf);
int buf_len;
int ret = 0;
@@ -5776,28 +5765,23 @@ static int send_capabilities(struct send_ctx *sctx)
XATTR_NAME_CAPS, strlen(XATTR_NAME_CAPS), 0);
if (!di) {
/* There is no xattr for this inode */
- goto out;
+ return 0;
} else if (IS_ERR(di)) {
- ret = PTR_ERR(di);
- goto out;
+ return PTR_ERR(di);
}
leaf = path->nodes[0];
buf_len = btrfs_dir_data_len(leaf, di);
buf = kmalloc(buf_len, GFP_KERNEL);
- if (!buf) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!buf)
+ return -ENOMEM;
data_ptr = (unsigned long)(di + 1) + btrfs_dir_name_len(leaf, di);
read_extent_buffer(leaf, buf, data_ptr, buf_len);
ret = send_set_xattr(sctx, XATTR_NAME_CAPS,
strlen(XATTR_NAME_CAPS), buf, buf_len);
-out:
- kfree(buf);
return ret;
}
@@ -7274,8 +7258,8 @@ static int search_key_again(const struct send_ctx *sctx,
if (unlikely(ret > 0)) {
btrfs_print_tree(path->nodes[path->lowest_level], false);
btrfs_err(root->fs_info,
-"send: key (%llu %u %llu) not found in %s root %llu, lowest_level %d, slot %d",
- key->objectid, key->type, key->offset,
+"send: key " BTRFS_KEY_FMT" not found in %s root %llu, lowest_level %d, slot %d",
+ BTRFS_KEY_FMT_VALUE(key),
(root == sctx->parent_root ? "parent" : "send"),
btrfs_root_id(root), path->lowest_level,
path->slots[path->lowest_level]);
@@ -7643,10 +7627,10 @@ static int btrfs_compare_trees(struct btrfs_root *left_root,
goto out;
}
- left_path->search_commit_root = 1;
- left_path->skip_locking = 1;
- right_path->search_commit_root = 1;
- right_path->skip_locking = 1;
+ left_path->search_commit_root = true;
+ left_path->skip_locking = true;
+ right_path->search_commit_root = true;
+ right_path->skip_locking = true;
/*
* Strategy: Go to the first items of both trees. Then do
diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c
index 97452fb5d29b..6babbe333741 100644
--- a/fs/btrfs/space-info.c
+++ b/fs/btrfs/space-info.c
@@ -15,6 +15,7 @@
#include "accessors.h"
#include "extent-tree.h"
#include "zoned.h"
+#include "delayed-inode.h"
/*
* HOW DOES SPACE RESERVATION WORK
@@ -67,7 +68,7 @@
* Assume we are unable to simply make the reservation because we do not have
* enough space
*
- * -> __reserve_bytes
+ * -> reserve_bytes
* create a reserve_ticket with ->bytes set to our reservation, add it to
* the tail of space_info->tickets, kick async flush thread
*
@@ -172,15 +173,14 @@
* thing with or without extra unallocated space.
*/
-u64 __pure btrfs_space_info_used(const struct btrfs_space_info *s_info,
- bool may_use_included)
-{
- ASSERT(s_info);
- return s_info->bytes_used + s_info->bytes_reserved +
- s_info->bytes_pinned + s_info->bytes_readonly +
- s_info->bytes_zone_unusable +
- (may_use_included ? s_info->bytes_may_use : 0);
-}
+struct reserve_ticket {
+ u64 bytes;
+ int error;
+ bool steal;
+ struct list_head list;
+ wait_queue_head_t wait;
+ spinlock_t lock;
+};
/*
* after adding space to the filesystem, we need to clear the full flags
@@ -192,7 +192,7 @@ void btrfs_clear_space_info_full(struct btrfs_fs_info *info)
struct btrfs_space_info *found;
list_for_each_entry(found, head, list)
- found->full = 0;
+ found->full = false;
}
/*
@@ -211,7 +211,7 @@ static u64 calc_chunk_size(const struct btrfs_fs_info *fs_info, u64 flags)
if (btrfs_is_zoned(fs_info))
return fs_info->zone_size;
- ASSERT(flags & BTRFS_BLOCK_GROUP_TYPE_MASK);
+ ASSERT(flags & BTRFS_BLOCK_GROUP_TYPE_MASK, "flags=%llu", flags);
if (flags & BTRFS_BLOCK_GROUP_DATA)
return BTRFS_MAX_DATA_CHUNK_SIZE;
@@ -262,8 +262,9 @@ static int create_space_info_sub_group(struct btrfs_space_info *parent, u64 flag
struct btrfs_space_info *sub_group;
int ret;
- ASSERT(parent->subgroup_id == BTRFS_SUB_GROUP_PRIMARY);
- ASSERT(id != BTRFS_SUB_GROUP_PRIMARY);
+ ASSERT(parent->subgroup_id == BTRFS_SUB_GROUP_PRIMARY,
+ "parent->subgroup_id=%d", parent->subgroup_id);
+ ASSERT(id != BTRFS_SUB_GROUP_PRIMARY, "id=%d", id);
sub_group = kzalloc(sizeof(*sub_group), GFP_NOFS);
if (!sub_group)
@@ -274,7 +275,7 @@ static int create_space_info_sub_group(struct btrfs_space_info *parent, u64 flag
sub_group->parent = parent;
sub_group->subgroup_id = id;
- ret = btrfs_sysfs_add_space_info_type(fs_info, sub_group);
+ ret = btrfs_sysfs_add_space_info_type(sub_group);
if (ret) {
kfree(sub_group);
parent->sub_group[index] = NULL;
@@ -308,7 +309,7 @@ static int create_space_info(struct btrfs_fs_info *info, u64 flags)
return ret;
}
- ret = btrfs_sysfs_add_space_info_type(info, space_info);
+ ret = btrfs_sysfs_add_space_info_type(space_info);
if (ret)
return ret;
@@ -372,8 +373,8 @@ void btrfs_add_bg_to_space_info(struct btrfs_fs_info *info,
space_info->bytes_readonly += block_group->bytes_super;
btrfs_space_info_update_bytes_zone_unusable(space_info, block_group->zone_unusable);
if (block_group->length > 0)
- space_info->full = 0;
- btrfs_try_granting_tickets(info, space_info);
+ space_info->full = false;
+ btrfs_try_granting_tickets(space_info);
spin_unlock(&space_info->lock);
block_group->space_info = space_info;
@@ -421,10 +422,10 @@ static u64 calc_effective_data_chunk_size(struct btrfs_fs_info *fs_info)
return min_t(u64, data_chunk_size, SZ_1G);
}
-static u64 calc_available_free_space(struct btrfs_fs_info *fs_info,
- const struct btrfs_space_info *space_info,
- enum btrfs_reserve_flush_enum flush)
+static u64 calc_available_free_space(const struct btrfs_space_info *space_info,
+ enum btrfs_reserve_flush_enum flush)
{
+ struct btrfs_fs_info *fs_info = space_info->fs_info;
u64 profile;
u64 avail;
u64 data_chunk_size;
@@ -490,44 +491,77 @@ static u64 calc_available_free_space(struct btrfs_fs_info *fs_info,
return avail;
}
-int btrfs_can_overcommit(struct btrfs_fs_info *fs_info,
- const struct btrfs_space_info *space_info, u64 bytes,
- enum btrfs_reserve_flush_enum flush)
+static inline bool check_can_overcommit(const struct btrfs_space_info *space_info,
+ u64 space_info_used_bytes, u64 bytes,
+ enum btrfs_reserve_flush_enum flush)
+{
+ const u64 avail = calc_available_free_space(space_info, flush);
+
+ return (space_info_used_bytes + bytes < space_info->total_bytes + avail);
+}
+
+static inline bool can_overcommit(const struct btrfs_space_info *space_info,
+ u64 space_info_used_bytes, u64 bytes,
+ enum btrfs_reserve_flush_enum flush)
+{
+ /* Don't overcommit when in mixed mode. */
+ if (space_info->flags & BTRFS_BLOCK_GROUP_DATA)
+ return false;
+
+ return check_can_overcommit(space_info, space_info_used_bytes, bytes, flush);
+}
+
+bool btrfs_can_overcommit(const struct btrfs_space_info *space_info, u64 bytes,
+ enum btrfs_reserve_flush_enum flush)
{
- u64 avail;
u64 used;
/* Don't overcommit when in mixed mode */
if (space_info->flags & BTRFS_BLOCK_GROUP_DATA)
- return 0;
+ return false;
used = btrfs_space_info_used(space_info, true);
- avail = calc_available_free_space(fs_info, space_info, flush);
- if (used + bytes < space_info->total_bytes + avail)
- return 1;
- return 0;
+ return check_can_overcommit(space_info, used, bytes, flush);
}
static void remove_ticket(struct btrfs_space_info *space_info,
- struct reserve_ticket *ticket)
+ struct reserve_ticket *ticket, int error)
{
+ lockdep_assert_held(&space_info->lock);
+
if (!list_empty(&ticket->list)) {
list_del_init(&ticket->list);
- ASSERT(space_info->reclaim_size >= ticket->bytes);
+ ASSERT(space_info->reclaim_size >= ticket->bytes,
+ "space_info->reclaim_size=%llu ticket->bytes=%llu",
+ space_info->reclaim_size, ticket->bytes);
space_info->reclaim_size -= ticket->bytes;
}
+
+ spin_lock(&ticket->lock);
+ /*
+ * If we are called from a task waiting on the ticket, it may happen
+ * that before it sets an error on the ticket, a reclaim task was able
+ * to satisfy the ticket. In that case ignore the error.
+ */
+ if (error && ticket->bytes > 0)
+ ticket->error = error;
+ else
+ ticket->bytes = 0;
+
+ wake_up(&ticket->wait);
+ spin_unlock(&ticket->lock);
}
/*
* This is for space we already have accounted in space_info->bytes_may_use, so
* basically when we're returning space from block_rsv's.
*/
-void btrfs_try_granting_tickets(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info)
+void btrfs_try_granting_tickets(struct btrfs_space_info *space_info)
{
struct list_head *head;
enum btrfs_reserve_flush_enum flush = BTRFS_RESERVE_NO_FLUSH;
+ u64 used = btrfs_space_info_used(space_info, true);
lockdep_assert_held(&space_info->lock);
@@ -535,19 +569,18 @@ void btrfs_try_granting_tickets(struct btrfs_fs_info *fs_info,
again:
while (!list_empty(head)) {
struct reserve_ticket *ticket;
- u64 used = btrfs_space_info_used(space_info, true);
+ u64 used_after;
ticket = list_first_entry(head, struct reserve_ticket, list);
+ used_after = used + ticket->bytes;
/* Check and see if our ticket can be satisfied now. */
- if ((used + ticket->bytes <= space_info->total_bytes) ||
- btrfs_can_overcommit(fs_info, space_info, ticket->bytes,
- flush)) {
+ if (used_after <= space_info->total_bytes ||
+ can_overcommit(space_info, used, ticket->bytes, flush)) {
btrfs_space_info_update_bytes_may_use(space_info, ticket->bytes);
- remove_ticket(space_info, ticket);
- ticket->bytes = 0;
+ remove_ticket(space_info, ticket, 0);
space_info->tickets_id++;
- wake_up(&ticket->wait);
+ used = used_after;
} else {
break;
}
@@ -594,9 +627,9 @@ static void dump_global_block_rsv(struct btrfs_fs_info *fs_info)
DUMP_BLOCK_RSV(fs_info, delayed_refs_rsv);
}
-static void __btrfs_dump_space_info(const struct btrfs_fs_info *fs_info,
- const struct btrfs_space_info *info)
+static void __btrfs_dump_space_info(const struct btrfs_space_info *info)
{
+ const struct btrfs_fs_info *fs_info = info->fs_info;
const char *flag_str = space_info_flag_to_str(info);
lockdep_assert_held(&info->lock);
@@ -613,16 +646,16 @@ static void __btrfs_dump_space_info(const struct btrfs_fs_info *fs_info,
info->bytes_readonly, info->bytes_zone_unusable);
}
-void btrfs_dump_space_info(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *info, u64 bytes,
+void btrfs_dump_space_info(struct btrfs_space_info *info, u64 bytes,
bool dump_block_groups)
{
+ struct btrfs_fs_info *fs_info = info->fs_info;
struct btrfs_block_group *cache;
u64 total_avail = 0;
int index = 0;
spin_lock(&info->lock);
- __btrfs_dump_space_info(fs_info, info);
+ __btrfs_dump_space_info(info);
dump_global_block_rsv(fs_info);
spin_unlock(&info->lock);
@@ -670,11 +703,11 @@ static inline u64 calc_reclaim_items_nr(const struct btrfs_fs_info *fs_info,
/*
* shrink metadata reservation for delalloc
*/
-static void shrink_delalloc(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info,
+static void shrink_delalloc(struct btrfs_space_info *space_info,
u64 to_reclaim, bool wait_ordered,
bool for_preempt)
{
+ struct btrfs_fs_info *fs_info = space_info->fs_info;
struct btrfs_trans_handle *trans;
u64 delalloc_bytes;
u64 ordered_bytes;
@@ -801,10 +834,10 @@ skip_async:
* and may fail for various reasons. The caller is supposed to examine the
* state of @space_info to detect the outcome.
*/
-static void flush_space(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info, u64 num_bytes,
- enum btrfs_flush_state state, bool for_preempt)
+static void flush_space(struct btrfs_space_info *space_info, u64 num_bytes,
+ enum btrfs_flush_state state, bool for_preempt)
{
+ struct btrfs_fs_info *fs_info = space_info->fs_info;
struct btrfs_root *root = fs_info->tree_root;
struct btrfs_trans_handle *trans;
int nr;
@@ -833,7 +866,7 @@ static void flush_space(struct btrfs_fs_info *fs_info,
case FLUSH_DELALLOC_FULL:
if (state == FLUSH_DELALLOC_FULL)
num_bytes = U64_MAX;
- shrink_delalloc(fs_info, space_info, num_bytes,
+ shrink_delalloc(space_info, num_bytes,
state != FLUSH_DELALLOC, for_preempt);
break;
case FLUSH_DELAYED_REFS_NR:
@@ -900,8 +933,7 @@ static void flush_space(struct btrfs_fs_info *fs_info,
return;
}
-static u64 btrfs_calc_reclaim_metadata_size(struct btrfs_fs_info *fs_info,
- const struct btrfs_space_info *space_info)
+static u64 btrfs_calc_reclaim_metadata_size(const struct btrfs_space_info *space_info)
{
u64 used;
u64 avail;
@@ -909,8 +941,7 @@ static u64 btrfs_calc_reclaim_metadata_size(struct btrfs_fs_info *fs_info,
lockdep_assert_held(&space_info->lock);
- avail = calc_available_free_space(fs_info, space_info,
- BTRFS_RESERVE_FLUSH_ALL);
+ avail = calc_available_free_space(space_info, BTRFS_RESERVE_FLUSH_ALL);
used = btrfs_space_info_used(space_info, true);
/*
@@ -925,18 +956,25 @@ static u64 btrfs_calc_reclaim_metadata_size(struct btrfs_fs_info *fs_info,
return to_reclaim;
}
-static bool need_preemptive_reclaim(struct btrfs_fs_info *fs_info,
- const struct btrfs_space_info *space_info)
+static bool need_preemptive_reclaim(const struct btrfs_space_info *space_info)
{
+ struct btrfs_fs_info *fs_info = space_info->fs_info;
const u64 global_rsv_size = btrfs_block_rsv_reserved(&fs_info->global_block_rsv);
u64 ordered, delalloc;
u64 thresh;
u64 used;
- thresh = mult_perc(space_info->total_bytes, 90);
-
lockdep_assert_held(&space_info->lock);
+ /*
+ * We have tickets queued, bail so we don't compete with the async
+ * flushers.
+ */
+ if (space_info->reclaim_size)
+ return false;
+
+ thresh = mult_perc(space_info->total_bytes, 90);
+
/* If we're just plain full then async reclaim just slows us down. */
if ((space_info->bytes_used + space_info->bytes_reserved +
global_rsv_size) >= thresh)
@@ -957,13 +995,6 @@ static bool need_preemptive_reclaim(struct btrfs_fs_info *fs_info,
return false;
/*
- * We have tickets queued, bail so we don't compete with the async
- * flushers.
- */
- if (space_info->reclaim_size)
- return false;
-
- /*
* If we have over half of the free space occupied by reservations or
* pinned then we want to start flushing.
*
@@ -992,8 +1023,7 @@ static bool need_preemptive_reclaim(struct btrfs_fs_info *fs_info,
* much delalloc we need for the background flusher to kick in.
*/
- thresh = calc_available_free_space(fs_info, space_info,
- BTRFS_RESERVE_FLUSH_ALL);
+ thresh = calc_available_free_space(space_info, BTRFS_RESERVE_FLUSH_ALL);
used = space_info->bytes_used + space_info->bytes_reserved +
space_info->bytes_readonly + global_rsv_size;
if (used < space_info->total_bytes)
@@ -1037,13 +1067,15 @@ static bool need_preemptive_reclaim(struct btrfs_fs_info *fs_info,
!test_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state));
}
-static bool steal_from_global_rsv(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info,
+static bool steal_from_global_rsv(struct btrfs_space_info *space_info,
struct reserve_ticket *ticket)
{
+ struct btrfs_fs_info *fs_info = space_info->fs_info;
struct btrfs_block_rsv *global_rsv = &fs_info->global_block_rsv;
u64 min_bytes;
+ lockdep_assert_held(&space_info->lock);
+
if (!ticket->steal)
return false;
@@ -1057,21 +1089,19 @@ static bool steal_from_global_rsv(struct btrfs_fs_info *fs_info,
return false;
}
global_rsv->reserved -= ticket->bytes;
- remove_ticket(space_info, ticket);
- ticket->bytes = 0;
- wake_up(&ticket->wait);
- space_info->tickets_id++;
if (global_rsv->reserved < global_rsv->size)
- global_rsv->full = 0;
+ global_rsv->full = false;
spin_unlock(&global_rsv->lock);
+ remove_ticket(space_info, ticket, 0);
+ space_info->tickets_id++;
+
return true;
}
/*
* We've exhausted our flushing, start failing tickets.
*
- * @fs_info - fs_info for this fs
* @space_info - the space info we were flushing
*
* We call this when we've exhausted our flushing ability and haven't made
@@ -1084,47 +1114,44 @@ static bool steal_from_global_rsv(struct btrfs_fs_info *fs_info,
* other tickets, or if it stumbles across a ticket that was smaller than the
* first ticket.
*/
-static bool maybe_fail_all_tickets(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info)
+static bool maybe_fail_all_tickets(struct btrfs_space_info *space_info)
{
+ struct btrfs_fs_info *fs_info = space_info->fs_info;
struct reserve_ticket *ticket;
u64 tickets_id = space_info->tickets_id;
- const bool aborted = BTRFS_FS_ERROR(fs_info);
+ const int abort_error = BTRFS_FS_ERROR(fs_info);
trace_btrfs_fail_all_tickets(fs_info, space_info);
if (btrfs_test_opt(fs_info, ENOSPC_DEBUG)) {
btrfs_info(fs_info, "cannot satisfy tickets, dumping space info");
- __btrfs_dump_space_info(fs_info, space_info);
+ __btrfs_dump_space_info(space_info);
}
while (!list_empty(&space_info->tickets) &&
tickets_id == space_info->tickets_id) {
ticket = list_first_entry(&space_info->tickets,
struct reserve_ticket, list);
+ if (unlikely(abort_error)) {
+ remove_ticket(space_info, ticket, abort_error);
+ } else {
+ if (steal_from_global_rsv(space_info, ticket))
+ return true;
- if (!aborted && steal_from_global_rsv(fs_info, space_info, ticket))
- return true;
-
- if (!aborted && btrfs_test_opt(fs_info, ENOSPC_DEBUG))
- btrfs_info(fs_info, "failing ticket with %llu bytes",
- ticket->bytes);
+ if (btrfs_test_opt(fs_info, ENOSPC_DEBUG))
+ btrfs_info(fs_info, "failing ticket with %llu bytes",
+ ticket->bytes);
- remove_ticket(space_info, ticket);
- if (aborted)
- ticket->error = -EIO;
- else
- ticket->error = -ENOSPC;
- wake_up(&ticket->wait);
+ remove_ticket(space_info, ticket, -ENOSPC);
- /*
- * We're just throwing tickets away, so more flushing may not
- * trip over btrfs_try_granting_tickets, so we need to call it
- * here to see if we can make progress with the next ticket in
- * the list.
- */
- if (!aborted)
- btrfs_try_granting_tickets(fs_info, space_info);
+ /*
+ * We're just throwing tickets away, so more flushing may
+ * not trip over btrfs_try_granting_tickets, so we need
+ * to call it here to see if we can make progress with
+ * the next ticket in the list.
+ */
+ btrfs_try_granting_tickets(space_info);
+ }
}
return (tickets_id != space_info->tickets_id);
}
@@ -1144,9 +1171,9 @@ static void do_async_reclaim_metadata_space(struct btrfs_space_info *space_info)
final_state = COMMIT_TRANS;
spin_lock(&space_info->lock);
- to_reclaim = btrfs_calc_reclaim_metadata_size(fs_info, space_info);
+ to_reclaim = btrfs_calc_reclaim_metadata_size(space_info);
if (!to_reclaim) {
- space_info->flush = 0;
+ space_info->flush = false;
spin_unlock(&space_info->lock);
return;
}
@@ -1155,15 +1182,14 @@ static void do_async_reclaim_metadata_space(struct btrfs_space_info *space_info)
flush_state = FLUSH_DELAYED_ITEMS_NR;
do {
- flush_space(fs_info, space_info, to_reclaim, flush_state, false);
+ flush_space(space_info, to_reclaim, flush_state, false);
spin_lock(&space_info->lock);
if (list_empty(&space_info->tickets)) {
- space_info->flush = 0;
+ space_info->flush = false;
spin_unlock(&space_info->lock);
return;
}
- to_reclaim = btrfs_calc_reclaim_metadata_size(fs_info,
- space_info);
+ to_reclaim = btrfs_calc_reclaim_metadata_size(space_info);
if (last_tickets_id == space_info->tickets_id) {
flush_state++;
} else {
@@ -1197,11 +1223,11 @@ static void do_async_reclaim_metadata_space(struct btrfs_space_info *space_info)
if (flush_state > final_state) {
commit_cycles++;
if (commit_cycles > 2) {
- if (maybe_fail_all_tickets(fs_info, space_info)) {
+ if (maybe_fail_all_tickets(space_info)) {
flush_state = FLUSH_DELAYED_ITEMS_NR;
commit_cycles--;
} else {
- space_info->flush = 0;
+ space_info->flush = false;
}
} else {
flush_state = FLUSH_DELAYED_ITEMS_NR;
@@ -1257,14 +1283,15 @@ static void btrfs_preempt_reclaim_metadata_space(struct work_struct *work)
trans_rsv = &fs_info->trans_block_rsv;
spin_lock(&space_info->lock);
- while (need_preemptive_reclaim(fs_info, space_info)) {
+ while (need_preemptive_reclaim(space_info)) {
enum btrfs_flush_state flush;
u64 delalloc_size = 0;
u64 to_reclaim, block_rsv_size;
const u64 global_rsv_size = btrfs_block_rsv_reserved(global_rsv);
+ const u64 bytes_may_use = space_info->bytes_may_use;
+ const u64 bytes_pinned = space_info->bytes_pinned;
- loops++;
-
+ spin_unlock(&space_info->lock);
/*
* We don't have a precise counter for the metadata being
* reserved for delalloc, so we'll approximate it by subtracting
@@ -1276,8 +1303,8 @@ static void btrfs_preempt_reclaim_metadata_space(struct work_struct *work)
btrfs_block_rsv_reserved(delayed_block_rsv) +
btrfs_block_rsv_reserved(delayed_refs_rsv) +
btrfs_block_rsv_reserved(trans_rsv);
- if (block_rsv_size < space_info->bytes_may_use)
- delalloc_size = space_info->bytes_may_use - block_rsv_size;
+ if (block_rsv_size < bytes_may_use)
+ delalloc_size = bytes_may_use - block_rsv_size;
/*
* We don't want to include the global_rsv in our calculation,
@@ -1294,10 +1321,10 @@ static void btrfs_preempt_reclaim_metadata_space(struct work_struct *work)
if (delalloc_size > block_rsv_size) {
to_reclaim = delalloc_size;
flush = FLUSH_DELALLOC;
- } else if (space_info->bytes_pinned >
+ } else if (bytes_pinned >
(btrfs_block_rsv_reserved(delayed_block_rsv) +
btrfs_block_rsv_reserved(delayed_refs_rsv))) {
- to_reclaim = space_info->bytes_pinned;
+ to_reclaim = bytes_pinned;
flush = COMMIT_TRANS;
} else if (btrfs_block_rsv_reserved(delayed_block_rsv) >
btrfs_block_rsv_reserved(delayed_refs_rsv)) {
@@ -1308,7 +1335,7 @@ static void btrfs_preempt_reclaim_metadata_space(struct work_struct *work)
flush = FLUSH_DELAYED_REFS_NR;
}
- spin_unlock(&space_info->lock);
+ loops++;
/*
* We don't want to reclaim everything, just a portion, so scale
@@ -1318,7 +1345,7 @@ static void btrfs_preempt_reclaim_metadata_space(struct work_struct *work)
to_reclaim >>= 2;
if (!to_reclaim)
to_reclaim = btrfs_calc_insert_metadata_size(fs_info, 1);
- flush_space(fs_info, space_info, to_reclaim, flush, true);
+ flush_space(space_info, to_reclaim, flush, true);
cond_resched();
spin_lock(&space_info->lock);
}
@@ -1383,7 +1410,7 @@ static void do_async_reclaim_data_space(struct btrfs_space_info *space_info)
spin_lock(&space_info->lock);
if (list_empty(&space_info->tickets)) {
- space_info->flush = 0;
+ space_info->flush = false;
spin_unlock(&space_info->lock);
return;
}
@@ -1391,27 +1418,27 @@ static void do_async_reclaim_data_space(struct btrfs_space_info *space_info)
spin_unlock(&space_info->lock);
while (!space_info->full) {
- flush_space(fs_info, space_info, U64_MAX, ALLOC_CHUNK_FORCE, false);
+ flush_space(space_info, U64_MAX, ALLOC_CHUNK_FORCE, false);
spin_lock(&space_info->lock);
if (list_empty(&space_info->tickets)) {
- space_info->flush = 0;
+ space_info->flush = false;
spin_unlock(&space_info->lock);
return;
}
/* Something happened, fail everything and bail. */
- if (BTRFS_FS_ERROR(fs_info))
+ if (unlikely(BTRFS_FS_ERROR(fs_info)))
goto aborted_fs;
last_tickets_id = space_info->tickets_id;
spin_unlock(&space_info->lock);
}
while (flush_state < ARRAY_SIZE(data_flush_states)) {
- flush_space(fs_info, space_info, U64_MAX,
+ flush_space(space_info, U64_MAX,
data_flush_states[flush_state], false);
spin_lock(&space_info->lock);
if (list_empty(&space_info->tickets)) {
- space_info->flush = 0;
+ space_info->flush = false;
spin_unlock(&space_info->lock);
return;
}
@@ -1425,16 +1452,16 @@ static void do_async_reclaim_data_space(struct btrfs_space_info *space_info)
if (flush_state >= ARRAY_SIZE(data_flush_states)) {
if (space_info->full) {
- if (maybe_fail_all_tickets(fs_info, space_info))
+ if (maybe_fail_all_tickets(space_info))
flush_state = 0;
else
- space_info->flush = 0;
+ space_info->flush = false;
} else {
flush_state = 0;
}
/* Something happened, fail everything and bail. */
- if (BTRFS_FS_ERROR(fs_info))
+ if (unlikely(BTRFS_FS_ERROR(fs_info)))
goto aborted_fs;
}
@@ -1443,8 +1470,8 @@ static void do_async_reclaim_data_space(struct btrfs_space_info *space_info)
return;
aborted_fs:
- maybe_fail_all_tickets(fs_info, space_info);
- space_info->flush = 0;
+ maybe_fail_all_tickets(space_info);
+ space_info->flush = false;
spin_unlock(&space_info->lock);
}
@@ -1489,40 +1516,47 @@ static const enum btrfs_flush_state evict_flush_states[] = {
RESET_ZONES,
};
-static void priority_reclaim_metadata_space(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info,
- struct reserve_ticket *ticket,
- const enum btrfs_flush_state *states,
- int states_nr)
+static bool is_ticket_served(struct reserve_ticket *ticket)
{
+ bool ret;
+
+ spin_lock(&ticket->lock);
+ ret = (ticket->bytes == 0);
+ spin_unlock(&ticket->lock);
+
+ return ret;
+}
+
+static void priority_reclaim_metadata_space(struct btrfs_space_info *space_info,
+ struct reserve_ticket *ticket,
+ const enum btrfs_flush_state *states,
+ int states_nr)
+{
+ struct btrfs_fs_info *fs_info = space_info->fs_info;
u64 to_reclaim;
int flush_state = 0;
- spin_lock(&space_info->lock);
- to_reclaim = btrfs_calc_reclaim_metadata_size(fs_info, space_info);
/*
* This is the priority reclaim path, so to_reclaim could be >0 still
* because we may have only satisfied the priority tickets and still
* left non priority tickets on the list. We would then have
* to_reclaim but ->bytes == 0.
*/
- if (ticket->bytes == 0) {
- spin_unlock(&space_info->lock);
+ if (is_ticket_served(ticket))
return;
- }
+
+ spin_lock(&space_info->lock);
+ to_reclaim = btrfs_calc_reclaim_metadata_size(space_info);
+ spin_unlock(&space_info->lock);
while (flush_state < states_nr) {
- spin_unlock(&space_info->lock);
- flush_space(fs_info, space_info, to_reclaim, states[flush_state],
- false);
- flush_state++;
- spin_lock(&space_info->lock);
- if (ticket->bytes == 0) {
- spin_unlock(&space_info->lock);
+ flush_space(space_info, to_reclaim, states[flush_state], false);
+ if (is_ticket_served(ticket))
return;
- }
+ flush_state++;
}
+ spin_lock(&space_info->lock);
/*
* Attempt to steal from the global rsv if we can, except if the fs was
* turned into error mode due to a transaction abort when flushing space
@@ -1531,48 +1565,38 @@ static void priority_reclaim_metadata_space(struct btrfs_fs_info *fs_info,
* just to have caller fail immediately instead of later when trying to
* modify the fs, making it easier to debug -ENOSPC problems.
*/
- if (BTRFS_FS_ERROR(fs_info)) {
- ticket->error = BTRFS_FS_ERROR(fs_info);
- remove_ticket(space_info, ticket);
- } else if (!steal_from_global_rsv(fs_info, space_info, ticket)) {
- ticket->error = -ENOSPC;
- remove_ticket(space_info, ticket);
- }
+ if (unlikely(BTRFS_FS_ERROR(fs_info)))
+ remove_ticket(space_info, ticket, BTRFS_FS_ERROR(fs_info));
+ else if (!steal_from_global_rsv(space_info, ticket))
+ remove_ticket(space_info, ticket, -ENOSPC);
/*
* We must run try_granting_tickets here because we could be a large
* ticket in front of a smaller ticket that can now be satisfied with
* the available space.
*/
- btrfs_try_granting_tickets(fs_info, space_info);
+ btrfs_try_granting_tickets(space_info);
spin_unlock(&space_info->lock);
}
-static void priority_reclaim_data_space(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info,
+static void priority_reclaim_data_space(struct btrfs_space_info *space_info,
struct reserve_ticket *ticket)
{
- spin_lock(&space_info->lock);
-
/* We could have been granted before we got here. */
- if (ticket->bytes == 0) {
- spin_unlock(&space_info->lock);
+ if (is_ticket_served(ticket))
return;
- }
+ spin_lock(&space_info->lock);
while (!space_info->full) {
spin_unlock(&space_info->lock);
- flush_space(fs_info, space_info, U64_MAX, ALLOC_CHUNK_FORCE, false);
- spin_lock(&space_info->lock);
- if (ticket->bytes == 0) {
- spin_unlock(&space_info->lock);
+ flush_space(space_info, U64_MAX, ALLOC_CHUNK_FORCE, false);
+ if (is_ticket_served(ticket))
return;
- }
+ spin_lock(&space_info->lock);
}
- ticket->error = -ENOSPC;
- remove_ticket(space_info, ticket);
- btrfs_try_granting_tickets(fs_info, space_info);
+ remove_ticket(space_info, ticket, -ENOSPC);
+ btrfs_try_granting_tickets(space_info);
spin_unlock(&space_info->lock);
}
@@ -1581,11 +1605,13 @@ static void wait_reserve_ticket(struct btrfs_space_info *space_info,
{
DEFINE_WAIT(wait);
- int ret = 0;
- spin_lock(&space_info->lock);
+ spin_lock(&ticket->lock);
while (ticket->bytes > 0 && ticket->error == 0) {
+ int ret;
+
ret = prepare_to_wait_event(&ticket->wait, &wait, TASK_KILLABLE);
+ spin_unlock(&ticket->lock);
if (ret) {
/*
* Delete us from the list. After we unlock the space
@@ -1595,24 +1621,23 @@ static void wait_reserve_ticket(struct btrfs_space_info *space_info,
* despite getting an error, resulting in a space leak
* (bytes_may_use counter of our space_info).
*/
- remove_ticket(space_info, ticket);
- ticket->error = -EINTR;
- break;
+ spin_lock(&space_info->lock);
+ remove_ticket(space_info, ticket, -EINTR);
+ spin_unlock(&space_info->lock);
+ return;
}
- spin_unlock(&space_info->lock);
schedule();
finish_wait(&ticket->wait, &wait);
- spin_lock(&space_info->lock);
+ spin_lock(&ticket->lock);
}
- spin_unlock(&space_info->lock);
+ spin_unlock(&ticket->lock);
}
/*
* Do the appropriate flushing and waiting for a ticket.
*
- * @fs_info: the filesystem
* @space_info: space info for the reservation
* @ticket: ticket for the reservation
* @start_ns: timestamp when the reservation started
@@ -1622,8 +1647,7 @@ static void wait_reserve_ticket(struct btrfs_space_info *space_info,
* This does the work of figuring out how to flush for the ticket, waiting for
* the reservation, and returning the appropriate error if there is one.
*/
-static int handle_reserve_ticket(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info,
+static int handle_reserve_ticket(struct btrfs_space_info *space_info,
struct reserve_ticket *ticket,
u64 start_ns, u64 orig_bytes,
enum btrfs_reserve_flush_enum flush)
@@ -1637,20 +1661,20 @@ static int handle_reserve_ticket(struct btrfs_fs_info *fs_info,
wait_reserve_ticket(space_info, ticket);
break;
case BTRFS_RESERVE_FLUSH_LIMIT:
- priority_reclaim_metadata_space(fs_info, space_info, ticket,
+ priority_reclaim_metadata_space(space_info, ticket,
priority_flush_states,
ARRAY_SIZE(priority_flush_states));
break;
case BTRFS_RESERVE_FLUSH_EVICT:
- priority_reclaim_metadata_space(fs_info, space_info, ticket,
+ priority_reclaim_metadata_space(space_info, ticket,
evict_flush_states,
ARRAY_SIZE(evict_flush_states));
break;
case BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE:
- priority_reclaim_data_space(fs_info, space_info, ticket);
+ priority_reclaim_data_space(space_info, ticket);
break;
default:
- ASSERT(0);
+ ASSERT(0, "flush=%d", flush);
break;
}
@@ -1662,9 +1686,10 @@ static int handle_reserve_ticket(struct btrfs_fs_info *fs_info,
* releasing reserved space (if an error happens the expectation is that
* space wasn't reserved at all).
*/
- ASSERT(!(ticket->bytes == 0 && ticket->error));
- trace_btrfs_reserve_ticket(fs_info, space_info->flags, orig_bytes,
- start_ns, flush, ticket->error);
+ ASSERT(!(ticket->bytes == 0 && ticket->error),
+ "ticket->bytes=%llu ticket->error=%d", ticket->bytes, ticket->error);
+ trace_btrfs_reserve_ticket(space_info->fs_info, space_info->flags,
+ orig_bytes, start_ns, flush, ticket->error);
return ret;
}
@@ -1678,9 +1703,9 @@ static inline bool is_normal_flushing(enum btrfs_reserve_flush_enum flush)
(flush == BTRFS_RESERVE_FLUSH_ALL_STEAL);
}
-static inline void maybe_clamp_preempt(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info)
+static inline void maybe_clamp_preempt(struct btrfs_space_info *space_info)
{
+ struct btrfs_fs_info *fs_info = space_info->fs_info;
u64 ordered = percpu_counter_sum_positive(&fs_info->ordered_bytes);
u64 delalloc = percpu_counter_sum_positive(&fs_info->delalloc_bytes);
@@ -1715,7 +1740,6 @@ static inline bool can_ticket(enum btrfs_reserve_flush_enum flush)
/*
* Try to reserve bytes from the block_rsv's space.
*
- * @fs_info: the filesystem
* @space_info: space info we want to allocate from
* @orig_bytes: number of bytes we want
* @flush: whether or not we can flush to make our reservation
@@ -1727,10 +1751,10 @@ static inline bool can_ticket(enum btrfs_reserve_flush_enum flush)
* regain reservations will be made and this will fail if there is not enough
* space already.
*/
-static int __reserve_bytes(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info, u64 orig_bytes,
- enum btrfs_reserve_flush_enum flush)
+static int reserve_bytes(struct btrfs_space_info *space_info, u64 orig_bytes,
+ enum btrfs_reserve_flush_enum flush)
{
+ struct btrfs_fs_info *fs_info = space_info->fs_info;
struct work_struct *async_work;
struct reserve_ticket ticket;
u64 start_ns = 0;
@@ -1738,7 +1762,7 @@ static int __reserve_bytes(struct btrfs_fs_info *fs_info,
int ret = -ENOSPC;
bool pending_tickets;
- ASSERT(orig_bytes);
+ ASSERT(orig_bytes, "orig_bytes=%llu", orig_bytes);
/*
* If have a transaction handle (current->journal_info != NULL), then
* the flush method can not be neither BTRFS_RESERVE_FLUSH_ALL* nor
@@ -1747,9 +1771,9 @@ static int __reserve_bytes(struct btrfs_fs_info *fs_info,
*/
if (current->journal_info) {
/* One assert per line for easier debugging. */
- ASSERT(flush != BTRFS_RESERVE_FLUSH_ALL);
- ASSERT(flush != BTRFS_RESERVE_FLUSH_ALL_STEAL);
- ASSERT(flush != BTRFS_RESERVE_FLUSH_EVICT);
+ ASSERT(flush != BTRFS_RESERVE_FLUSH_ALL, "flush=%d", flush);
+ ASSERT(flush != BTRFS_RESERVE_FLUSH_ALL_STEAL, "flush=%d", flush);
+ ASSERT(flush != BTRFS_RESERVE_FLUSH_EVICT, "flush=%d", flush);
}
if (flush == BTRFS_RESERVE_FLUSH_DATA)
@@ -1777,7 +1801,7 @@ static int __reserve_bytes(struct btrfs_fs_info *fs_info,
*/
if (!pending_tickets &&
((used + orig_bytes <= space_info->total_bytes) ||
- btrfs_can_overcommit(fs_info, space_info, orig_bytes, flush))) {
+ can_overcommit(space_info, used, orig_bytes, flush))) {
btrfs_space_info_update_bytes_may_use(space_info, orig_bytes);
ret = 0;
}
@@ -1788,7 +1812,7 @@ static int __reserve_bytes(struct btrfs_fs_info *fs_info,
* left to allocate for the block.
*/
if (ret && unlikely(flush == BTRFS_RESERVE_FLUSH_EMERGENCY)) {
- used = btrfs_space_info_used(space_info, false);
+ used -= space_info->bytes_may_use;
if (used + orig_bytes <= space_info->total_bytes) {
btrfs_space_info_update_bytes_may_use(space_info, orig_bytes);
ret = 0;
@@ -1807,6 +1831,7 @@ static int __reserve_bytes(struct btrfs_fs_info *fs_info,
ticket.error = 0;
space_info->reclaim_size += ticket.bytes;
init_waitqueue_head(&ticket.wait);
+ spin_lock_init(&ticket.lock);
ticket.steal = can_steal(flush);
if (trace_btrfs_reserve_ticket_enabled())
start_ns = ktime_get_ns();
@@ -1823,9 +1848,9 @@ static int __reserve_bytes(struct btrfs_fs_info *fs_info,
* preemptive flushing in order to keep up with
* the workload.
*/
- maybe_clamp_preempt(fs_info, space_info);
+ maybe_clamp_preempt(space_info);
- space_info->flush = 1;
+ space_info->flush = true;
trace_btrfs_trigger_flush(fs_info,
space_info->flags,
orig_bytes, flush,
@@ -1844,7 +1869,7 @@ static int __reserve_bytes(struct btrfs_fs_info *fs_info,
*/
if (!test_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags) &&
!work_busy(&fs_info->preempt_reclaim_work) &&
- need_preemptive_reclaim(fs_info, space_info)) {
+ need_preemptive_reclaim(space_info)) {
trace_btrfs_trigger_flush(fs_info, space_info->flags,
orig_bytes, flush, "preempt");
queue_work(system_dfl_wq,
@@ -1855,14 +1880,12 @@ static int __reserve_bytes(struct btrfs_fs_info *fs_info,
if (!ret || !can_ticket(flush))
return ret;
- return handle_reserve_ticket(fs_info, space_info, &ticket, start_ns,
- orig_bytes, flush);
+ return handle_reserve_ticket(space_info, &ticket, start_ns, orig_bytes, flush);
}
/*
* Try to reserve metadata bytes from the block_rsv's space.
*
- * @fs_info: the filesystem
* @space_info: the space_info we're allocating for
* @orig_bytes: number of bytes we want
* @flush: whether or not we can flush to make our reservation
@@ -1874,20 +1897,21 @@ static int __reserve_bytes(struct btrfs_fs_info *fs_info,
* regain reservations will be made and this will fail if there is not enough
* space already.
*/
-int btrfs_reserve_metadata_bytes(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info,
+int btrfs_reserve_metadata_bytes(struct btrfs_space_info *space_info,
u64 orig_bytes,
enum btrfs_reserve_flush_enum flush)
{
int ret;
- ret = __reserve_bytes(fs_info, space_info, orig_bytes, flush);
+ ret = reserve_bytes(space_info, orig_bytes, flush);
if (ret == -ENOSPC) {
+ struct btrfs_fs_info *fs_info = space_info->fs_info;
+
trace_btrfs_space_reservation(fs_info, "space_info:enospc",
space_info->flags, orig_bytes, 1);
if (btrfs_test_opt(fs_info, ENOSPC_DEBUG))
- btrfs_dump_space_info(fs_info, space_info, orig_bytes, false);
+ btrfs_dump_space_info(space_info, orig_bytes, false);
}
return ret;
}
@@ -1895,7 +1919,7 @@ int btrfs_reserve_metadata_bytes(struct btrfs_fs_info *fs_info,
/*
* Try to reserve data bytes for an allocation.
*
- * @fs_info: the filesystem
+ * @space_info: the space_info we're allocating for
* @bytes: number of bytes we need
* @flush: how we are allowed to flush
*
@@ -1910,15 +1934,17 @@ int btrfs_reserve_data_bytes(struct btrfs_space_info *space_info, u64 bytes,
ASSERT(flush == BTRFS_RESERVE_FLUSH_DATA ||
flush == BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE ||
- flush == BTRFS_RESERVE_NO_FLUSH);
- ASSERT(!current->journal_info || flush != BTRFS_RESERVE_FLUSH_DATA);
+ flush == BTRFS_RESERVE_NO_FLUSH, "flush=%d", flush);
+ ASSERT(!current->journal_info || flush != BTRFS_RESERVE_FLUSH_DATA,
+ "current->journal_info=0x%lx flush=%d",
+ (unsigned long)current->journal_info, flush);
- ret = __reserve_bytes(fs_info, space_info, bytes, flush);
+ ret = reserve_bytes(space_info, bytes, flush);
if (ret == -ENOSPC) {
trace_btrfs_space_reservation(fs_info, "space_info:enospc",
space_info->flags, bytes, 1);
if (btrfs_test_opt(fs_info, ENOSPC_DEBUG))
- btrfs_dump_space_info(fs_info, space_info, bytes, false);
+ btrfs_dump_space_info(space_info, bytes, false);
}
return ret;
}
@@ -1931,7 +1957,7 @@ __cold void btrfs_dump_space_info_for_trans_abort(struct btrfs_fs_info *fs_info)
btrfs_info(fs_info, "dumping space info:");
list_for_each_entry(space_info, &fs_info->space_info, list) {
spin_lock(&space_info->lock);
- __btrfs_dump_space_info(fs_info, space_info);
+ __btrfs_dump_space_info(space_info);
spin_unlock(&space_info->lock);
}
dump_global_block_rsv(fs_info);
@@ -1948,7 +1974,7 @@ u64 btrfs_account_ro_block_groups_free_space(struct btrfs_space_info *sinfo)
int factor;
/* It's df, we don't care if it's racy */
- if (list_empty(&sinfo->ro_bgs))
+ if (data_race(list_empty(&sinfo->ro_bgs)))
return 0;
spin_lock(&sinfo->lock);
@@ -2187,7 +2213,7 @@ void btrfs_return_free_space(struct btrfs_space_info *space_info, u64 len)
global_rsv->reserved += to_add;
btrfs_space_info_update_bytes_may_use(space_info, to_add);
if (global_rsv->reserved >= global_rsv->size)
- global_rsv->full = 1;
+ global_rsv->full = true;
len -= to_add;
}
spin_unlock(&global_rsv->lock);
@@ -2195,5 +2221,5 @@ void btrfs_return_free_space(struct btrfs_space_info *space_info, u64 len)
grant:
/* Add to any tickets we may have. */
if (len)
- btrfs_try_granting_tickets(fs_info, space_info);
+ btrfs_try_granting_tickets(space_info);
}
diff --git a/fs/btrfs/space-info.h b/fs/btrfs/space-info.h
index 679f22efb407..446c0614ad4a 100644
--- a/fs/btrfs/space-info.h
+++ b/fs/btrfs/space-info.h
@@ -142,11 +142,11 @@ struct btrfs_space_info {
flushing. The value is >> clamp, so turns
out to be a 2^clamp divisor. */
- unsigned int full:1; /* indicates that we cannot allocate any more
+ bool full; /* indicates that we cannot allocate any more
chunks for this space */
- unsigned int chunk_alloc:1; /* set if we are allocating a chunk */
+ bool chunk_alloc; /* set if we are allocating a chunk */
- unsigned int flush:1; /* set if we are trying to make space */
+ bool flush; /* set if we are trying to make space */
unsigned int force_alloc; /* set if we need to force a chunk
alloc for this space */
@@ -224,14 +224,6 @@ struct btrfs_space_info {
s64 reclaimable_bytes;
};
-struct reserve_ticket {
- u64 bytes;
- int error;
- bool steal;
- struct list_head list;
- wait_queue_head_t wait;
-};
-
static inline bool btrfs_mixed_space_info(const struct btrfs_space_info *space_info)
{
return ((space_info->flags & BTRFS_BLOCK_GROUP_METADATA) &&
@@ -266,6 +258,17 @@ DECLARE_SPACE_INFO_UPDATE(bytes_may_use, "space_info");
DECLARE_SPACE_INFO_UPDATE(bytes_pinned, "pinned");
DECLARE_SPACE_INFO_UPDATE(bytes_zone_unusable, "zone_unusable");
+static inline u64 btrfs_space_info_used(const struct btrfs_space_info *s_info,
+ bool may_use_included)
+{
+ lockdep_assert_held(&s_info->lock);
+
+ return s_info->bytes_used + s_info->bytes_reserved +
+ s_info->bytes_pinned + s_info->bytes_readonly +
+ s_info->bytes_zone_unusable +
+ (may_use_included ? s_info->bytes_may_use : 0);
+}
+
int btrfs_init_space_info(struct btrfs_fs_info *fs_info);
void btrfs_add_bg_to_space_info(struct btrfs_fs_info *info,
struct btrfs_block_group *block_group);
@@ -273,21 +276,15 @@ void btrfs_update_space_info_chunk_size(struct btrfs_space_info *space_info,
u64 chunk_size);
struct btrfs_space_info *btrfs_find_space_info(struct btrfs_fs_info *info,
u64 flags);
-u64 __pure btrfs_space_info_used(const struct btrfs_space_info *s_info,
- bool may_use_included);
void btrfs_clear_space_info_full(struct btrfs_fs_info *info);
-void btrfs_dump_space_info(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *info, u64 bytes,
+void btrfs_dump_space_info(struct btrfs_space_info *info, u64 bytes,
bool dump_block_groups);
-int btrfs_reserve_metadata_bytes(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info,
+int btrfs_reserve_metadata_bytes(struct btrfs_space_info *space_info,
u64 orig_bytes,
enum btrfs_reserve_flush_enum flush);
-void btrfs_try_granting_tickets(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info);
-int btrfs_can_overcommit(struct btrfs_fs_info *fs_info,
- const struct btrfs_space_info *space_info, u64 bytes,
- enum btrfs_reserve_flush_enum flush);
+void btrfs_try_granting_tickets(struct btrfs_space_info *space_info);
+bool btrfs_can_overcommit(const struct btrfs_space_info *space_info, u64 bytes,
+ enum btrfs_reserve_flush_enum flush);
static inline void btrfs_space_info_free_bytes_may_use(
struct btrfs_space_info *space_info,
@@ -295,7 +292,7 @@ static inline void btrfs_space_info_free_bytes_may_use(
{
spin_lock(&space_info->lock);
btrfs_space_info_update_bytes_may_use(space_info, -num_bytes);
- btrfs_try_granting_tickets(space_info->fs_info, space_info);
+ btrfs_try_granting_tickets(space_info);
spin_unlock(&space_info->lock);
}
int btrfs_reserve_data_bytes(struct btrfs_space_info *space_info, u64 bytes,
diff --git a/fs/btrfs/subpage.c b/fs/btrfs/subpage.c
index a7ba868e9372..f82e71f5d88b 100644
--- a/fs/btrfs/subpage.c
+++ b/fs/btrfs/subpage.c
@@ -180,7 +180,7 @@ static void btrfs_subpage_assert(const struct btrfs_fs_info *fs_info,
/* Basic checks */
ASSERT(folio_test_private(folio) && folio_get_private(folio));
ASSERT(IS_ALIGNED(start, fs_info->sectorsize) &&
- IS_ALIGNED(len, fs_info->sectorsize));
+ IS_ALIGNED(len, fs_info->sectorsize), "start=%llu len=%u", start, len);
/*
* The range check only works for mapped page, we can still have
* unmapped page like dummy extent buffer pages.
@@ -195,12 +195,11 @@ static void btrfs_subpage_assert(const struct btrfs_fs_info *fs_info,
#define subpage_calc_start_bit(fs_info, folio, name, start, len) \
({ \
unsigned int __start_bit; \
- const unsigned int blocks_per_folio = \
- btrfs_blocks_per_folio(fs_info, folio); \
+ const unsigned int __bpf = btrfs_blocks_per_folio(fs_info, folio); \
\
btrfs_subpage_assert(fs_info, folio, start, len); \
__start_bit = offset_in_folio(folio, start) >> fs_info->sectorsize_bits; \
- __start_bit += blocks_per_folio * btrfs_bitmap_nr_##name; \
+ __start_bit += __bpf * btrfs_bitmap_nr_##name; \
__start_bit; \
})
@@ -251,7 +250,9 @@ static bool btrfs_subpage_end_and_test_lock(const struct btrfs_fs_info *fs_info,
clear_bit(bit, bfs->bitmaps);
cleared++;
}
- ASSERT(atomic_read(&bfs->nr_locked) >= cleared);
+ ASSERT(atomic_read(&bfs->nr_locked) >= cleared,
+ "atomic_read(&bfs->nr_locked)=%d cleared=%d",
+ atomic_read(&bfs->nr_locked), cleared);
last = atomic_sub_and_test(cleared, &bfs->nr_locked);
spin_unlock_irqrestore(&bfs->lock, flags);
return last;
@@ -330,7 +331,9 @@ void btrfs_folio_end_lock_bitmap(const struct btrfs_fs_info *fs_info,
if (test_and_clear_bit(bit + start_bit, bfs->bitmaps))
cleared++;
}
- ASSERT(atomic_read(&bfs->nr_locked) >= cleared);
+ ASSERT(atomic_read(&bfs->nr_locked) >= cleared,
+ "atomic_read(&bfs->nr_locked)=%d cleared=%d",
+ atomic_read(&bfs->nr_locked), cleared);
last = atomic_sub_and_test(cleared, &bfs->nr_locked);
spin_unlock_irqrestore(&bfs->lock, flags);
if (last)
@@ -339,24 +342,20 @@ void btrfs_folio_end_lock_bitmap(const struct btrfs_fs_info *fs_info,
#define subpage_test_bitmap_all_set(fs_info, folio, name) \
({ \
- struct btrfs_folio_state *bfs = folio_get_private(folio); \
- const unsigned int blocks_per_folio = \
- btrfs_blocks_per_folio(fs_info, folio); \
+ struct btrfs_folio_state *__bfs = folio_get_private(folio); \
+ const unsigned int __bpf = btrfs_blocks_per_folio(fs_info, folio); \
\
- bitmap_test_range_all_set(bfs->bitmaps, \
- blocks_per_folio * btrfs_bitmap_nr_##name, \
- blocks_per_folio); \
+ bitmap_test_range_all_set(__bfs->bitmaps, \
+ __bpf * btrfs_bitmap_nr_##name, __bpf); \
})
#define subpage_test_bitmap_all_zero(fs_info, folio, name) \
({ \
- struct btrfs_folio_state *bfs = folio_get_private(folio); \
- const unsigned int blocks_per_folio = \
- btrfs_blocks_per_folio(fs_info, folio); \
+ struct btrfs_folio_state *__bfs = folio_get_private(folio); \
+ const unsigned int __bpf = btrfs_blocks_per_folio(fs_info, folio); \
\
- bitmap_test_range_all_zero(bfs->bitmaps, \
- blocks_per_folio * btrfs_bitmap_nr_##name, \
- blocks_per_folio); \
+ bitmap_test_range_all_zero(__bfs->bitmaps, \
+ __bpf * btrfs_bitmap_nr_##name, __bpf); \
})
void btrfs_subpage_set_uptodate(const struct btrfs_fs_info *fs_info,
@@ -446,6 +445,7 @@ void btrfs_subpage_set_writeback(const struct btrfs_fs_info *fs_info,
unsigned int start_bit = subpage_calc_start_bit(fs_info, folio,
writeback, start, len);
unsigned long flags;
+ bool keep_write;
spin_lock_irqsave(&bfs->lock, flags);
bitmap_set(bfs->bitmaps, start_bit, len >> fs_info->sectorsize_bits);
@@ -456,18 +456,9 @@ void btrfs_subpage_set_writeback(const struct btrfs_fs_info *fs_info,
* assume writeback is complete, and exit too early — violating sync
* ordering guarantees.
*/
+ keep_write = folio_test_dirty(folio);
if (!folio_test_writeback(folio))
- __folio_start_writeback(folio, true);
- if (!folio_test_dirty(folio)) {
- struct address_space *mapping = folio_mapping(folio);
- XA_STATE(xas, &mapping->i_pages, folio->index);
- unsigned long flags;
-
- xas_lock_irqsave(&xas, flags);
- xas_load(&xas);
- xas_clear_mark(&xas, PAGECACHE_TAG_TOWRITE);
- xas_unlock_irqrestore(&xas, flags);
- }
+ __folio_start_writeback(folio, keep_write);
spin_unlock_irqrestore(&bfs->lock, flags);
}
@@ -673,27 +664,23 @@ IMPLEMENT_BTRFS_PAGE_OPS(checked, folio_set_checked, folio_clear_checked,
#define GET_SUBPAGE_BITMAP(fs_info, folio, name, dst) \
{ \
- const unsigned int blocks_per_folio = \
- btrfs_blocks_per_folio(fs_info, folio); \
- const struct btrfs_folio_state *bfs = folio_get_private(folio); \
+ const unsigned int __bpf = btrfs_blocks_per_folio(fs_info, folio); \
+ const struct btrfs_folio_state *__bfs = folio_get_private(folio); \
\
- ASSERT(blocks_per_folio <= BITS_PER_LONG); \
- *dst = bitmap_read(bfs->bitmaps, \
- blocks_per_folio * btrfs_bitmap_nr_##name, \
- blocks_per_folio); \
+ ASSERT(__bpf <= BITS_PER_LONG); \
+ *dst = bitmap_read(__bfs->bitmaps, \
+ __bpf * btrfs_bitmap_nr_##name, __bpf); \
}
#define SUBPAGE_DUMP_BITMAP(fs_info, folio, name, start, len) \
{ \
unsigned long bitmap; \
- const unsigned int blocks_per_folio = \
- btrfs_blocks_per_folio(fs_info, folio); \
+ const unsigned int __bpf = btrfs_blocks_per_folio(fs_info, folio); \
\
GET_SUBPAGE_BITMAP(fs_info, folio, name, &bitmap); \
btrfs_warn(fs_info, \
"dumping bitmap start=%llu len=%u folio=%llu " #name "_bitmap=%*pbl", \
- start, len, folio_pos(folio), \
- blocks_per_folio, &bitmap); \
+ start, len, folio_pos(folio), __bpf, &bitmap); \
}
/*
diff --git a/fs/btrfs/subpage.h b/fs/btrfs/subpage.h
index ad0552db7c7d..d81a0ade559f 100644
--- a/fs/btrfs/subpage.h
+++ b/fs/btrfs/subpage.h
@@ -7,7 +7,6 @@
#include <linux/atomic.h>
#include <linux/sizes.h>
#include "btrfs_inode.h"
-#include "fs.h"
struct address_space;
struct folio;
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 430e7419349c..1999533b52be 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -807,17 +807,15 @@ char *btrfs_get_subvol_name_from_objectid(struct btrfs_fs_info *fs_info,
struct btrfs_root_ref *root_ref;
struct btrfs_inode_ref *inode_ref;
struct btrfs_key key;
- struct btrfs_path *path = NULL;
+ BTRFS_PATH_AUTO_FREE(path);
char *name = NULL, *ptr;
u64 dirid;
int len;
int ret;
path = btrfs_alloc_path();
- if (!path) {
- ret = -ENOMEM;
- goto err;
- }
+ if (!path)
+ return ERR_PTR(-ENOMEM);
name = kmalloc(PATH_MAX, GFP_KERNEL);
if (!name) {
@@ -905,7 +903,6 @@ char *btrfs_get_subvol_name_from_objectid(struct btrfs_fs_info *fs_info,
fs_root = NULL;
}
- btrfs_free_path(path);
if (ptr == name + PATH_MAX - 1) {
name[0] = '/';
name[1] = '\0';
@@ -916,7 +913,6 @@ char *btrfs_get_subvol_name_from_objectid(struct btrfs_fs_info *fs_info,
err:
btrfs_put_root(fs_root);
- btrfs_free_path(path);
kfree(name);
return ERR_PTR(ret);
}
@@ -1614,7 +1610,7 @@ static inline void btrfs_descending_sort_devices(
static inline int btrfs_calc_avail_data_space(struct btrfs_fs_info *fs_info,
u64 *free_bytes)
{
- struct btrfs_device_info *devices_info;
+ struct btrfs_device_info AUTO_KFREE(devices_info);
struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
struct btrfs_device *device;
u64 type;
@@ -1712,7 +1708,6 @@ static inline int btrfs_calc_avail_data_space(struct btrfs_fs_info *fs_info,
nr_devices--;
}
- kfree(devices_info);
*free_bytes = avail_space;
return 0;
}
@@ -2430,6 +2425,66 @@ static long btrfs_free_cached_objects(struct super_block *sb, struct shrink_cont
return 0;
}
+#ifdef CONFIG_BTRFS_EXPERIMENTAL
+static int btrfs_remove_bdev(struct super_block *sb, struct block_device *bdev)
+{
+ struct btrfs_fs_info *fs_info = btrfs_sb(sb);
+ struct btrfs_device *device;
+ struct btrfs_dev_lookup_args lookup_args = { .devt = bdev->bd_dev };
+ bool can_rw;
+
+ mutex_lock(&fs_info->fs_devices->device_list_mutex);
+ device = btrfs_find_device(fs_info->fs_devices, &lookup_args);
+ if (!device) {
+ mutex_unlock(&fs_info->fs_devices->device_list_mutex);
+ /* Device not found, should not affect the running fs, just give a warning. */
+ btrfs_warn(fs_info, "unable to find btrfs device for block device '%pg'", bdev);
+ return 0;
+ }
+ /*
+ * The to-be-removed device is already missing?
+ *
+ * That's weird but no special handling needed and can exit right now.
+ */
+ if (unlikely(test_and_set_bit(BTRFS_DEV_STATE_MISSING, &device->dev_state))) {
+ mutex_unlock(&fs_info->fs_devices->device_list_mutex);
+ btrfs_warn(fs_info, "btrfs device id %llu is already missing", device->devid);
+ return 0;
+ }
+
+ device->fs_devices->missing_devices++;
+ if (test_and_clear_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) {
+ list_del_init(&device->dev_alloc_list);
+ WARN_ON(device->fs_devices->rw_devices < 1);
+ device->fs_devices->rw_devices--;
+ }
+ can_rw = btrfs_check_rw_degradable(fs_info, device);
+ mutex_unlock(&fs_info->fs_devices->device_list_mutex);
+ /*
+ * Now device is considered missing, btrfs_device_name() won't give a
+ * meaningful result anymore, so only output the devid.
+ */
+ if (unlikely(!can_rw)) {
+ btrfs_crit(fs_info,
+ "btrfs device id %llu has gone missing, can not maintain read-write",
+ device->devid);
+ return -EIO;
+ }
+ btrfs_warn(fs_info,
+ "btrfs device id %llu has gone missing, continue as degraded",
+ device->devid);
+ btrfs_set_opt(fs_info->mount_opt, DEGRADED);
+ return 0;
+}
+
+static void btrfs_shutdown(struct super_block *sb)
+{
+ struct btrfs_fs_info *fs_info = btrfs_sb(sb);
+
+ btrfs_force_shutdown(fs_info);
+}
+#endif
+
static const struct super_operations btrfs_super_ops = {
.drop_inode = btrfs_drop_inode,
.evict_inode = btrfs_evict_inode,
@@ -2445,6 +2500,10 @@ static const struct super_operations btrfs_super_ops = {
.unfreeze_fs = btrfs_unfreeze,
.nr_cached_objects = btrfs_nr_cached_objects,
.free_cached_objects = btrfs_free_cached_objects,
+#ifdef CONFIG_BTRFS_EXPERIMENTAL
+ .remove_bdev = btrfs_remove_bdev,
+ .shutdown = btrfs_shutdown,
+#endif
};
static const struct file_operations btrfs_ctl_fops = {
diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c
index 81f52c1f55ce..1f64c132b387 100644
--- a/fs/btrfs/sysfs.c
+++ b/fs/btrfs/sysfs.c
@@ -10,6 +10,7 @@
#include <linux/completion.h>
#include <linux/bug.h>
#include <linux/list.h>
+#include <linux/string_choices.h>
#include <crypto/hash.h>
#include "messages.h"
#include "ctree.h"
@@ -25,6 +26,7 @@
#include "misc.h"
#include "fs.h"
#include "accessors.h"
+#include "zoned.h"
/*
* Structure name Path
@@ -1187,6 +1189,56 @@ 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)
{
@@ -1599,6 +1651,7 @@ 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
@@ -1981,13 +2034,12 @@ static const char *alloc_name(struct btrfs_space_info *space_info)
* Create a sysfs entry for a space info type at path
* /sys/fs/btrfs/UUID/allocation/TYPE
*/
-int btrfs_sysfs_add_space_info_type(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info)
+int btrfs_sysfs_add_space_info_type(struct btrfs_space_info *space_info)
{
int ret;
ret = kobject_init_and_add(&space_info->kobj, &space_info_ktype,
- fs_info->space_info_kobj, "%s",
+ space_info->fs_info->space_info_kobj, "%s",
alloc_name(space_info));
if (ret) {
kobject_put(&space_info->kobj);
diff --git a/fs/btrfs/sysfs.h b/fs/btrfs/sysfs.h
index 0f94ae923210..05498e5346c3 100644
--- a/fs/btrfs/sysfs.h
+++ b/fs/btrfs/sysfs.h
@@ -37,8 +37,7 @@ void __cold btrfs_exit_sysfs(void);
int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info);
void btrfs_sysfs_remove_mounted(struct btrfs_fs_info *fs_info);
void btrfs_sysfs_add_block_group_type(struct btrfs_block_group *cache);
-int btrfs_sysfs_add_space_info_type(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info);
+int btrfs_sysfs_add_space_info_type(struct btrfs_space_info *space_info);
void btrfs_sysfs_remove_space_info(struct btrfs_space_info *space_info);
void btrfs_sysfs_update_devid(struct btrfs_device *device);
diff --git a/fs/btrfs/tests/extent-io-tests.c b/fs/btrfs/tests/extent-io-tests.c
index b19328d077d3..a0187d6163df 100644
--- a/fs/btrfs/tests/extent-io-tests.c
+++ b/fs/btrfs/tests/extent-io-tests.c
@@ -505,7 +505,7 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb)
static int test_eb_bitmaps(u32 sectorsize, u32 nodesize)
{
struct btrfs_fs_info *fs_info;
- unsigned long *bitmap = NULL;
+ unsigned long AUTO_KFREE(bitmap);
struct extent_buffer *eb = NULL;
int ret;
@@ -551,7 +551,6 @@ static int test_eb_bitmaps(u32 sectorsize, u32 nodesize)
ret = __test_eb_bitmaps(bitmap, eb);
out:
free_extent_buffer(eb);
- kfree(bitmap);
btrfs_free_dummy_fs_info(fs_info);
return ret;
}
diff --git a/fs/btrfs/tests/extent-map-tests.c b/fs/btrfs/tests/extent-map-tests.c
index 42af6c737c6e..0b9f25dd1a68 100644
--- a/fs/btrfs/tests/extent-map-tests.c
+++ b/fs/btrfs/tests/extent-map-tests.c
@@ -1013,7 +1013,7 @@ static int test_rmap_block(struct btrfs_fs_info *fs_info,
struct rmap_test_vector *test)
{
struct btrfs_chunk_map *map;
- u64 *logical = NULL;
+ u64 AUTO_KFREE(logical);
int i, out_ndaddrs, out_stripe_len;
int ret;
@@ -1046,7 +1046,7 @@ static int test_rmap_block(struct btrfs_fs_info *fs_info,
if (ret) {
test_err("error adding chunk map to mapping tree");
btrfs_free_chunk_map(map);
- goto out_free;
+ return ret;
}
ret = btrfs_rmap_block(fs_info, map->start, btrfs_sb_offset(1),
@@ -1079,8 +1079,6 @@ static int test_rmap_block(struct btrfs_fs_info *fs_info,
ret = 0;
out:
btrfs_remove_chunk_map(fs_info, map);
-out_free:
- kfree(logical);
return ret;
}
diff --git a/fs/btrfs/tests/qgroup-tests.c b/fs/btrfs/tests/qgroup-tests.c
index 3fc8dc3fd980..05cfda8af422 100644
--- a/fs/btrfs/tests/qgroup-tests.c
+++ b/fs/btrfs/tests/qgroup-tests.c
@@ -20,7 +20,7 @@ static int insert_normal_tree_ref(struct btrfs_root *root, u64 bytenr,
struct btrfs_extent_item *item;
struct btrfs_extent_inline_ref *iref;
struct btrfs_tree_block_info *block_info;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct extent_buffer *leaf;
struct btrfs_key ins;
u32 size = sizeof(*item) + sizeof(*iref) + sizeof(*block_info);
@@ -41,7 +41,6 @@ static int insert_normal_tree_ref(struct btrfs_root *root, u64 bytenr,
ret = btrfs_insert_empty_item(&trans, root, path, &ins, size);
if (ret) {
test_err("couldn't insert ref %d", ret);
- btrfs_free_path(path);
return ret;
}
@@ -61,7 +60,6 @@ static int insert_normal_tree_ref(struct btrfs_root *root, u64 bytenr,
btrfs_set_extent_inline_ref_type(leaf, iref, BTRFS_TREE_BLOCK_REF_KEY);
btrfs_set_extent_inline_ref_offset(leaf, iref, root_objectid);
}
- btrfs_free_path(path);
return 0;
}
@@ -70,7 +68,7 @@ static int add_tree_ref(struct btrfs_root *root, u64 bytenr, u64 num_bytes,
{
struct btrfs_trans_handle trans;
struct btrfs_extent_item *item;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
u64 refs;
int ret;
@@ -90,7 +88,6 @@ static int add_tree_ref(struct btrfs_root *root, u64 bytenr, u64 num_bytes,
ret = btrfs_search_slot(&trans, root, &key, path, 0, 1);
if (ret) {
test_err("couldn't find extent ref");
- btrfs_free_path(path);
return ret;
}
@@ -112,7 +109,6 @@ static int add_tree_ref(struct btrfs_root *root, u64 bytenr, u64 num_bytes,
ret = btrfs_insert_empty_item(&trans, root, path, &key, 0);
if (ret)
test_err("failed to insert backref");
- btrfs_free_path(path);
return ret;
}
@@ -121,7 +117,7 @@ static int remove_extent_item(struct btrfs_root *root, u64 bytenr,
{
struct btrfs_trans_handle trans;
struct btrfs_key key;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
int ret;
btrfs_init_dummy_trans(&trans, NULL);
@@ -139,11 +135,9 @@ static int remove_extent_item(struct btrfs_root *root, u64 bytenr,
ret = btrfs_search_slot(&trans, root, &key, path, -1, 1);
if (ret) {
test_err("didn't find our key %d", ret);
- btrfs_free_path(path);
return ret;
}
btrfs_del_item(&trans, root, path);
- btrfs_free_path(path);
return 0;
}
@@ -152,7 +146,7 @@ static int remove_extent_ref(struct btrfs_root *root, u64 bytenr,
{
struct btrfs_trans_handle trans;
struct btrfs_extent_item *item;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
u64 refs;
int ret;
@@ -172,7 +166,6 @@ static int remove_extent_ref(struct btrfs_root *root, u64 bytenr,
ret = btrfs_search_slot(&trans, root, &key, path, 0, 1);
if (ret) {
test_err("couldn't find extent ref");
- btrfs_free_path(path);
return ret;
}
@@ -198,7 +191,6 @@ static int remove_extent_ref(struct btrfs_root *root, u64 bytenr,
return ret;
}
btrfs_del_item(&trans, root, path);
- btrfs_free_path(path);
return ret;
}
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 89ae0c7a610a..05ee4391c83a 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -32,6 +32,8 @@
#include "ioctl.h"
#include "relocation.h"
#include "scrub.h"
+#include "ordered-data.h"
+#include "delayed-inode.h"
static struct kmem_cache *btrfs_trans_handle_cachep;
@@ -138,7 +140,6 @@ static const unsigned int btrfs_blocked_trans_types[TRANS_STATE_MAX] = {
void btrfs_put_transaction(struct btrfs_transaction *transaction)
{
- WARN_ON(refcount_read(&transaction->use_count) == 0);
if (refcount_dec_and_test(&transaction->use_count)) {
BUG_ON(!list_empty(&transaction->list));
WARN_ON(!xa_empty(&transaction->delayed_refs.head_refs));
@@ -185,7 +186,8 @@ static noinline void switch_commit_roots(struct btrfs_trans_handle *trans)
* At this point no one can be using this transaction to modify any tree
* and no one can start another transaction to modify any tree either.
*/
- ASSERT(cur_trans->state == TRANS_STATE_COMMIT_DOING);
+ ASSERT(cur_trans->state == TRANS_STATE_COMMIT_DOING,
+ "cur_trans->state=%d", cur_trans->state);
down_write(&fs_info->commit_root_sem);
@@ -575,7 +577,7 @@ static int btrfs_reserve_trans_metadata(struct btrfs_fs_info *fs_info,
* We want to reserve all the bytes we may need all at once, so we only
* do 1 enospc flushing cycle per transaction start.
*/
- ret = btrfs_reserve_metadata_bytes(fs_info, si, bytes, flush);
+ ret = btrfs_reserve_metadata_bytes(si, bytes, flush);
/*
* If we are an emergency flush, which can steal from the global block
@@ -585,7 +587,7 @@ static int btrfs_reserve_trans_metadata(struct btrfs_fs_info *fs_info,
if (ret && flush == BTRFS_RESERVE_FLUSH_ALL_STEAL) {
bytes -= *delayed_refs_bytes;
*delayed_refs_bytes = 0;
- ret = btrfs_reserve_metadata_bytes(fs_info, si, bytes, flush);
+ ret = btrfs_reserve_metadata_bytes(si, bytes, flush);
}
return ret;
@@ -1024,13 +1026,18 @@ static void btrfs_trans_release_metadata(struct btrfs_trans_handle *trans)
struct btrfs_fs_info *fs_info = trans->fs_info;
if (!trans->block_rsv) {
- ASSERT(!trans->bytes_reserved);
- ASSERT(!trans->delayed_refs_bytes_reserved);
+ ASSERT(trans->bytes_reserved == 0,
+ "trans->bytes_reserved=%llu", trans->bytes_reserved);
+ ASSERT(trans->delayed_refs_bytes_reserved == 0,
+ "trans->delayed_refs_bytes_reserved=%llu",
+ trans->delayed_refs_bytes_reserved);
return;
}
if (!trans->bytes_reserved) {
- ASSERT(!trans->delayed_refs_bytes_reserved);
+ ASSERT(trans->delayed_refs_bytes_reserved == 0,
+ "trans->delayed_refs_bytes_reserved=%llu",
+ trans->delayed_refs_bytes_reserved);
return;
}
@@ -1229,7 +1236,8 @@ int btrfs_wait_tree_log_extents(struct btrfs_root *log_root, int mark)
bool errors = false;
int ret;
- ASSERT(btrfs_root_id(log_root) == BTRFS_TREE_LOG_OBJECTID);
+ ASSERT(btrfs_root_id(log_root) == BTRFS_TREE_LOG_OBJECTID,
+ "root_id(log_root)=%llu", btrfs_root_id(log_root));
ret = __btrfs_wait_marked_extents(fs_info, dirty_pages);
if ((mark & EXTENT_DIRTY_LOG1) &&
@@ -1334,7 +1342,8 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans)
* At this point no one can be using this transaction to modify any tree
* and no one can start another transaction to modify any tree either.
*/
- ASSERT(trans->transaction->state == TRANS_STATE_COMMIT_DOING);
+ ASSERT(trans->transaction->state == TRANS_STATE_COMMIT_DOING,
+ "trans->transaction->state=%d", trans->transaction->state);
eb = btrfs_lock_root_node(fs_info->tree_root);
ret = btrfs_cow_block(trans, fs_info->tree_root, eb, NULL,
@@ -1468,7 +1477,8 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans)
* At this point no one can be using this transaction to modify any tree
* and no one can start another transaction to modify any tree either.
*/
- ASSERT(trans->transaction->state == TRANS_STATE_COMMIT_DOING);
+ ASSERT(trans->transaction->state == TRANS_STATE_COMMIT_DOING,
+ "trans->transaction->state=%d", trans->transaction->state);
spin_lock(&fs_info->fs_roots_radix_lock);
while (1) {
@@ -1486,9 +1496,15 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans)
* At this point we can neither have tasks logging inodes
* from a root nor trying to commit a log tree.
*/
- ASSERT(atomic_read(&root->log_writers) == 0);
- ASSERT(atomic_read(&root->log_commit[0]) == 0);
- ASSERT(atomic_read(&root->log_commit[1]) == 0);
+ ASSERT(atomic_read(&root->log_writers) == 0,
+ "atomic_read(&root->log_writers)=%d",
+ atomic_read(&root->log_writers));
+ ASSERT(atomic_read(&root->log_commit[0]) == 0,
+ "atomic_read(&root->log_commit[0])=%d",
+ atomic_read(&root->log_commit[0]));
+ ASSERT(atomic_read(&root->log_commit[1]) == 0,
+ "atomic_read(&root->log_commit[1])=%d",
+ atomic_read(&root->log_commit[1]));
radix_tree_tag_clear(&fs_info->fs_roots_radix,
(unsigned long)btrfs_root_id(root),
@@ -2157,7 +2173,8 @@ static void add_pending_snapshot(struct btrfs_trans_handle *trans)
return;
lockdep_assert_held(&trans->fs_info->trans_lock);
- ASSERT(cur_trans->state >= TRANS_STATE_COMMIT_PREP);
+ ASSERT(cur_trans->state >= TRANS_STATE_COMMIT_PREP,
+ "cur_trans->state=%d", cur_trans->state);
list_add(&trans->pending_snapshot->list, &cur_trans->pending_snapshots);
}
@@ -2184,7 +2201,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)
struct btrfs_transaction *prev_trans = NULL;
int ret;
- ASSERT(refcount_read(&trans->use_count) == 1);
+ ASSERT(refcount_read(&trans->use_count) == 1,
+ "refcount_read(&trans->use_count)=%d", refcount_read(&trans->use_count));
btrfs_trans_state_lockdep_acquire(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_PREP);
clear_bit(BTRFS_FS_NEED_TRANS_COMMIT, &fs_info->flags);
diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h
index 9f7c777af635..18ef069197e5 100644
--- a/fs/btrfs/transaction.h
+++ b/fs/btrfs/transaction.h
@@ -14,10 +14,6 @@
#include <linux/wait.h>
#include "btrfs_inode.h"
#include "delayed-ref.h"
-#include "extent-io-tree.h"
-#include "block-rsv.h"
-#include "messages.h"
-#include "misc.h"
struct dentry;
struct inode;
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c
index c10b4c242acf..c21c21adf61e 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -186,7 +186,7 @@ static bool check_prev_ino(struct extent_buffer *leaf,
key->type == BTRFS_INODE_EXTREF_KEY ||
key->type == BTRFS_DIR_INDEX_KEY ||
key->type == BTRFS_DIR_ITEM_KEY ||
- key->type == BTRFS_EXTENT_DATA_KEY);
+ key->type == BTRFS_EXTENT_DATA_KEY, "key->type=%u", key->type);
/*
* Only subvolume trees along with their reloc trees need this check.
@@ -1618,10 +1618,9 @@ static int check_extent_item(struct extent_buffer *leaf,
if (unlikely(prev_end > key->objectid)) {
extent_err(leaf, slot,
- "previous extent [%llu %u %llu] overlaps current extent [%llu %u %llu]",
- prev_key->objectid, prev_key->type,
- prev_key->offset, key->objectid, key->type,
- key->offset);
+ "previous extent " BTRFS_KEY_FMT " overlaps current extent " BTRFS_KEY_FMT,
+ BTRFS_KEY_FMT_VALUE(prev_key),
+ BTRFS_KEY_FMT_VALUE(key));
return -EUCLEAN;
}
}
@@ -2060,10 +2059,9 @@ enum btrfs_tree_block_status __btrfs_check_leaf(struct extent_buffer *leaf)
/* Make sure the keys are in the right order */
if (unlikely(btrfs_comp_cpu_keys(&prev_key, &key) >= 0)) {
generic_err(leaf, slot,
- "bad key order, prev (%llu %u %llu) current (%llu %u %llu)",
- prev_key.objectid, prev_key.type,
- prev_key.offset, key.objectid, key.type,
- key.offset);
+ "bad key order, prev " BTRFS_KEY_FMT " current " BTRFS_KEY_FMT,
+ BTRFS_KEY_FMT_VALUE(&prev_key),
+ BTRFS_KEY_FMT_VALUE(&key));
return BTRFS_TREE_BLOCK_BAD_KEY_ORDER;
}
@@ -2181,10 +2179,9 @@ enum btrfs_tree_block_status __btrfs_check_node(struct extent_buffer *node)
if (unlikely(btrfs_comp_cpu_keys(&key, &next_key) >= 0)) {
generic_err(node, slot,
- "bad key order, current (%llu %u %llu) next (%llu %u %llu)",
- key.objectid, key.type, key.offset,
- next_key.objectid, next_key.type,
- next_key.offset);
+ "bad key order, current " BTRFS_KEY_FMT " next " BTRFS_KEY_FMT,
+ BTRFS_KEY_FMT_VALUE(&key),
+ BTRFS_KEY_FMT_VALUE(&next_key));
return BTRFS_TREE_BLOCK_BAD_KEY_ORDER;
}
}
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 30f3c3b849c1..fff37c8d96a4 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -29,6 +29,7 @@
#include "orphan.h"
#include "print-tree.h"
#include "tree-checker.h"
+#include "delayed-inode.h"
#define MAX_CONFLICT_INODES 10
@@ -198,9 +199,9 @@ static void do_abort_log_replay(struct walk_control *wc, const char *function,
if (wc->log_leaf) {
btrfs_crit(fs_info,
- "log tree (for root %llu) leaf currently being processed (slot %d key %llu %u %llu):",
+"log tree (for root %llu) leaf currently being processed (slot %d key " BTRFS_KEY_FMT "):",
btrfs_root_id(wc->root), wc->log_slot,
- wc->log_key.objectid, wc->log_key.type, wc->log_key.offset);
+ BTRFS_KEY_FMT_VALUE(&wc->log_key));
btrfs_print_leaf(wc->log_leaf);
}
@@ -262,7 +263,7 @@ static struct btrfs_inode *btrfs_iget_logging(u64 objectid, struct btrfs_root *r
struct btrfs_inode *inode;
/* Only meant to be called for subvolume roots and not for log roots. */
- ASSERT(btrfs_is_fstree(btrfs_root_id(root)));
+ ASSERT(btrfs_is_fstree(btrfs_root_id(root)), "root_id=%llu", btrfs_root_id(root));
/*
* We're holding a transaction handle whether we are logging or
@@ -501,7 +502,7 @@ static int overwrite_item(struct walk_control *wc)
* the leaf before writing into the log tree. See the comments at
* copy_items() for more details.
*/
- ASSERT(btrfs_root_id(root) != BTRFS_TREE_LOG_OBJECTID);
+ ASSERT(btrfs_root_id(root) != BTRFS_TREE_LOG_OBJECTID, "root_id=%llu", btrfs_root_id(root));
item_size = btrfs_item_size(wc->log_leaf, wc->log_slot);
src_ptr = btrfs_item_ptr_offset(wc->log_leaf, wc->log_slot);
@@ -510,9 +511,9 @@ static int overwrite_item(struct walk_control *wc)
ret = btrfs_search_slot(NULL, root, &wc->log_key, wc->subvol_path, 0, 0);
if (ret < 0) {
btrfs_abort_log_replay(wc, ret,
- "failed to search subvolume tree for key (%llu %u %llu) root %llu",
- wc->log_key.objectid, wc->log_key.type,
- wc->log_key.offset, btrfs_root_id(root));
+ "failed to search subvolume tree for key " BTRFS_KEY_FMT " root %llu",
+ BTRFS_KEY_FMT_VALUE(&wc->log_key),
+ btrfs_root_id(root));
return ret;
}
@@ -601,9 +602,9 @@ static int overwrite_item(struct walk_control *wc)
insert:
btrfs_release_path(wc->subvol_path);
/* try to insert the key into the destination tree */
- wc->subvol_path->skip_release_on_error = 1;
+ wc->subvol_path->skip_release_on_error = true;
ret = btrfs_insert_empty_item(trans, root, wc->subvol_path, &wc->log_key, item_size);
- wc->subvol_path->skip_release_on_error = 0;
+ wc->subvol_path->skip_release_on_error = false;
dst_eb = wc->subvol_path->nodes[0];
dst_slot = wc->subvol_path->slots[0];
@@ -618,9 +619,8 @@ insert:
btrfs_extend_item(trans, wc->subvol_path, item_size - found_size);
} else if (ret) {
btrfs_abort_log_replay(wc, ret,
- "failed to insert item for key (%llu %u %llu)",
- wc->log_key.objectid, wc->log_key.type,
- wc->log_key.offset);
+ "failed to insert item for key " BTRFS_KEY_FMT,
+ BTRFS_KEY_FMT_VALUE(&wc->log_key));
return ret;
}
dst_ptr = btrfs_item_ptr_offset(dst_eb, dst_slot);
@@ -829,9 +829,9 @@ static noinline int replay_one_extent(struct walk_control *wc)
&wc->log_key, sizeof(*item));
if (ret) {
btrfs_abort_log_replay(wc, ret,
- "failed to insert item with key (%llu %u %llu) root %llu",
- wc->log_key.objectid, wc->log_key.type,
- wc->log_key.offset, btrfs_root_id(root));
+ "failed to insert item with key " BTRFS_KEY_FMT " root %llu",
+ BTRFS_KEY_FMT_VALUE(&wc->log_key),
+ btrfs_root_id(root));
goto out;
}
dest_offset = btrfs_item_ptr_offset(wc->subvol_path->nodes[0],
@@ -1348,9 +1348,9 @@ again:
ret = btrfs_search_slot(NULL, root, &search_key, wc->subvol_path, 0, 0);
if (ret < 0) {
btrfs_abort_log_replay(wc, ret,
- "failed to search subvolume tree for key (%llu %u %llu) root %llu",
- search_key.objectid, search_key.type,
- search_key.offset, btrfs_root_id(root));
+ "failed to search subvolume tree for key " BTRFS_KEY_FMT " root %llu",
+ BTRFS_KEY_FMT_VALUE(&search_key),
+ btrfs_root_id(root));
return ret;
} else if (ret == 0) {
/*
@@ -1483,9 +1483,9 @@ again:
}
if (ret < 0) {
btrfs_abort_log_replay(wc, ret,
- "failed to search subvolume tree for key (%llu %u %llu) root %llu",
- wc->log_key.objectid, wc->log_key.type,
- wc->log_key.offset, btrfs_root_id(root));
+ "failed to search subvolume tree for key " BTRFS_KEY_FMT " root %llu",
+ BTRFS_KEY_FMT_VALUE(&wc->log_key),
+ btrfs_root_id(root));
goto out;
}
@@ -2282,7 +2282,8 @@ static noinline int replay_one_dir_item(struct walk_control *wc)
struct btrfs_dir_item *di;
/* We only log dir index keys, which only contain a single dir item. */
- ASSERT(wc->log_key.type == BTRFS_DIR_INDEX_KEY);
+ ASSERT(wc->log_key.type == BTRFS_DIR_INDEX_KEY,
+ "wc->log_key.type=%u", wc->log_key.type);
di = btrfs_item_ptr(wc->log_leaf, wc->log_slot, struct btrfs_dir_item);
ret = replay_one_name(wc, di);
@@ -2434,7 +2435,7 @@ static noinline int check_item_in_log(struct walk_control *wc,
* we need to do is process the dir index keys, we (and our caller) can
* safely ignore dir item keys (key type BTRFS_DIR_ITEM_KEY).
*/
- ASSERT(dir_key->type == BTRFS_DIR_INDEX_KEY);
+ ASSERT(dir_key->type == BTRFS_DIR_INDEX_KEY, "dir_key->type=%u", dir_key->type);
eb = wc->subvol_path->nodes[0];
slot = wc->subvol_path->slots[0];
@@ -2647,7 +2648,7 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
int ret = 0;
struct btrfs_key dir_key;
struct btrfs_key found_key;
- struct btrfs_path *log_path;
+ BTRFS_PATH_AUTO_FREE(log_path);
struct btrfs_inode *dir;
dir_key.objectid = dirid;
@@ -2664,7 +2665,6 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
* we replay the deletes before we copy in the inode item from the log.
*/
if (IS_ERR(dir)) {
- btrfs_free_path(log_path);
ret = PTR_ERR(dir);
if (ret == -ENOENT)
ret = 0;
@@ -2700,10 +2700,9 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
wc->subvol_path, 0, 0);
if (ret < 0) {
btrfs_abort_log_replay(wc, ret,
- "failed to search root %llu for key (%llu %u %llu)",
+ "failed to search root %llu for key " BTRFS_KEY_FMT,
btrfs_root_id(root),
- dir_key.objectid, dir_key.type,
- dir_key.offset);
+ BTRFS_KEY_FMT_VALUE(&dir_key));
goto out;
}
@@ -2745,7 +2744,6 @@ static noinline int replay_dir_deletes(struct walk_control *wc,
ret = 0;
out:
btrfs_release_path(wc->subvol_path);
- btrfs_free_path(log_path);
iput(&dir->vfs_inode);
return ret;
}
@@ -3340,7 +3338,8 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
mutex_unlock(&root->log_mutex);
return ctx->log_ret;
}
- ASSERT(log_transid == root->log_transid);
+ ASSERT(log_transid == root->log_transid,
+ "log_transid=%d root->log_transid=%d", log_transid, root->log_transid);
atomic_set(&root->log_commit[index1], 1);
/* wait for previous tree log sync to complete */
@@ -3480,7 +3479,9 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
ret = root_log_ctx.log_ret;
goto out;
}
- ASSERT(root_log_ctx.log_transid == log_root_tree->log_transid);
+ ASSERT(root_log_ctx.log_transid == log_root_tree->log_transid,
+ "root_log_ctx.log_transid=%d log_root_tree->log_transid=%d",
+ root_log_ctx.log_transid, log_root_tree->log_transid);
atomic_set(&log_root_tree->log_commit[index2], 1);
if (atomic_read(&log_root_tree->log_commit[(index2 + 1) % 2])) {
@@ -3584,7 +3585,9 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
* someone else already started it. We use <= and not < because the
* first log transaction has an ID of 0.
*/
- ASSERT(btrfs_get_root_last_log_commit(root) <= log_transid);
+ ASSERT(btrfs_get_root_last_log_commit(root) <= log_transid,
+ "last_log_commit(root)=%d log_transid=%d",
+ btrfs_get_root_last_log_commit(root), log_transid);
btrfs_set_root_last_log_commit(root, log_transid);
out_wake_log_root:
@@ -3895,10 +3898,10 @@ static int del_logged_dentry(struct btrfs_trans_handle *trans,
* or the entire directory.
*/
void btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
const struct fscrypt_str *name,
struct btrfs_inode *dir, u64 index)
{
+ struct btrfs_root *root = dir->root;
BTRFS_PATH_AUTO_FREE(path);
int ret;
@@ -3933,11 +3936,11 @@ void btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
/* see comments for btrfs_del_dir_entries_in_log */
void btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
const struct fscrypt_str *name,
- struct btrfs_inode *inode, u64 dirid)
+ struct btrfs_inode *inode,
+ struct btrfs_inode *dir)
{
- struct btrfs_root *log;
+ struct btrfs_root *root = dir->root;
int ret;
ret = inode_logged(trans, inode, NULL);
@@ -3952,10 +3955,10 @@ void btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
ASSERT(ret == 0, "join_running_log_trans() ret=%d", ret);
if (WARN_ON(ret))
return;
- log = root->log_root;
mutex_lock(&inode->log_mutex);
- ret = btrfs_del_inode_ref(trans, log, name, btrfs_ino(inode), dirid, NULL);
+ ret = btrfs_del_inode_ref(trans, root->log_root, name, btrfs_ino(inode),
+ btrfs_ino(dir), NULL);
mutex_unlock(&inode->log_mutex);
if (ret < 0 && ret != -ENOENT)
btrfs_set_log_full_commit(trans);
@@ -4017,7 +4020,7 @@ static int flush_dir_items_batch(struct btrfs_trans_handle *trans,
int count)
{
struct btrfs_root *log = inode->root->log_root;
- char *ins_data = NULL;
+ char AUTO_KFREE(ins_data);
struct btrfs_item_batch batch;
struct extent_buffer *dst;
unsigned long src_offset;
@@ -4028,7 +4031,7 @@ static int flush_dir_items_batch(struct btrfs_trans_handle *trans,
int ret;
int i;
- ASSERT(count > 0);
+ ASSERT(count > 0, "count=%d", count);
batch.nr = count;
if (count == 1) {
@@ -4062,7 +4065,7 @@ static int flush_dir_items_batch(struct btrfs_trans_handle *trans,
ret = btrfs_insert_empty_items(trans, log, dst_path, &batch);
if (ret)
- goto out;
+ return ret;
dst = dst_path->nodes[0];
/*
@@ -4081,7 +4084,9 @@ static int flush_dir_items_batch(struct btrfs_trans_handle *trans,
btrfs_release_path(dst_path);
last_index = batch.keys[count - 1].offset;
- ASSERT(last_index > inode->last_dir_index_offset);
+ ASSERT(last_index > inode->last_dir_index_offset,
+ "last_index=%llu inode->last_dir_index_offset=%llu",
+ last_index, inode->last_dir_index_offset);
/*
* If for some unexpected reason the last item's index is not greater
@@ -4094,8 +4099,6 @@ static int flush_dir_items_batch(struct btrfs_trans_handle *trans,
if (btrfs_get_first_dir_index_to_log(inode) == 0)
btrfs_set_first_dir_index_to_log(inode, batch.keys[0].offset);
-out:
- kfree(ins_data);
return ret;
}
@@ -4154,7 +4157,6 @@ static int process_dir_items_leaf(struct btrfs_trans_handle *trans,
for (int i = path->slots[0]; i < nritems; i++) {
struct btrfs_dir_item *di;
struct btrfs_key key;
- int ret;
btrfs_item_key_to_cpu(src, &key, i);
@@ -4224,8 +4226,6 @@ static int process_dir_items_leaf(struct btrfs_trans_handle *trans,
}
if (batch_size > 0) {
- int ret;
-
ret = flush_dir_items_batch(trans, inode, src, dst_path,
batch_start, batch_size);
if (ret < 0)
@@ -4410,7 +4410,9 @@ done:
* change in the current transaction), then we don't need to log
* a range, last_old_dentry_offset is == to last_offset.
*/
- ASSERT(last_old_dentry_offset <= last_offset);
+ ASSERT(last_old_dentry_offset <= last_offset,
+ "last_old_dentry_offset=%llu last_offset=%llu",
+ last_old_dentry_offset, last_offset);
if (last_old_dentry_offset < last_offset)
ret = insert_dir_log_key(trans, log, path, ino,
last_old_dentry_offset + 1,
@@ -4765,7 +4767,7 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
struct btrfs_key *ins_keys;
u32 *ins_sizes;
struct btrfs_item_batch batch;
- char *ins_data;
+ char AUTO_KFREE(ins_data);
int dst_index;
const bool skip_csum = (inode->flags & BTRFS_INODE_NODATASUM);
const u64 i_size = i_size_read(&inode->vfs_inode);
@@ -4893,7 +4895,7 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
disk_bytenr + extent_num_bytes - 1,
&ordered_sums, false);
if (ret < 0)
- goto out;
+ return ret;
ret = 0;
list_for_each_entry_safe(sums, sums_next, &ordered_sums, list) {
@@ -4903,7 +4905,7 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
kfree(sums);
}
if (ret)
- goto out;
+ return ret;
add_to_batch:
ins_sizes[dst_index] = btrfs_item_size(src, src_slot);
@@ -4917,11 +4919,11 @@ add_to_batch:
* so we don't need to do anything.
*/
if (batch.nr == 0)
- goto out;
+ return 0;
ret = btrfs_insert_empty_items(trans, log, dst_path, &batch);
if (ret)
- goto out;
+ return ret;
dst_index = 0;
for (int i = 0; i < nr; i++) {
@@ -4974,8 +4976,6 @@ copy_item:
}
btrfs_release_path(dst_path);
-out:
- kfree(ins_data);
return ret;
}
@@ -5414,12 +5414,12 @@ process:
set_bit(BTRFS_ORDERED_LOGGED, &ordered->flags);
if (!test_bit(BTRFS_ORDERED_COMPLETE, &ordered->flags)) {
- spin_lock_irq(&inode->ordered_tree_lock);
+ spin_lock(&inode->ordered_tree_lock);
if (!test_bit(BTRFS_ORDERED_COMPLETE, &ordered->flags)) {
set_bit(BTRFS_ORDERED_PENDING, &ordered->flags);
atomic_inc(&trans->transaction->pending_ordered);
}
- spin_unlock_irq(&inode->ordered_tree_lock);
+ spin_unlock(&inode->ordered_tree_lock);
}
btrfs_put_ordered_extent(ordered);
}
@@ -5694,9 +5694,8 @@ static int btrfs_check_ref_name_override(struct extent_buffer *eb,
struct btrfs_inode *inode,
u64 *other_ino, u64 *other_parent)
{
- int ret;
BTRFS_PATH_AUTO_FREE(search_path);
- char *name = NULL;
+ char AUTO_KFREE(name);
u32 name_len = 0;
u32 item_size = btrfs_item_size(eb, slot);
u32 cur_offset = 0;
@@ -5705,8 +5704,8 @@ static int btrfs_check_ref_name_override(struct extent_buffer *eb,
search_path = btrfs_alloc_path();
if (!search_path)
return -ENOMEM;
- search_path->search_commit_root = 1;
- search_path->skip_locking = 1;
+ search_path->search_commit_root = true;
+ search_path->skip_locking = true;
while (cur_offset < item_size) {
u64 parent;
@@ -5739,10 +5738,8 @@ static int btrfs_check_ref_name_override(struct extent_buffer *eb,
char *new_name;
new_name = krealloc(name, this_name_len, GFP_NOFS);
- if (!new_name) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!new_name)
+ return -ENOMEM;
name_len = this_name_len;
name = new_name;
}
@@ -5760,28 +5757,24 @@ static int btrfs_check_ref_name_override(struct extent_buffer *eb,
di, &di_key);
if (di_key.type == BTRFS_INODE_ITEM_KEY) {
if (di_key.objectid != key->objectid) {
- ret = 1;
*other_ino = di_key.objectid;
*other_parent = parent;
+ return 1;
} else {
- ret = 0;
+ return 0;
}
} else {
- ret = -EAGAIN;
+ return -EAGAIN;
}
- goto out;
} else if (IS_ERR(di)) {
- ret = PTR_ERR(di);
- goto out;
+ return PTR_ERR(di);
}
btrfs_release_path(search_path);
cur_offset += this_len;
}
- ret = 0;
-out:
- kfree(name);
- return ret;
+
+ return 0;
}
/*
@@ -6031,8 +6024,8 @@ static int conflicting_inode_is_dir(struct btrfs_root *root, u64 ino,
key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0;
- path->search_commit_root = 1;
- path->skip_locking = 1;
+ path->search_commit_root = true;
+ path->skip_locking = true;
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
if (WARN_ON_ONCE(ret > 0)) {
@@ -6052,8 +6045,8 @@ static int conflicting_inode_is_dir(struct btrfs_root *root, u64 ino,
}
btrfs_release_path(path);
- path->search_commit_root = 0;
- path->skip_locking = 0;
+ path->search_commit_root = false;
+ path->skip_locking = false;
return ret;
}
@@ -6543,7 +6536,7 @@ static int log_delayed_insertion_items(struct btrfs_trans_handle *trans,
curr = list_next_entry(curr, log_list);
}
- ASSERT(batch.nr >= 1);
+ ASSERT(batch.nr >= 1, "batch.nr=%d", batch.nr);
ret = insert_delayed_items_batch(trans, log, path, &batch, first);
curr = list_last_entry(delayed_ins_list, struct btrfs_delayed_item,
@@ -6587,7 +6580,9 @@ static int log_delayed_deletions_full(struct btrfs_trans_handle *trans,
}
last_dir_index = curr->index;
- ASSERT(last_dir_index >= first_dir_index);
+ ASSERT(last_dir_index >= first_dir_index,
+ "last_dir_index=%llu first_dir_index=%llu",
+ last_dir_index, first_dir_index);
ret = insert_dir_log_key(trans, inode->root->log_root, path,
ino, first_dir_index, last_dir_index);
@@ -6681,7 +6676,9 @@ static int log_delayed_deletions_incremental(struct btrfs_trans_handle *trans,
goto next_batch;
last_dir_index = last->index;
- ASSERT(last_dir_index >= first_dir_index);
+ ASSERT(last_dir_index >= first_dir_index,
+ "last_dir_index=%llu first_dir_index=%llu",
+ last_dir_index, first_dir_index);
/*
* If this range starts right after where the previous one ends,
* then we want to reuse the previous range item and change its
@@ -6748,7 +6745,8 @@ static int log_new_delayed_dentries(struct btrfs_trans_handle *trans,
*/
lockdep_assert_not_held(&inode->log_mutex);
- ASSERT(!ctx->logging_new_delayed_dentries);
+ ASSERT(!ctx->logging_new_delayed_dentries,
+ "ctx->logging_new_delayed_dentries=%d", ctx->logging_new_delayed_dentries);
ctx->logging_new_delayed_dentries = true;
list_for_each_entry(item, delayed_ins_list, log_list) {
@@ -7169,8 +7167,8 @@ static int btrfs_log_all_parents(struct btrfs_trans_handle *trans,
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
- path->skip_locking = 1;
- path->search_commit_root = 1;
+ path->skip_locking = true;
+ path->search_commit_root = true;
key.objectid = ino;
key.type = BTRFS_INODE_REF_KEY;
@@ -7203,28 +7201,24 @@ static int btrfs_log_all_parents(struct btrfs_trans_handle *trans,
item_size = btrfs_item_size(leaf, slot);
ptr = btrfs_item_ptr_offset(leaf, slot);
while (cur_offset < item_size) {
- struct btrfs_key inode_key;
+ u64 dir_id;
struct btrfs_inode *dir_inode;
- inode_key.type = BTRFS_INODE_ITEM_KEY;
- inode_key.offset = 0;
-
if (key.type == BTRFS_INODE_EXTREF_KEY) {
struct btrfs_inode_extref *extref;
extref = (struct btrfs_inode_extref *)
(ptr + cur_offset);
- inode_key.objectid = btrfs_inode_extref_parent(
- leaf, extref);
+ dir_id = btrfs_inode_extref_parent(leaf, extref);
cur_offset += sizeof(*extref);
cur_offset += btrfs_inode_extref_name_len(leaf,
extref);
} else {
- inode_key.objectid = key.offset;
+ dir_id = key.offset;
cur_offset = item_size;
}
- dir_inode = btrfs_iget_logging(inode_key.objectid, root);
+ dir_inode = btrfs_iget_logging(dir_id, root);
/*
* If the parent inode was deleted, return an error to
* fallback to a transaction commit. This is to prevent
@@ -7965,7 +7959,8 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
struct btrfs_path *path;
struct fscrypt_name fname;
- ASSERT(old_dir_index >= BTRFS_DIR_START_INDEX);
+ ASSERT(old_dir_index >= BTRFS_DIR_START_INDEX,
+ "old_dir_index=%llu", old_dir_index);
ret = fscrypt_setup_filename(&old_dir->vfs_inode,
&old_dentry->d_name, 0, &fname);
diff --git a/fs/btrfs/tree-log.h b/fs/btrfs/tree-log.h
index dc313e6bb2fa..41e47fda036d 100644
--- a/fs/btrfs/tree-log.h
+++ b/fs/btrfs/tree-log.h
@@ -8,8 +8,7 @@
#include <linux/list.h>
#include <linux/fs.h>
-#include "messages.h"
-#include "ctree.h"
+#include <linux/fscrypt.h>
#include "transaction.h"
struct inode;
@@ -80,13 +79,12 @@ int btrfs_log_dentry_safe(struct btrfs_trans_handle *trans,
struct dentry *dentry,
struct btrfs_log_ctx *ctx);
void btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
const struct fscrypt_str *name,
struct btrfs_inode *dir, u64 index);
void btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
- struct btrfs_root *root,
const struct fscrypt_str *name,
- struct btrfs_inode *inode, u64 dirid);
+ struct btrfs_inode *inode,
+ struct btrfs_inode *dir);
void btrfs_end_log_trans(struct btrfs_root *root);
void btrfs_pin_log_trans(struct btrfs_root *root);
void btrfs_record_unlink_dir(struct btrfs_trans_handle *trans,
diff --git a/fs/btrfs/uuid-tree.c b/fs/btrfs/uuid-tree.c
index 17b5e81123a1..e3a1310fa7d5 100644
--- a/fs/btrfs/uuid-tree.c
+++ b/fs/btrfs/uuid-tree.c
@@ -27,32 +27,26 @@ static int btrfs_uuid_tree_lookup(struct btrfs_root *uuid_root, const u8 *uuid,
u8 type, u64 subid)
{
int ret;
- struct btrfs_path *path = NULL;
+ BTRFS_PATH_AUTO_FREE(path);
struct extent_buffer *eb;
int slot;
u32 item_size;
unsigned long offset;
struct btrfs_key key;
- if (WARN_ON_ONCE(!uuid_root)) {
- ret = -ENOENT;
- goto out;
- }
+ if (WARN_ON_ONCE(!uuid_root))
+ return -ENOENT;
path = btrfs_alloc_path();
- if (!path) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!path)
+ return -ENOMEM;
btrfs_uuid_to_key(uuid, type, &key);
ret = btrfs_search_slot(NULL, uuid_root, &key, path, 0, 0);
- if (ret < 0) {
- goto out;
- } else if (ret > 0) {
- ret = -ENOENT;
- goto out;
- }
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ return -ENOENT;
eb = path->nodes[0];
slot = path->slots[0];
@@ -64,7 +58,7 @@ static int btrfs_uuid_tree_lookup(struct btrfs_root *uuid_root, const u8 *uuid,
btrfs_warn(uuid_root->fs_info,
"uuid item with illegal size %lu!",
(unsigned long)item_size);
- goto out;
+ return ret;
}
while (item_size) {
__le64 data;
@@ -78,8 +72,6 @@ static int btrfs_uuid_tree_lookup(struct btrfs_root *uuid_root, const u8 *uuid,
item_size -= sizeof(data);
}
-out:
- btrfs_free_path(path);
return ret;
}
@@ -89,7 +81,7 @@ int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, const u8 *uuid, u8 typ
struct btrfs_fs_info *fs_info = trans->fs_info;
struct btrfs_root *uuid_root = fs_info->uuid_root;
int ret;
- struct btrfs_path *path = NULL;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
struct extent_buffer *eb;
int slot;
@@ -100,18 +92,14 @@ int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, const u8 *uuid, u8 typ
if (ret != -ENOENT)
return ret;
- if (WARN_ON_ONCE(!uuid_root)) {
- ret = -EINVAL;
- goto out;
- }
+ if (WARN_ON_ONCE(!uuid_root))
+ return -EINVAL;
btrfs_uuid_to_key(uuid, type, &key);
path = btrfs_alloc_path();
- if (!path) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!path)
+ return -ENOMEM;
ret = btrfs_insert_empty_item(trans, uuid_root, path, &key,
sizeof(subid_le));
@@ -134,15 +122,12 @@ int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, const u8 *uuid, u8 typ
btrfs_warn(fs_info,
"insert uuid item failed %d (0x%016llx, 0x%016llx) type %u!",
ret, key.objectid, key.offset, type);
- goto out;
+ return ret;
}
- ret = 0;
subid_le = cpu_to_le64(subid_cpu);
write_extent_buffer(eb, &subid_le, offset, sizeof(subid_le));
-out:
- btrfs_free_path(path);
- return ret;
+ return 0;
}
int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, const u8 *uuid, u8 type,
@@ -151,7 +136,7 @@ int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, const u8 *uuid, u8
struct btrfs_fs_info *fs_info = trans->fs_info;
struct btrfs_root *uuid_root = fs_info->uuid_root;
int ret;
- struct btrfs_path *path = NULL;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
struct extent_buffer *eb;
int slot;
@@ -161,29 +146,23 @@ int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, const u8 *uuid, u8
unsigned long move_src;
unsigned long move_len;
- if (WARN_ON_ONCE(!uuid_root)) {
- ret = -EINVAL;
- goto out;
- }
+ if (WARN_ON_ONCE(!uuid_root))
+ return -EINVAL;
btrfs_uuid_to_key(uuid, type, &key);
path = btrfs_alloc_path();
- if (!path) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!path)
+ return -ENOMEM;
ret = btrfs_search_slot(trans, uuid_root, &key, path, -1, 1);
if (ret < 0) {
btrfs_warn(fs_info, "error %d while searching for uuid item!",
ret);
- goto out;
- }
- if (ret > 0) {
- ret = -ENOENT;
- goto out;
+ return ret;
}
+ if (ret > 0)
+ return -ENOENT;
eb = path->nodes[0];
slot = path->slots[0];
@@ -192,8 +171,7 @@ int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, const u8 *uuid, u8
if (!IS_ALIGNED(item_size, sizeof(u64))) {
btrfs_warn(fs_info, "uuid item with illegal size %lu!",
(unsigned long)item_size);
- ret = -ENOENT;
- goto out;
+ return -ENOENT;
}
while (item_size) {
__le64 read_subid;
@@ -205,16 +183,12 @@ int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, const u8 *uuid, u8
item_size -= sizeof(read_subid);
}
- if (!item_size) {
- ret = -ENOENT;
- goto out;
- }
+ if (!item_size)
+ return -ENOENT;
item_size = btrfs_item_size(eb, slot);
- if (item_size == sizeof(subid)) {
- ret = btrfs_del_item(trans, uuid_root, path);
- goto out;
- }
+ if (item_size == sizeof(subid))
+ return btrfs_del_item(trans, uuid_root, path);
move_dst = offset;
move_src = offset + sizeof(subid);
@@ -222,9 +196,7 @@ int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, const u8 *uuid, u8
memmove_extent_buffer(eb, move_dst, move_src, move_len);
btrfs_truncate_item(trans, path, item_size - sizeof(subid), 1);
-out:
- btrfs_free_path(path);
- return ret;
+ return 0;
}
static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type,
@@ -293,7 +265,7 @@ int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info)
{
struct btrfs_root *root = fs_info->uuid_root;
struct btrfs_key key;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
int ret = 0;
struct extent_buffer *leaf;
int slot;
@@ -301,10 +273,8 @@ int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info)
unsigned long offset;
path = btrfs_alloc_path();
- if (!path) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!path)
+ return -ENOMEM;
key.objectid = 0;
key.type = 0;
@@ -312,17 +282,15 @@ int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info)
again_search_slot:
ret = btrfs_search_forward(root, &key, path, BTRFS_OLDEST_GENERATION);
- if (ret) {
- if (ret > 0)
- ret = 0;
- goto out;
- }
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ return 0;
while (1) {
- if (btrfs_fs_closing(fs_info)) {
- ret = -EINTR;
- goto out;
- }
+ if (btrfs_fs_closing(fs_info))
+ return -EINTR;
+
cond_resched();
leaf = path->nodes[0];
slot = path->slots[0];
@@ -353,7 +321,7 @@ again_search_slot:
ret = btrfs_check_uuid_tree_entry(fs_info, uuid,
key.type, subid_cpu);
if (ret < 0)
- goto out;
+ return ret;
if (ret > 0) {
btrfs_release_path(path);
ret = btrfs_uuid_iter_rem(root, uuid, key.type,
@@ -369,7 +337,7 @@ again_search_slot:
goto again_search_slot;
}
if (ret < 0 && ret != -ENOENT)
- goto out;
+ return ret;
key.offset++;
goto again_search_slot;
}
@@ -386,8 +354,6 @@ skip:
break;
}
-out:
- btrfs_free_path(path);
return ret;
}
diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c
index 46bd8ca58670..06dfcb461f53 100644
--- a/fs/btrfs/verity.c
+++ b/fs/btrfs/verity.c
@@ -109,7 +109,7 @@ static int drop_verity_items(struct btrfs_inode *inode, u8 key_type)
{
struct btrfs_trans_handle *trans;
struct btrfs_root *root = inode->root;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
int count = 0;
int ret;
@@ -121,10 +121,8 @@ static int drop_verity_items(struct btrfs_inode *inode, u8 key_type)
while (1) {
/* 1 for the item being dropped */
trans = btrfs_start_transaction(root, 1);
- if (IS_ERR(trans)) {
- ret = PTR_ERR(trans);
- goto out;
- }
+ if (IS_ERR(trans))
+ return PTR_ERR(trans);
/*
* Walk backwards through all the items until we find one that
@@ -143,7 +141,7 @@ static int drop_verity_items(struct btrfs_inode *inode, u8 key_type)
path->slots[0]--;
} else if (ret < 0) {
btrfs_end_transaction(trans);
- goto out;
+ return ret;
}
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
@@ -161,17 +159,14 @@ static int drop_verity_items(struct btrfs_inode *inode, u8 key_type)
ret = btrfs_del_items(trans, root, path, path->slots[0], 1);
if (ret) {
btrfs_end_transaction(trans);
- goto out;
+ return ret;
}
count++;
btrfs_release_path(path);
btrfs_end_transaction(trans);
}
- ret = count;
btrfs_end_transaction(trans);
-out:
- btrfs_free_path(path);
- return ret;
+ return count;
}
/*
@@ -217,7 +212,7 @@ static int write_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
const char *src, u64 len)
{
struct btrfs_trans_handle *trans;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_root *root = inode->root;
struct extent_buffer *leaf;
struct btrfs_key key;
@@ -233,10 +228,8 @@ static int write_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
while (len > 0) {
/* 1 for the new item being inserted */
trans = btrfs_start_transaction(root, 1);
- if (IS_ERR(trans)) {
- ret = PTR_ERR(trans);
- break;
- }
+ if (IS_ERR(trans))
+ return PTR_ERR(trans);
key.objectid = btrfs_ino(inode);
key.type = key_type;
@@ -267,7 +260,6 @@ static int write_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
btrfs_end_transaction(trans);
}
- btrfs_free_path(path);
return ret;
}
@@ -296,7 +288,7 @@ static int write_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
static int read_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
char *dest, u64 len, struct folio *dest_folio)
{
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_root *root = inode->root;
struct extent_buffer *leaf;
struct btrfs_key key;
@@ -404,7 +396,6 @@ static int read_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
}
}
out:
- btrfs_free_path(path);
if (!ret)
ret = copied;
return ret;
@@ -587,6 +578,9 @@ static int btrfs_begin_enable_verity(struct file *filp)
btrfs_assert_inode_locked(inode);
+ if (IS_ENCRYPTED(&inode->vfs_inode))
+ return -EOPNOTSUPP;
+
if (test_bit(BTRFS_INODE_VERITY_IN_PROGRESS, &inode->runtime_flags))
return -EBUSY;
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index cc8aa4a04348..ae1742a35e76 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -739,7 +739,7 @@ static bool is_same_device(struct btrfs_device *device, const char *new_path)
{
struct path old = { .mnt = NULL, .dentry = NULL };
struct path new = { .mnt = NULL, .dentry = NULL };
- char *old_path = NULL;
+ char AUTO_KFREE(old_path);
bool is_same = false;
int ret;
@@ -765,7 +765,6 @@ static bool is_same_device(struct btrfs_device *device, const char *new_path)
if (path_equal(&old, &new))
is_same = true;
out:
- kfree(old_path);
path_put(&old);
path_put(&new);
return is_same;
@@ -1681,7 +1680,7 @@ static int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes,
struct btrfs_root *root = fs_info->dev_root;
struct btrfs_key key;
struct btrfs_dev_extent *dev_extent;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
u64 search_start;
u64 hole_size;
u64 max_hole_start;
@@ -1711,8 +1710,8 @@ again:
}
path->reada = READA_FORWARD;
- path->search_commit_root = 1;
- path->skip_locking = 1;
+ path->search_commit_root = true;
+ path->skip_locking = true;
key.objectid = device->devid;
key.type = BTRFS_DEV_EXTENT_KEY;
@@ -1812,7 +1811,6 @@ next:
"max_hole_start=%llu max_hole_size=%llu search_end=%llu",
max_hole_start, max_hole_size, search_end);
out:
- btrfs_free_path(path);
*start = max_hole_start;
if (len)
*len = max_hole_size;
@@ -1826,7 +1824,7 @@ static int btrfs_free_dev_extent(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info = device->fs_info;
struct btrfs_root *root = fs_info->dev_root;
int ret;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
struct btrfs_key found_key;
struct extent_buffer *leaf = NULL;
@@ -1845,7 +1843,7 @@ again:
ret = btrfs_previous_item(root, path, key.objectid,
BTRFS_DEV_EXTENT_KEY);
if (ret)
- goto out;
+ return ret;
leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
extent = btrfs_item_ptr(leaf, path->slots[0],
@@ -1860,7 +1858,7 @@ again:
extent = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_dev_extent);
} else {
- goto out;
+ return ret;
}
*dev_extent_len = btrfs_dev_extent_length(leaf, extent);
@@ -1868,8 +1866,6 @@ again:
ret = btrfs_del_item(trans, root, path);
if (ret == 0)
set_bit(BTRFS_TRANS_HAVE_FREE_BGS, &trans->transaction->flags);
-out:
- btrfs_free_path(path);
return ret;
}
@@ -1897,7 +1893,7 @@ static noinline int find_next_devid(struct btrfs_fs_info *fs_info,
int ret;
struct btrfs_key key;
struct btrfs_key found_key;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
path = btrfs_alloc_path();
if (!path)
@@ -1909,13 +1905,12 @@ static noinline int find_next_devid(struct btrfs_fs_info *fs_info,
ret = btrfs_search_slot(NULL, fs_info->chunk_root, &key, path, 0, 0);
if (ret < 0)
- goto error;
+ return ret;
if (unlikely(ret == 0)) {
/* Corruption */
btrfs_err(fs_info, "corrupted chunk tree devid -1 matched");
- ret = -EUCLEAN;
- goto error;
+ return -EUCLEAN;
}
ret = btrfs_previous_item(fs_info->chunk_root, path,
@@ -1928,10 +1923,7 @@ static noinline int find_next_devid(struct btrfs_fs_info *fs_info,
path->slots[0]);
*devid_ret = found_key.offset + 1;
}
- ret = 0;
-error:
- btrfs_free_path(path);
- return ret;
+ return 0;
}
/*
@@ -1942,7 +1934,7 @@ static int btrfs_add_dev_item(struct btrfs_trans_handle *trans,
struct btrfs_device *device)
{
int ret;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_dev_item *dev_item;
struct extent_buffer *leaf;
struct btrfs_key key;
@@ -1961,7 +1953,7 @@ static int btrfs_add_dev_item(struct btrfs_trans_handle *trans,
&key, sizeof(*dev_item));
btrfs_trans_release_chunk_metadata(trans);
if (ret)
- goto out;
+ return ret;
leaf = path->nodes[0];
dev_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dev_item);
@@ -1987,10 +1979,7 @@ static int btrfs_add_dev_item(struct btrfs_trans_handle *trans,
write_extent_buffer(leaf, trans->fs_info->fs_devices->metadata_uuid,
ptr, BTRFS_FSID_SIZE);
- ret = 0;
-out:
- btrfs_free_path(path);
- return ret;
+ return 0;
}
/*
@@ -2014,7 +2003,7 @@ static int btrfs_rm_dev_item(struct btrfs_trans_handle *trans,
{
struct btrfs_root *root = device->fs_info->chunk_root;
int ret;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
path = btrfs_alloc_path();
@@ -2028,16 +2017,12 @@ static int btrfs_rm_dev_item(struct btrfs_trans_handle *trans,
btrfs_reserve_chunk_metadata(trans, false);
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
btrfs_trans_release_chunk_metadata(trans);
- if (ret) {
- if (ret > 0)
- ret = -ENOENT;
- goto out;
- }
+ if (ret > 0)
+ return -ENOENT;
+ if (ret < 0)
+ return ret;
- ret = btrfs_del_item(trans, root, path);
-out:
- btrfs_free_path(path);
- return ret;
+ return btrfs_del_item(trans, root, path);
}
/*
@@ -2623,7 +2608,7 @@ static int btrfs_finish_sprout(struct btrfs_trans_handle *trans)
BTRFS_DEV_LOOKUP_ARGS(args);
struct btrfs_fs_info *fs_info = trans->fs_info;
struct btrfs_root *root = fs_info->chunk_root;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct extent_buffer *leaf;
struct btrfs_dev_item *dev_item;
struct btrfs_device *device;
@@ -2645,7 +2630,7 @@ static int btrfs_finish_sprout(struct btrfs_trans_handle *trans)
ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
btrfs_trans_release_chunk_metadata(trans);
if (ret < 0)
- goto error;
+ return ret;
leaf = path->nodes[0];
next_slot:
@@ -2654,7 +2639,7 @@ next_slot:
if (ret > 0)
break;
if (ret < 0)
- goto error;
+ return ret;
leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
btrfs_release_path(path);
@@ -2685,10 +2670,7 @@ next_slot:
path->slots[0]++;
goto next_slot;
}
- ret = 0;
-error:
- btrfs_free_path(path);
- return ret;
+ return 0;
}
int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path)
@@ -2943,7 +2925,7 @@ static noinline int btrfs_update_device(struct btrfs_trans_handle *trans,
struct btrfs_device *device)
{
int ret;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_root *root = device->fs_info->chunk_root;
struct btrfs_dev_item *dev_item;
struct extent_buffer *leaf;
@@ -2959,12 +2941,10 @@ static noinline int btrfs_update_device(struct btrfs_trans_handle *trans,
ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
if (ret < 0)
- goto out;
+ return ret;
- if (ret > 0) {
- ret = -ENOENT;
- goto out;
- }
+ if (ret > 0)
+ return -ENOENT;
leaf = path->nodes[0];
dev_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dev_item);
@@ -2978,8 +2958,6 @@ static noinline int btrfs_update_device(struct btrfs_trans_handle *trans,
btrfs_device_get_disk_total_bytes(device));
btrfs_set_device_bytes_used(leaf, dev_item,
btrfs_device_get_bytes_used(device));
-out:
- btrfs_free_path(path);
return ret;
}
@@ -3032,7 +3010,7 @@ static int btrfs_free_chunk(struct btrfs_trans_handle *trans, u64 chunk_offset)
struct btrfs_fs_info *fs_info = trans->fs_info;
struct btrfs_root *root = fs_info->chunk_root;
int ret;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
path = btrfs_alloc_path();
@@ -3045,23 +3023,21 @@ static int btrfs_free_chunk(struct btrfs_trans_handle *trans, u64 chunk_offset)
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
if (ret < 0)
- goto out;
- else if (unlikely(ret > 0)) { /* Logic error or corruption */
+ return ret;
+ if (unlikely(ret > 0)) {
+ /* Logic error or corruption */
btrfs_err(fs_info, "failed to lookup chunk %llu when freeing",
chunk_offset);
btrfs_abort_transaction(trans, -ENOENT);
- ret = -EUCLEAN;
- goto out;
+ return -EUCLEAN;
}
ret = btrfs_del_item(trans, root, path);
if (unlikely(ret < 0)) {
btrfs_err(fs_info, "failed to delete chunk %llu item", chunk_offset);
btrfs_abort_transaction(trans, ret);
- goto out;
+ return ret;
}
-out:
- btrfs_free_path(path);
return ret;
}
@@ -3498,7 +3474,7 @@ int btrfs_relocate_chunk(struct btrfs_fs_info *fs_info, u64 chunk_offset,
static int btrfs_relocate_sys_chunks(struct btrfs_fs_info *fs_info)
{
struct btrfs_root *chunk_root = fs_info->chunk_root;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct extent_buffer *leaf;
struct btrfs_chunk *chunk;
struct btrfs_key key;
@@ -3522,7 +3498,7 @@ again:
ret = btrfs_search_slot(NULL, chunk_root, &key, path, 0, 0);
if (ret < 0) {
mutex_unlock(&fs_info->reclaim_bgs_lock);
- goto error;
+ return ret;
}
if (unlikely(ret == 0)) {
/*
@@ -3532,9 +3508,8 @@ again:
* offset (one less than the previous one, wrong
* alignment and size).
*/
- ret = -EUCLEAN;
mutex_unlock(&fs_info->reclaim_bgs_lock);
- goto error;
+ return -EUCLEAN;
}
ret = btrfs_previous_item(chunk_root, path, key.objectid,
@@ -3542,7 +3517,7 @@ again:
if (ret)
mutex_unlock(&fs_info->reclaim_bgs_lock);
if (ret < 0)
- goto error;
+ return ret;
if (ret > 0)
break;
@@ -3576,8 +3551,6 @@ again:
} else if (WARN_ON(failed && retried)) {
ret = -ENOSPC;
}
-error:
- btrfs_free_path(path);
return ret;
}
@@ -4078,7 +4051,7 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info)
struct btrfs_root *chunk_root = fs_info->chunk_root;
u64 chunk_type;
struct btrfs_chunk *chunk;
- struct btrfs_path *path = NULL;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
struct btrfs_key found_key;
struct extent_buffer *leaf;
@@ -4249,7 +4222,6 @@ loop:
goto again;
}
error:
- btrfs_free_path(path);
if (enospc_errors) {
btrfs_info(fs_info, "%d enospc errors during balance",
enospc_errors);
@@ -4407,7 +4379,7 @@ static void describe_balance_start_or_resume(struct btrfs_fs_info *fs_info)
{
u32 size_buf = 1024;
char tmp_buf[192] = {'\0'};
- char *buf;
+ char AUTO_KFREE(buf);
char *bp;
u32 size_bp = size_buf;
int ret;
@@ -4455,8 +4427,6 @@ out_overflow:
btrfs_info(fs_info, "balance: %s %s",
(bctl->flags & BTRFS_BALANCE_RESUME) ?
"resume" : "start", buf);
-
- kfree(buf);
}
/*
@@ -4706,7 +4676,7 @@ int btrfs_recover_balance(struct btrfs_fs_info *fs_info)
struct btrfs_balance_control *bctl;
struct btrfs_balance_item *item;
struct btrfs_disk_balance_args disk_bargs;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct extent_buffer *leaf;
struct btrfs_key key;
int ret;
@@ -4721,17 +4691,14 @@ int btrfs_recover_balance(struct btrfs_fs_info *fs_info)
ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, path, 0, 0);
if (ret < 0)
- goto out;
+ return ret;
if (ret > 0) { /* ret = -ENOENT; */
- ret = 0;
- goto out;
+ return 0;
}
bctl = kzalloc(sizeof(*bctl), GFP_NOFS);
- if (!bctl) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!bctl)
+ return -ENOMEM;
leaf = path->nodes[0];
item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_balance_item);
@@ -4768,8 +4735,6 @@ int btrfs_recover_balance(struct btrfs_fs_info *fs_info)
fs_info->balance_ctl = bctl;
spin_unlock(&fs_info->balance_lock);
mutex_unlock(&fs_info->balance_mutex);
-out:
- btrfs_free_path(path);
return ret;
}
@@ -5590,9 +5555,8 @@ struct btrfs_block_group *btrfs_create_chunk(struct btrfs_trans_handle *trans,
{
struct btrfs_fs_info *info = trans->fs_info;
struct btrfs_fs_devices *fs_devices = info->fs_devices;
- struct btrfs_device_info *devices_info = NULL;
+ struct btrfs_device_info AUTO_KFREE(devices_info);
struct alloc_chunk_ctl ctl;
- struct btrfs_block_group *block_group;
int ret;
lockdep_assert_held(&info->chunk_mutex);
@@ -5625,22 +5589,14 @@ struct btrfs_block_group *btrfs_create_chunk(struct btrfs_trans_handle *trans,
return ERR_PTR(-ENOMEM);
ret = gather_device_info(fs_devices, &ctl, devices_info);
- if (ret < 0) {
- block_group = ERR_PTR(ret);
- goto out;
- }
+ if (ret < 0)
+ return ERR_PTR(ret);
ret = decide_stripe_size(fs_devices, &ctl, devices_info);
- if (ret < 0) {
- block_group = ERR_PTR(ret);
- goto out;
- }
-
- block_group = create_chunk(trans, &ctl, devices_info);
+ if (ret < 0)
+ return ERR_PTR(ret);
-out:
- kfree(devices_info);
- return block_group;
+ return create_chunk(trans, &ctl, devices_info);
}
/*
@@ -6073,12 +6029,7 @@ struct btrfs_io_context *alloc_btrfs_io_context(struct btrfs_fs_info *fs_info,
{
struct btrfs_io_context *bioc;
- bioc = kzalloc(
- /* The size of btrfs_io_context */
- sizeof(struct btrfs_io_context) +
- /* Plus the variable array for the stripes */
- sizeof(struct btrfs_io_stripe) * (total_stripes),
- GFP_NOFS);
+ bioc = kzalloc(struct_size(bioc, stripes, total_stripes), GFP_NOFS);
if (!bioc)
return NULL;
@@ -6804,6 +6755,8 @@ static bool dev_args_match_fs_devices(const struct btrfs_dev_lookup_args *args,
static bool dev_args_match_device(const struct btrfs_dev_lookup_args *args,
const struct btrfs_device *device)
{
+ if (args->devt)
+ return device->devt == args->devt;
if (args->missing) {
if (test_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &device->dev_state) &&
!device->bdev)
@@ -7452,7 +7405,7 @@ static void readahead_tree_node_children(struct extent_buffer *node)
int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info)
{
struct btrfs_root *root = fs_info->chunk_root;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct extent_buffer *leaf;
struct btrfs_key key;
struct btrfs_key found_key;
@@ -7491,7 +7444,7 @@ int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info)
* chunk tree, to keep it simple, just skip locking on the chunk tree.
*/
ASSERT(!test_bit(BTRFS_FS_OPEN, &fs_info->flags));
- path->skip_locking = 1;
+ path->skip_locking = true;
/*
* Read all device items, and then all the chunk items. All
@@ -7569,8 +7522,6 @@ int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info)
ret = 0;
error:
mutex_unlock(&uuid_mutex);
-
- btrfs_free_path(path);
return ret;
}
@@ -7670,7 +7621,7 @@ int btrfs_init_dev_stats(struct btrfs_fs_info *fs_info)
{
struct btrfs_fs_devices *fs_devices = fs_info->fs_devices, *seed_devs;
struct btrfs_device *device;
- struct btrfs_path *path = NULL;
+ BTRFS_PATH_AUTO_FREE(path);
int ret = 0;
path = btrfs_alloc_path();
@@ -7692,8 +7643,6 @@ int btrfs_init_dev_stats(struct btrfs_fs_info *fs_info)
}
out:
mutex_unlock(&fs_devices->device_list_mutex);
-
- btrfs_free_path(path);
return ret;
}
@@ -7702,7 +7651,7 @@ static int update_dev_stat_item(struct btrfs_trans_handle *trans,
{
struct btrfs_fs_info *fs_info = trans->fs_info;
struct btrfs_root *dev_root = fs_info->dev_root;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_key key;
struct extent_buffer *eb;
struct btrfs_dev_stats_item *ptr;
@@ -7721,7 +7670,7 @@ static int update_dev_stat_item(struct btrfs_trans_handle *trans,
btrfs_warn(fs_info,
"error %d while searching for dev_stats item for device %s",
ret, btrfs_dev_name(device));
- goto out;
+ return ret;
}
if (ret == 0 &&
@@ -7732,7 +7681,7 @@ static int update_dev_stat_item(struct btrfs_trans_handle *trans,
btrfs_warn(fs_info,
"delete too small dev_stats item for device %s failed %d",
btrfs_dev_name(device), ret);
- goto out;
+ return ret;
}
ret = 1;
}
@@ -7746,7 +7695,7 @@ static int update_dev_stat_item(struct btrfs_trans_handle *trans,
btrfs_warn(fs_info,
"insert dev_stats item for device %s failed %d",
btrfs_dev_name(device), ret);
- goto out;
+ return ret;
}
}
@@ -7755,8 +7704,6 @@ static int update_dev_stat_item(struct btrfs_trans_handle *trans,
for (i = 0; i < BTRFS_DEV_STAT_VALUES_MAX; i++)
btrfs_set_dev_stats_value(eb, ptr, i,
btrfs_dev_stat_read(device, i));
-out:
- btrfs_free_path(path);
return ret;
}
@@ -8046,7 +7993,7 @@ out:
*/
int btrfs_verify_dev_extents(struct btrfs_fs_info *fs_info)
{
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct btrfs_root *root = fs_info->dev_root;
struct btrfs_key key;
u64 prev_devid = 0;
@@ -8077,17 +8024,15 @@ int btrfs_verify_dev_extents(struct btrfs_fs_info *fs_info)
path->reada = READA_FORWARD;
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
if (ret < 0)
- goto out;
+ return ret;
if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
ret = btrfs_next_leaf(root, path);
if (ret < 0)
- goto out;
+ return ret;
/* No dev extents at all? Not good */
- if (unlikely(ret > 0)) {
- ret = -EUCLEAN;
- goto out;
- }
+ if (unlikely(ret > 0))
+ return -EUCLEAN;
}
while (1) {
struct extent_buffer *leaf = path->nodes[0];
@@ -8113,20 +8058,19 @@ int btrfs_verify_dev_extents(struct btrfs_fs_info *fs_info)
btrfs_err(fs_info,
"dev extent devid %llu physical offset %llu overlap with previous dev extent end %llu",
devid, physical_offset, prev_dev_ext_end);
- ret = -EUCLEAN;
- goto out;
+ return -EUCLEAN;
}
ret = verify_one_dev_extent(fs_info, chunk_offset, devid,
physical_offset, physical_len);
if (ret < 0)
- goto out;
+ return ret;
prev_devid = devid;
prev_dev_ext_end = physical_offset + physical_len;
ret = btrfs_next_item(root, path);
if (ret < 0)
- goto out;
+ return ret;
if (ret > 0) {
ret = 0;
break;
@@ -8134,10 +8078,7 @@ int btrfs_verify_dev_extents(struct btrfs_fs_info *fs_info)
}
/* Ensure all chunks have corresponding dev extents */
- ret = verify_chunk_dev_extent_mapping(fs_info);
-out:
- btrfs_free_path(path);
- return ret;
+ return verify_chunk_dev_extent_mapping(fs_info);
}
/*
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 2cbf8080eade..34b854c1a303 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -45,7 +45,7 @@ extern struct mutex uuid_mutex;
#define BTRFS_STRIPE_LEN_SHIFT (16)
#define BTRFS_STRIPE_LEN_MASK (BTRFS_STRIPE_LEN - 1)
-static_assert(const_ilog2(BTRFS_STRIPE_LEN) == BTRFS_STRIPE_LEN_SHIFT);
+static_assert(ilog2(BTRFS_STRIPE_LEN) == BTRFS_STRIPE_LEN_SHIFT);
/* Used by sanity check for btrfs_raid_types. */
#define const_ffs(n) (__builtin_ctzll(n) + 1)
@@ -58,8 +58,7 @@ static_assert(const_ilog2(BTRFS_STRIPE_LEN) == BTRFS_STRIPE_LEN_SHIFT);
*/
static_assert(const_ffs(BTRFS_BLOCK_GROUP_RAID0) <
const_ffs(BTRFS_BLOCK_GROUP_PROFILE_MASK & ~BTRFS_BLOCK_GROUP_RAID0));
-static_assert(const_ilog2(BTRFS_BLOCK_GROUP_RAID0) >
- ilog2(BTRFS_BLOCK_GROUP_TYPE_MASK));
+static_assert(ilog2(BTRFS_BLOCK_GROUP_RAID0) > ilog2(BTRFS_BLOCK_GROUP_TYPE_MASK));
/* ilog2() can handle both constants and variables */
#define BTRFS_BG_FLAG_TO_INDEX(profile) \
@@ -662,6 +661,11 @@ struct btrfs_dev_lookup_args {
u64 devid;
u8 *uuid;
u8 *fsid;
+ /*
+ * If devt is specified, all other members will be ignored as it is
+ * enough to uniquely locate a device.
+ */
+ dev_t devt;
bool missing;
};
diff --git a/fs/btrfs/xattr.c b/fs/btrfs/xattr.c
index 79fb1614bd0c..ab55d10bd71f 100644
--- a/fs/btrfs/xattr.c
+++ b/fs/btrfs/xattr.c
@@ -29,9 +29,8 @@ int btrfs_getxattr(const struct inode *inode, const char *name,
{
struct btrfs_dir_item *di;
struct btrfs_root *root = BTRFS_I(inode)->root;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
struct extent_buffer *leaf;
- int ret = 0;
unsigned long data_ptr;
path = btrfs_alloc_path();
@@ -41,26 +40,19 @@ int btrfs_getxattr(const struct inode *inode, const char *name,
/* lookup the xattr by name */
di = btrfs_lookup_xattr(NULL, root, path, btrfs_ino(BTRFS_I(inode)),
name, strlen(name), 0);
- if (!di) {
- ret = -ENODATA;
- goto out;
- } else if (IS_ERR(di)) {
- ret = PTR_ERR(di);
- goto out;
- }
+ if (!di)
+ return -ENODATA;
+ if (IS_ERR(di))
+ return PTR_ERR(di);
leaf = path->nodes[0];
/* if size is 0, that means we want the size of the attr */
- if (!size) {
- ret = btrfs_dir_data_len(leaf, di);
- goto out;
- }
+ if (!size)
+ return btrfs_dir_data_len(leaf, di);
/* now get the data out of our dir_item */
- if (btrfs_dir_data_len(leaf, di) > size) {
- ret = -ERANGE;
- goto out;
- }
+ if (btrfs_dir_data_len(leaf, di) > size)
+ return -ERANGE;
/*
* The way things are packed into the leaf is like this
@@ -73,11 +65,7 @@ int btrfs_getxattr(const struct inode *inode, const char *name,
btrfs_dir_name_len(leaf, di));
read_extent_buffer(leaf, buffer, data_ptr,
btrfs_dir_data_len(leaf, di));
- ret = btrfs_dir_data_len(leaf, di);
-
-out:
- btrfs_free_path(path);
- return ret;
+ return btrfs_dir_data_len(leaf, di);
}
int btrfs_setxattr(struct btrfs_trans_handle *trans, struct inode *inode,
@@ -85,7 +73,7 @@ int btrfs_setxattr(struct btrfs_trans_handle *trans, struct inode *inode,
{
struct btrfs_dir_item *di = NULL;
struct btrfs_root *root = BTRFS_I(inode)->root;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
size_t name_len = strlen(name);
int ret = 0;
@@ -97,7 +85,7 @@ int btrfs_setxattr(struct btrfs_trans_handle *trans, struct inode *inode,
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
- path->skip_release_on_error = 1;
+ path->skip_release_on_error = true;
if (!value) {
di = btrfs_lookup_xattr(trans, root, path,
@@ -212,7 +200,6 @@ int btrfs_setxattr(struct btrfs_trans_handle *trans, struct inode *inode,
*/
}
out:
- btrfs_free_path(path);
if (!ret) {
set_bit(BTRFS_INODE_COPY_EVERYTHING,
&BTRFS_I(inode)->runtime_flags);
@@ -278,7 +265,7 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
struct btrfs_key key;
struct inode *inode = d_inode(dentry);
struct btrfs_root *root = BTRFS_I(inode)->root;
- struct btrfs_path *path;
+ BTRFS_PATH_AUTO_FREE(path);
int iter_ret = 0;
int ret = 0;
size_t total_size = 0, size_left = size;
@@ -354,8 +341,6 @@ next:
else
ret = total_size;
- btrfs_free_path(path);
-
return ret;
}
diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c
index d1db7fa1fe58..359a98e6de85 100644
--- a/fs/btrfs/zoned.c
+++ b/fs/btrfs/zoned.c
@@ -37,8 +37,8 @@
#define BTRFS_SB_LOG_FIRST_OFFSET (512ULL * SZ_1G)
#define BTRFS_SB_LOG_SECOND_OFFSET (4096ULL * SZ_1G)
-#define BTRFS_SB_LOG_FIRST_SHIFT const_ilog2(BTRFS_SB_LOG_FIRST_OFFSET)
-#define BTRFS_SB_LOG_SECOND_SHIFT const_ilog2(BTRFS_SB_LOG_SECOND_OFFSET)
+#define BTRFS_SB_LOG_FIRST_SHIFT ilog2(BTRFS_SB_LOG_FIRST_OFFSET)
+#define BTRFS_SB_LOG_SECOND_SHIFT ilog2(BTRFS_SB_LOG_SECOND_OFFSET)
/* Number of superblock log zones */
#define BTRFS_NR_SB_LOG_ZONES 2
@@ -93,7 +93,8 @@ static int sb_write_pointer(struct block_device *bdev, struct blk_zone *zones,
sector_t sector;
for (int i = 0; i < BTRFS_NR_SB_LOG_ZONES; i++) {
- ASSERT(zones[i].type != BLK_ZONE_TYPE_CONVENTIONAL);
+ ASSERT(zones[i].type != BLK_ZONE_TYPE_CONVENTIONAL,
+ "zones[%d].type=%d", i, zones[i].type);
empty[i] = (zones[i].cond == BLK_ZONE_COND_EMPTY);
full[i] = sb_zone_is_full(&zones[i]);
}
@@ -166,14 +167,14 @@ static inline u32 sb_zone_number(int shift, int mirror)
{
u64 zone = U64_MAX;
- ASSERT(mirror < BTRFS_SUPER_MIRROR_MAX);
+ ASSERT(mirror < BTRFS_SUPER_MIRROR_MAX, "mirror=%d", mirror);
switch (mirror) {
case 0: zone = 0; break;
case 1: zone = 1ULL << (BTRFS_SB_LOG_FIRST_SHIFT - shift); break;
case 2: zone = 1ULL << (BTRFS_SB_LOG_SECOND_SHIFT - shift); break;
}
- ASSERT(zone <= U32_MAX);
+ ASSERT(zone <= U32_MAX, "zone=%llu", zone);
return (u32)zone;
}
@@ -240,7 +241,8 @@ static int btrfs_get_dev_zones(struct btrfs_device *device, u64 pos,
unsigned int i;
u32 zno;
- ASSERT(IS_ALIGNED(pos, zinfo->zone_size));
+ ASSERT(IS_ALIGNED(pos, zinfo->zone_size),
+ "pos=%llu zinfo->zone_size=%llu", pos, zinfo->zone_size);
zno = pos >> zinfo->zone_size_shift;
/*
* We cannot report zones beyond the zone end. So, it is OK to
@@ -264,8 +266,8 @@ static int btrfs_get_dev_zones(struct btrfs_device *device, u64 pos,
}
}
- ret = blkdev_report_zones(device->bdev, pos >> SECTOR_SHIFT, *nr_zones,
- copy_zone_info_cb, zones);
+ ret = blkdev_report_zones_cached(device->bdev, pos >> SECTOR_SHIFT,
+ *nr_zones, copy_zone_info_cb, zones);
if (ret < 0) {
btrfs_err(device->fs_info,
"zoned: failed to read zone %llu on %s (devid %llu)",
@@ -494,6 +496,7 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device, bool populate_cache)
case BLK_ZONE_COND_IMP_OPEN:
case BLK_ZONE_COND_EXP_OPEN:
case BLK_ZONE_COND_CLOSED:
+ case BLK_ZONE_COND_ACTIVE:
__set_bit(nreported, zone_info->active_zones);
nactive++;
break;
@@ -896,9 +899,9 @@ int btrfs_sb_log_location_bdev(struct block_device *bdev, int mirror, int rw,
if (sb_zone + 1 >= nr_zones)
return -ENOENT;
- ret = blkdev_report_zones(bdev, zone_start_sector(sb_zone, bdev),
- BTRFS_NR_SB_LOG_ZONES, copy_zone_info_cb,
- zones);
+ ret = blkdev_report_zones_cached(bdev, zone_start_sector(sb_zone, bdev),
+ BTRFS_NR_SB_LOG_ZONES,
+ copy_zone_info_cb, zones);
if (ret < 0)
return ret;
if (unlikely(ret != BTRFS_NR_SB_LOG_ZONES))
@@ -1055,8 +1058,10 @@ u64 btrfs_find_allocatable_zones(struct btrfs_device *device, u64 hole_start,
bool have_sb;
int i;
- ASSERT(IS_ALIGNED(hole_start, zinfo->zone_size));
- ASSERT(IS_ALIGNED(num_bytes, zinfo->zone_size));
+ ASSERT(IS_ALIGNED(hole_start, zinfo->zone_size),
+ "hole_start=%llu zinfo->zone_size=%llu", hole_start, zinfo->zone_size);
+ ASSERT(IS_ALIGNED(num_bytes, zinfo->zone_size),
+ "num_bytes=%llu zinfo->zone_size=%llu", num_bytes, zinfo->zone_size);
while (pos < hole_end) {
begin = pos >> shift;
@@ -1172,8 +1177,10 @@ int btrfs_ensure_empty_zones(struct btrfs_device *device, u64 start, u64 size)
u64 pos;
int ret;
- ASSERT(IS_ALIGNED(start, zinfo->zone_size));
- ASSERT(IS_ALIGNED(size, zinfo->zone_size));
+ ASSERT(IS_ALIGNED(start, zinfo->zone_size),
+ "start=%llu, zinfo->zone_size=%llu", start, zinfo->zone_size);
+ ASSERT(IS_ALIGNED(size, zinfo->zone_size),
+ "size=%llu, zinfo->zone_size=%llu", size, zinfo->zone_size);
if (begin + nbits > zinfo->nr_zones)
return -ERANGE;
@@ -1628,7 +1635,7 @@ int btrfs_load_block_group_zone_info(struct btrfs_block_group *cache, bool new)
struct btrfs_chunk_map *map;
u64 logical = cache->start;
u64 length = cache->length;
- struct zone_info *zone_info = NULL;
+ struct zone_info AUTO_KFREE(zone_info);
int ret;
int i;
unsigned long *active = NULL;
@@ -1782,7 +1789,6 @@ out:
cache->physical_map = NULL;
}
bitmap_free(active);
- kfree(zone_info);
return ret;
}
@@ -1809,14 +1815,14 @@ bool btrfs_use_zone_append(struct btrfs_bio *bbio)
{
u64 start = (bbio->bio.bi_iter.bi_sector << SECTOR_SHIFT);
struct btrfs_inode *inode = bbio->inode;
- struct btrfs_fs_info *fs_info = bbio->fs_info;
+ struct btrfs_fs_info *fs_info = inode->root->fs_info;
struct btrfs_block_group *cache;
bool ret = false;
if (!btrfs_is_zoned(fs_info))
return false;
- if (!inode || !is_data_inode(inode))
+ if (!is_data_inode(inode))
return false;
if (btrfs_op(&bbio->bio) != BTRFS_MAP_WRITE)
@@ -1867,7 +1873,7 @@ static void btrfs_rewrite_logical_zoned(struct btrfs_ordered_extent *ordered,
em = btrfs_search_extent_mapping(em_tree, ordered->file_offset,
ordered->num_bytes);
/* The em should be a new COW extent, thus it should not have an offset. */
- ASSERT(em->offset == 0);
+ ASSERT(em->offset == 0, "em->offset=%llu", em->offset);
em->disk_bytenr = logical;
btrfs_free_extent_map(em);
write_unlock(&em_tree->lock);
@@ -2578,7 +2584,8 @@ again:
struct btrfs_space_info *reloc_sinfo = data_sinfo->sub_group[0];
int factor;
- ASSERT(reloc_sinfo->subgroup_id == BTRFS_SUB_GROUP_DATA_RELOC);
+ ASSERT(reloc_sinfo->subgroup_id == BTRFS_SUB_GROUP_DATA_RELOC,
+ "reloc_sinfo->subgroup_id=%d", reloc_sinfo->subgroup_id);
factor = btrfs_bg_type_to_factor(bg->flags);
down_write(&space_info->groups_sem);
@@ -2592,9 +2599,9 @@ again:
space_info->disk_total -= bg->length * factor;
space_info->disk_total -= bg->zone_unusable;
/* There is no allocation ever happened. */
- ASSERT(bg->used == 0);
+ ASSERT(bg->used == 0, "bg->used=%llu", bg->used);
/* No super block in a block group on the zoned setup. */
- ASSERT(bg->bytes_super == 0);
+ ASSERT(bg->bytes_super == 0, "bg->bytes_super=%llu", bg->bytes_super);
spin_unlock(&space_info->lock);
bg->space_info = reloc_sinfo;
@@ -2620,7 +2627,8 @@ again:
/* Allocate new BG in the data relocation space_info. */
space_info = data_sinfo->sub_group[0];
- ASSERT(space_info->subgroup_id == BTRFS_SUB_GROUP_DATA_RELOC);
+ ASSERT(space_info->subgroup_id == BTRFS_SUB_GROUP_DATA_RELOC,
+ "space_info->subgroup_id=%d", space_info->subgroup_id);
ret = btrfs_chunk_alloc(trans, space_info, alloc_flags, CHUNK_ALLOC_FORCE);
btrfs_end_transaction(trans);
if (ret == 1) {
@@ -2750,10 +2758,9 @@ int btrfs_zone_finish_one_bg(struct btrfs_fs_info *fs_info)
return ret < 0 ? ret : 1;
}
-int btrfs_zoned_activate_one_bg(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info,
- bool do_finish)
+int btrfs_zoned_activate_one_bg(struct btrfs_space_info *space_info, bool do_finish)
{
+ struct btrfs_fs_info *fs_info = space_info->fs_info;
struct btrfs_block_group *bg;
int index;
@@ -2962,7 +2969,8 @@ int btrfs_reset_unused_block_groups(struct btrfs_space_info *space_info, u64 num
* This holds because we currently reset fully used then freed
* block group.
*/
- ASSERT(reclaimed == bg->zone_capacity);
+ ASSERT(reclaimed == bg->zone_capacity,
+ "reclaimed=%llu bg->zone_capacity=%llu", reclaimed, bg->zone_capacity);
bg->free_space_ctl->free_space += reclaimed;
space_info->bytes_zone_unusable -= reclaimed;
spin_unlock(&bg->lock);
diff --git a/fs/btrfs/zoned.h b/fs/btrfs/zoned.h
index 17c5656580dd..5cefdeb08b7b 100644
--- a/fs/btrfs/zoned.h
+++ b/fs/btrfs/zoned.h
@@ -15,7 +15,6 @@
#include "disk-io.h"
#include "block-group.h"
#include "btrfs_inode.h"
-#include "fs.h"
struct block_device;
struct extent_buffer;
@@ -94,8 +93,7 @@ bool btrfs_zoned_should_reclaim(const struct btrfs_fs_info *fs_info);
void btrfs_zoned_release_data_reloc_bg(struct btrfs_fs_info *fs_info, u64 logical,
u64 length);
int btrfs_zone_finish_one_bg(struct btrfs_fs_info *fs_info);
-int btrfs_zoned_activate_one_bg(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info, bool do_finish);
+int btrfs_zoned_activate_one_bg(struct btrfs_space_info *space_info, bool do_finish);
void btrfs_check_active_zone_reservation(struct btrfs_fs_info *fs_info);
int btrfs_reset_unused_block_groups(struct btrfs_space_info *space_info, u64 num_bytes);
#else /* CONFIG_BLK_DEV_ZONED */
@@ -262,8 +260,7 @@ static inline int btrfs_zone_finish_one_bg(struct btrfs_fs_info *fs_info)
return 1;
}
-static inline int btrfs_zoned_activate_one_bg(struct btrfs_fs_info *fs_info,
- struct btrfs_space_info *space_info,
+static inline int btrfs_zoned_activate_one_bg(struct btrfs_space_info *space_info,
bool do_finish)
{
/* Consider all the block groups are active */
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index d18c0eaef9b7..bf50c6e7a029 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -2155,7 +2155,7 @@ static ssize_t ceph_read_dir(struct file *file, char __user *buf, size_t size,
" rfiles: %20lld\n"
" rsubdirs: %20lld\n"
"rbytes: %20lld\n"
- "rctime: %10lld.%09ld\n",
+ "rctime: %ptSp\n",
ci->i_files + ci->i_subdirs,
ci->i_files,
ci->i_subdirs,
@@ -2163,8 +2163,7 @@ static ssize_t ceph_read_dir(struct file *file, char __user *buf, size_t size,
ci->i_rfiles,
ci->i_rsubdirs,
ci->i_rbytes,
- ci->i_rctime.tv_sec,
- ci->i_rctime.tv_nsec);
+ &ci->i_rctime);
}
if (*ppos >= dfi->dir_info_len)
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 37d3a2477c17..a596cb53f1ac 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -879,7 +879,9 @@ void ceph_fill_file_time(struct inode *inode, int issued,
{
struct ceph_client *cl = ceph_inode_to_client(inode);
struct ceph_inode_info *ci = ceph_inode(inode);
+ struct timespec64 iatime = inode_get_atime(inode);
struct timespec64 ictime = inode_get_ctime(inode);
+ struct timespec64 imtime = inode_get_mtime(inode);
int warn = 0;
if (issued & (CEPH_CAP_FILE_EXCL|
@@ -889,39 +891,26 @@ void ceph_fill_file_time(struct inode *inode, int issued,
CEPH_CAP_XATTR_EXCL)) {
if (ci->i_version == 0 ||
timespec64_compare(ctime, &ictime) > 0) {
- doutc(cl, "ctime %lld.%09ld -> %lld.%09ld inc w/ cap\n",
- ictime.tv_sec, ictime.tv_nsec,
- ctime->tv_sec, ctime->tv_nsec);
+ doutc(cl, "ctime %ptSp -> %ptSp inc w/ cap\n", &ictime, ctime);
inode_set_ctime_to_ts(inode, *ctime);
}
if (ci->i_version == 0 ||
ceph_seq_cmp(time_warp_seq, ci->i_time_warp_seq) > 0) {
/* the MDS did a utimes() */
- doutc(cl, "mtime %lld.%09ld -> %lld.%09ld tw %d -> %d\n",
- inode_get_mtime_sec(inode),
- inode_get_mtime_nsec(inode),
- mtime->tv_sec, mtime->tv_nsec,
- ci->i_time_warp_seq, (int)time_warp_seq);
+ doutc(cl, "mtime %ptSp -> %ptSp tw %d -> %d\n", &imtime, mtime,
+ ci->i_time_warp_seq, (int)time_warp_seq);
inode_set_mtime_to_ts(inode, *mtime);
inode_set_atime_to_ts(inode, *atime);
ci->i_time_warp_seq = time_warp_seq;
} else if (time_warp_seq == ci->i_time_warp_seq) {
- struct timespec64 ts;
-
/* nobody did utimes(); take the max */
- ts = inode_get_mtime(inode);
- if (timespec64_compare(mtime, &ts) > 0) {
- doutc(cl, "mtime %lld.%09ld -> %lld.%09ld inc\n",
- ts.tv_sec, ts.tv_nsec,
- mtime->tv_sec, mtime->tv_nsec);
+ if (timespec64_compare(mtime, &imtime) > 0) {
+ doutc(cl, "mtime %ptSp -> %ptSp inc\n", &imtime, mtime);
inode_set_mtime_to_ts(inode, *mtime);
}
- ts = inode_get_atime(inode);
- if (timespec64_compare(atime, &ts) > 0) {
- doutc(cl, "atime %lld.%09ld -> %lld.%09ld inc\n",
- ts.tv_sec, ts.tv_nsec,
- atime->tv_sec, atime->tv_nsec);
+ if (timespec64_compare(atime, &iatime) > 0) {
+ doutc(cl, "atime %ptSp -> %ptSp inc\n", &iatime, atime);
inode_set_atime_to_ts(inode, *atime);
}
} else if (issued & CEPH_CAP_FILE_EXCL) {
@@ -2703,10 +2692,8 @@ retry:
if (ia_valid & ATTR_ATIME) {
struct timespec64 atime = inode_get_atime(inode);
- doutc(cl, "%p %llx.%llx atime %lld.%09ld -> %lld.%09ld\n",
- inode, ceph_vinop(inode),
- atime.tv_sec, atime.tv_nsec,
- attr->ia_atime.tv_sec, attr->ia_atime.tv_nsec);
+ doutc(cl, "%p %llx.%llx atime %ptSp -> %ptSp\n",
+ inode, ceph_vinop(inode), &atime, &attr->ia_atime);
if (!do_sync && (issued & CEPH_CAP_FILE_EXCL)) {
ci->i_time_warp_seq++;
inode_set_atime_to_ts(inode, attr->ia_atime);
@@ -2780,10 +2767,8 @@ retry:
if (ia_valid & ATTR_MTIME) {
struct timespec64 mtime = inode_get_mtime(inode);
- doutc(cl, "%p %llx.%llx mtime %lld.%09ld -> %lld.%09ld\n",
- inode, ceph_vinop(inode),
- mtime.tv_sec, mtime.tv_nsec,
- attr->ia_mtime.tv_sec, attr->ia_mtime.tv_nsec);
+ doutc(cl, "%p %llx.%llx mtime %ptSp -> %ptSp\n",
+ inode, ceph_vinop(inode), &mtime, &attr->ia_mtime);
if (!do_sync && (issued & CEPH_CAP_FILE_EXCL)) {
ci->i_time_warp_seq++;
inode_set_mtime_to_ts(inode, attr->ia_mtime);
@@ -2804,13 +2789,11 @@ retry:
/* these do nothing */
if (ia_valid & ATTR_CTIME) {
+ struct timespec64 ictime = inode_get_ctime(inode);
bool only = (ia_valid & (ATTR_SIZE|ATTR_MTIME|ATTR_ATIME|
ATTR_MODE|ATTR_UID|ATTR_GID)) == 0;
- doutc(cl, "%p %llx.%llx ctime %lld.%09ld -> %lld.%09ld (%s)\n",
- inode, ceph_vinop(inode),
- inode_get_ctime_sec(inode),
- inode_get_ctime_nsec(inode),
- attr->ia_ctime.tv_sec, attr->ia_ctime.tv_nsec,
+ doutc(cl, "%p %llx.%llx ctime %ptSp -> %ptSp (%s)\n",
+ inode, ceph_vinop(inode), &ictime, &attr->ia_ctime,
only ? "ctime only" : "ignored");
if (only) {
/*
diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c
index 537165db4519..ad1f30bea175 100644
--- a/fs/ceph/xattr.c
+++ b/fs/ceph/xattr.c
@@ -249,8 +249,7 @@ static ssize_t ceph_vxattrcb_dir_rbytes(struct ceph_inode_info *ci, char *val,
static ssize_t ceph_vxattrcb_dir_rctime(struct ceph_inode_info *ci, char *val,
size_t size)
{
- return ceph_fmt_xattr(val, size, "%lld.%09ld", ci->i_rctime.tv_sec,
- ci->i_rctime.tv_nsec);
+ return ceph_fmt_xattr(val, size, "%ptSp", &ci->i_rctime);
}
/* dir pin */
@@ -307,8 +306,7 @@ static bool ceph_vxattrcb_snap_btime_exists(struct ceph_inode_info *ci)
static ssize_t ceph_vxattrcb_snap_btime(struct ceph_inode_info *ci, char *val,
size_t size)
{
- return ceph_fmt_xattr(val, size, "%lld.%09ld", ci->i_snap_btime.tv_sec,
- ci->i_snap_btime.tv_nsec);
+ return ceph_fmt_xattr(val, size, "%ptSp", &ci->i_snap_btime);
}
static ssize_t ceph_vxattrcb_cluster_fsid(struct ceph_inode_info *ci,
diff --git a/fs/coredump.c b/fs/coredump.c
index fe4099e0530b..8feb9c1cf83d 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -708,7 +708,7 @@ static bool coredump_sock_connect(struct core_name *cn, struct coredump_params *
*/
pidfs_coredump(cprm);
- retval = kernel_connect(socket, (struct sockaddr *)(&addr), addr_len,
+ retval = kernel_connect(socket, (struct sockaddr_unsized *)(&addr), addr_len,
O_NONBLOCK | SOCK_COREDUMP);
if (retval) {
diff --git a/fs/dlm/lowcomms.c b/fs/dlm/lowcomms.c
index 9a0b6c2b6b01..b3958008ba3f 100644
--- a/fs/dlm/lowcomms.c
+++ b/fs/dlm/lowcomms.c
@@ -1126,7 +1126,7 @@ static void writequeue_entry_complete(struct writequeue_entry *e, int completed)
static int sctp_bind_addrs(struct socket *sock, __be16 port)
{
struct sockaddr_storage localaddr;
- struct sockaddr *addr = (struct sockaddr *)&localaddr;
+ struct sockaddr_unsized *addr = (struct sockaddr_unsized *)&localaddr;
int i, addr_len, result = 0;
for (i = 0; i < dlm_local_count; i++) {
@@ -1599,7 +1599,7 @@ static int dlm_connect(struct connection *con)
log_print_ratelimited("connecting to %d", con->nodeid);
make_sockaddr(&addr, dlm_config.ci_tcp_port, &addr_len);
- result = kernel_connect(sock, (struct sockaddr *)&addr, addr_len, 0);
+ result = kernel_connect(sock, (struct sockaddr_unsized *)&addr, addr_len, 0);
switch (result) {
case -EINPROGRESS:
/* not an error */
@@ -1813,7 +1813,7 @@ static int dlm_tcp_bind(struct socket *sock)
memcpy(&src_addr, &dlm_local_addr[0], sizeof(src_addr));
make_sockaddr(&src_addr, 0, &addr_len);
- result = kernel_bind(sock, (struct sockaddr *)&src_addr,
+ result = kernel_bind(sock, (struct sockaddr_unsized *)&src_addr,
addr_len);
if (result < 0) {
/* This *may* not indicate a critical error */
@@ -1852,7 +1852,7 @@ static int dlm_tcp_listen_bind(struct socket *sock)
/* Bind to our port */
make_sockaddr(&dlm_local_addr[0], dlm_config.ci_tcp_port, &addr_len);
- return kernel_bind(sock, (struct sockaddr *)&dlm_local_addr[0],
+ return kernel_bind(sock, (struct sockaddr_unsized *)&dlm_local_addr[0],
addr_len);
}
diff --git a/fs/erofs/compress.h b/fs/erofs/compress.h
index 510e922c5193..84c8e52581f4 100644
--- a/fs/erofs/compress.h
+++ b/fs/erofs/compress.h
@@ -23,8 +23,8 @@ struct z_erofs_decompress_req {
struct z_erofs_decompressor {
int (*config)(struct super_block *sb, struct erofs_super_block *dsb,
void *data, int size);
- int (*decompress)(struct z_erofs_decompress_req *rq,
- struct page **pagepool);
+ const char *(*decompress)(struct z_erofs_decompress_req *rq,
+ struct page **pagepool);
int (*init)(void);
void (*exit)(void);
char *name;
@@ -70,10 +70,10 @@ struct z_erofs_stream_dctx {
bool bounced; /* is the bounce buffer used now? */
};
-int z_erofs_stream_switch_bufs(struct z_erofs_stream_dctx *dctx, void **dst,
- void **src, struct page **pgpl);
-int z_erofs_fixup_insize(struct z_erofs_decompress_req *rq, const char *padbuf,
- unsigned int padbufsize);
+const char *z_erofs_stream_switch_bufs(struct z_erofs_stream_dctx *dctx,
+ void **dst, void **src, struct page **pgpl);
+const char *z_erofs_fixup_insize(struct z_erofs_decompress_req *rq,
+ const char *padbuf, unsigned int padbufsize);
int __init z_erofs_init_decompressor(void);
void z_erofs_exit_decompressor(void);
int z_erofs_crypto_decompress(struct z_erofs_decompress_req *rq,
diff --git a/fs/erofs/decompressor.c b/fs/erofs/decompressor.c
index 354762c9723f..d5d090276391 100644
--- a/fs/erofs/decompressor.c
+++ b/fs/erofs/decompressor.c
@@ -105,44 +105,58 @@ static int z_erofs_lz4_prepare_dstpages(struct z_erofs_decompress_req *rq,
return kaddr ? 1 : 0;
}
-static void *z_erofs_lz4_handle_overlap(struct z_erofs_decompress_req *rq,
+static void *z_erofs_lz4_handle_overlap(const struct z_erofs_decompress_req *rq,
void *inpage, void *out, unsigned int *inputmargin,
int *maptype, bool may_inplace)
{
- unsigned int oend, omargin, total, i;
+ unsigned int oend, omargin, cnt, i;
struct page **in;
- void *src, *tmp;
-
- if (rq->inplace_io) {
- oend = rq->pageofs_out + rq->outputsize;
- omargin = PAGE_ALIGN(oend) - oend;
- if (rq->partial_decoding || !may_inplace ||
- omargin < LZ4_DECOMPRESS_INPLACE_MARGIN(rq->inputsize))
- goto docopy;
+ void *src;
+ /*
+ * If in-place I/O isn't used, for example, the bounce compressed cache
+ * can hold data for incomplete read requests. Just map the compressed
+ * buffer as well and decompress directly.
+ */
+ if (!rq->inplace_io) {
+ if (rq->inpages <= 1) {
+ *maptype = 0;
+ return inpage;
+ }
+ kunmap_local(inpage);
+ src = erofs_vm_map_ram(rq->in, rq->inpages);
+ if (!src)
+ return ERR_PTR(-ENOMEM);
+ *maptype = 1;
+ return src;
+ }
+ /*
+ * Then, deal with in-place I/Os. The reasons why in-place I/O is useful
+ * are: (1) It minimizes memory footprint during the I/O submission,
+ * which is useful for slow storage (including network devices and
+ * low-end HDDs/eMMCs) but with a lot inflight I/Os; (2) If in-place
+ * decompression can also be applied, it will reuse the unique buffer so
+ * that no extra CPU D-cache is polluted with temporary compressed data
+ * for extreme performance.
+ */
+ oend = rq->pageofs_out + rq->outputsize;
+ omargin = PAGE_ALIGN(oend) - oend;
+ if (!rq->partial_decoding && may_inplace &&
+ omargin >= LZ4_DECOMPRESS_INPLACE_MARGIN(rq->inputsize)) {
for (i = 0; i < rq->inpages; ++i)
if (rq->out[rq->outpages - rq->inpages + i] !=
rq->in[i])
- goto docopy;
- kunmap_local(inpage);
- *maptype = 3;
- return out + ((rq->outpages - rq->inpages) << PAGE_SHIFT);
- }
-
- if (rq->inpages <= 1) {
- *maptype = 0;
- return inpage;
+ break;
+ if (i >= rq->inpages) {
+ kunmap_local(inpage);
+ *maptype = 3;
+ return out + ((rq->outpages - rq->inpages) << PAGE_SHIFT);
+ }
}
- kunmap_local(inpage);
- src = erofs_vm_map_ram(rq->in, rq->inpages);
- if (!src)
- return ERR_PTR(-ENOMEM);
- *maptype = 1;
- return src;
-
-docopy:
- /* Or copy compressed data which can be overlapped to per-CPU buffer */
- in = rq->in;
+ /*
+ * If in-place decompression can't be applied, copy compressed data that
+ * may potentially overlap during decompression to a per-CPU buffer.
+ */
src = z_erofs_get_gbuf(rq->inpages);
if (!src) {
DBG_BUGON(1);
@@ -150,20 +164,13 @@ docopy:
return ERR_PTR(-EFAULT);
}
- tmp = src;
- total = rq->inputsize;
- while (total) {
- unsigned int page_copycnt =
- min_t(unsigned int, total, PAGE_SIZE - *inputmargin);
-
+ for (i = 0, in = rq->in; i < rq->inputsize; i += cnt, ++in) {
+ cnt = min_t(u32, rq->inputsize - i, PAGE_SIZE - *inputmargin);
if (!inpage)
inpage = kmap_local_page(*in);
- memcpy(tmp, inpage + *inputmargin, page_copycnt);
+ memcpy(src + i, inpage + *inputmargin, cnt);
kunmap_local(inpage);
inpage = NULL;
- tmp += page_copycnt;
- total -= page_copycnt;
- ++in;
*inputmargin = 0;
}
*maptype = 2;
@@ -171,21 +178,21 @@ docopy:
}
/*
- * Get the exact inputsize with zero_padding feature.
- * - For LZ4, it should work if zero_padding feature is on (5.3+);
- * - For MicroLZMA, it'd be enabled all the time.
+ * Get the exact on-disk size of the compressed data:
+ * - For LZ4, it should apply if the zero_padding feature is on (5.3+);
+ * - For others, zero_padding is enabled all the time.
*/
-int z_erofs_fixup_insize(struct z_erofs_decompress_req *rq, const char *padbuf,
- unsigned int padbufsize)
+const char *z_erofs_fixup_insize(struct z_erofs_decompress_req *rq,
+ const char *padbuf, unsigned int padbufsize)
{
const char *padend;
padend = memchr_inv(padbuf, 0, padbufsize);
if (!padend)
- return -EFSCORRUPTED;
+ return "compressed data start not found";
rq->inputsize -= padend - padbuf;
rq->pageofs_in += padend - padbuf;
- return 0;
+ return NULL;
}
static int z_erofs_lz4_decompress_mem(struct z_erofs_decompress_req *rq, u8 *dst)
@@ -193,6 +200,7 @@ static int z_erofs_lz4_decompress_mem(struct z_erofs_decompress_req *rq, u8 *dst
bool support_0padding = false, may_inplace = false;
unsigned int inputmargin;
u8 *out, *headpage, *src;
+ const char *reason;
int ret, maptype;
DBG_BUGON(*rq->in == NULL);
@@ -201,12 +209,12 @@ static int z_erofs_lz4_decompress_mem(struct z_erofs_decompress_req *rq, u8 *dst
/* LZ4 decompression inplace is only safe if zero_padding is enabled */
if (erofs_sb_has_zero_padding(EROFS_SB(rq->sb))) {
support_0padding = true;
- ret = z_erofs_fixup_insize(rq, headpage + rq->pageofs_in,
+ reason = z_erofs_fixup_insize(rq, headpage + rq->pageofs_in,
min_t(unsigned int, rq->inputsize,
rq->sb->s_blocksize - rq->pageofs_in));
- if (ret) {
+ if (reason) {
kunmap_local(headpage);
- return ret;
+ return IS_ERR(reason) ? PTR_ERR(reason) : -EFSCORRUPTED;
}
may_inplace = !((rq->pageofs_in + rq->inputsize) &
(rq->sb->s_blocksize - 1));
@@ -228,8 +236,6 @@ static int z_erofs_lz4_decompress_mem(struct z_erofs_decompress_req *rq, u8 *dst
rq->inputsize, rq->outputsize);
if (ret != rq->outputsize) {
- erofs_err(rq->sb, "failed to decompress %d in[%u, %u] out[%u]",
- ret, rq->inputsize, inputmargin, rq->outputsize);
if (ret >= 0)
memset(out + ret, 0, rq->outputsize - ret);
ret = -EFSCORRUPTED;
@@ -250,8 +256,8 @@ static int z_erofs_lz4_decompress_mem(struct z_erofs_decompress_req *rq, u8 *dst
return ret;
}
-static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq,
- struct page **pagepool)
+static const char *z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq,
+ struct page **pagepool)
{
unsigned int dst_maptype;
void *dst;
@@ -266,14 +272,14 @@ static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq,
/* general decoding path which can be used for all cases */
ret = z_erofs_lz4_prepare_dstpages(rq, pagepool);
if (ret < 0)
- return ret;
+ return ERR_PTR(ret);
if (ret > 0) {
dst = page_address(*rq->out);
dst_maptype = 1;
} else {
dst = erofs_vm_map_ram(rq->out, rq->outpages);
if (!dst)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
dst_maptype = 2;
}
}
@@ -282,11 +288,11 @@ static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq,
kunmap_local(dst);
else if (dst_maptype == 2)
vm_unmap_ram(dst, rq->outpages);
- return ret;
+ return ERR_PTR(ret);
}
-static int z_erofs_transform_plain(struct z_erofs_decompress_req *rq,
- struct page **pagepool)
+static const char *z_erofs_transform_plain(struct z_erofs_decompress_req *rq,
+ struct page **pagepool)
{
const unsigned int nrpages_in = rq->inpages, nrpages_out = rq->outpages;
const unsigned int bs = rq->sb->s_blocksize;
@@ -294,7 +300,7 @@ static int z_erofs_transform_plain(struct z_erofs_decompress_req *rq,
u8 *kin;
if (rq->outputsize > rq->inputsize)
- return -EOPNOTSUPP;
+ return ERR_PTR(-EOPNOTSUPP);
if (rq->alg == Z_EROFS_COMPRESSION_INTERLACED) {
cur = bs - (rq->pageofs_out & (bs - 1));
pi = (rq->pageofs_in + rq->inputsize - cur) & ~PAGE_MASK;
@@ -334,22 +340,19 @@ static int z_erofs_transform_plain(struct z_erofs_decompress_req *rq,
kunmap_local(kin);
}
DBG_BUGON(ni > nrpages_in);
- return 0;
+ return NULL;
}
-int z_erofs_stream_switch_bufs(struct z_erofs_stream_dctx *dctx, void **dst,
- void **src, struct page **pgpl)
+const char *z_erofs_stream_switch_bufs(struct z_erofs_stream_dctx *dctx,
+ void **dst, void **src, struct page **pgpl)
{
struct z_erofs_decompress_req *rq = dctx->rq;
- struct super_block *sb = rq->sb;
struct page **pgo, *tmppage;
unsigned int j;
if (!dctx->avail_out) {
- if (++dctx->no >= rq->outpages || !rq->outputsize) {
- erofs_err(sb, "insufficient space for decompressed data");
- return -EFSCORRUPTED;
- }
+ if (++dctx->no >= rq->outpages || !rq->outputsize)
+ return "insufficient space for decompressed data";
if (dctx->kout)
kunmap_local(dctx->kout);
@@ -360,7 +363,7 @@ int z_erofs_stream_switch_bufs(struct z_erofs_stream_dctx *dctx, void **dst,
*pgo = erofs_allocpage(pgpl, rq->gfp);
if (!*pgo) {
dctx->kout = NULL;
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
}
set_page_private(*pgo, Z_EROFS_SHORTLIVED_PAGE);
}
@@ -374,10 +377,8 @@ int z_erofs_stream_switch_bufs(struct z_erofs_stream_dctx *dctx, void **dst,
}
if (dctx->inbuf_pos == dctx->inbuf_sz && rq->inputsize) {
- if (++dctx->ni >= rq->inpages) {
- erofs_err(sb, "invalid compressed data");
- return -EFSCORRUPTED;
- }
+ if (++dctx->ni >= rq->inpages)
+ return "invalid compressed data";
if (dctx->kout) /* unlike kmap(), take care of the orders */
kunmap_local(dctx->kout);
kunmap_local(dctx->kin);
@@ -412,12 +413,12 @@ int z_erofs_stream_switch_bufs(struct z_erofs_stream_dctx *dctx, void **dst,
continue;
tmppage = erofs_allocpage(pgpl, rq->gfp);
if (!tmppage)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
set_page_private(tmppage, Z_EROFS_SHORTLIVED_PAGE);
copy_highpage(tmppage, rq->in[j]);
rq->in[j] = tmppage;
}
- return 0;
+ return NULL;
}
const struct z_erofs_decompressor *z_erofs_decomp[] = {
diff --git a/fs/erofs/decompressor_crypto.c b/fs/erofs/decompressor_crypto.c
index 97b77ab64432..5ef6f71d3b7f 100644
--- a/fs/erofs/decompressor_crypto.c
+++ b/fs/erofs/decompressor_crypto.c
@@ -9,16 +9,17 @@ static int __z_erofs_crypto_decompress(struct z_erofs_decompress_req *rq,
struct sg_table st_src, st_dst;
struct acomp_req *req;
struct crypto_wait wait;
+ const char *reason;
u8 *headpage;
int ret;
headpage = kmap_local_page(*rq->in);
- ret = z_erofs_fixup_insize(rq, headpage + rq->pageofs_in,
+ reason = z_erofs_fixup_insize(rq, headpage + rq->pageofs_in,
min_t(unsigned int, rq->inputsize,
rq->sb->s_blocksize - rq->pageofs_in));
kunmap_local(headpage);
- if (ret)
- return ret;
+ if (reason)
+ return IS_ERR(reason) ? PTR_ERR(reason) : -EFSCORRUPTED;
req = acomp_request_alloc(tfm);
if (!req)
diff --git a/fs/erofs/decompressor_deflate.c b/fs/erofs/decompressor_deflate.c
index 6909b2d529c7..3fb73000ed27 100644
--- a/fs/erofs/decompressor_deflate.c
+++ b/fs/erofs/decompressor_deflate.c
@@ -97,21 +97,22 @@ failed:
return -ENOMEM;
}
-static int __z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
- struct page **pgpl)
+static const char *__z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
+ struct page **pgpl)
{
struct super_block *sb = rq->sb;
struct z_erofs_stream_dctx dctx = { .rq = rq, .no = -1, .ni = 0 };
struct z_erofs_deflate *strm;
- int zerr, err;
+ const char *reason;
+ int zerr;
/* 1. get the exact DEFLATE compressed size */
dctx.kin = kmap_local_page(*rq->in);
- err = z_erofs_fixup_insize(rq, dctx.kin + rq->pageofs_in,
+ reason = z_erofs_fixup_insize(rq, dctx.kin + rq->pageofs_in,
min(rq->inputsize, sb->s_blocksize - rq->pageofs_in));
- if (err) {
+ if (reason) {
kunmap_local(dctx.kin);
- return err;
+ return reason;
}
/* 2. get an available DEFLATE context */
@@ -129,7 +130,7 @@ again:
/* 3. multi-call decompress */
zerr = zlib_inflateInit2(&strm->z, -MAX_WBITS);
if (zerr != Z_OK) {
- err = -EIO;
+ reason = ERR_PTR(-EINVAL);
goto failed_zinit;
}
@@ -143,10 +144,10 @@ again:
while (1) {
dctx.avail_out = strm->z.avail_out;
dctx.inbuf_sz = strm->z.avail_in;
- err = z_erofs_stream_switch_bufs(&dctx,
+ reason = z_erofs_stream_switch_bufs(&dctx,
(void **)&strm->z.next_out,
(void **)&strm->z.next_in, pgpl);
- if (err)
+ if (reason)
break;
strm->z.avail_out = dctx.avail_out;
strm->z.avail_in = dctx.inbuf_sz;
@@ -157,14 +158,14 @@ again:
break;
if (zerr == Z_STREAM_END && !rq->outputsize)
break;
- erofs_err(sb, "failed to decompress %d in[%u] out[%u]",
- zerr, rq->inputsize, rq->outputsize);
- err = -EFSCORRUPTED;
+ reason = (zerr == Z_DATA_ERROR ?
+ "corrupted compressed data" :
+ "unexpected end of stream");
break;
}
}
- if (zlib_inflateEnd(&strm->z) != Z_OK && !err)
- err = -EIO;
+ if (zlib_inflateEnd(&strm->z) != Z_OK && !reason)
+ reason = ERR_PTR(-EIO);
if (dctx.kout)
kunmap_local(dctx.kout);
failed_zinit:
@@ -175,11 +176,11 @@ failed_zinit:
z_erofs_deflate_head = strm;
spin_unlock(&z_erofs_deflate_lock);
wake_up(&z_erofs_deflate_wq);
- return err;
+ return reason;
}
-static int z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
- struct page **pgpl)
+static const char *z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
+ struct page **pgpl)
{
#ifdef CONFIG_EROFS_FS_ZIP_ACCEL
int err;
@@ -187,7 +188,7 @@ static int z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
if (!rq->partial_decoding) {
err = z_erofs_crypto_decompress(rq, pgpl);
if (err != -EOPNOTSUPP)
- return err;
+ return ERR_PTR(err);
}
#endif
diff --git a/fs/erofs/decompressor_lzma.c b/fs/erofs/decompressor_lzma.c
index 832cffb83a66..b4ea6978faae 100644
--- a/fs/erofs/decompressor_lzma.c
+++ b/fs/erofs/decompressor_lzma.c
@@ -146,23 +146,23 @@ again:
return err;
}
-static int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq,
- struct page **pgpl)
+static const char *z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq,
+ struct page **pgpl)
{
struct super_block *sb = rq->sb;
struct z_erofs_stream_dctx dctx = { .rq = rq, .no = -1, .ni = 0 };
struct xz_buf buf = {};
struct z_erofs_lzma *strm;
enum xz_ret xz_err;
- int err;
+ const char *reason;
/* 1. get the exact LZMA compressed size */
dctx.kin = kmap_local_page(*rq->in);
- err = z_erofs_fixup_insize(rq, dctx.kin + rq->pageofs_in,
+ reason = z_erofs_fixup_insize(rq, dctx.kin + rq->pageofs_in,
min(rq->inputsize, sb->s_blocksize - rq->pageofs_in));
- if (err) {
+ if (reason) {
kunmap_local(dctx.kin);
- return err;
+ return reason;
}
/* 2. get an available lzma context */
@@ -188,9 +188,9 @@ again:
dctx.avail_out = buf.out_size - buf.out_pos;
dctx.inbuf_sz = buf.in_size;
dctx.inbuf_pos = buf.in_pos;
- err = z_erofs_stream_switch_bufs(&dctx, (void **)&buf.out,
- (void **)&buf.in, pgpl);
- if (err)
+ reason = z_erofs_stream_switch_bufs(&dctx, (void **)&buf.out,
+ (void **)&buf.in, pgpl);
+ if (reason)
break;
if (buf.out_size == buf.out_pos) {
@@ -207,9 +207,9 @@ again:
if (xz_err != XZ_OK) {
if (xz_err == XZ_STREAM_END && !rq->outputsize)
break;
- erofs_err(sb, "failed to decompress %d in[%u] out[%u]",
- xz_err, rq->inputsize, rq->outputsize);
- err = -EFSCORRUPTED;
+ reason = (xz_err == XZ_DATA_ERROR ?
+ "corrupted compressed data" :
+ "unexpected end of stream");
break;
}
} while (1);
@@ -223,7 +223,7 @@ again:
z_erofs_lzma_head = strm;
spin_unlock(&z_erofs_lzma_lock);
wake_up(&z_erofs_lzma_wq);
- return err;
+ return reason;
}
const struct z_erofs_decompressor z_erofs_lzma_decomp = {
diff --git a/fs/erofs/decompressor_zstd.c b/fs/erofs/decompressor_zstd.c
index e38d93bb2104..beae49165c69 100644
--- a/fs/erofs/decompressor_zstd.c
+++ b/fs/erofs/decompressor_zstd.c
@@ -135,8 +135,8 @@ static int z_erofs_load_zstd_config(struct super_block *sb,
return strm ? -ENOMEM : 0;
}
-static int z_erofs_zstd_decompress(struct z_erofs_decompress_req *rq,
- struct page **pgpl)
+static const char *z_erofs_zstd_decompress(struct z_erofs_decompress_req *rq,
+ struct page **pgpl)
{
struct super_block *sb = rq->sb;
struct z_erofs_stream_dctx dctx = { .rq = rq, .no = -1, .ni = 0 };
@@ -144,15 +144,16 @@ static int z_erofs_zstd_decompress(struct z_erofs_decompress_req *rq,
zstd_out_buffer out_buf = { NULL, 0, 0 };
struct z_erofs_zstd *strm;
zstd_dstream *stream;
- int zerr, err;
+ const char *reason;
+ int zerr;
/* 1. get the exact compressed size */
dctx.kin = kmap_local_page(*rq->in);
- err = z_erofs_fixup_insize(rq, dctx.kin + rq->pageofs_in,
+ reason = z_erofs_fixup_insize(rq, dctx.kin + rq->pageofs_in,
min(rq->inputsize, sb->s_blocksize - rq->pageofs_in));
- if (err) {
+ if (reason) {
kunmap_local(dctx.kin);
- return err;
+ return reason;
}
/* 2. get an available ZSTD context */
@@ -161,7 +162,7 @@ static int z_erofs_zstd_decompress(struct z_erofs_decompress_req *rq,
/* 3. multi-call decompress */
stream = zstd_init_dstream(z_erofs_zstd_max_dictsize, strm->wksp, strm->wkspsz);
if (!stream) {
- err = -EIO;
+ reason = ERR_PTR(-ENOMEM);
goto failed_zinit;
}
@@ -174,9 +175,9 @@ static int z_erofs_zstd_decompress(struct z_erofs_decompress_req *rq,
do {
dctx.inbuf_sz = in_buf.size;
dctx.inbuf_pos = in_buf.pos;
- err = z_erofs_stream_switch_bufs(&dctx, &out_buf.dst,
+ reason = z_erofs_stream_switch_bufs(&dctx, &out_buf.dst,
(void **)&in_buf.src, pgpl);
- if (err)
+ if (reason)
break;
if (out_buf.size == out_buf.pos) {
@@ -191,11 +192,8 @@ static int z_erofs_zstd_decompress(struct z_erofs_decompress_req *rq,
if (zstd_is_error(zerr) ||
((rq->outputsize + dctx.avail_out) && (!zerr || (zerr > 0 &&
!(rq->inputsize + in_buf.size - in_buf.pos))))) {
- erofs_err(sb, "failed to decompress in[%u] out[%u]: %s",
- rq->inputsize, rq->outputsize,
- zstd_is_error(zerr) ? zstd_get_error_name(zerr) :
- "unexpected end of stream");
- err = -EFSCORRUPTED;
+ reason = zstd_is_error(zerr) ? zstd_get_error_name(zerr) :
+ "unexpected end of stream";
break;
}
} while (rq->outputsize + dctx.avail_out);
@@ -210,7 +208,7 @@ failed_zinit:
z_erofs_zstd_head = strm;
spin_unlock(&z_erofs_zstd_lock);
wake_up(&z_erofs_zstd_wq);
- return err;
+ return reason;
}
const struct z_erofs_decompressor z_erofs_zstd_decomp = {
diff --git a/fs/erofs/erofs_fs.h b/fs/erofs/erofs_fs.h
index 3d5738f80072..e24268acdd62 100644
--- a/fs/erofs/erofs_fs.h
+++ b/fs/erofs/erofs_fs.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0-only OR Apache-2.0 */
+/* SPDX-License-Identifier: MIT */
/*
* EROFS (Enhanced ROM File System) on-disk format definition
*
diff --git a/fs/erofs/fileio.c b/fs/erofs/fileio.c
index d27938435b2f..932e8b353ba1 100644
--- a/fs/erofs/fileio.c
+++ b/fs/erofs/fileio.c
@@ -34,13 +34,13 @@ static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret)
if (rq->bio.bi_end_io) {
if (ret < 0 && !rq->bio.bi_status)
rq->bio.bi_status = errno_to_blk_status(ret);
- rq->bio.bi_end_io(&rq->bio);
} else {
bio_for_each_folio_all(fi, &rq->bio) {
DBG_BUGON(folio_test_uptodate(fi.folio));
erofs_onlinefolio_end(fi.folio, ret, false);
}
}
+ bio_endio(&rq->bio);
bio_uninit(&rq->bio);
kfree(rq);
}
diff --git a/fs/erofs/fscache.c b/fs/erofs/fscache.c
index 362acf828279..7a346e20f7b7 100644
--- a/fs/erofs/fscache.c
+++ b/fs/erofs/fscache.c
@@ -185,7 +185,7 @@ static void erofs_fscache_bio_endio(void *priv, ssize_t transferred_or_error)
if (IS_ERR_VALUE(transferred_or_error))
io->bio.bi_status = errno_to_blk_status(transferred_or_error);
- io->bio.bi_end_io(&io->bio);
+ bio_endio(&io->bio);
BUILD_BUG_ON(offsetof(struct erofs_fscache_bio, io) != 0);
erofs_fscache_io_put(&io->io);
}
@@ -216,7 +216,7 @@ void erofs_fscache_submit_bio(struct bio *bio)
if (!ret)
return;
bio->bi_status = errno_to_blk_status(ret);
- bio->bi_end_io(bio);
+ bio_endio(bio);
}
static int erofs_fscache_meta_read_folio(struct file *data, struct folio *folio)
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index f3f8d8c066e4..937a215f626c 100644
--- a/fs/erofs/super.c
+++ b/fs/erofs/super.c
@@ -174,15 +174,15 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb,
if (!erofs_is_fileio_mode(sbi)) {
dif->dax_dev = fs_dax_get_by_bdev(file_bdev(file),
&dif->dax_part_off, NULL, NULL);
- if (!dif->dax_dev && test_opt(&sbi->opt, DAX_ALWAYS)) {
- erofs_info(sb, "DAX unsupported by %s. Turning off DAX.",
- dif->path);
- clear_opt(&sbi->opt, DAX_ALWAYS);
- }
} else if (!S_ISREG(file_inode(file)->i_mode)) {
fput(file);
return -EINVAL;
}
+ if (!dif->dax_dev && test_opt(&sbi->opt, DAX_ALWAYS)) {
+ erofs_info(sb, "DAX unsupported by %s. Turning off DAX.",
+ dif->path);
+ clear_opt(&sbi->opt, DAX_ALWAYS);
+ }
dif->file = file;
}
@@ -215,13 +215,13 @@ static int erofs_scan_devices(struct super_block *sb,
ondisk_extradevs, sbi->devs->extra_devices);
return -EINVAL;
}
- if (!ondisk_extradevs) {
- if (test_opt(&sbi->opt, DAX_ALWAYS) && !sbi->dif0.dax_dev) {
- erofs_info(sb, "DAX unsupported by block device. Turning off DAX.");
- clear_opt(&sbi->opt, DAX_ALWAYS);
- }
- return 0;
+
+ if (test_opt(&sbi->opt, DAX_ALWAYS) && !sbi->dif0.dax_dev) {
+ erofs_info(sb, "DAX unsupported by block device. Turning off DAX.");
+ clear_opt(&sbi->opt, DAX_ALWAYS);
}
+ if (!ondisk_extradevs)
+ return 0;
if (!sbi->devs->extra_devices && !erofs_is_fscache_mode(sb))
sbi->devs->flatdev = true;
@@ -639,6 +639,22 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
sbi->blkszbits = PAGE_SHIFT;
if (!sb->s_bdev) {
+ /*
+ * (File-backed mounts) EROFS claims it's safe to nest other
+ * fs contexts (including its own) due to self-controlled RO
+ * accesses/contexts and no side-effect changes that need to
+ * context save & restore so it can reuse the current thread
+ * context. However, it still needs to bump `s_stack_depth` to
+ * avoid kernel stack overflow from nested filesystems.
+ */
+ if (erofs_is_fileio_mode(sbi)) {
+ sb->s_stack_depth =
+ file_inode(sbi->dif0.file)->i_sb->s_stack_depth + 1;
+ if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
+ erofs_err(sb, "maximum fs stacking depth exceeded");
+ return -ENOTBLK;
+ }
+ }
sb->s_blocksize = PAGE_SIZE;
sb->s_blocksize_bits = PAGE_SHIFT;
diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
index bc80cfe482f7..461a929e0825 100644
--- a/fs/erofs/zdata.c
+++ b/fs/erofs/zdata.c
@@ -1267,12 +1267,13 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, int err)
struct erofs_sb_info *const sbi = EROFS_SB(be->sb);
struct z_erofs_pcluster *pcl = be->pcl;
unsigned int pclusterpages = z_erofs_pclusterpages(pcl);
- const struct z_erofs_decompressor *decomp =
+ const struct z_erofs_decompressor *alg =
z_erofs_decomp[pcl->algorithmformat];
+ bool try_free = true;
int i, j, jtop, err2;
struct page *page;
bool overlapped;
- bool try_free = true;
+ const char *reason;
mutex_lock(&pcl->lock);
be->nr_pages = PAGE_ALIGN(pcl->length + pcl->pageofs_out) >> PAGE_SHIFT;
@@ -1304,8 +1305,8 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, int err)
err2 = z_erofs_parse_in_bvecs(be, &overlapped);
if (err2)
err = err2;
- if (!err)
- err = decomp->decompress(&(struct z_erofs_decompress_req) {
+ if (!err) {
+ reason = alg->decompress(&(struct z_erofs_decompress_req) {
.sb = be->sb,
.in = be->compressed_pages,
.out = be->decompressed_pages,
@@ -1322,6 +1323,18 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, int err)
.gfp = pcl->besteffort ? GFP_KERNEL :
GFP_NOWAIT | __GFP_NORETRY
}, be->pagepool);
+ if (IS_ERR(reason)) {
+ erofs_err(be->sb, "failed to decompress (%s) %ld @ pa %llu size %u => %u",
+ alg->name, PTR_ERR(reason), pcl->pos,
+ pcl->pclustersize, pcl->length);
+ err = PTR_ERR(reason);
+ } else if (unlikely(reason)) {
+ erofs_err(be->sb, "failed to decompress (%s) %s @ pa %llu size %u => %u",
+ alg->name, reason, pcl->pos,
+ pcl->pclustersize, pcl->length);
+ err = -EFSCORRUPTED;
+ }
+ }
/* must handle all compressed pages before actual file pages */
if (pcl->from_meta) {
diff --git a/fs/exec.c b/fs/exec.c
index 7cb001a222d1..9d5ebc9d15b0 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1774,7 +1774,7 @@ out:
force_fatal_sig(SIGSEGV);
sched_mm_cid_after_execve(current);
- rseq_set_notify_resume(current);
+ rseq_force_update();
current->in_execve = 0;
return retval;
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
index c9329ed5c094..8040c731b3e4 100644
--- a/fs/ext4/balloc.c
+++ b/fs/ext4/balloc.c
@@ -752,7 +752,7 @@ ext4_fsblk_t ext4_new_meta_blocks(handle_t *handle, struct inode *inode,
*count = ar.len;
/*
* Account for the allocated meta blocks. We will never
- * fail EDQUOT for metdata, but we do account for it.
+ * fail EDQUOT for metadata, but we do account for it.
*/
if (!(*errp) && (flags & EXT4_MB_DELALLOC_RESERVED)) {
dquot_alloc_block_nofail(inode,
diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
index d4164c507a90..256fe2c1d4c1 100644
--- a/fs/ext4/dir.c
+++ b/fs/ext4/dir.c
@@ -192,13 +192,13 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
continue;
}
if (err > 0) {
- pgoff_t index = map.m_pblk >>
- (PAGE_SHIFT - inode->i_blkbits);
+ pgoff_t index = map.m_pblk << inode->i_blkbits >>
+ PAGE_SHIFT;
if (!ra_has_index(&file->f_ra, index))
page_cache_sync_readahead(
sb->s_bdev->bd_mapping,
- &file->f_ra, file,
- index, 1);
+ &file->f_ra, file, index,
+ 1 << EXT4_SB(sb)->s_min_folio_order);
file->f_ra.prev_pos = (loff_t)index << PAGE_SHIFT;
bh = ext4_bread(NULL, inode, map.m_lblk, 0);
if (IS_ERR(bh)) {
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 57087da6c7be..56112f201cac 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -260,6 +260,7 @@ struct ext4_map_blocks {
ext4_lblk_t m_lblk;
unsigned int m_len;
unsigned int m_flags;
+ u64 m_seq;
};
/*
@@ -367,7 +368,14 @@ struct ext4_io_submit {
blkbits))
#define EXT4_B_TO_LBLK(inode, offset) \
(round_up((offset), i_blocksize(inode)) >> (inode)->i_blkbits)
-
+#define EXT4_LBLK_TO_B(inode, lblk) ((loff_t)(lblk) << (inode)->i_blkbits)
+
+/* Translate a block number to a page index */
+#define EXT4_LBLK_TO_PG(inode, lblk) (EXT4_LBLK_TO_B((inode), (lblk)) >> \
+ PAGE_SHIFT)
+/* Translate a page index to a block number */
+#define EXT4_PG_TO_LBLK(inode, pnum) (((loff_t)(pnum) << PAGE_SHIFT) >> \
+ (inode)->i_blkbits)
/* Translate a block number to a cluster number */
#define EXT4_B2C(sbi, blk) ((blk) >> (sbi)->s_cluster_bits)
/* Translate a cluster number to a block number */
@@ -694,13 +702,22 @@ enum {
/* Caller is from the delayed allocation writeout path
* finally doing the actual allocation of delayed blocks */
#define EXT4_GET_BLOCKS_DELALLOC_RESERVE 0x0004
- /* caller is from the direct IO path, request to creation of an
- unwritten extents if not allocated, split the unwritten
- extent if blocks has been preallocated already*/
-#define EXT4_GET_BLOCKS_PRE_IO 0x0008
-#define EXT4_GET_BLOCKS_CONVERT 0x0010
-#define EXT4_GET_BLOCKS_IO_CREATE_EXT (EXT4_GET_BLOCKS_PRE_IO|\
+ /*
+ * This means that we cannot merge newly allocated extents, and if we
+ * found an unwritten extent, we need to split it.
+ */
+#define EXT4_GET_BLOCKS_SPLIT_NOMERGE 0x0008
+ /*
+ * Caller is from the dio or dioread_nolock buffered IO, reqest to
+ * create an unwritten extent if it does not exist or split the
+ * found unwritten extent. Also do not merge the newly created
+ * unwritten extent, io end will convert unwritten to written,
+ * and try to merge the written extent.
+ */
+#define EXT4_GET_BLOCKS_IO_CREATE_EXT (EXT4_GET_BLOCKS_SPLIT_NOMERGE|\
EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT)
+ /* Convert unwritten extent to initialized. */
+#define EXT4_GET_BLOCKS_CONVERT 0x0010
/* Eventual metadata allocation (due to growing extent tree)
* should not fail, so try to use reserved blocks for that.*/
#define EXT4_GET_BLOCKS_METADATA_NOFAIL 0x0020
@@ -1138,6 +1155,8 @@ struct ext4_inode_info {
ext4_lblk_t i_es_shrink_lblk; /* Offset where we start searching for
extents to shrink. Protected by
i_es_lock */
+ u64 i_es_seq; /* Change counter for extents.
+ Protected by i_es_lock */
/* ialloc */
ext4_group_t i_last_alloc_group;
@@ -1685,6 +1704,11 @@ struct ext4_sb_info {
/* record the last minlen when FITRIM is called. */
unsigned long s_last_trim_minblks;
+ /* minimum folio order of a page cache allocation */
+ u16 s_min_folio_order;
+ /* supported maximum folio order, 0 means not supported */
+ u16 s_max_folio_order;
+
/* Precomputed FS UUID checksum for seeding other checksums */
__u32 s_csum_seed;
@@ -2472,28 +2496,19 @@ static inline unsigned int ext4_dir_rec_len(__u8 name_len,
return (rec_len & ~EXT4_DIR_ROUND);
}
-/*
- * If we ever get support for fs block sizes > page_size, we'll need
- * to remove the #if statements in the next two functions...
- */
static inline unsigned int
ext4_rec_len_from_disk(__le16 dlen, unsigned blocksize)
{
unsigned len = le16_to_cpu(dlen);
-#if (PAGE_SIZE >= 65536)
if (len == EXT4_MAX_REC_LEN || len == 0)
return blocksize;
return (len & 65532) | ((len & 3) << 16);
-#else
- return len;
-#endif
}
static inline __le16 ext4_rec_len_to_disk(unsigned len, unsigned blocksize)
{
BUG_ON((len > blocksize) || (blocksize > (1 << 18)) || (len & 3));
-#if (PAGE_SIZE >= 65536)
if (len < 65536)
return cpu_to_le16(len);
if (len == blocksize) {
@@ -2503,9 +2518,6 @@ static inline __le16 ext4_rec_len_to_disk(unsigned len, unsigned blocksize)
return cpu_to_le16(0);
}
return cpu_to_le16((len & 65532) | ((len >> 16) & 3));
-#else
- return cpu_to_le16(len);
-#endif
}
/*
diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
index a0e66bc10093..05e5946ed9b3 100644
--- a/fs/ext4/ext4_jbd2.c
+++ b/fs/ext4/ext4_jbd2.c
@@ -16,8 +16,7 @@ int ext4_inode_journal_mode(struct inode *inode)
ext4_test_inode_flag(inode, EXT4_INODE_EA_INODE) ||
test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA ||
(ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA) &&
- !test_opt(inode->i_sb, DELALLOC) &&
- !mapping_large_folio_support(inode->i_mapping))) {
+ !test_opt(inode->i_sb, DELALLOC))) {
/* We do not support data journalling for encrypted data */
if (S_ISREG(inode->i_mode) && IS_ENCRYPTED(inode))
return EXT4_INODE_ORDERED_DATA_MODE; /* ordered */
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index ca5499e9412b..2cf5759ba689 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -333,7 +333,7 @@ ext4_force_split_extent_at(handle_t *handle, struct inode *inode,
int nofail)
{
int unwritten = ext4_ext_is_unwritten(path[path->p_depth].p_ext);
- int flags = EXT4_EX_NOCACHE | EXT4_GET_BLOCKS_PRE_IO;
+ int flags = EXT4_EX_NOCACHE | EXT4_GET_BLOCKS_SPLIT_NOMERGE;
if (nofail)
flags |= EXT4_GET_BLOCKS_METADATA_NOFAIL | EXT4_EX_NOFAIL;
@@ -2002,7 +2002,7 @@ ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
}
/* try to insert block into found extent and return */
- if (ex && !(gb_flags & EXT4_GET_BLOCKS_PRE_IO)) {
+ if (ex && !(gb_flags & EXT4_GET_BLOCKS_SPLIT_NOMERGE)) {
/*
* Try to see whether we should rather test the extent on
@@ -2181,7 +2181,7 @@ has_space:
merge:
/* try to merge extents */
- if (!(gb_flags & EXT4_GET_BLOCKS_PRE_IO))
+ if (!(gb_flags & EXT4_GET_BLOCKS_SPLIT_NOMERGE))
ext4_ext_try_to_merge(handle, inode, path, nearex);
/* time to correct all indexes above */
@@ -2213,7 +2213,7 @@ static int ext4_fill_es_cache_info(struct inode *inode,
while (block <= end) {
next = 0;
flags = 0;
- if (!ext4_es_lookup_extent(inode, block, &next, &es))
+ if (!ext4_es_lookup_extent(inode, block, &next, &es, NULL))
break;
if (ext4_es_is_unwritten(&es))
flags |= FIEMAP_EXTENT_UNWRITTEN;
@@ -3224,7 +3224,7 @@ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle,
else
ext4_ext_mark_initialized(ex);
- if (!(flags & EXT4_GET_BLOCKS_PRE_IO))
+ if (!(flags & EXT4_GET_BLOCKS_SPLIT_NOMERGE))
ext4_ext_try_to_merge(handle, inode, path, ex);
err = ext4_ext_dirty(handle, inode, path + path->p_depth);
@@ -3368,7 +3368,7 @@ static struct ext4_ext_path *ext4_split_extent(handle_t *handle,
if (map->m_lblk + map->m_len < ee_block + ee_len) {
split_flag1 = split_flag & EXT4_EXT_MAY_ZEROOUT;
- flags1 = flags | EXT4_GET_BLOCKS_PRE_IO;
+ flags1 = flags | EXT4_GET_BLOCKS_SPLIT_NOMERGE;
if (unwritten)
split_flag1 |= EXT4_EXT_MARK_UNWRIT1 |
EXT4_EXT_MARK_UNWRIT2;
@@ -3721,10 +3721,6 @@ static struct ext4_ext_path *ext4_split_convert_extents(handle_t *handle,
>> inode->i_sb->s_blocksize_bits;
if (eof_block < map->m_lblk + map->m_len)
eof_block = map->m_lblk + map->m_len;
- /*
- * It is safe to convert extent to initialized via explicit
- * zeroout only if extent is fully inside i_size or new_size.
- */
depth = ext_depth(inode);
ex = path[depth].p_ext;
ee_block = le32_to_cpu(ex->ee_block);
@@ -3735,11 +3731,15 @@ static struct ext4_ext_path *ext4_split_convert_extents(handle_t *handle,
split_flag |= EXT4_EXT_DATA_VALID1;
/* Convert to initialized */
} else if (flags & EXT4_GET_BLOCKS_CONVERT) {
+ /*
+ * It is safe to convert extent to initialized via explicit
+ * zeroout only if extent is fully inside i_size or new_size.
+ */
split_flag |= ee_block + ee_len <= eof_block ?
EXT4_EXT_MAY_ZEROOUT : 0;
split_flag |= (EXT4_EXT_MARK_UNWRIT2 | EXT4_EXT_DATA_VALID2);
}
- flags |= EXT4_GET_BLOCKS_PRE_IO;
+ flags |= EXT4_GET_BLOCKS_SPLIT_NOMERGE;
return ext4_split_extent(handle, inode, path, map, split_flag, flags,
allocated);
}
@@ -3911,7 +3911,7 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode,
*allocated, newblock);
/* get_block() before submitting IO, split the extent */
- if (flags & EXT4_GET_BLOCKS_PRE_IO) {
+ if (flags & EXT4_GET_BLOCKS_SPLIT_NOMERGE) {
path = ext4_split_convert_extents(handle, inode, map, path,
flags | EXT4_GET_BLOCKS_CONVERT, allocated);
if (IS_ERR(path))
@@ -4562,7 +4562,7 @@ retry:
* allow a full retry cycle for any remaining allocations
*/
retries = 0;
- epos = (loff_t)(map.m_lblk + ret) << blkbits;
+ epos = EXT4_LBLK_TO_B(inode, map.m_lblk + ret);
inode_set_ctime_current(inode);
if (new_size) {
if (epos > new_size)
@@ -5618,7 +5618,7 @@ static int ext4_insert_range(struct file *file, loff_t offset, loff_t len)
path = ext4_split_extent_at(handle, inode, path,
start_lblk, split_flag,
EXT4_EX_NOCACHE |
- EXT4_GET_BLOCKS_PRE_IO |
+ EXT4_GET_BLOCKS_SPLIT_NOMERGE |
EXT4_GET_BLOCKS_METADATA_NOFAIL);
}
diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c
index 31dc0496f8d0..e04fbf10fe4f 100644
--- a/fs/ext4/extents_status.c
+++ b/fs/ext4/extents_status.c
@@ -235,6 +235,13 @@ static inline ext4_lblk_t ext4_es_end(struct extent_status *es)
return es->es_lblk + es->es_len - 1;
}
+static inline void ext4_es_inc_seq(struct inode *inode)
+{
+ struct ext4_inode_info *ei = EXT4_I(inode);
+
+ WRITE_ONCE(ei->i_es_seq, ei->i_es_seq + 1);
+}
+
/*
* search through the tree for an delayed extent with a given offset. If
* it can't be found, try to find next extent.
@@ -906,7 +913,6 @@ void ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk,
newes.es_lblk = lblk;
newes.es_len = len;
ext4_es_store_pblock_status(&newes, pblk, status);
- trace_ext4_es_insert_extent(inode, &newes);
ext4_es_insert_extent_check(inode, &newes);
@@ -955,6 +961,11 @@ retry:
}
pending = err3;
}
+ /*
+ * TODO: For cache on-disk extents, there is no need to increment
+ * the sequence counter, this requires future optimization.
+ */
+ ext4_es_inc_seq(inode);
error:
write_unlock(&EXT4_I(inode)->i_es_lock);
/*
@@ -981,6 +992,7 @@ error:
if (err1 || err2 || err3 < 0)
goto retry;
+ trace_ext4_es_insert_extent(inode, &newes);
ext4_es_print_tree(inode);
return;
}
@@ -1027,8 +1039,8 @@ void ext4_es_cache_extent(struct inode *inode, ext4_lblk_t lblk,
* Return: 1 on found, 0 on not
*/
int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,
- ext4_lblk_t *next_lblk,
- struct extent_status *es)
+ ext4_lblk_t *next_lblk, struct extent_status *es,
+ u64 *pseq)
{
struct ext4_es_tree *tree;
struct ext4_es_stats *stats;
@@ -1087,6 +1099,8 @@ out:
} else
*next_lblk = 0;
}
+ if (pseq)
+ *pseq = EXT4_I(inode)->i_es_seq;
} else {
percpu_counter_inc(&stats->es_stats_cache_misses);
}
@@ -1550,7 +1564,6 @@ void ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
if (EXT4_SB(inode->i_sb)->s_mount_state & EXT4_FC_REPLAY)
return;
- trace_ext4_es_remove_extent(inode, lblk, len);
es_debug("remove [%u/%u) from extent status tree of inode %lu\n",
lblk, len, inode->i_ino);
@@ -1570,16 +1583,21 @@ retry:
*/
write_lock(&EXT4_I(inode)->i_es_lock);
err = __es_remove_extent(inode, lblk, end, &reserved, es);
+ if (err)
+ goto error;
/* Free preallocated extent if it didn't get used. */
if (es) {
if (!es->es_len)
__es_free_extent(es);
es = NULL;
}
+ ext4_es_inc_seq(inode);
+error:
write_unlock(&EXT4_I(inode)->i_es_lock);
if (err)
goto retry;
+ trace_ext4_es_remove_extent(inode, lblk, len);
ext4_es_print_tree(inode);
ext4_da_release_space(inode, reserved);
}
@@ -2140,8 +2158,6 @@ void ext4_es_insert_delayed_extent(struct inode *inode, ext4_lblk_t lblk,
newes.es_lblk = lblk;
newes.es_len = len;
ext4_es_store_pblock_status(&newes, ~0, EXTENT_STATUS_DELAYED);
- trace_ext4_es_insert_delayed_extent(inode, &newes, lclu_allocated,
- end_allocated);
ext4_es_insert_extent_check(inode, &newes);
@@ -2196,11 +2212,14 @@ retry:
pr2 = NULL;
}
}
+ ext4_es_inc_seq(inode);
error:
write_unlock(&EXT4_I(inode)->i_es_lock);
if (err1 || err2 || err3 < 0)
goto retry;
+ trace_ext4_es_insert_delayed_extent(inode, &newes, lclu_allocated,
+ end_allocated);
ext4_es_print_tree(inode);
ext4_print_pending_tree(inode);
return;
diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h
index 8f9c008d11e8..f3396cf32b44 100644
--- a/fs/ext4/extents_status.h
+++ b/fs/ext4/extents_status.h
@@ -148,7 +148,7 @@ extern void ext4_es_find_extent_range(struct inode *inode,
struct extent_status *es);
extern int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,
ext4_lblk_t *next_lblk,
- struct extent_status *es);
+ struct extent_status *es, u64 *pseq);
extern bool ext4_es_scan_range(struct inode *inode,
int (*matching_fn)(struct extent_status *es),
ext4_lblk_t lblk, ext4_lblk_t end);
diff --git a/fs/ext4/hash.c b/fs/ext4/hash.c
index 33cd5b6b02d5..48483cd015d3 100644
--- a/fs/ext4/hash.c
+++ b/fs/ext4/hash.c
@@ -268,7 +268,7 @@ static int __ext4fs_dirhash(const struct inode *dir, const char *name, int len,
combined_hash = fscrypt_fname_siphash(dir, &qname);
} else {
ext4_warning_inode(dir, "Siphash requires key");
- return -1;
+ return -EINVAL;
}
hash = (__u32)(combined_hash >> 32);
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index ba4fd9aba1c1..b20a1bf866ab 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -1293,7 +1293,6 @@ got:
ei->i_csum_seed = ext4_chksum(csum, (__u8 *)&gen, sizeof(gen));
}
- ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */
ext4_set_inode_state(inode, EXT4_STATE_NEW);
ei->i_extra_isize = sbi->s_want_extra_isize;
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 1b094a4f3866..1f6bc05593df 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -418,7 +418,12 @@ static int ext4_prepare_inline_data(handle_t *handle, struct inode *inode,
return -ENOSPC;
ext4_write_lock_xattr(inode, &no_expand);
-
+ /*
+ * ei->i_inline_size may have changed since the initial check
+ * if other xattrs were added. Recalculate to ensure
+ * ext4_update_inline_data() validates against current capacity.
+ */
+ (void) ext4_find_inline_data_nolock(inode);
if (ei->i_inline_off)
ret = ext4_update_inline_data(handle, inode, len);
else
@@ -446,9 +451,13 @@ static int ext4_destroy_inline_data_nolock(handle_t *handle,
if (!ei->i_inline_off)
return 0;
+ down_write(&ei->i_data_sem);
+
error = ext4_get_inode_loc(inode, &is.iloc);
- if (error)
+ if (error) {
+ up_write(&ei->i_data_sem);
return error;
+ }
error = ext4_xattr_ibody_find(inode, &i, &is);
if (error)
@@ -487,6 +496,7 @@ out:
brelse(is.iloc.bh);
if (error == -ENODATA)
error = 0;
+ up_write(&ei->i_data_sem);
return error;
}
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 78ea864fa8cd..0c466ccbed69 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -549,10 +549,13 @@ static int ext4_map_query_blocks(handle_t *handle, struct inode *inode,
retval = ext4_ext_map_blocks(handle, inode, map, flags);
else
retval = ext4_ind_map_blocks(handle, inode, map, flags);
-
- if (retval <= 0)
+ if (retval < 0)
return retval;
+ /* A hole? */
+ if (retval == 0)
+ goto out;
+
if (unlikely(retval != map->m_len)) {
ext4_warning(inode->i_sb,
"ES len assertion failed for inode "
@@ -572,11 +575,13 @@ static int ext4_map_query_blocks(handle_t *handle, struct inode *inode,
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
map->m_pblk, status, false);
- return retval;
+ } else {
+ retval = ext4_map_query_blocks_next_in_leaf(handle, inode, map,
+ orig_mlen);
}
-
- return ext4_map_query_blocks_next_in_leaf(handle, inode, map,
- orig_mlen);
+out:
+ map->m_seq = READ_ONCE(EXT4_I(inode)->i_es_seq);
+ return retval;
}
static int ext4_map_create_blocks(handle_t *handle, struct inode *inode,
@@ -647,8 +652,8 @@ static int ext4_map_create_blocks(handle_t *handle, struct inode *inode,
* If the extent has been zeroed out, we don't need to update
* extent status tree.
*/
- if (flags & EXT4_GET_BLOCKS_PRE_IO &&
- ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es)) {
+ if (flags & EXT4_GET_BLOCKS_SPLIT_NOMERGE &&
+ ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es, &map->m_seq)) {
if (ext4_es_is_written(&es))
return retval;
}
@@ -657,6 +662,7 @@ static int ext4_map_create_blocks(handle_t *handle, struct inode *inode,
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
ext4_es_insert_extent(inode, map->m_lblk, map->m_len, map->m_pblk,
status, flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE);
+ map->m_seq = READ_ONCE(EXT4_I(inode)->i_es_seq);
return retval;
}
@@ -722,7 +728,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
ext4_check_map_extents_env(inode);
/* Lookup extent status tree firstly */
- if (ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es)) {
+ if (ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es, &map->m_seq)) {
if (ext4_es_is_written(&es) || ext4_es_is_unwritten(&es)) {
map->m_pblk = ext4_es_pblock(&es) +
map->m_lblk - es.es_lblk;
@@ -809,7 +815,13 @@ found:
down_write(&EXT4_I(inode)->i_data_sem);
retval = ext4_map_create_blocks(handle, inode, map, flags);
up_write((&EXT4_I(inode)->i_data_sem));
- if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
+
+ if (retval < 0)
+ ext_debug(inode, "failed with err %d\n", retval);
+ if (retval <= 0)
+ return retval;
+
+ if (map->m_flags & EXT4_MAP_MAPPED) {
ret = check_block_validity(inode, map);
if (ret != 0)
return ret;
@@ -824,9 +836,8 @@ found:
!(flags & EXT4_GET_BLOCKS_ZERO) &&
!ext4_is_quota_file(inode) &&
ext4_should_order_data(inode)) {
- loff_t start_byte =
- (loff_t)map->m_lblk << inode->i_blkbits;
- loff_t length = (loff_t)map->m_len << inode->i_blkbits;
+ loff_t start_byte = EXT4_LBLK_TO_B(inode, map->m_lblk);
+ loff_t length = EXT4_LBLK_TO_B(inode, map->m_len);
if (flags & EXT4_GET_BLOCKS_IO_SUBMIT)
ret = ext4_jbd2_inode_add_wait(handle, inode,
@@ -838,12 +849,8 @@ found:
return ret;
}
}
- if (retval > 0 && (map->m_flags & EXT4_MAP_UNWRITTEN ||
- map->m_flags & EXT4_MAP_MAPPED))
- ext4_fc_track_range(handle, inode, map->m_lblk,
- map->m_lblk + map->m_len - 1);
- if (retval < 0)
- ext_debug(inode, "failed with err %d\n", retval);
+ ext4_fc_track_range(handle, inode, map->m_lblk, map->m_lblk +
+ map->m_len - 1);
return retval;
}
@@ -1162,8 +1169,7 @@ int ext4_block_write_begin(handle_t *handle, struct folio *folio,
unsigned block_start, block_end;
sector_t block;
int err = 0;
- unsigned blocksize = inode->i_sb->s_blocksize;
- unsigned bbits;
+ unsigned int blocksize = i_blocksize(inode);
struct buffer_head *bh, *head, *wait[2];
int nr_wait = 0;
int i;
@@ -1172,12 +1178,12 @@ int ext4_block_write_begin(handle_t *handle, struct folio *folio,
BUG_ON(!folio_test_locked(folio));
BUG_ON(to > folio_size(folio));
BUG_ON(from > to);
+ WARN_ON_ONCE(blocksize > folio_size(folio));
head = folio_buffers(folio);
if (!head)
head = create_empty_buffers(folio, blocksize, 0);
- bbits = ilog2(blocksize);
- block = (sector_t)folio->index << (PAGE_SHIFT - bbits);
+ block = EXT4_PG_TO_LBLK(inode, folio->index);
for (bh = head, block_start = 0; bh != head || !block_start;
block++, block_start = block_end, bh = bh->b_this_page) {
@@ -1907,7 +1913,7 @@ static int ext4_da_map_blocks(struct inode *inode, struct ext4_map_blocks *map)
ext4_check_map_extents_env(inode);
/* Lookup extent status tree firstly */
- if (ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es)) {
+ if (ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es, NULL)) {
map->m_len = min_t(unsigned int, map->m_len,
es.es_len - (map->m_lblk - es.es_lblk));
@@ -1960,7 +1966,7 @@ add_delayed:
* is held in write mode, before inserting a new da entry in
* the extent status tree.
*/
- if (ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es)) {
+ if (ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es, NULL)) {
map->m_len = min_t(unsigned int, map->m_len,
es.es_len - (map->m_lblk - es.es_lblk));
@@ -1978,6 +1984,8 @@ add_delayed:
map->m_flags |= EXT4_MAP_DELAYED;
retval = ext4_insert_delayed_blocks(inode, map->m_lblk, map->m_len);
+ if (!retval)
+ map->m_seq = READ_ONCE(EXT4_I(inode)->i_es_seq);
up_write(&EXT4_I(inode)->i_data_sem);
return retval;
@@ -2224,7 +2232,6 @@ static int mpage_process_folio(struct mpage_da_data *mpd, struct folio *folio,
ext4_lblk_t lblk = *m_lblk;
ext4_fsblk_t pblock = *m_pblk;
int err = 0;
- int blkbits = mpd->inode->i_blkbits;
ssize_t io_end_size = 0;
struct ext4_io_end_vec *io_end_vec = ext4_last_io_end_vec(io_end);
@@ -2250,7 +2257,8 @@ static int mpage_process_folio(struct mpage_da_data *mpd, struct folio *folio,
err = PTR_ERR(io_end_vec);
goto out;
}
- io_end_vec->offset = (loff_t)mpd->map.m_lblk << blkbits;
+ io_end_vec->offset = EXT4_LBLK_TO_B(mpd->inode,
+ mpd->map.m_lblk);
}
*map_bh = true;
goto out;
@@ -2260,7 +2268,7 @@ static int mpage_process_folio(struct mpage_da_data *mpd, struct folio *folio,
bh->b_blocknr = pblock++;
}
clear_buffer_unwritten(bh);
- io_end_size += (1 << blkbits);
+ io_end_size += i_blocksize(mpd->inode);
} while (lblk++, (bh = bh->b_this_page) != head);
io_end_vec->size += io_end_size;
@@ -2290,15 +2298,14 @@ static int mpage_map_and_submit_buffers(struct mpage_da_data *mpd)
struct folio_batch fbatch;
unsigned nr, i;
struct inode *inode = mpd->inode;
- int bpp_bits = PAGE_SHIFT - inode->i_blkbits;
pgoff_t start, end;
ext4_lblk_t lblk;
ext4_fsblk_t pblock;
int err;
bool map_bh = false;
- start = mpd->map.m_lblk >> bpp_bits;
- end = (mpd->map.m_lblk + mpd->map.m_len - 1) >> bpp_bits;
+ start = EXT4_LBLK_TO_PG(inode, mpd->map.m_lblk);
+ end = EXT4_LBLK_TO_PG(inode, mpd->map.m_lblk + mpd->map.m_len - 1);
pblock = mpd->map.m_pblk;
folio_batch_init(&fbatch);
@@ -2309,7 +2316,7 @@ static int mpage_map_and_submit_buffers(struct mpage_da_data *mpd)
for (i = 0; i < nr; i++) {
struct folio *folio = fbatch.folios[i];
- lblk = folio->index << bpp_bits;
+ lblk = EXT4_PG_TO_LBLK(inode, folio->index);
err = mpage_process_folio(mpd, folio, &lblk, &pblock,
&map_bh);
/*
@@ -2462,7 +2469,7 @@ static int mpage_map_and_submit_extent(handle_t *handle,
io_end_vec = ext4_alloc_io_end_vec(io_end);
if (IS_ERR(io_end_vec))
return PTR_ERR(io_end_vec);
- io_end_vec->offset = ((loff_t)map->m_lblk) << inode->i_blkbits;
+ io_end_vec->offset = EXT4_LBLK_TO_B(inode, map->m_lblk);
do {
err = mpage_map_one_extent(handle, mpd);
if (err < 0) {
@@ -2612,7 +2619,6 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd)
pgoff_t end = mpd->end_pos >> PAGE_SHIFT;
xa_mark_t tag;
int i, err = 0;
- int blkbits = mpd->inode->i_blkbits;
ext4_lblk_t lblk;
struct buffer_head *head;
handle_t *handle = NULL;
@@ -2648,7 +2654,7 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd)
*/
if (mpd->wbc->sync_mode == WB_SYNC_NONE &&
mpd->wbc->nr_to_write <=
- mpd->map.m_len >> (PAGE_SHIFT - blkbits))
+ EXT4_LBLK_TO_PG(mpd->inode, mpd->map.m_len))
goto out;
/* If we can't merge this page, we are done. */
@@ -2726,8 +2732,7 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd)
mpage_folio_done(mpd, folio);
} else {
/* Add all dirty buffers to mpd */
- lblk = ((ext4_lblk_t)folio->index) <<
- (PAGE_SHIFT - blkbits);
+ lblk = EXT4_PG_TO_LBLK(mpd->inode, folio->index);
head = folio_buffers(folio);
err = mpage_process_page_bufs(mpd, head, head,
lblk);
@@ -3499,8 +3504,8 @@ static void ext4_set_iomap(struct inode *inode, struct iomap *iomap,
iomap->dax_dev = EXT4_SB(inode->i_sb)->s_daxdev;
else
iomap->bdev = inode->i_sb->s_bdev;
- iomap->offset = (u64) map->m_lblk << blkbits;
- iomap->length = (u64) map->m_len << blkbits;
+ iomap->offset = EXT4_LBLK_TO_B(inode, map->m_lblk);
+ iomap->length = EXT4_LBLK_TO_B(inode, map->m_len);
if ((map->m_flags & EXT4_MAP_MAPPED) &&
!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
@@ -3540,7 +3545,7 @@ static int ext4_map_blocks_atomic_write_slow(handle_t *handle,
ext4_lblk_t m_lblk = map->m_lblk;
unsigned int m_len = map->m_len;
unsigned int mapped_len = 0, m_flags = 0;
- ext4_fsblk_t next_pblk;
+ ext4_fsblk_t next_pblk = 0;
bool check_next_pblk = false;
int ret = 0;
@@ -3674,7 +3679,6 @@ static int ext4_iomap_alloc(struct inode *inode, struct ext4_map_blocks *map,
unsigned int flags)
{
handle_t *handle;
- u8 blkbits = inode->i_blkbits;
int ret, dio_credits, m_flags = 0, retries = 0;
bool force_commit = false;
@@ -3733,7 +3737,7 @@ retry:
* i_disksize out to i_size. This could be beyond where direct I/O is
* happening and thus expose allocated blocks to direct I/O reads.
*/
- else if (((loff_t)map->m_lblk << blkbits) >= i_size_read(inode))
+ else if (EXT4_LBLK_TO_B(inode, map->m_lblk) >= i_size_read(inode))
m_flags = EXT4_GET_BLOCKS_CREATE;
else if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
m_flags = EXT4_GET_BLOCKS_IO_CREATE_EXT;
@@ -4068,7 +4072,7 @@ static int __ext4_block_zero_page_range(handle_t *handle,
blocksize = inode->i_sb->s_blocksize;
- iblock = folio->index << (PAGE_SHIFT - inode->i_sb->s_blocksize_bits);
+ iblock = EXT4_PG_TO_LBLK(inode, folio->index);
bh = folio_buffers(folio);
if (!bh)
@@ -4153,9 +4157,8 @@ static int ext4_block_zero_page_range(handle_t *handle,
struct address_space *mapping, loff_t from, loff_t length)
{
struct inode *inode = mapping->host;
- unsigned offset = from & (PAGE_SIZE-1);
unsigned blocksize = inode->i_sb->s_blocksize;
- unsigned max = blocksize - (offset & (blocksize - 1));
+ unsigned int max = blocksize - (from & (blocksize - 1));
/*
* correct length if it does not fall between
@@ -4180,7 +4183,6 @@ static int ext4_block_zero_page_range(handle_t *handle,
static int ext4_block_truncate_page(handle_t *handle,
struct address_space *mapping, loff_t from)
{
- unsigned offset = from & (PAGE_SIZE-1);
unsigned length;
unsigned blocksize;
struct inode *inode = mapping->host;
@@ -4189,8 +4191,8 @@ static int ext4_block_truncate_page(handle_t *handle,
if (IS_ENCRYPTED(inode) && !fscrypt_has_encryption_key(inode))
return 0;
- blocksize = inode->i_sb->s_blocksize;
- length = blocksize - (offset & (blocksize - 1));
+ blocksize = i_blocksize(inode);
+ length = blocksize - (from & (blocksize - 1));
return ext4_block_zero_page_range(handle, mapping, from, length);
}
@@ -4396,10 +4398,10 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
/*
* If the hole extends beyond i_size, set the hole to end after
- * the page that contains i_size.
+ * the block that contains i_size to save pointless tail block zeroing.
*/
- if (end > inode->i_size)
- end = round_up(inode->i_size, PAGE_SIZE);
+ if (end >= inode->i_size)
+ end = round_up(inode->i_size, sb->s_blocksize);
if (end > max_end)
end = max_end;
length = end - offset;
@@ -5142,37 +5144,23 @@ error:
return -EFSCORRUPTED;
}
-static bool ext4_should_enable_large_folio(struct inode *inode)
+void ext4_set_inode_mapping_order(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
+ u16 min_order, max_order;
- if (!S_ISREG(inode->i_mode))
- return false;
- if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA ||
- ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA))
- return false;
- if (ext4_has_feature_verity(sb))
- return false;
- if (ext4_has_feature_encrypt(sb))
- return false;
-
- return true;
-}
+ max_order = EXT4_SB(sb)->s_max_folio_order;
+ if (!max_order)
+ return;
-/*
- * Limit the maximum folio order to 2048 blocks to prevent overestimation
- * of reserve handle credits during the folio writeback in environments
- * where the PAGE_SIZE exceeds 4KB.
- */
-#define EXT4_MAX_PAGECACHE_ORDER(i) \
- umin(MAX_PAGECACHE_ORDER, (11 + (i)->i_blkbits - PAGE_SHIFT))
-void ext4_set_inode_mapping_order(struct inode *inode)
-{
- if (!ext4_should_enable_large_folio(inode))
+ min_order = EXT4_SB(sb)->s_min_folio_order;
+ if (!min_order && !S_ISREG(inode->i_mode))
return;
- mapping_set_folio_order_range(inode->i_mapping, 0,
- EXT4_MAX_PAGECACHE_ORDER(inode));
+ if (ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA))
+ max_order = min_order;
+
+ mapping_set_folio_order_range(inode->i_mapping, min_order, max_order);
}
struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
@@ -5284,7 +5272,6 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
ei->i_projid = make_kprojid(&init_user_ns, i_projid);
set_nlink(inode, le16_to_cpu(raw_inode->i_links_count));
- ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */
ei->i_inline_off = 0;
ei->i_dir_start_lookup = 0;
ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);
@@ -5517,7 +5504,9 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
if (ret)
goto bad_inode;
brelse(iloc.bh);
-
+ /* Initialize the "no ACL's" state for the simple cases */
+ if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR) && !ei->i_file_acl)
+ cache_no_acl(inode);
unlock_new_inode(inode);
return inode;
@@ -6548,14 +6537,14 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val)
* dirty data which can be converted only after flushing the dirty
* data (and journalled aops don't know how to handle these cases).
*/
- if (val) {
- filemap_invalidate_lock(inode->i_mapping);
- err = filemap_write_and_wait(inode->i_mapping);
- if (err < 0) {
- filemap_invalidate_unlock(inode->i_mapping);
- return err;
- }
+ filemap_invalidate_lock(inode->i_mapping);
+ err = filemap_write_and_wait(inode->i_mapping);
+ if (err < 0) {
+ filemap_invalidate_unlock(inode->i_mapping);
+ return err;
}
+ /* Before switch the inode journalling mode evict all the page cache. */
+ truncate_pagecache(inode, 0);
alloc_ctx = ext4_writepages_down_write(inode->i_sb);
jbd2_journal_lock_updates(journal);
@@ -6575,17 +6564,17 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val)
if (err < 0) {
jbd2_journal_unlock_updates(journal);
ext4_writepages_up_write(inode->i_sb, alloc_ctx);
+ filemap_invalidate_unlock(inode->i_mapping);
return err;
}
ext4_clear_inode_flag(inode, EXT4_INODE_JOURNAL_DATA);
}
ext4_set_aops(inode);
+ ext4_set_inode_mapping_order(inode);
jbd2_journal_unlock_updates(journal);
ext4_writepages_up_write(inode->i_sb, alloc_ctx);
-
- if (val)
- filemap_invalidate_unlock(inode->i_mapping);
+ filemap_invalidate_unlock(inode->i_mapping);
/* Finally we can mark the inode as dirty. */
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index a93a7baae990..7ce0fc40aec2 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -1394,6 +1394,10 @@ static int ext4_ioctl_set_tune_sb(struct file *filp,
if (copy_from_user(&params, in, sizeof(params)))
return -EFAULT;
+ if (strnlen(params.mount_opts, sizeof(params.mount_opts)) ==
+ sizeof(params.mount_opts))
+ return -E2BIG;
+
if ((params.set_flags & ~TUNE_OPS_SUPPORTED) != 0)
return -EOPNOTSUPP;
@@ -1641,16 +1645,6 @@ group_extend_out:
if (!(fd_file(donor)->f_mode & FMODE_WRITE))
return -EBADF;
- if (ext4_has_feature_bigalloc(sb)) {
- ext4_msg(sb, KERN_ERR,
- "Online defrag not supported with bigalloc");
- return -EOPNOTSUPP;
- } else if (IS_DAX(inode)) {
- ext4_msg(sb, KERN_ERR,
- "Online defrag not supported with DAX");
- return -EOPNOTSUPP;
- }
-
err = mnt_want_write_file(filp);
if (err)
return err;
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index 9087183602e4..56d50fd3310b 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -98,14 +98,14 @@
* block bitmap and buddy information. The information are stored in the
* inode as:
*
- * { page }
+ * { folio }
* [ group 0 bitmap][ group 0 buddy] [group 1][ group 1]...
*
*
* one block each for bitmap and buddy information. So for each group we
- * take up 2 blocks. A page can contain blocks_per_page (PAGE_SIZE /
- * blocksize) blocks. So it can have information regarding groups_per_page
- * which is blocks_per_page/2
+ * take up 2 blocks. A folio can contain blocks_per_folio (folio_size /
+ * blocksize) blocks. So it can have information regarding groups_per_folio
+ * which is blocks_per_folio/2
*
* The buddy cache inode is not stored on disk. The inode is thrown
* away when the filesystem is unmounted.
@@ -682,6 +682,24 @@ do { \
} \
} while (0)
+/*
+ * Perform buddy integrity check with the following steps:
+ *
+ * 1. Top-down validation (from highest order down to order 1, excluding order-0 bitmap):
+ * For each pair of adjacent orders, if a higher-order bit is set (indicating a free block),
+ * at most one of the two corresponding lower-order bits may be clear (free).
+ *
+ * 2. Order-0 (bitmap) validation, performed on bit pairs:
+ * - If either bit in a pair is set (1, allocated), then all corresponding higher-order bits
+ * must not be free (0).
+ * - If both bits in a pair are clear (0, free), then exactly one of the corresponding
+ * higher-order bits must be free (0).
+ *
+ * 3. Preallocation (pa) list validation:
+ * For each preallocated block (pa) in the group:
+ * - Verify that pa_pstart falls within the bounds of this block group.
+ * - Ensure the corresponding bit(s) in the order-0 bitmap are marked as allocated (1).
+ */
static void __mb_check_buddy(struct ext4_buddy *e4b, char *file,
const char *function, int line)
{
@@ -723,15 +741,6 @@ static void __mb_check_buddy(struct ext4_buddy *e4b, char *file,
continue;
}
- /* both bits in buddy2 must be 1 */
- MB_CHECK_ASSERT(mb_test_bit(i << 1, buddy2));
- MB_CHECK_ASSERT(mb_test_bit((i << 1) + 1, buddy2));
-
- for (j = 0; j < (1 << order); j++) {
- k = (i * (1 << order)) + j;
- MB_CHECK_ASSERT(
- !mb_test_bit(k, e4b->bd_bitmap));
- }
count++;
}
MB_CHECK_ASSERT(e4b->bd_info->bb_counters[order] == count);
@@ -747,15 +756,21 @@ static void __mb_check_buddy(struct ext4_buddy *e4b, char *file,
fragments++;
fstart = i;
}
- continue;
+ } else {
+ fstart = -1;
}
- fstart = -1;
- /* check used bits only */
- for (j = 0; j < e4b->bd_blkbits + 1; j++) {
- buddy2 = mb_find_buddy(e4b, j, &max2);
- k = i >> j;
- MB_CHECK_ASSERT(k < max2);
- MB_CHECK_ASSERT(mb_test_bit(k, buddy2));
+ if (!(i & 1)) {
+ int in_use, zero_bit_count = 0;
+
+ in_use = mb_test_bit(i, buddy) || mb_test_bit(i + 1, buddy);
+ for (j = 1; j < e4b->bd_blkbits + 2; j++) {
+ buddy2 = mb_find_buddy(e4b, j, &max2);
+ k = i >> j;
+ MB_CHECK_ASSERT(k < max2);
+ if (!mb_test_bit(k, buddy2))
+ zero_bit_count++;
+ }
+ MB_CHECK_ASSERT(zero_bit_count == !in_use);
}
}
MB_CHECK_ASSERT(!EXT4_MB_GRP_NEED_INIT(e4b->bd_info));
@@ -768,6 +783,8 @@ static void __mb_check_buddy(struct ext4_buddy *e4b, char *file,
ext4_group_t groupnr;
struct ext4_prealloc_space *pa;
pa = list_entry(cur, struct ext4_prealloc_space, pa_group_list);
+ if (!pa->pa_len)
+ continue;
ext4_get_group_no_and_offset(sb, pa->pa_pstart, &groupnr, &k);
MB_CHECK_ASSERT(groupnr == e4b->bd_group);
for (i = 0; i < pa->pa_len; i++)
@@ -1329,26 +1346,25 @@ static void mb_regenerate_buddy(struct ext4_buddy *e4b)
* block bitmap and buddy information. The information are
* stored in the inode as
*
- * { page }
+ * { folio }
* [ group 0 bitmap][ group 0 buddy] [group 1][ group 1]...
*
*
* one block each for bitmap and buddy information.
- * So for each group we take up 2 blocks. A page can
- * contain blocks_per_page (PAGE_SIZE / blocksize) blocks.
- * So it can have information regarding groups_per_page which
- * is blocks_per_page/2
+ * So for each group we take up 2 blocks. A folio can
+ * contain blocks_per_folio (folio_size / blocksize) blocks.
+ * So it can have information regarding groups_per_folio which
+ * is blocks_per_folio/2
*
* Locking note: This routine takes the block group lock of all groups
- * for this page; do not hold this lock when calling this routine!
+ * for this folio; do not hold this lock when calling this routine!
*/
-
static int ext4_mb_init_cache(struct folio *folio, char *incore, gfp_t gfp)
{
ext4_group_t ngroups;
unsigned int blocksize;
- int blocks_per_page;
- int groups_per_page;
+ int blocks_per_folio;
+ int groups_per_folio;
int err = 0;
int i;
ext4_group_t first_group, group;
@@ -1365,27 +1381,24 @@ static int ext4_mb_init_cache(struct folio *folio, char *incore, gfp_t gfp)
sb = inode->i_sb;
ngroups = ext4_get_groups_count(sb);
blocksize = i_blocksize(inode);
- blocks_per_page = PAGE_SIZE / blocksize;
+ blocks_per_folio = folio_size(folio) / blocksize;
+ WARN_ON_ONCE(!blocks_per_folio);
+ groups_per_folio = DIV_ROUND_UP(blocks_per_folio, 2);
mb_debug(sb, "init folio %lu\n", folio->index);
- groups_per_page = blocks_per_page >> 1;
- if (groups_per_page == 0)
- groups_per_page = 1;
-
/* allocate buffer_heads to read bitmaps */
- if (groups_per_page > 1) {
- i = sizeof(struct buffer_head *) * groups_per_page;
+ if (groups_per_folio > 1) {
+ i = sizeof(struct buffer_head *) * groups_per_folio;
bh = kzalloc(i, gfp);
if (bh == NULL)
return -ENOMEM;
} else
bh = &bhs;
- first_group = folio->index * blocks_per_page / 2;
-
/* read all groups the folio covers into the cache */
- for (i = 0, group = first_group; i < groups_per_page; i++, group++) {
+ first_group = EXT4_PG_TO_LBLK(inode, folio->index) / 2;
+ for (i = 0, group = first_group; i < groups_per_folio; i++, group++) {
if (group >= ngroups)
break;
@@ -1393,7 +1406,7 @@ static int ext4_mb_init_cache(struct folio *folio, char *incore, gfp_t gfp)
if (!grinfo)
continue;
/*
- * If page is uptodate then we came here after online resize
+ * If folio is uptodate then we came here after online resize
* which added some new uninitialized group info structs, so
* we must skip all initialized uptodate buddies on the folio,
* which may be currently in use by an allocating task.
@@ -1413,7 +1426,7 @@ static int ext4_mb_init_cache(struct folio *folio, char *incore, gfp_t gfp)
}
/* wait for I/O completion */
- for (i = 0, group = first_group; i < groups_per_page; i++, group++) {
+ for (i = 0, group = first_group; i < groups_per_folio; i++, group++) {
int err2;
if (!bh[i])
@@ -1423,8 +1436,8 @@ static int ext4_mb_init_cache(struct folio *folio, char *incore, gfp_t gfp)
err = err2;
}
- first_block = folio->index * blocks_per_page;
- for (i = 0; i < blocks_per_page; i++) {
+ first_block = EXT4_PG_TO_LBLK(inode, folio->index);
+ for (i = 0; i < blocks_per_folio; i++) {
group = (first_block + i) >> 1;
if (group >= ngroups)
break;
@@ -1501,7 +1514,7 @@ static int ext4_mb_init_cache(struct folio *folio, char *incore, gfp_t gfp)
out:
if (bh) {
- for (i = 0; i < groups_per_page; i++)
+ for (i = 0; i < groups_per_folio; i++)
brelse(bh[i]);
if (bh != &bhs)
kfree(bh);
@@ -1510,55 +1523,57 @@ out:
}
/*
- * Lock the buddy and bitmap pages. This make sure other parallel init_group
- * on the same buddy page doesn't happen whild holding the buddy page lock.
- * Return locked buddy and bitmap pages on e4b struct. If buddy and bitmap
- * are on the same page e4b->bd_buddy_folio is NULL and return value is 0.
+ * Lock the buddy and bitmap folios. This makes sure other parallel init_group
+ * on the same buddy folio doesn't happen while holding the buddy folio lock.
+ * Return locked buddy and bitmap folios on e4b struct. If buddy and bitmap
+ * are on the same folio e4b->bd_buddy_folio is NULL and return value is 0.
*/
-static int ext4_mb_get_buddy_page_lock(struct super_block *sb,
+static int ext4_mb_get_buddy_folio_lock(struct super_block *sb,
ext4_group_t group, struct ext4_buddy *e4b, gfp_t gfp)
{
struct inode *inode = EXT4_SB(sb)->s_buddy_cache;
- int block, pnum, poff;
- int blocks_per_page;
+ int block, pnum;
struct folio *folio;
e4b->bd_buddy_folio = NULL;
e4b->bd_bitmap_folio = NULL;
- blocks_per_page = PAGE_SIZE / sb->s_blocksize;
/*
* the buddy cache inode stores the block bitmap
* and buddy information in consecutive blocks.
* So for each group we need two blocks.
*/
block = group * 2;
- pnum = block / blocks_per_page;
- poff = block % blocks_per_page;
+ pnum = EXT4_LBLK_TO_PG(inode, block);
folio = __filemap_get_folio(inode->i_mapping, pnum,
FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
if (IS_ERR(folio))
return PTR_ERR(folio);
BUG_ON(folio->mapping != inode->i_mapping);
+ WARN_ON_ONCE(folio_size(folio) < sb->s_blocksize);
e4b->bd_bitmap_folio = folio;
- e4b->bd_bitmap = folio_address(folio) + (poff * sb->s_blocksize);
+ e4b->bd_bitmap = folio_address(folio) +
+ offset_in_folio(folio, EXT4_LBLK_TO_B(inode, block));
- if (blocks_per_page >= 2) {
- /* buddy and bitmap are on the same page */
+ block++;
+ pnum = EXT4_LBLK_TO_PG(inode, block);
+ if (folio_contains(folio, pnum)) {
+ /* buddy and bitmap are on the same folio */
return 0;
}
- /* blocks_per_page == 1, hence we need another page for the buddy */
- folio = __filemap_get_folio(inode->i_mapping, block + 1,
+ /* we need another folio for the buddy */
+ folio = __filemap_get_folio(inode->i_mapping, pnum,
FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
if (IS_ERR(folio))
return PTR_ERR(folio);
BUG_ON(folio->mapping != inode->i_mapping);
+ WARN_ON_ONCE(folio_size(folio) < sb->s_blocksize);
e4b->bd_buddy_folio = folio;
return 0;
}
-static void ext4_mb_put_buddy_page_lock(struct ext4_buddy *e4b)
+static void ext4_mb_put_buddy_folio_lock(struct ext4_buddy *e4b)
{
if (e4b->bd_bitmap_folio) {
folio_unlock(e4b->bd_bitmap_folio);
@@ -1572,7 +1587,7 @@ static void ext4_mb_put_buddy_page_lock(struct ext4_buddy *e4b)
/*
* Locking note: This routine calls ext4_mb_init_cache(), which takes the
- * block group lock of all groups for this page; do not hold the BG lock when
+ * block group lock of all groups for this folio; do not hold the BG lock when
* calling this routine!
*/
static noinline_for_stack
@@ -1592,14 +1607,14 @@ int ext4_mb_init_group(struct super_block *sb, ext4_group_t group, gfp_t gfp)
/*
* This ensures that we don't reinit the buddy cache
- * page which map to the group from which we are already
+ * folio which map to the group from which we are already
* allocating. If we are looking at the buddy cache we would
* have taken a reference using ext4_mb_load_buddy and that
- * would have pinned buddy page to page cache.
- * The call to ext4_mb_get_buddy_page_lock will mark the
- * page accessed.
+ * would have pinned buddy folio to page cache.
+ * The call to ext4_mb_get_buddy_folio_lock will mark the
+ * folio accessed.
*/
- ret = ext4_mb_get_buddy_page_lock(sb, group, &e4b, gfp);
+ ret = ext4_mb_get_buddy_folio_lock(sb, group, &e4b, gfp);
if (ret || !EXT4_MB_GRP_NEED_INIT(this_grp)) {
/*
* somebody initialized the group
@@ -1620,7 +1635,7 @@ int ext4_mb_init_group(struct super_block *sb, ext4_group_t group, gfp_t gfp)
if (e4b.bd_buddy_folio == NULL) {
/*
* If both the bitmap and buddy are in
- * the same page we don't need to force
+ * the same folio we don't need to force
* init the buddy
*/
ret = 0;
@@ -1636,23 +1651,21 @@ int ext4_mb_init_group(struct super_block *sb, ext4_group_t group, gfp_t gfp)
goto err;
}
err:
- ext4_mb_put_buddy_page_lock(&e4b);
+ ext4_mb_put_buddy_folio_lock(&e4b);
return ret;
}
/*
* Locking note: This routine calls ext4_mb_init_cache(), which takes the
- * block group lock of all groups for this page; do not hold the BG lock when
+ * block group lock of all groups for this folio; do not hold the BG lock when
* calling this routine!
*/
static noinline_for_stack int
ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
struct ext4_buddy *e4b, gfp_t gfp)
{
- int blocks_per_page;
int block;
int pnum;
- int poff;
struct folio *folio;
int ret;
struct ext4_group_info *grp;
@@ -1662,7 +1675,6 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
might_sleep();
mb_debug(sb, "load group %u\n", group);
- blocks_per_page = PAGE_SIZE / sb->s_blocksize;
grp = ext4_get_group_info(sb, group);
if (!grp)
return -EFSCORRUPTED;
@@ -1690,8 +1702,7 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
* So for each group we need two blocks.
*/
block = group * 2;
- pnum = block / blocks_per_page;
- poff = block % blocks_per_page;
+ pnum = EXT4_LBLK_TO_PG(inode, block);
/* Avoid locking the folio in the fast path ... */
folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
@@ -1723,7 +1734,8 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
goto err;
}
mb_cmp_bitmaps(e4b, folio_address(folio) +
- (poff * sb->s_blocksize));
+ offset_in_folio(folio,
+ EXT4_LBLK_TO_B(inode, block)));
}
folio_unlock(folio);
}
@@ -1739,12 +1751,18 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
/* Folios marked accessed already */
e4b->bd_bitmap_folio = folio;
- e4b->bd_bitmap = folio_address(folio) + (poff * sb->s_blocksize);
+ e4b->bd_bitmap = folio_address(folio) +
+ offset_in_folio(folio, EXT4_LBLK_TO_B(inode, block));
block++;
- pnum = block / blocks_per_page;
- poff = block % blocks_per_page;
+ pnum = EXT4_LBLK_TO_PG(inode, block);
+ /* buddy and bitmap are on the same folio? */
+ if (folio_contains(folio, pnum)) {
+ folio_get(folio);
+ goto update_buddy;
+ }
+ /* we need another folio for the buddy */
folio = __filemap_get_folio(inode->i_mapping, pnum, FGP_ACCESSED, 0);
if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
if (!IS_ERR(folio))
@@ -1779,9 +1797,11 @@ ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
goto err;
}
+update_buddy:
/* Folios marked accessed already */
e4b->bd_buddy_folio = folio;
- e4b->bd_buddy = folio_address(folio) + (poff * sb->s_blocksize);
+ e4b->bd_buddy = folio_address(folio) +
+ offset_in_folio(folio, EXT4_LBLK_TO_B(inode, block));
return 0;
@@ -2224,7 +2244,7 @@ static void ext4_mb_use_best_found(struct ext4_allocation_context *ac,
ac->ac_buddy = ret >> 16;
/*
- * take the page reference. We want the page to be pinned
+ * take the folio reference. We want the folio to be pinned
* so that we don't get a ext4_mb_init_cache_call for this
* group until we update the bitmap. That would mean we
* double allocate blocks. The reference is dropped
@@ -2930,7 +2950,7 @@ static int ext4_mb_scan_group(struct ext4_allocation_context *ac,
if (cr < CR_ANY_FREE && spin_is_locked(ext4_group_lock_ptr(sb, group)))
return 0;
- /* This now checks without needing the buddy page */
+ /* This now checks without needing the buddy folio */
ret = ext4_mb_good_group_nolock(ac, group, cr);
if (ret <= 0) {
if (!ac->ac_first_err)
@@ -3490,6 +3510,8 @@ static int ext4_mb_init_backend(struct super_block *sb)
* this will avoid confusion if it ever shows up during debugging. */
sbi->s_buddy_cache->i_ino = EXT4_BAD_INO;
EXT4_I(sbi->s_buddy_cache)->i_disksize = 0;
+ ext4_set_inode_mapping_order(sbi->s_buddy_cache);
+
for (i = 0; i < ngroups; i++) {
cond_resched();
desc = ext4_get_group_desc(sb, i, NULL);
@@ -4720,7 +4742,7 @@ static void ext4_discard_allocated_blocks(struct ext4_allocation_context *ac)
"ext4: mb_load_buddy failed (%d)", err))
/*
* This should never happen since we pin the
- * pages in the ext4_allocation_context so
+ * folios in the ext4_allocation_context so
* ext4_mb_load_buddy() should never fail.
*/
return;
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index 4b091c21908f..0550fd30fd10 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -13,28 +13,14 @@
#include "ext4.h"
#include "ext4_extents.h"
-/**
- * get_ext_path() - Find an extent path for designated logical block number.
- * @inode: inode to be searched
- * @lblock: logical block number to find an extent path
- * @path: pointer to an extent path
- *
- * ext4_find_extent wrapper. Return an extent path pointer on success,
- * or an error pointer on failure.
- */
-static inline struct ext4_ext_path *
-get_ext_path(struct inode *inode, ext4_lblk_t lblock,
- struct ext4_ext_path *path)
-{
- path = ext4_find_extent(inode, lblock, path, EXT4_EX_NOCACHE);
- if (IS_ERR(path))
- return path;
- if (path[ext_depth(inode)].p_ext == NULL) {
- ext4_free_ext_path(path);
- return ERR_PTR(-ENODATA);
- }
- return path;
-}
+#include <trace/events/ext4.h>
+
+struct mext_data {
+ struct inode *orig_inode; /* Origin file inode */
+ struct inode *donor_inode; /* Donor file inode */
+ struct ext4_map_blocks orig_map;/* Origin file's move mapping */
+ ext4_lblk_t donor_lblk; /* Start block of the donor file */
+};
/**
* ext4_double_down_write_data_sem() - write lock two inodes's i_data_sem
@@ -52,7 +38,6 @@ ext4_double_down_write_data_sem(struct inode *first, struct inode *second)
} else {
down_write(&EXT4_I(second)->i_data_sem);
down_write_nested(&EXT4_I(first)->i_data_sem, I_DATA_SEM_OTHER);
-
}
}
@@ -71,59 +56,14 @@ ext4_double_up_write_data_sem(struct inode *orig_inode,
up_write(&EXT4_I(donor_inode)->i_data_sem);
}
-/**
- * mext_check_coverage - Check that all extents in range has the same type
- *
- * @inode: inode in question
- * @from: block offset of inode
- * @count: block count to be checked
- * @unwritten: extents expected to be unwritten
- * @err: pointer to save error value
- *
- * Return 1 if all extents in range has expected type, and zero otherwise.
- */
-static int
-mext_check_coverage(struct inode *inode, ext4_lblk_t from, ext4_lblk_t count,
- int unwritten, int *err)
-{
- struct ext4_ext_path *path = NULL;
- struct ext4_extent *ext;
- int ret = 0;
- ext4_lblk_t last = from + count;
- while (from < last) {
- path = get_ext_path(inode, from, path);
- if (IS_ERR(path)) {
- *err = PTR_ERR(path);
- return ret;
- }
- ext = path[ext_depth(inode)].p_ext;
- if (unwritten != ext4_ext_is_unwritten(ext))
- goto out;
- from += ext4_ext_get_actual_len(ext);
- }
- ret = 1;
-out:
- ext4_free_ext_path(path);
- return ret;
-}
-
-/**
- * mext_folio_double_lock - Grab and lock folio on both @inode1 and @inode2
- *
- * @inode1: the inode structure
- * @inode2: the inode structure
- * @index1: folio index
- * @index2: folio index
- * @folio: result folio vector
- *
- * Grab two locked folio for inode's by inode order
- */
-static int
-mext_folio_double_lock(struct inode *inode1, struct inode *inode2,
- pgoff_t index1, pgoff_t index2, struct folio *folio[2])
+/* Grab and lock folio on both @inode1 and @inode2 by inode order. */
+static int mext_folio_double_lock(struct inode *inode1, struct inode *inode2,
+ pgoff_t index1, pgoff_t index2, size_t len,
+ struct folio *folio[2])
{
struct address_space *mapping[2];
unsigned int flags;
+ fgf_t fgp_flags = FGP_WRITEBEGIN;
BUG_ON(!inode1 || !inode2);
if (inode1 < inode2) {
@@ -136,14 +76,15 @@ mext_folio_double_lock(struct inode *inode1, struct inode *inode2,
}
flags = memalloc_nofs_save();
- folio[0] = __filemap_get_folio(mapping[0], index1, FGP_WRITEBEGIN,
+ fgp_flags |= fgf_set_order(len);
+ folio[0] = __filemap_get_folio(mapping[0], index1, fgp_flags,
mapping_gfp_mask(mapping[0]));
if (IS_ERR(folio[0])) {
memalloc_nofs_restore(flags);
return PTR_ERR(folio[0]);
}
- folio[1] = __filemap_get_folio(mapping[1], index2, FGP_WRITEBEGIN,
+ folio[1] = __filemap_get_folio(mapping[1], index2, fgp_flags,
mapping_gfp_mask(mapping[1]));
memalloc_nofs_restore(flags);
if (IS_ERR(folio[1])) {
@@ -164,8 +105,16 @@ mext_folio_double_lock(struct inode *inode1, struct inode *inode2,
return 0;
}
+static void mext_folio_double_unlock(struct folio *folio[2])
+{
+ folio_unlock(folio[0]);
+ folio_put(folio[0]);
+ folio_unlock(folio[1]);
+ folio_put(folio[1]);
+}
+
/* Force folio buffers uptodate w/o dropping folio's lock */
-static int mext_page_mkuptodate(struct folio *folio, size_t from, size_t to)
+static int mext_folio_mkuptodate(struct folio *folio, size_t from, size_t to)
{
struct inode *inode = folio->mapping->host;
sector_t block;
@@ -238,267 +187,313 @@ out:
return 0;
}
-/**
- * move_extent_per_page - Move extent data per page
- *
- * @o_filp: file structure of original file
- * @donor_inode: donor inode
- * @orig_page_offset: page index on original file
- * @donor_page_offset: page index on donor file
- * @data_offset_in_page: block index where data swapping starts
- * @block_len_in_page: the number of blocks to be swapped
- * @unwritten: orig extent is unwritten or not
- * @err: pointer to save return value
- *
- * Save the data in original inode blocks and replace original inode extents
- * with donor inode extents by calling ext4_swap_extents().
- * Finally, write out the saved data in new original inode blocks. Return
- * replaced block count.
+enum mext_move_type {MEXT_SKIP_EXTENT, MEXT_MOVE_EXTENT, MEXT_COPY_DATA};
+
+/*
+ * Start to move extent between the origin inode and the donor inode,
+ * hold one folio for each inode and check the candidate moving extent
+ * mapping status again.
*/
-static int
-move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
- pgoff_t orig_page_offset, pgoff_t donor_page_offset,
- int data_offset_in_page,
- int block_len_in_page, int unwritten, int *err)
+static int mext_move_begin(struct mext_data *mext, struct folio *folio[2],
+ enum mext_move_type *move_type)
{
- struct inode *orig_inode = file_inode(o_filp);
- struct folio *folio[2] = {NULL, NULL};
- handle_t *handle;
- ext4_lblk_t orig_blk_offset, donor_blk_offset;
- unsigned long blocksize = orig_inode->i_sb->s_blocksize;
- unsigned int tmp_data_size, data_size, replaced_size;
- int i, err2, jblocks, retries = 0;
- int replaced_count = 0;
- int from;
- int blocks_per_page = PAGE_SIZE >> orig_inode->i_blkbits;
- struct super_block *sb = orig_inode->i_sb;
- struct buffer_head *bh = NULL;
+ struct inode *orig_inode = mext->orig_inode;
+ struct inode *donor_inode = mext->donor_inode;
+ unsigned int blkbits = orig_inode->i_blkbits;
+ struct ext4_map_blocks donor_map = {0};
+ loff_t orig_pos, donor_pos;
+ size_t move_len;
+ int ret;
+
+ orig_pos = ((loff_t)mext->orig_map.m_lblk) << blkbits;
+ donor_pos = ((loff_t)mext->donor_lblk) << blkbits;
+ ret = mext_folio_double_lock(orig_inode, donor_inode,
+ orig_pos >> PAGE_SHIFT, donor_pos >> PAGE_SHIFT,
+ ((size_t)mext->orig_map.m_len) << blkbits, folio);
+ if (ret)
+ return ret;
/*
- * It needs twice the amount of ordinary journal buffers because
- * inode and donor_inode may change each different metadata blocks.
+ * Check the origin inode's mapping information again under the
+ * folio lock, as we do not hold the i_data_sem at all times, and
+ * it may change during the concurrent write-back operation.
*/
-again:
- *err = 0;
- jblocks = ext4_meta_trans_blocks(orig_inode, block_len_in_page,
- block_len_in_page) * 2;
- handle = ext4_journal_start(orig_inode, EXT4_HT_MOVE_EXTENTS, jblocks);
+ if (mext->orig_map.m_seq != READ_ONCE(EXT4_I(orig_inode)->i_es_seq)) {
+ ret = -ESTALE;
+ goto error;
+ }
+
+ /* Adjust the moving length according to the length of shorter folio. */
+ move_len = umin(folio_pos(folio[0]) + folio_size(folio[0]) - orig_pos,
+ folio_pos(folio[1]) + folio_size(folio[1]) - donor_pos);
+ move_len >>= blkbits;
+ if (move_len < mext->orig_map.m_len)
+ mext->orig_map.m_len = move_len;
+
+ donor_map.m_lblk = mext->donor_lblk;
+ donor_map.m_len = mext->orig_map.m_len;
+ donor_map.m_flags = 0;
+ ret = ext4_map_blocks(NULL, donor_inode, &donor_map, 0);
+ if (ret < 0)
+ goto error;
+
+ /* Adjust the moving length according to the donor mapping length. */
+ mext->orig_map.m_len = donor_map.m_len;
+
+ /* Skip moving if the donor range is a hole or a delalloc extent. */
+ if (!(donor_map.m_flags & (EXT4_MAP_MAPPED | EXT4_MAP_UNWRITTEN)))
+ *move_type = MEXT_SKIP_EXTENT;
+ /* If both mapping ranges are unwritten, no need to copy data. */
+ else if ((mext->orig_map.m_flags & EXT4_MAP_UNWRITTEN) &&
+ (donor_map.m_flags & EXT4_MAP_UNWRITTEN))
+ *move_type = MEXT_MOVE_EXTENT;
+ else
+ *move_type = MEXT_COPY_DATA;
+
+ return 0;
+error:
+ mext_folio_double_unlock(folio);
+ return ret;
+}
+
+/*
+ * Re-create the new moved mapping buffers of the original inode and commit
+ * the entire written range.
+ */
+static int mext_folio_mkwrite(struct inode *inode, struct folio *folio,
+ size_t from, size_t to)
+{
+ unsigned int blocksize = i_blocksize(inode);
+ struct buffer_head *bh, *head;
+ size_t block_start, block_end;
+ sector_t block;
+ int ret;
+
+ head = folio_buffers(folio);
+ if (!head)
+ head = create_empty_buffers(folio, blocksize, 0);
+
+ block = folio_pos(folio) >> inode->i_blkbits;
+ block_end = 0;
+ bh = head;
+ do {
+ block_start = block_end;
+ block_end = block_start + blocksize;
+ if (block_end <= from || block_start >= to)
+ continue;
+
+ ret = ext4_get_block(inode, block, bh, 0);
+ if (ret)
+ return ret;
+ } while (block++, (bh = bh->b_this_page) != head);
+
+ block_commit_write(folio, from, to);
+ return 0;
+}
+
+/*
+ * Save the data in original inode extent blocks and replace one folio size
+ * aligned original inode extent with one or one partial donor inode extent,
+ * and then write out the saved data in new original inode blocks. Pass out
+ * the replaced block count through m_len. Return 0 on success, and an error
+ * code otherwise.
+ */
+static int mext_move_extent(struct mext_data *mext, u64 *m_len)
+{
+ struct inode *orig_inode = mext->orig_inode;
+ struct inode *donor_inode = mext->donor_inode;
+ struct ext4_map_blocks *orig_map = &mext->orig_map;
+ unsigned int blkbits = orig_inode->i_blkbits;
+ struct folio *folio[2] = {NULL, NULL};
+ loff_t from, length;
+ enum mext_move_type move_type = 0;
+ handle_t *handle;
+ u64 r_len = 0;
+ unsigned int credits;
+ int ret, ret2;
+
+ *m_len = 0;
+ trace_ext4_move_extent_enter(orig_inode, orig_map, donor_inode,
+ mext->donor_lblk);
+ credits = ext4_chunk_trans_extent(orig_inode, 0) * 2;
+ handle = ext4_journal_start(orig_inode, EXT4_HT_MOVE_EXTENTS, credits);
if (IS_ERR(handle)) {
- *err = PTR_ERR(handle);
- return 0;
+ ret = PTR_ERR(handle);
+ goto out;
}
- orig_blk_offset = orig_page_offset * blocks_per_page +
- data_offset_in_page;
-
- donor_blk_offset = donor_page_offset * blocks_per_page +
- data_offset_in_page;
-
- /* Calculate data_size */
- if ((orig_blk_offset + block_len_in_page - 1) ==
- ((orig_inode->i_size - 1) >> orig_inode->i_blkbits)) {
- /* Replace the last block */
- tmp_data_size = orig_inode->i_size & (blocksize - 1);
- /*
- * If data_size equal zero, it shows data_size is multiples of
- * blocksize. So we set appropriate value.
- */
- if (tmp_data_size == 0)
- tmp_data_size = blocksize;
-
- data_size = tmp_data_size +
- ((block_len_in_page - 1) << orig_inode->i_blkbits);
- } else
- data_size = block_len_in_page << orig_inode->i_blkbits;
-
- replaced_size = data_size;
-
- *err = mext_folio_double_lock(orig_inode, donor_inode, orig_page_offset,
- donor_page_offset, folio);
- if (unlikely(*err < 0))
- goto stop_journal;
+ ret = mext_move_begin(mext, folio, &move_type);
+ if (ret)
+ goto stop_handle;
+
+ if (move_type == MEXT_SKIP_EXTENT)
+ goto unlock;
+
/*
- * If orig extent was unwritten it can become initialized
- * at any time after i_data_sem was dropped, in order to
- * serialize with delalloc we have recheck extent while we
- * hold page's lock, if it is still the case data copy is not
- * necessary, just swap data blocks between orig and donor.
+ * Copy the data. First, read the original inode data into the page
+ * cache. Then, release the existing mapping relationships and swap
+ * the extent. Finally, re-establish the new mapping relationships
+ * and dirty the page cache.
*/
- if (unwritten) {
- ext4_double_down_write_data_sem(orig_inode, donor_inode);
- /* If any of extents in range became initialized we have to
- * fallback to data copying */
- unwritten = mext_check_coverage(orig_inode, orig_blk_offset,
- block_len_in_page, 1, err);
- if (*err)
- goto drop_data_sem;
-
- unwritten &= mext_check_coverage(donor_inode, donor_blk_offset,
- block_len_in_page, 1, err);
- if (*err)
- goto drop_data_sem;
-
- if (!unwritten) {
- ext4_double_up_write_data_sem(orig_inode, donor_inode);
- goto data_copy;
- }
- if (!filemap_release_folio(folio[0], 0) ||
- !filemap_release_folio(folio[1], 0)) {
- *err = -EBUSY;
- goto drop_data_sem;
- }
- replaced_count = ext4_swap_extents(handle, orig_inode,
- donor_inode, orig_blk_offset,
- donor_blk_offset,
- block_len_in_page, 1, err);
- drop_data_sem:
- ext4_double_up_write_data_sem(orig_inode, donor_inode);
- goto unlock_folios;
+ if (move_type == MEXT_COPY_DATA) {
+ from = offset_in_folio(folio[0],
+ ((loff_t)orig_map->m_lblk) << blkbits);
+ length = ((loff_t)orig_map->m_len) << blkbits;
+
+ ret = mext_folio_mkuptodate(folio[0], from, from + length);
+ if (ret)
+ goto unlock;
}
-data_copy:
- from = offset_in_folio(folio[0],
- orig_blk_offset << orig_inode->i_blkbits);
- *err = mext_page_mkuptodate(folio[0], from, from + replaced_size);
- if (*err)
- goto unlock_folios;
-
- /* At this point all buffers in range are uptodate, old mapping layout
- * is no longer required, try to drop it now. */
+
if (!filemap_release_folio(folio[0], 0) ||
!filemap_release_folio(folio[1], 0)) {
- *err = -EBUSY;
- goto unlock_folios;
+ ret = -EBUSY;
+ goto unlock;
}
+
+ /* Move extent */
ext4_double_down_write_data_sem(orig_inode, donor_inode);
- replaced_count = ext4_swap_extents(handle, orig_inode, donor_inode,
- orig_blk_offset, donor_blk_offset,
- block_len_in_page, 1, err);
+ *m_len = ext4_swap_extents(handle, orig_inode, donor_inode,
+ orig_map->m_lblk, mext->donor_lblk,
+ orig_map->m_len, 1, &ret);
ext4_double_up_write_data_sem(orig_inode, donor_inode);
- if (*err) {
- if (replaced_count) {
- block_len_in_page = replaced_count;
- replaced_size =
- block_len_in_page << orig_inode->i_blkbits;
- } else
- goto unlock_folios;
- }
- /* Perform all necessary steps similar write_begin()/write_end()
- * but keeping in mind that i_size will not change */
- bh = folio_buffers(folio[0]);
- if (!bh)
- bh = create_empty_buffers(folio[0],
- 1 << orig_inode->i_blkbits, 0);
- for (i = 0; i < from >> orig_inode->i_blkbits; i++)
- bh = bh->b_this_page;
- for (i = 0; i < block_len_in_page; i++) {
- *err = ext4_get_block(orig_inode, orig_blk_offset + i, bh, 0);
- if (*err < 0)
- goto repair_branches;
- bh = bh->b_this_page;
- }
- block_commit_write(folio[0], from, from + replaced_size);
+ /* A short-length swap cannot occur after a successful swap extent. */
+ if (WARN_ON_ONCE(!ret && (*m_len != orig_map->m_len)))
+ ret = -EIO;
- /* Even in case of data=writeback it is reasonable to pin
- * inode to transaction, to prevent unexpected data loss */
- *err = ext4_jbd2_inode_add_write(handle, orig_inode,
- (loff_t)orig_page_offset << PAGE_SHIFT, replaced_size);
+ if (!(*m_len) || (move_type == MEXT_MOVE_EXTENT))
+ goto unlock;
-unlock_folios:
- folio_unlock(folio[0]);
- folio_put(folio[0]);
- folio_unlock(folio[1]);
- folio_put(folio[1]);
-stop_journal:
+ /* Copy data */
+ length = (*m_len) << blkbits;
+ ret2 = mext_folio_mkwrite(orig_inode, folio[0], from, from + length);
+ if (ret2) {
+ if (!ret)
+ ret = ret2;
+ goto repair_branches;
+ }
+ /*
+ * Even in case of data=writeback it is reasonable to pin
+ * inode to transaction, to prevent unexpected data loss.
+ */
+ ret2 = ext4_jbd2_inode_add_write(handle, orig_inode,
+ ((loff_t)orig_map->m_lblk) << blkbits, length);
+ if (!ret)
+ ret = ret2;
+unlock:
+ mext_folio_double_unlock(folio);
+stop_handle:
ext4_journal_stop(handle);
- if (*err == -ENOSPC &&
- ext4_should_retry_alloc(sb, &retries))
- goto again;
- /* Buffer was busy because probably is pinned to journal transaction,
- * force transaction commit may help to free it. */
- if (*err == -EBUSY && retries++ < 4 && EXT4_SB(sb)->s_journal &&
- jbd2_journal_force_commit_nested(EXT4_SB(sb)->s_journal))
- goto again;
- return replaced_count;
+out:
+ trace_ext4_move_extent_exit(orig_inode, orig_map->m_lblk, donor_inode,
+ mext->donor_lblk, orig_map->m_len, *m_len,
+ move_type, ret);
+ return ret;
repair_branches:
- /*
- * This should never ever happen!
- * Extents are swapped already, but we are not able to copy data.
- * Try to swap extents to it's original places
- */
- ext4_double_down_write_data_sem(orig_inode, donor_inode);
- replaced_count = ext4_swap_extents(handle, donor_inode, orig_inode,
- orig_blk_offset, donor_blk_offset,
- block_len_in_page, 0, &err2);
- ext4_double_up_write_data_sem(orig_inode, donor_inode);
- if (replaced_count != block_len_in_page) {
- ext4_error_inode_block(orig_inode, (sector_t)(orig_blk_offset),
- EIO, "Unable to copy data block,"
- " data will be lost.");
- *err = -EIO;
+ ret2 = 0;
+ r_len = ext4_swap_extents(handle, donor_inode, orig_inode,
+ mext->donor_lblk, orig_map->m_lblk,
+ *m_len, 0, &ret2);
+ 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!");
+ ret = -EIO;
}
- replaced_count = 0;
- goto unlock_folios;
+ *m_len = 0;
+ goto unlock;
}
-/**
- * mext_check_arguments - Check whether move extent can be done
- *
- * @orig_inode: original inode
- * @donor_inode: donor inode
- * @orig_start: logical start offset in block for orig
- * @donor_start: logical start offset in block for donor
- * @len: the number of blocks to be moved
- *
- * Check the arguments of ext4_move_extents() whether the files can be
- * exchanged with each other.
- * Return 0 on success, or a negative error value on failure.
+/*
+ * Check the validity of the basic filesystem environment and the
+ * inodes' support status.
*/
-static int
-mext_check_arguments(struct inode *orig_inode,
- struct inode *donor_inode, __u64 orig_start,
- __u64 donor_start, __u64 *len)
+static int mext_check_validity(struct inode *orig_inode,
+ struct inode *donor_inode)
{
- __u64 orig_eof, donor_eof;
- unsigned int blkbits = orig_inode->i_blkbits;
- unsigned int blocksize = 1 << blkbits;
+ struct super_block *sb = orig_inode->i_sb;
+
+ /* origin and donor should be different inodes */
+ if (orig_inode == donor_inode) {
+ ext4_debug("ext4 move extent: The argument files should not be same inode [ino:orig %lu, donor %lu]\n",
+ orig_inode->i_ino, donor_inode->i_ino);
+ return -EINVAL;
+ }
+
+ /* origin and donor should belone to the same filesystem */
+ if (orig_inode->i_sb != donor_inode->i_sb) {
+ ext4_debug("ext4 move extent: The argument files should be in same FS [ino:orig %lu, donor %lu]\n",
+ orig_inode->i_ino, donor_inode->i_ino);
+ return -EINVAL;
+ }
+
+ /* Regular file check */
+ if (!S_ISREG(orig_inode->i_mode) || !S_ISREG(donor_inode->i_mode)) {
+ ext4_debug("ext4 move extent: The argument files should be regular file [ino:orig %lu, donor %lu]\n",
+ orig_inode->i_ino, donor_inode->i_ino);
+ return -EINVAL;
+ }
- orig_eof = (i_size_read(orig_inode) + blocksize - 1) >> blkbits;
- donor_eof = (i_size_read(donor_inode) + blocksize - 1) >> blkbits;
+ if (ext4_has_feature_bigalloc(sb)) {
+ ext4_msg(sb, KERN_ERR,
+ "Online defrag not supported with bigalloc");
+ return -EOPNOTSUPP;
+ }
+ if (IS_DAX(orig_inode)) {
+ ext4_msg(sb, KERN_ERR,
+ "Online defrag not supported with DAX");
+ return -EOPNOTSUPP;
+ }
+
+ /*
+ * TODO: it's not obvious how to swap blocks for inodes with full
+ * journaling enabled.
+ */
+ if (ext4_should_journal_data(orig_inode) ||
+ ext4_should_journal_data(donor_inode)) {
+ ext4_msg(sb, KERN_ERR,
+ "Online defrag not supported with data journaling");
+ return -EOPNOTSUPP;
+ }
+
+ if (IS_ENCRYPTED(orig_inode) || IS_ENCRYPTED(donor_inode)) {
+ ext4_msg(sb, KERN_ERR,
+ "Online defrag not supported for encrypted files");
+ return -EOPNOTSUPP;
+ }
+
+ /* Ext4 move extent supports only extent based file */
+ if (!(ext4_test_inode_flag(orig_inode, EXT4_INODE_EXTENTS)) ||
+ !(ext4_test_inode_flag(donor_inode, EXT4_INODE_EXTENTS))) {
+ ext4_msg(sb, KERN_ERR,
+ "Online defrag not supported for non-extent files");
+ return -EOPNOTSUPP;
+ }
if (donor_inode->i_mode & (S_ISUID|S_ISGID)) {
- ext4_debug("ext4 move extent: suid or sgid is set"
- " to donor file [ino:orig %lu, donor %lu]\n",
+ ext4_debug("ext4 move extent: suid or sgid is set to donor file [ino:orig %lu, donor %lu]\n",
orig_inode->i_ino, donor_inode->i_ino);
return -EINVAL;
}
- if (IS_IMMUTABLE(donor_inode) || IS_APPEND(donor_inode))
+ if (IS_IMMUTABLE(donor_inode) || IS_APPEND(donor_inode)) {
+ ext4_debug("ext4 move extent: donor should not be immutable or append file [ino:orig %lu, donor %lu]\n",
+ orig_inode->i_ino, donor_inode->i_ino);
return -EPERM;
+ }
/* Ext4 move extent does not support swap files */
if (IS_SWAPFILE(orig_inode) || IS_SWAPFILE(donor_inode)) {
ext4_debug("ext4 move extent: The argument files should not be swap files [ino:orig %lu, donor %lu]\n",
- orig_inode->i_ino, donor_inode->i_ino);
+ orig_inode->i_ino, donor_inode->i_ino);
return -ETXTBSY;
}
- if (ext4_is_quota_file(orig_inode) && ext4_is_quota_file(donor_inode)) {
+ if (ext4_is_quota_file(orig_inode) || ext4_is_quota_file(donor_inode)) {
ext4_debug("ext4 move extent: The argument files should not be quota files [ino:orig %lu, donor %lu]\n",
- orig_inode->i_ino, donor_inode->i_ino);
- return -EOPNOTSUPP;
- }
-
- /* Ext4 move extent supports only extent based file */
- if (!(ext4_test_inode_flag(orig_inode, EXT4_INODE_EXTENTS))) {
- ext4_debug("ext4 move extent: orig file is not extents "
- "based file [ino:orig %lu]\n", orig_inode->i_ino);
- return -EOPNOTSUPP;
- } else if (!(ext4_test_inode_flag(donor_inode, EXT4_INODE_EXTENTS))) {
- ext4_debug("ext4 move extent: donor file is not extents "
- "based file [ino:donor %lu]\n", donor_inode->i_ino);
+ orig_inode->i_ino, donor_inode->i_ino);
return -EOPNOTSUPP;
}
@@ -507,12 +502,25 @@ mext_check_arguments(struct inode *orig_inode,
return -EINVAL;
}
+ return 0;
+}
+
+/*
+ * Check the moving range of ext4_move_extents() whether the files can be
+ * exchanged with each other, and adjust the length to fit within the file
+ * size. Return 0 on success, or a negative error value on failure.
+ */
+static int mext_check_adjust_range(struct inode *orig_inode,
+ struct inode *donor_inode, __u64 orig_start,
+ __u64 donor_start, __u64 *len)
+{
+ __u64 orig_eof, donor_eof;
+
/* Start offset should be same */
if ((orig_start & ~(PAGE_MASK >> orig_inode->i_blkbits)) !=
(donor_start & ~(PAGE_MASK >> orig_inode->i_blkbits))) {
- ext4_debug("ext4 move extent: orig and donor's start "
- "offsets are not aligned [ino:orig %lu, donor %lu]\n",
- orig_inode->i_ino, donor_inode->i_ino);
+ ext4_debug("ext4 move extent: orig and donor's start offsets are not aligned [ino:orig %lu, donor %lu]\n",
+ orig_inode->i_ino, donor_inode->i_ino);
return -EINVAL;
}
@@ -521,11 +529,14 @@ mext_check_arguments(struct inode *orig_inode,
(*len > EXT_MAX_BLOCKS) ||
(donor_start + *len >= EXT_MAX_BLOCKS) ||
(orig_start + *len >= EXT_MAX_BLOCKS)) {
- ext4_debug("ext4 move extent: Can't handle over [%u] blocks "
- "[ino:orig %lu, donor %lu]\n", EXT_MAX_BLOCKS,
- orig_inode->i_ino, donor_inode->i_ino);
+ ext4_debug("ext4 move extent: Can't handle over [%u] blocks [ino:orig %lu, donor %lu]\n",
+ EXT_MAX_BLOCKS,
+ orig_inode->i_ino, donor_inode->i_ino);
return -EINVAL;
}
+
+ orig_eof = EXT4_B_TO_LBLK(orig_inode, i_size_read(orig_inode));
+ donor_eof = EXT4_B_TO_LBLK(donor_inode, i_size_read(donor_inode));
if (orig_eof <= orig_start)
*len = 0;
else if (orig_eof < orig_start + *len - 1)
@@ -535,9 +546,8 @@ mext_check_arguments(struct inode *orig_inode,
else if (donor_eof < donor_start + *len - 1)
*len = donor_eof - donor_start;
if (!*len) {
- ext4_debug("ext4 move extent: len should not be 0 "
- "[ino:orig %lu, donor %lu]\n", orig_inode->i_ino,
- donor_inode->i_ino);
+ ext4_debug("ext4 move extent: len should not be 0 [ino:orig %lu, donor %lu]\n",
+ orig_inode->i_ino, donor_inode->i_ino);
return -EINVAL;
}
@@ -556,140 +566,81 @@ mext_check_arguments(struct inode *orig_inode,
*
* This function returns 0 and moved block length is set in moved_len
* if succeed, otherwise returns error value.
- *
*/
-int
-ext4_move_extents(struct file *o_filp, struct file *d_filp, __u64 orig_blk,
- __u64 donor_blk, __u64 len, __u64 *moved_len)
+int ext4_move_extents(struct file *o_filp, struct file *d_filp, __u64 orig_blk,
+ __u64 donor_blk, __u64 len, __u64 *moved_len)
{
struct inode *orig_inode = file_inode(o_filp);
struct inode *donor_inode = file_inode(d_filp);
- struct ext4_ext_path *path = NULL;
- int blocks_per_page = PAGE_SIZE >> orig_inode->i_blkbits;
- ext4_lblk_t o_end, o_start = orig_blk;
- ext4_lblk_t d_start = donor_blk;
+ struct mext_data mext;
+ struct super_block *sb = orig_inode->i_sb;
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
+ int retries = 0;
+ u64 m_len;
int ret;
- if (orig_inode->i_sb != donor_inode->i_sb) {
- ext4_debug("ext4 move extent: The argument files "
- "should be in same FS [ino:orig %lu, donor %lu]\n",
- orig_inode->i_ino, donor_inode->i_ino);
- return -EINVAL;
- }
-
- /* orig and donor should be different inodes */
- if (orig_inode == donor_inode) {
- ext4_debug("ext4 move extent: The argument files should not "
- "be same inode [ino:orig %lu, donor %lu]\n",
- orig_inode->i_ino, donor_inode->i_ino);
- return -EINVAL;
- }
-
- /* Regular file check */
- if (!S_ISREG(orig_inode->i_mode) || !S_ISREG(donor_inode->i_mode)) {
- ext4_debug("ext4 move extent: The argument files should be "
- "regular file [ino:orig %lu, donor %lu]\n",
- orig_inode->i_ino, donor_inode->i_ino);
- return -EINVAL;
- }
-
- /* TODO: it's not obvious how to swap blocks for inodes with full
- journaling enabled */
- if (ext4_should_journal_data(orig_inode) ||
- ext4_should_journal_data(donor_inode)) {
- ext4_msg(orig_inode->i_sb, KERN_ERR,
- "Online defrag not supported with data journaling");
- return -EOPNOTSUPP;
- }
-
- if (IS_ENCRYPTED(orig_inode) || IS_ENCRYPTED(donor_inode)) {
- ext4_msg(orig_inode->i_sb, KERN_ERR,
- "Online defrag not supported for encrypted files");
- return -EOPNOTSUPP;
- }
+ *moved_len = 0;
/* Protect orig and donor inodes against a truncate */
lock_two_nondirectories(orig_inode, donor_inode);
+ ret = mext_check_validity(orig_inode, donor_inode);
+ if (ret)
+ goto out;
+
/* Wait for all existing dio workers */
inode_dio_wait(orig_inode);
inode_dio_wait(donor_inode);
- /* Protect extent tree against block allocations via delalloc */
- ext4_double_down_write_data_sem(orig_inode, donor_inode);
- /* Check the filesystem environment whether move_extent can be done */
- ret = mext_check_arguments(orig_inode, donor_inode, orig_blk,
- donor_blk, &len);
+ /* Check and adjust the specified move_extent range. */
+ ret = mext_check_adjust_range(orig_inode, donor_inode, orig_blk,
+ donor_blk, &len);
if (ret)
goto out;
- o_end = o_start + len;
- *moved_len = 0;
- while (o_start < o_end) {
- struct ext4_extent *ex;
- ext4_lblk_t cur_blk, next_blk;
- pgoff_t orig_page_index, donor_page_index;
- int offset_in_page;
- int unwritten, cur_len;
-
- path = get_ext_path(orig_inode, o_start, path);
- if (IS_ERR(path)) {
- ret = PTR_ERR(path);
+ mext.orig_inode = orig_inode;
+ mext.donor_inode = donor_inode;
+ while (len) {
+ mext.orig_map.m_lblk = orig_blk;
+ mext.orig_map.m_len = len;
+ mext.orig_map.m_flags = 0;
+ mext.donor_lblk = donor_blk;
+
+ ret = ext4_map_blocks(NULL, orig_inode, &mext.orig_map, 0);
+ if (ret < 0)
goto out;
- }
- ex = path[path->p_depth].p_ext;
- cur_blk = le32_to_cpu(ex->ee_block);
- cur_len = ext4_ext_get_actual_len(ex);
- /* Check hole before the start pos */
- if (cur_blk + cur_len - 1 < o_start) {
- next_blk = ext4_ext_next_allocated_block(path);
- if (next_blk == EXT_MAX_BLOCKS) {
- ret = -ENODATA;
- goto out;
+
+ /* Skip moving if it is a hole or a delalloc extent. */
+ if (mext.orig_map.m_flags &
+ (EXT4_MAP_MAPPED | EXT4_MAP_UNWRITTEN)) {
+ ret = mext_move_extent(&mext, &m_len);
+ *moved_len += m_len;
+ if (!ret)
+ goto next;
+
+ /* Move failed or partially failed. */
+ if (m_len) {
+ orig_blk += m_len;
+ donor_blk += m_len;
+ len -= m_len;
}
- d_start += next_blk - o_start;
- o_start = next_blk;
- continue;
- /* Check hole after the start pos */
- } else if (cur_blk > o_start) {
- /* Skip hole */
- d_start += cur_blk - o_start;
- o_start = cur_blk;
- /* Extent inside requested range ?*/
- if (cur_blk >= o_end)
- goto out;
- } else { /* in_range(o_start, o_blk, o_len) */
- cur_len += cur_blk - o_start;
+ if (ret == -ESTALE)
+ continue;
+ if (ret == -ENOSPC &&
+ ext4_should_retry_alloc(sb, &retries))
+ continue;
+ if (ret == -EBUSY &&
+ sbi->s_journal && retries++ < 4 &&
+ jbd2_journal_force_commit_nested(sbi->s_journal))
+ continue;
+
+ goto out;
}
- unwritten = ext4_ext_is_unwritten(ex);
- if (o_end - o_start < cur_len)
- cur_len = o_end - o_start;
-
- orig_page_index = o_start >> (PAGE_SHIFT -
- orig_inode->i_blkbits);
- donor_page_index = d_start >> (PAGE_SHIFT -
- donor_inode->i_blkbits);
- offset_in_page = o_start % blocks_per_page;
- if (cur_len > blocks_per_page - offset_in_page)
- cur_len = blocks_per_page - offset_in_page;
- /*
- * Up semaphore to avoid following problems:
- * a. transaction deadlock among ext4_journal_start,
- * ->write_begin via pagefault, and jbd2_journal_commit
- * b. racing with ->read_folio, ->write_begin, and
- * ext4_get_block in move_extent_per_page
- */
- ext4_double_up_write_data_sem(orig_inode, donor_inode);
- /* Swap original branches with new branches */
- *moved_len += move_extent_per_page(o_filp, donor_inode,
- orig_page_index, donor_page_index,
- offset_in_page, cur_len,
- unwritten, &ret);
- ext4_double_down_write_data_sem(orig_inode, donor_inode);
- if (ret < 0)
- break;
- o_start += cur_len;
- d_start += cur_len;
+next:
+ orig_blk += mext.orig_map.m_len;
+ donor_blk += mext.orig_map.m_len;
+ len -= mext.orig_map.m_len;
+ retries = 0;
}
out:
@@ -698,9 +649,6 @@ out:
ext4_discard_preallocations(donor_inode);
}
- ext4_free_ext_path(path);
- ext4_double_up_write_data_sem(orig_inode, donor_inode);
unlock_two_nondirectories(orig_inode, donor_inode);
-
return ret;
}
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 2cd36f59c9e3..c4b5e252af0e 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -1076,7 +1076,7 @@ static int htree_dirblock_to_tree(struct file *dir_file,
for (; de < top; de = ext4_next_entry(de, dir->i_sb->s_blocksize)) {
if (ext4_check_dir_entry(dir, NULL, de, bh,
bh->b_data, bh->b_size,
- (block<<EXT4_BLOCK_SIZE_BITS(dir->i_sb))
+ EXT4_LBLK_TO_B(dir, block)
+ ((char *)de - bh->b_data))) {
/* silently ignore the rest of the block */
break;
@@ -1630,7 +1630,7 @@ restart:
}
set_buffer_verified(bh);
i = search_dirblock(bh, dir, fname,
- block << EXT4_BLOCK_SIZE_BITS(sb), res_dir);
+ EXT4_LBLK_TO_B(dir, block), res_dir);
if (i == 1) {
EXT4_I(dir)->i_dir_start_lookup = block;
ret = bh;
@@ -1710,7 +1710,6 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
struct ext4_filename *fname,
struct ext4_dir_entry_2 **res_dir)
{
- struct super_block * sb = dir->i_sb;
struct dx_frame frames[EXT4_HTREE_LEVEL], *frame;
struct buffer_head *bh;
ext4_lblk_t block;
@@ -1729,8 +1728,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
goto errout;
retval = search_dirblock(bh, dir, fname,
- block << EXT4_BLOCK_SIZE_BITS(sb),
- res_dir);
+ EXT4_LBLK_TO_B(dir, block), res_dir);
if (retval == 1)
goto success;
brelse(bh);
@@ -1762,7 +1760,7 @@ success:
static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
{
struct inode *inode;
- struct ext4_dir_entry_2 *de;
+ struct ext4_dir_entry_2 *de = NULL;
struct buffer_head *bh;
if (dentry->d_name.len > EXT4_NAME_LEN)
@@ -1818,7 +1816,7 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
struct dentry *ext4_get_parent(struct dentry *child)
{
__u32 ino;
- struct ext4_dir_entry_2 * de;
+ struct ext4_dir_entry_2 * de = NULL;
struct buffer_head *bh;
bh = ext4_find_entry(d_inode(child), &dotdot_name, &de, NULL);
@@ -3133,7 +3131,7 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
int retval;
struct inode *inode;
struct buffer_head *bh;
- struct ext4_dir_entry_2 *de;
+ struct ext4_dir_entry_2 *de = NULL;
handle_t *handle = NULL;
retval = ext4_emergency_state(dir->i_sb);
@@ -3224,7 +3222,7 @@ int __ext4_unlink(struct inode *dir, const struct qstr *d_name,
{
int retval = -ENOENT;
struct buffer_head *bh;
- struct ext4_dir_entry_2 *de;
+ struct ext4_dir_entry_2 *de = NULL;
handle_t *handle;
int skip_remove_dentry = 0;
@@ -3688,7 +3686,7 @@ static int ext4_find_delete_entry(handle_t *handle, struct inode *dir,
{
int retval = -ENOENT;
struct buffer_head *bh;
- struct ext4_dir_entry_2 *de;
+ struct ext4_dir_entry_2 *de = NULL;
bh = ext4_find_entry(dir, d_name, &de, NULL);
if (IS_ERR(bh))
diff --git a/fs/ext4/orphan.c b/fs/ext4/orphan.c
index 5fd54adf0c88..c9b93b670b0f 100644
--- a/fs/ext4/orphan.c
+++ b/fs/ext4/orphan.c
@@ -8,6 +8,8 @@
#include "ext4.h"
#include "ext4_jbd2.h"
+#define EXT4_MAX_ORPHAN_FILE_BLOCKS 512
+
static int ext4_orphan_file_add(handle_t *handle, struct inode *inode)
{
int i, j, start;
@@ -588,7 +590,7 @@ int ext4_init_orphan_info(struct super_block *sb)
* consuming absurd amounts of memory when pinning blocks of orphan
* file in memory.
*/
- if (inode->i_size > 8 << 20) {
+ if (inode->i_size > (EXT4_MAX_ORPHAN_FILE_BLOCKS << inode->i_blkbits)) {
ext4_msg(sb, KERN_ERR, "orphan file too big: %llu",
(unsigned long long)inode->i_size);
ret = -EFSCORRUPTED;
diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c
index f329daf6e5c7..e7f2350c725b 100644
--- a/fs/ext4/readpage.c
+++ b/fs/ext4/readpage.c
@@ -213,9 +213,7 @@ int ext4_mpage_readpages(struct inode *inode,
{
struct bio *bio = NULL;
sector_t last_block_in_bio = 0;
-
const unsigned blkbits = inode->i_blkbits;
- const unsigned blocks_per_page = PAGE_SIZE >> blkbits;
const unsigned blocksize = 1 << blkbits;
sector_t next_block;
sector_t block_in_file;
@@ -251,9 +249,8 @@ int ext4_mpage_readpages(struct inode *inode,
blocks_per_folio = folio_size(folio) >> blkbits;
first_hole = blocks_per_folio;
- block_in_file = next_block =
- (sector_t)folio->index << (PAGE_SHIFT - blkbits);
- last_block = block_in_file + nr_pages * blocks_per_page;
+ block_in_file = next_block = EXT4_PG_TO_LBLK(inode, folio->index);
+ last_block = EXT4_PG_TO_LBLK(inode, folio->index + nr_pages);
last_block_in_file = (ext4_readpage_limit(inode) +
blocksize - 1) >> blkbits;
if (last_block > last_block_in_file)
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 33e7c08c9529..87205660c5d0 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -698,7 +698,7 @@ static void ext4_handle_error(struct super_block *sb, bool force_ro, int error,
WARN_ON_ONCE(1);
if (!continue_fs && !ext4_emergency_ro(sb) && journal)
- jbd2_journal_abort(journal, -EIO);
+ jbd2_journal_abort(journal, -error);
if (!bdev_read_only(sb->s_bdev)) {
save_error_info(sb, error, ino, block, func, line);
@@ -1396,6 +1396,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
inode_set_iversion(&ei->vfs_inode, 1);
ei->i_flags = 0;
+ ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */
spin_lock_init(&ei->i_raw_lock);
ei->i_prealloc_node = RB_ROOT;
atomic_set(&ei->i_prealloc_active, 0);
@@ -1406,6 +1407,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
ei->i_es_all_nr = 0;
ei->i_es_shk_nr = 0;
ei->i_es_shrink_lblk = 0;
+ ei->i_es_seq = 0;
ei->i_reserved_data_blocks = 0;
spin_lock_init(&(ei->i_block_reservation_lock));
ext4_init_pending_tree(&ei->i_pending_tree);
@@ -2475,7 +2477,7 @@ static int parse_apply_sb_mount_options(struct super_block *sb,
struct ext4_fs_context *m_ctx)
{
struct ext4_sb_info *sbi = EXT4_SB(sb);
- char s_mount_opts[65];
+ char s_mount_opts[64];
struct ext4_fs_context *s_ctx = NULL;
struct fs_context *fc = NULL;
int ret = -ENOMEM;
@@ -2483,7 +2485,8 @@ static int parse_apply_sb_mount_options(struct super_block *sb,
if (!sbi->s_es->s_mount_opts[0])
return 0;
- strscpy_pad(s_mount_opts, sbi->s_es->s_mount_opts);
+ if (strscpy_pad(s_mount_opts, sbi->s_es->s_mount_opts) < 0)
+ return -E2BIG;
fc = kzalloc(sizeof(struct fs_context), GFP_KERNEL);
if (!fc)
@@ -4188,7 +4191,7 @@ int ext4_calculate_overhead(struct super_block *sb)
unsigned int j_blocks, j_inum = le32_to_cpu(es->s_journal_inum);
ext4_group_t i, ngroups = ext4_get_groups_count(sb);
ext4_fsblk_t overhead = 0;
- char *buf = (char *) get_zeroed_page(GFP_NOFS);
+ char *buf = kvmalloc(sb->s_blocksize, GFP_NOFS | __GFP_ZERO);
if (!buf)
return -ENOMEM;
@@ -4213,7 +4216,7 @@ int ext4_calculate_overhead(struct super_block *sb)
blks = count_overhead(sb, i, buf);
overhead += blks;
if (blks)
- memset(buf, 0, PAGE_SIZE);
+ memset(buf, 0, sb->s_blocksize);
cond_resched();
}
@@ -4236,7 +4239,7 @@ int ext4_calculate_overhead(struct super_block *sb)
}
sbi->s_overhead = overhead;
smp_wmb();
- free_page((unsigned long) buf);
+ kvfree(buf);
return 0;
}
@@ -4389,8 +4392,7 @@ static void ext4_set_def_opts(struct super_block *sb,
((def_mount_opts & EXT4_DEFM_NODELALLOC) == 0))
set_opt(sb, DELALLOC);
- if (sb->s_blocksize <= PAGE_SIZE)
- set_opt(sb, DIOREAD_NOLOCK);
+ set_opt(sb, DIOREAD_NOLOCK);
}
static int ext4_handle_clustersize(struct super_block *sb)
@@ -5040,6 +5042,41 @@ static const char *ext4_has_journal_option(struct super_block *sb)
return NULL;
}
+/*
+ * Limit the maximum folio order to 2048 blocks to prevent overestimation
+ * of reserve handle credits during the folio writeback in environments
+ * where the PAGE_SIZE exceeds 4KB.
+ */
+#define EXT4_MAX_PAGECACHE_ORDER(sb) \
+ umin(MAX_PAGECACHE_ORDER, (11 + (sb)->s_blocksize_bits - PAGE_SHIFT))
+static void ext4_set_max_mapping_order(struct super_block *sb)
+{
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
+
+ if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)
+ sbi->s_max_folio_order = sbi->s_min_folio_order;
+ else
+ sbi->s_max_folio_order = EXT4_MAX_PAGECACHE_ORDER(sb);
+}
+
+static int ext4_check_large_folio(struct super_block *sb)
+{
+ const char *err_str = NULL;
+
+ if (ext4_has_feature_encrypt(sb))
+ err_str = "encrypt";
+
+ if (!err_str) {
+ ext4_set_max_mapping_order(sb);
+ } else if (sb->s_blocksize > PAGE_SIZE) {
+ ext4_msg(sb, KERN_ERR, "bs(%lu) > ps(%lu) unsupported for %s",
+ sb->s_blocksize, PAGE_SIZE, err_str);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int ext4_load_super(struct super_block *sb, ext4_fsblk_t *lsb,
int silent)
{
@@ -5107,11 +5144,8 @@ static int ext4_load_super(struct super_block *sb, ext4_fsblk_t *lsb,
* If the default block size is not the same as the real block size,
* we need to reload it.
*/
- if (sb->s_blocksize == blocksize) {
- *lsb = logical_sb_block;
- sbi->s_sbh = bh;
- return 0;
- }
+ if (sb->s_blocksize == blocksize)
+ goto success;
/*
* bh must be released before kill_bdev(), otherwise
@@ -5142,6 +5176,9 @@ static int ext4_load_super(struct super_block *sb, ext4_fsblk_t *lsb,
ext4_msg(sb, KERN_ERR, "Magic mismatch, very weird!");
goto out;
}
+
+success:
+ sbi->s_min_folio_order = get_order(blocksize);
*lsb = logical_sb_block;
sbi->s_sbh = bh;
return 0;
@@ -5316,6 +5353,10 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
ext4_apply_options(fc, sb);
+ err = ext4_check_large_folio(sb);
+ if (err < 0)
+ goto failed_mount;
+
err = ext4_encoding_init(sb, es);
if (err)
goto failed_mount;
@@ -5842,7 +5883,7 @@ static int ext4_journal_bmap(journal_t *journal, sector_t *block)
ext4_msg(journal->j_inode->i_sb, KERN_CRIT,
"journal bmap failed: block %llu ret %d\n",
*block, ret);
- jbd2_journal_abort(journal, ret ? ret : -EIO);
+ jbd2_journal_abort(journal, ret ? ret : -EFSCORRUPTED);
return ret;
}
*block = map.m_pblk;
@@ -7412,7 +7453,8 @@ static struct file_system_type ext4_fs_type = {
.init_fs_context = ext4_init_fs_context,
.parameters = ext4_param_specs,
.kill_sb = ext4_kill_sb,
- .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP | FS_MGTIME,
+ .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP | FS_MGTIME |
+ FS_LBS,
};
MODULE_ALIAS_FS("ext4");
diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c
index 987bd00f916a..0018e09b867e 100644
--- a/fs/ext4/sysfs.c
+++ b/fs/ext4/sysfs.c
@@ -332,6 +332,9 @@ EXT4_ATTR_FEATURE(fast_commit);
#if IS_ENABLED(CONFIG_UNICODE) && defined(CONFIG_FS_ENCRYPTION)
EXT4_ATTR_FEATURE(encrypted_casefold);
#endif
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+EXT4_ATTR_FEATURE(blocksize_gt_pagesize);
+#endif
static struct attribute *ext4_feat_attrs[] = {
ATTR_LIST(lazy_itable_init),
@@ -352,6 +355,9 @@ static struct attribute *ext4_feat_attrs[] = {
#if IS_ENABLED(CONFIG_UNICODE) && defined(CONFIG_FS_ENCRYPTION)
ATTR_LIST(encrypted_casefold),
#endif
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ ATTR_LIST(blocksize_gt_pagesize),
+#endif
NULL,
};
ATTRIBUTE_GROUPS(ext4_feat);
diff --git a/fs/ext4/verity.c b/fs/ext4/verity.c
index b0acb0c50313..415d9c4d8a32 100644
--- a/fs/ext4/verity.c
+++ b/fs/ext4/verity.c
@@ -302,7 +302,7 @@ static int ext4_get_verity_descriptor_location(struct inode *inode,
end_lblk = le32_to_cpu(last_extent->ee_block) +
ext4_ext_get_actual_len(last_extent);
- desc_size_pos = (u64)end_lblk << inode->i_blkbits;
+ desc_size_pos = EXT4_LBLK_TO_B(inode, end_lblk);
ext4_free_ext_path(path);
if (desc_size_pos < sizeof(desc_size_disk))
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index ce7253b3f549..2e02efbddaac 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -1174,7 +1174,11 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,
if (block_csum)
end = (void *)bh->b_data + bh->b_size;
else {
- ext4_get_inode_loc(parent, &iloc);
+ err = ext4_get_inode_loc(parent, &iloc);
+ if (err) {
+ EXT4_ERROR_INODE(parent, "parent inode loc (error %d)", err);
+ return;
+ }
end = (void *)ext4_raw_inode(&iloc) + EXT4_SB(parent->i_sb)->s_inode_size;
}
diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c
index f6b12aebb8bb..f8c93dc45768 100644
--- a/fs/fuse/dev_uring.c
+++ b/fs/fuse/dev_uring.c
@@ -1209,14 +1209,15 @@ static void fuse_uring_send(struct fuse_ring_ent *ent, struct io_uring_cmd *cmd,
* User buffers are not mapped yet - the application does not have permission
* to write to it - this has to be executed in ring task context.
*/
-static void fuse_uring_send_in_task(struct io_uring_cmd *cmd,
- unsigned int issue_flags)
+static void fuse_uring_send_in_task(struct io_tw_req tw_req, io_tw_token_t tw)
{
+ unsigned int issue_flags = IO_URING_CMD_TASK_WORK_ISSUE_FLAGS;
+ struct io_uring_cmd *cmd = io_uring_cmd_from_tw(tw_req);
struct fuse_ring_ent *ent = uring_cmd_to_ring_ent(cmd);
struct fuse_ring_queue *queue = ent->queue;
int err;
- if (!(issue_flags & IO_URING_F_TASK_DEAD)) {
+ if (!tw.cancel) {
err = fuse_uring_prepare_send(ent, ent->fuse_req);
if (err) {
fuse_uring_next_fuse_req(ent, queue, issue_flags);
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index ff1cf335449a..e79ad087512a 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -431,7 +431,7 @@ static int gfs2_read_folio(struct file *file, struct folio *folio)
error = mpage_read_folio(folio, gfs2_block_map);
}
- if (gfs2_withdrawing_or_withdrawn(sdp))
+ if (gfs2_withdrawn(sdp))
return -EIO;
return error;
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index ee92f5910ae1..b2d23c98c996 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -1446,7 +1446,7 @@ static int gfs2_lock(struct file *file, int cmd, struct file_lock *fl)
if (!(fl->c.flc_flags & FL_POSIX))
return -ENOLCK;
- if (gfs2_withdrawing_or_withdrawn(sdp)) {
+ if (gfs2_withdrawn(sdp)) {
if (lock_is_unlock(fl))
locks_lock_file_wait(file, fl);
return -EIO;
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index c9712235e7a0..92e029104d8a 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -137,33 +137,6 @@ static void gfs2_glock_dealloc(struct rcu_head *rcu)
kmem_cache_free(gfs2_glock_cachep, gl);
}
-/**
- * glock_blocked_by_withdraw - determine if we can still use a glock
- * @gl: the glock
- *
- * We need to allow some glocks to be enqueued, dequeued, promoted, and demoted
- * when we're withdrawn. For example, to maintain metadata integrity, we should
- * disallow the use of inode and rgrp glocks when withdrawn. Other glocks like
- * the iopen or freeze glock may be safely used because none of their
- * metadata goes through the journal. So in general, we should disallow all
- * glocks that are journaled, and allow all the others. One exception is:
- * we need to allow our active journal to be promoted and demoted so others
- * may recover it and we can reacquire it when they're done.
- */
-static bool glock_blocked_by_withdraw(struct gfs2_glock *gl)
-{
- struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
-
- if (!gfs2_withdrawing_or_withdrawn(sdp))
- return false;
- if (gl->gl_ops->go_flags & GLOF_NONDISK)
- return false;
- if (!sdp->sd_jdesc ||
- gl->gl_name.ln_number == sdp->sd_jdesc->jd_no_addr)
- return false;
- return true;
-}
-
static void __gfs2_glock_free(struct gfs2_glock *gl)
{
rhashtable_remove_fast(&gl_hash_table, &gl->gl_node, ht_parms);
@@ -270,7 +243,7 @@ static void __gfs2_glock_put(struct gfs2_glock *gl)
GLOCK_BUG_ON(gl, !list_empty(&gl->gl_holders));
if (mapping) {
truncate_inode_pages_final(mapping);
- if (!gfs2_withdrawing_or_withdrawn(sdp))
+ if (!gfs2_withdrawn(sdp))
GLOCK_BUG_ON(gl, !mapping_empty(mapping));
}
trace_gfs2_glock_put(gl);
@@ -485,8 +458,14 @@ done:
static void do_promote(struct gfs2_glock *gl)
{
+ struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
struct gfs2_holder *gh, *current_gh;
+ if (gfs2_withdrawn(sdp)) {
+ do_error(gl, LM_OUT_ERROR);
+ return;
+ }
+
current_gh = find_first_holder(gl);
list_for_each_entry(gh, &gl->gl_holders, gh_list) {
if (test_bit(HIF_HOLDER, &gh->gh_iflags))
@@ -592,7 +571,6 @@ static void finish_xmote(struct gfs2_glock *gl, unsigned int ret)
state_change(gl, state);
}
-
/* Demote to UN request arrived during demote to SH or DF */
if (test_bit(GLF_DEMOTE_IN_PROGRESS, &gl->gl_flags) &&
gl->gl_state != LM_ST_UNLOCKED &&
@@ -663,16 +641,6 @@ out:
clear_bit(GLF_LOCK, &gl->gl_flags);
}
-static bool is_system_glock(struct gfs2_glock *gl)
-{
- struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
- struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode);
-
- if (gl == m_ip->i_gl)
- return true;
- return false;
-}
-
/**
* do_xmote - Calls the DLM to change the state of a lock
* @gl: The lock state
@@ -691,95 +659,47 @@ __acquires(&gl->gl_lockref.lock)
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
int ret;
- if (target != LM_ST_UNLOCKED && glock_blocked_by_withdraw(gl) &&
- gh && !(gh->gh_flags & LM_FLAG_NOEXP))
- goto skip_inval;
+ /*
+ * When a filesystem is withdrawing, the remaining cluster nodes will
+ * take care of recovering the withdrawing node's journal. We only
+ * need to make sure that once we trigger remote recovery, we won't
+ * write to the shared block device anymore. This means that here,
+ *
+ * - no new writes to the filesystem must be triggered (->go_sync()).
+ *
+ * - any cached data should be discarded by calling ->go_inval(), dirty
+ * or not and journaled or unjournaled.
+ *
+ * - no more dlm locking operations should be issued (->lm_lock()).
+ */
GLOCK_BUG_ON(gl, gl->gl_state == target);
GLOCK_BUG_ON(gl, gl->gl_state == gl->gl_target);
+
if (!glops->go_inval || !glops->go_sync)
goto skip_inval;
spin_unlock(&gl->gl_lockref.lock);
- ret = glops->go_sync(gl);
- /* If we had a problem syncing (due to io errors or whatever,
- * we should not invalidate the metadata or tell dlm to
- * release the glock to other nodes.
- */
- if (ret) {
- if (cmpxchg(&sdp->sd_log_error, 0, ret)) {
- fs_err(sdp, "Error %d syncing glock\n", ret);
- gfs2_dump_glock(NULL, gl, true);
+ if (!gfs2_withdrawn(sdp)) {
+ ret = glops->go_sync(gl);
+ if (ret) {
+ if (cmpxchg(&sdp->sd_log_error, 0, ret)) {
+ fs_err(sdp, "Error %d syncing glock\n", ret);
+ gfs2_dump_glock(NULL, gl, true);
+ gfs2_withdraw(sdp);
+ }
}
- spin_lock(&gl->gl_lockref.lock);
- goto skip_inval;
}
- if (target == LM_ST_UNLOCKED || target == LM_ST_DEFERRED) {
- /*
- * The call to go_sync should have cleared out the ail list.
- * If there are still items, we have a problem. We ought to
- * withdraw, but we can't because the withdraw code also uses
- * glocks. Warn about the error, dump the glock, then fall
- * through and wait for logd to do the withdraw for us.
- */
- if ((atomic_read(&gl->gl_ail_count) != 0) &&
- (!cmpxchg(&sdp->sd_log_error, 0, -EIO))) {
- gfs2_glock_assert_warn(gl,
- !atomic_read(&gl->gl_ail_count));
- gfs2_dump_glock(NULL, gl, true);
- }
+ if (target == LM_ST_UNLOCKED || target == LM_ST_DEFERRED)
glops->go_inval(gl, target == LM_ST_DEFERRED ? 0 : DIO_METADATA);
- }
spin_lock(&gl->gl_lockref.lock);
skip_inval:
- /*
- * Check for an error encountered since we called go_sync and go_inval.
- * If so, we can't withdraw from the glock code because the withdraw
- * code itself uses glocks (see function signal_our_withdraw) to
- * change the mount to read-only. Most importantly, we must not call
- * dlm to unlock the glock until the journal is in a known good state
- * (after journal replay) otherwise other nodes may use the object
- * (rgrp or dinode) and then later, journal replay will corrupt the
- * file system. The best we can do here is wait for the logd daemon
- * to see sd_log_error and withdraw, and in the meantime, requeue the
- * work for later.
- *
- * We make a special exception for some system glocks, such as the
- * system statfs inode glock, which needs to be granted before the
- * gfs2_quotad daemon can exit, and that exit needs to finish before
- * we can unmount the withdrawn file system.
- *
- * However, if we're just unlocking the lock (say, for unmount, when
- * gfs2_gl_hash_clear calls clear_glock) and recovery is complete
- * then it's okay to tell dlm to unlock it.
- */
- if (unlikely(sdp->sd_log_error) && !gfs2_withdrawing_or_withdrawn(sdp))
- gfs2_withdraw_delayed(sdp);
- if (glock_blocked_by_withdraw(gl) &&
- (target != LM_ST_UNLOCKED ||
- test_bit(SDF_WITHDRAW_RECOVERY, &sdp->sd_flags))) {
- if (!is_system_glock(gl)) {
- request_demote(gl, LM_ST_UNLOCKED, 0, false);
- /*
- * Ordinarily, we would call dlm and its callback would call
- * finish_xmote, which would call state_change() to the new state.
- * Since we withdrew, we won't call dlm, so call state_change
- * manually, but to the UNLOCKED state we desire.
- */
- state_change(gl, LM_ST_UNLOCKED);
- /*
- * We skip telling dlm to do the locking, so we won't get a
- * reply that would otherwise clear GLF_LOCK. So we clear it here.
- */
- if (!test_bit(GLF_CANCELING, &gl->gl_flags))
- clear_bit(GLF_LOCK, &gl->gl_flags);
- clear_bit(GLF_DEMOTE_IN_PROGRESS, &gl->gl_flags);
- gl->gl_lockref.count++;
- gfs2_glock_queue_work(gl, GL_GLOCK_DFT_HOLD);
- return;
- }
+ if (gfs2_withdrawn(sdp)) {
+ if (target != LM_ST_UNLOCKED)
+ target = LM_OUT_ERROR;
+ goto out;
}
if (ls->ls_ops->lm_lock) {
@@ -795,19 +715,23 @@ skip_inval:
}
clear_bit(GLF_PENDING_REPLY, &gl->gl_flags);
- if (ret == -ENODEV && gl->gl_target == LM_ST_UNLOCKED &&
- target == LM_ST_UNLOCKED) {
+ if (ret == -ENODEV) {
/*
* The lockspace has been released and the lock has
* been unlocked implicitly.
*/
+ if (target != LM_ST_UNLOCKED) {
+ target = LM_OUT_ERROR;
+ goto out;
+ }
} else {
fs_err(sdp, "lm_lock ret %d\n", ret);
- GLOCK_BUG_ON(gl, !gfs2_withdrawing_or_withdrawn(sdp));
+ GLOCK_BUG_ON(gl, !gfs2_withdrawn(sdp));
return;
}
}
+out:
/* Complete the operation now. */
finish_xmote(gl, target);
gl->gl_lockref.count++;
@@ -966,14 +890,14 @@ static struct gfs2_inode *gfs2_grab_existing_inode(struct gfs2_glock *gl)
return ip;
}
-static void gfs2_try_evict(struct gfs2_glock *gl)
+static void gfs2_try_to_evict(struct gfs2_glock *gl)
{
struct gfs2_inode *ip;
/*
* If there is contention on the iopen glock and we have an inode, try
* to grab and release the inode so that it can be evicted. The
- * GIF_DEFER_DELETE flag indicates to gfs2_evict_inode() that the inode
+ * GLF_DEFER_DELETE flag indicates to gfs2_evict_inode() that the inode
* should not be deleted locally. This will allow the remote node to
* go ahead and delete the inode without us having to do it, which will
* avoid rgrp glock thrashing.
@@ -1026,8 +950,14 @@ static void delete_work_func(struct work_struct *work)
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
bool verify_delete = test_and_clear_bit(GLF_VERIFY_DELETE, &gl->gl_flags);
+ /*
+ * Check for the GLF_VERIFY_DELETE above: this ensures that we won't
+ * immediately process GLF_VERIFY_DELETE work that the below call to
+ * gfs2_try_to_evict() queues.
+ */
+
if (test_and_clear_bit(GLF_TRY_TO_EVICT, &gl->gl_flags))
- gfs2_try_evict(gl);
+ gfs2_try_to_evict(gl);
if (verify_delete) {
u64 no_addr = gl->gl_name.ln_number;
@@ -1211,10 +1141,13 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number,
mapping = gfs2_glock2aspace(gl);
if (mapping) {
+ gfp_t gfp_mask;
+
mapping->a_ops = &gfs2_meta_aops;
mapping->host = sdp->sd_inode;
mapping->flags = 0;
- mapping_set_gfp_mask(mapping, GFP_NOFS);
+ gfp_mask = mapping_gfp_mask(sdp->sd_inode->i_mapping);
+ mapping_set_gfp_mask(mapping, gfp_mask);
mapping->i_private_data = NULL;
mapping->writeback_index = 0;
}
@@ -1241,7 +1174,7 @@ found:
* @state: the state we're requesting
* @flags: the modifier flags
* @gh: the holder structure
- *
+ * @ip: caller's return address for debugging
*/
void __gfs2_holder_init(struct gfs2_glock *gl, unsigned int state, u16 flags,
@@ -1539,9 +1472,10 @@ trap_recursive:
int gfs2_glock_nq(struct gfs2_holder *gh)
{
struct gfs2_glock *gl = gh->gh_gl;
+ struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
int error;
- if (glock_blocked_by_withdraw(gl) && !(gh->gh_flags & LM_FLAG_NOEXP))
+ if (gfs2_withdrawn(sdp))
return -EIO;
if (gh->gh_flags & GL_NOBLOCK) {
@@ -1566,7 +1500,7 @@ unlock:
gh->gh_error = 0;
spin_lock(&gl->gl_lockref.lock);
add_to_queue(gh);
- if (unlikely((LM_FLAG_NOEXP & gh->gh_flags) &&
+ if (unlikely((LM_FLAG_RECOVER & gh->gh_flags) &&
test_and_clear_bit(GLF_HAVE_FROZEN_REPLY, &gl->gl_flags))) {
set_bit(GLF_HAVE_REPLY, &gl->gl_flags);
gl->gl_lockref.count++;
@@ -1639,7 +1573,6 @@ static void __gfs2_glock_dq(struct gfs2_holder *gh)
void gfs2_glock_dq(struct gfs2_holder *gh)
{
struct gfs2_glock *gl = gh->gh_gl;
- struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
spin_lock(&gl->gl_lockref.lock);
if (!gfs2_holder_queued(gh)) {
@@ -1666,24 +1599,6 @@ void gfs2_glock_dq(struct gfs2_holder *gh)
goto out;
}
- /*
- * If we're in the process of file system withdraw, we cannot just
- * dequeue any glocks until our journal is recovered, lest we introduce
- * file system corruption. We need two exceptions to this rule: We need
- * to allow unlocking of nondisk glocks and the glock for our own
- * journal that needs recovery.
- */
- if (test_bit(SDF_WITHDRAW_RECOVERY, &sdp->sd_flags) &&
- glock_blocked_by_withdraw(gl) &&
- gh->gh_gl != sdp->sd_jinode_gl) {
- sdp->sd_glock_dqs_held++;
- spin_unlock(&gl->gl_lockref.lock);
- might_sleep();
- wait_on_bit(&sdp->sd_flags, SDF_WITHDRAW_RECOVERY,
- TASK_UNINTERRUPTIBLE);
- spin_lock(&gl->gl_lockref.lock);
- }
-
__gfs2_glock_dq(gh);
out:
spin_unlock(&gl->gl_lockref.lock);
@@ -1871,7 +1786,7 @@ void gfs2_glock_cb(struct gfs2_glock *gl, unsigned int state)
*
* Glocks are not frozen if (a) the result of the dlm operation is
* an error, (b) the locking operation was an unlock operation or
- * (c) if there is a "noexp" flagged request anywhere in the queue
+ * (c) if there is a "recover" flagged request anywhere in the queue
*
* Returns: 1 if freezing should occur, 0 otherwise
*/
@@ -1888,7 +1803,7 @@ static int gfs2_should_freeze(const struct gfs2_glock *gl)
list_for_each_entry(gh, &gl->gl_holders, gh_list) {
if (test_bit(HIF_HOLDER, &gh->gh_iflags))
continue;
- if (LM_FLAG_NOEXP & gh->gh_flags)
+ if (LM_FLAG_RECOVER & gh->gh_flags)
return 0;
}
@@ -2165,18 +2080,26 @@ static void dump_glock_func(struct gfs2_glock *gl)
dump_glock(NULL, gl, true);
}
-static void withdraw_dq(struct gfs2_glock *gl)
+static void withdraw_glock(struct gfs2_glock *gl)
{
spin_lock(&gl->gl_lockref.lock);
- if (!__lockref_is_dead(&gl->gl_lockref) &&
- glock_blocked_by_withdraw(gl))
+ if (!__lockref_is_dead(&gl->gl_lockref)) {
+ /*
+ * We don't want to write back any more dirty data. Unlock the
+ * remaining inode and resource group glocks; this will cause
+ * their ->go_inval() hooks to toss out all the remaining
+ * cached data, dirty or not.
+ */
+ if (gl->gl_ops->go_inval && gl->gl_state != LM_ST_UNLOCKED)
+ request_demote(gl, LM_ST_UNLOCKED, 0, false);
do_error(gl, LM_OUT_ERROR); /* remove pending waiters */
+ }
spin_unlock(&gl->gl_lockref.lock);
}
-void gfs2_gl_dq_holders(struct gfs2_sbd *sdp)
+void gfs2_withdraw_glocks(struct gfs2_sbd *sdp)
{
- glock_hash_walk(withdraw_dq, sdp);
+ glock_hash_walk(withdraw_glock, sdp);
}
/**
@@ -2237,7 +2160,7 @@ static const char *hflags2str(char *buf, u16 flags, unsigned long iflags)
*p++ = 't';
if (flags & LM_FLAG_TRY_1CB)
*p++ = 'T';
- if (flags & LM_FLAG_NOEXP)
+ if (flags & LM_FLAG_RECOVER)
*p++ = 'e';
if (flags & LM_FLAG_ANY)
*p++ = 'A';
@@ -2324,8 +2247,6 @@ static const char *gflags2str(char *buf, const struct gfs2_glock *gl)
*p++ = 'o';
if (test_bit(GLF_BLOCKING, gflags))
*p++ = 'b';
- if (test_bit(GLF_UNLOCKED, gflags))
- *p++ = 'x';
if (test_bit(GLF_INSTANTIATE_NEEDED, gflags))
*p++ = 'n';
if (test_bit(GLF_INSTANTIATE_IN_PROG, gflags))
diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h
index d041b922b45e..55d5985f32a0 100644
--- a/fs/gfs2/glock.h
+++ b/fs/gfs2/glock.h
@@ -58,10 +58,10 @@ enum {
* LM_FLAG_TRY_1CB
* Send one blocking callback if TRY is set and the lock is not granted.
*
- * LM_FLAG_NOEXP
+ * LM_FLAG_RECOVER
* GFS sets this flag on lock requests it makes while doing journal recovery.
- * These special requests should not be blocked due to the recovery like
- * ordinary locks would be.
+ * While ordinary requests are blocked until the end of recovery, requests
+ * with this flag set do proceed.
*
* LM_FLAG_ANY
* A SHARED request may also be granted in DEFERRED, or a DEFERRED request may
@@ -80,7 +80,7 @@ enum {
#define LM_FLAG_TRY 0x0001
#define LM_FLAG_TRY_1CB 0x0002
-#define LM_FLAG_NOEXP 0x0004
+#define LM_FLAG_RECOVER 0x0004
#define LM_FLAG_ANY 0x0008
#define LM_FLAG_NODE_SCOPE 0x0020
#define GL_ASYNC 0x0040
@@ -136,7 +136,7 @@ struct lm_lockops {
void (*lm_first_done) (struct gfs2_sbd *sdp);
void (*lm_recovery_result) (struct gfs2_sbd *sdp, unsigned int jid,
unsigned int result);
- void (*lm_unmount) (struct gfs2_sbd *sdp);
+ void (*lm_unmount) (struct gfs2_sbd *sdp, bool clean);
void (*lm_withdraw) (struct gfs2_sbd *sdp);
void (*lm_put_lock) (struct gfs2_glock *gl);
int (*lm_lock) (struct gfs2_glock *gl, unsigned int req_state,
@@ -263,7 +263,7 @@ bool gfs2_queue_verify_delete(struct gfs2_glock *gl, bool later);
void gfs2_cancel_delete_work(struct gfs2_glock *gl);
void gfs2_flush_delete_work(struct gfs2_sbd *sdp);
void gfs2_gl_hash_clear(struct gfs2_sbd *sdp);
-void gfs2_gl_dq_holders(struct gfs2_sbd *sdp);
+void gfs2_withdraw_glocks(struct gfs2_sbd *sdp);
void gfs2_glock_thaw(struct gfs2_sbd *sdp);
void gfs2_glock_free(struct gfs2_glock *gl);
void gfs2_glock_free_later(struct gfs2_glock *gl);
diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c
index c94e42b0c94d..2173ccf5034b 100644
--- a/fs/gfs2/glops.c
+++ b/fs/gfs2/glops.c
@@ -30,8 +30,6 @@
struct workqueue_struct *gfs2_freeze_wq;
-extern struct workqueue_struct *gfs2_control_wq;
-
static void gfs2_ail_error(struct gfs2_glock *gl, const struct buffer_head *bh)
{
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
@@ -45,7 +43,7 @@ static void gfs2_ail_error(struct gfs2_glock *gl, const struct buffer_head *bh)
gl->gl_name.ln_type, gl->gl_name.ln_number,
gfs2_glock2aspace(gl));
gfs2_lm(sdp, "AIL error\n");
- gfs2_withdraw_delayed(sdp);
+ gfs2_withdraw(sdp);
}
/**
@@ -83,9 +81,6 @@ static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync,
GLOCK_BUG_ON(gl, !fsync && atomic_read(&gl->gl_ail_count));
spin_unlock(&sdp->sd_ail_lock);
gfs2_log_unlock(sdp);
-
- if (gfs2_withdrawing(sdp))
- gfs2_withdraw(sdp);
}
@@ -178,7 +173,7 @@ static int gfs2_rgrp_metasync(struct gfs2_glock *gl)
filemap_fdatawrite_range(metamapping, start, end);
error = filemap_fdatawait_range(metamapping, start, end);
- WARN_ON_ONCE(error && !gfs2_withdrawing_or_withdrawn(sdp));
+ WARN_ON_ONCE(error && !gfs2_withdrawn(sdp));
mapping_set_error(metamapping, error);
if (error)
gfs2_io_error(sdp);
@@ -237,6 +232,7 @@ static void rgrp_go_inval(struct gfs2_glock *gl, int flags)
end = PAGE_ALIGN((rgd->rd_addr + rgd->rd_length) * bsize) - 1;
gfs2_rgrp_brelse(rgd);
WARN_ON_ONCE(!(flags & DIO_METADATA));
+ gfs2_assert_withdraw(sdp, !atomic_read(&gl->gl_ail_count));
truncate_inode_pages_range(mapping, start, end);
}
@@ -363,6 +359,8 @@ static void inode_go_inval(struct gfs2_glock *gl, int flags)
{
struct gfs2_inode *ip = gfs2_glock2inode(gl);
+ gfs2_assert_withdraw(gl->gl_name.ln_sbd, !atomic_read(&gl->gl_ail_count));
+
if (flags & DIO_METADATA) {
struct address_space *mapping = gfs2_glock2aspace(gl);
truncate_inode_pages(mapping, 0);
@@ -608,10 +606,10 @@ static int freeze_go_xmote_bh(struct gfs2_glock *gl)
j_gl->gl_ops->go_inval(j_gl, DIO_METADATA);
error = gfs2_find_jhead(sdp->sd_jdesc, &head);
- if (gfs2_assert_withdraw_delayed(sdp, !error))
+ if (gfs2_assert_withdraw(sdp, !error))
return error;
- if (gfs2_assert_withdraw_delayed(sdp, head.lh_flags &
- GFS2_LOG_HEAD_UNMOUNT))
+ if (gfs2_assert_withdraw(sdp, head.lh_flags &
+ GFS2_LOG_HEAD_UNMOUNT))
return -EIO;
gfs2_log_pointers_init(sdp, &head);
}
@@ -630,8 +628,7 @@ static void iopen_go_callback(struct gfs2_glock *gl, bool remote)
struct gfs2_inode *ip = gl->gl_object;
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
- if (!remote || sb_rdonly(sdp->sd_vfs) ||
- test_bit(SDF_KILL, &sdp->sd_flags))
+ if (!remote || test_bit(SDF_KILL, &sdp->sd_flags))
return;
if (gl->gl_demote_state == LM_ST_UNLOCKED &&
@@ -642,76 +639,8 @@ static void iopen_go_callback(struct gfs2_glock *gl, bool remote)
}
}
-/**
- * inode_go_unlocked - wake up anyone waiting for dlm's unlock ast
- * @gl: glock being unlocked
- *
- * For now, this is only used for the journal inode glock. In withdraw
- * situations, we need to wait for the glock to be unlocked so that we know
- * other nodes may proceed with recovery / journal replay.
- */
-static void inode_go_unlocked(struct gfs2_glock *gl)
-{
- /* Note that we cannot reference gl_object because it's already set
- * to NULL by this point in its lifecycle. */
- if (!test_bit(GLF_UNLOCKED, &gl->gl_flags))
- return;
- clear_bit_unlock(GLF_UNLOCKED, &gl->gl_flags);
- wake_up_bit(&gl->gl_flags, GLF_UNLOCKED);
-}
-
-/**
- * nondisk_go_callback - used to signal when a node did a withdraw
- * @gl: the nondisk glock
- * @remote: true if this came from a different cluster node
- *
- */
-static void nondisk_go_callback(struct gfs2_glock *gl, bool remote)
-{
- struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
-
- /* Ignore the callback unless it's from another node, and it's the
- live lock. */
- if (!remote || gl->gl_name.ln_number != GFS2_LIVE_LOCK)
- return;
-
- /* First order of business is to cancel the demote request. We don't
- * really want to demote a nondisk glock. At best it's just to inform
- * us of another node's withdraw. We'll keep it in SH mode. */
- clear_bit(GLF_DEMOTE, &gl->gl_flags);
- clear_bit(GLF_PENDING_DEMOTE, &gl->gl_flags);
-
- /* Ignore the unlock if we're withdrawn, unmounting, or in recovery. */
- if (test_bit(SDF_NORECOVERY, &sdp->sd_flags) ||
- test_bit(SDF_WITHDRAWN, &sdp->sd_flags) ||
- test_bit(SDF_REMOTE_WITHDRAW, &sdp->sd_flags))
- return;
-
- /* We only care when a node wants us to unlock, because that means
- * they want a journal recovered. */
- if (gl->gl_demote_state != LM_ST_UNLOCKED)
- return;
-
- if (sdp->sd_args.ar_spectator) {
- fs_warn(sdp, "Spectator node cannot recover journals.\n");
- return;
- }
-
- fs_warn(sdp, "Some node has withdrawn; checking for recovery.\n");
- set_bit(SDF_REMOTE_WITHDRAW, &sdp->sd_flags);
- /*
- * We can't call remote_withdraw directly here or gfs2_recover_journal
- * because this is called from the glock unlock function and the
- * remote_withdraw needs to enqueue and dequeue the same "live" glock
- * we were called from. So we queue it to the control work queue in
- * lock_dlm.
- */
- queue_delayed_work(gfs2_control_wq, &sdp->sd_control_work, 0);
-}
-
const struct gfs2_glock_operations gfs2_meta_glops = {
.go_type = LM_TYPE_META,
- .go_flags = GLOF_NONDISK,
};
const struct gfs2_glock_operations gfs2_inode_glops = {
@@ -722,7 +651,6 @@ const struct gfs2_glock_operations gfs2_inode_glops = {
.go_dump = inode_go_dump,
.go_type = LM_TYPE_INODE,
.go_flags = GLOF_ASPACE | GLOF_LVB,
- .go_unlocked = inode_go_unlocked,
};
const struct gfs2_glock_operations gfs2_rgrp_glops = {
@@ -738,36 +666,30 @@ const struct gfs2_glock_operations gfs2_freeze_glops = {
.go_xmote_bh = freeze_go_xmote_bh,
.go_callback = freeze_go_callback,
.go_type = LM_TYPE_NONDISK,
- .go_flags = GLOF_NONDISK,
};
const struct gfs2_glock_operations gfs2_iopen_glops = {
.go_type = LM_TYPE_IOPEN,
.go_callback = iopen_go_callback,
.go_dump = inode_go_dump,
- .go_flags = GLOF_NONDISK,
.go_subclass = 1,
};
const struct gfs2_glock_operations gfs2_flock_glops = {
.go_type = LM_TYPE_FLOCK,
- .go_flags = GLOF_NONDISK,
};
const struct gfs2_glock_operations gfs2_nondisk_glops = {
.go_type = LM_TYPE_NONDISK,
- .go_flags = GLOF_NONDISK,
- .go_callback = nondisk_go_callback,
};
const struct gfs2_glock_operations gfs2_quota_glops = {
.go_type = LM_TYPE_QUOTA,
- .go_flags = GLOF_LVB | GLOF_NONDISK,
+ .go_flags = GLOF_LVB,
};
const struct gfs2_glock_operations gfs2_journal_glops = {
.go_type = LM_TYPE_JOURNAL,
- .go_flags = GLOF_NONDISK,
};
const struct gfs2_glock_operations *gfs2_glops_list[] = {
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index 5a0ea416cfda..d05d8fe4e456 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -223,13 +223,11 @@ struct gfs2_glock_operations {
void (*go_dump)(struct seq_file *seq, const struct gfs2_glock *gl,
const char *fs_id_buf);
void (*go_callback)(struct gfs2_glock *gl, bool remote);
- void (*go_unlocked)(struct gfs2_glock *gl);
const int go_subclass;
const int go_type;
const unsigned long go_flags;
#define GLOF_ASPACE 1 /* address space attached */
#define GLOF_LVB 2 /* Lock Value Block attached */
-#define GLOF_NONDISK 8 /* not I/O related */
};
enum {
@@ -326,7 +324,6 @@ enum {
GLF_LRU = 13,
GLF_OBJECT = 14, /* Used only for tracing */
GLF_BLOCKING = 15,
- GLF_UNLOCKED = 16, /* Wait for glock to be unlocked */
GLF_TRY_TO_EVICT = 17, /* iopen glocks only */
GLF_VERIFY_DELETE = 18, /* iopen glocks only */
GLF_PENDING_REPLY = 19,
@@ -520,8 +517,6 @@ struct gfs2_jdesc {
struct list_head jd_revoke_list;
unsigned int jd_replay_tail;
-
- u64 jd_no_addr;
};
struct gfs2_statfs_change_host {
@@ -542,8 +537,7 @@ struct gfs2_statfs_change_host {
#define GFS2_ERRORS_DEFAULT GFS2_ERRORS_WITHDRAW
#define GFS2_ERRORS_WITHDRAW 0
-#define GFS2_ERRORS_CONTINUE 1 /* place holder for future feature */
-#define GFS2_ERRORS_RO 2 /* place holder for future feature */
+#define GFS2_ERRORS_DEACTIVATE 1
#define GFS2_ERRORS_PANIC 3
struct gfs2_args {
@@ -559,7 +553,7 @@ struct gfs2_args {
unsigned int ar_data:2; /* ordered/writeback */
unsigned int ar_meta:1; /* mount metafs */
unsigned int ar_discard:1; /* discard requests */
- unsigned int ar_errors:2; /* errors=withdraw | panic */
+ unsigned int ar_errors:2; /* errors=withdraw | deactivate | panic */
unsigned int ar_nobarrier:1; /* do not send barriers */
unsigned int ar_rgrplvb:1; /* use lvbs for rgrp info */
unsigned int ar_got_rgrplvb:1; /* Was the rgrplvb opt given? */
@@ -585,6 +579,7 @@ struct gfs2_tune {
unsigned int gt_complain_secs;
unsigned int gt_statfs_quantum;
unsigned int gt_statfs_slow;
+ unsigned int gt_withdraw_helper_timeout;
};
enum {
@@ -599,11 +594,6 @@ enum {
SDF_SKIP_DLM_UNLOCK = 8,
SDF_FORCE_AIL_FLUSH = 9,
SDF_FREEZE_INITIATOR = 10,
- SDF_WITHDRAWING = 11, /* Will withdraw eventually */
- SDF_WITHDRAW_IN_PROG = 12, /* Withdraw is in progress */
- SDF_REMOTE_WITHDRAW = 13, /* Performing remote recovery */
- SDF_WITHDRAW_RECOVERY = 14, /* Wait for journal recovery when we are
- withdrawing */
SDF_KILL = 15,
SDF_EVICTING = 16,
SDF_FROZEN = 17,
@@ -716,11 +706,13 @@ struct gfs2_sbd {
struct gfs2_glock *sd_rename_gl;
struct gfs2_glock *sd_freeze_gl;
struct work_struct sd_freeze_work;
+ struct work_struct sd_withdraw_work;
wait_queue_head_t sd_kill_wait;
wait_queue_head_t sd_async_glock_wait;
atomic_t sd_glock_disposal;
struct completion sd_locking_init;
- struct completion sd_wdack;
+ struct completion sd_withdraw_helper;
+ int sd_withdraw_helper_status;
struct delayed_work sd_control_work;
/* Inode Stuff */
@@ -761,7 +753,6 @@ struct gfs2_sbd {
struct gfs2_jdesc *sd_jdesc;
struct gfs2_holder sd_journal_gh;
struct gfs2_holder sd_jinode_gh;
- struct gfs2_glock *sd_jinode_gl;
struct gfs2_holder sd_sc_gh;
struct buffer_head *sd_sc_bh;
@@ -846,7 +837,6 @@ struct gfs2_sbd {
unsigned long sd_last_warning;
struct dentry *debugfs_dir; /* debugfs directory */
- unsigned long sd_glock_dqs_held;
};
#define GFS2_BAD_INO 1
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 890c87e3e365..36618e353199 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -89,6 +89,19 @@ static int iget_set(struct inode *inode, void *opaque)
return 0;
}
+void gfs2_setup_inode(struct inode *inode)
+{
+ gfp_t gfp_mask;
+
+ /*
+ * Ensure all page cache allocations are done from GFP_NOFS context to
+ * prevent direct reclaim recursion back into the filesystem and blowing
+ * stacks or deadlocking.
+ */
+ gfp_mask = mapping_gfp_mask(inode->i_mapping);
+ mapping_set_gfp_mask(inode->i_mapping, gfp_mask & ~__GFP_FS);
+}
+
/**
* gfs2_inode_lookup - Lookup an inode
* @sb: The super block
@@ -132,6 +145,7 @@ struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type,
struct gfs2_glock *io_gl;
int extra_flags = 0;
+ gfs2_setup_inode(inode);
error = gfs2_glock_get(sdp, no_addr, &gfs2_inode_glops, CREATE,
&ip->i_gl);
if (unlikely(error))
@@ -752,6 +766,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
error = -ENOMEM;
if (!inode)
goto fail_gunlock;
+ gfs2_setup_inode(inode);
ip = GFS2_I(inode);
error = posix_acl_create(dir, &mode, &default_acl, &acl);
diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h
index e43f08eb26e7..2fcd96dd1361 100644
--- a/fs/gfs2/inode.h
+++ b/fs/gfs2/inode.h
@@ -86,6 +86,7 @@ err:
return -EIO;
}
+void gfs2_setup_inode(struct inode *inode);
struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type,
u64 no_addr, u64 no_formal_ino,
unsigned int blktype);
diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c
index 4f00af7dd256..b8d249925395 100644
--- a/fs/gfs2/lock_dlm.c
+++ b/fs/gfs2/lock_dlm.c
@@ -15,9 +15,6 @@
#include <linux/sched/signal.h>
#include "incore.h"
-#include "glock.h"
-#include "glops.h"
-#include "recovery.h"
#include "util.h"
#include "sys.h"
#include "trace_gfs2.h"
@@ -139,8 +136,6 @@ static void gdlm_ast(void *arg)
switch (gl->gl_lksb.sb_status) {
case -DLM_EUNLOCK: /* Unlocked, so glock can be freed */
- if (gl->gl_ops->go_unlocked)
- gl->gl_ops->go_unlocked(gl);
gfs2_glock_free(gl);
return;
case -DLM_ECANCEL: /* Cancel while getting lock */
@@ -399,7 +394,6 @@ static void gdlm_cancel(struct gfs2_glock *gl)
/*
* dlm/gfs2 recovery coordination using dlm_recover callbacks
*
- * 0. gfs2 checks for another cluster node withdraw, needing journal replay
* 1. dlm_controld sees lockspace members change
* 2. dlm_controld blocks dlm-kernel locking activity
* 3. dlm_controld within dlm-kernel notifies gfs2 (recover_prep)
@@ -657,28 +651,6 @@ static int control_lock(struct gfs2_sbd *sdp, int mode, uint32_t flags)
&ls->ls_control_lksb, "control_lock");
}
-/**
- * remote_withdraw - react to a node withdrawing from the file system
- * @sdp: The superblock
- */
-static void remote_withdraw(struct gfs2_sbd *sdp)
-{
- struct gfs2_jdesc *jd;
- int ret = 0, count = 0;
-
- list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) {
- if (jd->jd_jid == sdp->sd_lockstruct.ls_jid)
- continue;
- ret = gfs2_recover_journal(jd, true);
- if (ret)
- break;
- count++;
- }
-
- /* Now drop the additional reference we acquired */
- fs_err(sdp, "Journals checked: %d, ret = %d.\n", count, ret);
-}
-
static void gfs2_control_func(struct work_struct *work)
{
struct gfs2_sbd *sdp = container_of(work, struct gfs2_sbd, sd_control_work.work);
@@ -689,13 +661,6 @@ static void gfs2_control_func(struct work_struct *work)
int recover_size;
int i, error;
- /* First check for other nodes that may have done a withdraw. */
- if (test_bit(SDF_REMOTE_WITHDRAW, &sdp->sd_flags)) {
- remote_withdraw(sdp);
- clear_bit(SDF_REMOTE_WITHDRAW, &sdp->sd_flags);
- return;
- }
-
spin_lock(&ls->ls_recover_spin);
/*
* No MOUNT_DONE means we're still mounting; control_mount()
@@ -1195,7 +1160,7 @@ static void gdlm_recover_prep(void *arg)
struct gfs2_sbd *sdp = arg;
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
- if (gfs2_withdrawing_or_withdrawn(sdp)) {
+ if (gfs2_withdrawn(sdp)) {
fs_err(sdp, "recover_prep ignored due to withdraw.\n");
return;
}
@@ -1221,7 +1186,7 @@ static void gdlm_recover_slot(void *arg, struct dlm_slot *slot)
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
int jid = slot->slot - 1;
- if (gfs2_withdrawing_or_withdrawn(sdp)) {
+ if (gfs2_withdrawn(sdp)) {
fs_err(sdp, "recover_slot jid %d ignored due to withdraw.\n",
jid);
return;
@@ -1250,7 +1215,7 @@ static void gdlm_recover_done(void *arg, struct dlm_slot *slots, int num_slots,
struct gfs2_sbd *sdp = arg;
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
- if (gfs2_withdrawing_or_withdrawn(sdp)) {
+ if (gfs2_withdrawn(sdp)) {
fs_err(sdp, "recover_done ignored due to withdraw.\n");
return;
}
@@ -1281,7 +1246,7 @@ static void gdlm_recovery_result(struct gfs2_sbd *sdp, unsigned int jid,
{
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
- if (gfs2_withdrawing_or_withdrawn(sdp)) {
+ if (gfs2_withdrawn(sdp)) {
fs_err(sdp, "recovery_result jid %d ignored due to withdraw.\n",
jid);
return;
@@ -1438,7 +1403,15 @@ static void gdlm_first_done(struct gfs2_sbd *sdp)
fs_err(sdp, "mount first_done error %d\n", error);
}
-static void gdlm_unmount(struct gfs2_sbd *sdp)
+/*
+ * gdlm_unmount - release our lockspace
+ * @sdp: the superblock
+ * @clean: Indicates whether or not the remaining nodes in the cluster should
+ * perform recovery. Recovery is necessary when a node withdraws and
+ * its journal remains dirty. Recovery isn't necessary when a node
+ * cleanly unmounts a filesystem.
+ */
+static void gdlm_unmount(struct gfs2_sbd *sdp, bool clean)
{
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
@@ -1456,7 +1429,9 @@ static void gdlm_unmount(struct gfs2_sbd *sdp)
release:
down_write(&ls->ls_sem);
if (ls->ls_dlm) {
- dlm_release_lockspace(ls->ls_dlm, DLM_RELEASE_NORMAL);
+ dlm_release_lockspace(ls->ls_dlm,
+ clean ? DLM_RELEASE_NORMAL :
+ DLM_RELEASE_RECOVER);
ls->ls_dlm = NULL;
}
up_write(&ls->ls_sem);
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index 115c4ac457e9..8312cd2cdae4 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -112,13 +112,11 @@ __acquires(&sdp->sd_ail_lock)
&tr->tr_ail2_list);
continue;
}
- if (!cmpxchg(&sdp->sd_log_error, 0, -EIO)) {
+ if (!cmpxchg(&sdp->sd_log_error, 0, -EIO))
gfs2_io_error_bh(sdp, bh);
- gfs2_withdraw_delayed(sdp);
- }
}
- if (gfs2_withdrawing_or_withdrawn(sdp)) {
+ if (gfs2_withdrawn(sdp)) {
gfs2_remove_from_ail(bd);
continue;
}
@@ -324,10 +322,8 @@ static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_trans *tr,
continue;
}
if (!buffer_uptodate(bh) &&
- !cmpxchg(&sdp->sd_log_error, 0, -EIO)) {
+ !cmpxchg(&sdp->sd_log_error, 0, -EIO))
gfs2_io_error_bh(sdp, bh);
- gfs2_withdraw_delayed(sdp);
- }
/*
* If we have space for revokes and the bd is no longer on any
* buf list, we can just add a revoke for it immediately and
@@ -807,9 +803,6 @@ void gfs2_flush_revokes(struct gfs2_sbd *sdp)
gfs2_log_lock(sdp);
gfs2_ail1_empty(sdp, max_revokes);
gfs2_log_unlock(sdp);
-
- if (gfs2_withdrawing(sdp))
- gfs2_withdraw(sdp);
}
/**
@@ -837,7 +830,7 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
struct super_block *sb = sdp->sd_vfs;
u64 dblock;
- if (gfs2_withdrawing_or_withdrawn(sdp))
+ if (gfs2_withdrawn(sdp))
return;
page = mempool_alloc(gfs2_page_pool, GFP_NOIO);
@@ -984,12 +977,9 @@ static void empty_ail1_list(struct gfs2_sbd *sdp)
gfs2_ail1_wait(sdp);
empty = gfs2_ail1_empty(sdp, 0);
- if (gfs2_withdrawing_or_withdrawn(sdp))
+ if (gfs2_withdrawn(sdp))
break;
}
-
- if (gfs2_withdrawing(sdp))
- gfs2_withdraw(sdp);
}
/**
@@ -1050,7 +1040,7 @@ repeat:
* Do this check while holding the log_flush_lock to prevent new
* buffers from being added to the ail via gfs2_pin()
*/
- if (gfs2_withdrawing_or_withdrawn(sdp) ||
+ if (gfs2_withdrawn(sdp) ||
!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags))
goto out;
@@ -1071,7 +1061,7 @@ repeat:
sdp->sd_log_tr = NULL;
tr->tr_first = first_log_head;
if (unlikely(frozen)) {
- if (gfs2_assert_withdraw_delayed(sdp,
+ if (gfs2_assert_withdraw(sdp,
!tr->tr_num_buf_new && !tr->tr_num_databuf_new))
goto out_withdraw;
}
@@ -1096,18 +1086,18 @@ repeat:
clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
if (unlikely(frozen))
- if (gfs2_assert_withdraw_delayed(sdp, !reserved_revokes))
+ if (gfs2_assert_withdraw(sdp, !reserved_revokes))
goto out_withdraw;
gfs2_ordered_write(sdp);
- if (gfs2_withdrawing_or_withdrawn(sdp))
+ if (gfs2_withdrawn(sdp))
goto out_withdraw;
lops_before_commit(sdp, tr);
- if (gfs2_withdrawing_or_withdrawn(sdp))
+ if (gfs2_withdrawn(sdp))
goto out_withdraw;
if (sdp->sd_jdesc)
gfs2_log_submit_bio(&sdp->sd_jdesc->jd_log_bio, REQ_OP_WRITE);
- if (gfs2_withdrawing_or_withdrawn(sdp))
+ if (gfs2_withdrawn(sdp))
goto out_withdraw;
if (sdp->sd_log_head != sdp->sd_log_flush_head) {
@@ -1115,7 +1105,7 @@ repeat:
} else if (sdp->sd_log_tail != sdp->sd_log_flush_tail && !sdp->sd_log_idle) {
log_write_header(sdp, flags);
}
- if (gfs2_withdrawing_or_withdrawn(sdp))
+ if (gfs2_withdrawn(sdp))
goto out_withdraw;
lops_after_commit(sdp, tr);
@@ -1133,7 +1123,7 @@ repeat:
if (!(flags & GFS2_LOG_HEAD_FLUSH_NORMAL)) {
if (!sdp->sd_log_idle) {
empty_ail1_list(sdp);
- if (gfs2_withdrawing_or_withdrawn(sdp))
+ if (gfs2_withdrawn(sdp))
goto out_withdraw;
log_write_header(sdp, flags);
}
@@ -1151,13 +1141,11 @@ out_end:
reserved_blocks += (reserved_revokes - sdp->sd_ldptrs) / sdp->sd_inptrs;
out:
if (used_blocks != reserved_blocks) {
- gfs2_assert_withdraw_delayed(sdp, used_blocks < reserved_blocks);
+ gfs2_assert_withdraw(sdp, used_blocks < reserved_blocks);
gfs2_log_release(sdp, reserved_blocks - used_blocks);
}
up_write(&sdp->sd_log_flush_lock);
gfs2_trans_free(sdp, tr);
- if (gfs2_withdrawing(sdp))
- gfs2_withdraw(sdp);
trace_gfs2_log_flush(sdp, 0, flags);
return;
@@ -1304,19 +1292,8 @@ int gfs2_logd(void *data)
set_freezable();
while (!kthread_should_stop()) {
- if (gfs2_withdrawing_or_withdrawn(sdp))
- break;
-
- /* Check for errors writing to the journal */
- if (sdp->sd_log_error) {
- gfs2_lm(sdp,
- "GFS2: fsid=%s: error %d: "
- "withdrawing the file system to "
- "prevent further damage.\n",
- sdp->sd_fsname, sdp->sd_log_error);
- gfs2_withdraw(sdp);
+ if (gfs2_withdrawn(sdp))
break;
- }
if (gfs2_jrnl_flush_reqd(sdp) || t == 0) {
gfs2_ail1_empty(sdp, 0);
@@ -1340,15 +1317,11 @@ int gfs2_logd(void *data)
test_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags) ||
gfs2_ail_flush_reqd(sdp) ||
gfs2_jrnl_flush_reqd(sdp) ||
- sdp->sd_log_error ||
- gfs2_withdrawing_or_withdrawn(sdp) ||
+ gfs2_withdrawn(sdp) ||
kthread_should_stop(),
t);
}
- if (gfs2_withdrawing(sdp))
- gfs2_withdraw(sdp);
-
return 0;
}
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index 9c8c305a75c4..97ebe457c00a 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -49,7 +49,7 @@ void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh)
if (test_set_buffer_pinned(bh))
gfs2_assert_withdraw(sdp, 0);
if (!buffer_uptodate(bh))
- gfs2_io_error_bh_wd(sdp, bh);
+ gfs2_io_error_bh(sdp, bh);
bd = bh->b_private;
/* If this buffer is in the AIL and it has already been written
* to in-place disk block, remove it from the AIL.
@@ -209,10 +209,7 @@ static void gfs2_end_log_write(struct bio *bio)
if (!cmpxchg(&sdp->sd_log_error, 0, err))
fs_err(sdp, "Error %d writing to journal, jid=%u\n",
err, sdp->sd_jdesc->jd_jid);
- gfs2_withdraw_delayed(sdp);
- /* prevent more writes to the journal */
- clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
- wake_up(&sdp->sd_logd_waitq);
+ gfs2_withdraw(sdp);
}
bio_for_each_segment_all(bvec, bio, iter_all) {
@@ -487,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(new, prev);
+ bio_chain(prev, new);
submit_bio(prev);
return new;
}
@@ -562,8 +559,7 @@ int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head)
bio = gfs2_log_alloc_bio(sdp, dblock, gfs2_end_log_read);
bio->bi_opf = REQ_OP_READ;
add_block_to_new_bio:
- if (!bio_add_folio(bio, folio, bsize, off))
- BUG();
+ bio_add_folio_nofail(bio, folio, bsize, off);
block_added:
off += bsize;
if (off == folio_size(folio))
diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index 7fb11ff71b5a..e4356198d8d8 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -263,8 +263,7 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
struct buffer_head *bh, *bhs[2];
int num = 0;
- if (gfs2_withdrawing_or_withdrawn(sdp) &&
- !gfs2_withdraw_in_prog(sdp)) {
+ if (gfs2_withdrawn(sdp)) {
*bhp = NULL;
return -EIO;
}
@@ -303,7 +302,7 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
if (unlikely(!buffer_uptodate(bh))) {
struct gfs2_trans *tr = current->journal_info;
if (tr && test_bit(TR_TOUCHED, &tr->tr_flags))
- gfs2_io_error_bh_wd(sdp, bh);
+ gfs2_io_error_bh(sdp, bh);
brelse(bh);
*bhp = NULL;
return -EIO;
@@ -322,8 +321,7 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh)
{
- if (gfs2_withdrawing_or_withdrawn(sdp) &&
- !gfs2_withdraw_in_prog(sdp))
+ if (gfs2_withdrawn(sdp))
return -EIO;
wait_on_buffer(bh);
@@ -331,11 +329,10 @@ int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh)
if (!buffer_uptodate(bh)) {
struct gfs2_trans *tr = current->journal_info;
if (tr && test_bit(TR_TOUCHED, &tr->tr_flags))
- gfs2_io_error_bh_wd(sdp, bh);
+ gfs2_io_error_bh(sdp, bh);
return -EIO;
}
- if (gfs2_withdrawing_or_withdrawn(sdp) &&
- !gfs2_withdraw_in_prog(sdp))
+ if (gfs2_withdrawn(sdp))
return -EIO;
return 0;
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index 889682f051ea..e7a88b717991 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -60,6 +60,7 @@ static void gfs2_tune_init(struct gfs2_tune *gt)
gt->gt_new_files_jdata = 0;
gt->gt_max_readahead = BIT(18);
gt->gt_complain_secs = 10;
+ gt->gt_withdraw_helper_timeout = 5;
}
void free_sbd(struct gfs2_sbd *sdp)
@@ -92,7 +93,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb)
init_waitqueue_head(&sdp->sd_async_glock_wait);
atomic_set(&sdp->sd_glock_disposal, 0);
init_completion(&sdp->sd_locking_init);
- init_completion(&sdp->sd_wdack);
+ init_completion(&sdp->sd_withdraw_helper);
spin_lock_init(&sdp->sd_statfs_spin);
spin_lock_init(&sdp->sd_rindex_spin);
@@ -370,7 +371,7 @@ static int init_locking(struct gfs2_sbd *sdp, struct gfs2_holder *mount_gh,
error = gfs2_glock_nq_num(sdp,
GFS2_MOUNT_LOCK, &gfs2_nondisk_glops,
LM_ST_EXCLUSIVE,
- LM_FLAG_NOEXP | GL_NOCACHE | GL_NOPID,
+ LM_FLAG_RECOVER | GL_NOCACHE | GL_NOPID,
mount_gh);
if (error) {
fs_err(sdp, "can't acquire mount glock: %d\n", error);
@@ -380,7 +381,7 @@ static int init_locking(struct gfs2_sbd *sdp, struct gfs2_holder *mount_gh,
error = gfs2_glock_nq_num(sdp,
GFS2_LIVE_LOCK, &gfs2_nondisk_glops,
LM_ST_SHARED,
- LM_FLAG_NOEXP | GL_EXACT | GL_NOPID,
+ LM_FLAG_RECOVER | GL_EXACT | GL_NOPID,
&sdp->sd_live_gh);
if (error) {
fs_err(sdp, "can't acquire live glock: %d\n", error);
@@ -542,8 +543,6 @@ static int gfs2_jindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ji_gh)
mutex_lock(&sdp->sd_jindex_mutex);
for (;;) {
- struct gfs2_inode *jip;
-
error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, ji_gh);
if (error)
break;
@@ -584,8 +583,6 @@ static int gfs2_jindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ji_gh)
d_mark_dontcache(jd->jd_inode);
spin_lock(&sdp->sd_jindex_spin);
jd->jd_jid = sdp->sd_journals++;
- jip = GFS2_I(jd->jd_inode);
- jd->jd_no_addr = jip->i_no_addr;
list_add_tail(&jd->jd_list, &sdp->sd_jindex_list);
spin_unlock(&sdp->sd_jindex_spin);
}
@@ -745,7 +742,7 @@ static int init_journal(struct gfs2_sbd *sdp, int undo)
error = gfs2_glock_nq_num(sdp, sdp->sd_lockstruct.ls_jid,
&gfs2_journal_glops,
LM_ST_EXCLUSIVE,
- LM_FLAG_NOEXP | GL_NOCACHE | GL_NOPID,
+ LM_FLAG_RECOVER | GL_NOPID,
&sdp->sd_journal_gh);
if (error) {
fs_err(sdp, "can't acquire journal glock: %d\n", error);
@@ -753,9 +750,8 @@ static int init_journal(struct gfs2_sbd *sdp, int undo)
}
ip = GFS2_I(sdp->sd_jdesc->jd_inode);
- sdp->sd_jinode_gl = ip->i_gl;
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED,
- LM_FLAG_NOEXP | GL_EXACT |
+ LM_FLAG_RECOVER | GL_EXACT |
GL_NOCACHE | GL_NOPID,
&sdp->sd_jinode_gh);
if (error) {
@@ -821,13 +817,10 @@ static int init_journal(struct gfs2_sbd *sdp, int undo)
fail_statfs:
uninit_statfs(sdp);
fail_jinode_gh:
- /* A withdraw may have done dq/uninit so now we need to check it */
- if (!sdp->sd_args.ar_spectator &&
- gfs2_holder_initialized(&sdp->sd_jinode_gh))
+ if (!sdp->sd_args.ar_spectator)
gfs2_glock_dq_uninit(&sdp->sd_jinode_gh);
fail_journal_gh:
- if (!sdp->sd_args.ar_spectator &&
- gfs2_holder_initialized(&sdp->sd_journal_gh))
+ if (!sdp->sd_args.ar_spectator)
gfs2_glock_dq_uninit(&sdp->sd_journal_gh);
fail_jindex:
gfs2_jindex_free(sdp);
@@ -1040,8 +1033,8 @@ hostdata_error:
void gfs2_lm_unmount(struct gfs2_sbd *sdp)
{
const struct lm_lockops *lm = sdp->sd_lockstruct.ls_ops;
- if (!gfs2_withdrawing_or_withdrawn(sdp) && lm->lm_unmount)
- lm->lm_unmount(sdp);
+ if (!gfs2_withdrawn(sdp) && lm->lm_unmount)
+ lm->lm_unmount(sdp, true);
}
static int wait_on_journal(struct gfs2_sbd *sdp)
@@ -1183,7 +1176,7 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc)
mapping = gfs2_aspace(sdp);
mapping->a_ops = &gfs2_rgrp_aops;
- mapping_set_gfp_mask(mapping, GFP_NOFS);
+ gfs2_setup_inode(sdp->sd_inode);
error = init_names(sdp, silent);
if (error)
@@ -1215,6 +1208,8 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc)
if (error)
goto fail_debug;
+ INIT_WORK(&sdp->sd_withdraw_work, gfs2_withdraw_func);
+
error = init_locking(sdp, &mount_gh, DO);
if (error)
goto fail_lm;
@@ -1401,12 +1396,14 @@ static const struct constant_table gfs2_param_data[] = {
};
enum opt_errors {
- Opt_errors_withdraw = GFS2_ERRORS_WITHDRAW,
- Opt_errors_panic = GFS2_ERRORS_PANIC,
+ Opt_errors_withdraw = GFS2_ERRORS_WITHDRAW,
+ Opt_errors_deactivate = GFS2_ERRORS_DEACTIVATE,
+ Opt_errors_panic = GFS2_ERRORS_PANIC,
};
static const struct constant_table gfs2_param_errors[] = {
{"withdraw", Opt_errors_withdraw },
+ {"deactivate", Opt_errors_deactivate },
{"panic", Opt_errors_panic },
{}
};
diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
index 2298e06797ac..b1692f12a602 100644
--- a/fs/gfs2/quota.c
+++ b/fs/gfs2/quota.c
@@ -125,7 +125,7 @@ static void gfs2_qd_dispose(struct gfs2_quota_data *qd)
hlist_bl_del_rcu(&qd->qd_hlist);
spin_unlock_bucket(qd->qd_hash);
- if (!gfs2_withdrawing_or_withdrawn(sdp)) {
+ if (!gfs2_withdrawn(sdp)) {
gfs2_assert_warn(sdp, !qd->qd_change);
gfs2_assert_warn(sdp, !qd->qd_slot_ref);
gfs2_assert_warn(sdp, !qd->qd_bh_count);
@@ -1551,27 +1551,13 @@ static void quotad_error(struct gfs2_sbd *sdp, const char *msg, int error)
{
if (error == 0 || error == -EROFS)
return;
- if (!gfs2_withdrawing_or_withdrawn(sdp)) {
+ if (!gfs2_withdrawn(sdp)) {
if (!cmpxchg(&sdp->sd_log_error, 0, error))
fs_err(sdp, "gfs2_quotad: %s error %d\n", msg, error);
wake_up(&sdp->sd_logd_waitq);
}
}
-static void quotad_check_timeo(struct gfs2_sbd *sdp, const char *msg,
- int (*fxn)(struct super_block *sb, int type),
- unsigned long t, unsigned long *timeo,
- unsigned int *new_timeo)
-{
- if (t >= *timeo) {
- int error = fxn(sdp->sd_vfs, 0);
- quotad_error(sdp, msg, error);
- *timeo = gfs2_tune_get_i(&sdp->sd_tune, new_timeo) * HZ;
- } else {
- *timeo -= t;
- }
-}
-
void gfs2_wake_up_statfs(struct gfs2_sbd *sdp) {
if (!sdp->sd_statfs_force_sync) {
sdp->sd_statfs_force_sync = 1;
@@ -1589,36 +1575,46 @@ void gfs2_wake_up_statfs(struct gfs2_sbd *sdp) {
int gfs2_quotad(void *data)
{
struct gfs2_sbd *sdp = data;
- struct gfs2_tune *tune = &sdp->sd_tune;
- unsigned long statfs_timeo = 0;
- unsigned long quotad_timeo = 0;
- unsigned long t = 0;
+ unsigned long now = jiffies;
+ unsigned long statfs_deadline = now;
+ unsigned long quotad_deadline = now;
set_freezable();
while (!kthread_should_stop()) {
- if (gfs2_withdrawing_or_withdrawn(sdp))
+ unsigned long t;
+
+ if (gfs2_withdrawn(sdp))
break;
- /* Update the master statfs file */
- if (sdp->sd_statfs_force_sync) {
- int error = gfs2_statfs_sync(sdp->sd_vfs, 0);
+ now = jiffies;
+ if (sdp->sd_statfs_force_sync ||
+ time_after(now, statfs_deadline)) {
+ unsigned int quantum;
+ int error;
+
+ /* Update the master statfs file */
+ error = gfs2_statfs_sync(sdp->sd_vfs, 0);
quotad_error(sdp, "statfs", error);
- statfs_timeo = gfs2_tune_get(sdp, gt_statfs_quantum) * HZ;
+
+ quantum = gfs2_tune_get(sdp, gt_statfs_quantum);
+ statfs_deadline = now + quantum * HZ;
}
- else
- quotad_check_timeo(sdp, "statfs", gfs2_statfs_sync, t,
- &statfs_timeo,
- &tune->gt_statfs_quantum);
+ if (time_after(now, quotad_deadline)) {
+ unsigned int quantum;
+ int error;
- /* Update quota file */
- quotad_check_timeo(sdp, "sync", gfs2_quota_sync, t,
- &quotad_timeo, &tune->gt_quota_quantum);
+ /* Update the quota file */
+ error = gfs2_quota_sync(sdp->sd_vfs, 0);
+ quotad_error(sdp, "sync", error);
- t = min(quotad_timeo, statfs_timeo);
+ quantum = gfs2_tune_get(sdp, gt_quota_quantum);
+ quotad_deadline = now + quantum * HZ;
+ }
- t = wait_event_freezable_timeout(sdp->sd_quota_wait,
+ t = min(statfs_deadline - now, quotad_deadline - now);
+ wait_event_freezable_timeout(sdp->sd_quota_wait,
sdp->sd_statfs_force_sync ||
- gfs2_withdrawing_or_withdrawn(sdp) ||
+ gfs2_withdrawn(sdp) ||
kthread_should_stop(),
t);
diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c
index 24250478b085..8c8202c68b64 100644
--- a/fs/gfs2/recovery.c
+++ b/fs/gfs2/recovery.c
@@ -408,7 +408,7 @@ void gfs2_recover_func(struct work_struct *work)
int error = 0;
int jlocked = 0;
- if (gfs2_withdrawing_or_withdrawn(sdp)) {
+ if (gfs2_withdrawn(sdp)) {
fs_err(sdp, "jid=%u: Recovery not attempted due to withdraw.\n",
jd->jd_jid);
goto fail;
@@ -424,7 +424,8 @@ void gfs2_recover_func(struct work_struct *work)
error = gfs2_glock_nq_num(sdp, jd->jd_jid, &gfs2_journal_glops,
LM_ST_EXCLUSIVE,
- LM_FLAG_NOEXP | LM_FLAG_TRY | GL_NOCACHE,
+ LM_FLAG_RECOVER | LM_FLAG_TRY |
+ GL_NOCACHE,
&j_gh);
switch (error) {
case 0:
@@ -440,7 +441,8 @@ void gfs2_recover_func(struct work_struct *work)
}
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED,
- LM_FLAG_NOEXP | GL_NOCACHE, &ji_gh);
+ LM_FLAG_RECOVER | GL_NOCACHE,
+ &ji_gh);
if (error)
goto fail_gunlock_j;
} else {
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index 644b2d1e7276..f6cd907b3ec6 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -137,7 +137,7 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp)
int error;
j_gl->gl_ops->go_inval(j_gl, DIO_METADATA);
- if (gfs2_withdrawing_or_withdrawn(sdp))
+ if (gfs2_withdrawn(sdp))
return -EIO;
if (sdp->sd_log_sequence == 0) {
@@ -147,7 +147,7 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp)
}
error = gfs2_quota_init(sdp);
- if (!error && gfs2_withdrawing_or_withdrawn(sdp))
+ if (!error && gfs2_withdrawn(sdp))
error = -EIO;
if (!error)
set_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
@@ -351,7 +351,7 @@ static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp)
gfs2_freeze_unlock(sdp);
error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_EXCLUSIVE,
- LM_FLAG_NOEXP | GL_NOPID,
+ LM_FLAG_RECOVER | GL_NOPID,
&sdp->sd_freeze_gh);
if (error)
goto relock_shared;
@@ -491,7 +491,7 @@ static void gfs2_dirty_inode(struct inode *inode, int flags)
if (unlikely(!ip->i_gl))
return;
- if (gfs2_withdrawing_or_withdrawn(sdp))
+ if (gfs2_withdrawn(sdp))
return;
if (!gfs2_glock_is_locked_by_me(ip->i_gl)) {
ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
@@ -597,13 +597,13 @@ restart:
if (!sb_rdonly(sb))
gfs2_make_fs_ro(sdp);
else {
- if (gfs2_withdrawing_or_withdrawn(sdp))
+ if (gfs2_withdrawn(sdp))
gfs2_destroy_threads(sdp);
gfs2_quota_cleanup(sdp);
}
- WARN_ON(gfs2_withdrawing(sdp));
+ flush_work(&sdp->sd_withdraw_work);
/* At this point, we're through modifying the disk */
@@ -749,9 +749,7 @@ static int gfs2_freeze_super(struct super_block *sb, enum freeze_holder who,
break;
}
- error = gfs2_do_thaw(sdp, who, freeze_owner);
- if (error)
- goto out;
+ (void)gfs2_do_thaw(sdp, who, freeze_owner);
if (error == -EBUSY)
fs_err(sdp, "waiting for recovery before freeze\n");
@@ -778,7 +776,7 @@ static int gfs2_freeze_fs(struct super_block *sb)
if (test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) {
gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_FREEZE |
GFS2_LFC_FREEZE_GO_SYNC);
- if (gfs2_withdrawing_or_withdrawn(sdp))
+ if (gfs2_withdrawn(sdp))
return -EIO;
}
return 0;
@@ -819,20 +817,6 @@ static int gfs2_thaw_super(struct super_block *sb, enum freeze_holder who,
return error;
}
-void gfs2_thaw_freeze_initiator(struct super_block *sb)
-{
- struct gfs2_sbd *sdp = sb->s_fs_info;
-
- mutex_lock(&sdp->sd_freeze_mutex);
- if (!test_bit(SDF_FREEZE_INITIATOR, &sdp->sd_flags))
- goto out;
-
- gfs2_freeze_unlock(sdp);
-
-out:
- mutex_unlock(&sdp->sd_freeze_mutex);
-}
-
/**
* statfs_slow_fill - fill in the sg for a given RG
* @rgd: the RG
@@ -1147,6 +1131,9 @@ static int gfs2_show_options(struct seq_file *s, struct dentry *root)
case GFS2_ERRORS_WITHDRAW:
state = "withdraw";
break;
+ case GFS2_ERRORS_DEACTIVATE:
+ state = "deactivate";
+ break;
case GFS2_ERRORS_PANIC:
state = "panic";
break;
diff --git a/fs/gfs2/super.h b/fs/gfs2/super.h
index b27a774d9580..173f1e74c2a9 100644
--- a/fs/gfs2/super.h
+++ b/fs/gfs2/super.h
@@ -47,7 +47,6 @@ void gfs2_statfs_change_out(const struct gfs2_statfs_change_host *sc,
void update_statfs(struct gfs2_sbd *sdp, struct buffer_head *m_bh);
int gfs2_statfs_sync(struct super_block *sb, int type);
void gfs2_freeze_func(struct work_struct *work);
-void gfs2_thaw_freeze_initiator(struct super_block *sb);
void free_local_statfs_inodes(struct gfs2_sbd *sdp);
struct inode *find_local_statfs_inode(struct gfs2_sbd *sdp,
diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c
index c3c8842920d2..7051db9dbea0 100644
--- a/fs/gfs2/sys.c
+++ b/fs/gfs2/sys.c
@@ -59,7 +59,7 @@ static struct kset *gfs2_kset;
static ssize_t id_show(struct gfs2_sbd *sdp, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%u:%u\n",
+ return sysfs_emit(buf, "%u:%u\n",
MAJOR(sdp->sd_vfs->s_dev), MINOR(sdp->sd_vfs->s_dev));
}
@@ -68,7 +68,7 @@ static ssize_t status_show(struct gfs2_sbd *sdp, char *buf)
unsigned long f = sdp->sd_flags;
ssize_t s;
- s = snprintf(buf, PAGE_SIZE,
+ s = sysfs_emit(buf,
"Journal Checked: %d\n"
"Journal Live: %d\n"
"Journal ID: %d\n"
@@ -84,10 +84,6 @@ static ssize_t status_show(struct gfs2_sbd *sdp, char *buf)
"Force AIL Flush: %d\n"
"FS Freeze Initiator: %d\n"
"FS Frozen: %d\n"
- "Withdrawing: %d\n"
- "Withdraw In Prog: %d\n"
- "Remote Withdraw: %d\n"
- "Withdraw Recovery: %d\n"
"Killing: %d\n"
"sd_log_error: %d\n"
"sd_log_flush_lock: %d\n"
@@ -117,10 +113,6 @@ static ssize_t status_show(struct gfs2_sbd *sdp, char *buf)
test_bit(SDF_FORCE_AIL_FLUSH, &f),
test_bit(SDF_FREEZE_INITIATOR, &f),
test_bit(SDF_FROZEN, &f),
- test_bit(SDF_WITHDRAWING, &f),
- test_bit(SDF_WITHDRAW_IN_PROG, &f),
- test_bit(SDF_REMOTE_WITHDRAW, &f),
- test_bit(SDF_WITHDRAW_RECOVERY, &f),
test_bit(SDF_KILL, &f),
sdp->sd_log_error,
rwsem_is_locked(&sdp->sd_log_flush_lock),
@@ -140,7 +132,7 @@ static ssize_t status_show(struct gfs2_sbd *sdp, char *buf)
static ssize_t fsname_show(struct gfs2_sbd *sdp, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%s\n", sdp->sd_fsname);
+ return sysfs_emit(buf, "%s\n", sdp->sd_fsname);
}
static ssize_t uuid_show(struct gfs2_sbd *sdp, char *buf)
@@ -150,7 +142,7 @@ static ssize_t uuid_show(struct gfs2_sbd *sdp, char *buf)
buf[0] = '\0';
if (uuid_is_null(&s->s_uuid))
return 0;
- return snprintf(buf, PAGE_SIZE, "%pUB\n", &s->s_uuid);
+ return sysfs_emit(buf, "%pUB\n", &s->s_uuid);
}
static ssize_t freeze_show(struct gfs2_sbd *sdp, char *buf)
@@ -158,7 +150,7 @@ static ssize_t freeze_show(struct gfs2_sbd *sdp, char *buf)
struct super_block *sb = sdp->sd_vfs;
int frozen = (sb->s_writers.frozen == SB_UNFROZEN) ? 0 : 1;
- return snprintf(buf, PAGE_SIZE, "%d\n", frozen);
+ return sysfs_emit(buf, "%d\n", frozen);
}
static ssize_t freeze_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
@@ -193,8 +185,8 @@ static ssize_t freeze_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
static ssize_t withdraw_show(struct gfs2_sbd *sdp, char *buf)
{
- unsigned int b = gfs2_withdrawing_or_withdrawn(sdp);
- return snprintf(buf, PAGE_SIZE, "%u\n", b);
+ unsigned int b = gfs2_withdrawn(sdp);
+ return sysfs_emit(buf, "%u\n", b);
}
static ssize_t withdraw_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
@@ -397,7 +389,7 @@ static struct kobj_type gfs2_ktype = {
static ssize_t proto_name_show(struct gfs2_sbd *sdp, char *buf)
{
const struct lm_lockops *ops = sdp->sd_lockstruct.ls_ops;
- return sprintf(buf, "%s\n", ops->lm_proto_name);
+ return sysfs_emit(buf, "%s\n", ops->lm_proto_name);
}
static ssize_t block_show(struct gfs2_sbd *sdp, char *buf)
@@ -408,7 +400,7 @@ static ssize_t block_show(struct gfs2_sbd *sdp, char *buf)
if (test_bit(DFL_BLOCK_LOCKS, &ls->ls_recover_flags))
val = 1;
- ret = sprintf(buf, "%d\n", val);
+ ret = sysfs_emit(buf, "%d\n", val);
return ret;
}
@@ -433,33 +425,27 @@ static ssize_t block_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
return len;
}
-static ssize_t wdack_show(struct gfs2_sbd *sdp, char *buf)
-{
- int val = completion_done(&sdp->sd_wdack) ? 1 : 0;
-
- return sprintf(buf, "%d\n", val);
-}
-
-static ssize_t wdack_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
+static ssize_t withdraw_helper_status_store(struct gfs2_sbd *sdp,
+ const char *buf,
+ size_t len)
{
int ret, val;
ret = kstrtoint(buf, 0, &val);
if (ret)
return ret;
-
- if ((val == 1) &&
- !strcmp(sdp->sd_lockstruct.ls_ops->lm_proto_name, "lock_dlm"))
- complete(&sdp->sd_wdack);
- else
+ if (val < 0 || val > 1)
return -EINVAL;
+
+ sdp->sd_withdraw_helper_status = val;
+ complete(&sdp->sd_withdraw_helper);
return len;
}
static ssize_t lkfirst_show(struct gfs2_sbd *sdp, char *buf)
{
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
- return sprintf(buf, "%d\n", ls->ls_first);
+ return sysfs_emit(buf, "%d\n", ls->ls_first);
}
static ssize_t lkfirst_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
@@ -492,7 +478,7 @@ out:
static ssize_t first_done_show(struct gfs2_sbd *sdp, char *buf)
{
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
- return sprintf(buf, "%d\n", !!test_bit(DFL_FIRST_MOUNT_DONE, &ls->ls_recover_flags));
+ return sysfs_emit(buf, "%d\n", !!test_bit(DFL_FIRST_MOUNT_DONE, &ls->ls_recover_flags));
}
int gfs2_recover_set(struct gfs2_sbd *sdp, unsigned jid)
@@ -550,18 +536,18 @@ out:
static ssize_t recover_done_show(struct gfs2_sbd *sdp, char *buf)
{
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
- return sprintf(buf, "%d\n", ls->ls_recover_jid_done);
+ return sysfs_emit(buf, "%d\n", ls->ls_recover_jid_done);
}
static ssize_t recover_status_show(struct gfs2_sbd *sdp, char *buf)
{
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
- return sprintf(buf, "%d\n", ls->ls_recover_jid_status);
+ return sysfs_emit(buf, "%d\n", ls->ls_recover_jid_status);
}
static ssize_t jid_show(struct gfs2_sbd *sdp, char *buf)
{
- return sprintf(buf, "%d\n", sdp->sd_lockstruct.ls_jid);
+ return sysfs_emit(buf, "%d\n", sdp->sd_lockstruct.ls_jid);
}
static ssize_t jid_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
@@ -599,7 +585,7 @@ static struct gfs2_attr gdlm_attr_##_name = __ATTR(_name,_mode,_show,_store)
GDLM_ATTR(proto_name, 0444, proto_name_show, NULL);
GDLM_ATTR(block, 0644, block_show, block_store);
-GDLM_ATTR(withdraw, 0644, wdack_show, wdack_store);
+GDLM_ATTR(withdraw, 0200, NULL, withdraw_helper_status_store);
GDLM_ATTR(jid, 0644, jid_show, jid_store);
GDLM_ATTR(first, 0644, lkfirst_show, lkfirst_store);
GDLM_ATTR(first_done, 0444, first_done_show, NULL);
@@ -626,7 +612,7 @@ static struct attribute *lock_module_attrs[] = {
static ssize_t quota_scale_show(struct gfs2_sbd *sdp, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%u %u\n",
+ return sysfs_emit(buf, "%u %u\n",
sdp->sd_tune.gt_quota_scale_num,
sdp->sd_tune.gt_quota_scale_den);
}
@@ -679,7 +665,7 @@ static struct gfs2_attr tune_attr_##name = __ATTR(name, 0644, show, store)
#define TUNE_ATTR_2(name, store) \
static ssize_t name##_show(struct gfs2_sbd *sdp, char *buf) \
{ \
- return snprintf(buf, PAGE_SIZE, "%u\n", sdp->sd_tune.gt_##name); \
+ return sysfs_emit(buf, "%u\n", sdp->sd_tune.gt_##name); \
} \
TUNE_ATTR_3(name, name##_show, store)
@@ -698,6 +684,7 @@ TUNE_ATTR(statfs_slow, 0);
TUNE_ATTR(new_files_jdata, 0);
TUNE_ATTR(statfs_quantum, 1);
TUNE_ATTR_3(quota_scale, quota_scale_show, quota_scale_store);
+TUNE_ATTR(withdraw_helper_timeout, 1);
static struct attribute *tune_attrs[] = {
&tune_attr_quota_warn_period.attr,
@@ -708,6 +695,7 @@ static struct attribute *tune_attrs[] = {
&tune_attr_statfs_quantum.attr,
&tune_attr_quota_scale.attr,
&tune_attr_new_files_jdata.attr,
+ &tune_attr_withdraw_helper_timeout.attr,
NULL,
};
diff --git a/fs/gfs2/trace_gfs2.h b/fs/gfs2/trace_gfs2.h
index 1c2507a27318..fcfbf68ec725 100644
--- a/fs/gfs2/trace_gfs2.h
+++ b/fs/gfs2/trace_gfs2.h
@@ -59,7 +59,6 @@
{(1UL << GLF_LRU), "L" }, \
{(1UL << GLF_OBJECT), "o" }, \
{(1UL << GLF_BLOCKING), "b" }, \
- {(1UL << GLF_UNLOCKED), "x" }, \
{(1UL << GLF_INSTANTIATE_NEEDED), "n" }, \
{(1UL << GLF_INSTANTIATE_IN_PROG), "N" }, \
{(1UL << GLF_TRY_TO_EVICT), "e" }, \
diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c
index 075f7e9abe47..6df65540e13d 100644
--- a/fs/gfs2/trans.c
+++ b/fs/gfs2/trans.c
@@ -49,7 +49,7 @@ int __gfs2_trans_begin(struct gfs2_trans *tr, struct gfs2_sbd *sdp,
}
BUG_ON(blocks == 0 && revokes == 0);
- if (!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags))
+ if (gfs2_withdrawn(sdp))
return -EROFS;
tr->tr_ip = ip;
@@ -85,25 +85,30 @@ int __gfs2_trans_begin(struct gfs2_trans *tr, struct gfs2_sbd *sdp,
*/
down_read(&sdp->sd_log_flush_lock);
+ if (unlikely(!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)))
+ goto out_not_live;
if (gfs2_log_try_reserve(sdp, tr, &extra_revokes))
goto reserved;
+
up_read(&sdp->sd_log_flush_lock);
gfs2_log_reserve(sdp, tr, &extra_revokes);
down_read(&sdp->sd_log_flush_lock);
-
-reserved:
- gfs2_log_release_revokes(sdp, extra_revokes);
if (unlikely(!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags))) {
- gfs2_log_release_revokes(sdp, tr->tr_revokes);
- up_read(&sdp->sd_log_flush_lock);
+ revokes = tr->tr_revokes + extra_revokes;
+ gfs2_log_release_revokes(sdp, revokes);
gfs2_log_release(sdp, tr->tr_reserved);
- sb_end_intwrite(sdp->sd_vfs);
- return -EROFS;
+ goto out_not_live;
}
+reserved:
+ gfs2_log_release_revokes(sdp, extra_revokes);
current->journal_info = tr;
-
return 0;
+
+out_not_live:
+ up_read(&sdp->sd_log_flush_lock);
+ sb_end_intwrite(sdp->sd_vfs);
+ return -EROFS;
}
int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,
@@ -255,7 +260,6 @@ void gfs2_trans_add_meta(struct gfs2_glock *gl, struct buffer_head *bh)
struct gfs2_bufdata *bd;
struct gfs2_meta_header *mh;
struct gfs2_trans *tr = current->journal_info;
- bool withdraw = false;
lock_buffer(bh);
if (buffer_pinned(bh)) {
@@ -289,14 +293,14 @@ void gfs2_trans_add_meta(struct gfs2_glock *gl, struct buffer_head *bh)
(unsigned long long)bd->bd_bh->b_blocknr);
BUG();
}
- if (gfs2_withdrawing_or_withdrawn(sdp)) {
+ if (gfs2_withdrawn(sdp)) {
fs_info(sdp, "GFS2:adding buf while withdrawn! 0x%llx\n",
(unsigned long long)bd->bd_bh->b_blocknr);
goto out_unlock;
}
if (unlikely(sb->s_writers.frozen == SB_FREEZE_COMPLETE)) {
fs_info(sdp, "GFS2:adding buf while frozen\n");
- withdraw = true;
+ gfs2_withdraw(sdp);
goto out_unlock;
}
gfs2_pin(sdp, bd->bd_bh);
@@ -306,8 +310,6 @@ void gfs2_trans_add_meta(struct gfs2_glock *gl, struct buffer_head *bh)
tr->tr_num_buf_new++;
out_unlock:
gfs2_log_unlock(sdp);
- if (withdraw)
- gfs2_assert_withdraw(sdp, 0);
out:
unlock_buffer(bh);
}
diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c
index 56412f63f3bb..02603200846d 100644
--- a/fs/gfs2/util.c
+++ b/fs/gfs2/util.c
@@ -58,7 +58,7 @@ int check_journal_clean(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
struct gfs2_inode *ip;
ip = GFS2_I(jd->jd_inode);
- error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_NOEXP |
+ error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_RECOVER |
GL_EXACT | GL_NOCACHE, &j_gh);
if (error) {
if (verbose)
@@ -99,7 +99,7 @@ out_unlock:
*/
int gfs2_freeze_lock_shared(struct gfs2_sbd *sdp)
{
- int flags = LM_FLAG_NOEXP | GL_EXACT;
+ int flags = LM_FLAG_RECOVER | GL_EXACT;
int error;
error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_SHARED, flags,
@@ -115,182 +115,32 @@ void gfs2_freeze_unlock(struct gfs2_sbd *sdp)
gfs2_glock_dq_uninit(&sdp->sd_freeze_gh);
}
-static void signal_our_withdraw(struct gfs2_sbd *sdp)
+static void do_withdraw(struct gfs2_sbd *sdp)
{
- struct gfs2_glock *live_gl = sdp->sd_live_gh.gh_gl;
- struct inode *inode;
- struct gfs2_inode *ip;
- struct gfs2_glock *i_gl;
- u64 no_formal_ino;
- int ret = 0;
- int tries;
-
- if (test_bit(SDF_NORECOVERY, &sdp->sd_flags) || !sdp->sd_jdesc)
+ down_write(&sdp->sd_log_flush_lock);
+ if (!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) {
+ up_write(&sdp->sd_log_flush_lock);
return;
-
- gfs2_ail_drain(sdp); /* frees all transactions */
- inode = sdp->sd_jdesc->jd_inode;
- ip = GFS2_I(inode);
- i_gl = ip->i_gl;
- no_formal_ino = ip->i_no_formal_ino;
-
- /* Prevent any glock dq until withdraw recovery is complete */
- set_bit(SDF_WITHDRAW_RECOVERY, &sdp->sd_flags);
- /*
- * Don't tell dlm we're bailing until we have no more buffers in the
- * wind. If journal had an IO error, the log code should just purge
- * the outstanding buffers rather than submitting new IO. Making the
- * file system read-only will flush the journal, etc.
- *
- * During a normal unmount, gfs2_make_fs_ro calls gfs2_log_shutdown
- * which clears SDF_JOURNAL_LIVE. In a withdraw, we must not write
- * any UNMOUNT log header, so we can't call gfs2_log_shutdown, and
- * therefore we need to clear SDF_JOURNAL_LIVE manually.
- */
- clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
- if (!sb_rdonly(sdp->sd_vfs)) {
- bool locked = mutex_trylock(&sdp->sd_freeze_mutex);
-
- wake_up(&sdp->sd_logd_waitq);
- wake_up(&sdp->sd_quota_wait);
-
- wait_event_timeout(sdp->sd_log_waitq,
- gfs2_log_is_empty(sdp),
- HZ * 5);
-
- sdp->sd_vfs->s_flags |= SB_RDONLY;
-
- if (locked)
- mutex_unlock(&sdp->sd_freeze_mutex);
-
- /*
- * Dequeue any pending non-system glock holders that can no
- * longer be granted because the file system is withdrawn.
- */
- gfs2_gl_dq_holders(sdp);
- }
-
- if (sdp->sd_lockstruct.ls_ops->lm_lock == NULL) { /* lock_nolock */
- if (!ret)
- ret = -EIO;
- clear_bit(SDF_WITHDRAW_RECOVERY, &sdp->sd_flags);
- goto skip_recovery;
- }
- /*
- * Drop the glock for our journal so another node can recover it.
- */
- if (gfs2_holder_initialized(&sdp->sd_journal_gh)) {
- gfs2_glock_dq_wait(&sdp->sd_journal_gh);
- gfs2_holder_uninit(&sdp->sd_journal_gh);
- }
- sdp->sd_jinode_gh.gh_flags |= GL_NOCACHE;
- gfs2_glock_dq(&sdp->sd_jinode_gh);
- gfs2_thaw_freeze_initiator(sdp->sd_vfs);
- wait_on_bit(&i_gl->gl_flags, GLF_DEMOTE, TASK_UNINTERRUPTIBLE);
-
- /*
- * holder_uninit to force glock_put, to force dlm to let go
- */
- gfs2_holder_uninit(&sdp->sd_jinode_gh);
-
- /*
- * Note: We need to be careful here:
- * Our iput of jd_inode will evict it. The evict will dequeue its
- * glock, but the glock dq will wait for the withdraw unless we have
- * exception code in glock_dq.
- */
- iput(inode);
- sdp->sd_jdesc->jd_inode = NULL;
- /*
- * Wait until the journal inode's glock is freed. This allows try locks
- * on other nodes to be successful, otherwise we remain the owner of
- * the glock as far as dlm is concerned.
- */
- if (i_gl->gl_ops->go_unlocked) {
- set_bit(GLF_UNLOCKED, &i_gl->gl_flags);
- wait_on_bit(&i_gl->gl_flags, GLF_UNLOCKED, TASK_UNINTERRUPTIBLE);
}
+ clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
+ up_write(&sdp->sd_log_flush_lock);
- /*
- * Dequeue the "live" glock, but keep a reference so it's never freed.
- */
- gfs2_glock_hold(live_gl);
- gfs2_glock_dq_wait(&sdp->sd_live_gh);
- /*
- * We enqueue the "live" glock in EX so that all other nodes
- * get a demote request and act on it. We don't really want the
- * lock in EX, so we send a "try" lock with 1CB to produce a callback.
- */
- fs_warn(sdp, "Requesting recovery of jid %d.\n",
- sdp->sd_lockstruct.ls_jid);
- gfs2_holder_reinit(LM_ST_EXCLUSIVE,
- LM_FLAG_TRY_1CB | LM_FLAG_NOEXP | GL_NOPID,
- &sdp->sd_live_gh);
- msleep(GL_GLOCK_MAX_HOLD);
- /*
- * This will likely fail in a cluster, but succeed standalone:
- */
- ret = gfs2_glock_nq(&sdp->sd_live_gh);
+ gfs2_ail_drain(sdp); /* frees all transactions */
- gfs2_glock_put(live_gl); /* drop extra reference we acquired */
- clear_bit(SDF_WITHDRAW_RECOVERY, &sdp->sd_flags);
+ wake_up(&sdp->sd_logd_waitq);
+ wake_up(&sdp->sd_quota_wait);
- /*
- * If we actually got the "live" lock in EX mode, there are no other
- * nodes available to replay our journal.
- */
- if (ret == 0) {
- fs_warn(sdp, "No other mounters found.\n");
- /*
- * We are about to release the lockspace. By keeping live_gl
- * locked here, we ensure that the next mounter coming along
- * will be a "first" mounter which will perform recovery.
- */
- goto skip_recovery;
- }
+ wait_event_timeout(sdp->sd_log_waitq,
+ gfs2_log_is_empty(sdp),
+ HZ * 5);
- /*
- * At this point our journal is evicted, so we need to get a new inode
- * for it. Once done, we need to call gfs2_find_jhead which
- * calls gfs2_map_journal_extents to map it for us again.
- *
- * Note that we don't really want it to look up a FREE block. The
- * GFS2_BLKST_FREE simply overrides a block check in gfs2_inode_lookup
- * which would otherwise fail because it requires grabbing an rgrp
- * glock, which would fail with -EIO because we're withdrawing.
- */
- inode = gfs2_inode_lookup(sdp->sd_vfs, DT_UNKNOWN,
- sdp->sd_jdesc->jd_no_addr, no_formal_ino,
- GFS2_BLKST_FREE);
- if (IS_ERR(inode)) {
- fs_warn(sdp, "Reprocessing of jid %d failed with %ld.\n",
- sdp->sd_lockstruct.ls_jid, PTR_ERR(inode));
- goto skip_recovery;
- }
- sdp->sd_jdesc->jd_inode = inode;
- d_mark_dontcache(inode);
+ sdp->sd_vfs->s_flags |= SB_RDONLY;
/*
- * Now wait until recovery is complete.
+ * Dequeue any pending non-system glock holders that can no
+ * longer be granted because the file system is withdrawn.
*/
- for (tries = 0; tries < 10; tries++) {
- ret = check_journal_clean(sdp, sdp->sd_jdesc, false);
- if (!ret)
- break;
- msleep(HZ);
- fs_warn(sdp, "Waiting for journal recovery jid %d.\n",
- sdp->sd_lockstruct.ls_jid);
- }
-skip_recovery:
- if (!ret)
- fs_warn(sdp, "Journal recovery complete for jid %d.\n",
- sdp->sd_lockstruct.ls_jid);
- else
- fs_warn(sdp, "Journal recovery skipped for jid %d until next "
- "mount.\n", sdp->sd_lockstruct.ls_jid);
- fs_warn(sdp, "Glock dequeues delayed: %lu\n", sdp->sd_glock_dqs_held);
- sdp->sd_glock_dqs_held = 0;
- wake_up_bit(&sdp->sd_flags, SDF_WITHDRAW_RECOVERY);
+ gfs2_withdraw_glocks(sdp);
}
void gfs2_lm(struct gfs2_sbd *sdp, const char *fmt, ...)
@@ -309,43 +159,104 @@ void gfs2_lm(struct gfs2_sbd *sdp, const char *fmt, ...)
va_end(args);
}
-void gfs2_withdraw(struct gfs2_sbd *sdp)
+/**
+ * gfs2_offline_uevent - run gfs2_withdraw_helper
+ * @sdp: The GFS2 superblock
+ */
+static bool gfs2_offline_uevent(struct gfs2_sbd *sdp)
{
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
- const struct lm_lockops *lm = ls->ls_ops;
+ long timeout;
- if (sdp->sd_args.ar_errors == GFS2_ERRORS_WITHDRAW) {
- unsigned long old = READ_ONCE(sdp->sd_flags), new;
+ /* Skip protocol "lock_nolock" which doesn't require shared storage. */
+ if (!ls->ls_ops->lm_lock)
+ return false;
- do {
- if (old & BIT(SDF_WITHDRAWN)) {
- wait_on_bit(&sdp->sd_flags,
- SDF_WITHDRAW_IN_PROG,
- TASK_UNINTERRUPTIBLE);
- return;
- }
- new = old | BIT(SDF_WITHDRAWN) | BIT(SDF_WITHDRAW_IN_PROG);
- } while (unlikely(!try_cmpxchg(&sdp->sd_flags, &old, new)));
+ /*
+ * The gfs2_withdraw_helper replies by writing one of the following
+ * status codes to "/sys$DEVPATH/lock_module/withdraw":
+ *
+ * 0 - The shared block device has been marked inactive. Future write
+ * operations will fail.
+ *
+ * 1 - The shared block device may still be active and carry out
+ * write operations.
+ *
+ * If the "offline" uevent isn't reacted upon in time, the event
+ * handler is assumed to have failed.
+ */
- fs_err(sdp, "about to withdraw this file system\n");
- BUG_ON(sdp->sd_args.ar_debug);
+ sdp->sd_withdraw_helper_status = -1;
+ kobject_uevent(&sdp->sd_kobj, KOBJ_OFFLINE);
+ timeout = gfs2_tune_get(sdp, gt_withdraw_helper_timeout) * HZ;
+ wait_for_completion_timeout(&sdp->sd_withdraw_helper, timeout);
+ if (sdp->sd_withdraw_helper_status == -1) {
+ fs_err(sdp, "%s timed out\n", "gfs2_withdraw_helper");
+ } else {
+ fs_err(sdp, "%s %s with status %d\n",
+ "gfs2_withdraw_helper",
+ sdp->sd_withdraw_helper_status == 0 ?
+ "succeeded" : "failed",
+ sdp->sd_withdraw_helper_status);
+ }
+ return sdp->sd_withdraw_helper_status == 0;
+}
+
+void gfs2_withdraw_func(struct work_struct *work)
+{
+ struct gfs2_sbd *sdp = container_of(work, struct gfs2_sbd, sd_withdraw_work);
+ struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+ const struct lm_lockops *lm = ls->ls_ops;
+ bool device_inactive;
+
+ if (test_bit(SDF_KILL, &sdp->sd_flags))
+ return;
+
+ BUG_ON(sdp->sd_args.ar_debug);
- signal_our_withdraw(sdp);
+ /*
+ * Try to deactivate the shared block device so that no more I/O will
+ * go through. If successful, we can immediately trigger remote
+ * recovery. Otherwise, we must first empty out all our local caches.
+ */
- kobject_uevent(&sdp->sd_kobj, KOBJ_OFFLINE);
+ device_inactive = gfs2_offline_uevent(sdp);
- if (!strcmp(sdp->sd_lockstruct.ls_ops->lm_proto_name, "lock_dlm"))
- wait_for_completion(&sdp->sd_wdack);
+ if (sdp->sd_args.ar_errors == GFS2_ERRORS_DEACTIVATE && !device_inactive)
+ panic("GFS2: fsid=%s: panic requested\n", sdp->sd_fsname);
- if (lm->lm_unmount) {
- fs_err(sdp, "telling LM to unmount\n");
- lm->lm_unmount(sdp);
+ if (lm->lm_unmount) {
+ if (device_inactive) {
+ lm->lm_unmount(sdp, false);
+ do_withdraw(sdp);
+ } else {
+ do_withdraw(sdp);
+ lm->lm_unmount(sdp, false);
}
- fs_err(sdp, "File system withdrawn\n");
+ } else {
+ do_withdraw(sdp);
+ }
+
+ fs_err(sdp, "file system withdrawn\n");
+}
+
+void gfs2_withdraw(struct gfs2_sbd *sdp)
+{
+ if (sdp->sd_args.ar_errors == GFS2_ERRORS_WITHDRAW ||
+ sdp->sd_args.ar_errors == GFS2_ERRORS_DEACTIVATE) {
+ if (test_and_set_bit(SDF_WITHDRAWN, &sdp->sd_flags))
+ return;
+
dump_stack();
- clear_bit(SDF_WITHDRAW_IN_PROG, &sdp->sd_flags);
- smp_mb__after_atomic();
- wake_up_bit(&sdp->sd_flags, SDF_WITHDRAW_IN_PROG);
+ /*
+ * There is no need to withdraw when the superblock hasn't been
+ * fully initialized, yet.
+ */
+ if (!(sdp->sd_vfs->s_flags & SB_BORN))
+ return;
+ fs_err(sdp, "about to withdraw this file system\n");
+ schedule_work(&sdp->sd_withdraw_work);
+ return;
}
if (sdp->sd_args.ar_errors == GFS2_ERRORS_PANIC)
@@ -357,10 +268,9 @@ void gfs2_withdraw(struct gfs2_sbd *sdp)
*/
void gfs2_assert_withdraw_i(struct gfs2_sbd *sdp, char *assertion,
- const char *function, char *file, unsigned int line,
- bool delayed)
+ const char *function, char *file, unsigned int line)
{
- if (gfs2_withdrawing_or_withdrawn(sdp))
+ if (gfs2_withdrawn(sdp))
return;
fs_err(sdp,
@@ -368,17 +278,7 @@ void gfs2_assert_withdraw_i(struct gfs2_sbd *sdp, char *assertion,
"function = %s, file = %s, line = %u\n",
assertion, function, file, line);
- /*
- * If errors=panic was specified on mount, it won't help to delay the
- * withdraw.
- */
- if (sdp->sd_args.ar_errors == GFS2_ERRORS_PANIC)
- delayed = false;
-
- if (delayed)
- gfs2_withdraw_delayed(sdp);
- else
- gfs2_withdraw(sdp);
+ gfs2_withdraw(sdp);
dump_stack();
}
@@ -520,22 +420,18 @@ void gfs2_io_error_i(struct gfs2_sbd *sdp, const char *function, char *file,
}
/*
- * gfs2_io_error_bh_i - Flag a buffer I/O error
- * @withdraw: withdraw the filesystem
+ * gfs2_io_error_bh_i - Flag a buffer I/O error and withdraw
*/
void gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh,
- const char *function, char *file, unsigned int line,
- bool withdraw)
+ const char *function, char *file, unsigned int line)
{
- if (gfs2_withdrawing_or_withdrawn(sdp))
+ if (gfs2_withdrawn(sdp))
return;
fs_err(sdp, "fatal: I/O error - "
"block = %llu, "
"function = %s, file = %s, line = %u\n",
(unsigned long long)bh->b_blocknr, function, file, line);
- if (withdraw)
- gfs2_withdraw(sdp);
+ gfs2_withdraw(sdp);
}
-
diff --git a/fs/gfs2/util.h b/fs/gfs2/util.h
index da0373b1e82b..ffcc47d6b0b4 100644
--- a/fs/gfs2/util.h
+++ b/fs/gfs2/util.h
@@ -37,24 +37,14 @@ do { \
void gfs2_assert_withdraw_i(struct gfs2_sbd *sdp, char *assertion,
- const char *function, char *file, unsigned int line,
- bool delayed);
+ const char *function, char *file, unsigned int line);
#define gfs2_assert_withdraw(sdp, assertion) \
({ \
bool _bool = (assertion); \
if (unlikely(!_bool)) \
gfs2_assert_withdraw_i((sdp), #assertion, \
- __func__, __FILE__, __LINE__, false); \
- !_bool; \
- })
-
-#define gfs2_assert_withdraw_delayed(sdp, assertion) \
- ({ \
- bool _bool = (assertion); \
- if (unlikely(!_bool)) \
- gfs2_assert_withdraw_i((sdp), #assertion, \
- __func__, __FILE__, __LINE__, true); \
+ __func__, __FILE__, __LINE__); \
!_bool; \
})
@@ -161,14 +151,10 @@ gfs2_io_error_i((sdp), __func__, __FILE__, __LINE__)
void gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh,
- const char *function, char *file, unsigned int line,
- bool withdraw);
-
-#define gfs2_io_error_bh_wd(sdp, bh) \
-gfs2_io_error_bh_i((sdp), (bh), __func__, __FILE__, __LINE__, true)
+ const char *function, char *file, unsigned int line);
#define gfs2_io_error_bh(sdp, bh) \
-gfs2_io_error_bh_i((sdp), (bh), __func__, __FILE__, __LINE__, false)
+gfs2_io_error_bh_i((sdp), (bh), __func__, __FILE__, __LINE__)
extern struct kmem_cache *gfs2_glock_cachep;
@@ -193,38 +179,12 @@ static inline unsigned int gfs2_tune_get_i(struct gfs2_tune *gt,
}
/**
- * gfs2_withdraw_delayed - withdraw as soon as possible without deadlocks
- * @sdp: the superblock
- */
-static inline void gfs2_withdraw_delayed(struct gfs2_sbd *sdp)
-{
- set_bit(SDF_WITHDRAWING, &sdp->sd_flags);
-}
-
-/**
- * gfs2_withdrawing_or_withdrawn - test whether the file system is withdrawing
- * or withdrawn
+ * gfs2_withdrawn - test whether the file system is withdrawn
* @sdp: the superblock
*/
-static inline bool gfs2_withdrawing_or_withdrawn(struct gfs2_sbd *sdp)
+static inline bool gfs2_withdrawn(struct gfs2_sbd *sdp)
{
- return unlikely(test_bit(SDF_WITHDRAWN, &sdp->sd_flags) ||
- test_bit(SDF_WITHDRAWING, &sdp->sd_flags));
-}
-
-/**
- * gfs2_withdrawing - check if a withdraw is pending
- * @sdp: the superblock
- */
-static inline bool gfs2_withdrawing(struct gfs2_sbd *sdp)
-{
- return unlikely(test_bit(SDF_WITHDRAWING, &sdp->sd_flags) &&
- !test_bit(SDF_WITHDRAWN, &sdp->sd_flags));
-}
-
-static inline bool gfs2_withdraw_in_prog(struct gfs2_sbd *sdp)
-{
- return unlikely(test_bit(SDF_WITHDRAW_IN_PROG, &sdp->sd_flags));
+ return unlikely(test_bit(SDF_WITHDRAWN, &sdp->sd_flags));
}
#define gfs2_tune_get(sdp, field) \
@@ -232,6 +192,8 @@ gfs2_tune_get_i(&(sdp)->sd_tune, &(sdp)->sd_tune.field)
__printf(2, 3)
void gfs2_lm(struct gfs2_sbd *sdp, const char *fmt, ...);
+
+void gfs2_withdraw_func(struct work_struct *work);
void gfs2_withdraw(struct gfs2_sbd *sdp);
#endif /* __UTIL_DOT_H__ */
diff --git a/fs/hfs/.kunitconfig b/fs/hfs/.kunitconfig
new file mode 100644
index 000000000000..5caa9af1e3bb
--- /dev/null
+++ b/fs/hfs/.kunitconfig
@@ -0,0 +1,7 @@
+CONFIG_KUNIT=y
+CONFIG_HFS_FS=y
+CONFIG_HFS_KUNIT_TEST=y
+CONFIG_BLOCK=y
+CONFIG_BUFFER_HEAD=y
+CONFIG_NLS=y
+CONFIG_LEGACY_DIRECT_IO=y
diff --git a/fs/hfs/Kconfig b/fs/hfs/Kconfig
index 5ea5cd8ecea9..7f3cbe43b4b7 100644
--- a/fs/hfs/Kconfig
+++ b/fs/hfs/Kconfig
@@ -13,3 +13,18 @@ config HFS_FS
To compile this file system support as a module, choose M here: the
module will be called hfs.
+
+config HFS_KUNIT_TEST
+ tristate "KUnit tests for HFS filesystem" if !KUNIT_ALL_TESTS
+ depends on HFS_FS && KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds KUnit tests for the HFS filesystem.
+
+ KUnit tests run during boot and output the results to the debug
+ log in TAP format (https://testanything.org/). Only useful for
+ kernel devs running KUnit test harness and are not for inclusion
+ into a production build.
+
+ For more information on KUnit and unit tests in general please
+ refer to the KUnit documentation in Documentation/dev-tools/kunit/.
diff --git a/fs/hfs/Makefile b/fs/hfs/Makefile
index b65459bf3dc4..a7c9ce6b4609 100644
--- a/fs/hfs/Makefile
+++ b/fs/hfs/Makefile
@@ -9,3 +9,5 @@ hfs-objs := bitmap.o bfind.o bnode.o brec.o btree.o \
catalog.o dir.o extent.o inode.o attr.o mdb.o \
part_tbl.o string.o super.o sysdep.o trans.o
+# KUnit tests
+obj-$(CONFIG_HFS_KUNIT_TEST) += string_test.o
diff --git a/fs/hfs/bfind.c b/fs/hfs/bfind.c
index c2f840c49e60..d56e47bdc517 100644
--- a/fs/hfs/bfind.c
+++ b/fs/hfs/bfind.c
@@ -167,7 +167,7 @@ release:
return res;
}
-int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len)
+int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len)
{
int res;
diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c
index fcfffe75d84e..13d58c51fc46 100644
--- a/fs/hfs/bnode.c
+++ b/fs/hfs/bnode.c
@@ -16,14 +16,14 @@
#include "btree.h"
static inline
-bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
+bool is_bnode_offset_valid(struct hfs_bnode *node, u32 off)
{
bool is_valid = off < node->tree->node_size;
if (!is_valid) {
pr_err("requested invalid offset: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d\n",
+ "node_size %u, offset %u\n",
node->this, node->type, node->height,
node->tree->node_size, off);
}
@@ -32,7 +32,7 @@ bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
}
static inline
-int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
+u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len)
{
unsigned int node_size;
@@ -42,12 +42,12 @@ int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
node_size = node->tree->node_size;
if ((off + len) > node_size) {
- int new_len = (int)node_size - off;
+ u32 new_len = node_size - off;
pr_err("requested length has been corrected: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d, "
- "requested_len %d, corrected_len %d\n",
+ "node_size %u, offset %u, "
+ "requested_len %u, corrected_len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len, new_len);
@@ -57,12 +57,12 @@ int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
return len;
}
-void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
+void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len)
{
struct page *page;
- int pagenum;
- int bytes_read;
- int bytes_to_read;
+ u32 pagenum;
+ u32 bytes_read;
+ u32 bytes_to_read;
if (!is_bnode_offset_valid(node, off))
return;
@@ -70,7 +70,7 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d, len %d\n",
+ "node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@@ -86,7 +86,7 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
if (pagenum >= node->tree->pages_per_bnode)
break;
page = node->page[pagenum];
- bytes_to_read = min_t(int, len - bytes_read, PAGE_SIZE - off);
+ bytes_to_read = min_t(u32, len - bytes_read, PAGE_SIZE - off);
memcpy_from_page(buf + bytes_read, page, off, bytes_to_read);
@@ -95,7 +95,7 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
}
}
-u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
+u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off)
{
__be16 data;
// optimize later...
@@ -103,7 +103,7 @@ u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
return be16_to_cpu(data);
}
-u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
+u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off)
{
u8 data;
// optimize later...
@@ -111,10 +111,10 @@ u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
return data;
}
-void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
+void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off)
{
struct hfs_btree *tree;
- int key_len;
+ u32 key_len;
tree = node->tree;
if (node->type == HFS_NODE_LEAF ||
@@ -125,14 +125,14 @@ void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
if (key_len > sizeof(hfs_btree_key) || key_len < 1) {
memset(key, 0, sizeof(hfs_btree_key));
- pr_err("hfs: Invalid key length: %d\n", key_len);
+ pr_err("hfs: Invalid key length: %u\n", key_len);
return;
}
hfs_bnode_read(node, key, off, key_len);
}
-void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
+void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len)
{
struct page *page;
@@ -142,7 +142,7 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d, len %d\n",
+ "node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@@ -157,20 +157,20 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
set_page_dirty(page);
}
-void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data)
+void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data)
{
__be16 v = cpu_to_be16(data);
// optimize later...
hfs_bnode_write(node, &v, off, 2);
}
-void hfs_bnode_write_u8(struct hfs_bnode *node, int off, u8 data)
+void hfs_bnode_write_u8(struct hfs_bnode *node, u32 off, u8 data)
{
// optimize later...
hfs_bnode_write(node, &data, off, 1);
}
-void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
+void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len)
{
struct page *page;
@@ -180,7 +180,7 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d, len %d\n",
+ "node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@@ -195,8 +195,8 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
set_page_dirty(page);
}
-void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
- struct hfs_bnode *src_node, int src, int len)
+void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
+ struct hfs_bnode *src_node, u32 src, u32 len)
{
struct page *src_page, *dst_page;
@@ -216,7 +216,7 @@ void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
set_page_dirty(dst_page);
}
-void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
+void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len)
{
struct page *page;
void *ptr;
diff --git a/fs/hfs/brec.c b/fs/hfs/brec.c
index e49a141c87e5..5a2f740ddefd 100644
--- a/fs/hfs/brec.c
+++ b/fs/hfs/brec.c
@@ -62,7 +62,7 @@ u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec)
return retval;
}
-int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len)
+int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len)
{
struct hfs_btree *tree;
struct hfs_bnode *node, *new_node;
diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c
index 54c20d01c342..7bc425283d49 100644
--- a/fs/hfs/btree.c
+++ b/fs/hfs/btree.c
@@ -259,7 +259,7 @@ static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx)
}
/* Make sure @tree has enough space for the @rsvd_nodes */
-int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes)
+int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes)
{
struct inode *inode = tree->inode;
u32 count;
diff --git a/fs/hfs/btree.h b/fs/hfs/btree.h
index 0e6baee93245..99be858b2446 100644
--- a/fs/hfs/btree.h
+++ b/fs/hfs/btree.h
@@ -86,87 +86,46 @@ struct hfs_find_data {
/* btree.c */
-extern struct hfs_btree *hfs_btree_open(struct super_block *, u32, btree_keycmp);
-extern void hfs_btree_close(struct hfs_btree *);
-extern void hfs_btree_write(struct hfs_btree *);
-extern int hfs_bmap_reserve(struct hfs_btree *, int);
-extern struct hfs_bnode * hfs_bmap_alloc(struct hfs_btree *);
+extern struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id,
+ btree_keycmp keycmp);
+extern void hfs_btree_close(struct hfs_btree *tree);
+extern void hfs_btree_write(struct hfs_btree *tree);
+extern int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes);
+extern struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree);
extern void hfs_bmap_free(struct hfs_bnode *node);
/* bnode.c */
-extern void hfs_bnode_read(struct hfs_bnode *, void *, int, int);
-extern u16 hfs_bnode_read_u16(struct hfs_bnode *, int);
-extern u8 hfs_bnode_read_u8(struct hfs_bnode *, int);
-extern void hfs_bnode_read_key(struct hfs_bnode *, void *, int);
-extern void hfs_bnode_write(struct hfs_bnode *, void *, int, int);
-extern void hfs_bnode_write_u16(struct hfs_bnode *, int, u16);
-extern void hfs_bnode_write_u8(struct hfs_bnode *, int, u8);
-extern void hfs_bnode_clear(struct hfs_bnode *, int, int);
-extern void hfs_bnode_copy(struct hfs_bnode *, int,
- struct hfs_bnode *, int, int);
-extern void hfs_bnode_move(struct hfs_bnode *, int, int, int);
-extern void hfs_bnode_dump(struct hfs_bnode *);
-extern void hfs_bnode_unlink(struct hfs_bnode *);
-extern struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *, u32);
-extern struct hfs_bnode *hfs_bnode_find(struct hfs_btree *, u32);
-extern void hfs_bnode_unhash(struct hfs_bnode *);
-extern void hfs_bnode_free(struct hfs_bnode *);
-extern struct hfs_bnode *hfs_bnode_create(struct hfs_btree *, u32);
-extern void hfs_bnode_get(struct hfs_bnode *);
-extern void hfs_bnode_put(struct hfs_bnode *);
+extern void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len);
+extern u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off);
+extern u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off);
+extern void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off);
+extern void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len);
+extern void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data);
+extern void hfs_bnode_write_u8(struct hfs_bnode *node, u32 off, u8 data);
+extern void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len);
+extern void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
+ struct hfs_bnode *src_node, u32 src, u32 len);
+extern void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len);
+extern void hfs_bnode_dump(struct hfs_bnode *node);
+extern void hfs_bnode_unlink(struct hfs_bnode *node);
+extern struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid);
+extern struct hfs_bnode *hfs_bnode_find(struct hfs_btree *tree, u32 num);
+extern void hfs_bnode_unhash(struct hfs_bnode *node);
+extern void hfs_bnode_free(struct hfs_bnode *node);
+extern struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num);
+extern void hfs_bnode_get(struct hfs_bnode *node);
+extern void hfs_bnode_put(struct hfs_bnode *node);
/* brec.c */
-extern u16 hfs_brec_lenoff(struct hfs_bnode *, u16, u16 *);
-extern u16 hfs_brec_keylen(struct hfs_bnode *, u16);
-extern int hfs_brec_insert(struct hfs_find_data *, void *, int);
-extern int hfs_brec_remove(struct hfs_find_data *);
+extern u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off);
+extern u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec);
+extern int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len);
+extern int hfs_brec_remove(struct hfs_find_data *fd);
/* bfind.c */
-extern int hfs_find_init(struct hfs_btree *, struct hfs_find_data *);
-extern void hfs_find_exit(struct hfs_find_data *);
-extern int __hfs_brec_find(struct hfs_bnode *, struct hfs_find_data *);
-extern int hfs_brec_find(struct hfs_find_data *);
-extern int hfs_brec_read(struct hfs_find_data *, void *, int);
-extern int hfs_brec_goto(struct hfs_find_data *, int);
-
-
-struct hfs_bnode_desc {
- __be32 next; /* (V) Number of the next node at this level */
- __be32 prev; /* (V) Number of the prev node at this level */
- u8 type; /* (F) The type of node */
- u8 height; /* (F) The level of this node (leaves=1) */
- __be16 num_recs; /* (V) The number of records in this node */
- u16 reserved;
-} __packed;
-
-#define HFS_NODE_INDEX 0x00 /* An internal (index) node */
-#define HFS_NODE_HEADER 0x01 /* The tree header node (node 0) */
-#define HFS_NODE_MAP 0x02 /* Holds part of the bitmap of used nodes */
-#define HFS_NODE_LEAF 0xFF /* A leaf (ndNHeight==1) node */
-
-struct hfs_btree_header_rec {
- __be16 depth; /* (V) The number of levels in this B-tree */
- __be32 root; /* (V) The node number of the root node */
- __be32 leaf_count; /* (V) The number of leaf records */
- __be32 leaf_head; /* (V) The number of the first leaf node */
- __be32 leaf_tail; /* (V) The number of the last leaf node */
- __be16 node_size; /* (F) The number of bytes in a node (=512) */
- __be16 max_key_len; /* (F) The length of a key in an index node */
- __be32 node_count; /* (V) The total number of nodes */
- __be32 free_nodes; /* (V) The number of unused nodes */
- u16 reserved1;
- __be32 clump_size; /* (F) clump size. not usually used. */
- u8 btree_type; /* (F) BTree type */
- u8 reserved2;
- __be32 attributes; /* (F) attributes */
- u32 reserved3[16];
-} __packed;
-
-#define BTREE_ATTR_BADCLOSE 0x00000001 /* b-tree not closed properly. not
- used by hfsplus. */
-#define HFS_TREE_BIGKEYS 0x00000002 /* key length is u16 instead of u8.
- used by hfsplus. */
-#define HFS_TREE_VARIDXKEYS 0x00000004 /* variable key length instead of
- max key length. use din catalog
- b-tree but not in extents
- b-tree (hfsplus). */
+extern int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd);
+extern void hfs_find_exit(struct hfs_find_data *fd);
+extern int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd);
+extern int hfs_brec_find(struct hfs_find_data *fd);
+extern int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len);
+extern int hfs_brec_goto(struct hfs_find_data *fd, int cnt);
diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c
index caebabb6642f..b80ba40e3877 100644
--- a/fs/hfs/catalog.c
+++ b/fs/hfs/catalog.c
@@ -322,9 +322,9 @@ int hfs_correct_next_unused_CNID(struct super_block *sb, u32 cnid)
}
}
+ node_id = node->prev;
hfs_bnode_put(node);
- node_id = node->prev;
} while (node_id >= leaf_head);
return -ENOENT;
diff --git a/fs/hfs/hfs.h b/fs/hfs/hfs.h
index 6f194d0768b6..3f2293ff6fdd 100644
--- a/fs/hfs/hfs.h
+++ b/fs/hfs/hfs.h
@@ -9,274 +9,7 @@
#ifndef _HFS_H
#define _HFS_H
-/* offsets to various blocks */
-#define HFS_DD_BLK 0 /* Driver Descriptor block */
-#define HFS_PMAP_BLK 1 /* First block of partition map */
-#define HFS_MDB_BLK 2 /* Block (w/i partition) of MDB */
-
-/* magic numbers for various disk blocks */
-#define HFS_DRVR_DESC_MAGIC 0x4552 /* "ER": driver descriptor map */
-#define HFS_OLD_PMAP_MAGIC 0x5453 /* "TS": old-type partition map */
-#define HFS_NEW_PMAP_MAGIC 0x504D /* "PM": new-type partition map */
-#define HFS_SUPER_MAGIC 0x4244 /* "BD": HFS MDB (super block) */
-#define HFS_MFS_SUPER_MAGIC 0xD2D7 /* MFS MDB (super block) */
-
-/* various FIXED size parameters */
-#define HFS_SECTOR_SIZE 512 /* size of an HFS sector */
-#define HFS_SECTOR_SIZE_BITS 9 /* log_2(HFS_SECTOR_SIZE) */
-#define HFS_NAMELEN 31 /* maximum length of an HFS filename */
-#define HFS_MAX_NAMELEN 128
-#define HFS_MAX_VALENCE 32767U
-
-/* Meanings of the drAtrb field of the MDB,
- * Reference: _Inside Macintosh: Files_ p. 2-61
- */
-#define HFS_SB_ATTRIB_HLOCK (1 << 7)
-#define HFS_SB_ATTRIB_UNMNT (1 << 8)
-#define HFS_SB_ATTRIB_SPARED (1 << 9)
-#define HFS_SB_ATTRIB_INCNSTNT (1 << 11)
-#define HFS_SB_ATTRIB_SLOCK (1 << 15)
-
-/* Some special File ID numbers */
-#define HFS_POR_CNID 1 /* Parent Of the Root */
-#define HFS_ROOT_CNID 2 /* ROOT directory */
-#define HFS_EXT_CNID 3 /* EXTents B-tree */
-#define HFS_CAT_CNID 4 /* CATalog B-tree */
-#define HFS_BAD_CNID 5 /* BAD blocks file */
-#define HFS_ALLOC_CNID 6 /* ALLOCation file (HFS+) */
-#define HFS_START_CNID 7 /* STARTup file (HFS+) */
-#define HFS_ATTR_CNID 8 /* ATTRibutes file (HFS+) */
-#define HFS_EXCH_CNID 15 /* ExchangeFiles temp id */
-#define HFS_FIRSTUSER_CNID 16
-
-/* values for hfs_cat_rec.cdrType */
-#define HFS_CDR_DIR 0x01 /* folder (directory) */
-#define HFS_CDR_FIL 0x02 /* file */
-#define HFS_CDR_THD 0x03 /* folder (directory) thread */
-#define HFS_CDR_FTH 0x04 /* file thread */
-
-/* legal values for hfs_ext_key.FkType and hfs_file.fork */
-#define HFS_FK_DATA 0x00
-#define HFS_FK_RSRC 0xFF
-
-/* bits in hfs_fil_entry.Flags */
-#define HFS_FIL_LOCK 0x01 /* locked */
-#define HFS_FIL_THD 0x02 /* file thread */
-#define HFS_FIL_DOPEN 0x04 /* data fork open */
-#define HFS_FIL_ROPEN 0x08 /* resource fork open */
-#define HFS_FIL_DIR 0x10 /* directory (always clear) */
-#define HFS_FIL_NOCOPY 0x40 /* copy-protected file */
-#define HFS_FIL_USED 0x80 /* open */
-
-/* bits in hfs_dir_entry.Flags. dirflags is 16 bits. */
-#define HFS_DIR_LOCK 0x01 /* locked */
-#define HFS_DIR_THD 0x02 /* directory thread */
-#define HFS_DIR_INEXPFOLDER 0x04 /* in a shared area */
-#define HFS_DIR_MOUNTED 0x08 /* mounted */
-#define HFS_DIR_DIR 0x10 /* directory (always set) */
-#define HFS_DIR_EXPFOLDER 0x20 /* share point */
-
-/* bits hfs_finfo.fdFlags */
-#define HFS_FLG_INITED 0x0100
-#define HFS_FLG_LOCKED 0x1000
-#define HFS_FLG_INVISIBLE 0x4000
-
-/*======== HFS structures as they appear on the disk ========*/
-
-/* Pascal-style string of up to 31 characters */
-struct hfs_name {
- u8 len;
- u8 name[HFS_NAMELEN];
-} __packed;
-
-struct hfs_point {
- __be16 v;
- __be16 h;
-} __packed;
-
-struct hfs_rect {
- __be16 top;
- __be16 left;
- __be16 bottom;
- __be16 right;
-} __packed;
-
-struct hfs_finfo {
- __be32 fdType;
- __be32 fdCreator;
- __be16 fdFlags;
- struct hfs_point fdLocation;
- __be16 fdFldr;
-} __packed;
-
-struct hfs_fxinfo {
- __be16 fdIconID;
- u8 fdUnused[8];
- __be16 fdComment;
- __be32 fdPutAway;
-} __packed;
-
-struct hfs_dinfo {
- struct hfs_rect frRect;
- __be16 frFlags;
- struct hfs_point frLocation;
- __be16 frView;
-} __packed;
-
-struct hfs_dxinfo {
- struct hfs_point frScroll;
- __be32 frOpenChain;
- __be16 frUnused;
- __be16 frComment;
- __be32 frPutAway;
-} __packed;
-
-union hfs_finder_info {
- struct {
- struct hfs_finfo finfo;
- struct hfs_fxinfo fxinfo;
- } file;
- struct {
- struct hfs_dinfo dinfo;
- struct hfs_dxinfo dxinfo;
- } dir;
-} __packed;
-
-/* Cast to a pointer to a generic bkey */
-#define HFS_BKEY(X) (((void)((X)->KeyLen)), ((struct hfs_bkey *)(X)))
-
-/* The key used in the catalog b-tree: */
-struct hfs_cat_key {
- u8 key_len; /* number of bytes in the key */
- u8 reserved; /* padding */
- __be32 ParID; /* CNID of the parent dir */
- struct hfs_name CName; /* The filename of the entry */
-} __packed;
-
-/* The key used in the extents b-tree: */
-struct hfs_ext_key {
- u8 key_len; /* number of bytes in the key */
- u8 FkType; /* HFS_FK_{DATA,RSRC} */
- __be32 FNum; /* The File ID of the file */
- __be16 FABN; /* allocation blocks number*/
-} __packed;
-
-typedef union hfs_btree_key {
- u8 key_len; /* number of bytes in the key */
- struct hfs_cat_key cat;
- struct hfs_ext_key ext;
-} hfs_btree_key;
-
-#define HFS_MAX_CAT_KEYLEN (sizeof(struct hfs_cat_key) - sizeof(u8))
-#define HFS_MAX_EXT_KEYLEN (sizeof(struct hfs_ext_key) - sizeof(u8))
-
-typedef union hfs_btree_key btree_key;
-
-struct hfs_extent {
- __be16 block;
- __be16 count;
-};
-typedef struct hfs_extent hfs_extent_rec[3];
-
-/* The catalog record for a file */
-struct hfs_cat_file {
- s8 type; /* The type of entry */
- u8 reserved;
- u8 Flags; /* Flags such as read-only */
- s8 Typ; /* file version number = 0 */
- struct hfs_finfo UsrWds; /* data used by the Finder */
- __be32 FlNum; /* The CNID */
- __be16 StBlk; /* obsolete */
- __be32 LgLen; /* The logical EOF of the data fork*/
- __be32 PyLen; /* The physical EOF of the data fork */
- __be16 RStBlk; /* obsolete */
- __be32 RLgLen; /* The logical EOF of the rsrc fork */
- __be32 RPyLen; /* The physical EOF of the rsrc fork */
- __be32 CrDat; /* The creation date */
- __be32 MdDat; /* The modified date */
- __be32 BkDat; /* The last backup date */
- struct hfs_fxinfo FndrInfo; /* more data for the Finder */
- __be16 ClpSize; /* number of bytes to allocate
- when extending files */
- hfs_extent_rec ExtRec; /* first extent record
- for the data fork */
- hfs_extent_rec RExtRec; /* first extent record
- for the resource fork */
- u32 Resrv; /* reserved by Apple */
-} __packed;
-
-/* the catalog record for a directory */
-struct hfs_cat_dir {
- s8 type; /* The type of entry */
- u8 reserved;
- __be16 Flags; /* flags */
- __be16 Val; /* Valence: number of files and
- dirs in the directory */
- __be32 DirID; /* The CNID */
- __be32 CrDat; /* The creation date */
- __be32 MdDat; /* The modification date */
- __be32 BkDat; /* The last backup date */
- struct hfs_dinfo UsrInfo; /* data used by the Finder */
- struct hfs_dxinfo FndrInfo; /* more data used by Finder */
- u8 Resrv[16]; /* reserved by Apple */
-} __packed;
-
-/* the catalog record for a thread */
-struct hfs_cat_thread {
- s8 type; /* The type of entry */
- u8 reserved[9]; /* reserved by Apple */
- __be32 ParID; /* CNID of parent directory */
- struct hfs_name CName; /* The name of this entry */
-} __packed;
-
-/* A catalog tree record */
-typedef union hfs_cat_rec {
- s8 type; /* The type of entry */
- struct hfs_cat_file file;
- struct hfs_cat_dir dir;
- struct hfs_cat_thread thread;
-} hfs_cat_rec;
-
-struct hfs_mdb {
- __be16 drSigWord; /* Signature word indicating fs type */
- __be32 drCrDate; /* fs creation date/time */
- __be32 drLsMod; /* fs modification date/time */
- __be16 drAtrb; /* fs attributes */
- __be16 drNmFls; /* number of files in root directory */
- __be16 drVBMSt; /* location (in 512-byte blocks)
- of the volume bitmap */
- __be16 drAllocPtr; /* location (in allocation blocks)
- to begin next allocation search */
- __be16 drNmAlBlks; /* number of allocation blocks */
- __be32 drAlBlkSiz; /* bytes in an allocation block */
- __be32 drClpSiz; /* clumpsize, the number of bytes to
- allocate when extending a file */
- __be16 drAlBlSt; /* location (in 512-byte blocks)
- of the first allocation block */
- __be32 drNxtCNID; /* CNID to assign to the next
- file or directory created */
- __be16 drFreeBks; /* number of free allocation blocks */
- u8 drVN[28]; /* the volume label */
- __be32 drVolBkUp; /* fs backup date/time */
- __be16 drVSeqNum; /* backup sequence number */
- __be32 drWrCnt; /* fs write count */
- __be32 drXTClpSiz; /* clumpsize for the extents B-tree */
- __be32 drCTClpSiz; /* clumpsize for the catalog B-tree */
- __be16 drNmRtDirs; /* number of directories in
- the root directory */
- __be32 drFilCnt; /* number of files in the fs */
- __be32 drDirCnt; /* number of directories in the fs */
- u8 drFndrInfo[32]; /* data used by the Finder */
- __be16 drEmbedSigWord; /* embedded volume signature */
- __be32 drEmbedExtent; /* starting block number (xdrStABN)
- and number of allocation blocks
- (xdrNumABlks) occupied by embedded
- volume */
- __be32 drXTFlSize; /* bytes in the extents B-tree */
- hfs_extent_rec drXTExtRec; /* extents B-tree's first 3 extents */
- __be32 drCTFlSize; /* bytes in the catalog B-tree */
- hfs_extent_rec drCTExtRec; /* catalog B-tree's first 3 extents */
-} __packed;
+#include <linux/hfs_common.h>
/*======== Data structures kept in memory ========*/
diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h
index fff149af89da..e94dbc04a1e4 100644
--- a/fs/hfs/hfs_fs.h
+++ b/fs/hfs/hfs_fs.h
@@ -18,7 +18,6 @@
#include <asm/byteorder.h>
#include <linux/uaccess.h>
-#include <linux/hfs_common.h>
#include "hfs.h"
@@ -140,74 +139,90 @@ struct hfs_sb_info {
#define HFS_FLG_ALT_MDB_DIRTY 2
/* bitmap.c */
-extern u32 hfs_vbm_search_free(struct super_block *, u32, u32 *);
-extern int hfs_clear_vbm_bits(struct super_block *, u16, u16);
+extern u32 hfs_vbm_search_free(struct super_block *sb, u32 goal, u32 *num_bits);
+extern int hfs_clear_vbm_bits(struct super_block *sb, u16 start, u16 count);
/* catalog.c */
-extern int hfs_cat_keycmp(const btree_key *, const btree_key *);
+extern int hfs_cat_keycmp(const btree_key *key1, const btree_key *key2);
struct hfs_find_data;
-extern int hfs_cat_find_brec(struct super_block *, u32, struct hfs_find_data *);
-extern int hfs_cat_create(u32, struct inode *, const struct qstr *, struct inode *);
-extern int hfs_cat_delete(u32, struct inode *, const struct qstr *);
-extern int hfs_cat_move(u32, struct inode *, const struct qstr *,
- struct inode *, const struct qstr *);
-extern void hfs_cat_build_key(struct super_block *, btree_key *, u32, const struct qstr *);
+extern int hfs_cat_find_brec(struct super_block *sb, u32 cnid,
+ struct hfs_find_data *fd);
+extern int hfs_cat_create(u32 cnid, struct inode *dir,
+ const struct qstr *str, struct inode *inode);
+extern int hfs_cat_delete(u32 cnid, struct inode *dir, const struct qstr *str);
+extern int hfs_cat_move(u32 cnid, struct inode *src_dir,
+ const struct qstr *src_name,
+ struct inode *dst_dir,
+ const struct qstr *dst_name);
+extern void hfs_cat_build_key(struct super_block *sb, btree_key *key,
+ u32 parent, const struct qstr *name);
/* dir.c */
extern const struct file_operations hfs_dir_operations;
extern const struct inode_operations hfs_dir_inode_operations;
/* extent.c */
-extern int hfs_ext_keycmp(const btree_key *, const btree_key *);
+extern int hfs_ext_keycmp(const btree_key *key1, const btree_key *key2);
extern u16 hfs_ext_find_block(struct hfs_extent *ext, u16 off);
-extern int hfs_free_fork(struct super_block *, struct hfs_cat_file *, int);
-extern int hfs_ext_write_extent(struct inode *);
-extern int hfs_extend_file(struct inode *);
-extern void hfs_file_truncate(struct inode *);
+extern int hfs_free_fork(struct super_block *sb,
+ struct hfs_cat_file *file, int type);
+extern int hfs_ext_write_extent(struct inode *inode);
+extern int hfs_extend_file(struct inode *inode);
+extern void hfs_file_truncate(struct inode *inode);
-extern int hfs_get_block(struct inode *, sector_t, struct buffer_head *, int);
+extern int hfs_get_block(struct inode *inode, sector_t block,
+ struct buffer_head *bh_result, int create);
/* inode.c */
extern const struct address_space_operations hfs_aops;
extern const struct address_space_operations hfs_btree_aops;
int hfs_write_begin(const struct kiocb *iocb, struct address_space *mapping,
- loff_t pos, unsigned len, struct folio **foliop, void **fsdata);
-extern struct inode *hfs_new_inode(struct inode *, const struct qstr *, umode_t);
-extern void hfs_inode_write_fork(struct inode *, struct hfs_extent *, __be32 *, __be32 *);
-extern int hfs_write_inode(struct inode *, struct writeback_control *);
-extern int hfs_inode_setattr(struct mnt_idmap *, struct dentry *,
- struct iattr *);
+ loff_t pos, unsigned int len, struct folio **foliop,
+ void **fsdata);
+extern struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name,
+ umode_t mode);
+extern void hfs_inode_write_fork(struct inode *inode, struct hfs_extent *ext,
+ __be32 *log_size, __be32 *phys_size);
+extern int hfs_write_inode(struct inode *inode, struct writeback_control *wbc);
+extern int hfs_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ struct iattr *attr);
extern void hfs_inode_read_fork(struct inode *inode, struct hfs_extent *ext,
- __be32 log_size, __be32 phys_size, u32 clump_size);
-extern struct inode *hfs_iget(struct super_block *, struct hfs_cat_key *, hfs_cat_rec *);
-extern void hfs_evict_inode(struct inode *);
-extern void hfs_delete_inode(struct inode *);
+ __be32 __log_size, __be32 phys_size,
+ u32 clump_size);
+extern struct inode *hfs_iget(struct super_block *sb, struct hfs_cat_key *key,
+ hfs_cat_rec *rec);
+extern void hfs_evict_inode(struct inode *inode);
+extern void hfs_delete_inode(struct inode *inode);
/* attr.c */
extern const struct xattr_handler * const hfs_xattr_handlers[];
/* mdb.c */
-extern int hfs_mdb_get(struct super_block *);
-extern void hfs_mdb_commit(struct super_block *);
-extern void hfs_mdb_close(struct super_block *);
-extern void hfs_mdb_put(struct super_block *);
+extern int hfs_mdb_get(struct super_block *sb);
+extern void hfs_mdb_commit(struct super_block *sb);
+extern void hfs_mdb_close(struct super_block *sb);
+extern void hfs_mdb_put(struct super_block *sb);
/* part_tbl.c */
-extern int hfs_part_find(struct super_block *, sector_t *, sector_t *);
+extern int hfs_part_find(struct super_block *sb,
+ sector_t *part_start, sector_t *part_size);
/* string.c */
extern const struct dentry_operations hfs_dentry_operations;
-extern int hfs_hash_dentry(const struct dentry *, struct qstr *);
-extern int hfs_strcmp(const unsigned char *, unsigned int,
- const unsigned char *, unsigned int);
+extern int hfs_hash_dentry(const struct dentry *dentry, struct qstr *this);
+extern int hfs_strcmp(const unsigned char *s1, unsigned int len1,
+ const unsigned char *s2, unsigned int len2);
extern int hfs_compare_dentry(const struct dentry *dentry,
- unsigned int len, const char *str, const struct qstr *name);
+ unsigned int len, const char *str,
+ const struct qstr *name);
/* trans.c */
-extern void hfs_asc2mac(struct super_block *, struct hfs_name *, const struct qstr *);
-extern int hfs_mac2asc(struct super_block *, char *, const struct hfs_name *);
+extern void hfs_asc2mac(struct super_block *sb,
+ struct hfs_name *out, const struct qstr *in);
+extern int hfs_mac2asc(struct super_block *sb,
+ char *out, const struct hfs_name *in);
/* super.c */
extern void hfs_mark_mdb_dirty(struct super_block *sb);
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index 81ad93e6312f..524db1389737 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -45,7 +45,8 @@ static void hfs_write_failed(struct address_space *mapping, loff_t to)
}
int hfs_write_begin(const struct kiocb *iocb, struct address_space *mapping,
- loff_t pos, unsigned len, struct folio **foliop, void **fsdata)
+ loff_t pos, unsigned int len, struct folio **foliop,
+ void **fsdata)
{
int ret;
diff --git a/fs/hfs/string.c b/fs/hfs/string.c
index 3912209153a8..0cfa35e82abc 100644
--- a/fs/hfs/string.c
+++ b/fs/hfs/string.c
@@ -16,6 +16,8 @@
#include "hfs_fs.h"
#include <linux/dcache.h>
+#include <kunit/visibility.h>
+
/*================ File-local variables ================*/
/*
@@ -65,6 +67,7 @@ int hfs_hash_dentry(const struct dentry *dentry, struct qstr *this)
this->hash = end_name_hash(hash);
return 0;
}
+EXPORT_SYMBOL_IF_KUNIT(hfs_hash_dentry);
/*
* Compare two strings in the HFS filename character ordering
@@ -87,6 +90,7 @@ int hfs_strcmp(const unsigned char *s1, unsigned int len1,
}
return len1 - len2;
}
+EXPORT_SYMBOL_IF_KUNIT(hfs_strcmp);
/*
* Test for equality of two strings in the HFS filename character ordering.
@@ -112,3 +116,4 @@ int hfs_compare_dentry(const struct dentry *dentry,
}
return 0;
}
+EXPORT_SYMBOL_IF_KUNIT(hfs_compare_dentry);
diff --git a/fs/hfs/string_test.c b/fs/hfs/string_test.c
new file mode 100644
index 000000000000..e1bf6f954312
--- /dev/null
+++ b/fs/hfs/string_test.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit tests for HFS string operations
+ *
+ * Copyright (C) 2025 Viacheslav Dubeyko <slava@dubeyko.com>
+ */
+
+#include <kunit/test.h>
+#include <linux/dcache.h>
+#include "hfs_fs.h"
+
+/* Test hfs_strcmp function */
+static void hfs_strcmp_test(struct kunit *test)
+{
+ /* Test equal strings */
+ KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("hello", 5, "hello", 5));
+ KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("test", 4, "test", 4));
+ KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("", 0, "", 0));
+
+ /* Test unequal strings */
+ KUNIT_EXPECT_NE(test, 0, hfs_strcmp("hello", 5, "world", 5));
+ KUNIT_EXPECT_NE(test, 0, hfs_strcmp("test", 4, "testing", 7));
+
+ /* Test different lengths */
+ KUNIT_EXPECT_LT(test, hfs_strcmp("test", 4, "testing", 7), 0);
+ KUNIT_EXPECT_GT(test, hfs_strcmp("testing", 7, "test", 4), 0);
+
+ /* Test case insensitive comparison (HFS should handle case) */
+ KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("Test", 4, "TEST", 4));
+ KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("hello", 5, "HELLO", 5));
+
+ /* Test with special characters */
+ KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("file.txt", 8, "file.txt", 8));
+ KUNIT_EXPECT_NE(test, 0, hfs_strcmp("file.txt", 8, "file.dat", 8));
+
+ /* Test boundary cases */
+ KUNIT_EXPECT_EQ(test, 0, hfs_strcmp("a", 1, "a", 1));
+ KUNIT_EXPECT_NE(test, 0, hfs_strcmp("a", 1, "b", 1));
+}
+
+/* Test hfs_hash_dentry function */
+static void hfs_hash_dentry_test(struct kunit *test)
+{
+ struct qstr test_name1, test_name2, test_name3;
+ struct dentry dentry = {};
+ char name1[] = "testfile";
+ char name2[] = "TestFile";
+ char name3[] = "different";
+
+ /* Initialize test strings */
+ test_name1.name = name1;
+ test_name1.len = strlen(name1);
+ test_name1.hash = 0;
+
+ test_name2.name = name2;
+ test_name2.len = strlen(name2);
+ test_name2.hash = 0;
+
+ test_name3.name = name3;
+ test_name3.len = strlen(name3);
+ test_name3.hash = 0;
+
+ /* Test hashing */
+ KUNIT_EXPECT_EQ(test, 0, hfs_hash_dentry(&dentry, &test_name1));
+ KUNIT_EXPECT_EQ(test, 0, hfs_hash_dentry(&dentry, &test_name2));
+ KUNIT_EXPECT_EQ(test, 0, hfs_hash_dentry(&dentry, &test_name3));
+
+ /* Case insensitive names should hash the same */
+ KUNIT_EXPECT_EQ(test, test_name1.hash, test_name2.hash);
+
+ /* Different names should have different hashes */
+ KUNIT_EXPECT_NE(test, test_name1.hash, test_name3.hash);
+}
+
+/* Test hfs_compare_dentry function */
+static void hfs_compare_dentry_test(struct kunit *test)
+{
+ struct qstr test_name;
+ struct dentry dentry = {};
+ char name[] = "TestFile";
+
+ test_name.name = name;
+ test_name.len = strlen(name);
+
+ /* Test exact match */
+ KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, 8,
+ "TestFile", &test_name));
+
+ /* Test case insensitive match */
+ KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, 8,
+ "testfile", &test_name));
+ KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, 8,
+ "TESTFILE", &test_name));
+
+ /* Test different names */
+ KUNIT_EXPECT_EQ(test, 1, hfs_compare_dentry(&dentry, 8,
+ "DiffFile", &test_name));
+
+ /* Test different lengths */
+ KUNIT_EXPECT_EQ(test, 1, hfs_compare_dentry(&dentry, 7,
+ "TestFil", &test_name));
+ KUNIT_EXPECT_EQ(test, 1, hfs_compare_dentry(&dentry, 9,
+ "TestFiles", &test_name));
+
+ /* Test empty string */
+ test_name.name = "";
+ test_name.len = 0;
+ KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, 0, "", &test_name));
+
+ /* Test HFS_NAMELEN boundary */
+ test_name.name = "This_is_a_very_long_filename_that_exceeds_normal_limits";
+ test_name.len = strlen(test_name.name);
+ KUNIT_EXPECT_EQ(test, 0, hfs_compare_dentry(&dentry, HFS_NAMELEN,
+ "This_is_a_very_long_filename_th", &test_name));
+}
+
+static struct kunit_case hfs_string_test_cases[] = {
+ KUNIT_CASE(hfs_strcmp_test),
+ KUNIT_CASE(hfs_hash_dentry_test),
+ KUNIT_CASE(hfs_compare_dentry_test),
+ {}
+};
+
+static struct kunit_suite hfs_string_test_suite = {
+ .name = "hfs_string",
+ .test_cases = hfs_string_test_cases,
+};
+
+kunit_test_suite(hfs_string_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests for HFS string operations");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
diff --git a/fs/hfsplus/.kunitconfig b/fs/hfsplus/.kunitconfig
new file mode 100644
index 000000000000..6c96dc7e872c
--- /dev/null
+++ b/fs/hfsplus/.kunitconfig
@@ -0,0 +1,8 @@
+CONFIG_KUNIT=y
+CONFIG_HFSPLUS_FS=y
+CONFIG_HFSPLUS_KUNIT_TEST=y
+CONFIG_BLOCK=y
+CONFIG_BUFFER_HEAD=y
+CONFIG_NLS=y
+CONFIG_NLS_UTF8=y
+CONFIG_LEGACY_DIRECT_IO=y
diff --git a/fs/hfsplus/Kconfig b/fs/hfsplus/Kconfig
index 8ce4a33a9ac7..ca8401cb6954 100644
--- a/fs/hfsplus/Kconfig
+++ b/fs/hfsplus/Kconfig
@@ -14,3 +14,18 @@ config HFSPLUS_FS
MacOS 8. It includes all Mac specific filesystem data such as
data forks and creator codes, but it also has several UNIX
style features such as file ownership and permissions.
+
+config HFSPLUS_KUNIT_TEST
+ tristate "KUnit tests for HFS+ filesystem" if !KUNIT_ALL_TESTS
+ depends on HFSPLUS_FS && KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds KUnit tests for the HFS+ filesystem.
+
+ KUnit tests run during boot and output the results to the debug
+ log in TAP format (https://testanything.org/). Only useful for
+ kernel devs running KUnit test harness and are not for inclusion
+ into a production build.
+
+ For more information on KUnit and unit tests in general please
+ refer to the KUnit documentation in Documentation/dev-tools/kunit/.
diff --git a/fs/hfsplus/Makefile b/fs/hfsplus/Makefile
index 9ed20e64b983..f2a9ae697e81 100644
--- a/fs/hfsplus/Makefile
+++ b/fs/hfsplus/Makefile
@@ -8,3 +8,6 @@ obj-$(CONFIG_HFSPLUS_FS) += hfsplus.o
hfsplus-objs := super.o options.o inode.o ioctl.o extents.o catalog.o dir.o btree.o \
bnode.o brec.o bfind.o tables.o unicode.o wrapper.o bitmap.o part_tbl.o \
attributes.o xattr.o xattr_user.o xattr_security.o xattr_trusted.o
+
+# KUnit tests
+obj-$(CONFIG_HFSPLUS_KUNIT_TEST) += unicode_test.o
diff --git a/fs/hfsplus/bfind.c b/fs/hfsplus/bfind.c
index afc9c89e8c6a..336d654861c5 100644
--- a/fs/hfsplus/bfind.c
+++ b/fs/hfsplus/bfind.c
@@ -210,7 +210,7 @@ release:
return res;
}
-int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len)
+int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len)
{
int res;
diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c
index 63e652ad1e0d..191661af9677 100644
--- a/fs/hfsplus/bnode.c
+++ b/fs/hfsplus/bnode.c
@@ -20,10 +20,10 @@
/* Copy a specified range of bytes from the raw data of a node */
-void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
+void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len)
{
struct page **pagep;
- int l;
+ u32 l;
if (!is_bnode_offset_valid(node, off))
return;
@@ -31,7 +31,7 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d, len %d\n",
+ "node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@@ -43,17 +43,17 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
pagep = node->page + (off >> PAGE_SHIFT);
off &= ~PAGE_MASK;
- l = min_t(int, len, PAGE_SIZE - off);
+ l = min_t(u32, len, PAGE_SIZE - off);
memcpy_from_page(buf, *pagep, off, l);
while ((len -= l) != 0) {
buf += l;
- l = min_t(int, len, PAGE_SIZE);
+ l = min_t(u32, len, PAGE_SIZE);
memcpy_from_page(buf, *++pagep, 0, l);
}
}
-u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
+u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off)
{
__be16 data;
/* TODO: optimize later... */
@@ -61,7 +61,7 @@ u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
return be16_to_cpu(data);
}
-u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
+u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off)
{
u8 data;
/* TODO: optimize later... */
@@ -69,10 +69,10 @@ u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
return data;
}
-void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
+void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off)
{
struct hfs_btree *tree;
- int key_len;
+ u32 key_len;
tree = node->tree;
if (node->type == HFS_NODE_LEAF ||
@@ -84,17 +84,17 @@ void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
if (key_len > sizeof(hfsplus_btree_key) || key_len < 1) {
memset(key, 0, sizeof(hfsplus_btree_key));
- pr_err("hfsplus: Invalid key length: %d\n", key_len);
+ pr_err("hfsplus: Invalid key length: %u\n", key_len);
return;
}
hfs_bnode_read(node, key, off, key_len);
}
-void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
+void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len)
{
struct page **pagep;
- int l;
+ u32 l;
if (!is_bnode_offset_valid(node, off))
return;
@@ -102,7 +102,7 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d, len %d\n",
+ "node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@@ -114,29 +114,29 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
pagep = node->page + (off >> PAGE_SHIFT);
off &= ~PAGE_MASK;
- l = min_t(int, len, PAGE_SIZE - off);
+ l = min_t(u32, len, PAGE_SIZE - off);
memcpy_to_page(*pagep, off, buf, l);
set_page_dirty(*pagep);
while ((len -= l) != 0) {
buf += l;
- l = min_t(int, len, PAGE_SIZE);
+ l = min_t(u32, len, PAGE_SIZE);
memcpy_to_page(*++pagep, 0, buf, l);
set_page_dirty(*pagep);
}
}
-void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data)
+void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data)
{
__be16 v = cpu_to_be16(data);
/* TODO: optimize later... */
hfs_bnode_write(node, &v, off, 2);
}
-void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
+void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len)
{
struct page **pagep;
- int l;
+ u32 l;
if (!is_bnode_offset_valid(node, off))
return;
@@ -144,7 +144,7 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d, len %d\n",
+ "node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@@ -156,22 +156,22 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
pagep = node->page + (off >> PAGE_SHIFT);
off &= ~PAGE_MASK;
- l = min_t(int, len, PAGE_SIZE - off);
+ l = min_t(u32, len, PAGE_SIZE - off);
memzero_page(*pagep, off, l);
set_page_dirty(*pagep);
while ((len -= l) != 0) {
- l = min_t(int, len, PAGE_SIZE);
+ l = min_t(u32, len, PAGE_SIZE);
memzero_page(*++pagep, 0, l);
set_page_dirty(*pagep);
}
}
-void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
- struct hfs_bnode *src_node, int src, int len)
+void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
+ struct hfs_bnode *src_node, u32 src, u32 len)
{
struct page **src_page, **dst_page;
- int l;
+ u32 l;
hfs_dbg("dst %u, src %u, len %u\n", dst, src, len);
if (!len)
@@ -188,12 +188,12 @@ void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
dst &= ~PAGE_MASK;
if (src == dst) {
- l = min_t(int, len, PAGE_SIZE - src);
+ l = min_t(u32, len, PAGE_SIZE - src);
memcpy_page(*dst_page, src, *src_page, src, l);
set_page_dirty(*dst_page);
while ((len -= l) != 0) {
- l = min_t(int, len, PAGE_SIZE);
+ l = min_t(u32, len, PAGE_SIZE);
memcpy_page(*++dst_page, 0, *++src_page, 0, l);
set_page_dirty(*dst_page);
}
@@ -225,11 +225,11 @@ void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
}
}
-void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
+void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len)
{
struct page **src_page, **dst_page;
void *src_ptr, *dst_ptr;
- int l;
+ u32 l;
hfs_dbg("dst %u, src %u, len %u\n", dst, src, len);
if (!len)
@@ -299,7 +299,7 @@ void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
dst &= ~PAGE_MASK;
if (src == dst) {
- l = min_t(int, len, PAGE_SIZE - src);
+ l = min_t(u32, len, PAGE_SIZE - src);
dst_ptr = kmap_local_page(*dst_page) + src;
src_ptr = kmap_local_page(*src_page) + src;
@@ -309,7 +309,7 @@ void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
kunmap_local(dst_ptr);
while ((len -= l) != 0) {
- l = min_t(int, len, PAGE_SIZE);
+ l = min_t(u32, len, PAGE_SIZE);
dst_ptr = kmap_local_page(*++dst_page);
src_ptr = kmap_local_page(*++src_page);
memmove(dst_ptr, src_ptr, l);
@@ -481,6 +481,7 @@ static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid)
tree->node_hash[hash] = node;
tree->node_hash_cnt++;
} else {
+ hfs_bnode_get(node2);
spin_unlock(&tree->hash_lock);
kfree(node);
wait_event(node2->lock_wq,
@@ -704,6 +705,5 @@ bool hfs_bnode_need_zeroout(struct hfs_btree *tree)
struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
const u32 volume_attr = be32_to_cpu(sbi->s_vhdr->attributes);
- return tree->cnid == HFSPLUS_CAT_CNID &&
- volume_attr & HFSPLUS_VOL_UNUSED_NODE_FIX;
+ return volume_attr & HFSPLUS_VOL_UNUSED_NODE_FIX;
}
diff --git a/fs/hfsplus/brec.c b/fs/hfsplus/brec.c
index b4645102feec..6796c1a80e99 100644
--- a/fs/hfsplus/brec.c
+++ b/fs/hfsplus/brec.c
@@ -60,7 +60,7 @@ u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec)
return retval;
}
-int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len)
+int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len)
{
struct hfs_btree *tree;
struct hfs_bnode *node, *new_node;
diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c
index 7cc5aea14572..229f25dc7c49 100644
--- a/fs/hfsplus/btree.c
+++ b/fs/hfsplus/btree.c
@@ -344,7 +344,7 @@ static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx)
}
/* Make sure @tree has enough space for the @rsvd_nodes */
-int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes)
+int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes)
{
struct inode *inode = tree->inode;
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c
index 1b3e27a0d5e0..cadf0b5f9342 100644
--- a/fs/hfsplus/dir.c
+++ b/fs/hfsplus/dir.c
@@ -552,8 +552,13 @@ static int hfsplus_rename(struct mnt_idmap *idmap,
res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata,
old_dir, &old_dentry->d_name,
new_dir, &new_dentry->d_name);
- if (!res)
+ if (!res) {
new_dentry->d_fsdata = old_dentry->d_fsdata;
+
+ res = hfsplus_cat_write_inode(old_dir);
+ if (!res)
+ res = hfsplus_cat_write_inode(new_dir);
+ }
return res;
}
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index 89e8b19c127b..45fe3a12ecba 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -16,7 +16,6 @@
#include <linux/buffer_head.h>
#include <linux/blkdev.h>
#include <linux/fs_context.h>
-#include <linux/hfs_common.h>
#include "hfsplus_raw.h"
/* Runtime config options */
@@ -357,21 +356,21 @@ u32 hfsplus_calc_btree_clump_size(u32 block_size, u32 node_size, u64 sectors,
struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id);
void hfs_btree_close(struct hfs_btree *tree);
int hfs_btree_write(struct hfs_btree *tree);
-int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes);
+int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes);
struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree);
void hfs_bmap_free(struct hfs_bnode *node);
/* bnode.c */
-void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len);
-u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off);
-u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off);
-void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off);
-void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len);
-void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data);
-void hfs_bnode_clear(struct hfs_bnode *node, int off, int len);
-void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
- struct hfs_bnode *src_node, int src, int len);
-void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len);
+void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len);
+u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off);
+u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off);
+void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off);
+void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len);
+void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data);
+void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len);
+void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
+ struct hfs_bnode *src_node, u32 src, u32 len);
+void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len);
void hfs_bnode_dump(struct hfs_bnode *node);
void hfs_bnode_unlink(struct hfs_bnode *node);
struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid);
@@ -386,7 +385,7 @@ bool hfs_bnode_need_zeroout(struct hfs_btree *tree);
/* brec.c */
u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off);
u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec);
-int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len);
+int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len);
int hfs_brec_remove(struct hfs_find_data *fd);
/* bfind.c */
@@ -399,7 +398,7 @@ int hfs_find_rec_by_key(struct hfs_bnode *bnode, struct hfs_find_data *fd,
int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd,
search_strategy_t rec_found);
int hfs_brec_find(struct hfs_find_data *fd, search_strategy_t do_key_compare);
-int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len);
+int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len);
int hfs_brec_goto(struct hfs_find_data *fd, int cnt);
/* catalog.c */
@@ -477,6 +476,8 @@ int hfs_part_find(struct super_block *sb, sector_t *part_start,
/* super.c */
struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino);
void hfsplus_mark_mdb_dirty(struct super_block *sb);
+void hfsplus_prepare_volume_header_for_commit(struct hfsplus_vh *vhdr);
+int hfsplus_commit_superblock(struct super_block *sb);
/* tables.c */
extern u16 hfsplus_case_fold_table[];
@@ -549,14 +550,14 @@ hfsplus_btree_lock_class(struct hfs_btree *tree)
}
static inline
-bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
+bool is_bnode_offset_valid(struct hfs_bnode *node, u32 off)
{
bool is_valid = off < node->tree->node_size;
if (!is_valid) {
pr_err("requested invalid offset: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d\n",
+ "node_size %u, offset %u\n",
node->this, node->type, node->height,
node->tree->node_size, off);
}
@@ -565,7 +566,7 @@ bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
}
static inline
-int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
+u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len)
{
unsigned int node_size;
@@ -575,12 +576,12 @@ int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
node_size = node->tree->node_size;
if ((off + len) > node_size) {
- int new_len = (int)node_size - off;
+ u32 new_len = node_size - off;
pr_err("requested length has been corrected: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d, "
- "requested_len %d, corrected_len %d\n",
+ "node_size %u, offset %u, "
+ "requested_len %u, corrected_len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len, new_len);
diff --git a/fs/hfsplus/hfsplus_raw.h b/fs/hfsplus/hfsplus_raw.h
index 68b4240c6191..83b5dbde924b 100644
--- a/fs/hfsplus/hfsplus_raw.h
+++ b/fs/hfsplus/hfsplus_raw.h
@@ -15,398 +15,6 @@
#define _LINUX_HFSPLUS_RAW_H
#include <linux/types.h>
-
-/* Some constants */
-#define HFSPLUS_SECTOR_SIZE 512
-#define HFSPLUS_SECTOR_SHIFT 9
-#define HFSPLUS_VOLHEAD_SECTOR 2
-#define HFSPLUS_VOLHEAD_SIG 0x482b
-#define HFSPLUS_VOLHEAD_SIGX 0x4858
-#define HFSPLUS_SUPER_MAGIC 0x482b
-#define HFSPLUS_MIN_VERSION 4
-#define HFSPLUS_CURRENT_VERSION 5
-
-#define HFSP_WRAP_MAGIC 0x4244
-#define HFSP_WRAP_ATTRIB_SLOCK 0x8000
-#define HFSP_WRAP_ATTRIB_SPARED 0x0200
-
-#define HFSP_WRAPOFF_SIG 0x00
-#define HFSP_WRAPOFF_ATTRIB 0x0A
-#define HFSP_WRAPOFF_ABLKSIZE 0x14
-#define HFSP_WRAPOFF_ABLKSTART 0x1C
-#define HFSP_WRAPOFF_EMBEDSIG 0x7C
-#define HFSP_WRAPOFF_EMBEDEXT 0x7E
-
-#define HFSP_HIDDENDIR_NAME \
- "\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80HFS+ Private Data"
-
-#define HFSP_HARDLINK_TYPE 0x686c6e6b /* 'hlnk' */
-#define HFSP_HFSPLUS_CREATOR 0x6866732b /* 'hfs+' */
-
-#define HFSP_SYMLINK_TYPE 0x736c6e6b /* 'slnk' */
-#define HFSP_SYMLINK_CREATOR 0x72686170 /* 'rhap' */
-
-#define HFSP_MOUNT_VERSION 0x482b4c78 /* 'H+Lx' */
-
-/* Structures used on disk */
-
-typedef __be32 hfsplus_cnid;
-typedef __be16 hfsplus_unichr;
-
-#define HFSPLUS_MAX_STRLEN 255
-#define HFSPLUS_ATTR_MAX_STRLEN 127
-
-/* A "string" as used in filenames, etc. */
-struct hfsplus_unistr {
- __be16 length;
- hfsplus_unichr unicode[HFSPLUS_MAX_STRLEN];
-} __packed;
-
-/*
- * A "string" is used in attributes file
- * for name of extended attribute
- */
-struct hfsplus_attr_unistr {
- __be16 length;
- hfsplus_unichr unicode[HFSPLUS_ATTR_MAX_STRLEN];
-} __packed;
-
-/* POSIX permissions */
-struct hfsplus_perm {
- __be32 owner;
- __be32 group;
- u8 rootflags;
- u8 userflags;
- __be16 mode;
- __be32 dev;
-} __packed;
-
-#define HFSPLUS_FLG_NODUMP 0x01
-#define HFSPLUS_FLG_IMMUTABLE 0x02
-#define HFSPLUS_FLG_APPEND 0x04
-
-/* A single contiguous area of a file */
-struct hfsplus_extent {
- __be32 start_block;
- __be32 block_count;
-} __packed;
-typedef struct hfsplus_extent hfsplus_extent_rec[8];
-
-/* Information for a "Fork" in a file */
-struct hfsplus_fork_raw {
- __be64 total_size;
- __be32 clump_size;
- __be32 total_blocks;
- hfsplus_extent_rec extents;
-} __packed;
-
-/* HFS+ Volume Header */
-struct hfsplus_vh {
- __be16 signature;
- __be16 version;
- __be32 attributes;
- __be32 last_mount_vers;
- u32 reserved;
-
- __be32 create_date;
- __be32 modify_date;
- __be32 backup_date;
- __be32 checked_date;
-
- __be32 file_count;
- __be32 folder_count;
-
- __be32 blocksize;
- __be32 total_blocks;
- __be32 free_blocks;
-
- __be32 next_alloc;
- __be32 rsrc_clump_sz;
- __be32 data_clump_sz;
- hfsplus_cnid next_cnid;
-
- __be32 write_count;
- __be64 encodings_bmp;
-
- u32 finder_info[8];
-
- struct hfsplus_fork_raw alloc_file;
- struct hfsplus_fork_raw ext_file;
- struct hfsplus_fork_raw cat_file;
- struct hfsplus_fork_raw attr_file;
- struct hfsplus_fork_raw start_file;
-} __packed;
-
-/* HFS+ volume attributes */
-#define HFSPLUS_VOL_UNMNT (1 << 8)
-#define HFSPLUS_VOL_SPARE_BLK (1 << 9)
-#define HFSPLUS_VOL_NOCACHE (1 << 10)
-#define HFSPLUS_VOL_INCNSTNT (1 << 11)
-#define HFSPLUS_VOL_NODEID_REUSED (1 << 12)
-#define HFSPLUS_VOL_JOURNALED (1 << 13)
-#define HFSPLUS_VOL_SOFTLOCK (1 << 15)
-#define HFSPLUS_VOL_UNUSED_NODE_FIX (1 << 31)
-
-/* HFS+ BTree node descriptor */
-struct hfs_bnode_desc {
- __be32 next;
- __be32 prev;
- s8 type;
- u8 height;
- __be16 num_recs;
- u16 reserved;
-} __packed;
-
-/* HFS+ BTree node types */
-#define HFS_NODE_INDEX 0x00 /* An internal (index) node */
-#define HFS_NODE_HEADER 0x01 /* The tree header node (node 0) */
-#define HFS_NODE_MAP 0x02 /* Holds part of the bitmap of used nodes */
-#define HFS_NODE_LEAF 0xFF /* A leaf (ndNHeight==1) node */
-
-/* HFS+ BTree header */
-struct hfs_btree_header_rec {
- __be16 depth;
- __be32 root;
- __be32 leaf_count;
- __be32 leaf_head;
- __be32 leaf_tail;
- __be16 node_size;
- __be16 max_key_len;
- __be32 node_count;
- __be32 free_nodes;
- u16 reserved1;
- __be32 clump_size;
- u8 btree_type;
- u8 key_type;
- __be32 attributes;
- u32 reserved3[16];
-} __packed;
-
-/* BTree attributes */
-#define HFS_TREE_BIGKEYS 2
-#define HFS_TREE_VARIDXKEYS 4
-
-/* HFS+ BTree misc info */
-#define HFSPLUS_TREE_HEAD 0
-#define HFSPLUS_NODE_MXSZ 32768
-#define HFSPLUS_ATTR_TREE_NODE_SIZE 8192
-#define HFSPLUS_BTREE_HDR_NODE_RECS_COUNT 3
-#define HFSPLUS_BTREE_HDR_USER_BYTES 128
-
-/* Some special File ID numbers (stolen from hfs.h) */
-#define HFSPLUS_POR_CNID 1 /* Parent Of the Root */
-#define HFSPLUS_ROOT_CNID 2 /* ROOT directory */
-#define HFSPLUS_EXT_CNID 3 /* EXTents B-tree */
-#define HFSPLUS_CAT_CNID 4 /* CATalog B-tree */
-#define HFSPLUS_BAD_CNID 5 /* BAD blocks file */
-#define HFSPLUS_ALLOC_CNID 6 /* ALLOCation file */
-#define HFSPLUS_START_CNID 7 /* STARTup file */
-#define HFSPLUS_ATTR_CNID 8 /* ATTRibutes file */
-#define HFSPLUS_EXCH_CNID 15 /* ExchangeFiles temp id */
-#define HFSPLUS_FIRSTUSER_CNID 16 /* first available user id */
-
-/* btree key type */
-#define HFSPLUS_KEY_CASEFOLDING 0xCF /* case-insensitive */
-#define HFSPLUS_KEY_BINARY 0xBC /* case-sensitive */
-
-/* HFS+ catalog entry key */
-struct hfsplus_cat_key {
- __be16 key_len;
- hfsplus_cnid parent;
- struct hfsplus_unistr name;
-} __packed;
-
-#define HFSPLUS_CAT_KEYLEN (sizeof(struct hfsplus_cat_key))
-
-/* Structs from hfs.h */
-struct hfsp_point {
- __be16 v;
- __be16 h;
-} __packed;
-
-struct hfsp_rect {
- __be16 top;
- __be16 left;
- __be16 bottom;
- __be16 right;
-} __packed;
-
-
-/* HFS directory info (stolen from hfs.h */
-struct DInfo {
- struct hfsp_rect frRect;
- __be16 frFlags;
- struct hfsp_point frLocation;
- __be16 frView;
-} __packed;
-
-struct DXInfo {
- struct hfsp_point frScroll;
- __be32 frOpenChain;
- __be16 frUnused;
- __be16 frComment;
- __be32 frPutAway;
-} __packed;
-
-/* HFS+ folder data (part of an hfsplus_cat_entry) */
-struct hfsplus_cat_folder {
- __be16 type;
- __be16 flags;
- __be32 valence;
- hfsplus_cnid id;
- __be32 create_date;
- __be32 content_mod_date;
- __be32 attribute_mod_date;
- __be32 access_date;
- __be32 backup_date;
- struct hfsplus_perm permissions;
- struct_group_attr(info, __packed,
- struct DInfo user_info;
- struct DXInfo finder_info;
- );
- __be32 text_encoding;
- __be32 subfolders; /* Subfolder count in HFSX. Reserved in HFS+. */
-} __packed;
-
-/* HFS file info (stolen from hfs.h) */
-struct FInfo {
- __be32 fdType;
- __be32 fdCreator;
- __be16 fdFlags;
- struct hfsp_point fdLocation;
- __be16 fdFldr;
-} __packed;
-
-struct FXInfo {
- __be16 fdIconID;
- u8 fdUnused[8];
- __be16 fdComment;
- __be32 fdPutAway;
-} __packed;
-
-/* HFS+ file data (part of a cat_entry) */
-struct hfsplus_cat_file {
- __be16 type;
- __be16 flags;
- u32 reserved1;
- hfsplus_cnid id;
- __be32 create_date;
- __be32 content_mod_date;
- __be32 attribute_mod_date;
- __be32 access_date;
- __be32 backup_date;
- struct hfsplus_perm permissions;
- struct_group_attr(info, __packed,
- struct FInfo user_info;
- struct FXInfo finder_info;
- );
- __be32 text_encoding;
- u32 reserved2;
-
- struct hfsplus_fork_raw data_fork;
- struct hfsplus_fork_raw rsrc_fork;
-} __packed;
-
-/* File and folder flag bits */
-#define HFSPLUS_FILE_LOCKED 0x0001
-#define HFSPLUS_FILE_THREAD_EXISTS 0x0002
-#define HFSPLUS_XATTR_EXISTS 0x0004
-#define HFSPLUS_ACL_EXISTS 0x0008
-#define HFSPLUS_HAS_FOLDER_COUNT 0x0010 /* Folder has subfolder count
- * (HFSX only) */
-
-/* HFS+ catalog thread (part of a cat_entry) */
-struct hfsplus_cat_thread {
- __be16 type;
- s16 reserved;
- hfsplus_cnid parentID;
- struct hfsplus_unistr nodeName;
-} __packed;
-
-#define HFSPLUS_MIN_THREAD_SZ 10
-
-/* A data record in the catalog tree */
-typedef union {
- __be16 type;
- struct hfsplus_cat_folder folder;
- struct hfsplus_cat_file file;
- struct hfsplus_cat_thread thread;
-} __packed hfsplus_cat_entry;
-
-/* HFS+ catalog entry type */
-#define HFSPLUS_FOLDER 0x0001
-#define HFSPLUS_FILE 0x0002
-#define HFSPLUS_FOLDER_THREAD 0x0003
-#define HFSPLUS_FILE_THREAD 0x0004
-
-/* HFS+ extents tree key */
-struct hfsplus_ext_key {
- __be16 key_len;
- u8 fork_type;
- u8 pad;
- hfsplus_cnid cnid;
- __be32 start_block;
-} __packed;
-
-#define HFSPLUS_EXT_KEYLEN sizeof(struct hfsplus_ext_key)
-
-#define HFSPLUS_XATTR_FINDER_INFO_NAME "com.apple.FinderInfo"
-#define HFSPLUS_XATTR_ACL_NAME "com.apple.system.Security"
-
-#define HFSPLUS_ATTR_INLINE_DATA 0x10
-#define HFSPLUS_ATTR_FORK_DATA 0x20
-#define HFSPLUS_ATTR_EXTENTS 0x30
-
-/* HFS+ attributes tree key */
-struct hfsplus_attr_key {
- __be16 key_len;
- __be16 pad;
- hfsplus_cnid cnid;
- __be32 start_block;
- struct hfsplus_attr_unistr key_name;
-} __packed;
-
-#define HFSPLUS_ATTR_KEYLEN sizeof(struct hfsplus_attr_key)
-
-/* HFS+ fork data attribute */
-struct hfsplus_attr_fork_data {
- __be32 record_type;
- __be32 reserved;
- struct hfsplus_fork_raw the_fork;
-} __packed;
-
-/* HFS+ extension attribute */
-struct hfsplus_attr_extents {
- __be32 record_type;
- __be32 reserved;
- struct hfsplus_extent extents;
-} __packed;
-
-#define HFSPLUS_MAX_INLINE_DATA_SIZE 3802
-
-/* HFS+ attribute inline data */
-struct hfsplus_attr_inline_data {
- __be32 record_type;
- __be32 reserved1;
- u8 reserved2[6];
- __be16 length;
- u8 raw_bytes[HFSPLUS_MAX_INLINE_DATA_SIZE];
-} __packed;
-
-/* A data record in the attributes tree */
-typedef union {
- __be32 record_type;
- struct hfsplus_attr_fork_data fork_data;
- struct hfsplus_attr_extents extents;
- struct hfsplus_attr_inline_data inline_data;
-} __packed hfsplus_attr_entry;
-
-/* HFS+ generic BTree key */
-typedef union {
- __be16 key_len;
- struct hfsplus_cat_key cat;
- struct hfsplus_ext_key ext;
- struct hfsplus_attr_key attr;
-} __packed hfsplus_btree_key;
+#include <linux/hfs_common.h>
#endif
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index b51a411ecd23..7ae6745ca7ae 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -180,13 +180,29 @@ const struct dentry_operations hfsplus_dentry_operations = {
.d_compare = hfsplus_compare_dentry,
};
-static void hfsplus_get_perms(struct inode *inode,
- struct hfsplus_perm *perms, int dir)
+static int hfsplus_get_perms(struct inode *inode,
+ struct hfsplus_perm *perms, int dir)
{
struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
u16 mode;
mode = be16_to_cpu(perms->mode);
+ if (dir) {
+ if (mode && !S_ISDIR(mode))
+ goto bad_type;
+ } else if (mode) {
+ switch (mode & S_IFMT) {
+ case S_IFREG:
+ case S_IFLNK:
+ case S_IFCHR:
+ case S_IFBLK:
+ case S_IFIFO:
+ case S_IFSOCK:
+ break;
+ default:
+ goto bad_type;
+ }
+ }
i_uid_write(inode, be32_to_cpu(perms->owner));
if ((test_bit(HFSPLUS_SB_UID, &sbi->flags)) || (!i_uid_read(inode) && !mode))
@@ -212,6 +228,10 @@ static void hfsplus_get_perms(struct inode *inode,
inode->i_flags |= S_APPEND;
else
inode->i_flags &= ~S_APPEND;
+ return 0;
+bad_type:
+ pr_err("invalid file type 0%04o for inode %lu\n", mode, inode->i_ino);
+ return -EIO;
}
static int hfsplus_file_open(struct inode *inode, struct file *file)
@@ -305,6 +325,7 @@ int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end,
struct inode *inode = file->f_mapping->host;
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
+ struct hfsplus_vh *vhdr = sbi->s_vhdr;
int error = 0, error2;
error = file_write_and_wait_range(file, start, end);
@@ -348,6 +369,14 @@ int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end,
error = error2;
}
+ mutex_lock(&sbi->vh_mutex);
+ hfsplus_prepare_volume_header_for_commit(vhdr);
+ mutex_unlock(&sbi->vh_mutex);
+
+ error2 = hfsplus_commit_superblock(inode->i_sb);
+ if (!error)
+ error = error2;
+
if (!test_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags))
blkdev_issue_flush(inode->i_sb->s_bdev);
@@ -516,7 +545,9 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
}
hfs_bnode_read(fd->bnode, &entry, fd->entryoffset,
sizeof(struct hfsplus_cat_folder));
- hfsplus_get_perms(inode, &folder->permissions, 1);
+ res = hfsplus_get_perms(inode, &folder->permissions, 1);
+ if (res)
+ goto out;
set_nlink(inode, 1);
inode->i_size = 2 + be32_to_cpu(folder->valence);
inode_set_atime_to_ts(inode, hfsp_mt2ut(folder->access_date));
@@ -545,7 +576,9 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
hfsplus_inode_read_fork(inode, HFSPLUS_IS_RSRC(inode) ?
&file->rsrc_fork : &file->data_fork);
- hfsplus_get_perms(inode, &file->permissions, 0);
+ res = hfsplus_get_perms(inode, &file->permissions, 0);
+ if (res)
+ goto out;
set_nlink(inode, 1);
if (S_ISREG(inode->i_mode)) {
if (file->permissions.dev)
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index 54e85e25a259..aaffa9e060a0 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -187,40 +187,15 @@ static void hfsplus_evict_inode(struct inode *inode)
}
}
-static int hfsplus_sync_fs(struct super_block *sb, int wait)
+int hfsplus_commit_superblock(struct super_block *sb)
{
struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
struct hfsplus_vh *vhdr = sbi->s_vhdr;
int write_backup = 0;
- int error, error2;
-
- if (!wait)
- return 0;
+ int error = 0, error2;
hfs_dbg("starting...\n");
- /*
- * Explicitly write out the special metadata inodes.
- *
- * While these special inodes are marked as hashed and written
- * out peridocically by the flusher threads we redirty them
- * during writeout of normal inodes, and thus the life lock
- * prevents us from getting the latest state to disk.
- */
- error = filemap_write_and_wait(sbi->cat_tree->inode->i_mapping);
- error2 = filemap_write_and_wait(sbi->ext_tree->inode->i_mapping);
- if (!error)
- error = error2;
- if (sbi->attr_tree) {
- error2 =
- filemap_write_and_wait(sbi->attr_tree->inode->i_mapping);
- if (!error)
- error = error2;
- }
- error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping);
- if (!error)
- error = error2;
-
mutex_lock(&sbi->vh_mutex);
mutex_lock(&sbi->alloc_mutex);
vhdr->free_blocks = cpu_to_be32(sbi->free_blocks);
@@ -249,11 +224,52 @@ static int hfsplus_sync_fs(struct super_block *sb, int wait)
sbi->part_start + sbi->sect_count - 2,
sbi->s_backup_vhdr_buf, NULL, REQ_OP_WRITE);
if (!error)
- error2 = error;
+ error = error2;
out:
mutex_unlock(&sbi->alloc_mutex);
mutex_unlock(&sbi->vh_mutex);
+ hfs_dbg("finished: err %d\n", error);
+
+ return error;
+}
+
+static int hfsplus_sync_fs(struct super_block *sb, int wait)
+{
+ struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
+ int error, error2;
+
+ if (!wait)
+ return 0;
+
+ hfs_dbg("starting...\n");
+
+ /*
+ * Explicitly write out the special metadata inodes.
+ *
+ * While these special inodes are marked as hashed and written
+ * out peridocically by the flusher threads we redirty them
+ * during writeout of normal inodes, and thus the life lock
+ * prevents us from getting the latest state to disk.
+ */
+ error = filemap_write_and_wait(sbi->cat_tree->inode->i_mapping);
+ error2 = filemap_write_and_wait(sbi->ext_tree->inode->i_mapping);
+ if (!error)
+ error = error2;
+ if (sbi->attr_tree) {
+ error2 =
+ filemap_write_and_wait(sbi->attr_tree->inode->i_mapping);
+ if (!error)
+ error = error2;
+ }
+ error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping);
+ if (!error)
+ error = error2;
+
+ error2 = hfsplus_commit_superblock(sb);
+ if (!error)
+ error = error2;
+
if (!test_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags))
blkdev_issue_flush(sb->s_bdev);
@@ -395,6 +411,15 @@ static const struct super_operations hfsplus_sops = {
.show_options = hfsplus_show_options,
};
+void hfsplus_prepare_volume_header_for_commit(struct hfsplus_vh *vhdr)
+{
+ vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION);
+ vhdr->modify_date = hfsp_now2mt();
+ be32_add_cpu(&vhdr->write_count, 1);
+ vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT);
+ vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT);
+}
+
static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)
{
struct hfsplus_vh *vhdr;
@@ -562,11 +587,7 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)
* H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused
* all three are registered with Apple for our use
*/
- vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION);
- vhdr->modify_date = hfsp_now2mt();
- be32_add_cpu(&vhdr->write_count, 1);
- vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT);
- vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT);
+ hfsplus_prepare_volume_header_for_commit(vhdr);
hfsplus_sync_fs(sb, 1);
if (!sbi->hidden_dir) {
diff --git a/fs/hfsplus/unicode.c b/fs/hfsplus/unicode.c
index 11e08a4a18b2..d3a142f4518b 100644
--- a/fs/hfsplus/unicode.c
+++ b/fs/hfsplus/unicode.c
@@ -11,6 +11,9 @@
#include <linux/types.h>
#include <linux/nls.h>
+
+#include <kunit/visibility.h>
+
#include "hfsplus_fs.h"
#include "hfsplus_raw.h"
@@ -72,6 +75,7 @@ int hfsplus_strcasecmp(const struct hfsplus_unistr *s1,
return 0;
}
}
+EXPORT_SYMBOL_IF_KUNIT(hfsplus_strcasecmp);
/* Compare names as a sequence of 16-bit unsigned integers */
int hfsplus_strcmp(const struct hfsplus_unistr *s1,
@@ -110,7 +114,7 @@ int hfsplus_strcmp(const struct hfsplus_unistr *s1,
return len1 < len2 ? -1 :
len1 > len2 ? 1 : 0;
}
-
+EXPORT_SYMBOL_IF_KUNIT(hfsplus_strcmp);
#define Hangul_SBase 0xac00
#define Hangul_LBase 0x1100
@@ -143,8 +147,9 @@ static u16 *hfsplus_compose_lookup(u16 *p, u16 cc)
return NULL;
}
-static int hfsplus_uni2asc(struct super_block *sb, const struct hfsplus_unistr *ustr,
- int max_len, char *astr, int *len_p)
+static int hfsplus_uni2asc(struct super_block *sb,
+ const struct hfsplus_unistr *ustr,
+ int max_len, char *astr, int *len_p)
{
const hfsplus_unichr *ip;
struct nls_table *nls = HFSPLUS_SB(sb)->nls;
@@ -285,6 +290,7 @@ inline int hfsplus_uni2asc_str(struct super_block *sb,
{
return hfsplus_uni2asc(sb, ustr, HFSPLUS_MAX_STRLEN, astr, len_p);
}
+EXPORT_SYMBOL_IF_KUNIT(hfsplus_uni2asc_str);
inline int hfsplus_uni2asc_xattr_str(struct super_block *sb,
const struct hfsplus_attr_unistr *ustr,
@@ -293,6 +299,7 @@ inline int hfsplus_uni2asc_xattr_str(struct super_block *sb,
return hfsplus_uni2asc(sb, (const struct hfsplus_unistr *)ustr,
HFSPLUS_ATTR_MAX_STRLEN, astr, len_p);
}
+EXPORT_SYMBOL_IF_KUNIT(hfsplus_uni2asc_xattr_str);
/*
* Convert one or more ASCII characters into a single unicode character.
@@ -420,6 +427,7 @@ int hfsplus_asc2uni(struct super_block *sb,
return -ENAMETOOLONG;
return 0;
}
+EXPORT_SYMBOL_IF_KUNIT(hfsplus_asc2uni);
/*
* Hash a string to an integer as appropriate for the HFS+ filesystem.
@@ -472,6 +480,7 @@ int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str)
return 0;
}
+EXPORT_SYMBOL_IF_KUNIT(hfsplus_hash_dentry);
/*
* Compare strings with HFS+ filename ordering.
@@ -563,3 +572,4 @@ int hfsplus_compare_dentry(const struct dentry *dentry,
return 1;
return 0;
}
+EXPORT_SYMBOL_IF_KUNIT(hfsplus_compare_dentry);
diff --git a/fs/hfsplus/unicode_test.c b/fs/hfsplus/unicode_test.c
new file mode 100644
index 000000000000..5a7a6859efe3
--- /dev/null
+++ b/fs/hfsplus/unicode_test.c
@@ -0,0 +1,1579 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit tests for HFS+ Unicode string operations
+ *
+ * Copyright (C) 2025 Viacheslav Dubeyko <slava@dubeyko.com>
+ */
+
+#include <kunit/test.h>
+#include <linux/nls.h>
+#include <linux/dcache.h>
+#include <linux/stringhash.h>
+#include "hfsplus_fs.h"
+
+struct test_mock_string_env {
+ struct hfsplus_unistr str1;
+ struct hfsplus_unistr str2;
+ char *buf;
+ u32 buf_size;
+};
+
+static struct test_mock_string_env *setup_mock_str_env(u32 buf_size)
+{
+ struct test_mock_string_env *env;
+
+ env = kzalloc(sizeof(struct test_mock_string_env), GFP_KERNEL);
+ if (!env)
+ return NULL;
+
+ env->buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!env->buf) {
+ kfree(env);
+ return NULL;
+ }
+
+ env->buf_size = buf_size;
+
+ return env;
+}
+
+static void free_mock_str_env(struct test_mock_string_env *env)
+{
+ if (env->buf)
+ kfree(env->buf);
+ kfree(env);
+}
+
+/* Helper function to create hfsplus_unistr */
+static void create_unistr(struct hfsplus_unistr *ustr, const char *ascii_str)
+{
+ int len = strlen(ascii_str);
+ int i;
+
+ memset(ustr->unicode, 0, sizeof(ustr->unicode));
+
+ ustr->length = cpu_to_be16(len);
+ for (i = 0; i < len && i < HFSPLUS_MAX_STRLEN; i++)
+ ustr->unicode[i] = cpu_to_be16((u16)ascii_str[i]);
+}
+
+static void corrupt_unistr(struct hfsplus_unistr *ustr)
+{
+ ustr->length = cpu_to_be16(U16_MAX);
+}
+
+/* Test hfsplus_strcasecmp function */
+static void hfsplus_strcasecmp_test(struct kunit *test)
+{
+ struct test_mock_string_env *mock_env;
+
+ mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+ KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+ /* Test identical strings */
+ create_unistr(&mock_env->str1, "hello");
+ create_unistr(&mock_env->str2, "hello");
+ KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2));
+
+ /* Test case insensitive comparison */
+ create_unistr(&mock_env->str1, "Hello");
+ create_unistr(&mock_env->str2, "hello");
+ KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2));
+
+ create_unistr(&mock_env->str1, "HELLO");
+ create_unistr(&mock_env->str2, "hello");
+ KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2));
+
+ /* Test different strings */
+ create_unistr(&mock_env->str1, "apple");
+ create_unistr(&mock_env->str2, "banana");
+ KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ create_unistr(&mock_env->str1, "zebra");
+ create_unistr(&mock_env->str2, "apple");
+ KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ /* Test different lengths */
+ create_unistr(&mock_env->str1, "test");
+ create_unistr(&mock_env->str2, "testing");
+ KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ create_unistr(&mock_env->str1, "testing");
+ create_unistr(&mock_env->str2, "test");
+ KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ /* Test empty strings */
+ create_unistr(&mock_env->str1, "");
+ create_unistr(&mock_env->str2, "");
+ KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2));
+
+ create_unistr(&mock_env->str1, "");
+ create_unistr(&mock_env->str2, "test");
+ KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ /* Test single characters */
+ create_unistr(&mock_env->str1, "A");
+ create_unistr(&mock_env->str2, "a");
+ KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2));
+
+ create_unistr(&mock_env->str1, "A");
+ create_unistr(&mock_env->str2, "B");
+ KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ /* Test maximum length strings */
+ memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN);
+ mock_env->buf[HFSPLUS_MAX_STRLEN] = '\0';
+ create_unistr(&mock_env->str1, mock_env->buf);
+ create_unistr(&mock_env->str2, mock_env->buf);
+ KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2));
+
+ /* Change one character in the middle */
+ mock_env->buf[HFSPLUS_MAX_STRLEN / 2] = 'b';
+ create_unistr(&mock_env->str2, mock_env->buf);
+ KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ /* Test corrupted strings */
+ create_unistr(&mock_env->str1, "");
+ corrupt_unistr(&mock_env->str1);
+ create_unistr(&mock_env->str2, "");
+ KUNIT_EXPECT_NE(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2));
+
+ create_unistr(&mock_env->str1, "");
+ create_unistr(&mock_env->str2, "");
+ corrupt_unistr(&mock_env->str2);
+ KUNIT_EXPECT_NE(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2));
+
+ create_unistr(&mock_env->str1, "test");
+ corrupt_unistr(&mock_env->str1);
+ create_unistr(&mock_env->str2, "testing");
+ KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ create_unistr(&mock_env->str1, "test");
+ create_unistr(&mock_env->str2, "testing");
+ corrupt_unistr(&mock_env->str2);
+ KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ create_unistr(&mock_env->str1, "testing");
+ corrupt_unistr(&mock_env->str1);
+ create_unistr(&mock_env->str2, "test");
+ KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ create_unistr(&mock_env->str1, "testing");
+ create_unistr(&mock_env->str2, "test");
+ corrupt_unistr(&mock_env->str2);
+ KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ free_mock_str_env(mock_env);
+}
+
+/* Test hfsplus_strcmp function (case-sensitive) */
+static void hfsplus_strcmp_test(struct kunit *test)
+{
+ struct test_mock_string_env *mock_env;
+
+ mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+ KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+ /* Test identical strings */
+ create_unistr(&mock_env->str1, "hello");
+ create_unistr(&mock_env->str2, "hello");
+ KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2));
+
+ /* Test case sensitive comparison - should NOT be equal */
+ create_unistr(&mock_env->str1, "Hello");
+ create_unistr(&mock_env->str2, "hello");
+ KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2));
+ /* 'H' < 'h' in Unicode */
+ KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ /* Test lexicographic ordering */
+ create_unistr(&mock_env->str1, "apple");
+ create_unistr(&mock_env->str2, "banana");
+ KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ create_unistr(&mock_env->str1, "zebra");
+ create_unistr(&mock_env->str2, "apple");
+ KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ /* Test different lengths with common prefix */
+ create_unistr(&mock_env->str1, "test");
+ create_unistr(&mock_env->str2, "testing");
+ KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ create_unistr(&mock_env->str1, "testing");
+ create_unistr(&mock_env->str2, "test");
+ KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ /* Test empty strings */
+ create_unistr(&mock_env->str1, "");
+ create_unistr(&mock_env->str2, "");
+ KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2));
+
+ /* Test maximum length strings */
+ memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN);
+ mock_env->buf[HFSPLUS_MAX_STRLEN] = '\0';
+ create_unistr(&mock_env->str1, mock_env->buf);
+ create_unistr(&mock_env->str2, mock_env->buf);
+ KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2));
+
+ /* Change one character in the middle */
+ mock_env->buf[HFSPLUS_MAX_STRLEN / 2] = 'b';
+ create_unistr(&mock_env->str2, mock_env->buf);
+ KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ /* Test corrupted strings */
+ create_unistr(&mock_env->str1, "");
+ corrupt_unistr(&mock_env->str1);
+ create_unistr(&mock_env->str2, "");
+ KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2));
+
+ create_unistr(&mock_env->str1, "");
+ create_unistr(&mock_env->str2, "");
+ corrupt_unistr(&mock_env->str2);
+ KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2));
+
+ create_unistr(&mock_env->str1, "test");
+ corrupt_unistr(&mock_env->str1);
+ create_unistr(&mock_env->str2, "testing");
+ KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ create_unistr(&mock_env->str1, "test");
+ create_unistr(&mock_env->str2, "testing");
+ corrupt_unistr(&mock_env->str2);
+ KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ create_unistr(&mock_env->str1, "testing");
+ corrupt_unistr(&mock_env->str1);
+ create_unistr(&mock_env->str2, "test");
+ KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ create_unistr(&mock_env->str1, "testing");
+ create_unistr(&mock_env->str2, "test");
+ corrupt_unistr(&mock_env->str2);
+ KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ free_mock_str_env(mock_env);
+}
+
+/* Test Unicode edge cases */
+static void hfsplus_unicode_edge_cases_test(struct kunit *test)
+{
+ struct test_mock_string_env *mock_env;
+
+ mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+ KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+ /* Test with special characters */
+ mock_env->str1.length = cpu_to_be16(3);
+ mock_env->str1.unicode[0] = cpu_to_be16(0x00E9); /* é */
+ mock_env->str1.unicode[1] = cpu_to_be16(0x00F1); /* ñ */
+ mock_env->str1.unicode[2] = cpu_to_be16(0x00FC); /* ü */
+
+ mock_env->str2.length = cpu_to_be16(3);
+ mock_env->str2.unicode[0] = cpu_to_be16(0x00E9); /* é */
+ mock_env->str2.unicode[1] = cpu_to_be16(0x00F1); /* ñ */
+ mock_env->str2.unicode[2] = cpu_to_be16(0x00FC); /* ü */
+
+ KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2));
+ KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2));
+
+ /* Test with different special characters */
+ mock_env->str2.unicode[1] = cpu_to_be16(0x00F2); /* ò */
+ KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2));
+
+ /* Test null characters within string (should be handled correctly) */
+ mock_env->str1.length = cpu_to_be16(3);
+ mock_env->str1.unicode[0] = cpu_to_be16('a');
+ mock_env->str1.unicode[1] = cpu_to_be16(0x0000); /* null */
+ mock_env->str1.unicode[2] = cpu_to_be16('b');
+
+ mock_env->str2.length = cpu_to_be16(3);
+ mock_env->str2.unicode[0] = cpu_to_be16('a');
+ mock_env->str2.unicode[1] = cpu_to_be16(0x0000); /* null */
+ mock_env->str2.unicode[2] = cpu_to_be16('b');
+
+ KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2));
+
+ free_mock_str_env(mock_env);
+}
+
+/* Test boundary conditions */
+static void hfsplus_unicode_boundary_test(struct kunit *test)
+{
+ struct test_mock_string_env *mock_env;
+ int i;
+
+ mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+ KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+ /* Test maximum length boundary */
+ mock_env->str1.length = cpu_to_be16(HFSPLUS_MAX_STRLEN);
+ mock_env->str2.length = cpu_to_be16(HFSPLUS_MAX_STRLEN);
+
+ for (i = 0; i < HFSPLUS_MAX_STRLEN; i++) {
+ mock_env->str1.unicode[i] = cpu_to_be16('A');
+ mock_env->str2.unicode[i] = cpu_to_be16('A');
+ }
+
+ KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2));
+
+ /* Change last character */
+ mock_env->str2.unicode[HFSPLUS_MAX_STRLEN - 1] = cpu_to_be16('B');
+ KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ /* Test zero length strings */
+ mock_env->str1.length = cpu_to_be16(0);
+ mock_env->str2.length = cpu_to_be16(0);
+ KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2));
+ KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2));
+
+ /* Test one character vs empty */
+ mock_env->str1.length = cpu_to_be16(1);
+ mock_env->str1.unicode[0] = cpu_to_be16('A');
+ mock_env->str2.length = cpu_to_be16(0);
+ KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,
+ &mock_env->str2), 0);
+ KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,
+ &mock_env->str2), 0);
+
+ free_mock_str_env(mock_env);
+}
+
+/* Mock superblock and NLS table for testing hfsplus_uni2asc */
+struct test_mock_sb {
+ struct nls_table nls;
+ struct hfsplus_sb_info sb_info;
+ struct super_block sb;
+};
+
+static struct test_mock_sb *setup_mock_sb(void)
+{
+ struct test_mock_sb *ptr;
+
+ ptr = kzalloc(sizeof(struct test_mock_sb), GFP_KERNEL);
+ if (!ptr)
+ return NULL;
+
+ ptr->nls.charset = "utf8";
+ ptr->nls.uni2char = NULL; /* Will use default behavior */
+ ptr->sb_info.nls = &ptr->nls;
+ ptr->sb.s_fs_info = &ptr->sb_info;
+
+ /* Set default flags - no decomposition, no case folding */
+ clear_bit(HFSPLUS_SB_NODECOMPOSE, &ptr->sb_info.flags);
+ clear_bit(HFSPLUS_SB_CASEFOLD, &ptr->sb_info.flags);
+
+ return ptr;
+}
+
+static void free_mock_sb(struct test_mock_sb *ptr)
+{
+ kfree(ptr);
+}
+
+/* Simple uni2char implementation for testing */
+static int test_uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ if (uni < 0x80) {
+ *out = (unsigned char)uni;
+ return 1;
+ }
+
+ /* For non-ASCII, just use '?' as fallback */
+ *out = '?';
+ return 1;
+}
+
+/* Test hfsplus_uni2asc basic functionality */
+static void hfsplus_uni2asc_basic_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct test_mock_string_env *mock_env;
+ int len, result;
+
+ mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+ KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ mock_sb->nls.uni2char = test_uni2char;
+
+ /* Test simple ASCII string conversion */
+ create_unistr(&mock_env->str1, "hello");
+ len = mock_env->buf_size;
+ result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+ mock_env->buf, &len);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, 5, len);
+ KUNIT_EXPECT_STREQ(test, "hello", mock_env->buf);
+
+ /* Test empty string */
+ create_unistr(&mock_env->str1, "");
+ len = mock_env->buf_size;
+ result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+ mock_env->buf, &len);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, 0, len);
+
+ /* Test single character */
+ create_unistr(&mock_env->str1, "A");
+ len = mock_env->buf_size;
+ result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+ mock_env->buf, &len);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, 1, len);
+ KUNIT_EXPECT_EQ(test, 'A', mock_env->buf[0]);
+
+ free_mock_str_env(mock_env);
+ free_mock_sb(mock_sb);
+}
+
+/* Test special character handling */
+static void hfsplus_uni2asc_special_chars_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct test_mock_string_env *mock_env;
+ int len, result;
+
+ mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+ KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ mock_sb->nls.uni2char = test_uni2char;
+
+ /* Test null character conversion (should become 0x2400) */
+ mock_env->str1.length = cpu_to_be16(1);
+ mock_env->str1.unicode[0] = cpu_to_be16(0x0000);
+ len = mock_env->buf_size;
+ result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+ mock_env->buf, &len);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, 1, len);
+ /* Our test implementation returns '?' for non-ASCII */
+ KUNIT_EXPECT_EQ(test, '?', mock_env->buf[0]);
+
+ /* Test forward slash conversion (should become colon) */
+ mock_env->str1.length = cpu_to_be16(1);
+ mock_env->str1.unicode[0] = cpu_to_be16('/');
+ len = mock_env->buf_size;
+ result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+ mock_env->buf, &len);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, 1, len);
+ KUNIT_EXPECT_EQ(test, ':', mock_env->buf[0]);
+
+ /* Test string with mixed special characters */
+ mock_env->str1.length = cpu_to_be16(3);
+ mock_env->str1.unicode[0] = cpu_to_be16('a');
+ mock_env->str1.unicode[1] = cpu_to_be16('/');
+ mock_env->str1.unicode[2] = cpu_to_be16('b');
+ len = mock_env->buf_size;
+ result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+ mock_env->buf, &len);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, 3, len);
+ KUNIT_EXPECT_EQ(test, 'a', mock_env->buf[0]);
+ KUNIT_EXPECT_EQ(test, ':', mock_env->buf[1]);
+ KUNIT_EXPECT_EQ(test, 'b', mock_env->buf[2]);
+
+ free_mock_str_env(mock_env);
+ free_mock_sb(mock_sb);
+}
+
+/* Test buffer length handling */
+static void hfsplus_uni2asc_buffer_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct test_mock_string_env *mock_env;
+ int len, result;
+
+ mock_env = setup_mock_str_env(10);
+ KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ mock_sb->nls.uni2char = test_uni2char;
+
+ /* Test insufficient buffer space */
+ create_unistr(&mock_env->str1, "toolongstring");
+ len = 5; /* Buffer too small */
+ result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+ mock_env->buf, &len);
+
+ KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
+ KUNIT_EXPECT_EQ(test, 5, len); /* Should be set to consumed length */
+
+ /* Test exact buffer size */
+ create_unistr(&mock_env->str1, "exact");
+ len = 5;
+ result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+ mock_env->buf, &len);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, 5, len);
+
+ /* Test zero length buffer */
+ create_unistr(&mock_env->str1, "test");
+ len = 0;
+ result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+ mock_env->buf, &len);
+
+ KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
+ KUNIT_EXPECT_EQ(test, 0, len);
+
+ free_mock_str_env(mock_env);
+ free_mock_sb(mock_sb);
+}
+
+/* Test corrupted unicode string handling */
+static void hfsplus_uni2asc_corrupted_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct test_mock_string_env *mock_env;
+ int len, result;
+
+ mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+ KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ mock_sb->nls.uni2char = test_uni2char;
+
+ /* Test corrupted length (too large) */
+ create_unistr(&mock_env->str1, "test");
+ corrupt_unistr(&mock_env->str1); /* Sets length to U16_MAX */
+ len = mock_env->buf_size;
+
+ result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+ mock_env->buf, &len);
+
+ /* Should still work but with corrected length */
+ KUNIT_EXPECT_EQ(test, 0, result);
+ /*
+ * Length should be corrected to HFSPLUS_MAX_STRLEN
+ * and processed accordingly
+ */
+ KUNIT_EXPECT_GT(test, len, 0);
+
+ free_mock_str_env(mock_env);
+ free_mock_sb(mock_sb);
+}
+
+/* Test edge cases and boundary conditions */
+static void hfsplus_uni2asc_edge_cases_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct test_mock_string_env *mock_env;
+ int len, result;
+ int i;
+
+ mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN * 2);
+ KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ mock_sb->nls.uni2char = test_uni2char;
+
+ /* Test maximum length string */
+ mock_env->str1.length = cpu_to_be16(HFSPLUS_MAX_STRLEN);
+ for (i = 0; i < HFSPLUS_MAX_STRLEN; i++)
+ mock_env->str1.unicode[i] = cpu_to_be16('a');
+
+ len = mock_env->buf_size;
+ result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+ mock_env->buf, &len);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN, len);
+
+ /* Verify all characters are 'a' */
+ for (i = 0; i < HFSPLUS_MAX_STRLEN; i++)
+ KUNIT_EXPECT_EQ(test, 'a', mock_env->buf[i]);
+
+ /* Test string with high Unicode values (non-ASCII) */
+ mock_env->str1.length = cpu_to_be16(3);
+ mock_env->str1.unicode[0] = cpu_to_be16(0x00E9); /* é */
+ mock_env->str1.unicode[1] = cpu_to_be16(0x00F1); /* ñ */
+ mock_env->str1.unicode[2] = cpu_to_be16(0x00FC); /* ü */
+ len = mock_env->buf_size;
+ result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,
+ mock_env->buf, &len);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, 3, len);
+ /* Our test implementation converts non-ASCII to '?' */
+ KUNIT_EXPECT_EQ(test, '?', mock_env->buf[0]);
+ KUNIT_EXPECT_EQ(test, '?', mock_env->buf[1]);
+ KUNIT_EXPECT_EQ(test, '?', mock_env->buf[2]);
+
+ free_mock_str_env(mock_env);
+ free_mock_sb(mock_sb);
+}
+
+/* Simple char2uni implementation for testing */
+static int test_char2uni(const unsigned char *rawstring,
+ int boundlen, wchar_t *uni)
+{
+ if (boundlen <= 0)
+ return -EINVAL;
+
+ *uni = (wchar_t)*rawstring;
+ return 1;
+}
+
+/* Helper function to check unicode string contents */
+static void check_unistr_content(struct kunit *test,
+ struct hfsplus_unistr *ustr,
+ const char *expected_ascii)
+{
+ int expected_len = strlen(expected_ascii);
+ int actual_len = be16_to_cpu(ustr->length);
+ int i;
+
+ KUNIT_EXPECT_EQ(test, expected_len, actual_len);
+
+ for (i = 0; i < expected_len && i < actual_len; i++) {
+ u16 expected_char = (u16)expected_ascii[i];
+ u16 actual_char = be16_to_cpu(ustr->unicode[i]);
+
+ KUNIT_EXPECT_EQ(test, expected_char, actual_char);
+ }
+}
+
+/* Test hfsplus_asc2uni basic functionality */
+static void hfsplus_asc2uni_basic_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct test_mock_string_env *mock_env;
+ int result;
+
+ mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+ KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ mock_sb->nls.char2uni = test_char2uni;
+
+ /* Test simple ASCII string conversion */
+ result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
+ HFSPLUS_MAX_STRLEN, "hello", 5);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ check_unistr_content(test, &mock_env->str1, "hello");
+
+ /* Test empty string */
+ result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
+ HFSPLUS_MAX_STRLEN, "", 0);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(mock_env->str1.length));
+
+ /* Test single character */
+ result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
+ HFSPLUS_MAX_STRLEN, "A", 1);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ check_unistr_content(test, &mock_env->str1, "A");
+
+ /* Test null-terminated string with explicit length */
+ result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
+ HFSPLUS_MAX_STRLEN, "test\0extra", 4);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ check_unistr_content(test, &mock_env->str1, "test");
+
+ free_mock_str_env(mock_env);
+ free_mock_sb(mock_sb);
+}
+
+/* Test special character handling in asc2uni */
+static void hfsplus_asc2uni_special_chars_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct test_mock_string_env *mock_env;
+ int result;
+
+ mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+ KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ mock_sb->nls.char2uni = test_char2uni;
+
+ /* Test colon conversion (should become forward slash) */
+ result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
+ HFSPLUS_MAX_STRLEN, ":", 1);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, 1, be16_to_cpu(mock_env->str1.length));
+ KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[0]));
+
+ /* Test string with mixed special characters */
+ result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
+ HFSPLUS_MAX_STRLEN, "a:b", 3);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(mock_env->str1.length));
+ KUNIT_EXPECT_EQ(test, 'a', be16_to_cpu(mock_env->str1.unicode[0]));
+ KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[1]));
+ KUNIT_EXPECT_EQ(test, 'b', be16_to_cpu(mock_env->str1.unicode[2]));
+
+ /* Test multiple special characters */
+ result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
+ HFSPLUS_MAX_STRLEN, ":::", 3);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(mock_env->str1.length));
+ KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[0]));
+ KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[1]));
+ KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[2]));
+
+ free_mock_str_env(mock_env);
+ free_mock_sb(mock_sb);
+}
+
+/* Test buffer length limits */
+static void hfsplus_asc2uni_buffer_limits_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct test_mock_string_env *mock_env;
+ int result;
+
+ mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 10);
+ KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ mock_sb->nls.char2uni = test_char2uni;
+
+ /* Test exact maximum length */
+ memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN);
+ result = hfsplus_asc2uni(&mock_sb->sb,
+ &mock_env->str1, HFSPLUS_MAX_STRLEN,
+ mock_env->buf, HFSPLUS_MAX_STRLEN);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN,
+ be16_to_cpu(mock_env->str1.length));
+
+ /* Test exceeding maximum length */
+ memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN + 5);
+ result = hfsplus_asc2uni(&mock_sb->sb,
+ &mock_env->str1, HFSPLUS_MAX_STRLEN,
+ mock_env->buf, HFSPLUS_MAX_STRLEN + 5);
+
+ KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
+ KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN,
+ be16_to_cpu(mock_env->str1.length));
+
+ /* Test with smaller max_unistr_len */
+ result = hfsplus_asc2uni(&mock_sb->sb,
+ &mock_env->str1, 5, "toolongstring", 13);
+
+ KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
+ KUNIT_EXPECT_EQ(test, 5, be16_to_cpu(mock_env->str1.length));
+
+ /* Test zero max length */
+ result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 0, "test", 4);
+
+ KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
+ KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(mock_env->str1.length));
+
+ free_mock_str_env(mock_env);
+ free_mock_sb(mock_sb);
+}
+
+/* Test error handling and edge cases */
+static void hfsplus_asc2uni_edge_cases_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct hfsplus_unistr ustr;
+ char test_str[] = {'a', '\0', 'b'};
+ int result;
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ mock_sb->nls.char2uni = test_char2uni;
+
+ /* Test zero length input */
+ result = hfsplus_asc2uni(&mock_sb->sb,
+ &ustr, HFSPLUS_MAX_STRLEN, "test", 0);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(ustr.length));
+
+ /* Test input with length mismatch */
+ result = hfsplus_asc2uni(&mock_sb->sb,
+ &ustr, HFSPLUS_MAX_STRLEN, "hello", 3);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ check_unistr_content(test, &ustr, "hel");
+
+ /* Test with various printable ASCII characters */
+ result = hfsplus_asc2uni(&mock_sb->sb,
+ &ustr, HFSPLUS_MAX_STRLEN, "ABC123!@#", 9);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ check_unistr_content(test, &ustr, "ABC123!@#");
+
+ /* Test null character in the middle */
+ result = hfsplus_asc2uni(&mock_sb->sb,
+ &ustr, HFSPLUS_MAX_STRLEN, test_str, 3);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(ustr.length));
+ KUNIT_EXPECT_EQ(test, 'a', be16_to_cpu(ustr.unicode[0]));
+ KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(ustr.unicode[1]));
+ KUNIT_EXPECT_EQ(test, 'b', be16_to_cpu(ustr.unicode[2]));
+
+ free_mock_sb(mock_sb);
+}
+
+/* Test decomposition flag behavior */
+static void hfsplus_asc2uni_decompose_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct test_mock_string_env *mock_env;
+ int result;
+
+ mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+ KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ mock_sb->nls.char2uni = test_char2uni;
+
+ /* Test with decomposition disabled (default) */
+ clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
+ result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
+ HFSPLUS_MAX_STRLEN, "test", 4);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ check_unistr_content(test, &mock_env->str1, "test");
+
+ /* Test with decomposition enabled */
+ set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
+ result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str2,
+ HFSPLUS_MAX_STRLEN, "test", 4);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ check_unistr_content(test, &mock_env->str2, "test");
+
+ /* For simple ASCII, both should produce the same result */
+ KUNIT_EXPECT_EQ(test,
+ be16_to_cpu(mock_env->str1.length),
+ be16_to_cpu(mock_env->str2.length));
+
+ free_mock_str_env(mock_env);
+ free_mock_sb(mock_sb);
+}
+
+/* Mock dentry for testing hfsplus_hash_dentry */
+static struct dentry test_dentry;
+
+static void setup_mock_dentry(struct super_block *sb)
+{
+ memset(&test_dentry, 0, sizeof(test_dentry));
+ test_dentry.d_sb = sb;
+}
+
+/* Helper function to create qstr */
+static void create_qstr(struct qstr *str, const char *name)
+{
+ str->name = name;
+ str->len = strlen(name);
+ str->hash = 0; /* Will be set by hash function */
+}
+
+/* Test hfsplus_hash_dentry basic functionality */
+static void hfsplus_hash_dentry_basic_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct qstr str1, str2;
+ int result;
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ setup_mock_dentry(&mock_sb->sb);
+ mock_sb->nls.char2uni = test_char2uni;
+
+ /* Test basic string hashing */
+ create_qstr(&str1, "hello");
+ result = hfsplus_hash_dentry(&test_dentry, &str1);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_NE(test, 0, str1.hash);
+
+ /* Test that identical strings produce identical hashes */
+ create_qstr(&str2, "hello");
+ result = hfsplus_hash_dentry(&test_dentry, &str2);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, str1.hash, str2.hash);
+
+ /* Test empty string */
+ create_qstr(&str1, "");
+ result = hfsplus_hash_dentry(&test_dentry, &str1);
+
+ /* Empty string should still produce a hash */
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /* Test single character */
+ create_qstr(&str1, "A");
+ result = hfsplus_hash_dentry(&test_dentry, &str1);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_NE(test, 0, str1.hash);
+
+ free_mock_sb(mock_sb);
+}
+
+/* Test case folding behavior in hash */
+static void hfsplus_hash_dentry_casefold_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct qstr str1, str2;
+ int result;
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ setup_mock_dentry(&mock_sb->sb);
+ mock_sb->nls.char2uni = test_char2uni;
+
+ /* Test with case folding disabled (default) */
+ clear_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
+
+ create_qstr(&str1, "Hello");
+ result = hfsplus_hash_dentry(&test_dentry, &str1);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ create_qstr(&str2, "hello");
+ result = hfsplus_hash_dentry(&test_dentry, &str2);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /*
+ * Without case folding, different cases
+ * should produce different hashes
+ */
+ KUNIT_EXPECT_NE(test, str1.hash, str2.hash);
+
+ /* Test with case folding enabled */
+ set_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
+
+ create_qstr(&str1, "Hello");
+ result = hfsplus_hash_dentry(&test_dentry, &str1);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ create_qstr(&str2, "hello");
+ result = hfsplus_hash_dentry(&test_dentry, &str2);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /* With case folding, different cases should produce same hash */
+ KUNIT_EXPECT_EQ(test, str1.hash, str2.hash);
+
+ /* Test mixed case */
+ create_qstr(&str1, "HeLLo");
+ result = hfsplus_hash_dentry(&test_dentry, &str1);
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_EQ(test, str1.hash, str2.hash);
+
+ free_mock_sb(mock_sb);
+}
+
+/* Test special character handling in hash */
+static void hfsplus_hash_dentry_special_chars_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct qstr str1, str2;
+ int result;
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ setup_mock_dentry(&mock_sb->sb);
+ mock_sb->nls.char2uni = test_char2uni;
+
+ /* Test colon conversion (: becomes /) */
+ create_qstr(&str1, "file:name");
+ result = hfsplus_hash_dentry(&test_dentry, &str1);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ create_qstr(&str2, "file/name");
+ result = hfsplus_hash_dentry(&test_dentry, &str2);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /* After conversion, these should produce the same hash */
+ KUNIT_EXPECT_EQ(test, str1.hash, str2.hash);
+
+ /* Test multiple special characters */
+ create_qstr(&str1, ":::");
+ result = hfsplus_hash_dentry(&test_dentry, &str1);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ create_qstr(&str2, "///");
+ result = hfsplus_hash_dentry(&test_dentry, &str2);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ KUNIT_EXPECT_EQ(test, str1.hash, str2.hash);
+
+ free_mock_sb(mock_sb);
+}
+
+/* Test decomposition flag behavior in hash */
+static void hfsplus_hash_dentry_decompose_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct qstr str1, str2;
+ int result;
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ setup_mock_dentry(&mock_sb->sb);
+ mock_sb->nls.char2uni = test_char2uni;
+
+ /* Test with decomposition disabled (default) */
+ clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
+
+ create_qstr(&str1, "test");
+ result = hfsplus_hash_dentry(&test_dentry, &str1);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /* Test with decomposition enabled */
+ set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
+
+ create_qstr(&str2, "test");
+ result = hfsplus_hash_dentry(&test_dentry, &str2);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /*
+ * For simple ASCII, decomposition shouldn't change
+ * the hash much but the function should still work correctly
+ */
+ KUNIT_EXPECT_NE(test, 0, str2.hash);
+
+ free_mock_sb(mock_sb);
+}
+
+/* Test hash consistency and distribution */
+static void hfsplus_hash_dentry_consistency_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct qstr str1, str2, str3;
+ unsigned long hash1;
+ int result;
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ setup_mock_dentry(&mock_sb->sb);
+ mock_sb->nls.char2uni = test_char2uni;
+
+ /* Test that same string always produces same hash */
+ create_qstr(&str1, "consistent");
+ result = hfsplus_hash_dentry(&test_dentry, &str1);
+ KUNIT_EXPECT_EQ(test, 0, result);
+ hash1 = str1.hash;
+
+ create_qstr(&str2, "consistent");
+ result = hfsplus_hash_dentry(&test_dentry, &str2);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ KUNIT_EXPECT_EQ(test, hash1, str2.hash);
+
+ /* Test that different strings produce different hashes */
+ create_qstr(&str3, "different");
+ result = hfsplus_hash_dentry(&test_dentry, &str3);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ KUNIT_EXPECT_NE(test, str1.hash, str3.hash);
+
+ /* Test similar strings should have different hashes */
+ create_qstr(&str1, "file1");
+ result = hfsplus_hash_dentry(&test_dentry, &str1);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ create_qstr(&str2, "file2");
+ result = hfsplus_hash_dentry(&test_dentry, &str2);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ KUNIT_EXPECT_NE(test, str1.hash, str2.hash);
+
+ free_mock_sb(mock_sb);
+}
+
+/* Test edge cases and boundary conditions */
+static void hfsplus_hash_dentry_edge_cases_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct test_mock_string_env *mock_env;
+ struct qstr str;
+ int result;
+
+ mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);
+ KUNIT_ASSERT_NOT_NULL(test, mock_env);
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ setup_mock_dentry(&mock_sb->sb);
+ mock_sb->nls.char2uni = test_char2uni;
+
+ /* Test very long filename */
+ memset(mock_env->buf, 'a', mock_env->buf_size - 1);
+ mock_env->buf[mock_env->buf_size - 1] = '\0';
+
+ create_qstr(&str, mock_env->buf);
+ result = hfsplus_hash_dentry(&test_dentry, &str);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_NE(test, 0, str.hash);
+
+ /* Test filename with all printable ASCII characters */
+ create_qstr(&str, "!@#$%^&*()_+-=[]{}|;':\",./<>?");
+ result = hfsplus_hash_dentry(&test_dentry, &str);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_NE(test, 0, str.hash);
+
+ /* Test with embedded null (though not typical for filenames) */
+ str.name = "file\0hidden";
+ str.len = 11; /* Include the null and text after it */
+ str.hash = 0;
+ result = hfsplus_hash_dentry(&test_dentry, &str);
+
+ KUNIT_EXPECT_EQ(test, 0, result);
+ KUNIT_EXPECT_NE(test, 0, str.hash);
+
+ free_mock_str_env(mock_env);
+ free_mock_sb(mock_sb);
+}
+
+/* Test hfsplus_compare_dentry basic functionality */
+static void hfsplus_compare_dentry_basic_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct qstr name;
+ int result;
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ setup_mock_dentry(&mock_sb->sb);
+ mock_sb->nls.char2uni = test_char2uni;
+
+ /* Test identical strings */
+ create_qstr(&name, "hello");
+ result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /* Test different strings - lexicographic order */
+ create_qstr(&name, "world");
+ result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name);
+ KUNIT_EXPECT_LT(test, result, 0); /* "hello" < "world" */
+
+ result = hfsplus_compare_dentry(&test_dentry, 5, "world", &name);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ create_qstr(&name, "hello");
+ result = hfsplus_compare_dentry(&test_dentry, 5, "world", &name);
+ KUNIT_EXPECT_GT(test, result, 0); /* "world" > "hello" */
+
+ /* Test empty strings */
+ create_qstr(&name, "");
+ result = hfsplus_compare_dentry(&test_dentry, 0, "", &name);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /* Test one empty, one non-empty */
+ create_qstr(&name, "test");
+ result = hfsplus_compare_dentry(&test_dentry, 0, "", &name);
+ KUNIT_EXPECT_LT(test, result, 0); /* "" < "test" */
+
+ create_qstr(&name, "");
+ result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
+ KUNIT_EXPECT_GT(test, result, 0); /* "test" > "" */
+
+ free_mock_sb(mock_sb);
+}
+
+/* Test case folding behavior in comparison */
+static void hfsplus_compare_dentry_casefold_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct qstr name;
+ int result;
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ setup_mock_dentry(&mock_sb->sb);
+ mock_sb->nls.char2uni = test_char2uni;
+
+ /* Test with case folding disabled (default) */
+ clear_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
+
+ create_qstr(&name, "hello");
+ result = hfsplus_compare_dentry(&test_dentry, 5, "Hello", &name);
+ /* Case sensitive: "Hello" != "hello" */
+ KUNIT_EXPECT_NE(test, 0, result);
+
+ create_qstr(&name, "Hello");
+ result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name);
+ /* Case sensitive: "hello" != "Hello" */
+ KUNIT_EXPECT_NE(test, 0, result);
+
+ /* Test with case folding enabled */
+ set_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
+
+ create_qstr(&name, "hello");
+ result = hfsplus_compare_dentry(&test_dentry, 5, "Hello", &name);
+ /* Case insensitive: "Hello" == "hello" */
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ create_qstr(&name, "Hello");
+ result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name);
+ /* Case insensitive: "hello" == "Hello" */
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /* Test mixed case */
+ create_qstr(&name, "TeSt");
+ result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ create_qstr(&name, "test");
+ result = hfsplus_compare_dentry(&test_dentry, 4, "TEST", &name);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ free_mock_sb(mock_sb);
+}
+
+/* Test special character handling in comparison */
+static void hfsplus_compare_dentry_special_chars_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct qstr name;
+ int result;
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ setup_mock_dentry(&mock_sb->sb);
+ mock_sb->nls.char2uni = test_char2uni;
+
+ /* Test colon conversion (: becomes /) */
+ create_qstr(&name, "file/name");
+ result = hfsplus_compare_dentry(&test_dentry, 9, "file:name", &name);
+ /* "file:name" == "file/name" after conversion */
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ create_qstr(&name, "file:name");
+ result = hfsplus_compare_dentry(&test_dentry, 9, "file/name", &name);
+ /* "file/name" == "file:name" after conversion */
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /* Test multiple special characters */
+ create_qstr(&name, "///");
+ result = hfsplus_compare_dentry(&test_dentry, 3, ":::", &name);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /* Test mixed special and regular characters */
+ create_qstr(&name, "a/b:c");
+ result = hfsplus_compare_dentry(&test_dentry, 5, "a:b/c", &name);
+ /* Both become "a/b/c" after conversion */
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ free_mock_sb(mock_sb);
+}
+
+/* Test length differences */
+static void hfsplus_compare_dentry_length_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct qstr name;
+ int result;
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ setup_mock_dentry(&mock_sb->sb);
+ mock_sb->nls.char2uni = test_char2uni;
+
+ /* Test different lengths with common prefix */
+ create_qstr(&name, "testing");
+ result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
+ KUNIT_EXPECT_LT(test, result, 0); /* "test" < "testing" */
+
+ create_qstr(&name, "test");
+ result = hfsplus_compare_dentry(&test_dentry, 7, "testing", &name);
+ KUNIT_EXPECT_GT(test, result, 0); /* "testing" > "test" */
+
+ /* Test exact length match */
+ create_qstr(&name, "exact");
+ result = hfsplus_compare_dentry(&test_dentry, 5, "exact", &name);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /* Test length parameter vs actual string content */
+ create_qstr(&name, "hello");
+ result = hfsplus_compare_dentry(&test_dentry, 3, "hel", &name);
+ KUNIT_EXPECT_LT(test, result, 0); /* "hel" < "hello" */
+
+ /* Test longer first string but shorter length parameter */
+ create_qstr(&name, "hi");
+ result = hfsplus_compare_dentry(&test_dentry, 2, "hello", &name);
+ /* "he" < "hi" (only first 2 chars compared) */
+ KUNIT_EXPECT_LT(test, result, 0);
+
+ free_mock_sb(mock_sb);
+}
+
+/* Test decomposition flag behavior */
+static void hfsplus_compare_dentry_decompose_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct qstr name;
+ int result;
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ setup_mock_dentry(&mock_sb->sb);
+ mock_sb->nls.char2uni = test_char2uni;
+
+ /* Test with decomposition disabled (default) */
+ clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
+
+ create_qstr(&name, "test");
+ result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /* Test with decomposition enabled */
+ set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
+
+ create_qstr(&name, "test");
+ result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /* For simple ASCII, decomposition shouldn't affect the result */
+ create_qstr(&name, "different");
+ result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name);
+ KUNIT_EXPECT_NE(test, 0, result);
+
+ free_mock_sb(mock_sb);
+}
+
+/* Test edge cases and boundary conditions */
+static void hfsplus_compare_dentry_edge_cases_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct qstr name;
+ char *long_str;
+ char *long_str2;
+ u32 str_size = HFSPLUS_MAX_STRLEN + 1;
+ struct qstr null_name = {
+ .name = "a\0b",
+ .len = 3,
+ .hash = 0
+ };
+ int result;
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ setup_mock_dentry(&mock_sb->sb);
+ mock_sb->nls.char2uni = test_char2uni;
+
+ long_str = kzalloc(str_size, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, long_str);
+
+ long_str2 = kzalloc(str_size, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, long_str2);
+
+ /* Test very long strings */
+ memset(long_str, 'a', str_size - 1);
+ long_str[str_size - 1] = '\0';
+
+ create_qstr(&name, long_str);
+ result = hfsplus_compare_dentry(&test_dentry, str_size - 1,
+ long_str, &name);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /* Test with difference at the end of long strings */
+ memset(long_str2, 'a', str_size - 1);
+ long_str2[str_size - 1] = '\0';
+ long_str2[str_size - 2] = 'b';
+ create_qstr(&name, long_str2);
+ result = hfsplus_compare_dentry(&test_dentry, str_size - 1,
+ long_str, &name);
+ KUNIT_EXPECT_LT(test, result, 0); /* 'a' < 'b' */
+
+ /* Test single character differences */
+ create_qstr(&name, "b");
+ result = hfsplus_compare_dentry(&test_dentry, 1, "a", &name);
+ KUNIT_EXPECT_LT(test, result, 0); /* 'a' < 'b' */
+
+ create_qstr(&name, "a");
+ result = hfsplus_compare_dentry(&test_dentry, 1, "b", &name);
+ KUNIT_EXPECT_GT(test, result, 0); /* 'b' > 'a' */
+
+ /* Test with null characters in the middle */
+ result = hfsplus_compare_dentry(&test_dentry, 3, "a\0b", &null_name);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /* Test all printable ASCII characters */
+ create_qstr(&name, "!@#$%^&*()");
+ result = hfsplus_compare_dentry(&test_dentry, 10, "!@#$%^&*()", &name);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ kfree(long_str);
+ kfree(long_str2);
+ free_mock_sb(mock_sb);
+}
+
+/* Test combined flag behaviors */
+static void hfsplus_compare_dentry_combined_flags_test(struct kunit *test)
+{
+ struct test_mock_sb *mock_sb;
+ struct qstr name;
+ int result;
+
+ mock_sb = setup_mock_sb();
+ KUNIT_ASSERT_NOT_NULL(test, mock_sb);
+
+ setup_mock_dentry(&mock_sb->sb);
+ mock_sb->nls.char2uni = test_char2uni;
+
+ /* Test with both casefold and decompose enabled */
+ set_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
+ set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
+
+ create_qstr(&name, "hello");
+ result = hfsplus_compare_dentry(&test_dentry, 5, "HELLO", &name);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /* Test special chars with case folding */
+ create_qstr(&name, "File/Name");
+ result = hfsplus_compare_dentry(&test_dentry, 9, "file:name", &name);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ /* Test with both flags disabled */
+ clear_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags);
+ clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
+
+ create_qstr(&name, "hello");
+ result = hfsplus_compare_dentry(&test_dentry, 5, "HELLO", &name);
+ KUNIT_EXPECT_NE(test, 0, result); /* Case sensitive */
+
+ /* But special chars should still be converted */
+ create_qstr(&name, "file/name");
+ result = hfsplus_compare_dentry(&test_dentry, 9, "file:name", &name);
+ KUNIT_EXPECT_EQ(test, 0, result);
+
+ free_mock_sb(mock_sb);
+}
+
+static struct kunit_case hfsplus_unicode_test_cases[] = {
+ KUNIT_CASE(hfsplus_strcasecmp_test),
+ KUNIT_CASE(hfsplus_strcmp_test),
+ KUNIT_CASE(hfsplus_unicode_edge_cases_test),
+ KUNIT_CASE(hfsplus_unicode_boundary_test),
+ KUNIT_CASE(hfsplus_uni2asc_basic_test),
+ KUNIT_CASE(hfsplus_uni2asc_special_chars_test),
+ KUNIT_CASE(hfsplus_uni2asc_buffer_test),
+ KUNIT_CASE(hfsplus_uni2asc_corrupted_test),
+ KUNIT_CASE(hfsplus_uni2asc_edge_cases_test),
+ KUNIT_CASE(hfsplus_asc2uni_basic_test),
+ KUNIT_CASE(hfsplus_asc2uni_special_chars_test),
+ KUNIT_CASE(hfsplus_asc2uni_buffer_limits_test),
+ KUNIT_CASE(hfsplus_asc2uni_edge_cases_test),
+ KUNIT_CASE(hfsplus_asc2uni_decompose_test),
+ KUNIT_CASE(hfsplus_hash_dentry_basic_test),
+ KUNIT_CASE(hfsplus_hash_dentry_casefold_test),
+ KUNIT_CASE(hfsplus_hash_dentry_special_chars_test),
+ KUNIT_CASE(hfsplus_hash_dentry_decompose_test),
+ KUNIT_CASE(hfsplus_hash_dentry_consistency_test),
+ KUNIT_CASE(hfsplus_hash_dentry_edge_cases_test),
+ KUNIT_CASE(hfsplus_compare_dentry_basic_test),
+ KUNIT_CASE(hfsplus_compare_dentry_casefold_test),
+ KUNIT_CASE(hfsplus_compare_dentry_special_chars_test),
+ KUNIT_CASE(hfsplus_compare_dentry_length_test),
+ KUNIT_CASE(hfsplus_compare_dentry_decompose_test),
+ KUNIT_CASE(hfsplus_compare_dentry_edge_cases_test),
+ KUNIT_CASE(hfsplus_compare_dentry_combined_flags_test),
+ {}
+};
+
+static struct kunit_suite hfsplus_unicode_test_suite = {
+ .name = "hfsplus_unicode",
+ .test_cases = hfsplus_unicode_test_cases,
+};
+
+kunit_test_suite(hfsplus_unicode_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests for HFS+ Unicode string operations");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
diff --git a/fs/hfsplus/xattr.c b/fs/hfsplus/xattr.c
index ece4d29c0ab9..da95a9de9a65 100644
--- a/fs/hfsplus/xattr.c
+++ b/fs/hfsplus/xattr.c
@@ -265,10 +265,8 @@ int __hfsplus_setxattr(struct inode *inode, const char *name,
struct hfs_find_data cat_fd;
hfsplus_cat_entry entry;
u16 cat_entry_flags, cat_entry_type;
- u16 folder_finderinfo_len = sizeof(struct DInfo) +
- sizeof(struct DXInfo);
- u16 file_finderinfo_len = sizeof(struct FInfo) +
- sizeof(struct FXInfo);
+ u16 folder_finderinfo_len = sizeof(DInfo) + sizeof(DXInfo);
+ u16 file_finderinfo_len = sizeof(FInfo) + sizeof(FXInfo);
if ((!S_ISREG(inode->i_mode) &&
!S_ISDIR(inode->i_mode)) ||
@@ -444,11 +442,11 @@ static ssize_t hfsplus_getxattr_finder_info(struct inode *inode,
ssize_t res = 0;
struct hfs_find_data fd;
u16 entry_type;
- u16 folder_rec_len = sizeof(struct DInfo) + sizeof(struct DXInfo);
- u16 file_rec_len = sizeof(struct FInfo) + sizeof(struct FXInfo);
+ u16 folder_rec_len = sizeof(DInfo) + sizeof(DXInfo);
+ u16 file_rec_len = sizeof(FInfo) + sizeof(FXInfo);
u16 record_len = max(folder_rec_len, file_rec_len);
- u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
- u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
+ u8 folder_finder_info[sizeof(DInfo) + sizeof(DXInfo)];
+ u8 file_finder_info[sizeof(FInfo) + sizeof(FXInfo)];
if (size >= record_len) {
res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
@@ -612,8 +610,8 @@ static ssize_t hfsplus_listxattr_finder_info(struct dentry *dentry,
struct inode *inode = d_inode(dentry);
struct hfs_find_data fd;
u16 entry_type;
- u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
- u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
+ u8 folder_finder_info[sizeof(DInfo) + sizeof(DXInfo)];
+ u8 file_finder_info[sizeof(FInfo) + sizeof(FXInfo)];
unsigned long len, found_bit;
int xattr_name_len, symbols_count;
@@ -629,14 +627,14 @@ static ssize_t hfsplus_listxattr_finder_info(struct dentry *dentry,
entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
if (entry_type == HFSPLUS_FOLDER) {
- len = sizeof(struct DInfo) + sizeof(struct DXInfo);
+ len = sizeof(DInfo) + sizeof(DXInfo);
hfs_bnode_read(fd.bnode, folder_finder_info,
fd.entryoffset +
offsetof(struct hfsplus_cat_folder, user_info),
len);
found_bit = find_first_bit((void *)folder_finder_info, len*8);
} else if (entry_type == HFSPLUS_FILE) {
- len = sizeof(struct FInfo) + sizeof(struct FXInfo);
+ len = sizeof(FInfo) + sizeof(FXInfo);
hfs_bnode_read(fd.bnode, file_finder_info,
fd.entryoffset +
offsetof(struct hfsplus_cat_file, user_info),
diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c
index 2d0719bf6d87..de89c5bef607 100644
--- a/fs/jbd2/checkpoint.c
+++ b/fs/jbd2/checkpoint.c
@@ -113,7 +113,7 @@ __releases(&journal->j_state_lock)
"journal space in %s\n", __func__,
journal->j_devname);
WARN_ON(1);
- jbd2_journal_abort(journal, -EIO);
+ jbd2_journal_abort(journal, -ENOSPC);
}
write_lock(&journal->j_state_lock);
} else {
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index d480b94117cd..c973162d5b31 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -937,8 +937,8 @@ int jbd2_journal_bmap(journal_t *journal, unsigned long blocknr,
printk(KERN_ALERT "%s: journal block not found "
"at offset %lu on %s\n",
__func__, blocknr, journal->j_devname);
+ jbd2_journal_abort(journal, ret ? ret : -EFSCORRUPTED);
err = -EIO;
- jbd2_journal_abort(journal, err);
} else {
*retp = block;
}
@@ -1521,7 +1521,6 @@ static journal_t *journal_init_common(struct block_device *bdev,
struct block_device *fs_dev,
unsigned long long start, int len, int blocksize)
{
- static struct lock_class_key jbd2_trans_commit_key;
journal_t *journal;
int err;
int n;
@@ -1530,6 +1529,7 @@ static journal_t *journal_init_common(struct block_device *bdev,
if (!journal)
return ERR_PTR(-ENOMEM);
+ lockdep_register_key(&journal->jbd2_trans_commit_key);
journal->j_blocksize = blocksize;
journal->j_dev = bdev;
journal->j_fs_dev = fs_dev;
@@ -1560,7 +1560,7 @@ static journal_t *journal_init_common(struct block_device *bdev,
journal->j_max_batch_time = 15000; /* 15ms */
atomic_set(&journal->j_reserved_credits, 0);
lockdep_init_map(&journal->j_trans_commit_map, "jbd2_handle",
- &jbd2_trans_commit_key, 0);
+ &journal->jbd2_trans_commit_key, 0);
/* The journal is marked for error until we succeed with recovery! */
journal->j_flags = JBD2_ABORT;
@@ -1611,6 +1611,7 @@ err_cleanup:
kfree(journal->j_wbuf);
jbd2_journal_destroy_revoke(journal);
journal_fail_superblock(journal);
+ lockdep_unregister_key(&journal->jbd2_trans_commit_key);
kfree(journal);
return ERR_PTR(err);
}
@@ -1858,8 +1859,9 @@ int jbd2_journal_update_sb_log_tail(journal_t *journal, tid_t tail_tid,
if (is_journal_aborted(journal))
return -EIO;
- if (jbd2_check_fs_dev_write_error(journal)) {
- jbd2_journal_abort(journal, -EIO);
+ ret = jbd2_check_fs_dev_write_error(journal);
+ if (ret) {
+ jbd2_journal_abort(journal, ret);
return -EIO;
}
@@ -2156,9 +2158,11 @@ int jbd2_journal_destroy(journal_t *journal)
* failed to write back to the original location, otherwise the
* filesystem may become inconsistent.
*/
- if (!is_journal_aborted(journal) &&
- jbd2_check_fs_dev_write_error(journal))
- jbd2_journal_abort(journal, -EIO);
+ if (!is_journal_aborted(journal)) {
+ int ret = jbd2_check_fs_dev_write_error(journal);
+ if (ret)
+ jbd2_journal_abort(journal, ret);
+ }
if (journal->j_sb_buffer) {
if (!is_journal_aborted(journal)) {
@@ -2187,6 +2191,7 @@ int jbd2_journal_destroy(journal_t *journal)
jbd2_journal_destroy_revoke(journal);
kfree(journal->j_fc_wbuf);
kfree(journal->j_wbuf);
+ lockdep_unregister_key(&journal->jbd2_trans_commit_key);
kfree(journal);
return err;
@@ -2349,6 +2354,12 @@ int jbd2_journal_set_features(journal_t *journal, unsigned long compat,
sb->s_feature_compat |= cpu_to_be32(compat);
sb->s_feature_ro_compat |= cpu_to_be32(ro);
sb->s_feature_incompat |= cpu_to_be32(incompat);
+ /*
+ * Update the checksum now so that it is valid even for read-only
+ * filesystems where jbd2_write_superblock() doesn't get called.
+ */
+ if (jbd2_journal_has_csum_v2or3(journal))
+ sb->s_checksum = jbd2_superblock_csum(sb);
unlock_buffer(journal->j_sb_buffer);
jbd2_journal_init_transaction_limits(journal);
@@ -2378,9 +2389,17 @@ void jbd2_journal_clear_features(journal_t *journal, unsigned long compat,
sb = journal->j_superblock;
+ lock_buffer(journal->j_sb_buffer);
sb->s_feature_compat &= ~cpu_to_be32(compat);
sb->s_feature_ro_compat &= ~cpu_to_be32(ro);
sb->s_feature_incompat &= ~cpu_to_be32(incompat);
+ /*
+ * Update the checksum now so that it is valid even for read-only
+ * filesystems where jbd2_write_superblock() doesn't get called.
+ */
+ if (jbd2_journal_has_csum_v2or3(journal))
+ sb->s_checksum = jbd2_superblock_csum(sb);
+ unlock_buffer(journal->j_sb_buffer);
jbd2_journal_init_transaction_limits(journal);
}
EXPORT_SYMBOL(jbd2_journal_clear_features);
diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c
index 3e510564de6e..dca4b5d8aaaa 100644
--- a/fs/jbd2/transaction.c
+++ b/fs/jbd2/transaction.c
@@ -441,7 +441,7 @@ repeat:
read_unlock(&journal->j_state_lock);
current->journal_info = handle;
- rwsem_acquire_read(&journal->j_trans_commit_map, 0, 0, _THIS_IP_);
+ rwsem_acquire_read(&journal->j_trans_commit_map, 0, 1, _THIS_IP_);
jbd2_journal_free_transaction(new_transaction);
/*
* Ensure that no allocations done while the transaction is open are
@@ -1219,7 +1219,8 @@ int jbd2_journal_get_write_access(handle_t *handle, struct buffer_head *bh)
return -EROFS;
journal = handle->h_transaction->t_journal;
- if (jbd2_check_fs_dev_write_error(journal)) {
+ rc = jbd2_check_fs_dev_write_error(journal);
+ if (rc) {
/*
* If the fs dev has writeback errors, it may have failed
* to async write out metadata buffers in the background.
@@ -1227,7 +1228,7 @@ int jbd2_journal_get_write_access(handle_t *handle, struct buffer_head *bh)
* it out again, which may lead to on-disk filesystem
* inconsistency. Aborting journal can avoid it happen.
*/
- jbd2_journal_abort(journal, -EIO);
+ jbd2_journal_abort(journal, rc);
return -EIO;
}
@@ -1284,14 +1285,23 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh)
* committing transaction's lists, but it HAS to be in Forget state in
* that case: the transaction must have deleted the buffer for it to be
* reused here.
+ * In the case of file system data inconsistency, for example, if the
+ * block bitmap of a referenced block is not set, it can lead to the
+ * situation where a block being committed is allocated and used again.
+ * As a result, the following condition will not be satisfied, so here
+ * we directly trigger a JBD abort instead of immediately invoking
+ * bugon.
*/
spin_lock(&jh->b_state_lock);
- J_ASSERT_JH(jh, (jh->b_transaction == transaction ||
- jh->b_transaction == NULL ||
- (jh->b_transaction == journal->j_committing_transaction &&
- jh->b_jlist == BJ_Forget)));
+ if (!(jh->b_transaction == transaction || jh->b_transaction == NULL ||
+ (jh->b_transaction == journal->j_committing_transaction &&
+ jh->b_jlist == BJ_Forget)) || jh->b_next_transaction != NULL) {
+ err = -EROFS;
+ spin_unlock(&jh->b_state_lock);
+ jbd2_journal_abort(journal, err);
+ goto out;
+ }
- J_ASSERT_JH(jh, jh->b_next_transaction == NULL);
J_ASSERT_JH(jh, buffer_locked(jh2bh(jh)));
if (jh->b_transaction == NULL) {
diff --git a/fs/lockd/netlink.c b/fs/lockd/netlink.c
index 6e00b02cad90..880c42b4f8c3 100644
--- a/fs/lockd/netlink.c
+++ b/fs/lockd/netlink.c
@@ -2,6 +2,7 @@
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/lockd.yaml */
/* YNL-GEN kernel source */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
#include <net/netlink.h>
#include <net/genetlink.h>
diff --git a/fs/lockd/netlink.h b/fs/lockd/netlink.h
index 1920543a7955..d8408f077dd8 100644
--- a/fs/lockd/netlink.h
+++ b/fs/lockd/netlink.h
@@ -2,6 +2,7 @@
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/lockd.yaml */
/* YNL-GEN kernel header */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
#ifndef _LINUX_LOCKD_GEN_H
#define _LINUX_LOCKD_GEN_H
diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c
index ca54aa583530..ac51a44e1065 100644
--- a/fs/nfsd/netlink.c
+++ b/fs/nfsd/netlink.c
@@ -2,6 +2,7 @@
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/nfsd.yaml */
/* YNL-GEN kernel source */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
#include <net/netlink.h>
#include <net/genetlink.h>
diff --git a/fs/nfsd/netlink.h b/fs/nfsd/netlink.h
index 8eb903f24c41..478117ff6b8c 100644
--- a/fs/nfsd/netlink.h
+++ b/fs/nfsd/netlink.h
@@ -2,6 +2,7 @@
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/nfsd.yaml */
/* YNL-GEN kernel header */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
#ifndef _LINUX_NFSD_GEN_H
#define _LINUX_NFSD_GEN_H
diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c
index eced9013a881..980ae9157248 100644
--- a/fs/ntfs3/attrib.c
+++ b/fs/ntfs3/attrib.c
@@ -1457,7 +1457,6 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr,
pgoff_t index = vbo[i] >> PAGE_SHIFT;
if (index != folio->index) {
- struct page *page = &folio->page;
u64 from = vbo[i] & ~(u64)(PAGE_SIZE - 1);
u64 to = min(from + PAGE_SIZE, wof_size);
@@ -1467,8 +1466,7 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr,
if (err)
goto out1;
- err = ntfs_bio_pages(sbi, run, &page, 1, from,
- to - from, REQ_OP_READ);
+ err = ntfs_read_run(sbi, run, addr, from, to - from);
if (err) {
folio->index = -1;
goto out1;
@@ -1862,7 +1860,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
struct ATTRIB *attr = NULL, *attr_b;
struct ATTR_LIST_ENTRY *le, *le_b;
struct mft_inode *mi, *mi_b;
- CLST svcn, evcn1, len, dealloc, alen;
+ CLST svcn, evcn1, len, dealloc, alen, done;
CLST vcn, end;
u64 valid_size, data_size, alloc_size, total_size;
u32 mask;
@@ -1925,6 +1923,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
len = bytes >> sbi->cluster_bits;
end = vcn + len;
dealloc = 0;
+ done = 0;
svcn = le64_to_cpu(attr_b->nres.svcn);
evcn1 = le64_to_cpu(attr_b->nres.evcn) + 1;
@@ -1933,23 +1932,28 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
attr = attr_b;
le = le_b;
mi = mi_b;
- } else if (!le_b) {
+ goto check_seg;
+ }
+
+ if (!le_b) {
err = -EINVAL;
goto out;
- } else {
- le = le_b;
- attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, NULL, 0, &vcn,
- &mi);
- if (!attr) {
- err = -EINVAL;
- goto out;
- }
+ }
- svcn = le64_to_cpu(attr->nres.svcn);
- evcn1 = le64_to_cpu(attr->nres.evcn) + 1;
+ le = le_b;
+ attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, NULL, 0, &vcn, &mi);
+ if (!attr) {
+ err = -EINVAL;
+ goto out;
}
for (;;) {
+ CLST vcn1, eat, next_svcn;
+
+ svcn = le64_to_cpu(attr->nres.svcn);
+ evcn1 = le64_to_cpu(attr->nres.evcn) + 1;
+
+check_seg:
if (svcn >= end) {
/* Shift VCN- */
attr->nres.svcn = cpu_to_le64(svcn - len);
@@ -1959,22 +1963,25 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
ni->attr_list.dirty = true;
}
mi->dirty = true;
- } else if (svcn < vcn || end < evcn1) {
- CLST vcn1, eat, next_svcn;
+ goto next_attr;
+ }
- /* Collapse a part of this attribute segment. */
- err = attr_load_runs(attr, ni, run, &svcn);
- if (err)
- goto out;
- vcn1 = max(vcn, svcn);
- eat = min(end, evcn1) - vcn1;
+ run_truncate(run, 0);
+ err = attr_load_runs(attr, ni, run, &svcn);
+ if (err)
+ goto out;
- err = run_deallocate_ex(sbi, run, vcn1, eat, &dealloc,
- true);
- if (err)
- goto out;
+ vcn1 = vcn + done; /* original vcn in attr/run. */
+ eat = min(end, evcn1) - vcn1;
+
+ err = run_deallocate_ex(sbi, run, vcn1, eat, &dealloc, true);
+ if (err)
+ goto out;
- if (!run_collapse_range(run, vcn1, eat)) {
+ if (svcn + eat < evcn1) {
+ /* Collapse a part of this attribute segment. */
+
+ if (!run_collapse_range(run, vcn1, eat, done)) {
err = -ENOMEM;
goto out;
}
@@ -1982,7 +1989,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
if (svcn >= vcn) {
/* Shift VCN */
attr->nres.svcn = cpu_to_le64(vcn);
- if (le) {
+ if (le && attr->nres.svcn != le->vcn) {
le->vcn = attr->nres.svcn;
ni->attr_list.dirty = true;
}
@@ -1993,7 +2000,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
goto out;
next_svcn = le64_to_cpu(attr->nres.evcn) + 1;
- if (next_svcn + eat < evcn1) {
+ if (next_svcn + eat + done < evcn1) {
err = ni_insert_nonresident(
ni, ATTR_DATA, NULL, 0, run, next_svcn,
evcn1 - eat - next_svcn, a_flags, &attr,
@@ -2007,18 +2014,9 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
/* Free all allocated memory. */
run_truncate(run, 0);
+ done += eat;
} else {
u16 le_sz;
- u16 roff = le16_to_cpu(attr->nres.run_off);
-
- if (roff > le32_to_cpu(attr->size)) {
- err = -EINVAL;
- goto out;
- }
-
- run_unpack_ex(RUN_DEALLOCATE, sbi, ni->mi.rno, svcn,
- evcn1 - 1, svcn, Add2Ptr(attr, roff),
- le32_to_cpu(attr->size) - roff);
/* Delete this attribute segment. */
mi_remove_attr(NULL, mi, attr);
@@ -2031,6 +2029,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
goto out;
}
+ done += evcn1 - svcn;
if (evcn1 >= alen)
break;
@@ -2048,11 +2047,12 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
err = -EINVAL;
goto out;
}
- goto next_attr;
+ continue;
}
le = (struct ATTR_LIST_ENTRY *)((u8 *)le - le_sz);
}
+next_attr:
if (evcn1 >= alen)
break;
@@ -2061,10 +2061,6 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
err = -EINVAL;
goto out;
}
-
-next_attr:
- svcn = le64_to_cpu(attr->nres.svcn);
- evcn1 = le64_to_cpu(attr->nres.evcn) + 1;
}
if (!attr_b) {
@@ -2554,7 +2550,7 @@ undo_insert_range:
if (attr_load_runs(attr, ni, run, NULL))
goto bad_inode;
- if (!run_collapse_range(run, vcn, len))
+ if (!run_collapse_range(run, vcn, len, 0))
goto bad_inode;
if (mi_pack_runs(mi, attr, run, evcn1 + len - svcn))
diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c
index 1b5c865a0339..b98e95d6b4d9 100644
--- a/fs/ntfs3/dir.c
+++ b/fs/ntfs3/dir.c
@@ -332,8 +332,7 @@ static inline bool ntfs_dir_emit(struct ntfs_sb_info *sbi,
* It does additional locks/reads just to get the type of name.
* Should we use additional mount option to enable branch below?
*/
- if (fname->dup.extend_data &&
- ino != ni->mi.rno) {
+ if (fname->dup.extend_data && ino != ni->mi.rno) {
struct inode *inode = ntfs_iget5(sbi->sb, &e->ref, NULL);
if (!IS_ERR_OR_NULL(inode)) {
dt_type = fs_umode_to_dtype(inode->i_mode);
diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
index 4c90ec2fa2ea..5016bccc2ac5 100644
--- a/fs/ntfs3/file.c
+++ b/fs/ntfs3/file.c
@@ -19,6 +19,12 @@
#include "ntfs.h"
#include "ntfs_fs.h"
+/*
+ * cifx, btrfs, exfat, ext4, f2fs use this constant.
+ * Hope this value will become common to all fs.
+ */
+#define NTFS3_IOC_SHUTDOWN _IOR('X', 125, __u32)
+
static int ntfs_ioctl_fitrim(struct ntfs_sb_info *sbi, unsigned long arg)
{
struct fstrim_range __user *user_range;
@@ -59,7 +65,7 @@ static int ntfs_ioctl_get_volume_label(struct ntfs_sb_info *sbi, u8 __user *buf)
static int ntfs_ioctl_set_volume_label(struct ntfs_sb_info *sbi, u8 __user *buf)
{
- u8 user[FSLABEL_MAX] = {0};
+ u8 user[FSLABEL_MAX] = { 0 };
int len;
if (!capable(CAP_SYS_ADMIN))
@@ -74,12 +80,46 @@ static int ntfs_ioctl_set_volume_label(struct ntfs_sb_info *sbi, u8 __user *buf)
}
/*
+ * ntfs_force_shutdown - helper function. Called from ioctl
+ */
+static int ntfs_force_shutdown(struct super_block *sb, u32 flags)
+{
+ int err;
+ struct ntfs_sb_info *sbi = sb->s_fs_info;
+
+ if (unlikely(ntfs3_forced_shutdown(sb)))
+ return 0;
+
+ /* No additional options yet (flags). */
+ err = bdev_freeze(sb->s_bdev);
+ if (err)
+ return err;
+ set_bit(NTFS_FLAGS_SHUTDOWN_BIT, &sbi->flags);
+ bdev_thaw(sb->s_bdev);
+ return 0;
+}
+
+static int ntfs_ioctl_shutdown(struct super_block *sb, unsigned long arg)
+{
+ u32 flags;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (get_user(flags, (__u32 __user *)arg))
+ return -EFAULT;
+
+ return ntfs_force_shutdown(sb, flags);
+}
+
+/*
* ntfs_ioctl - file_operations::unlocked_ioctl
*/
long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg)
{
struct inode *inode = file_inode(filp);
- struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info;
+ struct super_block *sb = inode->i_sb;
+ struct ntfs_sb_info *sbi = sb->s_fs_info;
/* Avoid any operation if inode is bad. */
if (unlikely(is_bad_ni(ntfs_i(inode))))
@@ -92,6 +132,8 @@ long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg)
return ntfs_ioctl_get_volume_label(sbi, (u8 __user *)arg);
case FS_IOC_SETFSLABEL:
return ntfs_ioctl_set_volume_label(sbi, (u8 __user *)arg);
+ case NTFS3_IOC_SHUTDOWN:
+ return ntfs_ioctl_shutdown(sb, arg);
}
return -ENOTTY; /* Inappropriate ioctl for device. */
}
@@ -325,9 +367,14 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)
return -EOPNOTSUPP;
}
- if (is_compressed(ni) && rw) {
- ntfs_inode_warn(inode, "mmap(write) compressed not supported");
- return -EOPNOTSUPP;
+ if (is_compressed(ni)) {
+ if (rw) {
+ ntfs_inode_warn(inode,
+ "mmap(write) compressed not supported");
+ return -EOPNOTSUPP;
+ }
+ /* Turn off readahead for compressed files. */
+ file->f_ra.ra_pages = 0;
}
if (rw) {
@@ -503,8 +550,6 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size)
if (dirty)
mark_inode_dirty(inode);
- /*ntfs_flush_inodes(inode->i_sb, inode, NULL);*/
-
return 0;
}
@@ -886,9 +931,24 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
if (err)
return err;
- if (is_compressed(ni) && (iocb->ki_flags & IOCB_DIRECT)) {
- ntfs_inode_warn(inode, "direct i/o + compressed not supported");
- return -EOPNOTSUPP;
+ if (is_compressed(ni)) {
+ if (iocb->ki_flags & IOCB_DIRECT) {
+ ntfs_inode_warn(
+ inode, "direct i/o + compressed not supported");
+ return -EOPNOTSUPP;
+ }
+ /* Turn off readahead for compressed files. */
+ file->f_ra.ra_pages = 0;
+ }
+
+ /* Check minimum alignment for dio. */
+ if (iocb->ki_flags & IOCB_DIRECT) {
+ struct super_block *sb = inode->i_sb;
+ struct ntfs_sb_info *sbi = sb->s_fs_info;
+ if ((iocb->ki_pos | iov_iter_alignment(iter)) &
+ sbi->bdev_blocksize_mask) {
+ iocb->ki_flags &= ~IOCB_DIRECT;
+ }
}
return generic_file_read_iter(iocb, iter);
@@ -908,6 +968,11 @@ static ssize_t ntfs_file_splice_read(struct file *in, loff_t *ppos,
if (err)
return err;
+ if (is_compressed(ntfs_i(inode))) {
+ /* Turn off readahead for compressed files. */
+ in->f_ra.ra_pages = 0;
+ }
+
return filemap_splice_read(in, ppos, pipe, len, flags);
}
@@ -1026,7 +1091,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
if (!frame_uptodate && off) {
err = ni_read_frame(ni, frame_vbo, pages,
- pages_per_frame);
+ pages_per_frame, 0);
if (err) {
for (ip = 0; ip < pages_per_frame; ip++) {
folio = page_folio(pages[ip]);
@@ -1091,7 +1156,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
if (off || (to < i_size && (to & (frame_size - 1)))) {
err = ni_read_frame(ni, frame_vbo, pages,
- pages_per_frame);
+ pages_per_frame, 0);
if (err) {
for (ip = 0; ip < pages_per_frame;
ip++) {
@@ -1114,8 +1179,8 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
size_t cp, tail = PAGE_SIZE - off;
folio = page_folio(pages[ip]);
- cp = copy_folio_from_iter_atomic(folio, off,
- min(tail, bytes), from);
+ cp = copy_folio_from_iter_atomic(
+ folio, off, min(tail, bytes), from);
flush_dcache_folio(folio);
copied += cp;
@@ -1312,7 +1377,7 @@ static int ntfs_file_release(struct inode *inode, struct file *file)
if (sbi->options->prealloc &&
((file->f_mode & FMODE_WRITE) &&
atomic_read(&inode->i_writecount) == 1)
- /*
+ /*
* The only file when inode->i_fop = &ntfs_file_operations and
* init_rwsem(&ni->file.run_lock) is not called explicitly is MFT.
*
@@ -1375,6 +1440,18 @@ static ssize_t ntfs_file_splice_write(struct pipe_inode_info *pipe,
return iter_file_splice_write(pipe, file, ppos, len, flags);
}
+/*
+ * ntfs_file_fsync - file_operations::fsync
+ */
+static int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+ struct inode *inode = file_inode(file);
+ if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
+ return -EIO;
+
+ return generic_file_fsync(file, start, end, datasync);
+}
+
// clang-format off
const struct inode_operations ntfs_file_inode_operations = {
.getattr = ntfs_getattr,
@@ -1397,7 +1474,7 @@ const struct file_operations ntfs_file_operations = {
.splice_write = ntfs_file_splice_write,
.mmap_prepare = ntfs_file_mmap_prepare,
.open = ntfs_file_open,
- .fsync = generic_file_fsync,
+ .fsync = ntfs_file_fsync,
.fallocate = ntfs_fallocate,
.release = ntfs_file_release,
};
diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
index 8f9fe1d7a690..641ddaf8d4a0 100644
--- a/fs/ntfs3/frecord.c
+++ b/fs/ntfs3/frecord.c
@@ -325,8 +325,10 @@ bool ni_add_subrecord(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi)
mi_get_ref(&ni->mi, &m->mrec->parent_ref);
- ni_add_mi(ni, m);
- *mi = m;
+ *mi = ni_ins_mi(ni, &ni->mi_tree, m->rno, &m->node);
+ if (*mi != m)
+ mi_put(m);
+
return true;
}
@@ -767,7 +769,7 @@ int ni_create_attr_list(struct ntfs_inode *ni)
* Skip estimating exact memory requirement.
* Looks like one record_size is always enough.
*/
- le = kmalloc(al_aligned(rs), GFP_NOFS);
+ le = kzalloc(al_aligned(rs), GFP_NOFS);
if (!le)
return -ENOMEM;
@@ -1015,9 +1017,9 @@ insert_ext:
out2:
ni_remove_mi(ni, mi);
- mi_put(mi);
out1:
+ mi_put(mi);
ntfs_mark_rec_free(sbi, rno, is_mft);
out:
@@ -2020,6 +2022,29 @@ out:
return err;
}
+static struct page *ntfs_lock_new_page(struct address_space *mapping,
+ pgoff_t index, gfp_t gfp)
+{
+ struct folio *folio = __filemap_get_folio(mapping, index,
+ FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
+ struct page *page;
+
+ if (IS_ERR(folio))
+ return ERR_CAST(folio);
+
+ if (!folio_test_uptodate(folio))
+ return folio_file_page(folio, index);
+
+ /* Use a temporary page to avoid data corruption */
+ folio_unlock(folio);
+ folio_put(folio);
+ page = alloc_page(gfp);
+ if (!page)
+ return ERR_PTR(-ENOMEM);
+ __SetPageLocked(page);
+ return page;
+}
+
/*
* ni_readpage_cmpr
*
@@ -2074,15 +2099,15 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio)
if (i == idx)
continue;
- pg = find_or_create_page(mapping, index, gfp_mask);
- if (!pg) {
- err = -ENOMEM;
+ pg = ntfs_lock_new_page(mapping, index, gfp_mask);
+ if (IS_ERR(pg)) {
+ err = PTR_ERR(pg);
goto out1;
}
pages[i] = pg;
}
- err = ni_read_frame(ni, frame_vbo, pages, pages_per_frame);
+ err = ni_read_frame(ni, frame_vbo, pages, pages_per_frame, 0);
out1:
for (i = 0; i < pages_per_frame; i++) {
@@ -2152,17 +2177,9 @@ int ni_decompress_file(struct ntfs_inode *ni)
*/
index = 0;
for (vbo = 0; vbo < i_size; vbo += bytes) {
- u32 nr_pages;
bool new;
- if (vbo + frame_size > i_size) {
- bytes = i_size - vbo;
- nr_pages = (bytes + PAGE_SIZE - 1) >> PAGE_SHIFT;
- } else {
- nr_pages = pages_per_frame;
- bytes = frame_size;
- }
-
+ bytes = vbo + frame_size > i_size ? (i_size - vbo) : frame_size;
end = bytes_to_cluster(sbi, vbo + bytes);
for (vcn = vbo >> sbi->cluster_bits; vcn < end; vcn += clen) {
@@ -2175,27 +2192,19 @@ int ni_decompress_file(struct ntfs_inode *ni)
for (i = 0; i < pages_per_frame; i++, index++) {
struct page *pg;
- pg = find_or_create_page(mapping, index, gfp_mask);
- if (!pg) {
+ pg = ntfs_lock_new_page(mapping, index, gfp_mask);
+ if (IS_ERR(pg)) {
while (i--) {
unlock_page(pages[i]);
put_page(pages[i]);
}
- err = -ENOMEM;
+ err = PTR_ERR(pg);
goto out;
}
pages[i] = pg;
}
- err = ni_read_frame(ni, vbo, pages, pages_per_frame);
-
- if (!err) {
- down_read(&ni->file.run_lock);
- err = ntfs_bio_pages(sbi, &ni->file.run, pages,
- nr_pages, vbo, bytes,
- REQ_OP_WRITE);
- up_read(&ni->file.run_lock);
- }
+ err = ni_read_frame(ni, vbo, pages, pages_per_frame, 1);
for (i = 0; i < pages_per_frame; i++) {
unlock_page(pages[i]);
@@ -2385,20 +2394,19 @@ out2:
* Pages - Array of locked pages.
*/
int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
- u32 pages_per_frame)
+ u32 pages_per_frame, int copy)
{
int err;
struct ntfs_sb_info *sbi = ni->mi.sbi;
u8 cluster_bits = sbi->cluster_bits;
char *frame_ondisk = NULL;
char *frame_mem = NULL;
- struct page **pages_disk = NULL;
struct ATTR_LIST_ENTRY *le = NULL;
struct runs_tree *run = &ni->file.run;
u64 valid_size = ni->i_valid;
u64 vbo_disk;
size_t unc_size;
- u32 frame_size, i, npages_disk, ondisk_size;
+ u32 frame_size, i, ondisk_size;
struct page *pg;
struct ATTRIB *attr;
CLST frame, clst_data;
@@ -2407,9 +2415,6 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
* To simplify decompress algorithm do vmap for source
* and target pages.
*/
- for (i = 0; i < pages_per_frame; i++)
- kmap(pages[i]);
-
frame_size = pages_per_frame << PAGE_SHIFT;
frame_mem = vmap(pages, pages_per_frame, VM_MAP, PAGE_KERNEL);
if (!frame_mem) {
@@ -2493,7 +2498,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
err = attr_wof_frame_info(ni, attr, run, frame64, frames,
frame_bits, &ondisk_size, &vbo_data);
if (err)
- goto out2;
+ goto out1;
if (frame64 == frames) {
unc_size = 1 + ((i_size - 1) & (frame_size - 1));
@@ -2504,7 +2509,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
if (ondisk_size > frame_size) {
err = -EINVAL;
- goto out2;
+ goto out1;
}
if (!attr->non_res) {
@@ -2525,10 +2530,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
ARRAY_SIZE(WOF_NAME), run, vbo_disk,
vbo_data + ondisk_size);
if (err)
- goto out2;
- npages_disk = (ondisk_size + (vbo_disk & (PAGE_SIZE - 1)) +
- PAGE_SIZE - 1) >>
- PAGE_SHIFT;
+ goto out1;
#endif
} else if (is_attr_compressed(attr)) {
/* LZNT compression. */
@@ -2562,61 +2564,37 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
if (clst_data >= NTFS_LZNT_CLUSTERS) {
/* Frame is not compressed. */
down_read(&ni->file.run_lock);
- err = ntfs_bio_pages(sbi, run, pages, pages_per_frame,
- frame_vbo, ondisk_size,
- REQ_OP_READ);
+ err = ntfs_read_run(sbi, run, frame_mem, frame_vbo,
+ ondisk_size);
up_read(&ni->file.run_lock);
goto out1;
}
vbo_disk = frame_vbo;
- npages_disk = (ondisk_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
} else {
__builtin_unreachable();
err = -EINVAL;
goto out1;
}
- pages_disk = kcalloc(npages_disk, sizeof(*pages_disk), GFP_NOFS);
- if (!pages_disk) {
+ /* Allocate memory to read compressed data to. */
+ frame_ondisk = kvmalloc(ondisk_size, GFP_KERNEL);
+ if (!frame_ondisk) {
err = -ENOMEM;
- goto out2;
- }
-
- for (i = 0; i < npages_disk; i++) {
- pg = alloc_page(GFP_KERNEL);
- if (!pg) {
- err = -ENOMEM;
- goto out3;
- }
- pages_disk[i] = pg;
- lock_page(pg);
- kmap(pg);
+ goto out1;
}
/* Read 'ondisk_size' bytes from disk. */
down_read(&ni->file.run_lock);
- err = ntfs_bio_pages(sbi, run, pages_disk, npages_disk, vbo_disk,
- ondisk_size, REQ_OP_READ);
+ err = ntfs_read_run(sbi, run, frame_ondisk, vbo_disk, ondisk_size);
up_read(&ni->file.run_lock);
if (err)
- goto out3;
-
- /*
- * To simplify decompress algorithm do vmap for source and target pages.
- */
- frame_ondisk = vmap(pages_disk, npages_disk, VM_MAP, PAGE_KERNEL_RO);
- if (!frame_ondisk) {
- err = -ENOMEM;
- goto out3;
- }
+ goto out2;
- /* Decompress: Frame_ondisk -> frame_mem. */
#ifdef CONFIG_NTFS3_LZX_XPRESS
if (run != &ni->file.run) {
/* LZX or XPRESS */
- err = decompress_lzx_xpress(
- sbi, frame_ondisk + (vbo_disk & (PAGE_SIZE - 1)),
- ondisk_size, frame_mem, unc_size, frame_size);
+ err = decompress_lzx_xpress(sbi, frame_ondisk, ondisk_size,
+ frame_mem, unc_size, frame_size);
} else
#endif
{
@@ -2634,30 +2612,25 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
memset(frame_mem + ok, 0, frame_size - ok);
}
- vunmap(frame_ondisk);
-
-out3:
- for (i = 0; i < npages_disk; i++) {
- pg = pages_disk[i];
- if (pg) {
- kunmap(pg);
- unlock_page(pg);
- put_page(pg);
- }
- }
- kfree(pages_disk);
-
out2:
+ kvfree(frame_ondisk);
+out1:
#ifdef CONFIG_NTFS3_LZX_XPRESS
if (run != &ni->file.run)
run_free(run);
+ if (!err && copy) {
+ /* We are called from 'ni_decompress_file' */
+ /* Copy decompressed LZX or XPRESS data into new place. */
+ down_read(&ni->file.run_lock);
+ err = ntfs_write_run(sbi, &ni->file.run, frame_mem, frame_vbo,
+ frame_size);
+ up_read(&ni->file.run_lock);
+ }
#endif
-out1:
vunmap(frame_mem);
out:
for (i = 0; i < pages_per_frame; i++) {
pg = pages[i];
- kunmap(pg);
SetPageUptodate(pg);
}
@@ -2680,13 +2653,10 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages,
u64 frame_vbo = folio_pos(folio);
CLST frame = frame_vbo >> frame_bits;
char *frame_ondisk = NULL;
- struct page **pages_disk = NULL;
struct ATTR_LIST_ENTRY *le = NULL;
char *frame_mem;
struct ATTRIB *attr;
struct mft_inode *mi;
- u32 i;
- struct page *pg;
size_t compr_size, ondisk_size;
struct lznt *lznt;
@@ -2721,38 +2691,18 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages,
goto out;
}
- pages_disk = kcalloc(pages_per_frame, sizeof(struct page *), GFP_NOFS);
- if (!pages_disk) {
- err = -ENOMEM;
- goto out;
- }
-
- for (i = 0; i < pages_per_frame; i++) {
- pg = alloc_page(GFP_KERNEL);
- if (!pg) {
- err = -ENOMEM;
- goto out1;
- }
- pages_disk[i] = pg;
- lock_page(pg);
- kmap(pg);
- }
-
- /* To simplify compress algorithm do vmap for source and target pages. */
- frame_ondisk = vmap(pages_disk, pages_per_frame, VM_MAP, PAGE_KERNEL);
+ /* Allocate memory to write compressed data to. */
+ frame_ondisk = kvmalloc(frame_size, GFP_KERNEL);
if (!frame_ondisk) {
err = -ENOMEM;
- goto out1;
+ goto out;
}
- for (i = 0; i < pages_per_frame; i++)
- kmap(pages[i]);
-
/* Map in-memory frame for read-only. */
frame_mem = vmap(pages, pages_per_frame, VM_MAP, PAGE_KERNEL_RO);
if (!frame_mem) {
err = -ENOMEM;
- goto out2;
+ goto out1;
}
mutex_lock(&sbi->compress.mtx_lznt);
@@ -2768,7 +2718,7 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages,
if (!lznt) {
mutex_unlock(&sbi->compress.mtx_lznt);
err = -ENOMEM;
- goto out3;
+ goto out2;
}
sbi->compress.lznt = lznt;
@@ -2805,30 +2755,16 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages,
goto out2;
down_read(&ni->file.run_lock);
- err = ntfs_bio_pages(sbi, &ni->file.run,
- ondisk_size < frame_size ? pages_disk : pages,
- pages_per_frame, frame_vbo, ondisk_size,
- REQ_OP_WRITE);
+ err = ntfs_write_run(sbi, &ni->file.run,
+ ondisk_size < frame_size ? frame_ondisk :
+ frame_mem,
+ frame_vbo, ondisk_size);
up_read(&ni->file.run_lock);
-out3:
- vunmap(frame_mem);
-
out2:
- for (i = 0; i < pages_per_frame; i++)
- kunmap(pages[i]);
-
- vunmap(frame_ondisk);
+ vunmap(frame_mem);
out1:
- for (i = 0; i < pages_per_frame; i++) {
- pg = pages_disk[i];
- if (pg) {
- kunmap(pg);
- unlock_page(pg);
- put_page(pg);
- }
- }
- kfree(pages_disk);
+ kvfree(frame_ondisk);
out:
return err;
}
@@ -3026,8 +2962,8 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
err = ni_add_name(new_dir_ni, ni, new_de);
if (!err) {
err = ni_remove_name(dir_ni, ni, de, &de2, &undo);
- WARN_ON(err && ni_remove_name(new_dir_ni, ni, new_de, &de2,
- &undo));
+ WARN_ON(err &&
+ ni_remove_name(new_dir_ni, ni, new_de, &de2, &undo));
}
/*
@@ -3127,7 +3063,8 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup,
if (attr) {
const struct REPARSE_POINT *rp;
- rp = resident_data_ex(attr, sizeof(struct REPARSE_POINT));
+ rp = resident_data_ex(attr,
+ sizeof(struct REPARSE_POINT));
/* If ATTR_REPARSE exists 'rp' can't be NULL. */
if (rp)
dup->extend_data = rp->ReparseTag;
diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c
index c7a2f191254d..5f138f715835 100644
--- a/fs/ntfs3/fsntfs.c
+++ b/fs/ntfs3/fsntfs.c
@@ -1349,7 +1349,14 @@ int ntfs_get_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo,
}
if (buffer_locked(bh))
__wait_on_buffer(bh);
- set_buffer_uptodate(bh);
+
+ lock_buffer(bh);
+ if (!buffer_uptodate(bh))
+ {
+ memset(bh->b_data, 0, blocksize);
+ set_buffer_uptodate(bh);
+ }
+ unlock_buffer(bh);
} else {
bh = ntfs_bread(sb, block);
if (!bh) {
@@ -1472,99 +1479,86 @@ int ntfs_write_bh(struct ntfs_sb_info *sbi, struct NTFS_RECORD_HEADER *rhdr,
}
/*
- * ntfs_bio_pages - Read/write pages from/to disk.
+ * ntfs_read_write_run - Read/Write disk's page cache.
*/
-int ntfs_bio_pages(struct ntfs_sb_info *sbi, const struct runs_tree *run,
- struct page **pages, u32 nr_pages, u64 vbo, u32 bytes,
- enum req_op op)
+int ntfs_read_write_run(struct ntfs_sb_info *sbi, const struct runs_tree *run,
+ void *buf, u64 vbo, size_t bytes, int wr)
{
- int err = 0;
- struct bio *new, *bio = NULL;
struct super_block *sb = sbi->sb;
- struct block_device *bdev = sb->s_bdev;
- struct page *page;
+ struct address_space *mapping = sb->s_bdev->bd_mapping;
u8 cluster_bits = sbi->cluster_bits;
- CLST lcn, clen, vcn, vcn_next;
- u32 add, off, page_idx;
+ CLST vcn_next, vcn = vbo >> cluster_bits;
+ CLST lcn, clen;
u64 lbo, len;
- size_t run_idx;
- struct blk_plug plug;
+ size_t idx;
+ u32 off, op;
+ struct folio *folio;
+ char *kaddr;
if (!bytes)
return 0;
- blk_start_plug(&plug);
+ if (!run_lookup_entry(run, vcn, &lcn, &clen, &idx))
+ return -ENOENT;
- /* Align vbo and bytes to be 512 bytes aligned. */
- lbo = (vbo + bytes + 511) & ~511ull;
- vbo = vbo & ~511ull;
- bytes = lbo - vbo;
+ if (lcn == SPARSE_LCN)
+ return -EINVAL;
- vcn = vbo >> cluster_bits;
- if (!run_lookup_entry(run, vcn, &lcn, &clen, &run_idx)) {
- err = -ENOENT;
- goto out;
- }
off = vbo & sbi->cluster_mask;
- page_idx = 0;
- page = pages[0];
+ lbo = ((u64)lcn << cluster_bits) + off;
+ len = ((u64)clen << cluster_bits) - off;
for (;;) {
- lbo = ((u64)lcn << cluster_bits) + off;
- len = ((u64)clen << cluster_bits) - off;
-new_bio:
- new = bio_alloc(bdev, nr_pages - page_idx, op, GFP_NOFS);
- if (bio) {
- bio_chain(bio, new);
- submit_bio(bio);
- }
- bio = new;
- bio->bi_iter.bi_sector = lbo >> 9;
+ /* Read range [lbo, lbo+len). */
+ folio = read_mapping_folio(mapping, lbo >> PAGE_SHIFT, NULL);
- while (len) {
- off = vbo & (PAGE_SIZE - 1);
- add = off + len > PAGE_SIZE ? (PAGE_SIZE - off) : len;
+ if (IS_ERR(folio))
+ return PTR_ERR(folio);
- if (bio_add_page(bio, page, add, off) < add)
- goto new_bio;
+ off = offset_in_page(lbo);
+ op = PAGE_SIZE - off;
- if (bytes <= add)
- goto out;
- bytes -= add;
- vbo += add;
+ if (op > len)
+ op = len;
+ if (op > bytes)
+ op = bytes;
- if (add + off == PAGE_SIZE) {
- page_idx += 1;
- if (WARN_ON(page_idx >= nr_pages)) {
- err = -EINVAL;
- goto out;
- }
- page = pages[page_idx];
- }
+ kaddr = kmap_local_folio(folio, 0);
+ if (wr) {
+ memcpy(kaddr + off, buf, op);
+ folio_mark_dirty(folio);
+ } else {
+ memcpy(buf, kaddr + off, op);
+ flush_dcache_folio(folio);
+ }
+ kunmap_local(kaddr);
+ folio_put(folio);
- if (len <= add)
- break;
- len -= add;
- lbo += add;
+ bytes -= op;
+ if (!bytes)
+ return 0;
+
+ buf += op;
+ len -= op;
+ if (len) {
+ /* next volume's page. */
+ lbo += op;
+ continue;
}
+ /* get next range. */
vcn_next = vcn + clen;
- if (!run_get_entry(run, ++run_idx, &vcn, &lcn, &clen) ||
+ if (!run_get_entry(run, ++idx, &vcn, &lcn, &clen) ||
vcn != vcn_next) {
- err = -ENOENT;
- goto out;
+ return -ENOENT;
}
- off = 0;
- }
-out:
- if (bio) {
- if (!err)
- err = submit_bio_wait(bio);
- bio_put(bio);
- }
- blk_finish_plug(&plug);
- return err;
+ if (lcn == SPARSE_LCN)
+ return -EINVAL;
+
+ lbo = ((u64)lcn << cluster_bits);
+ len = ((u64)clen << cluster_bits);
+ }
}
/*
diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c
index 6d1bf890929d..7157cfd70fdc 100644
--- a/fs/ntfs3/index.c
+++ b/fs/ntfs3/index.c
@@ -1924,7 +1924,8 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni,
* Undo critical operations.
*/
indx_mark_free(indx, ni, new_vbn >> indx->idx2vbn_bits);
- memcpy(hdr1, hdr1_saved, used1);
+ unsafe_memcpy(hdr1, hdr1_saved, used1,
+ "There are entries after the structure");
indx_write(indx, ni, n1, 0);
}
diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c
index 08266adc42ba..0a9ac5efeb67 100644
--- a/fs/ntfs3/inode.c
+++ b/fs/ntfs3/inode.c
@@ -472,6 +472,7 @@ end_enum:
/* Records in $Extend are not a files or general directories. */
inode->i_op = &ntfs_file_inode_operations;
mode = S_IFREG;
+ init_rwsem(&ni->file.run_lock);
} else {
err = -EINVAL;
goto out;
@@ -975,9 +976,9 @@ out:
/*
* ntfs_write_end - Address_space_operations::write_end.
*/
-int ntfs_write_end(const struct kiocb *iocb,
- struct address_space *mapping, loff_t pos,
- u32 len, u32 copied, struct folio *folio, void *fsdata)
+int ntfs_write_end(const struct kiocb *iocb, struct address_space *mapping,
+ loff_t pos, u32 len, u32 copied, struct folio *folio,
+ void *fsdata)
{
struct inode *inode = mapping->host;
struct ntfs_inode *ni = ntfs_i(inode);
@@ -1099,7 +1100,7 @@ ntfs_create_reparse_buffer(struct ntfs_sb_info *sbi, const char *symname,
typeof(rp->SymbolicLinkReparseBuffer) *rs;
bool is_absolute;
- is_absolute = (strlen(symname) > 1 && symname[1] == ':');
+ is_absolute = symname[0] && symname[1] == ':';
rp = kzalloc(ntfs_reparse_bytes(2 * size + 2, is_absolute), GFP_NOFS);
if (!rp)
@@ -1136,17 +1137,19 @@ ntfs_create_reparse_buffer(struct ntfs_sb_info *sbi, const char *symname,
/* PrintName + SubstituteName. */
rs->SubstituteNameOffset = cpu_to_le16(sizeof(short) * err);
- rs->SubstituteNameLength = cpu_to_le16(sizeof(short) * err + (is_absolute ? 8 : 0));
+ rs->SubstituteNameLength =
+ cpu_to_le16(sizeof(short) * err + (is_absolute ? 8 : 0));
rs->PrintNameLength = rs->SubstituteNameOffset;
/*
* TODO: Use relative path if possible to allow Windows to
* parse this path.
- * 0-absolute path 1- relative path (SYMLINK_FLAG_RELATIVE).
+ * 0-absolute path, 1- relative path (SYMLINK_FLAG_RELATIVE).
*/
rs->Flags = cpu_to_le32(is_absolute ? 0 : SYMLINK_FLAG_RELATIVE);
- memmove(rp_name + err + (is_absolute ? 4 : 0), rp_name, sizeof(short) * err);
+ memmove(rp_name + err + (is_absolute ? 4 : 0), rp_name,
+ sizeof(short) * err);
if (is_absolute) {
/* Decorate SubstituteName. */
@@ -1278,7 +1281,7 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir,
fa |= FILE_ATTRIBUTE_READONLY;
/* Allocate PATH_MAX bytes. */
- new_de = __getname();
+ new_de = kmem_cache_zalloc(names_cachep, GFP_KERNEL);
if (!new_de) {
err = -ENOMEM;
goto out1;
@@ -1635,7 +1638,8 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir,
* Use ni_find_attr cause layout of MFT record may be changed
* in ntfs_init_acl and ntfs_save_wsl_perm.
*/
- attr = ni_find_attr(ni, NULL, NULL, ATTR_NAME, NULL, 0, NULL, NULL);
+ attr = ni_find_attr(ni, NULL, NULL, ATTR_NAME, NULL, 0, NULL,
+ NULL);
if (attr) {
struct ATTR_FILE_NAME *fn;
@@ -1719,7 +1723,7 @@ int ntfs_link_inode(struct inode *inode, struct dentry *dentry)
struct NTFS_DE *de;
/* Allocate PATH_MAX bytes. */
- de = __getname();
+ de = kmem_cache_zalloc(names_cachep, GFP_KERNEL);
if (!de)
return -ENOMEM;
@@ -1757,7 +1761,7 @@ int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry)
return -EINVAL;
/* Allocate PATH_MAX bytes. */
- de = __getname();
+ de = kmem_cache_zalloc(names_cachep, GFP_KERNEL);
if (!de)
return -ENOMEM;
@@ -2102,7 +2106,6 @@ const struct address_space_operations ntfs_aops = {
const struct address_space_operations ntfs_aops_cmpr = {
.read_folio = ntfs_read_folio,
- .readahead = ntfs_readahead,
.dirty_folio = block_dirty_folio,
.direct_IO = ntfs_direct_IO,
};
diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c
index 82c8ae56beee..3b24ca02de61 100644
--- a/fs/ntfs3/namei.c
+++ b/fs/ntfs3/namei.c
@@ -207,13 +207,13 @@ static int ntfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
}
/*
- * ntfs_mkdir- inode_operations::mkdir
+ * ntfs_mkdir - inode_operations::mkdir
*/
static struct dentry *ntfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode)
{
- return ERR_PTR(ntfs_create_inode(idmap, dir, dentry, NULL, S_IFDIR | mode, 0,
- NULL, 0, NULL));
+ return ERR_PTR(ntfs_create_inode(idmap, dir, dentry, NULL,
+ S_IFDIR | mode, 0, NULL, 0, NULL));
}
/*
diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
index 630128716ea7..a4559c9f64e6 100644
--- a/fs/ntfs3/ntfs_fs.h
+++ b/fs/ntfs3/ntfs_fs.h
@@ -212,6 +212,7 @@ struct ntfs_sb_info {
u32 discard_granularity;
u64 discard_granularity_mask_inv; // ~(discard_granularity_mask_inv-1)
+ u32 bdev_blocksize_mask; // bdev_logical_block_size(bdev) - 1;
u32 cluster_size; // bytes per cluster
u32 cluster_mask; // == cluster_size - 1
@@ -570,7 +571,7 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio);
int ni_decompress_file(struct ntfs_inode *ni);
int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
- u32 pages_per_frame);
+ u32 pages_per_frame, int copy);
int ni_write_frame(struct ntfs_inode *ni, struct page **pages,
u32 pages_per_frame);
int ni_remove_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
@@ -584,7 +585,8 @@ int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
struct NTFS_DE *de);
int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
- struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de);
+ struct ntfs_inode *ni, struct NTFS_DE *de,
+ struct NTFS_DE *new_de);
bool ni_is_dirty(struct inode *inode);
@@ -632,9 +634,21 @@ int ntfs_get_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo,
u32 bytes, struct ntfs_buffers *nb);
int ntfs_write_bh(struct ntfs_sb_info *sbi, struct NTFS_RECORD_HEADER *rhdr,
struct ntfs_buffers *nb, int sync);
-int ntfs_bio_pages(struct ntfs_sb_info *sbi, const struct runs_tree *run,
- struct page **pages, u32 nr_pages, u64 vbo, u32 bytes,
- enum req_op op);
+int ntfs_read_write_run(struct ntfs_sb_info *sbi, const struct runs_tree *run,
+ void *buf, u64 vbo, size_t bytes, int wr);
+static inline int ntfs_read_run(struct ntfs_sb_info *sbi,
+ const struct runs_tree *run, void *buf, u64 vbo,
+ size_t bytes)
+{
+ return ntfs_read_write_run(sbi, run, buf, vbo, bytes, 0);
+}
+static inline int ntfs_write_run(struct ntfs_sb_info *sbi,
+ const struct runs_tree *run, void *buf,
+ u64 vbo, size_t bytes)
+{
+ return ntfs_read_write_run(sbi, run, buf, vbo, bytes, 1);
+}
+
int ntfs_bio_fill_1(struct ntfs_sb_info *sbi, const struct runs_tree *run);
int ntfs_vbo_to_lbo(struct ntfs_sb_info *sbi, const struct runs_tree *run,
u64 vbo, u64 *lbo, u64 *bytes);
@@ -709,8 +723,7 @@ int ntfs_set_size(struct inode *inode, u64 new_size);
int ntfs_get_block(struct inode *inode, sector_t vbn,
struct buffer_head *bh_result, int create);
int ntfs_write_begin(const struct kiocb *iocb, struct address_space *mapping,
- loff_t pos, u32 len, struct folio **foliop,
- void **fsdata);
+ loff_t pos, u32 len, struct folio **foliop, void **fsdata);
int ntfs_write_end(const struct kiocb *iocb, struct address_space *mapping,
loff_t pos, u32 len, u32 copied, struct folio *folio,
void *fsdata);
@@ -765,7 +778,7 @@ bool mi_remove_attr(struct ntfs_inode *ni, struct mft_inode *mi,
struct ATTRIB *attr);
bool mi_resize_attr(struct mft_inode *mi, struct ATTRIB *attr, int bytes);
int mi_pack_runs(struct mft_inode *mi, struct ATTRIB *attr,
- struct runs_tree *run, CLST len);
+ const struct runs_tree *run, CLST len);
static inline bool mi_is_ref(const struct mft_inode *mi,
const struct MFT_REF *ref)
{
@@ -800,7 +813,7 @@ void run_truncate_head(struct runs_tree *run, CLST vcn);
void run_truncate_around(struct runs_tree *run, CLST vcn);
bool run_add_entry(struct runs_tree *run, CLST vcn, CLST lcn, CLST len,
bool is_mft);
-bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len);
+bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len, CLST sub);
bool run_insert_range(struct runs_tree *run, CLST vcn, CLST len);
bool run_get_entry(const struct runs_tree *run, size_t index, CLST *vcn,
CLST *lcn, CLST *len);
@@ -979,11 +992,12 @@ static inline __le64 kernel2nt(const struct timespec64 *ts)
*/
static inline void nt2kernel(const __le64 tm, struct timespec64 *ts)
{
- u64 t = le64_to_cpu(tm) - _100ns2seconds * SecondsToStartOf1970;
+ s32 t32;
+ /* use signed 64 bit to support timestamps prior to epoch. xfstest 258. */
+ s64 t = le64_to_cpu(tm) - _100ns2seconds * SecondsToStartOf1970;
- // WARNING: do_div changes its first argument(!)
- ts->tv_nsec = do_div(t, _100ns2seconds) * 100;
- ts->tv_sec = t;
+ ts->tv_sec = div_s64_rem(t, _100ns2seconds, &t32);
+ ts->tv_nsec = t32 * 100;
}
static inline struct ntfs_sb_info *ntfs_sb(struct super_block *sb)
diff --git a/fs/ntfs3/record.c b/fs/ntfs3/record.c
index 714c7ecedca8..167093e8d287 100644
--- a/fs/ntfs3/record.c
+++ b/fs/ntfs3/record.c
@@ -621,7 +621,7 @@ bool mi_resize_attr(struct mft_inode *mi, struct ATTRIB *attr, int bytes)
* If failed record is not changed.
*/
int mi_pack_runs(struct mft_inode *mi, struct ATTRIB *attr,
- struct runs_tree *run, CLST len)
+ const struct runs_tree *run, CLST len)
{
int err = 0;
struct ntfs_sb_info *sbi = mi->sbi;
diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c
index 88550085f745..395b20492525 100644
--- a/fs/ntfs3/run.c
+++ b/fs/ntfs3/run.c
@@ -487,7 +487,7 @@ requires_new_range:
* Helper for attr_collapse_range(),
* which is helper for fallocate(collapse_range).
*/
-bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len)
+bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len, CLST sub)
{
size_t index, eat;
struct ntfs_run *r, *e, *eat_start, *eat_end;
@@ -511,7 +511,7 @@ bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len)
/* Collapse a middle part of normal run, split. */
if (!run_add_entry(run, vcn, SPARSE_LCN, len, false))
return false;
- return run_collapse_range(run, vcn, len);
+ return run_collapse_range(run, vcn, len, sub);
}
r += 1;
@@ -545,6 +545,13 @@ bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len)
memmove(eat_start, eat_end, (e - eat_end) * sizeof(*r));
run->count -= eat;
+ if (sub) {
+ e -= eat;
+ for (r = run->runs; r < e; r++) {
+ r->vcn -= sub;
+ }
+ }
+
return true;
}
@@ -984,8 +991,12 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
if (!dlcn)
return -EINVAL;
- if (check_add_overflow(prev_lcn, dlcn, &lcn))
+ /* Check special combination: 0 + SPARSE_LCN64. */
+ if (!prev_lcn && dlcn == SPARSE_LCN64) {
+ lcn = SPARSE_LCN64;
+ } else if (check_add_overflow(prev_lcn, dlcn, &lcn)) {
return -EINVAL;
+ }
prev_lcn = lcn;
} else {
/* The size of 'dlcn' can't be > 8. */
diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c
index 8d09dfec970a..8b0cf0ed4f72 100644
--- a/fs/ntfs3/super.c
+++ b/fs/ntfs3/super.c
@@ -16,6 +16,13 @@
* mi - MFT inode - One MFT record(usually 1024 bytes or 4K), consists of attributes.
* ni - NTFS inode - Extends linux inode. consists of one or more mft inodes.
* index - unit inside directory - 2K, 4K, <=page size, does not depend on cluster size.
+ * resident attribute - Attribute with content stored directly in the MFT record
+ * non-resident attribute - Attribute with content stored in clusters
+ * data_size - Size of attribute content in bytes. Equal to inode->i_size
+ * valid_size - Number of bytes written to the non-resident attribute
+ * allocated_size - Total size of clusters allocated for non-resident content
+ * total_size - Actual size of allocated clusters for sparse or compressed attributes
+ * - Constraint: valid_size <= data_size <= allocated_size
*
* WSL - Windows Subsystem for Linux
* https://docs.microsoft.com/en-us/windows/wsl/file-permissions
@@ -278,9 +285,9 @@ static const struct fs_parameter_spec ntfs_fs_parameters[] = {
fsparam_flag("hide_dot_files", Opt_hide_dot_files),
fsparam_flag("windows_names", Opt_windows_names),
fsparam_flag("showmeta", Opt_showmeta),
- fsparam_flag("acl", Opt_acl),
+ fsparam_flag_no("acl", Opt_acl),
fsparam_string("iocharset", Opt_iocharset),
- fsparam_flag("prealloc", Opt_prealloc),
+ fsparam_flag_no("prealloc", Opt_prealloc),
fsparam_flag("nocase", Opt_nocase),
{}
};
@@ -289,10 +296,8 @@ static const struct fs_parameter_spec ntfs_fs_parameters[] = {
/*
* Load nls table or if @nls is utf8 then return NULL.
*
- * It is good idea to use here "const char *nls".
- * But load_nls accepts "char*".
*/
-static struct nls_table *ntfs_load_nls(char *nls)
+static struct nls_table *ntfs_load_nls(const char *nls)
{
struct nls_table *ret;
@@ -391,7 +396,7 @@ static int ntfs_fs_parse_param(struct fs_context *fc,
param->string = NULL;
break;
case Opt_prealloc:
- opts->prealloc = 1;
+ opts->prealloc = !result.negated;
break;
case Opt_nocase:
opts->nocase = 1;
@@ -567,10 +572,8 @@ static void ntfs_create_procdir(struct super_block *sb)
if (e) {
struct ntfs_sb_info *sbi = sb->s_fs_info;
- proc_create_data("volinfo", 0444, e,
- &ntfs3_volinfo_fops, sb);
- proc_create_data("label", 0644, e,
- &ntfs3_label_fops, sb);
+ proc_create_data("volinfo", 0444, e, &ntfs3_volinfo_fops, sb);
+ proc_create_data("label", 0644, e, &ntfs3_label_fops, sb);
sbi->procdir = e;
}
}
@@ -601,10 +604,12 @@ static void ntfs_remove_proc_root(void)
}
}
#else
-static void ntfs_create_procdir(struct super_block *sb) {}
-static void ntfs_remove_procdir(struct super_block *sb) {}
-static void ntfs_create_proc_root(void) {}
-static void ntfs_remove_proc_root(void) {}
+// clang-format off
+static void ntfs_create_procdir(struct super_block *sb){}
+static void ntfs_remove_procdir(struct super_block *sb){}
+static void ntfs_create_proc_root(void){}
+static void ntfs_remove_proc_root(void){}
+// clang-format on
#endif
static struct kmem_cache *ntfs_inode_cachep;
@@ -698,6 +703,14 @@ static void ntfs_put_super(struct super_block *sb)
/* Mark rw ntfs as clear, if possible. */
ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
+
+ if (sbi->options) {
+ unload_nls(sbi->options->nls);
+ kfree(sbi->options->nls_name);
+ kfree(sbi->options);
+ sbi->options = NULL;
+ }
+
ntfs3_put_sbi(sbi);
}
@@ -934,6 +947,11 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
sbi->volume.blocks = dev_size >> PAGE_SHIFT;
+ /* Set dummy blocksize to read boot_block. */
+ if (!sb_min_blocksize(sb, PAGE_SIZE)) {
+ return -EINVAL;
+ }
+
read_boot:
bh = ntfs_bread(sb, boot_block);
if (!bh)
@@ -1058,6 +1076,7 @@ read_boot:
dev_size += sector_size - 1;
}
+ sbi->bdev_blocksize_mask = max(boot_sector_size, sector_size) - 1;
sbi->mft.lbo = mlcn << cluster_bits;
sbi->mft.lbo2 = mlcn2 << cluster_bits;
@@ -1199,7 +1218,8 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
int err;
struct ntfs_sb_info *sbi = sb->s_fs_info;
struct block_device *bdev = sb->s_bdev;
- struct ntfs_mount_options *options;
+ struct ntfs_mount_options *fc_opts;
+ struct ntfs_mount_options *options = NULL;
struct inode *inode;
struct ntfs_inode *ni;
size_t i, tt, bad_len, bad_frags;
@@ -1216,7 +1236,23 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
ref.high = 0;
sbi->sb = sb;
- sbi->options = options = fc->fs_private;
+ fc_opts = fc->fs_private;
+ if (!fc_opts) {
+ errorf(fc, "missing mount options");
+ return -EINVAL;
+ }
+ options = kmemdup(fc_opts, sizeof(*fc_opts), GFP_KERNEL);
+ if (!options)
+ return -ENOMEM;
+
+ if (fc_opts->nls_name) {
+ options->nls_name = kstrdup(fc_opts->nls_name, GFP_KERNEL);
+ if (!options->nls_name) {
+ kfree(options);
+ return -ENOMEM;
+ }
+ }
+ sbi->options = options;
fc->fs_private = NULL;
sb->s_flags |= SB_NODIRATIME;
sb->s_magic = 0x7366746e; // "ntfs"
@@ -1224,8 +1260,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_export_op = &ntfs_export_ops;
sb->s_time_gran = NTFS_TIME_GRAN; // 100 nsec
sb->s_xattr = ntfs_xattr_handlers;
- if (options->nocase)
- set_default_d_op(sb, &ntfs_dentry_ops);
+ set_default_d_op(sb, options->nocase ? &ntfs_dentry_ops : NULL);
options->nls = ntfs_load_nls(options->nls_name);
if (IS_ERR(options->nls)) {
@@ -1295,7 +1330,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
sbi->volume.ni = ni;
if (info->flags & VOLUME_FLAG_DIRTY) {
sbi->volume.real_dirty = true;
- ntfs_info(sb, "It is recommened to use chkdsk.");
+ ntfs_info(sb, "It is recommended to use chkdsk.");
}
/* Load $MFTMirr to estimate recs_mirr. */
@@ -1642,9 +1677,16 @@ load_root:
put_inode_out:
iput(inode);
out:
+ /* sbi->options == options */
+ if (options) {
+ unload_nls(options->nls);
+ kfree(options->nls_name);
+ kfree(options);
+ sbi->options = NULL;
+ }
+
ntfs3_put_sbi(sbi);
kfree(boot2);
- ntfs3_put_sbi(sbi);
return err;
}
@@ -1768,6 +1810,12 @@ static int __ntfs_init_fs_context(struct fs_context *fc)
opts->fs_gid = current_gid();
opts->fs_fmask_inv = ~current_umask();
opts->fs_dmask_inv = ~current_umask();
+ opts->prealloc = 1;
+
+#ifdef CONFIG_NTFS3_FS_POSIX_ACL
+ /* Set the default value 'acl' */
+ fc->sb_flags |= SB_POSIXACL;
+#endif
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE)
goto ok;
diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c
index e519e21596a7..c93df55e98d0 100644
--- a/fs/ntfs3/xattr.c
+++ b/fs/ntfs3/xattr.c
@@ -654,12 +654,22 @@ static noinline int ntfs_set_acl_ex(struct mnt_idmap *idmap,
err = ntfs_set_ea(inode, name, name_len, value, size, flags, 0, NULL);
if (err == -ENODATA && !size)
err = 0; /* Removing non existed xattr. */
- if (!err) {
- set_cached_acl(inode, type, acl);
+ if (err)
+ goto out;
+
+ if (inode->i_mode != mode) {
+ umode_t old_mode = inode->i_mode;
+ inode->i_mode = mode;
+ err = ntfs_save_wsl_perm(inode, NULL);
+ if (err) {
+ inode->i_mode = old_mode;
+ goto out;
+ }
inode->i_mode = mode;
- inode_set_ctime_current(inode);
- mark_inode_dirty(inode);
}
+ set_cached_acl(inode, type, acl);
+ inode_set_ctime_current(inode);
+ mark_inode_dirty(inode);
out:
kfree(value);
diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c
index b05d4e9d13b2..79b281e32f4c 100644
--- a/fs/ocfs2/cluster/tcp.c
+++ b/fs/ocfs2/cluster/tcp.c
@@ -1615,7 +1615,7 @@ static void o2net_start_connect(struct work_struct *work)
myaddr.sin_addr.s_addr = mynode->nd_ipv4_address;
myaddr.sin_port = htons(0); /* any port */
- ret = sock->ops->bind(sock, (struct sockaddr *)&myaddr,
+ ret = sock->ops->bind(sock, (struct sockaddr_unsized *)&myaddr,
sizeof(myaddr));
if (ret) {
mlog(ML_ERROR, "bind failed with %d at address %pI4\n",
@@ -1638,7 +1638,7 @@ static void o2net_start_connect(struct work_struct *work)
remoteaddr.sin_port = node->nd_ipv4_port;
ret = sc->sc_sock->ops->connect(sc->sc_sock,
- (struct sockaddr *)&remoteaddr,
+ (struct sockaddr_unsized *)&remoteaddr,
sizeof(remoteaddr),
O_NONBLOCK);
if (ret == -EINPROGRESS)
@@ -2002,7 +2002,7 @@ static int o2net_open_listening_sock(__be32 addr, __be16 port)
INIT_WORK(&o2net_listen_work, o2net_accept_many);
sock->sk->sk_reuse = SK_CAN_REUSE;
- ret = sock->ops->bind(sock, (struct sockaddr *)&sin, sizeof(sin));
+ ret = sock->ops->bind(sock, (struct sockaddr_unsized *)&sin, sizeof(sin));
if (ret < 0) {
printk(KERN_ERR "o2net: Error %d while binding socket at "
"%pI4:%u\n", ret, &addr, ntohs(port));
diff --git a/fs/resctrl/ctrlmondata.c b/fs/resctrl/ctrlmondata.c
index 0d0ef54fc4de..b2d178d3556e 100644
--- a/fs/resctrl/ctrlmondata.c
+++ b/fs/resctrl/ctrlmondata.c
@@ -24,7 +24,8 @@
#include "internal.h"
struct rdt_parse_data {
- struct rdtgroup *rdtgrp;
+ u32 closid;
+ enum rdtgrp_mode mode;
char *buf;
};
@@ -77,8 +78,8 @@ static int parse_bw(struct rdt_parse_data *data, struct resctrl_schema *s,
struct rdt_ctrl_domain *d)
{
struct resctrl_staged_config *cfg;
- u32 closid = data->rdtgrp->closid;
struct rdt_resource *r = s->res;
+ u32 closid = data->closid;
u32 bw_val;
cfg = &d->staged_config[s->conf_type];
@@ -156,9 +157,10 @@ static bool cbm_validate(char *buf, u32 *data, struct rdt_resource *r)
static int parse_cbm(struct rdt_parse_data *data, struct resctrl_schema *s,
struct rdt_ctrl_domain *d)
{
- struct rdtgroup *rdtgrp = data->rdtgrp;
+ enum rdtgrp_mode mode = data->mode;
struct resctrl_staged_config *cfg;
struct rdt_resource *r = s->res;
+ u32 closid = data->closid;
u32 cbm_val;
cfg = &d->staged_config[s->conf_type];
@@ -171,7 +173,7 @@ static int parse_cbm(struct rdt_parse_data *data, struct resctrl_schema *s,
* Cannot set up more than one pseudo-locked region in a cache
* hierarchy.
*/
- if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP &&
+ if (mode == RDT_MODE_PSEUDO_LOCKSETUP &&
rdtgroup_pseudo_locked_in_hierarchy(d)) {
rdt_last_cmd_puts("Pseudo-locked region in hierarchy\n");
return -EINVAL;
@@ -180,8 +182,7 @@ static int parse_cbm(struct rdt_parse_data *data, struct resctrl_schema *s,
if (!cbm_validate(data->buf, &cbm_val, r))
return -EINVAL;
- if ((rdtgrp->mode == RDT_MODE_EXCLUSIVE ||
- rdtgrp->mode == RDT_MODE_SHAREABLE) &&
+ if ((mode == RDT_MODE_EXCLUSIVE || mode == RDT_MODE_SHAREABLE) &&
rdtgroup_cbm_overlaps_pseudo_locked(d, cbm_val)) {
rdt_last_cmd_puts("CBM overlaps with pseudo-locked region\n");
return -EINVAL;
@@ -191,14 +192,14 @@ static int parse_cbm(struct rdt_parse_data *data, struct resctrl_schema *s,
* The CBM may not overlap with the CBM of another closid if
* either is exclusive.
*/
- if (rdtgroup_cbm_overlaps(s, d, cbm_val, rdtgrp->closid, true)) {
+ if (rdtgroup_cbm_overlaps(s, d, cbm_val, closid, true)) {
rdt_last_cmd_puts("Overlaps with exclusive group\n");
return -EINVAL;
}
- if (rdtgroup_cbm_overlaps(s, d, cbm_val, rdtgrp->closid, false)) {
- if (rdtgrp->mode == RDT_MODE_EXCLUSIVE ||
- rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
+ if (rdtgroup_cbm_overlaps(s, d, cbm_val, closid, false)) {
+ if (mode == RDT_MODE_EXCLUSIVE ||
+ mode == RDT_MODE_PSEUDO_LOCKSETUP) {
rdt_last_cmd_puts("Overlaps with other group\n");
return -EINVAL;
}
@@ -262,7 +263,8 @@ next:
list_for_each_entry(d, &r->ctrl_domains, hdr.list) {
if (d->hdr.id == dom_id) {
data.buf = dom;
- data.rdtgrp = rdtgrp;
+ data.closid = rdtgrp->closid;
+ data.mode = rdtgrp->mode;
if (parse_ctrlval(&data, s, d))
return -EINVAL;
if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
@@ -381,7 +383,8 @@ out:
return ret ?: nbytes;
}
-static void show_doms(struct seq_file *s, struct resctrl_schema *schema, int closid)
+static void show_doms(struct seq_file *s, struct resctrl_schema *schema,
+ char *resource_name, int closid)
{
struct rdt_resource *r = schema->res;
struct rdt_ctrl_domain *dom;
@@ -391,7 +394,8 @@ static void show_doms(struct seq_file *s, struct resctrl_schema *schema, int clo
/* Walking r->domains, ensure it can't race with cpuhp */
lockdep_assert_cpus_held();
- seq_printf(s, "%*s:", max_name_width, schema->name);
+ if (resource_name)
+ seq_printf(s, "%*s:", max_name_width, resource_name);
list_for_each_entry(dom, &r->ctrl_domains, hdr.list) {
if (sep)
seq_puts(s, ";");
@@ -437,7 +441,7 @@ int rdtgroup_schemata_show(struct kernfs_open_file *of,
closid = rdtgrp->closid;
list_for_each_entry(schema, &resctrl_schema_all, list) {
if (closid < schema->num_closid)
- show_doms(s, schema, closid);
+ show_doms(s, schema, schema->name, closid);
}
}
} else {
@@ -676,3 +680,280 @@ out:
rdtgroup_kn_unlock(of->kn);
return ret;
}
+
+int resctrl_io_alloc_show(struct kernfs_open_file *of, struct seq_file *seq, void *v)
+{
+ struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
+ struct rdt_resource *r = s->res;
+
+ mutex_lock(&rdtgroup_mutex);
+
+ if (r->cache.io_alloc_capable) {
+ if (resctrl_arch_get_io_alloc_enabled(r))
+ seq_puts(seq, "enabled\n");
+ else
+ seq_puts(seq, "disabled\n");
+ } else {
+ seq_puts(seq, "not supported\n");
+ }
+
+ mutex_unlock(&rdtgroup_mutex);
+
+ return 0;
+}
+
+/*
+ * resctrl_io_alloc_closid_supported() - io_alloc feature utilizes the
+ * highest CLOSID value to direct I/O traffic. Ensure that io_alloc_closid
+ * is in the supported range.
+ */
+static bool resctrl_io_alloc_closid_supported(u32 io_alloc_closid)
+{
+ return io_alloc_closid < closids_supported();
+}
+
+/*
+ * Initialize io_alloc CLOSID cache resource CBM with all usable (shared
+ * and unused) cache portions.
+ */
+static int resctrl_io_alloc_init_cbm(struct resctrl_schema *s, u32 closid)
+{
+ enum resctrl_conf_type peer_type;
+ struct rdt_resource *r = s->res;
+ struct rdt_ctrl_domain *d;
+ int ret;
+
+ rdt_staged_configs_clear();
+
+ ret = rdtgroup_init_cat(s, closid);
+ if (ret < 0)
+ goto out;
+
+ /* Keep CDP_CODE and CDP_DATA of io_alloc CLOSID's CBM in sync. */
+ if (resctrl_arch_get_cdp_enabled(r->rid)) {
+ peer_type = resctrl_peer_type(s->conf_type);
+ list_for_each_entry(d, &s->res->ctrl_domains, hdr.list)
+ memcpy(&d->staged_config[peer_type],
+ &d->staged_config[s->conf_type],
+ sizeof(d->staged_config[0]));
+ }
+
+ ret = resctrl_arch_update_domains(r, closid);
+out:
+ rdt_staged_configs_clear();
+ return ret;
+}
+
+/*
+ * resctrl_io_alloc_closid() - io_alloc feature routes I/O traffic using
+ * the highest available CLOSID. Retrieve the maximum CLOSID supported by the
+ * resource. Note that if Code Data Prioritization (CDP) is enabled, the number
+ * of available CLOSIDs is reduced by half.
+ */
+u32 resctrl_io_alloc_closid(struct rdt_resource *r)
+{
+ if (resctrl_arch_get_cdp_enabled(r->rid))
+ return resctrl_arch_get_num_closid(r) / 2 - 1;
+ else
+ return resctrl_arch_get_num_closid(r) - 1;
+}
+
+ssize_t resctrl_io_alloc_write(struct kernfs_open_file *of, char *buf,
+ size_t nbytes, loff_t off)
+{
+ struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
+ struct rdt_resource *r = s->res;
+ char const *grp_name;
+ u32 io_alloc_closid;
+ bool enable;
+ int ret;
+
+ ret = kstrtobool(buf, &enable);
+ if (ret)
+ return ret;
+
+ cpus_read_lock();
+ mutex_lock(&rdtgroup_mutex);
+
+ rdt_last_cmd_clear();
+
+ if (!r->cache.io_alloc_capable) {
+ rdt_last_cmd_printf("io_alloc is not supported on %s\n", s->name);
+ ret = -ENODEV;
+ goto out_unlock;
+ }
+
+ /* If the feature is already up to date, no action is needed. */
+ if (resctrl_arch_get_io_alloc_enabled(r) == enable)
+ goto out_unlock;
+
+ io_alloc_closid = resctrl_io_alloc_closid(r);
+ if (!resctrl_io_alloc_closid_supported(io_alloc_closid)) {
+ rdt_last_cmd_printf("io_alloc CLOSID (ctrl_hw_id) %u is not available\n",
+ io_alloc_closid);
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (enable) {
+ if (!closid_alloc_fixed(io_alloc_closid)) {
+ grp_name = rdtgroup_name_by_closid(io_alloc_closid);
+ WARN_ON_ONCE(!grp_name);
+ rdt_last_cmd_printf("CLOSID (ctrl_hw_id) %u for io_alloc is used by %s group\n",
+ io_alloc_closid, grp_name ? grp_name : "another");
+ ret = -ENOSPC;
+ goto out_unlock;
+ }
+
+ ret = resctrl_io_alloc_init_cbm(s, io_alloc_closid);
+ if (ret) {
+ rdt_last_cmd_puts("Failed to initialize io_alloc allocations\n");
+ closid_free(io_alloc_closid);
+ goto out_unlock;
+ }
+ } else {
+ closid_free(io_alloc_closid);
+ }
+
+ ret = resctrl_arch_io_alloc_enable(r, enable);
+ if (enable && ret) {
+ rdt_last_cmd_puts("Failed to enable io_alloc feature\n");
+ closid_free(io_alloc_closid);
+ }
+
+out_unlock:
+ mutex_unlock(&rdtgroup_mutex);
+ cpus_read_unlock();
+
+ return ret ?: nbytes;
+}
+
+int resctrl_io_alloc_cbm_show(struct kernfs_open_file *of, struct seq_file *seq, void *v)
+{
+ struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
+ struct rdt_resource *r = s->res;
+ int ret = 0;
+
+ cpus_read_lock();
+ mutex_lock(&rdtgroup_mutex);
+
+ rdt_last_cmd_clear();
+
+ if (!r->cache.io_alloc_capable) {
+ rdt_last_cmd_printf("io_alloc is not supported on %s\n", s->name);
+ ret = -ENODEV;
+ goto out_unlock;
+ }
+
+ if (!resctrl_arch_get_io_alloc_enabled(r)) {
+ rdt_last_cmd_printf("io_alloc is not enabled on %s\n", s->name);
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ /*
+ * When CDP is enabled, the CBMs of the highest CLOSID of CDP_CODE and
+ * CDP_DATA are kept in sync. As a result, the io_alloc CBMs shown for
+ * either CDP resource are identical and accurately represent the CBMs
+ * used for I/O.
+ */
+ show_doms(seq, s, NULL, resctrl_io_alloc_closid(r));
+
+out_unlock:
+ mutex_unlock(&rdtgroup_mutex);
+ cpus_read_unlock();
+ return ret;
+}
+
+static int resctrl_io_alloc_parse_line(char *line, struct rdt_resource *r,
+ struct resctrl_schema *s, u32 closid)
+{
+ enum resctrl_conf_type peer_type;
+ struct rdt_parse_data data;
+ struct rdt_ctrl_domain *d;
+ char *dom = NULL, *id;
+ unsigned long dom_id;
+
+next:
+ if (!line || line[0] == '\0')
+ return 0;
+
+ dom = strsep(&line, ";");
+ id = strsep(&dom, "=");
+ if (!dom || kstrtoul(id, 10, &dom_id)) {
+ rdt_last_cmd_puts("Missing '=' or non-numeric domain\n");
+ return -EINVAL;
+ }
+
+ dom = strim(dom);
+ list_for_each_entry(d, &r->ctrl_domains, hdr.list) {
+ if (d->hdr.id == dom_id) {
+ data.buf = dom;
+ data.mode = RDT_MODE_SHAREABLE;
+ data.closid = closid;
+ if (parse_cbm(&data, s, d))
+ return -EINVAL;
+ /*
+ * Keep io_alloc CLOSID's CBM of CDP_CODE and CDP_DATA
+ * in sync.
+ */
+ if (resctrl_arch_get_cdp_enabled(r->rid)) {
+ peer_type = resctrl_peer_type(s->conf_type);
+ memcpy(&d->staged_config[peer_type],
+ &d->staged_config[s->conf_type],
+ sizeof(d->staged_config[0]));
+ }
+ goto next;
+ }
+ }
+
+ return -EINVAL;
+}
+
+ssize_t resctrl_io_alloc_cbm_write(struct kernfs_open_file *of, char *buf,
+ size_t nbytes, loff_t off)
+{
+ struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
+ struct rdt_resource *r = s->res;
+ u32 io_alloc_closid;
+ int ret = 0;
+
+ /* Valid input requires a trailing newline */
+ if (nbytes == 0 || buf[nbytes - 1] != '\n')
+ return -EINVAL;
+
+ buf[nbytes - 1] = '\0';
+
+ cpus_read_lock();
+ mutex_lock(&rdtgroup_mutex);
+ rdt_last_cmd_clear();
+
+ if (!r->cache.io_alloc_capable) {
+ rdt_last_cmd_printf("io_alloc is not supported on %s\n", s->name);
+ ret = -ENODEV;
+ goto out_unlock;
+ }
+
+ if (!resctrl_arch_get_io_alloc_enabled(r)) {
+ rdt_last_cmd_printf("io_alloc is not enabled on %s\n", s->name);
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ io_alloc_closid = resctrl_io_alloc_closid(r);
+
+ rdt_staged_configs_clear();
+ ret = resctrl_io_alloc_parse_line(buf, r, s, io_alloc_closid);
+ if (ret)
+ goto out_clear_configs;
+
+ ret = resctrl_arch_update_domains(r, io_alloc_closid);
+
+out_clear_configs:
+ rdt_staged_configs_clear();
+out_unlock:
+ mutex_unlock(&rdtgroup_mutex);
+ cpus_read_unlock();
+
+ return ret ?: nbytes;
+}
diff --git a/fs/resctrl/internal.h b/fs/resctrl/internal.h
index cf1fd82dc5a9..bff4a54ae333 100644
--- a/fs/resctrl/internal.h
+++ b/fs/resctrl/internal.h
@@ -390,6 +390,8 @@ void rdt_staged_configs_clear(void);
bool closid_allocated(unsigned int closid);
+bool closid_alloc_fixed(u32 closid);
+
int resctrl_find_cleanest_closid(void);
void *rdt_kn_parent_priv(struct kernfs_node *kn);
@@ -426,6 +428,21 @@ int mbm_L3_assignments_show(struct kernfs_open_file *of, struct seq_file *s, voi
ssize_t mbm_L3_assignments_write(struct kernfs_open_file *of, char *buf, size_t nbytes,
loff_t off);
+int resctrl_io_alloc_show(struct kernfs_open_file *of, struct seq_file *seq, void *v);
+
+int rdtgroup_init_cat(struct resctrl_schema *s, u32 closid);
+
+enum resctrl_conf_type resctrl_peer_type(enum resctrl_conf_type my_type);
+
+ssize_t resctrl_io_alloc_write(struct kernfs_open_file *of, char *buf,
+ size_t nbytes, loff_t off);
+
+const char *rdtgroup_name_by_closid(u32 closid);
+int resctrl_io_alloc_cbm_show(struct kernfs_open_file *of, struct seq_file *seq,
+ void *v);
+ssize_t resctrl_io_alloc_cbm_write(struct kernfs_open_file *of, char *buf,
+ size_t nbytes, loff_t off);
+u32 resctrl_io_alloc_closid(struct rdt_resource *r);
#ifdef CONFIG_RESCTRL_FS_PSEUDO_LOCK
int rdtgroup_locksetup_enter(struct rdtgroup *rdtgrp);
diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c
index 0320360cd7a6..8e39dfda56bc 100644
--- a/fs/resctrl/rdtgroup.c
+++ b/fs/resctrl/rdtgroup.c
@@ -226,6 +226,11 @@ bool closid_allocated(unsigned int closid)
return !test_bit(closid, closid_free_map);
}
+bool closid_alloc_fixed(u32 closid)
+{
+ return __test_and_clear_bit(closid, closid_free_map);
+}
+
/**
* rdtgroup_mode_by_closid - Return mode of resource group with closid
* @closid: closid if the resource group
@@ -1057,15 +1062,17 @@ static int rdt_bit_usage_show(struct kernfs_open_file *of,
cpus_read_lock();
mutex_lock(&rdtgroup_mutex);
- hw_shareable = r->cache.shareable_bits;
list_for_each_entry(dom, &r->ctrl_domains, hdr.list) {
if (sep)
seq_putc(seq, ';');
+ hw_shareable = r->cache.shareable_bits;
sw_shareable = 0;
exclusive = 0;
seq_printf(seq, "%d=", dom->hdr.id);
for (i = 0; i < closids_supported(); i++) {
- if (!closid_allocated(i))
+ if (!closid_allocated(i) ||
+ (resctrl_arch_get_io_alloc_enabled(r) &&
+ i == resctrl_io_alloc_closid(r)))
continue;
ctrl_val = resctrl_arch_get_config(r, dom, i,
s->conf_type);
@@ -1093,6 +1100,21 @@ static int rdt_bit_usage_show(struct kernfs_open_file *of,
break;
}
}
+
+ /*
+ * When the "io_alloc" feature is enabled, a portion of the cache
+ * is configured for shared use between hardware and software.
+ * Also, when CDP is enabled the CBMs of CDP_CODE and CDP_DATA
+ * resources are kept in sync. So, the CBMs for "io_alloc" can
+ * be accessed through either resource.
+ */
+ if (resctrl_arch_get_io_alloc_enabled(r)) {
+ ctrl_val = resctrl_arch_get_config(r, dom,
+ resctrl_io_alloc_closid(r),
+ s->conf_type);
+ hw_shareable |= ctrl_val;
+ }
+
for (i = r->cache.cbm_len - 1; i >= 0; i--) {
pseudo_locked = dom->plr ? dom->plr->cbm : 0;
hwb = test_bit(i, &hw_shareable);
@@ -1247,7 +1269,7 @@ static int rdtgroup_mode_show(struct kernfs_open_file *of,
return 0;
}
-static enum resctrl_conf_type resctrl_peer_type(enum resctrl_conf_type my_type)
+enum resctrl_conf_type resctrl_peer_type(enum resctrl_conf_type my_type)
{
switch (my_type) {
case CDP_CODE:
@@ -1838,6 +1860,18 @@ void resctrl_bmec_files_show(struct rdt_resource *r, struct kernfs_node *l3_mon_
kernfs_put(mon_kn);
}
+const char *rdtgroup_name_by_closid(u32 closid)
+{
+ struct rdtgroup *rdtgrp;
+
+ list_for_each_entry(rdtgrp, &rdt_all_groups, rdtgroup_list) {
+ if (rdtgrp->closid == closid)
+ return rdt_kn_name(rdtgrp->kn);
+ }
+
+ return NULL;
+}
+
/* rdtgroup information files for one cache resource. */
static struct rftype res_common_files[] = {
{
@@ -1948,6 +1982,20 @@ static struct rftype res_common_files[] = {
.seq_show = rdt_thread_throttle_mode_show,
},
{
+ .name = "io_alloc",
+ .mode = 0644,
+ .kf_ops = &rdtgroup_kf_single_ops,
+ .seq_show = resctrl_io_alloc_show,
+ .write = resctrl_io_alloc_write,
+ },
+ {
+ .name = "io_alloc_cbm",
+ .mode = 0644,
+ .kf_ops = &rdtgroup_kf_single_ops,
+ .seq_show = resctrl_io_alloc_cbm_show,
+ .write = resctrl_io_alloc_cbm_write,
+ },
+ {
.name = "max_threshold_occupancy",
.mode = 0644,
.kf_ops = &rdtgroup_kf_single_ops,
@@ -2138,6 +2186,23 @@ static void thread_throttle_mode_init(void)
RFTYPE_CTRL_INFO | RFTYPE_RES_MB);
}
+/*
+ * The resctrl file "io_alloc" is added using L3 resource. However, it results
+ * in this file being visible for *all* cache resources (eg. L2 cache),
+ * whether it supports "io_alloc" or not.
+ */
+static void io_alloc_init(void)
+{
+ struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3);
+
+ if (r->cache.io_alloc_capable) {
+ resctrl_file_fflags_init("io_alloc", RFTYPE_CTRL_INFO |
+ RFTYPE_RES_CACHE);
+ resctrl_file_fflags_init("io_alloc_cbm",
+ RFTYPE_CTRL_INFO | RFTYPE_RES_CACHE);
+ }
+}
+
void resctrl_file_fflags_init(const char *config, unsigned long fflags)
{
struct rftype *rft;
@@ -3383,11 +3448,12 @@ static u32 cbm_ensure_valid(u32 _val, struct rdt_resource *r)
{
unsigned int cbm_len = r->cache.cbm_len;
unsigned long first_bit, zero_bit;
- unsigned long val = _val;
+ unsigned long val;
- if (!val)
- return 0;
+ if (!_val || r->cache.arch_has_sparse_bitmasks)
+ return _val;
+ val = _val;
first_bit = find_first_bit(&val, cbm_len);
zero_bit = find_next_zero_bit(&val, cbm_len, first_bit);
@@ -3480,7 +3546,7 @@ static int __init_one_rdt_domain(struct rdt_ctrl_domain *d, struct resctrl_schem
* If there are no more shareable bits available on any domain then
* the entire allocation will fail.
*/
-static int rdtgroup_init_cat(struct resctrl_schema *s, u32 closid)
+int rdtgroup_init_cat(struct resctrl_schema *s, u32 closid)
{
struct rdt_ctrl_domain *d;
int ret;
@@ -4408,6 +4474,8 @@ int resctrl_init(void)
thread_throttle_mode_init();
+ io_alloc_init();
+
ret = resctrl_mon_resource_init();
if (ret)
return ret;
diff --git a/fs/select.c b/fs/select.c
index 082cf60c7e23..65019b8ba3f7 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -776,17 +776,13 @@ static inline int get_sigset_argpack(struct sigset_argpack *to,
{
// the path is hot enough for overhead of copy_from_user() to matter
if (from) {
- if (can_do_masked_user_access())
- from = masked_user_access_begin(from);
- else if (!user_read_access_begin(from, sizeof(*from)))
- return -EFAULT;
- unsafe_get_user(to->p, &from->p, Efault);
- unsafe_get_user(to->size, &from->size, Efault);
- user_read_access_end();
+ scoped_user_read_access(from, Efault) {
+ unsafe_get_user(to->p, &from->p, Efault);
+ unsafe_get_user(to->size, &from->size, Efault);
+ }
}
return 0;
Efault:
- user_read_access_end();
return -EFAULT;
}
diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
index 1fb71d2d31b5..7fdcaf9feb16 100644
--- a/fs/smb/client/cifs_debug.c
+++ b/fs/smb/client/cifs_debug.c
@@ -249,9 +249,9 @@ static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
seq_puts(m, "# Format:\n");
seq_puts(m, "# <tree id> <ses id> <persistent fid> <flags> <count> <pid> <uid>");
#ifdef CONFIG_CIFS_DEBUG2
- seq_puts(m, " <filename> <lease> <mid>\n");
+ seq_puts(m, " <filename> <lease> <lease-key> <mid>\n");
#else
- seq_puts(m, " <filename> <lease>\n");
+ seq_puts(m, " <filename> <lease> <lease-key>\n");
#endif /* CIFS_DEBUG2 */
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
@@ -274,6 +274,7 @@ static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
/* Append lease/oplock caching state as RHW letters */
inode = d_inode(cfile->dentry);
+ cinode = NULL;
n = 0;
if (inode) {
cinode = CIFS_I(inode);
@@ -291,6 +292,12 @@ static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
else
seq_puts(m, "NONE");
+ seq_puts(m, " ");
+ if (cinode && cinode->lease_granted)
+ seq_printf(m, "%pUl", cinode->lease_key);
+ else
+ seq_puts(m, "-");
+
#ifdef CONFIG_CIFS_DEBUG2
seq_printf(m, " %llu", cfile->fid.mid);
#endif /* CONFIG_CIFS_DEBUG2 */
@@ -317,7 +324,7 @@ static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v)
seq_puts(m, "# Version:1\n");
seq_puts(m, "# Format:\n");
- seq_puts(m, "# <tree id> <sess id> <persistent fid> <path>\n");
+ seq_puts(m, "# <tree id> <sess id> <persistent fid> <lease-key> <path>\n");
spin_lock(&cifs_tcp_ses_lock);
list_for_each(stmp, &cifs_tcp_ses_list) {
@@ -336,11 +343,15 @@ static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v)
(unsigned long)atomic_long_read(&cfids->total_dirents_entries),
(unsigned long long)atomic64_read(&cfids->total_dirents_bytes));
list_for_each_entry(cfid, &cfids->entries, entry) {
- seq_printf(m, "0x%x 0x%llx 0x%llx %s",
+ seq_printf(m, "0x%x 0x%llx 0x%llx ",
tcon->tid,
ses->Suid,
- cfid->fid.persistent_fid,
- cfid->path);
+ cfid->fid.persistent_fid);
+ if (cfid->has_lease)
+ seq_printf(m, "%pUl ", cfid->fid.lease_key);
+ else
+ seq_puts(m, "- ");
+ seq_printf(m, "%s", cfid->path);
if (cfid->file_all_info_is_valid)
seq_printf(m, "\tvalid file info");
if (cfid->dirents.is_valid)
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 203e2aaa3c25..0c76e0a31386 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -24,8 +24,9 @@
#include "cifsacl.h"
#include <crypto/internal/hash.h>
#include <uapi/linux/cifs/cifs_mount.h>
-#include "../common/cifsglob.h"
+#include "../common/smbglob.h"
#include "../common/smb2pdu.h"
+#include "../common/fscc.h"
#include "smb2pdu.h"
#include <linux/filelock.h>
@@ -633,28 +634,6 @@ struct smb_version_operations {
struct kvec *xattr_iov);
};
-struct smb_version_values {
- char *version_string;
- __u16 protocol_id;
- __u32 req_capabilities;
- __u32 large_lock_type;
- __u32 exclusive_lock_type;
- __u32 shared_lock_type;
- __u32 unlock_lock_type;
- size_t header_preamble_size;
- size_t header_size;
- size_t max_header_size;
- size_t read_rsp_size;
- __le16 lock_cmd;
- unsigned int cap_unix;
- unsigned int cap_nt_find;
- unsigned int cap_large_files;
- unsigned int cap_unicode;
- __u16 signing_enabled;
- __u16 signing_required;
- size_t create_lease_size;
-};
-
#define HEADER_SIZE(server) (server->vals->header_size)
#define MAX_HEADER_SIZE(server) (server->vals->max_header_size)
#define HEADER_PREAMBLE_SIZE(server) (server->vals->header_preamble_size)
@@ -692,12 +671,6 @@ struct cifs_mnt_data {
int flags;
};
-static inline unsigned int
-get_rfc1002_length(void *buf)
-{
- return be32_to_cpu(*((__be32 *)buf)) & 0xffffff;
-}
-
struct TCP_Server_Info {
struct list_head tcp_ses_list;
struct list_head smb_ses_list;
diff --git a/fs/smb/client/cifspdu.h b/fs/smb/client/cifspdu.h
index d9cf7db0ac35..49f35cb3cf2e 100644
--- a/fs/smb/client/cifspdu.h
+++ b/fs/smb/client/cifspdu.h
@@ -12,12 +12,14 @@
#include <net/sock.h>
#include <linux/unaligned.h>
#include "../common/smbfsctl.h"
+#include "../common/smb2pdu.h"
#define CIFS_PROT 0
#define POSIX_PROT (CIFS_PROT+1)
#define BAD_PROT 0xFFFF
/* SMB command codes:
+ * See MS-CIFS 2.2.2.1
* Note some commands have minimal (wct=0,bcc=0), or uninteresting, responses
* (ie which include no useful data other than the SMB error code itself).
* This can allow us to avoid response buffer allocations and copy in some cases
@@ -86,7 +88,6 @@
#define NT_TRANSACT_GET_USER_QUOTA 0x07
#define NT_TRANSACT_SET_USER_QUOTA 0x08
-#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */
/* future chained NTCreateXReadX bigger, but for time being NTCreateX biggest */
/* among the requests (NTCreateX response is bigger with wct of 34) */
#define MAX_CIFS_HDR_SIZE 0x58 /* 4 len + 32 hdr + (2*24 wct) + 2 bct + 2 pad */
@@ -152,6 +153,7 @@
/*
* SMB flag definitions
+ * See MS-CIFS 2.2.3.1
*/
#define SMBFLG_EXTD_LOCK 0x01 /* server supports lock-read write-unlock smb */
#define SMBFLG_RCV_POSTED 0x02 /* obsolete */
@@ -165,6 +167,8 @@
/*
* SMB flag2 definitions
+ * See MS-CIFS 2.2.3.1
+ * MS-SMB 2.2.3.1
*/
#define SMBFLG2_KNOWS_LONG_NAMES cpu_to_le16(1) /* can send long (non-8.3)
path names in response */
@@ -180,98 +184,7 @@
#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000)
#define SMBFLG2_UNICODE cpu_to_le16(0x8000)
-/*
- * These are the file access permission bits defined in CIFS for the
- * NTCreateAndX as well as the level 0x107
- * TRANS2_QUERY_PATH_INFORMATION API. The level 0x107, SMB_QUERY_FILE_ALL_INFO
- * responds with the AccessFlags.
- * The AccessFlags specifies the access permissions a caller has to the
- * file and can have any suitable combination of the following values:
- */
-
-#define FILE_READ_DATA 0x00000001 /* Data can be read from the file */
- /* or directory child entries can */
- /* be listed together with the */
- /* associated child attributes */
- /* (so the FILE_READ_ATTRIBUTES on */
- /* the child entry is not needed) */
-#define FILE_WRITE_DATA 0x00000002 /* Data can be written to the file */
- /* or new file can be created in */
- /* the directory */
-#define FILE_APPEND_DATA 0x00000004 /* Data can be appended to the file */
- /* (for non-local files over SMB it */
- /* is same as FILE_WRITE_DATA) */
- /* or new subdirectory can be */
- /* created in the directory */
-#define FILE_READ_EA 0x00000008 /* Extended attributes associated */
- /* with the file can be read */
-#define FILE_WRITE_EA 0x00000010 /* Extended attributes associated */
- /* with the file can be written */
-#define FILE_EXECUTE 0x00000020 /*Data can be read into memory from */
- /* the file using system paging I/O */
- /* for executing the file / script */
- /* or right to traverse directory */
- /* (but by default all users have */
- /* directory bypass traverse */
- /* privilege and do not need this */
- /* permission on directories at all)*/
-#define FILE_DELETE_CHILD 0x00000040 /* Child entry can be deleted from */
- /* the directory (so the DELETE on */
- /* the child entry is not needed) */
-#define FILE_READ_ATTRIBUTES 0x00000080 /* Attributes associated with the */
- /* file or directory can be read */
-#define FILE_WRITE_ATTRIBUTES 0x00000100 /* Attributes associated with the */
- /* file or directory can be written */
-#define DELETE 0x00010000 /* The file or dir can be deleted */
-#define READ_CONTROL 0x00020000 /* The discretionary access control */
- /* list and ownership associated */
- /* with the file or dir can be read */
-#define WRITE_DAC 0x00040000 /* The discretionary access control */
- /* list associated with the file or */
- /* directory can be written */
-#define WRITE_OWNER 0x00080000 /* Ownership information associated */
- /* with the file/dir can be written */
-#define SYNCHRONIZE 0x00100000 /* The file handle can waited on to */
- /* synchronize with the completion */
- /* of an input/output request */
-#define SYSTEM_SECURITY 0x01000000 /* The system access control list */
- /* associated with the file or */
- /* directory can be read or written */
- /* (cannot be in DACL, can in SACL) */
-#define MAXIMUM_ALLOWED 0x02000000 /* Maximal subset of GENERIC_ALL */
- /* permissions which can be granted */
- /* (cannot be in DACL nor SACL) */
-#define GENERIC_ALL 0x10000000 /* Same as: GENERIC_EXECUTE | */
- /* GENERIC_WRITE | */
- /* GENERIC_READ | */
- /* FILE_DELETE_CHILD | */
- /* DELETE | */
- /* WRITE_DAC | */
- /* WRITE_OWNER */
- /* So GENERIC_ALL contains all bits */
- /* mentioned above except these two */
- /* SYSTEM_SECURITY MAXIMUM_ALLOWED */
-#define GENERIC_EXECUTE 0x20000000 /* Same as: FILE_EXECUTE | */
- /* FILE_READ_ATTRIBUTES | */
- /* READ_CONTROL | */
- /* SYNCHRONIZE */
-#define GENERIC_WRITE 0x40000000 /* Same as: FILE_WRITE_DATA | */
- /* FILE_APPEND_DATA | */
- /* FILE_WRITE_EA | */
- /* FILE_WRITE_ATTRIBUTES | */
- /* READ_CONTROL | */
- /* SYNCHRONIZE */
-#define GENERIC_READ 0x80000000 /* Same as: FILE_READ_DATA | */
- /* FILE_READ_EA | */
- /* FILE_READ_ATTRIBUTES | */
- /* READ_CONTROL | */
- /* SYNCHRONIZE */
-
-#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES)
-#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \
- | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)
-#define FILE_EXEC_RIGHTS (FILE_EXECUTE)
-
+/* Combinations of file access permission bits */
#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_WRITE_EA \
| FILE_READ_ATTRIBUTES \
| FILE_WRITE_ATTRIBUTES \
@@ -283,15 +196,6 @@
| FILE_WRITE_ATTRIBUTES \
| DELETE | READ_CONTROL | WRITE_DAC \
| WRITE_OWNER | SYNCHRONIZE)
-#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \
- | FILE_READ_ATTRIBUTES \
- | FILE_WRITE_ATTRIBUTES \
- | DELETE | READ_CONTROL | WRITE_DAC \
- | WRITE_OWNER | SYNCHRONIZE)
-
-#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \
- | READ_CONTROL | SYNCHRONIZE)
-
/*
* Invalid readdir handle
@@ -325,29 +229,30 @@
/*
* File Attribute flags
*/
-#define ATTR_READONLY 0x0001
-#define ATTR_HIDDEN 0x0002
-#define ATTR_SYSTEM 0x0004
-#define ATTR_VOLUME 0x0008
-#define ATTR_DIRECTORY 0x0010
-#define ATTR_ARCHIVE 0x0020
-#define ATTR_DEVICE 0x0040
-#define ATTR_NORMAL 0x0080
-#define ATTR_TEMPORARY 0x0100
-#define ATTR_SPARSE 0x0200
-#define ATTR_REPARSE 0x0400
-#define ATTR_COMPRESSED 0x0800
-#define ATTR_OFFLINE 0x1000 /* ie file not immediately available -
- on offline storage */
-#define ATTR_NOT_CONTENT_INDEXED 0x2000
-#define ATTR_ENCRYPTED 0x4000
-#define ATTR_POSIX_SEMANTICS 0x01000000
-#define ATTR_BACKUP_SEMANTICS 0x02000000
-#define ATTR_DELETE_ON_CLOSE 0x04000000
-#define ATTR_SEQUENTIAL_SCAN 0x08000000
-#define ATTR_RANDOM_ACCESS 0x10000000
-#define ATTR_NO_BUFFERING 0x20000000
-#define ATTR_WRITE_THROUGH 0x80000000
+#define ATTR_READONLY 0x0001 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_HIDDEN 0x0002 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_SYSTEM 0x0004 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_VOLUME 0x0008
+#define ATTR_DIRECTORY 0x0010 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_ARCHIVE 0x0020 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_DEVICE 0x0040
+#define ATTR_NORMAL 0x0080 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_TEMPORARY 0x0100 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_SPARSE 0x0200 /* See MS-SMB 2.2.1.2.1 */
+#define ATTR_REPARSE_POINT 0x0400 /* See MS-SMB 2.2.1.2.1 */
+#define ATTR_COMPRESSED 0x0800 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_OFFLINE 0x1000 /* See MS-SMB 2.2.1.2.1
+ ie file not immediately available -
+ on offline storage */
+#define ATTR_NOT_CONTENT_INDEXED 0x2000 /* See MS-SMB 2.2.1.2.1 */
+#define ATTR_ENCRYPTED 0x4000 /* See MS-SMB 2.2.1.2.1 */
+#define ATTR_POSIX_SEMANTICS 0x0100000 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_BACKUP_SEMANTICS 0x0200000 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_DELETE_ON_CLOSE 0x0400000 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_SEQUENTIAL_SCAN 0x0800000 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_RANDOM_ACCESS 0x1000000 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_NO_BUFFERING 0x2000000 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_WRITE_THROUGH 0x8000000 /* See MS-CIFS 2.2.1.2.3 */
/* ShareAccess flags */
#define FILE_NO_SHARE 0x00000000
@@ -417,38 +322,6 @@
#define GETU16(var) (*((__u16 *)var)) /* BB check for endian issues */
#define GETU32(var) (*((__u32 *)var)) /* BB check for endian issues */
-struct smb_hdr {
- __be32 smb_buf_length; /* BB length is only two (rarely three) bytes,
- with one or two byte "type" preceding it that will be
- zero - we could mask the type byte off */
- __u8 Protocol[4];
- __u8 Command;
- union {
- struct {
- __u8 ErrorClass;
- __u8 Reserved;
- __le16 Error;
- } __attribute__((packed)) DosError;
- __le32 CifsError;
- } __attribute__((packed)) Status;
- __u8 Flags;
- __le16 Flags2; /* note: le */
- __le16 PidHigh;
- union {
- struct {
- __le32 SequenceNumber; /* le */
- __u32 Reserved; /* zero */
- } __attribute__((packed)) Sequence;
- __u8 SecuritySignature[8]; /* le */
- } __attribute__((packed)) Signature;
- __u8 pad[2];
- __u16 Tid;
- __le16 Pid;
- __u16 Uid;
- __le16 Mid;
- __u8 WordCount;
-} __attribute__((packed));
-
/* given a pointer to an smb_hdr, retrieve a void pointer to the ByteCount */
static inline void *
BCC(struct smb_hdr *smb)
@@ -520,19 +393,15 @@ put_bcc(__u16 count, struct smb_hdr *hdr)
*
*/
-typedef struct negotiate_req {
- struct smb_hdr hdr; /* wct = 0 */
- __le16 ByteCount;
- unsigned char DialectsArray[];
-} __attribute__((packed)) NEGOTIATE_REQ;
-
#define MIN_TZ_ADJ (15 * 60) /* minimum grid for timezones in seconds */
#define READ_RAW_ENABLE 1
#define WRITE_RAW_ENABLE 2
#define RAW_ENABLE (READ_RAW_ENABLE | WRITE_RAW_ENABLE)
#define SMB1_CLIENT_GUID_SIZE (16)
-typedef struct negotiate_rsp {
+
+/* See MS-CIFS 2.2.4.52.2 */
+typedef struct smb_negotiate_rsp {
struct smb_hdr hdr; /* wct = 17 */
__le16 DialectIndex; /* 0xFFFF = no dialect acceptable */
__u8 SecurityMode;
@@ -556,9 +425,9 @@ typedef struct negotiate_rsp {
struct {
unsigned char GUID[SMB1_CLIENT_GUID_SIZE];
unsigned char SecurityBlob[];
- } __attribute__((packed)) extended_response;
- } __attribute__((packed)) u;
-} __attribute__((packed)) NEGOTIATE_RSP;
+ } __packed extended_response;
+ } __packed u;
+} __packed SMB_NEGOTIATE_RSP;
/* SecurityMode bits */
#define SECMODE_USER 0x01 /* off indicates share level security */
@@ -605,7 +474,7 @@ typedef union smb_com_session_setup_andx {
unsigned char SecurityBlob[]; /* followed by */
/* STRING NativeOS */
/* STRING NativeLanMan */
- } __attribute__((packed)) req; /* NTLM request format (with
+ } __packed req; /* NTLM request format (with
extended security */
struct { /* request format */
@@ -628,7 +497,7 @@ typedef union smb_com_session_setup_andx {
/* STRING PrimaryDomain */
/* STRING NativeOS */
/* STRING NativeLanMan */
- } __attribute__((packed)) req_no_secext; /* NTLM request format (without
+ } __packed req_no_secext; /* NTLM request format (without
extended security */
struct { /* default (NTLM) response format */
@@ -643,7 +512,7 @@ typedef union smb_com_session_setup_andx {
/* unsigned char * NativeOS; */
/* unsigned char * NativeLanMan; */
/* unsigned char * PrimaryDomain; */
- } __attribute__((packed)) resp; /* NTLM response
+ } __packed resp; /* NTLM response
(with or without extended sec) */
struct { /* request format */
@@ -663,7 +532,7 @@ typedef union smb_com_session_setup_andx {
/* STRING PrimaryDomain */
/* STRING NativeOS */
/* STRING NativeLanMan */
- } __attribute__((packed)) old_req; /* pre-NTLM (LANMAN2.1) req format */
+ } __packed old_req; /* pre-NTLM (LANMAN2.1) req format */
struct { /* default (NTLM) response format */
struct smb_hdr hdr; /* wct = 3 */
@@ -675,8 +544,8 @@ typedef union smb_com_session_setup_andx {
unsigned char NativeOS[]; /* followed by */
/* unsigned char * NativeLanMan; */
/* unsigned char * PrimaryDomain; */
- } __attribute__((packed)) old_resp; /* pre-NTLM (LANMAN2.1) response */
-} __attribute__((packed)) SESSION_SETUP_ANDX;
+ } __packed old_resp; /* pre-NTLM (LANMAN2.1) response */
+} __packed SESSION_SETUP_ANDX;
/* format of NLTMv2 Response ie "case sensitive password" hash when NTLMv2 */
@@ -690,7 +559,7 @@ struct ntlmssp2_name {
__le16 type;
__le16 length;
__u8 data[];
-} __attribute__((packed));
+} __packed;
struct ntlmv2_resp {
union {
@@ -698,20 +567,25 @@ struct ntlmv2_resp {
struct {
__u8 reserved[8];
__u8 key[CIFS_SERVER_CHALLENGE_SIZE];
- } __attribute__((packed)) challenge;
- } __attribute__((packed));
+ } __packed challenge;
+ } __packed;
__le32 blob_signature;
__u32 reserved;
__le64 time;
__u64 client_chal; /* random */
__u32 reserved2;
/* array of name entries could follow ending in minimum 4 byte struct */
-} __attribute__((packed));
+} __packed;
#define CIFS_NETWORK_OPSYS "CIFS VFS Client for Linux"
-/* Capabilities bits (for NTLM SessSetup request) */
+
+/*
+ * Capabilities bits (for NTLM SessSetup request)
+ * See MS-CIFS 2.2.4.52.2
+ * MS-SMB 2.2.4.5.2.1
+ */
#define CAP_UNICODE 0x00000004
#define CAP_LARGE_FILES 0x00000008
#define CAP_NT_SMBS 0x00000010
@@ -719,7 +593,7 @@ struct ntlmv2_resp {
#define CAP_LEVEL_II_OPLOCKS 0x00000080
#define CAP_NT_FIND 0x00000200 /* reserved should be zero
(because NT_SMBs implies the same thing?) */
-#define CAP_BULK_TRANSFER 0x20000000
+#define CAP_BULK_TRANSFER 0x00000400
#define CAP_EXTENDED_SECURITY 0x80000000
/* Action bits */
@@ -736,7 +610,7 @@ typedef struct smb_com_tconx_req {
unsigned char Password[]; /* followed by */
/* STRING Path *//* \\server\share name */
/* STRING Service */
-} __attribute__((packed)) TCONX_REQ;
+} __packed TCONX_REQ;
typedef struct smb_com_tconx_rsp {
struct smb_hdr hdr; /* wct = 3 , not extended response */
@@ -747,7 +621,7 @@ typedef struct smb_com_tconx_rsp {
__u16 ByteCount;
unsigned char Service[]; /* always ASCII, not Unicode */
/* STRING NativeFileSystem */
-} __attribute__((packed)) TCONX_RSP;
+} __packed TCONX_RSP;
typedef struct smb_com_tconx_rsp_ext {
struct smb_hdr hdr; /* wct = 7, extended response */
@@ -760,7 +634,7 @@ typedef struct smb_com_tconx_rsp_ext {
__u16 ByteCount;
unsigned char Service[]; /* always ASCII, not Unicode */
/* STRING NativeFileSystem */
-} __attribute__((packed)) TCONX_RSP_EXT;
+} __packed TCONX_RSP_EXT;
/* tree connect Flags */
@@ -796,14 +670,14 @@ typedef struct smb_com_echo_req {
__le16 EchoCount;
__le16 ByteCount;
char Data[];
-} __attribute__((packed)) ECHO_REQ;
+} __packed ECHO_REQ;
typedef struct smb_com_echo_rsp {
struct smb_hdr hdr;
__le16 SequenceNumber;
__le16 ByteCount;
char Data[];
-} __attribute__((packed)) ECHO_RSP;
+} __packed ECHO_RSP;
typedef struct smb_com_logoff_andx_req {
struct smb_hdr hdr; /* wct = 2 */
@@ -811,7 +685,7 @@ typedef struct smb_com_logoff_andx_req {
__u8 AndXReserved;
__u16 AndXOffset;
__u16 ByteCount;
-} __attribute__((packed)) LOGOFF_ANDX_REQ;
+} __packed LOGOFF_ANDX_REQ;
typedef struct smb_com_logoff_andx_rsp {
struct smb_hdr hdr; /* wct = 2 */
@@ -819,7 +693,7 @@ typedef struct smb_com_logoff_andx_rsp {
__u8 AndXReserved;
__u16 AndXOffset;
__u16 ByteCount;
-} __attribute__((packed)) LOGOFF_ANDX_RSP;
+} __packed LOGOFF_ANDX_RSP;
typedef union smb_com_tree_disconnect { /* as an alternative can use flag on
tree_connect PDU to effect disconnect */
@@ -827,36 +701,36 @@ typedef union smb_com_tree_disconnect { /* as an alternative can use flag on
struct {
struct smb_hdr hdr; /* wct = 0 */
__u16 ByteCount; /* bcc = 0 */
- } __attribute__((packed)) req;
+ } __packed req;
struct {
struct smb_hdr hdr; /* wct = 0 */
__u16 ByteCount; /* bcc = 0 */
- } __attribute__((packed)) resp;
-} __attribute__((packed)) TREE_DISCONNECT;
+ } __packed resp;
+} __packed TREE_DISCONNECT;
typedef struct smb_com_close_req {
struct smb_hdr hdr; /* wct = 3 */
__u16 FileID;
__u32 LastWriteTime; /* should be zero or -1 */
__u16 ByteCount; /* 0 */
-} __attribute__((packed)) CLOSE_REQ;
+} __packed CLOSE_REQ;
typedef struct smb_com_close_rsp {
struct smb_hdr hdr; /* wct = 0 */
__u16 ByteCount; /* bct = 0 */
-} __attribute__((packed)) CLOSE_RSP;
+} __packed CLOSE_RSP;
typedef struct smb_com_flush_req {
struct smb_hdr hdr; /* wct = 1 */
__u16 FileID;
__u16 ByteCount; /* 0 */
-} __attribute__((packed)) FLUSH_REQ;
+} __packed FLUSH_REQ;
typedef struct smb_com_findclose_req {
struct smb_hdr hdr; /* wct = 1 */
__u16 FileID;
__u16 ByteCount; /* 0 */
-} __attribute__((packed)) FINDCLOSE_REQ;
+} __packed FINDCLOSE_REQ;
/* OpenFlags */
#define REQ_MORE_INFO 0x00000001 /* legacy (OPEN_AND_X) only */
@@ -903,7 +777,7 @@ typedef struct smb_com_open_req { /* also handles create */
__u8 SecurityFlags;
__le16 ByteCount;
char fileName[];
-} __attribute__((packed)) OPEN_REQ;
+} __packed OPEN_REQ;
/* open response: oplock levels */
#define OPLOCK_NONE 0
@@ -935,7 +809,7 @@ typedef struct smb_com_open_rsp {
__le16 DeviceState;
__u8 DirectoryFlag;
__u16 ByteCount; /* bct = 0 */
-} __attribute__((packed)) OPEN_RSP;
+} __packed OPEN_RSP;
typedef struct smb_com_open_rsp_ext {
struct smb_hdr hdr; /* wct = 42 but meaningless due to MS bug? */
@@ -960,7 +834,7 @@ typedef struct smb_com_open_rsp_ext {
__le32 MaximalAccessRights;
__le32 GuestMaximalAccessRights;
__u16 ByteCount; /* bct = 0 */
-} __attribute__((packed)) OPEN_RSP_EXT;
+} __packed OPEN_RSP_EXT;
/* format of legacy open request */
@@ -980,7 +854,7 @@ typedef struct smb_com_openx_req {
__le32 Reserved;
__le16 ByteCount; /* file name follows */
char fileName[];
-} __attribute__((packed)) OPENX_REQ;
+} __packed OPENX_REQ;
typedef struct smb_com_openx_rsp {
struct smb_hdr hdr; /* wct = 15 */
@@ -998,7 +872,7 @@ typedef struct smb_com_openx_rsp {
__u32 FileId;
__u16 Reserved;
__u16 ByteCount;
-} __attribute__((packed)) OPENX_RSP;
+} __packed OPENX_RSP;
/* For encoding of POSIX Open Request - see trans2 function 0x209 data struct */
@@ -1020,7 +894,7 @@ typedef struct smb_com_writex_req {
__u8 Pad; /* BB check for whether padded to DWORD
boundary and optimum performance here */
char Data[];
-} __attribute__((packed)) WRITEX_REQ;
+} __packed WRITEX_REQ;
typedef struct smb_com_write_req {
struct smb_hdr hdr; /* wct = 14 */
@@ -1040,7 +914,7 @@ typedef struct smb_com_write_req {
__u8 Pad; /* BB check for whether padded to DWORD
boundary and optimum performance here */
char Data[];
-} __attribute__((packed)) WRITE_REQ;
+} __packed WRITE_REQ;
typedef struct smb_com_write_rsp {
struct smb_hdr hdr; /* wct = 6 */
@@ -1052,7 +926,7 @@ typedef struct smb_com_write_rsp {
__le16 CountHigh;
__u16 Reserved;
__u16 ByteCount;
-} __attribute__((packed)) WRITE_RSP;
+} __packed WRITE_RSP;
/* legacy read request for older servers */
typedef struct smb_com_readx_req {
@@ -1067,7 +941,7 @@ typedef struct smb_com_readx_req {
__le32 Reserved;
__le16 Remaining;
__le16 ByteCount;
-} __attribute__((packed)) READX_REQ;
+} __packed READX_REQ;
typedef struct smb_com_read_req {
struct smb_hdr hdr; /* wct = 12 */
@@ -1082,7 +956,7 @@ typedef struct smb_com_read_req {
__le16 Remaining;
__le32 OffsetHigh;
__le16 ByteCount;
-} __attribute__((packed)) READ_REQ;
+} __packed READ_REQ;
typedef struct smb_com_read_rsp {
struct smb_hdr hdr; /* wct = 12 */
@@ -1098,7 +972,7 @@ typedef struct smb_com_read_rsp {
__u64 Reserved2;
__u16 ByteCount;
/* read response data immediately follows */
-} __attribute__((packed)) READ_RSP;
+} __packed READ_RSP;
typedef struct locking_andx_range {
__le16 Pid;
@@ -1107,7 +981,7 @@ typedef struct locking_andx_range {
__le32 OffsetLow;
__le32 LengthHigh;
__le32 LengthLow;
-} __attribute__((packed)) LOCKING_ANDX_RANGE;
+} __packed LOCKING_ANDX_RANGE;
#define LOCKING_ANDX_SHARED_LOCK 0x01
#define LOCKING_ANDX_OPLOCK_RELEASE 0x02
@@ -1128,7 +1002,7 @@ typedef struct smb_com_lock_req {
__le16 NumberOfLocks;
__le16 ByteCount;
LOCKING_ANDX_RANGE Locks[];
-} __attribute__((packed)) LOCK_REQ;
+} __packed LOCK_REQ;
/* lock type */
#define CIFS_RDLCK 0
@@ -1141,7 +1015,7 @@ typedef struct cifs_posix_lock {
__le64 start;
__le64 length;
/* BB what about additional owner info to identify network client */
-} __attribute__((packed)) CIFS_POSIX_LOCK;
+} __packed CIFS_POSIX_LOCK;
typedef struct smb_com_lock_rsp {
struct smb_hdr hdr; /* wct = 2 */
@@ -1149,7 +1023,7 @@ typedef struct smb_com_lock_rsp {
__u8 AndXReserved;
__le16 AndXOffset;
__u16 ByteCount;
-} __attribute__((packed)) LOCK_RSP;
+} __packed LOCK_RSP;
typedef struct smb_com_rename_req {
struct smb_hdr hdr; /* wct = 1 */
@@ -1159,7 +1033,7 @@ typedef struct smb_com_rename_req {
unsigned char OldFileName[];
/* followed by __u8 BufferFormat2 */
/* followed by NewFileName */
-} __attribute__((packed)) RENAME_REQ;
+} __packed RENAME_REQ;
/* copy request flags */
#define COPY_MUST_BE_FILE 0x0001
@@ -1179,7 +1053,7 @@ typedef struct smb_com_copy_req {
unsigned char OldFileName[];
/* followed by __u8 BufferFormat2 */
/* followed by NewFileName string */
-} __attribute__((packed)) COPY_REQ;
+} __packed COPY_REQ;
typedef struct smb_com_copy_rsp {
struct smb_hdr hdr; /* wct = 1 */
@@ -1187,7 +1061,7 @@ typedef struct smb_com_copy_rsp {
__u16 ByteCount; /* may be zero */
__u8 BufferFormat; /* 0x04 - only present if errored file follows */
unsigned char ErrorFileName[]; /* only present if error in copy */
-} __attribute__((packed)) COPY_RSP;
+} __packed COPY_RSP;
#define CREATE_HARD_LINK 0x103
#define MOVEFILE_COPY_ALLOWED 0x0002
@@ -1203,12 +1077,12 @@ typedef struct smb_com_nt_rename_req { /* A5 - also used for create hardlink */
unsigned char OldFileName[];
/* followed by __u8 BufferFormat2 */
/* followed by NewFileName */
-} __attribute__((packed)) NT_RENAME_REQ;
+} __packed NT_RENAME_REQ;
typedef struct smb_com_rename_rsp {
struct smb_hdr hdr; /* wct = 0 */
__u16 ByteCount; /* bct = 0 */
-} __attribute__((packed)) RENAME_RSP;
+} __packed RENAME_RSP;
typedef struct smb_com_delete_file_req {
struct smb_hdr hdr; /* wct = 1 */
@@ -1216,43 +1090,43 @@ typedef struct smb_com_delete_file_req {
__le16 ByteCount;
__u8 BufferFormat; /* 4 = ASCII */
unsigned char fileName[];
-} __attribute__((packed)) DELETE_FILE_REQ;
+} __packed DELETE_FILE_REQ;
typedef struct smb_com_delete_file_rsp {
struct smb_hdr hdr; /* wct = 0 */
__u16 ByteCount; /* bct = 0 */
-} __attribute__((packed)) DELETE_FILE_RSP;
+} __packed DELETE_FILE_RSP;
typedef struct smb_com_delete_directory_req {
struct smb_hdr hdr; /* wct = 0 */
__le16 ByteCount;
__u8 BufferFormat; /* 4 = ASCII */
unsigned char DirName[];
-} __attribute__((packed)) DELETE_DIRECTORY_REQ;
+} __packed DELETE_DIRECTORY_REQ;
typedef struct smb_com_delete_directory_rsp {
struct smb_hdr hdr; /* wct = 0 */
__u16 ByteCount; /* bct = 0 */
-} __attribute__((packed)) DELETE_DIRECTORY_RSP;
+} __packed DELETE_DIRECTORY_RSP;
typedef struct smb_com_create_directory_req {
struct smb_hdr hdr; /* wct = 0 */
__le16 ByteCount;
__u8 BufferFormat; /* 4 = ASCII */
unsigned char DirName[];
-} __attribute__((packed)) CREATE_DIRECTORY_REQ;
+} __packed CREATE_DIRECTORY_REQ;
typedef struct smb_com_create_directory_rsp {
struct smb_hdr hdr; /* wct = 0 */
__u16 ByteCount; /* bct = 0 */
-} __attribute__((packed)) CREATE_DIRECTORY_RSP;
+} __packed CREATE_DIRECTORY_RSP;
typedef struct smb_com_query_information_req {
struct smb_hdr hdr; /* wct = 0 */
__le16 ByteCount; /* 1 + namelen + 1 */
__u8 BufferFormat; /* 4 = ASCII */
unsigned char FileName[];
-} __attribute__((packed)) QUERY_INFORMATION_REQ;
+} __packed QUERY_INFORMATION_REQ;
typedef struct smb_com_query_information_rsp {
struct smb_hdr hdr; /* wct = 10 */
@@ -1261,7 +1135,7 @@ typedef struct smb_com_query_information_rsp {
__le32 size;
__u16 reserved[5];
__le16 ByteCount; /* bcc = 0 */
-} __attribute__((packed)) QUERY_INFORMATION_RSP;
+} __packed QUERY_INFORMATION_RSP;
typedef struct smb_com_setattr_req {
struct smb_hdr hdr; /* wct = 8 */
@@ -1271,12 +1145,12 @@ typedef struct smb_com_setattr_req {
__le16 ByteCount;
__u8 BufferFormat; /* 4 = ASCII */
unsigned char fileName[];
-} __attribute__((packed)) SETATTR_REQ;
+} __packed SETATTR_REQ;
typedef struct smb_com_setattr_rsp {
struct smb_hdr hdr; /* wct = 0 */
__u16 ByteCount; /* bct = 0 */
-} __attribute__((packed)) SETATTR_RSP;
+} __packed SETATTR_RSP;
/* empty wct response to setattr */
@@ -1304,7 +1178,7 @@ typedef struct smb_com_ntransact_req {
__le16 ByteCount;
__u8 Pad[3];
__u8 Parms[];
-} __attribute__((packed)) NTRANSACT_REQ;
+} __packed NTRANSACT_REQ;
typedef struct smb_com_ntransact_rsp {
struct smb_hdr hdr; /* wct = 18 */
@@ -1321,15 +1195,7 @@ typedef struct smb_com_ntransact_rsp {
__u16 ByteCount;
/* __u8 Pad[3]; */
/* parms and data follow */
-} __attribute__((packed)) NTRANSACT_RSP;
-
-/* See MS-SMB 2.2.7.2.1.1 */
-struct srv_copychunk {
- __le64 SourceOffset;
- __le64 DestinationOffset;
- __le32 CopyLength;
- __u32 Reserved;
-} __packed;
+} __packed NTRANSACT_RSP;
typedef struct smb_com_transaction_ioctl_req {
struct smb_hdr hdr; /* wct = 23 */
@@ -1353,7 +1219,7 @@ typedef struct smb_com_transaction_ioctl_req {
__le16 ByteCount;
__u8 Pad[3];
__u8 Data[];
-} __attribute__((packed)) TRANSACT_IOCTL_REQ;
+} __packed TRANSACT_IOCTL_REQ;
typedef struct smb_com_transaction_compr_ioctl_req {
struct smb_hdr hdr; /* wct = 23 */
@@ -1377,7 +1243,7 @@ typedef struct smb_com_transaction_compr_ioctl_req {
__le16 ByteCount;
__u8 Pad[3];
__le16 compression_state; /* See below for valid flags */
-} __attribute__((packed)) TRANSACT_COMPR_IOCTL_REQ;
+} __packed TRANSACT_COMPR_IOCTL_REQ;
/* compression state flags */
#define COMPRESSION_FORMAT_NONE 0x0000
@@ -1398,7 +1264,7 @@ typedef struct smb_com_transaction_ioctl_rsp {
__u8 SetupCount; /* 1 */
__le16 ReturnedDataLen;
__le16 ByteCount;
-} __attribute__((packed)) TRANSACT_IOCTL_RSP;
+} __packed TRANSACT_IOCTL_RSP;
#define CIFS_ACL_OWNER 1
#define CIFS_ACL_GROUP 2
@@ -1425,7 +1291,7 @@ typedef struct smb_com_transaction_qsec_req {
__u16 Fid;
__u16 Reserved2;
__le32 AclFlags;
-} __attribute__((packed)) QUERY_SEC_DESC_REQ;
+} __packed QUERY_SEC_DESC_REQ;
typedef struct smb_com_transaction_ssec_req {
@@ -1448,7 +1314,7 @@ typedef struct smb_com_transaction_ssec_req {
__u16 Fid;
__u16 Reserved2;
__le32 AclFlags;
-} __attribute__((packed)) SET_SEC_DESC_REQ;
+} __packed SET_SEC_DESC_REQ;
typedef struct smb_com_transaction_change_notify_req {
struct smb_hdr hdr; /* wct = 23 */
@@ -1472,7 +1338,7 @@ typedef struct smb_com_transaction_change_notify_req {
__le16 ByteCount;
/* __u8 Pad[3];*/
/* __u8 Data[];*/
-} __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_REQ;
+} __packed TRANSACT_CHANGE_NOTIFY_REQ;
/* BB eventually change to use generic ntransact rsp struct
and validation routine */
@@ -1490,7 +1356,7 @@ typedef struct smb_com_transaction_change_notify_rsp {
__u8 SetupCount; /* 0 */
__u16 ByteCount;
/* __u8 Pad[3]; */
-} __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_RSP;
+} __packed TRANSACT_CHANGE_NOTIFY_RSP;
/* Completion Filter flags for Notify */
#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001
#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002
@@ -1521,7 +1387,7 @@ struct file_notify_information {
__le32 Action;
__le32 FileNameLength;
__u8 FileName[];
-} __attribute__((packed));
+} __packed;
struct cifs_quota_data {
__u32 rsrvd1; /* 0 */
@@ -1531,7 +1397,7 @@ struct cifs_quota_data {
__u64 soft_limit;
__u64 hard_limit;
char sid[]; /* variable size? */
-} __attribute__((packed));
+} __packed;
/* quota sub commands */
#define QUOTA_LIST_CONTINUE 0
@@ -1557,12 +1423,12 @@ struct trans2_req {
__u8 Reserved3;
__le16 SubCommand; /* 1st setup word - SetupCount words follow */
__le16 ByteCount;
-} __attribute__((packed));
+} __packed;
struct smb_t2_req {
struct smb_hdr hdr;
struct trans2_req t2_req;
-} __attribute__((packed));
+} __packed;
struct trans2_resp {
/* struct smb_hdr hdr precedes. Note wct = 10 + setup count */
@@ -1581,12 +1447,12 @@ struct trans2_resp {
__u16 ByteCount;
__u16 Reserved2;*/
/* data area follows */
-} __attribute__((packed));
+} __packed;
struct smb_t2_rsp {
struct smb_hdr hdr;
struct trans2_resp t2_rsp;
-} __attribute__((packed));
+} __packed;
/* PathInfo/FileInfo infolevels */
#define SMB_INFO_STANDARD 1
@@ -1683,14 +1549,14 @@ typedef struct smb_com_transaction2_qpi_req {
__le16 InformationLevel;
__u32 Reserved4;
char FileName[];
-} __attribute__((packed)) TRANSACTION2_QPI_REQ;
+} __packed TRANSACTION2_QPI_REQ;
typedef struct smb_com_transaction2_qpi_rsp {
struct smb_hdr hdr; /* wct = 10 + SetupCount */
struct trans2_resp t2;
__u16 ByteCount;
__u16 Reserved2; /* parameter word is present for infolevels > 100 */
-} __attribute__((packed)) TRANSACTION2_QPI_RSP;
+} __packed TRANSACTION2_QPI_RSP;
typedef struct smb_com_transaction2_spi_req {
struct smb_hdr hdr; /* wct = 15 */
@@ -1716,21 +1582,21 @@ typedef struct smb_com_transaction2_spi_req {
__le16 InformationLevel;
__u32 Reserved4;
char FileName[];
-} __attribute__((packed)) TRANSACTION2_SPI_REQ;
+} __packed TRANSACTION2_SPI_REQ;
typedef struct smb_com_transaction2_spi_rsp {
struct smb_hdr hdr; /* wct = 10 + SetupCount */
struct trans2_resp t2;
__u16 ByteCount;
__u16 Reserved2; /* parameter word is present for infolevels > 100 */
-} __attribute__((packed)) TRANSACTION2_SPI_RSP;
+} __packed TRANSACTION2_SPI_RSP;
struct set_file_rename {
__le32 overwrite; /* 1 = overwrite dest */
__u32 root_fid; /* zero */
__le32 target_name_len;
char target_name[]; /* Must be unicode */
-} __attribute__((packed));
+} __packed;
struct smb_com_transaction2_sfi_req {
struct smb_hdr hdr; /* wct = 15 */
@@ -1757,14 +1623,14 @@ struct smb_com_transaction2_sfi_req {
__le16 InformationLevel;
__u16 Reserved4;
__u8 payload[];
-} __attribute__((packed));
+} __packed;
struct smb_com_transaction2_sfi_rsp {
struct smb_hdr hdr; /* wct = 10 + SetupCount */
struct trans2_resp t2;
__u16 ByteCount;
__u16 Reserved2; /* parameter word reserved - present for infolevels > 100 */
-} __attribute__((packed));
+} __packed;
struct smb_t2_qfi_req {
struct smb_hdr hdr;
@@ -1772,14 +1638,14 @@ struct smb_t2_qfi_req {
__u8 Pad;
__u16 Fid;
__le16 InformationLevel;
-} __attribute__((packed));
+} __packed;
struct smb_t2_qfi_rsp {
struct smb_hdr hdr; /* wct = 10 + SetupCount */
struct trans2_resp t2;
__u16 ByteCount;
__u16 Reserved2; /* parameter word reserved - present for infolevels > 100 */
-} __attribute__((packed));
+} __packed;
/*
* Flags on T2 FINDFIRST and FINDNEXT
@@ -1821,13 +1687,13 @@ typedef struct smb_com_transaction2_ffirst_req {
__le16 InformationLevel;
__le32 SearchStorageType;
char FileName[];
-} __attribute__((packed)) TRANSACTION2_FFIRST_REQ;
+} __packed TRANSACTION2_FFIRST_REQ;
typedef struct smb_com_transaction2_ffirst_rsp {
struct smb_hdr hdr; /* wct = 10 */
struct trans2_resp t2;
__u16 ByteCount;
-} __attribute__((packed)) TRANSACTION2_FFIRST_RSP;
+} __packed TRANSACTION2_FFIRST_RSP;
typedef struct smb_com_transaction2_ffirst_rsp_parms {
__u16 SearchHandle;
@@ -1835,7 +1701,7 @@ typedef struct smb_com_transaction2_ffirst_rsp_parms {
__le16 EndofSearch;
__le16 EAErrorOffset;
__le16 LastNameOffset;
-} __attribute__((packed)) T2_FFIRST_RSP_PARMS;
+} __packed T2_FFIRST_RSP_PARMS;
typedef struct smb_com_transaction2_fnext_req {
struct smb_hdr hdr; /* wct = 15 */
@@ -1863,20 +1729,20 @@ typedef struct smb_com_transaction2_fnext_req {
__u32 ResumeKey;
__le16 SearchFlags;
char ResumeFileName[];
-} __attribute__((packed)) TRANSACTION2_FNEXT_REQ;
+} __packed TRANSACTION2_FNEXT_REQ;
typedef struct smb_com_transaction2_fnext_rsp {
struct smb_hdr hdr; /* wct = 10 */
struct trans2_resp t2;
__u16 ByteCount;
-} __attribute__((packed)) TRANSACTION2_FNEXT_RSP;
+} __packed TRANSACTION2_FNEXT_RSP;
typedef struct smb_com_transaction2_fnext_rsp_parms {
__le16 SearchCount;
__le16 EndofSearch;
__le16 EAErrorOffset;
__le16 LastNameOffset;
-} __attribute__((packed)) T2_FNEXT_RSP_PARMS;
+} __packed T2_FNEXT_RSP_PARMS;
/* QFSInfo Levels */
#define SMB_INFO_ALLOCATION 1
@@ -1920,14 +1786,14 @@ typedef struct smb_com_transaction2_qfsi_req {
__le16 ByteCount;
__u8 Pad;
__le16 InformationLevel;
-} __attribute__((packed)) TRANSACTION2_QFSI_REQ;
+} __packed TRANSACTION2_QFSI_REQ;
typedef struct smb_com_transaction_qfsi_rsp {
struct smb_hdr hdr; /* wct = 10 + SetupCount */
struct trans2_resp t2;
__u16 ByteCount;
__u8 Pad; /* may be three bytes? *//* followed by data area */
-} __attribute__((packed)) TRANSACTION2_QFSI_RSP;
+} __packed TRANSACTION2_QFSI_RSP;
typedef struct whoami_rsp_data { /* Query level 0x202 */
__u32 flags; /* 0 = Authenticated user 1 = GUEST */
@@ -1940,7 +1806,7 @@ typedef struct whoami_rsp_data { /* Query level 0x202 */
__u32 pad; /* reserved - MBZ */
/* __u64 gid_array[0]; */ /* may be empty */
/* __u8 * psid_list */ /* may be empty */
-} __attribute__((packed)) WHOAMI_RSP_DATA;
+} __packed WHOAMI_RSP_DATA;
/* SETFSInfo Levels */
#define SMB_SET_CIFS_UNIX_INFO 0x200
@@ -1973,7 +1839,7 @@ typedef struct smb_com_transaction2_setfsi_req {
__le16 ClientUnixMajor; /* Data start. */
__le16 ClientUnixMinor;
__le64 ClientUnixCap; /* Data end */
-} __attribute__((packed)) TRANSACTION2_SETFSI_REQ;
+} __packed TRANSACTION2_SETFSI_REQ;
/* level 0x203 request structure follows */
typedef struct smb_com_transaction2_setfs_enc_req {
@@ -1999,14 +1865,14 @@ typedef struct smb_com_transaction2_setfs_enc_req {
__u16 Reserved4; /* Parameters start. */
__le16 InformationLevel;/* Parameters end. */
/* NTLMSSP Blob, Data start. */
-} __attribute__((packed)) TRANSACTION2_SETFSI_ENC_REQ;
+} __packed TRANSACTION2_SETFSI_ENC_REQ;
/* response for setfsinfo levels 0x200 and 0x203 */
typedef struct smb_com_transaction2_setfsi_rsp {
struct smb_hdr hdr; /* wct = 10 */
struct trans2_resp t2;
__u16 ByteCount;
-} __attribute__((packed)) TRANSACTION2_SETFSI_RSP;
+} __packed TRANSACTION2_SETFSI_RSP;
typedef struct smb_com_transaction2_get_dfs_refer_req {
struct smb_hdr hdr; /* wct = 15 */
@@ -2032,7 +1898,7 @@ typedef struct smb_com_transaction2_get_dfs_refer_req {
seem to matter though */
__le16 MaxReferralLevel;
char RequestFileName[];
-} __attribute__((packed)) TRANSACTION2_GET_DFS_REFER_REQ;
+} __packed TRANSACTION2_GET_DFS_REFER_REQ;
#define DFS_VERSION cpu_to_le16(0x0003)
@@ -2054,7 +1920,7 @@ typedef struct dfs_referral_level_3 { /* version 4 is same, + one flag bit */
__le16 DfsAlternatePathOffset;
__le16 NetworkAddressOffset; /* offset of the link target */
__u8 ServiceSiteGuid[16]; /* MBZ, ignored */
-} __attribute__((packed)) REFERRAL3;
+} __packed REFERRAL3;
struct get_dfs_referral_rsp {
__le16 PathConsumed;
@@ -2094,7 +1960,7 @@ struct serverInfo {
unsigned char versionMinor;
unsigned long type;
unsigned int commentOffset;
-} __attribute__((packed));
+} __packed;
/*
* The following structure is the format of the data returned on a NetShareEnum
@@ -2106,27 +1972,20 @@ struct shareInfo {
char pad;
unsigned short type;
unsigned int commentOffset;
-} __attribute__((packed));
+} __packed;
struct aliasInfo {
char aliasName[9];
char pad;
unsigned int commentOffset;
unsigned char type[2];
-} __attribute__((packed));
+} __packed;
struct aliasInfo92 {
int aliasNameOffset;
int serverNameOffset;
int shareNameOffset;
-} __attribute__((packed));
-
-typedef struct {
- __le64 TotalAllocationUnits;
- __le64 FreeAllocationUnits;
- __le32 SectorsPerAllocationUnit;
- __le32 BytesPerSector;
-} __attribute__((packed)) FILE_SYSTEM_INFO; /* size info, level 0x103 */
+} __packed;
typedef struct {
__le32 fsid;
@@ -2134,13 +1993,13 @@ typedef struct {
__le32 TotalAllocationUnits;
__le32 FreeAllocationUnits;
__le16 BytesPerSector;
-} __attribute__((packed)) FILE_SYSTEM_ALLOC_INFO;
+} __packed FILE_SYSTEM_ALLOC_INFO;
typedef struct {
__le16 MajorVersionNumber;
__le16 MinorVersionNumber;
__le64 Capability;
-} __attribute__((packed)) FILE_SYSTEM_UNIX_INFO; /* Unix extension level 0x200*/
+} __packed FILE_SYSTEM_UNIX_INFO; /* Unix extension level 0x200*/
/* Version numbers for CIFS UNIX major and minor. */
#define CIFS_UNIX_MAJOR_VERSION 1
@@ -2175,28 +2034,6 @@ typedef struct {
#define CIFS_POSIX_EXTENSIONS 0x00000010 /* support for new QFSInfo */
-typedef struct {
- /* For undefined recommended transfer size return -1 in that field */
- __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */
- __le32 BlockSize;
- /* The next three fields are in terms of the block size.
- (above). If block size is unknown, 4096 would be a
- reasonable block size for a server to report.
- Note that returning the blocks/blocksavail removes need
- to make a second call (to QFSInfo level 0x103 to get this info.
- UserBlockAvail is typically less than or equal to BlocksAvail,
- if no distinction is made return the same value in each */
- __le64 TotalBlocks;
- __le64 BlocksAvail; /* bfree */
- __le64 UserBlocksAvail; /* bavail */
- /* For undefined Node fields or FSID return -1 */
- __le64 TotalFileNodes;
- __le64 FreeFileNodes;
- __le64 FileSysIdentifier; /* fsid */
- /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */
- /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */
-} __attribute__((packed)) FILE_SYSTEM_POSIX_INFO;
-
/* DeviceType Flags */
#define FILE_DEVICE_CD_ROM 0x00000002
#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003
@@ -2231,48 +2068,6 @@ typedef struct {
#define FILE_PORTABLE_DEVICE 0x00004000
#define FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL 0x00020000
-typedef struct {
- __le32 DeviceType;
- __le32 DeviceCharacteristics;
-} __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */
-
-/* minimum includes first three fields, and empty FS Name */
-#define MIN_FS_ATTR_INFO_SIZE 12
-
-
-/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */
-#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */
-#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */
-#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000
-#define FILE_SUPPORTS_USN_JOURNAL 0x02000000
-#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000
-#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000
-#define FILE_SUPPORTS_HARD_LINKS 0x00400000
-#define FILE_SUPPORTS_TRANSACTIONS 0x00200000
-#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000
-#define FILE_READ_ONLY_VOLUME 0x00080000
-#define FILE_NAMED_STREAMS 0x00040000
-#define FILE_SUPPORTS_ENCRYPTION 0x00020000
-#define FILE_SUPPORTS_OBJECT_IDS 0x00010000
-#define FILE_VOLUME_IS_COMPRESSED 0x00008000
-#define FILE_SUPPORTS_POSIX_UNLINK_RENAME 0x00000400
-#define FILE_RETURNS_CLEANUP_RESULT_INFO 0x00000200
-#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100
-#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080
-#define FILE_SUPPORTS_SPARSE_FILES 0x00000040
-#define FILE_VOLUME_QUOTAS 0x00000020
-#define FILE_FILE_COMPRESSION 0x00000010
-#define FILE_PERSISTENT_ACLS 0x00000008
-#define FILE_UNICODE_ON_DISK 0x00000004
-#define FILE_CASE_PRESERVED_NAMES 0x00000002
-#define FILE_CASE_SENSITIVE_SEARCH 0x00000001
-typedef struct {
- __le32 Attributes;
- __le32 MaxPathNameComponentLength;
- __le32 FileSystemNameLen;
- char FileSystemName[52]; /* do not have to save this - get subset? */
-} __attribute__((packed)) FILE_SYSTEM_ATTRIBUTE_INFO;
-
/******************************************************************************/
/* QueryFileInfo/QueryPathinfo (also for SetPath/SetFile) data buffer formats */
/******************************************************************************/
@@ -2297,7 +2092,7 @@ typedef struct { /* data block encoding of response to level 263 QPathInfo */
char __pad;
DECLARE_FLEX_ARRAY(char, FileName);
};
-} __attribute__((packed)) FILE_ALL_INFO; /* level 0x107 QPathInfo */
+} __packed FILE_ALL_INFO; /* level 0x107 QPathInfo */
typedef struct {
__le64 AllocationSize;
@@ -2306,7 +2101,7 @@ typedef struct {
__u8 DeletePending;
__u8 Directory;
__u16 Pad;
-} __attribute__((packed)) FILE_STANDARD_INFO; /* level 0x102 QPathInfo */
+} __packed FILE_STANDARD_INFO; /* level 0x102 QPathInfo */
/* defines for enumerating possible values of the Unix type field below */
@@ -2331,11 +2126,11 @@ typedef struct {
__le64 UniqueId;
__le64 Permissions;
__le64 Nlinks;
-} __attribute__((packed)) FILE_UNIX_BASIC_INFO; /* level 0x200 QPathInfo */
+} __packed FILE_UNIX_BASIC_INFO; /* level 0x200 QPathInfo */
typedef struct {
DECLARE_FLEX_ARRAY(char, LinkDest);
-} __attribute__((packed)) FILE_UNIX_LINK_INFO; /* level 0x201 QPathInfo */
+} __packed FILE_UNIX_LINK_INFO; /* level 0x201 QPathInfo */
/* The following three structures are needed only for
setting time to NT4 and some older servers via
@@ -2344,13 +2139,13 @@ typedef struct {
__u16 Day:5;
__u16 Month:4;
__u16 Year:7;
-} __attribute__((packed)) SMB_DATE;
+} __packed SMB_DATE;
typedef struct {
__u16 TwoSeconds:5;
__u16 Minutes:6;
__u16 Hours:5;
-} __attribute__((packed)) SMB_TIME;
+} __packed SMB_TIME;
typedef struct {
__le16 CreationDate; /* SMB Date see above */
@@ -2363,7 +2158,7 @@ typedef struct {
__le32 AllocationSize;
__le16 Attributes; /* verify not u32 */
__le32 EASize;
-} __attribute__((packed)) FILE_INFO_STANDARD; /* level 1 SetPath/FileInfo */
+} __packed FILE_INFO_STANDARD; /* level 1 SetPath/FileInfo */
typedef struct {
__le64 CreationTime;
@@ -2372,7 +2167,7 @@ typedef struct {
__le64 ChangeTime;
__le32 Attributes;
__u32 Pad;
-} __attribute__((packed)) FILE_BASIC_INFO; /* size info, level 0x101 */
+} __packed FILE_BASIC_INFO; /* size info, level 0x101 */
struct file_allocation_info {
__le64 AllocationSize; /* Note old Samba srvr rounds this up too much */
@@ -2380,11 +2175,11 @@ struct file_allocation_info {
struct file_end_of_file_info {
__le64 FileSize; /* offset to end of file */
-} __attribute__((packed)); /* size info, level 0x104 for set, 0x106 for query */
+} __packed; /* size info, level 0x104 for set, 0x106 for query */
struct file_alt_name_info {
DECLARE_FLEX_ARRAY(__u8, alt_name);
-} __attribute__((packed)); /* level 0x0108 */
+} __packed; /* level 0x0108 */
struct file_stream_info {
__le32 number_of_streams; /* BB check sizes and verify location */
@@ -2401,7 +2196,7 @@ struct file_compression_info {
__u8 ch_shift;
__u8 cl_shift;
__u8 pad[3];
-} __attribute__((packed)); /* level 0x10b */
+} __packed; /* level 0x10b */
/* POSIX ACL set/query path info structures */
#define CIFS_ACL_VERSION 1
@@ -2409,7 +2204,7 @@ struct cifs_posix_ace { /* access control entry (ACE) */
__u8 cifs_e_tag;
__u8 cifs_e_perm;
__le64 cifs_uid; /* or gid */
-} __attribute__((packed));
+} __packed;
struct cifs_posix_acl { /* access control list (ACL) */
__le16 version;
@@ -2417,7 +2212,7 @@ struct cifs_posix_acl { /* access control list (ACL) */
__le16 default_entry_count; /* default ACL - count of entries */
struct cifs_posix_ace ace_array[];
/* followed by struct cifs_posix_ace default_ace_array[] */
-} __attribute__((packed)); /* level 0x204 */
+} __packed; /* level 0x204 */
/* types of access control entries already defined in posix_acl.h */
/* #define CIFS_POSIX_ACL_USER_OBJ 0x01
@@ -2452,7 +2247,7 @@ typedef struct {
__le32 PosixOpenFlags;
__le64 Permissions;
__le16 Level; /* reply level requested (see QPathInfo levels) */
-} __attribute__((packed)) OPEN_PSX_REQ; /* level 0x209 SetPathInfo data */
+} __packed OPEN_PSX_REQ; /* level 0x209 SetPathInfo data */
typedef struct {
__le16 OplockFlags;
@@ -2461,27 +2256,27 @@ typedef struct {
__le16 ReturnedLevel;
__le16 Pad;
/* struct following varies based on requested level */
-} __attribute__((packed)) OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */
+} __packed OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */
#define SMB_POSIX_UNLINK_FILE_TARGET 0
#define SMB_POSIX_UNLINK_DIRECTORY_TARGET 1
struct unlink_psx_rq { /* level 0x20a SetPathInfo */
__le16 type;
-} __attribute__((packed));
+} __packed;
struct file_internal_info {
__le64 UniqueId; /* inode number */
-} __attribute__((packed)); /* level 0x3ee */
+} __packed; /* level 0x3ee */
struct file_mode_info {
__le32 Mode;
-} __attribute__((packed)); /* level 0x3f8 */
+} __packed; /* level 0x3f8 */
struct file_attrib_tag {
__le32 Attribute;
__le32 ReparseTag;
-} __attribute__((packed)); /* level 0x40b */
+} __packed; /* level 0x40b */
/********************************************************/
@@ -2496,71 +2291,7 @@ typedef struct {
char __pad;
DECLARE_FLEX_ARRAY(char, FileName);
};
-} __attribute__((packed)) FILE_UNIX_INFO; /* level 0x202 */
-
-typedef struct {
- __le32 NextEntryOffset;
- __u32 FileIndex;
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 EndOfFile;
- __le64 AllocationSize;
- __le32 ExtFileAttributes;
- __le32 FileNameLength;
- char FileName[];
-} __attribute__((packed)) FILE_DIRECTORY_INFO; /* level 0x101 FF resp data */
-
-typedef struct {
- __le32 NextEntryOffset;
- __u32 FileIndex;
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 EndOfFile;
- __le64 AllocationSize;
- __le32 ExtFileAttributes;
- __le32 FileNameLength;
- __le32 EaSize; /* length of the xattrs */
- char FileName[];
-} __attribute__((packed)) FILE_FULL_DIRECTORY_INFO; /* level 0x102 rsp data */
-
-typedef struct {
- __le32 NextEntryOffset;
- __u32 FileIndex;
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 EndOfFile;
- __le64 AllocationSize;
- __le32 ExtFileAttributes;
- __le32 FileNameLength;
- __le32 EaSize; /* EA size */
- __le32 Reserved;
- __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/
- char FileName[];
-} __attribute__((packed)) SEARCH_ID_FULL_DIR_INFO; /* level 0x105 FF rsp data */
-
-typedef struct {
- __le32 NextEntryOffset;
- __u32 FileIndex;
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 EndOfFile;
- __le64 AllocationSize;
- __le32 ExtFileAttributes;
- __le32 FileNameLength;
- __le32 EaSize; /* length of the xattrs */
- __u8 ShortNameLength;
- __u8 Reserved;
- __u8 ShortName[24];
- char FileName[];
-} __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FFrsp data */
+} __packed FILE_UNIX_INFO; /* level 0x202 */
typedef struct {
__u32 ResumeKey;
@@ -2575,7 +2306,7 @@ typedef struct {
__le16 Attributes; /* verify not u32 */
__u8 FileNameLength;
char FileName[];
-} __attribute__((packed)) FIND_FILE_STANDARD_INFO; /* level 0x1 FF resp data */
+} __packed FIND_FILE_STANDARD_INFO; /* level 0x1 FF resp data */
struct fea {
@@ -2584,21 +2315,21 @@ struct fea {
__le16 value_len;
char name[];
/* optionally followed by value */
-} __attribute__((packed));
+} __packed;
/* flags for _FEA.fEA */
#define FEA_NEEDEA 0x80 /* need EA bit */
struct fealist {
__le32 list_len;
struct fea list;
-} __attribute__((packed));
+} __packed;
/* used to hold an arbitrary blob of data */
struct data_blob {
__u8 *data;
size_t length;
void (*free) (struct data_blob *data_blob);
-} __attribute__((packed));
+} __packed;
#ifdef CONFIG_CIFS_POSIX
@@ -2701,7 +2432,7 @@ struct xsymlink {
char cr2; /* \n */
/* if room left, then end with \n then 0x20s by convention but not required */
char path[1024];
-} __attribute__((packed));
+} __packed;
typedef struct file_xattr_info {
/* BB do we need another field for flags? BB */
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index dcc50a2bfa4b..428e582e0414 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -361,7 +361,7 @@ vt2_err:
}
static int
-decode_ext_sec_blob(struct cifs_ses *ses, NEGOTIATE_RSP *pSMBr)
+decode_ext_sec_blob(struct cifs_ses *ses, SMB_NEGOTIATE_RSP *pSMBr)
{
int rc = 0;
u16 count;
@@ -419,8 +419,8 @@ CIFSSMBNegotiate(const unsigned int xid,
struct cifs_ses *ses,
struct TCP_Server_Info *server)
{
- NEGOTIATE_REQ *pSMB;
- NEGOTIATE_RSP *pSMBr;
+ SMB_NEGOTIATE_REQ *pSMB;
+ SMB_NEGOTIATE_RSP *pSMBr;
int rc = 0;
int bytes_returned;
int i;
@@ -614,7 +614,7 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
iov[0].iov_len = 4;
iov[0].iov_base = smb;
- iov[1].iov_len = get_rfc1002_length(smb);
+ iov[1].iov_len = get_rfc1002_len(smb);
iov[1].iov_base = (char *)smb + 4;
rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, NULL,
@@ -1458,7 +1458,7 @@ cifs_async_readv(struct cifs_io_subrequest *rdata)
rdata->iov[0].iov_base = smb;
rdata->iov[0].iov_len = 4;
rdata->iov[1].iov_base = (char *)smb + 4;
- rdata->iov[1].iov_len = get_rfc1002_length(smb);
+ rdata->iov[1].iov_len = get_rfc1002_len(smb);
trace_smb3_read_enter(rdata->rreq->debug_id,
rdata->subreq.debug_index,
@@ -1830,7 +1830,7 @@ cifs_async_writev(struct cifs_io_subrequest *wdata)
/* 4 for RFC1001 length + 1 for BCC */
iov[0].iov_len = 4;
iov[0].iov_base = smb;
- iov[1].iov_len = get_rfc1002_length(smb) + 1;
+ iov[1].iov_len = get_rfc1002_len(smb) + 1;
iov[1].iov_base = (char *)smb + 4;
rqst.rq_iov = iov;
@@ -4746,7 +4746,7 @@ CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,
/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
TRANSACTION2_QFSI_REQ *pSMB = NULL;
TRANSACTION2_QFSI_RSP *pSMBr = NULL;
- FILE_SYSTEM_INFO *response_data;
+ FILE_SYSTEM_SIZE_INFO *response_data;
int rc = 0;
int bytes_returned = 0;
__u16 params, byte_count;
@@ -4794,7 +4794,7 @@ QFSInfoRetry:
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
response_data =
- (FILE_SYSTEM_INFO
+ (FILE_SYSTEM_SIZE_INFO
*) (((char *) &pSMBr->hdr.Protocol) +
data_offset);
FSData->f_bsize =
@@ -4811,7 +4811,7 @@ QFSInfoRetry:
FSData->f_blocks =
le64_to_cpu(response_data->TotalAllocationUnits);
FSData->f_bfree = FSData->f_bavail =
- le64_to_cpu(response_data->FreeAllocationUnits);
+ le64_to_cpu(response_data->AvailableAllocationUnits);
cifs_dbg(FYI, "Blocks: %lld Free: %lld Block size %ld\n",
(unsigned long long)FSData->f_blocks,
(unsigned long long)FSData->f_bfree,
diff --git a/fs/smb/client/cifstransport.c b/fs/smb/client/cifstransport.c
index e98b95eff8c9..4c4f5befb6d3 100644
--- a/fs/smb/client/cifstransport.c
+++ b/fs/smb/client/cifstransport.c
@@ -165,7 +165,7 @@ SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
int resp_buf_type;
iov[0].iov_base = in_buf;
- iov[0].iov_len = get_rfc1002_length(in_buf) + 4;
+ iov[0].iov_len = get_rfc1002_len(in_buf) + 4;
flags |= CIFS_NO_RSP_BUF;
rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc);
@@ -177,7 +177,7 @@ int
cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
bool log_error)
{
- unsigned int len = get_rfc1002_length(mid->resp_buf) + 4;
+ unsigned int len = get_rfc1002_len(mid->resp_buf) + 4;
dump_smb(mid->resp_buf, min_t(u32, 92, len));
@@ -370,7 +370,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
goto out;
}
- *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
+ *pbytes_returned = get_rfc1002_len(midQ->resp_buf);
memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
rc = cifs_check_receive(midQ, server, 0);
out:
@@ -554,7 +554,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
goto out;
}
- *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
+ *pbytes_returned = get_rfc1002_len(midQ->resp_buf);
memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
rc = cifs_check_receive(midQ, server, 0);
out:
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index 2f94d93b95e9..0049d65de50b 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -1295,7 +1295,7 @@ cifs_demultiplex_thread(void *p)
* The right amount was read from socket - 4 bytes,
* so we can now interpret the length field.
*/
- pdu_length = get_rfc1002_length(buf);
+ pdu_length = get_rfc1002_len(buf);
cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length);
if (!is_smb_response(server, buf[0]))
@@ -2312,8 +2312,8 @@ out_err:
}
#else /* ! CONFIG_KEYS */
static inline int
-cifs_set_cifscreds(struct smb3_fs_context *ctx __attribute__((unused)),
- struct cifs_ses *ses __attribute__((unused)))
+cifs_set_cifscreds(struct smb3_fs_context *ctx __maybe_unused,
+ struct cifs_ses *ses __maybe_unused)
{
return -ENOSYS;
}
@@ -3104,7 +3104,7 @@ bind_socket(struct TCP_Server_Info *server)
struct socket *socket = server->ssocket;
rc = kernel_bind(socket,
- (struct sockaddr *) &server->srcaddr,
+ (struct sockaddr_unsized *) &server->srcaddr,
sizeof(server->srcaddr));
if (rc < 0) {
struct sockaddr_in *saddr4;
@@ -3403,7 +3403,7 @@ generic_ip_connect(struct TCP_Server_Info *server)
socket->sk->sk_sndbuf,
socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo);
- rc = kernel_connect(socket, saddr, slen,
+ rc = kernel_connect(socket, (struct sockaddr_unsized *)saddr, slen,
server->noblockcnt ? O_NONBLOCK : 0);
/*
* When mounting SMB root file systems, we do not want to block in
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index b75482730912..400e4fbd450f 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -1329,7 +1329,7 @@ static int cifs_get_fattr(struct cifs_open_info_data *data,
/* for easier reading */
FILE_ALL_INFO *fi;
FILE_DIRECTORY_INFO *fdi;
- SEARCH_ID_FULL_DIR_INFO *si;
+ FILE_ID_FULL_DIR_INFO *si;
rc = cifs_backup_query_path_info(xid, tcon, sb,
full_path,
@@ -1340,7 +1340,7 @@ static int cifs_get_fattr(struct cifs_open_info_data *data,
move_cifs_info_to_smb2(&data->fi, fi);
fdi = (FILE_DIRECTORY_INFO *)fi;
- si = (SEARCH_ID_FULL_DIR_INFO *)fi;
+ si = (FILE_ID_FULL_DIR_INFO *)fi;
cifs_dir_info_to_fattr(fattr, fdi, cifs_sb);
fattr->cf_uniqueid = le64_to_cpu(si->UniqueId);
diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
index e10123d8cd7d..340c44dc7b5b 100644
--- a/fs/smb/client/misc.c
+++ b/fs/smb/client/misc.c
@@ -318,7 +318,7 @@ static int
check_smb_hdr(struct smb_hdr *smb)
{
/* does it have the right SMB "signature" ? */
- if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) {
+ if (*(__le32 *) smb->Protocol != SMB1_PROTO_NUMBER) {
cifs_dbg(VFS, "Bad protocol string signature header 0x%x\n",
*(unsigned int *)smb->Protocol);
return 1;
diff --git a/fs/smb/client/ntlmssp.h b/fs/smb/client/ntlmssp.h
index 875de43b72de..a11fddc321f6 100644
--- a/fs/smb/client/ntlmssp.h
+++ b/fs/smb/client/ntlmssp.h
@@ -73,7 +73,7 @@ typedef struct _SECURITY_BUFFER {
__le16 Length;
__le16 MaximumLength;
__le32 BufferOffset; /* offset to buffer */
-} __attribute__((packed)) SECURITY_BUFFER;
+} __packed SECURITY_BUFFER;
typedef struct _NEGOTIATE_MESSAGE {
__u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
@@ -85,7 +85,7 @@ typedef struct _NEGOTIATE_MESSAGE {
do not set the version is present flag */
char DomainString[];
/* followed by WorkstationString */
-} __attribute__((packed)) NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE;
+} __packed NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE;
#define NTLMSSP_REVISION_W2K3 0x0F
@@ -121,7 +121,7 @@ typedef struct _CHALLENGE_MESSAGE {
SECURITY_BUFFER TargetInfoArray;
/* SECURITY_BUFFER for version info not present since we
do not set the version is present flag */
-} __attribute__((packed)) CHALLENGE_MESSAGE, *PCHALLENGE_MESSAGE;
+} __packed CHALLENGE_MESSAGE, *PCHALLENGE_MESSAGE;
typedef struct _AUTHENTICATE_MESSAGE {
__u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
@@ -136,7 +136,7 @@ typedef struct _AUTHENTICATE_MESSAGE {
struct ntlmssp_version Version;
/* SECURITY_BUFFER */
char UserString[];
-} __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE;
+} __packed AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE;
/*
* Size of the session key (crypto key encrypted with the password
diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c
index f0ce26622a14..7ff728503ed1 100644
--- a/fs/smb/client/readdir.c
+++ b/fs/smb/client/readdir.c
@@ -98,7 +98,7 @@ retry:
default:
break;
}
- } else if (fattr->cf_cifsattrs & ATTR_REPARSE) {
+ } else if (fattr->cf_cifsattrs & ATTR_REPARSE_POINT) {
reparse_need_reval = true;
}
@@ -138,7 +138,7 @@ retry:
* reparse tag and ctime haven't changed.
*/
rc = 0;
- if (fattr->cf_cifsattrs & ATTR_REPARSE) {
+ if (fattr->cf_cifsattrs & ATTR_REPARSE_POINT) {
if (likely(reparse_inode_match(inode, fattr))) {
fattr->cf_mode = inode->i_mode;
fattr->cf_rdev = inode->i_rdev;
@@ -190,7 +190,7 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
* TODO: go through all documented reparse tags to see if we can
* reasonably map some of them to directories vs. files vs. symlinks
*/
- if ((fattr->cf_cifsattrs & ATTR_REPARSE) &&
+ if ((fattr->cf_cifsattrs & ATTR_REPARSE_POINT) &&
cifs_reparse_point_to_fattr(cifs_sb, fattr, &data))
goto out_reparse;
@@ -258,7 +258,7 @@ cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info,
fattr->cf_nlink = le32_to_cpu(info->HardLinks);
fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes);
- if (fattr->cf_cifsattrs & ATTR_REPARSE)
+ if (fattr->cf_cifsattrs & ATTR_REPARSE_POINT)
fattr->cf_cifstag = le32_to_cpu(info->ReparseTag);
/* The Mode field in the response can now include the file type as well */
@@ -316,7 +316,7 @@ static void cifs_fulldir_info_to_fattr(struct cifs_fattr *fattr,
__dir_info_to_fattr(fattr, info);
/* See MS-FSCC 2.4.14, 2.4.19 */
- if (fattr->cf_cifsattrs & ATTR_REPARSE)
+ if (fattr->cf_cifsattrs & ATTR_REPARSE_POINT)
fattr->cf_cifstag = le32_to_cpu(di->EaSize);
cifs_fill_common_info(fattr, cifs_sb);
}
@@ -548,7 +548,7 @@ static void cifs_fill_dirent_full(struct cifs_dirent *de,
}
static void cifs_fill_dirent_search(struct cifs_dirent *de,
- const SEARCH_ID_FULL_DIR_INFO *info)
+ const FILE_ID_FULL_DIR_INFO *info)
{
de->name = &info->FileName[0];
de->namelen = le32_to_cpu(info->FileNameLength);
diff --git a/fs/smb/client/reparse.h b/fs/smb/client/reparse.h
index 66269c10beba..19caab2fd11e 100644
--- a/fs/smb/client/reparse.h
+++ b/fs/smb/client/reparse.h
@@ -93,7 +93,7 @@ static inline bool reparse_inode_match(struct inode *inode,
if (cinode->reparse_tag != IO_REPARSE_TAG_INTERNAL &&
cinode->reparse_tag != fattr->cf_cifstag)
return false;
- return (cinode->cifsAttrs & ATTR_REPARSE) &&
+ return (cinode->cifsAttrs & ATTR_REPARSE_POINT) &&
timespec64_equal(&ctime, &fattr->cf_ctime);
}
@@ -107,7 +107,7 @@ static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
attrs = le32_to_cpu(fi->DosAttributes);
if (data->reparse_point) {
- attrs |= ATTR_REPARSE;
+ attrs |= ATTR_REPARSE_POINT;
fi->DosAttributes = cpu_to_le32(attrs);
}
@@ -116,12 +116,12 @@ static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
attrs = le32_to_cpu(fi->Attributes);
if (data->reparse_point) {
- attrs |= ATTR_REPARSE;
+ attrs |= ATTR_REPARSE_POINT;
fi->Attributes = cpu_to_le32(attrs);
}
}
- ret = attrs & ATTR_REPARSE;
+ ret = attrs & ATTR_REPARSE_POINT;
return ret;
}
diff --git a/fs/smb/client/rfc1002pdu.h b/fs/smb/client/rfc1002pdu.h
index ac82c2f3a4a2..f5b143088b90 100644
--- a/fs/smb/client/rfc1002pdu.h
+++ b/fs/smb/client/rfc1002pdu.h
@@ -33,17 +33,17 @@ struct rfc1002_session_packet {
__u8 calling_len;
__u8 calling_name[32];
__u8 scope2; /* null */
- } __attribute__((packed)) session_req;
+ } __packed session_req;
struct {
__be32 retarget_ip_addr;
__be16 port;
- } __attribute__((packed)) retarget_resp;
+ } __packed retarget_resp;
__u8 neg_ses_resp_error_code;
/* POSITIVE_SESSION_RESPONSE packet does not include trailer.
SESSION_KEEP_ALIVE packet also does not include a trailer.
Trailer for the SESSION_MESSAGE packet is SMB/CIFS header */
- } __attribute__((packed)) trailer;
-} __attribute__((packed));
+ } __packed trailer;
+} __packed;
/* Negative Session Response error codes */
#define RFC1002_NOT_LISTENING_CALLED 0x80 /* not listening on called name */
diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
index 78650527d4bb..a479fe1396c0 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -648,7 +648,7 @@ static int cifs_query_path_info(const unsigned int xid,
if (!rc) {
move_cifs_info_to_smb2(&data->fi, &fi);
- data->reparse_point = le32_to_cpu(fi.Attributes) & ATTR_REPARSE;
+ data->reparse_point = le32_to_cpu(fi.Attributes) & ATTR_REPARSE_POINT;
}
#ifdef CONFIG_CIFS_XATTR
diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
index 69cb81fa0d3a..d0aad4821ed4 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -50,7 +50,7 @@ static inline __u32 file_create_options(struct dentry *dentry)
if (dentry) {
ci = CIFS_I(d_inode(dentry));
- if (ci->cifsAttrs & ATTR_REPARSE)
+ if (ci->cifsAttrs & ATTR_REPARSE_POINT)
return OPEN_REPARSE_POINT;
}
return 0;
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 1e39f2165e42..7ace6d4d305b 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -624,8 +624,8 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
struct network_interface_info_ioctl_rsp *p;
struct sockaddr_in *addr4;
struct sockaddr_in6 *addr6;
- struct iface_info_ipv4 *p4;
- struct iface_info_ipv6 *p6;
+ struct smb_sockaddr_in *p4;
+ struct smb_sockaddr_in6 *p6;
struct cifs_server_iface *info = NULL, *iface = NULL, *niface = NULL;
struct cifs_server_iface tmp_iface;
ssize_t bytes_left;
@@ -685,7 +685,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
*/
case INTERNETWORK:
addr4 = (struct sockaddr_in *)&tmp_iface.sockaddr;
- p4 = (struct iface_info_ipv4 *)p->Buffer;
+ p4 = (struct smb_sockaddr_in *)p->Buffer;
addr4->sin_family = AF_INET;
memcpy(&addr4->sin_addr, &p4->IPv4Address, 4);
@@ -697,7 +697,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
break;
case INTERNETWORKV6:
addr6 = (struct sockaddr_in6 *)&tmp_iface.sockaddr;
- p6 = (struct iface_info_ipv6 *)p->Buffer;
+ p6 = (struct smb_sockaddr_in6 *)p->Buffer;
addr6->sin6_family = AF_INET6;
memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16);
@@ -1524,11 +1524,11 @@ smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,
static int
SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid,
- struct copychunk_ioctl *pcchunk)
+ struct copychunk_ioctl_req *pcchunk)
{
int rc;
unsigned int ret_data_len;
- struct resume_key_req *res_key;
+ struct resume_key_ioctl_rsp *res_key;
rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
FSCTL_SRV_REQUEST_RESUME_KEY, NULL, 0 /* no input */,
@@ -1541,7 +1541,7 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
cifs_tcon_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc);
goto req_res_key_exit;
}
- if (ret_data_len < sizeof(struct resume_key_req)) {
+ if (ret_data_len < sizeof(struct resume_key_ioctl_rsp)) {
cifs_tcon_dbg(VFS, "Invalid refcopy resume key length\n");
rc = -EINVAL;
goto req_res_key_exit;
@@ -1857,10 +1857,10 @@ smb2_copychunk_range(const unsigned int xid,
{
int rc = 0;
unsigned int ret_data_len = 0;
- struct copychunk_ioctl *cc_req = NULL;
+ struct copychunk_ioctl_req *cc_req = NULL;
struct copychunk_ioctl_rsp *cc_rsp = NULL;
struct cifs_tcon *tcon;
- struct copychunk *chunk;
+ struct srv_copychunk *chunk;
u32 chunks, chunk_count, chunk_bytes;
u32 copy_bytes, copy_bytes_left;
u32 chunks_written, bytes_written;
@@ -4485,61 +4485,6 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
}
/*
- * Clear a read buffer, discarding the folios which have the 1st mark set.
- */
-static void cifs_clear_folioq_buffer(struct folio_queue *buffer)
-{
- struct folio_queue *folioq;
-
- while ((folioq = buffer)) {
- for (int s = 0; s < folioq_count(folioq); s++)
- if (folioq_is_marked(folioq, s))
- folio_put(folioq_folio(folioq, s));
- buffer = folioq->next;
- kfree(folioq);
- }
-}
-
-/*
- * Allocate buffer space into a folio queue.
- */
-static struct folio_queue *cifs_alloc_folioq_buffer(ssize_t size)
-{
- struct folio_queue *buffer = NULL, *tail = NULL, *p;
- struct folio *folio;
- unsigned int slot;
-
- do {
- if (!tail || folioq_full(tail)) {
- p = kmalloc(sizeof(*p), GFP_NOFS);
- if (!p)
- goto nomem;
- folioq_init(p, 0);
- if (tail) {
- tail->next = p;
- p->prev = tail;
- } else {
- buffer = p;
- }
- tail = p;
- }
-
- folio = folio_alloc(GFP_KERNEL|__GFP_HIGHMEM, 0);
- if (!folio)
- goto nomem;
-
- slot = folioq_append_mark(tail, folio);
- size -= folioq_folio_size(tail, slot);
- } while (size > 0);
-
- return buffer;
-
-nomem:
- cifs_clear_folioq_buffer(buffer);
- return NULL;
-}
-
-/*
* Copy data from an iterator to the folios in a folio queue buffer.
*/
static bool cifs_copy_iter_to_folioq(struct iov_iter *iter, size_t size,
@@ -4564,7 +4509,7 @@ void
smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst)
{
for (int i = 0; i < num_rqst; i++)
- cifs_clear_folioq_buffer(rqst[i].rq_buffer);
+ netfs_free_folioq_buffer(rqst[i].rq_buffer);
}
/*
@@ -4599,8 +4544,10 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst,
new->rq_nvec = old->rq_nvec;
if (size > 0) {
- buffer = cifs_alloc_folioq_buffer(size);
- if (!buffer)
+ size_t cur_size = 0;
+ rc = netfs_alloc_folioq_buffer(NULL, &buffer, &cur_size,
+ size, GFP_NOFS);
+ if (rc < 0)
goto err_free;
new->rq_buffer = buffer;
@@ -4932,7 +4879,7 @@ static void smb2_decrypt_offload(struct work_struct *work)
}
free_pages:
- cifs_clear_folioq_buffer(dw->buffer);
+ netfs_free_folioq_buffer(dw->buffer);
cifs_small_buf_release(dw->buf);
kfree(dw);
}
@@ -4970,9 +4917,9 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid,
dw->len = len;
len = round_up(dw->len, PAGE_SIZE);
- rc = -ENOMEM;
- dw->buffer = cifs_alloc_folioq_buffer(len);
- if (!dw->buffer)
+ size_t cur_size = 0;
+ rc = netfs_alloc_folioq_buffer(NULL, &dw->buffer, &cur_size, len, GFP_NOFS);
+ if (rc < 0)
goto discard_data;
iov_iter_folio_queue(&iter, ITER_DEST, dw->buffer, 0, 0, len);
@@ -5033,7 +4980,7 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid,
}
free_pages:
- cifs_clear_folioq_buffer(dw->buffer);
+ netfs_free_folioq_buffer(dw->buffer);
free_dw:
kfree(dw);
return rc;
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 8b4a4573e9c3..ef2c6ac500f7 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -2229,21 +2229,20 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
return rc;
}
-
-static struct create_durable *
+static create_durable_req_t *
create_durable_buf(void)
{
- struct create_durable *buf;
+ create_durable_req_t *buf;
- buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL);
+ buf = kzalloc(sizeof(create_durable_req_t), GFP_KERNEL);
if (!buf)
return NULL;
buf->ccontext.DataOffset = cpu_to_le16(offsetof
- (struct create_durable, Data));
+ (create_durable_req_t, Data));
buf->ccontext.DataLength = cpu_to_le32(16);
buf->ccontext.NameOffset = cpu_to_le16(offsetof
- (struct create_durable, Name));
+ (create_durable_req_t, Name));
buf->ccontext.NameLength = cpu_to_le16(4);
/* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */
buf->Name[0] = 'D';
@@ -2253,20 +2252,20 @@ create_durable_buf(void)
return buf;
}
-static struct create_durable *
+static create_durable_req_t *
create_reconnect_durable_buf(struct cifs_fid *fid)
{
- struct create_durable *buf;
+ create_durable_req_t *buf;
- buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL);
+ buf = kzalloc(sizeof(create_durable_req_t), GFP_KERNEL);
if (!buf)
return NULL;
buf->ccontext.DataOffset = cpu_to_le16(offsetof
- (struct create_durable, Data));
+ (create_durable_req_t, Data));
buf->ccontext.DataLength = cpu_to_le32(16);
buf->ccontext.NameOffset = cpu_to_le16(offsetof
- (struct create_durable, Name));
+ (create_durable_req_t, Name));
buf->ccontext.NameLength = cpu_to_le16(4);
buf->Data.Fid.PersistentFileId = fid->persistent_fid;
buf->Data.Fid.VolatileFileId = fid->volatile_fid;
@@ -2424,21 +2423,21 @@ add_lease_context(struct TCP_Server_Info *server,
return 0;
}
-static struct create_durable_v2 *
+static struct create_durable_req_v2 *
create_durable_v2_buf(struct cifs_open_parms *oparms)
{
struct cifs_fid *pfid = oparms->fid;
- struct create_durable_v2 *buf;
+ struct create_durable_req_v2 *buf;
- buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL);
+ buf = kzalloc(sizeof(struct create_durable_req_v2), GFP_KERNEL);
if (!buf)
return NULL;
buf->ccontext.DataOffset = cpu_to_le16(offsetof
- (struct create_durable_v2, dcontext));
- buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_context_v2));
+ (struct create_durable_req_v2, dcontext));
+ buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_context_v2_req));
buf->ccontext.NameOffset = cpu_to_le16(offsetof
- (struct create_durable_v2, Name));
+ (struct create_durable_req_v2, Name));
buf->ccontext.NameLength = cpu_to_le16(4);
/*
@@ -2508,7 +2507,7 @@ add_durable_v2_context(struct kvec *iov, unsigned int *num_iovec,
iov[num].iov_base = create_durable_v2_buf(oparms);
if (iov[num].iov_base == NULL)
return -ENOMEM;
- iov[num].iov_len = sizeof(struct create_durable_v2);
+ iov[num].iov_len = sizeof(struct create_durable_req_v2);
*num_iovec = num + 1;
return 0;
}
@@ -2552,7 +2551,7 @@ add_durable_context(struct kvec *iov, unsigned int *num_iovec,
iov[num].iov_base = create_durable_buf();
if (iov[num].iov_base == NULL)
return -ENOMEM;
- iov[num].iov_len = sizeof(struct create_durable);
+ iov[num].iov_len = sizeof(create_durable_req_t);
*num_iovec = num + 1;
return 0;
}
@@ -5454,7 +5453,7 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
info_buf_size = sizeof(FILE_DIRECTORY_INFO);
break;
case SMB_FIND_FILE_ID_FULL_DIR_INFO:
- info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO);
+ info_buf_size = sizeof(FILE_ID_FULL_DIR_INFO);
break;
case SMB_FIND_FILE_POSIX_INFO:
/* note that posix payload are variable size */
@@ -5982,8 +5981,8 @@ replay_again:
max_len = sizeof(FILE_SYSTEM_DEVICE_INFO);
min_len = sizeof(FILE_SYSTEM_DEVICE_INFO);
} else if (level == FS_ATTRIBUTE_INFORMATION) {
- max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO);
- min_len = MIN_FS_ATTR_INFO_SIZE;
+ max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO) + MAX_FS_NAME_LEN;
+ min_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO);
} else if (level == FS_SECTOR_SIZE_INFORMATION) {
max_len = sizeof(struct smb3_fs_ss_info);
min_len = sizeof(struct smb3_fs_ss_info);
@@ -6029,7 +6028,7 @@ replay_again:
if (level == FS_ATTRIBUTE_INFORMATION)
memcpy(&tcon->fsAttrInfo, offset
+ (char *)rsp, min_t(unsigned int,
- rsp_len, max_len));
+ rsp_len, min_len));
else if (level == FS_DEVICE_INFORMATION)
memcpy(&tcon->fsDevInfo, offset
+ (char *)rsp, sizeof(FILE_SYSTEM_DEVICE_INFO));
diff --git a/fs/smb/client/smb2pdu.h b/fs/smb/client/smb2pdu.h
index 101024f8f725..78bb99f29d38 100644
--- a/fs/smb/client/smb2pdu.h
+++ b/fs/smb/client/smb2pdu.h
@@ -133,46 +133,6 @@ struct share_redirect_error_context_rsp {
#define SMB2_LEASE_HANDLE_CACHING_HE 0x02
#define SMB2_LEASE_WRITE_CACHING_HE 0x04
-
-/* See MS-SMB2 2.2.13.2.11 */
-/* Flags */
-#define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002
-struct durable_context_v2 {
- __le32 Timeout;
- __le32 Flags;
- __u64 Reserved;
- __u8 CreateGuid[16];
-} __packed;
-
-struct create_durable_v2 {
- struct create_context_hdr ccontext;
- __u8 Name[8];
- struct durable_context_v2 dcontext;
-} __packed;
-
-/* See MS-SMB2 2.2.13.2.12 */
-struct durable_reconnect_context_v2 {
- struct {
- __u64 PersistentFileId;
- __u64 VolatileFileId;
- } Fid;
- __u8 CreateGuid[16];
- __le32 Flags; /* see above DHANDLE_FLAG_PERSISTENT */
-} __packed;
-
-/* See MS-SMB2 2.2.14.2.12 */
-struct durable_reconnect_context_v2_rsp {
- __le32 Timeout;
- __le32 Flags; /* see above DHANDLE_FLAG_PERSISTENT */
-} __packed;
-
-struct create_durable_handle_reconnect_v2 {
- struct create_context_hdr ccontext;
- __u8 Name[8];
- struct durable_reconnect_context_v2 dcontext;
- __u8 Pad[4];
-} __packed;
-
/* See MS-SMB2 2.2.13.2.5 */
struct crt_twarp_ctxt {
struct create_context_hdr ccontext;
@@ -193,36 +153,6 @@ struct crt_sd_ctxt {
struct smb3_sd sd;
} __packed;
-
-#define COPY_CHUNK_RES_KEY_SIZE 24
-struct resume_key_req {
- char ResumeKey[COPY_CHUNK_RES_KEY_SIZE];
- __le32 ContextLength; /* MBZ */
- char Context[]; /* ignored, Windows sets to 4 bytes of zero */
-} __packed;
-
-
-struct copychunk {
- __le64 SourceOffset;
- __le64 TargetOffset;
- __le32 Length;
- __le32 Reserved;
-} __packed;
-
-/* this goes in the ioctl buffer when doing a copychunk request */
-struct copychunk_ioctl {
- char SourceKey[COPY_CHUNK_RES_KEY_SIZE];
- __le32 ChunkCount;
- __le32 Reserved;
- struct copychunk Chunks[];
-} __packed;
-
-struct copychunk_ioctl_rsp {
- __le32 ChunksWritten;
- __le32 ChunkBytesWritten;
- __le32 TotalBytesWritten;
-} __packed;
-
/* See MS-FSCC 2.3.29 and 2.3.30 */
struct get_retrieval_pointer_count_req {
__le64 StartingVcn; /* virtual cluster number (signed) */
@@ -263,35 +193,6 @@ struct network_resiliency_req {
} __packed;
/* There is no buffer for the response ie no struct network_resiliency_rsp */
-#define RSS_CAPABLE cpu_to_le32(0x00000001)
-#define RDMA_CAPABLE cpu_to_le32(0x00000002)
-
-#define INTERNETWORK cpu_to_le16(0x0002)
-#define INTERNETWORKV6 cpu_to_le16(0x0017)
-
-struct network_interface_info_ioctl_rsp {
- __le32 Next; /* next interface. zero if this is last one */
- __le32 IfIndex;
- __le32 Capability; /* RSS or RDMA Capable */
- __le32 Reserved;
- __le64 LinkSpeed;
- __le16 Family;
- __u8 Buffer[126];
-} __packed;
-
-struct iface_info_ipv4 {
- __be16 Port;
- __be32 IPv4Address;
- __be64 Reserved;
-} __packed;
-
-struct iface_info_ipv6 {
- __be16 Port;
- __be32 FlowInfo;
- __u8 IPv6Address[16];
- __be32 ScopeId;
-} __packed;
-
#define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */
struct compress_ioctl {
@@ -323,19 +224,6 @@ struct smb2_file_reparse_point_info {
__le32 Tag;
} __packed;
-struct smb2_file_network_open_info {
- struct_group_attr(network_open_info, __packed,
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 AllocationSize;
- __le64 EndOfFile;
- __le32 Attributes;
- );
- __le32 Reserved;
-} __packed; /* level 34 Query also similar returned in close rsp and open rsp */
-
/* See MS-FSCC 2.4.21 */
struct smb2_file_id_information {
__le64 VolumeSerialNumber;
diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c
index c6c428c2e08d..788a0670c4a8 100644
--- a/fs/smb/client/smbdirect.c
+++ b/fs/smb/client/smbdirect.c
@@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/highmem.h>
#include <linux/folio_queue.h>
+#define __SMBDIRECT_SOCKET_DISCONNECT(__sc) smbd_disconnect_rdma_connection(__sc)
#include "../common/smbdirect/smbdirect_pdu.h"
#include "smbdirect.h"
#include "cifs_debug.h"
@@ -186,6 +187,9 @@ static void smbd_disconnect_rdma_work(struct work_struct *work)
struct smbdirect_socket *sc =
container_of(work, struct smbdirect_socket, disconnect_work);
+ if (sc->first_error == 0)
+ sc->first_error = -ECONNABORTED;
+
/*
* make sure this and other work is not queued again
* but here we don't block and avoid
@@ -197,9 +201,6 @@ static void smbd_disconnect_rdma_work(struct work_struct *work)
disable_work(&sc->idle.immediate_work);
disable_delayed_work(&sc->idle.timer_work);
- if (sc->first_error == 0)
- sc->first_error = -ECONNABORTED;
-
switch (sc->status) {
case SMBDIRECT_SOCKET_NEGOTIATE_NEEDED:
case SMBDIRECT_SOCKET_NEGOTIATE_RUNNING:
@@ -242,6 +243,9 @@ static void smbd_disconnect_rdma_work(struct work_struct *work)
static void smbd_disconnect_rdma_connection(struct smbdirect_socket *sc)
{
+ if (sc->first_error == 0)
+ sc->first_error = -ECONNABORTED;
+
/*
* make sure other work (than disconnect_work) is
* not queued again but here we don't block and avoid
@@ -252,9 +256,6 @@ static void smbd_disconnect_rdma_connection(struct smbdirect_socket *sc)
disable_work(&sc->idle.immediate_work);
disable_delayed_work(&sc->idle.timer_work);
- if (sc->first_error == 0)
- sc->first_error = -ECONNABORTED;
-
switch (sc->status) {
case SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED:
case SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED:
@@ -322,27 +323,27 @@ static int smbd_conn_upcall(
switch (event->event) {
case RDMA_CM_EVENT_ADDR_RESOLVED:
- WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING);
+ if (SMBDIRECT_CHECK_STATUS_DISCONNECT(sc, SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING))
+ break;
sc->status = SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED;
wake_up(&sc->status_wait);
break;
case RDMA_CM_EVENT_ROUTE_RESOLVED:
- WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING);
+ if (SMBDIRECT_CHECK_STATUS_DISCONNECT(sc, SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING))
+ break;
sc->status = SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED;
wake_up(&sc->status_wait);
break;
case RDMA_CM_EVENT_ADDR_ERROR:
log_rdma_event(ERR, "connecting failed event=%s\n", event_name);
- WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING);
sc->status = SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED;
smbd_disconnect_rdma_work(&sc->disconnect_work);
break;
case RDMA_CM_EVENT_ROUTE_ERROR:
log_rdma_event(ERR, "connecting failed event=%s\n", event_name);
- WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING);
sc->status = SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED;
smbd_disconnect_rdma_work(&sc->disconnect_work);
break;
@@ -428,7 +429,8 @@ static int smbd_conn_upcall(
min_t(u8, sp->responder_resources,
peer_responder_resources);
- WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING);
+ if (SMBDIRECT_CHECK_STATUS_DISCONNECT(sc, SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING))
+ break;
sc->status = SMBDIRECT_SOCKET_NEGOTIATE_NEEDED;
wake_up(&sc->status_wait);
break;
@@ -437,7 +439,6 @@ static int smbd_conn_upcall(
case RDMA_CM_EVENT_UNREACHABLE:
case RDMA_CM_EVENT_REJECTED:
log_rdma_event(ERR, "connecting failed event=%s\n", event_name);
- WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING);
sc->status = SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED;
smbd_disconnect_rdma_work(&sc->disconnect_work);
break;
@@ -699,7 +700,8 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
negotiate_done =
process_negotiation_response(response, wc->byte_len);
put_receive_buffer(sc, response);
- WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_NEGOTIATE_RUNNING);
+ if (SMBDIRECT_CHECK_STATUS_WARN(sc, SMBDIRECT_SOCKET_NEGOTIATE_RUNNING))
+ negotiate_done = false;
if (!negotiate_done) {
sc->status = SMBDIRECT_SOCKET_NEGOTIATE_FAILED;
smbd_disconnect_rdma_connection(sc);
diff --git a/fs/smb/common/cifsglob.h b/fs/smb/common/cifsglob.h
deleted file mode 100644
index 00fd215e3eb5..000000000000
--- a/fs/smb/common/cifsglob.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1 */
-/*
- *
- * Copyright (C) International Business Machines Corp., 2002,2008
- * Author(s): Steve French (sfrench@us.ibm.com)
- * Jeremy Allison (jra@samba.org)
- *
- */
-#ifndef _COMMON_CIFS_GLOB_H
-#define _COMMON_CIFS_GLOB_H
-
-static inline void inc_rfc1001_len(void *buf, int count)
-{
- be32_add_cpu((__be32 *)buf, count);
-}
-
-#define SMB1_VERSION_STRING "1.0"
-#define SMB20_VERSION_STRING "2.0"
-#define SMB21_VERSION_STRING "2.1"
-#define SMBDEFAULT_VERSION_STRING "default"
-#define SMB3ANY_VERSION_STRING "3"
-#define SMB30_VERSION_STRING "3.0"
-#define SMB302_VERSION_STRING "3.02"
-#define ALT_SMB302_VERSION_STRING "3.0.2"
-#define SMB311_VERSION_STRING "3.1.1"
-#define ALT_SMB311_VERSION_STRING "3.11"
-
-#define CIFS_DEFAULT_IOSIZE (1024 * 1024)
-
-#endif /* _COMMON_CIFS_GLOB_H */
diff --git a/fs/smb/common/fscc.h b/fs/smb/common/fscc.h
new file mode 100644
index 000000000000..35dbacdbb902
--- /dev/null
+++ b/fs/smb/common/fscc.h
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+/*
+ *
+ * Copyright (c) International Business Machines Corp., 2009, 2013
+ * Etersoft, 2012
+ * 2018 Samsung Electronics Co., Ltd.
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Pavel Shilovsky (pshilovsky@samba.org) 2012
+ * Namjae Jeon (linkinjeon@kernel.org)
+ *
+ */
+#ifndef _COMMON_SMB_FSCC_H
+#define _COMMON_SMB_FSCC_H
+
+/* See MS-FSCC 2.4.8 */
+typedef struct {
+ __le32 NextEntryOffset;
+ __u32 FileIndex;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le64 EndOfFile;
+ __le64 AllocationSize;
+ __le32 ExtFileAttributes;
+ __le32 FileNameLength;
+ __le32 EaSize; /* length of the xattrs */
+ __u8 ShortNameLength;
+ __u8 Reserved;
+ __u8 ShortName[24];
+ char FileName[];
+} __packed FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FFrsp data */
+
+/* See MS-FSCC 2.4.10 */
+typedef struct {
+ __le32 NextEntryOffset;
+ __u32 FileIndex;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le64 EndOfFile;
+ __le64 AllocationSize;
+ __le32 ExtFileAttributes;
+ __le32 FileNameLength;
+ char FileName[];
+} __packed FILE_DIRECTORY_INFO; /* level 0x101 FF resp data */
+
+/* See MS-FSCC 2.4.14 */
+typedef struct {
+ __le32 NextEntryOffset;
+ __u32 FileIndex;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le64 EndOfFile;
+ __le64 AllocationSize;
+ __le32 ExtFileAttributes;
+ __le32 FileNameLength;
+ __le32 EaSize; /* length of the xattrs */
+ char FileName[];
+} __packed FILE_FULL_DIRECTORY_INFO; /* level 0x102 rsp data */
+
+/* See MS-FSCC 2.4.24 */
+typedef struct {
+ __le32 NextEntryOffset;
+ __u32 FileIndex;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le64 EndOfFile;
+ __le64 AllocationSize;
+ __le32 ExtFileAttributes;
+ __le32 FileNameLength;
+ __le32 EaSize; /* EA size */
+ __le32 Reserved;
+ __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/
+ char FileName[];
+} __packed FILE_ID_FULL_DIR_INFO; /* level 0x105 FF rsp data */
+
+/* See MS-FSCC 2.4.34 */
+struct smb2_file_network_open_info {
+ struct_group_attr(network_open_info, __packed,
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le64 AllocationSize;
+ __le64 EndOfFile;
+ __le32 Attributes;
+ );
+ __le32 Reserved;
+} __packed; /* level 34 Query also similar returned in close rsp and open rsp */
+
+/* See MS-FSCC 2.5.1 */
+#define MAX_FS_NAME_LEN 52
+typedef struct {
+ __le32 Attributes;
+ __le32 MaxPathNameComponentLength;
+ __le32 FileSystemNameLen;
+ __le16 FileSystemName[]; /* do not have to save this - get subset? */
+} __packed FILE_SYSTEM_ATTRIBUTE_INFO;
+
+/* List of FileSystemAttributes - see MS-FSCC 2.5.1 */
+#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */
+#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */
+#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000
+#define FILE_SUPPORTS_USN_JOURNAL 0x02000000
+#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000
+#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000
+#define FILE_SUPPORTS_HARD_LINKS 0x00400000
+#define FILE_SUPPORTS_TRANSACTIONS 0x00200000
+#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000
+#define FILE_READ_ONLY_VOLUME 0x00080000
+#define FILE_NAMED_STREAMS 0x00040000
+#define FILE_SUPPORTS_ENCRYPTION 0x00020000
+#define FILE_SUPPORTS_OBJECT_IDS 0x00010000
+#define FILE_VOLUME_IS_COMPRESSED 0x00008000
+#define FILE_SUPPORTS_POSIX_UNLINK_RENAME 0x00000400
+#define FILE_RETURNS_CLEANUP_RESULT_INFO 0x00000200
+#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100
+#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080
+#define FILE_SUPPORTS_SPARSE_FILES 0x00000040
+#define FILE_VOLUME_QUOTAS 0x00000020
+#define FILE_FILE_COMPRESSION 0x00000010
+#define FILE_PERSISTENT_ACLS 0x00000008
+#define FILE_UNICODE_ON_DISK 0x00000004
+#define FILE_CASE_PRESERVED_NAMES 0x00000002
+#define FILE_CASE_SENSITIVE_SEARCH 0x00000001
+
+/* See MS-FSCC 2.5.8 */
+typedef struct {
+ __le64 TotalAllocationUnits;
+ __le64 AvailableAllocationUnits;
+ __le32 SectorsPerAllocationUnit;
+ __le32 BytesPerSector;
+} __packed FILE_SYSTEM_SIZE_INFO; /* size info, level 0x103 */
+
+/* See MS-FSCC 2.5.10 */
+typedef struct {
+ __le32 DeviceType;
+ __le32 DeviceCharacteristics;
+} __packed FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */
+
+/*
+ * See POSIX Extensions to MS-FSCC 2.3.2.1
+ * Link: https://gitlab.com/samba-team/smb3-posix-spec/-/blob/master/fscc_posix_extensions.md
+ */
+typedef struct {
+ /* For undefined recommended transfer size return -1 in that field */
+ __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */
+ __le32 BlockSize;
+ /* The next three fields are in terms of the block size.
+ * (above). If block size is unknown, 4096 would be a
+ * reasonable block size for a server to report.
+ * Note that returning the blocks/blocksavail removes need
+ * to make a second call (to QFSInfo level 0x103 to get this info.
+ * UserBlockAvail is typically less than or equal to BlocksAvail,
+ * if no distinction is made return the same value in each
+ */
+ __le64 TotalBlocks;
+ __le64 BlocksAvail; /* bfree */
+ __le64 UserBlocksAvail; /* bavail */
+ /* For undefined Node fields or FSID return -1 */
+ __le64 TotalFileNodes;
+ __le64 FreeFileNodes;
+ __le64 FileSysIdentifier; /* fsid */
+ /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */
+ /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */
+} __packed FILE_SYSTEM_POSIX_INFO;
+
+#endif /* _COMMON_SMB_FSCC_H */
diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h
index f79a5165a7cc..f38c5739a9d2 100644
--- a/fs/smb/common/smb2pdu.h
+++ b/fs/smb/common/smb2pdu.h
@@ -1149,12 +1149,6 @@ struct smb2_server_client_notification {
#define FILE_OVERWRITE_IF_LE cpu_to_le32(0x00000005)
#define FILE_CREATE_MASK_LE cpu_to_le32(0x00000007)
-#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \
- | FILE_READ_ATTRIBUTES)
-#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \
- | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)
-#define FILE_EXEC_RIGHTS (FILE_EXECUTE)
-
/* CreateOptions Flags */
#define FILE_DIRECTORY_FILE_LE cpu_to_le32(0x00000001)
/* same as #define CREATE_NOT_FILE_LE cpu_to_le32(0x00000001) */
@@ -1271,7 +1265,7 @@ struct create_posix {
} __packed;
/* See MS-SMB2 2.2.13.2.3 and MS-SMB2 2.2.13.2.4 */
-struct create_durable {
+typedef struct {
struct create_context_hdr ccontext;
__u8 Name[8];
union {
@@ -1281,7 +1275,7 @@ struct create_durable {
__u64 VolatileFileId;
} Fid;
} Data;
-} __packed;
+} __packed create_durable_req_t, create_durable_reconn_t;
/* See MS-SMB2 2.2.13.2.5 */
struct create_mxac_req {
@@ -1290,6 +1284,56 @@ struct create_mxac_req {
__le64 Timestamp;
} __packed;
+/*
+ * Flags
+ * See MS-SMB2 2.2.13.2.11
+ * MS-SMB2 2.2.13.2.12
+ * MS-SMB2 2.2.14.2.12
+ */
+#define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002
+
+/* See MS-SMB2 2.2.13.2.11 */
+struct durable_context_v2_req {
+ __le32 Timeout;
+ __le32 Flags; /* see SMB2_DHANDLE_FLAG_PERSISTENT */
+ __u64 Reserved;
+ __u8 CreateGuid[16];
+} __packed;
+
+struct create_durable_req_v2 {
+ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ struct durable_context_v2_req dcontext;
+} __packed;
+
+/* See MS-SMB2 2.2.13.2.12 */
+struct durable_reconnect_context_v2 {
+ struct {
+ __u64 PersistentFileId;
+ __u64 VolatileFileId;
+ } Fid;
+ __u8 CreateGuid[16];
+ __le32 Flags; /* see SMB2_DHANDLE_FLAG_PERSISTENT */
+} __packed;
+
+struct create_durable_handle_reconnect_v2 {
+ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ struct durable_reconnect_context_v2 dcontext;
+} __packed;
+
+/* See MS-SMB2 2.2.14.2.12 */
+struct durable_context_v2_rsp {
+ __le32 Timeout;
+ __le32 Flags; /* see SMB2_DHANDLE_FLAG_PERSISTENT */
+} __packed;
+
+struct create_durable_rsp_v2 {
+ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ struct durable_context_v2_rsp dcontext;
+} __packed;
+
/* See MS-SMB2 2.2.14.2.5 */
struct create_mxac_rsp {
struct create_context_hdr ccontext;
@@ -1388,6 +1432,45 @@ struct smb2_ioctl_req {
__u8 Buffer[];
} __packed;
+/* See MS-SMB2 2.2.31.1.1 */
+struct srv_copychunk {
+ __le64 SourceOffset;
+ __le64 TargetOffset;
+ __le32 Length;
+ __le32 Reserved;
+} __packed;
+
+#define COPY_CHUNK_RES_KEY_SIZE 24
+
+/* See MS-SMB2 2.2.31.1 */
+/* this goes in the ioctl buffer when doing a copychunk request */
+struct copychunk_ioctl_req {
+ union {
+ char SourceKey[COPY_CHUNK_RES_KEY_SIZE];
+ __le64 SourceKeyU64[3];
+ };
+ __le32 ChunkCount;
+ __le32 Reserved;
+ struct srv_copychunk Chunks[] __counted_by_le(ChunkCount);
+} __packed;
+
+/* See MS-SMB2 2.2.32.1 */
+struct copychunk_ioctl_rsp {
+ __le32 ChunksWritten;
+ __le32 ChunkBytesWritten;
+ __le32 TotalBytesWritten;
+} __packed;
+
+/* See MS-SMB2 2.2.32.3 */
+struct resume_key_ioctl_rsp {
+ union {
+ char ResumeKey[COPY_CHUNK_RES_KEY_SIZE];
+ __u64 ResumeKeyU64[3];
+ };
+ __le32 ContextLength; /* MBZ */
+ char Context[]; /* ignored, Windows sets to 4 bytes of zero */
+} __packed;
+
struct smb2_ioctl_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 49 */
@@ -1404,6 +1487,41 @@ struct smb2_ioctl_rsp {
__u8 Buffer[];
} __packed;
+/* See MS-SMB2 2.2.32.5.1.1 */
+struct smb_sockaddr_in {
+ __be16 Port;
+ __be32 IPv4Address;
+ __u8 Reserved[8];
+} __packed;
+
+/* See MS-SMB2 2.2.32.5.1.2 */
+struct smb_sockaddr_in6 {
+ __be16 Port;
+ __be32 FlowInfo;
+ __u8 IPv6Address[16];
+ __be32 ScopeId;
+} __packed;
+
+/* See MS-SMB2 2.2.32.5 and MS-SMB2 2.2.32.5.1 */
+#define RSS_CAPABLE cpu_to_le32(0x00000001)
+#define RDMA_CAPABLE cpu_to_le32(0x00000002)
+#define INTERNETWORK cpu_to_le16(0x0002)
+#define INTERNETWORKV6 cpu_to_le16(0x0017)
+struct network_interface_info_ioctl_rsp {
+ __le32 Next; /* next interface. zero if this is last one */
+ __le32 IfIndex;
+ __le32 Capability; /* RSS or RDMA Capable */
+ __le32 Reserved;
+ __le64 LinkSpeed;
+ union {
+ char SockAddr_Storage[128];
+ struct {
+ __le16 Family;
+ __u8 Buffer[126];
+ };
+ };
+} __packed;
+
/* this goes in the ioctl buffer when doing FSCTL_SET_ZERO_DATA */
struct file_zero_data_information {
__le64 FileOffset;
@@ -1893,6 +2011,151 @@ struct smb2_lease_ack {
__le64 LeaseDuration;
} __packed;
+/*
+ * See MS-CIFS 2.2.3.1
+ * MS-SMB 2.2.3.1
+ */
+struct smb_hdr {
+ __be32 smb_buf_length; /* BB length is only two (rarely three) bytes,
+ with one or two byte "type" preceding it that will be
+ zero - we could mask the type byte off */
+ __u8 Protocol[4];
+ __u8 Command;
+ union {
+ struct {
+ __u8 ErrorClass;
+ __u8 Reserved;
+ __le16 Error;
+ } __packed DosError;
+ __le32 CifsError;
+ } __packed Status;
+ __u8 Flags;
+ __le16 Flags2; /* note: le */
+ __le16 PidHigh;
+ union {
+ struct {
+ __le32 SequenceNumber; /* le */
+ __u32 Reserved; /* zero */
+ } __packed Sequence;
+ __u8 SecuritySignature[8]; /* le */
+ } __packed Signature;
+ __u8 pad[2];
+ __u16 Tid;
+ __le16 Pid;
+ __u16 Uid;
+ __le16 Mid;
+ __u8 WordCount;
+} __packed;
+
#define OP_BREAK_STRUCT_SIZE_20 24
#define OP_BREAK_STRUCT_SIZE_21 36
+
+/*
+ * See MS-SMB2 2.2.13.1.1
+ * MS-SMB 2.2.1.4.1
+ * These are the file access permission bits defined in CIFS for the
+ * NTCreateAndX as well as the level 0x107
+ * TRANS2_QUERY_PATH_INFORMATION API. The level 0x107, SMB_QUERY_FILE_ALL_INFO
+ * responds with the AccessFlags.
+ * The AccessFlags specifies the access permissions a caller has to the
+ * file and can have any suitable combination of the following values:
+ */
+#define FILE_READ_DATA 0x00000001 /* Data can be read from the file */
+ /* or directory child entries can */
+ /* be listed together with the */
+ /* associated child attributes */
+ /* (so the FILE_READ_ATTRIBUTES on */
+ /* the child entry is not needed) */
+#define FILE_WRITE_DATA 0x00000002 /* Data can be written to the file */
+ /* or new file can be created in */
+ /* the directory */
+#define FILE_APPEND_DATA 0x00000004 /* Data can be appended to the file */
+ /* (for non-local files over SMB it */
+ /* is same as FILE_WRITE_DATA) */
+ /* or new subdirectory can be */
+ /* created in the directory */
+#define FILE_READ_EA 0x00000008 /* Extended attributes associated */
+ /* with the file can be read */
+#define FILE_WRITE_EA 0x00000010 /* Extended attributes associated */
+ /* with the file can be written */
+#define FILE_EXECUTE 0x00000020 /*Data can be read into memory from */
+ /* the file using system paging I/O */
+ /* for executing the file / script */
+ /* or right to traverse directory */
+ /* (but by default all users have */
+ /* directory bypass traverse */
+ /* privilege and do not need this */
+ /* permission on directories at all)*/
+#define FILE_DELETE_CHILD 0x00000040 /* Child entry can be deleted from */
+ /* the directory (so the DELETE on */
+ /* the child entry is not needed) */
+#define FILE_READ_ATTRIBUTES 0x00000080 /* Attributes associated with the */
+ /* file or directory can be read */
+#define FILE_WRITE_ATTRIBUTES 0x00000100 /* Attributes associated with the */
+ /* file or directory can be written */
+#define DELETE 0x00010000 /* The file or dir can be deleted */
+#define READ_CONTROL 0x00020000 /* The discretionary access control */
+ /* list and ownership associated */
+ /* with the file or dir can be read */
+#define WRITE_DAC 0x00040000 /* The discretionary access control */
+ /* list associated with the file or */
+ /* directory can be written */
+#define WRITE_OWNER 0x00080000 /* Ownership information associated */
+ /* with the file/dir can be written */
+#define SYNCHRONIZE 0x00100000 /* The file handle can waited on to */
+ /* synchronize with the completion */
+ /* of an input/output request */
+#define SYSTEM_SECURITY 0x01000000 /* The system access control list */
+ /* associated with the file or */
+ /* directory can be read or written */
+ /* (cannot be in DACL, can in SACL) */
+#define MAXIMUM_ALLOWED 0x02000000 /* Maximal subset of GENERIC_ALL */
+ /* permissions which can be granted */
+ /* (cannot be in DACL nor SACL) */
+#define GENERIC_ALL 0x10000000 /* Same as: GENERIC_EXECUTE | */
+ /* GENERIC_WRITE | */
+ /* GENERIC_READ | */
+ /* FILE_DELETE_CHILD | */
+ /* DELETE | */
+ /* WRITE_DAC | */
+ /* WRITE_OWNER */
+ /* So GENERIC_ALL contains all bits */
+ /* mentioned above except these two */
+ /* SYSTEM_SECURITY MAXIMUM_ALLOWED */
+#define GENERIC_EXECUTE 0x20000000 /* Same as: FILE_EXECUTE | */
+ /* FILE_READ_ATTRIBUTES | */
+ /* READ_CONTROL | */
+ /* SYNCHRONIZE */
+#define GENERIC_WRITE 0x40000000 /* Same as: FILE_WRITE_DATA | */
+ /* FILE_APPEND_DATA | */
+ /* FILE_WRITE_EA | */
+ /* FILE_WRITE_ATTRIBUTES | */
+ /* READ_CONTROL | */
+ /* SYNCHRONIZE */
+#define GENERIC_READ 0x80000000 /* Same as: FILE_READ_DATA | */
+ /* FILE_READ_EA | */
+ /* FILE_READ_ATTRIBUTES | */
+ /* READ_CONTROL | */
+ /* SYNCHRONIZE */
+
+/* Combinations of file access permission bits */
+#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES)
+#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \
+ | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)
+#define FILE_EXEC_RIGHTS (FILE_EXECUTE)
+#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \
+ | FILE_READ_ATTRIBUTES \
+ | FILE_WRITE_ATTRIBUTES \
+ | DELETE | READ_CONTROL | WRITE_DAC \
+ | WRITE_OWNER | SYNCHRONIZE)
+#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \
+ | READ_CONTROL | SYNCHRONIZE)
+
+/* See MS-CIFS 2.2.4.52.1 */
+typedef struct smb_negotiate_req {
+ struct smb_hdr hdr; /* wct = 0 */
+ __le16 ByteCount;
+ unsigned char DialectsArray[];
+} __packed SMB_NEGOTIATE_REQ;
+
#endif /* _COMMON_SMB2PDU_H */
diff --git a/fs/smb/common/smbacl.h b/fs/smb/common/smbacl.h
index a624ec9e4a14..70bba5ff7fc1 100644
--- a/fs/smb/common/smbacl.h
+++ b/fs/smb/common/smbacl.h
@@ -92,14 +92,14 @@ struct smb_ntsd {
__le32 gsidoffset;
__le32 sacloffset;
__le32 dacloffset;
-} __attribute__((packed));
+} __packed;
struct smb_sid {
__u8 revision; /* revision level */
__u8 num_subauth;
__u8 authority[NUM_AUTHS];
__le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */
-} __attribute__((packed));
+} __packed;
/* size of a struct smb_sid, sans sub_auth array */
#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS)
@@ -109,7 +109,7 @@ struct smb_acl {
__le16 size;
__le16 num_aces;
__le16 reserved;
-} __attribute__((packed));
+} __packed;
struct smb_ace {
__u8 type; /* see above and MS-DTYP 2.4.4.1 */
@@ -117,6 +117,6 @@ struct smb_ace {
__le16 size;
__le32 access_req;
struct smb_sid sid; /* ie UUID of user or group who gets these perms */
-} __attribute__((packed));
+} __packed;
#endif /* _COMMON_SMBACL_H */
diff --git a/fs/smb/common/smbdirect/smbdirect_socket.h b/fs/smb/common/smbdirect/smbdirect_socket.h
index ee5a90d691c8..384b19177e1c 100644
--- a/fs/smb/common/smbdirect/smbdirect_socket.h
+++ b/fs/smb/common/smbdirect/smbdirect_socket.h
@@ -74,6 +74,19 @@ const char *smbdirect_socket_status_string(enum smbdirect_socket_status status)
return "<unknown>";
}
+/*
+ * This can be used with %1pe to print errors as strings or '0'
+ * And it avoids warnings like: warn: passing zero to 'ERR_PTR'
+ * from smatch -p=kernel --pedantic
+ */
+static __always_inline
+const void * __must_check SMBDIRECT_DEBUG_ERR_PTR(long error)
+{
+ if (error == 0)
+ return NULL;
+ return ERR_PTR(error);
+}
+
enum smbdirect_keepalive_status {
SMBDIRECT_KEEPALIVE_NONE,
SMBDIRECT_KEEPALIVE_PENDING,
@@ -381,6 +394,44 @@ static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc)
init_waitqueue_head(&sc->mr_io.cleanup.wait_queue);
}
+#define __SMBDIRECT_CHECK_STATUS_FAILED(__sc, __expected_status, __error_cmd, __unexpected_cmd) ({ \
+ bool __failed = false; \
+ if (unlikely((__sc)->first_error)) { \
+ __failed = true; \
+ __error_cmd \
+ } else if (unlikely((__sc)->status != (__expected_status))) { \
+ __failed = true; \
+ __unexpected_cmd \
+ } \
+ __failed; \
+})
+
+#define __SMBDIRECT_CHECK_STATUS_WARN(__sc, __expected_status, __unexpected_cmd) \
+ __SMBDIRECT_CHECK_STATUS_FAILED(__sc, __expected_status, \
+ , \
+ { \
+ const struct sockaddr_storage *__src = NULL; \
+ const struct sockaddr_storage *__dst = NULL; \
+ if ((__sc)->rdma.cm_id) { \
+ __src = &(__sc)->rdma.cm_id->route.addr.src_addr; \
+ __dst = &(__sc)->rdma.cm_id->route.addr.dst_addr; \
+ } \
+ WARN_ONCE(1, \
+ "expected[%s] != %s first_error=%1pe local=%pISpsfc remote=%pISpsfc\n", \
+ smbdirect_socket_status_string(__expected_status), \
+ smbdirect_socket_status_string((__sc)->status), \
+ SMBDIRECT_DEBUG_ERR_PTR((__sc)->first_error), \
+ __src, __dst); \
+ __unexpected_cmd \
+ })
+
+#define SMBDIRECT_CHECK_STATUS_WARN(__sc, __expected_status) \
+ __SMBDIRECT_CHECK_STATUS_WARN(__sc, __expected_status, /* nothing */)
+
+#define SMBDIRECT_CHECK_STATUS_DISCONNECT(__sc, __expected_status) \
+ __SMBDIRECT_CHECK_STATUS_WARN(__sc, __expected_status, \
+ __SMBDIRECT_SOCKET_DISCONNECT(__sc);)
+
struct smbdirect_send_io {
struct smbdirect_socket *socket;
struct ib_cqe cqe;
diff --git a/fs/smb/common/smbglob.h b/fs/smb/common/smbglob.h
new file mode 100644
index 000000000000..7853b5771128
--- /dev/null
+++ b/fs/smb/common/smbglob.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+/*
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2008
+ * 2018 Samsung Electronics Co., Ltd.
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Jeremy Allison (jra@samba.org)
+ * Namjae Jeon (linkinjeon@kernel.org)
+ *
+ */
+#ifndef _COMMON_SMB_GLOB_H
+#define _COMMON_SMB_GLOB_H
+
+#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff)
+
+struct smb_version_values {
+ char *version_string;
+ __u16 protocol_id;
+ __le16 lock_cmd;
+ __u32 req_capabilities;
+ __u32 max_read_size;
+ __u32 max_write_size;
+ __u32 max_trans_size;
+ __u32 max_credits;
+ __u32 large_lock_type;
+ __u32 exclusive_lock_type;
+ __u32 shared_lock_type;
+ __u32 unlock_lock_type;
+ size_t header_preamble_size;
+ size_t header_size;
+ size_t max_header_size;
+ size_t read_rsp_size;
+ unsigned int cap_unix;
+ unsigned int cap_nt_find;
+ unsigned int cap_large_files;
+ unsigned int cap_unicode;
+ __u16 signing_enabled;
+ __u16 signing_required;
+ size_t create_lease_size;
+ size_t create_durable_size;
+ size_t create_durable_v2_size;
+ size_t create_mxac_size;
+ size_t create_disk_id_size;
+ size_t create_posix_size;
+};
+
+static inline unsigned int get_rfc1002_len(void *buf)
+{
+ return be32_to_cpu(*((__be32 *)buf)) & 0xffffff;
+}
+
+static inline void inc_rfc1001_len(void *buf, int count)
+{
+ be32_add_cpu((__be32 *)buf, count);
+}
+
+#define SMB1_VERSION_STRING "1.0"
+#define SMB20_VERSION_STRING "2.0"
+#define SMB21_VERSION_STRING "2.1"
+#define SMBDEFAULT_VERSION_STRING "default"
+#define SMB3ANY_VERSION_STRING "3"
+#define SMB30_VERSION_STRING "3.0"
+#define SMB302_VERSION_STRING "3.02"
+#define ALT_SMB302_VERSION_STRING "3.0.2"
+#define SMB311_VERSION_STRING "3.1.1"
+#define ALT_SMB311_VERSION_STRING "3.11"
+
+#define CIFS_DEFAULT_IOSIZE (1024 * 1024)
+
+#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */
+
+#endif /* _COMMON_SMB_GLOB_H */
diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig
index 098cac98d31e..2775162c535c 100644
--- a/fs/smb/server/Kconfig
+++ b/fs/smb/server/Kconfig
@@ -7,15 +7,13 @@ config SMB_SERVER
select NLS_UTF8
select NLS_UCS2_UTILS
select CRYPTO
- select CRYPTO_MD5
- select CRYPTO_HMAC
select CRYPTO_ECB
select CRYPTO_LIB_ARC4
select CRYPTO_LIB_DES
+ select CRYPTO_LIB_MD5
select CRYPTO_LIB_SHA256
- select CRYPTO_SHA256
+ select CRYPTO_LIB_SHA512
select CRYPTO_CMAC
- select CRYPTO_SHA512
select CRYPTO_AEAD2
select CRYPTO_CCM
select CRYPTO_GCM
diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c
index b4020bb55a26..f2767c4b5132 100644
--- a/fs/smb/server/auth.c
+++ b/fs/smb/server/auth.c
@@ -13,6 +13,8 @@
#include <linux/xattr.h>
#include <crypto/hash.h>
#include <crypto/aead.h>
+#include <crypto/md5.h>
+#include <crypto/sha2.h>
#include <linux/random.h>
#include <linux/scatterlist.h>
@@ -69,85 +71,16 @@ void ksmbd_copy_gss_neg_header(void *buf)
memcpy(buf, NEGOTIATE_GSS_HEADER, AUTH_GSS_LENGTH);
}
-/**
- * ksmbd_gen_sess_key() - function to generate session key
- * @sess: session of connection
- * @hash: source hash value to be used for find session key
- * @hmac: source hmac value to be used for finding session key
- *
- */
-static int ksmbd_gen_sess_key(struct ksmbd_session *sess, char *hash,
- char *hmac)
-{
- struct ksmbd_crypto_ctx *ctx;
- int rc;
-
- ctx = ksmbd_crypto_ctx_find_hmacmd5();
- if (!ctx) {
- ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n");
- return -ENOMEM;
- }
-
- rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx),
- hash,
- CIFS_HMAC_MD5_HASH_SIZE);
- if (rc) {
- ksmbd_debug(AUTH, "hmacmd5 set key fail error %d\n", rc);
- goto out;
- }
-
- rc = crypto_shash_init(CRYPTO_HMACMD5(ctx));
- if (rc) {
- ksmbd_debug(AUTH, "could not init hmacmd5 error %d\n", rc);
- goto out;
- }
-
- rc = crypto_shash_update(CRYPTO_HMACMD5(ctx),
- hmac,
- SMB2_NTLMV2_SESSKEY_SIZE);
- if (rc) {
- ksmbd_debug(AUTH, "Could not update with response error %d\n", rc);
- goto out;
- }
-
- rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), sess->sess_key);
- if (rc) {
- ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", rc);
- goto out;
- }
-
-out:
- ksmbd_release_crypto_ctx(ctx);
- return rc;
-}
-
static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess,
char *ntlmv2_hash, char *dname)
{
int ret, len, conv_len;
wchar_t *domain = NULL;
__le16 *uniname = NULL;
- struct ksmbd_crypto_ctx *ctx;
+ struct hmac_md5_ctx ctx;
- ctx = ksmbd_crypto_ctx_find_hmacmd5();
- if (!ctx) {
- ksmbd_debug(AUTH, "can't generate ntlmv2 hash\n");
- return -ENOMEM;
- }
-
- ret = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx),
- user_passkey(sess->user),
+ hmac_md5_init_usingrawkey(&ctx, user_passkey(sess->user),
CIFS_ENCPWD_SIZE);
- if (ret) {
- ksmbd_debug(AUTH, "Could not set NT Hash as a key\n");
- goto out;
- }
-
- ret = crypto_shash_init(CRYPTO_HMACMD5(ctx));
- if (ret) {
- ksmbd_debug(AUTH, "could not init hmacmd5\n");
- goto out;
- }
/* convert user_name to unicode */
len = strlen(user_name(sess->user));
@@ -165,13 +98,7 @@ static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess,
}
UniStrupr(uniname);
- ret = crypto_shash_update(CRYPTO_HMACMD5(ctx),
- (char *)uniname,
- UNICODE_LEN(conv_len));
- if (ret) {
- ksmbd_debug(AUTH, "Could not update with user\n");
- goto out;
- }
+ hmac_md5_update(&ctx, (const u8 *)uniname, UNICODE_LEN(conv_len));
/* Convert domain name or conn name to unicode and uppercase */
len = strlen(dname);
@@ -188,21 +115,12 @@ static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess,
goto out;
}
- ret = crypto_shash_update(CRYPTO_HMACMD5(ctx),
- (char *)domain,
- UNICODE_LEN(conv_len));
- if (ret) {
- ksmbd_debug(AUTH, "Could not update with domain\n");
- goto out;
- }
-
- ret = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_hash);
- if (ret)
- ksmbd_debug(AUTH, "Could not generate md5 hash\n");
+ hmac_md5_update(&ctx, (const u8 *)domain, UNICODE_LEN(conv_len));
+ hmac_md5_final(&ctx, ntlmv2_hash);
+ ret = 0;
out:
kfree(uniname);
kfree(domain);
- ksmbd_release_crypto_ctx(ctx);
return ret;
}
@@ -223,73 +141,33 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess,
{
char ntlmv2_hash[CIFS_ENCPWD_SIZE];
char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE];
- struct ksmbd_crypto_ctx *ctx = NULL;
- char *construct = NULL;
- int rc, len;
-
- rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name);
- if (rc) {
- ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc);
- goto out;
- }
-
- ctx = ksmbd_crypto_ctx_find_hmacmd5();
- if (!ctx) {
- ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n");
- return -ENOMEM;
- }
-
- rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx),
- ntlmv2_hash,
- CIFS_HMAC_MD5_HASH_SIZE);
- if (rc) {
- ksmbd_debug(AUTH, "Could not set NTLMV2 Hash as a key\n");
- goto out;
- }
+ struct hmac_md5_ctx ctx;
+ int rc;
- rc = crypto_shash_init(CRYPTO_HMACMD5(ctx));
- if (rc) {
- ksmbd_debug(AUTH, "Could not init hmacmd5\n");
- goto out;
+ if (fips_enabled) {
+ ksmbd_debug(AUTH, "NTLMv2 support is disabled due to FIPS\n");
+ return -EOPNOTSUPP;
}
- len = CIFS_CRYPTO_KEY_SIZE + blen;
- construct = kzalloc(len, KSMBD_DEFAULT_GFP);
- if (!construct) {
- rc = -ENOMEM;
- goto out;
- }
-
- memcpy(construct, cryptkey, CIFS_CRYPTO_KEY_SIZE);
- memcpy(construct + CIFS_CRYPTO_KEY_SIZE, &ntlmv2->blob_signature, blen);
-
- rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), construct, len);
+ rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name);
if (rc) {
- ksmbd_debug(AUTH, "Could not update with response\n");
- goto out;
+ ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc);
+ return rc;
}
- rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_rsp);
- if (rc) {
- ksmbd_debug(AUTH, "Could not generate md5 hash\n");
- goto out;
- }
- ksmbd_release_crypto_ctx(ctx);
- ctx = NULL;
+ hmac_md5_init_usingrawkey(&ctx, ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
+ hmac_md5_update(&ctx, cryptkey, CIFS_CRYPTO_KEY_SIZE);
+ hmac_md5_update(&ctx, (const u8 *)&ntlmv2->blob_signature, blen);
+ hmac_md5_final(&ctx, ntlmv2_rsp);
- rc = ksmbd_gen_sess_key(sess, ntlmv2_hash, ntlmv2_rsp);
- if (rc) {
- ksmbd_debug(AUTH, "Could not generate sess key\n");
- goto out;
- }
+ /* Generate the session key */
+ hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE,
+ ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE,
+ sess->sess_key);
if (memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE) != 0)
- rc = -EINVAL;
-out:
- if (ctx)
- ksmbd_release_crypto_ctx(ctx);
- kfree(construct);
- return rc;
+ return -EINVAL;
+ return 0;
}
/**
@@ -589,46 +467,16 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
* @sig: signature value generated for client request packet
*
*/
-int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
- int n_vec, char *sig)
+void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
+ int n_vec, char *sig)
{
- struct ksmbd_crypto_ctx *ctx;
- int rc, i;
-
- ctx = ksmbd_crypto_ctx_find_hmacsha256();
- if (!ctx) {
- ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n");
- return -ENOMEM;
- }
-
- rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx),
- key,
- SMB2_NTLMV2_SESSKEY_SIZE);
- if (rc)
- goto out;
-
- rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx));
- if (rc) {
- ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc);
- goto out;
- }
+ struct hmac_sha256_ctx ctx;
+ int i;
- for (i = 0; i < n_vec; i++) {
- rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx),
- iov[i].iov_base,
- iov[i].iov_len);
- if (rc) {
- ksmbd_debug(AUTH, "hmacsha256 update error %d\n", rc);
- goto out;
- }
- }
-
- rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), sig);
- if (rc)
- ksmbd_debug(AUTH, "hmacsha256 generation error %d\n", rc);
-out:
- ksmbd_release_crypto_ctx(ctx);
- return rc;
+ hmac_sha256_init_usingrawkey(&ctx, key, SMB2_NTLMV2_SESSKEY_SIZE);
+ for (i = 0; i < n_vec; i++)
+ hmac_sha256_update(&ctx, iov[i].iov_base, iov[i].iov_len);
+ hmac_sha256_final(&ctx, sig);
}
/**
@@ -688,98 +536,39 @@ struct derivation {
bool binding;
};
-static int generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess,
- struct kvec label, struct kvec context, __u8 *key,
- unsigned int key_size)
+static void generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess,
+ struct kvec label, struct kvec context, __u8 *key,
+ unsigned int key_size)
{
unsigned char zero = 0x0;
__u8 i[4] = {0, 0, 0, 1};
__u8 L128[4] = {0, 0, 0, 128};
__u8 L256[4] = {0, 0, 1, 0};
- int rc;
unsigned char prfhash[SMB2_HMACSHA256_SIZE];
- unsigned char *hashptr = prfhash;
- struct ksmbd_crypto_ctx *ctx;
-
- memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
- memset(key, 0x0, key_size);
-
- ctx = ksmbd_crypto_ctx_find_hmacsha256();
- if (!ctx) {
- ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n");
- return -ENOMEM;
- }
-
- rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx),
- sess->sess_key,
- SMB2_NTLMV2_SESSKEY_SIZE);
- if (rc)
- goto smb3signkey_ret;
-
- rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx));
- if (rc) {
- ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc);
- goto smb3signkey_ret;
- }
-
- rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), i, 4);
- if (rc) {
- ksmbd_debug(AUTH, "could not update with n\n");
- goto smb3signkey_ret;
- }
+ struct hmac_sha256_ctx ctx;
- rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx),
- label.iov_base,
- label.iov_len);
- if (rc) {
- ksmbd_debug(AUTH, "could not update with label\n");
- goto smb3signkey_ret;
- }
-
- rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), &zero, 1);
- if (rc) {
- ksmbd_debug(AUTH, "could not update with zero\n");
- goto smb3signkey_ret;
- }
-
- rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx),
- context.iov_base,
- context.iov_len);
- if (rc) {
- ksmbd_debug(AUTH, "could not update with context\n");
- goto smb3signkey_ret;
- }
+ hmac_sha256_init_usingrawkey(&ctx, sess->sess_key,
+ SMB2_NTLMV2_SESSKEY_SIZE);
+ hmac_sha256_update(&ctx, i, 4);
+ hmac_sha256_update(&ctx, label.iov_base, label.iov_len);
+ hmac_sha256_update(&ctx, &zero, 1);
+ hmac_sha256_update(&ctx, context.iov_base, context.iov_len);
if (key_size == SMB3_ENC_DEC_KEY_SIZE &&
(conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM ||
conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM))
- rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L256, 4);
+ hmac_sha256_update(&ctx, L256, 4);
else
- rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L128, 4);
- if (rc) {
- ksmbd_debug(AUTH, "could not update with L\n");
- goto smb3signkey_ret;
- }
-
- rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), hashptr);
- if (rc) {
- ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n",
- rc);
- goto smb3signkey_ret;
- }
+ hmac_sha256_update(&ctx, L128, 4);
- memcpy(key, hashptr, key_size);
-
-smb3signkey_ret:
- ksmbd_release_crypto_ctx(ctx);
- return rc;
+ hmac_sha256_final(&ctx, prfhash);
+ memcpy(key, prfhash, key_size);
}
static int generate_smb3signingkey(struct ksmbd_session *sess,
struct ksmbd_conn *conn,
const struct derivation *signing)
{
- int rc;
struct channel *chann;
char *key;
@@ -792,10 +581,8 @@ static int generate_smb3signingkey(struct ksmbd_session *sess,
else
key = sess->smb3signingkey;
- rc = generate_key(conn, sess, signing->label, signing->context, key,
- SMB3_SIGN_KEY_SIZE);
- if (rc)
- return rc;
+ generate_key(conn, sess, signing->label, signing->context, key,
+ SMB3_SIGN_KEY_SIZE);
if (!(conn->dialect >= SMB30_PROT_ID && signing->binding))
memcpy(chann->smb3signingkey, key, SMB3_SIGN_KEY_SIZE);
@@ -851,23 +638,17 @@ struct derivation_twin {
struct derivation decryption;
};
-static int generate_smb3encryptionkey(struct ksmbd_conn *conn,
- struct ksmbd_session *sess,
- const struct derivation_twin *ptwin)
+static void generate_smb3encryptionkey(struct ksmbd_conn *conn,
+ struct ksmbd_session *sess,
+ const struct derivation_twin *ptwin)
{
- int rc;
-
- rc = generate_key(conn, sess, ptwin->encryption.label,
- ptwin->encryption.context, sess->smb3encryptionkey,
- SMB3_ENC_DEC_KEY_SIZE);
- if (rc)
- return rc;
+ generate_key(conn, sess, ptwin->encryption.label,
+ ptwin->encryption.context, sess->smb3encryptionkey,
+ SMB3_ENC_DEC_KEY_SIZE);
- rc = generate_key(conn, sess, ptwin->decryption.label,
- ptwin->decryption.context,
- sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE);
- if (rc)
- return rc;
+ generate_key(conn, sess, ptwin->decryption.label,
+ ptwin->decryption.context,
+ sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE);
ksmbd_debug(AUTH, "dumping generated AES encryption keys\n");
ksmbd_debug(AUTH, "Cipher type %d\n", conn->cipher_type);
@@ -886,11 +667,10 @@ static int generate_smb3encryptionkey(struct ksmbd_conn *conn,
ksmbd_debug(AUTH, "ServerOut Key %*ph\n",
SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3decryptionkey);
}
- return 0;
}
-int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn,
- struct ksmbd_session *sess)
+void ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn,
+ struct ksmbd_session *sess)
{
struct derivation_twin twin;
struct derivation *d;
@@ -907,11 +687,11 @@ int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn,
d->context.iov_base = "ServerIn ";
d->context.iov_len = 10;
- return generate_smb3encryptionkey(conn, sess, &twin);
+ generate_smb3encryptionkey(conn, sess, &twin);
}
-int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn,
- struct ksmbd_session *sess)
+void ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn,
+ struct ksmbd_session *sess)
{
struct derivation_twin twin;
struct derivation *d;
@@ -928,54 +708,26 @@ int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn,
d->context.iov_base = sess->Preauth_HashValue;
d->context.iov_len = 64;
- return generate_smb3encryptionkey(conn, sess, &twin);
+ generate_smb3encryptionkey(conn, sess, &twin);
}
int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf,
__u8 *pi_hash)
{
- int rc;
struct smb2_hdr *rcv_hdr = smb2_get_msg(buf);
char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId;
int msg_size = get_rfc1002_len(buf);
- struct ksmbd_crypto_ctx *ctx = NULL;
+ struct sha512_ctx sha_ctx;
if (conn->preauth_info->Preauth_HashId !=
SMB2_PREAUTH_INTEGRITY_SHA512)
return -EINVAL;
- ctx = ksmbd_crypto_ctx_find_sha512();
- if (!ctx) {
- ksmbd_debug(AUTH, "could not alloc sha512\n");
- return -ENOMEM;
- }
-
- rc = crypto_shash_init(CRYPTO_SHA512(ctx));
- if (rc) {
- ksmbd_debug(AUTH, "could not init shashn");
- goto out;
- }
-
- rc = crypto_shash_update(CRYPTO_SHA512(ctx), pi_hash, 64);
- if (rc) {
- ksmbd_debug(AUTH, "could not update with n\n");
- goto out;
- }
-
- rc = crypto_shash_update(CRYPTO_SHA512(ctx), all_bytes_msg, msg_size);
- if (rc) {
- ksmbd_debug(AUTH, "could not update with n\n");
- goto out;
- }
-
- rc = crypto_shash_final(CRYPTO_SHA512(ctx), pi_hash);
- if (rc) {
- ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc);
- goto out;
- }
-out:
- ksmbd_release_crypto_ctx(ctx);
- return rc;
+ sha512_init(&sha_ctx);
+ sha512_update(&sha_ctx, pi_hash, 64);
+ sha512_update(&sha_ctx, all_bytes_msg, msg_size);
+ sha512_final(&sha_ctx, pi_hash);
+ return 0;
}
static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id,
diff --git a/fs/smb/server/auth.h b/fs/smb/server/auth.h
index 6879a1bd1b91..6d351d61b0e5 100644
--- a/fs/smb/server/auth.h
+++ b/fs/smb/server/auth.h
@@ -52,18 +52,18 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob,
struct ksmbd_conn *conn);
int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
int in_len, char *out_blob, int *out_len);
-int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
- int n_vec, char *sig);
+void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
+ int n_vec, char *sig);
int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
int n_vec, char *sig);
int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess,
struct ksmbd_conn *conn);
int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess,
struct ksmbd_conn *conn);
-int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn,
- struct ksmbd_session *sess);
-int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn,
+void ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn,
struct ksmbd_session *sess);
+void ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn,
+ struct ksmbd_session *sess);
int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf,
__u8 *pi_hash);
#endif
diff --git a/fs/smb/server/crypto_ctx.c b/fs/smb/server/crypto_ctx.c
index 80bd68c8635e..fe29d186baf6 100644
--- a/fs/smb/server/crypto_ctx.c
+++ b/fs/smb/server/crypto_ctx.c
@@ -66,18 +66,9 @@ static struct shash_desc *alloc_shash_desc(int id)
struct shash_desc *shash;
switch (id) {
- case CRYPTO_SHASH_HMACMD5:
- tfm = crypto_alloc_shash("hmac(md5)", 0, 0);
- break;
- case CRYPTO_SHASH_HMACSHA256:
- tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
- break;
case CRYPTO_SHASH_CMACAES:
tfm = crypto_alloc_shash("cmac(aes)", 0, 0);
break;
- case CRYPTO_SHASH_SHA512:
- tfm = crypto_alloc_shash("sha512", 0, 0);
- break;
default:
return NULL;
}
@@ -180,26 +171,11 @@ static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id)
return NULL;
}
-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void)
-{
- return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5);
-}
-
-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void)
-{
- return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACSHA256);
-}
-
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void)
{
return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES);
}
-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void)
-{
- return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA512);
-}
-
static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id)
{
struct ksmbd_crypto_ctx *ctx;
diff --git a/fs/smb/server/crypto_ctx.h b/fs/smb/server/crypto_ctx.h
index ac64801d52d3..b9476ed520ae 100644
--- a/fs/smb/server/crypto_ctx.h
+++ b/fs/smb/server/crypto_ctx.h
@@ -10,10 +10,7 @@
#include <crypto/aead.h>
enum {
- CRYPTO_SHASH_HMACMD5 = 0,
- CRYPTO_SHASH_HMACSHA256,
- CRYPTO_SHASH_CMACAES,
- CRYPTO_SHASH_SHA512,
+ CRYPTO_SHASH_CMACAES = 0,
CRYPTO_SHASH_MAX,
};
@@ -35,25 +32,15 @@ struct ksmbd_crypto_ctx {
struct crypto_aead *ccmaes[CRYPTO_AEAD_MAX];
};
-#define CRYPTO_HMACMD5(c) ((c)->desc[CRYPTO_SHASH_HMACMD5])
-#define CRYPTO_HMACSHA256(c) ((c)->desc[CRYPTO_SHASH_HMACSHA256])
#define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES])
-#define CRYPTO_SHA512(c) ((c)->desc[CRYPTO_SHASH_SHA512])
-#define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm)
-#define CRYPTO_HMACSHA256_TFM(c)\
- ((c)->desc[CRYPTO_SHASH_HMACSHA256]->tfm)
#define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm)
-#define CRYPTO_SHA512_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA512]->tfm)
#define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM])
#define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM])
void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx);
-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void);
-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void);
-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void);
void ksmbd_crypto_destroy(void);
diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c
index ecfc57508671..d3483d9c757c 100644
--- a/fs/smb/server/mgmt/tree_connect.c
+++ b/fs/smb/server/mgmt/tree_connect.c
@@ -78,7 +78,6 @@ ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name)
tree_conn->t_state = TREE_NEW;
status.tree_conn = tree_conn;
atomic_set(&tree_conn->refcount, 1);
- init_waitqueue_head(&tree_conn->refcount_q);
ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn,
KSMBD_DEFAULT_GFP));
@@ -100,14 +99,8 @@ out_error:
void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon)
{
- /*
- * Checking waitqueue to releasing tree connect on
- * tree disconnect. waitqueue_active is safe because it
- * uses atomic operation for condition.
- */
- if (!atomic_dec_return(&tcon->refcount) &&
- waitqueue_active(&tcon->refcount_q))
- wake_up(&tcon->refcount_q);
+ if (atomic_dec_and_test(&tcon->refcount))
+ kfree(tcon);
}
int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
@@ -119,14 +112,11 @@ int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
xa_erase(&sess->tree_conns, tree_conn->id);
write_unlock(&sess->tree_conns_lock);
- if (!atomic_dec_and_test(&tree_conn->refcount))
- wait_event(tree_conn->refcount_q,
- atomic_read(&tree_conn->refcount) == 0);
-
ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
ksmbd_release_tree_conn_id(sess, tree_conn->id);
ksmbd_share_config_put(tree_conn->share_conf);
- kfree(tree_conn);
+ if (atomic_dec_and_test(&tree_conn->refcount))
+ kfree(tree_conn);
return ret;
}
diff --git a/fs/smb/server/mgmt/tree_connect.h b/fs/smb/server/mgmt/tree_connect.h
index a42cdd051041..f0023d86716f 100644
--- a/fs/smb/server/mgmt/tree_connect.h
+++ b/fs/smb/server/mgmt/tree_connect.h
@@ -33,7 +33,6 @@ struct ksmbd_tree_connect {
int maximal_access;
bool posix_extensions;
atomic_t refcount;
- wait_queue_head_t refcount_q;
unsigned int t_state;
};
diff --git a/fs/smb/server/misc.c b/fs/smb/server/misc.c
index cb2a11ffb23f..a543ec9d3581 100644
--- a/fs/smb/server/misc.c
+++ b/fs/smb/server/misc.c
@@ -164,6 +164,8 @@ char *convert_to_nt_pathname(struct ksmbd_share_config *share,
{
char *pathname, *ab_pathname, *nt_pathname;
int share_path_len = share->path_sz;
+ size_t ab_pathname_len;
+ int prefix;
pathname = kmalloc(PATH_MAX, KSMBD_DEFAULT_GFP);
if (!pathname)
@@ -180,15 +182,18 @@ char *convert_to_nt_pathname(struct ksmbd_share_config *share,
goto free_pathname;
}
- nt_pathname = kzalloc(strlen(&ab_pathname[share_path_len]) + 2,
- KSMBD_DEFAULT_GFP);
+ ab_pathname_len = strlen(&ab_pathname[share_path_len]);
+ prefix = ab_pathname[share_path_len] == '\0' ? 1 : 0;
+ nt_pathname = kmalloc(prefix + ab_pathname_len + 1, KSMBD_DEFAULT_GFP);
if (!nt_pathname) {
nt_pathname = ERR_PTR(-ENOMEM);
goto free_pathname;
}
- if (ab_pathname[share_path_len] == '\0')
- strcpy(nt_pathname, "/");
- strcat(nt_pathname, &ab_pathname[share_path_len]);
+
+ if (prefix)
+ *nt_pathname = '/';
+ memcpy(nt_pathname + prefix, &ab_pathname[share_path_len],
+ ab_pathname_len + 1);
ksmbd_conv_path_to_windows(nt_pathname);
diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
index a04d5702820d..1f07ebf431d7 100644
--- a/fs/smb/server/oplock.c
+++ b/fs/smb/server/oplock.c
@@ -1617,9 +1617,9 @@ void create_durable_rsp_buf(char *cc)
*/
void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp)
{
- struct create_durable_v2_rsp *buf;
+ struct create_durable_rsp_v2 *buf;
- buf = (struct create_durable_v2_rsp *)cc;
+ buf = (struct create_durable_rsp_v2 *)cc;
memset(buf, 0, sizeof(struct create_durable_rsp));
buf->ccontext.DataOffset = cpu_to_le16(offsetof
(struct create_durable_rsp, Data));
@@ -1633,9 +1633,9 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp)
buf->Name[2] = '2';
buf->Name[3] = 'Q';
- buf->Timeout = cpu_to_le32(fp->durable_timeout);
+ buf->dcontext.Timeout = cpu_to_le32(fp->durable_timeout);
if (fp->is_persistent)
- buf->Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
+ buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
}
/**
diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
index 40420544cc25..3cea16050e4f 100644
--- a/fs/smb/server/server.c
+++ b/fs/smb/server/server.c
@@ -622,13 +622,9 @@ MODULE_AUTHOR("Namjae Jeon <linkinjeon@kernel.org>");
MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER");
MODULE_LICENSE("GPL");
MODULE_SOFTDEP("pre: ecb");
-MODULE_SOFTDEP("pre: hmac");
-MODULE_SOFTDEP("pre: md5");
MODULE_SOFTDEP("pre: nls");
MODULE_SOFTDEP("pre: aes");
MODULE_SOFTDEP("pre: cmac");
-MODULE_SOFTDEP("pre: sha256");
-MODULE_SOFTDEP("pre: sha512");
MODULE_SOFTDEP("pre: aead2");
MODULE_SOFTDEP("pre: ccm");
MODULE_SOFTDEP("pre: gcm");
diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c
index ae501024665e..67a2d7a793f6 100644
--- a/fs/smb/server/smb2misc.c
+++ b/fs/smb/server/smb2misc.c
@@ -460,7 +460,7 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)
}
validate_credit:
- if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) &&
+ if ((work->conn->vals->req_capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) &&
smb2_validate_credit_charge(work->conn, hdr))
return 1;
diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c
index 606aa3c5189a..edd7eca0714a 100644
--- a/fs/smb/server/smb2ops.c
+++ b/fs/smb/server/smb2ops.c
@@ -15,7 +15,7 @@
static struct smb_version_values smb21_server_values = {
.version_string = SMB21_VERSION_STRING,
.protocol_id = SMB21_PROT_ID,
- .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
+ .req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
.max_read_size = SMB21_DEFAULT_IOSIZE,
.max_write_size = SMB21_DEFAULT_IOSIZE,
.max_trans_size = SMB21_DEFAULT_IOSIZE,
@@ -41,7 +41,7 @@ static struct smb_version_values smb21_server_values = {
static struct smb_version_values smb30_server_values = {
.version_string = SMB30_VERSION_STRING,
.protocol_id = SMB30_PROT_ID,
- .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
+ .req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
.max_read_size = SMB3_DEFAULT_IOSIZE,
.max_write_size = SMB3_DEFAULT_IOSIZE,
.max_trans_size = SMB3_DEFAULT_TRANS_SIZE,
@@ -59,7 +59,7 @@ static struct smb_version_values smb30_server_values = {
.cap_large_files = SMB2_LARGE_FILES,
.create_lease_size = sizeof(struct create_lease_v2),
.create_durable_size = sizeof(struct create_durable_rsp),
- .create_durable_v2_size = sizeof(struct create_durable_v2_rsp),
+ .create_durable_v2_size = sizeof(struct create_durable_rsp_v2),
.create_mxac_size = sizeof(struct create_mxac_rsp),
.create_disk_id_size = sizeof(struct create_disk_id_rsp),
.create_posix_size = sizeof(struct create_posix_rsp),
@@ -68,7 +68,7 @@ static struct smb_version_values smb30_server_values = {
static struct smb_version_values smb302_server_values = {
.version_string = SMB302_VERSION_STRING,
.protocol_id = SMB302_PROT_ID,
- .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
+ .req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
.max_read_size = SMB3_DEFAULT_IOSIZE,
.max_write_size = SMB3_DEFAULT_IOSIZE,
.max_trans_size = SMB3_DEFAULT_TRANS_SIZE,
@@ -86,7 +86,7 @@ static struct smb_version_values smb302_server_values = {
.cap_large_files = SMB2_LARGE_FILES,
.create_lease_size = sizeof(struct create_lease_v2),
.create_durable_size = sizeof(struct create_durable_rsp),
- .create_durable_v2_size = sizeof(struct create_durable_v2_rsp),
+ .create_durable_v2_size = sizeof(struct create_durable_rsp_v2),
.create_mxac_size = sizeof(struct create_mxac_rsp),
.create_disk_id_size = sizeof(struct create_disk_id_rsp),
.create_posix_size = sizeof(struct create_posix_rsp),
@@ -95,7 +95,7 @@ static struct smb_version_values smb302_server_values = {
static struct smb_version_values smb311_server_values = {
.version_string = SMB311_VERSION_STRING,
.protocol_id = SMB311_PROT_ID,
- .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
+ .req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
.max_read_size = SMB3_DEFAULT_IOSIZE,
.max_write_size = SMB3_DEFAULT_IOSIZE,
.max_trans_size = SMB3_DEFAULT_TRANS_SIZE,
@@ -113,7 +113,7 @@ static struct smb_version_values smb311_server_values = {
.cap_large_files = SMB2_LARGE_FILES,
.create_lease_size = sizeof(struct create_lease_v2),
.create_durable_size = sizeof(struct create_durable_rsp),
- .create_durable_v2_size = sizeof(struct create_durable_v2_rsp),
+ .create_durable_v2_size = sizeof(struct create_durable_rsp_v2),
.create_mxac_size = sizeof(struct create_mxac_rsp),
.create_disk_id_size = sizeof(struct create_disk_id_rsp),
.create_posix_size = sizeof(struct create_posix_rsp),
@@ -204,7 +204,7 @@ void init_smb2_1_server(struct ksmbd_conn *conn)
conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256_LE;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_LEASING;
}
/**
@@ -221,20 +221,20 @@ void init_smb3_0_server(struct ksmbd_conn *conn)
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_LEASING |
SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION &&
conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
(!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&
conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION))
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
}
/**
@@ -251,19 +251,19 @@ void init_smb3_02_server(struct ksmbd_conn *conn)
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_LEASING |
SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
(!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&
conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION))
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
}
/**
@@ -280,14 +280,14 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_LEASING |
SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
INIT_LIST_HEAD(&conn->preauth_sess_table);
return 0;
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 94454e8826b0..7ea6144c692c 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -282,7 +282,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work)
/* Not setting conn guid rsp->ServerGUID, as it
* not used by client for identifying connection
*/
- rsp->Capabilities = cpu_to_le32(conn->vals->capabilities);
+ rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities);
/* Default Max Message Size till SMB2.0, 64K*/
rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size);
rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size);
@@ -956,7 +956,7 @@ bool smb3_encryption_negotiated(struct ksmbd_conn *conn)
* SMB 3.0 and 3.0.2 dialects use the SMB2_GLOBAL_CAP_ENCRYPTION flag.
* SMB 3.1.1 uses the cipher_type field.
*/
- return (conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) ||
+ return (conn->vals->req_capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) ||
conn->cipher_type;
}
@@ -1210,7 +1210,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
rc = -EINVAL;
goto err_out;
}
- rsp->Capabilities = cpu_to_le32(conn->vals->capabilities);
+ rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities);
/* For stats */
conn->connection_type = conn->dialect;
@@ -1538,12 +1538,7 @@ static int ntlm_authenticate(struct ksmbd_work *work,
if (smb3_encryption_negotiated(conn) &&
!(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
- rc = conn->ops->generate_encryptionkey(conn, sess);
- if (rc) {
- ksmbd_debug(SMB,
- "SMB3 encryption key generation failed\n");
- return -EINVAL;
- }
+ conn->ops->generate_encryptionkey(conn, sess);
sess->enc = true;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)
rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE;
@@ -1640,12 +1635,7 @@ static int krb5_authenticate(struct ksmbd_work *work,
if (smb3_encryption_negotiated(conn) &&
!(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
- retval = conn->ops->generate_encryptionkey(conn, sess);
- if (retval) {
- ksmbd_debug(SMB,
- "SMB3 encryption key generation failed\n");
- return -EINVAL;
- }
+ conn->ops->generate_encryptionkey(conn, sess);
sess->enc = true;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)
rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE;
@@ -2168,7 +2158,7 @@ static int smb2_create_open_flags(bool file_present, __le32 access,
* smb2_tree_disconnect() - handler for smb tree connect request
* @work: smb work containing request buffer
*
- * Return: 0
+ * Return: 0 on success, otherwise error
*/
int smb2_tree_disconnect(struct ksmbd_work *work)
{
@@ -2200,7 +2190,6 @@ int smb2_tree_disconnect(struct ksmbd_work *work)
goto err_out;
}
- WARN_ON_ONCE(atomic_dec_and_test(&tcon->refcount));
tcon->t_state = TREE_DISCONNECTED;
write_unlock(&sess->tree_conns_lock);
@@ -2210,8 +2199,6 @@ int smb2_tree_disconnect(struct ksmbd_work *work)
goto err_out;
}
- work->tcon = NULL;
-
rsp->StructureSize = cpu_to_le16(4);
err = ksmbd_iov_pin_rsp(work, rsp,
sizeof(struct smb2_tree_disconnect_rsp));
@@ -2232,7 +2219,7 @@ err_out:
* smb2_session_logoff() - handler for session log off request
* @work: smb work containing request buffer
*
- * Return: 0
+ * Return: 0 on success, otherwise error
*/
int smb2_session_logoff(struct ksmbd_work *work)
{
@@ -2726,7 +2713,7 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
switch (dh_idx) {
case DURABLE_RECONN_V2:
{
- struct create_durable_reconn_v2_req *recon_v2;
+ struct create_durable_handle_reconnect_v2 *recon_v2;
if (dh_info->type == DURABLE_RECONN ||
dh_info->type == DURABLE_REQ_V2) {
@@ -2736,13 +2723,13 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
if (le16_to_cpu(context->DataOffset) +
le32_to_cpu(context->DataLength) <
- sizeof(struct create_durable_reconn_v2_req)) {
+ sizeof(struct create_durable_handle_reconnect_v2)) {
err = -EINVAL;
goto out;
}
- recon_v2 = (struct create_durable_reconn_v2_req *)context;
- persistent_id = recon_v2->Fid.PersistentFileId;
+ recon_v2 = (struct create_durable_handle_reconnect_v2 *)context;
+ persistent_id = recon_v2->dcontext.Fid.PersistentFileId;
dh_info->fp = ksmbd_lookup_durable_fd(persistent_id);
if (!dh_info->fp) {
ksmbd_debug(SMB, "Failed to get durable handle state\n");
@@ -2750,7 +2737,7 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
goto out;
}
- if (memcmp(dh_info->fp->create_guid, recon_v2->CreateGuid,
+ if (memcmp(dh_info->fp->create_guid, recon_v2->dcontext.CreateGuid,
SMB2_CREATE_GUID_SIZE)) {
err = -EBADF;
ksmbd_put_durable_fd(dh_info->fp);
@@ -2766,7 +2753,7 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
}
case DURABLE_RECONN:
{
- struct create_durable_reconn_req *recon;
+ create_durable_reconn_t *recon;
if (dh_info->type == DURABLE_RECONN_V2 ||
dh_info->type == DURABLE_REQ_V2) {
@@ -2776,12 +2763,12 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
if (le16_to_cpu(context->DataOffset) +
le32_to_cpu(context->DataLength) <
- sizeof(struct create_durable_reconn_req)) {
+ sizeof(create_durable_reconn_t)) {
err = -EINVAL;
goto out;
}
- recon = (struct create_durable_reconn_req *)context;
+ recon = (create_durable_reconn_t *)context;
persistent_id = recon->Data.Fid.PersistentFileId;
dh_info->fp = ksmbd_lookup_durable_fd(persistent_id);
if (!dh_info->fp) {
@@ -2816,7 +2803,7 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
durable_v2_blob =
(struct create_durable_req_v2 *)context;
ksmbd_debug(SMB, "Request for durable v2 open\n");
- dh_info->fp = ksmbd_lookup_fd_cguid(durable_v2_blob->CreateGuid);
+ dh_info->fp = ksmbd_lookup_fd_cguid(durable_v2_blob->dcontext.CreateGuid);
if (dh_info->fp) {
if (!memcmp(conn->ClientGUID, dh_info->fp->client_guid,
SMB2_CLIENT_GUID_SIZE)) {
@@ -2834,11 +2821,11 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
if ((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) ||
req_op_level == SMB2_OPLOCK_LEVEL_BATCH) {
dh_info->CreateGuid =
- durable_v2_blob->CreateGuid;
+ durable_v2_blob->dcontext.CreateGuid;
dh_info->persistent =
- le32_to_cpu(durable_v2_blob->Flags);
+ le32_to_cpu(durable_v2_blob->dcontext.Flags);
dh_info->timeout =
- le32_to_cpu(durable_v2_blob->Timeout);
+ le32_to_cpu(durable_v2_blob->dcontext.Timeout);
dh_info->type = dh_idx;
}
break;
@@ -3474,7 +3461,7 @@ int smb2_open(struct ksmbd_work *work)
share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp);
if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) ||
(req_op_level == SMB2_OPLOCK_LEVEL_LEASE &&
- !(conn->vals->capabilities & SMB2_GLOBAL_CAP_LEASING))) {
+ !(conn->vals->req_capabilities & SMB2_GLOBAL_CAP_LEASING))) {
if (share_ret < 0 && !S_ISDIR(file_inode(fp->filp)->i_mode)) {
rc = share_ret;
goto err_out1;
@@ -3796,15 +3783,15 @@ static int readdir_info_level_struct_sz(int info_level)
{
switch (info_level) {
case FILE_FULL_DIRECTORY_INFORMATION:
- return sizeof(struct file_full_directory_info);
+ return sizeof(FILE_FULL_DIRECTORY_INFO);
case FILE_BOTH_DIRECTORY_INFORMATION:
- return sizeof(struct file_both_directory_info);
+ return sizeof(FILE_BOTH_DIRECTORY_INFO);
case FILE_DIRECTORY_INFORMATION:
- return sizeof(struct file_directory_info);
+ return sizeof(FILE_DIRECTORY_INFO);
case FILE_NAMES_INFORMATION:
return sizeof(struct file_names_info);
case FILEID_FULL_DIRECTORY_INFORMATION:
- return sizeof(struct file_id_full_dir_info);
+ return sizeof(FILE_ID_FULL_DIR_INFO);
case FILEID_BOTH_DIRECTORY_INFORMATION:
return sizeof(struct file_id_both_directory_info);
case SMB_FIND_FILE_POSIX_INFO:
@@ -3819,9 +3806,9 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level)
switch (info_level) {
case FILE_FULL_DIRECTORY_INFORMATION:
{
- struct file_full_directory_info *ffdinfo;
+ FILE_FULL_DIRECTORY_INFO *ffdinfo;
- ffdinfo = (struct file_full_directory_info *)d_info->rptr;
+ ffdinfo = (FILE_FULL_DIRECTORY_INFO *)d_info->rptr;
d_info->rptr += le32_to_cpu(ffdinfo->NextEntryOffset);
d_info->name = ffdinfo->FileName;
d_info->name_len = le32_to_cpu(ffdinfo->FileNameLength);
@@ -3829,9 +3816,9 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level)
}
case FILE_BOTH_DIRECTORY_INFORMATION:
{
- struct file_both_directory_info *fbdinfo;
+ FILE_BOTH_DIRECTORY_INFO *fbdinfo;
- fbdinfo = (struct file_both_directory_info *)d_info->rptr;
+ fbdinfo = (FILE_BOTH_DIRECTORY_INFO *)d_info->rptr;
d_info->rptr += le32_to_cpu(fbdinfo->NextEntryOffset);
d_info->name = fbdinfo->FileName;
d_info->name_len = le32_to_cpu(fbdinfo->FileNameLength);
@@ -3839,9 +3826,9 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level)
}
case FILE_DIRECTORY_INFORMATION:
{
- struct file_directory_info *fdinfo;
+ FILE_DIRECTORY_INFO *fdinfo;
- fdinfo = (struct file_directory_info *)d_info->rptr;
+ fdinfo = (FILE_DIRECTORY_INFO *)d_info->rptr;
d_info->rptr += le32_to_cpu(fdinfo->NextEntryOffset);
d_info->name = fdinfo->FileName;
d_info->name_len = le32_to_cpu(fdinfo->FileNameLength);
@@ -3859,9 +3846,9 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level)
}
case FILEID_FULL_DIRECTORY_INFORMATION:
{
- struct file_id_full_dir_info *dinfo;
+ FILE_ID_FULL_DIR_INFO *dinfo;
- dinfo = (struct file_id_full_dir_info *)d_info->rptr;
+ dinfo = (FILE_ID_FULL_DIR_INFO *)d_info->rptr;
d_info->rptr += le32_to_cpu(dinfo->NextEntryOffset);
d_info->name = dinfo->FileName;
d_info->name_len = le32_to_cpu(dinfo->FileNameLength);
@@ -3944,9 +3931,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level,
switch (info_level) {
case FILE_FULL_DIRECTORY_INFORMATION:
{
- struct file_full_directory_info *ffdinfo;
+ FILE_FULL_DIRECTORY_INFO *ffdinfo;
- ffdinfo = (struct file_full_directory_info *)kstat;
+ ffdinfo = (FILE_FULL_DIRECTORY_INFO *)kstat;
ffdinfo->FileNameLength = cpu_to_le32(conv_len);
ffdinfo->EaSize =
smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode);
@@ -3960,9 +3947,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level,
}
case FILE_BOTH_DIRECTORY_INFORMATION:
{
- struct file_both_directory_info *fbdinfo;
+ FILE_BOTH_DIRECTORY_INFO *fbdinfo;
- fbdinfo = (struct file_both_directory_info *)kstat;
+ fbdinfo = (FILE_BOTH_DIRECTORY_INFO *)kstat;
fbdinfo->FileNameLength = cpu_to_le32(conv_len);
fbdinfo->EaSize =
smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode);
@@ -3978,9 +3965,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level,
}
case FILE_DIRECTORY_INFORMATION:
{
- struct file_directory_info *fdinfo;
+ FILE_DIRECTORY_INFO *fdinfo;
- fdinfo = (struct file_directory_info *)kstat;
+ fdinfo = (FILE_DIRECTORY_INFO *)kstat;
fdinfo->FileNameLength = cpu_to_le32(conv_len);
if (d_info->hide_dot_file && d_info->name[0] == '.')
fdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE;
@@ -4000,9 +3987,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level,
}
case FILEID_FULL_DIRECTORY_INFORMATION:
{
- struct file_id_full_dir_info *dinfo;
+ FILE_ID_FULL_DIR_INFO *dinfo;
- dinfo = (struct file_id_full_dir_info *)kstat;
+ dinfo = (FILE_ID_FULL_DIR_INFO *)kstat;
dinfo->FileNameLength = cpu_to_le32(conv_len);
dinfo->EaSize =
smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode);
@@ -4206,9 +4193,9 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info,
switch (info_level) {
case FILE_FULL_DIRECTORY_INFORMATION:
{
- struct file_full_directory_info *ffdinfo;
+ FILE_FULL_DIRECTORY_INFO *ffdinfo;
- ffdinfo = (struct file_full_directory_info *)d_info->wptr;
+ ffdinfo = (FILE_FULL_DIRECTORY_INFO *)d_info->wptr;
memcpy(ffdinfo->FileName, d_info->name, d_info->name_len);
ffdinfo->FileName[d_info->name_len] = 0x00;
ffdinfo->FileNameLength = cpu_to_le32(d_info->name_len);
@@ -4217,9 +4204,9 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info,
}
case FILE_BOTH_DIRECTORY_INFORMATION:
{
- struct file_both_directory_info *fbdinfo;
+ FILE_BOTH_DIRECTORY_INFO *fbdinfo;
- fbdinfo = (struct file_both_directory_info *)d_info->wptr;
+ fbdinfo = (FILE_BOTH_DIRECTORY_INFO *)d_info->wptr;
memcpy(fbdinfo->FileName, d_info->name, d_info->name_len);
fbdinfo->FileName[d_info->name_len] = 0x00;
fbdinfo->FileNameLength = cpu_to_le32(d_info->name_len);
@@ -4228,9 +4215,9 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info,
}
case FILE_DIRECTORY_INFORMATION:
{
- struct file_directory_info *fdinfo;
+ FILE_DIRECTORY_INFO *fdinfo;
- fdinfo = (struct file_directory_info *)d_info->wptr;
+ fdinfo = (FILE_DIRECTORY_INFO *)d_info->wptr;
memcpy(fdinfo->FileName, d_info->name, d_info->name_len);
fdinfo->FileName[d_info->name_len] = 0x00;
fdinfo->FileNameLength = cpu_to_le32(d_info->name_len);
@@ -4250,9 +4237,9 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info,
}
case FILEID_FULL_DIRECTORY_INFORMATION:
{
- struct file_id_full_dir_info *dinfo;
+ FILE_ID_FULL_DIR_INFO *dinfo;
- dinfo = (struct file_id_full_dir_info *)d_info->wptr;
+ dinfo = (FILE_ID_FULL_DIR_INFO *)d_info->wptr;
memcpy(dinfo->FileName, d_info->name, d_info->name_len);
dinfo->FileName[d_info->name_len] = 0x00;
dinfo->FileNameLength = cpu_to_le32(d_info->name_len);
@@ -4514,7 +4501,7 @@ again:
goto err_out;
} else {
no_buf_len:
- ((struct file_directory_info *)
+ ((FILE_DIRECTORY_INFO *)
((char *)rsp->Buffer + d_info.last_entry_offset))
->NextEntryOffset = 0;
if (d_info.data_count >= d_info.last_entry_off_align)
@@ -4560,7 +4547,7 @@ err_out2:
smb2_set_err_rsp(work);
ksmbd_fd_put(work, dir_fp);
ksmbd_revert_fsids(work);
- return 0;
+ return rc;
}
/**
@@ -5133,7 +5120,7 @@ static int get_file_internal_info(struct smb2_query_info_rsp *rsp,
static int get_file_network_open_info(struct smb2_query_info_rsp *rsp,
struct ksmbd_file *fp, void *rsp_org)
{
- struct smb2_file_ntwrk_info *file_info;
+ struct smb2_file_network_open_info *file_info;
struct kstat stat;
u64 time;
int ret;
@@ -5149,7 +5136,7 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp,
if (ret)
return ret;
- file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer;
+ file_info = (struct smb2_file_network_open_info *)rsp->Buffer;
file_info->CreationTime = cpu_to_le64(fp->create_time);
time = ksmbd_UnixTimeToNT(stat.atime);
@@ -5168,7 +5155,7 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp,
}
file_info->Reserved = cpu_to_le32(0);
rsp->OutputBufferLength =
- cpu_to_le32(sizeof(struct smb2_file_ntwrk_info));
+ cpu_to_le32(sizeof(struct smb2_file_network_open_info));
return 0;
}
@@ -5476,9 +5463,9 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
switch (fsinfoclass) {
case FS_DEVICE_INFORMATION:
{
- struct filesystem_device_info *info;
+ FILE_SYSTEM_DEVICE_INFO *info;
- info = (struct filesystem_device_info *)rsp->Buffer;
+ info = (FILE_SYSTEM_DEVICE_INFO *)rsp->Buffer;
info->DeviceType = cpu_to_le32(FILE_DEVICE_DISK);
info->DeviceCharacteristics =
@@ -5492,10 +5479,10 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
}
case FS_ATTRIBUTE_INFORMATION:
{
- struct filesystem_attribute_info *info;
+ FILE_SYSTEM_ATTRIBUTE_INFO *info;
size_t sz;
- info = (struct filesystem_attribute_info *)rsp->Buffer;
+ info = (FILE_SYSTEM_ATTRIBUTE_INFO *)rsp->Buffer;
info->Attributes = cpu_to_le32(FILE_SUPPORTS_OBJECT_IDS |
FILE_PERSISTENT_ACLS |
FILE_UNICODE_ON_DISK |
@@ -5514,7 +5501,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
"NTFS", PATH_MAX, conn->local_nls, 0);
len = len * 2;
info->FileSystemNameLen = cpu_to_le32(len);
- sz = sizeof(struct filesystem_attribute_info) + len;
+ sz = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO) + len;
rsp->OutputBufferLength = cpu_to_le32(sz);
break;
}
@@ -5546,11 +5533,11 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
}
case FS_SIZE_INFORMATION:
{
- struct filesystem_info *info;
+ FILE_SYSTEM_SIZE_INFO *info;
- info = (struct filesystem_info *)(rsp->Buffer);
+ info = (FILE_SYSTEM_SIZE_INFO *)(rsp->Buffer);
info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks);
- info->FreeAllocationUnits = cpu_to_le64(stfs.f_bfree);
+ info->AvailableAllocationUnits = cpu_to_le64(stfs.f_bfree);
info->SectorsPerAllocationUnit = cpu_to_le32(1);
info->BytesPerSector = cpu_to_le32(stfs.f_bsize);
rsp->OutputBufferLength = cpu_to_le32(24);
@@ -5633,14 +5620,14 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
}
case FS_POSIX_INFORMATION:
{
- struct filesystem_posix_info *info;
+ FILE_SYSTEM_POSIX_INFO *info;
if (!work->tcon->posix_extensions) {
pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n");
path_put(&path);
return -EOPNOTSUPP;
} else {
- info = (struct filesystem_posix_info *)(rsp->Buffer);
+ info = (FILE_SYSTEM_POSIX_INFO *)(rsp->Buffer);
info->OptimalTransferSize = cpu_to_le32(stfs.f_bsize);
info->BlockSize = cpu_to_le32(stfs.f_bsize);
info->TotalBlocks = cpu_to_le64(stfs.f_blocks);
@@ -5844,7 +5831,7 @@ static noinline int smb2_close_pipe(struct ksmbd_work *work)
* smb2_close() - handler for smb2 close file command
* @work: smb work containing close request buffer
*
- * Return: 0
+ * Return: 0 on success, otherwise error
*/
int smb2_close(struct ksmbd_work *work)
{
@@ -5969,7 +5956,7 @@ out:
* smb2_echo() - handler for smb2 echo(ping) command
* @work: smb work containing echo request buffer
*
- * Return: 0
+ * Return: 0 on success, otherwise error
*/
int smb2_echo(struct ksmbd_work *work)
{
@@ -6396,7 +6383,6 @@ static int set_file_mode_info(struct ksmbd_file *fp,
* @share: ksmbd_share_config pointer
*
* Return: 0 on success, otherwise error
- * TODO: need to implement an error handling for STATUS_INFO_LENGTH_MISMATCH
*/
static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
struct smb2_set_info_req *req,
@@ -6409,14 +6395,14 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
case FILE_BASIC_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_basic_info))
- return -EINVAL;
+ return -EMSGSIZE;
return set_file_basic_info(fp, (struct smb2_file_basic_info *)buffer, share);
}
case FILE_ALLOCATION_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_alloc_info))
- return -EINVAL;
+ return -EMSGSIZE;
return set_file_allocation_info(work, fp,
(struct smb2_file_alloc_info *)buffer);
@@ -6424,7 +6410,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
case FILE_END_OF_FILE_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_eof_info))
- return -EINVAL;
+ return -EMSGSIZE;
return set_end_of_file_info(work, fp,
(struct smb2_file_eof_info *)buffer);
@@ -6432,7 +6418,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
case FILE_RENAME_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_rename_info))
- return -EINVAL;
+ return -EMSGSIZE;
return set_rename_info(work, fp,
(struct smb2_file_rename_info *)buffer,
@@ -6441,7 +6427,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
case FILE_LINK_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_link_info))
- return -EINVAL;
+ return -EMSGSIZE;
return smb2_create_link(work, work->tcon->share_conf,
(struct smb2_file_link_info *)buffer,
@@ -6451,7 +6437,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
case FILE_DISPOSITION_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_disposition_info))
- return -EINVAL;
+ return -EMSGSIZE;
return set_file_disposition_info(fp,
(struct smb2_file_disposition_info *)buffer);
@@ -6465,7 +6451,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
}
if (buf_len < sizeof(struct smb2_ea_info))
- return -EINVAL;
+ return -EMSGSIZE;
return smb2_set_ea((struct smb2_ea_info *)buffer,
buf_len, &fp->filp->f_path, true);
@@ -6473,14 +6459,14 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
case FILE_POSITION_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_pos_info))
- return -EINVAL;
+ return -EMSGSIZE;
return set_file_position_info(fp, (struct smb2_file_pos_info *)buffer);
}
case FILE_MODE_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_mode_info))
- return -EINVAL;
+ return -EMSGSIZE;
return set_file_mode_info(fp, (struct smb2_file_mode_info *)buffer);
}
@@ -6587,6 +6573,8 @@ err_out:
rsp->hdr.Status = STATUS_ACCESS_DENIED;
else if (rc == -EINVAL)
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
+ else if (rc == -EMSGSIZE)
+ rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH;
else if (rc == -ESHARE)
rsp->hdr.Status = STATUS_SHARING_VIOLATION;
else if (rc == -ENOENT)
@@ -6842,7 +6830,7 @@ int smb2_read(struct ksmbd_work *work)
rsp->hdr.Status = STATUS_END_OF_FILE;
smb2_set_err_rsp(work);
ksmbd_fd_put(work, fp);
- return 0;
+ return -ENODATA;
}
ksmbd_debug(SMB, "nbytes %zu, offset %lld mincount %zu\n",
@@ -7764,11 +7752,11 @@ static int fsctl_copychunk(struct ksmbd_work *work,
}
src_fp = ksmbd_lookup_foreign_fd(work,
- le64_to_cpu(ci_req->ResumeKey[0]));
+ le64_to_cpu(ci_req->SourceKeyU64[0]));
dst_fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id);
ret = -EINVAL;
if (!src_fp ||
- src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) {
+ src_fp->persistent_id != le64_to_cpu(ci_req->SourceKeyU64[1])) {
rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND;
goto out;
}
@@ -7876,9 +7864,9 @@ ipv6_retry:
nii_rsp->Capability = 0;
if (netdev->real_num_tx_queues > 1)
- nii_rsp->Capability |= cpu_to_le32(RSS_CAPABLE);
+ nii_rsp->Capability |= RSS_CAPABLE;
if (ksmbd_rdma_capable_netdev(netdev))
- nii_rsp->Capability |= cpu_to_le32(RDMA_CAPABLE);
+ nii_rsp->Capability |= RDMA_CAPABLE;
nii_rsp->Next = cpu_to_le32(152);
nii_rsp->Reserved = 0;
@@ -7904,13 +7892,13 @@ ipv6_retry:
if (!ipv4_set) {
struct in_device *idev;
- sockaddr_storage->Family = cpu_to_le16(INTERNETWORK);
+ sockaddr_storage->Family = INTERNETWORK;
sockaddr_storage->addr4.Port = 0;
idev = __in_dev_get_rtnl(netdev);
if (!idev)
continue;
- sockaddr_storage->addr4.IPv4address =
+ sockaddr_storage->addr4.IPv4Address =
idev_ipv4_address(idev);
nbytes += sizeof(struct network_interface_info_ioctl_rsp);
ipv4_set = true;
@@ -7918,9 +7906,9 @@ ipv6_retry:
} else {
struct inet6_dev *idev6;
struct inet6_ifaddr *ifa;
- __u8 *ipv6_addr = sockaddr_storage->addr6.IPv6address;
+ __u8 *ipv6_addr = sockaddr_storage->addr6.IPv6Address;
- sockaddr_storage->Family = cpu_to_le16(INTERNETWORKV6);
+ sockaddr_storage->Family = INTERNETWORKV6;
sockaddr_storage->addr6.Port = 0;
sockaddr_storage->addr6.FlowInfo = 0;
@@ -7984,7 +7972,7 @@ static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn,
goto err_out;
}
- neg_rsp->Capabilities = cpu_to_le32(conn->vals->capabilities);
+ neg_rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities);
memset(neg_rsp->Guid, 0, SMB2_CLIENT_GUID_SIZE);
neg_rsp->SecurityMode = cpu_to_le16(conn->srv_sec_mode);
neg_rsp->Dialect = cpu_to_le16(conn->dialect);
@@ -8122,8 +8110,8 @@ static int fsctl_request_resume_key(struct ksmbd_work *work,
return -ENOENT;
memset(key_rsp, 0, sizeof(*key_rsp));
- key_rsp->ResumeKey[0] = req->VolatileFileId;
- key_rsp->ResumeKey[1] = req->PersistentFileId;
+ key_rsp->ResumeKeyU64[0] = req->VolatileFileId;
+ key_rsp->ResumeKeyU64[1] = req->PersistentFileId;
ksmbd_fd_put(work, fp);
return 0;
@@ -8164,7 +8152,7 @@ int smb2_ioctl(struct ksmbd_work *work)
id = req->VolatileFileId;
if (req->Flags != cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL)) {
- rsp->hdr.Status = STATUS_NOT_SUPPORTED;
+ ret = -EOPNOTSUPP;
goto out;
}
@@ -8184,8 +8172,9 @@ int smb2_ioctl(struct ksmbd_work *work)
case FSCTL_DFS_GET_REFERRALS:
case FSCTL_DFS_GET_REFERRALS_EX:
/* Not support DFS yet */
+ ret = -EOPNOTSUPP;
rsp->hdr.Status = STATUS_FS_DRIVER_REQUIRED;
- goto out;
+ goto out2;
case FSCTL_CREATE_OR_GET_OBJECT_ID:
{
struct file_object_buf_type1_ioctl_rsp *obj_buf;
@@ -8475,8 +8464,10 @@ out:
rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL;
else if (ret < 0 || rsp->hdr.Status == 0)
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
+
+out2:
smb2_set_err_rsp(work);
- return 0;
+ return ret;
}
/**
@@ -8755,7 +8746,7 @@ err_out:
* smb2_oplock_break() - dispatcher for smb2.0 and 2.1 oplock/lease break
* @work: smb work containing oplock/lease break command buffer
*
- * Return: 0
+ * Return: 0 on success, otherwise error
*/
int smb2_oplock_break(struct ksmbd_work *work)
{
@@ -8778,6 +8769,7 @@ int smb2_oplock_break(struct ksmbd_work *work)
le16_to_cpu(req->StructureSize));
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
smb2_set_err_rsp(work);
+ return -EINVAL;
}
return 0;
@@ -8787,7 +8779,7 @@ int smb2_oplock_break(struct ksmbd_work *work)
* smb2_notify() - handler for smb2 notify request
* @work: smb work containing notify command buffer
*
- * Return: 0
+ * Return: 0 on success, otherwise error
*/
int smb2_notify(struct ksmbd_work *work)
{
@@ -8801,12 +8793,12 @@ int smb2_notify(struct ksmbd_work *work)
if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) {
rsp->hdr.Status = STATUS_INTERNAL_ERROR;
smb2_set_err_rsp(work);
- return 0;
+ return -EIO;
}
smb2_set_err_rsp(work);
rsp->hdr.Status = STATUS_NOT_IMPLEMENTED;
- return 0;
+ return -EOPNOTSUPP;
}
/**
@@ -8861,9 +8853,8 @@ int smb2_check_sign_req(struct ksmbd_work *work)
iov[0].iov_base = (char *)&hdr->ProtocolId;
iov[0].iov_len = len;
- if (ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1,
- signature))
- return 0;
+ ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1,
+ signature);
if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) {
pr_err("bad smb2 signature\n");
@@ -8896,9 +8887,9 @@ void smb2_set_sign_rsp(struct ksmbd_work *work)
iov = &work->iov[work->iov_idx];
}
- if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec,
- signature))
- memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE);
+ ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec,
+ signature);
+ memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE);
}
/**
diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h
index 5163d5241b90..66cdc8e4a648 100644
--- a/fs/smb/server/smb2pdu.h
+++ b/fs/smb/server/smb2pdu.h
@@ -66,40 +66,8 @@ struct preauth_integrity_info {
/* Apple Defined Contexts */
#define SMB2_CREATE_AAPL "AAPL"
-struct create_durable_req_v2 {
- struct create_context_hdr ccontext;
- __u8 Name[8];
- __le32 Timeout;
- __le32 Flags;
- __u8 Reserved[8];
- __u8 CreateGuid[16];
-} __packed;
-
#define DURABLE_HANDLE_MAX_TIMEOUT 300000
-struct create_durable_reconn_req {
- struct create_context_hdr ccontext;
- __u8 Name[8];
- union {
- __u8 Reserved[16];
- struct {
- __u64 PersistentFileId;
- __u64 VolatileFileId;
- } Fid;
- } Data;
-} __packed;
-
-struct create_durable_reconn_v2_req {
- struct create_context_hdr ccontext;
- __u8 Name[8];
- struct {
- __u64 PersistentFileId;
- __u64 VolatileFileId;
- } Fid;
- __u8 CreateGuid[16];
- __le32 Flags;
-} __packed;
-
struct create_alloc_size_req {
struct create_context_hdr ccontext;
__u8 Name[8];
@@ -115,16 +83,6 @@ struct create_durable_rsp {
} Data;
} __packed;
-/* See MS-SMB2 2.2.13.2.11 */
-/* Flags */
-#define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002
-struct create_durable_v2_rsp {
- struct create_context_hdr ccontext;
- __u8 Name[8];
- __le32 Timeout;
- __le32 Flags;
-} __packed;
-
/* equivalent of the contents of SMB3.1.1 POSIX open context response */
struct create_posix_rsp {
struct create_context_hdr ccontext;
@@ -138,22 +96,6 @@ struct create_posix_rsp {
#define SMB2_0_IOCTL_IS_FSCTL 0x00000001
-struct smb_sockaddr_in {
- __be16 Port;
- __be32 IPv4address;
- __u8 Reserved[8];
-} __packed;
-
-struct smb_sockaddr_in6 {
- __be16 Port;
- __be32 FlowInfo;
- __u8 IPv6address[16];
- __be32 ScopeId;
-} __packed;
-
-#define INTERNETWORK 0x0002
-#define INTERNETWORKV6 0x0017
-
struct sockaddr_storage_rsp {
__le16 Family;
union {
@@ -162,18 +104,6 @@ struct sockaddr_storage_rsp {
};
} __packed;
-#define RSS_CAPABLE 0x00000001
-#define RDMA_CAPABLE 0x00000002
-
-struct network_interface_info_ioctl_rsp {
- __le32 Next; /* next interface. zero if this is last one */
- __le32 IfIndex;
- __le32 Capability; /* RSS or RDMA Capable */
- __le32 Reserved;
- __le64 LinkSpeed;
- char SockAddr_Storage[128];
-} __packed;
-
struct file_object_buf_type1_ioctl_rsp {
__u8 ObjectId[16];
__u8 BirthVolumeId[16];
@@ -181,32 +111,6 @@ struct file_object_buf_type1_ioctl_rsp {
__u8 DomainId[16];
} __packed;
-struct resume_key_ioctl_rsp {
- __u64 ResumeKey[3];
- __le32 ContextLength;
- __u8 Context[4]; /* ignored, Windows sets to 4 bytes of zero */
-} __packed;
-
-struct srv_copychunk {
- __le64 SourceOffset;
- __le64 TargetOffset;
- __le32 Length;
- __le32 Reserved;
-} __packed;
-
-struct copychunk_ioctl_req {
- __le64 ResumeKey[3];
- __le32 ChunkCount;
- __le32 Reserved;
- struct srv_copychunk Chunks[] __counted_by_le(ChunkCount);
-} __packed;
-
-struct copychunk_ioctl_rsp {
- __le32 ChunksWritten;
- __le32 ChunkBytesWritten;
- __le32 TotalBytesWritten;
-} __packed;
-
struct file_sparse {
__u8 SetSparse;
} __packed;
@@ -301,17 +205,6 @@ struct smb2_file_stream_info {
char StreamName[];
} __packed;
-struct smb2_file_ntwrk_info {
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 AllocationSize;
- __le64 EndOfFile;
- __le32 Attributes;
- __le32 Reserved;
-} __packed;
-
struct smb2_file_standard_info {
__le64 AllocationSize;
__le64 EndOfFile;
diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h
index 863716207a0d..2baf4aa330eb 100644
--- a/fs/smb/server/smb_common.h
+++ b/fs/smb/server/smb_common.h
@@ -10,8 +10,9 @@
#include "glob.h"
#include "nterr.h"
-#include "../common/cifsglob.h"
+#include "../common/smbglob.h"
#include "../common/smb2pdu.h"
+#include "../common/fscc.h"
#include "smb2pdu.h"
/* ksmbd's Specific ERRNO */
@@ -29,8 +30,6 @@
#define SMB_ECHO_INTERVAL (60 * HZ)
-#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */
-
#define MAX_STREAM_PROT_LEN 0x00FFFFFF
/* Responses when opening a file. */
@@ -39,80 +38,7 @@
#define F_CREATED 2
#define F_OVERWRITTEN 3
-/*
- * File Attribute flags
- */
-#define ATTR_POSIX_SEMANTICS 0x01000000
-#define ATTR_BACKUP_SEMANTICS 0x02000000
-#define ATTR_DELETE_ON_CLOSE 0x04000000
-#define ATTR_SEQUENTIAL_SCAN 0x08000000
-#define ATTR_RANDOM_ACCESS 0x10000000
-#define ATTR_NO_BUFFERING 0x20000000
-#define ATTR_WRITE_THROUGH 0x80000000
-
-/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */
-#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */
-#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */
-#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000
-#define FILE_SUPPORTS_USN_JOURNAL 0x02000000
-#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000
-#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000
-#define FILE_SUPPORTS_HARD_LINKS 0x00400000
-#define FILE_SUPPORTS_TRANSACTIONS 0x00200000
-#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000
-#define FILE_READ_ONLY_VOLUME 0x00080000
-#define FILE_NAMED_STREAMS 0x00040000
-#define FILE_SUPPORTS_ENCRYPTION 0x00020000
-#define FILE_SUPPORTS_OBJECT_IDS 0x00010000
-#define FILE_VOLUME_IS_COMPRESSED 0x00008000
-#define FILE_SUPPORTS_POSIX_UNLINK_RENAME 0x00000400
-#define FILE_RETURNS_CLEANUP_RESULT_INFO 0x00000200
-#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100
-#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080
-#define FILE_SUPPORTS_SPARSE_FILES 0x00000040
-#define FILE_VOLUME_QUOTAS 0x00000020
-#define FILE_FILE_COMPRESSION 0x00000010
-#define FILE_PERSISTENT_ACLS 0x00000008
-#define FILE_UNICODE_ON_DISK 0x00000004
-#define FILE_CASE_PRESERVED_NAMES 0x00000002
-#define FILE_CASE_SENSITIVE_SEARCH 0x00000001
-
-#define FILE_READ_DATA 0x00000001 /* Data can be read from the file */
-#define FILE_WRITE_DATA 0x00000002 /* Data can be written to the file */
-#define FILE_APPEND_DATA 0x00000004 /* Data can be appended to the file */
-#define FILE_READ_EA 0x00000008 /* Extended attributes associated */
-/* with the file can be read */
-#define FILE_WRITE_EA 0x00000010 /* Extended attributes associated */
-/* with the file can be written */
-#define FILE_EXECUTE 0x00000020 /*Data can be read into memory from */
-/* the file using system paging I/O */
-#define FILE_DELETE_CHILD 0x00000040
-#define FILE_READ_ATTRIBUTES 0x00000080 /* Attributes associated with the */
-/* file can be read */
-#define FILE_WRITE_ATTRIBUTES 0x00000100 /* Attributes associated with the */
-/* file can be written */
-#define DELETE 0x00010000 /* The file can be deleted */
-#define READ_CONTROL 0x00020000 /* The access control list and */
-/* ownership associated with the */
-/* file can be read */
-#define WRITE_DAC 0x00040000 /* The access control list and */
-/* ownership associated with the */
-/* file can be written. */
-#define WRITE_OWNER 0x00080000 /* Ownership information associated */
-/* with the file can be written */
-#define SYNCHRONIZE 0x00100000 /* The file handle can waited on to */
-/* synchronize with the completion */
-/* of an input/output request */
-#define GENERIC_ALL 0x10000000
-#define GENERIC_EXECUTE 0x20000000
-#define GENERIC_WRITE 0x40000000
-#define GENERIC_READ 0x80000000
-/* In summary - Relevant file */
-/* access flags from CIFS are */
-/* file_read_data, file_write_data */
-/* file_execute, file_read_attributes*/
-/* write_dac, and delete. */
-
+/* Combinations of file access permission bits */
#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \
| FILE_READ_ATTRIBUTES \
| DELETE | READ_CONTROL | WRITE_DAC \
@@ -123,14 +49,6 @@
| FILE_WRITE_ATTRIBUTES \
| DELETE | READ_CONTROL | WRITE_DAC \
| WRITE_OWNER | SYNCHRONIZE)
-#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \
- | FILE_READ_ATTRIBUTES \
- | FILE_WRITE_ATTRIBUTES \
- | DELETE | READ_CONTROL | WRITE_DAC \
- | WRITE_OWNER | SYNCHRONIZE)
-
-#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \
- | READ_CONTROL | SYNCHRONIZE)
/* generic flags for file open */
#define GENERIC_READ_FLAGS (READ_CONTROL | FILE_READ_DATA | \
@@ -151,71 +69,27 @@
FILE_EXECUTE | FILE_DELETE_CHILD | \
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES)
-#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff)
-#define SMB_COM_NEGOTIATE 0x72
-#define SMB1_CLIENT_GUID_SIZE (16)
+#define SMB_COM_NEGOTIATE 0x72 /* See MS-CIFS 2.2.2.1 */
+/* See MS-CIFS 2.2.3.1 */
#define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */
+/*
+ * See MS-CIFS 2.2.3.1
+ * MS-SMB 2.2.3.1
+ */
#define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40)
#define SMBFLG2_EXT_SEC cpu_to_le16(0x800)
#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000)
#define SMBFLG2_UNICODE cpu_to_le16(0x8000)
-struct smb_hdr {
- __be32 smb_buf_length;
- __u8 Protocol[4];
- __u8 Command;
- union {
- struct {
- __u8 ErrorClass;
- __u8 Reserved;
- __le16 Error;
- } __packed DosError;
- __le32 CifsError;
- } __packed Status;
- __u8 Flags;
- __le16 Flags2; /* note: le */
- __le16 PidHigh;
- union {
- struct {
- __le32 SequenceNumber; /* le */
- __u32 Reserved; /* zero */
- } __packed Sequence;
- __u8 SecuritySignature[8]; /* le */
- } __packed Signature;
- __u8 pad[2];
- __le16 Tid;
- __le16 Pid;
- __le16 Uid;
- __le16 Mid;
- __u8 WordCount;
-} __packed;
-
-struct smb_negotiate_req {
- struct smb_hdr hdr; /* wct = 0 */
- __le16 ByteCount;
- unsigned char DialectsArray[];
-} __packed;
-
+/* See MS-CIFS 2.2.4.52.2 */
struct smb_negotiate_rsp {
struct smb_hdr hdr; /* wct = 17 */
__le16 DialectIndex; /* 0xFFFF = no dialect acceptable */
__le16 ByteCount;
} __packed;
-struct filesystem_attribute_info {
- __le32 Attributes;
- __le32 MaxPathNameComponentLength;
- __le32 FileSystemNameLen;
- __le16 FileSystemName[]; /* do not have to save this - get subset? */
-} __packed;
-
-struct filesystem_device_info {
- __le32 DeviceType;
- __le32 DeviceCharacteristics;
-} __packed; /* device info level 0x104 */
-
struct filesystem_vol_info {
__le64 VolumeCreationTime;
__le32 SerialNumber;
@@ -224,13 +98,6 @@ struct filesystem_vol_info {
__le16 VolumeLabel[];
} __packed;
-struct filesystem_info {
- __le64 TotalAllocationUnits;
- __le64 FreeAllocationUnits;
- __le32 SectorsPerAllocationUnit;
- __le32 BytesPerSector;
-} __packed; /* size info, level 0x103 */
-
#define EXTENDED_INFO_MAGIC 0x43667364 /* Cfsd */
#define STRING_LENGTH 28
@@ -247,20 +114,6 @@ struct object_id_info {
struct fs_extended_info extended_info;
} __packed;
-struct file_directory_info {
- __le32 NextEntryOffset;
- __u32 FileIndex;
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 EndOfFile;
- __le64 AllocationSize;
- __le32 ExtFileAttributes;
- __le32 FileNameLength;
- char FileName[];
-} __packed; /* level 0x101 FF resp data */
-
struct file_names_info {
__le32 NextEntryOffset;
__u32 FileIndex;
@@ -268,39 +121,6 @@ struct file_names_info {
char FileName[];
} __packed; /* level 0xc FF resp data */
-struct file_full_directory_info {
- __le32 NextEntryOffset;
- __u32 FileIndex;
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 EndOfFile;
- __le64 AllocationSize;
- __le32 ExtFileAttributes;
- __le32 FileNameLength;
- __le32 EaSize;
- char FileName[];
-} __packed; /* level 0x102 FF resp */
-
-struct file_both_directory_info {
- __le32 NextEntryOffset;
- __u32 FileIndex;
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 EndOfFile;
- __le64 AllocationSize;
- __le32 ExtFileAttributes;
- __le32 FileNameLength;
- __le32 EaSize; /* length of the xattrs */
- __u8 ShortNameLength;
- __u8 Reserved;
- __u8 ShortName[24];
- char FileName[];
-} __packed; /* level 0x104 FFrsp data */
-
struct file_id_both_directory_info {
__le32 NextEntryOffset;
__u32 FileIndex;
@@ -321,75 +141,6 @@ struct file_id_both_directory_info {
char FileName[];
} __packed;
-struct file_id_full_dir_info {
- __le32 NextEntryOffset;
- __u32 FileIndex;
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 EndOfFile;
- __le64 AllocationSize;
- __le32 ExtFileAttributes;
- __le32 FileNameLength;
- __le32 EaSize; /* EA size */
- __le32 Reserved;
- __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/
- char FileName[];
-} __packed; /* level 0x105 FF rsp data */
-
-struct smb_version_values {
- char *version_string;
- __u16 protocol_id;
- __le16 lock_cmd;
- __u32 capabilities;
- __u32 max_read_size;
- __u32 max_write_size;
- __u32 max_trans_size;
- __u32 max_credits;
- __u32 large_lock_type;
- __u32 exclusive_lock_type;
- __u32 shared_lock_type;
- __u32 unlock_lock_type;
- size_t header_size;
- size_t max_header_size;
- size_t read_rsp_size;
- unsigned int cap_unix;
- unsigned int cap_nt_find;
- unsigned int cap_large_files;
- __u16 signing_enabled;
- __u16 signing_required;
- size_t create_lease_size;
- size_t create_durable_size;
- size_t create_durable_v2_size;
- size_t create_mxac_size;
- size_t create_disk_id_size;
- size_t create_posix_size;
-};
-
-struct filesystem_posix_info {
- /* For undefined recommended transfer size return -1 in that field */
- __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */
- __le32 BlockSize;
- /* The next three fields are in terms of the block size.
- * (above). If block size is unknown, 4096 would be a
- * reasonable block size for a server to report.
- * Note that returning the blocks/blocksavail removes need
- * to make a second call (to QFSInfo level 0x103 to get this info.
- * UserBlockAvail is typically less than or equal to BlocksAvail,
- * if no distinction is made return the same value in each
- */
- __le64 TotalBlocks;
- __le64 BlocksAvail; /* bfree */
- __le64 UserBlocksAvail; /* bavail */
- /* For undefined Node fields or FSID return -1 */
- __le64 TotalFileNodes;
- __le64 FreeFileNodes;
- __le64 FileSysIdentifier; /* fsid */
- /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */
- /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */
-} __packed;
-
struct smb_version_ops {
u16 (*get_cmd_val)(struct ksmbd_work *swork);
int (*init_rsp_hdr)(struct ksmbd_work *swork);
@@ -402,7 +153,7 @@ struct smb_version_ops {
int (*check_sign_req)(struct ksmbd_work *work);
void (*set_sign_rsp)(struct ksmbd_work *work);
int (*generate_signingkey)(struct ksmbd_session *sess, struct ksmbd_conn *conn);
- int (*generate_encryptionkey)(struct ksmbd_conn *conn, struct ksmbd_session *sess);
+ void (*generate_encryptionkey)(struct ksmbd_conn *conn, struct ksmbd_session *sess);
bool (*is_transform_hdr)(void *buf);
int (*decrypt_req)(struct ksmbd_work *work);
int (*encrypt_resp)(struct ksmbd_work *work);
@@ -452,9 +203,4 @@ unsigned int ksmbd_server_side_copy_max_chunk_size(void);
unsigned int ksmbd_server_side_copy_max_total_size(void);
bool is_asterisk(char *p);
__le32 smb_map_generic_desired_access(__le32 daccess);
-
-static inline unsigned int get_rfc1002_len(void *buf)
-{
- return be32_to_cpu(*((__be32 *)buf)) & 0xffffff;
-}
#endif /* __SMB_COMMON_H__ */
diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c
index 2c08cccfa680..2dbabe2d8005 100644
--- a/fs/smb/server/transport_ipc.c
+++ b/fs/smb/server/transport_ipc.c
@@ -553,12 +553,16 @@ static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle
up_write(&ipc_msg_table_lock);
ret = ipc_msg_send(msg);
- if (ret)
+ if (ret) {
+ down_write(&ipc_msg_table_lock);
goto out;
+ }
ret = wait_event_interruptible_timeout(entry.wait,
entry.response != NULL,
IPC_WAIT_TIMEOUT);
+
+ down_write(&ipc_msg_table_lock);
if (entry.response) {
ret = ipc_validate_msg(&entry);
if (ret) {
@@ -567,7 +571,6 @@ static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle
}
}
out:
- down_write(&ipc_msg_table_lock);
hash_del(&entry.ipc_table_hlist);
up_write(&ipc_msg_table_lock);
return entry.response;
diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c
index e2be9a496154..4e7ab8d9314f 100644
--- a/fs/smb/server/transport_rdma.c
+++ b/fs/smb/server/transport_rdma.c
@@ -19,6 +19,8 @@
#include <rdma/rdma_cm.h>
#include <rdma/rw.h>
+#define __SMBDIRECT_SOCKET_DISCONNECT(__sc) smb_direct_disconnect_rdma_connection(__sc)
+
#include "glob.h"
#include "connection.h"
#include "smb_common.h"
@@ -231,6 +233,9 @@ static void smb_direct_disconnect_rdma_work(struct work_struct *work)
struct smbdirect_socket *sc =
container_of(work, struct smbdirect_socket, disconnect_work);
+ if (sc->first_error == 0)
+ sc->first_error = -ECONNABORTED;
+
/*
* make sure this and other work is not queued again
* but here we don't block and avoid
@@ -241,9 +246,6 @@ static void smb_direct_disconnect_rdma_work(struct work_struct *work)
disable_delayed_work(&sc->idle.timer_work);
disable_work(&sc->idle.immediate_work);
- if (sc->first_error == 0)
- sc->first_error = -ECONNABORTED;
-
switch (sc->status) {
case SMBDIRECT_SOCKET_NEGOTIATE_NEEDED:
case SMBDIRECT_SOCKET_NEGOTIATE_RUNNING:
@@ -287,6 +289,9 @@ static void smb_direct_disconnect_rdma_work(struct work_struct *work)
static void
smb_direct_disconnect_rdma_connection(struct smbdirect_socket *sc)
{
+ if (sc->first_error == 0)
+ sc->first_error = -ECONNABORTED;
+
/*
* make sure other work (than disconnect_work) is
* not queued again but here we don't block and avoid
@@ -296,9 +301,6 @@ smb_direct_disconnect_rdma_connection(struct smbdirect_socket *sc)
disable_work(&sc->idle.immediate_work);
disable_delayed_work(&sc->idle.timer_work);
- if (sc->first_error == 0)
- sc->first_error = -ECONNABORTED;
-
switch (sc->status) {
case SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED:
case SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED:
@@ -639,7 +641,18 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
return;
}
sc->recv_io.reassembly.full_packet_received = true;
- WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_NEGOTIATE_NEEDED);
+ /*
+ * Some drivers (at least mlx5_ib) might post a
+ * recv completion before RDMA_CM_EVENT_ESTABLISHED,
+ * we need to adjust our expectation in that case.
+ */
+ if (!sc->first_error && sc->status == SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING)
+ sc->status = SMBDIRECT_SOCKET_NEGOTIATE_NEEDED;
+ if (SMBDIRECT_CHECK_STATUS_WARN(sc, SMBDIRECT_SOCKET_NEGOTIATE_NEEDED)) {
+ put_recvmsg(sc, recvmsg);
+ smb_direct_disconnect_rdma_connection(sc);
+ return;
+ }
sc->status = SMBDIRECT_SOCKET_NEGOTIATE_RUNNING;
enqueue_reassembly(sc, recvmsg, 0);
wake_up(&sc->status_wait);
@@ -1725,7 +1738,18 @@ static int smb_direct_cm_handler(struct rdma_cm_id *cm_id,
switch (event->event) {
case RDMA_CM_EVENT_ESTABLISHED: {
- WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING);
+ /*
+ * Some drivers (at least mlx5_ib) might post a
+ * recv completion before RDMA_CM_EVENT_ESTABLISHED,
+ * we need to adjust our expectation in that case.
+ *
+ * As we already started the negotiation, we just
+ * ignore RDMA_CM_EVENT_ESTABLISHED here.
+ */
+ if (!sc->first_error && sc->status > SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING)
+ break;
+ if (SMBDIRECT_CHECK_STATUS_DISCONNECT(sc, SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING))
+ break;
sc->status = SMBDIRECT_SOCKET_NEGOTIATE_NEEDED;
wake_up(&sc->status_wait);
break;
diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c
index d2e391c29464..4bb07937d7ef 100644
--- a/fs/smb/server/transport_tcp.c
+++ b/fs/smb/server/transport_tcp.c
@@ -22,7 +22,6 @@ struct interface {
struct socket *ksmbd_socket;
struct list_head entry;
char *name;
- struct mutex sock_release_lock;
int state;
};
@@ -56,19 +55,6 @@ static inline void ksmbd_tcp_reuseaddr(struct socket *sock)
sock_set_reuseaddr(sock->sk);
}
-static inline void ksmbd_tcp_rcv_timeout(struct socket *sock, s64 secs)
-{
- if (secs && secs < MAX_SCHEDULE_TIMEOUT / HZ - 1)
- WRITE_ONCE(sock->sk->sk_rcvtimeo, secs * HZ);
- else
- WRITE_ONCE(sock->sk->sk_rcvtimeo, MAX_SCHEDULE_TIMEOUT);
-}
-
-static inline void ksmbd_tcp_snd_timeout(struct socket *sock, s64 secs)
-{
- sock_set_sndtimeo(sock->sk, secs);
-}
-
static struct tcp_transport *alloc_transport(struct socket *client_sk)
{
struct tcp_transport *t;
@@ -236,20 +222,14 @@ static int ksmbd_kthread_fn(void *p)
unsigned int max_ip_conns;
while (!kthread_should_stop()) {
- mutex_lock(&iface->sock_release_lock);
if (!iface->ksmbd_socket) {
- mutex_unlock(&iface->sock_release_lock);
break;
}
- ret = kernel_accept(iface->ksmbd_socket, &client_sk,
- SOCK_NONBLOCK);
- mutex_unlock(&iface->sock_release_lock);
- if (ret) {
- if (ret == -EAGAIN)
- /* check for new connections every 100 msecs */
- schedule_timeout_interruptible(HZ / 10);
+ ret = kernel_accept(iface->ksmbd_socket, &client_sk, 0);
+ if (ret == -EINVAL)
+ break;
+ if (ret)
continue;
- }
if (!server_conf.max_ip_connections)
goto skip_max_ip_conns_limit;
@@ -458,10 +438,6 @@ static void tcp_destroy_socket(struct socket *ksmbd_socket)
if (!ksmbd_socket)
return;
- /* set zero to timeout */
- ksmbd_tcp_rcv_timeout(ksmbd_socket, 0);
- ksmbd_tcp_snd_timeout(ksmbd_socket, 0);
-
ret = kernel_sock_shutdown(ksmbd_socket, SHUT_RDWR);
if (ret)
pr_err("Failed to shutdown socket: %d\n", ret);
@@ -522,19 +498,16 @@ static int create_socket(struct interface *iface)
}
if (ipv4)
- ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin,
+ ret = kernel_bind(ksmbd_socket, (struct sockaddr_unsized *)&sin,
sizeof(sin));
else
- ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin6,
+ ret = kernel_bind(ksmbd_socket, (struct sockaddr_unsized *)&sin6,
sizeof(sin6));
if (ret) {
pr_err("Failed to bind socket: %d\n", ret);
goto out_error;
}
- ksmbd_socket->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT;
- ksmbd_socket->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT;
-
ret = kernel_listen(ksmbd_socket, KSMBD_SOCKET_BACKLOG);
if (ret) {
pr_err("Port listen() error: %d\n", ret);
@@ -604,12 +577,11 @@ static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event,
if (iface && iface->state == IFACE_STATE_CONFIGURED) {
ksmbd_debug(CONN, "netdev-down event: netdev(%s) is going down\n",
iface->name);
+ kernel_sock_shutdown(iface->ksmbd_socket, SHUT_RDWR);
tcp_stop_kthread(iface->ksmbd_kthread);
iface->ksmbd_kthread = NULL;
- mutex_lock(&iface->sock_release_lock);
- tcp_destroy_socket(iface->ksmbd_socket);
+ sock_release(iface->ksmbd_socket);
iface->ksmbd_socket = NULL;
- mutex_unlock(&iface->sock_release_lock);
iface->state = IFACE_STATE_DOWN;
break;
@@ -672,7 +644,6 @@ static struct interface *alloc_iface(char *ifname)
iface->name = ifname;
iface->state = IFACE_STATE_DOWN;
list_add(&iface->entry, &iface_list);
- mutex_init(&iface->sock_release_lock);
return iface;
}
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index 03fd7409be79..98b0eb966d91 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -303,6 +303,9 @@ static int check_lock_range(struct file *filp, loff_t start, loff_t end,
struct file_lock_context *ctx = locks_inode_context(file_inode(filp));
int error = 0;
+ if (start == end)
+ return 0;
+
if (!ctx || list_empty_careful(&ctx->flc_posix))
return 0;
@@ -767,7 +770,7 @@ int ksmbd_vfs_truncate(struct ksmbd_work *work,
if (size < inode->i_size) {
err = check_lock_range(filp, size,
inode->i_size - 1, WRITE);
- } else {
+ } else if (size > inode->i_size) {
err = check_lock_range(filp, inode->i_size,
size - 1, WRITE);
}
@@ -1591,7 +1594,7 @@ int ksmbd_vfs_get_dos_attrib_xattr(struct mnt_idmap *idmap,
*/
void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat)
{
- struct file_directory_info *info = (struct file_directory_info *)(*p);
+ FILE_DIRECTORY_INFO *info = (FILE_DIRECTORY_INFO *)(*p);
struct kstat *kstat = ksmbd_kstat->kstat;
u64 time;
diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
index dfed6fce8904..6ef116585af6 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -112,40 +112,62 @@ int ksmbd_query_inode_status(struct dentry *dentry)
read_lock(&inode_hash_lock);
ci = __ksmbd_inode_lookup(dentry);
- if (ci) {
- ret = KSMBD_INODE_STATUS_OK;
- if (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS))
- ret = KSMBD_INODE_STATUS_PENDING_DELETE;
- atomic_dec(&ci->m_count);
- }
read_unlock(&inode_hash_lock);
+ if (!ci)
+ return ret;
+
+ down_read(&ci->m_lock);
+ if (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS))
+ ret = KSMBD_INODE_STATUS_PENDING_DELETE;
+ else
+ ret = KSMBD_INODE_STATUS_OK;
+ up_read(&ci->m_lock);
+
+ atomic_dec(&ci->m_count);
return ret;
}
bool ksmbd_inode_pending_delete(struct ksmbd_file *fp)
{
- return (fp->f_ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS));
+ struct ksmbd_inode *ci = fp->f_ci;
+ int ret;
+
+ down_read(&ci->m_lock);
+ ret = (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS));
+ up_read(&ci->m_lock);
+
+ return ret;
}
void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp)
{
- fp->f_ci->m_flags |= S_DEL_PENDING;
+ struct ksmbd_inode *ci = fp->f_ci;
+
+ down_write(&ci->m_lock);
+ ci->m_flags |= S_DEL_PENDING;
+ up_write(&ci->m_lock);
}
void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp)
{
- fp->f_ci->m_flags &= ~S_DEL_PENDING;
+ struct ksmbd_inode *ci = fp->f_ci;
+
+ down_write(&ci->m_lock);
+ ci->m_flags &= ~S_DEL_PENDING;
+ up_write(&ci->m_lock);
}
void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp,
int file_info)
{
- if (ksmbd_stream_fd(fp)) {
- fp->f_ci->m_flags |= S_DEL_ON_CLS_STREAM;
- return;
- }
+ struct ksmbd_inode *ci = fp->f_ci;
- fp->f_ci->m_flags |= S_DEL_ON_CLS;
+ down_write(&ci->m_lock);
+ if (ksmbd_stream_fd(fp))
+ ci->m_flags |= S_DEL_ON_CLS_STREAM;
+ else
+ ci->m_flags |= S_DEL_ON_CLS;
+ up_write(&ci->m_lock);
}
static void ksmbd_inode_hash(struct ksmbd_inode *ci)
@@ -257,27 +279,41 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp)
struct file *filp;
filp = fp->filp;
- if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) {
- ci->m_flags &= ~S_DEL_ON_CLS_STREAM;
- err = ksmbd_vfs_remove_xattr(file_mnt_idmap(filp),
- &filp->f_path,
- fp->stream.name,
- true);
- if (err)
- pr_err("remove xattr failed : %s\n",
- fp->stream.name);
+
+ if (ksmbd_stream_fd(fp)) {
+ bool remove_stream_xattr = false;
+
+ down_write(&ci->m_lock);
+ if (ci->m_flags & S_DEL_ON_CLS_STREAM) {
+ ci->m_flags &= ~S_DEL_ON_CLS_STREAM;
+ remove_stream_xattr = true;
+ }
+ up_write(&ci->m_lock);
+
+ if (remove_stream_xattr) {
+ err = ksmbd_vfs_remove_xattr(file_mnt_idmap(filp),
+ &filp->f_path,
+ fp->stream.name,
+ true);
+ if (err)
+ pr_err("remove xattr failed : %s\n",
+ fp->stream.name);
+ }
}
if (atomic_dec_and_test(&ci->m_count)) {
+ bool do_unlink = false;
+
down_write(&ci->m_lock);
if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) {
ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING);
- up_write(&ci->m_lock);
- ksmbd_vfs_unlink(filp);
- down_write(&ci->m_lock);
+ do_unlink = true;
}
up_write(&ci->m_lock);
+ if (do_unlink)
+ ksmbd_vfs_unlink(filp);
+
ksmbd_inode_free(ci);
}
}
diff --git a/fs/xfs/libxfs/xfs_group.h b/fs/xfs/libxfs/xfs_group.h
index 4423932a2313..4ae638f1c2c5 100644
--- a/fs/xfs/libxfs/xfs_group.h
+++ b/fs/xfs/libxfs/xfs_group.h
@@ -98,6 +98,15 @@ xfs_group_max_blocks(
return xg->xg_mount->m_groups[xg->xg_type].blocks;
}
+static inline xfs_rfsblock_t
+xfs_groups_to_rfsbs(
+ struct xfs_mount *mp,
+ uint32_t nr_groups,
+ enum xfs_group_type type)
+{
+ return (xfs_rfsblock_t)mp->m_groups[type].blocks * nr_groups;
+}
+
static inline xfs_fsblock_t
xfs_group_start_fsb(
struct xfs_group *xg)
diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h
index 6c50cb2ece19..908e7060428c 100644
--- a/fs/xfs/libxfs/xfs_log_format.h
+++ b/fs/xfs/libxfs/xfs_log_format.h
@@ -31,6 +31,7 @@ typedef uint32_t xlog_tid_t;
#define XLOG_BIG_RECORD_BSIZE (32*1024) /* 32k buffers */
#define XLOG_MAX_RECORD_BSIZE (256*1024)
#define XLOG_HEADER_CYCLE_SIZE (32*1024) /* cycle data in header */
+#define XLOG_CYCLE_DATA_SIZE (XLOG_HEADER_CYCLE_SIZE / BBSIZE)
#define XLOG_MIN_RECORD_BSHIFT 14 /* 16384 == 1 << 14 */
#define XLOG_BIG_RECORD_BSHIFT 15 /* 32k == 1 << 15 */
#define XLOG_MAX_RECORD_BSHIFT 18 /* 256k == 1 << 18 */
@@ -125,7 +126,17 @@ struct xlog_op_header {
#define XLOG_FMT XLOG_FMT_LINUX_LE
#endif
-typedef struct xlog_rec_header {
+struct xlog_rec_ext_header {
+ __be32 xh_cycle; /* write cycle of log */
+ __be32 xh_cycle_data[XLOG_CYCLE_DATA_SIZE];
+ __u8 xh_reserved[252];
+};
+
+/* actual ext header payload size for checksumming */
+#define XLOG_REC_EXT_SIZE \
+ offsetofend(struct xlog_rec_ext_header, xh_cycle_data)
+
+struct xlog_rec_header {
__be32 h_magicno; /* log record (LR) identifier : 4 */
__be32 h_cycle; /* write cycle of log : 4 */
__be32 h_version; /* LR version : 4 */
@@ -135,7 +146,7 @@ typedef struct xlog_rec_header {
__le32 h_crc; /* crc of log record : 4 */
__be32 h_prev_block; /* block number to previous LR : 4 */
__be32 h_num_logops; /* number of log operations in this LR : 4 */
- __be32 h_cycle_data[XLOG_HEADER_CYCLE_SIZE / BBSIZE];
+ __be32 h_cycle_data[XLOG_CYCLE_DATA_SIZE];
/* fields added by the Linux port: */
__be32 h_fmt; /* format of log record : 4 */
@@ -160,30 +171,19 @@ typedef struct xlog_rec_header {
* (little-endian) architectures.
*/
__u32 h_pad0;
-} xlog_rec_header_t;
+
+ __u8 h_reserved[184];
+ struct xlog_rec_ext_header h_ext[];
+};
#ifdef __i386__
#define XLOG_REC_SIZE offsetofend(struct xlog_rec_header, h_size)
-#define XLOG_REC_SIZE_OTHER sizeof(struct xlog_rec_header)
+#define XLOG_REC_SIZE_OTHER offsetofend(struct xlog_rec_header, h_pad0)
#else
-#define XLOG_REC_SIZE sizeof(struct xlog_rec_header)
+#define XLOG_REC_SIZE offsetofend(struct xlog_rec_header, h_pad0)
#define XLOG_REC_SIZE_OTHER offsetofend(struct xlog_rec_header, h_size)
#endif /* __i386__ */
-typedef struct xlog_rec_ext_header {
- __be32 xh_cycle; /* write cycle of log : 4 */
- __be32 xh_cycle_data[XLOG_HEADER_CYCLE_SIZE / BBSIZE]; /* : 256 */
-} xlog_rec_ext_header_t;
-
-/*
- * Quite misnamed, because this union lays out the actual on-disk log buffer.
- */
-typedef union xlog_in_core2 {
- xlog_rec_header_t hic_header;
- xlog_rec_ext_header_t hic_xheader;
- char hic_sector[XLOG_HEADER_SIZE];
-} xlog_in_core_2_t;
-
/* not an on-disk structure, but needed by log recovery in userspace */
struct xfs_log_iovec {
void *i_addr; /* beginning address of region */
diff --git a/fs/xfs/libxfs/xfs_ondisk.h b/fs/xfs/libxfs/xfs_ondisk.h
index 7bfa3242e2c5..2e9715cc1641 100644
--- a/fs/xfs/libxfs/xfs_ondisk.h
+++ b/fs/xfs/libxfs/xfs_ondisk.h
@@ -174,9 +174,11 @@ xfs_check_ondisk_structs(void)
XFS_CHECK_STRUCT_SIZE(struct xfs_rud_log_format, 16);
XFS_CHECK_STRUCT_SIZE(struct xfs_map_extent, 32);
XFS_CHECK_STRUCT_SIZE(struct xfs_phys_extent, 16);
- XFS_CHECK_STRUCT_SIZE(struct xlog_rec_header, 328);
- XFS_CHECK_STRUCT_SIZE(struct xlog_rec_ext_header, 260);
+ XFS_CHECK_STRUCT_SIZE(struct xlog_rec_header, 512);
+ XFS_CHECK_STRUCT_SIZE(struct xlog_rec_ext_header, 512);
+ XFS_CHECK_OFFSET(struct xlog_rec_header, h_reserved, 328);
+ XFS_CHECK_OFFSET(struct xlog_rec_ext_header, xh_reserved, 260);
XFS_CHECK_OFFSET(struct xfs_bui_log_format, bui_extents, 16);
XFS_CHECK_OFFSET(struct xfs_cui_log_format, cui_extents, 16);
XFS_CHECK_OFFSET(struct xfs_rui_log_format, rui_extents, 16);
diff --git a/fs/xfs/libxfs/xfs_quota_defs.h b/fs/xfs/libxfs/xfs_quota_defs.h
index 763d941a8420..551d7ae46c5c 100644
--- a/fs/xfs/libxfs/xfs_quota_defs.h
+++ b/fs/xfs/libxfs/xfs_quota_defs.h
@@ -29,11 +29,9 @@ typedef uint8_t xfs_dqtype_t;
* flags for q_flags field in the dquot.
*/
#define XFS_DQFLAG_DIRTY (1u << 0) /* dquot is dirty */
-#define XFS_DQFLAG_FREEING (1u << 1) /* dquot is being torn down */
#define XFS_DQFLAG_STRINGS \
- { XFS_DQFLAG_DIRTY, "DIRTY" }, \
- { XFS_DQFLAG_FREEING, "FREEING" }
+ { XFS_DQFLAG_DIRTY, "DIRTY" }
/*
* We have the possibility of all three quota types being active at once, and
diff --git a/fs/xfs/libxfs/xfs_rtgroup.h b/fs/xfs/libxfs/xfs_rtgroup.h
index d4fcf591e63d..03f1e2493334 100644
--- a/fs/xfs/libxfs/xfs_rtgroup.h
+++ b/fs/xfs/libxfs/xfs_rtgroup.h
@@ -64,12 +64,6 @@ struct xfs_rtgroup {
*/
#define XFS_RTG_FREE XA_MARK_0
-/*
- * For zoned RT devices this is set on groups that are fully written and that
- * have unused blocks. Used by the garbage collection to pick targets.
- */
-#define XFS_RTG_RECLAIMABLE XA_MARK_1
-
static inline struct xfs_rtgroup *to_rtg(struct xfs_group *xg)
{
return container_of(xg, struct xfs_rtgroup, rtg_group);
@@ -371,4 +365,12 @@ static inline int xfs_initialize_rtgroups(struct xfs_mount *mp,
# define xfs_rtgroup_get_geometry(rtg, rgeo) (-EOPNOTSUPP)
#endif /* CONFIG_XFS_RT */
+static inline xfs_rfsblock_t
+xfs_rtgs_to_rfsbs(
+ struct xfs_mount *mp,
+ uint32_t nr_groups)
+{
+ return xfs_groups_to_rfsbs(mp, nr_groups, XG_TYPE_RTG);
+}
+
#endif /* __LIBXFS_RTGROUP_H */
diff --git a/fs/xfs/libxfs/xfs_zones.c b/fs/xfs/libxfs/xfs_zones.c
index b0791a71931c..b40f71f878b5 100644
--- a/fs/xfs/libxfs/xfs_zones.c
+++ b/fs/xfs/libxfs/xfs_zones.c
@@ -95,6 +95,7 @@ xfs_zone_validate_seq(
case BLK_ZONE_COND_IMP_OPEN:
case BLK_ZONE_COND_EXP_OPEN:
case BLK_ZONE_COND_CLOSED:
+ case BLK_ZONE_COND_ACTIVE:
return xfs_zone_validate_wp(zone, rtg, write_pointer);
case BLK_ZONE_COND_FULL:
return xfs_zone_validate_full(zone, rtg, write_pointer);
diff --git a/fs/xfs/scrub/quota.c b/fs/xfs/scrub/quota.c
index 58d6d4ed2853..5c5374c44c5a 100644
--- a/fs/xfs/scrub/quota.c
+++ b/fs/xfs/scrub/quota.c
@@ -155,12 +155,9 @@ xchk_quota_item(
* We want to validate the bmap record for the storage backing this
* dquot, so we need to lock the dquot and the quota file. For quota
* operations, the locking order is first the ILOCK and then the dquot.
- * However, dqiterate gave us a locked dquot, so drop the dquot lock to
- * get the ILOCK.
*/
- xfs_dqunlock(dq);
xchk_ilock(sc, XFS_ILOCK_SHARED);
- xfs_dqlock(dq);
+ mutex_lock(&dq->q_qlock);
/*
* Except for the root dquot, the actual dquot we got must either have
@@ -251,6 +248,7 @@ xchk_quota_item(
xchk_quota_item_timer(sc, offset, &dq->q_rtb);
out:
+ mutex_unlock(&dq->q_qlock);
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
return -ECANCELED;
@@ -330,7 +328,7 @@ xchk_quota(
xchk_dqiter_init(&cursor, sc, dqtype);
while ((error = xchk_dquot_iter(&cursor, &dq)) == 1) {
error = xchk_quota_item(&sqi, dq);
- xfs_qm_dqput(dq);
+ xfs_qm_dqrele(dq);
if (error)
break;
}
diff --git a/fs/xfs/scrub/quota_repair.c b/fs/xfs/scrub/quota_repair.c
index 8f4c8d41f308..b1d661aa5f06 100644
--- a/fs/xfs/scrub/quota_repair.c
+++ b/fs/xfs/scrub/quota_repair.c
@@ -184,17 +184,13 @@ xrep_quota_item(
/*
* We might need to fix holes in the bmap record for the storage
* backing this dquot, so we need to lock the dquot and the quota file.
- * dqiterate gave us a locked dquot, so drop the dquot lock to get the
- * ILOCK_EXCL.
*/
- xfs_dqunlock(dq);
xchk_ilock(sc, XFS_ILOCK_EXCL);
- xfs_dqlock(dq);
-
+ mutex_lock(&dq->q_qlock);
error = xrep_quota_item_bmap(sc, dq, &dirty);
xchk_iunlock(sc, XFS_ILOCK_EXCL);
if (error)
- return error;
+ goto out_unlock_dquot;
/* Check the limits. */
if (dq->q_blk.softlimit > dq->q_blk.hardlimit) {
@@ -246,7 +242,7 @@ xrep_quota_item(
xrep_quota_item_timer(sc, &dq->q_rtb, &dirty);
if (!dirty)
- return 0;
+ goto out_unlock_dquot;
trace_xrep_dquot_item(sc->mp, dq->q_type, dq->q_id);
@@ -257,8 +253,10 @@ xrep_quota_item(
xfs_qm_adjust_dqtimers(dq);
}
xfs_trans_log_dquot(sc->tp, dq);
- error = xfs_trans_roll(&sc->tp);
- xfs_dqlock(dq);
+ return xfs_trans_roll(&sc->tp);
+
+out_unlock_dquot:
+ mutex_unlock(&dq->q_qlock);
return error;
}
@@ -513,7 +511,7 @@ xrep_quota_problems(
xchk_dqiter_init(&cursor, sc, dqtype);
while ((error = xchk_dquot_iter(&cursor, &dq)) == 1) {
error = xrep_quota_item(&rqi, dq);
- xfs_qm_dqput(dq);
+ xfs_qm_dqrele(dq);
if (error)
break;
}
diff --git a/fs/xfs/scrub/quotacheck.c b/fs/xfs/scrub/quotacheck.c
index e4105aaafe84..d412a8359784 100644
--- a/fs/xfs/scrub/quotacheck.c
+++ b/fs/xfs/scrub/quotacheck.c
@@ -563,6 +563,7 @@ xqcheck_compare_dquot(
return -ECANCELED;
}
+ mutex_lock(&dq->q_qlock);
mutex_lock(&xqc->lock);
error = xfarray_load_sparse(counts, dq->q_id, &xcdq);
if (error)
@@ -589,7 +590,9 @@ xqcheck_compare_dquot(
xchk_set_incomplete(xqc->sc);
error = -ECANCELED;
}
+out_unlock:
mutex_unlock(&xqc->lock);
+ mutex_unlock(&dq->q_qlock);
if (error)
return error;
@@ -597,10 +600,6 @@ xqcheck_compare_dquot(
return -ECANCELED;
return 0;
-
-out_unlock:
- mutex_unlock(&xqc->lock);
- return error;
}
/*
@@ -636,7 +635,7 @@ xqcheck_walk_observations(
return error;
error = xqcheck_compare_dquot(xqc, dqtype, dq);
- xfs_qm_dqput(dq);
+ xfs_qm_dqrele(dq);
if (error)
return error;
@@ -674,7 +673,7 @@ xqcheck_compare_dqtype(
xchk_dqiter_init(&cursor, sc, dqtype);
while ((error = xchk_dquot_iter(&cursor, &dq)) == 1) {
error = xqcheck_compare_dquot(xqc, dqtype, dq);
- xfs_qm_dqput(dq);
+ xfs_qm_dqrele(dq);
if (error)
break;
}
diff --git a/fs/xfs/scrub/quotacheck_repair.c b/fs/xfs/scrub/quotacheck_repair.c
index dd8554c755b5..51be8d8d261b 100644
--- a/fs/xfs/scrub/quotacheck_repair.c
+++ b/fs/xfs/scrub/quotacheck_repair.c
@@ -52,13 +52,11 @@ xqcheck_commit_dquot(
bool dirty = false;
int error = 0;
- /* Unlock the dquot just long enough to allocate a transaction. */
- xfs_dqunlock(dq);
error = xchk_trans_alloc(xqc->sc, 0);
- xfs_dqlock(dq);
if (error)
return error;
+ mutex_lock(&dq->q_qlock);
xfs_trans_dqjoin(xqc->sc->tp, dq);
if (xchk_iscan_aborted(&xqc->iscan)) {
@@ -115,23 +113,12 @@ xqcheck_commit_dquot(
if (dq->q_id)
xfs_qm_adjust_dqtimers(dq);
xfs_trans_log_dquot(xqc->sc->tp, dq);
-
- /*
- * Transaction commit unlocks the dquot, so we must re-lock it so that
- * the caller can put the reference (which apparently requires a locked
- * dquot).
- */
- error = xrep_trans_commit(xqc->sc);
- xfs_dqlock(dq);
- return error;
+ return xrep_trans_commit(xqc->sc);
out_unlock:
mutex_unlock(&xqc->lock);
out_cancel:
xchk_trans_cancel(xqc->sc);
-
- /* Re-lock the dquot so the caller can put the reference. */
- xfs_dqlock(dq);
return error;
}
@@ -156,7 +143,7 @@ xqcheck_commit_dqtype(
xchk_dqiter_init(&cursor, sc, dqtype);
while ((error = xchk_dquot_iter(&cursor, &dq)) == 1) {
error = xqcheck_commit_dquot(xqc, dqtype, dq);
- xfs_qm_dqput(dq);
+ xfs_qm_dqrele(dq);
if (error)
break;
}
@@ -187,7 +174,7 @@ xqcheck_commit_dqtype(
return error;
error = xqcheck_commit_dquot(xqc, dqtype, dq);
- xfs_qm_dqput(dq);
+ xfs_qm_dqrele(dq);
if (error)
return error;
diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c
index 0bd8022e47b4..612ca682a513 100644
--- a/fs/xfs/xfs_dquot.c
+++ b/fs/xfs/xfs_dquot.c
@@ -31,7 +31,7 @@
*
* ip->i_lock
* qi->qi_tree_lock
- * dquot->q_qlock (xfs_dqlock() and friends)
+ * dquot->q_qlock
* dquot->q_flush (xfs_dqflock() and friends)
* qi->qi_lru_lock
*
@@ -801,10 +801,11 @@ xfs_dq_get_next_id(
static struct xfs_dquot *
xfs_qm_dqget_cache_lookup(
struct xfs_mount *mp,
- struct xfs_quotainfo *qi,
- struct radix_tree_root *tree,
- xfs_dqid_t id)
+ xfs_dqid_t id,
+ xfs_dqtype_t type)
{
+ struct xfs_quotainfo *qi = mp->m_quotainfo;
+ struct radix_tree_root *tree = xfs_dquot_tree(qi, type);
struct xfs_dquot *dqp;
restart:
@@ -816,16 +817,12 @@ restart:
return NULL;
}
- xfs_dqlock(dqp);
- if (dqp->q_flags & XFS_DQFLAG_FREEING) {
- xfs_dqunlock(dqp);
+ if (!lockref_get_not_dead(&dqp->q_lockref)) {
mutex_unlock(&qi->qi_tree_lock);
trace_xfs_dqget_freeing(dqp);
delay(1);
goto restart;
}
-
- dqp->q_nrefs++;
mutex_unlock(&qi->qi_tree_lock);
trace_xfs_dqget_hit(dqp);
@@ -836,8 +833,7 @@ restart:
/*
* Try to insert a new dquot into the in-core cache. If an error occurs the
* caller should throw away the dquot and start over. Otherwise, the dquot
- * is returned locked (and held by the cache) as if there had been a cache
- * hit.
+ * is returned (and held by the cache) as if there had been a cache hit.
*
* The insert needs to be done under memalloc_nofs context because the radix
* tree can do memory allocation during insert. The qi->qi_tree_lock is taken in
@@ -848,11 +844,12 @@ restart:
static int
xfs_qm_dqget_cache_insert(
struct xfs_mount *mp,
- struct xfs_quotainfo *qi,
- struct radix_tree_root *tree,
xfs_dqid_t id,
+ xfs_dqtype_t type,
struct xfs_dquot *dqp)
{
+ struct xfs_quotainfo *qi = mp->m_quotainfo;
+ struct radix_tree_root *tree = xfs_dquot_tree(qi, type);
unsigned int nofs_flags;
int error;
@@ -860,14 +857,11 @@ xfs_qm_dqget_cache_insert(
mutex_lock(&qi->qi_tree_lock);
error = radix_tree_insert(tree, id, dqp);
if (unlikely(error)) {
- /* Duplicate found! Caller must try again. */
trace_xfs_dqget_dup(dqp);
goto out_unlock;
}
- /* Return a locked dquot to the caller, with a reference taken. */
- xfs_dqlock(dqp);
- dqp->q_nrefs = 1;
+ lockref_init(&dqp->q_lockref);
qi->qi_dquots++;
out_unlock:
@@ -903,7 +897,7 @@ xfs_qm_dqget_checks(
/*
* Given the file system, id, and type (UDQUOT/GDQUOT/PDQUOT), return a
- * locked dquot, doing an allocation (if requested) as needed.
+ * dquot, doing an allocation (if requested) as needed.
*/
int
xfs_qm_dqget(
@@ -913,8 +907,6 @@ xfs_qm_dqget(
bool can_alloc,
struct xfs_dquot **O_dqpp)
{
- struct xfs_quotainfo *qi = mp->m_quotainfo;
- struct radix_tree_root *tree = xfs_dquot_tree(qi, type);
struct xfs_dquot *dqp;
int error;
@@ -923,28 +915,30 @@ xfs_qm_dqget(
return error;
restart:
- dqp = xfs_qm_dqget_cache_lookup(mp, qi, tree, id);
- if (dqp) {
- *O_dqpp = dqp;
- return 0;
- }
+ dqp = xfs_qm_dqget_cache_lookup(mp, id, type);
+ if (dqp)
+ goto found;
error = xfs_qm_dqread(mp, id, type, can_alloc, &dqp);
if (error)
return error;
- error = xfs_qm_dqget_cache_insert(mp, qi, tree, id, dqp);
+ error = xfs_qm_dqget_cache_insert(mp, id, type, dqp);
if (error) {
- /*
- * Duplicate found. Just throw away the new dquot and start
- * over.
- */
xfs_qm_dqdestroy(dqp);
- XFS_STATS_INC(mp, xs_qm_dquot_dups);
- goto restart;
+ if (error == -EEXIST) {
+ /*
+ * Duplicate found. Just throw away the new dquot and
+ * start over.
+ */
+ XFS_STATS_INC(mp, xs_qm_dquot_dups);
+ goto restart;
+ }
+ return error;
}
trace_xfs_dqget_miss(dqp);
+found:
*O_dqpp = dqp;
return 0;
}
@@ -999,15 +993,16 @@ xfs_qm_dqget_inode(
struct xfs_inode *ip,
xfs_dqtype_t type,
bool can_alloc,
- struct xfs_dquot **O_dqpp)
+ struct xfs_dquot **dqpp)
{
struct xfs_mount *mp = ip->i_mount;
- struct xfs_quotainfo *qi = mp->m_quotainfo;
- struct radix_tree_root *tree = xfs_dquot_tree(qi, type);
struct xfs_dquot *dqp;
xfs_dqid_t id;
int error;
+ ASSERT(!*dqpp);
+ xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);
+
error = xfs_qm_dqget_checks(mp, type);
if (error)
return error;
@@ -1019,11 +1014,9 @@ xfs_qm_dqget_inode(
id = xfs_qm_id_for_quotatype(ip, type);
restart:
- dqp = xfs_qm_dqget_cache_lookup(mp, qi, tree, id);
- if (dqp) {
- *O_dqpp = dqp;
- return 0;
- }
+ dqp = xfs_qm_dqget_cache_lookup(mp, id, type);
+ if (dqp)
+ goto found;
/*
* Dquot cache miss. We don't want to keep the inode lock across
@@ -1049,7 +1042,6 @@ restart:
if (dqp1) {
xfs_qm_dqdestroy(dqp);
dqp = dqp1;
- xfs_dqlock(dqp);
goto dqret;
}
} else {
@@ -1058,21 +1050,26 @@ restart:
return -ESRCH;
}
- error = xfs_qm_dqget_cache_insert(mp, qi, tree, id, dqp);
+ error = xfs_qm_dqget_cache_insert(mp, id, type, dqp);
if (error) {
- /*
- * Duplicate found. Just throw away the new dquot and start
- * over.
- */
xfs_qm_dqdestroy(dqp);
- XFS_STATS_INC(mp, xs_qm_dquot_dups);
- goto restart;
+ if (error == -EEXIST) {
+ /*
+ * Duplicate found. Just throw away the new dquot and
+ * start over.
+ */
+ XFS_STATS_INC(mp, xs_qm_dquot_dups);
+ goto restart;
+ }
+ return error;
}
dqret:
xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);
trace_xfs_dqget_miss(dqp);
- *O_dqpp = dqp;
+found:
+ trace_xfs_dqattach_get(dqp);
+ *dqpp = dqp;
return 0;
}
@@ -1098,63 +1095,41 @@ xfs_qm_dqget_next(
else if (error != 0)
break;
+ mutex_lock(&dqp->q_qlock);
if (!XFS_IS_DQUOT_UNINITIALIZED(dqp)) {
*dqpp = dqp;
return 0;
}
- xfs_qm_dqput(dqp);
+ mutex_unlock(&dqp->q_qlock);
+ xfs_qm_dqrele(dqp);
}
return error;
}
/*
- * Release a reference to the dquot (decrement ref-count) and unlock it.
- *
- * If there is a group quota attached to this dquot, carefully release that
- * too without tripping over deadlocks'n'stuff.
+ * Release a reference to the dquot.
*/
void
-xfs_qm_dqput(
+xfs_qm_dqrele(
struct xfs_dquot *dqp)
{
- ASSERT(dqp->q_nrefs > 0);
- ASSERT(XFS_DQ_IS_LOCKED(dqp));
+ if (!dqp)
+ return;
- trace_xfs_dqput(dqp);
+ trace_xfs_dqrele(dqp);
- if (--dqp->q_nrefs == 0) {
+ if (lockref_put_or_lock(&dqp->q_lockref))
+ return;
+ if (!--dqp->q_lockref.count) {
struct xfs_quotainfo *qi = dqp->q_mount->m_quotainfo;
- trace_xfs_dqput_free(dqp);
+ trace_xfs_dqrele_free(dqp);
if (list_lru_add_obj(&qi->qi_lru, &dqp->q_lru))
XFS_STATS_INC(dqp->q_mount, xs_qm_dquot_unused);
}
- xfs_dqunlock(dqp);
-}
-
-/*
- * Release a dquot. Flush it if dirty, then dqput() it.
- * dquot must not be locked.
- */
-void
-xfs_qm_dqrele(
- struct xfs_dquot *dqp)
-{
- if (!dqp)
- return;
-
- trace_xfs_dqrele(dqp);
-
- xfs_dqlock(dqp);
- /*
- * We don't care to flush it if the dquot is dirty here.
- * That will create stutters that we want to avoid.
- * Instead we do a delayed write when we try to reclaim
- * a dirty dquot. Also xfs_sync will take part of the burden...
- */
- xfs_qm_dqput(dqp);
+ spin_unlock(&dqp->q_lockref.lock);
}
/*
diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h
index 61217adf5ba5..bbb824adca82 100644
--- a/fs/xfs/xfs_dquot.h
+++ b/fs/xfs/xfs_dquot.h
@@ -71,7 +71,7 @@ struct xfs_dquot {
xfs_dqtype_t q_type;
uint16_t q_flags;
xfs_dqid_t q_id;
- uint q_nrefs;
+ struct lockref q_lockref;
int q_bufoffset;
xfs_daddr_t q_blkno;
xfs_fileoff_t q_fileoffset;
@@ -121,21 +121,6 @@ static inline void xfs_dqfunlock(struct xfs_dquot *dqp)
complete(&dqp->q_flush);
}
-static inline int xfs_dqlock_nowait(struct xfs_dquot *dqp)
-{
- return mutex_trylock(&dqp->q_qlock);
-}
-
-static inline void xfs_dqlock(struct xfs_dquot *dqp)
-{
- mutex_lock(&dqp->q_qlock);
-}
-
-static inline void xfs_dqunlock(struct xfs_dquot *dqp)
-{
- mutex_unlock(&dqp->q_qlock);
-}
-
static inline int
xfs_dquot_type(const struct xfs_dquot *dqp)
{
@@ -233,7 +218,6 @@ int xfs_qm_dqget_next(struct xfs_mount *mp, xfs_dqid_t id,
int xfs_qm_dqget_uncached(struct xfs_mount *mp,
xfs_dqid_t id, xfs_dqtype_t type,
struct xfs_dquot **dqpp);
-void xfs_qm_dqput(struct xfs_dquot *dqp);
void xfs_dqlock2(struct xfs_dquot *, struct xfs_dquot *);
void xfs_dqlockn(struct xfs_dqtrx *q);
@@ -246,9 +230,7 @@ void xfs_dquot_detach_buf(struct xfs_dquot *dqp);
static inline struct xfs_dquot *xfs_qm_dqhold(struct xfs_dquot *dqp)
{
- xfs_dqlock(dqp);
- dqp->q_nrefs++;
- xfs_dqunlock(dqp);
+ lockref_get(&dqp->q_lockref);
return dqp;
}
diff --git a/fs/xfs/xfs_dquot_item.c b/fs/xfs/xfs_dquot_item.c
index 271b195ebb93..b374cd9f1900 100644
--- a/fs/xfs/xfs_dquot_item.c
+++ b/fs/xfs/xfs_dquot_item.c
@@ -132,7 +132,7 @@ xfs_qm_dquot_logitem_push(
if (atomic_read(&dqp->q_pincount) > 0)
return XFS_ITEM_PINNED;
- if (!xfs_dqlock_nowait(dqp))
+ if (!mutex_trylock(&dqp->q_qlock))
return XFS_ITEM_LOCKED;
/*
@@ -177,7 +177,7 @@ xfs_qm_dquot_logitem_push(
out_relock_ail:
spin_lock(&lip->li_ailp->ail_lock);
out_unlock:
- xfs_dqunlock(dqp);
+ mutex_unlock(&dqp->q_qlock);
return rval;
}
@@ -195,7 +195,7 @@ xfs_qm_dquot_logitem_release(
* transaction layer, within trans_commit. Hence, no LI_HOLD flag
* for the logitem.
*/
- xfs_dqunlock(dqp);
+ mutex_unlock(&dqp->q_qlock);
}
STATIC void
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index f3fc4d21bfe1..23a920437fe4 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -358,7 +358,7 @@ xfs_reinit_inode(
static int
xfs_iget_recycle(
struct xfs_perag *pag,
- struct xfs_inode *ip) __releases(&ip->i_flags_lock)
+ struct xfs_inode *ip)
{
struct xfs_mount *mp = ip->i_mount;
struct inode *inode = VFS_I(ip);
@@ -366,20 +366,6 @@ xfs_iget_recycle(
trace_xfs_iget_recycle(ip);
- if (!xfs_ilock_nowait(ip, XFS_ILOCK_EXCL))
- return -EAGAIN;
-
- /*
- * We need to make it look like the inode is being reclaimed to prevent
- * the actual reclaim workers from stomping over us while we recycle
- * the inode. We can't clear the radix tree tag yet as it requires
- * pag_ici_lock to be held exclusive.
- */
- ip->i_flags |= XFS_IRECLAIM;
-
- spin_unlock(&ip->i_flags_lock);
- rcu_read_unlock();
-
ASSERT(!rwsem_is_locked(&inode->i_rwsem));
error = xfs_reinit_inode(mp, inode);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
@@ -576,10 +562,19 @@ xfs_iget_cache_hit(
/* The inode fits the selection criteria; process it. */
if (ip->i_flags & XFS_IRECLAIMABLE) {
- /* Drops i_flags_lock and RCU read lock. */
- error = xfs_iget_recycle(pag, ip);
- if (error == -EAGAIN)
+ /*
+ * We need to make it look like the inode is being reclaimed to
+ * prevent the actual reclaim workers from stomping over us
+ * while we recycle the inode. We can't clear the radix tree
+ * tag yet as it requires pag_ici_lock to be held exclusive.
+ */
+ if (!xfs_ilock_nowait(ip, XFS_ILOCK_EXCL))
goto out_skip;
+ ip->i_flags |= XFS_IRECLAIM;
+ spin_unlock(&ip->i_flags_lock);
+ rcu_read_unlock();
+
+ error = xfs_iget_recycle(pag, ip);
if (error)
return error;
} else {
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index 603e85c1ab4c..a311385b23d8 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -534,8 +534,8 @@ xlog_state_release_iclog(
*/
if ((iclog->ic_state == XLOG_STATE_WANT_SYNC ||
(iclog->ic_flags & XLOG_ICL_NEED_FUA)) &&
- !iclog->ic_header.h_tail_lsn) {
- iclog->ic_header.h_tail_lsn =
+ !iclog->ic_header->h_tail_lsn) {
+ iclog->ic_header->h_tail_lsn =
cpu_to_be64(atomic64_read(&log->l_tail_lsn));
}
@@ -1279,11 +1279,12 @@ xlog_get_iclog_buffer_size(
log->l_iclog_size = mp->m_logbsize;
/*
- * # headers = size / 32k - one header holds cycles from 32k of data.
+ * Combined size of the log record headers. The first 32k cycles
+ * are stored directly in the xlog_rec_header, the rest in the
+ * variable number of xlog_rec_ext_headers at its end.
*/
- log->l_iclog_heads =
- DIV_ROUND_UP(mp->m_logbsize, XLOG_HEADER_CYCLE_SIZE);
- log->l_iclog_hsize = log->l_iclog_heads << BBSHIFT;
+ log->l_iclog_hsize = struct_size(log->l_iclog->ic_header, h_ext,
+ DIV_ROUND_UP(mp->m_logbsize, XLOG_HEADER_CYCLE_SIZE) - 1);
}
void
@@ -1367,9 +1368,8 @@ xlog_alloc_log(
int num_bblks)
{
struct xlog *log;
- xlog_rec_header_t *head;
- xlog_in_core_t **iclogp;
- xlog_in_core_t *iclog, *prev_iclog=NULL;
+ struct xlog_in_core **iclogp;
+ struct xlog_in_core *iclog, *prev_iclog = NULL;
int i;
int error = -ENOMEM;
uint log2_size = 0;
@@ -1436,13 +1436,6 @@ xlog_alloc_log(
init_waitqueue_head(&log->l_flush_wait);
iclogp = &log->l_iclog;
- /*
- * The amount of memory to allocate for the iclog structure is
- * rather funky due to the way the structure is defined. It is
- * done this way so that we can use different sizes for machines
- * with different amounts of memory. See the definition of
- * xlog_in_core_t in xfs_log_priv.h for details.
- */
ASSERT(log->l_iclog_size >= 4096);
for (i = 0; i < log->l_iclog_bufs; i++) {
size_t bvec_size = howmany(log->l_iclog_size, PAGE_SIZE) *
@@ -1457,26 +1450,25 @@ xlog_alloc_log(
iclog->ic_prev = prev_iclog;
prev_iclog = iclog;
- iclog->ic_data = kvzalloc(log->l_iclog_size,
+ iclog->ic_header = kvzalloc(log->l_iclog_size,
GFP_KERNEL | __GFP_RETRY_MAYFAIL);
- if (!iclog->ic_data)
+ if (!iclog->ic_header)
goto out_free_iclog;
- head = &iclog->ic_header;
- memset(head, 0, sizeof(xlog_rec_header_t));
- head->h_magicno = cpu_to_be32(XLOG_HEADER_MAGIC_NUM);
- head->h_version = cpu_to_be32(
+ iclog->ic_header->h_magicno =
+ cpu_to_be32(XLOG_HEADER_MAGIC_NUM);
+ iclog->ic_header->h_version = cpu_to_be32(
xfs_has_logv2(log->l_mp) ? 2 : 1);
- head->h_size = cpu_to_be32(log->l_iclog_size);
- /* new fields */
- head->h_fmt = cpu_to_be32(XLOG_FMT);
- memcpy(&head->h_fs_uuid, &mp->m_sb.sb_uuid, sizeof(uuid_t));
+ iclog->ic_header->h_size = cpu_to_be32(log->l_iclog_size);
+ iclog->ic_header->h_fmt = cpu_to_be32(XLOG_FMT);
+ memcpy(&iclog->ic_header->h_fs_uuid, &mp->m_sb.sb_uuid,
+ sizeof(iclog->ic_header->h_fs_uuid));
+ iclog->ic_datap = (void *)iclog->ic_header + log->l_iclog_hsize;
iclog->ic_size = log->l_iclog_size - log->l_iclog_hsize;
iclog->ic_state = XLOG_STATE_ACTIVE;
iclog->ic_log = log;
atomic_set(&iclog->ic_refcnt, 0);
INIT_LIST_HEAD(&iclog->ic_callbacks);
- iclog->ic_datap = (void *)iclog->ic_data + log->l_iclog_hsize;
init_waitqueue_head(&iclog->ic_force_wait);
init_waitqueue_head(&iclog->ic_write_wait);
@@ -1504,7 +1496,7 @@ out_destroy_workqueue:
out_free_iclog:
for (iclog = log->l_iclog; iclog; iclog = prev_iclog) {
prev_iclog = iclog->ic_next;
- kvfree(iclog->ic_data);
+ kvfree(iclog->ic_header);
kfree(iclog);
if (prev_iclog == log->l_iclog)
break;
@@ -1524,36 +1516,19 @@ xlog_pack_data(
struct xlog_in_core *iclog,
int roundoff)
{
- int i, j, k;
- int size = iclog->ic_offset + roundoff;
- __be32 cycle_lsn;
- char *dp;
-
- cycle_lsn = CYCLE_LSN_DISK(iclog->ic_header.h_lsn);
+ struct xlog_rec_header *rhead = iclog->ic_header;
+ __be32 cycle_lsn = CYCLE_LSN_DISK(rhead->h_lsn);
+ char *dp = iclog->ic_datap;
+ int i;
- dp = iclog->ic_datap;
- for (i = 0; i < BTOBB(size); i++) {
- if (i >= (XLOG_HEADER_CYCLE_SIZE / BBSIZE))
- break;
- iclog->ic_header.h_cycle_data[i] = *(__be32 *)dp;
+ for (i = 0; i < BTOBB(iclog->ic_offset + roundoff); i++) {
+ *xlog_cycle_data(rhead, i) = *(__be32 *)dp;
*(__be32 *)dp = cycle_lsn;
dp += BBSIZE;
}
- if (xfs_has_logv2(log->l_mp)) {
- xlog_in_core_2_t *xhdr = iclog->ic_data;
-
- for ( ; i < BTOBB(size); i++) {
- j = i / (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
- k = i % (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
- xhdr[j].hic_xheader.xh_cycle_data[k] = *(__be32 *)dp;
- *(__be32 *)dp = cycle_lsn;
- dp += BBSIZE;
- }
-
- for (i = 1; i < log->l_iclog_heads; i++)
- xhdr[i].hic_xheader.xh_cycle = cycle_lsn;
- }
+ for (i = 0; i < (log->l_iclog_hsize >> BBSHIFT) - 1; i++)
+ rhead->h_ext[i].xh_cycle = cycle_lsn;
}
/*
@@ -1578,16 +1553,11 @@ xlog_cksum(
/* ... then for additional cycle data for v2 logs ... */
if (xfs_has_logv2(log->l_mp)) {
- union xlog_in_core2 *xhdr = (union xlog_in_core2 *)rhead;
- int i;
- int xheads;
+ int xheads, i;
- xheads = DIV_ROUND_UP(size, XLOG_HEADER_CYCLE_SIZE);
-
- for (i = 1; i < xheads; i++) {
- crc = crc32c(crc, &xhdr[i].hic_xheader,
- sizeof(struct xlog_rec_ext_header));
- }
+ xheads = DIV_ROUND_UP(size, XLOG_HEADER_CYCLE_SIZE) - 1;
+ for (i = 0; i < xheads; i++)
+ crc = crc32c(crc, &rhead->h_ext[i], XLOG_REC_EXT_SIZE);
}
/* ... and finally for the payload */
@@ -1671,11 +1641,11 @@ xlog_write_iclog(
iclog->ic_flags &= ~(XLOG_ICL_NEED_FLUSH | XLOG_ICL_NEED_FUA);
- if (is_vmalloc_addr(iclog->ic_data)) {
- if (!bio_add_vmalloc(&iclog->ic_bio, iclog->ic_data, count))
+ if (is_vmalloc_addr(iclog->ic_header)) {
+ if (!bio_add_vmalloc(&iclog->ic_bio, iclog->ic_header, count))
goto shutdown;
} else {
- bio_add_virt_nofail(&iclog->ic_bio, iclog->ic_data, count);
+ bio_add_virt_nofail(&iclog->ic_bio, iclog->ic_header, count);
}
/*
@@ -1804,19 +1774,19 @@ xlog_sync(
size = iclog->ic_offset;
if (xfs_has_logv2(log->l_mp))
size += roundoff;
- iclog->ic_header.h_len = cpu_to_be32(size);
+ iclog->ic_header->h_len = cpu_to_be32(size);
XFS_STATS_INC(log->l_mp, xs_log_writes);
XFS_STATS_ADD(log->l_mp, xs_log_blocks, BTOBB(count));
- bno = BLOCK_LSN(be64_to_cpu(iclog->ic_header.h_lsn));
+ bno = BLOCK_LSN(be64_to_cpu(iclog->ic_header->h_lsn));
/* Do we need to split this write into 2 parts? */
if (bno + BTOBB(count) > log->l_logBBsize)
- xlog_split_iclog(log, &iclog->ic_header, bno, count);
+ xlog_split_iclog(log, iclog->ic_header, bno, count);
/* calculcate the checksum */
- iclog->ic_header.h_crc = xlog_cksum(log, &iclog->ic_header,
+ iclog->ic_header->h_crc = xlog_cksum(log, iclog->ic_header,
iclog->ic_datap, XLOG_REC_SIZE, size);
/*
* Intentionally corrupt the log record CRC based on the error injection
@@ -1827,11 +1797,11 @@ xlog_sync(
*/
#ifdef DEBUG
if (XFS_TEST_ERROR(log->l_mp, XFS_ERRTAG_LOG_BAD_CRC)) {
- iclog->ic_header.h_crc &= cpu_to_le32(0xAAAAAAAA);
+ iclog->ic_header->h_crc &= cpu_to_le32(0xAAAAAAAA);
iclog->ic_fail_crc = true;
xfs_warn(log->l_mp,
"Intentionally corrupted log record at LSN 0x%llx. Shutdown imminent.",
- be64_to_cpu(iclog->ic_header.h_lsn));
+ be64_to_cpu(iclog->ic_header->h_lsn));
}
#endif
xlog_verify_iclog(log, iclog, count);
@@ -1843,10 +1813,10 @@ xlog_sync(
*/
STATIC void
xlog_dealloc_log(
- struct xlog *log)
+ struct xlog *log)
{
- xlog_in_core_t *iclog, *next_iclog;
- int i;
+ struct xlog_in_core *iclog, *next_iclog;
+ int i;
/*
* Destroy the CIL after waiting for iclog IO completion because an
@@ -1858,7 +1828,7 @@ xlog_dealloc_log(
iclog = log->l_iclog;
for (i = 0; i < log->l_iclog_bufs; i++) {
next_iclog = iclog->ic_next;
- kvfree(iclog->ic_data);
+ kvfree(iclog->ic_header);
kfree(iclog);
iclog = next_iclog;
}
@@ -1880,7 +1850,7 @@ xlog_state_finish_copy(
{
lockdep_assert_held(&log->l_icloglock);
- be32_add_cpu(&iclog->ic_header.h_num_logops, record_cnt);
+ be32_add_cpu(&iclog->ic_header->h_num_logops, record_cnt);
iclog->ic_offset += copy_bytes;
}
@@ -2303,7 +2273,7 @@ xlog_state_activate_iclog(
* We don't need to cover the dummy.
*/
if (*iclogs_changed == 0 &&
- iclog->ic_header.h_num_logops == cpu_to_be32(XLOG_COVER_OPS)) {
+ iclog->ic_header->h_num_logops == cpu_to_be32(XLOG_COVER_OPS)) {
*iclogs_changed = 1;
} else {
/*
@@ -2315,11 +2285,11 @@ xlog_state_activate_iclog(
iclog->ic_state = XLOG_STATE_ACTIVE;
iclog->ic_offset = 0;
- iclog->ic_header.h_num_logops = 0;
- memset(iclog->ic_header.h_cycle_data, 0,
- sizeof(iclog->ic_header.h_cycle_data));
- iclog->ic_header.h_lsn = 0;
- iclog->ic_header.h_tail_lsn = 0;
+ iclog->ic_header->h_num_logops = 0;
+ memset(iclog->ic_header->h_cycle_data, 0,
+ sizeof(iclog->ic_header->h_cycle_data));
+ iclog->ic_header->h_lsn = 0;
+ iclog->ic_header->h_tail_lsn = 0;
}
/*
@@ -2411,7 +2381,7 @@ xlog_get_lowest_lsn(
iclog->ic_state == XLOG_STATE_DIRTY)
continue;
- lsn = be64_to_cpu(iclog->ic_header.h_lsn);
+ lsn = be64_to_cpu(iclog->ic_header->h_lsn);
if ((lsn && !lowest_lsn) || XFS_LSN_CMP(lsn, lowest_lsn) < 0)
lowest_lsn = lsn;
} while ((iclog = iclog->ic_next) != log->l_iclog);
@@ -2446,7 +2416,7 @@ xlog_state_iodone_process_iclog(
* If this is not the lowest lsn iclog, then we will leave it
* for another completion to process.
*/
- header_lsn = be64_to_cpu(iclog->ic_header.h_lsn);
+ header_lsn = be64_to_cpu(iclog->ic_header->h_lsn);
lowest_lsn = xlog_get_lowest_lsn(log);
if (lowest_lsn && XFS_LSN_CMP(lowest_lsn, header_lsn) < 0)
return false;
@@ -2609,9 +2579,9 @@ xlog_state_get_iclog_space(
struct xlog_ticket *ticket,
int *logoffsetp)
{
- int log_offset;
- xlog_rec_header_t *head;
- xlog_in_core_t *iclog;
+ int log_offset;
+ struct xlog_rec_header *head;
+ struct xlog_in_core *iclog;
restart:
spin_lock(&log->l_icloglock);
@@ -2629,7 +2599,7 @@ restart:
goto restart;
}
- head = &iclog->ic_header;
+ head = iclog->ic_header;
atomic_inc(&iclog->ic_refcnt); /* prevents sync */
log_offset = iclog->ic_offset;
@@ -2794,7 +2764,7 @@ xlog_state_switch_iclogs(
if (!eventual_size)
eventual_size = iclog->ic_offset;
iclog->ic_state = XLOG_STATE_WANT_SYNC;
- iclog->ic_header.h_prev_block = cpu_to_be32(log->l_prev_block);
+ iclog->ic_header->h_prev_block = cpu_to_be32(log->l_prev_block);
log->l_prev_block = log->l_curr_block;
log->l_prev_cycle = log->l_curr_cycle;
@@ -2838,7 +2808,7 @@ xlog_force_and_check_iclog(
struct xlog_in_core *iclog,
bool *completed)
{
- xfs_lsn_t lsn = be64_to_cpu(iclog->ic_header.h_lsn);
+ xfs_lsn_t lsn = be64_to_cpu(iclog->ic_header->h_lsn);
int error;
*completed = false;
@@ -2850,7 +2820,7 @@ xlog_force_and_check_iclog(
* If the iclog has already been completed and reused the header LSN
* will have been rewritten by completion
*/
- if (be64_to_cpu(iclog->ic_header.h_lsn) != lsn)
+ if (be64_to_cpu(iclog->ic_header->h_lsn) != lsn)
*completed = true;
return 0;
}
@@ -2983,7 +2953,7 @@ xlog_force_lsn(
goto out_error;
iclog = log->l_iclog;
- while (be64_to_cpu(iclog->ic_header.h_lsn) != lsn) {
+ while (be64_to_cpu(iclog->ic_header->h_lsn) != lsn) {
trace_xlog_iclog_force_lsn(iclog, _RET_IP_);
iclog = iclog->ic_next;
if (iclog == log->l_iclog)
@@ -3249,7 +3219,7 @@ xlog_verify_dump_tail(
{
xfs_alert(log->l_mp,
"ran out of log space tail 0x%llx/0x%llx, head lsn 0x%llx, head 0x%x/0x%x, prev head 0x%x/0x%x",
- iclog ? be64_to_cpu(iclog->ic_header.h_tail_lsn) : -1,
+ iclog ? be64_to_cpu(iclog->ic_header->h_tail_lsn) : -1,
atomic64_read(&log->l_tail_lsn),
log->l_ailp->ail_head_lsn,
log->l_curr_cycle, log->l_curr_block,
@@ -3268,7 +3238,7 @@ xlog_verify_tail_lsn(
struct xlog *log,
struct xlog_in_core *iclog)
{
- xfs_lsn_t tail_lsn = be64_to_cpu(iclog->ic_header.h_tail_lsn);
+ xfs_lsn_t tail_lsn = be64_to_cpu(iclog->ic_header->h_tail_lsn);
int blocks;
if (CYCLE_LSN(tail_lsn) == log->l_prev_cycle) {
@@ -3322,13 +3292,12 @@ xlog_verify_iclog(
struct xlog_in_core *iclog,
int count)
{
- struct xlog_op_header *ophead;
- xlog_in_core_t *icptr;
- xlog_in_core_2_t *xhdr;
- void *base_ptr, *ptr, *p;
+ struct xlog_rec_header *rhead = iclog->ic_header;
+ struct xlog_in_core *icptr;
+ void *base_ptr, *ptr;
ptrdiff_t field_offset;
uint8_t clientid;
- int len, i, j, k, op_len;
+ int len, i, op_len;
int idx;
/* check validity of iclog pointers */
@@ -3342,11 +3311,10 @@ xlog_verify_iclog(
spin_unlock(&log->l_icloglock);
/* check log magic numbers */
- if (iclog->ic_header.h_magicno != cpu_to_be32(XLOG_HEADER_MAGIC_NUM))
+ if (rhead->h_magicno != cpu_to_be32(XLOG_HEADER_MAGIC_NUM))
xfs_emerg(log->l_mp, "%s: invalid magic num", __func__);
- base_ptr = ptr = &iclog->ic_header;
- p = &iclog->ic_header;
+ base_ptr = ptr = rhead;
for (ptr += BBSIZE; ptr < base_ptr + count; ptr += BBSIZE) {
if (*(__be32 *)ptr == cpu_to_be32(XLOG_HEADER_MAGIC_NUM))
xfs_emerg(log->l_mp, "%s: unexpected magic num",
@@ -3354,29 +3322,19 @@ xlog_verify_iclog(
}
/* check fields */
- len = be32_to_cpu(iclog->ic_header.h_num_logops);
+ len = be32_to_cpu(rhead->h_num_logops);
base_ptr = ptr = iclog->ic_datap;
- ophead = ptr;
- xhdr = iclog->ic_data;
for (i = 0; i < len; i++) {
- ophead = ptr;
+ struct xlog_op_header *ophead = ptr;
+ void *p = &ophead->oh_clientid;
/* clientid is only 1 byte */
- p = &ophead->oh_clientid;
field_offset = p - base_ptr;
if (field_offset & 0x1ff) {
clientid = ophead->oh_clientid;
} else {
idx = BTOBBT((void *)&ophead->oh_clientid - iclog->ic_datap);
- if (idx >= (XLOG_HEADER_CYCLE_SIZE / BBSIZE)) {
- j = idx / (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
- k = idx % (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
- clientid = xlog_get_client_id(
- xhdr[j].hic_xheader.xh_cycle_data[k]);
- } else {
- clientid = xlog_get_client_id(
- iclog->ic_header.h_cycle_data[idx]);
- }
+ clientid = xlog_get_client_id(*xlog_cycle_data(rhead, idx));
}
if (clientid != XFS_TRANSACTION && clientid != XFS_LOG) {
xfs_warn(log->l_mp,
@@ -3392,13 +3350,7 @@ xlog_verify_iclog(
op_len = be32_to_cpu(ophead->oh_len);
} else {
idx = BTOBBT((void *)&ophead->oh_len - iclog->ic_datap);
- if (idx >= (XLOG_HEADER_CYCLE_SIZE / BBSIZE)) {
- j = idx / (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
- k = idx % (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
- op_len = be32_to_cpu(xhdr[j].hic_xheader.xh_cycle_data[k]);
- } else {
- op_len = be32_to_cpu(iclog->ic_header.h_cycle_data[idx]);
- }
+ op_len = be32_to_cpu(*xlog_cycle_data(rhead, idx));
}
ptr += sizeof(struct xlog_op_header) + op_len;
}
@@ -3529,19 +3481,19 @@ xlog_force_shutdown(
STATIC int
xlog_iclogs_empty(
- struct xlog *log)
+ struct xlog *log)
{
- xlog_in_core_t *iclog;
+ struct xlog_in_core *iclog = log->l_iclog;
- iclog = log->l_iclog;
do {
/* endianness does not matter here, zero is zero in
* any language.
*/
- if (iclog->ic_header.h_num_logops)
+ if (iclog->ic_header->h_num_logops)
return 0;
iclog = iclog->ic_next;
} while (iclog != log->l_iclog);
+
return 1;
}
diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c
index f443757e93c2..778ac47adb8c 100644
--- a/fs/xfs/xfs_log_cil.c
+++ b/fs/xfs/xfs_log_cil.c
@@ -940,7 +940,7 @@ xlog_cil_set_ctx_write_state(
struct xlog_in_core *iclog)
{
struct xfs_cil *cil = ctx->cil;
- xfs_lsn_t lsn = be64_to_cpu(iclog->ic_header.h_lsn);
+ xfs_lsn_t lsn = be64_to_cpu(iclog->ic_header->h_lsn);
ASSERT(!ctx->commit_lsn);
if (!ctx->start_lsn) {
@@ -1458,9 +1458,9 @@ xlog_cil_push_work(
*/
spin_lock(&log->l_icloglock);
if (ctx->start_lsn != ctx->commit_lsn) {
- xfs_lsn_t plsn;
+ xfs_lsn_t plsn = be64_to_cpu(
+ ctx->commit_iclog->ic_prev->ic_header->h_lsn);
- plsn = be64_to_cpu(ctx->commit_iclog->ic_prev->ic_header.h_lsn);
if (plsn && XFS_LSN_CMP(plsn, ctx->commit_lsn) < 0) {
/*
* Waiting on ic_force_wait orders the completion of
diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h
index 0cfc654d8e87..0fe59f0525aa 100644
--- a/fs/xfs/xfs_log_priv.h
+++ b/fs/xfs/xfs_log_priv.h
@@ -158,10 +158,8 @@ struct xlog_ticket {
};
/*
- * - A log record header is 512 bytes. There is plenty of room to grow the
- * xlog_rec_header_t into the reserved space.
- * - ic_data follows, so a write to disk can start at the beginning of
- * the iclog.
+ * In-core log structure.
+ *
* - ic_forcewait is used to implement synchronous forcing of the iclog to disk.
* - ic_next is the pointer to the next iclog in the ring.
* - ic_log is a pointer back to the global log structure.
@@ -183,7 +181,7 @@ struct xlog_ticket {
* We'll put all the read-only and l_icloglock fields in the first cacheline,
* and move everything else out to subsequent cachelines.
*/
-typedef struct xlog_in_core {
+struct xlog_in_core {
wait_queue_head_t ic_force_wait;
wait_queue_head_t ic_write_wait;
struct xlog_in_core *ic_next;
@@ -198,8 +196,7 @@ typedef struct xlog_in_core {
/* reference counts need their own cacheline */
atomic_t ic_refcnt ____cacheline_aligned_in_smp;
- xlog_in_core_2_t *ic_data;
-#define ic_header ic_data->hic_header
+ struct xlog_rec_header *ic_header;
#ifdef DEBUG
bool ic_fail_crc : 1;
#endif
@@ -207,7 +204,7 @@ typedef struct xlog_in_core {
struct work_struct ic_end_io_work;
struct bio ic_bio;
struct bio_vec ic_bvec[];
-} xlog_in_core_t;
+};
/*
* The CIL context is used to aggregate per-transaction details as well be
@@ -409,7 +406,6 @@ struct xlog {
struct list_head *l_buf_cancel_table;
struct list_head r_dfops; /* recovered log intent items */
int l_iclog_hsize; /* size of iclog header */
- int l_iclog_heads; /* # of iclog header sectors */
uint l_sectBBsize; /* sector size in BBs (2^n) */
int l_iclog_size; /* size of log in bytes */
int l_iclog_bufs; /* number of iclog buffers */
@@ -422,7 +418,7 @@ struct xlog {
/* waiting for iclog flush */
int l_covered_state;/* state of "covering disk
* log entries" */
- xlog_in_core_t *l_iclog; /* head log queue */
+ struct xlog_in_core *l_iclog; /* head log queue */
spinlock_t l_icloglock; /* grab to change iclog state */
int l_curr_cycle; /* Cycle number of log writes */
int l_prev_cycle; /* Cycle number before last
@@ -711,4 +707,21 @@ xlog_item_space(
return round_up(nbytes, sizeof(uint64_t));
}
+/*
+ * Cycles over XLOG_CYCLE_DATA_SIZE overflow into the extended header that was
+ * added for v2 logs. Addressing for the cycles array there is off by one,
+ * because the first batch of cycles is in the original header.
+ */
+static inline __be32 *xlog_cycle_data(struct xlog_rec_header *rhead, unsigned i)
+{
+ if (i >= XLOG_CYCLE_DATA_SIZE) {
+ unsigned j = i / XLOG_CYCLE_DATA_SIZE;
+ unsigned k = i % XLOG_CYCLE_DATA_SIZE;
+
+ return &rhead->h_ext[j - 1].xh_cycle_data[k];
+ }
+
+ return &rhead->h_cycle_data[i];
+}
+
#endif /* __XFS_LOG_PRIV_H__ */
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 549d60959aee..03e42c7dab56 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -190,8 +190,8 @@ xlog_bwrite(
*/
STATIC void
xlog_header_check_dump(
- xfs_mount_t *mp,
- xlog_rec_header_t *head)
+ struct xfs_mount *mp,
+ struct xlog_rec_header *head)
{
xfs_debug(mp, "%s: SB : uuid = %pU, fmt = %d",
__func__, &mp->m_sb.sb_uuid, XLOG_FMT);
@@ -207,8 +207,8 @@ xlog_header_check_dump(
*/
STATIC int
xlog_header_check_recover(
- xfs_mount_t *mp,
- xlog_rec_header_t *head)
+ struct xfs_mount *mp,
+ struct xlog_rec_header *head)
{
ASSERT(head->h_magicno == cpu_to_be32(XLOG_HEADER_MAGIC_NUM));
@@ -238,8 +238,8 @@ xlog_header_check_recover(
*/
STATIC int
xlog_header_check_mount(
- xfs_mount_t *mp,
- xlog_rec_header_t *head)
+ struct xfs_mount *mp,
+ struct xlog_rec_header *head)
{
ASSERT(head->h_magicno == cpu_to_be32(XLOG_HEADER_MAGIC_NUM));
@@ -400,7 +400,7 @@ xlog_find_verify_log_record(
xfs_daddr_t i;
char *buffer;
char *offset = NULL;
- xlog_rec_header_t *head = NULL;
+ struct xlog_rec_header *head = NULL;
int error = 0;
int smallmem = 0;
int num_blks = *last_blk - start_blk;
@@ -437,7 +437,7 @@ xlog_find_verify_log_record(
goto out;
}
- head = (xlog_rec_header_t *)offset;
+ head = (struct xlog_rec_header *)offset;
if (head->h_magicno == cpu_to_be32(XLOG_HEADER_MAGIC_NUM))
break;
@@ -1237,7 +1237,7 @@ xlog_find_tail(
xfs_daddr_t *head_blk,
xfs_daddr_t *tail_blk)
{
- xlog_rec_header_t *rhead;
+ struct xlog_rec_header *rhead;
char *offset = NULL;
char *buffer;
int error;
@@ -1487,7 +1487,7 @@ xlog_add_record(
int tail_cycle,
int tail_block)
{
- xlog_rec_header_t *recp = (xlog_rec_header_t *)buf;
+ struct xlog_rec_header *recp = (struct xlog_rec_header *)buf;
memset(buf, 0, BBSIZE);
recp->h_magicno = cpu_to_be32(XLOG_HEADER_MAGIC_NUM);
@@ -2863,23 +2863,12 @@ xlog_unpack_data(
char *dp,
struct xlog *log)
{
- int i, j, k;
+ int i;
- for (i = 0; i < BTOBB(be32_to_cpu(rhead->h_len)) &&
- i < (XLOG_HEADER_CYCLE_SIZE / BBSIZE); i++) {
- *(__be32 *)dp = *(__be32 *)&rhead->h_cycle_data[i];
+ for (i = 0; i < BTOBB(be32_to_cpu(rhead->h_len)); i++) {
+ *(__be32 *)dp = *xlog_cycle_data(rhead, i);
dp += BBSIZE;
}
-
- if (xfs_has_logv2(log->l_mp)) {
- xlog_in_core_2_t *xhdr = (xlog_in_core_2_t *)rhead;
- for ( ; i < BTOBB(be32_to_cpu(rhead->h_len)); i++) {
- j = i / (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
- k = i % (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
- *(__be32 *)dp = xhdr[j].hic_xheader.xh_cycle_data[k];
- dp += BBSIZE;
- }
- }
}
/*
@@ -3008,7 +2997,7 @@ xlog_do_recovery_pass(
int pass,
xfs_daddr_t *first_bad) /* out: first bad log rec */
{
- xlog_rec_header_t *rhead;
+ struct xlog_rec_header *rhead;
xfs_daddr_t blk_no, rblk_no;
xfs_daddr_t rhead_blk;
char *offset;
@@ -3045,7 +3034,7 @@ xlog_do_recovery_pass(
if (error)
goto bread_err1;
- rhead = (xlog_rec_header_t *)offset;
+ rhead = (struct xlog_rec_header *)offset;
/*
* xfsprogs has a bug where record length is based on lsunit but
@@ -3152,7 +3141,7 @@ xlog_do_recovery_pass(
if (error)
goto bread_err2;
}
- rhead = (xlog_rec_header_t *)offset;
+ rhead = (struct xlog_rec_header *)offset;
error = xlog_valid_rec_header(log, rhead,
split_hblks ? blk_no : 0, h_size);
if (error)
@@ -3234,7 +3223,7 @@ xlog_do_recovery_pass(
if (error)
goto bread_err2;
- rhead = (xlog_rec_header_t *)offset;
+ rhead = (struct xlog_rec_header *)offset;
error = xlog_valid_rec_header(log, rhead, blk_no, h_size);
if (error)
goto bread_err2;
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index 23ba84ec919a..95be67ac6eb4 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -126,14 +126,16 @@ xfs_qm_dqpurge(
void *data)
{
struct xfs_quotainfo *qi = dqp->q_mount->m_quotainfo;
- int error = -EAGAIN;
- xfs_dqlock(dqp);
- if ((dqp->q_flags & XFS_DQFLAG_FREEING) || dqp->q_nrefs != 0)
- goto out_unlock;
-
- dqp->q_flags |= XFS_DQFLAG_FREEING;
+ spin_lock(&dqp->q_lockref.lock);
+ if (dqp->q_lockref.count > 0 || __lockref_is_dead(&dqp->q_lockref)) {
+ spin_unlock(&dqp->q_lockref.lock);
+ return -EAGAIN;
+ }
+ lockref_mark_dead(&dqp->q_lockref);
+ spin_unlock(&dqp->q_lockref.lock);
+ mutex_lock(&dqp->q_qlock);
xfs_qm_dqunpin_wait(dqp);
xfs_dqflock(dqp);
@@ -144,6 +146,7 @@ xfs_qm_dqpurge(
*/
if (XFS_DQ_IS_DIRTY(dqp)) {
struct xfs_buf *bp = NULL;
+ int error;
/*
* We don't care about getting disk errors here. We need
@@ -151,9 +154,9 @@ xfs_qm_dqpurge(
*/
error = xfs_dquot_use_attached_buf(dqp, &bp);
if (error == -EAGAIN) {
- xfs_dqfunlock(dqp);
- dqp->q_flags &= ~XFS_DQFLAG_FREEING;
- goto out_unlock;
+ /* resurrect the refcount from the dead. */
+ dqp->q_lockref.count = 0;
+ goto out_funlock;
}
if (!bp)
goto out_funlock;
@@ -177,7 +180,7 @@ out_funlock:
!test_bit(XFS_LI_IN_AIL, &dqp->q_logitem.qli_item.li_flags));
xfs_dqfunlock(dqp);
- xfs_dqunlock(dqp);
+ mutex_unlock(&dqp->q_qlock);
radix_tree_delete(xfs_dquot_tree(qi, xfs_dquot_type(dqp)), dqp->q_id);
qi->qi_dquots--;
@@ -192,10 +195,6 @@ out_funlock:
xfs_qm_dqdestroy(dqp);
return 0;
-
-out_unlock:
- xfs_dqunlock(dqp);
- return error;
}
/*
@@ -288,51 +287,6 @@ xfs_qm_unmount_quotas(
xfs_qm_destroy_quotainos(mp->m_quotainfo);
}
-STATIC int
-xfs_qm_dqattach_one(
- struct xfs_inode *ip,
- xfs_dqtype_t type,
- bool doalloc,
- struct xfs_dquot **IO_idqpp)
-{
- struct xfs_dquot *dqp;
- int error;
-
- xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);
- error = 0;
-
- /*
- * See if we already have it in the inode itself. IO_idqpp is &i_udquot
- * or &i_gdquot. This made the code look weird, but made the logic a lot
- * simpler.
- */
- dqp = *IO_idqpp;
- if (dqp) {
- trace_xfs_dqattach_found(dqp);
- return 0;
- }
-
- /*
- * Find the dquot from somewhere. This bumps the reference count of
- * dquot and returns it locked. This can return ENOENT if dquot didn't
- * exist on disk and we didn't ask it to allocate; ESRCH if quotas got
- * turned off suddenly.
- */
- error = xfs_qm_dqget_inode(ip, type, doalloc, &dqp);
- if (error)
- return error;
-
- trace_xfs_dqattach_get(dqp);
-
- /*
- * dqget may have dropped and re-acquired the ilock, but it guarantees
- * that the dquot returned is the one that should go in the inode.
- */
- *IO_idqpp = dqp;
- xfs_dqunlock(dqp);
- return 0;
-}
-
static bool
xfs_qm_need_dqattach(
struct xfs_inode *ip)
@@ -372,7 +326,7 @@ xfs_qm_dqattach_locked(
ASSERT(!xfs_is_metadir_inode(ip));
if (XFS_IS_UQUOTA_ON(mp) && !ip->i_udquot) {
- error = xfs_qm_dqattach_one(ip, XFS_DQTYPE_USER,
+ error = xfs_qm_dqget_inode(ip, XFS_DQTYPE_USER,
doalloc, &ip->i_udquot);
if (error)
goto done;
@@ -380,7 +334,7 @@ xfs_qm_dqattach_locked(
}
if (XFS_IS_GQUOTA_ON(mp) && !ip->i_gdquot) {
- error = xfs_qm_dqattach_one(ip, XFS_DQTYPE_GROUP,
+ error = xfs_qm_dqget_inode(ip, XFS_DQTYPE_GROUP,
doalloc, &ip->i_gdquot);
if (error)
goto done;
@@ -388,7 +342,7 @@ xfs_qm_dqattach_locked(
}
if (XFS_IS_PQUOTA_ON(mp) && !ip->i_pdquot) {
- error = xfs_qm_dqattach_one(ip, XFS_DQTYPE_PROJ,
+ error = xfs_qm_dqget_inode(ip, XFS_DQTYPE_PROJ,
doalloc, &ip->i_pdquot);
if (error)
goto done;
@@ -468,7 +422,7 @@ xfs_qm_dquot_isolate(
struct xfs_qm_isolate *isol = arg;
enum lru_status ret = LRU_SKIP;
- if (!xfs_dqlock_nowait(dqp))
+ if (!spin_trylock(&dqp->q_lockref.lock))
goto out_miss_busy;
/*
@@ -476,7 +430,7 @@ xfs_qm_dquot_isolate(
* from the LRU, leave it for the freeing task to complete the freeing
* process rather than risk it being free from under us here.
*/
- if (dqp->q_flags & XFS_DQFLAG_FREEING)
+ if (__lockref_is_dead(&dqp->q_lockref))
goto out_miss_unlock;
/*
@@ -485,16 +439,15 @@ xfs_qm_dquot_isolate(
* again.
*/
ret = LRU_ROTATE;
- if (XFS_DQ_IS_DIRTY(dqp) || atomic_read(&dqp->q_pincount) > 0) {
+ if (XFS_DQ_IS_DIRTY(dqp) || atomic_read(&dqp->q_pincount) > 0)
goto out_miss_unlock;
- }
/*
* This dquot has acquired a reference in the meantime remove it from
* the freelist and try again.
*/
- if (dqp->q_nrefs) {
- xfs_dqunlock(dqp);
+ if (dqp->q_lockref.count) {
+ spin_unlock(&dqp->q_lockref.lock);
XFS_STATS_INC(dqp->q_mount, xs_qm_dqwants);
trace_xfs_dqreclaim_want(dqp);
@@ -518,10 +471,9 @@ xfs_qm_dquot_isolate(
/*
* Prevent lookups now that we are past the point of no return.
*/
- dqp->q_flags |= XFS_DQFLAG_FREEING;
- xfs_dqunlock(dqp);
+ lockref_mark_dead(&dqp->q_lockref);
+ spin_unlock(&dqp->q_lockref.lock);
- ASSERT(dqp->q_nrefs == 0);
list_lru_isolate_move(lru, &dqp->q_lru, &isol->dispose);
XFS_STATS_DEC(dqp->q_mount, xs_qm_dquot_unused);
trace_xfs_dqreclaim_done(dqp);
@@ -529,7 +481,7 @@ xfs_qm_dquot_isolate(
return LRU_REMOVED;
out_miss_unlock:
- xfs_dqunlock(dqp);
+ spin_unlock(&dqp->q_lockref.lock);
out_miss_busy:
trace_xfs_dqreclaim_busy(dqp);
XFS_STATS_INC(dqp->q_mount, xs_qm_dqreclaim_misses);
@@ -1316,9 +1268,10 @@ xfs_qm_quotacheck_dqadjust(
return error;
}
+ mutex_lock(&dqp->q_qlock);
error = xfs_dquot_attach_buf(NULL, dqp);
if (error)
- return error;
+ goto out_unlock;
trace_xfs_dqadjust(dqp);
@@ -1348,8 +1301,10 @@ xfs_qm_quotacheck_dqadjust(
}
dqp->q_flags |= XFS_DQFLAG_DIRTY;
- xfs_qm_dqput(dqp);
- return 0;
+out_unlock:
+ mutex_unlock(&dqp->q_qlock);
+ xfs_qm_dqrele(dqp);
+ return error;
}
/*
@@ -1466,9 +1421,10 @@ xfs_qm_flush_one(
struct xfs_buf *bp = NULL;
int error = 0;
- xfs_dqlock(dqp);
- if (dqp->q_flags & XFS_DQFLAG_FREEING)
- goto out_unlock;
+ if (!lockref_get_not_dead(&dqp->q_lockref))
+ return 0;
+
+ mutex_lock(&dqp->q_qlock);
if (!XFS_DQ_IS_DIRTY(dqp))
goto out_unlock;
@@ -1488,7 +1444,8 @@ xfs_qm_flush_one(
xfs_buf_delwri_queue(bp, buffer_list);
xfs_buf_relse(bp);
out_unlock:
- xfs_dqunlock(dqp);
+ mutex_unlock(&dqp->q_qlock);
+ xfs_qm_dqrele(dqp);
return error;
}
@@ -1904,16 +1861,12 @@ xfs_qm_vop_dqalloc(
struct xfs_dquot *gq = NULL;
struct xfs_dquot *pq = NULL;
int error;
- uint lockflags;
if (!XFS_IS_QUOTA_ON(mp))
return 0;
ASSERT(!xfs_is_metadir_inode(ip));
- lockflags = XFS_ILOCK_EXCL;
- xfs_ilock(ip, lockflags);
-
if ((flags & XFS_QMOPT_INHERIT) && XFS_INHERIT_GID(ip))
gid = inode->i_gid;
@@ -1922,38 +1875,22 @@ xfs_qm_vop_dqalloc(
* if necessary. The dquot(s) will not be locked.
*/
if (XFS_NOT_DQATTACHED(mp, ip)) {
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
error = xfs_qm_dqattach_locked(ip, true);
- if (error) {
- xfs_iunlock(ip, lockflags);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ if (error)
return error;
- }
}
if ((flags & XFS_QMOPT_UQUOTA) && XFS_IS_UQUOTA_ON(mp)) {
ASSERT(O_udqpp);
if (!uid_eq(inode->i_uid, uid)) {
- /*
- * What we need is the dquot that has this uid, and
- * if we send the inode to dqget, the uid of the inode
- * takes priority over what's sent in the uid argument.
- * We must unlock inode here before calling dqget if
- * we're not sending the inode, because otherwise
- * we'll deadlock by doing trans_reserve while
- * holding ilock.
- */
- xfs_iunlock(ip, lockflags);
error = xfs_qm_dqget(mp, from_kuid(user_ns, uid),
XFS_DQTYPE_USER, true, &uq);
if (error) {
ASSERT(error != -ENOENT);
return error;
}
- /*
- * Get the ilock in the right order.
- */
- xfs_dqunlock(uq);
- lockflags = XFS_ILOCK_SHARED;
- xfs_ilock(ip, lockflags);
} else {
/*
* Take an extra reference, because we'll return
@@ -1966,16 +1903,12 @@ xfs_qm_vop_dqalloc(
if ((flags & XFS_QMOPT_GQUOTA) && XFS_IS_GQUOTA_ON(mp)) {
ASSERT(O_gdqpp);
if (!gid_eq(inode->i_gid, gid)) {
- xfs_iunlock(ip, lockflags);
error = xfs_qm_dqget(mp, from_kgid(user_ns, gid),
XFS_DQTYPE_GROUP, true, &gq);
if (error) {
ASSERT(error != -ENOENT);
goto error_rele;
}
- xfs_dqunlock(gq);
- lockflags = XFS_ILOCK_SHARED;
- xfs_ilock(ip, lockflags);
} else {
ASSERT(ip->i_gdquot);
gq = xfs_qm_dqhold(ip->i_gdquot);
@@ -1984,16 +1917,12 @@ xfs_qm_vop_dqalloc(
if ((flags & XFS_QMOPT_PQUOTA) && XFS_IS_PQUOTA_ON(mp)) {
ASSERT(O_pdqpp);
if (ip->i_projid != prid) {
- xfs_iunlock(ip, lockflags);
error = xfs_qm_dqget(mp, prid,
XFS_DQTYPE_PROJ, true, &pq);
if (error) {
ASSERT(error != -ENOENT);
goto error_rele;
}
- xfs_dqunlock(pq);
- lockflags = XFS_ILOCK_SHARED;
- xfs_ilock(ip, lockflags);
} else {
ASSERT(ip->i_pdquot);
pq = xfs_qm_dqhold(ip->i_pdquot);
@@ -2001,7 +1930,6 @@ xfs_qm_vop_dqalloc(
}
trace_xfs_dquot_dqalloc(ip);
- xfs_iunlock(ip, lockflags);
if (O_udqpp)
*O_udqpp = uq;
else
@@ -2078,7 +2006,7 @@ xfs_qm_vop_chown(
* back now.
*/
tp->t_flags |= XFS_TRANS_DIRTY;
- xfs_dqlock(prevdq);
+ mutex_lock(&prevdq->q_qlock);
if (isrt) {
ASSERT(prevdq->q_rtb.reserved >= ip->i_delayed_blks);
prevdq->q_rtb.reserved -= ip->i_delayed_blks;
@@ -2086,7 +2014,7 @@ xfs_qm_vop_chown(
ASSERT(prevdq->q_blk.reserved >= ip->i_delayed_blks);
prevdq->q_blk.reserved -= ip->i_delayed_blks;
}
- xfs_dqunlock(prevdq);
+ mutex_unlock(&prevdq->q_qlock);
/*
* Take an extra reference, because the inode is going to keep
diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h
index 35b64bc3a7a8..e88ed6ad0e65 100644
--- a/fs/xfs/xfs_qm.h
+++ b/fs/xfs/xfs_qm.h
@@ -57,7 +57,7 @@ struct xfs_quotainfo {
struct xfs_inode *qi_pquotaip; /* project quota inode */
struct xfs_inode *qi_dirip; /* quota metadir */
struct list_lru qi_lru;
- int qi_dquots;
+ uint64_t qi_dquots;
struct mutex qi_quotaofflock;/* to serialize quotaoff */
xfs_filblks_t qi_dqchunklen; /* # BBs in a chunk of dqs */
uint qi_dqperchunk; /* # ondisk dq in above chunk */
diff --git a/fs/xfs/xfs_qm_bhv.c b/fs/xfs/xfs_qm_bhv.c
index 245d754f382a..edc0aef3cf34 100644
--- a/fs/xfs/xfs_qm_bhv.c
+++ b/fs/xfs/xfs_qm_bhv.c
@@ -73,8 +73,10 @@ xfs_qm_statvfs(
struct xfs_dquot *dqp;
if (!xfs_qm_dqget(mp, ip->i_projid, XFS_DQTYPE_PROJ, false, &dqp)) {
+ mutex_lock(&dqp->q_qlock);
xfs_fill_statvfs_from_dquot(statp, ip, dqp);
- xfs_qm_dqput(dqp);
+ mutex_unlock(&dqp->q_qlock);
+ xfs_qm_dqrele(dqp);
}
}
diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c
index 0c78f30fa4a3..022e2179c06b 100644
--- a/fs/xfs/xfs_qm_syscalls.c
+++ b/fs/xfs/xfs_qm_syscalls.c
@@ -303,13 +303,12 @@ xfs_qm_scall_setqlim(
}
defq = xfs_get_defquota(q, xfs_dquot_type(dqp));
- xfs_dqunlock(dqp);
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_qm_setqlim, 0, 0, 0, &tp);
if (error)
goto out_rele;
- xfs_dqlock(dqp);
+ mutex_lock(&dqp->q_qlock);
xfs_trans_dqjoin(tp, dqp);
/*
@@ -459,6 +458,7 @@ xfs_qm_scall_getquota(
* If everything's NULL, this dquot doesn't quite exist as far as
* our utility programs are concerned.
*/
+ mutex_lock(&dqp->q_qlock);
if (XFS_IS_DQUOT_UNINITIALIZED(dqp)) {
error = -ENOENT;
goto out_put;
@@ -467,7 +467,8 @@ xfs_qm_scall_getquota(
xfs_qm_scall_getquota_fill_qc(mp, type, dqp, dst);
out_put:
- xfs_qm_dqput(dqp);
+ mutex_unlock(&dqp->q_qlock);
+ xfs_qm_dqrele(dqp);
return error;
}
@@ -497,7 +498,8 @@ xfs_qm_scall_getquota_next(
*id = dqp->q_id;
xfs_qm_scall_getquota_fill_qc(mp, type, dqp, dst);
+ mutex_unlock(&dqp->q_qlock);
- xfs_qm_dqput(dqp);
+ xfs_qm_dqrele(dqp);
return error;
}
diff --git a/fs/xfs/xfs_quotaops.c b/fs/xfs/xfs_quotaops.c
index 4c7f7ce4fd2f..94fbe3d99ec7 100644
--- a/fs/xfs/xfs_quotaops.c
+++ b/fs/xfs/xfs_quotaops.c
@@ -65,7 +65,7 @@ xfs_fs_get_quota_state(
memset(state, 0, sizeof(*state));
if (!XFS_IS_QUOTA_ON(mp))
return 0;
- state->s_incoredqs = q->qi_dquots;
+ state->s_incoredqs = min_t(uint64_t, q->qi_dquots, UINT_MAX);
if (XFS_IS_UQUOTA_ON(mp))
state->s_state[USRQUOTA].flags |= QCI_ACCT_ENABLED;
if (XFS_IS_UQUOTA_ENFORCED(mp))
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index 79b8641880ab..f70afbf3cb19 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -1350,7 +1350,7 @@ DECLARE_EVENT_CLASS(xfs_dquot_class,
__entry->id = dqp->q_id;
__entry->type = dqp->q_type;
__entry->flags = dqp->q_flags;
- __entry->nrefs = dqp->q_nrefs;
+ __entry->nrefs = data_race(dqp->q_lockref.count);
__entry->res_bcount = dqp->q_blk.reserved;
__entry->res_rtbcount = dqp->q_rtb.reserved;
@@ -1399,7 +1399,6 @@ DEFINE_DQUOT_EVENT(xfs_dqadjust);
DEFINE_DQUOT_EVENT(xfs_dqreclaim_want);
DEFINE_DQUOT_EVENT(xfs_dqreclaim_busy);
DEFINE_DQUOT_EVENT(xfs_dqreclaim_done);
-DEFINE_DQUOT_EVENT(xfs_dqattach_found);
DEFINE_DQUOT_EVENT(xfs_dqattach_get);
DEFINE_DQUOT_EVENT(xfs_dqalloc);
DEFINE_DQUOT_EVENT(xfs_dqtobp_read);
@@ -1409,9 +1408,8 @@ DEFINE_DQUOT_EVENT(xfs_dqget_hit);
DEFINE_DQUOT_EVENT(xfs_dqget_miss);
DEFINE_DQUOT_EVENT(xfs_dqget_freeing);
DEFINE_DQUOT_EVENT(xfs_dqget_dup);
-DEFINE_DQUOT_EVENT(xfs_dqput);
-DEFINE_DQUOT_EVENT(xfs_dqput_free);
DEFINE_DQUOT_EVENT(xfs_dqrele);
+DEFINE_DQUOT_EVENT(xfs_dqrele_free);
DEFINE_DQUOT_EVENT(xfs_dqflush);
DEFINE_DQUOT_EVENT(xfs_dqflush_force);
DEFINE_DQUOT_EVENT(xfs_dqflush_done);
@@ -4934,7 +4932,7 @@ DECLARE_EVENT_CLASS(xlog_iclog_class,
__entry->refcount = atomic_read(&iclog->ic_refcnt);
__entry->offset = iclog->ic_offset;
__entry->flags = iclog->ic_flags;
- __entry->lsn = be64_to_cpu(iclog->ic_header.h_lsn);
+ __entry->lsn = be64_to_cpu(iclog->ic_header->h_lsn);
__entry->caller_ip = caller_ip;
),
TP_printk("dev %d:%d state %s refcnt %d offset %u lsn 0x%llx flags %s caller %pS",
diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c
index 765456bf3428..c842ce06acd6 100644
--- a/fs/xfs/xfs_trans_dquot.c
+++ b/fs/xfs/xfs_trans_dquot.c
@@ -393,7 +393,7 @@ xfs_trans_dqlockedjoin(
unsigned int i;
ASSERT(q[0].qt_dquot != NULL);
if (q[1].qt_dquot == NULL) {
- xfs_dqlock(q[0].qt_dquot);
+ mutex_lock(&q[0].qt_dquot->q_qlock);
xfs_trans_dqjoin(tp, q[0].qt_dquot);
} else if (q[2].qt_dquot == NULL) {
xfs_dqlock2(q[0].qt_dquot, q[1].qt_dquot);
@@ -693,7 +693,7 @@ xfs_trans_unreserve_and_mod_dquots(
locked = already_locked;
if (qtrx->qt_blk_res) {
if (!locked) {
- xfs_dqlock(dqp);
+ mutex_lock(&dqp->q_qlock);
locked = true;
}
dqp->q_blk.reserved -=
@@ -701,7 +701,7 @@ xfs_trans_unreserve_and_mod_dquots(
}
if (qtrx->qt_ino_res) {
if (!locked) {
- xfs_dqlock(dqp);
+ mutex_lock(&dqp->q_qlock);
locked = true;
}
dqp->q_ino.reserved -=
@@ -710,14 +710,14 @@ xfs_trans_unreserve_and_mod_dquots(
if (qtrx->qt_rtblk_res) {
if (!locked) {
- xfs_dqlock(dqp);
+ mutex_lock(&dqp->q_qlock);
locked = true;
}
dqp->q_rtb.reserved -=
(xfs_qcnt_t)qtrx->qt_rtblk_res;
}
if (locked && !already_locked)
- xfs_dqunlock(dqp);
+ mutex_unlock(&dqp->q_qlock);
}
}
@@ -820,7 +820,7 @@ xfs_trans_dqresv(
struct xfs_dquot_res *blkres;
struct xfs_quota_limits *qlim;
- xfs_dqlock(dqp);
+ mutex_lock(&dqp->q_qlock);
defq = xfs_get_defquota(q, xfs_dquot_type(dqp));
@@ -887,16 +887,16 @@ xfs_trans_dqresv(
XFS_IS_CORRUPT(mp, dqp->q_ino.reserved < dqp->q_ino.count))
goto error_corrupt;
- xfs_dqunlock(dqp);
+ mutex_unlock(&dqp->q_qlock);
return 0;
error_return:
- xfs_dqunlock(dqp);
+ mutex_unlock(&dqp->q_qlock);
if (xfs_dquot_type(dqp) == XFS_DQTYPE_PROJ)
return -ENOSPC;
return -EDQUOT;
error_corrupt:
- xfs_dqunlock(dqp);
+ mutex_unlock(&dqp->q_qlock);
xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
xfs_fs_mark_sick(mp, XFS_SICK_FS_QUOTACHECK);
return -EFSCORRUPTED;
diff --git a/fs/xfs/xfs_zone_alloc.c b/fs/xfs/xfs_zone_alloc.c
index 8dde444596f1..bbcf21704ea0 100644
--- a/fs/xfs/xfs_zone_alloc.c
+++ b/fs/xfs/xfs_zone_alloc.c
@@ -103,9 +103,6 @@ xfs_zone_account_reclaimable(
*/
trace_xfs_zone_emptied(rtg);
- if (!was_full)
- xfs_group_clear_mark(xg, XFS_RTG_RECLAIMABLE);
-
spin_lock(&zi->zi_used_buckets_lock);
if (!was_full)
xfs_zone_remove_from_bucket(zi, rgno, from_bucket);
@@ -127,7 +124,6 @@ xfs_zone_account_reclaimable(
xfs_zone_add_to_bucket(zi, rgno, to_bucket);
spin_unlock(&zi->zi_used_buckets_lock);
- xfs_group_set_mark(xg, XFS_RTG_RECLAIMABLE);
if (zi->zi_gc_thread && xfs_zoned_need_gc(mp))
wake_up_process(zi->zi_gc_thread);
} else if (to_bucket != from_bucket) {
@@ -142,6 +138,28 @@ xfs_zone_account_reclaimable(
}
}
+/*
+ * Check if we have any zones that can be reclaimed by looking at the entry
+ * counters for the zone buckets.
+ */
+bool
+xfs_zoned_have_reclaimable(
+ struct xfs_zone_info *zi)
+{
+ int i;
+
+ spin_lock(&zi->zi_used_buckets_lock);
+ for (i = 0; i < XFS_ZONE_USED_BUCKETS; i++) {
+ if (zi->zi_used_bucket_entries[i]) {
+ spin_unlock(&zi->zi_used_buckets_lock);
+ return true;
+ }
+ }
+ spin_unlock(&zi->zi_used_buckets_lock);
+
+ return false;
+}
+
static void
xfs_open_zone_mark_full(
struct xfs_open_zone *oz)
@@ -1263,7 +1281,7 @@ xfs_mount_zones(
PAGE_SHIFT;
if (bdev_is_zoned(bt->bt_bdev)) {
- error = blkdev_report_zones(bt->bt_bdev,
+ error = blkdev_report_zones_cached(bt->bt_bdev,
XFS_FSB_TO_BB(mp, mp->m_sb.sb_rtstart),
mp->m_sb.sb_rgcount, xfs_get_zone_info_cb, &iz);
if (error < 0)
diff --git a/fs/xfs/xfs_zone_gc.c b/fs/xfs/xfs_zone_gc.c
index 4ade54445532..3c52cc1497d4 100644
--- a/fs/xfs/xfs_zone_gc.c
+++ b/fs/xfs/xfs_zone_gc.c
@@ -117,7 +117,6 @@ struct xfs_gc_bio {
struct xfs_rtgroup *victim_rtg;
/* Bio used for reads and writes, including the bvec used by it */
- struct bio_vec bv;
struct bio bio; /* must be last */
};
@@ -175,14 +174,13 @@ xfs_zoned_need_gc(
s64 available, free, threshold;
s32 remainder;
- if (!xfs_group_marked(mp, XG_TYPE_RTG, XFS_RTG_RECLAIMABLE))
+ if (!xfs_zoned_have_reclaimable(mp->m_zone_info))
return false;
available = xfs_estimate_freecounter(mp, XC_FREE_RTAVAILABLE);
if (available <
- mp->m_groups[XG_TYPE_RTG].blocks *
- (mp->m_max_open_zones - XFS_OPEN_GC_ZONES))
+ xfs_rtgs_to_rfsbs(mp, mp->m_max_open_zones - XFS_OPEN_GC_ZONES))
return true;
free = xfs_estimate_freecounter(mp, XC_FREE_RTEXTENTS);
@@ -1184,16 +1182,16 @@ xfs_zone_gc_mount(
goto out_put_gc_zone;
}
- mp->m_zone_info->zi_gc_thread = kthread_create(xfs_zoned_gcd, data,
+ zi->zi_gc_thread = kthread_create(xfs_zoned_gcd, data,
"xfs-zone-gc/%s", mp->m_super->s_id);
- if (IS_ERR(mp->m_zone_info->zi_gc_thread)) {
+ if (IS_ERR(zi->zi_gc_thread)) {
xfs_warn(mp, "unable to create zone gc thread");
- error = PTR_ERR(mp->m_zone_info->zi_gc_thread);
+ error = PTR_ERR(zi->zi_gc_thread);
goto out_free_gc_data;
}
/* xfs_zone_gc_start will unpark for rw mounts */
- kthread_park(mp->m_zone_info->zi_gc_thread);
+ kthread_park(zi->zi_gc_thread);
return 0;
out_free_gc_data:
diff --git a/fs/xfs/xfs_zone_priv.h b/fs/xfs/xfs_zone_priv.h
index 4322e26dd99a..ce7f0e2f4598 100644
--- a/fs/xfs/xfs_zone_priv.h
+++ b/fs/xfs/xfs_zone_priv.h
@@ -113,6 +113,7 @@ struct xfs_open_zone *xfs_open_zone(struct xfs_mount *mp,
int xfs_zone_gc_reset_sync(struct xfs_rtgroup *rtg);
bool xfs_zoned_need_gc(struct xfs_mount *mp);
+bool xfs_zoned_have_reclaimable(struct xfs_zone_info *zi);
int xfs_zone_gc_mount(struct xfs_mount *mp);
void xfs_zone_gc_unmount(struct xfs_mount *mp);
diff --git a/fs/xfs/xfs_zone_space_resv.c b/fs/xfs/xfs_zone_space_resv.c
index 9cd38716fd25..fc1a4d1ce10c 100644
--- a/fs/xfs/xfs_zone_space_resv.c
+++ b/fs/xfs/xfs_zone_space_resv.c
@@ -54,12 +54,10 @@ xfs_zoned_default_resblks(
{
switch (ctr) {
case XC_FREE_RTEXTENTS:
- return (uint64_t)XFS_RESERVED_ZONES *
- mp->m_groups[XG_TYPE_RTG].blocks +
- mp->m_sb.sb_rtreserved;
+ return xfs_rtgs_to_rfsbs(mp, XFS_RESERVED_ZONES) +
+ mp->m_sb.sb_rtreserved;
case XC_FREE_RTAVAILABLE:
- return (uint64_t)XFS_GC_ZONES *
- mp->m_groups[XG_TYPE_RTG].blocks;
+ return xfs_rtgs_to_rfsbs(mp, XFS_GC_ZONES);
default:
ASSERT(0);
return 0;
@@ -174,7 +172,7 @@ xfs_zoned_reserve_available(
* processing a pending GC request give up as we're fully out
* of space.
*/
- if (!xfs_group_marked(mp, XG_TYPE_RTG, XFS_RTG_RECLAIMABLE) &&
+ if (!xfs_zoned_have_reclaimable(mp->m_zone_info) &&
!xfs_is_zonegc_running(mp))
break;