Merge from vendor branch LIBARCHIVE:
[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.19 2006/11/03 16:33:38 corecode 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 = suser_cred(ap->a_cred, 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 = 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: */