Hello Joe,
This is a resend of my dm+sysfs patch, except I moved everything
(mapped_device, dm_table and dm_table's dm_dev list) back to where they
belong and added better code to test for and clean up from sysfs/kobject
failures.
I also unified the ref counting for the structs represented in sysfs. If
someone does "dmsetup remove mydev" but has a dm sysfs file open, the
memory for mydev should not be released until the file is closed. For
structs like mapped_device and dm_table I removed the atomic_t holders
variable and am just using the kobject ref count.
The patch was built and tested against 2.6.0-test9. I have also only
tested against the v4 ioctl.
Mike Christie
mikenc@xxxxxxxxxx
diff -Naur linux-2.6.0-test9/drivers/md/dm.c linux-2.6.0-test9-dm/drivers/md/dm.c
--- linux-2.6.0-test9/drivers/md/dm.c 2003-10-25 11:44:25.000000000 -0700
+++ linux-2.6.0-test9-dm/drivers/md/dm.c 2003-11-09 20:03:03.000000000 -0800
@@ -40,7 +40,6 @@
struct mapped_device {
struct rw_semaphore lock;
- atomic_t holders;
unsigned long flags;
@@ -69,6 +68,8 @@
*/
uint32_t event_nr;
wait_queue_head_t eventq;
+
+ struct kobject kobj;
};
#define MIN_IOS 256
@@ -599,7 +600,6 @@
memset(md, 0, sizeof(*md));
init_rwsem(&md->lock);
- atomic_set(&md->holders, 1);
md->queue = blk_alloc_queue(GFP_KERNEL);
if (!md->queue) {
@@ -643,7 +643,7 @@
return md;
}
-static void free_dev(struct mapped_device *md)
+void dm_free(struct mapped_device *md)
{
free_minor(md->disk->first_minor);
mempool_destroy(md->io_pool);
@@ -676,7 +676,8 @@
set_capacity(md->disk, size);
if (size == 0)
return 0;
-
+
+ dm_sysfs_rename_table(dm_table_get_kobj(t), "table");
dm_table_event_callback(md->map, event_callback, md);
dm_table_get(t);
@@ -689,8 +690,9 @@
if (!md->map)
return;
+ dm_sysfs_rename_table(dm_table_get_kobj(md->map), ".table");
dm_table_event_callback(md->map, NULL, NULL);
- dm_table_put(md->map);
+ dm_table_del(md->map);
md->map = NULL;
set_capacity(md->disk, 0);
}
@@ -702,11 +704,18 @@
struct mapped_device **result)
{
struct mapped_device *md;
+ int err;
md = alloc_dev(minor, persistent);
if (!md)
return -ENXIO;
+ err = dm_register_md(&md->disk->kobj, &md->kobj);
+ if (err) {
+ dm_free(md);
+ return err;
+ }
+
*result = md;
return 0;
}
@@ -723,17 +732,20 @@
void dm_get(struct mapped_device *md)
{
- atomic_inc(&md->holders);
+ kobject_get(&md->kobj);
}
void dm_put(struct mapped_device *md)
{
- if (atomic_dec_and_test(&md->holders)) {
- if (!test_bit(DMF_SUSPENDED, &md->flags) && md->map)
- dm_table_suspend_targets(md->map);
- __unbind(md);
- free_dev(md);
- }
+ kobject_put(&md->kobj);
+}
+
+void dm_del(struct mapped_device *md)
+{
+ if (!test_bit(DMF_SUSPENDED, &md->flags) && md->map)
+ dm_table_suspend_targets(md->map);
+ __unbind(md);
+ dm_unregister_md(&md->kobj);
}
/*
@@ -909,6 +921,16 @@
return t;
}
+struct mapped_device *dm_kobj_to_md(struct kobject *kobj)
+{
+ return container_of(kobj, struct mapped_device, kobj);
+}
+
+struct kobject *dm_md_get_kobj(struct mapped_device *md)
+{
+ return &md->kobj;
+}
+
int dm_suspended(struct mapped_device *md)
{
return test_bit(DMF_SUSPENDED, &md->flags);
diff -Naur linux-2.6.0-test9/drivers/md/dm.h linux-2.6.0-test9-dm/drivers/md/dm.h
--- linux-2.6.0-test9/drivers/md/dm.h 2003-10-25 11:43:34.000000000 -0700
+++ linux-2.6.0-test9-dm/drivers/md/dm.h 2003-11-09 15:07:54.000000000 -0800
@@ -48,17 +48,33 @@
struct mapped_device;
/*-----------------------------------------------------------------
+ * Functions for registering a mapped_device,
+ * table and target.
+ *---------------------------------------------------------------*/
+int dm_register_md(struct kobject *parent_kobj, struct kobject *kobj);
+void dm_unregister_md(struct kobject *kobj);
+int dm_register_table(struct kobject *parent_kobj, struct kobject *kobj);
+void dm_unregister_table(struct kobject *kobj);
+void dm_sysfs_rename_table(struct kobject *kobj, const char *name);
+int dm_register_target(struct kobject *parent_kobj,
+ struct kobject *kobj, int i);
+void dm_unregister_target(struct kobject *kobj);
+
+/*-----------------------------------------------------------------
* Functions for manipulating a struct mapped_device.
* Drop the reference with dm_put when you finish with the object.
*---------------------------------------------------------------*/
int dm_create(struct mapped_device **md);
int dm_create_with_minor(unsigned int minor, struct mapped_device **md);
+void dm_free(struct mapped_device *md);
+struct mapped_device *dm_kobj_to_md(struct kobject *kobj);
/*
* Reference counting for md.
*/
void dm_get(struct mapped_device *md);
void dm_put(struct mapped_device *md);
+void dm_del(struct mapped_device *md);
/*
* A device can still be used while suspended, but I/O is deferred.
@@ -90,15 +106,20 @@
*/
struct gendisk *dm_disk(struct mapped_device *md);
int dm_suspended(struct mapped_device *md);
+struct kobject *dm_md_get_kobj(struct mapped_device *md);
+struct mapped_device *dm_kobj_to_md(struct kobject *kobj);
/*-----------------------------------------------------------------
* Functions for manipulating a table. Tables are also reference
* counted.
*---------------------------------------------------------------*/
-int dm_table_create(struct dm_table **result, int mode);
+int dm_table_create(struct mapped_device *md, struct dm_table **result,
+ int mode);
void dm_table_get(struct dm_table *t);
void dm_table_put(struct dm_table *t);
+void dm_table_del(struct dm_table *t);
+void dm_table_free(struct dm_table *t);
int dm_table_add_target(struct dm_table *t, const char *type,
sector_t start, sector_t len, char *params);
@@ -115,6 +136,8 @@
int dm_table_get_mode(struct dm_table *t);
void dm_table_suspend_targets(struct dm_table *t);
void dm_table_resume_targets(struct dm_table *t);
+struct kobject *dm_table_get_kobj(struct dm_table *t);
+struct dm_table *dm_kobj_to_table(struct kobject *kobj);
/*-----------------------------------------------------------------
* A registry of target types.
diff -Naur linux-2.6.0-test9/drivers/md/dm-ioctl-v1.c linux-2.6.0-test9-dm/drivers/md/dm-ioctl-v1.c
--- linux-2.6.0-test9/drivers/md/dm-ioctl-v1.c 2003-10-25 11:44:52.000000000 -0700
+++ linux-2.6.0-test9-dm/drivers/md/dm-ioctl-v1.c 2003-11-09 15:30:59.000000000 -0800
@@ -236,7 +236,7 @@
list_del(&hc->uuid_list);
list_del(&hc->name_list);
unregister_with_devfs(hc);
- dm_put(hc->md);
+ dm_del(hc->md);
free_cell(hc);
}
@@ -572,7 +572,7 @@
r = populate_table(t, param);
if (r) {
- dm_table_put(t);
+ dm_table_del(t);
return r;
}
@@ -582,7 +582,7 @@
r = dm_create(&md);
if (r) {
- dm_table_put(t);
+ dm_table_del(t);
return r;
}
@@ -590,16 +590,16 @@
r = dm_suspend(md);
if (r) {
DMWARN("suspend failed");
- dm_table_put(t);
- dm_put(md);
+ dm_table_del(t);
+ dm_del(md);
return r;
}
/* swap in the table */
r = dm_swap_table(md, t);
if (r) {
DMWARN("table swap failed");
- dm_table_put(t);
- dm_put(md);
+ dm_table_del(t);
+ dm_del(md);
return r;
}
@@ -607,8 +607,8 @@
r = dm_resume(md);
if (r) {
DMWARN("resume failed");
- dm_table_put(t);
- dm_put(md);
+ dm_table_del(t);
+ dm_del(md);
return r;
}
@@ -900,20 +900,20 @@
r = populate_table(t, param);
if (r) {
- dm_table_put(t);
+ dm_table_del(t);
return r;
}
md = find_device(param);
if (!md) {
- dm_table_put(t);
+ dm_table_del(t);
return -ENXIO;
}
r = dm_swap_table(md, t);
if (r) {
dm_put(md);
- dm_table_put(t);
+ dm_table_del(t);
return r;
}
dm_table_put(t); /* md will have taken its own reference */
diff -Naur linux-2.6.0-test9/drivers/md/dm-ioctl-v4.c linux-2.6.0-test9-dm/drivers/md/dm-ioctl-v4.c
--- linux-2.6.0-test9/drivers/md/dm-ioctl-v4.c 2003-10-25 11:42:47.000000000 -0700
+++ linux-2.6.0-test9-dm/drivers/md/dm-ioctl-v4.c 2003-11-08 14:24:37.000000000 -0800
@@ -238,9 +238,9 @@
list_del(&hc->uuid_list);
list_del(&hc->name_list);
unregister_with_devfs(hc);
- dm_put(hc->md);
+ dm_del(hc->md);
if (hc->new_map)
- dm_table_put(hc->new_map);
+ dm_table_del(hc->new_map);
free_cell(hc);
}
@@ -490,7 +490,7 @@
r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md);
if (r) {
- dm_put(md);
+ dm_del(md);
return r;
}
@@ -646,8 +646,8 @@
r = dm_swap_table(md, new_map);
if (r) {
- dm_put(md);
- dm_table_put(new_map);
+ dm_del(md);
+ dm_table_del(new_map);
return r;
}
@@ -872,30 +872,32 @@
struct hash_cell *hc;
struct dm_table *t;
- r = dm_table_create(&t, get_mode(param));
- if (r)
- return r;
-
- r = populate_table(t, param, param_size);
- if (r) {
- dm_table_put(t);
- return r;
- }
-
down_write(&_hash_lock);
hc = __find_device_hash_cell(param);
if (!hc) {
DMWARN("device doesn't appear to be in the dev hash table.");
- up_write(&_hash_lock);
- return -ENXIO;
+ r = -ENXIO;
+ goto hash_unlock;
+ }
+
+ r = dm_table_create(hc->md, &t, get_mode(param));
+ if (r)
+ goto hash_unlock;
+
+ r = populate_table(t, param, param_size);
+ if (r) {
+ dm_table_del(t);
+ goto hash_unlock;
}
if (hc->new_map)
- dm_table_put(hc->new_map);
+ dm_table_del(hc->new_map);
hc->new_map = t;
param->flags |= DM_INACTIVE_PRESENT_FLAG;
r = __dev_status(hc->md, param);
+
+ hash_unlock:
up_write(&_hash_lock);
return r;
}
@@ -915,7 +917,7 @@
}
if (hc->new_map) {
- dm_table_put(hc->new_map);
+ dm_table_del(hc->new_map);
hc->new_map = NULL;
}
diff -Naur linux-2.6.0-test9/drivers/md/dm-kobj.c linux-2.6.0-test9-dm/drivers/md/dm-kobj.c
--- linux-2.6.0-test9/drivers/md/dm-kobj.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.0-test9-dm/drivers/md/dm-kobj.c 2003-11-10 17:43:20.000000000 -0800
@@ -0,0 +1,334 @@
+/*
+ * dm-kobj.c - driver model and sysfs code for Device Mapper
+ * Written by Mike Christie (mikenc@xxxxxxxxxx)
+ * Copyright (C) 2003 IBM Corp.
+ */
+
+#include "dm.h"
+
+#include <linux/kobject.h>
+#include <linux/dm-ioctl-v4.h>
+
+
+static inline struct dm_attribute *to_dm_attr(struct attribute *attr)
+{
+ return container_of((attr), struct dm_attribute, attr);
+}
+
+static ssize_t dm_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct dm_attribute *dm_attr = to_dm_attr(attr);
+
+ if (!dm_attr->show)
+ return 0;
+
+ return dm_attr->show(kobj, buf);
+}
+
+static ssize_t dm_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *page, size_t length)
+{
+ struct dm_attribute *dm_attr = to_dm_attr(attr);
+
+ if (!dm_attr->store)
+ return -EINVAL;
+
+ return dm_attr->store(kobj, page, length);
+}
+
+static struct sysfs_ops dm_sysfs_ops = {
+ .show = dm_attr_show,
+ .store = dm_attr_store,
+};
+
+/*********************************************************
+ *
+ * device mapper dm_dev sysfs functions
+ *
+ *********************************************************/
+
+static void device_kobj_release(struct kobject *kobj)
+{
+ kobject_put(kobj->parent);
+}
+
+static struct kobj_type device_ktype = {
+ .release = device_kobj_release,
+ .sysfs_ops = &dm_sysfs_ops,
+};
+
+int dm_register_dev(struct kobject *parent, struct kobject *kobj,
+ struct attribute_group *attrs, struct dm_dev *dev)
+{
+ int err = 0;
+ struct gendisk *disk;
+
+ if (!kobj || !parent || !dev)
+ return -EINVAL;
+
+ disk = dev->bdev->bd_disk;
+ kobj->parent = kobject_get(parent);
+ snprintf(kobj->name, KOBJ_NAME_LEN, "%s",
+ disk->disk_name);
+ kobj->ktype = &device_ktype;
+
+ err = kobject_register(kobj);
+ if (err)
+ goto put_parent;
+
+ if (attrs) {
+ err = sysfs_create_group(kobj, attrs);
+ if (err)
+ goto unreg;
+ }
+
+ err = sysfs_create_link(kobj, &disk->kobj,
+ disk->disk_name);
+ if (err)
+ goto rm_group;
+
+ return 0;
+
+
+ rm_group:
+ if (attrs)
+ sysfs_remove_group(kobj, attrs);
+ unreg:
+ kobject_unregister(kobj);
+ put_parent:
+ kobj->parent = NULL;
+ kobject_put(parent);
+
+ return err;
+}
+
+void dm_unregister_dev(struct kobject *kobj, struct dm_dev *dev)
+{
+ struct gendisk *disk;
+
+ if (kobj && dev) {
+ disk = dev->bdev->bd_disk;
+ sysfs_remove_link(kobj, disk->disk_name);
+ kobject_unregister(kobj);
+ }
+}
+
+/*********************************************************
+ *
+ * device mapper dm_target sysfs functions
+ *
+ *********************************************************/
+
+struct dm_target *dm_kobj_to_target(struct kobject *kobj)
+{
+ return container_of(kobj, struct dm_target, kobj);
+}
+
+static ssize_t target_show_begin(struct kobject *kobj, char *buf)
+{
+ struct dm_target *tgt = dm_kobj_to_target(kobj);
+ return snprintf(buf, 20, ""SECTOR_FORMAT"\n", tgt->begin);
+}
+
+static ssize_t target_show_len(struct kobject *kobj, char *buf)
+{
+ struct dm_target *tgt = dm_kobj_to_target(kobj);
+ return snprintf(buf, 20, ""SECTOR_FORMAT"\n", tgt->len);
+}
+
+static ssize_t target_show_type(struct kobject *kobj, char *buf)
+{
+ struct dm_target *tgt = dm_kobj_to_target(kobj);
+ return snprintf(buf, 32, "%s\n", tgt->type->name);
+}
+
+static DM_ATTR(start_sector, S_IRUGO, target_show_begin, NULL);
+static DM_ATTR(num_sectors, S_IRUGO, target_show_len, NULL);
+static DM_ATTR(type, S_IRUGO, target_show_type, NULL);
+
+static struct attribute *default_target_attrs[] = {
+ &dm_attr_type.attr,
+ &dm_attr_num_sectors.attr,
+ &dm_attr_start_sector.attr,
+ NULL
+};
+
+int dm_sysfs_create_group(struct kobject *kobj,
+ struct attribute_group *attr_group)
+{
+ return sysfs_create_group(kobj, attr_group);
+}
+
+static void target_kobj_release(struct kobject *kobj)
+{
+ kobject_put(kobj->parent);
+}
+
+static struct kobj_type target_ktype = {
+ .release = target_kobj_release,
+ .sysfs_ops = &dm_sysfs_ops,
+ .default_attrs = default_target_attrs,
+};
+
+int dm_register_target(struct kobject *parent, struct kobject *kobj, int i)
+{
+ int err;
+
+ if (!kobj || !parent)
+ return -EINVAL;
+
+ kobj->parent = kobject_get(parent);
+ snprintf(kobj->name, KOBJ_NAME_LEN, "%s%d", "target", i);
+ kobj->ktype = &target_ktype;
+
+ err = kobject_register(kobj);
+ if (err)
+ kobject_put(parent);
+
+ return err;
+}
+
+void dm_unregister_target(struct kobject *kobj)
+{
+ if (kobj) kobject_unregister(kobj);
+}
+
+/*********************************************************
+ *
+ * device mapper dm_table sysfs functions
+ *
+ *********************************************************/
+
+static void table_kobj_release(struct kobject *kobj)
+{
+ struct kobject *parent = kobj->parent;
+
+ dm_table_free(dm_kobj_to_table(kobj));
+ kobject_put(parent);
+}
+
+static struct kobj_type table_ktype = {
+ .release = table_kobj_release,
+};
+
+void dm_sysfs_rename_table(struct kobject *kobj, const char *name)
+{
+ sysfs_rename_dir(kobj, name);
+}
+
+int dm_register_table(struct kobject *parent, struct kobject *kobj)
+{
+ int err = 0;
+
+ if (!kobj || !parent)
+ return -EINVAL;
+
+ snprintf(kobj->name, KOBJ_NAME_LEN, "%s%p", ".table",
+ dm_kobj_to_table(kobj));
+ kobj->ktype = &table_ktype;
+ kobj->parent = kobject_get(parent);
+
+ err = kobject_register(kobj);
+ if (err) {
+ kobject_put(parent);
+ return err;
+ }
+
+ return err;
+}
+
+void dm_unregister_table(struct kobject *kobj)
+{
+ if (kobj) kobject_unregister(kobj);
+}
+
+/*********************************************************
+ *
+ * device mapper mapped_device sysfs functions
+ *
+ *********************************************************/
+
+static ssize_t md_show_event(struct kobject *kobj, char *buf)
+{
+ struct mapped_device *md = dm_kobj_to_md(kobj);
+ return snprintf(buf, 20, "%u\n", dm_get_event_nr(md));
+}
+
+static ssize_t md_show_flags(struct kobject *kobj, char *buf)
+{
+ unsigned int flags = 0;
+ struct gendisk *disk;
+ struct dm_table *tbl;
+ struct mapped_device *md = dm_kobj_to_md(kobj);
+
+ if (dm_suspended(md))
+ flags |= DM_SUSPEND_FLAG;
+
+ disk = dm_disk(md);
+ if (disk->policy)
+ flags |= DM_READONLY_FLAG;
+
+ tbl = dm_get_table(md);
+ if (tbl) {
+ flags |= DM_ACTIVE_PRESENT_FLAG;
+ dm_table_put(tbl);
+ }
+
+ return snprintf(buf, 20, "0x%x\n", flags);
+}
+
+static DM_ATTR(event_nr, S_IRUGO, md_show_event, NULL);
+static DM_ATTR(flags, S_IRUGO, md_show_flags, NULL);
+
+static struct attribute *md_attrs[] = {
+ &dm_attr_event_nr.attr,
+ &dm_attr_flags.attr,
+ NULL
+};
+
+static void md_kobj_release(struct kobject *kobj)
+{
+ struct mapped_device *md;
+ struct gendisk *disk;
+
+ md = dm_kobj_to_md(kobj);
+ disk = dm_disk(md);
+ dm_free(md);
+ kobject_put(&disk->kobj);
+}
+
+struct kobj_type md_ktype = {
+ .release = md_kobj_release,
+ .sysfs_ops = &dm_sysfs_ops,
+ .default_attrs = md_attrs,
+};
+
+int dm_register_md(struct kobject *parent, struct kobject *kobj)
+{
+ int err = 0;
+
+ if (!kobj || !parent)
+ return -EINVAL;
+
+ kobj->parent = kobject_get(parent);
+
+ snprintf(kobj->name, KOBJ_NAME_LEN, "%s", "mapped_device");
+ kobj->ktype = &md_ktype;
+
+ err = kobject_register(kobj);
+ if (err)
+ kobject_put(parent);
+
+ return err;
+}
+
+void dm_unregister_md(struct kobject *kobj)
+{
+ if (kobj) kobject_unregister(kobj);
+}
+
+EXPORT_SYMBOL(dm_register_dev);
+EXPORT_SYMBOL(dm_unregister_dev);
+EXPORT_SYMBOL(dm_kobj_to_target);
+EXPORT_SYMBOL(dm_sysfs_create_group);
diff -Naur linux-2.6.0-test9/drivers/md/dm-linear.c linux-2.6.0-test9-dm/drivers/md/dm-linear.c
--- linux-2.6.0-test9/drivers/md/dm-linear.c 2003-10-25 11:43:49.000000000 -0700
+++ linux-2.6.0-test9-dm/drivers/md/dm-linear.c 2003-11-08 15:44:33.000000000 -0800
@@ -18,6 +18,29 @@
struct linear_c {
struct dm_dev *dev;
sector_t start;
+ struct kobject kobj;
+};
+
+static inline struct linear_c *to_linear(struct kobject *kobj)
+{
+ return container_of(kobj, struct linear_c, kobj);
+}
+
+static ssize_t show_start(struct kobject *kobj, char *buf)
+{
+ struct linear_c *lc = to_linear(kobj);
+ return snprintf(buf, 32, ""SECTOR_FORMAT"\n", lc->start);
+}
+
+static DM_ATTR(start, S_IRUGO, show_start, NULL);
+
+static struct attribute *linear_attrs[] = {
+ &dm_attr_start.attr,
+ NULL
+};
+
+static struct attribute_group linear_attr_group = {
+ .attrs = linear_attrs,
};
/*
@@ -37,6 +60,7 @@
ti->error = "dm-linear: Cannot allocate linear context";
return -ENOMEM;
}
+ memset(lc, 0, sizeof(struct linear_c));
if (sscanf(argv[1], SECTOR_FORMAT, &lc->start) != 1) {
ti->error = "dm-linear: Invalid device sector";
@@ -49,6 +73,12 @@
goto bad;
}
+ if (dm_register_dev(&ti->kobj, &lc->kobj,
+ &linear_attr_group, lc->dev)) {
+ dm_put_device(lc->dev);
+ goto bad;
+ }
+
ti->private = lc;
return 0;
@@ -60,8 +90,13 @@
static void linear_dtr(struct dm_target *ti)
{
struct linear_c *lc = (struct linear_c *) ti->private;
+ dm_unregister_dev(&lc->kobj, lc->dev);
+}
- dm_put_device(ti, lc->dev);
+static void linear_free(struct dm_target *ti)
+{
+ struct linear_c *lc = (struct linear_c *) ti->private;
+ dm_put_device(lc->dev);
kfree(lc);
}
@@ -99,13 +134,14 @@
.module = THIS_MODULE,
.ctr = linear_ctr,
.dtr = linear_dtr,
+ .free = linear_free,
.map = linear_map,
.status = linear_status,
};
int __init dm_linear_init(void)
{
- int r = dm_register_target(&linear_target);
+ int r = dm_register_target_type(&linear_target);
if (r < 0)
DMERR("linear: register failed %d", r);
@@ -115,7 +151,7 @@
void dm_linear_exit(void)
{
- int r = dm_unregister_target(&linear_target);
+ int r = dm_unregister_target_type(&linear_target);
if (r < 0)
DMERR("linear: unregister failed %d", r);
diff -Naur linux-2.6.0-test9/drivers/md/dm-stripe.c linux-2.6.0-test9-dm/drivers/md/dm-stripe.c
--- linux-2.6.0-test9/drivers/md/dm-stripe.c 2003-10-25 11:43:35.000000000 -0700
+++ linux-2.6.0-test9-dm/drivers/md/dm-stripe.c 2003-11-08 16:17:33.000000000 -0800
@@ -15,6 +15,29 @@
struct stripe {
struct dm_dev *dev;
sector_t physical_start;
+ struct kobject kobj;
+};
+
+static inline struct stripe *to_stripe(struct kobject *kobj)
+{
+ return container_of(kobj, struct stripe, kobj);
+}
+
+static ssize_t show_start(struct kobject *kobj, char *buf)
+{
+ struct stripe *s = to_stripe(kobj);
+ return snprintf(buf, 32, ""SECTOR_FORMAT"\n", s->physical_start);
+}
+
+static DM_ATTR(start, S_IRUGO, show_start, NULL);
+
+static struct attribute *stripe_attrs[] = {
+ &dm_attr_start.attr,
+ NULL
+};
+
+static struct attribute_group stripe_attr_group = {
+ .attrs = stripe_attrs,
};
struct stripe_c {
@@ -30,6 +53,34 @@
struct stripe stripe[0];
};
+
+static ssize_t show_num_stripes(struct kobject *kobj, char *buf)
+{
+ struct dm_target *tgt = dm_kobj_to_target(kobj);
+ struct stripe_c *sc = (struct stripe_c *) tgt->private;
+ return snprintf(buf, 32, "%u\n", sc->stripes);
+}
+
+static ssize_t show_chunk_size(struct kobject *kobj, char *buf)
+{
+ struct dm_target *tgt = dm_kobj_to_target(kobj);
+ struct stripe_c *sc = (struct stripe_c *) tgt->private;
+ return snprintf(buf, 32, "%u\n", sc->stripe_width);
+}
+
+static DM_ATTR(num_stripes, S_IRUGO, show_num_stripes, NULL);
+static DM_ATTR(chunk_size, S_IRUGO, show_chunk_size, NULL);
+
+static struct attribute *stripe_c_attrs[] = {
+ &dm_attr_num_stripes.attr,
+ &dm_attr_chunk_size.attr,
+ NULL
+};
+
+static struct attribute_group stripe_c_attr_group = {
+ .attrs = stripe_c_attrs,
+};
+
static inline struct stripe_c *alloc_context(unsigned int stripes)
{
size_t len;
@@ -50,6 +101,8 @@
unsigned int stripe, char **argv)
{
sector_t start;
+
+ memset(&sc->stripe[stripe], 0, sizeof(struct stripe));
if (sscanf(argv[1], SECTOR_FORMAT, &start) != 1)
return -EINVAL;
@@ -144,13 +197,22 @@
if (r < 0) {
ti->error = "dm-stripe: Couldn't parse stripe "
"destination";
- while (i--)
- dm_put_device(ti, sc->stripe[i].dev);
+ while (i--) {
+ dm_unregister_dev(&sc->stripe[i].kobj,
+ sc->stripe[i].dev);
+ dm_put_device(sc->stripe[i].dev);
+ }
kfree(sc);
return r;
}
+
+ dm_register_dev(&ti->kobj,
+ &sc->stripe[i].kobj,
+ &stripe_attr_group,
+ sc->stripe[i].dev);
}
+ dm_sysfs_create_group(&ti->kobj, &stripe_c_attr_group);
ti->private = sc;
return 0;
}
@@ -161,7 +223,16 @@
struct stripe_c *sc = (struct stripe_c *) ti->private;
for (i = 0; i < sc->stripes; i++)
- dm_put_device(ti, sc->stripe[i].dev);
+ dm_unregister_dev(&sc->stripe[i].kobj, sc->stripe[i].dev);
+}
+
+static void stripe_free(struct dm_target *ti)
+{
+ unsigned int i;
+ struct stripe_c *sc = (struct stripe_c *) ti->private;
+
+ for (i = 0; i < sc->stripes; i++)
+ dm_put_device(sc->stripe[i].dev);
kfree(sc);
}
@@ -214,6 +285,7 @@
.module = THIS_MODULE,
.ctr = stripe_ctr,
.dtr = stripe_dtr,
+ .free = stripe_free,
.map = stripe_map,
.status = stripe_status,
};
@@ -222,7 +294,7 @@
{
int r;
- r = dm_register_target(&stripe_target);
+ r = dm_register_target_type(&stripe_target);
if (r < 0)
DMWARN("striped target registration failed");
@@ -231,7 +303,7 @@
void dm_stripe_exit(void)
{
- if (dm_unregister_target(&stripe_target))
+ if (dm_unregister_target_type(&stripe_target))
DMWARN("striped target unregistration failed");
return;
diff -Naur linux-2.6.0-test9/drivers/md/dm-table.c linux-2.6.0-test9-dm/drivers/md/dm-table.c
--- linux-2.6.0-test9/drivers/md/dm-table.c 2003-10-25 11:44:52.000000000 -0700
+++ linux-2.6.0-test9-dm/drivers/md/dm-table.c 2003-11-09 20:02:50.000000000 -0800
@@ -20,8 +20,6 @@
#define CHILDREN_PER_NODE (KEYS_PER_NODE + 1)
struct dm_table {
- atomic_t holders;
-
/* btree table */
unsigned int depth;
unsigned int counts[MAX_DEPTH]; /* in nodes */
@@ -51,6 +49,8 @@
/* events get handed up using this callback */
void (*event_fn)(void *);
void *event_context;
+
+ struct kobject kobj;
};
/*
@@ -202,8 +202,10 @@
return 0;
}
-int dm_table_create(struct dm_table **result, int mode)
+int dm_table_create(struct mapped_device *md,
+ struct dm_table **result, int mode)
{
+ int err = 0;
struct dm_table *t = kmalloc(sizeof(*t), GFP_NOIO);
if (!t)
@@ -211,18 +213,29 @@
memset(t, 0, sizeof(*t));
INIT_LIST_HEAD(&t->devices);
- atomic_set(&t->holders, 1);
/* allocate a single nodes worth of targets to begin with */
if (alloc_targets(t, KEYS_PER_NODE)) {
- kfree(t);
- t = NULL;
- return -ENOMEM;
+ err = -ENOMEM;
+ goto free_tbl;
}
+ err = dm_register_table(dm_md_get_kobj(md), &t->kobj);
+ if (err)
+ goto free_tgts;
+
t->mode = mode;
*result = t;
return 0;
+
+
+ free_tgts:
+ vfree(t->highs);
+ free_tbl:
+ kfree(t);
+ t = NULL;
+
+ return err;
}
static void free_devices(struct list_head *devices)
@@ -236,7 +249,7 @@
}
}
-void table_destroy(struct dm_table *t)
+void dm_table_free(struct dm_table *t)
{
unsigned int i;
@@ -248,9 +261,8 @@
for (i = 0; i < t->num_targets; i++) {
struct dm_target *tgt = t->targets + i;
- if (tgt->type->dtr)
- tgt->type->dtr(tgt);
-
+ if (tgt->type->free)
+ tgt->type->free(tgt);
dm_put_target_type(tgt->type);
}
@@ -269,13 +281,28 @@
void dm_table_get(struct dm_table *t)
{
- atomic_inc(&t->holders);
+ kobject_get(&t->kobj);
}
void dm_table_put(struct dm_table *t)
{
- if (atomic_dec_and_test(&t->holders))
- table_destroy(t);
+ kobject_put(&t->kobj);
+}
+
+void dm_table_del(struct dm_table *t)
+{
+ int i;
+
+ for (i = 0; i < t->num_targets; i++) {
+ struct dm_target *tgt = t->targets + i;
+
+ if (tgt->type->dtr)
+ tgt->type->dtr(tgt);
+
+ dm_unregister_target(&tgt->kobj);
+ }
+
+ dm_unregister_table(&t->kobj);
}
/*
@@ -460,7 +487,7 @@
if (!check_device_area(dd, start, len)) {
DMWARN("device %s too small for target", path);
- dm_put_device(ti, dd);
+ dm_put_device(dd);
return -EINVAL;
}
@@ -512,7 +539,7 @@
/*
* Decrement a devices use count and remove it if necessary.
*/
-void dm_put_device(struct dm_target *ti, struct dm_dev *dd)
+void dm_put_device(struct dm_dev *dd)
{
if (atomic_dec_and_test(&dd->count)) {
close_dev(dd);
@@ -637,6 +664,11 @@
tgt->begin = start;
tgt->len = len;
tgt->error = "Unknown error";
+ r = dm_register_target(&t->kobj, &tgt->kobj, t->num_targets);
+ if (r) {
+ tgt->error = "couldn't register target in sysfs";
+ goto put;
+ }
/*
* Does this target adjoin the previous one ?
@@ -644,19 +676,19 @@
if (!adjoin(t, tgt)) {
tgt->error = "Gap in table";
r = -EINVAL;
- goto bad;
+ goto unreg;
}
r = split_args(&argc, &argv, params);
if (r) {
tgt->error = "couldn't split parameters (insufficient memory)";
- goto bad;
+ goto unreg;
}
r = tgt->type->ctr(tgt, argc, argv);
kfree(argv);
if (r)
- goto bad;
+ goto unreg;
t->highs[t->num_targets++] = tgt->begin + tgt->len - 1;
@@ -665,9 +697,11 @@
combine_restrictions_low(&t->limits, &tgt->limits);
return 0;
- bad:
- printk(KERN_ERR DM_NAME ": %s\n", tgt->error);
+ unreg:
+ dm_unregister_target(&tgt->kobj);
+ put:
dm_put_target_type(tgt->type);
+ printk(KERN_ERR DM_NAME ": %s\n", tgt->error);
return r;
}
@@ -799,6 +833,16 @@
return t->mode;
}
+struct dm_table *dm_kobj_to_table(struct kobject *kobj)
+{
+ return container_of(kobj, struct dm_table, kobj);
+}
+
+struct kobject *dm_table_get_kobj(struct dm_table *t)
+{
+ return &t->kobj;
+}
+
void dm_table_suspend_targets(struct dm_table *t)
{
int i;
diff -Naur linux-2.6.0-test9/drivers/md/dm-target.c linux-2.6.0-test9-dm/drivers/md/dm-target.c
--- linux-2.6.0-test9/drivers/md/dm-target.c 2003-10-25 11:44:53.000000000 -0700
+++ linux-2.6.0-test9-dm/drivers/md/dm-target.c 2003-11-08 16:16:35.000000000 -0800
@@ -100,7 +100,7 @@
return ti;
}
-int dm_register_target(struct target_type *t)
+int dm_register_target_type(struct target_type *t)
{
int rv = 0;
struct tt_internal *ti = alloc_target(t);
@@ -121,7 +121,7 @@
return rv;
}
-int dm_unregister_target(struct target_type *t)
+int dm_unregister_target_type(struct target_type *t)
{
struct tt_internal *ti;
@@ -171,12 +171,12 @@
int dm_target_init(void)
{
- return dm_register_target(&error_target);
+ return dm_register_target_type(&error_target);
}
void dm_target_exit(void)
{
- if (dm_unregister_target(&error_target))
+ if (dm_unregister_target_type(&error_target))
DMWARN("error target unregistration failed");
}
diff -Naur linux-2.6.0-test9/drivers/md/Makefile linux-2.6.0-test9-dm/drivers/md/Makefile
--- linux-2.6.0-test9/drivers/md/Makefile 2003-10-25 11:43:49.000000000 -0700
+++ linux-2.6.0-test9-dm/drivers/md/Makefile 2003-11-09 20:30:42.000000000 -0800
@@ -3,7 +3,7 @@
#
dm-mod-objs := dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \
- dm-ioctl.o
+ dm-kobj.o dm-ioctl.o
# Note: link order is important. All raid personalities
# and xor.o must come before md.o, as they each initialise
diff -Naur linux-2.6.0-test9/include/linux/device-mapper.h linux-2.6.0-test9-dm/include/linux/device-mapper.h
--- linux-2.6.0-test9/include/linux/device-mapper.h 2003-10-25 11:44:05.000000000 -0700
+++ linux-2.6.0-test9-dm/include/linux/device-mapper.h 2003-11-09 20:03:44.000000000 -0800
@@ -22,11 +22,17 @@
/*
* The destructor doesn't need to free the dm_target, just
- * anything hidden ti->private.
+ * break down any structures registered in the constructor.
*/
typedef void (*dm_dtr_fn) (struct dm_target *ti);
/*
+ * This is called when it is safe to deallocate the
+ * the target's private structures.
+ */
+typedef void (*dm_free_fn) (struct dm_target *ti);
+
+/*
* The map function must return:
* < 0: error
* = 0: The target will handle the io by resubmitting it later
@@ -49,7 +55,7 @@
*/
int dm_get_device(struct dm_target *ti, const char *path, sector_t start,
sector_t len, int mode, struct dm_dev **result);
-void dm_put_device(struct dm_target *ti, struct dm_dev *d);
+void dm_put_device(struct dm_dev *d);
/*
* Information about a target type
@@ -59,6 +65,7 @@
struct module *module;
dm_ctr_fn ctr;
dm_dtr_fn dtr;
+ dm_free_fn free;
dm_map_fn map;
dm_suspend_fn suspend;
dm_resume_fn resume;
@@ -96,9 +103,40 @@
/* Used to provide an error string from the ctr */
char *error;
+
+ struct kobject kobj;
+ struct attribute_group *attr_group;
+};
+
+int dm_register_target_type(struct target_type *t);
+int dm_unregister_target_type(struct target_type *t);
+
+/*
+ * Functions and structures for displaying target
+ * and device attributes in sysfs. Look at dm-stripe
+ * and dm-linear for examples.
+ */
+
+struct dm_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct kobject *, char *);
+ ssize_t (*store)(struct kobject *, const char *, size_t);
+};
+
+#define DM_ATTR(_name,_mode,_show,_store) \
+struct dm_attribute dm_attr_##_name = { \
+ .attr = {.name = __stringify(_name), \
+ .mode = _mode, \
+ .owner = THIS_MODULE }, \
+ .show = _show, \
+ .store = _store, \
};
-int dm_register_target(struct target_type *t);
-int dm_unregister_target(struct target_type *t);
+struct dm_target *dm_kobj_to_target(struct kobject *kobj);
+int dm_sysfs_create_group(struct kobject *kobj,
+ struct attribute_group *attr_group);
+int dm_register_dev(struct kobject *parent, struct kobject *kobj,
+ struct attribute_group *attrs, struct dm_dev *dev);
+void dm_unregister_dev(struct kobject *kobj, struct dm_dev *dev);
#endif /* _LINUX_DEVICE_MAPPER_H */