2009-03-09 15:21:12 +08:00
|
|
|
/*
|
|
|
|
* SAS Transport Layer for MPT (Message Passing Technology) based controllers
|
|
|
|
*
|
|
|
|
* This code is based on drivers/scsi/mpt2sas/mpt2_transport.c
|
2010-03-17 18:58:04 +08:00
|
|
|
* Copyright (C) 2007-2010 LSI Corporation
|
2009-03-09 15:21:12 +08:00
|
|
|
* (mailto:DL-MPTFusionLinux@lsi.com)
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* NO WARRANTY
|
|
|
|
* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
|
|
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
|
|
|
|
* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
|
|
|
|
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
|
|
|
|
* solely responsible for determining the appropriateness of using and
|
|
|
|
* distributing the Program and assumes all risks associated with its
|
|
|
|
* exercise of rights under this Agreement, including but not limited to
|
|
|
|
* the risks and costs of program errors, damage to or loss of data,
|
|
|
|
* programs or equipment, and unavailability or interruption of operations.
|
|
|
|
|
|
|
|
* DISCLAIMER OF LIABILITY
|
|
|
|
* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
|
|
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
|
|
|
|
* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
|
|
|
|
|
|
|
|
* 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 Street, Fifth Floor, Boston, MA 02110-1301,
|
|
|
|
* USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/workqueue.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/pci.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2009-03-09 15:21:12 +08:00
|
|
|
|
|
|
|
#include <scsi/scsi.h>
|
|
|
|
#include <scsi/scsi_cmnd.h>
|
|
|
|
#include <scsi/scsi_device.h>
|
|
|
|
#include <scsi/scsi_host.h>
|
|
|
|
#include <scsi/scsi_transport_sas.h>
|
|
|
|
#include <scsi/scsi_dbg.h>
|
|
|
|
|
|
|
|
#include "mpt2sas_base.h"
|
|
|
|
/**
|
2009-09-23 19:51:29 +08:00
|
|
|
* _transport_sas_node_find_by_sas_address - sas node search
|
2009-03-09 15:21:12 +08:00
|
|
|
* @ioc: per adapter object
|
2009-09-23 19:51:29 +08:00
|
|
|
* @sas_address: sas address of expander or sas host
|
2009-03-09 15:21:12 +08:00
|
|
|
* Context: Calling function should acquire ioc->sas_node_lock.
|
|
|
|
*
|
|
|
|
* Search for either hba phys or expander device based on handle, then returns
|
|
|
|
* the sas_node object.
|
|
|
|
*/
|
|
|
|
static struct _sas_node *
|
2009-09-23 19:51:29 +08:00
|
|
|
_transport_sas_node_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc,
|
|
|
|
u64 sas_address)
|
2009-03-09 15:21:12 +08:00
|
|
|
{
|
2009-09-23 19:51:29 +08:00
|
|
|
if (ioc->sas_hba.sas_address == sas_address)
|
|
|
|
return &ioc->sas_hba;
|
|
|
|
else
|
|
|
|
return mpt2sas_scsih_expander_find_by_sas_address(ioc,
|
|
|
|
sas_address);
|
2009-03-09 15:21:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* _transport_convert_phy_link_rate -
|
|
|
|
* @link_rate: link rate returned from mpt firmware
|
|
|
|
*
|
|
|
|
* Convert link_rate from mpi fusion into sas_transport form.
|
|
|
|
*/
|
|
|
|
static enum sas_linkrate
|
|
|
|
_transport_convert_phy_link_rate(u8 link_rate)
|
|
|
|
{
|
|
|
|
enum sas_linkrate rc;
|
|
|
|
|
|
|
|
switch (link_rate) {
|
|
|
|
case MPI2_SAS_NEG_LINK_RATE_1_5:
|
|
|
|
rc = SAS_LINK_RATE_1_5_GBPS;
|
|
|
|
break;
|
|
|
|
case MPI2_SAS_NEG_LINK_RATE_3_0:
|
|
|
|
rc = SAS_LINK_RATE_3_0_GBPS;
|
|
|
|
break;
|
|
|
|
case MPI2_SAS_NEG_LINK_RATE_6_0:
|
|
|
|
rc = SAS_LINK_RATE_6_0_GBPS;
|
|
|
|
break;
|
|
|
|
case MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED:
|
|
|
|
rc = SAS_PHY_DISABLED;
|
|
|
|
break;
|
|
|
|
case MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED:
|
|
|
|
rc = SAS_LINK_RATE_FAILED;
|
|
|
|
break;
|
|
|
|
case MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR:
|
|
|
|
rc = SAS_SATA_PORT_SELECTOR;
|
|
|
|
break;
|
|
|
|
case MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS:
|
|
|
|
rc = SAS_PHY_RESET_IN_PROGRESS;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
case MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE:
|
|
|
|
case MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE:
|
|
|
|
rc = SAS_LINK_RATE_UNKNOWN;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* _transport_set_identify - set identify for phys and end devices
|
|
|
|
* @ioc: per adapter object
|
|
|
|
* @handle: device handle
|
|
|
|
* @identify: sas identify info
|
|
|
|
*
|
|
|
|
* Populates sas identify info.
|
|
|
|
*
|
|
|
|
* Returns 0 for success, non-zero for failure.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
_transport_set_identify(struct MPT2SAS_ADAPTER *ioc, u16 handle,
|
|
|
|
struct sas_identify *identify)
|
|
|
|
{
|
|
|
|
Mpi2SasDevicePage0_t sas_device_pg0;
|
|
|
|
Mpi2ConfigReply_t mpi_reply;
|
|
|
|
u32 device_info;
|
|
|
|
u32 ioc_status;
|
|
|
|
|
2009-08-20 15:52:00 +08:00
|
|
|
if (ioc->shost_recovery) {
|
|
|
|
printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
|
|
|
|
__func__, ioc->name);
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
2009-03-09 15:21:12 +08:00
|
|
|
if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
|
|
|
|
MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
2009-08-20 15:52:00 +08:00
|
|
|
|
2009-03-09 15:21:12 +08:00
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
2009-08-20 15:52:00 +08:00
|
|
|
return -ENXIO;
|
2009-03-09 15:21:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
|
|
|
|
MPI2_IOCSTATUS_MASK;
|
|
|
|
if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x)"
|
|
|
|
"\nfailure at %s:%d/%s()!\n", ioc->name, handle, ioc_status,
|
|
|
|
__FILE__, __LINE__, __func__);
|
2009-08-20 15:52:00 +08:00
|
|
|
return -EIO;
|
2009-03-09 15:21:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
memset(identify, 0, sizeof(identify));
|
|
|
|
device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
|
|
|
|
|
|
|
|
/* sas_address */
|
|
|
|
identify->sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
|
|
|
|
|
|
|
|
/* device_type */
|
|
|
|
switch (device_info & MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) {
|
|
|
|
case MPI2_SAS_DEVICE_INFO_NO_DEVICE:
|
|
|
|
identify->device_type = SAS_PHY_UNUSED;
|
|
|
|
break;
|
|
|
|
case MPI2_SAS_DEVICE_INFO_END_DEVICE:
|
|
|
|
identify->device_type = SAS_END_DEVICE;
|
|
|
|
break;
|
|
|
|
case MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER:
|
|
|
|
identify->device_type = SAS_EDGE_EXPANDER_DEVICE;
|
|
|
|
break;
|
|
|
|
case MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER:
|
|
|
|
identify->device_type = SAS_FANOUT_EXPANDER_DEVICE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* initiator_port_protocols */
|
|
|
|
if (device_info & MPI2_SAS_DEVICE_INFO_SSP_INITIATOR)
|
|
|
|
identify->initiator_port_protocols |= SAS_PROTOCOL_SSP;
|
|
|
|
if (device_info & MPI2_SAS_DEVICE_INFO_STP_INITIATOR)
|
|
|
|
identify->initiator_port_protocols |= SAS_PROTOCOL_STP;
|
|
|
|
if (device_info & MPI2_SAS_DEVICE_INFO_SMP_INITIATOR)
|
|
|
|
identify->initiator_port_protocols |= SAS_PROTOCOL_SMP;
|
|
|
|
if (device_info & MPI2_SAS_DEVICE_INFO_SATA_HOST)
|
|
|
|
identify->initiator_port_protocols |= SAS_PROTOCOL_SATA;
|
|
|
|
|
|
|
|
/* target_port_protocols */
|
|
|
|
if (device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET)
|
|
|
|
identify->target_port_protocols |= SAS_PROTOCOL_SSP;
|
|
|
|
if (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET)
|
|
|
|
identify->target_port_protocols |= SAS_PROTOCOL_STP;
|
|
|
|
if (device_info & MPI2_SAS_DEVICE_INFO_SMP_TARGET)
|
|
|
|
identify->target_port_protocols |= SAS_PROTOCOL_SMP;
|
|
|
|
if (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
|
|
|
|
identify->target_port_protocols |= SAS_PROTOCOL_SATA;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* mpt2sas_transport_done - internal transport layer callback handler.
|
|
|
|
* @ioc: per adapter object
|
|
|
|
* @smid: system request message index
|
2009-09-25 14:14:41 +08:00
|
|
|
* @msix_index: MSIX table index supplied by the OS
|
2009-03-09 15:21:12 +08:00
|
|
|
* @reply: reply message frame(lower 32bit addr)
|
|
|
|
*
|
|
|
|
* Callback handler when sending internal generated transport cmds.
|
|
|
|
* The callback index passed is `ioc->transport_cb_idx`
|
|
|
|
*
|
2009-09-14 13:34:23 +08:00
|
|
|
* Return 1 meaning mf should be freed from _base_interrupt
|
|
|
|
* 0 means the mf is freed from this function.
|
2009-03-09 15:21:12 +08:00
|
|
|
*/
|
2009-09-14 13:34:23 +08:00
|
|
|
u8
|
2009-09-25 14:14:41 +08:00
|
|
|
mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
|
2009-03-09 15:21:12 +08:00
|
|
|
u32 reply)
|
|
|
|
{
|
|
|
|
MPI2DefaultReply_t *mpi_reply;
|
|
|
|
|
|
|
|
mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
|
|
|
|
if (ioc->transport_cmds.status == MPT2_CMD_NOT_USED)
|
2009-09-14 13:34:23 +08:00
|
|
|
return 1;
|
2009-03-09 15:21:12 +08:00
|
|
|
if (ioc->transport_cmds.smid != smid)
|
2009-09-14 13:34:23 +08:00
|
|
|
return 1;
|
2009-03-09 15:21:12 +08:00
|
|
|
ioc->transport_cmds.status |= MPT2_CMD_COMPLETE;
|
|
|
|
if (mpi_reply) {
|
|
|
|
memcpy(ioc->transport_cmds.reply, mpi_reply,
|
|
|
|
mpi_reply->MsgLength*4);
|
|
|
|
ioc->transport_cmds.status |= MPT2_CMD_REPLY_VALID;
|
|
|
|
}
|
|
|
|
ioc->transport_cmds.status &= ~MPT2_CMD_PENDING;
|
|
|
|
complete(&ioc->transport_cmds.done);
|
2009-09-14 13:34:23 +08:00
|
|
|
return 1;
|
2009-03-09 15:21:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* report manufacture request structure */
|
|
|
|
struct rep_manu_request{
|
|
|
|
u8 smp_frame_type;
|
|
|
|
u8 function;
|
|
|
|
u8 reserved;
|
|
|
|
u8 request_length;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* report manufacture reply structure */
|
|
|
|
struct rep_manu_reply{
|
|
|
|
u8 smp_frame_type; /* 0x41 */
|
|
|
|
u8 function; /* 0x01 */
|
|
|
|
u8 function_result;
|
|
|
|
u8 response_length;
|
|
|
|
u16 expander_change_count;
|
|
|
|
u8 reserved0[2];
|
2009-10-05 18:23:06 +08:00
|
|
|
u8 sas_format;
|
2009-03-09 15:21:12 +08:00
|
|
|
u8 reserved2[3];
|
|
|
|
u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN];
|
|
|
|
u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN];
|
|
|
|
u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN];
|
|
|
|
u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN];
|
|
|
|
u16 component_id;
|
|
|
|
u8 component_revision_id;
|
|
|
|
u8 reserved3;
|
|
|
|
u8 vendor_specific[8];
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2009-05-19 03:02:08 +08:00
|
|
|
* _transport_expander_report_manufacture - obtain SMP report_manufacture
|
2009-03-09 15:21:12 +08:00
|
|
|
* @ioc: per adapter object
|
|
|
|
* @sas_address: expander sas address
|
|
|
|
* @edev: the sas_expander_device object
|
|
|
|
*
|
|
|
|
* Fills in the sas_expander_device object when SMP port is created.
|
|
|
|
*
|
|
|
|
* Returns 0 for success, non-zero for failure.
|
|
|
|
*/
|
|
|
|
static int
|
2009-05-19 03:02:08 +08:00
|
|
|
_transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
|
2009-03-09 15:21:12 +08:00
|
|
|
u64 sas_address, struct sas_expander_device *edev)
|
|
|
|
{
|
|
|
|
Mpi2SmpPassthroughRequest_t *mpi_request;
|
|
|
|
Mpi2SmpPassthroughReply_t *mpi_reply;
|
|
|
|
struct rep_manu_reply *manufacture_reply;
|
|
|
|
struct rep_manu_request *manufacture_request;
|
|
|
|
int rc;
|
|
|
|
u16 smid;
|
|
|
|
u32 ioc_state;
|
|
|
|
unsigned long timeleft;
|
|
|
|
void *psge;
|
|
|
|
u32 sgl_flags;
|
|
|
|
u8 issue_reset = 0;
|
|
|
|
void *data_out = NULL;
|
|
|
|
dma_addr_t data_out_dma;
|
|
|
|
u32 sz;
|
|
|
|
u64 *sas_address_le;
|
|
|
|
u16 wait_state_count;
|
|
|
|
|
2009-08-20 15:52:00 +08:00
|
|
|
if (ioc->shost_recovery) {
|
2009-03-09 15:21:12 +08:00
|
|
|
printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
|
|
|
|
__func__, ioc->name);
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_lock(&ioc->transport_cmds.mutex);
|
|
|
|
|
|
|
|
if (ioc->transport_cmds.status != MPT2_CMD_NOT_USED) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "%s: transport_cmds in use\n",
|
|
|
|
ioc->name, __func__);
|
|
|
|
rc = -EAGAIN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ioc->transport_cmds.status = MPT2_CMD_PENDING;
|
|
|
|
|
|
|
|
wait_state_count = 0;
|
|
|
|
ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
|
|
|
|
while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
|
|
|
|
if (wait_state_count++ == 10) {
|
|
|
|
printk(MPT2SAS_ERR_FMT
|
|
|
|
"%s: failed due to ioc not operational\n",
|
|
|
|
ioc->name, __func__);
|
|
|
|
rc = -EFAULT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ssleep(1);
|
|
|
|
ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
|
|
|
|
printk(MPT2SAS_INFO_FMT "%s: waiting for "
|
|
|
|
"operational state(count=%d)\n", ioc->name,
|
|
|
|
__func__, wait_state_count);
|
|
|
|
}
|
|
|
|
if (wait_state_count)
|
|
|
|
printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n",
|
|
|
|
ioc->name, __func__);
|
|
|
|
|
|
|
|
smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx);
|
|
|
|
if (!smid) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
|
|
|
|
ioc->name, __func__);
|
|
|
|
rc = -EAGAIN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
|
|
|
|
ioc->transport_cmds.smid = smid;
|
|
|
|
|
|
|
|
sz = sizeof(struct rep_manu_request) + sizeof(struct rep_manu_reply);
|
|
|
|
data_out = pci_alloc_consistent(ioc->pdev, sz, &data_out_dma);
|
|
|
|
|
|
|
|
if (!data_out) {
|
|
|
|
printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__,
|
|
|
|
__LINE__, __func__);
|
|
|
|
rc = -ENOMEM;
|
|
|
|
mpt2sas_base_free_smid(ioc, smid);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
manufacture_request = data_out;
|
|
|
|
manufacture_request->smp_frame_type = 0x40;
|
|
|
|
manufacture_request->function = 1;
|
|
|
|
manufacture_request->reserved = 0;
|
|
|
|
manufacture_request->request_length = 0;
|
|
|
|
|
|
|
|
memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
|
|
|
|
mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
|
|
|
|
mpi_request->PhysicalPort = 0xFF;
|
2009-09-25 14:14:41 +08:00
|
|
|
mpi_request->VF_ID = 0; /* TODO */
|
|
|
|
mpi_request->VP_ID = 0;
|
2009-03-09 15:21:12 +08:00
|
|
|
sas_address_le = (u64 *)&mpi_request->SASAddress;
|
|
|
|
*sas_address_le = cpu_to_le64(sas_address);
|
2009-10-05 18:23:06 +08:00
|
|
|
mpi_request->RequestDataLength =
|
|
|
|
cpu_to_le16(sizeof(struct rep_manu_request));
|
2009-03-09 15:21:12 +08:00
|
|
|
psge = &mpi_request->SGL;
|
|
|
|
|
|
|
|
/* WRITE sgel first */
|
|
|
|
sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
|
|
|
|
MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC);
|
|
|
|
sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
|
|
|
|
ioc->base_add_sg_single(psge, sgl_flags |
|
|
|
|
sizeof(struct rep_manu_request), data_out_dma);
|
|
|
|
|
|
|
|
/* incr sgel */
|
|
|
|
psge += ioc->sge_size;
|
|
|
|
|
|
|
|
/* READ sgel last */
|
|
|
|
sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
|
|
|
|
MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
|
|
|
|
MPI2_SGE_FLAGS_END_OF_LIST);
|
|
|
|
sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
|
|
|
|
ioc->base_add_sg_single(psge, sgl_flags |
|
|
|
|
sizeof(struct rep_manu_reply), data_out_dma +
|
|
|
|
sizeof(struct rep_manu_request));
|
|
|
|
|
|
|
|
dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "report_manufacture - "
|
|
|
|
"send to sas_addr(0x%016llx)\n", ioc->name,
|
|
|
|
(unsigned long long)sas_address));
|
2009-09-25 14:14:41 +08:00
|
|
|
mpt2sas_base_put_smid_default(ioc, smid);
|
2009-09-14 13:35:24 +08:00
|
|
|
init_completion(&ioc->transport_cmds.done);
|
2009-03-09 15:21:12 +08:00
|
|
|
timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done,
|
|
|
|
10*HZ);
|
|
|
|
|
|
|
|
if (!(ioc->transport_cmds.status & MPT2_CMD_COMPLETE)) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "%s: timeout\n",
|
|
|
|
ioc->name, __func__);
|
|
|
|
_debug_dump_mf(mpi_request,
|
|
|
|
sizeof(Mpi2SmpPassthroughRequest_t)/4);
|
|
|
|
if (!(ioc->transport_cmds.status & MPT2_CMD_RESET))
|
|
|
|
issue_reset = 1;
|
|
|
|
goto issue_host_reset;
|
|
|
|
}
|
|
|
|
|
|
|
|
dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "report_manufacture - "
|
|
|
|
"complete\n", ioc->name));
|
|
|
|
|
|
|
|
if (ioc->transport_cmds.status & MPT2_CMD_REPLY_VALID) {
|
|
|
|
u8 *tmp;
|
|
|
|
|
|
|
|
mpi_reply = ioc->transport_cmds.reply;
|
|
|
|
|
|
|
|
dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT
|
|
|
|
"report_manufacture - reply data transfer size(%d)\n",
|
|
|
|
ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength)));
|
|
|
|
|
|
|
|
if (le16_to_cpu(mpi_reply->ResponseDataLength) !=
|
|
|
|
sizeof(struct rep_manu_reply))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
manufacture_reply = data_out + sizeof(struct rep_manu_request);
|
|
|
|
strncpy(edev->vendor_id, manufacture_reply->vendor_id,
|
|
|
|
SAS_EXPANDER_VENDOR_ID_LEN);
|
|
|
|
strncpy(edev->product_id, manufacture_reply->product_id,
|
|
|
|
SAS_EXPANDER_PRODUCT_ID_LEN);
|
|
|
|
strncpy(edev->product_rev, manufacture_reply->product_rev,
|
|
|
|
SAS_EXPANDER_PRODUCT_REV_LEN);
|
2009-10-05 18:23:06 +08:00
|
|
|
edev->level = manufacture_reply->sas_format & 1;
|
|
|
|
if (edev->level) {
|
2009-03-09 15:21:12 +08:00
|
|
|
strncpy(edev->component_vendor_id,
|
|
|
|
manufacture_reply->component_vendor_id,
|
|
|
|
SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN);
|
|
|
|
tmp = (u8 *)&manufacture_reply->component_id;
|
|
|
|
edev->component_id = tmp[0] << 8 | tmp[1];
|
|
|
|
edev->component_revision_id =
|
|
|
|
manufacture_reply->component_revision_id;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT
|
|
|
|
"report_manufacture - no reply\n", ioc->name));
|
|
|
|
|
|
|
|
issue_host_reset:
|
|
|
|
if (issue_reset)
|
|
|
|
mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
|
|
|
|
FORCE_BIG_HAMMER);
|
|
|
|
out:
|
|
|
|
ioc->transport_cmds.status = MPT2_CMD_NOT_USED;
|
|
|
|
if (data_out)
|
|
|
|
pci_free_consistent(ioc->pdev, sz, data_out, data_out_dma);
|
|
|
|
|
|
|
|
mutex_unlock(&ioc->transport_cmds.mutex);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2010-04-05 16:48:34 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* _transport_delete_duplicate_port - (see below description)
|
|
|
|
* @ioc: per adapter object
|
|
|
|
* @sas_node: sas node object (either expander or sas host)
|
|
|
|
* @sas_address: sas address of device being added
|
|
|
|
* @phy_num: phy number
|
|
|
|
*
|
|
|
|
* This function is called when attempting to add a new port that is claiming
|
|
|
|
* the same phy resources already in use by another port. If we don't release
|
|
|
|
* the claimed phy resources, the sas transport layer will hang from the BUG
|
|
|
|
* in sas_port_add_phy.
|
|
|
|
*
|
|
|
|
* The reason we would hit this issue is becuase someone is changing the
|
|
|
|
* sas address of a device on the fly, meanwhile controller firmware sends
|
|
|
|
* EVENTs out of order when removing the previous instance of the device.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
_transport_delete_duplicate_port(struct MPT2SAS_ADAPTER *ioc,
|
|
|
|
struct _sas_node *sas_node, u64 sas_address, int phy_num)
|
|
|
|
{
|
|
|
|
struct _sas_port *mpt2sas_port, *mpt2sas_port_duplicate;
|
|
|
|
struct _sas_phy *mpt2sas_phy;
|
|
|
|
|
|
|
|
printk(MPT2SAS_ERR_FMT "new device located at sas_addr(0x%016llx), "
|
|
|
|
"phy_id(%d)\n", ioc->name, (unsigned long long)sas_address,
|
|
|
|
phy_num);
|
|
|
|
|
|
|
|
mpt2sas_port_duplicate = NULL;
|
|
|
|
list_for_each_entry(mpt2sas_port, &sas_node->sas_port_list, port_list) {
|
|
|
|
dev_printk(KERN_ERR, &mpt2sas_port->port->dev,
|
|
|
|
"existing device at sas_addr(0x%016llx), num_phys(%d)\n",
|
|
|
|
(unsigned long long)
|
|
|
|
mpt2sas_port->remote_identify.sas_address,
|
|
|
|
mpt2sas_port->num_phys);
|
|
|
|
list_for_each_entry(mpt2sas_phy, &mpt2sas_port->phy_list,
|
|
|
|
port_siblings) {
|
|
|
|
dev_printk(KERN_ERR, &mpt2sas_phy->phy->dev,
|
|
|
|
"phy_number(%d)\n", mpt2sas_phy->phy_id);
|
|
|
|
if (mpt2sas_phy->phy_id == phy_num)
|
|
|
|
mpt2sas_port_duplicate = mpt2sas_port;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mpt2sas_port_duplicate)
|
|
|
|
return;
|
|
|
|
|
|
|
|
dev_printk(KERN_ERR, &mpt2sas_port_duplicate->port->dev,
|
|
|
|
"deleting duplicate device at sas_addr(0x%016llx), phy(%d)!!!!\n",
|
|
|
|
(unsigned long long)
|
|
|
|
mpt2sas_port_duplicate->remote_identify.sas_address, phy_num);
|
|
|
|
ioc->logging_level |= MPT_DEBUG_TRANSPORT;
|
|
|
|
mpt2sas_transport_port_remove(ioc,
|
|
|
|
mpt2sas_port_duplicate->remote_identify.sas_address,
|
|
|
|
sas_node->sas_address);
|
|
|
|
ioc->logging_level &= ~MPT_DEBUG_TRANSPORT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* _transport_sanity_check - sanity check when adding a new port
|
|
|
|
* @ioc: per adapter object
|
|
|
|
* @sas_node: sas node object (either expander or sas host)
|
|
|
|
* @sas_address: sas address of device being added
|
|
|
|
*
|
|
|
|
* See the explanation above from _transport_delete_duplicate_port
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
_transport_sanity_check(struct MPT2SAS_ADAPTER *ioc, struct _sas_node *sas_node,
|
|
|
|
u64 sas_address)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < sas_node->num_phys; i++)
|
|
|
|
if (sas_node->phy[i].remote_identify.sas_address == sas_address)
|
|
|
|
if (sas_node->phy[i].phy_belongs_to_port)
|
|
|
|
_transport_delete_duplicate_port(ioc, sas_node,
|
|
|
|
sas_address, i);
|
|
|
|
}
|
|
|
|
|
2009-03-09 15:21:12 +08:00
|
|
|
/**
|
|
|
|
* mpt2sas_transport_port_add - insert port to the list
|
|
|
|
* @ioc: per adapter object
|
|
|
|
* @handle: handle of attached device
|
2009-09-23 19:51:29 +08:00
|
|
|
* @sas_address: sas address of parent expander or sas host
|
2009-03-09 15:21:12 +08:00
|
|
|
* Context: This function will acquire ioc->sas_node_lock.
|
|
|
|
*
|
|
|
|
* Adding new port object to the sas_node->sas_port_list.
|
|
|
|
*
|
|
|
|
* Returns mpt2sas_port.
|
|
|
|
*/
|
|
|
|
struct _sas_port *
|
|
|
|
mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
|
2009-09-23 19:51:29 +08:00
|
|
|
u64 sas_address)
|
2009-03-09 15:21:12 +08:00
|
|
|
{
|
|
|
|
struct _sas_phy *mpt2sas_phy, *next;
|
|
|
|
struct _sas_port *mpt2sas_port;
|
|
|
|
unsigned long flags;
|
|
|
|
struct _sas_node *sas_node;
|
|
|
|
struct sas_rphy *rphy;
|
|
|
|
int i;
|
|
|
|
struct sas_port *port;
|
|
|
|
|
|
|
|
mpt2sas_port = kzalloc(sizeof(struct _sas_port),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!mpt2sas_port) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&mpt2sas_port->port_list);
|
|
|
|
INIT_LIST_HEAD(&mpt2sas_port->phy_list);
|
|
|
|
spin_lock_irqsave(&ioc->sas_node_lock, flags);
|
2009-09-23 19:51:29 +08:00
|
|
|
sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address);
|
2009-03-09 15:21:12 +08:00
|
|
|
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
|
|
|
|
|
|
|
|
if (!sas_node) {
|
2009-09-23 19:51:29 +08:00
|
|
|
printk(MPT2SAS_ERR_FMT "%s: Could not find "
|
|
|
|
"parent sas_address(0x%016llx)!\n", ioc->name,
|
|
|
|
__func__, (unsigned long long)sas_address);
|
2009-03-09 15:21:12 +08:00
|
|
|
goto out_fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((_transport_set_identify(ioc, handle,
|
|
|
|
&mpt2sas_port->remote_identify))) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
goto out_fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mpt2sas_port->remote_identify.device_type == SAS_PHY_UNUSED) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
goto out_fail;
|
|
|
|
}
|
|
|
|
|
2010-04-05 16:48:34 +08:00
|
|
|
_transport_sanity_check(ioc, sas_node,
|
|
|
|
mpt2sas_port->remote_identify.sas_address);
|
|
|
|
|
2009-03-09 15:21:12 +08:00
|
|
|
for (i = 0; i < sas_node->num_phys; i++) {
|
|
|
|
if (sas_node->phy[i].remote_identify.sas_address !=
|
|
|
|
mpt2sas_port->remote_identify.sas_address)
|
|
|
|
continue;
|
|
|
|
list_add_tail(&sas_node->phy[i].port_siblings,
|
|
|
|
&mpt2sas_port->phy_list);
|
|
|
|
mpt2sas_port->num_phys++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mpt2sas_port->num_phys) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
goto out_fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
port = sas_port_alloc_num(sas_node->parent_dev);
|
|
|
|
if ((sas_port_add(port))) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
goto out_fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_for_each_entry(mpt2sas_phy, &mpt2sas_port->phy_list,
|
|
|
|
port_siblings) {
|
|
|
|
if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
|
|
|
|
dev_printk(KERN_INFO, &port->dev, "add: handle(0x%04x)"
|
|
|
|
", sas_addr(0x%016llx), phy(%d)\n", handle,
|
|
|
|
(unsigned long long)
|
|
|
|
mpt2sas_port->remote_identify.sas_address,
|
|
|
|
mpt2sas_phy->phy_id);
|
|
|
|
sas_port_add_phy(port, mpt2sas_phy->phy);
|
2010-04-05 16:48:34 +08:00
|
|
|
mpt2sas_phy->phy_belongs_to_port = 1;
|
2009-03-09 15:21:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
mpt2sas_port->port = port;
|
|
|
|
if (mpt2sas_port->remote_identify.device_type == SAS_END_DEVICE)
|
|
|
|
rphy = sas_end_device_alloc(port);
|
|
|
|
else
|
|
|
|
rphy = sas_expander_alloc(port,
|
|
|
|
mpt2sas_port->remote_identify.device_type);
|
|
|
|
|
|
|
|
rphy->identify = mpt2sas_port->remote_identify;
|
|
|
|
if ((sas_rphy_add(rphy))) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
}
|
|
|
|
if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
|
|
|
|
dev_printk(KERN_INFO, &rphy->dev, "add: handle(0x%04x), "
|
|
|
|
"sas_addr(0x%016llx)\n", handle,
|
|
|
|
(unsigned long long)
|
|
|
|
mpt2sas_port->remote_identify.sas_address);
|
|
|
|
mpt2sas_port->rphy = rphy;
|
|
|
|
spin_lock_irqsave(&ioc->sas_node_lock, flags);
|
|
|
|
list_add_tail(&mpt2sas_port->port_list, &sas_node->sas_port_list);
|
|
|
|
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
|
|
|
|
|
|
|
|
/* fill in report manufacture */
|
|
|
|
if (mpt2sas_port->remote_identify.device_type ==
|
|
|
|
MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER ||
|
|
|
|
mpt2sas_port->remote_identify.device_type ==
|
|
|
|
MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER)
|
2009-05-19 03:02:08 +08:00
|
|
|
_transport_expander_report_manufacture(ioc,
|
2009-03-09 15:21:12 +08:00
|
|
|
mpt2sas_port->remote_identify.sas_address,
|
|
|
|
rphy_to_expander_device(rphy));
|
|
|
|
|
|
|
|
return mpt2sas_port;
|
|
|
|
|
|
|
|
out_fail:
|
|
|
|
list_for_each_entry_safe(mpt2sas_phy, next, &mpt2sas_port->phy_list,
|
|
|
|
port_siblings)
|
|
|
|
list_del(&mpt2sas_phy->port_siblings);
|
|
|
|
kfree(mpt2sas_port);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* mpt2sas_transport_port_remove - remove port from the list
|
|
|
|
* @ioc: per adapter object
|
|
|
|
* @sas_address: sas address of attached device
|
2009-09-23 19:51:29 +08:00
|
|
|
* @sas_address_parent: sas address of parent expander or sas host
|
2009-03-09 15:21:12 +08:00
|
|
|
* Context: This function will acquire ioc->sas_node_lock.
|
|
|
|
*
|
|
|
|
* Removing object and freeing associated memory from the
|
|
|
|
* ioc->sas_port_list.
|
|
|
|
*
|
|
|
|
* Return nothing.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
|
2009-09-23 19:51:29 +08:00
|
|
|
u64 sas_address_parent)
|
2009-03-09 15:21:12 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
unsigned long flags;
|
|
|
|
struct _sas_port *mpt2sas_port, *next;
|
|
|
|
struct _sas_node *sas_node;
|
|
|
|
u8 found = 0;
|
|
|
|
struct _sas_phy *mpt2sas_phy, *next_phy;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ioc->sas_node_lock, flags);
|
2009-09-23 19:51:29 +08:00
|
|
|
sas_node = _transport_sas_node_find_by_sas_address(ioc,
|
|
|
|
sas_address_parent);
|
2009-03-09 15:21:12 +08:00
|
|
|
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
|
|
|
|
if (!sas_node)
|
|
|
|
return;
|
|
|
|
list_for_each_entry_safe(mpt2sas_port, next, &sas_node->sas_port_list,
|
|
|
|
port_list) {
|
|
|
|
if (mpt2sas_port->remote_identify.sas_address != sas_address)
|
|
|
|
continue;
|
|
|
|
found = 1;
|
|
|
|
list_del(&mpt2sas_port->port_list);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
if (!found)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < sas_node->num_phys; i++) {
|
|
|
|
if (sas_node->phy[i].remote_identify.sas_address == sas_address)
|
|
|
|
memset(&sas_node->phy[i].remote_identify, 0 ,
|
|
|
|
sizeof(struct sas_identify));
|
|
|
|
}
|
|
|
|
|
|
|
|
list_for_each_entry_safe(mpt2sas_phy, next_phy,
|
|
|
|
&mpt2sas_port->phy_list, port_siblings) {
|
|
|
|
if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
|
|
|
|
dev_printk(KERN_INFO, &mpt2sas_port->port->dev,
|
2009-09-23 19:51:29 +08:00
|
|
|
"remove: sas_addr(0x%016llx), phy(%d)\n",
|
2009-03-09 15:21:12 +08:00
|
|
|
(unsigned long long)
|
|
|
|
mpt2sas_port->remote_identify.sas_address,
|
|
|
|
mpt2sas_phy->phy_id);
|
2010-04-05 16:48:34 +08:00
|
|
|
mpt2sas_phy->phy_belongs_to_port = 0;
|
2009-03-09 15:21:12 +08:00
|
|
|
sas_port_delete_phy(mpt2sas_port->port, mpt2sas_phy->phy);
|
|
|
|
list_del(&mpt2sas_phy->port_siblings);
|
|
|
|
}
|
|
|
|
sas_port_delete(mpt2sas_port->port);
|
|
|
|
kfree(mpt2sas_port);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* mpt2sas_transport_add_host_phy - report sas_host phy to transport
|
|
|
|
* @ioc: per adapter object
|
|
|
|
* @mpt2sas_phy: mpt2sas per phy object
|
|
|
|
* @phy_pg0: sas phy page 0
|
|
|
|
* @parent_dev: parent device class object
|
|
|
|
*
|
|
|
|
* Returns 0 for success, non-zero for failure.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
mpt2sas_transport_add_host_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
|
|
|
|
*mpt2sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev)
|
|
|
|
{
|
|
|
|
struct sas_phy *phy;
|
|
|
|
int phy_index = mpt2sas_phy->phy_id;
|
|
|
|
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&mpt2sas_phy->port_siblings);
|
|
|
|
phy = sas_phy_alloc(parent_dev, phy_index);
|
|
|
|
if (!phy) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if ((_transport_set_identify(ioc, mpt2sas_phy->handle,
|
|
|
|
&mpt2sas_phy->identify))) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
phy->identify = mpt2sas_phy->identify;
|
|
|
|
mpt2sas_phy->attached_handle = le16_to_cpu(phy_pg0.AttachedDevHandle);
|
|
|
|
if (mpt2sas_phy->attached_handle)
|
|
|
|
_transport_set_identify(ioc, mpt2sas_phy->attached_handle,
|
|
|
|
&mpt2sas_phy->remote_identify);
|
|
|
|
phy->identify.phy_identifier = mpt2sas_phy->phy_id;
|
|
|
|
phy->negotiated_linkrate = _transport_convert_phy_link_rate(
|
|
|
|
phy_pg0.NegotiatedLinkRate & MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
|
|
|
|
phy->minimum_linkrate_hw = _transport_convert_phy_link_rate(
|
|
|
|
phy_pg0.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK);
|
|
|
|
phy->maximum_linkrate_hw = _transport_convert_phy_link_rate(
|
|
|
|
phy_pg0.HwLinkRate >> 4);
|
|
|
|
phy->minimum_linkrate = _transport_convert_phy_link_rate(
|
|
|
|
phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
|
|
|
|
phy->maximum_linkrate = _transport_convert_phy_link_rate(
|
|
|
|
phy_pg0.ProgrammedLinkRate >> 4);
|
|
|
|
|
|
|
|
if ((sas_phy_add(phy))) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
sas_phy_free(phy);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
|
|
|
|
dev_printk(KERN_INFO, &phy->dev,
|
|
|
|
"add: handle(0x%04x), sas_addr(0x%016llx)\n"
|
|
|
|
"\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
|
|
|
|
mpt2sas_phy->handle, (unsigned long long)
|
|
|
|
mpt2sas_phy->identify.sas_address,
|
|
|
|
mpt2sas_phy->attached_handle,
|
|
|
|
(unsigned long long)
|
|
|
|
mpt2sas_phy->remote_identify.sas_address);
|
|
|
|
mpt2sas_phy->phy = phy;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* mpt2sas_transport_add_expander_phy - report expander phy to transport
|
|
|
|
* @ioc: per adapter object
|
|
|
|
* @mpt2sas_phy: mpt2sas per phy object
|
|
|
|
* @expander_pg1: expander page 1
|
|
|
|
* @parent_dev: parent device class object
|
|
|
|
*
|
|
|
|
* Returns 0 for success, non-zero for failure.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
|
|
|
|
*mpt2sas_phy, Mpi2ExpanderPage1_t expander_pg1, struct device *parent_dev)
|
|
|
|
{
|
|
|
|
struct sas_phy *phy;
|
|
|
|
int phy_index = mpt2sas_phy->phy_id;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&mpt2sas_phy->port_siblings);
|
|
|
|
phy = sas_phy_alloc(parent_dev, phy_index);
|
|
|
|
if (!phy) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if ((_transport_set_identify(ioc, mpt2sas_phy->handle,
|
|
|
|
&mpt2sas_phy->identify))) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
phy->identify = mpt2sas_phy->identify;
|
|
|
|
mpt2sas_phy->attached_handle =
|
|
|
|
le16_to_cpu(expander_pg1.AttachedDevHandle);
|
|
|
|
if (mpt2sas_phy->attached_handle)
|
|
|
|
_transport_set_identify(ioc, mpt2sas_phy->attached_handle,
|
|
|
|
&mpt2sas_phy->remote_identify);
|
|
|
|
phy->identify.phy_identifier = mpt2sas_phy->phy_id;
|
|
|
|
phy->negotiated_linkrate = _transport_convert_phy_link_rate(
|
|
|
|
expander_pg1.NegotiatedLinkRate &
|
|
|
|
MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
|
|
|
|
phy->minimum_linkrate_hw = _transport_convert_phy_link_rate(
|
|
|
|
expander_pg1.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK);
|
|
|
|
phy->maximum_linkrate_hw = _transport_convert_phy_link_rate(
|
|
|
|
expander_pg1.HwLinkRate >> 4);
|
|
|
|
phy->minimum_linkrate = _transport_convert_phy_link_rate(
|
|
|
|
expander_pg1.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
|
|
|
|
phy->maximum_linkrate = _transport_convert_phy_link_rate(
|
|
|
|
expander_pg1.ProgrammedLinkRate >> 4);
|
|
|
|
|
|
|
|
if ((sas_phy_add(phy))) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
sas_phy_free(phy);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
|
|
|
|
dev_printk(KERN_INFO, &phy->dev,
|
|
|
|
"add: handle(0x%04x), sas_addr(0x%016llx)\n"
|
|
|
|
"\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
|
|
|
|
mpt2sas_phy->handle, (unsigned long long)
|
|
|
|
mpt2sas_phy->identify.sas_address,
|
|
|
|
mpt2sas_phy->attached_handle,
|
|
|
|
(unsigned long long)
|
|
|
|
mpt2sas_phy->remote_identify.sas_address);
|
|
|
|
mpt2sas_phy->phy = phy;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-08-20 15:52:39 +08:00
|
|
|
* mpt2sas_transport_update_links - refreshing phy link changes
|
2009-03-09 15:21:12 +08:00
|
|
|
* @ioc: per adapter object
|
2009-09-23 19:51:29 +08:00
|
|
|
* @sas_address: sas address of parent expander or sas host
|
|
|
|
* @handle: attached device handle
|
2009-03-09 15:21:12 +08:00
|
|
|
* @phy_numberv: phy number
|
|
|
|
* @link_rate: new link rate
|
|
|
|
*
|
|
|
|
* Returns nothing.
|
|
|
|
*/
|
|
|
|
void
|
2009-08-20 15:52:39 +08:00
|
|
|
mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc,
|
2009-09-23 19:51:29 +08:00
|
|
|
u64 sas_address, u16 handle, u8 phy_number, u8 link_rate)
|
2009-03-09 15:21:12 +08:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
struct _sas_node *sas_node;
|
|
|
|
struct _sas_phy *mpt2sas_phy;
|
|
|
|
|
2009-09-23 19:51:29 +08:00
|
|
|
if (ioc->shost_recovery)
|
2009-08-20 15:52:00 +08:00
|
|
|
return;
|
|
|
|
|
2009-03-09 15:21:12 +08:00
|
|
|
spin_lock_irqsave(&ioc->sas_node_lock, flags);
|
2009-09-23 19:51:29 +08:00
|
|
|
sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address);
|
2009-03-09 15:21:12 +08:00
|
|
|
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
|
|
|
|
if (!sas_node)
|
|
|
|
return;
|
|
|
|
|
|
|
|
mpt2sas_phy = &sas_node->phy[phy_number];
|
2009-09-23 19:51:29 +08:00
|
|
|
mpt2sas_phy->attached_handle = handle;
|
|
|
|
if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5))
|
|
|
|
_transport_set_identify(ioc, handle,
|
2009-03-09 15:21:12 +08:00
|
|
|
&mpt2sas_phy->remote_identify);
|
|
|
|
else
|
|
|
|
memset(&mpt2sas_phy->remote_identify, 0 , sizeof(struct
|
|
|
|
sas_identify));
|
|
|
|
|
|
|
|
if (mpt2sas_phy->phy)
|
|
|
|
mpt2sas_phy->phy->negotiated_linkrate =
|
|
|
|
_transport_convert_phy_link_rate(link_rate);
|
|
|
|
|
|
|
|
if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
|
|
|
|
dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev,
|
2009-09-23 19:51:29 +08:00
|
|
|
"refresh: parent sas_addr(0x%016llx),\n"
|
2009-03-09 15:21:12 +08:00
|
|
|
"\tlink_rate(0x%02x), phy(%d)\n"
|
|
|
|
"\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
|
2009-09-23 19:51:29 +08:00
|
|
|
(unsigned long long)sas_address,
|
|
|
|
link_rate, phy_number, handle, (unsigned long long)
|
2009-03-09 15:21:12 +08:00
|
|
|
mpt2sas_phy->remote_identify.sas_address);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void *
|
|
|
|
phy_to_ioc(struct sas_phy *phy)
|
|
|
|
{
|
|
|
|
struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
|
|
|
|
return shost_priv(shost);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void *
|
|
|
|
rphy_to_ioc(struct sas_rphy *rphy)
|
|
|
|
{
|
|
|
|
struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
|
|
|
|
return shost_priv(shost);
|
|
|
|
}
|
|
|
|
|
2009-12-16 21:25:26 +08:00
|
|
|
static struct _sas_phy *
|
|
|
|
_transport_find_local_phy(struct MPT2SAS_ADAPTER *ioc, struct sas_phy *phy)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ioc->sas_hba.num_phys; i++)
|
|
|
|
if (ioc->sas_hba.phy[i].phy == phy)
|
|
|
|
return(&ioc->sas_hba.phy[i]);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-03-09 15:21:12 +08:00
|
|
|
/**
|
2009-05-19 03:02:08 +08:00
|
|
|
* _transport_get_linkerrors -
|
2009-03-09 15:21:12 +08:00
|
|
|
* @phy: The sas phy object
|
|
|
|
*
|
|
|
|
* Only support sas_host direct attached phys.
|
|
|
|
* Returns 0 for success, non-zero for failure.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static int
|
2009-05-19 03:02:08 +08:00
|
|
|
_transport_get_linkerrors(struct sas_phy *phy)
|
2009-03-09 15:21:12 +08:00
|
|
|
{
|
|
|
|
struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy);
|
|
|
|
struct _sas_phy *mpt2sas_phy;
|
|
|
|
Mpi2ConfigReply_t mpi_reply;
|
|
|
|
Mpi2SasPhyPage1_t phy_pg1;
|
|
|
|
|
2009-12-16 21:25:26 +08:00
|
|
|
mpt2sas_phy = _transport_find_local_phy(ioc, phy);
|
2009-03-09 15:21:12 +08:00
|
|
|
|
|
|
|
if (!mpt2sas_phy) /* this phy not on sas_host */
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if ((mpt2sas_config_get_phy_pg1(ioc, &mpi_reply, &phy_pg1,
|
|
|
|
mpt2sas_phy->phy_id))) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo)
|
|
|
|
printk(MPT2SAS_INFO_FMT "phy(%d), ioc_status"
|
|
|
|
"(0x%04x), loginfo(0x%08x)\n", ioc->name,
|
|
|
|
mpt2sas_phy->phy_id,
|
|
|
|
le16_to_cpu(mpi_reply.IOCStatus),
|
|
|
|
le32_to_cpu(mpi_reply.IOCLogInfo));
|
|
|
|
|
|
|
|
phy->invalid_dword_count = le32_to_cpu(phy_pg1.InvalidDwordCount);
|
|
|
|
phy->running_disparity_error_count =
|
|
|
|
le32_to_cpu(phy_pg1.RunningDisparityErrorCount);
|
|
|
|
phy->loss_of_dword_sync_count =
|
|
|
|
le32_to_cpu(phy_pg1.LossDwordSynchCount);
|
|
|
|
phy->phy_reset_problem_count =
|
|
|
|
le32_to_cpu(phy_pg1.PhyResetProblemCount);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-05-19 03:02:08 +08:00
|
|
|
* _transport_get_enclosure_identifier -
|
2009-03-09 15:21:12 +08:00
|
|
|
* @phy: The sas phy object
|
|
|
|
*
|
|
|
|
* Obtain the enclosure logical id for an expander.
|
|
|
|
* Returns 0 for success, non-zero for failure.
|
|
|
|
*/
|
|
|
|
static int
|
2009-05-19 03:02:08 +08:00
|
|
|
_transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier)
|
2009-03-09 15:21:12 +08:00
|
|
|
{
|
|
|
|
struct MPT2SAS_ADAPTER *ioc = rphy_to_ioc(rphy);
|
|
|
|
struct _sas_node *sas_expander;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ioc->sas_node_lock, flags);
|
|
|
|
sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc,
|
|
|
|
rphy->identify.sas_address);
|
|
|
|
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
|
|
|
|
|
|
|
|
if (!sas_expander)
|
|
|
|
return -ENXIO;
|
|
|
|
|
|
|
|
*identifier = sas_expander->enclosure_logical_id;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-05-19 03:02:08 +08:00
|
|
|
* _transport_get_bay_identifier -
|
2009-03-09 15:21:12 +08:00
|
|
|
* @phy: The sas phy object
|
|
|
|
*
|
|
|
|
* Returns the slot id for a device that resides inside an enclosure.
|
|
|
|
*/
|
|
|
|
static int
|
2009-05-19 03:02:08 +08:00
|
|
|
_transport_get_bay_identifier(struct sas_rphy *rphy)
|
2009-03-09 15:21:12 +08:00
|
|
|
{
|
|
|
|
struct MPT2SAS_ADAPTER *ioc = rphy_to_ioc(rphy);
|
|
|
|
struct _sas_device *sas_device;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ioc->sas_device_lock, flags);
|
|
|
|
sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
|
|
|
|
rphy->identify.sas_address);
|
|
|
|
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
|
|
|
|
|
|
|
|
if (!sas_device)
|
|
|
|
return -ENXIO;
|
|
|
|
|
|
|
|
return sas_device->slot;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-05-19 03:02:08 +08:00
|
|
|
* _transport_phy_reset -
|
2009-03-09 15:21:12 +08:00
|
|
|
* @phy: The sas phy object
|
|
|
|
* @hard_reset:
|
|
|
|
*
|
|
|
|
* Only support sas_host direct attached phys.
|
|
|
|
* Returns 0 for success, non-zero for failure.
|
|
|
|
*/
|
|
|
|
static int
|
2009-05-19 03:02:08 +08:00
|
|
|
_transport_phy_reset(struct sas_phy *phy, int hard_reset)
|
2009-03-09 15:21:12 +08:00
|
|
|
{
|
|
|
|
struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy);
|
|
|
|
struct _sas_phy *mpt2sas_phy;
|
|
|
|
Mpi2SasIoUnitControlReply_t mpi_reply;
|
|
|
|
Mpi2SasIoUnitControlRequest_t mpi_request;
|
|
|
|
|
2009-12-16 21:25:26 +08:00
|
|
|
mpt2sas_phy = _transport_find_local_phy(ioc, phy);
|
2009-03-09 15:21:12 +08:00
|
|
|
|
|
|
|
if (!mpt2sas_phy) /* this phy not on sas_host */
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlReply_t));
|
|
|
|
mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
|
|
|
|
mpi_request.Operation = hard_reset ?
|
|
|
|
MPI2_SAS_OP_PHY_HARD_RESET : MPI2_SAS_OP_PHY_LINK_RESET;
|
|
|
|
mpi_request.PhyNum = mpt2sas_phy->phy_id;
|
|
|
|
|
|
|
|
if ((mpt2sas_base_sas_iounit_control(ioc, &mpi_reply, &mpi_request))) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo)
|
|
|
|
printk(MPT2SAS_INFO_FMT "phy(%d), ioc_status"
|
|
|
|
"(0x%04x), loginfo(0x%08x)\n", ioc->name,
|
|
|
|
mpt2sas_phy->phy_id,
|
|
|
|
le16_to_cpu(mpi_reply.IOCStatus),
|
|
|
|
le32_to_cpu(mpi_reply.IOCLogInfo));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-12-16 21:25:26 +08:00
|
|
|
/**
|
|
|
|
* _transport_phy_enable - enable/disable phys
|
|
|
|
* @phy: The sas phy object
|
|
|
|
* @enable: enable phy when true
|
|
|
|
*
|
|
|
|
* Only support sas_host direct attached phys.
|
|
|
|
* Returns 0 for success, non-zero for failure.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
_transport_phy_enable(struct sas_phy *phy, int enable)
|
|
|
|
{
|
|
|
|
struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy);
|
|
|
|
struct _sas_phy *mpt2sas_phy;
|
|
|
|
Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
|
|
|
|
Mpi2ConfigReply_t mpi_reply;
|
|
|
|
u16 ioc_status;
|
|
|
|
u16 sz;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
mpt2sas_phy = _transport_find_local_phy(ioc, phy);
|
|
|
|
|
|
|
|
if (!mpt2sas_phy) /* this phy not on sas_host */
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* sas_iounit page 1 */
|
|
|
|
sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys *
|
|
|
|
sizeof(Mpi2SasIOUnit1PhyData_t));
|
|
|
|
sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
|
|
|
|
if (!sas_iounit_pg1) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
|
|
|
|
sas_iounit_pg1, sz))) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
rc = -ENXIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
|
|
|
|
MPI2_IOCSTATUS_MASK;
|
|
|
|
if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
rc = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (enable)
|
|
|
|
sas_iounit_pg1->PhyData[mpt2sas_phy->phy_id].PhyFlags
|
|
|
|
&= ~MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
|
|
|
|
else
|
|
|
|
sas_iounit_pg1->PhyData[mpt2sas_phy->phy_id].PhyFlags
|
|
|
|
|= MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
|
|
|
|
|
|
|
|
mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, sz);
|
|
|
|
|
|
|
|
out:
|
|
|
|
kfree(sas_iounit_pg1);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* _transport_phy_speed - set phy min/max link rates
|
|
|
|
* @phy: The sas phy object
|
|
|
|
* @rates: rates defined in sas_phy_linkrates
|
|
|
|
*
|
|
|
|
* Only support sas_host direct attached phys.
|
|
|
|
* Returns 0 for success, non-zero for failure.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
_transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates)
|
|
|
|
{
|
|
|
|
struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy);
|
|
|
|
struct _sas_phy *mpt2sas_phy;
|
|
|
|
Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
|
|
|
|
Mpi2SasPhyPage0_t phy_pg0;
|
|
|
|
Mpi2ConfigReply_t mpi_reply;
|
|
|
|
u16 ioc_status;
|
|
|
|
u16 sz;
|
|
|
|
int i;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
mpt2sas_phy = _transport_find_local_phy(ioc, phy);
|
|
|
|
|
|
|
|
if (!mpt2sas_phy) /* this phy not on sas_host */
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!rates->minimum_linkrate)
|
|
|
|
rates->minimum_linkrate = phy->minimum_linkrate;
|
|
|
|
else if (rates->minimum_linkrate < phy->minimum_linkrate_hw)
|
|
|
|
rates->minimum_linkrate = phy->minimum_linkrate_hw;
|
|
|
|
|
|
|
|
if (!rates->maximum_linkrate)
|
|
|
|
rates->maximum_linkrate = phy->maximum_linkrate;
|
|
|
|
else if (rates->maximum_linkrate > phy->maximum_linkrate_hw)
|
|
|
|
rates->maximum_linkrate = phy->maximum_linkrate_hw;
|
|
|
|
|
|
|
|
/* sas_iounit page 1 */
|
|
|
|
sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys *
|
|
|
|
sizeof(Mpi2SasIOUnit1PhyData_t));
|
|
|
|
sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
|
|
|
|
if (!sas_iounit_pg1) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
|
|
|
|
sas_iounit_pg1, sz))) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
rc = -ENXIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
|
|
|
|
MPI2_IOCSTATUS_MASK;
|
|
|
|
if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
rc = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ioc->sas_hba.num_phys; i++) {
|
|
|
|
if (mpt2sas_phy->phy_id != i) {
|
|
|
|
sas_iounit_pg1->PhyData[i].MaxMinLinkRate =
|
|
|
|
(ioc->sas_hba.phy[i].phy->minimum_linkrate +
|
|
|
|
(ioc->sas_hba.phy[i].phy->maximum_linkrate << 4));
|
|
|
|
} else {
|
|
|
|
sas_iounit_pg1->PhyData[i].MaxMinLinkRate =
|
|
|
|
(rates->minimum_linkrate +
|
|
|
|
(rates->maximum_linkrate << 4));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1,
|
|
|
|
sz)) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
|
|
|
|
ioc->name, __FILE__, __LINE__, __func__);
|
|
|
|
rc = -ENXIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* link reset */
|
|
|
|
_transport_phy_reset(phy, 0);
|
|
|
|
|
|
|
|
/* read phy page 0, then update the rates in the sas transport phy */
|
|
|
|
if (!mpt2sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0,
|
|
|
|
mpt2sas_phy->phy_id)) {
|
|
|
|
phy->minimum_linkrate = _transport_convert_phy_link_rate(
|
|
|
|
phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
|
|
|
|
phy->maximum_linkrate = _transport_convert_phy_link_rate(
|
|
|
|
phy_pg0.ProgrammedLinkRate >> 4);
|
|
|
|
phy->negotiated_linkrate = _transport_convert_phy_link_rate(
|
|
|
|
phy_pg0.NegotiatedLinkRate &
|
|
|
|
MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
kfree(sas_iounit_pg1);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-09 15:21:12 +08:00
|
|
|
/**
|
2009-05-19 03:02:08 +08:00
|
|
|
* _transport_smp_handler - transport portal for smp passthru
|
2009-03-09 15:21:12 +08:00
|
|
|
* @shost: shost object
|
|
|
|
* @rphy: sas transport rphy object
|
|
|
|
* @req:
|
|
|
|
*
|
|
|
|
* This used primarily for smp_utils.
|
|
|
|
* Example:
|
|
|
|
* smp_rep_general /sys/class/bsg/expander-5:0
|
|
|
|
*/
|
|
|
|
static int
|
2009-05-19 03:02:08 +08:00
|
|
|
_transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
|
2009-03-09 15:21:12 +08:00
|
|
|
struct request *req)
|
|
|
|
{
|
|
|
|
struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
|
|
|
|
Mpi2SmpPassthroughRequest_t *mpi_request;
|
|
|
|
Mpi2SmpPassthroughReply_t *mpi_reply;
|
|
|
|
int rc;
|
|
|
|
u16 smid;
|
|
|
|
u32 ioc_state;
|
|
|
|
unsigned long timeleft;
|
|
|
|
void *psge;
|
|
|
|
u32 sgl_flags;
|
|
|
|
u8 issue_reset = 0;
|
|
|
|
dma_addr_t dma_addr_in = 0;
|
|
|
|
dma_addr_t dma_addr_out = 0;
|
|
|
|
u16 wait_state_count;
|
|
|
|
struct request *rsp = req->next_rq;
|
|
|
|
|
|
|
|
if (!rsp) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "%s: the smp response space is "
|
|
|
|
"missing\n", ioc->name, __func__);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* do we need to support multiple segments? */
|
|
|
|
if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "%s: multiple segments req %u %u, "
|
|
|
|
"rsp %u %u\n", ioc->name, __func__, req->bio->bi_vcnt,
|
2009-05-07 21:24:42 +08:00
|
|
|
blk_rq_bytes(req), rsp->bio->bi_vcnt, blk_rq_bytes(rsp));
|
2009-03-09 15:21:12 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2009-08-20 15:52:00 +08:00
|
|
|
if (ioc->shost_recovery) {
|
2009-03-09 15:21:12 +08:00
|
|
|
printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
|
|
|
|
__func__, ioc->name);
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = mutex_lock_interruptible(&ioc->transport_cmds.mutex);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if (ioc->transport_cmds.status != MPT2_CMD_NOT_USED) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "%s: transport_cmds in use\n", ioc->name,
|
|
|
|
__func__);
|
|
|
|
rc = -EAGAIN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ioc->transport_cmds.status = MPT2_CMD_PENDING;
|
|
|
|
|
|
|
|
wait_state_count = 0;
|
|
|
|
ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
|
|
|
|
while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
|
|
|
|
if (wait_state_count++ == 10) {
|
|
|
|
printk(MPT2SAS_ERR_FMT
|
|
|
|
"%s: failed due to ioc not operational\n",
|
|
|
|
ioc->name, __func__);
|
|
|
|
rc = -EFAULT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ssleep(1);
|
|
|
|
ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
|
|
|
|
printk(MPT2SAS_INFO_FMT "%s: waiting for "
|
|
|
|
"operational state(count=%d)\n", ioc->name,
|
|
|
|
__func__, wait_state_count);
|
|
|
|
}
|
|
|
|
if (wait_state_count)
|
|
|
|
printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n",
|
|
|
|
ioc->name, __func__);
|
|
|
|
|
|
|
|
smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx);
|
|
|
|
if (!smid) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
|
|
|
|
ioc->name, __func__);
|
|
|
|
rc = -EAGAIN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
|
|
|
|
ioc->transport_cmds.smid = smid;
|
|
|
|
|
|
|
|
memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
|
|
|
|
mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
|
|
|
|
mpi_request->PhysicalPort = 0xFF;
|
2009-09-25 14:14:41 +08:00
|
|
|
mpi_request->VF_ID = 0; /* TODO */
|
|
|
|
mpi_request->VP_ID = 0;
|
2009-03-09 15:21:12 +08:00
|
|
|
*((u64 *)&mpi_request->SASAddress) = (rphy) ?
|
|
|
|
cpu_to_le64(rphy->identify.sas_address) :
|
|
|
|
cpu_to_le64(ioc->sas_hba.sas_address);
|
2009-05-07 21:24:42 +08:00
|
|
|
mpi_request->RequestDataLength = cpu_to_le16(blk_rq_bytes(req) - 4);
|
2009-03-09 15:21:12 +08:00
|
|
|
psge = &mpi_request->SGL;
|
|
|
|
|
|
|
|
/* WRITE sgel first */
|
|
|
|
sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
|
|
|
|
MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC);
|
|
|
|
sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
|
|
|
|
dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio),
|
2009-05-07 21:24:42 +08:00
|
|
|
blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL);
|
2009-03-09 15:21:12 +08:00
|
|
|
if (!dma_addr_out) {
|
2009-10-05 18:23:06 +08:00
|
|
|
mpt2sas_base_free_smid(ioc, smid);
|
2009-03-09 15:21:12 +08:00
|
|
|
goto unmap;
|
|
|
|
}
|
|
|
|
|
2009-05-07 21:24:42 +08:00
|
|
|
ioc->base_add_sg_single(psge, sgl_flags | (blk_rq_bytes(req) - 4),
|
2009-03-09 15:21:12 +08:00
|
|
|
dma_addr_out);
|
|
|
|
|
|
|
|
/* incr sgel */
|
|
|
|
psge += ioc->sge_size;
|
|
|
|
|
|
|
|
/* READ sgel last */
|
|
|
|
sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
|
|
|
|
MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
|
|
|
|
MPI2_SGE_FLAGS_END_OF_LIST);
|
|
|
|
sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
|
2009-05-07 21:24:42 +08:00
|
|
|
dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio),
|
|
|
|
blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL);
|
2009-03-09 15:21:12 +08:00
|
|
|
if (!dma_addr_in) {
|
2009-10-05 18:23:06 +08:00
|
|
|
mpt2sas_base_free_smid(ioc, smid);
|
2009-03-09 15:21:12 +08:00
|
|
|
goto unmap;
|
|
|
|
}
|
|
|
|
|
2009-05-07 21:24:42 +08:00
|
|
|
ioc->base_add_sg_single(psge, sgl_flags | (blk_rq_bytes(rsp) + 4),
|
2009-03-09 15:21:12 +08:00
|
|
|
dma_addr_in);
|
|
|
|
|
|
|
|
dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s - "
|
|
|
|
"sending smp request\n", ioc->name, __func__));
|
|
|
|
|
2009-09-25 14:14:41 +08:00
|
|
|
mpt2sas_base_put_smid_default(ioc, smid);
|
2009-09-14 13:35:24 +08:00
|
|
|
init_completion(&ioc->transport_cmds.done);
|
2009-03-09 15:21:12 +08:00
|
|
|
timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done,
|
|
|
|
10*HZ);
|
|
|
|
|
|
|
|
if (!(ioc->transport_cmds.status & MPT2_CMD_COMPLETE)) {
|
|
|
|
printk(MPT2SAS_ERR_FMT "%s : timeout\n",
|
|
|
|
__func__, ioc->name);
|
|
|
|
_debug_dump_mf(mpi_request,
|
|
|
|
sizeof(Mpi2SmpPassthroughRequest_t)/4);
|
|
|
|
if (!(ioc->transport_cmds.status & MPT2_CMD_RESET))
|
|
|
|
issue_reset = 1;
|
|
|
|
goto issue_host_reset;
|
|
|
|
}
|
|
|
|
|
|
|
|
dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s - "
|
|
|
|
"complete\n", ioc->name, __func__));
|
|
|
|
|
|
|
|
if (ioc->transport_cmds.status & MPT2_CMD_REPLY_VALID) {
|
|
|
|
|
|
|
|
mpi_reply = ioc->transport_cmds.reply;
|
|
|
|
|
|
|
|
dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT
|
|
|
|
"%s - reply data transfer size(%d)\n",
|
|
|
|
ioc->name, __func__,
|
|
|
|
le16_to_cpu(mpi_reply->ResponseDataLength)));
|
|
|
|
|
|
|
|
memcpy(req->sense, mpi_reply, sizeof(*mpi_reply));
|
|
|
|
req->sense_len = sizeof(*mpi_reply);
|
2009-05-19 17:33:05 +08:00
|
|
|
req->resid_len = 0;
|
2010-03-17 18:54:52 +08:00
|
|
|
rsp->resid_len -=
|
|
|
|
le16_to_cpu(mpi_reply->ResponseDataLength);
|
2009-03-09 15:21:12 +08:00
|
|
|
} else {
|
|
|
|
dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT
|
|
|
|
"%s - no reply\n", ioc->name, __func__));
|
|
|
|
rc = -ENXIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
issue_host_reset:
|
|
|
|
if (issue_reset) {
|
|
|
|
mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
|
|
|
|
FORCE_BIG_HAMMER);
|
|
|
|
rc = -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
unmap:
|
|
|
|
if (dma_addr_out)
|
2009-05-07 21:24:42 +08:00
|
|
|
pci_unmap_single(ioc->pdev, dma_addr_out, blk_rq_bytes(req),
|
2009-03-09 15:21:12 +08:00
|
|
|
PCI_DMA_BIDIRECTIONAL);
|
|
|
|
if (dma_addr_in)
|
2009-05-07 21:24:42 +08:00
|
|
|
pci_unmap_single(ioc->pdev, dma_addr_in, blk_rq_bytes(rsp),
|
2009-03-09 15:21:12 +08:00
|
|
|
PCI_DMA_BIDIRECTIONAL);
|
|
|
|
|
|
|
|
out:
|
|
|
|
ioc->transport_cmds.status = MPT2_CMD_NOT_USED;
|
|
|
|
mutex_unlock(&ioc->transport_cmds.mutex);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sas_function_template mpt2sas_transport_functions = {
|
2009-05-19 03:02:08 +08:00
|
|
|
.get_linkerrors = _transport_get_linkerrors,
|
|
|
|
.get_enclosure_identifier = _transport_get_enclosure_identifier,
|
|
|
|
.get_bay_identifier = _transport_get_bay_identifier,
|
|
|
|
.phy_reset = _transport_phy_reset,
|
2009-12-16 21:25:26 +08:00
|
|
|
.phy_enable = _transport_phy_enable,
|
|
|
|
.set_phy_speed = _transport_phy_speed,
|
2009-05-19 03:02:08 +08:00
|
|
|
.smp_handler = _transport_smp_handler,
|
2009-03-09 15:21:12 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct scsi_transport_template *mpt2sas_transport_template;
|