Home | History | Annotate | Line # | Download | only in tools
      1 /*	$NetBSD: pvresize.c,v 1.1.1.2 2009/12/02 00:25:54 haad Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
      5  * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
      6  * Copyright (C) 2005 Zak Kipling. All rights reserved.
      7  *
      8  * This file is part of LVM2.
      9  *
     10  * This copyrighted material is made available to anyone wishing to use,
     11  * modify, copy, or redistribute it subject to the terms and conditions
     12  * of the GNU Lesser General Public License v.2.1.
     13  *
     14  * You should have received a copy of the GNU Lesser General Public License
     15  * along with this program; if not, write to the Free Software Foundation,
     16  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     17  */
     18 
     19 #include "tools.h"
     20 
     21 struct pvresize_params {
     22 	uint64_t new_size;
     23 
     24 	unsigned done;
     25 	unsigned total;
     26 };
     27 
     28 static int _pv_resize_single(struct cmd_context *cmd,
     29 			     struct volume_group *vg,
     30 			     struct physical_volume *pv,
     31 			     const uint64_t new_size)
     32 {
     33 	struct pv_list *pvl;
     34 	uint64_t size = 0;
     35 	uint32_t new_pe_count = 0;
     36 	int r = 0;
     37 	struct dm_list mdas;
     38 	const char *pv_name = pv_dev_name(pv);
     39 	const char *vg_name;
     40 	struct lvmcache_info *info;
     41 	int mda_count = 0;
     42 	struct volume_group *old_vg = vg;
     43 
     44 	dm_list_init(&mdas);
     45 
     46 	if (is_orphan_vg(pv_vg_name(pv))) {
     47 		vg_name = VG_ORPHANS;
     48 		if (!lock_vol(cmd, vg_name, LCK_VG_WRITE)) {
     49 			log_error("Can't get lock for orphans");
     50 			return 0;
     51 		}
     52 
     53 		if (!(pv = pv_read(cmd, pv_name, &mdas, NULL, 1, 0))) {
     54 			unlock_vg(cmd, vg_name);
     55 			log_error("Unable to read PV \"%s\"", pv_name);
     56 			return 0;
     57 		}
     58 
     59 		mda_count = dm_list_size(&mdas);
     60 	} else {
     61 		vg_name = pv_vg_name(pv);
     62 
     63 		vg = vg_read_for_update(cmd, vg_name, NULL, 0);
     64 
     65 		if (vg_read_error(vg))
     66 			goto bad;
     67 
     68 		if (!(pvl = find_pv_in_vg(vg, pv_name))) {
     69 			log_error("Unable to find \"%s\" in volume group \"%s\"",
     70 				  pv_name, vg->name);
     71 			goto bad;
     72 		}
     73 
     74 		pv = pvl->pv;
     75 
     76 		if (!(info = info_from_pvid(pv->dev->pvid, 0))) {
     77 			log_error("Can't get info for PV %s in volume group %s",
     78 				  pv_name, vg->name);
     79 			goto bad;
     80 		}
     81 
     82 		mda_count = dm_list_size(&info->mdas);
     83 
     84 		if (!archive(vg))
     85 			goto bad;
     86 	}
     87 
     88 	/* FIXME Create function to test compatibility properly */
     89 	if (mda_count > 1) {
     90 		log_error("%s: too many metadata areas for pvresize", pv_name);
     91 		goto bad;
     92 	}
     93 
     94 	if (!(pv->fmt->features & FMT_RESIZE_PV)) {
     95 		log_error("Physical volume %s format does not support resizing.",
     96 			  pv_name);
     97 		goto bad;
     98 	}
     99 
    100 	/* Get new size */
    101 	if (!dev_get_size(pv_dev(pv), &size)) {
    102 		log_error("%s: Couldn't get size.", pv_name);
    103 		goto bad;
    104 	}
    105 
    106 	if (new_size) {
    107 		if (new_size > size)
    108 			log_warn("WARNING: %s: Overriding real size. "
    109 				  "You could lose data.", pv_name);
    110 		log_verbose("%s: Pretending size is %" PRIu64 " not %" PRIu64
    111 			    " sectors.", pv_name, new_size, pv_size(pv));
    112 		size = new_size;
    113 	}
    114 
    115 	if (size < PV_MIN_SIZE) {
    116 		log_error("%s: Size must exceed minimum of %ld sectors.",
    117 			  pv_name, PV_MIN_SIZE);
    118 		goto bad;
    119 	}
    120 
    121 	if (size < pv_pe_start(pv)) {
    122 		log_error("%s: Size must exceed physical extent start of "
    123 			  "%" PRIu64 " sectors.", pv_name, pv_pe_start(pv));
    124 		goto bad;
    125 	}
    126 
    127 	pv->size = size;
    128 
    129 	if (vg) {
    130 		pv->size -= pv_pe_start(pv);
    131 		new_pe_count = pv_size(pv) / vg->extent_size;
    132 
    133  		if (!new_pe_count) {
    134 			log_error("%s: Size must leave space for at "
    135 				  "least one physical extent of "
    136 				  "%" PRIu32 " sectors.", pv_name,
    137 				  pv_pe_size(pv));
    138 			goto bad;
    139 		}
    140 
    141 		if (!pv_resize(pv, vg, new_pe_count))
    142 			goto_bad;
    143 	}
    144 
    145 	log_verbose("Resizing volume \"%s\" to %" PRIu64 " sectors.",
    146 		    pv_name, pv_size(pv));
    147 
    148 	log_verbose("Updating physical volume \"%s\"", pv_name);
    149 	if (!is_orphan_vg(pv_vg_name(pv))) {
    150 		if (!vg_write(vg) || !vg_commit(vg)) {
    151 			log_error("Failed to store physical volume \"%s\" in "
    152 				  "volume group \"%s\"", pv_name, vg->name);
    153 			goto bad;
    154 		}
    155 		backup(vg);
    156 	} else if (!(pv_write(cmd, pv, NULL, INT64_C(-1)))) {
    157 		log_error("Failed to store physical volume \"%s\"",
    158 			  pv_name);
    159 		goto bad;;
    160 	}
    161 
    162 	log_print("Physical volume \"%s\" changed", pv_name);
    163 	r = 1;
    164 
    165 bad:
    166 	unlock_vg(cmd, vg_name);
    167 	if (!old_vg)
    168 		vg_release(vg);
    169 	return r;
    170 }
    171 
    172 static int _pvresize_single(struct cmd_context *cmd,
    173 			    struct volume_group *vg,
    174 			    struct physical_volume *pv,
    175 			    void *handle)
    176 {
    177 	struct pvresize_params *params = (struct pvresize_params *) handle;
    178 
    179 	params->total++;
    180 
    181 	if (!_pv_resize_single(cmd, vg, pv, params->new_size)) {
    182 		stack;
    183 		return ECMD_FAILED;
    184 	}
    185 
    186 	params->done++;
    187 
    188 	return ECMD_PROCESSED;
    189 }
    190 
    191 int pvresize(struct cmd_context *cmd, int argc, char **argv)
    192 {
    193 	struct pvresize_params params;
    194 	int ret;
    195 
    196 	if (!argc) {
    197 		log_error("Please supply physical volume(s)");
    198 		return EINVALID_CMD_LINE;
    199 	}
    200 
    201 	if (arg_sign_value(cmd, physicalvolumesize_ARG, 0) == SIGN_MINUS) {
    202 		log_error("Physical volume size may not be negative");
    203 		return 0;
    204 	}
    205 
    206 	params.new_size = arg_uint64_value(cmd, physicalvolumesize_ARG,
    207 					   UINT64_C(0));
    208 
    209 	params.done = 0;
    210 	params.total = 0;
    211 
    212 	ret = process_each_pv(cmd, argc, argv, NULL, READ_FOR_UPDATE, 0, &params,
    213 			      _pvresize_single);
    214 
    215 	log_print("%d physical volume(s) resized / %d physical volume(s) "
    216 		  "not resized", params.done, params.total - params.done);
    217 
    218 	return ret;
    219 }
    220