]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob
dc8e22ecf832d36e77e71245d6a56942a4ed5ead
[thirdparty/kernel/stable-queue.git] /
1 From 6e643cd094de3bd0f97edcc1db0089afa24d909f Mon Sep 17 00:00:00 2001
2 From: "Darrick J. Wong" <darrick.wong@oracle.com>
3 Date: Thu, 7 Dec 2017 19:07:02 -0800
4 Subject: xfs: hold xfs_buf locked between shortform->leaf conversion and the addition of an attribute
5
6 From: Darrick J. Wong <darrick.wong@oracle.com>
7
8 commit 6e643cd094de3bd0f97edcc1db0089afa24d909f upstream.
9
10 The new attribute leaf buffer is not held locked across the transaction
11 roll between the shortform->leaf modification and the addition of the
12 new entry. As a result, the attribute buffer modification being made is
13 not atomic from an operational perspective. Hence the AIL push can grab
14 it in the transient state of "just created" after the initial
15 transaction is rolled, because the buffer has been released. This leads
16 to xfs_attr3_leaf_verify() asserting that hdr.count is zero, treating
17 this as in-memory corruption, and shutting down the filesystem.
18
19 Darrick ported the original patch to 4.15 and reworked it use the
20 xfs_defer_bjoin helper and hold/join the buffer correctly across the
21 second transaction roll.
22
23 Signed-off-by: Alex Lyakas <alex@zadarastorage.com>
24 Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
25 Reviewed-by: Christoph Hellwig <hch@lst.de>
26 Signed-off-by: Alex Lyakas <alex@zadara.com>
27 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
28
29 ---
30 fs/xfs/libxfs/xfs_attr.c | 20 +++++++++++++++-----
31 fs/xfs/libxfs/xfs_attr_leaf.c | 9 ++++++---
32 fs/xfs/libxfs/xfs_attr_leaf.h | 3 ++-
33 3 files changed, 23 insertions(+), 9 deletions(-)
34
35 --- a/fs/xfs/libxfs/xfs_attr.c
36 +++ b/fs/xfs/libxfs/xfs_attr.c
37 @@ -212,6 +212,7 @@ xfs_attr_set(
38 int flags)
39 {
40 struct xfs_mount *mp = dp->i_mount;
41 + struct xfs_buf *leaf_bp = NULL;
42 struct xfs_da_args args;
43 struct xfs_defer_ops dfops;
44 struct xfs_trans_res tres;
45 @@ -327,9 +328,16 @@ xfs_attr_set(
46 * GROT: another possible req'mt for a double-split btree op.
47 */
48 xfs_defer_init(args.dfops, args.firstblock);
49 - error = xfs_attr_shortform_to_leaf(&args);
50 + error = xfs_attr_shortform_to_leaf(&args, &leaf_bp);
51 if (error)
52 goto out_defer_cancel;
53 + /*
54 + * Prevent the leaf buffer from being unlocked so that a
55 + * concurrent AIL push cannot grab the half-baked leaf
56 + * buffer and run into problems with the write verifier.
57 + */
58 + xfs_trans_bhold(args.trans, leaf_bp);
59 + xfs_defer_bjoin(args.dfops, leaf_bp);
60 xfs_defer_ijoin(args.dfops, dp);
61 error = xfs_defer_finish(&args.trans, args.dfops);
62 if (error)
63 @@ -337,13 +345,14 @@ xfs_attr_set(
64
65 /*
66 * Commit the leaf transformation. We'll need another (linked)
67 - * transaction to add the new attribute to the leaf.
68 + * transaction to add the new attribute to the leaf, which
69 + * means that we have to hold & join the leaf buffer here too.
70 */
71 -
72 error = xfs_trans_roll_inode(&args.trans, dp);
73 if (error)
74 goto out;
75 -
76 + xfs_trans_bjoin(args.trans, leaf_bp);
77 + leaf_bp = NULL;
78 }
79
80 if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
81 @@ -374,8 +383,9 @@ xfs_attr_set(
82
83 out_defer_cancel:
84 xfs_defer_cancel(&dfops);
85 - args.trans = NULL;
86 out:
87 + if (leaf_bp)
88 + xfs_trans_brelse(args.trans, leaf_bp);
89 if (args.trans)
90 xfs_trans_cancel(args.trans);
91 xfs_iunlock(dp, XFS_ILOCK_EXCL);
92 --- a/fs/xfs/libxfs/xfs_attr_leaf.c
93 +++ b/fs/xfs/libxfs/xfs_attr_leaf.c
94 @@ -739,10 +739,13 @@ xfs_attr_shortform_getvalue(xfs_da_args_
95 }
96
97 /*
98 - * Convert from using the shortform to the leaf.
99 + * Convert from using the shortform to the leaf. On success, return the
100 + * buffer so that we can keep it locked until we're totally done with it.
101 */
102 int
103 -xfs_attr_shortform_to_leaf(xfs_da_args_t *args)
104 +xfs_attr_shortform_to_leaf(
105 + struct xfs_da_args *args,
106 + struct xfs_buf **leaf_bp)
107 {
108 xfs_inode_t *dp;
109 xfs_attr_shortform_t *sf;
110 @@ -821,7 +824,7 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t
111 sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
112 }
113 error = 0;
114 -
115 + *leaf_bp = bp;
116 out:
117 kmem_free(tmpbuffer);
118 return error;
119 --- a/fs/xfs/libxfs/xfs_attr_leaf.h
120 +++ b/fs/xfs/libxfs/xfs_attr_leaf.h
121 @@ -48,7 +48,8 @@ void xfs_attr_shortform_create(struct xf
122 void xfs_attr_shortform_add(struct xfs_da_args *args, int forkoff);
123 int xfs_attr_shortform_lookup(struct xfs_da_args *args);
124 int xfs_attr_shortform_getvalue(struct xfs_da_args *args);
125 -int xfs_attr_shortform_to_leaf(struct xfs_da_args *args);
126 +int xfs_attr_shortform_to_leaf(struct xfs_da_args *args,
127 + struct xfs_buf **leaf_bp);
128 int xfs_attr_shortform_remove(struct xfs_da_args *args);
129 int xfs_attr_shortform_allfit(struct xfs_buf *bp, struct xfs_inode *dp);
130 int xfs_attr_shortform_bytesfit(struct xfs_inode *dp, int bytes);