diff options
Diffstat (limited to 'drivers/scsi/isci/request.c')
-rw-r--r-- | drivers/scsi/isci/request.c | 1472 |
1 files changed, 1472 insertions, 0 deletions
diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c new file mode 100644 index 000000000000..e564121b6726 --- /dev/null +++ b/drivers/scsi/isci/request.c @@ -0,0 +1,1472 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "isci.h" +#include "scic_remote_device.h" +#include "scic_io_request.h" +#include "scic_task_request.h" +#include "scic_port.h" +#include "task.h" +#include "request.h" +#include "sata.h" +#include "scu_completion_codes.h" + + +static enum sci_status isci_request_ssp_request_construct( + struct isci_request *request) +{ + enum sci_status status; + + dev_dbg(&request->isci_host->pdev->dev, + "%s: request = %p\n", + __func__, + request); + status = scic_io_request_construct_basic_ssp( + request->sci_request_handle + ); + return status; +} + +static enum sci_status isci_request_stp_request_construct( + struct isci_request *request) +{ + struct sas_task *task = isci_request_access_task(request); + enum sci_status status; + struct host_to_dev_fis *register_fis; + + dev_dbg(&request->isci_host->pdev->dev, + "%s: request = %p\n", + __func__, + request); + + /* Get the host_to_dev_fis from the core and copy + * the fis from the task into it. + */ + register_fis = isci_sata_task_to_fis_copy(task); + + status = scic_io_request_construct_basic_sata( + request->sci_request_handle + ); + + /* Set the ncq tag in the fis, from the queue + * command in the task. + */ + if (isci_sata_is_task_ncq(task)) { + + isci_sata_set_ncq_tag( + register_fis, + task + ); + } + + return status; +} + +/** + * isci_smp_request_build() - This function builds the smp request object. + * @isci_host: This parameter specifies the ISCI host object + * @request: This parameter points to the isci_request object allocated in the + * request construct function. + * @sci_device: This parameter is the handle for the sci core's remote device + * object that is the destination for this request. + * + * SCI_SUCCESS on successfull completion, or specific failure code. + */ +static enum sci_status isci_smp_request_build( + struct isci_request *request) +{ + enum sci_status status = SCI_FAILURE; + struct sas_task *task = isci_request_access_task(request); + + void *command_iu_address = + scic_io_request_get_command_iu_address( + request->sci_request_handle + ); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: request = %p\n", + __func__, + request); + dev_dbg(&request->isci_host->pdev->dev, + "%s: smp_req len = %d\n", + __func__, + task->smp_task.smp_req.length); + + /* copy the smp_command to the address; */ + sg_copy_to_buffer(&task->smp_task.smp_req, 1, + (char *)command_iu_address, + sizeof(struct smp_request) + ); + + status = scic_io_request_construct_smp(request->sci_request_handle); + if (status != SCI_SUCCESS) + dev_warn(&request->isci_host->pdev->dev, + "%s: scic_io_request_construct_smp failed with " + "status = %d\n", + __func__, + status); + + return status; +} + +/** + * isci_io_request_build() - This function builds the io request object. + * @isci_host: This parameter specifies the ISCI host object + * @request: This parameter points to the isci_request object allocated in the + * request construct function. + * @sci_device: This parameter is the handle for the sci core's remote device + * object that is the destination for this request. + * + * SCI_SUCCESS on successfull completion, or specific failure code. + */ +static enum sci_status isci_io_request_build( + struct isci_host *isci_host, + struct isci_request *request, + struct isci_remote_device *isci_device) +{ + struct smp_discover_response_protocols dev_protocols; + enum sci_status status = SCI_SUCCESS; + struct sas_task *task = isci_request_access_task(request); + struct scic_sds_remote_device *sci_device = + isci_device->sci_device_handle; + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = 0x%p; request = %p, " + "num_scatter = %d\n", + __func__, + isci_device, + request, + task->num_scatter); + + /* map the sgl addresses, if present. + * libata does the mapping for sata devices + * before we get the request. + */ + if (task->num_scatter && + !sas_protocol_ata(task->task_proto) && + !(SAS_PROTOCOL_SMP & task->task_proto)) { + + request->num_sg_entries = dma_map_sg( + &isci_host->pdev->dev, + task->scatter, + task->num_scatter, + task->data_dir + ); + + if (request->num_sg_entries == 0) + return SCI_FAILURE_INSUFFICIENT_RESOURCES; + } + + /* build the common request object. For now, + * we will let the core allocate the IO tag. + */ + status = scic_io_request_construct( + isci_host->core_controller, + sci_device, + SCI_CONTROLLER_INVALID_IO_TAG, + request, + request->sci_request_mem_ptr, + (struct scic_sds_request **)&request->sci_request_handle + ); + + if (status != SCI_SUCCESS) { + dev_warn(&isci_host->pdev->dev, + "%s: failed request construct\n", + __func__); + return SCI_FAILURE; + } + + sci_object_set_association(request->sci_request_handle, request); + + /* Determine protocol and call the appropriate basic constructor */ + scic_remote_device_get_protocols(sci_device, &dev_protocols); + if (dev_protocols.u.bits.attached_ssp_target) + status = isci_request_ssp_request_construct(request); + else if (dev_protocols.u.bits.attached_stp_target) + status = isci_request_stp_request_construct(request); + else if (dev_protocols.u.bits.attached_smp_target) + status = isci_smp_request_build(request); + else { + dev_warn(&isci_host->pdev->dev, + "%s: unknown protocol\n", __func__); + return SCI_FAILURE; + } + + return SCI_SUCCESS; +} + + +/** + * isci_request_alloc_core() - This function gets the request object from the + * isci_host dma cache. + * @isci_host: This parameter specifies the ISCI host object + * @isci_request: This parameter will contain the pointer to the new + * isci_request object. + * @isci_device: This parameter is the pointer to the isci remote device object + * that is the destination for this request. + * @gfp_flags: This parameter specifies the os allocation flags. + * + * SCI_SUCCESS on successfull completion, or specific failure code. + */ +static int isci_request_alloc_core( + struct isci_host *isci_host, + struct isci_request **isci_request, + struct isci_remote_device *isci_device, + gfp_t gfp_flags) +{ + int ret = 0; + dma_addr_t handle; + struct isci_request *request; + + + /* get pointer to dma memory. This actually points + * to both the isci_remote_device object and the + * sci object. The isci object is at the beginning + * of the memory allocated here. + */ + request = dma_pool_alloc(isci_host->dma_pool, gfp_flags, &handle); + if (!request) { + dev_warn(&isci_host->pdev->dev, + "%s: dma_pool_alloc returned NULL\n", __func__); + return -ENOMEM; + } + + /* initialize the request object. */ + spin_lock_init(&request->state_lock); + isci_request_change_state(request, allocated); + request->sci_request_mem_ptr = ((u8 *)request) + + sizeof(struct isci_request); + request->request_daddr = handle; + request->isci_host = isci_host; + request->isci_device = isci_device; + request->io_request_completion = NULL; + + request->request_alloc_size = isci_host->dma_pool_alloc_size; + request->num_sg_entries = 0; + + request->complete_in_target = false; + + INIT_LIST_HEAD(&request->completed_node); + INIT_LIST_HEAD(&request->dev_node); + + *isci_request = request; + + return ret; +} + +static int isci_request_alloc_io( + struct isci_host *isci_host, + struct sas_task *task, + struct isci_request **isci_request, + struct isci_remote_device *isci_device, + gfp_t gfp_flags) +{ + int retval = isci_request_alloc_core(isci_host, isci_request, + isci_device, gfp_flags); + + if (!retval) { + (*isci_request)->ttype_ptr.io_task_ptr = task; + (*isci_request)->ttype = io_task; + + task->lldd_task = *isci_request; + } + return retval; +} + +/** + * isci_request_alloc_tmf() - This function gets the request object from the + * isci_host dma cache and initializes the relevant fields as a sas_task. + * @isci_host: This parameter specifies the ISCI host object + * @sas_task: This parameter is the task struct from the upper layer driver. + * @isci_request: This parameter will contain the pointer to the new + * isci_request object. + * @isci_device: This parameter is the pointer to the isci remote device object + * that is the destination for this request. + * @gfp_flags: This parameter specifies the os allocation flags. + * + * SCI_SUCCESS on successfull completion, or specific failure code. + */ +int isci_request_alloc_tmf( + struct isci_host *isci_host, + struct isci_tmf *isci_tmf, + struct isci_request **isci_request, + struct isci_remote_device *isci_device, + gfp_t gfp_flags) +{ + int retval = isci_request_alloc_core(isci_host, isci_request, + isci_device, gfp_flags); + + if (!retval) { + + (*isci_request)->ttype_ptr.tmf_task_ptr = isci_tmf; + (*isci_request)->ttype = tmf_task; + } + return retval; +} + +/** + * isci_request_signal_device_reset() - This function will set the "device + * needs target reset" flag in the given sas_tasks' task_state_flags, and + * then cause the task to be added into the SCSI error handler queue which + * will eventually be escalated to a target reset. + * + * + */ +static void isci_request_signal_device_reset( + struct isci_request *isci_request) +{ + unsigned long flags; + struct sas_task *task = isci_request_access_task(isci_request); + + dev_dbg(&isci_request->isci_host->pdev->dev, + "%s: request=%p, task=%p\n", __func__, isci_request, task); + + spin_lock_irqsave(&task->task_state_lock, flags); + task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + /* Cause this task to be scheduled in the SCSI error handler + * thread. + */ + sas_task_abort(task); +} + +/** + * isci_request_execute() - This function allocates the isci_request object, + * all fills in some common fields. + * @isci_host: This parameter specifies the ISCI host object + * @sas_task: This parameter is the task struct from the upper layer driver. + * @isci_request: This parameter will contain the pointer to the new + * isci_request object. + * @gfp_flags: This parameter specifies the os allocation flags. + * + * SCI_SUCCESS on successfull completion, or specific failure code. + */ +int isci_request_execute( + struct isci_host *isci_host, + struct sas_task *task, + struct isci_request **isci_request, + gfp_t gfp_flags) +{ + int ret = 0; + struct scic_sds_remote_device *sci_device; + enum sci_status status = SCI_FAILURE_UNSUPPORTED_PROTOCOL; + struct isci_remote_device *isci_device; + struct isci_request *request; + unsigned long flags; + + isci_device = isci_dev_from_domain_dev(task->dev); + sci_device = isci_device->sci_device_handle; + + /* do common allocation and init of request object. */ + ret = isci_request_alloc_io( + isci_host, + task, + &request, + isci_device, + gfp_flags + ); + + if (ret) + goto out; + + status = isci_io_request_build(isci_host, request, isci_device); + if (status == SCI_SUCCESS) { + + spin_lock_irqsave(&isci_host->scic_lock, flags); + + /* send the request, let the core assign the IO TAG. */ + status = scic_controller_start_io( + isci_host->core_controller, + sci_device, + request->sci_request_handle, + SCI_CONTROLLER_INVALID_IO_TAG + ); + + if (status == SCI_SUCCESS || + status == SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED) { + + /* Either I/O started OK, or the core has signaled that + * the device needs a target reset. + * + * In either case, hold onto the I/O for later. + * + * Update it's status and add it to the list in the + * remote device object. + */ + isci_request_change_state(request, started); + list_add(&request->dev_node, + &isci_device->reqs_in_process); + + if (status == + SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED) { + /* Signal libsas that we need the SCSI error + * handler thread to work on this I/O and that + * we want a device reset. + */ + isci_request_signal_device_reset(request); + + /* Change the status, since we are holding + * the I/O until it is managed by the SCSI + * error handler. + */ + status = SCI_SUCCESS; + } + } else + dev_warn(&isci_host->pdev->dev, + "%s: failed request start\n", + __func__); + + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + } else + dev_warn(&isci_host->pdev->dev, + "%s: request_construct failed - status = 0x%x\n", + __func__, + status); + + out: + if (status != SCI_SUCCESS) { + + /* release dma memory on failure. */ + isci_request_free(isci_host, request); + request = NULL; + ret = SCI_FAILURE; + } + + *isci_request = request; + return ret; +} + + +/** + * isci_request_process_response_iu() - This function sets the status and + * response iu, in the task struct, from the request object for the upper + * layer driver. + * @sas_task: This parameter is the task struct from the upper layer driver. + * @resp_iu: This parameter points to the response iu of the completed request. + * @dev: This parameter specifies the linux device struct. + * + * none. + */ +static void isci_request_process_response_iu( + struct sas_task *task, + struct ssp_response_iu *resp_iu, + struct device *dev) +{ + dev_dbg(dev, + "%s: resp_iu = %p " + "resp_iu->status = 0x%x,\nresp_iu->datapres = %d " + "resp_iu->response_data_len = %x, " + "resp_iu->sense_data_len = %x\nrepsonse data: ", + __func__, + resp_iu, + resp_iu->status, + resp_iu->datapres, + resp_iu->response_data_len, + resp_iu->sense_data_len); + + task->task_status.stat = resp_iu->status; + + /* libsas updates the task status fields based on the response iu. */ + sas_ssp_task_response(dev, task, resp_iu); +} + +/** + * isci_request_set_open_reject_status() - This function prepares the I/O + * completion for OPEN_REJECT conditions. + * @request: This parameter is the completed isci_request object. + * @response_ptr: This parameter specifies the service response for the I/O. + * @status_ptr: This parameter specifies the exec status for the I/O. + * @complete_to_host_ptr: This parameter specifies the action to be taken by + * the LLDD with respect to completing this request or forcing an abort + * condition on the I/O. + * @open_rej_reason: This parameter specifies the encoded reason for the + * abandon-class reject. + * + * none. + */ +static void isci_request_set_open_reject_status( + struct isci_request *request, + struct sas_task *task, + enum service_response *response_ptr, + enum exec_status *status_ptr, + enum isci_completion_selection *complete_to_host_ptr, + enum sas_open_rej_reason open_rej_reason) +{ + /* Task in the target is done. */ + request->complete_in_target = true; + *response_ptr = SAS_TASK_UNDELIVERED; + *status_ptr = SAS_OPEN_REJECT; + *complete_to_host_ptr = isci_perform_normal_io_completion; + task->task_status.open_rej_reason = open_rej_reason; +} + +/** + * isci_request_handle_controller_specific_errors() - This function decodes + * controller-specific I/O completion error conditions. + * @request: This parameter is the completed isci_request object. + * @response_ptr: This parameter specifies the service response for the I/O. + * @status_ptr: This parameter specifies the exec status for the I/O. + * @complete_to_host_ptr: This parameter specifies the action to be taken by + * the LLDD with respect to completing this request or forcing an abort + * condition on the I/O. + * + * none. + */ +static void isci_request_handle_controller_specific_errors( + struct isci_remote_device *isci_device, + struct isci_request *request, + struct sas_task *task, + enum service_response *response_ptr, + enum exec_status *status_ptr, + enum isci_completion_selection *complete_to_host_ptr) +{ + unsigned int cstatus; + + cstatus = scic_request_get_controller_status( + request->sci_request_handle + ); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: %p SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR " + "- controller status = 0x%x\n", + __func__, request, cstatus); + + /* Decode the controller-specific errors; most + * important is to recognize those conditions in which + * the target may still have a task outstanding that + * must be aborted. + * + * Note that there are SCU completion codes being + * named in the decode below for which SCIC has already + * done work to handle them in a way other than as + * a controller-specific completion code; these are left + * in the decode below for completeness sake. + */ + switch (cstatus) { + case SCU_TASK_DONE_DMASETUP_DIRERR: + /* Also SCU_TASK_DONE_SMP_FRM_TYPE_ERR: */ + case SCU_TASK_DONE_XFERCNT_ERR: + /* Also SCU_TASK_DONE_SMP_UFI_ERR: */ + if (task->task_proto == SAS_PROTOCOL_SMP) { + /* SCU_TASK_DONE_SMP_UFI_ERR == Task Done. */ + *response_ptr = SAS_TASK_COMPLETE; + + /* See if the device has been/is being stopped. Note + * that we ignore the quiesce state, since we are + * concerned about the actual device state. + */ + if ((isci_device->status == isci_stopping) || + (isci_device->status == isci_stopped)) + *status_ptr = SAS_DEVICE_UNKNOWN; + else + *status_ptr = SAS_ABORTED_TASK; + + request->complete_in_target = true; + + *complete_to_host_ptr = + isci_perform_normal_io_completion; + } else { + /* Task in the target is not done. */ + *response_ptr = SAS_TASK_UNDELIVERED; + + if ((isci_device->status == isci_stopping) || + (isci_device->status == isci_stopped)) + *status_ptr = SAS_DEVICE_UNKNOWN; + else + *status_ptr = SAM_STAT_TASK_ABORTED; + + request->complete_in_target = false; + + *complete_to_host_ptr = + isci_perform_error_io_completion; + } + + break; + + case SCU_TASK_DONE_CRC_ERR: + case SCU_TASK_DONE_NAK_CMD_ERR: + case SCU_TASK_DONE_EXCESS_DATA: + case SCU_TASK_DONE_UNEXP_FIS: + /* Also SCU_TASK_DONE_UNEXP_RESP: */ + case SCU_TASK_DONE_VIIT_ENTRY_NV: /* TODO - conditions? */ + case SCU_TASK_DONE_IIT_ENTRY_NV: /* TODO - conditions? */ + case SCU_TASK_DONE_RNCNV_OUTBOUND: /* TODO - conditions? */ + /* These are conditions in which the target + * has completed the task, so that no cleanup + * is necessary. + */ + *response_ptr = SAS_TASK_COMPLETE; + + /* See if the device has been/is being stopped. Note + * that we ignore the quiesce state, since we are + * concerned about the actual device state. + */ + if ((isci_device->status == isci_stopping) || + (isci_device->status == isci_stopped)) + *status_ptr = SAS_DEVICE_UNKNOWN; + else + *status_ptr = SAS_ABORTED_TASK; + + request->complete_in_target = true; + + *complete_to_host_ptr = isci_perform_normal_io_completion; + break; + + + /* Note that the only open reject completion codes seen here will be + * abandon-class codes; all others are automatically retried in the SCU. + */ + case SCU_TASK_OPEN_REJECT_WRONG_DESTINATION: + + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_WRONG_DEST); + break; + + case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION: + + /* Note - the return of AB0 will change when + * libsas implements detection of zone violations. + */ + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_RESV_AB0); + break; + + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1: + + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_RESV_AB1); + break; + + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2: + + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_RESV_AB2); + break; + + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3: + + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_RESV_AB3); + break; + + case SCU_TASK_OPEN_REJECT_BAD_DESTINATION: + + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_BAD_DEST); + break; + + case SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY: + + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_STP_NORES); + break; + + case SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED: + + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_EPROTO); + break; + + case SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED: + + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_CONN_RATE); + break; + + case SCU_TASK_DONE_LL_R_ERR: + /* Also SCU_TASK_DONE_ACK_NAK_TO: */ + case SCU_TASK_DONE_LL_PERR: + case SCU_TASK_DONE_LL_SY_TERM: + /* Also SCU_TASK_DONE_NAK_ERR:*/ + case SCU_TASK_DONE_LL_LF_TERM: + /* Also SCU_TASK_DONE_DATA_LEN_ERR: */ + case SCU_TASK_DONE_LL_ABORT_ERR: + case SCU_TASK_DONE_SEQ_INV_TYPE: + /* Also SCU_TASK_DONE_UNEXP_XR: */ + case SCU_TASK_DONE_XR_IU_LEN_ERR: + case SCU_TASK_DONE_INV_FIS_LEN: + /* Also SCU_TASK_DONE_XR_WD_LEN: */ + case SCU_TASK_DONE_SDMA_ERR: + case SCU_TASK_DONE_OFFSET_ERR: + case SCU_TASK_DONE_MAX_PLD_ERR: + case SCU_TASK_DONE_LF_ERR: + case SCU_TASK_DONE_SMP_RESP_TO_ERR: /* Escalate to dev reset? */ + case SCU_TASK_DONE_SMP_LL_RX_ERR: + case SCU_TASK_DONE_UNEXP_DATA: + case SCU_TASK_DONE_UNEXP_SDBFIS: + case SCU_TASK_DONE_REG_ERR: + case SCU_TASK_DONE_SDB_ERR: + case SCU_TASK_DONE_TASK_ABORT: + default: + /* Task in the target is not done. */ + *response_ptr = SAS_TASK_UNDELIVERED; + *status_ptr = SAM_STAT_TASK_ABORTED; + request->complete_in_target = false; + + *complete_to_host_ptr = isci_perform_error_io_completion; + break; + } +} + +/** + * isci_task_save_for_upper_layer_completion() - This function saves the + * request for later completion to the upper layer driver. + * @host: This parameter is a pointer to the host on which the the request + * should be queued (either as an error or success). + * @request: This parameter is the completed request. + * @response: This parameter is the response code for the completed task. + * @status: This parameter is the status code for the completed task. + * + * none. + */ +static void isci_task_save_for_upper_layer_completion( + struct isci_host *host, + struct isci_request *request, + enum service_response response, + enum exec_status status, + enum isci_completion_selection task_notification_selection) +{ + struct sas_task *task = isci_request_access_task(request); + + isci_task_set_completion_status(task, response, status, + task_notification_selection); + + + /* Tasks aborted specifically by a call to the lldd_abort_task + * function should not be completed to the host in the regular path. + */ + switch (task_notification_selection) { + + case isci_perform_normal_io_completion: + + /* Normal notification (task_done) */ + dev_dbg(&host->pdev->dev, + "%s: Normal - task = %p, response=%d, status=%d\n", + __func__, + task, + response, + status); + /* Add to the completed list. */ + list_add(&request->completed_node, + &host->requests_to_complete); + break; + + case isci_perform_aborted_io_completion: + /* + * No notification because this request is already + * in the abort path. + */ + dev_warn(&host->pdev->dev, + "%s: Aborted - task = %p, response=%d, status=%d\n", + __func__, + task, + response, + status); + break; + + case isci_perform_error_io_completion: + /* Use sas_task_abort */ + dev_warn(&host->pdev->dev, + "%s: Error - task = %p, response=%d, status=%d\n", + __func__, + task, + response, + status); + /* Add to the aborted list. */ + list_add(&request->completed_node, + &host->requests_to_abort); + break; + + default: + dev_warn(&host->pdev->dev, + "%s: Unknown - task = %p, response=%d, status=%d\n", + __func__, + task, + response, + status); + + /* Add to the aborted list. */ + list_add(&request->completed_node, + &host->requests_to_abort); + break; + } +} + +/** + * isci_request_io_request_complete() - This function is called by the sci core + * when an io request completes. + * @isci_host: This parameter specifies the ISCI host object + * @request: This parameter is the completed isci_request object. + * @completion_status: This parameter specifies the completion status from the + * sci core. + * + * none. + */ +void isci_request_io_request_complete( + struct isci_host *isci_host, + struct isci_request *request, + enum sci_io_status completion_status) +{ + struct sas_task *task = isci_request_access_task(request); + struct ssp_response_iu *resp_iu; + void *resp_buf; + unsigned long task_flags; + unsigned long state_flags; + struct completion *io_request_completion; + struct isci_remote_device *isci_device = request->isci_device; + enum service_response response = SAS_TASK_UNDELIVERED; + enum exec_status status = SAS_ABORTED_TASK; + enum isci_request_status request_status; + enum isci_completion_selection complete_to_host + = isci_perform_normal_io_completion; + + dev_dbg(&isci_host->pdev->dev, + "%s: request = %p, task = %p,\n" + "task->data_dir = %d completion_status = 0x%x\n", + __func__, + request, + task, + task->data_dir, + completion_status); + + spin_lock_irqsave(&request->state_lock, state_flags); + request_status = isci_request_get_state(request); + spin_unlock_irqrestore(&request->state_lock, state_flags); + + /* Decode the request status. Note that if the request has been + * aborted by a task management function, we don't care + * what the status is. + */ + switch (request_status) { + + case aborted: + /* "aborted" indicates that the request was aborted by a task + * management function, since once a task management request is + * perfomed by the device, the request only completes because + * of the subsequent driver terminate. + * + * Aborted also means an external thread is explicitly managing + * this request, so that we do not complete it up the stack. + * + * The target is still there (since the TMF was successful). + */ + request->complete_in_target = true; + response = SAS_TASK_COMPLETE; + + /* See if the device has been/is being stopped. Note + * that we ignore the quiesce state, since we are + * concerned about the actual device state. + */ + if ((isci_device->status == isci_stopping) + || (isci_device->status == isci_stopped) + ) + status = SAS_DEVICE_UNKNOWN; + else + status = SAS_ABORTED_TASK; + + complete_to_host = isci_perform_aborted_io_completion; + /* This was an aborted request. */ + break; + + case aborting: + /* aborting means that the task management function tried and + * failed to abort the request. We need to note the request + * as SAS_TASK_UNDELIVERED, so that the scsi mid layer marks the + * target as down. + * + * Aborting also means an external thread is explicitly managing + * this request, so that we do not complete it up the stack. + */ + request->complete_in_target = true; + response = SAS_TASK_UNDELIVERED; + + if ((isci_device->status == isci_stopping) || + (isci_device->status == isci_stopped)) + /* The device has been /is being stopped. Note that + * we ignore the quiesce state, since we are + * concerned about the actual device state. + */ + status = SAS_DEVICE_UNKNOWN; + else + status = SAS_PHY_DOWN; + + complete_to_host = isci_perform_aborted_io_completion; + + /* This was an aborted request. */ + break; + + case terminating: + + /* This was an terminated request. This happens when + * the I/O is being terminated because of an action on + * the device (reset, tear down, etc.), and the I/O needs + * to be completed up the stack. + */ + request->complete_in_target = true; + response = SAS_TASK_UNDELIVERED; + + /* See if the device has been/is being stopped. Note + * that we ignore the quiesce state, since we are + * concerned about the actual device state. + */ + if ((isci_device->status == isci_stopping) || + (isci_device->status == isci_stopped)) + status = SAS_DEVICE_UNKNOWN; + else + status = SAS_ABORTED_TASK; + + complete_to_host = isci_perform_normal_io_completion; + + /* This was a terminated request. */ + break; + + default: + + /* This is an active request being completed from the core. */ + switch (completion_status) { + + case SCI_IO_FAILURE_RESPONSE_VALID: + dev_dbg(&isci_host->pdev->dev, + "%s: SCI_IO_FAILURE_RESPONSE_VALID (%p/%p)\n", + __func__, + request, + task); + + if (sas_protocol_ata(task->task_proto)) { + resp_buf + = scic_stp_io_request_get_d2h_reg_address( + request->sci_request_handle + ); + isci_request_process_stp_response(task, + resp_buf + ); + + } else if (SAS_PROTOCOL_SSP == task->task_proto) { + + /* crack the iu response buffer. */ + resp_iu + = scic_io_request_get_response_iu_address( + request->sci_request_handle + ); + + isci_request_process_response_iu(task, resp_iu, + &isci_host->pdev->dev + ); + + } else if (SAS_PROTOCOL_SMP == task->task_proto) { + + dev_err(&isci_host->pdev->dev, + "%s: SCI_IO_FAILURE_RESPONSE_VALID: " + "SAS_PROTOCOL_SMP protocol\n", + __func__); + + } else + dev_err(&isci_host->pdev->dev, + "%s: unknown protocol\n", __func__); + + /* use the task status set in the task struct by the + * isci_request_process_response_iu call. + */ + request->complete_in_target = true; + response = task->task_status.resp; + status = task->task_status.stat; + break; + + case SCI_IO_SUCCESS: + case SCI_IO_SUCCESS_IO_DONE_EARLY: + + response = SAS_TASK_COMPLETE; + status = SAM_STAT_GOOD; + request->complete_in_target = true; + + if (task->task_proto == SAS_PROTOCOL_SMP) { + + u8 *command_iu_address + = scic_io_request_get_command_iu_address( + request->sci_request_handle + ); + + dev_dbg(&isci_host->pdev->dev, + "%s: SMP protocol completion\n", + __func__); + + sg_copy_from_buffer( + &task->smp_task.smp_resp, 1, + command_iu_address + + sizeof(struct smp_request), + sizeof(struct smp_resp) + ); + } else if (completion_status + == SCI_IO_SUCCESS_IO_DONE_EARLY) { + + /* This was an SSP / STP / SATA transfer. + * There is a possibility that less data than + * the maximum was transferred. + */ + u32 transferred_length + = scic_io_request_get_number_of_bytes_transferred( + request->sci_request_handle); + + task->task_status.residual + = task->total_xfer_len - transferred_length; + + /* If there were residual bytes, call this an + * underrun. + */ + if (task->task_status.residual != 0) + status = SAS_DATA_UNDERRUN; + + dev_dbg(&isci_host->pdev->dev, + "%s: SCI_IO_SUCCESS_IO_DONE_EARLY %d\n", + __func__, + status); + + } else + dev_dbg(&isci_host->pdev->dev, + "%s: SCI_IO_SUCCESS\n", + __func__); + + break; + + case SCI_IO_FAILURE_TERMINATED: + dev_dbg(&isci_host->pdev->dev, + "%s: SCI_IO_FAILURE_TERMINATED (%p/%p)\n", + __func__, + request, + task); + + /* The request was terminated explicitly. No handling + * is needed in the SCSI error handler path. + */ + request->complete_in_target = true; + response = SAS_TASK_UNDELIVERED; + + /* See if the device has been/is being stopped. Note + * that we ignore the quiesce state, since we are + * concerned about the actual device state. + */ + if ((isci_device->status == isci_stopping) || + (isci_device->status == isci_stopped)) + status = SAS_DEVICE_UNKNOWN; + else + status = SAS_ABORTED_TASK; + + complete_to_host = isci_perform_normal_io_completion; + break; + + case SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR: + + isci_request_handle_controller_specific_errors( + isci_device, request, task, &response, &status, + &complete_to_host); + + break; + + case SCI_IO_FAILURE_REMOTE_DEVICE_RESET_REQUIRED: + /* This is a special case, in that the I/O completion + * is telling us that the device needs a reset. + * In order for the device reset condition to be + * noticed, the I/O has to be handled in the error + * handler. Set the reset flag and cause the + * SCSI error thread to be scheduled. + */ + spin_lock_irqsave(&task->task_state_lock, task_flags); + task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; + spin_unlock_irqrestore(&task->task_state_lock, task_flags); + + complete_to_host = isci_perform_error_io_completion; + request->complete_in_target = false; + break; + + default: + /* Catch any otherwise unhandled error codes here. */ + dev_warn(&isci_host->pdev->dev, + "%s: invalid completion code: 0x%x - " + "isci_request = %p\n", + __func__, completion_status, request); + + response = SAS_TASK_UNDELIVERED; + + /* See if the device has been/is being stopped. Note + * that we ignore the quiesce state, since we are + * concerned about the actual device state. + */ + if ((isci_device->status == isci_stopping) || + (isci_device->status == isci_stopped)) + status = SAS_DEVICE_UNKNOWN; + else + status = SAS_ABORTED_TASK; + + complete_to_host = isci_perform_error_io_completion; + request->complete_in_target = false; + break; + } + break; + } + + isci_request_unmap_sgl(request, isci_host->pdev); + + /* Put the completed request on the correct list */ + isci_task_save_for_upper_layer_completion(isci_host, request, response, + status, complete_to_host + ); + + /* complete the io request to the core. */ + scic_controller_complete_io( + isci_host->core_controller, + isci_device->sci_device_handle, + request->sci_request_handle + ); + /* NULL the request handle so it cannot be completed or + * terminated again, and to cause any calls into abort + * task to recognize the already completed case. + */ + request->sci_request_handle = NULL; + + /* Only remove the request from the remote device list + * of pending requests if we have not requested error + * handling on this request. + */ + if (complete_to_host != isci_perform_error_io_completion) + list_del_init(&request->dev_node); + + + /* Save possible completion ptr. */ + io_request_completion = request->io_request_completion; + + if (io_request_completion) { + + /* This is inherantly a regular I/O request, + * since we are currently in the regular + * I/O completion callback function. + * Signal whoever is waiting that this + * request is complete. + */ + complete(io_request_completion); + } + + isci_host_can_dequeue(isci_host, 1); +} + +/** + * isci_request_io_request_get_transfer_length() - This function is called by + * the sci core to retrieve the transfer length for a given request. + * @request: This parameter is the isci_request object. + * + * length of transfer for specified request. + */ +u32 isci_request_io_request_get_transfer_length(struct isci_request *request) +{ + struct sas_task *task = isci_request_access_task(request); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: total_xfer_len: %d\n", + __func__, + task->total_xfer_len); + return task->total_xfer_len; +} + + +/** + * isci_request_io_request_get_data_direction() - This function is called by + * the sci core to retrieve the data direction for a given request. + * @request: This parameter is the isci_request object. + * + * data direction for specified request. + */ +SCI_IO_REQUEST_DATA_DIRECTION isci_request_io_request_get_data_direction( + struct isci_request *request) +{ + struct sas_task *task = isci_request_access_task(request); + SCI_IO_REQUEST_DATA_DIRECTION ret; + + switch (task->data_dir) { + + case DMA_FROM_DEVICE: + ret = SCI_IO_REQUEST_DATA_IN; + dev_dbg(&request->isci_host->pdev->dev, + "%s: request=%p, FROM_DEVICE\n", + __func__, + request); + break; + + case DMA_TO_DEVICE: + ret = SCI_IO_REQUEST_DATA_OUT; + dev_dbg(&request->isci_host->pdev->dev, + "%s: request=%p, TO_DEVICE\n", + __func__, + request); + break; + + case DMA_BIDIRECTIONAL: + case DMA_NONE: + default: + ret = SCI_IO_REQUEST_NO_DATA; + dev_dbg(&request->isci_host->pdev->dev, + "%s: request=%p, unhandled direction case, " + "data_dir=%d\n", + __func__, + request, + task->data_dir); + break; + + } + return ret; +} + +/** + * isci_request_sge_get_address_field() - This function is called by the sci + * core to retrieve the address field contents for a given sge. + * @request: This parameter is the isci_request object. + * @sge_address: This parameter is the sge. + * + * physical address in the specified sge. + */ +dma_addr_t isci_request_sge_get_address_field( + struct isci_request *request, + void *sge_address) +{ + struct sas_task *task = isci_request_access_task(request); + dma_addr_t ret; + struct isci_host *isci_host = isci_host_from_sas_ha( + task->dev->port->ha); + + dev_dbg(&isci_host->pdev->dev, + "%s: request = %p, sge_address = %p\n", + __func__, + request, + sge_address); + + if (task->data_dir == PCI_DMA_NONE) + return 0; + + /* the case where num_scatter == 0 is special, in that + * task->scatter is the actual buffer address, not an sgl. + * so a map single is required here. + */ + if ((task->num_scatter == 0) && + !sas_protocol_ata(task->task_proto)) { + ret = dma_map_single( + &isci_host->pdev->dev, + task->scatter, + task->total_xfer_len, + task->data_dir + ); + request->zero_scatter_daddr = ret; + } else + ret = sg_dma_address(((struct scatterlist *)sge_address)); + + dev_dbg(&isci_host->pdev->dev, + "%s: bus address = %lx\n", + __func__, + (unsigned long)ret); + + return ret; +} + + +/** + * isci_request_sge_get_length_field() - This function is called by the sci + * core to retrieve the length field contents for a given sge. + * @request: This parameter is the isci_request object. + * @sge_address: This parameter is the sge. + * + * length field value in the specified sge. + */ +u32 isci_request_sge_get_length_field( + struct isci_request *request, + void *sge_address) +{ + struct sas_task *task = isci_request_access_task(request); + int ret; + + dev_dbg(&request->isci_host->pdev->dev, + "%s: request = %p, sge_address = %p\n", + __func__, + request, + sge_address); + + if (task->data_dir == PCI_DMA_NONE) + return 0; + + /* the case where num_scatter == 0 is special, in that + * task->scatter is the actual buffer address, not an sgl. + * so we return total_xfer_len here. + */ + if (task->num_scatter == 0) + ret = task->total_xfer_len; + else + ret = sg_dma_len((struct scatterlist *)sge_address); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: len = %d\n", + __func__, + ret); + + return ret; +} + + +/** + * isci_request_ssp_io_request_get_cdb_address() - This function is called by + * the sci core to retrieve the cdb address for a given request. + * @request: This parameter is the isci_request object. + * + * cdb address for specified request. + */ +void *isci_request_ssp_io_request_get_cdb_address( + struct isci_request *request) +{ + struct sas_task *task = isci_request_access_task(request); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: request->task->ssp_task.cdb = %p\n", + __func__, + task->ssp_task.cdb); + return task->ssp_task.cdb; +} + + +/** + * isci_request_ssp_io_request_get_cdb_length() - This function is called by + * the sci core to retrieve the cdb length for a given request. + * @request: This parameter is the isci_request object. + * + * cdb length for specified request. + */ +u32 isci_request_ssp_io_request_get_cdb_length( + struct isci_request *request) +{ + return 16; +} + + +/** + * isci_request_ssp_io_request_get_lun() - This function is called by the sci + * core to retrieve the lun for a given request. + * @request: This parameter is the isci_request object. + * + * lun for specified request. + */ +u32 isci_request_ssp_io_request_get_lun( + struct isci_request *request) +{ + struct sas_task *task = isci_request_access_task(request); + +#ifdef DEBUG + int i; + + for (i = 0; i < 8; i++) + dev_dbg(&request->isci_host->pdev->dev, + "%s: request->task->ssp_task.LUN[%d] = %x\n", + __func__, i, request->task->ssp_task.LUN[i]); + +#endif + + return task->ssp_task.LUN[0]; +} + + +/** + * isci_request_ssp_io_request_get_task_attribute() - This function is called + * by the sci core to retrieve the task attribute for a given request. + * @request: This parameter is the isci_request object. + * + * task attribute for specified request. + */ +u32 isci_request_ssp_io_request_get_task_attribute( + struct isci_request *request) +{ + struct sas_task *task = isci_request_access_task(request); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: request->task->ssp_task.task_attr = %x\n", + __func__, + task->ssp_task.task_attr); + + return task->ssp_task.task_attr; +} + + +/** + * isci_request_ssp_io_request_get_command_priority() - This function is called + * by the sci core to retrieve the command priority for a given request. + * @request: This parameter is the isci_request object. + * + * command priority for specified request. + */ +u32 isci_request_ssp_io_request_get_command_priority( + struct isci_request *request) +{ + struct sas_task *task = isci_request_access_task(request); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: request->task->ssp_task.task_prio = %x\n", + __func__, + task->ssp_task.task_prio); + + return task->ssp_task.task_prio; +} |