@@ -21,27 +21,30 @@ static struct list_head *get_discard_list(struct btrfs_discard_ctl *discard_ctl,
21
21
return & discard_ctl -> discard_list [block_group -> discard_index ];
22
22
}
23
23
24
- static void add_to_discard_list (struct btrfs_discard_ctl * discard_ctl ,
25
- struct btrfs_block_group * block_group )
24
+ static void __add_to_discard_list (struct btrfs_discard_ctl * discard_ctl ,
25
+ struct btrfs_block_group * block_group )
26
26
{
27
- spin_lock (& discard_ctl -> lock );
28
-
29
- if (!btrfs_run_discard_work (discard_ctl )) {
30
- spin_unlock (& discard_ctl -> lock );
27
+ if (!btrfs_run_discard_work (discard_ctl ))
31
28
return ;
32
- }
33
29
34
30
if (list_empty (& block_group -> discard_list ) ||
35
31
block_group -> discard_index == BTRFS_DISCARD_INDEX_UNUSED ) {
36
32
if (block_group -> discard_index == BTRFS_DISCARD_INDEX_UNUSED )
37
33
block_group -> discard_index = BTRFS_DISCARD_INDEX_START ;
38
34
block_group -> discard_eligible_time = (ktime_get_ns () +
39
35
BTRFS_DISCARD_DELAY );
36
+ block_group -> discard_state = BTRFS_DISCARD_RESET_CURSOR ;
40
37
}
41
38
42
39
list_move_tail (& block_group -> discard_list ,
43
40
get_discard_list (discard_ctl , block_group ));
41
+ }
44
42
43
+ static void add_to_discard_list (struct btrfs_discard_ctl * discard_ctl ,
44
+ struct btrfs_block_group * block_group )
45
+ {
46
+ spin_lock (& discard_ctl -> lock );
47
+ __add_to_discard_list (discard_ctl , block_group );
45
48
spin_unlock (& discard_ctl -> lock );
46
49
}
47
50
@@ -60,6 +63,7 @@ static void add_to_discard_unused_list(struct btrfs_discard_ctl *discard_ctl,
60
63
block_group -> discard_index = BTRFS_DISCARD_INDEX_UNUSED ;
61
64
block_group -> discard_eligible_time = (ktime_get_ns () +
62
65
BTRFS_DISCARD_UNUSED_DELAY );
66
+ block_group -> discard_state = BTRFS_DISCARD_RESET_CURSOR ;
63
67
list_add_tail (& block_group -> discard_list ,
64
68
& discard_ctl -> discard_list [BTRFS_DISCARD_INDEX_UNUSED ]);
65
69
@@ -127,23 +131,40 @@ static struct btrfs_block_group *find_next_block_group(
127
131
/**
128
132
* peek_discard_list - wrap find_next_block_group()
129
133
* @discard_ctl: discard control
134
+ * @discard_state: the discard_state of the block_group after state management
130
135
*
131
136
* This wraps find_next_block_group() and sets the block_group to be in use.
137
+ * discard_state's control flow is managed here. Variables related to
138
+ * discard_state are reset here as needed (eg. discard_cursor). @discard_state
139
+ * is remembered as it may change while we're discarding, but we want the
140
+ * discard to execute in the context determined here.
132
141
*/
133
142
static struct btrfs_block_group * peek_discard_list (
134
- struct btrfs_discard_ctl * discard_ctl )
143
+ struct btrfs_discard_ctl * discard_ctl ,
144
+ enum btrfs_discard_state * discard_state )
135
145
{
136
146
struct btrfs_block_group * block_group ;
137
147
const u64 now = ktime_get_ns ();
138
148
139
149
spin_lock (& discard_ctl -> lock );
140
-
150
+ again :
141
151
block_group = find_next_block_group (discard_ctl , now );
142
152
143
- if (block_group && now < block_group -> discard_eligible_time )
153
+ if (block_group && now > block_group -> discard_eligible_time ) {
154
+ if (block_group -> discard_index == BTRFS_DISCARD_INDEX_UNUSED &&
155
+ block_group -> used != 0 ) {
156
+ __add_to_discard_list (discard_ctl , block_group );
157
+ goto again ;
158
+ }
159
+ if (block_group -> discard_state == BTRFS_DISCARD_RESET_CURSOR ) {
160
+ block_group -> discard_cursor = block_group -> start ;
161
+ block_group -> discard_state = BTRFS_DISCARD_EXTENTS ;
162
+ }
163
+ discard_ctl -> block_group = block_group ;
164
+ * discard_state = block_group -> discard_state ;
165
+ } else {
144
166
block_group = NULL ;
145
-
146
- discard_ctl -> block_group = block_group ;
167
+ }
147
168
148
169
spin_unlock (& discard_ctl -> lock );
149
170
@@ -254,24 +275,54 @@ static void btrfs_finish_discard_pass(struct btrfs_discard_ctl *discard_ctl,
254
275
* btrfs_discard_workfn - discard work function
255
276
* @work: work
256
277
*
257
- * This finds the next block_group to start discarding and then discards it.
278
+ * This finds the next block_group to start discarding and then discards a
279
+ * single region. It does this in a two-pass fashion: first extents and second
280
+ * bitmaps. Completely discarded block groups are sent to the unused_bgs path.
258
281
*/
259
282
static void btrfs_discard_workfn (struct work_struct * work )
260
283
{
261
284
struct btrfs_discard_ctl * discard_ctl ;
262
285
struct btrfs_block_group * block_group ;
286
+ enum btrfs_discard_state discard_state ;
263
287
u64 trimmed = 0 ;
264
288
265
289
discard_ctl = container_of (work , struct btrfs_discard_ctl , work .work );
266
290
267
- block_group = peek_discard_list (discard_ctl );
291
+ block_group = peek_discard_list (discard_ctl , & discard_state );
268
292
if (!block_group || !btrfs_run_discard_work (discard_ctl ))
269
293
return ;
270
294
271
- btrfs_trim_block_group (block_group , & trimmed , block_group -> start ,
272
- btrfs_block_group_end (block_group ), 0 );
295
+ /* Perform discarding */
296
+ if (discard_state == BTRFS_DISCARD_BITMAPS )
297
+ btrfs_trim_block_group_bitmaps (block_group , & trimmed ,
298
+ block_group -> discard_cursor ,
299
+ btrfs_block_group_end (block_group ),
300
+ 0 , true);
301
+ else
302
+ btrfs_trim_block_group_extents (block_group , & trimmed ,
303
+ block_group -> discard_cursor ,
304
+ btrfs_block_group_end (block_group ),
305
+ 0 , true);
306
+
307
+ /* Determine next steps for a block_group */
308
+ if (block_group -> discard_cursor >= btrfs_block_group_end (block_group )) {
309
+ if (discard_state == BTRFS_DISCARD_BITMAPS ) {
310
+ btrfs_finish_discard_pass (discard_ctl , block_group );
311
+ } else {
312
+ block_group -> discard_cursor = block_group -> start ;
313
+ spin_lock (& discard_ctl -> lock );
314
+ if (block_group -> discard_state !=
315
+ BTRFS_DISCARD_RESET_CURSOR )
316
+ block_group -> discard_state =
317
+ BTRFS_DISCARD_BITMAPS ;
318
+ spin_unlock (& discard_ctl -> lock );
319
+ }
320
+ }
321
+
322
+ spin_lock (& discard_ctl -> lock );
323
+ discard_ctl -> block_group = NULL ;
324
+ spin_unlock (& discard_ctl -> lock );
273
325
274
- btrfs_finish_discard_pass (discard_ctl , block_group );
275
326
btrfs_discard_schedule_work (discard_ctl , false);
276
327
}
277
328
0 commit comments