Skip to content

Commit

Permalink
loop: support 4k physical blocksize
Browse files Browse the repository at this point in the history
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: Hannes Reinecke <hare@suse.com>
Signed-off-by: Jens Axboe <axboe@fb.com>
  • Loading branch information
Hannes Reinecke authored and Jens Axboe committed Jun 8, 2017
1 parent 51001b7 commit f2c6df7
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 6 deletions.
43 changes: 37 additions & 6 deletions drivers/block/loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 */
Expand Down Expand Up @@ -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
Expand All @@ -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);
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) &&
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions drivers/block/loop.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions include/uapi/linux/loop.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -59,6 +60,8 @@ struct loop_info64 {
__u64 lo_init[2];
};

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

/*
* Loop filter types
*/
Expand Down

0 comments on commit f2c6df7

Please sign in to comment.