* 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;
        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);
        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;
 }
 
 
 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 &&
                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;
        }
 }
 
 
 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);
 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;
                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;
        }
 }
 
                                while (count--)
                                        qdio_kick_outbound_q(q);
                        }
+
+               /* report CC=2 conditions synchronously */
+               if (q->qdio_error)
+                       __qdio_outbound_processing(q);
                goto out;
        }