1 /* $NetBSD: lvresize.c,v 1.1.1.3 2009/12/02 00:25:53 haad Exp $ */
4 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5 * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
7 * This file is part of LVM2.
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.
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
22 struct lvresize_params {
30 const struct segment_type *segtype;
51 static int _validate_stripesize(struct cmd_context *cmd,
52 const struct volume_group *vg,
53 struct lvresize_params *lp)
55 if (arg_sign_value(cmd, stripesize_ARG, 0) == SIGN_MINUS) {
56 log_error("Stripesize may not be negative.");
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));
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",
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;
76 lp->stripe_size = arg_uint_value(cmd, stripesize_ARG, 0);
79 log_error("Mirrors and striping cannot be combined yet.");
82 if (lp->stripe_size & (lp->stripe_size - 1)) {
83 log_error("Stripe size must be power of 2");
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)
97 memset(&info, 0, sizeof(info));
99 if (!lv_info(cmd, lv, &info, 1, 0) && driver_version(NULL, 0)) {
100 log_error("lv_info failed: aborting");
106 log_error("Logical volume %s must be activated "
107 "before resizing filesystem", lp->lv_name);
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));
120 log_warn("THIS MAY DESTROY YOUR DATA (filesystem etc.)");
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);
135 enum fsadm_cmd_e { FSADM_CMD_CHECK, FSADM_CMD_RESIZE };
136 #define FSADM_CMD "fsadm"
137 #define FSADM_CMD_MAX_ARGS 6
140 * FSADM_CMD --dry-run --verbose --force check lv_path
141 * FSADM_CMD --dry-run --verbose --force resize lv_path size
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)
148 char lv_path[PATH_MAX];
149 char size_buf[SIZE_BUF];
150 const char *argv[FSADM_CMD_MAX_ARGS + 2];
153 argv[i++] = FSADM_CMD;
156 argv[i++] = "--dry-run";
158 if (verbose_level() >= _LOG_NOTICE)
159 argv[i++] = "--verbose";
161 if (arg_count(cmd, force_ARG))
162 argv[i++] = "--force";
164 argv[i++] = (fcmd == FSADM_CMD_RESIZE) ? "resize" : "check";
166 if (dm_snprintf(lv_path, PATH_MAX, "%s%s/%s", cmd->dev_dir, lp->vg_name,
168 log_error("Couldn't create LV path for %s", lp->lv_name);
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");
181 argv[i++] = size_buf;
186 return exec_cmd(cmd, argv);
189 static int _lvresize_params(struct cmd_context *cmd, int argc, char **argv,
190 struct lvresize_params *lp)
192 const char *cmd_name;
194 unsigned dev_dir_found = 0;
196 lp->sign = SIGN_NONE;
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;
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"
210 if ((arg_count(cmd, extents_ARG) + arg_count(cmd, size_ARG) == 0) &&
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 "
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);
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;
235 if (lp->resize == LV_EXTEND && lp->sign == SIGN_MINUS) {
236 log_error("Negative argument not permitted - use lvreduce");
240 if (lp->resize == LV_REDUCE && lp->sign == SIGN_PLUS) {
241 log_error("Positive sign not permitted - use lvextend");
245 lp->resizefs = arg_is_set(cmd, resizefs_ARG);
246 lp->nofsck = arg_is_set(cmd, nofsck_ARG);
249 log_error("Please provide the logical volume name");
253 lp->lv_name = argv[0];
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");
263 if (!validate_name(lp->vg_name)) {
264 log_error("Volume group name %s has invalid characters",
269 if ((st = strrchr(lp->lv_name, '/')))
270 lp->lv_name = st + 1;
278 static int _lvresize(struct cmd_context *cmd, struct volume_group *vg,
279 struct lvresize_params *lp)
281 struct logical_volume *lv;
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;
288 uint32_t pv_extent_count = 0;
289 alloc_policy_t alloc;
290 struct logical_volume *lock_lv;
292 struct lv_segment *seg;
293 uint32_t seg_extents;
295 struct dm_list *pvh = NULL;
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);
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);
308 log_warn("Varied striping not supported. Ignoring.");
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;
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;
322 if (arg_count(cmd, stripesize_ARG) &&
323 !_validate_stripesize(cmd, vg, lp))
324 return EINVALID_CMD_LINE;
328 if (lv->status & LOCKED) {
329 log_error("Can't resize locked LV %s", lv->name);
333 if (lv->status & CONVERTING) {
334 log_error("Can't resize %s while lvconvert in progress", lv->name);
338 alloc = arg_uint_value(cmd, alloc_ARG, lv->alloc);
341 if (lp->size % vg->extent_size) {
342 if (lp->sign == SIGN_MINUS)
343 lp->size -= lp->size % vg->extent_size;
345 lp->size += vg->extent_size -
346 (lp->size % vg->extent_size);
348 log_print("Rounding up size to full physical extent %s",
349 display_size(cmd, (uint64_t) lp->size));
352 lp->extents = lp->size / vg->extent_size;
355 if (!(pvh = lp->argc ? create_pv_list(cmd->mem, vg, lp->argc,
356 lp->argv, 1) : &vg->pvs)) {
361 switch(lp->percent) {
363 lp->extents = lp->extents * vg->extent_count / 100;
366 lp->extents = lp->extents * vg->free_count / 100;
369 lp->extents = lp->extents * lv->le_count / 100;
373 pv_extent_count = pv_list_extents_free(pvh);
374 lp->extents = lp->extents * pv_extent_count / 100;
376 lp->extents = lp->extents * vg->extent_count / 100;
382 if (lp->sign == SIGN_PLUS)
383 lp->extents += lv->le_count;
385 if (lp->sign == SIGN_MINUS) {
386 if (lp->extents >= lv->le_count) {
387 log_error("Unable to reduce %s below 1 extent",
389 return EINVALID_CMD_LINE;
392 lp->extents = lv->le_count - lp->extents;
396 log_error("New size of 0 not permitted");
397 return EINVALID_CMD_LINE;
400 if (lp->extents == lv->le_count) {
402 log_error("New size (%d extents) matches existing size "
403 "(%d extents)", lp->extents, lv->le_count);
404 return EINVALID_CMD_LINE;
406 lp->resize = LV_EXTEND; /* lets pretend zero size extension */
409 seg_size = lp->extents - lv->le_count;
411 /* Use segment type of last segment */
412 dm_list_iterate_items(seg, &lv->segments) {
413 lp->segtype = seg->segtype;
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;
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))
429 sz = seg->stripe_size;
430 str = seg->area_count;
432 if ((seg_stripesize && seg_stripesize != sz &&
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;
445 lp->stripes = seg_stripes;
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;
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));
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);
472 if (!arg_count(cmd, mirrors_ARG) && seg_mirrors) {
473 log_print("Extending %" PRIu32 " mirror images.",
475 lp->mirrors = seg_mirrors;
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;
484 /* If reducing, find stripes, stripesize & size of last segment */
485 if (lp->extents < lv->le_count) {
488 if (lp->stripes || lp->stripe_size || lp->mirrors)
489 log_error("Ignoring stripes, stripesize and mirrors "
490 "arguments when reducing");
492 dm_list_iterate_items(seg, &lv->segments) {
493 seg_extents = seg->len;
495 if (seg_is_striped(seg)) {
496 seg_stripesize = seg->stripe_size;
497 seg_stripes = seg->area_count;
500 if (seg_is_mirrored(seg))
501 seg_mirrors = lv_mirror_count(seg->lv);
505 if (lp->extents <= extents_used + seg_extents)
508 extents_used += seg_extents;
511 seg_size = lp->extents - extents_used;
512 lp->stripe_size = seg_stripesize;
513 lp->stripes = seg_stripes;
514 lp->mirrors = seg_mirrors;
517 if (lp->stripes > 1 && !lp->stripe_size) {
518 log_error("Stripesize for striped segment should not be 0!");
519 return EINVALID_CMD_LINE;
522 if ((lp->stripes > 1)) {
523 if (!(stripesize_extents = lp->stripe_size / vg->extent_size))
524 stripesize_extents = 1;
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;
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;
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;
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,
553 return EINVALID_CMD_LINE;
555 lp->resize = LV_EXTEND;
558 if (lv_is_origin(lv)) {
559 if (lp->resize == LV_REDUCE) {
560 log_error("Snapshot origin volumes cannot be reduced "
565 memset(&info, 0, sizeof(info));
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");
574 if ((lp->resize == LV_REDUCE) && lp->argc)
575 log_warn("Ignoring PVs on command line when reducing");
577 /* Request confirmation before operations that are often mistakes. */
578 if ((lp->resizefs || (lp->resize == LV_REDUCE)) &&
579 !_request_confirmation(cmd, vg, lv, lp)) {
586 !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_CHECK)) {
591 if ((lp->resize == LV_REDUCE) &&
592 !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE)) {
603 log_print("%sing logical volume %s to %s",
604 (lp->resize == LV_REDUCE) ? "Reduc" : "Extend",
606 display_size(cmd, (uint64_t) lp->extents * vg->extent_size));
608 if (lp->resize == LV_REDUCE) {
609 if (!lv_reduce(lv, lv->le_count - lp->extents)) {
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)) {
622 /* store vg on disk(s) */
628 /* If snapshot, must suspend all associated devices */
630 lock_lv = origin_from_cow(lv);
634 if (!suspend_lv(cmd, lock_lv)) {
635 log_error("Failed to suspend %s", lp->lv_name);
641 if (!vg_commit(vg)) {
643 resume_lv(cmd, lock_lv);
648 if (!resume_lv(cmd, lock_lv)) {
649 log_error("Problem reactivating %s", lp->lv_name);
656 log_print("Logical volume %s successfully resized", lp->lv_name);
658 if (lp->resizefs && (lp->resize == LV_EXTEND) &&
659 !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE)) {
664 return ECMD_PROCESSED;
667 int lvresize(struct cmd_context *cmd, int argc, char **argv)
669 struct lvresize_params lp;
670 struct volume_group *vg;
673 memset(&lp, 0, sizeof(lp));
675 if (!_lvresize_params(cmd, argc, argv, &lp))
676 return EINVALID_CMD_LINE;
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)) {
686 if (!(r = _lvresize(cmd, vg, &lp)))
689 unlock_and_release_vg(cmd, vg, lp.vg_name);