Skip to contentSkip to conversation

loop: support different logical block sizes

PATCH In review Omar Sandoval 5 participants 13 messages Aug 24, 2017 – Aug 31, 2017 Block layer

lore
Share & tools
lore
#0 lore raw

cover loop: support different logical block sizes

From: Omar Sandoval <[email protected]>

Hi, everyone,

Here's another attempt at supporting different logical block sizes for loop devices. First, some terminology:

* Logical block size: the lowest possible block size that the storage device can address.
* Physical block size: the lowest possible sector size that the hardware can operate on without reverting to read-modify-write operations. Always greater than or equal to the logical block size.

These are characteristics of the device itself tracked in the request_queue. For loop devices, both are currently always 512 bytes.

struct block_device also has another block size, basically a "soft" block size which is the preferred I/O size and can be changed from userspace. For loop devices, this is PAGE_SIZE if the backing file is a regular file or otherwise the soft block size of the backing block device.

Patch 1 simplifies how the soft block size is set. I'm tempted to not set it at all and use the default of PAGE_SIZE, but maybe someone is depending on inheriting the soft block size from the backing block device...

Patch 2 sets the physical block size of loop devices to a more meaningful value for loop, PAGE_SIZE.

Patch 3 allows us to change the logical block size. It adds a new ioctl instead of the previous approach (which extends LOOP_{GET,SET}_STATUS). Hannes, I assume you had a specific reason for doing it the way you did, care to explain?

Patch 4 is a cleanup.

This is based on my patch reverting the original block size support, targeting 4.14. Thanks!

Omar Sandoval (4):
loop: get rid of lo_blocksize
loop: set physical block size to PAGE_SIZE
loop: add ioctl for changing logical block size
loop: fold loop_switch() into callers

 drivers/block/loop.c      | 112 ++++++++++++++++------------------------------
 drivers/block/loop.h      |   1 -
 include/uapi/linux/loop.h |   1 +
 3 files changed, 40 insertions(+), 74 deletions(-)
Signature · 1 line
2.14.1
#1 lore raw

PATCH 1/4 loop: get rid of lo_blocksize

From: Omar Sandoval <[email protected]>

This is only used for setting the soft block size on the struct block_device once and then never used again.

---
 drivers/block/loop.c | 10 ++--------
 drivers/block/loop.h |  1 -
 2 files changed, 2 insertions(+), 9 deletions(-)
drivers/block/loop.c +2 -8
index f321b96405f5..54e091887199 100644
@@ -573,8 +573,6 @@ static void do_loop_switch(struct loop_device *lo, struct switch_request *p)
 	mapping = file->f_mapping;
 	mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask);
 	lo->lo_backing_file = file;
-	lo->lo_blocksize = S_ISBLK(mapping->host->i_mode) ?
-		mapping->host->i_bdev->bd_block_size : PAGE_SIZE;
 	lo->old_gfp_mask = mapping_gfp_mask(mapping);
 	mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS));
 	loop_update_dio(lo);
@@ -867,7 +865,6 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
 	struct file	*file, *f;
 	struct inode	*inode;
 	struct address_space *mapping;
-	unsigned lo_blocksize;
 	int		lo_flags = 0;
 	int		error;
 	loff_t		size;
@@ -911,9 +908,6 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
 	    !file->f_op->write_iter)
 		lo_flags |= LO_FLAGS_READ_ONLY;
 
-	lo_blocksize = S_ISBLK(inode->i_mode) ?
-		inode->i_bdev->bd_block_size : PAGE_SIZE;
-
 	error = -EFBIG;
 	size = get_loop_size(lo, file);
 	if ((loff_t)(sector_t)size != size)
@@ -927,7 +921,6 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
 	set_device_ro(bdev, (lo_flags & LO_FLAGS_READ_ONLY) != 0);
 
 	lo->use_dio = false;
-	lo->lo_blocksize = lo_blocksize;
 	lo->lo_device = bdev;
 	lo->lo_flags = lo_flags;
 	lo->lo_backing_file = file;
@@ -947,7 +940,8 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
 	/* let user-space know about the new size */
 	kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE);
 
-	set_blocksize(bdev, lo_blocksize);
+	set_blocksize(bdev, S_ISBLK(inode->i_mode) ?
+		      block_size(inode->i_bdev) : PAGE_SIZE);
 
 	lo->lo_state = Lo_bound;
 	if (part_shift)
drivers/block/loop.h +0 -1
index fecd3f97ef8c..efe57189d01e 100644
@@ -48,7 +48,6 @@ struct loop_device {
 
 	struct file *	lo_backing_file;
 	struct block_device *lo_device;
-	unsigned	lo_blocksize;
 	void		*key_data; 
 
 	gfp_t		old_gfp_mask;
Signature · 1 line
2.14.1
Reviewed-by on v1 1/4
#2 lore raw

Re: [PATCH 1/4] loop: get rid of lo_blocksize

Quoted 11 lines · Omar Sandoval

From: Omar Sandoval <[email protected]>

This is only used for setting the soft block size on the struct block_device once and then never used again.

---
 drivers/block/loop.c | 10 ++--------
 drivers/block/loop.h |  1 -
 2 files changed, 2 insertions(+), 9 deletions(-)

Cheers,

Hannes

Signature · 5 lines
Dr. Hannes Reinecke		   Teamlead Storage & Networking
[email protected]			               +49 911 74053 688
SUSE LINUX GmbH, Maxfeldstr. 5, 90409 Nürnberg
GF: F. Imendörffer, J. Smithard, J. Guild, D. Upmanyu, G. Norton
HRB 21284 (AG Nürnberg)
#3 lore raw

PATCH 2/4 loop: set physical block size to PAGE_SIZE

In reply to Omar Sandoval

From: Omar Sandoval <[email protected]>

The physical block size is "the lowest possible sector size that the hardware can operate on without reverting to read-modify-write operations" (from the comment on blk_queue_physical_block_size()). Since loop does buffered I/O on the backing file by default, the RMW unit is a page. This isn't the case for direct I/O mode, but let's keep it simple.

---
 drivers/block/loop.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/block/loop.c +2 -0
index 54e091887199..1a5b4ecf54ec 100644
@@ -1764,6 +1764,8 @@ static int loop_add(struct loop_device **l, int i)
 	}
 	lo->lo_queue->queuedata = lo;
 
+	blk_queue_physical_block_size(lo->lo_queue, PAGE_SIZE);
+
 	/*
 	 * It doesn't make sense to enable merge because the I/O
 	 * submitted to backing file is handled page by page.
Signature · 1 line
2.14.1
Reviewed-by on v1 2/4
#4 lore raw

Re: [PATCH 2/4] loop: set physical block size to PAGE_SIZE

Quoted 19 lines · Omar Sandoval

From: Omar Sandoval <[email protected]>

The physical block size is "the lowest possible sector size that the hardware can operate on without reverting to read-modify-write operations" (from the comment on blk_queue_physical_block_size()). Since loop does buffered I/O on the backing file by default, the RMW unit is a page. This isn't the case for direct I/O mode, but let's keep it simple.

---
 drivers/block/loop.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/block/loop.c +0 -0
index 54e091887199..1a5b4ecf54ec 100644

drivers/block/loop.c static int loop_add(struct loop_device **l, int i) · line 1771

Quoted 8 lines
drivers/block/loop.c +2 -0
@@ -1764,6 +1764,8 @@ static int loop_add(struct loop_device **l, int i)
 	}
 	lo->lo_queue->queuedata = lo;
 
+	blk_queue_physical_block_size(lo->lo_queue, PAGE_SIZE);
+
 	/*
 	 * It doesn't make sense to enable merge because the I/O
 	 * submitted to backing file is handled page by page.

Let's see it this one goes through ...

Cheers,

Hannes

Signature · 5 lines
Dr. Hannes Reinecke		   Teamlead Storage & Networking
[email protected]			               +49 911 74053 688
SUSE LINUX GmbH, Maxfeldstr. 5, 90409 Nürnberg
GF: F. Imendörffer, J. Smithard, J. Guild, D. Upmanyu, G. Norton
HRB 21284 (AG Nürnberg)
#5 lore raw

PATCH 3/4 loop: add ioctl for changing logical block size

In reply to Omar Sandoval

From: Omar Sandoval <[email protected]>

This is a different approach from the first attempt in f2c6df7dbf9a ("loop: support 4k physical blocksize"). Rather than extending LOOP_{GET,SET}_STATUS, add a separate ioctl just for setting the block size.

---
 drivers/block/loop.c      | 24 ++++++++++++++++++++++++
 include/uapi/linux/loop.h |  1 +
 2 files changed, 25 insertions(+)
drivers/block/loop.c +24 -0
index 1a5b4ecf54ec..6b1d932028e3 100644
@@ -1047,6 +1047,7 @@ static int loop_clr_fd(struct loop_device *lo)
 	memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE);
 	memset(lo->lo_crypt_name, 0, LO_NAME_SIZE);
 	memset(lo->lo_file_name, 0, LO_NAME_SIZE);
+	blk_queue_logical_block_size(lo->lo_queue, 512);
 	if (bdev) {
 		bdput(bdev);
 		invalidate_bdev(bdev);
@@ -1330,6 +1331,24 @@ static int loop_set_dio(struct loop_device *lo, unsigned long arg)
 	return error;
 }
 
+static int loop_set_block_size(struct loop_device *lo, unsigned long arg)
+{
+	if (lo->lo_state != Lo_bound)
+		return -ENXIO;
+
+	if (arg < 512 || arg > PAGE_SIZE || !is_power_of_2(arg))
+		return -EINVAL;
+
+	blk_mq_freeze_queue(lo->lo_queue);
+
+	blk_queue_logical_block_size(lo->lo_queue, arg);
+	loop_update_dio(lo);
+
+	blk_mq_unfreeze_queue(lo->lo_queue);
+
+	return 0;
+}
+
 static int lo_ioctl(struct block_device *bdev, fmode_t mode,
 	unsigned int cmd, unsigned long arg)
 {
@@ -1378,6 +1397,11 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
 		if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN))
 			err = loop_set_dio(lo, arg);
 		break;
+	case LOOP_SET_BLOCK_SIZE:
+		err = -EPERM;
+		if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN))
+			err = loop_set_block_size(lo, arg);
+		break;
 	default:
 		err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL;
 	}
include/uapi/linux/loop.h +1 -0
index c8125ec1f4f2..23158dbe2424 100644
@@ -88,6 +88,7 @@ struct loop_info64 {
 #define LOOP_CHANGE_FD		0x4C06
 #define LOOP_SET_CAPACITY	0x4C07
 #define LOOP_SET_DIRECT_IO	0x4C08
+#define LOOP_SET_BLOCK_SIZE	0x4C09
 
 /* /dev/loop-control interface */
 #define LOOP_CTL_ADD		0x4C80
Signature · 1 line
2.14.1
#6 lore raw

Re: [PATCH 3/4] loop: add ioctl for changing logical block size

Quoted 26 lines · Omar Sandoval

From: Omar Sandoval <[email protected]>

This is a different approach from the first attempt in f2c6df7dbf9a ("loop: support 4k physical blocksize"). Rather than extending LOOP_{GET,SET}_STATUS, add a separate ioctl just for setting the block size.

---
 drivers/block/loop.c      | 24 ++++++++++++++++++++++++
 include/uapi/linux/loop.h |  1 +
 2 files changed, 25 insertions(+)
drivers/block/loop.c +1 -0
index 1a5b4ecf54ec..6b1d932028e3 100644
@@ -1047,6 +1047,7 @@ static int loop_clr_fd(struct loop_device *lo)
 	memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE);
 	memset(lo->lo_crypt_name, 0, LO_NAME_SIZE);
 	memset(lo->lo_file_name, 0, LO_NAME_SIZE);
+	blk_queue_logical_block_size(lo->lo_queue, 512);
 	if (bdev) {
 		bdput(bdev);
 		invalidate_bdev(bdev);

drivers/block/loop.c static int loop_set_dio(struct loop_device *lo, unsigned long arg) · line 1340

Quoted 10 lines
drivers/block/loop.c +7 -0
@@ -1330,6 +1331,24 @@ static int loop_set_dio(struct loop_device *lo, unsigned long arg)
 	return error;
 }
 
+static int loop_set_block_size(struct loop_device *lo, unsigned long arg)
+{
+	if (lo->lo_state != Lo_bound)
+		return -ENXIO;
+
+	if (arg < 512 || arg > PAGE_SIZE || !is_power_of_2(arg))
+		return -EINVAL;

I'm not sure if the PAGE_SIZE is the right limit for the use-case where backing file is a block device ...but it's nothing important.

drivers/block/loop.c static int loop_set_dio(struct loop_device *lo, unsigned long arg) · line 1344

Quoted 3 lines
drivers/block/loop.c +3 -0
+	blk_mq_freeze_queue(lo->lo_queue);
+
+	blk_queue_logical_block_size(lo->lo_queue, arg);

Just note that this function also updates physical_block_size if the 'arg' is greater than the current setting. That's good thing :-)

Quoted 11 lines
drivers/block/loop.c +7 -0
+	loop_update_dio(lo);
+
+	blk_mq_unfreeze_queue(lo->lo_queue);
+
+	return 0;
+}
+
 static int lo_ioctl(struct block_device *bdev, fmode_t mode,
 	unsigned int cmd, unsigned long arg)
 {

drivers/block/loop.c static int lo_ioctl(struct block_device *bdev, fmode_t mode, · line 1404

Quoted 8 lines
drivers/block/loop.c +5 -0
@@ -1378,6 +1397,11 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
 		if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN))
 			err = loop_set_dio(lo, arg);
 		break;
+	case LOOP_SET_BLOCK_SIZE:
+		err = -EPERM;
+		if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN))
+			err = loop_set_block_size(lo, arg);
+		break;

I like it this ioctl idea. It makes loop based tests more comfortable as we can change sector size on the fly without loop device reset.

I did not test it yet, but this implementation seems the best. Thanks.

Karel

Signature · 2 lines
 Karel Zak  <[email protected]>
 http://karelzak.blogspot.com
#7 lore raw

Re: [PATCH 3/4] loop: add ioctl for changing logical block size

Quoted 30 lines · Karel Zak
Quoted 29 lines · Omar Sandoval

From: Omar Sandoval <[email protected]>

This is a different approach from the first attempt in f2c6df7dbf9a ("loop: support 4k physical blocksize"). Rather than extending LOOP_{GET,SET}_STATUS, add a separate ioctl just for setting the block size.

---
 drivers/block/loop.c      | 24 ++++++++++++++++++++++++
 include/uapi/linux/loop.h |  1 +
 2 files changed, 25 insertions(+)
drivers/block/loop.c +1 -0
index 1a5b4ecf54ec..6b1d932028e3 100644
@@ -1047,6 +1047,7 @@ static int loop_clr_fd(struct loop_device *lo)
 	memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE);
 	memset(lo->lo_crypt_name, 0, LO_NAME_SIZE);
 	memset(lo->lo_file_name, 0, LO_NAME_SIZE);
+	blk_queue_logical_block_size(lo->lo_queue, 512);
 	if (bdev) {
 		bdput(bdev);
 		invalidate_bdev(bdev);
@@ -1330,6 +1331,24 @@ static int loop_set_dio(struct loop_device *lo, unsigned long arg)
 	return error;
 }
 
Quoted 10 lines

drivers/block/loop.c static int loop_set_dio(struct loop_device *lo, unsigned long arg) · line 1340

Quoted 7 lines
drivers/block/loop.c +7 -0
+static int loop_set_block_size(struct loop_device *lo, unsigned long arg)
+{
+	if (lo->lo_state != Lo_bound)
+		return -ENXIO;
+
+	if (arg < 512 || arg > PAGE_SIZE || !is_power_of_2(arg))
+		return -EINVAL;

I'm not sure if the PAGE_SIZE is the right limit for the use-case where backing file is a block device ...but it's nothing important.

A bigger logical block size just means we'll send bigger I/Os down to the backing file, so I think it should be okay.

Quoted 6 lines

drivers/block/loop.c static int loop_set_dio(struct loop_device *lo, unsigned long arg) · line 1344

Quoted 3 lines
drivers/block/loop.c +3 -0
+	blk_mq_freeze_queue(lo->lo_queue);
+
+	blk_queue_logical_block_size(lo->lo_queue, arg);

Just note that this function also updates physical_block_size if the 'arg' is greater than the current setting. That's good thing :-)

Patch 2 sets the physical block size to PAGE_SIZE, and we cap the logical block size to PAGE_SIZE, so we won't actually hit that case anyways :)

Quoted 23 lines
Quoted 11 lines
drivers/block/loop.c +7 -0
+	loop_update_dio(lo);
+
+	blk_mq_unfreeze_queue(lo->lo_queue);
+
+	return 0;
+}
+
 static int lo_ioctl(struct block_device *bdev, fmode_t mode,
 	unsigned int cmd, unsigned long arg)
 {

drivers/block/loop.c static int lo_ioctl(struct block_device *bdev, fmode_t mode, · line 1404

Quoted 8 lines
drivers/block/loop.c +5 -0
@@ -1378,6 +1397,11 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
 		if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN))
 			err = loop_set_dio(lo, arg);
 		break;
+	case LOOP_SET_BLOCK_SIZE:
+		err = -EPERM;
+		if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN))
+			err = loop_set_block_size(lo, arg);
+		break;

I like it this ioctl idea. It makes loop based tests more comfortable as we can change sector size on the fly without loop device reset.

Quoted 1 line

I did not test it yet, but this implementation seems the best. Thanks.

Thanks! Let me know if you spot any issues when you test it.

Reviewed-by on v1 3/4
#8 lore raw

Re: [PATCH 3/4] loop: add ioctl for changing logical block size

In reply to Omar Sandoval
Quoted 68 lines · Omar Sandoval

From: Omar Sandoval <[email protected]>

This is a different approach from the first attempt in f2c6df7dbf9a ("loop: support 4k physical blocksize"). Rather than extending LOOP_{GET,SET}_STATUS, add a separate ioctl just for setting the block size.

---
 drivers/block/loop.c      | 24 ++++++++++++++++++++++++
 include/uapi/linux/loop.h |  1 +
 2 files changed, 25 insertions(+)
drivers/block/loop.c +24 -0
index 1a5b4ecf54ec..6b1d932028e3 100644
@@ -1047,6 +1047,7 @@ static int loop_clr_fd(struct loop_device *lo)
 	memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE);
 	memset(lo->lo_crypt_name, 0, LO_NAME_SIZE);
 	memset(lo->lo_file_name, 0, LO_NAME_SIZE);
+	blk_queue_logical_block_size(lo->lo_queue, 512);
 	if (bdev) {
 		bdput(bdev);
 		invalidate_bdev(bdev);
@@ -1330,6 +1331,24 @@ static int loop_set_dio(struct loop_device *lo, unsigned long arg)
 	return error;
 }
 
+static int loop_set_block_size(struct loop_device *lo, unsigned long arg)
+{
+	if (lo->lo_state != Lo_bound)
+		return -ENXIO;
+
+	if (arg < 512 || arg > PAGE_SIZE || !is_power_of_2(arg))
+		return -EINVAL;
+
+	blk_mq_freeze_queue(lo->lo_queue);
+
+	blk_queue_logical_block_size(lo->lo_queue, arg);
+	loop_update_dio(lo);
+
+	blk_mq_unfreeze_queue(lo->lo_queue);
+
+	return 0;
+}
+
 static int lo_ioctl(struct block_device *bdev, fmode_t mode,
 	unsigned int cmd, unsigned long arg)
 {
@@ -1378,6 +1397,11 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
 		if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN))
 			err = loop_set_dio(lo, arg);
 		break;
+	case LOOP_SET_BLOCK_SIZE:
+		err = -EPERM;
+		if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN))
+			err = loop_set_block_size(lo, arg);
+		break;
 	default:
 		err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL;
 	}
include/uapi/linux/loop.h +0 -0
index c8125ec1f4f2..23158dbe2424 100644

include/uapi/linux/loop.h struct loop_info64 { · line 94

Quoted 7 lines
include/uapi/linux/loop.h +1 -0
@@ -88,6 +88,7 @@ struct loop_info64 {
 #define LOOP_CHANGE_FD		0x4C06
 #define LOOP_SET_CAPACITY	0x4C07
 #define LOOP_SET_DIRECT_IO	0x4C08
+#define LOOP_SET_BLOCK_SIZE	0x4C09
 
 /* /dev/loop-control interface */
 #define LOOP_CTL_ADD		0x4C80

Hmm.

If the losetup / util-linux maintainers are on board with this, okay.

Cheers,

Hannes

Signature · 5 lines
Dr. Hannes Reinecke		   Teamlead Storage & Networking
[email protected]			               +49 911 74053 688
SUSE LINUX GmbH, Maxfeldstr. 5, 90409 Nürnberg
GF: F. Imendörffer, J. Smithard, J. Guild, D. Upmanyu, G. Norton
HRB 21284 (AG Nürnberg)
#9 lore raw

PATCH 4/4 loop: fold loop_switch() into callers

In reply to Omar Sandoval

From: Omar Sandoval <[email protected]>

The comments here are really outdated, and blk-mq made flushing much simpler, so just fold the two cases into the callers.

---
 drivers/block/loop.c | 76 ++++++++--------------------------------------------
 1 file changed, 11 insertions(+), 65 deletions(-)
drivers/block/loop.c +11 -65
index 6b1d932028e3..e60b4ac77577 100644
@@ -546,72 +546,12 @@ static int do_req_filebacked(struct loop_device *lo, struct request *rq)
 	}
 }
 
-struct switch_request {
-	struct file *file;
-	struct completion wait;
-};
-
 static inline void loop_update_dio(struct loop_device *lo)
 {
 	__loop_update_dio(lo, io_is_direct(lo->lo_backing_file) |
 			lo->use_dio);
 }
 
-/*
- * Do the actual switch; called from the BIO completion routine
- */
-static void do_loop_switch(struct loop_device *lo, struct switch_request *p)
-{
-	struct file *file = p->file;
-	struct file *old_file = lo->lo_backing_file;
-	struct address_space *mapping;
-
-	/* if no new file, only flush of queued bios requested */
-	if (!file)
-		return;
-
-	mapping = file->f_mapping;
-	mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask);
-	lo->lo_backing_file = file;
-	lo->old_gfp_mask = mapping_gfp_mask(mapping);
-	mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS));
-	loop_update_dio(lo);
-}
-
-/*
- * loop_switch performs the hard work of switching a backing store.
- * First it needs to flush existing IO, it does this by sending a magic
- * BIO down the pipe. The completion of this BIO does the actual switch.
- */
-static int loop_switch(struct loop_device *lo, struct file *file)
-{
-	struct switch_request w;
-
-	w.file = file;
-
-	/* freeze queue and wait for completion of scheduled requests */
-	blk_mq_freeze_queue(lo->lo_queue);
-
-	/* do the switch action */
-	do_loop_switch(lo, &w);
-
-	/* unfreeze */
-	blk_mq_unfreeze_queue(lo->lo_queue);
-
-	return 0;
-}
-
-/*
- * Helper to flush the IOs in loop, but keeping loop thread running
- */
-static int loop_flush(struct loop_device *lo)
-{
-	/* loop not yet configured, no running thread, nothing to flush */
-	if (lo->lo_state != Lo_bound)
-		return 0;
-	return loop_switch(lo, NULL);
-}
-
 static void loop_reread_partitions(struct loop_device *lo,
 				   struct block_device *bdev)
 {
@@ -676,9 +616,14 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
 		goto out_putf;
 
 	/* and ... switch */
-	error = loop_switch(lo, file);
-	if (error)
-		goto out_putf;
+	blk_mq_freeze_queue(lo->lo_queue);
+	mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask);
+	lo->lo_backing_file = file;
+	lo->old_gfp_mask = mapping_gfp_mask(file->f_mapping);
+	mapping_set_gfp_mask(file->f_mapping,
+			     lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS));
+	loop_update_dio(lo);
+	blk_mq_unfreeze_queue(lo->lo_queue);
 
 	fput(old_file);
 	if (lo->lo_flags & LO_FLAGS_PARTSCAN)
@@ -1601,12 +1546,13 @@ static void lo_release(struct gendisk *disk, fmode_t mode)
 		err = loop_clr_fd(lo);
 		if (!err)
 			return;
-	} else {
+	} else if (lo->lo_state == Lo_bound) {
 		/*
 		 * Otherwise keep thread (if running) and config,
 		 * but flush possible ongoing bios in thread.
 		 */
-		loop_flush(lo);
+		blk_mq_freeze_queue(lo->lo_queue);
+		blk_mq_unfreeze_queue(lo->lo_queue);
 	}
 
 	mutex_unlock(&lo->lo_ctl_mutex);
Signature · 1 line
2.14.1
Reviewed-by on v1 4/4
#10 lore raw

Re: [PATCH 4/4] loop: fold loop_switch() into callers

Quoted 10 lines · Omar Sandoval

From: Omar Sandoval <[email protected]>

The comments here are really outdated, and blk-mq made flushing much simpler, so just fold the two cases into the callers.

---
 drivers/block/loop.c | 76 ++++++++--------------------------------------------
 1 file changed, 11 insertions(+), 65 deletions(-)

Cheers,

Hannes

Signature · 5 lines
Dr. Hannes Reinecke		   Teamlead Storage & Networking
[email protected]			               +49 911 74053 688
SUSE LINUX GmbH, Maxfeldstr. 5, 90409 Nürnberg
GF: F. Imendörffer, J. Smithard, J. Guild, D. Upmanyu, G. Norton
HRB 21284 (AG Nürnberg)
Reviewed-by on the v1 series
#11 lore raw
In reply to Omar Sandoval
Quoted 46 lines · Omar Sandoval

From: Omar Sandoval <[email protected]>

Hi, everyone,

Here's another attempt at supporting different logical block sizes for loop devices. First, some terminology:

* Logical block size: the lowest possible block size that the storage device can address.
* Physical block size: the lowest possible sector size that the hardware can operate on without reverting to read-modify-write operations. Always greater than or equal to the logical block size.

These are characteristics of the device itself tracked in the request_queue. For loop devices, both are currently always 512 bytes.

struct block_device also has another block size, basically a "soft" block size which is the preferred I/O size and can be changed from userspace. For loop devices, this is PAGE_SIZE if the backing file is a regular file or otherwise the soft block size of the backing block device.

Patch 1 simplifies how the soft block size is set. I'm tempted to not set it at all and use the default of PAGE_SIZE, but maybe someone is depending on inheriting the soft block size from the backing block device...

Patch 2 sets the physical block size of loop devices to a more meaningful value for loop, PAGE_SIZE.

Patch 3 allows us to change the logical block size. It adds a new ioctl instead of the previous approach (which extends LOOP_{GET,SET}_STATUS). Hannes, I assume you had a specific reason for doing it the way you did, care to explain?

Patch 4 is a cleanup.

This is based on my patch reverting the original block size support, targeting 4.14. Thanks!

Omar Sandoval (4):
loop: get rid of lo_blocksize
loop: set physical block size to PAGE_SIZE
loop: add ioctl for changing logical block size
loop: fold loop_switch() into callers

Quoted 4 lines
 drivers/block/loop.c      | 112 ++++++++++++++++------------------------------
 drivers/block/loop.h      |   1 -
 include/uapi/linux/loop.h |   1 +
 3 files changed, 40 insertions(+), 74 deletions(-)

Looks fine,

Signature · 1 line
Ming
maintainer
#12 lore raw
In reply to Omar Sandoval
Quoted 35 lines · Omar Sandoval

From: Omar Sandoval <[email protected]>

Hi, everyone,

Here's another attempt at supporting different logical block sizes for loop devices. First, some terminology:

* Logical block size: the lowest possible block size that the storage device can address.
* Physical block size: the lowest possible sector size that the hardware can operate on without reverting to read-modify-write operations. Always greater than or equal to the logical block size.

These are characteristics of the device itself tracked in the request_queue. For loop devices, both are currently always 512 bytes.

struct block_device also has another block size, basically a "soft" block size which is the preferred I/O size and can be changed from userspace. For loop devices, this is PAGE_SIZE if the backing file is a regular file or otherwise the soft block size of the backing block device.

Patch 1 simplifies how the soft block size is set. I'm tempted to not set it at all and use the default of PAGE_SIZE, but maybe someone is depending on inheriting the soft block size from the backing block device...

Patch 2 sets the physical block size of loop devices to a more meaningful value for loop, PAGE_SIZE.

Patch 3 allows us to change the logical block size. It adds a new ioctl instead of the previous approach (which extends LOOP_{GET,SET}_STATUS). Hannes, I assume you had a specific reason for doing it the way you did, care to explain?

Quoted 4 lines

Patch 4 is a cleanup.

This is based on my patch reverting the original block size support, targeting 4.14. Thanks!

Applied for 4.14, thanks Omar.

Signature · 1 line
Jens Axboe