1 /* $NetBSD: ld.c,v 1.29 2004/08/30 00:34:41 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Andrew Doran and Charles M. Hannum. 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 the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Disk driver for use by RAID controllers. 41 */ 42 43 #include <sys/cdefs.h> 44 __KERNEL_RCSID(0, "$NetBSD: ld.c,v 1.29 2004/08/30 00:34:41 thorpej Exp $"); 45 46 #include "rnd.h" 47 48 #include <sys/param.h> 49 #include <sys/systm.h> 50 #include <sys/kernel.h> 51 #include <sys/device.h> 52 #include <sys/queue.h> 53 #include <sys/proc.h> 54 #include <sys/buf.h> 55 #include <sys/endian.h> 56 #include <sys/disklabel.h> 57 #include <sys/disk.h> 58 #include <sys/dkio.h> 59 #include <sys/stat.h> 60 #include <sys/lock.h> 61 #include <sys/conf.h> 62 #include <sys/fcntl.h> 63 #include <sys/vnode.h> 64 #include <sys/syslog.h> 65 #if NRND > 0 66 #include <sys/rnd.h> 67 #endif 68 69 #include <dev/ldvar.h> 70 71 static void ldgetdefaultlabel(struct ld_softc *, struct disklabel *); 72 static void ldgetdisklabel(struct ld_softc *); 73 static int ldlock(struct ld_softc *); 74 static void ldminphys(struct buf *bp); 75 static void ldshutdown(void *); 76 static void ldstart(struct ld_softc *); 77 static void ldunlock(struct ld_softc *); 78 79 extern struct cfdriver ld_cd; 80 81 static dev_type_open(ldopen); 82 static dev_type_close(ldclose); 83 static dev_type_read(ldread); 84 static dev_type_write(ldwrite); 85 static dev_type_ioctl(ldioctl); 86 static dev_type_strategy(ldstrategy); 87 static dev_type_dump(lddump); 88 static dev_type_size(ldsize); 89 90 const struct bdevsw ld_bdevsw = { 91 ldopen, ldclose, ldstrategy, ldioctl, lddump, ldsize, D_DISK 92 }; 93 94 const struct cdevsw ld_cdevsw = { 95 ldopen, ldclose, ldread, ldwrite, ldioctl, 96 nostop, notty, nopoll, nommap, nokqfilter, D_DISK 97 }; 98 99 static struct dkdriver lddkdriver = { ldstrategy }; 100 static void *ld_sdh; 101 102 void 103 ldattach(struct ld_softc *sc) 104 { 105 char buf[9]; 106 107 if ((sc->sc_flags & LDF_ENABLED) == 0) { 108 printf("%s: disabled\n", sc->sc_dv.dv_xname); 109 return; 110 } 111 112 /* Initialise and attach the disk structure. */ 113 sc->sc_dk.dk_driver = &lddkdriver; 114 sc->sc_dk.dk_name = sc->sc_dv.dv_xname; 115 disk_attach(&sc->sc_dk); 116 117 if (sc->sc_maxxfer > MAXPHYS) 118 sc->sc_maxxfer = MAXPHYS; 119 120 /* Build synthetic geometry if necessary. */ 121 if (sc->sc_nheads == 0 || sc->sc_nsectors == 0 || 122 sc->sc_ncylinders == 0) { 123 uint64_t ncyl; 124 125 if (sc->sc_secperunit <= 528 * 2048) /* 528MB */ 126 sc->sc_nheads = 16; 127 else if (sc->sc_secperunit <= 1024 * 2048) /* 1GB */ 128 sc->sc_nheads = 32; 129 else if (sc->sc_secperunit <= 21504 * 2048) /* 21GB */ 130 sc->sc_nheads = 64; 131 else if (sc->sc_secperunit <= 43008 * 2048) /* 42GB */ 132 sc->sc_nheads = 128; 133 else 134 sc->sc_nheads = 255; 135 136 sc->sc_nsectors = 63; 137 sc->sc_ncylinders = INT_MAX; 138 ncyl = sc->sc_secperunit / 139 (sc->sc_nheads * sc->sc_nsectors); 140 if (ncyl < INT_MAX) 141 sc->sc_ncylinders = (int)ncyl; 142 } 143 144 format_bytes(buf, sizeof(buf), sc->sc_secperunit * 145 sc->sc_secsize); 146 printf("%s: %s, %d cyl, %d head, %d sec, %d bytes/sect x %"PRIu64" sectors\n", 147 sc->sc_dv.dv_xname, buf, sc->sc_ncylinders, sc->sc_nheads, 148 sc->sc_nsectors, sc->sc_secsize, sc->sc_secperunit); 149 150 #if NRND > 0 151 /* Attach the device into the rnd source list. */ 152 rnd_attach_source(&sc->sc_rnd_source, sc->sc_dv.dv_xname, 153 RND_TYPE_DISK, 0); 154 #endif 155 156 /* Set the `shutdownhook'. */ 157 if (ld_sdh == NULL) 158 ld_sdh = shutdownhook_establish(ldshutdown, NULL); 159 bufq_alloc(&sc->sc_bufq, BUFQ_DISK_DEFAULT_STRAT()|BUFQ_SORT_RAWBLOCK); 160 } 161 162 int 163 ldadjqparam(struct ld_softc *sc, int max) 164 { 165 int s; 166 167 s = splbio(); 168 sc->sc_maxqueuecnt = max; 169 splx(s); 170 171 return (0); 172 } 173 174 int 175 ldbegindetach(struct ld_softc *sc, int flags) 176 { 177 int s, rv = 0; 178 179 if ((sc->sc_flags & LDF_ENABLED) == 0) 180 return (0); 181 182 if ((flags & DETACH_FORCE) == 0 && sc->sc_dk.dk_openmask != 0) 183 return (EBUSY); 184 185 s = splbio(); 186 sc->sc_maxqueuecnt = 0; 187 sc->sc_flags |= LDF_DETACH; 188 while (sc->sc_queuecnt > 0) { 189 sc->sc_flags |= LDF_DRAIN; 190 rv = tsleep(&sc->sc_queuecnt, PRIBIO, "lddrn", 0); 191 if (rv) 192 break; 193 } 194 splx(s); 195 196 return (rv); 197 } 198 199 void 200 ldenddetach(struct ld_softc *sc) 201 { 202 struct buf *bp; 203 int s, bmaj, cmaj, i, mn; 204 205 if ((sc->sc_flags & LDF_ENABLED) == 0) 206 return; 207 208 /* Wait for commands queued with the hardware to complete. */ 209 if (sc->sc_queuecnt != 0) 210 if (tsleep(&sc->sc_queuecnt, PRIBIO, "lddtch", 30 * hz)) 211 printf("%s: not drained\n", sc->sc_dv.dv_xname); 212 213 /* Locate the major numbers. */ 214 bmaj = bdevsw_lookup_major(&ld_bdevsw); 215 cmaj = cdevsw_lookup_major(&ld_cdevsw); 216 217 /* Kill off any queued buffers. */ 218 s = splbio(); 219 while ((bp = BUFQ_GET(&sc->sc_bufq)) != NULL) { 220 bp->b_error = EIO; 221 bp->b_flags |= B_ERROR; 222 bp->b_resid = bp->b_bcount; 223 biodone(bp); 224 } 225 bufq_free(&sc->sc_bufq); 226 splx(s); 227 228 /* Nuke the vnodes for any open instances. */ 229 for (i = 0; i < MAXPARTITIONS; i++) { 230 mn = DISKMINOR(sc->sc_dv.dv_unit, i); 231 vdevgone(bmaj, mn, mn, VBLK); 232 vdevgone(cmaj, mn, mn, VCHR); 233 } 234 235 /* Detach from the disk list. */ 236 disk_detach(&sc->sc_dk); 237 238 #if NRND > 0 239 /* Unhook the entropy source. */ 240 rnd_detach_source(&sc->sc_rnd_source); 241 #endif 242 243 /* 244 * XXX We can't really flush the cache here, beceause the 245 * XXX device may already be non-existent from the controller's 246 * XXX perspective. 247 */ 248 #if 0 249 /* Flush the device's cache. */ 250 if (sc->sc_flush != NULL) 251 if ((*sc->sc_flush)(sc) != 0) 252 printf("%s: unable to flush cache\n", 253 sc->sc_dv.dv_xname); 254 #endif 255 } 256 257 /* ARGSUSED */ 258 static void 259 ldshutdown(void *cookie) 260 { 261 struct ld_softc *sc; 262 int i; 263 264 for (i = 0; i < ld_cd.cd_ndevs; i++) { 265 if ((sc = device_lookup(&ld_cd, i)) == NULL) 266 continue; 267 if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0) 268 printf("%s: unable to flush cache\n", 269 sc->sc_dv.dv_xname); 270 } 271 } 272 273 /* ARGSUSED */ 274 static int 275 ldopen(dev_t dev, int flags, int fmt, struct proc *p) 276 { 277 struct ld_softc *sc; 278 int unit, part; 279 280 unit = DISKUNIT(dev); 281 if ((sc = device_lookup(&ld_cd, unit))== NULL) 282 return (ENXIO); 283 if ((sc->sc_flags & LDF_ENABLED) == 0) 284 return (ENODEV); 285 part = DISKPART(dev); 286 ldlock(sc); 287 288 if (sc->sc_dk.dk_openmask == 0) { 289 /* Load the partition info if not already loaded. */ 290 if ((sc->sc_flags & LDF_VLABEL) == 0) 291 ldgetdisklabel(sc); 292 } 293 294 /* Check that the partition exists. */ 295 if (part != RAW_PART && (part >= sc->sc_dk.dk_label->d_npartitions || 296 sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) { 297 ldunlock(sc); 298 return (ENXIO); 299 } 300 301 /* Ensure only one open at a time. */ 302 switch (fmt) { 303 case S_IFCHR: 304 sc->sc_dk.dk_copenmask |= (1 << part); 305 break; 306 case S_IFBLK: 307 sc->sc_dk.dk_bopenmask |= (1 << part); 308 break; 309 } 310 sc->sc_dk.dk_openmask = 311 sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; 312 313 ldunlock(sc); 314 return (0); 315 } 316 317 /* ARGSUSED */ 318 static int 319 ldclose(dev_t dev, int flags, int fmt, struct proc *p) 320 { 321 struct ld_softc *sc; 322 int part, unit; 323 324 unit = DISKUNIT(dev); 325 part = DISKPART(dev); 326 sc = device_lookup(&ld_cd, unit); 327 ldlock(sc); 328 329 switch (fmt) { 330 case S_IFCHR: 331 sc->sc_dk.dk_copenmask &= ~(1 << part); 332 break; 333 case S_IFBLK: 334 sc->sc_dk.dk_bopenmask &= ~(1 << part); 335 break; 336 } 337 sc->sc_dk.dk_openmask = 338 sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; 339 340 if (sc->sc_dk.dk_openmask == 0) { 341 if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0) 342 printf("%s: unable to flush cache\n", 343 sc->sc_dv.dv_xname); 344 if ((sc->sc_flags & LDF_KLABEL) == 0) 345 sc->sc_flags &= ~LDF_VLABEL; 346 } 347 348 ldunlock(sc); 349 return (0); 350 } 351 352 /* ARGSUSED */ 353 static int 354 ldread(dev_t dev, struct uio *uio, int ioflag) 355 { 356 357 return (physio(ldstrategy, NULL, dev, B_READ, ldminphys, uio)); 358 } 359 360 /* ARGSUSED */ 361 static int 362 ldwrite(dev_t dev, struct uio *uio, int ioflag) 363 { 364 365 return (physio(ldstrategy, NULL, dev, B_WRITE, ldminphys, uio)); 366 } 367 368 /* ARGSUSED */ 369 static int 370 ldioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p) 371 { 372 struct ld_softc *sc; 373 int part, unit, error; 374 #ifdef __HAVE_OLD_DISKLABEL 375 struct disklabel newlabel; 376 #endif 377 struct disklabel *lp; 378 379 unit = DISKUNIT(dev); 380 part = DISKPART(dev); 381 sc = device_lookup(&ld_cd, unit); 382 error = 0; 383 384 switch (cmd) { 385 case DIOCGDINFO: 386 memcpy(addr, sc->sc_dk.dk_label, sizeof(struct disklabel)); 387 return (0); 388 389 #ifdef __HAVE_OLD_DISKLABEL 390 case ODIOCGDINFO: 391 newlabel = *(sc->sc_dk.dk_label); 392 if (newlabel.d_npartitions > OLDMAXPARTITIONS) 393 return ENOTTY; 394 memcpy(addr, &newlabel, sizeof(struct olddisklabel)); 395 return (0); 396 #endif 397 398 case DIOCGPART: 399 ((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label; 400 ((struct partinfo *)addr)->part = 401 &sc->sc_dk.dk_label->d_partitions[part]; 402 break; 403 404 case DIOCWDINFO: 405 case DIOCSDINFO: 406 #ifdef __HAVE_OLD_DISKLABEL 407 case ODIOCWDINFO: 408 case ODIOCSDINFO: 409 410 if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) { 411 memset(&newlabel, 0, sizeof newlabel); 412 memcpy(&newlabel, addr, sizeof (struct olddisklabel)); 413 lp = &newlabel; 414 } else 415 #endif 416 lp = (struct disklabel *)addr; 417 418 if ((flag & FWRITE) == 0) 419 return (EBADF); 420 421 if ((error = ldlock(sc)) != 0) 422 return (error); 423 sc->sc_flags |= LDF_LABELLING; 424 425 error = setdisklabel(sc->sc_dk.dk_label, 426 lp, /*sc->sc_dk.dk_openmask : */0, 427 sc->sc_dk.dk_cpulabel); 428 if (error == 0 && (cmd == DIOCWDINFO 429 #ifdef __HAVE_OLD_DISKLABEL 430 || cmd == ODIOCWDINFO 431 #endif 432 )) 433 error = writedisklabel( 434 MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART), 435 ldstrategy, sc->sc_dk.dk_label, 436 sc->sc_dk.dk_cpulabel); 437 438 sc->sc_flags &= ~LDF_LABELLING; 439 ldunlock(sc); 440 break; 441 442 case DIOCKLABEL: 443 if ((flag & FWRITE) == 0) 444 return (EBADF); 445 if (*(int *)addr) 446 sc->sc_flags |= LDF_KLABEL; 447 else 448 sc->sc_flags &= ~LDF_KLABEL; 449 break; 450 451 case DIOCWLABEL: 452 if ((flag & FWRITE) == 0) 453 return (EBADF); 454 if (*(int *)addr) 455 sc->sc_flags |= LDF_WLABEL; 456 else 457 sc->sc_flags &= ~LDF_WLABEL; 458 break; 459 460 case DIOCGDEFLABEL: 461 ldgetdefaultlabel(sc, (struct disklabel *)addr); 462 break; 463 464 #ifdef __HAVE_OLD_DISKLABEL 465 case ODIOCGDEFLABEL: 466 ldgetdefaultlabel(sc, &newlabel); 467 if (newlabel.d_npartitions > OLDMAXPARTITIONS) 468 return ENOTTY; 469 memcpy(addr, &newlabel, sizeof (struct olddisklabel)); 470 break; 471 #endif 472 473 default: 474 error = ENOTTY; 475 break; 476 } 477 478 return (error); 479 } 480 481 static void 482 ldstrategy(struct buf *bp) 483 { 484 struct ld_softc *sc; 485 struct disklabel *lp; 486 daddr_t blkno; 487 int s, part; 488 489 sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev)); 490 part = DISKPART(bp->b_dev); 491 492 if ((sc->sc_flags & LDF_DETACH) != 0) { 493 bp->b_error = EIO; 494 goto bad; 495 } 496 497 lp = sc->sc_dk.dk_label; 498 499 /* 500 * The transfer must be a whole number of blocks and the offset must 501 * not be negative. 502 */ 503 if ((bp->b_bcount % lp->d_secsize) != 0 || bp->b_blkno < 0) { 504 bp->b_error = EINVAL; 505 goto bad; 506 } 507 508 /* If it's a null transfer, return immediately. */ 509 if (bp->b_bcount == 0) 510 goto done; 511 512 /* 513 * Do bounds checking and adjust the transfer. If error, process. 514 * If past the end of partition, just return. 515 */ 516 if (part != RAW_PART && 517 bounds_check_with_label(&sc->sc_dk, bp, 518 (sc->sc_flags & (LDF_WLABEL | LDF_LABELLING)) != 0) <= 0) { 519 goto done; 520 } 521 522 /* 523 * Convert the block number to absolute and put it in terms 524 * of the device's logical block size. 525 */ 526 if (lp->d_secsize == DEV_BSIZE) 527 blkno = bp->b_blkno; 528 else if (lp->d_secsize > DEV_BSIZE) 529 blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE); 530 else 531 blkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize); 532 533 if (part != RAW_PART) 534 blkno += lp->d_partitions[part].p_offset; 535 536 bp->b_rawblkno = blkno; 537 538 s = splbio(); 539 BUFQ_PUT(&sc->sc_bufq, bp); 540 ldstart(sc); 541 splx(s); 542 return; 543 544 bad: 545 bp->b_flags |= B_ERROR; 546 done: 547 bp->b_resid = bp->b_bcount; 548 biodone(bp); 549 } 550 551 static void 552 ldstart(struct ld_softc *sc) 553 { 554 struct buf *bp; 555 int error; 556 557 while (sc->sc_queuecnt < sc->sc_maxqueuecnt) { 558 /* See if there is work to do. */ 559 if ((bp = BUFQ_PEEK(&sc->sc_bufq)) == NULL) 560 break; 561 562 disk_busy(&sc->sc_dk); 563 sc->sc_queuecnt++; 564 565 if (__predict_true((error = (*sc->sc_start)(sc, bp)) == 0)) { 566 /* 567 * The back-end is running the job; remove it from 568 * the queue. 569 */ 570 (void) BUFQ_GET(&sc->sc_bufq); 571 } else { 572 disk_unbusy(&sc->sc_dk, 0, (bp->b_flags & B_READ)); 573 sc->sc_queuecnt--; 574 if (error == EAGAIN) { 575 /* 576 * Temporary resource shortage in the 577 * back-end; just defer the job until 578 * later. 579 * 580 * XXX We might consider a watchdog timer 581 * XXX to make sure we are kicked into action. 582 */ 583 break; 584 } else { 585 (void) BUFQ_GET(&sc->sc_bufq); 586 bp->b_error = error; 587 bp->b_flags |= B_ERROR; 588 bp->b_resid = bp->b_bcount; 589 biodone(bp); 590 } 591 } 592 } 593 } 594 595 void 596 lddone(struct ld_softc *sc, struct buf *bp) 597 { 598 599 if ((bp->b_flags & B_ERROR) != 0) { 600 diskerr(bp, "ld", "error", LOG_PRINTF, 0, sc->sc_dk.dk_label); 601 printf("\n"); 602 } 603 604 disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid, 605 (bp->b_flags & B_READ)); 606 #if NRND > 0 607 rnd_add_uint32(&sc->sc_rnd_source, bp->b_rawblkno); 608 #endif 609 biodone(bp); 610 611 if (--sc->sc_queuecnt <= sc->sc_maxqueuecnt) { 612 if ((sc->sc_flags & LDF_DRAIN) != 0) { 613 sc->sc_flags &= ~LDF_DRAIN; 614 wakeup(&sc->sc_queuecnt); 615 } 616 ldstart(sc); 617 } 618 } 619 620 static int 621 ldsize(dev_t dev) 622 { 623 struct ld_softc *sc; 624 int part, unit, omask, size; 625 626 unit = DISKUNIT(dev); 627 if ((sc = device_lookup(&ld_cd, unit)) == NULL) 628 return (ENODEV); 629 if ((sc->sc_flags & LDF_ENABLED) == 0) 630 return (ENODEV); 631 part = DISKPART(dev); 632 633 omask = sc->sc_dk.dk_openmask & (1 << part); 634 635 if (omask == 0 && ldopen(dev, 0, S_IFBLK, NULL) != 0) 636 return (-1); 637 else if (sc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP) 638 size = -1; 639 else 640 size = sc->sc_dk.dk_label->d_partitions[part].p_size * 641 (sc->sc_dk.dk_label->d_secsize / DEV_BSIZE); 642 if (omask == 0 && ldclose(dev, 0, S_IFBLK, NULL) != 0) 643 return (-1); 644 645 return (size); 646 } 647 648 /* 649 * Load the label information from the specified device. 650 */ 651 static void 652 ldgetdisklabel(struct ld_softc *sc) 653 { 654 const char *errstring; 655 656 ldgetdefaultlabel(sc, sc->sc_dk.dk_label); 657 658 /* Call the generic disklabel extraction routine. */ 659 errstring = readdisklabel(MAKEDISKDEV(0, sc->sc_dv.dv_unit, RAW_PART), 660 ldstrategy, sc->sc_dk.dk_label, sc->sc_dk.dk_cpulabel); 661 if (errstring != NULL) 662 printf("%s: %s\n", sc->sc_dv.dv_xname, errstring); 663 664 /* In-core label now valid. */ 665 sc->sc_flags |= LDF_VLABEL; 666 } 667 668 /* 669 * Construct a ficticious label. 670 */ 671 static void 672 ldgetdefaultlabel(struct ld_softc *sc, struct disklabel *lp) 673 { 674 675 memset(lp, 0, sizeof(struct disklabel)); 676 677 lp->d_secsize = sc->sc_secsize; 678 lp->d_ntracks = sc->sc_nheads; 679 lp->d_nsectors = sc->sc_nsectors; 680 lp->d_ncylinders = sc->sc_ncylinders; 681 lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 682 lp->d_type = DTYPE_LD; 683 strlcpy(lp->d_typename, "unknown", sizeof(lp->d_typename)); 684 strlcpy(lp->d_packname, "fictitious", sizeof(lp->d_packname)); 685 lp->d_secperunit = sc->sc_secperunit; 686 lp->d_rpm = 7200; 687 lp->d_interleave = 1; 688 lp->d_flags = 0; 689 690 lp->d_partitions[RAW_PART].p_offset = 0; 691 lp->d_partitions[RAW_PART].p_size = 692 lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); 693 lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 694 lp->d_npartitions = RAW_PART + 1; 695 696 lp->d_magic = DISKMAGIC; 697 lp->d_magic2 = DISKMAGIC; 698 lp->d_checksum = dkcksum(lp); 699 } 700 701 /* 702 * Wait interruptibly for an exclusive lock. 703 * 704 * XXX Several drivers do this; it should be abstracted and made MP-safe. 705 */ 706 static int 707 ldlock(struct ld_softc *sc) 708 { 709 int error; 710 711 while ((sc->sc_flags & LDF_LKHELD) != 0) { 712 sc->sc_flags |= LDF_LKWANTED; 713 if ((error = tsleep(sc, PRIBIO | PCATCH, "ldlck", 0)) != 0) 714 return (error); 715 } 716 sc->sc_flags |= LDF_LKHELD; 717 return (0); 718 } 719 720 /* 721 * Unlock and wake up any waiters. 722 */ 723 static void 724 ldunlock(struct ld_softc *sc) 725 { 726 727 sc->sc_flags &= ~LDF_LKHELD; 728 if ((sc->sc_flags & LDF_LKWANTED) != 0) { 729 sc->sc_flags &= ~LDF_LKWANTED; 730 wakeup(sc); 731 } 732 } 733 734 /* 735 * Take a dump. 736 */ 737 static int 738 lddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size) 739 { 740 struct ld_softc *sc; 741 struct disklabel *lp; 742 int unit, part, nsects, sectoff, towrt, nblk, maxblkcnt, rv; 743 static int dumping; 744 745 unit = DISKUNIT(dev); 746 if ((sc = device_lookup(&ld_cd, unit)) == NULL) 747 return (ENXIO); 748 if ((sc->sc_flags & LDF_ENABLED) == 0) 749 return (ENODEV); 750 if (sc->sc_dump == NULL) 751 return (ENXIO); 752 753 /* Check if recursive dump; if so, punt. */ 754 if (dumping) 755 return (EFAULT); 756 dumping = 1; 757 758 /* Convert to disk sectors. Request must be a multiple of size. */ 759 part = DISKPART(dev); 760 lp = sc->sc_dk.dk_label; 761 if ((size % lp->d_secsize) != 0) 762 return (EFAULT); 763 towrt = size / lp->d_secsize; 764 blkno = dbtob(blkno) / lp->d_secsize; /* blkno in DEV_BSIZE units */ 765 766 nsects = lp->d_partitions[part].p_size; 767 sectoff = lp->d_partitions[part].p_offset; 768 769 /* Check transfer bounds against partition size. */ 770 if ((blkno < 0) || ((blkno + towrt) > nsects)) 771 return (EINVAL); 772 773 /* Offset block number to start of partition. */ 774 blkno += sectoff; 775 776 /* Start dumping and return when done. */ 777 maxblkcnt = sc->sc_maxxfer / sc->sc_secsize - 1; 778 while (towrt > 0) { 779 nblk = min(maxblkcnt, towrt); 780 781 if ((rv = (*sc->sc_dump)(sc, va, blkno, nblk)) != 0) 782 return (rv); 783 784 towrt -= nblk; 785 blkno += nblk; 786 va += nblk * sc->sc_secsize; 787 } 788 789 dumping = 0; 790 return (0); 791 } 792 793 /* 794 * Adjust the size of a transfer. 795 */ 796 static void 797 ldminphys(struct buf *bp) 798 { 799 struct ld_softc *sc; 800 801 sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev)); 802 803 if (bp->b_bcount > sc->sc_maxxfer) 804 bp->b_bcount = sc->sc_maxxfer; 805 minphys(bp); 806 } 807