aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Glauber <jang@linux.vnet.ibm.com>2009-03-26 10:24:26 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2009-03-26 10:24:20 -0400
commitc38f96080955854e54df9cb392bc674e1ae330e1 (patch)
tree8bfc4019e8c52aaf40c9e76ca0372915c76c3a27
parente4c14e2085cd32f61e9ffc47d5b20d4f5f7639f3 (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>
-rw-r--r--drivers/s390/cio/qdio_main.c35
-rw-r--r--drivers/s390/cio/qdio_thinint.c8
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
811sched:
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)
822void qdio_outbound_timer(unsigned long data) 824void 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 }
1558out: 1572out:
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
162again:
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