11e66f787SSean Bruno /*- 21e66f787SSean Bruno * Copyright (c) 2018 Microsemi Corporation. 31e66f787SSean Bruno * All rights reserved. 41e66f787SSean Bruno * 51e66f787SSean Bruno * Redistribution and use in source and binary forms, with or without 61e66f787SSean Bruno * modification, are permitted provided that the following conditions 71e66f787SSean Bruno * are met: 81e66f787SSean Bruno * 1. Redistributions of source code must retain the above copyright 91e66f787SSean Bruno * notice, this list of conditions and the following disclaimer. 101e66f787SSean Bruno * 2. Redistributions in binary form must reproduce the above copyright 111e66f787SSean Bruno * notice, this list of conditions and the following disclaimer in the 121e66f787SSean Bruno * documentation and/or other materials provided with the distribution. 131e66f787SSean Bruno * 141e66f787SSean Bruno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 151e66f787SSean Bruno * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 161e66f787SSean Bruno * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 171e66f787SSean Bruno * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 181e66f787SSean Bruno * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 191e66f787SSean Bruno * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 201e66f787SSean Bruno * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 211e66f787SSean Bruno * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 221e66f787SSean Bruno * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 231e66f787SSean Bruno * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 241e66f787SSean Bruno * SUCH DAMAGE. 251e66f787SSean Bruno */ 261e66f787SSean Bruno 271e66f787SSean Bruno /* $FreeBSD$ */ 281e66f787SSean Bruno 291e66f787SSean Bruno #include "smartpqi_includes.h" 301e66f787SSean Bruno 311e66f787SSean Bruno /* Validate the scsi sense response code */ 321e66f787SSean Bruno static inline boolean_t pqisrc_scsi_sense_valid(const struct sense_header_scsi *sshdr) 331e66f787SSean Bruno { 341e66f787SSean Bruno DBG_FUNC("IN\n"); 351e66f787SSean Bruno 361e66f787SSean Bruno if (!sshdr) 371e66f787SSean Bruno return false; 381e66f787SSean Bruno 391e66f787SSean Bruno DBG_FUNC("OUT\n"); 401e66f787SSean Bruno 411e66f787SSean Bruno return (sshdr->response_code & 0x70) == 0x70; 421e66f787SSean Bruno } 431e66f787SSean Bruno 44*b17f4335SSean Bruno /* Initialize target ID pool for HBA/PDs */ 45*b17f4335SSean Bruno void pqisrc_init_targetid_pool(pqisrc_softstate_t *softs) 46*b17f4335SSean Bruno { 47*b17f4335SSean Bruno int i, tid = PQI_MAX_PHYSICALS + PQI_MAX_LOGICALS - 1; 48*b17f4335SSean Bruno 49*b17f4335SSean Bruno for(i = 0; i < PQI_MAX_PHYSICALS; i++) { 50*b17f4335SSean Bruno softs->tid_pool.tid[i] = tid--; 51*b17f4335SSean Bruno } 52*b17f4335SSean Bruno softs->tid_pool.index = i - 1; 53*b17f4335SSean Bruno } 54*b17f4335SSean Bruno 55*b17f4335SSean Bruno int pqisrc_alloc_tid(pqisrc_softstate_t *softs) 56*b17f4335SSean Bruno { 57*b17f4335SSean Bruno if(softs->tid_pool.index <= -1) { 58*b17f4335SSean Bruno DBG_ERR("Target ID exhausted\n"); 59*b17f4335SSean Bruno return INVALID_ELEM; 60*b17f4335SSean Bruno } 61*b17f4335SSean Bruno 62*b17f4335SSean Bruno return softs->tid_pool.tid[softs->tid_pool.index--]; 63*b17f4335SSean Bruno } 64*b17f4335SSean Bruno 65*b17f4335SSean Bruno void pqisrc_free_tid(pqisrc_softstate_t *softs, int tid) 66*b17f4335SSean Bruno { 67*b17f4335SSean Bruno if(softs->tid_pool.index >= PQI_MAX_PHYSICALS) { 68*b17f4335SSean Bruno DBG_ERR("Target ID queue is full\n"); 69*b17f4335SSean Bruno return; 70*b17f4335SSean Bruno } 71*b17f4335SSean Bruno 72*b17f4335SSean Bruno softs->tid_pool.index++; 73*b17f4335SSean Bruno softs->tid_pool.tid[softs->tid_pool.index] = tid; 74*b17f4335SSean Bruno } 75*b17f4335SSean Bruno 761e66f787SSean Bruno /* Update scsi sense info to a local buffer*/ 771e66f787SSean Bruno boolean_t pqisrc_update_scsi_sense(const uint8_t *buff, int len, 781e66f787SSean Bruno struct sense_header_scsi *header) 791e66f787SSean Bruno { 801e66f787SSean Bruno 811e66f787SSean Bruno DBG_FUNC("IN\n"); 821e66f787SSean Bruno 831e66f787SSean Bruno if (!buff || !len) 841e66f787SSean Bruno return false; 851e66f787SSean Bruno 861e66f787SSean Bruno memset(header, 0, sizeof(struct sense_header_scsi)); 871e66f787SSean Bruno 881e66f787SSean Bruno header->response_code = (buff[0] & 0x7f); 891e66f787SSean Bruno 901e66f787SSean Bruno if (!pqisrc_scsi_sense_valid(header)) 911e66f787SSean Bruno return false; 921e66f787SSean Bruno 931e66f787SSean Bruno if (header->response_code >= 0x72) { 941e66f787SSean Bruno /* descriptor format */ 951e66f787SSean Bruno if (len > 1) 961e66f787SSean Bruno header->sense_key = (buff[1] & 0xf); 971e66f787SSean Bruno if (len > 2) 981e66f787SSean Bruno header->asc = buff[2]; 991e66f787SSean Bruno if (len > 3) 1001e66f787SSean Bruno header->ascq = buff[3]; 1011e66f787SSean Bruno if (len > 7) 1021e66f787SSean Bruno header->additional_length = buff[7]; 1031e66f787SSean Bruno } else { 1041e66f787SSean Bruno /* fixed format */ 1051e66f787SSean Bruno if (len > 2) 1061e66f787SSean Bruno header->sense_key = (buff[2] & 0xf); 1071e66f787SSean Bruno if (len > 7) { 1081e66f787SSean Bruno len = (len < (buff[7] + 8)) ? 1091e66f787SSean Bruno len : (buff[7] + 8); 1101e66f787SSean Bruno if (len > 12) 1111e66f787SSean Bruno header->asc = buff[12]; 1121e66f787SSean Bruno if (len > 13) 1131e66f787SSean Bruno header->ascq = buff[13]; 1141e66f787SSean Bruno } 1151e66f787SSean Bruno } 1161e66f787SSean Bruno 1171e66f787SSean Bruno DBG_FUNC("OUT\n"); 1181e66f787SSean Bruno 1191e66f787SSean Bruno return true; 1201e66f787SSean Bruno } 1211e66f787SSean Bruno 1221e66f787SSean Bruno /* 1231e66f787SSean Bruno * Function used to build the internal raid request and analyze the response 1241e66f787SSean Bruno */ 1251e66f787SSean Bruno int pqisrc_build_send_raid_request(pqisrc_softstate_t *softs, pqisrc_raid_req_t *request, 1261e66f787SSean Bruno void *buff, size_t datasize, uint8_t cmd, uint16_t vpd_page, uint8_t *scsi3addr, 1271e66f787SSean Bruno raid_path_error_info_elem_t *error_info) 1281e66f787SSean Bruno { 1291e66f787SSean Bruno 1301e66f787SSean Bruno uint8_t *cdb; 1311e66f787SSean Bruno int ret = PQI_STATUS_SUCCESS; 1321e66f787SSean Bruno uint32_t tag = 0; 1331e66f787SSean Bruno struct dma_mem device_mem; 1341e66f787SSean Bruno sgt_t *sgd; 1351e66f787SSean Bruno 1361e66f787SSean Bruno ib_queue_t *ib_q = &softs->op_raid_ib_q[PQI_DEFAULT_IB_QUEUE]; 1371e66f787SSean Bruno ob_queue_t *ob_q = &softs->op_ob_q[PQI_DEFAULT_IB_QUEUE]; 1381e66f787SSean Bruno 1391e66f787SSean Bruno rcb_t *rcb = NULL; 1401e66f787SSean Bruno 1411e66f787SSean Bruno DBG_FUNC("IN\n"); 1421e66f787SSean Bruno 1431e66f787SSean Bruno memset(&device_mem, 0, sizeof(struct dma_mem)); 1441e66f787SSean Bruno 1451e66f787SSean Bruno /* for TUR datasize: 0 buff: NULL */ 1461e66f787SSean Bruno if (datasize) { 1471e66f787SSean Bruno device_mem.tag = "device_mem"; 1481e66f787SSean Bruno device_mem.size = datasize; 1491e66f787SSean Bruno device_mem.align = PQISRC_DEFAULT_DMA_ALIGN; 1501e66f787SSean Bruno 1511e66f787SSean Bruno ret = os_dma_mem_alloc(softs, &device_mem); 1521e66f787SSean Bruno 1531e66f787SSean Bruno if (ret) { 1541e66f787SSean Bruno DBG_ERR("failed to allocate dma memory for device_mem return code %d\n", ret); 1551e66f787SSean Bruno return ret; 1561e66f787SSean Bruno } 1571e66f787SSean Bruno 1581e66f787SSean Bruno sgd = (sgt_t *)&request->sg_descriptors[0]; 1591e66f787SSean Bruno 1601e66f787SSean Bruno sgd->addr = device_mem.dma_addr; 1611e66f787SSean Bruno sgd->len = datasize; 1621e66f787SSean Bruno sgd->flags = SG_FLAG_LAST; 1631e66f787SSean Bruno 1641e66f787SSean Bruno } 1651e66f787SSean Bruno 1661e66f787SSean Bruno /* Build raid path request */ 1671e66f787SSean Bruno request->header.iu_type = PQI_IU_TYPE_RAID_PATH_IO_REQUEST; 1681e66f787SSean Bruno 1691e66f787SSean Bruno request->header.iu_length = LE_16(offsetof(pqisrc_raid_req_t, 1701e66f787SSean Bruno sg_descriptors[1]) - PQI_REQUEST_HEADER_LENGTH); 1711e66f787SSean Bruno request->buffer_length = LE_32(datasize); 1721e66f787SSean Bruno memcpy(request->lun_number, scsi3addr, sizeof(request->lun_number)); 1731e66f787SSean Bruno request->task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE; 1741e66f787SSean Bruno request->additional_cdb_bytes_usage = PQI_ADDITIONAL_CDB_BYTES_0; 1751e66f787SSean Bruno 1761e66f787SSean Bruno cdb = request->cdb; 1771e66f787SSean Bruno 1781e66f787SSean Bruno switch (cmd) { 1791e66f787SSean Bruno case SA_INQUIRY: 1801e66f787SSean Bruno request->data_direction = SOP_DATA_DIR_TO_DEVICE; 1811e66f787SSean Bruno cdb[0] = SA_INQUIRY; 1821e66f787SSean Bruno if (vpd_page & VPD_PAGE) { 1831e66f787SSean Bruno cdb[1] = 0x1; 1841e66f787SSean Bruno cdb[2] = (uint8_t)vpd_page; 1851e66f787SSean Bruno } 1861e66f787SSean Bruno cdb[4] = (uint8_t)datasize; 1871e66f787SSean Bruno break; 1881e66f787SSean Bruno case SA_REPORT_LOG: 1891e66f787SSean Bruno case SA_REPORT_PHYS: 1901e66f787SSean Bruno request->data_direction = SOP_DATA_DIR_TO_DEVICE; 1911e66f787SSean Bruno cdb[0] = cmd; 1921e66f787SSean Bruno if (cmd == SA_REPORT_PHYS) 1931e66f787SSean Bruno cdb[1] = SA_REPORT_PHYS_EXTENDED; 1941e66f787SSean Bruno else 1951e66f787SSean Bruno cdb[1] = SA_REPORT_LOG_EXTENDED; 1961e66f787SSean Bruno cdb[8] = (uint8_t)((datasize) >> 8); 1971e66f787SSean Bruno cdb[9] = (uint8_t)datasize; 1981e66f787SSean Bruno break; 1991e66f787SSean Bruno case TEST_UNIT_READY: 2001e66f787SSean Bruno request->data_direction = SOP_DATA_DIR_NONE; 2011e66f787SSean Bruno break; 2021e66f787SSean Bruno case SA_GET_RAID_MAP: 2031e66f787SSean Bruno request->data_direction = SOP_DATA_DIR_TO_DEVICE; 2041e66f787SSean Bruno cdb[0] = SA_CISS_READ; 2051e66f787SSean Bruno cdb[1] = cmd; 2061e66f787SSean Bruno cdb[8] = (uint8_t)((datasize) >> 8); 2071e66f787SSean Bruno cdb[9] = (uint8_t)datasize; 2081e66f787SSean Bruno break; 2091e66f787SSean Bruno case SA_CACHE_FLUSH: 2101e66f787SSean Bruno request->data_direction = SOP_DATA_DIR_FROM_DEVICE; 211*b17f4335SSean Bruno memcpy(device_mem.virt_addr, buff, datasize); 2121e66f787SSean Bruno cdb[0] = BMIC_WRITE; 2131e66f787SSean Bruno cdb[6] = BMIC_CACHE_FLUSH; 2141e66f787SSean Bruno cdb[7] = (uint8_t)((datasize) << 8); 2151e66f787SSean Bruno cdb[8] = (uint8_t)((datasize) >> 8); 2161e66f787SSean Bruno break; 2171e66f787SSean Bruno case BMIC_IDENTIFY_CONTROLLER: 2181e66f787SSean Bruno case BMIC_IDENTIFY_PHYSICAL_DEVICE: 2191e66f787SSean Bruno request->data_direction = SOP_DATA_DIR_TO_DEVICE; 2201e66f787SSean Bruno cdb[0] = BMIC_READ; 2211e66f787SSean Bruno cdb[6] = cmd; 2221e66f787SSean Bruno cdb[7] = (uint8_t)((datasize) << 8); 2231e66f787SSean Bruno cdb[8] = (uint8_t)((datasize) >> 8); 2241e66f787SSean Bruno break; 2251e66f787SSean Bruno case BMIC_WRITE_HOST_WELLNESS: 2261e66f787SSean Bruno request->data_direction = SOP_DATA_DIR_FROM_DEVICE; 2271e66f787SSean Bruno memcpy(device_mem.virt_addr, buff, datasize); 2281e66f787SSean Bruno cdb[0] = BMIC_WRITE; 2291e66f787SSean Bruno cdb[6] = cmd; 2301e66f787SSean Bruno cdb[7] = (uint8_t)((datasize) << 8); 2311e66f787SSean Bruno cdb[8] = (uint8_t)((datasize) >> 8); 2321e66f787SSean Bruno break; 2331e66f787SSean Bruno case BMIC_SENSE_SUBSYSTEM_INFORMATION: 2341e66f787SSean Bruno request->data_direction = SOP_DATA_DIR_TO_DEVICE; 2351e66f787SSean Bruno cdb[0] = BMIC_READ; 2361e66f787SSean Bruno cdb[6] = cmd; 2371e66f787SSean Bruno cdb[7] = (uint8_t)((datasize) << 8); 2381e66f787SSean Bruno cdb[8] = (uint8_t)((datasize) >> 8); 2391e66f787SSean Bruno break; 2401e66f787SSean Bruno default: 2411e66f787SSean Bruno DBG_ERR("unknown command 0x%x", cmd); 2421e66f787SSean Bruno break; 2431e66f787SSean Bruno } 2441e66f787SSean Bruno 2451e66f787SSean Bruno tag = pqisrc_get_tag(&softs->taglist); 2461e66f787SSean Bruno if (INVALID_ELEM == tag) { 2471e66f787SSean Bruno DBG_ERR("Tag not available\n"); 2481e66f787SSean Bruno ret = PQI_STATUS_FAILURE; 2491e66f787SSean Bruno goto err_notag; 2501e66f787SSean Bruno } 2511e66f787SSean Bruno 2521e66f787SSean Bruno ((pqisrc_raid_req_t *)request)->request_id = tag; 2531e66f787SSean Bruno ((pqisrc_raid_req_t *)request)->error_index = ((pqisrc_raid_req_t *)request)->request_id; 2541e66f787SSean Bruno ((pqisrc_raid_req_t *)request)->response_queue_id = ob_q->q_id; 2551e66f787SSean Bruno rcb = &softs->rcb[tag]; 2561e66f787SSean Bruno rcb->success_cmp_callback = pqisrc_process_internal_raid_response_success; 2571e66f787SSean Bruno rcb->error_cmp_callback = pqisrc_process_internal_raid_response_error; 2581e66f787SSean Bruno 2591e66f787SSean Bruno rcb->req_pending = true; 2601e66f787SSean Bruno rcb->tag = tag; 2611e66f787SSean Bruno /* Submit Command */ 2621e66f787SSean Bruno ret = pqisrc_submit_cmnd(softs, ib_q, request); 2631e66f787SSean Bruno 2641e66f787SSean Bruno if (ret != PQI_STATUS_SUCCESS) { 2651e66f787SSean Bruno DBG_ERR("Unable to submit command\n"); 2661e66f787SSean Bruno goto err_out; 2671e66f787SSean Bruno } 2681e66f787SSean Bruno 2691e66f787SSean Bruno ret = pqisrc_wait_on_condition(softs, rcb); 2701e66f787SSean Bruno if (ret != PQI_STATUS_SUCCESS) { 2711e66f787SSean Bruno DBG_ERR("Internal RAID request timed out: cmd : 0x%c\n", cmd); 2721e66f787SSean Bruno goto err_out; 2731e66f787SSean Bruno } 2741e66f787SSean Bruno 2751e66f787SSean Bruno if (datasize) { 2761e66f787SSean Bruno if (buff) { 2771e66f787SSean Bruno memcpy(buff, device_mem.virt_addr, datasize); 2781e66f787SSean Bruno } 2791e66f787SSean Bruno os_dma_mem_free(softs, &device_mem); 2801e66f787SSean Bruno } 2811e66f787SSean Bruno 2821e66f787SSean Bruno ret = rcb->status; 2831e66f787SSean Bruno if (ret) { 2841e66f787SSean Bruno if(error_info) { 2851e66f787SSean Bruno memcpy(error_info, 2861e66f787SSean Bruno rcb->error_info, 2871e66f787SSean Bruno sizeof(*error_info)); 2881e66f787SSean Bruno 2891e66f787SSean Bruno if (error_info->data_out_result == 2901e66f787SSean Bruno PQI_RAID_DATA_IN_OUT_UNDERFLOW) { 2911e66f787SSean Bruno ret = PQI_STATUS_SUCCESS; 2921e66f787SSean Bruno } 2931e66f787SSean Bruno else{ 294*b17f4335SSean Bruno DBG_DISC("Error!! Bus=%u Target=%u, Cmd=0x%x," 2951e66f787SSean Bruno "Ret=%d\n", BMIC_GET_LEVEL_2_BUS(scsi3addr), 2961e66f787SSean Bruno BMIC_GET_LEVEL_TWO_TARGET(scsi3addr), 2971e66f787SSean Bruno cmd, ret); 2981e66f787SSean Bruno ret = PQI_STATUS_FAILURE; 2991e66f787SSean Bruno } 3001e66f787SSean Bruno } 3011e66f787SSean Bruno } else { 3021e66f787SSean Bruno if(error_info) { 3031e66f787SSean Bruno ret = PQI_STATUS_SUCCESS; 3041e66f787SSean Bruno memset(error_info, 0, sizeof(*error_info)); 3051e66f787SSean Bruno } 3061e66f787SSean Bruno } 3071e66f787SSean Bruno 3081e66f787SSean Bruno os_reset_rcb(rcb); 3091e66f787SSean Bruno pqisrc_put_tag(&softs->taglist, ((pqisrc_raid_req_t *)request)->request_id); 3101e66f787SSean Bruno DBG_FUNC("OUT\n"); 3111e66f787SSean Bruno return ret; 3121e66f787SSean Bruno 3131e66f787SSean Bruno err_out: 3141e66f787SSean Bruno DBG_ERR("Error!! Bus=%u Target=%u, Cmd=0x%x, Ret=%d\n", 3151e66f787SSean Bruno BMIC_GET_LEVEL_2_BUS(scsi3addr), BMIC_GET_LEVEL_TWO_TARGET(scsi3addr), 3161e66f787SSean Bruno cmd, ret); 3171e66f787SSean Bruno os_reset_rcb(rcb); 3181e66f787SSean Bruno pqisrc_put_tag(&softs->taglist, ((pqisrc_raid_req_t *)request)->request_id); 3191e66f787SSean Bruno err_notag: 3201e66f787SSean Bruno if (datasize) 3211e66f787SSean Bruno os_dma_mem_free(softs, &device_mem); 3221e66f787SSean Bruno DBG_FUNC("FAILED \n"); 3231e66f787SSean Bruno return ret; 3241e66f787SSean Bruno } 3251e66f787SSean Bruno 3261e66f787SSean Bruno /* common function used to send report physical and logical luns cmnds*/ 3271e66f787SSean Bruno static int pqisrc_report_luns(pqisrc_softstate_t *softs, uint8_t cmd, 3281e66f787SSean Bruno void *buff, size_t buf_len) 3291e66f787SSean Bruno { 3301e66f787SSean Bruno int ret; 3311e66f787SSean Bruno pqisrc_raid_req_t request; 3321e66f787SSean Bruno 3331e66f787SSean Bruno DBG_FUNC("IN\n"); 3341e66f787SSean Bruno 3351e66f787SSean Bruno memset(&request, 0, sizeof(request)); 3361e66f787SSean Bruno ret = pqisrc_build_send_raid_request(softs, &request, buff, 3371e66f787SSean Bruno buf_len, cmd, 0, (uint8_t *)RAID_CTLR_LUNID, NULL); 3381e66f787SSean Bruno 3391e66f787SSean Bruno DBG_FUNC("OUT\n"); 3401e66f787SSean Bruno 3411e66f787SSean Bruno return ret; 3421e66f787SSean Bruno } 3431e66f787SSean Bruno 3441e66f787SSean Bruno /* subroutine used to get physical and logical luns of the device */ 3451e66f787SSean Bruno static int pqisrc_get_physical_logical_luns(pqisrc_softstate_t *softs, uint8_t cmd, 3461e66f787SSean Bruno reportlun_data_ext_t **buff, size_t *data_length) 3471e66f787SSean Bruno { 3481e66f787SSean Bruno int ret; 3491e66f787SSean Bruno size_t list_len; 3501e66f787SSean Bruno size_t data_len; 3511e66f787SSean Bruno size_t new_lun_list_length; 3521e66f787SSean Bruno reportlun_data_ext_t *lun_data; 3531e66f787SSean Bruno reportlun_header_t report_lun_header; 3541e66f787SSean Bruno 3551e66f787SSean Bruno DBG_FUNC("IN\n"); 3561e66f787SSean Bruno 3571e66f787SSean Bruno ret = pqisrc_report_luns(softs, cmd, &report_lun_header, 3581e66f787SSean Bruno sizeof(report_lun_header)); 3591e66f787SSean Bruno 3601e66f787SSean Bruno if (ret) { 3611e66f787SSean Bruno DBG_ERR("failed return code: %d\n", ret); 3621e66f787SSean Bruno return ret; 3631e66f787SSean Bruno } 3641e66f787SSean Bruno list_len = BE_32(report_lun_header.list_length); 3651e66f787SSean Bruno 3661e66f787SSean Bruno retry: 3671e66f787SSean Bruno data_len = sizeof(reportlun_header_t) + list_len; 3681e66f787SSean Bruno *data_length = data_len; 3691e66f787SSean Bruno 3701e66f787SSean Bruno lun_data = os_mem_alloc(softs, data_len); 3711e66f787SSean Bruno 3721e66f787SSean Bruno if (!lun_data) { 3731e66f787SSean Bruno DBG_ERR("failed to allocate memory for lun_data\n"); 3741e66f787SSean Bruno return PQI_STATUS_FAILURE; 3751e66f787SSean Bruno } 3761e66f787SSean Bruno 3771e66f787SSean Bruno if (list_len == 0) { 378*b17f4335SSean Bruno DBG_DISC("list_len is 0\n"); 3791e66f787SSean Bruno memcpy(lun_data, &report_lun_header, sizeof(report_lun_header)); 3801e66f787SSean Bruno goto out; 3811e66f787SSean Bruno } 3821e66f787SSean Bruno 3831e66f787SSean Bruno ret = pqisrc_report_luns(softs, cmd, lun_data, data_len); 3841e66f787SSean Bruno 3851e66f787SSean Bruno if (ret) { 3861e66f787SSean Bruno DBG_ERR("error\n"); 3871e66f787SSean Bruno goto error; 3881e66f787SSean Bruno } 3891e66f787SSean Bruno 3901e66f787SSean Bruno new_lun_list_length = BE_32(lun_data->header.list_length); 3911e66f787SSean Bruno 3921e66f787SSean Bruno if (new_lun_list_length > list_len) { 3931e66f787SSean Bruno list_len = new_lun_list_length; 3941e66f787SSean Bruno os_mem_free(softs, (void *)lun_data, data_len); 3951e66f787SSean Bruno goto retry; 3961e66f787SSean Bruno } 3971e66f787SSean Bruno 3981e66f787SSean Bruno out: 3991e66f787SSean Bruno *buff = lun_data; 4001e66f787SSean Bruno DBG_FUNC("OUT\n"); 4011e66f787SSean Bruno return 0; 4021e66f787SSean Bruno 4031e66f787SSean Bruno error: 4041e66f787SSean Bruno os_mem_free(softs, (void *)lun_data, data_len); 4051e66f787SSean Bruno DBG_ERR("FAILED\n"); 4061e66f787SSean Bruno return ret; 4071e66f787SSean Bruno } 4081e66f787SSean Bruno 4091e66f787SSean Bruno /* 4101e66f787SSean Bruno * Function used to get physical and logical device list 4111e66f787SSean Bruno */ 4121e66f787SSean Bruno static int pqisrc_get_phys_log_device_list(pqisrc_softstate_t *softs, 4131e66f787SSean Bruno reportlun_data_ext_t **physical_dev_list, 4141e66f787SSean Bruno reportlun_data_ext_t **logical_dev_list, 4151e66f787SSean Bruno size_t *phys_data_length, 4161e66f787SSean Bruno size_t *log_data_length) 4171e66f787SSean Bruno { 4181e66f787SSean Bruno int ret = PQI_STATUS_SUCCESS; 4191e66f787SSean Bruno size_t logical_list_length; 4201e66f787SSean Bruno size_t logdev_data_length; 4211e66f787SSean Bruno size_t data_length; 4221e66f787SSean Bruno reportlun_data_ext_t *local_logdev_list; 4231e66f787SSean Bruno reportlun_data_ext_t *logdev_data; 4241e66f787SSean Bruno reportlun_header_t report_lun_header; 4251e66f787SSean Bruno 4261e66f787SSean Bruno 4271e66f787SSean Bruno DBG_FUNC("IN\n"); 4281e66f787SSean Bruno 4291e66f787SSean Bruno ret = pqisrc_get_physical_logical_luns(softs, SA_REPORT_PHYS, physical_dev_list, phys_data_length); 4301e66f787SSean Bruno if (ret) { 4311e66f787SSean Bruno DBG_ERR("report physical LUNs failed"); 4321e66f787SSean Bruno return ret; 4331e66f787SSean Bruno } 4341e66f787SSean Bruno 4351e66f787SSean Bruno ret = pqisrc_get_physical_logical_luns(softs, SA_REPORT_LOG, logical_dev_list, log_data_length); 4361e66f787SSean Bruno if (ret) { 4371e66f787SSean Bruno DBG_ERR("report logical LUNs failed"); 4381e66f787SSean Bruno return ret; 4391e66f787SSean Bruno } 4401e66f787SSean Bruno 4411e66f787SSean Bruno 4421e66f787SSean Bruno logdev_data = *logical_dev_list; 4431e66f787SSean Bruno 4441e66f787SSean Bruno if (logdev_data) { 4451e66f787SSean Bruno logical_list_length = 4461e66f787SSean Bruno BE_32(logdev_data->header.list_length); 4471e66f787SSean Bruno } else { 4481e66f787SSean Bruno memset(&report_lun_header, 0, sizeof(report_lun_header)); 4491e66f787SSean Bruno logdev_data = 4501e66f787SSean Bruno (reportlun_data_ext_t *)&report_lun_header; 4511e66f787SSean Bruno logical_list_length = 0; 4521e66f787SSean Bruno } 4531e66f787SSean Bruno 4541e66f787SSean Bruno logdev_data_length = sizeof(reportlun_header_t) + 4551e66f787SSean Bruno logical_list_length; 4561e66f787SSean Bruno 4571e66f787SSean Bruno /* Adding LOGICAL device entry for controller */ 4581e66f787SSean Bruno local_logdev_list = os_mem_alloc(softs, 4591e66f787SSean Bruno logdev_data_length + sizeof(reportlun_ext_entry_t)); 4601e66f787SSean Bruno if (!local_logdev_list) { 4611e66f787SSean Bruno data_length = *log_data_length; 4621e66f787SSean Bruno os_mem_free(softs, (char *)*logical_dev_list, data_length); 4631e66f787SSean Bruno *logical_dev_list = NULL; 4641e66f787SSean Bruno return PQI_STATUS_FAILURE; 4651e66f787SSean Bruno } 4661e66f787SSean Bruno 4671e66f787SSean Bruno memcpy(local_logdev_list, logdev_data, logdev_data_length); 4681e66f787SSean Bruno memset((uint8_t *)local_logdev_list + logdev_data_length, 0, 4691e66f787SSean Bruno sizeof(reportlun_ext_entry_t)); 4701e66f787SSean Bruno local_logdev_list->header.list_length = BE_32(logical_list_length + 4711e66f787SSean Bruno sizeof(reportlun_ext_entry_t)); 4721e66f787SSean Bruno data_length = *log_data_length; 4731e66f787SSean Bruno os_mem_free(softs, (char *)*logical_dev_list, data_length); 4741e66f787SSean Bruno *log_data_length = logdev_data_length + sizeof(reportlun_ext_entry_t); 4751e66f787SSean Bruno *logical_dev_list = local_logdev_list; 4761e66f787SSean Bruno 4771e66f787SSean Bruno DBG_FUNC("OUT\n"); 4781e66f787SSean Bruno 4791e66f787SSean Bruno return ret; 4801e66f787SSean Bruno } 4811e66f787SSean Bruno 4821e66f787SSean Bruno /* Subroutine used to set Bus-Target-Lun for the requested device */ 4831e66f787SSean Bruno static inline void pqisrc_set_btl(pqi_scsi_dev_t *device, 4841e66f787SSean Bruno int bus, int target, int lun) 4851e66f787SSean Bruno { 4861e66f787SSean Bruno DBG_FUNC("IN\n"); 4871e66f787SSean Bruno 4881e66f787SSean Bruno device->bus = bus; 4891e66f787SSean Bruno device->target = target; 4901e66f787SSean Bruno device->lun = lun; 4911e66f787SSean Bruno 4921e66f787SSean Bruno DBG_FUNC("OUT\n"); 4931e66f787SSean Bruno } 4941e66f787SSean Bruno 4951e66f787SSean Bruno inline boolean_t pqisrc_is_external_raid_device(pqi_scsi_dev_t *device) 4961e66f787SSean Bruno { 4971e66f787SSean Bruno return device->is_external_raid_device; 4981e66f787SSean Bruno } 4991e66f787SSean Bruno 5001e66f787SSean Bruno static inline boolean_t pqisrc_is_external_raid_addr(uint8_t *scsi3addr) 5011e66f787SSean Bruno { 5021e66f787SSean Bruno return scsi3addr[2] != 0; 5031e66f787SSean Bruno } 5041e66f787SSean Bruno 5051e66f787SSean Bruno /* Function used to assign Bus-Target-Lun for the requested device */ 5061e66f787SSean Bruno static void pqisrc_assign_btl(pqi_scsi_dev_t *device) 5071e66f787SSean Bruno { 5081e66f787SSean Bruno uint8_t *scsi3addr; 5091e66f787SSean Bruno uint32_t lunid; 5101e66f787SSean Bruno uint32_t bus; 5111e66f787SSean Bruno uint32_t target; 5121e66f787SSean Bruno uint32_t lun; 5131e66f787SSean Bruno DBG_FUNC("IN\n"); 5141e66f787SSean Bruno 5151e66f787SSean Bruno scsi3addr = device->scsi3addr; 5161e66f787SSean Bruno lunid = GET_LE32(scsi3addr); 5171e66f787SSean Bruno 5181e66f787SSean Bruno if (pqisrc_is_hba_lunid(scsi3addr)) { 5191e66f787SSean Bruno /* The specified device is the controller. */ 5201e66f787SSean Bruno pqisrc_set_btl(device, PQI_HBA_BUS, PQI_CTLR_INDEX, lunid & 0x3fff); 5211e66f787SSean Bruno device->target_lun_valid = true; 5221e66f787SSean Bruno return; 5231e66f787SSean Bruno } 5241e66f787SSean Bruno 5251e66f787SSean Bruno if (pqisrc_is_logical_device(device)) { 5261e66f787SSean Bruno if (pqisrc_is_external_raid_device(device)) { 527*b17f4335SSean Bruno DBG_DISC("External Raid Device!!!"); 5281e66f787SSean Bruno bus = PQI_EXTERNAL_RAID_VOLUME_BUS; 5291e66f787SSean Bruno target = (lunid >> 16) & 0x3fff; 5301e66f787SSean Bruno lun = lunid & 0xff; 5311e66f787SSean Bruno } else { 5321e66f787SSean Bruno bus = PQI_RAID_VOLUME_BUS; 5331e66f787SSean Bruno lun = 0; 5341e66f787SSean Bruno target = lunid & 0x3fff; 5351e66f787SSean Bruno } 5361e66f787SSean Bruno pqisrc_set_btl(device, bus, target, lun); 5371e66f787SSean Bruno device->target_lun_valid = true; 5381e66f787SSean Bruno return; 5391e66f787SSean Bruno } 5401e66f787SSean Bruno 5411e66f787SSean Bruno DBG_FUNC("OUT\n"); 5421e66f787SSean Bruno } 5431e66f787SSean Bruno 5441e66f787SSean Bruno /* Build and send the internal INQUIRY command to particular device */ 5451e66f787SSean Bruno static int pqisrc_send_scsi_inquiry(pqisrc_softstate_t *softs, 5461e66f787SSean Bruno uint8_t *scsi3addr, uint16_t vpd_page, uint8_t *buff, int buf_len) 5471e66f787SSean Bruno { 5481e66f787SSean Bruno int ret = PQI_STATUS_SUCCESS; 5491e66f787SSean Bruno pqisrc_raid_req_t request; 5501e66f787SSean Bruno raid_path_error_info_elem_t error_info; 5511e66f787SSean Bruno 5521e66f787SSean Bruno DBG_FUNC("IN\n"); 5531e66f787SSean Bruno 5541e66f787SSean Bruno memset(&request, 0, sizeof(request)); 5551e66f787SSean Bruno ret = pqisrc_build_send_raid_request(softs, &request, buff, buf_len, 5561e66f787SSean Bruno SA_INQUIRY, vpd_page, scsi3addr, &error_info); 5571e66f787SSean Bruno 5581e66f787SSean Bruno DBG_FUNC("OUT\n"); 5591e66f787SSean Bruno return ret; 5601e66f787SSean Bruno } 5611e66f787SSean Bruno 5621e66f787SSean Bruno /* Function used to parse the sense information from response */ 5631e66f787SSean Bruno static void pqisrc_fetch_sense_info(const uint8_t *sense_data, 5641e66f787SSean Bruno unsigned sense_data_length, uint8_t *sense_key, uint8_t *asc, uint8_t *ascq) 5651e66f787SSean Bruno { 5661e66f787SSean Bruno struct sense_header_scsi header; 5671e66f787SSean Bruno 5681e66f787SSean Bruno DBG_FUNC("IN\n"); 5691e66f787SSean Bruno 5701e66f787SSean Bruno *sense_key = 0; 5711e66f787SSean Bruno *ascq = 0; 5721e66f787SSean Bruno *asc = 0; 5731e66f787SSean Bruno 5741e66f787SSean Bruno if (pqisrc_update_scsi_sense(sense_data, sense_data_length, &header)) { 5751e66f787SSean Bruno *sense_key = header.sense_key; 5761e66f787SSean Bruno *asc = header.asc; 5771e66f787SSean Bruno *ascq = header.ascq; 5781e66f787SSean Bruno } 5791e66f787SSean Bruno 580*b17f4335SSean Bruno DBG_DISC("sense_key: %x asc: %x ascq: %x\n", *sense_key, *asc, *ascq); 5811e66f787SSean Bruno 5821e66f787SSean Bruno DBG_FUNC("OUT\n"); 5831e66f787SSean Bruno } 5841e66f787SSean Bruno 5851e66f787SSean Bruno /* Function used to validate volume offline status */ 5861e66f787SSean Bruno static uint8_t pqisrc_get_volume_offline_status(pqisrc_softstate_t *softs, 5871e66f787SSean Bruno uint8_t *scsi3addr) 5881e66f787SSean Bruno { 5891e66f787SSean Bruno int ret = PQI_STATUS_SUCCESS; 5901e66f787SSean Bruno uint8_t status = SA_LV_STATUS_VPD_UNSUPPORTED; 5911e66f787SSean Bruno uint8_t size; 5921e66f787SSean Bruno uint8_t *buff = NULL; 5931e66f787SSean Bruno 5941e66f787SSean Bruno DBG_FUNC("IN\n"); 5951e66f787SSean Bruno 5961e66f787SSean Bruno buff = os_mem_alloc(softs, 64); 5971e66f787SSean Bruno if (!buff) 5981e66f787SSean Bruno return PQI_STATUS_FAILURE; 5991e66f787SSean Bruno 6001e66f787SSean Bruno /* Get the size of the VPD return buff. */ 6011e66f787SSean Bruno ret = pqisrc_send_scsi_inquiry(softs, scsi3addr, VPD_PAGE | SA_VPD_LV_STATUS, 6021e66f787SSean Bruno buff, SCSI_VPD_HEADER_LENGTH); 6031e66f787SSean Bruno 6041e66f787SSean Bruno if (ret) 6051e66f787SSean Bruno goto out; 6061e66f787SSean Bruno 6071e66f787SSean Bruno size = buff[3]; 6081e66f787SSean Bruno 6091e66f787SSean Bruno /* Now get the whole VPD buff. */ 6101e66f787SSean Bruno ret = pqisrc_send_scsi_inquiry(softs, scsi3addr, VPD_PAGE | SA_VPD_LV_STATUS, 6111e66f787SSean Bruno buff, size + SCSI_VPD_HEADER_LENGTH); 6121e66f787SSean Bruno if (ret) 6131e66f787SSean Bruno goto out; 6141e66f787SSean Bruno 6151e66f787SSean Bruno status = buff[4]; 6161e66f787SSean Bruno 6171e66f787SSean Bruno out: 6181e66f787SSean Bruno os_mem_free(softs, (char *)buff, 64); 6191e66f787SSean Bruno DBG_FUNC("OUT\n"); 6201e66f787SSean Bruno 6211e66f787SSean Bruno return status; 6221e66f787SSean Bruno } 6231e66f787SSean Bruno 6241e66f787SSean Bruno 6251e66f787SSean Bruno /* Determine offline status of a volume. Returns appropriate SA_LV_* status.*/ 6261e66f787SSean Bruno static uint8_t pqisrc_get_dev_vol_status(pqisrc_softstate_t *softs, 6271e66f787SSean Bruno uint8_t *scsi3addr) 6281e66f787SSean Bruno { 6291e66f787SSean Bruno int ret = PQI_STATUS_SUCCESS; 6301e66f787SSean Bruno uint8_t *sense_data; 6311e66f787SSean Bruno unsigned sense_data_len; 6321e66f787SSean Bruno uint8_t sense_key; 6331e66f787SSean Bruno uint8_t asc; 6341e66f787SSean Bruno uint8_t ascq; 6351e66f787SSean Bruno uint8_t off_status; 6361e66f787SSean Bruno uint8_t scsi_status; 6371e66f787SSean Bruno pqisrc_raid_req_t request; 6381e66f787SSean Bruno raid_path_error_info_elem_t error_info; 6391e66f787SSean Bruno 6401e66f787SSean Bruno DBG_FUNC("IN\n"); 6411e66f787SSean Bruno 6421e66f787SSean Bruno memset(&request, 0, sizeof(request)); 6431e66f787SSean Bruno ret = pqisrc_build_send_raid_request(softs, &request, NULL, 0, 6441e66f787SSean Bruno TEST_UNIT_READY, 0, scsi3addr, &error_info); 6451e66f787SSean Bruno 6461e66f787SSean Bruno if (ret) 6471e66f787SSean Bruno goto error; 6481e66f787SSean Bruno sense_data = error_info.data; 6491e66f787SSean Bruno sense_data_len = LE_16(error_info.sense_data_len); 6501e66f787SSean Bruno 6511e66f787SSean Bruno if (sense_data_len > sizeof(error_info.data)) 6521e66f787SSean Bruno sense_data_len = sizeof(error_info.data); 6531e66f787SSean Bruno 6541e66f787SSean Bruno pqisrc_fetch_sense_info(sense_data, sense_data_len, &sense_key, &asc, 6551e66f787SSean Bruno &ascq); 6561e66f787SSean Bruno 6571e66f787SSean Bruno scsi_status = error_info.status; 6581e66f787SSean Bruno 6591e66f787SSean Bruno /* scsi status: "CHECK CONDN" / SK: "not ready" ? */ 6601e66f787SSean Bruno if (scsi_status != 2 || 6611e66f787SSean Bruno sense_key != 2 || 6621e66f787SSean Bruno asc != ASC_LUN_NOT_READY) { 6631e66f787SSean Bruno return SA_LV_OK; 6641e66f787SSean Bruno } 6651e66f787SSean Bruno 6661e66f787SSean Bruno /* Determine the reason for not ready state. */ 6671e66f787SSean Bruno off_status = pqisrc_get_volume_offline_status(softs, scsi3addr); 6681e66f787SSean Bruno 669*b17f4335SSean Bruno DBG_DISC("offline_status 0x%x\n", off_status); 6701e66f787SSean Bruno 6711e66f787SSean Bruno /* Keep volume offline in certain cases. */ 6721e66f787SSean Bruno switch (off_status) { 6731e66f787SSean Bruno case SA_LV_UNDERGOING_ERASE: 6741e66f787SSean Bruno case SA_LV_NOT_AVAILABLE: 6751e66f787SSean Bruno case SA_LV_UNDERGOING_RPI: 6761e66f787SSean Bruno case SA_LV_PENDING_RPI: 6771e66f787SSean Bruno case SA_LV_ENCRYPTED_NO_KEY: 6781e66f787SSean Bruno case SA_LV_PLAINTEXT_IN_ENCRYPT_ONLY_CONTROLLER: 6791e66f787SSean Bruno case SA_LV_UNDERGOING_ENCRYPTION: 6801e66f787SSean Bruno case SA_LV_UNDERGOING_ENCRYPTION_REKEYING: 6811e66f787SSean Bruno case SA_LV_ENCRYPTED_IN_NON_ENCRYPTED_CONTROLLER: 6821e66f787SSean Bruno return off_status; 6831e66f787SSean Bruno case SA_LV_STATUS_VPD_UNSUPPORTED: 6841e66f787SSean Bruno /* 6851e66f787SSean Bruno * If the VPD status page isn't available, 6861e66f787SSean Bruno * use ASC/ASCQ to determine state. 6871e66f787SSean Bruno */ 6881e66f787SSean Bruno if (ascq == ASCQ_LUN_NOT_READY_FORMAT_IN_PROGRESS || 6891e66f787SSean Bruno ascq == ASCQ_LUN_NOT_READY_INITIALIZING_CMD_REQ) 6901e66f787SSean Bruno return off_status; 6911e66f787SSean Bruno break; 6921e66f787SSean Bruno } 6931e66f787SSean Bruno 6941e66f787SSean Bruno DBG_FUNC("OUT\n"); 6951e66f787SSean Bruno 6961e66f787SSean Bruno return SA_LV_OK; 6971e66f787SSean Bruno 6981e66f787SSean Bruno error: 6991e66f787SSean Bruno return SA_LV_STATUS_VPD_UNSUPPORTED; 7001e66f787SSean Bruno } 7011e66f787SSean Bruno 7021e66f787SSean Bruno /* Validate the RAID map parameters */ 7031e66f787SSean Bruno static int pqisrc_raid_map_validation(pqisrc_softstate_t *softs, 7041e66f787SSean Bruno pqi_scsi_dev_t *device, pqisrc_raid_map_t *raid_map) 7051e66f787SSean Bruno { 7061e66f787SSean Bruno char *error_msg; 7071e66f787SSean Bruno uint32_t raidmap_size; 7081e66f787SSean Bruno uint32_t r5or6_blocks_per_row; 7091e66f787SSean Bruno unsigned phys_dev_num; 7101e66f787SSean Bruno unsigned num_raidmap_entries; 7111e66f787SSean Bruno 7121e66f787SSean Bruno DBG_FUNC("IN\n"); 7131e66f787SSean Bruno 7141e66f787SSean Bruno raidmap_size = LE_32(raid_map->structure_size); 7151e66f787SSean Bruno if (raidmap_size < offsetof(pqisrc_raid_map_t, dev_data)) { 7161e66f787SSean Bruno error_msg = "RAID map too small\n"; 7171e66f787SSean Bruno goto error; 7181e66f787SSean Bruno } 7191e66f787SSean Bruno 7201e66f787SSean Bruno if (raidmap_size > sizeof(*raid_map)) { 7211e66f787SSean Bruno error_msg = "RAID map too large\n"; 7221e66f787SSean Bruno goto error; 7231e66f787SSean Bruno } 7241e66f787SSean Bruno 7251e66f787SSean Bruno phys_dev_num = LE_16(raid_map->layout_map_count) * 7261e66f787SSean Bruno (LE_16(raid_map->data_disks_per_row) + 7271e66f787SSean Bruno LE_16(raid_map->metadata_disks_per_row)); 7281e66f787SSean Bruno num_raidmap_entries = phys_dev_num * 7291e66f787SSean Bruno LE_16(raid_map->row_cnt); 7301e66f787SSean Bruno 7311e66f787SSean Bruno if (num_raidmap_entries > RAID_MAP_MAX_ENTRIES) { 7321e66f787SSean Bruno error_msg = "invalid number of map entries in RAID map\n"; 7331e66f787SSean Bruno goto error; 7341e66f787SSean Bruno } 7351e66f787SSean Bruno 7361e66f787SSean Bruno if (device->raid_level == SA_RAID_1) { 7371e66f787SSean Bruno if (LE_16(raid_map->layout_map_count) != 2) { 7381e66f787SSean Bruno error_msg = "invalid RAID-1 map\n"; 7391e66f787SSean Bruno goto error; 7401e66f787SSean Bruno } 7411e66f787SSean Bruno } else if (device->raid_level == SA_RAID_ADM) { 7421e66f787SSean Bruno if (LE_16(raid_map->layout_map_count) != 3) { 7431e66f787SSean Bruno error_msg = "invalid RAID-1(ADM) map\n"; 7441e66f787SSean Bruno goto error; 7451e66f787SSean Bruno } 7461e66f787SSean Bruno } else if ((device->raid_level == SA_RAID_5 || 7471e66f787SSean Bruno device->raid_level == SA_RAID_6) && 7481e66f787SSean Bruno LE_16(raid_map->layout_map_count) > 1) { 7491e66f787SSean Bruno /* RAID 50/60 */ 7501e66f787SSean Bruno r5or6_blocks_per_row = 7511e66f787SSean Bruno LE_16(raid_map->strip_size) * 7521e66f787SSean Bruno LE_16(raid_map->data_disks_per_row); 7531e66f787SSean Bruno if (r5or6_blocks_per_row == 0) { 7541e66f787SSean Bruno error_msg = "invalid RAID-5 or RAID-6 map\n"; 7551e66f787SSean Bruno goto error; 7561e66f787SSean Bruno } 7571e66f787SSean Bruno } 7581e66f787SSean Bruno 7591e66f787SSean Bruno DBG_FUNC("OUT\n"); 7601e66f787SSean Bruno 7611e66f787SSean Bruno return 0; 7621e66f787SSean Bruno 7631e66f787SSean Bruno error: 7641e66f787SSean Bruno DBG_ERR("%s\n", error_msg); 7651e66f787SSean Bruno return PQI_STATUS_FAILURE; 7661e66f787SSean Bruno } 7671e66f787SSean Bruno 7681e66f787SSean Bruno /* Get device raidmap for the requested device */ 7691e66f787SSean Bruno static int pqisrc_get_device_raidmap(pqisrc_softstate_t *softs, 7701e66f787SSean Bruno pqi_scsi_dev_t *device) 7711e66f787SSean Bruno { 7721e66f787SSean Bruno int ret = PQI_STATUS_SUCCESS; 7731e66f787SSean Bruno pqisrc_raid_req_t request; 7741e66f787SSean Bruno pqisrc_raid_map_t *raid_map; 7751e66f787SSean Bruno 7761e66f787SSean Bruno DBG_FUNC("IN\n"); 7771e66f787SSean Bruno 7781e66f787SSean Bruno raid_map = os_mem_alloc(softs, sizeof(*raid_map)); 7791e66f787SSean Bruno if (!raid_map) 7801e66f787SSean Bruno return PQI_STATUS_FAILURE; 7811e66f787SSean Bruno 7821e66f787SSean Bruno memset(&request, 0, sizeof(request)); 7831e66f787SSean Bruno ret = pqisrc_build_send_raid_request(softs, &request, raid_map, sizeof(*raid_map), 7841e66f787SSean Bruno SA_GET_RAID_MAP, 0, device->scsi3addr, NULL); 7851e66f787SSean Bruno 7861e66f787SSean Bruno if (ret) { 7871e66f787SSean Bruno DBG_ERR("error in build send raid req ret=%d\n", ret); 7881e66f787SSean Bruno goto err_out; 7891e66f787SSean Bruno } 7901e66f787SSean Bruno 7911e66f787SSean Bruno ret = pqisrc_raid_map_validation(softs, device, raid_map); 7921e66f787SSean Bruno if (ret) { 7931e66f787SSean Bruno DBG_ERR("error in raid map validation ret=%d\n", ret); 7941e66f787SSean Bruno goto err_out; 7951e66f787SSean Bruno } 7961e66f787SSean Bruno 7971e66f787SSean Bruno device->raid_map = raid_map; 7981e66f787SSean Bruno DBG_FUNC("OUT\n"); 7991e66f787SSean Bruno return 0; 8001e66f787SSean Bruno 8011e66f787SSean Bruno err_out: 8021e66f787SSean Bruno os_mem_free(softs, (char*)raid_map, sizeof(*raid_map)); 8031e66f787SSean Bruno DBG_FUNC("FAILED \n"); 8041e66f787SSean Bruno return ret; 8051e66f787SSean Bruno } 8061e66f787SSean Bruno 8071e66f787SSean Bruno /* Get device ioaccel_status to validate the type of device */ 8081e66f787SSean Bruno static void pqisrc_get_dev_ioaccel_status(pqisrc_softstate_t *softs, 8091e66f787SSean Bruno pqi_scsi_dev_t *device) 8101e66f787SSean Bruno { 8111e66f787SSean Bruno int ret = PQI_STATUS_SUCCESS; 8121e66f787SSean Bruno uint8_t *buff; 8131e66f787SSean Bruno uint8_t ioaccel_status; 8141e66f787SSean Bruno 8151e66f787SSean Bruno DBG_FUNC("IN\n"); 8161e66f787SSean Bruno 8171e66f787SSean Bruno buff = os_mem_alloc(softs, 64); 8181e66f787SSean Bruno if (!buff) 8191e66f787SSean Bruno return; 8201e66f787SSean Bruno 8211e66f787SSean Bruno ret = pqisrc_send_scsi_inquiry(softs, device->scsi3addr, 8221e66f787SSean Bruno VPD_PAGE | SA_VPD_LV_IOACCEL_STATUS, buff, 64); 8231e66f787SSean Bruno if (ret) { 8241e66f787SSean Bruno DBG_ERR("error in send scsi inquiry ret=%d\n", ret); 8251e66f787SSean Bruno goto err_out; 8261e66f787SSean Bruno } 8271e66f787SSean Bruno 8281e66f787SSean Bruno ioaccel_status = buff[IOACCEL_STATUS_BYTE]; 8291e66f787SSean Bruno device->offload_config = 8301e66f787SSean Bruno !!(ioaccel_status & OFFLOAD_CONFIGURED_BIT); 8311e66f787SSean Bruno 8321e66f787SSean Bruno if (device->offload_config) { 8331e66f787SSean Bruno device->offload_enabled_pending = 8341e66f787SSean Bruno !!(ioaccel_status & OFFLOAD_ENABLED_BIT); 8351e66f787SSean Bruno if (pqisrc_get_device_raidmap(softs, device)) 8361e66f787SSean Bruno device->offload_enabled_pending = false; 8371e66f787SSean Bruno } 8381e66f787SSean Bruno 839*b17f4335SSean Bruno DBG_DISC("offload_config: 0x%x offload_enabled_pending: 0x%x \n", 8401e66f787SSean Bruno device->offload_config, device->offload_enabled_pending); 8411e66f787SSean Bruno 8421e66f787SSean Bruno err_out: 8431e66f787SSean Bruno os_mem_free(softs, (char*)buff, 64); 8441e66f787SSean Bruno DBG_FUNC("OUT\n"); 8451e66f787SSean Bruno } 8461e66f787SSean Bruno 8471e66f787SSean Bruno /* Get RAID level of requested device */ 8481e66f787SSean Bruno static void pqisrc_get_dev_raid_level(pqisrc_softstate_t *softs, 8491e66f787SSean Bruno pqi_scsi_dev_t *device) 8501e66f787SSean Bruno { 8511e66f787SSean Bruno uint8_t raid_level; 8521e66f787SSean Bruno uint8_t *buff; 8531e66f787SSean Bruno 8541e66f787SSean Bruno DBG_FUNC("IN\n"); 8551e66f787SSean Bruno 8561e66f787SSean Bruno raid_level = SA_RAID_UNKNOWN; 8571e66f787SSean Bruno 8581e66f787SSean Bruno buff = os_mem_alloc(softs, 64); 8591e66f787SSean Bruno if (buff) { 8601e66f787SSean Bruno int ret; 8611e66f787SSean Bruno ret = pqisrc_send_scsi_inquiry(softs, device->scsi3addr, 8621e66f787SSean Bruno VPD_PAGE | SA_VPD_LV_DEVICE_GEOMETRY, buff, 64); 8631e66f787SSean Bruno if (ret == 0) { 8641e66f787SSean Bruno raid_level = buff[8]; 8651e66f787SSean Bruno if (raid_level > SA_RAID_MAX) 8661e66f787SSean Bruno raid_level = SA_RAID_UNKNOWN; 8671e66f787SSean Bruno } 8681e66f787SSean Bruno os_mem_free(softs, (char*)buff, 64); 8691e66f787SSean Bruno } 8701e66f787SSean Bruno 8711e66f787SSean Bruno device->raid_level = raid_level; 872*b17f4335SSean Bruno DBG_DISC("RAID LEVEL: %x \n", raid_level); 8731e66f787SSean Bruno DBG_FUNC("OUT\n"); 8741e66f787SSean Bruno } 8751e66f787SSean Bruno 8761e66f787SSean Bruno /* Parse the inquiry response and determine the type of device */ 8771e66f787SSean Bruno static int pqisrc_get_dev_data(pqisrc_softstate_t *softs, 8781e66f787SSean Bruno pqi_scsi_dev_t *device) 8791e66f787SSean Bruno { 8801e66f787SSean Bruno int ret = PQI_STATUS_SUCCESS; 8811e66f787SSean Bruno uint8_t *inq_buff; 8821e66f787SSean Bruno 8831e66f787SSean Bruno DBG_FUNC("IN\n"); 8841e66f787SSean Bruno 8851e66f787SSean Bruno inq_buff = os_mem_alloc(softs, OBDR_TAPE_INQ_SIZE); 8861e66f787SSean Bruno if (!inq_buff) 8871e66f787SSean Bruno return PQI_STATUS_FAILURE; 8881e66f787SSean Bruno 8891e66f787SSean Bruno /* Send an inquiry to the device to see what it is. */ 8901e66f787SSean Bruno ret = pqisrc_send_scsi_inquiry(softs, device->scsi3addr, 0, inq_buff, 8911e66f787SSean Bruno OBDR_TAPE_INQ_SIZE); 8921e66f787SSean Bruno if (ret) 8931e66f787SSean Bruno goto err_out; 8941e66f787SSean Bruno pqisrc_sanitize_inquiry_string(&inq_buff[8], 8); 8951e66f787SSean Bruno pqisrc_sanitize_inquiry_string(&inq_buff[16], 16); 8961e66f787SSean Bruno 8971e66f787SSean Bruno device->devtype = inq_buff[0] & 0x1f; 8981e66f787SSean Bruno memcpy(device->vendor, &inq_buff[8], 8991e66f787SSean Bruno sizeof(device->vendor)); 9001e66f787SSean Bruno memcpy(device->model, &inq_buff[16], 9011e66f787SSean Bruno sizeof(device->model)); 902*b17f4335SSean Bruno DBG_DISC("DEV_TYPE: %x VENDOR: %s MODEL: %s\n", device->devtype, device->vendor, device->model); 9031e66f787SSean Bruno 9041e66f787SSean Bruno if (pqisrc_is_logical_device(device) && device->devtype == DISK_DEVICE) { 9051e66f787SSean Bruno if (pqisrc_is_external_raid_device(device)) { 9061e66f787SSean Bruno device->raid_level = SA_RAID_UNKNOWN; 9071e66f787SSean Bruno device->volume_status = SA_LV_OK; 9081e66f787SSean Bruno device->volume_offline = false; 9091e66f787SSean Bruno } 9101e66f787SSean Bruno else { 9111e66f787SSean Bruno pqisrc_get_dev_raid_level(softs, device); 9121e66f787SSean Bruno pqisrc_get_dev_ioaccel_status(softs, device); 9131e66f787SSean Bruno device->volume_status = pqisrc_get_dev_vol_status(softs, 9141e66f787SSean Bruno device->scsi3addr); 9151e66f787SSean Bruno device->volume_offline = device->volume_status != SA_LV_OK; 9161e66f787SSean Bruno } 9171e66f787SSean Bruno } 9181e66f787SSean Bruno 9191e66f787SSean Bruno /* 9201e66f787SSean Bruno * Check if this is a One-Button-Disaster-Recovery device 9211e66f787SSean Bruno * by looking for "$DR-10" at offset 43 in the inquiry data. 9221e66f787SSean Bruno */ 9231e66f787SSean Bruno device->is_obdr_device = (device->devtype == ROM_DEVICE && 9241e66f787SSean Bruno memcmp(&inq_buff[OBDR_SIG_OFFSET], OBDR_TAPE_SIG, 9251e66f787SSean Bruno OBDR_SIG_LEN) == 0); 9261e66f787SSean Bruno err_out: 9271e66f787SSean Bruno os_mem_free(softs, (char*)inq_buff, OBDR_TAPE_INQ_SIZE); 9281e66f787SSean Bruno 9291e66f787SSean Bruno DBG_FUNC("OUT\n"); 9301e66f787SSean Bruno return ret; 9311e66f787SSean Bruno } 9321e66f787SSean Bruno 9331e66f787SSean Bruno /* 9341e66f787SSean Bruno * BMIC (Basic Management And Interface Commands) command 9351e66f787SSean Bruno * to get the controller identify params 9361e66f787SSean Bruno */ 9371e66f787SSean Bruno static int pqisrc_identify_ctrl(pqisrc_softstate_t *softs, 9381e66f787SSean Bruno bmic_ident_ctrl_t *buff) 9391e66f787SSean Bruno { 9401e66f787SSean Bruno int ret = PQI_STATUS_SUCCESS; 9411e66f787SSean Bruno pqisrc_raid_req_t request; 9421e66f787SSean Bruno 9431e66f787SSean Bruno DBG_FUNC("IN\n"); 9441e66f787SSean Bruno 9451e66f787SSean Bruno memset(&request, 0, sizeof(request)); 9461e66f787SSean Bruno ret = pqisrc_build_send_raid_request(softs, &request, buff, sizeof(*buff), 9471e66f787SSean Bruno BMIC_IDENTIFY_CONTROLLER, 0, (uint8_t *)RAID_CTLR_LUNID, NULL); 9481e66f787SSean Bruno DBG_FUNC("OUT\n"); 9491e66f787SSean Bruno 9501e66f787SSean Bruno return ret; 9511e66f787SSean Bruno } 9521e66f787SSean Bruno 9531e66f787SSean Bruno /* Get the adapter FW version using BMIC_IDENTIFY_CONTROLLER */ 9541e66f787SSean Bruno int pqisrc_get_ctrl_fw_version(pqisrc_softstate_t *softs) 9551e66f787SSean Bruno { 9561e66f787SSean Bruno int ret = PQI_STATUS_SUCCESS; 9571e66f787SSean Bruno bmic_ident_ctrl_t *identify_ctrl; 9581e66f787SSean Bruno 9591e66f787SSean Bruno DBG_FUNC("IN\n"); 9601e66f787SSean Bruno 9611e66f787SSean Bruno identify_ctrl = os_mem_alloc(softs, sizeof(*identify_ctrl)); 9621e66f787SSean Bruno if (!identify_ctrl) { 9631e66f787SSean Bruno DBG_ERR("failed to allocate memory for identify_ctrl\n"); 9641e66f787SSean Bruno return PQI_STATUS_FAILURE; 9651e66f787SSean Bruno } 9661e66f787SSean Bruno 9671e66f787SSean Bruno memset(identify_ctrl, 0, sizeof(*identify_ctrl)); 9681e66f787SSean Bruno 9691e66f787SSean Bruno ret = pqisrc_identify_ctrl(softs, identify_ctrl); 9701e66f787SSean Bruno if (ret) 9711e66f787SSean Bruno goto out; 9721e66f787SSean Bruno 9731e66f787SSean Bruno softs->fw_build_number = identify_ctrl->fw_build_number; 9741e66f787SSean Bruno memcpy(softs->fw_version, identify_ctrl->fw_version, 9751e66f787SSean Bruno sizeof(identify_ctrl->fw_version)); 9761e66f787SSean Bruno softs->fw_version[sizeof(identify_ctrl->fw_version)] = '\0'; 9771e66f787SSean Bruno snprintf(softs->fw_version + 9781e66f787SSean Bruno strlen(softs->fw_version), 9791e66f787SSean Bruno sizeof(softs->fw_version), 9801e66f787SSean Bruno "-%u", identify_ctrl->fw_build_number); 9811e66f787SSean Bruno out: 9821e66f787SSean Bruno os_mem_free(softs, (char *)identify_ctrl, sizeof(*identify_ctrl)); 983*b17f4335SSean Bruno DBG_INIT("Firmware version: %s Firmware build number: %d\n", softs->fw_version, softs->fw_build_number); 9841e66f787SSean Bruno DBG_FUNC("OUT\n"); 9851e66f787SSean Bruno return ret; 9861e66f787SSean Bruno } 9871e66f787SSean Bruno 9881e66f787SSean Bruno /* BMIC command to determine scsi device identify params */ 9891e66f787SSean Bruno static int pqisrc_identify_physical_disk(pqisrc_softstate_t *softs, 9901e66f787SSean Bruno pqi_scsi_dev_t *device, 9911e66f787SSean Bruno bmic_ident_physdev_t *buff, 9921e66f787SSean Bruno int buf_len) 9931e66f787SSean Bruno { 9941e66f787SSean Bruno int ret = PQI_STATUS_SUCCESS; 9951e66f787SSean Bruno uint16_t bmic_device_index; 9961e66f787SSean Bruno pqisrc_raid_req_t request; 9971e66f787SSean Bruno 9981e66f787SSean Bruno 9991e66f787SSean Bruno DBG_FUNC("IN\n"); 10001e66f787SSean Bruno 10011e66f787SSean Bruno memset(&request, 0, sizeof(request)); 10021e66f787SSean Bruno bmic_device_index = BMIC_GET_DRIVE_NUMBER(device->scsi3addr); 10031e66f787SSean Bruno request.cdb[2] = (uint8_t)bmic_device_index; 10041e66f787SSean Bruno request.cdb[9] = (uint8_t)(bmic_device_index >> 8); 10051e66f787SSean Bruno 10061e66f787SSean Bruno ret = pqisrc_build_send_raid_request(softs, &request, buff, buf_len, 10071e66f787SSean Bruno BMIC_IDENTIFY_PHYSICAL_DEVICE, 0, (uint8_t *)RAID_CTLR_LUNID, NULL); 10081e66f787SSean Bruno DBG_FUNC("OUT\n"); 10091e66f787SSean Bruno return ret; 10101e66f787SSean Bruno } 10111e66f787SSean Bruno 10121e66f787SSean Bruno /* 10131e66f787SSean Bruno * Function used to get the scsi device information using one of BMIC 10141e66f787SSean Bruno * BMIC_IDENTIFY_PHYSICAL_DEVICE 10151e66f787SSean Bruno */ 10161e66f787SSean Bruno static void pqisrc_get_physical_device_info(pqisrc_softstate_t *softs, 10171e66f787SSean Bruno pqi_scsi_dev_t *device, 10181e66f787SSean Bruno bmic_ident_physdev_t *id_phys) 10191e66f787SSean Bruno { 10201e66f787SSean Bruno int ret = PQI_STATUS_SUCCESS; 10211e66f787SSean Bruno 10221e66f787SSean Bruno DBG_FUNC("IN\n"); 10231e66f787SSean Bruno memset(id_phys, 0, sizeof(*id_phys)); 10241e66f787SSean Bruno 10251e66f787SSean Bruno ret= pqisrc_identify_physical_disk(softs, device, 10261e66f787SSean Bruno id_phys, sizeof(*id_phys)); 10271e66f787SSean Bruno if (ret) { 10281e66f787SSean Bruno device->queue_depth = PQI_PHYSICAL_DISK_DEFAULT_MAX_QUEUE_DEPTH; 10291e66f787SSean Bruno return; 10301e66f787SSean Bruno } 10311e66f787SSean Bruno 10321e66f787SSean Bruno device->queue_depth = 10331e66f787SSean Bruno LE_16(id_phys->current_queue_depth_limit); 10341e66f787SSean Bruno device->device_type = id_phys->device_type; 10351e66f787SSean Bruno device->active_path_index = id_phys->active_path_number; 10361e66f787SSean Bruno device->path_map = id_phys->redundant_path_present_map; 10371e66f787SSean Bruno memcpy(&device->box, 10381e66f787SSean Bruno &id_phys->alternate_paths_phys_box_on_port, 10391e66f787SSean Bruno sizeof(device->box)); 10401e66f787SSean Bruno memcpy(&device->phys_connector, 10411e66f787SSean Bruno &id_phys->alternate_paths_phys_connector, 10421e66f787SSean Bruno sizeof(device->phys_connector)); 10431e66f787SSean Bruno device->bay = id_phys->phys_bay_in_box; 10441e66f787SSean Bruno 1045*b17f4335SSean Bruno DBG_DISC("BMIC DEV_TYPE: %x QUEUE DEPTH: 0x%x \n", device->device_type, device->queue_depth); 10461e66f787SSean Bruno DBG_FUNC("OUT\n"); 10471e66f787SSean Bruno } 10481e66f787SSean Bruno 10491e66f787SSean Bruno 10501e66f787SSean Bruno /* Function used to find the entry of the device in a list */ 10511e66f787SSean Bruno static device_status_t pqisrc_scsi_find_entry(pqisrc_softstate_t *softs, 10521e66f787SSean Bruno pqi_scsi_dev_t *device_to_find, 10531e66f787SSean Bruno pqi_scsi_dev_t **same_device) 10541e66f787SSean Bruno { 10551e66f787SSean Bruno pqi_scsi_dev_t *device; 10561e66f787SSean Bruno int i,j; 10571e66f787SSean Bruno DBG_FUNC("IN\n"); 10581e66f787SSean Bruno for(i = 0; i < PQI_MAX_DEVICES; i++) { 10591e66f787SSean Bruno for(j = 0; j < PQI_MAX_MULTILUN; j++) { 10601e66f787SSean Bruno if(softs->device_list[i][j] == NULL) 10611e66f787SSean Bruno continue; 10621e66f787SSean Bruno device = softs->device_list[i][j]; 10631e66f787SSean Bruno if (pqisrc_scsi3addr_equal(device_to_find->scsi3addr, 10641e66f787SSean Bruno device->scsi3addr)) { 10651e66f787SSean Bruno *same_device = device; 10661e66f787SSean Bruno if (pqisrc_device_equal(device_to_find, device)) { 10671e66f787SSean Bruno if (device_to_find->volume_offline) 10681e66f787SSean Bruno return DEVICE_CHANGED; 10691e66f787SSean Bruno return DEVICE_UNCHANGED; 10701e66f787SSean Bruno } 10711e66f787SSean Bruno return DEVICE_CHANGED; 10721e66f787SSean Bruno } 10731e66f787SSean Bruno } 10741e66f787SSean Bruno } 10751e66f787SSean Bruno DBG_FUNC("OUT\n"); 10761e66f787SSean Bruno 10771e66f787SSean Bruno return DEVICE_NOT_FOUND; 10781e66f787SSean Bruno } 10791e66f787SSean Bruno 10801e66f787SSean Bruno 10811e66f787SSean Bruno /* Update the newly added devices as existed device */ 10821e66f787SSean Bruno static void pqisrc_exist_device_update(pqisrc_softstate_t *softs, 10831e66f787SSean Bruno pqi_scsi_dev_t *device_exist, 10841e66f787SSean Bruno pqi_scsi_dev_t *new_device) 10851e66f787SSean Bruno { 10861e66f787SSean Bruno DBG_FUNC("IN\n"); 10871e66f787SSean Bruno device_exist->expose_device = new_device->expose_device; 10881e66f787SSean Bruno memcpy(device_exist->vendor, new_device->vendor, 10891e66f787SSean Bruno sizeof(device_exist->vendor)); 10901e66f787SSean Bruno memcpy(device_exist->model, new_device->model, 10911e66f787SSean Bruno sizeof(device_exist->model)); 10921e66f787SSean Bruno device_exist->is_physical_device = new_device->is_physical_device; 10931e66f787SSean Bruno device_exist->is_external_raid_device = 10941e66f787SSean Bruno new_device->is_external_raid_device; 10951e66f787SSean Bruno device_exist->sas_address = new_device->sas_address; 10961e66f787SSean Bruno device_exist->raid_level = new_device->raid_level; 10971e66f787SSean Bruno device_exist->queue_depth = new_device->queue_depth; 10981e66f787SSean Bruno device_exist->ioaccel_handle = new_device->ioaccel_handle; 10991e66f787SSean Bruno device_exist->volume_status = new_device->volume_status; 11001e66f787SSean Bruno device_exist->active_path_index = new_device->active_path_index; 11011e66f787SSean Bruno device_exist->path_map = new_device->path_map; 11021e66f787SSean Bruno device_exist->bay = new_device->bay; 11031e66f787SSean Bruno memcpy(device_exist->box, new_device->box, 11041e66f787SSean Bruno sizeof(device_exist->box)); 11051e66f787SSean Bruno memcpy(device_exist->phys_connector, new_device->phys_connector, 11061e66f787SSean Bruno sizeof(device_exist->phys_connector)); 11071e66f787SSean Bruno device_exist->offload_config = new_device->offload_config; 11081e66f787SSean Bruno device_exist->offload_enabled = false; 11091e66f787SSean Bruno device_exist->offload_enabled_pending = 11101e66f787SSean Bruno new_device->offload_enabled_pending; 11111e66f787SSean Bruno device_exist->offload_to_mirror = 0; 11121e66f787SSean Bruno if (device_exist->raid_map) 11131e66f787SSean Bruno os_mem_free(softs, 11141e66f787SSean Bruno (char *)device_exist->raid_map, 11151e66f787SSean Bruno sizeof(*device_exist->raid_map)); 11161e66f787SSean Bruno device_exist->raid_map = new_device->raid_map; 11171e66f787SSean Bruno /* To prevent this from being freed later. */ 11181e66f787SSean Bruno new_device->raid_map = NULL; 11191e66f787SSean Bruno DBG_FUNC("OUT\n"); 11201e66f787SSean Bruno } 11211e66f787SSean Bruno 11221e66f787SSean Bruno /* Validate the ioaccel_handle for a newly added device */ 11231e66f787SSean Bruno static pqi_scsi_dev_t *pqisrc_identify_device_via_ioaccel( 11241e66f787SSean Bruno pqisrc_softstate_t *softs, uint32_t ioaccel_handle) 11251e66f787SSean Bruno { 11261e66f787SSean Bruno pqi_scsi_dev_t *device; 11271e66f787SSean Bruno int i,j; 11281e66f787SSean Bruno DBG_FUNC("IN\n"); 11291e66f787SSean Bruno for(i = 0; i < PQI_MAX_DEVICES; i++) { 11301e66f787SSean Bruno for(j = 0; j < PQI_MAX_MULTILUN; j++) { 11311e66f787SSean Bruno if(softs->device_list[i][j] == NULL) 11321e66f787SSean Bruno continue; 11331e66f787SSean Bruno device = softs->device_list[i][j]; 11341e66f787SSean Bruno if (device->devtype != DISK_DEVICE) 11351e66f787SSean Bruno continue; 11361e66f787SSean Bruno if (pqisrc_is_logical_device(device)) 11371e66f787SSean Bruno continue; 11381e66f787SSean Bruno if (device->ioaccel_handle == ioaccel_handle) 11391e66f787SSean Bruno return device; 11401e66f787SSean Bruno } 11411e66f787SSean Bruno } 11421e66f787SSean Bruno DBG_FUNC("OUT\n"); 11431e66f787SSean Bruno 11441e66f787SSean Bruno return NULL; 11451e66f787SSean Bruno } 11461e66f787SSean Bruno 11471e66f787SSean Bruno /* Get the scsi device queue depth */ 11481e66f787SSean Bruno static void pqisrc_update_log_dev_qdepth(pqisrc_softstate_t *softs) 11491e66f787SSean Bruno { 11501e66f787SSean Bruno unsigned i; 11511e66f787SSean Bruno unsigned phys_dev_num; 11521e66f787SSean Bruno unsigned num_raidmap_entries; 11531e66f787SSean Bruno unsigned queue_depth; 11541e66f787SSean Bruno pqisrc_raid_map_t *raid_map; 11551e66f787SSean Bruno pqi_scsi_dev_t *device; 11561e66f787SSean Bruno raidmap_data_t *dev_data; 11571e66f787SSean Bruno pqi_scsi_dev_t *phys_disk; 11581e66f787SSean Bruno unsigned j; 1159*b17f4335SSean Bruno unsigned k; 11601e66f787SSean Bruno 11611e66f787SSean Bruno DBG_FUNC("IN\n"); 11621e66f787SSean Bruno 11631e66f787SSean Bruno for(i = 0; i < PQI_MAX_DEVICES; i++) { 11641e66f787SSean Bruno for(j = 0; j < PQI_MAX_MULTILUN; j++) { 11651e66f787SSean Bruno if(softs->device_list[i][j] == NULL) 11661e66f787SSean Bruno continue; 11671e66f787SSean Bruno device = softs->device_list[i][j]; 11681e66f787SSean Bruno if (device->devtype != DISK_DEVICE) 11691e66f787SSean Bruno continue; 11701e66f787SSean Bruno if (!pqisrc_is_logical_device(device)) 11711e66f787SSean Bruno continue; 11721e66f787SSean Bruno if (pqisrc_is_external_raid_device(device)) 11731e66f787SSean Bruno continue; 11741e66f787SSean Bruno device->queue_depth = PQI_LOGICAL_DISK_DEFAULT_MAX_QUEUE_DEPTH; 11751e66f787SSean Bruno raid_map = device->raid_map; 11761e66f787SSean Bruno if (!raid_map) 11771e66f787SSean Bruno return; 11781e66f787SSean Bruno dev_data = raid_map->dev_data; 11791e66f787SSean Bruno phys_dev_num = LE_16(raid_map->layout_map_count) * 11801e66f787SSean Bruno (LE_16(raid_map->data_disks_per_row) + 11811e66f787SSean Bruno LE_16(raid_map->metadata_disks_per_row)); 11821e66f787SSean Bruno num_raidmap_entries = phys_dev_num * 11831e66f787SSean Bruno LE_16(raid_map->row_cnt); 11841e66f787SSean Bruno 11851e66f787SSean Bruno queue_depth = 0; 1186*b17f4335SSean Bruno for (k = 0; k < num_raidmap_entries; k++) { 11871e66f787SSean Bruno phys_disk = pqisrc_identify_device_via_ioaccel(softs, 1188*b17f4335SSean Bruno dev_data[k].ioaccel_handle); 11891e66f787SSean Bruno 11901e66f787SSean Bruno if (!phys_disk) { 11911e66f787SSean Bruno DBG_WARN( 11921e66f787SSean Bruno "Failed to find physical disk handle for logical drive %016llx\n", 11931e66f787SSean Bruno (unsigned long long)BE_64(device->scsi3addr[0])); 11941e66f787SSean Bruno device->offload_enabled = false; 11951e66f787SSean Bruno device->offload_enabled_pending = false; 11961e66f787SSean Bruno if (raid_map) 11971e66f787SSean Bruno os_mem_free(softs, (char *)raid_map, sizeof(*raid_map)); 11981e66f787SSean Bruno device->raid_map = NULL; 11991e66f787SSean Bruno return; 12001e66f787SSean Bruno } 12011e66f787SSean Bruno 12021e66f787SSean Bruno queue_depth += phys_disk->queue_depth; 12031e66f787SSean Bruno } 12041e66f787SSean Bruno 12051e66f787SSean Bruno device->queue_depth = queue_depth; 12061e66f787SSean Bruno } /* end inner loop */ 12071e66f787SSean Bruno }/* end outer loop */ 12081e66f787SSean Bruno DBG_FUNC("OUT\n"); 12091e66f787SSean Bruno } 12101e66f787SSean Bruno 12111e66f787SSean Bruno /* Function used to add a scsi device to OS scsi subsystem */ 12121e66f787SSean Bruno static int pqisrc_add_device(pqisrc_softstate_t *softs, 12131e66f787SSean Bruno pqi_scsi_dev_t *device) 12141e66f787SSean Bruno { 12151e66f787SSean Bruno DBG_FUNC("IN\n"); 1216*b17f4335SSean Bruno DBG_WARN("vendor: %s model: %s bus:%d target:%d lun:%d is_physical_device:0x%x expose_device:0x%x volume_offline 0x%x volume_status 0x%x \n", 12171e66f787SSean Bruno device->vendor, device->model, device->bus, device->target, device->lun, device->is_physical_device, device->expose_device, device->volume_offline, device->volume_status); 12181e66f787SSean Bruno 12191e66f787SSean Bruno device->invalid = false; 12201e66f787SSean Bruno 12211e66f787SSean Bruno if(device->expose_device) { 12221e66f787SSean Bruno /* TBD: Call OS upper layer function to add the device entry */ 12231e66f787SSean Bruno os_add_device(softs,device); 12241e66f787SSean Bruno } 12251e66f787SSean Bruno DBG_FUNC("OUT\n"); 12261e66f787SSean Bruno return PQI_STATUS_SUCCESS; 12271e66f787SSean Bruno 12281e66f787SSean Bruno } 12291e66f787SSean Bruno 12301e66f787SSean Bruno /* Function used to remove a scsi device from OS scsi subsystem */ 12311e66f787SSean Bruno void pqisrc_remove_device(pqisrc_softstate_t *softs, 12321e66f787SSean Bruno pqi_scsi_dev_t *device) 12331e66f787SSean Bruno { 12341e66f787SSean Bruno DBG_FUNC("IN\n"); 1235*b17f4335SSean Bruno DBG_DISC("vendor: %s model: %s bus:%d target:%d lun:%d is_physical_device:0x%x expose_device:0x%x volume_offline 0x%x volume_status 0x%x \n", 12361e66f787SSean Bruno device->vendor, device->model, device->bus, device->target, device->lun, device->is_physical_device, device->expose_device, device->volume_offline, device->volume_status); 12371e66f787SSean Bruno 12381e66f787SSean Bruno /* TBD: Call OS upper layer function to remove the device entry */ 12391e66f787SSean Bruno device->invalid = true; 12401e66f787SSean Bruno os_remove_device(softs,device); 12411e66f787SSean Bruno DBG_FUNC("OUT\n"); 12421e66f787SSean Bruno } 12431e66f787SSean Bruno 12441e66f787SSean Bruno 12451e66f787SSean Bruno /* 12461e66f787SSean Bruno * When exposing new device to OS fails then adjst list according to the 12471e66f787SSean Bruno * mid scsi list 12481e66f787SSean Bruno */ 12491e66f787SSean Bruno static void pqisrc_adjust_list(pqisrc_softstate_t *softs, 12501e66f787SSean Bruno pqi_scsi_dev_t *device) 12511e66f787SSean Bruno { 12521e66f787SSean Bruno DBG_FUNC("IN\n"); 12531e66f787SSean Bruno 12541e66f787SSean Bruno if (!device) { 12551e66f787SSean Bruno DBG_ERR("softs = %p: device is NULL !!!\n", softs); 12561e66f787SSean Bruno return; 12571e66f787SSean Bruno } 12581e66f787SSean Bruno 12591e66f787SSean Bruno OS_ACQUIRE_SPINLOCK(&softs->devlist_lock); 12601e66f787SSean Bruno softs->device_list[device->target][device->lun] = NULL; 12611e66f787SSean Bruno OS_RELEASE_SPINLOCK(&softs->devlist_lock); 12621e66f787SSean Bruno pqisrc_device_mem_free(softs, device); 12631e66f787SSean Bruno 12641e66f787SSean Bruno DBG_FUNC("OUT\n"); 12651e66f787SSean Bruno } 12661e66f787SSean Bruno 12671e66f787SSean Bruno /* Debug routine used to display the RAID volume status of the device */ 12681e66f787SSean Bruno static void pqisrc_display_volume_status(pqisrc_softstate_t *softs, 12691e66f787SSean Bruno pqi_scsi_dev_t *device) 12701e66f787SSean Bruno { 12711e66f787SSean Bruno char *status; 12721e66f787SSean Bruno 12731e66f787SSean Bruno DBG_FUNC("IN\n"); 12741e66f787SSean Bruno switch (device->volume_status) { 12751e66f787SSean Bruno case SA_LV_OK: 12761e66f787SSean Bruno status = "Volume is online."; 12771e66f787SSean Bruno break; 12781e66f787SSean Bruno case SA_LV_UNDERGOING_ERASE: 12791e66f787SSean Bruno status = "Volume is undergoing background erase process."; 12801e66f787SSean Bruno break; 12811e66f787SSean Bruno case SA_LV_NOT_AVAILABLE: 12821e66f787SSean Bruno status = "Volume is waiting for transforming volume."; 12831e66f787SSean Bruno break; 12841e66f787SSean Bruno case SA_LV_UNDERGOING_RPI: 12851e66f787SSean Bruno status = "Volume is undergoing rapid parity initialization process."; 12861e66f787SSean Bruno break; 12871e66f787SSean Bruno case SA_LV_PENDING_RPI: 12881e66f787SSean Bruno status = "Volume is queued for rapid parity initialization process."; 12891e66f787SSean Bruno break; 12901e66f787SSean Bruno case SA_LV_ENCRYPTED_NO_KEY: 12911e66f787SSean Bruno status = "Volume is encrypted and cannot be accessed because key is not present."; 12921e66f787SSean Bruno break; 12931e66f787SSean Bruno case SA_LV_PLAINTEXT_IN_ENCRYPT_ONLY_CONTROLLER: 12941e66f787SSean Bruno status = "Volume is not encrypted and cannot be accessed because controller is in encryption-only mode."; 12951e66f787SSean Bruno break; 12961e66f787SSean Bruno case SA_LV_UNDERGOING_ENCRYPTION: 12971e66f787SSean Bruno status = "Volume is undergoing encryption process."; 12981e66f787SSean Bruno break; 12991e66f787SSean Bruno case SA_LV_UNDERGOING_ENCRYPTION_REKEYING: 13001e66f787SSean Bruno status = "Volume is undergoing encryption re-keying process."; 13011e66f787SSean Bruno break; 13021e66f787SSean Bruno case SA_LV_ENCRYPTED_IN_NON_ENCRYPTED_CONTROLLER: 13031e66f787SSean Bruno status = "Volume is encrypted and cannot be accessed because controller does not have encryption enabled."; 13041e66f787SSean Bruno break; 13051e66f787SSean Bruno case SA_LV_PENDING_ENCRYPTION: 13061e66f787SSean Bruno status = "Volume is pending migration to encrypted state, but process has not started."; 13071e66f787SSean Bruno break; 13081e66f787SSean Bruno case SA_LV_PENDING_ENCRYPTION_REKEYING: 13091e66f787SSean Bruno status = "Volume is encrypted and is pending encryption rekeying."; 13101e66f787SSean Bruno break; 13111e66f787SSean Bruno case SA_LV_STATUS_VPD_UNSUPPORTED: 13121e66f787SSean Bruno status = "Volume status is not available through vital product data pages."; 13131e66f787SSean Bruno break; 13141e66f787SSean Bruno default: 13151e66f787SSean Bruno status = "Volume is in an unknown state."; 13161e66f787SSean Bruno break; 13171e66f787SSean Bruno } 13181e66f787SSean Bruno 1319*b17f4335SSean Bruno DBG_DISC("scsi BTL %d:%d:%d %s\n", 13201e66f787SSean Bruno device->bus, device->target, device->lun, status); 13211e66f787SSean Bruno DBG_FUNC("OUT\n"); 13221e66f787SSean Bruno } 13231e66f787SSean Bruno 13241e66f787SSean Bruno void pqisrc_device_mem_free(pqisrc_softstate_t *softs, pqi_scsi_dev_t *device) 13251e66f787SSean Bruno { 1326*b17f4335SSean Bruno DBG_FUNC("IN\n"); 13271e66f787SSean Bruno if (!device) 13281e66f787SSean Bruno return; 13291e66f787SSean Bruno if (device->raid_map) { 13301e66f787SSean Bruno os_mem_free(softs, (char *)device->raid_map, sizeof(pqisrc_raid_map_t)); 13311e66f787SSean Bruno } 13321e66f787SSean Bruno os_mem_free(softs, (char *)device,sizeof(*device)); 1333*b17f4335SSean Bruno DBG_FUNC("OUT\n"); 13341e66f787SSean Bruno 13351e66f787SSean Bruno } 13361e66f787SSean Bruno 13371e66f787SSean Bruno /* OS should call this function to free the scsi device */ 13381e66f787SSean Bruno void pqisrc_free_device(pqisrc_softstate_t * softs,pqi_scsi_dev_t *device) 13391e66f787SSean Bruno { 13401e66f787SSean Bruno 13411e66f787SSean Bruno OS_ACQUIRE_SPINLOCK(&softs->devlist_lock); 1342*b17f4335SSean Bruno if (!pqisrc_is_logical_device(device)) { 1343*b17f4335SSean Bruno pqisrc_free_tid(softs,device->target); 1344*b17f4335SSean Bruno } 13451e66f787SSean Bruno pqisrc_device_mem_free(softs, device); 13461e66f787SSean Bruno OS_RELEASE_SPINLOCK(&softs->devlist_lock); 13471e66f787SSean Bruno 13481e66f787SSean Bruno } 13491e66f787SSean Bruno 13501e66f787SSean Bruno 13511e66f787SSean Bruno /* Update the newly added devices to the device list */ 13521e66f787SSean Bruno static void pqisrc_update_device_list(pqisrc_softstate_t *softs, 13531e66f787SSean Bruno pqi_scsi_dev_t *new_device_list[], int num_new_devices) 13541e66f787SSean Bruno { 13551e66f787SSean Bruno int ret; 13561e66f787SSean Bruno int i; 13571e66f787SSean Bruno device_status_t dev_status; 13581e66f787SSean Bruno pqi_scsi_dev_t *device; 13591e66f787SSean Bruno pqi_scsi_dev_t *same_device; 13601e66f787SSean Bruno pqi_scsi_dev_t **added = NULL; 13611e66f787SSean Bruno pqi_scsi_dev_t **removed = NULL; 13621e66f787SSean Bruno int nadded = 0, nremoved = 0; 13631e66f787SSean Bruno int j; 1364*b17f4335SSean Bruno int tid = 0; 1365*b17f4335SSean Bruno 1366*b17f4335SSean Bruno DBG_FUNC("IN\n"); 13671e66f787SSean Bruno 13681e66f787SSean Bruno added = os_mem_alloc(softs, sizeof(*added) * PQI_MAX_DEVICES); 13691e66f787SSean Bruno removed = os_mem_alloc(softs, sizeof(*removed) * PQI_MAX_DEVICES); 13701e66f787SSean Bruno 13711e66f787SSean Bruno if (!added || !removed) { 13721e66f787SSean Bruno DBG_WARN("Out of memory \n"); 13731e66f787SSean Bruno goto free_and_out; 13741e66f787SSean Bruno } 13751e66f787SSean Bruno 13761e66f787SSean Bruno OS_ACQUIRE_SPINLOCK(&softs->devlist_lock); 13771e66f787SSean Bruno 13781e66f787SSean Bruno for(i = 0; i < PQI_MAX_DEVICES; i++) { 13791e66f787SSean Bruno for(j = 0; j < PQI_MAX_MULTILUN; j++) { 13801e66f787SSean Bruno if(softs->device_list[i][j] == NULL) 13811e66f787SSean Bruno continue; 13821e66f787SSean Bruno device = softs->device_list[i][j]; 13831e66f787SSean Bruno device->device_gone = true; 13841e66f787SSean Bruno } 13851e66f787SSean Bruno } 13861e66f787SSean Bruno DBG_IO("Device list used an array\n"); 13871e66f787SSean Bruno for (i = 0; i < num_new_devices; i++) { 13881e66f787SSean Bruno device = new_device_list[i]; 13891e66f787SSean Bruno 13901e66f787SSean Bruno dev_status = pqisrc_scsi_find_entry(softs, device, 13911e66f787SSean Bruno &same_device); 13921e66f787SSean Bruno 13931e66f787SSean Bruno switch (dev_status) { 13941e66f787SSean Bruno case DEVICE_UNCHANGED: 13951e66f787SSean Bruno /* New Device present in existing device list */ 13961e66f787SSean Bruno device->new_device = false; 13971e66f787SSean Bruno same_device->device_gone = false; 13981e66f787SSean Bruno pqisrc_exist_device_update(softs, same_device, device); 13991e66f787SSean Bruno break; 14001e66f787SSean Bruno case DEVICE_NOT_FOUND: 14011e66f787SSean Bruno /* Device not found in existing list */ 14021e66f787SSean Bruno device->new_device = true; 14031e66f787SSean Bruno break; 14041e66f787SSean Bruno case DEVICE_CHANGED: 14051e66f787SSean Bruno /* Actual device gone need to add device to list*/ 14061e66f787SSean Bruno device->new_device = true; 14071e66f787SSean Bruno break; 14081e66f787SSean Bruno default: 14091e66f787SSean Bruno break; 14101e66f787SSean Bruno } 14111e66f787SSean Bruno } 14121e66f787SSean Bruno /* Process all devices that have gone away. */ 14131e66f787SSean Bruno for(i = 0, nremoved = 0; i < PQI_MAX_DEVICES; i++) { 14141e66f787SSean Bruno for(j = 0; j < PQI_MAX_MULTILUN; j++) { 14151e66f787SSean Bruno if(softs->device_list[i][j] == NULL) 14161e66f787SSean Bruno continue; 14171e66f787SSean Bruno device = softs->device_list[i][j]; 14181e66f787SSean Bruno if (device->device_gone) { 14191e66f787SSean Bruno softs->device_list[device->target][device->lun] = NULL; 14201e66f787SSean Bruno removed[nremoved] = device; 14211e66f787SSean Bruno nremoved++; 14221e66f787SSean Bruno } 14231e66f787SSean Bruno } 14241e66f787SSean Bruno } 14251e66f787SSean Bruno 14261e66f787SSean Bruno /* Process all new devices. */ 14271e66f787SSean Bruno for (i = 0, nadded = 0; i < num_new_devices; i++) { 14281e66f787SSean Bruno device = new_device_list[i]; 14291e66f787SSean Bruno if (!device->new_device) 14301e66f787SSean Bruno continue; 14311e66f787SSean Bruno if (device->volume_offline) 14321e66f787SSean Bruno continue; 14331e66f787SSean Bruno 1434*b17f4335SSean Bruno /* physical device */ 1435*b17f4335SSean Bruno if (!pqisrc_is_logical_device(device)) { 1436*b17f4335SSean Bruno tid = pqisrc_alloc_tid(softs); 1437*b17f4335SSean Bruno if(INVALID_ELEM != tid) 1438*b17f4335SSean Bruno pqisrc_set_btl(device, PQI_PHYSICAL_DEVICE_BUS, tid, 0); 1439*b17f4335SSean Bruno } 1440*b17f4335SSean Bruno 14411e66f787SSean Bruno softs->device_list[device->target][device->lun] = device; 1442*b17f4335SSean Bruno DBG_DISC("Added device %p at B : %d T : %d L : %d\n",device, 14431e66f787SSean Bruno device->bus,device->target,device->lun); 14441e66f787SSean Bruno /* To prevent this entry from being freed later. */ 14451e66f787SSean Bruno new_device_list[i] = NULL; 14461e66f787SSean Bruno added[nadded] = device; 14471e66f787SSean Bruno nadded++; 14481e66f787SSean Bruno } 14491e66f787SSean Bruno 14501e66f787SSean Bruno pqisrc_update_log_dev_qdepth(softs); 14511e66f787SSean Bruno 14521e66f787SSean Bruno for(i = 0; i < PQI_MAX_DEVICES; i++) { 14531e66f787SSean Bruno for(j = 0; j < PQI_MAX_MULTILUN; j++) { 14541e66f787SSean Bruno if(softs->device_list[i][j] == NULL) 14551e66f787SSean Bruno continue; 14561e66f787SSean Bruno device = softs->device_list[i][j]; 14571e66f787SSean Bruno device->offload_enabled = device->offload_enabled_pending; 14581e66f787SSean Bruno } 14591e66f787SSean Bruno } 14601e66f787SSean Bruno 14611e66f787SSean Bruno OS_RELEASE_SPINLOCK(&softs->devlist_lock); 14621e66f787SSean Bruno 14631e66f787SSean Bruno for(i = 0; i < nremoved; i++) { 14641e66f787SSean Bruno device = removed[i]; 14651e66f787SSean Bruno if (device == NULL) 14661e66f787SSean Bruno continue; 14671e66f787SSean Bruno pqisrc_remove_device(softs, device); 14681e66f787SSean Bruno pqisrc_display_device_info(softs, "removed", device); 14691e66f787SSean Bruno 14701e66f787SSean Bruno } 14711e66f787SSean Bruno 14721e66f787SSean Bruno for(i = 0; i < PQI_MAX_DEVICES; i++) { 14731e66f787SSean Bruno for(j = 0; j < PQI_MAX_MULTILUN; j++) { 14741e66f787SSean Bruno if(softs->device_list[i][j] == NULL) 14751e66f787SSean Bruno continue; 14761e66f787SSean Bruno device = softs->device_list[i][j]; 14771e66f787SSean Bruno /* 14781e66f787SSean Bruno * Notify the OS upper layer if the queue depth of any existing device has 14791e66f787SSean Bruno * changed. 14801e66f787SSean Bruno */ 14811e66f787SSean Bruno if (device->queue_depth != 14821e66f787SSean Bruno device->advertised_queue_depth) { 14831e66f787SSean Bruno device->advertised_queue_depth = device->queue_depth; 14841e66f787SSean Bruno /* TBD: Call OS upper layer function to change device Q depth */ 14851e66f787SSean Bruno } 14861e66f787SSean Bruno } 14871e66f787SSean Bruno } 14881e66f787SSean Bruno for(i = 0; i < nadded; i++) { 14891e66f787SSean Bruno device = added[i]; 14901e66f787SSean Bruno if (device->expose_device) { 14911e66f787SSean Bruno ret = pqisrc_add_device(softs, device); 14921e66f787SSean Bruno if (ret) { 14931e66f787SSean Bruno DBG_WARN("scsi %d:%d:%d addition failed, device not added\n", 14941e66f787SSean Bruno device->bus, device->target, 14951e66f787SSean Bruno device->lun); 14961e66f787SSean Bruno pqisrc_adjust_list(softs, device); 14971e66f787SSean Bruno continue; 14981e66f787SSean Bruno } 14991e66f787SSean Bruno } 15001e66f787SSean Bruno 15011e66f787SSean Bruno pqisrc_display_device_info(softs, "added", device); 15021e66f787SSean Bruno } 15031e66f787SSean Bruno 15041e66f787SSean Bruno /* Process all volumes that are offline. */ 15051e66f787SSean Bruno for (i = 0; i < num_new_devices; i++) { 15061e66f787SSean Bruno device = new_device_list[i]; 15071e66f787SSean Bruno if (!device) 15081e66f787SSean Bruno continue; 15091e66f787SSean Bruno if (!device->new_device) 15101e66f787SSean Bruno continue; 15111e66f787SSean Bruno if (device->volume_offline) { 15121e66f787SSean Bruno pqisrc_display_volume_status(softs, device); 15131e66f787SSean Bruno pqisrc_display_device_info(softs, "offline", device); 15141e66f787SSean Bruno } 15151e66f787SSean Bruno } 15161e66f787SSean Bruno 15171e66f787SSean Bruno free_and_out: 15181e66f787SSean Bruno if (added) 15191e66f787SSean Bruno os_mem_free(softs, (char *)added, 15201e66f787SSean Bruno sizeof(*added) * PQI_MAX_DEVICES); 15211e66f787SSean Bruno if (removed) 15221e66f787SSean Bruno os_mem_free(softs, (char *)removed, 15231e66f787SSean Bruno sizeof(*removed) * PQI_MAX_DEVICES); 15241e66f787SSean Bruno 1525*b17f4335SSean Bruno DBG_FUNC("OUT\n"); 15261e66f787SSean Bruno } 15271e66f787SSean Bruno 15281e66f787SSean Bruno /* 15291e66f787SSean Bruno * Let the Adapter know about driver version using one of BMIC 15301e66f787SSean Bruno * BMIC_WRITE_HOST_WELLNESS 15311e66f787SSean Bruno */ 15321e66f787SSean Bruno int pqisrc_write_driver_version_to_host_wellness(pqisrc_softstate_t *softs) 15331e66f787SSean Bruno { 15341e66f787SSean Bruno int rval = PQI_STATUS_SUCCESS; 15351e66f787SSean Bruno struct bmic_host_wellness_driver_version *host_wellness_driver_ver; 15361e66f787SSean Bruno size_t data_length; 15371e66f787SSean Bruno pqisrc_raid_req_t request; 15381e66f787SSean Bruno 15391e66f787SSean Bruno DBG_FUNC("IN\n"); 15401e66f787SSean Bruno 15411e66f787SSean Bruno memset(&request, 0, sizeof(request)); 15421e66f787SSean Bruno data_length = sizeof(*host_wellness_driver_ver); 15431e66f787SSean Bruno 15441e66f787SSean Bruno host_wellness_driver_ver = os_mem_alloc(softs, data_length); 15451e66f787SSean Bruno if (!host_wellness_driver_ver) { 15461e66f787SSean Bruno DBG_ERR("failed to allocate memory for host wellness driver_version\n"); 15471e66f787SSean Bruno return PQI_STATUS_FAILURE; 15481e66f787SSean Bruno } 15491e66f787SSean Bruno 15501e66f787SSean Bruno host_wellness_driver_ver->start_tag[0] = '<'; 15511e66f787SSean Bruno host_wellness_driver_ver->start_tag[1] = 'H'; 15521e66f787SSean Bruno host_wellness_driver_ver->start_tag[2] = 'W'; 15531e66f787SSean Bruno host_wellness_driver_ver->start_tag[3] = '>'; 15541e66f787SSean Bruno host_wellness_driver_ver->driver_version_tag[0] = 'D'; 15551e66f787SSean Bruno host_wellness_driver_ver->driver_version_tag[1] = 'V'; 15561e66f787SSean Bruno host_wellness_driver_ver->driver_version_length = LE_16(sizeof(host_wellness_driver_ver->driver_version)); 15571e66f787SSean Bruno strncpy(host_wellness_driver_ver->driver_version, softs->os_name, 15581e66f787SSean Bruno sizeof(host_wellness_driver_ver->driver_version)); 15591e66f787SSean Bruno if (strlen(softs->os_name) < sizeof(host_wellness_driver_ver->driver_version) ) { 15601e66f787SSean Bruno strncpy(host_wellness_driver_ver->driver_version + strlen(softs->os_name), PQISRC_DRIVER_VERSION, 15611e66f787SSean Bruno sizeof(host_wellness_driver_ver->driver_version) - strlen(softs->os_name)); 15621e66f787SSean Bruno } else { 1563*b17f4335SSean Bruno DBG_DISC("OS name length(%lu) is longer than buffer of driver_version\n", 15641e66f787SSean Bruno strlen(softs->os_name)); 15651e66f787SSean Bruno } 15661e66f787SSean Bruno host_wellness_driver_ver->driver_version[sizeof(host_wellness_driver_ver->driver_version) - 1] = '\0'; 15671e66f787SSean Bruno host_wellness_driver_ver->end_tag[0] = 'Z'; 15681e66f787SSean Bruno host_wellness_driver_ver->end_tag[1] = 'Z'; 15691e66f787SSean Bruno 15701e66f787SSean Bruno rval = pqisrc_build_send_raid_request(softs, &request, host_wellness_driver_ver,data_length, 15711e66f787SSean Bruno BMIC_WRITE_HOST_WELLNESS, 0, (uint8_t *)RAID_CTLR_LUNID, NULL); 15721e66f787SSean Bruno 15731e66f787SSean Bruno os_mem_free(softs, (char *)host_wellness_driver_ver, data_length); 15741e66f787SSean Bruno 15751e66f787SSean Bruno DBG_FUNC("OUT"); 15761e66f787SSean Bruno return rval; 15771e66f787SSean Bruno } 15781e66f787SSean Bruno 15791e66f787SSean Bruno /* 15801e66f787SSean Bruno * Write current RTC time from host to the adapter using 15811e66f787SSean Bruno * BMIC_WRITE_HOST_WELLNESS 15821e66f787SSean Bruno */ 15831e66f787SSean Bruno int pqisrc_write_current_time_to_host_wellness(pqisrc_softstate_t *softs) 15841e66f787SSean Bruno { 15851e66f787SSean Bruno int rval = PQI_STATUS_SUCCESS; 15861e66f787SSean Bruno struct bmic_host_wellness_time *host_wellness_time; 15871e66f787SSean Bruno size_t data_length; 15881e66f787SSean Bruno pqisrc_raid_req_t request; 15891e66f787SSean Bruno 15901e66f787SSean Bruno DBG_FUNC("IN\n"); 15911e66f787SSean Bruno 15921e66f787SSean Bruno memset(&request, 0, sizeof(request)); 15931e66f787SSean Bruno data_length = sizeof(*host_wellness_time); 15941e66f787SSean Bruno 15951e66f787SSean Bruno host_wellness_time = os_mem_alloc(softs, data_length); 15961e66f787SSean Bruno if (!host_wellness_time) { 15971e66f787SSean Bruno DBG_ERR("failed to allocate memory for host wellness time structure\n"); 15981e66f787SSean Bruno return PQI_STATUS_FAILURE; 15991e66f787SSean Bruno } 16001e66f787SSean Bruno 16011e66f787SSean Bruno host_wellness_time->start_tag[0] = '<'; 16021e66f787SSean Bruno host_wellness_time->start_tag[1] = 'H'; 16031e66f787SSean Bruno host_wellness_time->start_tag[2] = 'W'; 16041e66f787SSean Bruno host_wellness_time->start_tag[3] = '>'; 16051e66f787SSean Bruno host_wellness_time->time_tag[0] = 'T'; 16061e66f787SSean Bruno host_wellness_time->time_tag[1] = 'D'; 16071e66f787SSean Bruno host_wellness_time->time_length = LE_16(offsetof(struct bmic_host_wellness_time, time_length) - 16081e66f787SSean Bruno offsetof(struct bmic_host_wellness_time, century)); 16091e66f787SSean Bruno 16101e66f787SSean Bruno os_get_time(host_wellness_time); 16111e66f787SSean Bruno 16121e66f787SSean Bruno host_wellness_time->dont_write_tag[0] = 'D'; 16131e66f787SSean Bruno host_wellness_time->dont_write_tag[1] = 'W'; 16141e66f787SSean Bruno host_wellness_time->end_tag[0] = 'Z'; 16151e66f787SSean Bruno host_wellness_time->end_tag[1] = 'Z'; 16161e66f787SSean Bruno 16171e66f787SSean Bruno rval = pqisrc_build_send_raid_request(softs, &request, host_wellness_time,data_length, 16181e66f787SSean Bruno BMIC_WRITE_HOST_WELLNESS, 0, (uint8_t *)RAID_CTLR_LUNID, NULL); 16191e66f787SSean Bruno 16201e66f787SSean Bruno os_mem_free(softs, (char *)host_wellness_time, data_length); 16211e66f787SSean Bruno 16221e66f787SSean Bruno DBG_FUNC("OUT"); 16231e66f787SSean Bruno return rval; 16241e66f787SSean Bruno } 16251e66f787SSean Bruno 16261e66f787SSean Bruno /* 16271e66f787SSean Bruno * Function used to perform a rescan of scsi devices 16281e66f787SSean Bruno * for any config change events 16291e66f787SSean Bruno */ 16301e66f787SSean Bruno int pqisrc_scan_devices(pqisrc_softstate_t *softs) 16311e66f787SSean Bruno { 16321e66f787SSean Bruno boolean_t is_physical_device; 16331e66f787SSean Bruno int ret = PQI_STATUS_FAILURE; 16341e66f787SSean Bruno int i; 16351e66f787SSean Bruno int new_dev_cnt; 16361e66f787SSean Bruno int phy_log_dev_cnt; 16371e66f787SSean Bruno uint8_t *scsi3addr; 16381e66f787SSean Bruno uint32_t physical_cnt; 16391e66f787SSean Bruno uint32_t logical_cnt; 16401e66f787SSean Bruno uint32_t ndev_allocated = 0; 16411e66f787SSean Bruno size_t phys_data_length, log_data_length; 16421e66f787SSean Bruno reportlun_data_ext_t *physical_dev_list = NULL; 16431e66f787SSean Bruno reportlun_data_ext_t *logical_dev_list = NULL; 16441e66f787SSean Bruno reportlun_ext_entry_t *lun_ext_entry = NULL; 16451e66f787SSean Bruno bmic_ident_physdev_t *bmic_phy_info = NULL; 16461e66f787SSean Bruno pqi_scsi_dev_t **new_device_list = NULL; 16471e66f787SSean Bruno pqi_scsi_dev_t *device = NULL; 16481e66f787SSean Bruno 16491e66f787SSean Bruno 16501e66f787SSean Bruno DBG_FUNC("IN\n"); 16511e66f787SSean Bruno 16521e66f787SSean Bruno ret = pqisrc_get_phys_log_device_list(softs, &physical_dev_list, &logical_dev_list, 16531e66f787SSean Bruno &phys_data_length, &log_data_length); 16541e66f787SSean Bruno 16551e66f787SSean Bruno if (ret) 16561e66f787SSean Bruno goto err_out; 16571e66f787SSean Bruno 16581e66f787SSean Bruno physical_cnt = BE_32(physical_dev_list->header.list_length) 16591e66f787SSean Bruno / sizeof(physical_dev_list->lun_entries[0]); 16601e66f787SSean Bruno 16611e66f787SSean Bruno logical_cnt = BE_32(logical_dev_list->header.list_length) 16621e66f787SSean Bruno / sizeof(logical_dev_list->lun_entries[0]); 16631e66f787SSean Bruno 1664*b17f4335SSean Bruno DBG_DISC("physical_cnt %d logical_cnt %d\n", physical_cnt, logical_cnt); 16651e66f787SSean Bruno 16661e66f787SSean Bruno if (physical_cnt) { 16671e66f787SSean Bruno bmic_phy_info = os_mem_alloc(softs, sizeof(*bmic_phy_info)); 16681e66f787SSean Bruno if (bmic_phy_info == NULL) { 16691e66f787SSean Bruno ret = PQI_STATUS_FAILURE; 16701e66f787SSean Bruno DBG_ERR("failed to allocate memory for BMIC ID PHYS Device : %d\n", ret); 16711e66f787SSean Bruno goto err_out; 16721e66f787SSean Bruno } 16731e66f787SSean Bruno } 16741e66f787SSean Bruno phy_log_dev_cnt = physical_cnt + logical_cnt; 16751e66f787SSean Bruno new_device_list = os_mem_alloc(softs, 16761e66f787SSean Bruno sizeof(*new_device_list) * phy_log_dev_cnt); 16771e66f787SSean Bruno 16781e66f787SSean Bruno if (new_device_list == NULL) { 16791e66f787SSean Bruno ret = PQI_STATUS_FAILURE; 16801e66f787SSean Bruno DBG_ERR("failed to allocate memory for device list : %d\n", ret); 16811e66f787SSean Bruno goto err_out; 16821e66f787SSean Bruno } 16831e66f787SSean Bruno 16841e66f787SSean Bruno for (i = 0; i < phy_log_dev_cnt; i++) { 16851e66f787SSean Bruno new_device_list[i] = os_mem_alloc(softs, 16861e66f787SSean Bruno sizeof(*new_device_list[i])); 16871e66f787SSean Bruno if (new_device_list[i] == NULL) { 16881e66f787SSean Bruno ret = PQI_STATUS_FAILURE; 16891e66f787SSean Bruno DBG_ERR("failed to allocate memory for device list : %d\n", ret); 16901e66f787SSean Bruno ndev_allocated = i; 16911e66f787SSean Bruno goto err_out; 16921e66f787SSean Bruno } 16931e66f787SSean Bruno } 16941e66f787SSean Bruno 16951e66f787SSean Bruno ndev_allocated = phy_log_dev_cnt; 16961e66f787SSean Bruno new_dev_cnt = 0; 16971e66f787SSean Bruno for (i = 0; i < phy_log_dev_cnt; i++) { 16981e66f787SSean Bruno 16991e66f787SSean Bruno if (i < physical_cnt) { 17001e66f787SSean Bruno is_physical_device = true; 17011e66f787SSean Bruno lun_ext_entry = &physical_dev_list->lun_entries[i]; 17021e66f787SSean Bruno } else { 17031e66f787SSean Bruno is_physical_device = false; 17041e66f787SSean Bruno lun_ext_entry = 17051e66f787SSean Bruno &logical_dev_list->lun_entries[i - physical_cnt]; 17061e66f787SSean Bruno } 17071e66f787SSean Bruno 17081e66f787SSean Bruno scsi3addr = lun_ext_entry->lunid; 1709*b17f4335SSean Bruno /* Save the target sas adderess for external raid device */ 1710*b17f4335SSean Bruno if(lun_ext_entry->device_type == CONTROLLER_DEVICE) { 1711*b17f4335SSean Bruno int target = lun_ext_entry->lunid[3] & 0x3f; 1712*b17f4335SSean Bruno softs->target_sas_addr[target] = BE_64(lun_ext_entry->wwid); 1713*b17f4335SSean Bruno } 17141e66f787SSean Bruno 17151e66f787SSean Bruno /* Skip masked physical non-disk devices. */ 1716*b17f4335SSean Bruno if (MASKED_DEVICE(scsi3addr) && is_physical_device 1717*b17f4335SSean Bruno && (lun_ext_entry->ioaccel_handle == 0)) 17181e66f787SSean Bruno continue; 17191e66f787SSean Bruno 17201e66f787SSean Bruno device = new_device_list[new_dev_cnt]; 17211e66f787SSean Bruno memset(device, 0, sizeof(*device)); 17221e66f787SSean Bruno memcpy(device->scsi3addr, scsi3addr, sizeof(device->scsi3addr)); 17231e66f787SSean Bruno device->wwid = lun_ext_entry->wwid; 17241e66f787SSean Bruno device->is_physical_device = is_physical_device; 17251e66f787SSean Bruno if (!is_physical_device) 17261e66f787SSean Bruno device->is_external_raid_device = 17271e66f787SSean Bruno pqisrc_is_external_raid_addr(scsi3addr); 17281e66f787SSean Bruno 17291e66f787SSean Bruno 17301e66f787SSean Bruno /* Get device type, vendor, model, device ID. */ 17311e66f787SSean Bruno ret = pqisrc_get_dev_data(softs, device); 17321e66f787SSean Bruno if (ret) { 17331e66f787SSean Bruno DBG_WARN("Inquiry failed, skipping device %016llx\n", 17341e66f787SSean Bruno (unsigned long long)BE_64(device->scsi3addr[0])); 1735*b17f4335SSean Bruno DBG_DISC("INQUIRY FAILED \n"); 17361e66f787SSean Bruno continue; 17371e66f787SSean Bruno } 17381e66f787SSean Bruno pqisrc_assign_btl(device); 17391e66f787SSean Bruno 17401e66f787SSean Bruno /* 17411e66f787SSean Bruno * Expose all devices except for physical devices that 17421e66f787SSean Bruno * are masked. 17431e66f787SSean Bruno */ 17441e66f787SSean Bruno if (device->is_physical_device && 17451e66f787SSean Bruno MASKED_DEVICE(scsi3addr)) 17461e66f787SSean Bruno device->expose_device = false; 17471e66f787SSean Bruno else 17481e66f787SSean Bruno device->expose_device = true; 17491e66f787SSean Bruno 17501e66f787SSean Bruno if (device->is_physical_device && 17511e66f787SSean Bruno (lun_ext_entry->device_flags & 17521e66f787SSean Bruno REPORT_LUN_DEV_FLAG_AIO_ENABLED) && 17531e66f787SSean Bruno lun_ext_entry->ioaccel_handle) { 17541e66f787SSean Bruno device->aio_enabled = true; 17551e66f787SSean Bruno } 17561e66f787SSean Bruno switch (device->devtype) { 17571e66f787SSean Bruno case ROM_DEVICE: 17581e66f787SSean Bruno /* 17591e66f787SSean Bruno * We don't *really* support actual CD-ROM devices, 17601e66f787SSean Bruno * but we do support the HP "One Button Disaster 17611e66f787SSean Bruno * Recovery" tape drive which temporarily pretends to 17621e66f787SSean Bruno * be a CD-ROM drive. 17631e66f787SSean Bruno */ 17641e66f787SSean Bruno if (device->is_obdr_device) 17651e66f787SSean Bruno new_dev_cnt++; 17661e66f787SSean Bruno break; 17671e66f787SSean Bruno case DISK_DEVICE: 17681e66f787SSean Bruno case ZBC_DEVICE: 17691e66f787SSean Bruno if (device->is_physical_device) { 17701e66f787SSean Bruno device->ioaccel_handle = 17711e66f787SSean Bruno lun_ext_entry->ioaccel_handle; 17721e66f787SSean Bruno device->sas_address = BE_64(lun_ext_entry->wwid); 17731e66f787SSean Bruno pqisrc_get_physical_device_info(softs, device, 17741e66f787SSean Bruno bmic_phy_info); 17751e66f787SSean Bruno } 17761e66f787SSean Bruno new_dev_cnt++; 17771e66f787SSean Bruno break; 17781e66f787SSean Bruno case ENCLOSURE_DEVICE: 17791e66f787SSean Bruno if (device->is_physical_device) { 17801e66f787SSean Bruno device->sas_address = BE_64(lun_ext_entry->wwid); 17811e66f787SSean Bruno } 17821e66f787SSean Bruno new_dev_cnt++; 17831e66f787SSean Bruno break; 17841e66f787SSean Bruno case TAPE_DEVICE: 17851e66f787SSean Bruno case MEDIUM_CHANGER_DEVICE: 17861e66f787SSean Bruno new_dev_cnt++; 17871e66f787SSean Bruno break; 17881e66f787SSean Bruno case RAID_DEVICE: 17891e66f787SSean Bruno /* 17901e66f787SSean Bruno * Only present the HBA controller itself as a RAID 17911e66f787SSean Bruno * controller. If it's a RAID controller other than 17921e66f787SSean Bruno * the HBA itself (an external RAID controller, MSA500 17931e66f787SSean Bruno * or similar), don't present it. 17941e66f787SSean Bruno */ 17951e66f787SSean Bruno if (pqisrc_is_hba_lunid(scsi3addr)) 17961e66f787SSean Bruno new_dev_cnt++; 17971e66f787SSean Bruno break; 1798*b17f4335SSean Bruno case SES_DEVICE: 1799*b17f4335SSean Bruno case CONTROLLER_DEVICE: 1800*b17f4335SSean Bruno break; 18011e66f787SSean Bruno } 18021e66f787SSean Bruno } 1803*b17f4335SSean Bruno DBG_DISC("new_dev_cnt %d\n", new_dev_cnt); 18041e66f787SSean Bruno 18051e66f787SSean Bruno pqisrc_update_device_list(softs, new_device_list, new_dev_cnt); 18061e66f787SSean Bruno 18071e66f787SSean Bruno err_out: 18081e66f787SSean Bruno if (new_device_list) { 18091e66f787SSean Bruno for (i = 0; i < ndev_allocated; i++) { 18101e66f787SSean Bruno if (new_device_list[i]) { 18111e66f787SSean Bruno if(new_device_list[i]->raid_map) 18121e66f787SSean Bruno os_mem_free(softs, (char *)new_device_list[i]->raid_map, 18131e66f787SSean Bruno sizeof(pqisrc_raid_map_t)); 18141e66f787SSean Bruno os_mem_free(softs, (char*)new_device_list[i], 18151e66f787SSean Bruno sizeof(*new_device_list[i])); 18161e66f787SSean Bruno } 18171e66f787SSean Bruno } 18181e66f787SSean Bruno os_mem_free(softs, (char *)new_device_list, 18191e66f787SSean Bruno sizeof(*new_device_list) * ndev_allocated); 18201e66f787SSean Bruno } 18211e66f787SSean Bruno if(physical_dev_list) 18221e66f787SSean Bruno os_mem_free(softs, (char *)physical_dev_list, phys_data_length); 18231e66f787SSean Bruno if(logical_dev_list) 18241e66f787SSean Bruno os_mem_free(softs, (char *)logical_dev_list, log_data_length); 18251e66f787SSean Bruno if (bmic_phy_info) 18261e66f787SSean Bruno os_mem_free(softs, (char *)bmic_phy_info, sizeof(*bmic_phy_info)); 18271e66f787SSean Bruno 18281e66f787SSean Bruno DBG_FUNC("OUT \n"); 18291e66f787SSean Bruno 18301e66f787SSean Bruno return ret; 18311e66f787SSean Bruno } 18321e66f787SSean Bruno 18331e66f787SSean Bruno /* 18341e66f787SSean Bruno * Clean up memory allocated for devices. 18351e66f787SSean Bruno */ 18361e66f787SSean Bruno void pqisrc_cleanup_devices(pqisrc_softstate_t *softs) 18371e66f787SSean Bruno { 18381e66f787SSean Bruno 18391e66f787SSean Bruno int i = 0,j = 0; 18401e66f787SSean Bruno pqi_scsi_dev_t *dvp = NULL; 18411e66f787SSean Bruno DBG_FUNC("IN\n"); 18421e66f787SSean Bruno 18431e66f787SSean Bruno for(i = 0; i < PQI_MAX_DEVICES; i++) { 18441e66f787SSean Bruno for(j = 0; j < PQI_MAX_MULTILUN; j++) { 18451e66f787SSean Bruno if (softs->device_list[i][j] == NULL) 18461e66f787SSean Bruno continue; 18471e66f787SSean Bruno dvp = softs->device_list[i][j]; 18481e66f787SSean Bruno pqisrc_device_mem_free(softs, dvp); 18491e66f787SSean Bruno } 18501e66f787SSean Bruno } 18511e66f787SSean Bruno DBG_FUNC("OUT\n"); 18521e66f787SSean Bruno } 18531e66f787SSean Bruno 1854