+int elv_register(struct elevator_type *e)
+{
+ if (elevator_find(e->elevator_name))
+ BUG();
+
+ spin_lock_irq(&elv_list_lock);
+ list_add_tail(&e->list, &elv_list);
+ spin_unlock_irq(&elv_list_lock);
+
+ printk(KERN_INFO "io scheduler %s registered", e->elevator_name);
+ if (!strcmp(e->elevator_name, chosen_elevator))
+ printk(" (default)");
+ printk("\n");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(elv_register);
+
+void elv_unregister(struct elevator_type *e)
+{
+ spin_lock_irq(&elv_list_lock);
+ list_del_init(&e->list);
+ spin_unlock_irq(&elv_list_lock);
+}
+EXPORT_SYMBOL_GPL(elv_unregister);
+
+/*
+ * switch to new_e io scheduler. be careful not to introduce deadlocks -
+ * we don't free the old io scheduler, before we have allocated what we
+ * 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. we also do an intermediate
+ * switch to noop to ensure safety with stack-allocated requests, since they
+ * don't originate from the block layer allocator. noop is safe here, because
+ * it never needs to touch the elevator itself for completion events. DRAIN
+ * flags will make sure we don't touch it for additions either.
+ */
+static void elevator_switch(request_queue_t *q, struct elevator_type *new_e)
+{
+ elevator_t *e = kmalloc(sizeof(elevator_t), GFP_KERNEL);
+ struct elevator_type *noop_elevator = NULL;
+ elevator_t *old_elevator;
+
+ if (!e)
+ goto error;
+
+ /*
+ * first step, drain requests from the block freelist
+ */
+ blk_wait_queue_drained(q, 0);
+
+ /*
+ * unregister old elevator data
+ */
+ elv_unregister_queue(q);
+ old_elevator = q->elevator;
+
+ /*
+ * next step, switch to noop since it uses no private rq structures
+ * and doesn't allocate any memory for anything. then wait for any
+ * non-fs requests in-flight
+ */
+ noop_elevator = elevator_get("noop");
+ spin_lock_irq(q->queue_lock);
+ elevator_attach(q, noop_elevator, e);
+ spin_unlock_irq(q->queue_lock);
+
+ blk_wait_queue_drained(q, 1);
+
+ /*
+ * attach and start new elevator
+ */
+ if (elevator_attach(q, new_e, e))
+ goto fail;
+
+ if (elv_register_queue(q))
+ goto fail_register;
+
+ /*
+ * finally exit old elevator and start queue again
+ */
+ elevator_exit(old_elevator);
+ blk_finish_queue_drain(q);
+ elevator_put(noop_elevator);
+ return;
+
+fail_register:
+ /*
+ * switch failed, exit the new io scheduler and reattach the old
+ * one again (along with re-adding the sysfs dir)
+ */
+ elevator_exit(e);
+fail:
+ q->elevator = old_elevator;
+ elv_register_queue(q);
+ blk_finish_queue_drain(q);
+error:
+ if (noop_elevator)
+ elevator_put(noop_elevator);
+ elevator_put(new_e);
+ printk(KERN_ERR "elevator: switch to %s failed\n",new_e->elevator_name);
+}
+
+ssize_t elv_iosched_store(request_queue_t *q, const char *name, size_t count)
+{
+ char elevator_name[ELV_NAME_MAX];
+ struct elevator_type *e;
+
+ memset(elevator_name, 0, sizeof(elevator_name));
+ strncpy(elevator_name, name, sizeof(elevator_name));
+
+ if (elevator_name[strlen(elevator_name) - 1] == '\n')
+ elevator_name[strlen(elevator_name) - 1] = '\0';
+
+ e = elevator_get(elevator_name);
+ if (!e) {
+ printk(KERN_ERR "elevator: type %s not found\n", elevator_name);
+ return -EINVAL;
+ }
+
+ if (!strcmp(elevator_name, q->elevator->elevator_type->elevator_name))
+ return count;
+
+ elevator_switch(q, e);
+ return count;
+}
+
+ssize_t elv_iosched_show(request_queue_t *q, char *name)
+{
+ elevator_t *e = q->elevator;
+ struct elevator_type *elv = e->elevator_type;
+ struct list_head *entry;
+ int len = 0;
+
+ spin_lock_irq(q->queue_lock);
+ list_for_each(entry, &elv_list) {
+ struct elevator_type *__e;
+
+ __e = list_entry(entry, struct elevator_type, list);
+ if (!strcmp(elv->elevator_name, __e->elevator_name))
+ len += sprintf(name+len, "[%s] ", elv->elevator_name);
+ else
+ len += sprintf(name+len, "%s ", __e->elevator_name);
+ }
+ spin_unlock_irq(q->queue_lock);
+
+ len += sprintf(len+name, "\n");
+ return len;
+}
+