提交 e1f7de0c 编写于 作者: M Matt Gates 提交者: James Bottomley

[SCSI] hpsa: add support for 'fastpath' i/o

For certain i/o's to certain devices (unmasked physical disks) we
can bypass the RAID stack firmware and do the i/o to the device
directly and it will be faster.
Signed-off-by: NMatt Gates <matthew.gates@hp.com>
Signed-off-by: NStephen M. Cameron <scameron@beardog.cce.hp.com>
Signed-off-by: NJames Bottomley <JBottomley@Parallels.com>
上级 e1d9cbfa
...@@ -204,7 +204,7 @@ static void check_ioctl_unit_attention(struct ctlr_info *h, ...@@ -204,7 +204,7 @@ static void check_ioctl_unit_attention(struct ctlr_info *h,
struct CommandList *c); struct CommandList *c);
/* performant mode helper functions */ /* performant mode helper functions */
static void calc_bucket_map(int *bucket, int num_buckets, static void calc_bucket_map(int *bucket, int num_buckets,
int nsgs, int *bucket_map); int nsgs, int min_blocks, int *bucket_map);
static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h); static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h);
static inline u32 next_command(struct ctlr_info *h, u8 q); static inline u32 next_command(struct ctlr_info *h, u8 q);
static int hpsa_find_cfg_addrs(struct pci_dev *pdev, void __iomem *vaddr, static int hpsa_find_cfg_addrs(struct pci_dev *pdev, void __iomem *vaddr,
...@@ -570,6 +570,9 @@ static inline u32 next_command(struct ctlr_info *h, u8 q) ...@@ -570,6 +570,9 @@ static inline u32 next_command(struct ctlr_info *h, u8 q)
struct reply_pool *rq = &h->reply_queue[q]; struct reply_pool *rq = &h->reply_queue[q];
unsigned long flags; unsigned long flags;
if (h->transMethod & CFGTBL_Trans_io_accel1)
return h->access.command_completed(h, q);
if (unlikely(!(h->transMethod & CFGTBL_Trans_Performant))) if (unlikely(!(h->transMethod & CFGTBL_Trans_Performant)))
return h->access.command_completed(h, q); return h->access.command_completed(h, q);
...@@ -1203,7 +1206,8 @@ static void complete_scsi_command(struct CommandList *cp) ...@@ -1203,7 +1206,8 @@ static void complete_scsi_command(struct CommandList *cp)
h = cp->h; h = cp->h;
scsi_dma_unmap(cmd); /* undo the DMA mappings */ scsi_dma_unmap(cmd); /* undo the DMA mappings */
if (cp->Header.SGTotal > h->max_cmd_sg_entries) if ((cp->cmd_type == CMD_SCSI) &&
(cp->Header.SGTotal > h->max_cmd_sg_entries))
hpsa_unmap_sg_chain_block(h, cp); hpsa_unmap_sg_chain_block(h, cp);
cmd->result = (DID_OK << 16); /* host byte */ cmd->result = (DID_OK << 16); /* host byte */
...@@ -1227,6 +1231,19 @@ static void complete_scsi_command(struct CommandList *cp) ...@@ -1227,6 +1231,19 @@ static void complete_scsi_command(struct CommandList *cp)
return; return;
} }
/* For I/O accelerator commands, copy over some fields to the normal
* CISS header used below for error handling.
*/
if (cp->cmd_type == CMD_IOACCEL1) {
struct io_accel1_cmd *c = &h->ioaccel_cmd_pool[cp->cmdindex];
cp->Header.SGList = cp->Header.SGTotal = scsi_sg_count(cmd);
cp->Request.CDBLen = c->io_flags & IOACCEL1_IOFLAGS_CDBLEN_MASK;
cp->Header.Tag.lower = c->Tag.lower;
cp->Header.Tag.upper = c->Tag.upper;
memcpy(cp->Header.LUN.LunAddrBytes, c->CISS_LUN, 8);
memcpy(cp->Request.CDB, c->CDB, cp->Request.CDBLen);
}
/* an error has occurred */ /* an error has occurred */
switch (ei->CommandStatus) { switch (ei->CommandStatus) {
...@@ -2070,6 +2087,9 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno) ...@@ -2070,6 +2087,9 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
case TYPE_DISK: case TYPE_DISK:
if (i < nphysicals) if (i < nphysicals)
break; break;
memcpy(&this_device->ioaccel_handle,
&lunaddrbytes[20],
sizeof(this_device->ioaccel_handle));
ncurrent++; ncurrent++;
break; break;
case TYPE_TAPE: case TYPE_TAPE:
...@@ -2164,6 +2184,104 @@ static int hpsa_scatter_gather(struct ctlr_info *h, ...@@ -2164,6 +2184,104 @@ static int hpsa_scatter_gather(struct ctlr_info *h,
return 0; return 0;
} }
/*
* Queue a command to the I/O accelerator path.
* This method does not currently support S/G chaining.
*/
static int hpsa_scsi_ioaccel_queue_command(struct ctlr_info *h,
struct CommandList *c)
{
struct scsi_cmnd *cmd = c->scsi_cmd;
struct hpsa_scsi_dev_t *dev = cmd->device->hostdata;
struct io_accel1_cmd *cp = &h->ioaccel_cmd_pool[c->cmdindex];
unsigned int len;
unsigned int total_len = 0;
struct scatterlist *sg;
u64 addr64;
int use_sg, i;
struct SGDescriptor *curr_sg;
u32 control = IOACCEL1_CONTROL_SIMPLEQUEUE;
BUG_ON(cmd->cmd_len > IOACCEL1_IOFLAGS_CDBLEN_MAX);
c->cmd_type = CMD_IOACCEL1;
/* Adjust the DMA address to point to the accelerated command buffer */
c->busaddr = (u32) h->ioaccel_cmd_pool_dhandle +
(c->cmdindex * sizeof(*cp));
BUG_ON(c->busaddr & 0x0000007F);
use_sg = scsi_dma_map(cmd);
if (use_sg < 0)
return use_sg;
if (use_sg) {
curr_sg = cp->SG;
scsi_for_each_sg(cmd, sg, use_sg, i) {
addr64 = (u64) sg_dma_address(sg);
len = sg_dma_len(sg);
total_len += len;
curr_sg->Addr.lower = (u32) (addr64 & 0x0FFFFFFFFULL);
curr_sg->Addr.upper =
(u32) ((addr64 >> 32) & 0x0FFFFFFFFULL);
curr_sg->Len = len;
if (i == (scsi_sg_count(cmd) - 1))
curr_sg->Ext = HPSA_SG_LAST;
else
curr_sg->Ext = 0; /* we are not chaining */
curr_sg++;
}
switch (cmd->sc_data_direction) {
case DMA_TO_DEVICE:
control |= IOACCEL1_CONTROL_DATA_OUT;
break;
case DMA_FROM_DEVICE:
control |= IOACCEL1_CONTROL_DATA_IN;
break;
case DMA_NONE:
control |= IOACCEL1_CONTROL_NODATAXFER;
break;
default:
dev_err(&h->pdev->dev, "unknown data direction: %d\n",
cmd->sc_data_direction);
BUG();
break;
}
} else {
control |= IOACCEL1_CONTROL_NODATAXFER;
}
/* Fill out the command structure to submit */
cp->dev_handle = dev->ioaccel_handle;
cp->transfer_len = total_len;
cp->io_flags = IOACCEL1_IOFLAGS_IO_REQ |
(cmd->cmd_len & IOACCEL1_IOFLAGS_CDBLEN_MASK);
cp->control = control;
memcpy(cp->CDB, cmd->cmnd, cmd->cmd_len);
memcpy(cp->CISS_LUN, dev->scsi3addr, 8);
/* Tell the controller to post the reply to the queue for this
* processor. This seems to give the best I/O throughput.
*/
cp->ReplyQueue = smp_processor_id() % h->nreply_queues;
/* Set the bits in the address sent down to include:
* - performant mode bit (bit 0)
* - pull count (bits 1-3)
* - command type (bits 4-6)
*/
c->busaddr |= 1 | (h->ioaccel1_blockFetchTable[use_sg] << 1) |
IOACCEL1_BUSADDR_CMDTYPE;
/* execute command (bypassing cmd queue if possible) */
if (unlikely(h->access.fifo_full(h)))
enqueue_cmd_and_start_io(h, c);
else
h->access.submit_command(h, c);
return 0;
}
static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd, static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd,
void (*done)(struct scsi_cmnd *)) void (*done)(struct scsi_cmnd *))
...@@ -2207,6 +2325,14 @@ static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd, ...@@ -2207,6 +2325,14 @@ static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd,
c->cmd_type = CMD_SCSI; c->cmd_type = CMD_SCSI;
c->scsi_cmd = cmd; c->scsi_cmd = cmd;
/* Call alternate submit routine for I/O accelerated commands */
if ((likely(h->transMethod & CFGTBL_Trans_io_accel1)) &&
(dev->ioaccel_handle) &&
((cmd->cmnd[0] == READ_10) || (cmd->cmnd[0] == WRITE_10)) &&
(scsi_sg_count(cmd) <= IOACCEL1_MAXSGENTRIES))
return hpsa_scsi_ioaccel_queue_command(h, c);
c->Header.ReplyQueue = 0; /* unused in simple mode */ c->Header.ReplyQueue = 0; /* unused in simple mode */
memcpy(&c->Header.LUN.LunAddrBytes[0], &scsi3addr[0], 8); memcpy(&c->Header.LUN.LunAddrBytes[0], &scsi3addr[0], 8);
c->Header.Tag.lower = (c->cmdindex << DIRECT_LOOKUP_SHIFT); c->Header.Tag.lower = (c->cmdindex << DIRECT_LOOKUP_SHIFT);
...@@ -2780,6 +2906,7 @@ static struct CommandList *cmd_special_alloc(struct ctlr_info *h) ...@@ -2780,6 +2906,7 @@ static struct CommandList *cmd_special_alloc(struct ctlr_info *h)
return NULL; return NULL;
memset(c, 0, sizeof(*c)); memset(c, 0, sizeof(*c));
c->cmd_type = CMD_SCSI;
c->cmdindex = -1; c->cmdindex = -1;
c->err_info = pci_alloc_consistent(h->pdev, sizeof(*c->err_info), c->err_info = pci_alloc_consistent(h->pdev, sizeof(*c->err_info),
...@@ -3565,7 +3692,7 @@ static inline void finish_cmd(struct CommandList *c) ...@@ -3565,7 +3692,7 @@ static inline void finish_cmd(struct CommandList *c)
spin_unlock_irqrestore(&h->lock, flags); spin_unlock_irqrestore(&h->lock, flags);
dial_up_lockup_detection_on_fw_flash_complete(c->h, c); dial_up_lockup_detection_on_fw_flash_complete(c->h, c);
if (likely(c->cmd_type == CMD_SCSI)) if (likely(c->cmd_type == CMD_IOACCEL1 || c->cmd_type == CMD_SCSI))
complete_scsi_command(c); complete_scsi_command(c);
else if (c->cmd_type == CMD_IOCTL_PEND) else if (c->cmd_type == CMD_IOCTL_PEND)
complete(c->waiting); complete(c->waiting);
...@@ -4588,6 +4715,10 @@ static void hpsa_free_cmd_pool(struct ctlr_info *h) ...@@ -4588,6 +4715,10 @@ static void hpsa_free_cmd_pool(struct ctlr_info *h)
h->nr_cmds * sizeof(struct ErrorInfo), h->nr_cmds * sizeof(struct ErrorInfo),
h->errinfo_pool, h->errinfo_pool,
h->errinfo_pool_dhandle); h->errinfo_pool_dhandle);
if (h->ioaccel_cmd_pool)
pci_free_consistent(h->pdev,
h->nr_cmds * sizeof(struct io_accel1_cmd),
h->ioaccel_cmd_pool, h->ioaccel_cmd_pool_dhandle);
} }
static int hpsa_request_irq(struct ctlr_info *h, static int hpsa_request_irq(struct ctlr_info *h,
...@@ -4687,6 +4818,7 @@ static void hpsa_undo_allocations_after_kdump_soft_reset(struct ctlr_info *h) ...@@ -4687,6 +4818,7 @@ static void hpsa_undo_allocations_after_kdump_soft_reset(struct ctlr_info *h)
hpsa_free_irqs_and_disable_msix(h); hpsa_free_irqs_and_disable_msix(h);
hpsa_free_sg_chain_blocks(h); hpsa_free_sg_chain_blocks(h);
hpsa_free_cmd_pool(h); hpsa_free_cmd_pool(h);
kfree(h->ioaccel1_blockFetchTable);
kfree(h->blockFetchTable); kfree(h->blockFetchTable);
pci_free_consistent(h->pdev, h->reply_pool_size, pci_free_consistent(h->pdev, h->reply_pool_size,
h->reply_pool, h->reply_pool_dhandle); h->reply_pool, h->reply_pool_dhandle);
...@@ -5040,6 +5172,7 @@ static void hpsa_remove_one(struct pci_dev *pdev) ...@@ -5040,6 +5172,7 @@ static void hpsa_remove_one(struct pci_dev *pdev)
h->reply_pool, h->reply_pool_dhandle); h->reply_pool, h->reply_pool_dhandle);
kfree(h->cmd_pool_bits); kfree(h->cmd_pool_bits);
kfree(h->blockFetchTable); kfree(h->blockFetchTable);
kfree(h->ioaccel1_blockFetchTable);
kfree(h->hba_inquiry_data); kfree(h->hba_inquiry_data);
pci_disable_device(pdev); pci_disable_device(pdev);
pci_release_regions(pdev); pci_release_regions(pdev);
...@@ -5080,20 +5213,17 @@ static struct pci_driver hpsa_pci_driver = { ...@@ -5080,20 +5213,17 @@ static struct pci_driver hpsa_pci_driver = {
* bits of the command address. * bits of the command address.
*/ */
static void calc_bucket_map(int bucket[], int num_buckets, static void calc_bucket_map(int bucket[], int num_buckets,
int nsgs, int *bucket_map) int nsgs, int min_blocks, int *bucket_map)
{ {
int i, j, b, size; int i, j, b, size;
/* even a command with 0 SGs requires 4 blocks */
#define MINIMUM_TRANSFER_BLOCKS 4
#define NUM_BUCKETS 8
/* Note, bucket_map must have nsgs+1 entries. */ /* Note, bucket_map must have nsgs+1 entries. */
for (i = 0; i <= nsgs; i++) { for (i = 0; i <= nsgs; i++) {
/* Compute size of a command with i SG entries */ /* Compute size of a command with i SG entries */
size = i + MINIMUM_TRANSFER_BLOCKS; size = i + min_blocks;
b = num_buckets; /* Assume the biggest bucket */ b = num_buckets; /* Assume the biggest bucket */
/* Find the bucket that is just big enough */ /* Find the bucket that is just big enough */
for (j = 0; j < 8; j++) { for (j = 0; j < num_buckets; j++) {
if (bucket[j] >= size) { if (bucket[j] >= size) {
b = j; b = j;
break; break;
...@@ -5104,10 +5234,16 @@ static void calc_bucket_map(int bucket[], int num_buckets, ...@@ -5104,10 +5234,16 @@ static void calc_bucket_map(int bucket[], int num_buckets,
} }
} }
static void hpsa_enter_performant_mode(struct ctlr_info *h, u32 use_short_tags) static void hpsa_enter_performant_mode(struct ctlr_info *h, u32 trans_support)
{ {
int i; int i;
unsigned long register_value; unsigned long register_value;
unsigned long transMethod = CFGTBL_Trans_Performant |
(trans_support & CFGTBL_Trans_use_short_tags) |
CFGTBL_Trans_enable_directed_msix |
(trans_support & CFGTBL_Trans_io_accel1);
struct access_method access = SA5_performant_access;
/* This is a bit complicated. There are 8 registers on /* This is a bit complicated. There are 8 registers on
* the controller which we write to to tell it 8 different * the controller which we write to to tell it 8 different
...@@ -5139,7 +5275,7 @@ static void hpsa_enter_performant_mode(struct ctlr_info *h, u32 use_short_tags) ...@@ -5139,7 +5275,7 @@ static void hpsa_enter_performant_mode(struct ctlr_info *h, u32 use_short_tags)
bft[7] = SG_ENTRIES_IN_CMD + 4; bft[7] = SG_ENTRIES_IN_CMD + 4;
calc_bucket_map(bft, ARRAY_SIZE(bft), calc_bucket_map(bft, ARRAY_SIZE(bft),
SG_ENTRIES_IN_CMD, h->blockFetchTable); SG_ENTRIES_IN_CMD, 4, h->blockFetchTable);
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
writel(bft[i], &h->transtable->BlockFetch[i]); writel(bft[i], &h->transtable->BlockFetch[i]);
...@@ -5156,9 +5292,15 @@ static void hpsa_enter_performant_mode(struct ctlr_info *h, u32 use_short_tags) ...@@ -5156,9 +5292,15 @@ static void hpsa_enter_performant_mode(struct ctlr_info *h, u32 use_short_tags)
&h->transtable->RepQAddr[i].lower); &h->transtable->RepQAddr[i].lower);
} }
writel(CFGTBL_Trans_Performant | use_short_tags | writel(transMethod, &(h->cfgtable->HostWrite.TransportRequest));
CFGTBL_Trans_enable_directed_msix, /*
&(h->cfgtable->HostWrite.TransportRequest)); * enable outbound interrupt coalescing in accelerator mode;
*/
if (trans_support & CFGTBL_Trans_io_accel1) {
access = SA5_ioaccel_mode1_access;
writel(10, &h->cfgtable->HostWrite.CoalIntDelay);
writel(4, &h->cfgtable->HostWrite.CoalIntCount);
}
writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL); writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL);
hpsa_wait_for_mode_change_ack(h); hpsa_wait_for_mode_change_ack(h);
register_value = readl(&(h->cfgtable->TransportActive)); register_value = readl(&(h->cfgtable->TransportActive));
...@@ -5168,18 +5310,102 @@ static void hpsa_enter_performant_mode(struct ctlr_info *h, u32 use_short_tags) ...@@ -5168,18 +5310,102 @@ static void hpsa_enter_performant_mode(struct ctlr_info *h, u32 use_short_tags)
return; return;
} }
/* Change the access methods to the performant access methods */ /* Change the access methods to the performant access methods */
h->access = SA5_performant_access; h->access = access;
h->transMethod = CFGTBL_Trans_Performant; h->transMethod = transMethod;
if (!(trans_support & CFGTBL_Trans_io_accel1))
return;
/* Set up I/O accelerator mode */
for (i = 0; i < h->nreply_queues; i++) {
writel(i, h->vaddr + IOACCEL_MODE1_REPLY_QUEUE_INDEX);
h->reply_queue[i].current_entry =
readl(h->vaddr + IOACCEL_MODE1_PRODUCER_INDEX);
}
bft[7] = IOACCEL1_MAXSGENTRIES + 8;
calc_bucket_map(bft, ARRAY_SIZE(bft), IOACCEL1_MAXSGENTRIES, 8,
h->ioaccel1_blockFetchTable);
/* initialize all reply queue entries to unused */
memset(h->reply_pool, (u8) IOACCEL_MODE1_REPLY_UNUSED,
h->reply_pool_size);
/* set all the constant fields in the accelerator command
* frames once at init time to save CPU cycles later.
*/
for (i = 0; i < h->nr_cmds; i++) {
struct io_accel1_cmd *cp = &h->ioaccel_cmd_pool[i];
cp->function = IOACCEL1_FUNCTION_SCSIIO;
cp->err_info = (u32) (h->errinfo_pool_dhandle +
(i * sizeof(struct ErrorInfo)));
cp->err_info_len = sizeof(struct ErrorInfo);
cp->sgl_offset = IOACCEL1_SGLOFFSET;
cp->host_context_flags = IOACCEL1_HCFLAGS_CISS_FORMAT;
cp->timeout_sec = 0;
cp->ReplyQueue = 0;
cp->Tag.lower = (i << DIRECT_LOOKUP_SHIFT) | DIRECT_LOOKUP_BIT;
cp->Tag.upper = 0;
cp->host_addr.lower = (u32) (h->ioaccel_cmd_pool_dhandle +
(i * sizeof(struct io_accel1_cmd)));
cp->host_addr.upper = 0;
}
}
static int hpsa_alloc_ioaccel_cmd_and_bft(struct ctlr_info *h)
{
/* Command structures must be aligned on a 128-byte boundary
* because the 7 lower bits of the address are used by the
* hardware.
*/
#define IOACCEL1_COMMANDLIST_ALIGNMENT 128
BUILD_BUG_ON(sizeof(struct io_accel1_cmd) %
IOACCEL1_COMMANDLIST_ALIGNMENT);
h->ioaccel_cmd_pool =
pci_alloc_consistent(h->pdev,
h->nr_cmds * sizeof(*h->ioaccel_cmd_pool),
&(h->ioaccel_cmd_pool_dhandle));
h->ioaccel1_blockFetchTable =
kmalloc(((IOACCEL1_MAXSGENTRIES + 1) *
sizeof(u32)), GFP_KERNEL);
if ((h->ioaccel_cmd_pool == NULL) ||
(h->ioaccel1_blockFetchTable == NULL))
goto clean_up;
memset(h->ioaccel_cmd_pool, 0,
h->nr_cmds * sizeof(*h->ioaccel_cmd_pool));
return 0;
clean_up:
if (h->ioaccel_cmd_pool)
pci_free_consistent(h->pdev,
h->nr_cmds * sizeof(*h->ioaccel_cmd_pool),
h->ioaccel_cmd_pool, h->ioaccel_cmd_pool_dhandle);
kfree(h->ioaccel1_blockFetchTable);
return 1;
} }
static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h) static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
{ {
u32 trans_support; u32 trans_support;
unsigned long transMethod = CFGTBL_Trans_Performant |
CFGTBL_Trans_use_short_tags;
int i; int i;
if (hpsa_simple_mode) if (hpsa_simple_mode)
return; return;
/* Check for I/O accelerator mode support */
if (trans_support & CFGTBL_Trans_io_accel1) {
transMethod |= CFGTBL_Trans_io_accel1 |
CFGTBL_Trans_enable_directed_msix;
if (hpsa_alloc_ioaccel_cmd_and_bft(h))
goto clean_up;
}
/* TODO, check that this next line h->nreply_queues is correct */
trans_support = readl(&(h->cfgtable->TransportSupport)); trans_support = readl(&(h->cfgtable->TransportSupport));
if (!(trans_support & PERFORMANT_MODE)) if (!(trans_support & PERFORMANT_MODE))
return; return;
...@@ -5206,9 +5432,7 @@ static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h) ...@@ -5206,9 +5432,7 @@ static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
|| (h->blockFetchTable == NULL)) || (h->blockFetchTable == NULL))
goto clean_up; goto clean_up;
hpsa_enter_performant_mode(h, hpsa_enter_performant_mode(h, trans_support);
trans_support & CFGTBL_Trans_use_short_tags);
return; return;
clean_up: clean_up:
...@@ -5232,5 +5456,39 @@ static void __exit hpsa_cleanup(void) ...@@ -5232,5 +5456,39 @@ static void __exit hpsa_cleanup(void)
pci_unregister_driver(&hpsa_pci_driver); pci_unregister_driver(&hpsa_pci_driver);
} }
static void __attribute__((unused)) verify_offsets(void)
{
#define VERIFY_OFFSET(member, offset) \
BUILD_BUG_ON(offsetof(struct io_accel1_cmd, member) != offset)
VERIFY_OFFSET(dev_handle, 0x00);
VERIFY_OFFSET(reserved1, 0x02);
VERIFY_OFFSET(function, 0x03);
VERIFY_OFFSET(reserved2, 0x04);
VERIFY_OFFSET(err_info, 0x0C);
VERIFY_OFFSET(reserved3, 0x10);
VERIFY_OFFSET(err_info_len, 0x12);
VERIFY_OFFSET(reserved4, 0x13);
VERIFY_OFFSET(sgl_offset, 0x14);
VERIFY_OFFSET(reserved5, 0x15);
VERIFY_OFFSET(transfer_len, 0x1C);
VERIFY_OFFSET(reserved6, 0x20);
VERIFY_OFFSET(io_flags, 0x24);
VERIFY_OFFSET(reserved7, 0x26);
VERIFY_OFFSET(LUN, 0x34);
VERIFY_OFFSET(control, 0x3C);
VERIFY_OFFSET(CDB, 0x40);
VERIFY_OFFSET(reserved8, 0x50);
VERIFY_OFFSET(host_context_flags, 0x60);
VERIFY_OFFSET(timeout_sec, 0x62);
VERIFY_OFFSET(ReplyQueue, 0x64);
VERIFY_OFFSET(reserved9, 0x65);
VERIFY_OFFSET(Tag, 0x68);
VERIFY_OFFSET(host_addr, 0x70);
VERIFY_OFFSET(CISS_LUN, 0x78);
VERIFY_OFFSET(SG, 0x78 + 8);
#undef VERIFY_OFFSET
}
module_init(hpsa_init); module_init(hpsa_init);
module_exit(hpsa_cleanup); module_exit(hpsa_cleanup);
...@@ -46,6 +46,7 @@ struct hpsa_scsi_dev_t { ...@@ -46,6 +46,7 @@ struct hpsa_scsi_dev_t {
unsigned char vendor[8]; /* bytes 8-15 of inquiry data */ unsigned char vendor[8]; /* bytes 8-15 of inquiry data */
unsigned char model[16]; /* bytes 16-31 of inquiry data */ unsigned char model[16]; /* bytes 16-31 of inquiry data */
unsigned char raid_level; /* from inquiry page 0xC1 */ unsigned char raid_level; /* from inquiry page 0xC1 */
u32 ioaccel_handle;
}; };
struct reply_pool { struct reply_pool {
...@@ -95,6 +96,8 @@ struct ctlr_info { ...@@ -95,6 +96,8 @@ struct ctlr_info {
/* pointers to command and error info pool */ /* pointers to command and error info pool */
struct CommandList *cmd_pool; struct CommandList *cmd_pool;
dma_addr_t cmd_pool_dhandle; dma_addr_t cmd_pool_dhandle;
struct io_accel1_cmd *ioaccel_cmd_pool;
dma_addr_t ioaccel_cmd_pool_dhandle;
struct ErrorInfo *errinfo_pool; struct ErrorInfo *errinfo_pool;
dma_addr_t errinfo_pool_dhandle; dma_addr_t errinfo_pool_dhandle;
unsigned long *cmd_pool_bits; unsigned long *cmd_pool_bits;
...@@ -128,6 +131,7 @@ struct ctlr_info { ...@@ -128,6 +131,7 @@ struct ctlr_info {
u8 nreply_queues; u8 nreply_queues;
dma_addr_t reply_pool_dhandle; dma_addr_t reply_pool_dhandle;
u32 *blockFetchTable; u32 *blockFetchTable;
u32 *ioaccel1_blockFetchTable;
unsigned char *hba_inquiry_data; unsigned char *hba_inquiry_data;
u64 last_intr_timestamp; u64 last_intr_timestamp;
u32 last_heartbeat; u32 last_heartbeat;
...@@ -387,6 +391,45 @@ static bool SA5_performant_intr_pending(struct ctlr_info *h) ...@@ -387,6 +391,45 @@ static bool SA5_performant_intr_pending(struct ctlr_info *h)
return register_value & SA5_OUTDB_STATUS_PERF_BIT; return register_value & SA5_OUTDB_STATUS_PERF_BIT;
} }
#define SA5_IOACCEL_MODE1_INTR_STATUS_CMP_BIT 0x100
static bool SA5_ioaccel_mode1_intr_pending(struct ctlr_info *h)
{
unsigned long register_value = readl(h->vaddr + SA5_INTR_STATUS);
return (register_value & SA5_IOACCEL_MODE1_INTR_STATUS_CMP_BIT) ?
true : false;
}
#define IOACCEL_MODE1_REPLY_QUEUE_INDEX 0x1A0
#define IOACCEL_MODE1_PRODUCER_INDEX 0x1B8
#define IOACCEL_MODE1_CONSUMER_INDEX 0x1BC
#define IOACCEL_MODE1_REPLY_UNUSED 0xFFFFFFFFFFFFFFFFULL
static unsigned long SA5_ioaccel_mode1_completed(struct ctlr_info *h,
u8 q)
{
u64 register_value;
struct reply_pool *rq = &h->reply_queue[q];
unsigned long flags;
BUG_ON(q >= h->nreply_queues);
register_value = rq->head[rq->current_entry];
if (register_value != IOACCEL_MODE1_REPLY_UNUSED) {
rq->head[rq->current_entry] = IOACCEL_MODE1_REPLY_UNUSED;
if (++rq->current_entry == rq->size)
rq->current_entry = 0;
spin_lock_irqsave(&h->lock, flags);
h->commands_outstanding--;
spin_unlock_irqrestore(&h->lock, flags);
} else {
writel((q << 24) | rq->current_entry,
h->vaddr + IOACCEL_MODE1_CONSUMER_INDEX);
}
return (unsigned long) register_value;
}
static struct access_method SA5_access = { static struct access_method SA5_access = {
SA5_submit_command, SA5_submit_command,
SA5_intr_mask, SA5_intr_mask,
...@@ -395,6 +438,14 @@ static struct access_method SA5_access = { ...@@ -395,6 +438,14 @@ static struct access_method SA5_access = {
SA5_completed, SA5_completed,
}; };
static struct access_method SA5_ioaccel_mode1_access = {
SA5_submit_command,
SA5_performant_intr_mask,
SA5_fifo_full,
SA5_ioaccel_mode1_intr_pending,
SA5_ioaccel_mode1_completed,
};
static struct access_method SA5_performant_access = { static struct access_method SA5_performant_access = {
SA5_submit_command, SA5_submit_command,
SA5_performant_intr_mask, SA5_performant_intr_mask,
......
...@@ -129,6 +129,7 @@ ...@@ -129,6 +129,7 @@
#define CFGTBL_Trans_Simple 0x00000002l #define CFGTBL_Trans_Simple 0x00000002l
#define CFGTBL_Trans_Performant 0x00000004l #define CFGTBL_Trans_Performant 0x00000004l
#define CFGTBL_Trans_io_accel1 0x00000080l
#define CFGTBL_Trans_use_short_tags 0x20000000l #define CFGTBL_Trans_use_short_tags 0x20000000l
#define CFGTBL_Trans_enable_directed_msix (1 << 30) #define CFGTBL_Trans_enable_directed_msix (1 << 30)
...@@ -285,6 +286,7 @@ struct ErrorInfo { ...@@ -285,6 +286,7 @@ struct ErrorInfo {
/* Command types */ /* Command types */
#define CMD_IOCTL_PEND 0x01 #define CMD_IOCTL_PEND 0x01
#define CMD_SCSI 0x03 #define CMD_SCSI 0x03
#define CMD_IOACCEL1 0x04
#define DIRECT_LOOKUP_SHIFT 5 #define DIRECT_LOOKUP_SHIFT 5
#define DIRECT_LOOKUP_BIT 0x10 #define DIRECT_LOOKUP_BIT 0x10
...@@ -335,6 +337,63 @@ struct CommandList { ...@@ -335,6 +337,63 @@ struct CommandList {
u8 pad[COMMANDLIST_PAD]; u8 pad[COMMANDLIST_PAD];
}; };
/* Max S/G elements in I/O accelerator command */
#define IOACCEL1_MAXSGENTRIES 24
/*
* Structure for I/O accelerator (mode 1) commands.
* Note that this structure must be 128-byte aligned in size.
*/
struct io_accel1_cmd {
u16 dev_handle; /* 0x00 - 0x01 */
u8 reserved1; /* 0x02 */
u8 function; /* 0x03 */
u8 reserved2[8]; /* 0x04 - 0x0B */
u32 err_info; /* 0x0C - 0x0F */
u8 reserved3[2]; /* 0x10 - 0x11 */
u8 err_info_len; /* 0x12 */
u8 reserved4; /* 0x13 */
u8 sgl_offset; /* 0x14 */
u8 reserved5[7]; /* 0x15 - 0x1B */
u32 transfer_len; /* 0x1C - 0x1F */
u8 reserved6[4]; /* 0x20 - 0x23 */
u16 io_flags; /* 0x24 - 0x25 */
u8 reserved7[14]; /* 0x26 - 0x33 */
u8 LUN[8]; /* 0x34 - 0x3B */
u32 control; /* 0x3C - 0x3F */
u8 CDB[16]; /* 0x40 - 0x4F */
u8 reserved8[16]; /* 0x50 - 0x5F */
u16 host_context_flags; /* 0x60 - 0x61 */
u16 timeout_sec; /* 0x62 - 0x63 */
u8 ReplyQueue; /* 0x64 */
u8 reserved9[3]; /* 0x65 - 0x67 */
struct vals32 Tag; /* 0x68 - 0x6F */
struct vals32 host_addr; /* 0x70 - 0x77 */
u8 CISS_LUN[8]; /* 0x78 - 0x7F */
struct SGDescriptor SG[IOACCEL1_MAXSGENTRIES];
};
#define IOACCEL1_FUNCTION_SCSIIO 0x00
#define IOACCEL1_SGLOFFSET 32
#define IOACCEL1_IOFLAGS_IO_REQ 0x4000
#define IOACCEL1_IOFLAGS_CDBLEN_MASK 0x001F
#define IOACCEL1_IOFLAGS_CDBLEN_MAX 16
#define IOACCEL1_CONTROL_NODATAXFER 0x00000000
#define IOACCEL1_CONTROL_DATA_OUT 0x01000000
#define IOACCEL1_CONTROL_DATA_IN 0x02000000
#define IOACCEL1_CONTROL_TASKPRIO_MASK 0x00007800
#define IOACCEL1_CONTROL_TASKPRIO_SHIFT 11
#define IOACCEL1_CONTROL_SIMPLEQUEUE 0x00000000
#define IOACCEL1_CONTROL_HEADOFQUEUE 0x00000100
#define IOACCEL1_CONTROL_ORDEREDQUEUE 0x00000200
#define IOACCEL1_CONTROL_ACA 0x00000400
#define IOACCEL1_HCFLAGS_CISS_FORMAT 0x0013
#define IOACCEL1_BUSADDR_CMDTYPE 0x00000060
/* Configuration Table Structure */ /* Configuration Table Structure */
struct HostWrite { struct HostWrite {
u32 TransportRequest; u32 TransportRequest;
...@@ -346,6 +405,7 @@ struct HostWrite { ...@@ -346,6 +405,7 @@ struct HostWrite {
#define SIMPLE_MODE 0x02 #define SIMPLE_MODE 0x02
#define PERFORMANT_MODE 0x04 #define PERFORMANT_MODE 0x04
#define MEMQ_MODE 0x08 #define MEMQ_MODE 0x08
#define IOACCEL_MODE_1 0x80
struct CfgTable { struct CfgTable {
u8 Signature[4]; u8 Signature[4];
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册