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