Mercurial > illumos > illumos-gate
changeset 3935:0767d871beb3
6502757 sata: deadlock possible when device reset occurs during cfgadm operations
6502181 sata module should better inform target driver about device removal
6508809 sata: better handling of unexpected device removal and re-insertion is needed
6493063 sata framework incorrectly tests for partial information when doing IDENTIFY DEVICE
6503211 thumper:panic in sata module due to inadequate handling of memory shortage
6484766 SATA hotplug panics system (possibly with ZFS as root)
6520862 DKIOCSTATE to a SATA device panics system
author | pawelw |
---|---|
date | Fri, 30 Mar 2007 11:43:17 -0700 |
parents | 273f6bb7e684 |
children | 9bfdf69914f6 |
files | usr/src/uts/common/io/sata/impl/sata.c usr/src/uts/common/sys/sata/impl/sata.h |
diffstat | 2 files changed, 736 insertions(+), 341 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/common/io/sata/impl/sata.c Fri Mar 30 08:46:35 2007 -0700 +++ b/usr/src/uts/common/io/sata/impl/sata.c Fri Mar 30 11:43:17 2007 -0700 @@ -155,6 +155,8 @@ static void sata_process_device_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 *, + sata_address_t *); /* Local functions for ioctl */ static int32_t sata_get_port_num(sata_hba_inst_t *, struct devctl_iocdata *); @@ -185,6 +187,7 @@ static void sata_txlt_rw_completion(sata_pkt_t *); static void sata_txlt_atapi_completion(sata_pkt_t *); static void sata_txlt_nodata_cmd_completion(sata_pkt_t *); +static int sata_emul_rw_completion(sata_pkt_txlate_t *); static struct scsi_extended_sense *sata_immediate_error_response( sata_pkt_txlate_t *, int); @@ -211,14 +214,14 @@ 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_log(sata_hba_inst_t *, uint_t, char *fmt, ...); -static int sata_fetch_smart_return_status(sata_hba_inst_t *, +static int sata_fetch_smart_return_status(sata_hba_inst_t *, sata_drive_info_t *); -static int sata_fetch_smart_data(sata_hba_inst_t *, sata_drive_info_t *, +static int sata_fetch_smart_data(sata_hba_inst_t *, sata_drive_info_t *, struct smart_data *); -static int sata_smart_selftest_log(sata_hba_inst_t *, +static int sata_smart_selftest_log(sata_hba_inst_t *, sata_drive_info_t *, struct smart_selftest_log *); -static int sata_ext_smart_selftest_read_log(sata_hba_inst_t *, +static int sata_ext_smart_selftest_read_log(sata_hba_inst_t *, sata_drive_info_t *, struct smart_ext_selftest_log *, uint16_t); static int sata_smart_read_log(sata_hba_inst_t *, sata_drive_info_t *, uint8_t *, uint8_t, uint8_t); @@ -226,6 +229,10 @@ struct read_log_ext_directory *); static void sata_gen_sysevent(sata_hba_inst_t *, sata_address_t *, int); static void sata_xlate_errors(sata_pkt_txlate_t *); +static void sata_set_device_removed(dev_info_t *); +static boolean_t sata_check_device_removed(dev_info_t *); +static void sata_set_target_node_cleanup(sata_hba_inst_t *, int cport); + /* * SATA Framework will ignore SATA HBA driver cb_ops structure and @@ -259,7 +266,7 @@ static struct modlmisc modlmisc = { &mod_miscops, /* Type of module */ - "Generic SATA Driver v%I%" /* module name */ + "SATA Module v%I%" /* module name */ }; @@ -1169,24 +1176,42 @@ mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); tdip = sata_get_target_dip(dip, comp_port); - if (tdip != NULL) { - /* target node exist */ - if (ndi_devi_offline(tdip, - NDI_DEVI_REMOVE) != NDI_SUCCESS) { - /* - * Problem - * A target node remained - * attached. This happens when - * the file was open or a node - * was waiting for resources. - * Cannot do anything about it. - */ - SATA_LOG_D((sata_hba_inst, - CE_WARN, - "sata_hba_ioctl: " - "disconnect: cannot " - "remove target node!!!")); - } + if (tdip != NULL && ndi_devi_offline(tdip, + NDI_DEVI_REMOVE) != NDI_SUCCESS) { + /* + * Problem + * A target node remained + * attached. This happens when + * the file was open or a node + * was waiting for resources. + * Cannot do anything about it. + */ + SATA_LOG_D((sata_hba_inst, CE_WARN, + "sata_hba_ioctl: " + "disconnect: could not " + "unconfigure device before " + "disconnecting the SATA " + "port %d", cport)); + + /* + * 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, cport); } mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); @@ -1264,7 +1289,7 @@ SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_hba_ioctl: unconfigure: " "failed to unconfigure " - "device at cport %d", cport)); + "device at SATA port %d", cport)); rv = EIO; } /* @@ -1283,7 +1308,7 @@ SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_hba_ioctl: unconfigure: " "attempt to unconfigure non-existing device " - "at cport %d", cport)); + "at SATA port %d", cport)); rv = ENXIO; } @@ -1336,7 +1361,7 @@ cport)->cport_mutex); SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_hba_ioctl: connect: " - "failed to activate SATA cport %d", + "failed to activate SATA port %d", cport)); rv = EIO; break; @@ -1443,7 +1468,7 @@ sata_hba_inst, cport)->cport_mutex); SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_hba_ioctl: configure: " - "failed to activate SATA cport %d", + "failed to activate SATA port %d", cport)); rv = EIO; break; @@ -1516,26 +1541,55 @@ mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); if ((tdip = sata_get_target_dip(dip, comp_port)) != NULL) { - /* target node still exists */ - if (ndi_devi_online(tdip, 0) != NDI_SUCCESS) { - SATA_LOG_D((sata_hba_inst, CE_WARN, - "sata_hba_ioctl: configure: " - "onlining device at cport %d failed", - cport)); - rv = EIO; + /* + * Target node exists. Verify, that it belongs + * to existing, attached device and not to + * a removed device. + */ + if (sata_check_device_removed(tdip) == B_FALSE) { + if (ndi_devi_online(tdip, 0) != NDI_SUCCESS) { + SATA_LOG_D((sata_hba_inst, CE_WARN, + "sata_hba_ioctl: configure: " + "onlining device at SATA port %d " + "failed", cport)); + rv = EIO; + break; + } else { + mutex_enter(&SATA_CPORT_INFO( + sata_hba_inst, cport)->cport_mutex); + SATA_CPORT_INFO(sata_hba_inst, cport)-> + cport_tgtnode_clean = B_TRUE; + mutex_exit(&SATA_CPORT_INFO( + sata_hba_inst, cport)->cport_mutex); + } + } else { + sata_log(sata_hba_inst, CE_WARN, + "SATA device at port %d cannot be " + "configured. " + "Application(s) accessing previously " + "attached device " + "have to release it before newly inserted " + "device can be made accessible.", + cport); break; } } else { /* * No target node - need to create a new target node. */ + mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)-> + cport_mutex); + SATA_CPORT_INFO(sata_hba_inst, cport)-> + cport_tgtnode_clean = B_TRUE; + mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)-> + cport_mutex); tdip = sata_create_target_node(dip, sata_hba_inst, &sata_device.satadev_addr); if (tdip == NULL) { /* configure failed */ SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_hba_ioctl: configure: " - "configuring device at cport %d " + "configuring SATA device at port %d " "failed", cport)); rv = EIO; break; @@ -1853,7 +1907,7 @@ /* * This operation returns EFAULT if either reset - * controller failed or a re-probbing of any ports + * controller failed or a re-probing of any ports * failed. * We return here, because common return is for * a single cport operation. @@ -1909,7 +1963,7 @@ NULL); if (ndi_devi_offline(tdip, - NDI_UNCONFIG) != + NDI_DEVI_REMOVE) != NDI_SUCCESS) { SATA_LOG_D(( sata_hba_inst, @@ -1918,29 +1972,32 @@ "port deactivate: " "failed to " "unconfigure " - "device at cport " - "%d", cport)); + "device at port " + "%d before " + "deactivating " + "the port", cport)); } - if (ndi_devi_offline(tdip, - NDI_DEVI_REMOVE) != - NDI_SUCCESS) { - /* - * Problem; - * target node remained - * attached. - * Too bad... - */ - SATA_LOG_D(( - sata_hba_inst, - CE_WARN, - "sata_hba_ioctl: " - "port deactivate: " - "failed to " - "unconfigure " - "device at " - "cport %d", - cport)); - } + + + /* + * 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, cport); } mutex_enter(&SATA_CPORT_INFO( sata_hba_inst, cport)->cport_mutex); @@ -1997,7 +2054,7 @@ } SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_hba_ioctl: port deactivate: " - "cannot deactivate SATA cport %d", + "cannot deactivate SATA port %d", cport)); rv = EIO; } else { @@ -2044,7 +2101,7 @@ cport)->cport_mutex); SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_hba_ioctl: port activate: " - "cannot activate SATA cport %d", + "cannot activate SATA port %d", cport)); rv = EIO; break; @@ -2524,7 +2581,7 @@ if ((ddi_prop_update_int(DDI_DEV_T_NONE, sd->sd_dev, "pm-capable", 1)) != DDI_PROP_SUCCESS) { sata_log(sata_hba_inst, CE_WARN, - "device at port %d: will not be power-managed ", + "SATA device at port %d: will not be power-managed ", SCSI_TO_SATA_CPORT(sd->sd_address.a_target)); SATA_LOG_D((sata_hba_inst, CE_WARN, "failure updating pm-capable property")); @@ -2699,7 +2756,12 @@ mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, sata_device.satadev_addr.cport))); /* - * Allocate necessary DMA resources for the packet's buffer + * Allocate necessary DMA resources for the packet's data buffer + * NOTE: + * In case of read/write commands, DMA resource allocation here is + * based on the premise that the transfer length specified in + * the read/write scsi cdb will match exactly DMA resources - + * returning correct packet residue is crucial. */ if ((rval = sata_dma_buf_setup(spx, flags, callback, arg, &cur_dma_attr)) != DDI_SUCCESS) { @@ -2794,19 +2856,33 @@ ASSERT(spx != NULL && spx->txlt_scsi_pkt == pkt && spx->txlt_sata_pkt != NULL); - /* - * Mutex-protected section below is just to identify device type - * and switch to ATAPI processing, if necessary - */ cport = SCSI_TO_SATA_CPORT(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) { + if (sdinfo == NULL || + SATA_CPORT_INFO(sata_hba_inst, cport)->cport_tgtnode_clean == + B_FALSE) { mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport))); - spx->txlt_scsi_pkt->pkt_reason = CMD_DEV_GONE; + pkt->pkt_reason = CMD_DEV_GONE; + /* + * The sd target driver is checking CMD_DEV_GONE pkt_reason + * only in callback function (for normal requests) and + * in the dump code path. + * So, if the callback is available, we need to do + * the callback rather than returning TRAN_FATAL_ERROR here. + */ + if (pkt->pkt_comp != NULL) { + /* scsi callback required */ + if (taskq_dispatch(SATA_TXLT_TASKQ(spx), + (task_func_t *)pkt->pkt_comp, + (void *)pkt, TQ_SLEEP) == NULL) + /* Scheduling the callback failed */ + return (TRAN_BUSY); + return (TRAN_ACCEPT); + } + /* No callback available */ return (TRAN_FATAL_ERROR); } @@ -3297,8 +3373,12 @@ * Free DMA resources - cookies and handles */ ASSERT(spx->txlt_dma_cookie_list != NULL); - (void) kmem_free(spx->txlt_dma_cookie_list, - spx->txlt_dma_cookie_list_len * sizeof (ddi_dma_cookie_t)); + if (spx->txlt_dma_cookie_list != &spx->txlt_dma_cookie) { + (void) kmem_free(spx->txlt_dma_cookie_list, + spx->txlt_dma_cookie_list_len * + sizeof (ddi_dma_cookie_t)); + spx->txlt_dma_cookie_list = NULL; + } (void) ddi_dma_unbind_handle(spx->txlt_buf_dma_handle); (void) ddi_dma_free_handle(&spx->txlt_buf_dma_handle); } @@ -3329,8 +3409,12 @@ * Free DMA resources - cookies and handles */ ASSERT(spx->txlt_dma_cookie_list != NULL); - (void) kmem_free(spx->txlt_dma_cookie_list, - spx->txlt_dma_cookie_list_len * sizeof (ddi_dma_cookie_t)); + if (spx->txlt_dma_cookie_list != &spx->txlt_dma_cookie) { + (void) kmem_free(spx->txlt_dma_cookie_list, + spx->txlt_dma_cookie_list_len * + sizeof (ddi_dma_cookie_t)); + spx->txlt_dma_cookie_list = NULL; + } (void) ddi_dma_unbind_handle(spx->txlt_buf_dma_handle); (void) ddi_dma_free_handle(&spx->txlt_buf_dma_handle); } @@ -3423,6 +3507,24 @@ case 1: /* valid address but no device - it has disappeared ? */ spx->txlt_scsi_pkt->pkt_reason = CMD_DEV_GONE; + /* + * The sd target driver is checking CMD_DEV_GONE pkt_reason + * only in callback function (for normal requests) and + * in the dump code path. + * So, if the callback is available, we need to do + * the callback rather than returning TRAN_FATAL_ERROR here. + */ + if (spx->txlt_scsi_pkt->pkt_comp != NULL) { + /* scsi callback required */ + if (taskq_dispatch(SATA_TXLT_TASKQ(spx), + (task_func_t *)spx->txlt_scsi_pkt->pkt_comp, + (void *)spx->txlt_scsi_pkt, + TQ_SLEEP) == NULL) + /* Scheduling the callback failed */ + return (TRAN_BUSY); + + return (TRAN_ACCEPT); + } return (TRAN_FATAL_ERROR); default: /* all OK */ @@ -3433,16 +3535,27 @@ /* * If device is in reset condition, reject the packet with - * TRAN_BUSY + * TRAN_BUSY, unless: + * 1. system is panicking (dumping) + * In such case only one thread is running and there is no way to + * process reset. + * 2. cfgadm operation is is progress (internal APCTL lock is set) + * Some cfgadm operations involve drive commands, so reset condition + * needs to be ignored for IOCTL operations. */ if ((sdinfo->satadrv_event_flags & - (SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) && - !ddi_in_panic()) { - spx->txlt_scsi_pkt->pkt_reason = CMD_INCOMPLETE; - SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, - "sata_scsi_start: rejecting command because " - "of device reset state\n", NULL); - return (TRAN_BUSY); + (SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) != 0) { + + if (!ddi_in_panic() && + ((SATA_CPORT_EVENT_FLAGS(spx->txlt_sata_hba_inst, + sata_device.satadev_addr.cport) & + SATA_APCTL_LOCK_PORT_BUSY) == 0)) { + spx->txlt_scsi_pkt->pkt_reason = CMD_INCOMPLETE; + SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, + "sata_scsi_start: rejecting command because " + "of device reset state\n", NULL); + return (TRAN_BUSY); + } } /* @@ -3455,6 +3568,12 @@ spx->txlt_sata_pkt->satapkt_device.satadev_type = sdinfo->satadrv_type; spx->txlt_sata_pkt->satapkt_cmd.satacmd_flags = sata_initial_cmd_flags; + if ((SATA_CPORT_INFO(spx->txlt_sata_hba_inst, + sata_device.satadev_addr.cport)->cport_event_flags & + SATA_APCTL_LOCK_PORT_BUSY) != 0) { + spx->txlt_sata_pkt->satapkt_cmd.satacmd_flags. + sata_ignore_dev_reset = B_TRUE; + } spx->txlt_scsi_pkt->pkt_reason = CMD_CMPLT; @@ -3588,7 +3707,7 @@ if (taskq_dispatch(SATA_TXLT_TASKQ(spx), (task_func_t *)spx->txlt_scsi_pkt->pkt_comp, (void *)spx->txlt_scsi_pkt, - TQ_SLEEP) == 0) + TQ_SLEEP) == NULL) /* Scheduling the callback failed */ return (TRAN_BUSY); return (TRAN_ACCEPT); @@ -3628,7 +3747,7 @@ if (taskq_dispatch(SATA_TXLT_TASKQ(spx), (task_func_t *)spx->txlt_scsi_pkt->pkt_comp, (void *)spx->txlt_scsi_pkt, - TQ_SLEEP) == 0) + TQ_SLEEP) == NULL) /* Scheduling the callback failed */ return (TRAN_BUSY); return (TRAN_ACCEPT); @@ -3788,7 +3907,7 @@ * identifiers are common for SATA devices * But not now. */ - /*FALLTHRU*/ + /*FALLTHROUGH*/ default: /* Request for unsupported VPD page */ @@ -3816,7 +3935,7 @@ /* scsi callback required */ if (taskq_dispatch(SATA_TXLT_TASKQ(spx), (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt, - TQ_SLEEP) == 0) + TQ_SLEEP) == NULL) /* Scheduling the callback failed */ return (TRAN_BUSY); } @@ -3875,7 +3994,7 @@ /* scsi callback required */ if (taskq_dispatch(SATA_TXLT_TASKQ(spx), (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt, - TQ_SLEEP) == 0) + TQ_SLEEP) == NULL) /* Scheduling the callback failed */ return (TRAN_BUSY); return (TRAN_ACCEPT); @@ -3933,7 +4052,7 @@ /* scsi callback required */ if (taskq_dispatch(SATA_TXLT_TASKQ(spx), (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt, - TQ_SLEEP) == 0) + TQ_SLEEP) == NULL) /* Scheduling the callback failed */ return (TRAN_BUSY); @@ -3991,7 +4110,7 @@ /* scsi callback required */ if (taskq_dispatch(SATA_TXLT_TASKQ(spx), (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt, - TQ_SLEEP) == 0) + TQ_SLEEP) == NULL) /* Scheduling the callback failed */ return (TRAN_BUSY); @@ -4119,7 +4238,7 @@ /* scsi callback required */ if (taskq_dispatch(SATA_TXLT_TASKQ(spx), (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt, - TQ_SLEEP) == 0) + TQ_SLEEP) == NULL) /* Scheduling the callback failed */ return (TRAN_BUSY); @@ -4373,7 +4492,7 @@ /* scsi callback required */ if (taskq_dispatch(SATA_TXLT_TASKQ(spx), (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt, - TQ_SLEEP) == 0) + TQ_SLEEP) == NULL) /* Scheduling the callback failed */ return (TRAN_BUSY); @@ -4632,7 +4751,7 @@ /* scsi callback required */ if (taskq_dispatch(SATA_TXLT_TASKQ(spx), (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt, - TQ_SLEEP) == 0) + TQ_SLEEP) == NULL) /* Scheduling the callback failed */ return (TRAN_BUSY); @@ -4845,7 +4964,7 @@ /* scsi callback required */ if (taskq_dispatch(SATA_TXLT_TASKQ(spx), (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt, - TQ_SLEEP) == 0) + TQ_SLEEP) == NULL) /* Scheduling the callback failed */ return (TRAN_BUSY); @@ -4913,9 +5032,7 @@ scmd->satacmd_flags.sata_data_direction = SATA_DIR_READ; /* - * Build cmd block depending on the device capability and - * requested operation mode. - * Do not bother with non-dma mode. + * Extract LBA and sector count from scsi CDB. */ switch ((uint_t)scsipkt->pkt_cdbp[0]) { case SCMD_READ: @@ -4977,6 +5094,22 @@ return (sata_txlt_lba_out_of_range(spx)); } + /* + * For zero-length transfer, emulate good completion of the command + * (reasons for rejecting the command were already checked). + * No DMA resources were allocated. + */ + if (spx->txlt_dma_cookie_list == NULL) { + mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); + return (sata_emul_rw_completion(spx)); + } + + /* + * Build cmd block depending on the device capability and + * requested operation mode. + * Do not bother with non-dma mode - we are working only with + * devices supporting DMA. + */ scmd->satacmd_addr_type = ATA_ADDR_LBA; scmd->satacmd_device_reg = SATA_ADH_LBA; scmd->satacmd_cmd_reg = SATAC_READ_DMA; @@ -5129,9 +5262,7 @@ scmd->satacmd_flags.sata_data_direction = SATA_DIR_WRITE; /* - * Build cmd block depending on the device capability and - * requested operation mode. - * Do not bother with non-dma mode. + * Extract LBA and sector count from scsi CDB */ switch ((uint_t)scsipkt->pkt_cdbp[0]) { case SCMD_WRITE: @@ -5193,6 +5324,22 @@ return (sata_txlt_lba_out_of_range(spx)); } + /* + * For zero-length transfer, emulate good completion of the command + * (reasons for rejecting the command were already checked). + * No DMA resources were allocated. + */ + if (spx->txlt_dma_cookie_list == NULL) { + mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); + return (sata_emul_rw_completion(spx)); + } + + /* + * Build cmd block depending on the device capability and + * requested operation mode. + * Do not bother with non-dma mode- we are working only with + * devices supporting DMA. + */ scmd->satacmd_addr_type = ATA_ADDR_LBA; scmd->satacmd_device_reg = SATA_ADH_LBA; scmd->satacmd_cmd_reg = SATAC_WRITE_DMA; @@ -5575,11 +5722,11 @@ spx->txlt_sata_pkt->satapkt_device.satadev_addr.qual == SATA_ADDR_DCPORT) sata_log(sata_hba_inst, CE_CONT, - "port %d error", + "SATA port %d error", sata_device->satadev_addr.cport); else sata_log(sata_hba_inst, CE_CONT, - "port %d pmport %d error\n", + "SATA port %d pmport %d error\n", sata_device->satadev_addr.cport, sata_device->satadev_addr.pmport); @@ -5702,7 +5849,7 @@ /* scsi callback required */ if (taskq_dispatch(SATA_TXLT_TASKQ(spx), (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt, - TQ_SLEEP) == 0) + TQ_SLEEP) == NULL) /* Scheduling the callback failed */ return (TRAN_BUSY); return (TRAN_ACCEPT); @@ -5824,6 +5971,33 @@ /* + * Emulated SATA Read/Write command completion for zero-length requests. + * This request always succedes, so in synchronous mode it always returns + * TRAN_ACCEPT, and in non-synchronous mode it may return TRAN_BUSY if the + * callback cannot be scheduled. + */ +static int +sata_emul_rw_completion(sata_pkt_txlate_t *spx) +{ + struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt; + + scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | + STATE_SENT_CMD | STATE_GOT_STATUS; + scsipkt->pkt_reason = CMD_CMPLT; + *scsipkt->pkt_scbp = STATUS_GOOD; + if (!(spx->txlt_sata_pkt->satapkt_op_mode & SATA_OPMODE_SYNCH)) { + /* scsi callback required - have to schedule it */ + if (taskq_dispatch(SATA_TXLT_TASKQ(spx), + (task_func_t *)scsipkt->pkt_comp, + (void *)scsipkt, TQ_SLEEP) == NULL) + /* Scheduling the callback failed */ + return (TRAN_BUSY); + } + return (TRAN_ACCEPT); +} + + +/* * Translate completion status of SATA read/write commands into scsi response. * pkt completion_reason is checked to determine the completion status. * Do scsi callback if necessary. @@ -5983,7 +6157,6 @@ scsipkt->pkt_comp != NULL) /* scsi callback required */ (*scsipkt->pkt_comp)(scsipkt); - } /* @@ -7211,7 +7384,7 @@ 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", + "cannot create SATA attachment point for port %d", ncport); } @@ -7323,7 +7496,7 @@ 0) != DDI_SUCCESS) { sata_log(sata_hba_inst, CE_WARN, "sata_hba_attach: " - "cannot create sata attachment " + "cannot create SATA attachment " "point for port %d pmult port %d", ncport, npmport); } @@ -7491,6 +7664,7 @@ mutex_enter(&cportinfo->cport_mutex); sata_show_drive_info(sata_hba_inst, SATA_CPORTINFO_DRV_INFO(cportinfo)); + cportinfo->cport_tgtnode_clean = B_TRUE; mutex_exit(&cportinfo->cport_mutex); cdip = sata_create_target_node(pdip, sata_hba_inst, &sata_device.satadev_addr); @@ -7557,6 +7731,7 @@ mutex_enter(&cportinfo->cport_mutex); sata_show_drive_info(sata_hba_inst, pmportinfo->pmport_sata_drive); + pmportinfo->pmport_tgtnode_clean = B_TRUE; mutex_exit(&cportinfo->cport_mutex); cdip = sata_create_target_node(pdip, sata_hba_inst, &sata_device.satadev_addr); @@ -7590,6 +7765,8 @@ * * A dev_info_t pointer is returned if operation is successful, NULL is * returned otherwise. + * + * No port multiplier support. */ static dev_info_t * @@ -7728,7 +7905,7 @@ "node removal failed %d", rval)); } sata_log(sata_hba_inst, CE_WARN, "sata_create_target_node: " - "cannot create target node for device at port %d", + "cannot create target node for SATA device at port %d", sata_addr->cport); return (NULL); } @@ -7795,8 +7972,9 @@ if (rval != SATA_SUCCESS) { cportinfo->cport_state = SATA_PSTATE_FAILED; mutex_exit(&cportinfo->cport_mutex); - SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_hba_ioctl: " - "connect: port probbing failed")); + SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_reprobe_port: " + "SATA port %d probing failed", + cportinfo->cport_addr.cport)); return (SATA_FAILURE); } @@ -8691,11 +8869,12 @@ ddi_dma_free_handle(&spx->txlt_buf_dma_handle); spx->txlt_buf_dma_handle = 0; - kmem_free(spx->txlt_dma_cookie_list, - spx->txlt_dma_cookie_list_len * sizeof (ddi_dma_cookie_t)); - spx->txlt_dma_cookie_list = NULL; - spx->txlt_dma_cookie_list_len = 0; - + if (spx->txlt_dma_cookie_list != &spx->txlt_dma_cookie) { + kmem_free(spx->txlt_dma_cookie_list, + spx->txlt_dma_cookie_list_len * sizeof (ddi_dma_cookie_t)); + spx->txlt_dma_cookie_list = NULL; + spx->txlt_dma_cookie_list_len = 0; + } /* Free buffer */ scsi_free_consistent_buf(spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp); } @@ -8819,9 +8998,13 @@ * This function handles initial DMA resource allocation as well as * DMA window shift and may be called repeatedly for the same DMA window * until all DMA cookies in the DMA window are processed. - * - * Returns DDI_SUCCESS upon successful operation, - * returns failure code returned by failing commands or DDI_FAILURE when + * To guarantee that there is always a coherent set of cookies to process + * by SATA HBA driver (observing alignment, device granularity, etc.), + * the number of slots for DMA cookies is equal to lesser of a number of + * cookies in a DMA window and a max number of scatter/gather entries. + * + * Returns DDI_SUCCESS upon successful operation. + * Return failure code of a failing command or DDI_FAILURE when * internal cleanup failed. */ static int @@ -8829,15 +9012,13 @@ int (*callback)(caddr_t), caddr_t arg, ddi_dma_attr_t *cur_dma_attr) { - int rval; - ddi_dma_cookie_t cookie; - off_t offset; - size_t size; - int max_sg_len, req_sg_len, i; - uint_t dma_flags; - struct buf *bp; - uint64_t max_txfer_len; - uint64_t cur_txfer_len; + int rval; + off_t offset; + size_t size; + int max_sg_len, req_len, i; + uint_t dma_flags; + struct buf *bp; + uint64_t cur_txfer_len; ASSERT(spx->txlt_sata_pkt != NULL); @@ -8884,7 +9065,7 @@ rval = ddi_dma_buf_bind_handle( spx->txlt_buf_dma_handle, bp, dma_flags, callback, arg, - &cookie, + &spx->txlt_dma_cookie, &spx->txlt_curwin_num_dma_cookies); } else { /* Buffer is not aligned */ @@ -8967,7 +9148,8 @@ NULL, spx->txlt_tmp_buf, bufsz, dma_flags, ddicallback, 0, - &cookie, &spx->txlt_curwin_num_dma_cookies); + &spx->txlt_dma_cookie, + &spx->txlt_curwin_num_dma_cookies); } switch (rval) { @@ -8994,6 +9176,11 @@ "sata_dma_buf_setup: numwin failed\n")); return (DDI_FAILURE); } + SATADBG2(SATA_DBG_DMA_SETUP, + spx->txlt_sata_hba_inst, + "sata_dma_buf_setup: windows: %d, cookies: %d\n", + spx->txlt_num_dma_win, + spx->txlt_curwin_num_dma_cookies); spx->txlt_cur_dma_win = 0; break; @@ -9001,6 +9188,10 @@ /* DMA fully mapped */ spx->txlt_num_dma_win = 1; spx->txlt_cur_dma_win = 0; + SATADBG1(SATA_DBG_DMA_SETUP, + spx->txlt_sata_hba_inst, + "sata_dma_buf_setup: windows: 1 " + "cookies: %d\n", spx->txlt_curwin_num_dma_cookies); break; default: @@ -9038,10 +9229,9 @@ if (spx->txlt_cur_dma_win < spx->txlt_num_dma_win) { (void) ddi_dma_getwin(spx->txlt_buf_dma_handle, spx->txlt_cur_dma_win, &offset, &size, - &cookie, + &spx->txlt_dma_cookie, &spx->txlt_curwin_num_dma_cookies); spx->txlt_curwin_processed_dma_cookies = 0; - } else { /* No more windows! End of request! */ /* What to do? - panic for now */ @@ -9056,10 +9246,16 @@ } } } - /* There better be at least one DMA cookie */ + /* There better be at least one DMA cookie outstanding */ ASSERT((spx->txlt_curwin_num_dma_cookies - spx->txlt_curwin_processed_dma_cookies) > 0); + if (spx->txlt_dma_cookie_list == &spx->txlt_dma_cookie) { + /* The default cookie slot was used in previous run */ + ASSERT(spx->txlt_curwin_processed_dma_cookies == 0); + spx->txlt_dma_cookie_list = NULL; + spx->txlt_dma_cookie_list_len = 0; + } if (spx->txlt_curwin_processed_dma_cookies == 0) { /* * Processing a new DMA window - set-up dma cookies list. @@ -9081,23 +9277,65 @@ spx->txlt_dma_cookie_list_len = 0; } if (spx->txlt_dma_cookie_list == NULL) { - /* Allocate new dma cookie array */ - spx->txlt_dma_cookie_list = kmem_zalloc( - sizeof (ddi_dma_cookie_t) * - spx->txlt_curwin_num_dma_cookies, - callback == NULL_FUNC ? KM_NOSLEEP : KM_SLEEP); - spx->txlt_dma_cookie_list_len = - spx->txlt_curwin_num_dma_cookies; - } - /* - * Copy all DMA cookies into local list, so we will know their - * dma_size in advance of setting the sata_pkt. - * One cookie was already fetched, so copy it. - */ - *(&spx->txlt_dma_cookie_list[0]) = cookie; - for (i = 1; i < spx->txlt_curwin_num_dma_cookies; i++) { - ddi_dma_nextcookie(spx->txlt_buf_dma_handle, &cookie); - *(&spx->txlt_dma_cookie_list[i]) = cookie; + /* + * Calculate lesser of number of cookies in this + * DMA window and number of s/g entries. + */ + max_sg_len = cur_dma_attr->dma_attr_sgllen; + req_len = MIN(max_sg_len, + spx->txlt_curwin_num_dma_cookies); + + /* Allocate new dma cookie array if necessary */ + if (req_len == 1) { + /* Only one cookie - no need for a list */ + spx->txlt_dma_cookie_list = + &spx->txlt_dma_cookie; + spx->txlt_dma_cookie_list_len = 1; + } else { + /* + * More than one cookie - try to allocate space. + */ + spx->txlt_dma_cookie_list = kmem_zalloc( + sizeof (ddi_dma_cookie_t) * req_len, + callback == NULL_FUNC ? KM_NOSLEEP : + KM_SLEEP); + if (spx->txlt_dma_cookie_list == NULL) { + SATADBG1(SATA_DBG_DMA_SETUP, + spx->txlt_sata_hba_inst, + "sata_dma_buf_setup: cookie list " + "allocation failed\n", NULL); + /* + * We could not allocate space for + * neccessary number of dma cookies in + * this window, so we fail this request. + * Next invocation would try again to + * allocate space for cookie list. + * Note:Packet residue was not modified. + */ + return (DDI_DMA_NORESOURCES); + } else { + spx->txlt_dma_cookie_list_len = req_len; + } + } + } + /* + * Fetch DMA cookies into cookie list in sata_pkt_txlate. + * First cookie was already fetched. + */ + *(&spx->txlt_dma_cookie_list[0]) = spx->txlt_dma_cookie; + cur_txfer_len = + (uint64_t)spx->txlt_dma_cookie_list[0].dmac_size; + spx->txlt_sata_pkt->satapkt_cmd.satacmd_num_dma_cookies = 1; + spx->txlt_curwin_processed_dma_cookies++; + for (i = 1; (i < spx->txlt_dma_cookie_list_len) && + (i < spx->txlt_curwin_num_dma_cookies); i++) { + ddi_dma_nextcookie(spx->txlt_buf_dma_handle, + &spx->txlt_dma_cookie_list[i]); + cur_txfer_len += + (uint64_t)spx->txlt_dma_cookie_list[i].dmac_size; + spx->txlt_curwin_processed_dma_cookies++; + spx->txlt_sata_pkt-> + satapkt_cmd.satacmd_num_dma_cookies += 1; } } else { SATADBG2(SATA_DBG_DMA_SETUP, spx->txlt_sata_hba_inst, @@ -9105,88 +9343,52 @@ "cur cookie %d, total cookies %d\n", spx->txlt_curwin_processed_dma_cookies, spx->txlt_curwin_num_dma_cookies); - } - - /* - * Set-up sata_pkt cookie list. - * No single cookie transfer size would exceed max transfer size of - * an ATA command used for addressed device (tha adjustment of the dma - * attributes took care of this). But there may be more - * then one cookie, so the cmd cookie list has to be - * constrained by both a maximum scatter gather list length and - * a maximum transfer size restriction of an ATA command. - */ - - max_sg_len = cur_dma_attr->dma_attr_sgllen; - req_sg_len = MIN(max_sg_len, - (spx->txlt_curwin_num_dma_cookies - - spx->txlt_curwin_processed_dma_cookies)); - - ASSERT(req_sg_len > 0); - - max_txfer_len = MAX((cur_dma_attr->dma_attr_granular * 0x100), - cur_dma_attr->dma_attr_maxxfer); - - /* One cookie should be always available */ + + /* + * Not all cookies from the current dma window were used because + * of s/g limitation. + * There is no need to re-size the list - it was set at + * optimal size, or only default entry is used (s/g = 1). + */ + if (spx->txlt_dma_cookie_list == NULL) { + spx->txlt_dma_cookie_list = &spx->txlt_dma_cookie; + spx->txlt_dma_cookie_list_len = 1; + } + /* + * Since we are processing remaining cookies in a DMA window, + * there may be less of them than the number of entries in the + * current dma cookie list. + */ + req_len = MIN(spx->txlt_dma_cookie_list_len, + (spx->txlt_curwin_num_dma_cookies - + spx->txlt_curwin_processed_dma_cookies)); + + /* Fetch the next batch of cookies */ + for (i = 0, cur_txfer_len = 0; i < req_len; i++) { + ddi_dma_nextcookie(spx->txlt_buf_dma_handle, + &spx->txlt_dma_cookie_list[i]); + cur_txfer_len += + (uint64_t)spx->txlt_dma_cookie_list[i].dmac_size; + spx->txlt_sata_pkt-> + satapkt_cmd.satacmd_num_dma_cookies++; + spx->txlt_curwin_processed_dma_cookies++; + } + } + + ASSERT(spx->txlt_sata_pkt->satapkt_cmd.satacmd_num_dma_cookies > 0); + + /* Point sata_cmd to the cookie list */ spx->txlt_sata_pkt->satapkt_cmd.satacmd_dma_cookie_list = - &spx->txlt_dma_cookie_list[spx->txlt_curwin_processed_dma_cookies]; - - spx->txlt_sata_pkt->satapkt_cmd.satacmd_num_dma_cookies = 1; - - cur_txfer_len = - (uint64_t)spx->txlt_dma_cookie_list[ - spx->txlt_curwin_processed_dma_cookies].dmac_size; - - spx->txlt_curwin_processed_dma_cookies++; - - ASSERT(cur_txfer_len <= max_txfer_len); - - /* Add more cookies to the scatter-gather list */ - for (i = 1; i < req_sg_len; i++) { - if (cur_txfer_len < max_txfer_len) { - /* - * Check if the next cookie could be used by - * this sata_pkt. - */ - if ((cur_txfer_len + - spx->txlt_dma_cookie_list[ - spx->txlt_curwin_processed_dma_cookies]. - dmac_size) <= max_txfer_len) { - /* Yes, transfer lenght is within bounds */ - spx->txlt_sata_pkt-> - satapkt_cmd.satacmd_num_dma_cookies++; - cur_txfer_len += - spx->txlt_dma_cookie_list[ - spx->txlt_curwin_processed_dma_cookies]. - dmac_size; - spx->txlt_curwin_processed_dma_cookies++; - } else { - /* No, transfer would exceed max lenght. */ - SATADBG3(SATA_DBG_DMA_SETUP, - spx->txlt_sata_hba_inst, - "ncookies %d, size 0x%lx, " - "max_size 0x%lx\n", - spx->txlt_sata_pkt-> - satapkt_cmd.satacmd_num_dma_cookies, - cur_txfer_len, max_txfer_len); - break; - } - } else { - /* Cmd max transfer length reached */ - SATADBG3(SATA_DBG_DMA_SETUP, spx->txlt_sata_hba_inst, - "Max transfer length? " - "ncookies %d, size 0x%lx, max_size 0x%lx\n", - spx->txlt_sata_pkt-> - satapkt_cmd.satacmd_num_dma_cookies, - cur_txfer_len, max_txfer_len); - break; - } - } + &spx->txlt_dma_cookie_list[0]; + + /* Remember number of DMA cookies passed in sata packet */ + spx->txlt_num_dma_cookies = + spx->txlt_sata_pkt->satapkt_cmd.satacmd_num_dma_cookies; ASSERT(cur_txfer_len != 0); if (cur_txfer_len <= bp->b_bcount) spx->txlt_total_residue -= cur_txfer_len; - else + else { /* * Temporary DMA buffer has been padded by * ddi_dma_mem_alloc()! @@ -9197,6 +9399,7 @@ * the requested number of bytes. */ spx->txlt_total_residue -= bp->b_bcount; + } return (DDI_SUCCESS); } @@ -9284,11 +9487,12 @@ rval = (*SATA_START_FUNC(sata_hba_inst))(SATA_DIP(sata_hba_inst), spkt); if (rval == SATA_TRAN_ACCEPTED && spkt->satapkt_reason == SATA_PKT_COMPLETED) { - if ((sdinfo->satadrv_id.ai_config & 4) == 1) { - sata_log(sata_hba_inst, CE_WARN, + if ((sdinfo->satadrv_id.ai_config & SATA_INCOMPLETE_DATA) == + SATA_INCOMPLETE_DATA) { + SATA_LOG_D((sata_hba_inst, CE_WARN, "SATA disk device at port %d - " "partial Identify Data", - sdinfo->satadrv_addr.cport); + sdinfo->satadrv_addr.cport)); rval = 1; /* may retry later */ goto fail; } @@ -9712,7 +9916,7 @@ * 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 pmultiplier ports. + * For now, support is for cports only - no port multiplier device ports. */ static void @@ -9745,22 +9949,15 @@ switch (SATA_CPORT_DEV_TYPE(sata_hba_inst, cport)) { case SATA_DTYPE_NONE: { + ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED; + ap_state->ap_condition = AP_COND_OK; /* No device attached */ ap_state->ap_rstate = AP_RSTATE_EMPTY; - ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED; - ap_state->ap_condition = AP_COND_OK; break; } case SATA_DTYPE_UNKNOWN: case SATA_DTYPE_ATAPINONCD: case SATA_DTYPE_PMULT: /* Until PMult is supported */ - { - /* Unknown device attached */ - ap_state->ap_rstate = AP_RSTATE_CONNECTED; - ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED; - ap_state->ap_condition = AP_COND_UNKNOWN; - break; - } case SATA_DTYPE_ATADISK: case SATA_DTYPE_ATAPICD: { @@ -9774,13 +9971,28 @@ if (tdip != NULL) { ndi_devi_enter(dip, &circ); mutex_enter(&(DEVI(tdip)->devi_lock)); + if (DEVI_IS_DEVICE_REMOVED(tdip)) { + /* + * There could be the case where previously + * configured and opened device was removed + * and unknown device was plugged. + * In such case we want to show a device, and + * its configured or unconfigured state but + * indicate unusable condition untill the + * old target node is released and removed. + */ + ap_state->ap_condition = AP_COND_UNUSABLE; + } else { + ap_state->ap_condition = AP_COND_OK; + } if ((DEVI_IS_DEVICE_OFFLINE(tdip)) || (DEVI_IS_DEVICE_DOWN(tdip))) { - ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED; + ap_state->ap_ostate = + AP_OSTATE_UNCONFIGURED; } else { - ap_state->ap_ostate = AP_OSTATE_CONFIGURED; - } - ap_state->ap_condition = AP_COND_OK; + ap_state->ap_ostate = + AP_OSTATE_CONFIGURED; + } mutex_exit(&(DEVI(tdip)->devi_lock)); ndi_devi_exit(dip, circ); } else { @@ -9795,7 +10007,7 @@ ap_state->ap_condition = AP_COND_UNKNOWN; /* * This is actually internal error condition (non fatal), - * beacuse we already checked all defined device types. + * because we have already checked all defined device types. */ SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_cfgadm_state: Internal error: " @@ -10424,6 +10636,10 @@ sata_process_port_pwr_change(sata_hba_inst, saddr); } + if (event_flags & SATA_EVNT_TARGET_NODE_CLEANUP) { + sata_process_target_node_cleanup( + sata_hba_inst, saddr); + } } if (SATA_CPORT_DEV_TYPE(sata_hba_inst, ncport) != SATA_DTYPE_NONE) { @@ -10499,7 +10715,7 @@ /* Fail the port */ cportinfo->cport_state = SATA_PSTATE_FAILED; mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex); - sata_log(sata_hba_inst, CE_WARN, "port %d failed", saddr->cport); + sata_log(sata_hba_inst, CE_WARN, "SATA port %d failed", saddr->cport); } /* @@ -10545,25 +10761,24 @@ return; } - if ((sdinfo->satadrv_event_flags & SATA_EVNT_DEVICE_RESET) == 0) { + if ((sdinfo->satadrv_event_flags & + (SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) == 0) { /* Nothing to do */ mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)-> cport_mutex); return; } - - SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst, - "Processing port %d device reset", saddr->cport); - - if (sdinfo->satadrv_event_flags & SATA_EVNT_INPROC_DEVICE_RESET) { +#ifdef SATA_DEBUG + 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); - /* Just leave */ - mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)-> - cport_mutex); - return; - } + } +#endif + SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst, + "Processing port %d device reset", saddr->cport); /* Clear event flag */ sdinfo->satadrv_event_flags &= ~SATA_EVNT_DEVICE_RESET; @@ -10585,7 +10800,8 @@ cportinfo->cport_state = SATA_PSTATE_FAILED; mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)-> cport_mutex); - SATA_LOG_D((sata_hba_inst, CE_WARN, "Port %d probing failed", + SATA_LOG_D((sata_hba_inst, CE_WARN, + "SATA port %d probing failed", saddr->cport)); return; } @@ -10633,17 +10849,13 @@ SATA_PORT_DEVLINK_UP_MASK) == SATA_PORT_DEVLINK_UP && (sata_device.satadev_type & SATA_DTYPE_ATADISK) != 0) { /* - * We may retry this a bit later - reinstate reset - * condition + * We may retry this a bit later - in-process reset + * condition is already set. */ if ((cportinfo->cport_dev_type & SATA_VALID_DEV_TYPE) != 0 && SATA_CPORTINFO_DRV_INFO(cportinfo) != NULL) { sdinfo = SATA_CPORTINFO_DRV_INFO(cportinfo); - sdinfo->satadrv_event_flags |= - SATA_EVNT_DEVICE_RESET; - sdinfo->satadrv_event_flags &= - ~SATA_EVNT_INPROC_DEVICE_RESET; mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex); mutex_enter(&sata_hba_inst->satahba_mutex); @@ -10760,7 +10972,8 @@ cportinfo->cport_state = SATA_PSTATE_FAILED; mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)-> cport_mutex); - SATA_LOG_D((sata_hba_inst, CE_WARN, "Port %d probing failed", + SATA_LOG_D((sata_hba_inst, CE_WARN, + "SATA port %d probing failed", saddr->cport)); /* * We may want to release device info structure, but @@ -10963,7 +11176,8 @@ cportinfo->cport_state = SATA_PSTATE_FAILED; mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)-> cport_mutex); - SATA_LOG_D((sata_hba_inst, CE_WARN, "Port %d probing failed", + SATA_LOG_D((sata_hba_inst, CE_WARN, + "SATA port %d probing failed", saddr->cport)); /* * We may want to release device info structure, but @@ -11016,19 +11230,9 @@ tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst), saddr->cport); if (tdip != NULL) { /* - * target node exist - unconfigure device first, then remove - * the node - */ - if (ndi_devi_offline(tdip, NDI_UNCONFIG) != 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 unconfigure removed device.")); - } + * 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 @@ -11038,7 +11242,22 @@ SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_process_device_detached: " "Failed to remove target node for " - "removed device.")); + "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, + saddr->cport); + } } } /* @@ -11081,8 +11300,9 @@ cportinfo = SATA_CPORT_INFO(sata_hba_inst, saddr->cport); mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex); - /* Clear event flag first */ + /* Clear attach event flag first */ cportinfo->cport_event_flags &= ~SATA_EVNT_DEVICE_ATTACHED; + /* If the port is in SHUTDOWN or FAILED state, ignore event. */ if ((cportinfo->cport_state & (SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) { @@ -11094,9 +11314,9 @@ /* * If the sata_drive_info structure is found attached to the port info, - * something went wrong in the event reporting and processing sequence. - * To recover, arbitrarily release device info structure and issue - * a warning. + * 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_CPORTINFO_DRV_INFO(cportinfo) != NULL) { sdevinfo = SATA_CPORTINFO_DRV_INFO(cportinfo); @@ -11127,7 +11347,8 @@ cportinfo->cport_dev_attach_time = 0; mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)-> cport_mutex); - SATA_LOG_D((sata_hba_inst, CE_WARN, "Port %d probing failed", + SATA_LOG_D((sata_hba_inst, CE_WARN, + "SATA port %d probing failed", saddr->cport)); return; } else { @@ -11161,58 +11382,15 @@ sata_gen_sysevent(sata_hba_inst, saddr, SE_HINT_INSERT); /* - * 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); - if (tdip != NULL) { - - SATA_LOG_D((sata_hba_inst, CE_WARN, - "sata_process_device_attached: " - "old device target node exists!!!")); - /* - * target node exist - unconfigure device first, then remove - * the node - */ - if (ndi_devi_offline(tdip, NDI_UNCONFIG) != 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_attached: " - "Failed to unconfigure old target node!")); - } - /* Following call will retry node offlining and removing it */ - if (ndi_devi_offline(tdip, NDI_DEVI_REMOVE) != NDI_SUCCESS) { - /* PROBLEM - no device, but target node remained */ - SATA_LOG_D((sata_hba_inst, CE_WARN, - "sata_process_device_attached: " - "Failed to remove old target node!")); - /* - * It is not clear, what should be done here. - * For now, we will not attach a new device - */ - mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, - saddr->cport)->cport_mutex); - cportinfo->cport_dev_attach_time = 0; - mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, - saddr->cport)->cport_mutex); - return; - } - } - - /* - * Port reprobing will take care of the creation of the device info - * structure and determination of the device type. + * Port reprobing will take care of the creation of the device + * info structure and determination of the device type. */ sata_device.satadev_addr = *saddr; - rval = sata_reprobe_port(sata_hba_inst, &sata_device, + (void) sata_reprobe_port(sata_hba_inst, &sata_device, SATA_DEV_IDENTIFY_NORETRY); - mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex); + mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)-> + cport_mutex); if ((cportinfo->cport_state & SATA_STATE_READY) && (cportinfo->cport_dev_type != SATA_DTYPE_NONE)) { /* Some device is attached to the port */ @@ -11237,11 +11415,18 @@ } else { /* Timeout - cannot identify device */ cportinfo->cport_dev_attach_time = 0; + sata_log(sata_hba_inst, + CE_WARN, + "Cannot identify SATA device " + "at port %d - device will not be " + "attached.", + saddr->cport); } } else { /* - * Start tracking time for dev identification. - * save current time (lbolt value). + * Start tracking time for device + * identification. + * Save current time (lbolt value). */ cportinfo->cport_dev_attach_time = ddi_get_lbolt(); @@ -11253,24 +11438,92 @@ /* * If device was successfully attached, an explicit * 'configure' command will be needed to configure it. + * Log the message indicating that a device + * was attached. */ cportinfo->cport_dev_attach_time = 0; sata_log(sata_hba_inst, CE_WARN, - "SATA device attached at port %d", saddr->cport); + "SATA device detected at port %d", saddr->cport); if (SATA_CPORTINFO_DRV_INFO(cportinfo) != NULL) { sata_drive_info_t new_sdinfo; /* Log device info data */ - new_sdinfo = - *(SATA_CPORTINFO_DRV_INFO(cportinfo)); + new_sdinfo = *(SATA_CPORTINFO_DRV_INFO( + cportinfo)); sata_show_drive_info(sata_hba_inst, &new_sdinfo); } + + mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, + saddr->cport)->cport_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); + mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, + saddr->cport)->cport_mutex); + if (tdip != NULL) { + +#ifdef SATA_DEBUG + if ((cportinfo->cport_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(&SATA_CPORT_INFO(sata_hba_inst, + saddr->cport)->cport_mutex); + rval = ndi_devi_offline(tdip, + NDI_DEVI_REMOVE); + mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, + saddr->cport)->cport_mutex); + + if (rval == NDI_SUCCESS) { + cportinfo->cport_event_flags &= + ~SATA_EVNT_TARGET_NODE_CLEANUP; + cportinfo->cport_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.", + saddr->cport); + cportinfo->cport_event_flags |= + SATA_EVNT_TARGET_NODE_CLEANUP; + cportinfo->cport_tgtnode_clean = + B_FALSE; + } + } + } } else { cportinfo->cport_dev_attach_time = 0; } + event_flags = cportinfo->cport_event_flags; mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex); if (event_flags != 0) { @@ -11285,6 +11538,78 @@ /* + * Device Target Node Cleanup Event processing. + * If the target node associated with a sata port device is in + * DEVI_DEVICE_REMOVED state, an attempt is made to remove it. + * If the target node cannot be removed, the event flag is left intact, + * so that event daemon may re-run this function later. + * + * This function cannot be called in interrupt context (it may sleep). + * + * NOTE: Processes cport events only, not port multiplier ports. + */ +static void +sata_process_target_node_cleanup(sata_hba_inst_t *sata_hba_inst, + sata_address_t *saddr) +{ + sata_cport_info_t *cportinfo; + dev_info_t *tdip; + + SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst, + "Processing port %d device target node cleanup", saddr->cport); + + cportinfo = SATA_CPORT_INFO(sata_hba_inst, saddr->cport); + + /* + * 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); + if (tdip != NULL) { + /* + * target node exists - check if it is target node of + * a removed device. + */ + if (sata_check_device_removed(tdip) == B_TRUE) { + SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst, + "sata_process_target_node_cleanup: " + "old device target node exists!", NULL); + /* + * Unconfigure and remove the target node + */ + if (ndi_devi_offline(tdip, NDI_DEVI_REMOVE) == + NDI_SUCCESS) { + 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); + return; + } + /* + * Event daemon will retry the cleanup later. + */ + 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); + } + } 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); + } +} + + + +/* * sata_set_drive_features function compares current device features setting * with the saved device features settings and, if there is a difference, * it restores device features setting to the previously saved state. @@ -12149,3 +12474,65 @@ break; } } + + + +/* + * Set DEVI_DEVICE_REMOVED state in the SATA device target node. + */ +static void +sata_set_device_removed(dev_info_t *tdip) +{ + int circ; + + ASSERT(tdip != NULL); + + ndi_devi_enter(tdip, &circ); + mutex_enter(&DEVI(tdip)->devi_lock); + DEVI_SET_DEVICE_REMOVED(tdip); + mutex_exit(&DEVI(tdip)->devi_lock); + ndi_devi_exit(tdip, circ); +} + + +/* + * Set internal event instructing event daemon to try + * to perform the target node cleanup. + */ +static void +sata_set_target_node_cleanup(sata_hba_inst_t *sata_hba_inst, int cport) +{ + mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); + SATA_CPORT_EVENT_FLAGS(sata_hba_inst, cport) |= + SATA_EVNT_TARGET_NODE_CLEANUP; + SATA_CPORT_INFO(sata_hba_inst, cport)->cport_tgtnode_clean = B_FALSE; + mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex); + 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); +} + + +/* + * Check if the SATA device target node is in DEVI_DEVICE_REMOVED state, + * i.e. check if the target node state indicates that it belongs to a removed + * device. + * + * 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) +{ + ASSERT(tdip != NULL); + + if (DEVI_IS_DEVICE_REMOVED(tdip)) + return (B_TRUE); + else + return (B_FALSE); +}
--- a/usr/src/uts/common/sys/sata/impl/sata.h Fri Mar 30 08:46:35 2007 -0700 +++ b/usr/src/uts/common/sys/sata/impl/sata.h Fri Mar 30 11:43:17 2007 -0700 @@ -166,6 +166,8 @@ clock_t cport_dev_attach_time; struct sata_port_stats cport_stats; /* Port statistics */ + + boolean_t cport_tgtnode_clean; /* Target node usable */ }; typedef struct sata_cport_info sata_cport_info_t; @@ -292,6 +294,8 @@ clock_t pmport_dev_attach_time; struct sata_port_stats pmport_stats; /* Port statistics */ + + boolean_t pmport_tgtnode_clean; /* Target node usable */ }; typedef struct sata_pmport_info sata_pmport_info_t; @@ -351,6 +355,7 @@ #define SATA_EVNT_SKIP 0x40000000 #define SATA_EVNT_INPROC_DEVICE_RESET 0x08000000 #define SATA_EVNT_CLEAR_DEVICE_RESET 0x04000000 +#define SATA_EVNT_TARGET_NODE_CLEANUP 0x00000100 /* * Lock flags - used to serialize configuration operations @@ -368,9 +373,11 @@ SATA_EVNT_DEVICE_DETACHED | \ SATA_EVNT_LINK_LOST | \ SATA_EVNT_LINK_ESTABLISHED | \ - SATA_EVNT_PORT_FAILED) + SATA_EVNT_PORT_FAILED | \ + SATA_EVNT_TARGET_NODE_CLEANUP) /* Mask for drive events */ -#define SATA_EVNT_DRIVE_EVENTS SATA_EVNT_DEVICE_RESET +#define SATA_EVNT_DRIVE_EVENTS (SATA_EVNT_DEVICE_RESET | \ + SATA_EVNT_INPROC_DEVICE_RESET) #define SATA_EVNT_CONTROLLER_EVENTS SATA_EVNT_PWR_LEVEL_CHANGED /* Delays and timeounts definitions */ @@ -413,9 +420,10 @@ /* cookies in the current DMA window */ uint_t txlt_curwin_num_dma_cookies; - /* procesed dma cookies in current DMA win */ + /* processed dma cookies in current DMA win */ uint_t txlt_curwin_processed_dma_cookies; size_t txlt_total_residue; + ddi_dma_cookie_t txlt_dma_cookie; /* default dma cookie */ int txlt_dma_cookie_list_len; /* alloc list len */ ddi_dma_cookie_t *txlt_dma_cookie_list; /* dma cookie list */ int txlt_num_dma_cookies; /* dma cookies in list */ @@ -455,7 +463,6 @@ /* * Macros for accessing various structure fields - * */ #define SATA_TRAN(sata_hba_inst) \ @@ -512,6 +519,9 @@ #define SATA_CPORT_STATE(sata_hba_inst, cport) \ sata_hba_inst->satahba_dev_port[cport]->cport_state +#define SATA_CPORT_EVENT_FLAGS(sata_hba_inst, cport) \ + sata_hba_inst->satahba_dev_port[cport]->cport_event_flags + #define SATA_CPORT_SCR(sata_hba_inst, cport) \ sata_hba_inst->satahba_dev_port[cport]->cport_scr @@ -696,8 +706,6 @@ #define SATA_DBG_DMA_SETUP 0x400 #define SATA_DBG_DEV_SETTINGS 0x800 -extern int sata_debug_flag; - /* Debug macros */ #define SATADBG1(flag, sata, format, arg1) \ if (sata_debug_flags & (flag)) { \