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