1/*-
2 * Copyright (c) 2010 Alexander Motin <mav@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/kernel.h>
33#include <sys/module.h>
34#include <sys/lock.h>
35#include <sys/mutex.h>
36#include <sys/bio.h>
37#include <sys/sysctl.h>
38#include <sys/malloc.h>
39#include <sys/bitstring.h>
40#include <vm/uma.h>
41#include <machine/atomic.h>
42#include <geom/geom.h>
43#include <sys/proc.h>
44#include <sys/kthread.h>
45#include <geom/raid/g_raid.h>
46#include "g_raid_md_if.h"
47
48
49static struct g_raid_softc *
50g_raid_find_node(struct g_class *mp, const char *name)
51{
52	struct g_raid_softc *sc;
53	struct g_geom *gp;
54	struct g_provider *pp;
55	struct g_raid_volume *vol;
56
57	/* Look for geom with specified name. */
58	LIST_FOREACH(gp, &mp->geom, geom) {
59		sc = gp->softc;
60		if (sc == NULL)
61			continue;
62		if (sc->sc_stopping != 0)
63			continue;
64		if (strcasecmp(sc->sc_name, name) == 0)
65			return (sc);
66	}
67
68	/* Look for provider with specified name. */
69	LIST_FOREACH(gp, &mp->geom, geom) {
70		sc = gp->softc;
71		if (sc == NULL)
72			continue;
73		if (sc->sc_stopping != 0)
74			continue;
75		LIST_FOREACH(pp, &gp->provider, provider) {
76			if (strcmp(pp->name, name) == 0)
77				return (sc);
78			if (strncmp(pp->name, "raid/", 5) == 0 &&
79			    strcmp(pp->name + 5, name) == 0)
80				return (sc);
81		}
82	}
83
84	/* Look for volume with specified name. */
85	LIST_FOREACH(gp, &mp->geom, geom) {
86		sc = gp->softc;
87		if (sc == NULL)
88			continue;
89		if (sc->sc_stopping != 0)
90			continue;
91		TAILQ_FOREACH(vol, &sc->sc_volumes, v_next) {
92			if (strcmp(vol->v_name, name) == 0)
93				return (sc);
94		}
95	}
96	return (NULL);
97}
98
99static void
100g_raid_ctl_label(struct gctl_req *req, struct g_class *mp)
101{
102	struct g_geom *geom;
103	struct g_raid_softc *sc;
104	const char *format;
105	int *nargs;
106	int crstatus, ctlstatus;
107	char buf[64];
108
109	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
110	if (nargs == NULL) {
111		gctl_error(req, "No '%s' argument.", "nargs");
112		return;
113	}
114	if (*nargs < 4) {
115		gctl_error(req, "Invalid number of arguments.");
116		return;
117	}
118	format = gctl_get_asciiparam(req, "arg0");
119	if (format == NULL) {
120		gctl_error(req, "No format received.");
121		return;
122	}
123	crstatus = g_raid_create_node_format(format, req, &geom);
124	if (crstatus == G_RAID_MD_TASTE_FAIL) {
125		gctl_error(req, "Failed to create array with format '%s'.",
126		    format);
127		return;
128	}
129	sc = (struct g_raid_softc *)geom->softc;
130	g_topology_unlock();
131	sx_xlock(&sc->sc_lock);
132	ctlstatus = G_RAID_MD_CTL(sc->sc_md, req);
133	if (ctlstatus < 0) {
134		gctl_error(req, "Command failed: %d.", ctlstatus);
135		if (crstatus == G_RAID_MD_TASTE_NEW)
136			g_raid_destroy_node(sc, 0);
137	} else {
138		if (crstatus == G_RAID_MD_TASTE_NEW)
139			snprintf(buf, sizeof(buf), "%s created\n", sc->sc_name);
140		else
141			snprintf(buf, sizeof(buf), "%s reused\n", sc->sc_name);
142		gctl_set_param_err(req, "output", buf, strlen(buf) + 1);
143	}
144	sx_xunlock(&sc->sc_lock);
145	g_topology_lock();
146}
147
148static void
149g_raid_ctl_stop(struct gctl_req *req, struct g_class *mp)
150{
151	struct g_raid_softc *sc;
152	const char *nodename;
153	int *nargs, *force;
154	int error, how;
155
156	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
157	if (nargs == NULL) {
158		gctl_error(req, "No '%s' argument.", "nargs");
159		return;
160	}
161	if (*nargs != 1) {
162		gctl_error(req, "Invalid number of arguments.");
163		return;
164	}
165	nodename = gctl_get_asciiparam(req, "arg0");
166	if (nodename == NULL) {
167		gctl_error(req, "No array name received.");
168		return;
169	}
170	sc = g_raid_find_node(mp, nodename);
171	if (sc == NULL) {
172		gctl_error(req, "Array '%s' not found.", nodename);
173		return;
174	}
175	force = gctl_get_paraml(req, "force", sizeof(*force));
176	if (force != NULL && *force)
177		how = G_RAID_DESTROY_HARD;
178	else
179		how = G_RAID_DESTROY_SOFT;
180	g_topology_unlock();
181	sx_xlock(&sc->sc_lock);
182	error = g_raid_destroy(sc, how);
183	if (error != 0)
184		gctl_error(req, "Array is busy.");
185	g_topology_lock();
186}
187
188static void
189g_raid_ctl_other(struct gctl_req *req, struct g_class *mp)
190{
191	struct g_raid_softc *sc;
192	const char *nodename;
193	int *nargs;
194	int ctlstatus;
195
196	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
197	if (nargs == NULL) {
198		gctl_error(req, "No '%s' argument.", "nargs");
199		return;
200	}
201	if (*nargs < 1) {
202		gctl_error(req, "Invalid number of arguments.");
203		return;
204	}
205	nodename = gctl_get_asciiparam(req, "arg0");
206	if (nodename == NULL) {
207		gctl_error(req, "No array name received.");
208		return;
209	}
210	sc = g_raid_find_node(mp, nodename);
211	if (sc == NULL) {
212		gctl_error(req, "Array '%s' not found.", nodename);
213		return;
214	}
215	g_topology_unlock();
216	sx_xlock(&sc->sc_lock);
217	if (sc->sc_md != NULL) {
218		ctlstatus = G_RAID_MD_CTL(sc->sc_md, req);
219		if (ctlstatus < 0)
220			gctl_error(req, "Command failed: %d.", ctlstatus);
221	}
222	sx_xunlock(&sc->sc_lock);
223	g_topology_lock();
224}
225
226void
227g_raid_ctl(struct gctl_req *req, struct g_class *mp, const char *verb)
228{
229	uint32_t *version;
230
231	g_topology_assert();
232
233	version = gctl_get_paraml(req, "version", sizeof(*version));
234	if (version == NULL) {
235		gctl_error(req, "No '%s' argument.", "version");
236		return;
237	}
238	if (*version != G_RAID_VERSION) {
239		gctl_error(req, "Userland and kernel parts are out of sync.");
240		return;
241	}
242
243	if (strcmp(verb, "label") == 0)
244		g_raid_ctl_label(req, mp);
245	else if (strcmp(verb, "stop") == 0)
246		g_raid_ctl_stop(req, mp);
247	else
248		g_raid_ctl_other(req, mp);
249}
250