diff options
Diffstat (limited to 'usr/src/uts/common')
34 files changed, 1141 insertions, 397 deletions
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index 8439e9fb3e..87c9be72de 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -673,10 +673,10 @@ USBSPRL_OBJS += usbser_pl2303.o pl2303_dsd.o WC_OBJS += wscons.o -SCSI_OBJS += scsi_capabilities.o scsi_control.o scsi_watch.o \ - scsi_data.o scsi_resource.o scsi_subr.o \ - scsi_hba.o scsi_transport.o scsi_confsubr.o \ - scsi_reset_notify.o sas_transport.o +SCSI_OBJS += scsi_capabilities.o scsi_confsubr.o scsi_control.o \ + scsi_data.o scsi_hba.o scsi_reset_notify.o \ + scsi_resource.o scsi_subr.o scsi_transport.o scsi_watch.o \ + sas_transport.o SCSI_VHCI_OBJS += scsi_vhci.o mpapi_impl.o diff --git a/usr/src/uts/common/io/1394/targets/scsa1394/hba.c b/usr/src/uts/common/io/1394/targets/scsa1394/hba.c index 20addb89d9..6399e3c79c 100644 --- a/usr/src/uts/common/io/1394/targets/scsa1394/hba.c +++ b/usr/src/uts/common/io/1394/targets/scsa1394/hba.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -508,7 +508,7 @@ scsa1394_create_cmd_cache(scsa1394_state_t *sp) (void) sprintf(name, "scsa1394%d_cache", sp->s_instance); sp->s_cmd_cache = kmem_cache_create(name, - sizeof (scsa1394_cmd_t), sizeof (void *), + SCSA1394_CMD_SIZE, sizeof (void *), scsa1394_cmd_cache_constructor, scsa1394_cmd_cache_destructor, NULL, (void *)sp, NULL, 0); @@ -1316,7 +1316,7 @@ scsa1394_cmd_cache_constructor(void *buf, void *cdrarg, int kf) { scsa1394_cmd_t *cmd = buf; - bzero(buf, sizeof (scsa1394_cmd_t)); + bzero(buf, SCSA1394_CMD_SIZE); cmd->sc_task.ts_drv_priv = cmd; return (0); diff --git a/usr/src/uts/common/io/devinfo.c b/usr/src/uts/common/io/devinfo.c index 79279b4e31..25bc639c64 100644 --- a/usr/src/uts/common/io/devinfo.c +++ b/usr/src/uts/common/io/devinfo.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -1685,9 +1685,9 @@ di_copytree(struct dev_info *root, di_off_t *off_p, struct di_state *st) static di_off_t di_copynode(struct di_stack *dsp, struct di_state *st) { - di_off_t off; - struct di_node *me; - struct dev_info *node; + di_off_t off; + struct di_node *me; + struct dev_info *node; dcmn_err2((CE_CONT, "di_copynode: depth = %x\n", dsp->depth)); @@ -1751,35 +1751,25 @@ di_copynode(struct di_stack *dsp, struct di_state *st) #ifdef DEVID_COMPATIBILITY /* check for devid as property marker */ - if (node->devi_devid) { + if (node->devi_devid_str) { ddi_devid_t devid; - char *devidstr; int devid_size; /* - * The devid is now represented as a property. - * For micro release compatibility with di_devid interface - * in libdevinfo we must return it as a binary structure in' - * the snapshot. When di_devid is removed from libdevinfo - * in a future release (and devi_devid is deleted) then - * code related to DEVID_COMPATIBILITY can be removed. + * The devid is now represented as a property. For + * compatibility with di_devid() interface in libdevinfo we + * must return it as a binary structure in the snapshot. When + * (if) di_devid() is removed from libdevinfo then the code + * related to DEVID_COMPATIBILITY can be removed. */ - ASSERT(node->devi_devid == DEVID_COMPATIBILITY); -/* XXX should be DDI_DEV_T_NONE! */ - if (ddi_prop_lookup_string(DDI_DEV_T_ANY, (dev_info_t *)node, - DDI_PROP_DONTPASS, DEVID_PROP_NAME, &devidstr) == - DDI_PROP_SUCCESS) { - if (ddi_devid_str_decode(devidstr, &devid, NULL) == - DDI_SUCCESS) { - devid_size = ddi_devid_sizeof(devid); - off = di_checkmem(st, off, devid_size); - me->devid = off; - bcopy(devid, - di_mem_addr(st, off), devid_size); - off += devid_size; - ddi_devid_free(devid); - } - ddi_prop_free(devidstr); + if (ddi_devid_str_decode(node->devi_devid_str, &devid, NULL) == + DDI_SUCCESS) { + devid_size = ddi_devid_sizeof(devid); + off = di_checkmem(st, off, devid_size); + me->devid = off; + bcopy(devid, di_mem_addr(st, off), devid_size); + off += devid_size; + ddi_devid_free(devid); } } #endif /* DEVID_COMPATIBILITY */ @@ -2870,6 +2860,8 @@ di_getpath_data(dev_info_t *dip, di_off_t *poff_p, di_off_t noff, state = mdi_pi_get_state(pip); me->path_state = path_state_convert(state); + me->path_instance = mdi_pi_get_path_instance(pip); + /* * Get intermediate addressing info. */ diff --git a/usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci.c b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci.c index ef21a261e0..47a62af903 100644 --- a/usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci.c +++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci.c @@ -68,7 +68,7 @@ int vhci_prout_not_ready_retry = 180; /* * Version Macros */ -#define VHCI_NAME_VERSION "SCSI VHCI Driver 1.68" +#define VHCI_NAME_VERSION "SCSI VHCI Driver %I%" char vhci_version_name[] = VHCI_NAME_VERSION; int vhci_first_time = 0; @@ -1347,6 +1347,17 @@ pkt_cleanup: * If this changes it needs to be handled for the polled scenario. */ flags = vpkt->vpkt_hba_pkt->pkt_flags; + + /* + * Set the path_instance *before* sending the scsi_pkt down the path + * to mpxio's pHCI so that additional path abstractions at a pHCI + * level (like maybe iSCSI at some point in the future) can update + * the path_instance. + */ + if (scsi_pkt_allocated_correctly(vpkt->vpkt_hba_pkt)) + vpkt->vpkt_hba_pkt->pkt_path_instance = + mdi_pi_get_path_instance(vpkt->vpkt_path); + rval = scsi_transport(vpkt->vpkt_hba_pkt); if (rval == TRAN_ACCEPT) { if (flags & FLAG_NOINTR) { @@ -2101,6 +2112,7 @@ vhci_bind_transport(struct scsi_address *ap, struct vhci_pkt *vpkt, int flags, int mps_flag = MDI_SELECT_ONLINE_PATH; struct scsi_vhci_lun *vlun; time_t tnow; + int path_instance; vlun = ADDR2VLUN(ap); ASSERT(vlun != 0); @@ -2171,6 +2183,17 @@ vhci_bind_transport(struct scsi_address *ap, struct vhci_pkt *vpkt, int flags, } /* + * Get path_instance. Non-zero indicates that mdi_select_path should + * be called to select a specific instance. + * + * NB: Condition pkt_path_instance reference on proper allocation. + */ + if (scsi_pkt_allocated_correctly(vpkt->vpkt_tgt_pkt)) + path_instance = vpkt->vpkt_tgt_pkt->pkt_path_instance; + else + path_instance = 0; + + /* * If reservation is active bind the transport directly to the pip * with the reservation. */ @@ -2190,8 +2213,9 @@ vhci_bind_transport(struct scsi_address *ap, struct vhci_pkt *vpkt, int flags, } } try_again: - rval = mdi_select_path(cdip, vpkt->vpkt_tgt_init_bp, 0, NULL, - &pip); + rval = mdi_select_path(cdip, vpkt->vpkt_tgt_init_bp, + path_instance ? MDI_SELECT_PATH_INSTANCE : 0, + (void *)(intptr_t)path_instance, &pip); if (rval == MDI_BUSY) { if (pgr_sema_held) { sema_v(&vlun->svl_pgr_sema); @@ -2352,6 +2376,10 @@ bind_path: address = &pkt->pkt_address; } + /* Verify match of specified path_instance and selected path_instance */ + ASSERT((path_instance == 0) || + (path_instance == mdi_pi_get_path_instance(vpkt->vpkt_path))); + /* * For PKT_PARTIAL_DMA case, call pHCI's scsi_init_pkt whenever * target driver calls vhci_scsi_init_pkt. @@ -2988,6 +3016,21 @@ vhci_intr(struct scsi_pkt *pkt) tpkt->pkt_statistics = pkt->pkt_statistics; tpkt->pkt_reason = pkt->pkt_reason; + /* Return path_instance information back to the target driver. */ + if (scsi_pkt_allocated_correctly(tpkt)) { + if (scsi_pkt_allocated_correctly(pkt)) { + /* + * If both packets were correctly allocated, + * return path returned by pHCI. + */ + tpkt->pkt_path_instance = pkt->pkt_path_instance; + } else { + /* Otherwise return path of pHCI we used */ + tpkt->pkt_path_instance = + mdi_pi_get_path_instance(lpath); + } + } + if (pkt->pkt_cdbp[0] == SCMD_PROUT && ((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_REGISTER) || ((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_R_AND_IGNORE)) { @@ -3699,6 +3742,9 @@ vhci_update_pathstates(void *arg) SCMD_READ, 1, 1, 0); pkt->pkt_time = 3*30; pkt->pkt_flags = FLAG_NOINTR; + pkt->pkt_path_instance = + mdi_pi_get_path_instance(pip); + if ((scsi_transport(pkt) == TRAN_ACCEPT) && (pkt->pkt_reason == CMD_CMPLT) && (SCBP_C(pkt) == @@ -6741,7 +6787,9 @@ next_pathclass: pkt->pkt_cdbp, SCMD_READ, 1, 1, 0); pkt->pkt_flags = FLAG_NOINTR; check_path_again: + pkt->pkt_path_instance = mdi_pi_get_path_instance(npip); pkt->pkt_time = 3*30; + if (scsi_transport(pkt) == TRAN_ACCEPT) { switch (pkt->pkt_reason) { case CMD_CMPLT: @@ -7110,7 +7158,6 @@ vhci_lun_free(dev_info_t *tgt_dip) } dvlp->svl_lun_wwn = NULL; - if (dvlp->svl_fops_name) { kmem_free(dvlp->svl_fops_name, strlen(dvlp->svl_fops_name)+1); } @@ -8115,6 +8162,13 @@ vhci_uscsi_send_sense(struct scsi_pkt *pkt, mp_uscsi_cmd_t *mp_uscmdp) rqpkt->pkt_comp = vhci_uscsi_iodone; rqpkt->pkt_private = mp_uscmdp; + /* + * NOTE: This code path is related to MPAPI uscsi(7I), so path + * selection is not based on path_instance. + */ + if (scsi_pkt_allocated_correctly(rqpkt)) + rqpkt->pkt_path_instance = 0; + /* get her done */ switch (scsi_transport(rqpkt)) { case TRAN_ACCEPT: @@ -8340,6 +8394,13 @@ vhci_uscsi_iostart(struct buf *bp) (void *)uscmdp, (void *)uscmdp->uscsi_cdb, pkt->pkt_cdblen, (void *)bp, bp->b_bcount, (void *)mp_uscmdp->pip, stat_size)); + /* + * NOTE: This code path is related to MPAPI uscsi(7I), so path + * selection is not based on path_instance. + */ + if (scsi_pkt_allocated_correctly(pkt)) + pkt->pkt_path_instance = 0; + while (((rval = scsi_transport(pkt)) == TRAN_BUSY) && retry < vhci_uscsi_retry_count) { delay(drv_usectohz(vhci_uscsi_delay)); diff --git a/usr/src/uts/common/io/scsi/conf/scsi_confdata.c b/usr/src/uts/common/io/scsi/conf/scsi_confdata.c index 0633d3180b..d171b7d53f 100644 --- a/usr/src/uts/common/io/scsi/conf/scsi_confdata.c +++ b/usr/src/uts/common/io/scsi/conf/scsi_confdata.c @@ -2,9 +2,8 @@ * 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. + * 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. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 1998,2002 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -28,7 +27,7 @@ #ifdef _KERNEL -#include <sys/scsi/conf/autoconf.h> +#include <sys/scsi/scsi_types.h> /* * Autoconfiguration Dependent Data @@ -94,4 +93,9 @@ int scsi_tag_age_limit = 2; */ int scsi_watchdog_tick = 10; +/* + * default scsi target driver "fm-capable" property value + */ +int scsi_fm_capable = DDI_FM_EREPORT_CAPABLE; + #endif /* _KERNEL */ diff --git a/usr/src/uts/common/io/scsi/conf/scsi_confsubr.c b/usr/src/uts/common/io/scsi/conf/scsi_confsubr.c index a3da2b7cef..9a42f48ef3 100644 --- a/usr/src/uts/common/io/scsi/conf/scsi_confsubr.c +++ b/usr/src/uts/common/io/scsi/conf/scsi_confsubr.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -36,6 +36,7 @@ #include <sys/scsi/scsi.h> #include <sys/modctl.h> +#include <sys/bitmap.h> /* * macro for filling in lun value for scsi-1 support @@ -174,6 +175,7 @@ uint64_t scsi_max_phys_addr = 0xFFFFFFFFull; int scsi_sgl_size = 0xFF; #endif +ulong_t *scsi_pkt_bad_alloc_bitmap; int _init() @@ -194,6 +196,9 @@ _init() scsi_alloc_attr.dma_attr_sgllen = scsi_sgl_size; #endif + /* bitmap to limit scsi_pkt allocation violation messages */ + scsi_pkt_bad_alloc_bitmap = kmem_zalloc(BT_SIZEOFMAP(devcnt), KM_SLEEP); + return (mod_install(&modlinkage)); } @@ -706,7 +711,7 @@ done: out: /* * If lun > 0 we need to figure out if this is a scsi-1 device where - * the "real" lun needs to be embeded into the cdb. + * the "real" lun needs to be embedded into the cdb. */ if ((rval == SCSIPROBE_EXISTS) && (pass == 1) && (devp->sd_address.a_lun > 0) && (devp->sd_inq->inq_ansi == 0x1)) { diff --git a/usr/src/uts/common/io/scsi/impl/scsi_hba.c b/usr/src/uts/common/io/scsi/impl/scsi_hba.c index 546ed02424..13b404ac0e 100644 --- a/usr/src/uts/common/io/scsi/impl/scsi_hba.c +++ b/usr/src/uts/common/io/scsi/impl/scsi_hba.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -54,6 +54,9 @@ extern void scsi_sync_cache_pkt(struct scsi_address *, */ #define ROUNDUP(x) (((x) + 0x07) & ~0x07) +/* Magic number to track correct allocations in wrappers */ +#define PKT_WRAPPER_MAGIC 0xa110ced /* alloced correctly */ + static kmutex_t scsi_hba_mutex; kmutex_t scsi_log_mutex; @@ -132,7 +135,8 @@ static int scsi_hba_bus_config(dev_info_t *parent, uint_t flag, ddi_bus_config_op_t op, void *arg, dev_info_t **childp); static int scsi_hba_bus_unconfig(dev_info_t *parent, uint_t flag, ddi_bus_config_op_t op, void *arg); - +static int scsi_hba_fm_init_child(dev_info_t *self, dev_info_t *child, + int cap, ddi_iblock_cookie_t *ibc); static int scsi_hba_bus_power(dev_info_t *parent, void *impl_arg, pm_bus_power_op_t op, void *arg, void *result); @@ -163,7 +167,7 @@ static struct bus_ops scsi_hba_busops = { NULL, /* bus_intr_ctl */ scsi_hba_bus_config, /* bus_config */ scsi_hba_bus_unconfig, /* bus_unconfig */ - NULL, /* bus_fm_init */ + scsi_hba_fm_init_child, /* bus_fm_init */ NULL, /* bus_fm_fini */ NULL, /* bus_fm_access_enter */ NULL, /* bus_fm_access_exit */ @@ -222,13 +226,12 @@ scsi_uninitialize_hba_interface() int scsi_hba_pkt_constructor(void *buf, void *arg, int kmflag) { + struct scsi_pkt_cache_wrapper *pktw; struct scsi_pkt *pkt; scsi_hba_tran_t *tran = (scsi_hba_tran_t *)arg; int pkt_len; char *ptr; - pkt = &((struct scsi_pkt_cache_wrapper *)buf)->pcw_pkt; - /* * allocate a chunk of memory for the following: * scsi_pkt @@ -244,15 +247,20 @@ scsi_hba_pkt_constructor(void *buf, void *arg, int kmflag) if (tran->tran_hba_flags & SCSI_HBA_TRAN_SCB) pkt_len += DEFAULT_SCBLEN; bzero(buf, pkt_len); + ptr = buf; + pktw = buf; ptr += sizeof (struct scsi_pkt_cache_wrapper); + pkt = &(pktw->pcw_pkt); pkt->pkt_ha_private = (opaque_t)ptr; + + pktw->pcw_magic = PKT_WRAPPER_MAGIC; /* alloced correctly */ /* * keep track of the granularity at the time this handle was * allocated */ - ((struct scsi_pkt_cache_wrapper *)buf)->pcw_granular = - tran->tran_dma_attr.dma_attr_granular; + pktw->pcw_granular = tran->tran_dma_attr.dma_attr_granular; + if (ddi_dma_alloc_handle(tran->tran_hba_dip, &tran->tran_dma_attr, kmflag == KM_SLEEP ? SLEEP_FUNC: NULL_FUNC, NULL, @@ -283,6 +291,7 @@ scsi_hba_pkt_destructor(void *buf, void *arg) struct scsi_pkt *pkt = &(pktw->pcw_pkt); scsi_hba_tran_t *tran = (scsi_hba_tran_t *)arg; + ASSERT(pktw->pcw_magic == PKT_WRAPPER_MAGIC); ASSERT((pktw->pcw_flags & PCW_BOUND) == 0); if (tran->tran_pkt_destructor) (*tran->tran_pkt_destructor)(pkt, arg); @@ -293,8 +302,8 @@ scsi_hba_pkt_destructor(void *buf, void *arg) ASSERT(((tran->tran_hba_flags & SCSI_HBA_TRAN_SCB) == 0) || (pkt->pkt_scbp == (opaque_t)((char *)pkt + tran->tran_hba_len + - (((tran->tran_hba_flags & SCSI_HBA_TRAN_CDB) == 0) - ? 0 : DEFAULT_CDBLEN) + + (((tran->tran_hba_flags & SCSI_HBA_TRAN_CDB) == 0) ? + 0 : DEFAULT_CDBLEN) + DEFAULT_PRIVLEN + sizeof (struct scsi_pkt_cache_wrapper)))); ASSERT(((tran->tran_hba_flags & SCSI_HBA_TRAN_CDB) == 0) || (pkt->pkt_cdbp == (opaque_t)((char *)pkt + @@ -382,7 +391,8 @@ scsi_hba_attach_setup( int len; char *prop_name; const char *prop_value; - char *errmsg = + int capable; + static char *errmsg = "scsi_hba_attach: cannot create property '%s' for %s%d\n"; static const char *interconnect[] = INTERCONNECT_TYPE_ASCII; @@ -467,7 +477,7 @@ scsi_hba_attach_setup( if (ddi_prop_update_int(DDI_MAJOR_T_UNKNOWN, dip, prop_name, value) != DDI_PROP_SUCCESS) { cmn_err(CE_CONT, errmsg, prop_name, - ddi_get_name(dip), ddi_get_instance(dip)); + ddi_driver_name(dip), ddi_get_instance(dip)); } } @@ -479,7 +489,7 @@ scsi_hba_attach_setup( if (ddi_prop_update_int(DDI_MAJOR_T_UNKNOWN, dip, prop_name, value) != DDI_PROP_SUCCESS) { cmn_err(CE_CONT, errmsg, prop_name, - ddi_get_name(dip), ddi_get_instance(dip)); + ddi_driver_name(dip), ddi_get_instance(dip)); } } @@ -491,7 +501,7 @@ scsi_hba_attach_setup( if (ddi_prop_update_int(DDI_MAJOR_T_UNKNOWN, dip, prop_name, value) != DDI_PROP_SUCCESS) { cmn_err(CE_CONT, errmsg, prop_name, - ddi_get_name(dip), ddi_get_instance(dip)); + ddi_driver_name(dip), ddi_get_instance(dip)); } } @@ -503,7 +513,7 @@ scsi_hba_attach_setup( if (ddi_prop_update_int(DDI_MAJOR_T_UNKNOWN, dip, prop_name, value) != DDI_PROP_SUCCESS) { cmn_err(CE_CONT, errmsg, prop_name, - ddi_get_name(dip), ddi_get_instance(dip)); + ddi_driver_name(dip), ddi_get_instance(dip)); } } @@ -515,7 +525,7 @@ scsi_hba_attach_setup( if (ddi_prop_update_int(DDI_MAJOR_T_UNKNOWN, dip, prop_name, value) != DDI_PROP_SUCCESS) { cmn_err(CE_CONT, errmsg, prop_name, - ddi_get_name(dip), ddi_get_instance(dip)); + ddi_driver_name(dip), ddi_get_instance(dip)); } } if ((hba_tran->tran_hba_flags & SCSI_HBA_TRAN_ALLOC) && @@ -531,7 +541,8 @@ scsi_hba_attach_setup( prop_name, (char *)prop_value) != DDI_PROP_SUCCESS) { cmn_err(CE_CONT, errmsg, prop_name, - ddi_get_name(dip), ddi_get_instance(dip)); + ddi_driver_name(dip), + ddi_get_instance(dip)); } } } @@ -565,10 +576,56 @@ scsi_hba_attach_setup( } } + /* + * NOTE: SCSA maintains an 'fm-capable' domain, in tran_fm_capable, + * that is not dependent (limited by) the capabilities of its parents. + * For example a dip in a branch that is not DDI_FM_EREPORT_CAPABLE + * may report as capable, via tran_fm_capable, to its scsi_device + * children. + * + * Get 'fm-capable' property from driver.conf, if present. If not + * present, default to the scsi_fm_capable global (which has + * DDI_FM_EREPORT_CAPABLE set by default). + */ + if (hba_tran->tran_fm_capable == DDI_FM_NOT_CAPABLE) + hba_tran->tran_fm_capable = ddi_getprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP | DDI_PROP_NOTPROM, + "fm-capable", scsi_fm_capable); + + /* + * If an HBA is *not* doing its own fma support by calling + * ddi_fm_init() prior to scsi_hba_attach_setup(), we provide a + * minimal common SCSA implementation so that scsi_device children + * can generate ereports via scsi_fm_ereport_post(). We use + * ddi_fm_capable() to detect an HBA calling ddi_fm_init() prior to + * scsi_hba_attach_setup(). + */ + if (hba_tran->tran_fm_capable && + (ddi_fm_capable(dip) == DDI_FM_NOT_CAPABLE)) { + /* + * We are capable of something, pass our capabilities up + * the tree, but use a local variable so our parent can't + * limit our capabilities (we don't want our parent to + * clear DDI_FM_EREPORT_CAPABLE). + * + * NOTE: iblock cookies are not important because scsi + * HBAs always interrupt below LOCK_LEVEL. + */ + capable = hba_tran->tran_fm_capable; + ddi_fm_init(dip, &capable, NULL); + + /* + * Set SCSI_HBA_TRAN_FMSCSA bit to mark us as usiung the + * common minimal SCSA fm implementation - we called + * ddi_fm_init(), so we are responsible for calling + * ddi_fm_fini() in scsi_hba_detach(). + */ + hba_tran->tran_hba_flags |= SCSI_HBA_TRAN_FMSCSA; + } + return (DDI_SUCCESS); } - /* * Called by an HBA to detach an instance of the driver */ @@ -585,6 +642,14 @@ scsi_hba_detach(dev_info_t *dip) ASSERT(hba != NULL); ASSERT(hba->tran_open_flag == 0); + /* + * If we are taking care of mininal default fma implementation, + * call ddi_fm_fini(9F). + */ + if (hba->tran_hba_flags & SCSI_HBA_TRAN_FMSCSA) { + ddi_fm_fini(dip); + } + hba_dev_ops = ddi_get_driver(dip); ASSERT(hba_dev_ops != NULL); if (hba_dev_ops->devo_cb_ops->cb_open == scsi_hba_open) { @@ -680,8 +745,8 @@ smp_ctlops_reportdev(dev_info_t *dip, dev_info_t *rdip) ASSERT(hba != NULL); if (ddi_prop_lookup_string(DDI_DEV_T_ANY, rdip, - DDI_PROP_DONTPASS, SMP_WWN, &smp_wwn) - != DDI_SUCCESS) { + DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP | DDI_PROP_NOTPROM, + SMP_WWN, &smp_wwn) != DDI_SUCCESS) { return (DDI_FAILURE); } cmn_err(CE_CONT, @@ -729,8 +794,8 @@ smp_ctlops_initchild(dev_info_t *dip, dev_info_t *rdip) smp->smp_addr.a_hba_tran = hba; if (ddi_prop_lookup_string(DDI_DEV_T_ANY, rdip, - DDI_PROP_DONTPASS, SMP_WWN, &smp_wwn) - != DDI_SUCCESS) { + DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP | DDI_PROP_NOTPROM, + SMP_WWN, &smp_wwn) != DDI_SUCCESS) { return (DDI_FAILURE); } @@ -816,7 +881,8 @@ scsi_hba_bus_ctl( hba = ddi_get_driver_private(dip); ASSERT(hba != NULL); - if (ddi_prop_exists(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, + if (ddi_prop_exists(DDI_DEV_T_ANY, rdip, + DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP | DDI_PROP_NOTPROM, SMP_PROP)) { return (smp_ctlops_reportdev(dip, rdip)); } @@ -883,7 +949,8 @@ scsi_hba_bus_ctl( scsi_hba_tran_t *hba; dev_info_t *ndip; - if (ddi_prop_exists(DDI_DEV_T_ANY, child_dip, DDI_PROP_DONTPASS, + if (ddi_prop_exists(DDI_DEV_T_ANY, child_dip, + DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP | DDI_PROP_NOTPROM, SMP_PROP)) { return (smp_ctlops_initchild(dip, child_dip)); } @@ -931,7 +998,8 @@ scsi_hba_bus_ctl( cmn_err(CE_CONT, "%s%d: should support both or none of " "tran_get_name and tran_get_bus_addr\n", - ddi_get_name(dip), ddi_get_instance(dip)); + ddi_driver_name(dip), + ddi_get_instance(dip)); goto failure; } } @@ -1059,7 +1127,8 @@ failure: dev_info_t *child_dip = (dev_info_t *)arg; scsi_hba_tran_t *hba; - if (ddi_prop_exists(DDI_DEV_T_ANY, child_dip, DDI_PROP_DONTPASS, + if (ddi_prop_exists(DDI_DEV_T_ANY, child_dip, + DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP | DDI_PROP_NOTPROM, SMP_PROP)) { return (smp_ctlops_uninitchild(dip, child_dip)); } @@ -1122,8 +1191,8 @@ failure: case DDI_CTLOPS_POKE: case DDI_CTLOPS_PEEK: cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n", - ddi_get_name(dip), ddi_get_instance(dip), - op, ddi_get_name(rdip), ddi_get_instance(rdip)); + ddi_driver_name(dip), ddi_get_instance(dip), + op, ddi_driver_name(rdip), ddi_get_instance(rdip)); return (DDI_FAILURE); /* @@ -1194,13 +1263,12 @@ scsi_hba_tran_free( kmem_free(hba_tran, sizeof (scsi_hba_tran_t)); } - - /* * Private wrapper for scsi_pkt's allocated via scsi_hba_pkt_alloc() */ struct scsi_pkt_wrapper { struct scsi_pkt scsi_pkt; + int pkt_wrapper_magic; int pkt_wrapper_len; }; @@ -1227,6 +1295,7 @@ scsi_hba_pkt_alloc( struct scsi_pkt *pkt; struct scsi_pkt_wrapper *hba_pkt; caddr_t p; + int acmdlen, astatuslen, atgtlen, ahbalen; int pktlen; /* @@ -1240,12 +1309,12 @@ scsi_hba_pkt_alloc( /* * Round up so everything gets allocated on long-word boundaries */ - cmdlen = ROUNDUP(cmdlen); - tgtlen = ROUNDUP(tgtlen); - hbalen = ROUNDUP(hbalen); - statuslen = ROUNDUP(statuslen); - pktlen = sizeof (struct scsi_pkt_wrapper) - + cmdlen + tgtlen + hbalen + statuslen; + acmdlen = ROUNDUP(cmdlen); + astatuslen = ROUNDUP(statuslen); + atgtlen = ROUNDUP(tgtlen); + ahbalen = ROUNDUP(hbalen); + pktlen = sizeof (struct scsi_pkt_wrapper) + + acmdlen + astatuslen + atgtlen + ahbalen; hba_pkt = kmem_zalloc(pktlen, (callback == SLEEP_FUNC) ? KM_SLEEP : KM_NOSLEEP); @@ -1258,23 +1327,24 @@ scsi_hba_pkt_alloc( * Set up our private info on this pkt */ hba_pkt->pkt_wrapper_len = pktlen; + hba_pkt->pkt_wrapper_magic = PKT_WRAPPER_MAGIC; /* alloced correctly */ pkt = &hba_pkt->scsi_pkt; - p = (caddr_t)(hba_pkt + 1); /* * Set up pointers to private data areas, cdb, and status. */ + p = (caddr_t)(hba_pkt + 1); if (hbalen > 0) { pkt->pkt_ha_private = (opaque_t)p; - p += hbalen; + p += ahbalen; } if (tgtlen > 0) { pkt->pkt_private = (opaque_t)p; - p += tgtlen; + p += atgtlen; } if (statuslen > 0) { pkt->pkt_scbp = (uchar_t *)p; - p += statuslen; + p += astatuslen; } if (cmdlen > 0) { pkt->pkt_cdbp = (uchar_t *)p; @@ -1285,10 +1355,18 @@ scsi_hba_pkt_alloc( */ pkt->pkt_address = *ap; + /* + * NB: It may not be safe for drivers, esp target drivers, to depend + * on the following fields being set until all the scsi_pkt + * allocation violations discussed in scsi_pkt.h are all resolved. + */ + pkt->pkt_cdblen = cmdlen; + pkt->pkt_tgtlen = tgtlen; + pkt->pkt_scblen = statuslen; + return (pkt); } - /* * Called by an HBA to free a scsi_pkt */ @@ -1301,6 +1379,146 @@ scsi_hba_pkt_free( kmem_free(pkt, ((struct scsi_pkt_wrapper *)pkt)->pkt_wrapper_len); } +/* + * Return 1 if the scsi_pkt used a proper allocator. + * + * The DDI does not allow a driver to allocate it's own scsi_pkt(9S), a + * driver should not have *any* compiled in dependencies on "sizeof (struct + * scsi_pkt)". While this has been the case for many years, a number of + * drivers have still not been fixed. This function can be used to detect + * improperly allocated scsi_pkt structures, and produce messages identifying + * drivers that need to be fixed. + * + * While drivers in violation are being fixed, this function can also + * be used by the framework to detect packets that violated allocation + * rules. + * + * NB: It is possible, but very unlikely, for this code to return a false + * positive (finding correct magic, but for wrong reasons). Careful + * consideration is needed for callers using this interface to condition + * access to newer scsi_pkt fields (those after pkt_reason). + * + * NB: As an aid to minimizing the amount of work involved in 'fixing' legacy + * drivers that violate scsi_*(9S) allocation rules, private + * scsi_pkt_size()/scsi_size_clean() functions are available (see their + * implementation for details). + * + * *** Non-legacy use of scsi_pkt_size() is discouraged. *** + * + * NB: When supporting broken HBA drivers is not longer a concern, this + * code should be removed. + */ +int +scsi_pkt_allocated_correctly(struct scsi_pkt *pkt) +{ + struct scsi_pkt_wrapper *hba_pkt = (struct scsi_pkt_wrapper *)pkt; + int magic; + major_t major; +#ifdef DEBUG + int *pspwm, *pspcwm; + + /* + * We are getting scsi packets from two 'correct' wrapper schemes, + * make sure we are looking at the same place in both to detect + * proper allocation. + */ + pspwm = &((struct scsi_pkt_wrapper *)0)->pkt_wrapper_magic; + pspcwm = &((struct scsi_pkt_cache_wrapper *)0)->pcw_magic; + ASSERT(pspwm == pspcwm); +#endif /* DEBUG */ + + + /* + * Check to see if driver is scsi_size_clean(), assume it + * is using the scsi_pkt_size() interface everywhere it needs to + * if the driver indicates it is scsi_size_clean(). + */ + major = ddi_driver_major(P_TO_TRAN(pkt)->tran_hba_dip); + if (devnamesp[major].dn_flags & DN_SCSI_SIZE_CLEAN) + return (1); /* ok */ + + /* + * Special case crossing a page boundary. If the scsi_pkt was not + * allocated correctly, then accross a page boundary we have a + * fault hazzard. + */ + if ((((uintptr_t)(&hba_pkt->scsi_pkt)) & MMU_PAGEMASK) == + (((uintptr_t)(&hba_pkt->pkt_wrapper_magic)) & MMU_PAGEMASK)) { + /* fastpath, no cross-page hazzard */ + magic = hba_pkt->pkt_wrapper_magic; + } else { + /* add protection for cross-page hazzard */ + if (ddi_peek32((dev_info_t *)NULL, + &hba_pkt->pkt_wrapper_magic, &magic) == DDI_FAILURE) { + return (0); /* violation */ + } + } + + /* properly allocated packet always has correct magic */ + return ((magic == PKT_WRAPPER_MAGIC) ? 1 : 0); +} + +/* + * Private interfaces to simplify conversion of legacy drivers so they don't + * depend on scsi_*(9S) size. Instead of using these private interface, HBA + * drivers should use DDI sanctioned allocation methods: + * + * scsi_pkt Use scsi_hba_pkt_alloc(9F), or implement + * tran_setup_pkt(9E). + * + * scsi_device You are doing something strange/special, a scsi_device + * structure should only be allocated by scsi_hba.c + * initchild code or scsi_vhci.c code. + * + * scsi_hba_tran Use scsi_hba_tran_alloc(9F). + */ +size_t +scsi_pkt_size() +{ + return (sizeof (struct scsi_pkt)); +} + +size_t +scsi_hba_tran_size() +{ + return (sizeof (scsi_hba_tran_t)); +} + +size_t +scsi_device_size() +{ + return (sizeof (struct scsi_device)); +} + +/* + * Legacy compliance to scsi_pkt(9S) allocation rules through use of + * scsi_pkt_size() is detected by the 'scsi-size-clean' driver.conf property + * or an HBA driver calling to scsi_size_clean() from attach(9E). A driver + * developer should only indicate that a legacy driver is clean after using + * SCSI_SIZE_CLEAN_VERIFY to ensure compliance (see scsi_pkt.h). + */ +void +scsi_size_clean(dev_info_t *dip) +{ + major_t major; + struct devnames *dnp; + + ASSERT(dip); + major = ddi_driver_major(dip); + ASSERT(major < devcnt); + if (major >= devcnt) { + cmn_err(CE_WARN, "scsi_pkt_size: bogus major: %d", major); + return; + } + + /* Set DN_SCSI_SIZE_CLEAN flag in dn_flags. */ + dnp = &devnamesp[major]; + if ((dnp->dn_flags & DN_SCSI_SIZE_CLEAN) == 0) { + LOCK_DEV_OPS(&dnp->dn_lock); + dnp->dn_flags |= DN_SCSI_SIZE_CLEAN; + UNLOCK_DEV_OPS(&dnp->dn_lock); + } +} /* @@ -2448,6 +2666,16 @@ scsi_hba_nodename_compatible_free(char *nodename, char **compatible) (NCOMPAT * COMPAT_LONGEST)); } +/*ARGSUSED*/ +static int +scsi_hba_fm_init_child(dev_info_t *self, dev_info_t *child, int cap, + ddi_iblock_cookie_t *ibc) +{ + scsi_hba_tran_t *hba = ddi_get_driver_private(self); + + return (hba ? hba->tran_fm_capable : scsi_fm_capable); +} + static int scsi_hba_bus_power(dev_info_t *parent, void *impl_arg, pm_bus_power_op_t op, void *arg, void *result) diff --git a/usr/src/uts/common/io/scsi/impl/scsi_resource.c b/usr/src/uts/common/io/scsi/impl/scsi_resource.c index b7e49e8295..15397d539c 100644 --- a/usr/src/uts/common/io/scsi/impl/scsi_resource.c +++ b/usr/src/uts/common/io/scsi/impl/scsi_resource.c @@ -309,6 +309,7 @@ scsi_init_cache_pkt(struct scsi_address *ap, struct scsi_pkt *in_pktp, in_pktp->pkt_dma_offset = 0; in_pktp->pkt_dma_len = 0; in_pktp->pkt_dma_flags = 0; + in_pktp->pkt_path_instance = 0; ASSERT(in_pktp->pkt_numcookies == 0); pktw->pcw_curwin = 0; pktw->pcw_totalwin = 0; diff --git a/usr/src/uts/common/io/scsi/impl/scsi_subr.c b/usr/src/uts/common/io/scsi/impl/scsi_subr.c index 8cf17cc898..145e431b9d 100644 --- a/usr/src/uts/common/io/scsi/impl/scsi_subr.c +++ b/usr/src/uts/common/io/scsi/impl/scsi_subr.c @@ -1968,22 +1968,23 @@ scsi_uscsi_mincnt(struct buf *bp) } /* - * Function: scsi_uscsi_alloc_and_copyin + * Function: scsi_uscsi_alloc_and_copyin * * Description: Target drivers call this function to allocate memeory, - * copy in, and convert ILP32/LP64 to make preparations for handling - * uscsi commands. + * copy in, and convert ILP32/LP64 to make preparations for handling + * uscsi commands. * - * Arguments: arg - pointer to the caller's uscsi command struct - * flag - mode, corresponds to ioctl(9e) 'mode' - * ap - SCSI address structure - * uscmdp - pointer to the converted uscsi command + * Arguments: + * arg - pointer to the caller's uscsi command struct + * flag - mode, corresponds to ioctl(9e) 'mode' + * ap - SCSI address structure + * uscmdp - pointer to the converted uscsi command * * Return code: 0 - * EFAULT - * EINVAL + * EFAULT + * EINVAL * - * Context: Never called at interrupt context. + * Context: Never called at interrupt context. */ int @@ -2000,8 +2001,9 @@ scsi_uscsi_alloc_and_copyin(intptr_t arg, int flag, struct scsi_address *ap, #endif /* _MULTI_DATAMODEL */ struct uscsi_i_cmd *uicmd; struct uscsi_cmd *uscmd; - int max_hba_cdb; - int rval; + int max_hba_cdb; + int rval; + extern dev_info_t *scsi_vhci_dip; /* * In order to not worry about where the uscsi structure came @@ -2063,6 +2065,16 @@ scsi_uscsi_alloc_and_copyin(intptr_t arg, int flag, struct scsi_address *ap, } /* + * Currently, USCSI_PATH_INSTANCE is only valid when directed + * to scsi_vhci. + */ + if ((uscmd->uscsi_flags & USCSI_PATH_INSTANCE) && + (A_TO_TRAN(ap)->tran_hba_dip != scsi_vhci_dip)) { + rval = EFAULT; + goto done; + } + + /* * Perfunctory sanity checks. Get the maximum hba supported * cdb length first. */ @@ -2082,6 +2094,15 @@ scsi_uscsi_alloc_and_copyin(intptr_t arg, int flag, struct scsi_address *ap, } /* + * To extend uscsi_cmd in the future, we need to ensure current + * reserved bits remain unused (zero). + */ + if (uscmd->uscsi_flags & USCSI_RESERVED) { + rval = EINVAL; + goto done; + } + + /* * Now we get some space for the CDB, and copy the given CDB into * it. Use ddi_copyin() in case the data is in user space. */ @@ -2141,26 +2162,27 @@ done: } /* - * Function: scsi_uscsi_handle_cmd + * Function: scsi_uscsi_handle_cmd * * Description: Target drivers call this function to handle uscsi commands. * - * Arguments: dev - device number - * dataspace - UIO_USERSPACE or UIO_SYSSPACE - * uscmd - pointer to the converted uscsi command - * strat - pointer to the driver's strategy routine - * bp - buf struct ptr - * private_data - pointer to bp->b_private + * Arguments: + * dev - device number + * dataspace - UIO_USERSPACE or UIO_SYSSPACE + * uscmd - pointer to the converted uscsi command + * strat - pointer to the driver's strategy routine + * bp - buf struct ptr + * private_data - pointer to bp->b_private * * Return code: 0 - * EIO - scsi_reset() failed, or see biowait()/physio() codes. + * EIO - scsi_reset() failed, or see biowait()/physio() codes. * EINVAL * return code of biowait(9F) or physio(9F): - * EIO - IO error + * EIO - IO error * ENXIO - * EACCES - reservation conflict + * EACCES - reservation conflict * - * Context: Never called at interrupt context. + * Context: Never called at interrupt context. */ int @@ -2272,6 +2294,82 @@ scsi_uscsi_handle_cmd(dev_t dev, enum uio_seg dataspace, } /* + * Function: scsi_uscsi_pktinit + * + * Description: Target drivers call this function to transfer uscsi_cmd + * information into a scsi_pkt before sending the scsi_pkt. + * + * NB: At this point the implementation is limited to path_instance. + * At some point more code could be removed from the target driver by + * enhancing this function - with the added benifit of making the uscsi + * implementation more consistent accross all drivers. + * + * Arguments: + * uscmd - pointer to the uscsi command + * pkt - pointer to the scsi_pkt + * + * Return code: 1 on successfull transfer, 0 on failure. + */ +int +scsi_uscsi_pktinit(struct uscsi_cmd *uscmd, struct scsi_pkt *pkt) +{ + int path_instance; + + /* get path_instance from uscsi_cmd */ + path_instance = (uscmd->uscsi_flags & USCSI_PATH_INSTANCE) ? + uscmd->uscsi_path_instance : 0; + + /* + * Check to make sure the scsi_pkt was allocated correctly before + * transferring uscsi(7i) path_instance to scsi_pkt(9S). + */ + if (!scsi_pkt_allocated_correctly(pkt)) { + /* If path_instance is zero, pretend success */ + if (path_instance == 0) + return (1); /* pretend success */ + + return (0); /* failure */ + } + + pkt->pkt_path_instance = path_instance; + return (1); /* success */ +} + +/* + * Function: scsi_uscsi_pktfini + * + * Description: Target drivers call this function to transfer completed + * scsi_pkt information back into uscsi_cmd. + * + * NB: At this point the implementation is limited to path_instance. + * At some point more code could be removed from the target driver by + * enhancing this function - with the added benifit of making the uscsi + * implementation more consistent accross all drivers. + * + * Arguments: + * pkt - pointer to the scsi_pkt + * uscmd - pointer to the uscsi command + * + * Return code: 1 on successfull transfer, 0 on failure. + */ +int +scsi_uscsi_pktfini(struct scsi_pkt *pkt, struct uscsi_cmd *uscmd) +{ + /* + * Check to make sure the scsi_pkt was allocated correctly before + * transferring scsi_pkt(9S) path_instance to uscsi(7i). + */ + if (!scsi_pkt_allocated_correctly(pkt)) { + uscmd->uscsi_path_instance = 0; + return (0); /* failure */ + } + + uscmd->uscsi_path_instance = pkt->pkt_path_instance; + pkt->pkt_path_instance = 0; + return (1); /* success */ +} + +/* * Function: scsi_uscsi_copyout_and_free * * Description: Target drivers call this function to undo what was done by @@ -2285,7 +2383,6 @@ scsi_uscsi_handle_cmd(dev_t dev, enum uio_seg dataspace, * * Context: Never called at interrupt context. */ - int scsi_uscsi_copyout_and_free(intptr_t arg, struct uscsi_cmd *uscmd) { diff --git a/usr/src/uts/common/io/scsi/impl/scsi_transport.c b/usr/src/uts/common/io/scsi/impl/scsi_transport.c index eec642408f..0ba3f94fb9 100644 --- a/usr/src/uts/common/io/scsi/impl/scsi_transport.c +++ b/usr/src/uts/common/io/scsi/impl/scsi_transport.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -30,6 +30,7 @@ */ #include <sys/scsi/scsi.h> #include <sys/thread.h> +#include <sys/bitmap.h> #define A_TO_TRAN(ap) ((ap)->a_hba_tran) #define P_TO_TRAN(pkt) ((pkt)->pkt_address.a_hba_tran) @@ -44,9 +45,13 @@ int scsi_poll_user; int scsi_poll_intr; #endif +int scsi_pkt_bad_alloc_msg = 1; +extern ulong_t *scsi_pkt_bad_alloc_bitmap; extern kmutex_t scsi_flag_nointr_mutex; extern kcondvar_t scsi_flag_nointr_cv; +extern int do_polled_io; + /* * we used to set the callback_done value to NULL after the callback * but this interfered with esp/fas drivers that also set the callback @@ -75,7 +80,7 @@ static void scsi_consistent_comp(struct scsi_pkt *pkt) { struct scsi_pkt_cache_wrapper *pcw = - (struct scsi_pkt_cache_wrapper *)pkt; + (struct scsi_pkt_cache_wrapper *)pkt; pkt->pkt_comp = pcw->pcw_orig_comp; scsi_sync_pkt(pkt); @@ -96,18 +101,54 @@ int scsi_transport(struct scsi_pkt *pkt) { struct scsi_address *ap = P_TO_ADDR(pkt); - extern int do_polled_io; - int rval = TRAN_ACCEPT; + int rval = TRAN_ACCEPT; + major_t major; + + /* + * The DDI does not allow drivers to allocate their own scsi_pkt(9S), + * a driver can't have *any* compiled in dependencies on the + * "sizeof (struct scsi_pkt)". While this has been the case for years, + * many drivers have still not been fixed (or have regressed - tempted + * by kmem_cache_alloc()). The correct way to allocate a scsi_pkt + * is by calling scsi_hba_pkt_alloc(9F), or by implementing the + * tran_setup_pkt(9E) interfaces. + * + * The code below will identify drivers that violate this rule, and + * print a message. The message will identify broken drivers, and + * encourage getting these drivers fixed - after which this code + * can be removed. Getting HBA drivers fixed is important because + * broken drivers are an impediment to SCSA enhancement. + * + * We use the scsi_pkt_allocated_correctly() to determine if the + * scsi_pkt we are about to start was correctly allocated. The + * scsi_pkt_bad_alloc_bitmap is used to limit messages to one per + * driver per reboot, and with non-debug code we only check the + * first scsi_pkt. + */ + if (scsi_pkt_bad_alloc_msg) { + major = ddi_driver_major(P_TO_TRAN(pkt)->tran_hba_dip); + if (!BT_TEST(scsi_pkt_bad_alloc_bitmap, major) && + !scsi_pkt_allocated_correctly(pkt)) { + BT_SET(scsi_pkt_bad_alloc_bitmap, major); + cmn_err(CE_WARN, "%s: violates DDI scsi_pkt(9S) " + "allocation rules", + ddi_driver_name(P_TO_TRAN(pkt)->tran_hba_dip)); + } +#ifndef DEBUG + /* On non-debug kernel, only check the first packet */ + BT_SET(scsi_pkt_bad_alloc_bitmap, major); +#endif /* DEBUG */ + } /* determine if we need to sync the data on the HBA's behalf */ if ((pkt->pkt_dma_flags & DDI_DMA_CONSISTENT) && ((pkt->pkt_comp) != NULL) && ((P_TO_TRAN(pkt)->tran_setup_pkt) != NULL)) { struct scsi_pkt_cache_wrapper *pcw = - (struct scsi_pkt_cache_wrapper *)pkt; + (struct scsi_pkt_cache_wrapper *)pkt; _NOTE(SCHEME_PROTECTS_DATA("unique per pkt", \ - scsi_pkt_cache_wrapper::pcw_orig_comp)); + scsi_pkt_cache_wrapper::pcw_orig_comp)); pcw->pcw_orig_comp = pkt->pkt_comp; pkt->pkt_comp = scsi_consistent_comp; @@ -192,11 +233,11 @@ scsi_transport(struct scsi_pkt *pkt) pkt->pkt_flags |= FLAG_IMMEDIATE_CB; if ((status = (*A_TO_TRAN(ap)->tran_start)(ap, pkt)) == - TRAN_ACCEPT) { + TRAN_ACCEPT) { mutex_enter(&scsi_flag_nointr_mutex); while (pkt->pkt_comp != CALLBACK_DONE) { cv_wait(&scsi_flag_nointr_cv, - &scsi_flag_nointr_mutex); + &scsi_flag_nointr_mutex); } mutex_exit(&scsi_flag_nointr_mutex); } diff --git a/usr/src/uts/common/io/scsi/targets/sd.c b/usr/src/uts/common/io/scsi/targets/sd.c index 976494be61..3b62d3f24b 100644 --- a/usr/src/uts/common/io/scsi/targets/sd.c +++ b/usr/src/uts/common/io/scsi/targets/sd.c @@ -12369,6 +12369,9 @@ sd_initpkt_for_uscsi(struct buf *bp, struct scsi_pkt **pktpp) pktp->pkt_flags = flags; + /* Transfer uscsi information to scsi_pkt */ + (void) scsi_uscsi_pktinit(uscmd, pktp); + /* Copy the caller's CDB into the pkt... */ bcopy(uscmd->uscsi_cdb, pktp->pkt_cdbp, uscmd->uscsi_cdblen); @@ -12432,6 +12435,9 @@ sd_destroypkt_for_uscsi(struct buf *bp) uscmd->uscsi_status = ((*(pktp)->pkt_scbp) & STATUS_MASK); uscmd->uscsi_resid = bp->b_resid; + /* Transfer scsi_pkt information to uscsi */ + (void) scsi_uscsi_pktfini(pktp, uscmd); + /* * If enabled, copy any saved sense data into the area specified * by the uscsi command. diff --git a/usr/src/uts/common/io/scsi/targets/sgen.c b/usr/src/uts/common/io/scsi/targets/sgen.c index e4d9640aa3..3003e5412c 100644 --- a/usr/src/uts/common/io/scsi/targets/sgen.c +++ b/usr/src/uts/common/io/scsi/targets/sgen.c @@ -142,7 +142,7 @@ static int sgen_handle_autosense(sgen_state_t *, struct scsi_pkt *); static int sgen_handle_sense(sgen_state_t *); static int sgen_handle_incomplete(sgen_state_t *, struct scsi_pkt *); static int sgen_check_error(sgen_state_t *, struct buf *); -static int sgen_initiate_sense(sgen_state_t *); +static int sgen_initiate_sense(sgen_state_t *, int); static int sgen_scsi_transport(struct scsi_pkt *); static int sgen_tur(dev_t); @@ -1506,6 +1506,9 @@ sgen_make_uscsi_cmd(sgen_state_t *sg_state, struct buf *bp) pkt->pkt_flags |= FLAG_RENEGOTIATE_WIDE_SYNC; } + /* Transfer uscsi information to scsi_pkt */ + (void) scsi_uscsi_pktinit(ucmd, pkt); + sgen_log(sg_state, SGEN_DIAG2, "done sgen_make_uscsi_cmd()"); return (0); } @@ -1560,6 +1563,7 @@ static void sgen_callback(struct scsi_pkt *pkt) { sgen_state_t *sg_state; + struct uscsi_cmd *ucmd; struct buf *bp; int action; @@ -1580,10 +1584,14 @@ sgen_callback(struct scsi_pkt *pkt) sgen_log(sg_state, SGEN_DIAG2, "in sgen_callback() (command completion callback)"); } + ucmd = (struct uscsi_cmd *)bp->b_private; sgen_log(sg_state, SGEN_DIAG3, "sgen_callback: reason=0x%x resid=%ld " "state=0x%x", pkt->pkt_reason, pkt->pkt_resid, pkt->pkt_state); + /* Transfer scsi_pkt information to uscsi */ + (void) scsi_uscsi_pktfini(pkt, ucmd); + if (pkt->pkt_reason != CMD_CMPLT) { /* * The command did not complete. @@ -1628,7 +1636,9 @@ sgen_callback(struct scsi_pkt *pkt) * If there is sense to fetch, break out to prevent biodone'ing * until the sense fetch is complete. */ - if (sgen_initiate_sense(sg_state) == 0) + if (sgen_initiate_sense(sg_state, + scsi_pkt_allocated_correctly(pkt) ? + pkt->pkt_path_instance : 0) == 0) break; /*FALLTHROUGH*/ case COMMAND_DONE_ERROR: @@ -1651,8 +1661,12 @@ sgen_callback(struct scsi_pkt *pkt) * Send the sgen_rqspkt to the target, thereby requesting sense data. */ static int -sgen_initiate_sense(sgen_state_t *sg_state) +sgen_initiate_sense(sgen_state_t *sg_state, int path_instance) { + /* use same path_instance as command */ + if (scsi_pkt_allocated_correctly(sg_state->sgen_rqspkt)) + sg_state->sgen_rqspkt->pkt_path_instance = path_instance; + switch (sgen_scsi_transport(sg_state->sgen_rqspkt)) { case TRAN_ACCEPT: sgen_log(sg_state, SGEN_DIAG3, "sgen_initiate_sense: " diff --git a/usr/src/uts/common/io/scsi/targets/st.c b/usr/src/uts/common/io/scsi/targets/st.c index c021413f69..65fc94f01f 100644 --- a/usr/src/uts/common/io/scsi/targets/st.c +++ b/usr/src/uts/common/io/scsi/targets/st.c @@ -16203,7 +16203,7 @@ st_command_recovery(struct scsi_tape *un, struct scsi_pkt *pkt, /* * Create structure to hold all error state info. */ - errinfo = kmem_zalloc(sizeof (st_err_info), KM_SLEEP); + errinfo = kmem_zalloc(ST_ERR_INFO_SIZE, KM_SLEEP); errinfo->ei_error_type = onentry; errinfo->ei_failing_bp = ri->cmd_bp; COPY_POS(&errinfo->ei_expected_pos, &ri->pos); @@ -16212,13 +16212,13 @@ st_command_recovery(struct scsi_tape *un, struct scsi_pkt *pkt, return (COMMAND_DONE_ERROR); } - bcopy(pkt, &errinfo->ei_failed_pkt, sizeof (struct scsi_pkt)); + bcopy(pkt, &errinfo->ei_failed_pkt, scsi_pkt_size()); bcopy(pkt->pkt_scbp, &errinfo->ei_failing_status, SECMDS_STATUS_SIZE); ret = ddi_taskq_dispatch(un->un_recov_taskq, st_recover, errinfo, DDI_NOSLEEP); ASSERT(ret == DDI_SUCCESS); if (ret != DDI_SUCCESS) { - kmem_free(errinfo, sizeof (st_err_info)); + kmem_free(errinfo, ST_ERR_INFO_SIZE); return (COMMAND_DONE_ERROR); } return (JUST_RETURN); /* release calling thread */ @@ -16236,7 +16236,7 @@ st_recov_ret(struct scsi_tape *un, st_err_info *errinfo, errstate err) ASSERT(MUTEX_HELD(&un->un_sd->sd_mutex)); bp = errinfo->ei_failing_bp; - kmem_free(errinfo, sizeof (st_err_info)); + kmem_free(errinfo, ST_ERR_INFO_SIZE); switch (err) { case JUST_RETURN: diff --git a/usr/src/uts/common/os/ddifm.c b/usr/src/uts/common/os/ddifm.c index a111620eae..20a8cf99cc 100644 --- a/usr/src/uts/common/os/ddifm.c +++ b/usr/src/uts/common/os/ddifm.c @@ -212,119 +212,6 @@ ddi_fm_service_impact(dev_info_t *dip, int svc_impact) mutex_exit(&(DEVI(dip)->devi_lock)); } -static int -erpt_post_sleep(dev_info_t *dip, const char *error_class, uint64_t ena, - uint8_t version, va_list ap) -{ - char *devid, *name; - char device_path[MAXPATHLEN]; - char ddi_error_class[ERPT_CLASS_SZ]; - nvlist_t *ereport, *detector = NULL; - - /* - * Driver defect - should not call with DDI_SLEEP while - * in interrupt context - */ - if (servicing_interrupt()) { - i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, DDI_NOSLEEP); - return (1); - } - - if ((ereport = fm_nvlist_create(NULL)) == NULL) - return (1); - - /* - * Use the dev_path/devid for this device instance. - */ - detector = fm_nvlist_create(NULL); - if (dip == ddi_root_node()) { - device_path[0] = '/'; - device_path[1] = '\0'; - } else { - (void) ddi_pathname(dip, device_path); - } - - if (ddi_prop_lookup_string(DDI_DEV_T_NONE, dip, - DDI_PROP_DONTPASS, DEVID_PROP_NAME, &devid) == DDI_SUCCESS) { - fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, - device_path, devid); - ddi_prop_free(devid); - } else { - fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, - device_path, NULL); - } - - if (ena == 0) - ena = fm_ena_generate(0, FM_ENA_FMT1); - - (void) snprintf(ddi_error_class, ERPT_CLASS_SZ, "%s.%s", - DDI_IO_CLASS, error_class); - - fm_ereport_set(ereport, version, ddi_error_class, - ena, detector, NULL); - - name = va_arg(ap, char *); - (void) i_fm_payload_set(ereport, name, ap); - - fm_ereport_post(ereport, EVCH_SLEEP); - fm_nvlist_destroy(ereport, FM_NVA_FREE); - fm_nvlist_destroy(detector, FM_NVA_FREE); - - return (0); -} - -static int -erpt_post_nosleep(dev_info_t *dip, struct i_ddi_fmhdl *fmhdl, - const char *error_class, uint64_t ena, uint8_t version, va_list ap) -{ - char *name; - char device_path[MAXPATHLEN]; - char ddi_error_class[ERPT_CLASS_SZ]; - nvlist_t *ereport, *detector; - nv_alloc_t *nva; - errorq_elem_t *eqep; - - eqep = errorq_reserve(fmhdl->fh_errorq); - if (eqep == NULL) - return (1); - - ereport = errorq_elem_nvl(fmhdl->fh_errorq, eqep); - nva = errorq_elem_nva(fmhdl->fh_errorq, eqep); - - ASSERT(ereport); - ASSERT(nva); - - /* - * Use the dev_path/devid for this device instance. - */ - detector = fm_nvlist_create(nva); - if (dip == ddi_root_node()) { - device_path[0] = '/'; - device_path[1] = '\0'; - } else { - (void) ddi_pathname(dip, device_path); - } - - fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, - device_path, NULL); - - if (ena == 0) - ena = fm_ena_generate(0, FM_ENA_FMT1); - - (void) snprintf(ddi_error_class, ERPT_CLASS_SZ, "%s.%s", - DDI_IO_CLASS, error_class); - - fm_ereport_set(ereport, version, ddi_error_class, - ena, detector, NULL); - - name = va_arg(ap, char *); - (void) i_fm_payload_set(ereport, name, ap); - - errorq_commit(fmhdl->fh_errorq, eqep, ERRORQ_ASYNC); - - return (0); -} - void i_ddi_drv_ereport_post(dev_info_t *dip, const char *error_class, nvlist_t *errp, int sflag) @@ -404,54 +291,198 @@ i_ddi_drv_ereport_post(dev_info_t *dip, const char *error_class, } /* - * Generate an error report for consumption by the Solaris Fault Manager, - * fmd(1M). Valid ereport classes are defined in /usr/include/sys/fm/io. The - * ENA should be set if this error is a result of an error status returned - * from ddi_dma_err_check() or ddi_acc_err_check(). Otherwise, an ENA - * value of 0 is appropriate. - * - * If sflag == DDI_NOSLEEP, ddi_fm_ereport_post () may be called - * from user, kernel, interrupt or high-interrupt context. Otherwise, - * ddi_fm_ereport_post() must be called from user or kernel context. + * fm_dev_ereport_postv: Common consolidation private interface to + * post a device tree oriented dev_scheme ereport. The device tree is + * composed of the following entities: devinfo nodes, minor nodes, and + * pathinfo nodes. All entities are associated with some devinfo node, + * either directly or indirectly. The intended devinfo node association + * for the ereport is communicated by the 'dip' argument. A minor node, + * an entity below 'dip', is represented by a non-null 'minor_name' + * argument. An application specific caller, like scsi_fm_ereport_post, + * can override the devinfo path with a pathinfo path via a non-null + * 'devpath' argument - in this case 'dip' is the MPXIO client node and + * devpath should be the path through the pHCI devinfo node to the + * pathinfo node. + * + * This interface also allows the caller to decide if the error being + * reported is know to be associated with a specific device identity + * via the 'devid' argument. The caller needs to control wether the + * devid appears as an authority in the FMRI because for some types of + * errors, like transport errors, the identity of the device on the + * other end of the transport is not guaranteed to be the current + * identity of the dip. For transport errors the caller should specify + * a NULL devid, even when there is a valid devid associated with the dip. + * + * The ddi_fm_ereport_post() implementation calls this interface with + * just a dip: devpath, minor_name, and devid are all NULL. The + * scsi_fm_ereport_post() implementation may call this interface with + * non-null devpath, minor_name, and devid arguments depending on + * wether MPXIO is enabled, and wether a transport or non-transport + * error is being posted. */ void -ddi_fm_ereport_post(dev_info_t *dip, const char *error_class, uint64_t ena, - int sflag, ...) +fm_dev_ereport_postv(dev_info_t *dip, dev_info_t *eqdip, + const char *devpath, const char *minor_name, const char *devid, + const char *error_class, uint64_t ena, int sflag, va_list ap) { - int ret; - char *name; - data_type_t type; - uint8_t version; - va_list ap; - struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl; + struct i_ddi_fmhdl *fmhdl; + errorq_elem_t *eqep; + nv_alloc_t *nva; + nvlist_t *ereport = NULL; + nvlist_t *detector = NULL; + char *name; + data_type_t type; + uint8_t version; + char class[ERPT_CLASS_SZ]; + char path[MAXPATHLEN]; + + ASSERT(dip && eqdip && error_class); - if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip))) - return; + /* + * This interface should be called with a fm_capable eqdip. The + * ddi_fm_ereport_post* interfaces call with eqdip == dip, + * ndi_fm_ereport_post* interfaces call with eqdip == ddi_parent(dip). + */ + if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(eqdip))) + goto err; - ASSERT(fmhdl); + /* get ereport nvlist handle */ + if ((sflag == DDI_SLEEP) && !panicstr) { + /* + * Driver defect - should not call with DDI_SLEEP while in + * interrupt context. + */ + if (servicing_interrupt()) { + i_ddi_drv_ereport_post(dip, DVR_ECONTEXT, NULL, sflag); + goto err; + } - va_start(ap, sflag); + /* Use normal interfaces to allocate memory. */ + if ((ereport = fm_nvlist_create(NULL)) == NULL) + goto err; + nva = NULL; + } else { + /* Use errorq interfaces to avoid memory allocation. */ + fmhdl = DEVI(eqdip)->devi_fmhdl; + ASSERT(fmhdl); + eqep = errorq_reserve(fmhdl->fh_errorq); + if (eqep == NULL) + goto err; + + ereport = errorq_elem_nvl(fmhdl->fh_errorq, eqep); + nva = errorq_elem_nva(fmhdl->fh_errorq, eqep); + ASSERT(nva); + } + ASSERT(ereport); - /* First payload tuple should be the version */ + /* + * Form parts of an ereport: + * A: version + * B: error_class + * C: ena + * D: detector (path and optional devid authority) + * E: payload + * + * A: ereport version: first payload tuple must be the version. + */ name = va_arg(ap, char *); type = va_arg(ap, data_type_t); version = va_arg(ap, uint_t); - if (strcmp(name, FM_VERSION) != 0 && type != DATA_TYPE_UINT8) { - va_end(ap); + if ((strcmp(name, FM_VERSION) != 0) || (type != DATA_TYPE_UINT8)) { i_ddi_drv_ereport_post(dip, DVR_EVER, NULL, sflag); - return; + goto err; + } + + /* B: ereport error_class: add "io." prefix to class. */ + (void) snprintf(class, ERPT_CLASS_SZ, "%s.%s", + DDI_IO_CLASS, error_class); + + /* C: ereport ena: if not passed in, generate new ena. */ + if (ena == 0) + ena = fm_ena_generate(0, FM_ENA_FMT1); + + /* D: detector: form dev scheme fmri with path and devid. */ + if (devpath) { + (void) strlcpy(path, devpath, sizeof (path)); + } else { + /* derive devpath from dip */ + if (dip == ddi_root_node()) + (void) strcpy(path, "/"); + else + (void) ddi_pathname(dip, path); + } + if (minor_name) { + (void) strlcat(path, ":", sizeof (path)); + (void) strlcat(path, minor_name, sizeof (path)); } + detector = fm_nvlist_create(nva); + fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, path, devid); + + /* Pull parts of ereport together into ereport. */ + fm_ereport_set(ereport, version, class, ena, detector, NULL); + + /* Add the payload to ereport. */ + name = va_arg(ap, char *); + (void) i_fm_payload_set(ereport, name, ap); - if (sflag == DDI_SLEEP) - ret = erpt_post_sleep(dip, error_class, ena, version, ap); + /* Post the ereport. */ + if (nva) + errorq_commit(fmhdl->fh_errorq, eqep, ERRORQ_ASYNC); else - ret = erpt_post_nosleep(dip, fmhdl, error_class, ena, version, - ap); - va_end(ap); + fm_ereport_post(ereport, EVCH_SLEEP); + goto out; - if (ret != 0) + /* Count errors as drops. */ +err: if (fmhdl) atomic_add_64(&fmhdl->fh_kstat.fek_erpt_dropped.value.ui64, 1); +out: if (ereport && (nva == NULL)) + fm_nvlist_destroy(ereport, FM_NVA_FREE); + if (detector && (nva == NULL)) + fm_nvlist_destroy(detector, FM_NVA_FREE); +} + +/* + * Generate an error report for consumption by the Solaris Fault Manager, + * fmd(1M). Valid ereport classes are defined in /usr/include/sys/fm/io. + * + * The ENA should be set if this error is a result of an error status + * returned from ddi_dma_err_check() or ddi_acc_err_check(). Otherwise, + * an ENA value of 0 is appropriate. + * + * If sflag == DDI_NOSLEEP, ddi_fm_ereport_post () may be called + * from user, kernel, interrupt or high-interrupt context. Otherwise, + * ddi_fm_ereport_post() must be called from user or kernel context. + * + * The ndi_interfaces are provided for use by nexus drivers to post + * ereports about children who may not themselves be fm_capable. + * + * All interfaces end up in the common fm_dev_ereport_postv code above. + */ +void +ddi_fm_ereport_post(dev_info_t *dip, + const char *error_class, uint64_t ena, int sflag, ...) +{ + va_list ap; + + ASSERT(dip && error_class); + va_start(ap, sflag); + fm_dev_ereport_postv(dip, dip, NULL, NULL, NULL, + error_class, ena, sflag, ap); + va_end(ap); +} + +void +ndi_fm_ereport_post(dev_info_t *dip, + const char *error_class, uint64_t ena, int sflag, ...) +{ + va_list ap; + + ASSERT(dip && error_class && (sflag == DDI_SLEEP)); + va_start(ap, sflag); + fm_dev_ereport_postv(dip, ddi_get_parent(dip), NULL, NULL, NULL, + error_class, ena, sflag, ap); + va_end(ap); } /* diff --git a/usr/src/uts/common/os/devcfg.c b/usr/src/uts/common/os/devcfg.c index ff837895fb..80492cb39c 100644 --- a/usr/src/uts/common/os/devcfg.c +++ b/usr/src/uts/common/os/devcfg.c @@ -401,6 +401,9 @@ i_ddi_free_node(dev_info_t *dip) if (devi->devi_hw_prop_ptr) i_ddi_prop_list_delete(devi->devi_hw_prop_ptr); + if (DEVI(dip)->devi_devid_str) + ddi_devid_str_free(DEVI(dip)->devi_devid_str); + i_ddi_set_node_state(dip, DS_INVAL); da_log_enter(dip); if (devi->devi_audit) { @@ -1211,8 +1214,7 @@ attach_node(dev_info_t *dip) if (DEVI(dip)->devi_flags & DEVI_REGISTERED_DEVID) { DEVI(dip)->devi_flags &= ~DEVI_REGISTERED_DEVID; mutex_exit(&DEVI(dip)->devi_lock); - - e_devid_cache_unregister(dip); + ddi_devid_unregister(dip); } else mutex_exit(&DEVI(dip)->devi_lock); @@ -1319,8 +1321,7 @@ detach_node(dev_info_t *dip, uint_t flag) if (DEVI(dip)->devi_flags & DEVI_REGISTERED_DEVID) { DEVI(dip)->devi_flags &= ~DEVI_REGISTERED_DEVID; mutex_exit(&DEVI(dip)->devi_lock); - - e_devid_cache_unregister(dip); + ddi_devid_unregister(dip); } else mutex_exit(&DEVI(dip)->devi_lock); diff --git a/usr/src/uts/common/os/modsubr.c b/usr/src/uts/common/os/modsubr.c index 19954a6cf8..31d5501999 100644 --- a/usr/src/uts/common/os/modsubr.c +++ b/usr/src/uts/common/os/modsubr.c @@ -400,6 +400,9 @@ impl_make_parlist(major_t major) if (i_ddi_prop_search(DDI_DEV_T_ANY, DDI_OPEN_RETURNS_EINTR, DDI_PROP_TYPE_INT, &props)) dnp->dn_flags |= DN_OPEN_RETURNS_EINTR; + if (i_ddi_prop_search(DDI_DEV_T_ANY, "scsi-size-clean", + DDI_PROP_TYPE_INT, &props)) + dnp->dn_flags |= DN_SCSI_SIZE_CLEAN; } if (i_ddi_prop_search(DDI_DEV_T_ANY, DDI_VHCI_CLASS, diff --git a/usr/src/uts/common/os/sunddi.c b/usr/src/uts/common/os/sunddi.c index e919e8b846..8619288779 100644 --- a/usr/src/uts/common/os/sunddi.c +++ b/usr/src/uts/common/os/sunddi.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -7559,15 +7559,10 @@ i_ddi_devid_register(dev_info_t *dip, ddi_devid_t devid) return (DDI_FAILURE); } - ddi_devid_str_free(devid_str); - -#ifdef DEVID_COMPATIBILITY - /* - * marker for devinfo snapshot compatibility. - * This code gets deleted when di_devid is gone from libdevid - */ - DEVI(dip)->devi_devid = DEVID_COMPATIBILITY; -#endif /* DEVID_COMPATIBILITY */ + /* keep pointer to devid string for interrupt context fma code */ + if (DEVI(dip)->devi_devid_str) + ddi_devid_str_free(DEVI(dip)->devi_devid_str); + DEVI(dip)->devi_devid_str = devid_str; return (DDI_SUCCESS); } @@ -7603,13 +7598,10 @@ ddi_devid_register(dev_info_t *dip, ddi_devid_t devid) static void i_ddi_devid_unregister(dev_info_t *dip) { -#ifdef DEVID_COMPATIBILITY - /* - * marker for micro release devinfo snapshot compatibility. - * This code gets deleted for the minor release. - */ - DEVI(dip)->devi_devid = NULL; /* unset DEVID_PROP */ -#endif /* DEVID_COMPATIBILITY */ + if (DEVI(dip)->devi_devid_str) { + ddi_devid_str_free(DEVI(dip)->devi_devid_str); + DEVI(dip)->devi_devid_str = NULL; + } /* remove the devid property */ (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, DEVID_PROP_NAME); diff --git a/usr/src/uts/common/os/sunmdi.c b/usr/src/uts/common/os/sunmdi.c index d83be4e8c5..91e00cf82e 100644 --- a/usr/src/uts/common/os/sunmdi.c +++ b/usr/src/uts/common/os/sunmdi.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" @@ -157,6 +157,19 @@ static int mdi_bus_config_cache_hash_size = 256; static int mdi_mtc_off = 0; /* + * The "path" to a pathinfo node is identical to the /devices path to a + * devinfo node had the device been enumerated under a pHCI instead of + * a vHCI. This pathinfo "path" is associated with a 'path_instance'. + * This association persists across create/delete of the pathinfo nodes, + * but not across reboot. + */ +static uint_t mdi_pathmap_instance = 1; /* 0 -> any path */ +static int mdi_pathmap_hash_size = 256; +static kmutex_t mdi_pathmap_mutex; +static mod_hash_t *mdi_pathmap_bypath; /* "path"->instance */ +static mod_hash_t *mdi_pathmap_byinstance; /* instance->"path" */ + +/* * MDI component property name/value string definitions */ const char *mdi_component_prop = "mpxio-component"; @@ -306,13 +319,21 @@ i_mdi_init() initialized = 1; mutex_init(&mdi_mutex, NULL, MUTEX_DEFAULT, NULL); - /* - * Create our taskq resources - */ + + /* Create our taskq resources */ mdi_taskq = taskq_create("mdi_taskq", mdi_taskq_n_threads, MDI_TASKQ_PRI, MDI_TASKQ_MINALLOC, MDI_TASKQ_MAXALLOC, TASKQ_PREPOPULATE | TASKQ_CPR_SAFE); ASSERT(mdi_taskq != NULL); /* taskq_create never fails */ + + /* Allocate ['path_instance' <-> "path"] maps */ + mutex_init(&mdi_pathmap_mutex, NULL, MUTEX_DRIVER, NULL); + mdi_pathmap_bypath = mod_hash_create_strhash( + "mdi_pathmap_bypath", mdi_pathmap_hash_size, + mod_hash_null_valdtor); + mdi_pathmap_byinstance = mod_hash_create_idhash( + "mdi_pathmap_byinstance", mdi_pathmap_hash_size, + mod_hash_null_valdtor); } /* @@ -1915,11 +1936,15 @@ i_mdi_lba_lb(mdi_client_t *ct, mdi_pathinfo_t **ret_pip, struct buf *bp) * through vHCI drivers configuration file (driver.conf). * * vHCI drivers may override this default behavior by specifying - * appropriate flags. If start_pip is specified (non NULL) is - * used as start point to walk and find the next appropriate path. - * The following values are currently defined: - * MDI_SELECT_ONLINE_PATH (to select an ONLINE path) and/or - * MDI_SELECT_STANDBY_PATH (to select an STANDBY path). + * appropriate flags. The meaning of the thrid argument depends + * on the flags specified. If MDI_SELECT_PATH_INSTANCE is set + * then the argument is the "path instance" of the path to select. + * If MDI_SELECT_PATH_INSTANCE is not set then the argument is + * "start_pip". A non NULL "start_pip" is the starting point to + * walk and find the next appropriate path. The following values + * are currently defined: MDI_SELECT_ONLINE_PATH (to select an + * ONLINE path) and/or MDI_SELECT_STANDBY_PATH (to select an + * STANDBY path). * * The non-standard behavior is used by the scsi_vhci driver, * whenever it has to use a STANDBY/FAULTED path. Eg. during @@ -1945,7 +1970,7 @@ i_mdi_lba_lb(mdi_client_t *ct, mdi_pathinfo_t **ret_pip, struct buf *bp) /*ARGSUSED*/ int mdi_select_path(dev_info_t *cdip, struct buf *bp, int flags, - mdi_pathinfo_t *start_pip, mdi_pathinfo_t **ret_pip) + void *arg, mdi_pathinfo_t **ret_pip) { mdi_client_t *ct; mdi_pathinfo_t *pip; @@ -1957,6 +1982,18 @@ mdi_select_path(dev_info_t *cdip, struct buf *bp, int flags, int preferred = 1; /* preferred path */ int cond, cont = 1; int retry = 0; + mdi_pathinfo_t *start_pip; /* request starting pathinfo */ + int path_instance; /* request specific path instance */ + + /* determine type of arg based on flags */ + if (flags & MDI_SELECT_PATH_INSTANCE) { + flags &= ~MDI_SELECT_PATH_INSTANCE; + path_instance = (int)(intptr_t)arg; + start_pip = NULL; + } else { + path_instance = 0; + start_pip = (mdi_pathinfo_t *)arg; + } if (flags != 0) { /* @@ -2021,6 +2058,40 @@ mdi_select_path(dev_info_t *cdip, struct buf *bp, int flags, return (MDI_NOPATH); } + /* Caller is specifying a specific pathinfo path by path_instance */ + if (path_instance) { + /* search for pathinfo with correct path_instance */ + for (pip = head; + pip && (mdi_pi_get_path_instance(pip) != path_instance); + pip = (mdi_pathinfo_t *)MDI_PI(pip)->pi_client_link) + ; + + /* If path can't be selected then MDI_FAILURE is returned. */ + if (pip == NULL) { + MDI_CLIENT_UNLOCK(ct); + return (MDI_FAILURE); + } + + /* verify state of path */ + MDI_PI_LOCK(pip); + if (MDI_PI(pip)->pi_state != MDI_PATHINFO_STATE_ONLINE) { + MDI_PI_UNLOCK(pip); + MDI_CLIENT_UNLOCK(ct); + return (MDI_FAILURE); + } + + /* + * Return the path in hold state. Caller should release the + * lock by calling mdi_rele_path() + */ + MDI_PI_HOLD(pip); + MDI_PI_UNLOCK(pip); + ct->ct_path_last = pip; + *ret_pip = pip; + MDI_CLIENT_UNLOCK(ct); + return (MDI_SUCCESS); + } + /* * for non default behavior, bypass current * load balancing policy and always use LOAD_BALANCE_RR @@ -2753,8 +2824,12 @@ i_mdi_pi_alloc(mdi_phci_t *ph, char *paddr, mdi_client_t *ct) mdi_pathinfo_t *pip; int ct_circular; int ph_circular; + static char path[MAXPATHLEN]; + char *path_persistent; + int path_instance; int se_flag; int kmem_flag; + mod_hash_val_t hv; ASSERT(MDI_VHCI_CLIENT_LOCKED(ph->ph_vhci)); @@ -2778,6 +2853,36 @@ i_mdi_pi_alloc(mdi_phci_t *ph, char *paddr, mdi_client_t *ct) MDI_PI(pip)->pi_phci = ph; MDI_PI(pip)->pi_addr = kmem_alloc(strlen(paddr) + 1, KM_SLEEP); (void) strcpy(MDI_PI(pip)->pi_addr, paddr); + + /* + * We form the "path" to the pathinfo node, and see if we have + * already allocated a 'path_instance' for that "path". If so, + * we use the already allocated 'path_instance'. If not, we + * allocate a new 'path_instance' and associate it with a copy of + * the "path" string (which is never freed). The association + * between a 'path_instance' this "path" string persists until + * reboot. + */ + mutex_enter(&mdi_pathmap_mutex); + (void) ddi_pathname(ph->ph_dip, path); + (void) sprintf(path + strlen(path), "/%s@%s", + ddi_node_name(ct->ct_dip), MDI_PI(pip)->pi_addr); + if (mod_hash_find(mdi_pathmap_bypath, (mod_hash_key_t)path, &hv) == 0) { + path_instance = (uint_t)(intptr_t)hv; + } else { + /* allocate a new 'path_instance' and persistent "path" */ + path_instance = mdi_pathmap_instance++; + path_persistent = i_ddi_strdup(path, KM_SLEEP); + (void) mod_hash_insert(mdi_pathmap_bypath, + (mod_hash_key_t)path_persistent, + (mod_hash_val_t)(intptr_t)path_instance); + (void) mod_hash_insert(mdi_pathmap_byinstance, + (mod_hash_key_t)(intptr_t)path_instance, + (mod_hash_val_t)path_persistent); + } + mutex_exit(&mdi_pathmap_mutex); + MDI_PI(pip)->pi_path_instance = path_instance; + (void) nvlist_alloc(&MDI_PI(pip)->pi_prop, NV_UNIQUE_NAME, KM_SLEEP); ASSERT(MDI_PI(pip)->pi_prop != NULL); MDI_PI(pip)->pi_pprivate = NULL; @@ -2817,6 +2922,28 @@ i_mdi_pi_alloc(mdi_phci_t *ph, char *paddr, mdi_client_t *ct) } /* + * mdi_pi_pathname_by_instance(): + * Lookup of "path" by 'path_instance'. Return "path". + * NOTE: returned "path" remains valid forever (until reboot). + */ +char * +mdi_pi_pathname_by_instance(int path_instance) +{ + char *path; + mod_hash_val_t hv; + + /* mdi_pathmap lookup of "path" by 'path_instance' */ + mutex_enter(&mdi_pathmap_mutex); + if (mod_hash_find(mdi_pathmap_byinstance, + (mod_hash_key_t)(intptr_t)path_instance, &hv) == 0) + path = (char *)hv; + else + path = NULL; + mutex_exit(&mdi_pathmap_mutex); + return (path); +} + +/* * i_mdi_phci_add_path(): * Add a mdi_pathinfo node to pHCI list. * Notes: @@ -3725,6 +3852,34 @@ mdi_pi_get_addr(mdi_pathinfo_t *pip) } /* + * mdi_pi_get_path_instance(): + * Get the 'path_instance' of a mdi_pathinfo node + * + * Return Values: + * path_instance + */ +int +mdi_pi_get_path_instance(mdi_pathinfo_t *pip) +{ + if (pip == NULL) + return (0); + + return (MDI_PI(pip)->pi_path_instance); +} + +/* + * mdi_pi_pathname(): + * Return pointer to path to pathinfo node. + */ +char * +mdi_pi_pathname(mdi_pathinfo_t *pip) +{ + if (pip == NULL) + return (NULL); + return (mdi_pi_pathname_by_instance(mdi_pi_get_path_instance(pip))); +} + +/* * mdi_pi_get_client(): * Get the client devinfo associated with a mdi_pathinfo node * diff --git a/usr/src/uts/common/sys/1394/targets/scsa1394/cmd.h b/usr/src/uts/common/sys/1394/targets/scsa1394/cmd.h index b26c546f9f..f13059169e 100644 --- a/usr/src/uts/common/sys/1394/targets/scsa1394/cmd.h +++ b/usr/src/uts/common/sys/1394/targets/scsa1394/cmd.h @@ -2,9 +2,8 @@ * 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. + * 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. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -111,8 +110,12 @@ typedef struct scsa1394_cmd { size_t sc_total_blks; /* total xfer blocks */ size_t sc_resid_blks; /* blocks left */ - struct scsi_pkt sc_scsi_pkt; /* embedded SCSI packet */ + struct scsi_pkt sc_scsi_pkt; /* must be last */ + /* embedded SCSI packet */ + /* ... scsi_pkt_size() */ } scsa1394_cmd_t; +#define SCSA1394_CMD_SIZE (sizeof (struct scsa1394_cmd) - \ + sizeof (struct scsi_pkt) + scsi_pkt_size()) _NOTE(SCHEME_PROTECTS_DATA("unique per task", { scsa1394_cmd scsa1394_cmd_seg scsi_pkt scsi_inquiry scsi_extended_sense scsi_cdb scsi_arq_status })) diff --git a/usr/src/uts/common/sys/autoconf.h b/usr/src/uts/common/sys/autoconf.h index e7fbd33267..dcf45a48e0 100644 --- a/usr/src/uts/common/sys/autoconf.h +++ b/usr/src/uts/common/sys/autoconf.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -86,6 +86,7 @@ struct devnames { #define DN_GLDV3_DRIVER 0x1000 /* gldv3 (Nemo) driver */ #define DN_PHCI_DRIVER 0x2000 /* pHCI driver */ #define DN_OPEN_RETURNS_EINTR 0x4000 /* DDI_OPEN_RETURNS_EINTR prop */ +#define DN_SCSI_SIZE_CLEAN 0x8000 /* driver is scsi_size_clean() */ #ifdef _KERNEL diff --git a/usr/src/uts/common/sys/ddi_impldefs.h b/usr/src/uts/common/sys/ddi_impldefs.h index 2cc473236c..d1c31b318f 100644 --- a/usr/src/uts/common/sys/ddi_impldefs.h +++ b/usr/src/uts/common/sys/ddi_impldefs.h @@ -143,9 +143,8 @@ struct dev_info { int (*devi_bus_dma_unbindfunc)(dev_info_t *, dev_info_t *, ddi_dma_handle_t); -#ifdef DEVID_COMPATIBILITY - ddi_devid_t devi_devid; /* registered device id */ -#endif /* DEVID_COMPATIBILITY */ + char *devi_devid_str; /* registered device id */ + /* * power management entries * components exist even if the device is not currently power managed @@ -210,6 +209,7 @@ struct dev_info { char *devi_addr_buf; /* buffer for devi_addr */ char *devi_rebinding_name; /* binding_name of rebind */ + /* For device contracts that have this dip's minor node as resource */ kmutex_t devi_ct_lock; /* contract lock */ kcondvar_t devi_ct_cv; /* contract cv */ diff --git a/usr/src/uts/common/sys/ddifm.h b/usr/src/uts/common/sys/ddifm.h index 9fe6036d61..07fc00754f 100644 --- a/usr/src/uts/common/sys/ddifm.h +++ b/usr/src/uts/common/sys/ddifm.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -33,6 +33,7 @@ extern "C" { #endif #include <sys/dditypes.h> +#include <sys/va_list.h> extern int ddi_system_fmcap; @@ -89,10 +90,15 @@ typedef int (*ddi_err_func_t)(dev_info_t *, ddi_fm_error_t *, const void *); * DDI for error handling and ereport generation */ -/* ereporting and service changes */ +/* + * ereport generation: [ddi|ndi]_fm_ereport_post + */ extern void ddi_fm_ereport_post(dev_info_t *, const char *, uint64_t, int, ...); +extern void ndi_fm_ereport_post(dev_info_t *, const char *, uint64_t, int, ...); /* + * service changes: + * * After a hardened driver raises an ereport (or after pci_ereport_post() has * raised an ereport for an event which implecated one of a driver's access or * dma handles), the driver should always determine the service impact and diff --git a/usr/src/uts/common/sys/devinfo_impl.h b/usr/src/uts/common/sys/devinfo_impl.h index 1b78a5412c..5cc9705bad 100644 --- a/usr/src/uts/common/sys/devinfo_impl.h +++ b/usr/src/uts/common/sys/devinfo_impl.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -53,9 +53,9 @@ extern "C" { #define DINFOSUBTREE (DIIOC | 0x01) /* include subtree */ #define DINFOMINOR (DIIOC | 0x02) /* include minor data */ #define DINFOPROP (DIIOC | 0x04) /* include properties */ +#define DINFOPATH (DIIOC | 0x08) /* include i/o pathing information */ /* private bits */ -#define DINFOPATH (DIIOC | 0x08) /* include i/o pathing information */ #define DINFOPRIVDATA (DIIOC | 0x10) /* include private data */ #define DINFOFORCE (DIIOC | 0x20) /* force load all drivers */ #define DINFOCACHE (DIIOC | 0x100000) /* use cached data */ @@ -322,7 +322,9 @@ struct di_path { di_off_t path_prop; /* property list */ di_off_t path_addr; /* path addressing information */ di_path_state_t path_state; /* path state */ - uint_t path_snap_state; /* describes valid fields */ + uint_t path_snap_state; /* describes valid fields */ + int path_instance; /* path instance */ + uint64_t user_private_data; }; /* diff --git a/usr/src/uts/common/sys/mdi_impldefs.h b/usr/src/uts/common/sys/mdi_impldefs.h index 4838c1ca7b..4d913bb0c5 100644 --- a/usr/src/uts/common/sys/mdi_impldefs.h +++ b/usr/src/uts/common/sys/mdi_impldefs.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -712,6 +712,7 @@ struct mdi_pathinfo { /* protected by MDI_VHCI_CLIENT_LOCK vh_client_mutex... */ char *pi_addr; /* path unit address */ + int pi_path_instance; /* path instance */ /* protected by MDI_PI_LOCK pi_mutex... */ kmutex_t pi_mutex; /* per path mutex */ @@ -1123,19 +1124,23 @@ dev_info_t *mdi_phci_path2devinfo(dev_info_t *, caddr_t); * starting point; it starts walking from there to find the next appropriate * path. * - * The following values for 'flags' are currently defined: + * The following values for 'flags' are currently defined, the third argument + * to mdi_select_path depends on the flags used. * - * MDI_SELECT_ONLINE_PATH: select an ONLINE path - * MDI_SELECT_STANDBY_PATH: select a STANDBY path - * MDI_SELECT_USER_DISABLE_PATH: select user disable for failover and - * auto_failback + * <none>: default, arg is pip + * MDI_SELECT_ONLINE_PATH: select an ONLINE path, arg is pip + * MDI_SELECT_STANDBY_PATH: select a STANDBY path, arg is pip + * MDI_SELECT_USER_DISABLE_PATH: select user disable for failover and + * auto_failback + * MDI_SELECT_PATH_INSTANCE: select a specific path, arg is + * path instance * * The selected paths are returned in an mdi_hold_path() state (pi_ref_cnt), * caller should release the hold by calling mdi_rele_path() at the end of * operation. */ int mdi_select_path(dev_info_t *, struct buf *, int, - mdi_pathinfo_t *, mdi_pathinfo_t **); + void *, mdi_pathinfo_t **); int mdi_set_lb_policy(dev_info_t *, client_lb_t); int mdi_set_lb_region_size(dev_info_t *, int); client_lb_t mdi_get_lb_policy(dev_info_t *); @@ -1146,6 +1151,7 @@ client_lb_t mdi_get_lb_policy(dev_info_t *); #define MDI_SELECT_ONLINE_PATH 0x0001 #define MDI_SELECT_STANDBY_PATH 0x0002 #define MDI_SELECT_USER_DISABLE_PATH 0x0004 +#define MDI_SELECT_PATH_INSTANCE 0x0008 /* * MDI client device utility functions diff --git a/usr/src/uts/common/sys/nvpair.h b/usr/src/uts/common/sys/nvpair.h index 0675d1365a..701b8370a2 100644 --- a/usr/src/uts/common/sys/nvpair.h +++ b/usr/src/uts/common/sys/nvpair.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -220,15 +220,18 @@ int nvlist_lookup_string_array(nvlist_t *, const char *, char ***, uint_t *); int nvlist_lookup_nvlist_array(nvlist_t *, const char *, nvlist_t ***, uint_t *); int nvlist_lookup_hrtime(nvlist_t *, const char *, hrtime_t *); -int nvlist_lookup_pairs(nvlist_t *nvl, int, ...); +int nvlist_lookup_pairs(nvlist_t *, int, ...); -int nvlist_lookup_nvpair(nvlist_t *nvl, const char *, nvpair_t **); -boolean_t nvlist_exists(nvlist_t *nvl, const char *); +int nvlist_lookup_nvpair(nvlist_t *, const char *, nvpair_t **); +int nvlist_lookup_nvpair_embedded_index(nvlist_t *, const char *, nvpair_t **, + int *, char **); +boolean_t nvlist_exists(nvlist_t *, const char *); /* processing nvpair */ -nvpair_t *nvlist_next_nvpair(nvlist_t *nvl, nvpair_t *); +nvpair_t *nvlist_next_nvpair(nvlist_t *, nvpair_t *); char *nvpair_name(nvpair_t *); data_type_t nvpair_type(nvpair_t *); +int nvpair_type_is_array(nvpair_t *); int nvpair_value_boolean_value(nvpair_t *, boolean_t *); int nvpair_value_byte(nvpair_t *, uchar_t *); int nvpair_value_int8(nvpair_t *, int8_t *); diff --git a/usr/src/uts/common/sys/scsi/conf/autoconf.h b/usr/src/uts/common/sys/scsi/conf/autoconf.h index 4d382e8445..757732cb09 100644 --- a/usr/src/uts/common/sys/scsi/conf/autoconf.h +++ b/usr/src/uts/common/sys/scsi/conf/autoconf.h @@ -2,9 +2,8 @@ * 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. + * 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. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -167,6 +166,7 @@ extern int scsi_tag_age_limit; extern int scsi_watchdog_tick; extern int scsi_selection_timeout; /* specified in milli seconds */ extern int scsi_host_id; +extern int scsi_fm_capable; #endif /* _KERNEL */ #ifdef __cplusplus diff --git a/usr/src/uts/common/sys/scsi/conf/device.h b/usr/src/uts/common/sys/scsi/conf/device.h index 1f281b797c..9e68a0d675 100644 --- a/usr/src/uts/common/sys/scsi/conf/device.h +++ b/usr/src/uts/common/sys/scsi/conf/device.h @@ -2,9 +2,8 @@ * 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. + * 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. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -99,6 +98,22 @@ struct scsi_device { * attach(9E), and freed in the target driver detach(9E). */ caddr_t sd_private; + + + /* + * FMA capabilities of scsi_device. + */ + int sd_fm_capable; + +#ifdef SCSI_SIZE_CLEAN_VERIFY + /* + * Must be last: Building a driver with-and-without + * -DSCSI_SIZE_CLEAN_VERIFY, and checking driver modules for + * differences with a tools like 'wsdiff' allows a developer to verify + * that their driver has no dependencies on scsi*(9S) size. + */ + int _pad[8]; +#endif /* SCSI_SIZE_CLEAN_VERIFY */ }; #ifdef _KERNEL @@ -106,6 +121,7 @@ int scsi_slave(struct scsi_device *devp, int (*callback)(void)); int scsi_probe(struct scsi_device *devp, int (*callback)(void)); void scsi_unslave(struct scsi_device *devp); void scsi_unprobe(struct scsi_device *devp); +size_t scsi_device_size(); /* private */ #endif /* _KERNEL */ #ifdef __cplusplus diff --git a/usr/src/uts/common/sys/scsi/impl/transport.h b/usr/src/uts/common/sys/scsi/impl/transport.h index fb1e2c89cd..23c74392a3 100644 --- a/usr/src/uts/common/sys/scsi/impl/transport.h +++ b/usr/src/uts/common/sys/scsi/impl/transport.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -254,6 +254,8 @@ struct scsi_hba_tran { * usr/src/uts/common/sys/scsi/impl/services.h */ int tran_interconnect_type; + + /* tran_setup_pkt(9E) related scsi_pkt fields */ int (*tran_pkt_constructor)( struct scsi_pkt *pkt, scsi_hba_tran_t *tran, @@ -262,7 +264,6 @@ struct scsi_hba_tran { struct scsi_pkt *pkt, scsi_hba_tran_t *tran); kmem_cache_t *tran_pkt_cache_ptr; - uint_t tran_hba_len; int (*tran_setup_pkt)( struct scsi_pkt *pkt, @@ -274,7 +275,24 @@ struct scsi_hba_tran { ddi_dma_attr_t tran_dma_attr; void *tran_extension; + /* + * An fm_capable HBA driver can set tran_fm_capable prior to + * scsi_hba_attach_setup(). If not set, SCSA provides a default + * implementation. + */ + int tran_fm_capable; + +#ifdef SCSI_SIZE_CLEAN_VERIFY + /* + * Must be last: Building a driver with-and-without + * -DSCSI_SIZE_CLEAN_VERIFY, and checking driver modules for + * differences with a tools like 'wsdiff' allows a developer to verify + * that their driver has no dependencies on scsi*(9S) size. + */ + int _pad[8]; +#endif /* SCSI_SIZE_CLEAN_VERIFY */ }; +size_t scsi_hba_tran_size(); /* private */ #ifdef __lock_lint @@ -425,6 +443,7 @@ extern int scsi_hba_prop_update_inqstring( /* is called */ #define SCSI_HBA_TRAN_CDB 0x04 /* allocate cdb */ #define SCSI_HBA_TRAN_SCB 0x08 /* allocate sense */ +#define SCSI_HBA_TRAN_FMSCSA 0x10 /* using common ddi_fm_* */ /* * Flags for scsi_hba allocation functions @@ -452,7 +471,6 @@ extern int scsi_hba_prop_update_inqstring( #define INST2SCSI(x) (((x) << INST_MINOR_SHIFT) | SCSI_MINOR) #define MINOR2INST(x) ((x) >> INST_MINOR_SHIFT) - #endif /* _KERNEL */ diff --git a/usr/src/uts/common/sys/scsi/impl/uscsi.h b/usr/src/uts/common/sys/scsi/impl/uscsi.h index cb70632204..80e424d6aa 100644 --- a/usr/src/uts/common/sys/scsi/impl/uscsi.h +++ b/usr/src/uts/common/sys/scsi/impl/uscsi.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -52,7 +52,7 @@ struct uscsi_cmd { uchar_t uscsi_rqstatus; /* status of request sense cmd */ uchar_t uscsi_rqresid; /* resid of request sense cmd */ caddr_t uscsi_rqbuf; /* request sense buffer */ - void *uscsi_reserved_5; /* Reserved for Future Use */ + ulong_t uscsi_path_instance; /* private: hardware path */ }; #if defined(_SYSCALL32) @@ -69,7 +69,7 @@ struct uscsi_cmd32 { uchar_t uscsi_rqstatus; /* status of request sense cmd */ uchar_t uscsi_rqresid; /* resid of request sense cmd */ caddr32_t uscsi_rqbuf; /* request sense buffer */ - caddr32_t uscsi_reserved_5; /* Reserved for Future Use */ + uint32_t uscsi_path_instance; /* private: hardware path */ }; #define uscsi_cmd32touscsi_cmd(u32, ucmd) \ @@ -85,7 +85,7 @@ struct uscsi_cmd32 { ucmd->uscsi_rqstatus = u32->uscsi_rqstatus; \ ucmd->uscsi_rqresid = u32->uscsi_rqresid; \ ucmd->uscsi_rqbuf = (caddr_t)(uintptr_t)u32->uscsi_rqbuf; \ - ucmd->uscsi_reserved_5 = (void *)(uintptr_t)u32->uscsi_reserved_5; + ucmd->uscsi_path_instance = (ulong_t)u32->uscsi_path_instance; #define uscsi_cmdtouscsi_cmd32(ucmd, u32) \ @@ -101,7 +101,7 @@ struct uscsi_cmd32 { u32->uscsi_rqstatus = ucmd->uscsi_rqstatus; \ u32->uscsi_rqresid = ucmd->uscsi_rqresid; \ u32->uscsi_rqbuf = (caddr32_t)(uintptr_t)ucmd->uscsi_rqbuf; \ - u32->uscsi_reserved_5 = (caddr32_t)(uintptr_t)ucmd->uscsi_reserved_5; + u32->uscsi_path_instance = (uint32_t)ucmd->uscsi_path_instance; #endif /* _SYSCALL32 */ @@ -112,23 +112,27 @@ struct uscsi_cmd32 { /* * generic flags */ -#define USCSI_WRITE 0x00000 /* send data to device */ -#define USCSI_SILENT 0x00001 /* no error messages */ -#define USCSI_DIAGNOSE 0x00002 /* fail if any error occurs */ -#define USCSI_ISOLATE 0x00004 /* isolate from normal commands */ -#define USCSI_READ 0x00008 /* get data from device */ -#define USCSI_RESET_LUN 0x40000 /* Reset logical unit */ -#define USCSI_RESET 0x04000 /* Reset target */ -#define USCSI_RESET_TARGET USCSI_RESET /* Reset target */ -#define USCSI_RESET_ALL 0x08000 /* Reset all targets */ -#define USCSI_RQENABLE 0x10000 /* Enable Request Sense extensions */ -#define USCSI_RENEGOT 0x20000 /* renegotiate wide/sync on next I/O */ +#define USCSI_SILENT 0x00000001 /* no error messages */ +#define USCSI_DIAGNOSE 0x00000002 /* fail if any error occurs */ +#define USCSI_ISOLATE 0x00000004 /* isolate from normal commands */ +#define USCSI_READ 0x00000008 /* get data from device */ +#define USCSI_WRITE 0x00000000 /* send data to device */ + +#define USCSI_RESET 0x00004000 /* Reset target */ +#define USCSI_RESET_TARGET \ + USCSI_RESET /* Reset target */ +#define USCSI_RESET_ALL 0x00008000 /* Reset all targets */ +#define USCSI_RQENABLE 0x00010000 /* Enable Request Sense extensions */ +#define USCSI_RENEGOT 0x00020000 /* renegotiate wide/sync on next I/O */ +#define USCSI_RESET_LUN 0x00040000 /* Reset logical unit */ +#define USCSI_PATH_INSTANCE \ + 0x00080000 /* use path instance for transport */ /* * suitable for parallel SCSI bus only */ -#define USCSI_ASYNC 0x01000 /* Set bus to asynchronous mode */ -#define USCSI_SYNC 0x02000 /* Return bus to sync mode if possible */ +#define USCSI_ASYNC 0x00001000 /* Set bus to asynchronous mode */ +#define USCSI_SYNC 0x00002000 /* Set bus to sync mode if possible */ /* * the following flags should not be used at user level but may @@ -137,20 +141,20 @@ struct uscsi_cmd32 { /* * generic flags */ -#define USCSI_NOINTR 0x00040 /* No interrupts, NEVER to use this flag */ -#define USCSI_NOTAG 0x00100 /* Disable tagged queueing */ -#define USCSI_OTAG 0x00200 /* ORDERED QUEUE tagged cmd */ -#define USCSI_HTAG 0x00400 /* HEAD OF QUEUE tagged cmd */ -#define USCSI_HEAD 0x00800 /* Head of HA queue */ +#define USCSI_NOINTR 0x00000040 /* No interrupts, NEVER use this flag */ +#define USCSI_NOTAG 0x00000100 /* Disable tagged queueing */ +#define USCSI_OTAG 0x00000200 /* ORDERED QUEUE tagged cmd */ +#define USCSI_HTAG 0x00000400 /* HEAD OF QUEUE tagged cmd */ +#define USCSI_HEAD 0x00000800 /* Head of HA queue */ /* * suitable for parallel SCSI bus only */ -#define USCSI_NOPARITY 0x00010 /* run command without parity */ -#define USCSI_NODISCON 0x00020 /* run command without disconnects */ +#define USCSI_NOPARITY 0x00000010 /* run command without parity */ +#define USCSI_NODISCON 0x00000020 /* run command without disconnects */ -#define USCSI_RESERVED 0xfffc0000 /* Reserved Bits, must be zero */ +#define USCSI_RESERVED 0xfff00000 /* Reserved Bits, must be zero */ struct uscsi_rqs { int rqs_flags; /* see below */ @@ -188,9 +192,11 @@ struct uscsi_rqs32 { int scsi_uscsi_alloc_and_copyin(intptr_t, int, struct scsi_address *, struct uscsi_cmd **); +int scsi_uscsi_pktinit(struct uscsi_cmd *, struct scsi_pkt *); int scsi_uscsi_handle_cmd(dev_t, enum uio_seg, struct uscsi_cmd *, int (*)(struct buf *), struct buf *, void *); +int scsi_uscsi_pktfini(struct scsi_pkt *, struct uscsi_cmd *); int scsi_uscsi_copyout_and_free(intptr_t, struct uscsi_cmd *); #endif /* _KERNEL */ diff --git a/usr/src/uts/common/sys/scsi/scsi_pkt.h b/usr/src/uts/common/sys/scsi/scsi_pkt.h index 9196cc7b28..3a8b533e88 100644 --- a/usr/src/uts/common/sys/scsi/scsi_pkt.h +++ b/usr/src/uts/common/sys/scsi/scsi_pkt.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -61,15 +61,60 @@ struct scsi_pkt { uint_t pkt_state; /* state of command */ uint_t pkt_statistics; /* statistics */ uchar_t pkt_reason; /* reason completion called */ - uint_t pkt_cdblen; - uint_t pkt_tgtlen; - uint_t pkt_scblen; - ddi_dma_handle_t pkt_handle; - uint_t pkt_numcookies; - off_t pkt_dma_offset; - size_t pkt_dma_len; - uint_t pkt_dma_flags; - ddi_dma_cookie_t *pkt_cookies; + + /* + * The DDI does not allow a driver to allocate it's own scsi_pkt(9S), + * a driver should not have *any* compiled in dependencies on + * "sizeof (struct scsi_pkt)". If the driver has such dependencies, it + * limits SCSA's ability to evolve. The proper way for an HBA to + * allocate a scsi_pkt is via scsi_hba_pkt_alloc(9F), or the newer + * tran_setup_pkt(9E) interfaces. Allocation rules have been in place + * for many years, unfortunately a significant number of drivers + * are still broken. + * + * NB: Until we can trust drivers to follow DDI scsi_pkt(9S) allocation + * rules, access to all fields below require special consideration. + * Access to these fields is limited to code paths that 'know' correct + * scsi_pkt allocation interfaces must have been used. This means that + * any target driver access to these fields is suspect, since a target + * knows nothing about how an HBA drivers performs scsi_pkt allocation. + * + * NB: A private scsi_pkt_size() interface has been added to simplify + * 'fixing' legacy HBA drivers. Use of scsi_pkt_size() is only + * appropriate when the effort/cost of fixing a legacy driver to use + * proper DDI scsi_pkt allocation interfaces is too great given the + * remaining driver life. New HBA drivers should *not* use + * scsi_pkt_size(). + * + * NB: While HBA drivers with violations are being fixed, in + * rare cases access conditioned by scsi_pkt_allocated_correctly() is + * permitted. + */ + /* HBA driver only, iff scsi_hba_pkt_alloc(9F)|tran_seup_pkt(9E) used */ + uint_t pkt_cdblen; /* length of pkt_cdbp */ + uint_t pkt_tgtlen; /* length of pkt_private */ + uint_t pkt_scblen; /* lenght of pkt_scbp */ + + /* HBA driver only, iff tran_seup_pkt(9E) used */ + ddi_dma_handle_t pkt_handle; /* private */ + uint_t pkt_numcookies; /* number of DMA cookies */ + off_t pkt_dma_offset; /* private */ + size_t pkt_dma_len; /* private */ + uint_t pkt_dma_flags; /* DMA flags */ + ddi_dma_cookie_t *pkt_cookies; /* array of DMA cookies */ + + /* private: iff scsi_pkt_allocated_correctly() */ + int pkt_path_instance; /* pHCI transport path */ + +#ifdef SCSI_SIZE_CLEAN_VERIFY + /* + * Must be last: Building a driver with-and-without + * -DSCSI_SIZE_CLEAN_VERIFY, and checking driver modules for + * differences with a tools like 'wsdiff' allows a developer to verify + * that their driver has no dependencies on scsi*(9S) size. + */ + int i_pkt_pad[8]; +#endif /* SCSI_SIZE_CLEAN_VERIFY */ }; #endif /* _KERNEL */ @@ -112,9 +157,9 @@ struct scsi_pkt { /* * Following defines are for USCSI options. */ -#define FLAG_SILENT 0x00010000 -#define FLAG_DIAGNOSE 0x00020000 -#define FLAG_ISOLATE 0x00040000 +#define FLAG_SILENT 0x00010000 +#define FLAG_DIAGNOSE 0x00020000 +#define FLAG_ISOLATE 0x00040000 /* * Following define is for scsi_vhci. diff --git a/usr/src/uts/common/sys/scsi/scsi_resource.h b/usr/src/uts/common/sys/scsi/scsi_resource.h index f5c31ac948..d95527e951 100644 --- a/usr/src/uts/common/sys/scsi/scsi_resource.h +++ b/usr/src/uts/common/sys/scsi/scsi_resource.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -71,19 +71,17 @@ struct scsi_pkt *scsi_init_pkt(struct scsi_address *, int (*)(caddr_t), caddr_t); void scsi_destroy_pkt(struct scsi_pkt *); void scsi_free_consistent_buf(struct buf *); -struct scsi_pkt *scsi_resalloc(struct scsi_address *, int, - int, opaque_t, int (*)(void)); -struct scsi_pkt *scsi_pktalloc(struct scsi_address *, int, int, int (*)(void)); +int scsi_pkt_allocated_correctly(struct scsi_pkt *); struct scsi_pkt *scsi_dmaget(struct scsi_pkt *, opaque_t, int (*)(void)); void scsi_dmafree(struct scsi_pkt *); void scsi_sync_pkt(struct scsi_pkt *); -void scsi_resfree(struct scsi_pkt *); /* * Private wrapper for scsi_pkt's allocated via scsi_init_cache_pkt() */ struct scsi_pkt_cache_wrapper { struct scsi_pkt pcw_pkt; + int pcw_magic; uint_t pcw_total_xfer; uint_t pcw_curwin; uint_t pcw_totalwin; @@ -112,12 +110,15 @@ struct buf *scsi_pkt2bp(struct scsi_pkt *); #define DEFAULT_PRIVLEN 0 #define DEFAULT_SCBLEN (sizeof (struct scsi_arq_status)) -/* - * Preliminary version of the SCSA specification - * mentioned a routine called scsi_pktfree, which - * turned out to be semantically equivialent to - * scsi_resfree. - */ +/* Private functions */ +size_t scsi_pkt_size(); +void scsi_size_clean(dev_info_t *); + +/* Obsolete kernel functions: */ +struct scsi_pkt *scsi_pktalloc(struct scsi_address *, int, int, int (*)(void)); +struct scsi_pkt *scsi_resalloc(struct scsi_address *, int, + int, opaque_t, int (*)(void)); +void scsi_resfree(struct scsi_pkt *); #define scsi_pktfree scsi_resfree #endif /* _KERNEL */ diff --git a/usr/src/uts/common/sys/scsi/scsi_types.h b/usr/src/uts/common/sys/scsi/scsi_types.h index ab2f7d4e79..0ec842f2dd 100644 --- a/usr/src/uts/common/sys/scsi/scsi_types.h +++ b/usr/src/uts/common/sys/scsi/scsi_types.h @@ -2,9 +2,8 @@ * 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. + * 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. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -103,6 +102,7 @@ typedef void *opaque_t; #include <sys/stat.h> #include <sys/sunndi.h> #include <sys/devctl.h> +#include <sys/ddifm.h> #endif /* _KERNEL */ #endif /* _SYS_SCSI_SCSI_TYPES_H */ diff --git a/usr/src/uts/common/sys/scsi/targets/stdef.h b/usr/src/uts/common/sys/scsi/targets/stdef.h index 8d1179f906..0fcfa60a3a 100644 --- a/usr/src/uts/common/sys/scsi/targets/stdef.h +++ b/usr/src/uts/common/sys/scsi/targets/stdef.h @@ -1195,12 +1195,15 @@ typedef enum { }errstate; #ifdef _KERNEL typedef struct { - struct scsi_pkt ei_failed_pkt; - struct scsi_arq_status ei_failing_status; - tapepos_t ei_expected_pos; - errstate ei_error_type; - buf_t *ei_failing_bp; + struct scsi_arq_status ei_failing_status; + tapepos_t ei_expected_pos; + errstate ei_error_type; + buf_t *ei_failing_bp; + struct scsi_pkt ei_failed_pkt; /* must be last */ + /* ...scsi_pkt_size() */ } st_err_info; +#define ST_ERR_INFO_SIZE (sizeof (st_err_info) - \ + sizeof (struct scsi_pkt) + scsi_pkt_size()) #endif diff --git a/usr/src/uts/common/sys/sunmdi.h b/usr/src/uts/common/sys/sunmdi.h index c4a42633be..6b0dc9a5ea 100644 --- a/usr/src/uts/common/sys/sunmdi.h +++ b/usr/src/uts/common/sys/sunmdi.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -228,6 +228,9 @@ dev_info_t *mdi_pi_get_client(mdi_pathinfo_t *); dev_info_t *mdi_pi_get_phci(mdi_pathinfo_t *); char *mdi_pi_get_addr(mdi_pathinfo_t *); mdi_pathinfo_state_t mdi_pi_get_state(mdi_pathinfo_t *); +int mdi_pi_get_path_instance(mdi_pathinfo_t *); +char *mdi_pi_pathname_by_instance(int path_instance); +char *mdi_pi_pathname(mdi_pathinfo_t *); /* * mdi_pathinfo Property handling functions |