Commit 26efd447 authored by Mark Harmstone's avatar Mark Harmstone Committed by David Sterba
Browse files

btrfs: change btrfs_encoded_read() so that reading of extent is done by caller



Change the behaviour of btrfs_encoded_read() so that if it needs to read
an extent from disk, it leaves the extent and inode locked and returns
-EIOCBQUEUED. The caller is then responsible for doing the I/O via
btrfs_encoded_read_regular() and unlocking the extent and inode.

Signed-off-by: default avatarMark Harmstone <maharmstone@fb.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 4bca7412
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -615,7 +615,14 @@ int btrfs_encoded_read_regular_fill_pages(struct btrfs_inode *inode,
					  u64 disk_bytenr, u64 disk_io_size,
					  struct page **pages);
ssize_t btrfs_encoded_read(struct kiocb *iocb, struct iov_iter *iter,
			   struct btrfs_ioctl_encoded_io_args *encoded);
			   struct btrfs_ioctl_encoded_io_args *encoded,
			   struct extent_state **cached_state,
			   u64 *disk_bytenr, u64 *disk_io_size);
ssize_t btrfs_encoded_read_regular(struct kiocb *iocb, struct iov_iter *iter,
				   u64 start, u64 lockend,
				   struct extent_state **cached_state,
				   u64 disk_bytenr, u64 disk_io_size,
				   size_t count, bool compressed, bool *unlocked);
ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
			       const struct btrfs_ioctl_encoded_io_args *encoded);

+27 −30
Original line number Diff line number Diff line
@@ -9122,13 +9122,11 @@ int btrfs_encoded_read_regular_fill_pages(struct btrfs_inode *inode,
	return blk_status_to_errno(READ_ONCE(priv.status));
}

static ssize_t btrfs_encoded_read_regular(struct kiocb *iocb,
					  struct iov_iter *iter,
ssize_t btrfs_encoded_read_regular(struct kiocb *iocb, struct iov_iter *iter,
				   u64 start, u64 lockend,
				   struct extent_state **cached_state,
				   u64 disk_bytenr, u64 disk_io_size,
					  size_t count, bool compressed,
					  bool *unlocked)
				   size_t count, bool compressed, bool *unlocked)
{
	struct btrfs_inode *inode = BTRFS_I(file_inode(iocb->ki_filp));
	struct extent_io_tree *io_tree = &inode->io_tree;
@@ -9189,15 +9187,16 @@ static ssize_t btrfs_encoded_read_regular(struct kiocb *iocb,
}

ssize_t btrfs_encoded_read(struct kiocb *iocb, struct iov_iter *iter,
			   struct btrfs_ioctl_encoded_io_args *encoded)
			   struct btrfs_ioctl_encoded_io_args *encoded,
			   struct extent_state **cached_state,
			   u64 *disk_bytenr, u64 *disk_io_size)
{
	struct btrfs_inode *inode = BTRFS_I(file_inode(iocb->ki_filp));
	struct btrfs_fs_info *fs_info = inode->root->fs_info;
	struct extent_io_tree *io_tree = &inode->io_tree;
	ssize_t ret;
	size_t count = iov_iter_count(iter);
	u64 start, lockend, disk_bytenr, disk_io_size;
	struct extent_state *cached_state = NULL;
	u64 start, lockend;
	struct extent_map *em;
	bool unlocked = false;

@@ -9223,13 +9222,13 @@ ssize_t btrfs_encoded_read(struct kiocb *iocb, struct iov_iter *iter,
					       lockend - start + 1);
		if (ret)
			goto out_unlock_inode;
		lock_extent(io_tree, start, lockend, &cached_state);
		lock_extent(io_tree, start, lockend, cached_state);
		ordered = btrfs_lookup_ordered_range(inode, start,
						     lockend - start + 1);
		if (!ordered)
			break;
		btrfs_put_ordered_extent(ordered);
		unlock_extent(io_tree, start, lockend, &cached_state);
		unlock_extent(io_tree, start, lockend, cached_state);
		cond_resched();
	}

@@ -9249,7 +9248,7 @@ ssize_t btrfs_encoded_read(struct kiocb *iocb, struct iov_iter *iter,
		free_extent_map(em);
		em = NULL;
		ret = btrfs_encoded_read_inline(iocb, iter, start, lockend,
						&cached_state, extent_start,
						cached_state, extent_start,
						count, encoded, &unlocked);
		goto out_em;
	}
@@ -9262,12 +9261,12 @@ ssize_t btrfs_encoded_read(struct kiocb *iocb, struct iov_iter *iter,
			     inode->vfs_inode.i_size) - iocb->ki_pos;
	if (em->disk_bytenr == EXTENT_MAP_HOLE ||
	    (em->flags & EXTENT_FLAG_PREALLOC)) {
		disk_bytenr = EXTENT_MAP_HOLE;
		*disk_bytenr = EXTENT_MAP_HOLE;
		count = min_t(u64, count, encoded->len);
		encoded->len = count;
		encoded->unencoded_len = count;
	} else if (extent_map_is_compressed(em)) {
		disk_bytenr = em->disk_bytenr;
		*disk_bytenr = em->disk_bytenr;
		/*
		 * Bail if the buffer isn't large enough to return the whole
		 * compressed extent.
@@ -9276,7 +9275,7 @@ ssize_t btrfs_encoded_read(struct kiocb *iocb, struct iov_iter *iter,
			ret = -ENOBUFS;
			goto out_em;
		}
		disk_io_size = em->disk_num_bytes;
		*disk_io_size = em->disk_num_bytes;
		count = em->disk_num_bytes;
		encoded->unencoded_len = em->ram_bytes;
		encoded->unencoded_offset = iocb->ki_pos - (em->start - em->offset);
@@ -9286,44 +9285,42 @@ ssize_t btrfs_encoded_read(struct kiocb *iocb, struct iov_iter *iter,
			goto out_em;
		encoded->compression = ret;
	} else {
		disk_bytenr = extent_map_block_start(em) + (start - em->start);
		*disk_bytenr = extent_map_block_start(em) + (start - em->start);
		if (encoded->len > count)
			encoded->len = count;
		/*
		 * Don't read beyond what we locked. This also limits the page
		 * allocations that we'll do.
		 */
		disk_io_size = min(lockend + 1, iocb->ki_pos + encoded->len) - start;
		count = start + disk_io_size - iocb->ki_pos;
		*disk_io_size = min(lockend + 1, iocb->ki_pos + encoded->len) - start;
		count = start + *disk_io_size - iocb->ki_pos;
		encoded->len = count;
		encoded->unencoded_len = count;
		disk_io_size = ALIGN(disk_io_size, fs_info->sectorsize);
		*disk_io_size = ALIGN(*disk_io_size, fs_info->sectorsize);
	}
	free_extent_map(em);
	em = NULL;

	if (disk_bytenr == EXTENT_MAP_HOLE) {
		unlock_extent(io_tree, start, lockend, &cached_state);
	if (*disk_bytenr == EXTENT_MAP_HOLE) {
		unlock_extent(io_tree, start, lockend, cached_state);
		btrfs_inode_unlock(inode, BTRFS_ILOCK_SHARED);
		unlocked = true;
		ret = iov_iter_zero(count, iter);
		if (ret != count)
			ret = -EFAULT;
	} else {
		ret = btrfs_encoded_read_regular(iocb, iter, start, lockend,
						 &cached_state, disk_bytenr,
						 disk_io_size, count,
						 encoded->compression,
						 &unlocked);
		ret = -EIOCBQUEUED;
		goto out_em;
	}

out_em:
	free_extent_map(em);
out_unlock_extent:
	if (!unlocked)
		unlock_extent(io_tree, start, lockend, &cached_state);
	/* Leave inode and extent locked if we need to do a read. */
	if (!unlocked && ret != -EIOCBQUEUED)
		unlock_extent(io_tree, start, lockend, cached_state);
out_unlock_inode:
	if (!unlocked)
	if (!unlocked && ret != -EIOCBQUEUED)
		btrfs_inode_unlock(inode, BTRFS_ILOCK_SHARED);
	return ret;
}
+31 −1
Original line number Diff line number Diff line
@@ -4513,12 +4513,17 @@ static int btrfs_ioctl_encoded_read(struct file *file, void __user *argp,
	size_t copy_end_kernel = offsetofend(struct btrfs_ioctl_encoded_io_args,
					     flags);
	size_t copy_end;
	struct btrfs_inode *inode = BTRFS_I(file_inode(file));
	struct btrfs_fs_info *fs_info = inode->root->fs_info;
	struct extent_io_tree *io_tree = &inode->io_tree;
	struct iovec iovstack[UIO_FASTIOV];
	struct iovec *iov = iovstack;
	struct iov_iter iter;
	loff_t pos;
	struct kiocb kiocb;
	ssize_t ret;
	u64 disk_bytenr, disk_io_size;
	struct extent_state *cached_state = NULL;

	if (!capable(CAP_SYS_ADMIN)) {
		ret = -EPERM;
@@ -4571,7 +4576,32 @@ static int btrfs_ioctl_encoded_read(struct file *file, void __user *argp,
	init_sync_kiocb(&kiocb, file);
	kiocb.ki_pos = pos;

	ret = btrfs_encoded_read(&kiocb, &iter, &args);
	ret = btrfs_encoded_read(&kiocb, &iter, &args, &cached_state,
				 &disk_bytenr, &disk_io_size);

	if (ret == -EIOCBQUEUED) {
		bool unlocked = false;
		u64 start, lockend, count;

		start = ALIGN_DOWN(kiocb.ki_pos, fs_info->sectorsize);
		lockend = start + BTRFS_MAX_UNCOMPRESSED - 1;

		if (args.compression)
			count = disk_io_size;
		else
			count = args.len;

		ret = btrfs_encoded_read_regular(&kiocb, &iter, start, lockend,
						 &cached_state, disk_bytenr,
						 disk_io_size, count,
						 args.compression, &unlocked);

		if (!unlocked) {
			unlock_extent(io_tree, start, lockend, &cached_state);
			btrfs_inode_unlock(inode, BTRFS_ILOCK_SHARED);
		}
	}

	if (ret >= 0) {
		fsnotify_access(file);
		if (copy_to_user(argp + copy_end,