Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 121370
b: refs/heads/master
c: 7a0b4cb
h: refs/heads/master
v: v3
  • Loading branch information
Jan Glauber authored and Martin Schwidefsky committed Dec 25, 2008
1 parent 0c224f8 commit db9143e
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 104 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 50f769df1c4bea766c4eb927eae35728fb93e305
refs/heads/master: 7a0b4cbc7d7d99763511b488b08bbc2607ddd1e3
8 changes: 1 addition & 7 deletions trunk/drivers/s390/cio/qdio.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
#include "chsc.h"

#define QDIO_BUSY_BIT_PATIENCE 100 /* 100 microseconds */
#define QDIO_BUSY_BIT_GIVE_UP 2000000 /* 2 seconds = eternity */
#define QDIO_INPUT_THRESHOLD 500 /* 500 microseconds */

/*
Expand Down Expand Up @@ -195,12 +194,6 @@ struct qdio_input_q {
};

struct qdio_output_q {
/* failed siga-w attempts*/
atomic_t busy_siga_counter;

/* start time of busy condition */
u64 timestamp;

/* PCIs are enabled for the queue */
int pci_out_enabled;

Expand Down Expand Up @@ -251,6 +244,7 @@ struct qdio_q {

struct qdio_irq *irq_ptr;
struct tasklet_struct tasklet;
spinlock_t lock;

/* error condition during a data transfer */
unsigned int qdio_error;
Expand Down
150 changes: 54 additions & 96 deletions trunk/drivers/s390/cio/qdio_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ static inline int do_siga_input(struct subchannel_id schid, unsigned int mask)
* Note: For IQDC unicast queues only the highest priority queue is processed.
*/
static inline int do_siga_output(unsigned long schid, unsigned long mask,
u32 *bb, unsigned int fc)
unsigned int *bb, unsigned int fc)
{
register unsigned long __fc asm("0") = fc;
register unsigned long __schid asm("1") = schid;
Expand Down Expand Up @@ -284,8 +284,7 @@ static int qdio_siga_sync(struct qdio_q *q, unsigned int output,
if (!need_siga_sync(q))
return 0;

DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:");
DBF_DEV_HEX(DBF_INFO, q->irq_ptr, q, sizeof(void *));
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:%1d", q->nr);
qdio_perf_stat_inc(&perf_stats.siga_sync);

cc = do_siga_sync(q->irq_ptr->schid, output, input);
Expand All @@ -312,46 +311,37 @@ static inline int qdio_siga_sync_all(struct qdio_q *q)
return qdio_siga_sync(q, ~0U, ~0U);
}

static inline int qdio_do_siga_output(struct qdio_q *q, unsigned int *busy_bit)
static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit)
{
unsigned int fc = 0;
unsigned long schid;
unsigned int fc = 0;
u64 start_time = 0;
int cc;

if (q->u.out.use_enh_siga) {
if (q->u.out.use_enh_siga)
fc = 3;
}
if (!is_qebsm(q))
schid = *((u32 *)&q->irq_ptr->schid);
else {

if (is_qebsm(q)) {
schid = q->irq_ptr->sch_token;
fc |= 0x80;
}
return do_siga_output(schid, q->mask, busy_bit, fc);
}

static int qdio_siga_output(struct qdio_q *q)
{
int cc;
u32 busy_bit;
u64 start_time = 0;
else
schid = *((u32 *)&q->irq_ptr->schid);

DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr);
qdio_perf_stat_inc(&perf_stats.siga_out);
again:
cc = qdio_do_siga_output(q, &busy_bit);
if (queue_type(q) == QDIO_IQDIO_QFMT && cc == 2 && busy_bit) {
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w bb:%2d", q->nr);
cc = do_siga_output(schid, q->mask, busy_bit, fc);

/* hipersocket busy condition */
if (*busy_bit) {
WARN_ON(queue_type(q) != QDIO_IQDIO_QFMT || cc != 2);

if (!start_time)
if (!start_time) {
start_time = get_usecs();
else if ((get_usecs() - start_time) < QDIO_BUSY_BIT_PATIENCE)
goto again;
}
if ((get_usecs() - start_time) < QDIO_BUSY_BIT_PATIENCE)
goto again;
}

if (cc == 2 && busy_bit)
cc |= QDIO_ERROR_SIGA_BUSY;
if (cc)
DBF_ERROR("%4x SIGA-W:%2d", SCH_NO(q), cc);
return cc;
}

Expand Down Expand Up @@ -399,7 +389,7 @@ inline void qdio_stop_polling(struct qdio_q *q)

static void announce_buffer_error(struct qdio_q *q, int count)
{
q->qdio_error = QDIO_ERROR_SLSB_STATE;
q->qdio_error |= QDIO_ERROR_SLSB_STATE;

/* special handling for no target buffer empty */
if ((!q->is_input_q &&
Expand Down Expand Up @@ -716,68 +706,36 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q)
return 0;
}

/*
* VM could present us cc=2 and busy bit set on SIGA-write
* during reconfiguration of their Guest LAN (only in iqdio mode,
* otherwise qdio is asynchronous and cc=2 and busy bit there will take
* the queues down immediately).
*
* Therefore qdio_siga_output will try for a short time constantly,
* if such a condition occurs. If it doesn't change, it will
* increase the busy_siga_counter and save the timestamp, and
* schedule the queue for later processing. qdio_outbound_processing
* will check out the counter. If non-zero, it will call qdio_kick_outbound_q
* as often as the value of the counter. This will attempt further SIGA
* instructions. For each successful SIGA, the counter is
* decreased, for failing SIGAs the counter remains the same, after
* all. After some time of no movement, qdio_kick_outbound_q will
* finally fail and reflect corresponding error codes to call
* the upper layer module and have it take the queues down.
*
* Note that this is a change from the original HiperSockets design
* (saying cc=2 and busy bit means take the queues down), but in
* these days Guest LAN didn't exist... excessive cc=2 with busy bit
* conditions will still take the queues down, but the threshold is
* higher due to the Guest LAN environment.
*
* Called from outbound tasklet and do_QDIO handler.
*/
static void qdio_kick_outbound_q(struct qdio_q *q)
{
int rc;

DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kickoutq:%1d", q->nr);
unsigned int busy_bit;
int cc;

if (!need_siga_out(q))
return;

rc = qdio_siga_output(q);
switch (rc) {
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr);
qdio_perf_stat_inc(&perf_stats.siga_out);

cc = qdio_siga_output(q, &busy_bit);
switch (cc) {
case 0:
/* TODO: improve error handling for CC=0 case */
if (q->u.out.timestamp)
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "cc2 rslv:%4x",
atomic_read(&q->u.out.busy_siga_counter));
/* went smooth this time, reset timestamp */
q->u.out.timestamp = 0;
break;
/* cc=2 and busy bit */
case (2 | QDIO_ERROR_SIGA_BUSY):
atomic_inc(&q->u.out.busy_siga_counter);

/* if the last siga was successful, save timestamp here */
if (!q->u.out.timestamp)
q->u.out.timestamp = get_usecs();

/* if we're in time, don't touch qdio_error */
if (get_usecs() - q->u.out.timestamp < QDIO_BUSY_BIT_GIVE_UP) {
tasklet_schedule(&q->tasklet);
break;
case 2:
if (busy_bit) {
DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr);
q->qdio_error = cc | QDIO_ERROR_SIGA_BUSY;
} else {
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d",
q->nr);
q->qdio_error = cc;
}
DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr);
default:
/* for plain cc=1, 2 or 3 */
q->qdio_error = rc;
break;
case 1:
case 3:
DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc);
q->qdio_error = cc;
break;
}
}

Expand Down Expand Up @@ -808,22 +766,18 @@ static void qdio_kick_outbound_handler(struct qdio_q *q)

static void __qdio_outbound_processing(struct qdio_q *q)
{
int siga_attempts;
unsigned long flags;

qdio_perf_stat_inc(&perf_stats.tasklet_outbound);

/* see comment in qdio_kick_outbound_q */
siga_attempts = atomic_read(&q->u.out.busy_siga_counter);
while (siga_attempts--) {
atomic_dec(&q->u.out.busy_siga_counter);
qdio_kick_outbound_q(q);
}
spin_lock_irqsave(&q->lock, flags);

BUG_ON(atomic_read(&q->nr_buf_used) < 0);

if (qdio_outbound_q_moved(q))
qdio_kick_outbound_handler(q);

spin_unlock_irqrestore(&q->lock, flags);

if (queue_type(q) == QDIO_ZFCP_QFMT) {
if (!pci_out_supported(q) && !qdio_outbound_q_done(q))
tasklet_schedule(&q->tasklet);
Expand Down Expand Up @@ -1491,7 +1445,7 @@ static inline int buf_in_between(int bufnr, int start, int count)
static void handle_inbound(struct qdio_q *q, unsigned int callflags,
int bufnr, int count)
{
int used, rc, diff;
int used, cc, diff;

if (!q->u.in.polling)
goto set;
Expand Down Expand Up @@ -1532,9 +1486,9 @@ static void handle_inbound(struct qdio_q *q, unsigned int callflags,
return;

if (need_siga_in(q)) {
rc = qdio_siga_input(q);
if (rc)
q->qdio_error = rc;
cc = qdio_siga_input(q);
if (cc)
q->qdio_error = cc;
}
}

Expand Down Expand Up @@ -1581,6 +1535,10 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags,
while (count--)
qdio_kick_outbound_q(q);
}

/* report CC=2 conditions synchronously */
if (q->qdio_error)
__qdio_outbound_processing(q);
goto out;
}

Expand Down
1 change: 1 addition & 0 deletions trunk/drivers/s390/cio/qdio_setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ static void setup_queues_misc(struct qdio_q *q, struct qdio_irq *irq_ptr,
q->mask = 1 << (31 - i);
q->nr = i;
q->handler = handler;
spin_lock_init(&q->lock);
}

static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr,
Expand Down

0 comments on commit db9143e

Please sign in to comment.