sbd_io.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/debug.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/cred.h>
#include <sys/dditypes.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/ddi.h>
#include <sys/ddi_impldefs.h>
#include <sys/ndi_impldefs.h>
#include <sys/kmem.h>
#include <sys/note.h>
#include <sys/sbdpriv.h>
#include <sys/sbd_io.h>
#include <sys/machsystm.h>
extern void sbd_errno_decode(int err, sbderror_t *ep, dev_info_t *dip);
extern sbd_state_t ostate_cvt(sbd_istate_t);
/*
* Given a dev_info_t of a branch root, walk down the
* branch to attach drivers
*/
/*ARGSUSED*/
void
sbd_attach_io(sbd_handle_t *hp, sbderror_t *ep, dev_info_t *dip, int unit)
{
sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
ASSERT(e_ddi_branch_held(dip));
(void) e_ddi_branch_configure(dip, NULL, 0);
ASSERT(sbp->sb_iopath[unit] != NULL);
(void) ddi_pathname(dip, sbp->sb_iopath[unit]);
}
/*
* remove device nodes for the branch indicated by dip
* Hold the status lock so that status can safely do ddi_pathname().
*/
/*ARGSUSED*/
void
sbd_detach_io(sbd_handle_t *hp, sbderror_t *ep, dev_info_t *dip, int unit)
{
int rv;
dev_info_t *fdip = NULL;
sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
ASSERT(e_ddi_branch_held(dip));
mutex_enter(&sbp->sb_slock);
rv = e_ddi_branch_unconfigure(dip, &fdip, DEVI_BRANCH_EVENT);
mutex_exit(&sbp->sb_slock);
if (rv) {
/*
* If non-NULL, fdip is returned held and must be released.
*/
if (fdip != NULL) {
sbd_errno_decode(rv, ep, fdip);
ddi_release_devi(fdip);
} else {
sbd_errno_decode(rv, ep, dip);
}
}
}
/*ARGSUSED*/
void
sbd_init_io_unit(sbd_board_t *sbp, int unit)
{
sbd_istate_t new_state;
sbd_io_unit_t *ip;
dev_info_t *dip;
ip = SBD_GET_BOARD_IOUNIT(sbp, unit);
if (SBD_DEV_IS_ATTACHED(sbp, SBD_COMP_IO, unit)) {
new_state = SBD_STATE_CONFIGURED;
} else if (SBD_DEV_IS_PRESENT(sbp, SBD_COMP_IO, unit)) {
new_state = SBD_STATE_CONNECTED;
} else {
new_state = SBD_STATE_EMPTY;
}
dip = sbp->sb_devlist[NIX(SBD_COMP_IO)][unit];
ip->sbi_cm.sbdev_cond = sbd_get_comp_cond(dip);
/*
* Any changes to this io component should be performed above
* this call to ensure the component is fully initialized
* before transitioning to the new state.
*/
SBD_DEVICE_TRANSITION(sbp, SBD_COMP_IO, unit, new_state);
}
/*ARGSUSED*/
int
sbd_disconnect_io(sbd_handle_t *hp, int unit)
{
return (0);
}
int
sbd_pre_attach_io(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
{
_NOTE(ARGUNUSED(hp))
_NOTE(ARGUNUSED(devlist))
_NOTE(ARGUNUSED(devnum))
return (0);
}
/*ARGSUSED*/
int
sbd_pre_detach_io(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
{
fn_t f = "sbd_pre_detach_io";
PR_IO("%s...\n", f);
if (devnum <= 0)
return (-1);
/* fail if any I/O devices are referenced */
if (sbd_check_io_refs(hp, devlist, devnum) > 0) {
PR_IO("%s: failed - I/O devices ref'd\n", f);
return (-1);
}
return (0);
}
/*ARGSUSED*/
int
sbd_post_attach_io(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
{
_NOTE(ARGUNUSED(hp))
_NOTE(ARGUNUSED(devlist))
_NOTE(ARGUNUSED(devnum))
return (0);
}
/*ARGSUSED*/
int
sbd_post_detach_io(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
{
return (0);
}
/*ARGSUSED*/
int
sbd_io_status(sbd_handle_t *hp, sbd_devset_t devset, sbd_dev_stat_t *dsp)
{
int i, ix;
sbd_board_t *sbp;
sbd_io_stat_t *isp;
sbd_io_unit_t *ip;
sbd_istate_t dstate;
sbdp_handle_t *hdp;
sbderror_t *ep;
sbd_error_t *sep;
/*
* Only look for requested devices that are actually present.
*/
sbp = SBDH2BD(hp->h_sbd);
ep = HD2MACHERR(hp);
sep = kmem_zalloc(sizeof (sbd_error_t), KM_SLEEP);
hdp = sbd_get_sbdp_handle(sbp, hp);
/*
* Concurrent status and unconfigure, disconnect are allowed.
* To prevent DR code from accessing stale dips, check the
* present devset and access the dips with status lock held.
* Disconnect and unconfigure code change dip state with
* status lock (sb_slock) held.
*/
mutex_enter(&sbp->sb_slock);
devset &= SBD_DEVS_PRESENT(sbp);
for (i = ix = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
dev_info_t *dip;
int unit;
int namelen;
int refcount = 0;
if (DEVSET_IN_SET(devset, SBD_COMP_IO, i) == 0)
continue;
/*
* Check to make sure the io component is in a state
* where its fully initialized.
*/
if (SBD_DEVICE_STATE(sbp, SBD_COMP_IO, i) == SBD_STATE_EMPTY)
continue;
dip = sbp->sb_devlist[NIX(SBD_COMP_IO)][i];
if (dip == NULL)
continue;
isp = &dsp->d_io;
bzero((caddr_t)isp, sizeof (*isp));
namelen = sizeof (isp->is_name);
(void) ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, OBP_DEVICETYPE,
(caddr_t)isp->is_name, &namelen);
isp->is_unit = sbdp_get_unit_num(hdp, dip);
if (isp->is_unit < 0) {
if (hp->h_flags & SBD_IOCTL_FLAG_FORCE)
continue;
else {
SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
break;
}
}
unit = isp->is_unit;
dstate = SBD_DEVICE_STATE(sbp, SBD_COMP_IO, unit);
isp->is_ostate = ostate_cvt(dstate);
isp->is_type = SBD_COMP_IO;
ip = SBD_GET_BOARD_IOUNIT(sbp, unit);
isp->is_cm.c_cond = ip->sbi_cm.sbdev_cond;
isp->is_cm.c_busy = ip->sbi_cm.sbdev_busy;
isp->is_cm.c_time = ip->sbi_cm.sbdev_time;
/*
* This is safe to do as unconfigure and disconnect
* hold the status lock while changing dip state.
*/
(void) ddi_pathname(dip, isp->is_pathname);
/*
* We use a dummy handle in which to collect
* the major numbers of unsafe devices.
*/
sbdp_check_devices(dip, &refcount, sep);
isp->is_referenced = (refcount == 0) ? 0 : 1;
isp->is_unsafe_count = 0;
/*
* Reset error field since we don't care about
* errors at this level. The unsafe devices
* will be reported in the structure.
*/
SBD_SET_ERR(ep, ESBD_NOERROR);
ep->e_rsc[0] = '\0';
ix++;
dsp++;
}
mutex_exit(&sbp->sb_slock);
kmem_free(sep, sizeof (sbd_error_t));
sbd_release_sbdp_handle(hdp);
return (ix);
}
/*ARGSUSED*/
int
sbd_io_cnt(sbd_handle_t *hp, sbd_devset_t devset)
{
int i, ix;
sbd_board_t *sbp;
sbp = SBDH2BD(hp->h_sbd);
/*
* Only look for requested devices that are actually present.
*/
devset &= SBD_DEVS_PRESENT(sbp);
for (i = ix = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
dev_info_t *dip;
if (DEVSET_IN_SET(devset, SBD_COMP_IO, i) == 0)
continue;
dip = sbp->sb_devlist[NIX(SBD_COMP_IO)][i];
if (dip == NULL)
continue;
ix++;
}
return (ix);
}
int
sbd_check_io_refs(sbd_handle_t *hp, sbd_devlist_t devlist[], int devnum)
{
register int i, reftotal = 0;
fn_t f = "sbd_check_io_refs";
sbd_error_t *sep;
sbderror_t *ep;
sep = kmem_zalloc(sizeof (sbd_error_t), KM_SLEEP);
ep = HD2MACHERR(hp);
for (i = 0; i < devnum; i++) {
dev_info_t *dip;
int ref;
dip = devlist[i].dv_dip;
ref = 0;
sbdp_check_devices(dip, &ref, sep);
if (ref) {
if (SBD_GET_ERR(ep) == 0) {
SBD_GET_PERR(sep, ep);
}
SBD_GET_PERR(sep, &devlist[i].dv_error);
}
PR_IO("%s: dip(%s) ref = %d\n",
f, ddi_get_name(dip), ref);
reftotal += ref;
}
kmem_free(sep, sizeof (sbd_error_t));
return (reftotal);
}
int
sbd_check_io_attached(dev_info_t *dip, void *arg)
{
dev_info_t **tdip;
tdip = (dev_info_t **)arg;
if (dip == *tdip) {
int state;
state = ddi_get_devstate(dip);
if (i_ddi_node_state(dip) >= DS_ATTACHED ||
(state == DDI_DEVSTATE_UP)) {
*tdip = NULL;
return (DDI_WALK_TERMINATE);
}
}
return (DDI_WALK_CONTINUE);
}
int
sbd_pre_release_io(sbd_handle_t *hp,
sbd_devlist_t *devlist, int devnum)
{
fn_t f = "sbd_pre_release_io";
int rv = 0;
int i;
ASSERT(devnum > 0);
/* fail if any I/O devices are referenced */
if ((rv = sbd_check_io_refs(hp, devlist, devnum)) > 0) {
/*
* One of the devices may have failed check to see which
* and set in the main handle
*/
for (i = 0; i < devnum; i++) {
if (SBD_GET_ERR(&devlist[i].dv_error) != 0) {
(void) sbd_set_err_in_hdl(hp,
&devlist[i].dv_error);
break;
}
}
PR_IO("%s: failed - I/O devices ref'd\n", f);
}
return (rv);
}