dm: calculate queue limits during resume not load
authorMike Snitzer <snitzer@redhat.com>
Mon, 22 Jun 2009 09:12:34 +0000 (10:12 +0100)
committerAlasdair G Kergon <agk@redhat.com>
Mon, 22 Jun 2009 09:12:34 +0000 (10:12 +0100)
Currently, device-mapper maintains a separate instance of 'struct
queue_limits' for each table of each device.  When the configuration of
a device is to be changed, first its table is loaded and this structure
is populated, then the device is 'resumed' and the calculated
queue_limits are applied.

This places restrictions on how userspace may process related devices,
where it is often advantageous to 'load' tables for several devices
at once before 'resuming' them together.  As the new queue_limits
only take effect after the 'resume', if they are changing and one
device uses another, the latter must be 'resumed' before the former
may be 'loaded'.

This patch moves the calculation of these queue_limits out of
the 'load' operation into 'resume'.  Since we are no longer
pre-calculating this struct, we no longer need to maintain copies
within our dm structs.

dm_set_device_limits() now passes the 'start' of the device's
data area (aka pe_start) as the 'offset' to blk_stack_limits().

init_valid_queue_limits() is replaced by blk_set_default_limits().

Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Cc: martin.petersen@oracle.com
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
drivers/md/dm-table.c
drivers/md/dm.c
drivers/md/dm.h
include/linux/device-mapper.h

index 267817e..09a5711 100644 (file)
@@ -62,12 +62,6 @@ struct dm_table {
        /* a list of devices used by this table */
        struct list_head devices;
 
-       /*
-        * These are optimistic limits taken from all the
-        * targets, some targets will need smaller limits.
-        */
-       struct queue_limits limits;
-
        /* events get handed up using this callback */
        void (*event_fn)(void *);
        void *event_context;
@@ -346,18 +340,21 @@ static void close_dev(struct dm_dev_internal *d, struct mapped_device *md)
 /*
  * If possible, this checks an area of a destination device is valid.
  */
-static int device_area_is_valid(struct dm_target *ti, struct block_device *bdev,
-                            sector_t start, sector_t len)
+static int device_area_is_valid(struct dm_target *ti, struct dm_dev *dev,
+                               sector_t start, void *data)
 {
-       sector_t dev_size = i_size_read(bdev->bd_inode) >> SECTOR_SHIFT;
+       struct queue_limits *limits = data;
+       struct block_device *bdev = dev->bdev;
+       sector_t dev_size =
+               i_size_read(bdev->bd_inode) >> SECTOR_SHIFT;
        unsigned short logical_block_size_sectors =
-               ti->limits.logical_block_size >> SECTOR_SHIFT;
+               limits->logical_block_size >> SECTOR_SHIFT;
        char b[BDEVNAME_SIZE];
 
        if (!dev_size)
                return 1;
 
-       if ((start >= dev_size) || (start + len > dev_size)) {
+       if ((start >= dev_size) || (start + ti->len > dev_size)) {
                DMWARN("%s: %s too small for target",
                       dm_device_name(ti->table->md), bdevname(bdev, b));
                return 0;
@@ -371,16 +368,16 @@ static int device_area_is_valid(struct dm_target *ti, struct block_device *bdev,
                       "logical block size %hu of %s",
                       dm_device_name(ti->table->md),
                       (unsigned long long)start,
-                      ti->limits.logical_block_size, bdevname(bdev, b));
+                      limits->logical_block_size, bdevname(bdev, b));
                return 0;
        }
 
-       if (len & (logical_block_size_sectors - 1)) {
+       if (ti->len & (logical_block_size_sectors - 1)) {
                DMWARN("%s: len=%llu not aligned to h/w "
                       "logical block size %hu of %s",
                       dm_device_name(ti->table->md),
-                      (unsigned long long)len,
-                      ti->limits.logical_block_size, bdevname(bdev, b));
+                      (unsigned long long)ti->len,
+                      limits->logical_block_size, bdevname(bdev, b));
                return 0;
        }
 
@@ -479,18 +476,21 @@ static int __table_get_device(struct dm_table *t, struct dm_target *ti,
  */
 #define min_not_zero(l, r) (l == 0) ? r : ((r == 0) ? l : min(l, r))
 
-void dm_set_device_limits(struct dm_target *ti, struct block_device *bdev)
+int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
+                        sector_t start, void *data)
 {
+       struct queue_limits *limits = data;
+       struct block_device *bdev = dev->bdev;
        struct request_queue *q = bdev_get_queue(bdev);
        char b[BDEVNAME_SIZE];
 
        if (unlikely(!q)) {
                DMWARN("%s: Cannot set limits for nonexistent device %s",
                       dm_device_name(ti->table->md), bdevname(bdev, b));
-               return;
+               return 0;
        }
 
-       if (blk_stack_limits(&ti->limits, &q->limits, 0) < 0)
+       if (blk_stack_limits(limits, &q->limits, start) < 0)
                DMWARN("%s: target device %s is misaligned",
                       dm_device_name(ti->table->md), bdevname(bdev, b));
 
@@ -501,32 +501,21 @@ void dm_set_device_limits(struct dm_target *ti, struct block_device *bdev)
         */
 
        if (q->merge_bvec_fn && !ti->type->merge)
-               ti->limits.max_sectors =
-                       min_not_zero(ti->limits.max_sectors,
+               limits->max_sectors =
+                       min_not_zero(limits->max_sectors,
                                     (unsigned int) (PAGE_SIZE >> 9));
+       return 0;
 }
 EXPORT_SYMBOL_GPL(dm_set_device_limits);
 
 int dm_get_device(struct dm_target *ti, const char *path, sector_t start,
                  sector_t len, fmode_t mode, struct dm_dev **result)
 {
-       int r = __table_get_device(ti->table, ti, path,
-                                  start, len, mode, result);
-
-       if (r)
-               return r;
-
-       dm_set_device_limits(ti, (*result)->bdev);
-
-       if (!device_area_is_valid(ti, (*result)->bdev, start, len)) {
-               dm_put_device(ti, *result);
-               *result = NULL;
-               return -EINVAL;
-       }
-
-       return r;
+       return __table_get_device(ti->table, ti, path,
+                                 start, len, mode, result);
 }
 
+
 /*
  * Decrement a devices use count and remove it if necessary.
  */
@@ -641,34 +630,6 @@ int dm_split_args(int *argc, char ***argvp, char *input)
        return 0;
 }
 
-static void init_valid_queue_limits(struct queue_limits *limits)
-{
-       if (!limits->max_sectors)
-               limits->max_sectors = SAFE_MAX_SECTORS;
-       if (!limits->max_hw_sectors)
-               limits->max_hw_sectors = SAFE_MAX_SECTORS;
-       if (!limits->max_phys_segments)
-               limits->max_phys_segments = MAX_PHYS_SEGMENTS;
-       if (!limits->max_hw_segments)
-               limits->max_hw_segments = MAX_HW_SEGMENTS;
-       if (!limits->logical_block_size)
-               limits->logical_block_size = 1 << SECTOR_SHIFT;
-       if (!limits->physical_block_size)
-               limits->physical_block_size = 1 << SECTOR_SHIFT;
-       if (!limits->io_min)
-               limits->io_min = 1 << SECTOR_SHIFT;
-       if (!limits->max_segment_size)
-               limits->max_segment_size = MAX_SEGMENT_SIZE;
-       if (!limits->seg_boundary_mask)
-               limits->seg_boundary_mask = BLK_SEG_BOUNDARY_MASK;
-       if (!limits->bounce_pfn)
-               limits->bounce_pfn = -1;
-       /*
-        * The other fields (alignment_offset, io_opt, misaligned)
-        * hold 0 from the kzalloc().
-        */
-}
-
 /*
  * Impose necessary and sufficient conditions on a devices's table such
  * that any incoming bio which respects its logical_block_size can be
@@ -676,14 +637,15 @@ static void init_valid_queue_limits(struct queue_limits *limits)
  * two or more targets, the size of each piece it gets split into must
  * be compatible with the logical_block_size of the target processing it.
  */
-static int validate_hardware_logical_block_alignment(struct dm_table *table)
+static int validate_hardware_logical_block_alignment(struct dm_table *table,
+                                                struct queue_limits *limits)
 {
        /*
         * This function uses arithmetic modulo the logical_block_size
         * (in units of 512-byte sectors).
         */
        unsigned short device_logical_block_size_sects =
-               table->limits.logical_block_size >> SECTOR_SHIFT;
+               limits->logical_block_size >> SECTOR_SHIFT;
 
        /*
         * Offset of the start of the next table entry, mod logical_block_size.
@@ -697,6 +659,7 @@ static int validate_hardware_logical_block_alignment(struct dm_table *table)
        unsigned short remaining = 0;
 
        struct dm_target *uninitialized_var(ti);
+       struct queue_limits ti_limits;
        unsigned i = 0;
 
        /*
@@ -705,12 +668,19 @@ static int validate_hardware_logical_block_alignment(struct dm_table *table)
        while (i < dm_table_get_num_targets(table)) {
                ti = dm_table_get_target(table, i++);
 
+               blk_set_default_limits(&ti_limits);
+
+               /* combine all target devices' limits */
+               if (ti->type->iterate_devices)
+                       ti->type->iterate_devices(ti, dm_set_device_limits,
+                                                 &ti_limits);
+
                /*
                 * If the remaining sectors fall entirely within this
                 * table entry are they compatible with its logical_block_size?
                 */
                if (remaining < ti->len &&
-                   remaining & ((ti->limits.logical_block_size >>
+                   remaining & ((ti_limits.logical_block_size >>
                                  SECTOR_SHIFT) - 1))
                        break;  /* Error */
 
@@ -723,11 +693,11 @@ static int validate_hardware_logical_block_alignment(struct dm_table *table)
 
        if (remaining) {
                DMWARN("%s: table line %u (start sect %llu len %llu) "
-                      "not aligned to hardware logical block size %hu",
+                      "not aligned to h/w logical block size %hu",
                       dm_device_name(table->md), i,
                       (unsigned long long) ti->begin,
                       (unsigned long long) ti->len,
-                      table->limits.logical_block_size);
+                      limits->logical_block_size);
                return -EINVAL;
        }
 
@@ -786,12 +756,6 @@ int dm_table_add_target(struct dm_table *t, const char *type,
 
        t->highs[t->num_targets++] = tgt->begin + tgt->len - 1;
 
-       if (blk_stack_limits(&t->limits, &tgt->limits, 0) < 0)
-               DMWARN("%s: target device (start sect %llu len %llu) "
-                      "is misaligned",
-                      dm_device_name(t->md),
-                      (unsigned long long) tgt->begin,
-                      (unsigned long long) tgt->len);
        return 0;
 
  bad:
@@ -834,12 +798,6 @@ int dm_table_complete(struct dm_table *t)
        int r = 0;
        unsigned int leaf_nodes;
 
-       init_valid_queue_limits(&t->limits);
-
-       r = validate_hardware_logical_block_alignment(t);
-       if (r)
-               return r;
-
        /* how many indexes will the btree have ? */
        leaf_nodes = dm_div_up(t->num_targets, KEYS_PER_NODE);
        t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE);
@@ -914,6 +872,57 @@ struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector)
        return &t->targets[(KEYS_PER_NODE * n) + k];
 }
 
+/*
+ * Establish the new table's queue_limits and validate them.
+ */
+int dm_calculate_queue_limits(struct dm_table *table,
+                             struct queue_limits *limits)
+{
+       struct dm_target *uninitialized_var(ti);
+       struct queue_limits ti_limits;
+       unsigned i = 0;
+
+       blk_set_default_limits(limits);
+
+       while (i < dm_table_get_num_targets(table)) {
+               blk_set_default_limits(&ti_limits);
+
+               ti = dm_table_get_target(table, i++);
+
+               if (!ti->type->iterate_devices)
+                       goto combine_limits;
+
+               /*
+                * Combine queue limits of all the devices this target uses.
+                */
+               ti->type->iterate_devices(ti, dm_set_device_limits,
+                                         &ti_limits);
+
+               /*
+                * Check each device area is consistent with the target's
+                * overall queue limits.
+                */
+               if (!ti->type->iterate_devices(ti, device_area_is_valid,
+                                              &ti_limits))
+                       return -EINVAL;
+
+combine_limits:
+               /*
+                * Merge this target's queue limits into the overall limits
+                * for the table.
+                */
+               if (blk_stack_limits(limits, &ti_limits, 0) < 0)
+                       DMWARN("%s: target device "
+                              "(start sect %llu len %llu) "
+                              "is misaligned",
+                              dm_device_name(table->md),
+                              (unsigned long long) ti->begin,
+                              (unsigned long long) ti->len);
+       }
+
+       return validate_hardware_logical_block_alignment(table, limits);
+}
+
 /*
  * Set the integrity profile for this device if all devices used have
  * matching profiles.
@@ -953,14 +962,24 @@ no_integrity:
        return;
 }
 
-void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q)
+void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
+                              struct queue_limits *limits)
 {
+       /*
+        * Each target device in the table has a data area that should normally
+        * be aligned such that the DM device's alignment_offset is 0.
+        * FIXME: Propagate alignment_offsets up the stack and warn of
+        *        sub-optimal or inconsistent settings.
+        */
+       limits->alignment_offset = 0;
+       limits->misaligned = 0;
+
        /*
         * Copy table's limits to the DM device's request_queue
         */
-       q->limits = t->limits;
+       q->limits = *limits;
 
-       if (t->limits.no_cluster)
+       if (limits->no_cluster)
                queue_flag_clear_unlocked(QUEUE_FLAG_CLUSTER, q);
        else
                queue_flag_set_unlocked(QUEUE_FLAG_CLUSTER, q);
index a9210bb..f609793 100644 (file)
@@ -1313,7 +1313,8 @@ static void __set_size(struct mapped_device *md, sector_t size)
        mutex_unlock(&md->bdev->bd_inode->i_mutex);
 }
 
-static int __bind(struct mapped_device *md, struct dm_table *t)
+static int __bind(struct mapped_device *md, struct dm_table *t,
+                 struct queue_limits *limits)
 {
        struct request_queue *q = md->queue;
        sector_t size;
@@ -1337,7 +1338,7 @@ static int __bind(struct mapped_device *md, struct dm_table *t)
 
        write_lock(&md->map_lock);
        md->map = t;
-       dm_table_set_restrictions(t, q);
+       dm_table_set_restrictions(t, q, limits);
        write_unlock(&md->map_lock);
 
        return 0;
@@ -1562,6 +1563,7 @@ static void dm_queue_flush(struct mapped_device *md)
  */
 int dm_swap_table(struct mapped_device *md, struct dm_table *table)
 {
+       struct queue_limits limits;
        int r = -EINVAL;
 
        mutex_lock(&md->suspend_lock);
@@ -1570,8 +1572,12 @@ int dm_swap_table(struct mapped_device *md, struct dm_table *table)
        if (!dm_suspended(md))
                goto out;
 
+       r = dm_calculate_queue_limits(table, &limits);
+       if (r)
+               goto out;
+
        __unbind(md);
-       r = __bind(md, table);
+       r = __bind(md, table, &limits);
 
 out:
        mutex_unlock(&md->suspend_lock);
index b5935c6..604e85c 100644 (file)
@@ -41,7 +41,10 @@ void dm_table_event_callback(struct dm_table *t,
                             void (*fn)(void *), void *context);
 struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index);
 struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector);
-void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q);
+int dm_calculate_queue_limits(struct dm_table *table,
+                             struct queue_limits *limits);
+void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
+                              struct queue_limits *limits);
 struct list_head *dm_table_get_devices(struct dm_table *t);
 void dm_table_presuspend_targets(struct dm_table *t);
 void dm_table_postsuspend_targets(struct dm_table *t);
index deac3b4..e6bf3b8 100644 (file)
@@ -103,7 +103,8 @@ void dm_error(const char *message);
 /*
  * Combine device limits.
  */
-void dm_set_device_limits(struct dm_target *ti, struct block_device *bdev);
+int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
+                        sector_t start, void *data);
 
 struct dm_dev {
        struct block_device *bdev;
@@ -163,7 +164,6 @@ struct dm_target {
        sector_t begin;
        sector_t len;
 
-       /* FIXME: turn this into a mask, and merge with queue_limits */
        /* Always a power of 2 */
        sector_t split_io;
 
@@ -177,12 +177,6 @@ struct dm_target {
         */
        unsigned num_flush_requests;
 
-       /*
-        * These are automatically filled in by
-        * dm_table_get_device.
-        */
-       struct queue_limits limits;
-
        /* target specific data */
        void *private;