fs/btrfs/extent_map.c

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

File Facts

System
Linux kernel
Corpus path
fs/btrfs/extent_map.c
Extension
.c
Size
40448 bytes
Lines
1397
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_em_shrink_ctx {
	long nr_to_scan;
	long scanned;
};

static long btrfs_scan_inode(struct btrfs_inode *inode, struct btrfs_em_shrink_ctx *ctx)
{
	struct btrfs_fs_info *fs_info = inode->root->fs_info;
	const u64 cur_fs_gen = btrfs_get_fs_generation(fs_info);
	struct extent_map_tree *tree = &inode->extent_tree;
	long nr_dropped = 0;
	struct rb_node *node;

	lockdep_assert_held_write(&tree->lock);

	/*
	 * Take the mmap lock so that we serialize with the inode logging phase
	 * of fsync because we may need to set the full sync flag on the inode,
	 * in case we have to remove extent maps in the tree's list of modified
	 * extents. If we set the full sync flag in the inode while an fsync is
	 * in progress, we may risk missing new extents because before the flag
	 * is set, fsync decides to only wait for writeback to complete and then
	 * during inode logging it sees the flag set and uses the subvolume tree
	 * to find new extents, which may not be there yet because ordered
	 * extents haven't completed yet.
	 *
	 * We also do a try lock because we don't want to block for too long and
	 * we are holding the extent map tree's lock in write mode.
	 */
	if (!down_read_trylock(&inode->i_mmap_lock))
		return 0;

	node = rb_first(&tree->root);
	while (node) {
		struct rb_node *next = rb_next(node);
		struct extent_map *em;

		em = rb_entry(node, struct extent_map, rb_node);
		ctx->scanned++;

		if (em->flags & EXTENT_FLAG_PINNED)
			goto next;

		/*
		 * If the inode is in the list of modified extents (new) and its
		 * generation is the same (or is greater than) the current fs
		 * generation, it means it was not yet persisted so we have to
		 * set the full sync flag so that the next fsync will not miss
		 * it.
		 */
		if (!list_empty(&em->list) && em->generation >= cur_fs_gen)
			btrfs_set_inode_full_sync(inode);

		btrfs_remove_extent_mapping(inode, em);
		trace_btrfs_extent_map_shrinker_remove_em(inode, em);
		/* Drop the reference for the tree. */
		btrfs_free_extent_map(em);
		nr_dropped++;
next:
		if (ctx->scanned >= ctx->nr_to_scan)
			break;

		/*
		 * Stop if we need to reschedule or there's contention on the
		 * lock. This is to avoid slowing other tasks trying to take the
		 * lock.
		 */
		if (need_resched() || rwlock_needbreak(&tree->lock) ||
		    btrfs_fs_closing(fs_info))
			break;
		node = next;
	}
	up_read(&inode->i_mmap_lock);

	return nr_dropped;
}

static struct btrfs_inode *find_first_inode_to_shrink(struct btrfs_root *root,
						      u64 min_ino)
{
	struct btrfs_inode *inode;
	unsigned long from = min_ino;

	xa_lock(&root->inodes);
	while (true) {
		struct extent_map_tree *tree;

		inode = xa_find(&root->inodes, &from, ULONG_MAX, XA_PRESENT);
		if (!inode)
			break;

Annotation

Implementation Notes