s390/zcrypt: introduce state machine for the AP bus
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Mon, 14 Sep 2015 15:01:23 +0000 (17:01 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 14 Oct 2015 12:32:23 +0000 (14:32 +0200)
Replace the two fields 'unregistered' and 'reset' with a device
state with 5 possible values. Introduce two events for the AP devices,
device poll and device timeout. With the state machine it is easier
to deal with device initialization and suspend/resume. Device polling
is simpler as well, the arkane 'flags' passing is gone.

Reviewd-by: Ingo Tuchscherer <ingo.tuchscherer@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
drivers/s390/crypto/ap_bus.c
drivers/s390/crypto/ap_bus.h

index 7224bf7fa9bf1db740711f6aeab98579329127c4..92d3b67b38390a5517fec204009fc23bcb886522 100644 (file)
 
 #include "ap_bus.h"
 
-/* Some prototypes. */
-static void ap_scan_bus(struct work_struct *);
-static void ap_poll_all(unsigned long);
-static void ap_request_timeout(unsigned long);
-static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags);
-static int ap_device_remove(struct device *dev);
-static int ap_device_probe(struct device *dev);
-static void ap_interrupt_handler(struct airq_struct *airq);
-static void ap_reset(struct ap_device *ap_dev, unsigned long *flags);
-static int ap_select_domain(void);
-
 /*
  * Module description.
  */
@@ -92,12 +81,14 @@ static LIST_HEAD(ap_device_list);
 static struct workqueue_struct *ap_work_queue;
 static struct timer_list ap_config_timer;
 static int ap_config_time = AP_CONFIG_TIME;
+static void ap_scan_bus(struct work_struct *);
 static DECLARE_WORK(ap_config_work, ap_scan_bus);
 
 /*
  * Tasklet & timer for AP request polling and interrupts
  */
-static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0);
+static void ap_tasklet_fn(unsigned long);
+static DECLARE_TASKLET(ap_tasklet, ap_tasklet_fn, 0);
 static atomic_t ap_poll_requests = ATOMIC_INIT(0);
 static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait);
 static struct task_struct *ap_poll_kthread = NULL;
@@ -119,6 +110,8 @@ static int user_set_domain = 0;
 static struct bus_type ap_bus_type;
 
 /* Adapter interrupt definitions */
+static void ap_interrupt_handler(struct airq_struct *airq);
+
 static int ap_airq_flag;
 
 static struct airq_struct ap_airq = {
@@ -343,7 +336,9 @@ static int ap_queue_enable_interruption(struct ap_device *ap_dev, void *ind)
        case AP_RESPONSE_DECONFIGURED:
        case AP_RESPONSE_CHECKSTOPPED:
        case AP_RESPONSE_INVALID_ADDRESS:
-               return -ENODEV;
+               pr_err("Registering adapter interrupts for AP %d failed\n",
+                      AP_QID_DEVICE(ap_dev->qid));
+               return -EOPNOTSUPP;
        case AP_RESPONSE_RESET_IN_PROGRESS:
        case AP_RESPONSE_BUSY:
        default:
@@ -468,37 +463,6 @@ int ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length)
 }
 EXPORT_SYMBOL(ap_recv);
 
-/**
- * __ap_schedule_poll_timer(): Schedule poll timer.
- *
- * Set up the timer to run the poll tasklet
- */
-static inline void __ap_schedule_poll_timer(void)
-{
-       ktime_t hr_time;
-
-       spin_lock_bh(&ap_poll_timer_lock);
-       if (!hrtimer_is_queued(&ap_poll_timer) && !ap_suspend_flag) {
-               hr_time = ktime_set(0, poll_timeout);
-               hrtimer_forward_now(&ap_poll_timer, hr_time);
-               hrtimer_restart(&ap_poll_timer);
-       }
-       spin_unlock_bh(&ap_poll_timer_lock);
-}
-
-/**
- * ap_schedule_poll_timer(): Schedule poll timer.
- *
- * Set up the timer to run the poll tasklet
- */
-static inline void ap_schedule_poll_timer(void)
-{
-       if (ap_using_interrupts())
-               return;
-       __ap_schedule_poll_timer();
-}
-
-
 /**
  * ap_query_queue(): Check if an AP queue is available.
  * @qid: The AP queue number
@@ -541,73 +505,404 @@ static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type,
        }
 }
 
+/* State machine definitions and helpers */
+
+static void ap_sm_wait(enum ap_wait wait)
+{
+       ktime_t hr_time;
+
+       switch (wait) {
+       case AP_WAIT_AGAIN:
+       case AP_WAIT_INTERRUPT:
+               if (ap_using_interrupts())
+                       break;
+               if (ap_poll_kthread) {
+                       wake_up(&ap_poll_wait);
+                       break;
+               }
+               /* Fall through */
+       case AP_WAIT_TIMEOUT:
+               spin_lock_bh(&ap_poll_timer_lock);
+               if (!hrtimer_is_queued(&ap_poll_timer)) {
+                       hr_time = ktime_set(0, poll_timeout);
+                       hrtimer_forward_now(&ap_poll_timer, hr_time);
+                       hrtimer_restart(&ap_poll_timer);
+               }
+               spin_unlock_bh(&ap_poll_timer_lock);
+               break;
+       case AP_WAIT_NONE:
+       default:
+               break;
+       }
+}
+
+static enum ap_wait ap_sm_nop(struct ap_device *ap_dev)
+{
+       return AP_WAIT_NONE;
+}
+
+/**
+ * ap_sm_recv(): Receive pending reply messages from an AP device but do
+ *     not change the state of the device.
+ * @ap_dev: pointer to the AP device
+ *
+ * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
+ */
+static struct ap_queue_status ap_sm_recv(struct ap_device *ap_dev)
+{
+       struct ap_queue_status status;
+       struct ap_message *ap_msg;
+
+       status = __ap_recv(ap_dev->qid, &ap_dev->reply->psmid,
+                          ap_dev->reply->message, ap_dev->reply->length);
+       switch (status.response_code) {
+       case AP_RESPONSE_NORMAL:
+               atomic_dec(&ap_poll_requests);
+               ap_dev->queue_count--;
+               if (ap_dev->queue_count > 0)
+                       mod_timer(&ap_dev->timeout,
+                                 jiffies + ap_dev->drv->request_timeout);
+               list_for_each_entry(ap_msg, &ap_dev->pendingq, list) {
+                       if (ap_msg->psmid != ap_dev->reply->psmid)
+                               continue;
+                       list_del_init(&ap_msg->list);
+                       ap_dev->pendingq_count--;
+                       ap_msg->receive(ap_dev, ap_msg, ap_dev->reply);
+                       break;
+               }
+       case AP_RESPONSE_NO_PENDING_REPLY:
+               if (!status.queue_empty || ap_dev->queue_count <= 0)
+                       break;
+               /* The card shouldn't forget requests but who knows. */
+               atomic_sub(ap_dev->queue_count, &ap_poll_requests);
+               ap_dev->queue_count = 0;
+               list_splice_init(&ap_dev->pendingq, &ap_dev->requestq);
+               ap_dev->requestq_count += ap_dev->pendingq_count;
+               ap_dev->pendingq_count = 0;
+               break;
+       default:
+               break;
+       }
+       return status;
+}
+
+/**
+ * ap_sm_read(): Receive pending reply messages from an AP device.
+ * @ap_dev: pointer to the AP device
+ *
+ * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
+ */
+static enum ap_wait ap_sm_read(struct ap_device *ap_dev)
+{
+       struct ap_queue_status status;
+
+       status = ap_sm_recv(ap_dev);
+       switch (status.response_code) {
+       case AP_RESPONSE_NORMAL:
+               if (ap_dev->queue_count > 0)
+                       return AP_WAIT_AGAIN;
+               ap_dev->state = AP_STATE_IDLE;
+               return AP_WAIT_NONE;
+       case AP_RESPONSE_NO_PENDING_REPLY:
+               if (ap_dev->queue_count > 0)
+                       return AP_WAIT_INTERRUPT;
+               ap_dev->state = AP_STATE_IDLE;
+               return AP_WAIT_NONE;
+       default:
+               ap_dev->state = AP_STATE_BORKED;
+               return AP_WAIT_NONE;
+       }
+}
+
+/**
+ * ap_sm_write(): Send messages from the request queue to an AP device.
+ * @ap_dev: pointer to the AP device
+ *
+ * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
+ */
+static enum ap_wait ap_sm_write(struct ap_device *ap_dev)
+{
+       struct ap_queue_status status;
+       struct ap_message *ap_msg;
+
+       if (ap_dev->requestq_count <= 0)
+               return AP_WAIT_NONE;
+       /* Start the next request on the queue. */
+       ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list);
+       status = __ap_send(ap_dev->qid, ap_msg->psmid,
+                          ap_msg->message, ap_msg->length, ap_msg->special);
+       switch (status.response_code) {
+       case AP_RESPONSE_NORMAL:
+               atomic_inc(&ap_poll_requests);
+               ap_dev->queue_count++;
+               if (ap_dev->queue_count == 1)
+                       mod_timer(&ap_dev->timeout,
+                                 jiffies + ap_dev->drv->request_timeout);
+               list_move_tail(&ap_msg->list, &ap_dev->pendingq);
+               ap_dev->requestq_count--;
+               ap_dev->pendingq_count++;
+               if (ap_dev->queue_count < ap_dev->queue_depth) {
+                       ap_dev->state = AP_STATE_WORKING;
+                       return AP_WAIT_AGAIN;
+               }
+               /* fall through */
+       case AP_RESPONSE_Q_FULL:
+               ap_dev->state = AP_STATE_QUEUE_FULL;
+               return AP_WAIT_INTERRUPT;
+       case AP_RESPONSE_RESET_IN_PROGRESS:
+               ap_dev->state = AP_STATE_RESET_WAIT;
+               return AP_WAIT_TIMEOUT;
+       case AP_RESPONSE_MESSAGE_TOO_BIG:
+       case AP_RESPONSE_REQ_FAC_NOT_INST:
+               list_del_init(&ap_msg->list);
+               ap_dev->requestq_count--;
+               ap_msg->rc = -EINVAL;
+               ap_msg->receive(ap_dev, ap_msg, NULL);
+               return AP_WAIT_AGAIN;
+       default:
+               ap_dev->state = AP_STATE_BORKED;
+               return AP_WAIT_NONE;
+       }
+}
+
 /**
- * ap_init_queue(): Reset an AP queue.
+ * ap_sm_read_write(): Send and receive messages to/from an AP device.
+ * @ap_dev: pointer to the AP device
+ *
+ * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
+ */
+static enum ap_wait ap_sm_read_write(struct ap_device *ap_dev)
+{
+       return min(ap_sm_read(ap_dev), ap_sm_write(ap_dev));
+}
+
+/**
+ * ap_sm_reset(): Reset an AP queue.
  * @qid: The AP queue number
  *
  * Submit the Reset command to an AP queue.
- * Since the reset is asynchron set the state to 'RESET_IN_PROGRESS'
- * and check later via ap_poll_queue() if the reset is done.
  */
-static int ap_init_queue(struct ap_device *ap_dev)
+static enum ap_wait ap_sm_reset(struct ap_device *ap_dev)
 {
        struct ap_queue_status status;
 
        status = ap_reset_queue(ap_dev->qid);
        switch (status.response_code) {
        case AP_RESPONSE_NORMAL:
-               ap_dev->interrupt = AP_INTR_DISABLED;
-               ap_dev->reset = AP_RESET_IN_PROGRESS;
-               return 0;
        case AP_RESPONSE_RESET_IN_PROGRESS:
+               ap_dev->state = AP_STATE_RESET_WAIT;
+               ap_dev->interrupt = AP_INTR_DISABLED;
+               return AP_WAIT_TIMEOUT;
        case AP_RESPONSE_BUSY:
-               return -EBUSY;
+               return AP_WAIT_TIMEOUT;
        case AP_RESPONSE_Q_NOT_AVAIL:
        case AP_RESPONSE_DECONFIGURED:
        case AP_RESPONSE_CHECKSTOPPED:
        default:
-               return -ENODEV;
+               ap_dev->state = AP_STATE_BORKED;
+               return AP_WAIT_NONE;
        }
 }
 
 /**
- * ap_increase_queue_count(): Arm request timeout.
- * @ap_dev: Pointer to an AP device.
+ * ap_sm_reset_wait(): Test queue for completion of the reset operation
+ * @ap_dev: pointer to the AP device
  *
- * Arm request timeout if an AP device was idle and a new request is submitted.
+ * Returns AP_POLL_IMMEDIATELY, AP_POLL_AFTER_TIMEROUT or 0.
  */
-static void ap_increase_queue_count(struct ap_device *ap_dev)
+static enum ap_wait ap_sm_reset_wait(struct ap_device *ap_dev)
 {
-       int timeout = ap_dev->drv->request_timeout;
+       struct ap_queue_status status;
+       unsigned long info;
 
-       ap_dev->queue_count++;
-       if (ap_dev->queue_count == 1) {
-               mod_timer(&ap_dev->timeout, jiffies + timeout);
-               ap_dev->reset = AP_RESET_ARMED;
+       if (ap_dev->queue_count > 0)
+               /* Try to read a completed message and get the status */
+               status = ap_sm_recv(ap_dev);
+       else
+               /* Get the status with TAPQ */
+               status = ap_test_queue(ap_dev->qid, &info);
+
+       switch (status.response_code) {
+       case AP_RESPONSE_NORMAL:
+               if (ap_using_interrupts() &&
+                   ap_queue_enable_interruption(ap_dev,
+                                                ap_airq.lsi_ptr) == 0)
+                       ap_dev->state = AP_STATE_SETIRQ_WAIT;
+               else
+                       ap_dev->state = (ap_dev->queue_count > 0) ?
+                               AP_STATE_WORKING : AP_STATE_IDLE;
+               return AP_WAIT_AGAIN;
+       case AP_RESPONSE_BUSY:
+       case AP_RESPONSE_RESET_IN_PROGRESS:
+               return AP_WAIT_TIMEOUT;
+       case AP_RESPONSE_Q_NOT_AVAIL:
+       case AP_RESPONSE_DECONFIGURED:
+       case AP_RESPONSE_CHECKSTOPPED:
+       default:
+               ap_dev->state = AP_STATE_BORKED;
+               return AP_WAIT_NONE;
        }
 }
 
 /**
- * ap_decrease_queue_count(): Decrease queue count.
- * @ap_dev: Pointer to an AP device.
+ * ap_sm_setirq_wait(): Test queue for completion of the irq enablement
+ * @ap_dev: pointer to the AP device
  *
- * If AP device is still alive, re-schedule request timeout if there are still
- * pending requests.
+ * Returns AP_POLL_IMMEDIATELY, AP_POLL_AFTER_TIMEROUT or 0.
  */
-static void ap_decrease_queue_count(struct ap_device *ap_dev)
+static enum ap_wait ap_sm_setirq_wait(struct ap_device *ap_dev)
 {
-       int timeout = ap_dev->drv->request_timeout;
+       struct ap_queue_status status;
+       unsigned long info;
 
-       ap_dev->queue_count--;
        if (ap_dev->queue_count > 0)
-               mod_timer(&ap_dev->timeout, jiffies + timeout);
+               /* Try to read a completed message and get the status */
+               status = ap_sm_recv(ap_dev);
        else
-               /*
-                * The timeout timer should to be disabled now - since
-                * del_timer_sync() is very expensive, we just tell via the
-                * reset flag to ignore the pending timeout timer.
-                */
-               ap_dev->reset = AP_RESET_IGNORE;
+               /* Get the status with TAPQ */
+               status = ap_test_queue(ap_dev->qid, &info);
+
+       if (status.int_enabled == 1) {
+               /* Irqs are now enabled */
+               ap_dev->interrupt = AP_INTR_ENABLED;
+               ap_dev->state = (ap_dev->queue_count > 0) ?
+                       AP_STATE_WORKING : AP_STATE_IDLE;
+       }
+
+       switch (status.response_code) {
+       case AP_RESPONSE_NORMAL:
+               if (ap_dev->queue_count > 0)
+                       return AP_WAIT_AGAIN;
+               /* fallthrough */
+       case AP_RESPONSE_NO_PENDING_REPLY:
+               return AP_WAIT_TIMEOUT;
+       default:
+               ap_dev->state = AP_STATE_BORKED;
+               return AP_WAIT_NONE;
+       }
+}
+
+/*
+ * AP state machine jump table
+ */
+ap_func_t *ap_jumptable[NR_AP_STATES][NR_AP_EVENTS] = {
+       [AP_STATE_RESET_START] = {
+               [AP_EVENT_POLL] = ap_sm_reset,
+               [AP_EVENT_TIMEOUT] = ap_sm_nop,
+       },
+       [AP_STATE_RESET_WAIT] = {
+               [AP_EVENT_POLL] = ap_sm_reset_wait,
+               [AP_EVENT_TIMEOUT] = ap_sm_nop,
+       },
+       [AP_STATE_SETIRQ_WAIT] = {
+               [AP_EVENT_POLL] = ap_sm_setirq_wait,
+               [AP_EVENT_TIMEOUT] = ap_sm_nop,
+       },
+       [AP_STATE_IDLE] = {
+               [AP_EVENT_POLL] = ap_sm_write,
+               [AP_EVENT_TIMEOUT] = ap_sm_nop,
+       },
+       [AP_STATE_WORKING] = {
+               [AP_EVENT_POLL] = ap_sm_read_write,
+               [AP_EVENT_TIMEOUT] = ap_sm_reset,
+       },
+       [AP_STATE_QUEUE_FULL] = {
+               [AP_EVENT_POLL] = ap_sm_read,
+               [AP_EVENT_TIMEOUT] = ap_sm_reset,
+       },
+       [AP_STATE_SUSPEND_WAIT] = {
+               [AP_EVENT_POLL] = ap_sm_read,
+               [AP_EVENT_TIMEOUT] = ap_sm_nop,
+       },
+       [AP_STATE_BORKED] = {
+               [AP_EVENT_POLL] = ap_sm_nop,
+               [AP_EVENT_TIMEOUT] = ap_sm_nop,
+       },
+};
+
+static inline enum ap_wait ap_sm_event(struct ap_device *ap_dev,
+                                      enum ap_event event)
+{
+       return ap_jumptable[ap_dev->state][event](ap_dev);
+}
+
+static inline enum ap_wait ap_sm_event_loop(struct ap_device *ap_dev,
+                                           enum ap_event event)
+{
+       enum ap_wait wait;
+
+       while ((wait = ap_sm_event(ap_dev, event)) == AP_WAIT_AGAIN)
+               ;
+       return wait;
+}
+
+/**
+ * ap_request_timeout(): Handling of request timeouts
+ * @data: Holds the AP device.
+ *
+ * Handles request timeouts.
+ */
+static void ap_request_timeout(unsigned long data)
+{
+       struct ap_device *ap_dev = (struct ap_device *) data;
+
+       if (ap_suspend_flag)
+               return;
+       spin_lock_bh(&ap_dev->lock);
+       ap_sm_wait(ap_sm_event(ap_dev, AP_EVENT_TIMEOUT));
+       spin_unlock_bh(&ap_dev->lock);
+}
+
+/**
+ * ap_poll_timeout(): AP receive polling for finished AP requests.
+ * @unused: Unused pointer.
+ *
+ * Schedules the AP tasklet using a high resolution timer.
+ */
+static enum hrtimer_restart ap_poll_timeout(struct hrtimer *unused)
+{
+       if (!ap_suspend_flag)
+               tasklet_schedule(&ap_tasklet);
+       return HRTIMER_NORESTART;
+}
+
+/**
+ * ap_interrupt_handler() - Schedule ap_tasklet on interrupt
+ * @airq: pointer to adapter interrupt descriptor
+ */
+static void ap_interrupt_handler(struct airq_struct *airq)
+{
+       inc_irq_stat(IRQIO_APB);
+       if (!ap_suspend_flag)
+               tasklet_schedule(&ap_tasklet);
+}
+
+/**
+ * ap_tasklet_fn(): Tasklet to poll all AP devices.
+ * @dummy: Unused variable
+ *
+ * Poll all AP devices on the bus.
+ */
+static void ap_tasklet_fn(unsigned long dummy)
+{
+       struct ap_device *ap_dev;
+       enum ap_wait wait = AP_WAIT_NONE;
+
+       /* Reset the indicator if interrupts are used. Thus new interrupts can
+        * be received. Doing it in the beginning of the tasklet is therefor
+        * important that no requests on any AP get lost.
+        */
+       if (ap_using_interrupts())
+               xchg(ap_airq.lsi_ptr, 0);
+
+       spin_lock(&ap_device_list_lock);
+       list_for_each_entry(ap_dev, &ap_device_list, list) {
+               spin_lock_bh(&ap_dev->lock);
+               wait = min(wait, ap_sm_event_loop(ap_dev, AP_EVENT_POLL));
+               spin_unlock_bh(&ap_dev->lock);
+       }
+       spin_unlock(&ap_device_list_lock);
+       ap_sm_wait(wait);
 }
 
 /**
@@ -623,8 +918,6 @@ static void ap_decrease_queue_count(struct ap_device *ap_dev)
 static int ap_poll_thread(void *data)
 {
        DECLARE_WAITQUEUE(wait, current);
-       unsigned long flags;
-       struct ap_device *ap_dev;
 
        set_user_nice(current, MAX_NICE);
        set_freezable();
@@ -638,21 +931,12 @@ static int ap_poll_thread(void *data)
                }
                set_current_state(TASK_RUNNING);
                remove_wait_queue(&ap_poll_wait, &wait);
-
                if (need_resched()) {
                        schedule();
                        try_to_freeze();
                        continue;
                }
-
-               flags = 0;
-               spin_lock_bh(&ap_device_list_lock);
-               list_for_each_entry(ap_dev, &ap_device_list, list) {
-                       spin_lock(&ap_dev->lock);
-                       __ap_poll_device(ap_dev, &flags);
-                       spin_unlock(&ap_dev->lock);
-               }
-               spin_unlock_bh(&ap_device_list_lock);
+               ap_tasklet_fn(0);
        } while (!kthread_should_stop());
        return 0;
 }
@@ -682,6 +966,57 @@ static void ap_poll_thread_stop(void)
        mutex_unlock(&ap_poll_thread_mutex);
 }
 
+/**
+ * ap_queue_message(): Queue a request to an AP device.
+ * @ap_dev: The AP device to queue the message to
+ * @ap_msg: The message that is to be added
+ */
+void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
+{
+       /* For asynchronous message handling a valid receive-callback
+        * is required. */
+       BUG_ON(!ap_msg->receive);
+
+       spin_lock_bh(&ap_dev->lock);
+       /* Queue the message. */
+       list_add_tail(&ap_msg->list, &ap_dev->requestq);
+       ap_dev->requestq_count++;
+       ap_dev->total_request_count++;
+       /* Send/receive as many request from the queue as possible. */
+       ap_sm_wait(ap_sm_event_loop(ap_dev, AP_EVENT_POLL));
+       spin_unlock_bh(&ap_dev->lock);
+}
+EXPORT_SYMBOL(ap_queue_message);
+
+/**
+ * ap_cancel_message(): Cancel a crypto request.
+ * @ap_dev: The AP device that has the message queued
+ * @ap_msg: The message that is to be removed
+ *
+ * Cancel a crypto request. This is done by removing the request
+ * from the device pending or request queue. Note that the
+ * request stays on the AP queue. When it finishes the message
+ * reply will be discarded because the psmid can't be found.
+ */
+void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
+{
+       struct ap_message *tmp;
+
+       spin_lock_bh(&ap_dev->lock);
+       if (!list_empty(&ap_msg->list)) {
+               list_for_each_entry(tmp, &ap_dev->pendingq, list)
+                       if (tmp->psmid == ap_msg->psmid) {
+                               ap_dev->pendingq_count--;
+                               goto found;
+                       }
+               ap_dev->requestq_count--;
+found:
+               list_del_init(&ap_msg->list);
+       }
+       spin_unlock_bh(&ap_dev->lock);
+}
+EXPORT_SYMBOL(ap_cancel_message);
+
 /*
  * AP device related attributes.
  */
@@ -762,21 +1097,17 @@ static ssize_t ap_reset_show(struct device *dev,
        int rc = 0;
 
        spin_lock_bh(&ap_dev->lock);
-       switch (ap_dev->reset) {
-       case AP_RESET_IGNORE:
-               rc = snprintf(buf, PAGE_SIZE, "No Reset Timer set.\n");
+       switch (ap_dev->state) {
+       case AP_STATE_RESET_START:
+       case AP_STATE_RESET_WAIT:
+               rc = snprintf(buf, PAGE_SIZE, "Reset in progress.\n");
                break;
-       case AP_RESET_ARMED:
+       case AP_STATE_WORKING:
+       case AP_STATE_QUEUE_FULL:
                rc = snprintf(buf, PAGE_SIZE, "Reset Timer armed.\n");
                break;
-       case AP_RESET_DO:
-               rc = snprintf(buf, PAGE_SIZE, "Reset Timer expired.\n");
-               break;
-       case AP_RESET_IN_PROGRESS:
-               rc = snprintf(buf, PAGE_SIZE, "Reset in progress.\n");
-               break;
        default:
-               break;
+               rc = snprintf(buf, PAGE_SIZE, "No Reset Timer set.\n");
        }
        spin_unlock_bh(&ap_dev->lock);
        return rc;
@@ -791,17 +1122,12 @@ static ssize_t ap_interrupt_show(struct device *dev,
        int rc = 0;
 
        spin_lock_bh(&ap_dev->lock);
-       switch (ap_dev->interrupt) {
-       case AP_INTR_DISABLED:
-               rc = snprintf(buf, PAGE_SIZE, "Interrupts disabled.\n");
-               break;
-       case AP_INTR_ENABLED:
-               rc = snprintf(buf, PAGE_SIZE, "Interrupts enabled.\n");
-               break;
-       case AP_INTR_IN_PROGRESS:
+       if (ap_dev->state == AP_STATE_SETIRQ_WAIT)
                rc = snprintf(buf, PAGE_SIZE, "Enable Interrupt pending.\n");
-               break;
-       }
+       else if (ap_dev->interrupt == AP_INTR_ENABLED)
+               rc = snprintf(buf, PAGE_SIZE, "Interrupts enabled.\n");
+       else
+               rc = snprintf(buf, PAGE_SIZE, "Interrupts disabled.\n");
        spin_unlock_bh(&ap_dev->lock);
        return rc;
 }
@@ -898,16 +1224,14 @@ static int ap_uevent (struct device *dev, struct kobj_uevent_env *env)
 static int ap_dev_suspend(struct device *dev, pm_message_t state)
 {
        struct ap_device *ap_dev = to_ap_dev(dev);
-       unsigned long flags;
 
        /* Poll on the device until all requests are finished. */
-       do {
-               flags = 0;
-               spin_lock_bh(&ap_dev->lock);
-               __ap_poll_device(ap_dev, &flags);
-               spin_unlock_bh(&ap_dev->lock);
-       } while ((flags & 1) || (flags & 2));
-
+       spin_lock_bh(&ap_dev->lock);
+       ap_dev->state = AP_STATE_SUSPEND_WAIT;
+       while (ap_sm_event(ap_dev, AP_EVENT_POLL) != AP_WAIT_NONE)
+               ;
+       ap_dev->state = AP_STATE_BORKED;
+       spin_unlock_bh(&ap_dev->lock);
        return 0;
 }
 
@@ -957,7 +1281,6 @@ static void ap_bus_resume(void)
                xchg(ap_airq.lsi_ptr, 0);
        tasklet_enable(&ap_tasklet);
        queue_work(ap_work_queue, &ap_config_work);
-       wake_up(&ap_poll_wait);
 }
 
 static int ap_power_event(struct notifier_block *this, unsigned long event,
@@ -993,24 +1316,12 @@ static int ap_device_probe(struct device *dev)
 {
        struct ap_device *ap_dev = to_ap_dev(dev);
        struct ap_driver *ap_drv = to_ap_drv(dev->driver);
-       int rc;
-
-       ap_dev->drv = ap_drv;
-
-       spin_lock_bh(&ap_device_list_lock);
-       list_add(&ap_dev->list, &ap_device_list);
-       spin_unlock_bh(&ap_device_list_lock);
+       int rc;
 
+       ap_dev->drv = ap_drv;
        rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV;
-       if (rc) {
-               spin_lock_bh(&ap_device_list_lock);
-               list_del_init(&ap_dev->list);
-               spin_unlock_bh(&ap_device_list_lock);
-       } else {
-               if (ap_dev->reset == AP_RESET_IN_PROGRESS ||
-                       ap_dev->interrupt == AP_INTR_IN_PROGRESS)
-                       __ap_schedule_poll_timer();
-       }
+       if (rc)
+               ap_dev->drv = NULL;
        return rc;
 }
 
@@ -1064,6 +1375,11 @@ static int ap_device_remove(struct device *dev)
        return 0;
 }
 
+static void ap_device_release(struct device *dev)
+{
+       kfree(to_ap_dev(dev));
+}
+
 int ap_driver_register(struct ap_driver *ap_drv, struct module *owner,
                       char *name)
 {
@@ -1371,13 +1687,6 @@ out:
        return rc;
 }
 
-static void ap_interrupt_handler(struct airq_struct *airq)
-{
-       inc_irq_stat(IRQIO_APB);
-       if (!ap_suspend_flag)
-               tasklet_schedule(&ap_tasklet);
-}
-
 /**
  * __ap_scan_bus(): Scan the AP bus.
  * @dev: Pointer to device
@@ -1390,21 +1699,14 @@ static int __ap_scan_bus(struct device *dev, void *data)
        return to_ap_dev(dev)->qid == (ap_qid_t)(unsigned long) data;
 }
 
-static void ap_device_release(struct device *dev)
-{
-       struct ap_device *ap_dev = to_ap_dev(dev);
-
-       kfree(ap_dev);
-}
-
 static void ap_scan_bus(struct work_struct *unused)
 {
        struct ap_device *ap_dev;
        struct device *dev;
        ap_qid_t qid;
        int queue_depth = 0, device_type = 0;
-       unsigned int device_functions;
-       int rc, i;
+       unsigned int device_functions = 0;
+       int rc, i, borked;
 
        ap_query_configuration();
        if (ap_select_domain() != 0)
@@ -1420,17 +1722,15 @@ static void ap_scan_bus(struct work_struct *unused)
                if (dev) {
                        ap_dev = to_ap_dev(dev);
                        spin_lock_bh(&ap_dev->lock);
-                       if (rc == -ENODEV || ap_dev->unregistered) {
-                               spin_unlock_bh(&ap_dev->lock);
-                               if (ap_dev->unregistered)
-                                       i--;
-                               device_unregister(dev);
-                               put_device(dev);
-                               continue;
-                       }
+                       if (rc == -ENODEV)
+                               ap_dev->state = AP_STATE_BORKED;
+                       borked = ap_dev->state == AP_STATE_BORKED;
                        spin_unlock_bh(&ap_dev->lock);
+                       if (borked)     /* Remove broken device */
+                               device_unregister(dev);
                        put_device(dev);
-                       continue;
+                       if (!borked)
+                               continue;
                }
                if (rc)
                        continue;
@@ -1438,16 +1738,12 @@ static void ap_scan_bus(struct work_struct *unused)
                if (!ap_dev)
                        break;
                ap_dev->qid = qid;
-               rc = ap_init_queue(ap_dev);
-               if ((rc != 0) && (rc != -EBUSY)) {
-                       kfree(ap_dev);
-                       continue;
-               }
+               ap_dev->state = AP_STATE_RESET_START;
+               ap_dev->interrupt = AP_INTR_DISABLED;
                ap_dev->queue_depth = queue_depth;
                ap_dev->raw_hwtype = device_type;
                ap_dev->device_type = device_type;
                ap_dev->functions = device_functions;
-               ap_dev->unregistered = 1;
                spin_lock_init(&ap_dev->lock);
                INIT_LIST_HEAD(&ap_dev->pendingq);
                INIT_LIST_HEAD(&ap_dev->requestq);
@@ -1463,27 +1759,37 @@ static void ap_scan_bus(struct work_struct *unused)
 
                ap_dev->device.bus = &ap_bus_type;
                ap_dev->device.parent = ap_root_device;
-               if (dev_set_name(&ap_dev->device, "card%02x",
-                                AP_QID_DEVICE(ap_dev->qid))) {
+               rc = dev_set_name(&ap_dev->device, "card%02x",
+                                 AP_QID_DEVICE(ap_dev->qid));
+               if (rc) {
                        kfree(ap_dev);
                        continue;
                }
+               /* Add to list of devices */
+               spin_lock_bh(&ap_device_list_lock);
+               list_add(&ap_dev->list, &ap_device_list);
+               spin_unlock_bh(&ap_device_list_lock);
+               /* Start with a device reset */
+               spin_lock_bh(&ap_dev->lock);
+               ap_sm_wait(ap_sm_event(ap_dev, AP_EVENT_POLL));
+               spin_unlock_bh(&ap_dev->lock);
+               /* Register device */
                ap_dev->device.release = ap_device_release;
                rc = device_register(&ap_dev->device);
                if (rc) {
+                       spin_lock_bh(&ap_dev->lock);
+                       list_del_init(&ap_dev->list);
+                       spin_unlock_bh(&ap_dev->lock);
                        put_device(&ap_dev->device);
                        continue;
                }
                /* Add device attributes. */
                rc = sysfs_create_group(&ap_dev->device.kobj,
                                        &ap_dev_attr_group);
-               if (!rc) {
-                       spin_lock_bh(&ap_dev->lock);
-                       ap_dev->unregistered = 0;
-                       spin_unlock_bh(&ap_dev->lock);
-               }
-               else
+               if (rc) {
                        device_unregister(&ap_dev->device);
+                       continue;
+               }
        }
 out:
        mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ);
@@ -1496,377 +1802,6 @@ static void ap_config_timeout(unsigned long ptr)
        queue_work(ap_work_queue, &ap_config_work);
 }
 
-/**
- * ap_poll_read(): Receive pending reply messages from an AP device.
- * @ap_dev: pointer to the AP device
- * @flags: pointer to control flags, bit 2^0 is set if another poll is
- *        required, bit 2^1 is set if the poll timer needs to get armed
- *
- * Returns 0 if the device is still present, -ENODEV if not.
- */
-static int ap_poll_read(struct ap_device *ap_dev, unsigned long *flags)
-{
-       struct ap_queue_status status;
-       struct ap_message *ap_msg;
-
-       if (ap_dev->queue_count <= 0)
-               return 0;
-       status = __ap_recv(ap_dev->qid, &ap_dev->reply->psmid,
-                          ap_dev->reply->message, ap_dev->reply->length);
-       switch (status.response_code) {
-       case AP_RESPONSE_NORMAL:
-               ap_dev->interrupt = status.int_enabled;
-               atomic_dec(&ap_poll_requests);
-               ap_decrease_queue_count(ap_dev);
-               list_for_each_entry(ap_msg, &ap_dev->pendingq, list) {
-                       if (ap_msg->psmid != ap_dev->reply->psmid)
-                               continue;
-                       list_del_init(&ap_msg->list);
-                       ap_dev->pendingq_count--;
-                       ap_msg->receive(ap_dev, ap_msg, ap_dev->reply);
-                       break;
-               }
-               if (ap_dev->queue_count > 0)
-                       *flags |= 1;
-               break;
-       case AP_RESPONSE_NO_PENDING_REPLY:
-               ap_dev->interrupt = status.int_enabled;
-               if (status.queue_empty) {
-                       /* The card shouldn't forget requests but who knows. */
-                       atomic_sub(ap_dev->queue_count, &ap_poll_requests);
-                       ap_dev->queue_count = 0;
-                       list_splice_init(&ap_dev->pendingq, &ap_dev->requestq);
-                       ap_dev->requestq_count += ap_dev->pendingq_count;
-                       ap_dev->pendingq_count = 0;
-               } else
-                       *flags |= 2;
-               break;
-       default:
-               return -ENODEV;
-       }
-       return 0;
-}
-
-/**
- * ap_poll_write(): Send messages from the request queue to an AP device.
- * @ap_dev: pointer to the AP device
- * @flags: pointer to control flags, bit 2^0 is set if another poll is
- *        required, bit 2^1 is set if the poll timer needs to get armed
- *
- * Returns 0 if the device is still present, -ENODEV if not.
- */
-static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags)
-{
-       struct ap_queue_status status;
-       struct ap_message *ap_msg;
-
-       if (ap_dev->requestq_count <= 0 ||
-           (ap_dev->queue_count >= ap_dev->queue_depth) ||
-           (ap_dev->reset == AP_RESET_IN_PROGRESS))
-               return 0;
-       /* Start the next request on the queue. */
-       ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list);
-       status = __ap_send(ap_dev->qid, ap_msg->psmid,
-                          ap_msg->message, ap_msg->length, ap_msg->special);
-       switch (status.response_code) {
-       case AP_RESPONSE_NORMAL:
-               atomic_inc(&ap_poll_requests);
-               ap_increase_queue_count(ap_dev);
-               list_move_tail(&ap_msg->list, &ap_dev->pendingq);
-               ap_dev->requestq_count--;
-               ap_dev->pendingq_count++;
-               if (ap_dev->queue_count < ap_dev->queue_depth &&
-                   ap_dev->requestq_count > 0)
-                       *flags |= 1;
-               *flags |= 2;
-               break;
-       case AP_RESPONSE_RESET_IN_PROGRESS:
-               __ap_schedule_poll_timer();
-       case AP_RESPONSE_Q_FULL:
-               *flags |= 2;
-               break;
-       case AP_RESPONSE_MESSAGE_TOO_BIG:
-       case AP_RESPONSE_REQ_FAC_NOT_INST:
-               return -EINVAL;
-       default:
-               return -ENODEV;
-       }
-       return 0;
-}
-
-/**
- * ap_poll_queue(): Poll AP device for pending replies and send new messages.
- * Check if the queue has a pending reset. In case it's done re-enable
- * interrupts, otherwise reschedule the poll_timer for another attempt.
- * @ap_dev: pointer to the bus device
- * @flags: pointer to control flags, bit 2^0 is set if another poll is
- *        required, bit 2^1 is set if the poll timer needs to get armed
- *
- * Poll AP device for pending replies and send new messages. If either
- * ap_poll_read or ap_poll_write returns -ENODEV unregister the device.
- * Returns 0.
- */
-static inline int ap_poll_queue(struct ap_device *ap_dev, unsigned long *flags)
-{
-       struct ap_queue_status status;
-       int rc;
-
-       if (ap_dev->reset == AP_RESET_IN_PROGRESS) {
-               status = ap_test_queue(ap_dev->qid, NULL);
-               switch (status.response_code) {
-               case AP_RESPONSE_NORMAL:
-                       ap_dev->reset = AP_RESET_IGNORE;
-                       if (ap_using_interrupts()) {
-                               rc = ap_queue_enable_interruption(
-                                       ap_dev, ap_airq.lsi_ptr);
-                               if (!rc)
-                                       ap_dev->interrupt = AP_INTR_IN_PROGRESS;
-                               else if (rc == -ENODEV) {
-                                       pr_err("Registering adapter interrupts for "
-                                       "AP %d failed\n", AP_QID_DEVICE(ap_dev->qid));
-                                       return rc;
-                               }
-                       }
-                       /* fall through */
-               case AP_RESPONSE_BUSY:
-               case AP_RESPONSE_RESET_IN_PROGRESS:
-                       *flags |= AP_POLL_AFTER_TIMEOUT;
-                       break;
-               case AP_RESPONSE_Q_NOT_AVAIL:
-               case AP_RESPONSE_DECONFIGURED:
-               case AP_RESPONSE_CHECKSTOPPED:
-                       return -ENODEV;
-               default:
-                       break;
-               }
-       }
-
-       if ((ap_dev->reset != AP_RESET_IN_PROGRESS) &&
-               (ap_dev->interrupt == AP_INTR_IN_PROGRESS)) {
-               status = ap_test_queue(ap_dev->qid, NULL);
-               if (ap_using_interrupts()) {
-                       if (status.int_enabled == 1)
-                               ap_dev->interrupt = AP_INTR_ENABLED;
-                       else
-                               *flags |= AP_POLL_AFTER_TIMEOUT;
-               } else
-                       ap_dev->interrupt = AP_INTR_DISABLED;
-       }
-
-       rc = ap_poll_read(ap_dev, flags);
-       if (rc)
-               return rc;
-       return ap_poll_write(ap_dev, flags);
-}
-
-/**
- * __ap_queue_message(): Queue a message to a device.
- * @ap_dev: pointer to the AP device
- * @ap_msg: the message to be queued
- *
- * Queue a message to a device. Returns 0 if successful.
- */
-static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
-{
-       struct ap_queue_status status;
-
-       if (list_empty(&ap_dev->requestq) &&
-           (ap_dev->queue_count < ap_dev->queue_depth) &&
-           (ap_dev->reset != AP_RESET_IN_PROGRESS)) {
-               status = __ap_send(ap_dev->qid, ap_msg->psmid,
-                                  ap_msg->message, ap_msg->length,
-                                  ap_msg->special);
-               switch (status.response_code) {
-               case AP_RESPONSE_NORMAL:
-                       list_add_tail(&ap_msg->list, &ap_dev->pendingq);
-                       atomic_inc(&ap_poll_requests);
-                       ap_dev->pendingq_count++;
-                       ap_increase_queue_count(ap_dev);
-                       ap_dev->total_request_count++;
-                       break;
-               case AP_RESPONSE_Q_FULL:
-               case AP_RESPONSE_RESET_IN_PROGRESS:
-                       list_add_tail(&ap_msg->list, &ap_dev->requestq);
-                       ap_dev->requestq_count++;
-                       ap_dev->total_request_count++;
-                       return -EBUSY;
-               case AP_RESPONSE_REQ_FAC_NOT_INST:
-               case AP_RESPONSE_MESSAGE_TOO_BIG:
-                       ap_msg->rc = -EINVAL;
-                       ap_msg->receive(ap_dev, ap_msg, NULL);
-                       return -EINVAL;
-               default:        /* Device is gone. */
-                       ap_msg->rc = -ENODEV;
-                       ap_msg->receive(ap_dev, ap_msg, NULL);
-                       return -ENODEV;
-               }
-       } else {
-               list_add_tail(&ap_msg->list, &ap_dev->requestq);
-               ap_dev->requestq_count++;
-               ap_dev->total_request_count++;
-               return -EBUSY;
-       }
-       ap_schedule_poll_timer();
-       return 0;
-}
-
-void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
-{
-       unsigned long flags;
-       int rc;
-
-       /* For asynchronous message handling a valid receive-callback
-        * is required. */
-       BUG_ON(!ap_msg->receive);
-
-       spin_lock_bh(&ap_dev->lock);
-       if (!ap_dev->unregistered) {
-               /* Make room on the queue by polling for finished requests. */
-               rc = ap_poll_queue(ap_dev, &flags);
-               if (!rc)
-                       rc = __ap_queue_message(ap_dev, ap_msg);
-               if (!rc)
-                       wake_up(&ap_poll_wait);
-               if (rc == -ENODEV)
-                       ap_dev->unregistered = 1;
-       } else {
-               ap_msg->rc = -ENODEV;
-               ap_msg->receive(ap_dev, ap_msg, NULL);
-               rc = -ENODEV;
-       }
-       spin_unlock_bh(&ap_dev->lock);
-}
-EXPORT_SYMBOL(ap_queue_message);
-
-/**
- * ap_cancel_message(): Cancel a crypto request.
- * @ap_dev: The AP device that has the message queued
- * @ap_msg: The message that is to be removed
- *
- * Cancel a crypto request. This is done by removing the request
- * from the device pending or request queue. Note that the
- * request stays on the AP queue. When it finishes the message
- * reply will be discarded because the psmid can't be found.
- */
-void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
-{
-       struct ap_message *tmp;
-
-       spin_lock_bh(&ap_dev->lock);
-       if (!list_empty(&ap_msg->list)) {
-               list_for_each_entry(tmp, &ap_dev->pendingq, list)
-                       if (tmp->psmid == ap_msg->psmid) {
-                               ap_dev->pendingq_count--;
-                               goto found;
-                       }
-               ap_dev->requestq_count--;
-       found:
-               list_del_init(&ap_msg->list);
-       }
-       spin_unlock_bh(&ap_dev->lock);
-}
-EXPORT_SYMBOL(ap_cancel_message);
-
-/**
- * ap_poll_timeout(): AP receive polling for finished AP requests.
- * @unused: Unused pointer.
- *
- * Schedules the AP tasklet using a high resolution timer.
- */
-static enum hrtimer_restart ap_poll_timeout(struct hrtimer *unused)
-{
-       if (!ap_suspend_flag)
-               tasklet_schedule(&ap_tasklet);
-       return HRTIMER_NORESTART;
-}
-
-/**
- * ap_reset(): Reset a not responding AP device.
- * @ap_dev: Pointer to the AP device
- *
- * Reset a not responding AP device and move all requests from the
- * pending queue to the request queue.
- */
-static void ap_reset(struct ap_device *ap_dev, unsigned long *flags)
-{
-       int rc;
-
-       atomic_sub(ap_dev->queue_count, &ap_poll_requests);
-       ap_dev->queue_count = 0;
-       list_splice_init(&ap_dev->pendingq, &ap_dev->requestq);
-       ap_dev->requestq_count += ap_dev->pendingq_count;
-       ap_dev->pendingq_count = 0;
-       rc = ap_init_queue(ap_dev);
-       if (rc == -ENODEV)
-               ap_dev->unregistered = 1;
-       else
-               *flags |= AP_POLL_AFTER_TIMEOUT;
-}
-
-static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags)
-{
-       if (!ap_dev->unregistered) {
-               if (ap_poll_queue(ap_dev, flags))
-                       ap_dev->unregistered = 1;
-               if (ap_dev->reset == AP_RESET_DO)
-                       ap_reset(ap_dev, flags);
-       }
-       return 0;
-}
-
-/**
- * ap_poll_all(): Poll all AP devices.
- * @dummy: Unused variable
- *
- * Poll all AP devices on the bus in a round robin fashion. Continue
- * polling until bit 2^0 of the control flags is not set. If bit 2^1
- * of the control flags has been set arm the poll timer.
- */
-static void ap_poll_all(unsigned long dummy)
-{
-       unsigned long flags;
-       struct ap_device *ap_dev;
-
-       /* Reset the indicator if interrupts are used. Thus new interrupts can
-        * be received. Doing it in the beginning of the tasklet is therefor
-        * important that no requests on any AP get lost.
-        */
-       if (ap_using_interrupts())
-               xchg(ap_airq.lsi_ptr, 0);
-       do {
-               flags = 0;
-               spin_lock(&ap_device_list_lock);
-               list_for_each_entry(ap_dev, &ap_device_list, list) {
-                       spin_lock(&ap_dev->lock);
-                       __ap_poll_device(ap_dev, &flags);
-                       spin_unlock(&ap_dev->lock);
-               }
-               spin_unlock(&ap_device_list_lock);
-       } while (flags & AP_POLL_IMMEDIATELY);
-       if (flags & AP_POLL_AFTER_TIMEOUT)
-               __ap_schedule_poll_timer();
-}
-
-/**
- * ap_request_timeout(): Handling of request timeouts
- * @data: Holds the AP device.
- *
- * Handles request timeouts.
- */
-static void ap_request_timeout(unsigned long data)
-{
-       struct ap_device *ap_dev = (struct ap_device *) data;
-
-       if (ap_suspend_flag)
-               return;
-       if (ap_dev->reset == AP_RESET_ARMED) {
-               ap_dev->reset = AP_RESET_DO;
-
-               if (ap_using_interrupts())
-                       tasklet_schedule(&ap_tasklet);
-       }
-}
-
 static void ap_reset_domain(void)
 {
        int i;
@@ -1958,14 +1893,11 @@ int __init ap_module_init(void)
                goto out_root;
        }
 
-       if (ap_select_domain() == 0)
-               ap_scan_bus(NULL);
-
        /* Setup the AP bus rescan timer. */
        setup_timer(&ap_config_timer, ap_config_timeout, 0);
-       add_timer(&ap_config_timer);
 
-       /* Setup the high resultion poll timer.
+       /*
+        * Setup the high resultion poll timer.
         * If we are running under z/VM adjust polling to z/VM polling rate.
         */
        if (MACHINE_IS_VM)
@@ -1985,12 +1917,13 @@ int __init ap_module_init(void)
        if (rc)
                goto out_pm;
 
+       queue_work(ap_work_queue, &ap_config_work);
+
        return 0;
 
 out_pm:
        ap_poll_thread_stop();
 out_work:
-       del_timer_sync(&ap_config_timer);
        hrtimer_cancel(&ap_poll_timer);
        destroy_workqueue(ap_work_queue);
 out_root:
index af9b705976bc19f5a6436f48dfb1a9b06297724e..6adcbdf225d19754e0ec8e8e468e176428452fe0 100644 (file)
@@ -36,9 +36,6 @@
 #define AP_CONFIG_TIME 30      /* Time in seconds between AP bus rescans. */
 #define AP_POLL_TIME 1         /* Time in ticks between receive polls. */
 
-#define AP_POLL_IMMEDIATELY    1 /* continue running poll tasklet */
-#define AP_POLL_AFTER_TIMEOUT  2 /* run poll tasklet again after timout */
-
 extern int ap_domain_index;
 
 /**
@@ -119,20 +116,46 @@ static inline int ap_test_bit(unsigned int *ptr, unsigned int nr)
 #define AP_FUNC_EP11  5
 #define AP_FUNC_APXA  6
 
-/*
- * AP reset flag states
- */
-#define AP_RESET_IGNORE        0       /* request timeout will be ignored */
-#define AP_RESET_ARMED 1       /* request timeout timer is active */
-#define AP_RESET_DO    2       /* AP reset required */
-#define AP_RESET_IN_PROGRESS   3       /* AP reset in progress */
-
 /*
  * AP interrupt states
  */
 #define AP_INTR_DISABLED       0       /* AP interrupt disabled */
 #define AP_INTR_ENABLED                1       /* AP interrupt enabled */
-#define AP_INTR_IN_PROGRESS    3       /* AP interrupt in progress */
+
+/*
+ * AP device states
+ */
+enum ap_state {
+       AP_STATE_RESET_START,
+       AP_STATE_RESET_WAIT,
+       AP_STATE_SETIRQ_WAIT,
+       AP_STATE_IDLE,
+       AP_STATE_WORKING,
+       AP_STATE_QUEUE_FULL,
+       AP_STATE_SUSPEND_WAIT,
+       AP_STATE_BORKED,
+       NR_AP_STATES
+};
+
+/*
+ * AP device events
+ */
+enum ap_event {
+       AP_EVENT_POLL,
+       AP_EVENT_TIMEOUT,
+       NR_AP_EVENTS
+};
+
+/*
+ * AP wait behaviour
+ */
+enum ap_wait {
+       AP_WAIT_AGAIN,          /* retry immediately */
+       AP_WAIT_TIMEOUT,        /* wait for timeout */
+       AP_WAIT_INTERRUPT,      /* wait for thin interrupt (if available) */
+       AP_WAIT_NONE,           /* no wait */
+       NR_AP_WAIT
+};
 
 struct ap_device;
 struct ap_message;
@@ -151,20 +174,22 @@ struct ap_driver {
 int ap_driver_register(struct ap_driver *, struct module *, char *);
 void ap_driver_unregister(struct ap_driver *);
 
+typedef enum ap_wait (ap_func_t)(struct ap_device *ap_dev);
+
 struct ap_device {
        struct device device;
        struct ap_driver *drv;          /* Pointer to AP device driver. */
        spinlock_t lock;                /* Per device lock. */
        struct list_head list;          /* private list of all AP devices. */
 
+       enum ap_state state;            /* State of the AP device. */
+
        ap_qid_t qid;                   /* AP queue id. */
        int queue_depth;                /* AP queue depth.*/
        int device_type;                /* AP device type. */
        int raw_hwtype;                 /* AP raw hardware type. */
        unsigned int functions;         /* AP device function bitfield. */
-       int unregistered;               /* marks AP device as unregistered */
        struct timer_list timeout;      /* Timer for request timeouts. */
-       int reset;                      /* Reset required after req. timeout. */
 
        int interrupt;                  /* indicate if interrupts are enabled */
        int queue_count;                /* # messages currently on AP queue. */