summaryrefslogtreecommitdiffstats
path: root/libfdisk/src/alignment.c
diff options
context:
space:
mode:
authorKarel Zak2012-12-06 12:12:41 +0100
committerKarel Zak2013-03-11 11:20:41 +0100
commitaa42788d5f6beded3b36fb45cfaf4a27fff25698 (patch)
treee453964dc3422dadcd65fce5fa84494b6f9abfed /libfdisk/src/alignment.c
parentlibfdisk: add firstsector utils (diff)
downloadkernel-qcow2-util-linux-aa42788d5f6beded3b36fb45cfaf4a27fff25698.tar.gz
kernel-qcow2-util-linux-aa42788d5f6beded3b36fb45cfaf4a27fff25698.tar.xz
kernel-qcow2-util-linux-aa42788d5f6beded3b36fb45cfaf4a27fff25698.zip
libfdisk: add topology and geometry functions
- rename __discovery_* to fdisk_discovery_* - rename fdisk_context_force_sector_size() to fdisk_override_sector_size() - rename fdisk_context_set_user_geometry() to fdisk_override_geometry() - remove non-default sector size warning Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'libfdisk/src/alignment.c')
-rw-r--r--libfdisk/src/alignment.c251
1 files changed, 251 insertions, 0 deletions
diff --git a/libfdisk/src/alignment.c b/libfdisk/src/alignment.c
index b0d38ae23..e06e1afb0 100644
--- a/libfdisk/src/alignment.c
+++ b/libfdisk/src/alignment.c
@@ -1,6 +1,14 @@
+#ifdef HAVE_LIBBLKID
+#include <blkid.h>
+#endif
+#include "blkdev.h"
+
#include "fdiskP.h"
+/* temporary */
+extern int fdisk_reset_alignment(struct fdisk_context *cxt);
+
/*
* Alignment according to logical granulity (usually 1MiB)
*/
@@ -99,3 +107,246 @@ void fdisk_warn_alignment(struct fdisk_context *cxt, sector_t lba, int partition
printf(_("Partition %i does not start on physical sector boundary.\n"),
partition + 1);
}
+
+static unsigned long get_sector_size(int fd)
+{
+ int sect_sz;
+
+ if (!blkdev_get_sector_size(fd, &sect_sz))
+ return (unsigned long) sect_sz;
+ return DEFAULT_SECTOR_SIZE;
+}
+
+/**
+ * fdisk_override_sector_size:
+ * @cxt: fdisk context
+ * @s: required sector size
+ *
+ * Overwrites logical and physical sector size. Note that the default sector
+ * size is discovered by fdisk_new_context_from_device() from device topology.
+ *
+ * Don't use this function, rely on the default behavioer is more safe.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_override_sector_size(struct fdisk_context *cxt, sector_t s)
+{
+ if (!cxt)
+ return -EINVAL;
+
+ cxt->phy_sector_size = cxt->sector_size = s;
+ cxt->min_io_size = cxt->io_size = s;
+
+ fdisk_reset_alignment(cxt);
+ return 0;
+}
+
+static void recount_geometry(struct fdisk_context *cxt)
+{
+ cxt->geom.cylinders = cxt->total_sectors /
+ (cxt->geom.heads * cxt->geom.sectors);
+}
+
+/**
+ * fdisk_override_geometry:
+ * @cxt: fdisk context
+ * @cylinders: user specified cylinders
+ * @heads: user specified heads
+ * @sectors: user specified sectors
+ *
+ * Overrides autodiscovery and apply user specified geometry.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_override_geometry(struct fdisk_context *cxt,
+ unsigned int cylinders,
+ unsigned int heads,
+ unsigned int sectors)
+{
+ if (!cxt)
+ return -EINVAL;
+ if (heads)
+ cxt->geom.heads = heads;
+ if (sectors)
+ cxt->geom.sectors = sectors;
+
+ if (cylinders)
+ cxt->geom.cylinders = cylinders;
+ else
+ recount_geometry(cxt);
+
+ fdisk_reset_alignment(cxt);
+ return 0;
+}
+
+/*
+ * Generic (label independent) geometry
+ */
+int fdisk_discover_geometry(struct fdisk_context *cxt)
+{
+ sector_t nsects;
+ unsigned int h = 0, s = 0;
+
+ assert(cxt);
+ assert(!cxt->geom.heads);
+
+ /* get number of 512-byte sectors, and convert it the real sectors */
+ if (!blkdev_get_sectors(cxt->dev_fd, &nsects))
+ cxt->total_sectors = (nsects / (cxt->sector_size >> 9));
+
+ /* what the kernel/bios thinks the geometry is */
+ blkdev_get_geometry(cxt->dev_fd, &h, &s);
+ if (!h && !s) {
+ /* unable to discover geometry, use default values */
+ s = 63;
+ h = 255;
+ }
+
+ /* obtained heads and sectors */
+ cxt->geom.heads = h;
+ cxt->geom.sectors = s;
+ recount_geometry(cxt);
+
+ DBG(GEOMETRY, dbgprint("geometry discovered for %s: C/H/S: %lld/%d/%lld",
+ cxt->dev_path, cxt->geom.cylinders,
+ cxt->geom.heads, cxt->geom.sectors));
+ return 0;
+}
+
+int fdisk_discover_topology(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ assert(cxt->sector_size == 0);
+
+#ifdef HAVE_LIBBLKID
+ blkid_probe pr;
+
+ DBG(TOPOLOGY, dbgprint("initialize libblkid prober"));
+
+ pr = blkid_new_probe();
+ if (pr && blkid_probe_set_device(pr, cxt->dev_fd, 0, 0) == 0) {
+ blkid_topology tp = blkid_probe_get_topology(pr);
+
+ if (tp) {
+ cxt->min_io_size = blkid_topology_get_minimum_io_size(tp);
+ cxt->optimal_io_size = blkid_topology_get_optimal_io_size(tp);
+ cxt->phy_sector_size = blkid_topology_get_physical_sector_size(tp);
+ cxt->alignment_offset = blkid_topology_get_alignment_offset(tp);
+
+ /* I/O size used by fdisk */
+ cxt->io_size = cxt->optimal_io_size;
+ if (!cxt->io_size)
+ /* optimal IO is optional, default to minimum IO */
+ cxt->io_size = cxt->min_io_size;
+ }
+ }
+ blkid_free_probe(pr);
+#endif
+
+ cxt->sector_size = get_sector_size(cxt->dev_fd);
+ if (!cxt->phy_sector_size) /* could not discover physical size */
+ cxt->phy_sector_size = cxt->sector_size;
+
+ /* no blkid or error, use default values */
+ if (!cxt->min_io_size)
+ cxt->min_io_size = cxt->sector_size;
+ if (!cxt->io_size)
+ cxt->io_size = cxt->sector_size;
+
+ DBG(TOPOLOGY, dbgprint("topology discovered for %s:\n"
+ "\tlogical/physical sector sizes: %ld/%ld\n"
+ "\tfdisk/minimal/optimal io sizes: %ld/%ld/%ld\n",
+ cxt->dev_path, cxt->sector_size, cxt->phy_sector_size,
+ cxt->io_size, cxt->optimal_io_size, cxt->min_io_size));
+ return 0;
+}
+
+static int has_topology(struct fdisk_context *cxt)
+{
+ /*
+ * Assume that the device provides topology info if
+ * optimal_io_size is set or alignment_offset is set or
+ * minimum_io_size is not power of 2.
+ */
+ if (cxt &&
+ (cxt->optimal_io_size ||
+ cxt->alignment_offset ||
+ !is_power_of_2(cxt->min_io_size)))
+ return 1;
+ return 0;
+}
+
+/*
+ * The LBA of the first partition is based on the device geometry and topology.
+ * This offset is generic (and recommended) for all labels.
+ *
+ * Returns: 0 on error or number of logical sectors.
+ */
+sector_t fdisk_topology_get_first_lba(struct fdisk_context *cxt)
+{
+ sector_t x = 0, res;
+
+ if (!cxt)
+ return 0;
+
+ if (!cxt->io_size)
+ fdisk_discover_topology(cxt);
+
+ /*
+ * Align the begin of partitions to:
+ *
+ * a) topology
+ * a2) alignment offset
+ * a1) or physical sector (minimal_io_size, aka "grain")
+ *
+ * b) or default to 1MiB (2048 sectrors, Windows Vista default)
+ *
+ * c) or for very small devices use 1 phy.sector
+ */
+ if (has_topology(cxt)) {
+ if (cxt->alignment_offset)
+ x = cxt->alignment_offset;
+ else if (cxt->io_size > 2048 * 512)
+ x = cxt->io_size;
+ }
+ /* default to 1MiB */
+ if (!x)
+ x = 2048 * 512;
+
+ res = x / cxt->sector_size;
+
+ /* don't use huge offset on small devices */
+ if (cxt->total_sectors <= res * 4)
+ res = cxt->phy_sector_size / cxt->sector_size;
+
+ return res;
+}
+
+/*
+ * The LBA of the first partition is based on the device geometry and topology.
+ * This offset is generic generic (and recommended) for all labels.
+ *
+ * Returns: 0 on error or number of bytes.
+ */
+unsigned long fdisk_topology_get_grain(struct fdisk_context *cxt)
+{
+ unsigned long res;
+
+ if (!cxt)
+ return 0;
+
+ if (!cxt->io_size)
+ fdisk_discover_topology(cxt);
+
+ res = cxt->io_size;
+
+ /* use 1MiB grain always when possible */
+ if (res < 2048 * 512)
+ res = 2048 * 512;
+
+ /* don't use huge grain on small devices */
+ if (cxt->total_sectors <= (res * 4 / cxt->sector_size))
+ res = cxt->phy_sector_size;
+
+ return res;
+}