Skip to content

Commit 166afc4

Browse files
committed
Merge tag 'reflink-speedups-5.19_2022-04-28' of git://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into xfs-5.19-for-next
xfs: fix reflink inefficiencies As Dave Chinner has complained about on IRC, there are a couple of things about reflink that are very inefficient. First of all, we limited the size of all bunmapi operations to avoid flooding the log with defer ops in the worst case, but recent changes to the defer ops code have solved that problem, so get rid of the bunmapi length clamp. Second, the log reservations for reflink operations are far far larger than they need to be. Shrink them to exactly what we need to handle each deferred RUI and CUI log item, and no more. Also reduce logcount because we don't need 8 rolls per operation. Introduce a transaction reservation compatibility layer to avoid changing the minimum log size calculations. Signed-off-by: Dave Chinner <[email protected]>
2 parents 956f1b8 + 6ed7e50 commit 166afc4

File tree

9 files changed

+345
-139
lines changed

9 files changed

+345
-139
lines changed

fs/xfs/libxfs/xfs_bmap.c

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5280,7 +5280,6 @@ __xfs_bunmapi(
52805280
int whichfork; /* data or attribute fork */
52815281
xfs_fsblock_t sum;
52825282
xfs_filblks_t len = *rlen; /* length to unmap in file */
5283-
xfs_fileoff_t max_len;
52845283
xfs_fileoff_t end;
52855284
struct xfs_iext_cursor icur;
52865285
bool done = false;
@@ -5299,16 +5298,6 @@ __xfs_bunmapi(
52995298
ASSERT(len > 0);
53005299
ASSERT(nexts >= 0);
53015300

5302-
/*
5303-
* Guesstimate how many blocks we can unmap without running the risk of
5304-
* blowing out the transaction with a mix of EFIs and reflink
5305-
* adjustments.
5306-
*/
5307-
if (tp && xfs_is_reflink_inode(ip) && whichfork == XFS_DATA_FORK)
5308-
max_len = min(len, xfs_refcount_max_unmap(tp->t_log_res));
5309-
else
5310-
max_len = len;
5311-
53125301
error = xfs_iread_extents(tp, ip, whichfork);
53135302
if (error)
53145303
return error;
@@ -5347,7 +5336,7 @@ __xfs_bunmapi(
53475336

53485337
extno = 0;
53495338
while (end != (xfs_fileoff_t)-1 && end >= start &&
5350-
(nexts == 0 || extno < nexts) && max_len > 0) {
5339+
(nexts == 0 || extno < nexts)) {
53515340
/*
53525341
* Is the found extent after a hole in which end lives?
53535342
* Just back up to the previous extent, if so.
@@ -5381,14 +5370,6 @@ __xfs_bunmapi(
53815370
if (del.br_startoff + del.br_blockcount > end + 1)
53825371
del.br_blockcount = end + 1 - del.br_startoff;
53835372

5384-
/* How much can we safely unmap? */
5385-
if (max_len < del.br_blockcount) {
5386-
del.br_startoff += del.br_blockcount - max_len;
5387-
if (!wasdel)
5388-
del.br_startblock += del.br_blockcount - max_len;
5389-
del.br_blockcount = max_len;
5390-
}
5391-
53925373
if (!isrt)
53935374
goto delete;
53945375

@@ -5524,7 +5505,6 @@ __xfs_bunmapi(
55245505
if (error)
55255506
goto error0;
55265507

5527-
max_len -= del.br_blockcount;
55285508
end = del.br_startoff - 1;
55295509
nodelete:
55305510
/*

fs/xfs/libxfs/xfs_log_rlimit.c

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "xfs_trans_space.h"
1515
#include "xfs_da_btree.h"
1616
#include "xfs_bmap_btree.h"
17+
#include "xfs_trace.h"
1718

1819
/*
1920
* Calculate the maximum length in bytes that would be required for a local
@@ -36,6 +37,65 @@ xfs_log_calc_max_attrsetm_res(
3637
M_RES(mp)->tr_attrsetrt.tr_logres * nblks;
3738
}
3839

40+
/*
41+
* Compute an alternate set of log reservation sizes for use exclusively with
42+
* minimum log size calculations.
43+
*/
44+
static void
45+
xfs_log_calc_trans_resv_for_minlogblocks(
46+
struct xfs_mount *mp,
47+
struct xfs_trans_resv *resv)
48+
{
49+
unsigned int rmap_maxlevels = mp->m_rmap_maxlevels;
50+
51+
/*
52+
* In the early days of rmap+reflink, we always set the rmap maxlevels
53+
* to 9 even if the AG was small enough that it would never grow to
54+
* that height. Transaction reservation sizes influence the minimum
55+
* log size calculation, which influences the size of the log that mkfs
56+
* creates. Use the old value here to ensure that newly formatted
57+
* small filesystems will mount on older kernels.
58+
*/
59+
if (xfs_has_rmapbt(mp) && xfs_has_reflink(mp))
60+
mp->m_rmap_maxlevels = XFS_OLD_REFLINK_RMAP_MAXLEVELS;
61+
62+
xfs_trans_resv_calc(mp, resv);
63+
64+
if (xfs_has_reflink(mp)) {
65+
/*
66+
* In the early days of reflink, typical log operation counts
67+
* were greatly overestimated.
68+
*/
69+
resv->tr_write.tr_logcount = XFS_WRITE_LOG_COUNT_REFLINK;
70+
resv->tr_itruncate.tr_logcount =
71+
XFS_ITRUNCATE_LOG_COUNT_REFLINK;
72+
resv->tr_qm_dqalloc.tr_logcount = XFS_WRITE_LOG_COUNT_REFLINK;
73+
} else if (xfs_has_rmapbt(mp)) {
74+
/*
75+
* In the early days of non-reflink rmap, the impact of rmapbt
76+
* updates on log counts were not taken into account at all.
77+
*/
78+
resv->tr_write.tr_logcount = XFS_WRITE_LOG_COUNT;
79+
resv->tr_itruncate.tr_logcount = XFS_ITRUNCATE_LOG_COUNT;
80+
resv->tr_qm_dqalloc.tr_logcount = XFS_WRITE_LOG_COUNT;
81+
}
82+
83+
/*
84+
* In the early days of reflink, we did not use deferred refcount
85+
* update log items, so log reservations must be recomputed using the
86+
* old calculations.
87+
*/
88+
resv->tr_write.tr_logres =
89+
xfs_calc_write_reservation_minlogsize(mp);
90+
resv->tr_itruncate.tr_logres =
91+
xfs_calc_itruncate_reservation_minlogsize(mp);
92+
resv->tr_qm_dqalloc.tr_logres =
93+
xfs_calc_qm_dqalloc_reservation_minlogsize(mp);
94+
95+
/* Put everything back the way it was. This goes at the end. */
96+
mp->m_rmap_maxlevels = rmap_maxlevels;
97+
}
98+
3999
/*
40100
* Iterate over the log space reservation table to figure out and return
41101
* the maximum one in terms of the pre-calculated values which were done
@@ -46,29 +106,36 @@ xfs_log_get_max_trans_res(
46106
struct xfs_mount *mp,
47107
struct xfs_trans_res *max_resp)
48108
{
109+
struct xfs_trans_resv resv = {};
49110
struct xfs_trans_res *resp;
50111
struct xfs_trans_res *end_resp;
112+
unsigned int i;
51113
int log_space = 0;
52114
int attr_space;
53115

54116
attr_space = xfs_log_calc_max_attrsetm_res(mp);
55117

56-
resp = (struct xfs_trans_res *)M_RES(mp);
57-
end_resp = (struct xfs_trans_res *)(M_RES(mp) + 1);
58-
for (; resp < end_resp; resp++) {
118+
xfs_log_calc_trans_resv_for_minlogblocks(mp, &resv);
119+
120+
resp = (struct xfs_trans_res *)&resv;
121+
end_resp = (struct xfs_trans_res *)(&resv + 1);
122+
for (i = 0; resp < end_resp; i++, resp++) {
59123
int tmp = resp->tr_logcount > 1 ?
60124
resp->tr_logres * resp->tr_logcount :
61125
resp->tr_logres;
126+
127+
trace_xfs_trans_resv_calc_minlogsize(mp, i, resp);
62128
if (log_space < tmp) {
63129
log_space = tmp;
64130
*max_resp = *resp; /* struct copy */
65131
}
66132
}
67133

68134
if (attr_space > log_space) {
69-
*max_resp = M_RES(mp)->tr_attrsetm; /* struct copy */
135+
*max_resp = resv.tr_attrsetm; /* struct copy */
70136
max_resp->tr_logres = attr_space;
71137
}
138+
trace_xfs_log_get_max_trans_res(mp, max_resp);
72139
}
73140

74141
/*

fs/xfs/libxfs/xfs_refcount.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -886,8 +886,13 @@ xfs_refcount_still_have_space(
886886
{
887887
unsigned long overhead;
888888

889-
overhead = cur->bc_ag.refc.shape_changes *
890-
xfs_allocfree_log_count(cur->bc_mp, 1);
889+
/*
890+
* Worst case estimate: full splits of the free space and rmap btrees
891+
* to handle each of the shape changes to the refcount btree.
892+
*/
893+
overhead = xfs_allocfree_block_count(cur->bc_mp,
894+
cur->bc_ag.refc.shape_changes);
895+
overhead += cur->bc_mp->m_refc_maxlevels;
891896
overhead *= cur->bc_mp->m_sb.sb_blocksize;
892897

893898
/*
@@ -960,6 +965,7 @@ xfs_refcount_adjust_extents(
960965
* Either cover the hole (increment) or
961966
* delete the range (decrement).
962967
*/
968+
cur->bc_ag.refc.nr_ops++;
963969
if (tmp.rc_refcount) {
964970
error = xfs_refcount_insert(cur, &tmp,
965971
&found_tmp);
@@ -970,7 +976,6 @@ xfs_refcount_adjust_extents(
970976
error = -EFSCORRUPTED;
971977
goto out_error;
972978
}
973-
cur->bc_ag.refc.nr_ops++;
974979
} else {
975980
fsbno = XFS_AGB_TO_FSB(cur->bc_mp,
976981
cur->bc_ag.pag->pag_agno,
@@ -1001,11 +1006,11 @@ xfs_refcount_adjust_extents(
10011006
ext.rc_refcount += adj;
10021007
trace_xfs_refcount_modify_extent(cur->bc_mp,
10031008
cur->bc_ag.pag->pag_agno, &ext);
1009+
cur->bc_ag.refc.nr_ops++;
10041010
if (ext.rc_refcount > 1) {
10051011
error = xfs_refcount_update(cur, &ext);
10061012
if (error)
10071013
goto out_error;
1008-
cur->bc_ag.refc.nr_ops++;
10091014
} else if (ext.rc_refcount == 1) {
10101015
error = xfs_refcount_delete(cur, &found_rec);
10111016
if (error)
@@ -1014,7 +1019,6 @@ xfs_refcount_adjust_extents(
10141019
error = -EFSCORRUPTED;
10151020
goto out_error;
10161021
}
1017-
cur->bc_ag.refc.nr_ops++;
10181022
goto advloop;
10191023
} else {
10201024
fsbno = XFS_AGB_TO_FSB(cur->bc_mp,

fs/xfs/libxfs/xfs_refcount.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,17 @@ extern int xfs_refcount_recover_cow_leftovers(struct xfs_mount *mp,
6767
* log (plus any key updates) so we'll conservatively assume 32 bytes
6868
* per record. We must also leave space for btree splits on both ends
6969
* of the range and space for the CUD and a new CUI.
70+
*
71+
* Each EFI that we attach to the transaction is assumed to consume ~32 bytes.
72+
* This is a low estimate for an EFI tracking a single extent (16 bytes for the
73+
* EFI header, 16 for the extent, and 12 for the xlog op header), but the
74+
* estimate is acceptable if there's more than one extent being freed.
75+
* In the worst case of freeing every other block during a refcount decrease
76+
* operation, we amortize the space used for one EFI log item across 16
77+
* extents.
7078
*/
7179
#define XFS_REFCOUNT_ITEM_OVERHEAD 32
7280

73-
static inline xfs_fileoff_t xfs_refcount_max_unmap(int log_res)
74-
{
75-
return (log_res * 3 / 4) / XFS_REFCOUNT_ITEM_OVERHEAD;
76-
}
77-
7881
extern int xfs_refcount_has_record(struct xfs_btree_cur *cur,
7982
xfs_agblock_t bno, xfs_extlen_t len, bool *exists);
8083
union xfs_btree_rec;

0 commit comments

Comments
 (0)