[RFC 2/9] loop: add llseek(SEEK_HOLE/SEEK_DATA) support

From: Stefan Hajnoczi
Date: Thu Mar 28 2024 - 16:40:44 EST


Signed-off-by: Stefan Hajnoczi <stefanha@xxxxxxxxxx>
---
Open issues:
- The file offset is updated on both the blkdev file and the backing
file. Is there a way to avoid updating the backing file offset so the
file opened by userspace is not affected?
- Should this run in the worker or use the cgroups?
---
drivers/block/loop.c | 36 ++++++++++++++++++++++++++++++------
1 file changed, 30 insertions(+), 6 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 28a95fd366fea..6a89375de82e8 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -750,6 +750,29 @@ static void loop_sysfs_exit(struct loop_device *lo)
&loop_attribute_group);
}

+static loff_t lo_seek_hole_data(struct block_device *bdev, loff_t offset,
+ int whence)
+{
+ /* TODO need to activate cgroups or use worker? */
+ /* TODO locking? */
+ struct loop_device *lo = bdev->bd_disk->private_data;
+ struct file *file = lo->lo_backing_file;
+
+ if (lo->lo_offset > 0)
+ offset += lo->lo_offset; /* TODO underflow/overflow? */
+
+ /* TODO backing file offset is modified! */
+ offset = vfs_llseek(file, offset, whence);
+ if (offset < 0)
+ return offset;
+
+ if (lo->lo_offset > 0)
+ offset -= lo->lo_offset; /* TODO underflow/overflow? */
+ if (lo->lo_sizelimit > 0 && offset > lo->lo_sizelimit)
+ offset = lo->lo_sizelimit;
+ return offset;
+}
+
static void loop_config_discard(struct loop_device *lo,
struct queue_limits *lim)
{
@@ -1751,13 +1774,14 @@ static void lo_free_disk(struct gendisk *disk)
}

static const struct block_device_operations lo_fops = {
- .owner = THIS_MODULE,
- .release = lo_release,
- .ioctl = lo_ioctl,
+ .owner = THIS_MODULE,
+ .release = lo_release,
+ .ioctl = lo_ioctl,
#ifdef CONFIG_COMPAT
- .compat_ioctl = lo_compat_ioctl,
+ .compat_ioctl = lo_compat_ioctl,
#endif
- .free_disk = lo_free_disk,
+ .free_disk = lo_free_disk,
+ .seek_hole_data = lo_seek_hole_data,
};

/*
@@ -2140,7 +2164,7 @@ static int loop_control_remove(int idx)
pr_warn_once("deleting an unspecified loop device is not supported.\n");
return -EINVAL;
}
-
+
/* Hide this loop device for serialization. */
ret = mutex_lock_killable(&loop_ctl_mutex);
if (ret)
--
2.44.0