/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* rename or exchange identities of virtual device nodes
*/
#include <sys/sysmacros.h>
#define ROLE(r) \
((r) == MDRR_PARENT? "parent": \
(r) == MDRR_SELF? "self": \
(r) == MDRR_CHILD? "child": \
"<garbage>")
int md_rename_debug = 0;
/* delta guard rails */
/* transaction guard rails */
typedef struct role_change_mapping_tab_t {
const int ord;
const char *svc_name;
/*
* The actual table is at the end of the file, so we don't need
* many forward references
*/
/*
*
* Role swap rule table:
*
* New Role
* +---------------------------------------------------------------|
* | | Parent | Self | Child |
* +--------+-----------------+----------------+-------------------+
* | Parent | no default | ...no default | illegal |
* | | 1 (update kids) | 2 (update to) | 3 |
* Old +--------+-----------------+----------------+-------------------+
* Role | Self | ...self update | ...rename self | no default (down |
* | | 4 update up | 5 | 6 update from) |
* +--------+-----------------+----------------+-------------------+
* | Child | illegal | ...child | ...update |
* | | 7 | 8 update to | 9 parent |
* +---------------------------------------------------------------+
*
* and notes:
*
* - Boxes 1, 4 and 6 are the most interesting. They are responsible
* for updating the from unit's data structures. These may involve
* finding (former or future) children, resetting name keys and the like.
*
* - The "rename" operation is boxes 1, 5 and 9. Most of the work
* is done in box 5, since that contains both the "from" and "to"
* unit struct for rename.
*
* (There's got to be an eigen function for this; that diagonal
* axis is a role identity operation searching for an expression.)
*
* - Almost every transaction will call more than one of these.
* (Only a rename of a unit with no relatives will only call
* a single box.)
*
* - Box 4 "...update from" is the generic self->parent modifier.
* - Box 8 "...update to" is the generic child->self modifier.
* These can be generic because all of the information which
* needs to be updated is in the common portion of the unit
* structure when changing from their respective roles.
*
* - Boxes 1, 2 and 6 ("no default") indicate that per-metadevice
* information must be updated. For example, in box 1, children
* identities must be updated. Since different metadevice types
* detect and manipulate their children differently, there can
* be no generic "md_rename" function in this box.
*
* In addition to the named services in the table above, there
* MDRNM_LIST_URFOLKS, MDRNM_LIST_URSELF, MDRNM_LIST_URKIDS
* list a device's parents, self and children, respectively.
* In most cases the default functions can be used for parents
* and self. Top-level devices, are not required to have a
* "list folks" named service. Likewise, devices which can
* not have metadevice children, are not required to have the
* "list kids" named service. The LIST_UR* functions call back into
* the base driver (md_build_rendelta()) to package the changes to
* a device for addition onto the tree. The LIST_UR* named service
* then adds this "rename delta" onto the delta tree itself.
* This keeps private knowledge appropriately encapsulated.
* They return the number of devices which will need to be changed,
* and hence the number of elements they've added to the delta list
* or -1 for error.
*
* "lock" (MDRNM_LOCK), "unlock" (MDRNM_UNLOCK) and "check" (MDRNM_CHECK).
* These (un) write-lock all of the relevant in-core structs,
* including the unit structs for the device and quiesce i/o as necessary.
* The "check" named service verifies that this device
* is in a state where rename could and may occur at this time.
* Since the role_swap functions themselves cannot be undone
* (at least in this implementation), it is check()'s job to
* verify that the device is renamable (sic) or, if not, abort.
* The check function for the device participating in the role
* of "self" is usually where rename or exchange validity is verified.
*
* All of these functions take two arguments which may be thought
* of as the collective state changes of the tree of devices
* (md_rendelta_t *family) and the rename transaction state
* (md_rentxn_t rtxn or rtxnp).
*
*/
/*
* rename unit lock
* (default name service routine MDRNM_LOCK)
*/
static intptr_t
{
return (EINVAL);
}
/*
* target doesn't exist if renaming (by definition),
* so it need not be locked
*/
return (0);
}
return (ENODEV);
}
return (ENODEV);
}
return (0);
}
/*
* (default name service routine MDRNM_UNLOCK)
*/
/* ARGSUSED */
static void
{
}
/*
* This is used by the various MDRNM_LIST* named services.
*/
md_error_t *ep)
{
int err = 0;
/*
* For non-meta devices that are being renamed (in the future,
* that is) we would need to pass in default functions to
* accommodate them, provided the default function is
* on opaque devices.
*/
/* modindex */ 0, MDRNM_UNLOCK,
(intptr_t (*)()) md_rename_unlock);
goto out;
}
out:
if (err != 0) {
if (new) {
}
}
if (prev) {
}
return (new);
}
/*
* md_store_recid()
* used by role swap functions
*/
void
int *prec_idx,
{
}
}
if (add_recid) {
}
}
/*
* MDRNM_LIST_URFOLKS: generic named svc entry point
* add all parents onto the list pointed to by dlpp
* (only weird multi-parented devices need to have their
* own named svc to do this.)
*/
static int
{
return (-1);
}
return (0);
}
/*
* If supporting log renaming (and other multiparented devices)
* callout to each misc module to claim this waif and return the
* md_dev64_t of its parents.
*/
return (2);
}
NULL,
} else {
/* parent is swapping roles with self */
NULL,
}
if (!new) {
}
return (-1);
}
return (1);
}
/*
* MDRNM_LIST_URSELF: named svc entry point
* add all delta entries appropriate for ourselves onto the deltalist pointed
* to by dlpp
*/
static int
{
return (-1);
}
/* NULL */
}
/*
* renaming or
* from's parent is not to and to's parent is not from
*/
p,
} else {
exchange_up = TRUE;
}
/* self and parent are flipping */
p,
}
if (!new) {
}
return (-1);
}
if (!*dlpp) {
}
return (1);
}
/*
* free the tree of all deltas to devices involved in the rename transaction
*/
static void
{
int i = 0;
md_rendelta_t *r;
/* shift << because it makes the resultant pattern readable */
kmem_free(r, sizeof (md_rendelta_t));
}
}
/*
* walk down family tree, calling lock service function
*/
static int
{
md_rendelta_t *r;
int rc;
return (EINVAL);
}
return (rc);
}
}
return (0);
}
/*
* We rely on check() (MDRNM_CHECK) to make exhaustive checks,
* since we don't attempt to undo role_swap() failures.
*
* To implement an undo() function would require each role_swap()
* to store a log of previous state of the structures it changes,
* presumably anchored by the rendelta.
*
*/
static int
{
md_rendelta_t *r;
int rc;
/* no error packet to set? */
return (EINVAL);
}
/*
* <to> doesn't exist for rename
*/
}
/*
* for top being trans because it opens its sub-devices
*/
md_getminor(r->dev));
return (EBUSY);
}
break;
case MD_RENAME_VERSION_ONLINE:
break;
default:
md_getminor(r->dev));
return (EINVAL);
}
/* MD_UN_MOD_INPROGRESS includes the MD_UN_RENAMING bit */
md_getminor(r->dev));
return (EBUSY);
}
return (rc);
}
/* and be sure we can proceed */
if (!(r->role_swap)) {
md_getminor(r->dev));
return (EINVAL);
}
}
return (0);
}
/*
* rename role_swap() functions are responsible for updating their
* own parent, self and children references in both on-disk
* and in-core structures, as well as storing the changed
* record ids into recids and incrementing rec_idx.
*/
static void
{
md_rendelta_t *r;
}
/*
* there's some work to do, but not more than expected
*/
/*
* There's no way to indicate error from here,
* and even if we could, there's no undo mechanism.
* We've already modified the in-core structs, so
* We can't continue w/o committing, but we
* don't appear to have anything to commit.
*/
"md_rename: role_swap_dtree(family:%p, rtxnp:%p)",
return;
}
}
/*
* walk down delta tree, calling the unlock service for each device,
* provided any of the devices appear to have been locked
*/
static void
{
md_rendelta_t *r;
}
if (any_locked) {
/* unwind in reverse order */
/* NULL */
}
}
}
}
/*
* MDRNM_UPDATE_SELF
* This role swap function is identical for all unit types,
* so keep it here. It's also the best example because it
* touches all the modified portions of the relevant
* in-common structures.
*/
static void
{
/*
* self id changes in our own unit struct
*/
/*
* make sure that dest always has correct un_revision
* and rb_revision
*/
}
/*
* clear old array pointers to unit in-core and unit
*/
/*
* and point the new slots at the unit in-core and unit structs
*/
/*
* recreate kstats
* - destroy the ones associated with our former identity
* - reallocate and associate them with our new identity
*/
/*
* the unit in-core reference to the get next link's id changes
*/
/*
* name space addition of new key was done from user-level
* remove the old name's key here
*/
/*
* Remove associated device node as well
*/
/*
* and store the record id (from the unit struct) into recids
* for later commitment by md_rename()
*/
}
/*
*/
static void
{
}
}
/*
* exchange up (child->self)
*/
static void
{
/*
* self id changes in our own unit struct
* Note:
* - Since we're assuming the identity of "from" we use its mnum even
* though we're updating the "to" structures.
*/
/*
* our parent identifier becomes the new self, who was "to"
*/
/*
* point the set array pointers at the "new" unit and unit in-cores
* Note:
* - The other half of this transfer is done in the "update from"
*/
/*
* transfer kstats
*/
/*
* the unit in-core reference to the get next link's id changes
*/
/*
* name space additions, if necessary, were done from user-level.
* name space deletions, if necessary, were done in "exchange_from"
*/
/*
* and store the record id (from the unit struct) into recids
* for later comitment by md_rename()
*/
}
/*
* exchange up (self->parent)
*/
static void
{
/*
* self id changes in our own unit struct
* Note:
* - Since we're assuming the identity of "to" we use its mnum
* while we're updating the "to" structures.
*/
/*
* our parent identifier becomes the new parent, who was "from"
*/
/*
* point the set array pointers at the "new" unit and unit in-cores
* Note:
* - The other half of this transfer is done in the "update from"
*/
/*
* transfer kstats
*/
/*
* the unit in-core reference to the get next link's id changes
*/
/*
* name space additions, if necessary, were done from user-level.
* name space deletions, if necessary, were done in "exchange_from"
*/
/*
* and store the record id (from the unit struct) into recids
* for later comitment by md_rename()
*/
}
/*
* The order of the called role swap functions is critical.
* If they're not ordered as "all parents", then "all self"
* then "all child" transitions, we will almost certainly
* corrupt the data base and the in-core linkages. So,
* verify that the list built by the individual drivers is
* ok here.
*
* We could have done fancy bit encodings of the roles so
* it all fit into a single word and we wouldn't need the
* prev_ord field. But, since cpu power is cheaper than
* than people power, they're all separate for easier
* debugging and maintaining. (In the unlikely event that
* algorithm is the bottleneck, we should revisit this.)
*/
static bool_t
int previous,
int current,
{
/*
* we've backed up in processing the role table
*/
goto out;
}
/*
* we're repeating the same role transition
*/
case MDRR_PARENT:
/*
* require at least one of the devices to
* be multiparented for us to allow another
* parent transition
*/
goto out;
}
break;
case MDRR_CHILD:
/* it's ok to have multiple children */
break;
case MDRR_SELF:
/* it's never ok to have multiple self transitions */
/* FALLTHROUGH */
default:
goto out;
}
}
out:
if (!valid) {
if (md_rename_debug != 0) {
}
}
return (valid);
}
static role_change_tab_t *
{
}
}
/*
* we require a named svc if we've got two devices
* claiming to be changing roles in this manner
*/
if (!found ||
return (NULL);
}
return (found);
}
/*
* fill in the role swap named svc., now that we know each device
* and its changing role
*/
static int
)
{
md_rendelta_t *r;
int err = 0;
err = EOPNOTSUPP;
goto out;
}
r->dev, /* modindex */ 0,
/*
* someone probably called the ioctl directly and
* incorrectly, rather than via the libmeta wrappers
*/
if (!(r->role_swap)) {
err = EOPNOTSUPP;
goto out;
}
goto out;
}
found_self = TRUE;
}
md_getminor(r->dev));
goto out;
}
}
/*
* must be at least one selfish device
*/
if (!found_self) {
goto out;
}
out:
return (err);
}
/*
* dump contents of rename transaction
*/
static void
if (md_rename_debug == 0) {
return;
}
if (rtxnp) {
"revision: %d, uflags: %d, rec_idx: %d, n_recids: %d, rec_ids: %p%s",
}
}
/*
* dump contents of all deltas
*/
static void
{
md_rendelta_t *r;
int i;
if (md_rename_debug == 0) {
return;
}
" lock: %lx, unlock: %lx\n\t check: %lx, role_swap: %lx",
}
}
}
/*
* validate the rename request parameters
*/
static int
{
from_min);
return (ENOTSUP);
}
break;
case MD_RENAME_VERSION_ONLINE:
/* not supported until 5.0 */
/* FALLTHROUGH */
default:
from_min);
return (EPROTONOSUPPORT);
}
return (ENODEV);
}
return (ENODEV);
}
return (EINVAL);
}
return (EINVAL);
}
case MDRNOP_EXCHANGE:
/*
* exchange requires target to exist
*/
to_min);
return (ENODEV);
}
return (EINVAL);
}
/*
* <from> is not in the role of <self>,
* that is,
* <from> has a parent, which is <to> and <to> has a parent too
* or
* <to> has a parent, which is <from> and <to> can have a child
*/
from_min);
return (EINVAL);
}
from_min);
return (EINVAL);
}
break;
case MDRNOP_RENAME:
/*
* rename requires <to> not to exist
*/
to_min);
return (EEXIST);
}
/*
* and to be within valid ranges for the current
* limits on number of sets and metadevices
*/
return (EINVAL);
}
break;
default:
from_min);
return (EINVAL);
}
/*
* install guard rails
*/
return (0);
}
/*
* If the device being changed exhibits this capability, set the list
* relatives function pointer to the named service that lists the
* appropriate relatives for this capability.
*/
static int
char *svc_name,
)
{
int err;
err = 0;
goto out;
}
if ((capability == MD_CAN_DO_ANYTHING) ||
/* modindex */ 0, svc_name,
(intptr_t (*)()) default_svc_func);
if (!(*list_relatives_funcp)) {
goto out;
}
} else {
}
out:
return (err);
}
/*
* call list relations function, bump recid counter
* by number of members added to the delta list.
* Validate that the number of members added is within bounds.
*/
static int
int valid_min,
int valid_max
)
{
int n_added;
int err = 0;
goto out;
}
n_added = 0;
/* no relations of this type */
if (!add_relatives_funcp) {
goto out;
}
}
goto out;
}
out:
return (err);
}
/*
* build recid array
*/
static int
{
int err = 0;
goto out;
}
goto out;
}
KM_SLEEP);
goto out;
}
out:
if (err != 0) {
}
return (err);
}
/*
* build family tree (parent(s), self, children)
* The order of the resultant list is important, as it governs
* the order of locking, checking and changing the unit structures.
* Since we'll be changing them, we may not use the MD_UNIT, MDI_UNIT,
* and other pointer which depend on the array being correct.
* Use only the cached pointers (in rtxnp.)
*/
static md_rendelta_t *
{
int err;
if (err) {
goto out;
}
if (err) {
goto out;
}
/* no default list func */ ((int (*)()) NULL),
&add_kids);
if (err) {
goto out;
}
goto out;
}
goto out;
}
if (err != 0) {
goto out;
}
/*
* delta tree is still empty?
*/
goto out;
}
/*
* verify role change interactions
*/
goto out;
}
goto out;
}
out:
if (err != 0) {
}
return (family);
}
/*
* calls individual driver named service entry points
* to build a list of devices which need state changed,
* to verify that they're in a state where renames may occur,
* and to modify themselves into their new identities
*/
int
{
int err = 0;
return (EINVAL);
return (EINVAL);
}
/*
* Early exit if top is eof trans
*/
break;
} else {
}
}
return (EINVAL);
}
/*
* encapsulate user parameters
*/
goto cleanup;
}
/*
* catch this early, before taking any locks
*/
goto cleanup;
}
/*
* Locking and re-validation (of the per-unit state) is
* the array lock.
*/
/*
* rtxn is filled in on succesful completion of validate_txn_parms()
*/
goto cleanup;
}
/*
* build list of work to do, the "delta tree" for related devices
*/
goto cleanup;
}
goto cleanup;
}
goto cleanup;
}
/*
* let folks know
*/
}
if (family) {
}
}
}
}
static role_change_tab_t
role_swap_tab[] =
{
{
1, /* ordinal */
MDRR_PARENT, /* old role */
MDRR_PARENT, /* new role */
MDRNM_UPDATE_KIDS, /* named service */
NO_DEFAULT_ROLESWAP_SVC /* default role swap function */
},
{
2,
},
{
3,
},
{
4,
},
{
5,
},
{
6,
},
{
7,
},
{
8,
},
{
9,
},
/* terminator is old_role == MDRR_UNK */
{
0,
}
};