On Sun, 14 Aug 2005, James Bottomley wrote:
> On Sun, 2005-08-14 at 21:33 +0200, Guennadi Liakhovetski wrote:
> > Just to make sure everyone agrees on this - there's currently a know bug
> > in dc395x with highmem reported by Pierre Ossman in thread "Kernel panic
> > with dc395x in 2.6.12.2" on linux-scsi. It is also trivial to reproduce on
> > non-highmem machines. It was there also in 2.6.12. It only was hit by one
> > person because not many use dc395x controlled cards in highmem machines
> > today. The fix is known - at least revert my patch to dc395x commited to
> > 2.6.12-rc5. This will fix this bug, leaving another one, which is much
> > harder to hit. There was only one person who hit it, but he cannot test
> > any more patches - the hardware is not there any more. I think I have a
> > fix for that one too, but that's another story. So, I would say it would
> > be worth it reverting that patch before 2.6.13, but, that's because I feel
> > personal responsibility for that bug:-)
>
> OK, why don't we do this. Instead of having me trawl through the trees
> looking for the correct patch to reverse, why don't you attach it in an
> email and I'll try to get it in to 2.6.13?
Ok. Below.
Sorry again for the trouble
Guennadi
---
Guennadi Liakhovetski
Signed-off-by: Guennadi Liakhovetski <[email protected]>
diff -u a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c
--- a/drivers/scsi/dc395x.c 17 Nov 2004 21:04:51
+++ b/drivers/scsi/dc395x.c 22 Jan 2005 22:55:45
@@ -182,7 +182,7 @@
* cross a page boundy.
*/
#define SEGMENTX_LEN (sizeof(struct SGentry)*DC395x_MAX_SG_LISTENTRY)
-
+#define VIRTX_LEN (sizeof(void *) * DC395x_MAX_SG_LISTENTRY)
struct SGentry {
u32 address; /* bus! address */
@@ -234,6 +234,7 @@
u8 sg_count; /* No of HW sg entries for this request */
u8 sg_index; /* Index of HW sg entry for this request */
u32 total_xfer_length; /* Total number of bytes remaining to be transfered */
+ void **virt_map;
unsigned char *virt_addr; /* Virtual address of current transfer position */
/*
@@ -1020,14 +1021,14 @@
reqlen, cmd->request_buffer, cmd->use_sg,
srb->sg_count);
- srb->virt_addr = page_address(sl->page);
for (i = 0; i < srb->sg_count; i++) {
- u32 busaddr = (u32)sg_dma_address(&sl[i]);
- u32 seglen = (u32)sl[i].length;
- sgp[i].address = busaddr;
+ u32 seglen = (u32)sg_dma_len(sl + i);
+ sgp[i].address = (u32)sg_dma_address(sl + i);
sgp[i].length = seglen;
srb->total_xfer_length += seglen;
+ srb->virt_map[i] = kmap(sl[i].page);
}
+ srb->virt_addr = srb->virt_map[0];
sgp += srb->sg_count - 1;
/*
@@ -1964,6 +1965,7 @@
int segment = cmd->use_sg;
u32 xferred = srb->total_xfer_length - left; /* bytes transfered */
struct SGentry *psge = srb->segment_x + srb->sg_index;
+ void **virt = srb->virt_map;
dprintkdbg(DBG_0,
"sg_update_list: Transfered %i of %i bytes, %i remain\n",
@@ -2003,16 +2005,16 @@
/* We have to walk the scatterlist to find it */
sg = (struct scatterlist *)cmd->request_buffer;
+ idx = 0;
while (segment--) {
unsigned long mask =
~((unsigned long)sg->length - 1) & PAGE_MASK;
if ((sg_dma_address(sg) & mask) == (psge->address & mask)) {
- srb->virt_addr = (page_address(sg->page)
- + psge->address -
- (psge->address & PAGE_MASK));
+ srb->virt_addr = virt[idx] + (psge->address & ~PAGE_MASK);
return;
}
++sg;
+ ++idx;
}
dprintkl(KERN_ERR, "sg_update_list: sg_to_virt failed\n");
@@ -2138,7 +2140,7 @@
DC395x_read32(acb, TRM_S1040_DMA_CXCNT));
}
/*
- * calculate all the residue data that not yet tranfered
+ * calculate all the residue data that not yet transfered
* SCSI transfer counter + left in SCSI FIFO data
*
* .....TRM_S1040_SCSI_COUNTER (24bits)
@@ -3256,6 +3258,7 @@
struct scsi_cmnd *cmd = srb->cmd;
enum dma_data_direction dir = cmd->sc_data_direction;
if (cmd->use_sg && dir != PCI_DMA_NONE) {
+ int i;
/* unmap DC395x SG list */
dprintkdbg(DBG_SG, "pci_unmap_srb: list=%08x(%05x)\n",
srb->sg_bus_addr, SEGMENTX_LEN);
@@ -3265,6 +3268,8 @@
dprintkdbg(DBG_SG, "pci_unmap_srb: segs=%i buffer=%p\n",
cmd->use_sg, cmd->request_buffer);
/* unmap the sg segments */
+ for (i = 0; i < srb->sg_count; i++)
+ kunmap(virt_to_page(srb->virt_map[i]));
pci_unmap_sg(acb->dev,
(struct scatterlist *)cmd->request_buffer,
cmd->use_sg, dir);
@@ -3311,7 +3316,7 @@
if (cmd->use_sg) {
struct scatterlist* sg = (struct scatterlist *)cmd->request_buffer;
- ptr = (struct ScsiInqData *)(page_address(sg->page) + sg->offset);
+ ptr = (struct ScsiInqData *)(srb->virt_map[0] + sg->offset);
} else {
ptr = (struct ScsiInqData *)(cmd->request_buffer);
}
@@ -4246,8 +4251,9 @@
const unsigned srbs_per_page = PAGE_SIZE/SEGMENTX_LEN;
for (i = 0; i < DC395x_MAX_SRB_CNT; i += srbs_per_page)
- if (acb->srb_array[i].segment_x)
- kfree(acb->srb_array[i].segment_x);
+ kfree(acb->srb_array[i].segment_x);
+
+ vfree(acb->srb_array[0].virt_map);
}
@@ -4263,9 +4269,12 @@
int srb_idx = 0;
unsigned i = 0;
struct SGentry *ptr;
+ void **virt_array;
- for (i = 0; i < DC395x_MAX_SRB_CNT; i++)
+ for (i = 0; i < DC395x_MAX_SRB_CNT; i++) {
acb->srb_array[i].segment_x = NULL;
+ acb->srb_array[i].virt_map = NULL;
+ }
dprintkdbg(DBG_1, "Allocate %i pages for SG tables\n", pages);
while (pages--) {
@@ -4286,6 +4295,19 @@
ptr + (i * DC395x_MAX_SG_LISTENTRY);
else
dprintkl(KERN_DEBUG, "No space for tmsrb SG table reserved?!\n");
+
+ virt_array = vmalloc((DC395x_MAX_SRB_CNT + 1) * DC395x_MAX_SG_LISTENTRY * sizeof(void*));
+
+ if (!virt_array) {
+ adapter_sg_tables_free(acb);
+ return 1;
+ }
+
+ for (i = 0; i < DC395x_MAX_SRB_CNT + 1; i++) {
+ acb->srb_array[i].virt_map = virt_array;
+ virt_array += DC395x_MAX_SG_LISTENTRY;
+ }
+
return 0;
}
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[Index of Archives]
[Kernel Newbies]
[Netfilter]
[Bugtraq]
[Photo]
[Gimp]
[Yosemite News]
[MIPS Linux]
[ARM Linux]
[Linux Security]
[Linux RAID]
[Video 4 Linux]
[Linux for the blind]
|
|