Commit 038c5bed authored by Harald Freudenberger's avatar Harald Freudenberger Committed by Heiko Carstens

s390/ap: add ap status asynch error support

Review and extend the low level AP code to be able to
deal with asynchronous reported errors on APQNs.

The hypervisor and the SE guest may be confronted with
an asynchronously reported error at return of an AP
instruction. So all places where AP instructions are
called need review and may eventually need extensions.
However, not all places need rework. As together with
the AP status and the enabled asynch bit there is always
a response code set. The asynch error reporting comes
with new response codes which may be simple handled in
the default case of a switch statement.

The idea behind this patch is to report asynch errors
as -EPERM (read this as "Operation not permitted") which
reflects the fact that only a rapq (with F bit enabled)
is a valid AP instruction when an asynch error is flagged.

The AP queue state machine functions return
AP_SM_WAIT_NONE when a asynch error is detected to reflect
the fact, that the state machine can't do anything with
such an error as long as the queue is reset.

Unfortunately the ap bus scan function needed some
update as the ap_queue_info() now needs to return
3 states: 1 if an APQN exists and info is available,
-1 if it is assumed an APQN does not exist and the new
return value 0 without any info values filled. This 0
returncode is handled as "there is an APQN but we currently
don't know any more hw info about this, so please use
your previous info and try again later".
Signed-off-by: default avatarHarald Freudenberger <freude@linux.ibm.com>
Reviewed-by: default avatarHolger Dengler <dengler@linux.ibm.com>
Signed-off-by: default avatarHeiko Carstens <hca@linux.ibm.com>
parent 2d72eaf0
...@@ -342,11 +342,14 @@ EXPORT_SYMBOL(ap_test_config_ctrl_domain); ...@@ -342,11 +342,14 @@ EXPORT_SYMBOL(ap_test_config_ctrl_domain);
/* /*
* ap_queue_info(): Check and get AP queue info. * ap_queue_info(): Check and get AP queue info.
* Returns true if TAPQ succeeded and the info is filled or * Returns: 1 if APQN exists and info is filled,
* false otherwise. * 0 if APQN seems to exit but there is no info
* available (eg. caused by an asynch pending error)
* -1 invalid APQN, TAPQ error or AP queue status which
* indicates there is no APQN.
*/ */
static bool ap_queue_info(ap_qid_t qid, int *q_type, unsigned int *q_fac, static int ap_queue_info(ap_qid_t qid, int *q_type, unsigned int *q_fac,
int *q_depth, int *q_ml, bool *q_decfg, bool *q_cstop) int *q_depth, int *q_ml, bool *q_decfg, bool *q_cstop)
{ {
struct ap_queue_status status; struct ap_queue_status status;
struct ap_tapq_gr2 tapq_info; struct ap_tapq_gr2 tapq_info;
...@@ -356,10 +359,15 @@ static bool ap_queue_info(ap_qid_t qid, int *q_type, unsigned int *q_fac, ...@@ -356,10 +359,15 @@ static bool ap_queue_info(ap_qid_t qid, int *q_type, unsigned int *q_fac,
/* make sure we don't run into a specifiation exception */ /* make sure we don't run into a specifiation exception */
if (AP_QID_CARD(qid) > ap_max_adapter_id || if (AP_QID_CARD(qid) > ap_max_adapter_id ||
AP_QID_QUEUE(qid) > ap_max_domain_id) AP_QID_QUEUE(qid) > ap_max_domain_id)
return false; return -1;
/* call TAPQ on this APQN */ /* call TAPQ on this APQN */
status = ap_test_queue(qid, ap_apft_available(), &tapq_info); status = ap_test_queue(qid, ap_apft_available(), &tapq_info);
/* handle pending async error with return 'no info available' */
if (status.async)
return 0;
switch (status.response_code) { switch (status.response_code) {
case AP_RESPONSE_NORMAL: case AP_RESPONSE_NORMAL:
case AP_RESPONSE_RESET_IN_PROGRESS: case AP_RESPONSE_RESET_IN_PROGRESS:
...@@ -372,7 +380,7 @@ static bool ap_queue_info(ap_qid_t qid, int *q_type, unsigned int *q_fac, ...@@ -372,7 +380,7 @@ static bool ap_queue_info(ap_qid_t qid, int *q_type, unsigned int *q_fac,
* there is at least one of the mode bits set. * there is at least one of the mode bits set.
*/ */
if (WARN_ON_ONCE(!tapq_info.value)) if (WARN_ON_ONCE(!tapq_info.value))
return false; return 0;
*q_type = tapq_info.at; *q_type = tapq_info.at;
*q_fac = tapq_info.fac; *q_fac = tapq_info.fac;
*q_depth = tapq_info.qd; *q_depth = tapq_info.qd;
...@@ -396,12 +404,12 @@ static bool ap_queue_info(ap_qid_t qid, int *q_type, unsigned int *q_fac, ...@@ -396,12 +404,12 @@ static bool ap_queue_info(ap_qid_t qid, int *q_type, unsigned int *q_fac,
default: default:
break; break;
} }
return true; return 1;
default: default:
/* /*
* A response code which indicates, there is no info available. * A response code which indicates, there is no info available.
*/ */
return false; return -1;
} }
} }
...@@ -1798,12 +1806,12 @@ static inline void ap_scan_rm_card_dev_and_queue_devs(struct ap_card *ac) ...@@ -1798,12 +1806,12 @@ static inline void ap_scan_rm_card_dev_and_queue_devs(struct ap_card *ac)
*/ */
static inline void ap_scan_domains(struct ap_card *ac) static inline void ap_scan_domains(struct ap_card *ac)
{ {
int rc, dom, depth, type, ml;
bool decfg, chkstop; bool decfg, chkstop;
ap_qid_t qid;
unsigned int func;
struct device *dev;
struct ap_queue *aq; struct ap_queue *aq;
int rc, dom, depth, type, ml; struct device *dev;
unsigned int func;
ap_qid_t qid;
/* /*
* Go through the configuration for the domains and compare them * Go through the configuration for the domains and compare them
...@@ -1822,20 +1830,24 @@ static inline void ap_scan_domains(struct ap_card *ac) ...@@ -1822,20 +1830,24 @@ static inline void ap_scan_domains(struct ap_card *ac)
AP_DBF_INFO("%s(%d,%d) not in config anymore, rm queue dev\n", AP_DBF_INFO("%s(%d,%d) not in config anymore, rm queue dev\n",
__func__, ac->id, dom); __func__, ac->id, dom);
device_unregister(dev); device_unregister(dev);
put_device(dev);
} }
continue; goto put_dev_and_continue;
} }
/* domain is valid, get info from this APQN */ /* domain is valid, get info from this APQN */
if (!ap_queue_info(qid, &type, &func, &depth, rc = ap_queue_info(qid, &type, &func, &depth,
&ml, &decfg, &chkstop)) { &ml, &decfg, &chkstop);
if (aq) { switch (rc) {
case -1:
if (dev) {
AP_DBF_INFO("%s(%d,%d) queue_info() failed, rm queue dev\n", AP_DBF_INFO("%s(%d,%d) queue_info() failed, rm queue dev\n",
__func__, ac->id, dom); __func__, ac->id, dom);
device_unregister(dev); device_unregister(dev);
put_device(dev);
} }
continue; fallthrough;
case 0:
goto put_dev_and_continue;
default:
break;
} }
/* if no queue device exists, create a new one */ /* if no queue device exists, create a new one */
if (!aq) { if (!aq) {
...@@ -1951,12 +1963,12 @@ static inline void ap_scan_domains(struct ap_card *ac) ...@@ -1951,12 +1963,12 @@ static inline void ap_scan_domains(struct ap_card *ac)
*/ */
static inline void ap_scan_adapter(int ap) static inline void ap_scan_adapter(int ap)
{ {
int rc, dom, depth, type, comp_type, ml;
bool decfg, chkstop; bool decfg, chkstop;
ap_qid_t qid;
unsigned int func;
struct device *dev;
struct ap_card *ac; struct ap_card *ac;
int rc, dom, depth, type, comp_type, ml; struct device *dev;
unsigned int func;
ap_qid_t qid;
/* Is there currently a card device for this adapter ? */ /* Is there currently a card device for this adapter ? */
dev = bus_find_device(&ap_bus_type, NULL, dev = bus_find_device(&ap_bus_type, NULL,
...@@ -1986,11 +1998,11 @@ static inline void ap_scan_adapter(int ap) ...@@ -1986,11 +1998,11 @@ static inline void ap_scan_adapter(int ap)
if (ap_test_config_usage_domain(dom)) { if (ap_test_config_usage_domain(dom)) {
qid = AP_MKQID(ap, dom); qid = AP_MKQID(ap, dom);
if (ap_queue_info(qid, &type, &func, &depth, if (ap_queue_info(qid, &type, &func, &depth,
&ml, &decfg, &chkstop)) &ml, &decfg, &chkstop) > 0)
break; break;
} }
if (dom > ap_max_domain_id) { if (dom > ap_max_domain_id) {
/* Could not find a valid APQN for this adapter */ /* Could not find one valid APQN for this adapter */
if (ac) { if (ac) {
AP_DBF_INFO("%s(%d) no type info (no APQN found), rm card and queue devs\n", AP_DBF_INFO("%s(%d) no type info (no APQN found), rm card and queue devs\n",
__func__, ap); __func__, ap);
......
...@@ -50,6 +50,8 @@ static int ap_queue_enable_irq(struct ap_queue *aq, void *ind) ...@@ -50,6 +50,8 @@ static int ap_queue_enable_irq(struct ap_queue *aq, void *ind)
qirqctrl.ir = 1; qirqctrl.ir = 1;
qirqctrl.isc = AP_ISC; qirqctrl.isc = AP_ISC;
status = ap_aqic(aq->qid, qirqctrl, virt_to_phys(ind)); status = ap_aqic(aq->qid, qirqctrl, virt_to_phys(ind));
if (status.async)
return -EPERM;
switch (status.response_code) { switch (status.response_code) {
case AP_RESPONSE_NORMAL: case AP_RESPONSE_NORMAL:
case AP_RESPONSE_OTHERWISE_CHANGED: case AP_RESPONSE_OTHERWISE_CHANGED:
...@@ -96,6 +98,8 @@ int ap_send(ap_qid_t qid, unsigned long psmid, void *msg, size_t msglen) ...@@ -96,6 +98,8 @@ int ap_send(ap_qid_t qid, unsigned long psmid, void *msg, size_t msglen)
struct ap_queue_status status; struct ap_queue_status status;
status = __ap_send(qid, psmid, msg, msglen, 0); status = __ap_send(qid, psmid, msg, msglen, 0);
if (status.async)
return -EPERM;
switch (status.response_code) { switch (status.response_code) {
case AP_RESPONSE_NORMAL: case AP_RESPONSE_NORMAL:
return 0; return 0;
...@@ -117,6 +121,8 @@ int ap_recv(ap_qid_t qid, unsigned long *psmid, void *msg, size_t msglen) ...@@ -117,6 +121,8 @@ int ap_recv(ap_qid_t qid, unsigned long *psmid, void *msg, size_t msglen)
if (!msg) if (!msg)
return -EINVAL; return -EINVAL;
status = ap_dqap(qid, psmid, msg, msglen, NULL, NULL, NULL); status = ap_dqap(qid, psmid, msg, msglen, NULL, NULL, NULL);
if (status.async)
return -EPERM;
switch (status.response_code) { switch (status.response_code) {
case AP_RESPONSE_NORMAL: case AP_RESPONSE_NORMAL:
return 0; return 0;
...@@ -225,6 +231,8 @@ static enum ap_sm_wait ap_sm_read(struct ap_queue *aq) ...@@ -225,6 +231,8 @@ static enum ap_sm_wait ap_sm_read(struct ap_queue *aq)
if (!aq->reply) if (!aq->reply)
return AP_SM_WAIT_NONE; return AP_SM_WAIT_NONE;
status = ap_sm_recv(aq); status = ap_sm_recv(aq);
if (status.async)
return AP_SM_WAIT_NONE;
switch (status.response_code) { switch (status.response_code) {
case AP_RESPONSE_NORMAL: case AP_RESPONSE_NORMAL:
if (aq->queue_count > 0) { if (aq->queue_count > 0) {
...@@ -276,6 +284,8 @@ static enum ap_sm_wait ap_sm_write(struct ap_queue *aq) ...@@ -276,6 +284,8 @@ static enum ap_sm_wait ap_sm_write(struct ap_queue *aq)
status = __ap_send(qid, ap_msg->psmid, status = __ap_send(qid, ap_msg->psmid,
ap_msg->msg, ap_msg->len, ap_msg->msg, ap_msg->len,
ap_msg->flags & AP_MSG_FLAG_SPECIAL); ap_msg->flags & AP_MSG_FLAG_SPECIAL);
if (status.async)
return AP_SM_WAIT_NONE;
switch (status.response_code) { switch (status.response_code) {
case AP_RESPONSE_NORMAL: case AP_RESPONSE_NORMAL:
aq->queue_count = max_t(int, 1, aq->queue_count + 1); aq->queue_count = max_t(int, 1, aq->queue_count + 1);
...@@ -338,6 +348,8 @@ static enum ap_sm_wait ap_sm_reset(struct ap_queue *aq) ...@@ -338,6 +348,8 @@ static enum ap_sm_wait ap_sm_reset(struct ap_queue *aq)
struct ap_queue_status status; struct ap_queue_status status;
status = ap_rapq(aq->qid, aq->rapq_fbit); status = ap_rapq(aq->qid, aq->rapq_fbit);
if (status.async)
return AP_SM_WAIT_NONE;
switch (status.response_code) { switch (status.response_code) {
case AP_RESPONSE_NORMAL: case AP_RESPONSE_NORMAL:
case AP_RESPONSE_RESET_IN_PROGRESS: case AP_RESPONSE_RESET_IN_PROGRESS:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment