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 STATIC void vinum_initconf(void); 76 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; 81 82 /* 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 85 * add devsw entries. 86 */ 87 void 88 vinumattach(void *dummy) 89 { 90 char *cp, *cp1, *cp2, **drives; 91 int i, rv; 92 struct volume *vol; 93 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"); 97 98 log(LOG_INFO, "vinum: loaded\n"); 99 vinum_conf.flags |= VF_LOADED; /* we're loaded now */ 100 101 daemonq = NULL; /* initialize daemon's work queue */ 102 dqend = NULL; 103 104 #if 0 105 dev_ops_add(&vinum_ops, 0, 0); 106 #endif 107 108 vinum_initconf(); 109 110 /* 111 * Create superdev, wrongsuperdev, and controld devices. 112 */ 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); 122 123 /* 124 * See if the loader has passed us a disk to 125 * read the initial configuration from. 126 */ 127 if ((cp = kgetenv("vinum.drives")) != NULL) { 128 for (cp1 = cp, i = 0, drives = 0; *cp1 != '\0'; i++) { 129 cp2 = cp1; 130 while (*cp1 != '\0' && *cp1 != ',' && *cp1 != ' ') 131 cp1++; 132 if (*cp1 != '\0') 133 *cp1++ = '\0'; 134 drives = krealloc(drives, (unsigned long)((i + 1) * sizeof(char *)), 135 M_TEMP, M_WAITOK); 136 drives[i] = cp2; 137 } 138 if (i == 0) 139 goto bailout; 140 rv = vinum_scandisk(drives, i); 141 if (rv) 142 log(LOG_NOTICE, "vinum_scandisk() returned %d", rv); 143 bailout: 144 kfree(drives, M_TEMP); 145 } 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) 151 ) { 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); 155 break; 156 } 157 } 158 } 159 } 160 161 /* 162 * Check if we have anything open. If confopen is != 0, 163 * that goes for the super device as well, otherwise 164 * only for volumes. 165 * 166 * Return 0 if not inactive, 1 if inactive. 167 */ 168 int 169 vinum_inactive(int confopen) 170 { 171 int i; 172 int can_do = 1; /* assume we can do it */ 173 174 if (confopen && (vinum_conf.flags & VF_OPEN)) /* open by vinum(8)? */ 175 return 0; /* can't do it while we're open */ 176 lock_config(); 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 */ 180 can_do = 0; 181 break; 182 } 183 } 184 unlock_config(); 185 return can_do; 186 } 187 188 /* 189 * Free all structures. 190 * If cleardrive is 0, save the configuration; otherwise 191 * remove the configuration from the drive. 192 * 193 * Before coming here, ensure that no volumes are open. 194 */ 195 void 196 free_vinum(int cleardrive) 197 { 198 int i; 199 int drives_allocated = vinum_conf.drives_allocated; 200 201 if (DRIVE != NULL) { 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 */ 208 } 209 Free(DRIVE); 210 } 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 */ 215 } 216 if (SD != NULL) { 217 for (i = 0; i < vinum_conf.subdisks_allocated; i++) { 218 struct sd *sd = &vinum_conf.sd[i]; 219 if (sd->sd_dev) { 220 destroy_dev(sd->sd_dev); 221 sd->sd_dev = NULL; 222 } 223 } 224 Free(SD); 225 } 226 if (PLEX != NULL) { 227 for (i = 0; i < vinum_conf.plexes_allocated; i++) { 228 struct plex *plex = &vinum_conf.plex[i]; 229 230 if (plex->plex_dev) { 231 destroy_dev(plex->plex_dev); 232 plex->plex_dev = NULL; 233 } 234 235 if (plex->state != plex_unallocated) { /* we have real data there */ 236 if (plex->sdnos) 237 Free(plex->sdnos); 238 } 239 } 240 Free(PLEX); 241 } 242 if (VOL != NULL) { 243 for (i = 0; i < vinum_conf.volumes_allocated; i++) { 244 struct volume *vol = &vinum_conf.volume[i]; 245 246 if (vol->vol_dev) { 247 destroy_dev(vol->vol_dev); 248 vol->vol_dev = NULL; 249 } 250 } 251 Free(VOL); 252 } 253 bzero(&vinum_conf, sizeof(vinum_conf)); 254 vinum_initconf(); 255 } 256 257 STATIC void 258 vinum_initconf(void) 259 { 260 vinum_conf.physbufs = nswbuf / 2 + 1; 261 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; 268 269 /* volumes, ... */ 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; 275 276 /* plexes, ... */ 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; 282 283 /* and subdisks */ 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; 289 } 290 291 STATIC int 292 vinum_modevent(module_t mod, modeventtype_t type, void *unused) 293 { 294 switch (type) { 295 case MOD_LOAD: 296 vinumattach(NULL); 297 return 0; /* OK */ 298 case MOD_UNLOAD: 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 */ 304 305 if (vinum_super_dev) { 306 destroy_dev(vinum_super_dev); 307 vinum_super_dev = NULL; 308 } 309 if (vinum_wsuper_dev) { 310 destroy_dev(vinum_wsuper_dev); 311 vinum_wsuper_dev = NULL; 312 } 313 if (vinum_daemon_dev) { 314 destroy_dev(vinum_daemon_dev); 315 vinum_daemon_dev = NULL; 316 } 317 318 sync_devs(); 319 #ifdef VINUMDEBUG 320 if (total_malloced) { 321 int i; 322 #ifdef INVARIANTS 323 int *poke; 324 #endif 325 326 for (i = 0; i < malloccount; i++) { 327 if (debug & DEBUG_WARNINGS) /* want to hear about them */ 328 log(LOG_WARNING, 329 "vinum: exiting with %d bytes malloced from %s:%d\n", 330 malloced[i].size, 331 malloced[i].file, 332 malloced[i].line); 333 #ifdef INVARIANTS 334 poke = &((int *) malloced[i].address) 335 [malloced[i].size / (2 * sizeof(int))]; /* middle of the area */ 336 if (*poke == 0xdeadc0de) /* already freed */ 337 log(LOG_ERR, 338 "vinum: exiting with malloc table inconsistency at %p from %s:%d\n", 339 malloced[i].address, 340 malloced[i].file, 341 malloced[i].line); 342 #endif 343 Free(malloced[i].address); 344 } 345 } 346 #endif 347 dev_ops_remove_all(&vinum_ops); 348 log(LOG_INFO, "vinum: unloaded\n"); /* tell the world */ 349 return 0; 350 default: 351 break; 352 } 353 return 0; 354 } 355 356 moduledata_t vinum_mod = 357 { 358 "vinum", 359 (modeventhand_t) vinum_modevent, 360 0 361 }; 362 DECLARE_MODULE(vinum, vinum_mod, SI_SUB_RAID, SI_ORDER_MIDDLE); 363 364 /* ARGSUSED */ 365 /* Open a vinum object */ 366 int 367 vinumopen(struct dev_open_args *ap) 368 { 369 cdev_t dev = ap->a_head.a_dev; 370 int error; 371 unsigned int index; 372 struct volume *vol; 373 struct plex *plex; 374 struct sd *sd; 375 int devminor; /* minor number */ 376 377 devminor = minor(dev); 378 error = 0; 379 /* First, decide what we're looking at */ 380 switch (DEVTYPE(dev)) { 381 case VINUM_VOLUME_TYPE: 382 index = Volno(dev); 383 if (index >= vinum_conf.volumes_allocated) 384 return ENXIO; /* no such device */ 385 vol = &VOL[index]; 386 387 switch (vol->state) { 388 case volume_unallocated: 389 case volume_uninit: 390 return ENXIO; 391 392 case volume_up: 393 vol->flags |= VF_OPEN; /* note we're open */ 394 return 0; 395 396 case volume_down: 397 return EIO; 398 399 default: 400 return EINVAL; 401 } 402 403 case VINUM_PLEX_TYPE: 404 if (Volno(dev) >= vinum_conf.volumes_allocated) 405 return ENXIO; 406 /* FALLTHROUGH */ 407 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 */ 412 plex = &PLEX[index]; 413 414 switch (plex->state) { 415 case plex_referenced: 416 case plex_unallocated: 417 return EINVAL; 418 419 default: 420 plex->flags |= VF_OPEN; /* note we're open */ 421 return 0; 422 } 423 424 case VINUM_SD_TYPE: 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 */ 428 429 /* FALLTHROUGH */ 430 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 */ 436 sd = &SD[index]; 437 438 /* 439 * Opening a subdisk is always a special operation, so we 440 * ignore the state as long as it represents a real subdisk 441 */ 442 switch (sd->state) { 443 case sd_unallocated: 444 case sd_uninit: 445 return EINVAL; 446 447 default: 448 sd->flags |= VF_OPEN; /* note we're open */ 449 return 0; 450 } 451 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 */ 459 else 460 error = ENODEV; /* nothing, maybe a debug mismatch */ 461 } 462 return error; 463 464 /* Vinum drives are disks. We already have a disk 465 * driver, so don't handle them here */ 466 case VINUM_DRIVE_TYPE: 467 default: 468 return ENODEV; /* don't know what to do with these */ 469 } 470 } 471 472 /* ARGSUSED */ 473 int 474 vinumclose(struct dev_close_args *ap) 475 { 476 cdev_t dev = ap->a_head.a_dev; 477 unsigned int index; 478 struct volume *vol; 479 int devminor; 480 481 devminor = minor(dev); 482 index = Volno(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 */ 488 vol = &VOL[index]; 489 490 switch (vol->state) { 491 case volume_unallocated: 492 case volume_uninit: 493 return ENXIO; 494 495 case volume_up: 496 vol->flags &= ~VF_OPEN; /* reset our flags */ 497 return 0; 498 499 case volume_down: 500 return EIO; 501 502 default: 503 return EINVAL; 504 } 505 506 case VINUM_PLEX_TYPE: 507 if (Volno(dev) >= vinum_conf.volumes_allocated) 508 return ENXIO; 509 /* FALLTHROUGH */ 510 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 */ 516 return 0; 517 518 case VINUM_SD_TYPE: 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 */ 522 /* FALLTHROUGH */ 523 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 */ 529 return 0; 530 531 case VINUM_SUPERDEV_TYPE: 532 /* 533 * don't worry about whether we're root: 534 * nobody else would get this far. 535 */ 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 */ 542 } 543 return 0; 544 545 case VINUM_DRIVE_TYPE: 546 default: 547 return ENODEV; /* don't know what to do with these */ 548 } 549 } 550 551 /* size routine */ 552 int 553 vinumsize(struct dev_psize_args *ap) 554 { 555 cdev_t dev = ap->a_head.a_dev; 556 struct volume *vol; 557 558 vol = &VOL[Volno(dev)]; 559 560 if (vol->state == volume_up) { 561 ap->a_result = (int64_t)vol->size; 562 return(0); 563 } else { 564 return(ENXIO); 565 } 566 } 567 568 int 569 vinumdump(struct dev_dump_args *ap) 570 { 571 /* Not implemented. */ 572 return ENXIO; 573 } 574 575 int 576 vinumpoll(struct dev_poll_args *ap) 577 { 578 ap->a_events = seltrue(ap->a_head.a_dev, ap->a_events); 579 return(0); 580 } 581 582 /* Local Variables: */ 583 /* fill-column: 50 */ 584 /* End: */ 585