loop: add some basic read-only sysfs attributes
authorMilan Broz <mbroz@redhat.com>
Mon, 23 Aug 2010 13:16:00 +0000 (15:16 +0200)
committerJens Axboe <jaxboe@fusionio.com>
Mon, 23 Aug 2010 13:18:10 +0000 (15:18 +0200)
Create /sys/block/loopX/loop directory and provide these attributes:
 - backing_file
 - autoclear
 - offset
 - sizelimit

This loop directory is present only if loop device is configured.

To be used in util-linux-ng (and possibly elsewhere like udev rules)
where code need to get loop attributes from kernel (and not store
duplicate info in userspace).

Moreover loop ioctls are not even able to provide full backing
file info because of buffer limits.

Signed-off-by: Milan Broz <mbroz@redhat.com>
Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
drivers/block/loop.c

index f3c636d..dc55230 100644 (file)
@@ -74,6 +74,7 @@
 #include <linux/highmem.h>
 #include <linux/kthread.h>
 #include <linux/splice.h>
+#include <linux/sysfs.h>
 
 #include <asm/uaccess.h>
 
@@ -737,6 +738,103 @@ static inline int is_loop_device(struct file *file)
        return i && S_ISBLK(i->i_mode) && MAJOR(i->i_rdev) == LOOP_MAJOR;
 }
 
+/* loop sysfs attributes */
+
+static ssize_t loop_attr_show(struct device *dev, char *page,
+                             ssize_t (*callback)(struct loop_device *, char *))
+{
+       struct loop_device *l, *lo = NULL;
+
+       mutex_lock(&loop_devices_mutex);
+       list_for_each_entry(l, &loop_devices, lo_list)
+               if (disk_to_dev(l->lo_disk) == dev) {
+                       lo = l;
+                       break;
+               }
+       mutex_unlock(&loop_devices_mutex);
+
+       return lo ? callback(lo, page) : -EIO;
+}
+
+#define LOOP_ATTR_RO(_name)                                            \
+static ssize_t loop_attr_##_name##_show(struct loop_device *, char *); \
+static ssize_t loop_attr_do_show_##_name(struct device *d,             \
+                               struct device_attribute *attr, char *b) \
+{                                                                      \
+       return loop_attr_show(d, b, loop_attr_##_name##_show);          \
+}                                                                      \
+static struct device_attribute loop_attr_##_name =                     \
+       __ATTR(_name, S_IRUGO, loop_attr_do_show_##_name, NULL);
+
+static ssize_t loop_attr_backing_file_show(struct loop_device *lo, char *buf)
+{
+       ssize_t ret;
+       char *p = NULL;
+
+       mutex_lock(&lo->lo_ctl_mutex);
+       if (lo->lo_backing_file)
+               p = d_path(&lo->lo_backing_file->f_path, buf, PAGE_SIZE - 1);
+       mutex_unlock(&lo->lo_ctl_mutex);
+
+       if (IS_ERR_OR_NULL(p))
+               ret = PTR_ERR(p);
+       else {
+               ret = strlen(p);
+               memmove(buf, p, ret);
+               buf[ret++] = '\n';
+               buf[ret] = 0;
+       }
+
+       return ret;
+}
+
+static ssize_t loop_attr_offset_show(struct loop_device *lo, char *buf)
+{
+       return sprintf(buf, "%llu\n", (unsigned long long)lo->lo_offset);
+}
+
+static ssize_t loop_attr_sizelimit_show(struct loop_device *lo, char *buf)
+{
+       return sprintf(buf, "%llu\n", (unsigned long long)lo->lo_sizelimit);
+}
+
+static ssize_t loop_attr_autoclear_show(struct loop_device *lo, char *buf)
+{
+       int autoclear = (lo->lo_flags & LO_FLAGS_AUTOCLEAR);
+
+       return sprintf(buf, "%s\n", autoclear ? "1" : "0");
+}
+
+LOOP_ATTR_RO(backing_file);
+LOOP_ATTR_RO(offset);
+LOOP_ATTR_RO(sizelimit);
+LOOP_ATTR_RO(autoclear);
+
+static struct attribute *loop_attrs[] = {
+       &loop_attr_backing_file.attr,
+       &loop_attr_offset.attr,
+       &loop_attr_sizelimit.attr,
+       &loop_attr_autoclear.attr,
+       NULL,
+};
+
+static struct attribute_group loop_attribute_group = {
+       .name = "loop",
+       .attrs= loop_attrs,
+};
+
+static int loop_sysfs_init(struct loop_device *lo)
+{
+       return sysfs_create_group(&disk_to_dev(lo->lo_disk)->kobj,
+                                 &loop_attribute_group);
+}
+
+static void loop_sysfs_exit(struct loop_device *lo)
+{
+       sysfs_remove_group(&disk_to_dev(lo->lo_disk)->kobj,
+                          &loop_attribute_group);
+}
+
 static int loop_set_fd(struct loop_device *lo, fmode_t mode,
                       struct block_device *bdev, unsigned int arg)
 {
@@ -836,6 +934,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
 
        set_capacity(lo->lo_disk, size);
        bd_set_size(bdev, size << 9);
+       loop_sysfs_init(lo);
        /* let user-space know about the new size */
        kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE);
 
@@ -854,6 +953,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
        return 0;
 
 out_clr:
+       loop_sysfs_exit(lo);
        lo->lo_thread = NULL;
        lo->lo_device = NULL;
        lo->lo_backing_file = NULL;
@@ -950,6 +1050,7 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
        set_capacity(lo->lo_disk, 0);
        if (bdev) {
                bd_set_size(bdev, 0);
+               loop_sysfs_exit(lo);
                /* let user-space know about this change */
                kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE);
        }