root/usr/src/uts/common/io/usb/clients/usbser/usbser_rseq.c
/*
 * 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
 * or http://www.opensolaris.org/os/licensing.
 * 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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


/*
 * rseq implementation
 */

#include <sys/usb/clients/usbser/usbser_rseq.h>

#ifdef _KERNEL
#include <sys/debug.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
static long rseq_random();
#define random  rseq_random
#else
#include <assert.h>
#define ASSERT  assert
#include <stdlib.h>
#endif


/*ARGSUSED*/
static int
rseq_do_common(rseq_t *rseq, int num, uintptr_t arg, int flags, int fail_err,
                uintptr_t fail_num)
{
        int             i;
        rseq_step_t     *s;
        int             rval = RSEQ_OK;

        for (i = 0; i < num; i++) {
                s = &rseq[i].r_do;

                if (s->s_func == NULL) {
                        continue;
                }
                s->s_rval = (i != fail_num) ? s->s_func(arg) : fail_err;
                rval = (s->s_cb) ? (s->s_cb(rseq, i, arg)) : RSEQ_OK;

                if (rval == RSEQ_UNDO) {
                        (void) rseq_undo(rseq, i, arg, flags);
                        break;
                } else if (rval == RSEQ_ABORT) {
                        break;
                }
                ASSERT(rval == RSEQ_OK);
        }
        return (rval);
}


/*ARGSUSED*/
static int
rseq_undo_common(rseq_t *rseq, int num, uintptr_t arg, int flags, int fail_err,
                uintptr_t fail_num)
{
        int             i;
        rseq_step_t     *s;
        int             rval = RSEQ_OK;

        for (i = num - 1; i >= 0; i--) {
                s = &rseq[i].r_undo;

                if (s->s_func == NULL) {
                        continue;
                }
                s->s_rval = (i != fail_num) ? s->s_func(arg) : fail_err;
                rval = (s->s_cb) ? (s->s_cb(rseq, i, arg)) : RSEQ_OK;

                if (rval == RSEQ_ABORT) {
                        break;
                }
                ASSERT(rval == RSEQ_OK);
        }
        return (rval);
}


int
rseq_do(rseq_t *rseq, int num, uintptr_t arg, int flags)
{
        return (rseq_do_common(rseq, num, arg, flags, 0, -1));
}


int
rseq_undo(rseq_t *rseq, int num, uintptr_t arg, int flags)
{
        return (rseq_undo_common(rseq, num, arg, flags, 0, -1));
}

#ifdef DEBUG

#ifndef __lock_lint

static int
rseq_debug(rseq_t *rseq, int num, uintptr_t arg, int flags, int scenario,
                uintptr_t sarg1, uintptr_t sarg2,
                int (*func)(rseq_t *, int, uintptr_t, int, int, uintptr_t))
{
        int     rnd, rval = RSEQ_OK, i;

        switch (scenario) {
        case RSEQ_DBG_FAIL_ONE:
                rval = func(rseq, num, arg, flags, sarg1, sarg2);
                break;
        case RSEQ_DBG_FAIL_ONE_RANDOM:
                rnd = random() % num;
                rval = func(rseq, num, arg, flags, sarg1, rnd);
                break;
        case RSEQ_DBG_FAIL_ONEBYONE:
                for (i = 0; i < num; i++) {
                        rval = func(rseq, num, arg, flags, sarg1, i);
                        /*
                         * when aborted, the undo path is not executed, so we
                         * can't continue without the risk of resource leakage.
                         */
                        if (rval == RSEQ_ABORT) {
                                break;
                        }
                }
                break;
        default:
                ASSERT(!"rseq_debug: incorrect debug scenario");
                rval = RSEQ_ABORT;
        }
        return (rval);
}


int
rseq_do_debug(rseq_t *rseq, int num, uintptr_t arg, int flags, int scenario,
                uintptr_t sarg1, uintptr_t sarg2)
{
        return (rseq_debug(rseq, num, arg, flags, scenario, sarg1, sarg2,
            rseq_do_common));
}


int
rseq_undo_debug(rseq_t *rseq, int num, uintptr_t arg, int flags, int scenario,
                uintptr_t sarg1, uintptr_t sarg2)
{
        return (rseq_debug(rseq, num, arg, flags, scenario, sarg1, sarg2,
            rseq_undo_common));
}

#ifdef _KERNEL
static long
rseq_random()
{
        return (ddi_get_lbolt());
}
#endif /* _KERNEL */

#endif /* __lock_lint */

#endif /* DEBUG */