diff options
author | Julian Wiedmann <jwi@linux.ibm.com> | 2020-02-06 19:52:03 +0300 |
---|---|---|
committer | Vasily Gorbik <gor@linux.ibm.com> | 2020-09-14 11:30:07 +0300 |
commit | 309f98dbc66cb183a84781e2b19595f044a2d6e4 (patch) | |
tree | 17ce43c33e3083b27adf455ec78cc6b953bdd925 /drivers/s390 | |
parent | 4bf3ec384edf0bf893ec7bd62ccebb635b02efd9 (diff) | |
download | linux-309f98dbc66cb183a84781e2b19595f044a2d6e4.tar.xz |
s390/qdio: make qdio_handle_aobs() more robust
When processing a PENDING buffer with no attached aob, the current code
would get stuck on this buffer (as the 'continue' causes us to not
advance the buffer index) and process it repeatedly until the loop
terminates eventually.
Luckily this should never happen - the HW must not use the PENDING state
when no aob was provided. But we can still make this code path less
fragile and protect against buggy devices.
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/cio/qdio_main.c | 42 |
1 files changed, 20 insertions, 22 deletions
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 4fab8bba2cdd..0ed8c680cae4 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -531,26 +531,6 @@ static inline int qdio_inbound_q_done(struct qdio_q *q, unsigned int start) return 1; } -static inline void qdio_handle_aobs(struct qdio_q *q, int start, int count) -{ - unsigned char state = 0; - int j, b = start; - - for (j = 0; j < count; ++j) { - get_buf_state(q, b, &state, 0); - if (state == SLSB_P_OUTPUT_PENDING) { - struct qaob *aob = q->u.out.aobs[b]; - if (aob == NULL) - continue; - - q->u.out.sbal_state[b].flags |= - QDIO_OUTBUF_STATE_FLAG_PENDING; - q->u.out.aobs[b] = NULL; - } - b = next_buf(b); - } -} - static inline unsigned long qdio_aob_for_buffer(struct qdio_output_q *q, int bufnr) { @@ -640,6 +620,19 @@ void qdio_inbound_processing(unsigned long data) __qdio_inbound_processing(q); } +static void qdio_check_pending(struct qdio_q *q, unsigned int index) +{ + unsigned char state; + + if (get_buf_state(q, index, &state, 0) > 0 && + state == SLSB_P_OUTPUT_PENDING && + q->u.out.aobs[index]) { + q->u.out.sbal_state[index].flags |= + QDIO_OUTBUF_STATE_FLAG_PENDING; + q->u.out.aobs[index] = NULL; + } +} + static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start) { unsigned char state = 0; @@ -712,8 +705,13 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q, unsigned int start) if (count) { DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out moved:%1d", q->nr); - if (q->u.out.use_cq) - qdio_handle_aobs(q, start, count); + + if (q->u.out.use_cq) { + unsigned int i; + + for (i = 0; i < count; i++) + qdio_check_pending(q, QDIO_BUFNR(start + i)); + } } return count; |