[S390] qdio: prevent dsci access without adapter interrupts
authorJan Glauber <jang@linux.vnet.ibm.com>
Sun, 30 Oct 2011 14:17:20 +0000 (15:17 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Sun, 30 Oct 2011 14:16:47 +0000 (15:16 +0100)
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>
drivers/s390/cio/qdio.h
drivers/s390/cio/qdio_main.c
drivers/s390/cio/qdio_thinint.c

index 498a4cd99ff8d53e196f9570ced6e381cd873fb3..b962ffbc0803dead8c7efb3e49bd4f5d903bc6a4 100644 (file)
@@ -418,32 +418,6 @@ static inline int multicast_outbound(struct qdio_q *q)
 #define queue_irqs_disabled(q)                 \
        (test_bit(QDIO_QUEUE_IRQS_DISABLED, &q->u.in.queue_irq_state) != 0)
 
-#define TIQDIO_SHARED_IND              63
-
-/* device state change indicators */
-struct indicator_t {
-       u32 ind;        /* u32 because of compare-and-swap performance */
-       atomic_t count; /* use count, 0 or 1 for non-shared indicators */
-};
-
-extern struct indicator_t *q_indicators;
-
-static inline int has_multiple_inq_on_dsci(struct qdio_irq *irq)
-{
-       return irq->nr_input_qs > 1;
-}
-
-static inline int references_shared_dsci(struct qdio_irq *irq)
-{
-       return irq->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
-}
-
-static inline int shared_ind(struct qdio_q *q)
-{
-       struct qdio_irq *i = q->irq_ptr;
-       return references_shared_dsci(i) || has_multiple_inq_on_dsci(i);
-}
-
 extern u64 last_ai_time;
 
 /* prototypes for thin interrupt */
@@ -457,7 +431,8 @@ int tiqdio_allocate_memory(void);
 void tiqdio_free_memory(void);
 int tiqdio_register_thinints(void);
 void tiqdio_unregister_thinints(void);
-
+void clear_nonshared_ind(struct qdio_irq *);
+int test_nonshared_ind(struct qdio_irq *);
 
 /* prototypes for setup */
 void qdio_inbound_processing(unsigned long data);
index 2fcdc0b2f0aa8e5cbfc67687cb1318ede7a2d5f2..3ef8d071c64a683316a01b6e12ecd68f3d6f1d9d 100644 (file)
@@ -1719,9 +1719,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr)
 
        WARN_ON(queue_irqs_enabled(q));
 
-       if (!shared_ind(q))
-               xchg(q->irq_ptr->dsci, 0);
-
+       clear_nonshared_ind(irq_ptr);
        qdio_stop_polling(q);
        clear_bit(QDIO_QUEUE_IRQS_DISABLED, &q->u.in.queue_irq_state);
 
@@ -1729,7 +1727,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr)
         * We need to check again to not lose initiative after
         * resetting the ACK state.
         */
-       if (!shared_ind(q) && *q->irq_ptr->dsci)
+       if (test_nonshared_ind(irq_ptr))
                goto rescan;
        if (!qdio_inbound_q_done(q))
                goto rescan;
index 9d1e7efb5bb5cc1015928610df025943ead18c98..011eadea3ee422fabb85ef24caa784401682ff89 100644 (file)
  */
 #define TIQDIO_NR_NONSHARED_IND                63
 #define TIQDIO_NR_INDICATORS           (TIQDIO_NR_NONSHARED_IND + 1)
+#define TIQDIO_SHARED_IND              63
+
+/* device state change indicators */
+struct indicator_t {
+       u32 ind;        /* u32 because of compare-and-swap performance */
+       atomic_t count; /* use count, 0 or 1 for non-shared indicators */
+};
 
 /* list of thin interrupt input queues */
 static LIST_HEAD(tiq_list);
@@ -34,7 +41,7 @@ static DEFINE_MUTEX(tiq_list_lock);
 /* adapter local summary indicator */
 static u8 *tiqdio_alsi;
 
-struct indicator_t *q_indicators;
+static struct indicator_t *q_indicators;
 
 u64 last_ai_time;
 
@@ -90,6 +97,43 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
        synchronize_rcu();
 }
 
+static inline int has_multiple_inq_on_dsci(struct qdio_irq *irq_ptr)
+{
+       return irq_ptr->nr_input_qs > 1;
+}
+
+static inline int references_shared_dsci(struct qdio_irq *irq_ptr)
+{
+       return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
+}
+
+static inline int shared_ind(struct qdio_irq *irq_ptr)
+{
+       return references_shared_dsci(irq_ptr) ||
+               has_multiple_inq_on_dsci(irq_ptr);
+}
+
+void clear_nonshared_ind(struct qdio_irq *irq_ptr)
+{
+       if (!is_thinint_irq(irq_ptr))
+               return;
+       if (shared_ind(irq_ptr))
+               return;
+       xchg(irq_ptr->dsci, 0);
+}
+
+int test_nonshared_ind(struct qdio_irq *irq_ptr)
+{
+       if (!is_thinint_irq(irq_ptr))
+               return 0;
+       if (shared_ind(irq_ptr))
+               return 0;
+       if (*irq_ptr->dsci)
+               return 1;
+       else
+               return 0;
+}
+
 static inline u32 clear_shared_ind(void)
 {
        if (!atomic_read(&q_indicators[TIQDIO_SHARED_IND].count))
@@ -119,7 +163,7 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq)
                        q->u.in.queue_start_poll(q->irq_ptr->cdev, q->nr,
                                                 q->irq_ptr->int_parm);
                } else {
-                       if (!shared_ind(q))
+                       if (!shared_ind(q->irq_ptr))
                                xchg(q->irq_ptr->dsci, 0);
 
                        /*