1*7c604eeaShaad /* $NetBSD: lvresize.c,v 1.1.1.3 2009/12/02 00:25:53 haad Exp $ */
256a34939Shaad
356a34939Shaad /*
456a34939Shaad * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5*7c604eeaShaad * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
656a34939Shaad *
756a34939Shaad * This file is part of LVM2.
856a34939Shaad *
956a34939Shaad * This copyrighted material is made available to anyone wishing to use,
1056a34939Shaad * modify, copy, or redistribute it subject to the terms and conditions
1156a34939Shaad * of the GNU Lesser General Public License v.2.1.
1256a34939Shaad *
1356a34939Shaad * You should have received a copy of the GNU Lesser General Public License
1456a34939Shaad * along with this program; if not, write to the Free Software Foundation,
1556a34939Shaad * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1656a34939Shaad */
1756a34939Shaad
1856a34939Shaad #include "tools.h"
1956a34939Shaad
2056a34939Shaad #define SIZE_BUF 128
2156a34939Shaad
2256a34939Shaad struct lvresize_params {
2356a34939Shaad const char *vg_name;
2456a34939Shaad const char *lv_name;
2556a34939Shaad
2656a34939Shaad uint32_t stripes;
2756a34939Shaad uint32_t stripe_size;
2856a34939Shaad uint32_t mirrors;
2956a34939Shaad
3056a34939Shaad const struct segment_type *segtype;
3156a34939Shaad
3256a34939Shaad /* size */
3356a34939Shaad uint32_t extents;
3456a34939Shaad uint64_t size;
3556a34939Shaad sign_t sign;
3656a34939Shaad percent_t percent;
3756a34939Shaad
3856a34939Shaad enum {
3956a34939Shaad LV_ANY = 0,
4056a34939Shaad LV_REDUCE = 1,
4156a34939Shaad LV_EXTEND = 2
4256a34939Shaad } resize;
4356a34939Shaad
4456a34939Shaad int resizefs;
4556a34939Shaad int nofsck;
4656a34939Shaad
4756a34939Shaad int argc;
4856a34939Shaad char **argv;
4956a34939Shaad };
5056a34939Shaad
_validate_stripesize(struct cmd_context * cmd,const struct volume_group * vg,struct lvresize_params * lp)51*7c604eeaShaad static int _validate_stripesize(struct cmd_context *cmd,
5256a34939Shaad const struct volume_group *vg,
5356a34939Shaad struct lvresize_params *lp)
5456a34939Shaad {
5556a34939Shaad if (arg_sign_value(cmd, stripesize_ARG, 0) == SIGN_MINUS) {
5656a34939Shaad log_error("Stripesize may not be negative.");
5756a34939Shaad return 0;
5856a34939Shaad }
5956a34939Shaad
6056a34939Shaad if (arg_uint_value(cmd, stripesize_ARG, 0) > STRIPE_SIZE_LIMIT * 2) {
6156a34939Shaad log_error("Stripe size cannot be larger than %s",
6256a34939Shaad display_size(cmd, (uint64_t) STRIPE_SIZE_LIMIT));
6356a34939Shaad return 0;
6456a34939Shaad }
6556a34939Shaad
6656a34939Shaad if (!(vg->fid->fmt->features & FMT_SEGMENTS))
6756a34939Shaad log_warn("Varied stripesize not supported. Ignoring.");
6856a34939Shaad else if (arg_uint_value(cmd, stripesize_ARG, 0) > vg->extent_size * 2) {
6956a34939Shaad log_error("Reducing stripe size %s to maximum, "
7056a34939Shaad "physical extent size %s",
7156a34939Shaad display_size(cmd,
7256a34939Shaad (uint64_t) arg_uint_value(cmd, stripesize_ARG, 0)),
7356a34939Shaad display_size(cmd, (uint64_t) vg->extent_size));
7456a34939Shaad lp->stripe_size = vg->extent_size;
7556a34939Shaad } else
7656a34939Shaad lp->stripe_size = arg_uint_value(cmd, stripesize_ARG, 0);
7756a34939Shaad
7856a34939Shaad if (lp->mirrors) {
7956a34939Shaad log_error("Mirrors and striping cannot be combined yet.");
8056a34939Shaad return 0;
8156a34939Shaad }
8256a34939Shaad if (lp->stripe_size & (lp->stripe_size - 1)) {
8356a34939Shaad log_error("Stripe size must be power of 2");
8456a34939Shaad return 0;
8556a34939Shaad }
8656a34939Shaad
8756a34939Shaad return 1;
8856a34939Shaad }
8956a34939Shaad
_request_confirmation(struct cmd_context * cmd,const struct volume_group * vg,const struct logical_volume * lv,const struct lvresize_params * lp)90*7c604eeaShaad static int _request_confirmation(struct cmd_context *cmd,
9156a34939Shaad const struct volume_group *vg,
9256a34939Shaad const struct logical_volume *lv,
9356a34939Shaad const struct lvresize_params *lp)
9456a34939Shaad {
9556a34939Shaad struct lvinfo info;
9656a34939Shaad
9756a34939Shaad memset(&info, 0, sizeof(info));
9856a34939Shaad
9956a34939Shaad if (!lv_info(cmd, lv, &info, 1, 0) && driver_version(NULL, 0)) {
10056a34939Shaad log_error("lv_info failed: aborting");
10156a34939Shaad return 0;
10256a34939Shaad }
10356a34939Shaad
104*7c604eeaShaad if (lp->resizefs) {
105*7c604eeaShaad if (!info.exists) {
10656a34939Shaad log_error("Logical volume %s must be activated "
10756a34939Shaad "before resizing filesystem", lp->lv_name);
10856a34939Shaad return 0;
10956a34939Shaad }
110*7c604eeaShaad return 1;
111*7c604eeaShaad }
11256a34939Shaad
113*7c604eeaShaad if (!info.exists)
114*7c604eeaShaad return 1;
11556a34939Shaad
116*7c604eeaShaad log_warn("WARNING: Reducing active%s logical volume to %s",
117*7c604eeaShaad info.open_count ? " and open" : "",
118*7c604eeaShaad display_size(cmd, (uint64_t) lp->extents * vg->extent_size));
119*7c604eeaShaad
120*7c604eeaShaad log_warn("THIS MAY DESTROY YOUR DATA (filesystem etc.)");
12156a34939Shaad
12256a34939Shaad if (!arg_count(cmd, force_ARG)) {
123*7c604eeaShaad if (yes_no_prompt("Do you really want to reduce %s? [y/n]: ",
12456a34939Shaad lp->lv_name) == 'n') {
125*7c604eeaShaad log_print("Logical volume %s NOT reduced", lp->lv_name);
12656a34939Shaad return 0;
12756a34939Shaad }
12856a34939Shaad if (sigint_caught())
12956a34939Shaad return 0;
13056a34939Shaad }
13156a34939Shaad
13256a34939Shaad return 1;
13356a34939Shaad }
13456a34939Shaad
135*7c604eeaShaad enum fsadm_cmd_e { FSADM_CMD_CHECK, FSADM_CMD_RESIZE };
136*7c604eeaShaad #define FSADM_CMD "fsadm"
137*7c604eeaShaad #define FSADM_CMD_MAX_ARGS 6
138*7c604eeaShaad
139*7c604eeaShaad /*
140*7c604eeaShaad * FSADM_CMD --dry-run --verbose --force check lv_path
141*7c604eeaShaad * FSADM_CMD --dry-run --verbose --force resize lv_path size
142*7c604eeaShaad */
_fsadm_cmd(struct cmd_context * cmd,const struct volume_group * vg,const struct lvresize_params * lp,enum fsadm_cmd_e fcmd)143*7c604eeaShaad static int _fsadm_cmd(struct cmd_context *cmd,
14456a34939Shaad const struct volume_group *vg,
145*7c604eeaShaad const struct lvresize_params *lp,
146*7c604eeaShaad enum fsadm_cmd_e fcmd)
14756a34939Shaad {
14856a34939Shaad char lv_path[PATH_MAX];
14956a34939Shaad char size_buf[SIZE_BUF];
150*7c604eeaShaad const char *argv[FSADM_CMD_MAX_ARGS + 2];
151*7c604eeaShaad unsigned i = 0;
15256a34939Shaad
153*7c604eeaShaad argv[i++] = FSADM_CMD;
154*7c604eeaShaad
155*7c604eeaShaad if (test_mode())
156*7c604eeaShaad argv[i++] = "--dry-run";
157*7c604eeaShaad
158*7c604eeaShaad if (verbose_level() >= _LOG_NOTICE)
159*7c604eeaShaad argv[i++] = "--verbose";
160*7c604eeaShaad
161*7c604eeaShaad if (arg_count(cmd, force_ARG))
162*7c604eeaShaad argv[i++] = "--force";
163*7c604eeaShaad
164*7c604eeaShaad argv[i++] = (fcmd == FSADM_CMD_RESIZE) ? "resize" : "check";
165*7c604eeaShaad
166*7c604eeaShaad if (dm_snprintf(lv_path, PATH_MAX, "%s%s/%s", cmd->dev_dir, lp->vg_name,
167*7c604eeaShaad lp->lv_name) < 0) {
168*7c604eeaShaad log_error("Couldn't create LV path for %s", lp->lv_name);
16956a34939Shaad return 0;
17056a34939Shaad }
17156a34939Shaad
172*7c604eeaShaad argv[i++] = lv_path;
173*7c604eeaShaad
174*7c604eeaShaad if (fcmd == FSADM_CMD_RESIZE) {
175bec4d750Shaad if (dm_snprintf(size_buf, SIZE_BUF, "%" PRIu64 "K",
17656a34939Shaad (uint64_t) lp->extents * vg->extent_size / 2) < 0) {
17756a34939Shaad log_error("Couldn't generate new LV size string");
17856a34939Shaad return 0;
17956a34939Shaad }
18056a34939Shaad
181*7c604eeaShaad argv[i++] = size_buf;
182*7c604eeaShaad }
18356a34939Shaad
184*7c604eeaShaad argv[i] = NULL;
18556a34939Shaad
186*7c604eeaShaad return exec_cmd(cmd, argv);
18756a34939Shaad }
18856a34939Shaad
_lvresize_params(struct cmd_context * cmd,int argc,char ** argv,struct lvresize_params * lp)18956a34939Shaad static int _lvresize_params(struct cmd_context *cmd, int argc, char **argv,
19056a34939Shaad struct lvresize_params *lp)
19156a34939Shaad {
19256a34939Shaad const char *cmd_name;
19356a34939Shaad char *st;
19456a34939Shaad unsigned dev_dir_found = 0;
19556a34939Shaad
19656a34939Shaad lp->sign = SIGN_NONE;
19756a34939Shaad lp->resize = LV_ANY;
19856a34939Shaad
19956a34939Shaad cmd_name = command_name(cmd);
20056a34939Shaad if (!strcmp(cmd_name, "lvreduce"))
20156a34939Shaad lp->resize = LV_REDUCE;
20256a34939Shaad if (!strcmp(cmd_name, "lvextend"))
20356a34939Shaad lp->resize = LV_EXTEND;
20456a34939Shaad
20556a34939Shaad /*
20656a34939Shaad * Allow omission of extents and size if the user has given us
20756a34939Shaad * one or more PVs. Most likely, the intent was "resize this
20856a34939Shaad * LV the best you can with these PVs"
20956a34939Shaad */
21056a34939Shaad if ((arg_count(cmd, extents_ARG) + arg_count(cmd, size_ARG) == 0) &&
21156a34939Shaad (argc >= 2)) {
21256a34939Shaad lp->extents = 100;
21356a34939Shaad lp->percent = PERCENT_PVS;
21456a34939Shaad lp->sign = SIGN_PLUS;
21556a34939Shaad } else if ((arg_count(cmd, extents_ARG) +
21656a34939Shaad arg_count(cmd, size_ARG) != 1)) {
21756a34939Shaad log_error("Please specify either size or extents but not "
21856a34939Shaad "both.");
21956a34939Shaad return 0;
22056a34939Shaad }
22156a34939Shaad
22256a34939Shaad if (arg_count(cmd, extents_ARG)) {
22356a34939Shaad lp->extents = arg_uint_value(cmd, extents_ARG, 0);
22456a34939Shaad lp->sign = arg_sign_value(cmd, extents_ARG, SIGN_NONE);
22556a34939Shaad lp->percent = arg_percent_value(cmd, extents_ARG, PERCENT_NONE);
22656a34939Shaad }
22756a34939Shaad
22856a34939Shaad /* Size returned in kilobyte units; held in sectors */
22956a34939Shaad if (arg_count(cmd, size_ARG)) {
23056a34939Shaad lp->size = arg_uint64_value(cmd, size_ARG, UINT64_C(0));
23156a34939Shaad lp->sign = arg_sign_value(cmd, size_ARG, SIGN_NONE);
23256a34939Shaad lp->percent = PERCENT_NONE;
23356a34939Shaad }
23456a34939Shaad
23556a34939Shaad if (lp->resize == LV_EXTEND && lp->sign == SIGN_MINUS) {
23656a34939Shaad log_error("Negative argument not permitted - use lvreduce");
23756a34939Shaad return 0;
23856a34939Shaad }
23956a34939Shaad
24056a34939Shaad if (lp->resize == LV_REDUCE && lp->sign == SIGN_PLUS) {
24156a34939Shaad log_error("Positive sign not permitted - use lvextend");
24256a34939Shaad return 0;
24356a34939Shaad }
24456a34939Shaad
245*7c604eeaShaad lp->resizefs = arg_is_set(cmd, resizefs_ARG);
246*7c604eeaShaad lp->nofsck = arg_is_set(cmd, nofsck_ARG);
24756a34939Shaad
24856a34939Shaad if (!argc) {
24956a34939Shaad log_error("Please provide the logical volume name");
25056a34939Shaad return 0;
25156a34939Shaad }
25256a34939Shaad
25356a34939Shaad lp->lv_name = argv[0];
25456a34939Shaad argv++;
25556a34939Shaad argc--;
25656a34939Shaad
25756a34939Shaad if (!(lp->lv_name = skip_dev_dir(cmd, lp->lv_name, &dev_dir_found)) ||
25856a34939Shaad !(lp->vg_name = extract_vgname(cmd, lp->lv_name))) {
25956a34939Shaad log_error("Please provide a volume group name");
26056a34939Shaad return 0;
26156a34939Shaad }
26256a34939Shaad
26356a34939Shaad if (!validate_name(lp->vg_name)) {
26456a34939Shaad log_error("Volume group name %s has invalid characters",
26556a34939Shaad lp->vg_name);
26656a34939Shaad return 0;
26756a34939Shaad }
26856a34939Shaad
26956a34939Shaad if ((st = strrchr(lp->lv_name, '/')))
27056a34939Shaad lp->lv_name = st + 1;
27156a34939Shaad
27256a34939Shaad lp->argc = argc;
27356a34939Shaad lp->argv = argv;
27456a34939Shaad
27556a34939Shaad return 1;
27656a34939Shaad }
27756a34939Shaad
_lvresize(struct cmd_context * cmd,struct volume_group * vg,struct lvresize_params * lp)27856a34939Shaad static int _lvresize(struct cmd_context *cmd, struct volume_group *vg,
27956a34939Shaad struct lvresize_params *lp)
28056a34939Shaad {
28156a34939Shaad struct logical_volume *lv;
28256a34939Shaad struct lvinfo info;
28356a34939Shaad uint32_t stripesize_extents = 0;
28456a34939Shaad uint32_t seg_stripes = 0, seg_stripesize = 0, seg_size = 0;
28556a34939Shaad uint32_t seg_mirrors = 0;
28656a34939Shaad uint32_t extents_used = 0;
28756a34939Shaad uint32_t size_rest;
28856a34939Shaad uint32_t pv_extent_count = 0;
28956a34939Shaad alloc_policy_t alloc;
29056a34939Shaad struct logical_volume *lock_lv;
29156a34939Shaad struct lv_list *lvl;
29256a34939Shaad struct lv_segment *seg;
29356a34939Shaad uint32_t seg_extents;
29456a34939Shaad uint32_t sz, str;
29556a34939Shaad struct dm_list *pvh = NULL;
29656a34939Shaad
29756a34939Shaad /* does LV exist? */
29856a34939Shaad if (!(lvl = find_lv_in_vg(vg, lp->lv_name))) {
29956a34939Shaad log_error("Logical volume %s not found in volume group %s",
30056a34939Shaad lp->lv_name, lp->vg_name);
30156a34939Shaad return ECMD_FAILED;
30256a34939Shaad }
30356a34939Shaad
30456a34939Shaad if (arg_count(cmd, stripes_ARG)) {
30556a34939Shaad if (vg->fid->fmt->features & FMT_SEGMENTS)
30656a34939Shaad lp->stripes = arg_uint_value(cmd, stripes_ARG, 1);
30756a34939Shaad else
30856a34939Shaad log_warn("Varied striping not supported. Ignoring.");
30956a34939Shaad }
31056a34939Shaad
31156a34939Shaad if (arg_count(cmd, mirrors_ARG)) {
31256a34939Shaad if (vg->fid->fmt->features & FMT_SEGMENTS)
31356a34939Shaad lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 1) + 1;
31456a34939Shaad else
31556a34939Shaad log_warn("Mirrors not supported. Ignoring.");
31656a34939Shaad if (arg_sign_value(cmd, mirrors_ARG, 0) == SIGN_MINUS) {
31756a34939Shaad log_error("Mirrors argument may not be negative");
31856a34939Shaad return EINVALID_CMD_LINE;
31956a34939Shaad }
32056a34939Shaad }
32156a34939Shaad
322*7c604eeaShaad if (arg_count(cmd, stripesize_ARG) &&
323*7c604eeaShaad !_validate_stripesize(cmd, vg, lp))
32456a34939Shaad return EINVALID_CMD_LINE;
32556a34939Shaad
32656a34939Shaad lv = lvl->lv;
32756a34939Shaad
32856a34939Shaad if (lv->status & LOCKED) {
32956a34939Shaad log_error("Can't resize locked LV %s", lv->name);
33056a34939Shaad return ECMD_FAILED;
33156a34939Shaad }
33256a34939Shaad
33356a34939Shaad if (lv->status & CONVERTING) {
33456a34939Shaad log_error("Can't resize %s while lvconvert in progress", lv->name);
33556a34939Shaad return ECMD_FAILED;
33656a34939Shaad }
33756a34939Shaad
33856a34939Shaad alloc = arg_uint_value(cmd, alloc_ARG, lv->alloc);
33956a34939Shaad
34056a34939Shaad if (lp->size) {
34156a34939Shaad if (lp->size % vg->extent_size) {
34256a34939Shaad if (lp->sign == SIGN_MINUS)
34356a34939Shaad lp->size -= lp->size % vg->extent_size;
34456a34939Shaad else
34556a34939Shaad lp->size += vg->extent_size -
34656a34939Shaad (lp->size % vg->extent_size);
34756a34939Shaad
34856a34939Shaad log_print("Rounding up size to full physical extent %s",
34956a34939Shaad display_size(cmd, (uint64_t) lp->size));
35056a34939Shaad }
35156a34939Shaad
35256a34939Shaad lp->extents = lp->size / vg->extent_size;
35356a34939Shaad }
35456a34939Shaad
35556a34939Shaad if (!(pvh = lp->argc ? create_pv_list(cmd->mem, vg, lp->argc,
35656a34939Shaad lp->argv, 1) : &vg->pvs)) {
35756a34939Shaad stack;
35856a34939Shaad return ECMD_FAILED;
35956a34939Shaad }
36056a34939Shaad
36156a34939Shaad switch(lp->percent) {
36256a34939Shaad case PERCENT_VG:
36356a34939Shaad lp->extents = lp->extents * vg->extent_count / 100;
36456a34939Shaad break;
36556a34939Shaad case PERCENT_FREE:
36656a34939Shaad lp->extents = lp->extents * vg->free_count / 100;
36756a34939Shaad break;
36856a34939Shaad case PERCENT_LV:
36956a34939Shaad lp->extents = lp->extents * lv->le_count / 100;
37056a34939Shaad break;
37156a34939Shaad case PERCENT_PVS:
372*7c604eeaShaad if (lp->argc) {
37356a34939Shaad pv_extent_count = pv_list_extents_free(pvh);
37456a34939Shaad lp->extents = lp->extents * pv_extent_count / 100;
375*7c604eeaShaad } else
376*7c604eeaShaad lp->extents = lp->extents * vg->extent_count / 100;
37756a34939Shaad break;
37856a34939Shaad case PERCENT_NONE:
37956a34939Shaad break;
38056a34939Shaad }
38156a34939Shaad
38256a34939Shaad if (lp->sign == SIGN_PLUS)
38356a34939Shaad lp->extents += lv->le_count;
38456a34939Shaad
38556a34939Shaad if (lp->sign == SIGN_MINUS) {
38656a34939Shaad if (lp->extents >= lv->le_count) {
38756a34939Shaad log_error("Unable to reduce %s below 1 extent",
38856a34939Shaad lp->lv_name);
38956a34939Shaad return EINVALID_CMD_LINE;
39056a34939Shaad }
39156a34939Shaad
39256a34939Shaad lp->extents = lv->le_count - lp->extents;
39356a34939Shaad }
39456a34939Shaad
39556a34939Shaad if (!lp->extents) {
39656a34939Shaad log_error("New size of 0 not permitted");
39756a34939Shaad return EINVALID_CMD_LINE;
39856a34939Shaad }
39956a34939Shaad
40056a34939Shaad if (lp->extents == lv->le_count) {
401*7c604eeaShaad if (!lp->resizefs) {
40256a34939Shaad log_error("New size (%d extents) matches existing size "
40356a34939Shaad "(%d extents)", lp->extents, lv->le_count);
40456a34939Shaad return EINVALID_CMD_LINE;
40556a34939Shaad }
406*7c604eeaShaad lp->resize = LV_EXTEND; /* lets pretend zero size extension */
407*7c604eeaShaad }
40856a34939Shaad
40956a34939Shaad seg_size = lp->extents - lv->le_count;
41056a34939Shaad
41156a34939Shaad /* Use segment type of last segment */
41256a34939Shaad dm_list_iterate_items(seg, &lv->segments) {
41356a34939Shaad lp->segtype = seg->segtype;
41456a34939Shaad }
41556a34939Shaad
41656a34939Shaad /* FIXME Support LVs with mixed segment types */
41756a34939Shaad if (lp->segtype != arg_ptr_value(cmd, type_ARG, lp->segtype)) {
41856a34939Shaad log_error("VolumeType does not match (%s)", lp->segtype->name);
41956a34939Shaad return EINVALID_CMD_LINE;
42056a34939Shaad }
42156a34939Shaad
42256a34939Shaad /* If extending, find stripes, stripesize & size of last segment */
42356a34939Shaad if ((lp->extents > lv->le_count) &&
42456a34939Shaad !(lp->stripes == 1 || (lp->stripes > 1 && lp->stripe_size))) {
42556a34939Shaad dm_list_iterate_items(seg, &lv->segments) {
42656a34939Shaad if (!seg_is_striped(seg))
42756a34939Shaad continue;
42856a34939Shaad
42956a34939Shaad sz = seg->stripe_size;
43056a34939Shaad str = seg->area_count;
43156a34939Shaad
432*7c604eeaShaad if ((seg_stripesize && seg_stripesize != sz &&
433*7c604eeaShaad !lp->stripe_size) ||
43456a34939Shaad (seg_stripes && seg_stripes != str && !lp->stripes)) {
43556a34939Shaad log_error("Please specify number of "
43656a34939Shaad "stripes (-i) and stripesize (-I)");
43756a34939Shaad return EINVALID_CMD_LINE;
43856a34939Shaad }
43956a34939Shaad
44056a34939Shaad seg_stripesize = sz;
44156a34939Shaad seg_stripes = str;
44256a34939Shaad }
44356a34939Shaad
44456a34939Shaad if (!lp->stripes)
44556a34939Shaad lp->stripes = seg_stripes;
44656a34939Shaad
44756a34939Shaad if (!lp->stripe_size && lp->stripes > 1) {
44856a34939Shaad if (seg_stripesize) {
44956a34939Shaad log_print("Using stripesize of last segment %s",
45056a34939Shaad display_size(cmd, (uint64_t) seg_stripesize));
45156a34939Shaad lp->stripe_size = seg_stripesize;
45256a34939Shaad } else {
45356a34939Shaad lp->stripe_size =
45456a34939Shaad find_config_tree_int(cmd,
45556a34939Shaad "metadata/stripesize",
45656a34939Shaad DEFAULT_STRIPESIZE) * 2;
45756a34939Shaad log_print("Using default stripesize %s",
45856a34939Shaad display_size(cmd, (uint64_t) lp->stripe_size));
45956a34939Shaad }
46056a34939Shaad }
46156a34939Shaad }
46256a34939Shaad
46356a34939Shaad /* If extending, find mirrors of last segment */
46456a34939Shaad if ((lp->extents > lv->le_count)) {
46556a34939Shaad dm_list_iterate_back_items(seg, &lv->segments) {
46656a34939Shaad if (seg_is_mirrored(seg))
46756a34939Shaad seg_mirrors = lv_mirror_count(seg->lv);
46856a34939Shaad else
46956a34939Shaad seg_mirrors = 0;
47056a34939Shaad break;
47156a34939Shaad }
47256a34939Shaad if (!arg_count(cmd, mirrors_ARG) && seg_mirrors) {
47356a34939Shaad log_print("Extending %" PRIu32 " mirror images.",
47456a34939Shaad seg_mirrors);
47556a34939Shaad lp->mirrors = seg_mirrors;
47656a34939Shaad }
47756a34939Shaad if ((arg_count(cmd, mirrors_ARG) || seg_mirrors) &&
47856a34939Shaad (lp->mirrors != seg_mirrors)) {
47956a34939Shaad log_error("Cannot vary number of mirrors in LV yet.");
48056a34939Shaad return EINVALID_CMD_LINE;
48156a34939Shaad }
48256a34939Shaad }
48356a34939Shaad
48456a34939Shaad /* If reducing, find stripes, stripesize & size of last segment */
48556a34939Shaad if (lp->extents < lv->le_count) {
48656a34939Shaad extents_used = 0;
48756a34939Shaad
48856a34939Shaad if (lp->stripes || lp->stripe_size || lp->mirrors)
48956a34939Shaad log_error("Ignoring stripes, stripesize and mirrors "
49056a34939Shaad "arguments when reducing");
49156a34939Shaad
49256a34939Shaad dm_list_iterate_items(seg, &lv->segments) {
49356a34939Shaad seg_extents = seg->len;
49456a34939Shaad
49556a34939Shaad if (seg_is_striped(seg)) {
49656a34939Shaad seg_stripesize = seg->stripe_size;
49756a34939Shaad seg_stripes = seg->area_count;
49856a34939Shaad }
49956a34939Shaad
50056a34939Shaad if (seg_is_mirrored(seg))
50156a34939Shaad seg_mirrors = lv_mirror_count(seg->lv);
50256a34939Shaad else
50356a34939Shaad seg_mirrors = 0;
50456a34939Shaad
50556a34939Shaad if (lp->extents <= extents_used + seg_extents)
50656a34939Shaad break;
50756a34939Shaad
50856a34939Shaad extents_used += seg_extents;
50956a34939Shaad }
51056a34939Shaad
51156a34939Shaad seg_size = lp->extents - extents_used;
51256a34939Shaad lp->stripe_size = seg_stripesize;
51356a34939Shaad lp->stripes = seg_stripes;
51456a34939Shaad lp->mirrors = seg_mirrors;
51556a34939Shaad }
51656a34939Shaad
51756a34939Shaad if (lp->stripes > 1 && !lp->stripe_size) {
51856a34939Shaad log_error("Stripesize for striped segment should not be 0!");
51956a34939Shaad return EINVALID_CMD_LINE;
52056a34939Shaad }
52156a34939Shaad
52256a34939Shaad if ((lp->stripes > 1)) {
52356a34939Shaad if (!(stripesize_extents = lp->stripe_size / vg->extent_size))
52456a34939Shaad stripesize_extents = 1;
52556a34939Shaad
52656a34939Shaad if ((size_rest = seg_size % (lp->stripes * stripesize_extents))) {
52756a34939Shaad log_print("Rounding size (%d extents) down to stripe "
52856a34939Shaad "boundary size for segment (%d extents)",
52956a34939Shaad lp->extents, lp->extents - size_rest);
53056a34939Shaad lp->extents = lp->extents - size_rest;
53156a34939Shaad }
53256a34939Shaad
53356a34939Shaad if (lp->stripe_size < STRIPE_SIZE_MIN) {
53456a34939Shaad log_error("Invalid stripe size %s",
53556a34939Shaad display_size(cmd, (uint64_t) lp->stripe_size));
53656a34939Shaad return EINVALID_CMD_LINE;
53756a34939Shaad }
53856a34939Shaad }
53956a34939Shaad
54056a34939Shaad if (lp->extents < lv->le_count) {
54156a34939Shaad if (lp->resize == LV_EXTEND) {
54256a34939Shaad log_error("New size given (%d extents) not larger "
54356a34939Shaad "than existing size (%d extents)",
54456a34939Shaad lp->extents, lv->le_count);
54556a34939Shaad return EINVALID_CMD_LINE;
54656a34939Shaad }
547*7c604eeaShaad lp->resize = LV_REDUCE;
548*7c604eeaShaad } else if (lp->extents > lv->le_count) {
54956a34939Shaad if (lp->resize == LV_REDUCE) {
55056a34939Shaad log_error("New size given (%d extents) not less than "
55156a34939Shaad "existing size (%d extents)", lp->extents,
55256a34939Shaad lv->le_count);
55356a34939Shaad return EINVALID_CMD_LINE;
55456a34939Shaad }
555*7c604eeaShaad lp->resize = LV_EXTEND;
55656a34939Shaad }
55756a34939Shaad
55856a34939Shaad if (lv_is_origin(lv)) {
55956a34939Shaad if (lp->resize == LV_REDUCE) {
56056a34939Shaad log_error("Snapshot origin volumes cannot be reduced "
56156a34939Shaad "in size yet.");
56256a34939Shaad return ECMD_FAILED;
56356a34939Shaad }
56456a34939Shaad
56556a34939Shaad memset(&info, 0, sizeof(info));
56656a34939Shaad
56756a34939Shaad if (lv_info(cmd, lv, &info, 0, 0) && info.exists) {
56856a34939Shaad log_error("Snapshot origin volumes can be resized "
56956a34939Shaad "only while inactive: try lvchange -an");
57056a34939Shaad return ECMD_FAILED;
57156a34939Shaad }
57256a34939Shaad }
57356a34939Shaad
574*7c604eeaShaad if ((lp->resize == LV_REDUCE) && lp->argc)
57556a34939Shaad log_warn("Ignoring PVs on command line when reducing");
57656a34939Shaad
577*7c604eeaShaad /* Request confirmation before operations that are often mistakes. */
578*7c604eeaShaad if ((lp->resizefs || (lp->resize == LV_REDUCE)) &&
579*7c604eeaShaad !_request_confirmation(cmd, vg, lv, lp)) {
580*7c604eeaShaad stack;
58156a34939Shaad return ECMD_FAILED;
58256a34939Shaad }
58356a34939Shaad
58456a34939Shaad if (lp->resizefs) {
585*7c604eeaShaad if (!lp->nofsck &&
586*7c604eeaShaad !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_CHECK)) {
587*7c604eeaShaad stack;
58856a34939Shaad return ECMD_FAILED;
58956a34939Shaad }
59056a34939Shaad
591*7c604eeaShaad if ((lp->resize == LV_REDUCE) &&
592*7c604eeaShaad !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE)) {
593*7c604eeaShaad stack;
594*7c604eeaShaad return ECMD_FAILED;
595*7c604eeaShaad }
596*7c604eeaShaad }
597*7c604eeaShaad
59856a34939Shaad if (!archive(vg)) {
59956a34939Shaad stack;
60056a34939Shaad return ECMD_FAILED;
60156a34939Shaad }
60256a34939Shaad
60356a34939Shaad log_print("%sing logical volume %s to %s",
60456a34939Shaad (lp->resize == LV_REDUCE) ? "Reduc" : "Extend",
60556a34939Shaad lp->lv_name,
60656a34939Shaad display_size(cmd, (uint64_t) lp->extents * vg->extent_size));
60756a34939Shaad
60856a34939Shaad if (lp->resize == LV_REDUCE) {
60956a34939Shaad if (!lv_reduce(lv, lv->le_count - lp->extents)) {
61056a34939Shaad stack;
61156a34939Shaad return ECMD_FAILED;
61256a34939Shaad }
613*7c604eeaShaad } else if ((lp->extents > lv->le_count) && /* Ensure we extend */
614*7c604eeaShaad !lv_extend(lv, lp->segtype, lp->stripes,
61556a34939Shaad lp->stripe_size, lp->mirrors,
61656a34939Shaad lp->extents - lv->le_count,
61756a34939Shaad NULL, 0u, 0u, pvh, alloc)) {
61856a34939Shaad stack;
61956a34939Shaad return ECMD_FAILED;
62056a34939Shaad }
62156a34939Shaad
62256a34939Shaad /* store vg on disk(s) */
62356a34939Shaad if (!vg_write(vg)) {
62456a34939Shaad stack;
62556a34939Shaad return ECMD_FAILED;
62656a34939Shaad }
62756a34939Shaad
62856a34939Shaad /* If snapshot, must suspend all associated devices */
62956a34939Shaad if (lv_is_cow(lv))
63056a34939Shaad lock_lv = origin_from_cow(lv);
63156a34939Shaad else
63256a34939Shaad lock_lv = lv;
63356a34939Shaad
63456a34939Shaad if (!suspend_lv(cmd, lock_lv)) {
63556a34939Shaad log_error("Failed to suspend %s", lp->lv_name);
63656a34939Shaad vg_revert(vg);
637*7c604eeaShaad backup(vg);
63856a34939Shaad return ECMD_FAILED;
63956a34939Shaad }
64056a34939Shaad
64156a34939Shaad if (!vg_commit(vg)) {
64256a34939Shaad stack;
64356a34939Shaad resume_lv(cmd, lock_lv);
644*7c604eeaShaad backup(vg);
64556a34939Shaad return ECMD_FAILED;
64656a34939Shaad }
64756a34939Shaad
64856a34939Shaad if (!resume_lv(cmd, lock_lv)) {
64956a34939Shaad log_error("Problem reactivating %s", lp->lv_name);
650*7c604eeaShaad backup(vg);
65156a34939Shaad return ECMD_FAILED;
65256a34939Shaad }
65356a34939Shaad
654*7c604eeaShaad backup(vg);
655*7c604eeaShaad
65656a34939Shaad log_print("Logical volume %s successfully resized", lp->lv_name);
65756a34939Shaad
658*7c604eeaShaad if (lp->resizefs && (lp->resize == LV_EXTEND) &&
659*7c604eeaShaad !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE)) {
66056a34939Shaad stack;
66156a34939Shaad return ECMD_FAILED;
66256a34939Shaad }
66356a34939Shaad
66456a34939Shaad return ECMD_PROCESSED;
66556a34939Shaad }
66656a34939Shaad
lvresize(struct cmd_context * cmd,int argc,char ** argv)66756a34939Shaad int lvresize(struct cmd_context *cmd, int argc, char **argv)
66856a34939Shaad {
66956a34939Shaad struct lvresize_params lp;
67056a34939Shaad struct volume_group *vg;
67156a34939Shaad int r;
67256a34939Shaad
67356a34939Shaad memset(&lp, 0, sizeof(lp));
67456a34939Shaad
67556a34939Shaad if (!_lvresize_params(cmd, argc, argv, &lp))
67656a34939Shaad return EINVALID_CMD_LINE;
67756a34939Shaad
67856a34939Shaad log_verbose("Finding volume group %s", lp.vg_name);
679*7c604eeaShaad vg = vg_read_for_update(cmd, lp.vg_name, NULL, 0);
680*7c604eeaShaad if (vg_read_error(vg)) {
681*7c604eeaShaad vg_release(vg);
68256a34939Shaad stack;
68356a34939Shaad return ECMD_FAILED;
68456a34939Shaad }
68556a34939Shaad
68656a34939Shaad if (!(r = _lvresize(cmd, vg, &lp)))
68756a34939Shaad stack;
68856a34939Shaad
689*7c604eeaShaad unlock_and_release_vg(cmd, vg, lp.vg_name);
69056a34939Shaad
69156a34939Shaad return r;
69256a34939Shaad }
693