Hi
Thanks for reviewing this patch!
On Wed, 16 Mar 2005, Jens Axboe wrote:
> On Wed, Mar 16 2005, James Bottomley wrote:
...
> > I agree the kmap is inefficient. The efficient alternative is to do
> > dma_map_sg() and use kmap_atomic() in the interrupt routine where we do
> > the PIO cleanup---I'm afraid I just passed on explaining how to do
> > this ... unless you care to do the honours ?
In fact, the first version of my patch (attached below) did exactly this -
only when the driver recognises, that it needs to do PIO in the interrupt,
I would call kmap_atomic(), do PIO, then kunmap_atomic(). The main reason,
why I didn't submit that patch, was that I got confused about various KM_
macros, and I thought, since it is a valuable limited resource, only very
"special" drivers are allowed to use them / are allocated one of them.
But, I guess now, you can just do
local_irq_save(flags);
kmap_atomic();
...
kunmap_atomic();
local_irq_restore(flags);
Please, have a look. Or should we indeed go the "generic helper functions"
way?
> The kmap() isn't just inefficient, it's a problem to iterate over the sg
> list and kmap all the pages. That is illegal.
Hm, what do you mean "illegal"? Could you explain why?
> But it's not so tricky to get right, if the punting just happens in the
> isr. Basically just iterate over every sg entry left ala:
>
> for (i = start; i < sg_entries; i++) {
> unsigned long flags;
> char *ptr;
>
> local_irq_save(flags);
> ptr = kmap_atomic(sg->page, KM_BIO_SRC_IRQ);
>
> /* transfer to/from ptr + sg->offset, sg->length bytes */
>
> kunmap_atomic(ptr, KM_BIO_SRC_IRQ);
> local_irq_restore(flags);
> }
>
> I _think_ the sg->length field is universally called so, you should not
> use sg->dma_length/sg_dma_len() or sg_dma_address(), as we are outside
> the work of the iommu at this point.
One more fragment in the driver I wasn't sure about is this:
unsigned long mask =
~((unsigned long)sg->length - 1) & PAGE_MASK;
if ((sg_dma_address(sg) & mask) == (psge->address & mask)) {
Is sg->length guaranteed to be something like a power of 2 or smaller
than page? I thought about replacing the above with
+ if (sg_dma_address(sg) <= psge->address && sg_dma_address(sg) +
psge->length > psge->address) {
but wasn't sure.
Thanks
Guennadi
---
Guennadi Liakhovetski
Index: drivers/scsi/dc395x.c
===================================================================
RCS file: /usr/src/cvs/linux-2_6/drivers/scsi/dc395x.c,v
retrieving revision 1.1.1.4
diff -u -r1.1.1.4 dc395x.c
--- drivers/scsi/dc395x.c 17 Nov 2004 21:04:51 -0000 1.1.1.4
+++ drivers/scsi/dc395x.c 16 Mar 2005 20:12:07 -0000
@@ -185,7 +185,7 @@
struct SGentry {
- u32 address; /* bus! address */
+ dma_addr_t address; /* bus! address */
u32 length;
};
@@ -234,7 +234,8 @@
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 */
- unsigned char *virt_addr; /* Virtual address of current transfer
position */
+ struct page *page;
+ unsigned int offset;
/*
* The sense buffer handling function, request_sense, uses
@@ -989,7 +990,8 @@
srb->sg_count = 0;
srb->total_xfer_length = 0;
srb->sg_bus_addr = 0;
- srb->virt_addr = NULL;
+ srb->page = NULL;
+ srb->offset = 0;
srb->sg_index = 0;
srb->adapter_status = 0;
srb->target_status = 0;
@@ -1020,11 +1022,11 @@
reqlen, cmd->request_buffer, cmd->use_sg,
srb->sg_count);
- srb->virt_addr = page_address(sl->page);
+ srb->page = sl->page;
+ srb->offset = sl->offset;
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 = cpu_to_le32(0xffffffff &
sg_dma_address(sl +i));
sgp[i].length = seglen;
srb->total_xfer_length += seglen;
}
@@ -1065,7 +1067,8 @@
srb->total_xfer_length++;
srb->segment_x[0].length = srb->total_xfer_length;
- srb->virt_addr = cmd->request_buffer;
+ srb->page = virt_to_page(cmd->request_buffer);
+ srb->offset = (unsigned long)cmd->request_buffer & ~PAGE_MASK;
dprintkdbg(DBG_0,
"build_srb: [1] len=%d buf=%p use_sg=%d map=%08x\n",
srb->total_xfer_length, cmd->request_buffer,
@@ -1984,8 +1987,7 @@
psge->length -= xferred;
psge->address += xferred;
srb->sg_index = idx;
- pci_dma_sync_single_for_device(srb->dcb->
- acb->dev,
+ pci_dma_sync_single_for_device(srb->dcb->acb->dev,
srb->sg_bus_addr,
SEGMENTX_LEN,
PCI_DMA_TODEVICE);
@@ -1995,28 +1997,21 @@
}
sg_verify_length(srb);
- /* we need the corresponding virtual address */
- if (!segment) {
- srb->virt_addr += xferred;
- return;
- }
-
/* We have to walk the scatterlist to find it */
sg = (struct scatterlist *)cmd->request_buffer;
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));
+// if (sg_dma_address(sg) <= psge->address && sg_dma_address(sg) +
psge->length > psge->address) {
+ srb->page = sg->page;
+ srb->offset = psge->address & ~PAGE_MASK;
return;
}
++sg;
}
dprintkl(KERN_ERR, "sg_update_list: sg_to_virt failed\n");
- srb->virt_addr = NULL;
}
@@ -2138,7 +2133,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)
@@ -2184,15 +2179,10 @@
(dcb->sync_period & WIDE_SYNC) ? 2 : 1;
sg_update_list(srb, d_left_counter);
/* KG: Most ugly hack! Apparently, this works around a
chip bug */
- if ((srb->segment_x[srb->sg_index].length ==
- diff && srb->cmd->use_sg)
- || ((oldxferred & ~PAGE_MASK) ==
- (PAGE_SIZE - diff))
- ) {
- dprintkl(KERN_INFO, "data_out_phase0: "
- "Work around chip bug (%i)?\n", diff);
- d_left_counter =
- srb->total_xfer_length - diff;
+ if ((srb->segment_x[srb->sg_index].length == diff &&
srb->cmd->use_sg) ||
+ (oldxferred & ~PAGE_MASK) == PAGE_SIZE - diff) {
+ dprintkl(KERN_INFO, "data_out_phase0: Work
around chip bug (%i)?\n", diff);
+ d_left_counter = srb->total_xfer_length - diff;
sg_update_list(srb, d_left_counter);
/*srb->total_xfer_length -= diff; */
/*srb->virt_addr += diff; */
@@ -2297,19 +2287,18 @@
&& srb->total_xfer_length <= DC395x_LASTPIO) {
/*u32 addr = (srb->segment_x[srb->sg_index].address); */
/*sg_update_list (srb, d_left_counter); */
- dprintkdbg(DBG_PIO, "data_in_phase0: PIO (%i %s) to "
- "%p for remaining %i bytes:",
- DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT) &
0x1f,
- (srb->dcb->sync_period & WIDE_SYNC) ?
- "words" : "bytes",
- srb->virt_addr,
- srb->total_xfer_length);
+ char *page_addr, *virt_addr;
+ unsigned long flags;
if (srb->dcb->sync_period & WIDE_SYNC)
DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2,
CFG2_WIDEFIFO);
+ local_irq_save(flags);
+ page_addr = kmap_atomic(srb->page, KM_USER0);
+ virt_addr = page_addr + srb->offset;
+
while (DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT) !=
0x40) {
u8 byte = DC395x_read8(acb,
TRM_S1040_SCSI_FIFO);
- *(srb->virt_addr)++ = byte;
+ *(virt_addr)++ = byte;
if (debug_enabled(DBG_PIO))
printk(" %02x", byte);
d_left_counter--;
@@ -2320,7 +2309,7 @@
/* Read the last byte ... */
if (srb->total_xfer_length > 0) {
u8 byte = DC395x_read8(acb,
TRM_S1040_SCSI_FIFO);
- *(srb->virt_addr)++ = byte;
+ *(virt_addr)++ = byte;
srb->total_xfer_length--;
if (debug_enabled(DBG_PIO))
printk(" %02x", byte);
@@ -2328,6 +2317,8 @@
#endif
DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2, 0);
}
+ kunmap_atomic(page_addr, KM_IRQ0);
+ local_irq_restore(flags);
/*printk(" %08x", *(u32*)(bus_to_virt (addr))); */
/*srb->total_xfer_length = 0; */
if (debug_enabled(DBG_PIO))
@@ -2452,7 +2443,7 @@
DC395x_write32(acb, TRM_S1040_DMA_XLOWADDR,
srb->segment_x[0].address);
DC395x_write32(acb, TRM_S1040_DMA_XCNT,
- srb->segment_x[0].length);
+ cpu_to_le32(srb->segment_x[0].length));
}
/* load total transfer length (24bits) max value 16Mbyte */
DC395x_write32(acb, TRM_S1040_SCSI_COUNTER,
@@ -2485,22 +2476,25 @@
SCMD_FIFO_IN);
} else { /* write */
int ln = srb->total_xfer_length;
+ char *page_addr, *virt_addr;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ page_addr = kmap_atomic(srb->page, KM_USER0);
+ virt_addr = page_addr + srb->offset;
if (srb->dcb->sync_period & WIDE_SYNC)
DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2,
CFG2_WIDEFIFO);
- dprintkdbg(DBG_PIO,
- "data_io_transfer: PIO %i bytes from %p:",
- srb->total_xfer_length, srb->virt_addr);
-
while (srb->total_xfer_length) {
if (debug_enabled(DBG_PIO))
- printk(" %02x", (unsigned char)
*(srb->virt_addr));
+ printk(" %02x", (unsigned char)
*virt_addr);
DC395x_write8(acb, TRM_S1040_SCSI_FIFO,
- *(srb->virt_addr)++);
-
+ *virt_addr++);
sg_subtract_one(srb);
}
+ kunmap_atomic(page_addr, KM_USER0);
+ local_irq_restore(flags);
if (srb->dcb->sync_period & WIDE_SYNC) {
if (ln % 2) {
DC395x_write8(acb, TRM_S1040_SCSI_FIFO,
0);
-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html