]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/vinum/vinum.c
This commit was generated by cvs2svn to compensate for changes in r102514,
[FreeBSD/FreeBSD.git] / sys / dev / 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.34 2001/05/22 04:07:22 grog Exp grog $
39  * $FreeBSD$
40  */
41
42 #define STATIC static                                       /* nothing while we're testing XXX */
43
44 #include <dev/vinum/vinumhdr.h>
45 #include <sys/sysproto.h>                                   /* for sync(2) */
46 #include <sys/devicestat.h>
47 #ifdef VINUMDEBUG
48 #include <sys/reboot.h>
49 int debug = 0;
50 extern int total_malloced;
51 extern int malloccount;
52 extern struct mc malloced[];
53 #endif
54 #include <dev/vinum/request.h>
55
56 struct cdevsw vinum_cdevsw =
57 {
58     vinumopen, vinumclose, physread, physwrite,
59     vinumioctl, seltrue, nommap, vinumstrategy,
60     "vinum", VINUM_CDEV_MAJOR, nodump, vinumsize,
61     D_DISK
62 };
63
64 /* Called by main() during pseudo-device attachment. */
65 STATIC void vinumattach(void *);
66
67 STATIC int vinum_modevent(module_t mod, modeventtype_t type, void *unused);
68
69 struct _vinum_conf vinum_conf;                              /* configuration information */
70
71 dev_t vinum_daemon_dev;
72 dev_t vinum_super_dev;
73
74 /*
75  * Called by main() during pseudo-device attachment.  All we need
76  * to do is allocate enough space for devices to be configured later, and
77  * add devsw entries.
78  */
79 void
80 vinumattach(void *dummy)
81 {
82     /* modload should prevent multiple loads, so this is worth a panic */
83     if ((vinum_conf.flags & VF_LOADED) != 0)
84         panic("vinum: already loaded");
85
86     log(LOG_INFO, "vinum: loaded\n");
87 #ifdef VINUMDEBUG
88     vinum_conf.flags |= VF_LOADED | VF_HASDEBUG;            /* we're loaded now, and we support debug */
89 #else
90     vinum_conf.flags |= VF_LOADED;                          /* we're loaded now */
91 #endif
92
93     daemonq = NULL;                                         /* initialize daemon's work queue */
94     dqend = NULL;
95
96     vinum_daemon_dev = make_dev(&vinum_cdevsw,
97         VINUM_DAEMON_DEV,
98         UID_ROOT,
99         GID_WHEEL,
100         S_IRUSR | S_IWUSR,
101         "vinum/controld");
102     vinum_super_dev = make_dev(&vinum_cdevsw,
103         VINUM_SUPERDEV,
104         UID_ROOT,
105         GID_WHEEL,
106         S_IRUSR | S_IWUSR,
107         "vinum/control");
108
109     vinum_conf.version = VINUMVERSION;                      /* note what version we are */
110
111     /* allocate space: drives... */
112     DRIVE = (struct drive *) Malloc(sizeof(struct drive) * INITIAL_DRIVES);
113     CHECKALLOC(DRIVE, "vinum: no memory\n");
114     bzero(DRIVE, sizeof(struct drive) * INITIAL_DRIVES);
115     vinum_conf.drives_allocated = INITIAL_DRIVES;           /* number of drive slots allocated */
116     vinum_conf.drives_used = 0;                             /* and number in use */
117
118     /* volumes, ... */
119     VOL = (struct volume *) Malloc(sizeof(struct volume) * INITIAL_VOLUMES);
120     CHECKALLOC(VOL, "vinum: no memory\n");
121     bzero(VOL, sizeof(struct volume) * INITIAL_VOLUMES);
122     vinum_conf.volumes_allocated = INITIAL_VOLUMES;         /* number of volume slots allocated */
123     vinum_conf.volumes_used = 0;                            /* and number in use */
124
125     /* plexes, ... */
126     PLEX = (struct plex *) Malloc(sizeof(struct plex) * INITIAL_PLEXES);
127     CHECKALLOC(PLEX, "vinum: no memory\n");
128     bzero(PLEX, sizeof(struct plex) * INITIAL_PLEXES);
129     vinum_conf.plexes_allocated = INITIAL_PLEXES;           /* number of plex slots allocated */
130     vinum_conf.plexes_used = 0;                             /* and number in use */
131
132     /* and subdisks */
133     SD = (struct sd *) Malloc(sizeof(struct sd) * INITIAL_SUBDISKS);
134     CHECKALLOC(SD, "vinum: no memory\n");
135     bzero(SD, sizeof(struct sd) * INITIAL_SUBDISKS);
136     vinum_conf.subdisks_allocated = INITIAL_SUBDISKS;       /* number of sd slots allocated */
137     vinum_conf.subdisks_used = 0;                           /* and number in use */
138 }
139
140 /*
141  * Check if we have anything open.  If confopen is != 0,
142  * that goes for the super device as well, otherwise
143  * only for volumes.
144  *
145  * Return 0 if not inactive, 1 if inactive.
146  */
147 int
148 vinum_inactive(int confopen)
149 {
150     int i;
151     int can_do = 1;                                         /* assume we can do it */
152
153     if (confopen && (vinum_conf.flags & VF_OPEN))           /* open by vinum(8)? */
154         return 0;                                           /* can't do it while we're open */
155     lock_config();
156     for (i = 0; i < vinum_conf.volumes_allocated; i++) {
157         if ((VOL[i].state > volume_down)
158             && (VOL[i].flags & VF_OPEN)) {                  /* volume is open */
159             can_do = 0;
160             break;
161         }
162     }
163     unlock_config();
164     return can_do;
165 }
166
167 /*
168  * Free all structures.
169  * If cleardrive is 0, save the configuration; otherwise
170  * remove the configuration from the drive.
171  *
172  * Before coming here, ensure that no volumes are open.
173  */
174 void
175 free_vinum(int cleardrive)
176 {
177     int i;
178     int drives_allocated = vinum_conf.drives_allocated;
179
180     if (DRIVE != NULL) {
181         if (cleardrive) {                                   /* remove the vinum config */
182             for (i = 0; i < drives_allocated; i++)
183                 remove_drive(i);                            /* remove the drive */
184         } else {                                            /* keep the config */
185             for (i = 0; i < drives_allocated; i++)
186                 free_drive(&DRIVE[i]);                      /* close files and things */
187         }
188         Free(DRIVE);
189     }
190     while ((vinum_conf.flags & (VF_STOPPING | VF_DAEMONOPEN))
191         == (VF_STOPPING | VF_DAEMONOPEN)) {                 /* at least one daemon open, we're stopping */
192         queue_daemon_request(daemonrq_return, (union daemoninfo) 0); /* stop the daemon */
193         tsleep(&vinumclose, PUSER, "vstop", 1);             /* and wait for it */
194     }
195     if (SD != NULL) {
196         for (i = 0; i < vinum_conf.subdisks_allocated; i++) {
197             struct sd *sd = &SD[i];
198
199             if (sd->state != sd_unallocated)
200                 free_sd(i);
201         }
202         Free(SD);
203     }
204     if (PLEX != NULL) {
205         for (i = 0; i < vinum_conf.plexes_allocated; i++) {
206             struct plex *plex = &PLEX[i];
207
208             if (plex->state != plex_unallocated)            /* we have real data there */
209                 free_plex(i);
210         }
211         Free(PLEX);
212     }
213     if (VOL != NULL) {
214         for (i = 0; i < vinum_conf.volumes_allocated; i++) {
215             struct volume *volume = &VOL[i];
216
217             if (volume->state != volume_unallocated)
218                 free_volume(i);
219         }
220         Free(VOL);
221     }
222     bzero(&vinum_conf, sizeof(vinum_conf));
223 }
224
225 STATIC int
226 vinum_modevent(module_t mod, modeventtype_t type, void *unused)
227 {
228     struct sync_args dummyarg =
229     {0};
230
231     switch (type) {
232     case MOD_LOAD:
233         vinumattach(NULL);
234         return 0;                                           /* OK */
235     case MOD_UNLOAD:
236         if (!vinum_inactive(1))                             /* is anything open? */
237             return EBUSY;                                   /* yes, we can't do it */
238         vinum_conf.flags |= VF_STOPPING;                    /* note that we want to stop */
239         sync(curthread, &dummyarg);                         /* write out buffers */
240         free_vinum(0);                                      /* clean up */
241 #ifdef VINUMDEBUG
242         if (total_malloced) {
243             int i;
244 #ifdef INVARIANTS
245             int *poke;
246 #endif
247
248             for (i = 0; i < malloccount; i++) {
249                 if (debug & DEBUG_WARNINGS)                 /* want to hear about them */
250                     log(LOG_WARNING,
251                         "vinum: exiting with %d bytes malloced from %s:%d\n",
252                         malloced[i].size,
253                         malloced[i].file,
254                         malloced[i].line);
255 #ifdef INVARIANTS
256                 poke = &((int *) malloced[i].address)
257                     [malloced[i].size / (2 * sizeof(int))]; /* middle of the area */
258                 if (*poke == 0xdeadc0de)                    /* already freed */
259                     log(LOG_ERR,
260                         "vinum: exiting with malloc table inconsistency at %p from %s:%d\n",
261                         malloced[i].address,
262                         malloced[i].file,
263                         malloced[i].line);
264 #endif
265                 Free(malloced[i].address);
266             }
267         }
268 #endif
269         destroy_dev(vinum_daemon_dev);                      /* daemon device */
270         destroy_dev(vinum_super_dev);
271         log(LOG_INFO, "vinum: unloaded\n");                 /* tell the world */
272         return 0;
273     default:
274         break;
275     }
276     return 0;
277 }
278
279 static moduledata_t vinum_mod =
280 {
281     "vinum",
282     (modeventhand_t) vinum_modevent,
283     0
284 };
285 DECLARE_MODULE(vinum, vinum_mod, SI_SUB_VINUM, SI_ORDER_MIDDLE);
286
287 /* ARGSUSED */
288 /* Open a vinum object */
289 int
290 vinumopen(dev_t dev,
291     int flags,
292     int fmt,
293     struct thread *td)
294 {
295     int error;
296     unsigned int index;
297     struct volume *vol;
298     struct plex *plex;
299     struct sd *sd;
300     int devminor;                                           /* minor number */
301
302     devminor = minor(dev);
303     error = 0;
304     /* First, decide what we're looking at */
305     switch (DEVTYPE(dev)) {
306     case VINUM_VOLUME_TYPE:
307         index = Volno(dev);
308         if (index >= vinum_conf.volumes_allocated)
309             return ENXIO;                                   /* no such device */
310         vol = &VOL[index];
311
312         switch (vol->state) {
313         case volume_unallocated:
314         case volume_uninit:
315             return ENXIO;
316
317         case volume_up:
318             vol->flags |= VF_OPEN;                          /* note we're open */
319             return 0;
320
321         case volume_down:
322             return EIO;
323
324         default:
325             return EINVAL;
326         }
327
328     case VINUM_PLEX_TYPE:
329         if (Volno(dev) >= vinum_conf.volumes_allocated)
330             return ENXIO;
331         /* FALLTHROUGH */
332
333     case VINUM_RAWPLEX_TYPE:
334         index = Plexno(dev);                                /* get plex index in vinum_conf */
335         if (index >= vinum_conf.plexes_allocated)
336             return ENXIO;                                   /* no such device */
337         plex = &PLEX[index];
338
339         switch (plex->state) {
340         case plex_referenced:
341         case plex_unallocated:
342             return EINVAL;
343
344         default:
345             plex->flags |= VF_OPEN;                         /* note we're open */
346             return 0;
347         }
348
349     case VINUM_SD_TYPE:
350         if ((Volno(dev) >= vinum_conf.volumes_allocated)    /* no such volume */
351         ||(Plexno(dev) >= vinum_conf.plexes_allocated))     /* or no such plex */
352             return ENXIO;                                   /* no such device */
353
354         /* FALLTHROUGH */
355
356     case VINUM_RAWSD_TYPE:
357         index = Sdno(dev);                                  /* get the subdisk number */
358         if ((index >= vinum_conf.subdisks_allocated)        /* not a valid SD entry */
359         ||(SD[index].state < sd_init))                      /* or SD is not real */
360             return ENXIO;                                   /* no such device */
361         sd = &SD[index];
362
363         /*
364          * Opening a subdisk is always a special operation, so we
365          * ignore the state as long as it represents a real subdisk
366          */
367         switch (sd->state) {
368         case sd_unallocated:
369         case sd_uninit:
370             return EINVAL;
371
372         default:
373             sd->flags |= VF_OPEN;                           /* note we're open */
374             return 0;
375         }
376
377     case VINUM_SUPERDEV_TYPE:
378         error = suser(td);                                  /* are we root? */
379         if (error == 0) {                                   /* yes, can do */
380             if (devminor == VINUM_DAEMON_DEV)               /* daemon device */
381                 vinum_conf.flags |= VF_DAEMONOPEN;          /* we're open */
382             else if (devminor == VINUM_SUPERDEV)
383                 vinum_conf.flags |= VF_OPEN;                /* we're open */
384             else
385                 error = ENODEV;                             /* nothing, maybe a debug mismatch */
386         }
387         return error;
388
389         /* Vinum drives are disks.  We already have a disk
390          * driver, so don't handle them here */
391     case VINUM_DRIVE_TYPE:
392     default:
393         return ENODEV;                                      /* don't know what to do with these */
394     }
395 }
396
397 /* ARGSUSED */
398 int
399 vinumclose(dev_t dev,
400     int flags,
401     int fmt,
402     struct thread *td)
403 {
404     unsigned int index;
405     struct volume *vol;
406     int devminor;
407
408     devminor = minor(dev);
409     index = Volno(dev);
410     /* First, decide what we're looking at */
411     switch (DEVTYPE(dev)) {
412     case VINUM_VOLUME_TYPE:
413         if (index >= vinum_conf.volumes_allocated)
414             return ENXIO;                                   /* no such device */
415         vol = &VOL[index];
416
417         switch (vol->state) {
418         case volume_unallocated:
419         case volume_uninit:
420             return ENXIO;
421
422         case volume_up:
423             vol->flags &= ~VF_OPEN;                         /* reset our flags */
424             return 0;
425
426         case volume_down:
427             return EIO;
428
429         default:
430             return EINVAL;
431         }
432
433     case VINUM_PLEX_TYPE:
434         if (Volno(dev) >= vinum_conf.volumes_allocated)
435             return ENXIO;
436         /* FALLTHROUGH */
437
438     case VINUM_RAWPLEX_TYPE:
439         index = Plexno(dev);                                /* get plex index in vinum_conf */
440         if (index >= vinum_conf.plexes_allocated)
441             return ENXIO;                                   /* no such device */
442         PLEX[index].flags &= ~VF_OPEN;                      /* reset our flags */
443         return 0;
444
445     case VINUM_SD_TYPE:
446         if ((Volno(dev) >= vinum_conf.volumes_allocated) || /* no such volume */
447             (Plexno(dev) >= vinum_conf.plexes_allocated))   /* or no such plex */
448             return ENXIO;                                   /* no such device */
449         /* FALLTHROUGH */
450
451     case VINUM_RAWSD_TYPE:
452         index = Sdno(dev);                                  /* get the subdisk number */
453         if (index >= vinum_conf.subdisks_allocated)
454             return ENXIO;                                   /* no such device */
455         SD[index].flags &= ~VF_OPEN;                        /* reset our flags */
456         return 0;
457
458     case VINUM_SUPERDEV_TYPE:
459         /*
460          * don't worry about whether we're root:
461          * nobody else would get this far.
462          */
463         if (devminor == VINUM_SUPERDEV)                     /* normal superdev */
464             vinum_conf.flags &= ~VF_OPEN;                   /* no longer open */
465         else if (devminor == VINUM_DAEMON_DEV) {            /* the daemon device */
466             vinum_conf.flags &= ~VF_DAEMONOPEN;             /* no longer open */
467             if (vinum_conf.flags & VF_STOPPING)             /* we're stopping, */
468                 wakeup(&vinumclose);                        /* we can continue stopping now */
469         }
470         return 0;
471
472     case VINUM_DRIVE_TYPE:
473     default:
474         return ENODEV;                                      /* don't know what to do with these */
475     }
476 }
477
478 /* size routine */
479 int
480 vinumsize(dev_t dev)
481 {
482     struct volume *vol;
483     int size;
484
485     vol = &VOL[Volno(dev)];
486
487     if (vol->state == volume_up)
488         size = vol->size;
489     else
490         return 0;                                           /* err on the size of conservatism */
491
492     return size;
493 }
494
495 /* Local Variables: */
496 /* fill-column: 50 */
497 /* End: */