dm: Allow .prepare_ioctl to handle ioctls directly

This adds a 'bool *forward' parameter to .prepare_ioctl, which allows
device mapper targets to accept ioctls to themselves instead of the
underlying device. If the target already fully handled the ioctl, it
sets *forward to false and device mapper won't forward it to the
underlying device any more.

In order for targets to actually know what the ioctl is about and how to
handle it, pass also cmd and arg.

As long as targets restrict themselves to interpreting ioctls of type
DM_IOCTL, this is a backwards compatible change because previously, any
such ioctl would have been passed down through all device mapper layers
until it reached a device that can't understand the ioctl and would
return an error.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
This commit is contained in:
Kevin Wolf 2025-04-29 18:50:17 +02:00 committed by Mikulas Patocka
parent 13e79076c8
commit 4862c8861d
11 changed files with 44 additions and 16 deletions

View File

@ -534,7 +534,9 @@ static void dust_status(struct dm_target *ti, status_type_t type,
}
}
static int dust_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
static int dust_prepare_ioctl(struct dm_target *ti, struct block_device **bdev,
unsigned int cmd, unsigned long arg,
bool *forward)
{
struct dust_device *dd = ti->private;
struct dm_dev *dev = dd->dev;

View File

@ -415,7 +415,8 @@ static void ebs_status(struct dm_target *ti, status_type_t type,
}
}
static int ebs_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
static int ebs_prepare_ioctl(struct dm_target *ti, struct block_device **bdev,
unsigned int cmd, unsigned long arg, bool *forward)
{
struct ebs_c *ec = ti->private;
struct dm_dev *dev = ec->dev;

View File

@ -648,7 +648,9 @@ static void flakey_status(struct dm_target *ti, status_type_t type,
}
}
static int flakey_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
static int flakey_prepare_ioctl(struct dm_target *ti, struct block_device **bdev,
unsigned int cmd, unsigned long arg,
bool *forward)
{
struct flakey_c *fc = ti->private;

View File

@ -119,7 +119,9 @@ static void linear_status(struct dm_target *ti, status_type_t type,
}
}
static int linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
static int linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev,
unsigned int cmd, unsigned long arg,
bool *forward)
{
struct linear_c *lc = ti->private;
struct dm_dev *dev = lc->dev;

View File

@ -818,7 +818,9 @@ static void log_writes_status(struct dm_target *ti, status_type_t type,
}
static int log_writes_prepare_ioctl(struct dm_target *ti,
struct block_device **bdev)
struct block_device **bdev,
unsigned int cmd, unsigned long arg,
bool *forward)
{
struct log_writes_c *lc = ti->private;
struct dm_dev *dev = lc->dev;

View File

@ -2022,7 +2022,9 @@ static int multipath_message(struct dm_target *ti, unsigned int argc, char **arg
}
static int multipath_prepare_ioctl(struct dm_target *ti,
struct block_device **bdev)
struct block_device **bdev,
unsigned int cmd, unsigned long arg,
bool *forward)
{
struct multipath *m = ti->private;
struct pgpath *pgpath;

View File

@ -517,7 +517,9 @@ static void switch_status(struct dm_target *ti, status_type_t type,
*
* Passthrough all ioctls to the path for sector 0
*/
static int switch_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
static int switch_prepare_ioctl(struct dm_target *ti, struct block_device **bdev,
unsigned int cmd, unsigned long arg,
bool *forward)
{
struct switch_ctx *sctx = ti->private;
unsigned int path_nr;

View File

@ -994,7 +994,9 @@ static void verity_status(struct dm_target *ti, status_type_t type,
}
}
static int verity_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
static int verity_prepare_ioctl(struct dm_target *ti, struct block_device **bdev,
unsigned int cmd, unsigned long arg,
bool *forward)
{
struct dm_verity *v = ti->private;

View File

@ -1015,7 +1015,8 @@ static void dmz_io_hints(struct dm_target *ti, struct queue_limits *limits)
/*
* Pass on ioctl to the backend device.
*/
static int dmz_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
static int dmz_prepare_ioctl(struct dm_target *ti, struct block_device **bdev,
unsigned int cmd, unsigned long arg, bool *forward)
{
struct dmz_target *dmz = ti->private;
struct dmz_dev *dev = &dmz->dev[0];

View File

@ -411,7 +411,8 @@ static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
}
static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx,
struct block_device **bdev)
struct block_device **bdev, unsigned int cmd,
unsigned long arg, bool *forward)
{
struct dm_target *ti;
struct dm_table *map;
@ -434,8 +435,8 @@ static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx,
if (dm_suspended_md(md))
return -EAGAIN;
r = ti->type->prepare_ioctl(ti, bdev);
if (r == -ENOTCONN && !fatal_signal_pending(current)) {
r = ti->type->prepare_ioctl(ti, bdev, cmd, arg, forward);
if (r == -ENOTCONN && *forward && !fatal_signal_pending(current)) {
dm_put_live_table(md, *srcu_idx);
fsleep(10000);
goto retry;
@ -454,9 +455,10 @@ static int dm_blk_ioctl(struct block_device *bdev, blk_mode_t mode,
{
struct mapped_device *md = bdev->bd_disk->private_data;
int r, srcu_idx;
bool forward = true;
r = dm_prepare_ioctl(md, &srcu_idx, &bdev);
if (r < 0)
r = dm_prepare_ioctl(md, &srcu_idx, &bdev, cmd, arg, &forward);
if (!forward || r < 0)
goto out;
if (r > 0) {
@ -3630,10 +3632,13 @@ static int dm_pr_clear(struct block_device *bdev, u64 key)
struct mapped_device *md = bdev->bd_disk->private_data;
const struct pr_ops *ops;
int r, srcu_idx;
bool forward = true;
r = dm_prepare_ioctl(md, &srcu_idx, &bdev);
/* Not a real ioctl, but targets must not interpret non-DM ioctls */
r = dm_prepare_ioctl(md, &srcu_idx, &bdev, 0, 0, &forward);
if (r < 0)
goto out;
WARN_ON_ONCE(!forward);
ops = bdev->bd_disk->fops->pr_ops;
if (ops && ops->pr_clear)

View File

@ -93,7 +93,14 @@ typedef void (*dm_status_fn) (struct dm_target *ti, status_type_t status_type,
typedef int (*dm_message_fn) (struct dm_target *ti, unsigned int argc, char **argv,
char *result, unsigned int maxlen);
typedef int (*dm_prepare_ioctl_fn) (struct dm_target *ti, struct block_device **bdev);
/*
* Called with *forward == true. If it remains true, the ioctl should be
* forwarded to bdev. If it is reset to false, the target already fully handled
* the ioctl and the return value is the return value for the whole ioctl.
*/
typedef int (*dm_prepare_ioctl_fn) (struct dm_target *ti, struct block_device **bdev,
unsigned int cmd, unsigned long arg,
bool *forward);
#ifdef CONFIG_BLK_DEV_ZONED
typedef int (*dm_report_zones_fn) (struct dm_target *ti,