diff options
author | Finn Thain <fthain@telegraphics.com.au> | 2014-11-12 00:12:08 -0500 |
---|---|---|
committer | Christoph Hellwig <hch@lst.de> | 2014-11-20 03:11:13 -0500 |
commit | 16b29e75a78ae03250233468b68f7ae467d3dc7a (patch) | |
tree | a5ba9e016d5557d2a6f5fb60dc83ae906ae771af | |
parent | cbad48deb38d8e442db9760ca1f950cd24429707 (diff) |
atari_scsi: Fix atari_scsi deadlocks on Falcon
Don't disable irqs when waiting for the ST DMA "lock"; its release may
require an interrupt.
Introduce stdma_try_lock() for use in soft irq context. atari_scsi now tells
the SCSI mid-layer to defer queueing a command if the ST DMA lock is not
available, as per Michael's patch:
http://marc.info/?l=linux-m68k&m=139095335824863&w=2
The falcon_got_lock variable is race prone: we can't disable IRQs while
waiting to acquire the lock, so after acquiring it there must be some
interval during which falcon_got_lock remains false. Introduce
stdma_is_locked_by() to replace falcon_got_lock.
The falcon_got_lock tests in the EH handlers are incorrect these days. It
can happen that an EH handler is called after a command completes normally.
Remove these checks along with falcon_got_lock.
Also remove the complicated and racy fairness wait queues. If fairness is an
issue (when SCSI competes with IDE for the ST DMA interrupt), the solution
is likely to be a lower value for host->can_queue.
Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Tested-by: Michael Schmitz <schmitzmic@gmail.com>
Acked-by: Geert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: Christoph Hellwig <hch@lst.de>
-rw-r--r-- | arch/m68k/atari/stdma.c | 61 | ||||
-rw-r--r-- | arch/m68k/include/asm/atari_stdma.h | 4 | ||||
-rw-r--r-- | drivers/scsi/atari_NCR5380.c | 33 | ||||
-rw-r--r-- | drivers/scsi/atari_scsi.c | 75 |
4 files changed, 66 insertions, 107 deletions
diff --git a/arch/m68k/atari/stdma.c b/arch/m68k/atari/stdma.c index ddbf43ca8858..e5a66596b116 100644 --- a/arch/m68k/atari/stdma.c +++ b/arch/m68k/atari/stdma.c | |||
@@ -59,6 +59,31 @@ static irqreturn_t stdma_int (int irq, void *dummy); | |||
59 | /************************* End of Prototypes **************************/ | 59 | /************************* End of Prototypes **************************/ |
60 | 60 | ||
61 | 61 | ||
62 | /** | ||
63 | * stdma_try_lock - attempt to acquire ST DMA interrupt "lock" | ||
64 | * @handler: interrupt handler to use after acquisition | ||
65 | * | ||
66 | * Returns !0 if lock was acquired; otherwise 0. | ||
67 | */ | ||
68 | |||
69 | int stdma_try_lock(irq_handler_t handler, void *data) | ||
70 | { | ||
71 | unsigned long flags; | ||
72 | |||
73 | local_irq_save(flags); | ||
74 | if (stdma_locked) { | ||
75 | local_irq_restore(flags); | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | stdma_locked = 1; | ||
80 | stdma_isr = handler; | ||
81 | stdma_isr_data = data; | ||
82 | local_irq_restore(flags); | ||
83 | return 1; | ||
84 | } | ||
85 | EXPORT_SYMBOL(stdma_try_lock); | ||
86 | |||
62 | 87 | ||
63 | /* | 88 | /* |
64 | * Function: void stdma_lock( isrfunc isr, void *data ) | 89 | * Function: void stdma_lock( isrfunc isr, void *data ) |
@@ -78,19 +103,10 @@ static irqreturn_t stdma_int (int irq, void *dummy); | |||
78 | 103 | ||
79 | void stdma_lock(irq_handler_t handler, void *data) | 104 | void stdma_lock(irq_handler_t handler, void *data) |
80 | { | 105 | { |
81 | unsigned long flags; | ||
82 | |||
83 | local_irq_save(flags); /* protect lock */ | ||
84 | |||
85 | /* Since the DMA is used for file system purposes, we | 106 | /* Since the DMA is used for file system purposes, we |
86 | have to sleep uninterruptible (there may be locked | 107 | have to sleep uninterruptible (there may be locked |
87 | buffers) */ | 108 | buffers) */ |
88 | wait_event(stdma_wait, !stdma_locked); | 109 | wait_event(stdma_wait, stdma_try_lock(handler, data)); |
89 | |||
90 | stdma_locked = 1; | ||
91 | stdma_isr = handler; | ||
92 | stdma_isr_data = data; | ||
93 | local_irq_restore(flags); | ||
94 | } | 110 | } |
95 | EXPORT_SYMBOL(stdma_lock); | 111 | EXPORT_SYMBOL(stdma_lock); |
96 | 112 | ||
@@ -122,22 +138,25 @@ void stdma_release(void) | |||
122 | EXPORT_SYMBOL(stdma_release); | 138 | EXPORT_SYMBOL(stdma_release); |
123 | 139 | ||
124 | 140 | ||
125 | /* | 141 | /** |
126 | * Function: int stdma_others_waiting( void ) | 142 | * stdma_is_locked_by - allow lock holder to check whether it needs to release. |
127 | * | 143 | * @handler: interrupt handler previously used to acquire lock. |
128 | * Purpose: Check if someone waits for the ST-DMA lock. | ||
129 | * | ||
130 | * Inputs: none | ||
131 | * | ||
132 | * Returns: 0 if no one is waiting, != 0 otherwise | ||
133 | * | 144 | * |
145 | * Returns !0 if locked for the given handler; 0 otherwise. | ||
134 | */ | 146 | */ |
135 | 147 | ||
136 | int stdma_others_waiting(void) | 148 | int stdma_is_locked_by(irq_handler_t handler) |
137 | { | 149 | { |
138 | return waitqueue_active(&stdma_wait); | 150 | unsigned long flags; |
151 | int result; | ||
152 | |||
153 | local_irq_save(flags); | ||
154 | result = stdma_locked && (stdma_isr == handler); | ||
155 | local_irq_restore(flags); | ||
156 | |||
157 | return result; | ||
139 | } | 158 | } |
140 | EXPORT_SYMBOL(stdma_others_waiting); | 159 | EXPORT_SYMBOL(stdma_is_locked_by); |
141 | 160 | ||
142 | 161 | ||
143 | /* | 162 | /* |
diff --git a/arch/m68k/include/asm/atari_stdma.h b/arch/m68k/include/asm/atari_stdma.h index 8e389b7fa70c..d24e34d870dc 100644 --- a/arch/m68k/include/asm/atari_stdma.h +++ b/arch/m68k/include/asm/atari_stdma.h | |||
@@ -8,11 +8,11 @@ | |||
8 | 8 | ||
9 | /***************************** Prototypes *****************************/ | 9 | /***************************** Prototypes *****************************/ |
10 | 10 | ||
11 | int stdma_try_lock(irq_handler_t, void *); | ||
11 | void stdma_lock(irq_handler_t handler, void *data); | 12 | void stdma_lock(irq_handler_t handler, void *data); |
12 | void stdma_release( void ); | 13 | void stdma_release( void ); |
13 | int stdma_others_waiting( void ); | ||
14 | int stdma_islocked( void ); | 14 | int stdma_islocked( void ); |
15 | void *stdma_locked_by( void ); | 15 | int stdma_is_locked_by(irq_handler_t); |
16 | void stdma_init( void ); | 16 | void stdma_init( void ); |
17 | 17 | ||
18 | /************************* End of Prototypes **************************/ | 18 | /************************* End of Prototypes **************************/ |
diff --git a/drivers/scsi/atari_NCR5380.c b/drivers/scsi/atari_NCR5380.c index 4eeb9684977b..7a6f90ce0316 100644 --- a/drivers/scsi/atari_NCR5380.c +++ b/drivers/scsi/atari_NCR5380.c | |||
@@ -879,10 +879,10 @@ static void NCR5380_exit(struct Scsi_Host *instance) | |||
879 | * | 879 | * |
880 | */ | 880 | */ |
881 | 881 | ||
882 | static int NCR5380_queue_command_lck(struct scsi_cmnd *cmd, | 882 | static int NCR5380_queue_command(struct Scsi_Host *instance, |
883 | void (*done)(struct scsi_cmnd *)) | 883 | struct scsi_cmnd *cmd) |
884 | { | 884 | { |
885 | SETUP_HOSTDATA(cmd->device->host); | 885 | struct NCR5380_hostdata *hostdata = shost_priv(instance); |
886 | struct scsi_cmnd *tmp; | 886 | struct scsi_cmnd *tmp; |
887 | unsigned long flags; | 887 | unsigned long flags; |
888 | 888 | ||
@@ -893,7 +893,7 @@ static int NCR5380_queue_command_lck(struct scsi_cmnd *cmd, | |||
893 | printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n", | 893 | printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n", |
894 | H_NO(cmd)); | 894 | H_NO(cmd)); |
895 | cmd->result = (DID_ERROR << 16); | 895 | cmd->result = (DID_ERROR << 16); |
896 | done(cmd); | 896 | cmd->scsi_done(cmd); |
897 | return 0; | 897 | return 0; |
898 | } | 898 | } |
899 | #endif /* (NDEBUG & NDEBUG_NO_WRITE) */ | 899 | #endif /* (NDEBUG & NDEBUG_NO_WRITE) */ |
@@ -904,8 +904,6 @@ static int NCR5380_queue_command_lck(struct scsi_cmnd *cmd, | |||
904 | */ | 904 | */ |
905 | 905 | ||
906 | SET_NEXT(cmd, NULL); | 906 | SET_NEXT(cmd, NULL); |
907 | cmd->scsi_done = done; | ||
908 | |||
909 | cmd->result = 0; | 907 | cmd->result = 0; |
910 | 908 | ||
911 | /* | 909 | /* |
@@ -915,7 +913,6 @@ static int NCR5380_queue_command_lck(struct scsi_cmnd *cmd, | |||
915 | * sense data is only guaranteed to be valid while the condition exists. | 913 | * sense data is only guaranteed to be valid while the condition exists. |
916 | */ | 914 | */ |
917 | 915 | ||
918 | local_irq_save(flags); | ||
919 | /* ++guenther: now that the issue queue is being set up, we can lock ST-DMA. | 916 | /* ++guenther: now that the issue queue is being set up, we can lock ST-DMA. |
920 | * Otherwise a running NCR5380_main may steal the lock. | 917 | * Otherwise a running NCR5380_main may steal the lock. |
921 | * Lock before actually inserting due to fairness reasons explained in | 918 | * Lock before actually inserting due to fairness reasons explained in |
@@ -928,11 +925,11 @@ static int NCR5380_queue_command_lck(struct scsi_cmnd *cmd, | |||
928 | * because also a timer int can trigger an abort or reset, which would | 925 | * because also a timer int can trigger an abort or reset, which would |
929 | * alter queues and touch the lock. | 926 | * alter queues and touch the lock. |
930 | */ | 927 | */ |
931 | if (!IS_A_TT()) { | 928 | if (!falcon_get_lock()) |
932 | /* perhaps stop command timer here */ | 929 | return SCSI_MLQUEUE_HOST_BUSY; |
933 | falcon_get_lock(); | 930 | |
934 | /* perhaps restart command timer here */ | 931 | local_irq_save(flags); |
935 | } | 932 | |
936 | if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) { | 933 | if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) { |
937 | LIST(cmd, hostdata->issue_queue); | 934 | LIST(cmd, hostdata->issue_queue); |
938 | SET_NEXT(cmd, hostdata->issue_queue); | 935 | SET_NEXT(cmd, hostdata->issue_queue); |
@@ -956,15 +953,13 @@ static int NCR5380_queue_command_lck(struct scsi_cmnd *cmd, | |||
956 | * If we're not in an interrupt, we can call NCR5380_main() | 953 | * If we're not in an interrupt, we can call NCR5380_main() |
957 | * unconditionally, because it cannot be already running. | 954 | * unconditionally, because it cannot be already running. |
958 | */ | 955 | */ |
959 | if (in_interrupt() || ((flags >> 8) & 7) >= 6) | 956 | if (in_interrupt() || irqs_disabled()) |
960 | queue_main(); | 957 | queue_main(); |
961 | else | 958 | else |
962 | NCR5380_main(NULL); | 959 | NCR5380_main(NULL); |
963 | return 0; | 960 | return 0; |
964 | } | 961 | } |
965 | 962 | ||
966 | static DEF_SCSI_QCMD(NCR5380_queue_command) | ||
967 | |||
968 | /* | 963 | /* |
969 | * Function : NCR5380_main (void) | 964 | * Function : NCR5380_main (void) |
970 | * | 965 | * |
@@ -2554,10 +2549,6 @@ int NCR5380_abort(struct scsi_cmnd *cmd) | |||
2554 | 2549 | ||
2555 | local_irq_save(flags); | 2550 | local_irq_save(flags); |
2556 | 2551 | ||
2557 | if (!IS_A_TT() && !falcon_got_lock) | ||
2558 | printk(KERN_ERR "scsi%d: !!BINGO!! Falcon has no lock in NCR5380_abort\n", | ||
2559 | HOSTNO); | ||
2560 | |||
2561 | dprintk(NDEBUG_ABORT, "scsi%d: abort called basr 0x%02x, sr 0x%02x\n", HOSTNO, | 2552 | dprintk(NDEBUG_ABORT, "scsi%d: abort called basr 0x%02x, sr 0x%02x\n", HOSTNO, |
2562 | NCR5380_read(BUS_AND_STATUS_REG), | 2553 | NCR5380_read(BUS_AND_STATUS_REG), |
2563 | NCR5380_read(STATUS_REG)); | 2554 | NCR5380_read(STATUS_REG)); |
@@ -2756,10 +2747,6 @@ static int NCR5380_bus_reset(struct scsi_cmnd *cmd) | |||
2756 | struct scsi_cmnd *connected, *disconnected_queue; | 2747 | struct scsi_cmnd *connected, *disconnected_queue; |
2757 | #endif | 2748 | #endif |
2758 | 2749 | ||
2759 | if (!IS_A_TT() && !falcon_got_lock) | ||
2760 | printk(KERN_ERR "scsi%d: !!BINGO!! Falcon has no lock in NCR5380_reset\n", | ||
2761 | H_NO(cmd)); | ||
2762 | |||
2763 | NCR5380_print_status(cmd->device->host); | 2750 | NCR5380_print_status(cmd->device->host); |
2764 | 2751 | ||
2765 | /* get in phase */ | 2752 | /* get in phase */ |
diff --git a/drivers/scsi/atari_scsi.c b/drivers/scsi/atari_scsi.c index 48fabebdbbb0..b2e86d0630ce 100644 --- a/drivers/scsi/atari_scsi.c +++ b/drivers/scsi/atari_scsi.c | |||
@@ -184,7 +184,7 @@ static void atari_scsi_fetch_restbytes(void); | |||
184 | static irqreturn_t scsi_tt_intr(int irq, void *dummy); | 184 | static irqreturn_t scsi_tt_intr(int irq, void *dummy); |
185 | static irqreturn_t scsi_falcon_intr(int irq, void *dummy); | 185 | static irqreturn_t scsi_falcon_intr(int irq, void *dummy); |
186 | static void falcon_release_lock_if_possible(struct NCR5380_hostdata *hostdata); | 186 | static void falcon_release_lock_if_possible(struct NCR5380_hostdata *hostdata); |
187 | static void falcon_get_lock(void); | 187 | static int falcon_get_lock(void); |
188 | #ifdef CONFIG_ATARI_SCSI_RESET_BOOT | 188 | #ifdef CONFIG_ATARI_SCSI_RESET_BOOT |
189 | static void atari_scsi_reset_boot(void); | 189 | static void atari_scsi_reset_boot(void); |
190 | #endif | 190 | #endif |
@@ -473,17 +473,10 @@ static void atari_scsi_fetch_restbytes(void) | |||
473 | #endif /* REAL_DMA */ | 473 | #endif /* REAL_DMA */ |
474 | 474 | ||
475 | 475 | ||
476 | static int falcon_got_lock = 0; | ||
477 | static DECLARE_WAIT_QUEUE_HEAD(falcon_fairness_wait); | ||
478 | static int falcon_trying_lock = 0; | ||
479 | static DECLARE_WAIT_QUEUE_HEAD(falcon_try_wait); | ||
480 | static int falcon_dont_release = 0; | 476 | static int falcon_dont_release = 0; |
481 | 477 | ||
482 | /* This function releases the lock on the DMA chip if there is no | 478 | /* This function releases the lock on the DMA chip if there is no |
483 | * connected command and the disconnected queue is empty. On | 479 | * connected command and the disconnected queue is empty. |
484 | * releasing, instances of falcon_get_lock are awoken, that put | ||
485 | * themselves to sleep for fairness. They can now try to get the lock | ||
486 | * again (but others waiting longer more probably will win). | ||
487 | */ | 480 | */ |
488 | 481 | ||
489 | static void falcon_release_lock_if_possible(struct NCR5380_hostdata *hostdata) | 482 | static void falcon_release_lock_if_possible(struct NCR5380_hostdata *hostdata) |
@@ -495,20 +488,12 @@ static void falcon_release_lock_if_possible(struct NCR5380_hostdata *hostdata) | |||
495 | 488 | ||
496 | local_irq_save(flags); | 489 | local_irq_save(flags); |
497 | 490 | ||
498 | if (falcon_got_lock && !hostdata->disconnected_queue && | 491 | if (!hostdata->disconnected_queue && |
499 | !hostdata->issue_queue && !hostdata->connected) { | 492 | !hostdata->issue_queue && |
500 | 493 | !hostdata->connected && | |
501 | if (falcon_dont_release) { | 494 | !falcon_dont_release && |
502 | #if 0 | 495 | stdma_is_locked_by(scsi_falcon_intr)) |
503 | printk("WARNING: Lock release not allowed. Ignored\n"); | ||
504 | #endif | ||
505 | local_irq_restore(flags); | ||
506 | return; | ||
507 | } | ||
508 | falcon_got_lock = 0; | ||
509 | stdma_release(); | 496 | stdma_release(); |
510 | wake_up(&falcon_fairness_wait); | ||
511 | } | ||
512 | 497 | ||
513 | local_irq_restore(flags); | 498 | local_irq_restore(flags); |
514 | } | 499 | } |
@@ -517,51 +502,19 @@ static void falcon_release_lock_if_possible(struct NCR5380_hostdata *hostdata) | |||
517 | * If the DMA isn't locked already for SCSI, it tries to lock it by | 502 | * If the DMA isn't locked already for SCSI, it tries to lock it by |
518 | * calling stdma_lock(). But if the DMA is locked by the SCSI code and | 503 | * calling stdma_lock(). But if the DMA is locked by the SCSI code and |
519 | * there are other drivers waiting for the chip, we do not issue the | 504 | * there are other drivers waiting for the chip, we do not issue the |
520 | * command immediately but wait on 'falcon_fairness_queue'. We will be | 505 | * command immediately but tell the SCSI mid-layer to defer. |
521 | * waked up when the DMA is unlocked by some SCSI interrupt. After that | ||
522 | * we try to get the lock again. | ||
523 | * But we must be prepared that more than one instance of | ||
524 | * falcon_get_lock() is waiting on the fairness queue. They should not | ||
525 | * try all at once to call stdma_lock(), one is enough! For that, the | ||
526 | * first one sets 'falcon_trying_lock', others that see that variable | ||
527 | * set wait on the queue 'falcon_try_wait'. | ||
528 | * Complicated, complicated.... Sigh... | ||
529 | */ | 506 | */ |
530 | 507 | ||
531 | static void falcon_get_lock(void) | 508 | static int falcon_get_lock(void) |
532 | { | 509 | { |
533 | unsigned long flags; | ||
534 | |||
535 | if (IS_A_TT()) | 510 | if (IS_A_TT()) |
536 | return; | 511 | return 1; |
537 | 512 | ||
538 | local_irq_save(flags); | 513 | if (in_interrupt()) |
514 | return stdma_try_lock(scsi_falcon_intr, NULL); | ||
539 | 515 | ||
540 | wait_event_cmd(falcon_fairness_wait, | 516 | stdma_lock(scsi_falcon_intr, NULL); |
541 | in_interrupt() || !falcon_got_lock || !stdma_others_waiting(), | 517 | return 1; |
542 | local_irq_restore(flags), | ||
543 | local_irq_save(flags)); | ||
544 | |||
545 | while (!falcon_got_lock) { | ||
546 | if (in_irq()) | ||
547 | panic("Falcon SCSI hasn't ST-DMA lock in interrupt"); | ||
548 | if (!falcon_trying_lock) { | ||
549 | falcon_trying_lock = 1; | ||
550 | stdma_lock(scsi_falcon_intr, NULL); | ||
551 | falcon_got_lock = 1; | ||
552 | falcon_trying_lock = 0; | ||
553 | wake_up(&falcon_try_wait); | ||
554 | } else { | ||
555 | wait_event_cmd(falcon_try_wait, | ||
556 | falcon_got_lock && !falcon_trying_lock, | ||
557 | local_irq_restore(flags), | ||
558 | local_irq_save(flags)); | ||
559 | } | ||
560 | } | ||
561 | |||
562 | local_irq_restore(flags); | ||
563 | if (!falcon_got_lock) | ||
564 | panic("Falcon SCSI: someone stole the lock :-(\n"); | ||
565 | } | 518 | } |
566 | 519 | ||
567 | 520 | ||