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 | ||
