s390/zcryt: Handle AP configuration changes
authorHolger Dengler <hd@linux.vnet.ibm.com>
Mon, 10 Sep 2012 19:34:26 +0000 (21:34 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 26 Sep 2012 13:45:17 +0000 (15:45 +0200)
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>
drivers/s390/crypto/ap_bus.c
drivers/s390/crypto/ap_bus.h
drivers/s390/crypto/zcrypt_api.c
drivers/s390/crypto/zcrypt_api.h
drivers/s390/crypto/zcrypt_debug.h [new file with mode: 0644]
drivers/s390/crypto/zcrypt_error.h

index 39e5eb2..cf5d9dc 100644 (file)
@@ -950,6 +950,20 @@ void ap_driver_unregister(struct ap_driver *ap_drv)
 }
 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.
  */
index e9b963e..685f6cc 100644 (file)
@@ -231,6 +231,7 @@ int ap_recv(ap_qid_t, unsigned long long *, void *, size_t);
 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);
index f1f026e..31cfaa5 100644 (file)
 #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 DEFINE_SPINLOCK(zcrypt_device_lock);
 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 void zcrypt_rng_device_remove(void);
 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 @@ static ssize_t zcrypt_online_store(struct device *dev,
        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;
@@ -106,6 +119,24 @@ static struct attribute_group zcrypt_device_attr_group = {
        .attrs = zcrypt_device_attrs,
 };
 
+/**
+ * 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 @@ struct zcrypt_device *zcrypt_device_alloc(size_t max_response_size)
        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 @@ int zcrypt_device_register(struct zcrypt_device *zdev)
        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 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,
                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 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,
                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 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,
                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 @@ static long trans_modexpo32(struct file *filp, unsigned int cmd,
        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 @@ static long trans_modexpo_crt32(struct file *filp, unsigned int cmd,
        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 @@ static long trans_xcRB32(struct file *filp, unsigned int cmd,
        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 @@ static int zcrypt_rng_data_read(struct hwrng *rng, u32 *data)
         */
        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 @@ static void zcrypt_rng_device_remove(void)
        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 __init zcrypt_api_init(void)
 {
        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 @@ void zcrypt_api_exit(void)
 {
        remove_proc_entry("driver/z90crypt", NULL);
        misc_deregister(&zcrypt_misc_device);
+       zcrypt_debug_exit();
 }
 
 module_init(zcrypt_api_init);
index 02b2d35..8963291 100644 (file)
 #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 zcrypt_device {
 
        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 (file)
index 0000000..841ea72
--- /dev/null
@@ -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 */
index 0965e26..0079b66 100644 (file)
@@ -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 @@ static inline int convert_error(struct zcrypt_device *zdev,
                 * 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. */
        }
 }