1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
|
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
* All Rights Reserved.
*/
#ifdef OVERRIDE_SYSTEM_STATX
#define statx sys_statx
#endif
#include <fcntl.h>
#include <sys/stat.h>
#include "libxfs_priv.h"
#include "libxcmd.h"
#include <blkid/blkid.h>
#include "xfs_multidisk.h"
#include "libfrog/platform.h"
#include "libfrog/statx.h"
#define TERABYTES(count, blog) ((uint64_t)(count) << (40 - (blog)))
#define GIGABYTES(count, blog) ((uint64_t)(count) << (30 - (blog)))
#define MEGABYTES(count, blog) ((uint64_t)(count) << (20 - (blog)))
void
calc_default_ag_geometry(
int blocklog,
uint64_t dblocks,
int multidisk,
uint64_t *agsize,
uint64_t *agcount)
{
uint64_t blocks = 0;
int shift = 0;
/*
* First handle the high extreme - the point at which we will
* always use the maximum AG size.
*
* This applies regardless of storage configuration.
*/
if (dblocks >= TERABYTES(32, blocklog)) {
blocks = XFS_AG_MAX_BLOCKS(blocklog);
goto done;
}
/*
* For a single underlying storage device over 4TB in size
* use the maximum AG size. Between 128MB and 4TB, just use
* 4 AGs and scale up smoothly between min/max AG sizes.
*/
if (!multidisk) {
if (dblocks >= TERABYTES(4, blocklog)) {
blocks = XFS_AG_MAX_BLOCKS(blocklog);
goto done;
} else if (dblocks >= MEGABYTES(128, blocklog)) {
shift = XFS_NOMULTIDISK_AGLOG;
goto calc_blocks;
}
}
/*
* For the multidisk configs we choose an AG count based on the number
* of data blocks available, trying to keep the number of AGs higher
* than the single disk configurations. This makes the assumption that
* larger filesystems have more parallelism available to them.
*/
shift = XFS_MULTIDISK_AGLOG;
if (dblocks <= GIGABYTES(512, blocklog))
shift--;
if (dblocks <= GIGABYTES(8, blocklog))
shift--;
if (dblocks < MEGABYTES(128, blocklog))
shift--;
if (dblocks < MEGABYTES(64, blocklog))
shift--;
if (dblocks < MEGABYTES(32, blocklog))
shift--;
/*
* If dblocks is not evenly divisible by the number of
* desired AGs, round "blocks" up so we don't lose the
* last bit of the filesystem. The same principle applies
* to the AG count, so we don't lose the last AG!
*/
calc_blocks:
ASSERT(shift >= 0 && shift <= XFS_MULTIDISK_AGLOG);
blocks = dblocks >> shift;
if (dblocks & xfs_mask32lo(shift)) {
if (blocks < XFS_AG_MAX_BLOCKS(blocklog))
blocks++;
}
done:
*agsize = blocks;
*agcount = dblocks / blocks + (dblocks % blocks != 0);
}
void
calc_default_rtgroup_geometry(
int blocklog,
uint64_t rblocks,
uint64_t *rgsize,
uint64_t *rgcount)
{
uint64_t blocks = 0;
int shift = 0;
/*
* For a single underlying storage device over 4TB in size use the
* maximum rtgroup size. Between 128MB and 4TB, just use 4 rtgroups
* and scale up smoothly between min/max rtgroup sizes.
*/
if (rblocks >= TERABYTES(4, blocklog)) {
blocks = XFS_MAX_RGBLOCKS;
goto done;
}
if (rblocks >= MEGABYTES(128, blocklog)) {
shift = XFS_NOMULTIDISK_AGLOG;
goto calc_blocks;
}
/*
* If rblocks is not evenly divisible by the number of desired rt
* groups, round "blocks" up so we don't lose the last bit of the
* filesystem. The same principle applies to the rt group count, so we
* don't lose the last rt group!
*/
calc_blocks:
ASSERT(shift >= 0 && shift <= XFS_MULTIDISK_AGLOG);
blocks = rblocks >> shift;
if (rblocks & xfs_mask32lo(shift)) {
if (blocks < XFS_MAX_RGBLOCKS)
blocks++;
}
done:
*rgsize = blocks;
*rgcount = rblocks / blocks + (rblocks % blocks != 0);
}
/*
* Check for existing filesystem or partition table on device.
* Returns:
* 1 for existing fs or partition
* 0 for nothing found
* -1 for internal error
*/
int
check_overwrite(
const char *device)
{
const char *type;
blkid_probe pr = NULL;
int ret;
int fd;
long long size;
int bsz;
if (!device || !*device)
return 0;
ret = -1; /* will reset on success of all setup calls */
fd = open(device, O_RDONLY);
if (fd < 0)
goto out;
platform_findsizes((char *)device, fd, &size, &bsz);
close(fd);
/* nothing to overwrite on a 0-length device */
if (size == 0) {
ret = 0;
goto out;
}
pr = blkid_new_probe_from_filename(device);
if (!pr)
goto out;
ret = blkid_probe_enable_partitions(pr, 1);
if (ret < 0)
goto out;
ret = blkid_do_fullprobe(pr);
if (ret < 0)
goto out;
/*
* Blkid returns 1 for nothing found and 0 when it finds a signature,
* but we want the exact opposite, so reverse the return value here.
*
* In addition print some useful diagnostics about what actually is
* on the device.
*/
if (ret) {
ret = 0;
goto out;
}
if (!blkid_probe_lookup_value(pr, "TYPE", &type, NULL)) {
fprintf(stderr,
_("%s: %s appears to contain an existing "
"filesystem (%s).\n"), progname, device, type);
} else if (!blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL)) {
fprintf(stderr,
_("%s: %s appears to contain a partition "
"table (%s).\n"), progname, device, type);
} else {
fprintf(stderr,
_("%s: %s appears to contain something weird "
"according to blkid\n"), progname, device);
}
ret = 1;
out:
if (pr)
blkid_free_probe(pr);
/* libblkid 2.38.1 lies and can return -EIO */
if (ret < 0)
fprintf(stderr,
_("%s: probe of %s failed, cannot detect "
"existing filesystem.\n"), progname, device);
return ret;
}
static void
blkid_get_topology(
const char *device,
struct device_topology *dt,
int force_overwrite)
{
blkid_topology tp;
blkid_probe pr;
pr = blkid_new_probe_from_filename(device);
if (!pr)
return;
tp = blkid_probe_get_topology(pr);
if (!tp)
goto out_free_probe;
dt->logical_sector_size = blkid_topology_get_logical_sector_size(tp);
dt->physical_sector_size = blkid_topology_get_physical_sector_size(tp);
dt->sunit = blkid_topology_get_minimum_io_size(tp);
dt->swidth = blkid_topology_get_optimal_io_size(tp);
/*
* If the reported values are the same as the physical sector size
* do not bother to report anything. It will only cause warnings
* if people specify larger stripe units or widths manually.
*/
if (dt->sunit == dt->physical_sector_size ||
dt->swidth == dt->physical_sector_size) {
dt->sunit = 0;
dt->swidth = 0;
}
/*
* Blkid reports the information in terms of bytes, but we want it in
* terms of 512 bytes blocks (only to convert it to bytes later..)
*/
dt->sunit >>= 9;
dt->swidth >>= 9;
if (blkid_topology_get_alignment_offset(tp) != 0) {
fprintf(stderr,
_("warning: device is not properly aligned %s\n"),
device);
if (!force_overwrite) {
fprintf(stderr,
_("Use -f to force usage of a misaligned device\n"));
exit(EXIT_FAILURE);
}
/* Do not use physical sector size if the device is misaligned */
dt->physical_sector_size = dt->logical_sector_size;
}
blkid_free_probe(pr);
return;
out_free_probe:
blkid_free_probe(pr);
fprintf(stderr,
_("warning: unable to probe device topology for device %s\n"),
device);
}
static void
get_hw_atomic_writes_topology(
struct libxfs_dev *dev,
struct device_topology *dt)
{
struct statx sx;
int fd;
int ret;
fd = open(dev->name, O_RDONLY);
if (fd < 0)
return;
ret = statx(fd, "", AT_EMPTY_PATH, STATX_WRITE_ATOMIC, &sx);
if (ret)
goto out_close;
if (!(sx.stx_mask & STATX_WRITE_ATOMIC))
goto out_close;
dt->awu_min = sx.stx_atomic_write_unit_min >> 9;
dt->awu_max = max(sx.stx_atomic_write_unit_max_opt,
sx.stx_atomic_write_unit_max) >> 9;
out_close:
close(fd);
}
static void
get_device_topology(
struct libxfs_dev *dev,
struct device_topology *dt,
int force_overwrite)
{
struct stat st;
/*
* Nothing to do if this particular subvolume doesn't exist.
*/
if (!dev->name)
return;
/*
* If our target is a regular file, use platform_findsizes
* to try to obtain the underlying filesystem's requirements
* for direct IO; we'll set our sector size to that if possible.
*/
if (dev->isfile || (!stat(dev->name, &st) && S_ISREG(st.st_mode))) {
int flags = O_RDONLY;
long long dummy;
int fd;
/* with xi->disfile we may not have the file yet! */
if (dev->isfile)
flags |= O_CREAT;
fd = open(dev->name, flags, 0666);
if (fd >= 0) {
platform_findsizes(dev->name, fd, &dummy,
&dt->logical_sector_size);
close(fd);
} else {
dt->logical_sector_size = BBSIZE;
}
} else {
blkid_get_topology(dev->name, dt, force_overwrite);
get_hw_atomic_writes_topology(dev, dt);
}
ASSERT(dt->logical_sector_size);
/*
* Older kernels may not have physical/logical distinction.
*/
if (!dt->physical_sector_size)
dt->physical_sector_size = dt->logical_sector_size;
}
void
get_topology(
struct libxfs_init *xi,
struct fs_topology *ft,
int force_overwrite)
{
get_device_topology(&xi->data, &ft->data, force_overwrite);
get_device_topology(&xi->rt, &ft->rt, force_overwrite);
get_device_topology(&xi->log, &ft->log, force_overwrite);
}
|