fs/btrfs/fiemap.c

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

File Facts

System
Linux kernel
Corpus path
fs/btrfs/fiemap.c
Extension
.c
Size
28962 bytes
Lines
929
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

struct btrfs_fiemap_entry {
	u64 offset;
	u64 phys;
	u64 len;
	u32 flags;
};

/*
 * Indicate the caller of emit_fiemap_extent() that it needs to unlock the file
 * range from the inode's io tree, unlock the subvolume tree search path, flush
 * the fiemap cache and relock the file range and research the subvolume tree.
 * The value here is something negative that can't be confused with a valid
 * errno value and different from 1 because that's also a return value from
 * fiemap_fill_next_extent() and also it's often used to mean some btree search
 * did not find a key, so make it some distinct negative value.
 */
#define BTRFS_FIEMAP_FLUSH_CACHE (-(MAX_ERRNO + 1))

/*
 * Used to:
 *
 * - Cache the next entry to be emitted to the fiemap buffer, so that we can
 *   merge extents that are contiguous and can be grouped as a single one;
 *
 * - Store extents ready to be written to the fiemap buffer in an intermediary
 *   buffer. This intermediary buffer is to ensure that in case the fiemap
 *   buffer is memory mapped to the fiemap target file, we don't deadlock
 *   during btrfs_page_mkwrite(). This is because during fiemap we are locking
 *   an extent range in order to prevent races with delalloc flushing and
 *   ordered extent completion, which is needed in order to reliably detect
 *   delalloc in holes and prealloc extents. And this can lead to a deadlock
 *   if the fiemap buffer is memory mapped to the file we are running fiemap
 *   against (a silly, useless in practice scenario, but possible) because
 *   btrfs_page_mkwrite() will try to lock the same extent range.
 */
struct fiemap_cache {
	/* An array of ready fiemap entries. */
	struct btrfs_fiemap_entry *entries;
	/* Number of entries in the entries array. */
	int entries_size;
	/* Index of the next entry in the entries array to write to. */
	int entries_pos;
	/*
	 * Once the entries array is full, this indicates what's the offset for
	 * the next file extent item we must search for in the inode's subvolume
	 * tree after unlocking the extent range in the inode's io tree and
	 * releasing the search path.
	 */
	u64 next_search_offset;
	/*
	 * This matches struct fiemap_extent_info::fi_mapped_extents, we use it
	 * to count ourselves emitted extents and stop instead of relying on
	 * fiemap_fill_next_extent() because we buffer ready fiemap entries at
	 * the @entries array, and we want to stop as soon as we hit the max
	 * amount of extents to map, not just to save time but also to make the
	 * logic at extent_fiemap() simpler.
	 */
	unsigned int extents_mapped;
	/* Fields for the cached extent (unsubmitted, not ready, extent). */
	u64 offset;
	u64 phys;
	u64 len;
	u32 flags;
	bool cached;
};

static int flush_fiemap_cache(struct fiemap_extent_info *fieinfo,
			      struct fiemap_cache *cache)
{
	for (int i = 0; i < cache->entries_pos; i++) {
		struct btrfs_fiemap_entry *entry = &cache->entries[i];
		int ret;

		ret = fiemap_fill_next_extent(fieinfo, entry->offset,
					      entry->phys, entry->len,
					      entry->flags);
		/*
		 * Ignore 1 (reached max entries) because we keep track of that
		 * ourselves in emit_fiemap_extent().
		 */
		if (ret < 0)
			return ret;
	}
	cache->entries_pos = 0;

	return 0;
}

/*
 * Helper to submit fiemap extent.

Annotation

Implementation Notes