mirror of
https://github.com/torvalds/linux.git
synced 2024-12-04 18:13:04 +00:00
cacb6dc3d7
These particular problems were reported by Cisco and SAP and customers as well. Cisco reported on RHEL4 U6 and SAP reported on SLES9 SP4 and SLES10 SP2. We added these fixes on RHEL4 U6 and gave a private build to IBM and Cisco. Cisco and IBM tested it for more than 15 days and they reported that they did not see the issue so far. Before the fix, Cisco used to see the issue within 5 days. We generated a patch for SLES9 SP4 and SLES10 SP2 and submitted to Novell. Novell applied the patch and gave a test build to SAP. SAP tested and reported that the build is working properly. We also tested in our lab using the tools "dishogsync", which is IO stress tool and the tool was provided by Cisco. Issue1: File System going into read-only mode Root cause: The driver tends to not free the memory (FIB) when the management request exits prematurely. The accumulation of such un-freed memory causes the driver to fail to allocate anymore memory (FIB) and hence return 0x70000 value to the upper layer, which puts the file system into read only mode. Fix details: The fix makes sure to free the memory (FIB) even if the request exits prematurely hence ensuring the driver wouldn't run out of memory (FIBs). Issue2: False Raid Alert occurs When the Physical Drives and Logical drives are reported as deleted or added, even though there is no change done on the system Root cause: Driver IOCTLs is signaled with EINTR while waiting on response from the lower layers. Returning "EINTR" will never initiate internal retry. Fix details: The issue was fixed by replacing "EINTR" with "ERESTARTSYS" for mid-layer retries. Signed-off-by: Penchala Narasimha Reddy <ServeRAIDDriver@hcl.in> Signed-off-by: James Bottomley <James.Bottomley@suse.de>
2941 lines
83 KiB
C
2941 lines
83 KiB
C
/*
|
|
* Adaptec AAC series RAID controller driver
|
|
* (c) Copyright 2001 Red Hat Inc.
|
|
*
|
|
* based on the old aacraid driver that is..
|
|
* Adaptec aacraid device driver for Linux.
|
|
*
|
|
* Copyright (c) 2000-2007 Adaptec, Inc. (aacraid@adaptec.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, 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/types.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/completion.h>
|
|
#include <linux/blkdev.h>
|
|
#include <asm/uaccess.h>
|
|
#include <linux/highmem.h> /* For flush_kernel_dcache_page */
|
|
|
|
#include <scsi/scsi.h>
|
|
#include <scsi/scsi_cmnd.h>
|
|
#include <scsi/scsi_device.h>
|
|
#include <scsi/scsi_host.h>
|
|
|
|
#include "aacraid.h"
|
|
|
|
/* values for inqd_pdt: Peripheral device type in plain English */
|
|
#define INQD_PDT_DA 0x00 /* Direct-access (DISK) device */
|
|
#define INQD_PDT_PROC 0x03 /* Processor device */
|
|
#define INQD_PDT_CHNGR 0x08 /* Changer (jukebox, scsi2) */
|
|
#define INQD_PDT_COMM 0x09 /* Communication device (scsi2) */
|
|
#define INQD_PDT_NOLUN2 0x1f /* Unknown Device (scsi2) */
|
|
#define INQD_PDT_NOLUN 0x7f /* Logical Unit Not Present */
|
|
|
|
#define INQD_PDT_DMASK 0x1F /* Peripheral Device Type Mask */
|
|
#define INQD_PDT_QMASK 0xE0 /* Peripheral Device Qualifer Mask */
|
|
|
|
/*
|
|
* Sense codes
|
|
*/
|
|
|
|
#define SENCODE_NO_SENSE 0x00
|
|
#define SENCODE_END_OF_DATA 0x00
|
|
#define SENCODE_BECOMING_READY 0x04
|
|
#define SENCODE_INIT_CMD_REQUIRED 0x04
|
|
#define SENCODE_PARAM_LIST_LENGTH_ERROR 0x1A
|
|
#define SENCODE_INVALID_COMMAND 0x20
|
|
#define SENCODE_LBA_OUT_OF_RANGE 0x21
|
|
#define SENCODE_INVALID_CDB_FIELD 0x24
|
|
#define SENCODE_LUN_NOT_SUPPORTED 0x25
|
|
#define SENCODE_INVALID_PARAM_FIELD 0x26
|
|
#define SENCODE_PARAM_NOT_SUPPORTED 0x26
|
|
#define SENCODE_PARAM_VALUE_INVALID 0x26
|
|
#define SENCODE_RESET_OCCURRED 0x29
|
|
#define SENCODE_LUN_NOT_SELF_CONFIGURED_YET 0x3E
|
|
#define SENCODE_INQUIRY_DATA_CHANGED 0x3F
|
|
#define SENCODE_SAVING_PARAMS_NOT_SUPPORTED 0x39
|
|
#define SENCODE_DIAGNOSTIC_FAILURE 0x40
|
|
#define SENCODE_INTERNAL_TARGET_FAILURE 0x44
|
|
#define SENCODE_INVALID_MESSAGE_ERROR 0x49
|
|
#define SENCODE_LUN_FAILED_SELF_CONFIG 0x4c
|
|
#define SENCODE_OVERLAPPED_COMMAND 0x4E
|
|
|
|
/*
|
|
* Additional sense codes
|
|
*/
|
|
|
|
#define ASENCODE_NO_SENSE 0x00
|
|
#define ASENCODE_END_OF_DATA 0x05
|
|
#define ASENCODE_BECOMING_READY 0x01
|
|
#define ASENCODE_INIT_CMD_REQUIRED 0x02
|
|
#define ASENCODE_PARAM_LIST_LENGTH_ERROR 0x00
|
|
#define ASENCODE_INVALID_COMMAND 0x00
|
|
#define ASENCODE_LBA_OUT_OF_RANGE 0x00
|
|
#define ASENCODE_INVALID_CDB_FIELD 0x00
|
|
#define ASENCODE_LUN_NOT_SUPPORTED 0x00
|
|
#define ASENCODE_INVALID_PARAM_FIELD 0x00
|
|
#define ASENCODE_PARAM_NOT_SUPPORTED 0x01
|
|
#define ASENCODE_PARAM_VALUE_INVALID 0x02
|
|
#define ASENCODE_RESET_OCCURRED 0x00
|
|
#define ASENCODE_LUN_NOT_SELF_CONFIGURED_YET 0x00
|
|
#define ASENCODE_INQUIRY_DATA_CHANGED 0x03
|
|
#define ASENCODE_SAVING_PARAMS_NOT_SUPPORTED 0x00
|
|
#define ASENCODE_DIAGNOSTIC_FAILURE 0x80
|
|
#define ASENCODE_INTERNAL_TARGET_FAILURE 0x00
|
|
#define ASENCODE_INVALID_MESSAGE_ERROR 0x00
|
|
#define ASENCODE_LUN_FAILED_SELF_CONFIG 0x00
|
|
#define ASENCODE_OVERLAPPED_COMMAND 0x00
|
|
|
|
#define BYTE0(x) (unsigned char)(x)
|
|
#define BYTE1(x) (unsigned char)((x) >> 8)
|
|
#define BYTE2(x) (unsigned char)((x) >> 16)
|
|
#define BYTE3(x) (unsigned char)((x) >> 24)
|
|
|
|
/*------------------------------------------------------------------------------
|
|
* S T R U C T S / T Y P E D E F S
|
|
*----------------------------------------------------------------------------*/
|
|
/* SCSI inquiry data */
|
|
struct inquiry_data {
|
|
u8 inqd_pdt; /* Peripheral qualifier | Peripheral Device Type */
|
|
u8 inqd_dtq; /* RMB | Device Type Qualifier */
|
|
u8 inqd_ver; /* ISO version | ECMA version | ANSI-approved version */
|
|
u8 inqd_rdf; /* AENC | TrmIOP | Response data format */
|
|
u8 inqd_len; /* Additional length (n-4) */
|
|
u8 inqd_pad1[2];/* Reserved - must be zero */
|
|
u8 inqd_pad2; /* RelAdr | WBus32 | WBus16 | Sync | Linked |Reserved| CmdQue | SftRe */
|
|
u8 inqd_vid[8]; /* Vendor ID */
|
|
u8 inqd_pid[16];/* Product ID */
|
|
u8 inqd_prl[4]; /* Product Revision Level */
|
|
};
|
|
|
|
/*
|
|
* M O D U L E G L O B A L S
|
|
*/
|
|
|
|
static unsigned long aac_build_sg(struct scsi_cmnd* scsicmd, struct sgmap* sgmap);
|
|
static unsigned long aac_build_sg64(struct scsi_cmnd* scsicmd, struct sgmap64* psg);
|
|
static unsigned long aac_build_sgraw(struct scsi_cmnd* scsicmd, struct sgmapraw* psg);
|
|
static int aac_send_srb_fib(struct scsi_cmnd* scsicmd);
|
|
#ifdef AAC_DETAILED_STATUS_INFO
|
|
static char *aac_get_status_string(u32 status);
|
|
#endif
|
|
|
|
/*
|
|
* Non dasd selection is handled entirely in aachba now
|
|
*/
|
|
|
|
static int nondasd = -1;
|
|
static int aac_cache = 2; /* WCE=0 to avoid performance problems */
|
|
static int dacmode = -1;
|
|
int aac_msi;
|
|
int aac_commit = -1;
|
|
int startup_timeout = 180;
|
|
int aif_timeout = 120;
|
|
|
|
module_param(nondasd, int, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(nondasd, "Control scanning of hba for nondasd devices."
|
|
" 0=off, 1=on");
|
|
module_param_named(cache, aac_cache, int, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(cache, "Disable Queue Flush commands:\n"
|
|
"\tbit 0 - Disable FUA in WRITE SCSI commands\n"
|
|
"\tbit 1 - Disable SYNCHRONIZE_CACHE SCSI command\n"
|
|
"\tbit 2 - Disable only if Battery is protecting Cache");
|
|
module_param(dacmode, int, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(dacmode, "Control whether dma addressing is using 64 bit DAC."
|
|
" 0=off, 1=on");
|
|
module_param_named(commit, aac_commit, int, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(commit, "Control whether a COMMIT_CONFIG is issued to the"
|
|
" adapter for foreign arrays.\n"
|
|
"This is typically needed in systems that do not have a BIOS."
|
|
" 0=off, 1=on");
|
|
module_param_named(msi, aac_msi, int, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(msi, "IRQ handling."
|
|
" 0=PIC(default), 1=MSI, 2=MSI-X(unsupported, uses MSI)");
|
|
module_param(startup_timeout, int, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(startup_timeout, "The duration of time in seconds to wait for"
|
|
" adapter to have it's kernel up and\n"
|
|
"running. This is typically adjusted for large systems that do not"
|
|
" have a BIOS.");
|
|
module_param(aif_timeout, int, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(aif_timeout, "The duration of time in seconds to wait for"
|
|
" applications to pick up AIFs before\n"
|
|
"deregistering them. This is typically adjusted for heavily burdened"
|
|
" systems.");
|
|
|
|
int numacb = -1;
|
|
module_param(numacb, int, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(numacb, "Request a limit to the number of adapter control"
|
|
" blocks (FIB) allocated. Valid values are 512 and down. Default is"
|
|
" to use suggestion from Firmware.");
|
|
|
|
int acbsize = -1;
|
|
module_param(acbsize, int, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(acbsize, "Request a specific adapter control block (FIB)"
|
|
" size. Valid values are 512, 2048, 4096 and 8192. Default is to use"
|
|
" suggestion from Firmware.");
|
|
|
|
int update_interval = 30 * 60;
|
|
module_param(update_interval, int, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(update_interval, "Interval in seconds between time sync"
|
|
" updates issued to adapter.");
|
|
|
|
int check_interval = 24 * 60 * 60;
|
|
module_param(check_interval, int, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(check_interval, "Interval in seconds between adapter health"
|
|
" checks.");
|
|
|
|
int aac_check_reset = 1;
|
|
module_param_named(check_reset, aac_check_reset, int, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(check_reset, "If adapter fails health check, reset the"
|
|
" adapter. a value of -1 forces the reset to adapters programmed to"
|
|
" ignore it.");
|
|
|
|
int expose_physicals = -1;
|
|
module_param(expose_physicals, int, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(expose_physicals, "Expose physical components of the arrays."
|
|
" -1=protect 0=off, 1=on");
|
|
|
|
int aac_reset_devices;
|
|
module_param_named(reset_devices, aac_reset_devices, int, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(reset_devices, "Force an adapter reset at initialization.");
|
|
|
|
int aac_wwn = 1;
|
|
module_param_named(wwn, aac_wwn, int, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(wwn, "Select a WWN type for the arrays:\n"
|
|
"\t0 - Disable\n"
|
|
"\t1 - Array Meta Data Signature (default)\n"
|
|
"\t2 - Adapter Serial Number");
|
|
|
|
|
|
static inline int aac_valid_context(struct scsi_cmnd *scsicmd,
|
|
struct fib *fibptr) {
|
|
struct scsi_device *device;
|
|
|
|
if (unlikely(!scsicmd || !scsicmd->scsi_done)) {
|
|
dprintk((KERN_WARNING "aac_valid_context: scsi command corrupt\n"));
|
|
aac_fib_complete(fibptr);
|
|
aac_fib_free(fibptr);
|
|
return 0;
|
|
}
|
|
scsicmd->SCp.phase = AAC_OWNER_MIDLEVEL;
|
|
device = scsicmd->device;
|
|
if (unlikely(!device || !scsi_device_online(device))) {
|
|
dprintk((KERN_WARNING "aac_valid_context: scsi device corrupt\n"));
|
|
aac_fib_complete(fibptr);
|
|
aac_fib_free(fibptr);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* aac_get_config_status - check the adapter configuration
|
|
* @common: adapter to query
|
|
*
|
|
* Query config status, and commit the configuration if needed.
|
|
*/
|
|
int aac_get_config_status(struct aac_dev *dev, int commit_flag)
|
|
{
|
|
int status = 0;
|
|
struct fib * fibptr;
|
|
|
|
if (!(fibptr = aac_fib_alloc(dev)))
|
|
return -ENOMEM;
|
|
|
|
aac_fib_init(fibptr);
|
|
{
|
|
struct aac_get_config_status *dinfo;
|
|
dinfo = (struct aac_get_config_status *) fib_data(fibptr);
|
|
|
|
dinfo->command = cpu_to_le32(VM_ContainerConfig);
|
|
dinfo->type = cpu_to_le32(CT_GET_CONFIG_STATUS);
|
|
dinfo->count = cpu_to_le32(sizeof(((struct aac_get_config_status_resp *)NULL)->data));
|
|
}
|
|
|
|
status = aac_fib_send(ContainerCommand,
|
|
fibptr,
|
|
sizeof (struct aac_get_config_status),
|
|
FsaNormal,
|
|
1, 1,
|
|
NULL, NULL);
|
|
if (status < 0) {
|
|
printk(KERN_WARNING "aac_get_config_status: SendFIB failed.\n");
|
|
} else {
|
|
struct aac_get_config_status_resp *reply
|
|
= (struct aac_get_config_status_resp *) fib_data(fibptr);
|
|
dprintk((KERN_WARNING
|
|
"aac_get_config_status: response=%d status=%d action=%d\n",
|
|
le32_to_cpu(reply->response),
|
|
le32_to_cpu(reply->status),
|
|
le32_to_cpu(reply->data.action)));
|
|
if ((le32_to_cpu(reply->response) != ST_OK) ||
|
|
(le32_to_cpu(reply->status) != CT_OK) ||
|
|
(le32_to_cpu(reply->data.action) > CFACT_PAUSE)) {
|
|
printk(KERN_WARNING "aac_get_config_status: Will not issue the Commit Configuration\n");
|
|
status = -EINVAL;
|
|
}
|
|
}
|
|
/* Do not set XferState to zero unless receives a response from F/W */
|
|
if (status >= 0)
|
|
aac_fib_complete(fibptr);
|
|
|
|
/* Send a CT_COMMIT_CONFIG to enable discovery of devices */
|
|
if (status >= 0) {
|
|
if ((aac_commit == 1) || commit_flag) {
|
|
struct aac_commit_config * dinfo;
|
|
aac_fib_init(fibptr);
|
|
dinfo = (struct aac_commit_config *) fib_data(fibptr);
|
|
|
|
dinfo->command = cpu_to_le32(VM_ContainerConfig);
|
|
dinfo->type = cpu_to_le32(CT_COMMIT_CONFIG);
|
|
|
|
status = aac_fib_send(ContainerCommand,
|
|
fibptr,
|
|
sizeof (struct aac_commit_config),
|
|
FsaNormal,
|
|
1, 1,
|
|
NULL, NULL);
|
|
/* Do not set XferState to zero unless
|
|
* receives a response from F/W */
|
|
if (status >= 0)
|
|
aac_fib_complete(fibptr);
|
|
} else if (aac_commit == 0) {
|
|
printk(KERN_WARNING
|
|
"aac_get_config_status: Foreign device configurations are being ignored\n");
|
|
}
|
|
}
|
|
/* FIB should be freed only after getting the response from the F/W */
|
|
if (status != -ERESTARTSYS)
|
|
aac_fib_free(fibptr);
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* aac_get_containers - list containers
|
|
* @common: adapter to probe
|
|
*
|
|
* Make a list of all containers on this controller
|
|
*/
|
|
int aac_get_containers(struct aac_dev *dev)
|
|
{
|
|
struct fsa_dev_info *fsa_dev_ptr;
|
|
u32 index;
|
|
int status = 0;
|
|
struct fib * fibptr;
|
|
struct aac_get_container_count *dinfo;
|
|
struct aac_get_container_count_resp *dresp;
|
|
int maximum_num_containers = MAXIMUM_NUM_CONTAINERS;
|
|
|
|
if (!(fibptr = aac_fib_alloc(dev)))
|
|
return -ENOMEM;
|
|
|
|
aac_fib_init(fibptr);
|
|
dinfo = (struct aac_get_container_count *) fib_data(fibptr);
|
|
dinfo->command = cpu_to_le32(VM_ContainerConfig);
|
|
dinfo->type = cpu_to_le32(CT_GET_CONTAINER_COUNT);
|
|
|
|
status = aac_fib_send(ContainerCommand,
|
|
fibptr,
|
|
sizeof (struct aac_get_container_count),
|
|
FsaNormal,
|
|
1, 1,
|
|
NULL, NULL);
|
|
if (status >= 0) {
|
|
dresp = (struct aac_get_container_count_resp *)fib_data(fibptr);
|
|
maximum_num_containers = le32_to_cpu(dresp->ContainerSwitchEntries);
|
|
aac_fib_complete(fibptr);
|
|
}
|
|
/* FIB should be freed only after getting the response from the F/W */
|
|
if (status != -ERESTARTSYS)
|
|
aac_fib_free(fibptr);
|
|
|
|
if (maximum_num_containers < MAXIMUM_NUM_CONTAINERS)
|
|
maximum_num_containers = MAXIMUM_NUM_CONTAINERS;
|
|
fsa_dev_ptr = kzalloc(sizeof(*fsa_dev_ptr) * maximum_num_containers,
|
|
GFP_KERNEL);
|
|
if (!fsa_dev_ptr)
|
|
return -ENOMEM;
|
|
|
|
dev->fsa_dev = fsa_dev_ptr;
|
|
dev->maximum_num_containers = maximum_num_containers;
|
|
|
|
for (index = 0; index < dev->maximum_num_containers; ) {
|
|
fsa_dev_ptr[index].devname[0] = '\0';
|
|
|
|
status = aac_probe_container(dev, index);
|
|
|
|
if (status < 0) {
|
|
printk(KERN_WARNING "aac_get_containers: SendFIB failed.\n");
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* If there are no more containers, then stop asking.
|
|
*/
|
|
if (++index >= status)
|
|
break;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static void get_container_name_callback(void *context, struct fib * fibptr)
|
|
{
|
|
struct aac_get_name_resp * get_name_reply;
|
|
struct scsi_cmnd * scsicmd;
|
|
|
|
scsicmd = (struct scsi_cmnd *) context;
|
|
|
|
if (!aac_valid_context(scsicmd, fibptr))
|
|
return;
|
|
|
|
dprintk((KERN_DEBUG "get_container_name_callback[cpu %d]: t = %ld.\n", smp_processor_id(), jiffies));
|
|
BUG_ON(fibptr == NULL);
|
|
|
|
get_name_reply = (struct aac_get_name_resp *) fib_data(fibptr);
|
|
/* Failure is irrelevant, using default value instead */
|
|
if ((le32_to_cpu(get_name_reply->status) == CT_OK)
|
|
&& (get_name_reply->data[0] != '\0')) {
|
|
char *sp = get_name_reply->data;
|
|
sp[sizeof(((struct aac_get_name_resp *)NULL)->data)-1] = '\0';
|
|
while (*sp == ' ')
|
|
++sp;
|
|
if (*sp) {
|
|
struct inquiry_data inq;
|
|
char d[sizeof(((struct inquiry_data *)NULL)->inqd_pid)];
|
|
int count = sizeof(d);
|
|
char *dp = d;
|
|
do {
|
|
*dp++ = (*sp) ? *sp++ : ' ';
|
|
} while (--count > 0);
|
|
|
|
scsi_sg_copy_to_buffer(scsicmd, &inq, sizeof(inq));
|
|
memcpy(inq.inqd_pid, d, sizeof(d));
|
|
scsi_sg_copy_from_buffer(scsicmd, &inq, sizeof(inq));
|
|
}
|
|
}
|
|
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
|
|
|
|
aac_fib_complete(fibptr);
|
|
aac_fib_free(fibptr);
|
|
scsicmd->scsi_done(scsicmd);
|
|
}
|
|
|
|
/**
|
|
* aac_get_container_name - get container name, none blocking.
|
|
*/
|
|
static int aac_get_container_name(struct scsi_cmnd * scsicmd)
|
|
{
|
|
int status;
|
|
struct aac_get_name *dinfo;
|
|
struct fib * cmd_fibcontext;
|
|
struct aac_dev * dev;
|
|
|
|
dev = (struct aac_dev *)scsicmd->device->host->hostdata;
|
|
|
|
if (!(cmd_fibcontext = aac_fib_alloc(dev)))
|
|
return -ENOMEM;
|
|
|
|
aac_fib_init(cmd_fibcontext);
|
|
dinfo = (struct aac_get_name *) fib_data(cmd_fibcontext);
|
|
|
|
dinfo->command = cpu_to_le32(VM_ContainerConfig);
|
|
dinfo->type = cpu_to_le32(CT_READ_NAME);
|
|
dinfo->cid = cpu_to_le32(scmd_id(scsicmd));
|
|
dinfo->count = cpu_to_le32(sizeof(((struct aac_get_name_resp *)NULL)->data));
|
|
|
|
status = aac_fib_send(ContainerCommand,
|
|
cmd_fibcontext,
|
|
sizeof (struct aac_get_name),
|
|
FsaNormal,
|
|
0, 1,
|
|
(fib_callback)get_container_name_callback,
|
|
(void *) scsicmd);
|
|
|
|
/*
|
|
* Check that the command queued to the controller
|
|
*/
|
|
if (status == -EINPROGRESS) {
|
|
scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
|
|
return 0;
|
|
}
|
|
|
|
printk(KERN_WARNING "aac_get_container_name: aac_fib_send failed with status: %d.\n", status);
|
|
aac_fib_complete(cmd_fibcontext);
|
|
aac_fib_free(cmd_fibcontext);
|
|
return -1;
|
|
}
|
|
|
|
static int aac_probe_container_callback2(struct scsi_cmnd * scsicmd)
|
|
{
|
|
struct fsa_dev_info *fsa_dev_ptr = ((struct aac_dev *)(scsicmd->device->host->hostdata))->fsa_dev;
|
|
|
|
if ((fsa_dev_ptr[scmd_id(scsicmd)].valid & 1))
|
|
return aac_scsi_cmd(scsicmd);
|
|
|
|
scsicmd->result = DID_NO_CONNECT << 16;
|
|
scsicmd->scsi_done(scsicmd);
|
|
return 0;
|
|
}
|
|
|
|
static void _aac_probe_container2(void * context, struct fib * fibptr)
|
|
{
|
|
struct fsa_dev_info *fsa_dev_ptr;
|
|
int (*callback)(struct scsi_cmnd *);
|
|
struct scsi_cmnd * scsicmd = (struct scsi_cmnd *)context;
|
|
|
|
|
|
if (!aac_valid_context(scsicmd, fibptr))
|
|
return;
|
|
|
|
scsicmd->SCp.Status = 0;
|
|
fsa_dev_ptr = fibptr->dev->fsa_dev;
|
|
if (fsa_dev_ptr) {
|
|
struct aac_mount * dresp = (struct aac_mount *) fib_data(fibptr);
|
|
fsa_dev_ptr += scmd_id(scsicmd);
|
|
|
|
if ((le32_to_cpu(dresp->status) == ST_OK) &&
|
|
(le32_to_cpu(dresp->mnt[0].vol) != CT_NONE) &&
|
|
(le32_to_cpu(dresp->mnt[0].state) != FSCS_HIDDEN)) {
|
|
fsa_dev_ptr->valid = 1;
|
|
/* sense_key holds the current state of the spin-up */
|
|
if (dresp->mnt[0].state & cpu_to_le32(FSCS_NOT_READY))
|
|
fsa_dev_ptr->sense_data.sense_key = NOT_READY;
|
|
else if (fsa_dev_ptr->sense_data.sense_key == NOT_READY)
|
|
fsa_dev_ptr->sense_data.sense_key = NO_SENSE;
|
|
fsa_dev_ptr->type = le32_to_cpu(dresp->mnt[0].vol);
|
|
fsa_dev_ptr->size
|
|
= ((u64)le32_to_cpu(dresp->mnt[0].capacity)) +
|
|
(((u64)le32_to_cpu(dresp->mnt[0].capacityhigh)) << 32);
|
|
fsa_dev_ptr->ro = ((le32_to_cpu(dresp->mnt[0].state) & FSCS_READONLY) != 0);
|
|
}
|
|
if ((fsa_dev_ptr->valid & 1) == 0)
|
|
fsa_dev_ptr->valid = 0;
|
|
scsicmd->SCp.Status = le32_to_cpu(dresp->count);
|
|
}
|
|
aac_fib_complete(fibptr);
|
|
aac_fib_free(fibptr);
|
|
callback = (int (*)(struct scsi_cmnd *))(scsicmd->SCp.ptr);
|
|
scsicmd->SCp.ptr = NULL;
|
|
(*callback)(scsicmd);
|
|
return;
|
|
}
|
|
|
|
static void _aac_probe_container1(void * context, struct fib * fibptr)
|
|
{
|
|
struct scsi_cmnd * scsicmd;
|
|
struct aac_mount * dresp;
|
|
struct aac_query_mount *dinfo;
|
|
int status;
|
|
|
|
dresp = (struct aac_mount *) fib_data(fibptr);
|
|
dresp->mnt[0].capacityhigh = 0;
|
|
if ((le32_to_cpu(dresp->status) != ST_OK) ||
|
|
(le32_to_cpu(dresp->mnt[0].vol) != CT_NONE)) {
|
|
_aac_probe_container2(context, fibptr);
|
|
return;
|
|
}
|
|
scsicmd = (struct scsi_cmnd *) context;
|
|
|
|
if (!aac_valid_context(scsicmd, fibptr))
|
|
return;
|
|
|
|
aac_fib_init(fibptr);
|
|
|
|
dinfo = (struct aac_query_mount *)fib_data(fibptr);
|
|
|
|
dinfo->command = cpu_to_le32(VM_NameServe64);
|
|
dinfo->count = cpu_to_le32(scmd_id(scsicmd));
|
|
dinfo->type = cpu_to_le32(FT_FILESYS);
|
|
|
|
status = aac_fib_send(ContainerCommand,
|
|
fibptr,
|
|
sizeof(struct aac_query_mount),
|
|
FsaNormal,
|
|
0, 1,
|
|
_aac_probe_container2,
|
|
(void *) scsicmd);
|
|
/*
|
|
* Check that the command queued to the controller
|
|
*/
|
|
if (status == -EINPROGRESS)
|
|
scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
|
|
else if (status < 0) {
|
|
/* Inherit results from VM_NameServe, if any */
|
|
dresp->status = cpu_to_le32(ST_OK);
|
|
_aac_probe_container2(context, fibptr);
|
|
}
|
|
}
|
|
|
|
static int _aac_probe_container(struct scsi_cmnd * scsicmd, int (*callback)(struct scsi_cmnd *))
|
|
{
|
|
struct fib * fibptr;
|
|
int status = -ENOMEM;
|
|
|
|
if ((fibptr = aac_fib_alloc((struct aac_dev *)scsicmd->device->host->hostdata))) {
|
|
struct aac_query_mount *dinfo;
|
|
|
|
aac_fib_init(fibptr);
|
|
|
|
dinfo = (struct aac_query_mount *)fib_data(fibptr);
|
|
|
|
dinfo->command = cpu_to_le32(VM_NameServe);
|
|
dinfo->count = cpu_to_le32(scmd_id(scsicmd));
|
|
dinfo->type = cpu_to_le32(FT_FILESYS);
|
|
scsicmd->SCp.ptr = (char *)callback;
|
|
|
|
status = aac_fib_send(ContainerCommand,
|
|
fibptr,
|
|
sizeof(struct aac_query_mount),
|
|
FsaNormal,
|
|
0, 1,
|
|
_aac_probe_container1,
|
|
(void *) scsicmd);
|
|
/*
|
|
* Check that the command queued to the controller
|
|
*/
|
|
if (status == -EINPROGRESS) {
|
|
scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
|
|
return 0;
|
|
}
|
|
if (status < 0) {
|
|
scsicmd->SCp.ptr = NULL;
|
|
aac_fib_complete(fibptr);
|
|
aac_fib_free(fibptr);
|
|
}
|
|
}
|
|
if (status < 0) {
|
|
struct fsa_dev_info *fsa_dev_ptr = ((struct aac_dev *)(scsicmd->device->host->hostdata))->fsa_dev;
|
|
if (fsa_dev_ptr) {
|
|
fsa_dev_ptr += scmd_id(scsicmd);
|
|
if ((fsa_dev_ptr->valid & 1) == 0) {
|
|
fsa_dev_ptr->valid = 0;
|
|
return (*callback)(scsicmd);
|
|
}
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* aac_probe_container - query a logical volume
|
|
* @dev: device to query
|
|
* @cid: container identifier
|
|
*
|
|
* Queries the controller about the given volume. The volume information
|
|
* is updated in the struct fsa_dev_info structure rather than returned.
|
|
*/
|
|
static int aac_probe_container_callback1(struct scsi_cmnd * scsicmd)
|
|
{
|
|
scsicmd->device = NULL;
|
|
return 0;
|
|
}
|
|
|
|
int aac_probe_container(struct aac_dev *dev, int cid)
|
|
{
|
|
struct scsi_cmnd *scsicmd = kmalloc(sizeof(*scsicmd), GFP_KERNEL);
|
|
struct scsi_device *scsidev = kmalloc(sizeof(*scsidev), GFP_KERNEL);
|
|
int status;
|
|
|
|
if (!scsicmd || !scsidev) {
|
|
kfree(scsicmd);
|
|
kfree(scsidev);
|
|
return -ENOMEM;
|
|
}
|
|
scsicmd->list.next = NULL;
|
|
scsicmd->scsi_done = (void (*)(struct scsi_cmnd*))aac_probe_container_callback1;
|
|
|
|
scsicmd->device = scsidev;
|
|
scsidev->sdev_state = 0;
|
|
scsidev->id = cid;
|
|
scsidev->host = dev->scsi_host_ptr;
|
|
|
|
if (_aac_probe_container(scsicmd, aac_probe_container_callback1) == 0)
|
|
while (scsicmd->device == scsidev)
|
|
schedule();
|
|
kfree(scsidev);
|
|
status = scsicmd->SCp.Status;
|
|
kfree(scsicmd);
|
|
return status;
|
|
}
|
|
|
|
/* Local Structure to set SCSI inquiry data strings */
|
|
struct scsi_inq {
|
|
char vid[8]; /* Vendor ID */
|
|
char pid[16]; /* Product ID */
|
|
char prl[4]; /* Product Revision Level */
|
|
};
|
|
|
|
/**
|
|
* InqStrCopy - string merge
|
|
* @a: string to copy from
|
|
* @b: string to copy to
|
|
*
|
|
* Copy a String from one location to another
|
|
* without copying \0
|
|
*/
|
|
|
|
static void inqstrcpy(char *a, char *b)
|
|
{
|
|
|
|
while (*a != (char)0)
|
|
*b++ = *a++;
|
|
}
|
|
|
|
static char *container_types[] = {
|
|
"None",
|
|
"Volume",
|
|
"Mirror",
|
|
"Stripe",
|
|
"RAID5",
|
|
"SSRW",
|
|
"SSRO",
|
|
"Morph",
|
|
"Legacy",
|
|
"RAID4",
|
|
"RAID10",
|
|
"RAID00",
|
|
"V-MIRRORS",
|
|
"PSEUDO R4",
|
|
"RAID50",
|
|
"RAID5D",
|
|
"RAID5D0",
|
|
"RAID1E",
|
|
"RAID6",
|
|
"RAID60",
|
|
"Unknown"
|
|
};
|
|
|
|
char * get_container_type(unsigned tindex)
|
|
{
|
|
if (tindex >= ARRAY_SIZE(container_types))
|
|
tindex = ARRAY_SIZE(container_types) - 1;
|
|
return container_types[tindex];
|
|
}
|
|
|
|
/* Function: setinqstr
|
|
*
|
|
* Arguments: [1] pointer to void [1] int
|
|
*
|
|
* Purpose: Sets SCSI inquiry data strings for vendor, product
|
|
* and revision level. Allows strings to be set in platform dependant
|
|
* files instead of in OS dependant driver source.
|
|
*/
|
|
|
|
static void setinqstr(struct aac_dev *dev, void *data, int tindex)
|
|
{
|
|
struct scsi_inq *str;
|
|
|
|
str = (struct scsi_inq *)(data); /* cast data to scsi inq block */
|
|
memset(str, ' ', sizeof(*str));
|
|
|
|
if (dev->supplement_adapter_info.AdapterTypeText[0]) {
|
|
char * cp = dev->supplement_adapter_info.AdapterTypeText;
|
|
int c;
|
|
if ((cp[0] == 'A') && (cp[1] == 'O') && (cp[2] == 'C'))
|
|
inqstrcpy("SMC", str->vid);
|
|
else {
|
|
c = sizeof(str->vid);
|
|
while (*cp && *cp != ' ' && --c)
|
|
++cp;
|
|
c = *cp;
|
|
*cp = '\0';
|
|
inqstrcpy (dev->supplement_adapter_info.AdapterTypeText,
|
|
str->vid);
|
|
*cp = c;
|
|
while (*cp && *cp != ' ')
|
|
++cp;
|
|
}
|
|
while (*cp == ' ')
|
|
++cp;
|
|
/* last six chars reserved for vol type */
|
|
c = 0;
|
|
if (strlen(cp) > sizeof(str->pid)) {
|
|
c = cp[sizeof(str->pid)];
|
|
cp[sizeof(str->pid)] = '\0';
|
|
}
|
|
inqstrcpy (cp, str->pid);
|
|
if (c)
|
|
cp[sizeof(str->pid)] = c;
|
|
} else {
|
|
struct aac_driver_ident *mp = aac_get_driver_ident(dev->cardtype);
|
|
|
|
inqstrcpy (mp->vname, str->vid);
|
|
/* last six chars reserved for vol type */
|
|
inqstrcpy (mp->model, str->pid);
|
|
}
|
|
|
|
if (tindex < ARRAY_SIZE(container_types)){
|
|
char *findit = str->pid;
|
|
|
|
for ( ; *findit != ' '; findit++); /* walk till we find a space */
|
|
/* RAID is superfluous in the context of a RAID device */
|
|
if (memcmp(findit-4, "RAID", 4) == 0)
|
|
*(findit -= 4) = ' ';
|
|
if (((findit - str->pid) + strlen(container_types[tindex]))
|
|
< (sizeof(str->pid) + sizeof(str->prl)))
|
|
inqstrcpy (container_types[tindex], findit + 1);
|
|
}
|
|
inqstrcpy ("V1.0", str->prl);
|
|
}
|
|
|
|
static void get_container_serial_callback(void *context, struct fib * fibptr)
|
|
{
|
|
struct aac_get_serial_resp * get_serial_reply;
|
|
struct scsi_cmnd * scsicmd;
|
|
|
|
BUG_ON(fibptr == NULL);
|
|
|
|
scsicmd = (struct scsi_cmnd *) context;
|
|
if (!aac_valid_context(scsicmd, fibptr))
|
|
return;
|
|
|
|
get_serial_reply = (struct aac_get_serial_resp *) fib_data(fibptr);
|
|
/* Failure is irrelevant, using default value instead */
|
|
if (le32_to_cpu(get_serial_reply->status) == CT_OK) {
|
|
char sp[13];
|
|
/* EVPD bit set */
|
|
sp[0] = INQD_PDT_DA;
|
|
sp[1] = scsicmd->cmnd[2];
|
|
sp[2] = 0;
|
|
sp[3] = snprintf(sp+4, sizeof(sp)-4, "%08X",
|
|
le32_to_cpu(get_serial_reply->uid));
|
|
scsi_sg_copy_from_buffer(scsicmd, sp, sizeof(sp));
|
|
}
|
|
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
|
|
|
|
aac_fib_complete(fibptr);
|
|
aac_fib_free(fibptr);
|
|
scsicmd->scsi_done(scsicmd);
|
|
}
|
|
|
|
/**
|
|
* aac_get_container_serial - get container serial, none blocking.
|
|
*/
|
|
static int aac_get_container_serial(struct scsi_cmnd * scsicmd)
|
|
{
|
|
int status;
|
|
struct aac_get_serial *dinfo;
|
|
struct fib * cmd_fibcontext;
|
|
struct aac_dev * dev;
|
|
|
|
dev = (struct aac_dev *)scsicmd->device->host->hostdata;
|
|
|
|
if (!(cmd_fibcontext = aac_fib_alloc(dev)))
|
|
return -ENOMEM;
|
|
|
|
aac_fib_init(cmd_fibcontext);
|
|
dinfo = (struct aac_get_serial *) fib_data(cmd_fibcontext);
|
|
|
|
dinfo->command = cpu_to_le32(VM_ContainerConfig);
|
|
dinfo->type = cpu_to_le32(CT_CID_TO_32BITS_UID);
|
|
dinfo->cid = cpu_to_le32(scmd_id(scsicmd));
|
|
|
|
status = aac_fib_send(ContainerCommand,
|
|
cmd_fibcontext,
|
|
sizeof (struct aac_get_serial),
|
|
FsaNormal,
|
|
0, 1,
|
|
(fib_callback) get_container_serial_callback,
|
|
(void *) scsicmd);
|
|
|
|
/*
|
|
* Check that the command queued to the controller
|
|
*/
|
|
if (status == -EINPROGRESS) {
|
|
scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
|
|
return 0;
|
|
}
|
|
|
|
printk(KERN_WARNING "aac_get_container_serial: aac_fib_send failed with status: %d.\n", status);
|
|
aac_fib_complete(cmd_fibcontext);
|
|
aac_fib_free(cmd_fibcontext);
|
|
return -1;
|
|
}
|
|
|
|
/* Function: setinqserial
|
|
*
|
|
* Arguments: [1] pointer to void [1] int
|
|
*
|
|
* Purpose: Sets SCSI Unit Serial number.
|
|
* This is a fake. We should read a proper
|
|
* serial number from the container. <SuSE>But
|
|
* without docs it's quite hard to do it :-)
|
|
* So this will have to do in the meantime.</SuSE>
|
|
*/
|
|
|
|
static int setinqserial(struct aac_dev *dev, void *data, int cid)
|
|
{
|
|
/*
|
|
* This breaks array migration.
|
|
*/
|
|
return snprintf((char *)(data), sizeof(struct scsi_inq) - 4, "%08X%02X",
|
|
le32_to_cpu(dev->adapter_info.serial[0]), cid);
|
|
}
|
|
|
|
static inline void set_sense(struct sense_data *sense_data, u8 sense_key,
|
|
u8 sense_code, u8 a_sense_code, u8 bit_pointer, u16 field_pointer)
|
|
{
|
|
u8 *sense_buf = (u8 *)sense_data;
|
|
/* Sense data valid, err code 70h */
|
|
sense_buf[0] = 0x70; /* No info field */
|
|
sense_buf[1] = 0; /* Segment number, always zero */
|
|
|
|
sense_buf[2] = sense_key; /* Sense key */
|
|
|
|
sense_buf[12] = sense_code; /* Additional sense code */
|
|
sense_buf[13] = a_sense_code; /* Additional sense code qualifier */
|
|
|
|
if (sense_key == ILLEGAL_REQUEST) {
|
|
sense_buf[7] = 10; /* Additional sense length */
|
|
|
|
sense_buf[15] = bit_pointer;
|
|
/* Illegal parameter is in the parameter block */
|
|
if (sense_code == SENCODE_INVALID_CDB_FIELD)
|
|
sense_buf[15] |= 0xc0;/* Std sense key specific field */
|
|
/* Illegal parameter is in the CDB block */
|
|
sense_buf[16] = field_pointer >> 8; /* MSB */
|
|
sense_buf[17] = field_pointer; /* LSB */
|
|
} else
|
|
sense_buf[7] = 6; /* Additional sense length */
|
|
}
|
|
|
|
static int aac_bounds_32(struct aac_dev * dev, struct scsi_cmnd * cmd, u64 lba)
|
|
{
|
|
if (lba & 0xffffffff00000000LL) {
|
|
int cid = scmd_id(cmd);
|
|
dprintk((KERN_DEBUG "aacraid: Illegal lba\n"));
|
|
cmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
|
|
SAM_STAT_CHECK_CONDITION;
|
|
set_sense(&dev->fsa_dev[cid].sense_data,
|
|
HARDWARE_ERROR, SENCODE_INTERNAL_TARGET_FAILURE,
|
|
ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0);
|
|
memcpy(cmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
|
|
min_t(size_t, sizeof(dev->fsa_dev[cid].sense_data),
|
|
SCSI_SENSE_BUFFERSIZE));
|
|
cmd->scsi_done(cmd);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int aac_bounds_64(struct aac_dev * dev, struct scsi_cmnd * cmd, u64 lba)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void io_callback(void *context, struct fib * fibptr);
|
|
|
|
static int aac_read_raw_io(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u32 count)
|
|
{
|
|
u16 fibsize;
|
|
struct aac_raw_io *readcmd;
|
|
aac_fib_init(fib);
|
|
readcmd = (struct aac_raw_io *) fib_data(fib);
|
|
readcmd->block[0] = cpu_to_le32((u32)(lba&0xffffffff));
|
|
readcmd->block[1] = cpu_to_le32((u32)((lba&0xffffffff00000000LL)>>32));
|
|
readcmd->count = cpu_to_le32(count<<9);
|
|
readcmd->cid = cpu_to_le16(scmd_id(cmd));
|
|
readcmd->flags = cpu_to_le16(IO_TYPE_READ);
|
|
readcmd->bpTotal = 0;
|
|
readcmd->bpComplete = 0;
|
|
|
|
aac_build_sgraw(cmd, &readcmd->sg);
|
|
fibsize = sizeof(struct aac_raw_io) + ((le32_to_cpu(readcmd->sg.count) - 1) * sizeof (struct sgentryraw));
|
|
BUG_ON(fibsize > (fib->dev->max_fib_size - sizeof(struct aac_fibhdr)));
|
|
/*
|
|
* Now send the Fib to the adapter
|
|
*/
|
|
return aac_fib_send(ContainerRawIo,
|
|
fib,
|
|
fibsize,
|
|
FsaNormal,
|
|
0, 1,
|
|
(fib_callback) io_callback,
|
|
(void *) cmd);
|
|
}
|
|
|
|
static int aac_read_block64(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u32 count)
|
|
{
|
|
u16 fibsize;
|
|
struct aac_read64 *readcmd;
|
|
aac_fib_init(fib);
|
|
readcmd = (struct aac_read64 *) fib_data(fib);
|
|
readcmd->command = cpu_to_le32(VM_CtHostRead64);
|
|
readcmd->cid = cpu_to_le16(scmd_id(cmd));
|
|
readcmd->sector_count = cpu_to_le16(count);
|
|
readcmd->block = cpu_to_le32((u32)(lba&0xffffffff));
|
|
readcmd->pad = 0;
|
|
readcmd->flags = 0;
|
|
|
|
aac_build_sg64(cmd, &readcmd->sg);
|
|
fibsize = sizeof(struct aac_read64) +
|
|
((le32_to_cpu(readcmd->sg.count) - 1) *
|
|
sizeof (struct sgentry64));
|
|
BUG_ON (fibsize > (fib->dev->max_fib_size -
|
|
sizeof(struct aac_fibhdr)));
|
|
/*
|
|
* Now send the Fib to the adapter
|
|
*/
|
|
return aac_fib_send(ContainerCommand64,
|
|
fib,
|
|
fibsize,
|
|
FsaNormal,
|
|
0, 1,
|
|
(fib_callback) io_callback,
|
|
(void *) cmd);
|
|
}
|
|
|
|
static int aac_read_block(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u32 count)
|
|
{
|
|
u16 fibsize;
|
|
struct aac_read *readcmd;
|
|
aac_fib_init(fib);
|
|
readcmd = (struct aac_read *) fib_data(fib);
|
|
readcmd->command = cpu_to_le32(VM_CtBlockRead);
|
|
readcmd->cid = cpu_to_le32(scmd_id(cmd));
|
|
readcmd->block = cpu_to_le32((u32)(lba&0xffffffff));
|
|
readcmd->count = cpu_to_le32(count * 512);
|
|
|
|
aac_build_sg(cmd, &readcmd->sg);
|
|
fibsize = sizeof(struct aac_read) +
|
|
((le32_to_cpu(readcmd->sg.count) - 1) *
|
|
sizeof (struct sgentry));
|
|
BUG_ON (fibsize > (fib->dev->max_fib_size -
|
|
sizeof(struct aac_fibhdr)));
|
|
/*
|
|
* Now send the Fib to the adapter
|
|
*/
|
|
return aac_fib_send(ContainerCommand,
|
|
fib,
|
|
fibsize,
|
|
FsaNormal,
|
|
0, 1,
|
|
(fib_callback) io_callback,
|
|
(void *) cmd);
|
|
}
|
|
|
|
static int aac_write_raw_io(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u32 count, int fua)
|
|
{
|
|
u16 fibsize;
|
|
struct aac_raw_io *writecmd;
|
|
aac_fib_init(fib);
|
|
writecmd = (struct aac_raw_io *) fib_data(fib);
|
|
writecmd->block[0] = cpu_to_le32((u32)(lba&0xffffffff));
|
|
writecmd->block[1] = cpu_to_le32((u32)((lba&0xffffffff00000000LL)>>32));
|
|
writecmd->count = cpu_to_le32(count<<9);
|
|
writecmd->cid = cpu_to_le16(scmd_id(cmd));
|
|
writecmd->flags = (fua && ((aac_cache & 5) != 1) &&
|
|
(((aac_cache & 5) != 5) || !fib->dev->cache_protected)) ?
|
|
cpu_to_le16(IO_TYPE_WRITE|IO_SUREWRITE) :
|
|
cpu_to_le16(IO_TYPE_WRITE);
|
|
writecmd->bpTotal = 0;
|
|
writecmd->bpComplete = 0;
|
|
|
|
aac_build_sgraw(cmd, &writecmd->sg);
|
|
fibsize = sizeof(struct aac_raw_io) + ((le32_to_cpu(writecmd->sg.count) - 1) * sizeof (struct sgentryraw));
|
|
BUG_ON(fibsize > (fib->dev->max_fib_size - sizeof(struct aac_fibhdr)));
|
|
/*
|
|
* Now send the Fib to the adapter
|
|
*/
|
|
return aac_fib_send(ContainerRawIo,
|
|
fib,
|
|
fibsize,
|
|
FsaNormal,
|
|
0, 1,
|
|
(fib_callback) io_callback,
|
|
(void *) cmd);
|
|
}
|
|
|
|
static int aac_write_block64(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u32 count, int fua)
|
|
{
|
|
u16 fibsize;
|
|
struct aac_write64 *writecmd;
|
|
aac_fib_init(fib);
|
|
writecmd = (struct aac_write64 *) fib_data(fib);
|
|
writecmd->command = cpu_to_le32(VM_CtHostWrite64);
|
|
writecmd->cid = cpu_to_le16(scmd_id(cmd));
|
|
writecmd->sector_count = cpu_to_le16(count);
|
|
writecmd->block = cpu_to_le32((u32)(lba&0xffffffff));
|
|
writecmd->pad = 0;
|
|
writecmd->flags = 0;
|
|
|
|
aac_build_sg64(cmd, &writecmd->sg);
|
|
fibsize = sizeof(struct aac_write64) +
|
|
((le32_to_cpu(writecmd->sg.count) - 1) *
|
|
sizeof (struct sgentry64));
|
|
BUG_ON (fibsize > (fib->dev->max_fib_size -
|
|
sizeof(struct aac_fibhdr)));
|
|
/*
|
|
* Now send the Fib to the adapter
|
|
*/
|
|
return aac_fib_send(ContainerCommand64,
|
|
fib,
|
|
fibsize,
|
|
FsaNormal,
|
|
0, 1,
|
|
(fib_callback) io_callback,
|
|
(void *) cmd);
|
|
}
|
|
|
|
static int aac_write_block(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u32 count, int fua)
|
|
{
|
|
u16 fibsize;
|
|
struct aac_write *writecmd;
|
|
aac_fib_init(fib);
|
|
writecmd = (struct aac_write *) fib_data(fib);
|
|
writecmd->command = cpu_to_le32(VM_CtBlockWrite);
|
|
writecmd->cid = cpu_to_le32(scmd_id(cmd));
|
|
writecmd->block = cpu_to_le32((u32)(lba&0xffffffff));
|
|
writecmd->count = cpu_to_le32(count * 512);
|
|
writecmd->sg.count = cpu_to_le32(1);
|
|
/* ->stable is not used - it did mean which type of write */
|
|
|
|
aac_build_sg(cmd, &writecmd->sg);
|
|
fibsize = sizeof(struct aac_write) +
|
|
((le32_to_cpu(writecmd->sg.count) - 1) *
|
|
sizeof (struct sgentry));
|
|
BUG_ON (fibsize > (fib->dev->max_fib_size -
|
|
sizeof(struct aac_fibhdr)));
|
|
/*
|
|
* Now send the Fib to the adapter
|
|
*/
|
|
return aac_fib_send(ContainerCommand,
|
|
fib,
|
|
fibsize,
|
|
FsaNormal,
|
|
0, 1,
|
|
(fib_callback) io_callback,
|
|
(void *) cmd);
|
|
}
|
|
|
|
static struct aac_srb * aac_scsi_common(struct fib * fib, struct scsi_cmnd * cmd)
|
|
{
|
|
struct aac_srb * srbcmd;
|
|
u32 flag;
|
|
u32 timeout;
|
|
|
|
aac_fib_init(fib);
|
|
switch(cmd->sc_data_direction){
|
|
case DMA_TO_DEVICE:
|
|
flag = SRB_DataOut;
|
|
break;
|
|
case DMA_BIDIRECTIONAL:
|
|
flag = SRB_DataIn | SRB_DataOut;
|
|
break;
|
|
case DMA_FROM_DEVICE:
|
|
flag = SRB_DataIn;
|
|
break;
|
|
case DMA_NONE:
|
|
default: /* shuts up some versions of gcc */
|
|
flag = SRB_NoDataXfer;
|
|
break;
|
|
}
|
|
|
|
srbcmd = (struct aac_srb*) fib_data(fib);
|
|
srbcmd->function = cpu_to_le32(SRBF_ExecuteScsi);
|
|
srbcmd->channel = cpu_to_le32(aac_logical_to_phys(scmd_channel(cmd)));
|
|
srbcmd->id = cpu_to_le32(scmd_id(cmd));
|
|
srbcmd->lun = cpu_to_le32(cmd->device->lun);
|
|
srbcmd->flags = cpu_to_le32(flag);
|
|
timeout = cmd->request->timeout/HZ;
|
|
if (timeout == 0)
|
|
timeout = 1;
|
|
srbcmd->timeout = cpu_to_le32(timeout); // timeout in seconds
|
|
srbcmd->retry_limit = 0; /* Obsolete parameter */
|
|
srbcmd->cdb_size = cpu_to_le32(cmd->cmd_len);
|
|
return srbcmd;
|
|
}
|
|
|
|
static void aac_srb_callback(void *context, struct fib * fibptr);
|
|
|
|
static int aac_scsi_64(struct fib * fib, struct scsi_cmnd * cmd)
|
|
{
|
|
u16 fibsize;
|
|
struct aac_srb * srbcmd = aac_scsi_common(fib, cmd);
|
|
|
|
aac_build_sg64(cmd, (struct sgmap64*) &srbcmd->sg);
|
|
srbcmd->count = cpu_to_le32(scsi_bufflen(cmd));
|
|
|
|
memset(srbcmd->cdb, 0, sizeof(srbcmd->cdb));
|
|
memcpy(srbcmd->cdb, cmd->cmnd, cmd->cmd_len);
|
|
/*
|
|
* Build Scatter/Gather list
|
|
*/
|
|
fibsize = sizeof (struct aac_srb) - sizeof (struct sgentry) +
|
|
((le32_to_cpu(srbcmd->sg.count) & 0xff) *
|
|
sizeof (struct sgentry64));
|
|
BUG_ON (fibsize > (fib->dev->max_fib_size -
|
|
sizeof(struct aac_fibhdr)));
|
|
|
|
/*
|
|
* Now send the Fib to the adapter
|
|
*/
|
|
return aac_fib_send(ScsiPortCommand64, fib,
|
|
fibsize, FsaNormal, 0, 1,
|
|
(fib_callback) aac_srb_callback,
|
|
(void *) cmd);
|
|
}
|
|
|
|
static int aac_scsi_32(struct fib * fib, struct scsi_cmnd * cmd)
|
|
{
|
|
u16 fibsize;
|
|
struct aac_srb * srbcmd = aac_scsi_common(fib, cmd);
|
|
|
|
aac_build_sg(cmd, (struct sgmap*)&srbcmd->sg);
|
|
srbcmd->count = cpu_to_le32(scsi_bufflen(cmd));
|
|
|
|
memset(srbcmd->cdb, 0, sizeof(srbcmd->cdb));
|
|
memcpy(srbcmd->cdb, cmd->cmnd, cmd->cmd_len);
|
|
/*
|
|
* Build Scatter/Gather list
|
|
*/
|
|
fibsize = sizeof (struct aac_srb) +
|
|
(((le32_to_cpu(srbcmd->sg.count) & 0xff) - 1) *
|
|
sizeof (struct sgentry));
|
|
BUG_ON (fibsize > (fib->dev->max_fib_size -
|
|
sizeof(struct aac_fibhdr)));
|
|
|
|
/*
|
|
* Now send the Fib to the adapter
|
|
*/
|
|
return aac_fib_send(ScsiPortCommand, fib, fibsize, FsaNormal, 0, 1,
|
|
(fib_callback) aac_srb_callback, (void *) cmd);
|
|
}
|
|
|
|
static int aac_scsi_32_64(struct fib * fib, struct scsi_cmnd * cmd)
|
|
{
|
|
if ((sizeof(dma_addr_t) > 4) && fib->dev->needs_dac &&
|
|
(fib->dev->adapter_info.options & AAC_OPT_SGMAP_HOST64))
|
|
return FAILED;
|
|
return aac_scsi_32(fib, cmd);
|
|
}
|
|
|
|
int aac_get_adapter_info(struct aac_dev* dev)
|
|
{
|
|
struct fib* fibptr;
|
|
int rcode;
|
|
u32 tmp;
|
|
struct aac_adapter_info *info;
|
|
struct aac_bus_info *command;
|
|
struct aac_bus_info_response *bus_info;
|
|
|
|
if (!(fibptr = aac_fib_alloc(dev)))
|
|
return -ENOMEM;
|
|
|
|
aac_fib_init(fibptr);
|
|
info = (struct aac_adapter_info *) fib_data(fibptr);
|
|
memset(info,0,sizeof(*info));
|
|
|
|
rcode = aac_fib_send(RequestAdapterInfo,
|
|
fibptr,
|
|
sizeof(*info),
|
|
FsaNormal,
|
|
-1, 1, /* First `interrupt' command uses special wait */
|
|
NULL,
|
|
NULL);
|
|
|
|
if (rcode < 0) {
|
|
/* FIB should be freed only after
|
|
* getting the response from the F/W */
|
|
if (rcode != -ERESTARTSYS) {
|
|
aac_fib_complete(fibptr);
|
|
aac_fib_free(fibptr);
|
|
}
|
|
return rcode;
|
|
}
|
|
memcpy(&dev->adapter_info, info, sizeof(*info));
|
|
|
|
if (dev->adapter_info.options & AAC_OPT_SUPPLEMENT_ADAPTER_INFO) {
|
|
struct aac_supplement_adapter_info * sinfo;
|
|
|
|
aac_fib_init(fibptr);
|
|
|
|
sinfo = (struct aac_supplement_adapter_info *) fib_data(fibptr);
|
|
|
|
memset(sinfo,0,sizeof(*sinfo));
|
|
|
|
rcode = aac_fib_send(RequestSupplementAdapterInfo,
|
|
fibptr,
|
|
sizeof(*sinfo),
|
|
FsaNormal,
|
|
1, 1,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (rcode >= 0)
|
|
memcpy(&dev->supplement_adapter_info, sinfo, sizeof(*sinfo));
|
|
if (rcode == -ERESTARTSYS) {
|
|
fibptr = aac_fib_alloc(dev);
|
|
if (!fibptr)
|
|
return -ENOMEM;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* GetBusInfo
|
|
*/
|
|
|
|
aac_fib_init(fibptr);
|
|
|
|
bus_info = (struct aac_bus_info_response *) fib_data(fibptr);
|
|
|
|
memset(bus_info, 0, sizeof(*bus_info));
|
|
|
|
command = (struct aac_bus_info *)bus_info;
|
|
|
|
command->Command = cpu_to_le32(VM_Ioctl);
|
|
command->ObjType = cpu_to_le32(FT_DRIVE);
|
|
command->MethodId = cpu_to_le32(1);
|
|
command->CtlCmd = cpu_to_le32(GetBusInfo);
|
|
|
|
rcode = aac_fib_send(ContainerCommand,
|
|
fibptr,
|
|
sizeof (*bus_info),
|
|
FsaNormal,
|
|
1, 1,
|
|
NULL, NULL);
|
|
|
|
/* reasoned default */
|
|
dev->maximum_num_physicals = 16;
|
|
if (rcode >= 0 && le32_to_cpu(bus_info->Status) == ST_OK) {
|
|
dev->maximum_num_physicals = le32_to_cpu(bus_info->TargetsPerBus);
|
|
dev->maximum_num_channels = le32_to_cpu(bus_info->BusCount);
|
|
}
|
|
|
|
if (!dev->in_reset) {
|
|
char buffer[16];
|
|
tmp = le32_to_cpu(dev->adapter_info.kernelrev);
|
|
printk(KERN_INFO "%s%d: kernel %d.%d-%d[%d] %.*s\n",
|
|
dev->name,
|
|
dev->id,
|
|
tmp>>24,
|
|
(tmp>>16)&0xff,
|
|
tmp&0xff,
|
|
le32_to_cpu(dev->adapter_info.kernelbuild),
|
|
(int)sizeof(dev->supplement_adapter_info.BuildDate),
|
|
dev->supplement_adapter_info.BuildDate);
|
|
tmp = le32_to_cpu(dev->adapter_info.monitorrev);
|
|
printk(KERN_INFO "%s%d: monitor %d.%d-%d[%d]\n",
|
|
dev->name, dev->id,
|
|
tmp>>24,(tmp>>16)&0xff,tmp&0xff,
|
|
le32_to_cpu(dev->adapter_info.monitorbuild));
|
|
tmp = le32_to_cpu(dev->adapter_info.biosrev);
|
|
printk(KERN_INFO "%s%d: bios %d.%d-%d[%d]\n",
|
|
dev->name, dev->id,
|
|
tmp>>24,(tmp>>16)&0xff,tmp&0xff,
|
|
le32_to_cpu(dev->adapter_info.biosbuild));
|
|
buffer[0] = '\0';
|
|
if (aac_get_serial_number(
|
|
shost_to_class(dev->scsi_host_ptr), buffer))
|
|
printk(KERN_INFO "%s%d: serial %s",
|
|
dev->name, dev->id, buffer);
|
|
if (dev->supplement_adapter_info.VpdInfo.Tsid[0]) {
|
|
printk(KERN_INFO "%s%d: TSID %.*s\n",
|
|
dev->name, dev->id,
|
|
(int)sizeof(dev->supplement_adapter_info.VpdInfo.Tsid),
|
|
dev->supplement_adapter_info.VpdInfo.Tsid);
|
|
}
|
|
if (!aac_check_reset || ((aac_check_reset == 1) &&
|
|
(dev->supplement_adapter_info.SupportedOptions2 &
|
|
AAC_OPTION_IGNORE_RESET))) {
|
|
printk(KERN_INFO "%s%d: Reset Adapter Ignored\n",
|
|
dev->name, dev->id);
|
|
}
|
|
}
|
|
|
|
dev->cache_protected = 0;
|
|
dev->jbod = ((dev->supplement_adapter_info.FeatureBits &
|
|
AAC_FEATURE_JBOD) != 0);
|
|
dev->nondasd_support = 0;
|
|
dev->raid_scsi_mode = 0;
|
|
if(dev->adapter_info.options & AAC_OPT_NONDASD)
|
|
dev->nondasd_support = 1;
|
|
|
|
/*
|
|
* If the firmware supports ROMB RAID/SCSI mode and we are currently
|
|
* in RAID/SCSI mode, set the flag. For now if in this mode we will
|
|
* force nondasd support on. If we decide to allow the non-dasd flag
|
|
* additional changes changes will have to be made to support
|
|
* RAID/SCSI. the function aac_scsi_cmd in this module will have to be
|
|
* changed to support the new dev->raid_scsi_mode flag instead of
|
|
* leaching off of the dev->nondasd_support flag. Also in linit.c the
|
|
* function aac_detect will have to be modified where it sets up the
|
|
* max number of channels based on the aac->nondasd_support flag only.
|
|
*/
|
|
if ((dev->adapter_info.options & AAC_OPT_SCSI_MANAGED) &&
|
|
(dev->adapter_info.options & AAC_OPT_RAID_SCSI_MODE)) {
|
|
dev->nondasd_support = 1;
|
|
dev->raid_scsi_mode = 1;
|
|
}
|
|
if (dev->raid_scsi_mode != 0)
|
|
printk(KERN_INFO "%s%d: ROMB RAID/SCSI mode enabled\n",
|
|
dev->name, dev->id);
|
|
|
|
if (nondasd != -1)
|
|
dev->nondasd_support = (nondasd!=0);
|
|
if (dev->nondasd_support && !dev->in_reset)
|
|
printk(KERN_INFO "%s%d: Non-DASD support enabled.\n",dev->name, dev->id);
|
|
|
|
if (dma_get_required_mask(&dev->pdev->dev) > DMA_BIT_MASK(32))
|
|
dev->needs_dac = 1;
|
|
dev->dac_support = 0;
|
|
if ((sizeof(dma_addr_t) > 4) && dev->needs_dac &&
|
|
(dev->adapter_info.options & AAC_OPT_SGMAP_HOST64)) {
|
|
if (!dev->in_reset)
|
|
printk(KERN_INFO "%s%d: 64bit support enabled.\n",
|
|
dev->name, dev->id);
|
|
dev->dac_support = 1;
|
|
}
|
|
|
|
if(dacmode != -1) {
|
|
dev->dac_support = (dacmode!=0);
|
|
}
|
|
|
|
/* avoid problems with AAC_QUIRK_SCSI_32 controllers */
|
|
if (dev->dac_support && (aac_get_driver_ident(dev->cardtype)->quirks
|
|
& AAC_QUIRK_SCSI_32)) {
|
|
dev->nondasd_support = 0;
|
|
dev->jbod = 0;
|
|
expose_physicals = 0;
|
|
}
|
|
|
|
if(dev->dac_support != 0) {
|
|
if (!pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(64)) &&
|
|
!pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(64))) {
|
|
if (!dev->in_reset)
|
|
printk(KERN_INFO"%s%d: 64 Bit DAC enabled\n",
|
|
dev->name, dev->id);
|
|
} else if (!pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(32)) &&
|
|
!pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(32))) {
|
|
printk(KERN_INFO"%s%d: DMA mask set failed, 64 Bit DAC disabled\n",
|
|
dev->name, dev->id);
|
|
dev->dac_support = 0;
|
|
} else {
|
|
printk(KERN_WARNING"%s%d: No suitable DMA available.\n",
|
|
dev->name, dev->id);
|
|
rcode = -ENOMEM;
|
|
}
|
|
}
|
|
/*
|
|
* Deal with configuring for the individualized limits of each packet
|
|
* interface.
|
|
*/
|
|
dev->a_ops.adapter_scsi = (dev->dac_support)
|
|
? ((aac_get_driver_ident(dev->cardtype)->quirks & AAC_QUIRK_SCSI_32)
|
|
? aac_scsi_32_64
|
|
: aac_scsi_64)
|
|
: aac_scsi_32;
|
|
if (dev->raw_io_interface) {
|
|
dev->a_ops.adapter_bounds = (dev->raw_io_64)
|
|
? aac_bounds_64
|
|
: aac_bounds_32;
|
|
dev->a_ops.adapter_read = aac_read_raw_io;
|
|
dev->a_ops.adapter_write = aac_write_raw_io;
|
|
} else {
|
|
dev->a_ops.adapter_bounds = aac_bounds_32;
|
|
dev->scsi_host_ptr->sg_tablesize = (dev->max_fib_size -
|
|
sizeof(struct aac_fibhdr) -
|
|
sizeof(struct aac_write) + sizeof(struct sgentry)) /
|
|
sizeof(struct sgentry);
|
|
if (dev->dac_support) {
|
|
dev->a_ops.adapter_read = aac_read_block64;
|
|
dev->a_ops.adapter_write = aac_write_block64;
|
|
/*
|
|
* 38 scatter gather elements
|
|
*/
|
|
dev->scsi_host_ptr->sg_tablesize =
|
|
(dev->max_fib_size -
|
|
sizeof(struct aac_fibhdr) -
|
|
sizeof(struct aac_write64) +
|
|
sizeof(struct sgentry64)) /
|
|
sizeof(struct sgentry64);
|
|
} else {
|
|
dev->a_ops.adapter_read = aac_read_block;
|
|
dev->a_ops.adapter_write = aac_write_block;
|
|
}
|
|
dev->scsi_host_ptr->max_sectors = AAC_MAX_32BIT_SGBCOUNT;
|
|
if(!(dev->adapter_info.options & AAC_OPT_NEW_COMM)) {
|
|
/*
|
|
* Worst case size that could cause sg overflow when
|
|
* we break up SG elements that are larger than 64KB.
|
|
* Would be nice if we could tell the SCSI layer what
|
|
* the maximum SG element size can be. Worst case is
|
|
* (sg_tablesize-1) 4KB elements with one 64KB
|
|
* element.
|
|
* 32bit -> 468 or 238KB 64bit -> 424 or 212KB
|
|
*/
|
|
dev->scsi_host_ptr->max_sectors =
|
|
(dev->scsi_host_ptr->sg_tablesize * 8) + 112;
|
|
}
|
|
}
|
|
/* FIB should be freed only after getting the response from the F/W */
|
|
if (rcode != -ERESTARTSYS) {
|
|
aac_fib_complete(fibptr);
|
|
aac_fib_free(fibptr);
|
|
}
|
|
|
|
return rcode;
|
|
}
|
|
|
|
|
|
static void io_callback(void *context, struct fib * fibptr)
|
|
{
|
|
struct aac_dev *dev;
|
|
struct aac_read_reply *readreply;
|
|
struct scsi_cmnd *scsicmd;
|
|
u32 cid;
|
|
|
|
scsicmd = (struct scsi_cmnd *) context;
|
|
|
|
if (!aac_valid_context(scsicmd, fibptr))
|
|
return;
|
|
|
|
dev = fibptr->dev;
|
|
cid = scmd_id(scsicmd);
|
|
|
|
if (nblank(dprintk(x))) {
|
|
u64 lba;
|
|
switch (scsicmd->cmnd[0]) {
|
|
case WRITE_6:
|
|
case READ_6:
|
|
lba = ((scsicmd->cmnd[1] & 0x1F) << 16) |
|
|
(scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3];
|
|
break;
|
|
case WRITE_16:
|
|
case READ_16:
|
|
lba = ((u64)scsicmd->cmnd[2] << 56) |
|
|
((u64)scsicmd->cmnd[3] << 48) |
|
|
((u64)scsicmd->cmnd[4] << 40) |
|
|
((u64)scsicmd->cmnd[5] << 32) |
|
|
((u64)scsicmd->cmnd[6] << 24) |
|
|
(scsicmd->cmnd[7] << 16) |
|
|
(scsicmd->cmnd[8] << 8) | scsicmd->cmnd[9];
|
|
break;
|
|
case WRITE_12:
|
|
case READ_12:
|
|
lba = ((u64)scsicmd->cmnd[2] << 24) |
|
|
(scsicmd->cmnd[3] << 16) |
|
|
(scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5];
|
|
break;
|
|
default:
|
|
lba = ((u64)scsicmd->cmnd[2] << 24) |
|
|
(scsicmd->cmnd[3] << 16) |
|
|
(scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5];
|
|
break;
|
|
}
|
|
printk(KERN_DEBUG
|
|
"io_callback[cpu %d]: lba = %llu, t = %ld.\n",
|
|
smp_processor_id(), (unsigned long long)lba, jiffies);
|
|
}
|
|
|
|
BUG_ON(fibptr == NULL);
|
|
|
|
scsi_dma_unmap(scsicmd);
|
|
|
|
readreply = (struct aac_read_reply *)fib_data(fibptr);
|
|
switch (le32_to_cpu(readreply->status)) {
|
|
case ST_OK:
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
|
|
SAM_STAT_GOOD;
|
|
dev->fsa_dev[cid].sense_data.sense_key = NO_SENSE;
|
|
break;
|
|
case ST_NOT_READY:
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
|
|
SAM_STAT_CHECK_CONDITION;
|
|
set_sense(&dev->fsa_dev[cid].sense_data, NOT_READY,
|
|
SENCODE_BECOMING_READY, ASENCODE_BECOMING_READY, 0, 0);
|
|
memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
|
|
min_t(size_t, sizeof(dev->fsa_dev[cid].sense_data),
|
|
SCSI_SENSE_BUFFERSIZE));
|
|
break;
|
|
default:
|
|
#ifdef AAC_DETAILED_STATUS_INFO
|
|
printk(KERN_WARNING "io_callback: io failed, status = %d\n",
|
|
le32_to_cpu(readreply->status));
|
|
#endif
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
|
|
SAM_STAT_CHECK_CONDITION;
|
|
set_sense(&dev->fsa_dev[cid].sense_data,
|
|
HARDWARE_ERROR, SENCODE_INTERNAL_TARGET_FAILURE,
|
|
ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0);
|
|
memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
|
|
min_t(size_t, sizeof(dev->fsa_dev[cid].sense_data),
|
|
SCSI_SENSE_BUFFERSIZE));
|
|
break;
|
|
}
|
|
aac_fib_complete(fibptr);
|
|
aac_fib_free(fibptr);
|
|
|
|
scsicmd->scsi_done(scsicmd);
|
|
}
|
|
|
|
static int aac_read(struct scsi_cmnd * scsicmd)
|
|
{
|
|
u64 lba;
|
|
u32 count;
|
|
int status;
|
|
struct aac_dev *dev;
|
|
struct fib * cmd_fibcontext;
|
|
|
|
dev = (struct aac_dev *)scsicmd->device->host->hostdata;
|
|
/*
|
|
* Get block address and transfer length
|
|
*/
|
|
switch (scsicmd->cmnd[0]) {
|
|
case READ_6:
|
|
dprintk((KERN_DEBUG "aachba: received a read(6) command on id %d.\n", scmd_id(scsicmd)));
|
|
|
|
lba = ((scsicmd->cmnd[1] & 0x1F) << 16) |
|
|
(scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3];
|
|
count = scsicmd->cmnd[4];
|
|
|
|
if (count == 0)
|
|
count = 256;
|
|
break;
|
|
case READ_16:
|
|
dprintk((KERN_DEBUG "aachba: received a read(16) command on id %d.\n", scmd_id(scsicmd)));
|
|
|
|
lba = ((u64)scsicmd->cmnd[2] << 56) |
|
|
((u64)scsicmd->cmnd[3] << 48) |
|
|
((u64)scsicmd->cmnd[4] << 40) |
|
|
((u64)scsicmd->cmnd[5] << 32) |
|
|
((u64)scsicmd->cmnd[6] << 24) |
|
|
(scsicmd->cmnd[7] << 16) |
|
|
(scsicmd->cmnd[8] << 8) | scsicmd->cmnd[9];
|
|
count = (scsicmd->cmnd[10] << 24) |
|
|
(scsicmd->cmnd[11] << 16) |
|
|
(scsicmd->cmnd[12] << 8) | scsicmd->cmnd[13];
|
|
break;
|
|
case READ_12:
|
|
dprintk((KERN_DEBUG "aachba: received a read(12) command on id %d.\n", scmd_id(scsicmd)));
|
|
|
|
lba = ((u64)scsicmd->cmnd[2] << 24) |
|
|
(scsicmd->cmnd[3] << 16) |
|
|
(scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5];
|
|
count = (scsicmd->cmnd[6] << 24) |
|
|
(scsicmd->cmnd[7] << 16) |
|
|
(scsicmd->cmnd[8] << 8) | scsicmd->cmnd[9];
|
|
break;
|
|
default:
|
|
dprintk((KERN_DEBUG "aachba: received a read(10) command on id %d.\n", scmd_id(scsicmd)));
|
|
|
|
lba = ((u64)scsicmd->cmnd[2] << 24) |
|
|
(scsicmd->cmnd[3] << 16) |
|
|
(scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5];
|
|
count = (scsicmd->cmnd[7] << 8) | scsicmd->cmnd[8];
|
|
break;
|
|
}
|
|
dprintk((KERN_DEBUG "aac_read[cpu %d]: lba = %llu, t = %ld.\n",
|
|
smp_processor_id(), (unsigned long long)lba, jiffies));
|
|
if (aac_adapter_bounds(dev,scsicmd,lba))
|
|
return 0;
|
|
/*
|
|
* Alocate and initialize a Fib
|
|
*/
|
|
if (!(cmd_fibcontext = aac_fib_alloc(dev))) {
|
|
printk(KERN_WARNING "aac_read: fib allocation failed\n");
|
|
return -1;
|
|
}
|
|
|
|
status = aac_adapter_read(cmd_fibcontext, scsicmd, lba, count);
|
|
|
|
/*
|
|
* Check that the command queued to the controller
|
|
*/
|
|
if (status == -EINPROGRESS) {
|
|
scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
|
|
return 0;
|
|
}
|
|
|
|
printk(KERN_WARNING "aac_read: aac_fib_send failed with status: %d.\n", status);
|
|
/*
|
|
* For some reason, the Fib didn't queue, return QUEUE_FULL
|
|
*/
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_TASK_SET_FULL;
|
|
scsicmd->scsi_done(scsicmd);
|
|
aac_fib_complete(cmd_fibcontext);
|
|
aac_fib_free(cmd_fibcontext);
|
|
return 0;
|
|
}
|
|
|
|
static int aac_write(struct scsi_cmnd * scsicmd)
|
|
{
|
|
u64 lba;
|
|
u32 count;
|
|
int fua;
|
|
int status;
|
|
struct aac_dev *dev;
|
|
struct fib * cmd_fibcontext;
|
|
|
|
dev = (struct aac_dev *)scsicmd->device->host->hostdata;
|
|
/*
|
|
* Get block address and transfer length
|
|
*/
|
|
if (scsicmd->cmnd[0] == WRITE_6) /* 6 byte command */
|
|
{
|
|
lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3];
|
|
count = scsicmd->cmnd[4];
|
|
if (count == 0)
|
|
count = 256;
|
|
fua = 0;
|
|
} else if (scsicmd->cmnd[0] == WRITE_16) { /* 16 byte command */
|
|
dprintk((KERN_DEBUG "aachba: received a write(16) command on id %d.\n", scmd_id(scsicmd)));
|
|
|
|
lba = ((u64)scsicmd->cmnd[2] << 56) |
|
|
((u64)scsicmd->cmnd[3] << 48) |
|
|
((u64)scsicmd->cmnd[4] << 40) |
|
|
((u64)scsicmd->cmnd[5] << 32) |
|
|
((u64)scsicmd->cmnd[6] << 24) |
|
|
(scsicmd->cmnd[7] << 16) |
|
|
(scsicmd->cmnd[8] << 8) | scsicmd->cmnd[9];
|
|
count = (scsicmd->cmnd[10] << 24) | (scsicmd->cmnd[11] << 16) |
|
|
(scsicmd->cmnd[12] << 8) | scsicmd->cmnd[13];
|
|
fua = scsicmd->cmnd[1] & 0x8;
|
|
} else if (scsicmd->cmnd[0] == WRITE_12) { /* 12 byte command */
|
|
dprintk((KERN_DEBUG "aachba: received a write(12) command on id %d.\n", scmd_id(scsicmd)));
|
|
|
|
lba = ((u64)scsicmd->cmnd[2] << 24) | (scsicmd->cmnd[3] << 16)
|
|
| (scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5];
|
|
count = (scsicmd->cmnd[6] << 24) | (scsicmd->cmnd[7] << 16)
|
|
| (scsicmd->cmnd[8] << 8) | scsicmd->cmnd[9];
|
|
fua = scsicmd->cmnd[1] & 0x8;
|
|
} else {
|
|
dprintk((KERN_DEBUG "aachba: received a write(10) command on id %d.\n", scmd_id(scsicmd)));
|
|
lba = ((u64)scsicmd->cmnd[2] << 24) | (scsicmd->cmnd[3] << 16) | (scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5];
|
|
count = (scsicmd->cmnd[7] << 8) | scsicmd->cmnd[8];
|
|
fua = scsicmd->cmnd[1] & 0x8;
|
|
}
|
|
dprintk((KERN_DEBUG "aac_write[cpu %d]: lba = %llu, t = %ld.\n",
|
|
smp_processor_id(), (unsigned long long)lba, jiffies));
|
|
if (aac_adapter_bounds(dev,scsicmd,lba))
|
|
return 0;
|
|
/*
|
|
* Allocate and initialize a Fib then setup a BlockWrite command
|
|
*/
|
|
if (!(cmd_fibcontext = aac_fib_alloc(dev))) {
|
|
/* FIB temporarily unavailable,not catastrophic failure */
|
|
|
|
/* scsicmd->result = DID_ERROR << 16;
|
|
* scsicmd->scsi_done(scsicmd);
|
|
* return 0;
|
|
*/
|
|
printk(KERN_WARNING "aac_write: fib allocation failed\n");
|
|
return -1;
|
|
}
|
|
|
|
status = aac_adapter_write(cmd_fibcontext, scsicmd, lba, count, fua);
|
|
|
|
/*
|
|
* Check that the command queued to the controller
|
|
*/
|
|
if (status == -EINPROGRESS) {
|
|
scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
|
|
return 0;
|
|
}
|
|
|
|
printk(KERN_WARNING "aac_write: aac_fib_send failed with status: %d\n", status);
|
|
/*
|
|
* For some reason, the Fib didn't queue, return QUEUE_FULL
|
|
*/
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_TASK_SET_FULL;
|
|
scsicmd->scsi_done(scsicmd);
|
|
|
|
aac_fib_complete(cmd_fibcontext);
|
|
aac_fib_free(cmd_fibcontext);
|
|
return 0;
|
|
}
|
|
|
|
static void synchronize_callback(void *context, struct fib *fibptr)
|
|
{
|
|
struct aac_synchronize_reply *synchronizereply;
|
|
struct scsi_cmnd *cmd;
|
|
|
|
cmd = context;
|
|
|
|
if (!aac_valid_context(cmd, fibptr))
|
|
return;
|
|
|
|
dprintk((KERN_DEBUG "synchronize_callback[cpu %d]: t = %ld.\n",
|
|
smp_processor_id(), jiffies));
|
|
BUG_ON(fibptr == NULL);
|
|
|
|
|
|
synchronizereply = fib_data(fibptr);
|
|
if (le32_to_cpu(synchronizereply->status) == CT_OK)
|
|
cmd->result = DID_OK << 16 |
|
|
COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
|
|
else {
|
|
struct scsi_device *sdev = cmd->device;
|
|
struct aac_dev *dev = fibptr->dev;
|
|
u32 cid = sdev_id(sdev);
|
|
printk(KERN_WARNING
|
|
"synchronize_callback: synchronize failed, status = %d\n",
|
|
le32_to_cpu(synchronizereply->status));
|
|
cmd->result = DID_OK << 16 |
|
|
COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION;
|
|
set_sense(&dev->fsa_dev[cid].sense_data,
|
|
HARDWARE_ERROR, SENCODE_INTERNAL_TARGET_FAILURE,
|
|
ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0);
|
|
memcpy(cmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
|
|
min_t(size_t, sizeof(dev->fsa_dev[cid].sense_data),
|
|
SCSI_SENSE_BUFFERSIZE));
|
|
}
|
|
|
|
aac_fib_complete(fibptr);
|
|
aac_fib_free(fibptr);
|
|
cmd->scsi_done(cmd);
|
|
}
|
|
|
|
static int aac_synchronize(struct scsi_cmnd *scsicmd)
|
|
{
|
|
int status;
|
|
struct fib *cmd_fibcontext;
|
|
struct aac_synchronize *synchronizecmd;
|
|
struct scsi_cmnd *cmd;
|
|
struct scsi_device *sdev = scsicmd->device;
|
|
int active = 0;
|
|
struct aac_dev *aac;
|
|
u64 lba = ((u64)scsicmd->cmnd[2] << 24) | (scsicmd->cmnd[3] << 16) |
|
|
(scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5];
|
|
u32 count = (scsicmd->cmnd[7] << 8) | scsicmd->cmnd[8];
|
|
unsigned long flags;
|
|
|
|
/*
|
|
* Wait for all outstanding queued commands to complete to this
|
|
* specific target (block).
|
|
*/
|
|
spin_lock_irqsave(&sdev->list_lock, flags);
|
|
list_for_each_entry(cmd, &sdev->cmd_list, list)
|
|
if (cmd->SCp.phase == AAC_OWNER_FIRMWARE) {
|
|
u64 cmnd_lba;
|
|
u32 cmnd_count;
|
|
|
|
if (cmd->cmnd[0] == WRITE_6) {
|
|
cmnd_lba = ((cmd->cmnd[1] & 0x1F) << 16) |
|
|
(cmd->cmnd[2] << 8) |
|
|
cmd->cmnd[3];
|
|
cmnd_count = cmd->cmnd[4];
|
|
if (cmnd_count == 0)
|
|
cmnd_count = 256;
|
|
} else if (cmd->cmnd[0] == WRITE_16) {
|
|
cmnd_lba = ((u64)cmd->cmnd[2] << 56) |
|
|
((u64)cmd->cmnd[3] << 48) |
|
|
((u64)cmd->cmnd[4] << 40) |
|
|
((u64)cmd->cmnd[5] << 32) |
|
|
((u64)cmd->cmnd[6] << 24) |
|
|
(cmd->cmnd[7] << 16) |
|
|
(cmd->cmnd[8] << 8) |
|
|
cmd->cmnd[9];
|
|
cmnd_count = (cmd->cmnd[10] << 24) |
|
|
(cmd->cmnd[11] << 16) |
|
|
(cmd->cmnd[12] << 8) |
|
|
cmd->cmnd[13];
|
|
} else if (cmd->cmnd[0] == WRITE_12) {
|
|
cmnd_lba = ((u64)cmd->cmnd[2] << 24) |
|
|
(cmd->cmnd[3] << 16) |
|
|
(cmd->cmnd[4] << 8) |
|
|
cmd->cmnd[5];
|
|
cmnd_count = (cmd->cmnd[6] << 24) |
|
|
(cmd->cmnd[7] << 16) |
|
|
(cmd->cmnd[8] << 8) |
|
|
cmd->cmnd[9];
|
|
} else if (cmd->cmnd[0] == WRITE_10) {
|
|
cmnd_lba = ((u64)cmd->cmnd[2] << 24) |
|
|
(cmd->cmnd[3] << 16) |
|
|
(cmd->cmnd[4] << 8) |
|
|
cmd->cmnd[5];
|
|
cmnd_count = (cmd->cmnd[7] << 8) |
|
|
cmd->cmnd[8];
|
|
} else
|
|
continue;
|
|
if (((cmnd_lba + cmnd_count) < lba) ||
|
|
(count && ((lba + count) < cmnd_lba)))
|
|
continue;
|
|
++active;
|
|
break;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&sdev->list_lock, flags);
|
|
|
|
/*
|
|
* Yield the processor (requeue for later)
|
|
*/
|
|
if (active)
|
|
return SCSI_MLQUEUE_DEVICE_BUSY;
|
|
|
|
aac = (struct aac_dev *)sdev->host->hostdata;
|
|
if (aac->in_reset)
|
|
return SCSI_MLQUEUE_HOST_BUSY;
|
|
|
|
/*
|
|
* Allocate and initialize a Fib
|
|
*/
|
|
if (!(cmd_fibcontext = aac_fib_alloc(aac)))
|
|
return SCSI_MLQUEUE_HOST_BUSY;
|
|
|
|
aac_fib_init(cmd_fibcontext);
|
|
|
|
synchronizecmd = fib_data(cmd_fibcontext);
|
|
synchronizecmd->command = cpu_to_le32(VM_ContainerConfig);
|
|
synchronizecmd->type = cpu_to_le32(CT_FLUSH_CACHE);
|
|
synchronizecmd->cid = cpu_to_le32(scmd_id(scsicmd));
|
|
synchronizecmd->count =
|
|
cpu_to_le32(sizeof(((struct aac_synchronize_reply *)NULL)->data));
|
|
|
|
/*
|
|
* Now send the Fib to the adapter
|
|
*/
|
|
status = aac_fib_send(ContainerCommand,
|
|
cmd_fibcontext,
|
|
sizeof(struct aac_synchronize),
|
|
FsaNormal,
|
|
0, 1,
|
|
(fib_callback)synchronize_callback,
|
|
(void *)scsicmd);
|
|
|
|
/*
|
|
* Check that the command queued to the controller
|
|
*/
|
|
if (status == -EINPROGRESS) {
|
|
scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
|
|
return 0;
|
|
}
|
|
|
|
printk(KERN_WARNING
|
|
"aac_synchronize: aac_fib_send failed with status: %d.\n", status);
|
|
aac_fib_complete(cmd_fibcontext);
|
|
aac_fib_free(cmd_fibcontext);
|
|
return SCSI_MLQUEUE_HOST_BUSY;
|
|
}
|
|
|
|
static void aac_start_stop_callback(void *context, struct fib *fibptr)
|
|
{
|
|
struct scsi_cmnd *scsicmd = context;
|
|
|
|
if (!aac_valid_context(scsicmd, fibptr))
|
|
return;
|
|
|
|
BUG_ON(fibptr == NULL);
|
|
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
|
|
|
|
aac_fib_complete(fibptr);
|
|
aac_fib_free(fibptr);
|
|
scsicmd->scsi_done(scsicmd);
|
|
}
|
|
|
|
static int aac_start_stop(struct scsi_cmnd *scsicmd)
|
|
{
|
|
int status;
|
|
struct fib *cmd_fibcontext;
|
|
struct aac_power_management *pmcmd;
|
|
struct scsi_device *sdev = scsicmd->device;
|
|
struct aac_dev *aac = (struct aac_dev *)sdev->host->hostdata;
|
|
|
|
if (!(aac->supplement_adapter_info.SupportedOptions2 &
|
|
AAC_OPTION_POWER_MANAGEMENT)) {
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
|
|
SAM_STAT_GOOD;
|
|
scsicmd->scsi_done(scsicmd);
|
|
return 0;
|
|
}
|
|
|
|
if (aac->in_reset)
|
|
return SCSI_MLQUEUE_HOST_BUSY;
|
|
|
|
/*
|
|
* Allocate and initialize a Fib
|
|
*/
|
|
cmd_fibcontext = aac_fib_alloc(aac);
|
|
if (!cmd_fibcontext)
|
|
return SCSI_MLQUEUE_HOST_BUSY;
|
|
|
|
aac_fib_init(cmd_fibcontext);
|
|
|
|
pmcmd = fib_data(cmd_fibcontext);
|
|
pmcmd->command = cpu_to_le32(VM_ContainerConfig);
|
|
pmcmd->type = cpu_to_le32(CT_POWER_MANAGEMENT);
|
|
/* Eject bit ignored, not relevant */
|
|
pmcmd->sub = (scsicmd->cmnd[4] & 1) ?
|
|
cpu_to_le32(CT_PM_START_UNIT) : cpu_to_le32(CT_PM_STOP_UNIT);
|
|
pmcmd->cid = cpu_to_le32(sdev_id(sdev));
|
|
pmcmd->parm = (scsicmd->cmnd[1] & 1) ?
|
|
cpu_to_le32(CT_PM_UNIT_IMMEDIATE) : 0;
|
|
|
|
/*
|
|
* Now send the Fib to the adapter
|
|
*/
|
|
status = aac_fib_send(ContainerCommand,
|
|
cmd_fibcontext,
|
|
sizeof(struct aac_power_management),
|
|
FsaNormal,
|
|
0, 1,
|
|
(fib_callback)aac_start_stop_callback,
|
|
(void *)scsicmd);
|
|
|
|
/*
|
|
* Check that the command queued to the controller
|
|
*/
|
|
if (status == -EINPROGRESS) {
|
|
scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
|
|
return 0;
|
|
}
|
|
|
|
aac_fib_complete(cmd_fibcontext);
|
|
aac_fib_free(cmd_fibcontext);
|
|
return SCSI_MLQUEUE_HOST_BUSY;
|
|
}
|
|
|
|
/**
|
|
* aac_scsi_cmd() - Process SCSI command
|
|
* @scsicmd: SCSI command block
|
|
*
|
|
* Emulate a SCSI command and queue the required request for the
|
|
* aacraid firmware.
|
|
*/
|
|
|
|
int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
|
|
{
|
|
u32 cid;
|
|
struct Scsi_Host *host = scsicmd->device->host;
|
|
struct aac_dev *dev = (struct aac_dev *)host->hostdata;
|
|
struct fsa_dev_info *fsa_dev_ptr = dev->fsa_dev;
|
|
|
|
if (fsa_dev_ptr == NULL)
|
|
return -1;
|
|
/*
|
|
* If the bus, id or lun is out of range, return fail
|
|
* Test does not apply to ID 16, the pseudo id for the controller
|
|
* itself.
|
|
*/
|
|
cid = scmd_id(scsicmd);
|
|
if (cid != host->this_id) {
|
|
if (scmd_channel(scsicmd) == CONTAINER_CHANNEL) {
|
|
if((cid >= dev->maximum_num_containers) ||
|
|
(scsicmd->device->lun != 0)) {
|
|
scsicmd->result = DID_NO_CONNECT << 16;
|
|
scsicmd->scsi_done(scsicmd);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* If the target container doesn't exist, it may have
|
|
* been newly created
|
|
*/
|
|
if (((fsa_dev_ptr[cid].valid & 1) == 0) ||
|
|
(fsa_dev_ptr[cid].sense_data.sense_key ==
|
|
NOT_READY)) {
|
|
switch (scsicmd->cmnd[0]) {
|
|
case SERVICE_ACTION_IN:
|
|
if (!(dev->raw_io_interface) ||
|
|
!(dev->raw_io_64) ||
|
|
((scsicmd->cmnd[1] & 0x1f) != SAI_READ_CAPACITY_16))
|
|
break;
|
|
case INQUIRY:
|
|
case READ_CAPACITY:
|
|
case TEST_UNIT_READY:
|
|
if (dev->in_reset)
|
|
return -1;
|
|
return _aac_probe_container(scsicmd,
|
|
aac_probe_container_callback2);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} else { /* check for physical non-dasd devices */
|
|
if (dev->nondasd_support || expose_physicals ||
|
|
dev->jbod) {
|
|
if (dev->in_reset)
|
|
return -1;
|
|
return aac_send_srb_fib(scsicmd);
|
|
} else {
|
|
scsicmd->result = DID_NO_CONNECT << 16;
|
|
scsicmd->scsi_done(scsicmd);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* else Command for the controller itself
|
|
*/
|
|
else if ((scsicmd->cmnd[0] != INQUIRY) && /* only INQUIRY & TUR cmnd supported for controller */
|
|
(scsicmd->cmnd[0] != TEST_UNIT_READY))
|
|
{
|
|
dprintk((KERN_WARNING "Only INQUIRY & TUR command supported for controller, rcvd = 0x%x.\n", scsicmd->cmnd[0]));
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION;
|
|
set_sense(&dev->fsa_dev[cid].sense_data,
|
|
ILLEGAL_REQUEST, SENCODE_INVALID_COMMAND,
|
|
ASENCODE_INVALID_COMMAND, 0, 0);
|
|
memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
|
|
min_t(size_t, sizeof(dev->fsa_dev[cid].sense_data),
|
|
SCSI_SENSE_BUFFERSIZE));
|
|
scsicmd->scsi_done(scsicmd);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Handle commands here that don't really require going out to the adapter */
|
|
switch (scsicmd->cmnd[0]) {
|
|
case INQUIRY:
|
|
{
|
|
struct inquiry_data inq_data;
|
|
|
|
dprintk((KERN_DEBUG "INQUIRY command, ID: %d.\n", cid));
|
|
memset(&inq_data, 0, sizeof (struct inquiry_data));
|
|
|
|
if ((scsicmd->cmnd[1] & 0x1) && aac_wwn) {
|
|
char *arr = (char *)&inq_data;
|
|
|
|
/* EVPD bit set */
|
|
arr[0] = (scmd_id(scsicmd) == host->this_id) ?
|
|
INQD_PDT_PROC : INQD_PDT_DA;
|
|
if (scsicmd->cmnd[2] == 0) {
|
|
/* supported vital product data pages */
|
|
arr[3] = 2;
|
|
arr[4] = 0x0;
|
|
arr[5] = 0x80;
|
|
arr[1] = scsicmd->cmnd[2];
|
|
scsi_sg_copy_from_buffer(scsicmd, &inq_data,
|
|
sizeof(inq_data));
|
|
scsicmd->result = DID_OK << 16 |
|
|
COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
|
|
} else if (scsicmd->cmnd[2] == 0x80) {
|
|
/* unit serial number page */
|
|
arr[3] = setinqserial(dev, &arr[4],
|
|
scmd_id(scsicmd));
|
|
arr[1] = scsicmd->cmnd[2];
|
|
scsi_sg_copy_from_buffer(scsicmd, &inq_data,
|
|
sizeof(inq_data));
|
|
if (aac_wwn != 2)
|
|
return aac_get_container_serial(
|
|
scsicmd);
|
|
/* SLES 10 SP1 special */
|
|
scsicmd->result = DID_OK << 16 |
|
|
COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
|
|
} else {
|
|
/* vpd page not implemented */
|
|
scsicmd->result = DID_OK << 16 |
|
|
COMMAND_COMPLETE << 8 |
|
|
SAM_STAT_CHECK_CONDITION;
|
|
set_sense(&dev->fsa_dev[cid].sense_data,
|
|
ILLEGAL_REQUEST, SENCODE_INVALID_CDB_FIELD,
|
|
ASENCODE_NO_SENSE, 7, 2);
|
|
memcpy(scsicmd->sense_buffer,
|
|
&dev->fsa_dev[cid].sense_data,
|
|
min_t(size_t,
|
|
sizeof(dev->fsa_dev[cid].sense_data),
|
|
SCSI_SENSE_BUFFERSIZE));
|
|
}
|
|
scsicmd->scsi_done(scsicmd);
|
|
return 0;
|
|
}
|
|
inq_data.inqd_ver = 2; /* claim compliance to SCSI-2 */
|
|
inq_data.inqd_rdf = 2; /* A response data format value of two indicates that the data shall be in the format specified in SCSI-2 */
|
|
inq_data.inqd_len = 31;
|
|
/*Format for "pad2" is RelAdr | WBus32 | WBus16 | Sync | Linked |Reserved| CmdQue | SftRe */
|
|
inq_data.inqd_pad2= 0x32 ; /*WBus16|Sync|CmdQue */
|
|
/*
|
|
* Set the Vendor, Product, and Revision Level
|
|
* see: <vendor>.c i.e. aac.c
|
|
*/
|
|
if (cid == host->this_id) {
|
|
setinqstr(dev, (void *) (inq_data.inqd_vid), ARRAY_SIZE(container_types));
|
|
inq_data.inqd_pdt = INQD_PDT_PROC; /* Processor device */
|
|
scsi_sg_copy_from_buffer(scsicmd, &inq_data,
|
|
sizeof(inq_data));
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
|
|
scsicmd->scsi_done(scsicmd);
|
|
return 0;
|
|
}
|
|
if (dev->in_reset)
|
|
return -1;
|
|
setinqstr(dev, (void *) (inq_data.inqd_vid), fsa_dev_ptr[cid].type);
|
|
inq_data.inqd_pdt = INQD_PDT_DA; /* Direct/random access device */
|
|
scsi_sg_copy_from_buffer(scsicmd, &inq_data, sizeof(inq_data));
|
|
return aac_get_container_name(scsicmd);
|
|
}
|
|
case SERVICE_ACTION_IN:
|
|
if (!(dev->raw_io_interface) ||
|
|
!(dev->raw_io_64) ||
|
|
((scsicmd->cmnd[1] & 0x1f) != SAI_READ_CAPACITY_16))
|
|
break;
|
|
{
|
|
u64 capacity;
|
|
char cp[13];
|
|
unsigned int alloc_len;
|
|
|
|
dprintk((KERN_DEBUG "READ CAPACITY_16 command.\n"));
|
|
capacity = fsa_dev_ptr[cid].size - 1;
|
|
cp[0] = (capacity >> 56) & 0xff;
|
|
cp[1] = (capacity >> 48) & 0xff;
|
|
cp[2] = (capacity >> 40) & 0xff;
|
|
cp[3] = (capacity >> 32) & 0xff;
|
|
cp[4] = (capacity >> 24) & 0xff;
|
|
cp[5] = (capacity >> 16) & 0xff;
|
|
cp[6] = (capacity >> 8) & 0xff;
|
|
cp[7] = (capacity >> 0) & 0xff;
|
|
cp[8] = 0;
|
|
cp[9] = 0;
|
|
cp[10] = 2;
|
|
cp[11] = 0;
|
|
cp[12] = 0;
|
|
|
|
alloc_len = ((scsicmd->cmnd[10] << 24)
|
|
+ (scsicmd->cmnd[11] << 16)
|
|
+ (scsicmd->cmnd[12] << 8) + scsicmd->cmnd[13]);
|
|
|
|
alloc_len = min_t(size_t, alloc_len, sizeof(cp));
|
|
scsi_sg_copy_from_buffer(scsicmd, cp, alloc_len);
|
|
if (alloc_len < scsi_bufflen(scsicmd))
|
|
scsi_set_resid(scsicmd,
|
|
scsi_bufflen(scsicmd) - alloc_len);
|
|
|
|
/* Do not cache partition table for arrays */
|
|
scsicmd->device->removable = 1;
|
|
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
|
|
scsicmd->scsi_done(scsicmd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
case READ_CAPACITY:
|
|
{
|
|
u32 capacity;
|
|
char cp[8];
|
|
|
|
dprintk((KERN_DEBUG "READ CAPACITY command.\n"));
|
|
if (fsa_dev_ptr[cid].size <= 0x100000000ULL)
|
|
capacity = fsa_dev_ptr[cid].size - 1;
|
|
else
|
|
capacity = (u32)-1;
|
|
|
|
cp[0] = (capacity >> 24) & 0xff;
|
|
cp[1] = (capacity >> 16) & 0xff;
|
|
cp[2] = (capacity >> 8) & 0xff;
|
|
cp[3] = (capacity >> 0) & 0xff;
|
|
cp[4] = 0;
|
|
cp[5] = 0;
|
|
cp[6] = 2;
|
|
cp[7] = 0;
|
|
scsi_sg_copy_from_buffer(scsicmd, cp, sizeof(cp));
|
|
/* Do not cache partition table for arrays */
|
|
scsicmd->device->removable = 1;
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
|
|
SAM_STAT_GOOD;
|
|
scsicmd->scsi_done(scsicmd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
case MODE_SENSE:
|
|
{
|
|
char mode_buf[7];
|
|
int mode_buf_length = 4;
|
|
|
|
dprintk((KERN_DEBUG "MODE SENSE command.\n"));
|
|
mode_buf[0] = 3; /* Mode data length */
|
|
mode_buf[1] = 0; /* Medium type - default */
|
|
mode_buf[2] = 0; /* Device-specific param,
|
|
bit 8: 0/1 = write enabled/protected
|
|
bit 4: 0/1 = FUA enabled */
|
|
if (dev->raw_io_interface && ((aac_cache & 5) != 1))
|
|
mode_buf[2] = 0x10;
|
|
mode_buf[3] = 0; /* Block descriptor length */
|
|
if (((scsicmd->cmnd[2] & 0x3f) == 8) ||
|
|
((scsicmd->cmnd[2] & 0x3f) == 0x3f)) {
|
|
mode_buf[0] = 6;
|
|
mode_buf[4] = 8;
|
|
mode_buf[5] = 1;
|
|
mode_buf[6] = ((aac_cache & 6) == 2)
|
|
? 0 : 0x04; /* WCE */
|
|
mode_buf_length = 7;
|
|
if (mode_buf_length > scsicmd->cmnd[4])
|
|
mode_buf_length = scsicmd->cmnd[4];
|
|
}
|
|
scsi_sg_copy_from_buffer(scsicmd, mode_buf, mode_buf_length);
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
|
|
scsicmd->scsi_done(scsicmd);
|
|
|
|
return 0;
|
|
}
|
|
case MODE_SENSE_10:
|
|
{
|
|
char mode_buf[11];
|
|
int mode_buf_length = 8;
|
|
|
|
dprintk((KERN_DEBUG "MODE SENSE 10 byte command.\n"));
|
|
mode_buf[0] = 0; /* Mode data length (MSB) */
|
|
mode_buf[1] = 6; /* Mode data length (LSB) */
|
|
mode_buf[2] = 0; /* Medium type - default */
|
|
mode_buf[3] = 0; /* Device-specific param,
|
|
bit 8: 0/1 = write enabled/protected
|
|
bit 4: 0/1 = FUA enabled */
|
|
if (dev->raw_io_interface && ((aac_cache & 5) != 1))
|
|
mode_buf[3] = 0x10;
|
|
mode_buf[4] = 0; /* reserved */
|
|
mode_buf[5] = 0; /* reserved */
|
|
mode_buf[6] = 0; /* Block descriptor length (MSB) */
|
|
mode_buf[7] = 0; /* Block descriptor length (LSB) */
|
|
if (((scsicmd->cmnd[2] & 0x3f) == 8) ||
|
|
((scsicmd->cmnd[2] & 0x3f) == 0x3f)) {
|
|
mode_buf[1] = 9;
|
|
mode_buf[8] = 8;
|
|
mode_buf[9] = 1;
|
|
mode_buf[10] = ((aac_cache & 6) == 2)
|
|
? 0 : 0x04; /* WCE */
|
|
mode_buf_length = 11;
|
|
if (mode_buf_length > scsicmd->cmnd[8])
|
|
mode_buf_length = scsicmd->cmnd[8];
|
|
}
|
|
scsi_sg_copy_from_buffer(scsicmd, mode_buf, mode_buf_length);
|
|
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
|
|
scsicmd->scsi_done(scsicmd);
|
|
|
|
return 0;
|
|
}
|
|
case REQUEST_SENSE:
|
|
dprintk((KERN_DEBUG "REQUEST SENSE command.\n"));
|
|
memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data, sizeof (struct sense_data));
|
|
memset(&dev->fsa_dev[cid].sense_data, 0, sizeof (struct sense_data));
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
|
|
scsicmd->scsi_done(scsicmd);
|
|
return 0;
|
|
|
|
case ALLOW_MEDIUM_REMOVAL:
|
|
dprintk((KERN_DEBUG "LOCK command.\n"));
|
|
if (scsicmd->cmnd[4])
|
|
fsa_dev_ptr[cid].locked = 1;
|
|
else
|
|
fsa_dev_ptr[cid].locked = 0;
|
|
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
|
|
scsicmd->scsi_done(scsicmd);
|
|
return 0;
|
|
/*
|
|
* These commands are all No-Ops
|
|
*/
|
|
case TEST_UNIT_READY:
|
|
if (fsa_dev_ptr[cid].sense_data.sense_key == NOT_READY) {
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
|
|
SAM_STAT_CHECK_CONDITION;
|
|
set_sense(&dev->fsa_dev[cid].sense_data,
|
|
NOT_READY, SENCODE_BECOMING_READY,
|
|
ASENCODE_BECOMING_READY, 0, 0);
|
|
memcpy(scsicmd->sense_buffer,
|
|
&dev->fsa_dev[cid].sense_data,
|
|
min_t(size_t,
|
|
sizeof(dev->fsa_dev[cid].sense_data),
|
|
SCSI_SENSE_BUFFERSIZE));
|
|
scsicmd->scsi_done(scsicmd);
|
|
return 0;
|
|
}
|
|
/* FALLTHRU */
|
|
case RESERVE:
|
|
case RELEASE:
|
|
case REZERO_UNIT:
|
|
case REASSIGN_BLOCKS:
|
|
case SEEK_10:
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
|
|
scsicmd->scsi_done(scsicmd);
|
|
return 0;
|
|
|
|
case START_STOP:
|
|
return aac_start_stop(scsicmd);
|
|
}
|
|
|
|
switch (scsicmd->cmnd[0])
|
|
{
|
|
case READ_6:
|
|
case READ_10:
|
|
case READ_12:
|
|
case READ_16:
|
|
if (dev->in_reset)
|
|
return -1;
|
|
/*
|
|
* Hack to keep track of ordinal number of the device that
|
|
* corresponds to a container. Needed to convert
|
|
* containers to /dev/sd device names
|
|
*/
|
|
|
|
if (scsicmd->request->rq_disk)
|
|
strlcpy(fsa_dev_ptr[cid].devname,
|
|
scsicmd->request->rq_disk->disk_name,
|
|
min(sizeof(fsa_dev_ptr[cid].devname),
|
|
sizeof(scsicmd->request->rq_disk->disk_name) + 1));
|
|
|
|
return aac_read(scsicmd);
|
|
|
|
case WRITE_6:
|
|
case WRITE_10:
|
|
case WRITE_12:
|
|
case WRITE_16:
|
|
if (dev->in_reset)
|
|
return -1;
|
|
return aac_write(scsicmd);
|
|
|
|
case SYNCHRONIZE_CACHE:
|
|
if (((aac_cache & 6) == 6) && dev->cache_protected) {
|
|
scsicmd->result = DID_OK << 16 |
|
|
COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
|
|
scsicmd->scsi_done(scsicmd);
|
|
return 0;
|
|
}
|
|
/* Issue FIB to tell Firmware to flush it's cache */
|
|
if ((aac_cache & 6) != 2)
|
|
return aac_synchronize(scsicmd);
|
|
/* FALLTHRU */
|
|
default:
|
|
/*
|
|
* Unhandled commands
|
|
*/
|
|
dprintk((KERN_WARNING "Unhandled SCSI Command: 0x%x.\n", scsicmd->cmnd[0]));
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION;
|
|
set_sense(&dev->fsa_dev[cid].sense_data,
|
|
ILLEGAL_REQUEST, SENCODE_INVALID_COMMAND,
|
|
ASENCODE_INVALID_COMMAND, 0, 0);
|
|
memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
|
|
min_t(size_t,
|
|
sizeof(dev->fsa_dev[cid].sense_data),
|
|
SCSI_SENSE_BUFFERSIZE));
|
|
scsicmd->scsi_done(scsicmd);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int query_disk(struct aac_dev *dev, void __user *arg)
|
|
{
|
|
struct aac_query_disk qd;
|
|
struct fsa_dev_info *fsa_dev_ptr;
|
|
|
|
fsa_dev_ptr = dev->fsa_dev;
|
|
if (!fsa_dev_ptr)
|
|
return -EBUSY;
|
|
if (copy_from_user(&qd, arg, sizeof (struct aac_query_disk)))
|
|
return -EFAULT;
|
|
if (qd.cnum == -1)
|
|
qd.cnum = qd.id;
|
|
else if ((qd.bus == -1) && (qd.id == -1) && (qd.lun == -1))
|
|
{
|
|
if (qd.cnum < 0 || qd.cnum >= dev->maximum_num_containers)
|
|
return -EINVAL;
|
|
qd.instance = dev->scsi_host_ptr->host_no;
|
|
qd.bus = 0;
|
|
qd.id = CONTAINER_TO_ID(qd.cnum);
|
|
qd.lun = CONTAINER_TO_LUN(qd.cnum);
|
|
}
|
|
else return -EINVAL;
|
|
|
|
qd.valid = fsa_dev_ptr[qd.cnum].valid != 0;
|
|
qd.locked = fsa_dev_ptr[qd.cnum].locked;
|
|
qd.deleted = fsa_dev_ptr[qd.cnum].deleted;
|
|
|
|
if (fsa_dev_ptr[qd.cnum].devname[0] == '\0')
|
|
qd.unmapped = 1;
|
|
else
|
|
qd.unmapped = 0;
|
|
|
|
strlcpy(qd.name, fsa_dev_ptr[qd.cnum].devname,
|
|
min(sizeof(qd.name), sizeof(fsa_dev_ptr[qd.cnum].devname) + 1));
|
|
|
|
if (copy_to_user(arg, &qd, sizeof (struct aac_query_disk)))
|
|
return -EFAULT;
|
|
return 0;
|
|
}
|
|
|
|
static int force_delete_disk(struct aac_dev *dev, void __user *arg)
|
|
{
|
|
struct aac_delete_disk dd;
|
|
struct fsa_dev_info *fsa_dev_ptr;
|
|
|
|
fsa_dev_ptr = dev->fsa_dev;
|
|
if (!fsa_dev_ptr)
|
|
return -EBUSY;
|
|
|
|
if (copy_from_user(&dd, arg, sizeof (struct aac_delete_disk)))
|
|
return -EFAULT;
|
|
|
|
if (dd.cnum >= dev->maximum_num_containers)
|
|
return -EINVAL;
|
|
/*
|
|
* Mark this container as being deleted.
|
|
*/
|
|
fsa_dev_ptr[dd.cnum].deleted = 1;
|
|
/*
|
|
* Mark the container as no longer valid
|
|
*/
|
|
fsa_dev_ptr[dd.cnum].valid = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int delete_disk(struct aac_dev *dev, void __user *arg)
|
|
{
|
|
struct aac_delete_disk dd;
|
|
struct fsa_dev_info *fsa_dev_ptr;
|
|
|
|
fsa_dev_ptr = dev->fsa_dev;
|
|
if (!fsa_dev_ptr)
|
|
return -EBUSY;
|
|
|
|
if (copy_from_user(&dd, arg, sizeof (struct aac_delete_disk)))
|
|
return -EFAULT;
|
|
|
|
if (dd.cnum >= dev->maximum_num_containers)
|
|
return -EINVAL;
|
|
/*
|
|
* If the container is locked, it can not be deleted by the API.
|
|
*/
|
|
if (fsa_dev_ptr[dd.cnum].locked)
|
|
return -EBUSY;
|
|
else {
|
|
/*
|
|
* Mark the container as no longer being valid.
|
|
*/
|
|
fsa_dev_ptr[dd.cnum].valid = 0;
|
|
fsa_dev_ptr[dd.cnum].devname[0] = '\0';
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int aac_dev_ioctl(struct aac_dev *dev, int cmd, void __user *arg)
|
|
{
|
|
switch (cmd) {
|
|
case FSACTL_QUERY_DISK:
|
|
return query_disk(dev, arg);
|
|
case FSACTL_DELETE_DISK:
|
|
return delete_disk(dev, arg);
|
|
case FSACTL_FORCE_DELETE_DISK:
|
|
return force_delete_disk(dev, arg);
|
|
case FSACTL_GET_CONTAINERS:
|
|
return aac_get_containers(dev);
|
|
default:
|
|
return -ENOTTY;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* aac_srb_callback
|
|
* @context: the context set in the fib - here it is scsi cmd
|
|
* @fibptr: pointer to the fib
|
|
*
|
|
* Handles the completion of a scsi command to a non dasd device
|
|
*
|
|
*/
|
|
|
|
static void aac_srb_callback(void *context, struct fib * fibptr)
|
|
{
|
|
struct aac_dev *dev;
|
|
struct aac_srb_reply *srbreply;
|
|
struct scsi_cmnd *scsicmd;
|
|
|
|
scsicmd = (struct scsi_cmnd *) context;
|
|
|
|
if (!aac_valid_context(scsicmd, fibptr))
|
|
return;
|
|
|
|
BUG_ON(fibptr == NULL);
|
|
|
|
dev = fibptr->dev;
|
|
|
|
srbreply = (struct aac_srb_reply *) fib_data(fibptr);
|
|
|
|
scsicmd->sense_buffer[0] = '\0'; /* Initialize sense valid flag to false */
|
|
/*
|
|
* Calculate resid for sg
|
|
*/
|
|
|
|
scsi_set_resid(scsicmd, scsi_bufflen(scsicmd)
|
|
- le32_to_cpu(srbreply->data_xfer_length));
|
|
|
|
scsi_dma_unmap(scsicmd);
|
|
|
|
/*
|
|
* First check the fib status
|
|
*/
|
|
|
|
if (le32_to_cpu(srbreply->status) != ST_OK){
|
|
int len;
|
|
printk(KERN_WARNING "aac_srb_callback: srb failed, status = %d\n", le32_to_cpu(srbreply->status));
|
|
len = min_t(u32, le32_to_cpu(srbreply->sense_data_size),
|
|
SCSI_SENSE_BUFFERSIZE);
|
|
scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION;
|
|
memcpy(scsicmd->sense_buffer, srbreply->sense_data, len);
|
|
}
|
|
|
|
/*
|
|
* Next check the srb status
|
|
*/
|
|
switch( (le32_to_cpu(srbreply->srb_status))&0x3f){
|
|
case SRB_STATUS_ERROR_RECOVERY:
|
|
case SRB_STATUS_PENDING:
|
|
case SRB_STATUS_SUCCESS:
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
|
|
break;
|
|
case SRB_STATUS_DATA_OVERRUN:
|
|
switch(scsicmd->cmnd[0]){
|
|
case READ_6:
|
|
case WRITE_6:
|
|
case READ_10:
|
|
case WRITE_10:
|
|
case READ_12:
|
|
case WRITE_12:
|
|
case READ_16:
|
|
case WRITE_16:
|
|
if (le32_to_cpu(srbreply->data_xfer_length) < scsicmd->underflow) {
|
|
printk(KERN_WARNING"aacraid: SCSI CMD underflow\n");
|
|
} else {
|
|
printk(KERN_WARNING"aacraid: SCSI CMD Data Overrun\n");
|
|
}
|
|
scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8;
|
|
break;
|
|
case INQUIRY: {
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
|
|
break;
|
|
}
|
|
default:
|
|
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
|
|
break;
|
|
}
|
|
break;
|
|
case SRB_STATUS_ABORTED:
|
|
scsicmd->result = DID_ABORT << 16 | ABORT << 8;
|
|
break;
|
|
case SRB_STATUS_ABORT_FAILED:
|
|
// Not sure about this one - but assuming the hba was trying to abort for some reason
|
|
scsicmd->result = DID_ERROR << 16 | ABORT << 8;
|
|
break;
|
|
case SRB_STATUS_PARITY_ERROR:
|
|
scsicmd->result = DID_PARITY << 16 | MSG_PARITY_ERROR << 8;
|
|
break;
|
|
case SRB_STATUS_NO_DEVICE:
|
|
case SRB_STATUS_INVALID_PATH_ID:
|
|
case SRB_STATUS_INVALID_TARGET_ID:
|
|
case SRB_STATUS_INVALID_LUN:
|
|
case SRB_STATUS_SELECTION_TIMEOUT:
|
|
scsicmd->result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8;
|
|
break;
|
|
|
|
case SRB_STATUS_COMMAND_TIMEOUT:
|
|
case SRB_STATUS_TIMEOUT:
|
|
scsicmd->result = DID_TIME_OUT << 16 | COMMAND_COMPLETE << 8;
|
|
break;
|
|
|
|
case SRB_STATUS_BUSY:
|
|
scsicmd->result = DID_BUS_BUSY << 16 | COMMAND_COMPLETE << 8;
|
|
break;
|
|
|
|
case SRB_STATUS_BUS_RESET:
|
|
scsicmd->result = DID_RESET << 16 | COMMAND_COMPLETE << 8;
|
|
break;
|
|
|
|
case SRB_STATUS_MESSAGE_REJECTED:
|
|
scsicmd->result = DID_ERROR << 16 | MESSAGE_REJECT << 8;
|
|
break;
|
|
case SRB_STATUS_REQUEST_FLUSHED:
|
|
case SRB_STATUS_ERROR:
|
|
case SRB_STATUS_INVALID_REQUEST:
|
|
case SRB_STATUS_REQUEST_SENSE_FAILED:
|
|
case SRB_STATUS_NO_HBA:
|
|
case SRB_STATUS_UNEXPECTED_BUS_FREE:
|
|
case SRB_STATUS_PHASE_SEQUENCE_FAILURE:
|
|
case SRB_STATUS_BAD_SRB_BLOCK_LENGTH:
|
|
case SRB_STATUS_DELAYED_RETRY:
|
|
case SRB_STATUS_BAD_FUNCTION:
|
|
case SRB_STATUS_NOT_STARTED:
|
|
case SRB_STATUS_NOT_IN_USE:
|
|
case SRB_STATUS_FORCE_ABORT:
|
|
case SRB_STATUS_DOMAIN_VALIDATION_FAIL:
|
|
default:
|
|
#ifdef AAC_DETAILED_STATUS_INFO
|
|
printk("aacraid: SRB ERROR(%u) %s scsi cmd 0x%x - scsi status 0x%x\n",
|
|
le32_to_cpu(srbreply->srb_status) & 0x3F,
|
|
aac_get_status_string(
|
|
le32_to_cpu(srbreply->srb_status) & 0x3F),
|
|
scsicmd->cmnd[0],
|
|
le32_to_cpu(srbreply->scsi_status));
|
|
#endif
|
|
scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8;
|
|
break;
|
|
}
|
|
if (le32_to_cpu(srbreply->scsi_status) == SAM_STAT_CHECK_CONDITION) {
|
|
int len;
|
|
scsicmd->result |= SAM_STAT_CHECK_CONDITION;
|
|
len = min_t(u32, le32_to_cpu(srbreply->sense_data_size),
|
|
SCSI_SENSE_BUFFERSIZE);
|
|
#ifdef AAC_DETAILED_STATUS_INFO
|
|
printk(KERN_WARNING "aac_srb_callback: check condition, status = %d len=%d\n",
|
|
le32_to_cpu(srbreply->status), len);
|
|
#endif
|
|
memcpy(scsicmd->sense_buffer, srbreply->sense_data, len);
|
|
}
|
|
/*
|
|
* OR in the scsi status (already shifted up a bit)
|
|
*/
|
|
scsicmd->result |= le32_to_cpu(srbreply->scsi_status);
|
|
|
|
aac_fib_complete(fibptr);
|
|
aac_fib_free(fibptr);
|
|
scsicmd->scsi_done(scsicmd);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* aac_send_scb_fib
|
|
* @scsicmd: the scsi command block
|
|
*
|
|
* This routine will form a FIB and fill in the aac_srb from the
|
|
* scsicmd passed in.
|
|
*/
|
|
|
|
static int aac_send_srb_fib(struct scsi_cmnd* scsicmd)
|
|
{
|
|
struct fib* cmd_fibcontext;
|
|
struct aac_dev* dev;
|
|
int status;
|
|
|
|
dev = (struct aac_dev *)scsicmd->device->host->hostdata;
|
|
if (scmd_id(scsicmd) >= dev->maximum_num_physicals ||
|
|
scsicmd->device->lun > 7) {
|
|
scsicmd->result = DID_NO_CONNECT << 16;
|
|
scsicmd->scsi_done(scsicmd);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Allocate and initialize a Fib then setup a BlockWrite command
|
|
*/
|
|
if (!(cmd_fibcontext = aac_fib_alloc(dev))) {
|
|
return -1;
|
|
}
|
|
status = aac_adapter_scsi(cmd_fibcontext, scsicmd);
|
|
|
|
/*
|
|
* Check that the command queued to the controller
|
|
*/
|
|
if (status == -EINPROGRESS) {
|
|
scsicmd->SCp.phase = AAC_OWNER_FIRMWARE;
|
|
return 0;
|
|
}
|
|
|
|
printk(KERN_WARNING "aac_srb: aac_fib_send failed with status: %d\n", status);
|
|
aac_fib_complete(cmd_fibcontext);
|
|
aac_fib_free(cmd_fibcontext);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static unsigned long aac_build_sg(struct scsi_cmnd* scsicmd, struct sgmap* psg)
|
|
{
|
|
struct aac_dev *dev;
|
|
unsigned long byte_count = 0;
|
|
int nseg;
|
|
|
|
dev = (struct aac_dev *)scsicmd->device->host->hostdata;
|
|
// Get rid of old data
|
|
psg->count = 0;
|
|
psg->sg[0].addr = 0;
|
|
psg->sg[0].count = 0;
|
|
|
|
nseg = scsi_dma_map(scsicmd);
|
|
BUG_ON(nseg < 0);
|
|
if (nseg) {
|
|
struct scatterlist *sg;
|
|
int i;
|
|
|
|
psg->count = cpu_to_le32(nseg);
|
|
|
|
scsi_for_each_sg(scsicmd, sg, nseg, i) {
|
|
psg->sg[i].addr = cpu_to_le32(sg_dma_address(sg));
|
|
psg->sg[i].count = cpu_to_le32(sg_dma_len(sg));
|
|
byte_count += sg_dma_len(sg);
|
|
}
|
|
/* hba wants the size to be exact */
|
|
if (byte_count > scsi_bufflen(scsicmd)) {
|
|
u32 temp = le32_to_cpu(psg->sg[i-1].count) -
|
|
(byte_count - scsi_bufflen(scsicmd));
|
|
psg->sg[i-1].count = cpu_to_le32(temp);
|
|
byte_count = scsi_bufflen(scsicmd);
|
|
}
|
|
/* Check for command underflow */
|
|
if(scsicmd->underflow && (byte_count < scsicmd->underflow)){
|
|
printk(KERN_WARNING"aacraid: cmd len %08lX cmd underflow %08X\n",
|
|
byte_count, scsicmd->underflow);
|
|
}
|
|
}
|
|
return byte_count;
|
|
}
|
|
|
|
|
|
static unsigned long aac_build_sg64(struct scsi_cmnd* scsicmd, struct sgmap64* psg)
|
|
{
|
|
struct aac_dev *dev;
|
|
unsigned long byte_count = 0;
|
|
u64 addr;
|
|
int nseg;
|
|
|
|
dev = (struct aac_dev *)scsicmd->device->host->hostdata;
|
|
// Get rid of old data
|
|
psg->count = 0;
|
|
psg->sg[0].addr[0] = 0;
|
|
psg->sg[0].addr[1] = 0;
|
|
psg->sg[0].count = 0;
|
|
|
|
nseg = scsi_dma_map(scsicmd);
|
|
BUG_ON(nseg < 0);
|
|
if (nseg) {
|
|
struct scatterlist *sg;
|
|
int i;
|
|
|
|
scsi_for_each_sg(scsicmd, sg, nseg, i) {
|
|
int count = sg_dma_len(sg);
|
|
addr = sg_dma_address(sg);
|
|
psg->sg[i].addr[0] = cpu_to_le32(addr & 0xffffffff);
|
|
psg->sg[i].addr[1] = cpu_to_le32(addr>>32);
|
|
psg->sg[i].count = cpu_to_le32(count);
|
|
byte_count += count;
|
|
}
|
|
psg->count = cpu_to_le32(nseg);
|
|
/* hba wants the size to be exact */
|
|
if (byte_count > scsi_bufflen(scsicmd)) {
|
|
u32 temp = le32_to_cpu(psg->sg[i-1].count) -
|
|
(byte_count - scsi_bufflen(scsicmd));
|
|
psg->sg[i-1].count = cpu_to_le32(temp);
|
|
byte_count = scsi_bufflen(scsicmd);
|
|
}
|
|
/* Check for command underflow */
|
|
if(scsicmd->underflow && (byte_count < scsicmd->underflow)){
|
|
printk(KERN_WARNING"aacraid: cmd len %08lX cmd underflow %08X\n",
|
|
byte_count, scsicmd->underflow);
|
|
}
|
|
}
|
|
return byte_count;
|
|
}
|
|
|
|
static unsigned long aac_build_sgraw(struct scsi_cmnd* scsicmd, struct sgmapraw* psg)
|
|
{
|
|
unsigned long byte_count = 0;
|
|
int nseg;
|
|
|
|
// Get rid of old data
|
|
psg->count = 0;
|
|
psg->sg[0].next = 0;
|
|
psg->sg[0].prev = 0;
|
|
psg->sg[0].addr[0] = 0;
|
|
psg->sg[0].addr[1] = 0;
|
|
psg->sg[0].count = 0;
|
|
psg->sg[0].flags = 0;
|
|
|
|
nseg = scsi_dma_map(scsicmd);
|
|
BUG_ON(nseg < 0);
|
|
if (nseg) {
|
|
struct scatterlist *sg;
|
|
int i;
|
|
|
|
scsi_for_each_sg(scsicmd, sg, nseg, i) {
|
|
int count = sg_dma_len(sg);
|
|
u64 addr = sg_dma_address(sg);
|
|
psg->sg[i].next = 0;
|
|
psg->sg[i].prev = 0;
|
|
psg->sg[i].addr[1] = cpu_to_le32((u32)(addr>>32));
|
|
psg->sg[i].addr[0] = cpu_to_le32((u32)(addr & 0xffffffff));
|
|
psg->sg[i].count = cpu_to_le32(count);
|
|
psg->sg[i].flags = 0;
|
|
byte_count += count;
|
|
}
|
|
psg->count = cpu_to_le32(nseg);
|
|
/* hba wants the size to be exact */
|
|
if (byte_count > scsi_bufflen(scsicmd)) {
|
|
u32 temp = le32_to_cpu(psg->sg[i-1].count) -
|
|
(byte_count - scsi_bufflen(scsicmd));
|
|
psg->sg[i-1].count = cpu_to_le32(temp);
|
|
byte_count = scsi_bufflen(scsicmd);
|
|
}
|
|
/* Check for command underflow */
|
|
if(scsicmd->underflow && (byte_count < scsicmd->underflow)){
|
|
printk(KERN_WARNING"aacraid: cmd len %08lX cmd underflow %08X\n",
|
|
byte_count, scsicmd->underflow);
|
|
}
|
|
}
|
|
return byte_count;
|
|
}
|
|
|
|
#ifdef AAC_DETAILED_STATUS_INFO
|
|
|
|
struct aac_srb_status_info {
|
|
u32 status;
|
|
char *str;
|
|
};
|
|
|
|
|
|
static struct aac_srb_status_info srb_status_info[] = {
|
|
{ SRB_STATUS_PENDING, "Pending Status"},
|
|
{ SRB_STATUS_SUCCESS, "Success"},
|
|
{ SRB_STATUS_ABORTED, "Aborted Command"},
|
|
{ SRB_STATUS_ABORT_FAILED, "Abort Failed"},
|
|
{ SRB_STATUS_ERROR, "Error Event"},
|
|
{ SRB_STATUS_BUSY, "Device Busy"},
|
|
{ SRB_STATUS_INVALID_REQUEST, "Invalid Request"},
|
|
{ SRB_STATUS_INVALID_PATH_ID, "Invalid Path ID"},
|
|
{ SRB_STATUS_NO_DEVICE, "No Device"},
|
|
{ SRB_STATUS_TIMEOUT, "Timeout"},
|
|
{ SRB_STATUS_SELECTION_TIMEOUT, "Selection Timeout"},
|
|
{ SRB_STATUS_COMMAND_TIMEOUT, "Command Timeout"},
|
|
{ SRB_STATUS_MESSAGE_REJECTED, "Message Rejected"},
|
|
{ SRB_STATUS_BUS_RESET, "Bus Reset"},
|
|
{ SRB_STATUS_PARITY_ERROR, "Parity Error"},
|
|
{ SRB_STATUS_REQUEST_SENSE_FAILED,"Request Sense Failed"},
|
|
{ SRB_STATUS_NO_HBA, "No HBA"},
|
|
{ SRB_STATUS_DATA_OVERRUN, "Data Overrun/Data Underrun"},
|
|
{ SRB_STATUS_UNEXPECTED_BUS_FREE,"Unexpected Bus Free"},
|
|
{ SRB_STATUS_PHASE_SEQUENCE_FAILURE,"Phase Error"},
|
|
{ SRB_STATUS_BAD_SRB_BLOCK_LENGTH,"Bad Srb Block Length"},
|
|
{ SRB_STATUS_REQUEST_FLUSHED, "Request Flushed"},
|
|
{ SRB_STATUS_DELAYED_RETRY, "Delayed Retry"},
|
|
{ SRB_STATUS_INVALID_LUN, "Invalid LUN"},
|
|
{ SRB_STATUS_INVALID_TARGET_ID, "Invalid TARGET ID"},
|
|
{ SRB_STATUS_BAD_FUNCTION, "Bad Function"},
|
|
{ SRB_STATUS_ERROR_RECOVERY, "Error Recovery"},
|
|
{ SRB_STATUS_NOT_STARTED, "Not Started"},
|
|
{ SRB_STATUS_NOT_IN_USE, "Not In Use"},
|
|
{ SRB_STATUS_FORCE_ABORT, "Force Abort"},
|
|
{ SRB_STATUS_DOMAIN_VALIDATION_FAIL,"Domain Validation Failure"},
|
|
{ 0xff, "Unknown Error"}
|
|
};
|
|
|
|
char *aac_get_status_string(u32 status)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(srb_status_info); i++)
|
|
if (srb_status_info[i].status == status)
|
|
return srb_status_info[i].str;
|
|
|
|
return "Bad Status Code";
|
|
}
|
|
|
|
#endif
|