]> sourceware.org Git - lvm2.git/blob - tools/lvresize.c
Introduce origin_from_cow()
[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 lvinfo info;
120 uint32_t stripesize_extents = 0;
121 uint32_t seg_stripes = 0, seg_stripesize = 0, seg_size = 0;
122 uint32_t seg_mirrors = 0;
123 uint32_t extents_used = 0;
124 uint32_t size_rest;
125 alloc_policy_t alloc;
126 struct logical_volume *lock_lv;
127 struct lv_list *lvl;
128 int consistent = 1;
129 struct lv_segment *seg;
130 uint32_t seg_extents;
131 uint32_t sz, str;
132 struct list *pvh = NULL;
133 char size_buf[SIZE_BUF];
134 char lv_path[PATH_MAX];
135
136 if (!(vg = vg_read(cmd, lp->vg_name, &consistent))) {
137 log_error("Volume group %s doesn't exist", lp->vg_name);
138 return ECMD_FAILED;
139 }
140
141 if (vg->status & EXPORTED_VG) {
142 log_error("Volume group %s is exported", vg->name);
143 return ECMD_FAILED;
144 }
145
146 if (!(vg->status & LVM_WRITE)) {
147 log_error("Volume group %s is read-only", lp->vg_name);
148 return ECMD_FAILED;
149 }
150
151 /* does LV exist? */
152 if (!(lvl = find_lv_in_vg(vg, lp->lv_name))) {
153 log_error("Logical volume %s not found in volume group %s",
154 lp->lv_name, lp->vg_name);
155 return ECMD_FAILED;
156 }
157
158 if (arg_count(cmd, stripes_ARG)) {
159 if (vg->fid->fmt->features & FMT_SEGMENTS)
160 lp->stripes = arg_uint_value(cmd, stripes_ARG, 1);
161 else
162 log_print("Varied striping not supported. Ignoring.");
163 }
164
165 if (arg_count(cmd, mirrors_ARG)) {
166 if (vg->fid->fmt->features & FMT_SEGMENTS)
167 lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 1) + 1;
168 else
169 log_print("Mirrors not supported. Ignoring.");
170 if (arg_sign_value(cmd, mirrors_ARG, 0) == SIGN_MINUS) {
171 log_error("Mirrors argument may not be negative");
172 return 0;
173 }
174 }
175
176 if (arg_count(cmd, stripesize_ARG)) {
177 if (arg_sign_value(cmd, stripesize_ARG, 0) == SIGN_MINUS) {
178 log_error("Stripesize may not be negative.");
179 return ECMD_FAILED;
180 }
181 if (vg->fid->fmt->features & FMT_SEGMENTS)
182 lp->stripe_size = 2 * arg_uint_value(cmd,
183 stripesize_ARG, 0);
184 else
185 log_print("Varied stripesize not supported. Ignoring.");
186 if (lp->mirrors) {
187 log_error("Mirrors and striping cannot be combined yet.");
188 return ECMD_FAILED;
189 }
190 }
191
192 lv = lvl->lv;
193
194 if (lv->status & LOCKED) {
195 log_error("Can't resize locked LV %s", lv->name);
196 return ECMD_FAILED;
197 }
198
199 alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, lv->alloc);
200
201 if (lp->size) {
202 if (lp->size % vg->extent_size) {
203 if (lp->sign == SIGN_MINUS)
204 lp->size -= lp->size % vg->extent_size;
205 else
206 lp->size += vg->extent_size -
207 (lp->size % vg->extent_size);
208
209 log_print("Rounding up size to full physical extent %s",
210 display_size(cmd, (uint64_t) lp->size,
211 SIZE_SHORT));
212 }
213
214 lp->extents = lp->size / vg->extent_size;
215 }
216
217 if (lp->sign == SIGN_PLUS)
218 lp->extents += lv->le_count;
219
220 if (lp->sign == SIGN_MINUS) {
221 if (lp->extents >= lv->le_count) {
222 log_error("Unable to reduce %s below 1 extent",
223 lp->lv_name);
224 return EINVALID_CMD_LINE;
225 }
226
227 lp->extents = lv->le_count - lp->extents;
228 }
229
230 if (!lp->extents) {
231 log_error("New size of 0 not permitted");
232 return EINVALID_CMD_LINE;
233 }
234
235 if (lp->extents == lv->le_count) {
236 log_error("New size (%d extents) matches existing size "
237 "(%d extents)", lp->extents, lv->le_count);
238 return EINVALID_CMD_LINE;
239 }
240
241 seg_size = lp->extents - lv->le_count;
242
243 /* Use segment type of last segment */
244 list_iterate_items(seg, &lv->segments) {
245 lp->segtype = seg->segtype;
246 }
247
248 /* FIXME Support LVs with mixed segment types */
249 if (lp->segtype != (struct segment_type *) arg_ptr_value(cmd, type_ARG,
250 lp->segtype)) {
251 log_error("VolumeType does not match (%s)", lp->segtype->name);
252 return EINVALID_CMD_LINE;
253 }
254
255 /* If extending, find stripes, stripesize & size of last segment */
256 if ((lp->extents > lv->le_count) &&
257 !(lp->stripes == 1 || (lp->stripes > 1 && lp->stripe_size))) {
258 list_iterate_items(seg, &lv->segments) {
259 if (!seg_is_striped(seg))
260 continue;
261
262 sz = seg->stripe_size;
263 str = seg->area_count;
264
265 if ((seg_stripesize && seg_stripesize != sz
266 && !lp->stripe_size) ||
267 (seg_stripes && seg_stripes != str && !lp->stripes)) {
268 log_error("Please specify number of "
269 "stripes (-i) and stripesize (-I)");
270 return EINVALID_CMD_LINE;
271 }
272
273 seg_stripesize = sz;
274 seg_stripes = str;
275 }
276
277 if (!lp->stripes)
278 lp->stripes = seg_stripes;
279
280 if (!lp->stripe_size && lp->stripes > 1) {
281 if (seg_stripesize) {
282 log_print("Using stripesize of last segment "
283 "%dKB", seg_stripesize / 2);
284 lp->stripe_size = seg_stripesize;
285 } else {
286 lp->stripe_size =
287 find_config_int(cmd->cft->root,
288 "metadata/stripesize",
289 DEFAULT_STRIPESIZE) * 2;
290 log_print("Using default stripesize %dKB",
291 lp->stripe_size / 2);
292 }
293 }
294 }
295
296 /* If extending, find mirrors of last segment */
297 if ((lp->extents > lv->le_count)) {
298 list_iterate_back_items(seg, &lv->segments) {
299 if (seg_is_mirrored(seg))
300 seg_mirrors = seg->area_count;
301 else
302 seg_mirrors = 0;
303 break;
304 }
305 if (!arg_count(cmd, mirrors_ARG) && seg_mirrors) {
306 log_print("Extending %" PRIu32 " mirror images.",
307 seg_mirrors);
308 lp->mirrors = seg_mirrors;
309 }
310 if ((arg_count(cmd, mirrors_ARG) || seg_mirrors) &&
311 (lp->mirrors != seg_mirrors)) {
312 log_error("Cannot vary number of mirrors in LV yet.");
313 return EINVALID_CMD_LINE;
314 }
315 }
316
317 /* If reducing, find stripes, stripesize & size of last segment */
318 if (lp->extents < lv->le_count) {
319 extents_used = 0;
320
321 if (lp->stripes || lp->stripe_size || lp->mirrors)
322 log_error("Ignoring stripes, stripesize and mirrors "
323 "arguments when reducing");
324
325 list_iterate_items(seg, &lv->segments) {
326 seg_extents = seg->len;
327
328 if (seg_is_striped(seg)) {
329 seg_stripesize = seg->stripe_size;
330 seg_stripes = seg->area_count;
331 }
332
333 if (seg_is_mirrored(seg))
334 seg_mirrors = seg->area_count;
335 else
336 seg_mirrors = 0;
337
338 if (lp->extents <= extents_used + seg_extents)
339 break;
340
341 extents_used += seg_extents;
342 }
343
344 seg_size = lp->extents - extents_used;
345 lp->stripe_size = seg_stripesize;
346 lp->stripes = seg_stripes;
347 lp->mirrors = seg_mirrors;
348 }
349
350 if (lp->stripes > 1 && !lp->stripe_size) {
351 log_error("Stripesize for striped segment should not be 0!");
352 return EINVALID_CMD_LINE;
353 }
354
355 if ((lp->stripes > 1)) {
356 if (!(stripesize_extents = lp->stripe_size / vg->extent_size))
357 stripesize_extents = 1;
358
359 if ((size_rest = seg_size % (lp->stripes * stripesize_extents))) {
360 log_print("Rounding size (%d extents) down to stripe "
361 "boundary size for segment (%d extents)",
362 lp->extents, lp->extents - size_rest);
363 lp->extents = lp->extents - size_rest;
364 }
365 }
366
367 if (lp->extents == lv->le_count) {
368 log_error("New size (%d extents) matches existing size "
369 "(%d extents)", lp->extents, lv->le_count);
370 return EINVALID_CMD_LINE;
371 }
372
373 if (lp->extents < lv->le_count) {
374 if (lp->resize == LV_EXTEND) {
375 log_error("New size given (%d extents) not larger "
376 "than existing size (%d extents)",
377 lp->extents, lv->le_count);
378 return EINVALID_CMD_LINE;
379 } else
380 lp->resize = LV_REDUCE;
381 }
382
383 if (lp->extents > lv->le_count) {
384 if (lp->resize == LV_REDUCE) {
385 log_error("New size given (%d extents) not less than "
386 "existing size (%d extents)", lp->extents,
387 lv->le_count);
388 return EINVALID_CMD_LINE;
389 } else
390 lp->resize = LV_EXTEND;
391 }
392
393 if (lp->mirrors && activation() &&
394 lv_info(cmd, lv, &info, 0) && info.exists) {
395 log_error("Mirrors cannot be resized while active yet.");
396 return ECMD_FAILED;
397 }
398
399 if (lv_is_origin(lv)) {
400 if (lp->resize == LV_REDUCE) {
401 log_error("Snapshot origin volumes cannot be reduced "
402 "in size yet.");
403 return ECMD_FAILED;
404 }
405
406 memset(&info, 0, sizeof(info));
407
408 if (lv_info(cmd, lv, &info, 0) && info.exists) {
409 log_error("Snapshot origin volumes can be resized "
410 "only while inactive: try lvchange -an");
411 return ECMD_FAILED;
412 }
413 }
414
415 if (lp->resize == LV_REDUCE) {
416 if (lp->argc)
417 log_print("Ignoring PVs on command line when reducing");
418 } else if (!(pvh = lp->argc ? create_pv_list(cmd->mem, vg, lp->argc,
419 lp->argv, 1) : &vg->pvs)) {
420 stack;
421 return ECMD_FAILED;
422 }
423
424 if (lp->resize == LV_REDUCE || lp->resizefs) {
425 memset(&info, 0, sizeof(info));
426
427 if (!lv_info(cmd, lv, &info, 1) && driver_version(NULL, 0)) {
428 log_error("lv_info failed: aborting");
429 return ECMD_FAILED;
430 }
431
432 if (lp->resizefs && !info.exists) {
433 log_error("Logical volume %s must be activated "
434 "before resizing filesystem", lp->lv_name);
435 return ECMD_FAILED;
436 }
437
438 if (info.exists && !lp->resizefs && (lp->resize == LV_REDUCE)) {
439 log_print("WARNING: Reducing active%s logical volume "
440 "to %s", info.open_count ? " and open" : "",
441 display_size(cmd, (uint64_t) lp->extents *
442 vg->extent_size,
443 SIZE_SHORT));
444
445 log_print("THIS MAY DESTROY YOUR DATA "
446 "(filesystem etc.)");
447
448 if (!arg_count(cmd, force_ARG)) {
449 if (yes_no_prompt("Do you really want to "
450 "reduce %s? [y/n]: ",
451 lp->lv_name) == 'n') {
452 log_print("Logical volume %s NOT "
453 "reduced", lp->lv_name);
454 return ECMD_FAILED;
455 }
456 }
457 }
458 }
459
460 if (lp->resizefs) {
461 if (lvm_snprintf(lv_path, PATH_MAX, "%s%s/%s", cmd->dev_dir,
462 lp->vg_name, lp->lv_name) < 0) {
463 log_error("Couldn't create LV path for %s",
464 lp->lv_name);
465 return ECMD_FAILED;
466 }
467
468 if (lvm_snprintf(size_buf, SIZE_BUF, "%" PRIu64,
469 (uint64_t) lp->extents * vg->extent_size / 2)
470 < 0) {
471 log_error("Couldn't generate new LV size string");
472 return ECMD_FAILED;
473 }
474
475 if (!lp->nofsck) {
476 if (!exec_cmd("fsadm", "check", lv_path, NULL)) {
477 stack;
478 return ECMD_FAILED;
479 }
480 }
481
482 if (lp->resize == LV_REDUCE) {
483 if (!exec_cmd("fsadm", "resize", lv_path, size_buf)) {
484 stack;
485 return ECMD_FAILED;
486 }
487 }
488 }
489
490 if (!archive(vg)) {
491 stack;
492 return ECMD_FAILED;
493 }
494
495 log_print("%sing logical volume %s to %s",
496 (lp->resize == LV_REDUCE) ? "Reduc" : "Extend",
497 lp->lv_name,
498 display_size(cmd, (uint64_t) lp->extents * vg->extent_size,
499 SIZE_SHORT));
500
501 if (lp->resize == LV_REDUCE) {
502 if (!lv_reduce(lv, lv->le_count - lp->extents)) {
503 stack;
504 return ECMD_FAILED;
505 }
506 } else if (!lv_extend(lv, lp->segtype, lp->stripes,
507 lp->stripe_size, lp->mirrors,
508 lp->extents - lv->le_count,
509 NULL, 0u, 0u, pvh, alloc)) {
510 stack;
511 return ECMD_FAILED;
512 }
513
514 /* store vg on disk(s) */
515 if (!vg_write(vg)) {
516 stack;
517 return ECMD_FAILED;
518 }
519
520 backup(vg);
521
522 /* If snapshot, must suspend all associated devices */
523 if (lv_is_cow(lv))
524 lock_lv = origin_from_cow(lv);
525 else
526 lock_lv = lv;
527
528 if (!suspend_lv(cmd, lock_lv)) {
529 log_error("Failed to suspend %s", lp->lv_name);
530 vg_revert(vg);
531 return ECMD_FAILED;
532 }
533
534 if (!vg_commit(vg)) {
535 stack;
536 resume_lv(cmd, lock_lv);
537 return ECMD_FAILED;
538 }
539
540 if (!resume_lv(cmd, lock_lv)) {
541 log_error("Problem reactivating %s", lp->lv_name);
542 return ECMD_FAILED;
543 }
544
545 log_print("Logical volume %s successfully resized", lp->lv_name);
546
547 if (lp->resizefs && (lp->resize == LV_EXTEND)) {
548 if (!exec_cmd("fsadm", "resize", lv_path, size_buf)) {
549 stack;
550 return ECMD_FAILED;
551 }
552 }
553
554 return ECMD_PROCESSED;
555 }
556
557 int lvresize(struct cmd_context *cmd, int argc, char **argv)
558 {
559 struct lvresize_params lp;
560 int r;
561
562 memset(&lp, 0, sizeof(lp));
563
564 if (!_read_params(cmd, argc, argv, &lp))
565 return EINVALID_CMD_LINE;
566
567 log_verbose("Finding volume group %s", lp.vg_name);
568 if (!lock_vol(cmd, lp.vg_name, LCK_VG_WRITE)) {
569 log_error("Can't get lock for %s", lp.vg_name);
570 return ECMD_FAILED;
571 }
572
573 if (!(r = _lvresize(cmd, &lp)))
574 stack;
575
576 unlock_vg(cmd, lp.vg_name);
577
578 return r;
579 }
This page took 0.063674 seconds and 6 git commands to generate.