]> git.ipfire.org Git - ipfire-2.x.git/blobdiff - src/patches/suse-2.6.27.25/patches.drivers/blk-request-based-multipath-update
Updated xen patches taken from suse.
[ipfire-2.x.git] / src / patches / suse-2.6.27.25 / patches.drivers / blk-request-based-multipath-update
diff --git a/src/patches/suse-2.6.27.25/patches.drivers/blk-request-based-multipath-update b/src/patches/suse-2.6.27.25/patches.drivers/blk-request-based-multipath-update
new file mode 100644 (file)
index 0000000..e2a0ec8
--- /dev/null
@@ -0,0 +1,360 @@
+From: Kiyoshi Ueda <k-ueda@ct.jp.nec.com>
+Subject: Block layer fixes for request-based multipathing
+References: References: FATE#302108
+
+This is a combined patch from linux-2.6.git. Commit-IDs:
+
+d6c578ec08b3f07050401ed83193b3f21729213b
+afac32f0c9c68698eaf7688d52de859301a0539f
+ebd2bf40e9cfa4ebfa614703944f4eafdf0d2c64
+509395182b6b7cf7e3c1ca2cd669506d8f43ee01
+88171cad9ace4b67c5298e6504d70454296afb76
+
+Signed-off-by: Hannes Reinecke <hare@suse.de>
+
+---
+ block/blk-core.c        |  169 +++++++++++++++++++++++++++++++++++++++++++++---
+ block/blk-settings.c    |    6 +
+ drivers/scsi/scsi_lib.c |   32 +++++++++
+ include/linux/blkdev.h  |   12 +++
+ 4 files changed, 209 insertions(+), 10 deletions(-)
+
+--- a/block/blk-core.c
++++ b/block/blk-core.c
+@@ -592,7 +592,8 @@ blk_init_queue_node(request_fn_proc *rfn
+       q->request_fn           = rfn;
+       q->prep_rq_fn           = NULL;
+       q->unplug_fn            = generic_unplug_device;
+-      q->queue_flags          = (1 << QUEUE_FLAG_CLUSTER);
++      q->queue_flags          = (1 << QUEUE_FLAG_CLUSTER |
++                                 1 << QUEUE_FLAG_STACKABLE);
+       q->queue_lock           = lock;
+       blk_queue_segment_boundary(q, 0xffffffff);
+@@ -1586,6 +1587,87 @@ void blkdev_dequeue_request(struct reque
+ EXPORT_SYMBOL(blkdev_dequeue_request);
+ /**
++ * blk_rq_check_limits - Helper function to check a request for the queue limit
++ * @q:  the queue
++ * @rq: the request being checked
++ *
++ * Description:
++ *    @rq may have been made based on weaker limitations of upper-level queues
++ *    in request stacking drivers, and it may violate the limitation of @q.
++ *    Since the block layer and the underlying device driver trust @rq
++ *    after it is inserted to @q, it should be checked against @q before
++ *    the insertion using this generic function.
++ *
++ *    This function should also be useful for request stacking drivers
++ *    in some cases below, so export this fuction.
++ *    Request stacking drivers like request-based dm may change the queue
++ *    limits while requests are in the queue (e.g. dm's table swapping).
++ *    Such request stacking drivers should check those requests agaist
++ *    the new queue limits again when they dispatch those requests,
++ *    although such checkings are also done against the old queue limits
++ *    when submitting requests.
++ */
++int blk_rq_check_limits(struct request_queue *q, struct request *rq)
++{
++      if (rq->nr_sectors > q->max_sectors ||
++          rq->data_len > q->max_hw_sectors << 9) {
++              printk(KERN_ERR "%s: over max size limit.\n", __func__);
++              return -EIO;
++      }
++
++      /*
++       * queue's settings related to segment counting like q->bounce_pfn
++       * may differ from that of other stacking queues.
++       * Recalculate it to check the request correctly on this queue's
++       * limitation.
++       */
++      blk_recalc_rq_segments(rq);
++      if (rq->nr_phys_segments > q->max_phys_segments ||
++          rq->nr_phys_segments > q->max_hw_segments) {
++              printk(KERN_ERR "%s: over max segments limit.\n", __func__);
++              return -EIO;
++      }
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(blk_rq_check_limits);
++
++/**
++ * blk_insert_cloned_request - Helper for stacking drivers to submit a request
++ * @q:  the queue to submit the request
++ * @rq: the request being queued
++ */
++int blk_insert_cloned_request(struct request_queue *q, struct request *rq)
++{
++      unsigned long flags;
++
++      if (blk_rq_check_limits(q, rq))
++              return -EIO;
++
++#ifdef CONFIG_FAIL_MAKE_REQUEST
++      if (rq->rq_disk && rq->rq_disk->part0.make_it_fail &&
++          should_fail(&fail_make_request, blk_rq_bytes(rq)))
++              return -EIO;
++#endif
++
++      spin_lock_irqsave(q->queue_lock, flags);
++
++      /*
++       * Submitting request must be dequeued before calling this function
++       * because it will be linked to another request_queue
++       */
++      BUG_ON(blk_queued_rq(rq));
++
++      drive_stat_acct(rq, 1);
++      __elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 0);
++
++      spin_unlock_irqrestore(q->queue_lock, flags);
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(blk_insert_cloned_request);
++
++/**
+  * __end_that_request_first - end I/O on a request
+  * @req:      the request being processed
+  * @error:    %0 for success, < %0 for error
+@@ -1857,6 +1939,22 @@ void end_request(struct request *req, in
+ }
+ EXPORT_SYMBOL(end_request);
++static int end_that_request_data(struct request *rq, int error,
++                               unsigned int nr_bytes, unsigned int bidi_bytes)
++{
++      if (rq->bio) {
++              if (__end_that_request_first(rq, error, nr_bytes))
++                      return 1;
++
++              /* Bidi request must be completed as a whole */
++              if (blk_bidi_rq(rq) &&
++                  __end_that_request_first(rq->next_rq, error, bidi_bytes))
++                      return 1;
++      }
++
++      return 0;
++}
++
+ /**
+  * blk_end_io - Generic end_io function to complete a request.
+  * @rq:           the request being processed
+@@ -1883,15 +1981,8 @@ static int blk_end_io(struct request *rq
+       struct request_queue *q = rq->q;
+       unsigned long flags = 0UL;
+-      if (rq->bio) {
+-              if (__end_that_request_first(rq, error, nr_bytes))
+-                      return 1;
+-
+-              /* Bidi request must be completed as a whole */
+-              if (blk_bidi_rq(rq) &&
+-                  __end_that_request_first(rq->next_rq, error, bidi_bytes))
+-                      return 1;
+-      }
++      if (end_that_request_data(rq, error, nr_bytes, bidi_bytes))
++              return 1;
+       /* Special feature for tricky drivers */
+       if (drv_callback && drv_callback(rq))
+@@ -1974,6 +2065,36 @@ int blk_end_bidi_request(struct request 
+ EXPORT_SYMBOL_GPL(blk_end_bidi_request);
+ /**
++ * blk_update_request - Special helper function for request stacking drivers
++ * @rq:           the request being processed
++ * @error:        %0 for success, < %0 for error
++ * @nr_bytes:     number of bytes to complete @rq
++ *
++ * Description:
++ *     Ends I/O on a number of bytes attached to @rq, but doesn't complete
++ *     the request structure even if @rq doesn't have leftover.
++ *     If @rq has leftover, sets it up for the next range of segments.
++ *
++ *     This special helper function is only for request stacking drivers
++ *     (e.g. request-based dm) so that they can handle partial completion.
++ *     Actual device drivers should use blk_end_request instead.
++ */
++void blk_update_request(struct request *rq, int error, unsigned int nr_bytes)
++{
++      if (!end_that_request_data(rq, error, nr_bytes, 0)) {
++              /*
++               * These members are not updated in end_that_request_data()
++               * when all bios are completed.
++               * Update them so that the request stacking driver can find
++               * how many bytes remain in the request later.
++               */
++              rq->nr_sectors = rq->hard_nr_sectors = 0;
++              rq->current_nr_sectors = rq->hard_cur_sectors = 0;
++      }
++}
++EXPORT_SYMBOL_GPL(blk_update_request);
++
++/**
+  * blk_end_request_callback - Special helper function for tricky drivers
+  * @rq:           the request being processed
+  * @error:        %0 for success, < %0 for error
+@@ -2028,6 +2149,34 @@ void blk_rq_bio_prep(struct request_queu
+               rq->rq_disk = bio->bi_bdev->bd_disk;
+ }
++/**
++ * blk_lld_busy - Check if underlying low-level drivers of a device are busy
++ * @q : the queue of the device being checked
++ *
++ * Description:
++ *    Check if underlying low-level drivers of a device are busy.
++ *    If the drivers want to export their busy state, they must set own
++ *    exporting function using blk_queue_lld_busy() first.
++ *
++ *    Basically, this function is used only by request stacking drivers
++ *    to stop dispatching requests to underlying devices when underlying
++ *    devices are busy.  This behavior helps more I/O merging on the queue
++ *    of the request stacking driver and prevents I/O throughput regression
++ *    on burst I/O load.
++ *
++ * Return:
++ *    0 - Not busy (The request stacking driver should dispatch request)
++ *    1 - Busy (The request stacking driver should stop dispatching request)
++ */
++int blk_lld_busy(struct request_queue *q)
++{
++      if (q->lld_busy_fn)
++              return q->lld_busy_fn(q);
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(blk_lld_busy);
++
+ int kblockd_schedule_work(struct request_queue *q, struct work_struct *work)
+ {
+       return queue_work(kblockd_workqueue, work);
+--- a/block/blk-settings.c
++++ b/block/blk-settings.c
+@@ -89,6 +89,12 @@ void blk_queue_rq_timed_out(struct reque
+ }
+ EXPORT_SYMBOL_GPL(blk_queue_rq_timed_out);
++void blk_queue_lld_busy(struct request_queue *q, lld_busy_fn *fn)
++{
++      q->lld_busy_fn = fn;
++}
++EXPORT_SYMBOL_GPL(blk_queue_lld_busy);
++
+ /**
+  * blk_queue_make_request - define an alternate make_request function for a device
+  * @q:  the request queue for the device to be affected
+--- a/drivers/scsi/scsi_lib.c
++++ b/drivers/scsi/scsi_lib.c
+@@ -1465,6 +1465,37 @@ static inline int scsi_host_queue_ready(
+ }
+ /*
++ * Busy state exporting function for request stacking drivers.
++ *
++ * For efficiency, no lock is taken to check the busy state of
++ * shost/starget/sdev, since the returned value is not guaranteed and
++ * may be changed after request stacking drivers call the function,
++ * regardless of taking lock or not.
++ *
++ * When scsi can't dispatch I/Os anymore and needs to kill I/Os
++ * (e.g. !sdev), scsi needs to return 'not busy'.
++ * Otherwise, request stacking drivers may hold requests forever.
++ */
++static int scsi_lld_busy(struct request_queue *q)
++{
++      struct scsi_device *sdev = q->queuedata;
++      struct Scsi_Host *shost;
++      struct scsi_target *starget;
++
++      if (!sdev)
++              return 0;
++
++      shost = sdev->host;
++      starget = scsi_target(sdev);
++
++      if (scsi_host_in_recovery(shost) || scsi_host_is_busy(shost) ||
++          scsi_target_is_busy(starget) || scsi_device_is_busy(sdev))
++              return 1;
++
++      return 0;
++}
++
++/*
+  * Kill a request for a dead device
+  */
+ static void scsi_kill_request(struct request *req, struct request_queue *q)
+@@ -1778,6 +1809,7 @@ struct request_queue *scsi_alloc_queue(s
+       blk_queue_prep_rq(q, scsi_prep_fn);
+       blk_queue_softirq_done(q, scsi_softirq_done);
+       blk_queue_rq_timed_out(q, scsi_times_out);
++      blk_queue_lld_busy(q, scsi_lld_busy);
+       return q;
+ }
+--- a/include/linux/blkdev.h
++++ b/include/linux/blkdev.h
+@@ -272,6 +272,7 @@ typedef int (merge_bvec_fn) (struct requ
+ typedef void (prepare_flush_fn) (struct request_queue *, struct request *);
+ typedef void (softirq_done_fn)(struct request *);
+ typedef int (dma_drain_needed_fn)(struct request *);
++typedef int (lld_busy_fn) (struct request_queue *q);
+ enum blk_eh_timer_return {
+       BLK_EH_NOT_HANDLED,
+@@ -328,6 +329,7 @@ struct request_queue
+       softirq_done_fn         *softirq_done_fn;
+       rq_timed_out_fn         *rq_timed_out_fn;
+       dma_drain_needed_fn     *dma_drain_needed;
++      lld_busy_fn             *lld_busy_fn;
+       /*
+        * Dispatch queue sorting
+@@ -443,6 +445,7 @@ struct request_queue
+ #define QUEUE_FLAG_BIDI               9       /* queue supports bidi requests */
+ #define QUEUE_FLAG_NOMERGES    10     /* disable merge attempts */
+ #define QUEUE_FLAG_SAME_COMP   11     /* force complete on same CPU */
++#define QUEUE_FLAG_STACKABLE   13     /* supports request stacking */
+ static inline int queue_is_locked(struct request_queue *q)
+ {
+@@ -549,6 +552,8 @@ enum {
+ #define blk_queue_stopped(q)  test_bit(QUEUE_FLAG_STOPPED, &(q)->queue_flags)
+ #define blk_queue_nomerges(q) test_bit(QUEUE_FLAG_NOMERGES, &(q)->queue_flags)
+ #define blk_queue_flushing(q) ((q)->ordseq)
++#define blk_queue_stackable(q)        \
++      test_bit(QUEUE_FLAG_STACKABLE, &(q)->queue_flags)
+ #define blk_fs_request(rq)    ((rq)->cmd_type == REQ_TYPE_FS)
+ #define blk_pc_request(rq)    ((rq)->cmd_type == REQ_TYPE_BLOCK_PC)
+@@ -695,6 +700,10 @@ extern void __blk_put_request(struct req
+ extern struct request *blk_get_request(struct request_queue *, int, gfp_t);
+ extern void blk_insert_request(struct request_queue *, struct request *, int, void *);
+ extern void blk_requeue_request(struct request_queue *, struct request *);
++extern int blk_rq_check_limits(struct request_queue *q, struct request *rq);
++extern int blk_lld_busy(struct request_queue *q);
++extern int blk_insert_cloned_request(struct request_queue *q,
++                                   struct request *rq);
+ extern void blk_plug_device(struct request_queue *);
+ extern void blk_plug_device_unlocked(struct request_queue *);
+ extern int blk_remove_plug(struct request_queue *);
+@@ -792,6 +801,8 @@ extern void blk_complete_request(struct 
+ extern void __blk_complete_request(struct request *);
+ extern void blk_abort_request(struct request *);
+ extern void blk_abort_queue(struct request_queue *);
++extern void blk_update_request(struct request *rq, int error,
++                             unsigned int nr_bytes);
+ /*
+  * blk_end_request() takes bytes instead of sectors as a complete size.
+@@ -821,6 +832,7 @@ extern void blk_queue_update_dma_pad(str
+ extern int blk_queue_dma_drain(struct request_queue *q,
+                              dma_drain_needed_fn *dma_drain_needed,
+                              void *buf, unsigned int size);
++extern void blk_queue_lld_busy(struct request_queue *q, lld_busy_fn *fn);
+ extern void blk_queue_segment_boundary(struct request_queue *, unsigned long);
+ extern void blk_queue_prep_rq(struct request_queue *, prep_rq_fn *pfn);
+ extern void blk_queue_merge_bvec(struct request_queue *, merge_bvec_fn *);