From 2482ae1b96a558eec551575934d5f06c87b807af Mon Sep 17 00:00:00 2001 From: Marcel Telka Date: Mon, 30 Jan 2017 12:51:33 +0100 Subject: [PATCH] 5698 panic in mpt_sas: vmem_hash_delete(ffffff1aa3456000, 1, 8): bad free Reviewed by: Dan Fields Reviewed by: Andrew Stormont Approved by: Hans Rosenfeld --- .../uts/common/io/scsi/adapters/mpt_sas/mptsas.c | 134 +++++++++++++++------ .../common/sys/scsi/adapters/mpt_sas/mptsas_var.h | 2 + 2 files changed, 99 insertions(+), 37 deletions(-) diff --git a/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c b/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c index 72c8800f3e..00f3543a36 100644 --- a/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c +++ b/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c @@ -1352,6 +1352,7 @@ mptsas_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) cv_init(&mpt->m_fw_cv, NULL, CV_DRIVER, NULL); cv_init(&mpt->m_config_cv, NULL, CV_DRIVER, NULL); cv_init(&mpt->m_fw_diag_cv, NULL, CV_DRIVER, NULL); + cv_init(&mpt->m_extreq_sense_refcount_cv, NULL, CV_DRIVER, NULL); mutex_init_done++; mutex_enter(&mpt->m_mutex); @@ -1637,6 +1638,7 @@ fail: cv_destroy(&mpt->m_fw_cv); cv_destroy(&mpt->m_config_cv); cv_destroy(&mpt->m_fw_diag_cv); + cv_destroy(&mpt->m_extreq_sense_refcount_cv); } if (map_setup) { @@ -2054,7 +2056,7 @@ mptsas_do_detach(dev_info_t *dip) cv_destroy(&mpt->m_fw_cv); cv_destroy(&mpt->m_config_cv); cv_destroy(&mpt->m_fw_diag_cv); - + cv_destroy(&mpt->m_extreq_sense_refcount_cv); mptsas_smp_teardown(mpt); mptsas_hba_teardown(mpt); @@ -2651,6 +2653,8 @@ mptsas_alloc_sense_bufs(mptsas_t *mpt) size_t mem_size; int num_extrqsense_bufs; + ASSERT(mpt->m_extreq_sense_refcount == 0); + /* * re-alloc when it has already alloced */ @@ -3617,7 +3621,6 @@ mptsas_scsi_init_pkt(struct scsi_address *ap, struct scsi_pkt *pkt, { mptsas_cmd_t *cmd, *new_cmd; mptsas_t *mpt = ADDR2MPT(ap); - int failure = 1; uint_t oldcookiec; mptsas_target_t *ptgt = NULL; int rval; @@ -3655,44 +3658,79 @@ mptsas_scsi_init_pkt(struct scsi_address *ap, struct scsi_pkt *pkt, ddi_dma_handle_t save_dma_handle; cmd = kmem_cache_alloc(mpt->m_kmem_cache, kf); + if (cmd == NULL) + return (NULL); - if (cmd) { - save_dma_handle = cmd->cmd_dmahandle; - bzero(cmd, sizeof (*cmd) + scsi_pkt_size()); - cmd->cmd_dmahandle = save_dma_handle; - - pkt = (void *)((uchar_t *)cmd + - sizeof (struct mptsas_cmd)); - pkt->pkt_ha_private = (opaque_t)cmd; - pkt->pkt_address = *ap; - pkt->pkt_private = (opaque_t)cmd->cmd_pkt_private; - pkt->pkt_scbp = (opaque_t)&cmd->cmd_scb; - pkt->pkt_cdbp = (opaque_t)&cmd->cmd_cdb; - cmd->cmd_pkt = (struct scsi_pkt *)pkt; - cmd->cmd_cdblen = (uchar_t)cmdlen; - cmd->cmd_scblen = statuslen; - cmd->cmd_rqslen = SENSE_LENGTH; - cmd->cmd_tgt_addr = ptgt; - failure = 0; - } - - if (failure || (cmdlen > sizeof (cmd->cmd_cdb)) || + save_dma_handle = cmd->cmd_dmahandle; + bzero(cmd, sizeof (*cmd) + scsi_pkt_size()); + cmd->cmd_dmahandle = save_dma_handle; + + pkt = (void *)((uchar_t *)cmd + + sizeof (struct mptsas_cmd)); + pkt->pkt_ha_private = (opaque_t)cmd; + pkt->pkt_address = *ap; + pkt->pkt_private = (opaque_t)cmd->cmd_pkt_private; + pkt->pkt_scbp = (opaque_t)&cmd->cmd_scb; + pkt->pkt_cdbp = (opaque_t)&cmd->cmd_cdb; + cmd->cmd_pkt = (struct scsi_pkt *)pkt; + cmd->cmd_cdblen = (uchar_t)cmdlen; + cmd->cmd_scblen = statuslen; + cmd->cmd_rqslen = SENSE_LENGTH; + cmd->cmd_tgt_addr = ptgt; + + if ((cmdlen > sizeof (cmd->cmd_cdb)) || (tgtlen > PKT_PRIV_LEN) || (statuslen > EXTCMDS_STATUS_SIZE)) { - if (failure == 0) { - /* - * if extern alloc fails, all will be - * deallocated, including cmd - */ - failure = mptsas_pkt_alloc_extern(mpt, cmd, - cmdlen, tgtlen, statuslen, kf); + int failure; + + /* + * We are going to allocate external packet space which + * might include the sense data buffer for DMA so we + * need to increase the reference counter here. In a + * case the HBA is in reset we just simply free the + * allocated packet and bail out. + */ + mutex_enter(&mpt->m_mutex); + if (mpt->m_in_reset) { + mutex_exit(&mpt->m_mutex); + + cmd->cmd_flags = CFLAG_FREE; + kmem_cache_free(mpt->m_kmem_cache, cmd); + return (NULL); } - if (failure) { + mpt->m_extreq_sense_refcount++; + ASSERT(mpt->m_extreq_sense_refcount > 0); + mutex_exit(&mpt->m_mutex); + + /* + * if extern alloc fails, all will be + * deallocated, including cmd + */ + failure = mptsas_pkt_alloc_extern(mpt, cmd, + cmdlen, tgtlen, statuslen, kf); + + if (failure != 0 || cmd->cmd_extrqslen == 0) { /* - * if extern allocation fails, it will - * deallocate the new pkt as well + * If the external packet space allocation + * failed, or we didn't allocate the sense + * data buffer for DMA we need to decrease the + * reference counter. */ - return (NULL); + mutex_enter(&mpt->m_mutex); + ASSERT(mpt->m_extreq_sense_refcount > 0); + mpt->m_extreq_sense_refcount--; + if (mpt->m_extreq_sense_refcount == 0) + cv_broadcast( + &mpt->m_extreq_sense_refcount_cv); + mutex_exit(&mpt->m_mutex); + + if (failure != 0) { + /* + * if extern allocation fails, it will + * deallocate the new pkt as well + */ + return (NULL); + } } } new_cmd = cmd; @@ -3979,7 +4017,22 @@ mptsas_scsi_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt) cmd->cmd_flags = CFLAG_FREE; kmem_cache_free(mpt->m_kmem_cache, (void *)cmd); } else { + boolean_t extrqslen = cmd->cmd_extrqslen != 0; + mptsas_pkt_destroy_extern(mpt, cmd); + + /* + * If the packet had the sense data buffer for DMA allocated we + * need to decrease the reference counter. + */ + if (extrqslen) { + mutex_enter(&mpt->m_mutex); + ASSERT(mpt->m_extreq_sense_refcount > 0); + mpt->m_extreq_sense_refcount--; + if (mpt->m_extreq_sense_refcount == 0) + cv_broadcast(&mpt->m_extreq_sense_refcount_cv); + mutex_exit(&mpt->m_mutex); + } } } @@ -5695,7 +5748,7 @@ mptsas_check_scsi_io_error(mptsas_t *mpt, pMpi2SCSIIOReply_t reply, static void mptsas_check_task_mgt(mptsas_t *mpt, pMpi2SCSIManagementReply_t reply, - mptsas_cmd_t *cmd) + mptsas_cmd_t *cmd) { uint8_t task_type; uint16_t ioc_status; @@ -6129,7 +6182,8 @@ mptsas_update_phymask(mptsas_t *mpt) * 7. Physical disks are removed because of RAID creation. */ static void -mptsas_handle_dr(void *args) { +mptsas_handle_dr(void *args) +{ mptsas_topo_change_list_t *topo_node = NULL; mptsas_topo_change_list_t *save_node = NULL; mptsas_t *mpt; @@ -8986,7 +9040,7 @@ mptsas_do_scsi_reset(mptsas_t *mpt, uint16_t devhdl) static int mptsas_scsi_reset_notify(struct scsi_address *ap, int flag, - void (*callback)(caddr_t), caddr_t arg) + void (*callback)(caddr_t), caddr_t arg) { mptsas_t *mpt = ADDR2MPT(ap); @@ -12787,6 +12841,12 @@ mptsas_restart_ioc(mptsas_t *mpt) mpt->m_in_reset = TRUE; /* + * Wait until all the allocated sense data buffers for DMA are freed. + */ + while (mpt->m_extreq_sense_refcount > 0) + cv_wait(&mpt->m_extreq_sense_refcount_cv, &mpt->m_mutex); + + /* * Set all throttles to HOLD */ for (ptgt = refhash_first(mpt->m_targets); ptgt != NULL; diff --git a/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_var.h b/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_var.h index 3983188fce..2465883c36 100644 --- a/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_var.h +++ b/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_var.h @@ -808,6 +808,8 @@ typedef struct mptsas { uint64_t m_req_frame_dma_addr; caddr_t m_req_sense; caddr_t m_extreq_sense; + uint_t m_extreq_sense_refcount; + kcondvar_t m_extreq_sense_refcount_cv; uint64_t m_req_sense_dma_addr; caddr_t m_reply_frame; uint64_t m_reply_frame_dma_addr; -- 2.11.4.GIT