2 * Copyright (c) 1997, 1998
3 * Nan Yang Computer Services Limited. All rights reserved.
5 * Written by Greg Lehey
7 * This software is distributed under the so-called ``Berkeley
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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
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.
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.
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 $
43 #define STATIC static /* nothing while we're testing XXX */
46 #include <sys/sysproto.h> /* for sync(2) */
47 #include <sys/devicestat.h>
49 #include <sys/reboot.h>
51 extern int total_malloced
;
52 extern int malloccount
;
53 extern struct mc malloced
[];
57 struct dev_ops vinum_ops
=
59 { "vinum", VINUM_CDEV_MAJOR
, D_DISK
},
61 .d_close
= vinumclose
,
64 .d_ioctl
= vinumioctl
,
66 .d_strategy
= vinumstrategy
,
71 /* Called by main() during pseudo-device attachment. */
72 STATIC
void vinumattach(void *);
74 STATIC
int vinum_modevent(module_t mod
, modeventtype_t type
, void *unused
);
75 STATIC
void vinum_initconf(void);
77 struct _vinum_conf vinum_conf
; /* configuration information */
78 cdev_t vinum_super_dev
;
79 cdev_t vinum_wsuper_dev
;
80 cdev_t vinum_daemon_dev
;
83 * Called by main() during pseudo-device attachment. All we need
84 * to do is allocate enough space for devices to be configured later, and
88 vinumattach(void *dummy
)
90 char *cp
, *cp1
, *cp2
, **drives
;
94 /* modload should prevent multiple loads, so this is worth a panic */
95 if ((vinum_conf
.flags
& VF_LOADED
) != 0)
96 panic("vinum: already loaded");
98 log(LOG_INFO
, "vinum: loaded\n");
99 vinum_conf
.flags
|= VF_LOADED
; /* we're loaded now */
101 daemonq
= NULL
; /* initialize daemon's work queue */
105 dev_ops_add(&vinum_ops
, 0, 0);
111 * Create superdev, wrongsuperdev, and controld devices.
113 vinum_super_dev
= make_dev(&vinum_ops
, VINUM_SUPERDEV
,
114 UID_ROOT
, GID_WHEEL
, 0600,
115 VINUM_SUPERDEV_BASE
);
116 vinum_wsuper_dev
= make_dev(&vinum_ops
, VINUM_WRONGSUPERDEV
,
117 UID_ROOT
, GID_WHEEL
, 0600,
118 VINUM_WRONGSUPERDEV_BASE
);
119 vinum_daemon_dev
= make_dev(&vinum_ops
, VINUM_DAEMON_DEV
,
120 UID_ROOT
, GID_WHEEL
, 0600,
121 VINUM_DAEMON_DEV_BASE
);
124 * See if the loader has passed us a disk to
125 * read the initial configuration from.
127 if ((cp
= kgetenv("vinum.drives")) != NULL
) {
128 for (cp1
= cp
, i
= 0, drives
= 0; *cp1
!= '\0'; i
++) {
130 while (*cp1
!= '\0' && *cp1
!= ',' && *cp1
!= ' ')
134 drives
= krealloc(drives
, (unsigned long)((i
+ 1) * sizeof(char *)),
140 rv
= vinum_scandisk(drives
, i
);
142 log(LOG_NOTICE
, "vinum_scandisk() returned %d", rv
);
144 kfree(drives
, M_TEMP
);
146 if ((cp
= kgetenv("vinum.root")) != NULL
) {
147 for (i
= 0; i
< vinum_conf
.volumes_used
; i
++) {
148 vol
= &vinum_conf
.volume
[i
];
149 if ((vol
->state
== volume_up
)
150 && (strcmp (vol
->name
, cp
) == 0)
152 rootdev
= make_dev(&vinum_ops
, i
, UID_ROOT
, GID_OPERATOR
,
153 0640, VINUM_BASE
"vinumroot");
154 log(LOG_INFO
, "vinum: using volume %s for root device\n", cp
);
162 * Check if we have anything open. If confopen is != 0,
163 * that goes for the super device as well, otherwise
166 * Return 0 if not inactive, 1 if inactive.
169 vinum_inactive(int confopen
)
172 int can_do
= 1; /* assume we can do it */
174 if (confopen
&& (vinum_conf
.flags
& VF_OPEN
)) /* open by vinum(8)? */
175 return 0; /* can't do it while we're open */
177 for (i
= 0; i
< vinum_conf
.volumes_allocated
; i
++) {
178 if ((VOL
[i
].state
> volume_down
)
179 && (VOL
[i
].flags
& VF_OPEN
)) { /* volume is open */
189 * Free all structures.
190 * If cleardrive is 0, save the configuration; otherwise
191 * remove the configuration from the drive.
193 * Before coming here, ensure that no volumes are open.
196 free_vinum(int cleardrive
)
199 int drives_allocated
= vinum_conf
.drives_allocated
;
202 if (cleardrive
) { /* remove the vinum config */
203 for (i
= 0; i
< drives_allocated
; i
++)
204 remove_drive(i
); /* remove the drive */
205 } else { /* keep the config */
206 for (i
= 0; i
< drives_allocated
; i
++)
207 free_drive(&DRIVE
[i
]); /* close files and things */
211 while ((vinum_conf
.flags
& (VF_STOPPING
| VF_DAEMONOPEN
))
212 == (VF_STOPPING
| VF_DAEMONOPEN
)) { /* at least one daemon open, we're stopping */
213 queue_daemon_request(daemonrq_return
, (union daemoninfo
) 0); /* stop the daemon */
214 tsleep(&vinumclose
, 0, "vstop", 1); /* and wait for it */
217 for (i
= 0; i
< vinum_conf
.subdisks_allocated
; i
++) {
218 struct sd
*sd
= &vinum_conf
.sd
[i
];
220 destroy_dev(sd
->sd_dev
);
227 for (i
= 0; i
< vinum_conf
.plexes_allocated
; i
++) {
228 struct plex
*plex
= &vinum_conf
.plex
[i
];
230 if (plex
->plex_dev
) {
231 destroy_dev(plex
->plex_dev
);
232 plex
->plex_dev
= NULL
;
235 if (plex
->state
!= plex_unallocated
) { /* we have real data there */
243 for (i
= 0; i
< vinum_conf
.volumes_allocated
; i
++) {
244 struct volume
*vol
= &vinum_conf
.volume
[i
];
247 destroy_dev(vol
->vol_dev
);
253 bzero(&vinum_conf
, sizeof(vinum_conf
));
260 vinum_conf
.physbufs
= nswbuf
/ 2 + 1;
262 /* allocate space: drives... */
263 DRIVE
= (struct drive
*) Malloc(sizeof(struct drive
) * INITIAL_DRIVES
);
264 CHECKALLOC(DRIVE
, "vinum: no memory\n");
265 bzero(DRIVE
, sizeof(struct drive
) * INITIAL_DRIVES
);
266 vinum_conf
.drives_allocated
= INITIAL_DRIVES
;
267 vinum_conf
.drives_used
= 0;
270 VOL
= (struct volume
*) Malloc(sizeof(struct volume
) * INITIAL_VOLUMES
);
271 CHECKALLOC(VOL
, "vinum: no memory\n");
272 bzero(VOL
, sizeof(struct volume
) * INITIAL_VOLUMES
);
273 vinum_conf
.volumes_allocated
= INITIAL_VOLUMES
;
274 vinum_conf
.volumes_used
= 0;
277 PLEX
= (struct plex
*) Malloc(sizeof(struct plex
) * INITIAL_PLEXES
);
278 CHECKALLOC(PLEX
, "vinum: no memory\n");
279 bzero(PLEX
, sizeof(struct plex
) * INITIAL_PLEXES
);
280 vinum_conf
.plexes_allocated
= INITIAL_PLEXES
;
281 vinum_conf
.plexes_used
= 0;
284 SD
= (struct sd
*) Malloc(sizeof(struct sd
) * INITIAL_SUBDISKS
);
285 CHECKALLOC(SD
, "vinum: no memory\n");
286 bzero(SD
, sizeof(struct sd
) * INITIAL_SUBDISKS
);
287 vinum_conf
.subdisks_allocated
= INITIAL_SUBDISKS
;
288 vinum_conf
.subdisks_used
= 0;
292 vinum_modevent(module_t mod
, modeventtype_t type
, void *unused
)
299 if (!vinum_inactive(1)) /* is anything open? */
300 return EBUSY
; /* yes, we can't do it */
301 vinum_conf
.flags
|= VF_STOPPING
; /* note that we want to stop */
302 sys_sync(NULL
); /* write out buffers */
303 free_vinum(0); /* clean up */
305 if (vinum_super_dev
) {
306 destroy_dev(vinum_super_dev
);
307 vinum_super_dev
= NULL
;
309 if (vinum_wsuper_dev
) {
310 destroy_dev(vinum_wsuper_dev
);
311 vinum_wsuper_dev
= NULL
;
313 if (vinum_daemon_dev
) {
314 destroy_dev(vinum_daemon_dev
);
315 vinum_daemon_dev
= NULL
;
320 if (total_malloced
) {
326 for (i
= 0; i
< malloccount
; i
++) {
327 if (debug
& DEBUG_WARNINGS
) /* want to hear about them */
329 "vinum: exiting with %d bytes malloced from %s:%d\n",
334 poke
= &((int *) malloced
[i
].address
)
335 [malloced
[i
].size
/ (2 * sizeof(int))]; /* middle of the area */
336 if (*poke
== 0xdeadc0de) /* already freed */
338 "vinum: exiting with malloc table inconsistency at %p from %s:%d\n",
343 Free(malloced
[i
].address
);
347 dev_ops_remove_all(&vinum_ops
);
348 log(LOG_INFO
, "vinum: unloaded\n"); /* tell the world */
356 moduledata_t vinum_mod
=
359 (modeventhand_t
) vinum_modevent
,
362 DECLARE_MODULE(vinum
, vinum_mod
, SI_SUB_RAID
, SI_ORDER_MIDDLE
);
365 /* Open a vinum object */
367 vinumopen(struct dev_open_args
*ap
)
369 cdev_t dev
= ap
->a_head
.a_dev
;
375 int devminor
; /* minor number */
377 devminor
= minor(dev
);
379 /* First, decide what we're looking at */
380 switch (DEVTYPE(dev
)) {
381 case VINUM_VOLUME_TYPE
:
383 if (index
>= vinum_conf
.volumes_allocated
)
384 return ENXIO
; /* no such device */
387 switch (vol
->state
) {
388 case volume_unallocated
:
393 vol
->flags
|= VF_OPEN
; /* note we're open */
403 case VINUM_PLEX_TYPE
:
404 if (Volno(dev
) >= vinum_conf
.volumes_allocated
)
408 case VINUM_RAWPLEX_TYPE
:
409 index
= Plexno(dev
); /* get plex index in vinum_conf */
410 if (index
>= vinum_conf
.plexes_allocated
)
411 return ENXIO
; /* no such device */
414 switch (plex
->state
) {
415 case plex_referenced
:
416 case plex_unallocated
:
420 plex
->flags
|= VF_OPEN
; /* note we're open */
425 if ((Volno(dev
) >= vinum_conf
.volumes_allocated
) /* no such volume */
426 ||(Plexno(dev
) >= vinum_conf
.plexes_allocated
)) /* or no such plex */
427 return ENXIO
; /* no such device */
431 case VINUM_RAWSD_TYPE
:
432 index
= Sdno(dev
); /* get the subdisk number */
433 if ((index
>= vinum_conf
.subdisks_allocated
) /* not a valid SD entry */
434 ||(SD
[index
].state
< sd_init
)) /* or SD is not real */
435 return ENXIO
; /* no such device */
439 * Opening a subdisk is always a special operation, so we
440 * ignore the state as long as it represents a real subdisk
448 sd
->flags
|= VF_OPEN
; /* note we're open */
452 case VINUM_SUPERDEV_TYPE
:
453 error
= priv_check_cred(ap
->a_cred
, PRIV_ROOT
, 0); /* are we root? */
454 if (error
== 0) { /* yes, can do */
455 if (devminor
== VINUM_DAEMON_DEV
) /* daemon device */
456 vinum_conf
.flags
|= VF_DAEMONOPEN
; /* we're open */
457 else if (devminor
== VINUM_SUPERDEV
)
458 vinum_conf
.flags
|= VF_OPEN
; /* we're open */
460 error
= ENODEV
; /* nothing, maybe a debug mismatch */
464 /* Vinum drives are disks. We already have a disk
465 * driver, so don't handle them here */
466 case VINUM_DRIVE_TYPE
:
468 return ENODEV
; /* don't know what to do with these */
474 vinumclose(struct dev_close_args
*ap
)
476 cdev_t dev
= ap
->a_head
.a_dev
;
481 devminor
= minor(dev
);
483 /* First, decide what we're looking at */
484 switch (DEVTYPE(dev
)) {
485 case VINUM_VOLUME_TYPE
:
486 if (index
>= vinum_conf
.volumes_allocated
)
487 return ENXIO
; /* no such device */
490 switch (vol
->state
) {
491 case volume_unallocated
:
496 vol
->flags
&= ~VF_OPEN
; /* reset our flags */
506 case VINUM_PLEX_TYPE
:
507 if (Volno(dev
) >= vinum_conf
.volumes_allocated
)
511 case VINUM_RAWPLEX_TYPE
:
512 index
= Plexno(dev
); /* get plex index in vinum_conf */
513 if (index
>= vinum_conf
.plexes_allocated
)
514 return ENXIO
; /* no such device */
515 PLEX
[index
].flags
&= ~VF_OPEN
; /* reset our flags */
519 if ((Volno(dev
) >= vinum_conf
.volumes_allocated
) || /* no such volume */
520 (Plexno(dev
) >= vinum_conf
.plexes_allocated
)) /* or no such plex */
521 return ENXIO
; /* no such device */
524 case VINUM_RAWSD_TYPE
:
525 index
= Sdno(dev
); /* get the subdisk number */
526 if (index
>= vinum_conf
.subdisks_allocated
)
527 return ENXIO
; /* no such device */
528 SD
[index
].flags
&= ~VF_OPEN
; /* reset our flags */
531 case VINUM_SUPERDEV_TYPE
:
533 * don't worry about whether we're root:
534 * nobody else would get this far.
536 if (devminor
== VINUM_SUPERDEV
) /* normal superdev */
537 vinum_conf
.flags
&= ~VF_OPEN
; /* no longer open */
538 else if (devminor
== VINUM_DAEMON_DEV
) { /* the daemon device */
539 vinum_conf
.flags
&= ~VF_DAEMONOPEN
; /* no longer open */
540 if (vinum_conf
.flags
& VF_STOPPING
) /* we're stopping, */
541 wakeup(&vinumclose
); /* we can continue stopping now */
545 case VINUM_DRIVE_TYPE
:
547 return ENODEV
; /* don't know what to do with these */
553 vinumsize(struct dev_psize_args
*ap
)
555 cdev_t dev
= ap
->a_head
.a_dev
;
558 vol
= &VOL
[Volno(dev
)];
560 if (vol
->state
== volume_up
) {
561 ap
->a_result
= (int64_t)vol
->size
;
569 vinumdump(struct dev_dump_args
*ap
)
571 /* Not implemented. */
576 vinumpoll(struct dev_poll_args
*ap
)
578 ap
->a_events
= seltrue(ap
->a_head
.a_dev
, ap
->a_events
);
582 /* Local Variables: */
583 /* fill-column: 50 */