]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/s390/cio/qdio_main.c
Merge branch 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6
[linux-2.6-omap-h63xx.git] / drivers / s390 / cio / qdio_main.c
index 10cb0f8726e5a72a264e4b62a2cbb9c3e32b65b1..9e8a2914259bf1656eb22fe6f01b1cbdf600ae01 100644 (file)
@@ -380,11 +380,11 @@ inline void qdio_stop_polling(struct qdio_q *q)
 
        /* show the card that we are not polling anymore */
        if (is_qebsm(q)) {
-               set_buf_states(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT,
+               set_buf_states(q, q->u.in.ack_start, 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);
+               set_buf_state(q, q->u.in.ack_start, SLSB_P_INPUT_NOT_INIT);
 }
 
 static void announce_buffer_error(struct qdio_q *q, int count)
@@ -419,15 +419,15 @@ static inline void inbound_primed(struct qdio_q *q, int count)
                if (!q->u.in.polling) {
                        q->u.in.polling = 1;
                        q->u.in.ack_count = count;
-                       q->last_move_ftc = q->first_to_check;
+                       q->u.in.ack_start = q->first_to_check;
                        return;
                }
 
                /* delete the previous ACK's */
-               set_buf_states(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT,
+               set_buf_states(q, q->u.in.ack_start, SLSB_P_INPUT_NOT_INIT,
                               q->u.in.ack_count);
                q->u.in.ack_count = count;
-               q->last_move_ftc = q->first_to_check;
+               q->u.in.ack_start = q->first_to_check;
                return;
        }
 
@@ -439,14 +439,13 @@ static inline void inbound_primed(struct qdio_q *q, int count)
        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 {
+               set_buf_state(q, q->u.in.ack_start, SLSB_P_INPUT_NOT_INIT);
+       } else {
                q->u.in.polling = 1;
-               set_buf_state(q, q->first_to_check, SLSB_P_INPUT_ACK);
+               set_buf_state(q, new, SLSB_P_INPUT_ACK);
        }
 
-       q->last_move_ftc = new;
+       q->u.in.ack_start = new;
        count--;
        if (!count)
                return;
@@ -455,7 +454,7 @@ static inline void inbound_primed(struct qdio_q *q, int count)
         * 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,
+       set_buf_states(q, q->first_to_check, SLSB_P_INPUT_NOT_INIT,
                       count);
 }
 
@@ -523,7 +522,8 @@ int qdio_inbound_q_moved(struct qdio_q *q)
 
        bufnr = get_inbound_buffer_frontier(q);
 
-       if ((bufnr != q->last_move_ftc) || q->qdio_error) {
+       if ((bufnr != q->last_move) || q->qdio_error) {
+               q->last_move = bufnr;
                if (!need_siga_sync(q) && !pci_out_supported(q))
                        q->u.in.timestamp = get_usecs();
 
@@ -570,29 +570,30 @@ static int qdio_inbound_q_done(struct qdio_q *q)
        }
 }
 
-void qdio_kick_inbound_handler(struct qdio_q *q)
+void qdio_kick_handler(struct qdio_q *q)
 {
-       int count, start, end;
-
-       qdio_perf_stat_inc(&perf_stats.inbound_handler);
-
-       start = q->first_to_kick;
-       end = q->first_to_check;
-       if (end >= start)
-               count = end - start;
-       else
-               count = end + QDIO_MAX_BUFFERS_PER_Q - start;
-
-       DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kih s:%3d c:%3d", start, count);
+       int start = q->first_to_kick;
+       int end = q->first_to_check;
+       int count;
 
        if (unlikely(q->irq_ptr->state != QDIO_IRQ_STATE_ACTIVE))
                return;
 
-       q->handler(q->irq_ptr->cdev, q->qdio_error, q->nr,
-                  start, count, q->irq_ptr->int_parm);
+       count = sub_buf(end, start);
+
+       if (q->is_input_q) {
+               qdio_perf_stat_inc(&perf_stats.inbound_handler);
+               DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kih s:%3d c:%3d", start, count);
+       } else {
+               DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "koh: nr:%1d", q->nr);
+               DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "s:%3d c:%3d", start, count);
+       }
+
+       q->handler(q->irq_ptr->cdev, q->qdio_error, q->nr, start, count,
+                  q->irq_ptr->int_parm);
 
        /* for the next time */
-       q->first_to_kick = q->first_to_check;
+       q->first_to_kick = end;
        q->qdio_error = 0;
 }
 
@@ -603,7 +604,7 @@ again:
        if (!qdio_inbound_q_moved(q))
                return;
 
-       qdio_kick_inbound_handler(q);
+       qdio_kick_handler(q);
 
        if (!qdio_inbound_q_done(q))
                /* means poll time is not yet over */
@@ -698,21 +699,21 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q)
 
        bufnr = get_outbound_buffer_frontier(q);
 
-       if ((bufnr != q->last_move_ftc) || q->qdio_error) {
-               q->last_move_ftc = bufnr;
+       if ((bufnr != q->last_move) || q->qdio_error) {
+               q->last_move = bufnr;
                DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out moved:%1d", q->nr);
                return 1;
        } else
                return 0;
 }
 
-static void qdio_kick_outbound_q(struct qdio_q *q)
+static int qdio_kick_outbound_q(struct qdio_q *q)
 {
        unsigned int busy_bit;
        int cc;
 
        if (!need_siga_out(q))
-               return;
+               return 0;
 
        DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr);
        qdio_perf_stat_inc(&perf_stats.siga_out);
@@ -724,75 +725,37 @@ static void qdio_kick_outbound_q(struct qdio_q *q)
        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;
-               }
+                       cc |= QDIO_ERROR_SIGA_BUSY;
+               } else
+                       DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d", q->nr);
                break;
        case 1:
        case 3:
                DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc);
-               q->qdio_error = cc;
                break;
        }
-}
-
-static void qdio_kick_outbound_handler(struct qdio_q *q)
-{
-       int start, end, count;
-
-       start = q->first_to_kick;
-       end = q->last_move_ftc;
-       if (end >= start)
-               count = end - start;
-       else
-               count = end + QDIO_MAX_BUFFERS_PER_Q - start;
-
-       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;
-
-       q->handler(q->irq_ptr->cdev, q->qdio_error, q->nr, start, count,
-                  q->irq_ptr->int_parm);
-
-       /* for the next time: */
-       q->first_to_kick = q->last_move_ftc;
-       q->qdio_error = 0;
+       return cc;
 }
 
 static void __qdio_outbound_processing(struct qdio_q *q)
 {
-       unsigned long flags;
-
        qdio_perf_stat_inc(&perf_stats.tasklet_outbound);
-       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);
+               qdio_kick_handler(q);
 
-       if (queue_type(q) == QDIO_ZFCP_QFMT) {
+       if (queue_type(q) == QDIO_ZFCP_QFMT)
                if (!pci_out_supported(q) && !qdio_outbound_q_done(q))
-                       tasklet_schedule(&q->tasklet);
-               return;
-       }
+                       goto sched;
 
        /* bail out for HiperSockets unicast queues */
        if (queue_type(q) == QDIO_IQDIO_QFMT && !multicast_outbound(q))
                return;
 
        if ((queue_type(q) == QDIO_IQDIO_QFMT) &&
-           (atomic_read(&q->nr_buf_used)) > QDIO_IQDIO_POLL_LVL) {
-               tasklet_schedule(&q->tasklet);
-               return;
-       }
+           (atomic_read(&q->nr_buf_used)) > QDIO_IQDIO_POLL_LVL)
+               goto sched;
 
        if (q->u.out.pci_out_enabled)
                return;
@@ -810,6 +773,12 @@ static void __qdio_outbound_processing(struct qdio_q *q)
                        qdio_perf_stat_inc(&perf_stats.debug_tl_out_timer);
                }
        }
+       return;
+
+sched:
+       if (unlikely(q->irq_ptr->state == QDIO_IRQ_STATE_STOPPED))
+               return;
+       tasklet_schedule(&q->tasklet);
 }
 
 /* outbound tasklet */
@@ -822,6 +791,9 @@ void qdio_outbound_processing(unsigned long data)
 void qdio_outbound_timer(unsigned long data)
 {
        struct qdio_q *q = (struct qdio_q *)data;
+
+       if (unlikely(q->irq_ptr->state == QDIO_IRQ_STATE_STOPPED))
+               return;
        tasklet_schedule(&q->tasklet);
 }
 
@@ -863,6 +835,9 @@ static void qdio_int_handler_pci(struct qdio_irq *irq_ptr)
        int i;
        struct qdio_q *q;
 
+       if (unlikely(irq_ptr->state == QDIO_IRQ_STATE_STOPPED))
+               return;
+
        qdio_perf_stat_inc(&perf_stats.pci_int);
 
        for_each_input_queue(irq_ptr, q, i)
@@ -1065,8 +1040,9 @@ EXPORT_SYMBOL_GPL(qdio_get_ssqd_desc);
  * @cdev: associated ccw device
  * @how: use halt or clear to shutdown
  *
- * This function calls qdio_shutdown() for @cdev with method @how
- * and on success qdio_free() for @cdev.
+ * This function calls qdio_shutdown() for @cdev with method @how.
+ * and qdio_free(). The qdio_free() return value is ignored since
+ * !irq_ptr is already checked.
  */
 int qdio_cleanup(struct ccw_device *cdev, int how)
 {
@@ -1077,8 +1053,8 @@ int qdio_cleanup(struct ccw_device *cdev, int how)
                return -ENODEV;
 
        rc = qdio_shutdown(cdev, how);
-       if (rc == 0)
-               rc = qdio_free(cdev);
+
+       qdio_free(cdev);
        return rc;
 }
 EXPORT_SYMBOL_GPL(qdio_cleanup);
@@ -1090,11 +1066,11 @@ static void qdio_shutdown_queues(struct ccw_device *cdev)
        int i;
 
        for_each_input_queue(irq_ptr, q, i)
-               tasklet_disable(&q->tasklet);
+               tasklet_kill(&q->tasklet);
 
        for_each_output_queue(irq_ptr, q, i) {
-               tasklet_disable(&q->tasklet);
                del_timer(&q->u.out.timer);
+               tasklet_kill(&q->tasklet);
        }
 }
 
@@ -1112,6 +1088,7 @@ int qdio_shutdown(struct ccw_device *cdev, int how)
        if (!irq_ptr)
                return -ENODEV;
 
+       BUG_ON(irqs_disabled());
        DBF_EVENT("qshutdown:%4x", cdev->private->schid.sch_no);
 
        mutex_lock(&irq_ptr->setup_mutex);
@@ -1124,6 +1101,12 @@ int qdio_shutdown(struct ccw_device *cdev, int how)
                return 0;
        }
 
+       /*
+        * Indicate that the device is going down. Scheduling the queue
+        * tasklets is forbidden from here on.
+        */
+       qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED);
+
        tiqdio_remove_input_queues(irq_ptr);
        qdio_shutdown_queues(cdev);
        qdio_shutdown_debug_entries(irq_ptr, cdev);
@@ -1403,9 +1386,8 @@ int qdio_activate(struct ccw_device *cdev)
        switch (irq_ptr->state) {
        case QDIO_IRQ_STATE_STOPPED:
        case QDIO_IRQ_STATE_ERR:
-               mutex_unlock(&irq_ptr->setup_mutex);
-               qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
-               return -EIO;
+               rc = -EIO;
+               break;
        default:
                qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ACTIVE);
                rc = 0;
@@ -1442,10 +1424,10 @@ static inline int buf_in_between(int bufnr, int start, int count)
  * @bufnr: first buffer to process
  * @count: how many buffers are emptied
  */
-static void handle_inbound(struct qdio_q *q, unsigned int callflags,
-                          int bufnr, int count)
+static int handle_inbound(struct qdio_q *q, unsigned int callflags,
+                         int bufnr, int count)
 {
-       int used, cc, diff;
+       int used, diff;
 
        if (!q->u.in.polling)
                goto set;
@@ -1456,19 +1438,18 @@ static void handle_inbound(struct qdio_q *q, unsigned int callflags,
                q->u.in.polling = 0;
                q->u.in.ack_count = 0;
                goto set;
-       } else if (buf_in_between(q->last_move_ftc, bufnr, count)) {
+       } else if (buf_in_between(q->u.in.ack_start, bufnr, count)) {
                if (is_qebsm(q)) {
-                       /* partial overwrite, just update last_move_ftc */
+                       /* partial overwrite, just update ack_start */
                        diff = add_buf(bufnr, count);
-                       diff = sub_buf(diff, q->last_move_ftc);
+                       diff = sub_buf(diff, q->u.in.ack_start);
                        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);
+                       q->u.in.ack_start = add_buf(q->u.in.ack_start, diff);
                }
                else
                        /* the only ACK will be deleted, so stop polling */
@@ -1483,13 +1464,11 @@ set:
 
        /* no need to signal as long as the adapter had free buffers */
        if (used)
-               return;
+               return 0;
 
-       if (need_siga_in(q)) {
-               cc = qdio_siga_input(q);
-               if (cc)
-                       q->qdio_error = cc;
-       }
+       if (need_siga_in(q))
+               return qdio_siga_input(q);
+       return 0;
 }
 
 /**
@@ -1499,11 +1478,11 @@ set:
  * @bufnr: first buffer to process
  * @count: how many buffers are filled
  */
-static void handle_outbound(struct qdio_q *q, unsigned int callflags,
-                           int bufnr, int count)
+static int handle_outbound(struct qdio_q *q, unsigned int callflags,
+                          int bufnr, int count)
 {
        unsigned char state;
-       int used;
+       int used, rc = 0;
 
        qdio_perf_stat_inc(&perf_stats.outbound_handler);
 
@@ -1518,27 +1497,26 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags,
 
        if (queue_type(q) == QDIO_IQDIO_QFMT) {
                if (multicast_outbound(q))
-                       qdio_kick_outbound_q(q);
+                       rc = qdio_kick_outbound_q(q);
                else
                        if ((q->irq_ptr->ssqd_desc.mmwc > 1) &&
                            (count > 1) &&
                            (count <= q->irq_ptr->ssqd_desc.mmwc)) {
                                /* exploit enhanced SIGA */
                                q->u.out.use_enh_siga = 1;
-                               qdio_kick_outbound_q(q);
+                               rc = qdio_kick_outbound_q(q);
                        } else {
                                /*
                                * One siga-w per buffer required for unicast
                                * HiperSockets.
                                */
                                q->u.out.use_enh_siga = 0;
-                               while (count--)
-                                       qdio_kick_outbound_q(q);
+                               while (count--) {
+                                       rc = qdio_kick_outbound_q(q);
+                                       if (rc)
+                                               goto out;
+                               }
                        }
-
-               /* report CC=2 conditions synchronously */
-               if (q->qdio_error)
-                       __qdio_outbound_processing(q);
                goto out;
        }
 
@@ -1550,14 +1528,14 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags,
        /* try to fast requeue buffers */
        get_buf_state(q, prev_buf(bufnr), &state, 0);
        if (state != SLSB_CU_OUTPUT_PRIMED)
-               qdio_kick_outbound_q(q);
+               rc = qdio_kick_outbound_q(q);
        else {
                DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "fast-req");
                qdio_perf_stat_inc(&perf_stats.fast_requeue);
        }
 out:
-       /* Fixme: could wait forever if called from process context */
        tasklet_schedule(&q->tasklet);
+       return rc;
 }
 
 /**
@@ -1596,14 +1574,12 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags,
                return -EBUSY;
 
        if (callflags & QDIO_FLAG_SYNC_INPUT)
-               handle_inbound(irq_ptr->input_qs[q_nr], callflags, bufnr,
-                              count);
+               return 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
-               return -EINVAL;
-       return 0;
+               return handle_outbound(irq_ptr->output_qs[q_nr],
+                                      callflags, bufnr, count);
+       return -EINVAL;
 }
 EXPORT_SYMBOL_GPL(do_QDIO);