xref: /dragonfly/sys/dev/raid/vinum/vinum.c (revision f746689a)
1 /*-
2  * Copyright (c) 1997, 1998
3  *	Nan Yang Computer Services Limited.  All rights reserved.
4  *
5  *  Written by Greg Lehey
6  *
7  *  This software is distributed under the so-called ``Berkeley
8  *  License'':
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by Nan Yang Computer
21  *      Services Limited.
22  * 4. Neither the name of the Company nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * This software is provided ``as is'', and any express or implied
27  * warranties, including, but not limited to, the implied warranties of
28  * merchantability and fitness for a particular purpose are disclaimed.
29  * In no event shall the company or contributors be liable for any
30  * direct, indirect, incidental, special, exemplary, or consequential
31  * damages (including, but not limited to, procurement of substitute
32  * goods or services; loss of use, data, or profits; or business
33  * interruption) however caused and on any theory of liability, whether
34  * in contract, strict liability, or tort (including negligence or
35  * otherwise) arising in any way out of the use of this software, even if
36  * advised of the possibility of such damage.
37  *
38  * $Id: vinum.c,v 1.33 2001/01/09 06:19:15 grog Exp grog $
39  * $FreeBSD: src/sys/dev/vinum/vinum.c,v 1.38.2.3 2003/01/07 12:14:16 joerg Exp $
40  * $DragonFly: src/sys/dev/raid/vinum/vinum.c,v 1.20 2007/05/15 22:44:12 dillon Exp $
41  */
42 
43 #define STATIC static					    /* nothing while we're testing XXX */
44 
45 #include "vinumhdr.h"
46 #include <sys/sysproto.h>				    /* for sync(2) */
47 #include <sys/devicestat.h>
48 #ifdef VINUMDEBUG
49 #include <sys/reboot.h>
50 int debug = 0;
51 extern int total_malloced;
52 extern int malloccount;
53 extern struct mc malloced[];
54 #endif
55 #include "request.h"
56 
57 struct dev_ops vinum_ops =
58 {
59 	{ "vinum", VINUM_CDEV_MAJOR, D_DISK },
60 	.d_open =	vinumopen,
61 	.d_close =	vinumclose,
62 	.d_read =	physread,
63 	.d_write =	physwrite,
64 	.d_ioctl =	vinumioctl,
65 	.d_poll =	vinumpoll,
66 	.d_strategy =	vinumstrategy,
67 	.d_dump =	vinumdump,
68 	.d_psize =	vinumsize,
69 };
70 
71 /* Called by main() during pseudo-device attachment. */
72 STATIC void vinumattach(void *);
73 
74 STATIC int vinum_modevent(module_t mod, modeventtype_t type, void *unused);
75 
76 struct _vinum_conf vinum_conf;				    /* configuration information */
77 
78 /*
79  * Called by main() during pseudo-device attachment.  All we need
80  * to do is allocate enough space for devices to be configured later, and
81  * add devsw entries.
82  */
83 void
84 vinumattach(void *dummy)
85 {
86     char *cp, *cp1, *cp2, **drives;
87     int i, rv;
88     struct volume *vol;
89 
90     /* modload should prevent multiple loads, so this is worth a panic */
91     if ((vinum_conf.flags & VF_LOADED) != 0)
92 	panic("vinum: already loaded");
93 
94     log(LOG_INFO, "vinum: loaded\n");
95     vinum_conf.flags |= VF_LOADED;			    /* we're loaded now */
96 
97     daemonq = NULL;					    /* initialize daemon's work queue */
98     dqend = NULL;
99 
100     dev_ops_add(&vinum_ops, 0, 0);			    /* add the ops entry */
101 
102     vinum_conf.physbufs = nswbuf / 2 + 1;		    /* maximum amount of physical bufs */
103 
104     /* allocate space: drives... */
105     DRIVE = (struct drive *) Malloc(sizeof(struct drive) * INITIAL_DRIVES);
106     CHECKALLOC(DRIVE, "vinum: no memory\n");
107     bzero(DRIVE, sizeof(struct drive) * INITIAL_DRIVES);
108     vinum_conf.drives_allocated = INITIAL_DRIVES;	    /* number of drive slots allocated */
109     vinum_conf.drives_used = 0;				    /* and number in use */
110 
111     /* volumes, ... */
112     VOL = (struct volume *) Malloc(sizeof(struct volume) * INITIAL_VOLUMES);
113     CHECKALLOC(VOL, "vinum: no memory\n");
114     bzero(VOL, sizeof(struct volume) * INITIAL_VOLUMES);
115     vinum_conf.volumes_allocated = INITIAL_VOLUMES;	    /* number of volume slots allocated */
116     vinum_conf.volumes_used = 0;			    /* and number in use */
117 
118     /* plexes, ... */
119     PLEX = (struct plex *) Malloc(sizeof(struct plex) * INITIAL_PLEXES);
120     CHECKALLOC(PLEX, "vinum: no memory\n");
121     bzero(PLEX, sizeof(struct plex) * INITIAL_PLEXES);
122     vinum_conf.plexes_allocated = INITIAL_PLEXES;	    /* number of plex slots allocated */
123     vinum_conf.plexes_used = 0;				    /* and number in use */
124 
125     /* and subdisks */
126     SD = (struct sd *) Malloc(sizeof(struct sd) * INITIAL_SUBDISKS);
127     CHECKALLOC(SD, "vinum: no memory\n");
128     bzero(SD, sizeof(struct sd) * INITIAL_SUBDISKS);
129     vinum_conf.subdisks_allocated = INITIAL_SUBDISKS;	    /* number of sd slots allocated */
130     vinum_conf.subdisks_used = 0;			    /* and number in use */
131 
132     /*
133      * See if the loader has passed us a disk to
134      * read the initial configuration from.
135      */
136     if ((cp = kgetenv("vinum.drives")) != NULL) {
137 	for (cp1 = cp, i = 0, drives = 0; *cp1 != '\0'; i++) {
138 	    cp2 = cp1;
139 	    while (*cp1 != '\0' && *cp1 != ',' && *cp1 != ' ')
140 		cp1++;
141 	    if (*cp1 != '\0')
142 		*cp1++ = '\0';
143 	    drives = krealloc(drives, (unsigned long)((i + 1) * sizeof(char *)),
144 			     M_TEMP, M_WAITOK);
145 	    drives[i] = cp2;
146 	}
147 	if (i == 0)
148 	    goto bailout;
149 	rv = vinum_scandisk(drives, i);
150 	if (rv)
151 	    log(LOG_NOTICE, "vinum_scandisk() returned %d", rv);
152     bailout:
153 	kfree(drives, M_TEMP);
154     }
155     if ((cp = kgetenv("vinum.root")) != NULL) {
156 	for (i = 0; i < vinum_conf.volumes_used; i++) {
157 	    vol = &vinum_conf.volume[i];
158 	    if ((vol->state == volume_up)
159 		&& (strcmp (vol->name, cp) == 0)
160 	    ) {
161 		rootdev = make_dev(&vinum_ops, i, UID_ROOT, GID_OPERATOR,
162 				0640, "vinum");
163 		log(LOG_INFO, "vinum: using volume %s for root device\n", cp);
164 		break;
165 	    }
166 	}
167     }
168 }
169 
170 /*
171  * Check if we have anything open.  If confopen is != 0,
172  * that goes for the super device as well, otherwise
173  * only for volumes.
174  *
175  * Return 0 if not inactive, 1 if inactive.
176  */
177 int
178 vinum_inactive(int confopen)
179 {
180     int i;
181     int can_do = 1;					    /* assume we can do it */
182 
183     if (confopen && (vinum_conf.flags & VF_OPEN))	    /* open by vinum(8)? */
184 	return 0;					    /* can't do it while we're open */
185     lock_config();
186     for (i = 0; i < vinum_conf.volumes_allocated; i++) {
187 	if ((VOL[i].state > volume_down)
188 	    && (VOL[i].flags & VF_OPEN)) {		    /* volume is open */
189 	    can_do = 0;
190 	    break;
191 	}
192     }
193     unlock_config();
194     return can_do;
195 }
196 
197 /*
198  * Free all structures.
199  * If cleardrive is 0, save the configuration; otherwise
200  * remove the configuration from the drive.
201  *
202  * Before coming here, ensure that no volumes are open.
203  */
204 void
205 free_vinum(int cleardrive)
206 {
207     int i;
208     int drives_allocated = vinum_conf.drives_allocated;
209 
210     if (DRIVE != NULL) {
211 	if (cleardrive) {				    /* remove the vinum config */
212 	    for (i = 0; i < drives_allocated; i++)
213 		remove_drive(i);			    /* remove the drive */
214 	} else {					    /* keep the config */
215 	    for (i = 0; i < drives_allocated; i++)
216 		free_drive(&DRIVE[i]);			    /* close files and things */
217 	}
218 	Free(DRIVE);
219     }
220     while ((vinum_conf.flags & (VF_STOPPING | VF_DAEMONOPEN))
221 	== (VF_STOPPING | VF_DAEMONOPEN)) {		    /* at least one daemon open, we're stopping */
222 	queue_daemon_request(daemonrq_return, (union daemoninfo) 0); /* stop the daemon */
223 	tsleep(&vinumclose, 0, "vstop", 1);		    /* and wait for it */
224     }
225     if (SD != NULL)
226 	Free(SD);
227     if (PLEX != NULL) {
228 	for (i = 0; i < vinum_conf.plexes_allocated; i++) {
229 	    struct plex *plex = &vinum_conf.plex[i];
230 
231 	    if (plex->state != plex_unallocated) {	    /* we have real data there */
232 		if (plex->sdnos)
233 		    Free(plex->sdnos);
234 	    }
235 	}
236 	Free(PLEX);
237     }
238     if (VOL != NULL)
239 	Free(VOL);
240     bzero(&vinum_conf, sizeof(vinum_conf));
241 }
242 
243 STATIC int
244 vinum_modevent(module_t mod, modeventtype_t type, void *unused)
245 {
246     switch (type) {
247     case MOD_LOAD:
248 	vinumattach(NULL);
249 	return 0;					    /* OK */
250     case MOD_UNLOAD:
251 	if (!vinum_inactive(1))				    /* is anything open? */
252 	    return EBUSY;				    /* yes, we can't do it */
253 	vinum_conf.flags |= VF_STOPPING;		    /* note that we want to stop */
254 	sys_sync(NULL);			    /* write out buffers */
255 	free_vinum(0);					    /* clean up */
256 #ifdef VINUMDEBUG
257 	if (total_malloced) {
258 	    int i;
259 #ifdef INVARIANTS
260 	    int *poke;
261 #endif
262 
263 	    for (i = 0; i < malloccount; i++) {
264 		if (debug & DEBUG_WARNINGS)		    /* want to hear about them */
265 		    log(LOG_WARNING,
266 			"vinum: exiting with %d bytes malloced from %s:%d\n",
267 			malloced[i].size,
268 			malloced[i].file,
269 			malloced[i].line);
270 #ifdef INVARIANTS
271 		poke = &((int *) malloced[i].address)
272 		    [malloced[i].size / (2 * sizeof(int))]; /* middle of the area */
273 		if (*poke == 0xdeadc0de)		    /* already freed */
274 		    log(LOG_ERR,
275 			"vinum: exiting with malloc table inconsistency at %p from %s:%d\n",
276 			malloced[i].address,
277 			malloced[i].file,
278 			malloced[i].line);
279 #endif
280 		Free(malloced[i].address);
281 	    }
282 	}
283 #endif
284 	dev_ops_remove(&vinum_ops, 0, 0);
285 	log(LOG_INFO, "vinum: unloaded\n");		    /* tell the world */
286 	return 0;
287     default:
288 	break;
289     }
290     return 0;
291 }
292 
293 moduledata_t vinum_mod =
294 {
295     "vinum",
296     (modeventhand_t) vinum_modevent,
297     0
298 };
299 DECLARE_MODULE(vinum, vinum_mod, SI_SUB_RAID, SI_ORDER_MIDDLE);
300 
301 /* ARGSUSED */
302 /* Open a vinum object */
303 int
304 vinumopen(struct dev_open_args *ap)
305 {
306     cdev_t dev = ap->a_head.a_dev;
307     int error;
308     unsigned int index;
309     struct volume *vol;
310     struct plex *plex;
311     struct sd *sd;
312     int devminor;					    /* minor number */
313 
314     devminor = minor(dev);
315     error = 0;
316     /* First, decide what we're looking at */
317     switch (DEVTYPE(dev)) {
318     case VINUM_VOLUME_TYPE:
319 	index = Volno(dev);
320 	if (index >= vinum_conf.volumes_allocated)
321 	    return ENXIO;				    /* no such device */
322 	vol = &VOL[index];
323 
324 	switch (vol->state) {
325 	case volume_unallocated:
326 	case volume_uninit:
327 	    return ENXIO;
328 
329 	case volume_up:
330 	    vol->flags |= VF_OPEN;			    /* note we're open */
331 	    return 0;
332 
333 	case volume_down:
334 	    return EIO;
335 
336 	default:
337 	    return EINVAL;
338 	}
339 
340     case VINUM_PLEX_TYPE:
341 	if (Volno(dev) >= vinum_conf.volumes_allocated)
342 	    return ENXIO;
343 	/* FALLTHROUGH */
344 
345     case VINUM_RAWPLEX_TYPE:
346 	index = Plexno(dev);				    /* get plex index in vinum_conf */
347 	if (index >= vinum_conf.plexes_allocated)
348 	    return ENXIO;				    /* no such device */
349 	plex = &PLEX[index];
350 
351 	switch (plex->state) {
352 	case plex_referenced:
353 	case plex_unallocated:
354 	    return EINVAL;
355 
356 	default:
357 	    plex->flags |= VF_OPEN;			    /* note we're open */
358 	    return 0;
359 	}
360 
361     case VINUM_SD_TYPE:
362 	if ((Volno(dev) >= vinum_conf.volumes_allocated)    /* no such volume */
363 	||(Plexno(dev) >= vinum_conf.plexes_allocated))	    /* or no such plex */
364 	    return ENXIO;				    /* no such device */
365 
366 	/* FALLTHROUGH */
367 
368     case VINUM_RAWSD_TYPE:
369 	index = Sdno(dev);				    /* get the subdisk number */
370 	if ((index >= vinum_conf.subdisks_allocated)	    /* not a valid SD entry */
371 	||(SD[index].state < sd_init))			    /* or SD is not real */
372 	    return ENXIO;				    /* no such device */
373 	sd = &SD[index];
374 
375 	/*
376 	 * Opening a subdisk is always a special operation, so we
377 	 * ignore the state as long as it represents a real subdisk
378 	 */
379 	switch (sd->state) {
380 	case sd_unallocated:
381 	case sd_uninit:
382 	    return EINVAL;
383 
384 	default:
385 	    sd->flags |= VF_OPEN;			    /* note we're open */
386 	    return 0;
387 	}
388 
389     case VINUM_SUPERDEV_TYPE:
390 	error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0);  /* are we root? */
391 	if (error == 0) {				    /* yes, can do */
392 	    if (devminor == VINUM_DAEMON_DEV)		    /* daemon device */
393 		vinum_conf.flags |= VF_DAEMONOPEN;	    /* we're open */
394 	    else if (devminor == VINUM_SUPERDEV)
395 		vinum_conf.flags |= VF_OPEN;		    /* we're open */
396 	    else
397 		error = ENODEV;				    /* nothing, maybe a debug mismatch */
398 	}
399 	return error;
400 
401 	/* Vinum drives are disks.  We already have a disk
402 	 * driver, so don't handle them here */
403     case VINUM_DRIVE_TYPE:
404     default:
405 	return ENODEV;					    /* don't know what to do with these */
406     }
407 }
408 
409 /* ARGSUSED */
410 int
411 vinumclose(struct dev_close_args *ap)
412 {
413     cdev_t dev = ap->a_head.a_dev;
414     unsigned int index;
415     struct volume *vol;
416     int devminor;
417 
418     devminor = minor(dev);
419     index = Volno(dev);
420     /* First, decide what we're looking at */
421     switch (DEVTYPE(dev)) {
422     case VINUM_VOLUME_TYPE:
423 	if (index >= vinum_conf.volumes_allocated)
424 	    return ENXIO;				    /* no such device */
425 	vol = &VOL[index];
426 
427 	switch (vol->state) {
428 	case volume_unallocated:
429 	case volume_uninit:
430 	    return ENXIO;
431 
432 	case volume_up:
433 	    vol->flags &= ~VF_OPEN;			    /* reset our flags */
434 	    return 0;
435 
436 	case volume_down:
437 	    return EIO;
438 
439 	default:
440 	    return EINVAL;
441 	}
442 
443     case VINUM_PLEX_TYPE:
444 	if (Volno(dev) >= vinum_conf.volumes_allocated)
445 	    return ENXIO;
446 	/* FALLTHROUGH */
447 
448     case VINUM_RAWPLEX_TYPE:
449 	index = Plexno(dev);				    /* get plex index in vinum_conf */
450 	if (index >= vinum_conf.plexes_allocated)
451 	    return ENXIO;				    /* no such device */
452 	PLEX[index].flags &= ~VF_OPEN;			    /* reset our flags */
453 	return 0;
454 
455     case VINUM_SD_TYPE:
456 	if ((Volno(dev) >= vinum_conf.volumes_allocated) || /* no such volume */
457 	    (Plexno(dev) >= vinum_conf.plexes_allocated))   /* or no such plex */
458 	    return ENXIO;				    /* no such device */
459 	/* FALLTHROUGH */
460 
461     case VINUM_RAWSD_TYPE:
462 	index = Sdno(dev);				    /* get the subdisk number */
463 	if (index >= vinum_conf.subdisks_allocated)
464 	    return ENXIO;				    /* no such device */
465 	SD[index].flags &= ~VF_OPEN;			    /* reset our flags */
466 	return 0;
467 
468     case VINUM_SUPERDEV_TYPE:
469 	/*
470 	 * don't worry about whether we're root:
471 	 * nobody else would get this far.
472 	 */
473 	if (devminor == VINUM_SUPERDEV)			    /* normal superdev */
474 	    vinum_conf.flags &= ~VF_OPEN;		    /* no longer open */
475 	else if (devminor == VINUM_DAEMON_DEV) {	    /* the daemon device */
476 	    vinum_conf.flags &= ~VF_DAEMONOPEN;		    /* no longer open */
477 	    if (vinum_conf.flags & VF_STOPPING)		    /* we're stopping, */
478 		wakeup(&vinumclose);			    /* we can continue stopping now */
479 	}
480 	return 0;
481 
482     case VINUM_DRIVE_TYPE:
483     default:
484 	return ENODEV;					    /* don't know what to do with these */
485     }
486 }
487 
488 /* size routine */
489 int
490 vinumsize(struct dev_psize_args *ap)
491 {
492     cdev_t dev = ap->a_head.a_dev;
493     struct volume *vol;
494 
495     vol = &VOL[Volno(dev)];
496 
497     if (vol->state == volume_up) {
498 	ap->a_result = (int64_t)vol->size;
499 	return(0);
500     } else {
501 	return(ENXIO);
502     }
503 }
504 
505 int
506 vinumdump(struct dev_dump_args *ap)
507 {
508     /* Not implemented. */
509     return ENXIO;
510 }
511 
512 int
513 vinumpoll(struct dev_poll_args *ap)
514 {
515     ap->a_events = seltrue(ap->a_head.a_dev, ap->a_events);
516     return(0);
517 }
518 
519 /* Local Variables: */
520 /* fill-column: 50 */
521 /* End: */
522