Merge branch 'vendor/GCC44'
[dragonfly.git] / sys / dev / raid / vinum / vinum.c
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 #if 0
101     dev_ops_add(&vinum_ops, 0, 0);
102 #endif
103
104     vinum_conf.physbufs = nswbuf / 2 + 1;                   /* maximum amount of physical bufs */
105
106     /* allocate space: drives... */
107     DRIVE = (struct drive *) Malloc(sizeof(struct drive) * INITIAL_DRIVES);
108     CHECKALLOC(DRIVE, "vinum: no memory\n");
109     bzero(DRIVE, sizeof(struct drive) * INITIAL_DRIVES);
110     vinum_conf.drives_allocated = INITIAL_DRIVES;           /* number of drive slots allocated */
111     vinum_conf.drives_used = 0;                             /* and number in use */
112
113     /* volumes, ... */
114     VOL = (struct volume *) Malloc(sizeof(struct volume) * INITIAL_VOLUMES);
115     CHECKALLOC(VOL, "vinum: no memory\n");
116     bzero(VOL, sizeof(struct volume) * INITIAL_VOLUMES);
117     vinum_conf.volumes_allocated = INITIAL_VOLUMES;         /* number of volume slots allocated */
118     vinum_conf.volumes_used = 0;                            /* and number in use */
119
120     /* plexes, ... */
121     PLEX = (struct plex *) Malloc(sizeof(struct plex) * INITIAL_PLEXES);
122     CHECKALLOC(PLEX, "vinum: no memory\n");
123     bzero(PLEX, sizeof(struct plex) * INITIAL_PLEXES);
124     vinum_conf.plexes_allocated = INITIAL_PLEXES;           /* number of plex slots allocated */
125     vinum_conf.plexes_used = 0;                             /* and number in use */
126
127     /* and subdisks */
128     SD = (struct sd *) Malloc(sizeof(struct sd) * INITIAL_SUBDISKS);
129     CHECKALLOC(SD, "vinum: no memory\n");
130     bzero(SD, sizeof(struct sd) * INITIAL_SUBDISKS);
131     vinum_conf.subdisks_allocated = INITIAL_SUBDISKS;       /* number of sd slots allocated */
132     vinum_conf.subdisks_used = 0;                           /* and number in use */
133
134     /*
135      * See if the loader has passed us a disk to
136      * read the initial configuration from.
137      */
138     if ((cp = kgetenv("vinum.drives")) != NULL) {
139         for (cp1 = cp, i = 0, drives = 0; *cp1 != '\0'; i++) {
140             cp2 = cp1;
141             while (*cp1 != '\0' && *cp1 != ',' && *cp1 != ' ')
142                 cp1++;
143             if (*cp1 != '\0')
144                 *cp1++ = '\0';
145             drives = krealloc(drives, (unsigned long)((i + 1) * sizeof(char *)),
146                              M_TEMP, M_WAITOK);
147             drives[i] = cp2;
148         }
149         if (i == 0)
150             goto bailout;
151         rv = vinum_scandisk(drives, i);
152         if (rv)
153             log(LOG_NOTICE, "vinum_scandisk() returned %d", rv);
154     bailout:
155         kfree(drives, M_TEMP);
156     }
157     if ((cp = kgetenv("vinum.root")) != NULL) {
158         for (i = 0; i < vinum_conf.volumes_used; i++) {
159             vol = &vinum_conf.volume[i];
160             if ((vol->state == volume_up)
161                 && (strcmp (vol->name, cp) == 0) 
162             ) {
163                 rootdev = make_dev(&vinum_ops, i, UID_ROOT, GID_OPERATOR,
164                                 0640, "vinum");
165                 log(LOG_INFO, "vinum: using volume %s for root device\n", cp);
166                 break;
167             }
168         }
169     }
170 }
171
172 /*
173  * Check if we have anything open.  If confopen is != 0,
174  * that goes for the super device as well, otherwise
175  * only for volumes.
176  *
177  * Return 0 if not inactive, 1 if inactive.
178  */
179 int
180 vinum_inactive(int confopen)
181 {
182     int i;
183     int can_do = 1;                                         /* assume we can do it */
184
185     if (confopen && (vinum_conf.flags & VF_OPEN))           /* open by vinum(8)? */
186         return 0;                                           /* can't do it while we're open */
187     lock_config();
188     for (i = 0; i < vinum_conf.volumes_allocated; i++) {
189         if ((VOL[i].state > volume_down)
190             && (VOL[i].flags & VF_OPEN)) {                  /* volume is open */
191             can_do = 0;
192             break;
193         }
194     }
195     unlock_config();
196     return can_do;
197 }
198
199 /*
200  * Free all structures.
201  * If cleardrive is 0, save the configuration; otherwise
202  * remove the configuration from the drive.
203  *
204  * Before coming here, ensure that no volumes are open.
205  */
206 void
207 free_vinum(int cleardrive)
208 {
209     int i;
210     int drives_allocated = vinum_conf.drives_allocated;
211
212     if (DRIVE != NULL) {
213         if (cleardrive) {                                   /* remove the vinum config */
214             for (i = 0; i < drives_allocated; i++)
215                 remove_drive(i);                            /* remove the drive */
216         } else {                                            /* keep the config */
217             for (i = 0; i < drives_allocated; i++)
218                 free_drive(&DRIVE[i]);                      /* close files and things */
219         }
220         Free(DRIVE);
221     }
222     while ((vinum_conf.flags & (VF_STOPPING | VF_DAEMONOPEN))
223         == (VF_STOPPING | VF_DAEMONOPEN)) {                 /* at least one daemon open, we're stopping */
224         queue_daemon_request(daemonrq_return, (union daemoninfo) 0); /* stop the daemon */
225         tsleep(&vinumclose, 0, "vstop", 1);                 /* and wait for it */
226     }
227     if (SD != NULL)
228         Free(SD);
229     if (PLEX != NULL) {
230         for (i = 0; i < vinum_conf.plexes_allocated; i++) {
231             struct plex *plex = &vinum_conf.plex[i];
232
233             if (plex->state != plex_unallocated) {          /* we have real data there */
234                 if (plex->sdnos)
235                     Free(plex->sdnos);
236             }
237         }
238         Free(PLEX);
239     }
240     if (VOL != NULL)
241         Free(VOL);
242     bzero(&vinum_conf, sizeof(vinum_conf));
243 }
244
245 STATIC int
246 vinum_modevent(module_t mod, modeventtype_t type, void *unused)
247 {
248     switch (type) {
249     case MOD_LOAD:
250         vinumattach(NULL);
251         return 0;                                           /* OK */
252     case MOD_UNLOAD:
253         if (!vinum_inactive(1))                             /* is anything open? */
254             return EBUSY;                                   /* yes, we can't do it */
255         vinum_conf.flags |= VF_STOPPING;                    /* note that we want to stop */
256         sys_sync(NULL);                     /* write out buffers */
257         free_vinum(0);                                      /* clean up */
258 #ifdef VINUMDEBUG
259         if (total_malloced) {
260             int i;
261 #ifdef INVARIANTS
262             int *poke;
263 #endif
264
265             for (i = 0; i < malloccount; i++) {
266                 if (debug & DEBUG_WARNINGS)                 /* want to hear about them */
267                     log(LOG_WARNING,
268                         "vinum: exiting with %d bytes malloced from %s:%d\n",
269                         malloced[i].size,
270                         malloced[i].file,
271                         malloced[i].line);
272 #ifdef INVARIANTS
273                 poke = &((int *) malloced[i].address)
274                     [malloced[i].size / (2 * sizeof(int))]; /* middle of the area */
275                 if (*poke == 0xdeadc0de)                    /* already freed */
276                     log(LOG_ERR,
277                         "vinum: exiting with malloc table inconsistency at %p from %s:%d\n",
278                         malloced[i].address,
279                         malloced[i].file,
280                         malloced[i].line);
281 #endif
282                 Free(malloced[i].address);
283             }
284         }
285 #endif
286         dev_ops_remove_all(&vinum_ops);
287         log(LOG_INFO, "vinum: unloaded\n");                 /* tell the world */
288         return 0;
289     default:
290         break;
291     }
292     return 0;
293 }
294
295 moduledata_t vinum_mod =
296 {
297     "vinum",
298     (modeventhand_t) vinum_modevent,
299     0
300 };
301 DECLARE_MODULE(vinum, vinum_mod, SI_SUB_RAID, SI_ORDER_MIDDLE);
302
303 /* ARGSUSED */
304 /* Open a vinum object */
305 int
306 vinumopen(struct dev_open_args *ap)
307 {
308     cdev_t dev = ap->a_head.a_dev;
309     int error;
310     unsigned int index;
311     struct volume *vol;
312     struct plex *plex;
313     struct sd *sd;
314     int devminor;                                           /* minor number */
315
316     devminor = minor(dev);
317     error = 0;
318     /* First, decide what we're looking at */
319     switch (DEVTYPE(dev)) {
320     case VINUM_VOLUME_TYPE:
321         index = Volno(dev);
322         if (index >= vinum_conf.volumes_allocated)
323             return ENXIO;                                   /* no such device */
324         vol = &VOL[index];
325
326         switch (vol->state) {
327         case volume_unallocated:
328         case volume_uninit:
329             return ENXIO;
330
331         case volume_up:
332             vol->flags |= VF_OPEN;                          /* note we're open */
333             return 0;
334
335         case volume_down:
336             return EIO;
337
338         default:
339             return EINVAL;
340         }
341
342     case VINUM_PLEX_TYPE:
343         if (Volno(dev) >= vinum_conf.volumes_allocated)
344             return ENXIO;
345         /* FALLTHROUGH */
346
347     case VINUM_RAWPLEX_TYPE:
348         index = Plexno(dev);                                /* get plex index in vinum_conf */
349         if (index >= vinum_conf.plexes_allocated)
350             return ENXIO;                                   /* no such device */
351         plex = &PLEX[index];
352
353         switch (plex->state) {
354         case plex_referenced:
355         case plex_unallocated:
356             return EINVAL;
357
358         default:
359             plex->flags |= VF_OPEN;                         /* note we're open */
360             return 0;
361         }
362
363     case VINUM_SD_TYPE:
364         if ((Volno(dev) >= vinum_conf.volumes_allocated)    /* no such volume */
365         ||(Plexno(dev) >= vinum_conf.plexes_allocated))     /* or no such plex */
366             return ENXIO;                                   /* no such device */
367
368         /* FALLTHROUGH */
369
370     case VINUM_RAWSD_TYPE:
371         index = Sdno(dev);                                  /* get the subdisk number */
372         if ((index >= vinum_conf.subdisks_allocated)        /* not a valid SD entry */
373         ||(SD[index].state < sd_init))                      /* or SD is not real */
374             return ENXIO;                                   /* no such device */
375         sd = &SD[index];
376
377         /*
378          * Opening a subdisk is always a special operation, so we
379          * ignore the state as long as it represents a real subdisk
380          */
381         switch (sd->state) {
382         case sd_unallocated:
383         case sd_uninit:
384             return EINVAL;
385
386         default:
387             sd->flags |= VF_OPEN;                           /* note we're open */
388             return 0;
389         }
390
391     case VINUM_SUPERDEV_TYPE:
392         error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0);  /* are we root? */
393         if (error == 0) {                                   /* yes, can do */
394             if (devminor == VINUM_DAEMON_DEV)               /* daemon device */
395                 vinum_conf.flags |= VF_DAEMONOPEN;          /* we're open */
396             else if (devminor == VINUM_SUPERDEV)
397                 vinum_conf.flags |= VF_OPEN;                /* we're open */
398             else
399                 error = ENODEV;                             /* nothing, maybe a debug mismatch */
400         }
401         return error;
402
403         /* Vinum drives are disks.  We already have a disk
404          * driver, so don't handle them here */
405     case VINUM_DRIVE_TYPE:
406     default:
407         return ENODEV;                                      /* don't know what to do with these */
408     }
409 }
410
411 /* ARGSUSED */
412 int
413 vinumclose(struct dev_close_args *ap)
414 {
415     cdev_t dev = ap->a_head.a_dev;
416     unsigned int index;
417     struct volume *vol;
418     int devminor;
419
420     devminor = minor(dev);
421     index = Volno(dev);
422     /* First, decide what we're looking at */
423     switch (DEVTYPE(dev)) {
424     case VINUM_VOLUME_TYPE:
425         if (index >= vinum_conf.volumes_allocated)
426             return ENXIO;                                   /* no such device */
427         vol = &VOL[index];
428
429         switch (vol->state) {
430         case volume_unallocated:
431         case volume_uninit:
432             return ENXIO;
433
434         case volume_up:
435             vol->flags &= ~VF_OPEN;                         /* reset our flags */
436             return 0;
437
438         case volume_down:
439             return EIO;
440
441         default:
442             return EINVAL;
443         }
444
445     case VINUM_PLEX_TYPE:
446         if (Volno(dev) >= vinum_conf.volumes_allocated)
447             return ENXIO;
448         /* FALLTHROUGH */
449
450     case VINUM_RAWPLEX_TYPE:
451         index = Plexno(dev);                                /* get plex index in vinum_conf */
452         if (index >= vinum_conf.plexes_allocated)
453             return ENXIO;                                   /* no such device */
454         PLEX[index].flags &= ~VF_OPEN;                      /* reset our flags */
455         return 0;
456
457     case VINUM_SD_TYPE:
458         if ((Volno(dev) >= vinum_conf.volumes_allocated) || /* no such volume */
459             (Plexno(dev) >= vinum_conf.plexes_allocated))   /* or no such plex */
460             return ENXIO;                                   /* no such device */
461         /* FALLTHROUGH */
462
463     case VINUM_RAWSD_TYPE:
464         index = Sdno(dev);                                  /* get the subdisk number */
465         if (index >= vinum_conf.subdisks_allocated)
466             return ENXIO;                                   /* no such device */
467         SD[index].flags &= ~VF_OPEN;                        /* reset our flags */
468         return 0;
469
470     case VINUM_SUPERDEV_TYPE:
471         /*
472          * don't worry about whether we're root:
473          * nobody else would get this far.
474          */
475         if (devminor == VINUM_SUPERDEV)                     /* normal superdev */
476             vinum_conf.flags &= ~VF_OPEN;                   /* no longer open */
477         else if (devminor == VINUM_DAEMON_DEV) {            /* the daemon device */
478             vinum_conf.flags &= ~VF_DAEMONOPEN;             /* no longer open */
479             if (vinum_conf.flags & VF_STOPPING)             /* we're stopping, */
480                 wakeup(&vinumclose);                        /* we can continue stopping now */
481         }
482         return 0;
483
484     case VINUM_DRIVE_TYPE:
485     default:
486         return ENODEV;                                      /* don't know what to do with these */
487     }
488 }
489
490 /* size routine */
491 int
492 vinumsize(struct dev_psize_args *ap)
493 {
494     cdev_t dev = ap->a_head.a_dev;
495     struct volume *vol;
496
497     vol = &VOL[Volno(dev)];
498
499     if (vol->state == volume_up) {
500         ap->a_result = (int64_t)vol->size;
501         return(0);
502     } else {
503         return(ENXIO);
504     }
505 }
506
507 int
508 vinumdump(struct dev_dump_args *ap)
509 {
510     /* Not implemented. */
511     return ENXIO;
512 }
513
514 int
515 vinumpoll(struct dev_poll_args *ap)
516 {
517     ap->a_events = seltrue(ap->a_head.a_dev, ap->a_events);
518     return(0);
519 }
520
521 /* Local Variables: */
522 /* fill-column: 50 */
523 /* End: */