Skip to content

Commit da080fe

Browse files
dennisszhoukdave
authored andcommitted
btrfs: keep track of free space bitmap trim status cleanliness
There is a cap in btrfs in the amount of free extents that a block group can have. When it surpasses that threshold, future extents are placed into bitmaps. Instead of keeping track of if a certain bit is trimmed or not in a second bitmap, keep track of the relative state of the bitmap. With async discard, trimming bitmaps becomes a more frequent operation. As a trade off with simplicity, we keep track of if discarding a bitmap is in progress. If we fully scan a bitmap and trim as necessary, the bitmap is marked clean. This has some caveats as the min block size may skip over regions deemed too small. But this should be a reasonable trade off rather than keeping a second bitmap and making allocation paths more complex. The downside is we may overtrim, but ideally the min block size should prevent us from doing that too often and getting stuck trimming pathological cases. BTRFS_TRIM_STATE_TRIMMING is added to indicate a bitmap is in the process of being trimmed. If additional free space is added to that bitmap, the bit is cleared. A bitmap will be marked BTRFS_TRIM_STATE_TRIMMED if the trimming code was able to reach the end of it and the former is still set. Reviewed-by: Josef Bacik <[email protected]> Signed-off-by: Dennis Zhou <[email protected]> Reviewed-by: David Sterba <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent a7ccb25 commit da080fe

File tree

2 files changed

+92
-9
lines changed

2 files changed

+92
-9
lines changed

fs/btrfs/free-space-cache.c

Lines changed: 80 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1979,11 +1979,18 @@ static noinline int remove_from_bitmap(struct btrfs_free_space_ctl *ctl,
19791979

19801980
static u64 add_bytes_to_bitmap(struct btrfs_free_space_ctl *ctl,
19811981
struct btrfs_free_space *info, u64 offset,
1982-
u64 bytes)
1982+
u64 bytes, enum btrfs_trim_state trim_state)
19831983
{
19841984
u64 bytes_to_set = 0;
19851985
u64 end;
19861986

1987+
/*
1988+
* This is a tradeoff to make bitmap trim state minimal. We mark the
1989+
* whole bitmap untrimmed if at any point we add untrimmed regions.
1990+
*/
1991+
if (trim_state == BTRFS_TRIM_STATE_UNTRIMMED)
1992+
info->trim_state = BTRFS_TRIM_STATE_UNTRIMMED;
1993+
19871994
end = info->offset + (u64)(BITS_PER_BITMAP * ctl->unit);
19881995

19891996
bytes_to_set = min(end - offset, bytes);
@@ -2058,10 +2065,12 @@ static int insert_into_bitmap(struct btrfs_free_space_ctl *ctl,
20582065
struct btrfs_block_group *block_group = NULL;
20592066
int added = 0;
20602067
u64 bytes, offset, bytes_added;
2068+
enum btrfs_trim_state trim_state;
20612069
int ret;
20622070

20632071
bytes = info->bytes;
20642072
offset = info->offset;
2073+
trim_state = info->trim_state;
20652074

20662075
if (!ctl->op->use_bitmap(ctl, info))
20672076
return 0;
@@ -2096,8 +2105,8 @@ static int insert_into_bitmap(struct btrfs_free_space_ctl *ctl,
20962105
}
20972106

20982107
if (entry->offset == offset_to_bitmap(ctl, offset)) {
2099-
bytes_added = add_bytes_to_bitmap(ctl, entry,
2100-
offset, bytes);
2108+
bytes_added = add_bytes_to_bitmap(ctl, entry, offset,
2109+
bytes, trim_state);
21012110
bytes -= bytes_added;
21022111
offset += bytes_added;
21032112
}
@@ -2116,7 +2125,8 @@ static int insert_into_bitmap(struct btrfs_free_space_ctl *ctl,
21162125
goto new_bitmap;
21172126
}
21182127

2119-
bytes_added = add_bytes_to_bitmap(ctl, bitmap_info, offset, bytes);
2128+
bytes_added = add_bytes_to_bitmap(ctl, bitmap_info, offset, bytes,
2129+
trim_state);
21202130
bytes -= bytes_added;
21212131
offset += bytes_added;
21222132
added = 0;
@@ -2150,6 +2160,7 @@ static int insert_into_bitmap(struct btrfs_free_space_ctl *ctl,
21502160
/* allocate the bitmap */
21512161
info->bitmap = kmem_cache_zalloc(btrfs_free_space_bitmap_cachep,
21522162
GFP_NOFS);
2163+
info->trim_state = BTRFS_TRIM_STATE_TRIMMED;
21532164
spin_lock(&ctl->tree_lock);
21542165
if (!info->bitmap) {
21552166
ret = -ENOMEM;
@@ -3324,6 +3335,37 @@ static int trim_no_bitmap(struct btrfs_block_group *block_group,
33243335
return ret;
33253336
}
33263337

3338+
/*
3339+
* If we break out of trimming a bitmap prematurely, we should reset the
3340+
* trimming bit. In a rather contrieved case, it's possible to race here so
3341+
* reset the state to BTRFS_TRIM_STATE_UNTRIMMED.
3342+
*
3343+
* start = start of bitmap
3344+
* end = near end of bitmap
3345+
*
3346+
* Thread 1: Thread 2:
3347+
* trim_bitmaps(start)
3348+
* trim_bitmaps(end)
3349+
* end_trimming_bitmap()
3350+
* reset_trimming_bitmap()
3351+
*/
3352+
static void reset_trimming_bitmap(struct btrfs_free_space_ctl *ctl, u64 offset)
3353+
{
3354+
struct btrfs_free_space *entry;
3355+
3356+
spin_lock(&ctl->tree_lock);
3357+
entry = tree_search_offset(ctl, offset, 1, 0);
3358+
if (entry)
3359+
entry->trim_state = BTRFS_TRIM_STATE_UNTRIMMED;
3360+
spin_unlock(&ctl->tree_lock);
3361+
}
3362+
3363+
static void end_trimming_bitmap(struct btrfs_free_space *entry)
3364+
{
3365+
if (btrfs_free_space_trimming_bitmap(entry))
3366+
entry->trim_state = BTRFS_TRIM_STATE_TRIMMED;
3367+
}
3368+
33273369
static int trim_bitmaps(struct btrfs_block_group *block_group,
33283370
u64 *total_trimmed, u64 start, u64 end, u64 minlen)
33293371
{
@@ -3348,16 +3390,33 @@ static int trim_bitmaps(struct btrfs_block_group *block_group,
33483390
}
33493391

33503392
entry = tree_search_offset(ctl, offset, 1, 0);
3351-
if (!entry) {
3393+
if (!entry || btrfs_free_space_trimmed(entry)) {
33523394
spin_unlock(&ctl->tree_lock);
33533395
mutex_unlock(&ctl->cache_writeout_mutex);
33543396
next_bitmap = true;
33553397
goto next;
33563398
}
33573399

3400+
/*
3401+
* Async discard bitmap trimming begins at by setting the start
3402+
* to be key.objectid and the offset_to_bitmap() aligns to the
3403+
* start of the bitmap. This lets us know we are fully
3404+
* scanning the bitmap rather than only some portion of it.
3405+
*/
3406+
if (start == offset)
3407+
entry->trim_state = BTRFS_TRIM_STATE_TRIMMING;
3408+
33583409
bytes = minlen;
33593410
ret2 = search_bitmap(ctl, entry, &start, &bytes, false);
33603411
if (ret2 || start >= end) {
3412+
/*
3413+
* This keeps the invariant that all bytes are trimmed
3414+
* if BTRFS_TRIM_STATE_TRIMMED is set on a bitmap.
3415+
*/
3416+
if (ret2 && !minlen)
3417+
end_trimming_bitmap(entry);
3418+
else
3419+
entry->trim_state = BTRFS_TRIM_STATE_UNTRIMMED;
33613420
spin_unlock(&ctl->tree_lock);
33623421
mutex_unlock(&ctl->cache_writeout_mutex);
33633422
next_bitmap = true;
@@ -3366,6 +3425,7 @@ static int trim_bitmaps(struct btrfs_block_group *block_group,
33663425

33673426
bytes = min(bytes, end - start);
33683427
if (bytes < minlen) {
3428+
entry->trim_state = BTRFS_TRIM_STATE_UNTRIMMED;
33693429
spin_unlock(&ctl->tree_lock);
33703430
mutex_unlock(&ctl->cache_writeout_mutex);
33713431
goto next;
@@ -3383,18 +3443,21 @@ static int trim_bitmaps(struct btrfs_block_group *block_group,
33833443

33843444
ret = do_trimming(block_group, total_trimmed, start, bytes,
33853445
start, bytes, &trim_entry);
3386-
if (ret)
3446+
if (ret) {
3447+
reset_trimming_bitmap(ctl, offset);
33873448
break;
3449+
}
33883450
next:
33893451
if (next_bitmap) {
33903452
offset += BITS_PER_BITMAP * ctl->unit;
3453+
start = offset;
33913454
} else {
33923455
start += bytes;
3393-
if (start >= offset + BITS_PER_BITMAP * ctl->unit)
3394-
offset += BITS_PER_BITMAP * ctl->unit;
33953456
}
33963457

33973458
if (fatal_signal_pending(current)) {
3459+
if (start != offset)
3460+
reset_trimming_bitmap(ctl, offset);
33983461
ret = -ERESTARTSYS;
33993462
break;
34003463
}
@@ -3448,7 +3511,9 @@ void btrfs_put_block_group_trimming(struct btrfs_block_group *block_group)
34483511
int btrfs_trim_block_group(struct btrfs_block_group *block_group,
34493512
u64 *trimmed, u64 start, u64 end, u64 minlen)
34503513
{
3514+
struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl;
34513515
int ret;
3516+
u64 rem = 0;
34523517

34533518
*trimmed = 0;
34543519

@@ -3465,6 +3530,10 @@ int btrfs_trim_block_group(struct btrfs_block_group *block_group,
34653530
goto out;
34663531

34673532
ret = trim_bitmaps(block_group, trimmed, start, end, minlen);
3533+
div64_u64_rem(end, BITS_PER_BITMAP * ctl->unit, &rem);
3534+
/* If we ended in the middle of a bitmap, reset the trimming flag */
3535+
if (rem)
3536+
reset_trimming_bitmap(ctl, offset_to_bitmap(ctl, end));
34683537
out:
34693538
btrfs_put_block_group_trimming(block_group);
34703539
return ret;
@@ -3649,6 +3718,7 @@ int test_add_free_space_entry(struct btrfs_block_group *cache,
36493718
struct btrfs_free_space_ctl *ctl = cache->free_space_ctl;
36503719
struct btrfs_free_space *info = NULL, *bitmap_info;
36513720
void *map = NULL;
3721+
enum btrfs_trim_state trim_state = BTRFS_TRIM_STATE_TRIMMED;
36523722
u64 bytes_added;
36533723
int ret;
36543724

@@ -3690,7 +3760,8 @@ int test_add_free_space_entry(struct btrfs_block_group *cache,
36903760
info = NULL;
36913761
}
36923762

3693-
bytes_added = add_bytes_to_bitmap(ctl, bitmap_info, offset, bytes);
3763+
bytes_added = add_bytes_to_bitmap(ctl, bitmap_info, offset, bytes,
3764+
trim_state);
36943765

36953766
bytes -= bytes_added;
36963767
offset += bytes_added;

fs/btrfs/free-space-cache.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,16 @@
88

99
/*
1010
* This is the trim state of an extent or bitmap.
11+
*
12+
* BTRFS_TRIM_STATE_TRIMMING is special and used to maintain the state of a
13+
* bitmap as we may need several trims to fully trim a single bitmap entry.
14+
* This is reset should any free space other than trimmed space be added to the
15+
* bitmap.
1116
*/
1217
enum btrfs_trim_state {
1318
BTRFS_TRIM_STATE_UNTRIMMED,
1419
BTRFS_TRIM_STATE_TRIMMED,
20+
BTRFS_TRIM_STATE_TRIMMING,
1521
};
1622

1723
struct btrfs_free_space {
@@ -29,6 +35,12 @@ static inline bool btrfs_free_space_trimmed(struct btrfs_free_space *info)
2935
return (info->trim_state == BTRFS_TRIM_STATE_TRIMMED);
3036
}
3137

38+
static inline bool btrfs_free_space_trimming_bitmap(
39+
struct btrfs_free_space *info)
40+
{
41+
return (info->trim_state == BTRFS_TRIM_STATE_TRIMMING);
42+
}
43+
3244
struct btrfs_free_space_ctl {
3345
spinlock_t tree_lock;
3446
struct rb_root free_space_offset;

0 commit comments

Comments
 (0)