Skip to content

Commit 3448914

Browse files
Martijn Coenenaxboe
authored andcommitted
loop: Add LOOP_CONFIGURE ioctl
This allows userspace to completely setup a loop device with a single ioctl, removing the in-between state where the device can be partially configured - eg the loop device has a backing file associated with it, but is reading from the wrong offset. Besides removing the intermediate state, another big benefit of this ioctl is that LOOP_SET_STATUS can be slow; the main reason for this slowness is that LOOP_SET_STATUS(64) calls blk_mq_freeze_queue() to freeze the associated queue; this requires waiting for RCU synchronization, which I've measured can take about 15-20ms on this device on average. In addition to doing what LOOP_SET_STATUS can do, LOOP_CONFIGURE can also be used to: - Set the correct block size immediately by setting loop_config.block_size (avoids LOOP_SET_BLOCK_SIZE) - Explicitly request direct I/O mode by setting LO_FLAGS_DIRECT_IO in loop_config.info.lo_flags (avoids LOOP_SET_DIRECT_IO) - Explicitly request read-only mode by setting LO_FLAGS_READ_ONLY in loop_config.info.lo_flags Here's setting up ~70 regular loop devices with an offset on an x86 Android device, using LOOP_SET_FD and LOOP_SET_STATUS: vsoc_x86:/system/apex # time for i in `seq 30 100`; do losetup -r -o 4096 /dev/block/loop$i com.android.adbd.apex; done 0m03.40s real 0m00.02s user 0m00.03s system Here's configuring ~70 devices in the same way, but using a modified losetup that uses the new LOOP_CONFIGURE ioctl: vsoc_x86:/system/apex # time for i in `seq 30 100`; do losetup -r -o 4096 /dev/block/loop$i com.android.adbd.apex; done 0m01.94s real 0m00.01s user 0m00.01s system Signed-off-by: Martijn Coenen <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Signed-off-by: Jens Axboe <[email protected]>
1 parent faf1d25 commit 3448914

File tree

2 files changed

+97
-28
lines changed

2 files changed

+97
-28
lines changed

drivers/block/loop.c

Lines changed: 76 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,19 @@ static void __loop_update_dio(struct loop_device *lo, bool dio)
228228
blk_mq_unfreeze_queue(lo->lo_queue);
229229
}
230230

231+
/**
232+
* loop_validate_block_size() - validates the passed in block size
233+
* @bsize: size to validate
234+
*/
235+
static int
236+
loop_validate_block_size(unsigned short bsize)
237+
{
238+
if (bsize < 512 || bsize > PAGE_SIZE || !is_power_of_2(bsize))
239+
return -EINVAL;
240+
241+
return 0;
242+
}
243+
231244
/**
232245
* loop_set_size() - sets device size and notifies userspace
233246
* @lo: struct loop_device to set the size for
@@ -1050,23 +1063,24 @@ loop_set_status_from_info(struct loop_device *lo,
10501063
return 0;
10511064
}
10521065

1053-
static int loop_set_fd(struct loop_device *lo, fmode_t mode,
1054-
struct block_device *bdev, unsigned int arg)
1066+
static int loop_configure(struct loop_device *lo, fmode_t mode,
1067+
struct block_device *bdev,
1068+
const struct loop_config *config)
10551069
{
10561070
struct file *file;
10571071
struct inode *inode;
10581072
struct address_space *mapping;
10591073
struct block_device *claimed_bdev = NULL;
1060-
int lo_flags = 0;
10611074
int error;
10621075
loff_t size;
10631076
bool partscan;
1077+
unsigned short bsize;
10641078

10651079
/* This is safe, since we have a reference from open(). */
10661080
__module_get(THIS_MODULE);
10671081

10681082
error = -EBADF;
1069-
file = fget(arg);
1083+
file = fget(config->fd);
10701084
if (!file)
10711085
goto out;
10721086

@@ -1075,7 +1089,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
10751089
* here to avoid changing device under exclusive owner.
10761090
*/
10771091
if (!(mode & FMODE_EXCL)) {
1078-
claimed_bdev = bd_start_claiming(bdev, loop_set_fd);
1092+
claimed_bdev = bd_start_claiming(bdev, loop_configure);
10791093
if (IS_ERR(claimed_bdev)) {
10801094
error = PTR_ERR(claimed_bdev);
10811095
goto out_putf;
@@ -1097,42 +1111,55 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
10971111
mapping = file->f_mapping;
10981112
inode = mapping->host;
10991113

1114+
size = get_loop_size(lo, file);
1115+
1116+
if ((config->info.lo_flags & ~LOOP_CONFIGURE_SETTABLE_FLAGS) != 0) {
1117+
error = -EINVAL;
1118+
goto out_unlock;
1119+
}
1120+
1121+
if (config->block_size) {
1122+
error = loop_validate_block_size(config->block_size);
1123+
if (error)
1124+
goto out_unlock;
1125+
}
1126+
1127+
error = loop_set_status_from_info(lo, &config->info);
1128+
if (error)
1129+
goto out_unlock;
1130+
11001131
if (!(file->f_mode & FMODE_WRITE) || !(mode & FMODE_WRITE) ||
11011132
!file->f_op->write_iter)
1102-
lo_flags |= LO_FLAGS_READ_ONLY;
1103-
1104-
size = get_loop_size(lo, file);
1133+
lo->lo_flags |= LO_FLAGS_READ_ONLY;
11051134

11061135
error = loop_prepare_queue(lo);
11071136
if (error)
11081137
goto out_unlock;
11091138

11101139
error = 0;
11111140

1112-
set_device_ro(bdev, (lo_flags & LO_FLAGS_READ_ONLY) != 0);
1141+
set_device_ro(bdev, (lo->lo_flags & LO_FLAGS_READ_ONLY) != 0);
11131142

1114-
lo->use_dio = false;
1143+
lo->use_dio = lo->lo_flags & LO_FLAGS_DIRECT_IO;
11151144
lo->lo_device = bdev;
1116-
lo->lo_flags = lo_flags;
11171145
lo->lo_backing_file = file;
1118-
lo->transfer = NULL;
1119-
lo->ioctl = NULL;
1120-
lo->lo_sizelimit = 0;
11211146
lo->old_gfp_mask = mapping_gfp_mask(mapping);
11221147
mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS));
11231148

1124-
if (!(lo_flags & LO_FLAGS_READ_ONLY) && file->f_op->fsync)
1149+
if (!(lo->lo_flags & LO_FLAGS_READ_ONLY) && file->f_op->fsync)
11251150
blk_queue_write_cache(lo->lo_queue, true, false);
11261151

1127-
if (io_is_direct(lo->lo_backing_file) && inode->i_sb->s_bdev) {
1152+
if (config->block_size)
1153+
bsize = config->block_size;
1154+
else if (io_is_direct(lo->lo_backing_file) && inode->i_sb->s_bdev)
11281155
/* In case of direct I/O, match underlying block size */
1129-
unsigned short bsize = bdev_logical_block_size(
1130-
inode->i_sb->s_bdev);
1156+
bsize = bdev_logical_block_size(inode->i_sb->s_bdev);
1157+
else
1158+
bsize = 512;
11311159

1132-
blk_queue_logical_block_size(lo->lo_queue, bsize);
1133-
blk_queue_physical_block_size(lo->lo_queue, bsize);
1134-
blk_queue_io_min(lo->lo_queue, bsize);
1135-
}
1160+
blk_queue_logical_block_size(lo->lo_queue, bsize);
1161+
blk_queue_physical_block_size(lo->lo_queue, bsize);
1162+
blk_queue_io_min(lo->lo_queue, bsize);
11361163

11371164
loop_update_rotational(lo);
11381165
loop_update_dio(lo);
@@ -1155,14 +1182,14 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
11551182
if (partscan)
11561183
loop_reread_partitions(lo, bdev);
11571184
if (claimed_bdev)
1158-
bd_abort_claiming(bdev, claimed_bdev, loop_set_fd);
1185+
bd_abort_claiming(bdev, claimed_bdev, loop_configure);
11591186
return 0;
11601187

11611188
out_unlock:
11621189
mutex_unlock(&loop_ctl_mutex);
11631190
out_bdev:
11641191
if (claimed_bdev)
1165-
bd_abort_claiming(bdev, claimed_bdev, loop_set_fd);
1192+
bd_abort_claiming(bdev, claimed_bdev, loop_configure);
11661193
out_putf:
11671194
fput(file);
11681195
out:
@@ -1582,8 +1609,9 @@ static int loop_set_block_size(struct loop_device *lo, unsigned long arg)
15821609
if (lo->lo_state != Lo_bound)
15831610
return -ENXIO;
15841611

1585-
if (arg < 512 || arg > PAGE_SIZE || !is_power_of_2(arg))
1586-
return -EINVAL;
1612+
err = loop_validate_block_size(arg);
1613+
if (err)
1614+
return err;
15871615

15881616
if (lo->lo_queue->limits.logical_block_size == arg)
15891617
return 0;
@@ -1645,8 +1673,27 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
16451673
int err;
16461674

16471675
switch (cmd) {
1648-
case LOOP_SET_FD:
1649-
return loop_set_fd(lo, mode, bdev, arg);
1676+
case LOOP_SET_FD: {
1677+
/*
1678+
* Legacy case - pass in a zeroed out struct loop_config with
1679+
* only the file descriptor set , which corresponds with the
1680+
* default parameters we'd have used otherwise.
1681+
*/
1682+
struct loop_config config;
1683+
1684+
memset(&config, 0, sizeof(config));
1685+
config.fd = arg;
1686+
1687+
return loop_configure(lo, mode, bdev, &config);
1688+
}
1689+
case LOOP_CONFIGURE: {
1690+
struct loop_config config;
1691+
1692+
if (copy_from_user(&config, argp, sizeof(config)))
1693+
return -EFAULT;
1694+
1695+
return loop_configure(lo, mode, bdev, &config);
1696+
}
16501697
case LOOP_CHANGE_FD:
16511698
return loop_change_fd(lo, bdev, arg);
16521699
case LOOP_CLR_FD:
@@ -1818,6 +1865,7 @@ static int lo_compat_ioctl(struct block_device *bdev, fmode_t mode,
18181865
case LOOP_CLR_FD:
18191866
case LOOP_GET_STATUS64:
18201867
case LOOP_SET_STATUS64:
1868+
case LOOP_CONFIGURE:
18211869
arg = (unsigned long) compat_ptr(arg);
18221870
/* fall through */
18231871
case LOOP_SET_FD:

include/uapi/linux/loop.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ enum {
3131
/* LO_FLAGS that can be cleared using LOOP_SET_STATUS(64) */
3232
#define LOOP_SET_STATUS_CLEARABLE_FLAGS (LO_FLAGS_AUTOCLEAR)
3333

34+
/* LO_FLAGS that can be set using LOOP_CONFIGURE */
35+
#define LOOP_CONFIGURE_SETTABLE_FLAGS (LO_FLAGS_READ_ONLY | LO_FLAGS_AUTOCLEAR \
36+
| LO_FLAGS_PARTSCAN | LO_FLAGS_DIRECT_IO)
37+
3438
#include <asm/posix_types.h> /* for __kernel_old_dev_t */
3539
#include <linux/types.h> /* for __u64 */
3640

@@ -66,6 +70,22 @@ struct loop_info64 {
6670
__u64 lo_init[2];
6771
};
6872

73+
/**
74+
* struct loop_config - Complete configuration for a loop device.
75+
* @fd: fd of the file to be used as a backing file for the loop device.
76+
* @block_size: block size to use; ignored if 0.
77+
* @info: struct loop_info64 to configure the loop device with.
78+
*
79+
* This structure is used with the LOOP_CONFIGURE ioctl, and can be used to
80+
* atomically setup and configure all loop device parameters at once.
81+
*/
82+
struct loop_config {
83+
__u32 fd;
84+
__u32 block_size;
85+
struct loop_info64 info;
86+
__u64 __reserved[8];
87+
};
88+
6989
/*
7090
* Loop filter types
7191
*/
@@ -96,6 +116,7 @@ struct loop_info64 {
96116
#define LOOP_SET_CAPACITY 0x4C07
97117
#define LOOP_SET_DIRECT_IO 0x4C08
98118
#define LOOP_SET_BLOCK_SIZE 0x4C09
119+
#define LOOP_CONFIGURE 0x4C0A
99120

100121
/* /dev/loop-control interface */
101122
#define LOOP_CTL_ADD 0x4C80

0 commit comments

Comments
 (0)