Mercurial > illumos > illumos-gate
diff usr/src/uts/common/io/sata/impl/sata.c @ 10318:811db323512d
PSARC/2009/394 SATA Framework Port Multiplier Support
6422924 sata framework has to support port multipliers
6691950 ahci driver needs to support SIL3726/4726 SATA port multiplier
author | Xiao-Yu Zhang <Xiao-Yu.Zhang@Sun.COM> |
---|---|
date | Sun, 16 Aug 2009 16:01:00 +0800 |
parents | 27d5d7e9cd70 |
children | 12b08c516444 |
line wrap: on
line diff
--- a/usr/src/uts/common/io/sata/impl/sata.c Sun Aug 16 01:17:24 2009 +0800 +++ b/usr/src/uts/common/io/sata/impl/sata.c Sun Aug 16 16:01:00 2009 +0800 @@ -50,6 +50,7 @@ #include <sys/sata/sata_hba.h> #include <sys/sata/sata_defs.h> #include <sys/sata/sata_cfgadm.h> +#include <sys/sata/sata_blacklist.h> /* Debug flags - defined in sata.h */ int sata_debug_flags = 0; @@ -61,6 +62,7 @@ #define SATA_ENABLE_QUEUING 1 #define SATA_ENABLE_NCQ 2 #define SATA_ENABLE_PROCESS_EVENTS 4 +#define SATA_ENABLE_PMULT_FBS 8 /* FIS-Based Switching */ int sata_func_enable = SATA_ENABLE_PROCESS_EVENTS | SATA_ENABLE_QUEUING | SATA_ENABLE_NCQ; @@ -129,7 +131,7 @@ #define LEGACY_HWID_LEN 64 /* Model (40) + Serial (20) + pad */ -static char sata_rev_tag[] = {"1.43"}; +static char sata_rev_tag[] = {"1.44"}; /* * SATA cb_ops functions @@ -167,13 +169,21 @@ static void sata_event_daemon(void *); static void sata_event_thread_control(int); static void sata_process_controller_events(sata_hba_inst_t *sata_hba_inst); +static void sata_process_pmult_events(sata_hba_inst_t *, uint8_t); static void sata_process_device_reset(sata_hba_inst_t *, sata_address_t *); +static void sata_process_pmdevice_reset(sata_hba_inst_t *, sata_address_t *); static void sata_process_port_failed_event(sata_hba_inst_t *, sata_address_t *); static void sata_process_port_link_events(sata_hba_inst_t *, sata_address_t *); +static void sata_process_pmport_link_events(sata_hba_inst_t *, + sata_address_t *); static void sata_process_device_detached(sata_hba_inst_t *, sata_address_t *); +static void sata_process_pmdevice_detached(sata_hba_inst_t *, + sata_address_t *); static void sata_process_device_attached(sata_hba_inst_t *, sata_address_t *); +static void sata_process_pmdevice_attached(sata_hba_inst_t *, + sata_address_t *); static void sata_process_port_pwr_change(sata_hba_inst_t *, sata_address_t *); static void sata_process_cntrl_pwr_level_change(sata_hba_inst_t *); static void sata_process_target_node_cleanup(sata_hba_inst_t *, @@ -220,7 +230,7 @@ static int32_t sata_get_port_num(sata_hba_inst_t *, struct devctl_iocdata *); static void sata_cfgadm_state(sata_hba_inst_t *, int32_t, devctl_ap_state_t *); -static dev_info_t *sata_get_target_dip(dev_info_t *, int32_t); +static dev_info_t *sata_get_target_dip(dev_info_t *, uint8_t, uint8_t); static dev_info_t *sata_get_scsi_target_dip(dev_info_t *, sata_address_t *); static dev_info_t *sata_devt_to_devinfo(dev_t); static int sata_ioctl_connect(sata_hba_inst_t *, sata_device_t *); @@ -250,11 +260,19 @@ static void sata_remove_hba_instance(dev_info_t *); static int sata_validate_sata_hba_tran(dev_info_t *, sata_hba_tran_t *); static void sata_probe_ports(sata_hba_inst_t *); +static void sata_probe_pmports(sata_hba_inst_t *, uint8_t); static int sata_reprobe_port(sata_hba_inst_t *, sata_device_t *, int); -static int sata_add_device(dev_info_t *, sata_hba_inst_t *, int cport, - int pmport); +static int sata_reprobe_pmult(sata_hba_inst_t *, sata_device_t *, int); +static int sata_reprobe_pmport(sata_hba_inst_t *, sata_device_t *, int); +static void sata_alloc_pmult(sata_hba_inst_t *, sata_device_t *); +static void sata_free_pmult(sata_hba_inst_t *, sata_device_t *); +static int sata_add_device(dev_info_t *, sata_hba_inst_t *, sata_device_t *); +static int sata_offline_device(sata_hba_inst_t *, sata_device_t *, + sata_drive_info_t *); static dev_info_t *sata_create_target_node(dev_info_t *, sata_hba_inst_t *, sata_address_t *); +static void sata_remove_target_node(sata_hba_inst_t *, + sata_address_t *); static int sata_validate_scsi_address(sata_hba_inst_t *, struct scsi_address *, sata_device_t *); static int sata_validate_sata_address(sata_hba_inst_t *, int, int, int); @@ -276,6 +294,7 @@ static int sata_fetch_device_identify_data(sata_hba_inst_t *, sata_drive_info_t *); static void sata_update_port_info(sata_hba_inst_t *, sata_device_t *); +static void sata_update_pmport_info(sata_hba_inst_t *, sata_device_t *); static void sata_update_port_scr(sata_port_scr_t *, sata_device_t *); static int sata_set_dma_mode(sata_hba_inst_t *, sata_drive_info_t *); static int sata_set_cache_mode(sata_hba_inst_t *, sata_drive_info_t *, int); @@ -319,6 +338,7 @@ static void sata_save_drive_settings(sata_drive_info_t *); static void sata_show_drive_info(sata_hba_inst_t *, sata_drive_info_t *); +static void sata_show_pmult_info(sata_hba_inst_t *, sata_device_t *); static void sata_log(sata_hba_inst_t *, uint_t, char *fmt, ...); static void sata_trace_log(sata_hba_inst_t *, uint_t, const char *fmt, ...); static int sata_fetch_smart_return_status(sata_hba_inst_t *, @@ -527,7 +547,7 @@ _NOTE(SCHEME_PROTECTS_DATA("No Mutex Needed", sata_hba_inst::satahba_dip)) _NOTE(SCHEME_PROTECTS_DATA("Scheme", sata_hba_inst::satahba_attached)) _NOTE(DATA_READABLE_WITHOUT_LOCK(sata_hba_inst::satahba_dev_port)) -_NOTE(MUTEX_PROTECTS_DATA(sata_hba_inst::satahba_mutex, +_NOTE(MUTEX_PROTECTS_DATA(sata_hba_inst::satahba_mutex, sata_hba_inst::satahba_event_flags)) _NOTE(MUTEX_PROTECTS_DATA(sata_cport_info::cport_mutex, \ sata_cport_info::cport_devp)) @@ -539,10 +559,18 @@ _NOTE(MUTEX_PROTECTS_DATA(sata_cport_info::cport_mutex, \ sata_cport_info::cport_state)) _NOTE(DATA_READABLE_WITHOUT_LOCK(sata_cport_info::cport_state)) -_NOTE(MUTEX_PROTECTS_DATA(sata_cport_info::cport_mutex, \ +_NOTE(MUTEX_PROTECTS_DATA(sata_pmport_info::pmport_mutex, \ sata_pmport_info::pmport_state)) _NOTE(DATA_READABLE_WITHOUT_LOCK(sata_pmport_info::pmport_state)) +_NOTE(MUTEX_PROTECTS_DATA(sata_pmport_info::pmport_mutex, \ + sata_pmport_info::pmport_dev_type)) _NOTE(DATA_READABLE_WITHOUT_LOCK(sata_pmport_info::pmport_dev_type)) +_NOTE(MUTEX_PROTECTS_DATA(sata_pmport_info::pmport_mutex, \ + sata_pmport_info::pmport_sata_drive)) +_NOTE(MUTEX_PROTECTS_DATA(sata_pmport_info::pmport_mutex, \ + sata_pmport_info::pmport_tgtnode_clean)) +_NOTE(MUTEX_PROTECTS_DATA(sata_pmport_info::pmport_mutex, \ + sata_pmport_info::pmport_event_flags)) _NOTE(DATA_READABLE_WITHOUT_LOCK(sata_pmport_info::pmport_sata_drive)) _NOTE(DATA_READABLE_WITHOUT_LOCK(sata_pmult_info::pmult_dev_port)) _NOTE(DATA_READABLE_WITHOUT_LOCK(sata_pmult_info::pmult_num_dev_ports)) @@ -928,7 +956,7 @@ * * When the last HBA instance is detached, the event daemon is terminated. * - * NOTE: cport support only, no port multiplier support. + * NOTE: Port multiplier is supported. */ int sata_hba_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) @@ -937,8 +965,10 @@ sata_hba_inst_t *sata_hba_inst; scsi_hba_tran_t *scsi_hba_tran; sata_cport_info_t *cportinfo; + sata_pmult_info_t *pminfo; sata_drive_info_t *sdinfo; - int ncport; + sata_device_t sdevice; + int ncport, npmport; SATADBG3(SATA_DBG_HBA_IF, NULL, "sata_hba_detach: node %s (%s%d)\n", ddi_node_name(dip), ddi_driver_name(dip), ddi_get_instance(dip)); @@ -970,7 +1000,7 @@ sdinfo = SATA_CPORTINFO_DRV_INFO(cportinfo); if (sdinfo != NULL) { tdip = sata_get_target_dip(dip, - ncport); + ncport, 0); if (tdip != NULL) { if (ndi_devi_offline(tdip, NDI_DEVI_REMOVE) != @@ -985,6 +1015,44 @@ } } } + } else { /* SATA_DTYPE_PMULT */ + mutex_enter(&cportinfo->cport_mutex); + pminfo = SATA_CPORTINFO_PMULT_INFO(cportinfo); + + if (pminfo == NULL) { + SATA_LOG_D((sata_hba_inst, CE_WARN, + "sata_hba_detach: Port multiplier " + "not ready yet!")); + mutex_exit(&cportinfo->cport_mutex); + return (DDI_FAILURE); + } + + /* + * Detach would fail if removal of any of the + * target nodes is failed - albeit in that + * case some of them may have been removed. + */ + for (npmport = 0; npmport < SATA_NUM_PMPORTS( + sata_hba_inst, ncport); npmport++) { + tdip = sata_get_target_dip(dip, ncport, + npmport); + if (tdip != NULL) { + if (ndi_devi_offline(tdip, + NDI_DEVI_REMOVE) != + NDI_SUCCESS) { + SATA_LOG_D(( + sata_hba_inst, + CE_WARN, + "sata_hba_detach: " + "Target node not " + "removed !")); + mutex_exit(&cportinfo-> + cport_mutex); + return (DDI_FAILURE); + } + } + } + mutex_exit(&cportinfo->cport_mutex); } } /* @@ -1026,6 +1094,10 @@ mutex_destroy(&cportinfo->cport_mutex); kmem_free(cportinfo, sizeof (sata_cport_info_t)); + } else { /* SATA_DTYPE_PMULT */ + sdevice.satadev_addr.cport = (uint8_t)ncport; + sdevice.satadev_addr.qual = SATA_ADDR_PMULT; + sata_free_pmult(sata_hba_inst, &sdevice); } } @@ -1196,8 +1268,9 @@ * 0 if successful, * error code if operation failed. * - * NOTE: Port Multiplier is not supported. - * + * Port Multiplier support is supported now. + * + * NOTE: qual should be SATA_ADDR_DCPORT or SATA_ADDR_DPMPORT */ static int @@ -1260,10 +1333,14 @@ return (EINVAL); } + /* + * According to SCSI_TO_SATA_ADDR_QUAL, qual should be either + * SATA_ADDR_DCPORT or SATA_ADDR_DPMPORT. + */ cport = SCSI_TO_SATA_CPORT(comp_port); pmport = SCSI_TO_SATA_PMPORT(comp_port); - /* Only cport is considered now, i.e. SATA_ADDR_CPORT */ - qual = SATA_ADDR_CPORT; + qual = SCSI_TO_SATA_ADDR_QUAL(comp_port); + if (sata_validate_sata_address(sata_hba_inst, cport, pmport, qual) != 0) { ndi_dc_freehdl(dcp); @@ -1422,8 +1499,9 @@ pmport = SCSI_TO_SATA_PMPORT(ioc.port); qual = SCSI_TO_SATA_ADDR_QUAL(ioc.port); - /* Override address qualifier - handle cport only for now */ - qual = SATA_ADDR_CPORT; + SATADBG3(SATA_DBG_IOCTL_IF, sata_hba_inst, + "sata_hba_ioctl: target port is %d:%d (%d)", + cport, pmport, qual); if (sata_validate_sata_address(sata_hba_inst, cport, pmport, qual) != 0) @@ -1502,12 +1580,7 @@ break; case SATA_CFGA_GET_DEVICE_PATH: - if (qual == SATA_ADDR_CPORT) - sata_device.satadev_addr.qual = - SATA_ADDR_DCPORT; - else - sata_device.satadev_addr.qual = - SATA_ADDR_DPMPORT; + rv = sata_ioctl_get_device_path(sata_hba_inst, &sata_device, &ioc, mode); break; @@ -1678,6 +1751,138 @@ } /* + * Create READ PORT MULTIPLIER and WRITE PORT MULTIPLIER sata packet + * + * No association with any scsi packet is made and no callback routine is + * specified. + * + * Returns a pointer to sata packet upon successfull packet creation. + * Returns NULL, if packet cannot be created. + * + * NOTE: Input/Output value includes 64 bits accoring to SATA Spec 2.6, + * only lower 32 bits are available currently. + */ +sata_pkt_t * +sata_get_rdwr_pmult_pkt(dev_info_t *dip, sata_device_t *sd, + uint8_t regn, uint32_t regv, uint32_t type) +{ + sata_hba_inst_t *sata_hba_inst; + sata_pkt_txlate_t *spx; + sata_pkt_t *spkt; + sata_cmd_t *scmd; + + /* Only READ/WRITE commands are accepted. */ + ASSERT(type == SATA_RDWR_PMULT_PKT_TYPE_READ || + type == SATA_RDWR_PMULT_PKT_TYPE_WRITE); + + mutex_enter(&sata_mutex); + for (sata_hba_inst = sata_hba_list; sata_hba_inst != NULL; + sata_hba_inst = sata_hba_inst->satahba_next) { + if (SATA_DIP(sata_hba_inst) == dip) + break; + } + mutex_exit(&sata_mutex); + ASSERT(sata_hba_inst != NULL); + + spx = kmem_zalloc(sizeof (sata_pkt_txlate_t), KM_SLEEP); + spx->txlt_sata_hba_inst = sata_hba_inst; + spx->txlt_scsi_pkt = NULL; /* No scsi pkt involved */ + spkt = sata_pkt_alloc(spx, SLEEP_FUNC); + if (spkt == NULL) { + kmem_free(spx, sizeof (sata_pkt_txlate_t)); + return (NULL); + } + + /* + * NOTE: We need to send this command to the port multiplier, + * that means send to SATA_PMULT_HOSTPORT(0xf) pmport + * + * sata_device contains the address of actual target device, and the + * pmport number in the command comes from the sata_device structure. + */ + spkt->satapkt_device.satadev_addr = sd->satadev_addr; + spkt->satapkt_device.satadev_addr.pmport = SATA_PMULT_HOSTPORT; + spkt->satapkt_device.satadev_addr.qual = SATA_ADDR_PMULT; + + /* Fill sata_pkt */ + spkt->satapkt_op_mode = SATA_OPMODE_SYNCH | SATA_OPMODE_POLLING; + spkt->satapkt_comp = NULL; /* Synchronous mode, no callback */ + spkt->satapkt_time = 10; /* Timeout 10s */ + + /* Build READ PORT MULTIPLIER cmd in the sata_pkt */ + scmd = &spkt->satapkt_cmd; + scmd->satacmd_features_reg = regn & 0xff; + scmd->satacmd_features_reg_ext = (regn >> 8) & 0xff; + scmd->satacmd_device_reg = sd->satadev_addr.pmport; + scmd->satacmd_addr_type = 0; /* N/A */ + + scmd->satacmd_flags.sata_ignore_dev_reset = B_TRUE; + + if (type == SATA_RDWR_PMULT_PKT_TYPE_READ) { + scmd->satacmd_cmd_reg = SATAC_READ_PORTMULT; + scmd->satacmd_flags.sata_data_direction = SATA_DIR_READ; + scmd->satacmd_flags.sata_special_regs = 1; + scmd->satacmd_flags.sata_copy_out_lba_high_lsb = 1; + scmd->satacmd_flags.sata_copy_out_lba_mid_lsb = 1; + scmd->satacmd_flags.sata_copy_out_lba_low_lsb = 1; + scmd->satacmd_flags.sata_copy_out_sec_count_lsb = 1; + } else if (type == SATA_RDWR_PMULT_PKT_TYPE_WRITE) { + scmd->satacmd_cmd_reg = SATAC_WRITE_PORTMULT; + scmd->satacmd_flags.sata_data_direction = SATA_DIR_WRITE; + scmd->satacmd_sec_count_lsb = regv & 0xff; + scmd->satacmd_lba_low_lsb = regv >> 8 & 0xff; + scmd->satacmd_lba_mid_lsb = regv >> 16 & 0xff; + scmd->satacmd_lba_high_lsb = regv >> 24 & 0xff; + } + + return (spkt); +} + +/* + * Free sata packet and any associated resources allocated previously by + * sata_get_rdwr_pmult_pkt(). + * + * Void return. + */ +void +sata_free_rdwr_pmult_pkt(sata_pkt_t *sata_pkt) +{ + sata_pkt_txlate_t *spx = + (sata_pkt_txlate_t *)sata_pkt->satapkt_framework_private; + + /* Free allocated resources */ + sata_pkt_free(spx); + kmem_free(spx, sizeof (sata_pkt_txlate_t)); +} + +/* + * Search a port multiplier in the blacklist and update the flags if a match + * is found. + * + * Returns: + * SATA_SUCCESS if any matched entry is found. + * SATA_FAILURE if no matched entry is found. + */ +int +sata_check_pmult_blacklist(sata_device_t *sd) +{ + sata_pmult_bl_t *blp; + for (blp = sata_pmult_blacklist; blp->bl_gscr0; blp++) { + if (sd->satadev_gscr.gscr0 != blp->bl_gscr0 && blp->bl_gscr0) + continue; + if (sd->satadev_gscr.gscr1 != blp->bl_gscr1 && blp->bl_gscr1) + continue; + if (sd->satadev_gscr.gscr2 != blp->bl_gscr2 && blp->bl_gscr2) + continue; + + cmn_err(CE_WARN, "!Port multiplier is on the blacklist."); + sd->satadev_add_info = blp->bl_flags; + return (SATA_SUCCESS); + } + return (SATA_FAILURE); +} + +/* * sata_name_child is for composing the name of the node * the format of the name is "target,0". */ @@ -2125,9 +2330,11 @@ sata_hba_inst_t *sata_hba_inst = (sata_hba_inst_t *)(ap->a_hba_tran->tran_hba_private); sata_pkt_txlate_t *spx = (sata_pkt_txlate_t *)pkt->pkt_ha_private; + sata_device_t *sdevice = &spx->txlt_sata_pkt->satapkt_device; sata_drive_info_t *sdinfo; struct buf *bp; - int cport; + uint8_t cport, pmport; + boolean_t dev_gone = B_FALSE; int rval; SATADBG1(SATA_DBG_SCSI_IF, sata_hba_inst, @@ -2137,15 +2344,42 @@ spx->txlt_scsi_pkt == pkt && spx->txlt_sata_pkt != NULL); cport = SCSI_TO_SATA_CPORT(ap->a_target); + pmport = SCSI_TO_SATA_PMPORT(ap->a_target); mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, cport))); - sdinfo = sata_get_device_info(sata_hba_inst, - &spx->txlt_sata_pkt->satapkt_device); - if (sdinfo == NULL || - SATA_CPORT_INFO(sata_hba_inst, cport)->cport_tgtnode_clean == - B_FALSE || - (sdinfo->satadrv_state & SATA_DSTATE_FAILED) != 0) { - + + if (sdevice->satadev_addr.qual == SATA_ADDR_DCPORT) { + sdinfo = sata_get_device_info(sata_hba_inst, sdevice); + if (sdinfo == NULL || + SATA_CPORT_INFO(sata_hba_inst, cport)-> + cport_tgtnode_clean == B_FALSE || + (sdinfo->satadrv_state & SATA_DSTATE_FAILED) != 0) { + dev_gone = B_TRUE; + } + } else if (sdevice->satadev_addr.qual == SATA_ADDR_DPMPORT) { + if (SATA_CPORT_DEV_TYPE(sata_hba_inst, cport) != + SATA_DTYPE_PMULT || SATA_PMULT_INFO(sata_hba_inst, + cport) == NULL) { + dev_gone = B_TRUE; + } else if (SATA_PMPORT_INFO(sata_hba_inst, cport, + pmport) == NULL) { + dev_gone = B_TRUE; + } else { + mutex_enter(&(SATA_PMPORT_MUTEX(sata_hba_inst, + cport, pmport))); + sdinfo = sata_get_device_info(sata_hba_inst, sdevice); + if (sdinfo == NULL || + SATA_PMPORT_INFO(sata_hba_inst, cport, pmport)-> + pmport_tgtnode_clean == B_FALSE || + (sdinfo->satadrv_state & SATA_DSTATE_FAILED) != 0) { + dev_gone = B_TRUE; + } + mutex_exit(&(SATA_PMPORT_MUTEX(sata_hba_inst, + cport, pmport))); + } + } + + if (dev_gone == B_TRUE) { mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport))); pkt->pkt_reason = CMD_DEV_GONE; /* @@ -2424,8 +2658,12 @@ mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, sata_device.satadev_addr.cport))); if (level == RESET_ALL) { - /* port reset - cport only */ - sata_device.satadev_addr.qual = SATA_ADDR_CPORT; + /* port reset */ + if (sata_device.satadev_addr.qual == SATA_ADDR_DCPORT) + sata_device.satadev_addr.qual = SATA_ADDR_CPORT; + else + sata_device.satadev_addr.qual = SATA_ADDR_PMPORT; + if ((*SATA_RESET_DPORT_FUNC(sata_hba_inst)) (SATA_DIP(sata_hba_inst), &sata_device) == SATA_SUCCESS) return (1); @@ -2815,9 +3053,16 @@ case -1: /* Invalid address or invalid device type */ + SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, + "sata_scsi_start: reject command because " + "dev type or address is invalid\n", NULL); return (TRAN_BADPKT); case 1: /* valid address but no device - it has disappeared ? */ + SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, + "sata_scsi_start: reject command because " + "device is gone\n", NULL); + spx->txlt_scsi_pkt->pkt_reason = CMD_DEV_GONE; *reason = CMD_DEV_GONE; /* @@ -3142,7 +3387,7 @@ * SATA translate command: Inquiry / Identify Device * Use cached Identify Device data for now, rather than issuing actual * Device Identify cmd request. If device is detached and re-attached, - * asynchromous event processing should fetch and refresh Identify Device + * asynchronous event processing should fetch and refresh Identify Device * data. * Two VPD pages are supported now: * Vital Product Data page @@ -5766,22 +6011,23 @@ * have called the sata_pkt callback function for this packet. * * The scsi callback has to be performed by the caller of this routine. - * - * Note 2: No port multiplier support for now. */ static int sata_hba_start(sata_pkt_txlate_t *spx, int *rval) { - int stat, cport; + int stat; + uint8_t cport = SATA_TXLT_CPORT(spx); + uint8_t pmport = SATA_TXLT_PMPORT(spx); sata_hba_inst_t *sata_hba_inst = spx->txlt_sata_hba_inst; sata_drive_info_t *sdinfo; - sata_device_t *sata_device; + sata_pmult_info_t *pminfo; + sata_pmport_info_t *pmportinfo = NULL; + sata_device_t *sata_device = NULL; uint8_t cmd; struct sata_cmd_flags cmd_flags; ASSERT(spx->txlt_sata_pkt != NULL); - cport = SATA_TXLT_CPORT(spx); ASSERT(mutex_owned(&SATA_CPORT_MUTEX(sata_hba_inst, cport))); sdinfo = sata_get_device_info(sata_hba_inst, @@ -5789,13 +6035,40 @@ ASSERT(sdinfo != NULL); /* Clear device reset state? */ - if (sdinfo->satadrv_event_flags & SATA_EVNT_CLEAR_DEVICE_RESET) { - spx->txlt_sata_pkt->satapkt_cmd.satacmd_flags. - sata_clear_dev_reset = B_TRUE; - sdinfo->satadrv_event_flags &= ~SATA_EVNT_CLEAR_DEVICE_RESET; - SATADBG1(SATA_DBG_EVENTS, sata_hba_inst, - "sata_hba_start: clearing device reset state\n", NULL); - } + /* qual should be XXX_DPMPORT, but add XXX_PMPORT in case */ + if (sdinfo->satadrv_addr.qual == SATA_ADDR_DPMPORT || + sdinfo->satadrv_addr.qual == SATA_ADDR_PMPORT) { + + /* + * Get the pmult_info of the its parent port multiplier, all + * sub-devices share a common device reset flags on in + * pmult_info. + */ + pminfo = SATA_PMULT_INFO(sata_hba_inst, cport); + pmportinfo = pminfo->pmult_dev_port[pmport]; + ASSERT(pminfo != NULL); + if (pminfo->pmult_event_flags & SATA_EVNT_CLEAR_DEVICE_RESET) { + spx->txlt_sata_pkt->satapkt_cmd.satacmd_flags. + sata_clear_dev_reset = B_TRUE; + pminfo->pmult_event_flags &= + ~SATA_EVNT_CLEAR_DEVICE_RESET; + SATADBG1(SATA_DBG_EVENTS, sata_hba_inst, + "sata_hba_start: clearing device reset state" + "on pmult.\n", NULL); + } + } else { + if (sdinfo->satadrv_event_flags & + SATA_EVNT_CLEAR_DEVICE_RESET) { + spx->txlt_sata_pkt->satapkt_cmd.satacmd_flags. + sata_clear_dev_reset = B_TRUE; + sdinfo->satadrv_event_flags &= + ~SATA_EVNT_CLEAR_DEVICE_RESET; + SATADBG1(SATA_DBG_EVENTS, sata_hba_inst, + "sata_hba_start: clearing device reset state\n", + NULL); + } + } + cmd = spx->txlt_sata_pkt->satapkt_cmd.satacmd_cmd_reg; cmd_flags = spx->txlt_sata_pkt->satapkt_cmd.satacmd_flags; sata_device = &spx->txlt_sata_pkt->satapkt_device; @@ -5857,7 +6130,7 @@ sata_device->satadev_addr.cport); else sata_log(sata_hba_inst, CE_CONT, - "SATA port %d pmport %d error\n", + "SATA port %d:%d error\n", sata_device->satadev_addr.cport, sata_device->satadev_addr.pmport); @@ -5869,7 +6142,17 @@ * because original packet's sata address refered to a device * attached to some port. */ - sata_update_port_info(sata_hba_inst, sata_device); + if (sdinfo->satadrv_addr.qual == SATA_ADDR_DPMPORT || + sdinfo->satadrv_addr.qual == SATA_ADDR_PMPORT) { + mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport))); + mutex_enter(&pmportinfo->pmport_mutex); + sata_update_pmport_info(sata_hba_inst, sata_device); + mutex_exit(&pmportinfo->pmport_mutex); + mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, cport))); + } else { + sata_update_port_info(sata_hba_inst, sata_device); + } + spx->txlt_scsi_pkt->pkt_reason = CMD_TRAN_ERR; *rval = TRAN_FATAL_ERROR; break; @@ -5944,8 +6227,14 @@ * the device reset state, * so the next sata packet may carry it to HBA. */ - sdinfo->satadrv_event_flags |= - SATA_EVNT_CLEAR_DEVICE_RESET; + if (sdinfo->satadrv_addr.qual == SATA_ADDR_PMPORT || + sdinfo->satadrv_addr.qual == SATA_ADDR_DPMPORT) { + pminfo->pmult_event_flags |= + SATA_EVNT_CLEAR_DEVICE_RESET; + } else { + sdinfo->satadrv_event_flags |= + SATA_EVNT_CLEAR_DEVICE_RESET; + } } } return (-1); @@ -8779,10 +9068,6 @@ mutex_exit(&sata_mutex); } - - - - /* * Probe all SATA ports of the specified HBA instance. * The assumption is that there are no target and attachment point minor nodes @@ -8802,11 +9087,9 @@ sata_probe_ports(sata_hba_inst_t *sata_hba_inst) { dev_info_t *dip = SATA_DIP(sata_hba_inst); - int ncport, npmport; + int ncport; sata_cport_info_t *cportinfo; sata_drive_info_t *drive; - sata_pmult_info_t *pminfo; - sata_pmport_info_t *pmportinfo; sata_device_t sata_device; int rval; dev_t minor_number; @@ -8837,8 +9120,8 @@ * an attachment point */ mutex_exit(&cportinfo->cport_mutex); - minor_number = - SATA_MAKE_AP_MINOR(ddi_get_instance(dip), ncport, 0, 0); + minor_number = SATA_MAKE_AP_MINOR(ddi_get_instance(dip), + ncport, 0, SATA_ADDR_CPORT); (void) sprintf(name, "%d", ncport); if (ddi_create_minor_node(dip, name, S_IFCHR, minor_number, DDI_NT_SATA_ATTACHMENT_POINT, 0) != @@ -8860,7 +9143,7 @@ (dip, &sata_device); mutex_enter(&cportinfo->cport_mutex); - sata_update_port_scr(&cportinfo->cport_scr, &sata_device); + cportinfo->cport_scr = sata_device.satadev_scr; if (rval != SATA_SUCCESS) { /* Something went wrong? Fail the port */ cportinfo->cport_state = SATA_PSTATE_FAILED; @@ -8895,7 +9178,7 @@ drive->satadrv_state = SATA_STATE_UNKNOWN; mutex_exit(&cportinfo->cport_mutex); - if (sata_add_device(dip, sata_hba_inst, ncport, 0) != + if (sata_add_device(dip, sata_hba_inst, &sata_device) != SATA_SUCCESS) { /* * Plugged device was not correctly identified. @@ -8910,135 +9193,114 @@ goto reprobe_cport; } } - } else { - mutex_exit(&cportinfo->cport_mutex); - ASSERT(cportinfo->cport_dev_type == SATA_DTYPE_PMULT); - pminfo = kmem_zalloc(sizeof (sata_pmult_info_t), - KM_SLEEP); - mutex_enter(&cportinfo->cport_mutex); - ASSERT(pminfo != NULL); - SATA_CPORTINFO_PMULT_INFO(cportinfo) = pminfo; - pminfo->pmult_addr.cport = cportinfo->cport_addr.cport; - pminfo->pmult_addr.pmport = SATA_PMULT_HOSTPORT; - pminfo->pmult_addr.qual = SATA_ADDR_PMPORT; - pminfo->pmult_num_dev_ports = - sata_device.satadev_add_info; - mutex_init(&pminfo->pmult_mutex, NULL, MUTEX_DRIVER, - NULL); - pminfo->pmult_state = SATA_STATE_PROBING; + } else { /* SATA_DTYPE_PMULT */ mutex_exit(&cportinfo->cport_mutex); - /* Probe Port Multiplier ports */ - for (npmport = 0; - npmport < pminfo->pmult_num_dev_ports; - npmport++) { - pmportinfo = kmem_zalloc( - sizeof (sata_pmport_info_t), KM_SLEEP); - mutex_enter(&cportinfo->cport_mutex); - ASSERT(pmportinfo != NULL); - pmportinfo->pmport_addr.cport = ncport; - pmportinfo->pmport_addr.pmport = npmport; - pmportinfo->pmport_addr.qual = - SATA_ADDR_PMPORT; - pminfo->pmult_dev_port[npmport] = pmportinfo; - - mutex_init(&pmportinfo->pmport_mutex, NULL, - MUTEX_DRIVER, NULL); - - mutex_exit(&cportinfo->cport_mutex); - - /* Create an attachment point */ - minor_number = SATA_MAKE_AP_MINOR( - ddi_get_instance(dip), ncport, npmport, 1); - (void) sprintf(name, "%d.%d", ncport, npmport); - if (ddi_create_minor_node(dip, name, S_IFCHR, - minor_number, DDI_NT_SATA_ATTACHMENT_POINT, - 0) != DDI_SUCCESS) { - sata_log(sata_hba_inst, CE_WARN, - "sata_hba_attach: " - "cannot create SATA attachment " - "point for port %d pmult port %d", - ncport, npmport); - } - - start_time = ddi_get_lbolt(); - reprobe_pmport: - sata_device.satadev_addr.pmport = npmport; - sata_device.satadev_addr.qual = - SATA_ADDR_PMPORT; - - rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst)) - (dip, &sata_device); - mutex_enter(&cportinfo->cport_mutex); - - /* sata_update_port_info() */ - sata_update_port_scr(&pmportinfo->pmport_scr, - &sata_device); - - if (rval != SATA_SUCCESS) { - pmportinfo->pmport_state = - SATA_PSTATE_FAILED; - mutex_exit(&cportinfo->cport_mutex); - continue; - } - pmportinfo->pmport_state &= - ~SATA_STATE_PROBING; - pmportinfo->pmport_state |= SATA_STATE_PROBED; - pmportinfo->pmport_dev_type = - sata_device.satadev_type; - - pmportinfo->pmport_state |= SATA_STATE_READY; - if (pmportinfo->pmport_dev_type == - SATA_DTYPE_NONE) { - mutex_exit(&cportinfo->cport_mutex); - continue; - } - /* Port multipliers cannot be chained */ - ASSERT(pmportinfo->pmport_dev_type != - SATA_DTYPE_PMULT); - /* - * There is something attached to Port - * Multiplier device port - * Allocate device info structure - */ - if (pmportinfo->pmport_sata_drive == NULL) { - mutex_exit(&cportinfo->cport_mutex); - pmportinfo->pmport_sata_drive = - kmem_zalloc( - sizeof (sata_drive_info_t), - KM_SLEEP); - mutex_enter(&cportinfo->cport_mutex); - } - drive = pmportinfo->pmport_sata_drive; - drive->satadrv_addr.cport = - pmportinfo->pmport_addr.cport; - drive->satadrv_addr.pmport = npmport; - drive->satadrv_addr.qual = SATA_ADDR_DPMPORT; - drive->satadrv_type = pmportinfo-> - pmport_dev_type; - drive->satadrv_state = SATA_STATE_UNKNOWN; - - mutex_exit(&cportinfo->cport_mutex); - if (sata_add_device(dip, sata_hba_inst, ncport, - npmport) != SATA_SUCCESS) { - /* - * Plugged device was not correctly - * identified. Retry, within the - * SATA_DEV_IDENTIFY_TIMEOUT - */ - cur_time = ddi_get_lbolt(); - if ((cur_time - start_time) < - drv_usectohz( - SATA_DEV_IDENTIFY_TIMEOUT)) { - /* sleep for a while */ - delay(drv_usectohz( - SATA_DEV_RETRY_DLY)); - goto reprobe_pmport; - } - } - } + /* Allocate sata_pmult_info and sata_pmport_info */ + sata_alloc_pmult(sata_hba_inst, &sata_device); + + /* Log the information of the port multiplier */ + sata_show_pmult_info(sata_hba_inst, &sata_device); + + /* Probe its pmports */ + sata_probe_pmports(sata_hba_inst, ncport); + } + } +} + +/* + * Probe all device ports behind a port multiplier. + * + * PMult-related structure should be allocated before by sata_alloc_pmult(). + * + * NOTE1: Only called from sata_probe_ports() + * NOTE2: No mutex should be hold. + */ +static void +sata_probe_pmports(sata_hba_inst_t *sata_hba_inst, uint8_t ncport) +{ + dev_info_t *dip = SATA_DIP(sata_hba_inst); + sata_pmult_info_t *pmultinfo = NULL; + sata_pmport_info_t *pmportinfo = NULL; + sata_drive_info_t *drive = NULL; + sata_device_t sata_device; + + clock_t start_time, cur_time; + int npmport; + int rval; + + pmultinfo = SATA_PMULT_INFO(sata_hba_inst, ncport); + + /* Probe Port Multiplier ports */ + for (npmport = 0; npmport < pmultinfo->pmult_num_dev_ports; npmport++) { + pmportinfo = pmultinfo->pmult_dev_port[npmport]; + start_time = ddi_get_lbolt(); +reprobe_pmport: + sata_device.satadev_addr.cport = ncport; + sata_device.satadev_addr.pmport = npmport; + sata_device.satadev_addr.qual = SATA_ADDR_PMPORT; + sata_device.satadev_rev = SATA_DEVICE_REV; + + /* Let HBA driver probe it. */ + rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst)) + (dip, &sata_device); + mutex_enter(&pmportinfo->pmport_mutex); + + pmportinfo->pmport_scr = sata_device.satadev_scr; + + if (rval != SATA_SUCCESS) { pmportinfo->pmport_state = - SATA_STATE_PROBED | SATA_STATE_READY; + SATA_PSTATE_FAILED; + mutex_exit(&pmportinfo->pmport_mutex); + continue; + } + pmportinfo->pmport_state &= ~SATA_STATE_PROBING; + pmportinfo->pmport_state |= SATA_STATE_PROBED; + pmportinfo->pmport_dev_type = sata_device.satadev_type; + + pmportinfo->pmport_state |= SATA_STATE_READY; + if (pmportinfo->pmport_dev_type == + SATA_DTYPE_NONE) { + SATADBG2(SATA_DBG_PMULT, sata_hba_inst, + "no device found at port %d:%d", ncport, npmport); + mutex_exit(&pmportinfo->pmport_mutex); + continue; + } + /* Port multipliers cannot be chained */ + ASSERT(pmportinfo->pmport_dev_type != SATA_DTYPE_PMULT); + /* + * There is something attached to Port + * Multiplier device port + * Allocate device info structure + */ + if (pmportinfo->pmport_sata_drive == NULL) { + mutex_exit(&pmportinfo->pmport_mutex); + pmportinfo->pmport_sata_drive = + kmem_zalloc(sizeof (sata_drive_info_t), KM_SLEEP); + mutex_enter(&pmportinfo->pmport_mutex); + } + drive = pmportinfo->pmport_sata_drive; + drive->satadrv_addr.cport = pmportinfo->pmport_addr.cport; + drive->satadrv_addr.pmport = npmport; + drive->satadrv_addr.qual = SATA_ADDR_DPMPORT; + drive->satadrv_type = pmportinfo-> pmport_dev_type; + drive->satadrv_state = SATA_STATE_UNKNOWN; + + mutex_exit(&pmportinfo->pmport_mutex); + rval = sata_add_device(dip, sata_hba_inst, &sata_device); + + if (rval != SATA_SUCCESS) { + /* + * Plugged device was not correctly identified. + * Retry, within the SATA_DEV_IDENTIFY_TIMEOUT + */ + cur_time = ddi_get_lbolt(); + if ((cur_time - start_time) < drv_usectohz( + SATA_DEV_IDENTIFY_TIMEOUT)) { + /* sleep for a while */ + delay(drv_usectohz(SATA_DEV_RETRY_DLY)); + goto reprobe_pmport; + } } } } @@ -9052,28 +9314,29 @@ * * This function cannot be called from an interrupt context. * - * ONLY DISK TARGET NODES ARE CREATED NOW + * Create target nodes for disk, CD/DVD, Tape and ATAPI disk devices * * Returns SATA_SUCCESS when port/device was fully processed, SATA_FAILURE when * device identification failed - adding a device could be retried. * */ static int -sata_add_device(dev_info_t *pdip, sata_hba_inst_t *sata_hba_inst, int cport, - int pmport) +sata_add_device(dev_info_t *pdip, sata_hba_inst_t *sata_hba_inst, + sata_device_t *sata_device) { sata_cport_info_t *cportinfo; sata_pmult_info_t *pminfo; sata_pmport_info_t *pmportinfo; dev_info_t *cdip; /* child dip */ - sata_device_t sata_device; + sata_address_t *saddr = &sata_device->satadev_addr; + uint8_t cport, pmport; int rval; - - + cport = saddr->cport; + pmport = saddr->pmport; cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport); ASSERT(cportinfo->cport_dev_type != SATA_DTYPE_NONE); - mutex_enter(&cportinfo->cport_mutex); + /* * Some device is attached to a controller port. * We rely on controllers distinquishing between no-device, @@ -9082,25 +9345,24 @@ * positively the dev type before trying to attach * the target driver. */ - sata_device.satadev_rev = SATA_DEVICE_REV; - if (cportinfo->cport_dev_type != SATA_DTYPE_PMULT) { - /* - * Not port multiplier. - */ - sata_device.satadev_addr = cportinfo->cport_addr; - sata_device.satadev_addr.qual = SATA_ADDR_DCPORT; - mutex_exit(&cportinfo->cport_mutex); - - rval = sata_probe_device(sata_hba_inst, &sata_device); + sata_device->satadev_rev = SATA_DEVICE_REV; + switch (saddr->qual) { + case SATA_ADDR_CPORT: + /* + * Add a non-port-multiplier device at controller port. + */ + saddr->qual = SATA_ADDR_DCPORT; + + rval = sata_probe_device(sata_hba_inst, sata_device); if (rval != SATA_SUCCESS || - sata_device.satadev_type == SATA_DTYPE_UNKNOWN) + sata_device->satadev_type == SATA_DTYPE_UNKNOWN) return (SATA_FAILURE); mutex_enter(&cportinfo->cport_mutex); sata_show_drive_info(sata_hba_inst, SATA_CPORTINFO_DRV_INFO(cportinfo)); - if ((sata_device.satadev_type & SATA_VALID_DEV_TYPE) == 0) { + if ((sata_device->satadev_type & SATA_VALID_DEV_TYPE) == 0) { /* * Could not determine device type or * a device is not supported. @@ -9110,7 +9372,7 @@ mutex_exit(&cportinfo->cport_mutex); return (SATA_SUCCESS); } - cportinfo->cport_dev_type = sata_device.satadev_type; + cportinfo->cport_dev_type = sata_device->satadev_type; cportinfo->cport_tgtnode_clean = B_TRUE; mutex_exit(&cportinfo->cport_mutex); @@ -9130,59 +9392,58 @@ "SATA device at port %d - " "default device features could not be set." " Device may not operate as expected.", - cportinfo->cport_addr.cport); - } - - cdip = sata_create_target_node(pdip, sata_hba_inst, - &sata_device.satadev_addr); - mutex_enter(&cportinfo->cport_mutex); + cport); + } + + cdip = sata_create_target_node(pdip, sata_hba_inst, saddr); if (cdip == NULL) { /* * Attaching target node failed. * We retain sata_drive_info structure... */ - mutex_exit(&cportinfo->cport_mutex); return (SATA_SUCCESS); } + + mutex_enter(&cportinfo->cport_mutex); (SATA_CPORTINFO_DRV_INFO(cportinfo))-> satadrv_state = SATA_STATE_READY; - } else { - /* This must be Port Multiplier type */ - if (cportinfo->cport_dev_type != SATA_DTYPE_PMULT) { - SATA_LOG_D((sata_hba_inst, CE_WARN, - "sata_add_device: " - "unrecognized dev type %x", - cportinfo->cport_dev_type)); - mutex_exit(&cportinfo->cport_mutex); - return (SATA_SUCCESS); - } + mutex_exit(&cportinfo->cport_mutex); + + break; + + case SATA_ADDR_PMPORT: + saddr->qual = SATA_ADDR_DPMPORT; + + mutex_enter(&cportinfo->cport_mutex); + /* It must be a Port Multiplier at the controller port */ + ASSERT(cportinfo->cport_dev_type == SATA_DTYPE_PMULT); + pminfo = SATA_CPORTINFO_PMULT_INFO(cportinfo); - pmportinfo = pminfo->pmult_dev_port[pmport]; - sata_device.satadev_addr = pmportinfo->pmport_addr; - sata_device.satadev_addr.qual = SATA_ADDR_DPMPORT; + pmportinfo = pminfo->pmult_dev_port[saddr->pmport]; mutex_exit(&cportinfo->cport_mutex); - rval = sata_probe_device(sata_hba_inst, &sata_device); + rval = sata_probe_device(sata_hba_inst, sata_device); if (rval != SATA_SUCCESS || - sata_device.satadev_type == SATA_DTYPE_UNKNOWN) { + sata_device->satadev_type == SATA_DTYPE_UNKNOWN) { return (SATA_FAILURE); } - mutex_enter(&cportinfo->cport_mutex); + + mutex_enter(&pmportinfo->pmport_mutex); sata_show_drive_info(sata_hba_inst, - SATA_CPORTINFO_DRV_INFO(cportinfo)); - - if ((sata_device.satadev_type & SATA_VALID_DEV_TYPE) == 0) { + SATA_PMPORTINFO_DRV_INFO(pmportinfo)); + + if ((sata_device->satadev_type & SATA_VALID_DEV_TYPE) == 0) { /* * Could not determine device type. * Degrade this device to unknown. */ pmportinfo->pmport_dev_type = SATA_DTYPE_UNKNOWN; - mutex_exit(&cportinfo->cport_mutex); + mutex_exit(&pmportinfo->pmport_mutex); return (SATA_SUCCESS); } - pmportinfo->pmport_dev_type = sata_device.satadev_type; + pmportinfo->pmport_dev_type = sata_device->satadev_type; pmportinfo->pmport_tgtnode_clean = B_TRUE; - mutex_exit(&cportinfo->cport_mutex); + mutex_exit(&pmportinfo->pmport_mutex); /* * Initialize device to the desired state. @@ -9197,30 +9458,106 @@ if (rval == SATA_RETRY) sata_log(sata_hba_inst, CE_WARN, - "SATA device at port %d pmport %d - " + "SATA device at port %d:%d - " "default device features could not be set." " Device may not operate as expected.", - pmportinfo->pmport_addr.cport, - pmportinfo->pmport_addr.pmport); - } - cdip = sata_create_target_node(pdip, sata_hba_inst, - &sata_device.satadev_addr); - mutex_enter(&cportinfo->cport_mutex); + cport, pmport); + } + + cdip = sata_create_target_node(pdip, sata_hba_inst, saddr); if (cdip == NULL) { /* * Attaching target node failed. * We retain sata_drive_info structure... */ - mutex_exit(&cportinfo->cport_mutex); return (SATA_SUCCESS); } + mutex_enter(&pmportinfo->pmport_mutex); pmportinfo->pmport_sata_drive->satadrv_state |= SATA_STATE_READY; - } - mutex_exit(&cportinfo->cport_mutex); + mutex_exit(&pmportinfo->pmport_mutex); + + break; + + default: + return (SATA_FAILURE); + } + return (SATA_SUCCESS); } +/* + * Clean up target node at specific address. + * + * NOTE: No Mutex should be hold. + */ +static int +sata_offline_device(sata_hba_inst_t *sata_hba_inst, + sata_device_t *sata_device, sata_drive_info_t *sdinfo) +{ + uint8_t cport, pmport, qual; + dev_info_t *tdip; + + cport = sata_device->satadev_addr.cport; + pmport = sata_device->satadev_addr.pmport; + qual = sata_device->satadev_addr.qual; + + if (qual == SATA_ADDR_DCPORT) { + SATA_LOG_D((sata_hba_inst, CE_WARN, + "sata_hba_ioctl: disconnect device at port %d", cport)); + } else { + SATA_LOG_D((sata_hba_inst, CE_WARN, + "sata_hba_ioctl: disconnect device at port %d:%d", + cport, pmport)); + } + + /* We are addressing attached device, not a port */ + sata_device->satadev_addr.qual = + sdinfo->satadrv_addr.qual; + tdip = sata_get_scsi_target_dip(SATA_DIP(sata_hba_inst), + &sata_device->satadev_addr); + if (tdip != NULL && ndi_devi_offline(tdip, + NDI_DEVI_REMOVE) != NDI_SUCCESS) { + /* + * Problem : + * The target node remained attached. + * This happens when the device file was open + * or a node was waiting for resources. + * Cannot do anything about it. + */ + if (qual == SATA_ADDR_DCPORT) { + SATA_LOG_D((sata_hba_inst, CE_WARN, + "sata_hba_ioctl: disconnect: could " + "not unconfigure device before " + "disconnecting the SATA port %d", + cport)); + } else { + SATA_LOG_D((sata_hba_inst, CE_WARN, + "sata_hba_ioctl: disconnect: could " + "not unconfigure device before " + "disconnecting the SATA port %d:%d", + cport, pmport)); + } + /* + * Set DEVICE REMOVED state in the target + * node. It will prevent access to the device + * even when a new device is attached, until + * the old target node is released, removed and + * recreated for a new device. + */ + sata_set_device_removed(tdip); + + /* + * Instruct event daemon to try the target + * node cleanup later. + */ + sata_set_target_node_cleanup( + sata_hba_inst, &sata_device->satadev_addr); + } + + + return (SATA_SUCCESS); +} /* @@ -9230,8 +9567,6 @@ * * A dev_info_t pointer is returned if operation is successful, NULL is * returned otherwise. - * - * No port multiplier support. */ static dev_info_t * @@ -9406,6 +9741,76 @@ return (NULL); } +/* + * Remove a target node. + */ +static void +sata_remove_target_node(sata_hba_inst_t *sata_hba_inst, + sata_address_t *sata_addr) +{ + dev_info_t *tdip; + uint8_t cport = sata_addr->cport; + uint8_t pmport = sata_addr->pmport; + uint8_t qual = sata_addr->qual; + + /* Note the sata daemon uses the address of the port/pmport */ + ASSERT(qual == SATA_ADDR_CPORT || qual == SATA_ADDR_PMPORT); + + /* Remove target node */ + tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst), cport, pmport); + if (tdip != NULL) { + /* + * Target node exists. Unconfigure device + * then remove the target node (one ndi + * operation). + */ + if (ndi_devi_offline(tdip, NDI_DEVI_REMOVE) != NDI_SUCCESS) { + /* + * PROBLEM - no device, but target node remained. This + * happens when the file was open or node was waiting + * for resources. + */ + SATA_LOG_D((sata_hba_inst, CE_WARN, + "sata_remove_target_node: " + "Failed to remove target node for " + "detached SATA device.")); + /* + * Set target node state to DEVI_DEVICE_REMOVED. But + * re-check first that the node still exists. + */ + tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst), + cport, pmport); + if (tdip != NULL) { + sata_set_device_removed(tdip); + /* + * Instruct event daemon to retry the cleanup + * later. + */ + sata_set_target_node_cleanup(sata_hba_inst, + sata_addr); + } + } + + if (qual == SATA_ADDR_CPORT) + sata_log(sata_hba_inst, CE_WARN, + "SATA device detached at port %d", cport); + else + sata_log(sata_hba_inst, CE_WARN, + "SATA device detached at port %d:%d", + cport, pmport); + } +#ifdef SATA_DEBUG + else { + if (qual == SATA_ADDR_CPORT) + sata_log(sata_hba_inst, CE_WARN, + "target node not found at port %d", cport); + else + sata_log(sata_hba_inst, CE_WARN, + "target node not found at port %d:%d", + cport, pmport); + } +#endif +} /* @@ -9424,14 +9829,14 @@ * * This function cannot be called in interrupt context - it may sleep. * - * NOte: Port multiplier is not supported yet, although there may be some - * pieces of code referencing to it. + * Note: Port multiplier is supported. */ static int sata_reprobe_port(sata_hba_inst_t *sata_hba_inst, sata_device_t *sata_device, int flag) { sata_cport_info_t *cportinfo; + sata_pmult_info_t *pmultinfo; sata_drive_info_t *sdinfo, *osdinfo; boolean_t init_device = B_FALSE; int prev_device_type = SATA_DTYPE_NONE; @@ -9439,11 +9844,28 @@ int prev_device_state = 0; clock_t start_time; int retry = B_FALSE; + uint8_t cport = sata_device->satadev_addr.cport; int rval_probe, rval_init; + /* + * If target is pmport, sata_reprobe_pmport() will handle it. + */ + if (sata_device->satadev_addr.qual == SATA_ADDR_PMPORT || + sata_device->satadev_addr.qual == SATA_ADDR_DPMPORT) + return (sata_reprobe_pmport(sata_hba_inst, sata_device, flag)); + /* We only care about host sata cport for now */ cportinfo = SATA_CPORT_INFO(sata_hba_inst, sata_device->satadev_addr.cport); + + /* + * If a port multiplier was previously attached (we have no idea it + * still there or not), sata_reprobe_pmult() will handle it. + */ + if (cportinfo->cport_dev_type == SATA_DTYPE_PMULT) + return (sata_reprobe_pmult(sata_hba_inst, sata_device, flag)); + + /* Store sata_drive_info when a non-pmult device was attached. */ osdinfo = SATA_CPORTINFO_DRV_INFO(cportinfo); if (osdinfo != NULL) { /* @@ -9509,24 +9931,22 @@ } cportinfo->cport_state |= SATA_STATE_READY; + cportinfo->cport_state |= SATA_STATE_PROBED; + cportinfo->cport_dev_type = sata_device->satadev_type; sdinfo = SATA_CPORTINFO_DRV_INFO(cportinfo); /* * If we are re-probing the port, there may be * sata_drive_info structure attached - * (or sata_pm_info, if PMult is supported). */ if (sata_device->satadev_type == SATA_DTYPE_NONE) { + /* * There is no device, so remove device info structure, * if necessary. - * Only direct attached drive is considered now, until - * port multiplier is supported. If the previously - * attached device was a port multiplier, we would need - * to take care of devices attached beyond the port - * multiplier. - */ + */ + /* Device change: Drive -> None */ SATA_CPORTINFO_DRV_INFO(cportinfo) = NULL; cportinfo->cport_dev_type = SATA_DTYPE_NONE; if (sdinfo != NULL) { @@ -9537,9 +9957,12 @@ } mutex_exit(&cportinfo->cport_mutex); return (SATA_SUCCESS); + } if (sata_device->satadev_type != SATA_DTYPE_PMULT) { + + /* Device (may) change: Drive -> Drive */ if (sdinfo == NULL) { /* * There is some device attached, but there is @@ -9578,14 +10001,36 @@ cportinfo->cport_dev_type = SATA_DTYPE_UNKNOWN; sata_device->satadev_addr.qual = sdinfo->satadrv_addr.qual; } else { - /* - * The device is a port multiplier - not handled now. - */ - cportinfo->cport_dev_type = SATA_DTYPE_UNKNOWN; + /* Device change: Drive -> PMult */ + SATA_CPORTINFO_DRV_INFO(cportinfo) = NULL; + if (sdinfo != NULL) { + kmem_free(sdinfo, sizeof (sata_drive_info_t)); + sata_log(sata_hba_inst, CE_WARN, + "SATA device detached " + "from port %d", cportinfo->cport_addr.cport); + } + + sata_log(sata_hba_inst, CE_WARN, + "SATA port multiplier detected at port %d", + cportinfo->cport_addr.cport); + + mutex_exit(&cportinfo->cport_mutex); + sata_alloc_pmult(sata_hba_inst, sata_device); + sata_show_pmult_info(sata_hba_inst, sata_device); + mutex_enter(&cportinfo->cport_mutex); + + /* + * Mark all the port multiplier port behind the port + * multiplier behind with link events, so that the sata daemon + * will update their status. + */ + pmultinfo = SATA_PMULT_INFO(sata_hba_inst, cport); + pmultinfo->pmult_event_flags |= SATA_EVNT_DEVICE_RESET; mutex_exit(&cportinfo->cport_mutex); return (SATA_SUCCESS); } mutex_exit(&cportinfo->cport_mutex); + /* * Figure out what kind of device we are really * dealing with. Failure of identifying device does not fail this @@ -9675,6 +10120,621 @@ } /* + * Reprobe a controller port that connected to a port multiplier. + * + * NOTE: No Mutex should be hold. + */ +static int +sata_reprobe_pmult(sata_hba_inst_t *sata_hba_inst, sata_device_t *sata_device, + int flag) +{ + _NOTE(ARGUNUSED(flag)) + sata_cport_info_t *cportinfo; + sata_pmult_info_t *pmultinfo; + uint8_t cport = sata_device->satadev_addr.cport; + int rval_probe; + + cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport); + pmultinfo = SATA_PMULT_INFO(sata_hba_inst, cport); + + /* probe port */ + mutex_enter(&cportinfo->cport_mutex); + cportinfo->cport_state &= ~SATA_PORT_STATE_CLEAR_MASK; + cportinfo->cport_state |= SATA_STATE_PROBING; + mutex_exit(&cportinfo->cport_mutex); + + rval_probe = (*SATA_PROBE_PORT_FUNC(sata_hba_inst)) + (SATA_DIP(sata_hba_inst), sata_device); + + mutex_enter(&cportinfo->cport_mutex); + if (rval_probe != SATA_SUCCESS) { + cportinfo->cport_state = SATA_PSTATE_FAILED; + SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_reprobe_pmult: " + "SATA port %d probing failed", cport)); + sata_log(sata_hba_inst, CE_WARN, + "SATA port multiplier detached at port %d", cport); + mutex_exit(&cportinfo->cport_mutex); + sata_free_pmult(sata_hba_inst, sata_device); + return (SATA_FAILURE); + } + + /* + * update sata port state and set device type + */ + sata_update_port_info(sata_hba_inst, sata_device); + cportinfo->cport_state &= ~SATA_STATE_PROBING; + cportinfo->cport_state |= SATA_STATE_PROBED; + + /* + * Sanity check - Port is active? Is the link active? + * Is there any device attached? + */ + if ((cportinfo->cport_state & + (SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) || + (cportinfo->cport_scr.sstatus & SATA_PORT_DEVLINK_UP_MASK) != + SATA_PORT_DEVLINK_UP || + (sata_device->satadev_type == SATA_DTYPE_NONE)) { + cportinfo->cport_dev_type = SATA_DTYPE_NONE; + mutex_exit(&cportinfo->cport_mutex); + sata_free_pmult(sata_hba_inst, sata_device); + sata_log(sata_hba_inst, CE_WARN, + "SATA port multiplier detached at port %d", cport); + return (SATA_SUCCESS); + } + + /* + * Device changed: PMult -> Non-PMult + * + * This situation is uncommon, most possibly being caused by errors + * after which the port multiplier is not correct initialized and + * recognized. In that case the new device will be marked as unknown + * and will not be automatically probed in this routine. Instead + * system administrator could manually restart it via cfgadm(1M). + */ + if (sata_device->satadev_type != SATA_DTYPE_PMULT) { + cportinfo->cport_dev_type = SATA_DTYPE_UNKNOWN; + mutex_exit(&cportinfo->cport_mutex); + sata_free_pmult(sata_hba_inst, sata_device); + sata_log(sata_hba_inst, CE_WARN, + "SATA port multiplier detached at port %d", cport); + return (SATA_FAILURE); + } + + /* + * Now we know it is a port multiplier. However, if this is not the + * previously attached port multiplier - they may have different + * pmport numbers - we need to re-allocate data structures for every + * pmport and drive. + * + * Port multipliers of the same model have identical values in these + * registers, so it is still necessary to update the information of + * all drives attached to the previous port multiplier afterwards. + */ + if ((sata_device->satadev_gscr.gscr0 != pmultinfo->pmult_gscr.gscr0) || + (sata_device->satadev_gscr.gscr1 != pmultinfo->pmult_gscr.gscr1) || + (sata_device->satadev_gscr.gscr2 != pmultinfo->pmult_gscr.gscr2)) { + + /* Device changed: PMult -> another PMult */ + mutex_exit(&cportinfo->cport_mutex); + sata_free_pmult(sata_hba_inst, sata_device); + sata_alloc_pmult(sata_hba_inst, sata_device); + mutex_enter(&cportinfo->cport_mutex); + + SATADBG1(SATA_DBG_PMULT, sata_hba_inst, + "SATA port multiplier [changed] at port %d", cport); + sata_log(sata_hba_inst, CE_WARN, + "SATA port multiplier detected at port %d", cport); + } + + /* + * Mark all the port multiplier port behind the port + * multiplier behind with link events, so that the sata daemon + * will update their status. + */ + pmultinfo->pmult_event_flags |= SATA_EVNT_DEVICE_RESET; + mutex_exit(&cportinfo->cport_mutex); + + return (SATA_SUCCESS); +} + +/* + * Re-probe a port multiplier port, check for a device and attach info + * structures when necessary. Identify Device data is fetched, if possible. + * Assumption: sata address is already validated as port multiplier port. + * SATA_SUCCESS is returned if port is re-probed sucessfully, regardless of + * the presence of a device and its type. + * + * flag arg specifies that the function should try multiple times to identify + * device type and to initialize it, or it should return immediately on failure. + * SATA_DEV_IDENTIFY_RETRY - retry + * SATA_DEV_IDENTIFY_NORETRY - no retry + * + * SATA_FAILURE is returned if one of the operations failed. + * + * This function cannot be called in interrupt context - it may sleep. + * + * NOTE: Should be only called by sata_probe_port() in case target port is a + * port multiplier port. + * NOTE: No Mutex should be hold. + */ +static int +sata_reprobe_pmport(sata_hba_inst_t *sata_hba_inst, sata_device_t *sata_device, + int flag) +{ + sata_cport_info_t *cportinfo = NULL; + sata_pmport_info_t *pmportinfo = NULL; + sata_drive_info_t *sdinfo, *osdinfo; + sata_device_t sdevice; + boolean_t init_device = B_FALSE; + int prev_device_type = SATA_DTYPE_NONE; + int prev_device_settings = 0; + int prev_device_state = 0; + clock_t start_time; + uint8_t cport = sata_device->satadev_addr.cport; + uint8_t pmport = sata_device->satadev_addr.pmport; + int rval; + + cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport); + pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport); + osdinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo); + + if (osdinfo != NULL) { + /* + * We are re-probing port with a previously attached device. + * Save previous device type and settings. + */ + prev_device_type = pmportinfo->pmport_dev_type; + prev_device_settings = osdinfo->satadrv_settings; + prev_device_state = osdinfo->satadrv_state; + } + + start_time = ddi_get_lbolt(); + + /* check parent status */ + mutex_enter(&cportinfo->cport_mutex); + if ((cportinfo->cport_state & + (SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) || + (cportinfo->cport_scr.sstatus & SATA_PORT_DEVLINK_UP_MASK) != + SATA_PORT_DEVLINK_UP) { + mutex_exit(&cportinfo->cport_mutex); + return (SATA_FAILURE); + } + mutex_exit(&cportinfo->cport_mutex); + +retry_probe_pmport: + + /* probe port */ + mutex_enter(&pmportinfo->pmport_mutex); + pmportinfo->pmport_state &= ~SATA_PORT_STATE_CLEAR_MASK; + pmportinfo->pmport_state |= SATA_STATE_PROBING; + mutex_exit(&pmportinfo->pmport_mutex); + + rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst)) + (SATA_DIP(sata_hba_inst), sata_device); + + /* might need retry because we cannot touch registers. */ + if (rval == SATA_FAILURE) { + mutex_enter(&pmportinfo->pmport_mutex); + pmportinfo->pmport_state = SATA_PSTATE_FAILED; + mutex_exit(&pmportinfo->pmport_mutex); + SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_reprobe_pmport: " + "SATA port %d:%d probing failed", + cport, pmport)); + return (SATA_FAILURE); + } else if (rval == SATA_RETRY) { + SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_reprobe_pmport: " + "SATA port %d:%d probing failed, retrying...", + cport, pmport)); + clock_t cur_time = ddi_get_lbolt(); + /* + * A device was not successfully identified or initialized. + * Track retry time for device identification. + */ + if ((cur_time - start_time) < + drv_usectohz(SATA_DEV_REPROBE_TIMEOUT)) { + /* sleep for a while */ + delay(drv_usectohz(SATA_DEV_RETRY_DLY)); + goto retry_probe_pmport; + } else { + mutex_enter(&pmportinfo->pmport_mutex); + if (SATA_PMPORTINFO_DRV_INFO(pmportinfo) != NULL) + SATA_PMPORTINFO_DRV_INFO(pmportinfo)-> + satadrv_state = SATA_DSTATE_FAILED; + mutex_exit(&pmportinfo->pmport_mutex); + return (SATA_SUCCESS); + } + } + + /* + * Sanity check - Controller port is active? Is the link active? + * Is it still a port multiplier? + */ + if ((cportinfo->cport_state & + (SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) || + (cportinfo->cport_scr.sstatus & SATA_PORT_DEVLINK_UP_MASK) != + SATA_PORT_DEVLINK_UP || + (cportinfo->cport_dev_type != SATA_DTYPE_PMULT)) { + /* + * Port in non-usable state or no link active/no + * device. Free info structure. + */ + cportinfo->cport_dev_type = SATA_DTYPE_UNKNOWN; + + sdevice.satadev_addr.cport = cport; + sdevice.satadev_addr.pmport = pmport; + sdevice.satadev_addr.qual = SATA_ADDR_PMULT; + mutex_exit(&cportinfo->cport_mutex); + + sata_free_pmult(sata_hba_inst, &sdevice); + return (SATA_FAILURE); + } + + /* SATA_SUCCESS NOW */ + /* + * update sata port state and set device type + */ + mutex_enter(&pmportinfo->pmport_mutex); + sata_update_pmport_info(sata_hba_inst, sata_device); + pmportinfo->pmport_state &= ~SATA_STATE_PROBING; + + /* + * Sanity check - Port is active? Is the link active? + * Is there any device attached? + */ + if ((pmportinfo->pmport_state & + (SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) || + (pmportinfo->pmport_scr.sstatus & SATA_PORT_DEVLINK_UP_MASK) != + SATA_PORT_DEVLINK_UP) { + /* + * Port in non-usable state or no link active/no device. + * Free info structure if necessary (direct attached drive + * only, for now! + */ + sdinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo); + SATA_PMPORTINFO_DRV_INFO(pmportinfo) = NULL; + /* Add here differentiation for device attached or not */ + pmportinfo->pmport_dev_type = SATA_DTYPE_NONE; + mutex_exit(&pmportinfo->pmport_mutex); + if (sdinfo != NULL) + kmem_free(sdinfo, sizeof (sata_drive_info_t)); + return (SATA_SUCCESS); + } + + pmportinfo->pmport_state |= SATA_STATE_READY; + pmportinfo->pmport_dev_type = sata_device->satadev_type; + sdinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo); + + /* + * If we are re-probing the port, there may be + * sata_drive_info structure attached + * (or sata_pm_info, if PMult is supported). + */ + if (sata_device->satadev_type == SATA_DTYPE_NONE) { + /* + * There is no device, so remove device info structure, + * if necessary. + */ + SATA_PMPORTINFO_DRV_INFO(pmportinfo) = NULL; + pmportinfo->pmport_dev_type = SATA_DTYPE_NONE; + if (sdinfo != NULL) { + kmem_free(sdinfo, sizeof (sata_drive_info_t)); + sata_log(sata_hba_inst, CE_WARN, + "SATA device detached from port %d:%d", + cport, pmport); + } + mutex_exit(&pmportinfo->pmport_mutex); + return (SATA_SUCCESS); + } + + /* this should not be a pmult */ + ASSERT(sata_device->satadev_type != SATA_DTYPE_PMULT); + if (sdinfo == NULL) { + /* + * There is some device attached, but there is + * no sata_drive_info structure - allocate one + */ + mutex_exit(&pmportinfo->pmport_mutex); + sdinfo = kmem_zalloc(sizeof (sata_drive_info_t), + KM_SLEEP); + mutex_enter(&pmportinfo->pmport_mutex); + /* + * Recheck, that the port state did not change when we + * released mutex. + */ + if (pmportinfo->pmport_state & SATA_STATE_READY) { + SATA_PMPORTINFO_DRV_INFO(pmportinfo) = sdinfo; + sdinfo->satadrv_addr = pmportinfo->pmport_addr; + sdinfo->satadrv_addr.qual = SATA_ADDR_DPMPORT; + sdinfo->satadrv_type = SATA_DTYPE_UNKNOWN; + sdinfo->satadrv_state = SATA_STATE_UNKNOWN; + } else { + /* + * Port is not in ready state, we + * cannot attach a device. + */ + mutex_exit(&pmportinfo->pmport_mutex); + kmem_free(sdinfo, sizeof (sata_drive_info_t)); + return (SATA_SUCCESS); + } + /* + * Since we are adding device, presumably new one, + * indicate that it should be initalized, + * as well as some internal framework states). + */ + init_device = B_TRUE; + } + + pmportinfo->pmport_dev_type = SATA_DTYPE_UNKNOWN; + sata_device->satadev_addr.qual = sdinfo->satadrv_addr.qual; + + mutex_exit(&pmportinfo->pmport_mutex); + /* + * Figure out what kind of device we are really + * dealing with. + */ + rval = sata_probe_device(sata_hba_inst, sata_device); + + mutex_enter(&pmportinfo->pmport_mutex); + if (rval == SATA_SUCCESS) { + /* + * If we are dealing with the same type of a device as before, + * restore its settings flags. + */ + if (osdinfo != NULL && + sata_device->satadev_type == prev_device_type) + sdinfo->satadrv_settings = prev_device_settings; + + mutex_exit(&pmportinfo->pmport_mutex); + /* Set initial device features, if necessary */ + if (init_device == B_TRUE) { + rval = sata_initialize_device(sata_hba_inst, sdinfo); + } + if (rval == SATA_SUCCESS) + return (rval); + } else { + /* + * If there was some device info before we probe the device, + * restore previous device setting, so we can retry from scratch + * later. Providing, of course, that device has not disappeared + * during probing process. + */ + if (sata_device->satadev_type != SATA_DTYPE_NONE) { + if (osdinfo != NULL) { + pmportinfo->pmport_dev_type = prev_device_type; + sdinfo->satadrv_type = prev_device_type; + sdinfo->satadrv_state = prev_device_state; + } + } else { + /* device is gone */ + kmem_free(sdinfo, sizeof (sata_drive_info_t)); + pmportinfo->pmport_dev_type = SATA_DTYPE_NONE; + SATA_PMPORTINFO_DRV_INFO(pmportinfo) = NULL; + mutex_exit(&pmportinfo->pmport_mutex); + return (SATA_SUCCESS); + } + mutex_exit(&pmportinfo->pmport_mutex); + } + + if (flag == SATA_DEV_IDENTIFY_RETRY) { + clock_t cur_time = ddi_get_lbolt(); + /* + * A device was not successfully identified or initialized. + * Track retry time for device identification. + */ + if ((cur_time - start_time) < + drv_usectohz(SATA_DEV_REPROBE_TIMEOUT)) { + /* sleep for a while */ + delay(drv_usectohz(SATA_DEV_RETRY_DLY)); + goto retry_probe_pmport; + } else { + mutex_enter(&pmportinfo->pmport_mutex); + if (SATA_PMPORTINFO_DRV_INFO(pmportinfo) != NULL) + SATA_PMPORTINFO_DRV_INFO(pmportinfo)-> + satadrv_state = SATA_DSTATE_FAILED; + mutex_exit(&pmportinfo->pmport_mutex); + } + } + return (SATA_SUCCESS); +} + +/* + * Allocated related structure for a port multiplier and its device ports + * + * Port multiplier should be ready and probed, and related information like + * the number of the device ports should be store in sata_device_t. + * + * NOTE: No Mutex should be hold. + */ +static void +sata_alloc_pmult(sata_hba_inst_t *sata_hba_inst, sata_device_t *sata_device) +{ + dev_info_t *dip = SATA_DIP(sata_hba_inst); + sata_cport_info_t *cportinfo = NULL; + sata_pmult_info_t *pmultinfo = NULL; + sata_pmport_info_t *pmportinfo = NULL; + dev_t minor_number; + char name[16]; + uint8_t cport = sata_device->satadev_addr.cport; + int npmport; + + cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport); + + /* This function might be called while a port-mult is hot-plugged. */ + mutex_enter(&cportinfo->cport_mutex); + + /* dev_type's not updated when get called from sata_reprobe_port() */ + cportinfo->cport_dev_type = SATA_DTYPE_PMULT; + if (SATA_CPORTINFO_PMULT_INFO(cportinfo) == NULL) { + /* Create a pmult_info structure */ + SATA_CPORTINFO_PMULT_INFO(cportinfo) = + kmem_zalloc(sizeof (sata_pmult_info_t), KM_SLEEP); + } + pmultinfo = SATA_CPORTINFO_PMULT_INFO(cportinfo); + + pmultinfo->pmult_addr = sata_device->satadev_addr; + pmultinfo->pmult_addr.qual = SATA_ADDR_PMULT; + pmultinfo->pmult_state = SATA_STATE_PROBING; + pmultinfo->pmult_gscr = sata_device->satadev_gscr; + pmultinfo->pmult_num_dev_ports = sata_device->satadev_add_info; + + /* Initialize pmport_info structure */ + for (npmport = 0; npmport < pmultinfo->pmult_num_dev_ports; + npmport++) { + + /* if everything is allocated, skip */ + if (SATA_PMPORT_INFO(sata_hba_inst, cport, npmport) != NULL) + continue; + + pmportinfo = kmem_zalloc(sizeof (sata_pmport_info_t), KM_SLEEP); + mutex_init(&pmportinfo->pmport_mutex, NULL, MUTEX_DRIVER, NULL); + mutex_exit(&cportinfo->cport_mutex); + + mutex_enter(&pmportinfo->pmport_mutex); + pmportinfo->pmport_addr.cport = cport; + pmportinfo->pmport_addr.pmport = (uint8_t)npmport; + pmportinfo->pmport_addr.qual = SATA_ADDR_PMPORT; + pmportinfo->pmport_state &= ~SATA_PORT_STATE_CLEAR_MASK; + mutex_exit(&pmportinfo->pmport_mutex); + + mutex_enter(&cportinfo->cport_mutex); + SATA_PMPORT_INFO(sata_hba_inst, cport, npmport) = pmportinfo; + + /* Create an attachment point */ + minor_number = SATA_MAKE_AP_MINOR(ddi_get_instance(dip), + cport, (uint8_t)npmport, SATA_ADDR_PMPORT); + (void) sprintf(name, "%d.%d", cport, npmport); + + if (ddi_create_minor_node(dip, name, S_IFCHR, minor_number, + DDI_NT_SATA_ATTACHMENT_POINT, 0) != DDI_SUCCESS) { + sata_log(sata_hba_inst, CE_WARN, "sata_hba_attach: " + "cannot create SATA attachment point for " + "port %d:%d", cport, npmport); + } + } + + pmultinfo->pmult_state &= ~SATA_STATE_PROBING; + pmultinfo->pmult_state |= (SATA_STATE_PROBED|SATA_STATE_READY); + + mutex_exit(&cportinfo->cport_mutex); +} + +/* + * Free data structures when a port multiplier is removed. + * + * NOTE: No Mutex should be hold. + */ +static void +sata_free_pmult(sata_hba_inst_t *sata_hba_inst, sata_device_t *sata_device) +{ + sata_cport_info_t *cportinfo; + sata_pmult_info_t *pmultinfo; + sata_pmport_info_t *pmportinfo; + sata_device_t pmport_device; + sata_drive_info_t *sdinfo; + dev_info_t *tdip; + char name[16]; + uint8_t cport = sata_device->satadev_addr.cport; + int npmport; + + cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport); + + /* This function might be called while port-mult is hot plugged. */ + mutex_enter(&cportinfo->cport_mutex); + + pmultinfo = SATA_CPORTINFO_PMULT_INFO(cportinfo); + ASSERT(pmultinfo != NULL); + + /* Free pmport_info structure */ + for (npmport = 0; npmport < pmultinfo->pmult_num_dev_ports; + npmport++) { + pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, npmport); + if (pmportinfo == NULL) + continue; + mutex_exit(&cportinfo->cport_mutex); + + mutex_enter(&pmportinfo->pmport_mutex); + sdinfo = pmportinfo->pmport_sata_drive; + SATA_PMPORTINFO_DRV_INFO(pmportinfo) = NULL; + mutex_exit(&pmportinfo->pmport_mutex); + + /* Remove attachment point. */ + name[0] = '\0'; + (void) sprintf(name, "%d.%d", cport, npmport); + ddi_remove_minor_node(SATA_DIP(sata_hba_inst), name); + sata_log(sata_hba_inst, CE_NOTE, + "Remove attachment point of port %d:%d", + cport, npmport); + + /* + * Rumove target node + */ + bzero(&pmport_device, sizeof (sata_device_t)); + pmport_device.satadev_rev = SATA_DEVICE_REV; + pmport_device.satadev_addr.cport = cport; + pmport_device.satadev_addr.pmport = (uint8_t)npmport; + pmport_device.satadev_addr.qual = SATA_ADDR_DPMPORT; + + tdip = sata_get_scsi_target_dip(SATA_DIP(sata_hba_inst), + &(pmport_device.satadev_addr)); + if (tdip != NULL && ndi_devi_offline(tdip, + NDI_DEVI_REMOVE) != NDI_SUCCESS) { + /* + * Problem : + * The target node remained attached. + * This happens when the device file was open + * or a node was waiting for resources. + * Cannot do anything about it. + */ + SATA_LOG_D((sata_hba_inst, CE_WARN, + "sata_free_pmult: could not unconfigure device " + "before disconnecting the SATA port %d:%d", + cport, npmport)); + + /* + * Set DEVICE REMOVED state in the target + * node. It will prevent access to the device + * even when a new device is attached, until + * the old target node is released, removed and + * recreated for a new device. + */ + sata_set_device_removed(tdip); + + /* + * Instruct event daemon to try the target + * node cleanup later. + */ + sata_set_target_node_cleanup( + sata_hba_inst, &(pmport_device.satadev_addr)); + + } + mutex_enter(&cportinfo->cport_mutex); + + /* + * Add here differentiation for device attached or not + */ + if (sdinfo != NULL) { + sata_log(sata_hba_inst, CE_WARN, + "SATA device detached from port %d:%d", + cport, npmport); + kmem_free(sdinfo, sizeof (sata_drive_info_t)); + } + + mutex_destroy(&pmportinfo->pmport_mutex); + kmem_free(pmportinfo, sizeof (sata_pmport_info_t)); + } + + kmem_free(pmultinfo, sizeof (sata_pmult_info_t)); + + cportinfo->cport_devp.cport_sata_pmult = NULL; + cportinfo->cport_dev_type = SATA_DTYPE_NONE; + + sata_log(sata_hba_inst, CE_WARN, + "SATA port multiplier detached at port %d", cport); + + mutex_exit(&cportinfo->cport_mutex); +} + +/* * Initialize device * Specified device is initialized to a default state. * @@ -9826,6 +10886,8 @@ * returns 1 if address is valid but device is not attached, * returns -1 if bad address or device is of an unsupported type. * Upon return sata_device argument is set. + * + * Port multiplier is supported now. */ static int sata_validate_scsi_address(sata_hba_inst_t *sata_hba_inst, @@ -9859,8 +10921,7 @@ cportinfo->cport_dev_type == SATA_DTYPE_NONE) goto out; - if (cportinfo->cport_dev_type == SATA_DTYPE_PMULT || - (cportinfo->cport_dev_type & + if ((cportinfo->cport_dev_type & SATA_VALID_DEV_TYPE) == 0) { rval = -1; goto out; @@ -9965,6 +11026,7 @@ static int sata_probe_device(sata_hba_inst_t *sata_hba_inst, sata_device_t *sata_device) { + sata_pmport_info_t *pmportinfo; sata_drive_info_t *sdinfo; sata_drive_info_t new_sdinfo; /* local drive info struct */ int rval; @@ -9978,6 +11040,13 @@ mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, sata_device->satadev_addr.cport))); + if (sata_device->satadev_addr.qual == SATA_ADDR_DPMPORT) { + pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, + sata_device->satadev_addr.cport, + sata_device->satadev_addr.pmport); + ASSERT(pmportinfo != NULL); + } + /* Get pointer to port-linked sata device info structure */ sdinfo = sata_get_device_info(sata_hba_inst, sata_device); if (sdinfo != NULL) { @@ -10041,11 +11110,14 @@ SATA_CPORT_DEV_TYPE(sata_hba_inst, sata_device->satadev_addr.cport) = sdinfo->satadrv_type; - else /* SATA_ADDR_DPMPORT */ + else { /* SATA_ADDR_DPMPORT */ + mutex_enter(&pmportinfo->pmport_mutex); SATA_PMPORT_DEV_TYPE(sata_hba_inst, sata_device->satadev_addr.cport, sata_device->satadev_addr.pmport) = sdinfo->satadrv_type; + mutex_exit(&pmportinfo->pmport_mutex); + } mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, sata_device->satadev_addr.cport))); return (SATA_SUCCESS); @@ -10069,6 +11141,7 @@ SATA_DTYPE_UNKNOWN; else { /* SATA_ADDR_DPMPORT */ + mutex_enter(&pmportinfo->pmport_mutex); if ((SATA_PMULT_INFO(sata_hba_inst, sata_device->satadev_addr.cport) != NULL) && (SATA_PMPORT_INFO(sata_hba_inst, @@ -10078,6 +11151,7 @@ sata_device->satadev_addr.cport, sata_device->satadev_addr.pmport) = SATA_DTYPE_UNKNOWN; + mutex_exit(&pmportinfo->pmport_mutex); } } mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, @@ -10085,7 +11159,6 @@ return (rval); } - /* * Get pointer to sata_drive_info structure. * @@ -10132,6 +11205,11 @@ if (pmport > SATA_NUM_PMPORTS(sata_hba_inst, cport)) return (NULL); + if (!(SATA_PMPORT_STATE(sata_hba_inst, cport, pmport) & + (SATA_STATE_PROBED | SATA_STATE_READY))) + /* Port multiplier port not probed yet */ + return (NULL); + return (SATA_PMPORT_DRV_INFO(sata_hba_inst, cport, pmport)); } @@ -10300,7 +11378,7 @@ cmn_err(CE_CONT, "?\t%s port %d\n", msg_buf, sdinfo->satadrv_addr.cport); else - cmn_err(CE_CONT, "?\t%s port %d pmport %d\n", + cmn_err(CE_CONT, "?\t%s port %d:%d\n", msg_buf, sdinfo->satadrv_addr.cport, sdinfo->satadrv_addr.pmport); @@ -10414,6 +11492,59 @@ } } +/* + * Log/display port multiplier information + */ +static void +sata_show_pmult_info(sata_hba_inst_t *sata_hba_inst, + sata_device_t *sata_device) +{ + _NOTE(ARGUNUSED(sata_hba_inst)) + + char msg_buf[MAXPATHLEN]; + uint32_t gscr0, gscr1, gscr2, gscr64; + + gscr0 = sata_device->satadev_gscr.gscr0; + gscr1 = sata_device->satadev_gscr.gscr1; + gscr2 = sata_device->satadev_gscr.gscr2; + gscr64 = sata_device->satadev_gscr.gscr64; + + cmn_err(CE_CONT, "?Port Multiplier %d device-ports found at port %d", + sata_device->satadev_add_info, sata_device->satadev_addr.cport); + + (void) sprintf(msg_buf, "\tVendor_ID 0x%04x, Module_ID 0x%04x", + gscr0 & 0xffff, (gscr0 >> 16) & 0xffff); + cmn_err(CE_CONT, "?%s", msg_buf); + + (void) strcpy(msg_buf, "\tSupport SATA PMP Spec "); + if (gscr1 & (1 << 3)) + (void) strlcat(msg_buf, "1.2", MAXPATHLEN); + else if (gscr1 & (1 << 2)) + (void) strlcat(msg_buf, "1.1", MAXPATHLEN); + else if (gscr1 & (1 << 1)) + (void) strlcat(msg_buf, "1.0", MAXPATHLEN); + else + (void) strlcat(msg_buf, "unknown", MAXPATHLEN); + cmn_err(CE_CONT, "?%s", msg_buf); + + (void) strcpy(msg_buf, "\tSupport "); + if (gscr64 & (1 << 3)) + (void) strlcat(msg_buf, "Asy-Notif, ", + MAXPATHLEN); + if (gscr64 & (1 << 2)) + (void) strlcat(msg_buf, "Dyn-SSC, ", MAXPATHLEN); + if (gscr64 & (1 << 1)) + (void) strlcat(msg_buf, "Iss-PMREQ, ", MAXPATHLEN); + if (gscr64 & (1 << 0)) + (void) strlcat(msg_buf, "BIST", MAXPATHLEN); + if ((gscr64 & 0xf) == 0) + (void) strlcat(msg_buf, "nothing", MAXPATHLEN); + cmn_err(CE_CONT, "?%s", msg_buf); + + (void) sprintf(msg_buf, "\tNumber of exposed device fan-out ports: %d", + gscr2 & SATA_PMULT_PORTNUM_MASK); + cmn_err(CE_CONT, "?%s", msg_buf); +} /* * sata_save_drive_settings extracts current setting of the device and stores @@ -11128,7 +12259,6 @@ sata_common_free_dma_rsrcs(spx); } - /* * Fetch Device Identify data. * Send DEVICE IDENTIFY or IDENTIFY PACKET DEVICE (depending on a device type) @@ -11631,19 +12761,6 @@ /* - * Update port SCR block - */ -static void -sata_update_port_scr(sata_port_scr_t *port_scr, sata_device_t *device) -{ - port_scr->sstatus = device->satadev_scr.sstatus; - port_scr->serror = device->satadev_scr.serror; - port_scr->scontrol = device->satadev_scr.scontrol; - port_scr->sactive = device->satadev_scr.sactive; - port_scr->snotific = device->satadev_scr.snotific; -} - -/* * Update state and copy port ss* values from passed sata_device structure. * sata_address is validated - if not valid, nothing is changed in sata_scsi * configuration struct. @@ -11655,54 +12772,60 @@ */ static void sata_update_port_info(sata_hba_inst_t *sata_hba_inst, - sata_device_t *sata_device) -{ - ASSERT(mutex_owned(&SATA_CPORT_MUTEX(sata_hba_inst, - sata_device->satadev_addr.cport))); + sata_device_t *sata_device) +{ + sata_cport_info_t *cportinfo; if (sata_device->satadev_addr.qual == SATA_ADDR_CPORT || sata_device->satadev_addr.qual == SATA_ADDR_DCPORT) { - - sata_cport_info_t *cportinfo; - if (SATA_NUM_CPORTS(sata_hba_inst) <= sata_device->satadev_addr.cport) return; cportinfo = SATA_CPORT_INFO(sata_hba_inst, sata_device->satadev_addr.cport); - sata_update_port_scr(&cportinfo->cport_scr, sata_device); + + ASSERT(mutex_owned(&cportinfo->cport_mutex)); + cportinfo->cport_scr = sata_device->satadev_scr; /* Preserve SATA_PSTATE_SHUTDOWN flag */ cportinfo->cport_state &= ~(SATA_PSTATE_PWRON | SATA_PSTATE_PWROFF | SATA_PSTATE_FAILED); cportinfo->cport_state |= sata_device->satadev_state & SATA_PSTATE_VALID; - } else { - sata_pmport_info_t *pmportinfo; - - if ((sata_device->satadev_addr.qual != SATA_ADDR_PMPORT) || - (sata_device->satadev_addr.qual != SATA_ADDR_DPMPORT) || - SATA_NUM_PMPORTS(sata_hba_inst, - sata_device->satadev_addr.cport) < - sata_device->satadev_addr.pmport) - return; - - pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, - sata_device->satadev_addr.cport, - sata_device->satadev_addr.pmport); - sata_update_port_scr(&pmportinfo->pmport_scr, sata_device); - - /* Preserve SATA_PSTATE_SHUTDOWN flag */ - pmportinfo->pmport_state &= - ~(SATA_PSTATE_PWRON | SATA_PSTATE_PWROFF | - SATA_PSTATE_FAILED); - pmportinfo->pmport_state |= - sata_device->satadev_state & SATA_PSTATE_VALID; - } -} - - + } +} + +void +sata_update_pmport_info(sata_hba_inst_t *sata_hba_inst, + sata_device_t *sata_device) +{ + sata_pmport_info_t *pmportinfo; + + if ((sata_device->satadev_addr.qual != SATA_ADDR_PMPORT && + sata_device->satadev_addr.qual != SATA_ADDR_DPMPORT) || + SATA_NUM_PMPORTS(sata_hba_inst, + sata_device->satadev_addr.cport) < + sata_device->satadev_addr.pmport) { + SATADBG1(SATA_DBG_PMULT, sata_hba_inst, + "sata_update_port_info: error address %p.", + &sata_device->satadev_addr); + return; + } + + pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, + sata_device->satadev_addr.cport, + sata_device->satadev_addr.pmport); + + ASSERT(mutex_owned(&pmportinfo->pmport_mutex)); + pmportinfo->pmport_scr = sata_device->satadev_scr; + + /* Preserve SATA_PSTATE_SHUTDOWN flag */ + pmportinfo->pmport_state &= + ~(SATA_PSTATE_PWRON | SATA_PSTATE_PWROFF | SATA_PSTATE_FAILED); + pmportinfo->pmport_state |= + sata_device->satadev_state & SATA_PSTATE_VALID; +} /* * Extract SATA port specification from an IOCTL argument. @@ -11710,7 +12833,7 @@ * This function return the port the user land send us as is, unless it * cannot retrieve port spec, then -1 is returned. * - * Note: Only cport - no port multiplier port. + * Support port multiplier. */ static int32_t sata_get_port_num(sata_hba_inst_t *sata_hba_inst, struct devctl_iocdata *dcp) @@ -11736,22 +12859,41 @@ * way as a scsi target number. * At this moment it carries only cport number. * - * No PMult hotplug support. + * PMult hotplug is supported now. * * Returns dev_info_t pointer if target device was found, NULL otherwise. */ static dev_info_t * -sata_get_target_dip(dev_info_t *dip, int32_t port) +sata_get_target_dip(dev_info_t *dip, uint8_t cport, uint8_t pmport) { dev_info_t *cdip = NULL; int target, tgt; - int ncport; int circ; - - ncport = port & SATA_CFGA_CPORT_MASK; - target = SATA_TO_SCSI_TARGET(ncport, 0, SATA_ADDR_DCPORT); - + uint8_t qual; + + sata_hba_inst_t *sata_hba_inst; + scsi_hba_tran_t *scsi_hba_tran; + + /* Get target id */ + scsi_hba_tran = ddi_get_driver_private(dip); + if (scsi_hba_tran == NULL) + return (NULL); + + sata_hba_inst = scsi_hba_tran->tran_hba_private; + + if (sata_hba_inst == NULL) + return (NULL); + + /* Identify a port-mult by cport_info.cport_dev_type */ + if (SATA_CPORT_DEV_TYPE(sata_hba_inst, cport) == SATA_DTYPE_PMULT) + qual = SATA_ADDR_DPMPORT; + else + qual = SATA_ADDR_DCPORT; + + target = SATA_TO_SCSI_TARGET(cport, pmport, qual); + + /* Retrieve target dip */ ndi_devi_enter(dip, &circ); for (cdip = ddi_get_child(dip); cdip != NULL; ) { dev_info_t *next = ddi_get_next_sibling(cdip); @@ -11783,9 +12925,6 @@ * the AP - it is not a sata_address. * It is a combination of cport, pmport and address qualifier, encoded same * way as a scsi target number. - * At this moment it carries only cport number. - * - * No PMult hotplug support. * * Returns dev_info_t pointer if target device was found, NULL otherwise. */ @@ -11843,27 +12982,32 @@ * Failure of the port_deactivate may keep port in the physically active state, * or may fail the port. * - * NOTE: Port multiplier code is not completed nor tested. + * NOTE: Port multiplier is supported. */ static int sata_ioctl_disconnect(sata_hba_inst_t *sata_hba_inst, sata_device_t *sata_device) { - sata_drive_info_t *sdinfo = NULL; + sata_drive_info_t *sdinfo = NULL, *subsdinfo = NULL; sata_cport_info_t *cportinfo = NULL; sata_pmport_info_t *pmportinfo = NULL; sata_pmult_info_t *pmultinfo = NULL; - dev_info_t *tdip; + sata_device_t subsdevice; int cport, pmport, qual; int rval = SATA_SUCCESS; + int npmport = 0; int rv = 0; cport = sata_device->satadev_addr.cport; pmport = sata_device->satadev_addr.pmport; qual = sata_device->satadev_addr.qual; - ASSERT(qual == SATA_ADDR_CPORT || qual == SATA_ADDR_PMPORT); + ASSERT(qual == SATA_ADDR_DCPORT || qual == SATA_ADDR_DPMPORT); + if (qual == SATA_ADDR_DCPORT) + qual = SATA_ADDR_CPORT; + else + qual = SATA_ADDR_PMPORT; /* * DEVCTL_AP_DISCONNECT invokes sata_hba_inst->satahba_tran-> @@ -11878,131 +13022,184 @@ /* Check the current state of the port */ rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst)) (SATA_DIP(sata_hba_inst), sata_device); - mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); - sata_update_port_info(sata_hba_inst, sata_device); - if (rval != SATA_SUCCESS || - (sata_device->satadev_state & SATA_PSTATE_FAILED) != 0) { - /* Device port status is unknown or it is in failed state */ - if (qual == SATA_ADDR_PMPORT) { - SATA_PMPORT_STATE(sata_hba_inst, cport, pmport) = + + cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport); + + /* + * Processing port mulitiplier + */ + if (qual == SATA_ADDR_CPORT && + SATA_CPORT_DEV_TYPE(sata_hba_inst, cport) == SATA_DTYPE_PMULT) { + mutex_enter(&cportinfo->cport_mutex); + + /* Check controller port status */ + sata_update_port_info(sata_hba_inst, sata_device); + if (rval != SATA_SUCCESS || + (sata_device->satadev_state & SATA_PSTATE_FAILED) != 0) { + /* + * Device port status is unknown or it is in failed + * state + */ + SATA_CPORT_STATE(sata_hba_inst, cport) = SATA_PSTATE_FAILED; SATADBG1(SATA_DBG_IOCTL_IF, sata_hba_inst, "sata_hba_ioctl: connect: failed to deactivate " "SATA port %d", cport); - } else { - SATA_CPORT_STATE(sata_hba_inst, cport) = + mutex_exit(&cportinfo->cport_mutex); + return (EIO); + } + + /* Disconnect all sub-devices. */ + pmultinfo = SATA_CPORTINFO_PMULT_INFO(cportinfo); + if (pmultinfo != NULL) { + + for (npmport = 0; npmport < SATA_NUM_PMPORTS( + sata_hba_inst, cport); npmport ++) { + subsdinfo = SATA_PMPORT_DRV_INFO( + sata_hba_inst, cport, npmport); + if (subsdinfo == NULL) + continue; + + subsdevice.satadev_addr = subsdinfo-> + satadrv_addr; + + mutex_exit(&cportinfo->cport_mutex); + if (sata_ioctl_disconnect(sata_hba_inst, + &subsdevice) == SATA_SUCCESS) { + SATADBG2(SATA_DBG_PMULT, sata_hba_inst, + "[Remove] device at port %d:%d " + "successfully.", cport, npmport); + } + mutex_enter(&cportinfo->cport_mutex); + } + } + + /* Disconnect the port multiplier */ + cportinfo->cport_state &= ~SATA_STATE_READY; + mutex_exit(&cportinfo->cport_mutex); + + sata_device->satadev_addr.qual = qual; + rval = (*SATA_PORT_DEACTIVATE_FUNC(sata_hba_inst)) + (SATA_DIP(sata_hba_inst), sata_device); + + sata_gen_sysevent(sata_hba_inst, &sata_device->satadev_addr, + SE_NO_HINT); + + mutex_enter(&cportinfo->cport_mutex); + sata_update_port_info(sata_hba_inst, sata_device); + if (rval != SATA_SUCCESS && + sata_device->satadev_state & SATA_PSTATE_FAILED) { + cportinfo->cport_state = SATA_PSTATE_FAILED; + rv = EIO; + } else { + cportinfo->cport_state |= SATA_PSTATE_SHUTDOWN; + } + mutex_exit(&cportinfo->cport_mutex); + + return (rv); + } + + /* + * Process non-port-multiplier device - it could be a drive connected + * to a port multiplier port or a controller port. + */ + if (qual == SATA_ADDR_PMPORT) { + pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport); + mutex_enter(&pmportinfo->pmport_mutex); + sata_update_pmport_info(sata_hba_inst, sata_device); + if (rval != SATA_SUCCESS || + (sata_device->satadev_state & SATA_PSTATE_FAILED) != 0) { + SATA_PMPORT_STATE(sata_hba_inst, cport, pmport) = SATA_PSTATE_FAILED; SATADBG2(SATA_DBG_IOCTL_IF, sata_hba_inst, "sata_hba_ioctl: connect: failed to deactivate " "SATA port %d:%d", cport, pmport); - } - mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, - cport)->cport_mutex); - return (EIO); - } - /* - * Set port's dev_state to not ready - this will disable - * an access to a potentially attached device. - */ - cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport); - if (qual == SATA_ADDR_PMPORT) { - pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport); + mutex_exit(&pmportinfo->pmport_mutex); + return (EIO); + } + if (pmportinfo->pmport_dev_type != SATA_DTYPE_NONE) { sdinfo = pmportinfo->pmport_sata_drive; ASSERT(sdinfo != NULL); } + + /* + * Set port's dev_state to not ready - this will disable + * an access to a potentially attached device. + */ pmportinfo->pmport_state &= ~SATA_STATE_READY; - } else { - /* Assuming cport */ - - if (cportinfo->cport_dev_type != SATA_DTYPE_NONE) { - if (cportinfo->cport_dev_type == SATA_DTYPE_PMULT) { - pmultinfo = - cportinfo->cport_devp.cport_sata_pmult; - ASSERT(pmultinfo != NULL); - } else { - sdinfo = cportinfo->cport_devp.cport_sata_drive; - } + + /* Remove and release sata_drive info structure. */ + if (sdinfo != NULL) { + if ((sdinfo->satadrv_type & + SATA_VALID_DEV_TYPE) != 0) { + /* + * If a target node exists, try to offline + * a device and remove target node. + */ + mutex_exit(&pmportinfo->pmport_mutex); + (void) sata_offline_device(sata_hba_inst, + sata_device, sdinfo); + mutex_enter(&pmportinfo->pmport_mutex); + } + + SATA_PMPORTINFO_DRV_INFO(pmportinfo) = NULL; + pmportinfo->pmport_dev_type = SATA_DTYPE_NONE; + (void) kmem_free((void *)sdinfo, + sizeof (sata_drive_info_t)); + } + mutex_exit(&pmportinfo->pmport_mutex); + + } else if (qual == SATA_ADDR_CPORT) { + mutex_enter(&cportinfo->cport_mutex); + sata_update_port_info(sata_hba_inst, sata_device); + if (rval != SATA_SUCCESS || + (sata_device->satadev_state & SATA_PSTATE_FAILED) != 0) { + /* + * Device port status is unknown or it is in failed + * state + */ + SATA_CPORT_STATE(sata_hba_inst, cport) = + SATA_PSTATE_FAILED; + SATADBG1(SATA_DBG_IOCTL_IF, sata_hba_inst, + "sata_hba_ioctl: connect: failed to deactivate " + "SATA port %d", cport); + mutex_exit(&cportinfo->cport_mutex); + return (EIO); + } + + if (cportinfo->cport_dev_type == SATA_DTYPE_PMULT) { + pmultinfo = SATA_CPORTINFO_PMULT_INFO(cportinfo); + ASSERT(pmultinfo != NULL); + } else if (cportinfo->cport_dev_type != SATA_DTYPE_NONE) { + sdinfo = SATA_CPORTINFO_DRV_INFO(cportinfo); + ASSERT(sdinfo != NULL); } cportinfo->cport_state &= ~SATA_STATE_READY; - } - if (sdinfo != NULL) { - if ((sdinfo->satadrv_type & (SATA_VALID_DEV_TYPE)) != 0) { - /* - * If a target node exists, try to offline - * a device and remove target node. - */ - mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, - cport)->cport_mutex); - /* We are addressing attached device, not a port */ - sata_device->satadev_addr.qual = - sdinfo->satadrv_addr.qual; - tdip = sata_get_scsi_target_dip(SATA_DIP(sata_hba_inst), - &sata_device->satadev_addr); - if (tdip != NULL && ndi_devi_offline(tdip, - NDI_DEVI_REMOVE) != NDI_SUCCESS) { - /* - * Problem - * The target node remained attached. - * This happens when the device file was open - * or a node was waiting for resources. - * Cannot do anything about it. - */ - if (qual == SATA_ADDR_CPORT) { - SATA_LOG_D((sata_hba_inst, CE_WARN, - "sata_hba_ioctl: disconnect: could " - "not unconfigure device before " - "disconnecting the SATA port %d", - cport)); - } else { - SATA_LOG_D((sata_hba_inst, CE_WARN, - "sata_hba_ioctl: disconnect: could " - "not unconfigure device before " - "disconnecting the SATA port %d:%d", - cport, pmport)); - } - /* - * Set DEVICE REMOVED state in the target - * node. It will prevent access to the device - * even when a new device is attached, until - * the old target node is released, removed and - * recreated for a new device. - */ - sata_set_device_removed(tdip); - - /* - * Instruct event daemon to try the target - * node cleanup later. - */ - sata_set_target_node_cleanup( - sata_hba_inst, &sata_device->satadev_addr); - } - mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, - cport)->cport_mutex); - } - - /* Remove and release sata_drive info structure. */ - if (pmportinfo != NULL) { - SATA_PMPORT_DRV_INFO(sata_hba_inst, cport, pmport) = - NULL; - pmportinfo->pmport_dev_type = SATA_DTYPE_NONE; - } else { + + if (sdinfo != NULL) { + if ((sdinfo->satadrv_type & + SATA_VALID_DEV_TYPE) != 0) { + /* + * If a target node exists, try to offline + * a device and remove target node. + */ + mutex_exit(&cportinfo->cport_mutex); + (void) sata_offline_device(sata_hba_inst, + sata_device, sdinfo); + mutex_enter(&cportinfo->cport_mutex); + } + SATA_CPORTINFO_DRV_INFO(cportinfo) = NULL; cportinfo->cport_dev_type = SATA_DTYPE_NONE; - } - (void) kmem_free((void *)sdinfo, sizeof (sata_drive_info_t)); - } -#if 0 - else if (pmultinfo != NULL) { - /* - * Port Multiplier itself needs special handling. - * All device ports need to be processed here! - */ - } -#endif - mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); + (void) kmem_free((void *)sdinfo, + sizeof (sata_drive_info_t)); + } + mutex_exit(&cportinfo->cport_mutex); + } + /* Just ask HBA driver to deactivate port */ - /* sata_device->satadev_addr.qual = SATA_ADDR_DCPORT; */ + sata_device->satadev_addr.qual = qual; rval = (*SATA_PORT_DEACTIVATE_FUNC(sata_hba_inst)) (SATA_DIP(sata_hba_inst), sata_device); @@ -12014,33 +13211,46 @@ sata_gen_sysevent(sata_hba_inst, &sata_device->satadev_addr, SE_NO_HINT); - mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); - sata_update_port_info(sata_hba_inst, sata_device); - - if (rval != SATA_SUCCESS) { - /* - * Port deactivation failure - do not - * change port state unless the state - * returned by HBA indicates a port failure. - * NOTE: device structures were released, so devices now are - * invisible! Port reset is needed to re-enumerate devices. - */ - if (sata_device->satadev_state & SATA_PSTATE_FAILED) { - if (pmportinfo != NULL) - pmportinfo->pmport_state = SATA_PSTATE_FAILED; - else - cportinfo->cport_state = SATA_PSTATE_FAILED; + if (qual == SATA_ADDR_PMPORT) { + mutex_enter(&pmportinfo->pmport_mutex); + sata_update_pmport_info(sata_hba_inst, sata_device); + + if (rval != SATA_SUCCESS && + sata_device->satadev_state & SATA_PSTATE_FAILED) { + /* + * Port deactivation failure - do not change port + * state unless the state returned by HBA indicates a + * port failure. + * + * NOTE: device structures were released, so devices + * now are invisible! Port reset is needed to + * re-enumerate devices. + */ + pmportinfo->pmport_state = SATA_PSTATE_FAILED; rv = EIO; - } - } else { - /* - * Deactivation succeded. From now on the sata framework - * will not care what is happening to the device, until - * the port is activated again. - */ - cportinfo->cport_state |= SATA_PSTATE_SHUTDOWN; - } - mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); + } else { + /* + * Deactivation succeded. From now on the sata framework + * will not care what is happening to the device, until + * the port is activated again. + */ + pmportinfo->pmport_state |= SATA_PSTATE_SHUTDOWN; + } + mutex_exit(&pmportinfo->pmport_mutex); + } else if (qual == SATA_ADDR_CPORT) { + mutex_enter(&cportinfo->cport_mutex); + sata_update_port_info(sata_hba_inst, sata_device); + + if (rval != SATA_SUCCESS && + sata_device->satadev_state & SATA_PSTATE_FAILED) { + cportinfo->cport_state = SATA_PSTATE_FAILED; + rv = EIO; + } else { + cportinfo->cport_state |= SATA_PSTATE_SHUTDOWN; + } + mutex_exit(&cportinfo->cport_mutex); + } + return (rv); } @@ -12066,21 +13276,29 @@ * This operation may remove port failed state and will * try to make port active and in good standing. * - * NOTE: Port multiplier code is not completed nor tested. + * NOTE: Port multiplier is supported. */ static int sata_ioctl_connect(sata_hba_inst_t *sata_hba_inst, sata_device_t *sata_device) { - int cport, pmport, qual; + sata_pmport_info_t *pmportinfo = NULL; + uint8_t cport, pmport, qual; int rv = 0; cport = sata_device->satadev_addr.cport; pmport = sata_device->satadev_addr.pmport; qual = sata_device->satadev_addr.qual; - ASSERT(qual == SATA_ADDR_CPORT || qual == SATA_ADDR_PMPORT); + ASSERT(qual == SATA_ADDR_DCPORT || qual == SATA_ADDR_DPMPORT); + if (qual == SATA_ADDR_DCPORT) + qual = SATA_ADDR_CPORT; + else + qual = SATA_ADDR_PMPORT; + + if (qual == SATA_ADDR_PMPORT) + pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport); /* * DEVCTL_AP_CONNECT would invoke sata_hba_inst-> @@ -12098,40 +13316,46 @@ /* * Port activation failure. */ - mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, - cport)->cport_mutex); - sata_update_port_info(sata_hba_inst, sata_device); - if (sata_device->satadev_state & SATA_PSTATE_FAILED) { - if (qual == SATA_ADDR_DCPORT) { + if (qual == SATA_ADDR_CPORT) { + mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, + cport)->cport_mutex); + sata_update_port_info(sata_hba_inst, sata_device); + if (sata_device->satadev_state & SATA_PSTATE_FAILED) { SATA_CPORT_STATE(sata_hba_inst, cport) = SATA_PSTATE_FAILED; SATADBG1(SATA_DBG_IOCTL_IF, sata_hba_inst, "sata_hba_ioctl: connect: failed to " "activate SATA port %d", cport); - } else { /* port multiplier device port */ + } + mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, + cport)->cport_mutex); + } else { /* port multiplier device port */ + mutex_enter(&pmportinfo->pmport_mutex); + sata_update_pmport_info(sata_hba_inst, sata_device); + if (sata_device->satadev_state & SATA_PSTATE_FAILED) { SATA_PMPORT_STATE(sata_hba_inst, cport, pmport) = SATA_PSTATE_FAILED; SATADBG2(SATA_DBG_IOCTL_IF, sata_hba_inst, "sata_hba_ioctl: connect: failed to " "activate SATA port %d:%d", cport, pmport); - - } - } - mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, - cport)->cport_mutex); - SATADBG2(SATA_DBG_IOCTL_IF, sata_hba_inst, - "sata_hba_ioctl: connect: failed to activate SATA " - "port %d:%d", cport, pmport); + } + mutex_exit(&pmportinfo->pmport_mutex); + } return (EIO); } /* Virgin port state - will be updated by the port re-probe. */ - mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); - if (qual == SATA_ADDR_CPORT) + if (qual == SATA_ADDR_CPORT) { + mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, + cport)->cport_mutex); SATA_CPORT_STATE(sata_hba_inst, cport) = 0; - else /* port multiplier device port */ + mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, + cport)->cport_mutex); + } else { /* port multiplier device port */ + mutex_enter(&pmportinfo->pmport_mutex); SATA_PMPORT_STATE(sata_hba_inst, cport, pmport) = 0; - mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); + mutex_exit(&pmportinfo->pmport_mutex); + } /* * Probe the port to find its state and attached device. @@ -12154,15 +13378,22 @@ if (sata_device->satadev_type != SATA_DTYPE_NONE) { if (qual == SATA_ADDR_CPORT) { - sata_log(sata_hba_inst, CE_WARN, - "SATA device detected at port %d", cport); - if (sata_device->satadev_type == SATA_DTYPE_UNKNOWN) { + if (sata_device->satadev_type == SATA_DTYPE_PMULT) { + sata_log(sata_hba_inst, CE_WARN, + "SATA port multiplier detected " + "at port %d", cport); + } else { + sata_log(sata_hba_inst, CE_WARN, + "SATA device detected at port %d", cport); + if (sata_device->satadev_type == + SATA_DTYPE_UNKNOWN) { /* * A device was not successfully identified */ sata_log(sata_hba_inst, CE_WARN, "Could not identify SATA " "device at port %d", cport); + } } } else { /* port multiplier device port */ sata_log(sata_hba_inst, CE_WARN, @@ -12250,7 +13481,7 @@ * If target node does not exist, a new target node is created. In both cases * an attempt is made to online (configure) the device. * - * NOTE: Port multiplier code is not completed nor tested. + * NOTE: Port multiplier is supported. */ static int sata_ioctl_configure(sata_hba_inst_t *sata_hba_inst, @@ -12271,22 +13502,37 @@ /* Get current port state */ rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst)) (SATA_DIP(sata_hba_inst), sata_device); - mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); - sata_update_port_info(sata_hba_inst, sata_device); - - if (rval != SATA_SUCCESS || - (sata_device->satadev_state & SATA_PSTATE_FAILED) != 0) { - /* - * Obviously, device on a failed port is not visible - */ - mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); - return (ENXIO); - } cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport); - if (qual == SATA_ADDR_PMPORT) + if (qual == SATA_ADDR_DPMPORT) { pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport); - mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); + mutex_enter(&pmportinfo->pmport_mutex); + sata_update_pmport_info(sata_hba_inst, sata_device); + if (rval != SATA_SUCCESS || + (sata_device->satadev_state & SATA_PSTATE_FAILED) != 0) { + /* + * Obviously, device on a failed port is not visible + */ + mutex_exit(&pmportinfo->pmport_mutex); + return (ENXIO); + } + mutex_exit(&pmportinfo->pmport_mutex); + } else { + mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, + cport)->cport_mutex); + sata_update_port_info(sata_hba_inst, sata_device); + if (rval != SATA_SUCCESS || + (sata_device->satadev_state & SATA_PSTATE_FAILED) != 0) { + /* + * Obviously, device on a failed port is not visible + */ + mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, + cport)->cport_mutex); + return (ENXIO); + } + mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, + cport)->cport_mutex); + } if ((sata_device->satadev_state & SATA_PSTATE_SHUTDOWN) != 0) { /* need to activate port */ @@ -12304,40 +13550,52 @@ * unless the state returned by HBA indicates a port * failure. */ - mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, - cport)->cport_mutex); - sata_update_port_info(sata_hba_inst, sata_device); - if (sata_device->satadev_state & SATA_PSTATE_FAILED) { - if (qual == SATA_ADDR_PMPORT) + if (qual == SATA_ADDR_DPMPORT) { + mutex_enter(&pmportinfo->pmport_mutex); + sata_update_pmport_info(sata_hba_inst, + sata_device); + if (sata_device->satadev_state & + SATA_PSTATE_FAILED) pmportinfo->pmport_state = SATA_PSTATE_FAILED; - else + mutex_exit(&pmportinfo->pmport_mutex); + } else { + mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, + cport)->cport_mutex); + sata_update_port_info(sata_hba_inst, + sata_device); + if (sata_device->satadev_state & + SATA_PSTATE_FAILED) cportinfo->cport_state = SATA_PSTATE_FAILED; - } - mutex_exit(&SATA_CPORT_INFO( - sata_hba_inst, cport)->cport_mutex); - SATA_LOG_D((sata_hba_inst, CE_WARN, - "sata_hba_ioctl: configure: " - "failed to activate SATA port %d:%d", - cport, pmport)); - return (EIO); - } - /* - * Generate sysevent - EC_DR / ESC_DR_AP_STATE_CHANGE - * without the hint. - */ - sata_gen_sysevent(sata_hba_inst, - &sata_device->satadev_addr, SE_NO_HINT); - - mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)-> - cport_mutex); - /* Virgin port state */ - if (qual == SATA_ADDR_PMPORT) - pmportinfo->pmport_state = 0; - else - cportinfo->cport_state = 0; - mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); + mutex_exit(&SATA_CPORT_INFO( + sata_hba_inst, cport)->cport_mutex); + } + } + SATA_LOG_D((sata_hba_inst, CE_WARN, + "sata_hba_ioctl: configure: " + "failed to activate SATA port %d:%d", + cport, pmport)); + return (EIO); + } + /* + * Generate sysevent - EC_DR / ESC_DR_AP_STATE_CHANGE + * without the hint. + */ + sata_gen_sysevent(sata_hba_inst, + &sata_device->satadev_addr, SE_NO_HINT); + + /* Virgin port state */ + if (qual == SATA_ADDR_DPMPORT) { + mutex_enter(&pmportinfo->pmport_mutex); + pmportinfo->pmport_state = 0; + mutex_exit(&pmportinfo->pmport_mutex); + } else { + mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, + cport)-> cport_mutex); + cportinfo->cport_state = 0; + mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, + cport)->cport_mutex); } /* * Always reprobe port, to get current device info. @@ -12347,7 +13605,7 @@ return (EIO); if (sata_device->satadev_type != SATA_DTYPE_NONE && target == FALSE) { - if (qual == SATA_ADDR_PMPORT) { + if (qual == SATA_ADDR_DPMPORT) { /* * That's the transition from "inactive" port * to active one with device attached. @@ -12379,7 +13637,7 @@ * For now, configure only disks and other valid target devices. */ if (!(sata_device->satadev_type & SATA_VALID_DEV_TYPE)) { - if (qual == SATA_ADDR_CPORT) { + if (qual == SATA_ADDR_DCPORT) { if (sata_device->satadev_type == SATA_DTYPE_UNKNOWN) { /* * A device was not successfully identified @@ -12411,7 +13669,7 @@ * to clear device reset condition. */ mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); - if (qual == SATA_ADDR_PMPORT) + if (qual == SATA_ADDR_DPMPORT) sata_device->satadev_addr.qual = SATA_ADDR_DPMPORT; else sata_device->satadev_addr.qual = SATA_ADDR_DCPORT; @@ -12466,29 +13724,34 @@ "%d:%d failed", cport, pmport)); return (EIO); } - mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, - cport)->cport_mutex); - - if (qual == SATA_ADDR_DPMPORT) + + if (qual == SATA_ADDR_DPMPORT) { + mutex_enter(&pmportinfo->pmport_mutex); pmportinfo->pmport_tgtnode_clean = B_TRUE; - else + mutex_exit(&pmportinfo->pmport_mutex); + } else { + mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, + cport)->cport_mutex); cportinfo-> cport_tgtnode_clean = B_TRUE; - - mutex_exit(&SATA_CPORT_INFO( - sata_hba_inst, cport)->cport_mutex); + mutex_exit(&SATA_CPORT_INFO( + sata_hba_inst, cport)->cport_mutex); + } } else { /* * No target node - need to create a new target node. */ - mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)-> - cport_mutex); - if (qual == SATA_ADDR_DPMPORT) + if (qual == SATA_ADDR_DPMPORT) { + mutex_enter(&pmportinfo->pmport_mutex); pmportinfo->pmport_tgtnode_clean = B_TRUE; - else + mutex_exit(&pmportinfo->pmport_mutex); + } else { + mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)-> + cport_mutex); cportinfo-> cport_tgtnode_clean = B_TRUE; - - mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)-> - cport_mutex); + mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)-> + cport_mutex); + } + tdip = sata_create_target_node(SATA_DIP(sata_hba_inst), sata_hba_inst, &sata_device->satadev_addr); if (tdip == NULL) { @@ -12510,7 +13773,7 @@ * Even if the unconfigure fails, proceed with the * port deactivation. * - * NOTE: Port Multiplier code is not completed and tested. + * NOTE: Port Multiplier is supported now. */ static int @@ -12519,10 +13782,13 @@ { int cport, pmport, qual; int rval, rv = 0; + int npmport; sata_cport_info_t *cportinfo; - sata_pmport_info_t *pmportinfo = NULL; + sata_pmport_info_t *pmportinfo; + sata_pmult_info_t *pmultinfo; dev_info_t *tdip; sata_drive_info_t *sdinfo = NULL; + sata_device_t subsdevice; /* Sanity check */ if (SATA_PORT_DEACTIVATE_FUNC(sata_hba_inst) == NULL) @@ -12532,33 +13798,101 @@ pmport = sata_device->satadev_addr.pmport; qual = sata_device->satadev_addr.qual; + /* SCSI_TO_SATA_ADDR_QUAL() translate ap_id into a device qualifier */ + ASSERT(qual == SATA_ADDR_DCPORT || qual == SATA_ADDR_DPMPORT); + if (qual == SATA_ADDR_DCPORT) + qual = SATA_ADDR_CPORT; + else + qual = SATA_ADDR_PMPORT; + + cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport); + if (qual == SATA_ADDR_PMPORT) + pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport); + + /* + * Processing port multiplier + */ + if (qual == SATA_ADDR_CPORT && + SATA_CPORT_DEV_TYPE(sata_hba_inst, cport) == SATA_DTYPE_PMULT) { + mutex_enter(&cportinfo->cport_mutex); + + /* Deactivate all sub-deices */ + pmultinfo = SATA_CPORTINFO_PMULT_INFO(cportinfo); + if (pmultinfo != NULL) { + for (npmport = 0; npmport < SATA_NUM_PMPORTS( + sata_hba_inst, cport); npmport++) { + + subsdevice.satadev_addr.cport = cport; + subsdevice.satadev_addr.pmport = + (uint8_t)npmport; + subsdevice.satadev_addr.qual = + SATA_ADDR_DPMPORT; + + SATADBG2(SATA_DBG_PMULT, sata_hba_inst, + "sata_hba_ioctl: deactivate: trying to " + "deactivate SATA port %d:%d", + cport, npmport); + + mutex_exit(&cportinfo->cport_mutex); + if (sata_ioctl_deactivate(sata_hba_inst, + &subsdevice) == SATA_SUCCESS) { + SATADBG2(SATA_DBG_PMULT, sata_hba_inst, + "[Deactivate] device at port %d:%d " + "successfully.", cport, npmport); + } + mutex_enter(&cportinfo->cport_mutex); + } + } + + /* Deactivate the port multiplier now. */ + cportinfo->cport_state &= ~SATA_STATE_READY; + mutex_exit(&cportinfo->cport_mutex); + + sata_device->satadev_addr.qual = qual; + rval = (*SATA_PORT_DEACTIVATE_FUNC(sata_hba_inst)) + (SATA_DIP(sata_hba_inst), sata_device); + + sata_gen_sysevent(sata_hba_inst, &sata_device->satadev_addr, + SE_NO_HINT); + + mutex_enter(&cportinfo->cport_mutex); + sata_update_port_info(sata_hba_inst, sata_device); + if (rval != SATA_SUCCESS) { + if (sata_device->satadev_state & SATA_PSTATE_FAILED) { + cportinfo->cport_state = SATA_PSTATE_FAILED; + } + rv = EIO; + } else { + cportinfo->cport_state |= SATA_PSTATE_SHUTDOWN; + } + mutex_exit(&cportinfo->cport_mutex); + + return (rv); + } + + /* + * Process non-port-multiplier device - it could be a drive connected + * to a port multiplier port or a controller port. + */ mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); - cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport); if (qual == SATA_ADDR_CPORT) { sata_device->satadev_addr.qual = SATA_ADDR_DCPORT; if (cportinfo->cport_dev_type != SATA_DTYPE_NONE) { - /* - * For now, assume that port multiplier is not - * supported, i.e. deal only with valid devices - */ + /* deal only with valid devices */ if ((cportinfo->cport_dev_type & SATA_VALID_DEV_TYPE) != 0) sdinfo = SATA_CPORTINFO_DRV_INFO(cportinfo); - /* - * If attached device is a port multiplier, we will - * have to unconfigure all devices attached to the - * port multiplier. Add this code here. - */ } cportinfo->cport_state &= ~SATA_STATE_READY; } else { /* Port multiplier device port */ - pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport); + mutex_enter(&pmportinfo->pmport_mutex); sata_device->satadev_addr.qual = SATA_ADDR_DPMPORT; if (pmportinfo->pmport_dev_type != SATA_DTYPE_NONE && (pmportinfo->pmport_dev_type & SATA_VALID_DEV_TYPE) != 0) sdinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo); pmportinfo->pmport_state &= ~SATA_STATE_READY; + mutex_exit(&pmportinfo->pmport_mutex); } if (sdinfo != NULL) { @@ -12611,17 +13945,22 @@ SATA_CPORTINFO_DRV_INFO(cportinfo) = NULL; cportinfo->cport_dev_type = SATA_DTYPE_NONE; } else { /* port multiplier device port */ + mutex_enter(&pmportinfo->pmport_mutex); SATA_PMPORTINFO_DRV_INFO(pmportinfo) = NULL; pmportinfo->pmport_dev_type = SATA_DTYPE_NONE; + mutex_exit(&pmportinfo->pmport_mutex); } (void) kmem_free((void *)sdinfo, sizeof (sata_drive_info_t)); } + if (qual == SATA_ADDR_CPORT) { cportinfo->cport_state &= ~(SATA_STATE_PROBED | SATA_STATE_PROBING); - } else { /* port multiplier device port */ + } else if (qual == SATA_ADDR_PMPORT) { + mutex_enter(&pmportinfo->pmport_mutex); pmportinfo->pmport_state &= ~(SATA_STATE_PROBED | SATA_STATE_PROBING); + mutex_exit(&pmportinfo->pmport_mutex); } mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); @@ -12658,6 +13997,7 @@ cportinfo->cport_state |= SATA_PSTATE_SHUTDOWN; } } else { + mutex_enter(&pmportinfo->pmport_mutex); if (rval != SATA_SUCCESS) { if (sata_device->satadev_state & SATA_PSTATE_FAILED) { SATA_PMPORT_STATE(sata_hba_inst, cport, @@ -12671,6 +14011,7 @@ } else { pmportinfo->pmport_state |= SATA_PSTATE_SHUTDOWN; } + mutex_exit(&pmportinfo->pmport_mutex); } mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); @@ -12681,7 +14022,7 @@ /* * Process ioctl port activate request. * - * NOTE: Port multiplier code is not completed nor tested. + * NOTE: Port multiplier is supported now. */ static int sata_ioctl_activate(sata_hba_inst_t *sata_hba_inst, @@ -12700,8 +14041,19 @@ pmport = sata_device->satadev_addr.pmport; qual = sata_device->satadev_addr.qual; + cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport); + + /* + * The qual translate from ap_id (by SCSI_TO_SATA_ADDR_QUAL()) + * is a device. But what we are dealing with is port/pmport. + */ + ASSERT(qual == SATA_ADDR_DCPORT || qual == SATA_ADDR_DPMPORT); + if (qual == SATA_ADDR_DCPORT) + sata_device->satadev_addr.qual = qual = SATA_ADDR_CPORT; + else + sata_device->satadev_addr.qual = qual = SATA_ADDR_PMPORT; + mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); - cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport); if (qual == SATA_ADDR_PMPORT) { pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport); if (pmportinfo->pmport_state & SATA_PSTATE_SHUTDOWN || @@ -12725,9 +14077,11 @@ cport)->cport_mutex); sata_update_port_info(sata_hba_inst, sata_device); if (sata_device->satadev_state & SATA_PSTATE_FAILED) { - if (qual == SATA_ADDR_PMPORT) + if (qual == SATA_ADDR_PMPORT) { + mutex_enter(&pmportinfo->pmport_mutex); pmportinfo->pmport_state = SATA_PSTATE_FAILED; - else + mutex_exit(&pmportinfo->pmport_mutex); + } else cportinfo->cport_state = SATA_PSTATE_FAILED; mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, @@ -12740,9 +14094,11 @@ mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); } mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); - if (qual == SATA_ADDR_PMPORT) + if (qual == SATA_ADDR_PMPORT) { + mutex_enter(&pmportinfo->pmport_mutex); pmportinfo->pmport_state &= ~SATA_PSTATE_SHUTDOWN; - else + mutex_exit(&pmportinfo->pmport_mutex); + } else cportinfo->cport_state &= ~SATA_PSTATE_SHUTDOWN; mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); @@ -12788,13 +14144,6 @@ sata_log(sata_hba_inst, CE_WARN, "SATA port multiplier detected at port %d", cport); - /* - * Because the detected device is a port - * multiplier, we need to reprobe every device - * port on the port multiplier and show every - * device found attached. - * Add this code here. - */ } } } @@ -12806,7 +14155,7 @@ /* * Process ioctl reset port request. * - * NOTE: Port multiplier code is not completed nor tested. + * NOTE: Port-Multiplier is supported. */ static int sata_ioctl_reset_port(sata_hba_inst_t *sata_hba_inst, @@ -12819,6 +14168,16 @@ pmport = sata_device->satadev_addr.pmport; qual = sata_device->satadev_addr.qual; + /* + * The qual translate from ap_id (by SCSI_TO_SATA_ADDR_QUAL()) + * is a device. But what we are dealing with is port/pmport. + */ + if (qual == SATA_ADDR_DCPORT) + sata_device->satadev_addr.qual = qual = SATA_ADDR_CPORT; + else + sata_device->satadev_addr.qual = qual = SATA_ADDR_PMPORT; + ASSERT(qual == SATA_ADDR_CPORT || qual == SATA_ADDR_PMPORT); + /* Sanity check */ if (SATA_RESET_DPORT_FUNC(sata_hba_inst) == NULL) { SATA_LOG_D((sata_hba_inst, CE_WARN, @@ -12839,9 +14198,14 @@ if (qual == SATA_ADDR_CPORT) SATA_CPORT_STATE(sata_hba_inst, cport) = SATA_PSTATE_FAILED; - else + else { + mutex_enter(&SATA_PMPORT_MUTEX(sata_hba_inst, cport, + pmport)); SATA_PMPORT_STATE(sata_hba_inst, cport, pmport) = SATA_PSTATE_FAILED; + mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, cport, + pmport)); + } mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)-> cport_mutex); rv = EIO; @@ -12862,13 +14226,14 @@ /* * Process ioctl reset device request. * - * NOTE: Port multiplier code is not completed nor tested. + * NOTE: Port multiplier is supported. */ static int sata_ioctl_reset_device(sata_hba_inst_t *sata_hba_inst, sata_device_t *sata_device) { - sata_drive_info_t *sdinfo; + sata_drive_info_t *sdinfo = NULL; + sata_pmult_info_t *pmultinfo = NULL; int cport, pmport; int rv = 0; @@ -12884,17 +14249,21 @@ pmport = sata_device->satadev_addr.pmport; mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); - if (sata_device->satadev_addr.qual == SATA_ADDR_CPORT) { - sata_device->satadev_addr.qual = SATA_ADDR_DCPORT; - sdinfo = SATA_CPORT_DRV_INFO(sata_hba_inst, - sata_device->satadev_addr.cport); + if (sata_device->satadev_addr.qual == SATA_ADDR_DCPORT) { + if (SATA_CPORT_DEV_TYPE(sata_hba_inst, cport) == + SATA_DTYPE_PMULT) + pmultinfo = SATA_CPORT_INFO(sata_hba_inst, cport)-> + cport_devp.cport_sata_pmult; + else + sdinfo = SATA_CPORT_DRV_INFO(sata_hba_inst, + sata_device->satadev_addr.cport); } else { /* port multiplier */ sata_device->satadev_addr.qual = SATA_ADDR_DPMPORT; sdinfo = SATA_PMPORT_DRV_INFO(sata_hba_inst, sata_device->satadev_addr.cport, sata_device->satadev_addr.pmport); } - if (sdinfo == NULL) { + if (sdinfo == NULL && pmultinfo == NULL) { mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); return (EINVAL); } @@ -12914,15 +14283,23 @@ * or port disconnect/connect and re-probing is * needed to change it's state */ - sdinfo->satadrv_state &= ~SATA_STATE_READY; - sdinfo->satadrv_state |= SATA_DSTATE_FAILED; + if (sdinfo != NULL) { + sdinfo->satadrv_state &= ~SATA_STATE_READY; + sdinfo->satadrv_state |= SATA_DSTATE_FAILED; + } else if (pmultinfo != NULL) { + pmultinfo->pmult_state &= ~SATA_STATE_READY; + pmultinfo->pmult_state |= SATA_DSTATE_FAILED; + } + mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); rv = EIO; } /* * If attached device was a port multiplier, some extra processing - * may be needed, to bring it back (if port re-probing did not handle - * it). Add such code here. + * may be needed to bring it back. SATA specification requies a + * mandatory software reset on host port to reliably enumerate a port + * multiplier, the HBA driver should handle that after reset + * operation. */ return (rv); } @@ -12930,8 +14307,6 @@ /* * Process ioctl reset all request. - * - * NOTE: Port multiplier code is not completed nor tested. */ static int sata_ioctl_reset_all(sata_hba_inst_t *sata_hba_inst) @@ -12969,12 +14344,12 @@ rv = EBUSY; break; } else { + /* + * It is enough to lock cport in command-based + * switching mode. + */ SATA_CPORT_INFO(sata_hba_inst, tcport)-> cport_event_flags |= SATA_APCTL_LOCK_PORT_BUSY; - /* - * If there is a port multiplier attached, we may need - * to lock its port as well. If so, add such code here. - */ } mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, tcport)-> cport_mutex); @@ -12983,7 +14358,7 @@ if (rv == 0) { /* * All cports were successfully locked. - * Reset main SATA controller only for now - no PMult. + * Reset main SATA controller. * Set the device address to port 0, to have a valid device * address. */ @@ -13001,7 +14376,6 @@ * Because ports were reset, port states are unknown. * They should be re-probed to get their state and * attached devices should be reinitialized. - * Add code here to re-probe port multiplier device ports. */ for (tcport = 0; tcport < SATA_NUM_CPORTS(sata_hba_inst); tcport++) { @@ -13009,6 +14383,12 @@ sata_device.satadev_addr.pmport = tpmport; sata_device.satadev_addr.qual = SATA_ADDR_CPORT; + /* + * The sata_reprobe_port() will mark a + * SATA_EVNT_DEVICE_RESET event on the port + * multiplier, all its sub-ports will be probed by + * sata daemon afterwards. + */ if (sata_reprobe_port(sata_hba_inst, &sata_device, SATA_DEV_IDENTIFY_RETRY) != SATA_SUCCESS) rv = EIO; @@ -13070,9 +14450,14 @@ if (qual == SATA_ADDR_CPORT) SATA_CPORT_STATE(sata_hba_inst, cport) = SATA_PSTATE_FAILED; - else /* port ultiplier device port */ + else { /* port multiplier device port */ + mutex_enter(&SATA_PMPORT_MUTEX(sata_hba_inst, + cport, pmport)); SATA_PMPORT_STATE(sata_hba_inst, cport, pmport) = SATA_PSTATE_FAILED; + mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, + cport, pmport)); + } mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)-> cport_mutex); @@ -13102,20 +14487,23 @@ * SCSI_TO_SATA_PMPORT extracts pmport number and * SCSI_TO_SATA_ADDR_QUAL extracts port mulitplier qualifier flag. * - * For now, support is for cports only - no port multiplier device ports. + * Port multiplier is supported. */ static void sata_cfgadm_state(sata_hba_inst_t *sata_hba_inst, int32_t port, devctl_ap_state_t *ap_state) { - uint16_t cport; - int port_state; + uint8_t cport, pmport, qual; + uint32_t port_state, pmult_state; + uint32_t dev_type; sata_drive_info_t *sdinfo; - /* Cport only */ cport = SCSI_TO_SATA_CPORT(port); - + pmport = SCSI_TO_SATA_PMPORT(port); + qual = SCSI_TO_SATA_ADDR_QUAL(port); + + /* Check cport state */ port_state = SATA_CPORT_STATE(sata_hba_inst, cport); if (port_state & SATA_PSTATE_SHUTDOWN || port_state & SATA_PSTATE_FAILED) { @@ -13129,11 +14517,34 @@ return; } - /* Need to check pmult device port here as well, when supported */ + /* cport state is okay. Now check pmport state */ + if (qual == SATA_ADDR_DPMPORT || qual == SATA_ADDR_PMPORT) { + /* Sanity check */ + if (SATA_CPORT_DEV_TYPE(sata_hba_inst, cport) != + SATA_DTYPE_PMULT || SATA_PMPORT_INFO(sata_hba_inst, + cport, pmport) == NULL) + return; + port_state = SATA_PMPORT_STATE(sata_hba_inst, cport, pmport); + if (port_state & SATA_PSTATE_SHUTDOWN || + port_state & SATA_PSTATE_FAILED) { + ap_state->ap_rstate = AP_RSTATE_DISCONNECTED; + ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED; + if (port_state & SATA_PSTATE_FAILED) + ap_state->ap_condition = AP_COND_FAILED; + else + ap_state->ap_condition = AP_COND_UNKNOWN; + + return; + } + } /* Port is enabled and ready */ - - switch (SATA_CPORT_DEV_TYPE(sata_hba_inst, cport)) { + if (qual == SATA_ADDR_DCPORT || qual == SATA_ADDR_CPORT) + dev_type = SATA_CPORT_DEV_TYPE(sata_hba_inst, cport); + else + dev_type = SATA_PMPORT_DEV_TYPE(sata_hba_inst, cport, pmport); + + switch (dev_type) { case SATA_DTYPE_NONE: { ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED; @@ -13142,8 +14553,30 @@ ap_state->ap_rstate = AP_RSTATE_EMPTY; break; } - case SATA_DTYPE_UNKNOWN: - case SATA_DTYPE_PMULT: /* Until PMult is supported */ + case SATA_DTYPE_PMULT: + { + /* Need to check port multiplier state */ + ASSERT(qual == SATA_ADDR_DCPORT); + pmult_state = SATA_PMULT_INFO(sata_hba_inst, cport)-> + pmult_state; + if (pmult_state & (SATA_PSTATE_SHUTDOWN|SATA_PSTATE_FAILED)) { + ap_state->ap_rstate = AP_RSTATE_DISCONNECTED; + ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED; + if (pmult_state & SATA_PSTATE_FAILED) + ap_state->ap_condition = AP_COND_FAILED; + else + ap_state->ap_condition = AP_COND_UNKNOWN; + + return; + } + + /* Port multiplier is not configurable */ + ap_state->ap_ostate = AP_OSTATE_CONFIGURED; + ap_state->ap_rstate = AP_RSTATE_CONNECTED; + ap_state->ap_condition = AP_COND_OK; + break; + } + case SATA_DTYPE_ATADISK: case SATA_DTYPE_ATAPICD: case SATA_DTYPE_ATAPITAPE: @@ -13154,7 +14587,7 @@ int circ; dip = SATA_DIP(sata_hba_inst); - tdip = sata_get_target_dip(dip, port); + tdip = sata_get_target_dip(dip, cport, pmport); ap_state->ap_rstate = AP_RSTATE_CONNECTED; if (tdip != NULL) { ndi_devi_enter(dip, &circ); @@ -13225,7 +14658,9 @@ /* * Process ioctl get device path request. * - * NOTE: Port multiplier code is not completed nor tested. + * NOTE: Port multiplier has no target dip. Devices connected to port + * multiplier have target node attached to the HBA node. The only difference + * between them and the directly-attached device node is a target address. */ static int sata_ioctl_get_device_path(sata_hba_inst_t *sata_hba_inst, @@ -13270,7 +14705,7 @@ /* * Process ioctl get attachment point type request. * - * NOTE: Port multiplier code is not completed nor tested. + * NOTE: Port multiplier is supported. */ static int sata_ioctl_get_ap_type(sata_hba_inst_t *sata_hba_inst, @@ -13280,7 +14715,7 @@ const char *ap_type; int dev_type; - if (sata_device->satadev_addr.qual == SATA_ADDR_CPORT) + if (sata_device->satadev_addr.qual == SATA_ADDR_DCPORT) dev_type = SATA_CPORT_DEV_TYPE(sata_hba_inst, sata_device->satadev_addr.cport); else /* pmport */ @@ -13307,7 +14742,7 @@ break; case SATA_DTYPE_PMULT: - ap_type = "pmult"; + ap_type = "sata-pmult"; break; case SATA_DTYPE_UNKNOWN: @@ -13343,7 +14778,7 @@ * This operation should return to cfgadm the device model * information string * - * NOTE: Port multiplier code is not completed nor tested. + * NOTE: Port multiplier is supported. */ static int sata_ioctl_get_model_info(sata_hba_inst_t *sata_hba_inst, @@ -13355,7 +14790,7 @@ mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, sata_device->satadev_addr.cport)->cport_mutex); - if (sata_device->satadev_addr.qual == SATA_ADDR_CPORT) + if (sata_device->satadev_addr.qual == SATA_ADDR_DCPORT) sdinfo = SATA_CPORT_DRV_INFO(sata_hba_inst, sata_device->satadev_addr.cport); else /* port multiplier */ @@ -13401,7 +14836,7 @@ * This operation should return to cfgadm the device firmware revision * information string * - * NOTE: Port multiplier code is not completed nor tested. + * Port multiplier is supported. */ static int sata_ioctl_get_revfirmware_info(sata_hba_inst_t *sata_hba_inst, @@ -13413,7 +14848,7 @@ mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, sata_device->satadev_addr.cport)->cport_mutex); - if (sata_device->satadev_addr.qual == SATA_ADDR_CPORT) + if (sata_device->satadev_addr.qual == SATA_ADDR_DCPORT) sdinfo = SATA_CPORT_DRV_INFO(sata_hba_inst, sata_device->satadev_addr.cport); else /* port multiplier */ @@ -13458,7 +14893,7 @@ * Process ioctl get device serial number info request. * This operation should return to cfgadm the device serial number string. * - * NOTE: Port multiplier code is not completed nor tested. + * NOTE: Port multiplier is supported. */ static int sata_ioctl_get_serialnumber_info(sata_hba_inst_t *sata_hba_inst, @@ -13470,7 +14905,7 @@ mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, sata_device->satadev_addr.cport)->cport_mutex); - if (sata_device->satadev_addr.qual == SATA_ADDR_CPORT) + if (sata_device->satadev_addr.qual == SATA_ADDR_DCPORT) sdinfo = SATA_CPORT_DRV_INFO(sata_hba_inst, sata_device->satadev_addr.cport); else /* port multiplier */ @@ -14719,6 +16154,7 @@ { sata_hba_inst_t *sata_hba_inst = NULL; sata_address_t *saddr; + sata_pmult_info_t *pmultinfo; sata_drive_info_t *sdinfo; sata_port_stats_t *pstats; sata_cport_info_t *cportinfo; @@ -14758,10 +16194,6 @@ saddr = &sata_device->satadev_addr; if (saddr->cport >= SATA_NUM_CPORTS(sata_hba_inst)) return; - if (saddr->qual == SATA_ADDR_PMPORT || - saddr->qual == SATA_ADDR_DPMPORT) - /* Port Multiplier not supported yet */ - return; cport = saddr->cport; pmport = saddr->pmport; @@ -14773,20 +16205,74 @@ * Port has to be initialized, or we cannot accept an event. */ if ((saddr->qual & (SATA_ADDR_CPORT | SATA_ADDR_PMPORT | - SATA_ADDR_DCPORT | SATA_ADDR_DPMPORT)) != 0) { - if ((saddr->qual & (SATA_ADDR_CPORT | SATA_ADDR_DCPORT)) != 0) { - mutex_enter(&sata_hba_inst->satahba_mutex); - cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport); - mutex_exit(&sata_hba_inst->satahba_mutex); - if (cportinfo == NULL || cportinfo->cport_state == 0) - return; - } else { - mutex_enter(&sata_hba_inst->satahba_mutex); - pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, - cport, pmport); - mutex_exit(&sata_hba_inst->satahba_mutex); - if (pmportinfo == NULL || pmportinfo->pmport_state == 0) - return; + SATA_ADDR_DCPORT | SATA_ADDR_DPMPORT | SATA_ADDR_PMULT)) != 0) { + mutex_enter(&sata_hba_inst->satahba_mutex); + cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport); + mutex_exit(&sata_hba_inst->satahba_mutex); + if (cportinfo == NULL || cportinfo->cport_state == 0) + return; + } + + if ((saddr->qual & (SATA_ADDR_PMULT | SATA_ADDR_PMPORT | + SATA_ADDR_DPMPORT)) != 0) { + if (cportinfo->cport_dev_type != SATA_DTYPE_PMULT) { + SATA_LOG_D((sata_hba_inst, CE_WARN, + "sata_hba_event_notify: Non-pmult device (0x%x)" + "is attached to port %d, ignore pmult/pmport " + "event 0x%x", cportinfo->cport_dev_type, + cport, event)); + return; + } + + mutex_enter(&cportinfo->cport_mutex); + pmultinfo = SATA_PMULT_INFO(sata_hba_inst, cport); + mutex_exit(&cportinfo->cport_mutex); + + /* + * The daemon might be processing attachment of port + * multiplier, in that case we should ignore events on its + * sub-devices. + * + * NOTE: Only pmult_state is checked in sata_hba_event_notify. + * The pmport_state is checked by sata daemon. + */ + if (pmultinfo == NULL || + pmultinfo->pmult_state == SATA_STATE_UNKNOWN) { + SATA_LOG_D((sata_hba_inst, CE_WARN, + "sata_hba_event_notify: pmult is not" + "available at port %d:%d, ignore event 0x%x", + cport, pmport, event)); + return; + } + } + + if ((saddr->qual & + (SATA_ADDR_PMPORT | SATA_ADDR_DPMPORT)) != 0) { + + mutex_enter(&cportinfo->cport_mutex); + if (pmport > SATA_NUM_PMPORTS(sata_hba_inst, cport)) { + SATA_LOG_D((sata_hba_inst, CE_WARN, + "sata_hba_event_notify: invalid/" + "un-implemented port %d:%d (%d ports), " + "ignore event 0x%x", cport, pmport, + SATA_NUM_PMPORTS(sata_hba_inst, cport), event)); + mutex_exit(&cportinfo->cport_mutex); + return; + } + mutex_exit(&cportinfo->cport_mutex); + + mutex_enter(&sata_hba_inst->satahba_mutex); + pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, + cport, pmport); + mutex_exit(&sata_hba_inst->satahba_mutex); + + /* pmport is implemented/valid? */ + if (pmportinfo == NULL) { + SATA_LOG_D((sata_hba_inst, CE_WARN, + "sata_hba_event_notify: invalid/" + "un-implemented port %d:%d, ignore " + "event 0x%x", cport, pmport, event)); + return; } } @@ -14816,6 +16302,8 @@ &(SATA_CPORT_INFO(sata_hba_inst, cport))-> cport_stats; } else { + mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport))); + mutex_enter(&pmportinfo->pmport_mutex); /* Port multiplier's device port event */ (SATA_PMPORT_INFO(sata_hba_inst, cport, pmport))-> pmport_event_flags |= @@ -14823,6 +16311,8 @@ pstats = &(SATA_PMPORT_INFO(sata_hba_inst, cport, pmport))-> pmport_stats; + mutex_exit(&pmportinfo->pmport_mutex); + mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, cport))); } /* @@ -14915,6 +16405,42 @@ event & ~SATA_EVNT_DRIVE_EVENTS); } mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport))); + } else if (saddr->qual == SATA_ADDR_PMULT) { + mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, cport))); + + /* qualify this event */ + if ((event & (SATA_EVNT_DEVICE_RESET | + SATA_EVNT_PMULT_LINK_CHANGED)) == 0) { + /* Invalid event for a port multiplier */ + (void) sprintf(buf2, err_msg_evnt_2, + event & SATA_EVNT_DEVICE_RESET); + mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport))); + goto event_info; + } + + pmultinfo = SATA_PMULT_INFO(sata_hba_inst, cport); + + if (event & SATA_EVNT_DEVICE_RESET) { + + SATADBG1(SATA_DBG_PMULT, sata_hba_inst, + "[Reset] port-mult on cport %d", cport); + pmultinfo->pmult_event_flags |= + SATA_EVNT_DEVICE_RESET; + (void) strlcat(buf1, "pmult reset, ", + SATA_EVENT_MAX_MSG_LENGTH); + } + + if (event & SATA_EVNT_PMULT_LINK_CHANGED) { + + SATADBG1(SATA_DBG_PMULT, sata_hba_inst, + "pmult link changed on cport %d", cport); + pmultinfo->pmult_event_flags |= + SATA_EVNT_PMULT_LINK_CHANGED; + (void) strlcat(buf1, "pmult link changed, ", + SATA_EVENT_MAX_MSG_LENGTH); + } + mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport))); + } else { if (saddr->qual != SATA_ADDR_NULL) { /* Wrong address qualifier */ @@ -15113,7 +16639,7 @@ * * NOTE: At the moment, device event processing is limited to hard disks * only. - * cports only are supported - no pmports. + * Port multiplier is supported now. */ static void sata_process_controller_events(sata_hba_inst_t *sata_hba_inst) @@ -15122,6 +16648,7 @@ uint32_t event_flags; sata_address_t *saddr; sata_cport_info_t *cportinfo; + sata_pmult_info_t *pmultinfo; SATADBG1(SATA_DBG_EVENTS_CNTRL, sata_hba_inst, "Processing controller %d event(s)", @@ -15236,7 +16763,38 @@ sata_hba_inst, saddr); } } + + + /* + * Scan port multiplier and all its sub-ports event flags. + * The events are marked by + * (1) sata_pmult_info.pmult_event_flags + * (2) sata_pmport_info.pmport_event_flags + */ mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, ncport))); + if (cportinfo->cport_dev_type == SATA_DTYPE_PMULT) { + /* + * There should be another extra check: this + * port multiplier still exists? + */ + pmultinfo = SATA_PMULT_INFO(sata_hba_inst, + ncport); + + if (pmultinfo != NULL) { + mutex_exit(&(SATA_CPORT_MUTEX( + sata_hba_inst, ncport))); + sata_process_pmult_events( + sata_hba_inst, ncport); + mutex_enter(&(SATA_CPORT_MUTEX( + sata_hba_inst, ncport))); + } else { + SATADBG1(SATA_DBG_PMULT, sata_hba_inst, + "Port-multiplier is gone. " + "Ignore all sub-device events " + "at port %d.", ncport); + } + } + if ((SATA_CPORT_DEV_TYPE(sata_hba_inst, ncport) != SATA_DTYPE_NONE) && (SATA_CPORT_DRV_INFO(sata_hba_inst, ncport) != NULL)) { @@ -15258,6 +16816,190 @@ } /* + * Specific port multiplier instance event processing. At the moment, device + * event processing is limited to link/attach event only. + * + * NOTE: power management event is not supported yet. + */ +static void +sata_process_pmult_events(sata_hba_inst_t *sata_hba_inst, uint8_t cport) +{ + sata_cport_info_t *cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport); + sata_pmult_info_t *pmultinfo; + sata_pmport_info_t *pmportinfo; + sata_address_t *saddr; + sata_device_t sata_device; + uint32_t event_flags; + int npmport; + int rval; + + SATADBG2(SATA_DBG_EVENTS_CNTRL|SATA_DBG_PMULT, sata_hba_inst, + "Processing pmult event(s) on cport %d of controller %d", + cport, ddi_get_instance(SATA_DIP(sata_hba_inst))); + + /* First process events on port multiplier */ + mutex_enter(&cportinfo->cport_mutex); + pmultinfo = SATA_PMULT_INFO(sata_hba_inst, cport); + event_flags = pmultinfo->pmult_event_flags; + + /* + * Reset event (of port multiplier) has higher priority because the + * port multiplier itself might be failed or removed after reset. + */ + if (event_flags & SATA_EVNT_DEVICE_RESET) { + /* + * The status of the sub-links are uncertain, + * so mark all sub-ports as RESET + */ + for (npmport = 0; npmport < SATA_NUM_PMPORTS( + sata_hba_inst, cport); npmport ++) { + pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, + cport, npmport); + if (pmportinfo == NULL) { + /* That's weird. */ + SATA_LOG_D((sata_hba_inst, CE_WARN, + "sata_hba_event_notify: " + "invalid/un-implemented " + "port %d:%d (%d ports), ", + cport, npmport, SATA_NUM_PMPORTS( + sata_hba_inst, cport))); + continue; + } + + mutex_enter(&pmportinfo->pmport_mutex); + + /* Mark all pmport to unknow state. */ + pmportinfo->pmport_state = SATA_STATE_UNKNOWN; + /* Mark all pmports with link events. */ + pmportinfo->pmport_event_flags = + (SATA_EVNT_LINK_ESTABLISHED|SATA_EVNT_LINK_LOST); + mutex_exit(&pmportinfo->pmport_mutex); + } + + } else if (event_flags & SATA_EVNT_PMULT_LINK_CHANGED) { + /* + * We need probe the port multiplier to know what has + * happened. + */ + bzero(&sata_device, sizeof (sata_device_t)); + sata_device.satadev_rev = SATA_DEVICE_REV; + sata_device.satadev_addr.cport = cport; + sata_device.satadev_addr.pmport = SATA_PMULT_HOSTPORT; + sata_device.satadev_addr.qual = SATA_ADDR_PMULT; + + mutex_exit(&cportinfo->cport_mutex); + rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst)) + (SATA_DIP(sata_hba_inst), &sata_device); + mutex_enter(&cportinfo->cport_mutex); + if (rval != SATA_SUCCESS) { + /* Something went wrong? Fail the port */ + cportinfo->cport_state = SATA_PSTATE_FAILED; + mutex_exit(&cportinfo->cport_mutex); + SATA_LOG_D((sata_hba_inst, CE_WARN, + "SATA port %d probing failed", cport)); + + /* PMult structure must be released. */ + sata_free_pmult(sata_hba_inst, &sata_device); + return; + } + + sata_update_port_info(sata_hba_inst, &sata_device); + + /* + * Sanity check - Port is active? Is the link active? + * The device is still a port multiplier? + */ + if ((cportinfo->cport_state & + (SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) || + ((cportinfo->cport_scr.sstatus & + SATA_PORT_DEVLINK_UP_MASK) != SATA_PORT_DEVLINK_UP) || + (cportinfo->cport_dev_type != SATA_DTYPE_PMULT)) { + mutex_exit(&cportinfo->cport_mutex); + + /* PMult structure must be released. */ + sata_free_pmult(sata_hba_inst, &sata_device); + return; + } + + /* Probed succeed, set port ready. */ + cportinfo->cport_state |= + SATA_STATE_PROBED | SATA_STATE_READY; + } + + /* Release port multiplier event flags. */ + pmultinfo->pmult_event_flags &= + ~(SATA_EVNT_DEVICE_RESET|SATA_EVNT_PMULT_LINK_CHANGED); + mutex_exit(&cportinfo->cport_mutex); + + /* + * Check all sub-links. + */ + for (npmport = 0; npmport < SATA_NUM_PMPORTS(sata_hba_inst, cport); + npmport ++) { + pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, npmport); + mutex_enter(&pmportinfo->pmport_mutex); + event_flags = pmportinfo->pmport_event_flags; + mutex_exit(&pmportinfo->pmport_mutex); + saddr = &pmportinfo->pmport_addr; + + if ((event_flags & + (SATA_EVNT_PORT_EVENTS | SATA_EVNT_DRIVE_EVENTS)) != 0) { + /* + * Got port multiplier port event. + * We need some hierarchy of event processing as they + * are affecting each other: + * 1. device detached/attached + * 2. link events - link events may trigger device + * detached or device attached events in some + * circumstances. + */ + if (event_flags & SATA_EVNT_DEVICE_DETACHED) { + sata_process_pmdevice_detached(sata_hba_inst, + saddr); + } + if (event_flags & SATA_EVNT_DEVICE_ATTACHED) { + sata_process_pmdevice_attached(sata_hba_inst, + saddr); + } + if (event_flags & SATA_EVNT_LINK_ESTABLISHED || + event_flags & SATA_EVNT_LINK_LOST) { + sata_process_pmport_link_events(sata_hba_inst, + saddr); + } + if (event_flags & SATA_EVNT_TARGET_NODE_CLEANUP) { + sata_process_target_node_cleanup( + sata_hba_inst, saddr); + } + } + + /* Checking drive event(s). */ + mutex_enter(&pmportinfo->pmport_mutex); + if (pmportinfo->pmport_dev_type != SATA_DTYPE_NONE && + pmportinfo->pmport_sata_drive != NULL) { + event_flags = pmportinfo->pmport_sata_drive-> + satadrv_event_flags; + if (event_flags & (SATA_EVNT_DEVICE_RESET | + SATA_EVNT_INPROC_DEVICE_RESET)) { + + /* Have device event */ + sata_process_pmdevice_reset(sata_hba_inst, + saddr); + } + } + mutex_exit(&pmportinfo->pmport_mutex); + + /* Release PORT_BUSY flag */ + mutex_enter(&cportinfo->cport_mutex); + cportinfo->cport_event_flags &= ~SATA_EVNT_LOCK_PORT_BUSY; + mutex_exit(&cportinfo->cport_mutex); + } + + SATADBG2(SATA_DBG_EVENTS_CNTRL|SATA_DBG_PMULT, sata_hba_inst, + "[DONE] pmult event(s) on cport %d of controller %d", + cport, ddi_get_instance(SATA_DIP(sata_hba_inst))); +} + +/* * Process HBA power level change reported by HBA driver. * Not implemented at this time - event is ignored. */ @@ -15355,6 +17097,16 @@ return; } + if ((SATA_CPORT_DEV_TYPE(sata_hba_inst, saddr->cport) == + SATA_DTYPE_PMULT)) { + /* + * Should not happened: this is already handled in + * sata_hba_event_notify() + */ + mutex_exit(&cportinfo->cport_mutex); + goto done; + } + if ((SATA_CPORT_DEV_TYPE(sata_hba_inst, saddr->cport) & SATA_VALID_DEV_TYPE) == 0) { /* @@ -15570,6 +17322,243 @@ /* + * Port Multiplier Port Device Reset Event processing. + * + * NOTE: This function has to be entered with pmport mutex held. It exits with + * mutex held as well, but can release mutex during the processing. + */ +static void +sata_process_pmdevice_reset(sata_hba_inst_t *sata_hba_inst, + sata_address_t *saddr) +{ + sata_drive_info_t old_sdinfo; /* local copy of the drive info */ + sata_drive_info_t *sdinfo = NULL; + sata_cport_info_t *cportinfo = NULL; + sata_pmport_info_t *pmportinfo = NULL; + sata_pmult_info_t *pminfo = NULL; + sata_device_t sata_device; + uint8_t cport = saddr->cport; + uint8_t pmport = saddr->pmport; + int rval; + + SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst, + "Processing drive reset at port %d:%d", cport, pmport); + + cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport); + pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport); + sdinfo = SATA_PMPORT_DRV_INFO(sata_hba_inst, cport, pmport); + + /* + * If the port is in SHUTDOWN or FAILED state, or device is in FAILED + * state, ignore reset event. + */ + if (((cportinfo->cport_state & + (SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) || + (sdinfo->satadrv_state & SATA_DSTATE_FAILED) != 0) { + sdinfo->satadrv_event_flags &= + ~(SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET); + return; + } + + if ((pmportinfo->pmport_dev_type & SATA_VALID_DEV_TYPE) == 0) { + /* + * This should not happen - coding error. + * But we can recover, so do not panic, just clean up + * and if in debug mode, log the message. + */ +#ifdef SATA_DEBUG + sata_log(sata_hba_inst, CE_WARN, + "sata_process_pmdevice_reset: " + "Invalid device type with sdinfo!", NULL); +#endif + sdinfo->satadrv_event_flags = 0; + return; + } + +#ifdef SATA_DEBUG + if ((sdinfo->satadrv_event_flags & + (SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) == 0) { + /* Nothing to do */ + /* Something is weird - why we are processing dev reset? */ + SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst, + "No device reset event!!!!", NULL); + + return; + } + if ((sdinfo->satadrv_event_flags & + (SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) == + (SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) { + /* Something is weird - new device reset event */ + SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst, + "Overlapping device reset events!", NULL); + } +#endif + SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst, + "Processing port %d:%d device reset", cport, pmport); + + /* Clear event flag */ + sdinfo->satadrv_event_flags &= ~SATA_EVNT_DEVICE_RESET; + + /* It seems that we always need to check the port state first */ + sata_device.satadev_rev = SATA_DEVICE_REV; + sata_device.satadev_addr = *saddr; + /* + * We have to exit mutex, because the HBA probe port function may + * block on its own mutex. + */ + mutex_exit(&pmportinfo->pmport_mutex); + rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst)) + (SATA_DIP(sata_hba_inst), &sata_device); + mutex_enter(&pmportinfo->pmport_mutex); + + sata_update_pmport_info(sata_hba_inst, &sata_device); + if (rval != SATA_SUCCESS) { + /* Something went wrong? Fail the port */ + pmportinfo->pmport_state = SATA_PSTATE_FAILED; + sdinfo = SATA_PMPORT_DRV_INFO(sata_hba_inst, saddr->cport, + saddr->pmport); + if (sdinfo != NULL) + sdinfo->satadrv_event_flags = 0; + mutex_exit(&pmportinfo->pmport_mutex); + SATA_LOG_D((sata_hba_inst, CE_WARN, + "SATA port %d:%d probing failed", + saddr->cport, saddr->pmport)); + mutex_enter(&pmportinfo->pmport_mutex); + return; + } + if ((sata_device.satadev_scr.sstatus & + SATA_PORT_DEVLINK_UP_MASK) != + SATA_PORT_DEVLINK_UP || + sata_device.satadev_type == SATA_DTYPE_NONE) { + /* + * No device to process, anymore. Some other event processing + * would or have already performed port info cleanup. + * To be safe (HBA may need it), request clearing device + * reset condition. + */ + sdinfo = SATA_PMPORT_DRV_INFO(sata_hba_inst, saddr->cport, + saddr->pmport); + if (sdinfo != NULL) { + sdinfo->satadrv_event_flags &= + ~SATA_EVNT_INPROC_DEVICE_RESET; + /* must clear flags on cport */ + pminfo = SATA_PMULT_INFO(sata_hba_inst, + saddr->cport); + pminfo->pmult_event_flags |= + SATA_EVNT_CLEAR_DEVICE_RESET; + } + return; + } + + sdinfo = SATA_PMPORT_DRV_INFO(sata_hba_inst, saddr->cport, + saddr->pmport); + if (sdinfo == NULL) { + return; + } + if ((sdinfo->satadrv_event_flags & + SATA_EVNT_INPROC_DEVICE_RESET) == 0) { + /* + * Start tracking time for device feature restoration and + * identification. Save current time (lbolt value). + */ + sdinfo->satadrv_reset_time = ddi_get_lbolt(); + } + /* Mark device reset processing as active */ + sdinfo->satadrv_event_flags |= SATA_EVNT_INPROC_DEVICE_RESET; + + old_sdinfo = *sdinfo; /* local copy of the drive info */ + mutex_exit(&pmportinfo->pmport_mutex); + + if (sata_set_drive_features(sata_hba_inst, &old_sdinfo, 1) == + SATA_FAILURE) { + /* + * Restoring drive setting failed. + * Probe the port first, to check if the port state has changed + */ + sata_device.satadev_rev = SATA_DEVICE_REV; + sata_device.satadev_addr = *saddr; + sata_device.satadev_addr.qual = SATA_ADDR_PMPORT; + + /* probe port */ + rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst)) + (SATA_DIP(sata_hba_inst), &sata_device); + mutex_enter(&pmportinfo->pmport_mutex); + if (rval == SATA_SUCCESS && + (sata_device.satadev_state & + (SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) == 0 && + (sata_device.satadev_scr.sstatus & + SATA_PORT_DEVLINK_UP_MASK) == SATA_PORT_DEVLINK_UP && + sata_device.satadev_type != SATA_DTYPE_NONE) { + /* + * We may retry this a bit later - in-process reset + * condition should be already set. + * Track retry time for device identification. + */ + if ((pmportinfo->pmport_dev_type & + SATA_VALID_DEV_TYPE) != 0 && + SATA_PMPORTINFO_DRV_INFO(pmportinfo) != NULL && + sdinfo->satadrv_reset_time != 0) { + clock_t cur_time = ddi_get_lbolt(); + /* + * If the retry time limit was not + * exceeded, retry. + */ + if ((cur_time - sdinfo->satadrv_reset_time) < + drv_usectohz(SATA_DEV_REPROBE_TIMEOUT)) { + mutex_enter( + &sata_hba_inst->satahba_mutex); + sata_hba_inst->satahba_event_flags |= + SATA_EVNT_MAIN; + mutex_exit( + &sata_hba_inst->satahba_mutex); + mutex_enter(&sata_mutex); + sata_event_pending |= SATA_EVNT_MAIN; + mutex_exit(&sata_mutex); + return; + } + } + /* Fail the drive */ + sdinfo->satadrv_state = SATA_DSTATE_FAILED; + + sata_log(sata_hba_inst, CE_WARN, + "SATA device at port %d:%d - device failed", + saddr->cport, saddr->pmport); + } else { + /* + * No point of retrying - some other event processing + * would or already did port info cleanup. + * To be safe (HBA may need it), + * request clearing device reset condition. + */ + sdinfo->satadrv_event_flags |= + SATA_EVNT_CLEAR_DEVICE_RESET; + } + sdinfo->satadrv_event_flags &= ~SATA_EVNT_INPROC_DEVICE_RESET; + sdinfo->satadrv_reset_time = 0; + return; + } + /* + * Raise the flag indicating that the next sata command could + * be sent with SATA_CLEAR_DEV_RESET_STATE flag, if no new device + * reset is reported. + */ + mutex_enter(&pmportinfo->pmport_mutex); + if (SATA_PMPORTINFO_DRV_INFO(pmportinfo) != NULL) { + sdinfo->satadrv_reset_time = 0; + if (pmportinfo->pmport_dev_type & SATA_VALID_DEV_TYPE) { + sdinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo); + sdinfo->satadrv_event_flags &= + ~SATA_EVNT_INPROC_DEVICE_RESET; + /* must clear flags on cport */ + pminfo = SATA_PMULT_INFO(sata_hba_inst, + saddr->cport); + pminfo->pmult_event_flags |= + SATA_EVNT_CLEAR_DEVICE_RESET; + } + } +} + +/* * Port Link Events processing. * Every link established event may involve device reset (due to * COMRESET signal, equivalent of the hard reset) so arbitrarily @@ -15590,7 +17579,8 @@ * device detached event processing after link lost timeout. * Else, the event is ignored. * - * NOTE: Only cports are processed for now, i.e. no port multiplier ports + * NOTE: Port multiplier ports events are handled by + * sata_process_pmport_link_events(); */ static void sata_process_port_link_events(sata_hba_inst_t *sata_hba_inst, @@ -15674,8 +17664,7 @@ * For the sanity sake check if a device is attached - check * return state of a port probing. */ - if (sata_device.satadev_type != SATA_DTYPE_NONE && - sata_device.satadev_type != SATA_DTYPE_PMULT) { + if (sata_device.satadev_type != SATA_DTYPE_NONE) { /* * HBA port probe indicated that there is a device * attached. Check if the framework had device info @@ -15795,22 +17784,242 @@ } /* + * Port Multiplier Port Link Events processing. + */ +static void +sata_process_pmport_link_events(sata_hba_inst_t *sata_hba_inst, + sata_address_t *saddr) +{ + sata_device_t sata_device; + sata_pmport_info_t *pmportinfo = NULL; + sata_drive_info_t *sdinfo = NULL; + uint32_t event_flags; + uint8_t cport = saddr->cport; + uint8_t pmport = saddr->pmport; + int rval; + + SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst, + "Processing port %d:%d link event(s)", + cport, pmport); + + pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport); + mutex_enter(&pmportinfo->pmport_mutex); + event_flags = pmportinfo->pmport_event_flags; + + /* Reset event flags first */ + pmportinfo->pmport_event_flags &= + ~(SATA_EVNT_LINK_ESTABLISHED | SATA_EVNT_LINK_LOST); + + /* If the port is in SHUTDOWN or FAILED state, ignore link events. */ + if ((pmportinfo->pmport_state & + (SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) { + mutex_exit(&pmportinfo->pmport_mutex); + return; + } + + /* + * For the sanity sake get current port state. + * Set device address only. Other sata_device fields should be + * set by HBA driver. + */ + sata_device.satadev_rev = SATA_DEVICE_REV; + sata_device.satadev_addr = *saddr; + /* + * We have to exit mutex, because the HBA probe port function may + * block on its own mutex. + */ + mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, saddr->cport, + saddr->pmport)); + rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst)) + (SATA_DIP(sata_hba_inst), &sata_device); + mutex_enter(&SATA_PMPORT_MUTEX(sata_hba_inst, saddr->cport, + saddr->pmport)); + sata_update_pmport_info(sata_hba_inst, &sata_device); + if (rval != SATA_SUCCESS) { + /* Something went wrong? Fail the port */ + pmportinfo->pmport_state = SATA_PSTATE_FAILED; + mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, saddr->cport, + saddr->pmport)); + SATA_LOG_D((sata_hba_inst, CE_WARN, + "SATA port %d:%d probing failed", + saddr->cport, saddr->pmport)); + /* + * We may want to release device info structure, but + * it is not necessary. + */ + return; + } else { + /* port probed successfully */ + pmportinfo->pmport_state |= + SATA_STATE_PROBED | SATA_STATE_READY; + } + mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, + saddr->cport, saddr->pmport)); + mutex_enter(&SATA_PMPORT_MUTEX(sata_hba_inst, + saddr->cport, saddr->pmport)); + if (event_flags & SATA_EVNT_LINK_ESTABLISHED) { + + if ((sata_device.satadev_scr.sstatus & + SATA_PORT_DEVLINK_UP_MASK) != SATA_PORT_DEVLINK_UP) { + /* Ignore event */ + SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst, + "Ignoring port %d:%d link established event - " + "link down", + saddr->cport, saddr->pmport); + goto linklost; + } + + SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst, + "Processing port %d:%d link established event", + cport, pmport); + + /* + * For the sanity sake check if a device is attached - check + * return state of a port probing. + */ + if (sata_device.satadev_type != SATA_DTYPE_NONE && + sata_device.satadev_type != SATA_DTYPE_PMULT) { + /* + * HBA port probe indicated that there is a device + * attached. Check if the framework had device info + * structure attached for this device. + */ + if (pmportinfo->pmport_dev_type != SATA_DTYPE_NONE) { + ASSERT(SATA_PMPORTINFO_DRV_INFO(pmportinfo) != + NULL); + + sdinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo); + if ((sdinfo->satadrv_type & + SATA_VALID_DEV_TYPE) != 0) { + /* + * Dev info structure is present. + * If dev_type is set to known type in + * the framework's drive info struct + * then the device existed before and + * the link was probably lost + * momentarily - in such case + * we may want to check device + * identity. + * Identity check is not supported now. + * + * Link established event + * triggers device reset event. + */ + (SATA_PMPORTINFO_DRV_INFO(pmportinfo))-> + satadrv_event_flags |= + SATA_EVNT_DEVICE_RESET; + } + } else if (pmportinfo->pmport_dev_type == + SATA_DTYPE_NONE) { + /* + * We got new device attached! If HBA does not + * generate device attached events, trigger it + * here. + */ + if (!(SATA_FEATURES(sata_hba_inst) & + SATA_CTLF_HOTPLUG)) { + pmportinfo->pmport_event_flags |= + SATA_EVNT_DEVICE_ATTACHED; + } + } + /* Reset link lost timeout */ + pmportinfo->pmport_link_lost_time = 0; + } + } +linklost: + if (event_flags & SATA_EVNT_LINK_LOST) { +#ifdef SATA_DEBUG + if (pmportinfo->pmport_link_lost_time == 0) { + SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst, + "Processing port %d:%d link lost event", + saddr->cport, saddr->pmport); + } +#endif + if ((sata_device.satadev_scr.sstatus & + SATA_PORT_DEVLINK_UP_MASK) == SATA_PORT_DEVLINK_UP) { + /* Ignore event */ + SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst, + "Ignoring port %d:%d link lost event - link is up", + saddr->cport, saddr->pmport); + goto done; + } + /* + * When HBA cannot generate device attached/detached events, + * we need to track link lost time and eventually generate + * device detach event. + */ + if (!(SATA_FEATURES(sata_hba_inst) & SATA_CTLF_HOTPLUG)) { + /* We are tracking link lost time */ + if (pmportinfo->pmport_link_lost_time == 0) { + /* save current time (lbolt value) */ + pmportinfo->pmport_link_lost_time = + ddi_get_lbolt(); + /* just keep link lost event */ + pmportinfo->pmport_event_flags |= + SATA_EVNT_LINK_LOST; + } else { + clock_t cur_time = ddi_get_lbolt(); + if ((cur_time - + pmportinfo->pmport_link_lost_time) >= + drv_usectohz( + SATA_EVNT_LINK_LOST_TIMEOUT)) { + /* trigger device detach event */ + pmportinfo->pmport_event_flags |= + SATA_EVNT_DEVICE_DETACHED; + pmportinfo->pmport_link_lost_time = 0; + SATADBG2(SATA_DBG_EVENTS, + sata_hba_inst, + "Triggering port %d:%d " + "device detached event", + saddr->cport, saddr->pmport); + } else { + /* keep link lost event */ + pmportinfo->pmport_event_flags |= + SATA_EVNT_LINK_LOST; + } + } + } + /* + * We could change port state to disable/delay access to + * the attached device until the link is recovered. + */ + } +done: + event_flags = pmportinfo->pmport_event_flags; + mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, saddr->cport, + saddr->pmport)); + if (event_flags != 0) { + mutex_enter(&sata_hba_inst->satahba_mutex); + sata_hba_inst->satahba_event_flags |= SATA_EVNT_MAIN; + mutex_exit(&sata_hba_inst->satahba_mutex); + mutex_enter(&sata_mutex); + sata_event_pending |= SATA_EVNT_MAIN; + mutex_exit(&sata_mutex); + } +} + +/* * Device Detached Event processing. * Port is probed to find if a device is really gone. If so, * the device info structure is detached from the SATA port info structure * and released. * Port status is updated. * - * NOTE: Process cports event only, no port multiplier ports. + * NOTE: Port multiplier ports events are handled by + * sata_process_pmdevice_detached() */ static void sata_process_device_detached(sata_hba_inst_t *sata_hba_inst, sata_address_t *saddr) { sata_cport_info_t *cportinfo; + sata_pmport_info_t *pmportinfo; sata_drive_info_t *sdevinfo; sata_device_t sata_device; - dev_info_t *tdip; + sata_address_t pmport_addr; + char name[16]; + uint8_t cport = saddr->cport; + int npmport; int rval; SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst, @@ -15878,58 +18087,205 @@ /* * We need to detach and release device info structure here */ - if (SATA_CPORTINFO_DRV_INFO(cportinfo) != NULL) { - sdevinfo = SATA_CPORTINFO_DRV_INFO(cportinfo); - SATA_CPORTINFO_DRV_INFO(cportinfo) = NULL; + if (cportinfo->cport_dev_type == SATA_DTYPE_PMULT) { + /* + * A port-multiplier is removed. + * + * Calling sata_process_pmdevice_detached() does not work + * here. The port multiplier is gone, so we cannot probe + * sub-port any more and all pmult-related data structure must + * be de-allocated immediately. Following structure of every + * implemented sub-port behind the pmult are required to + * released. + * + * - attachment point + * - target node + * - sata_drive_info + * - sata_pmport_info + */ + for (npmport = 0; npmport < SATA_NUM_PMPORTS(sata_hba_inst, + cport); npmport ++) { + SATADBG2(SATA_DBG_PMULT|SATA_DBG_EVENTS_PROC, + sata_hba_inst, + "Detaching target node at port %d:%d", + cport, npmport); + + mutex_exit(&SATA_CPORT_MUTEX(sata_hba_inst, cport)); + + /* Remove attachment point. */ + name[0] = '\0'; + (void) sprintf(name, "%d.%d", cport, npmport); + ddi_remove_minor_node(SATA_DIP(sata_hba_inst), name); + sata_log(sata_hba_inst, CE_NOTE, + "Remove attachment point of port %d:%d", + cport, npmport); + + /* Remove target node */ + pmport_addr.cport = cport; + pmport_addr.pmport = (uint8_t)npmport; + pmport_addr.qual = SATA_ADDR_PMPORT; + sata_remove_target_node(sata_hba_inst, &pmport_addr); + + mutex_enter(&SATA_CPORT_MUTEX(sata_hba_inst, cport)); + + /* Release sata_pmport_info & sata_drive_info. */ + pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, + cport, npmport); + ASSERT(pmportinfo != NULL); + + sdevinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo); + if (sdevinfo != NULL) { + (void) kmem_free((void *) sdevinfo, + sizeof (sata_drive_info_t)); + } + + /* Release sata_pmport_info at last */ + (void) kmem_free((void *) pmportinfo, + sizeof (sata_pmport_info_t)); + } + + /* Finally, release sata_pmult_info */ + (void) kmem_free((void *) + SATA_CPORTINFO_PMULT_INFO(cportinfo), + sizeof (sata_pmult_info_t)); + SATA_CPORTINFO_PMULT_INFO(cportinfo) = NULL; + + sata_log(sata_hba_inst, CE_WARN, + "SATA port-multiplier detached at port %d", cport); + + cportinfo->cport_dev_type = SATA_DTYPE_NONE; + mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, + saddr->cport)->cport_mutex); + } else { + if (SATA_CPORTINFO_DRV_INFO(cportinfo) != NULL) { + sdevinfo = SATA_CPORTINFO_DRV_INFO(cportinfo); + SATA_CPORTINFO_DRV_INFO(cportinfo) = NULL; + (void) kmem_free((void *)sdevinfo, + sizeof (sata_drive_info_t)); + } + sata_log(sata_hba_inst, CE_WARN, + "SATA device detached at port %d", cport); + + cportinfo->cport_dev_type = SATA_DTYPE_NONE; + mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, + saddr->cport)->cport_mutex); + + /* + * Try to offline a device and remove target node + * if it still exists + */ + sata_remove_target_node(sata_hba_inst, saddr); + } + + + /* + * Generate sysevent - EC_DR / ESC_DR_AP_STATE_CHANGE + * with the hint: SE_HINT_REMOVE + */ + sata_gen_sysevent(sata_hba_inst, saddr, SE_HINT_REMOVE); +} + +/* + * Port Multiplier Port Device Deattached Event processing. + * + * NOTE: No Mutex should be hold. + */ +static void +sata_process_pmdevice_detached(sata_hba_inst_t *sata_hba_inst, + sata_address_t *saddr) +{ + sata_pmport_info_t *pmportinfo; + sata_drive_info_t *sdevinfo; + sata_device_t sata_device; + int rval; + uint8_t cport, pmport; + + cport = saddr->cport; + pmport = saddr->pmport; + + SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst, + "Processing port %d:%d device detached", + cport, pmport); + + pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport); + mutex_enter(&SATA_PMPORT_MUTEX(sata_hba_inst, cport, pmport)); + + /* Clear event flag */ + pmportinfo->pmport_event_flags &= ~SATA_EVNT_DEVICE_DETACHED; + + /* If the port is in SHUTDOWN or FAILED state, ignore detach event. */ + if ((pmportinfo->pmport_state & + (SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) { + mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, cport, pmport)); + return; + } + /* For sanity, re-probe the port */ + sata_device.satadev_rev = SATA_DEVICE_REV; + sata_device.satadev_addr = *saddr; + + /* + * We have to exit mutex, because the HBA probe port function may + * block on its own mutex. + */ + mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, cport, pmport)); + rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst)) + (SATA_DIP(sata_hba_inst), &sata_device); + mutex_enter(&SATA_PMPORT_MUTEX(sata_hba_inst, cport, pmport)); + sata_update_pmport_info(sata_hba_inst, &sata_device); + if (rval != SATA_SUCCESS) { + /* Something went wrong? Fail the port */ + pmportinfo->pmport_state = SATA_PSTATE_FAILED; + mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, cport, pmport)); + SATA_LOG_D((sata_hba_inst, CE_WARN, + "SATA port %d:%d probing failed", + saddr->pmport)); + /* + * We may want to release device info structure, but + * it is not necessary. + */ + return; + } else { + /* port probed successfully */ + pmportinfo->pmport_state |= + SATA_STATE_PROBED | SATA_STATE_READY; + } + /* + * Check if a device is still attached. For sanity, check also + * link status - if no link, there is no device. + */ + if ((sata_device.satadev_scr.sstatus & SATA_PORT_DEVLINK_UP_MASK) == + SATA_PORT_DEVLINK_UP && sata_device.satadev_type != + SATA_DTYPE_NONE) { + /* + * Device is still attached - ignore detach event. + */ + mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, cport, pmport)); + SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst, + "Ignoring detach - device still attached to port %d", + sata_device.satadev_addr.pmport); + return; + } + /* + * We need to detach and release device info structure here + */ + if (SATA_PMPORTINFO_DRV_INFO(pmportinfo) != NULL) { + sdevinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo); + SATA_PMPORTINFO_DRV_INFO(pmportinfo) = NULL; (void) kmem_free((void *)sdevinfo, sizeof (sata_drive_info_t)); } - cportinfo->cport_dev_type = SATA_DTYPE_NONE; + pmportinfo->pmport_dev_type = SATA_DTYPE_NONE; /* * Device cannot be reached anymore, even if the target node may be * still present. */ - - mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex); - sata_log(sata_hba_inst, CE_WARN, "SATA device detached at port %d", - sata_device.satadev_addr.cport); + mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, cport, pmport)); /* * Try to offline a device and remove target node if it still exists */ - tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst), saddr->cport); - if (tdip != NULL) { - /* - * Target node exists. Unconfigure device then remove - * the target node (one ndi operation). - */ - if (ndi_devi_offline(tdip, NDI_DEVI_REMOVE) != NDI_SUCCESS) { - /* - * PROBLEM - no device, but target node remained - * This happens when the file was open or node was - * waiting for resources. - */ - SATA_LOG_D((sata_hba_inst, CE_WARN, - "sata_process_device_detached: " - "Failed to remove target node for " - "detached SATA device.")); - /* - * Set target node state to DEVI_DEVICE_REMOVED. - * But re-check first that the node still exists. - */ - tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst), - saddr->cport); - if (tdip != NULL) { - sata_set_device_removed(tdip); - /* - * Instruct event daemon to retry the - * cleanup later. - */ - sata_set_target_node_cleanup(sata_hba_inst, - &sata_device.satadev_addr); - } - } - } + sata_remove_target_node(sata_hba_inst, saddr); + /* * Generate sysevent - EC_DR / ESC_DR_AP_STATE_CHANGE * with the hint: SE_HINT_REMOVE @@ -15951,18 +18307,22 @@ * * This function cannot be called in interrupt context (it may sleep). * - * NOTE: Process cports event only, no port multiplier ports. + * NOTE: Port multiplier ports events are handled by + * sata_process_pmdevice_attached() */ static void sata_process_device_attached(sata_hba_inst_t *sata_hba_inst, sata_address_t *saddr) { - sata_cport_info_t *cportinfo; - sata_drive_info_t *sdevinfo; + sata_cport_info_t *cportinfo = NULL; + sata_drive_info_t *sdevinfo = NULL; + sata_pmult_info_t *pmultinfo = NULL; + sata_pmport_info_t *pmportinfo = NULL; sata_device_t sata_device; dev_info_t *tdip; - uint32_t event_flags; + uint32_t event_flags = 0, pmult_event_flags = 0; int rval; + int npmport; SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst, "Processing port %d device attached", saddr->cport); @@ -16103,6 +18463,40 @@ cportinfo->cport_event_flags |= SATA_EVNT_DEVICE_ATTACHED; } + } else if (cportinfo->cport_dev_type == SATA_DTYPE_PMULT) { + cportinfo->cport_dev_attach_time = 0; + sata_log(sata_hba_inst, CE_NOTE, + "SATA port-multiplier detected at port %d", + saddr->cport); + + if (SATA_CPORTINFO_PMULT_INFO(cportinfo) != NULL) { + /* Log the info of new port multiplier */ + sata_show_pmult_info(sata_hba_inst, + &sata_device); + } + + ASSERT(SATA_CPORTINFO_PMULT_INFO(cportinfo) != NULL); + pmultinfo = SATA_CPORTINFO_PMULT_INFO(cportinfo); + for (npmport = 0; npmport < + pmultinfo->pmult_num_dev_ports; npmport++) { + pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, + saddr->cport, npmport); + ASSERT(pmportinfo != NULL); + + mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, + saddr->cport)->cport_mutex); + mutex_enter(&pmportinfo->pmport_mutex); + /* Marked all pmports with link events. */ + pmportinfo->pmport_event_flags = + SATA_EVNT_LINK_ESTABLISHED; + pmult_event_flags |= + pmportinfo->pmport_event_flags; + mutex_exit(&pmportinfo->pmport_mutex); + mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, + saddr->cport)->cport_mutex); + } + /* Auto-online is not available for PMult now. */ + } else { /* * If device was successfully attached, the subsequent @@ -16138,7 +18532,7 @@ * device was detached. */ tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst), - saddr->cport); + saddr->cport, saddr->pmport); mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex); if (tdip != NULL) { @@ -16203,6 +18597,269 @@ event_flags = cportinfo->cport_event_flags; mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex); + if (event_flags != 0 || pmult_event_flags != 0) { + mutex_enter(&sata_hba_inst->satahba_mutex); + sata_hba_inst->satahba_event_flags |= SATA_EVNT_MAIN; + mutex_exit(&sata_hba_inst->satahba_mutex); + mutex_enter(&sata_mutex); + sata_event_pending |= SATA_EVNT_MAIN; + mutex_exit(&sata_mutex); + } +} + +/* + * Port Multiplier Port Device Attached Event processing. + * + * NOTE: No Mutex should be hold. + */ +static void +sata_process_pmdevice_attached(sata_hba_inst_t *sata_hba_inst, + sata_address_t *saddr) +{ + sata_pmport_info_t *pmportinfo; + sata_drive_info_t *sdinfo; + sata_device_t sata_device; + dev_info_t *tdip; + uint32_t event_flags; + uint8_t cport = saddr->cport; + uint8_t pmport = saddr->pmport; + int rval; + + SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst, + "Processing port %d:%d device attached", cport, pmport); + + pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport); + + mutex_enter(&pmportinfo->pmport_mutex); + + /* Clear attach event flag first */ + pmportinfo->pmport_event_flags &= ~SATA_EVNT_DEVICE_ATTACHED; + + /* If the port is in SHUTDOWN or FAILED state, ignore event. */ + if ((pmportinfo->pmport_state & + (SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) { + pmportinfo->pmport_dev_attach_time = 0; + mutex_exit(&pmportinfo->pmport_mutex); + return; + } + + /* + * If the sata_drive_info structure is found attached to the port info, + * despite the fact the device was removed and now it is re-attached, + * the old drive info structure was not removed. + * Arbitrarily release device info structure. + */ + if (SATA_PMPORTINFO_DRV_INFO(pmportinfo) != NULL) { + sdinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo); + SATA_PMPORTINFO_DRV_INFO(pmportinfo) = NULL; + (void) kmem_free((void *)sdinfo, + sizeof (sata_drive_info_t)); + SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst, + "Arbitrarily detaching old device info.", NULL); + } + pmportinfo->pmport_dev_type = SATA_DTYPE_NONE; + + /* For sanity, re-probe the port */ + sata_device.satadev_rev = SATA_DEVICE_REV; + sata_device.satadev_addr = *saddr; + + /* + * We have to exit mutex, because the HBA probe port function may + * block on its own mutex. + */ + mutex_exit(&pmportinfo->pmport_mutex); + rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst)) + (SATA_DIP(sata_hba_inst), &sata_device); + mutex_enter(&pmportinfo->pmport_mutex); + + sata_update_pmport_info(sata_hba_inst, &sata_device); + if (rval != SATA_SUCCESS) { + /* Something went wrong? Fail the port */ + pmportinfo->pmport_state = SATA_PSTATE_FAILED; + pmportinfo->pmport_dev_attach_time = 0; + mutex_exit(&pmportinfo->pmport_mutex); + SATA_LOG_D((sata_hba_inst, CE_WARN, + "SATA port %d:%d probing failed", cport, pmport)); + return; + } else { + /* pmport probed successfully */ + pmportinfo->pmport_state |= + SATA_STATE_PROBED | SATA_STATE_READY; + } + /* + * Check if a device is still attached. For sanity, check also + * link status - if no link, there is no device. + */ + if ((sata_device.satadev_scr.sstatus & SATA_PORT_DEVLINK_UP_MASK) != + SATA_PORT_DEVLINK_UP || sata_device.satadev_type == + SATA_DTYPE_NONE) { + /* + * No device - ignore attach event. + */ + pmportinfo->pmport_dev_attach_time = 0; + mutex_exit(&pmportinfo->pmport_mutex); + SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst, + "Ignoring attach - no device connected to port %d:%d", + cport, pmport); + return; + } + + mutex_exit(&pmportinfo->pmport_mutex); + /* + * Generate sysevent - EC_DR / ESC_DR_AP_STATE_CHANGE + * with the hint: SE_HINT_INSERT + */ + sata_gen_sysevent(sata_hba_inst, saddr, SE_HINT_INSERT); + + /* + * Port reprobing will take care of the creation of the device + * info structure and determination of the device type. + */ + sata_device.satadev_addr = *saddr; + (void) sata_reprobe_port(sata_hba_inst, &sata_device, + SATA_DEV_IDENTIFY_NORETRY); + + mutex_enter(&pmportinfo->pmport_mutex); + if ((pmportinfo->pmport_state & SATA_STATE_READY) && + (pmportinfo->pmport_dev_type != SATA_DTYPE_NONE)) { + /* Some device is attached to the port */ + if (pmportinfo->pmport_dev_type == SATA_DTYPE_UNKNOWN) { + /* + * A device was not successfully attached. + * Track retry time for device identification. + */ + if (pmportinfo->pmport_dev_attach_time != 0) { + clock_t cur_time = ddi_get_lbolt(); + /* + * If the retry time limit was not exceeded, + * reinstate attach event. + */ + if ((cur_time - + pmportinfo->pmport_dev_attach_time) < + drv_usectohz( + SATA_DEV_IDENTIFY_TIMEOUT)) { + /* OK, restore attach event */ + pmportinfo->pmport_event_flags |= + SATA_EVNT_DEVICE_ATTACHED; + } else { + /* Timeout - cannot identify device */ + pmportinfo->pmport_dev_attach_time = 0; + sata_log(sata_hba_inst, CE_WARN, + "Could not identify SATA device " + "at port %d:%d", + cport, pmport); + } + } else { + /* + * Start tracking time for device + * identification. + * Save current time (lbolt value). + */ + pmportinfo->pmport_dev_attach_time = + ddi_get_lbolt(); + /* Restore attach event */ + pmportinfo->pmport_event_flags |= + SATA_EVNT_DEVICE_ATTACHED; + } + } else { + /* + * If device was successfully attached, the subsequent + * action depends on a state of the + * sata_auto_online variable. If it is set to zero. + * an explicit 'configure' command will be needed to + * configure it. If its value is non-zero, we will + * attempt to online (configure) the device. + * First, log the message indicating that a device + * was attached. + */ + pmportinfo->pmport_dev_attach_time = 0; + sata_log(sata_hba_inst, CE_WARN, + "SATA device detected at port %d:%d", + cport, pmport); + + if (SATA_PMPORTINFO_DRV_INFO(pmportinfo) != NULL) { + sata_drive_info_t new_sdinfo; + + /* Log device info data */ + new_sdinfo = *(SATA_PMPORTINFO_DRV_INFO( + pmportinfo)); + sata_show_drive_info(sata_hba_inst, + &new_sdinfo); + } + + mutex_exit(&pmportinfo->pmport_mutex); + + /* + * Make sure that there is no target node for that + * device. If so, release it. It should not happen, + * unless we had problem removing the node when + * device was detached. + */ + tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst), + saddr->cport, saddr->pmport); + mutex_enter(&pmportinfo->pmport_mutex); + if (tdip != NULL) { + +#ifdef SATA_DEBUG + if ((pmportinfo->pmport_event_flags & + SATA_EVNT_TARGET_NODE_CLEANUP) == 0) + sata_log(sata_hba_inst, CE_WARN, + "sata_process_device_attached: " + "old device target node exists!"); +#endif + /* + * target node exists - try to unconfigure + * device and remove the node. + */ + mutex_exit(&pmportinfo->pmport_mutex); + rval = ndi_devi_offline(tdip, + NDI_DEVI_REMOVE); + mutex_enter(&pmportinfo->pmport_mutex); + + if (rval == NDI_SUCCESS) { + pmportinfo->pmport_event_flags &= + ~SATA_EVNT_TARGET_NODE_CLEANUP; + pmportinfo->pmport_tgtnode_clean = + B_TRUE; + } else { + /* + * PROBLEM - the target node remained + * and it belongs to a previously + * attached device. + * This happens when the file was open + * or the node was waiting for + * resources at the time the + * associated device was removed. + * Instruct event daemon to retry the + * cleanup later. + */ + sata_log(sata_hba_inst, + CE_WARN, + "Application(s) accessing " + "previously attached SATA " + "device have to release " + "it before newly inserted " + "device can be made accessible." + "at port %d:%d", + cport, pmport); + pmportinfo->pmport_event_flags |= + SATA_EVNT_TARGET_NODE_CLEANUP; + pmportinfo->pmport_tgtnode_clean = + B_FALSE; + } + } + if (sata_auto_online != 0) { + pmportinfo->pmport_event_flags |= + SATA_EVNT_AUTOONLINE_DEVICE; + } + + } + } else { + pmportinfo->pmport_dev_attach_time = 0; + } + + event_flags = pmportinfo->pmport_event_flags; + mutex_exit(&pmportinfo->pmport_mutex); if (event_flags != 0) { mutex_enter(&sata_hba_inst->satahba_mutex); sata_hba_inst->satahba_event_flags |= SATA_EVNT_MAIN; @@ -16211,8 +18868,19 @@ sata_event_pending |= SATA_EVNT_MAIN; mutex_exit(&sata_mutex); } -} - + + /* clear the reset_in_progress events */ + if (SATA_PMPORTINFO_DRV_INFO(pmportinfo) != NULL) { + if (pmportinfo->pmport_dev_type & SATA_VALID_DEV_TYPE) { + /* must clear flags on cport */ + sata_pmult_info_t *pminfo = + SATA_PMULT_INFO(sata_hba_inst, + saddr->cport); + pminfo->pmult_event_flags |= + SATA_EVNT_CLEAR_DEVICE_RESET; + } + } +} /* * Device Target Node Cleanup Event processing. @@ -16241,7 +18909,8 @@ * Check if there is target node for that device and it is in the * DEVI_DEVICE_REMOVED state. If so, release it. */ - tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst), saddr->cport); + tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst), saddr->cport, + saddr->pmport); if (tdip != NULL) { /* * target node exists - check if it is target node of @@ -16275,12 +18944,32 @@ mutex_exit(&sata_mutex); } } else { - mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, - saddr->cport)->cport_mutex); - cportinfo->cport_event_flags &= - ~SATA_EVNT_TARGET_NODE_CLEANUP; - mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, - saddr->cport)->cport_mutex); + if (saddr->qual == SATA_ADDR_CPORT || + saddr->qual == SATA_ADDR_DCPORT) { + mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, + saddr->cport)->cport_mutex); + cportinfo->cport_event_flags &= + ~SATA_EVNT_TARGET_NODE_CLEANUP; + mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, + saddr->cport)->cport_mutex); + } else { + /* sanity check */ + if (SATA_CPORT_DEV_TYPE(sata_hba_inst, saddr->cport) != + SATA_DTYPE_PMULT || SATA_PMULT_INFO(sata_hba_inst, + saddr->cport) == NULL) + return; + if (SATA_PMPORT_INFO(sata_hba_inst, saddr->cport, + saddr->pmport) == NULL) + return; + + mutex_enter(&SATA_PMPORT_INFO(sata_hba_inst, + saddr->cport, saddr->pmport)->pmport_mutex); + SATA_PMPORT_INFO(sata_hba_inst, saddr->cport, + saddr->pmport)->pmport_event_flags &= + ~SATA_EVNT_TARGET_NODE_CLEANUP; + mutex_exit(&SATA_PMPORT_INFO(sata_hba_inst, + saddr->cport, saddr->pmport)->pmport_mutex); + } } } @@ -16327,7 +19016,8 @@ * DEVI_DEVICE_REMOVED state. If so, abort onlining but keep * the event for later processing. */ - tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst), saddr->cport); + tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst), saddr->cport, + saddr->pmport); if (tdip != NULL) { /* * target node exists - check if it is target node of @@ -16490,12 +19180,26 @@ sata_set_target_node_cleanup(sata_hba_inst_t *sata_hba_inst, sata_address_t *saddr) { - mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex); - SATA_CPORT_EVENT_FLAGS(sata_hba_inst, saddr->cport) |= - SATA_EVNT_TARGET_NODE_CLEANUP; - SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_tgtnode_clean = - B_FALSE; - mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex); + if (saddr->qual == SATA_ADDR_CPORT || + saddr->qual == SATA_ADDR_DCPORT) { + mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, + saddr->cport)->cport_mutex); + SATA_CPORT_EVENT_FLAGS(sata_hba_inst, saddr->cport) |= + SATA_EVNT_TARGET_NODE_CLEANUP; + SATA_CPORT_INFO(sata_hba_inst, saddr->cport)-> + cport_tgtnode_clean = B_FALSE; + mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, + saddr->cport)->cport_mutex); + } else { + mutex_enter(&SATA_PMPORT_INFO(sata_hba_inst, + saddr->cport, saddr->pmport)->pmport_mutex); + SATA_PMPORT_EVENT_FLAGS(sata_hba_inst, saddr->cport, + saddr->pmport) |= SATA_EVNT_TARGET_NODE_CLEANUP; + SATA_PMPORT_INFO(sata_hba_inst, saddr->cport, saddr->pmport)-> + pmport_tgtnode_clean = B_FALSE; + mutex_exit(&SATA_PMPORT_INFO(sata_hba_inst, + saddr->cport, saddr->pmport)->pmport_mutex); + } mutex_enter(&sata_hba_inst->satahba_mutex); sata_hba_inst->satahba_event_flags |= SATA_EVNT_MAIN; mutex_exit(&sata_hba_inst->satahba_mutex); @@ -16512,8 +19216,6 @@ * * Returns B_TRUE if the target node is in DEVI_DEVICE_REMOVED state, * B_FALSE otherwise. - * - * NOTE: No port multiplier support. */ static boolean_t sata_check_device_removed(dev_info_t *tdip)