Quelle delalloc-space.c
Sprache: C
// SPDX-License-Identifier: GPL-2.0
#include "messages.h"
#include "ctree.h"
#include "delalloc-space.h"
#include "block-rsv.h"
#include "btrfs_inode.h"
#include "space-info.h"
#include "qgroup.h"
#include "fs.h"
/*
* HOW DOES THIS WORK
*
* There are two stages to data reservations , one for data and one for metadata
* to handle the new extents and checksums generated by writing data .
*
*
* DATA RESERVATION
* The general flow of the data reservation is as follows
*
* - > Reserve
* We call into btrfs_reserve_data_bytes ( ) for the user request bytes that
* they wish to write . We make this reservation and add it to
* space_info - > bytes_may_use . We set EXTENT_DELALLOC on the inode io_tree
* for the range and carry on if this is buffered , or follow up trying to
* make a real allocation if we are pre - allocating or doing O_DIRECT .
*
* - > Use
* At writepages ( ) / prealloc / O_DIRECT time we will call into
* btrfs_reserve_extent ( ) for some part or all of this range of bytes . We
* will make the allocation and subtract space_info - > bytes_may_use by the
* original requested length and increase the space_info - > bytes_reserved by
* the allocated length . This distinction is important because compression
* may allocate a smaller on disk extent than we previously reserved .
*
* - > Allocation
* finish_ordered_io ( ) will insert the new file extent item for this range ,
* and then add a delayed ref update for the extent tree . Once that delayed
* ref is written the extent size is subtracted from
* space_info - > bytes_reserved and added to space_info - > bytes_used .
*
* Error handling
*
* - > By the reservation maker
* This is the simplest case , we haven ' t completed our operation and we know
* how much we reserved , we can simply call
* btrfs_free_reserved_data_space * ( ) and it will be removed from
* space_info - > bytes_may_use .
*
* - > After the reservation has been made , but before cow_file_range ( )
* This is specifically for the delalloc case . You must clear
* EXTENT_DELALLOC with the EXTENT_CLEAR_DATA_RESV bit , and the range will
* be subtracted from space_info - > bytes_may_use .
*
* METADATA RESERVATION
* The general metadata reservation lifetimes are discussed elsewhere , this
* will just focus on how it is used for delalloc space .
*
* We keep track of two things on a per inode bases
*
* - > outstanding_extents
* This is the number of file extent items we ' ll need to handle all of the
* outstanding DELALLOC space we have in this inode . We limit the maximum
* size of an extent , so a large contiguous dirty area may require more than
* one outstanding_extent , which is why count_max_extents ( ) is used to
* determine how many outstanding_extents get added .
*
* - > csum_bytes
* This is essentially how many dirty bytes we have for this inode , so we
* can calculate the number of checksum items we would have to add in order
* to checksum our outstanding data .
*
* We keep a per - inode block_rsv in order to make it easier to keep track of
* our reservation . We use btrfs_calculate_inode_block_rsv_size ( ) to
* calculate the current theoretical maximum reservation we would need for the
* metadata for this inode . We call this and then adjust our reservation as
* necessary , either by attempting to reserve more space , or freeing up excess
* space .
*
* OUTSTANDING_EXTENTS HANDLING
*
* - > outstanding_extents is used for keeping track of how many extents we will
* need to use for this inode , and it will fluctuate depending on where you are
* in the life cycle of the dirty data . Consider the following normal case for
* a completely clean inode , with a num_bytes < our maximum allowed extent size
*
* - > reserve
* - > outstanding_extents + = 1 ( current value is 1 )
*
* - > set_delalloc
* - > outstanding_extents + = 1 ( current value is 2 )
*
* - > btrfs_delalloc_release_extents ( )
* - > outstanding_extents - = 1 ( current value is 1 )
*
* We must call this once we are done , as we hold our reservation for the
* duration of our operation , and then assume set_delalloc will update the
* counter appropriately .
*
* - > add ordered extent
* - > outstanding_extents + = 1 ( current value is 2 )
*
* - > btrfs_clear_delalloc_extent
* - > outstanding_extents - = 1 ( current value is 1 )
*
* - > finish_ordered_io / btrfs_remove_ordered_extent
* - > outstanding_extents - = 1 ( current value is 0 )
*
* Each stage is responsible for their own accounting of the extent , thus
* making error handling and cleanup easier .
*/
static inline struct btrfs_space_info *data_sinfo_for_inode(const struct btrfs_inode *inode)
{
struct btrfs_fs_info *fs_info = inode->root->fs_info;
if (btrfs_is_zoned(fs_info) && btrfs_is_data_reloc_root(inode->root)) {
ASSERT(fs_info->data_sinfo->sub_group[0 ]->subgroup_id ==
BTRFS_SUB_GROUP_DATA_RELOC);
return fs_info->data_sinfo->sub_group[0 ];
}
return fs_info->data_sinfo;
}
int btrfs_alloc_data_chunk_ondemand(const struct btrfs_inode *inode, u64 bytes)
{
struct btrfs_root *root = inode->root;
struct btrfs_fs_info *fs_info = root->fs_info;
enum btrfs_reserve_flush_enum flush = BTRFS_RESERVE_FLUSH_DATA;
/* Make sure bytes are sectorsize aligned */
bytes = ALIGN(bytes, fs_info->sectorsize);
if (btrfs_is_free_space_inode(inode))
flush = BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE;
return btrfs_reserve_data_bytes(data_sinfo_for_inode(inode), bytes, flush);
}
int btrfs_check_data_free_space(struct btrfs_inode *inode,
struct extent_changeset **reserved, u64 start,
u64 len, bool noflush)
{
struct btrfs_fs_info *fs_info = inode->root->fs_info;
enum btrfs_reserve_flush_enum flush = BTRFS_RESERVE_FLUSH_DATA;
int ret;
/* align the range */
len = round_up(start + len, fs_info->sectorsize) -
round_down(start, fs_info->sectorsize);
start = round_down(start, fs_info->sectorsize);
if (noflush)
flush = BTRFS_RESERVE_NO_FLUSH;
else if (btrfs_is_free_space_inode(inode))
flush = BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE;
ret = btrfs_reserve_data_bytes(data_sinfo_for_inode(inode), len, flush);
if (ret < 0 )
return ret;
/* Use new btrfs_qgroup_reserve_data to reserve precious data space. */
ret = btrfs_qgroup_reserve_data(inode, reserved, start, len);
if (ret < 0 ) {
btrfs_free_reserved_data_space_noquota(inode, len);
extent_changeset_free(*reserved);
*reserved = NULL;
} else {
ret = 0 ;
}
return ret;
}
/*
* Called if we need to clear a data reservation for this inode
* Normally in a error case .
*
* This one will * NOT * use accurate qgroup reserved space API , just for case
* which we can ' t sleep and is sure it won ' t affect qgroup reserved space .
* Like clear_bit_hook ( ) .
*/
void btrfs_free_reserved_data_space_noquota(struct btrfs_inode *inode, u64 len)
{
struct btrfs_fs_info *fs_info = inode->root->fs_info;
ASSERT(IS_ALIGNED(len, fs_info->sectorsize));
btrfs_space_info_free_bytes_may_use(data_sinfo_for_inode(inode), len);
}
/*
* Called if we need to clear a data reservation for this inode
* Normally in a error case .
*
* This one will handle the per - inode data rsv map for accurate reserved
* space framework .
*/
void btrfs_free_reserved_data_space(struct btrfs_inode *inode,
struct extent_changeset *reserved, u64 start, u64 len)
{
struct btrfs_fs_info *fs_info = inode->root->fs_info;
/* Make sure the range is aligned to sectorsize */
len = round_up(start + len, fs_info->sectorsize) -
round_down(start, fs_info->sectorsize);
start = round_down(start, fs_info->sectorsize);
btrfs_free_reserved_data_space_noquota(inode, len);
btrfs_qgroup_free_data(inode, reserved, start, len, NULL);
}
/*
* Release any excessive reservations for an inode .
*
* @ inode : the inode we need to release from
* @ qgroup_free : free or convert qgroup meta . Unlike normal operation , qgroup
* meta reservation needs to know if we are freeing qgroup
* reservation or just converting it into per - trans . Normally
* @ qgroup_free is true for error handling , and false for normal
* release .
*
* This is the same as btrfs_block_rsv_release , except that it handles the
* tracepoint for the reservation .
*/
static void btrfs_inode_rsv_release(struct btrfs_inode *inode, bool qgroup_free)
{
struct btrfs_fs_info *fs_info = inode->root->fs_info;
struct btrfs_block_rsv *block_rsv = &inode->block_rsv;
u64 released = 0 ;
u64 qgroup_to_release = 0 ;
/*
* Since we statically set the block_rsv - > size we just want to say we
* are releasing 0 bytes , and then we ' ll just get the reservation over
* the size free ' d .
*/
released = btrfs_block_rsv_release(fs_info, block_rsv, 0 ,
&qgroup_to_release);
if (released > 0 )
trace_btrfs_space_reservation(fs_info, "delalloc" ,
btrfs_ino(inode), released, 0 );
if (qgroup_free)
btrfs_qgroup_free_meta_prealloc(inode->root, qgroup_to_release);
else
btrfs_qgroup_convert_reserved_meta(inode->root,
qgroup_to_release);
}
static void btrfs_calculate_inode_block_rsv_size(struct btrfs_fs_info *fs_info,
struct btrfs_inode *inode)
{
struct btrfs_block_rsv *block_rsv = &inode->block_rsv;
u64 reserve_size = 0 ;
u64 qgroup_rsv_size = 0 ;
unsigned outstanding_extents;
lockdep_assert_held(&inode->lock);
outstanding_extents = inode->outstanding_extents;
/*
* Insert size for the number of outstanding extents , 1 normal size for
* updating the inode .
*/
if (outstanding_extents) {
reserve_size = btrfs_calc_insert_metadata_size(fs_info,
outstanding_extents);
reserve_size += btrfs_calc_metadata_size(fs_info, 1 );
}
if (!(inode->flags & BTRFS_INODE_NODATASUM)) {
u64 csum_leaves;
csum_leaves = btrfs_csum_bytes_to_leaves(fs_info, inode->csum_bytes);
reserve_size += btrfs_calc_insert_metadata_size(fs_info, csum_leaves);
}
/*
* For qgroup rsv , the calculation is very simple :
* account one nodesize for each outstanding extent
*
* This is overestimating in most cases .
*/
qgroup_rsv_size = (u64)outstanding_extents * fs_info->nodesize;
spin_lock(&block_rsv->lock);
block_rsv->size = reserve_size;
block_rsv->qgroup_rsv_size = qgroup_rsv_size;
spin_unlock(&block_rsv->lock);
}
static void calc_inode_reservations(struct btrfs_inode *inode,
u64 num_bytes, u64 disk_num_bytes,
u64 *meta_reserve, u64 *qgroup_reserve)
{
struct btrfs_fs_info *fs_info = inode->root->fs_info;
u64 nr_extents = count_max_extents(fs_info, num_bytes);
u64 csum_leaves;
u64 inode_update = btrfs_calc_metadata_size(fs_info, 1 );
if (inode->flags & BTRFS_INODE_NODATASUM)
csum_leaves = 0 ;
else
csum_leaves = btrfs_csum_bytes_to_leaves(fs_info, disk_num_bytes);
*meta_reserve = btrfs_calc_insert_metadata_size(fs_info,
nr_extents + csum_leaves);
/*
* finish_ordered_io has to update the inode , so add the space required
* for an inode update .
*/
*meta_reserve += inode_update;
*qgroup_reserve = nr_extents * fs_info->nodesize;
}
int btrfs_delalloc_reserve_metadata(struct btrfs_inode *inode, u64 num_bytes,
u64 disk_num_bytes, bool noflush)
{
struct btrfs_root *root = inode->root;
struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_block_rsv *block_rsv = &inode->block_rsv;
u64 meta_reserve, qgroup_reserve;
unsigned nr_extents;
enum btrfs_reserve_flush_enum flush = BTRFS_RESERVE_FLUSH_ALL;
int ret = 0 ;
/*
* If we are a free space inode we need to not flush since we will be in
* the middle of a transaction commit . We also don ' t need the delalloc
* mutex since we won ' t race with anybody . We need this mostly to make
* lockdep shut its filthy mouth .
*
* If we have a transaction open ( can happen if we call truncate_block
* from truncate ) , then we need FLUSH_LIMIT so we don ' t deadlock .
*/
if (noflush || btrfs_is_free_space_inode(inode)) {
flush = BTRFS_RESERVE_NO_FLUSH;
} else {
if (current->journal_info)
flush = BTRFS_RESERVE_FLUSH_LIMIT;
}
num_bytes = ALIGN(num_bytes, fs_info->sectorsize);
disk_num_bytes = ALIGN(disk_num_bytes, fs_info->sectorsize);
/*
* We always want to do it this way , every other way is wrong and ends
* in tears . Pre - reserving the amount we are going to add will always
* be the right way , because otherwise if we have enough parallelism we
* could end up with thousands of inodes all holding little bits of
* reservations they were able to make previously and the only way to
* reclaim that space is to ENOSPC out the operations and clear
* everything out and try again , which is bad . This way we just
* over - reserve slightly , and clean up the mess when we are done .
*/
calc_inode_reservations(inode, num_bytes, disk_num_bytes,
&meta_reserve, &qgroup_reserve);
ret = btrfs_qgroup_reserve_meta_prealloc(root, qgroup_reserve, true ,
noflush);
if (ret)
return ret;
ret = btrfs_reserve_metadata_bytes(fs_info, block_rsv->space_info,
meta_reserve, flush);
if (ret) {
btrfs_qgroup_free_meta_prealloc(root, qgroup_reserve);
return ret;
}
/*
* Now we need to update our outstanding extents and csum bytes _ first_
* and then add the reservation to the block_rsv . This keeps us from
* racing with an ordered completion or some such that would think it
* needs to free the reservation we just made .
*/
nr_extents = count_max_extents(fs_info, num_bytes);
spin_lock(&inode->lock);
btrfs_mod_outstanding_extents(inode, nr_extents);
if (!(inode->flags & BTRFS_INODE_NODATASUM))
inode->csum_bytes += disk_num_bytes;
btrfs_calculate_inode_block_rsv_size(fs_info, inode);
spin_unlock(&inode->lock);
/* Now we can safely add our space to our block rsv */
btrfs_block_rsv_add_bytes(block_rsv, meta_reserve, false );
trace_btrfs_space_reservation(root->fs_info, "delalloc" ,
btrfs_ino(inode), meta_reserve, 1 );
spin_lock(&block_rsv->lock);
block_rsv->qgroup_rsv_reserved += qgroup_reserve;
spin_unlock(&block_rsv->lock);
return 0 ;
}
/*
* Release a metadata reservation for an inode .
*
* @ inode : the inode to release the reservation for .
* @ num_bytes : the number of bytes we are releasing .
* @ qgroup_free : free qgroup reservation or convert it to per - trans reservation
*
* This will release the metadata reservation for an inode . This can be called
* once we complete IO for a given set of bytes to release their metadata
* reservations , or on error for the same reason .
*/
void btrfs_delalloc_release_metadata(struct btrfs_inode *inode, u64 num_bytes,
bool qgroup_free)
{
struct btrfs_fs_info *fs_info = inode->root->fs_info;
num_bytes = ALIGN(num_bytes, fs_info->sectorsize);
spin_lock(&inode->lock);
if (!(inode->flags & BTRFS_INODE_NODATASUM))
inode->csum_bytes -= num_bytes;
btrfs_calculate_inode_block_rsv_size(fs_info, inode);
spin_unlock(&inode->lock);
if (btrfs_is_testing(fs_info))
return ;
btrfs_inode_rsv_release(inode, qgroup_free);
}
/*
* Release our outstanding_extents for an inode .
*
* @ inode : the inode to balance the reservation for .
* @ num_bytes : the number of bytes we originally reserved with
*
* When we reserve space we increase outstanding_extents for the extents we may
* add . Once we ' ve set the range as delalloc or created our ordered extents we
* have outstanding_extents to track the real usage , so we use this to free our
* temporarily tracked outstanding_extents . This _ must_ be used in conjunction
* with btrfs_delalloc_reserve_metadata .
*/
void btrfs_delalloc_release_extents(struct btrfs_inode *inode, u64 num_bytes)
{
struct btrfs_fs_info *fs_info = inode->root->fs_info;
unsigned num_extents;
spin_lock(&inode->lock);
num_extents = count_max_extents(fs_info, num_bytes);
btrfs_mod_outstanding_extents(inode, -num_extents);
btrfs_calculate_inode_block_rsv_size(fs_info, inode);
spin_unlock(&inode->lock);
if (btrfs_is_testing(fs_info))
return ;
btrfs_inode_rsv_release(inode, true );
}
/* Shrink a previously reserved extent to a new length. */
void btrfs_delalloc_shrink_extents(struct btrfs_inode *inode, u64 reserved_len, u64 new_len)
{
struct btrfs_fs_info *fs_info = inode->root->fs_info;
const u32 reserved_num_extents = count_max_extents(fs_info, reserved_len);
const u32 new_num_extents = count_max_extents(fs_info, new_len);
const int diff_num_extents = new_num_extents - reserved_num_extents;
ASSERT(new_len <= reserved_len);
if (new_num_extents == reserved_num_extents)
return ;
spin_lock(&inode->lock);
btrfs_mod_outstanding_extents(inode, diff_num_extents);
btrfs_calculate_inode_block_rsv_size(fs_info, inode);
spin_unlock(&inode->lock);
if (btrfs_is_testing(fs_info))
return ;
btrfs_inode_rsv_release(inode, true );
}
/*
* Reserve data and metadata space for delalloc
*
* @ inode : inode we ' re writing to
* @ start : start range we are writing to
* @ len : how long the range we are writing to
* @ reserved : mandatory parameter , record actually reserved qgroup ranges of
* current reservation .
*
* This will do the following things
*
* - reserve space in data space info for num bytes and reserve precious
* corresponding qgroup space
* ( Done in check_data_free_space )
*
* - reserve space for metadata space , based on the number of outstanding
* extents and how much csums will be needed also reserve metadata space in a
* per root over - reserve method .
* - add to the inodes - > delalloc_bytes
* - add it to the fs_info ' s delalloc inodes list .
* ( Above 3 all done in delalloc_reserve_metadata )
*
* Return 0 for success
* Return < 0 for error ( - ENOSPC or - EDQUOT )
*/
int btrfs_delalloc_reserve_space(struct btrfs_inode *inode,
struct extent_changeset **reserved, u64 start, u64 len)
{
int ret;
ret = btrfs_check_data_free_space(inode, reserved, start, len, false );
if (ret < 0 )
return ret;
ret = btrfs_delalloc_reserve_metadata(inode, len, len, false );
if (ret < 0 ) {
btrfs_free_reserved_data_space(inode, *reserved, start, len);
extent_changeset_free(*reserved);
*reserved = NULL;
}
return ret;
}
/*
* Release data and metadata space for delalloc
*
* @ inode : inode we ' re releasing space for
* @ reserved : list of changed / reserved ranges
* @ start : start position of the space already reserved
* @ len : length of the space already reserved
* @ qgroup_free : should qgroup reserved - space also be freed
*
* Release the metadata space that was not used and will decrement
* - > delalloc_bytes and remove it from the fs_info - > delalloc_inodes list if
* there are no delalloc bytes left . Also it will handle the qgroup reserved
* space .
*/
void btrfs_delalloc_release_space(struct btrfs_inode *inode,
struct extent_changeset *reserved,
u64 start, u64 len, bool qgroup_free)
{
btrfs_delalloc_release_metadata(inode, len, qgroup_free);
btrfs_free_reserved_data_space(inode, reserved, start, len);
}
Messung V0.5 in Prozent C=97 H=100 G=98
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland
2026-10-09
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126438
#Domains=1867298