aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio/qdio_thinint.c
diff options
context:
space:
mode:
authorJan Glauber <jang@linux.vnet.ibm.com>2011-10-30 10:17:20 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2011-10-30 10:16:47 -0400
commit5f4026f8b2e4c5e26713d6c707592a33326a88c4 (patch)
tree9819bcca00f6de5f265cd48523f9009a04d303ef /drivers/s390/cio/qdio_thinint.c
parent2a3a2d66aa4e5abaf8f9222d21735321f02a00dc (diff)
[S390] qdio: prevent dsci access without adapter interrupts
A kernel panic may occur during sending or receiving network packets on a machine without adapter interrupts since commit d36deae. The bug is triggered by writing to the shared indicator address which is set to 0 if the machine doesn't have adapter interrupts. Make the reading and setting of the shared indicator dependent on the adapter interrupt feature and while at it move the code to the file containing the adapter interrupt related code. Thanks to Jan Jaeger for tracking this down. Reported-by: Jan Jaeger <jan.jaeger@westnet.com.au> Tested-by: Jan Jaeger <jan.jaeger@westnet.com.au> Signed-off-by: Jan Glauber <jang@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio/qdio_thinint.c')
-rw-r--r--drivers/s390/cio/qdio_thinint.c48
1 files changed, 46 insertions, 2 deletions
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index 9d1e7efb5bb..011eadea3ee 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -26,6 +26,13 @@
26 */ 26 */
27#define TIQDIO_NR_NONSHARED_IND 63 27#define TIQDIO_NR_NONSHARED_IND 63
28#define TIQDIO_NR_INDICATORS (TIQDIO_NR_NONSHARED_IND + 1) 28#define TIQDIO_NR_INDICATORS (TIQDIO_NR_NONSHARED_IND + 1)
29#define TIQDIO_SHARED_IND 63
30
31/* device state change indicators */
32struct indicator_t {
33 u32 ind; /* u32 because of compare-and-swap performance */
34 atomic_t count; /* use count, 0 or 1 for non-shared indicators */
35};
29 36
30/* list of thin interrupt input queues */ 37/* list of thin interrupt input queues */
31static LIST_HEAD(tiq_list); 38static LIST_HEAD(tiq_list);
@@ -34,7 +41,7 @@ static DEFINE_MUTEX(tiq_list_lock);
34/* adapter local summary indicator */ 41/* adapter local summary indicator */
35static u8 *tiqdio_alsi; 42static u8 *tiqdio_alsi;
36 43
37struct indicator_t *q_indicators; 44static struct indicator_t *q_indicators;
38 45
39u64 last_ai_time; 46u64 last_ai_time;
40 47
@@ -90,6 +97,43 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
90 synchronize_rcu(); 97 synchronize_rcu();
91} 98}
92 99
100static inline int has_multiple_inq_on_dsci(struct qdio_irq *irq_ptr)
101{
102 return irq_ptr->nr_input_qs > 1;
103}
104
105static inline int references_shared_dsci(struct qdio_irq *irq_ptr)
106{
107 return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
108}
109
110static inline int shared_ind(struct qdio_irq *irq_ptr)
111{
112 return references_shared_dsci(irq_ptr) ||
113 has_multiple_inq_on_dsci(irq_ptr);
114}
115
116void clear_nonshared_ind(struct qdio_irq *irq_ptr)
117{
118 if (!is_thinint_irq(irq_ptr))
119 return;
120 if (shared_ind(irq_ptr))
121 return;
122 xchg(irq_ptr->dsci, 0);
123}
124
125int test_nonshared_ind(struct qdio_irq *irq_ptr)
126{
127 if (!is_thinint_irq(irq_ptr))
128 return 0;
129 if (shared_ind(irq_ptr))
130 return 0;
131 if (*irq_ptr->dsci)
132 return 1;
133 else
134 return 0;
135}
136
93static inline u32 clear_shared_ind(void) 137static inline u32 clear_shared_ind(void)
94{ 138{
95 if (!atomic_read(&q_indicators[TIQDIO_SHARED_IND].count)) 139 if (!atomic_read(&q_indicators[TIQDIO_SHARED_IND].count))
@@ -119,7 +163,7 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq)
119 q->u.in.queue_start_poll(q->irq_ptr->cdev, q->nr, 163 q->u.in.queue_start_poll(q->irq_ptr->cdev, q->nr,
120 q->irq_ptr->int_parm); 164 q->irq_ptr->int_parm);
121 } else { 165 } else {
122 if (!shared_ind(q)) 166 if (!shared_ind(q->irq_ptr))
123 xchg(q->irq_ptr->dsci, 0); 167 xchg(q->irq_ptr->dsci, 0);
124 168
125 /* 169 /*