sp_ioctl.c revision ff5ca3bd17dee7e2bf2e4f2e3a2b354e0ecbd00d
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Soft partitioning metadevice driver (md_sp), administrative routines.
*
* This file contains the administrative routines for the soft partitioning
* metadevice driver. All administration is done through the use of ioctl's.
*
* The primary ioctl's supported by soft partitions are as follows:
*
* MD_IOCSET - set up a new soft partition.
* MD_IOCGET - get the unit structure of a soft partition.
* MD_IOCRESET - delete a soft partition.
* MD_IOCGROW - add space to a soft partition.
* MD_IOCGETDEVS - get the device the soft partition is built on.
* MD_IOC_SPSTATUS - set the status (un_status field in the soft
* partition unit structure) for one or more soft
* partitions.
*
* Note that, as with other metadevices, the majority of the work for
* (specifically in libmeta, see meta_sp.c). The driver's main administrative
* function is to maintain the in-core & metadb entries associated with a soft
* partition.
*
* In addition, a few other ioctl's are supported via helper routines in
* the md driver. These are:
*
* DKIOCINFO - get "disk" information.
* DKIOCGEOM - get geometry information.
* DKIOCGVTOC - get vtoc information.
*/
#include <sys/sysmacros.h>
extern int md_status;
extern md_krwlock_t md_unit_array_rw;
/*
* FUNCTION: sp_getun()
* INPUT: mnum - minor number of soft partition to get.
* OUTPUT: mde - return error pointer.
* RETURNS: mp_unit_t * - ptr to unit structure requested
* NULL - error
* PURPOSE: Returns a reference to the soft partition unit structure
* indicated by the passed-in minor number.
*/
static mp_unit_t *
{
mdi_unit_t *ui;
/* check set */
return (NULL);
}
return (NULL);
}
return (NULL);
}
return (NULL);
}
return (un);
}
/*
* FUNCTION: sp_setstatus()
* INPUT: d - data ptr passed in from ioctl.
* mode - pass-through to ddi_copyin.
* lockp - lock ptr.
* OUTPUT: none.
* RETURNS: 0 - success.
* non-zero - error.
* PURPOSE: Set the status of one or more soft partitions atomically.
* this implements the MD_IOC_SPSTATUS ioctl. Soft partitions
* are passed in as an array of minor numbers. The un_status
* field in the unit structure of each soft partition is set to
* the status passed in and all unit structures are recommitted
* to the metadb at once.
*/
static int
{
int err = 0;
/* allocate minor number and recids arrays */
/* copyin minor number array */
goto out;
/* check to make sure all units are valid first */
for (i = 0; i < nunits; i++) {
goto out;
}
}
/* update state for all units */
for (i = 0; i < nunits; i++) {
}
recids[i] = 0;
out:
return (err);
}
/*
* FUNCTION: sp_update_watermarks()
* INPUT: d - data ptr passed in from ioctl.
* mode - pass-through to ddi_copyin.
* OUTPUT: none.
* RETURNS: 0 - success.
* non-zero - error.
* PURPOSE: This implements the MD_IOC_SPUPDATEWM ioctl.
* Watermarks are passed in an array.
*/
static int
sp_update_watermarks(void *d, int mode)
{
int err = 0;
int i;
return (EFAULT);
/* Validate the set */
/*
* Once we're here, we are no longer stateless: we cannot
* return without first freeing the watermarks and offset
* arrays we just allocated. So use the "out" label instead
* of "return."
*/
/* Retrieve the watermark and offset arrays from user land */
goto out;
}
goto out;
}
/*
* NOTE: For multi-node sets we only commit the watermarks if we are
* the master node. This avoids an ioctl-within-ioctl deadlock if the
* underlying device is a mirror.
*/
goto out;
}
}
/*
* Flag the fact that we're coming from an ioctl handler to the
* underlying device so that it can take appropriate action if needed.
* This is necessary for multi-owner mirrors as they may need to
* update the metadevice state as a result of the layered open.
*/
goto out;
}
/*
* Even the "constant" fields should be initialized
* here, since bioreset() below will clear them.
*/
/*
* For MN sets only:
* Use a special flag MD_STR_WMUPDATE, for the following case:
* If the watermarks reside on a mirror disk and a switch
* of ownership is triggered by this IO,
* the message that is generated by that request must be
* processed even if the commd subsystem is currently suspended.
*
* For non-MN sets or non-mirror metadevices,
* this flag has no meaning and is not checked.
*/
break;
}
/* Get the buf_t ready for the next iteration */
}
out:
return (err);
}
/*
* FUNCTION: sp_read_watermark()
* INPUT: d - data ptr passed in from ioctl.
* mode - pass-through to ddi_copyin.
* OUTPUT: none.
* RETURNS: 0 - success.
* non-zero - error.
* PURPOSE: This implements the MD_IOC_SPREADWM ioctl.
*/
static int
sp_read_watermark(void *d, int mode)
{
/*
* Flag the fact that we are being called from ioctl context so that
* the underlying device can take any necessary extra steps to handle
* this scenario.
*/
}
/*
* Taking advantage of the knowledge that mdmderror()
* returns 0, so we don't really need to keep track of
* an error code other than in the error struct.
*/
}
sizeof (mp_watermark_t), mode)) {
return (EFAULT);
}
return (0);
}
/*
* FUNCTION: sp_set()
* INPUT: d - data ptr passed in from ioctl.
* mode - pass-through to ddi_copyin.
* OUTPUT: none.
* RETURNS: 0 - success.
* non-zero - error.
* PURPOSE: Create a soft partition. The unit structure representing
* the soft partiton is passed down from userland. We allocate
* a metadb entry, copyin the unit the structure, handle any
* metadevice parenting issues, then commit the record to the
* metadb. Once the record is in the metadb, we must also
* build the associated in-core structures. This is done via
* sp_build_incore() (see sp.c).
*/
static int
{
void *rec_addr;
int err;
/* validate set */
/* get the record type */
/* check if there is already a device with this minor number */
/* create the db record for this soft partition */
#if defined(_ILP32)
#else
#endif
} else {
}
/* set initial value for possible child record */
recids[1] = 0;
if (recids[0] < 0)
/* get the address of the soft partition db record */
/*
* at this point we can happily mess with the soft partition
* db record since we haven't committed it to the metadb yet.
* if we crash before we commit, the uncommitted record will be
* automatically purged.
*/
/* copy in the user's soft partition unit struct */
return (EFAULT);
}
/* fill in common unit structure fields which aren't set in userland */
/* All 64 bit metadevices only support EFI labels. */
}
/* if we are parenting a metadevice, set our child's parent field */
/* it's a metadevice, need to parent it */
}
/* set child recid and recids end marker */
recids[2] = 0;
}
/*
* build the incore structures.
*/
return (err);
}
/*
* Update unit availability
*/
/*
* commit the record.
* if we had to update a child record, it will get commited
* as well.
*/
/* create the mdi_unit struct for this soft partition */
return (0);
}
/*
* FUNCTION: sp_get()
* INPUT: d - data ptr.
* mode - pass-through to ddi_copyout.
* lock - lock ptr.
* OUTPUT: none.
* RETURNS: 0 - success.
* non-zero - error.
* PURPOSE: Get the soft partition unit structure specified by the
* minor number. the in-core unit structure is obtained
* and copied into the md_i_get structure passed down from
* userland.
*/
static int
{
mdi_unit_t *ui;
md_i_get_t *migp = d;
/* make sure this is a valid unit structure */
/* get the mdi_unit */
}
/*
* md_ioctl_readerlock returns a reference to the in-core
* unit structure. this lock will be dropped by
* md_ioctl_lock_exit() before the ioctl returns.
*/
/* verify the md_i_get structure */
return (0);
}
return (EFAULT);
}
/* copyout unit */
return (EFAULT);
return (0);
}
/*
* FUNCTION: sp_reset()
* INPUT: reset_params - soft partitioning reset parameters.
* OUTPUT: none.
* RETURNS: 0 - success.
* non-zero - error.
* PURPOSE: Do the setup work needed to delete a soft partition.
* note that the actual removal of both in-core and metadb
* structures is done in the reset_sp() routine (see sp.c).
* In addition, since multiple soft partitions may exist
* on top of a single metadevice, the soft partition reset
* parameters (md_sp_reset_t) contains information about
* underlying metadevice. If the underlying metadevice is
* to be deparented, the new_parent field will be MD_NO_PARENT,
* otherwise it will be contain the minor number of another
* soft partition built on top of the underlying metadevice.
*/
static int
{
mdi_unit_t *ui;
/* get the unit structure */
}
/* don't delete if we have a parent */
}
(void) md_unit_openclose_enter(ui);
/* don't delete if we are currently open */
if (md_unit_isopen(ui)) {
}
/*
* if we are built on metadevice, we need to deparent
* or reparent that metadevice.
*/
}
/* remove the soft partition */
/*
* Update unit availability
*/
/*
* If MN set, reset s_un_next so all nodes can have
* the same view of the next available slot when
* nodes are -w and -j
*/
if (MD_MNSET_SETNO(setno)) {
}
/* release locks and return */
out:
return (0);
}
/*
* FUNCTION: sp_grow()
* INPUT: d - data ptr.
* mode - pass-through to ddi_copyin.
* lockp - lock ptr.
* OUTPUT: none.
* RETURNS: 0 - success.
* non-zero - error.
* PURPOSE: Attach more space to a soft partition. We are passed in
* a new unit structure with the new extents and other updated
* information. The new unit structure essentially replaces
* the old unit for this soft partition. We place the new
* unit into the metadb, delete the old metadb record, and
* then update the in-core unit structure array to point to
* the new unit.
*/
static int
{
mdi_unit_t *ui;
int i;
mddb_recid_t old_vtoc = 0;
int err;
int rval = 0;
int npar;
/* validate set */
/* make sure this soft partition already exists */
/* handle any parents */
if (npar >= 1) {
return (EFAULT);
}
}
/*
* handle parent locking. grab the unit writer lock,
* then all parent ioctl locks, and then finally our own.
* parents should be sorted to avoid deadlock.
*/
for (i = 0; i < npar; ++i) {
(void) md_ioctl_writerlock(&plock[i],
}
/*
* Preserve the friendly name nature of the unit that is growing.
*/
#if defined(_ILP32)
goto out;
#else
#endif
} else {
}
if (recid < 0) {
goto out;
}
/* get the address of the new unit */
/* copy in the user's unit struct */
if (err) {
goto out;
}
/* All 64 bit metadevices only support EFI labels. */
/*
* If the device was previously smaller than a terabyte,
* and had a vtoc record attached to it, we remove the
* vtoc record, because the layout has changed completely.
*/
(un->c.un_vtoc_id != 0)) {
new_un->c.un_vtoc_id =
}
}
/* commit new unit struct */
/*
* delete old unit struct.
*/
/* place new unit in in-core array */
/*
* If old_vtoc has a non zero value, we know:
* - This unit crossed the border from smaller to larger one TB
* - There was a vtoc record for the unit,
* - This vtoc record is no longer needed, because
* a new efi record has been created for this un.
*/
if (old_vtoc != 0) {
}
/* release locks, return success */
out:
for (i = npar - 1; (i >= 0); --i)
md_ioctl_writerexit(&plock[i]);
return (rval);
}
/*
* FUNCTION: sp_getdevs()
* INPUT: d - data ptr.
* mode - pass-through to ddi_copyout.
* lockp - lock ptr.
* OUTPUT: none.
* RETURNS: 0 - success.
* non-zero - error.
* PURPOSE: Get the device on which the soft partition is built.
* This is simply a matter of copying out the md_dev64_t stored
* in the soft partition unit structure.
*/
static int
void *d,
int mode,
)
{
mdi_unit_t *ui;
/* check set */
/* check unit */
}
/* get unit */
/* only ever 1 device for a soft partition */
/* do miniroot->target device translation */
== NODEV64)
return (ENODEV);
}
/* copyout dev information */
return (EFAULT);
}
return (0);
}
/*
* sp_set_capability:
* ------------------
* Called to set or clear a capability for a softpart
* called by the MD_MN_SET_CAP ioctl.
*/
static int
{
mdi_unit_t *ui;
int err = 0;
return (EINVAL);
/* This function is only valid for a multi-node set */
if (!MD_MNSET_SETNO(setno)) {
return (EINVAL);
}
if (p->sc_set & DKV_ABR_CAP) {
void (*inc_abr_count)();
/* Increment abr count in underlying metadevice */
0, MD_INC_ABR_COUNT, 0);
if (inc_abr_count != NULL)
} else {
void (*dec_abr_count)();
/* Decrement abr count in underlying metadevice */
0, MD_DEC_ABR_COUNT, 0);
if (dec_abr_count != NULL)
}
if (p->sc_set & DKV_DMR_CAP) {
} else {
}
return (err);
}
/*
* FUNCTION: sp_admin_ioctl().
* INPUT: cmd - ioctl to be handled.
* data - data ptr.
* lockp - lock ptr.
* OUTPUT: none.
* RETURNS: 0 - success.
* non-zero - error.
* PURPOSE: Handle administrative ioctl's. Essentially a large
* switch statement to dispatch the ioctl's to their
* handlers. See comment at beginning of file for specifics
* on which ioctl's are handled.
*/
static int
{
void *d = NULL;
int err = 0;
/* We can only handle 32-bit clients for internal commands */
return (EINVAL);
}
/* handle ioctl */
switch (cmd) {
case MD_IOCSET:
{
/* create new soft partition */
return (EACCES);
sz = sizeof (md_set_params_t);
break;
}
break;
}
case MD_IOCGET:
{
/* get soft partition unit structure */
return (EACCES);
sz = sizeof (md_i_get_t);
break;
}
break;
}
case MD_IOCRESET:
{
/* delete soft partition */
return (EACCES);
sz = sizeof (md_sp_reset_t);
break;
}
break;
}
case MD_IOCGROW:
{
/* grow soft partition */
return (EACCES);
sz = sizeof (md_grow_params_t);
break;
}
break;
}
case MD_IOCGET_DEVS:
{
/* get underlying device */
return (EACCES);
sz = sizeof (md_getdevs_params_t);
break;
}
break;
}
case MD_IOC_SPSTATUS:
{
/* set the status field of one or more soft partitions */
return (EACCES);
sz = sizeof (md_sp_statusset_t);
break;
}
break;
}
case MD_IOC_SPUPDATEWM:
case MD_MN_IOC_SPUPDATEWM:
{
return (EACCES);
sz = sizeof (md_sp_update_wm_t);
break;
}
break;
}
case MD_IOC_SPREADWM:
{
return (EACCES);
sz = sizeof (md_sp_read_wm_t);
break;
}
break;
}
case MD_MN_SET_CAP:
{
return (EACCES);
sz = sizeof (md_mn_setcap_params_t);
break;
}
break;
}
default:
return (ENOTTY);
}
/*
* copyout and free any args
*/
if (sz != 0) {
if (err == 0) {
}
}
}
return (err);
}
/*
* FUNCTION: md_sp_ioctl()
* INPUT: dev - device we are operating on.
* cmd - ioctl to be handled.
* data - data ptr.
* lockp - lock ptr.
* OUTPUT: none.
* RETURNS: 0 - success.
* non-zero - error.
* PURPOSE: Dispatch ioctl's. Administrative ioctl's are handled
* by sp_admin_ioctl. All others (see comment at beginning
* of this file) are handled in-line here.
*/
int
{
mdi_unit_t *ui;
int err = 0;
/* handle admin ioctls */
if (mnum == MD_ADM_MINOR)
/* check unit */
return (ENXIO);
/* is this a supported ioctl? */
if (err != 0) {
return (err);
}
/* handle ioctl */
switch (cmd) {
case DKIOCINFO:
{
/* "disk" info */
struct dk_cinfo *p;
return (EACCES);
p = kmem_alloc(sizeof (*p), KM_SLEEP);
kmem_free(p, sizeof (*p));
return (err);
}
case DKIOCGMEDIAINFO:
{
struct dk_minfo p;
return (EACCES);
return (err);
}
case DKIOCGGEOM:
{
/* geometry information */
struct dk_geom *p;
return (EACCES);
p = kmem_alloc(sizeof (*p), KM_SLEEP);
mode) != 0)
kmem_free(p, sizeof (*p));
return (err);
}
case DKIOCGAPART:
{
err = 0;
mode) != 0)
}
#ifdef _SYSCALL32
else {
mode) != 0)
}
#endif /* _SYSCALL32 */
return (err);
}
case DKIOCGVTOC:
{
/* vtoc information */
return (EACCES);
}
#ifdef _SYSCALL32
else {
}
#endif /* _SYSCALL32 */
return (err);
}
case DKIOCSVTOC:
{
return (EACCES);
}
}
#ifdef _SYSCALL32
else {
} else {
}
}
#endif /* _SYSCALL32 */
if (err == 0)
return (err);
}
case DKIOCGEXTVTOC:
{
/* extended vtoc information */
return (EACCES);
return (err);
}
case DKIOCSEXTVTOC:
{
return (EACCES);
}
if (err == 0)
return (err);
}
case DKIOCGETEFI:
{
/*
* This one can be done centralized,
* no need to put in the same code for all types of metadevices
*/
}
case DKIOCSETEFI:
{
/*
* This one can be done centralized,
* no need to put in the same code for all types of metadevices
*/
}
case DKIOCPARTITION:
{
}
case DKIOCGETVOLCAP:
{
/*
* Return the supported capabilities for the soft-partition.
* We can only support those caps that are provided by the
* underlying device.
*/
return (EINVAL);
return (EACCES);
/* Send ioctl to underlying driver */
lockp);
if (err == 0)
return (err);
}
case DKIOCSETVOLCAP:
{
/*
* Enable a supported capability (as returned by DKIOCGETVOLCAP)
* Do not pass the request down as we're the top-level device
* handler for the application.
* If the requested capability is supported (set in ui_capab),
* set the corresponding bit in ui_tstate so that we can pass
* the appropriate flag when performing i/o.
* This request is propagated to all nodes.
*/
volcapset_t volcap = 0;
void (*check_offline)();
int offline_status = 0;
return (EINVAL);
return (EACCES);
return (EFAULT);
/*
* Send DKIOCGETVOLCAP to underlying driver to see if
* capability supported
*/
if (err != 0)
return (err);
/* Save capabilities */
/*
* Error if required capability not supported by underlying
* driver
*/
return (ENOTSUP);
/*
* Check if underlying mirror has an offline submirror,
* fail if there is on offline submirror
*/
0, MD_CHECK_OFFLINE, 0);
if (check_offline != NULL)
if (offline_status)
return (EINVAL);
volcap |= DKV_ABR_CAP;
/* Only send capability message if there is a change */
return (err);
}
case DKIOCDMR:
{
/*
* Only valid for MN sets. We need to pass it down to the
* underlying driver if its a metadevice, after we've modified
* the offsets to pick up the correct lower-level device
* position.
*/
#ifdef _MULTI_DATAMODEL
#endif /* _MULTI_DATAMODEL */
return (EINVAL);
return (EINVAL);
return (ENOMEM);
/*
* Underlying device supports directed mirror read, so update
* the user-supplied offset to pick the correct block from the
* partitioned metadevice.
*/
#ifdef _MULTI_DATAMODEL
return (ENOMEM);
}
case DDI_MODEL_ILP32:
return (EFAULT);
}
break;
case DDI_MODEL_NONE:
return (EFAULT);
}
break;
default:
return (EFAULT);
}
#else /* ! _MULTI_DATAMODEL */
return (EFAULT);
}
#endif /* _MULTI_DATA_MODEL */
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
sizeof (vdr32->vdr_side_name));
break;
case DDI_MODEL_NONE:
break;
}
#else /* ! _MULTI_DATA_MODEL */
#endif /* _MULTI_DATA_MODEL */
#ifdef _MULTI_DATAMODEL
#endif /* _MULTI_DATAMODEL */
return (err);
}
}
/* Option not handled */
return (ENOTTY);
}