/* * QLogic iSCSI HBA Driver * Copyright (c) 2003-2013 QLogic Corporation * * See LICENSE.qla4xxx for copyright and licensing details. */ #include "ql4_def.h" #include "ql4_glbl.h" #include "ql4_dbg.h" #include "ql4_inline.h" /** * qla4xxx_copy_sense - copy sense data into cmd sense buffer * @ha: Pointer to host adapter structure. * @sts_entry: Pointer to status entry structure. * @srb: Pointer to srb structure. **/ static void qla4xxx_copy_sense(struct scsi_qla_host *ha, struct status_entry *sts_entry, struct srb *srb) { struct scsi_cmnd *cmd = srb->cmd; uint16_t sense_len; memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); sense_len = le16_to_cpu(sts_entry->senseDataByteCnt); if (sense_len == 0) { DEBUG2(ql4_printk(KERN_INFO, ha, "scsi%ld:%d:%d:%llu: %s:" " sense len 0\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun, __func__)); ha->status_srb = NULL; return; } /* Save total available sense length, * not to exceed cmd's sense buffer size */ sense_len = min_t(uint16_t, sense_len, SCSI_SENSE_BUFFERSIZE); srb->req_sense_ptr = cmd->sense_buffer; srb->req_sense_len = sense_len; /* Copy sense from sts_entry pkt */ sense_len = min_t(uint16_t, sense_len, IOCB_MAX_SENSEDATA_LEN); memcpy(cmd->sense_buffer, sts_entry->senseData, sense_len); DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%llu: %s: sense key = %x, " "ASL= %02x, ASC/ASCQ = %02x/%02x\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun, __func__, sts_entry->senseData[2] & 0x0f, sts_entry->senseData[7], sts_entry->senseData[12], sts_entry->senseData[13])); DEBUG5(qla4xxx_dump_buffer(cmd->sense_buffer, sense_len)); srb->flags |= SRB_GOT_SENSE; /* Update srb, in case a sts_cont pkt follows */ srb->req_sense_ptr += sense_len; srb->req_sense_len -= sense_len; if (srb->req_sense_len != 0) ha->status_srb = srb; else ha->status_srb = NULL; } /** * qla4xxx_status_cont_entry - Process a Status Continuations entry. * @ha: SCSI driver HA context * @sts_cont: Entry pointer * * Extended sense data. */ static void qla4xxx_status_cont_entry(struct scsi_qla_host *ha, struct status_cont_entry *sts_cont) { struct srb *srb = ha->status_srb; struct scsi_cmnd *cmd; uint16_t sense_len; if (srb == NULL) return; cmd = srb->cmd; if (cmd == NULL) { DEBUG2(printk(KERN_INFO "scsi%ld: %s: Cmd already returned " "back to OS srb=%p srb->state:%d\n", ha->host_no, __func__, srb, srb->state)); ha->status_srb = NULL; return; } /* Copy sense data. */ sense_len = min_t(uint16_t, srb->req_sense_len, IOCB_MAX_EXT_SENSEDATA_LEN); memcpy(srb->req_sense_ptr, sts_cont->ext_sense_data, sense_len); DEBUG5(qla4xxx_dump_buffer(srb->req_sense_ptr, sense_len)); srb->req_sense_ptr += sense_len; srb->req_sense_len -= sense_len; /* Place command on done queue. */ if (srb->req_sense_len == 0) { kref_put(&srb->srb_ref, qla4xxx_srb_compl); ha->status_srb = NULL; } } /** * qla4xxx_status_entry - processes status IOCBs * @ha: Pointer to host adapter structure. * @sts_entry: Pointer to status entry structure. **/ static void qla4xxx_status_entry(struct scsi_qla_host *ha, struct status_entry *sts_entry) { uint8_t scsi_status; struct scsi_cmnd *cmd; struct srb *srb; struct ddb_entry *ddb_entry; uint32_t residual; srb = qla4xxx_del_from_active_array(ha, le32_to_cpu(sts_entry->handle)); if (!srb) { ql4_printk(KERN_WARNING, ha, "%s invalid status entry: " "handle=0x%0x, srb=%p\n", __func__, sts_entry->handle, srb); if (is_qla80XX(ha)) set_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags); else set_bit(DPC_RESET_HA, &ha->dpc_flags); return; } cmd = srb->cmd; if (cmd == NULL) { DEBUG2(printk("scsi%ld: %s: Command already returned back to " "OS pkt->handle=%d srb=%p srb->state:%d\n", ha->host_no, __func__, sts_entry->handle, srb, srb->state)); ql4_printk(KERN_WARNING, ha, "Command is NULL:" " already returned to OS (srb=%p)\n", srb); return; } ddb_entry = srb->ddb; if (ddb_entry == NULL) { cmd->result = DID_NO_CONNECT << 16; goto status_entry_exit; } residual = le32_to_cpu(sts_entry->residualByteCnt); /* Translate ISP error to a Linux SCSI error. */ scsi_status = sts_entry->scsiStatus; switch (sts_entry->completionStatus) { case SCS_COMPLETE: if (sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_OVER) { cmd->result = DID_ERROR << 16; break; } if (sts_entry->iscsiFlags &ISCSI_FLAG_RESIDUAL_UNDER) { scsi_set_resid(cmd, residual); if (!scsi_status && ((scsi_bufflen(cmd) - residual) < cmd->underflow)) { cmd->result = DID_ERROR << 16; DEBUG2(printk("scsi%ld:%d:%d:%llu: %s: " "Mid-layer Data underrun0, " "xferlen = 0x%x, " "residual = 0x%x\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun, __func__, scsi_bufflen(cmd), residual)); break; } } cmd->result = DID_OK << 16 | scsi_status; if (scsi_status != SCSI_CHECK_CONDITION) break; /* Copy Sense Data into sense buffer. */ qla4xxx_copy_sense(ha, sts_entry, srb); break; case SCS_INCOMPLETE: /* Always set the status to DID_ERROR, since * all conditions result in that status anyway */ cmd->result = DID_ERROR << 16; break; case SCS_RESET_OCCURRED: DEBUG2(printk("scsi%ld:%d:%d:%llu: %s: Device RESET occurred\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun, __func__)); cmd->result = DID_RESET << 16; break; case SCS_ABORTED: DEBUG2(printk("scsi%ld:%d:%d:%llu: %s: Abort occurred\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun, __func__)); cmd->result = DID_RESET << 16; break; case SCS_TIMEOUT: DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%llu: Timeout\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun)); cmd->result = DID_TRANSPORT_DISRUPTED << 16; /* * Mark device missing so that we won't continue to send * I/O to this device. We should get a ddb state change * AEN soon. */ if (iscsi_is_session_online(ddb_entry->sess)) qla4xxx_mark_device_missing(ddb_entry->sess); break; case SCS_DATA_UNDERRUN: case SCS_DATA_OVERRUN: if ((sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_OVER) || (sts_entry->completionStatus == SCS_DATA_OVERRUN)) { DEBUG2(printk("scsi%ld:%d:%d:%llu: %s: " "Data overrun\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun, __func__)); cmd->result = DID_ERROR << 16; break; } scsi_set_resid(cmd, residual); if (sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_UNDER) { /* Both the firmware and target reported UNDERRUN: * * MID-LAYER UNDERFLOW case: * Some kernels do not properly detect midlayer * underflow, so we manually check it and return * ERROR if the minimum required data was not * received. * * ALL OTHER cases: * Fall thru to check scsi_status */ if (!scsi_status && (scsi_bufflen(cmd) - residual) < cmd->underflow) { DEBUG2(ql4_printk(KERN_INFO, ha, "scsi%ld:%d:%d:%llu: %s: Mid-layer Data underrun, xferlen = 0x%x,residual = 0x%x\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun, __func__, scsi_bufflen(cmd), residual)); cmd->result = DID_ERROR << 16; break; } } else if (scsi_status != SAM_STAT_TASK_SET_FULL && scsi_status != SAM_STAT_BUSY) { /* * The firmware reports UNDERRUN, but the target does * not report it: * * scsi_status | host_byte device_byte * | (19:16) (7:0) * ============= | ========= =========== * TASK_SET_FULL | DID_OK scsi_status * BUSY | DID_OK scsi_status * ALL OTHERS | DID_ERROR scsi_status * * Note: If scsi_status is task set full or busy, * then this else if would fall thru to check the * scsi_status and return DID_OK. */ DEBUG2(ql4_printk(KERN_INFO, ha, "scsi%ld:%d:%d:%llu: %s: Dropped frame(s) detected (0x%x of 0x%x bytes).\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun, __func__, residual, scsi_bufflen(cmd))); cmd->result = DID_ERROR << 16 | scsi_status; goto check_scsi_status; } cmd->result = DID_OK << 16 | scsi_status; check_scsi_status: if (scsi_status == SAM_STAT_CHECK_CONDITION) qla4xxx_copy_sense(ha, sts_entry, srb); break; case SCS_DEVICE_LOGGED_OUT: case SCS_DEVICE_UNAVAILABLE: DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%llu: SCS_DEVICE " "state: 0x%x\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun, sts_entry->completionStatus)); /* * Mark device missing so that we won't continue to * send I/O to this device. We should get a ddb * state change AEN soon. */ if (iscsi_is_session_online(ddb_entry->sess)) qla4xxx_mark_device_missing(ddb_entry->sess); cmd->result = DID_TRANSPORT_DISRUPTED << 16; break; case SCS_QUEUE_FULL: /* * SCSI Mid-Layer handles device queue full */ cmd->result = DID_OK << 16 | sts_entry->scsiStatus; DEBUG2(printk("scsi%ld:%d:%llu: %s: QUEUE FULL detected " "compl=%02x, scsi=%02x, state=%02x, iFlags=%02x," " iResp=%02x\n", ha->host_no, cmd->device->id, cmd->device->lun, __func__, sts_entry->completionStatus, sts_entry->scsiStatus, sts_entry->state_flags, sts_entry->iscsiFlags, sts_entry->iscsiResponse)); break; default: cmd->result = DID_ERROR << 16; break; } status_entry_exit: /* complete the request, if not waiting for status_continuation pkt */ srb->cc_stat = sts_entry->completionStatus; if (ha->status_srb == NULL) kref_put(&srb->srb_ref, qla4xxx_srb_compl); } /** * qla4xxx_passthru_status_entry - processes passthru status IOCBs (0x3C) * @ha: Pointer to host adapter structure. * @sts_entry: Pointer to status entry structure. **/ static void qla4xxx_passthru_status_entry(struct scsi_qla_host *ha, struct passthru_status *sts_entry) { struct iscsi_task *task; struct ddb_entry *ddb_entry; struct ql4_task_data *task_data; struct iscsi_cls_conn *cls_conn; struct iscsi_conn *conn; itt_t itt; uint32_t fw_ddb_index; itt = sts_entry->handle; fw_ddb_index = le32_to_cpu(sts_entry->target); ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, fw_ddb_index); if (ddb_entry == NULL) { ql4_printk(KERN_ERR, ha, "%s: Invalid target index = 0x%x\n", __func__, sts_entry->target); return; } cls_conn = ddb_entry->conn; conn = cls_conn->dd_data; spin_lock(&conn->session->back_lock); task = iscsi_itt_to_task(conn, itt); spin_unlock(&conn->session->back_lock); if (task == NULL) { ql4_printk(KERN_ERR, ha, "%s: Task is NULL\n", __func__); return; } task_data = task->dd_data; memcpy(&task_data->sts, sts_entry, sizeof(struct passthru_status)); ha->iocb_cnt -= task_data->iocb_req_cnt; queue_work(ha->task_wq, &task_data->task_work); } static struct mrb *qla4xxx_del_mrb_from_active_array(struct scsi_qla_host *ha, uint32_t index) { struct mrb *mrb = NULL; /* validate handle and remove from active array */ if (index >= MAX_MRB) return mrb; mrb = ha->active_mrb_array[index]; ha->active_mrb_array[index] = NULL; if (!mrb) return mrb; /* update counters */ ha->iocb_cnt -= mrb->iocb_cnt; return mrb; } static void qla4xxx_mbox_status_entry(struct scsi_qla_host *ha, struct mbox_status_iocb *mbox_sts_entry) { struct mrb *mrb; uint32_t status; uint32_t data_size; mrb = qla4xxx_del_mrb_from_active_array(ha, le32_to_cpu(mbox_sts_entry->handle)); if (mrb == NULL) { ql4_printk(KERN_WARNING, ha, "%s: mrb[%d] is null\n", __func__, mbox_sts_entry->handle); return; } switch (mrb->mbox_cmd) { case MBOX_CMD_PING: DEBUG2(ql4_printk(KERN_INFO, ha, "%s: mbox_cmd = 0x%x, " "mbox_sts[0] = 0x%x, mbox_sts[6] = 0x%x\n", __func__, mrb->mbox_cmd, mbox_sts_entry->out_mbox[0], mbox_sts_entry->out_mbox[6])); if (mbox_sts_entry->out_mbox[0] == MBOX_STS_COMMAND_COMPLETE) status = ISCSI_PING_SUCCESS; else status = mbox_sts_entry->out_mbox[6]; data_size = sizeof(mbox_sts_entry->out_mbox); qla4xxx_post_ping_evt_work(ha, status, mrb->pid, data_size, (uint8_t *) mbox_sts_entry->out_mbox); break; default: DEBUG2(ql4_printk(KERN_WARNING, ha, "%s: invalid mbox_cmd = " "0x%x\n", __func__, mrb->mbox_cmd)); } kfree(mrb); return; } /** * qla4xxx_process_response_queue - process response queue completions * @ha: Pointer to host adapter structure. * * This routine process response queue completions in interrupt context. * Hardware_lock locked upon entry **/ void qla4xxx_process_response_queue(struct scsi_q
.. This work is licensed under a Creative Commons Attribution 4.0 International
.. License.
.. http://creativecommons.org/licenses/by/4.0
.. (c) 2016 Huawei Technologies Co.,Ltd and others

============
Architecture
============

Abstract
========
This chapter describes the yardstick framework software architecture. We will
introduce it from Use-Case View, Logical View, Process View and Deployment
View. More technical details will be introduced in this chapter.

Overview
========

Architecture overview
---------------------
Yardstick is mainly written in Python, and test configurations are made
in YAML. Documentation is written in reStructuredText format, i.e. .rst
files. Yardstick is inspired by Rally. Yardstick is intended to run on a
computer with access and credentials to a cloud. The test case is described
in a configuration file given as an argument.

How it works: the benchmark task configuration file is parsed and converted
into an internal model. The context part of the model is converted into a Heat
template and deployed into a stack. Each scenario is run using a runner, either
serially or in parallel. Each runner runs in its own subprocess executing
commands in a VM using SSH. The output of each scenario is written as json
records to a file or influxdb or http server, we use influxdb as the backend,
the test result will be shown with grafana.


Concept
-------
**Benchmark** - assess the relative performance of something

**Benchmark** configuration file - describes a single test case in yaml format

**Context** - The set of Cloud resources used by a scenario, such as user
names, image names, affinity rules and network configurations. A context is
converted into a simplified Heat template, which is used to deploy onto the
Openstack environment.

**Data** - Output produced by running a benchmark, written to a file in json
format

**Runner** - Logic that determines how a test scenario is run and reported, for
example the number of test iterations, input value stepping and test duration.
Predefined runner types exist for re-usage, see `Runner types`_.

**Scenario** - Type/class of measurement for example Ping, Pktgen, (Iperf,
LmBench, ...)

**SLA** - Relates to what result boundary a test case must meet to pass. For
example a latency limit, amount or ratio of lost packets and so on. Action
based on :term:`SLA` can be configured, either just to log (monitor) or to stop
further testing (assert). The :term:`SLA` criteria is set in the benchmark
configuration file and evaluated by the runner.


Runner types
------------

There exists several predefined runner types to choose between when designing
a test scenario:

**Arithmetic:**
Every test run arithmetically steps the specified input value(s) in the
test scenario, adding a value to the previous input value. It is also possible
to combine several input values for the same test case in different
combinations.

Snippet of an Arithmetic runner configuration:
::


  runner:
      type: Arithmetic
      iterators:
      -
        name: stride
        start: 64
        stop: 128
        step: 64

**Duration:**
The test runs for a specific period of time before completed.

Snippet of a Duration runner configuration:
::


  runner:
    type: Duration
    duration: 30

**Sequence:**
The test changes a specified input value to the scenario. The input values
to the sequence are specified in a list in the benchmark configuration file.

Snippet of a Sequence runner configuration:
::


  runner:
    type: Sequence
    scenario_option_name: packetsize
    sequence:
    - 100
    - 200
    - 250


**Iteration:**
Tests are run a specified number of times before completed.

Snippet of an Iteration runner configuration:
::


  runner:
    type: Iteration
    iterations: 2




Use-Case View
=============
Yardstick Use-Case View shows two kinds of users. One is the Tester who will
do testing in cloud, the other is the User who is more concerned with test
result and result analyses.

For testers, they will run a single test case or test case suite to verify
infrastructure compliance or bencnmark their own infrastructure performance.
Test result will be stored by dispatcher module, three kinds of store method
(file, influxdb and http) can be configured. The detail information of
scenarios and runners can be queried with CLI by testers.

For users, they would check test result with four ways.

If dispatcher module is configured as file(default), there are two ways to
check test result. One is to get result from yardstick.out ( default path:
/tmp/yardstick.out), the other is to get plot of test result, it will be shown
if users execute command "yardstick-plot".

If dispatcher module is configured as influxdb, users will check test
result on Grafana which is most commonly used for visualizing time series data.

If dispatcher module is configured as http, users will check test result
on OPNFV testing dashboard which use MongoDB as backend.

.. image:: images/Use_case.png
   :width: 800px
   :alt: Yardstick Use-Case View

Logical View
============
Yardstick Logical View describes the most important classes, their
organization, and the most important use-case realizations.

Main classes:

**TaskCommands** - "yardstick task" subcommand handler.

**HeatContext** - Do test yaml file context section model convert to HOT,
deploy and undeploy Openstack heat stack.

**Runner** - Logic that determines how a test scenario is run and reported.

**TestScenario** - Type/class of measurement for example Ping, Pktgen, (Iperf,
LmBench, ...)

**Dispatcher** - Choose user defined way to store test results.

TaskCommands is the "yardstick task" subcommand's main entry. It takes yaml
file (e.g. test.yaml) as input, and uses HeatContext to convert the yaml
file's context section to HOT. After Openstack heat stack is deployed by
HeatContext with the converted HOT, TaskCommands use Runner to run specified
TestScenario. During first runner initialization, it will create output
process. The output process use Dispatcher to push test results. The Runner
will also create a process to execute TestScenario. And there is a
multiprocessing queue between each runner process and output process, so the
runner process can push the real-time test results to the storage media.
TestScenario is commonly connected with VMs by using ssh. It sets up VMs and
run test measurement scripts through the ssh tunnel. After all TestScenaio
is finished, TaskCommands will undeploy the heat stack. Then the whole test is
finished.

.. image:: images/Yardstick_framework_architecture_in_D.png
   :width: 800px
   :alt: Yardstick framework architecture in Danube

Process View (Test execution flow)
==================================
Yardstick process view shows how yardstick runs a test case. Below is the
sequence graph about the test execution flow using heat context, and each
object represents one module in yardstick:

.. image:: images/test_execution_flow.png
   :width: 800px
   :alt: Yardstick Process View

A user wants to do a test with yardstick. He can use the CLI to input the
command to start a task. "TaskCommands" will receive the command and ask
"HeatContext" to parse the context. "HeatContext" will then ask "Model" to
convert the model. After the model is generated, "HeatContext" will inform
"Openstack" to deploy the heat stack by heat template. After "Openstack"
deploys the stack, "HeatContext" will inform "Runner" to run the specific test
case.

Firstly, "Runner" would ask "TestScenario" to process the specific scenario.
Then "TestScenario" will start to log on the openstack by ssh protocal and
execute the test case on the specified VMs. After the script execution
finishes, "TestScenario" will send a message to inform "Runner". When the
testing job is done, "Runner" will inform "Dispatcher" to output the test
result via file, influxdb or http. After the result is output, "HeatContext"
will call "Openstack" to undeploy the heat stack. Once the stack is
undepoyed, the whole test ends.

Deployment View
===============
Yardstick deployment view shows how the yardstick tool can be deployed into the
underlying platform. Generally, yardstick tool is installed on JumpServer(see
`07-installation` for detail installation steps), and JumpServer is
connected with other control/compute servers by networking. Based on this
deployment, yardstick can run the test cases on these hosts, and get the test
result for better showing.

.. image:: images/Deployment.png
   :width: 800px
   :alt: Yardstick Deployment View

Yardstick Directory structure
=============================

**yardstick/** - Yardstick main directory.

*tests/ci/* - Used for continuous integration of Yardstick at different PODs and
        with support for different installers.

*docs/* - All documentation is stored here, such as configuration guides,
          user guides and Yardstick descriptions.

*etc/* - Used for test cases requiring specific POD configurations.

*samples/* - test case samples are stored here, most of all scenario and
             feature's samples are shown in this directory.

*tests/* - Here both Yardstick internal tests (*functional/* and *unit/*) as
           well as the test cases run to verify the NFVI (*opnfv/*) are stored.
           Also configurations of what to run daily and weekly at the different
           PODs is located here.

*tools/* - Currently contains tools to build image for VMs which are deployed
           by Heat. Currently contains how to build the yardstick-trusty-server
           image with the different tools that are needed from within the
           image.

*plugin/* - Plug-in configuration files are stored here.

*vTC/* - Contains the files for running the virtual Traffic Classifier tests.

*yardstick/* - Contains the internals of Yardstick: Runners, Scenario, Contexts,
               CLI parsing, keys, plotting tools, dispatcher, plugin
               install/remove scripts and so on.
spin_lock_irqsave(&ha->hardware_lock, flags); while (1) { if (!(readl(&ha->qla4_82xx_reg->host_int) & ISRX_82XX_RISC_INT)) { qla4_82xx_spurious_interrupt(ha, reqs_count); break; } intr_status = readl(&ha->qla4_82xx_reg->host_status); if ((intr_status & (HSRX_RISC_MB_INT | HSRX_RISC_IOCB_INT)) == 0) { qla4_82xx_spurious_interrupt(ha, reqs_count); break; } ha->isp_ops->interrupt_service_routine(ha, intr_status); /* Enable Interrupt */ qla4_82xx_wr_32(ha, ha->nx_legacy_intr.tgt_mask_reg, 0xfbff); if (++reqs_count == MAX_REQS_SERVICED_PER_INTR) break; } spin_unlock_irqrestore(&ha->hardware_lock, flags); return IRQ_HANDLED; } #define LEG_INT_PTR_B31 (1 << 31) #define LEG_INT_PTR_B30 (1 << 30) #define PF_BITS_MASK (0xF << 16) /** * qla4_83xx_intr_handler - hardware interrupt handler. * @irq: Unused * @dev_id: Pointer to host adapter structure **/ irqreturn_t qla4_83xx_intr_handler(int irq, void *dev_id) { struct scsi_qla_host *ha = dev_id; uint32_t leg_int_ptr = 0; unsigned long flags = 0; ha->isr_count++; leg_int_ptr = readl(&ha->qla4_83xx_reg->leg_int_ptr); /* Legacy interrupt is valid if bit31 of leg_int_ptr is set */ if (!(leg_int_ptr & LEG_INT_PTR_B31)) { DEBUG7(ql4_printk(KERN_ERR, ha, "%s: Legacy Interrupt Bit 31 not set, spurious interrupt!\n", __func__)); return IRQ_NONE; } /* Validate the PCIE function ID set in leg_int_ptr bits [19..16] */ if ((leg_int_ptr & PF_BITS_MASK) != ha->pf_bit) { DEBUG7(ql4_printk(KERN_ERR, ha, "%s: Incorrect function ID 0x%x in legacy interrupt register, ha->pf_bit = 0x%x\n", __func__, (leg_int_ptr & PF_BITS_MASK), ha->pf_bit)); return IRQ_NONE; } /* To de-assert legacy interrupt, write 0 to Legacy Interrupt Trigger * Control register and poll till Legacy Interrupt Pointer register * bit30 is 0. */ writel(0, &ha->qla4_83xx_reg->leg_int_trig); do { leg_int_ptr = readl(&ha->qla4_83xx_reg->leg_int_ptr); if ((leg_int_ptr & PF_BITS_MASK) != ha->pf_bit) break; } while (leg_int_ptr & LEG_INT_PTR_B30); spin_lock_irqsave(&ha->hardware_lock, flags); leg_int_ptr = readl(&ha->qla4_83xx_reg->risc_intr); ha->isp_ops->interrupt_service_routine(ha, leg_int_ptr); spin_unlock_irqrestore(&ha->hardware_lock, flags); return IRQ_HANDLED; } irqreturn_t qla4_8xxx_msi_handler(int irq, void *dev_id) { struct scsi_qla_host *ha; ha = (struct scsi_qla_host *) dev_id; if (!ha) { DEBUG2(printk(KERN_INFO "qla4xxx: MSIX: Interrupt with NULL host ptr\n")); return IRQ_NONE; } ha->isr_count++; /* clear the interrupt */ qla4_82xx_wr_32(ha, ha->nx_legacy_intr.tgt_status_reg, 0xffffffff); /* read twice to ensure write is flushed */ qla4_82xx_rd_32(ha, ISR_INT_VECTOR); qla4_82xx_rd_32(ha, ISR_INT_VECTOR); return qla4_8xxx_default_intr_handler(irq, dev_id); } static irqreturn_t qla4_83xx_mailbox_intr_handler(int irq, void *dev_id) { struct scsi_qla_host *ha = dev_id; unsigned long flags; uint32_t ival = 0; spin_lock_irqsave(&ha->hardware_lock, flags); ival = readl(&ha->qla4_83xx_reg->risc_intr); if (ival == 0) { ql4_printk(KERN_INFO, ha, "%s: It is a spurious mailbox interrupt!\n", __func__); ival = readl(&ha->qla4_83xx_reg->mb_int_mask); ival &= ~INT_MASK_FW_MB; writel(ival, &ha->qla4_83xx_reg->mb_int_mask); goto exit; } qla4xxx_isr_decode_mailbox(ha, readl(&ha->qla4_83xx_reg->mailbox_out[0])); writel(0, &ha->qla4_83xx_reg->risc_intr); ival = readl(&ha->qla4_83xx_reg->mb_int_mask); ival &= ~INT_MASK_FW_MB; writel(ival, &ha->qla4_83xx_reg->mb_int_mask); ha->isr_count++; exit: spin_unlock_irqrestore(&ha->hardware_lock, flags); return IRQ_HANDLED; } /** * qla4_8xxx_default_intr_handler - hardware interrupt handler. * @irq: Unused * @dev_id: Pointer to host adapter structure * * This interrupt handler is called directly for MSI-X, and * called indirectly for MSI. **/ irqreturn_t qla4_8xxx_default_intr_handler(int irq, void *dev_id) { struct scsi_qla_host *ha = dev_id; unsigned long flags; uint32_t intr_status; uint8_t reqs_count = 0; if (is_qla8032(ha) || is_qla8042(ha)) { qla4_83xx_mailbox_intr_handler(irq, dev_id); } else { spin_lock_irqsave(&ha->hardware_lock, flags); while (1) { if (!(readl(&ha->qla4_82xx_reg->host_int) & ISRX_82XX_RISC_INT)) { qla4_82xx_spurious_interrupt(ha, reqs_count); break; } intr_status = readl(&ha->qla4_82xx_reg->host_status); if ((intr_status & (HSRX_RISC_MB_INT | HSRX_RISC_IOCB_INT)) == 0) { qla4_82xx_spurious_interrupt(ha, reqs_count); break; } ha->isp_ops->interrupt_service_routine(ha, intr_status); if (++reqs_count == MAX_REQS_SERVICED_PER_INTR) break; } ha->isr_count++; spin_unlock_irqrestore(&ha->hardware_lock, flags); } return IRQ_HANDLED; } irqreturn_t qla4_8xxx_msix_rsp_q(int irq, void *dev_id) { struct scsi_qla_host *ha = dev_id; unsigned long flags; int intr_status; uint32_t ival = 0; spin_lock_irqsave(&ha->hardware_lock, flags); if (is_qla8032(ha) || is_qla8042(ha)) { ival = readl(&ha->qla4_83xx_reg->iocb_int_mask); if (ival == 0) { ql4_printk(KERN_INFO, ha, "%s: It is a spurious iocb interrupt!\n", __func__); goto exit_msix_rsp_q; } qla4xxx_process_response_queue(ha); writel(0, &ha->qla4_83xx_reg->iocb_int_mask); } else { intr_status = readl(&ha->qla4_82xx_reg->host_status); if (intr_status & HSRX_RISC_IOCB_INT) { qla4xxx_process_response_queue(ha); writel(0, &ha->qla4_82xx_reg->host_int); } else { ql4_printk(KERN_INFO, ha, "%s: spurious iocb interrupt...\n", __func__); goto exit_msix_rsp_q; } } ha->isr_count++; exit_msix_rsp_q: spin_unlock_irqrestore(&ha->hardware_lock, flags); return IRQ_HANDLED; } /** * qla4xxx_process_aen - processes AENs generated by firmware * @ha: pointer to host adapter structure. * @process_aen: type of AENs to process * * Processes specific types of Asynchronous Events generated by firmware. * The type of AENs to process is specified by process_aen and can be * PROCESS_ALL_AENS 0 * FLUSH_DDB_CHANGED_AENS 1 * RELOGIN_DDB_CHANGED_AENS 2 **/ void qla4xxx_process_aen(struct scsi_qla_host * ha, uint8_t process_aen) { uint32_t mbox_sts[MBOX_AEN_REG_COUNT]; struct aen *aen; int i; unsigned long flags; spin_lock_irqsave(&ha->hardware_lock, flags); while (ha->aen_out != ha->aen_in) { aen = &ha->aen_q[ha->aen_out]; /* copy aen information to local structure */ for (i = 0; i < MBOX_AEN_REG_COUNT; i++) mbox_sts[i] = aen->mbox_sts[i]; ha->aen_q_count++; ha->aen_out++; if (ha->aen_out == MAX_AEN_ENTRIES) ha->aen_out = 0; spin_unlock_irqrestore(&ha->hardware_lock, flags); DEBUG2(printk("qla4xxx(%ld): AEN[%d]=0x%08x, mbx1=0x%08x mbx2=0x%08x" " mbx3=0x%08x mbx4=0x%08x\n", ha->host_no, (ha->aen_out ? (ha->aen_out-1): (MAX_AEN_ENTRIES-1)), mbox_sts[0], mbox_sts[1], mbox_sts[2], mbox_sts[3], mbox_sts[4])); switch (mbox_sts[0]) { case MBOX_ASTS_DATABASE_CHANGED: switch (process_aen) { case FLUSH_DDB_CHANGED_AENS: DEBUG2(printk("scsi%ld: AEN[%d] %04x, index " "[%d] state=%04x FLUSHED!\n", ha->host_no, ha->aen_out, mbox_sts[0], mbox_sts[2], mbox_sts[3])); break; case PROCESS_ALL_AENS: default: /* Specific device. */ if (mbox_sts[1] == 1) qla4xxx_process_ddb_changed(ha, mbox_sts[2], mbox_sts[3], mbox_sts[4]); break; } } spin_lock_irqsave(&ha->hardware_lock, flags); } spin_unlock_irqrestore(&ha->hardware_lock, flags); } int qla4xxx_request_irqs(struct scsi_qla_host *ha) { int ret = 0; int rval = QLA_ERROR; if (is_qla40XX(ha)) goto try_intx; if (ql4xenablemsix == 2) { /* Note: MSI Interrupts not supported for ISP8324 and ISP8042 */ if (is_qla8032(ha) || is_qla8042(ha)) { ql4_printk(KERN_INFO, ha, "%s: MSI Interrupts not supported for ISP%04x, Falling back-to INTx mode\n", __func__, ha->pdev->device); goto try_intx; } goto try_msi; } if (ql4xenablemsix == 0 || ql4xenablemsix != 1) goto try_intx; /* Trying MSI-X */ ret = qla4_8xxx_enable_msix(ha); if (!ret) { DEBUG2(ql4_printk(KERN_INFO, ha, "MSI-X: Enabled (0x%X).\n", ha->revision_id)); goto irq_attached; } else { if (is_qla8032(ha) || is_qla8042(ha)) { ql4_printk(KERN_INFO, ha, "%s: ISP%04x: MSI-X: Falling back-to INTx mode. ret = %d\n", __func__, ha->pdev->device, ret); goto try_intx; } } ql4_printk(KERN_WARNING, ha, "MSI-X: Falling back-to MSI mode -- %d.\n", ret); try_msi: /* Trying MSI */ ret = pci_enable_msi(ha->pdev); if (!ret) { ret = request_irq(ha->pdev->irq, qla4_8xxx_msi_handler, 0, DRIVER_NAME, ha); if (!ret) { DEBUG2(ql4_printk(KERN_INFO, ha, "MSI: Enabled.\n")); set_bit(AF_MSI_ENABLED, &ha->flags); goto irq_attached; } else { ql4_printk(KERN_WARNING, ha, "MSI: Failed to reserve interrupt %d " "already in use.\n", ha->pdev->irq); pci_disable_msi(ha->pdev); } } try_intx: if (is_qla8022(ha)) { ql4_printk(KERN_WARNING, ha, "%s: ISP82xx Legacy interrupt not supported\n", __func__); goto irq_not_attached; } /* Trying INTx */ ret = request_irq(ha->pdev->irq, ha->isp_ops->intr_handler, IRQF_SHARED, DRIVER_NAME, ha); if (!ret) { DEBUG2(ql4_printk(KERN_INFO, ha, "INTx: Enabled.\n")); set_bit(AF_INTx_ENABLED, &ha->flags); goto irq_attached; } else { ql4_printk(KERN_WARNING, ha, "INTx: Failed to reserve interrupt %d already in" " use.\n", ha->pdev->irq); goto irq_not_attached; } irq_attached: set_bit(AF_IRQ_ATTACHED, &ha->flags); ha->host->irq = ha->pdev->irq; ql4_printk(KERN_INFO, ha, "%s: irq %d attached\n", __func__, ha->pdev->irq); rval = QLA_SUCCESS; irq_not_attached: return rval; } void qla4xxx_free_irqs(struct scsi_qla_host *ha) { if (test_and_clear_bit(AF_IRQ_ATTACHED, &ha->flags)) { if (test_bit(AF_MSIX_ENABLED, &ha->flags)) { qla4_8xxx_disable_msix(ha); } else if (test_and_clear_bit(AF_MSI_ENABLED, &ha->flags)) { free_irq(ha->pdev->irq, ha); pci_disable_msi(ha->pdev); } else if (test_and_clear_bit(AF_INTx_ENABLED, &ha->flags)) { free_irq(ha->pdev->irq, ha); } } }