]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - block/blk-ioc.c
bio_copy_user_iov(): don't ignore ->iov_offset
[mirror_ubuntu-artful-kernel.git] / block / blk-ioc.c
index b12f9c87b4c31cd76310dc56f8ec95c23086c7e0..63898d229cb90bccf30430a59b33378721585636 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/bio.h>
 #include <linux/blkdev.h>
 #include <linux/slab.h>
+#include <linux/sched/task.h>
 
 #include "blk.h"
 
@@ -36,8 +37,8 @@ static void icq_free_icq_rcu(struct rcu_head *head)
 }
 
 /*
- * Exit an icq. Called with both ioc and q locked for sq, only ioc locked for
- * mq.
+ * Exit an icq. Called with ioc locked for blk-mq, and with both ioc
+ * and queue locked for legacy.
  */
 static void ioc_exit_icq(struct io_cq *icq)
 {
@@ -54,7 +55,10 @@ static void ioc_exit_icq(struct io_cq *icq)
        icq->flags |= ICQ_EXITED;
 }
 
-/* Release an icq.  Called with both ioc and q locked. */
+/*
+ * Release an icq. Called with ioc locked for blk-mq, and with both ioc
+ * and queue locked for legacy.
+ */
 static void ioc_destroy_icq(struct io_cq *icq)
 {
        struct io_context *ioc = icq->ioc;
@@ -62,7 +66,6 @@ static void ioc_destroy_icq(struct io_cq *icq)
        struct elevator_type *et = q->elevator->type;
 
        lockdep_assert_held(&ioc->lock);
-       lockdep_assert_held(q->queue_lock);
 
        radix_tree_delete(&ioc->icq_tree, icq->q->id);
        hlist_del_init(&icq->ioc_node);
@@ -222,24 +225,40 @@ void exit_io_context(struct task_struct *task)
        put_io_context_active(ioc);
 }
 
+static void __ioc_clear_queue(struct list_head *icq_list)
+{
+       unsigned long flags;
+
+       while (!list_empty(icq_list)) {
+               struct io_cq *icq = list_entry(icq_list->next,
+                                              struct io_cq, q_node);
+               struct io_context *ioc = icq->ioc;
+
+               spin_lock_irqsave(&ioc->lock, flags);
+               ioc_destroy_icq(icq);
+               spin_unlock_irqrestore(&ioc->lock, flags);
+       }
+}
+
 /**
  * ioc_clear_queue - break any ioc association with the specified queue
  * @q: request_queue being cleared
  *
- * Walk @q->icq_list and exit all io_cq's.  Must be called with @q locked.
+ * Walk @q->icq_list and exit all io_cq's.
  */
 void ioc_clear_queue(struct request_queue *q)
 {
-       lockdep_assert_held(q->queue_lock);
+       LIST_HEAD(icq_list);
 
-       while (!list_empty(&q->icq_list)) {
-               struct io_cq *icq = list_entry(q->icq_list.next,
-                                              struct io_cq, q_node);
-               struct io_context *ioc = icq->ioc;
+       spin_lock_irq(q->queue_lock);
+       list_splice_init(&q->icq_list, &icq_list);
 
-               spin_lock(&ioc->lock);
-               ioc_destroy_icq(icq);
-               spin_unlock(&ioc->lock);
+       if (q->mq_ops) {
+               spin_unlock_irq(q->queue_lock);
+               __ioc_clear_queue(&icq_list);
+       } else {
+               __ioc_clear_queue(&icq_list);
+               spin_unlock_irq(q->queue_lock);
        }
 }