diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2006-03-18 18:35:43 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2006-03-18 18:35:43 -0500 |
commit | 3d1ab40f4c20767afbd361b258a531d73e3e6fc2 (patch) | |
tree | 2da79cc8b47a98b0496b6e762fa790a8d547977b /block/elevator.c | |
parent | 1cc9be68ebcc1de9904bf225441613878da9c0d8 (diff) |
[PATCH] elevator_t lifetime rules and sysfs fixes
Diffstat (limited to 'block/elevator.c')
-rw-r--r-- | block/elevator.c | 146 |
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) | |||
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,78 @@ 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; | ||
656 | |||
657 | if (!entry->show) | ||
658 | return -EIO; | ||
659 | |||
660 | mutex_lock(&e->sysfs_lock); | ||
661 | error = e->ops ? entry->show(e, page) : -ENOENT; | ||
662 | mutex_unlock(&e->sysfs_lock); | ||
663 | return error; | ||
664 | } | ||
633 | 665 | ||
634 | e->kobj.parent = kobject_get(&q->kobj); | 666 | static ssize_t |
635 | if (!e->kobj.parent) | 667 | elv_attr_store(struct kobject *kobj, struct attribute *attr, |
636 | return -EBUSY; | 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; | ||
637 | 673 | ||
638 | snprintf(e->kobj.name, KOBJ_NAME_LEN, "%s", "iosched"); | 674 | if (!entry->store) |
639 | e->kobj.ktype = e->elevator_type->elevator_ktype; | 675 | return -EIO; |
640 | 676 | ||
641 | return kobject_register(&e->kobj); | 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 | }; | ||
692 | |||
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 attribute **attr = e->elevator_type->elevator_attrs; | ||
703 | if (attr) { | ||
704 | while (*attr) { | ||
705 | if (sysfs_create_file(&e->kobj,*attr++)) | ||
706 | break; | ||
707 | } | ||
708 | } | ||
709 | kobject_uevent(&e->kobj, KOBJ_ADD); | ||
710 | } | ||
711 | return error; | ||
642 | } | 712 | } |
643 | 713 | ||
644 | void elv_unregister_queue(struct request_queue *q) | 714 | void elv_unregister_queue(struct request_queue *q) |
645 | { | 715 | { |
646 | if (q) { | 716 | if (q) { |
647 | elevator_t *e = q->elevator; | 717 | elevator_t *e = q->elevator; |
648 | kobject_unregister(&e->kobj); | 718 | kobject_uevent(&e->kobj, KOBJ_REMOVE); |
649 | kobject_put(&q->kobj); | 719 | kobject_del(&e->kobj); |
650 | } | 720 | } |
651 | } | 721 | } |
652 | 722 | ||
@@ -697,16 +767,16 @@ EXPORT_SYMBOL_GPL(elv_unregister); | |||
697 | * need for the new one. this way we have a chance of going back to the old | 767 | * need for the new one. this way we have a chance of going back to the old |
698 | * one, if the new one fails init for some reason. | 768 | * one, if the new one fails init for some reason. |
699 | */ | 769 | */ |
700 | static void elevator_switch(request_queue_t *q, struct elevator_type *new_e) | 770 | static int elevator_switch(request_queue_t *q, struct elevator_type *new_e) |
701 | { | 771 | { |
702 | elevator_t *old_elevator, *e; | 772 | elevator_t *old_elevator, *e; |
703 | 773 | ||
704 | /* | 774 | /* |
705 | * Allocate new elevator | 775 | * Allocate new elevator |
706 | */ | 776 | */ |
707 | e = kmalloc(sizeof(elevator_t), GFP_KERNEL); | 777 | e = elevator_alloc(new_e); |
708 | if (!e) | 778 | if (!e) |
709 | goto error; | 779 | return 0; |
710 | 780 | ||
711 | /* | 781 | /* |
712 | * Turn on BYPASS and drain all requests w/ elevator private data | 782 | * 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) | |||
737 | /* | 807 | /* |
738 | * attach and start new elevator | 808 | * attach and start new elevator |
739 | */ | 809 | */ |
740 | if (elevator_attach(q, new_e, e)) | 810 | if (elevator_attach(q, e)) |
741 | goto fail; | 811 | goto fail; |
742 | 812 | ||
743 | if (elv_register_queue(q)) | 813 | if (elv_register_queue(q)) |
@@ -748,7 +818,7 @@ static void elevator_switch(request_queue_t *q, struct elevator_type *new_e) | |||
748 | */ | 818 | */ |
749 | elevator_exit(old_elevator); | 819 | elevator_exit(old_elevator); |
750 | clear_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags); | 820 | clear_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags); |
751 | return; | 821 | return 1; |
752 | 822 | ||
753 | fail_register: | 823 | fail_register: |
754 | /* | 824 | /* |
@@ -761,10 +831,9 @@ fail: | |||
761 | q->elevator = old_elevator; | 831 | q->elevator = old_elevator; |
762 | elv_register_queue(q); | 832 | elv_register_queue(q); |
763 | clear_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags); | 833 | clear_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags); |
764 | kfree(e); | 834 | if (e) |
765 | error: | 835 | kobject_put(&e->kobj); |
766 | elevator_put(new_e); | 836 | return 0; |
767 | printk(KERN_ERR "elevator: switch to %s failed\n",new_e->elevator_name); | ||
768 | } | 837 | } |
769 | 838 | ||
770 | ssize_t elv_iosched_store(request_queue_t *q, const char *name, size_t count) | 839 | 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) | |||
791 | return count; | 860 | return count; |
792 | } | 861 | } |
793 | 862 | ||
794 | elevator_switch(q, e); | 863 | if (!elevator_switch(q, e)) |
864 | printk(KERN_ERR "elevator: switch to %s failed\n",elevator_name); | ||
795 | return count; | 865 | return count; |
796 | } | 866 | } |
797 | 867 | ||