1 /*- 2 * Copyright (c) 2005 Chris Jones 3 * All rights reserved. 4 * 5 * This software was developed for the FreeBSD Project by Chris Jones 6 * thanks to the support of Google's Summer of Code program and 7 * mentoring by Lukas Ertl. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 #include <sys/param.h> 36 #include <sys/libkern.h> 37 #include <sys/kernel.h> 38 #include <sys/malloc.h> 39 40 #include <geom/geom.h> 41 #include <geom/vinum/geom_vinum_var.h> 42 #include <geom/vinum/geom_vinum.h> 43 #include <geom/vinum/geom_vinum_share.h> 44 45 static int gv_move_sd(struct gv_softc *, struct gctl_req *, 46 struct gv_sd *, char *, int); 47 48 void 49 gv_move(struct g_geom *gp, struct gctl_req *req) 50 { 51 struct gv_softc *sc; 52 struct gv_sd *s; 53 char buf[20], *destination, *object; 54 int *argc, err, *flags, i, type; 55 56 sc = gp->softc; 57 58 argc = gctl_get_paraml(req, "argc", sizeof(*argc)); 59 flags = gctl_get_paraml(req, "flags", sizeof(*flags)); 60 destination = gctl_get_param(req, "destination", NULL); 61 if (destination == NULL) { 62 gctl_error(req, "no destination given"); 63 return; 64 } 65 if (gv_object_type(sc, destination) != GV_TYPE_DRIVE) { 66 gctl_error(req, "destination '%s' is not a drive", destination); 67 return; 68 } 69 70 /* 71 * We start with 1 here, because argv[0] on the command line is the 72 * destination drive. 73 */ 74 for (i = 1; i < *argc; i++) { 75 snprintf(buf, sizeof(buf), "argv%d", i); 76 object = gctl_get_param(req, buf, NULL); 77 if (object == NULL) 78 continue; 79 80 type = gv_object_type(sc, object); 81 if (type != GV_TYPE_SD) { 82 gctl_error(req, "you can only move subdisks; " 83 "'%s' isn't one", object); 84 return; 85 } 86 87 s = gv_find_sd(sc, object); 88 if (s == NULL) { 89 gctl_error(req, "unknown subdisk '%s'", object); 90 return; 91 } 92 err = gv_move_sd(sc, req, s, destination, *flags); 93 if (err) 94 return; 95 } 96 97 gv_save_config_all(sc); 98 } 99 100 /* Move a subdisk. */ 101 static int 102 gv_move_sd(struct gv_softc *sc, struct gctl_req *req, struct gv_sd *cursd, char *destination, int flags) 103 { 104 struct gv_drive *d; 105 struct gv_sd *newsd, *s, *s2; 106 struct gv_plex *p; 107 struct g_consumer *cp; 108 char errstr[ERRBUFSIZ]; 109 int err; 110 111 g_topology_assert(); 112 KASSERT(cursd != NULL, ("gv_move_sd: NULL cursd")); 113 114 cp = cursd->consumer; 115 116 if (cp != NULL && (cp->acr || cp->acw || cp->ace)) { 117 gctl_error(req, "subdisk '%s' is busy", cursd->name); 118 return (-1); 119 } 120 121 if (!(flags && GV_FLAG_F)) { 122 gctl_error(req, "-f flag not passed; move would be " 123 "destructive"); 124 return (-1); 125 } 126 127 d = gv_find_drive(sc, destination); 128 if (d == NULL) { 129 gctl_error(req, "destination drive '%s' not found", 130 destination); 131 return (-1); 132 } 133 134 if (d == cursd->drive_sc) { 135 gctl_error(req, "subdisk '%s' already on drive '%s'", 136 cursd->name, destination); 137 return (-1); 138 } 139 140 /* XXX: Does it have to be part of a plex? */ 141 p = gv_find_plex(sc, cursd->plex); 142 if (p == NULL) { 143 gctl_error(req, "subdisk '%s' is not part of a plex", 144 cursd->name); 145 return (-1); 146 } 147 148 /* Stale the old subdisk. */ 149 err = gv_set_sd_state(cursd, GV_SD_STALE, 150 GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG); 151 if (err) { 152 gctl_error(req, "could not set the subdisk '%s' to state " 153 "'stale'", cursd->name); 154 return (err); 155 } 156 157 /* 158 * Create new subdisk. Ideally, we'd use gv_new_sd, but that requires 159 * us to create a string for it to parse, which is silly. 160 * TODO: maybe refactor gv_new_sd such that this is no longer the case. 161 */ 162 newsd = g_malloc(sizeof(struct gv_sd), M_WAITOK | M_ZERO); 163 newsd->plex_offset = cursd->plex_offset; 164 newsd->size = cursd->size; 165 newsd->drive_offset = -1; 166 strncpy(newsd->name, cursd->name, GV_MAXSDNAME); 167 strncpy(newsd->drive, destination, GV_MAXDRIVENAME); 168 strncpy(newsd->plex, cursd->plex, GV_MAXPLEXNAME); 169 newsd->state = GV_SD_STALE; 170 newsd->vinumconf = cursd->vinumconf; 171 172 err = gv_sd_to_drive(sc, d, newsd, errstr, ERRBUFSIZ); 173 if (err) { 174 /* XXX not enough free space? */ 175 gctl_error(req, errstr); 176 g_free(newsd); 177 return (err); 178 } 179 180 /* Replace the old sd by the new one. */ 181 if (cp != NULL) 182 g_detach(cp); 183 LIST_FOREACH_SAFE(s, &p->subdisks, in_plex, s2) { 184 if (s == cursd) { 185 p->sdcount--; 186 p->size -= s->size; 187 err = gv_rm_sd(sc, req, s, 0); 188 if (err) 189 return (err); 190 191 } 192 } 193 194 gv_sd_to_plex(p, newsd, 1); 195 196 /* Creates the new providers.... */ 197 gv_drive_modify(d); 198 199 /* And reconnect the consumer ... */ 200 if (cp != NULL) { 201 newsd->consumer = cp; 202 err = g_attach(cp, newsd->provider); 203 if (err) { 204 g_destroy_consumer(cp); 205 gctl_error(req, "proposed move would create a loop " 206 "in GEOM config"); 207 return (err); 208 } 209 } 210 211 LIST_INSERT_HEAD(&sc->subdisks, newsd, sd); 212 213 gv_save_config_all(sc); 214 215 return (0); 216 } 217