[S390] zcrypt: Do not add/remove devices in s/r callbacks

Devices are no longer removed or added in the suspend and resume
callbacks. Instead they are marked unregistered in suspend. In the
resume callback the ap_scan_bus method is scheduled. The bus scan
function will remove the old device and add new ones. This way all
the device handling will be done in only one function. Additionaly
the case where the domain might change during suspend/resume is
caught. In that case the devices qid needs to re-calculated in
order of having it found by the scan method.

Signed-off-by: Felix Beck <felix.beck@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index 090b32a..1294876 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -60,6 +60,7 @@
 static void ap_interrupt_handler(void *unused1, void *unused2);
 static void ap_reset(struct ap_device *ap_dev);
 static void ap_config_timeout(unsigned long ptr);
+static int ap_select_domain(void);
 
 /*
  * Module description.
@@ -109,6 +110,10 @@
 
 /* Suspend flag */
 static int ap_suspend_flag;
+/* Flag to check if domain was set through module parameter domain=. This is
+ * important when supsend and resume is done in a z/VM environment where the
+ * domain might change. */
+static int user_set_domain = 0;
 static struct bus_type ap_bus_type;
 
 /**
@@ -643,6 +648,7 @@
 			destroy_workqueue(ap_work_queue);
 			ap_work_queue = NULL;
 		}
+
 		tasklet_disable(&ap_tasklet);
 	}
 	/* Poll on the device until all requests are finished. */
@@ -653,7 +659,10 @@
 		spin_unlock_bh(&ap_dev->lock);
 	} while ((flags & 1) || (flags & 2));
 
-	ap_device_remove(dev);
+	spin_lock_bh(&ap_dev->lock);
+	ap_dev->unregistered = 1;
+	spin_unlock_bh(&ap_dev->lock);
+
 	return 0;
 }
 
@@ -666,11 +675,10 @@
 		ap_suspend_flag = 0;
 		if (!ap_interrupts_available())
 			ap_interrupt_indicator = NULL;
-		ap_device_probe(dev);
-		ap_reset(ap_dev);
-		setup_timer(&ap_dev->timeout, ap_request_timeout,
-			    (unsigned long) ap_dev);
-		ap_scan_bus(NULL);
+		if (!user_set_domain) {
+			ap_domain_index = -1;
+			ap_select_domain();
+		}
 		init_timer(&ap_config_timer);
 		ap_config_timer.function = ap_config_timeout;
 		ap_config_timer.data = 0;
@@ -686,12 +694,14 @@
 			tasklet_schedule(&ap_tasklet);
 		if (ap_thread_flag)
 			rc = ap_poll_thread_start();
-	} else {
-		ap_device_probe(dev);
-		ap_reset(ap_dev);
-		setup_timer(&ap_dev->timeout, ap_request_timeout,
-			    (unsigned long) ap_dev);
 	}
+	if (AP_QID_QUEUE(ap_dev->qid) != ap_domain_index) {
+		spin_lock_bh(&ap_dev->lock);
+		ap_dev->qid = AP_MKQID(AP_QID_DEVICE(ap_dev->qid),
+				       ap_domain_index);
+		spin_unlock_bh(&ap_dev->lock);
+	}
+	queue_work(ap_work_queue, &ap_config_work);
 
 	return rc;
 }
@@ -1079,6 +1089,8 @@
 			spin_lock_bh(&ap_dev->lock);
 			if (rc || ap_dev->unregistered) {
 				spin_unlock_bh(&ap_dev->lock);
+				if (ap_dev->unregistered)
+					i--;
 				device_unregister(dev);
 				put_device(dev);
 				continue;
@@ -1586,6 +1598,12 @@
 			   ap_domain_index);
 		return -EINVAL;
 	}
+	/* In resume callback we need to know if the user had set the domain.
+	 * If so, we can not just reset it.
+	 */
+	if (ap_domain_index >= 0)
+		user_set_domain = 1;
+
 	if (ap_instructions_available() != 0) {
 		pr_warning("The hardware system does not support "
 			   "AP instructions\n");