s390/ap: handle outband SE bind state change
authorHarald Freudenberger <freude@linux.ibm.com>
Thu, 9 Nov 2023 10:24:20 +0000 (11:24 +0100)
committerAlexander Gordeev <agordeev@linux.ibm.com>
Thu, 30 Nov 2023 15:24:23 +0000 (16:24 +0100)
This patch addresses some weird scenarios where an outband
manipulation of the SE bind state of a queue assigned and
maybe in use by an SE guest with AP pass-through support
took place. So for example when the guest has bound and
associated a queue and then this domain has been zeroed on
the service element.

Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
drivers/s390/crypto/ap_bus.c
drivers/s390/crypto/ap_bus.h
drivers/s390/crypto/ap_queue.c

index 20396eccb33bca97052162e1e28c1146bb314adb..f46dd6abacd709b2395d15d25151eb266ecacbf1 100644 (file)
@@ -1847,6 +1847,7 @@ static inline void ap_scan_domains(struct ap_card *ac)
                        aq->card = ac;
                        aq->config = !decfg;
                        aq->chkstop = chkstop;
+                       aq->se_bstate = hwinfo.bs;
                        dev = &aq->ap_dev.device;
                        dev->bus = &ap_bus_type;
                        dev->parent = &ac->ap_dev.device;
@@ -1876,6 +1877,8 @@ static inline void ap_scan_domains(struct ap_card *ac)
                }
                /* handle state changes on already existing queue device */
                spin_lock_bh(&aq->lock);
+               /* SE bind state */
+               aq->se_bstate = hwinfo.bs;
                /* checkstop state */
                if (chkstop && !aq->chkstop) {
                        /* checkstop on */
index cc256424f70b1363c160753edda25fd9deea024c..98814839ef301ed796f0700ea11acbcbd7262742 100644 (file)
@@ -194,7 +194,7 @@ struct ap_queue {
        bool config;                    /* configured state */
        bool chkstop;                   /* checkstop state */
        ap_qid_t qid;                   /* AP queue id. */
-       bool se_bound;                  /* SE bound state */
+       unsigned int se_bstate;         /* SE bind state (BS) */
        unsigned int assoc_idx;         /* SE association index */
        int queue_count;                /* # messages currently on AP queue. */
        int pendingq_count;             /* # requests on pendingq list. */
index 39f250f1f1c32f2b73e9a7c8f820a067f24876c1..682595443145a390c57c082ef08b26aa5b2bcde8 100644 (file)
@@ -317,7 +317,6 @@ static enum ap_sm_wait ap_sm_reset(struct ap_queue *aq)
        case AP_RESPONSE_RESET_IN_PROGRESS:
                aq->sm_state = AP_SM_STATE_RESET_WAIT;
                aq->rapq_fbit = 0;
-               aq->se_bound = false;
                return AP_SM_WAIT_LOW_TIMEOUT;
        default:
                aq->dev_state = AP_DEV_STATE_ERROR;
@@ -338,17 +337,15 @@ static enum ap_sm_wait ap_sm_reset(struct ap_queue *aq)
 static enum ap_sm_wait ap_sm_reset_wait(struct ap_queue *aq)
 {
        struct ap_queue_status status;
+       struct ap_tapq_hwinfo hwinfo;
        void *lsi_ptr;
 
-       if (aq->queue_count > 0 && aq->reply)
-               /* Try to read a completed message and get the status */
-               status = ap_sm_recv(aq);
-       else
-               /* Get the status with TAPQ */
-               status = ap_tapq(aq->qid, NULL);
+       /* Get the status with TAPQ */
+       status = ap_test_queue(aq->qid, 1, &hwinfo);
 
        switch (status.response_code) {
        case AP_RESPONSE_NORMAL:
+               aq->se_bstate = hwinfo.bs;
                lsi_ptr = ap_airq_ptr();
                if (lsi_ptr && ap_queue_enable_irq(aq, lsi_ptr) == 0)
                        aq->sm_state = AP_SM_STATE_SETIRQ_WAIT;
@@ -441,6 +438,9 @@ static enum ap_sm_wait ap_sm_assoc_wait(struct ap_queue *aq)
                return AP_SM_WAIT_NONE;
        }
 
+       /* update queue's SE bind state */
+       aq->se_bstate = hwinfo.bs;
+
        /* check bs bits */
        switch (hwinfo.bs) {
        case AP_BS_Q_USABLE:
@@ -851,6 +851,12 @@ static ssize_t se_bind_show(struct device *dev,
                           AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
                return -EIO;
        }
+
+       /* update queue's SE bind state */
+       spin_lock_bh(&aq->lock);
+       aq->se_bstate = hwinfo.bs;
+       spin_unlock_bh(&aq->lock);
+
        switch (hwinfo.bs) {
        case AP_BS_Q_USABLE:
        case AP_BS_Q_USABLE_NO_SECURE_KEY:
@@ -866,6 +872,7 @@ static ssize_t se_bind_store(struct device *dev,
 {
        struct ap_queue *aq = to_ap_queue(dev);
        struct ap_queue_status status;
+       struct ap_tapq_hwinfo hwinfo;
        bool value;
        int rc;
 
@@ -877,39 +884,80 @@ static ssize_t se_bind_store(struct device *dev,
        if (rc)
                return rc;
 
-       if (value) {
-               /* bind, do BAPQ */
-               spin_lock_bh(&aq->lock);
-               if (aq->sm_state < AP_SM_STATE_IDLE) {
-                       spin_unlock_bh(&aq->lock);
-                       return -EBUSY;
-               }
-               status = ap_bapq(aq->qid);
-               spin_unlock_bh(&aq->lock);
-               if (!status.response_code) {
-                       aq->se_bound = true;
-                       AP_DBF_INFO("%s bapq(0x%02x.%04x) success\n", __func__,
-                                   AP_QID_CARD(aq->qid),
-                                   AP_QID_QUEUE(aq->qid));
-               } else {
-                       AP_DBF_WARN("%s RC 0x%02x on bapq(0x%02x.%04x)\n",
-                                   __func__, status.response_code,
-                                   AP_QID_CARD(aq->qid),
-                                   AP_QID_QUEUE(aq->qid));
-                       return -EIO;
-               }
-       } else {
-               /* unbind, set F bit arg and trigger RAPQ */
+       if (!value) {
+               /* Unbind. Set F bit arg and trigger RAPQ */
                spin_lock_bh(&aq->lock);
                __ap_flush_queue(aq);
                aq->rapq_fbit = 1;
-               aq->assoc_idx = ASSOC_IDX_INVALID;
-               aq->sm_state = AP_SM_STATE_RESET_START;
-               ap_wait(ap_sm_event(aq, AP_SM_EVENT_POLL));
-               spin_unlock_bh(&aq->lock);
+               _ap_queue_init_state(aq);
+               rc = count;
+               goto out;
        }
 
-       return count;
+       /* Bind. Check current SE bind state */
+       status = ap_test_queue(aq->qid, 1, &hwinfo);
+       if (status.response_code) {
+               AP_DBF_WARN("%s RC 0x%02x on tapq(0x%02x.%04x)\n",
+                           __func__, status.response_code,
+                           AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+               return -EIO;
+       }
+
+       /* Update BS state */
+       spin_lock_bh(&aq->lock);
+       aq->se_bstate = hwinfo.bs;
+       if (hwinfo.bs != AP_BS_Q_AVAIL_FOR_BINDING) {
+               AP_DBF_WARN("%s bind attempt with bs %d on queue 0x%02x.%04x\n",
+                           __func__, hwinfo.bs,
+                           AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+               rc = -EINVAL;
+               goto out;
+       }
+
+       /* Check SM state */
+       if (aq->sm_state < AP_SM_STATE_IDLE) {
+               rc = -EBUSY;
+               goto out;
+       }
+
+       /* invoke BAPQ */
+       status = ap_bapq(aq->qid);
+       if (status.response_code) {
+               AP_DBF_WARN("%s RC 0x%02x on bapq(0x%02x.%04x)\n",
+                           __func__, status.response_code,
+                           AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+               rc = -EIO;
+               goto out;
+       }
+       aq->assoc_idx = ASSOC_IDX_INVALID;
+
+       /* verify SE bind state */
+       status = ap_test_queue(aq->qid, 1, &hwinfo);
+       if (status.response_code) {
+               AP_DBF_WARN("%s RC 0x%02x on tapq(0x%02x.%04x)\n",
+                           __func__, status.response_code,
+                           AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+               rc = -EIO;
+               goto out;
+       }
+       aq->se_bstate = hwinfo.bs;
+       if (!(hwinfo.bs == AP_BS_Q_USABLE ||
+             hwinfo.bs == AP_BS_Q_USABLE_NO_SECURE_KEY)) {
+               AP_DBF_WARN("%s BAPQ success, but bs shows %d on queue 0x%02x.%04x\n",
+                           __func__, hwinfo.bs,
+                           AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+               rc = -EIO;
+               goto out;
+       }
+
+       /* SE bind was successful */
+       AP_DBF_INFO("%s bapq(0x%02x.%04x) success\n", __func__,
+                   AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+       rc = count;
+
+out:
+       spin_unlock_bh(&aq->lock);
+       return rc;
 }
 
 static DEVICE_ATTR_RW(se_bind);
@@ -932,6 +980,11 @@ static ssize_t se_associate_show(struct device *dev,
                return -EIO;
        }
 
+       /* update queue's SE bind state */
+       spin_lock_bh(&aq->lock);
+       aq->se_bstate = hwinfo.bs;
+       spin_unlock_bh(&aq->lock);
+
        switch (hwinfo.bs) {
        case AP_BS_Q_USABLE:
                if (aq->assoc_idx == ASSOC_IDX_INVALID) {
@@ -954,6 +1007,7 @@ static ssize_t se_associate_store(struct device *dev,
 {
        struct ap_queue *aq = to_ap_queue(dev);
        struct ap_queue_status status;
+       struct ap_tapq_hwinfo hwinfo;
        unsigned int value;
        int rc;
 
@@ -967,18 +1021,28 @@ static ssize_t se_associate_store(struct device *dev,
        if (value >= ASSOC_IDX_INVALID)
                return -EINVAL;
 
+       /* check current SE bind state */
+       status = ap_test_queue(aq->qid, 1, &hwinfo);
+       if (status.response_code) {
+               AP_DBF_WARN("%s RC 0x%02x on tapq(0x%02x.%04x)\n",
+                           __func__, status.response_code,
+                           AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+               return -EIO;
+       }
        spin_lock_bh(&aq->lock);
-
-       /* sm should be in idle state */
-       if (aq->sm_state != AP_SM_STATE_IDLE) {
-               spin_unlock_bh(&aq->lock);
-               return -EBUSY;
+       aq->se_bstate = hwinfo.bs;
+       if (hwinfo.bs != AP_BS_Q_USABLE_NO_SECURE_KEY) {
+               AP_DBF_WARN("%s association attempt with bs %d on queue 0x%02x.%04x\n",
+                           __func__, hwinfo.bs,
+                           AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
+               rc = -EINVAL;
+               goto out;
        }
 
-       /* already associated or association pending ? */
-       if (aq->assoc_idx != ASSOC_IDX_INVALID) {
-               spin_unlock_bh(&aq->lock);
-               return -EINVAL;
+       /* check SM state */
+       if (aq->sm_state != AP_SM_STATE_IDLE) {
+               rc = -EBUSY;
+               goto out;
        }
 
        /* trigger the asynchronous association request */
@@ -989,17 +1053,20 @@ static ssize_t se_associate_store(struct device *dev,
                aq->sm_state = AP_SM_STATE_ASSOC_WAIT;
                aq->assoc_idx = value;
                ap_wait(ap_sm_event(aq, AP_SM_EVENT_POLL));
-               spin_unlock_bh(&aq->lock);
                break;
        default:
-               spin_unlock_bh(&aq->lock);
                AP_DBF_WARN("%s RC 0x%02x on aapq(0x%02x.%04x)\n",
                            __func__, status.response_code,
                            AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));
-               return -EIO;
+               rc = -EIO;
+               goto out;
        }
 
-       return count;
+       rc = count;
+
+out:
+       spin_unlock_bh(&aq->lock);
+       return rc;
 }
 
 static DEVICE_ATTR_RW(se_associate);
@@ -1122,7 +1189,9 @@ bool ap_queue_usable(struct ap_queue *aq)
        }
 
        /* SE guest's queues additionally need to be bound */
-       if (ap_q_needs_bind(aq) && !aq->se_bound)
+       if (ap_q_needs_bind(aq) &&
+           !(aq->se_bstate == AP_BS_Q_USABLE ||
+             aq->se_bstate == AP_BS_Q_USABLE_NO_SECURE_KEY))
                rc = false;
 
 unlock_and_out: