revert android-tegra-2.6.36-honeycomb-mr1-9001adc to v2.6.36
[firefly-linux-kernel-4.4.55.git] / drivers / md / dm-table.c
index 1a6cb3c7822ed96c6c0b2eaa764522c5f0e7ae89..f9fc07d7a4b91f9db6aeb688cb5346bf604d1ac4 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/blkdev.h>
 #include <linux/namei.h>
 #include <linux/ctype.h>
+#include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/mutex.h>
@@ -53,6 +54,8 @@ struct dm_table {
        sector_t *highs;
        struct dm_target *targets;
 
+       unsigned discards_supported:1;
+
        /*
         * Indicates the rw permissions for the new logical
         * device.  This should be a combination of FMODE_READ
@@ -202,6 +205,7 @@ int dm_table_create(struct dm_table **result, fmode_t mode,
 
        INIT_LIST_HEAD(&t->devices);
        atomic_set(&t->holders, 0);
+       t->discards_supported = 1;
 
        if (!num_targets)
                num_targets = KEYS_PER_NODE;
@@ -237,11 +241,14 @@ void dm_table_destroy(struct dm_table *t)
 {
        unsigned int i;
 
+       if (!t)
+               return;
+
        while (atomic_read(&t->holders))
                msleep(1);
        smp_mb();
 
-       /* free the indexes (see dm_table_complete) */
+       /* free the indexes */
        if (t->depth >= 2)
                vfree(t->index[t->depth - 2]);
 
@@ -425,8 +432,7 @@ static int upgrade_mode(struct dm_dev_internal *dd, fmode_t new_mode,
  * it's already present.
  */
 static int __table_get_device(struct dm_table *t, struct dm_target *ti,
-                             const char *path, sector_t start, sector_t len,
-                             fmode_t mode, struct dm_dev **result)
+                     const char *path, fmode_t mode, struct dm_dev **result)
 {
        int r;
        dev_t uninitialized_var(dev);
@@ -499,16 +505,15 @@ int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
                return 0;
        }
 
-       if (blk_stack_limits(limits, &q->limits, start << 9) < 0)
-               DMWARN("%s: target device %s is misaligned: "
+       if (bdev_stack_limits(limits, bdev, start) < 0)
+               DMWARN("%s: adding target device %s caused an alignment inconsistency: "
                       "physical_block_size=%u, logical_block_size=%u, "
                       "alignment_offset=%u, start=%llu",
                       dm_device_name(ti->table->md), bdevname(bdev, b),
                       q->limits.physical_block_size,
                       q->limits.logical_block_size,
                       q->limits.alignment_offset,
-                      (unsigned long long) start << 9);
-
+                      (unsigned long long) start << SECTOR_SHIFT);
 
        /*
         * Check if merge fn is supported.
@@ -524,11 +529,10 @@ int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
 }
 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 dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
+                 struct dm_dev **result)
 {
-       return __table_get_device(ti->table, ti, path,
-                                 start, len, mode, result);
+       return __table_get_device(ti->table, ti, path, mode, result);
 }
 
 
@@ -600,11 +604,8 @@ int dm_split_args(int *argc, char ***argvp, char *input)
                return -ENOMEM;
 
        while (1) {
-               start = end;
-
                /* Skip whitespace */
-               while (*start && isspace(*start))
-                       start++;
+               start = skip_spaces(end);
 
                if (!*start)
                        break;  /* success, we hit the end */
@@ -772,6 +773,9 @@ int dm_table_add_target(struct dm_table *t, const char *type,
 
        t->highs[t->num_targets++] = tgt->begin + tgt->len - 1;
 
+       if (!tgt->num_discard_requests)
+               t->discards_supported = 0;
+
        return 0;
 
  bad:
@@ -780,7 +784,7 @@ int dm_table_add_target(struct dm_table *t, const char *type,
        return r;
 }
 
-int dm_table_set_type(struct dm_table *t)
+static int dm_table_set_type(struct dm_table *t)
 {
        unsigned i;
        unsigned bio_based = 0, request_based = 0;
@@ -902,7 +906,7 @@ static int setup_indexes(struct dm_table *t)
 /*
  * Builds the btree to index the map.
  */
-int dm_table_complete(struct dm_table *t)
+static int dm_table_build_index(struct dm_table *t)
 {
        int r = 0;
        unsigned int leaf_nodes;
@@ -921,6 +925,55 @@ int dm_table_complete(struct dm_table *t)
        return r;
 }
 
+/*
+ * Register the mapped device for blk_integrity support if
+ * the underlying devices support it.
+ */
+static int dm_table_prealloc_integrity(struct dm_table *t, struct mapped_device *md)
+{
+       struct list_head *devices = dm_table_get_devices(t);
+       struct dm_dev_internal *dd;
+
+       list_for_each_entry(dd, devices, list)
+               if (bdev_get_integrity(dd->dm_dev.bdev))
+                       return blk_integrity_register(dm_disk(md), NULL);
+
+       return 0;
+}
+
+/*
+ * Prepares the table for use by building the indices,
+ * setting the type, and allocating mempools.
+ */
+int dm_table_complete(struct dm_table *t)
+{
+       int r;
+
+       r = dm_table_set_type(t);
+       if (r) {
+               DMERR("unable to set table type");
+               return r;
+       }
+
+       r = dm_table_build_index(t);
+       if (r) {
+               DMERR("unable to build btrees");
+               return r;
+       }
+
+       r = dm_table_prealloc_integrity(t, t->md);
+       if (r) {
+               DMERR("could not register integrity profile.");
+               return r;
+       }
+
+       r = dm_table_alloc_md_mempools(t);
+       if (r)
+               DMERR("unable to allocate mempools");
+
+       return r;
+}
+
 static DEFINE_MUTEX(_event_lock);
 void dm_table_event_callback(struct dm_table *t,
                             void (*fn)(void *), void *context)
@@ -1025,9 +1078,9 @@ combine_limits:
                 * for the table.
                 */
                if (blk_stack_limits(limits, &ti_limits, 0) < 0)
-                       DMWARN("%s: target device "
+                       DMWARN("%s: adding target device "
                               "(start sect %llu len %llu) "
-                              "is misaligned",
+                              "caused an alignment inconsistency",
                               dm_device_name(table->md),
                               (unsigned long long) ti->begin,
                               (unsigned long long) ti->len);
@@ -1078,15 +1131,6 @@ no_integrity:
 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
         */
@@ -1097,6 +1141,11 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
        else
                queue_flag_set_unlocked(QUEUE_FLAG_CLUSTER, q);
 
+       if (!dm_table_supports_discards(t))
+               queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, q);
+       else
+               queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
+
        dm_table_set_integrity(t);
 
        /*
@@ -1240,11 +1289,42 @@ void dm_table_unplug_all(struct dm_table *t)
 
 struct mapped_device *dm_table_get_md(struct dm_table *t)
 {
-       dm_get(t->md);
-
        return t->md;
 }
 
+static int device_discard_capable(struct dm_target *ti, struct dm_dev *dev,
+                                 sector_t start, sector_t len, void *data)
+{
+       struct request_queue *q = bdev_get_queue(dev->bdev);
+
+       return q && blk_queue_discard(q);
+}
+
+bool dm_table_supports_discards(struct dm_table *t)
+{
+       struct dm_target *ti;
+       unsigned i = 0;
+
+       if (!t->discards_supported)
+               return 0;
+
+       /*
+        * Ensure that at least one underlying device supports discards.
+        * t->devices includes internal dm devices such as mirror logs
+        * so we need to use iterate_devices here, which targets
+        * supporting discard must provide.
+        */
+       while (i < dm_table_get_num_targets(t)) {
+               ti = dm_table_get_target(t, i++);
+
+               if (ti->type->iterate_devices &&
+                   ti->type->iterate_devices(ti, device_discard_capable, NULL))
+                       return 1;
+       }
+
+       return 0;
+}
+
 EXPORT_SYMBOL(dm_vcalloc);
 EXPORT_SYMBOL(dm_get_device);
 EXPORT_SYMBOL(dm_put_device);