aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio
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
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')
-rw-r--r--drivers/s390/cio/qdio.h29
-rw-r--r--drivers/s390/cio/qdio_main.c6
-rw-r--r--drivers/s390/cio/qdio_thinint.c48
3 files changed, 50 insertions, 33 deletions
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index 498a4cd99ff8..b962ffbc0803 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -418,32 +418,6 @@ static inline int multicast_outbound(struct qdio_q *q)
418#define queue_irqs_disabled(q) \ 418#define queue_irqs_disabled(q) \
419 (test_bit(QDIO_QUEUE_IRQS_DISABLED, &q->u.in.queue_irq_state) != 0) 419 (test_bit(QDIO_QUEUE_IRQS_DISABLED, &q->u.in.queue_irq_state) != 0)
420 420
421#define TIQDIO_SHARED_IND 63
422
423/* device state change indicators */
424struct indicator_t {
425 u32 ind; /* u32 because of compare-and-swap performance */
426 atomic_t count; /* use count, 0 or 1 for non-shared indicators */
427};
428
429extern struct indicator_t *q_indicators;
430
431static inline int has_multiple_inq_on_dsci(struct qdio_irq *irq)
432{
433 return irq->nr_input_qs > 1;
434}
435
436static inline int references_shared_dsci(struct qdio_irq *irq)
437{
438 return irq->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
439}
440
441static inline int shared_ind(struct qdio_q *q)
442{
443 struct qdio_irq *i = q->irq_ptr;
444 return references_shared_dsci(i) || has_multiple_inq_on_dsci(i);
445}
446
447extern u64 last_ai_time; 421extern u64 last_ai_time;
448 422
449/* prototypes for thin interrupt */ 423/* prototypes for thin interrupt */
@@ -457,7 +431,8 @@ int tiqdio_allocate_memory(void);
457void tiqdio_free_memory(void); 431void tiqdio_free_memory(void);
458int tiqdio_register_thinints(void); 432int tiqdio_register_thinints(void);
459void tiqdio_unregister_thinints(void); 433void tiqdio_unregister_thinints(void);
460 434void clear_nonshared_ind(struct qdio_irq *);
435int test_nonshared_ind(struct qdio_irq *);
461 436
462/* prototypes for setup */ 437/* prototypes for setup */
463void qdio_inbound_processing(unsigned long data); 438void qdio_inbound_processing(unsigned long data);
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index 2fcdc0b2f0aa..3ef8d071c64a 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -1719,9 +1719,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr)
1719 1719
1720 WARN_ON(queue_irqs_enabled(q)); 1720 WARN_ON(queue_irqs_enabled(q));
1721 1721
1722 if (!shared_ind(q)) 1722 clear_nonshared_ind(irq_ptr);
1723 xchg(q->irq_ptr->dsci, 0);
1724
1725 qdio_stop_polling(q); 1723 qdio_stop_polling(q);
1726 clear_bit(QDIO_QUEUE_IRQS_DISABLED, &q->u.in.queue_irq_state); 1724 clear_bit(QDIO_QUEUE_IRQS_DISABLED, &q->u.in.queue_irq_state);
1727 1725
@@ -1729,7 +1727,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr)
1729 * We need to check again to not lose initiative after 1727 * We need to check again to not lose initiative after
1730 * resetting the ACK state. 1728 * resetting the ACK state.
1731 */ 1729 */
1732 if (!shared_ind(q) && *q->irq_ptr->dsci) 1730 if (test_nonshared_ind(irq_ptr))
1733 goto rescan; 1731 goto rescan;
1734 if (!qdio_inbound_q_done(q)) 1732 if (!qdio_inbound_q_done(q))
1735 goto rescan; 1733 goto rescan;
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index 9d1e7efb5bb5..011eadea3ee4 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 /*