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