Merge branch 'for-2.6.37/barrier' of git://git.kernel.dk/linux-2.6-block
[linux-2.6.git] / drivers / block / loop.c
index 5d27bc6..6c48b35 100644 (file)
 #include <linux/compat.h>
 #include <linux/suspend.h>
 #include <linux/freezer.h>
-#include <linux/smp_lock.h>
+#include <linux/mutex.h>
 #include <linux/writeback.h>
 #include <linux/buffer_head.h>         /* for invalidate_bdev() */
 #include <linux/completion.h>
 #include <linux/highmem.h>
 #include <linux/kthread.h>
 #include <linux/splice.h>
+#include <linux/sysfs.h>
 
 #include <asm/uaccess.h>
 
+static DEFINE_MUTEX(loop_mutex);
 static LIST_HEAD(loop_devices);
 static DEFINE_MUTEX(loop_devices_mutex);
 
@@ -737,6 +739,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 +935,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 +954,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 +1051,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);
        }
@@ -1409,11 +1511,11 @@ static int lo_open(struct block_device *bdev, fmode_t mode)
 {
        struct loop_device *lo = bdev->bd_disk->private_data;
 
-       lock_kernel();
+       mutex_lock(&loop_mutex);
        mutex_lock(&lo->lo_ctl_mutex);
        lo->lo_refcnt++;
        mutex_unlock(&lo->lo_ctl_mutex);
-       unlock_kernel();
+       mutex_unlock(&loop_mutex);
 
        return 0;
 }
@@ -1423,7 +1525,7 @@ static int lo_release(struct gendisk *disk, fmode_t mode)
        struct loop_device *lo = disk->private_data;
        int err;
 
-       lock_kernel();
+       mutex_lock(&loop_mutex);
        mutex_lock(&lo->lo_ctl_mutex);
 
        if (--lo->lo_refcnt)
@@ -1448,7 +1550,7 @@ static int lo_release(struct gendisk *disk, fmode_t mode)
 out:
        mutex_unlock(&lo->lo_ctl_mutex);
 out_unlocked:
-       lock_kernel();
+       mutex_unlock(&loop_mutex);
        return 0;
 }