]> Pileus Git - ~andy/linux/blobdiff - block/elevator.c
x32: Add struct ucontext_x32
[~andy/linux] / block / elevator.c
index 66343d6917d0cc64bbeccb7f55572237b8f78aba..91e18f8af9becaace380cc687d279eb1f47cb79a 100644 (file)
@@ -61,8 +61,8 @@ static int elv_iosched_allow_merge(struct request *rq, struct bio *bio)
        struct request_queue *q = rq->q;
        struct elevator_queue *e = q->elevator;
 
-       if (e->ops->elevator_allow_merge_fn)
-               return e->ops->elevator_allow_merge_fn(q, rq, bio);
+       if (e->type->ops.elevator_allow_merge_fn)
+               return e->type->ops.elevator_allow_merge_fn(q, rq, bio);
 
        return 1;
 }
@@ -168,17 +168,13 @@ static struct elevator_type *elevator_get(const char *name)
        return e;
 }
 
-static void *elevator_init_queue(struct request_queue *q,
-                                struct elevator_queue *eq)
+static int elevator_init_queue(struct request_queue *q,
+                              struct elevator_queue *eq)
 {
-       return eq->ops->elevator_init_fn(q);
-}
-
-static void elevator_attach(struct request_queue *q, struct elevator_queue *eq,
-                          void *data)
-{
-       q->elevator = eq;
-       eq->elevator_data = data;
+       eq->elevator_data = eq->type->ops.elevator_init_fn(q);
+       if (eq->elevator_data)
+               return 0;
+       return -ENOMEM;
 }
 
 static char chosen_elevator[ELV_NAME_MAX];
@@ -207,8 +203,7 @@ static struct elevator_queue *elevator_alloc(struct request_queue *q,
        if (unlikely(!eq))
                goto err;
 
-       eq->ops = &e->ops;
-       eq->elevator_type = e;
+       eq->type = e;
        kobject_init(&eq->kobj, &elv_ktype);
        mutex_init(&eq->sysfs_lock);
 
@@ -232,7 +227,7 @@ static void elevator_release(struct kobject *kobj)
        struct elevator_queue *e;
 
        e = container_of(kobj, struct elevator_queue, kobj);
-       elevator_put(e->elevator_type);
+       elevator_put(e->type);
        kfree(e->hash);
        kfree(e);
 }
@@ -241,7 +236,7 @@ int elevator_init(struct request_queue *q, char *name)
 {
        struct elevator_type *e = NULL;
        struct elevator_queue *eq;
-       void *data;
+       int err;
 
        if (unlikely(q->elevator))
                return 0;
@@ -278,13 +273,13 @@ int elevator_init(struct request_queue *q, char *name)
        if (!eq)
                return -ENOMEM;
 
-       data = elevator_init_queue(q, eq);
-       if (!data) {
+       err = elevator_init_queue(q, eq);
+       if (err) {
                kobject_put(&eq->kobj);
-               return -ENOMEM;
+               return err;
        }
 
-       elevator_attach(q, eq, data);
+       q->elevator = eq;
        return 0;
 }
 EXPORT_SYMBOL(elevator_init);
@@ -292,9 +287,8 @@ EXPORT_SYMBOL(elevator_init);
 void elevator_exit(struct elevator_queue *e)
 {
        mutex_lock(&e->sysfs_lock);
-       if (e->ops->elevator_exit_fn)
-               e->ops->elevator_exit_fn(e);
-       e->ops = NULL;
+       if (e->type->ops.elevator_exit_fn)
+               e->type->ops.elevator_exit_fn(e);
        mutex_unlock(&e->sysfs_lock);
 
        kobject_put(&e->kobj);
@@ -504,8 +498,8 @@ int elv_merge(struct request_queue *q, struct request **req, struct bio *bio)
                return ELEVATOR_BACK_MERGE;
        }
 
-       if (e->ops->elevator_merge_fn)
-               return e->ops->elevator_merge_fn(q, req, bio);
+       if (e->type->ops.elevator_merge_fn)
+               return e->type->ops.elevator_merge_fn(q, req, bio);
 
        return ELEVATOR_NO_MERGE;
 }
@@ -548,8 +542,8 @@ void elv_merged_request(struct request_queue *q, struct request *rq, int type)
 {
        struct elevator_queue *e = q->elevator;
 
-       if (e->ops->elevator_merged_fn)
-               e->ops->elevator_merged_fn(q, rq, type);
+       if (e->type->ops.elevator_merged_fn)
+               e->type->ops.elevator_merged_fn(q, rq, type);
 
        if (type == ELEVATOR_BACK_MERGE)
                elv_rqhash_reposition(q, rq);
@@ -563,8 +557,8 @@ void elv_merge_requests(struct request_queue *q, struct request *rq,
        struct elevator_queue *e = q->elevator;
        const int next_sorted = next->cmd_flags & REQ_SORTED;
 
-       if (next_sorted && e->ops->elevator_merge_req_fn)
-               e->ops->elevator_merge_req_fn(q, rq, next);
+       if (next_sorted && e->type->ops.elevator_merge_req_fn)
+               e->type->ops.elevator_merge_req_fn(q, rq, next);
 
        elv_rqhash_reposition(q, rq);
 
@@ -581,8 +575,8 @@ void elv_bio_merged(struct request_queue *q, struct request *rq,
 {
        struct elevator_queue *e = q->elevator;
 
-       if (e->ops->elevator_bio_merged_fn)
-               e->ops->elevator_bio_merged_fn(q, rq, bio);
+       if (e->type->ops.elevator_bio_merged_fn)
+               e->type->ops.elevator_bio_merged_fn(q, rq, bio);
 }
 
 void elv_requeue_request(struct request_queue *q, struct request *rq)
@@ -608,12 +602,12 @@ void elv_drain_elevator(struct request_queue *q)
 
        lockdep_assert_held(q->queue_lock);
 
-       while (q->elevator->ops->elevator_dispatch_fn(q, 1))
+       while (q->elevator->type->ops.elevator_dispatch_fn(q, 1))
                ;
        if (q->nr_sorted && printed++ < 10) {
                printk(KERN_ERR "%s: forced dispatching is broken "
                       "(nr_sorted=%u), please report this\n",
-                      q->elevator->elevator_type->elevator_name, q->nr_sorted);
+                      q->elevator->type->elevator_name, q->nr_sorted);
        }
 }
 
@@ -702,7 +696,7 @@ void __elv_add_request(struct request_queue *q, struct request *rq, int where)
                 * rq cannot be accessed after calling
                 * elevator_add_req_fn.
                 */
-               q->elevator->ops->elevator_add_req_fn(q, rq);
+               q->elevator->type->ops.elevator_add_req_fn(q, rq);
                break;
 
        case ELEVATOR_INSERT_FLUSH:
@@ -731,8 +725,8 @@ struct request *elv_latter_request(struct request_queue *q, struct request *rq)
 {
        struct elevator_queue *e = q->elevator;
 
-       if (e->ops->elevator_latter_req_fn)
-               return e->ops->elevator_latter_req_fn(q, rq);
+       if (e->type->ops.elevator_latter_req_fn)
+               return e->type->ops.elevator_latter_req_fn(q, rq);
        return NULL;
 }
 
@@ -740,8 +734,8 @@ struct request *elv_former_request(struct request_queue *q, struct request *rq)
 {
        struct elevator_queue *e = q->elevator;
 
-       if (e->ops->elevator_former_req_fn)
-               return e->ops->elevator_former_req_fn(q, rq);
+       if (e->type->ops.elevator_former_req_fn)
+               return e->type->ops.elevator_former_req_fn(q, rq);
        return NULL;
 }
 
@@ -749,10 +743,8 @@ int elv_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask)
 {
        struct elevator_queue *e = q->elevator;
 
-       if (e->ops->elevator_set_req_fn)
-               return e->ops->elevator_set_req_fn(q, rq, gfp_mask);
-
-       rq->elevator_private[0] = NULL;
+       if (e->type->ops.elevator_set_req_fn)
+               return e->type->ops.elevator_set_req_fn(q, rq, gfp_mask);
        return 0;
 }
 
@@ -760,16 +752,16 @@ void elv_put_request(struct request_queue *q, struct request *rq)
 {
        struct elevator_queue *e = q->elevator;
 
-       if (e->ops->elevator_put_req_fn)
-               e->ops->elevator_put_req_fn(rq);
+       if (e->type->ops.elevator_put_req_fn)
+               e->type->ops.elevator_put_req_fn(rq);
 }
 
 int elv_may_queue(struct request_queue *q, int rw)
 {
        struct elevator_queue *e = q->elevator;
 
-       if (e->ops->elevator_may_queue_fn)
-               return e->ops->elevator_may_queue_fn(q, rw);
+       if (e->type->ops.elevator_may_queue_fn)
+               return e->type->ops.elevator_may_queue_fn(q, rw);
 
        return ELV_MQUEUE_MAY;
 }
@@ -804,8 +796,8 @@ void elv_completed_request(struct request_queue *q, struct request *rq)
        if (blk_account_rq(rq)) {
                q->in_flight[rq_is_sync(rq)]--;
                if ((rq->cmd_flags & REQ_SORTED) &&
-                   e->ops->elevator_completed_req_fn)
-                       e->ops->elevator_completed_req_fn(q, rq);
+                   e->type->ops.elevator_completed_req_fn)
+                       e->type->ops.elevator_completed_req_fn(q, rq);
        }
 }
 
@@ -823,7 +815,7 @@ elv_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
 
        e = container_of(kobj, struct elevator_queue, kobj);
        mutex_lock(&e->sysfs_lock);
-       error = e->ops ? entry->show(e, page) : -ENOENT;
+       error = e->type ? entry->show(e, page) : -ENOENT;
        mutex_unlock(&e->sysfs_lock);
        return error;
 }
@@ -841,7 +833,7 @@ elv_attr_store(struct kobject *kobj, struct attribute *attr,
 
        e = container_of(kobj, struct elevator_queue, kobj);
        mutex_lock(&e->sysfs_lock);
-       error = e->ops ? entry->store(e, page, length) : -ENOENT;
+       error = e->type ? entry->store(e, page, length) : -ENOENT;
        mutex_unlock(&e->sysfs_lock);
        return error;
 }
@@ -856,14 +848,13 @@ static struct kobj_type elv_ktype = {
        .release        = elevator_release,
 };
 
-int elv_register_queue(struct request_queue *q)
+int __elv_register_queue(struct request_queue *q, struct elevator_queue *e)
 {
-       struct elevator_queue *e = q->elevator;
        int error;
 
        error = kobject_add(&e->kobj, &q->kobj, "%s", "iosched");
        if (!error) {
-               struct elv_fs_entry *attr = e->elevator_type->elevator_attrs;
+               struct elv_fs_entry *attr = e->type->elevator_attrs;
                if (attr) {
                        while (attr->attr.name) {
                                if (sysfs_create_file(&e->kobj, &attr->attr))
@@ -876,31 +867,55 @@ int elv_register_queue(struct request_queue *q)
        }
        return error;
 }
-EXPORT_SYMBOL(elv_register_queue);
 
-static void __elv_unregister_queue(struct elevator_queue *e)
+int elv_register_queue(struct request_queue *q)
 {
-       kobject_uevent(&e->kobj, KOBJ_REMOVE);
-       kobject_del(&e->kobj);
-       e->registered = 0;
+       return __elv_register_queue(q, q->elevator);
 }
+EXPORT_SYMBOL(elv_register_queue);
 
 void elv_unregister_queue(struct request_queue *q)
 {
-       if (q)
-               __elv_unregister_queue(q->elevator);
+       if (q) {
+               struct elevator_queue *e = q->elevator;
+
+               kobject_uevent(&e->kobj, KOBJ_REMOVE);
+               kobject_del(&e->kobj);
+               e->registered = 0;
+       }
 }
 EXPORT_SYMBOL(elv_unregister_queue);
 
-void elv_register(struct elevator_type *e)
+int elv_register(struct elevator_type *e)
 {
        char *def = "";
 
+       /* create icq_cache if requested */
+       if (e->icq_size) {
+               if (WARN_ON(e->icq_size < sizeof(struct io_cq)) ||
+                   WARN_ON(e->icq_align < __alignof__(struct io_cq)))
+                       return -EINVAL;
+
+               snprintf(e->icq_cache_name, sizeof(e->icq_cache_name),
+                        "%s_io_cq", e->elevator_name);
+               e->icq_cache = kmem_cache_create(e->icq_cache_name, e->icq_size,
+                                                e->icq_align, 0, NULL);
+               if (!e->icq_cache)
+                       return -ENOMEM;
+       }
+
+       /* register, don't allow duplicate names */
        spin_lock(&elv_list_lock);
-       BUG_ON(elevator_find(e->elevator_name));
+       if (elevator_find(e->elevator_name)) {
+               spin_unlock(&elv_list_lock);
+               if (e->icq_cache)
+                       kmem_cache_destroy(e->icq_cache);
+               return -EBUSY;
+       }
        list_add_tail(&e->list, &elv_list);
        spin_unlock(&elv_list_lock);
 
+       /* print pretty message */
        if (!strcmp(e->elevator_name, chosen_elevator) ||
                        (!*chosen_elevator &&
                         !strcmp(e->elevator_name, CONFIG_DEFAULT_IOSCHED)))
@@ -908,30 +923,26 @@ void elv_register(struct elevator_type *e)
 
        printk(KERN_INFO "io scheduler %s registered%s\n", e->elevator_name,
                                                                def);
+       return 0;
 }
 EXPORT_SYMBOL_GPL(elv_register);
 
 void elv_unregister(struct elevator_type *e)
 {
-       struct task_struct *g, *p;
+       /* unregister */
+       spin_lock(&elv_list_lock);
+       list_del_init(&e->list);
+       spin_unlock(&elv_list_lock);
 
        /*
-        * Iterate every thread in the process to remove the io contexts.
+        * Destroy icq_cache if it exists.  icq's are RCU managed.  Make
+        * sure all RCU operations are complete before proceeding.
         */
-       if (e->ops.trim) {
-               read_lock(&tasklist_lock);
-               do_each_thread(g, p) {
-                       task_lock(p);
-                       if (p->io_context)
-                               e->ops.trim(p->io_context);
-                       task_unlock(p);
-               } while_each_thread(g, p);
-               read_unlock(&tasklist_lock);
+       if (e->icq_cache) {
+               rcu_barrier();
+               kmem_cache_destroy(e->icq_cache);
+               e->icq_cache = NULL;
        }
-
-       spin_lock(&elv_list_lock);
-       list_del_init(&e->list);
-       spin_unlock(&elv_list_lock);
 }
 EXPORT_SYMBOL_GPL(elv_unregister);
 
@@ -944,54 +955,41 @@ EXPORT_SYMBOL_GPL(elv_unregister);
 static int elevator_switch(struct request_queue *q, struct elevator_type *new_e)
 {
        struct elevator_queue *old_elevator, *e;
-       void *data;
        int err;
 
-       /*
-        * Allocate new elevator
-        */
+       /* allocate new elevator */
        e = elevator_alloc(q, new_e);
        if (!e)
                return -ENOMEM;
 
-       data = elevator_init_queue(q, e);
-       if (!data) {
+       err = elevator_init_queue(q, e);
+       if (err) {
                kobject_put(&e->kobj);
-               return -ENOMEM;
+               return err;
        }
 
-       /*
-        * Turn on BYPASS and drain all requests w/ elevator private data
-        */
+       /* turn on BYPASS and drain all requests w/ elevator private data */
        elv_quiesce_start(q);
 
-       /*
-        * Remember old elevator.
-        */
-       old_elevator = q->elevator;
-
-       /*
-        * attach and start new elevator
-        */
-       spin_lock_irq(q->queue_lock);
-       elevator_attach(q, e, data);
-       spin_unlock_irq(q->queue_lock);
-
-       if (old_elevator->registered) {
-               __elv_unregister_queue(old_elevator);
-
-               err = elv_register_queue(q);
+       /* unregister old queue, register new one and kill old elevator */
+       if (q->elevator->registered) {
+               elv_unregister_queue(q);
+               err = __elv_register_queue(q, e);
                if (err)
                        goto fail_register;
        }
 
-       /*
-        * finally exit old elevator and turn off BYPASS.
-        */
+       /* done, clear io_cq's, switch elevators and turn off BYPASS */
+       spin_lock_irq(q->queue_lock);
+       ioc_clear_queue(q);
+       old_elevator = q->elevator;
+       q->elevator = e;
+       spin_unlock_irq(q->queue_lock);
+
        elevator_exit(old_elevator);
        elv_quiesce_end(q);
 
-       blk_add_trace_msg(q, "elv switch: %s", e->elevator_type->elevator_name);
+       blk_add_trace_msg(q, "elv switch: %s", e->type->elevator_name);
 
        return 0;
 
@@ -1001,7 +999,6 @@ fail_register:
         * one again (along with re-adding the sysfs dir)
         */
        elevator_exit(e);
-       q->elevator = old_elevator;
        elv_register_queue(q);
        elv_quiesce_end(q);
 
@@ -1026,7 +1023,7 @@ int elevator_change(struct request_queue *q, const char *name)
                return -EINVAL;
        }
 
-       if (!strcmp(elevator_name, q->elevator->elevator_type->elevator_name)) {
+       if (!strcmp(elevator_name, q->elevator->type->elevator_name)) {
                elevator_put(e);
                return 0;
        }
@@ -1061,7 +1058,7 @@ ssize_t elv_iosched_show(struct request_queue *q, char *name)
        if (!q->elevator || !blk_queue_stackable(q))
                return sprintf(name, "none\n");
 
-       elv = e->elevator_type;
+       elv = e->type;
 
        spin_lock(&elv_list_lock);
        list_for_each_entry(__e, &elv_list, list) {