[SCSI] aacraid: Add Power Management support

For firmware that supports the feature(s), add the ability to start or
stop an array using the associated SCSI commands, to automatically
manage the spin-up of an array on new I/O reporting back the
appropriate check conditions and actions in cooperation with the
normal timeout mechanisms and enable the blackout period management in
the Firmware associated with the background spin-down of the arrays
when the Firmware times out and deems the arrays as idle.

Signed-off-by: Mark Salyzyn <aacraid@adaptec.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
This commit is contained in:
Mark Salyzyn 2008-04-30 16:03:42 -04:00 committed by James Bottomley
parent a4576b5da6
commit 655d722cf7
4 changed files with 154 additions and 11 deletions

View File

@ -498,6 +498,11 @@ static void _aac_probe_container2(void * context, struct fib * fibptr)
(le32_to_cpu(dresp->mnt[0].vol) != CT_NONE) && (le32_to_cpu(dresp->mnt[0].vol) != CT_NONE) &&
(le32_to_cpu(dresp->mnt[0].state) != FSCS_HIDDEN)) { (le32_to_cpu(dresp->mnt[0].state) != FSCS_HIDDEN)) {
fsa_dev_ptr->valid = 1; 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->type = le32_to_cpu(dresp->mnt[0].vol);
fsa_dev_ptr->size fsa_dev_ptr->size
= ((u64)le32_to_cpu(dresp->mnt[0].capacity)) + = ((u64)le32_to_cpu(dresp->mnt[0].capacity)) +
@ -1509,20 +1514,35 @@ static void io_callback(void *context, struct fib * fibptr)
scsi_dma_unmap(scsicmd); scsi_dma_unmap(scsicmd);
readreply = (struct aac_read_reply *)fib_data(fibptr); readreply = (struct aac_read_reply *)fib_data(fibptr);
if (le32_to_cpu(readreply->status) == ST_OK) switch (le32_to_cpu(readreply->status)) {
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD; case ST_OK:
else { 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 #ifdef AAC_DETAILED_STATUS_INFO
printk(KERN_WARNING "io_callback: io failed, status = %d\n", printk(KERN_WARNING "io_callback: io failed, status = %d\n",
le32_to_cpu(readreply->status)); le32_to_cpu(readreply->status));
#endif #endif
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION; scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
SAM_STAT_CHECK_CONDITION;
set_sense(&dev->fsa_dev[cid].sense_data, set_sense(&dev->fsa_dev[cid].sense_data,
HARDWARE_ERROR, SENCODE_INTERNAL_TARGET_FAILURE, HARDWARE_ERROR, SENCODE_INTERNAL_TARGET_FAILURE,
ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0); ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0);
memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data, memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data,
min_t(size_t, sizeof(dev->fsa_dev[cid].sense_data), min_t(size_t, sizeof(dev->fsa_dev[cid].sense_data),
SCSI_SENSE_BUFFERSIZE)); SCSI_SENSE_BUFFERSIZE));
break;
} }
aac_fib_complete(fibptr); aac_fib_complete(fibptr);
aac_fib_free(fibptr); aac_fib_free(fibptr);
@ -1863,6 +1883,84 @@ static int aac_synchronize(struct scsi_cmnd *scsicmd)
return SCSI_MLQUEUE_HOST_BUSY; 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 * aac_scsi_cmd() - Process SCSI command
* @scsicmd: SCSI command block * @scsicmd: SCSI command block
@ -1899,7 +1997,9 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
* If the target container doesn't exist, it may have * If the target container doesn't exist, it may have
* been newly created * been newly created
*/ */
if ((fsa_dev_ptr[cid].valid & 1) == 0) { if (((fsa_dev_ptr[cid].valid & 1) == 0) ||
(fsa_dev_ptr[cid].sense_data.sense_key ==
NOT_READY)) {
switch (scsicmd->cmnd[0]) { switch (scsicmd->cmnd[0]) {
case SERVICE_ACTION_IN: case SERVICE_ACTION_IN:
if (!(dev->raw_io_interface) || if (!(dev->raw_io_interface) ||
@ -2091,8 +2191,8 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
scsi_sg_copy_from_buffer(scsicmd, cp, sizeof(cp)); scsi_sg_copy_from_buffer(scsicmd, cp, sizeof(cp));
/* Do not cache partition table for arrays */ /* Do not cache partition table for arrays */
scsicmd->device->removable = 1; scsicmd->device->removable = 1;
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 |
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD; SAM_STAT_GOOD;
scsicmd->scsi_done(scsicmd); scsicmd->scsi_done(scsicmd);
return 0; return 0;
@ -2187,15 +2287,32 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
* These commands are all No-Ops * These commands are all No-Ops
*/ */
case TEST_UNIT_READY: 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 RESERVE:
case RELEASE: case RELEASE:
case REZERO_UNIT: case REZERO_UNIT:
case REASSIGN_BLOCKS: case REASSIGN_BLOCKS:
case SEEK_10: case SEEK_10:
case START_STOP:
scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD; scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD;
scsicmd->scsi_done(scsicmd); scsicmd->scsi_done(scsicmd);
return 0; return 0;
case START_STOP:
return aac_start_stop(scsicmd);
} }
switch (scsicmd->cmnd[0]) switch (scsicmd->cmnd[0])

View File

@ -12,7 +12,7 @@
*----------------------------------------------------------------------------*/ *----------------------------------------------------------------------------*/
#ifndef AAC_DRIVER_BUILD #ifndef AAC_DRIVER_BUILD
# define AAC_DRIVER_BUILD 2455 # define AAC_DRIVER_BUILD 2456
# define AAC_DRIVER_BRANCH "-ms" # define AAC_DRIVER_BRANCH "-ms"
#endif #endif
#define MAXIMUM_NUM_CONTAINERS 32 #define MAXIMUM_NUM_CONTAINERS 32
@ -424,6 +424,8 @@ struct aac_init
*/ */
__le32 InitFlags; /* flags for supported features */ __le32 InitFlags; /* flags for supported features */
#define INITFLAGS_NEW_COMM_SUPPORTED 0x00000001 #define INITFLAGS_NEW_COMM_SUPPORTED 0x00000001
#define INITFLAGS_DRIVER_USES_UTC_TIME 0x00000010
#define INITFLAGS_DRIVER_SUPPORTS_PM 0x00000020
__le32 MaxIoCommands; /* max outstanding commands */ __le32 MaxIoCommands; /* max outstanding commands */
__le32 MaxIoSize; /* largest I/O command */ __le32 MaxIoSize; /* largest I/O command */
__le32 MaxFibSize; /* largest FIB to adapter */ __le32 MaxFibSize; /* largest FIB to adapter */
@ -867,8 +869,10 @@ struct aac_supplement_adapter_info
}; };
#define AAC_FEATURE_FALCON cpu_to_le32(0x00000010) #define AAC_FEATURE_FALCON cpu_to_le32(0x00000010)
#define AAC_FEATURE_JBOD cpu_to_le32(0x08000000) #define AAC_FEATURE_JBOD cpu_to_le32(0x08000000)
#define AAC_OPTION_MU_RESET cpu_to_le32(0x00000001) /* SupportedOptions2 */
#define AAC_OPTION_IGNORE_RESET cpu_to_le32(0x00000002) #define AAC_OPTION_MU_RESET cpu_to_le32(0x00000001)
#define AAC_OPTION_IGNORE_RESET cpu_to_le32(0x00000002)
#define AAC_OPTION_POWER_MANAGEMENT cpu_to_le32(0x00000004)
#define AAC_SIS_VERSION_V3 3 #define AAC_SIS_VERSION_V3 3
#define AAC_SIS_SLOT_UNKNOWN 0xFF #define AAC_SIS_SLOT_UNKNOWN 0xFF
@ -1148,6 +1152,7 @@ struct aac_dev
#define ST_DQUOT 69 #define ST_DQUOT 69
#define ST_STALE 70 #define ST_STALE 70
#define ST_REMOTE 71 #define ST_REMOTE 71
#define ST_NOT_READY 72
#define ST_BADHANDLE 10001 #define ST_BADHANDLE 10001
#define ST_NOT_SYNC 10002 #define ST_NOT_SYNC 10002
#define ST_BAD_COOKIE 10003 #define ST_BAD_COOKIE 10003
@ -1269,6 +1274,18 @@ struct aac_synchronize_reply {
u8 data[16]; u8 data[16];
}; };
#define CT_POWER_MANAGEMENT 245
#define CT_PM_START_UNIT 2
#define CT_PM_STOP_UNIT 3
#define CT_PM_UNIT_IMMEDIATE 1
struct aac_power_management {
__le32 command; /* VM_ContainerConfig */
__le32 type; /* CT_POWER_MANAGEMENT */
__le32 sub; /* CT_PM_* */
__le32 cid;
__le32 parm; /* CT_PM_sub_* */
};
#define CT_PAUSE_IO 65 #define CT_PAUSE_IO 65
#define CT_RELEASE_IO 66 #define CT_RELEASE_IO 66
struct aac_pause { struct aac_pause {
@ -1536,6 +1553,7 @@ struct aac_mntent {
#define FSCS_NOTCLEAN 0x0001 /* fsck is necessary before mounting */ #define FSCS_NOTCLEAN 0x0001 /* fsck is necessary before mounting */
#define FSCS_READONLY 0x0002 /* possible result of broken mirror */ #define FSCS_READONLY 0x0002 /* possible result of broken mirror */
#define FSCS_HIDDEN 0x0004 /* should be ignored - set during a clear */ #define FSCS_HIDDEN 0x0004 /* should be ignored - set during a clear */
#define FSCS_NOT_READY 0x0008 /* Array spinning up to fulfil request */
struct aac_query_mount { struct aac_query_mount {
__le32 command; __le32 command;

View File

@ -97,6 +97,8 @@ static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long co
init->InitFlags = cpu_to_le32(INITFLAGS_NEW_COMM_SUPPORTED); init->InitFlags = cpu_to_le32(INITFLAGS_NEW_COMM_SUPPORTED);
dprintk((KERN_WARNING"aacraid: New Comm Interface enabled\n")); dprintk((KERN_WARNING"aacraid: New Comm Interface enabled\n"));
} }
init->InitFlags |= cpu_to_le32(INITFLAGS_DRIVER_USES_UTC_TIME |
INITFLAGS_DRIVER_SUPPORTS_PM);
init->MaxIoCommands = cpu_to_le32(dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB); init->MaxIoCommands = cpu_to_le32(dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB);
init->MaxIoSize = cpu_to_le32(dev->scsi_host_ptr->max_sectors << 9); init->MaxIoSize = cpu_to_le32(dev->scsi_host_ptr->max_sectors << 9);
init->MaxFibSize = cpu_to_le32(dev->max_fib_size); init->MaxFibSize = cpu_to_le32(dev->max_fib_size);

View File

@ -811,6 +811,12 @@ static ssize_t aac_show_flags(struct device *cdev,
"SAI_READ_CAPACITY_16\n"); "SAI_READ_CAPACITY_16\n");
if (dev->jbod) if (dev->jbod)
len += snprintf(buf + len, PAGE_SIZE - len, "SUPPORTED_JBOD\n"); len += snprintf(buf + len, PAGE_SIZE - len, "SUPPORTED_JBOD\n");
if (dev->supplement_adapter_info.SupportedOptions2 &
AAC_OPTION_POWER_MANAGEMENT)
len += snprintf(buf + len, PAGE_SIZE - len,
"SUPPORTED_POWER_MANAGEMENT\n");
if (dev->msi)
len += snprintf(buf + len, PAGE_SIZE - len, "PCI_HAS_MSI\n");
return len; return len;
} }