dm: allocate buffer for messages with small number of arguments using GFP_NOIO
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / md / dm-table.c
index f221812..41d907b 100644 (file)
@@ -545,14 +545,28 @@ static int adjoin(struct dm_table *table, struct dm_target *ti)
 
 /*
  * Used to dynamically allocate the arg array.
+ *
+ * We do first allocation with GFP_NOIO because dm-mpath and dm-thin must
+ * process messages even if some device is suspended. These messages have a
+ * small fixed number of arguments.
+ *
+ * On the other hand, dm-switch needs to process bulk data using messages and
+ * excessive use of GFP_NOIO could cause trouble.
  */
 static char **realloc_argv(unsigned *array_size, char **old_argv)
 {
        char **argv;
        unsigned new_size;
+       gfp_t gfp;
 
-       new_size = *array_size ? *array_size * 2 : 64;
-       argv = kmalloc(new_size * sizeof(*argv), GFP_KERNEL);
+       if (*array_size) {
+               new_size = *array_size * 2;
+               gfp = GFP_KERNEL;
+       } else {
+               new_size = 8;
+               gfp = GFP_NOIO;
+       }
+       argv = kmalloc(new_size * sizeof(*argv), gfp);
        if (argv) {
                memcpy(argv, old_argv, *array_size * sizeof(*argv));
                *array_size = new_size;
@@ -860,14 +874,17 @@ EXPORT_SYMBOL(dm_consume_args);
 static int dm_table_set_type(struct dm_table *t)
 {
        unsigned i;
-       unsigned bio_based = 0, request_based = 0;
+       unsigned bio_based = 0, request_based = 0, hybrid = 0;
        struct dm_target *tgt;
        struct dm_dev_internal *dd;
        struct list_head *devices;
+       unsigned live_md_type;
 
        for (i = 0; i < t->num_targets; i++) {
                tgt = t->targets + i;
-               if (dm_target_request_based(tgt))
+               if (dm_target_hybrid(tgt))
+                       hybrid = 1;
+               else if (dm_target_request_based(tgt))
                        request_based = 1;
                else
                        bio_based = 1;
@@ -879,6 +896,19 @@ static int dm_table_set_type(struct dm_table *t)
                }
        }
 
+       if (hybrid && !bio_based && !request_based) {
+               /*
+                * The targets can work either way.
+                * Determine the type from the live device.
+                * Default to bio-based if device is new.
+                */
+               live_md_type = dm_get_md_type(t->md);
+               if (live_md_type == DM_TYPE_REQUEST_BASED)
+                       request_based = 1;
+               else
+                       bio_based = 1;
+       }
+
        if (bio_based) {
                /* We must use this table as bio-based */
                t->type = DM_TYPE_BIO_BASED;