s390/zcryt: Handle AP configuration changes

Detect external AP bus configuration changes and request
an AP device rescan.

Signed-off-by: Holger Dengler <hd@linux.vnet.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 39e5eb2..cf5d9dc 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -950,6 +950,20 @@
 }
 EXPORT_SYMBOL(ap_driver_unregister);
 
+void ap_bus_force_rescan(void)
+{
+	/* Delete the AP bus rescan timer. */
+	del_timer(&ap_config_timer);
+
+	/* processing a synchonuous bus rescan */
+	ap_scan_bus(NULL);
+
+	/* Setup the AP bus rescan timer again. */
+	ap_config_timer.expires = jiffies + ap_config_time * HZ;
+	add_timer(&ap_config_timer);
+}
+EXPORT_SYMBOL(ap_bus_force_rescan);
+
 /*
  * AP bus attributes.
  */
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index e9b963e..685f6cc0 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -231,6 +231,7 @@
 void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg);
 void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg);
 void ap_flush_queue(struct ap_device *ap_dev);
+void ap_bus_force_rescan(void);
 
 int ap_module_init(void);
 void ap_module_exit(void);
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index f1f026e..31cfaa5 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -38,7 +38,10 @@
 #include <linux/atomic.h>
 #include <asm/uaccess.h>
 #include <linux/hw_random.h>
+#include <linux/debugfs.h>
+#include <asm/debug.h>
 
+#include "zcrypt_debug.h"
 #include "zcrypt_api.h"
 
 /*
@@ -53,6 +56,10 @@
 static LIST_HEAD(zcrypt_device_list);
 static int zcrypt_device_count = 0;
 static atomic_t zcrypt_open_count = ATOMIC_INIT(0);
+static atomic_t zcrypt_rescan_count = ATOMIC_INIT(0);
+
+atomic_t zcrypt_rescan_req = ATOMIC_INIT(0);
+EXPORT_SYMBOL(zcrypt_rescan_req);
 
 static int zcrypt_rng_device_add(void);
 static void zcrypt_rng_device_remove(void);
@@ -60,6 +67,10 @@
 static DEFINE_SPINLOCK(zcrypt_ops_list_lock);
 static LIST_HEAD(zcrypt_ops_list);
 
+static debug_info_t *zcrypt_dbf_common;
+static debug_info_t *zcrypt_dbf_devices;
+static struct dentry *debugfs_root;
+
 /*
  * Device attributes common for all crypto devices.
  */
@@ -89,6 +100,8 @@
 	if (sscanf(buf, "%d\n", &online) != 1 || online < 0 || online > 1)
 		return -EINVAL;
 	zdev->online = online;
+	ZCRYPT_DBF_DEV(DBF_INFO, zdev, "dev%04xo%dman", zdev->ap_dev->qid,
+		       zdev->online);
 	if (!online)
 		ap_flush_queue(zdev->ap_dev);
 	return count;
@@ -107,6 +120,24 @@
 };
 
 /**
+ * Process a rescan of the transport layer.
+ *
+ * Returns 1, if the rescan has been processed, otherwise 0.
+ */
+static inline int zcrypt_process_rescan(void)
+{
+	if (atomic_read(&zcrypt_rescan_req)) {
+		atomic_set(&zcrypt_rescan_req, 0);
+		atomic_inc(&zcrypt_rescan_count);
+		ap_bus_force_rescan();
+		ZCRYPT_DBF_COMMON(DBF_INFO, "rescan%07d",
+				  atomic_inc_return(&zcrypt_rescan_count));
+		return 1;
+	}
+	return 0;
+}
+
+/**
  * __zcrypt_increase_preference(): Increase preference of a crypto device.
  * @zdev: Pointer the crypto device
  *
@@ -194,6 +225,7 @@
 	zdev->reply.length = max_response_size;
 	spin_lock_init(&zdev->lock);
 	INIT_LIST_HEAD(&zdev->list);
+	zdev->dbf_area = zcrypt_dbf_devices;
 	return zdev;
 
 out_free:
@@ -229,6 +261,8 @@
 	kref_init(&zdev->refcount);
 	spin_lock_bh(&zcrypt_device_lock);
 	zdev->online = 1;	/* New devices are online by default. */
+	ZCRYPT_DBF_DEV(DBF_INFO, zdev, "dev%04xo%dreg", zdev->ap_dev->qid,
+		       zdev->online);
 	list_add_tail(&zdev->list, &zcrypt_device_list);
 	__zcrypt_increase_preference(zdev);
 	zcrypt_device_count++;
@@ -707,6 +741,11 @@
 		do {
 			rc = zcrypt_rsa_modexpo(&mex);
 		} while (rc == -EAGAIN);
+		/* on failure: retry once again after a requested rescan */
+		if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+			do {
+				rc = zcrypt_rsa_modexpo(&mex);
+			} while (rc == -EAGAIN);
 		if (rc)
 			return rc;
 		return put_user(mex.outputdatalength, &umex->outputdatalength);
@@ -719,6 +758,11 @@
 		do {
 			rc = zcrypt_rsa_crt(&crt);
 		} while (rc == -EAGAIN);
+		/* on failure: retry once again after a requested rescan */
+		if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+			do {
+				rc = zcrypt_rsa_crt(&crt);
+			} while (rc == -EAGAIN);
 		if (rc)
 			return rc;
 		return put_user(crt.outputdatalength, &ucrt->outputdatalength);
@@ -731,6 +775,11 @@
 		do {
 			rc = zcrypt_send_cprb(&xcRB);
 		} while (rc == -EAGAIN);
+		/* on failure: retry once again after a requested rescan */
+		if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+			do {
+				rc = zcrypt_send_cprb(&xcRB);
+			} while (rc == -EAGAIN);
 		if (copy_to_user(uxcRB, &xcRB, sizeof(xcRB)))
 			return -EFAULT;
 		return rc;
@@ -837,10 +886,15 @@
 	do {
 		rc = zcrypt_rsa_modexpo(&mex64);
 	} while (rc == -EAGAIN);
-	if (!rc)
-		rc = put_user(mex64.outputdatalength,
-			      &umex32->outputdatalength);
-	return rc;
+	/* on failure: retry once again after a requested rescan */
+	if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+		do {
+			rc = zcrypt_rsa_modexpo(&mex64);
+		} while (rc == -EAGAIN);
+	if (rc)
+		return rc;
+	return put_user(mex64.outputdatalength,
+			&umex32->outputdatalength);
 }
 
 struct compat_ica_rsa_modexpo_crt {
@@ -877,10 +931,15 @@
 	do {
 		rc = zcrypt_rsa_crt(&crt64);
 	} while (rc == -EAGAIN);
-	if (!rc)
-		rc = put_user(crt64.outputdatalength,
-			      &ucrt32->outputdatalength);
-	return rc;
+	/* on failure: retry once again after a requested rescan */
+	if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+		do {
+			rc = zcrypt_rsa_crt(&crt64);
+		} while (rc == -EAGAIN);
+	if (rc)
+		return rc;
+	return put_user(crt64.outputdatalength,
+			&ucrt32->outputdatalength);
 }
 
 struct compat_ica_xcRB {
@@ -936,6 +995,11 @@
 	do {
 		rc = zcrypt_send_cprb(&xcRB64);
 	} while (rc == -EAGAIN);
+	/* on failure: retry once again after a requested rescan */
+	if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+		do {
+			rc = zcrypt_send_cprb(&xcRB64);
+		} while (rc == -EAGAIN);
 	xcRB32.reply_control_blk_length = xcRB64.reply_control_blk_length;
 	xcRB32.reply_data_length = xcRB64.reply_data_length;
 	xcRB32.status = xcRB64.status;
@@ -1193,6 +1257,9 @@
 	 */
 	if (zcrypt_rng_buffer_index == 0) {
 		rc = zcrypt_rng((char *) zcrypt_rng_buffer);
+		/* on failure: retry once again after a requested rescan */
+		if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+			rc = zcrypt_rng((char *) zcrypt_rng_buffer);
 		if (rc < 0)
 			return -EIO;
 		zcrypt_rng_buffer_index = rc / sizeof *data;
@@ -1245,6 +1312,30 @@
 	mutex_unlock(&zcrypt_rng_mutex);
 }
 
+int __init zcrypt_debug_init(void)
+{
+	debugfs_root = debugfs_create_dir("zcrypt", NULL);
+
+	zcrypt_dbf_common = debug_register("zcrypt_common", 1, 1, 16);
+	debug_register_view(zcrypt_dbf_common, &debug_hex_ascii_view);
+	debug_set_level(zcrypt_dbf_common, DBF_ERR);
+
+	zcrypt_dbf_devices = debug_register("zcrypt_devices", 1, 1, 16);
+	debug_register_view(zcrypt_dbf_devices, &debug_hex_ascii_view);
+	debug_set_level(zcrypt_dbf_devices, DBF_ERR);
+
+	return 0;
+}
+
+void zcrypt_debug_exit(void)
+{
+	debugfs_remove(debugfs_root);
+	if (zcrypt_dbf_common)
+		debug_unregister(zcrypt_dbf_common);
+	if (zcrypt_dbf_devices)
+		debug_unregister(zcrypt_dbf_devices);
+}
+
 /**
  * zcrypt_api_init(): Module initialization.
  *
@@ -1254,6 +1345,12 @@
 {
 	int rc;
 
+	rc = zcrypt_debug_init();
+	if (rc)
+		goto out;
+
+	atomic_set(&zcrypt_rescan_req, 0);
+
 	/* Register the request sprayer. */
 	rc = misc_register(&zcrypt_misc_device);
 	if (rc < 0)
@@ -1283,6 +1380,7 @@
 {
 	remove_proc_entry("driver/z90crypt", NULL);
 	misc_deregister(&zcrypt_misc_device);
+	zcrypt_debug_exit();
 }
 
 module_init(zcrypt_api_init);
diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h
index 02b2d35..8963291 100644
--- a/drivers/s390/crypto/zcrypt_api.h
+++ b/drivers/s390/crypto/zcrypt_api.h
@@ -29,8 +29,10 @@
 #ifndef _ZCRYPT_API_H_
 #define _ZCRYPT_API_H_
 
-#include "ap_bus.h"
+#include <linux/atomic.h>
+#include <asm/debug.h>
 #include <asm/zcrypt.h>
+#include "ap_bus.h"
 
 /* deprecated status calls */
 #define ICAZ90STATUS		_IOR(ZCRYPT_IOCTL_MAGIC, 0x10, struct ica_z90_status)
@@ -112,8 +114,13 @@
 
 	struct ap_message reply;	/* Per-device reply structure. */
 	int max_exp_bit_length;
+
+	debug_info_t *dbf_area;		/* debugging */
 };
 
+/* transport layer rescanning */
+extern atomic_t zcrypt_rescan_req;
+
 struct zcrypt_device *zcrypt_device_alloc(size_t);
 void zcrypt_device_free(struct zcrypt_device *);
 void zcrypt_device_get(struct zcrypt_device *);
diff --git a/drivers/s390/crypto/zcrypt_debug.h b/drivers/s390/crypto/zcrypt_debug.h
new file mode 100644
index 0000000..841ea72
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_debug.h
@@ -0,0 +1,59 @@
+/*
+ *  Copyright IBM Corp. 2012
+ *  Author(s): Holger Dengler (hd@linux.vnet.ibm.com)
+ */
+#ifndef ZCRYPT_DEBUG_H
+#define ZCRYPT_DEBUG_H
+
+#include <asm/debug.h>
+#include "zcrypt_api.h"
+
+/* that gives us 15 characters in the text event views */
+#define ZCRYPT_DBF_LEN	16
+
+/* sort out low debug levels early to avoid wasted sprints */
+static inline int zcrypt_dbf_passes(debug_info_t *dbf_grp, int level)
+{
+	return (level <= dbf_grp->level);
+}
+
+#define DBF_ERR		3	/* error conditions	*/
+#define DBF_WARN	4	/* warning conditions	*/
+#define DBF_INFO	6	/* informational	*/
+
+#define RC2WARN(rc) ((rc) ? DBF_WARN : DBF_INFO)
+
+#define ZCRYPT_DBF_COMMON(level, text...) \
+	do { \
+		if (zcrypt_dbf_passes(zcrypt_dbf_common, level)) { \
+			char debug_buffer[ZCRYPT_DBF_LEN]; \
+			snprintf(debug_buffer, ZCRYPT_DBF_LEN, text); \
+			debug_text_event(zcrypt_dbf_common, level, \
+					 debug_buffer); \
+		} \
+	} while (0)
+
+#define ZCRYPT_DBF_DEVICES(level, text...) \
+	do { \
+		if (zcrypt_dbf_passes(zcrypt_dbf_devices, level)) { \
+			char debug_buffer[ZCRYPT_DBF_LEN]; \
+			snprintf(debug_buffer, ZCRYPT_DBF_LEN, text); \
+			debug_text_event(zcrypt_dbf_devices, level, \
+					 debug_buffer); \
+		} \
+	} while (0)
+
+#define ZCRYPT_DBF_DEV(level, device, text...) \
+	do { \
+		if (zcrypt_dbf_passes(device->dbf_area, level)) { \
+			char debug_buffer[ZCRYPT_DBF_LEN]; \
+			snprintf(debug_buffer, ZCRYPT_DBF_LEN, text); \
+			debug_text_event(device->dbf_area, level, \
+					 debug_buffer); \
+		} \
+	} while (0)
+
+int zcrypt_debug_init(void);
+void zcrypt_debug_exit(void);
+
+#endif /* ZCRYPT_DEBUG_H */
diff --git a/drivers/s390/crypto/zcrypt_error.h b/drivers/s390/crypto/zcrypt_error.h
index 0965e26..0079b66 100644
--- a/drivers/s390/crypto/zcrypt_error.h
+++ b/drivers/s390/crypto/zcrypt_error.h
@@ -26,6 +26,8 @@
 #ifndef _ZCRYPT_ERROR_H_
 #define _ZCRYPT_ERROR_H_
 
+#include <linux/atomic.h>
+#include "zcrypt_debug.h"
 #include "zcrypt_api.h"
 
 /**
@@ -108,16 +110,27 @@
 		 * and then repeat the request.
 		 */
 		WARN_ON(1);
+		atomic_set(&zcrypt_rescan_req, 1);
 		zdev->online = 0;
+		ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%drc%d",
+			       zdev->ap_dev->qid,
+			       zdev->online, ehdr->reply_code);
 		return -EAGAIN;
 	case REP82_ERROR_TRANSPORT_FAIL:
 	case REP82_ERROR_MACHINE_FAILURE:
 	//   REP88_ERROR_MODULE_FAILURE		// '10' CEX2A
 		/* If a card fails disable it and repeat the request. */
+		atomic_set(&zcrypt_rescan_req, 1);
 		zdev->online = 0;
+		ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%drc%d",
+			       zdev->ap_dev->qid,
+			       zdev->online, ehdr->reply_code);
 		return -EAGAIN;
 	default:
 		zdev->online = 0;
+		ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%drc%d",
+			       zdev->ap_dev->qid,
+			       zdev->online, ehdr->reply_code);
 		return -EAGAIN;	/* repeat the request on a different device. */
 	}
 }