提交 5f4026f8 编写于 作者: J Jan Glauber 提交者: Martin Schwidefsky

[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 d36deae7.
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: NJan Jaeger <jan.jaeger@westnet.com.au>
Tested-by: NJan Jaeger <jan.jaeger@westnet.com.au>
Signed-off-by: NJan Glauber <jang@linux.vnet.ibm.com>
Signed-off-by: NMartin Schwidefsky <schwidefsky@de.ibm.com>
上级 2a3a2d66
...@@ -418,32 +418,6 @@ static inline int multicast_outbound(struct qdio_q *q) ...@@ -418,32 +418,6 @@ static inline int multicast_outbound(struct qdio_q *q)
#define queue_irqs_disabled(q) \ #define queue_irqs_disabled(q) \
(test_bit(QDIO_QUEUE_IRQS_DISABLED, &q->u.in.queue_irq_state) != 0) (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; extern u64 last_ai_time;
/* prototypes for thin interrupt */ /* prototypes for thin interrupt */
...@@ -457,7 +431,8 @@ int tiqdio_allocate_memory(void); ...@@ -457,7 +431,8 @@ int tiqdio_allocate_memory(void);
void tiqdio_free_memory(void); void tiqdio_free_memory(void);
int tiqdio_register_thinints(void); int tiqdio_register_thinints(void);
void tiqdio_unregister_thinints(void); void tiqdio_unregister_thinints(void);
void clear_nonshared_ind(struct qdio_irq *);
int test_nonshared_ind(struct qdio_irq *);
/* prototypes for setup */ /* prototypes for setup */
void qdio_inbound_processing(unsigned long data); void qdio_inbound_processing(unsigned long data);
......
...@@ -1719,9 +1719,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr) ...@@ -1719,9 +1719,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr)
WARN_ON(queue_irqs_enabled(q)); WARN_ON(queue_irqs_enabled(q));
if (!shared_ind(q)) clear_nonshared_ind(irq_ptr);
xchg(q->irq_ptr->dsci, 0);
qdio_stop_polling(q); qdio_stop_polling(q);
clear_bit(QDIO_QUEUE_IRQS_DISABLED, &q->u.in.queue_irq_state); 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) ...@@ -1729,7 +1727,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr)
* We need to check again to not lose initiative after * We need to check again to not lose initiative after
* resetting the ACK state. * resetting the ACK state.
*/ */
if (!shared_ind(q) && *q->irq_ptr->dsci) if (test_nonshared_ind(irq_ptr))
goto rescan; goto rescan;
if (!qdio_inbound_q_done(q)) if (!qdio_inbound_q_done(q))
goto rescan; goto rescan;
......
...@@ -26,6 +26,13 @@ ...@@ -26,6 +26,13 @@
*/ */
#define TIQDIO_NR_NONSHARED_IND 63 #define TIQDIO_NR_NONSHARED_IND 63
#define TIQDIO_NR_INDICATORS (TIQDIO_NR_NONSHARED_IND + 1) #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 */ /* list of thin interrupt input queues */
static LIST_HEAD(tiq_list); static LIST_HEAD(tiq_list);
...@@ -34,7 +41,7 @@ static DEFINE_MUTEX(tiq_list_lock); ...@@ -34,7 +41,7 @@ static DEFINE_MUTEX(tiq_list_lock);
/* adapter local summary indicator */ /* adapter local summary indicator */
static u8 *tiqdio_alsi; static u8 *tiqdio_alsi;
struct indicator_t *q_indicators; static struct indicator_t *q_indicators;
u64 last_ai_time; u64 last_ai_time;
...@@ -90,6 +97,43 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) ...@@ -90,6 +97,43 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
synchronize_rcu(); 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) static inline u32 clear_shared_ind(void)
{ {
if (!atomic_read(&q_indicators[TIQDIO_SHARED_IND].count)) 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,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->u.in.queue_start_poll(q->irq_ptr->cdev, q->nr,
q->irq_ptr->int_parm); q->irq_ptr->int_parm);
} else { } else {
if (!shared_ind(q)) if (!shared_ind(q->irq_ptr))
xchg(q->irq_ptr->dsci, 0); xchg(q->irq_ptr->dsci, 0);
/* /*
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册