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