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 24b702d649a9..db3d0d8296a0 100644 --- a/block/elevator.c +++ b/block/elevator.c | |||
@@ -120,15 +120,10 @@ static struct elevator_type *elevator_get(const char *name) | |||
120 | return e; | 120 | return e; |
121 | } | 121 | } |
122 | 122 | ||
123 | static int elevator_attach(request_queue_t *q, struct elevator_type *e, | 123 | static int elevator_attach(request_queue_t *q, struct elevator_queue *eq) |
124 | struct elevator_queue *eq) | ||
125 | { | 124 | { |
126 | int ret = 0; | 125 | int ret = 0; |
127 | 126 | ||
128 | memset(eq, 0, sizeof(*eq)); | ||
129 | eq->ops = &e->ops; | ||
130 | eq->elevator_type = e; | ||
131 | |||
132 | q->elevator = eq; | 127 | q->elevator = eq; |
133 | 128 | ||
134 | if (eq->ops->elevator_init_fn) | 129 | if (eq->ops->elevator_init_fn) |
@@ -154,6 +149,32 @@ static int __init elevator_setup(char *str) | |||
154 | 149 | ||
155 | __setup("elevator=", elevator_setup); | 150 | __setup("elevator=", elevator_setup); |
156 | 151 | ||
152 | static struct kobj_type elv_ktype; | ||
153 | |||
154 | static elevator_t *elevator_alloc(struct elevator_type *e) | ||
155 | { | ||
156 | elevator_t *eq = kmalloc(sizeof(elevator_t), GFP_KERNEL); | ||
157 | if (eq) { | ||
158 | memset(eq, 0, sizeof(*eq)); | ||
159 | eq->ops = &e->ops; | ||
160 | eq->elevator_type = e; | ||
161 | kobject_init(&eq->kobj); | ||
162 | snprintf(eq->kobj.name, KOBJ_NAME_LEN, "%s", "iosched"); | ||
163 | eq->kobj.ktype = &elv_ktype; | ||
164 | mutex_init(&eq->sysfs_lock); | ||
165 | } else { | ||
166 | elevator_put(e); | ||
167 | } | ||
168 | return eq; | ||
169 | } | ||
170 | |||
171 | static void elevator_release(struct kobject *kobj) | ||
172 | { | ||
173 | elevator_t *e = container_of(kobj, elevator_t, kobj); | ||
174 | elevator_put(e->elevator_type); | ||
175 | kfree(e); | ||
176 | } | ||
177 | |||
157 | int elevator_init(request_queue_t *q, char *name) | 178 | int elevator_init(request_queue_t *q, char *name) |
158 | { | 179 | { |
159 | struct elevator_type *e = NULL; | 180 | struct elevator_type *e = NULL; |
@@ -176,29 +197,26 @@ int elevator_init(request_queue_t *q, char *name) | |||
176 | e = elevator_get("noop"); | 197 | e = elevator_get("noop"); |
177 | } | 198 | } |
178 | 199 | ||
179 | eq = kmalloc(sizeof(struct elevator_queue), GFP_KERNEL); | 200 | eq = elevator_alloc(e); |
180 | if (!eq) { | 201 | if (!eq) |
181 | elevator_put(e); | ||
182 | return -ENOMEM; | 202 | return -ENOMEM; |
183 | } | ||
184 | 203 | ||
185 | ret = elevator_attach(q, e, eq); | 204 | ret = elevator_attach(q, eq); |
186 | if (ret) { | 205 | if (ret) |
187 | kfree(eq); | 206 | kobject_put(&eq->kobj); |
188 | elevator_put(e); | ||
189 | } | ||
190 | 207 | ||
191 | return ret; | 208 | return ret; |
192 | } | 209 | } |
193 | 210 | ||
194 | void elevator_exit(elevator_t *e) | 211 | void elevator_exit(elevator_t *e) |
195 | { | 212 | { |
213 | mutex_lock(&e->sysfs_lock); | ||
196 | if (e->ops->elevator_exit_fn) | 214 | if (e->ops->elevator_exit_fn) |
197 | e->ops->elevator_exit_fn(e); | 215 | e->ops->elevator_exit_fn(e); |
216 | e->ops = NULL; | ||
217 | mutex_unlock(&e->sysfs_lock); | ||
198 | 218 | ||
199 | elevator_put(e->elevator_type); | 219 | kobject_put(&e->kobj); |
200 | e->elevator_type = NULL; | ||
201 | kfree(e); | ||
202 | } | 220 | } |
203 | 221 | ||
204 | /* | 222 | /* |
@@ -627,26 +645,79 @@ void elv_completed_request(request_queue_t *q, struct request *rq) | |||
627 | } | 645 | } |
628 | } | 646 | } |
629 | 647 | ||
630 | int elv_register_queue(struct request_queue *q) | 648 | #define to_elv(atr) container_of((atr), struct elv_fs_entry, attr) |
649 | |||
650 | static ssize_t | ||
651 | elv_attr_show(struct kobject *kobj, struct attribute *attr, char *page) | ||
631 | { | 652 | { |
632 | elevator_t *e = q->elevator; | 653 | elevator_t *e = container_of(kobj, elevator_t, kobj); |
654 | struct elv_fs_entry *entry = to_elv(attr); | ||
655 | ssize_t error; | ||
633 | 656 | ||
634 | e->kobj.parent = kobject_get(&q->kobj); | 657 | if (!entry->show) |
635 | if (!e->kobj.parent) | 658 | return -EIO; |
636 | return -EBUSY; | ||
637 | 659 | ||
638 | snprintf(e->kobj.name, KOBJ_NAME_LEN, "%s", "iosched"); | 660 | mutex_lock(&e->sysfs_lock); |
639 | e->kobj.ktype = e->elevator_type->elevator_ktype; | 661 | error = e->ops ? entry->show(e, page) : -ENOENT; |
662 | mutex_unlock(&e->sysfs_lock); | ||
663 | return error; | ||
664 | } | ||
665 | |||
666 | static ssize_t | ||
667 | elv_attr_store(struct kobject *kobj, struct attribute *attr, | ||
668 | const char *page, size_t length) | ||
669 | { | ||
670 | elevator_t *e = container_of(kobj, elevator_t, kobj); | ||
671 | struct elv_fs_entry *entry = to_elv(attr); | ||
672 | ssize_t error; | ||
673 | |||
674 | if (!entry->store) | ||
675 | return -EIO; | ||
676 | |||
677 | mutex_lock(&e->sysfs_lock); | ||
678 | error = e->ops ? entry->store(e, page, length) : -ENOENT; | ||
679 | mutex_unlock(&e->sysfs_lock); | ||
680 | return error; | ||
681 | } | ||
682 | |||
683 | static struct sysfs_ops elv_sysfs_ops = { | ||
684 | .show = elv_attr_show, | ||
685 | .store = elv_attr_store, | ||
686 | }; | ||
687 | |||
688 | static struct kobj_type elv_ktype = { | ||
689 | .sysfs_ops = &elv_sysfs_ops, | ||
690 | .release = elevator_release, | ||
691 | }; | ||
640 | 692 | ||
641 | return kobject_register(&e->kobj); | 693 | int elv_register_queue(struct request_queue *q) |
694 | { | ||
695 | elevator_t *e = q->elevator; | ||
696 | int error; | ||
697 | |||
698 | e->kobj.parent = &q->kobj; | ||
699 | |||
700 | error = kobject_add(&e->kobj); | ||
701 | if (!error) { | ||
702 | struct elv_fs_entry *attr = e->elevator_type->elevator_attrs; | ||
703 | if (attr) { | ||
704 | while (attr->attr.name) { | ||
705 | if (sysfs_create_file(&e->kobj, &attr->attr)) | ||
706 | break; | ||
707 | attr++; | ||
708 | } | ||
709 | } | ||
710 | kobject_uevent(&e->kobj, KOBJ_ADD); | ||
711 | } | ||
712 | return error; | ||
642 | } | 713 | } |
643 | 714 | ||
644 | void elv_unregister_queue(struct request_queue *q) | 715 | void elv_unregister_queue(struct request_queue *q) |
645 | { | 716 | { |
646 | if (q) { | 717 | if (q) { |
647 | elevator_t *e = q->elevator; | 718 | elevator_t *e = q->elevator; |
648 | kobject_unregister(&e->kobj); | 719 | kobject_uevent(&e->kobj, KOBJ_REMOVE); |
649 | kobject_put(&q->kobj); | 720 | kobject_del(&e->kobj); |
650 | } | 721 | } |
651 | } | 722 | } |
652 | 723 | ||
@@ -675,21 +746,15 @@ void elv_unregister(struct elevator_type *e) | |||
675 | /* | 746 | /* |
676 | * Iterate every thread in the process to remove the io contexts. | 747 | * Iterate every thread in the process to remove the io contexts. |
677 | */ | 748 | */ |
678 | read_lock(&tasklist_lock); | 749 | if (e->ops.trim) { |
679 | do_each_thread(g, p) { | 750 | read_lock(&tasklist_lock); |
680 | struct io_context *ioc = p->io_context; | 751 | do_each_thread(g, p) { |
681 | if (ioc && ioc->cic) { | 752 | task_lock(p); |
682 | ioc->cic->exit(ioc->cic); | 753 | e->ops.trim(p->io_context); |
683 | ioc->cic->dtor(ioc->cic); | 754 | task_unlock(p); |
684 | ioc->cic = NULL; | 755 | } while_each_thread(g, p); |
685 | } | 756 | read_unlock(&tasklist_lock); |
686 | if (ioc && ioc->aic) { | 757 | } |
687 | ioc->aic->exit(ioc->aic); | ||
688 | ioc->aic->dtor(ioc->aic); | ||
689 | ioc->aic = NULL; | ||
690 | } | ||
691 | } while_each_thread(g, p); | ||
692 | read_unlock(&tasklist_lock); | ||
693 | 758 | ||
694 | spin_lock_irq(&elv_list_lock); | 759 | spin_lock_irq(&elv_list_lock); |
695 | list_del_init(&e->list); | 760 | list_del_init(&e->list); |
@@ -703,16 +768,16 @@ EXPORT_SYMBOL_GPL(elv_unregister); | |||
703 | * need for the new one. this way we have a chance of going back to the old | 768 | * need for the new one. this way we have a chance of going back to the old |
704 | * one, if the new one fails init for some reason. | 769 | * one, if the new one fails init for some reason. |
705 | */ | 770 | */ |
706 | static void elevator_switch(request_queue_t *q, struct elevator_type *new_e) | 771 | static int elevator_switch(request_queue_t *q, struct elevator_type *new_e) |
707 | { | 772 | { |
708 | elevator_t *old_elevator, *e; | 773 | elevator_t *old_elevator, *e; |
709 | 774 | ||
710 | /* | 775 | /* |
711 | * Allocate new elevator | 776 | * Allocate new elevator |
712 | */ | 777 | */ |
713 | e = kmalloc(sizeof(elevator_t), GFP_KERNEL); | 778 | e = elevator_alloc(new_e); |
714 | if (!e) | 779 | if (!e) |
715 | goto error; | 780 | return 0; |
716 | 781 | ||
717 | /* | 782 | /* |
718 | * Turn on BYPASS and drain all requests w/ elevator private data | 783 | * 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) | |||
743 | /* | 808 | /* |
744 | * attach and start new elevator | 809 | * attach and start new elevator |
745 | */ | 810 | */ |
746 | if (elevator_attach(q, new_e, e)) | 811 | if (elevator_attach(q, e)) |
747 | goto fail; | 812 | goto fail; |
748 | 813 | ||
749 | if (elv_register_queue(q)) | 814 | if (elv_register_queue(q)) |
@@ -754,7 +819,7 @@ static void elevator_switch(request_queue_t *q, struct elevator_type *new_e) | |||
754 | */ | 819 | */ |
755 | elevator_exit(old_elevator); | 820 | elevator_exit(old_elevator); |
756 | clear_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags); | 821 | clear_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags); |
757 | return; | 822 | return 1; |
758 | 823 | ||
759 | fail_register: | 824 | fail_register: |
760 | /* | 825 | /* |
@@ -767,10 +832,9 @@ fail: | |||
767 | q->elevator = old_elevator; | 832 | q->elevator = old_elevator; |
768 | elv_register_queue(q); | 833 | elv_register_queue(q); |
769 | clear_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags); | 834 | clear_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags); |
770 | kfree(e); | 835 | if (e) |
771 | error: | 836 | kobject_put(&e->kobj); |
772 | elevator_put(new_e); | 837 | return 0; |
773 | printk(KERN_ERR "elevator: switch to %s failed\n",new_e->elevator_name); | ||
774 | } | 838 | } |
775 | 839 | ||
776 | ssize_t elv_iosched_store(request_queue_t *q, const char *name, size_t count) | 840 | 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) | |||
797 | return count; | 861 | return count; |
798 | } | 862 | } |
799 | 863 | ||
800 | elevator_switch(q, e); | 864 | if (!elevator_switch(q, e)) |
865 | printk(KERN_ERR "elevator: switch to %s failed\n",elevator_name); | ||
801 | return count; | 866 | return count; |
802 | } | 867 | } |
803 | 868 | ||