mirror of
https://github.com/torvalds/linux
synced 2024-10-01 00:39:03 +00:00
s390/qdio: remove 'merge_pending' mechanism
For non-QEBSM devices, get_buf_states() merges PENDING and EMPTY buffers into a single group of finished buffers. To allow the upper-layer driver to differentiate between the two states, qdio_check_pending() looks at each buffer's state again and sets the sbal_state flag to QDIO_OUTBUF_STATE_FLAG_PENDING accordingly. So effectively we're spending overhead on _every_ Output Queue inspection, just to avoid some additional TX completion calls in case a group of buffers has completed with mixed EMPTY / PENDING state. Given that PENDING buffers should rarely occur, this is a bad trade-off. In particular so as the additional checks in get_buf_states() affect _all_ device types (even those that don't use the PENDING state). Rip it all out, and just report the PENDING completions separately as we already do for QEBSM devices. Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com> Reviewed-by: Benjamin Block <bblock@linux.ibm.com> Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
This commit is contained in:
parent
7940eaf2e9
commit
2223318c28
|
@ -250,17 +250,13 @@ struct slsb {
|
||||||
* struct qdio_outbuf_state - SBAL related asynchronous operation information
|
* struct qdio_outbuf_state - SBAL related asynchronous operation information
|
||||||
* (for communication with upper layer programs)
|
* (for communication with upper layer programs)
|
||||||
* (only required for use with completion queues)
|
* (only required for use with completion queues)
|
||||||
* @flags: flags indicating state of buffer
|
|
||||||
* @user: pointer to upper layer program's state information related to SBAL
|
* @user: pointer to upper layer program's state information related to SBAL
|
||||||
* (stored in user1 data of QAOB)
|
* (stored in user1 data of QAOB)
|
||||||
*/
|
*/
|
||||||
struct qdio_outbuf_state {
|
struct qdio_outbuf_state {
|
||||||
u8 flags;
|
|
||||||
void *user;
|
void *user;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define QDIO_OUTBUF_STATE_FLAG_PENDING 0x01
|
|
||||||
|
|
||||||
#define CHSC_AC1_INITIATE_INPUTQ 0x80
|
#define CHSC_AC1_INITIATE_INPUTQ 0x80
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -202,7 +202,7 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start,
|
||||||
*/
|
*/
|
||||||
static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
|
static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
|
||||||
unsigned char *state, unsigned int count,
|
unsigned char *state, unsigned int count,
|
||||||
int auto_ack, int merge_pending)
|
int auto_ack)
|
||||||
{
|
{
|
||||||
unsigned char __state = 0;
|
unsigned char __state = 0;
|
||||||
int i = 1;
|
int i = 1;
|
||||||
|
@ -217,18 +217,9 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
|
||||||
if (__state & SLSB_OWNER_CU)
|
if (__state & SLSB_OWNER_CU)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (merge_pending && __state == SLSB_P_OUTPUT_PENDING)
|
|
||||||
__state = SLSB_P_OUTPUT_EMPTY;
|
|
||||||
|
|
||||||
for (; i < count; i++) {
|
for (; i < count; i++) {
|
||||||
bufnr = next_buf(bufnr);
|
bufnr = next_buf(bufnr);
|
||||||
|
|
||||||
/* merge PENDING into EMPTY: */
|
|
||||||
if (merge_pending &&
|
|
||||||
q->slsb.val[bufnr] == SLSB_P_OUTPUT_PENDING &&
|
|
||||||
__state == SLSB_P_OUTPUT_EMPTY)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* stop if next state differs from initial state: */
|
/* stop if next state differs from initial state: */
|
||||||
if (q->slsb.val[bufnr] != __state)
|
if (q->slsb.val[bufnr] != __state)
|
||||||
break;
|
break;
|
||||||
|
@ -242,7 +233,7 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
|
||||||
static inline int get_buf_state(struct qdio_q *q, unsigned int bufnr,
|
static inline int get_buf_state(struct qdio_q *q, unsigned int bufnr,
|
||||||
unsigned char *state, int auto_ack)
|
unsigned char *state, int auto_ack)
|
||||||
{
|
{
|
||||||
return get_buf_states(q, bufnr, state, 1, auto_ack, 0);
|
return get_buf_states(q, bufnr, state, 1, auto_ack);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* wrap-around safe setting of slsb states, returns number of changed buffers */
|
/* wrap-around safe setting of slsb states, returns number of changed buffers */
|
||||||
|
@ -464,7 +455,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q, unsigned int start,
|
||||||
* No siga sync here, as a PCI or we after a thin interrupt
|
* No siga sync here, as a PCI or we after a thin interrupt
|
||||||
* already sync'ed the queues.
|
* already sync'ed the queues.
|
||||||
*/
|
*/
|
||||||
count = get_buf_states(q, start, &state, count, 1, 0);
|
count = get_buf_states(q, start, &state, count, 1);
|
||||||
if (!count)
|
if (!count)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -541,7 +532,6 @@ static inline unsigned long qdio_aob_for_buffer(struct qdio_output_q *q,
|
||||||
WARN_ON_ONCE(phys_aob & 0xFF);
|
WARN_ON_ONCE(phys_aob & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
q->sbal_state[bufnr].flags = 0;
|
|
||||||
return phys_aob;
|
return phys_aob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -554,19 +544,6 @@ static inline int qdio_tasklet_schedule(struct qdio_q *q)
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start,
|
||||||
unsigned int *error)
|
unsigned int *error)
|
||||||
{
|
{
|
||||||
|
@ -587,7 +564,7 @@ static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start,
|
||||||
if (!count)
|
if (!count)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
count = get_buf_states(q, start, &state, count, 0, q->u.out.use_cq);
|
count = get_buf_states(q, start, &state, count, 0);
|
||||||
if (!count)
|
if (!count)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -609,6 +586,9 @@ static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start,
|
||||||
account_sbals(q, count);
|
account_sbals(q, count);
|
||||||
return count;
|
return count;
|
||||||
case SLSB_P_OUTPUT_ERROR:
|
case SLSB_P_OUTPUT_ERROR:
|
||||||
|
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out error:%1d %02x",
|
||||||
|
q->nr, count);
|
||||||
|
|
||||||
*error = QDIO_ERROR_SLSB_STATE;
|
*error = QDIO_ERROR_SLSB_STATE;
|
||||||
process_buffer_error(q, start, count);
|
process_buffer_error(q, start, count);
|
||||||
atomic_sub(count, &q->nr_buf_used);
|
atomic_sub(count, &q->nr_buf_used);
|
||||||
|
@ -640,27 +620,6 @@ static inline int qdio_outbound_q_done(struct qdio_q *q)
|
||||||
return atomic_read(&q->nr_buf_used) == 0;
|
return atomic_read(&q->nr_buf_used) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int qdio_outbound_q_moved(struct qdio_q *q, unsigned int start,
|
|
||||||
unsigned int *error)
|
|
||||||
{
|
|
||||||
int count;
|
|
||||||
|
|
||||||
count = get_outbound_buffer_frontier(q, start, error);
|
|
||||||
|
|
||||||
if (count) {
|
|
||||||
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out moved:%1d", q->nr);
|
|
||||||
|
|
||||||
if (q->u.out.use_cq && *error != QDIO_ERROR_SLSB_PENDING) {
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++)
|
|
||||||
qdio_check_pending(q, QDIO_BUFNR(start + i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int qdio_kick_outbound_q(struct qdio_q *q, unsigned int count,
|
static int qdio_kick_outbound_q(struct qdio_q *q, unsigned int count,
|
||||||
unsigned long aob)
|
unsigned long aob)
|
||||||
{
|
{
|
||||||
|
@ -715,7 +674,7 @@ void qdio_outbound_tasklet(struct tasklet_struct *t)
|
||||||
qperf_inc(q, tasklet_outbound);
|
qperf_inc(q, tasklet_outbound);
|
||||||
WARN_ON_ONCE(atomic_read(&q->nr_buf_used) < 0);
|
WARN_ON_ONCE(atomic_read(&q->nr_buf_used) < 0);
|
||||||
|
|
||||||
count = qdio_outbound_q_moved(q, start, &error);
|
count = get_outbound_buffer_frontier(q, start, &error);
|
||||||
if (count) {
|
if (count) {
|
||||||
q->first_to_check = add_buf(start, count);
|
q->first_to_check = add_buf(start, count);
|
||||||
|
|
||||||
|
@ -1482,7 +1441,7 @@ static int __qdio_inspect_queue(struct qdio_q *q, unsigned int *bufnr,
|
||||||
|
|
||||||
*error = 0;
|
*error = 0;
|
||||||
count = q->is_input_q ? get_inbound_buffer_frontier(q, start, error) :
|
count = q->is_input_q ? get_inbound_buffer_frontier(q, start, error) :
|
||||||
qdio_outbound_q_moved(q, start, error);
|
get_outbound_buffer_frontier(q, start, error);
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
@ -6076,9 +6076,7 @@ static void qeth_iqd_tx_complete(struct qeth_qdio_out_q *queue,
|
||||||
struct qeth_card *card = queue->card;
|
struct qeth_card *card = queue->card;
|
||||||
bool error = !!qdio_error;
|
bool error = !!qdio_error;
|
||||||
|
|
||||||
if ((qdio_error == QDIO_ERROR_SLSB_PENDING) ||
|
if (qdio_error == QDIO_ERROR_SLSB_PENDING) {
|
||||||
(queue->bufstates && (queue->bufstates[bidx].flags &
|
|
||||||
QDIO_OUTBUF_STATE_FLAG_PENDING))) {
|
|
||||||
WARN_ON_ONCE(card->options.cq != QETH_CQ_ENABLED);
|
WARN_ON_ONCE(card->options.cq != QETH_CQ_ENABLED);
|
||||||
|
|
||||||
QETH_CARD_TEXT_(card, 5, "pel%u", bidx);
|
QETH_CARD_TEXT_(card, 5, "pel%u", bidx);
|
||||||
|
|
Loading…
Reference in a new issue