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