s390/ap: handle outband SE bind state change

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>
This commit is contained in:
Harald Freudenberger 2023-11-09 11:24:20 +01:00 committed by Alexander Gordeev
parent d4c53ae8e4
commit 207022d39d
3 changed files with 122 additions and 50 deletions

View 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 */

View 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. */

View 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: