loop: optionally use ioctl(LOOP_CONFIGURE) to set up loopdevs

LOOP_CONFIGURE is added to Linux 5.8

function                                             old     new   delta
NO_LOOP_CONFIGURE (old code):
set_loop                                             784     782      -2
LOOP_CONFIGURE:
set_loop                                             784     653    -131
TRY_LOOP_CONFIGURE:
set_loop                                             784     811     +27

Based on a patch by Xiaoming Ni <nixiaoming@huawei.com>

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2022-12-13 14:27:08 +01:00
parent 9df54deead
commit 45734a2351
2 changed files with 65 additions and 9 deletions

View File

@ -369,3 +369,25 @@ config UNICODE_PRESERVE_BROKEN
For example, this means that entering 'l', 's', ' ', 0xff, [Enter] For example, this means that entering 'l', 's', ' ', 0xff, [Enter]
at shell prompt will list file named 0xff (single char name at shell prompt will list file named 0xff (single char name
with char value 255), not file named '?'. with char value 255), not file named '?'.
choice
prompt "Use LOOP_CONFIGURE for losetup and loop mounts"
default TRY_LOOP_CONFIGURE
help
LOOP_CONFIGURE is added to Linux 5.8
https://lwn.net/Articles/820408/
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.
config LOOP_CONFIGURE
bool "use LOOP_CONFIGURE, needs kernel >= 5.8"
config NO_LOOP_CONFIGURE
bool "use LOOP_SET_FD + LOOP_SET_STATUS"
config TRY_LOOP_CONFIGURE
bool "try LOOP_CONFIGURE, fall back to LOOP_SET_FD + LOOP_SET_STATUS"
endchoice

View File

@ -110,26 +110,39 @@ static int get_next_free_loop(char *dev, int id)
return loopdevno; return loopdevno;
} }
static int set_loopdev_params(int ffd, #if ENABLE_TRY_LOOP_CONFIGURE || ENABLE_LOOP_CONFIGURE
int lfd, const char *file, # define LOOP_CONFIGURE 0x4C0A
struct loop_config {
uint32_t fd;
uint32_t block_size;
struct loop_info64 info;
uint64_t __reserved[8];
};
#endif
static int set_loopdev_params(int lfd,
int ffd, const char *file,
unsigned long long offset, unsigned long long offset,
unsigned long long sizelimit, unsigned long long sizelimit,
unsigned flags) unsigned flags)
{ {
int rc; int rc;
#if ENABLE_TRY_LOOP_CONFIGURE || ENABLE_LOOP_CONFIGURE
struct loop_config lconfig;
# define loopinfo lconfig.info
#else
bb_loop_info loopinfo; bb_loop_info loopinfo;
#endif
rc = ioctl(lfd, BB_LOOP_GET_STATUS, &loopinfo); rc = ioctl(lfd, BB_LOOP_GET_STATUS, &loopinfo);
/* If device is free, try to claim it */ /* If device is free, try to claim it */
if (rc && errno == ENXIO) { if (rc && errno == ENXIO) {
/* Associate free loop device with file */ #if ENABLE_TRY_LOOP_CONFIGURE || ENABLE_LOOP_CONFIGURE
rc = ioctl(lfd, LOOP_SET_FD, ffd); memset(&lconfig, 0, sizeof(lconfig));
if (rc != 0) { #else
/* Ouch... race: the device already has a fd */
return -1;
}
memset(&loopinfo, 0, sizeof(loopinfo)); memset(&loopinfo, 0, sizeof(loopinfo));
#endif
safe_strncpy((char *)loopinfo.lo_file_name, file, LO_NAME_SIZE); safe_strncpy((char *)loopinfo.lo_file_name, file, LO_NAME_SIZE);
loopinfo.lo_offset = offset; loopinfo.lo_offset = offset;
loopinfo.lo_sizelimit = sizelimit; loopinfo.lo_sizelimit = sizelimit;
@ -140,6 +153,25 @@ static int set_loopdev_params(int ffd,
* is wrong (would free the loop device!) * is wrong (would free the loop device!)
*/ */
loopinfo.lo_flags = (flags & ~BB_LO_FLAGS_READ_ONLY); loopinfo.lo_flags = (flags & ~BB_LO_FLAGS_READ_ONLY);
#if ENABLE_TRY_LOOP_CONFIGURE || ENABLE_LOOP_CONFIGURE
lconfig.fd = ffd;
rc = ioctl(lfd, LOOP_CONFIGURE, &lconfig);
if (rc == 0)
return rc; /* SUCCESS! */
# if ENABLE_TRY_LOOP_CONFIGURE
if (errno != EINVAL)
return rc; /* error other than old kernel */
/* Old kernel, fall through into old way to do it: */
# endif
#endif
#if ENABLE_TRY_LOOP_CONFIGURE || ENABLE_NO_LOOP_CONFIGURE
/* Associate free loop device with file */
rc = ioctl(lfd, LOOP_SET_FD, ffd);
if (rc != 0) {
/* Ouch... race: the device already has a fd */
return rc;
}
rc = ioctl(lfd, BB_LOOP_SET_STATUS, &loopinfo); rc = ioctl(lfd, BB_LOOP_SET_STATUS, &loopinfo);
if (rc != 0 && (loopinfo.lo_flags & BB_LO_FLAGS_AUTOCLEAR)) { if (rc != 0 && (loopinfo.lo_flags & BB_LO_FLAGS_AUTOCLEAR)) {
/* Old kernel, does not support LO_FLAGS_AUTOCLEAR? */ /* Old kernel, does not support LO_FLAGS_AUTOCLEAR? */
@ -151,8 +183,10 @@ static int set_loopdev_params(int ffd,
return rc; /* SUCCESS! */ return rc; /* SUCCESS! */
/* failure, undo LOOP_SET_FD */ /* failure, undo LOOP_SET_FD */
ioctl(lfd, LOOP_CLR_FD, 0); // actually, 0 param is unnecessary ioctl(lfd, LOOP_CLR_FD, 0); // actually, 0 param is unnecessary
#endif
} }
return -1; return -1;
#undef loopinfo
} }
/* Returns opened fd to the loop device, <0 on error. /* Returns opened fd to the loop device, <0 on error.
@ -227,7 +261,7 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse
goto try_next_loopN; goto try_next_loopN;
} }
rc = set_loopdev_params(ffd, lfd, file, offset, sizelimit, flags); rc = set_loopdev_params(lfd, ffd, file, offset, sizelimit, flags);
if (rc == 0) { if (rc == 0) {
/* SUCCESS! */ /* SUCCESS! */
if (!*device) if (!*device)