1 /* $NetBSD: ld.c,v 1.19 2003/01/27 02:43:30 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.19 2003/01/27 02:43:30 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 int ldstart(struct ld_softc *, struct buf *); 77 static void ldunlock(struct ld_softc *); 78 79 extern struct cfdriver ld_cd; 80 81 dev_type_open(ldopen); 82 dev_type_close(ldclose); 83 dev_type_read(ldread); 84 dev_type_write(ldwrite); 85 dev_type_ioctl(ldioctl); 86 dev_type_strategy(ldstrategy); 87 dev_type_dump(lddump); 88 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 if (sc->sc_secperunit <= 528 * 2048) /* 528MB */ 124 sc->sc_nheads = 16; 125 else if (sc->sc_secperunit <= 1024 * 2048) /* 1GB */ 126 sc->sc_nheads = 32; 127 else if (sc->sc_secperunit <= 21504 * 2048) /* 21GB */ 128 sc->sc_nheads = 64; 129 else if (sc->sc_secperunit <= 43008 * 2048) /* 42GB */ 130 sc->sc_nheads = 128; 131 else 132 sc->sc_nheads = 255; 133 134 sc->sc_nsectors = 63; 135 sc->sc_ncylinders = sc->sc_secperunit / 136 (sc->sc_nheads * sc->sc_nsectors); 137 } 138 139 format_bytes(buf, sizeof(buf), (u_int64_t)sc->sc_secperunit * 140 sc->sc_secsize); 141 printf("%s: %s, %d cyl, %d head, %d sec, %d bytes/sect x %d sectors\n", 142 sc->sc_dv.dv_xname, buf, sc->sc_ncylinders, sc->sc_nheads, 143 sc->sc_nsectors, sc->sc_secsize, sc->sc_secperunit); 144 145 #if NRND > 0 146 /* Attach the device into the rnd source list. */ 147 rnd_attach_source(&sc->sc_rnd_source, sc->sc_dv.dv_xname, 148 RND_TYPE_DISK, 0); 149 #endif 150 151 /* Set the `shutdownhook'. */ 152 if (ld_sdh == NULL) 153 ld_sdh = shutdownhook_establish(ldshutdown, NULL); 154 bufq_alloc(&sc->sc_bufq, BUFQ_FCFS); 155 } 156 157 int 158 ldadjqparam(struct ld_softc *sc, int max) 159 { 160 int s, rv; 161 162 s = splbio(); 163 sc->sc_maxqueuecnt = max; 164 if (sc->sc_queuecnt > max) { 165 sc->sc_flags |= LDF_DRAIN; 166 rv = tsleep(&sc->sc_queuecnt, PRIBIO, "lddrn", 30 * hz); 167 sc->sc_flags &= ~LDF_DRAIN; 168 } else 169 rv = 0; 170 splx(s); 171 172 return (rv); 173 } 174 175 int 176 ldbegindetach(struct ld_softc *sc, int flags) 177 { 178 int s, rv; 179 180 if ((sc->sc_flags & LDF_ENABLED) == 0) 181 return (0); 182 183 if ((flags & DETACH_FORCE) == 0 && sc->sc_dk.dk_openmask != 0) 184 return (EBUSY); 185 186 s = splbio(); 187 sc->sc_flags |= LDF_DETACH; 188 rv = ldadjqparam(sc, 0); 189 splx(s); 190 191 return (rv); 192 } 193 194 void 195 ldenddetach(struct ld_softc *sc) 196 { 197 struct buf *bp; 198 int s, bmaj, cmaj, i, mn; 199 200 if ((sc->sc_flags & LDF_ENABLED) == 0) 201 return; 202 203 /* Wait for commands queued with the hardware to complete. */ 204 if (sc->sc_queuecnt != 0) 205 if (tsleep(&sc->sc_queuecnt, PRIBIO, "lddtch", 30 * hz)) 206 printf("%s: not drained\n", sc->sc_dv.dv_xname); 207 208 /* Locate the major numbers. */ 209 bmaj = bdevsw_lookup_major(&ld_bdevsw); 210 cmaj = cdevsw_lookup_major(&ld_cdevsw); 211 212 /* Kill off any queued buffers. */ 213 s = splbio(); 214 while ((bp = BUFQ_GET(&sc->sc_bufq)) != NULL) { 215 bp->b_error = EIO; 216 bp->b_flags |= B_ERROR; 217 bp->b_resid = bp->b_bcount; 218 biodone(bp); 219 } 220 bufq_free(&sc->sc_bufq); 221 splx(s); 222 223 /* Nuke the vnodes for any open instances. */ 224 for (i = 0; i < MAXPARTITIONS; i++) { 225 mn = DISKMINOR(sc->sc_dv.dv_unit, i); 226 vdevgone(bmaj, mn, mn, VBLK); 227 vdevgone(cmaj, mn, mn, VCHR); 228 } 229 230 /* Detach from the disk list. */ 231 disk_detach(&sc->sc_dk); 232 233 #if NRND > 0 234 /* Unhook the entropy source. */ 235 rnd_detach_source(&sc->sc_rnd_source); 236 #endif 237 238 /* Flush the device's cache. */ 239 if (sc->sc_flush != NULL) 240 if ((*sc->sc_flush)(sc) != 0) 241 printf("%s: unable to flush cache\n", 242 sc->sc_dv.dv_xname); 243 } 244 245 /* ARGSUSED */ 246 static void 247 ldshutdown(void *cookie) 248 { 249 struct ld_softc *sc; 250 int i; 251 252 for (i = 0; i < ld_cd.cd_ndevs; i++) { 253 if ((sc = device_lookup(&ld_cd, i)) == NULL) 254 continue; 255 if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0) 256 printf("%s: unable to flush cache\n", 257 sc->sc_dv.dv_xname); 258 } 259 } 260 261 /* ARGSUSED */ 262 int 263 ldopen(dev_t dev, int flags, int fmt, struct proc *p) 264 { 265 struct ld_softc *sc; 266 int unit, part; 267 268 unit = DISKUNIT(dev); 269 if ((sc = device_lookup(&ld_cd, unit))== NULL) 270 return (ENXIO); 271 if ((sc->sc_flags & LDF_ENABLED) == 0) 272 return (ENODEV); 273 part = DISKPART(dev); 274 ldlock(sc); 275 276 if (sc->sc_dk.dk_openmask == 0) 277 ldgetdisklabel(sc); 278 279 /* Check that the partition exists. */ 280 if (part != RAW_PART && (part >= sc->sc_dk.dk_label->d_npartitions || 281 sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) { 282 ldunlock(sc); 283 return (ENXIO); 284 } 285 286 /* Ensure only one open at a time. */ 287 switch (fmt) { 288 case S_IFCHR: 289 sc->sc_dk.dk_copenmask |= (1 << part); 290 break; 291 case S_IFBLK: 292 sc->sc_dk.dk_bopenmask |= (1 << part); 293 break; 294 } 295 sc->sc_dk.dk_openmask = 296 sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; 297 298 ldunlock(sc); 299 return (0); 300 } 301 302 /* ARGSUSED */ 303 int 304 ldclose(dev_t dev, int flags, int fmt, struct proc *p) 305 { 306 struct ld_softc *sc; 307 int part, unit; 308 309 unit = DISKUNIT(dev); 310 part = DISKPART(dev); 311 sc = device_lookup(&ld_cd, unit); 312 ldlock(sc); 313 314 switch (fmt) { 315 case S_IFCHR: 316 sc->sc_dk.dk_copenmask &= ~(1 << part); 317 break; 318 case S_IFBLK: 319 sc->sc_dk.dk_bopenmask &= ~(1 << part); 320 break; 321 } 322 sc->sc_dk.dk_openmask = 323 sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; 324 325 if (sc->sc_dk.dk_openmask == 0 && sc->sc_flush != NULL) 326 if ((*sc->sc_flush)(sc) != 0) 327 printf("%s: unable to flush cache\n", 328 sc->sc_dv.dv_xname); 329 330 ldunlock(sc); 331 return (0); 332 } 333 334 /* ARGSUSED */ 335 int 336 ldread(dev_t dev, struct uio *uio, int ioflag) 337 { 338 339 return (physio(ldstrategy, NULL, dev, B_READ, ldminphys, uio)); 340 } 341 342 /* ARGSUSED */ 343 int 344 ldwrite(dev_t dev, struct uio *uio, int ioflag) 345 { 346 347 return (physio(ldstrategy, NULL, dev, B_WRITE, ldminphys, uio)); 348 } 349 350 /* ARGSUSED */ 351 int 352 ldioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p) 353 { 354 struct ld_softc *sc; 355 int part, unit, error; 356 #ifdef __HAVE_OLD_DISKLABEL 357 struct disklabel newlabel; 358 #endif 359 struct disklabel *lp; 360 361 unit = DISKUNIT(dev); 362 part = DISKPART(dev); 363 sc = device_lookup(&ld_cd, unit); 364 error = 0; 365 366 switch (cmd) { 367 case DIOCGDINFO: 368 memcpy(addr, sc->sc_dk.dk_label, sizeof(struct disklabel)); 369 return (0); 370 371 #ifdef __HAVE_OLD_DISKLABEL 372 case ODIOCGDINFO: 373 newlabel = *(sc->sc_dk.dk_label); 374 if (newlabel.d_npartitions > OLDMAXPARTITIONS) 375 return ENOTTY; 376 memcpy(addr, &newlabel, sizeof(struct olddisklabel)); 377 return (0); 378 #endif 379 380 case DIOCGPART: 381 ((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label; 382 ((struct partinfo *)addr)->part = 383 &sc->sc_dk.dk_label->d_partitions[part]; 384 break; 385 386 case DIOCWDINFO: 387 case DIOCSDINFO: 388 #ifdef __HAVE_OLD_DISKLABEL 389 case ODIOCWDINFO: 390 case ODIOCSDINFO: 391 392 if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) { 393 memset(&newlabel, 0, sizeof newlabel); 394 memcpy(&newlabel, addr, sizeof (struct olddisklabel)); 395 lp = &newlabel; 396 } else 397 #endif 398 lp = (struct disklabel *)addr; 399 400 if ((flag & FWRITE) == 0) 401 return (EBADF); 402 403 if ((error = ldlock(sc)) != 0) 404 return (error); 405 sc->sc_flags |= LDF_LABELLING; 406 407 error = setdisklabel(sc->sc_dk.dk_label, 408 lp, /*sc->sc_dk.dk_openmask : */0, 409 sc->sc_dk.dk_cpulabel); 410 if (error == 0 && (cmd == DIOCWDINFO 411 #ifdef __HAVE_OLD_DISKLABEL 412 || cmd == ODIOCWDINFO 413 #endif 414 )) 415 error = writedisklabel( 416 MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART), 417 ldstrategy, sc->sc_dk.dk_label, 418 sc->sc_dk.dk_cpulabel); 419 420 sc->sc_flags &= ~LDF_LABELLING; 421 ldunlock(sc); 422 break; 423 424 case DIOCWLABEL: 425 if ((flag & FWRITE) == 0) 426 return (EBADF); 427 if (*(int *)addr) 428 sc->sc_flags |= LDF_WLABEL; 429 else 430 sc->sc_flags &= ~LDF_WLABEL; 431 break; 432 433 case DIOCGDEFLABEL: 434 ldgetdefaultlabel(sc, (struct disklabel *)addr); 435 break; 436 437 #ifdef __HAVE_OLD_DISKLABEL 438 case ODIOCGDEFLABEL: 439 ldgetdefaultlabel(sc, &newlabel); 440 if (newlabel.d_npartitions > OLDMAXPARTITIONS) 441 return ENOTTY; 442 memcpy(addr, &newlabel, sizeof (struct olddisklabel)); 443 break; 444 #endif 445 446 default: 447 error = ENOTTY; 448 break; 449 } 450 451 return (error); 452 } 453 454 void 455 ldstrategy(struct buf *bp) 456 { 457 struct ld_softc *sc; 458 int s; 459 460 sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev)); 461 462 s = splbio(); 463 if (sc->sc_queuecnt >= sc->sc_maxqueuecnt) { 464 BUFQ_PUT(&sc->sc_bufq, bp); 465 splx(s); 466 return; 467 } 468 splx(s); 469 ldstart(sc, bp); 470 } 471 472 static int 473 ldstart(struct ld_softc *sc, struct buf *bp) 474 { 475 struct disklabel *lp; 476 int part, s, rv; 477 478 if ((sc->sc_flags & LDF_DETACH) != 0) { 479 bp->b_error = EIO; 480 bp->b_flags |= B_ERROR; 481 bp->b_resid = bp->b_bcount; 482 biodone(bp); 483 return (-1); 484 } 485 486 part = DISKPART(bp->b_dev); 487 lp = sc->sc_dk.dk_label; 488 489 /* 490 * The transfer must be a whole number of blocks and the offset must 491 * not be negative. 492 */ 493 if ((bp->b_bcount % lp->d_secsize) != 0 || bp->b_blkno < 0) { 494 bp->b_flags |= B_ERROR; 495 biodone(bp); 496 return (-1); 497 } 498 499 /* 500 * If it's a null transfer, return. 501 */ 502 if (bp->b_bcount == 0) { 503 bp->b_resid = bp->b_bcount; 504 biodone(bp); 505 return (-1); 506 } 507 508 /* 509 * Do bounds checking and adjust the transfer. If error, process. 510 * If past the end of partition, just return. 511 */ 512 if (part != RAW_PART && 513 bounds_check_with_label(bp, lp, 514 (sc->sc_flags & (LDF_WLABEL | LDF_LABELLING)) != 0) <= 0) { 515 bp->b_resid = bp->b_bcount; 516 biodone(bp); 517 return (-1); 518 } 519 520 /* 521 * Convert the logical block number to a physical one and put it in 522 * terms of the device's logical block size. 523 */ 524 if (lp->d_secsize >= DEV_BSIZE) 525 bp->b_rawblkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE); 526 else 527 bp->b_rawblkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize); 528 529 if (part != RAW_PART) 530 bp->b_rawblkno += lp->d_partitions[part].p_offset; 531 532 s = splbio(); 533 disk_busy(&sc->sc_dk); 534 sc->sc_queuecnt++; 535 splx(s); 536 537 if ((rv = (*sc->sc_start)(sc, bp)) != 0) { 538 bp->b_error = rv; 539 bp->b_flags |= B_ERROR; 540 bp->b_resid = bp->b_bcount; 541 s = splbio(); 542 lddone(sc, bp); 543 splx(s); 544 } 545 546 return (0); 547 } 548 549 void 550 lddone(struct ld_softc *sc, struct buf *bp) 551 { 552 553 if ((bp->b_flags & B_ERROR) != 0) { 554 diskerr(bp, "ld", "error", LOG_PRINTF, 0, sc->sc_dk.dk_label); 555 printf("\n"); 556 } 557 558 disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid, 559 (bp->b_flags & B_READ)); 560 #if NRND > 0 561 rnd_add_uint32(&sc->sc_rnd_source, bp->b_rawblkno); 562 #endif 563 biodone(bp); 564 565 if (--sc->sc_queuecnt <= sc->sc_maxqueuecnt) { 566 if ((sc->sc_flags & LDF_DRAIN) != 0) 567 wakeup(&sc->sc_queuecnt); 568 while ((bp = BUFQ_GET(&sc->sc_bufq)) != NULL) { 569 if (!ldstart(sc, bp)) 570 break; 571 } 572 } 573 } 574 575 int 576 ldsize(dev_t dev) 577 { 578 struct ld_softc *sc; 579 int part, unit, omask, size; 580 581 unit = DISKUNIT(dev); 582 if ((sc = device_lookup(&ld_cd, unit)) == NULL) 583 return (ENODEV); 584 if ((sc->sc_flags & LDF_ENABLED) == 0) 585 return (ENODEV); 586 part = DISKPART(dev); 587 588 omask = sc->sc_dk.dk_openmask & (1 << part); 589 590 if (omask == 0 && ldopen(dev, 0, S_IFBLK, NULL) != 0) 591 return (-1); 592 else if (sc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP) 593 size = -1; 594 else 595 size = sc->sc_dk.dk_label->d_partitions[part].p_size * 596 (sc->sc_dk.dk_label->d_secsize / DEV_BSIZE); 597 if (omask == 0 && ldclose(dev, 0, S_IFBLK, NULL) != 0) 598 return (-1); 599 600 return (size); 601 } 602 603 /* 604 * Load the label information from the specified device. 605 */ 606 static void 607 ldgetdisklabel(struct ld_softc *sc) 608 { 609 const char *errstring; 610 611 ldgetdefaultlabel(sc, sc->sc_dk.dk_label); 612 613 /* Call the generic disklabel extraction routine. */ 614 errstring = readdisklabel(MAKEDISKDEV(0, sc->sc_dv.dv_unit, RAW_PART), 615 ldstrategy, sc->sc_dk.dk_label, sc->sc_dk.dk_cpulabel); 616 if (errstring != NULL) 617 printf("%s: %s\n", sc->sc_dv.dv_xname, errstring); 618 } 619 620 /* 621 * Construct a ficticious label. 622 */ 623 static void 624 ldgetdefaultlabel(struct ld_softc *sc, struct disklabel *lp) 625 { 626 627 memset(lp, 0, sizeof(struct disklabel)); 628 629 lp->d_secsize = sc->sc_secsize; 630 lp->d_ntracks = sc->sc_nheads; 631 lp->d_nsectors = sc->sc_nsectors; 632 lp->d_ncylinders = sc->sc_ncylinders; 633 lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 634 lp->d_type = DTYPE_LD; 635 strcpy(lp->d_typename, "unknown"); 636 strcpy(lp->d_packname, "fictitious"); 637 lp->d_secperunit = sc->sc_secperunit; 638 lp->d_rpm = 7200; 639 lp->d_interleave = 1; 640 lp->d_flags = 0; 641 642 lp->d_partitions[RAW_PART].p_offset = 0; 643 lp->d_partitions[RAW_PART].p_size = 644 lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); 645 lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 646 lp->d_npartitions = RAW_PART + 1; 647 648 lp->d_magic = DISKMAGIC; 649 lp->d_magic2 = DISKMAGIC; 650 lp->d_checksum = dkcksum(lp); 651 } 652 653 /* 654 * Wait interruptibly for an exclusive lock. 655 * 656 * XXX Several drivers do this; it should be abstracted and made MP-safe. 657 */ 658 static int 659 ldlock(struct ld_softc *sc) 660 { 661 int error; 662 663 while ((sc->sc_flags & LDF_LKHELD) != 0) { 664 sc->sc_flags |= LDF_LKWANTED; 665 if ((error = tsleep(sc, PRIBIO | PCATCH, "ldlck", 0)) != 0) 666 return (error); 667 } 668 sc->sc_flags |= LDF_LKHELD; 669 return (0); 670 } 671 672 /* 673 * Unlock and wake up any waiters. 674 */ 675 static void 676 ldunlock(struct ld_softc *sc) 677 { 678 679 sc->sc_flags &= ~LDF_LKHELD; 680 if ((sc->sc_flags & LDF_LKWANTED) != 0) { 681 sc->sc_flags &= ~LDF_LKWANTED; 682 wakeup(sc); 683 } 684 } 685 686 /* 687 * Take a dump. 688 */ 689 int 690 lddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size) 691 { 692 struct ld_softc *sc; 693 struct disklabel *lp; 694 int unit, part, nsects, sectoff, towrt, nblk, maxblkcnt, rv; 695 static int dumping; 696 697 unit = DISKUNIT(dev); 698 if ((sc = device_lookup(&ld_cd, unit)) == NULL) 699 return (ENXIO); 700 if ((sc->sc_flags & LDF_ENABLED) == 0) 701 return (ENODEV); 702 if (sc->sc_dump == NULL) 703 return (ENXIO); 704 705 /* Check if recursive dump; if so, punt. */ 706 if (dumping) 707 return (EFAULT); 708 dumping = 1; 709 710 /* Convert to disk sectors. Request must be a multiple of size. */ 711 part = DISKPART(dev); 712 lp = sc->sc_dk.dk_label; 713 if ((size % lp->d_secsize) != 0) 714 return (EFAULT); 715 towrt = size / lp->d_secsize; 716 blkno = dbtob(blkno) / lp->d_secsize; /* blkno in DEV_BSIZE units */ 717 718 nsects = lp->d_partitions[part].p_size; 719 sectoff = lp->d_partitions[part].p_offset; 720 721 /* Check transfer bounds against partition size. */ 722 if ((blkno < 0) || ((blkno + towrt) > nsects)) 723 return (EINVAL); 724 725 /* Offset block number to start of partition. */ 726 blkno += sectoff; 727 728 /* Start dumping and return when done. */ 729 maxblkcnt = sc->sc_maxxfer / sc->sc_secsize - 1; 730 while (towrt > 0) { 731 nblk = min(maxblkcnt, towrt); 732 733 if ((rv = (*sc->sc_dump)(sc, va, blkno, nblk)) != 0) 734 return (rv); 735 736 towrt -= nblk; 737 blkno += nblk; 738 va += nblk * sc->sc_secsize; 739 } 740 741 dumping = 0; 742 return (0); 743 } 744 745 /* 746 * Adjust the size of a transfer. 747 */ 748 static void 749 ldminphys(struct buf *bp) 750 { 751 struct ld_softc *sc; 752 753 sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev)); 754 755 if (bp->b_bcount > sc->sc_maxxfer) 756 bp->b_bcount = sc->sc_maxxfer; 757 minphys(bp); 758 } 759