(root)/
util-linux-2.39/
libfdisk/
src/
alignment.c
       1  
       2  #ifdef HAVE_LIBBLKID
       3  #include <blkid.h>
       4  #endif
       5  #include "blkdev.h"
       6  
       7  #include "fdiskP.h"
       8  
       9  /**
      10   * SECTION: alignment
      11   * @title: Alignment
      12   * @short_description: functions to align partitions and work with disk topology and geometry
      13   *
      14   * The libfdisk aligns the end of the partitions to make it possible to align
      15   * the next partition to the "grain" (see fdisk_get_grain_size()). The grain is
      16   * usually 1MiB (or more for devices where optimal I/O is greater than 1MiB).
      17   *
      18   * It means that the library does not align strictly to physical sector size
      19   * (or minimal or optimal I/O), but it uses greater granularity. It makes
      20   * partition tables more portable. If you copy disk layout from 512-sector to
      21   * 4K-sector device, all partitions are still aligned to physical sectors.
      22   *
      23   * This unified concept also makes partition tables more user friendly, all
      24   * tables look same, LBA of the first partition is 2048 sectors everywhere, etc.
      25   *
      26   * It's recommended to not change any alignment or device properties. All is
      27   * initialized by default by fdisk_assign_device().
      28   *
      29   * Note that terminology used by libfdisk is:
      30   *   - device properties: I/O limits (topology), geometry, sector size, ...
      31   *   - alignment: first, last LBA, grain, ...
      32   *
      33   * The alignment setting may be modified by disk label driver.
      34   */
      35  
      36  /*
      37   * Alignment according to logical granularity (usually 1MiB)
      38   */
      39  static int lba_is_aligned(struct fdisk_context *cxt, uintmax_t lba)
      40  {
      41  	unsigned long granularity = max(cxt->phy_sector_size, cxt->min_io_size);
      42  	uintmax_t offset;
      43  
      44  	if (cxt->grain > granularity)
      45  		granularity = cxt->grain;
      46  
      47  	offset = (lba * cxt->sector_size) % granularity;
      48  
      49  	return !((granularity + cxt->alignment_offset - offset) % granularity);
      50  }
      51  
      52  /*
      53   * Alignment according to physical device topology (usually minimal i/o size)
      54   */
      55  static int lba_is_phy_aligned(struct fdisk_context *cxt, fdisk_sector_t lba)
      56  {
      57  	unsigned long granularity = max(cxt->phy_sector_size, cxt->min_io_size);
      58  	uintmax_t offset = (lba * cxt->sector_size) % granularity;
      59  
      60  	return !((granularity + cxt->alignment_offset - offset) % granularity);
      61  }
      62  
      63  /**
      64   * fdisk_align_lba:
      65   * @cxt: context
      66   * @lba: address to align
      67   * @direction: FDISK_ALIGN_{UP,DOWN,NEAREST}
      68   *
      69   * This function aligns @lba to the "grain" (see fdisk_get_grain_size()). If the
      70   * device uses alignment offset then the result is moved according the offset
      71   * to be on the physical boundary.
      72   *
      73   * Returns: alignment LBA.
      74   */
      75  fdisk_sector_t fdisk_align_lba(struct fdisk_context *cxt, fdisk_sector_t lba, int direction)
      76  {
      77  	fdisk_sector_t res;
      78  
      79  	if (lba_is_aligned(cxt, lba))
      80  		res = lba;
      81  	else {
      82  		fdisk_sector_t sects_in_phy = cxt->grain / cxt->sector_size;
      83  
      84  		if (lba < cxt->first_lba)
      85  			res = cxt->first_lba;
      86  
      87  		else if (direction == FDISK_ALIGN_UP)
      88  			res = ((lba + sects_in_phy) / sects_in_phy) * sects_in_phy;
      89  
      90  		else if (direction == FDISK_ALIGN_DOWN)
      91  			res = (lba / sects_in_phy) * sects_in_phy;
      92  
      93  		else /* FDISK_ALIGN_NEAREST */
      94  			res = ((lba + sects_in_phy / 2) / sects_in_phy) * sects_in_phy;
      95  
      96  		if (cxt->alignment_offset && !lba_is_aligned(cxt, res) &&
      97  		    res > cxt->alignment_offset / cxt->sector_size) {
      98  			/*
      99  			 * apply alignment_offset
     100  			 *
     101  			 * On disk with alignment compensation physical blocks starts
     102  			 * at LBA < 0 (usually LBA -1). It means we have to move LBA
     103  			 * according the offset to be on the physical boundary.
     104  			 */
     105  			/* fprintf(stderr, "LBA: %llu apply alignment_offset\n", res); */
     106  			res -= (max(cxt->phy_sector_size, cxt->min_io_size) -
     107  					cxt->alignment_offset) / cxt->sector_size;
     108  
     109  			if (direction == FDISK_ALIGN_UP && res < lba)
     110  				res += sects_in_phy;
     111  		}
     112  	}
     113  /*
     114  	if (lba != res)
     115  		DBG(CXT, ul_debugobj(cxt, "LBA %12ju aligned-%s %12ju [grain=%lus]",
     116  				(uintmax_t) lba,
     117  				direction == FDISK_ALIGN_UP ? "up  " :
     118  				direction == FDISK_ALIGN_DOWN ? "down" : "near",
     119  				(uintmax_t) res,
     120  				cxt->grain / cxt->sector_size));
     121  	else
     122  		DBG(CXT, ul_debugobj(cxt, "LBA %12ju already aligned", (uintmax_t)lba));
     123  */
     124  	return res;
     125  }
     126  
     127  /**
     128   * fdisk_align_lba_in_range:
     129   * @cxt: context
     130   * @lba: LBA
     131   * @start: range start
     132   * @stop: range stop
     133   *
     134   * Align @lba, the result has to be between @start and @stop
     135   *
     136   * Returns: aligned LBA
     137   */
     138  fdisk_sector_t fdisk_align_lba_in_range(struct fdisk_context *cxt,
     139  				  fdisk_sector_t lba, fdisk_sector_t start, fdisk_sector_t stop)
     140  {
     141  	fdisk_sector_t res;
     142  
     143  	/*DBG(CXT, ul_debugobj(cxt, "LBA: align in range <%ju..%ju>", (uintmax_t) start, (uintmax_t) stop));*/
     144  
     145  	if (start + (cxt->grain / cxt->sector_size) <= stop) {
     146  		start = fdisk_align_lba(cxt, start, FDISK_ALIGN_UP);
     147  		stop = fdisk_align_lba(cxt, stop, FDISK_ALIGN_DOWN);
     148  	}
     149  
     150  	if (start + (cxt->grain / cxt->sector_size) > stop) {
     151  		DBG(CXT, ul_debugobj(cxt, "LBA: area smaller than grain, don't align"));
     152  		res = lba;
     153  		goto done;
     154  	}
     155  
     156  	lba = fdisk_align_lba(cxt, lba, FDISK_ALIGN_NEAREST);
     157  
     158  	if (lba < start)
     159  		res = start;
     160  	else if (lba > stop)
     161  		res = stop;
     162  	else
     163  		res = lba;
     164  done:
     165  	DBG(CXT, ul_debugobj(cxt, "%ju in range <%ju..%ju> aligned to %ju",
     166  				(uintmax_t) lba,
     167  				(uintmax_t) start,
     168  				(uintmax_t) stop,
     169  				(uintmax_t) res));
     170  	return res;
     171  }
     172  
     173  /**
     174   * fdisk_lba_is_phy_aligned:
     175   * @cxt: context
     176   * @lba: LBA to check
     177   *
     178   * Check if the @lba is aligned to physical sector boundary.
     179   *
     180   * Returns: 1 if aligned.
     181   */
     182  int fdisk_lba_is_phy_aligned(struct fdisk_context *cxt, fdisk_sector_t lba)
     183  {
     184  	return lba_is_phy_aligned(cxt, lba);
     185  }
     186  
     187  static unsigned long get_sector_size(struct fdisk_context *cxt)
     188  {
     189  	int sect_sz;
     190  
     191  	if (!fdisk_is_regfile(cxt) &&
     192  	    !blkdev_get_sector_size(cxt->dev_fd, &sect_sz))
     193  		return (unsigned long) sect_sz;
     194  
     195  	return DEFAULT_SECTOR_SIZE;
     196  }
     197  
     198  static void recount_geometry(struct fdisk_context *cxt)
     199  {
     200  	if (!cxt->geom.heads)
     201  		cxt->geom.heads = 255;
     202  	if (!cxt->geom.sectors)
     203  		cxt->geom.sectors = 63;
     204  
     205  	cxt->geom.cylinders = cxt->total_sectors /
     206  		(cxt->geom.heads * cxt->geom.sectors);
     207  }
     208  
     209  /**
     210   * fdisk_override_geometry:
     211   * @cxt: fdisk context
     212   * @cylinders: user specified cylinders
     213   * @heads: user specified heads
     214   * @sectors: user specified sectors
     215   *
     216   * Overrides auto-discovery. The function fdisk_reset_device_properties()
     217   * restores the original setting.
     218   *
     219   * The difference between fdisk_override_geometry() and fdisk_save_user_geometry()
     220   * is that saved user geometry is persistent setting and it's applied always
     221   * when device is assigned to the context or device properties are reset.
     222   *
     223   * Returns: 0 on success, < 0 on error.
     224   */
     225  int fdisk_override_geometry(struct fdisk_context *cxt,
     226  			    unsigned int cylinders,
     227  			    unsigned int heads,
     228  			    unsigned int sectors)
     229  {
     230  	if (!cxt)
     231  		return -EINVAL;
     232  	if (heads)
     233  		cxt->geom.heads = heads;
     234  	if (sectors)
     235  		cxt->geom.sectors = sectors;
     236  
     237  	if (cylinders)
     238  		cxt->geom.cylinders = cylinders;
     239  	else
     240  		recount_geometry(cxt);
     241  
     242  	fdisk_reset_alignment(cxt);
     243  
     244  	DBG(CXT, ul_debugobj(cxt, "override C/H/S: %u/%u/%u",
     245  		(unsigned) cxt->geom.cylinders,
     246  		(unsigned) cxt->geom.heads,
     247  		(unsigned) cxt->geom.sectors));
     248  
     249  	return 0;
     250  }
     251  
     252  /**
     253   * fdisk_save_user_geometry:
     254   * @cxt: context
     255   * @cylinders: C
     256   * @heads: H
     257   * @sectors: S
     258   *
     259   * Save user defined geometry to use it for partitioning.
     260   *
     261   * The user properties are applied by fdisk_assign_device() or
     262   * fdisk_reset_device_properties().
     263  
     264   * Returns: <0 on error, 0 on success.
     265   */
     266  int fdisk_save_user_geometry(struct fdisk_context *cxt,
     267  			    unsigned int cylinders,
     268  			    unsigned int heads,
     269  			    unsigned int sectors)
     270  {
     271  	if (!cxt)
     272  		return -EINVAL;
     273  
     274  	if (heads)
     275  		cxt->user_geom.heads = heads > 256 ? 0 : heads;
     276  	if (sectors)
     277  		cxt->user_geom.sectors = sectors >= 64 ? 0 : sectors;
     278  	if (cylinders)
     279  		cxt->user_geom.cylinders = cylinders;
     280  
     281  	DBG(CXT, ul_debugobj(cxt, "user C/H/S: %u/%u/%u",
     282  				(unsigned) cxt->user_geom.cylinders,
     283  				(unsigned) cxt->user_geom.heads,
     284  				(unsigned) cxt->user_geom.sectors));
     285  
     286  	return 0;
     287  }
     288  
     289  /**
     290   * fdisk_save_user_sector_size:
     291   * @cxt: context
     292   * @phy: physical sector size
     293   * @log: logical sector size
     294   *
     295   * Save user defined sector sizes to use it for partitioning.
     296   *
     297   * The user properties are applied by fdisk_assign_device() or
     298   * fdisk_reset_device_properties().
     299   *
     300   * Returns: <0 on error, 0 on success.
     301   */
     302  int fdisk_save_user_sector_size(struct fdisk_context *cxt,
     303  				unsigned int phy,
     304  				unsigned int log)
     305  {
     306  	if (!cxt)
     307  		return -EINVAL;
     308  
     309  	DBG(CXT, ul_debugobj(cxt, "user phy/log sector size: %u/%u", phy, log));
     310  
     311  	cxt->user_pyh_sector = phy;
     312  	cxt->user_log_sector = log;
     313  
     314  	return 0;
     315  }
     316  
     317  /**
     318   * fdisk_save_user_grain:
     319   * @cxt: context
     320   * @grain: size in bytes (>= 512, multiple of 512)
     321   *
     322   * Save user define grain size. The size is used to align partitions.
     323   *
     324   * The default is 1MiB (or optimal I/O size if greater than 1MiB). It's strongly
     325   * recommended to use the default.
     326   *
     327   * The smallest possible granularity for partitioning is physical sector size
     328   * (or minimal I/O size; the bigger number win). If the user's @grain size is
     329   * too small then the smallest possible granularity is used. It means
     330   * fdisk_save_user_grain(cxt, 512) forces libfdisk to use grain as small as
     331   * possible.
     332   *
     333   * The setting is applied by fdisk_assign_device() or
     334   * fdisk_reset_device_properties().
     335   *
     336   * Returns: <0 on error, 0 on success.
     337   */
     338  int fdisk_save_user_grain(struct fdisk_context *cxt, unsigned long grain)
     339  {
     340  	if (!cxt || grain % 512)
     341  		return -EINVAL;
     342  
     343  	DBG(CXT, ul_debugobj(cxt, "user grain size: %lu", grain));
     344  	cxt->user_grain = grain;
     345  	return 0;
     346  }
     347  
     348  /**
     349   * fdisk_has_user_device_properties:
     350   * @cxt: context
     351   *
     352   * Returns: 1 if user specified any properties
     353   */
     354  int fdisk_has_user_device_properties(struct fdisk_context *cxt)
     355  {
     356  	return (cxt->user_pyh_sector || cxt->user_log_sector ||
     357  		cxt->user_grain ||
     358  		fdisk_has_user_device_geometry(cxt));
     359  }
     360  
     361  int fdisk_has_user_device_geometry(struct fdisk_context *cxt)
     362  {
     363  	return (cxt->user_geom.heads || cxt->user_geom.sectors || cxt->user_geom.cylinders);
     364  }
     365  
     366  int fdisk_apply_user_device_properties(struct fdisk_context *cxt)
     367  {
     368  	if (!cxt)
     369  		return -EINVAL;
     370  
     371  	DBG(CXT, ul_debugobj(cxt, "applying user device properties"));
     372  
     373  	if (cxt->user_pyh_sector)
     374  		cxt->phy_sector_size = cxt->user_pyh_sector;
     375  	if (cxt->user_log_sector) {
     376  		uint64_t old_total = cxt->total_sectors;
     377  		uint64_t old_secsz = cxt->sector_size;
     378  
     379  		cxt->sector_size = cxt->min_io_size =
     380  			cxt->io_size = cxt->user_log_sector;
     381  
     382  		if (cxt->sector_size != old_secsz) {
     383  			cxt->total_sectors = (old_total * (old_secsz/512)) / (cxt->sector_size >> 9);
     384  			DBG(CXT, ul_debugobj(cxt, "new total sectors: %ju", (uintmax_t)cxt->total_sectors));
     385  		}
     386  	}
     387  
     388  	if (cxt->user_geom.heads)
     389  		cxt->geom.heads = cxt->user_geom.heads;
     390  	if (cxt->user_geom.sectors)
     391  		cxt->geom.sectors = cxt->user_geom.sectors;
     392  
     393  	if (cxt->user_geom.cylinders)
     394  		cxt->geom.cylinders = cxt->user_geom.cylinders;
     395  	else if (cxt->user_geom.heads || cxt->user_geom.sectors)
     396  		recount_geometry(cxt);
     397  
     398  	fdisk_reset_alignment(cxt);
     399  
     400  	if (cxt->user_grain) {
     401  		unsigned long granularity = max(cxt->phy_sector_size, cxt->min_io_size);
     402  
     403  		cxt->grain = cxt->user_grain < granularity ? granularity : cxt->user_grain;
     404  		DBG(CXT, ul_debugobj(cxt, "new grain: %lu", cxt->grain));
     405  	}
     406  
     407  	if (cxt->firstsector_bufsz != cxt->sector_size)
     408  		fdisk_read_firstsector(cxt);
     409  
     410  	DBG(CXT, ul_debugobj(cxt, "new C/H/S: %u/%u/%u",
     411  		(unsigned) cxt->geom.cylinders,
     412  		(unsigned) cxt->geom.heads,
     413  		(unsigned) cxt->geom.sectors));
     414  	DBG(CXT, ul_debugobj(cxt, "new log/phy sector size: %u/%u",
     415  		(unsigned) cxt->sector_size,
     416  		(unsigned) cxt->phy_sector_size));
     417  
     418  	return 0;
     419  }
     420  
     421  void fdisk_zeroize_device_properties(struct fdisk_context *cxt)
     422  {
     423  	assert(cxt);
     424  
     425  	cxt->io_size = 0;
     426  	cxt->optimal_io_size = 0;
     427  	cxt->min_io_size = 0;
     428  	cxt->phy_sector_size = 0;
     429  	cxt->sector_size = 0;
     430  	cxt->alignment_offset = 0;
     431  	cxt->grain = 0;
     432  	cxt->first_lba = 0;
     433  	cxt->last_lba = 0;
     434  	cxt->total_sectors = 0;
     435  
     436  	memset(&cxt->geom, 0, sizeof(struct fdisk_geometry));
     437  }
     438  
     439  /**
     440   * fdisk_reset_device_properties:
     441   * @cxt: context
     442   *
     443   * Resets and discovery topology (I/O limits), geometry, re-read the first
     444   * rector on the device if necessary and apply user device setting (geometry
     445   * and sector size), then initialize alignment according to label driver (see
     446   * fdisk_reset_alignment()).
     447   *
     448   * You don't have to use this function by default, fdisk_assign_device() is
     449   * smart enough to initialize all necessary setting.
     450   *
     451   * Returns: 0 on success, <0 on error.
     452   */
     453  int fdisk_reset_device_properties(struct fdisk_context *cxt)
     454  {
     455  	int rc;
     456  
     457  	if (!cxt)
     458  		return -EINVAL;
     459  
     460  	DBG(CXT, ul_debugobj(cxt, "*** resetting device properties"));
     461  
     462  	fdisk_zeroize_device_properties(cxt);
     463  	fdisk_discover_topology(cxt);
     464  	fdisk_discover_geometry(cxt);
     465  
     466  	rc = fdisk_read_firstsector(cxt);
     467  	if (rc)
     468  		return rc;
     469  
     470  	fdisk_apply_user_device_properties(cxt);
     471  	return 0;
     472  }
     473  
     474  /*
     475   * Generic (label independent) geometry
     476   */
     477  int fdisk_discover_geometry(struct fdisk_context *cxt)
     478  {
     479  	fdisk_sector_t nsects = 0;
     480  	unsigned int h = 0, s = 0;
     481  
     482  	assert(cxt);
     483  	assert(cxt->geom.heads == 0);
     484  
     485  	DBG(CXT, ul_debugobj(cxt, "%s: discovering geometry...", cxt->dev_path));
     486  
     487  	if (fdisk_is_regfile(cxt))
     488  		cxt->total_sectors = cxt->dev_st.st_size / cxt->sector_size;
     489  	else {
     490  		/* get number of 512-byte sectors, and convert it the real sectors */
     491  		if (!blkdev_get_sectors(cxt->dev_fd, (unsigned long long *) &nsects))
     492  			cxt->total_sectors = (nsects / (cxt->sector_size >> 9));
     493  
     494  		/* what the kernel/bios thinks the geometry is */
     495  		blkdev_get_geometry(cxt->dev_fd, &h, &s);
     496  	}
     497  
     498  	DBG(CXT, ul_debugobj(cxt, "total sectors: %ju (ioctl=%ju)",
     499  				(uintmax_t) cxt->total_sectors,
     500  				(uintmax_t) nsects));
     501  
     502  	cxt->geom.cylinders = 0;
     503  	cxt->geom.heads = h;
     504  	cxt->geom.sectors = s;
     505  
     506  	/* obtained heads and sectors */
     507  	recount_geometry(cxt);
     508  
     509  	DBG(CXT, ul_debugobj(cxt, "result: C/H/S: %u/%u/%u",
     510  			       (unsigned) cxt->geom.cylinders,
     511  			       (unsigned) cxt->geom.heads,
     512  			       (unsigned) cxt->geom.sectors));
     513  	return 0;
     514  }
     515  
     516  int fdisk_discover_topology(struct fdisk_context *cxt)
     517  {
     518  #ifdef HAVE_LIBBLKID
     519  	blkid_probe pr;
     520  #endif
     521  	assert(cxt);
     522  	assert(cxt->sector_size == 0);
     523  
     524  	DBG(CXT, ul_debugobj(cxt, "%s: discovering topology...", cxt->dev_path));
     525  #ifdef HAVE_LIBBLKID
     526  	DBG(CXT, ul_debugobj(cxt, "initialize libblkid prober"));
     527  
     528  	pr = blkid_new_probe();
     529  	if (pr && blkid_probe_set_device(pr, cxt->dev_fd, 0, 0) == 0) {
     530  		blkid_topology tp = blkid_probe_get_topology(pr);
     531  
     532  		if (tp) {
     533  			cxt->min_io_size = blkid_topology_get_minimum_io_size(tp);
     534  			cxt->optimal_io_size = blkid_topology_get_optimal_io_size(tp);
     535  			cxt->phy_sector_size = blkid_topology_get_physical_sector_size(tp);
     536  			cxt->alignment_offset = blkid_topology_get_alignment_offset(tp);
     537  
     538  			/* I/O size used by fdisk */
     539  			cxt->io_size = cxt->optimal_io_size;
     540  			if (!cxt->io_size)
     541  				/* optimal I/O is optional, default to minimum IO */
     542  				cxt->io_size = cxt->min_io_size;
     543  
     544  			if (cxt->io_size && cxt->phy_sector_size) {
     545  				if (cxt->io_size == 33553920) {
     546  					/* 33553920 (32 MiB - 512) is always a controller error */
     547  					DBG(CXT, ul_debugobj(cxt, "ignore bad I/O size 33553920"));
     548  					cxt->io_size = cxt->phy_sector_size;
     549  				} else if ((cxt->io_size % cxt->phy_sector_size) != 0) {
     550  					/* ignore optimal I/O if not aligned to phy.sector size */
     551  					DBG(CXT, ul_debugobj(cxt, "ignore misaligned I/O size"));
     552  					cxt->io_size = cxt->phy_sector_size;
     553  				}
     554  			}
     555  
     556  		}
     557  	}
     558  	blkid_free_probe(pr);
     559  #endif
     560  
     561  	cxt->sector_size = get_sector_size(cxt);
     562  	if (!cxt->phy_sector_size) /* could not discover physical size */
     563  		cxt->phy_sector_size = cxt->sector_size;
     564  
     565  	/* no blkid or error, use default values */
     566  	if (!cxt->min_io_size)
     567  		cxt->min_io_size = cxt->sector_size;
     568  	if (!cxt->io_size)
     569  		cxt->io_size = cxt->sector_size;
     570  
     571  	DBG(CXT, ul_debugobj(cxt, "result: log/phy sector size: %ld/%ld",
     572  			cxt->sector_size, cxt->phy_sector_size));
     573  	DBG(CXT, ul_debugobj(cxt, "result: fdisk/optimal/minimal io: %ld/%ld/%ld",
     574  		       cxt->io_size, cxt->optimal_io_size, cxt->min_io_size));
     575  	return 0;
     576  }
     577  
     578  static int has_topology(struct fdisk_context *cxt)
     579  {
     580  	/*
     581  	 * Assume that the device provides topology info if
     582  	 * optimal_io_size is set or alignment_offset is set or
     583  	 * minimum_io_size is not power of 2.
     584  	 */
     585  	if (cxt &&
     586  	    (cxt->optimal_io_size ||
     587  	     cxt->alignment_offset ||
     588  	     !is_power_of_2(cxt->min_io_size)))
     589  		return 1;
     590  	return 0;
     591  }
     592  
     593  /*
     594   * The LBA of the first partition is based on the device geometry and topology.
     595   * This offset is generic (and recommended) for all labels.
     596   *
     597   * Returns: 0 on error or number of logical sectors.
     598   */
     599  static fdisk_sector_t topology_get_first_lba(struct fdisk_context *cxt)
     600  {
     601  	fdisk_sector_t x = 0, res;
     602  
     603  	if (!cxt)
     604  		return 0;
     605  
     606  	if (!cxt->io_size)
     607  		fdisk_discover_topology(cxt);
     608  
     609  	/*
     610  	 * Align the begin of partitions to:
     611  	 *
     612  	 * a) topology
     613  	 *  a2) alignment offset
     614  	 *  a1) or physical sector (minimal_io_size, aka "grain")
     615  	 *
     616  	 * b) or default to 1MiB (2048 sectors, Windows Vista default)
     617  	 *
     618  	 * c) or for very small devices use 1 phy.sector
     619  	 */
     620  	if (has_topology(cxt)) {
     621  		if (cxt->alignment_offset)
     622  			x = cxt->alignment_offset;
     623  		else if (cxt->io_size > 2048 * 512)
     624  			x = cxt->io_size;
     625  	}
     626  	/* default to 1MiB */
     627  	if (!x)
     628  		x = 2048 * 512;
     629  
     630  	res = x / cxt->sector_size;
     631  
     632  	/* don't use huge offset on small devices */
     633  	if (cxt->total_sectors <= res * 4)
     634  		res = cxt->phy_sector_size / cxt->sector_size;
     635  
     636  	return res;
     637  }
     638  
     639  static unsigned long topology_get_grain(struct fdisk_context *cxt)
     640  {
     641  	unsigned long res;
     642  
     643  	if (!cxt)
     644  		return 0;
     645  
     646  	if (!cxt->io_size)
     647  		fdisk_discover_topology(cxt);
     648  
     649  	res = cxt->io_size;
     650  
     651  	/* use 1MiB grain always when possible */
     652  	if (res < 2048 * 512)
     653  		res = 2048 * 512;
     654  
     655  	/* don't use huge grain on small devices */
     656  	if (cxt->total_sectors <= (res * 4 / cxt->sector_size))
     657  		res = cxt->phy_sector_size;
     658  
     659  	return res;
     660  }
     661  
     662  /* apply label alignment setting to the context -- if not sure use
     663   * fdisk_reset_alignment()
     664   */
     665  int fdisk_apply_label_device_properties(struct fdisk_context *cxt)
     666  {
     667  	int rc = 0;
     668  
     669  	if (cxt->label && cxt->label->op->reset_alignment) {
     670  		DBG(CXT, ul_debugobj(cxt, "applying label device properties..."));
     671  		rc = cxt->label->op->reset_alignment(cxt);
     672  	}
     673  	return rc;
     674  }
     675  
     676  /**
     677   * fdisk_reset_alignment:
     678   * @cxt: fdisk context
     679   *
     680   * Resets alignment setting to the default and label specific values. This
     681   * function does not change device properties (I/O limits, geometry etc.).
     682   *
     683   * Returns: 0 on success, < 0 in case of error.
     684   */
     685  int fdisk_reset_alignment(struct fdisk_context *cxt)
     686  {
     687  	int rc = 0;
     688  
     689  	if (!cxt)
     690  		return -EINVAL;
     691  
     692  	DBG(CXT, ul_debugobj(cxt, "resetting alignment..."));
     693  
     694  	/* default */
     695  	cxt->grain = topology_get_grain(cxt);
     696  	cxt->first_lba = topology_get_first_lba(cxt);
     697  	cxt->last_lba = cxt->total_sectors - 1;
     698  
     699  	/* overwrite default by label stuff */
     700  	rc = fdisk_apply_label_device_properties(cxt);
     701  
     702  	DBG(CXT, ul_debugobj(cxt, "alignment reset to: "
     703  			    "first LBA=%ju, last LBA=%ju, grain=%lu [rc=%d]",
     704  			    (uintmax_t) cxt->first_lba, (uintmax_t) cxt->last_lba,
     705  			    cxt->grain,	rc));
     706  	return rc;
     707  }
     708  
     709  
     710  fdisk_sector_t fdisk_scround(struct fdisk_context *cxt, fdisk_sector_t num)
     711  {
     712  	fdisk_sector_t un = fdisk_get_units_per_sector(cxt);
     713  	return (num + un - 1) / un;
     714  }
     715  
     716  fdisk_sector_t fdisk_cround(struct fdisk_context *cxt, fdisk_sector_t num)
     717  {
     718  	return fdisk_use_cylinders(cxt) ?
     719  			(num / fdisk_get_units_per_sector(cxt)) + 1 : num;
     720  }
     721