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