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 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 = priv_check_cred(ap->a_cred, PRIV_ROOT, 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 = (int64_t)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: */ 522