block: Fix topology stacking for data and discard alignment
[safe/jmp/linux-2.6] / block / blk-settings.c
index d29498e..e14fcbc 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/blkdev.h>
 #include <linux/bootmem.h>     /* for max_pfn/max_low_pfn */
 #include <linux/gcd.h>
+#include <linux/jiffies.h>
 
 #include "blk.h"
 
@@ -96,6 +97,11 @@ void blk_set_default_limits(struct queue_limits *lim)
        lim->max_segment_size = MAX_SEGMENT_SIZE;
        lim->max_sectors = BLK_DEF_MAX_SECTORS;
        lim->max_hw_sectors = INT_MAX;
+       lim->max_discard_sectors = 0;
+       lim->discard_granularity = 0;
+       lim->discard_alignment = 0;
+       lim->discard_misaligned = 0;
+       lim->discard_zeroes_data = -1;
        lim->logical_block_size = lim->physical_block_size = lim->io_min = 512;
        lim->bounce_pfn = (unsigned long)(BLK_BOUNCE_ANY >> PAGE_SHIFT);
        lim->alignment_offset = 0;
@@ -140,7 +146,7 @@ void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn)
        q->nr_batching = BLK_BATCH_REQ;
 
        q->unplug_thresh = 4;           /* hmm */
-       q->unplug_delay = (3 * HZ) / 1000;      /* 3 milliseconds */
+       q->unplug_delay = msecs_to_jiffies(3);  /* 3 milliseconds */
        if (q->unplug_delay == 0)
                q->unplug_delay = 1;
 
@@ -239,6 +245,18 @@ void blk_queue_max_hw_sectors(struct request_queue *q, unsigned int max_sectors)
 EXPORT_SYMBOL(blk_queue_max_hw_sectors);
 
 /**
+ * blk_queue_max_discard_sectors - set max sectors for a single discard
+ * @q:  the request queue for the device
+ * @max_discard_sectors: maximum number of sectors to discard
+ **/
+void blk_queue_max_discard_sectors(struct request_queue *q,
+               unsigned int max_discard_sectors)
+{
+       q->limits.max_discard_sectors = max_discard_sectors;
+}
+EXPORT_SYMBOL(blk_queue_max_discard_sectors);
+
+/**
  * blk_queue_max_phys_segments - set max phys segments for a request for this queue
  * @q:  the request queue for the device
  * @max_segments:  max number of segments
@@ -475,6 +493,16 @@ void blk_queue_stack_limits(struct request_queue *t, struct request_queue *b)
 }
 EXPORT_SYMBOL(blk_queue_stack_limits);
 
+static unsigned int lcm(unsigned int a, unsigned int b)
+{
+       if (a && b)
+               return (a * b) / gcd(a, b);
+       else if (b)
+               return b;
+
+       return a;
+}
+
 /**
  * blk_stack_limits - adjust queue_limits for stacked devices
  * @t: the stacking driver limits (top)
@@ -489,6 +517,9 @@ EXPORT_SYMBOL(blk_queue_stack_limits);
 int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
                     sector_t offset)
 {
+       sector_t alignment;
+       unsigned int top, bottom, granularity;
+
        t->max_sectors = min_not_zero(t->max_sectors, b->max_sectors);
        t->max_hw_sectors = min_not_zero(t->max_hw_sectors, b->max_hw_sectors);
        t->bounce_pfn = min_not_zero(t->bounce_pfn, b->bounce_pfn);
@@ -505,6 +536,19 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
        t->max_segment_size = min_not_zero(t->max_segment_size,
                                           b->max_segment_size);
 
+       granularity = max(b->physical_block_size, b->io_min);
+       alignment = b->alignment_offset - (offset & (granularity - 1));
+
+       if (t->alignment_offset != alignment) {
+
+               top = max(t->physical_block_size, t->io_min)
+                       + t->alignment_offset;
+               bottom = granularity + alignment;
+
+               if (max(top, bottom) & (min(top, bottom) - 1))
+                       t->misaligned = 1;
+       }
+
        t->logical_block_size = max(t->logical_block_size,
                                    b->logical_block_size);
 
@@ -512,37 +556,55 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
                                     b->physical_block_size);
 
        t->io_min = max(t->io_min, b->io_min);
+       t->io_opt = lcm(t->io_opt, b->io_opt);
+
        t->no_cluster |= b->no_cluster;
+       t->discard_zeroes_data &= b->discard_zeroes_data;
 
-       /* Bottom device offset aligned? */
-       if (offset &&
-           (offset & (b->physical_block_size - 1)) != b->alignment_offset) {
+       if (t->physical_block_size & (t->logical_block_size - 1)) {
+               t->physical_block_size = t->logical_block_size;
                t->misaligned = 1;
-               return -1;
        }
 
-       /* If top has no alignment offset, inherit from bottom */
-       if (!t->alignment_offset)
-               t->alignment_offset =
-                       b->alignment_offset & (b->physical_block_size - 1);
+       if (t->io_min & (t->physical_block_size - 1)) {
+               t->io_min = t->physical_block_size;
+               t->misaligned = 1;
+       }
 
-       /* Top device aligned on logical block boundary? */
-       if (t->alignment_offset & (t->logical_block_size - 1)) {
+       if (t->io_opt & (t->physical_block_size - 1)) {
+               t->io_opt = 0;
                t->misaligned = 1;
-               return -1;
        }
 
-       /* Find lcm() of optimal I/O size */
-       if (t->io_opt && b->io_opt)
-               t->io_opt = (t->io_opt * b->io_opt) / gcd(t->io_opt, b->io_opt);
-       else if (b->io_opt)
-               t->io_opt = b->io_opt;
+       t->alignment_offset = lcm(t->alignment_offset, alignment)
+               & (max(t->physical_block_size, t->io_min) - 1);
 
-       /* Verify that optimal I/O size is a multiple of io_min */
-       if (t->io_min && t->io_opt % t->io_min)
-               return -1;
+       if (t->alignment_offset & (t->logical_block_size - 1))
+               t->misaligned = 1;
 
-       return 0;
+       /* Discard alignment and granularity */
+       if (b->discard_granularity) {
+
+               alignment = b->discard_alignment -
+                       (offset & (b->discard_granularity - 1));
+
+               if (t->discard_granularity != 0 &&
+                   t->discard_alignment != alignment) {
+                       top = t->discard_granularity + t->discard_alignment;
+                       bottom = b->discard_granularity + alignment;
+
+                       /* Verify that top and bottom intervals line up */
+                       if (max(top, bottom) & (min(top, bottom) - 1))
+                               t->discard_misaligned = 1;
+               }
+
+               t->discard_granularity = max(t->discard_granularity,
+                                            b->discard_granularity);
+               t->discard_alignment = lcm(t->discard_alignment, alignment) &
+                       (t->discard_granularity - 1);
+       }
+
+       return t->misaligned ? -1 : 0;
 }
 EXPORT_SYMBOL(blk_stack_limits);