block: implement CONFIG_DEBUG_BLOCK_EXT_DEVT
authorTejun Heo <tj@kernel.org>
Mon, 25 Aug 2008 10:47:25 +0000 (19:47 +0900)
committerJens Axboe <jens.axboe@oracle.com>
Thu, 9 Oct 2008 06:56:06 +0000 (08:56 +0200)
Extended devt introduces non-contiguos device numbers.  This patch
implements a debug option which forces most devt allocations to be
from the extended area and spreads them out.  This is enabled by
default if DEBUG_KERNEL is set and achieves...

1. Detects code paths in kernel or userland which expect predetermined
   consecutive device numbers.

2. When something goes wrong, avoid corruption as adding to the minor
   of earlier partition won't lead to the wrong but valid device.

Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
block/genhd.c
drivers/ide/ide-disk.c
drivers/scsi/sd.c
lib/Kconfig.debug

index ee4b13520e598a3a0c9f7d93047573a2bdcdfd0a..67e5a59ced2a16bf4bd7aa80271ca20838650a11 100644 (file)
@@ -298,6 +298,38 @@ EXPORT_SYMBOL(unregister_blkdev);
 
 static struct kobj_map *bdev_map;
 
+/**
+ * blk_mangle_minor - scatter minor numbers apart
+ * @minor: minor number to mangle
+ *
+ * Scatter consecutively allocated @minor number apart if MANGLE_DEVT
+ * is enabled.  Mangling twice gives the original value.
+ *
+ * RETURNS:
+ * Mangled value.
+ *
+ * CONTEXT:
+ * Don't care.
+ */
+static int blk_mangle_minor(int minor)
+{
+#ifdef CONFIG_DEBUG_BLOCK_EXT_DEVT
+       int i;
+
+       for (i = 0; i < MINORBITS / 2; i++) {
+               int low = minor & (1 << i);
+               int high = minor & (1 << (MINORBITS - 1 - i));
+               int distance = MINORBITS - 1 - 2 * i;
+
+               minor ^= low | high;    /* clear both bits */
+               low <<= distance;       /* swap the positions */
+               high >>= distance;
+               minor |= low | high;    /* and set */
+       }
+#endif
+       return minor;
+}
+
 /**
  * blk_alloc_devt - allocate a dev_t for a partition
  * @part: partition to allocate dev_t for
@@ -339,7 +371,7 @@ int blk_alloc_devt(struct hd_struct *part, dev_t *devt)
                return -EBUSY;
        }
 
-       *devt = MKDEV(BLOCK_EXT_MAJOR, idx);
+       *devt = MKDEV(BLOCK_EXT_MAJOR, blk_mangle_minor(idx));
        return 0;
 }
 
@@ -361,7 +393,7 @@ void blk_free_devt(dev_t devt)
 
        if (MAJOR(devt) == BLOCK_EXT_MAJOR) {
                mutex_lock(&ext_devt_mutex);
-               idr_remove(&ext_devt_idr, MINOR(devt));
+               idr_remove(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
                mutex_unlock(&ext_devt_mutex);
        }
 }
@@ -473,7 +505,7 @@ struct gendisk *get_gendisk(dev_t devt, int *partno)
                struct hd_struct *part;
 
                mutex_lock(&ext_devt_mutex);
-               part = idr_find(&ext_devt_idr, MINOR(devt));
+               part = idr_find(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
                if (part && get_disk(part_to_disk(part))) {
                        *partno = part->partno;
                        disk = part_to_disk(part);
index 7a88de9ada29d47d4ec04ba652c26974dce5588d..a072df5053ae3827c076d5edaeca65a8bc613c6f 100644 (file)
 #include <asm/div64.h>
 
 #define IDE_DISK_PARTS         (1 << PARTN_BITS)
+
+#if !defined(CONFIG_DEBUG_BLOCK_EXT_DEVT)
 #define IDE_DISK_MINORS                IDE_DISK_PARTS
+#else
+#define IDE_DISK_MINORS                1
+#endif
+
 #define IDE_DISK_EXT_MINORS    (IDE_DISK_PARTS - IDE_DISK_MINORS)
 
 struct ide_disk_obj {
index d1bb0e1d2d2897574d292753e754aca227d7aa39..280d231a86ed378c8237cb079fb6163610a2a1f5 100644 (file)
@@ -87,7 +87,13 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_MOD);
 MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC);
 
 #define SD_PARTS       64
+
+#if !defined(CONFIG_DEBUG_BLOCK_EXT_DEVT)
 #define SD_MINORS      16
+#else
+#define SD_MINORS      1
+#endif
+
 #define SD_EXT_MINORS  (SD_PARTS - SD_MINORS)
 
 static int  sd_revalidate_disk(struct gendisk *);
index 0b504814e378067ff120b266e5b26c9fdd6fb90e..5a536f703a833a23aa358e5b157c993644e1f87e 100644 (file)
@@ -624,6 +624,22 @@ config BACKTRACE_SELF_TEST
 
          Say N if you are unsure.
 
+config DEBUG_BLOCK_EXT_DEVT
+        bool "Force extended block device numbers and spread them"
+       depends on DEBUG_KERNEL
+       depends on BLOCK
+       default y
+       help
+         Conventionally, block device numbers are allocated from
+         predetermined contiguous area.  However, extended block area
+         may introduce non-contiguous block device numbers.  This
+         option forces most block device numbers to be allocated from
+         the extended space and spreads them to discover kernel or
+         userland code paths which assume predetermined contiguous
+         device number allocation.
+
+         Say N if you are unsure.
+
 config LKDTM
        tristate "Linux Kernel Dump Test Tool Module"
        depends on DEBUG_KERNEL