mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-05 18:39:59 +00:00
btrfs: describe the space reservation system in general
Add another comment to cover how the space reservation system works generally. This covers the actual reservation flow, as well as how flushing is handled. Signed-off-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
6f4ad559ea
commit
4b8b052888
1 changed files with 147 additions and 0 deletions
|
@ -10,6 +10,153 @@
|
|||
#include "transaction.h"
|
||||
#include "block-group.h"
|
||||
|
||||
/*
|
||||
* HOW DOES SPACE RESERVATION WORK
|
||||
*
|
||||
* If you want to know about delalloc specifically, there is a separate comment
|
||||
* for that with the delalloc code. This comment is about how the whole system
|
||||
* works generally.
|
||||
*
|
||||
* BASIC CONCEPTS
|
||||
*
|
||||
* 1) space_info. This is the ultimate arbiter of how much space we can use.
|
||||
* There's a description of the bytes_ fields with the struct declaration,
|
||||
* refer to that for specifics on each field. Suffice it to say that for
|
||||
* reservations we care about total_bytes - SUM(space_info->bytes_) when
|
||||
* determining if there is space to make an allocation. There is a space_info
|
||||
* for METADATA, SYSTEM, and DATA areas.
|
||||
*
|
||||
* 2) block_rsv's. These are basically buckets for every different type of
|
||||
* metadata reservation we have. You can see the comment in the block_rsv
|
||||
* code on the rules for each type, but generally block_rsv->reserved is how
|
||||
* much space is accounted for in space_info->bytes_may_use.
|
||||
*
|
||||
* 3) btrfs_calc*_size. These are the worst case calculations we used based
|
||||
* on the number of items we will want to modify. We have one for changing
|
||||
* items, and one for inserting new items. Generally we use these helpers to
|
||||
* determine the size of the block reserves, and then use the actual bytes
|
||||
* values to adjust the space_info counters.
|
||||
*
|
||||
* MAKING RESERVATIONS, THE NORMAL CASE
|
||||
*
|
||||
* We call into either btrfs_reserve_data_bytes() or
|
||||
* btrfs_reserve_metadata_bytes(), depending on which we're looking for, with
|
||||
* num_bytes we want to reserve.
|
||||
*
|
||||
* ->reserve
|
||||
* space_info->bytes_may_reserve += num_bytes
|
||||
*
|
||||
* ->extent allocation
|
||||
* Call btrfs_add_reserved_bytes() which does
|
||||
* space_info->bytes_may_reserve -= num_bytes
|
||||
* space_info->bytes_reserved += extent_bytes
|
||||
*
|
||||
* ->insert reference
|
||||
* Call btrfs_update_block_group() which does
|
||||
* space_info->bytes_reserved -= extent_bytes
|
||||
* space_info->bytes_used += extent_bytes
|
||||
*
|
||||
* MAKING RESERVATIONS, FLUSHING NORMALLY (non-priority)
|
||||
*
|
||||
* Assume we are unable to simply make the reservation because we do not have
|
||||
* enough space
|
||||
*
|
||||
* -> __reserve_bytes
|
||||
* create a reserve_ticket with ->bytes set to our reservation, add it to
|
||||
* the tail of space_info->tickets, kick async flush thread
|
||||
*
|
||||
* ->handle_reserve_ticket
|
||||
* wait on ticket->wait for ->bytes to be reduced to 0, or ->error to be set
|
||||
* on the ticket.
|
||||
*
|
||||
* -> btrfs_async_reclaim_metadata_space/btrfs_async_reclaim_data_space
|
||||
* Flushes various things attempting to free up space.
|
||||
*
|
||||
* -> btrfs_try_granting_tickets()
|
||||
* This is called by anything that either subtracts space from
|
||||
* space_info->bytes_may_use, ->bytes_pinned, etc, or adds to the
|
||||
* space_info->total_bytes. This loops through the ->priority_tickets and
|
||||
* then the ->tickets list checking to see if the reservation can be
|
||||
* completed. If it can the space is added to space_info->bytes_may_use and
|
||||
* the ticket is woken up.
|
||||
*
|
||||
* -> ticket wakeup
|
||||
* Check if ->bytes == 0, if it does we got our reservation and we can carry
|
||||
* on, if not return the appropriate error (ENOSPC, but can be EINTR if we
|
||||
* were interrupted.)
|
||||
*
|
||||
* MAKING RESERVATIONS, FLUSHING HIGH PRIORITY
|
||||
*
|
||||
* Same as the above, except we add ourselves to the
|
||||
* space_info->priority_tickets, and we do not use ticket->wait, we simply
|
||||
* call flush_space() ourselves for the states that are safe for us to call
|
||||
* without deadlocking and hope for the best.
|
||||
*
|
||||
* THE FLUSHING STATES
|
||||
*
|
||||
* Generally speaking we will have two cases for each state, a "nice" state
|
||||
* and a "ALL THE THINGS" state. In btrfs we delay a lot of work in order to
|
||||
* reduce the locking over head on the various trees, and even to keep from
|
||||
* doing any work at all in the case of delayed refs. Each of these delayed
|
||||
* things however hold reservations, and so letting them run allows us to
|
||||
* reclaim space so we can make new reservations.
|
||||
*
|
||||
* FLUSH_DELAYED_ITEMS
|
||||
* Every inode has a delayed item to update the inode. Take a simple write
|
||||
* for example, we would update the inode item at write time to update the
|
||||
* mtime, and then again at finish_ordered_io() time in order to update the
|
||||
* isize or bytes. We keep these delayed items to coalesce these operations
|
||||
* into a single operation done on demand. These are an easy way to reclaim
|
||||
* metadata space.
|
||||
*
|
||||
* FLUSH_DELALLOC
|
||||
* Look at the delalloc comment to get an idea of how much space is reserved
|
||||
* for delayed allocation. We can reclaim some of this space simply by
|
||||
* running delalloc, but usually we need to wait for ordered extents to
|
||||
* reclaim the bulk of this space.
|
||||
*
|
||||
* FLUSH_DELAYED_REFS
|
||||
* We have a block reserve for the outstanding delayed refs space, and every
|
||||
* delayed ref operation holds a reservation. Running these is a quick way
|
||||
* to reclaim space, but we want to hold this until the end because COW can
|
||||
* churn a lot and we can avoid making some extent tree modifications if we
|
||||
* are able to delay for as long as possible.
|
||||
*
|
||||
* ALLOC_CHUNK
|
||||
* We will skip this the first time through space reservation, because of
|
||||
* overcommit and we don't want to have a lot of useless metadata space when
|
||||
* our worst case reservations will likely never come true.
|
||||
*
|
||||
* RUN_DELAYED_IPUTS
|
||||
* If we're freeing inodes we're likely freeing checksums, file extent
|
||||
* items, and extent tree items. Loads of space could be freed up by these
|
||||
* operations, however they won't be usable until the transaction commits.
|
||||
*
|
||||
* COMMIT_TRANS
|
||||
* may_commit_transaction() is the ultimate arbiter on whether we commit the
|
||||
* transaction or not. In order to avoid constantly churning we do all the
|
||||
* above flushing first and then commit the transaction as the last resort.
|
||||
* However we need to take into account things like pinned space that would
|
||||
* be freed, plus any delayed work we may not have gotten rid of in the case
|
||||
* of metadata.
|
||||
*
|
||||
* OVERCOMMIT
|
||||
*
|
||||
* Because we hold so many reservations for metadata we will allow you to
|
||||
* reserve more space than is currently free in the currently allocate
|
||||
* metadata space. This only happens with metadata, data does not allow
|
||||
* overcommitting.
|
||||
*
|
||||
* You can see the current logic for when we allow overcommit in
|
||||
* btrfs_can_overcommit(), but it only applies to unallocated space. If there
|
||||
* is no unallocated space to be had, all reservations are kept within the
|
||||
* free space in the allocated metadata chunks.
|
||||
*
|
||||
* Because of overcommitting, you generally want to use the
|
||||
* btrfs_can_overcommit() logic for metadata allocations, as it does the right
|
||||
* thing with or without extra unallocated space.
|
||||
*/
|
||||
|
||||
u64 __pure btrfs_space_info_used(struct btrfs_space_info *s_info,
|
||||
bool may_use_included)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue