/*
* 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.
* Copyright 2012 Milan Jurik. All rights reserved.
*/
#include <sys/sysmacros.h>
extern int md_status;
extern md_ops_t trans_md_ops;
extern md_krwlock_t md_unit_array_rw;
static mt_unit_t *
{
return (NULL);
}
return (NULL);
}
}
return (NULL);
}
return ((mt_unit_t *)1);
}
return (NULL);
}
if (flags & ARRAY_WRITER)
else if (flags & ARRAY_READER)
else /* RD_LOCK */
}
return (NULL);
}
return (un);
}
#ifdef DEBUG
/*
* DEBUG ROUTINES
* THESE ROUTINES ARE ONLY USED WHEN ASSERTS ARE ENABLED
*/
extern int (*mdv_strategy_tstpnt)(buf_t *, int, void*);
/*
* return the global stats struct
*/
static int
{
return (0);
}
return (EFAULT);
sizeof (struct transstats), mode))
return (EFAULT);
return (0);
}
/*
* test ioctls
*/
/*
* TEST TRYGETBLK
*/
/*ARGSUSED1*/
static int
{
int test;
return (EINVAL);
/*
* test 1 -- don't find nonexistant buf
*/
test = 1;
goto errout;
/*
* test 2 - don't find stale buf
*/
test = 2;
goto errout;
goto errout;
/*
* test 3 -- don't find busy buf
*/
test = 3;
goto errout;
goto errout;
/*
* test 4 -- don't find not-done buf
*/
test = 4;
goto errout;
goto errout;
/*
* test 5 -- find an idle buf
*/
test = 5;
goto errout;
goto errout;
bp = 0;
test = 0; /* no test failed */
if (bp) {
}
if (test)
return (EINVAL);
return (0);
}
/*
* TEST TRYGETPAGE
*/
static page_t *
{
/*
* get a locked page
*/
return (NULL);
/*
* get the iolock
*/
if (!page_io_trylock(pp)) {
return (NULL);
}
return (pp);
}
/*ARGSUSED1*/
static int
{
int test;
extern void pvn_io_done(struct page *);
return (EINVAL);
/*
* get rid of the devices pages
*/
/*
* test 1 -- don't find nonexistant page
*/
test = 1;
goto errout;
/*
* test 2 -- don't find busy page
*/
test = 2;
goto errout;
if (trans_trypage(cvp, 0))
goto errout;
pp = 0;
/*
* test 3 - find an idle page
*/
test = 3;
goto errout;
goto errout;
pp = 0;
test = 0; /* no test failed */
if (pp)
/*
* get rid of the file's pages
*/
if (test)
return (EINVAL);
return (0);
}
/*
* TEST TSD
*/
struct tothread {
int test;
int error;
int exits;
int step;
};
static int allocatorvalue;
static int okdestructoralloc;
static void
{
/*
* wait for other thread
*/
}
static void
{
/*
* wakeup other threads
*/
}
static void
{
int exits;
/*
* check that threads clean up *all* TSD at exit
*/
}
static void
{
okdestructoralloc = 0;
if (value) {
if (*value == allocatorvalue)
okdestructoralloc = 1;
}
}
static void *
trans_test_allocator(void)
{
int *value;
*value = allocatorvalue;
return ((void *)value);
}
/*
* thread used to test TSD destroy functionality
*/
static void
{
int i;
/*
* Register cpr callback
*/
"trans_test_thread");
/*
* get some TSD
*/
for (i = NKEYS - 1; i >= 0; --i)
goto errout;
}
/*
* tell parent that we have TSD
*/
/*
* wait for parent to destroy some of our TSD
*/
/*
* make sure that the appropriate TSD was destroyed
*/
goto errout;
}
for (i = 0; i < NKEYS; ++i)
goto errout;
}
/*
* set up cpr exit
*/
thread_exit();
/*
* error -- make sure the parent will wake up (error code in tp)
*/
/*
* set up cpr exit
*/
thread_exit();
}
static void
{
/*
* initialize the per thread struct and make a thread
*/
}
/*
* driver for TSD tests -- *NOT REENTRANT*
*/
/*ARGSUSED1*/
static int
{
int test;
int i;
int error;
/*
* destroy old keys, if any
*/
for (i = 0; i < NKEYS; ++i)
tsd_destroy(&keys[i]);
/*
* test 1 -- simple create and destroy keys tests
*/
test = 1;
error = 0;
for (i = 0; i < NKEYS; ++i) {
/* get with no set should return NULL */
error = 100;
goto errout;
}
/* destroyed key should be 0 */
tsd_destroy(&keys[i]);
if (keys[i]) {
error = 110;
goto errout;
}
/* destroy the key twice */
tsd_destroy(&keys[i]);
/* destroyed key should be 0 */
if (keys[i]) {
error = 120;
goto errout;
}
/* getting a destroyed key should return NULL */
error = 130;
goto errout;
}
/* recreate the key */
/* should be the same key as before */
error = 140;
goto errout;
}
/* initial value should be NULL */
error = 150;
goto errout;
}
/* cleanup */
tsd_destroy(&keys[i]);
}
/*
* test 2 -- recreate keys
*/
test = 2;
error = 0;
for (i = 0; i < NKEYS; ++i)
for (i = 0; i < NKEYS; ++i) {
/* make sure the keys were created */
if (keys[i] == 0) {
error = 200;
goto errout;
}
/* make sure that recreating key doesn't change it */
error = 210;
goto errout;
}
}
for (i = 0; i < NKEYS; ++i)
tsd_destroy(&keys[i]);
/*
* test 3 -- check processing for unset and destroyed keys
*/
test = 3;
error = 0;
/* getting a 0 key returns NULL */
error = 300;
goto errout;
}
/* setting a 0 key returns error */
error = 310;
goto errout;
}
/* setting a created key returns no error */
error = 320;
goto errout;
}
tsd_destroy(&key);
/* setting a destroyed key returns error */
error = 330;
goto errout;
}
/*
* test 4 -- make sure that set and get work
*/
test = 4;
error = 0;
for (i = 0; i < NKEYS; ++i) {
/* set a value */
/* get the value */
error = 400;
goto errout;
}
/* set the value to NULL */
/* get the NULL */
error = 410;
goto errout;
}
}
/* cleanup */
for (i = 0; i < NKEYS; ++i)
tsd_destroy(&keys[i]);
/*
*/
test = 5;
error = 0;
/* create the keys */
for (i = 0; i < NKEYS; ++i)
/* create some threads */
for (i = 0; i < NTSDTHREADS; ++i)
trans_test_threadcreate(&tta[i]);
/* wait for the threads to assign TSD */
for (i = 0; i < NTSDTHREADS; ++i)
/* destroy some of the keys */
tsd_destroy(&keys[0]);
/* wakeup the threads -- they check that the destroy took */
for (i = 0; i < NTSDTHREADS; ++i)
/* wait for the threads to exit (also checks for TSD cleanup) */
for (i = 0; i < NTSDTHREADS; ++i)
/* destroy the rest of the keys */
for (i = 0; i < NKEYS; ++i)
tsd_destroy(&keys[i]);
/* check for error */
for (i = 0; i < NTSDTHREADS; ++i) {
if (!error)
}
/*
* test 6 -- test getcreate
*/
test = 6;
error = 0;
/* make sure the keys are destroyed */
for (i = 0; i < NKEYS; ++i)
tsd_destroy(&keys[i]);
for (i = 0; i < NKEYS; ++i) {
allocatorvalue = i;
error = 600;
goto errout;
}
}
for (i = 0; i < NKEYS; ++i) {
allocatorvalue = i;
error = 610;
goto errout;
}
}
/* make sure destructor gets called when we destroy the keys */
for (i = 0; i < NKEYS; ++i) {
allocatorvalue = i;
okdestructoralloc = 0;
tsd_destroy(&keys[i]);
if (okdestructoralloc == 0) {
error = 620;
goto errout;
}
}
/* make sure the keys are destroyed */
for (i = 0; i < NKEYS; ++i)
tsd_destroy(&keys[i]);
/* return test # and error code (if any) */
return (error);
}
/*
* Error Injection Structures, Data, and Functions:
*
* Error injection is used to test the Harpy error recovery system. The
* MD_IOC_INJECTERRORS ioctl is used to start or continue error injection on a
* unit, and MD_IOC_STOPERRORS turns it off. An mt_error structure is
* associated with every trans device for which we are injecting errors. When
* MD_IOC_INJECTERRORS is issued, mdv_strategy_tstpnt is set to point to
* trans_error_injector(), so that it gets called for every MDD I/O operation.
*
* The trans unit can be in one of three states:
*
* count down - Each I/O causes er_count_down to be decremented.
* When er_count_down reaches 0, an error is injected,
* the block number is remembered. Without makeing
* special provisions, the log area would receive a
* small percentage of the injected errors. Thus,
* trans_check_error() will be written, so that every
* other error is injected on the log.
*
* suspend - No errors are generated and the counters are not
* (we're not testing them) and so that the test script can
* set up another test. The transition back to the count
* down state occurs when MD_IOC_INJECTERRORS is invoked
* again.
*/
typedef enum {
} mte_state;
typedef struct mt_error {
/* Following fields describe error we are injecting. */
/* error. */
} mt_error_t;
static mt_error_t *
{
*pred_errp = &error_list_head;
break;
}
return (errp);
}
static mt_error_t *
{
break;
}
return (errp);
}
static int
{
int rv = 0;
case mte_count_down:
errp->er_count_down--;
if (errp->er_count_down == 0) {
/*
* Every other error that we inject should be on
* the log device. Errors will be injected on the
* log device when errp->er_total_errors is even
* and on the master device when it is odd. If
* this I/O is not for the appropriate device, we
* will set errp->er_count_down to 1, so that we
* can try again later.
*/
/* simulate an error */
/* remember the error. */
errp->er_total_errors++;
/* reset counters. */
rv = 1;
} else {
/* Try again next time. */
}
}
break;
case mte_suspend:
/* No errors while suspended. */
break;
case mte_watch_block:
rv = 1;
}
break;
}
return (rv);
}
static int
{
int rv = 0;
int trv = 0;
/* Target is our master device. */
}
/*
* Target is our log device. Unfortunately, the same
* device may also be used for the MDD database.
* Therefore, we need to make sure that the I/O is for
* the range of blocks designated as our log.
*/
}
}
}
/*
* If we are producing an error (rv != 0) we need to make sure that
* biodone gets called. If the tstpnt returned non-zero,
* we'll assume that it called biodone.
*/
md_biodone(bp);
}
return (rv);
}
/*
* Prepare to inject errors on the master and log devices associated with the
* unit specified in migp. The first time that trans_inject_errors() is called
* for a unit, an mt_error_t structure is allocated and initialized for the
* unit. Subsequent calls for the unit will just insure that the unit is in the
* count down state.
*
* If an mt_error structure is allocated and it is the first one to be put in
* the list, mdv_strategy_tstpnt (which is referenced in md_call_strategy()) is
* set to trans_error_injector so that it will be called to see if an I/O
* request should be treated as an error.
*/
/*ARGSUSED1*/
static int
{
int rv = 0;
return (EINVAL);
/*
* If there is already a an error structure for the unit make sure that
* it is in count down mode.
*/
} else {
/*
* Initialize error structure.
*/
errp->er_total_errors = 0;
errp->er_bad_unit = 0;
errp->er_bad_block = 0;
/* Insert it into the list. */
/*
* Set up md_call_strategy to call our error injector.
*/
if (mdv_strategy_tstpnt != trans_error_injector) {
}
}
return (rv);
}
/*ARGSUSED1*/
static int
{
int rv = 0;
return (EINVAL);
/* Remove from list. */
}
} else {
/* unit not set up for errors. */
}
/* Free memory. */
}
return (rv);
}
int
{
return (1);
}
int
{
return (1);
}
/*
* END OF DEBUG ROUTINES
*/
#endif /* DEBUG */
/*
* BEGIN RELEASE DEBUG
* The following routines remain in the released product for testability
*/
/*
* ufs error injection remains in the released product
*/
/*ARGSUSED1*/
static int
{
return (EINVAL);
return (0);
}
/*
* shadow test remains in the released product
*/
static int
{
return (EINVAL);
return (EINVAL);
/* Get shadow device. User always passes down 32 bit devt */
return (EFAULT);
}
/* Save shadow device designator. */
return (0);
}
/*
* END RELEASE DEBUG
*/
static int
{
return (0);
return (0);
}
return (EFAULT);
log:
goto master;
/*
* refresh log fields in case log was metattach'ed
*/
/*
* check for log dev dynconcat; can only pick up extra space when the
* tail physically follows the head in the circular log
*/
}
}
return (EFAULT);
return (0);
}
static int
{
}
}
return (0);
}
/*ARGSUSED1*/
static int
{
return (0);
/*
* check for master dev dynconcat
*/
struct mdc_unit *c;
if (c->un_total_blocks > MD_MAX_BLKS_FOR_SMALL_DEVS) {
} else {
}
}
return (0);
}
/*ARGSUSED1*/
static int
{
int error;
/* acquire both md_unit_array_rw, and unit_reader lock */
return (0);
/*
*/
return (EACCES);
/*
* detach the log
*/
return (error);
}
static int
{
return (0);
return (0);
}
return (EFAULT);
mode))
return (EFAULT);
return (0);
}
static int
{
int ndev;
return (0);
return (0);
}
return (ENODEV);
}
return (EFAULT);
return (ENODEV);
}
return (EFAULT);
return (0);
}
static int
{
int error;
return (0);
/* This prevents new opens */
}
}
/*
* detach the log
*/
/*
* reset (aka remove; aka delete) the trans device
*/
if (error == 0)
return (error);
}
static int
{
return (0);
}
static int
{
return (0);
}
static int
{
return (0);
}
static int
{
return (ENXIO);
return (0);
}
static int
)
{
}
static int
{
}
static int
)
{
return (0);
}
static int
{
void *d = NULL;
int err = 0;
/* We can only handle 32-bit clients for internal commands */
return (EINVAL);
}
switch (cmd) {
case MD_IOCGET:
{
return (EACCES);
sz = sizeof (md_i_get_t);
return (ENOMEM);
break;
}
break;
}
case MD_IOCGET_LOG:
{
return (EACCES);
sz = sizeof (md_i_get_t);
return (ENOMEM);
break;
}
break;
}
case MD_IOCRESET:
{
md_i_reset_t *p;
return (EACCES);
return (ENOMEM);
break;
}
break;
}
case MD_IOCGROW:
{
return (EACCES);
sz = sizeof (md_grow_params_t);
return (ENOMEM);
break;
}
break;
}
case MD_IOC_TRANS_DETACH:
{
return (EACCES);
sz = sizeof (md_i_get_t);
return (ENOMEM);
break;
}
break;
}
case MD_IOCREPLACE:
{
replace_params_t *p;
return (EACCES);
return (ENOMEM);
break;
}
err = trans_replace(p);
break;
}
case MD_IOCGET_DEVS:
{
return (EACCES);
sz = sizeof (md_getdevs_params_t);
return (ENOMEM);
break;
}
break;
}
/*
* debug ioctls
*/
#ifdef DEBUG
case MD_IOCGET_TRANSSTATS:
{
return (EACCES);
sz = sizeof (md_i_get_t);
return (ENOMEM);
break;
}
break;
}
case MD_IOC_DEBUG:
{
return (EACCES);
sz = sizeof (md_i_get_t);
return (ENOMEM);
break;
}
mdigp = d;
break;
}
case MD_IOC_TSD:
{
return (EACCES);
sz = sizeof (md_i_get_t);
return (ENOMEM);
break;
}
break;
}
case MD_IOC_TRYGETBLK:
{
return (EACCES);
sz = sizeof (md_i_get_t);
return (ENOMEM);
break;
}
break;
}
case MD_IOC_TRYPAGE:
{
return (EACCES);
sz = sizeof (md_i_get_t);
return (ENOMEM);
break;
}
break;
}
case MD_IOC_INJECTERRORS:
{
return (EACCES);
sz = sizeof (md_i_get_t);
return (ENOMEM);
break;
}
break;
}
case MD_IOC_STOPERRORS:
{
return (EACCES);
sz = sizeof (md_i_get_t);
return (ENOMEM);
break;
}
break;
}
case MD_IOC_ISDEBUG:
break;
#else /* ! DEBUG */
case MD_IOC_ISDEBUG:
case MD_IOCGET_TRANSSTATS:
case MD_IOC_STOPERRORS:
case MD_IOC_TSD:
case MD_IOC_TRYGETBLK:
case MD_IOC_TRYPAGE:
break;
/*
* error injection behaves like MD_IOC_UFSERROR in released product
*/
case MD_IOC_INJECTERRORS:
{
return (EACCES);
sz = sizeof (md_i_get_t);
return (ENOMEM);
break;
}
break;
}
/*
* only the shadow test is allowed in the released product
*/
case MD_IOC_DEBUG:
{
return (EACCES);
sz = sizeof (md_i_get_t);
return (ENOMEM);
break;
}
mdigp = d;
break;
}
#endif /* ! DEBUG */
/*
* BEGIN RELEASE DEBUG
* The following routines remain in the released product for testability
*/
case MD_IOC_UFSERROR:
{
return (EACCES);
sz = sizeof (md_i_get_t);
return (ENOMEM);
break;
}
break;
}
case MD_IOC_SETSHADOW:
{
return (EACCES);
sz = sizeof (md_i_get_t);
return (ENOMEM);
break;
}
break;
}
/*
* END RELEASE DEBUG
*/
default:
return (ENOTTY);
}
/*
* copyout and free any args
*/
if (sz != 0) {
if (err == 0) {
}
}
md_trans_free(d, sz);
}
return (err);
}
int
{
int err = 0;
/* handle admin ioctls */
if (mnum == MD_ADM_MINOR)
/* check unit */
return (ENXIO);
/* dispatch ioctl */
switch (cmd) {
case DKIOCINFO:
{
struct dk_cinfo *p;
return (EACCES);
if ((p = md_trans_zalloc(sizeof (*p))) == NULL)
return (ENOMEM);
md_trans_free(p, sizeof (*p));
return (err);
}
case DKIOCGGEOM:
{
struct dk_geom *p;
return (EACCES);
if ((p = md_trans_zalloc(sizeof (*p))) == NULL)
return (ENOMEM);
mode) != 0)
}
md_trans_free(p, sizeof (*p));
return (err);
}
case DKIOCGVTOC:
{
return (EACCES);
return (err);
}
}
#ifdef _SYSCALL32
else {
}
#endif /* _SYSCALL32 */
return (err);
}
case DKIOCSVTOC:
{
return (EACCES);
}
}
#ifdef _SYSCALL32
else {
} else {
}
}
#endif /* _SYSCALL32 */
if (err == 0)
return (err);
}
case DKIOCGEXTVTOC:
{
return (EACCES);
return (err);
}
return (err);
}
case DKIOCSEXTVTOC:
{
return (EACCES);
}
if (err == 0)
return (err);
}
case DKIOCGAPART:
{
return (err);
}
mode) != 0)
}
#ifdef _SYSCALL32
else {
mode) != 0)
}
#endif /* _SYSCALL32 */
return (err);
}
/*
* error recovery.
*/
case _FIOISLOG:
return (trans_islog(un));
default:
return (ENOTTY);
}
}
/*
* rename named service entry points and support functions
*/
/*
* 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.
*/
void
{
/*
* self id changes in our own unit struct
* both mechanisms for identifying the trans must be reset.
*/
/*
* 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
*/
/*
* 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
*/
/*
* and store the record id (from the unit struct) into recids
* for later commitment by md_rename()
*/
}
/*
* MDRNM_UPDATE_KIDS
*/
void
{
/*
* since our role isn't changing (parent->parent)
* one of our children must be changing; which one is it?
* find the child being modified, and update
* our notion of it
*/
/* both devices must be metadevices in order to be updated */
(master_min == from_min)) {
} else {
panic("trans_renexch_update_kids: not a metadevice");
/*NOTREACHED*/
}
}
/*
* MDRNM_SELF_UPDATE_FROM (exchange down) [self->child]
*/
void
{
/*
* if we're exchanging a trans, it had better be a metadevice
*/
/*
* both mechanisms for identifying a trans must be updated
*/
/*
* parent identifier need not change
*/
/*
* 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 to"
*/
/*
* transfer kstats
*/
/*
* the unit in-core reference to the get next link's id changes
*/
/*
* which one of our children is changing?
*
* Note that the check routines forbid changing the log (for now)
* because there's no lockfs-like trans-ufs "freeze and remount"
* or "freeze and bobbit the log."
*/
/* both devices must be metadevices in order to be updated */
(master_min == to_min)) {
/* master and log can't both be changed */
/* master and log can't both be changed */
} else {
panic("trans_exchange_self_update_from_down: not a metadevice");
/*NOTREACHED*/
}
/*
* the new master must exist in the name space
*/
/*
* delete the key for the changed child from the namespace
*/
/*
* and store the record id (from the unit struct) into recids
*/
}
/*
* MDRNM_PARENT_UPDATE_TO (exchange down) [parent->self]
*/
void
{
/*
* both mechanisms for identifying a trans must be updated
*/
/*
* parent identifier need not change
*/
/*
* 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 to"
*/
/*
* transfer kstats
*/
/*
* the unit in-core reference to the get next link's id changes
*/
/*
* which one of our children is changing?
*/
/* both devices must be metadevices in order to be updated */
(master_min == from_min)) {
/* can't be changing log and master */
/* can't be changing log and master */
} else {
panic("trans_exchange_parent_update_to: not a metadevice");
/*NOTREACHED*/
}
/*
* delete the key for the changed child from the namespace
*/
/*
* and store the record id (from the unit struct) into recids
*/
}
/*
* MDRNM_LIST_URKIDS: named svc entry point
* all all delta entries appropriate for our children onto the
* deltalist pointd to by dlpp
*/
int
{
int n_children;
n_children = 0;
return (-1);
}
/* NULL */
}
if (!new) {
}
return (-1);
}
++n_children;
}
if (!new) {
}
return (-1);
}
++n_children;
}
return (n_children);
}
/*
* support routine for MDRNM_CHECK
*/
static int
mdi_unit_t *ui,
{
from_min);
return (EINVAL);
}
return (EINVAL);
}
return (EINVAL);
}
case MDRNOP_EXCHANGE:
/*
* may only swap with our child (master) if it is a metadevice
*/
to_min);
return (EINVAL);
}
to_min);
return (EINVAL);
}
to_min);
return (EINVAL);
}
break;
case MDRNOP_RENAME:
break;
default:
from_min);
return (EINVAL);
}
return (0); /* ok */
}
/*
* Named service entry point: MDRNM_CHECK
*/
{
int err = 0;
return (EINVAL);
}
/*
* trans' may not be open, if it is being modified in the exchange
* or rename; trans-UFS hasn't been verified to handle the change
* out from underneath it.
*/
return (EBUSY);
}
}
/*
* can't rename or exchange with a log attached
*/
return (EBUSY);
}
case MDRR_SELF:
/*
* self does additional checks
*/
if (err != 0) {
goto out;
}
/* FALLTHROUGH */
case MDRR_PARENT:
/*
* top_is_trans is only used to check for online
* since trans holds the sub-devices open
*/
break;
default:
break;
}
out:
return (err);
}