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/libkern.h> 36 #include <sys/malloc.h> 37 38 #include <geom/geom.h> 39 #include <geom/vinum/geom_vinum_var.h> 40 #include <geom/vinum/geom_vinum.h> 41 42 void 43 gv_move(struct g_geom *gp, struct gctl_req *req) 44 { 45 struct gv_softc *sc; 46 struct gv_sd *s; 47 struct gv_drive *d; 48 char buf[20], *destination, *object; 49 int *argc, *flags, i, type; 50 51 sc = gp->softc; 52 53 argc = gctl_get_paraml(req, "argc", sizeof(*argc)); 54 if (argc == NULL) { 55 gctl_error(req, "no arguments given"); 56 return; 57 } 58 flags = gctl_get_paraml(req, "flags", sizeof(*flags)); 59 if (flags == NULL) { 60 gctl_error(req, "no flags given"); 61 return; 62 } 63 destination = gctl_get_param(req, "destination", NULL); 64 if (destination == NULL) { 65 gctl_error(req, "no destination given"); 66 return; 67 } 68 if (gv_object_type(sc, destination) != GV_TYPE_DRIVE) { 69 gctl_error(req, "destination '%s' is not a drive", destination); 70 return; 71 } 72 d = gv_find_drive(sc, destination); 73 74 /* 75 * We start with 1 here, because argv[0] on the command line is the 76 * destination drive. 77 */ 78 for (i = 1; i < *argc; i++) { 79 snprintf(buf, sizeof(buf), "argv%d", i); 80 object = gctl_get_param(req, buf, NULL); 81 if (object == NULL) 82 continue; 83 84 type = gv_object_type(sc, object); 85 if (type != GV_TYPE_SD) { 86 gctl_error(req, "you can only move subdisks; " 87 "'%s' isn't one", object); 88 return; 89 } 90 91 s = gv_find_sd(sc, object); 92 if (s == NULL) { 93 gctl_error(req, "unknown subdisk '%s'", object); 94 return; 95 } 96 gv_post_event(sc, GV_EVENT_MOVE_SD, s, d, *flags, 0); 97 } 98 } 99 100 /* Move a subdisk. */ 101 int 102 gv_move_sd(struct gv_softc *sc, struct gv_sd *cursd, 103 struct gv_drive *destination, int flags) 104 { 105 struct gv_drive *d; 106 struct gv_sd *newsd, *s, *s2; 107 struct gv_plex *p; 108 int err; 109 110 g_topology_assert(); 111 KASSERT(cursd != NULL, ("gv_move_sd: NULL cursd")); 112 KASSERT(destination != NULL, ("gv_move_sd: NULL destination")); 113 114 d = cursd->drive_sc; 115 116 if ((gv_consumer_is_open(d->consumer) || 117 gv_consumer_is_open(destination->consumer)) && 118 !(flags && GV_FLAG_F)) { 119 G_VINUM_DEBUG(0, "consumers on current and destination drive " 120 " still open"); 121 return (GV_ERR_ISBUSY); 122 } 123 124 if (!(flags && GV_FLAG_F)) { 125 G_VINUM_DEBUG(1, "-f flag not passed; move would be " 126 "destructive"); 127 return (GV_ERR_INVFLAG); 128 } 129 130 if (destination == cursd->drive_sc) { 131 G_VINUM_DEBUG(1, "subdisk '%s' already on drive '%s'", 132 cursd->name, destination->name); 133 return (GV_ERR_ISATTACHED); 134 } 135 136 /* XXX: Does it have to be part of a plex? */ 137 p = gv_find_plex(sc, cursd->plex); 138 if (p == NULL) { 139 G_VINUM_DEBUG(0, "subdisk '%s' is not part of a plex", 140 cursd->name); 141 return (GV_ERR_NOTFOUND); 142 } 143 144 /* Stale the old subdisk. */ 145 err = gv_set_sd_state(cursd, GV_SD_STALE, 146 GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG); 147 if (err) { 148 G_VINUM_DEBUG(0, "could not set the subdisk '%s' to state " 149 "'stale'", cursd->name); 150 return (err); 151 } 152 153 /* 154 * Create new subdisk. Ideally, we'd use gv_new_sd, but that requires 155 * us to create a string for it to parse, which is silly. 156 * TODO: maybe refactor gv_new_sd such that this is no longer the case. 157 */ 158 newsd = g_malloc(sizeof(struct gv_sd), M_WAITOK | M_ZERO); 159 newsd->plex_offset = cursd->plex_offset; 160 newsd->size = cursd->size; 161 newsd->drive_offset = -1; 162 strlcpy(newsd->name, cursd->name, sizeof(newsd->name)); 163 strlcpy(newsd->drive, destination->name, sizeof(newsd->drive)); 164 strlcpy(newsd->plex, cursd->plex, sizeof(newsd->plex)); 165 newsd->state = GV_SD_STALE; 166 newsd->vinumconf = cursd->vinumconf; 167 168 err = gv_sd_to_drive(newsd, destination); 169 if (err) { 170 /* XXX not enough free space? */ 171 g_free(newsd); 172 return (err); 173 } 174 175 /* Replace the old sd by the new one. */ 176 LIST_FOREACH_SAFE(s, &p->subdisks, in_plex, s2) { 177 if (s == cursd) { 178 gv_rm_sd(sc, s); 179 } 180 } 181 gv_sd_to_plex(newsd, p); 182 LIST_INSERT_HEAD(&sc->subdisks, newsd, sd); 183 /* Update volume size of plex. */ 184 if (p->vol_sc != NULL) 185 gv_update_vol_size(p->vol_sc, gv_vol_size(p->vol_sc)); 186 gv_save_config(p->vinumconf); 187 return (0); 188 } 189