root/usr/src/uts/sun4u/opl/io/oplmsu/oplmsu_cmn_func.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
 */
/*
 * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006
 */

#include <sys/errno.h>
#include <sys/modctl.h>
#include <sys/stat.h>
#include <sys/kmem.h>
#include <sys/ksynch.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/termio.h>
#include <sys/ddi.h>
#include <sys/file.h>
#include <sys/disp.h>
#include <sys/sunddi.h>
#include <sys/sunldi.h>
#include <sys/sunndi.h>
#include <sys/strsun.h>
#include <sys/oplmsu/oplmsu.h>
#include <sys/oplmsu/oplmsu_proto.h>

/*
 * Link upper_path_table structure
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_WRITER]
 *  -. uinst_t->u_lock : M
 *  -. uinst_t->l_lock : A
 *  -. uinst_t->c_lock : A
 */
void
oplmsu_link_upath(upath_t *add_upath)
{

        ASSERT(add_upath != NULL);
        ASSERT(RW_WRITE_HELD(&oplmsu_uinst->lock));
        ASSERT(MUTEX_HELD(&oplmsu_uinst->u_lock));

        if (oplmsu_uinst->first_upath == NULL) {
                oplmsu_uinst->first_upath = add_upath;
                add_upath->u_prev = NULL;
        } else {
                upath_t *last_upath;

                last_upath = oplmsu_uinst->last_upath;
                last_upath->u_next = add_upath;
                add_upath->u_prev = last_upath;
        }

        oplmsu_uinst->last_upath = add_upath;
        add_upath->u_next = NULL;
}

/*
 * Unlink upper_path_table structure
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_WRITER]
 *  -. uinst_t->u_lock : P
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
void
oplmsu_unlink_upath(upath_t *del_upath)
{
        upath_t **first, **last;

        ASSERT(RW_WRITE_HELD(&oplmsu_uinst->lock));

        first = &oplmsu_uinst->first_upath;
        last = &oplmsu_uinst->last_upath;

        if ((*first != del_upath) && (*last != del_upath)) {
                del_upath->u_prev->u_next = del_upath->u_next;
                del_upath->u_next->u_prev = del_upath->u_prev;
        } else {
                if (*first == del_upath) {
                        *first = (*first)->u_next;
                        if (*first) {
                                (*first)->u_prev = NULL;
                        }
                }

                if (*last == del_upath) {
                        *last = (*last)->u_prev;
                        if (*last) {
                                (*last)->u_next = NULL;
                        }
                }
        }

        del_upath->u_next = NULL;
        del_upath->u_prev = NULL;
}

/*
 * Link lower_path_table structure
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_WRITER]
 *  -. uinst_t->u_lock : A
 *  -. uinst_t->l_lock : M
 *  -. uinst_t->c_lock : A
 */
void
oplmsu_link_lpath(lpath_t *add_lpath)
{

        ASSERT(add_lpath != NULL);
        ASSERT(RW_WRITE_HELD(&oplmsu_uinst->lock));

        if (oplmsu_uinst->first_lpath == NULL) {
                oplmsu_uinst->first_lpath = add_lpath;
                add_lpath->l_prev = NULL;
        } else {
                lpath_t *last_lpath;

                last_lpath = oplmsu_uinst->last_lpath;
                last_lpath->l_next = add_lpath;
                add_lpath->l_prev = last_lpath;
        }

        oplmsu_uinst->last_lpath = add_lpath;
        add_lpath->l_next = NULL;
}

/*
 * Unlink lower_path_table structure
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_WRITER]
 *  -. uinst_t->u_lock : P
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
void
oplmsu_unlink_lpath(lpath_t *del_lpath)
{
        lpath_t **first, **last;

        ASSERT(RW_WRITE_HELD(&oplmsu_uinst->lock));

        first = &oplmsu_uinst->first_lpath;
        last = &oplmsu_uinst->last_lpath;

        if ((*first != del_lpath) && (*last != del_lpath)) {
                del_lpath->l_prev->l_next = del_lpath->l_next;
                del_lpath->l_next->l_prev = del_lpath->l_prev;
        } else {
                if (*first == del_lpath) {
                        *first = (*first)->l_next;
                        if (*first) {
                                (*first)->l_prev = NULL;
                        }
                }

                if (*last == del_lpath) {
                        *last = (*last)->l_prev;
                        if (*last) {
                                (*last)->l_next = NULL;
                        }
                }
        }

        del_lpath->l_next = NULL;
        del_lpath->l_prev = NULL;
}

/*
 * Link msgb structure of high priority
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER]
 *  -. uinst_t->u_lock : A
 *  -. uinst_t->l_lock : A [It depends on caller]
 *  -. uinst_t->c_lock : A [It depends on caller]
 */
void
oplmsu_link_high_primsg(mblk_t **first, mblk_t **last, mblk_t *add_msg)
{

        ASSERT(add_msg != NULL);
        ASSERT(RW_READ_HELD(&oplmsu_uinst->lock));

        if (*first == NULL) {
                *first = add_msg;
                add_msg->b_prev = NULL;
        } else {
                (*last)->b_next = add_msg;
                add_msg->b_prev = *last;
        }

        *last = add_msg;
        add_msg->b_next = NULL;
}

/*
 * Check whether lower path is usable by lower path info table address
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : A
 *  -. uinst_t->l_lock : M
 *  -. uinst_t->c_lock : P
 */
int
oplmsu_check_lpath_usable(void)
{
        lpath_t *lpath;
        int     rval = SUCCESS;

        ASSERT(RW_LOCK_HELD(&oplmsu_uinst->lock));
        ASSERT(MUTEX_HELD(&oplmsu_uinst->l_lock));

        lpath = oplmsu_uinst->first_lpath;
        while (lpath) {
                if ((lpath->hndl_uqueue != NULL) || (lpath->hndl_mp != NULL)) {
                        rval = BUSY;
                        break;
                }
                lpath = lpath->l_next;
        }
        return (rval);
}

/*
 * Search upath_t by path number
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : M
 *  -. uinst_t->l_lock : A
 *  -. uinst_t->c_lock : P
 */
upath_t *
oplmsu_search_upath_info(int path_no)
{
        upath_t *upath;

        ASSERT(RW_LOCK_HELD(&oplmsu_uinst->lock));
        ASSERT(MUTEX_HELD(&oplmsu_uinst->u_lock));

        upath = oplmsu_uinst->first_upath;
        while (upath) {
                if (upath->path_no == path_no) {
                        break;
                }
                upath = upath->u_next;
        }
        return (upath);
}

/*
 * Send M_IOCACK(or M_IOCNAK) message to stream
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : P
 *  -. uinst_t->u_lock : P
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
void
oplmsu_iocack(queue_t *q, mblk_t *mp, int errno)
{
        struct iocblk   *iocp = NULL;

        ASSERT(mp != NULL);

        iocp = (struct iocblk *)mp->b_rptr;
        iocp->ioc_error = errno;

        if (errno) {    /* Error */
                mp->b_datap->db_type = M_IOCNAK;
                iocp->ioc_rval = FAILURE;

                OPLMSU_TRACE(q, mp, MSU_TRC_UO);
                qreply(q, mp);
        } else {        /* Good */
                mp->b_datap->db_type = M_IOCACK;
                iocp->ioc_rval = SUCCESS;

                OPLMSU_TRACE(q, mp, MSU_TRC_UO);
                qreply(q, mp);
        }
}

/*
 * Delete all upath_t
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_WRITER]
 *  -. uinst_t->u_lock : M
 *  -. uinst_t->l_lock : A
 *  -. uinst_t->c_lock : A
 */
void
oplmsu_delete_upath_info(void)
{
        upath_t *upath, *next_upath;

        ASSERT(RW_WRITE_HELD(&oplmsu_uinst->lock));
        ASSERT(MUTEX_HELD(&oplmsu_uinst->u_lock));

        upath = oplmsu_uinst->first_upath;
        oplmsu_uinst->first_upath = NULL;
        oplmsu_uinst->last_upath = NULL;

        while (upath) {
                next_upath = upath->u_next;
                kmem_free(upath, sizeof (upath_t));
                upath = next_upath;
        }
}

/*
 * Set queue and ioctl to lpath_t
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : A
 *  -. uinst_t->l_lock : M
 *  -. uinst_t->c_lock : P
 */
int
oplmsu_set_ioctl_path(lpath_t *lpath, queue_t *hndl_queue, mblk_t *mp)
{
        int     rval = SUCCESS;

        ASSERT(RW_LOCK_HELD(&oplmsu_uinst->lock));
        ASSERT(MUTEX_HELD(&oplmsu_uinst->l_lock));

        if ((lpath->hndl_uqueue == NULL) && (lpath->hndl_mp == NULL) &&
            (lpath->sw_flag == 0)) {
                if ((lpath->status == MSU_EXT_NOTUSED) ||
                    (lpath->status == MSU_EXT_ACTIVE_CANDIDATE) ||
                    (lpath->status == MSU_SETID_NU)) {
                        if (hndl_queue == NULL) {
                                lpath->hndl_uqueue = hndl_queue;
                        } else {
                                lpath->hndl_uqueue = WR(hndl_queue);
                        }
                        lpath->hndl_mp = mp;
                } else {
                        rval = BUSY;
                }
        } else {
                rval = BUSY;
        }
        return (rval);
}

/*
 * Clear queue and ioctl to lpath_t
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : A
 *  -. uinst_t->l_lock : M
 *  -. uinst_t->c_lock : P
 */
void
oplmsu_clear_ioctl_path(lpath_t *lpath)
{

        ASSERT(RW_LOCK_HELD(&oplmsu_uinst->lock));
        ASSERT(MUTEX_HELD(&oplmsu_uinst->l_lock));

        lpath->hndl_uqueue = NULL;
        lpath->hndl_mp = NULL;
}

/*
 * Get instanse status from status of upath_t
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : M
 *  -. uinst_t->l_lock : A
 *  -. uinst_t->c_lock : P
 */
int
oplmsu_get_inst_status(void)
{
        upath_t *upath;
        int     sts, pre_sts = INST_STAT_UNCONFIGURED;

        ASSERT(RW_LOCK_HELD(&oplmsu_uinst->lock));
        ASSERT(MUTEX_HELD(&oplmsu_uinst->u_lock));

        upath = oplmsu_uinst->first_upath;
        while (upath) {
                if (((upath->status == MSU_PSTAT_ACTIVE) &&
                    (upath->traditional_status == MSU_ACTIVE)) ||
                    ((upath->status == MSU_PSTAT_STANDBY) &&
                    (upath->traditional_status == MSU_STANDBY))) {
                        sts = INST_STAT_ONLINE;
                } else if (((upath->status == MSU_PSTAT_STOP) &&
                    (upath->traditional_status == MSU_STOP)) ||
                    ((upath->status == MSU_PSTAT_FAIL) &&
                    (upath->traditional_status == MSU_FAIL))) {
                        sts = INST_STAT_OFFLINE;
                } else if (((upath->status == MSU_PSTAT_DISCON) &&
                    (upath->traditional_status == MSU_DISCON)) ||
                    ((upath->status == MSU_PSTAT_EMPTY) &&
                    (upath->traditional_status == MSU_EMPTY))) {
                        sts = INST_STAT_UNCONFIGURED;
                } else {
                        sts = INST_STAT_BUSY;
                }

                if (pre_sts > sts) {
                        pre_sts = sts;
                }
                upath = upath->u_next;
        }
        return (pre_sts);
}

/*
 * Search path of "online:standby" status
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : M
 *  -. uinst_t->l_lock : A
 *  -. uinst_t->c_lock : P
 */
upath_t *
oplmsu_search_standby(void)
{
        upath_t *upath, *altn_upath = NULL;
        int     max_pathnum = UNDEFINED;

        ASSERT(RW_LOCK_HELD(&oplmsu_uinst->lock));
        ASSERT(MUTEX_HELD(&oplmsu_uinst->u_lock));

        upath = oplmsu_uinst->first_upath;
        while (upath) {
                if ((upath->status == MSU_PSTAT_STANDBY) &&
                    (upath->traditional_status == MSU_STANDBY) &&
                    (upath->lpath != NULL)) {
                        if ((max_pathnum == UNDEFINED) ||
                            (max_pathnum > upath->path_no)) {
                                max_pathnum = upath->path_no;
                                altn_upath = upath;
                        }
                }
                upath = upath->u_next;
        }
        return (altn_upath);
}

/*
 * Search path of "offline:stop" status, and minimum path number
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : M
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
void
oplmsu_search_min_stop_path(void)
{
        upath_t *upath, *min_upath;
        lpath_t *lpath;
        int     min_no = UNDEFINED;
        int     active_flag = 0;

        ASSERT(RW_LOCK_HELD(&oplmsu_uinst->lock));
        ASSERT(MUTEX_HELD(&oplmsu_uinst->u_lock));

        upath = oplmsu_uinst->first_upath;
        while (upath) {
                if ((upath->status == MSU_PSTAT_ACTIVE) &&
                    (upath->traditional_status == MSU_ACTIVE)) {
                        active_flag = 1;
                        break;
                } else if ((upath->status == MSU_PSTAT_STOP) &&
                    (upath->traditional_status == MSU_STOP)) {
                        if (upath->lpath != NULL) {
                                if ((min_no == UNDEFINED) ||
                                    (upath->path_no < min_no)) {
                                        lpath = upath->lpath;
                                        mutex_enter(&oplmsu_uinst->l_lock);
                                        if (lpath->status == MSU_EXT_NOTUSED) {
                                                min_upath = upath;
                                                min_no = upath->path_no;
                                        }
                                        mutex_exit(&oplmsu_uinst->l_lock);
                                }
                        }
                }
                upath = upath->u_next;
        }

        if (active_flag == 0) {
                lpath = min_upath->lpath;
                mutex_enter(&oplmsu_uinst->l_lock);
                lpath->src_upath = NULL;
                lpath->status = MSU_EXT_ACTIVE_CANDIDATE;
                mutex_exit(&oplmsu_uinst->l_lock);
        }
}

/*
 * Get the total number of serial paths
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_WRITER]
 *  -. uinst_t->u_lock : M
 *  -. uinst_t->l_lock : A
 *  -. uinst_t->c_lock : A
 */
int
oplmsu_get_pathnum(void)
{
        upath_t *upath;
        int     total_num = 0;

        ASSERT(RW_WRITE_HELD(&oplmsu_uinst->lock));
        ASSERT(MUTEX_HELD(&oplmsu_uinst->u_lock));

        if (oplmsu_uinst->first_upath != NULL) {
                upath = oplmsu_uinst->first_upath;
                while (upath) {
                        total_num++;
                        upath = upath->u_next;
                }
        }
        return (total_num);
}

/*
 * Put XOFF/ XON message on write queue
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : A
 *  -. uinst_t->l_lock : A
 *  -. uinst_t->c_lock : A
 */
int
oplmsu_cmn_put_xoffxon(queue_t *queue, int data)
{
        mblk_t  *mp;
        int     rval = SUCCESS;

        /* Send M_START */
        if ((mp = allocb(0, BPRI_LO)) != NULL) {
                mp->b_datap->db_type = M_START;
                (void) putq(queue, mp);

                /* Send M_DATA(XOFF, XON) */
                if ((mp = allocb(sizeof (int), BPRI_LO)) != NULL) {
                        *(uint_t *)mp->b_rptr = data;
                        mp->b_wptr = mp->b_rptr + sizeof (int);
                        (void) putq(queue, mp);
                } else {
                        rval = FAILURE;
                }
        } else {
                rval = FAILURE;
        }
        return (rval);
}

/*
 * Put XOFF message on write queue for all standby paths
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : M
 *  -. uinst_t->l_lock : M
 *  -. uinst_t->c_lock : P
 */
void
oplmsu_cmn_putxoff_standby(void)
{
        upath_t *upath;
        lpath_t *lpath;

        ASSERT(RW_LOCK_HELD(&oplmsu_uinst->lock));
        ASSERT(MUTEX_HELD(&oplmsu_uinst->u_lock));
        ASSERT(MUTEX_HELD(&oplmsu_uinst->l_lock));

        upath = oplmsu_uinst->first_upath;
        while (upath) {
                lpath = upath->lpath;
                if ((upath->status != MSU_PSTAT_STANDBY) ||
                    (lpath == NULL)) {
                        upath = upath->u_next;
                        continue;
                }

                (void) oplmsu_cmn_put_xoffxon(
                    WR(lpath->lower_queue), MSU_XOFF_4);
                upath = upath->u_next;
        }
}

/*
 * Set M_FLUSH message
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : A [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : A
 *  -. uinst_t->l_lock : A
 *  -. uinst_t->c_lock : A
 */
void
oplmsu_cmn_set_mflush(mblk_t *mp)
{

        mp->b_datap->db_type = M_FLUSH;
        *mp->b_rptr = FLUSHW;
        mp->b_wptr = mp->b_rptr + sizeof (char);
}

/*
 * Set status informations of upath_t
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : M
 *  -. uinst_t->l_lock : A
 *  -. uinst_t->c_lock : A
 */
void
oplmsu_cmn_set_upath_sts(upath_t *upath, int sts, int prev_sts,
    ulong_t trad_sts)
{

        ASSERT(RW_LOCK_HELD(&oplmsu_uinst->lock));
        ASSERT(MUTEX_HELD(&oplmsu_uinst->u_lock));

        upath->status = sts;
        upath->prev_status = prev_sts;
        upath->traditional_status = trad_sts;
}

/*
 * Allocate a message block
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER]
 *  -. uinst_t->u_lock : A
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
int
oplmsu_cmn_allocmb(queue_t *q, mblk_t *mp, mblk_t **nmp, size_t size,
    int rw_flag)
{
        int     rval = SUCCESS;

        ASSERT(RW_READ_HELD(&oplmsu_uinst->lock));

        if ((*nmp = (mblk_t *)allocb(size, BPRI_LO)) == NULL) {
                oplmsu_cmn_bufcall(q, mp, size, rw_flag);
                rval = FAILURE;
        } else {
                (*nmp)->b_wptr = (*nmp)->b_rptr + size;
        }
        return (rval);
}

/*
 * Copy a message
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER]
 *  -. uinst_t->u_lock : A
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
int
oplmsu_cmn_copymb(queue_t *q, mblk_t *mp, mblk_t **nmp, mblk_t *cmp,
    int rw_flag)
{
        int     rval = SUCCESS;

        ASSERT(RW_READ_HELD(&oplmsu_uinst->lock));

        if ((*nmp = copymsg(cmp)) == NULL) {
                oplmsu_cmn_bufcall(q, mp, msgsize(cmp), rw_flag);
                rval = FAILURE;
        }
        return (rval);
}

/*
 * bufcall request
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER]
 *  -. uinst_t->u_lock : A
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
void
oplmsu_cmn_bufcall(queue_t *q, mblk_t *mp, size_t size, int rw_flag)
{

        ASSERT(RW_READ_HELD(&oplmsu_uinst->lock));

        if (rw_flag == MSU_WRITE_SIDE) {
                ctrl_t  *ctrl;

                (void) putbq(q, mp);

                mutex_enter(&oplmsu_uinst->c_lock);
                ctrl = (ctrl_t *)q->q_ptr;
                if (ctrl->wbuf_id != 0) {
                        mutex_exit(&oplmsu_uinst->c_lock);
                        return;
                }

                ctrl->wbuftbl->q = q;
                ctrl->wbuftbl->rw_flag = rw_flag;
                ctrl->wbuf_id = bufcall(size, BPRI_LO, oplmsu_cmn_bufcb,
                    (void *)ctrl->wbuftbl);

                if (ctrl->wbuf_id == 0) {
                        if (ctrl->wtout_id != 0) {
                                mutex_exit(&oplmsu_uinst->c_lock);
                                return;
                        }

                        ctrl->wtout_id = timeout(oplmsu_cmn_bufcb,
                            (void *)ctrl->wbuftbl, drv_usectohz(MSU_TM_500MS));
                }
                mutex_exit(&oplmsu_uinst->c_lock);
        } else if (rw_flag == MSU_READ_SIDE) {
                lpath_t *lpath;
                mblk_t  *wrk_msg;

                mutex_enter(&oplmsu_uinst->l_lock);
                lpath = (lpath_t *)q->q_ptr;
                if (mp->b_datap->db_type >= QPCTL) {
                        if (lpath->first_lpri_hi == NULL) {
                                lpath->last_lpri_hi = mp;
                                mp->b_next = NULL;
                        } else {
                                wrk_msg = lpath->first_lpri_hi;
                                wrk_msg->b_prev = mp;
                                mp->b_next = wrk_msg;
                        }
                        mp->b_prev = NULL;
                        lpath->first_lpri_hi = mp;
                } else {
                        (void) putbq(q, mp);
                }

                if (lpath->rbuf_id != 0) {
                        mutex_exit(&oplmsu_uinst->l_lock);
                        return;
                }

                lpath->rbuftbl->q = q;
                lpath->rbuftbl->rw_flag = rw_flag;
                lpath->rbuf_id = bufcall(size, BPRI_LO, oplmsu_cmn_bufcb,
                    (void *)lpath->rbuftbl);

                if (lpath->rbuf_id == 0) {
                        if (lpath->rtout_id != 0) {
                                mutex_exit(&oplmsu_uinst->l_lock);
                                return;
                        }

                        lpath->rtout_id = timeout(oplmsu_cmn_bufcb,
                            (void *)lpath->rbuftbl, drv_usectohz(MSU_TM_500MS));
                }
                mutex_exit(&oplmsu_uinst->l_lock);
        }
}

/*
 * Previous sequence for active path change
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER]
 *  -. uinst_t->u_lock : A
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
int
oplmsu_cmn_prechg(queue_t *q, mblk_t *mp, int rw_flag, mblk_t **term_mp,
    int *term_ioctl, int *term_stat)
{

        ASSERT(RW_READ_HELD(&oplmsu_uinst->lock));

        if (oplmsu_uinst->tcsets_p != NULL) {
                struct iocblk   *iocp;

                if (oplmsu_cmn_copymb(q, mp, term_mp, oplmsu_uinst->tcsets_p,
                    rw_flag) == -1) {
                        return (FAILURE);
                }

                iocp = (struct iocblk *)(*term_mp)->b_rptr;
                *term_ioctl = iocp->ioc_cmd;
                *term_stat = MSU_WTCS_ACK;
        } else if (oplmsu_uinst->tiocmset_p != NULL) {
                if (oplmsu_cmn_copymb(q, mp, term_mp, oplmsu_uinst->tiocmset_p,
                    rw_flag) == -1) {
                        return (FAILURE);
                }

                *term_ioctl = TIOCMSET;
                *term_stat = MSU_WTMS_ACK;
        } else if (oplmsu_uinst->tiocspps_p != NULL) {
                if (oplmsu_cmn_copymb(q, mp, term_mp, oplmsu_uinst->tiocspps_p,
                    rw_flag) == -1) {
                        return (FAILURE);
                }

                *term_ioctl = TIOCSPPS;
                *term_stat = MSU_WPPS_ACK;
        } else if (oplmsu_uinst->tiocswinsz_p != NULL) {
                if (oplmsu_cmn_copymb(q, mp, term_mp,
                    oplmsu_uinst->tiocswinsz_p, rw_flag) == -1) {
                        return (FAILURE);
                }

                *term_ioctl = TIOCSWINSZ;
                *term_stat = MSU_WWSZ_ACK;
        } else if (oplmsu_uinst->tiocssoftcar_p != NULL) {
                if (oplmsu_cmn_copymb(q, mp, term_mp,
                    oplmsu_uinst->tiocssoftcar_p, rw_flag) == -1) {
                        return (FAILURE);
                }

                *term_ioctl = TIOCSSOFTCAR;
                *term_stat = MSU_WCAR_ACK;
        } else {
                *term_stat = MSU_WPTH_CHG;
                *term_mp = NULL;
        }
        return (SUCCESS);
}

/*
 * Pick up termios to re-set
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : A
 *  -. uinst_t->l_lock : A
 *  -. uinst_t->c_lock : A
 */
int
oplmsu_stop_prechg(mblk_t **term_mp, int *term_ioctl, int *term_stat)
{

        ASSERT(RW_LOCK_HELD(&oplmsu_uinst->lock));

        if (oplmsu_uinst->tcsets_p != NULL) {
                struct iocblk   *iocp;

                if ((*term_mp = copymsg(oplmsu_uinst->tcsets_p)) == NULL) {
                        return (FAILURE);
                }

                iocp = (struct iocblk *)(*term_mp)->b_rptr;
                *term_ioctl = iocp->ioc_cmd;
                *term_stat = MSU_WTCS_ACK;
        } else if (oplmsu_uinst->tiocmset_p != NULL) {
                if ((*term_mp = copymsg(oplmsu_uinst->tiocmset_p)) == NULL) {
                        return (FAILURE);
                }

                *term_ioctl = TIOCMSET;
                *term_stat = MSU_WTMS_ACK;
        } else if (oplmsu_uinst->tiocspps_p != NULL) {
                if ((*term_mp = copymsg(oplmsu_uinst->tiocspps_p)) == NULL) {
                        return (FAILURE);
                }

                *term_ioctl = TIOCSPPS;
                *term_stat = MSU_WPPS_ACK;
        } else if (oplmsu_uinst->tiocswinsz_p != NULL) {
                if ((*term_mp = copymsg(oplmsu_uinst->tiocswinsz_p)) == NULL) {
                        return (FAILURE);
                }

                *term_ioctl = TIOCSWINSZ;
                *term_stat = MSU_WWSZ_ACK;
        } else if (oplmsu_uinst->tiocssoftcar_p != NULL) {
                if ((*term_mp = copymsg(oplmsu_uinst->tiocssoftcar_p))
                    == NULL) {
                        return (FAILURE);
                }

                *term_ioctl = TIOCSSOFTCAR;
                *term_stat = MSU_WCAR_ACK;
        } else {
                *term_stat = MSU_WPTH_CHG;
                *term_mp = NULL;
        }
        return (SUCCESS);
}

/*
 * Previous sequence for active path change termio
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER]
 *  -. uinst_t->u_lock : A
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
int
oplmsu_cmn_prechg_termio(queue_t *q, mblk_t *mp, int rw_flag, int prev_flag,
    mblk_t **term_mp, int *term_stat)
{

        ASSERT(RW_READ_HELD(&oplmsu_uinst->lock));

        if ((prev_flag == MSU_TIOS_TCSETS) &&
            (oplmsu_uinst->tiocmset_p != NULL)) {
                if (oplmsu_cmn_copymb(q, mp, term_mp, oplmsu_uinst->tiocmset_p,
                    rw_flag) == FAILURE) {
                        return (FAILURE);
                }

                *term_stat = MSU_WTMS_ACK;
        } else if ((prev_flag <= MSU_TIOS_MSET) &&
            (oplmsu_uinst->tiocspps_p != NULL)) {
                if (oplmsu_cmn_copymb(q, mp, term_mp, oplmsu_uinst->tiocspps_p,
                    rw_flag) == FAILURE) {
                        return (FAILURE);
                }

                *term_stat = MSU_WPPS_ACK;
        } else if ((prev_flag <= MSU_TIOS_PPS) &&
            (oplmsu_uinst->tiocswinsz_p != NULL)) {
                if (oplmsu_cmn_copymb(q, mp, term_mp,
                    oplmsu_uinst->tiocswinsz_p, rw_flag) == FAILURE) {
                        return (FAILURE);
                }

                *term_stat = MSU_WWSZ_ACK;
        } else if ((prev_flag <= MSU_TIOS_WINSZP) &&
            (oplmsu_uinst->tiocssoftcar_p != NULL)) {
                if (oplmsu_cmn_copymb(q, mp, term_mp,
                    oplmsu_uinst->tiocssoftcar_p, rw_flag) == FAILURE) {
                        return (FAILURE);
                }

                *term_stat = MSU_WCAR_ACK;
        } else if (prev_flag <= MSU_TIOS_SOFTCAR) {
                *term_mp = NULL;
                *term_stat = MSU_WPTH_CHG;
        }
        return (SUCCESS);
}

/*
 * Pull up messages
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : P
 *  -. uinst_t->u_lock : P
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
int
oplmsu_cmn_pullup_msg(queue_t *q, mblk_t *mp)
{
        mblk_t  *nmp = NULL;

        if ((mp != NULL) && (mp->b_cont != NULL) &&
            (mp->b_cont->b_cont != NULL)) {
                if ((nmp = msgpullup(mp->b_cont, -1)) == NULL) {
                        oplmsu_iocack(q, mp, ENOSR);
                        return (FAILURE);
                } else {
                        freemsg(mp->b_cont);
                        mp->b_cont = nmp;
                }
        }
        return (SUCCESS);
}

/*
 * Wake up flow control
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : P
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
void
oplmsu_cmn_wakeup(queue_t *q)
{
        ctrl_t  *ctrl;

        ASSERT(RW_LOCK_HELD(&oplmsu_uinst->lock));

        mutex_enter(&oplmsu_uinst->c_lock);
        ctrl = (ctrl_t *)q->q_ptr;
        if (ctrl->sleep_flag == CV_SLEEP) {
                ctrl->sleep_flag = CV_WAKEUP;
                cv_signal(&ctrl->cvp);
        }
        mutex_exit(&oplmsu_uinst->c_lock);
}

/*
 * bufcall() and timeout() callback entry for read/write stream
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : P
 *  -. uinst_t->u_lock : P
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
void
oplmsu_cmn_bufcb(void *arg)
{
        struct buf_tbl  *buftbl = arg;
        lpath_t         *lpath;
        ctrl_t          *ctrl;
        queue_t         *q;
        int             lq_flag = 0;

        rw_enter(&oplmsu_uinst->lock, RW_WRITER);
        mutex_enter(&oplmsu_uinst->l_lock);

        lpath = oplmsu_uinst->first_lpath;
        while (lpath) {
                if ((buftbl == lpath->rbuftbl) &&
                    (buftbl->rw_flag == MSU_READ_SIDE)) {
                        if ((lpath->rbuf_id == 0) && (lpath->rtout_id == 0)) {
                                mutex_exit(&oplmsu_uinst->l_lock);
                                rw_exit(&oplmsu_uinst->lock);
                        } else {
                                q = lpath->rbuftbl->q;
                                lpath->rbuftbl->q = NULL;
                                lpath->rbuftbl->rw_flag = UNDEFINED;

                                if (lpath->rbuf_id) {
                                        lpath->rbuf_id = 0;
                                } else {
                                        lpath->rtout_id = 0;
                                }
                                mutex_exit(&oplmsu_uinst->l_lock);

                                if (oplmsu_queue_flag == 1) {
                                        lq_flag = 1;
                                        oplmsu_queue_flag = 0;
                                }

                                rw_exit(&oplmsu_uinst->lock);
                                oplmsu_rcmn_high_qenable(q);

                                if (lq_flag == 1) {
                                        rw_enter(&oplmsu_uinst->lock,
                                            RW_WRITER);
                                        oplmsu_queue_flag = 1;
                                        rw_exit(&oplmsu_uinst->lock);
                                }
                        }
                        return;
                }
                lpath = lpath->l_next;
        }
        mutex_exit(&oplmsu_uinst->l_lock);

        mutex_enter(&oplmsu_uinst->c_lock);
        if ((ctrl = oplmsu_uinst->user_ctrl) != NULL) {
                if ((buftbl == ctrl->wbuftbl) &&
                    (buftbl->rw_flag == MSU_WRITE_SIDE)) {
                        oplmsu_wbufcb_posthndl(ctrl);
                        mutex_exit(&oplmsu_uinst->c_lock);
                        rw_exit(&oplmsu_uinst->lock);
                        return;
                }
        }

        if ((ctrl = oplmsu_uinst->meta_ctrl) != NULL) {
                if ((buftbl == ctrl->wbuftbl) &&
                    (buftbl->rw_flag == MSU_WRITE_SIDE)) {
                        oplmsu_wbufcb_posthndl(ctrl);
                        mutex_exit(&oplmsu_uinst->c_lock);
                        rw_exit(&oplmsu_uinst->lock);
                        return;
                }
        }
        mutex_exit(&oplmsu_uinst->c_lock);
        rw_exit(&oplmsu_uinst->lock);
}

/*
 * bufcall() or timeout() callback post handling for write stream
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_WRITER]
 *  -. uinst_t->u_lock : P
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : M
 */
void
oplmsu_wbufcb_posthndl(ctrl_t *ctrl)
{
        queue_t *q;
        int     lq_flag = 0;

        ASSERT(RW_WRITE_HELD(&oplmsu_uinst->lock));
        ASSERT(MUTEX_HELD(&oplmsu_uinst->c_lock));

        if ((ctrl->wbuf_id == 0) && (ctrl->wtout_id == 0)) {
                return;
        }

        q = ctrl->wbuftbl->q;
        ctrl->wbuftbl->q = NULL;
        ctrl->wbuftbl->rw_flag = UNDEFINED;
        if (ctrl->wbuf_id) {
                ctrl->wbuf_id = 0;
        } else {
                ctrl->wtout_id = 0;
        }

        if (oplmsu_queue_flag == 1) {
                lq_flag = 1;
                oplmsu_queue_flag = 0;
        }

        mutex_exit(&oplmsu_uinst->c_lock);
        oplmsu_wcmn_high_qenable(q, RW_WRITER);
        mutex_enter(&oplmsu_uinst->c_lock);

        if (lq_flag == 1) {
                oplmsu_queue_flag = 1;
        }
}

/*
 *      COMMON FUNCTIONS FOR WRITE STREAM
 */

/*
 * Check control node and driver privilege
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : A
 *  -. uinst_t->l_lock : A
 *  -. uinst_t->c_lock : P
 */
int
oplmsu_wcmn_chknode(queue_t *q, int node, mblk_t *mp)
{
        struct iocblk   *iocp;

        ASSERT(RW_LOCK_HELD(&oplmsu_uinst->lock));

        mutex_enter(&oplmsu_uinst->c_lock);
        if (((ctrl_t *)q->q_ptr)->node_type != node) {
                mutex_exit(&oplmsu_uinst->c_lock);
                cmn_err(CE_WARN, "oplmsu: chk-node: ctrl node type = %d", node);
                return (EINVAL);
        }
        mutex_exit(&oplmsu_uinst->c_lock);

        /* Check super-user by oplmsu.conf */
        if (oplmsu_check_su != 0) {
                iocp = (struct iocblk *)mp->b_rptr;
                if (drv_priv(iocp->ioc_cr) != 0) {
                        cmn_err(CE_WARN, "oplmsu: chk-node: Permission denied");
                        return (EPERM);
                }
        }
        return (SUCCESS);
}

/*
 * Flush handle for write side stream
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : P
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
void
oplmsu_wcmn_flush_hndl(queue_t *q, mblk_t *mp, krw_t rw)
{
        queue_t *dst_queue = NULL;

        ASSERT(RW_LOCK_HELD(&oplmsu_uinst->lock));

        if (*mp->b_rptr & FLUSHW) {     /* Write side */
                flushq(q, FLUSHDATA);
        }

        dst_queue = oplmsu_uinst->lower_queue;
        if (dst_queue == NULL) {
                if (*mp->b_rptr & FLUSHR) {
                        flushq(RD(q), FLUSHDATA);
                        *mp->b_rptr &= ~FLUSHW;

                        rw_exit(&oplmsu_uinst->lock);
                        OPLMSU_TRACE(q, mp, MSU_TRC_UO);
                        qreply(q, mp);
                        rw_enter(&oplmsu_uinst->lock, rw);
                } else {
                        freemsg(mp);
                }
        } else {
                (void) putq(WR(dst_queue), mp);
        }
}

/*
 * Through message handle for write side stream
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : P
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
int
oplmsu_wcmn_through_hndl(queue_t *q, mblk_t *mp, int pri_flag, krw_t rw)
{
        queue_t *usr_queue = NULL, *dst_queue = NULL;
        ctrl_t  *ctrl;

        ASSERT(RW_LOCK_HELD(&oplmsu_uinst->lock));

        mutex_enter(&oplmsu_uinst->c_lock);
        if ((ctrl = oplmsu_uinst->user_ctrl) != NULL) {
                usr_queue = ctrl->queue;
                mutex_exit(&oplmsu_uinst->c_lock);
        } else {
                mutex_exit(&oplmsu_uinst->c_lock);
                if (mp->b_datap->db_type == M_IOCTL) {
                        rw_exit(&oplmsu_uinst->lock);
                        oplmsu_iocack(q, mp, ENODEV);
                        rw_enter(&oplmsu_uinst->lock, rw);
                } else {
                        freemsg(mp);
                }
                return (SUCCESS);
        }

        if (oplmsu_uinst->lower_queue != NULL) {
                dst_queue = WR(oplmsu_uinst->lower_queue);
        } else {
                cmn_err(CE_WARN, "!oplmsu: through-lwq: "
                    "Active path doesn't exist");

                if (mp->b_datap->db_type == M_IOCTL) {
                        rw_exit(&oplmsu_uinst->lock);
                        oplmsu_iocack(q, mp, ENODEV);
                        rw_enter(&oplmsu_uinst->lock, rw);
                } else {
                        freemsg(mp);
                }
                return (SUCCESS);
        }

        if ((usr_queue == WR(q)) || (usr_queue == RD(q))) {
                if (pri_flag == MSU_HIGH) {
                        (void) putq(dst_queue, mp);
                } else {
                        if (canput(dst_queue)) {
                                (void) putq(dst_queue, mp);
                        } else {
                                oplmsu_wcmn_norm_putbq(WR(q), mp, dst_queue);
                                return (FAILURE);
                        }
                }
        } else {
                cmn_err(CE_WARN, "oplmsu: through-lwq: "
                    "Inappropriate message for this node");

                if (mp->b_datap->db_type == M_IOCTL) {
                        rw_exit(&oplmsu_uinst->lock);
                        oplmsu_iocack(q, mp, ENODEV);
                        rw_enter(&oplmsu_uinst->lock, rw);
                } else {
                        freemsg(mp);
                }
        }
        return (SUCCESS);
}

/*
 * Get high priority message from buffer for upper write stream
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : A
 *  -. uinst_t->l_lock : A
 *  -. uinst_t->c_lock : P
 */
mblk_t *
oplmsu_wcmn_high_getq(queue_t *uwq)
{
        mblk_t  *mp;
        ctrl_t  *ctrl;

        ASSERT(RW_LOCK_HELD(&oplmsu_uinst->lock));

        mutex_enter(&oplmsu_uinst->c_lock);
        ctrl = (ctrl_t *)uwq->q_ptr;
        mp = ctrl->first_upri_hi;
        if (mp != NULL) {
                if (mp->b_next == NULL) {
                        ctrl->first_upri_hi = NULL;
                        ctrl->last_upri_hi = NULL;
                } else {
                        ctrl->first_upri_hi = mp->b_next;
                        mp->b_next->b_prev = NULL;
                        mp->b_next = NULL;
                }
                mp->b_prev = NULL;
        }
        mutex_exit(&oplmsu_uinst->c_lock);
        return (mp);
}

/*
 * putbq() function for normal priority message of write stream
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : A
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
void
oplmsu_wcmn_norm_putbq(queue_t *uwq, mblk_t *mp, queue_t *dq)
{
        lpath_t *lpath;

        ASSERT(mp != NULL);
        ASSERT(RW_LOCK_HELD(&oplmsu_uinst->lock));

        mutex_enter(&oplmsu_uinst->l_lock);
        lpath = (lpath_t *)dq->q_ptr;
        lpath->uwq_flag = 1;
        lpath->uwq_queue = uwq;
        mutex_exit(&oplmsu_uinst->l_lock);
        (void) putbq(uwq, mp);
}

/*
 * Restart queuing for high priority message of write stream when flow control
 * failed
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER or RW_WRITER]
 *  -. uinst_t->u_lock : P
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
void
oplmsu_wcmn_high_qenable(queue_t *q, krw_t rw)
{
        mblk_t  *mp;

        ASSERT(RW_LOCK_HELD(&oplmsu_uinst->lock));

        if (oplmsu_queue_flag == 1) {
                return;
        }

        /* Handle high priority message */
        while (mp = oplmsu_wcmn_high_getq(WR(q))) {
                if (mp->b_datap->db_type & M_FLUSH) {
                        oplmsu_wcmn_flush_hndl(q, mp, rw);
                        continue;
                }

                if (oplmsu_wcmn_through_hndl(q, mp, MSU_HIGH, rw) == FAILURE) {
                        return;
                }
        }
        qenable(WR(q)); /* enable upper write queue */
}

/*
 *      COMMON FUNCTIONS FOR READ STREAM
 */

/*
 * Flush handle for read side stream
 *
 * Requires lock ( M: mandatory  P: prohibited  A: allowed
 *  -. uinst_t->lock   : M [RW_READER]
 *  -. uinst_t->u_lock : P
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
void
oplmsu_rcmn_flush_hndl(queue_t *q, mblk_t *mp)
{
        queue_t *dst_queue = NULL;
        ctrl_t  *ctrl;

        ASSERT(RW_READ_HELD(&oplmsu_uinst->lock));

        if (*mp->b_rptr & FLUSHR) {
                /* Remove only data messages from read queue */
                flushq(q, FLUSHDATA);
        }

        mutex_enter(&oplmsu_uinst->c_lock);
        if ((ctrl = oplmsu_uinst->user_ctrl) != NULL) {
                dst_queue = RD(ctrl->queue);
                mutex_exit(&oplmsu_uinst->c_lock);

                if (dst_queue != NULL) {
                        (void) putq(dst_queue, mp);
                } else {
                        if (*mp->b_rptr & FLUSHW) {
                                flushq(WR(q), FLUSHDATA);
                                *mp->b_rptr &= ~FLUSHR;

                                rw_exit(&oplmsu_uinst->lock);
                                OPLMSU_TRACE(q, mp, MSU_TRC_LO);
                                qreply(q, mp);
                                rw_enter(&oplmsu_uinst->lock, RW_READER);
                        } else {
                                freemsg(mp);
                        }
                }
        } else {
                mutex_exit(&oplmsu_uinst->c_lock);
                if (*mp->b_rptr & FLUSHW) {
                        flushq(WR(q), FLUSHDATA);
                        *mp->b_rptr &= ~FLUSHR;

                        rw_exit(&oplmsu_uinst->lock);
                        OPLMSU_TRACE(q, mp, MSU_TRC_LO);
                        qreply(q, mp);
                        rw_enter(&oplmsu_uinst->lock, RW_READER);
                } else {
                        freemsg(mp);
                }
        }
}

/*
 * Through message handle for read side stream
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : M [RW_READER]
 *  -. uinst_t->u_lock : A
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
int
oplmsu_rcmn_through_hndl(queue_t *q, mblk_t *mp, int pri_flag)
{
        lpath_t *lpath;
        ctrl_t  *ctrl;
        queue_t *dst_queue = NULL;
        int     act_flag;

        ASSERT(RW_READ_HELD(&oplmsu_uinst->lock));

        mutex_enter(&oplmsu_uinst->l_lock);
        lpath = (lpath_t *)q->q_ptr;
        if (lpath->uinst != NULL) {
                act_flag = ACTIVE_RES;
        } else {
                act_flag = NOT_ACTIVE_RES;
        }
        mutex_exit(&oplmsu_uinst->l_lock);

        mutex_enter(&oplmsu_uinst->c_lock);
        if (((ctrl = oplmsu_uinst->user_ctrl) != NULL) &&
            (((mp->b_datap->db_type == M_IOCACK) ||
            (mp->b_datap->db_type == M_IOCNAK)) || (act_flag == ACTIVE_RES))) {
                dst_queue = RD(ctrl->queue);
        } else {
                mutex_exit(&oplmsu_uinst->c_lock);
                freemsg(mp);
                return (SUCCESS);
        }

        if (pri_flag == MSU_HIGH) {
                (void) putq(dst_queue, mp);
        } else {
                if (canput(dst_queue)) {
                        (void) putq(dst_queue, mp);
                } else {
                        /*
                         * Place a normal priority message at the head of
                         * read queue
                         */

                        ctrl = (ctrl_t *)dst_queue->q_ptr;
                        ctrl->lrq_flag = 1;
                        ctrl->lrq_queue = q;
                        mutex_exit(&oplmsu_uinst->c_lock);
                        (void) putbq(q, mp);
                        return (FAILURE);
                }
        }
        mutex_exit(&oplmsu_uinst->c_lock);
        return (SUCCESS);
}

/*
 * Restart queuing for high priority message of read stream
 * when flow control failed
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : P
 *  -. uinst_t->u_lock : P
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
void
oplmsu_rcmn_high_qenable(queue_t *q)
{
        mblk_t          *mp;
        struct iocblk   *iocp = NULL;
        lpath_t         *lpath;
        int             rval;

        rw_enter(&oplmsu_uinst->lock, RW_READER);

        for (;;) {      /* Handle high priority message */
                mutex_enter(&oplmsu_uinst->l_lock);
                lpath = (lpath_t *)q->q_ptr;
                if ((mp = lpath->first_lpri_hi) == NULL) {
                        mutex_exit(&oplmsu_uinst->l_lock);
                        break;
                }

                if (mp->b_next == NULL) {
                        lpath->first_lpri_hi = NULL;
                        lpath->last_lpri_hi = NULL;
                } else {
                        lpath->first_lpri_hi = mp->b_next;
                        mp->b_next->b_prev = NULL;
                        mp->b_next = NULL;
                }
                mp->b_prev = NULL;
                mutex_exit(&oplmsu_uinst->l_lock);

                rval = SUCCESS;
                switch (mp->b_datap->db_type) {
                case M_IOCACK :         /* FALLTHRU */
                case M_IOCNAK :
                        iocp = (struct iocblk *)mp->b_rptr;
                        switch (iocp->ioc_cmd) {
                        case TCSETS :           /* FALLTHRU */
                        case TCSETSW :          /* FALLTHRU */
                        case TCSETSF :          /* FALLTHRU */
                        case TIOCMSET :         /* FALLTHRU */
                        case TIOCSPPS :         /* FALLTHRU */
                        case TIOCSWINSZ :       /* FALLTHRU */
                        case TIOCSSOFTCAR :
                                rw_exit(&oplmsu_uinst->lock);
                                rval = oplmsu_lrioctl_termios(q, mp);
                                rw_enter(&oplmsu_uinst->lock, RW_WRITER);
                                break;

                        default :
                                rval = oplmsu_rcmn_through_hndl(
                                    q, mp, MSU_HIGH);
                                if (rval == FAILURE) {
                                        rw_exit(&oplmsu_uinst->lock);
                                        return;
                                }
                        }
                        break;

                case M_ERROR :
                        rw_exit(&oplmsu_uinst->lock);
                        rval = oplmsu_lrmsg_error(q, mp);
                        rw_enter(&oplmsu_uinst->lock, RW_WRITER);
                        break;

                case M_FLUSH :
                        oplmsu_rcmn_flush_hndl(q, mp);
                        break;

                default :
                        rval = oplmsu_rcmn_through_hndl(q, mp, MSU_HIGH);
                        if (rval == FAILURE) {
                                rw_exit(&oplmsu_uinst->lock);
                                return;
                        }
                }

                if (rval == FAILURE) {
                        break;
                }
        }

        rw_exit(&oplmsu_uinst->lock);
        qenable(q);     /* Enable lower read queue */
}

#ifdef DEBUG
/*
 * Online trace
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : P
 *  -. uinst_t->u_lock : P
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
void
oplmsu_cmn_trace(queue_t *q, mblk_t *mp, int op)
{
        struct iocblk   *iocp;

        if ((op < MSU_TRC_UI) || (op > MSU_TRC_CLS)) {
                return;
        }

        mutex_enter(&oplmsu_ltrc_lock);

        if (oplmsu_debug_mode & MSU_DPRINT_ON) {
                oplmsu_cmn_msglog(mp, op);
        }

        /* Trace current counter */
        (void) drv_getparm(LBOLT, (void *)&oplmsu_ltrc_ccnt);

        if (oplmsu_ltrc_cur == oplmsu_ltrc_tail) {
                oplmsu_ltrc_cur = oplmsu_ltrc_top;
        } else {
                oplmsu_ltrc_cur++;
        }
        oplmsu_ltrc_cur->q = q;
        oplmsu_ltrc_cur->mp = mp;

        switch (op) {
        case MSU_TRC_UI :
                oplmsu_ltrc_cur->op[0] = 'u';
                oplmsu_ltrc_cur->op[1] = 'i';
                break;

        case MSU_TRC_UO :
                oplmsu_ltrc_cur->op[0] = 'u';
                oplmsu_ltrc_cur->op[1] = 'o';
                break;

        case MSU_TRC_LI :
                oplmsu_ltrc_cur->op[0] = 'l';
                oplmsu_ltrc_cur->op[1] = 'i';
                break;

        case MSU_TRC_LO :
                oplmsu_ltrc_cur->op[0] = 'l';
                oplmsu_ltrc_cur->op[1] = 'o';
                break;

        case MSU_TRC_OPN :
                oplmsu_ltrc_cur->op[0] = 'o';
                oplmsu_ltrc_cur->op[1] = 'p';
                break;

        case MSU_TRC_CLS :
                oplmsu_ltrc_cur->op[0] = 'c';
                oplmsu_ltrc_cur->op[1] = 'l';
                break;
        }

        if ((op == MSU_TRC_LI) || (op == MSU_TRC_LO)) {
                mutex_enter(&oplmsu_uinst->l_lock);
                oplmsu_ltrc_cur->pathno = ((lpath_t *)q->q_ptr)->path_no;
                mutex_exit(&oplmsu_uinst->l_lock);
        } else {
                oplmsu_ltrc_cur->pathno = 0;
        }

        if ((op == MSU_TRC_OPN) || (op == MSU_TRC_CLS)) {
                oplmsu_ltrc_cur->msg_type = 0;
                oplmsu_ltrc_cur->msg_cmd = 0;
                oplmsu_ltrc_cur->data = 0;

                switch ((ulong_t)mp) {
                case MSU_NODE_USER :
                        oplmsu_ltrc_cur->data = MSU_TRC_USER;
                        break;

                case MSU_NODE_META :
                        oplmsu_ltrc_cur->data = MSU_TRC_META;
                        break;
                }
                oplmsu_ltrc_cur->mp = NULL;
        } else {
                oplmsu_ltrc_cur->msg_type = mp->b_datap->db_type;
                iocp = (struct iocblk *)mp->b_rptr;
                oplmsu_ltrc_cur->msg_cmd = iocp->ioc_cmd;

                if ((mp->b_datap->db_type == M_IOCTL) ||
                    (mp->b_datap->db_type == M_IOCACK) ||
                    (mp->b_datap->db_type == M_IOCNAK)) {
                        oplmsu_ltrc_cur->msg_cmd = iocp->ioc_cmd;

                        if (mp->b_cont != NULL) {
                                oplmsu_ltrc_cur->data =
                                    (ulong_t)mp->b_cont->b_rptr;
                        } else {
                                oplmsu_ltrc_cur->data = 0;
                        }
                } else {
                        oplmsu_ltrc_cur->msg_cmd = 0;

                        if (mp->b_rptr == NULL) {
                                oplmsu_ltrc_cur->data = 0;
                        } else {
                                oplmsu_ltrc_cur->data = *(ulong_t *)mp->b_rptr;
                        }
                }
        }
        mutex_exit(&oplmsu_ltrc_lock);
}

/*
 * Display message log to console
 *
 * Requires Lock (( M: Mandatory, P: Prohibited, A: Allowed ))
 *  -. uinst_t->lock   : P
 *  -. uinst_t->u_lock : P
 *  -. uinst_t->l_lock : P
 *  -. uinst_t->c_lock : P
 */
void
oplmsu_cmn_msglog(mblk_t *mp, int direction)
{
        uchar_t *cur = NULL;
        mblk_t  *tmp_mp = NULL;
        ulong_t len;
        ulong_t line;
        ulong_t col;
        ulong_t row;
        ulong_t count;
        char    buffer[70];
        char    *bufp;

        if (mp == NULL) {
                return;
        }

        switch (direction) {
        case 0:
                cmn_err(CE_NOTE, "!---------- Upper in --------");
                break;

        case 1:
                cmn_err(CE_NOTE, "!---------- Upper out -------");
                break;

        case 2:
                cmn_err(CE_NOTE, "!---------- Lower in --------");
                break;

        case 3:
                cmn_err(CE_NOTE, "!---------- Lower out -------");
                break;

        default:
                return;
        }

        for (tmp_mp = mp; tmp_mp; tmp_mp = tmp_mp->b_cont) {
                cmn_err(CE_NOTE, "!db_type = 0x%02x", tmp_mp->b_datap->db_type);

                len = tmp_mp->b_wptr - tmp_mp->b_rptr;
                line = (len + 31) / 32;
                cur = (uchar_t *)tmp_mp->b_rptr;
                count = 0;

                for (col = 0; col < line; col++) {
                        bufp = buffer;

                        for (row = 0; row < 32; row++) {
                                if (row != 0 && (row % 8) == 0) {
                                        *bufp = ' ';
                                        bufp++;
                                }
                                (void) sprintf(bufp, "%02x", *cur);
                                bufp += 2;
                                cur++;
                                count++;

                                if (count >= len) {
                                        break;
                                }
                        }
                        *bufp = '\0';
                        cmn_err(CE_NOTE, "!%s", buffer);

                        if (count >= len) {
                                break;
                        }
                }
        }
}

void
oplmsu_cmn_prt_pathname(dev_info_t *dip)
{
        char    pathname[128];
        char    wrkbuf[128];

        (void) ddi_pathname(dip, wrkbuf);
        *(wrkbuf + strlen(wrkbuf)) = '\0';
        (void) sprintf(pathname, "/devices%s:%c", wrkbuf,
            'a'+ ddi_get_instance(dip));

        DBG_PRINT((CE_NOTE, "oplmsu: debug-info: "
            "Active path change to path => %s", pathname));
}
#endif