]> sourceware.org Git - lvm2.git/blob - tools/lvresize.c
prevent active mirror resize for now
[lvm2.git] / tools / lvresize.c
1 /*
2 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
3 * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
4 *
5 * This file is part of LVM2.
6 *
7 * This copyrighted material is made available to anyone wishing to use,
8 * modify, copy, or redistribute it subject to the terms and conditions
9 * of the GNU General Public License v.2.
10 *
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software Foundation,
13 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 */
15
16 #include "tools.h"
17
18 #define SIZE_BUF 128
19
20 struct lvresize_params {
21 const char *vg_name;
22 const char *lv_name;
23
24 uint32_t stripes;
25 uint32_t stripe_size;
26 uint32_t mirrors;
27
28 struct segment_type *segtype;
29
30 /* size */
31 uint32_t extents;
32 uint64_t size;
33 sign_t sign;
34
35 enum {
36 LV_ANY = 0,
37 LV_REDUCE = 1,
38 LV_EXTEND = 2
39 } resize;
40
41 int resizefs;
42 int nofsck;
43
44 int argc;
45 char **argv;
46 };
47
48 static int _read_params(struct cmd_context *cmd, int argc, char **argv,
49 struct lvresize_params *lp)
50 {
51 const char *cmd_name;
52 char *st;
53
54 lp->sign = SIGN_NONE;
55 lp->resize = LV_ANY;
56
57 cmd_name = command_name(cmd);
58 if (!strcmp(cmd_name, "lvreduce"))
59 lp->resize = LV_REDUCE;
60 if (!strcmp(cmd_name, "lvextend"))
61 lp->resize = LV_EXTEND;
62
63 if (arg_count(cmd, extents_ARG) + arg_count(cmd, size_ARG) != 1) {
64 log_error("Please specify either size or extents (not both)");
65 return 0;
66 }
67
68 if (arg_count(cmd, extents_ARG)) {
69 lp->extents = arg_uint_value(cmd, extents_ARG, 0);
70 lp->sign = arg_sign_value(cmd, extents_ARG, SIGN_NONE);
71 }
72
73 /* Size returned in kilobyte units; held in sectors */
74 if (arg_count(cmd, size_ARG)) {
75 lp->size = arg_uint64_value(cmd, size_ARG, UINT64_C(0)) * 2;
76 lp->sign = arg_sign_value(cmd, size_ARG, SIGN_NONE);
77 }
78
79 if (lp->resize == LV_EXTEND && lp->sign == SIGN_MINUS) {
80 log_error("Negative argument not permitted - use lvreduce");
81 return 0;
82 }
83
84 if (lp->resize == LV_REDUCE && lp->sign == SIGN_PLUS) {
85 log_error("Positive sign not permitted - use lvextend");
86 return 0;
87 }
88
89 lp->resizefs = arg_count(cmd, resizefs_ARG) ? 1 : 0;
90 lp->nofsck = arg_count(cmd, nofsck_ARG) ? 1 : 0;
91
92 if (!argc) {
93 log_error("Please provide the logical volume name");
94 return 0;
95 }
96
97 lp->lv_name = argv[0];
98 argv++;
99 argc--;
100
101 if (!(lp->vg_name = extract_vgname(cmd, lp->lv_name))) {
102 log_error("Please provide a volume group name");
103 return 0;
104 }
105
106 if ((st = strrchr(lp->lv_name, '/')))
107 lp->lv_name = st + 1;
108
109 lp->argc = argc;
110 lp->argv = argv;
111
112 return 1;
113 }
114
115 static int _lvresize(struct cmd_context *cmd, struct lvresize_params *lp)
116 {
117 struct volume_group *vg;
118 struct logical_volume *lv;
119 struct lv_segment *snap_seg;
120 struct lvinfo info;
121 uint32_t stripesize_extents = 0;
122 uint32_t seg_stripes = 0, seg_stripesize = 0, seg_size = 0;
123 uint32_t seg_mirrors = 0;
124 uint32_t extents_used = 0;
125 uint32_t size_rest;
126 alloc_policy_t alloc;
127 char *lock_lvid;
128 struct lv_list *lvl;
129 int consistent = 1;
130 struct lv_segment *seg;
131 uint32_t seg_extents;
132 uint32_t sz, str;
133 struct list *pvh = NULL;
134 char size_buf[SIZE_BUF];
135 char lv_path[PATH_MAX];
136
137 if (!(vg = vg_read(cmd, lp->vg_name, &consistent))) {
138 log_error("Volume group %s doesn't exist", lp->vg_name);
139 return ECMD_FAILED;
140 }
141
142 if (vg->status & EXPORTED_VG) {
143 log_error("Volume group %s is exported", vg->name);
144 return ECMD_FAILED;
145 }
146
147 if (!(vg->status & LVM_WRITE)) {
148 log_error("Volume group %s is read-only", lp->vg_name);
149 return ECMD_FAILED;
150 }
151
152 /* does LV exist? */
153 if (!(lvl = find_lv_in_vg(vg, lp->lv_name))) {
154 log_error("Logical volume %s not found in volume group %s",
155 lp->lv_name, lp->vg_name);
156 return ECMD_FAILED;
157 }
158
159 if (arg_count(cmd, stripes_ARG)) {
160 if (vg->fid->fmt->features & FMT_SEGMENTS)
161 lp->stripes = arg_uint_value(cmd, stripes_ARG, 1);
162 else
163 log_print("Varied striping not supported. Ignoring.");
164 }
165
166 if (arg_count(cmd, mirrors_ARG)) {
167 if (vg->fid->fmt->features & FMT_SEGMENTS)
168 lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 1) + 1;
169 else
170 log_print("Mirrors not supported. Ignoring.");
171 }
172
173 if (arg_count(cmd, stripesize_ARG)) {
174 if (arg_sign_value(cmd, stripesize_ARG, 0) == SIGN_MINUS) {
175 log_error("Stripesize may not be negative.");
176 return ECMD_FAILED;
177 }
178 if (vg->fid->fmt->features & FMT_SEGMENTS)
179 lp->stripe_size = 2 * arg_uint_value(cmd,
180 stripesize_ARG, 0);
181 else
182 log_print("Varied stripesize not supported. Ignoring.");
183 if (lp->mirrors) {
184 log_error("Mirrors and striping cannot be combined yet.");
185 return ECMD_FAILED;
186 }
187 }
188
189 lv = lvl->lv;
190
191 if (lv->status & LOCKED) {
192 log_error("Can't resize locked LV %s", lv->name);
193 return ECMD_FAILED;
194 }
195
196 alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, lv->alloc);
197
198 if (lp->size) {
199 if (lp->size % vg->extent_size) {
200 if (lp->sign == SIGN_MINUS)
201 lp->size -= lp->size % vg->extent_size;
202 else
203 lp->size += vg->extent_size -
204 (lp->size % vg->extent_size);
205
206 log_print("Rounding up size to full physical extent %s",
207 display_size(cmd, (uint64_t) lp->size,
208 SIZE_SHORT));
209 }
210
211 lp->extents = lp->size / vg->extent_size;
212 }
213
214 if (lp->sign == SIGN_PLUS)
215 lp->extents += lv->le_count;
216
217 if (lp->sign == SIGN_MINUS) {
218 if (lp->extents >= lv->le_count) {
219 log_error("Unable to reduce %s below 1 extent",
220 lp->lv_name);
221 return EINVALID_CMD_LINE;
222 }
223
224 lp->extents = lv->le_count - lp->extents;
225 }
226
227 if (!lp->extents) {
228 log_error("New size of 0 not permitted");
229 return EINVALID_CMD_LINE;
230 }
231
232 if (lp->extents == lv->le_count) {
233 log_error("New size (%d extents) matches existing size "
234 "(%d extents)", lp->extents, lv->le_count);
235 return EINVALID_CMD_LINE;
236 }
237
238 seg_size = lp->extents - lv->le_count;
239
240 /* Use segment type of last segment */
241 list_iterate_items(seg, &lv->segments) {
242 lp->segtype = seg->segtype;
243 }
244
245 /* FIXME Support LVs with mixed segment types */
246 if (lp->segtype != (struct segment_type *) arg_ptr_value(cmd, type_ARG,
247 lp->segtype)) {
248 log_error("VolumeType does not match (%s)", lp->segtype->name);
249 return EINVALID_CMD_LINE;
250 }
251
252 /* If extending, find stripes, stripesize & size of last segment */
253 if ((lp->extents > lv->le_count) &&
254 !(lp->stripes == 1 || (lp->stripes > 1 && lp->stripe_size))) {
255 list_iterate_items(seg, &lv->segments) {
256 if (!seg_is_striped(seg))
257 continue;
258
259 sz = seg->stripe_size;
260 str = seg->area_count;
261
262 if ((seg_stripesize && seg_stripesize != sz
263 && !lp->stripe_size) ||
264 (seg_stripes && seg_stripes != str && !lp->stripes)) {
265 log_error("Please specify number of "
266 "stripes (-i) and stripesize (-I)");
267 return EINVALID_CMD_LINE;
268 }
269
270 seg_stripesize = sz;
271 seg_stripes = str;
272 }
273
274 if (!lp->stripes)
275 lp->stripes = seg_stripes;
276
277 if (!lp->stripe_size && lp->stripes > 1) {
278 if (seg_stripesize) {
279 log_print("Using stripesize of last segment "
280 "%dKB", seg_stripesize / 2);
281 lp->stripe_size = seg_stripesize;
282 } else {
283 lp->stripe_size =
284 find_config_int(cmd->cft->root,
285 "metadata/stripesize",
286 DEFAULT_STRIPESIZE) * 2;
287 log_print("Using default stripesize %dKB",
288 lp->stripe_size / 2);
289 }
290 }
291 }
292
293 /* If extending, find mirrors of last segment */
294 if ((lp->extents > lv->le_count)) {
295 list_iterate_back_items(seg, &lv->segments) {
296 if (seg_is_mirrored(seg))
297 seg_mirrors = seg->area_count;
298 else
299 seg_mirrors = 0;
300 break;
301 }
302 if (!arg_count(cmd, mirrors_ARG) && seg_mirrors) {
303 log_print("Extending %" PRIu32 " mirror images.",
304 seg_mirrors);
305 lp->mirrors = seg_mirrors;
306 }
307 if ((arg_count(cmd, mirrors_ARG) || seg_mirrors) &&
308 (lp->mirrors != seg_mirrors)) {
309 log_error("Cannot vary number of mirrors in LV yet.");
310 return EINVALID_CMD_LINE;
311 }
312 }
313
314 /* If reducing, find stripes, stripesize & size of last segment */
315 if (lp->extents < lv->le_count) {
316 extents_used = 0;
317
318 if (lp->stripes || lp->stripe_size || lp->mirrors)
319 log_error("Ignoring stripes, stripesize and mirrors "
320 "arguments when reducing");
321
322 list_iterate_items(seg, &lv->segments) {
323 seg_extents = seg->len;
324
325 if (seg_is_striped(seg)) {
326 seg_stripesize = seg->stripe_size;
327 seg_stripes = seg->area_count;
328 }
329
330 if (seg_is_mirrored(seg))
331 seg_mirrors = seg->area_count;
332 else
333 seg_mirrors = 0;
334
335 if (lp->extents <= extents_used + seg_extents)
336 break;
337
338 extents_used += seg_extents;
339 }
340
341 seg_size = lp->extents - extents_used;
342 lp->stripe_size = seg_stripesize;
343 lp->stripes = seg_stripes;
344 lp->mirrors = seg_mirrors;
345 }
346
347 if (lp->stripes > 1 && !lp->stripe_size) {
348 log_error("Stripesize for striped segment should not be 0!");
349 return EINVALID_CMD_LINE;
350 }
351
352 if ((lp->stripes > 1)) {
353 if (!(stripesize_extents = lp->stripe_size / vg->extent_size))
354 stripesize_extents = 1;
355
356 if ((size_rest = seg_size % (lp->stripes * stripesize_extents))) {
357 log_print("Rounding size (%d extents) down to stripe "
358 "boundary size for segment (%d extents)",
359 lp->extents, lp->extents - size_rest);
360 lp->extents = lp->extents - size_rest;
361 }
362 }
363
364 if (lp->extents == lv->le_count) {
365 log_error("New size (%d extents) matches existing size "
366 "(%d extents)", lp->extents, lv->le_count);
367 return EINVALID_CMD_LINE;
368 }
369
370 if (lp->extents < lv->le_count) {
371 if (lp->resize == LV_EXTEND) {
372 log_error("New size given (%d extents) not larger "
373 "than existing size (%d extents)",
374 lp->extents, lv->le_count);
375 return EINVALID_CMD_LINE;
376 } else
377 lp->resize = LV_REDUCE;
378 }
379
380 if (lp->extents > lv->le_count) {
381 if (lp->resize == LV_REDUCE) {
382 log_error("New size given (%d extents) not less than "
383 "existing size (%d extents)", lp->extents,
384 lv->le_count);
385 return EINVALID_CMD_LINE;
386 } else
387 lp->resize = LV_EXTEND;
388 }
389
390 if (lp->mirrors && activation() &&
391 lv_info(lv, &info, 0) && info.exists) {
392 log_error("Mirrors cannot be resized while active yet.");
393 return ECMD_FAILED;
394 }
395
396 if (lv_is_origin(lv)) {
397 if (lp->resize == LV_REDUCE) {
398 log_error("Snapshot origin volumes cannot be reduced "
399 "in size yet.");
400 return ECMD_FAILED;
401 }
402
403 memset(&info, 0, sizeof(info));
404
405 if (lv_info(lv, &info, 0) && info.exists) {
406 log_error("Snapshot origin volumes can be resized "
407 "only while inactive: try lvchange -an");
408 return ECMD_FAILED;
409 }
410 }
411
412 if (lp->resize == LV_REDUCE) {
413 if (lp->argc)
414 log_print("Ignoring PVs on command line when reducing");
415 } else if (!(pvh = lp->argc ? create_pv_list(cmd->mem, vg, lp->argc,
416 lp->argv, 1) : &vg->pvs)) {
417 stack;
418 return ECMD_FAILED;
419 }
420
421 if (lp->resize == LV_REDUCE || lp->resizefs) {
422 memset(&info, 0, sizeof(info));
423
424 if (!lv_info(lv, &info, 1) && driver_version(NULL, 0)) {
425 log_error("lv_info failed: aborting");
426 return ECMD_FAILED;
427 }
428
429 if (lp->resizefs && !info.exists) {
430 log_error("Logical volume %s must be activated "
431 "before resizing filesystem", lp->lv_name);
432 return ECMD_FAILED;
433 }
434
435 if (info.exists && !lp->resizefs && (lp->resize == LV_REDUCE)) {
436 log_print("WARNING: Reducing active%s logical volume "
437 "to %s", info.open_count ? " and open" : "",
438 display_size(cmd, (uint64_t) lp->extents *
439 vg->extent_size,
440 SIZE_SHORT));
441
442 log_print("THIS MAY DESTROY YOUR DATA "
443 "(filesystem etc.)");
444
445 if (!arg_count(cmd, force_ARG)) {
446 if (yes_no_prompt("Do you really want to "
447 "reduce %s? [y/n]: ",
448 lp->lv_name) == 'n') {
449 log_print("Logical volume %s NOT "
450 "reduced", lp->lv_name);
451 return ECMD_FAILED;
452 }
453 }
454 }
455 }
456
457 if (lp->resizefs) {
458 if (lvm_snprintf(lv_path, PATH_MAX, "%s%s/%s", cmd->dev_dir,
459 lp->vg_name, lp->lv_name) < 0) {
460 log_error("Couldn't create LV path for %s",
461 lp->lv_name);
462 return ECMD_FAILED;
463 }
464
465 if (lvm_snprintf(size_buf, SIZE_BUF, "%" PRIu64,
466 (uint64_t) lp->extents * vg->extent_size / 2)
467 < 0) {
468 log_error("Couldn't generate new LV size string");
469 return ECMD_FAILED;
470 }
471
472 if (!lp->nofsck) {
473 if (!exec_cmd("fsadm", "check", lv_path, NULL)) {
474 stack;
475 return ECMD_FAILED;
476 }
477 }
478
479 if (lp->resize == LV_REDUCE) {
480 if (!exec_cmd("fsadm", "resize", lv_path, size_buf)) {
481 stack;
482 return ECMD_FAILED;
483 }
484 }
485 }
486
487 if (!archive(vg)) {
488 stack;
489 return ECMD_FAILED;
490 }
491
492 log_print("%sing logical volume %s to %s",
493 (lp->resize == LV_REDUCE) ? "Reduc" : "Extend",
494 lp->lv_name,
495 display_size(cmd, (uint64_t) lp->extents * vg->extent_size,
496 SIZE_SHORT));
497
498 if (lp->resize == LV_REDUCE) {
499 if (!lv_reduce(lv, lv->le_count - lp->extents)) {
500 stack;
501 return ECMD_FAILED;
502 }
503 } else if (!lv_extend(lv, lp->segtype, lp->stripes,
504 lp->stripe_size, lp->mirrors,
505 lp->extents - lv->le_count,
506 NULL, 0u, 0u, pvh, alloc)) {
507 stack;
508 return ECMD_FAILED;
509 }
510
511 /* store vg on disk(s) */
512 if (!vg_write(vg)) {
513 stack;
514 return ECMD_FAILED;
515 }
516
517 backup(vg);
518
519 /* If snapshot, must suspend all associated devices */
520 if ((snap_seg = find_cow(lv)))
521 lock_lvid = snap_seg->origin->lvid.s;
522 else
523 lock_lvid = lv->lvid.s;
524
525 if (!suspend_lv(cmd, lock_lvid)) {
526 log_error("Failed to suspend %s", lp->lv_name);
527 vg_revert(vg);
528 return ECMD_FAILED;
529 }
530
531 if (!vg_commit(vg)) {
532 stack;
533 resume_lv(cmd, lock_lvid);
534 return ECMD_FAILED;
535 }
536
537 if (!resume_lv(cmd, lock_lvid)) {
538 log_error("Problem reactivating %s", lp->lv_name);
539 return ECMD_FAILED;
540 }
541
542 log_print("Logical volume %s successfully resized", lp->lv_name);
543
544 if (lp->resizefs && (lp->resize == LV_EXTEND)) {
545 if (!exec_cmd("fsadm", "resize", lv_path, size_buf)) {
546 stack;
547 return ECMD_FAILED;
548 }
549 }
550
551 return ECMD_PROCESSED;
552 }
553
554 int lvresize(struct cmd_context *cmd, int argc, char **argv)
555 {
556 struct lvresize_params lp;
557 int r;
558
559 memset(&lp, 0, sizeof(lp));
560
561 if (!_read_params(cmd, argc, argv, &lp))
562 return EINVALID_CMD_LINE;
563
564 log_verbose("Finding volume group %s", lp.vg_name);
565 if (!lock_vol(cmd, lp.vg_name, LCK_VG_WRITE)) {
566 log_error("Can't get lock for %s", lp.vg_name);
567 return ECMD_FAILED;
568 }
569
570 if (!(r = _lvresize(cmd, &lp)))
571 stack;
572
573 unlock_vg(cmd, lp.vg_name);
574
575 return r;
576 }
This page took 0.064928 seconds and 6 git commands to generate.