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