Skip to content

Commit 784eb7d

Browse files
Dave Chinnerdchinner
authored andcommitted
xfs: add in-memory iunlink log item
Now that we have a clean operation to update the di_next_unlinked field of inode cluster buffers, we can easily defer this operation to transaction commit time so we can order the inode cluster buffer locking consistently. To do this, we introduce a new in-memory log item to track the unlinked list item modification that we are going to make. This follows the same observations as the in-memory double linked list used to track unlinked inodes in that the inodes on the list are pinned in memory and cannot go away, and hence we can simply reference them for the duration of the transaction without needing to take active references or pin them or look them up. This allows us to pass the xfs_inode to the transaction commit code along with the modification to be made, and then order the logged modifications via the ->iop_sort and ->iop_precommit operations for the new log item type. As this is an in-memory log item, it doesn't have formatting, CIL or AIL operational hooks - it exists purely to run the inode unlink modifications and is then removed from the transaction item list and freed once the precommit operation has run. Signed-off-by: Dave Chinner <[email protected]> Reviewed-by: Darrick J. Wong <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]>
1 parent fad743d commit 784eb7d

File tree

5 files changed

+219
-63
lines changed

5 files changed

+219
-63
lines changed

fs/xfs/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ xfs-y += xfs_log.o \
106106
xfs_icreate_item.o \
107107
xfs_inode_item.o \
108108
xfs_inode_item_recover.o \
109+
xfs_iunlink_item.o \
109110
xfs_refcount_item.o \
110111
xfs_rmap_item.o \
111112
xfs_log_recover.o \

fs/xfs/xfs_inode.c

Lines changed: 1 addition & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "xfs_trans.h"
2121
#include "xfs_buf_item.h"
2222
#include "xfs_inode_item.h"
23+
#include "xfs_iunlink_item.h"
2324
#include "xfs_ialloc.h"
2425
#include "xfs_bmap.h"
2526
#include "xfs_bmap_util.h"
@@ -1905,69 +1906,6 @@ xfs_iunlink_update_bucket(
19051906
return 0;
19061907
}
19071908

1908-
/* Set an in-core inode's unlinked pointer and return the old value. */
1909-
static int
1910-
xfs_iunlink_log_inode(
1911-
struct xfs_trans *tp,
1912-
struct xfs_inode *ip,
1913-
struct xfs_perag *pag,
1914-
xfs_agino_t next_agino)
1915-
{
1916-
struct xfs_mount *mp = tp->t_mountp;
1917-
struct xfs_dinode *dip;
1918-
struct xfs_buf *ibp;
1919-
xfs_agino_t old_value;
1920-
int offset;
1921-
int error;
1922-
1923-
ASSERT(xfs_verify_agino_or_null(pag, next_agino));
1924-
1925-
error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &ibp);
1926-
if (error)
1927-
return error;
1928-
dip = xfs_buf_offset(ibp, ip->i_imap.im_boffset);
1929-
1930-
/* Make sure the old pointer isn't garbage. */
1931-
old_value = be32_to_cpu(dip->di_next_unlinked);
1932-
if (old_value != ip->i_next_unlinked ||
1933-
!xfs_verify_agino_or_null(pag, old_value)) {
1934-
xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, dip,
1935-
sizeof(*dip), __this_address);
1936-
error = -EFSCORRUPTED;
1937-
goto out;
1938-
}
1939-
1940-
/*
1941-
* Since we're updating a linked list, we should never find that the
1942-
* current pointer is the same as the new value, unless we're
1943-
* terminating the list.
1944-
*/
1945-
if (old_value == next_agino) {
1946-
if (next_agino != NULLAGINO) {
1947-
xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__,
1948-
dip, sizeof(*dip), __this_address);
1949-
error = -EFSCORRUPTED;
1950-
}
1951-
goto out;
1952-
}
1953-
1954-
trace_xfs_iunlink_update_dinode(mp, pag->pag_agno,
1955-
XFS_INO_TO_AGINO(mp, ip->i_ino),
1956-
be32_to_cpu(dip->di_next_unlinked), next_agino);
1957-
1958-
dip->di_next_unlinked = cpu_to_be32(next_agino);
1959-
offset = ip->i_imap.im_boffset +
1960-
offsetof(struct xfs_dinode, di_next_unlinked);
1961-
1962-
xfs_dinode_calc_crc(mp, dip);
1963-
xfs_trans_inode_buf(tp, ibp);
1964-
xfs_trans_log_buf(tp, ibp, offset, offset + sizeof(xfs_agino_t) - 1);
1965-
return 0;
1966-
out:
1967-
xfs_trans_brelse(tp, ibp);
1968-
return error;
1969-
}
1970-
19711909
static int
19721910
xfs_iunlink_insert_inode(
19731911
struct xfs_trans *tp,

fs/xfs/xfs_iunlink_item.c

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (c) 2020-2022, Red Hat, Inc.
4+
* All Rights Reserved.
5+
*/
6+
#include "xfs.h"
7+
#include "xfs_fs.h"
8+
#include "xfs_shared.h"
9+
#include "xfs_format.h"
10+
#include "xfs_log_format.h"
11+
#include "xfs_trans_resv.h"
12+
#include "xfs_mount.h"
13+
#include "xfs_inode.h"
14+
#include "xfs_trans.h"
15+
#include "xfs_trans_priv.h"
16+
#include "xfs_ag.h"
17+
#include "xfs_iunlink_item.h"
18+
#include "xfs_trace.h"
19+
#include "xfs_error.h"
20+
21+
struct kmem_cache *xfs_iunlink_cache;
22+
23+
static inline struct xfs_iunlink_item *IUL_ITEM(struct xfs_log_item *lip)
24+
{
25+
return container_of(lip, struct xfs_iunlink_item, item);
26+
}
27+
28+
static void
29+
xfs_iunlink_item_release(
30+
struct xfs_log_item *lip)
31+
{
32+
struct xfs_iunlink_item *iup = IUL_ITEM(lip);
33+
34+
xfs_perag_put(iup->pag);
35+
kmem_cache_free(xfs_iunlink_cache, IUL_ITEM(lip));
36+
}
37+
38+
39+
static uint64_t
40+
xfs_iunlink_item_sort(
41+
struct xfs_log_item *lip)
42+
{
43+
return IUL_ITEM(lip)->ip->i_ino;
44+
}
45+
46+
/*
47+
* Look up the inode cluster buffer and log the on-disk unlinked inode change
48+
* we need to make.
49+
*/
50+
static int
51+
xfs_iunlink_log_dinode(
52+
struct xfs_trans *tp,
53+
struct xfs_iunlink_item *iup)
54+
{
55+
struct xfs_mount *mp = tp->t_mountp;
56+
struct xfs_inode *ip = iup->ip;
57+
struct xfs_dinode *dip;
58+
struct xfs_buf *ibp;
59+
int offset;
60+
int error;
61+
62+
error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &ibp);
63+
if (error)
64+
return error;
65+
/*
66+
* Don't log the unlinked field on stale buffers as this may be the
67+
* transaction that frees the inode cluster and relogging the buffer
68+
* here will incorrectly remove the stale state.
69+
*/
70+
if (ibp->b_flags & XBF_STALE)
71+
goto out;
72+
73+
dip = xfs_buf_offset(ibp, ip->i_imap.im_boffset);
74+
75+
/* Make sure the old pointer isn't garbage. */
76+
if (be32_to_cpu(dip->di_next_unlinked) != iup->old_agino) {
77+
xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, dip,
78+
sizeof(*dip), __this_address);
79+
error = -EFSCORRUPTED;
80+
goto out;
81+
}
82+
83+
trace_xfs_iunlink_update_dinode(mp, iup->pag->pag_agno,
84+
XFS_INO_TO_AGINO(mp, ip->i_ino),
85+
be32_to_cpu(dip->di_next_unlinked), iup->next_agino);
86+
87+
dip->di_next_unlinked = cpu_to_be32(iup->next_agino);
88+
offset = ip->i_imap.im_boffset +
89+
offsetof(struct xfs_dinode, di_next_unlinked);
90+
91+
xfs_dinode_calc_crc(mp, dip);
92+
xfs_trans_inode_buf(tp, ibp);
93+
xfs_trans_log_buf(tp, ibp, offset, offset + sizeof(xfs_agino_t) - 1);
94+
return 0;
95+
out:
96+
xfs_trans_brelse(tp, ibp);
97+
return error;
98+
}
99+
100+
/*
101+
* On precommit, we grab the inode cluster buffer for the inode number we were
102+
* passed, then update the next unlinked field for that inode in the buffer and
103+
* log the buffer. This ensures that the inode cluster buffer was logged in the
104+
* correct order w.r.t. other inode cluster buffers. We can then remove the
105+
* iunlink item from the transaction and release it as it is has now served it's
106+
* purpose.
107+
*/
108+
static int
109+
xfs_iunlink_item_precommit(
110+
struct xfs_trans *tp,
111+
struct xfs_log_item *lip)
112+
{
113+
struct xfs_iunlink_item *iup = IUL_ITEM(lip);
114+
int error;
115+
116+
error = xfs_iunlink_log_dinode(tp, iup);
117+
list_del(&lip->li_trans);
118+
xfs_iunlink_item_release(lip);
119+
return error;
120+
}
121+
122+
static const struct xfs_item_ops xfs_iunlink_item_ops = {
123+
.iop_release = xfs_iunlink_item_release,
124+
.iop_sort = xfs_iunlink_item_sort,
125+
.iop_precommit = xfs_iunlink_item_precommit,
126+
};
127+
128+
129+
/*
130+
* Initialize the inode log item for a newly allocated (in-core) inode.
131+
*
132+
* Inode extents can only reside within an AG. Hence specify the starting
133+
* block for the inode chunk by offset within an AG as well as the
134+
* length of the allocated extent.
135+
*
136+
* This joins the item to the transaction and marks it dirty so
137+
* that we don't need a separate call to do this, nor does the
138+
* caller need to know anything about the iunlink item.
139+
*/
140+
int
141+
xfs_iunlink_log_inode(
142+
struct xfs_trans *tp,
143+
struct xfs_inode *ip,
144+
struct xfs_perag *pag,
145+
xfs_agino_t next_agino)
146+
{
147+
struct xfs_mount *mp = tp->t_mountp;
148+
struct xfs_iunlink_item *iup;
149+
150+
ASSERT(xfs_verify_agino_or_null(pag, next_agino));
151+
ASSERT(xfs_verify_agino_or_null(pag, ip->i_next_unlinked));
152+
153+
/*
154+
* Since we're updating a linked list, we should never find that the
155+
* current pointer is the same as the new value, unless we're
156+
* terminating the list.
157+
*/
158+
if (ip->i_next_unlinked == next_agino) {
159+
if (next_agino != NULLAGINO)
160+
return -EFSCORRUPTED;
161+
return 0;
162+
}
163+
164+
iup = kmem_cache_zalloc(xfs_iunlink_cache, GFP_KERNEL | __GFP_NOFAIL);
165+
xfs_log_item_init(mp, &iup->item, XFS_LI_IUNLINK,
166+
&xfs_iunlink_item_ops);
167+
168+
iup->ip = ip;
169+
iup->next_agino = next_agino;
170+
iup->old_agino = ip->i_next_unlinked;
171+
172+
atomic_inc(&pag->pag_ref);
173+
iup->pag = pag;
174+
175+
xfs_trans_add_item(tp, &iup->item);
176+
tp->t_flags |= XFS_TRANS_DIRTY;
177+
set_bit(XFS_LI_DIRTY, &iup->item.li_flags);
178+
return 0;
179+
}
180+

fs/xfs/xfs_iunlink_item.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (c) 2020-2022, Red Hat, Inc.
4+
* All Rights Reserved.
5+
*/
6+
#ifndef XFS_IUNLINK_ITEM_H
7+
#define XFS_IUNLINK_ITEM_H 1
8+
9+
struct xfs_trans;
10+
struct xfs_inode;
11+
struct xfs_perag;
12+
13+
/* in memory log item structure */
14+
struct xfs_iunlink_item {
15+
struct xfs_log_item item;
16+
struct xfs_inode *ip;
17+
struct xfs_perag *pag;
18+
xfs_agino_t next_agino;
19+
xfs_agino_t old_agino;
20+
};
21+
22+
extern struct kmem_cache *xfs_iunlink_cache;
23+
24+
int xfs_iunlink_log_inode(struct xfs_trans *tp, struct xfs_inode *ip,
25+
struct xfs_perag *pag, xfs_agino_t next_agino);
26+
27+
#endif /* XFS_IUNLINK_ITEM_H */

fs/xfs/xfs_super.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "xfs_defer.h"
4141
#include "xfs_attr_item.h"
4242
#include "xfs_xattr.h"
43+
#include "xfs_iunlink_item.h"
4344

4445
#include <linux/magic.h>
4546
#include <linux/fs_context.h>
@@ -2096,8 +2097,16 @@ xfs_init_caches(void)
20962097
if (!xfs_attri_cache)
20972098
goto out_destroy_attrd_cache;
20982099

2100+
xfs_iunlink_cache = kmem_cache_create("xfs_iul_item",
2101+
sizeof(struct xfs_iunlink_item),
2102+
0, 0, NULL);
2103+
if (!xfs_iunlink_cache)
2104+
goto out_destroy_attri_cache;
2105+
20992106
return 0;
21002107

2108+
out_destroy_attri_cache:
2109+
kmem_cache_destroy(xfs_attri_cache);
21012110
out_destroy_attrd_cache:
21022111
kmem_cache_destroy(xfs_attrd_cache);
21032112
out_destroy_bui_cache:
@@ -2148,6 +2157,7 @@ xfs_destroy_caches(void)
21482157
* destroy caches.
21492158
*/
21502159
rcu_barrier();
2160+
kmem_cache_destroy(xfs_iunlink_cache);
21512161
kmem_cache_destroy(xfs_attri_cache);
21522162
kmem_cache_destroy(xfs_attrd_cache);
21532163
kmem_cache_destroy(xfs_bui_cache);

0 commit comments

Comments
 (0)