From bbd50e172f75b1d12ef9b1bcf593b51a44199016 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Thu, 25 Dec 2008 13:38:43 +0100 Subject: [S390] qdio: fix qeth port count detection qeth needs to get the port count information before qdio has allocated a page for the chsc operation. Extend qdio_get_ssqd_desc() to store the data in the specified structure. Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio_main.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/s390/cio/qdio_main.c') diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 7c86591..7572a00 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -1129,23 +1129,23 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, /** * qdio_get_ssqd_desc - get qdio subchannel description * @cdev: ccw device to get description for + * @data: where to store the ssqd * - * Returns a pointer to the saved qdio subchannel description, - * or NULL for not setup qdio devices. + * Returns 0 or an error code. The results of the chsc are stored in the + * specified structure. */ -struct qdio_ssqd_desc *qdio_get_ssqd_desc(struct ccw_device *cdev) +int qdio_get_ssqd_desc(struct ccw_device *cdev, + struct qdio_ssqd_desc *data) { - struct qdio_irq *irq_ptr; char dbf_text[15]; + if (!cdev || !cdev->private) + return -EINVAL; + sprintf(dbf_text, "qssq%4x", cdev->private->schid.sch_no); QDIO_DBF_TEXT0(0, setup, dbf_text); - irq_ptr = cdev->private->qdio_data; - if (!irq_ptr) - return NULL; - - return &irq_ptr->ssqd_desc; + return qdio_setup_get_ssqd(NULL, &cdev->private->schid, data); } EXPORT_SYMBOL_GPL(qdio_get_ssqd_desc); -- cgit v1.1 From 23589d057ac2da81e456d4a9dcdd9135fd96f493 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Thu, 25 Dec 2008 13:38:44 +0100 Subject: [S390] qdio: add eqbs/sqbs instruction counters Add counters for the eqbs and sqbs instructions that indicate how often we issued the instructions and how often the instructions returned with less buffers than specified. Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio_main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/s390/cio/qdio_main.c') diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 7572a00..a44a8c5 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -129,6 +129,7 @@ static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state, char dbf_text[15]; BUG_ON(!q->irq_ptr->sch_token); + qdio_perf_stat_inc(&perf_stats.debug_eqbs_all); if (!q->is_input_q) nr += q->irq_ptr->nr_input_qs; @@ -139,8 +140,10 @@ again: /* At least one buffer was processed, return and extract the remaining * buffers later. */ - if ((ccq == 96) && (count != tmp_count)) + if ((ccq == 96) && (count != tmp_count)) { + qdio_perf_stat_inc(&perf_stats.debug_eqbs_incomplete); return (count - tmp_count); + } if (rc == 1) { QDIO_DBF_TEXT5(1, trace, "eqAGAIN"); goto again; @@ -179,6 +182,7 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start, char dbf_text[15]; BUG_ON(!q->irq_ptr->sch_token); + qdio_perf_stat_inc(&perf_stats.debug_sqbs_all); if (!q->is_input_q) nr += q->irq_ptr->nr_input_qs; @@ -187,6 +191,7 @@ again: rc = qdio_check_ccq(q, ccq); if (rc == 1) { QDIO_DBF_TEXT5(1, trace, "sqAGAIN"); + qdio_perf_stat_inc(&perf_stats.debug_sqbs_incomplete); goto again; } if (rc < 0) { -- cgit v1.1 From 9a1ce28aeb7a8b1666eaa9f104c1a2f5a149f9df Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Thu, 25 Dec 2008 13:38:45 +0100 Subject: [S390] qdio: fix compile warning under 31 bit The QEBSM instructions are only available for CONFIG_64BIT, they are not used under 31 bit. Make compiler happy about the false positive: drivers/s390/cio/qdio_main.c: In function ?qdio_inbound_q_done?: drivers/s390/cio/qdio_main.c:532: warning: ?state? may be used uninitialized in this function Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/s390/cio/qdio_main.c') diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index a44a8c5..9e6ab41 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -529,7 +529,7 @@ int qdio_inbound_q_moved(struct qdio_q *q) static int qdio_inbound_q_done(struct qdio_q *q) { - unsigned char state; + unsigned char state = 0; #ifdef CONFIG_QDIO_DEBUG char dbf_text[15]; #endif -- cgit v1.1 From 22f9934767f49012ffbae753b28b8055bd28348f Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Thu, 25 Dec 2008 13:38:46 +0100 Subject: [S390] qdio: rework debug feature logging - make qdio_trace a per device view - remove s390dbf exceptions - remove CONFIG_QDIO_DEBUG, not needed anymore if we check for the level before calling sprintf - use snprintf for dbf entries - add start markers to see if the dbf view wrapped - add a global error view for all queues Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio_main.c | 324 +++++++++++++------------------------------ 1 file changed, 95 insertions(+), 229 deletions(-) (limited to 'drivers/s390/cio/qdio_main.c') diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 9e6ab41..c810214 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -95,8 +95,6 @@ static inline int do_siga_output(unsigned long schid, unsigned long mask, static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq) { - char dbf_text[15]; - /* all done or next buffer state different */ if (ccq == 0 || ccq == 32) return 0; @@ -104,8 +102,7 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq) if (ccq == 96 || ccq == 97) return 1; /* notify devices immediately */ - sprintf(dbf_text, "%d", ccq); - QDIO_DBF_TEXT2(1, trace, dbf_text); + DBF_ERROR("%4x ccq:%3d", SCH_NO(q), ccq); return -EIO; } @@ -126,7 +123,6 @@ static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state, int tmp_count = count, tmp_start = start; int nr = q->nr; int rc; - char dbf_text[15]; BUG_ON(!q->irq_ptr->sch_token); qdio_perf_stat_inc(&perf_stats.debug_eqbs_all); @@ -144,15 +140,15 @@ again: qdio_perf_stat_inc(&perf_stats.debug_eqbs_incomplete); return (count - tmp_count); } + if (rc == 1) { - QDIO_DBF_TEXT5(1, trace, "eqAGAIN"); + DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS again:%2d", ccq); goto again; } if (rc < 0) { - QDIO_DBF_TEXT2(1, trace, "eqberr"); - sprintf(dbf_text, "%2x,%2x,%d,%d", count, tmp_count, ccq, nr); - QDIO_DBF_TEXT2(1, trace, dbf_text); + DBF_ERROR("%4x EQBS ERROR", SCH_NO(q)); + DBF_ERROR("%3d%3d%2d", count, tmp_count, nr); q->handler(q->irq_ptr->cdev, QDIO_ERROR_ACTIVATE_CHECK_CONDITION, 0, -1, -1, q->irq_ptr->int_parm); @@ -179,7 +175,6 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start, int tmp_count = count, tmp_start = start; int nr = q->nr; int rc; - char dbf_text[15]; BUG_ON(!q->irq_ptr->sch_token); qdio_perf_stat_inc(&perf_stats.debug_sqbs_all); @@ -190,17 +185,13 @@ again: ccq = do_sqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count); rc = qdio_check_ccq(q, ccq); if (rc == 1) { - QDIO_DBF_TEXT5(1, trace, "sqAGAIN"); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "SQBS again:%2d", ccq); qdio_perf_stat_inc(&perf_stats.debug_sqbs_incomplete); goto again; } if (rc < 0) { - QDIO_DBF_TEXT3(1, trace, "sqberr"); - sprintf(dbf_text, "%2x,%2x", count, tmp_count); - QDIO_DBF_TEXT3(1, trace, dbf_text); - sprintf(dbf_text, "%d,%d", ccq, nr); - QDIO_DBF_TEXT3(1, trace, dbf_text); - + DBF_ERROR("%4x SQBS ERROR", SCH_NO(q)); + DBF_ERROR("%3d%3d%2d", count, tmp_count, nr); q->handler(q->irq_ptr->cdev, QDIO_ERROR_ACTIVATE_CHECK_CONDITION, 0, -1, -1, q->irq_ptr->int_parm); @@ -287,14 +278,13 @@ 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 *)); qdio_perf_stat_inc(&perf_stats.siga_sync); cc = do_siga_sync(q->irq_ptr->schid, output, input); - if (cc) { - QDIO_DBF_TEXT4(0, trace, "sigasync"); - QDIO_DBF_HEX4(0, trace, &q, sizeof(void *)); - QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *)); - } + if (cc) + DBF_ERROR("%4x SIGA-S:%2d", SCH_NO(q), cc); return cc; } @@ -338,17 +328,13 @@ static int qdio_siga_output(struct qdio_q *q) int cc; u32 busy_bit; u64 start_time = 0; - char dbf_text[15]; - - QDIO_DBF_TEXT5(0, trace, "sigaout"); - QDIO_DBF_HEX5(0, trace, &q, sizeof(void *)); + 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) { - sprintf(dbf_text, "bb%4x%2x", q->irq_ptr->schid.sch_no, q->nr); - QDIO_DBF_TEXT3(0, trace, dbf_text); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w bb:%2d", q->nr); if (!start_time) start_time = get_usecs(); @@ -359,7 +345,7 @@ again: if (cc == 2 && busy_bit) cc |= QDIO_ERROR_SIGA_BUSY; if (cc) - QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *)); + DBF_ERROR("%4x SIGA-W:%2d", SCH_NO(q), cc); return cc; } @@ -367,14 +353,12 @@ static inline int qdio_siga_input(struct qdio_q *q) { int cc; - QDIO_DBF_TEXT4(0, trace, "sigain"); - QDIO_DBF_HEX4(0, trace, &q, sizeof(void *)); - + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-r:%1d", q->nr); qdio_perf_stat_inc(&perf_stats.siga_in); cc = do_siga_input(q->irq_ptr->schid, q->mask); if (cc) - QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *)); + DBF_ERROR("%4x SIGA-R:%2d", SCH_NO(q), cc); return cc; } @@ -407,18 +391,12 @@ inline void qdio_stop_polling(struct qdio_q *q) static void announce_buffer_error(struct qdio_q *q) { - char dbf_text[15]; - - if (q->is_input_q) - QDIO_DBF_TEXT3(1, trace, "inperr"); - else - QDIO_DBF_TEXT3(0, trace, "outperr"); - - sprintf(dbf_text, "%x-%x-%x", q->first_to_check, - q->sbal[q->first_to_check]->element[14].flags, - q->sbal[q->first_to_check]->element[15].flags); - QDIO_DBF_TEXT3(1, trace, dbf_text); - QDIO_DBF_HEX2(1, trace, q->sbal[q->first_to_check], 256); + DBF_ERROR("%4x BUF ERROR", SCH_NO(q)); + DBF_ERROR((q->is_input_q) ? "IN:%2d" : "OUT:%2d", q->nr); + DBF_ERROR("FTC:%3d", q->first_to_check); + DBF_ERROR("F14:%2x F15:%2x", + q->sbal[q->first_to_check]->element[14].flags & 0xff, + q->sbal[q->first_to_check]->element[15].flags & 0xff); q->qdio_error = QDIO_ERROR_SLSB_STATE; } @@ -461,7 +439,7 @@ check_next: switch (state) { case SLSB_P_INPUT_PRIMED: - QDIO_DBF_TEXT5(0, trace, "inptprim"); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim: %3d", count); /* * Only ACK the first buffer. The ACK will be removed in @@ -500,13 +478,12 @@ check_next: case SLSB_CU_INPUT_EMPTY: case SLSB_P_INPUT_NOT_INIT: case SLSB_P_INPUT_ACK: - QDIO_DBF_TEXT5(0, trace, "inpnipro"); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in nop"); break; default: BUG(); } out: - QDIO_DBF_HEX4(0, trace, &q->first_to_check, sizeof(int)); return q->first_to_check; } @@ -520,8 +497,7 @@ int qdio_inbound_q_moved(struct qdio_q *q) if (!need_siga_sync(q) && !pci_out_supported(q)) q->u.in.timestamp = get_usecs(); - QDIO_DBF_TEXT4(0, trace, "inhasmvd"); - QDIO_DBF_HEX4(0, trace, &q, sizeof(void *)); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in moved"); return 1; } else return 0; @@ -530,9 +506,6 @@ int qdio_inbound_q_moved(struct qdio_q *q) static int qdio_inbound_q_done(struct qdio_q *q) { unsigned char state = 0; -#ifdef CONFIG_QDIO_DEBUG - char dbf_text[15]; -#endif if (!atomic_read(&q->nr_buf_used)) return 1; @@ -557,20 +530,12 @@ static int qdio_inbound_q_done(struct qdio_q *q) * has (probably) not moved (see qdio_inbound_processing). */ if (get_usecs() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) { -#ifdef CONFIG_QDIO_DEBUG - QDIO_DBF_TEXT4(0, trace, "inqisdon"); - QDIO_DBF_HEX4(0, trace, &q, sizeof(void *)); - sprintf(dbf_text, "pf%02x", q->first_to_check); - QDIO_DBF_TEXT4(0, trace, dbf_text); -#endif /* CONFIG_QDIO_DEBUG */ + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in done:%3d", + q->first_to_check); return 1; } else { -#ifdef CONFIG_QDIO_DEBUG - QDIO_DBF_TEXT4(0, trace, "inqisntd"); - QDIO_DBF_HEX4(0, trace, &q, sizeof(void *)); - sprintf(dbf_text, "pf%02x", q->first_to_check); - QDIO_DBF_TEXT4(0, trace, dbf_text); -#endif /* CONFIG_QDIO_DEBUG */ + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in notd:%3d", + q->first_to_check); return 0; } } @@ -578,9 +543,6 @@ static int qdio_inbound_q_done(struct qdio_q *q) void qdio_kick_inbound_handler(struct qdio_q *q) { int count, start, end; -#ifdef CONFIG_QDIO_DEBUG - char dbf_text[15]; -#endif qdio_perf_stat_inc(&perf_stats.inbound_handler); @@ -591,10 +553,7 @@ void qdio_kick_inbound_handler(struct qdio_q *q) else count = end + QDIO_MAX_BUFFERS_PER_Q - start; -#ifdef CONFIG_QDIO_DEBUG - sprintf(dbf_text, "s=%2xc=%2x", start, count); - QDIO_DBF_TEXT4(0, trace, dbf_text); -#endif /* CONFIG_QDIO_DEBUG */ + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kih s:%3d c:%3d", start, count); if (unlikely(q->irq_ptr->state != QDIO_IRQ_STATE_ACTIVE)) return; @@ -667,7 +626,7 @@ check_next: switch (state) { case SLSB_P_OUTPUT_EMPTY: /* the adapter got it */ - QDIO_DBF_TEXT5(0, trace, "outpempt"); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out empty:%1d %3d", q->nr, count); atomic_sub(count, &q->nr_buf_used); q->first_to_check = add_buf(q->first_to_check, count); @@ -686,7 +645,7 @@ check_next: break; case SLSB_CU_OUTPUT_PRIMED: /* the adapter has not fetched the output yet */ - QDIO_DBF_TEXT5(0, trace, "outpprim"); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out primed:%1d", q->nr); break; case SLSB_P_OUTPUT_NOT_INIT: case SLSB_P_OUTPUT_HALTED: @@ -711,8 +670,7 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q) if ((bufnr != q->last_move_ftc) || q->qdio_error) { q->last_move_ftc = bufnr; - QDIO_DBF_TEXT4(0, trace, "oqhasmvd"); - QDIO_DBF_HEX4(0, trace, &q, sizeof(void *)); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out moved:%1d", q->nr); return 1; } else return 0; @@ -747,12 +705,8 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q) static void qdio_kick_outbound_q(struct qdio_q *q) { int rc; -#ifdef CONFIG_QDIO_DEBUG - char dbf_text[15]; - QDIO_DBF_TEXT5(0, trace, "kickoutq"); - QDIO_DBF_HEX5(0, trace, &q, sizeof(void *)); -#endif /* CONFIG_QDIO_DEBUG */ + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kickoutq:%1d", q->nr); if (!need_siga_out(q)) return; @@ -761,15 +715,9 @@ static void qdio_kick_outbound_q(struct qdio_q *q) switch (rc) { case 0: /* TODO: improve error handling for CC=0 case */ -#ifdef CONFIG_QDIO_DEBUG - if (q->u.out.timestamp) { - QDIO_DBF_TEXT3(0, trace, "cc2reslv"); - sprintf(dbf_text, "%4x%2x%2x", q->irq_ptr->schid.sch_no, - q->nr, - atomic_read(&q->u.out.busy_siga_counter)); - QDIO_DBF_TEXT3(0, trace, dbf_text); - } -#endif /* CONFIG_QDIO_DEBUG */ + 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; @@ -786,12 +734,7 @@ static void qdio_kick_outbound_q(struct qdio_q *q) tasklet_schedule(&q->tasklet); break; } - QDIO_DBF_TEXT2(0, trace, "cc2REPRT"); -#ifdef CONFIG_QDIO_DEBUG - sprintf(dbf_text, "%4x%2x%2x", q->irq_ptr->schid.sch_no, q->nr, - atomic_read(&q->u.out.busy_siga_counter)); - QDIO_DBF_TEXT3(0, trace, dbf_text); -#endif /* CONFIG_QDIO_DEBUG */ + DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr); default: /* for plain cc=1, 2 or 3 */ q->qdio_error = rc; @@ -801,9 +744,6 @@ static void qdio_kick_outbound_q(struct qdio_q *q) static void qdio_kick_outbound_handler(struct qdio_q *q) { int start, end, count; -#ifdef CONFIG_QDIO_DEBUG - char dbf_text[15]; -#endif start = q->first_to_kick; end = q->last_move_ftc; @@ -812,13 +752,8 @@ static void qdio_kick_outbound_handler(struct qdio_q *q) else count = end + QDIO_MAX_BUFFERS_PER_Q - start; -#ifdef CONFIG_QDIO_DEBUG - QDIO_DBF_TEXT4(0, trace, "kickouth"); - QDIO_DBF_HEX4(0, trace, &q, sizeof(void *)); - - sprintf(dbf_text, "s=%2xc=%2x", start, count); - QDIO_DBF_TEXT4(0, trace, dbf_text); -#endif /* CONFIG_QDIO_DEBUG */ + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kickouth: %1d", q->nr); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "s:%3d c:%3d", start, count); if (unlikely(q->irq_ptr->state != QDIO_IRQ_STATE_ACTIVE)) return; @@ -913,27 +848,18 @@ void qdio_check_outbound_after_thinint(struct qdio_q *q) static inline void qdio_set_state(struct qdio_irq *irq_ptr, enum qdio_irq_states state) { -#ifdef CONFIG_QDIO_DEBUG - char dbf_text[15]; - - QDIO_DBF_TEXT5(0, trace, "newstate"); - sprintf(dbf_text, "%4x%4x", irq_ptr->schid.sch_no, state); - QDIO_DBF_TEXT5(0, trace, dbf_text); -#endif /* CONFIG_QDIO_DEBUG */ + DBF_DEV_EVENT(DBF_INFO, irq_ptr, "newstate: %1d", state); irq_ptr->state = state; mb(); } -static void qdio_irq_check_sense(struct subchannel_id schid, struct irb *irb) +static void qdio_irq_check_sense(struct qdio_irq *irq_ptr, struct irb *irb) { - char dbf_text[15]; - if (irb->esw.esw0.erw.cons) { - sprintf(dbf_text, "sens%4x", schid.sch_no); - QDIO_DBF_TEXT2(1, trace, dbf_text); - QDIO_DBF_HEX0(0, trace, irb, 64); - QDIO_DBF_HEX0(0, trace, irb->ecw, 64); + DBF_ERROR("%4x sense:", irq_ptr->schid.sch_no); + DBF_ERROR_HEX(irb, 64); + DBF_ERROR_HEX(irb->ecw, 64); } } @@ -967,14 +893,10 @@ static void qdio_handle_activate_check(struct ccw_device *cdev, { struct qdio_irq *irq_ptr = cdev->private->qdio_data; struct qdio_q *q; - char dbf_text[15]; - QDIO_DBF_TEXT2(1, trace, "ick2"); - sprintf(dbf_text, "%s", dev_name(&cdev->dev)); - QDIO_DBF_TEXT2(1, trace, dbf_text); - QDIO_DBF_HEX2(0, trace, &intparm, sizeof(int)); - QDIO_DBF_HEX2(0, trace, &dstat, sizeof(int)); - QDIO_DBF_HEX2(0, trace, &cstat, sizeof(int)); + DBF_ERROR("%4x ACT CHECK", irq_ptr->schid.sch_no); + DBF_ERROR("intp :%lx", intparm); + DBF_ERROR("ds: %2x cs:%2x", dstat, cstat); if (irq_ptr->nr_input_qs) { q = irq_ptr->input_qs[0]; @@ -1027,28 +949,29 @@ static void qdio_int_error(struct ccw_device *cdev) } static int qdio_establish_check_errors(struct ccw_device *cdev, int cstat, - int dstat) + int dstat) { struct qdio_irq *irq_ptr = cdev->private->qdio_data; if (cstat || (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END))) { - QDIO_DBF_TEXT2(1, setup, "eq:ckcon"); + DBF_ERROR("EQ:ck con"); goto error; } if (!(dstat & DEV_STAT_DEV_END)) { - QDIO_DBF_TEXT2(1, setup, "eq:no de"); + DBF_ERROR("EQ:no dev"); goto error; } if (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) { - QDIO_DBF_TEXT2(1, setup, "eq:badio"); + DBF_ERROR("EQ: bad io"); goto error; } return 0; error: - QDIO_DBF_HEX2(0, trace, &cstat, sizeof(int)); - QDIO_DBF_HEX2(0, trace, &dstat, sizeof(int)); + DBF_ERROR("%4x EQ:error", irq_ptr->schid.sch_no); + DBF_ERROR("ds: %2x cs:%2x", dstat, cstat); + qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); return 1; } @@ -1057,12 +980,8 @@ static void qdio_establish_handle_irq(struct ccw_device *cdev, int cstat, int dstat) { struct qdio_irq *irq_ptr = cdev->private->qdio_data; - char dbf_text[15]; - - sprintf(dbf_text, "qehi%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); - QDIO_DBF_TEXT0(0, trace, dbf_text); + DBF_DEV_EVENT(DBF_INFO, irq_ptr, "qest irq"); if (!qdio_establish_check_errors(cdev, cstat, dstat)) qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ESTABLISHED); } @@ -1073,25 +992,21 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, { struct qdio_irq *irq_ptr = cdev->private->qdio_data; int cstat, dstat; - char dbf_text[15]; qdio_perf_stat_inc(&perf_stats.qdio_int); if (!intparm || !irq_ptr) { - sprintf(dbf_text, "qihd%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT2(1, setup, dbf_text); + DBF_ERROR("qint:%4x", cdev->private->schid.sch_no); return; } if (IS_ERR(irb)) { switch (PTR_ERR(irb)) { case -EIO: - sprintf(dbf_text, "ierr%4x", irq_ptr->schid.sch_no); - QDIO_DBF_TEXT2(1, setup, dbf_text); + DBF_ERROR("%4x IO error", irq_ptr->schid.sch_no); return; case -ETIMEDOUT: - sprintf(dbf_text, "qtoh%4x", irq_ptr->schid.sch_no); - QDIO_DBF_TEXT2(1, setup, dbf_text); + DBF_ERROR("%4x IO timeout", irq_ptr->schid.sch_no); qdio_int_error(cdev); return; default: @@ -1099,7 +1014,7 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, return; } } - qdio_irq_check_sense(irq_ptr->schid, irb); + qdio_irq_check_sense(irq_ptr, irb); cstat = irb->scsw.cmd.cstat; dstat = irb->scsw.cmd.dstat; @@ -1142,14 +1057,11 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, int qdio_get_ssqd_desc(struct ccw_device *cdev, struct qdio_ssqd_desc *data) { - char dbf_text[15]; if (!cdev || !cdev->private) return -EINVAL; - sprintf(dbf_text, "qssq%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); - + DBF_EVENT("get ssqd:%4x", cdev->private->schid.sch_no); return qdio_setup_get_ssqd(NULL, &cdev->private->schid, data); } EXPORT_SYMBOL_GPL(qdio_get_ssqd_desc); @@ -1164,14 +1076,9 @@ EXPORT_SYMBOL_GPL(qdio_get_ssqd_desc); */ int qdio_cleanup(struct ccw_device *cdev, int how) { - struct qdio_irq *irq_ptr; - char dbf_text[15]; + struct qdio_irq *irq_ptr = cdev->private->qdio_data; int rc; - sprintf(dbf_text, "qcln%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); - - irq_ptr = cdev->private->qdio_data; if (!irq_ptr) return -ENODEV; @@ -1204,18 +1111,15 @@ static void qdio_shutdown_queues(struct ccw_device *cdev) */ int qdio_shutdown(struct ccw_device *cdev, int how) { - struct qdio_irq *irq_ptr; + struct qdio_irq *irq_ptr = cdev->private->qdio_data; int rc; unsigned long flags; - char dbf_text[15]; - sprintf(dbf_text, "qshu%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); - - irq_ptr = cdev->private->qdio_data; if (!irq_ptr) return -ENODEV; + DBF_EVENT("qshutdown:%4x", cdev->private->schid.sch_no); + mutex_lock(&irq_ptr->setup_mutex); /* * Subchannel was already shot down. We cannot prevent being called @@ -1239,10 +1143,8 @@ int qdio_shutdown(struct ccw_device *cdev, int how) /* default behaviour is halt */ rc = ccw_device_halt(cdev, QDIO_DOING_CLEANUP); if (rc) { - sprintf(dbf_text, "sher%4x", irq_ptr->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); - sprintf(dbf_text, "rc=%d", rc); - QDIO_DBF_TEXT0(0, setup, dbf_text); + DBF_ERROR("%4x SHUTD ERR", irq_ptr->schid.sch_no); + DBF_ERROR("rc:%4d", rc); goto no_cleanup; } @@ -1276,17 +1178,18 @@ EXPORT_SYMBOL_GPL(qdio_shutdown); */ int qdio_free(struct ccw_device *cdev) { - struct qdio_irq *irq_ptr; - char dbf_text[15]; - - sprintf(dbf_text, "qfre%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); + struct qdio_irq *irq_ptr = cdev->private->qdio_data; - irq_ptr = cdev->private->qdio_data; if (!irq_ptr) return -ENODEV; + DBF_EVENT("qfree:%4x", cdev->private->schid.sch_no); mutex_lock(&irq_ptr->setup_mutex); + + if (irq_ptr->debug_area != NULL) { + debug_unregister(irq_ptr->debug_area); + irq_ptr->debug_area = NULL; + } cdev->private->qdio_data = NULL; mutex_unlock(&irq_ptr->setup_mutex); @@ -1305,10 +1208,6 @@ EXPORT_SYMBOL_GPL(qdio_free); int qdio_initialize(struct qdio_initialize *init_data) { int rc; - char dbf_text[15]; - - sprintf(dbf_text, "qini%4x", init_data->cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); rc = qdio_allocate(init_data); if (rc) @@ -1328,10 +1227,8 @@ EXPORT_SYMBOL_GPL(qdio_initialize); int qdio_allocate(struct qdio_initialize *init_data) { struct qdio_irq *irq_ptr; - char dbf_text[15]; - sprintf(dbf_text, "qalc%4x", init_data->cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); + DBF_EVENT("qallocate:%4x", init_data->cdev->private->schid.sch_no); if ((init_data->no_input_qs && !init_data->input_handler) || (init_data->no_output_qs && !init_data->output_handler)) @@ -1345,16 +1242,13 @@ int qdio_allocate(struct qdio_initialize *init_data) (!init_data->output_sbal_addr_array)) return -EINVAL; - qdio_allocate_do_dbf(init_data); - /* irq_ptr must be in GFP_DMA since it contains ccw1.cda */ irq_ptr = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!irq_ptr) goto out_err; - QDIO_DBF_TEXT0(0, setup, "irq_ptr:"); - QDIO_DBF_HEX0(0, setup, &irq_ptr, sizeof(void *)); mutex_init(&irq_ptr->setup_mutex); + qdio_allocate_dbf(init_data, irq_ptr); /* * Allocate a page for the chsc calls in qdio_establish. @@ -1372,9 +1266,6 @@ int qdio_allocate(struct qdio_initialize *init_data) goto out_rel; WARN_ON((unsigned long)irq_ptr->qdr & 0xfff); - QDIO_DBF_TEXT0(0, setup, "qdr:"); - QDIO_DBF_HEX0(0, setup, &irq_ptr->qdr, sizeof(void *)); - if (qdio_allocate_qs(irq_ptr, init_data->no_input_qs, init_data->no_output_qs)) goto out_rel; @@ -1395,14 +1286,12 @@ EXPORT_SYMBOL_GPL(qdio_allocate); */ int qdio_establish(struct qdio_initialize *init_data) { - char dbf_text[20]; struct qdio_irq *irq_ptr; struct ccw_device *cdev = init_data->cdev; unsigned long saveflags; int rc; - sprintf(dbf_text, "qest%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); + DBF_EVENT("qestablish:%4x", cdev->private->schid.sch_no); irq_ptr = cdev->private->qdio_data; if (!irq_ptr) @@ -1432,10 +1321,8 @@ int qdio_establish(struct qdio_initialize *init_data) rc = ccw_device_start(cdev, &irq_ptr->ccw, QDIO_DOING_ESTABLISH, 0, 0); if (rc) { - sprintf(dbf_text, "eq:io%4x", irq_ptr->schid.sch_no); - QDIO_DBF_TEXT2(1, setup, dbf_text); - sprintf(dbf_text, "eq:rc%4x", rc); - QDIO_DBF_TEXT2(1, setup, dbf_text); + DBF_ERROR("%4x est IO ERR", irq_ptr->schid.sch_no); + DBF_ERROR("rc:%4x", rc); } spin_unlock_irqrestore(get_ccwdev_lock(cdev), saveflags); @@ -1456,10 +1343,8 @@ int qdio_establish(struct qdio_initialize *init_data) } qdio_setup_ssqd_info(irq_ptr); - sprintf(dbf_text, "qDmmwc%2x", irq_ptr->ssqd_desc.mmwc); - QDIO_DBF_TEXT2(0, setup, dbf_text); - sprintf(dbf_text, "qib ac%2x", irq_ptr->qib.ac); - QDIO_DBF_TEXT2(0, setup, dbf_text); + DBF_EVENT("qDmmwc:%2x", irq_ptr->ssqd_desc.mmwc); + DBF_EVENT("qib ac:%4x", irq_ptr->qib.ac); /* qebsm is now setup if available, initialize buffer states */ qdio_init_buf_states(irq_ptr); @@ -1480,10 +1365,8 @@ int qdio_activate(struct ccw_device *cdev) struct qdio_irq *irq_ptr; int rc; unsigned long saveflags; - char dbf_text[20]; - sprintf(dbf_text, "qact%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT0(0, setup, dbf_text); + DBF_EVENT("qactivate:%4x", cdev->private->schid.sch_no); irq_ptr = cdev->private->qdio_data; if (!irq_ptr) @@ -1509,10 +1392,8 @@ int qdio_activate(struct ccw_device *cdev) rc = ccw_device_start(cdev, &irq_ptr->ccw, QDIO_DOING_ACTIVATE, 0, DOIO_DENY_PREFETCH); if (rc) { - sprintf(dbf_text, "aq:io%4x", irq_ptr->schid.sch_no); - QDIO_DBF_TEXT2(1, setup, dbf_text); - sprintf(dbf_text, "aq:rc%4x", rc); - QDIO_DBF_TEXT2(1, setup, dbf_text); + DBF_ERROR("%4x act IO ERR", irq_ptr->schid.sch_no); + DBF_ERROR("rc:%4x", rc); } spin_unlock_irqrestore(get_ccwdev_lock(cdev), saveflags); @@ -1658,7 +1539,7 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags, if (state != SLSB_CU_OUTPUT_PRIMED) qdio_kick_outbound_q(q); else { - QDIO_DBF_TEXT5(0, trace, "fast-req"); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "fast-req"); qdio_perf_stat_inc(&perf_stats.fast_requeue); } out: @@ -1678,12 +1559,6 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags, int q_nr, int bufnr, int count) { struct qdio_irq *irq_ptr; -#ifdef CONFIG_QDIO_DEBUG - char dbf_text[20]; - - sprintf(dbf_text, "doQD%4x", cdev->private->schid.sch_no); - QDIO_DBF_TEXT3(0, trace, dbf_text); -#endif /* CONFIG_QDIO_DEBUG */ if ((bufnr > QDIO_MAX_BUFFERS_PER_Q) || (count > QDIO_MAX_BUFFERS_PER_Q) || @@ -1697,33 +1572,24 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags, if (!irq_ptr) return -ENODEV; -#ifdef CONFIG_QDIO_DEBUG if (callflags & QDIO_FLAG_SYNC_INPUT) - QDIO_DBF_HEX3(0, trace, &irq_ptr->input_qs[q_nr], - sizeof(void *)); + DBF_DEV_EVENT(DBF_INFO, irq_ptr, "doQDIO input"); else - QDIO_DBF_HEX3(0, trace, &irq_ptr->output_qs[q_nr], - sizeof(void *)); - - sprintf(dbf_text, "flag%04x", callflags); - QDIO_DBF_TEXT3(0, trace, dbf_text); - sprintf(dbf_text, "qi%02xct%02x", bufnr, count); - QDIO_DBF_TEXT3(0, trace, dbf_text); -#endif /* CONFIG_QDIO_DEBUG */ + DBF_DEV_EVENT(DBF_INFO, irq_ptr, "doQDIO output"); + DBF_DEV_EVENT(DBF_INFO, irq_ptr, "q:%1d flag:%4x", q_nr, callflags); + DBF_DEV_EVENT(DBF_INFO, irq_ptr, "buf:%2d cnt:%3d", bufnr, count); if (irq_ptr->state != QDIO_IRQ_STATE_ACTIVE) return -EBUSY; if (callflags & QDIO_FLAG_SYNC_INPUT) - handle_inbound(irq_ptr->input_qs[q_nr], - callflags, bufnr, count); + handle_inbound(irq_ptr->input_qs[q_nr], callflags, bufnr, + count); else if (callflags & QDIO_FLAG_SYNC_OUTPUT) - handle_outbound(irq_ptr->output_qs[q_nr], - callflags, bufnr, count); - else { - QDIO_DBF_TEXT3(1, trace, "doQD:inv"); + handle_outbound(irq_ptr->output_qs[q_nr], callflags, bufnr, + count); + else return -EINVAL; - } return 0; } EXPORT_SYMBOL_GPL(do_QDIO); -- cgit v1.1 From 50f769df1c4bea766c4eb927eae35728fb93e305 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Thu, 25 Dec 2008 13:38:47 +0100 Subject: [S390] qdio: improve inbound buffer acknowledgement - Use automatic acknowledgement of incoming buffers in QEBSM mode - Move ACK for non-QEBSM mode always to the newest buffer to prevent a race with qdio_stop_polling - Remove the polling spinlock, the upper layer drivers return new buffers in the same code path and could not run in parallel - Don't flood the error log in case of no-target-buffer-empty - In handle_inbound we check if we would overwrite an ACK'ed buffer, if so advance the pointer to the oldest ACK'ed buffer so we don't overwrite an empty buffer in qdio_stop_polling Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio_main.c | 181 ++++++++++++++++++++++++++++--------------- 1 file changed, 118 insertions(+), 63 deletions(-) (limited to 'drivers/s390/cio/qdio_main.c') diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index c810214..0b4c09c 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -112,12 +112,13 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq) * @state: state of the extracted buffers * @start: buffer number to start at * @count: count of buffers to examine + * @auto_ack: automatically acknowledge buffers * * Returns the number of successfull extracted equal buffer states. * Stops processing if a state is different from the last buffers state. */ static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state, - int start, int count) + int start, int count, int auto_ack) { unsigned int ccq = 0; int tmp_count = count, tmp_start = start; @@ -130,7 +131,8 @@ static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state, if (!q->is_input_q) nr += q->irq_ptr->nr_input_qs; again: - ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count); + ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count, + auto_ack); rc = qdio_check_ccq(q, ccq); /* At least one buffer was processed, return and extract the remaining @@ -176,6 +178,9 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start, int nr = q->nr; int rc; + if (!count) + return 0; + BUG_ON(!q->irq_ptr->sch_token); qdio_perf_stat_inc(&perf_stats.debug_sqbs_all); @@ -203,7 +208,8 @@ again: /* returns number of examined buffers and their common state in *state */ 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) { unsigned char __state = 0; int i; @@ -212,7 +218,7 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr, BUG_ON(count > QDIO_MAX_BUFFERS_PER_Q); if (is_qebsm(q)) - return qdio_do_eqbs(q, state, bufnr, count); + return qdio_do_eqbs(q, state, bufnr, count, auto_ack); for (i = 0; i < count; i++) { if (!__state) @@ -226,9 +232,9 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr, } inline int get_buf_state(struct qdio_q *q, unsigned int bufnr, - unsigned char *state) + unsigned char *state, int auto_ack) { - return get_buf_states(q, bufnr, state, 1); + return get_buf_states(q, bufnr, state, 1, auto_ack); } /* wrap-around safe setting of slsb states, returns number of changed buffers */ @@ -376,29 +382,91 @@ void qdio_sync_after_thinint(struct qdio_q *q) inline void qdio_stop_polling(struct qdio_q *q) { - spin_lock_bh(&q->u.in.lock); - if (!q->u.in.polling) { - spin_unlock_bh(&q->u.in.lock); + if (!q->u.in.polling) return; - } + q->u.in.polling = 0; qdio_perf_stat_inc(&perf_stats.debug_stop_polling); /* show the card that we are not polling anymore */ - set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT); - spin_unlock_bh(&q->u.in.lock); + if (is_qebsm(q)) { + set_buf_states(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT, + q->u.in.ack_count); + q->u.in.ack_count = 0; + } else + set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT); } -static void announce_buffer_error(struct qdio_q *q) +static void announce_buffer_error(struct qdio_q *q, int count) { + q->qdio_error = QDIO_ERROR_SLSB_STATE; + + /* special handling for no target buffer empty */ + if ((!q->is_input_q && + (q->sbal[q->first_to_check]->element[15].flags & 0xff) == 0x10)) { + qdio_perf_stat_inc(&perf_stats.outbound_target_full); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "OUTFULL FTC:%3d", + q->first_to_check); + return; + } + DBF_ERROR("%4x BUF ERROR", SCH_NO(q)); DBF_ERROR((q->is_input_q) ? "IN:%2d" : "OUT:%2d", q->nr); - DBF_ERROR("FTC:%3d", q->first_to_check); + DBF_ERROR("FTC:%3d C:%3d", q->first_to_check, count); DBF_ERROR("F14:%2x F15:%2x", q->sbal[q->first_to_check]->element[14].flags & 0xff, q->sbal[q->first_to_check]->element[15].flags & 0xff); +} - q->qdio_error = QDIO_ERROR_SLSB_STATE; +static inline void inbound_primed(struct qdio_q *q, int count) +{ + int new; + + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim: %3d", count); + + /* for QEBSM the ACK was already set by EQBS */ + if (is_qebsm(q)) { + if (!q->u.in.polling) { + q->u.in.polling = 1; + q->u.in.ack_count = count; + q->last_move_ftc = q->first_to_check; + return; + } + + /* delete the previous ACK's */ + set_buf_states(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT, + q->u.in.ack_count); + q->u.in.ack_count = count; + q->last_move_ftc = q->first_to_check; + return; + } + + /* + * ACK the newest buffer. The ACK will be removed in qdio_stop_polling + * or by the next inbound run. + */ + new = add_buf(q->first_to_check, count - 1); + if (q->u.in.polling) { + /* reset the previous ACK but first set the new one */ + set_buf_state(q, new, SLSB_P_INPUT_ACK); + set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT); + } + else { + q->u.in.polling = 1; + set_buf_state(q, q->first_to_check, SLSB_P_INPUT_ACK); + } + + q->last_move_ftc = new; + count--; + if (!count) + return; + + /* + * Need to change all PRIMED buffers to NOT_INIT, otherwise + * we're loosing initiative in the thinint code. + */ + set_buf_states(q, next_buf(q->first_to_check), SLSB_P_INPUT_NOT_INIT, + count); } static int get_inbound_buffer_frontier(struct qdio_q *q) @@ -407,13 +475,6 @@ static int get_inbound_buffer_frontier(struct qdio_q *q) unsigned char state; /* - * If we still poll don't update last_move_ftc, keep the - * previously ACK buffer there. - */ - if (!q->u.in.polling) - q->last_move_ftc = q->first_to_check; - - /* * Don't check 128 buffers, as otherwise qdio_inbound_q_moved * would return 0. */ @@ -433,34 +494,13 @@ check_next: if (q->first_to_check == stop) goto out; - count = get_buf_states(q, q->first_to_check, &state, count); + count = get_buf_states(q, q->first_to_check, &state, count, 1); if (!count) goto out; switch (state) { case SLSB_P_INPUT_PRIMED: - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim: %3d", count); - - /* - * Only ACK the first buffer. The ACK will be removed in - * qdio_stop_polling. - */ - if (q->u.in.polling) - state = SLSB_P_INPUT_NOT_INIT; - else { - q->u.in.polling = 1; - state = SLSB_P_INPUT_ACK; - } - set_buf_state(q, q->first_to_check, state); - - /* - * Need to change all PRIMED buffers to NOT_INIT, otherwise - * we're loosing initiative in the thinint code. - */ - if (count > 1) - set_buf_states(q, next_buf(q->first_to_check), - SLSB_P_INPUT_NOT_INIT, count - 1); - + inbound_primed(q, count); /* * No siga-sync needed for non-qebsm here, as the inbound queue * will be synced on the next siga-r, resp. @@ -470,7 +510,7 @@ check_next: atomic_sub(count, &q->nr_buf_used); goto check_next; case SLSB_P_INPUT_ERROR: - announce_buffer_error(q); + announce_buffer_error(q, count); /* process the buffer, the upper layer will take care of it */ q->first_to_check = add_buf(q->first_to_check, count); atomic_sub(count, &q->nr_buf_used); @@ -516,7 +556,7 @@ static int qdio_inbound_q_done(struct qdio_q *q) */ qdio_siga_sync_q(q); - get_buf_state(q, q->first_to_check, &state); + get_buf_state(q, q->first_to_check, &state, 0); if (state == SLSB_P_INPUT_PRIMED) /* we got something to do */ return 0; @@ -619,7 +659,7 @@ check_next: if (q->first_to_check == stop) return q->first_to_check; - count = get_buf_states(q, q->first_to_check, &state, count); + count = get_buf_states(q, q->first_to_check, &state, count, 0); if (!count) return q->first_to_check; @@ -638,7 +678,7 @@ check_next: break; goto check_next; case SLSB_P_OUTPUT_ERROR: - announce_buffer_error(q); + announce_buffer_error(q, count); /* process the buffer, the upper layer will take care of it */ q->first_to_check = add_buf(q->first_to_check, count); atomic_sub(count, &q->nr_buf_used); @@ -1451,23 +1491,38 @@ 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) { - unsigned long flags; - int used, rc; + int used, rc, diff; - /* - * do_QDIO could run in parallel with the queue tasklet so the - * upper-layer programm could empty the ACK'ed buffer here. - * If that happens we must clear the polling flag, otherwise - * qdio_stop_polling() could set the buffer to NOT_INIT after - * it was set to EMPTY which would kill us. - */ - spin_lock_irqsave(&q->u.in.lock, flags); - if (q->u.in.polling) - if (buf_in_between(q->last_move_ftc, bufnr, count)) + if (!q->u.in.polling) + goto set; + + /* protect against stop polling setting an ACK for an emptied slsb */ + if (count == QDIO_MAX_BUFFERS_PER_Q) { + /* overwriting everything, just delete polling status */ + q->u.in.polling = 0; + q->u.in.ack_count = 0; + goto set; + } else if (buf_in_between(q->last_move_ftc, bufnr, count)) { + if (is_qebsm(q)) { + /* partial overwrite, just update last_move_ftc */ + diff = add_buf(bufnr, count); + diff = sub_buf(diff, q->last_move_ftc); + q->u.in.ack_count -= diff; + if (q->u.in.ack_count <= 0) { + q->u.in.polling = 0; + q->u.in.ack_count = 0; + /* TODO: must we set last_move_ftc to something meaningful? */ + goto set; + } + q->last_move_ftc = add_buf(q->last_move_ftc, diff); + } + else + /* the only ACK will be deleted, so stop polling */ q->u.in.polling = 0; + } +set: count = set_buf_states(q, bufnr, SLSB_CU_INPUT_EMPTY, count); - spin_unlock_irqrestore(&q->u.in.lock, flags); used = atomic_add_return(count, &q->nr_buf_used) - count; BUG_ON(used + count > QDIO_MAX_BUFFERS_PER_Q); @@ -1535,7 +1590,7 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags, } /* try to fast requeue buffers */ - get_buf_state(q, prev_buf(bufnr), &state); + get_buf_state(q, prev_buf(bufnr), &state, 0); if (state != SLSB_CU_OUTPUT_PRIMED) qdio_kick_outbound_q(q); else { -- cgit v1.1 From 7a0b4cbc7d7d99763511b488b08bbc2607ddd1e3 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Thu, 25 Dec 2008 13:38:48 +0100 Subject: [S390] qdio: fix error reporting for hipersockets Hipersocket connections can encounter temporary busy conditions. In case of the busy bit set we retry the SIGA operation immediatelly. If the busy condition still persists after 100 ms we fail and report the error to the upper layer. The second stage retry logic is removed. In case of ongoing busy conditions the upper layer needs to reset the connection. The reporting of a SIGA error is now done synchronously to allow the network driver to requeue the buffers. Also no error trace is created for the temporary SIGA errors so the error message view is not flooded. Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio_main.c | 150 ++++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 96 deletions(-) (limited to 'drivers/s390/cio/qdio_main.c') diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 0b4c09c..744f928 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -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; @@ -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); @@ -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; } @@ -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 && @@ -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; } } @@ -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); @@ -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; @@ -1532,9 +1486,9 @@ set: 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; } } @@ -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; } -- cgit v1.1