1 /* $NetBSD: lvresize.c,v 1.1.1.3 2009/12/02 00:25:53 haad Exp $ */ 2 3 /* 4 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. 5 * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved. 6 * 7 * This file is part of LVM2. 8 * 9 * This copyrighted material is made available to anyone wishing to use, 10 * modify, copy, or redistribute it subject to the terms and conditions 11 * of the GNU Lesser General Public License v.2.1. 12 * 13 * You should have received a copy of the GNU Lesser General Public License 14 * along with this program; if not, write to the Free Software Foundation, 15 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 */ 17 18 #include "tools.h" 19 20 #define SIZE_BUF 128 21 22 struct lvresize_params { 23 const char *vg_name; 24 const char *lv_name; 25 26 uint32_t stripes; 27 uint32_t stripe_size; 28 uint32_t mirrors; 29 30 const struct segment_type *segtype; 31 32 /* size */ 33 uint32_t extents; 34 uint64_t size; 35 sign_t sign; 36 percent_t percent; 37 38 enum { 39 LV_ANY = 0, 40 LV_REDUCE = 1, 41 LV_EXTEND = 2 42 } resize; 43 44 int resizefs; 45 int nofsck; 46 47 int argc; 48 char **argv; 49 }; 50 51 static int _validate_stripesize(struct cmd_context *cmd, 52 const struct volume_group *vg, 53 struct lvresize_params *lp) 54 { 55 if (arg_sign_value(cmd, stripesize_ARG, 0) == SIGN_MINUS) { 56 log_error("Stripesize may not be negative."); 57 return 0; 58 } 59 60 if (arg_uint_value(cmd, stripesize_ARG, 0) > STRIPE_SIZE_LIMIT * 2) { 61 log_error("Stripe size cannot be larger than %s", 62 display_size(cmd, (uint64_t) STRIPE_SIZE_LIMIT)); 63 return 0; 64 } 65 66 if (!(vg->fid->fmt->features & FMT_SEGMENTS)) 67 log_warn("Varied stripesize not supported. Ignoring."); 68 else if (arg_uint_value(cmd, stripesize_ARG, 0) > vg->extent_size * 2) { 69 log_error("Reducing stripe size %s to maximum, " 70 "physical extent size %s", 71 display_size(cmd, 72 (uint64_t) arg_uint_value(cmd, stripesize_ARG, 0)), 73 display_size(cmd, (uint64_t) vg->extent_size)); 74 lp->stripe_size = vg->extent_size; 75 } else 76 lp->stripe_size = arg_uint_value(cmd, stripesize_ARG, 0); 77 78 if (lp->mirrors) { 79 log_error("Mirrors and striping cannot be combined yet."); 80 return 0; 81 } 82 if (lp->stripe_size & (lp->stripe_size - 1)) { 83 log_error("Stripe size must be power of 2"); 84 return 0; 85 } 86 87 return 1; 88 } 89 90 static int _request_confirmation(struct cmd_context *cmd, 91 const struct volume_group *vg, 92 const struct logical_volume *lv, 93 const struct lvresize_params *lp) 94 { 95 struct lvinfo info; 96 97 memset(&info, 0, sizeof(info)); 98 99 if (!lv_info(cmd, lv, &info, 1, 0) && driver_version(NULL, 0)) { 100 log_error("lv_info failed: aborting"); 101 return 0; 102 } 103 104 if (lp->resizefs) { 105 if (!info.exists) { 106 log_error("Logical volume %s must be activated " 107 "before resizing filesystem", lp->lv_name); 108 return 0; 109 } 110 return 1; 111 } 112 113 if (!info.exists) 114 return 1; 115 116 log_warn("WARNING: Reducing active%s logical volume to %s", 117 info.open_count ? " and open" : "", 118 display_size(cmd, (uint64_t) lp->extents * vg->extent_size)); 119 120 log_warn("THIS MAY DESTROY YOUR DATA (filesystem etc.)"); 121 122 if (!arg_count(cmd, force_ARG)) { 123 if (yes_no_prompt("Do you really want to reduce %s? [y/n]: ", 124 lp->lv_name) == 'n') { 125 log_print("Logical volume %s NOT reduced", lp->lv_name); 126 return 0; 127 } 128 if (sigint_caught()) 129 return 0; 130 } 131 132 return 1; 133 } 134 135 enum fsadm_cmd_e { FSADM_CMD_CHECK, FSADM_CMD_RESIZE }; 136 #define FSADM_CMD "fsadm" 137 #define FSADM_CMD_MAX_ARGS 6 138 139 /* 140 * FSADM_CMD --dry-run --verbose --force check lv_path 141 * FSADM_CMD --dry-run --verbose --force resize lv_path size 142 */ 143 static int _fsadm_cmd(struct cmd_context *cmd, 144 const struct volume_group *vg, 145 const struct lvresize_params *lp, 146 enum fsadm_cmd_e fcmd) 147 { 148 char lv_path[PATH_MAX]; 149 char size_buf[SIZE_BUF]; 150 const char *argv[FSADM_CMD_MAX_ARGS + 2]; 151 unsigned i = 0; 152 153 argv[i++] = FSADM_CMD; 154 155 if (test_mode()) 156 argv[i++] = "--dry-run"; 157 158 if (verbose_level() >= _LOG_NOTICE) 159 argv[i++] = "--verbose"; 160 161 if (arg_count(cmd, force_ARG)) 162 argv[i++] = "--force"; 163 164 argv[i++] = (fcmd == FSADM_CMD_RESIZE) ? "resize" : "check"; 165 166 if (dm_snprintf(lv_path, PATH_MAX, "%s%s/%s", cmd->dev_dir, lp->vg_name, 167 lp->lv_name) < 0) { 168 log_error("Couldn't create LV path for %s", lp->lv_name); 169 return 0; 170 } 171 172 argv[i++] = lv_path; 173 174 if (fcmd == FSADM_CMD_RESIZE) { 175 if (dm_snprintf(size_buf, SIZE_BUF, "%" PRIu64 "K", 176 (uint64_t) lp->extents * vg->extent_size / 2) < 0) { 177 log_error("Couldn't generate new LV size string"); 178 return 0; 179 } 180 181 argv[i++] = size_buf; 182 } 183 184 argv[i] = NULL; 185 186 return exec_cmd(cmd, argv); 187 } 188 189 static int _lvresize_params(struct cmd_context *cmd, int argc, char **argv, 190 struct lvresize_params *lp) 191 { 192 const char *cmd_name; 193 char *st; 194 unsigned dev_dir_found = 0; 195 196 lp->sign = SIGN_NONE; 197 lp->resize = LV_ANY; 198 199 cmd_name = command_name(cmd); 200 if (!strcmp(cmd_name, "lvreduce")) 201 lp->resize = LV_REDUCE; 202 if (!strcmp(cmd_name, "lvextend")) 203 lp->resize = LV_EXTEND; 204 205 /* 206 * Allow omission of extents and size if the user has given us 207 * one or more PVs. Most likely, the intent was "resize this 208 * LV the best you can with these PVs" 209 */ 210 if ((arg_count(cmd, extents_ARG) + arg_count(cmd, size_ARG) == 0) && 211 (argc >= 2)) { 212 lp->extents = 100; 213 lp->percent = PERCENT_PVS; 214 lp->sign = SIGN_PLUS; 215 } else if ((arg_count(cmd, extents_ARG) + 216 arg_count(cmd, size_ARG) != 1)) { 217 log_error("Please specify either size or extents but not " 218 "both."); 219 return 0; 220 } 221 222 if (arg_count(cmd, extents_ARG)) { 223 lp->extents = arg_uint_value(cmd, extents_ARG, 0); 224 lp->sign = arg_sign_value(cmd, extents_ARG, SIGN_NONE); 225 lp->percent = arg_percent_value(cmd, extents_ARG, PERCENT_NONE); 226 } 227 228 /* Size returned in kilobyte units; held in sectors */ 229 if (arg_count(cmd, size_ARG)) { 230 lp->size = arg_uint64_value(cmd, size_ARG, UINT64_C(0)); 231 lp->sign = arg_sign_value(cmd, size_ARG, SIGN_NONE); 232 lp->percent = PERCENT_NONE; 233 } 234 235 if (lp->resize == LV_EXTEND && lp->sign == SIGN_MINUS) { 236 log_error("Negative argument not permitted - use lvreduce"); 237 return 0; 238 } 239 240 if (lp->resize == LV_REDUCE && lp->sign == SIGN_PLUS) { 241 log_error("Positive sign not permitted - use lvextend"); 242 return 0; 243 } 244 245 lp->resizefs = arg_is_set(cmd, resizefs_ARG); 246 lp->nofsck = arg_is_set(cmd, nofsck_ARG); 247 248 if (!argc) { 249 log_error("Please provide the logical volume name"); 250 return 0; 251 } 252 253 lp->lv_name = argv[0]; 254 argv++; 255 argc--; 256 257 if (!(lp->lv_name = skip_dev_dir(cmd, lp->lv_name, &dev_dir_found)) || 258 !(lp->vg_name = extract_vgname(cmd, lp->lv_name))) { 259 log_error("Please provide a volume group name"); 260 return 0; 261 } 262 263 if (!validate_name(lp->vg_name)) { 264 log_error("Volume group name %s has invalid characters", 265 lp->vg_name); 266 return 0; 267 } 268 269 if ((st = strrchr(lp->lv_name, '/'))) 270 lp->lv_name = st + 1; 271 272 lp->argc = argc; 273 lp->argv = argv; 274 275 return 1; 276 } 277 278 static int _lvresize(struct cmd_context *cmd, struct volume_group *vg, 279 struct lvresize_params *lp) 280 { 281 struct logical_volume *lv; 282 struct lvinfo info; 283 uint32_t stripesize_extents = 0; 284 uint32_t seg_stripes = 0, seg_stripesize = 0, seg_size = 0; 285 uint32_t seg_mirrors = 0; 286 uint32_t extents_used = 0; 287 uint32_t size_rest; 288 uint32_t pv_extent_count = 0; 289 alloc_policy_t alloc; 290 struct logical_volume *lock_lv; 291 struct lv_list *lvl; 292 struct lv_segment *seg; 293 uint32_t seg_extents; 294 uint32_t sz, str; 295 struct dm_list *pvh = NULL; 296 297 /* does LV exist? */ 298 if (!(lvl = find_lv_in_vg(vg, lp->lv_name))) { 299 log_error("Logical volume %s not found in volume group %s", 300 lp->lv_name, lp->vg_name); 301 return ECMD_FAILED; 302 } 303 304 if (arg_count(cmd, stripes_ARG)) { 305 if (vg->fid->fmt->features & FMT_SEGMENTS) 306 lp->stripes = arg_uint_value(cmd, stripes_ARG, 1); 307 else 308 log_warn("Varied striping not supported. Ignoring."); 309 } 310 311 if (arg_count(cmd, mirrors_ARG)) { 312 if (vg->fid->fmt->features & FMT_SEGMENTS) 313 lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 1) + 1; 314 else 315 log_warn("Mirrors not supported. Ignoring."); 316 if (arg_sign_value(cmd, mirrors_ARG, 0) == SIGN_MINUS) { 317 log_error("Mirrors argument may not be negative"); 318 return EINVALID_CMD_LINE; 319 } 320 } 321 322 if (arg_count(cmd, stripesize_ARG) && 323 !_validate_stripesize(cmd, vg, lp)) 324 return EINVALID_CMD_LINE; 325 326 lv = lvl->lv; 327 328 if (lv->status & LOCKED) { 329 log_error("Can't resize locked LV %s", lv->name); 330 return ECMD_FAILED; 331 } 332 333 if (lv->status & CONVERTING) { 334 log_error("Can't resize %s while lvconvert in progress", lv->name); 335 return ECMD_FAILED; 336 } 337 338 alloc = arg_uint_value(cmd, alloc_ARG, lv->alloc); 339 340 if (lp->size) { 341 if (lp->size % vg->extent_size) { 342 if (lp->sign == SIGN_MINUS) 343 lp->size -= lp->size % vg->extent_size; 344 else 345 lp->size += vg->extent_size - 346 (lp->size % vg->extent_size); 347 348 log_print("Rounding up size to full physical extent %s", 349 display_size(cmd, (uint64_t) lp->size)); 350 } 351 352 lp->extents = lp->size / vg->extent_size; 353 } 354 355 if (!(pvh = lp->argc ? create_pv_list(cmd->mem, vg, lp->argc, 356 lp->argv, 1) : &vg->pvs)) { 357 stack; 358 return ECMD_FAILED; 359 } 360 361 switch(lp->percent) { 362 case PERCENT_VG: 363 lp->extents = lp->extents * vg->extent_count / 100; 364 break; 365 case PERCENT_FREE: 366 lp->extents = lp->extents * vg->free_count / 100; 367 break; 368 case PERCENT_LV: 369 lp->extents = lp->extents * lv->le_count / 100; 370 break; 371 case PERCENT_PVS: 372 if (lp->argc) { 373 pv_extent_count = pv_list_extents_free(pvh); 374 lp->extents = lp->extents * pv_extent_count / 100; 375 } else 376 lp->extents = lp->extents * vg->extent_count / 100; 377 break; 378 case PERCENT_NONE: 379 break; 380 } 381 382 if (lp->sign == SIGN_PLUS) 383 lp->extents += lv->le_count; 384 385 if (lp->sign == SIGN_MINUS) { 386 if (lp->extents >= lv->le_count) { 387 log_error("Unable to reduce %s below 1 extent", 388 lp->lv_name); 389 return EINVALID_CMD_LINE; 390 } 391 392 lp->extents = lv->le_count - lp->extents; 393 } 394 395 if (!lp->extents) { 396 log_error("New size of 0 not permitted"); 397 return EINVALID_CMD_LINE; 398 } 399 400 if (lp->extents == lv->le_count) { 401 if (!lp->resizefs) { 402 log_error("New size (%d extents) matches existing size " 403 "(%d extents)", lp->extents, lv->le_count); 404 return EINVALID_CMD_LINE; 405 } 406 lp->resize = LV_EXTEND; /* lets pretend zero size extension */ 407 } 408 409 seg_size = lp->extents - lv->le_count; 410 411 /* Use segment type of last segment */ 412 dm_list_iterate_items(seg, &lv->segments) { 413 lp->segtype = seg->segtype; 414 } 415 416 /* FIXME Support LVs with mixed segment types */ 417 if (lp->segtype != arg_ptr_value(cmd, type_ARG, lp->segtype)) { 418 log_error("VolumeType does not match (%s)", lp->segtype->name); 419 return EINVALID_CMD_LINE; 420 } 421 422 /* If extending, find stripes, stripesize & size of last segment */ 423 if ((lp->extents > lv->le_count) && 424 !(lp->stripes == 1 || (lp->stripes > 1 && lp->stripe_size))) { 425 dm_list_iterate_items(seg, &lv->segments) { 426 if (!seg_is_striped(seg)) 427 continue; 428 429 sz = seg->stripe_size; 430 str = seg->area_count; 431 432 if ((seg_stripesize && seg_stripesize != sz && 433 !lp->stripe_size) || 434 (seg_stripes && seg_stripes != str && !lp->stripes)) { 435 log_error("Please specify number of " 436 "stripes (-i) and stripesize (-I)"); 437 return EINVALID_CMD_LINE; 438 } 439 440 seg_stripesize = sz; 441 seg_stripes = str; 442 } 443 444 if (!lp->stripes) 445 lp->stripes = seg_stripes; 446 447 if (!lp->stripe_size && lp->stripes > 1) { 448 if (seg_stripesize) { 449 log_print("Using stripesize of last segment %s", 450 display_size(cmd, (uint64_t) seg_stripesize)); 451 lp->stripe_size = seg_stripesize; 452 } else { 453 lp->stripe_size = 454 find_config_tree_int(cmd, 455 "metadata/stripesize", 456 DEFAULT_STRIPESIZE) * 2; 457 log_print("Using default stripesize %s", 458 display_size(cmd, (uint64_t) lp->stripe_size)); 459 } 460 } 461 } 462 463 /* If extending, find mirrors of last segment */ 464 if ((lp->extents > lv->le_count)) { 465 dm_list_iterate_back_items(seg, &lv->segments) { 466 if (seg_is_mirrored(seg)) 467 seg_mirrors = lv_mirror_count(seg->lv); 468 else 469 seg_mirrors = 0; 470 break; 471 } 472 if (!arg_count(cmd, mirrors_ARG) && seg_mirrors) { 473 log_print("Extending %" PRIu32 " mirror images.", 474 seg_mirrors); 475 lp->mirrors = seg_mirrors; 476 } 477 if ((arg_count(cmd, mirrors_ARG) || seg_mirrors) && 478 (lp->mirrors != seg_mirrors)) { 479 log_error("Cannot vary number of mirrors in LV yet."); 480 return EINVALID_CMD_LINE; 481 } 482 } 483 484 /* If reducing, find stripes, stripesize & size of last segment */ 485 if (lp->extents < lv->le_count) { 486 extents_used = 0; 487 488 if (lp->stripes || lp->stripe_size || lp->mirrors) 489 log_error("Ignoring stripes, stripesize and mirrors " 490 "arguments when reducing"); 491 492 dm_list_iterate_items(seg, &lv->segments) { 493 seg_extents = seg->len; 494 495 if (seg_is_striped(seg)) { 496 seg_stripesize = seg->stripe_size; 497 seg_stripes = seg->area_count; 498 } 499 500 if (seg_is_mirrored(seg)) 501 seg_mirrors = lv_mirror_count(seg->lv); 502 else 503 seg_mirrors = 0; 504 505 if (lp->extents <= extents_used + seg_extents) 506 break; 507 508 extents_used += seg_extents; 509 } 510 511 seg_size = lp->extents - extents_used; 512 lp->stripe_size = seg_stripesize; 513 lp->stripes = seg_stripes; 514 lp->mirrors = seg_mirrors; 515 } 516 517 if (lp->stripes > 1 && !lp->stripe_size) { 518 log_error("Stripesize for striped segment should not be 0!"); 519 return EINVALID_CMD_LINE; 520 } 521 522 if ((lp->stripes > 1)) { 523 if (!(stripesize_extents = lp->stripe_size / vg->extent_size)) 524 stripesize_extents = 1; 525 526 if ((size_rest = seg_size % (lp->stripes * stripesize_extents))) { 527 log_print("Rounding size (%d extents) down to stripe " 528 "boundary size for segment (%d extents)", 529 lp->extents, lp->extents - size_rest); 530 lp->extents = lp->extents - size_rest; 531 } 532 533 if (lp->stripe_size < STRIPE_SIZE_MIN) { 534 log_error("Invalid stripe size %s", 535 display_size(cmd, (uint64_t) lp->stripe_size)); 536 return EINVALID_CMD_LINE; 537 } 538 } 539 540 if (lp->extents < lv->le_count) { 541 if (lp->resize == LV_EXTEND) { 542 log_error("New size given (%d extents) not larger " 543 "than existing size (%d extents)", 544 lp->extents, lv->le_count); 545 return EINVALID_CMD_LINE; 546 } 547 lp->resize = LV_REDUCE; 548 } else if (lp->extents > lv->le_count) { 549 if (lp->resize == LV_REDUCE) { 550 log_error("New size given (%d extents) not less than " 551 "existing size (%d extents)", lp->extents, 552 lv->le_count); 553 return EINVALID_CMD_LINE; 554 } 555 lp->resize = LV_EXTEND; 556 } 557 558 if (lv_is_origin(lv)) { 559 if (lp->resize == LV_REDUCE) { 560 log_error("Snapshot origin volumes cannot be reduced " 561 "in size yet."); 562 return ECMD_FAILED; 563 } 564 565 memset(&info, 0, sizeof(info)); 566 567 if (lv_info(cmd, lv, &info, 0, 0) && info.exists) { 568 log_error("Snapshot origin volumes can be resized " 569 "only while inactive: try lvchange -an"); 570 return ECMD_FAILED; 571 } 572 } 573 574 if ((lp->resize == LV_REDUCE) && lp->argc) 575 log_warn("Ignoring PVs on command line when reducing"); 576 577 /* Request confirmation before operations that are often mistakes. */ 578 if ((lp->resizefs || (lp->resize == LV_REDUCE)) && 579 !_request_confirmation(cmd, vg, lv, lp)) { 580 stack; 581 return ECMD_FAILED; 582 } 583 584 if (lp->resizefs) { 585 if (!lp->nofsck && 586 !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_CHECK)) { 587 stack; 588 return ECMD_FAILED; 589 } 590 591 if ((lp->resize == LV_REDUCE) && 592 !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE)) { 593 stack; 594 return ECMD_FAILED; 595 } 596 } 597 598 if (!archive(vg)) { 599 stack; 600 return ECMD_FAILED; 601 } 602 603 log_print("%sing logical volume %s to %s", 604 (lp->resize == LV_REDUCE) ? "Reduc" : "Extend", 605 lp->lv_name, 606 display_size(cmd, (uint64_t) lp->extents * vg->extent_size)); 607 608 if (lp->resize == LV_REDUCE) { 609 if (!lv_reduce(lv, lv->le_count - lp->extents)) { 610 stack; 611 return ECMD_FAILED; 612 } 613 } else if ((lp->extents > lv->le_count) && /* Ensure we extend */ 614 !lv_extend(lv, lp->segtype, lp->stripes, 615 lp->stripe_size, lp->mirrors, 616 lp->extents - lv->le_count, 617 NULL, 0u, 0u, pvh, alloc)) { 618 stack; 619 return ECMD_FAILED; 620 } 621 622 /* store vg on disk(s) */ 623 if (!vg_write(vg)) { 624 stack; 625 return ECMD_FAILED; 626 } 627 628 /* If snapshot, must suspend all associated devices */ 629 if (lv_is_cow(lv)) 630 lock_lv = origin_from_cow(lv); 631 else 632 lock_lv = lv; 633 634 if (!suspend_lv(cmd, lock_lv)) { 635 log_error("Failed to suspend %s", lp->lv_name); 636 vg_revert(vg); 637 backup(vg); 638 return ECMD_FAILED; 639 } 640 641 if (!vg_commit(vg)) { 642 stack; 643 resume_lv(cmd, lock_lv); 644 backup(vg); 645 return ECMD_FAILED; 646 } 647 648 if (!resume_lv(cmd, lock_lv)) { 649 log_error("Problem reactivating %s", lp->lv_name); 650 backup(vg); 651 return ECMD_FAILED; 652 } 653 654 backup(vg); 655 656 log_print("Logical volume %s successfully resized", lp->lv_name); 657 658 if (lp->resizefs && (lp->resize == LV_EXTEND) && 659 !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE)) { 660 stack; 661 return ECMD_FAILED; 662 } 663 664 return ECMD_PROCESSED; 665 } 666 667 int lvresize(struct cmd_context *cmd, int argc, char **argv) 668 { 669 struct lvresize_params lp; 670 struct volume_group *vg; 671 int r; 672 673 memset(&lp, 0, sizeof(lp)); 674 675 if (!_lvresize_params(cmd, argc, argv, &lp)) 676 return EINVALID_CMD_LINE; 677 678 log_verbose("Finding volume group %s", lp.vg_name); 679 vg = vg_read_for_update(cmd, lp.vg_name, NULL, 0); 680 if (vg_read_error(vg)) { 681 vg_release(vg); 682 stack; 683 return ECMD_FAILED; 684 } 685 686 if (!(r = _lvresize(cmd, vg, &lp))) 687 stack; 688 689 unlock_and_release_vg(cmd, vg, lp.vg_name); 690 691 return r; 692 } 693