Skip to content

Commit 5551bc3

Browse files
fthainmartinkpetersen
authored andcommitted
scsi: mac_scsi: Disallow bus errors during PDMA send
SD cards can produce write latency spikes on the order of a hundred milliseconds. If the target firmware does not hide that latency during DATA IN and OUT phases it can cause the PDMA circuitry to raise a processor bus fault which in turn leads to an unreliable byte count and a DMA overrun. The Last Byte Sent flag is used to detect the overrun but this mechanism is unreliable on some systems. Instead, set a DID_ERROR result whenever there is a bus fault during a PDMA send, unless the cause was a phase mismatch. Cc: [email protected] # 5.15+ Reported-and-tested-by: Stan Johnson <[email protected]> Fixes: 7c1f3e3 ("scsi: mac_scsi: Treat Last Byte Sent time-out as failure") Signed-off-by: Finn Thain <[email protected]> Link: https://lore.kernel.org/r/cc38df687ace2c4ffc375a683b2502fc476b600d.1723001788.git.fthain@linux-m68k.org Signed-off-by: Martin K. Petersen <[email protected]>
1 parent 5545c31 commit 5551bc3

File tree

1 file changed

+19
-25
lines changed

1 file changed

+19
-25
lines changed

drivers/scsi/mac_scsi.c

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,15 @@ __setup("mac5380=", mac_scsi_setup);
102102
* Linux SCSI drivers lack knowledge of the timing behaviour of SCSI targets
103103
* so bus errors are unavoidable.
104104
*
105-
* If a MOVE.B instruction faults, we assume that zero bytes were transferred
106-
* and simply retry. That assumption probably depends on target behaviour but
107-
* seems to hold up okay. The NOP provides synchronization: without it the
108-
* fault can sometimes occur after the program counter has moved past the
109-
* offending instruction. Post-increment addressing can't be used.
105+
* If a MOVE.B instruction faults during a receive operation, we assume the
106+
* target sent nothing and try again. That assumption probably depends on
107+
* target firmware but it seems to hold up okay. If a fault happens during a
108+
* send operation, the target may or may not have seen /ACK and got the byte.
109+
* It's uncertain so the whole SCSI command gets retried.
110+
*
111+
* The NOP is needed for synchronization because the fault address in the
112+
* exception stack frame may or may not be the instruction that actually
113+
* caused the bus error. Post-increment addressing can't be used.
110114
*/
111115

112116
#define MOVE_BYTE(operands) \
@@ -243,22 +247,21 @@ static inline int mac_pdma_send(unsigned char *start, void __iomem *io, int n)
243247
if (n >= 1) {
244248
MOVE_BYTE("%0@,%3@");
245249
if (result)
246-
goto out;
250+
return -1;
247251
}
248252
if (n >= 1 && ((unsigned long)addr & 1)) {
249253
MOVE_BYTE("%0@,%3@");
250254
if (result)
251-
goto out;
255+
return -2;
252256
}
253257
while (n >= 32)
254258
MOVE_16_WORDS("%0@+,%3@");
255259
while (n >= 2)
256260
MOVE_WORD("%0@+,%3@");
257261
if (result)
258-
return start - addr; /* Negated to indicate uncertain length */
262+
return start - addr - 1; /* Negated to indicate uncertain length */
259263
if (n == 1)
260264
MOVE_BYTE("%0@,%3@");
261-
out:
262265
return addr - start;
263266
}
264267

@@ -307,7 +310,6 @@ static inline int macscsi_pread(struct NCR5380_hostdata *hostdata,
307310
{
308311
u8 __iomem *s = hostdata->pdma_io + (INPUT_DATA_REG << 4);
309312
unsigned char *d = dst;
310-
int result = 0;
311313

312314
hostdata->pdma_residual = len;
313315

@@ -343,19 +345,19 @@ static inline int macscsi_pread(struct NCR5380_hostdata *hostdata,
343345
if (bytes == 0)
344346
continue;
345347

346-
result = -1;
348+
if (macscsi_wait_for_drq(hostdata) <= 0)
349+
set_host_byte(hostdata->connected, DID_ERROR);
347350
break;
348351
}
349352

350-
return result;
353+
return 0;
351354
}
352355

353356
static inline int macscsi_pwrite(struct NCR5380_hostdata *hostdata,
354357
unsigned char *src, int len)
355358
{
356359
unsigned char *s = src;
357360
u8 __iomem *d = hostdata->pdma_io + (OUTPUT_DATA_REG << 4);
358-
int result = 0;
359361

360362
hostdata->pdma_residual = len;
361363

@@ -377,17 +379,8 @@ static inline int macscsi_pwrite(struct NCR5380_hostdata *hostdata,
377379
hostdata->pdma_residual -= bytes;
378380
}
379381

380-
if (hostdata->pdma_residual == 0) {
381-
if (NCR5380_poll_politely(hostdata, TARGET_COMMAND_REG,
382-
TCR_LAST_BYTE_SENT,
383-
TCR_LAST_BYTE_SENT,
384-
0) < 0) {
385-
scmd_printk(KERN_ERR, hostdata->connected,
386-
"%s: Last Byte Sent timeout\n", __func__);
387-
result = -1;
388-
}
382+
if (hostdata->pdma_residual == 0)
389383
break;
390-
}
391384

392385
if (bytes > 0)
393386
continue;
@@ -400,11 +393,12 @@ static inline int macscsi_pwrite(struct NCR5380_hostdata *hostdata,
400393
if (bytes == 0)
401394
continue;
402395

403-
result = -1;
396+
if (macscsi_wait_for_drq(hostdata) <= 0)
397+
set_host_byte(hostdata->connected, DID_ERROR);
404398
break;
405399
}
406400

407-
return result;
401+
return 0;
408402
}
409403

410404
static int macscsi_dma_xfer_len(struct NCR5380_hostdata *hostdata,

0 commit comments

Comments
 (0)