diff options
author | Jan Glauber <jang@linux.vnet.ibm.com> | 2009-03-26 10:24:26 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2009-03-26 10:24:20 -0400 |
commit | c38f96080955854e54df9cb392bc674e1ae330e1 (patch) | |
tree | 8bfc4019e8c52aaf40c9e76ca0372915c76c3a27 /drivers/s390 | |
parent | e4c14e2085cd32f61e9ffc47d5b20d4f5f7639f3 (diff) |
[S390] qdio: proper kill of qdio tasklets
The queue tasklets were stopped with tasklet_disable. Although tasklet_disable
prevents the tasklet from beeing executed it is still possible that a tasklet
is scheduled on a CPU at that point. A following qdio_establish calls
tasklet_init which clears the tasklet count and the tasklet state leading to
the following Oops:
<2>kernel BUG at kernel/softirq.c:392!
<4>illegal operation: 0001 [#1] SMP
<4>Modules linked in: iptable_filter ip_tables x_tables dm_round_robin dm_multipath scsi_dh sg sd_mod crc_t10dif nfs lockd nfs
_acl sunrpc fuse loop dm_mod qeth_l3 ipv6 zfcp qeth scsi_transport_fc qdio scsi_tgt scsi_mod chsc_sch ccwgroup dasd_eckd_mod dasdm
od ext3 mbcache jbd
<4>Supported: Yes
<4>CPU: 0 Not tainted 2.6.27.13-1.1.mz13-default #1
<4>Process blast.LzS_64 (pid: 16445, task: 000000006cc02538, ksp: 000000006cb67998)
<4>Krnl PSW : 0704c00180000000 00000000001399f4 (tasklet_action+0xc8/0x1d4)
<4> R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:3 CC:0 PM:0 EA:3
<4>Krnl GPRS: ffffffff00000030 0000000000000002 0000000000000002 fffffffffffffffe
<4> 000000000013aabe 00000000003b6a18 fffffffffffffffd 0000000000000000
<4> 00000000006705a8 000000007d0914a8 000000007d0914b0 000000007fecfd30
<4> 0000000000000000 00000000003b63e8 000000007fecfd90 000000007fecfd30
<4>Krnl Code: 00000000001399e8: b9200021 cgr %r2,%r1
<4> 00000000001399ec: a7740004 brc 7,1399f4
<4> 00000000001399f0: a7f40001 brc 15,1399f2
<4> >00000000001399f4: c0100027e8ee larl %r1,636bd0
<4> 00000000001399fa: bf1f1008 icm %r1,15,8(%r1)
<4> 00000000001399fe: a7840019 brc 8,139a30
<4> 0000000000139a02: c0300027e8ef larl %r3,636be0
<4> 0000000000139a08: e3c030000004 lg %r12,0(%r3)
<4>Call Trace:
<4>([<0000000000139c12>] tasklet_hi_action+0x112/0x1d4)
<4> [<000000000013aabe>] __do_softirq+0xde/0x1c4
<4> [<000000000010fa2e>] do_softirq+0x96/0xb0
<4> [<000000000013a8d8>] irq_exit+0x70/0xcc
<4> [<000000000010d1d8>] do_extint+0xf0/0x110
<4> [<0000000000113b10>] ext_no_vtime+0x16/0x1a
<4> [<000003e0000a3662>] ext3_dirty_inode+0xe6/0xe8 [ext3]
<4>([<00000000001f6cf2>] __mark_inode_dirty+0x52/0x1d4)
<4> [<000003e0000a44f0>] ext3_ordered_write_end+0x138/0x190 [ext3]
<4> [<000000000018d5ec>] generic_perform_write+0x174/0x230
<4> [<0000000000190144>] generic_file_buffered_write+0xb4/0x194
<4> [<0000000000190864>] __generic_file_aio_write_nolock+0x418/0x454
<4> [<0000000000190ee2>] generic_file_aio_write+0x76/0xe4
<4> [<000003e0000a05c2>] ext3_file_write+0x3e/0xc8 [ext3]
<4> [<00000000001cc2fe>] do_sync_write+0xd6/0x120
<4> [<00000000001ccfc8>] vfs_write+0xac/0x184
<4> [<00000000001cd218>] SyS_write+0x68/0xe0
<4> [<0000000000113402>] sysc_noemu+0x10/0x16
<4> [<0000020000043188>] 0x20000043188
<4>Last Breaking-Event-Address:
<4> [<00000000001399f0>] tasklet_action+0xc4/0x1d4
<6>qdio: 0.0.c61b ZFCP on SC f67 using AI:1 QEBSM:0 PCI:1 TDD:1 SIGA: W AOP
<4> <0>Kernel panic - not syncing: Fatal exception in interrupt
Use tasklet_kill instead of tasklet_disbale. Since tasklet_schedule must not be
called after tasklet_kill use the QDIO_IRQ_STATE_STOPPED to inidicate that a
queue is going down and prevent further tasklet schedules in that case.
Remove superflous tasklet_schedule from input queue setup, at that time
the queues are not ready so the schedule results in a NOP.
Signed-off-by: Jan Glauber <jang@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/cio/qdio_main.c | 35 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_thinint.c | 8 |
2 files changed, 28 insertions, 15 deletions
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index fa902703996c..1974ec7bf0ed 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c | |||
@@ -778,21 +778,17 @@ static void __qdio_outbound_processing(struct qdio_q *q) | |||
778 | 778 | ||
779 | spin_unlock_irqrestore(&q->lock, flags); | 779 | spin_unlock_irqrestore(&q->lock, flags); |
780 | 780 | ||
781 | if (queue_type(q) == QDIO_ZFCP_QFMT) { | 781 | if (queue_type(q) == QDIO_ZFCP_QFMT) |
782 | if (!pci_out_supported(q) && !qdio_outbound_q_done(q)) | 782 | if (!pci_out_supported(q) && !qdio_outbound_q_done(q)) |
783 | tasklet_schedule(&q->tasklet); | 783 | goto sched; |
784 | return; | ||
785 | } | ||
786 | 784 | ||
787 | /* bail out for HiperSockets unicast queues */ | 785 | /* bail out for HiperSockets unicast queues */ |
788 | if (queue_type(q) == QDIO_IQDIO_QFMT && !multicast_outbound(q)) | 786 | if (queue_type(q) == QDIO_IQDIO_QFMT && !multicast_outbound(q)) |
789 | return; | 787 | return; |
790 | 788 | ||
791 | if ((queue_type(q) == QDIO_IQDIO_QFMT) && | 789 | if ((queue_type(q) == QDIO_IQDIO_QFMT) && |
792 | (atomic_read(&q->nr_buf_used)) > QDIO_IQDIO_POLL_LVL) { | 790 | (atomic_read(&q->nr_buf_used)) > QDIO_IQDIO_POLL_LVL) |
793 | tasklet_schedule(&q->tasklet); | 791 | goto sched; |
794 | return; | ||
795 | } | ||
796 | 792 | ||
797 | if (q->u.out.pci_out_enabled) | 793 | if (q->u.out.pci_out_enabled) |
798 | return; | 794 | return; |
@@ -810,6 +806,12 @@ static void __qdio_outbound_processing(struct qdio_q *q) | |||
810 | qdio_perf_stat_inc(&perf_stats.debug_tl_out_timer); | 806 | qdio_perf_stat_inc(&perf_stats.debug_tl_out_timer); |
811 | } | 807 | } |
812 | } | 808 | } |
809 | return; | ||
810 | |||
811 | sched: | ||
812 | if (unlikely(q->irq_ptr->state == QDIO_IRQ_STATE_STOPPED)) | ||
813 | return; | ||
814 | tasklet_schedule(&q->tasklet); | ||
813 | } | 815 | } |
814 | 816 | ||
815 | /* outbound tasklet */ | 817 | /* outbound tasklet */ |
@@ -822,6 +824,9 @@ void qdio_outbound_processing(unsigned long data) | |||
822 | void qdio_outbound_timer(unsigned long data) | 824 | void qdio_outbound_timer(unsigned long data) |
823 | { | 825 | { |
824 | struct qdio_q *q = (struct qdio_q *)data; | 826 | struct qdio_q *q = (struct qdio_q *)data; |
827 | |||
828 | if (unlikely(q->irq_ptr->state == QDIO_IRQ_STATE_STOPPED)) | ||
829 | return; | ||
825 | tasklet_schedule(&q->tasklet); | 830 | tasklet_schedule(&q->tasklet); |
826 | } | 831 | } |
827 | 832 | ||
@@ -863,6 +868,9 @@ static void qdio_int_handler_pci(struct qdio_irq *irq_ptr) | |||
863 | int i; | 868 | int i; |
864 | struct qdio_q *q; | 869 | struct qdio_q *q; |
865 | 870 | ||
871 | if (unlikely(irq_ptr->state == QDIO_IRQ_STATE_STOPPED)) | ||
872 | return; | ||
873 | |||
866 | qdio_perf_stat_inc(&perf_stats.pci_int); | 874 | qdio_perf_stat_inc(&perf_stats.pci_int); |
867 | 875 | ||
868 | for_each_input_queue(irq_ptr, q, i) | 876 | for_each_input_queue(irq_ptr, q, i) |
@@ -1090,11 +1098,11 @@ static void qdio_shutdown_queues(struct ccw_device *cdev) | |||
1090 | int i; | 1098 | int i; |
1091 | 1099 | ||
1092 | for_each_input_queue(irq_ptr, q, i) | 1100 | for_each_input_queue(irq_ptr, q, i) |
1093 | tasklet_disable(&q->tasklet); | 1101 | tasklet_kill(&q->tasklet); |
1094 | 1102 | ||
1095 | for_each_output_queue(irq_ptr, q, i) { | 1103 | for_each_output_queue(irq_ptr, q, i) { |
1096 | tasklet_disable(&q->tasklet); | ||
1097 | del_timer(&q->u.out.timer); | 1104 | del_timer(&q->u.out.timer); |
1105 | tasklet_kill(&q->tasklet); | ||
1098 | } | 1106 | } |
1099 | } | 1107 | } |
1100 | 1108 | ||
@@ -1125,6 +1133,12 @@ int qdio_shutdown(struct ccw_device *cdev, int how) | |||
1125 | return 0; | 1133 | return 0; |
1126 | } | 1134 | } |
1127 | 1135 | ||
1136 | /* | ||
1137 | * Indicate that the device is going down. Scheduling the queue | ||
1138 | * tasklets is forbidden from here on. | ||
1139 | */ | ||
1140 | qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED); | ||
1141 | |||
1128 | tiqdio_remove_input_queues(irq_ptr); | 1142 | tiqdio_remove_input_queues(irq_ptr); |
1129 | qdio_shutdown_queues(cdev); | 1143 | qdio_shutdown_queues(cdev); |
1130 | qdio_shutdown_debug_entries(irq_ptr, cdev); | 1144 | qdio_shutdown_debug_entries(irq_ptr, cdev); |
@@ -1556,7 +1570,6 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags, | |||
1556 | qdio_perf_stat_inc(&perf_stats.fast_requeue); | 1570 | qdio_perf_stat_inc(&perf_stats.fast_requeue); |
1557 | } | 1571 | } |
1558 | out: | 1572 | out: |
1559 | /* Fixme: could wait forever if called from process context */ | ||
1560 | tasklet_schedule(&q->tasklet); | 1573 | tasklet_schedule(&q->tasklet); |
1561 | } | 1574 | } |
1562 | 1575 | ||
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 981044c83864..c7c5512a892e 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c | |||
@@ -101,7 +101,6 @@ void tiqdio_add_input_queues(struct qdio_irq *irq_ptr) | |||
101 | list_add_rcu(&q->entry, &tiq_list); | 101 | list_add_rcu(&q->entry, &tiq_list); |
102 | mutex_unlock(&tiq_list_lock); | 102 | mutex_unlock(&tiq_list_lock); |
103 | xchg(irq_ptr->dsci, 1); | 103 | xchg(irq_ptr->dsci, 1); |
104 | tasklet_schedule(&tiqdio_tasklet); | ||
105 | } | 104 | } |
106 | 105 | ||
107 | /* | 106 | /* |
@@ -159,7 +158,6 @@ static void __tiqdio_inbound_processing(struct qdio_q *q) | |||
159 | */ | 158 | */ |
160 | qdio_check_outbound_after_thinint(q); | 159 | qdio_check_outbound_after_thinint(q); |
161 | 160 | ||
162 | again: | ||
163 | if (!qdio_inbound_q_moved(q)) | 161 | if (!qdio_inbound_q_moved(q)) |
164 | return; | 162 | return; |
165 | 163 | ||
@@ -167,7 +165,8 @@ again: | |||
167 | 165 | ||
168 | if (!tiqdio_inbound_q_done(q)) { | 166 | if (!tiqdio_inbound_q_done(q)) { |
169 | qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop); | 167 | qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop); |
170 | goto again; | 168 | if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED)) |
169 | tasklet_schedule(&q->tasklet); | ||
171 | } | 170 | } |
172 | 171 | ||
173 | qdio_stop_polling(q); | 172 | qdio_stop_polling(q); |
@@ -177,7 +176,8 @@ again: | |||
177 | */ | 176 | */ |
178 | if (!tiqdio_inbound_q_done(q)) { | 177 | if (!tiqdio_inbound_q_done(q)) { |
179 | qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop2); | 178 | qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop2); |
180 | goto again; | 179 | if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED)) |
180 | tasklet_schedule(&q->tasklet); | ||
181 | } | 181 | } |
182 | } | 182 | } |
183 | 183 | ||