diff options
| author | gwr <gwr@NetBSD.org> | 1995-10-08 23:30:57 +0000 |
|---|---|---|
| committer | gwr <gwr@NetBSD.org> | 1995-10-08 23:30:57 +0000 |
| commit | 9a79c5a0435d0128ace890a5719d3e216cb00aef (patch) | |
| tree | 47b98893f7263a19675461994a225718a9507691 /sys | |
| parent | 03cd2cc0b339f57abbac7b5f4c56f7f5c9e544b5 (diff) | |
Machine-independent RAM-disk driver.
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/dev/md.c | 484 | ||||
| -rw-r--r-- | sys/dev/md.h | 59 | ||||
| -rw-r--r-- | sys/dev/ramdisk.c | 484 | ||||
| -rw-r--r-- | sys/dev/ramdisk.h | 59 |
4 files changed, 1086 insertions, 0 deletions
diff --git a/sys/dev/md.c b/sys/dev/md.c new file mode 100644 index 00000000000..cd33f454251 --- /dev/null +++ b/sys/dev/md.c @@ -0,0 +1,484 @@ +/* $NetBSD: md.c,v 1.1 1995/10/08 23:30:57 gwr Exp $ */ + +/* + * Copyright (c) 1995 Gordon W. Ross, Leo Weppelman. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by + * Gordon W. Ross and Leo Weppelman. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This implements a general-puspose RAM-disk. + * See ramdisk.h for notes on the config types. + * + * Note that this driver provides the same functionality + * as the MFS filesystem hack, but this is better because + * you can use this for any filesystem type you'd like! + * + * Credit for most of the kmem ramdisk code goes to: + * Leo Weppelman (atari) and Phil Nelson (pc532) + * Credit for the ideas behind the "user space RAM" code goes + * to the authors of the MFS implementation. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/device.h> + +#include <vm/vm.h> +#include <vm/vm_kern.h> +/* Don't want all those other VM headers... */ +extern vm_offset_t kmem_alloc __P((vm_map_t, vm_size_t)); + +#include <dev/ramdisk.h> + +/* + * By default, include the user-space functionality. + * Use: option RAMDISK_SERVER=0 to turn it off. + */ +#ifndef RAMDISK_SERVER +#define RAMDISK_SERVER 1 +#endif + +/* + * XXX: the "control" unit is (base unit + 16). + * We should just use the cdev as the "control", but + * that interferes with the security stuff preventing + * simulatneous use of raw and block devices. + * + * XXX Assumption: 16 RAM-disks are enough! + */ +#define RD_IS_CTRL(unit) (unit & 0x10) +#define RD_UNIT(unit) (unit & 0xF) + +/* + * XXX - This is just for a sanity check. Only + * applies to kernel-space RAM disk allocations. + */ +#define RD_KMEM_MAX_SIZE 0x100000 /* 1MB */ + +/* autoconfig stuff... */ + +struct rd_softc { + struct device sc_dev; /* REQUIRED first entry */ + struct rd_conf sc_rd; + struct buf *sc_buflist; + int sc_flags; +}; +/* shorthand for fields in sc_rd: */ +#define sc_addr sc_rd.rd_addr +#define sc_size sc_rd.rd_size +#define sc_type sc_rd.rd_type +/* flags */ +#define RD_ISOPEN 0x01 +#define RD_SERVED 0x02 + +static int rd_match (struct device *, void *self, void *); +static void rd_attach(struct device *, struct device *self, void *); + +struct cfdriver rdcd = { + NULL, "rd", rd_match, rd_attach, + DV_DISK, sizeof(struct device), NULL, 0 }; + +static int +rd_match(parent, self, aux) + struct device *parent; + void *self; + void *aux; +{ + return(1); +} + +static void +rd_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct rd_softc *sc = (struct rd_softc *)self; + + /* XXX - Could accept aux info here to set the config. */ +#ifdef RAMDISK_HOOKS + /* + * This external function might setup a pre-loaded disk. + * All it would need to do is setup the rd_conf struct. + * See sys/arch/sun3/dev/rd_root.c for an example. + */ + rd_attach_hook(sc->sc_dev.dv_unit, &sc->sc_rd); +#endif + printf("\n"); +} + +/* + * operational routines: + * open, close, read, write, strategy, + * ioctl, dump, size + */ + +void rdstrategy __P((struct buf *bp)); + +#if RAMDISK_SERVER +static int rd_server_loop __P((struct rd_softc *sc)); +static int rd_ioctl_server __P((struct rd_softc *sc, + struct rd_conf *urd, struct proc *proc)); +#endif + +int rddump(dev, blkno, va, size) + dev_t dev; + daddr_t blkno; + caddr_t va; + size_t size; +{ + return ENODEV; +} + +int rdsize(dev_t dev) +{ + int unit; + struct rd_softc *sc; + + /* Disallow control units. */ + unit = minor(dev); + if (unit >= rdcd.cd_ndevs) + return 0; + sc = rdcd.cd_devs[unit]; + if (sc == NULL) + return 0; + + if (sc->sc_type == RD_UNCONFIGURED) + return 0; + + return (sc->sc_size >> DEV_BSHIFT); +} + +int rdopen(dev, flag, fmt, proc) + dev_t dev; + int flag, fmt; + struct proc *proc; +{ + int md, unit; + struct rd_softc *sc; + + md = minor(dev); + unit = RD_UNIT(md); + if (unit >= rdcd.cd_ndevs) + return ENXIO; + sc = rdcd.cd_devs[unit]; + if (sc == NULL) + return ENXIO; + + /* + * The control device is not exclusive, and can + * open uninitialized units (so you can setconf). + */ + if (RD_IS_CTRL(md)) + return 0; + +#ifdef RAMDISK_HOOKS + /* Call the open hook to allow loading the device. */ + rd_open_hook(unit, &sc->sc_rd); +#endif + + /* + * This is a normal, "slave" device, so + * enforce initialized, exclusive open. + */ + if (sc->sc_type == RD_UNCONFIGURED) + return ENXIO; + if (sc->sc_flags & RD_ISOPEN) + return EBUSY; + + return 0; +} + +int rdclose(dev, flag, fmt, proc) + dev_t dev; + int flag, fmt; + struct proc *proc; +{ + int md, unit; + struct rd_softc *sc; + + md = minor(dev); + unit = RD_UNIT(md); + sc = rdcd.cd_devs[unit]; + + if (RD_IS_CTRL(md)) + return 0; + + /* Normal device. */ + sc->sc_flags = 0; + + return 0; +} + +int +rdread(dev, uio) + dev_t dev; + struct uio *uio; +{ + return (physio(rdstrategy, NULL, dev, B_READ, minphys, uio)); +} + +int +rdwrite(dev, uio) + dev_t dev; + struct uio *uio; +{ + return (physio(rdstrategy, NULL, dev, B_WRITE, minphys, uio)); +} + +/* + * Handle I/O requests, either directly, or + * by passing them to the server process. + */ +void +rdstrategy(bp) + struct buf *bp; +{ + int md, unit; + struct rd_softc *sc; + caddr_t addr; + size_t off, xfer; + + md = minor(bp->b_dev); + unit = RD_UNIT(md); + sc = rdcd.cd_devs[unit]; + + switch (sc->sc_type) { +#if RAMDISK_SERVER + case RD_UMEM_SERVER: + /* Just add this job to the server's queue. */ + bp->b_actf = sc->sc_buflist; + sc->sc_buflist = bp; + if (bp->b_actf == NULL) { + /* server queue was empty. */ + wakeup((caddr_t)sc); + /* see rd_server_loop() */ + } + /* no biodone in this case */ + return; +#endif /* RAMDISK_SERVER */ + + case RD_KMEM_FIXED: + case RD_KMEM_ALLOCATED: + /* These are in kernel space. Access directly. */ + bp->b_resid = bp->b_bcount; + off = (bp->b_blkno << DEV_BSHIFT); + if (off >= sc->sc_size) { + if (bp->b_flags & B_READ) + break; /* EOF */ + goto set_eio; + } + xfer = bp->b_resid; + if (xfer > (sc->sc_size - off)) + xfer = (sc->sc_size - off); + addr = sc->sc_addr + off; + if (bp->b_flags & B_READ) + bcopy(addr, bp->b_data, xfer); + else + bcopy(bp->b_data, addr, xfer); + bp->b_resid -= xfer; + break; + + default: + bp->b_resid = bp->b_bcount; + set_eio: + bp->b_error = EIO; + bp->b_flags |= B_ERROR; + break; + } + biodone(bp); +} + +int +rdioctl(dev, cmd, data, flag, proc) + dev_t dev; + u_long cmd; + int flag; + caddr_t data; + struct proc *proc; +{ + int md, unit; + struct rd_softc *sc; + struct rd_conf *urd; + + md = minor(dev); + unit = RD_UNIT(md); + sc = rdcd.cd_devs[unit]; + + /* If this is not the control device, punt! */ + if (RD_IS_CTRL(md) == 0) + return ENOTTY; + + urd = (struct rd_conf *)data; + switch (cmd) { + case RD_GETCONF: + *urd = sc->sc_rd; + return 0; + + case RD_SETCONF: + /* Can only set it once. */ + if (sc->sc_type != RD_UNCONFIGURED) + break; + switch (urd->rd_type) { + case RD_KMEM_ALLOCATED: + return rd_ioctl_kalloc(sc, urd, proc); +#if RAMDISK_SERVER + case RD_UMEM_SERVER: + return rd_ioctl_server(sc, urd, proc); +#endif + default: + break; + } + break; + } + return EINVAL; +} + +/* + * Handle ioctl RD_SETCONF for (sc_type == RD_KMEM_ALLOCATED) + * Just allocate some kernel memory and return. + */ +int +rd_ioctl_kalloc(sc, urd, proc) + struct rd_softc *sc; + struct rd_conf *urd; + struct proc *proc; +{ + vm_offset_t addr; + vm_size_t size; + + /* Sanity check the size. */ + size = urd->rd_size; + if (size > RD_KMEM_MAX_SIZE) + return EINVAL; + addr = kmem_alloc(kernel_map, size); + if (!addr) + return ENOMEM; + + /* This unit is now configured. */ + sc->sc_addr = (caddr_t)addr; /* kernel space */ + sc->sc_size = (size_t)size; + sc->sc_type = RD_KMEM_ALLOCATED; + return 0; +} + +#if RAMDISK_SERVER + +/* + * Handle ioctl RD_SETCONF for (sc_type == RD_KMEM_ALLOCATED) + * Set config, then become the I/O server for this unit. + */ +int +rd_ioctl_server(sc, urd, proc) + struct rd_softc *sc; + struct rd_conf *urd; + struct proc *proc; +{ + vm_offset_t end; + int error; + + /* Sanity check addr, size. */ + end = (vm_offset_t) (urd->rd_addr + urd->rd_size); + + if ((end >= VM_MAXUSER_ADDRESS) || + (end < ((vm_offset_t) urd->rd_addr)) ) + return EINVAL; + + /* This unit is now configured. */ + sc->sc_addr = urd->rd_addr; /* user space */ + sc->sc_size = urd->rd_size; + sc->sc_type = RD_UMEM_SERVER; + + /* Become the server daemon */ + error = rd_server_loop(sc); + + /* This server is now going away! */ + sc->sc_type = RD_UNCONFIGURED; + sc->sc_addr = 0; + sc->sc_size = 0; + + return (error); +} + +int rd_sleep_pri = PWAIT | PCATCH; + +static int +rd_server_loop(sc) + struct rd_softc *sc; +{ + struct buf *bp; + caddr_t addr; /* user space address */ + size_t off; /* offset into "device" */ + size_t xfer; /* amount to transfer */ + int error; + + for (;;) { + /* Wait for some work to arrive. */ + while (sc->sc_buflist == NULL) { + error = tsleep((caddr_t)sc, rd_sleep_pri, "rd_idle", 0); + if (error) + return error; + } + + /* Unlink buf from head of list. */ + bp = sc->sc_buflist; + sc->sc_buflist = bp->b_actf; + bp->b_actf = NULL; + + /* Do the transfer to/from user space. */ + error = 0; + bp->b_resid = bp->b_bcount; + off = (bp->b_blkno << DEV_BSHIFT); + if (off >= sc->sc_size) { + if (bp->b_flags & B_READ) + goto done; /* EOF (not an error) */ + error = EIO; + goto done; + } + xfer = bp->b_resid; + if (xfer > (sc->sc_size - off)) + xfer = (sc->sc_size - off); + addr = sc->sc_addr + off; + if (bp->b_flags & B_READ) + error = copyin(addr, bp->b_data, xfer); + else + error = copyout(bp->b_data, addr, xfer); + if (!error) + bp->b_resid -= xfer; + + done: + if (error) { + bp->b_error = error; + bp->b_flags |= B_ERROR; + } + biodone(bp); + } +} + +#endif /* RAMDISK_SERVER */ diff --git a/sys/dev/md.h b/sys/dev/md.h new file mode 100644 index 00000000000..a055ad85a38 --- /dev/null +++ b/sys/dev/md.h @@ -0,0 +1,59 @@ + +/* + * RAM-disk ioctl functions: + */ + +#include <sys/ioccom.h> + +struct rd_conf { + caddr_t rd_addr; + size_t rd_size; + int rd_type; +}; + +#define RD_GETCONF _IOR('r', 0, struct rd_conf) /* get unit config */ +#define RD_SETCONF _IOW('r', 1, struct rd_conf) /* set unit config */ + +/* + * There are three configurations supported for each unit, + * reflected in the value of the rd_type field: + */ +#define RD_UNCONFIGURED 0 +/* + * Not yet configured. Open returns ENXIO. + */ +#define RD_KMEM_FIXED 1 +/* + * Disk image resident in kernel (patched in or loaded). + * Requires that the function: rd_set_kmem() is called to + * attach the (initialized) kernel memory to be used by the + * device. This can be called by an "open hook" if this + * driver is compiled with the RD_OPEN_HOOK option. + * No attempt will be made to free this memory. + */ +#define RD_KMEM_ALLOCATED 2 +/* + * Small, wired-down chunk of kernel memory obtained from + * kmem_alloc(). The allocation is performed by an ioctl + * call on the "control" unit (regular unit + 16) + */ +#define RD_UMEM_SERVER 3 +/* + * Indirect access to user-space of a user-level server. + * (Like the MFS hack, but better! 8^) Device operates + * only while the server has the control device open and + * continues to service I/O requests. The process that + * does this setconf will become the I/O server. + * Support for this configuration type is optional: + * option RAMDISK_SERVER + */ + +#ifdef _KERNEL +/* + * If the option RAMDISK_HOOKS is on, then these functions are + * called by the ramdisk driver to allow machine-dependent to + * configure and/or load each ramdisk unit. + */ +extern void rd_attach_hook __P((int unit, struct rd_conf *)); +extern void rd_open_hook __P((int unit, struct rd_conf *)); +#endif diff --git a/sys/dev/ramdisk.c b/sys/dev/ramdisk.c new file mode 100644 index 00000000000..556af6ef2b0 --- /dev/null +++ b/sys/dev/ramdisk.c @@ -0,0 +1,484 @@ +/* $NetBSD: ramdisk.c,v 1.1 1995/10/08 23:30:57 gwr Exp $ */ + +/* + * Copyright (c) 1995 Gordon W. Ross, Leo Weppelman. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by + * Gordon W. Ross and Leo Weppelman. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This implements a general-puspose RAM-disk. + * See ramdisk.h for notes on the config types. + * + * Note that this driver provides the same functionality + * as the MFS filesystem hack, but this is better because + * you can use this for any filesystem type you'd like! + * + * Credit for most of the kmem ramdisk code goes to: + * Leo Weppelman (atari) and Phil Nelson (pc532) + * Credit for the ideas behind the "user space RAM" code goes + * to the authors of the MFS implementation. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/device.h> + +#include <vm/vm.h> +#include <vm/vm_kern.h> +/* Don't want all those other VM headers... */ +extern vm_offset_t kmem_alloc __P((vm_map_t, vm_size_t)); + +#include <dev/ramdisk.h> + +/* + * By default, include the user-space functionality. + * Use: option RAMDISK_SERVER=0 to turn it off. + */ +#ifndef RAMDISK_SERVER +#define RAMDISK_SERVER 1 +#endif + +/* + * XXX: the "control" unit is (base unit + 16). + * We should just use the cdev as the "control", but + * that interferes with the security stuff preventing + * simulatneous use of raw and block devices. + * + * XXX Assumption: 16 RAM-disks are enough! + */ +#define RD_IS_CTRL(unit) (unit & 0x10) +#define RD_UNIT(unit) (unit & 0xF) + +/* + * XXX - This is just for a sanity check. Only + * applies to kernel-space RAM disk allocations. + */ +#define RD_KMEM_MAX_SIZE 0x100000 /* 1MB */ + +/* autoconfig stuff... */ + +struct rd_softc { + struct device sc_dev; /* REQUIRED first entry */ + struct rd_conf sc_rd; + struct buf *sc_buflist; + int sc_flags; +}; +/* shorthand for fields in sc_rd: */ +#define sc_addr sc_rd.rd_addr +#define sc_size sc_rd.rd_size +#define sc_type sc_rd.rd_type +/* flags */ +#define RD_ISOPEN 0x01 +#define RD_SERVED 0x02 + +static int rd_match (struct device *, void *self, void *); +static void rd_attach(struct device *, struct device *self, void *); + +struct cfdriver rdcd = { + NULL, "rd", rd_match, rd_attach, + DV_DISK, sizeof(struct device), NULL, 0 }; + +static int +rd_match(parent, self, aux) + struct device *parent; + void *self; + void *aux; +{ + return(1); +} + +static void +rd_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct rd_softc *sc = (struct rd_softc *)self; + + /* XXX - Could accept aux info here to set the config. */ +#ifdef RAMDISK_HOOKS + /* + * This external function might setup a pre-loaded disk. + * All it would need to do is setup the rd_conf struct. + * See sys/arch/sun3/dev/rd_root.c for an example. + */ + rd_attach_hook(sc->sc_dev.dv_unit, &sc->sc_rd); +#endif + printf("\n"); +} + +/* + * operational routines: + * open, close, read, write, strategy, + * ioctl, dump, size + */ + +void rdstrategy __P((struct buf *bp)); + +#if RAMDISK_SERVER +static int rd_server_loop __P((struct rd_softc *sc)); +static int rd_ioctl_server __P((struct rd_softc *sc, + struct rd_conf *urd, struct proc *proc)); +#endif + +int rddump(dev, blkno, va, size) + dev_t dev; + daddr_t blkno; + caddr_t va; + size_t size; +{ + return ENODEV; +} + +int rdsize(dev_t dev) +{ + int unit; + struct rd_softc *sc; + + /* Disallow control units. */ + unit = minor(dev); + if (unit >= rdcd.cd_ndevs) + return 0; + sc = rdcd.cd_devs[unit]; + if (sc == NULL) + return 0; + + if (sc->sc_type == RD_UNCONFIGURED) + return 0; + + return (sc->sc_size >> DEV_BSHIFT); +} + +int rdopen(dev, flag, fmt, proc) + dev_t dev; + int flag, fmt; + struct proc *proc; +{ + int md, unit; + struct rd_softc *sc; + + md = minor(dev); + unit = RD_UNIT(md); + if (unit >= rdcd.cd_ndevs) + return ENXIO; + sc = rdcd.cd_devs[unit]; + if (sc == NULL) + return ENXIO; + + /* + * The control device is not exclusive, and can + * open uninitialized units (so you can setconf). + */ + if (RD_IS_CTRL(md)) + return 0; + +#ifdef RAMDISK_HOOKS + /* Call the open hook to allow loading the device. */ + rd_open_hook(unit, &sc->sc_rd); +#endif + + /* + * This is a normal, "slave" device, so + * enforce initialized, exclusive open. + */ + if (sc->sc_type == RD_UNCONFIGURED) + return ENXIO; + if (sc->sc_flags & RD_ISOPEN) + return EBUSY; + + return 0; +} + +int rdclose(dev, flag, fmt, proc) + dev_t dev; + int flag, fmt; + struct proc *proc; +{ + int md, unit; + struct rd_softc *sc; + + md = minor(dev); + unit = RD_UNIT(md); + sc = rdcd.cd_devs[unit]; + + if (RD_IS_CTRL(md)) + return 0; + + /* Normal device. */ + sc->sc_flags = 0; + + return 0; +} + +int +rdread(dev, uio) + dev_t dev; + struct uio *uio; +{ + return (physio(rdstrategy, NULL, dev, B_READ, minphys, uio)); +} + +int +rdwrite(dev, uio) + dev_t dev; + struct uio *uio; +{ + return (physio(rdstrategy, NULL, dev, B_WRITE, minphys, uio)); +} + +/* + * Handle I/O requests, either directly, or + * by passing them to the server process. + */ +void +rdstrategy(bp) + struct buf *bp; +{ + int md, unit; + struct rd_softc *sc; + caddr_t addr; + size_t off, xfer; + + md = minor(bp->b_dev); + unit = RD_UNIT(md); + sc = rdcd.cd_devs[unit]; + + switch (sc->sc_type) { +#if RAMDISK_SERVER + case RD_UMEM_SERVER: + /* Just add this job to the server's queue. */ + bp->b_actf = sc->sc_buflist; + sc->sc_buflist = bp; + if (bp->b_actf == NULL) { + /* server queue was empty. */ + wakeup((caddr_t)sc); + /* see rd_server_loop() */ + } + /* no biodone in this case */ + return; +#endif /* RAMDISK_SERVER */ + + case RD_KMEM_FIXED: + case RD_KMEM_ALLOCATED: + /* These are in kernel space. Access directly. */ + bp->b_resid = bp->b_bcount; + off = (bp->b_blkno << DEV_BSHIFT); + if (off >= sc->sc_size) { + if (bp->b_flags & B_READ) + break; /* EOF */ + goto set_eio; + } + xfer = bp->b_resid; + if (xfer > (sc->sc_size - off)) + xfer = (sc->sc_size - off); + addr = sc->sc_addr + off; + if (bp->b_flags & B_READ) + bcopy(addr, bp->b_data, xfer); + else + bcopy(bp->b_data, addr, xfer); + bp->b_resid -= xfer; + break; + + default: + bp->b_resid = bp->b_bcount; + set_eio: + bp->b_error = EIO; + bp->b_flags |= B_ERROR; + break; + } + biodone(bp); +} + +int +rdioctl(dev, cmd, data, flag, proc) + dev_t dev; + u_long cmd; + int flag; + caddr_t data; + struct proc *proc; +{ + int md, unit; + struct rd_softc *sc; + struct rd_conf *urd; + + md = minor(dev); + unit = RD_UNIT(md); + sc = rdcd.cd_devs[unit]; + + /* If this is not the control device, punt! */ + if (RD_IS_CTRL(md) == 0) + return ENOTTY; + + urd = (struct rd_conf *)data; + switch (cmd) { + case RD_GETCONF: + *urd = sc->sc_rd; + return 0; + + case RD_SETCONF: + /* Can only set it once. */ + if (sc->sc_type != RD_UNCONFIGURED) + break; + switch (urd->rd_type) { + case RD_KMEM_ALLOCATED: + return rd_ioctl_kalloc(sc, urd, proc); +#if RAMDISK_SERVER + case RD_UMEM_SERVER: + return rd_ioctl_server(sc, urd, proc); +#endif + default: + break; + } + break; + } + return EINVAL; +} + +/* + * Handle ioctl RD_SETCONF for (sc_type == RD_KMEM_ALLOCATED) + * Just allocate some kernel memory and return. + */ +int +rd_ioctl_kalloc(sc, urd, proc) + struct rd_softc *sc; + struct rd_conf *urd; + struct proc *proc; +{ + vm_offset_t addr; + vm_size_t size; + + /* Sanity check the size. */ + size = urd->rd_size; + if (size > RD_KMEM_MAX_SIZE) + return EINVAL; + addr = kmem_alloc(kernel_map, size); + if (!addr) + return ENOMEM; + + /* This unit is now configured. */ + sc->sc_addr = (caddr_t)addr; /* kernel space */ + sc->sc_size = (size_t)size; + sc->sc_type = RD_KMEM_ALLOCATED; + return 0; +} + +#if RAMDISK_SERVER + +/* + * Handle ioctl RD_SETCONF for (sc_type == RD_KMEM_ALLOCATED) + * Set config, then become the I/O server for this unit. + */ +int +rd_ioctl_server(sc, urd, proc) + struct rd_softc *sc; + struct rd_conf *urd; + struct proc *proc; +{ + vm_offset_t end; + int error; + + /* Sanity check addr, size. */ + end = (vm_offset_t) (urd->rd_addr + urd->rd_size); + + if ((end >= VM_MAXUSER_ADDRESS) || + (end < ((vm_offset_t) urd->rd_addr)) ) + return EINVAL; + + /* This unit is now configured. */ + sc->sc_addr = urd->rd_addr; /* user space */ + sc->sc_size = urd->rd_size; + sc->sc_type = RD_UMEM_SERVER; + + /* Become the server daemon */ + error = rd_server_loop(sc); + + /* This server is now going away! */ + sc->sc_type = RD_UNCONFIGURED; + sc->sc_addr = 0; + sc->sc_size = 0; + + return (error); +} + +int rd_sleep_pri = PWAIT | PCATCH; + +static int +rd_server_loop(sc) + struct rd_softc *sc; +{ + struct buf *bp; + caddr_t addr; /* user space address */ + size_t off; /* offset into "device" */ + size_t xfer; /* amount to transfer */ + int error; + + for (;;) { + /* Wait for some work to arrive. */ + while (sc->sc_buflist == NULL) { + error = tsleep((caddr_t)sc, rd_sleep_pri, "rd_idle", 0); + if (error) + return error; + } + + /* Unlink buf from head of list. */ + bp = sc->sc_buflist; + sc->sc_buflist = bp->b_actf; + bp->b_actf = NULL; + + /* Do the transfer to/from user space. */ + error = 0; + bp->b_resid = bp->b_bcount; + off = (bp->b_blkno << DEV_BSHIFT); + if (off >= sc->sc_size) { + if (bp->b_flags & B_READ) + goto done; /* EOF (not an error) */ + error = EIO; + goto done; + } + xfer = bp->b_resid; + if (xfer > (sc->sc_size - off)) + xfer = (sc->sc_size - off); + addr = sc->sc_addr + off; + if (bp->b_flags & B_READ) + error = copyin(addr, bp->b_data, xfer); + else + error = copyout(bp->b_data, addr, xfer); + if (!error) + bp->b_resid -= xfer; + + done: + if (error) { + bp->b_error = error; + bp->b_flags |= B_ERROR; + } + biodone(bp); + } +} + +#endif /* RAMDISK_SERVER */ diff --git a/sys/dev/ramdisk.h b/sys/dev/ramdisk.h new file mode 100644 index 00000000000..a055ad85a38 --- /dev/null +++ b/sys/dev/ramdisk.h @@ -0,0 +1,59 @@ + +/* + * RAM-disk ioctl functions: + */ + +#include <sys/ioccom.h> + +struct rd_conf { + caddr_t rd_addr; + size_t rd_size; + int rd_type; +}; + +#define RD_GETCONF _IOR('r', 0, struct rd_conf) /* get unit config */ +#define RD_SETCONF _IOW('r', 1, struct rd_conf) /* set unit config */ + +/* + * There are three configurations supported for each unit, + * reflected in the value of the rd_type field: + */ +#define RD_UNCONFIGURED 0 +/* + * Not yet configured. Open returns ENXIO. + */ +#define RD_KMEM_FIXED 1 +/* + * Disk image resident in kernel (patched in or loaded). + * Requires that the function: rd_set_kmem() is called to + * attach the (initialized) kernel memory to be used by the + * device. This can be called by an "open hook" if this + * driver is compiled with the RD_OPEN_HOOK option. + * No attempt will be made to free this memory. + */ +#define RD_KMEM_ALLOCATED 2 +/* + * Small, wired-down chunk of kernel memory obtained from + * kmem_alloc(). The allocation is performed by an ioctl + * call on the "control" unit (regular unit + 16) + */ +#define RD_UMEM_SERVER 3 +/* + * Indirect access to user-space of a user-level server. + * (Like the MFS hack, but better! 8^) Device operates + * only while the server has the control device open and + * continues to service I/O requests. The process that + * does this setconf will become the I/O server. + * Support for this configuration type is optional: + * option RAMDISK_SERVER + */ + +#ifdef _KERNEL +/* + * If the option RAMDISK_HOOKS is on, then these functions are + * called by the ramdisk driver to allow machine-dependent to + * configure and/or load each ramdisk unit. + */ +extern void rd_attach_hook __P((int unit, struct rd_conf *)); +extern void rd_open_hook __P((int unit, struct rd_conf *)); +#endif |
