summaryrefslogtreecommitdiffstats
path: root/block/elevator.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2006-03-18 18:35:43 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2006-03-18 18:35:43 -0500
commit3d1ab40f4c20767afbd361b258a531d73e3e6fc2 (patch)
tree2da79cc8b47a98b0496b6e762fa790a8d547977b /block/elevator.c
parent1cc9be68ebcc1de9904bf225441613878da9c0d8 (diff)
downloadlinux-3d1ab40f4c20767afbd361b258a531d73e3e6fc2.tar.gz
linux-3d1ab40f4c20767afbd361b258a531d73e3e6fc2.tar.bz2
linux-3d1ab40f4c20767afbd361b258a531d73e3e6fc2.zip
[PATCH] elevator_t lifetime rules and sysfs fixes
Diffstat (limited to 'block/elevator.c')
-rw-r--r--block/elevator.c146
1 files changed, 108 insertions, 38 deletions
diff --git a/block/elevator.c b/block/elevator.c
index 0232df2b16e6..0d2db536c6a7 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -120,15 +120,10 @@ static struct elevator_type *elevator_get(const char *name)
return e;
}
-static int elevator_attach(request_queue_t *q, struct elevator_type *e,
- struct elevator_queue *eq)
+static int elevator_attach(request_queue_t *q, struct elevator_queue *eq)
{
int ret = 0;
- memset(eq, 0, sizeof(*eq));
- eq->ops = &e->ops;
- eq->elevator_type = e;
-
q->elevator = eq;
if (eq->ops->elevator_init_fn)
@@ -154,6 +149,32 @@ static int __init elevator_setup(char *str)
__setup("elevator=", elevator_setup);
+static struct kobj_type elv_ktype;
+
+static elevator_t *elevator_alloc(struct elevator_type *e)
+{
+ elevator_t *eq = kmalloc(sizeof(elevator_t), GFP_KERNEL);
+ if (eq) {
+ memset(eq, 0, sizeof(*eq));
+ eq->ops = &e->ops;
+ eq->elevator_type = e;
+ kobject_init(&eq->kobj);
+ snprintf(eq->kobj.name, KOBJ_NAME_LEN, "%s", "iosched");
+ eq->kobj.ktype = &elv_ktype;
+ mutex_init(&eq->sysfs_lock);
+ } else {
+ elevator_put(e);
+ }
+ return eq;
+}
+
+static void elevator_release(struct kobject *kobj)
+{
+ elevator_t *e = container_of(kobj, elevator_t, kobj);
+ elevator_put(e->elevator_type);
+ kfree(e);
+}
+
int elevator_init(request_queue_t *q, char *name)
{
struct elevator_type *e = NULL;
@@ -176,29 +197,26 @@ int elevator_init(request_queue_t *q, char *name)
e = elevator_get("noop");
}
- eq = kmalloc(sizeof(struct elevator_queue), GFP_KERNEL);
- if (!eq) {
- elevator_put(e);
+ eq = elevator_alloc(e);
+ if (!eq)
return -ENOMEM;
- }
- ret = elevator_attach(q, e, eq);
- if (ret) {
- kfree(eq);
- elevator_put(e);
- }
+ ret = elevator_attach(q, eq);
+ if (ret)
+ kobject_put(&eq->kobj);
return ret;
}
void elevator_exit(elevator_t *e)
{
+ mutex_lock(&e->sysfs_lock);
if (e->ops->elevator_exit_fn)
e->ops->elevator_exit_fn(e);
+ e->ops = NULL;
+ mutex_unlock(&e->sysfs_lock);
- elevator_put(e->elevator_type);
- e->elevator_type = NULL;
- kfree(e);
+ kobject_put(&e->kobj);
}
/*
@@ -627,26 +645,78 @@ void elv_completed_request(request_queue_t *q, struct request *rq)
}
}
-int elv_register_queue(struct request_queue *q)
+#define to_elv(atr) container_of((atr), struct elv_fs_entry, attr)
+
+static ssize_t
+elv_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
{
- elevator_t *e = q->elevator;
+ elevator_t *e = container_of(kobj, elevator_t, kobj);
+ struct elv_fs_entry *entry = to_elv(attr);
+ ssize_t error;
+
+ if (!entry->show)
+ return -EIO;
+
+ mutex_lock(&e->sysfs_lock);
+ error = e->ops ? entry->show(e, page) : -ENOENT;
+ mutex_unlock(&e->sysfs_lock);
+ return error;
+}
- e->kobj.parent = kobject_get(&q->kobj);
- if (!e->kobj.parent)
- return -EBUSY;
+static ssize_t
+elv_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *page, size_t length)
+{
+ elevator_t *e = container_of(kobj, elevator_t, kobj);
+ struct elv_fs_entry *entry = to_elv(attr);
+ ssize_t error;
- snprintf(e->kobj.name, KOBJ_NAME_LEN, "%s", "iosched");
- e->kobj.ktype = e->elevator_type->elevator_ktype;
+ if (!entry->store)
+ return -EIO;
- return kobject_register(&e->kobj);
+ mutex_lock(&e->sysfs_lock);
+ error = e->ops ? entry->store(e, page, length) : -ENOENT;
+ mutex_unlock(&e->sysfs_lock);
+ return error;
+}
+
+static struct sysfs_ops elv_sysfs_ops = {
+ .show = elv_attr_show,
+ .store = elv_attr_store,
+};
+
+static struct kobj_type elv_ktype = {
+ .sysfs_ops = &elv_sysfs_ops,
+ .release = elevator_release,
+};
+
+int elv_register_queue(struct request_queue *q)
+{
+ elevator_t *e = q->elevator;
+ int error;
+
+ e->kobj.parent = &q->kobj;
+
+ error = kobject_add(&e->kobj);
+ if (!error) {
+ struct attribute **attr = e->elevator_type->elevator_attrs;
+ if (attr) {
+ while (*attr) {
+ if (sysfs_create_file(&e->kobj,*attr++))
+ break;
+ }
+ }
+ kobject_uevent(&e->kobj, KOBJ_ADD);
+ }
+ return error;
}
void elv_unregister_queue(struct request_queue *q)
{
if (q) {
elevator_t *e = q->elevator;
- kobject_unregister(&e->kobj);
- kobject_put(&q->kobj);
+ kobject_uevent(&e->kobj, KOBJ_REMOVE);
+ kobject_del(&e->kobj);
}
}
@@ -697,16 +767,16 @@ EXPORT_SYMBOL_GPL(elv_unregister);
* need for the new one. this way we have a chance of going back to the old
* one, if the new one fails init for some reason.
*/
-static void elevator_switch(request_queue_t *q, struct elevator_type *new_e)
+static int elevator_switch(request_queue_t *q, struct elevator_type *new_e)
{
elevator_t *old_elevator, *e;
/*
* Allocate new elevator
*/
- e = kmalloc(sizeof(elevator_t), GFP_KERNEL);
+ e = elevator_alloc(new_e);
if (!e)
- goto error;
+ return 0;
/*
* Turn on BYPASS and drain all requests w/ elevator private data
@@ -737,7 +807,7 @@ static void elevator_switch(request_queue_t *q, struct elevator_type *new_e)
/*
* attach and start new elevator
*/
- if (elevator_attach(q, new_e, e))
+ if (elevator_attach(q, e))
goto fail;
if (elv_register_queue(q))
@@ -748,7 +818,7 @@ static void elevator_switch(request_queue_t *q, struct elevator_type *new_e)
*/
elevator_exit(old_elevator);
clear_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags);
- return;
+ return 1;
fail_register:
/*
@@ -761,10 +831,9 @@ fail:
q->elevator = old_elevator;
elv_register_queue(q);
clear_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags);
- kfree(e);
-error:
- elevator_put(new_e);
- printk(KERN_ERR "elevator: switch to %s failed\n",new_e->elevator_name);
+ if (e)
+ kobject_put(&e->kobj);
+ return 0;
}
ssize_t elv_iosched_store(request_queue_t *q, const char *name, size_t count)
@@ -791,7 +860,8 @@ ssize_t elv_iosched_store(request_queue_t *q, const char *name, size_t count)
return count;
}
- elevator_switch(q, e);
+ if (!elevator_switch(q, e))
+ printk(KERN_ERR "elevator: switch to %s failed\n",elevator_name);
return count;
}