diff options
Diffstat (limited to 'block/elevator.c')
-rw-r--r-- | block/elevator.c | 171 |
1 files changed, 118 insertions, 53 deletions
diff --git a/block/elevator.c b/block/elevator.c index 24b702d649a..db3d0d8296a 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,79 @@ 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; - e->kobj.parent = kobject_get(&q->kobj); - if (!e->kobj.parent) - return -EBUSY; + if (!entry->show) + return -EIO; - snprintf(e->kobj.name, KOBJ_NAME_LEN, "%s", "iosched"); - e->kobj.ktype = e->elevator_type->elevator_ktype; + mutex_lock(&e->sysfs_lock); + error = e->ops ? entry->show(e, page) : -ENOENT; + mutex_unlock(&e->sysfs_lock); + return error; +} + +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; + + if (!entry->store) + return -EIO; + + 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, +}; - return kobject_register(&e->kobj); +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 elv_fs_entry *attr = e->elevator_type->elevator_attrs; + if (attr) { + while (attr->attr.name) { + if (sysfs_create_file(&e->kobj, &attr->attr)) + break; + attr++; + } + } + 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); } } @@ -675,21 +746,15 @@ void elv_unregister(struct elevator_type *e) /* * Iterate every thread in the process to remove the io contexts. */ - read_lock(&tasklist_lock); - do_each_thread(g, p) { - struct io_context *ioc = p->io_context; - if (ioc && ioc->cic) { - ioc->cic->exit(ioc->cic); - ioc->cic->dtor(ioc->cic); - ioc->cic = NULL; - } - if (ioc && ioc->aic) { - ioc->aic->exit(ioc->aic); - ioc->aic->dtor(ioc->aic); - ioc->aic = NULL; - } - } while_each_thread(g, p); - read_unlock(&tasklist_lock); + if (e->ops.trim) { + read_lock(&tasklist_lock); + do_each_thread(g, p) { + task_lock(p); + e->ops.trim(p->io_context); + task_unlock(p); + } while_each_thread(g, p); + read_unlock(&tasklist_lock); + } spin_lock_irq(&elv_list_lock); list_del_init(&e->list); @@ -703,16 +768,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 @@ -743,7 +808,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)) @@ -754,7 +819,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: /* @@ -767,10 +832,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) @@ -797,7 +861,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; } |