cciss: Add enhanced scatter-gather support.

cciss: Add enhanced scatter-gather support.  For controllers which
supported, more than 512 scatter-gather elements per command may
be used, and the max transfer size can be increased to 8192 blocks.

Signed-off-by: Don Brace <brace@beardog.cce.hp.com>
Signed-off-by: Stephen M. Cameron <scameron@beardog.cce.hp.com>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
This commit is contained in:
Don Brace 2009-11-12 12:50:01 -06:00 committed by Jens Axboe
parent da0021841c
commit 5c07a311a8
3 changed files with 188 additions and 20 deletions

View File

@ -1655,9 +1655,11 @@ static void cciss_softirq_done(struct request *rq)
{ {
CommandList_struct *cmd = rq->completion_data; CommandList_struct *cmd = rq->completion_data;
ctlr_info_t *h = hba[cmd->ctlr]; ctlr_info_t *h = hba[cmd->ctlr];
SGDescriptor_struct *curr_sg = cmd->SG;
unsigned long flags; unsigned long flags;
u64bit temp64; u64bit temp64;
int i, ddir; int i, ddir;
int sg_index = 0;
if (cmd->Request.Type.Direction == XFER_READ) if (cmd->Request.Type.Direction == XFER_READ)
ddir = PCI_DMA_FROMDEVICE; ddir = PCI_DMA_FROMDEVICE;
@ -1667,9 +1669,22 @@ static void cciss_softirq_done(struct request *rq)
/* command did not need to be retried */ /* command did not need to be retried */
/* unmap the DMA mapping for all the scatter gather elements */ /* unmap the DMA mapping for all the scatter gather elements */
for (i = 0; i < cmd->Header.SGList; i++) { for (i = 0; i < cmd->Header.SGList; i++) {
temp64.val32.lower = cmd->SG[i].Addr.lower; if (curr_sg[sg_index].Ext == CCISS_SG_CHAIN) {
temp64.val32.upper = cmd->SG[i].Addr.upper; temp64.val32.lower = cmd->SG[i].Addr.lower;
pci_unmap_page(h->pdev, temp64.val, cmd->SG[i].Len, ddir); temp64.val32.upper = cmd->SG[i].Addr.upper;
pci_dma_sync_single_for_cpu(h->pdev, temp64.val,
cmd->SG[i].Len, ddir);
pci_unmap_single(h->pdev, temp64.val,
cmd->SG[i].Len, ddir);
/* Point to the next block */
curr_sg = h->cmd_sg_list[cmd->cmdindex]->sgchain;
sg_index = 0;
}
temp64.val32.lower = curr_sg[sg_index].Addr.lower;
temp64.val32.upper = curr_sg[sg_index].Addr.upper;
pci_unmap_page(h->pdev, temp64.val, curr_sg[sg_index].Len,
ddir);
++sg_index;
} }
#ifdef CCISS_DEBUG #ifdef CCISS_DEBUG
@ -1781,10 +1796,10 @@ static int cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
blk_queue_bounce_limit(disk->queue, h->pdev->dma_mask); blk_queue_bounce_limit(disk->queue, h->pdev->dma_mask);
/* This is a hardware imposed limit. */ /* This is a hardware imposed limit. */
blk_queue_max_hw_segments(disk->queue, MAXSGENTRIES); blk_queue_max_hw_segments(disk->queue, h->maxsgentries);
/* This is a limit in the driver and could be eliminated. */ /* This is a limit in the driver and could be eliminated. */
blk_queue_max_phys_segments(disk->queue, MAXSGENTRIES); blk_queue_max_phys_segments(disk->queue, h->maxsgentries);
blk_queue_max_sectors(disk->queue, h->cciss_max_sectors); blk_queue_max_sectors(disk->queue, h->cciss_max_sectors);
@ -3063,9 +3078,13 @@ static void do_cciss_request(struct request_queue *q)
int seg; int seg;
struct request *creq; struct request *creq;
u64bit temp64; u64bit temp64;
struct scatterlist tmp_sg[MAXSGENTRIES]; struct scatterlist *tmp_sg;
SGDescriptor_struct *curr_sg;
drive_info_struct *drv; drive_info_struct *drv;
int i, dir; int i, dir;
int nseg = 0;
int sg_index = 0;
int chained = 0;
/* We call start_io here in case there is a command waiting on the /* We call start_io here in case there is a command waiting on the
* queue that has not been sent. * queue that has not been sent.
@ -3078,13 +3097,14 @@ static void do_cciss_request(struct request_queue *q)
if (!creq) if (!creq)
goto startio; goto startio;
BUG_ON(creq->nr_phys_segments > MAXSGENTRIES); BUG_ON(creq->nr_phys_segments > h->maxsgentries);
if ((c = cmd_alloc(h, 1)) == NULL) if ((c = cmd_alloc(h, 1)) == NULL)
goto full; goto full;
blk_start_request(creq); blk_start_request(creq);
tmp_sg = h->scatter_list[c->cmdindex];
spin_unlock_irq(q->queue_lock); spin_unlock_irq(q->queue_lock);
c->cmd_type = CMD_RWREQ; c->cmd_type = CMD_RWREQ;
@ -3113,7 +3133,7 @@ static void do_cciss_request(struct request_queue *q)
(int)blk_rq_pos(creq), (int)blk_rq_sectors(creq)); (int)blk_rq_pos(creq), (int)blk_rq_sectors(creq));
#endif /* CCISS_DEBUG */ #endif /* CCISS_DEBUG */
sg_init_table(tmp_sg, MAXSGENTRIES); sg_init_table(tmp_sg, h->maxsgentries);
seg = blk_rq_map_sg(q, creq, tmp_sg); seg = blk_rq_map_sg(q, creq, tmp_sg);
/* get the DMA records for the setup */ /* get the DMA records for the setup */
@ -3122,25 +3142,70 @@ static void do_cciss_request(struct request_queue *q)
else else
dir = PCI_DMA_TODEVICE; dir = PCI_DMA_TODEVICE;
curr_sg = c->SG;
sg_index = 0;
chained = 0;
for (i = 0; i < seg; i++) { for (i = 0; i < seg; i++) {
c->SG[i].Len = tmp_sg[i].length; if (((sg_index+1) == (h->max_cmd_sgentries)) &&
!chained && ((seg - i) > 1)) {
nseg = seg - i;
curr_sg[sg_index].Len = (nseg) *
sizeof(SGDescriptor_struct);
curr_sg[sg_index].Ext = CCISS_SG_CHAIN;
/* Point to next chain block. */
curr_sg = h->cmd_sg_list[c->cmdindex]->sgchain;
sg_index = 0;
chained = 1;
}
curr_sg[sg_index].Len = tmp_sg[i].length;
temp64.val = (__u64) pci_map_page(h->pdev, sg_page(&tmp_sg[i]), temp64.val = (__u64) pci_map_page(h->pdev, sg_page(&tmp_sg[i]),
tmp_sg[i].offset, tmp_sg[i].offset,
tmp_sg[i].length, dir); tmp_sg[i].length, dir);
c->SG[i].Addr.lower = temp64.val32.lower; curr_sg[sg_index].Addr.lower = temp64.val32.lower;
c->SG[i].Addr.upper = temp64.val32.upper; curr_sg[sg_index].Addr.upper = temp64.val32.upper;
c->SG[i].Ext = 0; // we are not chaining curr_sg[sg_index].Ext = 0; /* we are not chaining */
++sg_index;
} }
if (chained) {
int len;
curr_sg = c->SG;
sg_index = h->max_cmd_sgentries - 1;
len = curr_sg[sg_index].Len;
/* Setup pointer to next chain block.
* Fill out last element in current chain
* block with address of next chain block.
*/
temp64.val = pci_map_single(h->pdev,
h->cmd_sg_list[c->cmdindex]->sgchain,
len, dir);
h->cmd_sg_list[c->cmdindex]->sg_chain_dma = temp64.val;
curr_sg[sg_index].Addr.lower = temp64.val32.lower;
curr_sg[sg_index].Addr.upper = temp64.val32.upper;
pci_dma_sync_single_for_device(h->pdev,
h->cmd_sg_list[c->cmdindex]->sg_chain_dma,
len, dir);
}
/* track how many SG entries we are using */ /* track how many SG entries we are using */
if (seg > h->maxSG) if (seg > h->maxSG)
h->maxSG = seg; h->maxSG = seg;
#ifdef CCISS_DEBUG #ifdef CCISS_DEBUG
printk(KERN_DEBUG "cciss: Submitting %u sectors in %d segments\n", printk(KERN_DEBUG "cciss: Submitting %ld sectors in %d segments "
blk_rq_sectors(creq), seg); "chained[%d]\n",
blk_rq_sectors(creq), seg, chained);
#endif /* CCISS_DEBUG */ #endif /* CCISS_DEBUG */
c->Header.SGList = c->Header.SGTotal = seg; c->Header.SGList = c->Header.SGTotal = seg + chained;
if (seg > h->max_cmd_sgentries)
c->Header.SGList = h->max_cmd_sgentries;
if (likely(blk_fs_request(creq))) { if (likely(blk_fs_request(creq))) {
if(h->cciss_read == CCISS_READ_10) { if(h->cciss_read == CCISS_READ_10) {
c->Request.CDB[1] = 0; c->Request.CDB[1] = 0;
@ -3713,6 +3778,23 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
* leave a little room for ioctl calls. * leave a little room for ioctl calls.
*/ */
c->max_commands = readl(&(c->cfgtable->CmdsOutMax)); c->max_commands = readl(&(c->cfgtable->CmdsOutMax));
c->maxsgentries = readl(&(c->cfgtable->MaxSGElements));
/*
* Limit native command to 32 s/g elements to save dma'able memory.
* Howvever spec says if 0, use 31
*/
c->max_cmd_sgentries = 31;
if (c->maxsgentries > 512) {
c->max_cmd_sgentries = 32;
c->chainsize = c->maxsgentries - c->max_cmd_sgentries + 1;
c->maxsgentries -= 1; /* account for chain pointer */
} else {
c->maxsgentries = 31; /* Default to traditional value */
c->chainsize = 0; /* traditional */
}
c->product_name = products[prod_index].product_name; c->product_name = products[prod_index].product_name;
c->access = *(products[prod_index].access); c->access = *(products[prod_index].access);
c->nr_cmds = c->max_commands - 4; c->nr_cmds = c->max_commands - 4;
@ -4039,6 +4121,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
{ {
int i; int i;
int j = 0; int j = 0;
int k = 0;
int rc; int rc;
int dac, return_code; int dac, return_code;
InquiryData_struct *inq_buff; InquiryData_struct *inq_buff;
@ -4142,6 +4225,53 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
printk(KERN_ERR "cciss: out of memory"); printk(KERN_ERR "cciss: out of memory");
goto clean4; goto clean4;
} }
/* Need space for temp scatter list */
hba[i]->scatter_list = kmalloc(hba[i]->max_commands *
sizeof(struct scatterlist *),
GFP_KERNEL);
for (k = 0; k < hba[i]->nr_cmds; k++) {
hba[i]->scatter_list[k] = kmalloc(sizeof(struct scatterlist) *
hba[i]->maxsgentries,
GFP_KERNEL);
if (hba[i]->scatter_list[k] == NULL) {
printk(KERN_ERR "cciss%d: could not allocate "
"s/g lists\n", i);
goto clean4;
}
}
hba[i]->cmd_sg_list = kmalloc(sizeof(struct Cmd_sg_list *) *
hba[i]->nr_cmds,
GFP_KERNEL);
if (!hba[i]->cmd_sg_list) {
printk(KERN_ERR "cciss%d: Cannot get memory for "
"s/g chaining.\n", i);
goto clean4;
}
/* Build up chain blocks for each command */
if (hba[i]->chainsize > 0) {
for (j = 0; j < hba[i]->nr_cmds; j++) {
hba[i]->cmd_sg_list[j] =
kmalloc(sizeof(struct Cmd_sg_list),
GFP_KERNEL);
if (!hba[i]->cmd_sg_list[j]) {
printk(KERN_ERR "cciss%d: Cannot get memory "
"for chain block.\n", i);
goto clean4;
}
/* Need a block of chainsized s/g elements. */
hba[i]->cmd_sg_list[j]->sgchain =
kmalloc((hba[i]->chainsize *
sizeof(SGDescriptor_struct)),
GFP_KERNEL);
if (!hba[i]->cmd_sg_list[j]->sgchain) {
printk(KERN_ERR "cciss%d: Cannot get memory "
"for s/g chains\n", i);
goto clean4;
}
}
}
spin_lock_init(&hba[i]->lock); spin_lock_init(&hba[i]->lock);
/* Initialize the pdev driver private data. /* Initialize the pdev driver private data.
@ -4187,7 +4317,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
cciss_procinit(i); cciss_procinit(i);
hba[i]->cciss_max_sectors = 2048; hba[i]->cciss_max_sectors = 8192;
rebuild_lun_table(hba[i], 1, 0); rebuild_lun_table(hba[i], 1, 0);
hba[i]->busy_initializing = 0; hba[i]->busy_initializing = 0;
@ -4195,6 +4325,15 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
clean4: clean4:
kfree(hba[i]->cmd_pool_bits); kfree(hba[i]->cmd_pool_bits);
/* Free up sg elements */
for (k = 0; k < hba[i]->nr_cmds; k++)
kfree(hba[i]->scatter_list[k]);
kfree(hba[i]->scatter_list);
for (j = 0; j < hba[i]->nr_cmds; j++) {
if (hba[i]->cmd_sg_list[j])
kfree(hba[i]->cmd_sg_list[j]->sgchain);
kfree(hba[i]->cmd_sg_list[j]);
}
if (hba[i]->cmd_pool) if (hba[i]->cmd_pool)
pci_free_consistent(hba[i]->pdev, pci_free_consistent(hba[i]->pdev,
hba[i]->nr_cmds * sizeof(CommandList_struct), hba[i]->nr_cmds * sizeof(CommandList_struct),
@ -4308,6 +4447,14 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
pci_free_consistent(hba[i]->pdev, hba[i]->nr_cmds * sizeof(ErrorInfo_struct), pci_free_consistent(hba[i]->pdev, hba[i]->nr_cmds * sizeof(ErrorInfo_struct),
hba[i]->errinfo_pool, hba[i]->errinfo_pool_dhandle); hba[i]->errinfo_pool, hba[i]->errinfo_pool_dhandle);
kfree(hba[i]->cmd_pool_bits); kfree(hba[i]->cmd_pool_bits);
/* Free up sg elements */
for (j = 0; j < hba[i]->nr_cmds; j++)
kfree(hba[i]->scatter_list[j]);
kfree(hba[i]->scatter_list);
for (j = 0; j < hba[i]->nr_cmds; j++) {
kfree(hba[i]->cmd_sg_list[j]->sgchain);
kfree(hba[i]->cmd_sg_list[j]);
}
/* /*
* Deliberately omit pci_disable_device(): it does something nasty to * Deliberately omit pci_disable_device(): it does something nasty to
* Smart Array controllers that pci_enable_device does not undo * Smart Array controllers that pci_enable_device does not undo

View File

@ -55,7 +55,13 @@ typedef struct _drive_info_struct
char device_initialized; /* indicates whether dev is initialized */ char device_initialized; /* indicates whether dev is initialized */
} drive_info_struct; } drive_info_struct;
struct ctlr_info struct Cmd_sg_list {
SGDescriptor_struct *sgchain;
dma64_addr_t sg_chain_dma;
int chain_block_size;
};
struct ctlr_info
{ {
int ctlr; int ctlr;
char devname[8]; char devname[8];
@ -75,6 +81,16 @@ struct ctlr_info
int num_luns; int num_luns;
int highest_lun; int highest_lun;
int usage_count; /* number of opens all all minor devices */ int usage_count; /* number of opens all all minor devices */
/* Need space for temp sg list
* number of scatter/gathers supported
* number of scatter/gathers in chained block
*/
struct scatterlist **scatter_list;
int maxsgentries;
int chainsize;
int max_cmd_sgentries;
struct Cmd_sg_list **cmd_sg_list;
# define DOORBELL_INT 0 # define DOORBELL_INT 0
# define PERF_MODE_INT 1 # define PERF_MODE_INT 1
# define SIMPLE_MODE_INT 2 # define SIMPLE_MODE_INT 2

View File

@ -7,7 +7,8 @@
//general boundary defintions //general boundary defintions
#define SENSEINFOBYTES 32//note that this value may vary between host implementations #define SENSEINFOBYTES 32//note that this value may vary between host implementations
#define MAXSGENTRIES 31 #define MAXSGENTRIES 32
#define CCISS_SG_CHAIN 0x80000000
#define MAXREPLYQS 256 #define MAXREPLYQS 256
//Command Status value //Command Status value
@ -319,6 +320,10 @@ typedef struct _CfgTable_struct {
BYTE ServerName[16]; BYTE ServerName[16];
DWORD HeartBeat; DWORD HeartBeat;
DWORD SCSI_Prefetch; DWORD SCSI_Prefetch;
DWORD MaxSGElements;
DWORD MaxLogicalUnits;
DWORD MaxPhysicalDrives;
DWORD MaxPhysicalDrivesPerLogicalUnit;
} CfgTable_struct; } CfgTable_struct;
#pragma pack() #pragma pack()
#endif // CCISS_CMD_H #endif // CCISS_CMD_H