1/* $NetBSD: ld.c,v 1.68 2010/09/20 06:54:06 kiyohara 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32/* 33 * Disk driver for use by RAID controllers. 34 */ 35 36#include <sys/cdefs.h> 37__KERNEL_RCSID(0, "$NetBSD: ld.c,v 1.68 2010/09/20 06:54:06 kiyohara Exp $"); 38 39#include <sys/param.h> 40#include <sys/systm.h> 41#include <sys/kernel.h> 42#include <sys/device.h> 43#include <sys/queue.h> 44#include <sys/proc.h> 45#include <sys/buf.h> 46#include <sys/bufq.h> 47#include <sys/endian.h> 48#include <sys/disklabel.h> 49#include <sys/disk.h> 50#include <sys/dkio.h> 51#include <sys/stat.h> 52#include <sys/conf.h> 53#include <sys/fcntl.h> 54#include <sys/vnode.h> 55#include <sys/syslog.h> 56#include <sys/mutex.h> 57#include <sys/rnd.h> 58 59#include <dev/ldvar.h> 60 61#include <prop/proplib.h> 62 63static void ldgetdefaultlabel(struct ld_softc *, struct disklabel *); 64static void ldgetdisklabel(struct ld_softc *); 65static void ldminphys(struct buf *bp); 66static bool ld_suspend(device_t, const pmf_qual_t *); 67static bool ld_shutdown(device_t, int); 68static void ldstart(struct ld_softc *, struct buf *); 69static void ld_set_properties(struct ld_softc *); 70static void ld_config_interrupts (device_t); 71static int ldlastclose(device_t); 72 73extern struct cfdriver ld_cd; 74 75static dev_type_open(ldopen); 76static dev_type_close(ldclose); 77static dev_type_read(ldread); 78static dev_type_write(ldwrite); 79static dev_type_ioctl(ldioctl); 80static dev_type_strategy(ldstrategy); 81static dev_type_dump(lddump); 82static dev_type_size(ldsize); 83 84const struct bdevsw ld_bdevsw = { 85 ldopen, ldclose, ldstrategy, ldioctl, lddump, ldsize, D_DISK 86}; 87 88const struct cdevsw ld_cdevsw = { 89 ldopen, ldclose, ldread, ldwrite, ldioctl, 90 nostop, notty, nopoll, nommap, nokqfilter, D_DISK 91}; 92 93static struct dkdriver lddkdriver = { ldstrategy, ldminphys }; 94 95void 96ldattach(struct ld_softc *sc) 97{ 98 char tbuf[9]; 99 100 mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_VM); 101 102 if ((sc->sc_flags & LDF_ENABLED) == 0) { 103 aprint_normal_dev(sc->sc_dv, "disabled\n"); 104 return; 105 } 106 107 /* Initialise and attach the disk structure. */ 108 disk_init(&sc->sc_dk, device_xname(sc->sc_dv), &lddkdriver); 109 disk_attach(&sc->sc_dk); 110 111 if (sc->sc_maxxfer > MAXPHYS) 112 sc->sc_maxxfer = MAXPHYS; 113 114 /* Build synthetic geometry if necessary. */ 115 if (sc->sc_nheads == 0 || sc->sc_nsectors == 0 || 116 sc->sc_ncylinders == 0) { 117 uint64_t ncyl; 118 119 if (sc->sc_secperunit <= 528 * 2048) /* 528MB */ 120 sc->sc_nheads = 16; 121 else if (sc->sc_secperunit <= 1024 * 2048) /* 1GB */ 122 sc->sc_nheads = 32; 123 else if (sc->sc_secperunit <= 21504 * 2048) /* 21GB */ 124 sc->sc_nheads = 64; 125 else if (sc->sc_secperunit <= 43008 * 2048) /* 42GB */ 126 sc->sc_nheads = 128; 127 else 128 sc->sc_nheads = 255; 129 130 sc->sc_nsectors = 63; 131 sc->sc_ncylinders = INT_MAX; 132 ncyl = sc->sc_secperunit / 133 (sc->sc_nheads * sc->sc_nsectors); 134 if (ncyl < INT_MAX) 135 sc->sc_ncylinders = (int)ncyl; 136 } 137 138 format_bytes(tbuf, sizeof(tbuf), sc->sc_secperunit * 139 sc->sc_secsize); 140 aprint_normal_dev(sc->sc_dv, "%s, %d cyl, %d head, %d sec, " 141 "%d bytes/sect x %"PRIu64" sectors\n", 142 tbuf, sc->sc_ncylinders, sc->sc_nheads, 143 sc->sc_nsectors, sc->sc_secsize, sc->sc_secperunit); 144 sc->sc_disksize512 = sc->sc_secperunit * sc->sc_secsize / DEV_BSIZE; 145 146 ld_set_properties(sc); 147 148 /* Attach the device into the rnd source list. */ 149 rnd_attach_source(&sc->sc_rnd_source, device_xname(sc->sc_dv), 150 RND_TYPE_DISK, 0); 151 152 /* Register with PMF */ 153 if (!pmf_device_register1(sc->sc_dv, ld_suspend, NULL, ld_shutdown)) 154 aprint_error_dev(sc->sc_dv, 155 "couldn't establish power handler\n"); 156 157 bufq_alloc(&sc->sc_bufq, BUFQ_DISK_DEFAULT_STRAT, BUFQ_SORT_RAWBLOCK); 158 159 /* Discover wedges on this disk. */ 160 config_interrupts(sc->sc_dv, ld_config_interrupts); 161} 162 163int 164ldadjqparam(struct ld_softc *sc, int xmax) 165{ 166 int s; 167 168 s = splbio(); 169 sc->sc_maxqueuecnt = xmax; 170 splx(s); 171 172 return (0); 173} 174 175int 176ldbegindetach(struct ld_softc *sc, int flags) 177{ 178 int s, rv = 0; 179 180 if ((sc->sc_flags & LDF_ENABLED) == 0) 181 return (0); 182 183 rv = disk_begindetach(&sc->sc_dk, ldlastclose, sc->sc_dv, flags); 184 185 if (rv != 0) 186 return rv; 187 188 s = splbio(); 189 sc->sc_maxqueuecnt = 0; 190 sc->sc_flags |= LDF_DETACH; 191 while (sc->sc_queuecnt > 0) { 192 sc->sc_flags |= LDF_DRAIN; 193 rv = tsleep(&sc->sc_queuecnt, PRIBIO, "lddrn", 0); 194 if (rv) 195 break; 196 } 197 splx(s); 198 199 return (rv); 200} 201 202void 203ldenddetach(struct ld_softc *sc) 204{ 205 int s, bmaj, cmaj, i, mn; 206 207 if ((sc->sc_flags & LDF_ENABLED) == 0) 208 return; 209 210 /* Wait for commands queued with the hardware to complete. */ 211 if (sc->sc_queuecnt != 0) 212 if (tsleep(&sc->sc_queuecnt, PRIBIO, "lddtch", 30 * hz)) 213 printf("%s: not drained\n", device_xname(sc->sc_dv)); 214 215 /* Locate the major numbers. */ 216 bmaj = bdevsw_lookup_major(&ld_bdevsw); 217 cmaj = cdevsw_lookup_major(&ld_cdevsw); 218 219 /* Kill off any queued buffers. */ 220 s = splbio(); 221 bufq_drain(sc->sc_bufq); 222 splx(s); 223 224 bufq_free(sc->sc_bufq); 225 226 /* Nuke the vnodes for any open instances. */ 227 for (i = 0; i < MAXPARTITIONS; i++) { 228 mn = DISKMINOR(device_unit(sc->sc_dv), i); 229 vdevgone(bmaj, mn, mn, VBLK); 230 vdevgone(cmaj, mn, mn, VCHR); 231 } 232 233 /* Delete all of our wedges. */ 234 dkwedge_delall(&sc->sc_dk); 235 236 /* Detach from the disk list. */ 237 disk_detach(&sc->sc_dk); 238 disk_destroy(&sc->sc_dk); 239 240 /* Unhook the entropy source. */ 241 rnd_detach_source(&sc->sc_rnd_source); 242 243 /* Deregister with PMF */ 244 pmf_device_deregister(sc->sc_dv); 245 246 /* 247 * XXX We can't really flush the cache here, beceause the 248 * XXX device may already be non-existent from the controller's 249 * XXX perspective. 250 */ 251#if 0 252 /* Flush the device's cache. */ 253 if (sc->sc_flush != NULL) 254 if ((*sc->sc_flush)(sc, 0) != 0) 255 aprint_error_dev(&sc->sc_dv, "unable to flush cache\n"); 256#endif 257 mutex_destroy(&sc->sc_mutex); 258} 259 260/* ARGSUSED */ 261static bool 262ld_suspend(device_t dev, const pmf_qual_t *qual) 263{ 264 return ld_shutdown(dev, 0); 265} 266 267/* ARGSUSED */ 268static bool 269ld_shutdown(device_t dev, int flags) 270{ 271 struct ld_softc *sc = device_private(dev); 272 273 if (sc->sc_flush != NULL && (*sc->sc_flush)(sc, LDFL_POLL) != 0) { 274 printf("%s: unable to flush cache\n", device_xname(dev)); 275 return false; 276 } 277 278 return true; 279} 280 281/* ARGSUSED */ 282static int 283ldopen(dev_t dev, int flags, int fmt, struct lwp *l) 284{ 285 struct ld_softc *sc; 286 int error, unit, part; 287 288 unit = DISKUNIT(dev); 289 if ((sc = device_lookup_private(&ld_cd, unit)) == NULL) 290 return (ENXIO); 291 if ((sc->sc_flags & LDF_ENABLED) == 0) 292 return (ENODEV); 293 part = DISKPART(dev); 294 295 mutex_enter(&sc->sc_dk.dk_openlock); 296 297 if (sc->sc_dk.dk_openmask == 0) { 298 /* Load the partition info if not already loaded. */ 299 if ((sc->sc_flags & LDF_VLABEL) == 0) 300 ldgetdisklabel(sc); 301 } 302 303 /* Check that the partition exists. */ 304 if (part != RAW_PART && (part >= sc->sc_dk.dk_label->d_npartitions || 305 sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) { 306 error = ENXIO; 307 goto bad1; 308 } 309 310 /* Ensure only one open at a time. */ 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 error = 0; 323 bad1: 324 mutex_exit(&sc->sc_dk.dk_openlock); 325 return (error); 326} 327 328static int 329ldlastclose(device_t self) 330{ 331 struct ld_softc *sc = device_private(self); 332 333 if (sc->sc_flush != NULL && (*sc->sc_flush)(sc, 0) != 0) 334 aprint_error_dev(self, "unable to flush cache\n"); 335 if ((sc->sc_flags & LDF_KLABEL) == 0) 336 sc->sc_flags &= ~LDF_VLABEL; 337 338 return 0; 339} 340 341/* ARGSUSED */ 342static int 343ldclose(dev_t dev, int flags, int fmt, struct lwp *l) 344{ 345 struct ld_softc *sc; 346 int part, unit; 347 348 unit = DISKUNIT(dev); 349 part = DISKPART(dev); 350 sc = device_lookup_private(&ld_cd, unit); 351 352 mutex_enter(&sc->sc_dk.dk_openlock); 353 354 switch (fmt) { 355 case S_IFCHR: 356 sc->sc_dk.dk_copenmask &= ~(1 << part); 357 break; 358 case S_IFBLK: 359 sc->sc_dk.dk_bopenmask &= ~(1 << part); 360 break; 361 } 362 sc->sc_dk.dk_openmask = 363 sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; 364 365 if (sc->sc_dk.dk_openmask == 0) 366 ldlastclose(sc->sc_dv); 367 368 mutex_exit(&sc->sc_dk.dk_openlock); 369 return (0); 370} 371 372/* ARGSUSED */ 373static int 374ldread(dev_t dev, struct uio *uio, int ioflag) 375{ 376 377 return (physio(ldstrategy, NULL, dev, B_READ, ldminphys, uio)); 378} 379 380/* ARGSUSED */ 381static int 382ldwrite(dev_t dev, struct uio *uio, int ioflag) 383{ 384 385 return (physio(ldstrategy, NULL, dev, B_WRITE, ldminphys, uio)); 386} 387 388/* ARGSUSED */ 389static int 390ldioctl(dev_t dev, u_long cmd, void *addr, int32_t flag, struct lwp *l) 391{ 392 struct ld_softc *sc; 393 int part, unit, error; 394#ifdef __HAVE_OLD_DISKLABEL 395 struct disklabel newlabel; 396#endif 397 struct disklabel *lp; 398 399 unit = DISKUNIT(dev); 400 part = DISKPART(dev); 401 sc = device_lookup_private(&ld_cd, unit); 402 403 error = disk_ioctl(&sc->sc_dk, cmd, addr, flag, l); 404 if (error != EPASSTHROUGH) 405 return (error); 406 407 error = 0; 408 switch (cmd) { 409 case DIOCGDINFO: 410 memcpy(addr, sc->sc_dk.dk_label, sizeof(struct disklabel)); 411 return (0); 412 413#ifdef __HAVE_OLD_DISKLABEL 414 case ODIOCGDINFO: 415 newlabel = *(sc->sc_dk.dk_label); 416 if (newlabel.d_npartitions > OLDMAXPARTITIONS) 417 return ENOTTY; 418 memcpy(addr, &newlabel, sizeof(struct olddisklabel)); 419 return (0); 420#endif 421 422 case DIOCGPART: 423 ((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label; 424 ((struct partinfo *)addr)->part = 425 &sc->sc_dk.dk_label->d_partitions[part]; 426 break; 427 428 case DIOCWDINFO: 429 case DIOCSDINFO: 430#ifdef __HAVE_OLD_DISKLABEL 431 case ODIOCWDINFO: 432 case ODIOCSDINFO: 433 434 if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) { 435 memset(&newlabel, 0, sizeof newlabel); 436 memcpy(&newlabel, addr, sizeof (struct olddisklabel)); 437 lp = &newlabel; 438 } else 439#endif 440 lp = (struct disklabel *)addr; 441 442 if ((flag & FWRITE) == 0) 443 return (EBADF); 444 445 mutex_enter(&sc->sc_dk.dk_openlock); 446 sc->sc_flags |= LDF_LABELLING; 447 448 error = setdisklabel(sc->sc_dk.dk_label, 449 lp, /*sc->sc_dk.dk_openmask : */0, 450 sc->sc_dk.dk_cpulabel); 451 if (error == 0 && (cmd == DIOCWDINFO 452#ifdef __HAVE_OLD_DISKLABEL 453 || cmd == ODIOCWDINFO 454#endif 455 )) 456 error = writedisklabel( 457 MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART), 458 ldstrategy, sc->sc_dk.dk_label, 459 sc->sc_dk.dk_cpulabel); 460 461 sc->sc_flags &= ~LDF_LABELLING; 462 mutex_exit(&sc->sc_dk.dk_openlock); 463 break; 464 465 case DIOCKLABEL: 466 if ((flag & FWRITE) == 0) 467 return (EBADF); 468 if (*(int *)addr) 469 sc->sc_flags |= LDF_KLABEL; 470 else 471 sc->sc_flags &= ~LDF_KLABEL; 472 break; 473 474 case DIOCWLABEL: 475 if ((flag & FWRITE) == 0) 476 return (EBADF); 477 if (*(int *)addr) 478 sc->sc_flags |= LDF_WLABEL; 479 else 480 sc->sc_flags &= ~LDF_WLABEL; 481 break; 482 483 case DIOCGDEFLABEL: 484 ldgetdefaultlabel(sc, (struct disklabel *)addr); 485 break; 486 487#ifdef __HAVE_OLD_DISKLABEL 488 case ODIOCGDEFLABEL: 489 ldgetdefaultlabel(sc, &newlabel); 490 if (newlabel.d_npartitions > OLDMAXPARTITIONS) 491 return ENOTTY; 492 memcpy(addr, &newlabel, sizeof (struct olddisklabel)); 493 break; 494#endif 495 496 case DIOCCACHESYNC: 497 /* 498 * XXX Do we really need to care about having a writable 499 * file descriptor here? 500 */ 501 if ((flag & FWRITE) == 0) 502 error = EBADF; 503 else if (sc->sc_flush) 504 error = (*sc->sc_flush)(sc, 0); 505 else 506 error = 0; /* XXX Error out instead? */ 507 break; 508 509 case DIOCAWEDGE: 510 { 511 struct dkwedge_info *dkw = (void *) addr; 512 513 if ((flag & FWRITE) == 0) 514 return (EBADF); 515 516 /* If the ioctl happens here, the parent is us. */ 517 strlcpy(dkw->dkw_parent, device_xname(sc->sc_dv), 518 sizeof(dkw->dkw_parent)); 519 return (dkwedge_add(dkw)); 520 } 521 522 case DIOCDWEDGE: 523 { 524 struct dkwedge_info *dkw = (void *) addr; 525 526 if ((flag & FWRITE) == 0) 527 return (EBADF); 528 529 /* If the ioctl happens here, the parent is us. */ 530 strlcpy(dkw->dkw_parent, device_xname(sc->sc_dv), 531 sizeof(dkw->dkw_parent)); 532 return (dkwedge_del(dkw)); 533 } 534 535 case DIOCLWEDGES: 536 { 537 struct dkwedge_list *dkwl = (void *) addr; 538 539 return (dkwedge_list(&sc->sc_dk, dkwl, l)); 540 } 541 case DIOCGSTRATEGY: 542 { 543 struct disk_strategy *dks = (void *)addr; 544 545 mutex_enter(&sc->sc_mutex); 546 strlcpy(dks->dks_name, bufq_getstrategyname(sc->sc_bufq), 547 sizeof(dks->dks_name)); 548 mutex_exit(&sc->sc_mutex); 549 dks->dks_paramlen = 0; 550 551 return 0; 552 } 553 case DIOCSSTRATEGY: 554 { 555 struct disk_strategy *dks = (void *)addr; 556 struct bufq_state *new, *old; 557 558 if ((flag & FWRITE) == 0) 559 return EPERM; 560 561 if (dks->dks_param != NULL) 562 return EINVAL; 563 564 dks->dks_name[sizeof(dks->dks_name) - 1] = 0; /* ensure term */ 565 error = bufq_alloc(&new, dks->dks_name, 566 BUFQ_EXACT|BUFQ_SORT_RAWBLOCK); 567 if (error) 568 return error; 569 570 mutex_enter(&sc->sc_mutex); 571 old = sc->sc_bufq; 572 bufq_move(new, old); 573 sc->sc_bufq = new; 574 mutex_exit(&sc->sc_mutex); 575 bufq_free(old); 576 577 return 0; 578 } 579 default: 580 error = ENOTTY; 581 break; 582 } 583 584 return (error); 585} 586 587static void 588ldstrategy(struct buf *bp) 589{ 590 struct ld_softc *sc; 591 struct disklabel *lp; 592 daddr_t blkno; 593 int s, part; 594 595 sc = device_lookup_private(&ld_cd, DISKUNIT(bp->b_dev)); 596 part = DISKPART(bp->b_dev); 597 598 if ((sc->sc_flags & LDF_DETACH) != 0) { 599 bp->b_error = EIO; 600 goto done; 601 } 602 603 lp = sc->sc_dk.dk_label; 604 605 /* 606 * The transfer must be a whole number of blocks and the offset must 607 * not be negative. 608 */ 609 if ((bp->b_bcount % lp->d_secsize) != 0 || bp->b_blkno < 0) { 610 bp->b_error = EINVAL; 611 goto done; 612 } 613 614 /* If it's a null transfer, return immediately. */ 615 if (bp->b_bcount == 0) 616 goto done; 617 618 /* 619 * Do bounds checking and adjust the transfer. If error, process. 620 * If past the end of partition, just return. 621 */ 622 if (part == RAW_PART) { 623 if (bounds_check_with_mediasize(bp, DEV_BSIZE, 624 sc->sc_disksize512) <= 0) 625 goto done; 626 } else { 627 if (bounds_check_with_label(&sc->sc_dk, bp, 628 (sc->sc_flags & (LDF_WLABEL | LDF_LABELLING)) != 0) <= 0) 629 goto done; 630 } 631 632 /* 633 * Convert the block number to absolute and put it in terms 634 * of the device's logical block size. 635 */ 636 if (lp->d_secsize == DEV_BSIZE) 637 blkno = bp->b_blkno; 638 else if (lp->d_secsize > DEV_BSIZE) 639 blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE); 640 else 641 blkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize); 642 643 if (part != RAW_PART) 644 blkno += lp->d_partitions[part].p_offset; 645 646 bp->b_rawblkno = blkno; 647 648 s = splbio(); 649 ldstart(sc, bp); 650 splx(s); 651 return; 652 653 done: 654 bp->b_resid = bp->b_bcount; 655 biodone(bp); 656} 657 658static void 659ldstart(struct ld_softc *sc, struct buf *bp) 660{ 661 int error; 662 663 mutex_enter(&sc->sc_mutex); 664 665 if (bp != NULL) 666 bufq_put(sc->sc_bufq, bp); 667 668 while (sc->sc_queuecnt < sc->sc_maxqueuecnt) { 669 /* See if there is work to do. */ 670 if ((bp = bufq_peek(sc->sc_bufq)) == NULL) 671 break; 672 673 disk_busy(&sc->sc_dk); 674 sc->sc_queuecnt++; 675 676 if (__predict_true((error = (*sc->sc_start)(sc, bp)) == 0)) { 677 /* 678 * The back-end is running the job; remove it from 679 * the queue. 680 */ 681 (void) bufq_get(sc->sc_bufq); 682 } else { 683 disk_unbusy(&sc->sc_dk, 0, (bp->b_flags & B_READ)); 684 sc->sc_queuecnt--; 685 if (error == EAGAIN) { 686 /* 687 * Temporary resource shortage in the 688 * back-end; just defer the job until 689 * later. 690 * 691 * XXX We might consider a watchdog timer 692 * XXX to make sure we are kicked into action. 693 */ 694 break; 695 } else { 696 (void) bufq_get(sc->sc_bufq); 697 bp->b_error = error; 698 bp->b_resid = bp->b_bcount; 699 mutex_exit(&sc->sc_mutex); 700 biodone(bp); 701 mutex_enter(&sc->sc_mutex); 702 } 703 } 704 } 705 706 mutex_exit(&sc->sc_mutex); 707} 708 709void 710lddone(struct ld_softc *sc, struct buf *bp) 711{ 712 713 if (bp->b_error != 0) { 714 diskerr(bp, "ld", "error", LOG_PRINTF, 0, sc->sc_dk.dk_label); 715 printf("\n"); 716 } 717 718 disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid, 719 (bp->b_flags & B_READ)); 720 rnd_add_uint32(&sc->sc_rnd_source, bp->b_rawblkno); 721 biodone(bp); 722 723 mutex_enter(&sc->sc_mutex); 724 if (--sc->sc_queuecnt <= sc->sc_maxqueuecnt) { 725 if ((sc->sc_flags & LDF_DRAIN) != 0) { 726 sc->sc_flags &= ~LDF_DRAIN; 727 wakeup(&sc->sc_queuecnt); 728 } 729 mutex_exit(&sc->sc_mutex); 730 ldstart(sc, NULL); 731 } else 732 mutex_exit(&sc->sc_mutex); 733} 734 735static int 736ldsize(dev_t dev) 737{ 738 struct ld_softc *sc; 739 int part, unit, omask, size; 740 741 unit = DISKUNIT(dev); 742 if ((sc = device_lookup_private(&ld_cd, unit)) == NULL) 743 return (ENODEV); 744 if ((sc->sc_flags & LDF_ENABLED) == 0) 745 return (ENODEV); 746 part = DISKPART(dev); 747 748 omask = sc->sc_dk.dk_openmask & (1 << part); 749 750 if (omask == 0 && ldopen(dev, 0, S_IFBLK, NULL) != 0) 751 return (-1); 752 else if (sc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP) 753 size = -1; 754 else 755 size = sc->sc_dk.dk_label->d_partitions[part].p_size * 756 (sc->sc_dk.dk_label->d_secsize / DEV_BSIZE); 757 if (omask == 0 && ldclose(dev, 0, S_IFBLK, NULL) != 0) 758 return (-1); 759 760 return (size); 761} 762 763/* 764 * Load the label information from the specified device. 765 */ 766static void 767ldgetdisklabel(struct ld_softc *sc) 768{ 769 const char *errstring; 770 771 ldgetdefaultlabel(sc, sc->sc_dk.dk_label); 772 773 /* Call the generic disklabel extraction routine. */ 774 errstring = readdisklabel(MAKEDISKDEV(0, device_unit(sc->sc_dv), 775 RAW_PART), ldstrategy, sc->sc_dk.dk_label, sc->sc_dk.dk_cpulabel); 776 if (errstring != NULL) 777 printf("%s: %s\n", device_xname(sc->sc_dv), errstring); 778 779 /* In-core label now valid. */ 780 sc->sc_flags |= LDF_VLABEL; 781} 782 783/* 784 * Construct a ficticious label. 785 */ 786static void 787ldgetdefaultlabel(struct ld_softc *sc, struct disklabel *lp) 788{ 789 790 memset(lp, 0, sizeof(struct disklabel)); 791 792 lp->d_secsize = sc->sc_secsize; 793 lp->d_ntracks = sc->sc_nheads; 794 lp->d_nsectors = sc->sc_nsectors; 795 lp->d_ncylinders = sc->sc_ncylinders; 796 lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 797 lp->d_type = DTYPE_LD; 798 strlcpy(lp->d_typename, "unknown", sizeof(lp->d_typename)); 799 strlcpy(lp->d_packname, "fictitious", sizeof(lp->d_packname)); 800 lp->d_secperunit = sc->sc_secperunit; 801 lp->d_rpm = 7200; 802 lp->d_interleave = 1; 803 lp->d_flags = 0; 804 805 lp->d_partitions[RAW_PART].p_offset = 0; 806 lp->d_partitions[RAW_PART].p_size = 807 lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); 808 lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 809 lp->d_npartitions = RAW_PART + 1; 810 811 lp->d_magic = DISKMAGIC; 812 lp->d_magic2 = DISKMAGIC; 813 lp->d_checksum = dkcksum(lp); 814} 815 816/* 817 * Take a dump. 818 */ 819static int 820lddump(dev_t dev, daddr_t blkno, void *vav, size_t size) 821{ 822 char *va = vav; 823 struct ld_softc *sc; 824 struct disklabel *lp; 825 int unit, part, nsects, sectoff, towrt, nblk, maxblkcnt, rv; 826 static int dumping; 827 828 unit = DISKUNIT(dev); 829 if ((sc = device_lookup_private(&ld_cd, unit)) == NULL) 830 return (ENXIO); 831 if ((sc->sc_flags & LDF_ENABLED) == 0) 832 return (ENODEV); 833 if (sc->sc_dump == NULL) 834 return (ENXIO); 835 836 /* Check if recursive dump; if so, punt. */ 837 if (dumping) 838 return (EFAULT); 839 dumping = 1; 840 841 /* Convert to disk sectors. Request must be a multiple of size. */ 842 part = DISKPART(dev); 843 lp = sc->sc_dk.dk_label; 844 if ((size % lp->d_secsize) != 0) 845 return (EFAULT); 846 towrt = size / lp->d_secsize; 847 blkno = dbtob(blkno) / lp->d_secsize; /* blkno in DEV_BSIZE units */ 848 849 nsects = lp->d_partitions[part].p_size; 850 sectoff = lp->d_partitions[part].p_offset; 851 852 /* Check transfer bounds against partition size. */ 853 if ((blkno < 0) || ((blkno + towrt) > nsects)) 854 return (EINVAL); 855 856 /* Offset block number to start of partition. */ 857 blkno += sectoff; 858 859 /* Start dumping and return when done. */ 860 maxblkcnt = sc->sc_maxxfer / sc->sc_secsize - 1; 861 while (towrt > 0) { 862 nblk = min(maxblkcnt, towrt); 863 864 if ((rv = (*sc->sc_dump)(sc, va, blkno, nblk)) != 0) 865 return (rv); 866 867 towrt -= nblk; 868 blkno += nblk; 869 va += nblk * sc->sc_secsize; 870 } 871 872 dumping = 0; 873 return (0); 874} 875 876/* 877 * Adjust the size of a transfer. 878 */ 879static void 880ldminphys(struct buf *bp) 881{ 882 struct ld_softc *sc; 883 884 sc = device_lookup_private(&ld_cd, DISKUNIT(bp->b_dev)); 885 886 if (bp->b_bcount > sc->sc_maxxfer) 887 bp->b_bcount = sc->sc_maxxfer; 888 minphys(bp); 889} 890 891static void 892ld_set_properties(struct ld_softc *ld) 893{ 894 prop_dictionary_t disk_info, odisk_info, geom; 895 896 disk_info = prop_dictionary_create(); 897 898 geom = prop_dictionary_create(); 899 900 prop_dictionary_set_uint64(geom, "sectors-per-unit", 901 ld->sc_secperunit); 902 903 prop_dictionary_set_uint32(geom, "sector-size", 904 ld->sc_secsize); 905 906 prop_dictionary_set_uint16(geom, "sectors-per-track", 907 ld->sc_nsectors); 908 909 prop_dictionary_set_uint16(geom, "tracks-per-cylinder", 910 ld->sc_nheads); 911 912 prop_dictionary_set_uint64(geom, "cylinders-per-unit", 913 ld->sc_ncylinders); 914 915 prop_dictionary_set(disk_info, "geometry", geom); 916 prop_object_release(geom); 917 918 prop_dictionary_set(device_properties(ld->sc_dv), 919 "disk-info", disk_info); 920 921 /* 922 * Don't release disk_info here; we keep a reference to it. 923 * disk_detach() will release it when we go away. 924 */ 925 926 odisk_info = ld->sc_dk.dk_info; 927 ld->sc_dk.dk_info = disk_info; 928 if (odisk_info) 929 prop_object_release(odisk_info); 930} 931 932static void 933ld_config_interrupts(device_t d) 934{ 935 struct ld_softc *sc = device_private(d); 936 dkwedge_discover(&sc->sc_dk); 937} 938