]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libfdisk: support alignment to non power of 2
authorKarel Zak <kzak@redhat.com>
Mon, 8 Jun 2015 13:41:33 +0000 (15:41 +0200)
committerKarel Zak <kzak@redhat.com>
Mon, 8 Jun 2015 13:57:23 +0000 (15:57 +0200)
Let's create a disk with 33553920 bytes optimal I/O size:

# modprobe scsi_debug dev_size_mb=1000 opt_blks=65535

and try to create partition:

 echo -e 'n\n\n\n\n+512M\np\nq\n'  | fdisk /dev/sdc

old version:

 Device     Boot Start     End Sectors  Size Id Type
 /dev/sdc1       65535 1114110 1048576  512M 83 Linux

The next partition will be expected on sector 1114110 + 1, but it's
not aligned to optimal I/O:

        ((1114110 + 1) * 512) % 33553920 = 8192

fixed version:

 Device     Boot Start     End Sectors  Size Id Type
 /dev/sdc1       65535 1114094 1048560  512M 83 Linux

        ((1114094 + 1) * 512) % 33553920 = 0

Note that the same problem with alignment calculation has been fixed
in Linux kernel by commit b8839b8c55f3fdd60dc36abcda7e0266aff7985c
(Oct 2014).

The patch also improves fdisk_align_lba_in_range() to not align sizes
smaller than grain (default 1MiB) to make it possible to create really
small partitions.

Reported-by: Tom Yan <tom.ty89@gmail.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
libfdisk/src/alignment.c

index 1baee57a84b6a447b60e6514e01212841b4ecd15..c658a5bee1584ac0d4b9397846fba110b0ab602c 100644 (file)
 /*
  * Alignment according to logical granularity (usually 1MiB)
  */
-static int lba_is_aligned(struct fdisk_context *cxt, fdisk_sector_t lba)
+static int lba_is_aligned(struct fdisk_context *cxt, uintmax_t lba)
 {
        unsigned long granularity = max(cxt->phy_sector_size, cxt->min_io_size);
        uintmax_t offset;
 
        if (cxt->grain > granularity)
                granularity = cxt->grain;
-       offset = (lba * cxt->sector_size) & (granularity - 1);
 
-       return !((granularity + cxt->alignment_offset - offset) & (granularity - 1));
+       offset = (lba * cxt->sector_size) % granularity;
+
+       return !((granularity + cxt->alignment_offset - offset) % granularity);
 }
 
 /*
@@ -54,9 +55,9 @@ static int lba_is_aligned(struct fdisk_context *cxt, fdisk_sector_t lba)
 static int lba_is_phy_aligned(struct fdisk_context *cxt, fdisk_sector_t lba)
 {
        unsigned long granularity = max(cxt->phy_sector_size, cxt->min_io_size);
-       uintmax_t offset = (lba * cxt->sector_size) & (granularity - 1);
+       uintmax_t offset = (lba * cxt->sector_size) % granularity;
 
-       return !((granularity + cxt->alignment_offset - offset) & (granularity - 1));
+       return !((granularity + cxt->alignment_offset - offset) % granularity);
 }
 
 /**
@@ -111,9 +112,15 @@ fdisk_sector_t fdisk_align_lba(struct fdisk_context *cxt, fdisk_sector_t lba, in
        }
 
        if (lba != res)
-               DBG(CXT, ul_debugobj(cxt, "LBA %ju -aligned-to-> %ju",
+               DBG(CXT, ul_debugobj(cxt, "LBA %ju -aligned-%s-> %ju [grain=%jus]",
                                (uintmax_t) lba,
-                               (uintmax_t) res));
+                               direction == FDISK_ALIGN_UP ? "up" :
+                               direction == FDISK_ALIGN_DOWN ? "down" : "near",
+                               (uintmax_t) res,
+                               cxt->grain / cxt->sector_size));
+       else
+               DBG(CXT, ul_debugobj(cxt, "LBA %ju -unchanged-", lba));
+
        return res;
 }
 
@@ -135,6 +142,15 @@ fdisk_sector_t fdisk_align_lba_in_range(struct fdisk_context *cxt,
 
        start = fdisk_align_lba(cxt, start, FDISK_ALIGN_UP);
        stop = fdisk_align_lba(cxt, stop, FDISK_ALIGN_DOWN);
+
+       if (lba > start && lba < stop
+           && (lba - start) < (cxt->grain / cxt->sector_size)) {
+
+               DBG(CXT, ul_debugobj(cxt, "LBA: area smaller than grain, don't align"));
+               res = lba;
+               goto done;
+       }
+
        lba = fdisk_align_lba(cxt, lba, FDISK_ALIGN_NEAREST);
 
        if (lba < start)
@@ -144,6 +160,7 @@ fdisk_sector_t fdisk_align_lba_in_range(struct fdisk_context *cxt,
        else
                res = lba;
 
+done:
        DBG(CXT, ul_debugobj(cxt, "LBA %ju range:<%ju..%ju>, result: %ju",
                                (uintmax_t) lba,
                                (uintmax_t) start,