Skip to content

Commit 9118264

Browse files
josefbacikkdave
authored andcommitted
btrfs: stop partially refilling tickets when releasing space
btrfs_space_info_add_old_bytes is used when adding the extra space from an existing reservation back into the space_info to be used by any waiting tickets. In order to keep us from overcommitting we check to make sure that we can still use this space for our reserve ticket, and if we cannot we'll simply subtract it from space_info->bytes_may_use. However this is problematic, because it assumes that only changes to bytes_may_use would affect our ability to make reservations. Any changes to bytes_reserved would be missed. If we were unable to make a reservation prior because of reserved space, but that reserved space was free'd due to unlink or truncate and we were allowed to immediately reclaim that metadata space we would still ENOSPC. Consider the example where we create a file with a bunch of extents, using up 2MiB of actual space for the new tree blocks. Then we try to make a reservation of 2MiB but we do not have enough space to make this reservation. The iput() occurs in another thread and we remove this space, and since we did not write the blocks we simply do space_info->bytes_reserved -= 2MiB. We would never see this because we do not check our space info used, we just try to re-use the freed reservations. To fix this problem, and to greatly simplify the wakeup code, do away with this partial refilling nonsense. Use btrfs_space_info_add_old_bytes to subtract the reservation from space_info->bytes_may_use, and then check the ticket against the total used of the space_info the same way we do with the initial reservation attempt. This keeps the reservation logic consistent and solves the problem of early ENOSPC in the case that we free up space in places other than bytes_may_use and bytes_pinned. Thanks, Signed-off-by: Josef Bacik <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent a43c383 commit 9118264

File tree

1 file changed

+16
-27
lines changed

1 file changed

+16
-27
lines changed

fs/btrfs/space-info.c

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -233,52 +233,41 @@ void btrfs_space_info_add_old_bytes(struct btrfs_fs_info *fs_info,
233233
struct btrfs_space_info *space_info,
234234
u64 num_bytes)
235235
{
236-
struct reserve_ticket *ticket;
237236
struct list_head *head;
238-
u64 used;
239237
enum btrfs_reserve_flush_enum flush = BTRFS_RESERVE_NO_FLUSH;
240-
bool check_overcommit = false;
241238

242239
spin_lock(&space_info->lock);
243240
head = &space_info->priority_tickets;
241+
btrfs_space_info_update_bytes_may_use(fs_info, space_info, -num_bytes);
244242

245-
/*
246-
* If we are over our limit then we need to check and see if we can
247-
* overcommit, and if we can't then we just need to free up our space
248-
* and not satisfy any requests.
249-
*/
250-
used = btrfs_space_info_used(space_info, true);
251-
if (used - num_bytes >= space_info->total_bytes)
252-
check_overcommit = true;
253243
again:
254-
while (!list_empty(head) && num_bytes) {
255-
ticket = list_first_entry(head, struct reserve_ticket,
256-
list);
257-
/*
258-
* We use 0 bytes because this space is already reserved, so
259-
* adding the ticket space would be a double count.
260-
*/
261-
if (check_overcommit &&
262-
!can_overcommit(fs_info, space_info, 0, flush, false))
263-
break;
264-
if (num_bytes >= ticket->bytes) {
244+
while (!list_empty(head)) {
245+
struct reserve_ticket *ticket;
246+
u64 used = btrfs_space_info_used(space_info, true);
247+
248+
ticket = list_first_entry(head, struct reserve_ticket, list);
249+
250+
/* Check and see if our ticket can be satisified now. */
251+
if ((used + ticket->bytes <= space_info->total_bytes) ||
252+
can_overcommit(fs_info, space_info, ticket->bytes, flush,
253+
false)) {
254+
btrfs_space_info_update_bytes_may_use(fs_info,
255+
space_info,
256+
ticket->bytes);
265257
list_del_init(&ticket->list);
266-
num_bytes -= ticket->bytes;
267258
ticket->bytes = 0;
268259
space_info->tickets_id++;
269260
wake_up(&ticket->wait);
270261
} else {
271-
ticket->bytes -= num_bytes;
272-
num_bytes = 0;
262+
break;
273263
}
274264
}
275265

276-
if (num_bytes && head == &space_info->priority_tickets) {
266+
if (head == &space_info->priority_tickets) {
277267
head = &space_info->tickets;
278268
flush = BTRFS_RESERVE_FLUSH_ALL;
279269
goto again;
280270
}
281-
btrfs_space_info_update_bytes_may_use(fs_info, space_info, -num_bytes);
282271
spin_unlock(&space_info->lock);
283272
}
284273

0 commit comments

Comments
 (0)