Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f2c6df7d authored by Hannes Reinecke's avatar Hannes Reinecke Committed by Jens Axboe
Browse files

loop: support 4k physical blocksize



When generating bootable VM images certain systems (most notably
s390x) require devices with 4k blocksize. This patch implements
a new flag 'LO_FLAGS_BLOCKSIZE' which will set the physical
blocksize to that of the underlying device, and allow to change
the logical blocksize for up to the physical blocksize.

Signed-off-by: default avatarHannes Reinecke <hare@suse.com>
Signed-off-by: default avatarJens Axboe <axboe@fb.com>
parent 51001b7d
Loading
Loading
Loading
Loading
+37 −6
Original line number Diff line number Diff line
@@ -221,7 +221,8 @@ static void __loop_update_dio(struct loop_device *lo, bool dio)
}

static int
figure_loop_size(struct loop_device *lo, loff_t offset, loff_t sizelimit)
figure_loop_size(struct loop_device *lo, loff_t offset, loff_t sizelimit,
		 loff_t logical_blocksize)
{
	loff_t size = get_size(offset, sizelimit, lo->lo_backing_file);
	sector_t x = (sector_t)size;
@@ -233,6 +234,12 @@ figure_loop_size(struct loop_device *lo, loff_t offset, loff_t sizelimit)
		lo->lo_offset = offset;
	if (lo->lo_sizelimit != sizelimit)
		lo->lo_sizelimit = sizelimit;
	if (lo->lo_flags & LO_FLAGS_BLOCKSIZE) {
		lo->lo_logical_blocksize = logical_blocksize;
		blk_queue_physical_block_size(lo->lo_queue, lo->lo_blocksize);
		blk_queue_logical_block_size(lo->lo_queue,
					     lo->lo_logical_blocksize);
	}
	set_capacity(lo->lo_disk, x);
	bd_set_size(bdev, (loff_t)get_capacity(bdev->bd_disk) << 9);
	/* let user-space know about the new size */
@@ -810,6 +817,7 @@ static void loop_config_discard(struct loop_device *lo)
	struct file *file = lo->lo_backing_file;
	struct inode *inode = file->f_mapping->host;
	struct request_queue *q = lo->lo_queue;
	int lo_bits = 9;

	/*
	 * We use punch hole to reclaim the free space used by the
@@ -829,8 +837,11 @@ static void loop_config_discard(struct loop_device *lo)

	q->limits.discard_granularity = inode->i_sb->s_blocksize;
	q->limits.discard_alignment = 0;
	blk_queue_max_discard_sectors(q, UINT_MAX >> 9);
	blk_queue_max_write_zeroes_sectors(q, UINT_MAX >> 9);
	if (lo->lo_flags & LO_FLAGS_BLOCKSIZE)
		lo_bits = blksize_bits(lo->lo_logical_blocksize);

	blk_queue_max_discard_sectors(q, UINT_MAX >> lo_bits);
	blk_queue_max_write_zeroes_sectors(q, UINT_MAX >> lo_bits);
	queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
}

@@ -918,6 +929,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,

	lo->use_dio = false;
	lo->lo_blocksize = lo_blocksize;
	lo->lo_logical_blocksize = 512;
	lo->lo_device = bdev;
	lo->lo_flags = lo_flags;
	lo->lo_backing_file = file;
@@ -1083,6 +1095,7 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
	int err;
	struct loop_func_table *xfer;
	kuid_t uid = current_uid();
	int lo_flags = lo->lo_flags;

	if (lo->lo_encrypt_key_size &&
	    !uid_eq(lo->lo_key_owner, uid) &&
@@ -1115,9 +1128,26 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
	if (err)
		goto exit;

	if (info->lo_flags & LO_FLAGS_BLOCKSIZE) {
		if (!(lo->lo_flags & LO_FLAGS_BLOCKSIZE))
			lo->lo_logical_blocksize = 512;
		lo->lo_flags |= LO_FLAGS_BLOCKSIZE;
		if (LO_INFO_BLOCKSIZE(info) != 512 &&
		    LO_INFO_BLOCKSIZE(info) != 1024 &&
		    LO_INFO_BLOCKSIZE(info) != 2048 &&
		    LO_INFO_BLOCKSIZE(info) != 4096)
			return -EINVAL;
		if (LO_INFO_BLOCKSIZE(info) > lo->lo_blocksize)
			return -EINVAL;
	}

	if (lo->lo_offset != info->lo_offset ||
	    lo->lo_sizelimit != info->lo_sizelimit)
		if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit)) {
	    lo->lo_sizelimit != info->lo_sizelimit ||
	    lo->lo_flags != lo_flags ||
	    ((lo->lo_flags & LO_FLAGS_BLOCKSIZE) &&
	     lo->lo_logical_blocksize != LO_INFO_BLOCKSIZE(info))) {
		if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit,
				     LO_INFO_BLOCKSIZE(info)))
			err = -EFBIG;
			goto exit;
		}
@@ -1308,7 +1338,8 @@ static int loop_set_capacity(struct loop_device *lo)
	if (unlikely(lo->lo_state != Lo_bound))
		return -ENXIO;

	return figure_loop_size(lo, lo->lo_offset, lo->lo_sizelimit);
	return figure_loop_size(lo, lo->lo_offset, lo->lo_sizelimit,
				lo->lo_logical_blocksize);
}

static int loop_set_dio(struct loop_device *lo, unsigned long arg)
+1 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ struct loop_device {
	struct file *	lo_backing_file;
	struct block_device *lo_device;
	unsigned	lo_blocksize;
	unsigned	lo_logical_blocksize;
	void		*key_data; 

	gfp_t		old_gfp_mask;
+3 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ enum {
	LO_FLAGS_AUTOCLEAR	= 4,
	LO_FLAGS_PARTSCAN	= 8,
	LO_FLAGS_DIRECT_IO	= 16,
	LO_FLAGS_BLOCKSIZE	= 32,
};

#include <asm/posix_types.h>	/* for __kernel_old_dev_t */
@@ -59,6 +60,8 @@ struct loop_info64 {
	__u64		   lo_init[2];
};

#define LO_INFO_BLOCKSIZE(l) (l)->lo_init[0]

/*
 * Loop filter types
 */