fs/btrfs/discard.c

Source file repositories/reference/linux-study-clean/fs/btrfs/discard.c

File Facts

System
Linux kernel
Corpus path
fs/btrfs/discard.c
Extension
.c
Size
25726 bytes
Lines
829
Domain
Core OS
Bucket
VFS And Filesystem Core
Inferred role
Core OS: implementation source
Status
source implementation candidate

Why This File Exists

Core operating-system implementation surface: boot, tasks, memory, VFS, syscall-facing interfaces, synchronization, credentials, and isolation.

Dependency Surface

Detected Declarations

Annotated Snippet

if (!list_empty(discard_list)) {
			block_group = list_first_entry(discard_list,
						       struct btrfs_block_group,
						       discard_list);

			if (!ret_block_group)
				ret_block_group = block_group;

			if (ret_block_group->discard_eligible_time < now)
				break;

			if (ret_block_group->discard_eligible_time >
			    block_group->discard_eligible_time)
				ret_block_group = block_group;
		}
	}

	return ret_block_group;
}

/*
 * Check whether a block group is empty.
 *
 * "Empty" here means that there are no extents physically located within the
 * device extents corresponding to this block group.
 *
 * For a remapped block group, this means that all of its identity remaps have
 * been removed. For a non-remapped block group, this means that no extents
 * have an address within its range, and that nothing has been remapped to be
 * within it.
 */
static bool block_group_is_empty(const struct btrfs_block_group *bg)
{
	if (bg->flags & BTRFS_BLOCK_GROUP_REMAPPED)
		return bg->identity_remap_count == 0;

	return bg->used == 0 && bg->remap_bytes == 0;
}

/*
 * Look up next block group and set it for use.
 *
 * @discard_ctl:   discard control
 * @discard_state: the discard_state of the block_group after state management
 * @discard_index: the discard_index of the block_group after state management
 * @now:           time when discard was invoked, in ns
 *
 * Wrap find_next_block_group() and set the block_group to be in use.
 * @discard_state's control flow is managed here.  Variables related to
 * @discard_state are reset here as needed (eg. @discard_cursor).  @discard_state
 * and @discard_index are remembered as it may change while we're discarding,
 * but we want the discard to execute in the context determined here.
 */
static struct btrfs_block_group *peek_discard_list(
					struct btrfs_discard_ctl *discard_ctl,
					enum btrfs_discard_state *discard_state,
					int *discard_index, u64 now)
{
	struct btrfs_block_group *block_group;

	spin_lock(&discard_ctl->lock);
again:
	block_group = find_next_block_group(discard_ctl, now);

	if (block_group && now >= block_group->discard_eligible_time) {
		const bool empty = block_group_is_empty(block_group);

		if (block_group->discard_index == BTRFS_DISCARD_INDEX_UNUSED &&
		    !empty) {
			if (btrfs_is_block_group_data_only(block_group)) {
				__add_to_discard_list(discard_ctl, block_group);
				/*
				 * The block group must have been moved to other
				 * discard list even if discard was disabled in
				 * the meantime or a transaction abort happened,
				 * otherwise we can end up in an infinite loop,
				 * always jumping into the 'again' label and
				 * keep getting this block group over and over
				 * in case there are no other block groups in
				 * the discard lists.
				 */
				ASSERT(block_group->discard_index !=
				       BTRFS_DISCARD_INDEX_UNUSED,
				       "discard_index=%d",
				       block_group->discard_index);
			} else {
				list_del_init(&block_group->discard_list);
				btrfs_put_block_group(block_group);
			}
			goto again;

Annotation

Implementation Notes