xref: /dragonfly/contrib/lvm2/dist/tools/pvresize.c (revision a8ca8ac6)
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