1 /* $NetBSD: ld.c,v 1.39 2005/12/11 12:20:53 christos 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.39 2005/12/11 12:20:53 christos 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/bufq.h> 56 #include <sys/endian.h> 57 #include <sys/disklabel.h> 58 #include <sys/disk.h> 59 #include <sys/dkio.h> 60 #include <sys/stat.h> 61 #include <sys/lock.h> 62 #include <sys/conf.h> 63 #include <sys/fcntl.h> 64 #include <sys/vnode.h> 65 #include <sys/syslog.h> 66 #if NRND > 0 67 #include <sys/rnd.h> 68 #endif 69 70 #include <dev/ldvar.h> 71 72 static void ldgetdefaultlabel(struct ld_softc *, struct disklabel *); 73 static void ldgetdisklabel(struct ld_softc *); 74 static void ldminphys(struct buf *bp); 75 static void ldshutdown(void *); 76 static void ldstart(struct ld_softc *); 77 78 extern struct cfdriver ld_cd; 79 80 static dev_type_open(ldopen); 81 static dev_type_close(ldclose); 82 static dev_type_read(ldread); 83 static dev_type_write(ldwrite); 84 static dev_type_ioctl(ldioctl); 85 static dev_type_strategy(ldstrategy); 86 static dev_type_dump(lddump); 87 static dev_type_size(ldsize); 88 89 const struct bdevsw ld_bdevsw = { 90 ldopen, ldclose, ldstrategy, ldioctl, lddump, ldsize, D_DISK 91 }; 92 93 const struct cdevsw ld_cdevsw = { 94 ldopen, ldclose, ldread, ldwrite, ldioctl, 95 nostop, notty, nopoll, nommap, nokqfilter, D_DISK 96 }; 97 98 static struct dkdriver lddkdriver = { ldstrategy, ldminphys }; 99 static void *ld_sdh; 100 101 void 102 ldattach(struct ld_softc *sc) 103 { 104 char tbuf[9]; 105 106 if ((sc->sc_flags & LDF_ENABLED) == 0) { 107 aprint_normal("%s: disabled\n", sc->sc_dv.dv_xname); 108 return; 109 } 110 111 /* Initialise and attach the disk structure. */ 112 sc->sc_dk.dk_driver = &lddkdriver; 113 sc->sc_dk.dk_name = sc->sc_dv.dv_xname; 114 disk_attach(&sc->sc_dk); 115 116 if (sc->sc_maxxfer > MAXPHYS) 117 sc->sc_maxxfer = MAXPHYS; 118 119 /* Build synthetic geometry if necessary. */ 120 if (sc->sc_nheads == 0 || sc->sc_nsectors == 0 || 121 sc->sc_ncylinders == 0) { 122 uint64_t ncyl; 123 124 if (sc->sc_secperunit <= 528 * 2048) /* 528MB */ 125 sc->sc_nheads = 16; 126 else if (sc->sc_secperunit <= 1024 * 2048) /* 1GB */ 127 sc->sc_nheads = 32; 128 else if (sc->sc_secperunit <= 21504 * 2048) /* 21GB */ 129 sc->sc_nheads = 64; 130 else if (sc->sc_secperunit <= 43008 * 2048) /* 42GB */ 131 sc->sc_nheads = 128; 132 else 133 sc->sc_nheads = 255; 134 135 sc->sc_nsectors = 63; 136 sc->sc_ncylinders = INT_MAX; 137 ncyl = sc->sc_secperunit / 138 (sc->sc_nheads * sc->sc_nsectors); 139 if (ncyl < INT_MAX) 140 sc->sc_ncylinders = (int)ncyl; 141 } 142 143 format_bytes(tbuf, sizeof(tbuf), sc->sc_secperunit * 144 sc->sc_secsize); 145 aprint_normal("%s: %s, %d cyl, %d head, %d sec, %d bytes/sect x %"PRIu64" sectors\n", 146 sc->sc_dv.dv_xname, tbuf, sc->sc_ncylinders, sc->sc_nheads, 147 sc->sc_nsectors, sc->sc_secsize, sc->sc_secperunit); 148 149 #if NRND > 0 150 /* Attach the device into the rnd source list. */ 151 rnd_attach_source(&sc->sc_rnd_source, sc->sc_dv.dv_xname, 152 RND_TYPE_DISK, 0); 153 #endif 154 155 /* Set the `shutdownhook'. */ 156 if (ld_sdh == NULL) 157 ld_sdh = shutdownhook_establish(ldshutdown, NULL); 158 bufq_alloc(&sc->sc_bufq, BUFQ_DISK_DEFAULT_STRAT, BUFQ_SORT_RAWBLOCK); 159 160 /* Discover wedges on this disk. */ 161 dkwedge_discover(&sc->sc_dk); 162 } 163 164 int 165 ldadjqparam(struct ld_softc *sc, int xmax) 166 { 167 int s; 168 169 s = splbio(); 170 sc->sc_maxqueuecnt = xmax; 171 splx(s); 172 173 return (0); 174 } 175 176 int 177 ldbegindetach(struct ld_softc *sc, int flags) 178 { 179 int s, rv = 0; 180 181 if ((sc->sc_flags & LDF_ENABLED) == 0) 182 return (0); 183 184 if ((flags & DETACH_FORCE) == 0 && sc->sc_dk.dk_openmask != 0) 185 return (EBUSY); 186 187 s = splbio(); 188 sc->sc_maxqueuecnt = 0; 189 sc->sc_flags |= LDF_DETACH; 190 while (sc->sc_queuecnt > 0) { 191 sc->sc_flags |= LDF_DRAIN; 192 rv = tsleep(&sc->sc_queuecnt, PRIBIO, "lddrn", 0); 193 if (rv) 194 break; 195 } 196 splx(s); 197 198 return (rv); 199 } 200 201 void 202 ldenddetach(struct ld_softc *sc) 203 { 204 int s, bmaj, cmaj, i, mn; 205 206 if ((sc->sc_flags & LDF_ENABLED) == 0) 207 return; 208 209 /* Wait for commands queued with the hardware to complete. */ 210 if (sc->sc_queuecnt != 0) 211 if (tsleep(&sc->sc_queuecnt, PRIBIO, "lddtch", 30 * hz)) 212 printf("%s: not drained\n", sc->sc_dv.dv_xname); 213 214 /* Locate the major numbers. */ 215 bmaj = bdevsw_lookup_major(&ld_bdevsw); 216 cmaj = cdevsw_lookup_major(&ld_cdevsw); 217 218 /* Kill off any queued buffers. */ 219 s = splbio(); 220 bufq_drain(sc->sc_bufq); 221 splx(s); 222 223 bufq_free(sc->sc_bufq); 224 225 /* Nuke the vnodes for any open instances. */ 226 for (i = 0; i < MAXPARTITIONS; i++) { 227 mn = DISKMINOR(sc->sc_dv.dv_unit, i); 228 vdevgone(bmaj, mn, mn, VBLK); 229 vdevgone(cmaj, mn, mn, VCHR); 230 } 231 232 /* Delete all of our wedges. */ 233 dkwedge_delall(&sc->sc_dk); 234 235 /* Detach from the disk list. */ 236 disk_detach(&sc->sc_dk); 237 238 #if NRND > 0 239 /* Unhook the entropy source. */ 240 rnd_detach_source(&sc->sc_rnd_source); 241 #endif 242 243 /* 244 * XXX We can't really flush the cache here, beceause the 245 * XXX device may already be non-existent from the controller's 246 * XXX perspective. 247 */ 248 #if 0 249 /* Flush the device's cache. */ 250 if (sc->sc_flush != NULL) 251 if ((*sc->sc_flush)(sc) != 0) 252 printf("%s: unable to flush cache\n", 253 sc->sc_dv.dv_xname); 254 #endif 255 } 256 257 /* ARGSUSED */ 258 static void 259 ldshutdown(void *cookie) 260 { 261 struct ld_softc *sc; 262 int i; 263 264 for (i = 0; i < ld_cd.cd_ndevs; i++) { 265 if ((sc = device_lookup(&ld_cd, i)) == NULL) 266 continue; 267 if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0) 268 printf("%s: unable to flush cache\n", 269 sc->sc_dv.dv_xname); 270 } 271 } 272 273 /* ARGSUSED */ 274 static int 275 ldopen(dev_t dev, int flags, int fmt, struct lwp *l) 276 { 277 struct ld_softc *sc; 278 int error, unit, part; 279 280 unit = DISKUNIT(dev); 281 if ((sc = device_lookup(&ld_cd, unit)) == NULL) 282 return (ENXIO); 283 if ((sc->sc_flags & LDF_ENABLED) == 0) 284 return (ENODEV); 285 part = DISKPART(dev); 286 287 if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE, NULL)) != 0) 288 return (error); 289 290 if (sc->sc_dk.dk_openmask == 0) { 291 /* Load the partition info if not already loaded. */ 292 if ((sc->sc_flags & LDF_VLABEL) == 0) 293 ldgetdisklabel(sc); 294 } 295 296 /* Check that the partition exists. */ 297 if (part != RAW_PART && (part >= sc->sc_dk.dk_label->d_npartitions || 298 sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) { 299 error = ENXIO; 300 goto bad1; 301 } 302 303 /* Ensure only one open at a time. */ 304 switch (fmt) { 305 case S_IFCHR: 306 sc->sc_dk.dk_copenmask |= (1 << part); 307 break; 308 case S_IFBLK: 309 sc->sc_dk.dk_bopenmask |= (1 << part); 310 break; 311 } 312 sc->sc_dk.dk_openmask = 313 sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; 314 315 (void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL); 316 return (0); 317 318 bad1: 319 (void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL); 320 return (error); 321 } 322 323 /* ARGSUSED */ 324 static int 325 ldclose(dev_t dev, int flags, int fmt, struct lwp *l) 326 { 327 struct ld_softc *sc; 328 int error, part, unit; 329 330 unit = DISKUNIT(dev); 331 part = DISKPART(dev); 332 sc = device_lookup(&ld_cd, unit); 333 334 if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE, NULL)) != 0) 335 return (error); 336 337 switch (fmt) { 338 case S_IFCHR: 339 sc->sc_dk.dk_copenmask &= ~(1 << part); 340 break; 341 case S_IFBLK: 342 sc->sc_dk.dk_bopenmask &= ~(1 << part); 343 break; 344 } 345 sc->sc_dk.dk_openmask = 346 sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; 347 348 if (sc->sc_dk.dk_openmask == 0) { 349 if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0) 350 printf("%s: unable to flush cache\n", 351 sc->sc_dv.dv_xname); 352 if ((sc->sc_flags & LDF_KLABEL) == 0) 353 sc->sc_flags &= ~LDF_VLABEL; 354 } 355 356 (void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL); 357 return (0); 358 } 359 360 /* ARGSUSED */ 361 static int 362 ldread(dev_t dev, struct uio *uio, int ioflag) 363 { 364 365 return (physio(ldstrategy, NULL, dev, B_READ, ldminphys, uio)); 366 } 367 368 /* ARGSUSED */ 369 static int 370 ldwrite(dev_t dev, struct uio *uio, int ioflag) 371 { 372 373 return (physio(ldstrategy, NULL, dev, B_WRITE, ldminphys, uio)); 374 } 375 376 /* ARGSUSED */ 377 static int 378 ldioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct lwp *l) 379 { 380 struct ld_softc *sc; 381 int part, unit, error; 382 #ifdef __HAVE_OLD_DISKLABEL 383 struct disklabel newlabel; 384 #endif 385 struct disklabel *lp; 386 387 unit = DISKUNIT(dev); 388 part = DISKPART(dev); 389 sc = device_lookup(&ld_cd, unit); 390 error = 0; 391 392 switch (cmd) { 393 case DIOCGDINFO: 394 memcpy(addr, sc->sc_dk.dk_label, sizeof(struct disklabel)); 395 return (0); 396 397 #ifdef __HAVE_OLD_DISKLABEL 398 case ODIOCGDINFO: 399 newlabel = *(sc->sc_dk.dk_label); 400 if (newlabel.d_npartitions > OLDMAXPARTITIONS) 401 return ENOTTY; 402 memcpy(addr, &newlabel, sizeof(struct olddisklabel)); 403 return (0); 404 #endif 405 406 case DIOCGPART: 407 ((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label; 408 ((struct partinfo *)addr)->part = 409 &sc->sc_dk.dk_label->d_partitions[part]; 410 break; 411 412 case DIOCWDINFO: 413 case DIOCSDINFO: 414 #ifdef __HAVE_OLD_DISKLABEL 415 case ODIOCWDINFO: 416 case ODIOCSDINFO: 417 418 if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) { 419 memset(&newlabel, 0, sizeof newlabel); 420 memcpy(&newlabel, addr, sizeof (struct olddisklabel)); 421 lp = &newlabel; 422 } else 423 #endif 424 lp = (struct disklabel *)addr; 425 426 if ((flag & FWRITE) == 0) 427 return (EBADF); 428 429 if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE, 430 NULL)) != 0) 431 return (error); 432 sc->sc_flags |= LDF_LABELLING; 433 434 error = setdisklabel(sc->sc_dk.dk_label, 435 lp, /*sc->sc_dk.dk_openmask : */0, 436 sc->sc_dk.dk_cpulabel); 437 if (error == 0 && (cmd == DIOCWDINFO 438 #ifdef __HAVE_OLD_DISKLABEL 439 || cmd == ODIOCWDINFO 440 #endif 441 )) 442 error = writedisklabel( 443 MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART), 444 ldstrategy, sc->sc_dk.dk_label, 445 sc->sc_dk.dk_cpulabel); 446 447 sc->sc_flags &= ~LDF_LABELLING; 448 (void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL); 449 break; 450 451 case DIOCKLABEL: 452 if ((flag & FWRITE) == 0) 453 return (EBADF); 454 if (*(int *)addr) 455 sc->sc_flags |= LDF_KLABEL; 456 else 457 sc->sc_flags &= ~LDF_KLABEL; 458 break; 459 460 case DIOCWLABEL: 461 if ((flag & FWRITE) == 0) 462 return (EBADF); 463 if (*(int *)addr) 464 sc->sc_flags |= LDF_WLABEL; 465 else 466 sc->sc_flags &= ~LDF_WLABEL; 467 break; 468 469 case DIOCGDEFLABEL: 470 ldgetdefaultlabel(sc, (struct disklabel *)addr); 471 break; 472 473 #ifdef __HAVE_OLD_DISKLABEL 474 case ODIOCGDEFLABEL: 475 ldgetdefaultlabel(sc, &newlabel); 476 if (newlabel.d_npartitions > OLDMAXPARTITIONS) 477 return ENOTTY; 478 memcpy(addr, &newlabel, sizeof (struct olddisklabel)); 479 break; 480 #endif 481 482 case DIOCCACHESYNC: 483 /* 484 * XXX Do we really need to care about having a writable 485 * file descriptor here? 486 */ 487 if ((flag & FWRITE) == 0) 488 error = EBADF; 489 else if (sc->sc_flush) 490 error = (*sc->sc_flush)(sc); 491 else 492 error = 0; /* XXX Error out instead? */ 493 break; 494 495 case DIOCAWEDGE: 496 { 497 struct dkwedge_info *dkw = (void *) addr; 498 499 if ((flag & FWRITE) == 0) 500 return (EBADF); 501 502 /* If the ioctl happens here, the parent is us. */ 503 strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname); 504 return (dkwedge_add(dkw)); 505 } 506 507 case DIOCDWEDGE: 508 { 509 struct dkwedge_info *dkw = (void *) addr; 510 511 if ((flag & FWRITE) == 0) 512 return (EBADF); 513 514 /* If the ioctl happens here, the parent is us. */ 515 strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname); 516 return (dkwedge_del(dkw)); 517 } 518 519 case DIOCLWEDGES: 520 { 521 struct dkwedge_list *dkwl = (void *) addr; 522 523 return (dkwedge_list(&sc->sc_dk, dkwl, l)); 524 } 525 526 default: 527 error = ENOTTY; 528 break; 529 } 530 531 return (error); 532 } 533 534 static void 535 ldstrategy(struct buf *bp) 536 { 537 struct ld_softc *sc; 538 struct disklabel *lp; 539 daddr_t blkno; 540 int s, part; 541 542 sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev)); 543 part = DISKPART(bp->b_dev); 544 545 if ((sc->sc_flags & LDF_DETACH) != 0) { 546 bp->b_error = EIO; 547 goto bad; 548 } 549 550 lp = sc->sc_dk.dk_label; 551 552 /* 553 * The transfer must be a whole number of blocks and the offset must 554 * not be negative. 555 */ 556 if ((bp->b_bcount % lp->d_secsize) != 0 || bp->b_blkno < 0) { 557 bp->b_error = EINVAL; 558 goto bad; 559 } 560 561 /* If it's a null transfer, return immediately. */ 562 if (bp->b_bcount == 0) 563 goto done; 564 565 /* 566 * Do bounds checking and adjust the transfer. If error, process. 567 * If past the end of partition, just return. 568 */ 569 if (part != RAW_PART && 570 bounds_check_with_label(&sc->sc_dk, bp, 571 (sc->sc_flags & (LDF_WLABEL | LDF_LABELLING)) != 0) <= 0) { 572 goto done; 573 } 574 575 /* 576 * Convert the block number to absolute and put it in terms 577 * of the device's logical block size. 578 */ 579 if (lp->d_secsize == DEV_BSIZE) 580 blkno = bp->b_blkno; 581 else if (lp->d_secsize > DEV_BSIZE) 582 blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE); 583 else 584 blkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize); 585 586 if (part != RAW_PART) 587 blkno += lp->d_partitions[part].p_offset; 588 589 bp->b_rawblkno = blkno; 590 591 s = splbio(); 592 BUFQ_PUT(sc->sc_bufq, bp); 593 ldstart(sc); 594 splx(s); 595 return; 596 597 bad: 598 bp->b_flags |= B_ERROR; 599 done: 600 bp->b_resid = bp->b_bcount; 601 biodone(bp); 602 } 603 604 static void 605 ldstart(struct ld_softc *sc) 606 { 607 struct buf *bp; 608 int error; 609 610 while (sc->sc_queuecnt < sc->sc_maxqueuecnt) { 611 /* See if there is work to do. */ 612 if ((bp = BUFQ_PEEK(sc->sc_bufq)) == NULL) 613 break; 614 615 disk_busy(&sc->sc_dk); 616 sc->sc_queuecnt++; 617 618 if (__predict_true((error = (*sc->sc_start)(sc, bp)) == 0)) { 619 /* 620 * The back-end is running the job; remove it from 621 * the queue. 622 */ 623 (void) BUFQ_GET(sc->sc_bufq); 624 } else { 625 disk_unbusy(&sc->sc_dk, 0, (bp->b_flags & B_READ)); 626 sc->sc_queuecnt--; 627 if (error == EAGAIN) { 628 /* 629 * Temporary resource shortage in the 630 * back-end; just defer the job until 631 * later. 632 * 633 * XXX We might consider a watchdog timer 634 * XXX to make sure we are kicked into action. 635 */ 636 break; 637 } else { 638 (void) BUFQ_GET(sc->sc_bufq); 639 bp->b_error = error; 640 bp->b_flags |= B_ERROR; 641 bp->b_resid = bp->b_bcount; 642 biodone(bp); 643 } 644 } 645 } 646 } 647 648 void 649 lddone(struct ld_softc *sc, struct buf *bp) 650 { 651 652 if ((bp->b_flags & B_ERROR) != 0) { 653 diskerr(bp, "ld", "error", LOG_PRINTF, 0, sc->sc_dk.dk_label); 654 printf("\n"); 655 } 656 657 disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid, 658 (bp->b_flags & B_READ)); 659 #if NRND > 0 660 rnd_add_uint32(&sc->sc_rnd_source, bp->b_rawblkno); 661 #endif 662 biodone(bp); 663 664 if (--sc->sc_queuecnt <= sc->sc_maxqueuecnt) { 665 if ((sc->sc_flags & LDF_DRAIN) != 0) { 666 sc->sc_flags &= ~LDF_DRAIN; 667 wakeup(&sc->sc_queuecnt); 668 } 669 ldstart(sc); 670 } 671 } 672 673 static int 674 ldsize(dev_t dev) 675 { 676 struct ld_softc *sc; 677 int part, unit, omask, size; 678 679 unit = DISKUNIT(dev); 680 if ((sc = device_lookup(&ld_cd, unit)) == NULL) 681 return (ENODEV); 682 if ((sc->sc_flags & LDF_ENABLED) == 0) 683 return (ENODEV); 684 part = DISKPART(dev); 685 686 omask = sc->sc_dk.dk_openmask & (1 << part); 687 688 if (omask == 0 && ldopen(dev, 0, S_IFBLK, NULL) != 0) 689 return (-1); 690 else if (sc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP) 691 size = -1; 692 else 693 size = sc->sc_dk.dk_label->d_partitions[part].p_size * 694 (sc->sc_dk.dk_label->d_secsize / DEV_BSIZE); 695 if (omask == 0 && ldclose(dev, 0, S_IFBLK, NULL) != 0) 696 return (-1); 697 698 return (size); 699 } 700 701 /* 702 * Load the label information from the specified device. 703 */ 704 static void 705 ldgetdisklabel(struct ld_softc *sc) 706 { 707 const char *errstring; 708 709 ldgetdefaultlabel(sc, sc->sc_dk.dk_label); 710 711 /* Call the generic disklabel extraction routine. */ 712 errstring = readdisklabel(MAKEDISKDEV(0, sc->sc_dv.dv_unit, RAW_PART), 713 ldstrategy, sc->sc_dk.dk_label, sc->sc_dk.dk_cpulabel); 714 if (errstring != NULL) 715 printf("%s: %s\n", sc->sc_dv.dv_xname, errstring); 716 717 /* In-core label now valid. */ 718 sc->sc_flags |= LDF_VLABEL; 719 } 720 721 /* 722 * Construct a ficticious label. 723 */ 724 static void 725 ldgetdefaultlabel(struct ld_softc *sc, struct disklabel *lp) 726 { 727 728 memset(lp, 0, sizeof(struct disklabel)); 729 730 lp->d_secsize = sc->sc_secsize; 731 lp->d_ntracks = sc->sc_nheads; 732 lp->d_nsectors = sc->sc_nsectors; 733 lp->d_ncylinders = sc->sc_ncylinders; 734 lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 735 lp->d_type = DTYPE_LD; 736 strlcpy(lp->d_typename, "unknown", sizeof(lp->d_typename)); 737 strlcpy(lp->d_packname, "fictitious", sizeof(lp->d_packname)); 738 lp->d_secperunit = sc->sc_secperunit; 739 lp->d_rpm = 7200; 740 lp->d_interleave = 1; 741 lp->d_flags = 0; 742 743 lp->d_partitions[RAW_PART].p_offset = 0; 744 lp->d_partitions[RAW_PART].p_size = 745 lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); 746 lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 747 lp->d_npartitions = RAW_PART + 1; 748 749 lp->d_magic = DISKMAGIC; 750 lp->d_magic2 = DISKMAGIC; 751 lp->d_checksum = dkcksum(lp); 752 } 753 754 /* 755 * Take a dump. 756 */ 757 static int 758 lddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size) 759 { 760 struct ld_softc *sc; 761 struct disklabel *lp; 762 int unit, part, nsects, sectoff, towrt, nblk, maxblkcnt, rv; 763 static int dumping; 764 765 unit = DISKUNIT(dev); 766 if ((sc = device_lookup(&ld_cd, unit)) == NULL) 767 return (ENXIO); 768 if ((sc->sc_flags & LDF_ENABLED) == 0) 769 return (ENODEV); 770 if (sc->sc_dump == NULL) 771 return (ENXIO); 772 773 /* Check if recursive dump; if so, punt. */ 774 if (dumping) 775 return (EFAULT); 776 dumping = 1; 777 778 /* Convert to disk sectors. Request must be a multiple of size. */ 779 part = DISKPART(dev); 780 lp = sc->sc_dk.dk_label; 781 if ((size % lp->d_secsize) != 0) 782 return (EFAULT); 783 towrt = size / lp->d_secsize; 784 blkno = dbtob(blkno) / lp->d_secsize; /* blkno in DEV_BSIZE units */ 785 786 nsects = lp->d_partitions[part].p_size; 787 sectoff = lp->d_partitions[part].p_offset; 788 789 /* Check transfer bounds against partition size. */ 790 if ((blkno < 0) || ((blkno + towrt) > nsects)) 791 return (EINVAL); 792 793 /* Offset block number to start of partition. */ 794 blkno += sectoff; 795 796 /* Start dumping and return when done. */ 797 maxblkcnt = sc->sc_maxxfer / sc->sc_secsize - 1; 798 while (towrt > 0) { 799 nblk = min(maxblkcnt, towrt); 800 801 if ((rv = (*sc->sc_dump)(sc, va, blkno, nblk)) != 0) 802 return (rv); 803 804 towrt -= nblk; 805 blkno += nblk; 806 va += nblk * sc->sc_secsize; 807 } 808 809 dumping = 0; 810 return (0); 811 } 812 813 /* 814 * Adjust the size of a transfer. 815 */ 816 static void 817 ldminphys(struct buf *bp) 818 { 819 struct ld_softc *sc; 820 821 sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev)); 822 823 if (bp->b_bcount > sc->sc_maxxfer) 824 bp->b_bcount = sc->sc_maxxfer; 825 minphys(bp); 826 } 827