s390/zcrypt: Fix wrong dispatching for control domain CPRBs
authorHarald Freudenberger <freude@linux.ibm.com>
Tue, 21 May 2019 11:50:09 +0000 (13:50 +0200)
committerHeiko Carstens <heiko.carstens@de.ibm.com>
Tue, 28 May 2019 12:49:38 +0000 (14:49 +0200)
The zcrypt device driver does not handle CPRBs which address
a control domain correctly. This fix introduces a workaround:
The domain field of the request CPRB is checked if there is
a valid domain value in there. If this is true and the value
is a control only domain (a domain which is enabled in the
crypto config ADM mask but disabled in the AQM mask) the
CPRB is forwarded to the default usage domain. If there is
no default domain, the request is rejected with an ENODEV.

This fix is important for maintaining crypto adapters. For
example one LPAR can use a crypto adapter domain ('Control
and Usage') but another LPAR needs to be able to maintain
this adapter domain ('Control'). Scenarios like this did
not work properly and the patch enables this.

Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
arch/s390/include/asm/ap.h
drivers/s390/crypto/ap_bus.c
drivers/s390/crypto/ap_bus.h
drivers/s390/crypto/zcrypt_api.c

index e94a0a28b5ebe22b944ea73b1ac48bdcf52d9e63..aea32dda3d1433891f2731a26aea81c3c0acc3c5 100644 (file)
@@ -160,8 +160,8 @@ struct ap_config_info {
        unsigned char Nd;               /* max # of Domains - 1 */
        unsigned char _reserved3[10];
        unsigned int apm[8];            /* AP ID mask */
-       unsigned int aqm[8];            /* AP queue mask */
-       unsigned int adm[8];            /* AP domain mask */
+       unsigned int aqm[8];            /* AP (usage) queue mask */
+       unsigned int adm[8];            /* AP (control) domain mask */
        unsigned char _reserved4[16];
 } __aligned(8);
 
index cc30e4f07fffabe0da6968dfa379c9ef9de843a3..b9fc502c58c24a146c9e5c5109312a2d40c7d268 100644 (file)
@@ -254,19 +254,37 @@ static inline int ap_test_config_card_id(unsigned int id)
 }
 
 /*
- * ap_test_config_domain(): Test, whether an AP usage domain is configured.
+ * ap_test_config_usage_domain(): Test, whether an AP usage domain
+ * is configured.
  * @domain AP usage domain ID
  *
  * Returns 0 if the usage domain is not configured
  *        1 if the usage domain is configured or
  *          if the configuration information is not available
  */
-static inline int ap_test_config_domain(unsigned int domain)
+int ap_test_config_usage_domain(unsigned int domain)
 {
        if (!ap_configuration)  /* QCI not supported */
                return domain < 16;
        return ap_test_config(ap_configuration->aqm, domain);
 }
+EXPORT_SYMBOL(ap_test_config_usage_domain);
+
+/*
+ * ap_test_config_ctrl_domain(): Test, whether an AP control domain
+ * is configured.
+ * @domain AP control domain ID
+ *
+ * Returns 1 if the control domain is configured
+ *        0 in all other cases
+ */
+int ap_test_config_ctrl_domain(unsigned int domain)
+{
+       if (!ap_configuration)  /* QCI not supported */
+               return 0;
+       return ap_test_config(ap_configuration->adm, domain);
+}
+EXPORT_SYMBOL(ap_test_config_ctrl_domain);
 
 /**
  * ap_query_queue(): Check if an AP queue is available.
@@ -1267,7 +1285,7 @@ static void ap_select_domain(void)
        best_domain = -1;
        max_count = 0;
        for (i = 0; i < AP_DOMAINS; i++) {
-               if (!ap_test_config_domain(i) ||
+               if (!ap_test_config_usage_domain(i) ||
                    !test_bit_inv(i, ap_perms.aqm))
                        continue;
                count = 0;
@@ -1442,7 +1460,7 @@ static void _ap_scan_bus_adapter(int id)
                                      (void *)(long) qid,
                                      __match_queue_device_with_qid);
                aq = dev ? to_ap_queue(dev) : NULL;
-               if (!ap_test_config_domain(dom)) {
+               if (!ap_test_config_usage_domain(dom)) {
                        if (dev) {
                                /* Queue device exists but has been
                                 * removed from configuration.
index 15a98a673c5cc3323980f15e95d3418b1c65e028..6f3cf37776cae5b038f647b9e056c94412bf71c6 100644 (file)
@@ -251,6 +251,9 @@ void ap_wait(enum ap_wait wait);
 void ap_request_timeout(struct timer_list *t);
 void ap_bus_force_rescan(void);
 
+int ap_test_config_usage_domain(unsigned int domain);
+int ap_test_config_ctrl_domain(unsigned int domain);
+
 void ap_queue_init_reply(struct ap_queue *aq, struct ap_message *ap_msg);
 struct ap_queue *ap_queue_create(ap_qid_t qid, int device_type);
 void ap_queue_prepare_remove(struct ap_queue *aq);
index 852b8c2299c1cd10cab61f9e429a4650b337c5c5..1058b4b5cc1ec5d1b8ccb4c6e70f803e163507a3 100644 (file)
@@ -822,7 +822,7 @@ static long _zcrypt_send_cprb(struct ap_perms *perms,
        struct ap_message ap_msg;
        unsigned int weight, pref_weight;
        unsigned int func_code;
-       unsigned short *domain;
+       unsigned short *domain, tdom;
        int qid = 0, rc = -ENODEV;
        struct module *mod;
 
@@ -834,6 +834,17 @@ static long _zcrypt_send_cprb(struct ap_perms *perms,
        if (rc)
                goto out;
 
+       /*
+        * If a valid target domain is set and this domain is NOT a usage
+        * domain but a control only domain, use the default domain as target.
+        */
+       tdom = *domain;
+       if (tdom >= 0 && tdom < AP_DOMAINS &&
+           !ap_test_config_usage_domain(tdom) &&
+           ap_test_config_ctrl_domain(tdom) &&
+           ap_domain_index >= 0)
+               tdom = ap_domain_index;
+
        pref_zc = NULL;
        pref_zq = NULL;
        spin_lock(&zcrypt_list_lock);
@@ -856,8 +867,8 @@ static long _zcrypt_send_cprb(struct ap_perms *perms,
                        /* check if device is online and eligible */
                        if (!zq->online ||
                            !zq->ops->send_cprb ||
-                           ((*domain != (unsigned short) AUTOSELECT) &&
-                            (*domain != AP_QID_QUEUE(zq->queue->qid))))
+                           (tdom != (unsigned short) AUTOSELECT &&
+                            tdom != AP_QID_QUEUE(zq->queue->qid)))
                                continue;
                        /* check if device node has admission for this queue */
                        if (!zcrypt_check_queue(perms,