ramdisk.c revision e4c8a2fa6413b807e6a4a804cc0059b62ea9730d
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Ramdisk device driver.
*
* There are two types of ramdisk: 'real' OBP-created ramdisks, and 'pseudo'
* ramdisks created at runtime with no corresponding OBP device node. The
* ramdisk(7D) driver is capable of dealing with both, and with the creation
* and deletion of 'pseudo' ramdisks.
*
* Every ramdisk has a single 'state' structure which maintains data for
* that ramdisk, and is assigned a single minor number. The bottom 10-bits
* of the minor number index the state structures; the top 8-bits give a
* 'real OBP disk' number, i.e. they are zero for 'pseudo' ramdisks. Thus
* it is possible to distinguish 'real' from 'pseudo' ramdisks using the
* top 8-bits of the minor number.
*
* Each OBP-created ramdisk has its own node in the device tree with an
* "existing" property which describes the one-or-more physical address ranges
* assigned to the ramdisk. All 'pseudo' ramdisks share a common devinfo
* structure.
*
* A single character device node is used by ramdiskadm(1M) to communicate
* with the ramdisk driver, with minor number 0:
*
* /dev/ramdiskctl -> /devices/pseudo/ramdisk@0:ctl
*
* For consistent access, block and raw device nodes are created for *every*
* ramdisk. For 'pseudo' ramdisks:
*
*
* For OBP-created ramdisks:
*
*
* This allows the transition from the standalone to the kernel to proceed
* when booting from a ramdisk, and for the installation to correctly identify
* the root device.
*/
#include <sys/sysmacros.h>
#include <vm/seg_kmem.h>
/*
* An opaque handle where information about our set of ramdisk devices lives.
*/
static void *rd_statep;
/*
* Pointer to devinfo for the 'pseudo' ramdisks. Real OBP-created ramdisks
* get their own individual devinfo.
*/
/*
* Global state lock.
*/
/*
* Maximum number of ramdisks supported by this driver.
*/
/*
* Percentage of physical memory which can be assigned to pseudo ramdisks,
* what that equates to in pages, and how many pages are currently assigned.
*/
static pgcnt_t rd_max_physmem;
static pgcnt_t rd_tot_physmem;
/*
* Is the driver busy, i.e. are there any pseudo ramdisk devices in existence?
*/
static int
rd_is_busy(void)
{
return (EBUSY);
}
}
return (0);
}
/*
* Find the first free minor number; returns zero if there isn't one.
*/
static minor_t
rd_find_free_minor(void)
{
return (minor);
}
}
return (0);
}
/*
* Locate the rd_devstate for the named ramdisk; returns NULL if not found.
* Each ramdisk is identified uniquely by name, i.e. an OBP-created ramdisk
* cannot have the same name as a pseudo ramdisk.
*/
static rd_devstate_t *
rd_find_named_disk(char *name)
{
return (rsp);
}
}
return (NULL);
}
/*
* Locate the rd_devstate for the real OBP-created ramdisk whose devinfo
* is referenced by 'dip'; returns NULL if not found (shouldn't happen).
*/
static rd_devstate_t *
{
return (rsp);
}
}
return (NULL);
}
/*
* Is the ramdisk open?
*/
static int
{
}
/*
* Mark the ramdisk open.
*/
static int
{
switch (otyp) {
case OTYP_CHR:
break;
case OTYP_BLK:
break;
case OTYP_LYR:
rsp->rd_lyr_open_cnt++;
break;
default:
return (-1);
}
return (0);
}
/*
* Mark the ramdisk closed.
*/
static void
{
switch (otyp) {
case OTYP_CHR:
rsp->rd_chr_open = 0;
break;
case OTYP_BLK:
rsp->rd_blk_open = 0;
break;
case OTYP_LYR:
rsp->rd_lyr_open_cnt--;
break;
default:
break;
}
}
static void
rd_init_tuneables(void)
{
char *prop, *p;
/*
* Ensure sanity of 'rd_max_disks', which may be tuned in ramdisk.conf.
*/
p = prop;
}
if (rd_max_disks >= RD_MAX_DISKS) {
}
/*
* Ensure sanity of 'rd_percent_physmem', which may be tuned
* in ramdisk.conf.
*/
p = prop;
}
if (rd_percent_physmem >= 100) {
" using default (%u%%).", rd_percent_physmem,
}
/*
* Since availrmem_initial is a long, this won't overflow.
*/
}
/*
* Allocate enough physical pages to hold "npages" pages. Returns an
* array of page_t * pointers that can later be mapped in or out via
* rd_{un}map_window() but is otherwise opaque, or NULL on failure.
*/
page_t **
{
spgcnt_t i;
return (NULL);
return (NULL);
if (!page_create_wait(npages, 0)) {
return (NULL);
}
return (NULL);
}
goto out;
}
PP_CLRFREE(pp);
PP_CLRAGED(pp);
}
for (i = 0; i < npages; i++)
page_downgrade(ppa[i]);
rd_tot_physmem += npages;
return (ppa);
out:
page_create_putback(npages - i);
while (--i >= 0)
return (NULL);
}
/*
* Free physical pages previously allocated via rd_phys_alloc(); note that
* this function may block as it has to wait until it can exclusively lock
* all the pages first.
*/
static void
{
pgcnt_t i;
for (i = 0; i < npages; ++i) {
if (! page_tryupgrade(ppa[i])) {
page_unlock(ppa[i]);
;
}
}
rd_tot_physmem -= npages;
}
/*
* Remove a window mapping (if present).
*/
static void
{
}
}
/*
* Map a portion of the ramdisk into the virtual window.
*/
static void
{
/*
* Already mapped; is offset within our window?
*/
return;
}
/*
* No, we need to re-map; toss the old mapping.
*/
}
/*
* Different algorithms depending on whether this is a real
* OBP-created ramdisk, or a pseudo ramdisk.
*/
/*
* Find the range of pages which should be mapped.
*/
}
/*
* Load the mapping.
*/
}
} else {
uint_t i;
/*
* Real OBP-created ramdisk: locate the physical range which
* contains this offset.
*/
for (i = 0; i < rsp->rd_nexisting; ++i) {
break;
}
}
/*
* Load the mapping.
*/
}
}
/*
* Fakes up a disk geometry, and one big partition, based on the size
* of the file. This is needed because we allow newfs'ing the device,
* and newfs will do several disk ioctls to figure out the geometry and
* partition information. It uses that information to determine the parameters
* to pass to mkfs. Geometry is pretty much irrelevant these days, but we
* have to support it.
*
* Stolen from lofi.c - should maybe split out common code sometime.
*/
static void
{
/* dk_geom - see dkio(7I) */
/*
* dkg_ncyl _could_ be set to one here (one big cylinder with gobs
* of sectors), but that breaks programs like fdisk which want to
* partition a disk by cylinder. With one cylinder, you can't create
* an fdisk partition and put pcfs on it for testing (hard to pick
* a number between one and one).
*
* The cheezy floppy test is an attempt to not have too few cylinders
* for a small file, or so many on a big file that you waste space
* for backup superblocks or cylinder group structures.
*/
else
/* in case file file is < 100k */
/* vtoc - see dkio(7I) */
/*
* The partition size cannot just be the number of sectors, because
* that might not end on a cylinder boundary. And if that's the case,
*/
/* dk_cinfo - see dkio(7I) */
/*
* newfs uses this to set maxcontig. Must not be < 16, or it
* will be 0 when newfs multiplies it by DEV_BSIZE and divides
* it by the block size. Then tunefs doesn't work because
* maxcontig is 0.
*/
}
/*
* Deallocate resources (virtual and physical, device nodes, structures)
* from a ramdisk.
*/
static void
{
}
}
if (rsp->rd_existing) {
}
}
/*
* Remove the block and raw device nodes.
*/
} else {
}
/*
* Remove the "Size" and "Nblocks" properties.
*/
}
}
/*
* Allocate resources (virtual and physical, device nodes, structures)
* to a ramdisk.
*/
static rd_devstate_t *
{
minor = rd_find_free_minor();
return (NULL);
}
/*
* Allocate virtual window onto ramdisk.
*/
goto create_failed;
}
/*
* Allocate physical memory for non-OBP ramdisks.
* Create pseudo block and raw device nodes.
*/
goto create_failed;
}
/*
* For non-OBP ramdisks the device nodes are:
*
*/
DDI_PSEUDO, 0) == DDI_FAILURE) {
goto create_failed;
}
DDI_PSEUDO, 0) == DDI_FAILURE) {
goto create_failed;
}
} else {
/*
* For OBP-created ramdisks the device nodes are:
*
*/
DDI_PSEUDO, 0) == DDI_FAILURE) {
goto create_failed;
}
DDI_PSEUDO, 0) == DDI_FAILURE) {
goto create_failed;
}
}
/*
* Create the "Size" and "Nblocks" properties.
*/
goto create_failed;
}
goto create_failed;
}
/*
* Allocate kstat stuff.
*/
MUTEX_DRIVER, NULL);
}
return (rsp);
/*
* Cleanup.
*/
return (NULL);
}
/*
* Undo what we did in rd_attach, freeing resources and removing things which
* we installed. The system framework guarantees we are not active with this
* devinfo node in any other entry points at this time.
*/
static int
{
/*
* Pseudo node: can't detach if any pseudo ramdisks exist.
*/
if (rd_is_busy()) {
return (DDI_FAILURE);
}
} else {
/*
* A 'real' ramdisk; find the state and free resources.
*/
}
}
return (DDI_SUCCESS);
}
static int
{
char *name;
switch (cmd) {
case DDI_ATTACH:
/*
* For pseudo ramdisk devinfo set up state 0 and :ctl device;
* else it's an OBP-created ramdisk.
*/
if (is_pseudo_device(dip)) {
/*
* The zeroth minor is reserved for the ramdisk
* 'control' device.
*/
DDI_FAILURE) {
goto attach_failed;
}
goto attach_failed;
}
} else {
"%s: name too long - ignoring\n", name);
goto attach_failed;
}
/*
* An OBP-created ramdisk must have an 'existing'
* property; get and check it.
*/
"%s: " RD_EXISTING_PROP_NAME
" property missing\n", name);
goto attach_failed;
}
"%s: " RD_EXISTING_PROP_NAME
" illegal size\n", name);
goto attach_failed;
}
/*
* Calculate the size of the ramdisk.
*/
for (i = 0; i < nep; ++i) {
}
/*
* Allocate driver resources for the ramdisk.
*/
goto attach_failed;
}
}
return (DDI_SUCCESS);
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/*
* Use our common detach routine to unallocate any stuff which
* was allocated above.
*/
(void) rd_common_detach(dip);
}
return (DDI_FAILURE);
}
static int
{
int e;
switch (cmd) {
case DDI_DETACH:
e = rd_common_detach(dip);
return (e);
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/*ARGSUSED*/
static int
{
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
case DDI_INFO_DEVT2INSTANCE:
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
default:
return (DDI_FAILURE);
}
}
/*ARGSUSED3*/
static int
{
if (minor == RD_CTL_MINOR) {
/*
* Master control device; must be opened exclusively.
*/
return (EINVAL);
}
return (ENXIO);
}
if (rd_is_open(rsp)) {
return (EBUSY);
}
return (0);
}
return (ENXIO);
}
return (EINVAL);
}
return (0);
}
/*ARGSUSED*/
static int
{
return (EINVAL);
}
return (0);
}
static void
{
}
}
static void
{
while (nbytes > 0) {
if (reading) {
} else {
}
offset += copy_bytes;
buf_addr += copy_bytes;
nbytes -= copy_bytes;
}
}
static int
{
} else {
}
} else {
}
}
}
return (0);
}
/*ARGSUSED*/
static int
{
return (EINVAL);
}
/*ARGSUSED*/
static int
{
return (EINVAL);
}
/*ARGSUSED*/
static int
{
return (EFAULT);
}
if (size == 0) {
return (EINVAL);
}
return (EEXIST);
}
return (EAGAIN);
}
}
/*ARGSUSED*/
static int
{
return (EFAULT);
}
return (EINVAL);
}
if (rd_is_open(rsp)) {
return (EBUSY);
}
return (0);
}
/*ARGSUSED*/
static int
{
int error;
enum dkio_state dkstate;
/*
* Ramdisk ioctls only apply to the master device.
*/
if (minor == RD_CTL_MINOR) {
/*
* The query commands only need read-access - i.e., normal
* users are allowed to do those on the controlling device
* as long as they can open it read-only.
*/
switch (cmd) {
case RD_CREATE_DISK:
return (EPERM);
case RD_DELETE_DISK:
return (EPERM);
default:
return (EINVAL);
}
}
return (ENXIO);
}
/*
* These are for faking out utilities like newfs.
*/
switch (cmd) {
case DKIOCGVTOC:
case DDI_MODEL_ILP32: {
return (EFAULT);
}
break;
case DDI_MODEL_NONE:
return (EFAULT);
break;
}
return (0);
case DKIOCINFO:
if (error)
return (EFAULT);
return (0);
case DKIOCG_VIRTGEOM:
case DKIOCG_PHYGEOM:
case DKIOCGGEOM:
if (error)
return (EFAULT);
return (0);
case DKIOCSTATE:
/* the file is always there */
sizeof (enum dkio_state), mode);
if (error)
return (EFAULT);
return (0);
default:
return (ENOTTY);
}
}
nodev, /* dump */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
NULL,
};
0,
nulldev, /* identify */
nulldev, /* probe */
nodev, /* reset */
(struct bus_ops *)0
};
extern struct mod_ops mod_driverops;
"ramdisk driver v%I%",
};
static struct modlinkage modlinkage = {
&modldrv,
0
};
int
_init(void)
{
int e;
if ((e = ddi_soft_state_init(&rd_statep,
sizeof (rd_devstate_t), 0)) != 0) {
return (e);
}
if ((e = mod_install(&modlinkage)) != 0) {
}
return (e);
}
int
_fini(void)
{
int e;
if ((e = mod_remove(&modlinkage)) != 0) {
return (e);
}
return (e);
}
int
{
}