Annotation of sys/dev/ramdisk.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: ramdisk.c,v 1.37 2007/06/20 18:15:46 deraadt Exp $ */
! 2: /* $NetBSD: ramdisk.c,v 1.8 1996/04/12 08:30:09 leo Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 1995 Gordon W. Ross, Leo Weppelman.
! 6: * All rights reserved.
! 7: *
! 8: * Redistribution and use in source and binary forms, with or without
! 9: * modification, are permitted provided that the following conditions
! 10: * are met:
! 11: * 1. Redistributions of source code must retain the above copyright
! 12: * notice, this list of conditions and the following disclaimer.
! 13: * 2. Redistributions in binary form must reproduce the above copyright
! 14: * notice, this list of conditions and the following disclaimer in the
! 15: * documentation and/or other materials provided with the distribution.
! 16: * 3. The name of the author may not be used to endorse or promote products
! 17: * derived from this software without specific prior written permission.
! 18: * 4. All advertising materials mentioning features or use of this software
! 19: * must display the following acknowledgement:
! 20: * This product includes software developed by
! 21: * Gordon W. Ross and Leo Weppelman.
! 22: *
! 23: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 24: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 25: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 26: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 27: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
! 28: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
! 29: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
! 30: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 31: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
! 32: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 33: */
! 34:
! 35: /*
! 36: * This implements a general-purpose RAM-disk.
! 37: * See ramdisk.h for notes on the config types.
! 38: *
! 39: * Note that this driver provides the same functionality
! 40: * as the MFS filesystem hack, but this is better because
! 41: * you can use this for any filesystem type you'd like!
! 42: *
! 43: * Credit for most of the kmem ramdisk code goes to:
! 44: * Leo Weppelman (atari) and Phil Nelson (pc532)
! 45: * Credit for the ideas behind the "user space RAM" code goes
! 46: * to the authors of the MFS implementation.
! 47: */
! 48:
! 49: #include <sys/param.h>
! 50: #include <sys/kernel.h>
! 51: #include <sys/malloc.h>
! 52: #include <sys/systm.h>
! 53: #include <sys/buf.h>
! 54: #include <sys/device.h>
! 55: #include <sys/file.h>
! 56: #include <sys/disk.h>
! 57: #include <sys/proc.h>
! 58: #include <sys/conf.h>
! 59: #include <sys/disklabel.h>
! 60: #include <sys/dkio.h>
! 61:
! 62: #include <uvm/uvm_extern.h>
! 63:
! 64: #include <dev/ramdisk.h>
! 65:
! 66: /*
! 67: * By default, include the user-space functionality.
! 68: * Use: option RAMDISK_SERVER=0 to turn it off.
! 69: */
! 70: #if !defined(RAMDISK_SERVER) && !defined(SMALL_KERNEL)
! 71: #define RAMDISK_SERVER 1
! 72: #endif
! 73:
! 74: /*
! 75: * XXX: the "control" unit is (base unit + 16).
! 76: * We should just use the cdev as the "control", but
! 77: * that interferes with the security stuff preventing
! 78: * simultaneous use of raw and block devices.
! 79: *
! 80: * XXX Assumption: 16 RAM-disks are enough!
! 81: */
! 82: #define RD_MAX_UNITS 0x10
! 83: #define RD_IS_CTRL(dev) (DISKPART(dev) == RAW_PART)
! 84:
! 85: /* autoconfig stuff... */
! 86:
! 87: struct rd_softc {
! 88: struct device sc_dev; /* REQUIRED first entry */
! 89: struct disk sc_dkdev; /* hook for generic disk handling */
! 90: struct rd_conf sc_rd;
! 91: #if RAMDISK_SERVER
! 92: struct buf *sc_buflist;
! 93: #endif
! 94: };
! 95: /* shorthand for fields in sc_rd: */
! 96: #define sc_addr sc_rd.rd_addr
! 97: #define sc_size sc_rd.rd_size
! 98: #define sc_type sc_rd.rd_type
! 99:
! 100: void rdattach(int);
! 101: void rd_attach(struct device *, struct device *, void *);
! 102: void rdgetdisklabel(dev_t, struct rd_softc *, struct disklabel *, int);
! 103:
! 104: /*
! 105: * Some ports (like i386) use a swapgeneric that wants to
! 106: * snoop around in this rd_cd structure. It is preserved
! 107: * (for now) to remain compatible with such practice.
! 108: * XXX - that practice is questionable...
! 109: */
! 110: struct cfdriver rd_cd = {
! 111: NULL, "rd", DV_DULL
! 112: };
! 113:
! 114: void rdstrategy(struct buf *bp);
! 115: struct dkdriver rddkdriver = { rdstrategy };
! 116:
! 117: int ramdisk_ndevs;
! 118: void *ramdisk_devs[RD_MAX_UNITS];
! 119:
! 120: /*
! 121: * This is called if we are configured as a pseudo-device
! 122: */
! 123: void
! 124: rdattach(n)
! 125: int n;
! 126: {
! 127: struct rd_softc *sc;
! 128: int i;
! 129:
! 130: #ifdef DIAGNOSTIC
! 131: if (ramdisk_ndevs) {
! 132: printf("ramdisk: multiple attach calls?\n");
! 133: return;
! 134: }
! 135: #endif
! 136:
! 137: /* XXX: Are we supposed to provide a default? */
! 138: if (n <= 1)
! 139: n = 1;
! 140: if (n > RD_MAX_UNITS)
! 141: n = RD_MAX_UNITS;
! 142: ramdisk_ndevs = n;
! 143:
! 144: /* XXX: Fake-up rd_cd (see above) */
! 145: rd_cd.cd_ndevs = ramdisk_ndevs;
! 146: rd_cd.cd_devs = ramdisk_devs;
! 147:
! 148: /* Attach as if by autoconfig. */
! 149: for (i = 0; i < n; i++) {
! 150: sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK);
! 151: bzero((caddr_t)sc, sizeof(*sc));
! 152: if (snprintf(sc->sc_dev.dv_xname, sizeof(sc->sc_dev.dv_xname),
! 153: "rd%d", i) >= sizeof(sc->sc_dev.dv_xname)) {
! 154: printf("rdattach: device name too long\n");
! 155: free(sc, M_DEVBUF);
! 156: return;
! 157: }
! 158: ramdisk_devs[i] = sc;
! 159: sc->sc_dev.dv_unit = i;
! 160: rd_attach(NULL, &sc->sc_dev, NULL);
! 161: }
! 162: }
! 163:
! 164: void
! 165: rd_attach(parent, self, aux)
! 166: struct device *parent, *self;
! 167: void *aux;
! 168: {
! 169: struct rd_softc *sc = (struct rd_softc *)self;
! 170:
! 171: /* XXX - Could accept aux info here to set the config. */
! 172: #ifdef RAMDISK_HOOKS
! 173: /*
! 174: * This external function might setup a pre-loaded disk.
! 175: * All it would need to do is setup the rd_conf struct.
! 176: * See sys/arch/sun3/dev/rd_root.c for an example.
! 177: */
! 178: rd_attach_hook(sc->sc_dev.dv_unit, &sc->sc_rd);
! 179: #endif
! 180:
! 181: /*
! 182: * Initialize and attach the disk structure.
! 183: */
! 184: sc->sc_dkdev.dk_driver = &rddkdriver;
! 185: sc->sc_dkdev.dk_name = sc->sc_dev.dv_xname;
! 186: disk_attach(&sc->sc_dkdev);
! 187: }
! 188:
! 189: /*
! 190: * operational routines:
! 191: * open, close, read, write, strategy,
! 192: * ioctl, dump, size
! 193: */
! 194:
! 195: #if RAMDISK_SERVER
! 196: int rd_server_loop(struct rd_softc *sc);
! 197: int rd_ioctl_server(struct rd_softc *sc,
! 198: struct rd_conf *urd, struct proc *proc);
! 199: #endif
! 200: int rd_ioctl_kalloc(struct rd_softc *sc,
! 201: struct rd_conf *urd, struct proc *proc);
! 202:
! 203: dev_type_open(rdopen);
! 204: dev_type_close(rdclose);
! 205: dev_type_read(rdread);
! 206: dev_type_write(rdwrite);
! 207: dev_type_ioctl(rdioctl);
! 208: dev_type_size(rdsize);
! 209: dev_type_dump(rddump);
! 210:
! 211: int
! 212: rddump(dev, blkno, va, size)
! 213: dev_t dev;
! 214: daddr64_t blkno;
! 215: caddr_t va;
! 216: size_t size;
! 217: {
! 218: return ENODEV;
! 219: }
! 220:
! 221: daddr64_t
! 222: rdsize(dev_t dev)
! 223: {
! 224: int part, unit;
! 225: struct rd_softc *sc;
! 226:
! 227: /* Disallow control units. */
! 228: unit = DISKUNIT(dev);
! 229: if (unit >= ramdisk_ndevs)
! 230: return 0;
! 231: sc = ramdisk_devs[unit];
! 232: if (sc == NULL)
! 233: return 0;
! 234:
! 235: if (sc->sc_type == RD_UNCONFIGURED)
! 236: return 0;
! 237:
! 238: rdgetdisklabel(dev, sc, sc->sc_dkdev.dk_label, 0);
! 239: part = DISKPART(dev);
! 240: if (part >= sc->sc_dkdev.dk_label->d_npartitions)
! 241: return 0;
! 242: else
! 243: return DL_GETPSIZE(&sc->sc_dkdev.dk_label->d_partitions[part]) *
! 244: (sc->sc_dkdev.dk_label->d_secsize / DEV_BSIZE);
! 245: }
! 246:
! 247: int
! 248: rdopen(dev, flag, fmt, proc)
! 249: dev_t dev;
! 250: int flag, fmt;
! 251: struct proc *proc;
! 252: {
! 253: int unit;
! 254: struct rd_softc *sc;
! 255:
! 256: unit = DISKUNIT(dev);
! 257: if (unit >= ramdisk_ndevs)
! 258: return ENXIO;
! 259: sc = ramdisk_devs[unit];
! 260: if (sc == NULL)
! 261: return ENXIO;
! 262:
! 263: /*
! 264: * The control device is not exclusive, and can
! 265: * open uninitialized units (so you can setconf).
! 266: */
! 267: if (RD_IS_CTRL(dev))
! 268: return 0;
! 269:
! 270: #ifdef RAMDISK_HOOKS
! 271: /* Call the open hook to allow loading the device. */
! 272: rd_open_hook(unit, &sc->sc_rd);
! 273: #endif
! 274:
! 275: /*
! 276: * This is a normal, "slave" device, so
! 277: * enforce initialized, exclusive open.
! 278: */
! 279: if (sc->sc_type == RD_UNCONFIGURED)
! 280: return ENXIO;
! 281:
! 282: return 0;
! 283: }
! 284:
! 285: int
! 286: rdclose(dev, flag, fmt, proc)
! 287: dev_t dev;
! 288: int flag, fmt;
! 289: struct proc *proc;
! 290: {
! 291:
! 292: return 0;
! 293: }
! 294:
! 295: int
! 296: rdread(dev, uio, flags)
! 297: dev_t dev;
! 298: struct uio *uio;
! 299: int flags;
! 300: {
! 301: return (physio(rdstrategy, NULL, dev, B_READ, minphys, uio));
! 302: }
! 303:
! 304: int
! 305: rdwrite(dev, uio, flags)
! 306: dev_t dev;
! 307: struct uio *uio;
! 308: int flags;
! 309: {
! 310: return (physio(rdstrategy, NULL, dev, B_WRITE, minphys, uio));
! 311: }
! 312:
! 313: /*
! 314: * Handle I/O requests, either directly, or
! 315: * by passing them to the server process.
! 316: */
! 317: void
! 318: rdstrategy(bp)
! 319: struct buf *bp;
! 320: {
! 321: int unit, part;
! 322: struct rd_softc *sc;
! 323: caddr_t addr;
! 324: size_t off, xfer;
! 325: int s;
! 326:
! 327: unit = DISKUNIT(bp->b_dev);
! 328: sc = ramdisk_devs[unit];
! 329:
! 330: /* Sort rogue requests out */
! 331: if (sc == NULL || bp->b_blkno < 0 ||
! 332: (bp->b_bcount % sc->sc_dkdev.dk_label->d_secsize) != 0) {
! 333: bp->b_error = EINVAL;
! 334: goto bad;
! 335: }
! 336:
! 337: /* Do not write on "no trespassing" areas... */
! 338: part = DISKPART(bp->b_dev);
! 339: if (part != RAW_PART &&
! 340: bounds_check_with_label(bp, sc->sc_dkdev.dk_label, 1) <= 0)
! 341: goto bad;
! 342:
! 343: switch (sc->sc_type) {
! 344: #if RAMDISK_SERVER
! 345: case RD_UMEM_SERVER:
! 346: /* Just add this job to the server's queue. */
! 347: bp->b_actf = sc->sc_buflist;
! 348: sc->sc_buflist = bp;
! 349: if (bp->b_actf == NULL) {
! 350: /* server queue was empty. */
! 351: wakeup((caddr_t)sc);
! 352: /* see rd_server_loop() */
! 353: }
! 354: /* no biodone in this case */
! 355: return;
! 356: #endif /* RAMDISK_SERVER */
! 357:
! 358: case RD_KMEM_FIXED:
! 359: case RD_KMEM_ALLOCATED:
! 360: /* These are in kernel space. Access directly. */
! 361: bp->b_resid = bp->b_bcount;
! 362: off = (bp->b_blkno << DEV_BSHIFT);
! 363: xfer = bp->b_bcount;
! 364: if (xfer > (sc->sc_size - off))
! 365: xfer = (sc->sc_size - off);
! 366: addr = sc->sc_addr + off;
! 367: if (bp->b_flags & B_READ)
! 368: bcopy(addr, bp->b_data, xfer);
! 369: else
! 370: bcopy(bp->b_data, addr, xfer);
! 371: bp->b_resid -= xfer;
! 372: break;
! 373:
! 374: default:
! 375: bp->b_error = EIO;
! 376: bad:
! 377: bp->b_flags |= B_ERROR;
! 378: bp->b_resid = bp->b_bcount;
! 379: break;
! 380: }
! 381:
! 382: s = splbio();
! 383: biodone(bp);
! 384: splx(s);
! 385: }
! 386:
! 387: int
! 388: rdioctl(dev, cmd, data, flag, proc)
! 389: dev_t dev;
! 390: u_long cmd;
! 391: int flag;
! 392: caddr_t data;
! 393: struct proc *proc;
! 394: {
! 395: int unit;
! 396: struct disklabel *lp;
! 397: struct rd_softc *sc;
! 398: struct rd_conf *urd;
! 399: int error;
! 400:
! 401: unit = DISKUNIT(dev);
! 402: sc = ramdisk_devs[unit];
! 403:
! 404: urd = (struct rd_conf *)data;
! 405: switch (cmd) {
! 406: case DIOCRLDINFO:
! 407: if (sc->sc_type == RD_UNCONFIGURED)
! 408: break;
! 409: lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK);
! 410: rdgetdisklabel(dev, sc, lp, 0);
! 411: bcopy(lp, sc->sc_dkdev.dk_label, sizeof(*lp));
! 412: free(lp, M_TEMP);
! 413: return 0;
! 414:
! 415: case DIOCGPDINFO:
! 416: if (sc->sc_type == RD_UNCONFIGURED)
! 417: break;
! 418: rdgetdisklabel(dev, sc, (struct disklabel *)data, 1);
! 419: return 0;
! 420:
! 421: case DIOCGDINFO:
! 422: if (sc->sc_type == RD_UNCONFIGURED) {
! 423: break;
! 424: }
! 425: *(struct disklabel *)data = *(sc->sc_dkdev.dk_label);
! 426: return 0;
! 427:
! 428: case DIOCGPART:
! 429: ((struct partinfo *)data)->disklab = sc->sc_dkdev.dk_label;
! 430: ((struct partinfo *)data)->part =
! 431: &sc->sc_dkdev.dk_label->d_partitions[DISKPART(dev)];
! 432: return 0;
! 433:
! 434: case DIOCWDINFO:
! 435: case DIOCSDINFO:
! 436: if (sc->sc_type == RD_UNCONFIGURED) {
! 437: break;
! 438: }
! 439: if ((flag & FWRITE) == 0)
! 440: return EBADF;
! 441:
! 442: error = setdisklabel(sc->sc_dkdev.dk_label,
! 443: (struct disklabel *)data, /*sd->sc_dk.dk_openmask : */0);
! 444: if (error == 0) {
! 445: if (cmd == DIOCWDINFO)
! 446: error = writedisklabel(DISKLABELDEV(dev),
! 447: rdstrategy, sc->sc_dkdev.dk_label);
! 448: }
! 449:
! 450: return error;
! 451:
! 452: case DIOCWLABEL:
! 453: if (sc->sc_type == RD_UNCONFIGURED) {
! 454: break;
! 455: }
! 456: if ((flag & FWRITE) == 0)
! 457: return EBADF;
! 458: return 0;
! 459:
! 460: case RD_GETCONF:
! 461: /* If this is not the control device, punt! */
! 462: if (RD_IS_CTRL(dev) == 0) {
! 463: break;
! 464: }
! 465: *urd = sc->sc_rd;
! 466: return 0;
! 467:
! 468: case RD_SETCONF:
! 469: /* If this is not the control device, punt! */
! 470: if (RD_IS_CTRL(dev) == 0) {
! 471: break;
! 472: }
! 473: /* Can only set it once. */
! 474: if (sc->sc_type != RD_UNCONFIGURED) {
! 475: break;
! 476: }
! 477: switch (urd->rd_type) {
! 478: case RD_KMEM_ALLOCATED:
! 479: return rd_ioctl_kalloc(sc, urd, proc);
! 480: #if RAMDISK_SERVER
! 481: case RD_UMEM_SERVER:
! 482: return rd_ioctl_server(sc, urd, proc);
! 483: #endif
! 484: default:
! 485: break;
! 486: }
! 487: break;
! 488: }
! 489: return EINVAL;
! 490: }
! 491:
! 492: void
! 493: rdgetdisklabel(dev_t dev, struct rd_softc *sc, struct disklabel *lp,
! 494: int spoofonly)
! 495: {
! 496: bzero(lp, sizeof(struct disklabel));
! 497:
! 498: lp->d_secsize = DEV_BSIZE;
! 499: lp->d_ntracks = 1;
! 500: lp->d_nsectors = sc->sc_size >> DEV_BSHIFT;
! 501: lp->d_ncylinders = 1;
! 502: lp->d_secpercyl = lp->d_nsectors;
! 503: if (lp->d_secpercyl == 0) {
! 504: lp->d_secpercyl = 100;
! 505: /* as long as it's not 0 - readdisklabel divides by it (?) */
! 506: }
! 507:
! 508: strncpy(lp->d_typename, "RAM disk", sizeof(lp->d_typename));
! 509: lp->d_type = DTYPE_SCSI;
! 510: strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
! 511: DL_SETDSIZE(lp, lp->d_nsectors);
! 512: lp->d_rpm = 3600;
! 513: lp->d_interleave = 1;
! 514: lp->d_version = 1;
! 515:
! 516: lp->d_magic = DISKMAGIC;
! 517: lp->d_magic2 = DISKMAGIC;
! 518: lp->d_checksum = dkcksum(lp);
! 519:
! 520: /*
! 521: * Call the generic disklabel extraction routine
! 522: */
! 523: readdisklabel(DISKLABELDEV(dev), rdstrategy, lp, spoofonly);
! 524: }
! 525:
! 526: /*
! 527: * Handle ioctl RD_SETCONF for (sc_type == RD_KMEM_ALLOCATED)
! 528: * Just allocate some kernel memory and return.
! 529: */
! 530: int
! 531: rd_ioctl_kalloc(sc, urd, proc)
! 532: struct rd_softc *sc;
! 533: struct rd_conf *urd;
! 534: struct proc *proc;
! 535: {
! 536: vaddr_t addr;
! 537: vsize_t size;
! 538:
! 539: /* Sanity check the size. */
! 540: size = urd->rd_size;
! 541: addr = uvm_km_zalloc(kernel_map, size);
! 542: if (!addr)
! 543: return ENOMEM;
! 544:
! 545: /* This unit is now configured. */
! 546: sc->sc_addr = (caddr_t)addr; /* kernel space */
! 547: sc->sc_size = (size_t)size;
! 548: sc->sc_type = RD_KMEM_ALLOCATED;
! 549: return 0;
! 550: }
! 551:
! 552: #if RAMDISK_SERVER
! 553:
! 554: /*
! 555: * Handle ioctl RD_SETCONF for (sc_type == RD_UMEM_SERVER)
! 556: * Set config, then become the I/O server for this unit.
! 557: */
! 558: int
! 559: rd_ioctl_server(sc, urd, proc)
! 560: struct rd_softc *sc;
! 561: struct rd_conf *urd;
! 562: struct proc *proc;
! 563: {
! 564: vaddr_t end;
! 565: int error;
! 566:
! 567: /* Sanity check addr, size. */
! 568: end = (vaddr_t) (urd->rd_addr + urd->rd_size);
! 569:
! 570: if ((end >= VM_MAXUSER_ADDRESS) || (end < ((vaddr_t) urd->rd_addr)) )
! 571: return EINVAL;
! 572:
! 573: /* This unit is now configured. */
! 574: sc->sc_addr = urd->rd_addr; /* user space */
! 575: sc->sc_size = urd->rd_size;
! 576: sc->sc_type = RD_UMEM_SERVER;
! 577:
! 578: /* Become the server daemon */
! 579: error = rd_server_loop(sc);
! 580:
! 581: /* This server is now going away! */
! 582: sc->sc_type = RD_UNCONFIGURED;
! 583: sc->sc_addr = 0;
! 584: sc->sc_size = 0;
! 585:
! 586: return (error);
! 587: }
! 588:
! 589: int rd_sleep_pri = PWAIT | PCATCH;
! 590:
! 591: int
! 592: rd_server_loop(sc)
! 593: struct rd_softc *sc;
! 594: {
! 595: struct buf *bp;
! 596: caddr_t addr; /* user space address */
! 597: size_t off; /* offset into "device" */
! 598: size_t xfer; /* amount to transfer */
! 599: int error;
! 600: int s;
! 601:
! 602: for (;;) {
! 603: /* Wait for some work to arrive. */
! 604: while (sc->sc_buflist == NULL) {
! 605: error = tsleep((caddr_t)sc, rd_sleep_pri, "rd_idle", 0);
! 606: if (error)
! 607: return error;
! 608: }
! 609:
! 610: /* Unlink buf from head of list. */
! 611: bp = sc->sc_buflist;
! 612: sc->sc_buflist = bp->b_actf;
! 613: bp->b_actf = NULL;
! 614:
! 615: /* Do the transfer to/from user space. */
! 616: error = 0;
! 617: bp->b_resid = bp->b_bcount;
! 618: off = (bp->b_blkno << DEV_BSHIFT);
! 619: if (off >= sc->sc_size) {
! 620: if (bp->b_flags & B_READ)
! 621: goto done; /* EOF (not an error) */
! 622: error = EIO;
! 623: goto done;
! 624: }
! 625: xfer = bp->b_resid;
! 626: if (xfer > (sc->sc_size - off))
! 627: xfer = (sc->sc_size - off);
! 628: addr = sc->sc_addr + off;
! 629: if (bp->b_flags & B_READ)
! 630: error = copyin(addr, bp->b_data, xfer);
! 631: else
! 632: error = copyout(bp->b_data, addr, xfer);
! 633: if (!error)
! 634: bp->b_resid -= xfer;
! 635:
! 636: done:
! 637: if (error) {
! 638: bp->b_error = error;
! 639: bp->b_flags |= B_ERROR;
! 640: }
! 641: s = splbio();
! 642: biodone(bp);
! 643: splx(s);
! 644: }
! 645: }
! 646:
! 647: #endif /* RAMDISK_SERVER */
CVSweb