/* * Data structures to support multiple adapters by the LLD. * pmcraid_adapter_count - count of configured adapters
*/ static atomic_t pmcraid_adapter_count = ATOMIC_INIT(0);
/* * Supporting user-level control interface through IOCTL commands. * pmcraid_major - major number to use * pmcraid_minor - minor number(s) to use
*/ staticunsignedint pmcraid_major; staticconststructclass pmcraid_class = {
.name = PMCRAID_DEVFILE,
}; static DECLARE_BITMAP(pmcraid_minor, PMCRAID_MAX_ADAPTERS);
/** * pmcraid_sdev_init - Prepare for commands to a device * @scsi_dev: scsi device struct * * This function is called by mid-layer prior to sending any command to the new * device. Stores resource entry details of the device in scsi_device struct. * Queuecommand uses the resource handle and other details to fill up IOARCB * while sending commands to the device. * * Return value: * 0 on success / -ENXIO if device does not exist
*/ staticint pmcraid_sdev_init(struct scsi_device *scsi_dev)
{ struct pmcraid_resource_entry *temp, *res = NULL; struct pmcraid_instance *pinstance;
u8 target, bus, lun; unsignedlong lock_flags; int rc = -ENXIO;
u16 fw_version;
/* Driver exposes VSET and GSCSI resources only; all other device types * are not exposed. Resource list is synchronized using resource lock * so any traversal or modifications to the list should be done inside * this lock
*/
spin_lock_irqsave(&pinstance->resource_lock, lock_flags);
list_for_each_entry(temp, &pinstance->used_res_q, queue) {
/* do not expose VSETs with order-ids > MAX_VSET_TARGETS */ if (RES_IS_VSET(temp->cfg_entry)) { if (fw_version <= PMCRAID_FW_VERSION_1)
target = temp->cfg_entry.unique_flags1; else
target = le16_to_cpu(temp->cfg_entry.array_id) & 0xFF;
if (target > PMCRAID_MAX_VSET_TARGETS) continue;
bus = PMCRAID_VSET_BUS_ID;
lun = 0;
} elseif (RES_IS_GSCSI(temp->cfg_entry)) {
target = RES_TARGET(temp->cfg_entry.resource_address);
bus = PMCRAID_PHYS_BUS_ID;
lun = RES_LUN(temp->cfg_entry.resource_address);
} else { continue;
}
if (bus == scsi_dev->channel &&
target == scsi_dev->id &&
lun == scsi_dev->lun) {
res = temp; break;
}
}
/** * pmcraid_sdev_configure - Configures a SCSI device * @scsi_dev: scsi device struct * @lim: queue limits * * This function is executed by SCSI mid layer just after a device is first * scanned (i.e. it has responded to an INQUIRY). For VSET resources, the * timeout value (default 30s) will be over-written to a higher value (60s) * and max_sectors value will be over-written to 512. It also sets queue depth * to host->cmd_per_lun value * * Return value: * 0 on success
*/ staticint pmcraid_sdev_configure(struct scsi_device *scsi_dev, struct queue_limits *lim)
{ struct pmcraid_resource_entry *res = scsi_dev->hostdata;
if (!res) return 0;
/* LLD exposes VSETs and Enclosure devices only */ if (RES_IS_GSCSI(res->cfg_entry) &&
scsi_dev->type != TYPE_ENCLOSURE) return -ENXIO;
/* * We never want to report TCQ support for these types of devices.
*/ if (!RES_IS_GSCSI(res->cfg_entry) && !RES_IS_VSET(res->cfg_entry))
scsi_dev->tagged_supported = 0;
return 0;
}
/** * pmcraid_sdev_destroy - Unconfigure a SCSI device before removing it * * @scsi_dev: scsi device struct * * This is called by mid-layer before removing a device. Pointer assignments * done in pmcraid_sdev_init will be reset to NULL here. * * Return value * none
*/ staticvoid pmcraid_sdev_destroy(struct scsi_device *scsi_dev)
{ struct pmcraid_resource_entry *res;
res = (struct pmcraid_resource_entry *)scsi_dev->hostdata;
if (res)
res->scsi_dev = NULL;
scsi_dev->hostdata = NULL;
}
/** * pmcraid_change_queue_depth - Change the device's queue depth * @scsi_dev: scsi device struct * @depth: depth to set * * Return value * actual depth set
*/ staticint pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth)
{ if (depth > PMCRAID_MAX_CMD_PER_LUN)
depth = PMCRAID_MAX_CMD_PER_LUN; return scsi_change_queue_depth(scsi_dev, depth);
}
/** * pmcraid_init_cmdblk - initializes a command block * * @cmd: pointer to struct pmcraid_cmd to be initialized * @index: if >=0 first time initialization; otherwise reinitialization * * Return Value * None
*/ staticvoid pmcraid_init_cmdblk(struct pmcraid_cmd *cmd, int index)
{ struct pmcraid_ioarcb *ioarcb = &(cmd->ioa_cb->ioarcb);
dma_addr_t dma_addr = cmd->ioa_cb_bus_addr;
if (index >= 0) { /* first time initialization (called from probe) */
u32 ioasa_offset =
offsetof(struct pmcraid_control_block, ioasa);
/** * pmcraid_reset_type - Determine the required reset type * @pinstance: pointer to adapter instance structure * * IOA requires hard reset if any of the following conditions is true. * 1. If HRRQ valid interrupt is not masked * 2. IOA reset alert doorbell is set * 3. If there are any error interrupts
*/ staticvoid pmcraid_reset_type(struct pmcraid_instance *pinstance)
{
u32 mask;
u32 intrs;
u32 alerts;
/** * pmcraid_reset_alert_done - completion routine for reset_alert * @t: pointer to command block used in reset sequence * Return value * None
*/ staticvoid pmcraid_reset_alert_done(struct timer_list *t)
{ struct pmcraid_cmd *cmd = timer_container_of(cmd, t, timer); struct pmcraid_instance *pinstance = cmd->drv_inst;
u32 status = ioread32(pinstance->ioa_status); unsignedlong lock_flags;
/* if the critical operation in progress bit is set or the wait times * out, invoke reset engine to proceed with hard reset. If there is * some more time to wait, restart the timer
*/ if (((status & INTRS_CRITICAL_OP_IN_PROGRESS) == 0) ||
cmd->time_left <= 0) {
pmcraid_info("critical op is reset proceeding with reset\n");
spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
pmcraid_ioa_reset(cmd);
spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
} else {
pmcraid_info("critical op is not yet reset waiting again\n"); /* restart timer if some more time is available to wait */
cmd->time_left -= PMCRAID_CHECK_FOR_RESET_TIMEOUT;
cmd->timer.expires = jiffies + PMCRAID_CHECK_FOR_RESET_TIMEOUT;
cmd->timer.function = pmcraid_reset_alert_done;
add_timer(&cmd->timer);
}
}
staticvoid pmcraid_notify_ioastate(struct pmcraid_instance *, u32); /** * pmcraid_reset_alert - alerts IOA for a possible reset * @cmd: command block to be used for reset sequence. * * Return Value * returns 0 if pci config-space is accessible and RESET_DOORBELL is * successfully written to IOA. Returns non-zero in case pci_config_space * is not accessible
*/ staticvoid pmcraid_reset_alert(struct pmcraid_cmd *cmd)
{ struct pmcraid_instance *pinstance = cmd->drv_inst;
u32 doorbells; int rc;
u16 pci_reg;
/* If we are able to access IOA PCI config space, alert IOA that we are * going to reset it soon. This enables IOA to preserv persistent error * data if any. In case memory space is not accessible, proceed with * BIST or slot_reset
*/
rc = pci_read_config_word(pinstance->pdev, PCI_COMMAND, &pci_reg); if ((rc == PCIBIOS_SUCCESSFUL) && (pci_reg & PCI_COMMAND_MEMORY)) {
/* wait for IOA permission i.e until CRITICAL_OPERATION bit is * reset IOA doesn't generate any interrupts when CRITICAL * OPERATION bit is reset. A timer is started to wait for this * bit to be reset.
*/
cmd->time_left = PMCRAID_RESET_TIMEOUT;
cmd->timer.expires = jiffies + PMCRAID_CHECK_FOR_RESET_TIMEOUT;
cmd->timer.function = pmcraid_reset_alert_done;
add_timer(&cmd->timer);
iowrite32(DOORBELL_IOA_RESET_ALERT,
pinstance->int_regs.host_ioa_interrupt_reg);
doorbells =
ioread32(pinstance->int_regs.host_ioa_interrupt_reg);
pmcraid_info("doorbells after reset alert: %x\n", doorbells);
} else {
pmcraid_info("PCI config is not accessible starting BIST\n");
pinstance->ioa_state = IOA_STATE_IN_HARD_RESET;
pmcraid_start_bist(cmd);
}
}
/** * pmcraid_timeout_handler - Timeout handler for internally generated ops * * @t: pointer to command structure, that got timedout * * This function blocks host requests and initiates an adapter reset. * * Return value: * None
*/ staticvoid pmcraid_timeout_handler(struct timer_list *t)
{ struct pmcraid_cmd *cmd = timer_container_of(cmd, t, timer); struct pmcraid_instance *pinstance = cmd->drv_inst; unsignedlong lock_flags;
dev_info(&pinstance->pdev->dev, "Adapter being reset due to cmd(CDB[0] = %x) timeout\n",
cmd->ioa_cb->ioarcb.cdb[0]);
/* Command timeouts result in hard reset sequence. The command that got * timed out may be the one used as part of reset sequence. In this * case restart reset sequence using the same command block even if * reset is in progress. Otherwise fail this command and get a free * command block to restart the reset sequence.
*/
spin_lock_irqsave(pinstance->host->host_lock, lock_flags); if (!pinstance->ioa_reset_in_progress) {
pinstance->ioa_reset_attempts = 0;
cmd = pmcraid_get_free_cmd(pinstance);
/* If we are out of command blocks, just return here itself. * Some other command's timeout handler can do the reset job
*/ if (cmd == NULL) {
spin_unlock_irqrestore(pinstance->host->host_lock,
lock_flags);
pmcraid_err("no free cmnd block for timeout handler\n"); return;
}
pinstance->reset_cmd = cmd;
pinstance->ioa_reset_in_progress = 1;
} else {
pmcraid_info("reset is already in progress\n");
if (pinstance->reset_cmd != cmd) { /* This command should have been given to IOA, this * command will be completed by fail_outstanding_cmds * anyway
*/
pmcraid_err("cmd is pending but reset in progress\n");
}
/* If this command was being used as part of the reset * sequence, set cmd_done pointer to pmcraid_ioa_reset. This * causes fail_outstanding_commands not to return the command * block back to free pool
*/ if (cmd == pinstance->reset_cmd)
cmd->cmd_done = pmcraid_ioa_reset;
}
/* Notify apps of important IOA bringup/bringdown sequences */ if (pinstance->scn.ioa_state != PMC_DEVICE_EVENT_RESET_START &&
pinstance->scn.ioa_state != PMC_DEVICE_EVENT_SHUTDOWN_START)
pmcraid_notify_ioastate(pinstance,
PMC_DEVICE_EVENT_RESET_START);
/* Some of the internal commands are sent with callers blocking for the * response. Same will be indicated as part of cmd->completion_req * field. Response path needs to wake up any waiters waiting for cmd * completion if this flag is set.
*/ if (cmd->completion_req) {
cmd->completion_req = 0;
complete(&cmd->wait_for_completion);
}
/* most of the internal commands are completed by caller itself, so * no need to return the command block back to free pool until we are * required to do so (e.g once done with initialization).
*/ if (cmd->release) {
cmd->release = 0;
pmcraid_return_cmd(cmd);
}
}
/** * pmcraid_reinit_cfgtable_done - done function for cfg table reinitialization * * @cmd: command that got response from IOA * * This routine is called after driver re-reads configuration table due to a * lost CCN. It returns the command block back to free pool and schedules * worker thread to add/delete devices into the system. * * Return Value: * none
*/ staticvoid pmcraid_reinit_cfgtable_done(struct pmcraid_cmd *cmd)
{
pmcraid_info("response internal cmd CDB[0] = %x ioasc = %x\n",
cmd->ioa_cb->ioarcb.cdb[0],
le32_to_cpu(cmd->ioa_cb->ioasa.ioasc));
if (cmd->release) {
cmd->release = 0;
pmcraid_return_cmd(cmd);
}
pmcraid_info("scheduling worker for config table reinitialization\n");
schedule_work(&cmd->drv_inst->worker_q);
}
/** * pmcraid_erp_done - Process completion of SCSI error response from device * @cmd: pmcraid_command * * This function copies the sense buffer into the scsi_cmd struct and completes * scsi_cmd by calling scsi_done function. * * Return value: * none
*/ staticvoid pmcraid_erp_done(struct pmcraid_cmd *cmd)
{ struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd; struct pmcraid_instance *pinstance = cmd->drv_inst;
u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc);
/** * _pmcraid_fire_command - sends an IOA command to adapter * * This function adds the given block into pending command list * and returns without waiting * * @cmd : command to be sent to the device * * Return Value * None
*/ staticvoid _pmcraid_fire_command(struct pmcraid_cmd *cmd)
{ struct pmcraid_instance *pinstance = cmd->drv_inst; unsignedlong lock_flags;
/* Add this command block to pending cmd pool. We do this prior to * writting IOARCB to ioarrin because IOA might complete the command * by the time we are about to add it to the list. Response handler * (isr/tasklet) looks for cmd block in the pending pending list.
*/
spin_lock_irqsave(&pinstance->pending_pool_lock, lock_flags);
list_add_tail(&cmd->free_list, &pinstance->pending_cmd_pool);
spin_unlock_irqrestore(&pinstance->pending_pool_lock, lock_flags);
atomic_inc(&pinstance->outstanding_cmds);
/* driver writes lower 32-bit value of IOARCB address only */
mb();
iowrite32(le64_to_cpu(cmd->ioa_cb->ioarcb.ioarcb_bus_addr), pinstance->ioarrin);
}
/** * pmcraid_send_cmd - fires a command to IOA * * This function also sets up timeout function, and command completion * function * * @cmd: pointer to the command block to be fired to IOA * @cmd_done: command completion function, called once IOA responds * @timeout: timeout to wait for this command completion * @timeout_func: timeout handler * * Return value * none
*/ staticvoid pmcraid_send_cmd( struct pmcraid_cmd *cmd, void (*cmd_done) (struct pmcraid_cmd *), unsignedlong timeout, void (*timeout_func) (struct timer_list *)
)
{ /* initialize done function */
cmd->cmd_done = cmd_done;
/** * pmcraid_ioa_shutdown - sends SHUTDOWN command to ioa * * @cmd: pointer to the command block used as part of reset sequence * * Return Value * None
*/ staticvoid pmcraid_ioa_shutdown(struct pmcraid_cmd *cmd)
{
pmcraid_info("response for Cancel CCN CDB[0] = %x ioasc = %x\n",
cmd->ioa_cb->ioarcb.cdb[0],
le32_to_cpu(cmd->ioa_cb->ioasa.ioasc));
/* Note that commands sent during reset require next command to be sent * to IOA. Hence reinit the done function as well as timeout function
*/
pmcraid_reinit_cmdblk(cmd);
cmd->ioa_cb->ioarcb.request_type = REQ_TYPE_IOACMD;
cmd->ioa_cb->ioarcb.resource_handle =
cpu_to_le32(PMCRAID_IOA_RES_HANDLE);
cmd->ioa_cb->ioarcb.cdb[0] = PMCRAID_IOA_SHUTDOWN;
cmd->ioa_cb->ioarcb.cdb[1] = PMCRAID_SHUTDOWN_NORMAL;
/* fire shutdown command to hardware. */
pmcraid_info("firing normal shutdown command (%d) to IOA\n",
le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle));
staticvoid pmcraid_querycfg(struct pmcraid_cmd *); /** * pmcraid_get_fwversion_done - completion function for get_fwversion * * @cmd: pointer to command block used to send INQUIRY command * * Return Value * none
*/ staticvoid pmcraid_get_fwversion_done(struct pmcraid_cmd *cmd)
{ struct pmcraid_instance *pinstance = cmd->drv_inst;
u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc); unsignedlong lock_flags;
/* configuration table entry size depends on firmware version. If fw * version is not known, it is not possible to interpret IOA config * table
*/ if (ioasc) {
pmcraid_err("IOA Inquiry failed with %x\n", ioasc);
spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT;
pmcraid_reset_alert(cmd);
spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
} else {
pmcraid_querycfg(cmd);
}
}
/** * pmcraid_get_fwversion - reads firmware version information * * @cmd: pointer to command block used to send INQUIRY command * * Return Value * none
*/ staticvoid pmcraid_get_fwversion(struct pmcraid_cmd *cmd)
{ struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; struct pmcraid_ioadl_desc *ioadl; struct pmcraid_instance *pinstance = cmd->drv_inst;
u16 data_size = sizeof(struct pmcraid_inquiry_data);
/* Since entire inquiry data it can be part of IOARCB itself
*/
ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) +
offsetof(struct pmcraid_ioarcb,
add_data.u.ioadl[0]));
ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc));
ioarcb->ioarcb_bus_addr &= cpu_to_le64(~(0x1FULL));
/* initialize the hrrq number where IOA will respond to this command */
ioarcb->hrrq_id = index;
ioarcb->cdb[0] = PMCRAID_IDENTIFY_HRRQ;
ioarcb->cdb[1] = index;
/* IOA expects 64-bit pci address to be written in B.E format * (i.e cdb[2]=MSByte..cdb[9]=LSB.
*/
pmcraid_info("HRRQ_IDENTIFY with hrrq:ioarcb:index => %llx:%llx:%x\n",
hrrq_addr, ioarcb->ioarcb_bus_addr, index);
/* Subsequent commands require HRRQ identification to be successful. * Note that this gets called even during reset from SCSI mid-layer * or tasklet
*/
pmcraid_send_cmd(cmd, done_function,
PMCRAID_INTERNAL_TIMEOUT,
pmcraid_timeout_handler);
}
/** * pmcraid_send_hcam - Send an HCAM to IOA * @pinstance: ioa config struct * @type: HCAM type * * This function will send a Host Controlled Async command to IOA. * * Return value: * none
*/ staticvoid pmcraid_send_hcam(struct pmcraid_instance *pinstance, u8 type)
{ struct pmcraid_cmd *cmd = pmcraid_init_hcam(pinstance, type);
pmcraid_send_hcam_cmd(cmd);
}
/** * pmcraid_prepare_cancel_cmd - prepares a command block to abort another * * @cmd: pointer to cmd that is used as cancelling command * @cmd_to_cancel: pointer to the command that needs to be cancelled
*/ staticvoid pmcraid_prepare_cancel_cmd( struct pmcraid_cmd *cmd, struct pmcraid_cmd *cmd_to_cancel
)
{ struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
__be64 ioarcb_addr;
/* IOARCB address of the command to be cancelled is given in * cdb[2]..cdb[9] is Big-Endian format. Note that length bits in * IOARCB address are not masked.
*/
ioarcb_addr = cpu_to_be64(le64_to_cpu(cmd_to_cancel->ioa_cb->ioarcb.ioarcb_bus_addr));
/* Get the resource handle to where the command to be aborted has been * sent.
*/
ioarcb->resource_handle = cmd_to_cancel->ioa_cb->ioarcb.resource_handle;
ioarcb->request_type = REQ_TYPE_IOACMD;
memset(ioarcb->cdb, 0, PMCRAID_MAX_CDB_LEN);
ioarcb->cdb[0] = PMCRAID_ABORT_CMD;
/** * pmcraid_cancel_hcam - sends ABORT task to abort a given HCAM * * @cmd: command to be used as cancelling command * @type: HCAM type * @cmd_done: op done function for the cancelling command
*/ staticvoid pmcraid_cancel_hcam( struct pmcraid_cmd *cmd,
u8 type, void (*cmd_done) (struct pmcraid_cmd *)
)
{ struct pmcraid_instance *pinstance; struct pmcraid_hostrcb *hcam;
/* prepare for cancelling previous hcam command. If the HCAM is * currently not pending with IOA, we would have hcam->cmd as non-null
*/ if (hcam->cmd == NULL) return;
pmcraid_prepare_cancel_cmd(cmd, hcam->cmd);
/* writing to IOARRIN must be protected by host_lock, as mid-layer * schedule queuecommand while we are doing this
*/
pmcraid_send_cmd(cmd, cmd_done,
PMCRAID_INTERNAL_TIMEOUT,
pmcraid_timeout_handler);
}
/** * pmcraid_cancel_ccn - cancel CCN HCAM already registered with IOA * * @cmd: command block to be used for cancelling the HCAM
*/ staticvoid pmcraid_cancel_ccn(struct pmcraid_cmd *cmd)
{
pmcraid_info("response for Cancel LDN CDB[0] = %x ioasc = %x\n",
cmd->ioa_cb->ioarcb.cdb[0],
le32_to_cpu(cmd->ioa_cb->ioasa.ioasc));
/** * pmcraid_cancel_ldn - cancel LDN HCAM already registered with IOA * * @cmd: command block to be used for cancelling the HCAM
*/ staticvoid pmcraid_cancel_ldn(struct pmcraid_cmd *cmd)
{
pmcraid_cancel_hcam(cmd,
PMCRAID_HCAM_CODE_LOG_DATA,
pmcraid_cancel_ccn);
}
/** * pmcraid_expose_resource - check if the resource can be exposed to OS * * @fw_version: firmware version code * @cfgte: pointer to configuration table entry of the resource * * Return value: * true if resource can be added to midlayer, false(0) otherwise
*/ staticint pmcraid_expose_resource(u16 fw_version, struct pmcraid_config_table_entry *cfgte)
{ int retval = 0;
/* * pmcraid_notify_aen - sends event msg to user space application * @pinstance: pointer to adapter instance structure * * Return value: * 0 if success, error value in case of any failure.
*/ staticint pmcraid_notify_aen( struct pmcraid_instance *pinstance, struct pmcraid_aen_msg *aen_msg,
u32 data_size)
{ struct sk_buff *skb; void *msg_header;
u32 total_size, nla_genl_hdr_total_size; int result;
result = genlmsg_multicast(&pmcraid_event_family, skb,
0, 0, GFP_ATOMIC);
/* If there are no listeners, genlmsg_multicast may return non-zero * value.
*/ if (result)
pmcraid_info("error (%x) sending aen event message\n", result); return result;
}
/** * pmcraid_notify_ccn - notifies about CCN event msg to user space * @pinstance: pointer adapter instance structure * * Return value: * 0 if success, error value in case of any failure
*/ staticint pmcraid_notify_ccn(struct pmcraid_instance *pinstance)
{ return pmcraid_notify_aen(pinstance,
pinstance->ccn.msg,
le32_to_cpu(pinstance->ccn.hcam->data_len) + sizeof(struct pmcraid_hcam_hdr));
}
/** * pmcraid_notify_ldn - notifies about CCN event msg to user space * @pinstance: pointer adapter instance structure * * Return value: * 0 if success, error value in case of any failure
*/ staticint pmcraid_notify_ldn(struct pmcraid_instance *pinstance)
{ return pmcraid_notify_aen(pinstance,
pinstance->ldn.msg,
le32_to_cpu(pinstance->ldn.hcam->data_len) + sizeof(struct pmcraid_hcam_hdr));
}
/** * pmcraid_notify_ioastate - sends IOA state event msg to user space * @pinstance: pointer adapter instance structure * @evt: controller state event to be sent * * Return value: * 0 if success, error value in case of any failure
*/ staticvoid pmcraid_notify_ioastate(struct pmcraid_instance *pinstance, u32 evt)
{
pinstance->scn.ioa_state = evt;
pmcraid_notify_aen(pinstance,
&pinstance->scn.msg, sizeof(u32));
}
/** * pmcraid_handle_config_change - Handle a config change from the adapter * @pinstance: pointer to per adapter instance structure * * Return value: * none
*/
/* If this HCAM indicates a lost notification, read the config table */ if (pinstance->ccn.hcam->notification_lost) {
cfgcmd = pmcraid_get_free_cmd(pinstance); if (cfgcmd) {
pmcraid_info("lost CCN, reading config table\b");
pinstance->reinit_cfg_table = 1;
pmcraid_querycfg(cfgcmd);
} else {
pmcraid_err("lost CCN, no free cmd for querycfg\n");
} goto out_notify_apps;
}
/* If this resource is not going to be added to mid-layer, just notify * applications and return. If this notification is about hiding a VSET * resource, check if it was exposed already.
*/ if (pinstance->ccn.hcam->notification_type ==
NOTIFICATION_TYPE_ENTRY_CHANGED &&
cfg_entry->resource_type == RES_TYPE_VSET) {
hidden_entry = (cfg_entry->unique_flags1 & 0x80) != 0;
} elseif (!pmcraid_expose_resource(fw_version, cfg_entry)) { goto out_notify_apps;
}
if (hidden_entry) {
spin_unlock_irqrestore(&pinstance->resource_lock,
lock_flags); goto out_notify_apps;
}
/* If there are more number of resources than what driver can * manage, do not notify the applications about the CCN. Just * ignore this notifications and re-register the same HCAM
*/ if (list_empty(&pinstance->free_res_q)) {
spin_unlock_irqrestore(&pinstance->resource_lock,
lock_flags);
pmcraid_err("too many resources attached\n");
spin_lock_irqsave(pinstance->host->host_lock,
host_lock_flags);
pmcraid_send_hcam(pinstance,
PMCRAID_HCAM_CODE_CONFIG_CHANGE);
spin_unlock_irqrestore(pinstance->host->host_lock,
host_lock_flags); return;
}
res = list_entry(pinstance->free_res_q.next, struct pmcraid_resource_entry, queue);
if (ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET ||
ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET_BY_OTHER) {
dev_info(&pinstance->pdev->dev, "UnitAttention due to IOA Bus Reset\n");
scsi_report_bus_reset(
pinstance->host,
RES_BUS(hcam_ldn->error_log.fd_ra));
}
return;
}
/** * pmcraid_process_ccn - Op done function for a CCN. * @cmd: pointer to command struct * * This function is the op done function for a configuration * change notification * * Return value: * none
*/ staticvoid pmcraid_process_ccn(struct pmcraid_cmd *cmd)
{ struct pmcraid_instance *pinstance = cmd->drv_inst;
u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc); unsignedlong lock_flags;
/* If driver initiated IOA reset happened while this hcam was pending * with IOA, or IOA bringdown sequence is in progress, no need to * re-register the hcam
*/ if (ioasc == PMCRAID_IOASC_IOA_WAS_RESET ||
atomic_read(&pinstance->ccn.ignore) == 1) { return;
} elseif (ioasc) {
dev_info(&pinstance->pdev->dev, "Host RCB (CCN) failed with IOASC: 0x%08X\n", ioasc);
spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE);
spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
} else {
pmcraid_handle_config_change(pinstance);
}
}
staticvoid pmcraid_initiate_reset(struct pmcraid_instance *); staticvoid pmcraid_set_timestamp(struct pmcraid_cmd *cmd); /** * pmcraid_process_ldn - op done function for an LDN * @cmd: pointer to command block * * Return value * none
*/ staticvoid pmcraid_process_ldn(struct pmcraid_cmd *cmd)
{ struct pmcraid_instance *pinstance = cmd->drv_inst; struct pmcraid_hcam_ldn *ldn_hcam =
(struct pmcraid_hcam_ldn *)pinstance->ldn.hcam;
u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc);
u32 fd_ioasc = le32_to_cpu(ldn_hcam->error_log.fd_ioasc); unsignedlong lock_flags;
/* return the command block back to freepool */
pinstance->ldn.cmd = NULL;
pmcraid_return_cmd(cmd);
/* If driver initiated IOA reset happened while this hcam was pending * with IOA, no need to re-register the hcam as reset engine will do it * once reset sequence is complete
*/ if (ioasc == PMCRAID_IOASC_IOA_WAS_RESET ||
atomic_read(&pinstance->ccn.ignore) == 1) { return;
} elseif (!ioasc) {
pmcraid_handle_error_log(pinstance); if (fd_ioasc == PMCRAID_IOASC_NR_IOA_RESET_REQUIRED) {
spin_lock_irqsave(pinstance->host->host_lock,
lock_flags);
pmcraid_initiate_reset(pinstance);
spin_unlock_irqrestore(pinstance->host->host_lock,
lock_flags); return;
} if (fd_ioasc == PMCRAID_IOASC_TIME_STAMP_OUT_OF_SYNC) {
pinstance->timestamp_error = 1;
pmcraid_set_timestamp(cmd);
}
} else {
dev_info(&pinstance->pdev->dev, "Host RCB(LDN) failed with IOASC: 0x%08X\n", ioasc);
} /* send netlink message for HCAM notification if enabled */ if (!pmcraid_disable_aen)
pmcraid_notify_ldn(pinstance);
cmd = pmcraid_init_hcam(pinstance, PMCRAID_HCAM_CODE_LOG_DATA); if (cmd)
pmcraid_send_hcam_cmd(cmd);
}
/** * pmcraid_register_hcams - register HCAMs for CCN and LDN * * @pinstance: pointer per adapter instance structure * * Return Value * none
*/ staticvoid pmcraid_register_hcams(struct pmcraid_instance *pinstance)
{
pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE);
pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_LOG_DATA);
}
/** * pmcraid_unregister_hcams - cancel HCAMs registered already * @cmd: pointer to command used as part of reset sequence
*/ staticvoid pmcraid_unregister_hcams(struct pmcraid_cmd *cmd)
{ struct pmcraid_instance *pinstance = cmd->drv_inst;
/* During IOA bringdown, HCAM gets fired and tasklet proceeds with * handling hcam response though it is not necessary. In order to * prevent this, set 'ignore', so that bring-down sequence doesn't * re-send any more hcams
*/
atomic_set(&pinstance->ccn.ignore, 1);
atomic_set(&pinstance->ldn.ignore, 1);
/* If adapter reset was forced as part of runtime reset sequence, * start the reset sequence. Reset will be triggered even in case * IOA unit_check.
*/ if ((pinstance->force_ioa_reset && !pinstance->ioa_bringdown) ||
pinstance->ioa_unit_check) {
pinstance->force_ioa_reset = 0;
pinstance->ioa_unit_check = 0;
pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT;
pmcraid_reset_alert(cmd); return;
}
/* Driver tries to cancel HCAMs by sending ABORT TASK for each HCAM * one after the other. So CCN cancellation will be triggered by * pmcraid_cancel_ldn itself.
*/
pmcraid_cancel_ldn(cmd);
}
/** * pmcraid_soft_reset - performs a soft reset and makes IOA become ready * @cmd : pointer to reset command block * * Return Value * none
*/ staticvoid pmcraid_soft_reset(struct pmcraid_cmd *cmd)
{ struct pmcraid_instance *pinstance = cmd->drv_inst;
u32 int_reg;
u32 doorbell;
/* There will be an interrupt when Transition to Operational bit is * set so tasklet would execute next reset task. The timeout handler * would re-initiate a reset
*/
cmd->cmd_done = pmcraid_ioa_reset;
cmd->timer.expires = jiffies +
msecs_to_jiffies(PMCRAID_TRANSOP_TIMEOUT);
cmd->timer.function = pmcraid_timeout_handler;
if (!timer_pending(&cmd->timer))
add_timer(&cmd->timer);
/* Enable destructive diagnostics on IOA if it is not yet in * operational state
*/
doorbell = DOORBELL_RUNTIME_RESET |
DOORBELL_ENABLE_DESTRUCTIVE_DIAGS;
/* Since we do RESET_ALERT and Start BIST we have to again write * MSIX Doorbell to indicate the interrupt mode
*/ if (pinstance->interrupt_mode) {
iowrite32(DOORBELL_INTR_MODE_MSIX,
pinstance->int_regs.host_ioa_interrupt_reg);
ioread32(pinstance->int_regs.host_ioa_interrupt_reg);
}
pmcraid_info("Waiting for IOA to become operational %x:%x\n",
ioread32(pinstance->int_regs.host_ioa_interrupt_reg),
int_reg);
}
/** * pmcraid_get_dump - retrieves IOA dump in case of Unit Check interrupt * * @pinstance: pointer to adapter instance structure * * Return Value * none
*/ staticvoid pmcraid_get_dump(struct pmcraid_instance *pinstance)
{
pmcraid_info("%s is not yet implemented\n", __func__);
}
/** * pmcraid_fail_outstanding_cmds - Fails all outstanding ops. * @pinstance: pointer to adapter instance structure * * This function fails all outstanding ops. If they are submitted to IOA * already, it sends cancel all messages if IOA is still accepting IOARCBs, * otherwise just completes the commands and returns the cmd blocks to free * pool. * * Return value: * none
*/ staticvoid pmcraid_fail_outstanding_cmds(struct pmcraid_instance *pinstance)
{ struct pmcraid_cmd *cmd, *temp; unsignedlong lock_flags;
/* pending command list is protected by pending_pool_lock. Its * traversal must be done as within this lock
*/
spin_lock_irqsave(&pinstance->pending_pool_lock, lock_flags);
list_for_each_entry_safe(cmd, temp, &pinstance->pending_cmd_pool,
free_list) {
list_del(&cmd->free_list);
spin_unlock_irqrestore(&pinstance->pending_pool_lock,
lock_flags);
cmd->ioa_cb->ioasa.ioasc =
cpu_to_le32(PMCRAID_IOASC_IOA_WAS_RESET);
cmd->ioa_cb->ioasa.ilid =
cpu_to_le32(PMCRAID_DRIVER_ILID);
/* In case the command timer is still running */
timer_delete(&cmd->timer);
/* If this is an IO command, complete it by invoking scsi_done * function. If this is one of the internal commands other * than pmcraid_ioa_reset and HCAM commands invoke cmd_done to * complete it
*/ if (cmd->scsi_cmd) {
/** * pmcraid_ioa_reset - Implementation of IOA reset logic * * @cmd: pointer to the cmd block to be used for entire reset process * * This function executes most of the steps required for IOA reset. This gets * called by user threads (modprobe/insmod/rmmod) timer, tasklet and midlayer's * 'eh_' thread. Access to variables used for controlling the reset sequence is * synchronized using host lock. Various functions called during reset process * would make use of a single command block, pointer to which is also stored in * adapter instance structure. * * Return Value * None
*/ staticvoid pmcraid_ioa_reset(struct pmcraid_cmd *cmd)
{ struct pmcraid_instance *pinstance = cmd->drv_inst;
u8 reset_complete = 0;
pinstance->ioa_reset_in_progress = 1;
if (pinstance->reset_cmd != cmd) {
pmcraid_err("reset is called with different command block\n");
pinstance->reset_cmd = cmd;
}
pmcraid_info("reset_engine: state = %d, command = %p\n",
pinstance->ioa_state, cmd);
switch (pinstance->ioa_state) {
case IOA_STATE_DEAD: /* If IOA is offline, whatever may be the reset reason, just * return. callers might be waiting on the reset wait_q, wake * up them
*/
pmcraid_err("IOA is offline no reset is possible\n");
reset_complete = 1; break;
case IOA_STATE_IN_BRINGDOWN: /* we enter here, once ioa shutdown command is processed by IOA * Alert IOA for a possible reset. If reset alert fails, IOA * goes through hard-reset
*/
pmcraid_disable_interrupts(pinstance, ~0);
pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT;
pmcraid_reset_alert(cmd); break;
case IOA_STATE_UNKNOWN: /* We may be called during probe or resume. Some pre-processing * is required for prior to reset
*/
scsi_block_requests(pinstance->host);
/* If asked to reset while IOA was processing responses or * there are any error responses then IOA may require * hard-reset.
*/ if (pinstance->ioa_hard_reset == 0) { if (ioread32(pinstance->ioa_status) &
INTRS_TRANSITION_TO_OPERATIONAL) {
pmcraid_info("sticky bit set, bring-up\n");
pinstance->ioa_state = IOA_STATE_IN_BRINGUP;
pmcraid_reinit_cmdblk(cmd);
pmcraid_identify_hrrq(cmd);
} else {
pinstance->ioa_state = IOA_STATE_IN_SOFT_RESET;
pmcraid_soft_reset(cmd);
}
} else { /* Alert IOA of a possible reset and wait for critical * operation in progress bit to reset
*/
pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT;
pmcraid_reset_alert(cmd);
} break;
case IOA_STATE_IN_RESET_ALERT: /* If critical operation in progress bit is reset or wait gets * timed out, reset proceeds with starting BIST on the IOA. * pmcraid_ioa_hard_reset keeps a count of reset attempts. If * they are 3 or more, reset engine marks IOA dead and returns
*/
pinstance->ioa_state = IOA_STATE_IN_HARD_RESET;
pmcraid_start_bist(cmd); break;
case IOA_STATE_IN_HARD_RESET:
pinstance->ioa_reset_attempts++;
/* retry reset if we haven't reached maximum allowed limit */ if (pinstance->ioa_reset_attempts > PMCRAID_RESET_ATTEMPTS) {
pinstance->ioa_reset_attempts = 0;
pmcraid_err("IOA didn't respond marking it as dead\n");
pinstance->ioa_state = IOA_STATE_DEAD;
/* Once either bist or pci reset is done, restore PCI config * space. If this fails, proceed with hard reset again
*/
pci_restore_state(pinstance->pdev);
/* fail all pending commands */
pmcraid_fail_outstanding_cmds(pinstance);
/* check if unit check is active, if so extract dump */ if (pinstance->ioa_unit_check) {
pmcraid_info("unit check is active\n");
pinstance->ioa_unit_check = 0;
pmcraid_get_dump(pinstance);
pinstance->ioa_reset_attempts--;
pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT;
pmcraid_reset_alert(cmd); break;
}
/* if the reset reason is to bring-down the ioa, we might be * done with the reset restore pci_config_space and complete * the reset
*/ if (pinstance->ioa_bringdown) {
pmcraid_info("bringing down the adapter\n");
pinstance->ioa_shutdown_type = SHUTDOWN_NONE;
pinstance->ioa_bringdown = 0;
pinstance->ioa_state = IOA_STATE_UNKNOWN;
pmcraid_notify_ioastate(pinstance,
PMC_DEVICE_EVENT_SHUTDOWN_SUCCESS);
reset_complete = 1;
} else { /* bring-up IOA, so proceed with soft reset * Reinitialize hrrq_buffers and their indices also * enable interrupts after a pci_restore_state
*/ if (pmcraid_reset_enable_ioa(pinstance)) {
pinstance->ioa_state = IOA_STATE_IN_BRINGUP;
pmcraid_info("bringing up the adapter\n");
pmcraid_reinit_cmdblk(cmd);
pmcraid_identify_hrrq(cmd);
} else {
pinstance->ioa_state = IOA_STATE_IN_SOFT_RESET;
pmcraid_soft_reset(cmd);
}
} break;
case IOA_STATE_IN_SOFT_RESET: /* TRANSITION TO OPERATIONAL is on so start initialization * sequence
*/
pmcraid_info("In softreset proceeding with bring-up\n");
pinstance->ioa_state = IOA_STATE_IN_BRINGUP;
/* Initialization commands start with HRRQ identification. From * now on tasklet completes most of the commands as IOA is up * and intrs are enabled
*/
pmcraid_identify_hrrq(cmd); break;
case IOA_STATE_IN_BRINGUP: /* we are done with bringing up of IOA, change the ioa_state to * operational and wake up any waiters
*/
pinstance->ioa_state = IOA_STATE_OPERATIONAL;
reset_complete = 1; break;
case IOA_STATE_OPERATIONAL: default: /* When IOA is operational and a reset is requested, check for * the reset reason. If reset is to bring down IOA, unregister * HCAMs and initiate shutdown; if adapter reset is forced then * restart reset sequence again
*/ if (pinstance->ioa_shutdown_type == SHUTDOWN_NONE &&
pinstance->force_ioa_reset == 0) {
pmcraid_notify_ioastate(pinstance,
PMC_DEVICE_EVENT_RESET_SUCCESS);
reset_complete = 1;
} else { if (pinstance->ioa_shutdown_type != SHUTDOWN_NONE)
pinstance->ioa_state = IOA_STATE_IN_BRINGDOWN;
pmcraid_reinit_cmdblk(cmd);
pmcraid_unregister_hcams(cmd);
} break;
}
/* reset will be completed if ioa_state is either DEAD or UNKNOWN or * OPERATIONAL. Reset all control variables used during reset, wake up * any waiting threads and let the SCSI mid-layer send commands. Note * that host_lock must be held before invoking scsi_report_bus_reset.
*/ if (reset_complete) {
pinstance->ioa_reset_in_progress = 0;
pinstance->ioa_reset_attempts = 0;
pinstance->reset_cmd = NULL;
pinstance->ioa_shutdown_type = SHUTDOWN_NONE;
pinstance->ioa_bringdown = 0;
pmcraid_return_cmd(cmd);
/* If target state is to bring up the adapter, proceed with * hcam registration and resource exposure to mid-layer.
*/ if (pinstance->ioa_state == IOA_STATE_OPERATIONAL)
pmcraid_register_hcams(pinstance);
wake_up_all(&pinstance->reset_wait_q);
}
return;
}
/** * pmcraid_initiate_reset - initiates reset sequence. This is called from * ISR/tasklet during error interrupts including IOA unit check. If reset * is already in progress, it just returns, otherwise initiates IOA reset * to bring IOA up to operational state. * * @pinstance: pointer to adapter instance structure * * Return value * none
*/ staticvoid pmcraid_initiate_reset(struct pmcraid_instance *pinstance)
{ struct pmcraid_cmd *cmd;
/* If the reset is already in progress, just return, otherwise start * reset sequence and return
*/ if (!pinstance->ioa_reset_in_progress) {
scsi_block_requests(pinstance->host);
cmd = pmcraid_get_free_cmd(pinstance);
if (cmd == NULL) {
pmcraid_err("no cmnd blocks for initiate_reset\n"); return;
}
/** * pmcraid_reset_reload - utility routine for doing IOA reset either to bringup * or bringdown IOA * @pinstance: pointer adapter instance structure * @shutdown_type: shutdown type to be used NONE, NORMAL or ABRREV * @target_state: expected target state after reset * * Note: This command initiates reset and waits for its completion. Hence this * should not be called from isr/timer/tasklet functions (timeout handlers, * error response handlers and interrupt handlers). * * Return Value * 1 in case ioa_state is not target_state, 0 otherwise.
*/ staticint pmcraid_reset_reload( struct pmcraid_instance *pinstance,
u8 shutdown_type,
u8 target_state
)
{ struct pmcraid_cmd *reset_cmd = NULL; unsignedlong lock_flags; int reset = 1;
/** * pmcraid_reset_bringdown - wrapper over pmcraid_reset_reload to bringdown IOA * * @pinstance: pointer to adapter instance structure * * Return Value * whatever is returned from pmcraid_reset_reload
*/ staticint pmcraid_reset_bringdown(struct pmcraid_instance *pinstance)
{ return pmcraid_reset_reload(pinstance,
SHUTDOWN_NORMAL,
IOA_STATE_UNKNOWN);
}
/** * pmcraid_reset_bringup - wrapper over pmcraid_reset_reload to bring up IOA * * @pinstance: pointer to adapter instance structure * * Return Value * whatever is returned from pmcraid_reset_reload
*/ staticint pmcraid_reset_bringup(struct pmcraid_instance *pinstance)
{
pmcraid_notify_ioastate(pinstance, PMC_DEVICE_EVENT_RESET_START);
/** * pmcraid_request_sense - Send request sense to a device * @cmd: pmcraid command struct * * This function sends a request sense to a device as a result of a check * condition. This method re-uses the same command block that failed earlier.
*/ staticvoid pmcraid_request_sense(struct pmcraid_cmd *cmd)
{ struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl; struct device *dev = &cmd->drv_inst->pdev->dev;
cmd->sense_buffer = cmd->scsi_cmd->sense_buffer;
cmd->sense_buffer_dma = dma_map_single(dev, cmd->sense_buffer,
SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); if (dma_mapping_error(dev, cmd->sense_buffer_dma)) {
pmcraid_err
("couldn't allocate sense buffer for request sense\n");
pmcraid_erp_done(cmd); return;
}
/* request sense might be called as part of error response processing * which runs in tasklets context. It is possible that mid-layer might * schedule queuecommand during this time, hence, writting to IOARRIN * must be protect by host_lock
*/
pmcraid_send_cmd(cmd, pmcraid_erp_done,
PMCRAID_REQUEST_SENSE_TIMEOUT,
pmcraid_timeout_handler);
}
/** * pmcraid_cancel_all - cancel all outstanding IOARCBs as part of error recovery * @cmd: command that failed * @need_sense: true if request_sense is required after cancel all * * This function sends a cancel all to a device to clear the queue.
*/ staticvoid pmcraid_cancel_all(struct pmcraid_cmd *cmd, bool need_sense)
{ struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd; struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; struct pmcraid_resource_entry *res = scsi_cmd->device->hostdata;
/* writing to IOARRIN must be protected by host_lock, as mid-layer * schedule queuecommand while we are doing this
*/
pmcraid_send_cmd(cmd, need_sense ?
pmcraid_erp_done : pmcraid_request_sense,
PMCRAID_REQUEST_SENSE_TIMEOUT,
pmcraid_timeout_handler);
}
/** * pmcraid_frame_auto_sense: frame fixed format sense information * * @cmd: pointer to failing command block * * Return value * none
*/ staticvoid pmcraid_frame_auto_sense(struct pmcraid_cmd *cmd)
{
u8 *sense_buf = cmd->scsi_cmd->sense_buffer; struct pmcraid_resource_entry *res = cmd->scsi_cmd->device->hostdata; struct pmcraid_ioasa *ioasa = &cmd->ioa_cb->ioasa;
u32 ioasc = le32_to_cpu(ioasa->ioasc);
u32 failing_lba = 0;
/** * pmcraid_error_handler - Error response handlers for a SCSI op * @cmd: pointer to pmcraid_cmd that has failed * * This function determines whether or not to initiate ERP on the affected * device. This is called from a tasklet, which doesn't hold any locks. * * Return value: * 0 it caller can complete the request, otherwise 1 where in error * handler itself completes the request and returns the command block * back to free-pool
*/ staticint pmcraid_error_handler(struct pmcraid_cmd *cmd)
{ struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd; struct pmcraid_resource_entry *res = scsi_cmd->device->hostdata; struct pmcraid_instance *pinstance = cmd->drv_inst; struct pmcraid_ioasa *ioasa = &cmd->ioa_cb->ioasa;
u32 ioasc = le32_to_cpu(ioasa->ioasc);
u32 masked_ioasc = ioasc & PMCRAID_IOASC_SENSE_MASK; bool sense_copied = false;
if (!res) {
pmcraid_info("resource pointer is NULL\n"); return 0;
}
/* If this was a SCSI read/write command keep count of errors */ if (SCSI_CMD_TYPE(scsi_cmd->cmnd[0]) == SCSI_READ_CMD)
atomic_inc(&res->read_failures); elseif (SCSI_CMD_TYPE(scsi_cmd->cmnd[0]) == SCSI_WRITE_CMD)
atomic_inc(&res->write_failures);
if (!RES_IS_GSCSI(res->cfg_entry) &&
masked_ioasc != PMCRAID_IOASC_HW_DEVICE_BUS_STATUS_ERROR) {
pmcraid_frame_auto_sense(cmd);
}
/* Log IOASC/IOASA information based on user settings */
pmcraid_ioasc_logger(ioasc, cmd);
switch (masked_ioasc) {
case PMCRAID_IOASC_AC_TERMINATED_BY_HOST:
scsi_cmd->result |= (DID_ABORT << 16); break;
case PMCRAID_IOASC_IR_INVALID_RESOURCE_HANDLE: case PMCRAID_IOASC_HW_CANNOT_COMMUNICATE:
scsi_cmd->result |= (DID_NO_CONNECT << 16); break;
case PMCRAID_IOASC_ME_READ_ERROR_NO_REALLOC:
scsi_cmd->result |= (DID_PASSTHROUGH << 16); break;
case PMCRAID_IOASC_UA_BUS_WAS_RESET: case PMCRAID_IOASC_UA_BUS_WAS_RESET_BY_OTHER: if (!res->reset_progress)
scsi_report_bus_reset(pinstance->host,
scsi_cmd->device->channel);
scsi_cmd->result |= (DID_ERROR << 16); break;
case PMCRAID_IOASC_HW_DEVICE_BUS_STATUS_ERROR:
scsi_cmd->result |= PMCRAID_IOASC_SENSE_STATUS(ioasc);
res->sync_reqd = 1;
/* if check_condition is not active return with error otherwise * get/frame the sense buffer
*/ if (PMCRAID_IOASC_SENSE_STATUS(ioasc) !=
SAM_STAT_CHECK_CONDITION &&
PMCRAID_IOASC_SENSE_STATUS(ioasc) != SAM_STAT_ACA_ACTIVE) return 0;
/* If we have auto sense data as part of IOASA pass it to * mid-layer
*/ if (ioasa->auto_sense_length != 0) { short sense_len = le16_to_cpu(ioasa->auto_sense_length); int data_size = min_t(u16, sense_len,
SCSI_SENSE_BUFFERSIZE);
/** * pmcraid_reset_device - device reset handler functions * * @scsi_dev: scsi device struct * @timeout: command timeout * @modifier: reset modifier indicating the reset sequence to be performed * * This function issues a device reset to the affected device. * A LUN reset will be sent to the device first. If that does * not work, a target reset will be sent. * * Return value: * SUCCESS / FAILED
*/ staticint pmcraid_reset_device( struct scsi_device *scsi_dev, unsignedlong timeout,
u8 modifier)
{ struct pmcraid_cmd *cmd; struct pmcraid_instance *pinstance; struct pmcraid_resource_entry *res; struct pmcraid_ioarcb *ioarcb; unsignedlong lock_flags;
u32 ioasc;
pinstance =
(struct pmcraid_instance *)scsi_dev->host->hostdata;
res = scsi_dev->hostdata;
/* If adapter is currently going through reset/reload, return failed. * This will force the mid-layer to call _eh_bus/host reset, which * will then go to sleep and wait for the reset to complete
*/
spin_lock_irqsave(pinstance->host->host_lock, lock_flags); if (pinstance->ioa_reset_in_progress ||
pinstance->ioa_state == IOA_STATE_DEAD) {
spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags); return FAILED;
}
pmcraid_info("cmd(CDB[0] = %x) for %x with index = %d\n",
cmd->ioa_cb->ioarcb.cdb[0],
le32_to_cpu(cmd->ioa_cb->ioarcb.resource_handle),
le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle) >> 2);
/* RESET_DEVICE command completes after all pending IOARCBs are * completed. Once this command is completed, pmcraind_internal_done * will wake up the 'completion' queue.
*/
wait_for_completion(&cmd->wait_for_completion);
/* complete the command here itself and return the command block * to free list
*/
pmcraid_return_cmd(cmd);
res->reset_progress = 0;
ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc);
/* set the return value based on the returned ioasc */ return PMCRAID_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS;
}
/** * _pmcraid_io_done - helper for pmcraid_io_done function * * @cmd: pointer to pmcraid command struct * @reslen: residual data length to be set in the ioasa * @ioasc: ioasc either returned by IOA or set by driver itself. * * This function is invoked by pmcraid_io_done to complete mid-layer * scsi ops. * * Return value: * 0 if caller is required to return it to free_pool. Returns 1 if * caller need not worry about freeing command block as error handler * will take care of that.
*/
staticint _pmcraid_io_done(struct pmcraid_cmd *cmd, int reslen, int ioasc)
{ struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd; int rc = 0;
if (PMCRAID_IOASC_SENSE_KEY(ioasc) != 0)
rc = pmcraid_error_handler(cmd);
if (rc == 0) {
scsi_dma_unmap(scsi_cmd);
scsi_done(scsi_cmd);
}
return rc;
}
/** * pmcraid_io_done - SCSI completion function * * @cmd: pointer to pmcraid command struct * * This function is invoked by tasklet/mid-layer error handler to completing * the SCSI ops sent from mid-layer. * * Return value * none
*/
if (_pmcraid_io_done(cmd, reslen, ioasc) == 0)
pmcraid_return_cmd(cmd);
}
/** * pmcraid_abort_cmd - Aborts a single IOARCB already submitted to IOA * * @cmd: command block of the command to be aborted * * Return Value: * returns pointer to command structure used as cancelling cmd
*/ staticstruct pmcraid_cmd *pmcraid_abort_cmd(struct pmcraid_cmd *cmd)
{ struct pmcraid_cmd *cancel_cmd; struct pmcraid_instance *pinstance;
/** * pmcraid_abort_complete - Waits for ABORT TASK completion * * @cancel_cmd: command block use as cancelling command * * Return Value: * returns SUCCESS if ABORT TASK has good completion * otherwise FAILED
*/ staticint pmcraid_abort_complete(struct pmcraid_cmd *cancel_cmd)
{ struct pmcraid_resource_entry *res;
u32 ioasc;
wait_for_completion(&cancel_cmd->wait_for_completion);
res = cancel_cmd->res;
cancel_cmd->res = NULL;
ioasc = le32_to_cpu(cancel_cmd->ioa_cb->ioasa.ioasc);
/* If the abort task is not timed out we will get a Good completion * as sense_key, otherwise we may get one the following responses * due to subsequent bus reset or device reset. In case IOASC is * NR_SYNC_REQUIRED, set sync_reqd flag for the corresponding resource
*/ if (ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET ||
ioasc == PMCRAID_IOASC_NR_SYNC_REQUIRED) { if (ioasc == PMCRAID_IOASC_NR_SYNC_REQUIRED)
res->sync_reqd = 1;
ioasc = 0;
}
/* complete the command here itself */
pmcraid_return_cmd(cancel_cmd); return PMCRAID_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS;
}
/** * pmcraid_eh_abort_handler - entry point for aborting a single task on errors * * @scsi_cmd: scsi command struct given by mid-layer. When this is called * mid-layer ensures that no other commands are queued. This * never gets called under interrupt, but a separate eh thread. * * Return value: * SUCCESS / FAILED
*/ staticint pmcraid_eh_abort_handler(struct scsi_cmnd *scsi_cmd)
{ struct pmcraid_instance *pinstance; struct pmcraid_cmd *cmd; struct pmcraid_resource_entry *res; unsignedlong host_lock_flags; unsignedlong pending_lock_flags; struct pmcraid_cmd *cancel_cmd = NULL; int cmd_found = 0; int rc = FAILED;
/* If we are currently going through reset/reload, return failed. * This will force the mid-layer to eventually call * pmcraid_eh_host_reset which will then go to sleep and wait for the * reset to complete
*/
spin_lock_irqsave(pinstance->host->host_lock, host_lock_flags);
/* loop over pending cmd list to find cmd corresponding to this * scsi_cmd. Note that this command might not have been completed * already. locking: all pending commands are protected with * pending_pool_lock.
*/
spin_lock_irqsave(&pinstance->pending_pool_lock, pending_lock_flags);
list_for_each_entry(cmd, &pinstance->pending_cmd_pool, free_list) {
/* If the command to be aborted was given to IOA and still pending with * it, send ABORT_TASK to abort this and wait for its completion
*/ if (cmd_found)
cancel_cmd = pmcraid_abort_cmd(cmd);
if (cancel_cmd) {
cancel_cmd->res = cmd->scsi_cmd->device->hostdata;
rc = pmcraid_abort_complete(cancel_cmd);
}
return cmd_found ? rc : SUCCESS;
}
/** * pmcraid_eh_device_reset_handler - bus/target/device reset handler callbacks * * @scmd: pointer to scsi_cmd that was sent to the resource to be reset. * * All these routines invokve pmcraid_reset_device with appropriate parameters. * Since these are called from mid-layer EH thread, no other IO will be queued * to the resource being reset. However, control path (IOCTL) may be active so * it is necessary to synchronize IOARRIN writes which pmcraid_reset_device * takes care by locking/unlocking host_lock. * * Return value * SUCCESS or FAILED
*/ staticint pmcraid_eh_device_reset_handler(struct scsi_cmnd *scmd)
{
scmd_printk(KERN_INFO, scmd, "resetting device due to an I/O command timeout.\n"); return pmcraid_reset_device(scmd->device,
PMCRAID_INTERNAL_TIMEOUT,
RESET_DEVICE_LUN);
}
/* * The reset device code insists on us passing down * a device, so grab the first device on the bus.
*/
spin_lock_irqsave(&pinstance->resource_lock, lock_flags);
list_for_each_entry(temp, &pinstance->used_res_q, queue) { if (scmd->device->channel == PMCRAID_VSET_BUS_ID &&
RES_IS_VSET(temp->cfg_entry)) {
res = temp; break;
} elseif (scmd->device->channel == PMCRAID_PHYS_BUS_ID &&
RES_IS_GSCSI(temp->cfg_entry)) {
res = temp; break;
}
} if (res)
sdev = res->scsi_dev;
spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags); if (!sdev) return FAILED;
sdev_printk(KERN_INFO, sdev, "Doing bus reset due to an I/O command timeout.\n"); return pmcraid_reset_device(sdev,
PMCRAID_RESET_BUS_TIMEOUT,
RESET_DEVICE_BUS);
}
shost_for_each_device(tmp, shost) { if ((tmp->channel == scmd->device->channel) &&
(tmp->id == scmd->device->id)) {
scsi_dev = tmp; break;
}
} if (!scsi_dev) return FAILED;
sdev_printk(KERN_INFO, scsi_dev, "Doing target reset due to an I/O command timeout.\n");
ret = pmcraid_reset_device(scsi_dev,
PMCRAID_INTERNAL_TIMEOUT,
RESET_DEVICE_TARGET);
scsi_device_put(scsi_dev); return ret;
}
/** * pmcraid_eh_host_reset_handler - adapter reset handler callback * * @scmd: pointer to scsi_cmd that was sent to a resource of adapter * * Initiates adapter reset to bring it up to operational state * * Return value * SUCCESS or FAILED
*/ staticint pmcraid_eh_host_reset_handler(struct scsi_cmnd *scmd)
{ unsignedlong interval = 10000; /* 10 seconds interval */ int waits = jiffies_to_msecs(PMCRAID_RESET_HOST_TIMEOUT) / interval; struct pmcraid_instance *pinstance =
(struct pmcraid_instance *)(scmd->device->host->hostdata);
/* wait for an additional 150 seconds just in case firmware could come * up and if it could complete all the pending commands excluding the * two HCAM (CCN and LDN).
*/ while (waits--) { if (atomic_read(&pinstance->outstanding_cmds) <=
PMCRAID_MAX_HCAM_CMD) return SUCCESS;
msleep(interval);
}
dev_err(&pinstance->pdev->dev, "Adapter being reset due to an I/O command timeout.\n"); return pmcraid_reset_bringup(pinstance) == 0 ? SUCCESS : FAILED;
}
/** * pmcraid_init_ioadls - initializes IOADL related fields in IOARCB * @cmd: pmcraid command struct * @sgcount: count of scatter-gather elements * * Return value * returns pointer pmcraid_ioadl_desc, initialized to point to internal * or external IOADLs
*/ staticstruct pmcraid_ioadl_desc *
pmcraid_init_ioadls(struct pmcraid_cmd *cmd, int sgcount)
{ struct pmcraid_ioadl_desc *ioadl; struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb; int ioadl_count = 0;
if ((sgcount + ioadl_count) > (ARRAY_SIZE(ioarcb->add_data.u.ioadl))) { /* external ioadls start at offset 0x80 from control_block * structure, re-using 24 out of 27 ioadls part of IOARCB. * It is necessary to indicate to firmware that driver is * using ioadls to be treated as external to IOARCB.
*/
ioarcb->ioarcb_bus_addr &= cpu_to_le64(~(0x1FULL));
ioarcb->ioadl_bus_addr =
cpu_to_le64((cmd->ioa_cb_bus_addr) +
offsetof(struct pmcraid_ioarcb,
add_data.u.ioadl[3]));
ioadl = &ioarcb->add_data.u.ioadl[3];
} else {
ioarcb->ioadl_bus_addr =
cpu_to_le64((cmd->ioa_cb_bus_addr) +
offsetof(struct pmcraid_ioarcb,
add_data.u.ioadl[ioadl_count]));
/** * pmcraid_build_ioadl - Build a scatter/gather list and map the buffer * @pinstance: pointer to adapter instance structure * @cmd: pmcraid command struct * * This function is invoked by queuecommand entry point while sending a command * to firmware. This builds ioadl descriptors and sets up ioarcb fields. * * Return value: * 0 on success or -1 on failure
*/ staticint pmcraid_build_ioadl( struct pmcraid_instance *pinstance, struct pmcraid_cmd *cmd
)
{ int i, nseg; struct scatterlist *sglist;
/** * pmcraid_queuecommand_lck - Queue a mid-layer request * @scsi_cmd: scsi command struct * * This function queues a request generated by the mid-layer. Midlayer calls * this routine within host->lock. Some of the functions called by queuecommand * would use cmd block queue locks (free_pool_lock and pending_pool_lock) * * Return value: * 0 on success * SCSI_MLQUEUE_DEVICE_BUSY if device is busy * SCSI_MLQUEUE_HOST_BUSY if host is busy
*/ staticint pmcraid_queuecommand_lck(struct scsi_cmnd *scsi_cmd)
{ struct pmcraid_instance *pinstance; struct pmcraid_resource_entry *res; struct pmcraid_ioarcb *ioarcb; struct pmcraid_cmd *cmd;
u32 fw_version; int rc = 0;
/* if adapter is marked as dead, set result to DID_NO_CONNECT complete * the command
*/ if (pinstance->ioa_state == IOA_STATE_DEAD) {
pmcraid_info("IOA is dead, but queuecommand is scheduled\n");
scsi_cmd->result = (DID_NO_CONNECT << 16);
scsi_done(scsi_cmd); return 0;
}
/* If IOA reset is in progress, can't queue the commands */ if (pinstance->ioa_reset_in_progress) return SCSI_MLQUEUE_HOST_BUSY;
/* Firmware doesn't support SYNCHRONIZE_CACHE command (0x35), complete * the command here itself with success return
*/ if (scsi_cmd->cmnd[0] == SYNCHRONIZE_CACHE) {
pmcraid_info("SYNC_CACHE(0x35), completing in driver itself\n");
scsi_done(scsi_cmd); return 0;
}
/* initialize the command and IOARCB to be sent to IOA */
cmd = pmcraid_get_free_cmd(pinstance);
if (cmd == NULL) {
pmcraid_err("free command block is not available\n"); return SCSI_MLQUEUE_HOST_BUSY;
}
/* set hrrq number where the IOA should respond to. Note that all cmds * generated internally uses hrrq_id 0, exception to this is the cmd * block of scsi_cmd which is re-used (e.g. cancel/abort), which uses * hrrq_id assigned here in queuecommand
*/
ioarcb->hrrq_id = atomic_add_return(1, &(pinstance->last_message_id)) %
pinstance->num_hrrq;
cmd->cmd_done = pmcraid_io_done;
if (RES_IS_GSCSI(res->cfg_entry) || RES_IS_VSET(res->cfg_entry)) { if (scsi_cmd->underflow == 0)
ioarcb->request_flags0 |= INHIBIT_UL_CHECK;
if (res->sync_reqd) {
ioarcb->request_flags0 |= SYNC_COMPLETE;
res->sync_reqd = 0;
}
ioarcb->request_flags0 |= NO_LINK_DESCS;
if (scsi_cmd->flags & SCMD_TAGGED)
ioarcb->request_flags1 |= TASK_TAG_SIMPLE;
if (RES_IS_GSCSI(res->cfg_entry))
ioarcb->request_flags1 |= DELAY_AFTER_RESET;
}
/* Populate adapter instance * pointer for use by ioctl */
pinstance = container_of(inode->i_cdev, struct pmcraid_instance, cdev);
filep->private_data = pinstance;
return 0;
}
/* * pmcraid_fasync - Async notifier registration from applications * * This function adds the calling process to a driver global queue. When an * event occurs, SIGIO will be sent to all processes in this queue.
*/ staticint pmcraid_chr_fasync(int fd, struct file *filep, int mode)
{ struct pmcraid_instance *pinstance; int rc;
/** * pmcraid_ioctl_driver - ioctl handler for commands handled by driver itself * * @pinstance: pointer to adapter instance structure * @cmd: ioctl command passed in * @buflen: length of user_buffer * @user_buffer: user buffer pointer * * Return Value * 0 in case of success, otherwise appropriate error code
*/ staticlong pmcraid_ioctl_driver( struct pmcraid_instance *pinstance, unsignedint cmd, unsignedint buflen, void __user *user_buffer
)
{ int rc = -ENOSYS;
switch (cmd) { case PMCRAID_IOCTL_RESET_ADAPTER:
pmcraid_reset_bringup(pinstance);
rc = 0; break;
default: break;
}
return rc;
}
/** * pmcraid_check_ioctl_buffer - check for proper access to user buffer * * @cmd: ioctl command * @arg: user buffer * @hdr: pointer to kernel memory for pmcraid_ioctl_header * * Return Value * negetive error code if there are access issues, otherwise zero. * Upon success, returns ioctl header copied out of user buffer.
*/
staticint pmcraid_check_ioctl_buffer( int cmd, void __user *arg, struct pmcraid_ioctl_header *hdr
)
{ int rc;
if (copy_from_user(hdr, arg, sizeof(struct pmcraid_ioctl_header))) {
pmcraid_err("couldn't copy ioctl header from user buffer\n"); return -EFAULT;
}
if (!hrrq_id) { /* Read the interrupt */
intrs_val = pmcraid_read_interrupts(pinstance); if (intrs_val &&
((ioread32(pinstance->int_regs.host_ioa_interrupt_reg)
& DOORBELL_INTR_MSIX_CLR) == 0)) { /* Any error interrupts including unit_check, * initiate IOA reset.In case of unit check indicate * to reset_sequence that IOA unit checked and prepare * for a dump during reset sequence
*/ if (intrs_val & PMCRAID_ERROR_INTERRUPTS) { if (intrs_val & INTRS_IOA_UNIT_CHECK)
pinstance->ioa_unit_check = 1;
pmcraid_err("ISR: error interrupts: %x \
initiating reset\n", intrs_val);
spin_lock_irqsave(pinstance->host->host_lock,
lock_flags);
pmcraid_initiate_reset(pinstance);
spin_unlock_irqrestore(
pinstance->host->host_lock,
lock_flags);
} /* If interrupt was as part of the ioa initialization, * clear it. Delete the timer and wakeup the * reset engine to proceed with reset sequence
*/ if (intrs_val & INTRS_TRANSITION_TO_OPERATIONAL)
pmcraid_clr_trans_op(pinstance);
/* Clear the interrupt register by writing * to host to ioa doorbell. Once done * FW will clear the interrupt.
*/
iowrite32(DOORBELL_INTR_MSIX_CLR,
pinstance->int_regs.host_ioa_interrupt_reg);
ioread32(pinstance->int_regs.host_ioa_interrupt_reg);
/** * pmcraid_isr - implements legacy interrupt handling routine * * @irq: interrupt vector number * @dev_id: pointer hrrq_vector * * Return Value * IRQ_HANDLED if interrupt is handled or IRQ_NONE if ignored
*/ static irqreturn_t pmcraid_isr(int irq, void *dev_id)
{ struct pmcraid_isr_param *hrrq_vector; struct pmcraid_instance *pinstance;
u32 intrs; unsignedlong lock_flags; int hrrq_id = 0;
/* In case of legacy interrupt mode where interrupts are shared across * isrs, it may be possible that the current interrupt is not from IOA
*/ if (!dev_id) {
printk(KERN_INFO "%s(): NULL host pointer\n", __func__); return IRQ_NONE;
}
hrrq_vector = (struct pmcraid_isr_param *)dev_id;
pinstance = hrrq_vector->drv_inst;
intrs = pmcraid_read_interrupts(pinstance);
if (unlikely((intrs & PMCRAID_PCI_INTERRUPTS) == 0)) return IRQ_NONE;
/* Any error interrupts including unit_check, initiate IOA reset. * In case of unit check indicate to reset_sequence that IOA unit * checked and prepare for a dump during reset sequence
*/ if (intrs & PMCRAID_ERROR_INTERRUPTS) {
if (intrs & INTRS_IOA_UNIT_CHECK)
pinstance->ioa_unit_check = 1;
iowrite32(intrs,
pinstance->int_regs.ioa_host_interrupt_clr_reg);
pmcraid_err("ISR: error interrupts: %x initiating reset\n",
intrs);
intrs = ioread32(
pinstance->int_regs.ioa_host_interrupt_clr_reg);
spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
pmcraid_initiate_reset(pinstance);
spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
} else { /* If interrupt was as part of the ioa initialization, * clear. Delete the timer and wakeup the * reset engine to proceed with reset sequence
*/ if (intrs & INTRS_TRANSITION_TO_OPERATIONAL) {
pmcraid_clr_trans_op(pinstance);
} else {
iowrite32(intrs,
pinstance->int_regs.ioa_host_interrupt_clr_reg);
ioread32(
pinstance->int_regs.ioa_host_interrupt_clr_reg);
pinstance = container_of(workp, struct pmcraid_instance, worker_q); /* add resources only after host is added into system */ if (!atomic_read(&pinstance->expose_resources)) return;
/* loop through each of the commands responded by IOA. Each HRRQ buf is * protected by its own lock. Traversals must be done within this lock * as there may be multiple tasklets running on multiple CPUs. Note * that the lock is held just for picking up the response handle and * manipulating hrrq_curr/toggle_bit values.
*/
spin_lock_irqsave(lockp, hrrq_lock_flags);
resp = le32_to_cpu(*(pinstance->hrrq_curr[id]));
while ((resp & HRRQ_TOGGLE_BIT) ==
pinstance->host_toggle_bit[id]) {
if (cmd->cmd_done == pmcraid_ioa_reset) {
spin_lock_irqsave(pinstance->host->host_lock,
host_lock_flags);
cmd->cmd_done(cmd);
spin_unlock_irqrestore(pinstance->host->host_lock,
host_lock_flags);
} elseif (cmd->cmd_done != NULL) {
cmd->cmd_done(cmd);
} /* loop over until we are done with all responses */
spin_lock_irqsave(lockp, hrrq_lock_flags);
resp = le32_to_cpu(*(pinstance->hrrq_curr[id]));
}
spin_unlock_irqrestore(lockp, hrrq_lock_flags);
}
/** * pmcraid_unregister_interrupt_handler - de-register interrupts handlers * @pinstance: pointer to adapter instance structure * * This routine un-registers registered interrupt handler and * also frees irqs/vectors. * * Return Value * None
*/ static void pmcraid_unregister_interrupt_handler(struct pmcraid_instance *pinstance)
{ struct pci_dev *pdev = pinstance->pdev; int i;
for (i = 0; i < pinstance->num_hrrq; i++)
free_irq(pci_irq_vector(pdev, i), &pinstance->hrrq_vector[i]);
/** * pmcraid_release_cmd_blocks - release buufers allocated for command blocks * @pinstance: per adapter instance structure pointer * @max_index: number of buffer blocks to release * * Return Value * None
*/ staticvoid
pmcraid_release_cmd_blocks(struct pmcraid_instance *pinstance, int max_index)
{ int i; for (i = 0; i < max_index; i++) {
kmem_cache_free(pinstance->cmd_cachep, pinstance->cmd_list[i]);
pinstance->cmd_list[i] = NULL;
}
kmem_cache_destroy(pinstance->cmd_cachep);
pinstance->cmd_cachep = NULL;
}
/** * pmcraid_release_control_blocks - releases buffers alloced for control blocks * @pinstance: pointer to per adapter instance structure * @max_index: number of buffers (from 0 onwards) to release * * This function assumes that the command blocks for which control blocks are * linked are not released. * * Return Value * None
*/ staticvoid
pmcraid_release_control_blocks( struct pmcraid_instance *pinstance, int max_index
)
{ int i;
if (pinstance->control_pool == NULL) return;
for (i = 0; i < max_index; i++) {
dma_pool_free(pinstance->control_pool,
pinstance->cmd_list[i]->ioa_cb,
pinstance->cmd_list[i]->ioa_cb_bus_addr);
pinstance->cmd_list[i]->ioa_cb = NULL;
pinstance->cmd_list[i]->ioa_cb_bus_addr = 0;
}
dma_pool_destroy(pinstance->control_pool);
pinstance->control_pool = NULL;
}
/** * pmcraid_allocate_cmd_blocks - allocate memory for cmd block structures * @pinstance: pointer to per adapter instance structure * * Allocates memory for command blocks using kernel slab allocator. * * Return Value * 0 in case of success; -ENOMEM in case of failure
*/ staticint pmcraid_allocate_cmd_blocks(struct pmcraid_instance *pinstance)
{ int i;
for (i = 0; i < PMCRAID_MAX_CMD; i++) {
pinstance->cmd_list[i] =
kmem_cache_alloc(pinstance->cmd_cachep, GFP_KERNEL); if (!pinstance->cmd_list[i]) {
pmcraid_release_cmd_blocks(pinstance, i); return -ENOMEM;
}
} return 0;
}
/** * pmcraid_allocate_control_blocks - allocates memory control blocks * @pinstance : pointer to per adapter instance structure * * This function allocates PCI memory for DMAable buffers like IOARCB, IOADLs * and IOASAs. This is called after command blocks are already allocated. * * Return Value * 0 in case it can allocate all control blocks, otherwise -ENOMEM
*/ staticint pmcraid_allocate_control_blocks(struct pmcraid_instance *pinstance)
{ int i;
for (i = 0; i < PMCRAID_MAX_CMD; i++) {
pinstance->cmd_list[i]->ioa_cb =
dma_pool_zalloc(
pinstance->control_pool,
GFP_KERNEL,
&(pinstance->cmd_list[i]->ioa_cb_bus_addr));
/** * pmcraid_release_host_rrqs - release memory allocated for hrrq buffer(s) * @pinstance: pointer to per adapter instance structure * @maxindex: size of hrrq buffer pointer array * * Return Value * None
*/ staticvoid
pmcraid_release_host_rrqs(struct pmcraid_instance *pinstance, int maxindex)
{ int i;
for (i = 0; i < maxindex; i++) {
dma_free_coherent(&pinstance->pdev->dev,
HRRQ_ENTRY_SIZE * PMCRAID_MAX_CMD,
pinstance->hrrq_start[i],
pinstance->hrrq_start_bus_addr[i]);
/* reset pointers and toggle bit to zeros */
pinstance->hrrq_start[i] = NULL;
pinstance->hrrq_start_bus_addr[i] = 0;
pinstance->host_toggle_bit[i] = 0;
}
}
/** * pmcraid_allocate_host_rrqs - Allocate and initialize host RRQ buffers * @pinstance: pointer to per adapter instance structure * * Return value * 0 hrrq buffers are allocated, -ENOMEM otherwise.
*/ staticint pmcraid_allocate_host_rrqs(struct pmcraid_instance *pinstance)
{ int i, buffer_size;
buffer_size = HRRQ_ENTRY_SIZE * PMCRAID_MAX_CMD;
for (i = 0; i < pinstance->num_hrrq; i++) {
pinstance->hrrq_start[i] =
dma_alloc_coherent(&pinstance->pdev->dev, buffer_size,
&pinstance->hrrq_start_bus_addr[i],
GFP_KERNEL); if (!pinstance->hrrq_start[i]) {
pmcraid_err("pci_alloc failed for hrrq vector : %d\n",
i);
pmcraid_release_host_rrqs(pinstance, i); return -ENOMEM;
}
for (i = 0; i < PMCRAID_MAX_RESOURCES; i++)
list_del(&pinstance->res_entries[i].queue);
kfree(pinstance->res_entries);
pinstance->res_entries = NULL;
}
pmcraid_release_hcams(pinstance);
}
/** * pmcraid_allocate_config_buffers - allocates DMAable memory for config table * @pinstance : pointer to per adapter instance structure * * Return Value * 0 for successful allocation, -ENOMEM for any failure
*/ staticint pmcraid_allocate_config_buffers(struct pmcraid_instance *pinstance)
{ int i;
if (NULL == pinstance->cfg_table) {
pmcraid_err("couldn't alloc DMA memory for config table\n");
pmcraid_release_config_buffers(pinstance); return -ENOMEM;
}
if (pmcraid_allocate_hcams(pinstance)) {
pmcraid_err("could not alloc DMA memory for HCAMS\n");
pmcraid_release_config_buffers(pinstance); return -ENOMEM;
}
return 0;
}
/** * pmcraid_init_tasklets - registers tasklets for response handling * * @pinstance: pointer adapter instance structure * * Return value * none
*/ staticvoid pmcraid_init_tasklets(struct pmcraid_instance *pinstance)
{ int i; for (i = 0; i < pinstance->num_hrrq; i++)
tasklet_init(&pinstance->isr_tasklet[i],
pmcraid_tasklet_function,
(unsignedlong)&pinstance->hrrq_vector[i]);
}
/** * pmcraid_kill_tasklets - destroys tasklets registered for response handling * * @pinstance: pointer to adapter instance structure * * Return value * none
*/ staticvoid pmcraid_kill_tasklets(struct pmcraid_instance *pinstance)
{ int i; for (i = 0; i < pinstance->num_hrrq; i++)
tasklet_kill(&pinstance->isr_tasklet[i]);
}
/** * pmcraid_init_buffers - allocates memory and initializes various structures * @pinstance: pointer to per adapter instance structure * * This routine pre-allocates memory based on the type of block as below: * cmdblocks(PMCRAID_MAX_CMD): kernel memory using kernel's slab_allocator, * IOARCBs(PMCRAID_MAX_CMD) : DMAable memory, using pci pool allocator * config-table entries : DMAable memory using dma_alloc_coherent * HostRRQs : DMAable memory, using dma_alloc_coherent * * Return Value * 0 in case all of the blocks are allocated, -ENOMEM otherwise.
*/ staticint pmcraid_init_buffers(struct pmcraid_instance *pinstance)
{ int i;
if (pmcraid_allocate_host_rrqs(pinstance)) {
pmcraid_err("couldn't allocate memory for %d host rrqs\n",
pinstance->num_hrrq); return -ENOMEM;
}
if (pmcraid_allocate_config_buffers(pinstance)) {
pmcraid_err("couldn't allocate memory for config buffers\n");
pmcraid_release_host_rrqs(pinstance, pinstance->num_hrrq); return -ENOMEM;
}
if (pmcraid_allocate_cmd_blocks(pinstance)) {
pmcraid_err("couldn't allocate memory for cmd blocks\n");
pmcraid_release_config_buffers(pinstance);
pmcraid_release_host_rrqs(pinstance, pinstance->num_hrrq); return -ENOMEM;
}
if (pmcraid_allocate_control_blocks(pinstance)) {
pmcraid_err("couldn't allocate memory control blocks\n");
pmcraid_release_config_buffers(pinstance);
pmcraid_release_cmd_blocks(pinstance, PMCRAID_MAX_CMD);
pmcraid_release_host_rrqs(pinstance, pinstance->num_hrrq); return -ENOMEM;
}
/* allocate DMAable memory for set timestamp data buffer */
pinstance->timestamp_data = dma_alloc_coherent(&pinstance->pdev->dev, sizeof(struct pmcraid_timestamp_data),
&pinstance->timestamp_data_baddr,
GFP_KERNEL); if (pinstance->timestamp_data == NULL) {
pmcraid_err("couldn't allocate DMA memory for \
set time_stamp \n");
pmcraid_release_buffers(pinstance); return -ENOMEM;
}
/* Initialize all the command blocks and add them to free pool. No * need to lock (free_pool_lock) as this is done in initialization * itself
*/ for (i = 0; i < PMCRAID_MAX_CMD; i++) { struct pmcraid_cmd *cmdp = pinstance->cmd_list[i];
pmcraid_init_cmdblk(cmdp, i);
cmdp->drv_inst = pinstance;
list_add_tail(&cmdp->free_list, &pinstance->free_cmd_pool);
}
return 0;
}
/** * pmcraid_reinit_buffers - resets various buffer pointers * @pinstance: pointer to adapter instance * Return value * none
*/ staticvoid pmcraid_reinit_buffers(struct pmcraid_instance *pinstance)
{ int i; int buffer_size = HRRQ_ENTRY_SIZE * PMCRAID_MAX_CMD;
for (i = 0; i < pinstance->num_hrrq; i++) {
memset(pinstance->hrrq_start[i], 0, buffer_size);
pinstance->hrrq_curr[i] = pinstance->hrrq_start[i];
pinstance->hrrq_end[i] =
pinstance->hrrq_start[i] + PMCRAID_MAX_CMD - 1;
pinstance->host_toggle_bit[i] = 1;
}
}
/** * pmcraid_init_instance - initialize per instance data structure * @pdev: pointer to pci device structure * @host: pointer to Scsi_Host structure * @mapped_pci_addr: memory mapped IOA configuration registers * * Return Value * 0 on success, non-zero in case of any failure
*/ staticint pmcraid_init_instance(struct pci_dev *pdev, struct Scsi_Host *host, void __iomem *mapped_pci_addr)
{ struct pmcraid_instance *pinstance =
(struct pmcraid_instance *)host->hostdata;
/** * pmcraid_shutdown - shutdown adapter controller. * @pdev: pci device struct * * Issues an adapter shutdown to the card waits for its completion * * Return value * none
*/ staticvoid pmcraid_shutdown(struct pci_dev *pdev)
{ struct pmcraid_instance *pinstance = pci_get_drvdata(pdev);
pmcraid_reset_bringdown(pinstance);
}
/* * pmcraid_get_minor - returns unused minor number from minor number bitmap
*/ staticunsignedshort pmcraid_get_minor(void)
{ int minor;
minor = find_first_zero_bit(pmcraid_minor, PMCRAID_MAX_ADAPTERS);
__set_bit(minor, pmcraid_minor); return minor;
}
/* * pmcraid_release_minor - releases given minor back to minor number bitmap
*/ staticvoid pmcraid_release_minor(unsignedshort minor)
{
__clear_bit(minor, pmcraid_minor);
}
/** * pmcraid_setup_chrdev - allocates a minor number and registers a char device * * @pinstance: pointer to adapter instance for which to register device * * Return value * 0 in case of success, otherwise non-zero
*/ staticint pmcraid_setup_chrdev(struct pmcraid_instance *pinstance)
{ int minor; int error;
minor = pmcraid_get_minor();
cdev_init(&pinstance->cdev, &pmcraid_fops);
pinstance->cdev.owner = THIS_MODULE;
/* Start with hard reset sequence which brings up IOA to operational * state as well as completes the reset sequence.
*/
pinstance->ioa_hard_reset = 1;
/* Start IOA firmware initialization and bring card to Operational * state.
*/ if (pmcraid_reset_bringup(pinstance)) {
dev_err(&pdev->dev, "couldn't initialize IOA\n");
rc = -ENODEV; goto release_tasklets;
}
/** * pmcraid_complete_ioa_reset - Called by either timer or tasklet during * completion of the ioa reset * @cmd: pointer to reset command block
*/ staticvoid pmcraid_complete_ioa_reset(struct pmcraid_cmd *cmd)
{ struct pmcraid_instance *pinstance = cmd->drv_inst; unsignedlong flags;
/* If this was called as part of resource table reinitialization due to * lost CCN, it is enough to return the command block back to free pool * as part of set_supported_devs completion function.
*/ if (cmd->drv_inst->reinit_cfg_table) {
cmd->drv_inst->reinit_cfg_table = 0;
cmd->release = 1;
cmd_done = pmcraid_reinit_cfgtable_done;
}
/* we will be done with the reset sequence after set supported devices, * setup the done function to return the command block back to free * pool
*/
pmcraid_send_cmd(cmd,
cmd_done,
PMCRAID_SET_SUP_DEV_TIMEOUT,
pmcraid_timeout_handler); return;
}
/** * pmcraid_set_timestamp - set the timestamp to IOAFP * * @cmd: pointer to pmcraid_cmd structure * * Return Value * 0 for success or non-zero for failure cases
*/ staticvoid pmcraid_set_timestamp(struct pmcraid_cmd *cmd)
{ struct pmcraid_instance *pinstance = cmd->drv_inst; struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
__be32 time_stamp_len = cpu_to_be32(PMCRAID_TIMESTAMP_LEN); struct pmcraid_ioadl_desc *ioadl;
u64 timestamp;
/** * pmcraid_init_res_table - Initialize the resource table * @cmd: pointer to pmcraid command struct * * This function looks through the existing resource table, comparing * it with the config table. This function will take care of old/new * devices and schedule adding/removing them from the mid-layer * as appropriate. * * Return value * None
*/ staticvoid pmcraid_init_res_table(struct pmcraid_cmd *cmd)
{ struct pmcraid_instance *pinstance = cmd->drv_inst; struct pmcraid_resource_entry *res, *temp; struct pmcraid_config_table_entry *cfgte; unsignedlong lock_flags; int found, rc, i;
u16 fw_version;
LIST_HEAD(old_res);
if (pinstance->cfg_table->flags & MICROCODE_UPDATE_REQUIRED)
pmcraid_err("IOA requires microcode download\n");
/* resource list is protected by pinstance->resource_lock. * init_res_table can be called from probe (user-thread) or runtime * reset (timer/tasklet)
*/
spin_lock_irqsave(&pinstance->resource_lock, lock_flags);
/* firmware requires 4-byte length field, specified in B.E format */
memcpy(&(ioarcb->cdb[10]), &cfg_table_size, sizeof(cfg_table_size));
/* Since entire config table can be described by single IOADL, it can * be part of IOARCB itself
*/
ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) +
offsetof(struct pmcraid_ioarcb,
add_data.u.ioadl[0]));
ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc));
ioarcb->ioarcb_bus_addr &= cpu_to_le64(~0x1FULL);
/* Firmware requires the system bus address of IOARCB to be within * 32-bit addressable range though it has 64-bit IOARRIN register. * However, firmware supports 64-bit streaming DMA buffers, whereas * coherent buffers are to be 32-bit. Since dma_alloc_coherent always * returns memory within 4GB (if not, change this logic), coherent * buffers are within firmware acceptable address ranges.
*/ if (sizeof(dma_addr_t) == 4 ||
dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)))
rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
/* firmware expects 32-bit DMA addresses for IOARRIN register; set 32 * bit mask for dma_alloc_coherent to return addresses within 4GB
*/ if (rc == 0)
rc = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
if (rc != 0) {
dev_err(&pdev->dev, "Failed to set PCI DMA mask\n"); goto cleanup_nomem;
}
/* Schedule worker thread to handle CCN and take care of adding and * removing devices to OS
*/
atomic_set(&pinstance->expose_resources, 1);
schedule_work(&pinstance->worker_q); return rc;
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.112Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-04-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.