All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/6] isci: initial driver release (part1: intro and lldd)
@ 2011-02-07  0:34 Dan Williams
  2011-02-07  0:34 ` [RFC PATCH 1/6] isci: initialization Dan Williams
                   ` (6 more replies)
  0 siblings, 7 replies; 36+ messages in thread
From: Dan Williams @ 2011-02-07  0:34 UTC (permalink / raw)
  To: james.bottomley
  Cc: dave.jiang, linux-scsi, jacek.danecki, ed.ciechanowski,
	jeffrey.d.skirvin, edmund.nadolski

Introduction:
The upcoming Intel(R) C600 series Patsburg chipset family integrates
6Gb/s SAS capabilities.  Depending on the version up to two 4-port SAS
controllers will be presented.  This implementation does not utilize
firmware and relies on the driver to manage hardware state machines and
some protocol (to a higher degree than existing libsas drivers, more
details below).  The isci driver is divided into two components the lldd
layer which interfaces with libsas and the core which manages the
hardware state and protocol exceptions.  Development can be tracked
here:

	git://git.kernel.org/pub/scm/linux/kernel/git/djbw/isci.git

This is an early release (fixes and reworks still in progress), but is
ready for community feedback.

Driver overview:
The hardware accelerates all the protocol fast paths (SSP, STP FPDMA and
UDMA), and relies on the the driver for everything else.  One example is
legacy sata pio reads which are managed "manually" by their own state
machine in the driver.  The driver submits the read and when the data
fis comes back the hardware, not having acceleration for pio operations,
assigns it to an unidentified frame queue.  The core then associates that
frame with the pending pio request, copies the data into the location
specified by the original sgl, and completes the command up to the lldd.

Responsibility for these types of exceptions and slow path cases are
what contribute to the size of the driver compared to its peers.
Although the overall diffstat of drivers/scsi/isci/ is currently:

	100 files changed, 51183 insertions(+), 0 deletions(-)

...the physical line count of the driver is around 23K [1], and expect
that number to drop further as the driver is cleaned up (see below).
 
Fixes and reworks overview (current todo list summary):
1/ The core handles the low level hardware interface and state machines,
   but it also grew "too high" with OS abstracted convenience functions at
   its interface to the lldd layer.  The interface routines in
   drivers/scsi/isci/deprecated.c are set for removal as the lldd layer
   pushes down to replace the OS abstracted core routines with a native
   implementation.

2/ Locking and lifetime (lldd_dev) issues in the lldd 

3/ dma_map and kmap (in the pio data in case)

4/ Type safety, identifier names, and remaining style issues.

Current code:
As can be seen by the shortlog of the git tree a number of cleanups and
reworks have already been applied, some of which would have made the
early review more difficult.

Dan Williams (34):
      isci: Kconfig and Makefile
      isci: Make silicon revision configurable
      isci: kill unused build options
      isci: kill unnecessary call to pci_disable_msix()
      isci: kill can queue device file
      isci: auto install isci firmware
      isci: clean up abort checks at submission
      isci: cleanup sci_base_state
      isci: controller state machine cleanup step1
      isci: readable io request state machines step1
      isci: controller state machine cleanup step2
      isci: controller state machine cleanup step3
      isci: kill off dubious usage of likely/unlikely
      isci: fix driver name string
      isci: kill ->dma_pool_name
      isci: use a module scope kmem_cache rather then per-host
      isci: kill isci_controller_init_names
      isci: rename dev_p to pdev
      isci: cleanup scic_remote_device_construct
      isci: __iomem annotations
      isci: remove 'library' dependency for initializing registers
      isci: refactor init to handle arbitrary controller layouts
      isci: move controller allocation to the core (kill isci_module_struct)
      isci: kill 'library' interface
      isci: fix ata protocol detection
      isci: use sas_protocol_ata
      isci: remove some host template debug overrides
      isci: drop isci_queuecommand
      isci: fix up queue parameters
      isci: allow the silicon revision to be specified at runtime
      isci: smp_request is too large for stack allocation
      isci: sci_object cleanup step1
      isci: drop redundant name from path
      isci: consolidate core

Dave Jiang (11):
      isci: Loading oem params through binary firmware instead of via module params
      isci: move initialization to managed device model
      isci: remove sci_environment.h file
      isci: Moved lld logging calls to dev_* calls
      isci: Remove logger in core and use appropriate dev_* calls
      isci: Removing ASSERT() calls and change them to BUG_ON()
      isci: Move pci mapping functions to dma mapping functions
      isci: Remove MIN/MAX macro and use native linux macros
      isci: remove special macros for upper and lower 32 bits
      isci: Removing over-enthusiastic gotos
      isci: clean up scic_cb_io_request_get_physical_address()

Edmund Nadolski (11):
      isci: remove extraneous typedefs/enums/comments from sci_types.h
      isci: remove SCI_TIMER_CALLBACK_T typedef
      isci: remove SCI_OBJECT_HANDLE_T typedef
      isci: remove SCI_TASK_REQUEST_HANDLE_T typedef
      isci: remove SCI_IO_REQUEST_HANDLE_T typedef
      isci: remove SCI_PHY_HANDLE_T typedef
      isci: fix typo recieved to received
      isci: remove SCI_PORT_HANDLE_T typedef
      isci: remove SCI_REMOTE_DEVICE_HANDLE_T typedef
      isci: remove SCI_MEMORY_DESCRIPTOR_HANDLE_T typedef
      isci: remove SCI_CONTROLLER_HANDLE_T typedef

  146 files changed, 16620 insertions(+), 26286 deletions(-)

The patches that follow this cover letter are a snapshot of the current
state of the lldd portion of the driver (with the above cleanups already
applied).  The core will follow later, and the full driver is always
available from the git url above.

Suggestions and comments welcome.

--
Dan for the isci driver team

[1]: generated using David A. Wheeler's 'SLOCCount'.

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [RFC PATCH 1/6] isci: initialization
  2011-02-07  0:34 [RFC PATCH 0/6] isci: initial driver release (part1: intro and lldd) Dan Williams
@ 2011-02-07  0:34 ` Dan Williams
  2011-02-17  8:22   ` Jeff Garzik
                     ` (3 more replies)
  2011-02-07  0:34 ` [RFC PATCH 2/6] isci: task (libsas interface support) Dan Williams
                   ` (5 subsequent siblings)
  6 siblings, 4 replies; 36+ messages in thread
From: Dan Williams @ 2011-02-07  0:34 UTC (permalink / raw)
  To: james.bottomley
  Cc: dave.jiang, linux-scsi, jacek.danecki, ed.ciechanowski,
	jeffrey.d.skirvin, edmund.nadolski

Driver and controller initialization.

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/scsi/isci/Makefile |   30 ++
 drivers/scsi/isci/host.c   |  781 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/isci/host.h   |  283 ++++++++++++++++
 drivers/scsi/isci/init.c   |  613 +++++++++++++++++++++++++++++++++++
 drivers/scsi/isci/isci.h   |  138 ++++++++
 5 files changed, 1845 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/isci/Makefile
 create mode 100644 drivers/scsi/isci/host.c
 create mode 100644 drivers/scsi/isci/host.h
 create mode 100644 drivers/scsi/isci/init.c
 create mode 100644 drivers/scsi/isci/isci.h

diff --git a/drivers/scsi/isci/Makefile b/drivers/scsi/isci/Makefile
new file mode 100644
index 0000000..34f7af3
--- /dev/null
+++ b/drivers/scsi/isci/Makefile
@@ -0,0 +1,30 @@
+#TODO kill SCIC_SDS_4_ENABLED it is always true for this
+#generation of silicon
+EXTRA_CFLAGS += -DSCIC_SDS_4_ENABLED
+
+#temporary until atapi support ready
+EXTRA_CFLAGS += -DDISABLE_ATAPI
+
+EXTRA_CFLAGS += -Idrivers/scsi/isci/core/ -Idrivers/scsi/isci/
+obj-$(CONFIG_SCSI_ISCI) += isci.o
+isci-objs := init.o phy.o request.o sata.o \
+	     remote_device.o port.o timers.o deprecated.o \
+	     host.o task.o events.o \
+	     core/scic_sds_controller.o  \
+	     core/scic_sds_remote_device.o    \
+	     core/scic_sds_request.o \
+	     core/scic_sds_stp_request.o \
+	     core/scic_sds_stp_packet_request.o \
+	     core/scic_sds_stp_remote_device.o \
+	     core/scic_sds_port.o \
+	     core/scic_sds_port_configuration_agent.o \
+	     core/scic_sds_phy.o \
+	     core/scic_sds_ssp_request.o \
+	     core/scic_sds_remote_node_context.o \
+	     core/scic_sds_smp_request.o \
+	     core/scic_sds_smp_remote_device.o \
+	     core/scic_sds_remote_node_table.o \
+	     core/scic_sds_unsolicited_frame_control.o \
+	     core/sci_base_memory_descriptor_list.o \
+	     core/sci_base_state_machine.o \
+	     core/sci_util.o
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c
new file mode 100644
index 0000000..6f16f4d
--- /dev/null
+++ b/drivers/scsi/isci/host.c
@@ -0,0 +1,781 @@
+/*
+ * 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_io_request.h"
+#include "scic_remote_device.h"
+#include "scic_port.h"
+
+#include "port.h"
+#include "request.h"
+#include "host.h"
+
+/**
+ * isci_isr() - This function is the interrupt service routine for the
+ *    controller. It schedules the tasklet and returns.
+ * @vec: This parameter specifies the interrupt vector.
+ * @data: This parameter specifies the ISCI host object.
+ *
+ * IRQ_HANDLED if out interrupt otherwise, IRQ_NONE
+ */
+irqreturn_t isci_isr(int vec, void *data)
+{
+	struct isci_host *isci_host
+		= (struct isci_host *)data;
+	struct scic_controller_handler_methods *handlers
+		= &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR];
+	irqreturn_t ret = IRQ_NONE;
+
+	if (isci_host_get_state(isci_host) != isci_starting
+	    && handlers->interrupt_handler) {
+
+		if (handlers->interrupt_handler(isci_host->core_controller)) {
+			if (isci_host_get_state(isci_host) != isci_stopped) {
+				tasklet_schedule(
+					&isci_host->completion_tasklet);
+			} else
+				dev_dbg(&isci_host->pdev->dev,
+					"%s: controller stopped\n",
+					__func__);
+			ret = IRQ_HANDLED;
+		}
+	} else
+		dev_warn(&isci_host->pdev->dev,
+			 "%s: get_handler_methods failed, "
+			 "isci_host->status = 0x%x\n",
+			 __func__,
+			 isci_host_get_state(isci_host));
+
+	return ret;
+}
+
+irqreturn_t isci_legacy_isr(int vec, void *data)
+{
+	struct pci_dev *pdev = data;
+	struct isci_host *isci_host;
+	struct scic_controller_handler_methods *handlers;
+	irqreturn_t ret = IRQ_NONE;
+
+	/*
+	 *  Since this is a legacy interrupt, either or both
+	 *  controllers could have triggered it.  Thus, we have to call
+	 *  the legacy interrupt handler for all controllers on the
+	 *  PCI function.
+	 */
+	for_each_isci_host(isci_host, pdev) {
+		handlers = &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR];
+
+		if (isci_host_get_state(isci_host) != isci_starting
+		    && handlers->interrupt_handler) {
+
+			if (handlers->interrupt_handler(isci_host->core_controller)) {
+				if (isci_host_get_state(isci_host) != isci_stopped) {
+					tasklet_schedule(
+						&isci_host->completion_tasklet);
+				} else
+					dev_dbg(&isci_host->pdev->dev,
+						"%s: controller stopped\n",
+						__func__);
+				ret = IRQ_HANDLED;
+			}
+		} else
+			dev_warn(&isci_host->pdev->dev,
+				 "%s: get_handler_methods failed, "
+				 "isci_host->status = 0x%x\n",
+				 __func__,
+				 isci_host_get_state(isci_host));
+	}
+	return ret;
+}
+
+
+/**
+ * isci_host_start_complete() - This function is called by the core library,
+ *    through the ISCI Module, to indicate controller start status.
+ * @isci_host: This parameter specifies the ISCI host object
+ * @completion_status: This parameter specifies the completion status from the
+ *    core library.
+ *
+ */
+void isci_host_start_complete(
+	struct isci_host *isci_host,
+	enum sci_status completion_status)
+{
+	if (completion_status == SCI_SUCCESS) {
+		dev_dbg(&isci_host->pdev->dev,
+			"%s: completion_status: SCI_SUCCESS\n", __func__);
+		isci_host_change_state(isci_host, isci_ready);
+		complete_all(&isci_host->start_complete);
+	} else
+		dev_err(&isci_host->pdev->dev,
+			"controller start failed with "
+			"completion_status = 0x%x;",
+			completion_status);
+
+}
+
+
+
+/**
+ * isci_host_scan_finished() - This function is one of the SCSI Host Template
+ *    functions. The SCSI midlayer calls this function during a target scan,
+ *    approx. once every 10 millisecs.
+ * @shost: This parameter specifies the SCSI host being scanned
+ * @time: This parameter specifies the number of ticks since the scan started.
+ *
+ * scan status, zero indicates the SCSI midlayer should continue to poll,
+ * otherwise assume controller is ready.
+ */
+int isci_host_scan_finished(
+	struct Scsi_Host *shost,
+	unsigned long time)
+{
+	struct isci_host *isci_host
+		= isci_host_from_sas_ha(SHOST_TO_SAS_HA(shost));
+
+	struct scic_controller_handler_methods *handlers
+		= &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR];
+
+	if (handlers->interrupt_handler == NULL) {
+		dev_err(&isci_host->pdev->dev,
+			"%s: scic_controller_get_handler_methods failed\n",
+			__func__);
+		return 1;
+	}
+
+	/**
+	 * check interrupt_handler's status and call completion_handler if true,
+	 * link_up events should be coming from the scu core lib, as phy's come
+	 * online. for each link_up from the core, call
+	 * get_received_identify_address_frame, copy the frame into the
+	 * sas_phy object and call libsas notify_port_event(PORTE_BYTES_DMAED).
+	 * continue to return zero from thee scan_finished routine until
+	 * the scic_cb_controller_start_complete() call comes from the core.
+	 **/
+	if (handlers->interrupt_handler(isci_host->core_controller))
+		handlers->completion_handler(isci_host->core_controller);
+
+	if (isci_starting == isci_host_get_state(isci_host)
+	    && time < (HZ * 10)) {
+		dev_dbg(&isci_host->pdev->dev,
+			"%s: isci_host->status = %d, time = %ld\n",
+			     __func__, isci_host_get_state(isci_host), time);
+		return 0;
+	}
+
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_host->status = %d, time = %ld\n",
+		 __func__, isci_host_get_state(isci_host), time);
+
+	scic_controller_enable_interrupts(isci_host->core_controller);
+
+	return 1;
+
+}
+
+
+/**
+ * isci_host_scan_start() - This function is one of the SCSI Host Template
+ *    function, called by the SCSI mid layer berfore a target scan begins. The
+ *    core library controller start routine is called from here.
+ * @shost: This parameter specifies the SCSI host to be scanned
+ *
+ */
+void isci_host_scan_start(struct Scsi_Host *shost)
+{
+	struct isci_host *isci_host;
+
+	isci_host = isci_host_from_sas_ha(SHOST_TO_SAS_HA(shost));
+	isci_host_change_state(isci_host, isci_starting);
+
+	scic_controller_disable_interrupts(isci_host->core_controller);
+	init_completion(&isci_host->start_complete);
+	scic_controller_start(
+		isci_host->core_controller,
+		scic_controller_get_suggested_start_timeout(
+			isci_host->core_controller)
+		);
+}
+
+void isci_host_stop_complete(
+	struct isci_host *isci_host,
+	enum sci_status completion_status)
+{
+	isci_host_change_state(isci_host, isci_stopped);
+	scic_controller_disable_interrupts(
+		isci_host->core_controller
+		);
+	complete(&isci_host->stop_complete);
+}
+
+static struct coherent_memory_info *isci_host_alloc_mdl_struct(
+	struct isci_host *isci_host,
+	u32 size)
+{
+	struct coherent_memory_info *mdl_struct;
+	void *uncached_address = NULL;
+
+
+	mdl_struct = devm_kzalloc(&isci_host->pdev->dev,
+				  sizeof(*mdl_struct),
+				  GFP_KERNEL);
+	if (!mdl_struct)
+		return NULL;
+
+	INIT_LIST_HEAD(&mdl_struct->node);
+
+	uncached_address = dmam_alloc_coherent(&isci_host->pdev->dev,
+					       size,
+					       &mdl_struct->dma_handle,
+					       GFP_KERNEL);
+	if (!uncached_address)
+		return NULL;
+
+	/* memset the whole memory area. */
+	memset((char *)uncached_address, 0, size);
+	mdl_struct->vaddr = uncached_address;
+	mdl_struct->size = (size_t)size;
+
+	return mdl_struct;
+}
+
+static void isci_host_build_mde(
+	struct sci_physical_memory_descriptor *mde_struct,
+	struct coherent_memory_info *mdl_struct)
+{
+	unsigned long address = 0;
+	dma_addr_t dma_addr = 0;
+
+	address = (unsigned long)mdl_struct->vaddr;
+	dma_addr = mdl_struct->dma_handle;
+
+	/* to satisfy the alignment. */
+	if ((address % mde_struct->constant_memory_alignment) != 0) {
+		int align_offset
+			= (mde_struct->constant_memory_alignment
+			   - (address % mde_struct->constant_memory_alignment));
+		address += align_offset;
+		dma_addr += align_offset;
+	}
+
+	mde_struct->virtual_address = (void *)address;
+	mde_struct->physical_address = dma_addr;
+	mdl_struct->mde = mde_struct;
+}
+
+static int isci_host_mdl_allocate_coherent(
+	struct isci_host *isci_host)
+{
+	struct sci_physical_memory_descriptor *current_mde;
+	struct coherent_memory_info *mdl_struct;
+	u32 size = 0;
+
+	struct sci_base_memory_descriptor_list *mdl_handle
+		= sci_controller_get_memory_descriptor_list_handle(
+		isci_host->core_controller);
+
+	sci_mdl_first_entry(mdl_handle);
+
+	current_mde = sci_mdl_get_current_entry(mdl_handle);
+
+	while (current_mde != NULL) {
+
+		size = (current_mde->constant_memory_size
+			+ current_mde->constant_memory_alignment);
+
+		mdl_struct = isci_host_alloc_mdl_struct(isci_host, size);
+		if (!mdl_struct)
+			return -ENOMEM;
+
+		list_add_tail(&mdl_struct->node, &isci_host->mdl_struct_list);
+
+		isci_host_build_mde(current_mde, mdl_struct);
+
+		sci_mdl_next_entry(mdl_handle);
+		current_mde = sci_mdl_get_current_entry(mdl_handle);
+	}
+
+	return 0;
+}
+
+
+/**
+ * isci_host_completion_routine() - This function is the delayed service
+ *    routine that calls the sci core library's completion handler. It's
+ *    scheduled as a tasklet from the interrupt service routine when interrupts
+ *    in use, or set as the timeout function in polled mode.
+ * @data: This parameter specifies the ISCI host object
+ *
+ */
+static void isci_host_completion_routine(unsigned long data)
+{
+	struct isci_host *isci_host = (struct isci_host *)data;
+	struct scic_controller_handler_methods *handlers
+		= &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR];
+	struct list_head completed_request_list;
+	struct list_head aborted_request_list;
+	struct list_head *current_position;
+	struct list_head *next_position;
+	struct isci_request *request;
+	struct isci_request *next_request;
+	struct sas_task *task;
+
+	INIT_LIST_HEAD(&completed_request_list);
+	INIT_LIST_HEAD(&aborted_request_list);
+
+	spin_lock_irq(&isci_host->scic_lock);
+
+	if (handlers->completion_handler) {
+		handlers->completion_handler(
+			isci_host->core_controller
+			);
+	}
+	/* Take the lists of completed I/Os from the host. */
+	list_splice_init(&isci_host->requests_to_complete,
+			 &completed_request_list);
+
+	list_splice_init(&isci_host->requests_to_abort,
+			 &aborted_request_list);
+
+	spin_unlock_irq(&isci_host->scic_lock);
+
+	/* Process any completions in the lists. */
+	list_for_each_safe(current_position, next_position,
+			   &completed_request_list) {
+
+		request = list_entry(current_position, struct isci_request,
+				     completed_node);
+		task = isci_request_access_task(request);
+
+		/* Normal notification (task_done) */
+		dev_dbg(&isci_host->pdev->dev,
+			"%s: Normal - request/task = %p/%p\n",
+			__func__,
+			request,
+			task);
+
+		task->task_done(task);
+		task->lldd_task = NULL;
+
+		/* Free the request object. */
+		isci_request_free(isci_host, request);
+	}
+	list_for_each_entry_safe(request, next_request, &aborted_request_list,
+				 completed_node) {
+
+		task = isci_request_access_task(request);
+
+		/* Use sas_task_abort */
+		dev_warn(&isci_host->pdev->dev,
+			 "%s: Error - request/task = %p/%p\n",
+			 __func__,
+			 request,
+			 task);
+
+		/* Put the task into the abort path. */
+		sas_task_abort(task);
+	}
+
+}
+
+void isci_host_deinit(
+	struct isci_host *isci_host)
+{
+	int i;
+
+	isci_host_change_state(isci_host, isci_stopping);
+	for (i = 0; i < SCI_MAX_PORTS; i++) {
+		struct isci_port *port = &isci_host->isci_ports[i];
+		struct isci_remote_device *device, *tmpdev;
+		list_for_each_entry_safe(device, tmpdev,
+					 &port->remote_dev_list, node) {
+			isci_remote_device_change_state(device, isci_stopping);
+			isci_remote_device_stop(device);
+		}
+	}
+
+	/* stop the comtroller and wait for completion.  */
+	init_completion(&isci_host->stop_complete);
+	scic_controller_stop(
+		isci_host->core_controller,
+		SCIC_CONTROLLER_STOP_TIMEOUT
+		);
+	wait_for_completion(&isci_host->stop_complete);
+	/* next, reset the controller.           */
+	scic_controller_reset(isci_host->core_controller);
+}
+
+static int isci_verify_firmware(const struct firmware *fw,
+				struct isci_firmware *isci_fw)
+{
+	const u8 *tmp;
+
+	if (fw->size < ISCI_FIRMWARE_MIN_SIZE)
+		return -EINVAL;
+
+	tmp = fw->data;
+
+	/* 12th char should be the NULL terminate for the ID string */
+	if (tmp[11] != '\0')
+		return -EINVAL;
+
+	if (strncmp("#SCU MAGIC#", tmp, 11) != 0)
+		return -EINVAL;
+
+	isci_fw->id = tmp;
+	isci_fw->version = fw->data[ISCI_FW_VER_OFS];
+	isci_fw->subversion = fw->data[ISCI_FW_SUBVER_OFS];
+
+	tmp = fw->data + ISCI_FW_DATA_OFS;
+
+	while (*tmp != ISCI_FW_HDR_EOF) {
+		switch (*tmp) {
+		case ISCI_FW_HDR_PHYMASK:
+			tmp++;
+			isci_fw->phy_masks_size = *tmp;
+			tmp++;
+			isci_fw->phy_masks = (const u32 *)tmp;
+			tmp += sizeof(u32) * isci_fw->phy_masks_size;
+			break;
+
+		case ISCI_FW_HDR_PHYGEN:
+			tmp++;
+			isci_fw->phy_gens_size = *tmp;
+			tmp++;
+			isci_fw->phy_gens = (const u32 *)tmp;
+			tmp += sizeof(u32) * isci_fw->phy_gens_size;
+			break;
+
+		case ISCI_FW_HDR_SASADDR:
+			tmp++;
+			isci_fw->sas_addrs_size = *tmp;
+			tmp++;
+			isci_fw->sas_addrs = (const u64 *)tmp;
+			tmp += sizeof(u64) * isci_fw->sas_addrs_size;
+			break;
+
+		default:
+			pr_err("bad field in firmware binary blob\n");
+			return -EINVAL;
+		}
+	}
+
+	pr_info("isci firmware v%u.%u loaded.\n",
+	       isci_fw->version, isci_fw->subversion);
+
+	return SCI_SUCCESS;
+}
+
+static void __iomem *scu_base(struct isci_host *isci_host)
+{
+	struct pci_dev *pdev = isci_host->pdev;
+	int id = isci_host->id;
+
+	return pcim_iomap_table(pdev)[SCI_SCU_BAR * 2] + SCI_SCU_BAR_SIZE * id;
+}
+
+static void __iomem *smu_base(struct isci_host *isci_host)
+{
+	struct pci_dev *pdev = isci_host->pdev;
+	int id = isci_host->id;
+
+	return pcim_iomap_table(pdev)[SCI_SMU_BAR * 2] + SCI_SMU_BAR_SIZE * id;
+}
+
+#define SCI_MAX_TIMER_COUNT 25
+
+int isci_host_init(struct isci_host *isci_host)
+{
+	int err = 0;
+	int index = 0;
+	enum sci_status status;
+	struct scic_sds_controller *controller;
+	struct scic_sds_port *scic_port;
+	struct scic_controller_handler_methods *handlers
+		= &isci_host->scic_irq_handlers[0];
+	union scic_oem_parameters scic_oem_params;
+	union scic_user_parameters scic_user_params;
+	const struct firmware *fw = NULL;
+	struct isci_firmware *isci_fw = NULL;
+
+	INIT_LIST_HEAD(&isci_host->timer_list_struct.timers);
+	isci_timer_list_construct(
+		&isci_host->timer_list_struct,
+		SCI_MAX_TIMER_COUNT
+		);
+
+	controller = scic_controller_alloc(&isci_host->pdev->dev);
+
+	if (!controller) {
+		err = -ENOMEM;
+		dev_err(&isci_host->pdev->dev, "%s: failed (%d)\n", __func__, err);
+		goto out;
+	}
+
+	isci_host->core_controller = controller;
+	spin_lock_init(&isci_host->state_lock);
+	spin_lock_init(&isci_host->scic_lock);
+	spin_lock_init(&isci_host->queue_lock);
+
+	isci_host_change_state(isci_host, isci_starting);
+	isci_host->can_queue = ISCI_CAN_QUEUE_VAL;
+
+	status = scic_controller_construct(controller, scu_base(isci_host),
+					   smu_base(isci_host));
+
+	if (status != SCI_SUCCESS) {
+		dev_err(&isci_host->pdev->dev,
+			"%s: scic_controller_construct failed - status = %x\n",
+			__func__,
+			status);
+		err = -ENODEV;
+		goto out;
+	}
+
+	isci_host->sas_ha.dev = &isci_host->pdev->dev;
+	isci_host->sas_ha.lldd_ha = isci_host;
+
+	/*----------- SCIC controller Initialization Stuff ------------------
+	 * set association host adapter struct in core controller.
+	 */
+	sci_object_set_association(isci_host->core_controller,
+				   (void *)isci_host
+				   );
+
+	/* grab initial values stored in the controller object for OEM and USER
+	 * parameters */
+	scic_oem_parameters_get(controller, &scic_oem_params);
+	scic_user_parameters_get(controller, &scic_user_params);
+
+	isci_fw = devm_kzalloc(&isci_host->pdev->dev,
+			       sizeof(struct isci_firmware),
+			       GFP_KERNEL);
+	if (!isci_fw) {
+		dev_warn(&isci_host->pdev->dev,
+			 "allocating firmware struct failed\n");
+		dev_warn(&isci_host->pdev->dev,
+			 "Default OEM configuration being used:"
+			 " 4 narrow ports, and default SAS Addresses\n");
+		goto set_default_params;
+	}
+
+	status = request_firmware(&fw, ISCI_FW_NAME, &isci_host->pdev->dev);
+	if (status) {
+		dev_warn(&isci_host->pdev->dev,
+			 "Loading firmware failed, using default values\n");
+		dev_warn(&isci_host->pdev->dev,
+			 "Default OEM configuration being used:"
+			 " 4 narrow ports, and default SAS Addresses\n");
+		goto set_default_params;
+	}
+	else {
+		status = isci_verify_firmware(fw, isci_fw);
+		if (status != SCI_SUCCESS) {
+			dev_warn(&isci_host->pdev->dev,
+				 "firmware verification failed\n");
+			dev_warn(&isci_host->pdev->dev,
+				 "Default OEM configuration being used:"
+				 " 4 narrow ports, and default SAS "
+				 "Addresses\n");
+			goto set_default_params;
+		}
+
+		/* grab any OEM and USER parameters specified at module load */
+		status = isci_parse_oem_parameters(&scic_oem_params,
+						   isci_host->id, isci_fw);
+		if (status != SCI_SUCCESS) {
+			dev_warn(&isci_host->pdev->dev,
+				 "parsing firmware oem parameters failed\n");
+			err = -EINVAL;
+			goto out;
+		}
+
+		status = isci_parse_user_parameters(&scic_user_params,
+						    isci_host->id, isci_fw);
+		if (status != SCI_SUCCESS) {
+			dev_warn(&isci_host->pdev->dev,
+				 "%s: isci_parse_user_parameters"
+				 " failed\n", __func__);
+			err = -EINVAL;
+			goto out;
+		}
+	}
+
+ set_default_params:
+
+	status = scic_oem_parameters_set(isci_host->core_controller,
+					 &scic_oem_params
+					 );
+
+	if (status != SCI_SUCCESS) {
+		dev_warn(&isci_host->pdev->dev,
+			 "%s: scic_oem_parameters_set failed\n",
+			 __func__);
+		err = -ENODEV;
+		goto out;
+	}
+
+
+	status = scic_user_parameters_set(isci_host->core_controller,
+					  &scic_user_params
+					  );
+
+	if (status != SCI_SUCCESS) {
+		dev_warn(&isci_host->pdev->dev,
+			 "%s: scic_user_parameters_set failed\n",
+			 __func__);
+		err = -ENODEV;
+		goto out;
+	}
+
+	status = scic_controller_initialize(isci_host->core_controller);
+	if (status != SCI_SUCCESS) {
+		dev_warn(&isci_host->pdev->dev,
+			 "%s: scic_controller_initialize failed -"
+			 " status = 0x%x\n",
+			 __func__, status);
+		err = -ENODEV;
+		goto out;
+	}
+
+	/* @todo: use both MSI-X interrupts, and don't do indirect
+	 * calls to the handlers just register direct calls
+	 */
+	if (isci_host->pdev->msix_enabled) {
+		status = scic_controller_get_handler_methods(
+			SCIC_MSIX_INTERRUPT_TYPE,
+			SCI_MSIX_DOUBLE_VECTOR,
+			handlers
+			);
+	} else {
+		status = scic_controller_get_handler_methods(
+			SCIC_LEGACY_LINE_INTERRUPT_TYPE,
+			0,
+			handlers
+			);
+	}
+
+	if (status != SCI_SUCCESS) {
+		handlers->interrupt_handler = NULL;
+		handlers->completion_handler = NULL;
+		dev_err(&isci_host->pdev->dev,
+			"%s: scic_controller_get_handler_methods failed\n",
+			__func__);
+	}
+
+	tasklet_init(&isci_host->completion_tasklet,
+		     isci_host_completion_routine,
+		     (unsigned long)isci_host
+		     );
+
+	INIT_LIST_HEAD(&(isci_host->mdl_struct_list));
+
+	INIT_LIST_HEAD(&isci_host->requests_to_complete);
+	INIT_LIST_HEAD(&isci_host->requests_to_abort);
+
+	/* populate mdl with dma memory. scu_mdl_allocate_coherent() */
+	err = isci_host_mdl_allocate_coherent(isci_host);
+
+	if (err)
+		goto err_out;
+
+	/*
+	 * keep the pool alloc size around, will use it for a bounds checking
+	 * when trying to convert virtual addresses to physical addresses
+	 */
+	isci_host->dma_pool_alloc_size = sizeof(struct isci_request) +
+					 scic_io_request_get_object_size();
+	isci_host->dma_pool = dmam_pool_create(DRV_NAME, &isci_host->pdev->dev,
+					       isci_host->dma_pool_alloc_size,
+					       SLAB_HWCACHE_ALIGN, 0);
+
+	if (!isci_host->dma_pool) {
+		err = -ENOMEM;
+		goto req_obj_err_out;
+	}
+
+	for (index = 0; index < SCI_MAX_PORTS; index++) {
+		isci_port_init(&isci_host->isci_ports[index],
+			       isci_host, index);
+	}
+
+	for (index = 0; index < SCI_MAX_PHYS; index++)
+		isci_phy_init(&isci_host->phys[index], isci_host, index);
+
+	/* Why are we doing this? Is this even necessary? */
+	memcpy(&isci_host->sas_addr[0], &isci_host->phys[0].sas_addr[0],
+	       SAS_ADDR_SIZE);
+
+	/* Start the ports */
+	for (index = 0; index < SCI_MAX_PORTS; index++) {
+
+		scic_controller_get_port_handle(controller, index, &scic_port);
+		scic_port_start(scic_port);
+	}
+
+	goto out;
+
+/* SPB_Debug: destroy request object cache */
+ req_obj_err_out:
+/* SPB_Debug: destroy remote object cache */
+ err_out:
+/* SPB_Debug: undo controller init, construct and alloc, remove from parent
+ * controller list. */
+ out:
+	if (fw)
+		release_firmware(fw);
+	return err;
+}
diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h
new file mode 100644
index 0000000..3530076
--- /dev/null
+++ b/drivers/scsi/isci/host.h
@@ -0,0 +1,283 @@
+/*
+ * 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.
+ */
+
+/**
+ * This file contains the isci_module initialization routines.
+ *
+ * host.h
+ */
+
+
+
+#if !defined(_SCI_HOST_H_)
+#define _SCI_HOST_H_
+
+#include "phy.h"
+/*#include "task.h"*/
+#include "timers.h"
+#include "remote_device.h"
+#include "scic_user_callback.h"
+
+#define DRV_NAME "isci"
+#define SCI_PCI_BAR_COUNT 2
+#define SCI_NUM_MSI_X_INT 2
+#define SCI_SMU_BAR       0
+#define SCI_SMU_BAR_SIZE  (16*1024)
+#define SCI_SCU_BAR       1
+#define SCI_SCU_BAR_SIZE  (4*1024*1024)
+#define SCI_IO_SPACE_BAR0 2
+#define SCI_IO_SPACE_BAR1 3
+#define SCI_MSIX_NORMAL_VECTOR 0
+#define SCI_MSIX_ERROR_VECTOR 1
+#define SCI_MSIX_SINGLE_VECTOR 1
+#define SCI_MSIX_DOUBLE_VECTOR 2
+#define ISCI_CAN_QUEUE_VAL 250 /* < SCI_MAX_IO_REQUESTS ? */
+#define SCIC_CONTROLLER_STOP_TIMEOUT 5000
+
+struct coherent_memory_info {
+	struct list_head node;
+	dma_addr_t dma_handle;
+	void *vaddr;
+	size_t size;
+	struct sci_physical_memory_descriptor *mde;
+};
+
+struct isci_host {
+	struct scic_sds_controller *core_controller;
+	struct scic_controller_handler_methods scic_irq_handlers[SCI_NUM_MSI_X_INT];
+	union scic_oem_parameters oem_parameters;
+
+	int id; /* unique within a given pci device */
+	struct isci_timer_list timer_list_struct;
+	void *core_ctrl_memory;
+	struct dma_pool *dma_pool;
+	unsigned int dma_pool_alloc_size;
+	struct isci_phy phys[SCI_MAX_PHYS];
+
+	/* isci_ports and sas_ports are implicitly parallel to the
+	 * ports maintained by the core
+	 */
+	struct isci_port isci_ports[SCI_MAX_PORTS];
+	struct asd_sas_port sas_ports[SCI_MAX_PORTS];
+	struct sas_ha_struct sas_ha;
+
+	int can_queue;
+	spinlock_t queue_lock;
+	spinlock_t state_lock;
+
+	struct pci_dev *pdev;
+	u8 sas_addr[SAS_ADDR_SIZE];
+
+	enum isci_status status;
+	struct Scsi_Host *shost;
+	struct tasklet_struct completion_tasklet;
+	struct list_head mdl_struct_list;
+	struct list_head requests_to_complete;
+	struct list_head requests_to_abort;
+	struct completion stop_complete;
+	struct completion start_complete;
+	spinlock_t scic_lock;
+	struct isci_host *next;
+};
+
+
+/**
+ * struct isci_pci_info - This class represents the pci function containing the
+ *    controllers. Depending on PCI SKU, there could be up to 2 controllers in
+ *    the PCI function.
+ */
+#define SCI_MAX_MSIX_INT (SCI_NUM_MSI_X_INT*SCI_MAX_CONTROLLERS)
+
+struct isci_pci_info {
+	struct msix_entry msix_entries[SCI_MAX_MSIX_INT];
+	int core_lib_array_index;
+	SCI_LIBRARY_HANDLE_T core_lib_handle;
+	struct isci_host *hosts;
+};
+
+static inline struct isci_pci_info *to_pci_info(struct pci_dev *pdev)
+{
+	return pci_get_drvdata(pdev);
+}
+
+#define for_each_isci_host(isci_host, pdev) \
+	for (isci_host = to_pci_info(pdev)->hosts;\
+	     isci_host; isci_host = isci_host->next)
+
+static inline
+enum isci_status isci_host_get_state(
+	struct isci_host *isci_host)
+{
+	return isci_host->status;
+}
+
+
+static inline void isci_host_change_state(
+	struct isci_host *isci_host,
+	enum isci_status status)
+{
+	unsigned long flags;
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_host = %p, state = 0x%x",
+		__func__,
+		isci_host,
+		status);
+	spin_lock_irqsave(&isci_host->state_lock, flags);
+	isci_host->status = status;
+	spin_unlock_irqrestore(&isci_host->state_lock, flags);
+
+}
+
+static inline int isci_host_can_queue(
+	struct isci_host *isci_host,
+	int num)
+{
+	int ret = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&isci_host->queue_lock, flags);
+	if ((isci_host->can_queue - num) < 0) {
+		dev_dbg(&isci_host->pdev->dev,
+			"%s: isci_host->can_queue = %d\n",
+			__func__,
+			isci_host->can_queue);
+		ret = -SAS_QUEUE_FULL;
+
+	} else
+		isci_host->can_queue -= num;
+
+	spin_unlock_irqrestore(&isci_host->queue_lock, flags);
+
+	return ret;
+}
+
+static inline void isci_host_can_dequeue(
+	struct isci_host *isci_host,
+	int num)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&isci_host->queue_lock, flags);
+	isci_host->can_queue += num;
+	spin_unlock_irqrestore(&isci_host->queue_lock, flags);
+}
+
+/**
+ * isci_host_from_sas_ha() - This accessor retrieves the isci_host object
+ *    reference from the Linux sas_ha_struct reference.
+ * @ha_struct,: This parameter points to the Linux sas_ha_struct object
+ *
+ * A reference to the associated isci_host structure.
+ */
+#define isci_host_from_sas_ha(ha_struct) \
+	((struct isci_host *)(ha_struct)->lldd_ha)
+
+/**
+ * isci_host_scan_finished() -
+ *
+ * This function is one of the SCSI Host Template functions. The SCSI midlayer
+ * calls this function during a target scan, approx. once every 10 millisecs.
+ */
+int isci_host_scan_finished(
+	struct Scsi_Host *,
+	unsigned long);
+
+
+/**
+ * isci_host_scan_start() -
+ *
+ * This function is one of the SCSI Host Template function, called by the SCSI
+ * mid layer berfore a target scan begins. The core library controller start
+ * routine is called from here.
+ */
+void isci_host_scan_start(
+	struct Scsi_Host *);
+
+/**
+ * isci_host_start_complete() -
+ *
+ * This function is called by the core library, through the ISCI Module, to
+ * indicate controller start status.
+ */
+void isci_host_start_complete(
+	struct isci_host *,
+	enum sci_status);
+
+void isci_host_stop_complete(
+	struct isci_host *isci_host,
+	enum sci_status completion_status);
+
+int isci_host_init(struct isci_host *);
+
+void isci_host_init_controller_names(
+	struct isci_host *isci_host,
+	unsigned int controller_idx);
+
+void isci_host_deinit(
+	struct isci_host *);
+
+void isci_host_port_link_up(
+	struct isci_host *,
+	struct scic_sds_port *,
+	struct scic_sds_phy *);
+int isci_host_dev_found(struct domain_device *);
+
+void isci_host_remote_device_start_complete(
+	struct isci_host *,
+	struct isci_remote_device *,
+	enum sci_status);
+
+#endif /* !defined(_SCI_HOST_H_) */
diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c
new file mode 100644
index 0000000..07b072f
--- /dev/null
+++ b/drivers/scsi/isci/init.c
@@ -0,0 +1,613 @@
+/*
+ * 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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/string.h>
+#include "isci.h"
+#include "task.h"
+#include "sci_controller_constants.h"
+#include "scic_remote_device.h"
+#include "sci_environment.h"
+
+static struct scsi_transport_template *isci_transport_template;
+struct kmem_cache *isci_kmem_cache;
+
+static DEFINE_PCI_DEVICE_TABLE(isci_id_table) = {
+	{ PCI_VDEVICE(INTEL, 0x1D61),},
+	{ PCI_VDEVICE(INTEL, 0x1D63),},
+	{ PCI_VDEVICE(INTEL, 0x1D65),},
+	{ PCI_VDEVICE(INTEL, 0x1D67),},
+	{ PCI_VDEVICE(INTEL, 0x1D69),},
+	{ PCI_VDEVICE(INTEL, 0x1D6B),},
+	{ PCI_VDEVICE(INTEL, 0x1D60),},
+	{ PCI_VDEVICE(INTEL, 0x1D62),},
+	{ PCI_VDEVICE(INTEL, 0x1D64),},
+	{ PCI_VDEVICE(INTEL, 0x1D66),},
+	{ PCI_VDEVICE(INTEL, 0x1D68),},
+	{ PCI_VDEVICE(INTEL, 0x1D6A),},
+	{}
+};
+
+static int __devinit isci_pci_probe(
+	struct pci_dev *pdev,
+	const struct pci_device_id *device_id_p);
+
+static void __devexit isci_pci_remove(struct pci_dev *pdev);
+
+MODULE_DEVICE_TABLE(pci, isci_id_table);
+
+static struct pci_driver isci_pci_driver = {
+	.name		= DRV_NAME,
+	.id_table	= isci_id_table,
+	.probe		= isci_pci_probe,
+	.remove		= __devexit_p(isci_pci_remove),
+};
+
+/* linux isci specific settings */
+int loglevel = 3;
+module_param(loglevel, int, S_IRUGO | S_IWUSR);
+
+#if defined(CONFIG_PBG_HBA_A0)
+int isci_si_rev = ISCI_SI_REVA0;
+#elif defined(CONFIG_PBG_HBA_A2)
+int isci_si_rev = ISCI_SI_REVA2;
+#else
+int isci_si_rev = ISCI_SI_REVB0;
+#endif
+module_param(isci_si_rev, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(isci_si_rev, "override default si rev (0: A0 1: A2 2: B0)");
+
+static struct scsi_host_template isci_sht = {
+
+	.module				= THIS_MODULE,
+	.name				= DRV_NAME,
+	.queuecommand			= sas_queuecommand,
+	.target_alloc			= sas_target_alloc,
+	.slave_configure		= sas_slave_configure,
+	.slave_destroy			= sas_slave_destroy,
+	.scan_finished			= isci_host_scan_finished,
+	.scan_start			= isci_host_scan_start,
+	.change_queue_depth		= sas_change_queue_depth,
+	.change_queue_type		= sas_change_queue_type,
+	.bios_param			= sas_bios_param,
+	.can_queue			= ISCI_CAN_QUEUE_VAL,
+	.cmd_per_lun			= 1,
+	.this_id			= -1,
+	.sg_tablesize			= SG_ALL,
+	.max_sectors			= SCSI_DEFAULT_MAX_SECTORS,
+	.use_clustering			= ENABLE_CLUSTERING,
+	.eh_device_reset_handler	= sas_eh_device_reset_handler,
+	.eh_bus_reset_handler		= isci_bus_reset_handler,
+	.slave_alloc			= sas_slave_alloc,
+	.target_destroy			= sas_target_destroy,
+	.ioctl				= sas_ioctl,
+};
+
+static struct sas_domain_function_template isci_transport_ops  = {
+
+	/* The class calls these to notify the LLDD of an event. */
+	.lldd_port_formed	= isci_port_formed,
+	.lldd_port_deformed	= isci_port_deformed,
+
+	/* The class calls these when a device is found or gone. */
+	.lldd_dev_found		= isci_remote_device_found,
+	.lldd_dev_gone		= isci_remote_device_gone,
+
+	.lldd_execute_task	= isci_task_execute_task,
+	/* Task Management Functions. Must be called from process context. */
+	.lldd_abort_task	= isci_task_abort_task,
+	.lldd_abort_task_set	= isci_task_abort_task_set,
+	.lldd_clear_aca		= isci_task_clear_aca,
+	.lldd_clear_task_set	= isci_task_clear_task_set,
+	.lldd_I_T_nexus_reset	= isci_task_I_T_nexus_reset,
+	.lldd_lu_reset		= isci_task_lu_reset,
+	.lldd_query_task	= isci_task_query_task,
+
+	/* Port and Adapter management */
+	.lldd_clear_nexus_port	= isci_task_clear_nexus_port,
+	.lldd_clear_nexus_ha	= isci_task_clear_nexus_ha,
+
+	/* Phy management */
+	.lldd_control_phy	= isci_phy_control,
+};
+
+
+/******************************************************************************
+* P R O T E C T E D  M E T H O D S
+******************************************************************************/
+
+
+
+/**
+ * isci_register_sas_ha() - This method initializes various lldd
+ *    specific members of the sas_ha struct and calls the libsas
+ *    sas_register_ha() function.
+ * @isci_host: This parameter specifies the lldd specific wrapper for the
+ *    libsas sas_ha struct.
+ *
+ * This method returns an error code indicating sucess or failure. The user
+ * should check for possible memory allocation error return otherwise, a zero
+ * indicates success.
+ */
+static int isci_register_sas_ha(struct isci_host *isci_host)
+{
+	int i;
+	struct sas_ha_struct *sas_ha = &(isci_host->sas_ha);
+	struct asd_sas_phy **sas_phys;
+	struct asd_sas_port **sas_ports;
+
+	sas_phys = devm_kzalloc(&isci_host->pdev->dev,
+				SCI_MAX_PHYS * sizeof(void *),
+				GFP_KERNEL);
+	if (!sas_phys)
+		return -ENOMEM;
+
+	sas_ports = devm_kzalloc(&isci_host->pdev->dev,
+				 SCI_MAX_PORTS * sizeof(void *),
+				 GFP_KERNEL);
+	if (!sas_ports)
+		return -ENOMEM;
+
+	/*----------------- Libsas Initialization Stuff----------------------
+	 * Set various fields in the sas_ha struct:
+	 */
+
+	sas_ha->sas_ha_name = DRV_NAME;
+	sas_ha->lldd_module = THIS_MODULE;
+	sas_ha->sas_addr    = &(isci_host->sas_addr[0]);
+
+	/* set the array of phy and port structs.  */
+	for (i = 0; i < SCI_MAX_PHYS; i++) {
+		sas_phys[i] = &(isci_host->phys[i].sas_phy);
+		sas_ports[i] = &(isci_host->sas_ports[i]);
+	}
+
+	sas_ha->sas_phy  = sas_phys;
+	sas_ha->sas_port = sas_ports;
+	sas_ha->num_phys = SCI_MAX_PHYS;
+
+	sas_ha->lldd_queue_size = ISCI_CAN_QUEUE_VAL;
+	sas_ha->lldd_max_execute_num = 1;
+	sas_ha->strict_wide_ports = 1;
+
+	sas_register_ha(sas_ha);
+
+	return 0;
+}
+
+static void isci_unregister_sas_ha(struct isci_host *isci_host)
+{
+	if (!isci_host)
+		return;
+
+	sas_unregister_ha(&(isci_host->sas_ha));
+
+	sas_remove_host(isci_host->shost);
+	scsi_remove_host(isci_host->shost);
+	scsi_host_put(isci_host->shost);
+}
+
+static int __devinit isci_pci_init(struct pci_dev *pdev)
+{
+	int err, bar_num, bar_mask;
+	void __iomem * const *iomap;
+
+	err = pcim_enable_device(pdev);
+	if (err) {
+		dev_err(&pdev->dev,
+			"failed enable PCI device %s!\n",
+			pci_name(pdev));
+		return err;
+	}
+
+	for (bar_num = 0; bar_num < SCI_PCI_BAR_COUNT; bar_num++)
+		bar_mask |= 1 << (bar_num * 2);
+
+	err = pcim_iomap_regions(pdev, bar_mask, DRV_NAME);
+	if (err)
+		return err;
+
+	iomap = pcim_iomap_table(pdev);
+	if (!iomap)
+		return -ENOMEM;
+
+	pci_set_master(pdev);
+
+	err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
+	if (err) {
+		err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+		if (err)
+			return err;
+	}
+
+	err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
+	if (err) {
+		err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static struct isci_host *isci_host_by_id(struct pci_dev *pdev, int id)
+{
+	struct isci_host *h;
+
+	for_each_isci_host(h, pdev)
+		if (h->id == id)
+			return h;
+	return NULL;
+}
+
+static int num_controllers(struct pci_dev *pdev)
+{
+	/* bar size alone can tell us if we are running with a dual controller
+	 * part, no need to trust revision ids that might be under broken firmware
+	 * control
+	 */
+	resource_size_t scu_bar_size = pci_resource_len(pdev, SCI_SCU_BAR*2);
+	resource_size_t smu_bar_size = pci_resource_len(pdev, SCI_SMU_BAR*2);
+	
+	if (scu_bar_size >= SCI_SCU_BAR_SIZE*SCI_MAX_CONTROLLERS &&
+	    smu_bar_size >= SCI_SMU_BAR_SIZE*SCI_MAX_CONTROLLERS)
+		return SCI_MAX_CONTROLLERS;
+	else
+		return 1;
+}
+
+static int isci_setup_interrupts(struct pci_dev *pdev)
+{
+	int err, i, num_msix;
+	struct isci_pci_info *pci_info = to_pci_info(pdev);
+
+	/*
+	 *  Determine the number of vectors associated with this
+	 *  PCI function.
+	 */
+	num_msix = num_controllers(pdev) * SCI_NUM_MSI_X_INT;
+
+	for (i = 0; i < num_msix; i++)
+		pci_info->msix_entries[i].entry = i;
+
+	err = pci_enable_msix(pdev, pci_info->msix_entries, num_msix);
+	if (err)
+		goto intx;
+
+	for (i = 0; i < num_msix; i++) {
+		int id = i / SCI_NUM_MSI_X_INT;
+		struct msix_entry *msix = &pci_info->msix_entries[i];
+		struct isci_host *isci_host = isci_host_by_id(pdev, id);
+
+		BUG_ON(!isci_host);
+
+		/* @todo: need to handle error case. */
+		err = devm_request_irq(&pdev->dev, msix->vector, isci_isr, 0,
+				       DRV_NAME"-msix", isci_host);
+		if (!err)
+			continue;
+
+		dev_info(&pdev->dev, "msix setup failed falling back to intx\n");
+		while (i--) {
+			id = i / SCI_NUM_MSI_X_INT;
+			isci_host = isci_host_by_id(pdev, id);
+			msix = &pci_info->msix_entries[i];
+			devm_free_irq(&pdev->dev, msix->vector, isci_host);
+		}
+		pci_disable_msix(pdev);
+		goto intx;
+	}
+
+	return 0;
+
+ intx:
+	err = devm_request_irq(&pdev->dev, pdev->irq, isci_legacy_isr,
+			       IRQF_SHARED, DRV_NAME"-intx", pdev);
+
+	return err;
+}
+
+/**
+ * isci_parse_oem_parameters() - This method will take OEM parameters
+ *    from the module init parameters and copy them to oem_params. This will
+ *    only copy values that are not set to the module parameter default values
+ * @oem_parameters: This parameter specifies the controller default OEM
+ *    parameters. It is expected that this has been initialized to the default
+ *    parameters for the controller
+ *
+ *
+ */
+enum sci_status isci_parse_oem_parameters(union scic_oem_parameters *oem_params,
+					  int scu_index,
+					  struct isci_firmware *fw)
+{
+	int i;
+
+	/* check for valid inputs */
+	if (!(scu_index >= 0
+	      && scu_index < SCI_MAX_CONTROLLERS
+	      && oem_params != NULL)) {
+		return SCI_FAILURE;
+	}
+
+	for (i = 0; i < SCI_MAX_PHYS; i++) {
+		int array_idx = i + (SCI_MAX_PHYS * scu_index);
+		u64 sas_addr = fw->sas_addrs[array_idx];
+
+		if (sas_addr != 0) {
+			oem_params->sds1.phys[i].sas_address.low =
+				(u32)(sas_addr & 0xffffffff);
+			oem_params->sds1.phys[i].sas_address.high =
+				(u32)((sas_addr >> 32) & 0xffffffff);
+		}
+	}
+
+	for (i = 0; i < SCI_MAX_PORTS; i++) {
+		int array_idx = i + (SCI_MAX_PORTS * scu_index);
+		u32 pmask = fw->phy_masks[array_idx];
+
+		oem_params->sds1.ports[i].phy_mask = pmask;
+	}
+
+	return SCI_SUCCESS;
+}
+
+/**
+ * isci_parse_user_parameters() - This method will take user parameters
+ *    from the module init parameters and copy them to user_params. This will
+ *    only copy values that are not set to the module parameter default values
+ * @user_parameters: This parameter specifies the controller default user
+ *    parameters. It is expected that this has been initialized to the default
+ *    parameters for the controller
+ *
+ *
+ */
+enum sci_status isci_parse_user_parameters(
+	union scic_user_parameters *user_params,
+	int scu_index,
+	struct isci_firmware *fw)
+{
+	int i;
+
+	if (!(scu_index >= 0
+	      && scu_index < SCI_MAX_CONTROLLERS
+	      && user_params != NULL)) {
+		return SCI_FAILURE;
+	}
+
+	for (i = 0; i < SCI_MAX_PORTS; i++) {
+		int array_idx = i + (SCI_MAX_PORTS * scu_index);
+		u32 gen = fw->phy_gens[array_idx];
+
+		user_params->sds1.phys[i].max_speed_generation = gen;
+
+	}
+
+	return SCI_SUCCESS;
+}
+
+static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id)
+{
+	struct isci_host *isci_host;
+	struct Scsi_Host *shost;
+	int err;
+
+	isci_host = devm_kzalloc(&pdev->dev, sizeof(*isci_host), GFP_KERNEL);
+	if (!isci_host)
+		return NULL;
+
+	isci_host->pdev = pdev;
+	isci_host->id = id;
+
+	shost = scsi_host_alloc(&isci_sht, sizeof(void *));
+	if (!shost)
+		return NULL;
+	isci_host->shost = shost;
+
+	err = isci_host_init(isci_host);
+	if (err)
+		goto err_shost;
+
+	SHOST_TO_SAS_HA(shost) = &isci_host->sas_ha;
+	isci_host->sas_ha.core.shost = shost;
+	shost->transportt = isci_transport_template;
+
+	shost->max_id = ~0;
+	shost->max_lun = ~0;
+	shost->max_cmd_len = MAX_COMMAND_SIZE;
+
+	err = scsi_add_host(shost, &pdev->dev);
+	if (err)
+		goto err_shost;
+
+	err = isci_register_sas_ha(isci_host);
+	if (err)
+		goto err_shost_remove;
+
+	return isci_host;
+
+ err_shost_remove:
+	scsi_remove_host(shost);
+ err_shost:
+	scsi_host_put(shost);
+
+	return NULL;
+}
+
+static void check_si_rev(struct pci_dev *pdev)
+{
+	if (num_controllers(pdev) > 1)
+		isci_si_rev = ISCI_SI_REVB0;
+	else {
+		switch (pdev->revision) {
+		case 0:
+		case 1:
+			/* if the id is ambiguous don't update isci_si_rev */
+			break;
+		case 3:
+			isci_si_rev = ISCI_SI_REVA2;
+			break;
+		default:
+		case 4:
+			isci_si_rev = ISCI_SI_REVB0;
+			break;
+		}
+	}
+
+	dev_info(&pdev->dev, "driver configured for %s silicon (rev: %d)\n",
+		 isci_si_rev == ISCI_SI_REVA0 ? "A0" :
+		 isci_si_rev == ISCI_SI_REVA2 ? "A2" : "B0", pdev->revision);
+		
+}
+
+static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct isci_pci_info *pci_info;
+	int err, i;
+	struct isci_host *isci_host;
+
+	check_si_rev(pdev);
+
+	pci_info = devm_kzalloc(&pdev->dev, sizeof(*pci_info), GFP_KERNEL);
+	if (!pci_info)
+		return -ENOMEM;
+	pci_set_drvdata(pdev, pci_info);
+
+	err = isci_pci_init(pdev);
+	if (err)
+		return err;
+
+	for (i = 0; i < num_controllers(pdev); i++) {
+		struct isci_host *h = isci_host_alloc(pdev, i);
+
+		if (!h) {
+			err = -ENOMEM;
+			goto err_host_alloc;
+		}
+
+		h->next = pci_info->hosts;
+		pci_info->hosts = h;
+	}
+
+	err = isci_setup_interrupts(pdev);
+	if (err)
+		goto err_host_alloc;
+
+	for_each_isci_host(isci_host, pdev)
+		scsi_scan_host(isci_host->shost);
+
+	return 0;
+
+ err_host_alloc:
+	for_each_isci_host(isci_host, pdev)
+		isci_unregister_sas_ha(isci_host);
+	return err;
+}
+
+static void __devexit isci_pci_remove(struct pci_dev *pdev)
+{
+	struct isci_host *isci_host;
+
+	for_each_isci_host(isci_host, pdev) {
+		isci_unregister_sas_ha(isci_host);
+		isci_host_deinit(isci_host);
+		scic_controller_disable_interrupts(isci_host->core_controller);
+	}
+}
+
+static __init int isci_init(void)
+{
+	int err = -ENOMEM;
+
+	pr_info("%s: Intel(R) C600 SAS Controller Driver\n", DRV_NAME);
+
+	isci_kmem_cache = kmem_cache_create(DRV_NAME,
+					    sizeof(struct isci_remote_device) +
+					    scic_remote_device_get_object_size(),
+					    0, 0, NULL);
+	if (!isci_kmem_cache)
+		return err;
+
+	isci_transport_template = sas_domain_attach_transport(&isci_transport_ops);
+	if (!isci_transport_template)
+		goto err_kmem;
+
+	err = pci_register_driver(&isci_pci_driver);
+	if (err)
+		goto err_sas;
+
+	return 0;
+
+ err_sas:
+	sas_release_transport(isci_transport_template);
+ err_kmem:
+	kmem_cache_destroy(isci_kmem_cache);
+
+	return err;
+}
+
+static __exit void isci_exit(void)
+{
+	pci_unregister_driver(&isci_pci_driver);
+	sas_release_transport(isci_transport_template);
+	kmem_cache_destroy(isci_kmem_cache);
+}
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_FIRMWARE(ISCI_FW_NAME);
+module_init(isci_init);
+module_exit(isci_exit);
diff --git a/drivers/scsi/isci/isci.h b/drivers/scsi/isci/isci.h
new file mode 100644
index 0000000..7d984f4
--- /dev/null
+++ b/drivers/scsi/isci/isci.h
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+/**
+ * This file contains the isci_module object definition.
+ *
+ * isci.h
+ */
+
+#if !defined(_SCI_MODULE_H_)
+#define _SCI_MODULE_H_
+
+/**
+ * This file contains the SCI low level driver interface to the SCI and Libsas
+ *    Libraries.
+ *
+ * isci.h
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+#include <linux/bug.h>
+#include <scsi/libsas.h>
+#include <scsi/scsi.h>
+
+#include "sci_types.h"
+#include "sci_base_controller.h"
+#include "scic_controller.h"
+#include "host.h"
+#include "timers.h"
+#include "sci_status.h"
+
+extern int loglevel;
+extern struct kmem_cache *isci_kmem_cache;
+
+#define ISCI_FW_NAME		"isci/isci_firmware.bin"
+
+#define ISCI_FIRMWARE_MIN_SIZE	149
+
+#define ISCI_FW_IDSIZE		12
+#define ISCI_FW_VER_OFS		ISCI_FW_IDSIZE
+#define ISCI_FW_SUBVER_OFS	ISCI_FW_VER_OFS + 1
+#define ISCI_FW_DATA_OFS	ISCI_FW_SUBVER_OFS + 1
+
+#define ISCI_FW_HDR_PHYMASK	0x1
+#define ISCI_FW_HDR_PHYGEN	0x2
+#define ISCI_FW_HDR_SASADDR	0x3
+#define ISCI_FW_HDR_EOF		0xff
+
+struct isci_firmware {
+	const u8 *id;
+	u8 version;
+	u8 subversion;
+	const u32 *phy_masks;
+	u8 phy_masks_size;
+	const u32 *phy_gens;
+	u8 phy_gens_size;
+	const u64 *sas_addrs;
+	u8 sas_addrs_size;
+};
+
+irqreturn_t isci_isr(int vec, void *data);
+irqreturn_t isci_legacy_isr(int vec, void *data);
+
+enum sci_status isci_parse_oem_parameters(
+	union scic_oem_parameters *oem_params,
+	int scu_index,
+	struct isci_firmware *fw);
+
+enum sci_status isci_parse_user_parameters(
+	union scic_user_parameters *user_params,
+	int scu_index,
+	struct isci_firmware *fw);
+
+#ifdef ISCI_SLAVE_ALLOC
+extern int ISCI_SLAVE_ALLOC(struct scsi_device *scsi_dev);
+#endif /* ISCI_SLAVE_ALLOC */
+
+#ifdef ISCI_SLAVE_DESTROY
+extern void ISCI_SLAVE_DESTROY(struct scsi_device *scsi_dev);
+#endif  /* ISCI_SLAVE_DESTROY */
+#endif  /* !defined(_SCI_MODULE_H_) */


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [RFC PATCH 2/6] isci: task (libsas interface support)
  2011-02-07  0:34 [RFC PATCH 0/6] isci: initial driver release (part1: intro and lldd) Dan Williams
  2011-02-07  0:34 ` [RFC PATCH 1/6] isci: initialization Dan Williams
@ 2011-02-07  0:34 ` Dan Williams
  2011-02-09 15:01   ` David Milburn
  2011-02-07  0:34 ` [RFC PATCH 3/6] isci: request (core request infrastructure) Dan Williams
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 36+ messages in thread
From: Dan Williams @ 2011-02-07  0:34 UTC (permalink / raw)
  To: james.bottomley
  Cc: dave.jiang, linux-scsi, jacek.danecki, ed.ciechanowski,
	jeffrey.d.skirvin, edmund.nadolski

libsas interface routines and infrastructure.

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/scsi/isci/task.c | 1691 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/isci/task.h |  368 ++++++++++
 2 files changed, 2059 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/isci/task.c
 create mode 100644 drivers/scsi/isci/task.h

diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c
new file mode 100644
index 0000000..5e6f558
--- /dev/null
+++ b/drivers/scsi/isci/task.c
@@ -0,0 +1,1691 @@
+/*
+ * 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 <linux/completion.h>
+#include "scic_task_request.h"
+#include "scic_remote_device.h"
+#include "scic_io_request.h"
+#include "scic_sds_remote_device.h"
+#include "scic_sds_remote_node_context.h"
+#include "isci.h"
+#include "request.h"
+#include "sata.h"
+#include "task.h"
+
+
+/**
+ * isci_task_execute_task() - This function is one of the SAS Domain Template
+ *    functions. This function is called by libsas to send a task down to
+ *    hardware.
+ * @task: This parameter specifies the SAS task to send.
+ * @num: This parameter specifies the number of tasks to queue.
+ * @gfp_flags: This parameter specifies the context of this call.
+ *
+ * status, zero indicates success.
+ */
+int isci_task_execute_task(struct sas_task *task, int num, gfp_t gfp_flags)
+{
+	struct isci_host *isci_host;
+	struct isci_request *request = NULL;
+	struct isci_remote_device *device;
+	unsigned long flags;
+	unsigned long quiesce_flags = 0;
+	int ret;
+	enum sci_status status;
+
+
+	dev_dbg(task->dev->port->ha->dev, "%s: num=%d\n", __func__, num);
+
+	if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
+
+		isci_task_complete_for_upper_layer(
+			task,
+			SAS_TASK_UNDELIVERED,
+			SAM_STAT_TASK_ABORTED,
+			isci_perform_normal_io_completion
+			);
+
+		return 0;  /* The I/O was accepted (and failed). */
+	}
+	if ((task->dev == NULL) || (task->dev->port == NULL)) {
+
+		/* Indicate SAS_TASK_UNDELIVERED, so that the scsi midlayer
+		 * removes the target.
+		 */
+		isci_task_complete_for_upper_layer(
+			task,
+			SAS_TASK_UNDELIVERED,
+			SAS_DEVICE_UNKNOWN,
+			isci_perform_normal_io_completion
+			);
+		return 0;  /* The I/O was accepted (and failed). */
+	}
+	isci_host = isci_host_from_sas_ha(task->dev->port->ha);
+
+	/* Check if we have room for more tasks */
+	ret = isci_host_can_queue(isci_host, num);
+
+	if (ret) {
+		dev_warn(task->dev->port->ha->dev, "%s: queue full\n", __func__);
+		return ret;
+	}
+
+	do {
+		dev_dbg(task->dev->port->ha->dev,
+			"task = %p, num = %d; dev = %p; cmd = %p\n",
+			    task, num, task->dev, task->uldd_task);
+
+		if ((task->dev == NULL) || (task->dev->port == NULL)) {
+			dev_warn(task->dev->port->ha->dev,
+				 "%s: task %p's port or dev == NULL!\n",
+				 __func__, task);
+
+			/* Indicate SAS_TASK_UNDELIVERED, so that the scsi
+			 * midlayer removes the target.
+			 */
+			isci_task_complete_for_upper_layer(
+				task,
+				SAS_TASK_UNDELIVERED,
+				SAS_DEVICE_UNKNOWN,
+				isci_perform_normal_io_completion
+				);
+			/* We don't have a valid host reference, so we
+			 * can't control the host queueing condition.
+			 */
+			continue;
+		}
+
+		device = isci_dev_from_domain_dev(task->dev);
+
+		isci_host = isci_host_from_sas_ha(task->dev->port->ha);
+
+		/* check if the controller hasn't started or if the device
+		 * is ready but not accepting IO.
+		 */
+		if (device) {
+
+			spin_lock_irqsave(&device->host_quiesce_lock,
+					  quiesce_flags);
+		}
+		/* From this point onward, any process that needs to guarantee
+		 * that there is no kernel I/O being started will have to wait
+		 * for the quiesce spinlock.
+		 */
+
+		if (isci_host_get_state(isci_host) == isci_starting ||
+		    (device && ((isci_remote_device_get_state(device) == isci_ready) ||
+		    (isci_remote_device_get_state(device) == isci_host_quiesce)))) {
+
+			/* Forces a retry from scsi mid layer. */
+			dev_warn(task->dev->port->ha->dev,
+				 "%s: task %p: isci_host->status = %d, "
+				 "device = %p\n",
+				 __func__,
+				 task,
+				 isci_host_get_state(isci_host),
+				 device);
+
+			if (device)
+				dev_dbg(task->dev->port->ha->dev,
+					"%s: device->status = 0x%x\n",
+					__func__,
+					isci_remote_device_get_state(device));
+
+			/* Indicate QUEUE_FULL so that the scsi midlayer
+			 * retries.
+			 */
+			isci_task_complete_for_upper_layer(
+				task,
+				SAS_TASK_COMPLETE,
+				SAS_QUEUE_FULL,
+				isci_perform_normal_io_completion
+				);
+			isci_host_can_dequeue(isci_host, 1);
+		}
+		/* the device is going down... */
+		else if (!device || (isci_ready_for_io != isci_remote_device_get_state(device))) {
+
+			dev_dbg(task->dev->port->ha->dev,
+				"%s: task %p: isci_host->status = %d, "
+				"device = %p\n",
+				__func__,
+				task,
+				isci_host_get_state(isci_host),
+				device);
+
+			if (device)
+				dev_dbg(task->dev->port->ha->dev,
+					"%s: device->status = 0x%x\n",
+					__func__,
+					isci_remote_device_get_state(device));
+
+			/* Indicate SAS_TASK_UNDELIVERED, so that the scsi
+			 * midlayer removes the target.
+			 */
+			isci_task_complete_for_upper_layer(
+				task,
+				SAS_TASK_UNDELIVERED,
+				SAS_DEVICE_UNKNOWN,
+				isci_perform_normal_io_completion
+				);
+			isci_host_can_dequeue(isci_host, 1);
+
+		} else {
+			/* build and send the request. */
+			status = isci_request_execute(isci_host, task, &request,
+						      gfp_flags);
+
+			if (status == SCI_SUCCESS) {
+				spin_lock_irqsave(&task->task_state_lock, flags);
+				task->task_state_flags |= SAS_TASK_AT_INITIATOR;
+				spin_unlock_irqrestore(&task->task_state_lock, flags);
+			} else {
+				/* Indicate QUEUE_FULL so that the scsi
+				 * midlayer retries. if the request
+				 * failed for remote device reasons,
+				 * it gets returned as
+				 * SAS_TASK_UNDELIVERED next time
+				 * through.
+				 */
+				isci_task_complete_for_upper_layer(
+						task,
+						SAS_TASK_COMPLETE,
+						SAS_QUEUE_FULL,
+						isci_perform_normal_io_completion
+						);
+				isci_host_can_dequeue(isci_host, 1);
+			}
+		}
+		if (device) {
+			spin_unlock_irqrestore(&device->host_quiesce_lock,
+					       quiesce_flags
+					       );
+		}
+		task = list_entry(task->list.next, struct sas_task, list);
+	} while (--num > 0);
+	return 0;
+}
+
+
+
+/**
+ * isci_task_request_build() - This function builds the task 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.
+ * @tmf: This parameter is the task management struct to be built
+ *
+ * SCI_SUCCESS on successfull completion, or specific failure code.
+ */
+static enum sci_status isci_task_request_build(
+	struct isci_host *isci_host,
+	struct isci_request **isci_request,
+	struct isci_tmf *isci_tmf)
+{
+	struct scic_sds_remote_device *sci_device;
+	enum sci_status status = SCI_FAILURE;
+	struct isci_request *request;
+	struct isci_remote_device *isci_device;
+/*	struct sci_sas_identify_address_frame_protocols dev_protocols; */
+	struct smp_discover_response_protocols dev_protocols;
+
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_tmf = %p\n", __func__, isci_tmf);
+
+	isci_device = isci_tmf->device;
+	sci_device = isci_device->sci_device_handle;
+
+	/* do common allocation and init of request object. */
+	status = isci_request_alloc_tmf(
+		isci_host,
+		isci_tmf,
+		&request,
+		isci_device,
+		GFP_ATOMIC
+		);
+
+	if (status != SCI_SUCCESS)
+		goto out;
+
+	/* let the core do it's construct. */
+	status = scic_task_request_construct(
+		isci_host->core_controller,
+		sci_device,
+		SCI_CONTROLLER_INVALID_IO_TAG,
+		request,
+		request->sci_request_mem_ptr,
+		&request->sci_request_handle
+		);
+
+	if (status != SCI_SUCCESS) {
+		dev_warn(&isci_host->pdev->dev,
+			 "%s: scic_task_request_construct failed - "
+			 "status = 0x%x\n",
+			 __func__,
+			 status);
+		goto errout;
+	}
+
+	sci_object_set_association(
+		request->sci_request_handle,
+		request
+		);
+
+	scic_remote_device_get_protocols(
+		sci_device,
+		&dev_protocols
+		);
+
+	/* let the core do it's protocol
+	 * specific construction.
+	 */
+	if (dev_protocols.u.bits.attached_ssp_target) {
+
+		isci_tmf->proto = SAS_PROTOCOL_SSP;
+		status = scic_task_request_construct_ssp(
+			request->sci_request_handle
+			);
+		if (status != SCI_SUCCESS)
+			goto errout;
+	}
+
+	if (dev_protocols.u.bits.attached_stp_target) {
+
+		isci_tmf->proto = SAS_PROTOCOL_SATA;
+		status = isci_sata_management_task_request_build(request);
+
+		if (status != SCI_SUCCESS)
+			goto errout;
+	}
+
+	goto out;
+
+ errout:
+
+	/* release the dma memory if we fail. */
+	isci_request_free(isci_host, request);
+	request = NULL;
+
+ out:
+	*isci_request = request;
+	return status;
+}
+
+/**
+ * isci_tmf_timeout_cb() - This function is called as a kernel callback when
+ *    the timeout period for the TMF has expired.
+ *
+ *
+ */
+static void isci_tmf_timeout_cb(void *tmf_request_arg)
+{
+	struct isci_request *request = (struct isci_request *)tmf_request_arg;
+	struct isci_tmf *tmf = isci_request_access_tmf(request);
+	enum sci_status status;
+
+	BUG_ON(request->ttype != tmf_task);
+
+	/* This task management request has timed-out.  Terminate the request
+	 * so that the request eventually completes to the requestor in the
+	 * request completion callback path.
+	 */
+	/* Note - the timer callback function itself has provided spinlock
+	 * exclusion from the start and completion paths.  No need to take
+	 * the request->isci_host->scic_lock here.
+	 */
+
+	if (tmf->timeout_timer != NULL) {
+		/* Call the users callback, if any. */
+		if (tmf->cb_state_func != NULL)
+			tmf->cb_state_func(isci_tmf_timed_out, tmf,
+					   tmf->cb_data);
+
+		/* Terminate the TMF transmit request. */
+		status = scic_controller_terminate_request(
+			request->isci_host->core_controller,
+			request->isci_device->sci_device_handle,
+			request->sci_request_handle
+			);
+
+		dev_dbg(&request->isci_host->pdev->dev,
+			"%s: tmf_request = %p; tmf = %p; status = %d\n",
+			__func__, request, tmf, status);
+	} else
+		dev_dbg(&request->isci_host->pdev->dev,
+			"%s: timer already canceled! "
+			"tmf_request = %p; tmf = %p\n",
+			__func__, request, tmf);
+
+	/* No need to unlock since the caller to this callback is doing it for
+	 * us.
+	 * request->isci_host->scic_lock
+	 */
+}
+
+/**
+ * isci_task_execute_tmf() - This function builds and sends a task request,
+ *    then waits for the completion.
+ * @isci_host: This parameter specifies the ISCI host object
+ * @tmf: This parameter is the pointer to the task management structure for
+ *    this request.
+ * @timeout_ms: This parameter specifies the timeout period for the task
+ *    management request.
+ *
+ * TMF_RESP_FUNC_COMPLETE on successful completion of the TMF (this includes
+ * error conditions reported in the IU status), or TMF_RESP_FUNC_FAILED.
+ */
+int isci_task_execute_tmf(
+	struct isci_host *isci_host,
+	struct isci_tmf *tmf,
+	unsigned long timeout_ms)
+{
+	DECLARE_COMPLETION_ONSTACK(completion);
+	enum sci_status status = SCI_FAILURE;
+	struct scic_sds_remote_device *sci_device;
+	struct isci_remote_device *isci_device = tmf->device;
+	struct isci_request *request;
+	int ret = TMF_RESP_FUNC_FAILED;
+	unsigned long flags;
+
+	/* sanity check, return TMF_RESP_FUNC_FAILED
+	 * if the device is not there and ready.
+	 */
+	if (!isci_device ||
+	    ((isci_ready_for_io != isci_remote_device_get_state(isci_device)) &&
+	    (isci_host_quiesce != isci_remote_device_get_state(isci_device)))) {
+		dev_dbg(&isci_host->pdev->dev,
+			"%s: isci_device = %p not ready (%d)\n",
+			__func__,
+			isci_device,
+			isci_remote_device_get_state(isci_device));
+		return TMF_RESP_FUNC_FAILED;
+	} else
+		dev_dbg(&isci_host->pdev->dev,
+			"%s: isci_device = %p\n",
+			__func__, isci_device);
+
+	sci_device = isci_device->sci_device_handle;
+
+	/* Assign the pointer to the TMF's completion kernel wait structure. */
+	tmf->complete = &completion;
+
+	isci_task_request_build(
+		isci_host,
+		&request,
+		tmf
+		);
+
+	if (!request) {
+		dev_warn(&isci_host->pdev->dev,
+			"%s: isci_task_request_build failed\n",
+			__func__);
+		return TMF_RESP_FUNC_FAILED;
+	}
+
+	/* Allocate the TMF timeout timer. */
+	tmf->timeout_timer = isci_timer_create(
+		&isci_host->timer_list_struct,
+		isci_host,
+		request,
+		isci_tmf_timeout_cb
+		);
+
+	spin_lock_irqsave(&isci_host->scic_lock, flags);
+
+	/* Start the timer. */
+	if (tmf->timeout_timer)
+		isci_timer_start(tmf->timeout_timer, timeout_ms);
+	else
+		dev_warn(&isci_host->pdev->dev,
+			 "%s: isci_timer_create failed!!!!\n",
+			 __func__);
+
+	/* start the TMF io. */
+	status = scic_controller_start_task(
+		isci_host->core_controller,
+		sci_device,
+		request->sci_request_handle,
+		SCI_CONTROLLER_INVALID_IO_TAG
+		);
+
+	if (status != SCI_SUCCESS) {
+		dev_warn(&isci_host->pdev->dev,
+			 "%s: start_io failed - status = 0x%x, request = %p\n",
+			 __func__,
+			 status,
+			 request);
+		goto cleanup_request;
+	}
+
+	/* Call the users callback, if any. */
+	if (tmf->cb_state_func != NULL)
+		tmf->cb_state_func(isci_tmf_started, tmf, tmf->cb_data);
+
+	/* Change the state of the TMF-bearing request to "started". */
+	isci_request_change_state(request, started);
+
+	/* add the request to the remote device request list. */
+	list_add(&request->dev_node, &isci_device->reqs_in_process);
+
+	spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+
+	/* Wait for the TMF to complete, or a timeout. */
+	wait_for_completion(&completion);
+
+	isci_print_tmf(tmf);
+
+	if (tmf->status == SCI_SUCCESS)
+		ret =  TMF_RESP_FUNC_COMPLETE;
+	else if (tmf->status == SCI_FAILURE_IO_RESPONSE_VALID) {
+		dev_dbg(&isci_host->pdev->dev,
+			"%s: tmf.status == "
+			"SCI_FAILURE_IO_RESPONSE_VALID\n",
+			__func__);
+		ret =  TMF_RESP_FUNC_COMPLETE;
+	}
+	/* Else - leave the default "failed" status alone. */
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: completed request = %p\n",
+		__func__,
+		request);
+
+	if (request->io_request_completion != NULL) {
+
+		/* The fact that this is non-NULL for a TMF request
+		 * means there is a thread waiting for this TMF to
+		 * finish.
+		 */
+		complete(request->io_request_completion);
+	}
+
+	spin_lock_irqsave(&isci_host->scic_lock, flags);
+
+ cleanup_request:
+
+	/* Clean up the timer if needed. */
+	if (tmf->timeout_timer) {
+		isci_timer_stop(tmf->timeout_timer);
+		isci_timer_free(&isci_host->timer_list_struct,
+				tmf->timeout_timer);
+		tmf->timeout_timer = NULL;
+	}
+
+	spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+
+	isci_request_free(isci_host, request);
+
+	return ret;
+}
+
+void isci_task_build_tmf(
+	struct isci_tmf *tmf,
+	struct isci_remote_device *isci_device,
+	enum isci_tmf_function_codes code,
+	void (*tmf_sent_cb)(enum isci_tmf_cb_state,
+			    struct isci_tmf *,
+			    void *),
+	void *cb_data)
+{
+	dev_dbg(&isci_device->isci_port->isci_host->pdev->dev,
+		"%s: isci_device = %p\n", __func__, isci_device);
+
+	memset(tmf, 0, sizeof(*tmf));
+
+	tmf->device        = isci_device;
+	tmf->tmf_code      = code;
+	tmf->timeout_timer = NULL;
+	tmf->cb_state_func = tmf_sent_cb;
+	tmf->cb_data       = cb_data;
+}
+
+static struct isci_request *isci_task_get_request_from_task(
+	struct sas_task *task,
+	struct isci_host **isci_host,
+	struct isci_remote_device **isci_device)
+{
+
+	struct isci_request *request = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&task->task_state_lock, flags);
+
+	request = task->lldd_task;
+
+	/* If task is already done, the request isn't valid */
+	if (!(task->task_state_flags & SAS_TASK_STATE_DONE) &&
+	    (task->task_state_flags & SAS_TASK_AT_INITIATOR) &&
+	    (request != NULL)) {
+
+		if (isci_host != NULL)
+			*isci_host = request->isci_host;
+
+		if (isci_device != NULL)
+			*isci_device = request->isci_device;
+	}
+
+	spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+	return request;
+}
+
+/**
+ * isci_task_validate_request_to_abort() - This function checks the given I/O
+ *    against the "started" state.  If the request is still "started", it's
+ *    state is changed to aborted. NOTE: isci_host->scic_lock MUST BE HELD
+ *    BEFORE CALLING THIS FUNCTION.
+ * @isci_request: This parameter specifies the request object to control.
+ * @isci_host: This parameter specifies the ISCI host object
+ * @isci_device: This is the device to which the request is pending.
+ * @aborted_io_completion: This is a completion structure that will be added to
+ *    the request in case it is changed to aborting; this completion is
+ *    triggered when the request is fully completed.
+ *
+ * Either "started" on successful change of the task status to "aborted", or
+ * "unallocated" if the task cannot be controlled.
+ */
+static enum isci_request_status isci_task_validate_request_to_abort(
+	struct isci_request *isci_request,
+	struct isci_host *isci_host,
+	struct isci_remote_device *isci_device,
+	struct completion *aborted_io_completion)
+{
+	enum isci_request_status old_state = unallocated;
+
+	/* Only abort the task if it's in the
+	 *  device's request_in_process list
+	 */
+	if (isci_request && !list_empty(&isci_request->dev_node)) {
+		old_state = isci_request_change_started_to_aborted(
+			isci_request, aborted_io_completion);
+
+		/* Only abort requests in the started state. */
+		if (old_state != started)
+			old_state = unallocated;
+	}
+
+	return old_state;
+}
+
+static void isci_request_cleanup_completed_loiterer(
+	struct isci_host *isci_host,
+	struct isci_remote_device *isci_device,
+	struct isci_request *isci_request)
+{
+	struct sas_task *task = isci_request_access_task(isci_request);
+	unsigned long flags;
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_device=%p, request=%p, task=%p\n",
+		__func__, isci_device, isci_request,
+		isci_request->ttype_ptr.io_task_ptr);
+
+	spin_lock_irqsave(&isci_host->scic_lock, flags);
+	list_del_init(&isci_request->dev_node);
+	if (task != NULL)
+		task->lldd_task = NULL;
+	spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+
+	isci_request_free(isci_host, isci_request);
+}
+/**
+ * isci_terminate_request_core() - This function will terminate the given
+ *    request, and wait for it to complete.  This function must only be called
+ *    from a thread that can wait.  Note that the request is terminated and
+ *    completed (back to the host, if started there).
+ * @isci_host: This SCU.
+ * @isci_device: The target.
+ * @isci_request: The I/O request to be terminated.
+ *
+ *
+ */
+static void isci_terminate_request_core(
+	struct isci_host *isci_host,
+	struct isci_remote_device *isci_device,
+	struct isci_request *isci_request,
+	struct completion *request_completion)
+{
+	enum sci_status status                 = SCI_SUCCESS;
+	bool was_terminated         = false;
+	bool needs_cleanup_handling = false;
+	enum isci_request_status request_status;
+	unsigned long flags;
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: device = %p; request = %p\n",
+		__func__, isci_device, isci_request);
+
+	/* Peek at the current status of the request.  This will tell
+	 * us if there was special handling on the request such that it
+	 * needs to be detached and freed here.
+	 */
+	spin_lock_irqsave(&isci_request->state_lock, flags);
+	request_status = isci_request_get_state(isci_request);
+
+	/* TMFs are in their own thread */
+	if ((isci_request->ttype == io_task) &&
+	    ((request_status == aborted) ||
+	     (request_status == aborting) ||
+	     (request_status == terminating)))
+		/* The completion routine won't free a request in
+		 * the aborted/aborting/terminating state, so we do
+		 * it here.
+		 */
+		needs_cleanup_handling = true;
+
+	spin_unlock_irqrestore(&isci_request->state_lock, flags);
+
+	spin_lock_irqsave(&isci_host->scic_lock, flags);
+	/* Make sure the request wasn't just sitting around signalling
+	 * device condition (if the request handle is NULL, then the
+	 * request completed but needed additional handling here).
+	 */
+	if (isci_request->sci_request_handle != NULL) {
+		was_terminated = true;
+		status = scic_controller_terminate_request(
+			isci_host->core_controller,
+			isci_device->sci_device_handle,
+			isci_request->sci_request_handle
+			);
+	}
+	spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+
+	/*
+	 * The only time the request to terminate will
+	 * fail is when the io request is completed and
+	 * being aborted.
+	 */
+	if (status != SCI_SUCCESS)
+		dev_err(&isci_host->pdev->dev,
+			"%s: scic_controller_terminate_request"
+			" returned = 0x%x\n",
+			__func__,
+			status);
+	else {
+		if (was_terminated) {
+			dev_dbg(&isci_host->pdev->dev,
+				"%s: before completion wait (%p)\n",
+				__func__,
+				request_completion);
+
+			/* Wait here for the request to complete. */
+			wait_for_completion(request_completion);
+
+			dev_dbg(&isci_host->pdev->dev,
+				"%s: after completion wait (%p)\n",
+				__func__,
+				request_completion);
+		}
+
+		if (needs_cleanup_handling)
+			isci_request_cleanup_completed_loiterer(
+				isci_host, isci_device, isci_request
+				);
+	}
+}
+static void isci_terminate_request(
+	struct isci_host *isci_host,
+	struct isci_remote_device *isci_device,
+	struct isci_request *isci_request,
+	enum isci_request_status new_request_state)
+{
+	enum isci_request_status old_state;
+
+	DECLARE_COMPLETION_ONSTACK(request_completion);
+	unsigned long flags;
+
+	spin_lock_irqsave(&isci_host->scic_lock, flags);
+
+	/* Change state to "new_request_state" if it is currently "started" */
+	old_state = isci_request_change_started_to_newstate(
+		isci_request,
+		&request_completion,
+		new_request_state
+		);
+
+	spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+
+	if (old_state == started)
+		/* This request was not already being aborted. If it had been,
+		 * then the aborting I/O (ie. the TMF request) would not be in
+		 * the aborting state, and thus would be terminated here.  Note
+		 * that since the TMF completion's call to the kernel function
+		 * "complete()" does not happen until the pending I/O request
+		 * terminate fully completes, we do not have to implement a
+		 * special wait here for already aborting requests - the
+		 * termination of the TMF request will force the request
+		 * to finish it's already started terminate.
+		 */
+		isci_terminate_request_core(isci_host, isci_device,
+					    isci_request, &request_completion);
+}
+
+/**
+ * isci_terminate_pending_requests() - This function will change the all of the
+ *    requests on the given device's state to "aborting", will terminate the
+ *    requests, and wait for them to complete.  This function must only be
+ *    called from a thread that can wait.  Note that the requests are all
+ *    terminated and completed (back to the host, if started there).
+ * @isci_host: This parameter specifies SCU.
+ * @isci_device: This parameter specifies the target.
+ *
+ *
+ */
+void isci_terminate_pending_requests(
+	struct isci_host *isci_host,
+	struct isci_remote_device *isci_device,
+	enum isci_request_status new_request_state)
+{
+	struct isci_request *isci_request;
+	struct sas_task *task;
+	bool done = false;
+	unsigned long flags;
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_device = %p (new request state = %d)\n",
+		__func__, isci_device, new_request_state);
+
+	#define ISCI_TERMINATE_SHOW_PENDING_REQUESTS
+	#ifdef ISCI_TERMINATE_SHOW_PENDING_REQUESTS
+	{
+		struct isci_request *request;
+
+		/* Only abort the task if it's in the
+		 * device's request_in_process list
+		 */
+		list_for_each_entry(request,
+				    &isci_device->reqs_in_process,
+				    dev_node)
+			dev_dbg(&isci_host->pdev->dev,
+				"%s: isci_device = %p; request is on "
+				"reqs_in_process list: %p\n",
+				__func__, isci_device, request);
+	}
+	#endif /* ISCI_TERMINATE_SHOW_PENDING_REQUESTS */
+
+	/* Clean up all pending requests. */
+	do {
+		spin_lock_irqsave(&isci_host->scic_lock, flags);
+
+		if (list_empty(&isci_device->reqs_in_process)) {
+
+			done = true;
+			spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+
+			dev_dbg(&isci_host->pdev->dev,
+				"%s: isci_device = %p; done.\n",
+				__func__, isci_device);
+		} else {
+			/* The list was not empty - grab the first request. */
+			isci_request = list_first_entry(
+				&isci_device->reqs_in_process,
+				struct isci_request, dev_node
+				);
+			/* Note that we are not expecting to have to control
+			 * the target to abort the request.
+			 */
+			isci_request->complete_in_target = true;
+
+			spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+
+			/* Get the libsas task reference. */
+			task = isci_request_access_task(isci_request);
+
+			dev_dbg(&isci_host->pdev->dev,
+				"%s: isci_device=%p request=%p; task=%p\n",
+				__func__, isci_device, isci_request, task);
+
+			/* Mark all still pending I/O with the selected next
+			 * state.
+			 */
+			isci_terminate_request(isci_host, isci_device,
+					       isci_request, new_request_state
+					       );
+
+			/* Set the 'done' state on the task. */
+			if (task)
+				isci_task_all_done(task);
+		}
+	} while (!done);
+}
+
+/**
+ * isci_task_send_lu_reset_sas() - This function is called by of the SAS Domain
+ *    Template functions.
+ * @lun: This parameter specifies the lun to be reset.
+ *
+ * status, zero indicates success.
+ */
+static int isci_task_send_lu_reset_sas(
+	struct isci_host *isci_host,
+	struct isci_remote_device *isci_device,
+	u8 *lun)
+{
+	struct isci_tmf tmf;
+	int ret = TMF_RESP_FUNC_FAILED;
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_host = %p, isci_device = %p\n",
+		__func__, isci_host, isci_device);
+	/* Send the LUN reset to the target.  By the time the call returns,
+	 * the TMF has fully exected in the target (in which case the return
+	 * value is "TMF_RESP_FUNC_COMPLETE", or the request timed-out (or
+	 * was otherwise unable to be executed ("TMF_RESP_FUNC_FAILED").
+	 */
+	isci_task_build_tmf(&tmf, isci_device, isci_tmf_ssp_lun_reset, NULL,
+			    NULL);
+
+	#define ISCI_LU_RESET_TIMEOUT_MS 2000 /* 2 second timeout. */
+	ret = isci_task_execute_tmf(isci_host, &tmf, ISCI_LU_RESET_TIMEOUT_MS);
+
+	if (ret == TMF_RESP_FUNC_COMPLETE)
+		dev_dbg(&isci_host->pdev->dev,
+			"%s: %p: TMF_LU_RESET passed\n",
+			__func__, isci_device);
+	else
+		dev_dbg(&isci_host->pdev->dev,
+			"%s: %p: TMF_LU_RESET failed (%x)\n",
+			__func__, isci_device, ret);
+
+	return ret;
+}
+
+/**
+ * isci_task_lu_reset() - This function is one of the SAS Domain Template
+ *    functions. This is one of the Task Management functoins called by libsas,
+ *    to reset the given lun. Note the assumption that while this call is
+ *    executing, no I/O will be sent by the host to the device.
+ * @lun: This parameter specifies the lun to be reset.
+ *
+ * status, zero indicates success.
+ */
+int isci_task_lu_reset(
+	struct domain_device *domain_device,
+	u8 *lun)
+{
+	struct isci_host *isci_host = NULL;
+	struct isci_remote_device *isci_device = NULL;
+	int ret;
+	bool device_stopping = false;
+
+	if (domain_device == NULL) {
+		pr_warn("%s: domain_device == NULL\n", __func__);
+		return TMF_RESP_FUNC_FAILED;
+	}
+
+	isci_device = isci_dev_from_domain_dev(domain_device);
+
+	if (domain_device->port != NULL)
+		isci_host = isci_host_from_sas_ha(domain_device->port->ha);
+
+	pr_debug("%s: domain_device=%p, isci_host=%p; isci_device=%p\n",
+		 __func__, domain_device, isci_host, isci_device);
+
+	if (isci_device != NULL)
+		device_stopping = (isci_device->status == isci_stopping)
+				  || (isci_device->status == isci_stopped);
+
+	/* If there is a device reset pending on any request in the
+	 * device's list, fail this LUN reset request in order to
+	 * escalate to the device reset.
+	 */
+	if ((isci_device == NULL) ||
+	    (isci_host == NULL) ||
+	    ((isci_host != NULL) &&
+	     (isci_device != NULL) &&
+	     (device_stopping ||
+	      (isci_device_is_reset_pending(isci_host, isci_device))))) {
+		dev_warn(&isci_host->pdev->dev,
+			 "%s: No dev (%p), no host (%p), or "
+			 "RESET PENDING: domain_device=%p\n",
+			 __func__, isci_device, isci_host, domain_device);
+		return TMF_RESP_FUNC_FAILED;
+	}
+
+	/* Stop I/O to the remote device. */
+	isci_device_set_host_quiesce_lock_state(isci_device, true);
+
+	/* Send the task management part of the reset. */
+	if (sas_protocol_ata(domain_device->tproto)) {
+		ret = isci_task_send_lu_reset_sata(
+			isci_host, isci_device, lun
+			);
+	} else
+		ret = isci_task_send_lu_reset_sas(isci_host, isci_device, lun);
+
+	/* If the LUN reset worked, all the I/O can now be terminated. */
+	if (ret == TMF_RESP_FUNC_COMPLETE)
+		/* Terminate all I/O now. */
+		isci_terminate_pending_requests(isci_host,
+						isci_device,
+						terminating);
+
+	/* Resume I/O to the remote device. */
+	isci_device_set_host_quiesce_lock_state(isci_device, false);
+
+	return ret;
+}
+
+
+/*	 int (*lldd_clear_nexus_port)(struct asd_sas_port *); */
+int isci_task_clear_nexus_port(struct asd_sas_port *port)
+{
+	return TMF_RESP_FUNC_FAILED;
+}
+
+
+
+int isci_task_clear_nexus_ha(struct sas_ha_struct *ha)
+{
+	return TMF_RESP_FUNC_FAILED;
+}
+
+int isci_task_I_T_nexus_reset(struct domain_device *dev)
+{
+	return TMF_RESP_FUNC_FAILED;
+}
+
+
+/* Task Management Functions. Must be called from process context.	 */
+
+/**
+ * isci_abort_task_process_cb() - This is a helper function for the abort task
+ *    TMF command.  It manages the request state with respect to the successful
+ *    transmission / completion of the abort task request.
+ * @cb_state: This parameter specifies when this function was called - after
+ *    the TMF request has been started and after it has timed-out.
+ * @tmf: This parameter specifies the TMF in progress.
+ *
+ *
+ */
+static void isci_abort_task_process_cb(
+	enum isci_tmf_cb_state cb_state,
+	struct isci_tmf *tmf,
+	void *cb_data)
+{
+	struct isci_request *old_request;
+
+	old_request = (struct isci_request *)cb_data;
+
+	dev_dbg(&old_request->isci_host->pdev->dev,
+		"%s: tmf=%p, old_request=%p\n",
+		__func__, tmf, old_request);
+
+	switch (cb_state) {
+
+	case isci_tmf_started:
+		/* The TMF has been started.  Nothing to do here, since the
+		 * request state was already set to "aborted" by the abort
+		 * task function.
+		 */
+		BUG_ON(old_request->status != aborted);
+		break;
+
+	case isci_tmf_timed_out:
+
+		/* Set the task's state to "aborting", since the abort task
+		 * function thread set it to "aborted" (above) in anticipation
+		 * of the task management request working correctly.  Since the
+		 * timeout has now fired, the TMF request failed.  We set the
+		 * state such that the request completion will indicate the
+		 * device is no longer present.
+		 */
+		isci_request_change_state(old_request, aborting);
+		break;
+
+	default:
+		dev_err(&old_request->isci_host->pdev->dev,
+			"%s: Bad cb_state (%d): tmf=%p, old_request=%p\n",
+			__func__, cb_state, tmf, old_request);
+		break;
+	}
+}
+
+/**
+ * isci_task_abort_task() - This function is one of the SAS Domain Template
+ *    functions. This function is called by libsas to abort a specified task.
+ * @task: This parameter specifies the SAS task to abort.
+ *
+ * status, zero indicates success.
+ */
+int isci_task_abort_task(struct sas_task *task)
+{
+	DECLARE_COMPLETION_ONSTACK(aborted_io_completion);
+	struct isci_request *old_request = NULL;
+	struct isci_remote_device *isci_device = NULL;
+	struct isci_host *isci_host = NULL;
+	struct isci_tmf tmf;
+	int ret = TMF_RESP_FUNC_FAILED;
+	unsigned long flags;
+	bool any_dev_reset, device_stopping;
+
+	/* Get the isci_request reference from the task.  Note that
+	 * this check does not depend on the pending request list
+	 * in the device, because tasks driving resets may land here
+	 * after completion in the core.
+	 */
+	old_request = isci_task_get_request_from_task(task, &isci_host,
+						      &isci_device);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: task = %p\n", __func__, task);
+
+	/* Check if the device has been / is currently being removed.
+	 * If so, no task management will be done, and the I/O will
+	 * be terminated.
+	 */
+	device_stopping = (isci_device->status == isci_stopping)
+			  || (isci_device->status == isci_stopped);
+
+#ifdef NOMORE
+	/* This abort task function is the first stop of the libsas error
+	 * handler thread. Since libsas is executing in a thread with a
+	 * referernce to the "task" parameter, that task cannot be completed
+	 * directly back to the upper layers.  In order to make sure that
+	 * the task is managed correctly if this abort task fails, set the
+	 * "SAS_TASK_STATE_ABORTED" bit now such that completions up the
+	 * stack will be intercepted and only allowed to happen in the
+	 * libsas SCSI error handler thread.
+	 */
+	spin_lock_irqsave(&task->task_state_lock, flags);
+	task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+	spin_unlock_irqrestore(&task->task_state_lock, flags);
+#endif  /* NOMORE */
+
+	/* This version of the driver will fail abort requests for
+	 * SATA/STP.  Failing the abort request this way will cause the
+	 * SCSI error handler thread to escalate to LUN reset
+	 */
+	if (sas_protocol_ata(task->task_proto) && !device_stopping) {
+		dev_warn(&isci_host->pdev->dev,
+			    " task %p is for a STP/SATA device;"
+			    " returning TMF_RESP_FUNC_FAILED\n"
+			    " to cause a LUN reset...\n", task);
+		return TMF_RESP_FUNC_FAILED;
+	}
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: old_request == %p\n", __func__, old_request);
+
+	spin_lock_irqsave(&task->task_state_lock, flags);
+
+	/* Don't do resets to stopping devices. */
+	if (device_stopping)
+		task->task_state_flags &= ~SAS_TASK_NEED_DEV_RESET;
+
+	/* See if there is a pending device reset for this device. */
+	any_dev_reset = task->task_state_flags & SAS_TASK_NEED_DEV_RESET;
+
+	spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+	if ((isci_device != NULL) && !device_stopping)
+		any_dev_reset = any_dev_reset
+				|| isci_device_is_reset_pending(isci_host,
+								isci_device
+								);
+
+	/* If the extraction of the request reference from the task
+	 * failed, then the request has been completed (or if there is a
+	 * pending reset then this abort request function must be failed
+	 * in order to escalate to the target reset).
+	 */
+	if ((old_request == NULL) ||
+	    ((old_request != NULL) &&
+	     (old_request->sci_request_handle == NULL) &&
+	     (old_request->complete_in_target)) ||
+	     any_dev_reset) {
+
+		spin_lock_irqsave(&task->task_state_lock, flags);
+
+		/* If the device reset task flag is set, fail the task
+		 * management request.  Otherwise, the original request
+		 * has completed.
+		 */
+		if (any_dev_reset) {
+
+			/* Turn off the task's DONE to make sure this
+			 * task is escalated to a target reset.
+			 */
+			task->task_state_flags &= ~SAS_TASK_STATE_DONE;
+
+			/* Fail the task management request in order to
+			 * escalate to the target reset.
+			 */
+			ret = TMF_RESP_FUNC_FAILED;
+
+			dev_dbg(&isci_host->pdev->dev,
+				"%s: Failing task abort in order to "
+				"escalate to target reset because\n"
+				"SAS_TASK_NEED_DEV_RESET is set for "
+				"task %p on dev %p\n",
+				__func__, task, isci_device);
+
+		} else {
+			ret = TMF_RESP_FUNC_COMPLETE;
+
+			dev_dbg(&isci_host->pdev->dev,
+				"%s: abort task not needed for %p\n",
+				__func__, task);
+
+			/* The request has already completed and there
+			 * is nothing to do here other than to set the task
+			 * done bit, and indicate that the task abort function
+			 * was sucessful.
+			 */
+			isci_set_task_doneflags(task);
+
+			/* Set the abort bit to make sure that libsas sticks the
+			 * task in the completed task queue.
+			 */
+/*			task->task_state_flags |= SAS_TASK_STATE_ABORTED; */
+
+			/* Check for the situation where the request was
+			 * left around on the device list but the
+			 * request already completed.
+			 */
+			if (old_request && !old_request->sci_request_handle) {
+
+				isci_request_cleanup_completed_loiterer(
+					isci_host, isci_device, old_request
+					);
+			}
+		}
+		spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+		return ret;
+	}
+
+	spin_lock_irqsave(&isci_host->scic_lock, flags);
+
+	/* Sanity check the request status, and set the I/O kernel completion
+	 * struct that will be triggered when the request completes.
+	 */
+	if (isci_task_validate_request_to_abort(
+		    old_request,
+		    isci_host,
+		    isci_device,
+		    &aborted_io_completion)
+	    == unallocated) {
+		dev_dbg(&isci_host->pdev->dev,
+			"%s: old_request not valid for device = %p\n",
+			__func__,
+			isci_device);
+		old_request = NULL;
+	}
+
+	if (!old_request) {
+
+		/* There is no isci_request attached to the sas_task.
+		 * It must have been completed and detached.
+		 */
+		dev_dbg(&isci_host->pdev->dev,
+			"%s: old_request == NULL\n",
+			__func__);
+
+		spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+
+		/* Set the state on the task. */
+		isci_task_all_done(task);
+
+		return TMF_RESP_FUNC_COMPLETE;
+	}
+	if (task->task_proto == SAS_PROTOCOL_SMP || device_stopping) {
+
+		if (device_stopping)
+			dev_dbg(&isci_host->pdev->dev,
+				"%s: device is stopping, thus no TMF\n",
+				__func__);
+		else
+			dev_dbg(&isci_host->pdev->dev,
+				"%s: request is SMP, thus no TMF\n",
+				__func__);
+
+		old_request->complete_in_target = true;
+
+		spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+
+		/* Set the state on the task. */
+		isci_task_all_done(task);
+
+		ret = TMF_RESP_FUNC_COMPLETE;
+
+		/* Stopping and SMP devices are not sent a TMF, and are not
+		 * reset, but the outstanding I/O request is terminated here.
+		 *
+		 * Clean up the request on our side, and wait for the aborted
+		 * I/O to complete.
+		 */
+		isci_terminate_request_core(isci_host, isci_device, old_request,
+					    &aborted_io_completion);
+	} else {
+		/* Fill in the tmf stucture */
+		isci_task_build_tmf(&tmf, isci_device, isci_tmf_ssp_task_abort,
+				    isci_abort_task_process_cb, old_request);
+
+		tmf.io_tag = scic_io_request_get_io_tag(
+			old_request->sci_request_handle
+			);
+
+		spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+
+		#define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* half second timeout. */
+		ret = isci_task_execute_tmf(isci_host, &tmf,
+					    ISCI_ABORT_TASK_TIMEOUT_MS);
+
+		if (ret == TMF_RESP_FUNC_COMPLETE) {
+			old_request->complete_in_target = true;
+
+			/* Clean up the request on our side, and wait for the aborted I/O to
+			 * complete.
+			 */
+			isci_terminate_request_core(isci_host, isci_device, old_request,
+						    &aborted_io_completion);
+
+			/* Set the state on the task. */
+			isci_task_all_done(task);
+		} else
+			dev_err(&isci_host->pdev->dev,
+				"%s: isci_task_send_tmf failed\n",
+				__func__);
+	}
+
+	return ret;
+}
+
+/**
+ * isci_task_abort_task_set() - This function is one of the SAS Domain Template
+ *    functions. This is one of the Task Management functoins called by libsas,
+ *    to abort all task for the given lun.
+ * @d_device: This parameter specifies the domain device associated with this
+ *    request.
+ * @lun: This parameter specifies the lun associated with this request.
+ *
+ * status, zero indicates success.
+ */
+int isci_task_abort_task_set(
+	struct domain_device *d_device,
+	u8 *lun)
+{
+	return TMF_RESP_FUNC_FAILED;
+}
+
+
+/**
+ * isci_task_clear_aca() - This function is one of the SAS Domain Template
+ *    functions. This is one of the Task Management functoins called by libsas.
+ * @d_device: This parameter specifies the domain device associated with this
+ *    request.
+ * @lun: This parameter specifies the lun	 associated with this request.
+ *
+ * status, zero indicates success.
+ */
+int isci_task_clear_aca(
+	struct domain_device *d_device,
+	u8 *lun)
+{
+	return TMF_RESP_FUNC_FAILED;
+}
+
+
+
+/**
+ * isci_task_clear_task_set() - This function is one of the SAS Domain Template
+ *    functions. This is one of the Task Management functoins called by libsas.
+ * @d_device: This parameter specifies the domain device associated with this
+ *    request.
+ * @lun: This parameter specifies the lun	 associated with this request.
+ *
+ * status, zero indicates success.
+ */
+int isci_task_clear_task_set(
+	struct domain_device *d_device,
+	u8 *lun)
+{
+	return TMF_RESP_FUNC_FAILED;
+}
+
+
+/**
+ * isci_task_query_task() - This function is implemented to cause libsas to
+ *    correctly escalate the failed abort to a LUN or target reset (this is
+ *    because sas_scsi_find_task libsas function does not correctly interpret
+ *    all return codes from the abort task call).  When TMF_RESP_FUNC_SUCC is
+ *    returned, libsas turns this into a LUN reset; when FUNC_FAILED is
+ *    returned, libsas will turn this into a target reset
+ * @task: This parameter specifies the sas task being queried.
+ * @lun: This parameter specifies the lun associated with this request.
+ *
+ * status, zero indicates success.
+ */
+int isci_task_query_task(
+	struct sas_task *task)
+{
+	/* See if there is a pending device reset for this device. */
+	if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET)
+		return TMF_RESP_FUNC_FAILED;
+	else
+		return TMF_RESP_FUNC_SUCC;
+}
+
+/**
+ * isci_task_request_complete() - This function is called by the sci core when
+ *    an task 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_task_request_complete(
+	struct isci_host *isci_host,
+	struct isci_request *request,
+	enum sci_task_status completion_status)
+{
+	struct isci_remote_device *isci_device = request->isci_device;
+	enum isci_request_status old_state;
+	struct isci_tmf *tmf = isci_request_access_tmf(request);
+	struct completion *tmf_complete;
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: request = %p, status=%d\n",
+		__func__, request, completion_status);
+
+	old_state = isci_request_change_state(request, completed);
+
+	tmf->status = completion_status;
+	request->complete_in_target = true;
+
+	if (SAS_PROTOCOL_SSP == tmf->proto) {
+
+		memcpy(&tmf->resp.resp_iu,
+		       scic_io_request_get_response_iu_address(
+			       request->sci_request_handle
+			       ),
+		       sizeof(struct sci_ssp_response_iu));
+
+	} else if (SAS_PROTOCOL_SATA == tmf->proto) {
+
+		memcpy(&tmf->resp.d2h_fis,
+		       scic_stp_io_request_get_d2h_reg_address(
+			       request->sci_request_handle
+			       ),
+		       sizeof(struct sata_fis_reg_d2h)
+		       );
+	}
+
+	/* Manage the timer if it is still running. */
+	if (tmf->timeout_timer) {
+
+		isci_timer_stop(tmf->timeout_timer);
+		isci_timer_free(&isci_host->timer_list_struct,
+				tmf->timeout_timer);
+		tmf->timeout_timer = NULL;
+	}
+
+	/* PRINT_TMF( ((struct isci_tmf *)request->task)); */
+	tmf_complete = tmf->complete;
+
+	scic_controller_complete_task(
+		isci_host->core_controller,
+		isci_device->sci_device_handle,
+		request->sci_request_handle
+		);
+	/* NULL the request handle to make sure it cannot be terminated
+	 *  or completed again.
+	 */
+	request->sci_request_handle = NULL;
+
+	isci_request_change_state(request, unallocated);
+	list_del_init(&request->dev_node);
+
+	/* The task management part completes last. */
+	complete(tmf_complete);
+}
+
+
+/**
+ * isci_task_ssp_request_get_lun() - This function is called by the sci core to
+ *    retrieve the lun for a given task request.
+ * @request: This parameter is the isci_request object.
+ *
+ * lun for specified task request.
+ */
+u32 isci_task_ssp_request_get_lun(struct isci_request *request)
+{
+	struct isci_tmf *isci_tmf = isci_request_access_tmf(request);
+
+	dev_dbg(&request->isci_host->pdev->dev,
+		"%s: lun = %d\n", __func__, isci_tmf->lun[0]);
+/* @todo: build lun from array of bytes to 32 bit */
+	return isci_tmf->lun[0];
+}
+
+/**
+ * isci_task_ssp_request_get_function() - This function is called by the sci
+ *    core to retrieve the function for a given task request.
+ * @request: This parameter is the isci_request object.
+ *
+ * function code for specified task request.
+ */
+u8 isci_task_ssp_request_get_function(struct isci_request *request)
+{
+	struct isci_tmf *isci_tmf = isci_request_access_tmf(request);
+
+	dev_dbg(&request->isci_host->pdev->dev,
+		"%s: func = %d\n", __func__, isci_tmf->tmf_code);
+
+	return isci_tmf->tmf_code;
+}
+
+/**
+ * isci_task_ssp_request_get_io_tag_to_manage() - This function is called by
+ *    the sci core to retrieve the io tag for a given task request.
+ * @request: This parameter is the isci_request object.
+ *
+ * io tag for specified task request.
+ */
+u16 isci_task_ssp_request_get_io_tag_to_manage(struct isci_request *request)
+{
+	u16 io_tag = SCI_CONTROLLER_INVALID_IO_TAG;
+
+	if (tmf_task == request->ttype) {
+		struct isci_tmf *tmf = isci_request_access_tmf(request);
+		io_tag = tmf->io_tag;
+	}
+
+	dev_dbg(&request->isci_host->pdev->dev,
+		"%s: request = %p, io_tag = %d\n",
+		__func__, request, io_tag);
+
+	return io_tag;
+}
+
+/**
+ * isci_task_ssp_request_get_response_data_address() - This function is called
+ *    by the sci core to retrieve the response data address for a given task
+ *    request.
+ * @request: This parameter is the isci_request object.
+ *
+ * response data address for specified task request.
+ */
+void *isci_task_ssp_request_get_response_data_address(
+	struct isci_request *request)
+{
+	struct isci_tmf *isci_tmf = isci_request_access_tmf(request);
+
+	return &isci_tmf->resp.resp_iu;
+}
+
+/**
+ * isci_task_ssp_request_get_response_data_length() - This function is called
+ *    by the sci core to retrieve the response data length for a given task
+ *    request.
+ * @request: This parameter is the isci_request object.
+ *
+ * response data length for specified task request.
+ */
+u32 isci_task_ssp_request_get_response_data_length(
+	struct isci_request *request)
+{
+	struct isci_tmf *isci_tmf = isci_request_access_tmf(request);
+
+	return sizeof(isci_tmf->resp.resp_iu);
+}
+
+/**
+ * isci_bus_reset_handler() - This function performs a target reset of the
+ *    device referenced by "cmd'.  This function is exported through the
+ *    "struct scsi_host_template" structure such that it is called when an I/O
+ *    recovery process has escalated to a target reset. Note that this function
+ *    is called from the scsi error handler event thread, so may block on calls.
+ * @scsi_cmd: This parameter specifies the target to be reset.
+ *
+ * SUCCESS if the reset process was successful, else FAILED.
+ */
+int isci_bus_reset_handler(struct scsi_cmnd *cmd)
+{
+	unsigned long flags = 0;
+	struct isci_host *isci_host = NULL;
+	enum sci_status status;
+	int base_status;
+	struct isci_remote_device *isci_dev
+		= isci_dev_from_domain_dev(
+		sdev_to_domain_dev(cmd->device));
+
+	dev_dbg(&cmd->device->sdev_gendev,
+		"%s: cmd %p, isci_dev %p\n",
+		__func__, cmd, isci_dev);
+
+	if (!isci_dev) {
+		dev_warn(&cmd->device->sdev_gendev,
+			 "%s: isci_dev is GONE!\n",
+			 __func__);
+
+		return TMF_RESP_FUNC_COMPLETE; /* Nothing to reset. */
+	}
+
+	if (isci_dev->isci_port != NULL)
+		isci_host = isci_dev->isci_port->isci_host;
+
+	if (isci_host != NULL)
+		spin_lock_irqsave(&isci_host->scic_lock, flags);
+
+	status = scic_remote_device_reset(isci_dev->sci_device_handle);
+	if (status != SCI_SUCCESS) {
+
+		if (isci_host != NULL)
+			spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+
+		scmd_printk(KERN_WARNING, cmd,
+			    "%s: scic_remote_device_reset(%p) returned %d!\n",
+			    __func__, isci_dev, status);
+
+		return TMF_RESP_FUNC_FAILED;
+	}
+	if (isci_host != NULL)
+		spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+
+	/* Stop I/O to the remote device. */
+	isci_device_set_host_quiesce_lock_state(isci_dev, true);
+
+	/* Make sure all pending requests are able to be fully terminated. */
+	isci_device_clear_reset_pending(isci_dev);
+
+	/* Terminate in-progress I/O now. */
+	isci_remote_device_nuke_requests(isci_dev);
+
+	/* Call into the libsas default handler (which calls sas_phy_reset). */
+	base_status = sas_eh_bus_reset_handler(cmd);
+
+	if (base_status != SUCCESS) {
+
+		/* There can be cases where the resets to individual devices
+		 * behind an expander will fail because of an unplug of the
+		 * expander itself.
+		 */
+		scmd_printk(KERN_WARNING, cmd,
+			    "%s: sas_eh_bus_reset_handler(%p) returned %d!\n",
+			    __func__, cmd, base_status);
+	}
+
+	/* WHAT TO DO HERE IF sas_phy_reset FAILS? */
+
+	if (isci_host != NULL)
+		spin_lock_irqsave(&isci_host->scic_lock, flags);
+	status
+		= scic_remote_device_reset_complete(isci_dev->sci_device_handle);
+
+	if (isci_host != NULL)
+		spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+
+	if (status != SCI_SUCCESS) {
+		scmd_printk(KERN_WARNING, cmd,
+			    "%s: scic_remote_device_reset_complete(%p) "
+			    "returned %d!\n",
+			    __func__, isci_dev, status);
+	}
+	/* WHAT TO DO HERE IF scic_remote_device_reset_complete FAILS? */
+
+	dev_dbg(&cmd->device->sdev_gendev,
+		"%s: cmd %p, isci_dev %p complete.\n",
+		__func__, cmd, isci_dev);
+
+	/* Resume I/O to the remote device. */
+	isci_device_set_host_quiesce_lock_state(isci_dev, false);
+
+	return TMF_RESP_FUNC_COMPLETE;
+}
diff --git a/drivers/scsi/isci/task.h b/drivers/scsi/isci/task.h
new file mode 100644
index 0000000..ced6a8b
--- /dev/null
+++ b/drivers/scsi/isci/task.h
@@ -0,0 +1,368 @@
+/*
+ * 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.
+ */
+
+#if !defined(_ISCI_TASK_H_)
+#define _ISCI_TASK_H_
+
+struct isci_request;
+struct isci_host;
+
+/**
+ * enum isci_tmf_cb_state - This enum defines the possible states in which the
+ *    TMF callback function is invoked during the TMF execution process.
+ *
+ *
+ */
+enum isci_tmf_cb_state {
+
+	isci_tmf_init_state = 0,
+	isci_tmf_started,
+	isci_tmf_timed_out
+};
+
+/**
+ * enum isci_tmf_function_codes - This enum defines the possible preparations
+ *    of task management requests.
+ *
+ *
+ */
+enum isci_tmf_function_codes {
+
+	isci_tmf_func_none      = 0,
+	isci_tmf_ssp_task_abort = TMF_ABORT_TASK,
+	isci_tmf_ssp_lun_reset  = TMF_LU_RESET,
+	isci_tmf_sata_srst_high = TMF_LU_RESET + 0x100, /* Non SCSI */
+	isci_tmf_sata_srst_low  = TMF_LU_RESET + 0x101  /* Non SCSI */
+};
+/**
+ * struct isci_tmf - This class represents the task management object which
+ *    acts as an interface to libsas for processing task management requests
+ *
+ *
+ */
+struct isci_tmf {
+
+	struct completion *complete;
+	enum sas_protocol proto;
+	union {
+		struct sci_ssp_response_iu resp_iu;
+		struct dev_to_host_fis d2h_fis;
+	}                            resp;
+	unsigned char lun[8];
+	u16 io_tag;
+	struct isci_remote_device *device;
+	enum isci_tmf_function_codes tmf_code;
+	int status;
+
+	struct isci_timer *timeout_timer;
+
+	/* The optional callback function allows the user process to
+	 * track the TMF transmit / timeout conditions.
+	 */
+	void (*cb_state_func)(
+		enum isci_tmf_cb_state,
+		struct isci_tmf *, void *);
+	void *cb_data;
+
+};
+
+static inline void isci_print_tmf(
+	struct isci_tmf *tmf)
+{
+	if (SAS_PROTOCOL_SATA == tmf->proto)
+		dev_dbg(&tmf->device->isci_port->isci_host->pdev->dev,
+			"%s: status = %x\n"
+			"tmf->resp.d2h_fis.status = %x\n"
+			"tmf->resp.d2h_fis.error = %x\n",
+			__func__,
+			tmf->status,
+			tmf->resp.d2h_fis.status,
+			tmf->resp.d2h_fis.error);
+	else
+		dev_dbg(&tmf->device->isci_port->isci_host->pdev->dev,
+			"%s: status = %x\n"
+			"tmf->resp.resp_iu.data_present = %x\n"
+			"tmf->resp.resp_iu.status = %x\n"
+			"tmf->resp.resp_iu.data_length = %x\n"
+			"tmf->resp.resp_iu.data[0] = %x\n"
+			"tmf->resp.resp_iu.data[1] = %x\n"
+			"tmf->resp.resp_iu.data[2] = %x\n"
+			"tmf->resp.resp_iu.data[3] = %x\n",
+			__func__,
+			tmf->status,
+			tmf->resp.resp_iu.data_present,
+			tmf->resp.resp_iu.status,
+			(tmf->resp.resp_iu.response_data_length[0] << 24) +
+			(tmf->resp.resp_iu.response_data_length[1] << 16) +
+			(tmf->resp.resp_iu.response_data_length[2] << 8) +
+			tmf->resp.resp_iu.response_data_length[3],
+			tmf->resp.resp_iu.data[0],
+			tmf->resp.resp_iu.data[1],
+			tmf->resp.resp_iu.data[2],
+			tmf->resp.resp_iu.data[3]);
+}
+
+
+int isci_task_execute_task(
+	struct sas_task *task,
+	int num,
+	gfp_t gfp_flags);
+
+int isci_task_abort_task(
+	struct sas_task *task);
+
+int isci_task_abort_task_set(
+	struct domain_device *d_device,
+	u8 *lun);
+
+int isci_task_clear_aca(
+	struct domain_device *d_device,
+	u8 *lun);
+
+int isci_task_clear_task_set(
+	struct domain_device *d_device,
+	u8 *lun);
+
+int isci_task_query_task(
+	struct sas_task *task);
+
+int isci_task_lu_reset(
+	struct domain_device *d_device,
+	u8 *lun);
+
+int isci_task_clear_nexus_port(
+	struct asd_sas_port *port);
+
+int isci_task_clear_nexus_ha(
+	struct sas_ha_struct *ha);
+
+int isci_task_I_T_nexus_reset(
+	struct domain_device *d_device);
+
+void isci_task_request_complete(
+	struct isci_host *isci_host,
+	struct isci_request *request,
+	enum sci_task_status completion_status);
+
+u16 isci_task_ssp_request_get_io_tag_to_manage(
+	struct isci_request *request);
+
+u8 isci_task_ssp_request_get_function(
+	struct isci_request *request);
+
+u32 isci_task_ssp_request_get_lun(
+	struct isci_request *request);
+
+void *isci_task_ssp_request_get_response_data_address(
+	struct isci_request *request);
+
+u32 isci_task_ssp_request_get_response_data_length(
+	struct isci_request *request);
+
+int isci_queuecommand(
+	struct scsi_cmnd *scsi_cmd,
+	void (*donefunc)(struct scsi_cmnd *));
+
+int isci_bus_reset_handler(struct scsi_cmnd *cmd);
+
+void isci_task_build_tmf(
+	struct isci_tmf *tmf,
+	struct isci_remote_device *isci_device,
+	enum isci_tmf_function_codes code,
+	void (*tmf_sent_cb)(
+		enum isci_tmf_cb_state,
+		struct isci_tmf *, void *),
+	void *cb_data);
+
+int isci_task_execute_tmf(
+	struct isci_host *isci_host,
+	struct isci_tmf *tmf,
+	unsigned long timeout_ms);
+
+/**
+ * enum isci_completion_selection - This enum defines the possible actions to
+ *    take with respect to a given request's notification back to libsas.
+ *
+ *
+ */
+enum isci_completion_selection {
+
+	isci_perform_normal_io_completion,      /* Normal notify (task_done) */
+	isci_perform_aborted_io_completion,     /* No notification.   */
+	isci_perform_error_io_completion        /* Use sas_task_abort */
+};
+
+static inline void isci_set_task_doneflags(
+	struct sas_task *task)
+{
+	/* Since no futher action will be taken on this task,
+	 * make sure to mark it complete from the lldd perspective.
+	 */
+	task->task_state_flags |= SAS_TASK_STATE_DONE;
+	task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+	task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+}
+/**
+ * isci_task_all_done() - This function clears the task bits to indicate the
+ *    LLDD is done with the task.
+ *
+ *
+ */
+static inline void isci_task_all_done(
+	struct sas_task *task)
+{
+	unsigned long flags;
+
+	/* Since no futher action will be taken on this task,
+	 * make sure to mark it complete from the lldd perspective.
+	 */
+	spin_lock_irqsave(&task->task_state_lock, flags);
+	isci_set_task_doneflags(task);
+	spin_unlock_irqrestore(&task->task_state_lock, flags);
+}
+
+/**
+ * isci_task_set_completion_status() - This function sets the completion status
+ *    for the request.
+ * @task: 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 inline void isci_task_set_completion_status(
+	struct sas_task *task,
+	enum service_response response,
+	enum exec_status status,
+	enum isci_completion_selection task_notification_selection)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&task->task_state_lock, flags);
+
+	task->task_status.resp = response;
+	task->task_status.stat = status;
+
+	/* Don't set DONE (or clear AT_INITIATOR) for any task going into the
+	 * error path, because the EH interprets that as a handled error condition.
+	 * Also don't take action if there is a reset pending.
+	 */
+	if ((task_notification_selection != isci_perform_error_io_completion)
+	    && !(task->task_state_flags & SAS_TASK_NEED_DEV_RESET))
+		isci_set_task_doneflags(task);
+
+	spin_unlock_irqrestore(&task->task_state_lock, flags);
+}
+/**
+ * isci_task_complete_for_upper_layer() - This function completes the request
+ *    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 inline void isci_task_complete_for_upper_layer(
+	struct sas_task *task,
+	enum service_response response,
+	enum exec_status status,
+	enum isci_completion_selection task_notification_selection)
+{
+	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(task->dev->port->ha->dev,
+			"%s: Normal - task = %p, response=%d, status=%d\n",
+			__func__, task, response, status);
+		task->task_done(task);
+		task->lldd_task = NULL;
+		break;
+
+	case isci_perform_aborted_io_completion:
+		/* No notification because this request is already in the
+		 * abort path.
+		 */
+		dev_warn(task->dev->port->ha->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(task->dev->port->ha->dev,
+			 "%s: Error - task = %p, response=%d, status=%d\n",
+			 __func__, task, response, status);
+		sas_task_abort(task);
+		break;
+
+	default:
+		dev_warn(task->dev->port->ha->dev,
+			 "%s: isci task notification default case!",
+			 __func__);
+		sas_task_abort(task);
+		break;
+	}
+}
+
+#endif /* !defined(_SCI_TASK_H_) */


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [RFC PATCH 3/6] isci: request (core request infrastructure)
  2011-02-07  0:34 [RFC PATCH 0/6] isci: initial driver release (part1: intro and lldd) Dan Williams
  2011-02-07  0:34 ` [RFC PATCH 1/6] isci: initialization Dan Williams
  2011-02-07  0:34 ` [RFC PATCH 2/6] isci: task (libsas interface support) Dan Williams
@ 2011-02-07  0:34 ` Dan Williams
  2011-03-18 16:41   ` Christoph Hellwig
  2011-02-07  0:34 ` [RFC PATCH 4/6] isci: hardware / topology event handling Dan Williams
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 36+ messages in thread
From: Dan Williams @ 2011-02-07  0:34 UTC (permalink / raw)
  To: james.bottomley
  Cc: dave.jiang, linux-scsi, jacek.danecki, ed.ciechanowski,
	jeffrey.d.skirvin, edmund.nadolski

Interface to core/ for request construction, submission, completion and
error handling.

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/scsi/isci/request.c | 1472 +++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/isci/request.h |  429 +++++++++++++
 2 files changed, 1901 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/isci/request.c
 create mode 100644 drivers/scsi/isci/request.h

diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c
new file mode 100644
index 0000000..e564121
--- /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;
+}
diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h
new file mode 100644
index 0000000..5079d4a
--- /dev/null
+++ b/drivers/scsi/isci/request.h
@@ -0,0 +1,429 @@
+/*
+ * 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.
+ */
+
+#if !defined(_ISCI_REQUEST_H_)
+#define _ISCI_REQUEST_H_
+
+#include "isci.h"
+
+/**
+ * struct isci_request_status - This enum defines the possible states of an I/O
+ *    request.
+ *
+ *
+ */
+enum isci_request_status {
+	unallocated = 0x00,
+	allocated   = 0x01,
+	started     = 0x02,
+	completed   = 0x03,
+	aborting    = 0x04,
+	aborted     = 0x05,
+	terminating = 0x06
+};
+
+enum task_type {
+	io_task  = 0,
+	tmf_task = 1
+};
+
+/**
+ * struct isci_request - This class represents the request object used to track
+ *    IO, smp and TMF request internal. It wraps the SCIC request object.
+ *
+ *
+ */
+struct isci_request {
+
+	struct scic_sds_request *sci_request_handle;
+
+	enum isci_request_status status;
+	enum task_type ttype;
+	unsigned short io_tag;
+	bool complete_in_target;
+
+	union ttype_ptr_union {
+		struct sas_task *io_task_ptr;   /* When ttype==io_task  */
+		struct isci_tmf *tmf_task_ptr;  /* When ttype==tmf_task */
+	} ttype_ptr;
+	struct isci_host *isci_host;
+	struct isci_remote_device *isci_device;
+	/* For use in the requests_to_{complete|abort} lists: */
+	struct list_head completed_node;
+	/* For use in the reqs_in_process list: */
+	struct list_head dev_node;
+	void *sci_request_mem_ptr;
+	spinlock_t state_lock;
+	dma_addr_t request_daddr;
+	dma_addr_t zero_scatter_daddr;
+
+	unsigned int num_sg_entries;                    /* returned by pci_alloc_sg */
+	unsigned int request_alloc_size;                /* size of block from dma_pool_alloc */
+
+	/** Note: "io_request_completion" is completed in two different ways
+	 * depending on whether this is a TMF or regular request.
+	 * - TMF requests are completed in the thread that started them;
+	 * - regular requests are completed in the request completion callback
+	 *   function.
+	 * This difference in operation allows the aborter of a TMF request
+	 * to be sure that once the TMF request completes, the I/O that the
+	 * TMF was aborting is guaranteed to have completed.
+	 */
+	struct completion *io_request_completion;
+};
+
+/**
+ * This function gets the status of the request object.
+ * @request: This parameter points to the isci_request object
+ *
+ * status of the object as a isci_request_status enum.
+ */
+static inline
+enum isci_request_status isci_request_get_state(
+	struct isci_request *isci_request)
+{
+	BUG_ON(isci_request == NULL);
+
+	/*probably a bad sign...	*/
+	if (isci_request->status == unallocated)
+		dev_warn(&isci_request->isci_host->pdev->dev,
+			 "%s: isci_request->status == unallocated\n",
+			 __func__);
+
+	return isci_request->status;
+}
+
+
+/**
+ * isci_request_change_state() - This function sets the status of the request
+ *    object.
+ * @request: This parameter points to the isci_request object
+ * @status: This Parameter is the new status of the object
+ *
+ */
+static inline enum isci_request_status isci_request_change_state(
+	struct isci_request *isci_request,
+	enum isci_request_status status)
+{
+	enum isci_request_status old_state;
+	unsigned long flags;
+
+	dev_dbg(&isci_request->isci_host->pdev->dev,
+		"%s: isci_request = %p, state = 0x%x\n",
+		__func__,
+		isci_request,
+		status);
+
+	BUG_ON(isci_request == NULL);
+
+	spin_lock_irqsave(&isci_request->state_lock, flags);
+	old_state = isci_request->status;
+	isci_request->status = status;
+	spin_unlock_irqrestore(&isci_request->state_lock, flags);
+
+	return old_state;
+}
+
+/**
+ * isci_request_change_started_to_newstate() - This function sets the status of
+ *    the request object.
+ * @request: This parameter points to the isci_request object
+ * @status: This Parameter is the new status of the object
+ *
+ * state previous to any change.
+ */
+static inline enum isci_request_status isci_request_change_started_to_newstate(
+	struct isci_request *isci_request,
+	struct completion *completion_ptr,
+	enum isci_request_status newstate)
+{
+	enum isci_request_status old_state;
+	unsigned long flags;
+
+	BUG_ON(isci_request == NULL);
+
+	spin_lock_irqsave(&isci_request->state_lock, flags);
+
+	old_state = isci_request->status;
+
+	if (old_state == started) {
+		BUG_ON(isci_request->io_request_completion != NULL);
+
+		isci_request->io_request_completion = completion_ptr;
+		isci_request->status = newstate;
+	}
+	spin_unlock_irqrestore(&isci_request->state_lock, flags);
+
+	dev_dbg(&isci_request->isci_host->pdev->dev,
+		"%s: isci_request = %p, old_state = 0x%x\n",
+		__func__,
+		isci_request,
+		old_state);
+
+	return old_state;
+}
+
+/**
+ * isci_request_change_started_to_aborted() - This function sets the status of
+ *    the request object.
+ * @request: This parameter points to the isci_request object
+ * @completion_ptr: This parameter is saved as the kernel completion structure
+ *    signalled when the old request completes.
+ *
+ * state previous to any change.
+ */
+static inline enum isci_request_status isci_request_change_started_to_aborted(
+	struct isci_request *isci_request,
+	struct completion *completion_ptr)
+{
+	return isci_request_change_started_to_newstate(
+		       isci_request, completion_ptr, aborted
+		       );
+}
+/**
+ * isci_request_free() - This function frees the request object.
+ * @isci_host: This parameter specifies the ISCI host object
+ * @isci_request: This parameter points to the isci_request object
+ *
+ */
+static inline void isci_request_free(
+	struct isci_host *isci_host,
+	struct isci_request *isci_request)
+{
+	BUG_ON(isci_request == NULL);
+
+	/* release the dma memory if we fail. */
+	dma_pool_free(isci_host->dma_pool, isci_request,
+		      isci_request->request_daddr);
+}
+
+
+/* #define ISCI_REQUEST_VALIDATE_ACCESS
+ */
+
+#ifdef ISCI_REQUEST_VALIDATE_ACCESS
+
+static inline
+struct sas_task *isci_request_access_task(struct isci_request *isci_request)
+{
+	BUG_ON(isci_request->ttype != io_task);
+	return isci_request->ttype_ptr.io_task_ptr;
+}
+
+static inline
+struct isci_tmf *isci_request_access_tmf(struct isci_request *isci_request)
+{
+	BUG_ON(isci_request->ttype != tmf_task);
+	return isci_request->ttype_ptr.tmf_task_ptr;
+}
+
+#else  /* not ISCI_REQUEST_VALIDATE_ACCESS */
+
+#define isci_request_access_task(RequestPtr) \
+	((RequestPtr)->ttype_ptr.io_task_ptr)
+
+#define isci_request_access_tmf(RequestPtr)  \
+	((RequestPtr)->ttype_ptr.tmf_task_ptr)
+
+#endif /* not ISCI_REQUEST_VALIDATE_ACCESS */
+
+
+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 isci_request_execute(
+	struct isci_host *isci_host,
+	struct sas_task *task,
+	struct isci_request **request,
+	gfp_t gfp_flags);
+
+/**
+ * isci_request_unmap_sgl() - This function unmaps the DMA address of a given
+ *    sgl
+ * @request: This parameter points to the isci_request object
+ * @*pdev: This Parameter is the pci_device struct for the controller
+ *
+ */
+static inline void isci_request_unmap_sgl(
+	struct isci_request *request,
+	struct pci_dev *pdev)
+{
+	struct sas_task *task = isci_request_access_task(request);
+
+	dev_dbg(&request->isci_host->pdev->dev,
+		"%s: request = %p, task = %p,\n"
+		"task->data_dir = %d, is_sata = %d\n ",
+		__func__,
+		request,
+		task,
+		task->data_dir,
+		sas_protocol_ata(task->task_proto));
+
+	if ((task->data_dir != PCI_DMA_NONE) &&
+	    !sas_protocol_ata(task->task_proto)) {
+		if (task->num_scatter == 0)
+			/* 0 indicates a single dma address */
+			dma_unmap_single(
+				&pdev->dev,
+				request->zero_scatter_daddr,
+				task->total_xfer_len,
+				task->data_dir
+				);
+
+		else  /* unmap the sgl dma addresses */
+			dma_unmap_sg(
+				&pdev->dev,
+				task->scatter,
+				request->num_sg_entries,
+				task->data_dir
+				);
+	}
+}
+
+
+void isci_request_io_request_complete(
+	struct isci_host *isci_host,
+	struct isci_request *request,
+	enum sci_io_status completion_status);
+
+u32 isci_request_io_request_get_transfer_length(
+	struct isci_request *request);
+
+SCI_IO_REQUEST_DATA_DIRECTION isci_request_io_request_get_data_direction(
+	struct isci_request *request);
+
+/**
+ * isci_request_io_request_get_next_sge() - This function is called by the sci
+ *    core to retrieve the next sge for a given request.
+ * @request: This parameter is the isci_request object.
+ * @current_sge_address: This parameter is the last sge retrieved by the sci
+ *    core for this request.
+ *
+ * pointer to the next sge for specified request.
+ */
+static inline void *isci_request_io_request_get_next_sge(
+	struct isci_request *request,
+	void *current_sge_address)
+{
+	struct sas_task *task = isci_request_access_task(request);
+	void *ret = NULL;
+
+	dev_dbg(&request->isci_host->pdev->dev,
+		"%s: request = %p, "
+		"current_sge_address = %p, "
+		"num_scatter = %d\n",
+		__func__,
+		request,
+		current_sge_address,
+		task->num_scatter);
+
+	if (!current_sge_address)	/* First time through.. */
+		ret = task->scatter;    /* always task->scatter */
+	else if (task->num_scatter == 0) /* Next element, if num_scatter == 0 */
+		ret = NULL;              /* there is only one element. */
+	else
+		ret = sg_next(current_sge_address);     /* sg_next returns NULL
+							 * for the last element
+							 */
+
+	dev_dbg(&request->isci_host->pdev->dev,
+		"%s: next sge address = %p\n",
+		__func__,
+		ret);
+
+	return ret;
+}
+
+dma_addr_t isci_request_sge_get_address_field(
+	struct isci_request *request,
+	void *sge_address);
+
+u32 isci_request_sge_get_length_field(
+	struct isci_request *request,
+	void *sge_address);
+
+void *isci_request_ssp_io_request_get_cdb_address(
+	struct isci_request *request);
+
+u32 isci_request_ssp_io_request_get_cdb_length(
+	struct isci_request *request);
+
+u32  isci_request_ssp_io_request_get_lun(
+	struct isci_request *request);
+
+u32 isci_request_ssp_io_request_get_task_attribute(
+	struct isci_request *request);
+
+u32 isci_request_ssp_io_request_get_command_priority(
+	struct isci_request *request);
+
+
+
+
+
+void isci_terminate_pending_requests(
+	struct isci_host *isci_host,
+	struct isci_remote_device *isci_device,
+	enum isci_request_status new_request_state);
+
+
+
+
+#endif /* !defined(_ISCI_REQUEST_H_) */


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [RFC PATCH 4/6] isci: hardware / topology event handling
  2011-02-07  0:34 [RFC PATCH 0/6] isci: initial driver release (part1: intro and lldd) Dan Williams
                   ` (2 preceding siblings ...)
  2011-02-07  0:34 ` [RFC PATCH 3/6] isci: request (core request infrastructure) Dan Williams
@ 2011-02-07  0:34 ` Dan Williams
  2011-03-18 16:18   ` Christoph Hellwig
  2011-02-07  0:35 ` [RFC PATCH 5/6] isci: phy, port, and remote device Dan Williams
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 36+ messages in thread
From: Dan Williams @ 2011-02-07  0:34 UTC (permalink / raw)
  To: james.bottomley
  Cc: dave.jiang, linux-scsi, jacek.danecki, ed.ciechanowski,
	jeffrey.d.skirvin, edmund.nadolski

1/ Core notifications (phy, port, and device events)
2/ Timer interface.
2/ OS abstraction core callbacks (deprecated / to be removed)

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/scsi/isci/deprecated.c |  491 ++++++++++++++++++++++++++++++++
 drivers/scsi/isci/events.c     |  619 ++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/isci/timers.c     |  319 +++++++++++++++++++++
 drivers/scsi/isci/timers.h     |  126 ++++++++
 4 files changed, 1555 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/isci/deprecated.c
 create mode 100644 drivers/scsi/isci/events.c
 create mode 100644 drivers/scsi/isci/timers.c
 create mode 100644 drivers/scsi/isci/timers.h

diff --git a/drivers/scsi/isci/deprecated.c b/drivers/scsi/isci/deprecated.c
new file mode 100644
index 0000000..847e687
--- /dev/null
+++ b/drivers/scsi/isci/deprecated.c
@@ -0,0 +1,491 @@
+/*
+ * 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.
+ */
+
+
+/**
+ * This file contains isci module object implementation.
+ *
+ *
+ */
+
+#include "isci.h"
+#include "request.h"
+#include "sata.h"
+#include "task.h"
+
+
+/**
+ * scic_cb_stall_execution() - This method is called when the core requires the
+ *    OS driver to stall execution.  This method is utilized during
+ *    initialization or non-performance paths only.
+ * @microseconds: This parameter specifies the number of microseconds for which
+ *    to stall.  The operating system driver is allowed to round this value up
+ *    where necessary.
+ *
+ * none.
+ */
+void scic_cb_stall_execution(
+	u32 microseconds)
+{
+	udelay(microseconds);
+}
+
+
+/**
+ * scic_cb_io_request_get_physical_address() - This callback method asks the
+ *    user to provide the physical address for the supplied virtual address
+ *    when building an io request object.
+ * @controller: This parameter is the core controller object handle.
+ * @io_request: This parameter is the io request object handle for which the
+ *    physical address is being requested.
+ *
+ *
+ */
+void scic_cb_io_request_get_physical_address(
+	struct scic_sds_controller *controller,
+	struct scic_sds_request *io_request,
+	void *virtual_address,
+	dma_addr_t *physical_address)
+{
+	struct isci_request *request =
+		(struct isci_request *)sci_object_get_association(io_request);
+
+	char *requested_address = (char *)virtual_address;
+	char *base_address = (char *)request;
+
+	BUG_ON(requested_address < base_address);
+	BUG_ON((requested_address - base_address) >=
+			request->request_alloc_size);
+
+	*physical_address = request->request_daddr +
+		(requested_address - base_address);
+}
+
+/**
+ * scic_cb_io_request_get_transfer_length() - This callback method asks the
+ *    user to provide the number of bytes to be transfered as part of this
+ *    request.
+ * @scic_user_io_request: This parameter points to the user's IO request
+ *    object.  It is a cookie that allows the user to provide the necessary
+ *    information for this callback.
+ *
+ * This method returns the number of payload data bytes to be transfered for
+ * this IO request.
+ */
+u32 scic_cb_io_request_get_transfer_length(
+	void *scic_user_io_request)
+{
+	return isci_request_io_request_get_transfer_length(
+		       scic_user_io_request
+		       );
+}
+
+
+/**
+ * scic_cb_io_request_get_data_direction() - This callback method asks the user
+ *    to provide the data direction for this request.
+ * @scic_user_io_request: This parameter points to the user's IO request
+ *    object.  It is a cookie that allows the user to provide the necessary
+ *    information for this callback.
+ *
+ * This method returns the value of SCI_IO_REQUEST_DATA_OUT or
+ * SCI_IO_REQUEST_DATA_IN, or SCI_IO_REQUEST_NO_DATA.
+ */
+SCI_IO_REQUEST_DATA_DIRECTION scic_cb_io_request_get_data_direction(
+	void *scic_user_io_request)
+{
+	return isci_request_io_request_get_data_direction(
+		       scic_user_io_request
+		       );
+}
+
+
+/**
+ * scic_cb_io_request_get_next_sge() - This callback method asks the user to
+ *    provide the address to where the next Scatter-Gather Element is located.
+ * @scic_user_io_request: This parameter points to the user's IO request
+ *    object.  It is a cookie that allows the user to provide the necessary
+ *    information for this callback.
+ * @current_sge_address: This parameter specifies the address for the current
+ *    SGE (i.e. the one that has just processed).
+ *
+ * An address specifying the location for the next scatter gather element to be
+ * processed.
+ */
+void scic_cb_io_request_get_next_sge(
+	void *scic_user_io_request,
+	void *current_sge_address,
+	void **next_sge)
+{
+	*next_sge = isci_request_io_request_get_next_sge(
+		scic_user_io_request,
+		current_sge_address
+		);
+}
+
+/**
+ * scic_cb_sge_get_address_field() - This callback method asks the user to
+ *    provide the contents of the "address" field in the Scatter-Gather Element.
+ * @scic_user_io_request: This parameter points to the user's IO request
+ *    object.  It is a cookie that allows the user to provide the necessary
+ *    information for this callback.
+ * @sge_address: This parameter specifies the address for the SGE from which to
+ *    retrieve the address field.
+ *
+ * A physical address specifying the contents of the SGE's address field.
+ */
+dma_addr_t scic_cb_sge_get_address_field(
+	void *scic_user_io_request,
+	void *sge_address)
+{
+	return isci_request_sge_get_address_field(
+		       scic_user_io_request,
+		       sge_address
+		       );
+}
+
+/**
+ * scic_cb_sge_get_length_field() - This callback method asks the user to
+ *    provide the contents of the "length" field in the Scatter-Gather Element.
+ * @scic_user_io_request: This parameter points to the user's IO request
+ *    object.  It is a cookie that allows the user to provide the necessary
+ *    information for this callback.
+ * @sge_address: This parameter specifies the address for the SGE from which to
+ *    retrieve the address field.
+ *
+ * This method returns the length field specified inside the SGE referenced by
+ * the sge_address parameter.
+ */
+u32 scic_cb_sge_get_length_field(
+	void *scic_user_io_request,
+	void *sge_address)
+{
+	return isci_request_sge_get_length_field(
+		       scic_user_io_request,
+		       sge_address
+		       );
+}
+
+/**
+ * scic_cb_ssp_io_request_get_cdb_address() - This callback method asks the
+ *    user to provide the address for the command descriptor block (CDB)
+ *    associated with this IO request.
+ * @scic_user_io_request: This parameter points to the user's IO request
+ *    object.  It is a cookie that allows the user to provide the necessary
+ *    information for this callback.
+ *
+ * This method returns the virtual address of the CDB.
+ */
+void *scic_cb_ssp_io_request_get_cdb_address(
+	void *scic_user_io_request)
+{
+	return isci_request_ssp_io_request_get_cdb_address(
+		       scic_user_io_request
+		       );
+}
+
+/**
+ * scic_cb_ssp_io_request_get_cdb_length() - This callback method asks the user
+ *    to provide the length of the command descriptor block (CDB) associated
+ *    with this IO request.
+ * @scic_user_io_request: This parameter points to the user's IO request
+ *    object.  It is a cookie that allows the user to provide the necessary
+ *    information for this callback.
+ *
+ * This method returns the length of the CDB.
+ */
+u32 scic_cb_ssp_io_request_get_cdb_length(
+	void *scic_user_io_request)
+{
+	return isci_request_ssp_io_request_get_cdb_length(
+		       scic_user_io_request
+		       );
+}
+
+/**
+ * scic_cb_ssp_io_request_get_lun() - This callback method asks the user to
+ *    provide the Logical Unit (LUN) associated with this IO request.
+ * @scic_user_io_request: This parameter points to the user's IO request
+ *    object.  It is a cookie that allows the user to provide the necessary
+ *    information for this callback.
+ *
+ * This method returns the LUN associated with this request. This should be u64?
+ */
+u32 scic_cb_ssp_io_request_get_lun(
+	void *scic_user_io_request)
+{
+	return isci_request_ssp_io_request_get_lun(scic_user_io_request);
+}
+
+/**
+ * scic_cb_ssp_io_request_get_task_attribute() - This callback method asks the
+ *    user to provide the task attribute associated with this IO request.
+ * @scic_user_io_request: This parameter points to the user's IO request
+ *    object.  It is a cookie that allows the user to provide the necessary
+ *    information for this callback.
+ *
+ * This method returns the task attribute associated with this IO request.
+ */
+u32 scic_cb_ssp_io_request_get_task_attribute(
+	void *scic_user_io_request)
+{
+	return isci_request_ssp_io_request_get_task_attribute(
+		       scic_user_io_request
+		       );
+}
+
+/**
+ * scic_cb_ssp_io_request_get_command_priority() - This callback method asks
+ *    the user to provide the command priority associated with this IO request.
+ * @scic_user_io_request: This parameter points to the user's IO request
+ *    object.  It is a cookie that allows the user to provide the necessary
+ *    information for this callback.
+ *
+ * This method returns the command priority associated with this IO request.
+ */
+u32 scic_cb_ssp_io_request_get_command_priority(
+	void *scic_user_io_request)
+{
+	return isci_request_ssp_io_request_get_command_priority(
+		       scic_user_io_request
+		       );
+}
+
+/**
+ * scic_cb_ssp_task_request_get_lun() - This method returns the Logical Unit to
+ *    be utilized for this task management request.
+ * @scic_user_task_request: This parameter points to the user's task request
+ *    object.  It is a cookie that allows the user to provide the necessary
+ *    information for this callback.
+ *
+ * This method returns the LUN associated with this request. This should be u64?
+ */
+u32 scic_cb_ssp_task_request_get_lun(
+	void *scic_user_task_request)
+{
+	return isci_task_ssp_request_get_lun(
+		       (struct isci_request *)scic_user_task_request
+		       );
+}
+
+/**
+ * scic_cb_ssp_task_request_get_function() - This method returns the task
+ *    management function to be utilized for this task request.
+ * @scic_user_task_request: This parameter points to the user's task request
+ *    object.  It is a cookie that allows the user to provide the necessary
+ *    information for this callback.
+ *
+ * This method returns an unsigned byte representing the task management
+ * function to be performed.
+ */
+u8 scic_cb_ssp_task_request_get_function(
+	void *scic_user_task_request)
+{
+	return isci_task_ssp_request_get_function(
+		       (struct isci_request *)scic_user_task_request
+		       );
+}
+
+/**
+ * scic_cb_ssp_task_request_get_io_tag_to_manage() - This method returns the
+ *    task management IO tag to be managed. Depending upon the task management
+ *    function the value returned from this method may be ignored.
+ * @scic_user_task_request: This parameter points to the user's task request
+ *    object.  It is a cookie that allows the user to provide the necessary
+ *    information for this callback.
+ *
+ * This method returns an unsigned 16-bit word depicting the IO tag to be
+ * managed.
+ */
+u16 scic_cb_ssp_task_request_get_io_tag_to_manage(
+	void *scic_user_task_request)
+{
+	return isci_task_ssp_request_get_io_tag_to_manage(
+		       (struct isci_request *)scic_user_task_request
+		       );
+}
+
+/**
+ * scic_cb_ssp_task_request_get_response_data_address() - This callback method
+ *    asks the user to provide the virtual address of the response data buffer
+ *    for the supplied IO request.
+ * @scic_user_task_request: This parameter points to the user's task request
+ *    object.  It is a cookie that allows the user to provide the necessary
+ *    information for this callback.
+ *
+ * This method returns the virtual address for the response data buffer
+ * associated with this IO request.
+ */
+void *scic_cb_ssp_task_request_get_response_data_address(
+	void *scic_user_task_request)
+{
+	return isci_task_ssp_request_get_response_data_address(
+		       (struct isci_request *)scic_user_task_request
+		       );
+}
+
+/**
+ * scic_cb_ssp_task_request_get_response_data_length() - This callback method
+ *    asks the user to provide the length of the response data buffer for the
+ *    supplied IO request.
+ * @scic_user_task_request: This parameter points to the user's task request
+ *    object.  It is a cookie that allows the user to provide the necessary
+ *    information for this callback.
+ *
+ * This method returns the length of the response buffer data associated with
+ * this IO request.
+ */
+u32 scic_cb_ssp_task_request_get_response_data_length(
+	void *scic_user_task_request)
+{
+	return isci_task_ssp_request_get_response_data_length(
+		       (struct isci_request *)scic_user_task_request
+		       );
+}
+
+#if !defined(DISABLE_ATAPI)
+/**
+ * scic_cb_stp_packet_io_request_get_cdb_address() - This user callback asks
+ *    the user to provide stp packet io's the CDB address.
+ * @scic_user_io_request:
+ *
+ * The packet IO's cdb adress.
+ */
+void *scic_cb_stp_packet_io_request_get_cdb_address(
+	void *scic_user_io_request)
+{
+	return isci_request_stp_packet_io_request_get_cdb_address(
+		       scic_user_io_request
+		       );
+}
+
+
+/**
+ * scic_cb_stp_packet_io_request_get_cdb_length() - This user callback asks the
+ *    user to provide stp packet io's the CDB length.
+ * @scic_user_io_request:
+ *
+ * The packet IO's cdb length.
+ */
+u32 scic_cb_stp_packet_io_request_get_cdb_length(
+	void *scic_user_io_request)
+{
+	return isci_request_stp_packet_io_request_get_cdb_length(
+		       scic_user_io_request
+		       );
+}
+#endif /* #if !defined(DISABLE_ATAPI) */
+
+
+/**
+ * scic_cb_io_request_do_copy_rx_frames() - This callback method asks the user
+ *    if the received RX frame data is to be copied to the SGL or should be
+ *    stored by the SCI core to be retrieved later with the
+ *    scic_io_request_get_rx_frame().
+ * @scic_user_io_request: This parameter points to the user's IO request
+ *    object.  It is a cookie that allows the user to provide the necessary
+ *    information for this callback.
+ *
+ * This method returns true if the SCI core should copy the received frame data
+ * to the SGL location or false if the SCI user wants to retrieve the frame
+ * data at a later time.
+ */
+bool scic_cb_io_request_do_copy_rx_frames(
+	void *scic_user_io_request)
+{
+	struct sas_task *task
+		= isci_request_access_task(
+		(struct isci_request *)scic_user_io_request
+		);
+
+	return (task->data_dir == DMA_NONE) ? false : true;
+}
+
+/**
+ * scic_cb_get_virtual_address() - This callback method asks the user to
+ *    provide the virtual address for the supplied physical address.
+ * @controller: This parameter is the core controller object handle.
+ * @physical_address: This parameter is the physical address which is to be
+ *    returned as a virtual address.
+ *
+ * The method returns the virtual address for the supplied physical address.
+ */
+void *scic_cb_get_virtual_address(
+	struct scic_sds_controller *controller,
+	dma_addr_t physical_address)
+{
+	void *virt_addr = (void *)phys_to_virt(physical_address);
+
+	return virt_addr;
+}
+
+/**
+ * scic_cb_request_get_sat_protocol() - This callback method asks the user to
+ *    return the SAT protocol definition for this IO request.  This method is
+ *    only called by the SCI core if the request type constructed is SATA.
+ * @scic_user_io_request: This parameter points to the user's IO request
+ *    object.  It is a cookie that allows the user to provide the necessary
+ *    information for this callback.
+ *
+ * This method returns one of the sat.h defined protocols for the given io
+ * request.
+ */
+u8 scic_cb_request_get_sat_protocol(
+	void *scic_user_io_request)
+{
+	return isci_sata_get_sat_protocol(
+		       (struct isci_request *)scic_user_io_request
+		       );
+}
diff --git a/drivers/scsi/isci/events.c b/drivers/scsi/isci/events.c
new file mode 100644
index 0000000..75f9cd5
--- /dev/null
+++ b/drivers/scsi/isci/events.c
@@ -0,0 +1,619 @@
+/*
+ * 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.
+ */
+
+
+/**
+ * This file contains isci module object implementation.
+ *
+ *
+ */
+
+#include "isci.h"
+#include "request.h"
+#include "sata.h"
+#include "task.h"
+
+/**
+ * scic_cb_timer_create() - This callback method asks the user to create a
+ *    timer and provide a handle for this timer for use in further timer
+ *    interactions. The appropriate isci timer object function is called to
+ *    create a timer object.
+ * @timer_callback: This parameter specifies the callback method to be invoked
+ *    whenever the timer expires.
+ * @controller: This parameter specifies the controller with which this timer
+ *    is to be associated.
+ * @cookie: This parameter specifies a piece of information that the user must
+ *    retain.  This cookie is to be supplied by the user anytime a timeout
+ *    occurs for the created timer.
+ *
+ * This method returns a handle to a timer object created by the user.  The
+ * handle will be utilized for all further interactions relating to this timer.
+ */
+void *scic_cb_timer_create(
+	struct scic_sds_controller *controller,
+	void (*timer_callback)(void *),
+	void *cookie)
+{
+	struct isci_host *isci_host;
+	struct isci_timer *timer = NULL;
+
+	isci_host = (struct isci_host *)sci_object_get_association(controller);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_host = %p",
+		__func__, isci_host);
+
+	timer = isci_timer_create(&isci_host->timer_list_struct,
+				  isci_host,
+				  cookie,
+				  timer_callback);
+
+	dev_dbg(&isci_host->pdev->dev, "%s: timer = %p\n", __func__, timer);
+
+	return (void *)timer;
+}
+
+
+/**
+ * scic_cb_timer_start() - This callback method asks the user to start the
+ *    supplied timer. The appropriate isci timer object function is called to
+ *    start the timer.
+ * @controller: This parameter specifies the controller with which this timer
+ *    is to associated.
+ * @timer: This parameter specifies the timer to be started.
+ * @milliseconds: This parameter specifies the number of milliseconds for which
+ *    to stall.  The operating system driver is allowed to round this value up
+ *    where necessary.
+ *
+ */
+void scic_cb_timer_start(
+	struct scic_sds_controller *controller,
+	void *timer,
+	u32 milliseconds)
+{
+	struct isci_host *isci_host;
+
+	isci_host =
+		(struct isci_host *)sci_object_get_association(controller);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_host = %p, timer = %p, milliseconds = %d\n",
+		__func__, isci_host, timer, milliseconds);
+
+	isci_timer_start((struct isci_timer *)timer, milliseconds);
+
+}
+
+/**
+ * scic_cb_timer_stop() - This callback method asks the user to stop the
+ *    supplied timer. The appropriate isci timer object function is called to
+ *    stop the timer.
+ * @controller: This parameter specifies the controller with which this timer
+ *    is to associated.
+ * @timer: This parameter specifies the timer to be stopped.
+ *
+ */
+void scic_cb_timer_stop(
+	struct scic_sds_controller *controller,
+	void *timer)
+{
+	struct isci_host *isci_host;
+
+	isci_host =
+		(struct isci_host *)sci_object_get_association(controller);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_host = %p, timer = %p\n",
+		__func__, isci_host, timer);
+
+	isci_timer_stop((struct isci_timer *)timer);
+}
+
+/**
+ * scic_cb_controller_start_complete() - This user callback will inform the
+ *    user that the controller has finished the start process. The associated
+ *    isci host adapter's start_complete function is called.
+ * @controller: This parameter specifies the controller that was started.
+ * @completion_status: This parameter specifies the results of the start
+ *    operation.  SCI_SUCCESS indicates successful completion.
+ *
+ */
+void scic_cb_controller_start_complete(
+	struct scic_sds_controller *controller,
+	enum sci_status completion_status)
+{
+	struct isci_host *isci_host =
+		(struct isci_host *)sci_object_get_association(controller);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_host = %p\n", __func__, isci_host);
+
+	isci_host_start_complete(isci_host, completion_status);
+}
+
+/**
+ * scic_cb_controller_stop_complete() - This user callback will inform the user
+ *    that the controller has finished the stop process. The associated isci
+ *    host adapter's start_complete function is called.
+ * @controller: This parameter specifies the controller that was stopped.
+ * @completion_status: This parameter specifies the results of the stop
+ *    operation.  SCI_SUCCESS indicates successful completion.
+ *
+ */
+void scic_cb_controller_stop_complete(
+	struct scic_sds_controller *controller,
+	enum sci_status completion_status)
+{
+	struct isci_host *isci_host =
+		(struct isci_host *)sci_object_get_association(controller);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: status = 0x%x\n", __func__, completion_status);
+	isci_host_stop_complete(isci_host, completion_status);
+}
+
+/**
+ * scic_cb_io_request_complete() - This user callback will inform the user that
+ *    an IO request has completed.
+ * @controller: This parameter specifies the controller on which the IO is
+ *    completing.
+ * @remote_device: This parameter specifies the remote device on which this IO
+ *    request is completing.
+ * @io_request: This parameter specifies the IO request that has completed.
+ * @completion_status: This parameter specifies the results of the IO request
+ *    operation.  SCI_SUCCESS indicates successful completion.
+ *
+ */
+void scic_cb_io_request_complete(
+	struct scic_sds_controller *controller,
+	struct scic_sds_remote_device *remote_device,
+	struct scic_sds_request *scic_io_request,
+	enum sci_io_status completion_status)
+{
+	struct isci_request *request;
+	struct isci_host *isci_host;
+
+	isci_host =
+		(struct isci_host *)sci_object_get_association(controller);
+
+	request =
+		(struct isci_request *)sci_object_get_association(
+			scic_io_request
+			);
+
+	isci_request_io_request_complete(isci_host,
+					 request,
+					 completion_status);
+}
+
+/**
+ * scic_cb_task_request_complete() - This user callback will inform the user
+ *    that a task management request completed.
+ * @controller: This parameter specifies the controller on which the task
+ *    management request is completing.
+ * @remote_device: This parameter specifies the remote device on which this
+ *    task management request is completing.
+ * @task_request: This parameter specifies the task management request that has
+ *    completed.
+ * @completion_status: This parameter specifies the results of the IO request
+ *    operation.  SCI_SUCCESS indicates successful completion.
+ *
+ */
+void scic_cb_task_request_complete(
+	struct scic_sds_controller *controller,
+	struct scic_sds_remote_device *remote_device,
+	struct scic_sds_request *scic_task_request,
+	enum sci_task_status completion_status)
+{
+	struct isci_request *request;
+	struct isci_host *isci_host;
+
+	isci_host =
+		(struct isci_host *)sci_object_get_association(controller);
+
+	request =
+		(struct isci_request *)sci_object_get_association(
+			scic_task_request);
+
+	isci_task_request_complete(isci_host, request, completion_status);
+}
+
+/**
+ * scic_cb_port_stop_complete() - This method informs the user when a stop
+ *    operation on the port has completed.
+ * @controller: This parameter represents the controller which contains the
+ *    port.
+ * @port: This parameter specifies the SCI port object for which the callback
+ *    is being invoked.
+ * @completion_status: This parameter specifies the status for the operation
+ *    being completed.
+ *
+ */
+void scic_cb_port_stop_complete(
+	struct scic_sds_controller *controller,
+	struct scic_sds_port *port,
+	enum sci_status completion_status)
+{
+	pr_warn("%s:************************************************\n",
+		__func__);
+}
+
+/**
+ * scic_cb_port_hard_reset_complete() - This method informs the user when a
+ *    hard reset on the port has completed.  This hard reset could have been
+ *    initiated by the user or by the remote port.
+ * @controller: This parameter represents the controller which contains the
+ *    port.
+ * @port: This parameter specifies the SCI port object for which the callback
+ *    is being invoked.
+ * @completion_status: This parameter specifies the status for the operation
+ *    being completed.
+ *
+ */
+void scic_cb_port_hard_reset_complete(
+	struct scic_sds_controller *controller,
+	struct scic_sds_port *port,
+	enum sci_status completion_status)
+{
+	struct isci_port *isci_port
+		= (struct isci_port *)sci_object_get_association(port);
+
+	isci_port_hard_reset_complete(isci_port, completion_status);
+}
+
+/**
+ * scic_cb_port_ready() - This method informs the user that the port is now in
+ *    a ready state and can be utilized to issue IOs.
+ * @controller: This parameter represents the controller which contains the
+ *    port.
+ * @port: This parameter specifies the SCI port object for which the callback
+ *    is being invoked.
+ *
+ */
+void scic_cb_port_ready(
+	struct scic_sds_controller *controller,
+	struct scic_sds_port *port)
+{
+	struct isci_port *isci_port;
+	struct isci_host *isci_host;
+
+	isci_host =
+		(struct isci_host *)sci_object_get_association(controller);
+
+	isci_port =
+		(struct isci_port *)sci_object_get_association(port);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_port = %p\n", __func__, isci_port);
+
+	isci_port_ready(isci_host, isci_port);
+}
+
+/**
+ * scic_cb_port_not_ready() - This method informs the user that the port is now
+ *    not in a ready (i.e. busy) state and can't be utilized to issue IOs.
+ * @controller: This parameter represents the controller which contains the
+ *    port.
+ * @port: This parameter specifies the SCI port object for which the callback
+ *    is being invoked.
+ *
+ */
+void scic_cb_port_not_ready(
+	struct scic_sds_controller *controller,
+	struct scic_sds_port *port,
+	u32 reason_code)
+{
+	struct isci_port *isci_port;
+	struct isci_host *isci_host;
+
+	isci_host =
+		(struct isci_host *)sci_object_get_association(controller);
+
+	isci_port =
+		(struct isci_port *)sci_object_get_association(port);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_port = %p\n", __func__, isci_port);
+
+	isci_port_not_ready(isci_host, isci_port);
+}
+
+/**
+ * scic_cb_port_invalid_link_up() - This method informs the SCI Core user that
+ *    a phy/link became ready, but the phy is not allowed in the port.  In some
+ *    situations the underlying hardware only allows for certain phy to port
+ *    mappings.  If these mappings are violated, then this API is invoked.
+ * @controller: This parameter represents the controller which contains the
+ *    port.
+ * @port: This parameter specifies the SCI port object for which the callback
+ *    is being invoked.
+ * @phy: This parameter specifies the phy that came ready, but the phy can't be
+ *    a valid member of the port.
+ *
+ */
+void scic_cb_port_invalid_link_up(
+	struct scic_sds_controller *controller,
+	struct scic_sds_port *port,
+	struct scic_sds_phy *phy)
+{
+	pr_warn("%s:************************************************\n",
+		__func__);
+}
+
+/**
+ * scic_cb_port_bc_change_primitive_received() - This callback method informs
+ *    the user that a broadcast change primitive was received.
+ * @controller: This parameter represents the controller which contains the
+ *    port.
+ * @port: This parameter specifies the SCI port object for which the callback
+ *    is being invoked.  For instances where the phy on which the primitive was
+ *    received is not part of a port, this parameter will be
+ *    SCI_INVALID_HANDLE_T.
+ * @phy: This parameter specifies the phy on which the primitive was received.
+ *
+ */
+void scic_cb_port_bc_change_primitive_received(
+	struct scic_sds_controller *controller,
+	struct scic_sds_port *port,
+	struct scic_sds_phy *phy)
+{
+	struct isci_host *isci_host;
+
+	isci_host =
+		(struct isci_host *)sci_object_get_association(controller);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: port = %p, phy = %p\n", __func__, port, phy);
+	isci_port_bc_change_received(isci_host, port, phy);
+}
+
+
+
+
+/**
+ * scic_cb_port_link_up() - This callback method informs the user that a phy
+ *    has become operational and is capable of communicating with the remote
+ *    end point.
+ * @controller: This parameter represents the controller associated with the
+ *    phy.
+ * @port: This parameter specifies the port object for which the user callback
+ *    is being invoked.  There may be conditions where this parameter can be
+ *    SCI_INVALID_HANDLE
+ * @phy: This parameter specifies the phy object for which the user callback is
+ *    being invoked.
+ *
+ * none.
+ */
+void scic_cb_port_link_up(
+	struct scic_sds_controller *controller,
+	struct scic_sds_port *port,
+	struct scic_sds_phy *phy)
+{
+	struct isci_host *isci_host;
+
+	isci_host =
+		(struct isci_host *)sci_object_get_association(controller);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: phy = %p\n", __func__, phy);
+
+	isci_port_link_up(isci_host, port, phy);
+}
+
+/**
+ * scic_cb_port_link_down() - This callback method informs the user that a phy
+ *    is no longer operational and is not capable of communicating with the
+ *    remote end point.
+ * @controller: This parameter represents the controller associated with the
+ *    phy.
+ * @port: This parameter specifies the port object for which the user callback
+ *    is being invoked.  There may be conditions where this parameter can be
+ *    SCI_INVALID_HANDLE
+ * @phy: This parameter specifies the phy object for which the user callback is
+ *    being invoked.
+ *
+ * none.
+ */
+void scic_cb_port_link_down(
+	struct scic_sds_controller *controller,
+	struct scic_sds_port *port,
+	struct scic_sds_phy *phy)
+{
+	struct isci_host *isci_host;
+	struct isci_phy *isci_phy;
+	struct isci_port *isci_port;
+
+	isci_host =
+		(struct isci_host *)sci_object_get_association(controller);
+
+	isci_phy =
+		(struct isci_phy *)sci_object_get_association(phy);
+
+	isci_port =
+		(struct isci_port *)sci_object_get_association(port);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_port = %p\n", __func__, isci_port);
+
+	isci_port_link_down(isci_host, isci_phy, isci_port);
+}
+
+/**
+ * scic_cb_remote_device_start_complete() - This user callback method will
+ *    inform the user that a start operation has completed.
+ * @controller: This parameter specifies the core controller associated with
+ *    the completion callback.
+ * @remote_device: This parameter specifies the remote device associated with
+ *    the completion callback.
+ * @completion_status: This parameter specifies the completion status for the
+ *    operation.
+ *
+ */
+void scic_cb_remote_device_start_complete(
+	struct scic_sds_controller *controller,
+	struct scic_sds_remote_device *remote_device,
+	enum sci_status completion_status)
+{
+	struct isci_host *isci_host;
+	struct isci_remote_device *isci_device;
+
+	isci_host =
+		(struct isci_host *)sci_object_get_association(controller);
+
+	isci_device =
+		(struct isci_remote_device *)sci_object_get_association(
+			remote_device
+			);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_device = %p\n", __func__, isci_device);
+
+	isci_remote_device_start_complete(
+		isci_host, isci_device, completion_status);
+
+}
+
+/**
+ * scic_cb_remote_device_stop_complete() - This user callback method will
+ *    inform the user that a stop operation has completed.
+ * @controller: This parameter specifies the core controller associated with
+ *    the completion callback.
+ * @remote_device: This parameter specifies the remote device associated with
+ *    the completion callback.
+ * @completion_status: This parameter specifies the completion status for the
+ *    operation.
+ *
+ */
+void scic_cb_remote_device_stop_complete(
+	struct scic_sds_controller *controller,
+	struct scic_sds_remote_device *remote_device,
+	enum sci_status completion_status)
+{
+	struct isci_host *isci_host;
+	struct isci_remote_device *isci_device;
+
+	isci_host =
+		(struct isci_host *)sci_object_get_association(controller);
+
+	isci_device =
+		(struct isci_remote_device *)sci_object_get_association(
+			remote_device
+			);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_device = %p\n", __func__, isci_device);
+
+	isci_remote_device_stop_complete(
+		isci_host, isci_device, completion_status);
+
+}
+
+/**
+ * scic_cb_remote_device_ready() - This user callback method will inform the
+ *    user that a remote device is now capable of handling IO requests.
+ * @controller: This parameter specifies the core controller associated with
+ *    the completion callback.
+ * @remote_device: This parameter specifies the remote device associated with
+ *    the callback.
+ *
+ */
+void scic_cb_remote_device_ready(
+	struct scic_sds_controller *controller,
+	struct scic_sds_remote_device *remote_device)
+{
+	struct isci_remote_device *isci_device =
+		(struct isci_remote_device *)
+		sci_object_get_association(remote_device);
+
+	dev_dbg(&isci_device->isci_port->isci_host->pdev->dev,
+		"%s: isci_device = %p\n", __func__, isci_device);
+
+	isci_remote_device_ready(isci_device);
+}
+
+/**
+ * scic_cb_remote_device_not_ready() - This user callback method will inform
+ *    the user that a remote device is no longer capable of handling IO
+ *    requests (until a ready callback is invoked).
+ * @controller: This parameter specifies the core controller associated with
+ *    the completion callback.
+ * @remote_device: This parameter specifies the remote device associated with
+ *    the callback.
+ * @reason_code: This parameter specifies the reason for the remote device
+ *    going to a not ready state.
+ *
+ */
+void scic_cb_remote_device_not_ready(
+	struct scic_sds_controller *controller,
+	struct scic_sds_remote_device *remote_device,
+	u32 reason_code)
+{
+	struct isci_remote_device *isci_device =
+		(struct isci_remote_device *)
+		sci_object_get_association(remote_device);
+
+	struct isci_host *isci_host;
+
+	isci_host =
+		(struct isci_host *)sci_object_get_association(controller);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_device = %p, reason_code = %x\n",
+		__func__, isci_device, reason_code);
+
+	isci_remote_device_not_ready(isci_device, reason_code);
+}
+
+
diff --git a/drivers/scsi/isci/timers.c b/drivers/scsi/isci/timers.c
new file mode 100644
index 0000000..ca72308
--- /dev/null
+++ b/drivers/scsi/isci/timers.c
@@ -0,0 +1,319 @@
+/*
+ * 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 "timers.h"
+
+
+/**
+ * isci_timer_list_construct() - This method contrucst the SCI Timer List
+ *    object used by the SCI Module class. The construction process involves
+ *    creating isci_timer objects and adding them to the SCI Timer List
+ *    object's list member. The number of isci_timer objects is determined by
+ *    the timer_list_size parameter.
+ * @isci_timer_list: This parameter points to the SCI Timer List object being
+ *    constructed. The calling routine is responsible for allocating the memory
+ *    for isci_timer_list and initializing the timer list_head member of
+ *    isci_timer_list.
+ * @timer_list_size: This parameter specifies the number of isci_timer objects
+ *    contained by the SCI Timer List. which this timer is to be associated.
+ *
+ * This method returns an error code indicating sucess or failure. The user
+ * should check for possible memory allocation error return otherwise, a zero
+ * indicates success.
+ */
+int isci_timer_list_construct(
+	struct isci_timer_list *isci_timer_list,
+	int timer_list_size)
+{
+	struct isci_timer *isci_timer;
+	int i;
+	int err = 0;
+
+
+	for (i = 0; i < timer_list_size; i++) {
+
+		isci_timer = kzalloc(sizeof(*isci_timer), GFP_KERNEL);
+
+		if (!isci_timer) {
+
+			err = -ENOMEM;
+			break;
+		}
+		isci_timer->used = 0;
+		isci_timer->stopped = 1;
+		isci_timer->parent = isci_timer_list;
+		list_add(&isci_timer->node, &isci_timer_list->timers);
+	}
+
+	return 0;
+
+}
+
+
+/**
+ * isci_timer_list_destroy() - This method destroys the SCI Timer List object
+ *    used by the SCI Module class. The destruction  process involves freeing
+ *    memory allocated for isci_timer objects on the SCI Timer List object's
+ *    timers list_head member. If any isci_timer objects are mark as "in use",
+ *    they are not freed and the function returns an error code of -EBUSY.
+ * @isci_timer_list: This parameter points to the SCI Timer List object being
+ *    destroyed.
+ *
+ * This method returns an error code indicating sucess or failure. The user
+ * should check for possible -EBUSY error return, in the event of one or more
+ * isci_timers still "in use", otherwise, a zero indicates success.
+ */
+int isci_timer_list_destroy(
+	struct isci_timer_list *isci_timer_list)
+{
+	struct isci_timer *timer, *tmp;
+
+	list_for_each_entry_safe(timer, tmp, &isci_timer_list->timers, node) {
+		isci_timer_free(isci_timer_list, timer);
+		list_del(&timer->node);
+		kfree(timer);
+	}
+	return 0;
+}
+
+
+
+static void isci_timer_restart(struct isci_timer *isci_timer)
+{
+	struct timer_list *timer =
+		&isci_timer->timer;
+	unsigned long timeout;
+
+	dev_dbg(&isci_timer->isci_host->pdev->dev,
+		"%s: isci_timer = %p\n", __func__, isci_timer);
+
+	isci_timer->restart = 0;
+	isci_timer->stopped = 0;
+	timeout = isci_timer->timeout_value;
+	timeout = (timeout * HZ) / 1000;
+	timeout = timeout ? timeout : 1;
+	mod_timer(timer, jiffies + timeout);
+}
+
+/**
+ * This method pulls an isci_timer object off of the list for the SCI Timer
+ *    List object specified, marks the isci_timer as "in use" and initializes
+ *    it with user callback function and cookie data. The timer is not start at
+ *    this time, just reserved for the user.
+ * @isci_timer_list: This parameter points to the SCI Timer List from which the
+ *    timer is reserved.
+ * @cookie: This parameter specifies a piece of information that the user must
+ *    retain.  This cookie is to be supplied by the user anytime a timeout
+ *    occurs for the created timer.
+ * @timer_callback: This parameter specifies the callback method to be invoked
+ *    whenever the timer expires.
+ *
+ * This method returns a pointer to an isci_timer object reserved from the SCI
+ * Timer List.  The pointer will be utilized for all further interactions
+ * relating to this timer.
+ */
+
+static void timer_function(unsigned long data)
+{
+
+	struct isci_timer *timer     = (struct isci_timer *)data;
+	struct isci_host *isci_host = timer->isci_host;
+	unsigned long flags;
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_timer = %p\n", __func__, timer);
+
+	if (isci_stopped == isci_host_get_state(isci_host)) {
+		timer->stopped = 1;
+		return;
+	}
+
+	spin_lock_irqsave(&isci_host->scic_lock, flags);
+
+	if (!timer->stopped) {
+		timer->stopped = 1;
+		timer->timer_callback(timer->cookie);
+
+		if (timer->restart)
+			isci_timer_restart(timer);
+	}
+
+	spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+}
+
+
+struct isci_timer *isci_timer_create(
+	struct isci_timer_list *isci_timer_list,
+	struct isci_host *isci_host,
+	void *cookie,
+	void (*timer_callback)(void *))
+{
+
+	struct timer_list *timer;
+	struct isci_timer *isci_timer;
+	struct list_head *timer_list =
+		&isci_timer_list->timers;
+	unsigned long flags;
+
+	spin_lock_irqsave(&isci_host->scic_lock, flags);
+
+	if (list_empty(timer_list)) {
+		spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+		return NULL;
+	}
+
+	isci_timer = list_entry(timer_list->next, struct isci_timer, node);
+
+	if (isci_timer->used) {
+		spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+		return NULL;
+	}
+	isci_timer->used = 1;
+	isci_timer->stopped = 1;
+	list_move_tail(&isci_timer->node, timer_list);
+
+	spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+
+	timer = &isci_timer->timer;
+	timer->data = (unsigned long)isci_timer;
+	timer->function = timer_function;
+	isci_timer->cookie = cookie;
+	isci_timer->timer_callback = timer_callback;
+	isci_timer->isci_host = isci_host;
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_timer = %p\n", __func__, isci_timer);
+
+	return isci_timer;
+}
+
+/**
+ * isci_timer_free() - This method frees the isci_timer, marking it "free to
+ *    use", then places its back at the head of the timers list for the SCI
+ *    Timer List object specified.
+ * @isci_timer_list: This parameter points to the SCI Timer List from which the
+ *    timer is reserved.
+ * @isci_timer: This parameter specifies the timer to be freed.
+ *
+ */
+void isci_timer_free(
+	struct isci_timer_list *isci_timer_list,
+	struct isci_timer *isci_timer)
+{
+	struct list_head *timer_list = &isci_timer_list->timers;
+
+	dev_dbg(&isci_timer->isci_host->pdev->dev,
+		"%s: isci_timer = %p\n", __func__, isci_timer);
+
+	if (list_empty(timer_list))
+		return;
+
+	isci_timer->used = 0;
+	list_move(&isci_timer->node, timer_list);
+
+	if (!isci_timer->stopped) {
+		del_timer(&isci_timer->timer);
+		isci_timer->stopped = 1;
+	}
+}
+
+/**
+ * isci_timer_start() - This method starts the specified isci_timer, with the
+ *    specified timeout value.
+ * @isci_timer: This parameter specifies the timer to be started.
+ * @timeout: This parameter specifies the timeout, in milliseconds, after which
+ *    the associated callback function will be called.
+ *
+ */
+void isci_timer_start(
+	struct isci_timer *isci_timer,
+	unsigned long timeout)
+{
+	struct timer_list *timer = &isci_timer->timer;
+
+	dev_dbg(&isci_timer->isci_host->pdev->dev,
+		"%s: isci_timer = %p\n", __func__, isci_timer);
+
+	isci_timer->timeout_value = timeout;
+	init_timer(timer);
+	timeout = (timeout * HZ) / 1000;
+	timeout = timeout ? timeout : 1;
+
+	timer->expires = jiffies + timeout;
+	timer->data = (unsigned long)isci_timer;
+	timer->function = timer_function;
+	isci_timer->stopped = 0;
+	isci_timer->restart = 0;
+	add_timer(timer);
+}
+
+/**
+ * isci_timer_stop() - This method stops the supplied isci_timer.
+ * @isci_timer: This parameter specifies the isci_timer to be stopped.
+ *
+ */
+void isci_timer_stop(struct isci_timer *isci_timer)
+{
+	dev_dbg(&isci_timer->isci_host->pdev->dev,
+		"%s: isci_timer = %p\n", __func__, isci_timer);
+
+	if (isci_timer->stopped)
+		return;
+
+	isci_timer->stopped = 1;
+
+	del_timer(&isci_timer->timer);
+}
diff --git a/drivers/scsi/isci/timers.h b/drivers/scsi/isci/timers.h
new file mode 100644
index 0000000..ca5c215
--- /dev/null
+++ b/drivers/scsi/isci/timers.h
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+#if !defined(_SCI_TIMER_H_)
+#define _SCI_TIMER_H_
+
+#include <linux/timer.h>
+#include <linux/types.h>
+
+/***************************************************
+ * isci_timer
+ ***************************************************
+ */
+/**
+ * struct isci_timer_list - This class is the container for all isci_timer
+ *    objects
+ *
+ *
+ */
+struct isci_timer_list {
+
+	struct list_head timers;
+};
+
+/**
+ * struct isci_timer - This class represents the timer object used by SCIC. It
+ *    wraps the Linux timer_list object.
+ *
+ *
+ */
+struct isci_timer {
+	int used;
+	int stopped;
+	int restart;
+	int timeout_value;
+	void *cookie;
+	void (*timer_callback)(void *);
+	struct list_head node;
+	struct timer_list timer;
+	struct isci_timer_list *parent;
+	struct isci_host *isci_host;
+};
+
+#define to_isci_timer(t)	\
+	container_of(t, struct isci_timer, timer);
+
+int isci_timer_list_construct(
+	struct isci_timer_list *isci_timer_list,
+	int timer_list_size);
+
+
+int isci_timer_list_destroy(
+	struct isci_timer_list *isci_timer_list);
+
+struct isci_timer *isci_timer_create(
+	struct isci_timer_list *isci_timer_list,
+	struct isci_host *isci_host,
+	void *cookie,
+	void (*timer_callback)(void *));
+
+void isci_timer_free(
+	struct isci_timer_list *isci_timer_list,
+	struct isci_timer *isci_timer);
+
+void isci_timer_start(
+	struct isci_timer *isci_timer,
+	unsigned long timeout);
+
+void isci_timer_stop(
+	struct isci_timer *isci_timer);
+
+
+#endif /* !defined (_SCI_TIMER_H_) */
+


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [RFC PATCH 5/6] isci: phy, port, and remote device
  2011-02-07  0:34 [RFC PATCH 0/6] isci: initial driver release (part1: intro and lldd) Dan Williams
                   ` (3 preceding siblings ...)
  2011-02-07  0:34 ` [RFC PATCH 4/6] isci: hardware / topology event handling Dan Williams
@ 2011-02-07  0:35 ` Dan Williams
  2011-02-07  0:35 ` [RFC PATCH 6/6] isci: sata support and phy settings via request_firmware() Dan Williams
  2011-02-07  7:58 ` [RFC PATCH 1/6] isci: initialization jack_wang
  6 siblings, 0 replies; 36+ messages in thread
From: Dan Williams @ 2011-02-07  0:35 UTC (permalink / raw)
  To: james.bottomley
  Cc: dave.jiang, linux-scsi, jacek.danecki, ed.ciechanowski,
	jeffrey.d.skirvin, edmund.nadolski

Infrastructure for phy initialization, port creation / destruction, and
remote device arrival / departure.

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/scsi/isci/phy.c           |  179 +++++++++
 drivers/scsi/isci/phy.h           |  104 ++++++
 drivers/scsi/isci/port.c          |  484 ++++++++++++++++++++++++++
 drivers/scsi/isci/port.h          |  153 ++++++++
 drivers/scsi/isci/remote_device.c |  698 +++++++++++++++++++++++++++++++++++++
 drivers/scsi/isci/remote_device.h |  154 ++++++++
 6 files changed, 1772 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/isci/phy.c
 create mode 100644 drivers/scsi/isci/phy.h
 create mode 100644 drivers/scsi/isci/port.c
 create mode 100644 drivers/scsi/isci/port.h
 create mode 100644 drivers/scsi/isci/remote_device.c
 create mode 100644 drivers/scsi/isci/remote_device.h

diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c
new file mode 100644
index 0000000..fbda570
--- /dev/null
+++ b/drivers/scsi/isci/phy.c
@@ -0,0 +1,179 @@
+/*
+ * 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 "phy.h"
+#include "scic_port.h"
+#include "scic_config_parameters.h"
+
+
+/**
+ * isci_phy_init() - This function is called by the probe function to
+ *    initialize the phy objects. This func assumes that the isci_port objects
+ *    associated with the SCU have been initialized.
+ * @isci_phy: This parameter specifies the isci_phy object to initialize
+ * @isci_host: This parameter specifies the parent SCU host object for this
+ *    isci_phy
+ * @index: This parameter specifies which SCU phy associates with this
+ *    isci_phy. Generally, SCU phy 0 relates isci_phy 0, etc.
+ *
+ */
+void isci_phy_init(
+	struct isci_phy *phy,
+	struct isci_host *isci_host,
+	int index)
+{
+	struct scic_sds_controller *controller = isci_host->core_controller;
+	struct scic_sds_phy *scic_phy;
+	union scic_oem_parameters oem_parameters;
+	enum sci_status status = SCI_SUCCESS;
+
+	/*--------------- SCU_Phy Initialization Stuff -----------------------*/
+
+	status = scic_controller_get_phy_handle(controller, index, &scic_phy);
+	if (status == SCI_SUCCESS) {
+		sci_object_set_association(scic_phy, (void *)phy);
+		phy->sci_phy_handle = scic_phy;
+	} else
+		dev_err(&isci_host->pdev->dev,
+			"failed scic_controller_get_phy_handle\n");
+
+	scic_oem_parameters_get(controller, &oem_parameters);
+
+	phy->sas_addr[0] =  oem_parameters.sds1.phys[index].sas_address.low
+			   & 0xFF;
+	phy->sas_addr[1] = (oem_parameters.sds1.phys[index].sas_address.low
+			    >> 8)   & 0xFF;
+	phy->sas_addr[2] = (oem_parameters.sds1.phys[index].sas_address.low
+			    >> 16)  & 0xFF;
+	phy->sas_addr[3] = (oem_parameters.sds1.phys[index].sas_address.low
+			    >> 24)  & 0xFF;
+	phy->sas_addr[4] =  oem_parameters.sds1.phys[index].sas_address.high
+			   & 0xFF;
+	phy->sas_addr[5] = (oem_parameters.sds1.phys[index].sas_address.high
+			    >> 8)  & 0xFF;
+	phy->sas_addr[6] = (oem_parameters.sds1.phys[index].sas_address.high
+			    >> 16) & 0xFF;
+	phy->sas_addr[7] = (oem_parameters.sds1.phys[index].sas_address.high
+			    >> 24) & 0xFF;
+
+	phy->isci_port = NULL;
+	phy->sas_phy.enabled = 0;
+	phy->sas_phy.id = index;
+	phy->sas_phy.sas_addr = &phy->sas_addr[0];
+	phy->sas_phy.frame_rcvd = (u8 *)&phy->frame_rcvd;
+	phy->sas_phy.ha = &isci_host->sas_ha;
+	phy->sas_phy.lldd_phy = phy;
+	phy->sas_phy.enabled = 1;
+	phy->sas_phy.class = SAS;
+	phy->sas_phy.iproto = SAS_PROTOCOL_ALL;
+	phy->sas_phy.tproto = 0;
+	phy->sas_phy.type = PHY_TYPE_PHYSICAL;
+	phy->sas_phy.role = PHY_ROLE_INITIATOR;
+	phy->sas_phy.oob_mode = OOB_NOT_CONNECTED;
+	phy->sas_phy.linkrate = SAS_LINK_RATE_UNKNOWN;
+	memset((u8 *)&phy->frame_rcvd, 0, sizeof(phy->frame_rcvd));
+}
+
+
+/**
+ * isci_phy_control() - This function is one of the SAS Domain Template
+ *    functions. This is a phy management function.
+ * @phy: This parameter specifies the sphy being controlled.
+ * @func: This parameter specifies the phy control function being invoked.
+ * @buf: This parameter is specific to the phy function being invoked.
+ *
+ * status, zero indicates success.
+ */
+int isci_phy_control(
+	struct asd_sas_phy *phy,
+	enum phy_func func,
+	void *buf)
+{
+	int ret            = TMF_RESP_FUNC_COMPLETE;
+	struct isci_phy *isci_phy_ptr  = (struct isci_phy *)phy->lldd_phy;
+	struct isci_port *isci_port_ptr = NULL;
+
+	if (isci_phy_ptr != NULL)
+		isci_port_ptr = isci_phy_ptr->isci_port;
+
+	if ((isci_phy_ptr == NULL) || (isci_port_ptr == NULL)) {
+		pr_err("%s: asd_sas_phy %p: lldd_phy %p or "
+		       "isci_port %p == NULL!\n",
+		       __func__, phy, isci_phy_ptr, isci_port_ptr);
+		return TMF_RESP_FUNC_FAILED;
+	}
+
+	pr_debug("%s: phy %p; func %d; buf %p; isci phy %p, port %p\n",
+		 __func__, phy, func, buf, isci_phy_ptr, isci_port_ptr);
+
+	switch (func) {
+	case PHY_FUNC_HARD_RESET:
+	case PHY_FUNC_LINK_RESET:
+
+		/* Perform the port reset. */
+		ret = isci_port_perform_hard_reset(isci_port_ptr, isci_phy_ptr);
+
+		break;
+
+	case PHY_FUNC_DISABLE:
+	default:
+		pr_debug("%s: phy %p; func %d NOT IMPLEMENTED!\n",
+			 __func__, phy, func);
+		ret = TMF_RESP_FUNC_FAILED;
+		break;
+	}
+	return ret;
+}
diff --git a/drivers/scsi/isci/phy.h b/drivers/scsi/isci/phy.h
new file mode 100644
index 0000000..44b727f
--- /dev/null
+++ b/drivers/scsi/isci/phy.h
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+
+#if !defined(_ISCI_PHY_H_)
+#define _ISCI_PHY_H_
+
+#include "port.h"
+#include "host.h"
+#include <scsi/libsas.h>
+
+
+/**
+ * struct isci_phy - This class implements the ISCI specific representation of
+ *    the phy object.
+ *
+ *
+ */
+
+struct isci_phy {
+
+	struct scic_sds_phy *sci_phy_handle;
+
+	struct asd_sas_phy sas_phy;
+	struct sas_identify_frame *frame;
+	struct isci_port *isci_port;
+	u8 sas_addr[SAS_ADDR_SIZE];
+
+	union {
+
+		u8 aif[sizeof(struct sci_sas_identify_address_frame)];
+		u8 fis[sizeof(struct sata_fis_reg_d2h)];
+
+	} frame_rcvd;
+};
+
+#define to_isci_phy(p)	\
+	container_of(p, struct isci_phy, sas_phy);
+
+struct isci_host;
+
+void isci_phy_init(
+	struct isci_phy *phy,
+	struct isci_host *isci_host,
+	int index);
+
+int isci_phy_control(
+	struct asd_sas_phy *phy,
+	enum phy_func func,
+	void *buf);
+
+#endif /* !defined(_ISCI_PHY_H_) */
diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c
new file mode 100644
index 0000000..2343f65
--- /dev/null
+++ b/drivers/scsi/isci/port.c
@@ -0,0 +1,484 @@
+/*
+ * 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.
+ */
+
+/**
+ * This file contains the isci port implementation.
+ *
+ *
+ */
+
+
+#include <linux/workqueue.h>
+#include "isci.h"
+#include "scic_io_request.h"
+#include "scic_remote_device.h"
+#include "scic_phy.h"
+#include "scic_sds_phy.h"
+#include "scic_port.h"
+#include "port.h"
+#include "request.h"
+
+static void isci_port_change_state(
+	struct isci_port *isci_port,
+	enum isci_status status);
+
+
+
+/**
+ * isci_port_init() - This function initializes the given isci_port object.
+ * @isci_port: This parameter specifies the port object to be initialized.
+ * @isci_host: This parameter specifies parent controller object for the port.
+ * @index: This parameter specifies which SCU port the isci_port associates
+ *    with. Generally, SCU port 0 relates to isci_port 0, etc.
+ *
+ */
+void isci_port_init(
+	struct isci_port *isci_port,
+	struct isci_host *isci_host,
+	int index)
+{
+	struct scic_sds_port *scic_port;
+	struct scic_sds_controller *controller = isci_host->core_controller;
+
+	INIT_LIST_HEAD(&isci_port->remote_dev_list);
+	INIT_LIST_HEAD(&isci_port->domain_dev_list);
+	spin_lock_init(&isci_port->remote_device_lock);
+	spin_lock_init(&isci_port->state_lock);
+	init_completion(&isci_port->start_complete);
+	isci_port->isci_host = isci_host;
+	isci_port_change_state(isci_port, isci_freed);
+
+	(void)scic_controller_get_port_handle(controller, index, &scic_port);
+	sci_object_set_association(scic_port, isci_port);
+	isci_port->sci_port_handle = scic_port;
+}
+
+
+/**
+ * isci_port_get_state() - This function gets the status of the port object.
+ * @isci_port: This parameter points to the isci_port object
+ *
+ * status of the object as a isci_status enum.
+ */
+enum isci_status isci_port_get_state(
+	struct isci_port *isci_port)
+{
+	return isci_port->status;
+}
+
+static void isci_port_change_state(
+	struct isci_port *isci_port,
+	enum isci_status status)
+{
+	unsigned long flags;
+
+	dev_dbg(&isci_port->isci_host->pdev->dev,
+		"%s: isci_port = %p, state = 0x%x\n",
+		__func__, isci_port, status);
+
+	spin_lock_irqsave(&isci_port->state_lock, flags);
+	isci_port->status = status;
+	spin_unlock_irqrestore(&isci_port->state_lock, flags);
+}
+
+void isci_port_bc_change_received(
+	struct isci_host *isci_host,
+	struct scic_sds_port *port,
+	struct scic_sds_phy *phy)
+{
+	struct isci_phy *isci_phy =
+		(struct isci_phy *)sci_object_get_association(phy);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_phy = %p, sas_phy = %p\n",
+		__func__,
+		isci_phy,
+		&isci_phy->sas_phy);
+
+	isci_host->sas_ha.notify_port_event(
+		&isci_phy->sas_phy,
+		PORTE_BROADCAST_RCVD
+		);
+
+	scic_port_enable_broadcast_change_notification(port);
+}
+
+/**
+ * isci_port_link_up() - This function is called by the sci core when a link
+ *    becomes active. the identify address frame is retrieved from the core and
+ *    a notify port event is sent to libsas.
+ * @isci_host: This parameter specifies the isci host object.
+ * @port: This parameter specifies the sci port with the active link.
+ * @phy: This parameter specifies the sci phy with the active link.
+ *
+ */
+void isci_port_link_up(
+	struct isci_host *isci_host,
+	struct scic_sds_port *port,
+	struct scic_sds_phy *phy)
+{
+	unsigned long flags;
+	struct scic_port_properties properties;
+	struct isci_phy *isci_phy
+		= (struct isci_phy *)sci_object_get_association(phy);
+	struct isci_port *isci_port
+		= (struct isci_port *)sci_object_get_association(port);
+	enum sci_status call_status;
+	unsigned long success = true;
+
+	BUG_ON(isci_phy->isci_port != NULL);
+	isci_phy->isci_port = isci_port;
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_port = %p\n",
+		__func__, isci_port);
+
+	spin_lock_irqsave(&isci_phy->sas_phy.frame_rcvd_lock, flags);
+
+	isci_port_change_state(isci_phy->isci_port, isci_starting);
+
+	scic_port_get_properties(port, &properties);
+
+	if (properties.remote.protocols.u.bits.stp_target) {
+
+		struct scic_sata_phy_properties sata_phy_properties;
+
+		isci_phy->sas_phy.oob_mode = SATA_OOB_MODE;
+
+		/* Get a copy of the signature fis for libsas */
+		call_status = scic_sata_phy_get_properties(phy,
+							   &sata_phy_properties);
+
+		/* 
+		 * XXX I am concerned about this "assert". shouldn't we
+		 * handle the return appropriately?
+		 */
+		BUG_ON(call_status != SCI_SUCCESS);
+
+		memcpy(isci_phy->frame_rcvd.fis,
+		       &sata_phy_properties.signature_fis,
+		       sizeof(struct sata_fis_reg_d2h));
+
+		isci_phy->sas_phy.frame_rcvd_size = sizeof(struct sata_fis_reg_d2h);
+
+		/*
+		 * For direct-attached SATA devices, the SCI core will
+		 * automagically assign a SAS address to the end device
+		 * for the purpose of creating a port. This SAS address
+		 * will not be the same as assigned to the PHY and needs
+		 * to be obtained from struct scic_port_properties properties.
+		 */
+
+		BUG_ON(((size_t)SAS_ADDR_SIZE / 2)
+		       != sizeof(properties.remote.sas_address.low));
+
+		memcpy(&isci_phy->sas_phy.attached_sas_addr[0],
+		       &properties.remote.sas_address.low,
+		       SAS_ADDR_SIZE / 2);
+
+		memcpy(&isci_phy->sas_phy.attached_sas_addr[4],
+		       &properties.remote.sas_address.high,
+		       SAS_ADDR_SIZE / 2);
+
+	} else if (properties.remote.protocols.u.bits.ssp_target ||
+		   properties.remote.protocols.u.bits.smp_target) {
+
+		struct scic_sas_phy_properties sas_phy_properties;
+
+		isci_phy->sas_phy.oob_mode = SAS_OOB_MODE;
+
+		/* Get a copy of the identify address frame for libsas */
+		call_status = scic_sas_phy_get_properties(phy,
+							  &sas_phy_properties);
+
+		BUG_ON(call_status != SCI_SUCCESS);
+
+		memcpy(isci_phy->frame_rcvd.aif,
+		       &(sas_phy_properties.received_iaf),
+		       sizeof(struct sci_sas_identify_address_frame));
+
+		isci_phy->sas_phy.frame_rcvd_size
+			= sizeof(struct sci_sas_identify_address_frame);
+
+		/* Copy the attached SAS address from the IAF */
+		memcpy(isci_phy->sas_phy.attached_sas_addr,
+		       ((struct sas_identify_frame *)
+			(&isci_phy->frame_rcvd.aif))->sas_addr,
+		       SAS_ADDR_SIZE);
+
+	} else {
+		dev_err(&isci_host->pdev->dev, "%s: unkown target\n", __func__);
+		success = false;
+	}
+
+	spin_unlock_irqrestore(&isci_phy->sas_phy.frame_rcvd_lock, flags);
+
+	/* Notify libsas that we have an address frame, if indeed
+	 * we've found an SSP, SMP, or STP target */
+	if (success)
+		isci_host->sas_ha.notify_port_event(&isci_phy->sas_phy,
+						    PORTE_BYTES_DMAED);
+}
+
+
+/**
+ * isci_port_link_down() - This function is called by the sci core when a link
+ *    becomes inactive.
+ * @isci_host: This parameter specifies the isci host object.
+ * @phy: This parameter specifies the isci phy with the active link.
+ * @port: This parameter specifies the isci port with the active link.
+ *
+ */
+void isci_port_link_down(
+	struct isci_host *isci_host,
+	struct isci_phy *isci_phy,
+	struct isci_port *isci_port)
+{
+	struct isci_remote_device *isci_device;
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_port = %p\n", __func__, isci_port);
+
+	if (isci_port) {
+
+		/* check to see if this is the last phy on this port. */
+		if (isci_phy->sas_phy.port
+		    && isci_phy->sas_phy.port->num_phys == 1) {
+
+			/* change the state for all devices on this port.
+			 * The next task sent to this device will be returned
+			 * as SAS_TASK_UNDELIVERED, and the scsi mid layer
+			 * will remove the target
+			 */
+			list_for_each_entry(isci_device,
+					    &isci_port->remote_dev_list,
+					    node) {
+				dev_dbg(&isci_host->pdev->dev,
+					"%s: isci_device = %p\n",
+					__func__, isci_device);
+				isci_remote_device_change_state(isci_device,
+								isci_stopping);
+			}
+		}
+		isci_port_change_state(isci_port, isci_stopping);
+	}
+
+	/* Notify libsas of the borken link, this will trigger calls to our
+	 * isci_port_deformed and isci_dev_gone functions.
+	 */
+	sas_phy_disconnected(&isci_phy->sas_phy);
+	isci_host->sas_ha.notify_phy_event(&isci_phy->sas_phy,
+					   PHYE_LOSS_OF_SIGNAL);
+
+	isci_phy->isci_port = NULL;
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_port = %p - Done\n", __func__, isci_port);
+}
+
+
+/**
+ * isci_port_deformed() - This function is called by libsas when a port becomes
+ *    inactive.
+ * @phy: This parameter specifies the libsas phy with the inactive port.
+ *
+ */
+void isci_port_deformed(
+	struct asd_sas_phy *phy)
+{
+	pr_debug("%s: sas_phy = %p\n", __func__, phy);
+}
+
+/**
+ * isci_port_formed() - This function is called by libsas when a port becomes
+ *    active.
+ * @phy: This parameter specifies the libsas phy with the active port.
+ *
+ */
+void isci_port_formed(
+	struct asd_sas_phy *phy)
+{
+	pr_debug("%s: sas_phy = %p, sas_port = %p\n", __func__, phy, phy->port);
+}
+
+/**
+ * isci_port_ready() - This function is called by the sci core when a link
+ *    becomes ready.
+ * @isci_host: This parameter specifies the isci host object.
+ * @port: This parameter specifies the sci port with the active link.
+ *
+ */
+void isci_port_ready(
+	struct isci_host *isci_host,
+	struct isci_port *isci_port)
+{
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_port = %p\n", __func__, isci_port);
+
+	complete_all(&isci_port->start_complete);
+	isci_port_change_state(isci_port, isci_ready);
+	return;
+}
+
+/**
+ * isci_port_not_ready() - This function is called by the sci core when a link
+ *    is not ready. All remote devices on this link will be removed if they are
+ *    in the stopping state.
+ * @isci_host: This parameter specifies the isci host object.
+ * @port: This parameter specifies the sci port with the active link.
+ *
+ */
+void isci_port_not_ready(
+	struct isci_host *isci_host,
+	struct isci_port *isci_port)
+{
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_port = %p\n", __func__, isci_port);
+}
+
+/**
+ * isci_port_hard_reset_complete() - This function is called by the sci core
+ *    when the hard reset complete notification has been received.
+ * @port: This parameter specifies the sci port with the active link.
+ * @completion_status: This parameter specifies the core status for the reset
+ *    process.
+ *
+ */
+void isci_port_hard_reset_complete(
+	struct isci_port *isci_port,
+	enum sci_status completion_status)
+{
+	dev_dbg(&isci_port->isci_host->pdev->dev,
+		"%s: isci_port = %p, completion_status=%x\n",
+		     __func__, isci_port, completion_status);
+
+	/* Save the status of the hard reset from the port. */
+	isci_port->hard_reset_status = completion_status;
+
+	complete_all(&isci_port->hard_reset_complete);
+}
+/**
+ * isci_port_perform_hard_reset() - This function is one of the SAS Domain
+ *    Template functions. This is a phy management function.
+ * @isci_port:
+ * @isci_phy:
+ *
+ * status, TMF_RESP_FUNC_COMPLETE indicates success.
+ */
+int isci_port_perform_hard_reset(
+	struct isci_port *isci_port,
+	struct isci_phy *isci_phy)
+{
+	enum sci_status status;
+	int ret = TMF_RESP_FUNC_COMPLETE;
+	unsigned long flags;
+
+
+	dev_dbg(&isci_port->isci_host->pdev->dev,
+		"%s: isci_port = %p\n",
+		__func__, isci_port);
+
+	BUG_ON(isci_port == NULL);
+
+	init_completion(&isci_port->hard_reset_complete);
+
+	spin_lock_irqsave(&isci_port->isci_host->scic_lock, flags);
+
+	#define ISCI_PORT_RESET_TIMEOUT SCIC_SDS_SIGNATURE_FIS_TIMEOUT
+	status = scic_port_hard_reset(isci_port->sci_port_handle,
+				      ISCI_PORT_RESET_TIMEOUT);
+
+	spin_unlock_irqrestore(&isci_port->isci_host->scic_lock, flags);
+
+	if (status == SCI_SUCCESS) {
+		wait_for_completion(&isci_port->hard_reset_complete);
+
+		dev_dbg(&isci_port->isci_host->pdev->dev,
+			"%s: isci_port = %p; hard reset completion\n",
+			__func__, isci_port);
+
+		if (isci_port->hard_reset_status != SCI_SUCCESS)
+			ret = TMF_RESP_FUNC_FAILED;
+	} else {
+		ret = TMF_RESP_FUNC_FAILED;
+
+		dev_err(&isci_port->isci_host->pdev->dev,
+			"%s: isci_port = %p; scic_port_hard_reset call"
+			" failed 0x%x\n",
+			__func__, isci_port, status);
+
+	}
+
+	/* If the hard reset for the port has failed, consider this
+	 * the same as link failures on all phys in the port.
+	 */
+	if (ret != TMF_RESP_FUNC_COMPLETE) {
+		BUG_ON(isci_port->isci_host == NULL);
+
+		dev_err(&isci_port->isci_host->pdev->dev,
+			"%s: isci_port = %p; hard reset failed "
+			"(0x%x) - sending link down to libsas for phy %p\n",
+			__func__,
+			isci_port,
+			isci_port->hard_reset_status,
+			isci_phy);
+
+		isci_port_link_down(isci_port->isci_host,
+				    isci_phy,
+				    isci_port);
+	}
+
+	return ret;
+}
diff --git a/drivers/scsi/isci/port.h b/drivers/scsi/isci/port.h
new file mode 100644
index 0000000..b01b0c6
--- /dev/null
+++ b/drivers/scsi/isci/port.h
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+/**
+ * This file contains the isci_port object definition.
+ *
+ * port.h
+ */
+
+#if !defined(_ISCI_PORT_H_)
+#define _ISCI_PORT_H_
+
+struct isci_phy;
+struct isci_host;
+
+
+enum isci_status {
+	isci_freed        = 0x00,
+	isci_starting     = 0x01,
+	isci_ready        = 0x02,
+	isci_ready_for_io = 0x03,
+	isci_stopping     = 0x04,
+	isci_stopped      = 0x05,
+	isci_host_quiesce = 0x06
+};
+
+/**
+ * struct isci_port - This class represents the port object used to internally
+ *    represent libsas port objects. It also keeps a list of remote device
+ *    objects.
+ *
+ *
+ */
+struct isci_port {
+
+	struct scic_sds_port *sci_port_handle;
+
+	enum isci_status status;
+	struct isci_host *isci_host;
+	struct asd_sas_port sas_port;
+	struct list_head remote_dev_list;
+	spinlock_t remote_device_lock;
+	spinlock_t state_lock;
+	struct list_head domain_dev_list;
+	struct completion start_complete;
+	struct completion hard_reset_complete;
+	enum sci_status hard_reset_status;
+};
+
+#define to_isci_port(p)	\
+	container_of(p, struct isci_port, sas_port);
+
+enum isci_status isci_port_get_state(
+	struct isci_port *isci_port);
+
+
+
+void isci_port_formed(
+	struct asd_sas_phy *);
+
+void isci_port_deformed(
+	struct asd_sas_phy *);
+
+void isci_port_bc_change_received(
+	struct isci_host *isci_host,
+	struct scic_sds_port *port,
+	struct scic_sds_phy *phy);
+
+void isci_port_link_up(
+	struct isci_host *isci_host,
+	struct scic_sds_port *port,
+	struct scic_sds_phy *phy);
+
+void isci_port_link_down(
+	struct isci_host *isci_host,
+	struct isci_phy *isci_phy,
+	struct isci_port *port);
+
+void isci_port_ready(
+	struct isci_host *isci_host,
+	struct isci_port *isci_port);
+
+void isci_port_not_ready(
+	struct isci_host *isci_host,
+	struct isci_port *port);
+
+void isci_port_init(
+	struct isci_port *port,
+	struct isci_host *host,
+	int index);
+
+void isci_port_hard_reset_complete(
+	struct isci_port *isci_port,
+	enum sci_status completion_status);
+
+int isci_port_perform_hard_reset(
+	struct isci_port *isci_port_ptr,
+	struct isci_phy *isci_phy_ptr);
+
+#endif /* !defined(_ISCI_PORT_H_) */
+
diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c
new file mode 100644
index 0000000..dbf3c82
--- /dev/null
+++ b/drivers/scsi/isci/remote_device.c
@@ -0,0 +1,698 @@
+/*
+ * 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_io_request.h"
+#include "scic_remote_device.h"
+#include "scic_phy.h"
+#include "scic_port.h"
+#include "port.h"
+#include "remote_device.h"
+#include "request.h"
+#include "task.h"
+
+
+
+/**
+ * isci_remote_device_deconstruct() - This function frees an isci_remote_device.
+ * @isci_host: This parameter specifies the isci host object.
+ * @isci_device: This parameter specifies the remote device to be freed.
+ *
+ */
+static void isci_remote_device_deconstruct(
+	struct isci_host *isci_host,
+	struct isci_remote_device *isci_device)
+{
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_device = %p\n", __func__, isci_device);
+
+	/* There should not be any outstanding io's. All paths to
+	 * here should go through isci_remote_device_nuke_requests.
+	 * If we hit this condition, we will need a way to complete
+	 * io requests in process */
+	while (!list_empty(&isci_device->reqs_in_process)) {
+
+		dev_err(&isci_host->pdev->dev,
+			"%s: ** request list not empty! **\n", __func__);
+		BUG();
+	}
+
+	/* Remove all related references to this device and free
+	 * the cache object.
+	 */
+	scic_remote_device_destruct(isci_device->sci_device_handle);
+	isci_device->domain_dev->lldd_dev = NULL;
+	list_del(&isci_device->node);
+	kmem_cache_free(isci_kmem_cache, isci_device);
+}
+
+
+/**
+ * isci_remote_device_construct() - This function calls the scic remote device
+ *    construct and start functions, it waits on the remote device start
+ *    completion.
+ * @port: This parameter specifies the isci port with the remote device.
+ * @isci_device: This parameter specifies the isci remote device
+ *
+ * status from the scic calls, the caller to this function should clean up
+ * resources as appropriate.
+ */
+static enum sci_status isci_remote_device_construct(
+	struct isci_port *port,
+	struct isci_remote_device *isci_device)
+{
+	enum sci_status status = SCI_SUCCESS;
+
+	/* let the core do it's common constuction. */
+	scic_remote_device_construct(port->sci_port_handle,
+				     isci_device->sci_device_handle);
+
+	/* let the core do it's device specific constuction. */
+	if (isci_device->domain_dev->parent &&
+	    (isci_device->domain_dev->parent->dev_type == EDGE_DEV)) {
+		int i;
+
+		/* struct smp_response_discover discover_response; */
+		struct discover_resp discover_response;
+		struct domain_device *parent =
+			isci_device->domain_dev->parent;
+
+		struct expander_device *parent_ex = &parent->ex_dev;
+
+		for (i = 0; i < parent_ex->num_phys; i++) {
+
+			struct ex_phy *phy = &parent_ex->ex_phy[i];
+
+			if ((phy->phy_state == PHY_VACANT) ||
+			    (phy->phy_state == PHY_NOT_PRESENT))
+				continue;
+
+			if (SAS_ADDR(phy->attached_sas_addr)
+			    == SAS_ADDR(isci_device->domain_dev->sas_addr)) {
+
+				discover_response.attached_dev_type
+					= phy->attached_dev_type;
+				discover_response.linkrate
+					= phy->linkrate;
+				discover_response.attached_sata_host
+					= phy->attached_sata_host;
+				discover_response.attached_sata_dev
+					= phy->attached_sata_dev;
+				discover_response.attached_sata_ps
+					= phy->attached_sata_ps;
+				discover_response.iproto
+					= phy->attached_iproto >> 1;
+				discover_response.tproto
+					= phy->attached_tproto >> 1;
+				memcpy(
+					discover_response.attached_sas_addr,
+					phy->attached_sas_addr,
+					SAS_ADDR_SIZE
+					);
+				discover_response.attached_phy_id
+					= phy->attached_phy_id;
+				discover_response.change_count
+					= phy->phy_change_count;
+				discover_response.routing_attr
+					= phy->routing_attr;
+				discover_response.hmin_linkrate
+					= phy->phy->minimum_linkrate_hw;
+				discover_response.hmax_linkrate
+					= phy->phy->maximum_linkrate_hw;
+				discover_response.pmin_linkrate
+					= phy->phy->minimum_linkrate;
+				discover_response.pmax_linkrate
+					= phy->phy->maximum_linkrate;
+			}
+		}
+
+
+		dev_dbg(&port->isci_host->pdev->dev,
+			"%s: parent->dev_type = EDGE_DEV\n",
+			__func__);
+
+		status = scic_remote_device_ea_construct(
+			isci_device->sci_device_handle,
+			(struct smp_response_discover *)&discover_response
+			);
+
+	} else
+		status = scic_remote_device_da_construct(
+			isci_device->sci_device_handle
+			);
+
+
+	if (status != SCI_SUCCESS) {
+		dev_dbg(&port->isci_host->pdev->dev,
+			"%s: scic_remote_device_da_construct failed - "
+			"isci_device = %p\n",
+			__func__,
+			isci_device);
+
+		return status;
+	}
+
+	sci_object_set_association(
+		isci_device->sci_device_handle,
+		isci_device
+		);
+
+	BUG_ON(port->isci_host == NULL);
+
+	/* start the device. */
+	status = scic_remote_device_start(
+		isci_device->sci_device_handle,
+		ISCI_REMOTE_DEVICE_START_TIMEOUT
+		);
+
+	if (status != SCI_SUCCESS) {
+		dev_warn(&port->isci_host->pdev->dev,
+			 "%s: scic_remote_device_start failed\n",
+			 __func__);
+		return status;
+	}
+
+	return status;
+}
+
+
+/**
+ * isci_remote_device_nuke_requests() - This function terminates all requests
+ *    for a given remote device.
+ * @isci_device: This parameter specifies the remote device
+ *
+ */
+void isci_remote_device_nuke_requests(
+	struct isci_remote_device *isci_device)
+{
+	DECLARE_COMPLETION_ONSTACK(aborted_task_completion);
+	struct isci_host *isci_host;
+
+	isci_host = isci_device->isci_port->isci_host;
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_device = %p\n", __func__, isci_device);
+
+	/* Cleanup all requests pending for this device. */
+	isci_terminate_pending_requests(isci_host, isci_device, terminating);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_device = %p, done\n", __func__, isci_device);
+}
+
+
+
+/**
+ * This function builds the isci_remote_device when a libsas dev_found message
+ *    is received.
+ * @isci_host: This parameter specifies the isci host object.
+ * @port: This parameter specifies the isci_port conected to this device.
+ *
+ * pointer to new isci_remote_device.
+ */
+static struct isci_remote_device *
+isci_remote_device_alloc(struct isci_host *isci_host, struct isci_port *port)
+{
+	struct isci_remote_device *isci_device;
+	struct scic_sds_remote_device *sci_dev;
+
+	isci_device = kmem_cache_zalloc(isci_kmem_cache, GFP_KERNEL);
+
+	if (!isci_device) {
+		dev_warn(&isci_host->pdev->dev, "%s: failed\n", __func__);
+		return NULL;
+	}
+
+	sci_dev = (struct scic_sds_remote_device *) &isci_device[1];
+	isci_device->sci_device_handle = sci_dev;
+	INIT_LIST_HEAD(&isci_device->reqs_in_process);
+	INIT_LIST_HEAD(&isci_device->node);
+	isci_device->host_quiesce          = false;
+
+	spin_lock_init(&isci_device->state_lock);
+	spin_lock_init(&isci_device->host_quiesce_lock);
+	isci_remote_device_change_state(isci_device, isci_freed);
+
+	return isci_device;
+
+}
+/**
+ * isci_device_set_host_quiesce_lock_state() - This function sets the host I/O
+ *    quiesce lock state for the remote_device object.
+ * @isci_device,: This parameter points to the isci_remote_device object
+ * @isci_device: This parameter specifies the new quiesce state.
+ *
+ */
+void isci_device_set_host_quiesce_lock_state(
+	struct isci_remote_device *isci_device,
+	bool lock_state)
+{
+	unsigned long flags;
+
+	dev_dbg(&isci_device->isci_port->isci_host->pdev->dev,
+		"%s: isci_device=%p, lock_state=%d\n",
+		__func__, isci_device, lock_state);
+
+	spin_lock_irqsave(&isci_device->host_quiesce_lock, flags);
+	isci_device->host_quiesce = lock_state;
+	spin_unlock_irqrestore(&isci_device->host_quiesce_lock, flags);
+}
+
+/**
+ * isci_remote_device_ready() - This function is called by the scic when the
+ *    remote device is ready. We mark the isci device as ready and signal the
+ *    waiting proccess.
+ * @isci_host: This parameter specifies the isci host object.
+ * @isci_device: This parameter specifies the remote device
+ *
+ */
+void isci_remote_device_ready(struct isci_remote_device *isci_device)
+{
+	unsigned long flags;
+
+	dev_dbg(&isci_device->isci_port->isci_host->pdev->dev,
+		"%s: isci_device = %p\n", __func__, isci_device);
+
+	/* device ready is actually a "ready for io" state. */
+	if ((isci_starting == isci_remote_device_get_state(isci_device)) ||
+	    (isci_ready == isci_remote_device_get_state(isci_device))) {
+		spin_lock_irqsave(&isci_device->isci_port->remote_device_lock,
+				  flags);
+		isci_remote_device_change_state(isci_device, isci_ready_for_io);
+		if (isci_device->completion)
+			complete(isci_device->completion);
+		spin_unlock_irqrestore(
+				&isci_device->isci_port->remote_device_lock,
+				flags);
+	}
+
+}
+
+/**
+ * isci_remote_device_not_ready() - This function is called by the scic when
+ *    the remote device is not ready. We mark the isci device as ready (not
+ *    "ready_for_io") and signal the waiting proccess.
+ * @isci_host: This parameter specifies the isci host object.
+ * @isci_device: This parameter specifies the remote device
+ *
+ */
+void isci_remote_device_not_ready(
+	struct isci_remote_device *isci_device,
+	u32 reason_code)
+{
+	dev_dbg(&isci_device->isci_port->isci_host->pdev->dev,
+		"%s: isci_device = %p\n", __func__, isci_device);
+
+	if (reason_code == SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED)
+		isci_remote_device_change_state(isci_device, isci_stopping);
+	else
+		/* device ready is actually a "not ready for io" state. */
+		isci_remote_device_change_state(isci_device, isci_ready);
+}
+
+/**
+ * isci_remote_device_stop_complete() - This function is called by the scic
+ *    when the remote device stop has completed. We mark the isci device as not
+ *    ready and remove the isci remote device.
+ * @isci_host: This parameter specifies the isci host object.
+ * @isci_device: This parameter specifies the remote device.
+ * @status: This parameter specifies status of the completion.
+ *
+ */
+void isci_remote_device_stop_complete(
+	struct isci_host *isci_host,
+	struct isci_remote_device *isci_device,
+	enum sci_status status)
+{
+	struct completion *completion = isci_device->completion;
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: complete isci_device = %p, status = 0x%x\n",
+		__func__,
+		isci_device,
+		status);
+
+	isci_remote_device_change_state(isci_device, isci_stopped);
+
+	/* after stop, we can tear down resources. */
+	isci_remote_device_deconstruct(isci_host, isci_device);
+
+	/* notify interested parties. */
+	if (completion)
+		complete(completion);
+}
+
+/**
+ * isci_remote_device_start_complete() - This function is called by the scic
+ *    when the remote device start has completed
+ * @isci_host: This parameter specifies the isci host object.
+ * @isci_device: This parameter specifies the remote device.
+ * @status: This parameter specifies status of the completion.
+ *
+ */
+void isci_remote_device_start_complete(
+	struct isci_host *isci_host,
+	struct isci_remote_device *isci_device,
+	enum sci_status status)
+{
+
+
+}
+
+
+/**
+ * isci_remote_device_stop() - This function is called internally to stop the
+ *    remote device.
+ * @isci_host: This parameter specifies the isci host object.
+ * @isci_device: This parameter specifies the remote device.
+ *
+ * The status of the scic request to stop.
+ */
+enum sci_status isci_remote_device_stop(
+	struct isci_remote_device *isci_device)
+{
+	enum sci_status status;
+	unsigned long flags;
+
+	DECLARE_COMPLETION_ONSTACK(completion);
+
+	dev_dbg(&isci_device->isci_port->isci_host->pdev->dev,
+		"%s: isci_device = %p\n", __func__, isci_device);
+
+	isci_remote_device_change_state(isci_device, isci_stopping);
+
+	/* We need comfirmation that stop completed. */
+	isci_device->completion = &completion;
+
+	BUG_ON(isci_device->isci_port == NULL);
+	BUG_ON(isci_device->isci_port->isci_host == NULL);
+
+	spin_lock_irqsave(&isci_device->isci_port->isci_host->scic_lock, flags);
+
+	status = scic_remote_device_stop(
+		isci_device->sci_device_handle,
+		50
+		);
+
+	spin_unlock_irqrestore(&isci_device->isci_port->isci_host->scic_lock, flags);
+
+	/* Wait for the stop complete callback. */
+	if (status == SCI_SUCCESS)
+		wait_for_completion(&completion);
+
+	dev_dbg(&isci_device->isci_port->isci_host->pdev->dev,
+		"%s: isci_device = %p - after completion wait\n",
+		__func__, isci_device);
+
+	isci_device->completion = NULL;
+	return status;
+}
+
+/**
+ * isci_remote_device_gone() - This function is called by libsas when a domain
+ *    device is removed.
+ * @domain_device: This parameter specifies the libsas domain device.
+ *
+ */
+void isci_remote_device_gone(
+	struct domain_device *domain_dev)
+{
+	struct isci_remote_device *isci_device = isci_dev_from_domain_dev(
+		domain_dev);
+
+	dev_err(&isci_device->isci_port->isci_host->pdev->dev,
+		"%s: domain_device = %p, isci_device = %p, isci_port = %p\n",
+		__func__, domain_dev, isci_device, isci_device->isci_port);
+
+	if (isci_device != NULL)
+		isci_remote_device_stop(isci_device);
+}
+
+
+/**
+ * isci_remote_device_found() - This function is called by libsas when a remote
+ *    device is discovered. A remote device object is created and started. the
+ *    function then sleeps until the sci core device started message is
+ *    received.
+ * @domain_device: This parameter specifies the libsas domain device.
+ *
+ * status, zero indicates success.
+ */
+int isci_remote_device_found(struct domain_device *domain_dev)
+{
+	unsigned long flags;
+	struct isci_host *isci_host;
+	struct isci_port *isci_port;
+	struct isci_phy *isci_phy;
+	struct asd_sas_port *sas_port;
+	struct asd_sas_phy *sas_phy;
+	struct isci_remote_device *isci_device;
+	enum sci_status status;
+	DECLARE_COMPLETION_ONSTACK(completion);
+
+	isci_host = isci_host_from_sas_ha(domain_dev->port->ha);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: domain_device = %p\n", __func__, domain_dev);
+
+	sas_port = domain_dev->port;
+	sas_phy = list_first_entry(&sas_port->phy_list, struct asd_sas_phy,
+				   port_phy_el);
+	isci_phy = to_isci_phy(sas_phy);
+	isci_port = isci_phy->isci_port;
+
+	/* we are being called for a device on this port,
+	 * so it has to come up eventually
+	 */
+	wait_for_completion(&isci_port->start_complete);
+
+	if ((isci_stopping == isci_port_get_state(isci_port)) ||
+	    (isci_stopped == isci_port_get_state(isci_port)))
+		return -ENODEV;
+
+	isci_device = isci_remote_device_alloc(isci_host, isci_port);
+
+	INIT_LIST_HEAD(&isci_device->node);
+	domain_dev->lldd_dev = isci_device;
+	isci_device->domain_dev = domain_dev;
+	isci_device->isci_port = isci_port;
+	isci_remote_device_change_state(isci_device, isci_starting);
+
+
+	spin_lock_irqsave(&isci_port->remote_device_lock, flags);
+	list_add_tail(&isci_device->node, &isci_port->remote_dev_list);
+
+	/* for the device ready event. */
+	isci_device->completion = &completion;
+
+	status = isci_remote_device_construct(isci_port, isci_device);
+
+	spin_unlock_irqrestore(&isci_port->remote_device_lock, flags);
+
+	/* wait for the device ready callback. */
+	wait_for_completion(isci_device->completion);
+	isci_device->completion = NULL;
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_device = %p\n",
+		__func__, isci_device);
+
+	if (status != SCI_SUCCESS) {
+
+		spin_lock_irqsave(&isci_port->remote_device_lock, flags);
+		isci_remote_device_deconstruct(
+			isci_host,
+			isci_device
+			);
+		spin_unlock_irqrestore(&isci_port->remote_device_lock, flags);
+		return -ENODEV;
+	}
+
+	wait_for_completion(&isci_host->start_complete);
+
+	return 0;
+}
+/**
+ * isci_device_is_reset_pending() - This function will check if there is any
+ *    pending reset condition on the device.
+ * @request: This parameter is the isci_device object.
+ *
+ * true if there is a reset pending for the device.
+ */
+bool isci_device_is_reset_pending(
+	struct isci_host *isci_host,
+	struct isci_remote_device *isci_device)
+{
+	struct isci_request *isci_request;
+	struct isci_request *tmp_req;
+	bool reset_is_pending = false;
+	unsigned long flags;
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_device = %p\n", __func__, isci_device);
+
+	spin_lock_irqsave(&isci_host->scic_lock, flags);
+
+	/* Check for reset on all pending requests. */
+	list_for_each_entry_safe(isci_request, tmp_req,
+				 &isci_device->reqs_in_process, dev_node) {
+		dev_dbg(&isci_host->pdev->dev,
+			"%s: isci_device = %p request = %p\n",
+			__func__, isci_device, isci_request);
+
+		if (isci_request->ttype == io_task) {
+
+			unsigned long flags;
+			struct sas_task *task = isci_request_access_task(
+				isci_request);
+
+			spin_lock_irqsave(&task->task_state_lock, flags);
+			if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET)
+				reset_is_pending = true;
+			spin_unlock_irqrestore(&task->task_state_lock, flags);
+		}
+	}
+
+	spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+
+	dev_dbg(&isci_host->pdev->dev,
+		"%s: isci_device = %p reset_is_pending = %d\n",
+		__func__, isci_device, reset_is_pending);
+
+	return reset_is_pending;
+}
+
+/**
+ * isci_device_clear_reset_pending() - This function will clear if any pending
+ *    reset condition flags on the device.
+ * @request: This parameter is the isci_device object.
+ *
+ * true if there is a reset pending for the device.
+ */
+void isci_device_clear_reset_pending(struct isci_remote_device *isci_device)
+{
+	struct isci_request *isci_request;
+	struct isci_request *tmp_req;
+	struct isci_host *isci_host = NULL;
+	unsigned long flags = 0;
+
+	/* FIXME more port gone confusion, and this time it makes the
+	 * locking "fun"
+	 */
+	if (isci_device->isci_port != NULL)
+		isci_host = isci_device->isci_port->isci_host;
+
+	/*
+	 * FIXME when the isci_host gets sorted out
+	 * use dev_dbg()
+	 */
+	pr_debug("%s: isci_device=%p, isci_host=%p\n",
+		 __func__, isci_device, isci_host);
+
+	if (isci_host != NULL)
+		spin_lock_irqsave(&isci_host->scic_lock, flags);
+	else
+		pr_err("%s: isci_device %p; isci_host == NULL!\n",
+		       __func__, isci_device);
+
+	/* Clear reset pending on all pending requests. */
+	list_for_each_entry_safe(isci_request, tmp_req,
+				 &isci_device->reqs_in_process, dev_node) {
+		/*
+		 * FIXME when the conditional spinlock is gone
+		 * change to dev_dbg()
+		 */
+		pr_debug("%s: isci_device = %p request = %p\n",
+			 __func__, isci_device, isci_request);
+
+		if (isci_request->ttype == io_task) {
+
+			unsigned long flags2;
+			struct sas_task *task = isci_request_access_task(
+				isci_request);
+
+			spin_lock_irqsave(&task->task_state_lock, flags2);
+			task->task_state_flags &= ~SAS_TASK_NEED_DEV_RESET;
+			spin_unlock_irqrestore(&task->task_state_lock, flags2);
+		}
+	}
+
+	if (isci_host != NULL)
+		spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+}
+
+/**
+ * isci_remote_device_change_state() - This function gets the status of the
+ *    remote_device object.
+ * @isci_device: This parameter points to the isci_remote_device object
+ *
+ * status of the object as a isci_status enum.
+ */
+void isci_remote_device_change_state(
+	struct isci_remote_device *isci_device,
+	enum isci_status status)
+{
+	unsigned long flags;
+
+	dev_dbg(&isci_device->isci_port->isci_host->pdev->dev,
+		"%s: isci_device = %p, state = 0x%x",
+		__func__,
+		isci_device,
+		status);
+
+	spin_lock_irqsave(&isci_device->state_lock, flags);
+	isci_device->status = status;
+	spin_unlock_irqrestore(&isci_device->state_lock, flags);
+}
diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h
new file mode 100644
index 0000000..a208f81
--- /dev/null
+++ b/drivers/scsi/isci/remote_device.h
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#if !defined(_ISCI_REMOTE_DEVICE_H_)
+#define _ISCI_REMOTE_DEVICE_H_
+#include "scic_user_callback.h"
+
+struct isci_host;
+struct scic_sds_remote_device;
+
+struct isci_remote_device {
+	struct scic_sds_remote_device *sci_device_handle;
+	enum isci_status status;
+	struct isci_port *isci_port;
+	struct domain_device *domain_dev;
+	struct completion *completion;
+	struct list_head node;
+	struct list_head reqs_in_process;
+	struct work_struct stop_work;
+	spinlock_t state_lock;
+	spinlock_t host_quiesce_lock;
+	bool host_quiesce;
+};
+
+#define to_isci_remote_device(p)	\
+	container_of(p, struct isci_remote_device, sci_remote_device);
+
+#define ISCI_REMOTE_DEVICE_START_TIMEOUT 5000
+
+
+/**
+ * This function gets the status of the remote_device object.
+ * @isci_device: This parameter points to the isci_remote_device object
+ *
+ * status of the object as a isci_status enum.
+ */
+static inline
+enum isci_status isci_remote_device_get_state(
+	struct isci_remote_device *isci_device)
+{
+	return (isci_device->host_quiesce)
+	       ? isci_host_quiesce
+	       : isci_device->status;
+}
+
+
+/**
+ * isci_dev_from_domain_dev() - This accessor retrieves the remote_device
+ *    object reference from the Linux domain_device reference.
+ * @domdev,: This parameter points to the Linux domain_device object .
+ *
+ * A reference to the associated isci remote device.
+ */
+#define isci_dev_from_domain_dev(domdev) \
+	((struct isci_remote_device *)(domdev)->lldd_dev)
+
+void isci_remote_device_start_complete(
+	struct isci_host *,
+	struct isci_remote_device *,
+	enum sci_status);
+
+void isci_remote_device_stop_complete(
+	struct isci_host *,
+	struct isci_remote_device *,
+	enum sci_status);
+
+enum sci_status isci_remote_device_stop(
+	struct isci_remote_device *isci_device);
+
+void isci_remote_device_nuke_requests(
+	struct isci_remote_device *isci_device);
+
+void isci_remote_device_ready(
+	struct isci_remote_device *);
+
+void isci_remote_device_not_ready(
+	struct isci_remote_device *,
+	u32);
+
+void isci_remote_device_gone(
+	struct domain_device *domain_dev);
+
+int isci_remote_device_found(
+	struct domain_device *domain_dev);
+
+bool isci_device_is_reset_pending(
+	struct isci_host *isci_host,
+	struct isci_remote_device *isci_device);
+
+void isci_device_clear_reset_pending(
+	struct isci_remote_device *isci_device);
+
+void isci_device_set_host_quiesce_lock_state(
+	struct isci_remote_device *isci_device,
+	bool lock_state);
+
+void isci_remote_device_change_state(
+	struct isci_remote_device *isci_device,
+	enum isci_status status);
+
+#endif /* !defined(_ISCI_REMOTE_DEVICE_H_) */
+


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [RFC PATCH 6/6] isci: sata support and phy settings via request_firmware()
  2011-02-07  0:34 [RFC PATCH 0/6] isci: initial driver release (part1: intro and lldd) Dan Williams
                   ` (4 preceding siblings ...)
  2011-02-07  0:35 ` [RFC PATCH 5/6] isci: phy, port, and remote device Dan Williams
@ 2011-02-07  0:35 ` Dan Williams
  2011-02-07  7:58 ` [RFC PATCH 1/6] isci: initialization jack_wang
  6 siblings, 0 replies; 36+ messages in thread
From: Dan Williams @ 2011-02-07  0:35 UTC (permalink / raw)
  To: james.bottomley
  Cc: dave.jiang, linux-scsi, jacek.danecki, ed.ciechanowski,
	jeffrey.d.skirvin, edmund.nadolski

1/ sata protocol detection
2/ "firmware" for feeding the driver sas address and phy settings until
   platform firmware provides the same.

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/scsi/isci/firmware/Makefile    |   19 ++
 drivers/scsi/isci/firmware/README      |   36 +++
 drivers/scsi/isci/firmware/create_fw.c |  177 ++++++++++++++++
 drivers/scsi/isci/sata.c               |  356 ++++++++++++++++++++++++++++++++
 drivers/scsi/isci/sata.h               |   83 +++++++
 drivers/scsi/isci/sci_environment.h    |  112 ++++++++++
 6 files changed, 783 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/isci/firmware/Makefile
 create mode 100644 drivers/scsi/isci/firmware/README
 create mode 100644 drivers/scsi/isci/firmware/create_fw.c
 create mode 100644 drivers/scsi/isci/sata.c
 create mode 100644 drivers/scsi/isci/sata.h
 create mode 100644 drivers/scsi/isci/sci_environment.h

diff --git a/drivers/scsi/isci/firmware/Makefile b/drivers/scsi/isci/firmware/Makefile
new file mode 100644
index 0000000..5f54461
--- /dev/null
+++ b/drivers/scsi/isci/firmware/Makefile
@@ -0,0 +1,19 @@
+# Makefile for create_fw
+#
+CC=gcc
+CFLAGS=-c -Wall -O2 -g
+LDFLAGS=
+SOURCES=create_fw.c
+OBJECTS=$(SOURCES:.cpp=.o)
+EXECUTABLE=create_fw
+
+all: $(SOURCES) $(EXECUTABLE)
+
+$(EXECUTABLE): $(OBJECTS)
+	$(CC) $(LDFLAGS) $(OBJECTS) -o $@
+
+.c.o:
+	$(CC) $(CFLAGS) $< -O $@
+
+clean:
+	rm -f *.o $(EXECUTABLE)
diff --git a/drivers/scsi/isci/firmware/README b/drivers/scsi/isci/firmware/README
new file mode 100644
index 0000000..cf7e428
--- /dev/null
+++ b/drivers/scsi/isci/firmware/README
@@ -0,0 +1,36 @@
+This defines the temporary binary blow we are to pass to the SCU
+driver to emulate the binary firmware that we will eventually be
+able to access via NVRAM on the SCU controller.
+
+The current size of the binary blob is expected to be 149 bytes or larger
+
+Header Types:
+0x1: Phy Masks
+0x2: Phy Gens
+0x3: SAS Addrs
+0xff: End of Data
+
+ID string - u8[12]: "#SCU MAGIC#\0"
+Version - u8: 1
+SubVersion - u8: 0
+
+Header Type - u8: 0x1
+Size - u8: 8
+Phy Mask - u32[8]
+
+Header Type - u8: 0x2
+Size - u8: 8
+Phy Gen - u32[8]
+
+Header Type - u8: 0x3
+Size - u8: 8
+Sas Addr - u64[8]
+
+Header Type - u8: 0xf
+
+
+==============================================================================
+
+Place isci_firmware.bin in /lib/firmware
+Be sure to recreate the initramfs image to include the firmware. 
+
diff --git a/drivers/scsi/isci/firmware/create_fw.c b/drivers/scsi/isci/firmware/create_fw.c
new file mode 100644
index 0000000..442caac
--- /dev/null
+++ b/drivers/scsi/isci/firmware/create_fw.c
@@ -0,0 +1,177 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+char blob_name[] = "isci_firmware.bin";
+char id[] = "#SCU MAGIC#";
+unsigned char version = 1;
+unsigned char sub_version = 0;
+
+
+/*
+ * For all defined arrays:
+ * elements 0-3 are for SCU0, ports 0-3
+ * elements 4-7 are for SCU1, ports 0-3
+ *
+ * valid configurations for one SCU are:
+ *  P0  P1  P2  P3
+ * ----------------
+ * 0xF,0x0,0x0,0x0 # 1 x4 port
+ * 0x3,0x0,0x4,0x8 # Phys 0 and 1 are a x2 port, phy 2 and phy 3 are each x1
+ *                 # ports
+ * 0x1,0x2,0xC,0x0 # Phys 0 and 1 are each x1 ports, phy 2 and phy 3 are a x2
+ *                 # port
+ * 0x3,0x0,0xC,0x0 # Phys 0 and 1 are a x2 port, phy 2 and phy 3 are a x2 port
+ * 0x1,0x2,0x4,0x8 # Each phy is a x1 port (this is the default configuration)
+ *
+ * if there is a port/phy on which you do not wish to override the default
+ * values, use the value assigned to UNINIT_PARAM (255).
+ */
+unsigned int phy_mask[] = { 1, 2, 4, 8, 1, 2, 4, 8 };
+
+
+/* denotes SAS generation. i.e. 3: SAS Gen 3 6G */
+unsigned int phy_gen[] = { 3, 3, 3, 3, 3, 3, 3, 3 };
+
+/*
+ * if there is a port/phy on which you do not wish to override the default
+ * values, use the value "0000000000000000". SAS address of zero's is
+ * considered invalid and will not be used.
+ */
+unsigned long long sas_addr[] = { 0x5FCFFFFFF0000000ULL,
+				  0x5FCFFFFFF1000000ULL,
+				  0x5FCFFFFFF2000000ULL,
+				  0x5FCFFFFFF3000000ULL,
+				  0x5FCFFFFFF4000000ULL,
+				  0x5FCFFFFFF5000000ULL,
+				  0x5FCFFFFFF6000000ULL,
+				  0x5FCFFFFFF7000000ULL };
+
+int write_blob(void)
+{
+	FILE *fd;
+	int err;
+
+	fd = fopen(blob_name, "w+");
+	if (!fd) {
+		perror("Open file for write failed");
+		return -EIO;
+	}
+
+	/* write id */
+	err = fwrite((void *)id, sizeof(char), strlen(id)+1, fd);
+	if (err == 0) {
+		perror("write id failed");
+		return err;
+	}
+
+	/* write version */
+	err = fwrite((void *)&version, sizeof(version), 1, fd);
+	if (err == 0) {
+		perror("write version failed");
+		return err;
+	}
+
+	/* write sub version */
+	err = fwrite((void *)&sub_version, sizeof(sub_version), 1, fd);
+	if (err == 0) {
+		perror("write subversion failed");
+		return err;
+	}
+
+	/* write phy mask header */
+	err = fputc(0x1, fd);
+	if (err == EOF) {
+		perror("write phy mask header failed");
+		return -EIO;
+	}
+
+	/* write size */
+	err = fputc(8, fd);
+	if (err == EOF) {
+		perror("write phy mask size failed");
+		return -EIO;
+	}
+
+	/* write phy masks */
+	err = fwrite((void *)phy_mask, 1, sizeof(phy_mask), fd);
+	if (err == 0) {
+		perror("write phy_mask failed");
+		return err;
+	}
+
+	/* write phy gen header */
+	err = fputc(0x2, fd);
+	if (err == EOF) {
+		perror("write phy gen header failed");
+		return -EIO;
+	}
+
+	/* write size */
+	err = fputc(8, fd);
+	if (err == EOF) {
+		perror("write phy gen size failed");
+		return -EIO;
+	}
+
+	/* write phy_gen */
+	err = fwrite((void *)phy_gen,
+		     1,
+		     sizeof(phy_gen),
+		     fd);
+	if (err == 0) {
+		perror("write phy_gen failed");
+		return err;
+	}
+
+	/* write phy gen header */
+	err = fputc(0x3, fd);
+	if (err == EOF) {
+		perror("write sas addr header failed");
+		return -EIO;
+	}
+
+	/* write size */
+	err = fputc(8, fd);
+	if (err == EOF) {
+		perror("write sas addr size failed");
+		return -EIO;
+	}
+
+	/* write sas_addr */
+	err = fwrite((void *)sas_addr,
+		     1,
+		     sizeof(sas_addr),
+		     fd);
+	if (err == 0) {
+		perror("write sas_addr failed");
+		return err;
+	}
+
+	/* write end header */
+	err = fputc(0xff, fd);
+	if (err == EOF) {
+		perror("write end header failed");
+		return -EIO;
+	}
+
+	fclose(fd);
+
+	return 0;
+}
+
+int main(void)
+{
+	int err;
+
+	err = write_blob();
+	if (err < 0)
+		return err;
+
+	return 0;
+}
diff --git a/drivers/scsi/isci/sata.c b/drivers/scsi/isci/sata.c
new file mode 100644
index 0000000..19b0eea
--- /dev/null
+++ b/drivers/scsi/isci/sata.c
@@ -0,0 +1,356 @@
+/*
+ * 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_sds_remote_device.h"
+#include "scic_io_request.h"
+#include "scic_task_request.h"
+#include "task.h"
+#include "request.h"
+#include "sata.h"
+#include "intel_sat.h"
+#include "intel_ata.h"
+
+static u8 isci_sata_get_management_task_protocol(struct isci_tmf *tmf);
+
+
+/**
+ * isci_sata_task_to_fis_copy() - This function gets the host_to_dev_fis from
+ *    the core and copies the fis from the task into it.
+ * @task: This parameter is a pointer to the task struct from libsas.
+ *
+ * pointer to the host_to_dev_fis from the core request object.
+ */
+struct host_to_dev_fis *isci_sata_task_to_fis_copy(struct sas_task *task)
+{
+	struct isci_request *request = task->lldd_task;
+	struct host_to_dev_fis *register_fis =
+		scic_stp_io_request_get_h2d_reg_address(
+			request->sci_request_handle
+			);
+
+	memcpy(
+		(u8 *)register_fis,
+		(u8 *)&task->ata_task.fis,
+		sizeof(struct host_to_dev_fis)
+		);
+
+	if (!task->ata_task.device_control_reg_update)
+		register_fis->flags |= 0x80;
+
+	register_fis->flags &= 0xF0;
+
+	return register_fis;
+}
+
+/**
+ * isci_sata_is_task_ncq() - This function determines if the given stp task is
+ *    a ncq request.
+ * @task: This parameter is a pointer to the task struct from libsas.
+ *
+ * true if the task is ncq
+ */
+bool isci_sata_is_task_ncq(struct sas_task *task)
+{
+	struct ata_queued_cmd *qc = task->uldd_task;
+
+	bool ret = (qc &&
+		    (qc->tf.command == ATA_CMD_FPDMA_WRITE ||
+		     qc->tf.command == ATA_CMD_FPDMA_READ));
+
+	return ret;
+}
+
+/**
+ * isci_sata_set_ncq_tag() - This function sets the ncq tag field in the
+ *    host_to_dev_fis equal to the tag in the queue command in the task.
+ * @task: This parameter is a pointer to the task struct from libsas.
+ * @register_fis: This parameter is a pointer to the host_to_dev_fis from the
+ *    core request object.
+ *
+ */
+void isci_sata_set_ncq_tag(
+	struct host_to_dev_fis *register_fis,
+	struct sas_task *task)
+{
+	struct ata_queued_cmd *qc = task->uldd_task;
+	struct isci_request *request = task->lldd_task;
+
+	register_fis->sector_count = qc->tag << 3;
+	scic_stp_io_request_set_ncq_tag(request->sci_request_handle, qc->tag);
+}
+
+/**
+ * isci_request_process_stp_response() - This function sets the status and
+ *    response, 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.
+ * @response_buffer: This parameter points to the response of the completed
+ *    request.
+ *
+ * none.
+ */
+void isci_request_process_stp_response(
+	struct sas_task *task,
+	void *response_buffer)
+{
+	struct sata_fis_reg_d2h *d2h_reg_fis = (struct sata_fis_reg_d2h *)response_buffer;
+	struct task_status_struct *ts = &task->task_status;
+	struct ata_task_resp *resp = (void *)&ts->buf[0];
+
+	resp->frame_len = le16_to_cpu(*(__le16 *)(response_buffer + 6));
+	memcpy(&resp->ending_fis[0], response_buffer + 16, 24);
+	ts->buf_valid_size = sizeof(*resp);
+
+	/**
+	 * If the device fault bit is set in the status register, then
+	 * set the sense data and return.
+	 */
+	if (d2h_reg_fis->status & ATA_STATUS_REG_DEVICE_FAULT_BIT)
+		ts->stat = SAS_PROTO_RESPONSE;
+	else
+		ts->stat = SAM_STAT_GOOD;
+
+	ts->resp = SAS_TASK_COMPLETE;
+}
+
+/**
+ * isci_sata_get_sat_protocol() - retrieve the sat protocol for the request
+ * @isci_request: ata request
+ *
+ * Note: temporary implementation until expert mode removes the callback
+ *
+ */
+u8 isci_sata_get_sat_protocol(struct isci_request *isci_request)
+{
+	struct sas_task *task;
+	struct domain_device *dev;
+
+	dev_dbg(&isci_request->isci_host->pdev->dev,
+		"%s: isci_request = %p, ttype = %d\n",
+		__func__, isci_request, isci_request->ttype);
+
+	if (tmf_task == isci_request->ttype) {
+		struct isci_tmf *tmf = isci_request_access_tmf(isci_request);
+
+		return isci_sata_get_management_task_protocol(tmf);
+	}
+
+	task = isci_request_access_task(isci_request);
+	dev = task->dev;
+
+	if (!sas_protocol_ata(task->task_proto)) {
+		WARN(1, "unhandled task protocol\n");
+		return SAT_PROTOCOL_NON_DATA;
+	}
+
+	if (task->data_dir == DMA_NONE)
+		return SAT_PROTOCOL_NON_DATA;
+
+	/* the "_IN" protocol types are equivalent to their "_OUT"
+	 * analogs as far as the core is concerned
+	 */
+	if (dev->sata_dev.command_set == ATAPI_COMMAND_SET) {
+		if (task->ata_task.dma_xfer)
+			return SAT_PROTOCOL_PACKET_DMA_DATA_IN;
+		else
+			return SAT_PROTOCOL_PACKET_PIO_DATA_IN;
+	}
+
+	if (task->ata_task.use_ncq)
+		return SAT_PROTOCOL_FPDMA;
+
+	if (task->ata_task.dma_xfer)
+		return SAT_PROTOCOL_UDMA_DATA_IN;
+	else
+		return SAT_PROTOCOL_PIO_DATA_IN;
+}
+
+static u8 isci_sata_get_management_task_protocol(
+	struct isci_tmf *tmf)
+{
+	u8 ret = 0;
+
+	pr_warn("tmf = %p, func = %d\n", tmf, tmf->tmf_code);
+
+	if ((tmf->tmf_code == isci_tmf_sata_srst_high) ||
+	    (tmf->tmf_code == isci_tmf_sata_srst_low)) {
+		pr_warn("%s: tmf->tmf_code == TMF_LU_RESET\n", __func__);
+		ret = SAT_PROTOCOL_SOFT_RESET;
+	}
+
+	return ret;
+}
+
+enum sci_status isci_sata_management_task_request_build(
+	struct isci_request *isci_request)
+{
+	struct isci_tmf *isci_tmf;
+	enum sci_status status;
+
+	if (tmf_task != isci_request->ttype)
+		return SCI_FAILURE;
+
+	isci_tmf = isci_request_access_tmf(isci_request);
+
+	switch (isci_tmf->tmf_code) {
+
+	case isci_tmf_sata_srst_high:
+	case isci_tmf_sata_srst_low:
+	{
+		struct host_to_dev_fis *register_fis =
+			scic_stp_io_request_get_h2d_reg_address(
+				isci_request->sci_request_handle
+				);
+
+		memset(register_fis, 0, sizeof(*register_fis));
+
+		register_fis->fis_type  =  0x27;
+		register_fis->flags     &= ~0x80;
+		register_fis->flags     &= 0xF0;
+		if (isci_tmf->tmf_code == isci_tmf_sata_srst_high)
+			register_fis->control |= ATA_SRST;
+		else
+			register_fis->control &= ~ATA_SRST;
+		break;
+	}
+	/* other management commnd go here... */
+	default:
+		return SCI_FAILURE;
+	}
+
+	/* core builds the protocol specific request
+	 *  based on the h2d fis.
+	 */
+	status = scic_task_request_construct_sata(
+		isci_request->sci_request_handle
+		);
+
+	return status;
+}
+
+/**
+ * isci_task_send_lu_reset_sata() - This function is called by of the SAS
+ *    Domain Template functions. This is one of the Task Management functoins
+ *    called by libsas, to reset the given SAS lun. Note the assumption that
+ *    while this call is executing, no I/O will be sent by the host to the
+ *    device.
+ * @lun: This parameter specifies the lun to be reset.
+ *
+ * status, zero indicates success.
+ */
+int isci_task_send_lu_reset_sata(
+	struct isci_host *isci_host,
+	struct isci_remote_device *isci_device,
+	u8 *lun)
+{
+	struct isci_tmf tmf;
+	int ret = TMF_RESP_FUNC_FAILED;
+	unsigned long flags;
+
+	/* Send the initial SRST to the target */
+	#define ISCI_SRST_TIMEOUT_MS 20 /* 20 ms timeout. */
+	isci_task_build_tmf(&tmf, isci_device, isci_tmf_sata_srst_high,
+			    NULL, NULL
+			    );
+
+	ret = isci_task_execute_tmf(isci_host, &tmf, ISCI_SRST_TIMEOUT_MS);
+
+	if (ret != TMF_RESP_FUNC_COMPLETE) {
+		dev_warn(&isci_host->pdev->dev,
+			 "%s: Assert SRST failed (%p) = %x",
+			 __func__,
+			 isci_device,
+			 ret);
+
+		/* Return the failure so that the LUN reset is escalated
+		 * to a target reset.
+		 */
+		goto out;
+	}
+
+	/* Leave SRST high for a bit. */
+	#define ISCI_SRST_ASSERT_DELAY 100 /* usecs */
+	scic_cb_stall_execution(ISCI_SRST_ASSERT_DELAY);
+
+	/* Deassert SRST. */
+	isci_task_build_tmf(&tmf, isci_device, isci_tmf_sata_srst_low,
+			    NULL, NULL
+			    );
+	ret = isci_task_execute_tmf(isci_host, &tmf, ISCI_SRST_TIMEOUT_MS);
+
+	if (ret == TMF_RESP_FUNC_COMPLETE)
+		dev_dbg(&isci_host->pdev->dev,
+			"%s: SATA LUN reset passed (%p)\n",
+			__func__,
+			isci_device);
+	else
+		dev_warn(&isci_host->pdev->dev,
+			 "%s: Deassert SRST failed (%p)=%x\n",
+			 __func__,
+			 isci_device,
+			 ret);
+
+ out:
+	spin_lock_irqsave(&isci_host->scic_lock, flags);
+
+	/* Resume the device. */
+	scic_sds_remote_device_resume(isci_device->sci_device_handle);
+
+	spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+
+	return ret;
+}
diff --git a/drivers/scsi/isci/sata.h b/drivers/scsi/isci/sata.h
new file mode 100644
index 0000000..b6ba25b
--- /dev/null
+++ b/drivers/scsi/isci/sata.h
@@ -0,0 +1,83 @@
+/*
+ * 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 "intel_sat.h"
+
+
+
+struct host_to_dev_fis *isci_sata_task_to_fis_copy(
+	struct sas_task *task);
+
+bool isci_sata_is_task_ncq(
+	struct sas_task *task);
+
+void isci_sata_set_ncq_tag(
+	struct host_to_dev_fis *register_fis,
+	struct sas_task *task);
+
+void isci_request_process_stp_response(
+	struct sas_task *task,
+	void *response_buffer);
+
+u8 isci_sata_get_sat_protocol(
+	struct isci_request *isci_request);
+
+enum sci_status isci_sata_management_task_request_build(
+	struct isci_request *isci_request);
+
+int isci_task_send_lu_reset_sata(
+	struct isci_host *isci_host,
+	struct isci_remote_device *isci_device,
+	u8 *lun);
diff --git a/drivers/scsi/isci/sci_environment.h b/drivers/scsi/isci/sci_environment.h
new file mode 100644
index 0000000..e1020ee
--- /dev/null
+++ b/drivers/scsi/isci/sci_environment.h
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+#ifndef _SCI_ENVIRONMENT_H_
+#define _SCI_ENVIRONMENT_H_
+
+#include "isci.h"
+
+struct scic_sds_controller;
+struct scic_sds_phy;
+struct scic_sds_port;
+struct scic_sds_remote_device;
+
+static inline struct device *scic_to_dev(struct scic_sds_controller *scic)
+{
+	struct isci_host *isci_host = sci_object_get_association(scic);
+
+	return &isci_host->pdev->dev;
+}
+
+static inline struct device *sciphy_to_dev(struct scic_sds_phy *sci_phy)
+{
+	struct isci_phy *iphy = sci_object_get_association(sci_phy);
+
+	if (!iphy || !iphy->isci_port || !iphy->isci_port->isci_host)
+		return NULL;
+
+	return &iphy->isci_port->isci_host->pdev->dev;
+}
+
+static inline struct device *sciport_to_dev(struct scic_sds_port *sci_port)
+{
+	struct isci_port *iport = sci_object_get_association(sci_port);
+
+	if (!iport || !iport->isci_host)
+		return NULL;
+
+	return &iport->isci_host->pdev->dev;
+}
+
+static inline struct device *scirdev_to_dev(struct scic_sds_remote_device *sci_dev)
+{
+	struct isci_remote_device *idev = sci_object_get_association(sci_dev);
+
+	if (!idev || !idev->isci_port || !idev->isci_port->isci_host)
+		return NULL;
+
+	return &idev->isci_port->isci_host->pdev->dev;
+}
+
+enum {
+	ISCI_SI_REVA0,
+	ISCI_SI_REVA2,
+	ISCI_SI_REVB0,
+};
+
+extern int isci_si_rev;
+
+
+#endif


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 1/6] isci: initialization
  2011-02-07  0:34 [RFC PATCH 0/6] isci: initial driver release (part1: intro and lldd) Dan Williams
                   ` (5 preceding siblings ...)
  2011-02-07  0:35 ` [RFC PATCH 6/6] isci: sata support and phy settings via request_firmware() Dan Williams
@ 2011-02-07  7:58 ` jack_wang
  2011-02-14  7:49   ` Dan Williams
  6 siblings, 1 reply; 36+ messages in thread
From: jack_wang @ 2011-02-07  7:58 UTC (permalink / raw)
  To: Dan Williams, james.bottomley
  Cc: dave.jiang, linux-scsi, jacek.danecki, ed.ciechanowski,
	jeffrey.d.skirvin, edmund.nadolski


+
+ /**
+  * check interrupt_handler's status and call completion_handler if true,
+  * link_up events should be coming from the scu core lib, as phy's come
+  * online. for each link_up from the core, call
+  * get_received_identify_address_frame, copy the frame into the
+  * sas_phy object and call libsas notify_port_event(PORTE_BYTES_DMAED).
+  * continue to return zero from thee scan_finished routine until

[Jack] Here "thee" seams typo of "the"




+static struct coherent_memory_info *isci_host_alloc_mdl_struct(
+ struct isci_host *isci_host,
+ u32 size)
+{
+ struct coherent_memory_info *mdl_struct;
+ void *uncached_address = NULL;
+
+
+ mdl_struct = devm_kzalloc(&isci_host->pdev->dev,
+   sizeof(*mdl_struct),
+   GFP_KERNEL);
+ if (!mdl_struct)
+ return NULL;
+
+ INIT_LIST_HEAD(&mdl_struct->node);
+
+ uncached_address = dmam_alloc_coherent(&isci_host->pdev->dev,
+        size,
+        &mdl_struct->dma_handle,
+        GFP_KERNEL);
+ if (!uncached_address)
+ return NULL;
+
+ /* memset the whole memory area. */
+ memset((char *)uncached_address, 0, size);
[Jack] cast void * is not needed.
+ mdl_struct->vaddr = uncached_address;
+ mdl_struct->size = (size_t)size;
+
+ return mdl_struct;
+}
+
+static void isci_host_build_mde(
+ struct sci_physical_memory_descriptor *mde_struct,
+ struct coherent_memory_info *mdl_struct)
+{
+ unsigned long address = 0;
+ dma_addr_t dma_addr = 0;
+
+ address = (unsigned long)mdl_struct->vaddr;
[Jack] Why cast void vadd to usigned long? This may cause problem on x64.

+ dma_addr = mdl_struct->dma_handle;
+
+ /* to satisfy the alignment. */
+ if ((address % mde_struct->constant_memory_alignment) != 0) {
+ int align_offset
+ = (mde_struct->constant_memory_alignment
+    - (address % mde_struct->constant_memory_alignment));
+ address += align_offset;
+ dma_addr += align_offset;
+ }
+
+ mde_struct->virtual_address = (void *)address;
+ mde_struct->physical_address = dma_addr;
+ mdl_struct->mde = mde_struct;
+}
+



+
+ /* stop the comtroller and wait for completion.  */
[Jack] controller.

+

+
+static inline struct isci_pci_info *to_pci_info(struct pci_dev *pdev)
+{
+ return pci_get_drvdata(pdev);
+}
+
[Jack] This wrap is not needed I think.


+static inline
+enum isci_status isci_host_get_state(
+ struct isci_host *isci_host)
+{
+ return isci_host->status;
+}
[Jack] This wrap is not needed too.




^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 2/6] isci: task (libsas interface support)
  2011-02-07  0:34 ` [RFC PATCH 2/6] isci: task (libsas interface support) Dan Williams
@ 2011-02-09 15:01   ` David Milburn
  2011-02-14  7:14     ` Dan Williams
  0 siblings, 1 reply; 36+ messages in thread
From: David Milburn @ 2011-02-09 15:01 UTC (permalink / raw)
  To: Dan Williams
  Cc: james.bottomley, dave.jiang, linux-scsi, jacek.danecki,
	ed.ciechanowski, jeffrey.d.skirvin, edmund.nadolski

Dan Williams wrote:
> libsas interface routines and infrastructure.
> 
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> ---
>  drivers/scsi/isci/task.c | 1691 ++++++++++++++++++++++++++++++++++++++++++++++
>  drivers/scsi/isci/task.h |  368 ++++++++++
>  2 files changed, 2059 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/scsi/isci/task.c
>  create mode 100644 drivers/scsi/isci/task.h
> 
> diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c
> new file mode 100644
> index 0000000..5e6f558
> --- /dev/null
> +++ b/drivers/scsi/isci/task.c
> @@ -0,0 +1,1691 @@
> +/*
> + * 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 <linux/completion.h>
> +#include "scic_task_request.h"
> +#include "scic_remote_device.h"
> +#include "scic_io_request.h"
> +#include "scic_sds_remote_device.h"
> +#include "scic_sds_remote_node_context.h"
> +#include "isci.h"
> +#include "request.h"
> +#include "sata.h"
> +#include "task.h"
> +
> +
> +/**
> + * isci_task_execute_task() - This function is one of the SAS Domain Template
> + *    functions. This function is called by libsas to send a task down to
> + *    hardware.
> + * @task: This parameter specifies the SAS task to send.
> + * @num: This parameter specifies the number of tasks to queue.
> + * @gfp_flags: This parameter specifies the context of this call.
> + *
> + * status, zero indicates success.
> + */
> +int isci_task_execute_task(struct sas_task *task, int num, gfp_t gfp_flags)
> +{
> +	struct isci_host *isci_host;
> +	struct isci_request *request = NULL;
> +	struct isci_remote_device *device;
> +	unsigned long flags;
> +	unsigned long quiesce_flags = 0;
> +	int ret;
> +	enum sci_status status;
> +
> +
> +	dev_dbg(task->dev->port->ha->dev, "%s: num=%d\n", __func__, num);
> +
> +	if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
> +
> +		isci_task_complete_for_upper_layer(
> +			task,
> +			SAS_TASK_UNDELIVERED,
> +			SAM_STAT_TASK_ABORTED,
> +			isci_perform_normal_io_completion
> +			);
> +
> +		return 0;  /* The I/O was accepted (and failed). */

Dan, is this the right status to return to libsas?

Looks like it checks (->lldd_execute_task) for non-zero result.

> +	}
> +	if ((task->dev == NULL) || (task->dev->port == NULL)) {
> +
> +		/* Indicate SAS_TASK_UNDELIVERED, so that the scsi midlayer
> +		 * removes the target.
> +		 */
> +		isci_task_complete_for_upper_layer(
> +			task,
> +			SAS_TASK_UNDELIVERED,
> +			SAS_DEVICE_UNKNOWN,
> +			isci_perform_normal_io_completion
> +			);
> +		return 0;  /* The I/O was accepted (and failed). */

Same here?

> +	}
> +	isci_host = isci_host_from_sas_ha(task->dev->port->ha);
> +
> +	/* Check if we have room for more tasks */
> +	ret = isci_host_can_queue(isci_host, num);
> +
> +	if (ret) {
> +		dev_warn(task->dev->port->ha->dev, "%s: queue full\n", __func__);
> +		return ret;
> +	}
> +



> +/**
> + * isci_task_send_lu_reset_sas() - This function is called by of the SAS Domain
> + *    Template functions.
> + * @lun: This parameter specifies the lun to be reset.
> + *
> + * status, zero indicates success.
> + */
> +static int isci_task_send_lu_reset_sas(
> +	struct isci_host *isci_host,
> +	struct isci_remote_device *isci_device,
> +	u8 *lun)
> +{
> +	struct isci_tmf tmf;
> +	int ret = TMF_RESP_FUNC_FAILED;
> +
> +	dev_dbg(&isci_host->pdev->dev,
> +		"%s: isci_host = %p, isci_device = %p\n",
> +		__func__, isci_host, isci_device);
> +	/* Send the LUN reset to the target.  By the time the call returns,
> +	 * the TMF has fully exected in the target (in which case the return
> +	 * value is "TMF_RESP_FUNC_COMPLETE", or the request timed-out (or
> +	 * was otherwise unable to be executed ("TMF_RESP_FUNC_FAILED").
> +	 */
> +	isci_task_build_tmf(&tmf, isci_device, isci_tmf_ssp_lun_reset, NULL,
> +			    NULL);
> +
> +	#define ISCI_LU_RESET_TIMEOUT_MS 2000 /* 2 second timeout. */

Maybe move #define to isci_task.h?

> +	ret = isci_task_execute_tmf(isci_host, &tmf, ISCI_LU_RESET_TIMEOUT_MS);
> +
> +	if (ret == TMF_RESP_FUNC_COMPLETE)
> +		dev_dbg(&isci_host->pdev->dev,
> +			"%s: %p: TMF_LU_RESET passed\n",
> +			__func__, isci_device);
> +	else
> +		dev_dbg(&isci_host->pdev->dev,
> +			"%s: %p: TMF_LU_RESET failed (%x)\n",
> +			__func__, isci_device, ret);
> +
> +	return ret;
> +}
> +
> +/**

> +
> +/*	 int (*lldd_clear_nexus_port)(struct asd_sas_port *); */
> +int isci_task_clear_nexus_port(struct asd_sas_port *port)
> +{
> +	return TMF_RESP_FUNC_FAILED;
> +}
> +
> +
> +
> +int isci_task_clear_nexus_ha(struct sas_ha_struct *ha)
> +{
> +	return TMF_RESP_FUNC_FAILED;
> +}
> +
> +int isci_task_I_T_nexus_reset(struct domain_device *dev)
> +{
> +	return TMF_RESP_FUNC_FAILED;
> +}
> +

These will be used during libsas error handling.


> +/**
> + * isci_task_abort_task() - This function is one of the SAS Domain Template
> + *    functions. This function is called by libsas to abort a specified task.
> + * @task: This parameter specifies the SAS task to abort.
> + *
> + * status, zero indicates success.
> + */
> +int isci_task_abort_task(struct sas_task *task)
> +{
scic_lock, flags);


> +
> +		#define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* half second timeout. */

Maybe move this #define to isci_task.h?

> +		ret = isci_task_execute_tmf(isci_host, &tmf,
> +					    ISCI_ABORT_TASK_TIMEOUT_MS);
> +
> +		if (ret == TMF_RESP_FUNC_COMPLETE) {
> +			old_request->complete_in_target = true;
> +
> +			/* Clean up the request on our side, and wait for the aborted I/O to
> +			 * complete.
> +			 */
> +			isci_terminate_request_core(isci_host, isci_device, old_request,
> +						    &aborted_io_completion);
> +
> +			/* Set the state on the task. */
> +			isci_task_all_done(task);
> +		} else
> +			dev_err(&isci_host->pdev->dev,
> +				"%s: isci_task_send_tmf failed\n",
> +				__func__);
> +	}
> +
> +	return ret;
> +}
> +
> +/**
> + * isci_task_abort_task_set() - This function is one of the SAS Domain Template
> + *    functions. This is one of the Task Management functoins called by libsas,
> + *    to abort all task for the given lun.
> + * @d_device: This parameter specifies the domain device associated with this
> + *    request.
> + * @lun: This parameter specifies the lun associated with this request.
> + *
> + * status, zero indicates success.
> + */
> +int isci_task_abort_task_set(
> +	struct domain_device *d_device,
> +	u8 *lun)
> +{
> +	return TMF_RESP_FUNC_FAILED;
> +}
> +
> +
> +/**
> + * isci_task_clear_aca() - This function is one of the SAS Domain Template
> + *    functions. This is one of the Task Management functoins called by libsas.
> + * @d_device: This parameter specifies the domain device associated with this
> + *    request.
> + * @lun: This parameter specifies the lun	 associated with this request.
> + *
> + * status, zero indicates success.
> + */
> +int isci_task_clear_aca(
> +	struct domain_device *d_device,
> +	u8 *lun)
> +{
> +	return TMF_RESP_FUNC_FAILED;
> +}
> +
> +
> +
> +/**
> + * isci_task_clear_task_set() - This function is one of the SAS Domain Template
> + *    functions. This is one of the Task Management functoins called by libsas.
> + * @d_device: This parameter specifies the domain device associated with this
> + *    request.
> + * @lun: This parameter specifies the lun	 associated with this request.
> + *
> + * status, zero indicates success.
> + */
> +int isci_task_clear_task_set(
> +	struct domain_device *d_device,
> +	u8 *lun)
> +{
> +	return TMF_RESP_FUNC_FAILED;
> +}

More libsas error recovery functions.

Thanks,
David

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 2/6] isci: task (libsas interface support)
  2011-02-09 15:01   ` David Milburn
@ 2011-02-14  7:14     ` Dan Williams
  2011-02-16 18:48       ` David Milburn
  0 siblings, 1 reply; 36+ messages in thread
From: Dan Williams @ 2011-02-14  7:14 UTC (permalink / raw)
  To: David Milburn
  Cc: james.bottomley, dave.jiang, linux-scsi, jacek.danecki,
	ed.ciechanowski, jeffrey.d.skirvin, edmund.nadolski

Thanks David, comments below.

>> +       if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
>> +
>> +               isci_task_complete_for_upper_layer(
>> +                       task,
>> +                       SAS_TASK_UNDELIVERED,
>> +                       SAM_STAT_TASK_ABORTED,
>> +                       isci_perform_normal_io_completion
>> +                       );
>> +
>> +               return 0;  /* The I/O was accepted (and failed). */
>
> Dan, is this the right status to return to libsas?
>
> Looks like it checks (->lldd_execute_task) for non-zero result.

I think this is self defense that can be deleted, how can a task be
aborted before it has even returned from the submission routine.

>
>> +       }
>> +       if ((task->dev == NULL) || (task->dev->port == NULL)) {
>> +
>> +               /* Indicate SAS_TASK_UNDELIVERED, so that the scsi
>> midlayer
>> +                * removes the target.
>> +                */
>> +               isci_task_complete_for_upper_layer(
>> +                       task,
>> +                       SAS_TASK_UNDELIVERED,
>> +                       SAS_DEVICE_UNKNOWN,
>> +                       isci_perform_normal_io_completion
>> +                       );
>> +               return 0;  /* The I/O was accepted (and failed). */
>
> Same here?

Yeah, we should always be able to dereference task->dev because ->dev
is referenced counted against outstanding commands.  If we have a task
by definition we have a dev.

[..]
>> +        */
>> +       isci_task_build_tmf(&tmf, isci_device, isci_tmf_ssp_lun_reset,
>> NULL,
>> +                           NULL);
>> +
>> +       #define ISCI_LU_RESET_TIMEOUT_MS 2000 /* 2 second timeout. */
>
> Maybe move #define to isci_task.h?

Ok.

>
>> +       ret = isci_task_execute_tmf(isci_host, &tmf,
>> ISCI_LU_RESET_TIMEOUT_MS);
>> +
>> +       if (ret == TMF_RESP_FUNC_COMPLETE)
>> +               dev_dbg(&isci_host->pdev->dev,
>> +                       "%s: %p: TMF_LU_RESET passed\n",
>> +                       __func__, isci_device);
>> +       else
>> +               dev_dbg(&isci_host->pdev->dev,
>> +                       "%s: %p: TMF_LU_RESET failed (%x)\n",
>> +                       __func__, isci_device, ret);
>> +
>> +       return ret;
>> +}
>> +
>> +/**
>
>> +
>> +/*      int (*lldd_clear_nexus_port)(struct asd_sas_port *); */
>> +int isci_task_clear_nexus_port(struct asd_sas_port *port)
>> +{
>> +       return TMF_RESP_FUNC_FAILED;
>> +}
>> +
>> +
>> +
>> +int isci_task_clear_nexus_ha(struct sas_ha_struct *ha)
>> +{
>> +       return TMF_RESP_FUNC_FAILED;
>> +}
>> +
>> +int isci_task_I_T_nexus_reset(struct domain_device *dev)
>> +{
>> +       return TMF_RESP_FUNC_FAILED;
>> +}
>> +
>
> These will be used during libsas error handling.

The driver implements abort_task, and promotes the rest to lu_reset.
Assuming the documentation is not out of date:

     A SAS LLDD should also implement at least one of the Task
     Management Functions (TMFs) described in SAM:

...how urgent is the need for these other task management routines?

Speaking of mandatory libsas methods all other libsas drivers outside
of aic94xx forgo implementing

             /* Port and Adapter management */
             int (*lldd_clear_nexus_port)(struct sas_port *);
             int (*lldd_clear_nexus_ha)(struct sas_ha_struct *);

     A SAS LLDD should implement at least one of those.

...not clear that at least one of those is mandatory.

>
>
>> +/**
>> + * isci_task_abort_task() - This function is one of the SAS Domain
>> Template
>> + *    functions. This function is called by libsas to abort a specified
>> task.
>> + * @task: This parameter specifies the SAS task to abort.
>> + *
>> + * status, zero indicates success.
>> + */
>> +int isci_task_abort_task(struct sas_task *task)
>> +{
>
> scic_lock, flags);
>
>
>> +
>> +               #define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* half second
>> timeout. */
>
> Maybe move this #define to isci_task.h?

Ok.

[..]
>> + * isci_task_abort_task_set() - This function is one of the SAS Domain
>> Template
>> + *    functions. This is one of the Task Management functoins called by
>> libsas,
>> + *    to abort all task for the given lun.
>> + * @d_device: This parameter specifies the domain device associated with
>> this
>> + *    request.
>> + * @lun: This parameter specifies the lun associated with this request.
>> + *
>> + * status, zero indicates success.
>> + */
>> +int isci_task_abort_task_set(
>> +       struct domain_device *d_device,
>> +       u8 *lun)
>> +{
>> +       return TMF_RESP_FUNC_FAILED;
>> +}
>> +
>> +
>> +/**
>> + * isci_task_clear_aca() - This function is one of the SAS Domain
>> Template
>> + *    functions. This is one of the Task Management functoins called by
>> libsas.
>> + * @d_device: This parameter specifies the domain device associated with
>> this
>> + *    request.
>> + * @lun: This parameter specifies the lun       associated with this
>> request.
>> + *
>> + * status, zero indicates success.
>> + */
>> +int isci_task_clear_aca(
>> +       struct domain_device *d_device,
>> +       u8 *lun)
>> +{
>> +       return TMF_RESP_FUNC_FAILED;
>> +}
>> +
>> +
>> +
>> +/**
>> + * isci_task_clear_task_set() - This function is one of the SAS Domain
>> Template
>> + *    functions. This is one of the Task Management functoins called by
>> libsas.
>> + * @d_device: This parameter specifies the domain device associated with
>> this
>> + *    request.
>> + * @lun: This parameter specifies the lun       associated with this
>> request.
>> + *
>> + * status, zero indicates success.
>> + */
>> +int isci_task_clear_task_set(
>> +       struct domain_device *d_device,
>> +       u8 *lun)
>> +{
>> +       return TMF_RESP_FUNC_FAILED;
>> +}
>
> More libsas error recovery functions.

Same comment as above the driver has the little and the big recovery
hammers, the medium hammers can be prioritized behind other fixes I
think.

--
Dan
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 1/6] isci: initialization
  2011-02-07  7:58 ` [RFC PATCH 1/6] isci: initialization jack_wang
@ 2011-02-14  7:49   ` Dan Williams
  0 siblings, 0 replies; 36+ messages in thread
From: Dan Williams @ 2011-02-14  7:49 UTC (permalink / raw)
  To: jack_wang
  Cc: james.bottomley, dave.jiang, linux-scsi, jacek.danecki,
	ed.ciechanowski, jeffrey.d.skirvin, edmund.nadolski

Thanks Jack, comments below:

On Sun, Feb 6, 2011 at 11:58 PM, jack_wang <jack_wang@usish.com> wrote:
>
> +
> + /**
> +  * check interrupt_handler's status and call completion_handler if true,
> +  * link_up events should be coming from the scu core lib, as phy's come
> +  * online. for each link_up from the core, call
> +  * get_received_identify_address_frame, copy the frame into the
> +  * sas_phy object and call libsas notify_port_event(PORTE_BYTES_DMAED).
> +  * continue to return zero from thee scan_finished routine until
>
> [Jack] Here "thee" seams typo of "the"

Yes, fixed.

> + /* memset the whole memory area. */
> + memset((char *)uncached_address, 0, size);
> [Jack] cast void * is not needed.

Yes, and neither is the comment.

> + struct sci_physical_memory_descriptor *mde_struct,
> + struct coherent_memory_info *mdl_struct)
> +{
> + unsigned long address = 0;
> + dma_addr_t dma_addr = 0;
> +
> + address = (unsigned long)mdl_struct->vaddr;
> [Jack] Why cast void vadd to usigned long? This may cause problem on x64.

No, Linux is LP64.  However this routine does not seem to be aware of
the fact that dma_alloc_coherent always returns at least page
alignment, and also open codes the ALIGN() macro.

> + /* stop the comtroller and wait for completion.  */
> [Jack] controller.

Yes, misspelled and superfluous given the context... deleted.

> +static inline struct isci_pci_info *to_pci_info(struct pci_dev *pdev)
> +{
> + return pci_get_drvdata(pdev);
> +}
> +
> [Jack] This wrap is not needed I think.

Ok, fixed up all occurrences of unnecessary line wrapping in host.h.

> +static inline
> +enum isci_status isci_host_get_state(
> + struct isci_host *isci_host)
> +{
> + return isci_host->status;
> +}
> [Jack] This wrap is not needed too.

This helper is not needed.  Will remove.

--
Dan
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 2/6] isci: task (libsas interface support)
  2011-02-14  7:14     ` Dan Williams
@ 2011-02-16 18:48       ` David Milburn
  2011-02-16 19:35         ` David Milburn
  0 siblings, 1 reply; 36+ messages in thread
From: David Milburn @ 2011-02-16 18:48 UTC (permalink / raw)
  To: Dan Williams
  Cc: james.bottomley, dave.jiang, linux-scsi, jacek.danecki,
	ed.ciechanowski, jeffrey.d.skirvin, edmund.nadolski

Dan Williams wrote:
> Thanks David, comments below.
> 
>>> +       if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
>>> +
>>> +               isci_task_complete_for_upper_layer(
>>> +                       task,
>>> +                       SAS_TASK_UNDELIVERED,
>>> +                       SAM_STAT_TASK_ABORTED,
>>> +                       isci_perform_normal_io_completion
>>> +                       );
>>> +
>>> +               return 0;  /* The I/O was accepted (and failed). */
>> Dan, is this the right status to return to libsas?
>>
>> Looks like it checks (->lldd_execute_task) for non-zero result.
> 
> I think this is self defense that can be deleted, how can a task be
> aborted before it has even returned from the submission routine.
> 
>>> +       }
>>> +       if ((task->dev == NULL) || (task->dev->port == NULL)) {
>>> +
>>> +               /* Indicate SAS_TASK_UNDELIVERED, so that the scsi
>>> midlayer
>>> +                * removes the target.
>>> +                */
>>> +               isci_task_complete_for_upper_layer(
>>> +                       task,
>>> +                       SAS_TASK_UNDELIVERED,
>>> +                       SAS_DEVICE_UNKNOWN,
>>> +                       isci_perform_normal_io_completion
>>> +                       );
>>> +               return 0;  /* The I/O was accepted (and failed). */
>> Same here?
> 
> Yeah, we should always be able to dereference task->dev because ->dev
> is referenced counted against outstanding commands.  If we have a task
> by definition we have a dev.
> 
> [..]
>>> +        */
>>> +       isci_task_build_tmf(&tmf, isci_device, isci_tmf_ssp_lun_reset,
>>> NULL,
>>> +                           NULL);
>>> +
>>> +       #define ISCI_LU_RESET_TIMEOUT_MS 2000 /* 2 second timeout. */
>> Maybe move #define to isci_task.h?
> 
> Ok.
> 
>>> +       ret = isci_task_execute_tmf(isci_host, &tmf,
>>> ISCI_LU_RESET_TIMEOUT_MS);
>>> +
>>> +       if (ret == TMF_RESP_FUNC_COMPLETE)
>>> +               dev_dbg(&isci_host->pdev->dev,
>>> +                       "%s: %p: TMF_LU_RESET passed\n",
>>> +                       __func__, isci_device);
>>> +       else
>>> +               dev_dbg(&isci_host->pdev->dev,
>>> +                       "%s: %p: TMF_LU_RESET failed (%x)\n",
>>> +                       __func__, isci_device, ret);
>>> +
>>> +       return ret;
>>> +}
>>> +
>>> +/**
>>> +
>>> +/*      int (*lldd_clear_nexus_port)(struct asd_sas_port *); */
>>> +int isci_task_clear_nexus_port(struct asd_sas_port *port)
>>> +{
>>> +       return TMF_RESP_FUNC_FAILED;
>>> +}
>>> +
>>> +
>>> +
>>> +int isci_task_clear_nexus_ha(struct sas_ha_struct *ha)
>>> +{
>>> +       return TMF_RESP_FUNC_FAILED;
>>> +}
>>> +
>>> +int isci_task_I_T_nexus_reset(struct domain_device *dev)
>>> +{
>>> +       return TMF_RESP_FUNC_FAILED;
>>> +}
>>> +
>> These will be used during libsas error handling.
> 
> The driver implements abort_task, and promotes the rest to lu_reset.
> Assuming the documentation is not out of date:
> 
>      A SAS LLDD should also implement at least one of the Task
>      Management Functions (TMFs) described in SAM:
> 
> ...how urgent is the need for these other task management routines?

Dan,

Looks like some of these will be used down the scsi error handling
path

scsi_error_handler
  eh_strategy_handler (sas_scsi_recover_host)
   sas_eh_handle_sas_errors

For some of these you may be able to build and execute, something
like

isci_task_abort_task()
{
	isci_task_build_tmf(TMF_ABORT_TASK)
	isci_task_execute_tmf()
}


Also, looks like other libsas drivers do a phy reset from 
lldd_I_T_nexus_reset,
probably ok to eliminate lldd_clear_aca for now.

> 
> Speaking of mandatory libsas methods all other libsas drivers outside
> of aic94xx forgo implementing
> 
>              /* Port and Adapter management */
>              int (*lldd_clear_nexus_port)(struct sas_port *);
>              int (*lldd_clear_nexus_ha)(struct sas_ha_struct *);
> 
>      A SAS LLDD should implement at least one of those.
> 
> ...not clear that at least one of those is mandatory.
> 

Yes, good point, probably ok to initially eliminate these.

Thanks,
David


>>
>>> +/**
>>> + * isci_task_abort_task() - This function is one of the SAS Domain
>>> Template
>>> + *    functions. This function is called by libsas to abort a specified
>>> task.
>>> + * @task: This parameter specifies the SAS task to abort.
>>> + *
>>> + * status, zero indicates success.
>>> + */
>>> +int isci_task_abort_task(struct sas_task *task)
>>> +{
>> scic_lock, flags);
>>
>>
>>> +
>>> +               #define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* half second
>>> timeout. */
>> Maybe move this #define to isci_task.h?
> 
> Ok.
> 
> [..]
>>> + * isci_task_abort_task_set() - This function is one of the SAS Domain
>>> Template
>>> + *    functions. This is one of the Task Management functoins called by
>>> libsas,
>>> + *    to abort all task for the given lun.
>>> + * @d_device: This parameter specifies the domain device associated with
>>> this
>>> + *    request.
>>> + * @lun: This parameter specifies the lun associated with this request.
>>> + *
>>> + * status, zero indicates success.
>>> + */
>>> +int isci_task_abort_task_set(
>>> +       struct domain_device *d_device,
>>> +       u8 *lun)
>>> +{
>>> +       return TMF_RESP_FUNC_FAILED;
>>> +}
>>> +
>>> +
>>> +/**
>>> + * isci_task_clear_aca() - This function is one of the SAS Domain
>>> Template
>>> + *    functions. This is one of the Task Management functoins called by
>>> libsas.
>>> + * @d_device: This parameter specifies the domain device associated with
>>> this
>>> + *    request.
>>> + * @lun: This parameter specifies the lun       associated with this
>>> request.
>>> + *
>>> + * status, zero indicates success.
>>> + */
>>> +int isci_task_clear_aca(
>>> +       struct domain_device *d_device,
>>> +       u8 *lun)
>>> +{
>>> +       return TMF_RESP_FUNC_FAILED;
>>> +}
>>> +
>>> +
>>> +
>>> +/**
>>> + * isci_task_clear_task_set() - This function is one of the SAS Domain
>>> Template
>>> + *    functions. This is one of the Task Management functoins called by
>>> libsas.
>>> + * @d_device: This parameter specifies the domain device associated with
>>> this
>>> + *    request.
>>> + * @lun: This parameter specifies the lun       associated with this
>>> request.
>>> + *
>>> + * status, zero indicates success.
>>> + */
>>> +int isci_task_clear_task_set(
>>> +       struct domain_device *d_device,
>>> +       u8 *lun)
>>> +{
>>> +       return TMF_RESP_FUNC_FAILED;
>>> +}
>> More libsas error recovery functions.
> 
> Same comment as above the driver has the little and the big recovery
> hammers, the medium hammers can be prioritized behind other fixes I
> think.
> 
> --
> Dan
> --
> To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 2/6] isci: task (libsas interface support)
  2011-02-16 18:48       ` David Milburn
@ 2011-02-16 19:35         ` David Milburn
  0 siblings, 0 replies; 36+ messages in thread
From: David Milburn @ 2011-02-16 19:35 UTC (permalink / raw)
  To: Dan Williams
  Cc: David Milburn, james.bottomley, dave.jiang, linux-scsi,
	jacek.danecki, ed.ciechanowski, jeffrey.d.skirvin,
	edmund.nadolski

David Milburn wrote:
> Dan Williams wrote:
>> Thanks David, comments below.

> 
> isci_task_abort_task()
> {
>     isci_task_build_tmf(TMF_ABORT_TASK)

Sorry, I meant to say TMF_ABORT_TASK_SET for
isci_task_abort_task_set().

David

>     isci_task_execute_tmf()
> }
> 
> 

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 1/6] isci: initialization
  2011-02-07  0:34 ` [RFC PATCH 1/6] isci: initialization Dan Williams
@ 2011-02-17  8:22   ` Jeff Garzik
  2011-02-19  0:12     ` Dan Williams
  2011-02-17  8:25   ` Christoph Hellwig
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 36+ messages in thread
From: Jeff Garzik @ 2011-02-17  8:22 UTC (permalink / raw)
  To: Dan Williams
  Cc: james.bottomley, dave.jiang, linux-scsi, jacek.danecki,
	ed.ciechanowski, jeffrey.d.skirvin, edmund.nadolski

On 02/06/2011 07:34 PM, Dan Williams wrote:
> +irqreturn_t isci_isr(int vec, void *data)
> +{
> +	struct isci_host *isci_host
> +		= (struct isci_host *)data;
> +	struct scic_controller_handler_methods *handlers
> +		=&isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR];
> +	irqreturn_t ret = IRQ_NONE;
> +
> +	if (isci_host_get_state(isci_host) != isci_starting
> +	&&  handlers->interrupt_handler) {
> +
> +		if (handlers->interrupt_handler(isci_host->core_controller)) {
> +			if (isci_host_get_state(isci_host) != isci_stopped) {
> +				tasklet_schedule(
> +					&isci_host->completion_tasklet);
> +			} else
> +				dev_dbg(&isci_host->pdev->dev,
> +					"%s: controller stopped\n",
> +					__func__);
> +			ret = IRQ_HANDLED;
> +		}
> +	} else
> +		dev_warn(&isci_host->pdev->dev,
> +			 "%s: get_handler_methods failed, "
> +			 "isci_host->status = 0x%x\n",
> +			 __func__,
> +			 isci_host_get_state(isci_host));
> +
> +	return ret;
> +}

Obviously my grep-fu is failing me...  where is interrupt_handler 
assigned a value?  Creating a pointer for the interrupt handler, rather 
than directly registering the proper callback with the kernel, seems a 
bit odd.

	Jeff



^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 1/6] isci: initialization
  2011-02-07  0:34 ` [RFC PATCH 1/6] isci: initialization Dan Williams
  2011-02-17  8:22   ` Jeff Garzik
@ 2011-02-17  8:25   ` Christoph Hellwig
  2011-02-19  0:23     ` Dan Williams
  2011-03-04 23:35   ` James Bottomley
  2011-03-18 16:51   ` Christoph Hellwig
  3 siblings, 1 reply; 36+ messages in thread
From: Christoph Hellwig @ 2011-02-17  8:25 UTC (permalink / raw)
  To: Dan Williams
  Cc: james.bottomley, dave.jiang, linux-scsi, jacek.danecki,
	ed.ciechanowski, jeffrey.d.skirvin, edmund.nadolski

> +irqreturn_t isci_isr(int vec, void *data)
> +{
> +	struct isci_host *isci_host
> +		= (struct isci_host *)data;
> +	struct scic_controller_handler_methods *handlers
> +		= &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR];
> +	irqreturn_t ret = IRQ_NONE;

> +	if (isci_host_get_state(isci_host) != isci_starting
> +	    && handlers->interrupt_handler) {

Also there should be no need for a state check here.  register_irq
must not happen before you're ready to handle the interrupt.


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 1/6] isci: initialization
  2011-02-17  8:22   ` Jeff Garzik
@ 2011-02-19  0:12     ` Dan Williams
  0 siblings, 0 replies; 36+ messages in thread
From: Dan Williams @ 2011-02-19  0:12 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: james.bottomley, Jiang, Dave, linux-scsi, Danecki, Jacek,
	Ciechanowski, Ed, Skirvin, Jeffrey D, Nadolski, Edmund

On Thu, 2011-02-17 at 00:22 -0800, Jeff Garzik wrote:
> Obviously my grep-fu is failing me...  where is interrupt_handler 
> assigned a value?  Creating a pointer for the interrupt handler, rather 
> than directly registering the proper callback with the kernel, seems a 
> bit odd.
> 

Yes... the following will be showing up in the git tree shortly along
with some other interrupt fixes.

>From bb17c26248fad2e897fb5a351f36bba2d0dcb077 Mon Sep 17 00:00:00 2001
From: Dan Williams <dan.j.williams@intel.com>
Date: Fri, 18 Feb 2011 09:25:05 -0800
Subject: [PATCH] isci: bypass scic_controller_get_handler_methods()

The indirection is unnecessary and broken in the current case that assigns the
handlers based on a not up-to-date pdev->msix_enabled value.

Route the handlers directly to the requisite core routines.

Todo: hook up error interrupt handling

Reported-by: Jeff Garzik <jeff@garzik.org>
Cc: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Edmund Nadolski <edmund.nadolski@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/scsi/isci/core/scic_sds_controller.c |   10 +-
 drivers/scsi/isci/host.c                     |  158 ++++++--------------------
 drivers/scsi/isci/host.h                     |   12 --
 drivers/scsi/isci/init.c                     |    4 +-
 drivers/scsi/isci/isci.h                     |    7 +-
 5 files changed, 45 insertions(+), 146 deletions(-)

diff --git a/drivers/scsi/isci/core/scic_sds_controller.c b/drivers/scsi/isci/core/scic_sds_controller.c
index 6a32d91..cd8017f 100644
--- a/drivers/scsi/isci/core/scic_sds_controller.c
+++ b/drivers/scsi/isci/core/scic_sds_controller.c
@@ -1898,8 +1898,7 @@ static void scic_sds_controller_single_vector_completion_handler(
  *
  * bool true if an interrupt is processed false if no interrupt was processed
  */
-static bool scic_sds_controller_normal_vector_interrupt_handler(
-	struct scic_sds_controller *scic)
+bool scic_sds_controller_isr(struct scic_sds_controller *scic)
 {
 	if (scic_sds_controller_completion_queue_has_entries(scic)) {
 		return true;
@@ -1925,8 +1924,7 @@ static bool scic_sds_controller_normal_vector_interrupt_handler(
  * This is the method provided to handle the completions for a normal MSIX
  *    message.
  */
-static void scic_sds_controller_normal_vector_completion_handler(
-	struct scic_sds_controller *scic)
+void scic_sds_controller_completion_handler(struct scic_sds_controller *scic)
 {
 	/* Empty out the completion queue */
 	if (scic_sds_controller_completion_queue_has_entries(scic))
@@ -2582,9 +2580,9 @@ enum sci_status scic_controller_get_handler_methods(
 			status = SCI_SUCCESS;
 		} else if (message_count == 2) {
 			handler_methods[0].interrupt_handler
-				= scic_sds_controller_normal_vector_interrupt_handler;
+				= scic_sds_controller_isr;
 			handler_methods[0].completion_handler
-				= scic_sds_controller_normal_vector_completion_handler;
+				= scic_sds_controller_completion_handler;
 
 			handler_methods[1].interrupt_handler
 				= scic_sds_controller_error_vector_interrupt_handler;
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c
index 6f16f4d..b66e620 100644
--- a/drivers/scsi/isci/host.c
+++ b/drivers/scsi/isci/host.c
@@ -62,80 +62,49 @@
 #include "request.h"
 #include "host.h"
 
-/**
- * isci_isr() - This function is the interrupt service routine for the
- *    controller. It schedules the tasklet and returns.
- * @vec: This parameter specifies the interrupt vector.
- * @data: This parameter specifies the ISCI host object.
- *
- * IRQ_HANDLED if out interrupt otherwise, IRQ_NONE
- */
-irqreturn_t isci_isr(int vec, void *data)
+irqreturn_t isci_msix_isr(int vec, void *data)
 {
-	struct isci_host *isci_host
-		= (struct isci_host *)data;
-	struct scic_controller_handler_methods *handlers
-		= &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR];
-	irqreturn_t ret = IRQ_NONE;
-
-	if (isci_host_get_state(isci_host) != isci_starting
-	    && handlers->interrupt_handler) {
-
-		if (handlers->interrupt_handler(isci_host->core_controller)) {
-			if (isci_host_get_state(isci_host) != isci_stopped) {
-				tasklet_schedule(
-					&isci_host->completion_tasklet);
-			} else
-				dev_dbg(&isci_host->pdev->dev,
-					"%s: controller stopped\n",
-					__func__);
-			ret = IRQ_HANDLED;
+	struct isci_host *ihost = data;
+	struct scic_sds_controller *scic = ihost->core_controller;
+
+	if (isci_host_get_state(ihost) != isci_starting) {
+		if (scic_sds_controller_isr(scic)) {
+			if (isci_host_get_state(ihost) != isci_stopped)
+				tasklet_schedule(&ihost->completion_tasklet);
+			else
+				dev_dbg(&ihost->pdev->dev,
+					"%s: controller stopped\n", __func__);
 		}
-	} else
-		dev_warn(&isci_host->pdev->dev,
-			 "%s: get_handler_methods failed, "
-			 "isci_host->status = 0x%x\n",
-			 __func__,
-			 isci_host_get_state(isci_host));
+	}
 
-	return ret;
+	return IRQ_HANDLED;
 }
 
-irqreturn_t isci_legacy_isr(int vec, void *data)
+irqreturn_t isci_intx_isr(int vec, void *data)
 {
 	struct pci_dev *pdev = data;
-	struct isci_host *isci_host;
-	struct scic_controller_handler_methods *handlers;
+	struct isci_host *ihost;
 	irqreturn_t ret = IRQ_NONE;
 
-	/*
-	 *  Since this is a legacy interrupt, either or both
-	 *  controllers could have triggered it.  Thus, we have to call
-	 *  the legacy interrupt handler for all controllers on the
-	 *  PCI function.
-	 */
-	for_each_isci_host(isci_host, pdev) {
-		handlers = &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR];
-
-		if (isci_host_get_state(isci_host) != isci_starting
-		    && handlers->interrupt_handler) {
-
-			if (handlers->interrupt_handler(isci_host->core_controller)) {
-				if (isci_host_get_state(isci_host) != isci_stopped) {
-					tasklet_schedule(
-						&isci_host->completion_tasklet);
-				} else
-					dev_dbg(&isci_host->pdev->dev,
+	for_each_isci_host(ihost, pdev) {
+		struct scic_sds_controller *scic = ihost->core_controller;
+
+		if (isci_host_get_state(ihost) != isci_starting) {
+			if (scic_sds_controller_isr(scic)) {
+				if (isci_host_get_state(ihost) != isci_stopped)
+					tasklet_schedule(&ihost->completion_tasklet);
+				else
+					dev_dbg(&ihost->pdev->dev,
 						"%s: controller stopped\n",
 						__func__);
 				ret = IRQ_HANDLED;
 			}
 		} else
-			dev_warn(&isci_host->pdev->dev,
+			dev_warn(&ihost->pdev->dev,
 				 "%s: get_handler_methods failed, "
-				 "isci_host->status = 0x%x\n",
+				 "ihost->status = 0x%x\n",
 				 __func__,
-				 isci_host_get_state(isci_host));
+				 isci_host_get_state(ihost));
 	}
 	return ret;
 }
@@ -166,34 +135,9 @@ void isci_host_start_complete(
 
 }
 
-
-
-/**
- * isci_host_scan_finished() - This function is one of the SCSI Host Template
- *    functions. The SCSI midlayer calls this function during a target scan,
- *    approx. once every 10 millisecs.
- * @shost: This parameter specifies the SCSI host being scanned
- * @time: This parameter specifies the number of ticks since the scan started.
- *
- * scan status, zero indicates the SCSI midlayer should continue to poll,
- * otherwise assume controller is ready.
- */
-int isci_host_scan_finished(
-	struct Scsi_Host *shost,
-	unsigned long time)
+int isci_host_scan_finished(struct Scsi_Host *shost, unsigned long time)
 {
-	struct isci_host *isci_host
-		= isci_host_from_sas_ha(SHOST_TO_SAS_HA(shost));
-
-	struct scic_controller_handler_methods *handlers
-		= &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR];
-
-	if (handlers->interrupt_handler == NULL) {
-		dev_err(&isci_host->pdev->dev,
-			"%s: scic_controller_get_handler_methods failed\n",
-			__func__);
-		return 1;
-	}
+	struct isci_host *isci_host = isci_host_from_sas_ha(SHOST_TO_SAS_HA(shost));
 
 	/**
 	 * check interrupt_handler's status and call completion_handler if true,
@@ -204,8 +148,8 @@ int isci_host_scan_finished(
 	 * continue to return zero from thee scan_finished routine until
 	 * the scic_cb_controller_start_complete() call comes from the core.
 	 **/
-	if (handlers->interrupt_handler(isci_host->core_controller))
-		handlers->completion_handler(isci_host->core_controller);
+	if (scic_sds_controller_isr(isci_host->core_controller))
+		scic_sds_controller_completion_handler(isci_host->core_controller);
 
 	if (isci_starting == isci_host_get_state(isci_host)
 	    && time < (HZ * 10)) {
@@ -363,8 +307,6 @@ static int isci_host_mdl_allocate_coherent(
 static void isci_host_completion_routine(unsigned long data)
 {
 	struct isci_host *isci_host = (struct isci_host *)data;
-	struct scic_controller_handler_methods *handlers
-		= &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR];
 	struct list_head completed_request_list;
 	struct list_head aborted_request_list;
 	struct list_head *current_position;
@@ -378,11 +320,8 @@ static void isci_host_completion_routine(unsigned long data)
 
 	spin_lock_irq(&isci_host->scic_lock);
 
-	if (handlers->completion_handler) {
-		handlers->completion_handler(
-			isci_host->core_controller
-			);
-	}
+	scic_sds_controller_completion_handler(isci_host->core_controller);
+
 	/* Take the lists of completed I/Os from the host. */
 	list_splice_init(&isci_host->requests_to_complete,
 			 &completed_request_list);
@@ -544,8 +483,6 @@ int isci_host_init(struct isci_host *isci_host)
 	enum sci_status status;
 	struct scic_sds_controller *controller;
 	struct scic_sds_port *scic_port;
-	struct scic_controller_handler_methods *handlers
-		= &isci_host->scic_irq_handlers[0];
 	union scic_oem_parameters scic_oem_params;
 	union scic_user_parameters scic_user_params;
 	const struct firmware *fw = NULL;
@@ -691,35 +628,8 @@ int isci_host_init(struct isci_host *isci_host)
 		goto out;
 	}
 
-	/* @todo: use both MSI-X interrupts, and don't do indirect
-	 * calls to the handlers just register direct calls
-	 */
-	if (isci_host->pdev->msix_enabled) {
-		status = scic_controller_get_handler_methods(
-			SCIC_MSIX_INTERRUPT_TYPE,
-			SCI_MSIX_DOUBLE_VECTOR,
-			handlers
-			);
-	} else {
-		status = scic_controller_get_handler_methods(
-			SCIC_LEGACY_LINE_INTERRUPT_TYPE,
-			0,
-			handlers
-			);
-	}
-
-	if (status != SCI_SUCCESS) {
-		handlers->interrupt_handler = NULL;
-		handlers->completion_handler = NULL;
-		dev_err(&isci_host->pdev->dev,
-			"%s: scic_controller_get_handler_methods failed\n",
-			__func__);
-	}
-
 	tasklet_init(&isci_host->completion_tasklet,
-		     isci_host_completion_routine,
-		     (unsigned long)isci_host
-		     );
+		     isci_host_completion_routine, (unsigned long)isci_host);
 
 	INIT_LIST_HEAD(&(isci_host->mdl_struct_list));
 
diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h
index 4f4b99d..421d3de 100644
--- a/drivers/scsi/isci/host.h
+++ b/drivers/scsi/isci/host.h
@@ -53,13 +53,6 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-/**
- * This file contains the isci_module initialization routines.
- *
- * host.h
- */
-
-
 
 #if !defined(_SCI_HOST_H_)
 #define _SCI_HOST_H_
@@ -79,10 +72,6 @@
 #define SCI_SCU_BAR_SIZE  (4*1024*1024)
 #define SCI_IO_SPACE_BAR0 2
 #define SCI_IO_SPACE_BAR1 3
-#define SCI_MSIX_NORMAL_VECTOR 0
-#define SCI_MSIX_ERROR_VECTOR 1
-#define SCI_MSIX_SINGLE_VECTOR 1
-#define SCI_MSIX_DOUBLE_VECTOR 2
 #define ISCI_CAN_QUEUE_VAL 250 /* < SCI_MAX_IO_REQUESTS ? */
 #define SCIC_CONTROLLER_STOP_TIMEOUT 5000
 
@@ -96,7 +85,6 @@ struct coherent_memory_info {
 
 struct isci_host {
 	struct scic_sds_controller *core_controller;
-	struct scic_controller_handler_methods scic_irq_handlers[SCI_NUM_MSI_X_INT];
 	union scic_oem_parameters oem_parameters;
 
 	int id; /* unique within a given pci device */
diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c
index e3d9b15..f2bd92b 100644
--- a/drivers/scsi/isci/init.c
+++ b/drivers/scsi/isci/init.c
@@ -334,7 +334,7 @@ static int isci_setup_interrupts(struct pci_dev *pdev)
 		BUG_ON(!isci_host);
 
 		/* @todo: need to handle error case. */
-		err = devm_request_irq(&pdev->dev, msix->vector, isci_isr, 0,
+		err = devm_request_irq(&pdev->dev, msix->vector, isci_msix_isr, 0,
 				       DRV_NAME"-msix", isci_host);
 		if (!err)
 			continue;
@@ -353,7 +353,7 @@ static int isci_setup_interrupts(struct pci_dev *pdev)
 	return 0;
 
  intx:
-	err = devm_request_irq(&pdev->dev, pdev->irq, isci_legacy_isr,
+	err = devm_request_irq(&pdev->dev, pdev->irq, isci_intx_isr,
 			       IRQF_SHARED, DRV_NAME"-intx", pdev);
 
 	return err;
diff --git a/drivers/scsi/isci/isci.h b/drivers/scsi/isci/isci.h
index 6aee3c9..3dc0f6c 100644
--- a/drivers/scsi/isci/isci.h
+++ b/drivers/scsi/isci/isci.h
@@ -113,8 +113,11 @@ struct isci_firmware {
 	u8 sas_addrs_size;
 };
 
-irqreturn_t isci_isr(int vec, void *data);
-irqreturn_t isci_legacy_isr(int vec, void *data);
+irqreturn_t isci_msix_isr(int vec, void *data);
+irqreturn_t isci_intx_isr(int vec, void *data);
+
+bool scic_sds_controller_isr(struct scic_sds_controller *scic);
+void scic_sds_controller_completion_handler(struct scic_sds_controller *scic);
 
 enum sci_status isci_parse_oem_parameters(
 	union scic_oem_parameters *oem_params,
-- 
1.7.2.3




^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 1/6] isci: initialization
  2011-02-17  8:25   ` Christoph Hellwig
@ 2011-02-19  0:23     ` Dan Williams
  0 siblings, 0 replies; 36+ messages in thread
From: Dan Williams @ 2011-02-19  0:23 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: james.bottomley, Jiang, Dave, linux-scsi, Danecki, Jacek,
	Ciechanowski, Ed, Skirvin, Jeffrey D, Nadolski, Edmund

On Thu, 2011-02-17 at 00:25 -0800, Christoph Hellwig wrote:
> > +irqreturn_t isci_isr(int vec, void *data)
> > +{
> > +	struct isci_host *isci_host
> > +		= (struct isci_host *)data;
> > +	struct scic_controller_handler_methods *handlers
> > +		= &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR];
> > +	irqreturn_t ret = IRQ_NONE;
> 
> > +	if (isci_host_get_state(isci_host) != isci_starting
> > +	    && handlers->interrupt_handler) {
> 
> Also there should be no need for a state check here.  register_irq
> must not happen before you're ready to handle the interrupt.
> 

Yes, the state checks are not needed.  Here is the proposed clean up.

>From 2f89431766a7178e4a1beb87d3606d020f981e94 Mon Sep 17 00:00:00 2001
From: Dan Williams <dan.j.williams@intel.com>
Date: Fri, 18 Feb 2011 09:25:07 -0800
Subject: [PATCH] isci: cleanup "starting" state handling

The lldd actively disallows requests in the "starting" state.  Retrying
or holding off commands in this state is sub-optimal:
1/ it adds another state check to the fast path
2/ retrying can cause libsas to give up

However, isci's ->lldd_dev_found() routine already waits for controller
start to complete before allowing further progress.  Checking the
"starting" state in isci_task_execute_task and the isr is redundant and
misleading.  Clean this up and introduce a controller-wide event queue
to start reeling in "completion" proliferation in the driver.

The "stopping" state cleanups are in a similar vein, rely on the the isr
and other paths being precluded from occurring rather than implementing
state checking logic.

Reported-by: Christoph Hellwig <hch@infradead.org>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Edmund Nadolski <edmund.nadolski@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/scsi/isci/host.c          |  145 +++++++++++++------------------------
 drivers/scsi/isci/host.h          |   17 ++++-
 drivers/scsi/isci/remote_device.c |    4 +-
 drivers/scsi/isci/task.c          |    3 +-
 4 files changed, 67 insertions(+), 102 deletions(-)

diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c
index b66e620..dbdc3ba 100644
--- a/drivers/scsi/isci/host.c
+++ b/drivers/scsi/isci/host.c
@@ -67,15 +67,8 @@ irqreturn_t isci_msix_isr(int vec, void *data)
 	struct isci_host *ihost = data;
 	struct scic_sds_controller *scic = ihost->core_controller;
 
-	if (isci_host_get_state(ihost) != isci_starting) {
-		if (scic_sds_controller_isr(scic)) {
-			if (isci_host_get_state(ihost) != isci_stopped)
-				tasklet_schedule(&ihost->completion_tasklet);
-			else
-				dev_dbg(&ihost->pdev->dev,
-					"%s: controller stopped\n", __func__);
-		}
-	}
+	if (scic_sds_controller_isr(scic))
+		tasklet_schedule(&ihost->completion_tasklet);
 
 	return IRQ_HANDLED;
 }
@@ -89,22 +82,10 @@ irqreturn_t isci_intx_isr(int vec, void *data)
 	for_each_isci_host(ihost, pdev) {
 		struct scic_sds_controller *scic = ihost->core_controller;
 
-		if (isci_host_get_state(ihost) != isci_starting) {
-			if (scic_sds_controller_isr(scic)) {
-				if (isci_host_get_state(ihost) != isci_stopped)
-					tasklet_schedule(&ihost->completion_tasklet);
-				else
-					dev_dbg(&ihost->pdev->dev,
-						"%s: controller stopped\n",
-						__func__);
-				ret = IRQ_HANDLED;
-			}
-		} else
-			dev_warn(&ihost->pdev->dev,
-				 "%s: get_handler_methods failed, "
-				 "ihost->status = 0x%x\n",
-				 __func__,
-				 isci_host_get_state(ihost));
+		if (scic_sds_controller_isr(scic)) {
+			tasklet_schedule(&ihost->completion_tasklet);
+			ret = IRQ_HANDLED;
+		}
 	}
 	return ret;
 }
@@ -118,26 +99,19 @@ irqreturn_t isci_intx_isr(int vec, void *data)
  *    core library.
  *
  */
-void isci_host_start_complete(
-	struct isci_host *isci_host,
-	enum sci_status completion_status)
+void isci_host_start_complete(struct isci_host *ihost, enum sci_status completion_status)
 {
-	if (completion_status == SCI_SUCCESS) {
-		dev_dbg(&isci_host->pdev->dev,
-			"%s: completion_status: SCI_SUCCESS\n", __func__);
-		isci_host_change_state(isci_host, isci_ready);
-		complete_all(&isci_host->start_complete);
-	} else
-		dev_err(&isci_host->pdev->dev,
-			"controller start failed with "
-			"completion_status = 0x%x;",
-			completion_status);
-
+	if (completion_status != SCI_SUCCESS)
+		dev_info(&ihost->pdev->dev,
+			"controller start timed out, continuing...\n");
+	isci_host_change_state(ihost, isci_ready);
+	clear_bit(IHOST_START_PENDING, &ihost->flags);
+	wake_up(&ihost->eventq);
 }
 
 int isci_host_scan_finished(struct Scsi_Host *shost, unsigned long time)
 {
-	struct isci_host *isci_host = isci_host_from_sas_ha(SHOST_TO_SAS_HA(shost));
+	struct isci_host *ihost = isci_host_from_sas_ha(SHOST_TO_SAS_HA(shost));
 
 	/**
 	 * check interrupt_handler's status and call completion_handler if true,
@@ -148,61 +122,44 @@ int isci_host_scan_finished(struct Scsi_Host *shost, unsigned long time)
 	 * continue to return zero from thee scan_finished routine until
 	 * the scic_cb_controller_start_complete() call comes from the core.
 	 **/
-	if (scic_sds_controller_isr(isci_host->core_controller))
-		scic_sds_controller_completion_handler(isci_host->core_controller);
+	if (scic_sds_controller_isr(ihost->core_controller))
+		scic_sds_controller_completion_handler(ihost->core_controller);
 
-	if (isci_starting == isci_host_get_state(isci_host)
-	    && time < (HZ * 10)) {
-		dev_dbg(&isci_host->pdev->dev,
-			"%s: isci_host->status = %d, time = %ld\n",
-			     __func__, isci_host_get_state(isci_host), time);
+	if (test_bit(IHOST_START_PENDING, &ihost->flags) && time < HZ*10) {
+		dev_dbg(&ihost->pdev->dev,
+			"%s: ihost->status = %d, time = %ld\n",
+			     __func__, isci_host_get_state(ihost), time);
 		return 0;
 	}
 
 
-	dev_dbg(&isci_host->pdev->dev,
-		"%s: isci_host->status = %d, time = %ld\n",
-		 __func__, isci_host_get_state(isci_host), time);
+	dev_dbg(&ihost->pdev->dev,
+		"%s: ihost->status = %d, time = %ld\n",
+		 __func__, isci_host_get_state(ihost), time);
 
-	scic_controller_enable_interrupts(isci_host->core_controller);
+	scic_controller_enable_interrupts(ihost->core_controller);
 
 	return 1;
 
 }
 
-
-/**
- * isci_host_scan_start() - This function is one of the SCSI Host Template
- *    function, called by the SCSI mid layer berfore a target scan begins. The
- *    core library controller start routine is called from here.
- * @shost: This parameter specifies the SCSI host to be scanned
- *
- */
 void isci_host_scan_start(struct Scsi_Host *shost)
 {
-	struct isci_host *isci_host;
-
-	isci_host = isci_host_from_sas_ha(SHOST_TO_SAS_HA(shost));
-	isci_host_change_state(isci_host, isci_starting);
+	struct isci_host *ihost = isci_host_from_sas_ha(SHOST_TO_SAS_HA(shost));
+	struct scic_sds_controller *scic = ihost->core_controller;
+	unsigned long tmo = scic_controller_get_suggested_start_timeout(scic);
 
-	scic_controller_disable_interrupts(isci_host->core_controller);
-	init_completion(&isci_host->start_complete);
-	scic_controller_start(
-		isci_host->core_controller,
-		scic_controller_get_suggested_start_timeout(
-			isci_host->core_controller)
-		);
+	set_bit(IHOST_START_PENDING, &ihost->flags);
+	scic_controller_disable_interrupts(ihost->core_controller);
+	scic_controller_start(scic, tmo);
 }
 
-void isci_host_stop_complete(
-	struct isci_host *isci_host,
-	enum sci_status completion_status)
+void isci_host_stop_complete(struct isci_host *ihost, enum sci_status completion_status)
 {
-	isci_host_change_state(isci_host, isci_stopped);
-	scic_controller_disable_interrupts(
-		isci_host->core_controller
-		);
-	complete(&isci_host->stop_complete);
+	isci_host_change_state(ihost, isci_stopped);
+	scic_controller_disable_interrupts(ihost->core_controller);
+	clear_bit(IHOST_STOP_PENDING, &ihost->flags);
+	wake_up(&ihost->eventq);
 }
 
 static struct coherent_memory_info *isci_host_alloc_mdl_struct(
@@ -370,31 +327,26 @@ static void isci_host_completion_routine(unsigned long data)
 
 }
 
-void isci_host_deinit(
-	struct isci_host *isci_host)
+void isci_host_deinit(struct isci_host *ihost)
 {
+	struct scic_sds_controller *scic = ihost->core_controller;
 	int i;
 
-	isci_host_change_state(isci_host, isci_stopping);
+	isci_host_change_state(ihost, isci_stopping);
 	for (i = 0; i < SCI_MAX_PORTS; i++) {
-		struct isci_port *port = &isci_host->isci_ports[i];
-		struct isci_remote_device *device, *tmpdev;
-		list_for_each_entry_safe(device, tmpdev,
-					 &port->remote_dev_list, node) {
-			isci_remote_device_change_state(device, isci_stopping);
-			isci_remote_device_stop(device);
+		struct isci_port *port = &ihost->isci_ports[i];
+		struct isci_remote_device *idev, *d;
+
+		list_for_each_entry_safe(idev, d, &port->remote_dev_list, node) {
+			isci_remote_device_change_state(idev, isci_stopping);
+			isci_remote_device_stop(idev);
 		}
 	}
 
-	/* stop the comtroller and wait for completion.  */
-	init_completion(&isci_host->stop_complete);
-	scic_controller_stop(
-		isci_host->core_controller,
-		SCIC_CONTROLLER_STOP_TIMEOUT
-		);
-	wait_for_completion(&isci_host->stop_complete);
-	/* next, reset the controller.           */
-	scic_controller_reset(isci_host->core_controller);
+	set_bit(IHOST_STOP_PENDING, &ihost->flags);
+	scic_controller_stop(scic, SCIC_CONTROLLER_STOP_TIMEOUT);
+	wait_for_stop(ihost);
+	scic_controller_reset(scic);
 }
 
 static int isci_verify_firmware(const struct firmware *fw,
@@ -506,6 +458,7 @@ int isci_host_init(struct isci_host *isci_host)
 	spin_lock_init(&isci_host->state_lock);
 	spin_lock_init(&isci_host->scic_lock);
 	spin_lock_init(&isci_host->queue_lock);
+	init_waitqueue_head(&isci_host->eventq);
 
 	isci_host_change_state(isci_host, isci_starting);
 	isci_host->can_queue = ISCI_CAN_QUEUE_VAL;
diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h
index 421d3de..26768c5 100644
--- a/drivers/scsi/isci/host.h
+++ b/drivers/scsi/isci/host.h
@@ -109,13 +109,15 @@ struct isci_host {
 	u8 sas_addr[SAS_ADDR_SIZE];
 
 	enum isci_status status;
+	#define IHOST_START_PENDING 0
+	#define IHOST_STOP_PENDING 1
+	unsigned long flags;
+	wait_queue_head_t eventq;
 	struct Scsi_Host *shost;
 	struct tasklet_struct completion_tasklet;
 	struct list_head mdl_struct_list;
 	struct list_head requests_to_complete;
 	struct list_head requests_to_abort;
-	struct completion stop_complete;
-	struct completion start_complete;
 	spinlock_t scic_lock;
 	struct isci_host *next;
 };
@@ -202,6 +204,17 @@ static inline void isci_host_can_dequeue(
 	spin_unlock_irqrestore(&isci_host->queue_lock, flags);
 }
 
+static inline void wait_for_start(struct isci_host *ihost)
+{
+	wait_event(ihost->eventq, !test_bit(IHOST_START_PENDING, &ihost->flags));
+}
+
+static inline void wait_for_stop(struct isci_host *ihost)
+{
+	wait_event(ihost->eventq, !test_bit(IHOST_STOP_PENDING, &ihost->flags));
+}
+
+
 /**
  * isci_host_from_sas_ha() - This accessor retrieves the isci_host object
  *    reference from the Linux sas_ha_struct reference.
diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c
index dbf3c82..936f229 100644
--- a/drivers/scsi/isci/remote_device.c
+++ b/drivers/scsi/isci/remote_device.c
@@ -507,6 +507,8 @@ int isci_remote_device_found(struct domain_device *domain_dev)
 	dev_dbg(&isci_host->pdev->dev,
 		"%s: domain_device = %p\n", __func__, domain_dev);
 
+	wait_for_start(isci_host);
+
 	sas_port = domain_dev->port;
 	sas_phy = list_first_entry(&sas_port->phy_list, struct asd_sas_phy,
 				   port_phy_el);
@@ -560,8 +562,6 @@ int isci_remote_device_found(struct domain_device *domain_dev)
 		return -ENODEV;
 	}
 
-	wait_for_completion(&isci_host->start_complete);
-
 	return 0;
 }
 /**
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c
index 5e6f558..6f98f6c 100644
--- a/drivers/scsi/isci/task.c
+++ b/drivers/scsi/isci/task.c
@@ -164,8 +164,7 @@ int isci_task_execute_task(struct sas_task *task, int num, gfp_t gfp_flags)
 		 * for the quiesce spinlock.
 		 */
 
-		if (isci_host_get_state(isci_host) == isci_starting ||
-		    (device && ((isci_remote_device_get_state(device) == isci_ready) ||
+		if ((device && ((isci_remote_device_get_state(device) == isci_ready) ||
 		    (isci_remote_device_get_state(device) == isci_host_quiesce)))) {
 
 			/* Forces a retry from scsi mid layer. */
-- 
1.7.2.3




^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 1/6] isci: initialization
  2011-02-07  0:34 ` [RFC PATCH 1/6] isci: initialization Dan Williams
  2011-02-17  8:22   ` Jeff Garzik
  2011-02-17  8:25   ` Christoph Hellwig
@ 2011-03-04 23:35   ` James Bottomley
  2011-03-08  1:51     ` Dan Williams
  2011-03-18 16:51   ` Christoph Hellwig
  3 siblings, 1 reply; 36+ messages in thread
From: James Bottomley @ 2011-03-04 23:35 UTC (permalink / raw)
  To: Dan Williams
  Cc: dave.jiang, linux-scsi, jacek.danecki, ed.ciechanowski,
	jeffrey.d.skirvin, edmund.nadolski

On Sun, 2011-02-06 at 16:34 -0800, Dan Williams wrote:
> Driver and controller initialization.

Firstly, there's a lot of docbook abuse in here:


> +/**
> + * isci_isr() - This function is the interrupt service routine for the
> + *    controller. It schedules the tasklet and returns.
> + * @vec: This parameter specifies the interrupt vector.
> + * @data: This parameter specifies the ISCI host object.
> + *
> + * IRQ_HANDLED if out interrupt otherwise, IRQ_NONE

Unless Randy has fixed it, docbook headers don't allow continuation
lines like this.  Then it's <function_name> - <short description> and
<function_name> shouldn't have braces.

Secondly, could you pass this through lindent; stuff like this:


> +		status = scic_controller_get_handler_methods(
> +			SCIC_MSIX_INTERRUPT_TYPE,
> +			SCI_MSIX_DOUBLE_VECTOR,
> +			handlers
> +			);

and


> +	if (!(scu_index >= 0
> +	      && scu_index < SCI_MAX_CONTROLLERS
> +	      && oem_params != NULL)) {

Keeps leaping out at me.

The headers also need to be standard too:


> +#if !defined(_SCI_HOST_H_)
> +#define _SCI_HOST_H_


should be

#ifdef _SCI_HOST_H
#define _SCI_HOST_H


> +static void isci_host_build_mde(
> +	struct sci_physical_memory_descriptor *mde_struct,
> +	struct coherent_memory_info *mdl_struct)
> +{
> +	unsigned long address = 0;
> +	dma_addr_t dma_addr = 0;
> +
> +	address = (unsigned long)mdl_struct->vaddr;
> +	dma_addr = mdl_struct->dma_handle;
> +
> +	/* to satisfy the alignment. */
> +	if ((address % mde_struct->constant_memory_alignment) != 0) {
> +		int align_offset
> +			= (mde_struct->constant_memory_alignment
> +			   - (address % mde_struct->constant_memory_alignment));
> +		address += align_offset;
> +		dma_addr += align_offset;
> +	}
> +
> +	mde_struct->virtual_address = (void *)address;
> +	mde_struct->physical_address = dma_addr;
> +	mdl_struct->mde = mde_struct;
> +}

This is a weird function.  Why not just do ALIGN(x,
mde_struct->constant_memory_alignment) and further, why not do it in
isci_host_alloc_mdl_struct()?


> +#if defined(CONFIG_PBG_HBA_A0)
> +int isci_si_rev = ISCI_SI_REVA0;
> +#elif defined(CONFIG_PBG_HBA_A2)
> +int isci_si_rev = ISCI_SI_REVA2;
> +#else
> +int isci_si_rev = ISCI_SI_REVB0;
> +#endif

There aren't any config entries for PGB_HBA_X as far as I can see ... is
this just some sort of remnant?


> +static struct isci_host *isci_host_by_id(struct pci_dev *pdev, int id)
> +{
> +	struct isci_host *h;
> +
> +	for_each_isci_host(h, pdev)
> +		if (h->id == id)
> +			return h;
> +	return NULL;
> +}

How many hosts per PCI can their actually be?  Because all this list
based stuff looks a bit heavyweight, and if it's only up to 8 or so, a
fixed size list would be far easier.


> +	for (i = 0; i < num_msix; i++) {
> +		int id = i / SCI_NUM_MSI_X_INT;
> +		struct msix_entry *msix = &pci_info->msix_entries[i];
> +		struct isci_host *isci_host = isci_host_by_id(pdev, id);
> +
> +		BUG_ON(!isci_host);
> +
> +		/* @todo: need to handle error case. */
> +		err = devm_request_irq(&pdev->dev, msix->vector, isci_isr, 0,
> +				       DRV_NAME"-msix", isci_host);
> +		if (!err)
> +			continue;
> +
> +		dev_info(&pdev->dev, "msix setup failed falling back to intx\n");
> +		while (i--) {
> +			id = i / SCI_NUM_MSI_X_INT;
> +			isci_host = isci_host_by_id(pdev, id);
> +			msix = &pci_info->msix_entries[i];
> +			devm_free_irq(&pdev->dev, msix->vector, isci_host);
> +		}
> +		pci_disable_msix(pdev);
> +		goto intx;
> +	}

This is really non-standard error handling.  We usually do the
initialisations inline and the error cleanup out of line with a goto.
Having both in a for loop, with an unwind while causes a double take.


> +#ifdef ISCI_SLAVE_ALLOC
> +extern int ISCI_SLAVE_ALLOC(struct scsi_device *scsi_dev);
> +#endif /* ISCI_SLAVE_ALLOC */
> +
> +#ifdef ISCI_SLAVE_DESTROY
> +extern void ISCI_SLAVE_DESTROY(struct scsi_device *scsi_dev);
> +#endif  /* ISCI_SLAVE_DESTROY */

This is just left over debuuging, right?


> +#include <asm/string.h>

Should be <linux/string.h>


> +struct isci_host {
> +	struct scic_sds_controller *core_controller;
> +	struct scic_controller_handler_methods scic_irq_handlers[SCI_NUM_MSI_X_INT];
> +	union scic_oem_parameters oem_parameters;
> +
> +	int id; /* unique within a given pci device */
> +	struct isci_timer_list timer_list_struct;
> +	void *core_ctrl_memory;
> +	struct dma_pool *dma_pool;
> +	unsigned int dma_pool_alloc_size;
> +	struct isci_phy phys[SCI_MAX_PHYS];
> +
> +	/* isci_ports and sas_ports are implicitly parallel to the
> +	 * ports maintained by the core
> +	 */
> +	struct isci_port isci_ports[SCI_MAX_PORTS];
> +	struct asd_sas_port sas_ports[SCI_MAX_PORTS];
> +	struct sas_ha_struct sas_ha;
> +
> +	int can_queue;
> +	spinlock_t queue_lock;
> +	spinlock_t state_lock;
> +
> +	struct pci_dev *pdev;
> +	u8 sas_addr[SAS_ADDR_SIZE];
> +
> +	enum isci_status status;
> +	struct Scsi_Host *shost;
> +	struct tasklet_struct completion_tasklet;
> +	struct list_head mdl_struct_list;
> +	struct list_head requests_to_complete;
> +	struct list_head requests_to_abort;
> +	struct completion stop_complete;
> +	struct completion start_complete;
> +	spinlock_t scic_lock;
> +	struct isci_host *next;
> +};

The locking in this whole driver is a bit on the heavy side
(particularly when I look at the further patches for the device).  But
basically, I've no idea from this layout what any of these are
protecting.


> +	/* @todo: use both MSI-X interrupts, and don't do indirect
> +	 * calls to the handlers just register direct calls
> +	 */
> +	if (isci_host->pdev->msix_enabled) {
> +		status = scic_controller_get_handler_methods(
> +			SCIC_MSIX_INTERRUPT_TYPE,
> +			SCI_MSIX_DOUBLE_VECTOR,
> +			handlers
> +			);

Agree wholeheartedly with the comment.  The indirect function calling
looks a complete mess.  One question, though:  where is
scic_controller_get_handler_methods() defined?  I can't find it in any
of the six patches.

I also wonder about the tasklet processing stuff ... wouldn't threaded
interrupts be simpler?

James



^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 1/6] isci: initialization
  2011-03-04 23:35   ` James Bottomley
@ 2011-03-08  1:51     ` Dan Williams
  0 siblings, 0 replies; 36+ messages in thread
From: Dan Williams @ 2011-03-08  1:51 UTC (permalink / raw)
  To: James Bottomley
  Cc: Jiang, Dave, linux-scsi, Danecki, Jacek, Ciechanowski, Ed,
	Skirvin, Jeffrey D, Nadolski, Edmund

Thanks, comments below...

On Fri, 2011-03-04 at 15:35 -0800, James Bottomley wrote:
> On Sun, 2011-02-06 at 16:34 -0800, Dan Williams wrote:
> > Driver and controller initialization.
> 
> Firstly, there's a lot of docbook abuse in here:
> 
> 
> > +/**
> > + * isci_isr() - This function is the interrupt service routine for the
> > + *    controller. It schedules the tasklet and returns.
> > + * @vec: This parameter specifies the interrupt vector.
> > + * @data: This parameter specifies the ISCI host object.
> > + *
> > + * IRQ_HANDLED if out interrupt otherwise, IRQ_NONE
> 
> Unless Randy has fixed it, docbook headers don't allow continuation
> lines like this.  Then it's <function_name> - <short description> and
> <function_name> shouldn't have braces.

Not to say that there is not other docbook abuse (like /** usage without
a description block), but Johannes fixed this particular issue in commit
6423133b "kernel-doc: allow multi-line declaration purpose
descriptions".  The braces are in the example in
Documentation/kernel-doc-nano-HOWTO.txt.  I'd just as soon leave them if
they are not problematic.

> Secondly, could you pass this through lindent; stuff like this:
> 
> 
> > +		status = scic_controller_get_handler_methods(
> > +			SCIC_MSIX_INTERRUPT_TYPE,
> > +			SCI_MSIX_DOUBLE_VECTOR,
> > +			handlers
> > +			);
> 
> and
> 
> 
> > +	if (!(scu_index >= 0
> > +	      && scu_index < SCI_MAX_CONTROLLERS
> > +	      && oem_params != NULL)) {
> 
> Keeps leaping out at me.

Me as well.  The initial pass at addressing these things was done with
uncrustify [1] and its Linux rules.  Unfortunately it chose this
formatting and since checkpatch did not complain we are having to go
back and clean this stuff up manually.

Lindent does not do much better:

        /* check for valid inputs */
        if (!(scu_index >= 0
              && scu_index < SCI_MAX_CONTROLLERS && oem_params != NULL)) {
                return SCI_FAILURE;
        }

Ideally this would be 

        /* check for valid inputs */
        if (scu_index < 0 || scu_index >= SCI_MAX_CONTROLLERS || !oem_params))
                return SCI_FAILURE;

> The headers also need to be standard too:
> 
> 
> > +#if !defined(_SCI_HOST_H_)
> > +#define _SCI_HOST_H_
> 
> 
> should be
> 
> #ifdef _SCI_HOST_H
> #define _SCI_HOST_H
> 

Ok, simple enough.

> 
> > +static void isci_host_build_mde(
> > +	struct sci_physical_memory_descriptor *mde_struct,
> > +	struct coherent_memory_info *mdl_struct)
> > +{
> > +	unsigned long address = 0;
> > +	dma_addr_t dma_addr = 0;
> > +
> > +	address = (unsigned long)mdl_struct->vaddr;
> > +	dma_addr = mdl_struct->dma_handle;
> > +
> > +	/* to satisfy the alignment. */
> > +	if ((address % mde_struct->constant_memory_alignment) != 0) {
> > +		int align_offset
> > +			= (mde_struct->constant_memory_alignment
> > +			   - (address % mde_struct->constant_memory_alignment));
> > +		address += align_offset;
> > +		dma_addr += align_offset;
> > +	}
> > +
> > +	mde_struct->virtual_address = (void *)address;
> > +	mde_struct->physical_address = dma_addr;
> > +	mdl_struct->mde = mde_struct;
> > +}
> 
> This is a weird function.  Why not just do ALIGN(x,
> mde_struct->constant_memory_alignment) and further, why not do it in
> isci_host_alloc_mdl_struct()?

Probably because the original author did not know about the ALIGN macro,
or that dma_alloc_coherent() returns page aligned memory by default,
I'll get this cleaned up.


> > +#if defined(CONFIG_PBG_HBA_A0)
> > +int isci_si_rev = ISCI_SI_REVA0;
> > +#elif defined(CONFIG_PBG_HBA_A2)
> > +int isci_si_rev = ISCI_SI_REVA2;
> > +#else
> > +int isci_si_rev = ISCI_SI_REVB0;
> > +#endif
> 
> There aren't any config entries for PGB_HBA_X as far as I can see ... is
> this just some sort of remnant?

I probably neglected the Kconfig in this initial export of the git tree
state into the omnibus (lldd) patch set, but these are still present as
can be seen in git [2].

> 
> 
> > +static struct isci_host *isci_host_by_id(struct pci_dev *pdev, int id)
> > +{
> > +	struct isci_host *h;
> > +
> > +	for_each_isci_host(h, pdev)
> > +		if (h->id == id)
> > +			return h;
> > +	return NULL;
> > +}
> 
> How many hosts per PCI can their actually be?  Because all this list
> based stuff looks a bit heavyweight, and if it's only up to 8 or so, a
> fixed size list would be far easier.

It's only 2 per pci device.  Ok, an array of pointers would make this
function go away... will fix.

> > +	for (i = 0; i < num_msix; i++) {
> > +		int id = i / SCI_NUM_MSI_X_INT;
> > +		struct msix_entry *msix = &pci_info->msix_entries[i];
> > +		struct isci_host *isci_host = isci_host_by_id(pdev, id);
> > +
> > +		BUG_ON(!isci_host);
> > +
> > +		/* @todo: need to handle error case. */
> > +		err = devm_request_irq(&pdev->dev, msix->vector, isci_isr, 0,
> > +				       DRV_NAME"-msix", isci_host);
> > +		if (!err)
> > +			continue;
> > +
> > +		dev_info(&pdev->dev, "msix setup failed falling back to intx\n");
> > +		while (i--) {
> > +			id = i / SCI_NUM_MSI_X_INT;
> > +			isci_host = isci_host_by_id(pdev, id);
> > +			msix = &pci_info->msix_entries[i];
> > +			devm_free_irq(&pdev->dev, msix->vector, isci_host);
> > +		}
> > +		pci_disable_msix(pdev);
> > +		goto intx;
> > +	}
> 
> This is really non-standard error handling.  We usually do the
> initialisations inline and the error cleanup out of line with a goto.
> Having both in a for loop, with an unwind while causes a double take.
> 

Ok, unwinding the msix setup can be moved out of the for loop.


> 
> > +#ifdef ISCI_SLAVE_ALLOC
> > +extern int ISCI_SLAVE_ALLOC(struct scsi_device *scsi_dev);
> > +#endif /* ISCI_SLAVE_ALLOC */
> > +
> > +#ifdef ISCI_SLAVE_DESTROY
> > +extern void ISCI_SLAVE_DESTROY(struct scsi_device *scsi_dev);
> > +#endif  /* ISCI_SLAVE_DESTROY */
> 
> This is just left over debuuging, right?

Yes, will delete.

> 
> 
> > +#include <asm/string.h>
> 
> Should be <linux/string.h>

Ok, I added this to the "trivial cleanups" patch that I am tracking.

> 
> 
> > +struct isci_host {
> > +	struct scic_sds_controller *core_controller;
> > +	struct scic_controller_handler_methods scic_irq_handlers[SCI_NUM_MSI_X_INT];
> > +	union scic_oem_parameters oem_parameters;
> > +
> > +	int id; /* unique within a given pci device */
> > +	struct isci_timer_list timer_list_struct;
> > +	void *core_ctrl_memory;
> > +	struct dma_pool *dma_pool;
> > +	unsigned int dma_pool_alloc_size;
> > +	struct isci_phy phys[SCI_MAX_PHYS];
> > +
> > +	/* isci_ports and sas_ports are implicitly parallel to the
> > +	 * ports maintained by the core
> > +	 */
> > +	struct isci_port isci_ports[SCI_MAX_PORTS];
> > +	struct asd_sas_port sas_ports[SCI_MAX_PORTS];
> > +	struct sas_ha_struct sas_ha;
> > +
> > +	int can_queue;
> > +	spinlock_t queue_lock;
> > +	spinlock_t state_lock;
> > +
> > +	struct pci_dev *pdev;
> > +	u8 sas_addr[SAS_ADDR_SIZE];
> > +
> > +	enum isci_status status;
> > +	struct Scsi_Host *shost;
> > +	struct tasklet_struct completion_tasklet;
> > +	struct list_head mdl_struct_list;
> > +	struct list_head requests_to_complete;
> > +	struct list_head requests_to_abort;
> > +	struct completion stop_complete;
> > +	struct completion start_complete;
> > +	spinlock_t scic_lock;
> > +	struct isci_host *next;
> > +};
> 
> The locking in this whole driver is a bit on the heavy side
> (particularly when I look at the further patches for the device).  But
> basically, I've no idea from this layout what any of these are
> protecting.
> 

Yes.  This is a topic of the ongoing rework, I should have some cleanup
patches in this area to share shortly.  The short summary is to:

1/ make scic_lock loosely analogous to mvi->lock in mvsas
2/ delete unnecessary locks (made irrelevant by the fixes in 1)

> 
> > +	/* @todo: use both MSI-X interrupts, and don't do indirect
> > +	 * calls to the handlers just register direct calls
> > +	 */
> > +	if (isci_host->pdev->msix_enabled) {
> > +		status = scic_controller_get_handler_methods(
> > +			SCIC_MSIX_INTERRUPT_TYPE,
> > +			SCI_MSIX_DOUBLE_VECTOR,
> > +			handlers
> > +			);
> 
> Agree wholeheartedly with the comment.  The indirect function calling
> looks a complete mess.

This was also reported by Jeff [3] and Christoph [4] (see links for the
cleanup patches).

> One question, though:  where is
> scic_controller_get_handler_methods() defined?  I can't find it in any
> of the six patches.

The initial patch set only covered the lldd layer, given the feedback
rate it is time to post the core.  It has benefited from some cleanups
in the interim.  The full driver is available in git [5], but I'll get
it converted into a snapshot patch set and sent to the list for review.

> I also wonder about the tasklet processing stuff ... wouldn't threaded
> interrupts be simpler?

Perhaps, but one of the features of tasklets that the driver may want to
take advantage of in the future is tasklet_disable() to flush and
suspend the event queue.  Is there an analogue with a threaded interrupt
handler?

--
Dan


[1]: http://uncrustify.sourceforge.net/ used for initial import commit
bcd0d38e "isci: import baseline driver with automated conversions"

[2]:
http://git.kernel.org/?p=linux/kernel/git/djbw/isci.git;a=blob;f=drivers/scsi/Kconfig;h=625d418fhb=HEAD#l853

[3]: http://marc.info/?l=linux-scsi&m=129807306630285&w=2

[4]: http://marc.info/?l=linux-scsi&m=129807373431007&w=2

[5]: git://git.kernel.org/pub/scm/linux/kernel/git/djbw/isci.git


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/6] isci: hardware / topology event handling
  2011-02-07  0:34 ` [RFC PATCH 4/6] isci: hardware / topology event handling Dan Williams
@ 2011-03-18 16:18   ` Christoph Hellwig
  2011-03-23  8:15     ` Dan Williams
  0 siblings, 1 reply; 36+ messages in thread
From: Christoph Hellwig @ 2011-03-18 16:18 UTC (permalink / raw)
  To: Dan Williams
  Cc: james.bottomley, dave.jiang, linux-scsi, jacek.danecki,
	ed.ciechanowski, jeffrey.d.skirvin, edmund.nadolski

On Sun, Feb 06, 2011 at 04:34:55PM -0800, Dan Williams wrote:
> 2/ Timer interface.

This just needs to go away.  It's just a bad emulation of
Solaris/Windows style dynamically allocated timer lists, that just make
life harder for anyone trying to use it.

Same for the whole events.c code, which is just pointless wrappers.


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 3/6] isci: request (core request infrastructure)
  2011-02-07  0:34 ` [RFC PATCH 3/6] isci: request (core request infrastructure) Dan Williams
@ 2011-03-18 16:41   ` Christoph Hellwig
  0 siblings, 0 replies; 36+ messages in thread
From: Christoph Hellwig @ 2011-03-18 16:41 UTC (permalink / raw)
  To: Dan Williams
  Cc: james.bottomley, dave.jiang, linux-scsi, jacek.danecki,
	ed.ciechanowski, jeffrey.d.skirvin, edmund.nadolski

> +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;
> +}

This has just a single caller and would be much cleaner just inlined
there.

Btw, care to explain why you don't allocate the full sas_task for
TMF requests?  It seems like a lot of code would benefit from that
generalization.

> +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;
> +}

Again, much better opencoded in the only caller.

> +	ret = isci_request_alloc_io(
> +		isci_host,
> +		task,
> +		&request,
> +		isci_device,
> +		gfp_flags
> +		);

How about reformatting arguments in the whole driver in the normal way,
e.g.

	ret = isci_request_alloc_io(isci_host, task, &request, isci_device,
				    gfp_flags);

> +	status = isci_io_request_build(isci_host, request, isci_device);
> +	if (status == SCI_SUCCESS) {

just goto out here for the not successfull case here to keep the
indentation down.

> +		    status == SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED) {

		SCI_REMOTE_RESET_REQUIRED would be more than enough.

> + out:
> +	if (status != SCI_SUCCESS) {
> +
> +		/* release dma memory on failure. */
> +		isci_request_free(isci_host, request);
> +		request = NULL;
> +		ret = SCI_FAILURE;
> +	}
> +
> +	*isci_request = request;

Just do the 

	*isci_request = request;
	return SCI_SUCCESS;

before the out label and then simply the out label to:

	isci_request_free(isci_host, request);
	*isci_request = NULL;
	return SCI_FAILURE;

btw, what exactly is the scope of the SCI_ return values?  They seem to
be used for specific actions above, but also abused as simple true/fail
bools in other places.  I think you want specific return values for
a couple functions and else 0/-errno in normal linux style.

> +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);

Can be opencoded in it's only caller.

> +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;
> +}

Not a very useful helper.  Just have a goto in the only calling function
to fill the field out - the only thing differing is the open rejection
reason, which can be stored in a local variable.

> +		if ((isci_device->status == isci_stopping)
> +		    || (isci_device->status == isci_stopped)
> +		    )

very confusing indentation, this should be:

		if (isci_device->status == isci_stopping ||
		    isci_device->status == isci_stopped)

> +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;
> +}

Just opencode it in the only caller.  And get rid of the totally
mad isci_request_access_task obsfucation too please.

> +/**
> + * 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;
> +}

Completely pointless.  Either use a constant or better derive it from
the host template so that it's just set in one place.

> +u32 isci_request_ssp_io_request_get_lun(
> +	struct isci_request *request)
> +{
> +	struct sas_task *task = isci_request_access_task(request);
> +
> +	return task->ssp_task.LUN[0];
> +}

Again, pointless.

> +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;
> +}

Once more.

> +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;
> +}

And again..

> +static inline
> +enum isci_request_status isci_request_get_state(
> +	struct isci_request *isci_request)
> +{
> +	BUG_ON(isci_request == NULL);
> +
> +	/*probably a bad sign...	*/
> +	if (isci_request->status == unallocated)
> +		dev_warn(&isci_request->isci_host->pdev->dev,
> +			 "%s: isci_request->status == unallocated\n",
> +			 __func__);
> +
> +	return isci_request->status;
> +}

Here again.

> +static inline enum isci_request_status isci_request_change_started_to_newstate(
> +	struct isci_request *isci_request,
> +	struct completion *completion_ptr,
> +	enum isci_request_status newstate)
> +{
> +	enum isci_request_status old_state;
> +	unsigned long flags;
> +
> +	BUG_ON(isci_request == NULL);
> +
> +	spin_lock_irqsave(&isci_request->state_lock, flags);
> +
> +	old_state = isci_request->status;
> +
> +	if (old_state == started) {
> +		BUG_ON(isci_request->io_request_completion != NULL);
> +
> +		isci_request->io_request_completion = completion_ptr;
> +		isci_request->status = newstate;
> +	}
> +	spin_unlock_irqrestore(&isci_request->state_lock, flags);
> +
> +	dev_dbg(&isci_request->isci_host->pdev->dev,
> +		"%s: isci_request = %p, old_state = 0x%x\n",
> +		__func__,
> +		isci_request,
> +		old_state);
> +
> +	return old_state;
> +}

This one seems far too large to be inlined.

> +static inline enum isci_request_status isci_request_change_started_to_aborted(
> +	struct isci_request *isci_request,
> +	struct completion *completion_ptr)
> +{
> +	return isci_request_change_started_to_newstate(
> +		       isci_request, completion_ptr, aborted
> +		       );
> +}

Another completely pointless wrapper.

> +
> +#define isci_request_access_task(RequestPtr) \
> +	((RequestPtr)->ttype_ptr.io_task_ptr)
> +
> +#define isci_request_access_tmf(RequestPtr)  \
> +	((RequestPtr)->ttype_ptr.tmf_task_ptr)

Pretty pointless wrappers that are longer than their expansion.

> +	if ((task->data_dir != PCI_DMA_NONE) &&
> +	    !sas_protocol_ata(task->task_proto)) {
> +		if (task->num_scatter == 0)
> +			/* 0 indicates a single dma address */
> +			dma_unmap_single(
> +				&pdev->dev,
> +				request->zero_scatter_daddr,
> +				task->total_xfer_len,
> +				task->data_dir
> +				);
> +
> +		else  /* unmap the sgl dma addresses */
> +			dma_unmap_sg(
> +				&pdev->dev,
> +				task->scatter,
> +				request->num_sg_entries,
> +				task->data_dir
> +				);
> +	}

The driver should use S/G lists for it's internal commands as well to


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 1/6] isci: initialization
  2011-02-07  0:34 ` [RFC PATCH 1/6] isci: initialization Dan Williams
                     ` (2 preceding siblings ...)
  2011-03-04 23:35   ` James Bottomley
@ 2011-03-18 16:51   ` Christoph Hellwig
  3 siblings, 0 replies; 36+ messages in thread
From: Christoph Hellwig @ 2011-03-18 16:51 UTC (permalink / raw)
  To: Dan Williams
  Cc: james.bottomley, dave.jiang, linux-scsi, jacek.danecki,
	ed.ciechanowski, jeffrey.d.skirvin, edmund.nadolski

> +	/*
> +	 *  Since this is a legacy interrupt, either or both
> +	 *  controllers could have triggered it.  Thus, we have to call
> +	 *  the legacy interrupt handler for all controllers on the
> +	 *  PCI function.
> +	 */
> +	for_each_isci_host(isci_host, pdev) {

Just one request_irq for each host, and the core irq layer will handle
it fine.

> +static inline struct isci_pci_info *to_pci_info(struct pci_dev *pdev)
> +{
> +	return pci_get_drvdata(pdev);
> +}

Pretty pointless as pci_get_drvdata returns a void pointer, which you can
assign directly to a struct isci_pci_info *

> +static inline
> +enum isci_status isci_host_get_state(
> +	struct isci_host *isci_host)
> +{
> +	return isci_host->status;
> +}

Completely pointless.

> +/**
> + * isci_host_scan_start() -
> + *
> + * This function is one of the SCSI Host Template function, called by the SCSI
> + * mid layer berfore a target scan begins. The core library controller start
> + * routine is called from here.
> + */

Why do you have pseudo-kerneldoc comments in the headers?  That's not
going to be parserd by the kerneldoc tools and just confuses everyone.

> +static int __devinit isci_pci_probe(
> +	struct pci_dev *pdev,
> +	const struct pci_device_id *device_id_p);
> +
> +static void __devexit isci_pci_remove(struct pci_dev *pdev);

Just move the pci_driver table behing the implementation.

> +#if defined(CONFIG_PBG_HBA_A0)
> +int isci_si_rev = ISCI_SI_REVA0;
> +#elif defined(CONFIG_PBG_HBA_A2)
> +int isci_si_rev = ISCI_SI_REVA2;
> +#else
> +int isci_si_rev = ISCI_SI_REVB0;
> +#endif
> +module_param(isci_si_rev, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(isci_si_rev, "override default si rev (0: A0 1: A2 2: B0)");

The revision needs to be in the device specific structure, not a global
variable.

> +/******************************************************************************
> +* P R O T E C T E D  M E T H O D S
> +******************************************************************************/

Err, no..

> +/**
> + * isci_register_sas_ha() - This method initializes various lldd
> + *    specific members of the sas_ha struct and calls the libsas
> + *    sas_register_ha() function.
> + * @isci_host: This parameter specifies the lldd specific wrapper for the
> + *    libsas sas_ha struct.
> + *
> + * This method returns an error code indicating sucess or failure. The user
> + * should check for possible memory allocation error return otherwise, a zero
> + * indicates success.
> + */

It's not a method but a function.  And the documentation really doesn't
tell anything worthwhile while we're at it.

> +static void isci_unregister_sas_ha(struct isci_host *isci_host)
> +{
> +	if (!isci_host)
> +		return;

How could this happen?

> +/**
> + * This file contains the isci_module object definition.
> + *
> + * isci.h
> + */

Ok, and what exactly does this comment try to tell us?

> +
> +#if !defined(_SCI_MODULE_H_)
> +#define _SCI_MODULE_H_
> +
> +/**
> + * This file contains the SCI low level driver interface to the SCI and Libsas
> + *    Libraries.
> + *
> + * isci.h
> + */

Or this one just five lines later?


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/6] isci: hardware / topology event handling
  2011-03-18 16:18   ` Christoph Hellwig
@ 2011-03-23  8:15     ` Dan Williams
  2011-03-23  8:40       ` Christoph Hellwig
  0 siblings, 1 reply; 36+ messages in thread
From: Dan Williams @ 2011-03-23  8:15 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: james.bottomley, dave.jiang, linux-scsi, jacek.danecki,
	ed.ciechanowski, jeffrey.d.skirvin, edmund.nadolski

On Fri, Mar 18, 2011 at 9:18 AM, Christoph Hellwig <hch@infradead.org>
wrote:> On Sun, Feb 06, 2011 at 04:34:55PM -0800, Dan Williams wrote:
>> 2/ Timer interface.
>
> This just needs to go away.  It's just a bad emulation of
> Solaris/Windows style dynamically allocated timer lists, that just make
> life harder for anyone trying to use it.

Yeah, started hacking at it [1], but it can all just be converted to
delayed work.

> Same for the whole events.c code, which is just pointless wrappers.

Ok.

--
Dan

[1]: http://git.kernel.org/?p=linux/kernel/git/djbw/isci.git;a=commitdiff;h=8d32e5b2aefe0d5e966cc7f5c5e887aa2d2b6090;hp=db3dd417bed5b2be0c95ace9e4b8871102ca5dcf
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/6] isci: hardware / topology event handling
  2011-03-23  8:15     ` Dan Williams
@ 2011-03-23  8:40       ` Christoph Hellwig
  2011-03-23  9:04         ` Dan Williams
  0 siblings, 1 reply; 36+ messages in thread
From: Christoph Hellwig @ 2011-03-23  8:40 UTC (permalink / raw)
  To: Dan Williams
  Cc: Christoph Hellwig, james.bottomley, dave.jiang, linux-scsi,
	jacek.danecki, ed.ciechanowski, jeffrey.d.skirvin,
	edmund.nadolski

On Wed, Mar 23, 2011 at 01:15:08AM -0700, Dan Williams wrote:
> > This just needs to go away. ?It's just a bad emulation of
> > Solaris/Windows style dynamically allocated timer lists, that just make
> > life harder for anyone trying to use it.
> 
> Yeah, started hacking at it [1], but it can all just be converted to
> delayed work.

Currently it's plain timers, converting it to a delayed work and thus
moving it to process context seems like a non-trivial conversions.

Does it buy you anything?


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/6] isci: hardware / topology event handling
  2011-03-23  8:40       ` Christoph Hellwig
@ 2011-03-23  9:04         ` Dan Williams
  2011-03-23  9:08           ` Christoph Hellwig
  0 siblings, 1 reply; 36+ messages in thread
From: Dan Williams @ 2011-03-23  9:04 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: james.bottomley, dave.jiang, linux-scsi, jacek.danecki,
	ed.ciechanowski, jeffrey.d.skirvin, edmund.nadolski

On Wed, Mar 23, 2011 at 1:40 AM, Christoph Hellwig <hch@infradead.org> wrote:
> On Wed, Mar 23, 2011 at 01:15:08AM -0700, Dan Williams wrote:
>> > This just needs to go away. ?It's just a bad emulation of
>> > Solaris/Windows style dynamically allocated timer lists, that just make
>> > life harder for anyone trying to use it.
>>
>> Yeah, started hacking at it [1], but it can all just be converted to
>> delayed work.
>
> Currently it's plain timers, converting it to a delayed work and thus
> moving it to process context seems like a non-trivial conversions.
>
> Does it buy you anything?
>

Not much beyond the defined semantics of cancel_delayed_work and
delayed_work_pending.  I was looking to eliminate the current open
coded equivalent functionality.

I also want to get rid of the pre-allocation, which I assume was the
reason for the original comment?  But that can be accomplished without
going all the way to delayed_work.

--
Dan

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/6] isci: hardware / topology event handling
  2011-03-23  9:04         ` Dan Williams
@ 2011-03-23  9:08           ` Christoph Hellwig
  2011-03-24  0:07             ` Dan Williams
  0 siblings, 1 reply; 36+ messages in thread
From: Christoph Hellwig @ 2011-03-23  9:08 UTC (permalink / raw)
  To: Dan Williams
  Cc: Christoph Hellwig, james.bottomley, dave.jiang, linux-scsi,
	jacek.danecki, ed.ciechanowski, jeffrey.d.skirvin,
	edmund.nadolski

On Wed, Mar 23, 2011 at 02:04:28AM -0700, Dan Williams wrote:
> Not much beyond the defined semantics of cancel_delayed_work and
> delayed_work_pending.  I was looking to eliminate the current open
> coded equivalent functionality.

What's the problem with del_timer_sync and timer_pending?

> I also want to get rid of the pre-allocation, which I assume was the
> reason for the original comment?

The preallocation is even more pointless.  Just embedd the struct
timer_list where the code currently has a pointer to the timer object.


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/6] isci: hardware / topology event handling
  2011-03-23  9:08           ` Christoph Hellwig
@ 2011-03-24  0:07             ` Dan Williams
  2011-03-24  6:26               ` Christoph Hellwig
  0 siblings, 1 reply; 36+ messages in thread
From: Dan Williams @ 2011-03-24  0:07 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: james.bottomley, dave.jiang, linux-scsi, jacek.danecki,
	ed.ciechanowski, jeffrey.d.skirvin, edmund.nadolski

On Wed, Mar 23, 2011 at 2:08 AM, Christoph Hellwig <hch@infradead.org> wrote:
> On Wed, Mar 23, 2011 at 02:04:28AM -0700, Dan Williams wrote:
>> Not much beyond the defined semantics of cancel_delayed_work and
>> delayed_work_pending.  I was looking to eliminate the current open
>> coded equivalent functionality.
>
> What's the problem with del_timer_sync and timer_pending?

Actually none.  I mistakenly thought delayed_work_pending would return
true for executing work as well... so the only remaining benefit of
delayed_work over the timer api is flush_workqueue().

One of the fixes I made in that interim timer api cleanup (8d32e5b2)
was to use del_timer_sync on all pre-allocated timers to make sure
nothing fired or was in the process of firing as the driver was
brought down (unlikely).  The core runs under
spin_lock_irqsave(ihost->scic_lock) context, and the expectation is
that all these timeout callbacks are done in that context.  As a
result we can't call del_timer_sync from the core to cancel a timer
because that might deadlock on a timer event that is spinning on the
lock.  Instead we need to set a "cancelled" flag that the timer
callback will evaluate while holding the lock to check if it was
cancelled while wait to acquire.  I was thinking
"!delayed_work_pending()" could stand-in for "cancelled", but it
can't.

So we need a different "flush all pending events" mechanism once the
timers are no longer globally allocated, and that's why I suggested
delayed_work.  The core will have called cancel_delayed_work on all
pending items.

>> I also want to get rid of the pre-allocation, which I assume was the
>> reason for the original comment?
>
> The preallocation is even more pointless.  Just embedd the struct
> timer_list where the code currently has a pointer to the timer object.

Exactly.

--
Dan
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/6] isci: hardware / topology event handling
  2011-03-24  0:07             ` Dan Williams
@ 2011-03-24  6:26               ` Christoph Hellwig
  2011-03-25  0:57                 ` Dan Williams
  0 siblings, 1 reply; 36+ messages in thread
From: Christoph Hellwig @ 2011-03-24  6:26 UTC (permalink / raw)
  To: Dan Williams
  Cc: Christoph Hellwig, james.bottomley, dave.jiang, linux-scsi,
	jacek.danecki, ed.ciechanowski, jeffrey.d.skirvin,
	edmund.nadolski

On Wed, Mar 23, 2011 at 05:07:49PM -0700, Dan Williams wrote:
> One of the fixes I made in that interim timer api cleanup (8d32e5b2)
> was to use del_timer_sync on all pre-allocated timers to make sure
> nothing fired or was in the process of firing as the driver was
> brought down (unlikely).  The core runs under
> spin_lock_irqsave(ihost->scic_lock) context, and the expectation is
> that all these timeout callbacks are done in that context.

I'm afraid that you'll have to fix that design mistake first.  Running
large amounts of code under a spinlock just is not a good idea for
a wide range of reasons.


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/6] isci: hardware / topology event handling
  2011-03-24  6:26               ` Christoph Hellwig
@ 2011-03-25  0:57                 ` Dan Williams
  2011-03-25 19:45                   ` Christoph Hellwig
  0 siblings, 1 reply; 36+ messages in thread
From: Dan Williams @ 2011-03-25  0:57 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: james.bottomley, dave.jiang, linux-scsi, jacek.danecki,
	ed.ciechanowski, jeffrey.d.skirvin, edmund.nadolski

On Wed, Mar 23, 2011 at 11:26 PM, Christoph Hellwig <hch@infradead.org> wrote:
> On Wed, Mar 23, 2011 at 05:07:49PM -0700, Dan Williams wrote:
>> One of the fixes I made in that interim timer api cleanup (8d32e5b2)
>> was to use del_timer_sync on all pre-allocated timers to make sure
>> nothing fired or was in the process of firing as the driver was
>> brought down (unlikely).  The core runs under
>> spin_lock_irqsave(ihost->scic_lock) context, and the expectation is
>> that all these timeout callbacks are done in that context.
>
> I'm afraid that you'll have to fix that design mistake first.  Running
> large amounts of code under a spinlock just is not a good idea for
> a wide range of reasons.

I have been waiting for this issue to be raised.  This was one of the
first items brought up in our internal reviews of the Linux driver.
Why does it still exist and what is the rationale for addressing it
incrementally?:

Starting with simple locking and then scaling it is arguably easier
than unwinding a more complex locking scheme implemented too early in
the design phase.  The lock synchronizes i/o submission against the
completion/event-queue.  When the event queue runs the state of the
world might change (phy, port, task context, remote node context), so
the lock is ensuring a self consistent view of the state machines for
new submissions.  Now that the core is a slower moving target (in
terms of code changes) we are incrementally peeling back / minimizing
the code run under the lock where it is found to be problematic.

I will say that in terms of lock overhead investigations one of things
that makes this lock heavier than it needs to be (irqs disabled) is
libata's need to hold host_lock over submissions.  That situation is
problematic for other reasons [1], and is something that has been in
the back of my mind since first evaluating isci locking.  I expect our
fast path locking overhead is on par with mvsas which holds an
irqs-disabled lock across the interrupt handler, and the entire
lldd_execute_task path.  The higher touch slow path cases, like SATA
exception / PIO handling, probably have similar overhead to libata's
locking, but yes this needs to quantified and addressed.

--
Dan

[1]: http://marc.info/?l=linux-scsi&m=129791105419577&w=2
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/6] isci: hardware / topology event handling
  2011-03-25  0:57                 ` Dan Williams
@ 2011-03-25 19:45                   ` Christoph Hellwig
  2011-03-25 21:39                     ` Dan Williams
  0 siblings, 1 reply; 36+ messages in thread
From: Christoph Hellwig @ 2011-03-25 19:45 UTC (permalink / raw)
  To: Dan Williams
  Cc: Christoph Hellwig, james.bottomley, dave.jiang, linux-scsi,
	jacek.danecki, ed.ciechanowski, jeffrey.d.skirvin,
	edmund.nadolski

On Thu, Mar 24, 2011 at 05:57:17PM -0700, Dan Williams wrote:
> I have been waiting for this issue to be raised.  This was one of the
> first items brought up in our internal reviews of the Linux driver.
> Why does it still exist and what is the rationale for addressing it
> incrementally?:
> 
> Starting with simple locking and then scaling it is arguably easier
> than unwinding a more complex locking scheme implemented too early in
> the design phase.

I don't care about the lock scalability.  The problem with a global
spinlock is that a lot of primitives that a driver needs need to block,
and with a global spinlock that's almost impossible to handle, as
you'd need to drop the lock, and have very little chance to figure
out what state it actually protects that now needs to be re-checked.

Things are a little better with a global sleeping lock as it allows
you to block, as long as you don't actually plan to keep it over I/O.


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/6] isci: hardware / topology event handling
  2011-03-25 19:45                   ` Christoph Hellwig
@ 2011-03-25 21:39                     ` Dan Williams
  2011-03-25 22:07                       ` Christoph Hellwig
  0 siblings, 1 reply; 36+ messages in thread
From: Dan Williams @ 2011-03-25 21:39 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: james.bottomley, dave.jiang, linux-scsi, jacek.danecki,
	ed.ciechanowski, jeffrey.d.skirvin, edmund.nadolski

On Fri, Mar 25, 2011 at 12:45 PM, Christoph Hellwig <hch@infradead.org> wrote:
> On Thu, Mar 24, 2011 at 05:57:17PM -0700, Dan Williams wrote:
>> I have been waiting for this issue to be raised.  This was one of the
>> first items brought up in our internal reviews of the Linux driver.
>> Why does it still exist and what is the rationale for addressing it
>> incrementally?:
>>
>> Starting with simple locking and then scaling it is arguably easier
>> than unwinding a more complex locking scheme implemented too early in
>> the design phase.
>
> I don't care about the lock scalability.  The problem with a global
> spinlock is that a lot of primitives that a driver needs need to block,
> and with a global spinlock that's almost impossible to handle, as
> you'd need to drop the lock, and have very little chance to figure
> out what state it actually protects that now needs to be re-checked.

In that sense the scope of what scic_lock protects is limited to the
core/hardware state machines (essentially a controller firmware-like
context).  Anything that blocks or interfaces with an upper layer runs
unlocked in the lldd layer, and we don't play any games of dropping
the lock from the core.  Cancelling and flushing timed events is
really the only place where the lock gets in the way.  Having a
"timer_cancelled" flag for these few cases is the approach the core
took over dropping the lock, del_timer_sync, and re-validating state.
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/6] isci: hardware / topology event handling
  2011-03-25 21:39                     ` Dan Williams
@ 2011-03-25 22:07                       ` Christoph Hellwig
  2011-03-25 22:34                         ` Dan Williams
  0 siblings, 1 reply; 36+ messages in thread
From: Christoph Hellwig @ 2011-03-25 22:07 UTC (permalink / raw)
  To: Dan Williams
  Cc: Christoph Hellwig, james.bottomley, dave.jiang, linux-scsi,
	jacek.danecki, ed.ciechanowski, jeffrey.d.skirvin,
	edmund.nadolski

On Fri, Mar 25, 2011 at 02:39:03PM -0700, Dan Williams wrote:
> In that sense the scope of what scic_lock protects is limited to the
> core/hardware state machines (essentially a controller firmware-like
> context).  Anything that blocks or interfaces with an upper layer runs
> unlocked in the lldd layer, and we don't play any games of dropping
> the lock from the core.  Cancelling and flushing timed events is
> really the only place where the lock gets in the way.  Having a
> "timer_cancelled" flag for these few cases is the approach the core
> took over dropping the lock, del_timer_sync, and re-validating state.

If you just want to deactivate a handler and not wait for it to finish
you can simply use del_timer instead of del_timer_sync.  But it's still
up to you to make sure all timer handlers have finished before freeing
their containing structures or any other resource that they use, which
tends to be rather complicated, and hard to maintain in the long run.

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/6] isci: hardware / topology event handling
  2011-03-25 22:07                       ` Christoph Hellwig
@ 2011-03-25 22:34                         ` Dan Williams
  2011-03-27 22:28                           ` Christoph Hellwig
  0 siblings, 1 reply; 36+ messages in thread
From: Dan Williams @ 2011-03-25 22:34 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: james.bottomley, dave.jiang, linux-scsi, jacek.danecki,
	ed.ciechanowski, jeffrey.d.skirvin, edmund.nadolski

On Fri, Mar 25, 2011 at 3:07 PM, Christoph Hellwig <hch@infradead.org> wrote:
> On Fri, Mar 25, 2011 at 02:39:03PM -0700, Dan Williams wrote:
>> In that sense the scope of what scic_lock protects is limited to the
>> core/hardware state machines (essentially a controller firmware-like
>> context).  Anything that blocks or interfaces with an upper layer runs
>> unlocked in the lldd layer, and we don't play any games of dropping
>> the lock from the core.  Cancelling and flushing timed events is
>> really the only place where the lock gets in the way.  Having a
>> "timer_cancelled" flag for these few cases is the approach the core
>> took over dropping the lock, del_timer_sync, and re-validating state.
>
> If you just want to deactivate a handler and not wait for it to finish
> you can simply use del_timer instead of del_timer_sync.  But it's still
> up to you to make sure all timer handlers have finished before freeing
> their containing structures or any other resource that they use, which
> tends to be rather complicated, and hard to maintain in the long run.

Ok, so that's why I went all the way to recommending a switch to
delayed_workqueue.  We can bring down the core and ensure all events
have seen a cancellation.  None of these containing objects are
dynamically destroyed outside of driver exit, nor would they care
about the switch from timer callback to process context.  That final
flush is the only elusive bit that is covered by flush_workqueue.
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/6] isci: hardware / topology event handling
  2011-03-25 22:34                         ` Dan Williams
@ 2011-03-27 22:28                           ` Christoph Hellwig
  2011-03-29  1:11                             ` Dan Williams
  0 siblings, 1 reply; 36+ messages in thread
From: Christoph Hellwig @ 2011-03-27 22:28 UTC (permalink / raw)
  To: Dan Williams
  Cc: Christoph Hellwig, james.bottomley, dave.jiang, linux-scsi,
	jacek.danecki, ed.ciechanowski, jeffrey.d.skirvin,
	edmund.nadolski

On Fri, Mar 25, 2011 at 03:34:39PM -0700, Dan Williams wrote:
> Ok, so that's why I went all the way to recommending a switch to
> delayed_workqueue.  We can bring down the core and ensure all events
> have seen a cancellation.  None of these containing objects are
> dynamically destroyed outside of driver exit, nor would they care
> about the switch from timer callback to process context.  That final
> flush is the only elusive bit that is covered by flush_workqueue.

This seems like a really nasy hack to me.  Right now there are 9
callers of isci_timer_create, of those

  5 operate on struct scic_sds_controller
  2 operate on struct scic_sds_phy 
  1 operates on struct scic_sds_port
  1 operates on struct isci_request

of these at least the request has completely different life time rules
than the scic_sds_controller, so both the existing code and the
flush_workqueue variant would be incorrect.

I'd suggest that as a first step you remove the dynamic allocation of
the timers with a structure embedded into the containing structure
and replace isci_timer_list_destroy with a calls to
del_timer_sync when the containing structure is freed.

After that you can trivial remove the wrappers and just opencode the
Linux timer calls, which should lead to much simpler and easier to
understand code.

If the process context of delayed work items provices a significant
benefit to your execution model you can convert the timers to delayed
work items now, and remove the irqsafe locking.  Given that the
isci code still does some non-trivial work from it's interrupt
handlers and tasklets I'm not sure it's going to buy you much, though.

It might be worth to try replacing the tasklets with threaded interrupts
to simplify the locking model, but it will need carefully benchmarking
to evaluate the performance impact.


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/6] isci: hardware / topology event handling
  2011-03-27 22:28                           ` Christoph Hellwig
@ 2011-03-29  1:11                             ` Dan Williams
  2011-03-30  0:37                               ` Dan Williams
  0 siblings, 1 reply; 36+ messages in thread
From: Dan Williams @ 2011-03-29  1:11 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: james.bottomley, dave.jiang, linux-scsi, jacek.danecki,
	ed.ciechanowski, jeffrey.d.skirvin, edmund.nadolski

On Sun, Mar 27, 2011 at 3:28 PM, Christoph Hellwig <hch@infradead.org> wrote:
> On Fri, Mar 25, 2011 at 03:34:39PM -0700, Dan Williams wrote:
>> Ok, so that's why I went all the way to recommending a switch to
>> delayed_workqueue.  We can bring down the core and ensure all events
>> have seen a cancellation.  None of these containing objects are
>> dynamically destroyed outside of driver exit, nor would they care
>> about the switch from timer callback to process context.  That final
>> flush is the only elusive bit that is covered by flush_workqueue.
>
> This seems like a really nasy hack to me.  Right now there are 9
> callers of isci_timer_create, of those
>
>  5 operate on struct scic_sds_controller
>  2 operate on struct scic_sds_phy
>  1 operates on struct scic_sds_port
>  1 operates on struct isci_request
>
> of these at least the request has completely different life time rules
> than the scic_sds_controller, so both the existing code and the
> flush_workqueue variant would be incorrect.

Indeed. isci_request is the only usage of this "timer-api" outside of
the core and should have used the raw Linux api from the beginning.

> I'd suggest that as a first step you remove the dynamic allocation of
> the timers with a structure embedded into the containing structure
> and replace isci_timer_list_destroy with a calls to
> del_timer_sync when the containing structure is freed.

Ok, I was making this more difficult than it needed to be, all of
these objects can be reached when stopping the core and we can just
del_timer_sync once we know everything has been cancelled and the core
is known to be quiesced.

> After that you can trivial remove the wrappers and just opencode the
> Linux timer calls, which should lead to much simpler and easier to
> understand code.
>
> If the process context of delayed work items provices a significant
> benefit to your execution model you can convert the timers to delayed
> work items now, and remove the irqsafe locking.  Given that the
> isci code still does some non-trivial work from it's interrupt
> handlers and tasklets I'm not sure it's going to buy you much, though.

The irqsafe locking is an external requirement imposed by libata,
although we already drop the lock

--
Dan
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [RFC PATCH 4/6] isci: hardware / topology event handling
  2011-03-29  1:11                             ` Dan Williams
@ 2011-03-30  0:37                               ` Dan Williams
  0 siblings, 0 replies; 36+ messages in thread
From: Dan Williams @ 2011-03-30  0:37 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: james.bottomley, dave.jiang, linux-scsi, jacek.danecki,
	ed.ciechanowski, jeffrey.d.skirvin, edmund.nadolski

On Mon, Mar 28, 2011 at 6:11 PM, Dan Williams <dan.j.williams@intel.com> wrote:
>> If the process context of delayed work items provices a significant
>> benefit to your execution model you can convert the timers to delayed
>> work items now, and remove the irqsafe locking.  Given that the
>> isci code still does some non-trivial work from it's interrupt
>> handlers and tasklets I'm not sure it's going to buy you much, though.
>
> The irqsafe locking is an external requirement imposed by libata,
> although we already drop the lock
>

<hit 'send' too early>

I was going to say we (and the other libsas drivers that call
task->done() or sas_task_abort() from lldd_execute_task_context)
already drop the lock in error conditions.  If that is a safe
operation then perhaps we could up level that lock drop or otherwise
move ata submission to an unlocked context to allow libsas drivers to
use sleeping locks and threaded interrupts.

--
Dan
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 36+ messages in thread

end of thread, other threads:[~2011-03-30  0:37 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-02-07  0:34 [RFC PATCH 0/6] isci: initial driver release (part1: intro and lldd) Dan Williams
2011-02-07  0:34 ` [RFC PATCH 1/6] isci: initialization Dan Williams
2011-02-17  8:22   ` Jeff Garzik
2011-02-19  0:12     ` Dan Williams
2011-02-17  8:25   ` Christoph Hellwig
2011-02-19  0:23     ` Dan Williams
2011-03-04 23:35   ` James Bottomley
2011-03-08  1:51     ` Dan Williams
2011-03-18 16:51   ` Christoph Hellwig
2011-02-07  0:34 ` [RFC PATCH 2/6] isci: task (libsas interface support) Dan Williams
2011-02-09 15:01   ` David Milburn
2011-02-14  7:14     ` Dan Williams
2011-02-16 18:48       ` David Milburn
2011-02-16 19:35         ` David Milburn
2011-02-07  0:34 ` [RFC PATCH 3/6] isci: request (core request infrastructure) Dan Williams
2011-03-18 16:41   ` Christoph Hellwig
2011-02-07  0:34 ` [RFC PATCH 4/6] isci: hardware / topology event handling Dan Williams
2011-03-18 16:18   ` Christoph Hellwig
2011-03-23  8:15     ` Dan Williams
2011-03-23  8:40       ` Christoph Hellwig
2011-03-23  9:04         ` Dan Williams
2011-03-23  9:08           ` Christoph Hellwig
2011-03-24  0:07             ` Dan Williams
2011-03-24  6:26               ` Christoph Hellwig
2011-03-25  0:57                 ` Dan Williams
2011-03-25 19:45                   ` Christoph Hellwig
2011-03-25 21:39                     ` Dan Williams
2011-03-25 22:07                       ` Christoph Hellwig
2011-03-25 22:34                         ` Dan Williams
2011-03-27 22:28                           ` Christoph Hellwig
2011-03-29  1:11                             ` Dan Williams
2011-03-30  0:37                               ` Dan Williams
2011-02-07  0:35 ` [RFC PATCH 5/6] isci: phy, port, and remote device Dan Williams
2011-02-07  0:35 ` [RFC PATCH 6/6] isci: sata support and phy settings via request_firmware() Dan Williams
2011-02-07  7:58 ` [RFC PATCH 1/6] isci: initialization jack_wang
2011-02-14  7:49   ` Dan Williams

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.