Merge remote-tracking branch 'agust/next' into next
[firefly-linux-kernel-4.4.55.git] / block / genhd.c
index 3993ebf4135fb1adb8e25c9562dadd996d1c6be2..3c001fba80c76f3f6a22296d07a9f94cc60044a1 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/mutex.h>
 #include <linux/idr.h>
 #include <linux/log2.h>
+#include <linux/pm_runtime.h>
 
 #include "blk.h"
 
@@ -25,7 +26,7 @@ static DEFINE_MUTEX(block_class_lock);
 struct kobject *block_depr;
 
 /* for extended dynamic devt allocation, currently only one major is used */
-#define MAX_EXT_DEVT           (1 << MINORBITS)
+#define NR_EXT_DEVT            (1 << MINORBITS)
 
 /* For extended devt allocation.  ext_devt_mutex prevents look up
  * results from going away underneath its user.
@@ -410,7 +411,7 @@ static int blk_mangle_minor(int minor)
 int blk_alloc_devt(struct hd_struct *part, dev_t *devt)
 {
        struct gendisk *disk = part_to_disk(part);
-       int idx, rc;
+       int idx;
 
        /* in consecutive minor range? */
        if (part->partno < disk->minors) {
@@ -419,19 +420,11 @@ int blk_alloc_devt(struct hd_struct *part, dev_t *devt)
        }
 
        /* allocate ext devt */
-       do {
-               if (!idr_pre_get(&ext_devt_idr, GFP_KERNEL))
-                       return -ENOMEM;
-               rc = idr_get_new(&ext_devt_idr, part, &idx);
-       } while (rc == -EAGAIN);
-
-       if (rc)
-               return rc;
-
-       if (idx > MAX_EXT_DEVT) {
-               idr_remove(&ext_devt_idr, idx);
-               return -EBUSY;
-       }
+       mutex_lock(&ext_devt_mutex);
+       idx = idr_alloc(&ext_devt_idr, part, 0, NR_EXT_DEVT, GFP_KERNEL);
+       mutex_unlock(&ext_devt_mutex);
+       if (idx < 0)
+               return idx == -ENOSPC ? -EBUSY : idx;
 
        *devt = MKDEV(BLOCK_EXT_MAJOR, blk_mangle_minor(idx));
        return 0;
@@ -534,6 +527,14 @@ static void register_disk(struct gendisk *disk)
                        return;
                }
        }
+
+       /*
+        * avoid probable deadlock caused by allocating memory with
+        * GFP_KERNEL in runtime_resume callback of its all ancestor
+        * devices
+        */
+       pm_runtime_set_memalloc_noio(ddev, true);
+
        disk->part0.holder_dir = kobject_create_and_add("holders", &ddev->kobj);
        disk->slave_dir = kobject_create_and_add("slaves", &ddev->kobj);
 
@@ -646,7 +647,6 @@ void del_gendisk(struct gendisk *disk)
        disk_part_iter_exit(&piter);
 
        invalidate_partition(disk, 0);
-       blk_free_devt(disk_to_dev(disk)->devt);
        set_capacity(disk, 0);
        disk->flags &= ~GENHD_FL_UP;
 
@@ -663,7 +663,9 @@ void del_gendisk(struct gendisk *disk)
        disk->driverfs_dev = NULL;
        if (!sysfs_deprecated)
                sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk)));
+       pm_runtime_set_memalloc_noio(disk_to_dev(disk), false);
        device_del(disk_to_dev(disk));
+       blk_free_devt(disk_to_dev(disk)->devt);
 }
 EXPORT_SYMBOL(del_gendisk);