[PATCH] elevator_t lifetime rules and sysfs fixes
[linux-3.10.git] / block / elevator.c
index 0232df2..0d2db53 100644 (file)
@@ -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;
 }