1 |
/*- |
2 |
* Copyright (c) 2004-2009 Pawel Jakub Dawidek <pjd@FreeBSD.org> |
3 |
* All rights reserved. |
4 |
* |
5 |
* Redistribution and use in source and binary forms, with or without |
6 |
* modification, are permitted provided that the following conditions |
7 |
* are met: |
8 |
* 1. Redistributions of source code must retain the above copyright |
9 |
* notice, this list of conditions and the following disclaimer. |
10 |
* 2. Redistributions in binary form must reproduce the above copyright |
11 |
* notice, this list of conditions and the following disclaimer in the |
12 |
* documentation and/or other materials provided with the distribution. |
13 |
* |
14 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND |
15 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
17 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE |
18 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
19 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
20 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
21 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
22 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
23 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
24 |
* SUCH DAMAGE. |
25 |
*/ |
26 |
|
27 |
#include <sys/cdefs.h> |
28 |
__FBSDID("$FreeBSD: src/sys/geom/mirror/g_mirror_ctl.c,v 1.18.2.1 2011/09/29 18:42:44 mav Exp $"); |
29 |
|
30 |
#include <sys/param.h> |
31 |
#include <sys/systm.h> |
32 |
#include <sys/kernel.h> |
33 |
#include <sys/module.h> |
34 |
#include <sys/lock.h> |
35 |
#include <sys/mutex.h> |
36 |
#include <sys/bio.h> |
37 |
#include <sys/sysctl.h> |
38 |
#include <sys/malloc.h> |
39 |
#include <sys/bitstring.h> |
40 |
#include <vm/uma.h> |
41 |
#include <machine/atomic.h> |
42 |
#include <geom/geom.h> |
43 |
#include <sys/proc.h> |
44 |
#include <sys/kthread.h> |
45 |
#include <geom/mirror/g_mirror.h> |
46 |
|
47 |
|
48 |
static struct g_mirror_softc * |
49 |
g_mirror_find_device(struct g_class *mp, const char *name) |
50 |
{ |
51 |
struct g_mirror_softc *sc; |
52 |
struct g_geom *gp; |
53 |
|
54 |
g_topology_lock(); |
55 |
LIST_FOREACH(gp, &mp->geom, geom) { |
56 |
sc = gp->softc; |
57 |
if (sc == NULL) |
58 |
continue; |
59 |
if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) |
60 |
continue; |
61 |
if (strcmp(gp->name, name) == 0 || |
62 |
strcmp(sc->sc_name, name) == 0) { |
63 |
g_topology_unlock(); |
64 |
sx_xlock(&sc->sc_lock); |
65 |
return (sc); |
66 |
} |
67 |
} |
68 |
g_topology_unlock(); |
69 |
return (NULL); |
70 |
} |
71 |
|
72 |
static struct g_mirror_disk * |
73 |
g_mirror_find_disk(struct g_mirror_softc *sc, const char *name) |
74 |
{ |
75 |
struct g_mirror_disk *disk; |
76 |
|
77 |
sx_assert(&sc->sc_lock, SX_XLOCKED); |
78 |
if (strncmp(name, "/dev/", 5) == 0) |
79 |
name += 5; |
80 |
LIST_FOREACH(disk, &sc->sc_disks, d_next) { |
81 |
if (disk->d_consumer == NULL) |
82 |
continue; |
83 |
if (disk->d_consumer->provider == NULL) |
84 |
continue; |
85 |
if (strcmp(disk->d_consumer->provider->name, name) == 0) |
86 |
return (disk); |
87 |
} |
88 |
return (NULL); |
89 |
} |
90 |
|
91 |
static void |
92 |
g_mirror_ctl_configure(struct gctl_req *req, struct g_class *mp) |
93 |
{ |
94 |
struct g_mirror_softc *sc; |
95 |
struct g_mirror_disk *disk; |
96 |
const char *name, *balancep, *prov; |
97 |
intmax_t *slicep, *priority; |
98 |
uint32_t slice; |
99 |
uint8_t balance; |
100 |
int *autosync, *noautosync, *failsync, *nofailsync, *hardcode, *dynamic; |
101 |
int *nargs, do_sync = 0, dirty = 1, do_priority = 0; |
102 |
|
103 |
nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); |
104 |
if (nargs == NULL) { |
105 |
gctl_error(req, "No '%s' argument.", "nargs"); |
106 |
return; |
107 |
} |
108 |
if (*nargs != 1 && *nargs != 2) { |
109 |
gctl_error(req, "Invalid number of arguments."); |
110 |
return; |
111 |
} |
112 |
name = gctl_get_asciiparam(req, "arg0"); |
113 |
if (name == NULL) { |
114 |
gctl_error(req, "No 'arg%u' argument.", 0); |
115 |
return; |
116 |
} |
117 |
balancep = gctl_get_asciiparam(req, "balance"); |
118 |
if (balancep == NULL) { |
119 |
gctl_error(req, "No '%s' argument.", "balance"); |
120 |
return; |
121 |
} |
122 |
autosync = gctl_get_paraml(req, "autosync", sizeof(*autosync)); |
123 |
if (autosync == NULL) { |
124 |
gctl_error(req, "No '%s' argument.", "autosync"); |
125 |
return; |
126 |
} |
127 |
noautosync = gctl_get_paraml(req, "noautosync", sizeof(*noautosync)); |
128 |
if (noautosync == NULL) { |
129 |
gctl_error(req, "No '%s' argument.", "noautosync"); |
130 |
return; |
131 |
} |
132 |
failsync = gctl_get_paraml(req, "failsync", sizeof(*failsync)); |
133 |
if (failsync == NULL) { |
134 |
gctl_error(req, "No '%s' argument.", "failsync"); |
135 |
return; |
136 |
} |
137 |
nofailsync = gctl_get_paraml(req, "nofailsync", sizeof(*nofailsync)); |
138 |
if (nofailsync == NULL) { |
139 |
gctl_error(req, "No '%s' argument.", "nofailsync"); |
140 |
return; |
141 |
} |
142 |
hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode)); |
143 |
if (hardcode == NULL) { |
144 |
gctl_error(req, "No '%s' argument.", "hardcode"); |
145 |
return; |
146 |
} |
147 |
dynamic = gctl_get_paraml(req, "dynamic", sizeof(*dynamic)); |
148 |
if (dynamic == NULL) { |
149 |
gctl_error(req, "No '%s' argument.", "dynamic"); |
150 |
return; |
151 |
} |
152 |
priority = gctl_get_paraml(req, "priority", sizeof(*priority)); |
153 |
if (priority == NULL) { |
154 |
gctl_error(req, "No '%s' argument.", "priority"); |
155 |
return; |
156 |
} |
157 |
if (*priority < -1 || *priority > 255) { |
158 |
gctl_error(req, "Priority range is 0 to 255, %jd given", |
159 |
*priority); |
160 |
return; |
161 |
} |
162 |
/* |
163 |
* Since we have a priority, we also need a provider now. |
164 |
* Note: be WARNS safe, by always assigning prov and only throw an |
165 |
* error if *priority != -1. |
166 |
*/ |
167 |
prov = gctl_get_asciiparam(req, "arg1"); |
168 |
if (*priority > -1) { |
169 |
if (prov == NULL) { |
170 |
gctl_error(req, "Priority needs a disk name"); |
171 |
return; |
172 |
} |
173 |
do_priority = 1; |
174 |
} |
175 |
if (*autosync && *noautosync) { |
176 |
gctl_error(req, "'%s' and '%s' specified.", "autosync", |
177 |
"noautosync"); |
178 |
return; |
179 |
} |
180 |
if (*failsync && *nofailsync) { |
181 |
gctl_error(req, "'%s' and '%s' specified.", "failsync", |
182 |
"nofailsync"); |
183 |
return; |
184 |
} |
185 |
if (*hardcode && *dynamic) { |
186 |
gctl_error(req, "'%s' and '%s' specified.", "hardcode", |
187 |
"dynamic"); |
188 |
return; |
189 |
} |
190 |
sc = g_mirror_find_device(mp, name); |
191 |
if (sc == NULL) { |
192 |
gctl_error(req, "No such device: %s.", name); |
193 |
return; |
194 |
} |
195 |
if (*balancep == '\0') |
196 |
balance = sc->sc_balance; |
197 |
else { |
198 |
if (balance_id(balancep) == -1) { |
199 |
gctl_error(req, "Invalid balance algorithm."); |
200 |
sx_xunlock(&sc->sc_lock); |
201 |
return; |
202 |
} |
203 |
balance = balance_id(balancep); |
204 |
} |
205 |
slicep = gctl_get_paraml(req, "slice", sizeof(*slicep)); |
206 |
if (slicep == NULL) { |
207 |
gctl_error(req, "No '%s' argument.", "slice"); |
208 |
sx_xunlock(&sc->sc_lock); |
209 |
return; |
210 |
} |
211 |
if (*slicep == -1) |
212 |
slice = sc->sc_slice; |
213 |
else |
214 |
slice = *slicep; |
215 |
/* Enforce usage() of -p not allowing any other options. */ |
216 |
if (do_priority && (*autosync || *noautosync || *failsync || |
217 |
*nofailsync || *hardcode || *dynamic || *slicep != -1 || |
218 |
*balancep != '\0')) { |
219 |
sx_xunlock(&sc->sc_lock); |
220 |
gctl_error(req, "only -p accepted when setting priority"); |
221 |
return; |
222 |
} |
223 |
if (sc->sc_balance == balance && sc->sc_slice == slice && !*autosync && |
224 |
!*noautosync && !*failsync && !*nofailsync && !*hardcode && |
225 |
!*dynamic && !do_priority) { |
226 |
sx_xunlock(&sc->sc_lock); |
227 |
gctl_error(req, "Nothing has changed."); |
228 |
return; |
229 |
} |
230 |
if ((!do_priority && *nargs != 1) || (do_priority && *nargs != 2)) { |
231 |
sx_xunlock(&sc->sc_lock); |
232 |
gctl_error(req, "Invalid number of arguments."); |
233 |
return; |
234 |
} |
235 |
if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) { |
236 |
sx_xunlock(&sc->sc_lock); |
237 |
gctl_error(req, "Not all disks connected. Try 'forget' command " |
238 |
"first."); |
239 |
return; |
240 |
} |
241 |
sc->sc_balance = balance; |
242 |
sc->sc_slice = slice; |
243 |
if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0) { |
244 |
if (*autosync) { |
245 |
sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOAUTOSYNC; |
246 |
do_sync = 1; |
247 |
} |
248 |
} else { |
249 |
if (*noautosync) |
250 |
sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC; |
251 |
} |
252 |
if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOFAILSYNC) != 0) { |
253 |
if (*failsync) |
254 |
sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOFAILSYNC; |
255 |
} else { |
256 |
if (*nofailsync) { |
257 |
sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOFAILSYNC; |
258 |
dirty = 0; |
259 |
} |
260 |
} |
261 |
LIST_FOREACH(disk, &sc->sc_disks, d_next) { |
262 |
/* |
263 |
* Handle priority first, since we only need one disk, do one |
264 |
* operation on it and then we're done. No need to check other |
265 |
* flags, as usage doesn't allow it. |
266 |
*/ |
267 |
if (do_priority) { |
268 |
if (strcmp(disk->d_name, prov) == 0) { |
269 |
if (disk->d_priority == *priority) |
270 |
gctl_error(req, "Nothing has changed."); |
271 |
else { |
272 |
disk->d_priority = *priority; |
273 |
g_mirror_update_metadata(disk); |
274 |
} |
275 |
break; |
276 |
} |
277 |
continue; |
278 |
} |
279 |
if (do_sync) { |
280 |
if (disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING) |
281 |
disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC; |
282 |
} |
283 |
if (*hardcode) |
284 |
disk->d_flags |= G_MIRROR_DISK_FLAG_HARDCODED; |
285 |
else if (*dynamic) |
286 |
disk->d_flags &= ~G_MIRROR_DISK_FLAG_HARDCODED; |
287 |
if (!dirty) |
288 |
disk->d_flags &= ~G_MIRROR_DISK_FLAG_DIRTY; |
289 |
g_mirror_update_metadata(disk); |
290 |
if (do_sync) { |
291 |
if (disk->d_state == G_MIRROR_DISK_STATE_STALE) { |
292 |
g_mirror_event_send(disk, |
293 |
G_MIRROR_DISK_STATE_DISCONNECTED, |
294 |
G_MIRROR_EVENT_DONTWAIT); |
295 |
} |
296 |
} |
297 |
} |
298 |
sx_xunlock(&sc->sc_lock); |
299 |
} |
300 |
|
301 |
static void |
302 |
g_mirror_ctl_rebuild(struct gctl_req *req, struct g_class *mp) |
303 |
{ |
304 |
struct g_mirror_metadata md; |
305 |
struct g_mirror_softc *sc; |
306 |
struct g_mirror_disk *disk; |
307 |
struct g_provider *pp; |
308 |
const char *name; |
309 |
char param[16]; |
310 |
int error, *nargs; |
311 |
u_int i; |
312 |
|
313 |
nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); |
314 |
if (nargs == NULL) { |
315 |
gctl_error(req, "No '%s' argument.", "nargs"); |
316 |
return; |
317 |
} |
318 |
if (*nargs < 2) { |
319 |
gctl_error(req, "Too few arguments."); |
320 |
return; |
321 |
} |
322 |
name = gctl_get_asciiparam(req, "arg0"); |
323 |
if (name == NULL) { |
324 |
gctl_error(req, "No 'arg%u' argument.", 0); |
325 |
return; |
326 |
} |
327 |
sc = g_mirror_find_device(mp, name); |
328 |
if (sc == NULL) { |
329 |
gctl_error(req, "No such device: %s.", name); |
330 |
return; |
331 |
} |
332 |
for (i = 1; i < (u_int)*nargs; i++) { |
333 |
snprintf(param, sizeof(param), "arg%u", i); |
334 |
name = gctl_get_asciiparam(req, param); |
335 |
if (name == NULL) { |
336 |
gctl_error(req, "No 'arg%u' argument.", i); |
337 |
continue; |
338 |
} |
339 |
disk = g_mirror_find_disk(sc, name); |
340 |
if (disk == NULL) { |
341 |
gctl_error(req, "No such provider: %s.", name); |
342 |
continue; |
343 |
} |
344 |
if (g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) == 1 && |
345 |
disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) { |
346 |
/* |
347 |
* This is the last active disk. There will be nothing |
348 |
* to rebuild it from, so deny this request. |
349 |
*/ |
350 |
gctl_error(req, |
351 |
"Provider %s is the last active provider in %s.", |
352 |
name, sc->sc_geom->name); |
353 |
break; |
354 |
} |
355 |
/* |
356 |
* Do rebuild by resetting syncid, disconnecting the disk and |
357 |
* connecting it again. |
358 |
*/ |
359 |
disk->d_sync.ds_syncid = 0; |
360 |
if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0) |
361 |
disk->d_flags |= G_MIRROR_DISK_FLAG_FORCE_SYNC; |
362 |
g_mirror_update_metadata(disk); |
363 |
pp = disk->d_consumer->provider; |
364 |
g_topology_lock(); |
365 |
error = g_mirror_read_metadata(disk->d_consumer, &md); |
366 |
g_topology_unlock(); |
367 |
g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED, |
368 |
G_MIRROR_EVENT_WAIT); |
369 |
if (error != 0) { |
370 |
gctl_error(req, "Cannot read metadata from %s.", |
371 |
pp->name); |
372 |
continue; |
373 |
} |
374 |
error = g_mirror_add_disk(sc, pp, &md); |
375 |
if (error != 0) { |
376 |
gctl_error(req, "Cannot reconnect component %s.", |
377 |
pp->name); |
378 |
continue; |
379 |
} |
380 |
} |
381 |
sx_xunlock(&sc->sc_lock); |
382 |
} |
383 |
|
384 |
static void |
385 |
g_mirror_ctl_insert(struct gctl_req *req, struct g_class *mp) |
386 |
{ |
387 |
struct g_mirror_softc *sc; |
388 |
struct g_mirror_disk *disk; |
389 |
struct g_mirror_metadata md; |
390 |
struct g_provider *pp; |
391 |
struct g_consumer *cp; |
392 |
intmax_t *priority; |
393 |
const char *name; |
394 |
char param[16]; |
395 |
u_char *sector; |
396 |
u_int i, n; |
397 |
int error, *nargs, *hardcode, *inactive; |
398 |
struct { |
399 |
struct g_provider *provider; |
400 |
struct g_consumer *consumer; |
401 |
} *disks; |
402 |
|
403 |
nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); |
404 |
if (nargs == NULL) { |
405 |
gctl_error(req, "No '%s' argument.", "nargs"); |
406 |
return; |
407 |
} |
408 |
if (*nargs < 2) { |
409 |
gctl_error(req, "Too few arguments."); |
410 |
return; |
411 |
} |
412 |
priority = gctl_get_paraml(req, "priority", sizeof(*priority)); |
413 |
if (priority == NULL) { |
414 |
gctl_error(req, "No '%s' argument.", "priority"); |
415 |
return; |
416 |
} |
417 |
inactive = gctl_get_paraml(req, "inactive", sizeof(*inactive)); |
418 |
if (inactive == NULL) { |
419 |
gctl_error(req, "No '%s' argument.", "inactive"); |
420 |
return; |
421 |
} |
422 |
hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode)); |
423 |
if (hardcode == NULL) { |
424 |
gctl_error(req, "No '%s' argument.", "hardcode"); |
425 |
return; |
426 |
} |
427 |
name = gctl_get_asciiparam(req, "arg0"); |
428 |
if (name == NULL) { |
429 |
gctl_error(req, "No 'arg%u' argument.", 0); |
430 |
return; |
431 |
} |
432 |
sc = g_mirror_find_device(mp, name); |
433 |
if (sc == NULL) { |
434 |
gctl_error(req, "No such device: %s.", name); |
435 |
return; |
436 |
} |
437 |
if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) { |
438 |
gctl_error(req, "Not all disks connected."); |
439 |
sx_xunlock(&sc->sc_lock); |
440 |
return; |
441 |
} |
442 |
|
443 |
disks = g_malloc(sizeof(*disks) * (*nargs), M_WAITOK | M_ZERO); |
444 |
g_topology_lock(); |
445 |
for (i = 1, n = 0; i < (u_int)*nargs; i++) { |
446 |
snprintf(param, sizeof(param), "arg%u", i); |
447 |
name = gctl_get_asciiparam(req, param); |
448 |
if (name == NULL) { |
449 |
gctl_error(req, "No 'arg%u' argument.", i); |
450 |
continue; |
451 |
} |
452 |
if (g_mirror_find_disk(sc, name) != NULL) { |
453 |
gctl_error(req, "Provider %s already inserted.", name); |
454 |
continue; |
455 |
} |
456 |
if (strncmp(name, "/dev/", 5) == 0) |
457 |
name += 5; |
458 |
pp = g_provider_by_name(name); |
459 |
if (pp == NULL) { |
460 |
gctl_error(req, "Unknown provider %s.", name); |
461 |
continue; |
462 |
} |
463 |
if (sc->sc_provider->mediasize > |
464 |
pp->mediasize - pp->sectorsize) { |
465 |
gctl_error(req, "Provider %s too small.", name); |
466 |
continue; |
467 |
} |
468 |
if ((sc->sc_provider->sectorsize % pp->sectorsize) != 0) { |
469 |
gctl_error(req, "Invalid sectorsize of provider %s.", |
470 |
name); |
471 |
continue; |
472 |
} |
473 |
cp = g_new_consumer(sc->sc_geom); |
474 |
if (g_attach(cp, pp) != 0) { |
475 |
g_destroy_consumer(cp); |
476 |
gctl_error(req, "Cannot attach to provider %s.", name); |
477 |
continue; |
478 |
} |
479 |
if (g_access(cp, 0, 1, 1) != 0) { |
480 |
g_detach(cp); |
481 |
g_destroy_consumer(cp); |
482 |
gctl_error(req, "Cannot access provider %s.", name); |
483 |
continue; |
484 |
} |
485 |
disks[n].provider = pp; |
486 |
disks[n].consumer = cp; |
487 |
n++; |
488 |
} |
489 |
if (n == 0) { |
490 |
g_topology_unlock(); |
491 |
sx_xunlock(&sc->sc_lock); |
492 |
g_free(disks); |
493 |
return; |
494 |
} |
495 |
sc->sc_ndisks += n; |
496 |
again: |
497 |
for (i = 0; i < n; i++) { |
498 |
if (disks[i].consumer == NULL) |
499 |
continue; |
500 |
g_mirror_fill_metadata(sc, NULL, &md); |
501 |
md.md_priority = *priority; |
502 |
if (*inactive) |
503 |
md.md_dflags |= G_MIRROR_DISK_FLAG_INACTIVE; |
504 |
pp = disks[i].provider; |
505 |
if (*hardcode) { |
506 |
strlcpy(md.md_provider, pp->name, |
507 |
sizeof(md.md_provider)); |
508 |
} else { |
509 |
bzero(md.md_provider, sizeof(md.md_provider)); |
510 |
} |
511 |
md.md_provsize = pp->mediasize; |
512 |
sector = g_malloc(pp->sectorsize, M_WAITOK); |
513 |
mirror_metadata_encode(&md, sector); |
514 |
error = g_write_data(disks[i].consumer, |
515 |
pp->mediasize - pp->sectorsize, sector, pp->sectorsize); |
516 |
g_free(sector); |
517 |
if (error != 0) { |
518 |
gctl_error(req, "Cannot store metadata on %s.", |
519 |
pp->name); |
520 |
g_access(disks[i].consumer, 0, -1, -1); |
521 |
g_detach(disks[i].consumer); |
522 |
g_destroy_consumer(disks[i].consumer); |
523 |
disks[i].consumer = NULL; |
524 |
disks[i].provider = NULL; |
525 |
sc->sc_ndisks--; |
526 |
goto again; |
527 |
} |
528 |
} |
529 |
g_topology_unlock(); |
530 |
if (i == 0) { |
531 |
/* All writes failed. */ |
532 |
sx_xunlock(&sc->sc_lock); |
533 |
g_free(disks); |
534 |
return; |
535 |
} |
536 |
LIST_FOREACH(disk, &sc->sc_disks, d_next) { |
537 |
g_mirror_update_metadata(disk); |
538 |
} |
539 |
/* |
540 |
* Release provider and wait for retaste. |
541 |
*/ |
542 |
g_topology_lock(); |
543 |
for (i = 0; i < n; i++) { |
544 |
if (disks[i].consumer == NULL) |
545 |
continue; |
546 |
g_access(disks[i].consumer, 0, -1, -1); |
547 |
g_detach(disks[i].consumer); |
548 |
g_destroy_consumer(disks[i].consumer); |
549 |
} |
550 |
g_topology_unlock(); |
551 |
sx_xunlock(&sc->sc_lock); |
552 |
g_free(disks); |
553 |
} |
554 |
|
555 |
static void |
556 |
g_mirror_ctl_remove(struct gctl_req *req, struct g_class *mp) |
557 |
{ |
558 |
struct g_mirror_softc *sc; |
559 |
struct g_mirror_disk *disk; |
560 |
const char *name; |
561 |
char param[16]; |
562 |
int *nargs; |
563 |
u_int i, active; |
564 |
|
565 |
nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); |
566 |
if (nargs == NULL) { |
567 |
gctl_error(req, "No '%s' argument.", "nargs"); |
568 |
return; |
569 |
} |
570 |
if (*nargs < 2) { |
571 |
gctl_error(req, "Too few arguments."); |
572 |
return; |
573 |
} |
574 |
name = gctl_get_asciiparam(req, "arg0"); |
575 |
if (name == NULL) { |
576 |
gctl_error(req, "No 'arg%u' argument.", 0); |
577 |
return; |
578 |
} |
579 |
sc = g_mirror_find_device(mp, name); |
580 |
if (sc == NULL) { |
581 |
gctl_error(req, "No such device: %s.", name); |
582 |
return; |
583 |
} |
584 |
if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) { |
585 |
sx_xunlock(&sc->sc_lock); |
586 |
gctl_error(req, "Not all disks connected. Try 'forget' command " |
587 |
"first."); |
588 |
return; |
589 |
} |
590 |
active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE); |
591 |
for (i = 1; i < (u_int)*nargs; i++) { |
592 |
snprintf(param, sizeof(param), "arg%u", i); |
593 |
name = gctl_get_asciiparam(req, param); |
594 |
if (name == NULL) { |
595 |
gctl_error(req, "No 'arg%u' argument.", i); |
596 |
continue; |
597 |
} |
598 |
disk = g_mirror_find_disk(sc, name); |
599 |
if (disk == NULL) { |
600 |
gctl_error(req, "No such provider: %s.", name); |
601 |
continue; |
602 |
} |
603 |
if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) { |
604 |
if (active > 1) |
605 |
active--; |
606 |
else { |
607 |
gctl_error(req, "%s: Can't remove the last " |
608 |
"ACTIVE component %s.", sc->sc_geom->name, |
609 |
name); |
610 |
continue; |
611 |
} |
612 |
} |
613 |
g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DESTROY, |
614 |
G_MIRROR_EVENT_DONTWAIT); |
615 |
} |
616 |
sx_xunlock(&sc->sc_lock); |
617 |
} |
618 |
|
619 |
static void |
620 |
g_mirror_ctl_deactivate(struct gctl_req *req, struct g_class *mp) |
621 |
{ |
622 |
struct g_mirror_softc *sc; |
623 |
struct g_mirror_disk *disk; |
624 |
const char *name; |
625 |
char param[16]; |
626 |
int *nargs; |
627 |
u_int i; |
628 |
|
629 |
nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); |
630 |
if (nargs == NULL) { |
631 |
gctl_error(req, "No '%s' argument.", "nargs"); |
632 |
return; |
633 |
} |
634 |
if (*nargs < 2) { |
635 |
gctl_error(req, "Too few arguments."); |
636 |
return; |
637 |
} |
638 |
name = gctl_get_asciiparam(req, "arg0"); |
639 |
if (name == NULL) { |
640 |
gctl_error(req, "No 'arg%u' argument.", 0); |
641 |
return; |
642 |
} |
643 |
sc = g_mirror_find_device(mp, name); |
644 |
if (sc == NULL) { |
645 |
gctl_error(req, "No such device: %s.", name); |
646 |
return; |
647 |
} |
648 |
for (i = 1; i < (u_int)*nargs; i++) { |
649 |
snprintf(param, sizeof(param), "arg%u", i); |
650 |
name = gctl_get_asciiparam(req, param); |
651 |
if (name == NULL) { |
652 |
gctl_error(req, "No 'arg%u' argument.", i); |
653 |
continue; |
654 |
} |
655 |
disk = g_mirror_find_disk(sc, name); |
656 |
if (disk == NULL) { |
657 |
gctl_error(req, "No such provider: %s.", name); |
658 |
continue; |
659 |
} |
660 |
disk->d_flags |= G_MIRROR_DISK_FLAG_INACTIVE; |
661 |
disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC; |
662 |
g_mirror_update_metadata(disk); |
663 |
sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID; |
664 |
g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED, |
665 |
G_MIRROR_EVENT_DONTWAIT); |
666 |
} |
667 |
sx_xunlock(&sc->sc_lock); |
668 |
} |
669 |
|
670 |
static void |
671 |
g_mirror_ctl_forget(struct gctl_req *req, struct g_class *mp) |
672 |
{ |
673 |
struct g_mirror_softc *sc; |
674 |
struct g_mirror_disk *disk; |
675 |
const char *name; |
676 |
char param[16]; |
677 |
int *nargs; |
678 |
u_int i; |
679 |
|
680 |
nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); |
681 |
if (nargs == NULL) { |
682 |
gctl_error(req, "No '%s' argument.", "nargs"); |
683 |
return; |
684 |
} |
685 |
if (*nargs < 1) { |
686 |
gctl_error(req, "Missing device(s)."); |
687 |
return; |
688 |
} |
689 |
|
690 |
for (i = 0; i < (u_int)*nargs; i++) { |
691 |
snprintf(param, sizeof(param), "arg%u", i); |
692 |
name = gctl_get_asciiparam(req, param); |
693 |
if (name == NULL) { |
694 |
gctl_error(req, "No 'arg%u' argument.", i); |
695 |
return; |
696 |
} |
697 |
sc = g_mirror_find_device(mp, name); |
698 |
if (sc == NULL) { |
699 |
gctl_error(req, "No such device: %s.", name); |
700 |
return; |
701 |
} |
702 |
if (g_mirror_ndisks(sc, -1) == sc->sc_ndisks) { |
703 |
sx_xunlock(&sc->sc_lock); |
704 |
G_MIRROR_DEBUG(1, |
705 |
"All disks connected in %s, skipping.", |
706 |
sc->sc_name); |
707 |
continue; |
708 |
} |
709 |
sc->sc_ndisks = g_mirror_ndisks(sc, -1); |
710 |
LIST_FOREACH(disk, &sc->sc_disks, d_next) { |
711 |
g_mirror_update_metadata(disk); |
712 |
} |
713 |
sx_xunlock(&sc->sc_lock); |
714 |
} |
715 |
} |
716 |
|
717 |
static void |
718 |
g_mirror_ctl_stop(struct gctl_req *req, struct g_class *mp) |
719 |
{ |
720 |
struct g_mirror_softc *sc; |
721 |
int *force, *nargs, error; |
722 |
const char *name; |
723 |
char param[16]; |
724 |
u_int i; |
725 |
int how; |
726 |
|
727 |
nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); |
728 |
if (nargs == NULL) { |
729 |
gctl_error(req, "No '%s' argument.", "nargs"); |
730 |
return; |
731 |
} |
732 |
if (*nargs < 1) { |
733 |
gctl_error(req, "Missing device(s)."); |
734 |
return; |
735 |
} |
736 |
force = gctl_get_paraml(req, "force", sizeof(*force)); |
737 |
if (force == NULL) { |
738 |
gctl_error(req, "No '%s' argument.", "force"); |
739 |
return; |
740 |
} |
741 |
if (*force) |
742 |
how = G_MIRROR_DESTROY_HARD; |
743 |
else |
744 |
how = G_MIRROR_DESTROY_SOFT; |
745 |
|
746 |
for (i = 0; i < (u_int)*nargs; i++) { |
747 |
snprintf(param, sizeof(param), "arg%u", i); |
748 |
name = gctl_get_asciiparam(req, param); |
749 |
if (name == NULL) { |
750 |
gctl_error(req, "No 'arg%u' argument.", i); |
751 |
return; |
752 |
} |
753 |
sc = g_mirror_find_device(mp, name); |
754 |
if (sc == NULL) { |
755 |
gctl_error(req, "No such device: %s.", name); |
756 |
return; |
757 |
} |
758 |
g_cancel_event(sc); |
759 |
error = g_mirror_destroy(sc, how); |
760 |
if (error != 0) { |
761 |
gctl_error(req, "Cannot destroy device %s (error=%d).", |
762 |
sc->sc_geom->name, error); |
763 |
sx_xunlock(&sc->sc_lock); |
764 |
return; |
765 |
} |
766 |
/* No need to unlock, because lock is already dead. */ |
767 |
} |
768 |
} |
769 |
|
770 |
void |
771 |
g_mirror_config(struct gctl_req *req, struct g_class *mp, const char *verb) |
772 |
{ |
773 |
uint32_t *version; |
774 |
|
775 |
g_topology_assert(); |
776 |
|
777 |
version = gctl_get_paraml(req, "version", sizeof(*version)); |
778 |
if (version == NULL) { |
779 |
gctl_error(req, "No '%s' argument.", "version"); |
780 |
return; |
781 |
} |
782 |
if (*version != G_MIRROR_VERSION) { |
783 |
gctl_error(req, "Userland and kernel parts are out of sync."); |
784 |
return; |
785 |
} |
786 |
|
787 |
g_topology_unlock(); |
788 |
if (strcmp(verb, "configure") == 0) |
789 |
g_mirror_ctl_configure(req, mp); |
790 |
else if (strcmp(verb, "rebuild") == 0) |
791 |
g_mirror_ctl_rebuild(req, mp); |
792 |
else if (strcmp(verb, "insert") == 0) |
793 |
g_mirror_ctl_insert(req, mp); |
794 |
else if (strcmp(verb, "remove") == 0) |
795 |
g_mirror_ctl_remove(req, mp); |
796 |
else if (strcmp(verb, "deactivate") == 0) |
797 |
g_mirror_ctl_deactivate(req, mp); |
798 |
else if (strcmp(verb, "forget") == 0) |
799 |
g_mirror_ctl_forget(req, mp); |
800 |
else if (strcmp(verb, "stop") == 0) |
801 |
g_mirror_ctl_stop(req, mp); |
802 |
else |
803 |
gctl_error(req, "Unknown verb."); |
804 |
g_topology_lock(); |
805 |
} |