/* * 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 */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * UPPER WRITE SERVICE PROCEDURE */ /* I_PLINK ioctl command received */ int oplmsu_uwioctl_iplink(queue_t *uwq, mblk_t *mp) { struct linkblk *lbp; lpath_t *lpath; int ncode; if (mp == NULL) { return (EINVAL); } if ((mp->b_cont->b_wptr - mp->b_cont->b_rptr) < sizeof (struct linkblk)) { cmn_err(CE_WARN, "oplmsu: uw-iplink: Invalid data length"); oplmsu_iocack(uwq, mp, EINVAL); return (EINVAL); } lbp = (struct linkblk *)mp->b_cont->b_rptr; rw_enter(&oplmsu_uinst->lock, RW_WRITER); /* * Check whether this is called by super-user privilege. * uwq => Queue of meta control node */ ncode = oplmsu_wcmn_chknode(uwq, MSU_NODE_META, mp); if (ncode != SUCCESS) { rw_exit(&oplmsu_uinst->lock); oplmsu_iocack(uwq, mp, ncode); return (ncode); } /* Allocate kernel memory for lpath_t */ lpath = (lpath_t *)kmem_zalloc(sizeof (lpath_t), KM_NOSLEEP); if (lpath == NULL) { rw_exit(&oplmsu_uinst->lock); cmn_err(CE_WARN, "oplmsu: uw-iplink: " "Failed to allocate kernel memory"); oplmsu_iocack(uwq, mp, ENOMEM); return (ENOMEM); } /* * Initialize members of lpath_t */ lpath->rbuftbl = (struct buf_tbl *)kmem_zalloc(sizeof (struct buf_tbl), KM_NOSLEEP); if (lpath->rbuftbl == NULL) { rw_exit(&oplmsu_uinst->lock); kmem_free(lpath, sizeof (lpath_t)); cmn_err(CE_WARN, "oplmsu: uw-iplink: " "Failed to allocate kernel memory"); oplmsu_iocack(uwq, mp, ENOMEM); return (ENOMEM); } cv_init(&lpath->sw_cv, "oplmsu lpath condvar", CV_DRIVER, NULL); lpath->src_upath = NULL; lpath->status = MSU_EXT_NOTUSED; lpath->lower_queue = lbp->l_qbot; /* Set lower queue pointer */ lpath->link_id = lbp->l_index; /* Set Link-ID */ lpath->path_no = UNDEFINED; /* Set initial path number */ lpath->abt_char = oplmsu_uinst->abts; /* Set abort character seq */ WR(lpath->lower_queue)->q_ptr = lpath; RD(lpath->lower_queue)->q_ptr = lpath; oplmsu_link_lpath(lpath); /* Link lpath_t */ rw_exit(&oplmsu_uinst->lock); oplmsu_iocack(uwq, mp, 0); return (SUCCESS); } /* I_PUNLINK ioctl command received */ int oplmsu_uwioctl_ipunlink(queue_t *uwq, mblk_t *mp) { struct linkblk *lbp; upath_t *upath; lpath_t *lpath; mblk_t *hmp = NULL, *next_hmp = NULL; bufcall_id_t rbuf_id; timeout_id_t rtout_id; int ncode; int use_flag; if (mp == NULL) { return (EINVAL); } if ((mp->b_cont->b_wptr - mp->b_cont->b_rptr) < sizeof (struct linkblk)) { cmn_err(CE_WARN, "oplmsu: uw-ipunlink: Invalid data length"); oplmsu_iocack(uwq, mp, ENOSR); return (ENOSR); } lbp = (struct linkblk *)mp->b_cont->b_rptr; rw_enter(&oplmsu_uinst->lock, RW_WRITER); /* * Check whether this is called by super-user privilege. * uwq => Queue of meta control node */ ncode = oplmsu_wcmn_chknode(uwq, MSU_NODE_META, mp); if (ncode != SUCCESS) { rw_exit(&oplmsu_uinst->lock); oplmsu_iocack(uwq, mp, ncode); return (ncode); } mutex_enter(&oplmsu_uinst->u_lock); mutex_enter(&oplmsu_uinst->l_lock); /* * Search for a corresponding lower path information table to * lbp->l_qbot from the lower queue address. */ lpath = oplmsu_uinst->first_lpath; while (lpath) { if ((lpath->lower_queue == RD(lbp->l_qbot)) || (lpath->lower_queue == WR(lbp->l_qbot))) { break; } lpath = lpath->l_next; } if (lpath == NULL) { mutex_exit(&oplmsu_uinst->l_lock); mutex_exit(&oplmsu_uinst->u_lock); rw_exit(&oplmsu_uinst->lock); cmn_err(CE_WARN, "oplmsu: uw-ipunlink: " "Proper lpath_t doesn't find"); oplmsu_iocack(uwq, mp, EINVAL); return (EINVAL); } /* lpath_t come into the busy status */ use_flag = oplmsu_set_ioctl_path(lpath, uwq, NULL); if (use_flag == BUSY) { mutex_exit(&oplmsu_uinst->l_lock); mutex_exit(&oplmsu_uinst->u_lock); rw_exit(&oplmsu_uinst->lock); cmn_err(CE_WARN, "oplmsu: uw-ipunlink: " "Other processing is using lower path"); oplmsu_iocack(uwq, mp, EBUSY); return (EBUSY); } /* upath_t is retrieved by using the path number */ upath = oplmsu_search_upath_info(lpath->path_no); if (upath != NULL) { /* When the upath_t exists */ switch (upath->status) { case MSU_PSTAT_STOP : /* FALLTHRU */ case MSU_PSTAT_FAIL : /* * When traditional_status is MSU_SETID, the path * status is changed into the state of disconnect. */ if (upath->traditional_status == MSU_SETID) { oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_DISCON, upath->status, MSU_DISCON); upath->lpath = NULL; break; } /* * When traditional_status isn't MSU_SETID, * the error is reported. */ default : /* * When upath->status isn't MSU_PSTAT_STOP or * MSU_PSTAT_FAIL, the error is reported. */ oplmsu_clear_ioctl_path(lpath); mutex_exit(&oplmsu_uinst->l_lock); cmn_err(CE_WARN, "oplmsu: uw-ipunlink: " "trad_status = %lx", upath->traditional_status); cmn_err(CE_WARN, "oplmsu: uw-ipunlink: " "status = %d", upath->status); mutex_exit(&oplmsu_uinst->u_lock); rw_exit(&oplmsu_uinst->lock); oplmsu_iocack(uwq, mp, EINVAL); return (EINVAL); } } else { /* * This pattern is no upper info table before config_add or * after config_del. */ /* * When the upper path table doesn't exist, path is deleted * with config_del/config_add ioctl processed. */ if ((lpath->status != MSU_LINK_NU) && (lpath->status != MSU_SETID_NU)) { oplmsu_clear_ioctl_path(lpath); mutex_exit(&oplmsu_uinst->l_lock); mutex_exit(&oplmsu_uinst->u_lock); rw_exit(&oplmsu_uinst->lock); oplmsu_iocack(uwq, mp, EINVAL); return (EINVAL); } } oplmsu_uinst->inst_status = oplmsu_get_inst_status(); oplmsu_clear_ioctl_path(lpath); /* Free high priority message */ if (lpath->first_lpri_hi != NULL) { cmn_err(CE_WARN, "oplmsu: uw-ipunlink: " "Free high-priority message by unlinking lower path"); for (hmp = lpath->first_lpri_hi; hmp; ) { next_hmp = hmp->b_next; freemsg(hmp); hmp = next_hmp; } lpath->first_lpri_hi = NULL; lpath->last_lpri_hi = NULL; } rbuf_id = lpath->rbuf_id; rtout_id = lpath->rtout_id; lpath->rbuf_id = 0; lpath->rtout_id = 0; kmem_free(lpath->rbuftbl, sizeof (struct buf_tbl)); lpath->rbuftbl = NULL; cv_destroy(&lpath->sw_cv); oplmsu_unlink_lpath(lpath); kmem_free(lpath, sizeof (lpath_t)); mutex_exit(&oplmsu_uinst->l_lock); mutex_exit(&oplmsu_uinst->u_lock); rw_exit(&oplmsu_uinst->lock); if (rbuf_id != 0) { unbufcall(rbuf_id); } if (rtout_id != 0) { untimeout(rtout_id); } oplmsu_iocack(uwq, mp, 0); return (SUCCESS); } /* termio ioctl command received */ int oplmsu_uwioctl_termios(queue_t *uwq, mblk_t *mp) { struct iocblk *iocp = NULL; queue_t *dst_queue; upath_t *upath = NULL; lpath_t *lpath = NULL; mblk_t *nmp = NULL; ctrl_t *ctrl; int term_stat; int use_flag; if (mp == NULL) { return (EINVAL); } if (mp->b_cont == NULL) { cmn_err(CE_WARN, "oplmsu: uw-termios: " "b_cont data block is NULL"); oplmsu_iocack(uwq, mp, EINVAL); return (FAILURE); } if (mp->b_cont->b_rptr == NULL) { cmn_err(CE_WARN, "oplmsu: uw-termios: " "b_rptr data pointer is NULL"); oplmsu_iocack(uwq, mp, EINVAL); return (EINVAL); } iocp = (struct iocblk *)mp->b_rptr; rw_enter(&oplmsu_uinst->lock, RW_READER); /* * Check control node type * uwq : Queue of user control node */ mutex_enter(&oplmsu_uinst->c_lock); ctrl = (ctrl_t *)uwq->q_ptr; if (ctrl != NULL) { if (ctrl->node_type != MSU_NODE_USER) { mutex_exit(&oplmsu_uinst->c_lock); rw_exit(&oplmsu_uinst->lock); cmn_err(CE_WARN, "oplmsu: uw-termios: " "ctrl node type = %d", ctrl->node_type); oplmsu_iocack(uwq, mp, EINVAL); return (EINVAL); } } mutex_exit(&oplmsu_uinst->c_lock); switch (iocp->ioc_cmd) { case TCSETS : /* FALLTHRU */ case TCSETSW : /* FALLTHRU */ case TCSETSF : term_stat = MSU_WTCS_ACK; break; case TIOCMSET : term_stat = MSU_WTMS_ACK; break; case TIOCSPPS : term_stat = MSU_WPPS_ACK; break; case TIOCSWINSZ : term_stat = MSU_WWSZ_ACK; break; case TIOCSSOFTCAR : term_stat = MSU_WCAR_ACK; break; default : rw_exit(&oplmsu_uinst->lock); cmn_err(CE_WARN, "oplmsu: uw-termios: ioctl mismatch"); oplmsu_iocack(uwq, mp, EINVAL); return (EINVAL); } if (oplmsu_uinst->lower_queue == NULL) { rw_exit(&oplmsu_uinst->lock); cmn_err(CE_WARN, "!oplmsu: uw-termios: " "Active path doesn't exist"); oplmsu_iocack(uwq, mp, ENODEV); return (FAILURE); } lpath = oplmsu_uinst->lower_queue->q_ptr; if (lpath == NULL) { rw_exit(&oplmsu_uinst->lock); cmn_err(CE_WARN, "oplmsu: uw-termios: " "Proper lpath_t doesn't exist"); oplmsu_iocack(uwq, mp, EINVAL); return (EINVAL); } if (oplmsu_cmn_copymb(uwq, mp, &nmp, mp, MSU_WRITE_SIDE) == FAILURE) { rw_exit(&oplmsu_uinst->lock); return (FAILURE); } mutex_enter(&oplmsu_uinst->u_lock); mutex_enter(&oplmsu_uinst->l_lock); upath = oplmsu_search_upath_info(lpath->path_no); if (upath == NULL) { mutex_exit(&oplmsu_uinst->l_lock); mutex_exit(&oplmsu_uinst->u_lock); rw_exit(&oplmsu_uinst->lock); cmn_err(CE_WARN, "oplmsu: uw-termios: " "Proper upath_t doesn't find"); oplmsu_iocack(uwq, mp, EINVAL); return (EINVAL); } /* Set ioctl command to lower path info table */ use_flag = oplmsu_set_ioctl_path(lpath, uwq, mp); if (use_flag == BUSY) { mutex_exit(&oplmsu_uinst->l_lock); mutex_exit(&oplmsu_uinst->u_lock); freemsg(nmp); if (ctrl != NULL) { mutex_enter(&oplmsu_uinst->c_lock); ctrl->wait_queue = uwq; mutex_exit(&oplmsu_uinst->c_lock); rw_exit(&oplmsu_uinst->lock); putbq(uwq, mp); return (SUCCESS); } else { rw_exit(&oplmsu_uinst->lock); oplmsu_iocack(uwq, mp, EBUSY); return (EBUSY); } } /* Set destination queue (active path) */ dst_queue = WR(oplmsu_uinst->lower_queue); if (canput(dst_queue)) { lpath->src_upath = NULL; lpath->status = upath->traditional_status; upath->traditional_status = term_stat; mutex_exit(&oplmsu_uinst->l_lock); mutex_exit(&oplmsu_uinst->u_lock); rw_exit(&oplmsu_uinst->lock); putq(dst_queue, nmp); return (SUCCESS); } else { oplmsu_clear_ioctl_path(lpath); mutex_exit(&oplmsu_uinst->l_lock); mutex_exit(&oplmsu_uinst->u_lock); freemsg(nmp); oplmsu_wcmn_norm_putbq(WR(uwq), mp, dst_queue); rw_exit(&oplmsu_uinst->lock); return (FAILURE); } }