Skip to content

Commit cdb8c2a

Browse files
lyakhJames Bottomley
authored andcommitted
[SCSI] dc395x: dynamically map scatter-gather for PIO
The current dc395x driver uses PIO to transfer up to 4 bytes which do not get transferred by DMA (under unclear circumstances). For this the driver uses page_address() which is broken on highmem. Apart from this the actual calculation of the virtual address is wrong (even without highmem). So, e.g., for reading it reads bytes from the driver to a wrong address and returns wrong data, I guess, for writing it would just output random data to the device. The proper fix, as suggested by many, is to dynamically map data using kmap_atomic(page, KM_BIO_SRC_IRQ) / kunmap_atomic(virt). The reason why it has not been done until now, although I've done some preliminary patches more than a year ago was that nobody interested in fixing this problem was able to reliably reproduce it. Now it changed - with the help from Sebastian Frei (CC'ed) I was able to trigger the PIO path. Thus, I was also able to test and debug it. There are 4 cases when PIO is used in dc395x - data-in / -out with and without scatter-gather. I was able to reproduce and test only data-in with and without SG. So, the data-out path is still untested, but it is also somewhat simpler than the data-in. Fredrik Roubert (also CC'ed) also had PIO triggering on his system, and in his case it was data-out without SG. It would be great if he could test the attached patch on his system, but even if he cannot, I would still request to apply the patch and just wait if anybody cries... Implementation: I put 2 new functions in scsi_lib.c and their declarations in scsi_cmnd.h. I exported them without _GPL, although, I don't feel strongly about that - not many drivers are likely to use them. But there is at least one more - I want to use them in tmscsim.c. Whether these are the right files for the functions and their declarations - not sure either. Actually, they are not scsi-specific, so, might go somewhere around other scattergather magic? They are not platform specific either, and most SG functions are defined under arch/*/... As these issues were discussed previously there were some more routines suggested to manipulate scattergather buffers, I think, some of them were needed around crypto code... So, might be a common place reasonable, like lib/scattergather.c? I am open here. Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
1 parent 4c021dd commit cdb8c2a

File tree

3 files changed

+176
-102
lines changed

3 files changed

+176
-102
lines changed

drivers/scsi/dc395x.c

Lines changed: 115 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -230,13 +230,12 @@ struct ScsiReqBlk {
230230
struct scsi_cmnd *cmd;
231231

232232
struct SGentry *segment_x; /* Linear array of hw sg entries (up to 64 entries) */
233-
u32 sg_bus_addr; /* Bus address of sg list (ie, of segment_x) */
233+
dma_addr_t sg_bus_addr; /* Bus address of sg list (ie, of segment_x) */
234234

235235
u8 sg_count; /* No of HW sg entries for this request */
236236
u8 sg_index; /* Index of HW sg entry for this request */
237-
u32 total_xfer_length; /* Total number of bytes remaining to be transfered */
238-
unsigned char *virt_addr; /* Virtual address of current transfer position */
239-
237+
size_t total_xfer_length; /* Total number of bytes remaining to be transfered */
238+
size_t request_length; /* Total number of bytes in this request */
240239
/*
241240
* The sense buffer handling function, request_sense, uses
242241
* the first hw sg entry (segment_x[0]) and the transfer
@@ -246,8 +245,7 @@ struct ScsiReqBlk {
246245
* total_xfer_length in xferred. These values are restored in
247246
* pci_unmap_srb_sense. This is the only place xferred is used.
248247
*/
249-
unsigned char *virt_addr_req; /* Saved virtual address of the request buffer */
250-
u32 xferred; /* Saved copy of total_xfer_length */
248+
size_t xferred; /* Saved copy of total_xfer_length */
251249

252250
u16 state;
253251

@@ -977,17 +975,6 @@ static void send_srb(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
977975
}
978976
}
979977

980-
static inline void pio_trigger(void)
981-
{
982-
static int feedback_requested;
983-
984-
if (!feedback_requested) {
985-
feedback_requested = 1;
986-
printk(KERN_WARNING "%s: Please, contact <linux-scsi@vger.kernel.org> "
987-
"to help improve support for your system.\n", __FILE__);
988-
}
989-
}
990-
991978
/* Prepare SRB for being sent to Device DCB w/ command *cmd */
992979
static void build_srb(struct scsi_cmnd *cmd, struct DeviceCtlBlk *dcb,
993980
struct ScsiReqBlk *srb)
@@ -1001,7 +988,6 @@ static void build_srb(struct scsi_cmnd *cmd, struct DeviceCtlBlk *dcb,
1001988
srb->sg_count = 0;
1002989
srb->total_xfer_length = 0;
1003990
srb->sg_bus_addr = 0;
1004-
srb->virt_addr = NULL;
1005991
srb->sg_index = 0;
1006992
srb->adapter_status = 0;
1007993
srb->target_status = 0;
@@ -1032,7 +1018,6 @@ static void build_srb(struct scsi_cmnd *cmd, struct DeviceCtlBlk *dcb,
10321018
reqlen, cmd->request_buffer, cmd->use_sg,
10331019
srb->sg_count);
10341020

1035-
srb->virt_addr = page_address(sl->page);
10361021
for (i = 0; i < srb->sg_count; i++) {
10371022
u32 busaddr = (u32)sg_dma_address(&sl[i]);
10381023
u32 seglen = (u32)sl[i].length;
@@ -1077,12 +1062,14 @@ static void build_srb(struct scsi_cmnd *cmd, struct DeviceCtlBlk *dcb,
10771062
srb->total_xfer_length++;
10781063

10791064
srb->segment_x[0].length = srb->total_xfer_length;
1080-
srb->virt_addr = cmd->request_buffer;
1065+
10811066
dprintkdbg(DBG_0,
10821067
"build_srb: [1] len=%d buf=%p use_sg=%d map=%08x\n",
10831068
srb->total_xfer_length, cmd->request_buffer,
10841069
cmd->use_sg, srb->segment_x[0].address);
10851070
}
1071+
1072+
srb->request_length = srb->total_xfer_length;
10861073
}
10871074

10881075

@@ -1414,10 +1401,10 @@ static int dc395x_eh_abort(struct scsi_cmnd *cmd)
14141401
}
14151402
srb = find_cmd(cmd, &dcb->srb_going_list);
14161403
if (srb) {
1417-
dprintkl(KERN_DEBUG, "eh_abort: Command in progress");
1404+
dprintkl(KERN_DEBUG, "eh_abort: Command in progress\n");
14181405
/* XXX: Should abort the command here */
14191406
} else {
1420-
dprintkl(KERN_DEBUG, "eh_abort: Command not found");
1407+
dprintkl(KERN_DEBUG, "eh_abort: Command not found\n");
14211408
}
14221409
return FAILED;
14231410
}
@@ -1976,14 +1963,11 @@ static void sg_verify_length(struct ScsiReqBlk *srb)
19761963

19771964
/*
19781965
* Compute the next Scatter Gather list index and adjust its length
1979-
* and address if necessary; also compute virt_addr
1966+
* and address if necessary
19801967
*/
19811968
static void sg_update_list(struct ScsiReqBlk *srb, u32 left)
19821969
{
19831970
u8 idx;
1984-
struct scatterlist *sg;
1985-
struct scsi_cmnd *cmd = srb->cmd;
1986-
int segment = cmd->use_sg;
19871971
u32 xferred = srb->total_xfer_length - left; /* bytes transfered */
19881972
struct SGentry *psge = srb->segment_x + srb->sg_index;
19891973

@@ -2016,29 +2000,6 @@ static void sg_update_list(struct ScsiReqBlk *srb, u32 left)
20162000
psge++;
20172001
}
20182002
sg_verify_length(srb);
2019-
2020-
/* we need the corresponding virtual address */
2021-
if (!segment || (srb->flag & AUTO_REQSENSE)) {
2022-
srb->virt_addr += xferred;
2023-
return;
2024-
}
2025-
2026-
/* We have to walk the scatterlist to find it */
2027-
sg = (struct scatterlist *)cmd->request_buffer;
2028-
while (segment--) {
2029-
unsigned long mask =
2030-
~((unsigned long)sg->length - 1) & PAGE_MASK;
2031-
if ((sg_dma_address(sg) & mask) == (psge->address & mask)) {
2032-
srb->virt_addr = (page_address(sg->page)
2033-
+ psge->address -
2034-
(psge->address & PAGE_MASK));
2035-
return;
2036-
}
2037-
++sg;
2038-
}
2039-
2040-
dprintkl(KERN_ERR, "sg_update_list: sg_to_virt failed\n");
2041-
srb->virt_addr = NULL;
20422003
}
20432004

20442005

@@ -2050,15 +2011,7 @@ static void sg_update_list(struct ScsiReqBlk *srb, u32 left)
20502011
*/
20512012
static void sg_subtract_one(struct ScsiReqBlk *srb)
20522013
{
2053-
srb->total_xfer_length--;
2054-
srb->segment_x[srb->sg_index].length--;
2055-
if (srb->total_xfer_length &&
2056-
!srb->segment_x[srb->sg_index].length) {
2057-
if (debug_enabled(DBG_PIO))
2058-
printk(" (next segment)");
2059-
srb->sg_index++;
2060-
sg_update_list(srb, srb->total_xfer_length);
2061-
}
2014+
sg_update_list(srb, srb->total_xfer_length - 1);
20622015
}
20632016

20642017

@@ -2118,7 +2071,7 @@ static void data_out_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
21182071
* If we need more data, the DMA SG list will be freshly set up, anyway
21192072
*/
21202073
dprintkdbg(DBG_PIO, "data_out_phase0: "
2121-
"DMA{fifcnt=0x%02x fifostat=0x%02x} "
2074+
"DMA{fifocnt=0x%02x fifostat=0x%02x} "
21222075
"SCSI{fifocnt=0x%02x cnt=0x%06x status=0x%04x} total=0x%06x\n",
21232076
DC395x_read8(acb, TRM_S1040_DMA_FIFOCNT),
21242077
DC395x_read8(acb, TRM_S1040_DMA_FIFOSTAT),
@@ -2239,12 +2192,11 @@ static void data_out_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
22392192
data_io_transfer(acb, srb, XFERDATAOUT);
22402193
}
22412194

2242-
22432195
static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
22442196
u16 *pscsi_status)
22452197
{
22462198
u16 scsi_status = *pscsi_status;
2247-
u32 d_left_counter = 0;
2199+
22482200
dprintkdbg(DBG_0, "data_in_phase0: (pid#%li) <%02i-%i>\n",
22492201
srb->cmd->pid, srb->cmd->device->id, srb->cmd->device->lun);
22502202

@@ -2262,6 +2214,9 @@ static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
22622214
* seem to be a bad idea, actually.
22632215
*/
22642216
if (!(srb->state & SRB_XFERPAD)) {
2217+
u32 d_left_counter;
2218+
unsigned int sc, fc;
2219+
22652220
if (scsi_status & PARITYERROR) {
22662221
dprintkl(KERN_INFO, "data_in_phase0: (pid#%li) "
22672222
"Parity Error\n", srb->cmd->pid);
@@ -2298,59 +2253,99 @@ static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
22982253
DC395x_read8(acb, TRM_S1040_DMA_FIFOSTAT));
22992254
}
23002255
/* Now: Check remainig data: The SCSI counters should tell us ... */
2301-
d_left_counter = DC395x_read32(acb, TRM_S1040_SCSI_COUNTER)
2302-
+ ((DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT) & 0x1f)
2256+
sc = DC395x_read32(acb, TRM_S1040_SCSI_COUNTER);
2257+
fc = DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT);
2258+
d_left_counter = sc + ((fc & 0x1f)
23032259
<< ((srb->dcb->sync_period & WIDE_SYNC) ? 1 :
23042260
0));
23052261
dprintkdbg(DBG_KG, "data_in_phase0: "
23062262
"SCSI{fifocnt=0x%02x%s ctr=0x%08x} "
23072263
"DMA{fifocnt=0x%02x fifostat=0x%02x ctr=0x%08x} "
23082264
"Remain{totxfer=%i scsi_fifo+ctr=%i}\n",
2309-
DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT),
2265+
fc,
23102266
(srb->dcb->sync_period & WIDE_SYNC) ? "words" : "bytes",
2311-
DC395x_read32(acb, TRM_S1040_SCSI_COUNTER),
2312-
DC395x_read8(acb, TRM_S1040_DMA_FIFOCNT),
2267+
sc,
2268+
fc,
23132269
DC395x_read8(acb, TRM_S1040_DMA_FIFOSTAT),
23142270
DC395x_read32(acb, TRM_S1040_DMA_CXCNT),
23152271
srb->total_xfer_length, d_left_counter);
23162272
#if DC395x_LASTPIO
23172273
/* KG: Less than or equal to 4 bytes can not be transfered via DMA, it seems. */
23182274
if (d_left_counter
23192275
&& srb->total_xfer_length <= DC395x_LASTPIO) {
2276+
size_t left_io = srb->total_xfer_length;
2277+
23202278
/*u32 addr = (srb->segment_x[srb->sg_index].address); */
23212279
/*sg_update_list (srb, d_left_counter); */
2322-
dprintkdbg(DBG_PIO, "data_in_phase0: PIO (%i %s) to "
2323-
"%p for remaining %i bytes:",
2324-
DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT) & 0x1f,
2280+
dprintkdbg(DBG_PIO, "data_in_phase0: PIO (%i %s) "
2281+
"for remaining %i bytes:",
2282+
fc & 0x1f,
23252283
(srb->dcb->sync_period & WIDE_SYNC) ?
23262284
"words" : "bytes",
2327-
srb->virt_addr,
23282285
srb->total_xfer_length);
23292286
if (srb->dcb->sync_period & WIDE_SYNC)
23302287
DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2,
23312288
CFG2_WIDEFIFO);
2332-
while (DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT) != 0x40) {
2333-
u8 byte = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
2334-
pio_trigger();
2335-
*(srb->virt_addr)++ = byte;
2336-
if (debug_enabled(DBG_PIO))
2337-
printk(" %02x", byte);
2338-
d_left_counter--;
2339-
sg_subtract_one(srb);
2340-
}
2341-
if (srb->dcb->sync_period & WIDE_SYNC) {
2342-
#if 1
2343-
/* Read the last byte ... */
2344-
if (srb->total_xfer_length > 0) {
2345-
u8 byte = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
2346-
pio_trigger();
2347-
*(srb->virt_addr)++ = byte;
2348-
srb->total_xfer_length--;
2289+
while (left_io) {
2290+
unsigned char *virt, *base = NULL;
2291+
unsigned long flags = 0;
2292+
size_t len = left_io;
2293+
2294+
if (srb->cmd->use_sg) {
2295+
size_t offset = srb->request_length - left_io;
2296+
local_irq_save(flags);
2297+
/* Assumption: it's inside one page as it's at most 4 bytes and
2298+
I just assume it's on a 4-byte boundary */
2299+
base = scsi_kmap_atomic_sg((struct scatterlist *)srb->cmd->request_buffer,
2300+
srb->sg_count, &offset, &len);
2301+
virt = base + offset;
2302+
} else {
2303+
virt = srb->cmd->request_buffer + srb->cmd->request_bufflen - left_io;
2304+
len = left_io;
2305+
}
2306+
left_io -= len;
2307+
2308+
while (len) {
2309+
u8 byte;
2310+
byte = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
2311+
*virt++ = byte;
2312+
23492313
if (debug_enabled(DBG_PIO))
23502314
printk(" %02x", byte);
2315+
2316+
d_left_counter--;
2317+
sg_subtract_one(srb);
2318+
2319+
len--;
2320+
2321+
fc = DC395x_read8(acb, TRM_S1040_SCSI_FIFOCNT);
2322+
2323+
if (fc == 0x40) {
2324+
left_io = 0;
2325+
break;
2326+
}
2327+
}
2328+
2329+
WARN_ON((fc != 0x40) == !d_left_counter);
2330+
2331+
if (fc == 0x40 && (srb->dcb->sync_period & WIDE_SYNC)) {
2332+
/* Read the last byte ... */
2333+
if (srb->total_xfer_length > 0) {
2334+
u8 byte = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
2335+
2336+
*virt++ = byte;
2337+
srb->total_xfer_length--;
2338+
if (debug_enabled(DBG_PIO))
2339+
printk(" %02x", byte);
2340+
}
2341+
2342+
DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2, 0);
2343+
}
2344+
2345+
if (srb->cmd->use_sg) {
2346+
scsi_kunmap_atomic_sg(base);
2347+
local_irq_restore(flags);
23512348
}
2352-
#endif
2353-
DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2, 0);
23542349
}
23552350
/*printk(" %08x", *(u32*)(bus_to_virt (addr))); */
23562351
/*srb->total_xfer_length = 0; */
@@ -2509,22 +2504,43 @@ static void data_io_transfer(struct AdapterCtlBlk *acb,
25092504
SCMD_FIFO_IN);
25102505
} else { /* write */
25112506
int ln = srb->total_xfer_length;
2507+
size_t left_io = srb->total_xfer_length;
2508+
25122509
if (srb->dcb->sync_period & WIDE_SYNC)
25132510
DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2,
25142511
CFG2_WIDEFIFO);
2515-
dprintkdbg(DBG_PIO,
2516-
"data_io_transfer: PIO %i bytes from %p:",
2517-
srb->total_xfer_length, srb->virt_addr);
25182512

2519-
while (srb->total_xfer_length) {
2520-
if (debug_enabled(DBG_PIO))
2521-
printk(" %02x", (unsigned char) *(srb->virt_addr));
2513+
while (left_io) {
2514+
unsigned char *virt, *base = NULL;
2515+
unsigned long flags = 0;
2516+
size_t len = left_io;
2517+
2518+
if (srb->cmd->use_sg) {
2519+
size_t offset = srb->request_length - left_io;
2520+
local_irq_save(flags);
2521+
/* Again, max 4 bytes */
2522+
base = scsi_kmap_atomic_sg((struct scatterlist *)srb->cmd->request_buffer,
2523+
srb->sg_count, &offset, &len);
2524+
virt = base + offset;
2525+
} else {
2526+
virt = srb->cmd->request_buffer + srb->cmd->request_bufflen - left_io;
2527+
len = left_io;
2528+
}
2529+
left_io -= len;
2530+
2531+
while (len--) {
2532+
if (debug_enabled(DBG_PIO))
2533+
printk(" %02x", *virt);
25222534

2523-
pio_trigger();
2524-
DC395x_write8(acb, TRM_S1040_SCSI_FIFO,
2525-
*(srb->virt_addr)++);
2535+
DC395x_write8(acb, TRM_S1040_SCSI_FIFO, *virt++);
25262536

2527-
sg_subtract_one(srb);
2537+
sg_subtract_one(srb);
2538+
}
2539+
2540+
if (srb->cmd->use_sg) {
2541+
scsi_kunmap_atomic_sg(base);
2542+
local_irq_restore(flags);
2543+
}
25282544
}
25292545
if (srb->dcb->sync_period & WIDE_SYNC) {
25302546
if (ln % 2) {
@@ -3319,7 +3335,6 @@ static void pci_unmap_srb_sense(struct AdapterCtlBlk *acb,
33193335
srb->segment_x[DC395x_MAX_SG_LISTENTRY - 1].address;
33203336
srb->segment_x[0].length =
33213337
srb->segment_x[DC395x_MAX_SG_LISTENTRY - 1].length;
3322-
srb->virt_addr = srb->virt_addr_req;
33233338
}
33243339

33253340

@@ -3713,8 +3728,6 @@ static void request_sense(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
37133728
srb->xferred = srb->total_xfer_length;
37143729
/* srb->segment_x : a one entry of S/G list table */
37153730
srb->total_xfer_length = sizeof(cmd->sense_buffer);
3716-
srb->virt_addr_req = srb->virt_addr;
3717-
srb->virt_addr = cmd->sense_buffer;
37183731
srb->segment_x[0].length = sizeof(cmd->sense_buffer);
37193732
/* Map sense buffer */
37203733
srb->segment_x[0].address =

0 commit comments

Comments
 (0)