1 /* $NetBSD: ld.c,v 1.33 2004/10/28 07:07:39 yamt 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.33 2004/10/28 07:07:39 yamt 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 buf[9]; 105 106 if ((sc->sc_flags & LDF_ENABLED) == 0) { 107 printf("%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(buf, sizeof(buf), sc->sc_secperunit * 144 sc->sc_secsize); 145 printf("%s: %s, %d cyl, %d head, %d sec, %d bytes/sect x %"PRIu64" sectors\n", 146 sc->sc_dv.dv_xname, buf, 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 max) 166 { 167 int s; 168 169 s = splbio(); 170 sc->sc_maxqueuecnt = max; 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 struct buf *bp; 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", sc->sc_dv.dv_xname); 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 while ((bp = BUFQ_GET(&sc->sc_bufq)) != NULL) { 222 bp->b_error = EIO; 223 bp->b_flags |= B_ERROR; 224 bp->b_resid = bp->b_bcount; 225 biodone(bp); 226 } 227 bufq_free(&sc->sc_bufq); 228 splx(s); 229 230 /* Nuke the vnodes for any open instances. */ 231 for (i = 0; i < MAXPARTITIONS; i++) { 232 mn = DISKMINOR(sc->sc_dv.dv_unit, i); 233 vdevgone(bmaj, mn, mn, VBLK); 234 vdevgone(cmaj, mn, mn, VCHR); 235 } 236 237 /* Delete all of our wedges. */ 238 dkwedge_delall(&sc->sc_dk); 239 240 /* Detach from the disk list. */ 241 disk_detach(&sc->sc_dk); 242 243 #if NRND > 0 244 /* Unhook the entropy source. */ 245 rnd_detach_source(&sc->sc_rnd_source); 246 #endif 247 248 /* 249 * XXX We can't really flush the cache here, beceause the 250 * XXX device may already be non-existent from the controller's 251 * XXX perspective. 252 */ 253 #if 0 254 /* Flush the device's cache. */ 255 if (sc->sc_flush != NULL) 256 if ((*sc->sc_flush)(sc) != 0) 257 printf("%s: unable to flush cache\n", 258 sc->sc_dv.dv_xname); 259 #endif 260 } 261 262 /* ARGSUSED */ 263 static void 264 ldshutdown(void *cookie) 265 { 266 struct ld_softc *sc; 267 int i; 268 269 for (i = 0; i < ld_cd.cd_ndevs; i++) { 270 if ((sc = device_lookup(&ld_cd, i)) == NULL) 271 continue; 272 if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0) 273 printf("%s: unable to flush cache\n", 274 sc->sc_dv.dv_xname); 275 } 276 } 277 278 /* ARGSUSED */ 279 static int 280 ldopen(dev_t dev, int flags, int fmt, struct proc *p) 281 { 282 struct ld_softc *sc; 283 int error, unit, part; 284 285 unit = DISKUNIT(dev); 286 if ((sc = device_lookup(&ld_cd, unit)) == NULL) 287 return (ENXIO); 288 if ((sc->sc_flags & LDF_ENABLED) == 0) 289 return (ENODEV); 290 part = DISKPART(dev); 291 292 if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE, NULL)) != 0) 293 return (error); 294 295 if (sc->sc_dk.dk_openmask == 0) { 296 /* Load the partition info if not already loaded. */ 297 if ((sc->sc_flags & LDF_VLABEL) == 0) 298 ldgetdisklabel(sc); 299 } 300 301 /* Check that the partition exists. */ 302 if (part != RAW_PART && (part >= sc->sc_dk.dk_label->d_npartitions || 303 sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) { 304 error = ENXIO; 305 goto bad1; 306 } 307 308 /* Ensure only one open at a time. */ 309 switch (fmt) { 310 case S_IFCHR: 311 sc->sc_dk.dk_copenmask |= (1 << part); 312 break; 313 case S_IFBLK: 314 sc->sc_dk.dk_bopenmask |= (1 << part); 315 break; 316 } 317 sc->sc_dk.dk_openmask = 318 sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; 319 320 (void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL); 321 return (0); 322 323 bad1: 324 (void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL); 325 return (error); 326 } 327 328 /* ARGSUSED */ 329 static int 330 ldclose(dev_t dev, int flags, int fmt, struct proc *p) 331 { 332 struct ld_softc *sc; 333 int error, part, unit; 334 335 unit = DISKUNIT(dev); 336 part = DISKPART(dev); 337 sc = device_lookup(&ld_cd, unit); 338 339 if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE, NULL)) != 0) 340 return (error); 341 342 switch (fmt) { 343 case S_IFCHR: 344 sc->sc_dk.dk_copenmask &= ~(1 << part); 345 break; 346 case S_IFBLK: 347 sc->sc_dk.dk_bopenmask &= ~(1 << part); 348 break; 349 } 350 sc->sc_dk.dk_openmask = 351 sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; 352 353 if (sc->sc_dk.dk_openmask == 0) { 354 if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0) 355 printf("%s: unable to flush cache\n", 356 sc->sc_dv.dv_xname); 357 if ((sc->sc_flags & LDF_KLABEL) == 0) 358 sc->sc_flags &= ~LDF_VLABEL; 359 } 360 361 (void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL); 362 return (0); 363 } 364 365 /* ARGSUSED */ 366 static int 367 ldread(dev_t dev, struct uio *uio, int ioflag) 368 { 369 370 return (physio(ldstrategy, NULL, dev, B_READ, ldminphys, uio)); 371 } 372 373 /* ARGSUSED */ 374 static int 375 ldwrite(dev_t dev, struct uio *uio, int ioflag) 376 { 377 378 return (physio(ldstrategy, NULL, dev, B_WRITE, ldminphys, uio)); 379 } 380 381 /* ARGSUSED */ 382 static int 383 ldioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p) 384 { 385 struct ld_softc *sc; 386 int part, unit, error; 387 #ifdef __HAVE_OLD_DISKLABEL 388 struct disklabel newlabel; 389 #endif 390 struct disklabel *lp; 391 392 unit = DISKUNIT(dev); 393 part = DISKPART(dev); 394 sc = device_lookup(&ld_cd, unit); 395 error = 0; 396 397 switch (cmd) { 398 case DIOCGDINFO: 399 memcpy(addr, sc->sc_dk.dk_label, sizeof(struct disklabel)); 400 return (0); 401 402 #ifdef __HAVE_OLD_DISKLABEL 403 case ODIOCGDINFO: 404 newlabel = *(sc->sc_dk.dk_label); 405 if (newlabel.d_npartitions > OLDMAXPARTITIONS) 406 return ENOTTY; 407 memcpy(addr, &newlabel, sizeof(struct olddisklabel)); 408 return (0); 409 #endif 410 411 case DIOCGPART: 412 ((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label; 413 ((struct partinfo *)addr)->part = 414 &sc->sc_dk.dk_label->d_partitions[part]; 415 break; 416 417 case DIOCWDINFO: 418 case DIOCSDINFO: 419 #ifdef __HAVE_OLD_DISKLABEL 420 case ODIOCWDINFO: 421 case ODIOCSDINFO: 422 423 if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) { 424 memset(&newlabel, 0, sizeof newlabel); 425 memcpy(&newlabel, addr, sizeof (struct olddisklabel)); 426 lp = &newlabel; 427 } else 428 #endif 429 lp = (struct disklabel *)addr; 430 431 if ((flag & FWRITE) == 0) 432 return (EBADF); 433 434 if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE, 435 NULL)) != 0) 436 return (error); 437 sc->sc_flags |= LDF_LABELLING; 438 439 error = setdisklabel(sc->sc_dk.dk_label, 440 lp, /*sc->sc_dk.dk_openmask : */0, 441 sc->sc_dk.dk_cpulabel); 442 if (error == 0 && (cmd == DIOCWDINFO 443 #ifdef __HAVE_OLD_DISKLABEL 444 || cmd == ODIOCWDINFO 445 #endif 446 )) 447 error = writedisklabel( 448 MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART), 449 ldstrategy, sc->sc_dk.dk_label, 450 sc->sc_dk.dk_cpulabel); 451 452 sc->sc_flags &= ~LDF_LABELLING; 453 (void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL); 454 break; 455 456 case DIOCKLABEL: 457 if ((flag & FWRITE) == 0) 458 return (EBADF); 459 if (*(int *)addr) 460 sc->sc_flags |= LDF_KLABEL; 461 else 462 sc->sc_flags &= ~LDF_KLABEL; 463 break; 464 465 case DIOCWLABEL: 466 if ((flag & FWRITE) == 0) 467 return (EBADF); 468 if (*(int *)addr) 469 sc->sc_flags |= LDF_WLABEL; 470 else 471 sc->sc_flags &= ~LDF_WLABEL; 472 break; 473 474 case DIOCGDEFLABEL: 475 ldgetdefaultlabel(sc, (struct disklabel *)addr); 476 break; 477 478 #ifdef __HAVE_OLD_DISKLABEL 479 case ODIOCGDEFLABEL: 480 ldgetdefaultlabel(sc, &newlabel); 481 if (newlabel.d_npartitions > OLDMAXPARTITIONS) 482 return ENOTTY; 483 memcpy(addr, &newlabel, sizeof (struct olddisklabel)); 484 break; 485 #endif 486 487 case DIOCCACHESYNC: 488 /* 489 * XXX Do we really need to care about having a writable 490 * file descriptor here? 491 */ 492 if ((flag & FWRITE) == 0) 493 error = EBADF; 494 else if (sc->sc_flush) 495 error = (*sc->sc_flush)(sc); 496 else 497 error = 0; /* XXX Error out instead? */ 498 break; 499 500 case DIOCAWEDGE: 501 { 502 struct dkwedge_info *dkw = (void *) addr; 503 504 if ((flag & FWRITE) == 0) 505 return (EBADF); 506 507 /* If the ioctl happens here, the parent is us. */ 508 strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname); 509 return (dkwedge_add(dkw)); 510 } 511 512 case DIOCDWEDGE: 513 { 514 struct dkwedge_info *dkw = (void *) addr; 515 516 if ((flag & FWRITE) == 0) 517 return (EBADF); 518 519 /* If the ioctl happens here, the parent is us. */ 520 strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname); 521 return (dkwedge_del(dkw)); 522 } 523 524 case DIOCLWEDGES: 525 { 526 struct dkwedge_list *dkwl = (void *) addr; 527 528 return (dkwedge_list(&sc->sc_dk, dkwl, p)); 529 } 530 531 default: 532 error = ENOTTY; 533 break; 534 } 535 536 return (error); 537 } 538 539 static void 540 ldstrategy(struct buf *bp) 541 { 542 struct ld_softc *sc; 543 struct disklabel *lp; 544 daddr_t blkno; 545 int s, part; 546 547 sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev)); 548 part = DISKPART(bp->b_dev); 549 550 if ((sc->sc_flags & LDF_DETACH) != 0) { 551 bp->b_error = EIO; 552 goto bad; 553 } 554 555 lp = sc->sc_dk.dk_label; 556 557 /* 558 * The transfer must be a whole number of blocks and the offset must 559 * not be negative. 560 */ 561 if ((bp->b_bcount % lp->d_secsize) != 0 || bp->b_blkno < 0) { 562 bp->b_error = EINVAL; 563 goto bad; 564 } 565 566 /* If it's a null transfer, return immediately. */ 567 if (bp->b_bcount == 0) 568 goto done; 569 570 /* 571 * Do bounds checking and adjust the transfer. If error, process. 572 * If past the end of partition, just return. 573 */ 574 if (part != RAW_PART && 575 bounds_check_with_label(&sc->sc_dk, bp, 576 (sc->sc_flags & (LDF_WLABEL | LDF_LABELLING)) != 0) <= 0) { 577 goto done; 578 } 579 580 /* 581 * Convert the block number to absolute and put it in terms 582 * of the device's logical block size. 583 */ 584 if (lp->d_secsize == DEV_BSIZE) 585 blkno = bp->b_blkno; 586 else if (lp->d_secsize > DEV_BSIZE) 587 blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE); 588 else 589 blkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize); 590 591 if (part != RAW_PART) 592 blkno += lp->d_partitions[part].p_offset; 593 594 bp->b_rawblkno = blkno; 595 596 s = splbio(); 597 BUFQ_PUT(&sc->sc_bufq, bp); 598 ldstart(sc); 599 splx(s); 600 return; 601 602 bad: 603 bp->b_flags |= B_ERROR; 604 done: 605 bp->b_resid = bp->b_bcount; 606 biodone(bp); 607 } 608 609 static void 610 ldstart(struct ld_softc *sc) 611 { 612 struct buf *bp; 613 int error; 614 615 while (sc->sc_queuecnt < sc->sc_maxqueuecnt) { 616 /* See if there is work to do. */ 617 if ((bp = BUFQ_PEEK(&sc->sc_bufq)) == NULL) 618 break; 619 620 disk_busy(&sc->sc_dk); 621 sc->sc_queuecnt++; 622 623 if (__predict_true((error = (*sc->sc_start)(sc, bp)) == 0)) { 624 /* 625 * The back-end is running the job; remove it from 626 * the queue. 627 */ 628 (void) BUFQ_GET(&sc->sc_bufq); 629 } else { 630 disk_unbusy(&sc->sc_dk, 0, (bp->b_flags & B_READ)); 631 sc->sc_queuecnt--; 632 if (error == EAGAIN) { 633 /* 634 * Temporary resource shortage in the 635 * back-end; just defer the job until 636 * later. 637 * 638 * XXX We might consider a watchdog timer 639 * XXX to make sure we are kicked into action. 640 */ 641 break; 642 } else { 643 (void) BUFQ_GET(&sc->sc_bufq); 644 bp->b_error = error; 645 bp->b_flags |= B_ERROR; 646 bp->b_resid = bp->b_bcount; 647 biodone(bp); 648 } 649 } 650 } 651 } 652 653 void 654 lddone(struct ld_softc *sc, struct buf *bp) 655 { 656 657 if ((bp->b_flags & B_ERROR) != 0) { 658 diskerr(bp, "ld", "error", LOG_PRINTF, 0, sc->sc_dk.dk_label); 659 printf("\n"); 660 } 661 662 disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid, 663 (bp->b_flags & B_READ)); 664 #if NRND > 0 665 rnd_add_uint32(&sc->sc_rnd_source, bp->b_rawblkno); 666 #endif 667 biodone(bp); 668 669 if (--sc->sc_queuecnt <= sc->sc_maxqueuecnt) { 670 if ((sc->sc_flags & LDF_DRAIN) != 0) { 671 sc->sc_flags &= ~LDF_DRAIN; 672 wakeup(&sc->sc_queuecnt); 673 } 674 ldstart(sc); 675 } 676 } 677 678 static int 679 ldsize(dev_t dev) 680 { 681 struct ld_softc *sc; 682 int part, unit, omask, size; 683 684 unit = DISKUNIT(dev); 685 if ((sc = device_lookup(&ld_cd, unit)) == NULL) 686 return (ENODEV); 687 if ((sc->sc_flags & LDF_ENABLED) == 0) 688 return (ENODEV); 689 part = DISKPART(dev); 690 691 omask = sc->sc_dk.dk_openmask & (1 << part); 692 693 if (omask == 0 && ldopen(dev, 0, S_IFBLK, NULL) != 0) 694 return (-1); 695 else if (sc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP) 696 size = -1; 697 else 698 size = sc->sc_dk.dk_label->d_partitions[part].p_size * 699 (sc->sc_dk.dk_label->d_secsize / DEV_BSIZE); 700 if (omask == 0 && ldclose(dev, 0, S_IFBLK, NULL) != 0) 701 return (-1); 702 703 return (size); 704 } 705 706 /* 707 * Load the label information from the specified device. 708 */ 709 static void 710 ldgetdisklabel(struct ld_softc *sc) 711 { 712 const char *errstring; 713 714 ldgetdefaultlabel(sc, sc->sc_dk.dk_label); 715 716 /* Call the generic disklabel extraction routine. */ 717 errstring = readdisklabel(MAKEDISKDEV(0, sc->sc_dv.dv_unit, RAW_PART), 718 ldstrategy, sc->sc_dk.dk_label, sc->sc_dk.dk_cpulabel); 719 if (errstring != NULL) 720 printf("%s: %s\n", sc->sc_dv.dv_xname, errstring); 721 722 /* In-core label now valid. */ 723 sc->sc_flags |= LDF_VLABEL; 724 } 725 726 /* 727 * Construct a ficticious label. 728 */ 729 static void 730 ldgetdefaultlabel(struct ld_softc *sc, struct disklabel *lp) 731 { 732 733 memset(lp, 0, sizeof(struct disklabel)); 734 735 lp->d_secsize = sc->sc_secsize; 736 lp->d_ntracks = sc->sc_nheads; 737 lp->d_nsectors = sc->sc_nsectors; 738 lp->d_ncylinders = sc->sc_ncylinders; 739 lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 740 lp->d_type = DTYPE_LD; 741 strlcpy(lp->d_typename, "unknown", sizeof(lp->d_typename)); 742 strlcpy(lp->d_packname, "fictitious", sizeof(lp->d_packname)); 743 lp->d_secperunit = sc->sc_secperunit; 744 lp->d_rpm = 7200; 745 lp->d_interleave = 1; 746 lp->d_flags = 0; 747 748 lp->d_partitions[RAW_PART].p_offset = 0; 749 lp->d_partitions[RAW_PART].p_size = 750 lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); 751 lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 752 lp->d_npartitions = RAW_PART + 1; 753 754 lp->d_magic = DISKMAGIC; 755 lp->d_magic2 = DISKMAGIC; 756 lp->d_checksum = dkcksum(lp); 757 } 758 759 /* 760 * Take a dump. 761 */ 762 static int 763 lddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size) 764 { 765 struct ld_softc *sc; 766 struct disklabel *lp; 767 int unit, part, nsects, sectoff, towrt, nblk, maxblkcnt, rv; 768 static int dumping; 769 770 unit = DISKUNIT(dev); 771 if ((sc = device_lookup(&ld_cd, unit)) == NULL) 772 return (ENXIO); 773 if ((sc->sc_flags & LDF_ENABLED) == 0) 774 return (ENODEV); 775 if (sc->sc_dump == NULL) 776 return (ENXIO); 777 778 /* Check if recursive dump; if so, punt. */ 779 if (dumping) 780 return (EFAULT); 781 dumping = 1; 782 783 /* Convert to disk sectors. Request must be a multiple of size. */ 784 part = DISKPART(dev); 785 lp = sc->sc_dk.dk_label; 786 if ((size % lp->d_secsize) != 0) 787 return (EFAULT); 788 towrt = size / lp->d_secsize; 789 blkno = dbtob(blkno) / lp->d_secsize; /* blkno in DEV_BSIZE units */ 790 791 nsects = lp->d_partitions[part].p_size; 792 sectoff = lp->d_partitions[part].p_offset; 793 794 /* Check transfer bounds against partition size. */ 795 if ((blkno < 0) || ((blkno + towrt) > nsects)) 796 return (EINVAL); 797 798 /* Offset block number to start of partition. */ 799 blkno += sectoff; 800 801 /* Start dumping and return when done. */ 802 maxblkcnt = sc->sc_maxxfer / sc->sc_secsize - 1; 803 while (towrt > 0) { 804 nblk = min(maxblkcnt, towrt); 805 806 if ((rv = (*sc->sc_dump)(sc, va, blkno, nblk)) != 0) 807 return (rv); 808 809 towrt -= nblk; 810 blkno += nblk; 811 va += nblk * sc->sc_secsize; 812 } 813 814 dumping = 0; 815 return (0); 816 } 817 818 /* 819 * Adjust the size of a transfer. 820 */ 821 static void 822 ldminphys(struct buf *bp) 823 { 824 struct ld_softc *sc; 825 826 sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev)); 827 828 if (bp->b_bcount > sc->sc_maxxfer) 829 bp->b_bcount = sc->sc_maxxfer; 830 minphys(bp); 831 } 832