#include "smartpqi_includes.h"
int
pqisrc_QuerySenseFeatures(pqisrc_softstate_t *softs)
{
bmic_sense_feature_aio_buffer_t *features;
int ret;
pqisrc_raid_req_t request;
softs->max_aio_write_raid5_6 =
PQISRC_MAX_AIO_RAID5_OR_6_WRITE;
softs->max_aio_write_raid1_10_2drv =
PQISRC_MAX_AIO_RAID1_OR_10_WRITE_2DRV;
softs->max_aio_write_raid1_10_3drv =
PQISRC_MAX_AIO_RAID1_OR_10_WRITE_3DRV;
softs->max_aio_rw_xfer_crypto_nvme =
PQISRC_MAX_AIO_RW_XFER_NVME_CRYPTO;
softs->max_aio_rw_xfer_crypto_sas_sata =
PQISRC_MAX_AIO_RW_XFER_SAS_SATA_CRYPTO;
#ifdef DEVICE_HINT
softs->enable_stream_detection = softs->hint.stream_status;
#endif
features = os_mem_alloc(softs, sizeof(*features));
if (!features) {
DBG_ERR("Failed to allocate memory for sense aio features.\n");
goto err;
}
memset(features, 0, sizeof(*features));
memset(&request, 0, sizeof(request));
request.data_direction = SOP_DATA_DIR_TO_DEVICE;
request.cmd.bmic_cdb.op_code = BMIC_READ;
request.cmd.cdb[2] = IO_SENSE_FEATURES_PAGE;
request.cmd.cdb[3] = SENSE_FEATURES_AIO_SUBPAGE;
request.cmd.bmic_cdb.cmd = BMIC_SENSE_FEATURE;
request.cmd.bmic_cdb.xfer_len = BE_16(sizeof(*features));
ret = pqisrc_prepare_send_ctrlr_request(softs, &request,
features, sizeof(*features));
if (ret)
goto free_err;
if (features->aio_subpage.header.total_length >=
MINIMUM_AIO_SUBPAGE_LENGTH) {
DBG_INIT("AIO support subpage valid. total_length = 0x%0x.\n",
features->aio_subpage.header.total_length);
softs->adv_aio_capable = true;
softs->max_aio_write_raid5_6 =
(features->aio_subpage.max_aio_write_raid5_6 == 0) ?
PQISRC_MAX_AIO_NO_LIMIT :
features->aio_subpage.max_aio_write_raid5_6 * 1024;
softs->max_aio_write_raid1_10_2drv =
(features->aio_subpage.max_aio_write_raid1_10_2drv
== 0) ? PQISRC_MAX_AIO_NO_LIMIT :
features->aio_subpage.max_aio_write_raid1_10_2drv
* 1024;
softs->max_aio_write_raid1_10_3drv =
(features->aio_subpage.max_aio_write_raid1_10_3drv
== 0) ? PQISRC_MAX_AIO_NO_LIMIT :
features->aio_subpage.max_aio_write_raid1_10_3drv
* 1024;
softs->max_aio_rw_xfer_crypto_nvme =
(features->aio_subpage.max_aio_rw_xfer_crypto_nvme
== 0) ? PQISRC_MAX_AIO_NO_LIMIT :
features->aio_subpage.max_aio_rw_xfer_crypto_nvme
* 1024;
softs->max_aio_rw_xfer_crypto_sas_sata =
(features->aio_subpage.max_aio_rw_xfer_crypto_sas_sata
== 0) ? PQISRC_MAX_AIO_NO_LIMIT :
features->aio_subpage.max_aio_rw_xfer_crypto_sas_sata
* 1024;
DBG_INIT("softs->max_aio_write_raid5_6: 0x%x\n",
softs->max_aio_write_raid5_6);
DBG_INIT("softs->max_aio_write_raid1_10_2drv: 0x%x\n",
softs->max_aio_write_raid1_10_2drv);
DBG_INIT("softs->max_aio_write_raid1_10_3drv: 0x%x\n",
softs->max_aio_write_raid1_10_3drv);
DBG_INIT("softs->max_aio_rw_xfer_crypto_nvme: 0x%x\n",
softs->max_aio_rw_xfer_crypto_nvme);
DBG_INIT("softs->max_aio_rw_xfer_crypto_sas_sata: 0x%x\n",
softs->max_aio_rw_xfer_crypto_sas_sata);
} else {
DBG_WARN("Problem getting AIO support subpage settings. "
"Disabling advanced AIO writes.\n");
softs->adv_aio_capable = false;
}
os_mem_free(softs, features, sizeof(*features));
return ret;
free_err:
os_mem_free(softs, features, sizeof(*features));
err:
return PQI_STATUS_FAILURE;
}
void
pqisrc_init_bitmap(pqisrc_softstate_t *softs)
{
memset(&softs->bit_map, SLOT_AVAILABLE, sizeof(softs->bit_map));
}
void
pqisrc_remove_target_bit(pqisrc_softstate_t *softs, int target)
{
if((target == PQI_CTLR_INDEX) || (target == INVALID_ELEM)) {
DBG_ERR("Invalid target ID\n");
return;
}
DBG_DISC("Giving back target %d\n", target);
softs->bit_map.bit_vector[target] = SLOT_AVAILABLE;
}
int
pqisrc_find_avail_target(pqisrc_softstate_t *softs)
{
int avail_target;
for(avail_target = 1; avail_target < MAX_TARGET_BIT; avail_target++) {
if(softs->bit_map.bit_vector[avail_target] == SLOT_AVAILABLE){
softs->bit_map.bit_vector[avail_target] = SLOT_TAKEN;
DBG_DISC("Avail_target is %d\n", avail_target);
return avail_target;
}
}
DBG_ERR("No available targets\n");
return INVALID_ELEM;
}
static inline void
pqisrc_set_btl(pqi_scsi_dev_t *device, int bus, int target, int lun)
{
DBG_FUNC("IN\n");
device->bus = bus;
device->target = target;
device->lun = lun;
DBG_FUNC("OUT\n");
}
boolean_t
pqisrc_add_softs_entry(pqisrc_softstate_t *softs, pqi_scsi_dev_t *device,
uint8_t const *scsi3addr)
{
int j;
int tid = 0;
unsigned char addr1[8], addr2[8];
pqi_scsi_dev_t *temp_device;
if(pqisrc_is_hba_lunid(scsi3addr))
goto add_device_to_dev_list;
if(!pqisrc_is_logical_device(device)) {
tid = pqisrc_find_avail_target(softs);
if(INVALID_ELEM != tid){
pqisrc_set_btl(device, PQI_PHYSICAL_DEVICE_BUS, tid, 0);
goto add_device_to_dev_list;
}
}
if (pqisrc_is_external_raid_device(device)) {
memcpy(addr1, device->scsi3addr, 8);
for(j = 0; j < PQI_MAX_DEVICES; j++) {
if(softs->dev_list[j] == NULL)
continue;
temp_device = softs->dev_list[j];
memcpy(addr2, temp_device->scsi3addr, 8);
if (addr1[2] == addr2[2]) {
pqisrc_set_btl(device, PQI_EXTERNAL_RAID_VOLUME_BUS,
temp_device->target,device->scsi3addr[0]);
goto add_device_to_dev_list;
}
}
tid = pqisrc_find_avail_target(softs);
if(INVALID_ELEM != tid){
pqisrc_set_btl(device, PQI_EXTERNAL_RAID_VOLUME_BUS, tid, device->scsi3addr[0]);
goto add_device_to_dev_list;
}
}
if(pqisrc_is_logical_device(device) && !pqisrc_is_external_raid_device(device))
goto add_device_to_dev_list;
memcpy(addr1, device->scsi3addr, 8);
addr1[4] = 0;
addr1[5] = 0;
for(j = 0; j < PQI_MAX_DEVICES; j++) {
if(softs->dev_list[j] == NULL)
continue;
temp_device = softs->dev_list[j];
memcpy(addr2, temp_device->scsi3addr, 8);
addr2[4] = 0;
addr2[5] = 0;
if(memcmp(addr1, addr2, 8) == 0) {
pqisrc_set_btl(device, temp_device->bus,
temp_device->target, temp_device->scsi3addr[4]);
goto add_device_to_dev_list;
}
}
DBG_ERR("The device is not a physical, lun or ptraid device"
"B%d:T%d:L%d\n", device->bus, device->target,
device->lun );
return false;
add_device_to_dev_list:
softs->num_devs++;
for(j = 0; j < PQI_MAX_DEVICES; j++) {
if(softs->dev_list[j])
continue;
softs->dev_list[j] = device;
break;
}
DBG_NOTE("Added device [%d of %d]: B%d:T%d:L%d\n",
j, softs->num_devs, device->bus, device->target,
device->lun);
return true;
}
int
pqisrc_find_btl_list_index(pqisrc_softstate_t *softs,
int bus, int target, int lun)
{
int index;
pqi_scsi_dev_t *temp_device;
for(index = 0; index < PQI_MAX_DEVICES; index++) {
if(softs->dev_list[index] == NULL)
continue;
temp_device = softs->dev_list[index];
if(bus == softs->bus_id &&
target == temp_device->target &&
lun == temp_device->lun){
return index;
}
if ((temp_device->is_physical_device) && (target == temp_device->target)
&& (temp_device->is_multi_lun)) {
return index;
}
}
return INVALID_ELEM;
}
int
pqisrc_find_device_list_index(pqisrc_softstate_t *softs, pqi_scsi_dev_t const *device)
{
int index;
pqi_scsi_dev_t *temp_device;
for(index = 0; index < PQI_MAX_DEVICES; index++) {
if(softs->dev_list[index] == NULL)
continue;
temp_device = softs->dev_list[index];
if(device->bus == temp_device->bus &&
device->target == temp_device->target
&& device->lun == temp_device->lun){
DBG_DISC("Returning device list index %d\n", index);
return index;
}
}
return INVALID_ELEM;
}
int
pqisrc_delete_softs_entry(pqisrc_softstate_t *softs, pqi_scsi_dev_t const *device)
{
int index;
index = pqisrc_find_device_list_index(softs, device);
if (0 <= index && index < MAX_TARGET_BIT) {
softs->dev_list[index] = NULL;
softs->num_devs--;
DBG_NOTE("Removing device: B%d:T%d:L%d positioned at %d\n",
device->bus, device->target, device->lun, index);
return PQI_STATUS_SUCCESS;
}
if (index == INVALID_ELEM) {
DBG_NOTE("Invalid device, either it was already removed "
"or never added\n");
return PQI_STATUS_FAILURE;
}
DBG_ERR("This is a bogus device\n");
return PQI_STATUS_FAILURE;
}
int
pqisrc_simple_dma_alloc(pqisrc_softstate_t *softs, struct dma_mem *device_mem,
size_t datasize, sgt_t *sgd)
{
int ret = PQI_STATUS_SUCCESS;
memset(device_mem, 0, sizeof(struct dma_mem));
if (datasize) {
os_strlcpy(device_mem->tag, "device_mem", sizeof(device_mem->tag));
device_mem->size = datasize;
device_mem->align = PQISRC_DEFAULT_DMA_ALIGN;
ret = os_dma_mem_alloc(softs, device_mem);
if (ret) {
DBG_ERR("failed to allocate dma memory for device_mem return code %d\n", ret);
return ret;
}
ASSERT(device_mem->size == datasize);
sgd->addr = device_mem->dma_addr;
sgd->len = datasize;
sgd->flags = SG_FLAG_LAST;
}
return ret;
}
static int
pqisrc_build_send_raid_request(pqisrc_softstate_t *softs, struct dma_mem device_mem,
pqisrc_raid_req_t *request, void *buff,
size_t datasize, uint8_t cmd, uint8_t *scsi3addr,
raid_path_error_info_elem_t *error_info)
{
uint32_t tag = 0;
int ret = PQI_STATUS_SUCCESS;
ib_queue_t *ib_q = &softs->op_raid_ib_q[PQI_DEFAULT_IB_QUEUE];
ob_queue_t const *ob_q = &softs->op_ob_q[PQI_DEFAULT_IB_QUEUE];
rcb_t *rcb = NULL;
request->header.iu_type = PQI_IU_TYPE_RAID_PATH_IO_REQUEST;
request->header.iu_length = LE_16(offsetof(pqisrc_raid_req_t,
sg_descriptors[1]) - PQI_REQUEST_HEADER_LENGTH);
request->buffer_length = LE_32(datasize);
memcpy(request->lun_number, scsi3addr, sizeof(request->lun_number));
request->task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE;
request->additional_cdb_bytes_usage = PQI_ADDITIONAL_CDB_BYTES_0;
tag = pqisrc_get_tag(&softs->taglist);
if (INVALID_ELEM == tag) {
DBG_ERR("Tag not available\n");
ret = PQI_STATUS_FAILURE;
goto err_notag;
}
((pqisrc_raid_req_t *)request)->request_id = tag;
((pqisrc_raid_req_t *)request)->error_index = ((pqisrc_raid_req_t *)request)->request_id;
((pqisrc_raid_req_t *)request)->response_queue_id = ob_q->q_id;
rcb = &softs->rcb[tag];
rcb->success_cmp_callback = pqisrc_process_internal_raid_response_success;
rcb->error_cmp_callback = pqisrc_process_internal_raid_response_error;
rcb->req_pending = true;
rcb->tag = tag;
ret = pqisrc_submit_cmnd(softs, ib_q, request);
if (ret != PQI_STATUS_SUCCESS) {
DBG_ERR("Unable to submit command\n");
goto err_out;
}
ret = pqisrc_wait_on_condition(softs, rcb, PQISRC_CMD_TIMEOUT);
if (ret != PQI_STATUS_SUCCESS) {
DBG_ERR("Internal RAID request timed out: cmd : 0x%c\n", cmd);
goto err_out;
}
if (datasize) {
if (buff) {
memcpy(buff, device_mem.virt_addr, datasize);
}
os_dma_mem_free(softs, &device_mem);
}
ret = rcb->status;
if (ret) {
if(error_info) {
memcpy(error_info,
rcb->error_info,
sizeof(*error_info));
if (error_info->data_out_result ==
PQI_RAID_DATA_IN_OUT_UNDERFLOW) {
ret = PQI_STATUS_SUCCESS;
}
else{
DBG_WARN("Bus=%u Target=%u, Cmd=0x%x,"
"Ret=%d\n", BMIC_GET_LEVEL_2_BUS(scsi3addr),
BMIC_GET_LEVEL_TWO_TARGET(scsi3addr),
cmd, ret);
ret = PQI_STATUS_FAILURE;
}
}
} else {
if(error_info) {
ret = PQI_STATUS_SUCCESS;
memset(error_info, 0, sizeof(*error_info));
}
}
os_reset_rcb(rcb);
pqisrc_put_tag(&softs->taglist, ((pqisrc_raid_req_t *)request)->request_id);
DBG_FUNC("OUT\n");
return ret;
err_out:
DBG_ERR("Error!! Bus=%u Target=%u, Cmd=0x%x, Ret=%d\n",
BMIC_GET_LEVEL_2_BUS(scsi3addr), BMIC_GET_LEVEL_TWO_TARGET(scsi3addr),
cmd, ret);
os_reset_rcb(rcb);
pqisrc_put_tag(&softs->taglist, ((pqisrc_raid_req_t *)request)->request_id);
err_notag:
if (datasize)
os_dma_mem_free(softs, &device_mem);
DBG_FUNC("FAILED \n");
return ret;
}
int
pqisrc_prepare_send_raid(pqisrc_softstate_t *softs, pqisrc_raid_req_t *request,
void *buff, size_t datasize, uint8_t *scsi3addr,
raid_path_error_info_elem_t *error_info)
{
struct dma_mem device_mem;
int ret = PQI_STATUS_SUCCESS;
uint8_t cmd = IS_BMIC_OPCODE(request->cmd.cdb[0]) ? request->cmd.cdb[6] : request->cmd.cdb[0];
ret = pqisrc_simple_dma_alloc(softs, &device_mem, datasize, request->sg_descriptors);
if (PQI_STATUS_SUCCESS != ret){
DBG_ERR("failed to allocate dma memory for device_mem return code %d\n", ret);
return ret;
}
if (datasize && buff && request->data_direction == SOP_DATA_DIR_FROM_DEVICE)
memcpy(device_mem.virt_addr, buff, datasize);
ret = pqisrc_build_send_raid_request(softs, device_mem, request, buff, datasize,
cmd, scsi3addr, error_info);
return ret;
}
int
pqisrc_prepare_send_ctrlr_request(pqisrc_softstate_t *softs, pqisrc_raid_req_t *request,
void *buff, size_t datasize)
{
raid_path_error_info_elem_t error_info;
uint8_t *scsi3addr = RAID_CTLR_LUNID;
return pqisrc_prepare_send_raid(softs, request, buff, datasize, scsi3addr, &error_info);
}
static int
pqisrc_report_luns(pqisrc_softstate_t *softs, uint8_t cmd,
void *buff, size_t buf_len)
{
int ret;
pqisrc_raid_req_t request;
DBG_FUNC("IN\n");
memset(&request, 0, sizeof(request));
request.data_direction = SOP_DATA_DIR_TO_DEVICE;
switch (cmd) {
case SA_REPORT_LOG:
request.cmd.cdb[0] = SA_REPORT_LOG;
request.cmd.cdb[1] = SA_REPORT_LOG_EXTENDED;
break;
case SA_REPORT_PHYS:
request.cmd.cdb[0] = SA_REPORT_PHYS;
request.cmd.cdb[1] = SA_REPORT_PHYS_EXTENDED;
break;
case PQI_LOG_EXT_QUEUE_ENABLE:
request.cmd.cdb[0] = SA_REPORT_LOG;
request.cmd.cdb[1] = (PQI_LOG_EXT_QUEUE_DEPTH_ENABLED | SA_REPORT_LOG_EXTENDED);
break;
}
request.cmd.cdb[8] = (uint8_t)((buf_len) >> 8);
request.cmd.cdb[9] = (uint8_t)buf_len;
ret = pqisrc_prepare_send_ctrlr_request(softs, &request, buff, buf_len);
DBG_FUNC("OUT\n");
return ret;
}
int
pqisrc_get_physical_logical_luns(pqisrc_softstate_t *softs, uint8_t cmd,
reportlun_data_ext_t **buff, size_t *data_length)
{
int ret;
size_t list_len;
size_t data_len;
size_t new_lun_list_length;
reportlun_data_ext_t *lun_data;
reportlun_header_t report_lun_header;
DBG_FUNC("IN\n");
ret = pqisrc_report_luns(softs, cmd, &report_lun_header,
sizeof(report_lun_header));
if (ret) {
DBG_ERR("failed return code: %d\n", ret);
return ret;
}
list_len = BE_32(report_lun_header.list_length);
retry:
data_len = sizeof(reportlun_header_t) + list_len;
*data_length = data_len;
lun_data = os_mem_alloc(softs, data_len);
if (!lun_data) {
DBG_ERR("failed to allocate memory for lun_data\n");
return PQI_STATUS_FAILURE;
}
if (list_len == 0) {
DBG_DISC("list_len is 0\n");
memcpy(lun_data, &report_lun_header, sizeof(report_lun_header));
goto out;
}
ret = pqisrc_report_luns(softs, cmd, lun_data, data_len);
if (ret) {
DBG_ERR("error\n");
goto error;
}
new_lun_list_length = BE_32(lun_data->header.list_length);
if (new_lun_list_length > list_len) {
list_len = new_lun_list_length;
os_mem_free(softs, (void *)lun_data, data_len);
goto retry;
}
out:
*buff = lun_data;
DBG_FUNC("OUT\n");
return 0;
error:
os_mem_free(softs, (void *)lun_data, data_len);
DBG_ERR("FAILED\n");
return ret;
}
static int
pqisrc_get_queue_lun_list(pqisrc_softstate_t *softs, uint8_t cmd,
reportlun_queue_depth_data_t **buff, size_t *data_length)
{
int ret;
size_t list_len;
size_t data_len;
size_t new_lun_list_length;
reportlun_queue_depth_data_t *lun_data;
reportlun_header_t report_lun_header;
DBG_FUNC("IN\n");
ret = pqisrc_report_luns(softs, cmd, &report_lun_header,
sizeof(report_lun_header));
if (ret) {
DBG_ERR("failed return code: %d\n", ret);
return ret;
}
list_len = BE_32(report_lun_header.list_length);
retry:
data_len = sizeof(reportlun_header_t) + list_len;
*data_length = data_len;
lun_data = os_mem_alloc(softs, data_len);
if (!lun_data) {
DBG_ERR("failed to allocate memory for lun_data\n");
return PQI_STATUS_FAILURE;
}
if (list_len == 0) {
DBG_DISC("list_len is 0\n");
memcpy(lun_data, &report_lun_header, sizeof(report_lun_header));
goto out;
}
ret = pqisrc_report_luns(softs, cmd, lun_data, data_len);
if (ret) {
DBG_ERR("error\n");
goto error;
}
new_lun_list_length = BE_32(lun_data->header.list_length);
if (new_lun_list_length > list_len) {
list_len = new_lun_list_length;
os_mem_free(softs, (void *)lun_data, data_len);
goto retry;
}
out:
*buff = lun_data;
DBG_FUNC("OUT\n");
return 0;
error:
os_mem_free(softs, (void *)lun_data, data_len);
DBG_ERR("FAILED\n");
return ret;
}
static int
pqisrc_get_phys_log_device_list(pqisrc_softstate_t *softs,
reportlun_data_ext_t **physical_dev_list,
reportlun_data_ext_t **logical_dev_list,
reportlun_queue_depth_data_t **queue_dev_list,
size_t *queue_data_length,
size_t *phys_data_length,
size_t *log_data_length)
{
int ret = PQI_STATUS_SUCCESS;
size_t logical_list_length;
size_t logdev_data_length;
size_t data_length;
reportlun_data_ext_t *local_logdev_list;
reportlun_data_ext_t *logdev_data;
reportlun_header_t report_lun_header;
DBG_FUNC("IN\n");
ret = pqisrc_get_physical_logical_luns(softs, SA_REPORT_PHYS, physical_dev_list, phys_data_length);
if (ret) {
DBG_ERR("report physical LUNs failed\n");
return ret;
}
ret = pqisrc_get_physical_logical_luns(softs, SA_REPORT_LOG, logical_dev_list, log_data_length);
if (ret) {
DBG_ERR("report logical LUNs failed\n");
return ret;
}
#ifdef PQI_NEED_RESCAN_TIMER_FOR_RBOD_HOTPLUG
if(softs->log_dev_data_length != *log_data_length) {
if(softs->log_dev_list)
os_mem_free(softs, softs->log_dev_list, softs->log_dev_data_length);
softs->log_dev_list = os_mem_alloc(softs, *log_data_length);
}
memcpy(softs->log_dev_list, *logical_dev_list, *log_data_length);
softs->log_dev_data_length = *log_data_length;
#endif
ret = pqisrc_get_queue_lun_list(softs, PQI_LOG_EXT_QUEUE_ENABLE, queue_dev_list, queue_data_length);
if (ret) {
DBG_ERR("report logical LUNs failed\n");
return ret;
}
logdev_data = *logical_dev_list;
if (logdev_data) {
logical_list_length =
BE_32(logdev_data->header.list_length);
} else {
memset(&report_lun_header, 0, sizeof(report_lun_header));
logdev_data =
(reportlun_data_ext_t *)&report_lun_header;
logical_list_length = 0;
}
logdev_data_length = sizeof(reportlun_header_t) +
logical_list_length;
local_logdev_list = os_mem_alloc(softs,
logdev_data_length + sizeof(reportlun_ext_entry_t));
if (!local_logdev_list) {
data_length = *log_data_length;
os_mem_free(softs, (char *)*logical_dev_list, data_length);
*logical_dev_list = NULL;
return PQI_STATUS_FAILURE;
}
memcpy(local_logdev_list, logdev_data, logdev_data_length);
memset((uint8_t *)local_logdev_list + logdev_data_length, 0,
sizeof(reportlun_ext_entry_t));
local_logdev_list->header.list_length = BE_32(logical_list_length +
sizeof(reportlun_ext_entry_t));
data_length = *log_data_length;
os_mem_free(softs, (char *)*logical_dev_list, data_length);
*log_data_length = logdev_data_length + sizeof(reportlun_ext_entry_t);
*logical_dev_list = local_logdev_list;
DBG_FUNC("OUT\n");
return ret;
}
inline boolean_t
pqisrc_is_external_raid_device(pqi_scsi_dev_t const *device)
{
return device->is_external_raid_device;
}
static inline boolean_t
pqisrc_is_external_raid_addr(uint8_t const *scsi3addr)
{
return scsi3addr[2] != 0;
}
static void
pqisrc_assign_btl(pqisrc_softstate_t *softs, pqi_scsi_dev_t *device)
{
uint8_t *scsi3addr;
uint32_t lunid;
uint32_t bus;
uint32_t target;
uint32_t lun;
DBG_FUNC("IN\n");
scsi3addr = device->scsi3addr;
lunid = GET_LE32(scsi3addr);
if (pqisrc_is_hba_lunid(scsi3addr)) {
pqisrc_set_btl(device, PQI_HBA_BUS, PQI_CTLR_INDEX, (lunid & 0x3fff));
device->target_lun_valid = true;
return;
}
if (pqisrc_is_logical_device(device)) {
bus = PQI_RAID_VOLUME_BUS;
lun = (lunid & 0x3fff) + 1;
target = 0;
pqisrc_set_btl(device, bus, target, lun);
device->target_lun_valid = true;
return;
}
DBG_FUNC("OUT\n");
}
int
pqisrc_send_scsi_inquiry(pqisrc_softstate_t *softs,
uint8_t *scsi3addr, uint16_t vpd_page, uint8_t *buff, int buf_len)
{
int ret = PQI_STATUS_SUCCESS;
pqisrc_raid_req_t request;
raid_path_error_info_elem_t error_info;
DBG_FUNC("IN\n");
memset(&request, 0, sizeof(request));
request.data_direction = SOP_DATA_DIR_TO_DEVICE;
request.cmd.cdb[0] = SA_INQUIRY;
if (vpd_page & VPD_PAGE) {
request.cmd.cdb[1] = 0x1;
request.cmd.cdb[2] = (uint8_t)vpd_page;
}
ASSERT(buf_len < 256);
request.cmd.cdb[4] = (uint8_t)buf_len;
if (softs->timeout_in_passthrough) {
request.timeout_in_sec = PQISRC_INQUIRY_TIMEOUT;
}
pqisrc_prepare_send_raid(softs, &request, buff, buf_len, scsi3addr, &error_info);
DBG_FUNC("OUT\n");
return ret;
}
static void pqisrc_get_dev_vol_status(pqisrc_softstate_t *softs,
pqi_scsi_dev_t *device)
{
int ret;
uint8_t status = SA_LV_STATUS_VPD_UNSUPPORTED;
uint8_t vpd_size = sizeof(vpd_volume_status);
uint8_t offline = true;
size_t page_length;
vpd_volume_status *vpd;
DBG_FUNC("IN\n");
vpd = os_mem_alloc(softs, vpd_size);
if (vpd == NULL)
goto out;
ret = pqisrc_send_scsi_inquiry(softs, device->scsi3addr, VPD_PAGE | SA_VPD_LV_STATUS,
(uint8_t *)vpd, vpd_size);
if (ret) {
DBG_WARN("Inquiry returned failed status\n");
goto out;
}
if (vpd->page_code != SA_VPD_LV_STATUS) {
DBG_WARN("Returned invalid buffer\n");
goto out;
}
page_length = offsetof(vpd_volume_status, volume_status) + vpd->page_length;
if (page_length < vpd_size)
goto out;
status = vpd->volume_status;
offline = (vpd->flags & SA_LV_FLAGS_NO_HOST_IO)!=0;
out:
device->volume_offline = offline;
device->volume_status = status;
os_mem_free(softs, (char *)vpd, vpd_size);
DBG_FUNC("OUT\n");
return;
}
static int
pqisrc_raid_map_validation(pqisrc_softstate_t *softs,
pqi_scsi_dev_t const *device, pqisrc_raid_map_t *raid_map)
{
char *error_msg;
uint32_t raidmap_size;
uint32_t r5or6_blocks_per_row;
DBG_FUNC("IN\n");
raidmap_size = LE_32(raid_map->structure_size);
if (raidmap_size < offsetof(pqisrc_raid_map_t, dev_data)) {
error_msg = "RAID map too small\n";
goto error;
}
#if 0
phys_dev_num = LE_16(raid_map->layout_map_count) *
(LE_16(raid_map->data_disks_per_row) +
LE_16(raid_map->metadata_disks_per_row));
#endif
if (device->raid_level == SA_RAID_1) {
if (LE_16(raid_map->layout_map_count) != 2) {
error_msg = "invalid RAID-1 map\n";
goto error;
}
} else if (device->raid_level == SA_RAID_ADM) {
if (LE_16(raid_map->layout_map_count) != 3) {
error_msg = "invalid RAID-1(triple) map\n";
goto error;
}
} else if ((device->raid_level == SA_RAID_5 ||
device->raid_level == SA_RAID_6) &&
LE_16(raid_map->layout_map_count) > 1) {
r5or6_blocks_per_row =
LE_16(raid_map->strip_size) *
LE_16(raid_map->data_disks_per_row);
if (r5or6_blocks_per_row == 0) {
error_msg = "invalid RAID-5 or RAID-6 map\n";
goto error;
}
}
DBG_FUNC("OUT\n");
return 0;
error:
DBG_NOTE("%s\n", error_msg);
return PQI_STATUS_FAILURE;
}
static int
pqisrc_get_device_raidmap(pqisrc_softstate_t *softs, pqi_scsi_dev_t *device)
{
int ret = PQI_STATUS_SUCCESS;
int raidmap_alloc_size = sizeof(pqisrc_raid_map_t);
int raidmap_reported_size;
int structure_size;
int ii;
int *next_offload_to_mirror;
pqisrc_raid_req_t request;
pqisrc_raid_map_t *raid_map;
DBG_FUNC("IN\n");
for (ii = 0; ii < 2; ii++)
{
raid_map = os_mem_alloc(softs, raidmap_alloc_size);
if (!raid_map)
return PQI_STATUS_FAILURE;
memset(&request, 0, sizeof(request));
request.data_direction = SOP_DATA_DIR_TO_DEVICE;
request.cmd.cdb[0] = SA_CISS_READ;
request.cmd.cdb[1] = SA_GET_RAID_MAP;
request.cmd.cdb[8] = (uint8_t)((raidmap_alloc_size) >> 8);
request.cmd.cdb[9] = (uint8_t)(raidmap_alloc_size);
ret = pqisrc_prepare_send_raid(softs, &request, raid_map, raidmap_alloc_size, device->scsi3addr, NULL);
if (ret) {
DBG_ERR("error in build send raid req ret=%d\n", ret);
goto err_out;
}
raidmap_reported_size = LE_32(raid_map->structure_size);
if (raidmap_reported_size <= raidmap_alloc_size)
break;
DBG_NOTE("Raid map is larger than 1024 entries, request once again\n");
os_mem_free(softs, (char*)raid_map, raidmap_alloc_size);
raidmap_alloc_size = raidmap_reported_size;
}
ret = pqisrc_raid_map_validation(softs, device, raid_map);
if (ret) {
DBG_NOTE("error in raid map validation ret=%d\n", ret);
goto err_out;
}
structure_size = raid_map->data_disks_per_row * sizeof(*next_offload_to_mirror);
next_offload_to_mirror = os_mem_alloc(softs, structure_size);
if (!next_offload_to_mirror) {
ret = PQI_STATUS_FAILURE;
goto err_out;
}
device->raid_map = raid_map;
device->offload_to_mirror = next_offload_to_mirror;
DBG_FUNC("OUT\n");
return 0;
err_out:
os_mem_free(softs, (char*)raid_map, sizeof(*raid_map));
DBG_FUNC("FAILED \n");
return ret;
}
static void
pqisrc_get_dev_ioaccel_status(pqisrc_softstate_t *softs,
pqi_scsi_dev_t *device)
{
int ret = PQI_STATUS_SUCCESS;
uint8_t *buff;
uint8_t ioaccel_status;
DBG_FUNC("IN\n");
buff = os_mem_alloc(softs, 64);
if (!buff)
return;
ret = pqisrc_send_scsi_inquiry(softs, device->scsi3addr,
VPD_PAGE | SA_VPD_LV_IOACCEL_STATUS, buff, 64);
if (ret) {
DBG_ERR("error in send scsi inquiry ret=%d\n", ret);
goto err_out;
}
ioaccel_status = buff[IOACCEL_STATUS_BYTE];
device->offload_config =
!!(ioaccel_status & OFFLOAD_CONFIGURED_BIT);
if (device->offload_config) {
device->offload_enabled_pending =
!!(ioaccel_status & OFFLOAD_ENABLED_BIT);
if (pqisrc_get_device_raidmap(softs, device))
device->offload_enabled_pending = false;
}
DBG_DISC("offload_config: 0x%x offload_enabled_pending: 0x%x \n",
device->offload_config, device->offload_enabled_pending);
err_out:
os_mem_free(softs, (char*)buff, 64);
DBG_FUNC("OUT\n");
}
static void
pqisrc_get_dev_raid_level(pqisrc_softstate_t *softs, pqi_scsi_dev_t *device)
{
uint8_t raid_level;
uint8_t *buff;
DBG_FUNC("IN\n");
raid_level = SA_RAID_UNKNOWN;
buff = os_mem_alloc(softs, 64);
if (buff) {
int ret;
ret = pqisrc_send_scsi_inquiry(softs, device->scsi3addr,
VPD_PAGE | SA_VPD_LV_DEVICE_GEOMETRY, buff, 64);
if (ret == 0) {
raid_level = buff[8];
if (raid_level > SA_RAID_MAX)
raid_level = SA_RAID_UNKNOWN;
}
os_mem_free(softs, (char*)buff, 64);
}
device->raid_level = raid_level;
DBG_DISC("RAID LEVEL: %x \n", raid_level);
DBG_FUNC("OUT\n");
}
static int
pqisrc_get_dev_data(pqisrc_softstate_t *softs, pqi_scsi_dev_t *device)
{
int ret = PQI_STATUS_SUCCESS;
uint8_t *inq_buff;
int retry = 3;
DBG_FUNC("IN\n");
inq_buff = os_mem_alloc(softs, OBDR_TAPE_INQ_SIZE);
if (!inq_buff)
return PQI_STATUS_FAILURE;
while(retry--) {
ret = pqisrc_send_scsi_inquiry(softs, device->scsi3addr, 0, inq_buff,
OBDR_TAPE_INQ_SIZE);
if (!ret)
break;
DBG_WARN("Retrying inquiry !!!\n");
}
if(retry <= 0)
goto err_out;
pqisrc_sanitize_inquiry_string(&inq_buff[8], 8);
pqisrc_sanitize_inquiry_string(&inq_buff[16], 16);
device->devtype = inq_buff[0] & 0x1f;
memcpy(device->vendor, &inq_buff[8],
sizeof(device->vendor));
memcpy(device->model, &inq_buff[16],
sizeof(device->model));
DBG_DISC("DEV_TYPE: %x VENDOR: %.8s MODEL: %.16s\n", device->devtype, device->vendor, device->model);
if (pqisrc_is_logical_device(device) && device->devtype == DISK_DEVICE) {
if (pqisrc_is_external_raid_device(device)) {
device->raid_level = SA_RAID_UNKNOWN;
device->volume_status = SA_LV_OK;
device->volume_offline = false;
}
else {
pqisrc_get_dev_raid_level(softs, device);
pqisrc_get_dev_ioaccel_status(softs, device);
pqisrc_get_dev_vol_status(softs, device);
}
}
device->is_obdr_device = (device->devtype == ROM_DEVICE &&
memcmp(&inq_buff[OBDR_SIG_OFFSET], OBDR_TAPE_SIG,
OBDR_SIG_LEN) == 0);
err_out:
os_mem_free(softs, (char*)inq_buff, OBDR_TAPE_INQ_SIZE);
DBG_FUNC("OUT\n");
return ret;
}
static int
pqisrc_identify_ctrl(pqisrc_softstate_t *softs, bmic_ident_ctrl_t *buff)
{
int ret = PQI_STATUS_SUCCESS;
pqisrc_raid_req_t request;
DBG_FUNC("IN\n");
memset(&request, 0, sizeof(request));
request.data_direction = SOP_DATA_DIR_TO_DEVICE;
request.cmd.bmic_cdb.op_code = BMIC_READ;
request.cmd.bmic_cdb.cmd = BMIC_IDENTIFY_CONTROLLER;
request.cmd.bmic_cdb.xfer_len = BE_16(sizeof(*buff));
ret = pqisrc_prepare_send_ctrlr_request(softs, &request, buff, sizeof(*buff));
DBG_FUNC("OUT\n");
return ret;
}
int
pqisrc_get_ctrl_fw_version(pqisrc_softstate_t *softs)
{
int ret = PQI_STATUS_SUCCESS;
bmic_ident_ctrl_t *identify_ctrl;
DBG_FUNC("IN\n");
identify_ctrl = os_mem_alloc(softs, sizeof(*identify_ctrl));
if (!identify_ctrl) {
DBG_ERR("failed to allocate memory for identify_ctrl\n");
return PQI_STATUS_FAILURE;
}
memset(identify_ctrl, 0, sizeof(*identify_ctrl));
ret = pqisrc_identify_ctrl(softs, identify_ctrl);
if (ret)
goto out;
softs->fw_build_number = identify_ctrl->fw_build_number;
memcpy(softs->fw_version, identify_ctrl->fw_version,
sizeof(identify_ctrl->fw_version));
softs->fw_version[sizeof(identify_ctrl->fw_version)] = '\0';
snprintf(softs->fw_version +
strlen(softs->fw_version),
sizeof(softs->fw_version),
"-%u", identify_ctrl->fw_build_number);
out:
os_mem_free(softs, (char *)identify_ctrl, sizeof(*identify_ctrl));
DBG_NOTE("Firmware version: %s Firmware build number: %d\n", softs->fw_version, softs->fw_build_number);
DBG_FUNC("OUT\n");
return ret;
}
static int
pqisrc_identify_physical_disk(pqisrc_softstate_t *softs,
pqi_scsi_dev_t *device,
bmic_ident_physdev_t *buff,
int buf_len)
{
int ret = PQI_STATUS_SUCCESS;
uint16_t bmic_device_index;
pqisrc_raid_req_t request;
DBG_FUNC("IN\n");
memset(&request, 0, sizeof(request));
bmic_device_index = BMIC_GET_DRIVE_NUMBER(device->scsi3addr);
request.data_direction = SOP_DATA_DIR_TO_DEVICE;
request.cmd.bmic_cdb.op_code = BMIC_READ;
request.cmd.bmic_cdb.cmd = BMIC_IDENTIFY_PHYSICAL_DEVICE;
request.cmd.bmic_cdb.xfer_len = BE_16(buf_len);
request.cmd.cdb[2] = (uint8_t)bmic_device_index;
request.cmd.cdb[9] = (uint8_t)(bmic_device_index >> 8);
ret = pqisrc_prepare_send_ctrlr_request(softs, &request, buff, buf_len);
DBG_FUNC("OUT\n");
return ret;
}
static void
pqisrc_get_physical_device_info(pqisrc_softstate_t *softs,
pqi_scsi_dev_t *device,
bmic_ident_physdev_t *id_phys)
{
int ret = PQI_STATUS_SUCCESS;
DBG_FUNC("IN\n");
memset(id_phys, 0, sizeof(*id_phys));
ret= pqisrc_identify_physical_disk(softs, device,
id_phys, sizeof(*id_phys));
if (ret) {
device->queue_depth = PQI_PHYSICAL_DISK_DEFAULT_MAX_QUEUE_DEPTH;
return;
}
device->queue_depth =
LE_16(id_phys->current_queue_depth_limit);
device->device_type = id_phys->device_type;
device->active_path_index = id_phys->active_path_number;
device->path_map = id_phys->redundant_path_present_map;
memcpy(&device->box,
&id_phys->alternate_paths_phys_box_on_port,
sizeof(device->box));
memcpy(&device->phys_connector,
&id_phys->alternate_paths_phys_connector,
sizeof(device->phys_connector));
device->bay = id_phys->phys_bay_in_box;
if (id_phys->multi_lun_device_lun_count) {
device->is_multi_lun = true;
}
DBG_DISC("BMIC DEV_TYPE: %x QUEUE DEPTH: 0x%x \n", device->device_type, device->queue_depth);
DBG_FUNC("OUT\n");
}
static device_status_t
pqisrc_scsi_find_entry(pqisrc_softstate_t *softs,
pqi_scsi_dev_t const *device_to_find, pqi_scsi_dev_t **same_device)
{
pqi_scsi_dev_t *device;
int i;
DBG_FUNC("IN\n");
for(i = 0; i < PQI_MAX_DEVICES; i++) {
device = softs->dev_list[i];
if(device == NULL)
continue;
if (pqisrc_scsi3addr_equal(device_to_find->scsi3addr,
device->scsi3addr)) {
*same_device = device;
if (device->in_remove == true)
return DEVICE_IN_REMOVE;
if (pqisrc_device_equal(device_to_find, device)) {
if (device_to_find->volume_offline)
return DEVICE_CHANGED;
return DEVICE_UNCHANGED;
}
return DEVICE_CHANGED;
}
}
DBG_FUNC("OUT\n");
return DEVICE_NOT_FOUND;
}
static void
pqisrc_exist_device_update(pqisrc_softstate_t const *softs,
pqi_scsi_dev_t *device_exist, pqi_scsi_dev_t *new_device)
{
DBG_FUNC("IN\n");
device_exist->expose_device = new_device->expose_device;
memcpy(device_exist->vendor, new_device->vendor,
sizeof(device_exist->vendor));
memcpy(device_exist->model, new_device->model,
sizeof(device_exist->model));
device_exist->is_physical_device = new_device->is_physical_device;
device_exist->is_external_raid_device =
new_device->is_external_raid_device;
if ((softs->ld_rescan) && (pqisrc_is_logical_device(device_exist))) {
device_exist->scsi_rescan = true;
}
device_exist->sas_address = new_device->sas_address;
device_exist->raid_level = new_device->raid_level;
device_exist->queue_depth = new_device->queue_depth;
device_exist->ioaccel_handle = new_device->ioaccel_handle;
device_exist->volume_status = new_device->volume_status;
device_exist->active_path_index = new_device->active_path_index;
device_exist->path_map = new_device->path_map;
device_exist->bay = new_device->bay;
memcpy(device_exist->box, new_device->box,
sizeof(device_exist->box));
memcpy(device_exist->phys_connector, new_device->phys_connector,
sizeof(device_exist->phys_connector));
device_exist->offload_config = new_device->offload_config;
device_exist->offload_enabled_pending =
new_device->offload_enabled_pending;
if (device_exist->offload_to_mirror) {
device_exist->temp_offload_to_mirror = device_exist->offload_to_mirror;
}
device_exist->offload_to_mirror = new_device->offload_to_mirror;
if (device_exist->raid_map) {
device_exist->temp_raid_map = device_exist->raid_map;
}
device_exist->raid_map = new_device->raid_map;
new_device->raid_map = NULL;
new_device->offload_to_mirror = NULL;
DBG_FUNC("OUT\n");
}
static int
pqisrc_add_device(pqisrc_softstate_t *softs, pqi_scsi_dev_t *device)
{
DBG_FUNC("IN\n");
DBG_NOTE("vendor: %s model: %s B%d:T%d:L%d is_physical_device:0x%x expose_device:0x%x volume_offline 0x%x volume_status 0x%x \n",
device->vendor, device->model, device->bus, device->target, device->lun, device->is_physical_device, device->expose_device, device->volume_offline, device->volume_status);
device->invalid = false;
device->schedule_rescan = false;
device->softs = softs;
device->in_remove = false;
if(device->expose_device) {
pqisrc_init_device_active_io(softs, device);
device_printf(softs->os_specific.pqi_dev,
"device added: vendor=%s model=%s B%d:T%d:L%d type=%s\n",
device->vendor, device->model,
device->bus, device->target, device->lun,
device->is_physical_device ? "physical" : "logical");
os_add_device(softs,device);
}
DBG_FUNC("OUT\n");
return PQI_STATUS_SUCCESS;
}
void
pqisrc_remove_device(pqisrc_softstate_t *softs, pqi_scsi_dev_t *device)
{
DBG_FUNC("IN\n");
DBG_NOTE("vendor: %s model: %s B%d:T%d:L%d is_physical_device:0x%x expose_device:0x%x volume_offline 0x%x volume_status 0x%x \n",
device->vendor, device->model, device->bus, device->target, device->lun, device->is_physical_device, device->expose_device, device->volume_offline, device->volume_status);
device->invalid = true;
if (device->expose_device == false) {
DBG_NOTE("Deallocated Masked Device Resources.\n");
pqisrc_free_device(softs,device);
return;
}
pqisrc_wait_for_device_commands_to_complete(softs, device);
device_printf(softs->os_specific.pqi_dev,
"device removed: vendor=%s model=%s B%d:T%d:L%d type=%s\n",
device->vendor, device->model,
device->bus, device->target, device->lun,
device->is_physical_device ? "physical" : "logical");
os_remove_device(softs,device);
DBG_FUNC("OUT\n");
}
static void
pqisrc_display_volume_status(pqisrc_softstate_t *softs, pqi_scsi_dev_t const *device)
{
char *status;
DBG_FUNC("IN\n");
switch (device->volume_status) {
case SA_LV_OK:
status = "Volume is online.";
break;
case SA_LV_UNDERGOING_ERASE:
status = "Volume is undergoing background erase process.";
break;
case SA_LV_NOT_AVAILABLE:
status = "Volume is waiting for transforming volume.";
break;
case SA_LV_UNDERGOING_RPI:
status = "Volume is undergoing rapid parity initialization process.";
break;
case SA_LV_PENDING_RPI:
status = "Volume is queued for rapid parity initialization process.";
break;
case SA_LV_ENCRYPTED_NO_KEY:
status = "Volume is encrypted and cannot be accessed because key is not present.";
break;
case SA_LV_PLAINTEXT_IN_ENCRYPT_ONLY_CONTROLLER:
status = "Volume is not encrypted and cannot be accessed because controller is in encryption-only mode.";
break;
case SA_LV_UNDERGOING_ENCRYPTION:
status = "Volume is undergoing encryption process.";
break;
case SA_LV_UNDERGOING_ENCRYPTION_REKEYING:
status = "Volume is undergoing encryption re-keying process.";
break;
case SA_LV_ENCRYPTED_IN_NON_ENCRYPTED_CONTROLLER:
status = "Volume is encrypted and cannot be accessed because controller does not have encryption enabled.";
break;
case SA_LV_PENDING_ENCRYPTION:
status = "Volume is pending migration to encrypted state, but process has not started.";
break;
case SA_LV_PENDING_ENCRYPTION_REKEYING:
status = "Volume is encrypted and is pending encryption rekeying.";
break;
case SA_LV_STATUS_VPD_UNSUPPORTED:
status = "Volume status is not available through vital product data pages.";
break;
case SA_LV_UNDERGOING_EXPANSION:
status = "Volume undergoing expansion";
break;
case SA_LV_QUEUED_FOR_EXPANSION:
status = "Volume queued for expansion";
break;
case SA_LV_EJECTED:
status = "Volume ejected";
break;
case SA_LV_WRONG_PHYSICAL_DRIVE_REPLACED:
status = "Volume has wrong physical drive replaced";
break;
case SA_LV_DISABLED_SCSI_ID_CONFLICT:
status = "Volume disabled scsi id conflict";
break;
case SA_LV_HARDWARE_HAS_OVERHEATED:
status = "Volume hardware has over heated";
break;
case SA_LV_HARDWARE_OVERHEATING:
status = "Volume hardware over heating";
break;
case SA_LV_PHYSICAL_DRIVE_CONNECTION_PROBLEM:
status = "Volume physical drive connection problem";
break;
default:
status = "Volume is in an unknown state.";
break;
}
DBG_NOTE("scsi B%d:T%d:L%d %s\n",
device->bus, device->target, device->lun, status);
DBG_FUNC("OUT\n");
}
void
pqisrc_device_mem_free(pqisrc_softstate_t *softs, pqi_scsi_dev_t *device)
{
DBG_FUNC("IN\n");
if (!device)
return;
if (device->raid_map) {
os_mem_free(softs, (char *)device->raid_map, sizeof(pqisrc_raid_map_t));
}
if (device->offload_to_mirror) {
os_mem_free(softs, (int *)device->offload_to_mirror, sizeof(*(device->offload_to_mirror)));
}
os_mem_free(softs, (char *)device,sizeof(*device));
DBG_FUNC("OUT\n");
}
void
pqisrc_free_device(pqisrc_softstate_t * softs, pqi_scsi_dev_t *device)
{
rcb_t *rcb;
uint8_t *scsi3addr;
int i, index;
pqi_scsi_dev_t *temp_device;
unsigned char addr1[8], addr2[8];
for(i = 1; i <= softs->max_outstanding_io; i++)
{
rcb = &softs->rcb[i];
if(rcb->dvp == device) {
DBG_WARN("Pending requests for the removing device\n");
rcb->dvp = NULL;
}
}
index = pqisrc_find_btl_list_index(softs, device->bus, device->target, device->lun);
OS_ACQUIRE_SPINLOCK(&softs->devlist_lock);
scsi3addr = device->scsi3addr;
if (!pqisrc_is_logical_device(device) && !MASKED_DEVICE(scsi3addr)) {
DBG_NOTE("Giving back target %i \n", device->target);
pqisrc_remove_target_bit(softs, device->target);
}
if (pqisrc_is_external_raid_device(device)) {
memcpy(addr1, device->scsi3addr, 8);
for(i = 0; i < PQI_MAX_DEVICES; i++) {
if(softs->dev_list[i] == NULL)
continue;
temp_device = softs->dev_list[i];
memcpy(addr2, temp_device->scsi3addr, 8);
if(memcmp(addr1, addr2, 8) == 0) {
continue;
}
if (addr1[2] == addr2[2]) {
break;
}
}
if(i == PQI_MAX_DEVICES) {
pqisrc_remove_target_bit(softs, device->target);
}
}
if (index >= 0 && index < PQI_MAX_DEVICES)
softs->dev_list[index] = NULL;
if (device->expose_device == true){
pqisrc_delete_softs_entry(softs, device);
DBG_NOTE("Removed memory for device: B%d:T%d:L%d\n",
device->bus, device->target, device->lun);
OS_RELEASE_SPINLOCK(&softs->devlist_lock);
pqisrc_device_mem_free(softs, device);
} else {
OS_RELEASE_SPINLOCK(&softs->devlist_lock);
}
}
static void
pqisrc_update_device_list(pqisrc_softstate_t *softs,
pqi_scsi_dev_t *new_device_list[], int num_new_devices)
{
int i;
device_status_t dev_status;
pqi_scsi_dev_t *device;
pqi_scsi_dev_t *same_device;
pqi_scsi_dev_t **added = NULL;
pqi_scsi_dev_t **removed = NULL;
int nadded = 0, nremoved = 0;
uint8_t *scsi3addr;
DBG_FUNC("IN\n");
added = os_mem_alloc(softs, sizeof(*added) * PQI_MAX_DEVICES);
removed = os_mem_alloc(softs, sizeof(*removed) * PQI_MAX_DEVICES);
if (!added || !removed) {
DBG_WARN("Out of memory \n");
goto free_and_out;
}
OS_ACQUIRE_SPINLOCK(&softs->devlist_lock);
for(i = 0; i < PQI_MAX_DEVICES; i++) {
if(softs->dev_list[i] == NULL)
continue;
device = softs->dev_list[i];
device->device_gone = true;
}
DBG_IO("Device list used an array\n");
for (i = 0; i < num_new_devices; i++) {
device = new_device_list[i];
dev_status = pqisrc_scsi_find_entry(softs, device,
&same_device);
switch (dev_status) {
case DEVICE_UNCHANGED:
device->new_device = false;
same_device->device_gone = false;
pqisrc_exist_device_update(softs, same_device, device);
break;
case DEVICE_NOT_FOUND:
device->new_device = true;
DBG_DISC("new device found B%d:T%d:L%d\n",
device->bus, device->target, device->lun);
break;
case DEVICE_CHANGED:
device->new_device = true;
DBG_DISC("device changed B%d:T%d:L%d\n",
device->bus, device->target, device->lun);
break;
case DEVICE_IN_REMOVE:
device->new_device = false;
same_device->schedule_rescan = true;
DBG_DISC("device in removal B%d:T%d:L%d, scheduling rescan\n",
device->bus, device->target, device->lun);
break;
default:
break;
}
}
for(i = 0; i < PQI_MAX_DEVICES; i++) {
device = softs->dev_list[i];
if(device == NULL)
continue;
if (device->device_gone) {
if(device->in_remove == true) {
continue;
}
device->in_remove = true;
removed[nremoved] = device;
softs->num_devs--;
nremoved++;
}
}
for (i = 0, nadded = 0; i < num_new_devices; i++) {
device = new_device_list[i];
if (!device->new_device)
continue;
if (device->volume_offline)
continue;
scsi3addr = device->scsi3addr;
if (device->expose_device || !MASKED_DEVICE(scsi3addr)){
if(pqisrc_add_softs_entry(softs, device, scsi3addr)){
new_device_list[i] = NULL;
added[nadded] = device;
nadded++;
}
}
}
for(i = 0; i < PQI_MAX_DEVICES; i++) {
device = softs->dev_list[i];
if(device == NULL)
continue;
if (device->offload_enabled != device->offload_enabled_pending)
{
DBG_NOTE("[B%d:T%d:L%d]Changing AIO to %d (was %d)\n",
device->bus, device->target, device->lun,
device->offload_enabled_pending,
device->offload_enabled);
}
device->offload_enabled = device->offload_enabled_pending;
}
OS_RELEASE_SPINLOCK(&softs->devlist_lock);
for(i = 0; i < nremoved; i++) {
device = removed[i];
if (device == NULL)
continue;
pqisrc_display_device_info(softs, "removed", device);
pqisrc_remove_device(softs, device);
}
OS_ACQUIRE_SPINLOCK(&softs->devlist_lock);
for(i = 0; i < PQI_MAX_DEVICES; i++) {
if(softs->dev_list[i] == NULL)
continue;
device = softs->dev_list[i];
if (device->in_remove)
continue;
if (pqisrc_is_logical_device(device) &&
device->firmware_queue_depth_set == false)
device->queue_depth = PQI_LOGICAL_DISK_DEFAULT_MAX_QUEUE_DEPTH;
}
OS_RELEASE_SPINLOCK(&softs->devlist_lock);
for(i = 0; i < nadded; i++) {
device = added[i];
if (device->expose_device) {
pqisrc_add_device(softs, device);
}
pqisrc_display_device_info(softs, "added", device);
}
for (i = 0; i < num_new_devices; i++) {
device = new_device_list[i];
if (!device)
continue;
if (!device->new_device)
continue;
if (device->volume_offline) {
pqisrc_display_volume_status(softs, device);
pqisrc_display_device_info(softs, "offline", device);
}
}
for (i = 0; i < PQI_MAX_DEVICES; i++) {
device = softs->dev_list[i];
if(device == NULL)
continue;
DBG_DISC("Current device %d : B%d:T%d:L%d\n",
i, device->bus, device->target,
device->lun);
if (device->scsi_rescan) {
os_rescan_target(softs, device);
}
if (device->temp_offload_to_mirror) {
os_mem_free(softs,
(int *) device->temp_offload_to_mirror,
sizeof(*(device->temp_offload_to_mirror)));
}
if (device->temp_raid_map) {
os_mem_free(softs,
(int *) device->temp_raid_map,
sizeof(*(device->temp_raid_map)));
}
}
softs->ld_rescan = false;
free_and_out:
if (added)
os_mem_free(softs, (char *)added,
sizeof(*added) * PQI_MAX_DEVICES);
if (removed)
os_mem_free(softs, (char *)removed,
sizeof(*removed) * PQI_MAX_DEVICES);
DBG_FUNC("OUT\n");
}
int
pqisrc_write_driver_version_to_host_wellness(pqisrc_softstate_t *softs)
{
int rval = PQI_STATUS_SUCCESS;
struct bmic_host_wellness_driver_version *host_wellness_driver_ver;
size_t data_length;
pqisrc_raid_req_t request;
DBG_FUNC("IN\n");
memset(&request, 0, sizeof(request));
data_length = sizeof(*host_wellness_driver_ver);
host_wellness_driver_ver = os_mem_alloc(softs, data_length);
if (!host_wellness_driver_ver) {
DBG_ERR("failed to allocate memory for host wellness driver_version\n");
return PQI_STATUS_FAILURE;
}
host_wellness_driver_ver->start_tag[0] = '<';
host_wellness_driver_ver->start_tag[1] = 'H';
host_wellness_driver_ver->start_tag[2] = 'W';
host_wellness_driver_ver->start_tag[3] = '>';
host_wellness_driver_ver->driver_version_tag[0] = 'D';
host_wellness_driver_ver->driver_version_tag[1] = 'V';
host_wellness_driver_ver->driver_version_length = LE_16(sizeof(host_wellness_driver_ver->driver_version));
strncpy(host_wellness_driver_ver->driver_version, softs->os_name,
sizeof(host_wellness_driver_ver->driver_version));
if (strlen(softs->os_name) < sizeof(host_wellness_driver_ver->driver_version) ) {
strncpy(host_wellness_driver_ver->driver_version + strlen(softs->os_name), PQISRC_DRIVER_VERSION,
sizeof(host_wellness_driver_ver->driver_version) - strlen(softs->os_name));
} else {
DBG_DISC("OS name length(%u) is longer than buffer of driver_version\n",
(unsigned int)strlen(softs->os_name));
}
host_wellness_driver_ver->driver_version[sizeof(host_wellness_driver_ver->driver_version) - 1] = '\0';
host_wellness_driver_ver->dont_write_tag[0] = 'D';
host_wellness_driver_ver->dont_write_tag[1] = 'W';
host_wellness_driver_ver->end_tag[0] = 'Z';
host_wellness_driver_ver->end_tag[1] = 'Z';
request.data_direction = SOP_DATA_DIR_FROM_DEVICE;
request.cmd.bmic_cdb.op_code = BMIC_WRITE;
request.cmd.bmic_cdb.cmd = BMIC_WRITE_HOST_WELLNESS;
request.cmd.bmic_cdb.xfer_len = BE_16(data_length);
rval = pqisrc_prepare_send_ctrlr_request(softs, &request, host_wellness_driver_ver, data_length);
os_mem_free(softs, (char *)host_wellness_driver_ver, data_length);
DBG_FUNC("OUT\n");
return rval;
}
int
pqisrc_write_current_time_to_host_wellness(pqisrc_softstate_t *softs)
{
int rval = PQI_STATUS_SUCCESS;
struct bmic_host_wellness_time *host_wellness_time;
size_t data_length;
pqisrc_raid_req_t request;
DBG_FUNC("IN\n");
memset(&request, 0, sizeof(request));
data_length = sizeof(*host_wellness_time);
host_wellness_time = os_mem_alloc(softs, data_length);
if (!host_wellness_time) {
DBG_ERR("failed to allocate memory for host wellness time structure\n");
return PQI_STATUS_FAILURE;
}
host_wellness_time->start_tag[0] = '<';
host_wellness_time->start_tag[1] = 'H';
host_wellness_time->start_tag[2] = 'W';
host_wellness_time->start_tag[3] = '>';
host_wellness_time->time_tag[0] = 'T';
host_wellness_time->time_tag[1] = 'D';
host_wellness_time->time_length = LE_16(offsetof(struct bmic_host_wellness_time, dont_write_tag) -
offsetof(struct bmic_host_wellness_time, hour));
os_get_time(host_wellness_time);
host_wellness_time->dont_write_tag[0] = 'D';
host_wellness_time->dont_write_tag[1] = 'W';
host_wellness_time->end_tag[0] = 'Z';
host_wellness_time->end_tag[1] = 'Z';
request.data_direction = SOP_DATA_DIR_FROM_DEVICE;
request.cmd.bmic_cdb.op_code = BMIC_WRITE;
request.cmd.bmic_cdb.cmd = BMIC_WRITE_HOST_WELLNESS;
request.cmd.bmic_cdb.xfer_len = BE_16(data_length);
rval = pqisrc_prepare_send_ctrlr_request(softs, &request, host_wellness_time, data_length);
os_mem_free(softs, (char *)host_wellness_time, data_length);
DBG_FUNC("OUT\n");
return rval;
}
static void
pqisrc_get_device_vpd_info(pqisrc_softstate_t *softs,
bmic_ident_physdev_t const *bmic_phy_info,pqi_scsi_dev_t *device)
{
DBG_FUNC("IN\n");
memcpy(&device->wwid, &bmic_phy_info->padding[79], sizeof(device->wwid));
DBG_FUNC("OUT\n");
}
int
pqisrc_scan_devices(pqisrc_softstate_t *softs)
{
boolean_t is_physical_device;
int ret;
int i;
int new_dev_cnt;
int phy_log_dev_cnt;
size_t queue_log_data_length;
uint8_t *scsi3addr;
uint8_t multiplier;
uint16_t qdepth;
uint32_t physical_cnt;
uint32_t logical_cnt;
uint32_t logical_queue_cnt;
uint32_t ndev_allocated = 0;
size_t phys_data_length, log_data_length;
reportlun_data_ext_t *physical_dev_list = NULL;
reportlun_data_ext_t *logical_dev_list = NULL;
reportlun_ext_entry_t *lun_ext_entry = NULL;
reportlun_queue_depth_data_t *logical_queue_dev_list = NULL;
bmic_ident_physdev_t *bmic_phy_info = NULL;
pqi_scsi_dev_t **new_device_list = NULL;
pqi_scsi_dev_t *device = NULL;
#ifdef PQI_NEED_RESCAN_TIMER_FOR_RBOD_HOTPLUG
int num_ext_raid_devices = 0;
#endif
DBG_FUNC("IN\n");
ret = pqisrc_get_phys_log_device_list(softs, &physical_dev_list, &logical_dev_list,
&logical_queue_dev_list, &queue_log_data_length,
&phys_data_length, &log_data_length);
if (ret)
goto err_out;
physical_cnt = BE_32(physical_dev_list->header.list_length)
/ sizeof(physical_dev_list->lun_entries[0]);
logical_cnt = BE_32(logical_dev_list->header.list_length)
/ sizeof(logical_dev_list->lun_entries[0]);
logical_queue_cnt = BE_32(logical_queue_dev_list->header.list_length)
/ sizeof(logical_queue_dev_list->lun_entries[0]);
DBG_DISC("physical_cnt %u logical_cnt %u queue_cnt %u\n", physical_cnt, logical_cnt, logical_queue_cnt);
if (physical_cnt) {
bmic_phy_info = os_mem_alloc(softs, sizeof(*bmic_phy_info));
if (bmic_phy_info == NULL) {
ret = PQI_STATUS_FAILURE;
DBG_ERR("failed to allocate memory for BMIC ID PHYS Device : %d\n", ret);
goto err_out;
}
}
phy_log_dev_cnt = physical_cnt + logical_cnt;
new_device_list = os_mem_alloc(softs,
sizeof(*new_device_list) * phy_log_dev_cnt);
if (new_device_list == NULL) {
ret = PQI_STATUS_FAILURE;
DBG_ERR("failed to allocate memory for device list : %d\n", ret);
goto err_out;
}
for (i = 0; i < phy_log_dev_cnt; i++) {
new_device_list[i] = os_mem_alloc(softs,
sizeof(*new_device_list[i]));
if (new_device_list[i] == NULL) {
ret = PQI_STATUS_FAILURE;
DBG_ERR("failed to allocate memory for device list : %d\n", ret);
ndev_allocated = i;
goto err_out;
}
}
ndev_allocated = phy_log_dev_cnt;
new_dev_cnt = 0;
for (i = 0; i < phy_log_dev_cnt; i++) {
if (i < physical_cnt) {
is_physical_device = true;
lun_ext_entry = &physical_dev_list->lun_entries[i];
} else {
is_physical_device = false;
lun_ext_entry =
&logical_dev_list->lun_entries[i - physical_cnt];
}
scsi3addr = lun_ext_entry->lunid;
if(lun_ext_entry->device_type == CONTROLLER_DEVICE) {
#ifdef PQI_NEED_RESCAN_TIMER_FOR_RBOD_HOTPLUG
num_ext_raid_devices++;
#endif
int target = lun_ext_entry->lunid[3] & 0x3f;
softs->target_sas_addr[target] = BE_64(lun_ext_entry->wwid);
}
if (MASKED_DEVICE(scsi3addr) && is_physical_device
&& (lun_ext_entry->ioaccel_handle == 0))
continue;
device = new_device_list[new_dev_cnt];
memset(device, 0, sizeof(*device));
memcpy(device->scsi3addr, scsi3addr, sizeof(device->scsi3addr));
device->wwid = lun_ext_entry->wwid;
device->is_physical_device = is_physical_device;
if (!is_physical_device && logical_queue_cnt--) {
device->is_external_raid_device =
pqisrc_is_external_raid_addr(scsi3addr);
multiplier = logical_queue_dev_list->lun_entries[i - physical_cnt].multiplier;
qdepth = logical_queue_dev_list->lun_entries[i - physical_cnt].queue_depth;
if (multiplier) {
device->firmware_queue_depth_set = true;
device->queue_depth = qdepth*256;
} else {
device->firmware_queue_depth_set = true;
device->queue_depth = qdepth*16;
}
if (device->queue_depth > softs->adapterQDepth) {
device->firmware_queue_depth_set = true;
device->queue_depth = softs->adapterQDepth;
}
if ((multiplier == 1) &&
(qdepth >= MAX_RAW_M256_QDEPTH))
device->firmware_queue_depth_set = false;
if ((multiplier == 0) &&
(qdepth >= MAX_RAW_M16_QDEPTH))
device->firmware_queue_depth_set = false;
}
ret = pqisrc_get_dev_data(softs, device);
if (ret) {
DBG_WARN("Inquiry failed, skipping device %016llx\n",
(unsigned long long)BE_64(device->scsi3addr[0]));
DBG_DISC("INQUIRY FAILED \n");
continue;
}
if (device->devtype == RAID_DEVICE) {
device->firmware_queue_depth_set = true;
device->queue_depth = softs->adapterQDepth;
}
pqisrc_assign_btl(softs, device);
if (device->is_physical_device &&
MASKED_DEVICE(scsi3addr))
device->expose_device = false;
else
device->expose_device = true;
if (device->is_physical_device &&
(lun_ext_entry->device_flags &
REPORT_LUN_DEV_FLAG_AIO_ENABLED) &&
lun_ext_entry->ioaccel_handle) {
device->aio_enabled = true;
}
switch (device->devtype) {
case ROM_DEVICE:
if (device->is_obdr_device)
new_dev_cnt++;
break;
case DISK_DEVICE:
case ZBC_DEVICE:
if (device->is_physical_device) {
device->ioaccel_handle =
lun_ext_entry->ioaccel_handle;
pqisrc_get_physical_device_info(softs, device,
bmic_phy_info);
if ( (!softs->page83id_in_rpl) && (bmic_phy_info->device_type == BMIC_DEVICE_TYPE_SATA)) {
pqisrc_get_device_vpd_info(softs, bmic_phy_info, device);
}
device->sas_address = BE_64(device->wwid);
}
new_dev_cnt++;
break;
case ENCLOSURE_DEVICE:
if (device->is_physical_device) {
device->sas_address = BE_64(lun_ext_entry->wwid);
}
new_dev_cnt++;
break;
case TAPE_DEVICE:
case MEDIUM_CHANGER_DEVICE:
new_dev_cnt++;
break;
case RAID_DEVICE:
if (pqisrc_is_hba_lunid(scsi3addr))
new_dev_cnt++;
break;
case SES_DEVICE:
case CONTROLLER_DEVICE:
default:
break;
}
}
DBG_DISC("new_dev_cnt %d\n", new_dev_cnt);
#ifdef PQI_NEED_RESCAN_TIMER_FOR_RBOD_HOTPLUG
if(num_ext_raid_devices)
os_start_rescan_timer(softs);
else
os_stop_rescan_timer(softs);
#endif
pqisrc_update_device_list(softs, new_device_list, new_dev_cnt);
err_out:
if (new_device_list) {
for (i = 0; i < ndev_allocated; i++) {
if (new_device_list[i]) {
if(new_device_list[i]->raid_map)
os_mem_free(softs, (char *)new_device_list[i]->raid_map,
sizeof(pqisrc_raid_map_t));
os_mem_free(softs, (char*)new_device_list[i],
sizeof(*new_device_list[i]));
}
}
os_mem_free(softs, (char *)new_device_list,
sizeof(*new_device_list) * ndev_allocated);
}
if(physical_dev_list)
os_mem_free(softs, (char *)physical_dev_list, phys_data_length);
if(logical_dev_list)
os_mem_free(softs, (char *)logical_dev_list, log_data_length);
if(logical_queue_dev_list)
os_mem_free(softs, (char*)logical_queue_dev_list,
queue_log_data_length);
if (bmic_phy_info)
os_mem_free(softs, (char *)bmic_phy_info, sizeof(*bmic_phy_info));
DBG_FUNC("OUT \n");
return ret;
}
void
pqisrc_cleanup_devices(pqisrc_softstate_t *softs)
{
int i = 0;
pqi_scsi_dev_t *device = NULL;
DBG_FUNC("IN\n");
for(i = 0; i < PQI_MAX_DEVICES; i++) {
if(softs->dev_list[i] == NULL)
continue;
device = softs->dev_list[i];
pqisrc_device_mem_free(softs, device);
}
DBG_FUNC("OUT\n");
}