Skip to content

Commit 69659e4

Browse files
dgchinnercmaiolino
authored andcommitted
xfs: unmapped buffer item size straddling mismatch
We never log large contiguous regions of unmapped buffers, so this bug is never triggered by the current code. However, the slowpath for formatting buffer straddling regions is broken. That is, the size and shape of the log vector calculated across a straddle does not match how the formatting code formats a straddle. This results in a log vector with an uninitialised iovec and this causes a crash when xlog_write_full() goes to copy the iovec into the journal. Whilst touching this code, don't bother checking mapped or single folio buffers for discontiguous regions because they don't have them. This significantly reduces the overhead of this check when logging large buffers as calling xfs_buf_offset() is not free and it occurs a *lot* in those cases. Fixes: 929f8b0 ("xfs: optimise xfs_buf_item_size/format for contiguous regions") Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Carlos Maiolino <cem@kernel.org>
1 parent 32f6987 commit 69659e4

1 file changed

Lines changed: 14 additions & 4 deletions

File tree

fs/xfs/xfs_buf_item.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ xfs_buf_log_format_size(
5757
(blfp->blf_map_size * sizeof(blfp->blf_data_map[0]));
5858
}
5959

60+
/*
61+
* We only have to worry about discontiguous buffer range straddling on unmapped
62+
* buffers. Everything else will have a contiguous data region we can copy from.
63+
*/
6064
static inline bool
6165
xfs_buf_item_straddle(
6266
struct xfs_buf *bp,
@@ -66,6 +70,9 @@ xfs_buf_item_straddle(
6670
{
6771
void *first, *last;
6872

73+
if (bp->b_page_count == 1 || !(bp->b_flags & XBF_UNMAPPED))
74+
return false;
75+
6976
first = xfs_buf_offset(bp, offset + (first_bit << XFS_BLF_SHIFT));
7077
last = xfs_buf_offset(bp,
7178
offset + ((first_bit + nbits) << XFS_BLF_SHIFT));
@@ -133,11 +140,13 @@ xfs_buf_item_size_segment(
133140
return;
134141

135142
slow_scan:
136-
/* Count the first bit we jumped out of the above loop from */
137-
(*nvecs)++;
138-
*nbytes += XFS_BLF_CHUNK;
143+
ASSERT(bp->b_addr == NULL);
139144
last_bit = first_bit;
145+
nbits = 1;
140146
while (last_bit != -1) {
147+
148+
*nbytes += XFS_BLF_CHUNK;
149+
141150
/*
142151
* This takes the bit number to start looking from and
143152
* returns the next set bit from there. It returns -1
@@ -152,6 +161,8 @@ xfs_buf_item_size_segment(
152161
* else keep scanning the current set of bits.
153162
*/
154163
if (next_bit == -1) {
164+
if (first_bit != last_bit)
165+
(*nvecs)++;
155166
break;
156167
} else if (next_bit != last_bit + 1 ||
157168
xfs_buf_item_straddle(bp, offset, first_bit, nbits)) {
@@ -163,7 +174,6 @@ xfs_buf_item_size_segment(
163174
last_bit++;
164175
nbits++;
165176
}
166-
*nbytes += XFS_BLF_CHUNK;
167177
}
168178
}
169179

0 commit comments

Comments
 (0)