diff options
author | Harald Freudenberger <freude@linux.ibm.com> | 2021-06-30 16:10:56 +0200 |
---|---|---|
committer | Vasily Gorbik <gor@linux.ibm.com> | 2021-07-08 15:37:27 +0200 |
commit | 1f0d22defd59f603d63ba51483eeb8d72726ce8b (patch) | |
tree | 2d5596036c3cdb260d56cc3b7902490f72ba25a2 | |
parent | bd39654a2282c1a51c044575a6bc00d641d5dfd1 (diff) |
s390/ap: Rework ap_dqap to deal with messages greater than recv buffer
Rework of the ap_dqap() inline function with the dqap inline assembler
invocation and the caller code in ap_queue.c to be able to handle
replies which exceed the receive buffer size.
ap_dqap() now provides two additional parameters to handle together
with the caller the case where a reply in the firmware queue entry
exceeds the given message buffer size. It depends on the caller how to
exactly handle this. The behavior implemented now by ap_sm_recv() in
ap_queue.c is to simple purge this entry from the firmware queue and
let the caller 'receive' a -EMSGSIZE for the request without
delivering any reply data - not even a truncated reply message.
However, the reworked ap_dqap() could now get invoked in a way that
the message is received in multiple parts and the caller assembles the
parts into one reply message.
Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Suggested-by: Juergen Christ <jchrist@linux.ibm.com>
Reviewed-by: Juergen Christ <jchrist@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
-rw-r--r-- | arch/s390/include/asm/ap.h | 48 | ||||
-rw-r--r-- | drivers/s390/crypto/ap_queue.c | 28 |
2 files changed, 65 insertions, 11 deletions
diff --git a/arch/s390/include/asm/ap.h b/arch/s390/include/asm/ap.h index 837d1699b109..5e77a81ab36a 100644 --- a/arch/s390/include/asm/ap.h +++ b/arch/s390/include/asm/ap.h @@ -325,6 +325,8 @@ static inline struct ap_queue_status ap_nqap(ap_qid_t qid, * @psmid: Pointer to program supplied message identifier * @msg: The message text * @length: The message length + * @reslength: Resitual length on return + * @resgr0: input: gr0 value (only used if != 0), output: resitual gr0 content * * Returns AP queue status structure. * Condition code 1 on DQAP means the receive has taken place @@ -336,12 +338,25 @@ static inline struct ap_queue_status ap_nqap(ap_qid_t qid, * Note that gpr2 is used by the DQAP instruction to keep track of * any 'residual' length, in case the instruction gets interrupted. * Hence it gets zeroed before the instruction. + * If the message does not fit into the buffer, this function will + * return with a truncated message and the reply in the firmware queue + * is not removed. This is indicated to the caller with an + * ap_queue_status response_code value of all bits on (0xFF) and (if + * the reslength ptr is given) the remaining length is stored in + * *reslength and (if the resgr0 ptr is given) the updated gr0 value + * for further processing of this msg entry is stored in *resgr0. The + * caller needs to detect this situation and should invoke ap_dqap + * with a valid resgr0 ptr and a value in there != 0 to indicate that + * *resgr0 is to be used instead of qid to further process this entry. */ static inline struct ap_queue_status ap_dqap(ap_qid_t qid, unsigned long long *psmid, - void *msg, size_t length) + void *msg, size_t length, + size_t *reslength, + unsigned long *resgr0) { - register unsigned long reg0 asm("0") = qid | 0x80000000UL; + register unsigned long reg0 asm("0") = + resgr0 && *resgr0 ? *resgr0 : qid | 0x80000000UL; register struct ap_queue_status reg1 asm ("1"); register unsigned long reg2 asm("2") = 0UL; register unsigned long reg4 asm("4") = (unsigned long) msg; @@ -349,14 +364,33 @@ static inline struct ap_queue_status ap_dqap(ap_qid_t qid, register unsigned long reg6 asm("6") = 0UL; register unsigned long reg7 asm("7") = 0UL; - asm volatile( - "0: .long 0xb2ae0064\n" /* DQAP */ - " brc 6,0b\n" + "0: ltgr %[bl],%[bl]\n" /* if avail. buf len is 0 then */ + " jz 2f\n" /* go out of this asm block */ + "1: .long 0xb2ae0064\n" /* DQAP */ + " brc 6,0b\n" /* if partially do again */ + "2:\n" : "+d" (reg0), "=d" (reg1), "+d" (reg2), - "+d" (reg4), "+d" (reg5), "+d" (reg6), "+d" (reg7) + "+d" (reg4), [bl] "+d" (reg5), "+d" (reg6), "+d" (reg7) : : "cc", "memory"); - *psmid = (((unsigned long long) reg6) << 32) + reg7; + + if (reslength) + *reslength = reg2; + if (reg2 != 0 && reg5 == 0) { + /* + * Partially complete, status in gr1 is not set. + * Signal the caller that this dqap is only partially received + * with a special status response code 0xFF and *resgr0 updated + */ + reg1.response_code = 0xFF; + if (resgr0) + *resgr0 = reg0; + } else { + *psmid = (((unsigned long long) reg6) << 32) + reg7; + if (resgr0) + *resgr0 = 0; + } + return reg1; } diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c index c5e0fe0286e5..669f96fddad6 100644 --- a/drivers/s390/crypto/ap_queue.c +++ b/drivers/s390/crypto/ap_queue.c @@ -101,7 +101,7 @@ int ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length) if (msg == NULL) return -EINVAL; - status = ap_dqap(qid, psmid, msg, length); + status = ap_dqap(qid, psmid, msg, length, NULL, NULL); switch (status.response_code) { case AP_RESPONSE_NORMAL: return 0; @@ -136,9 +136,24 @@ static struct ap_queue_status ap_sm_recv(struct ap_queue *aq) struct ap_queue_status status; struct ap_message *ap_msg; bool found = false; + size_t reslen; + unsigned long resgr0 = 0; + int parts = 0; + + /* + * DQAP loop until response code and resgr0 indicate that + * the msg is totally received. As we use the very same buffer + * the msg is overwritten with each invocation. That's intended + * and the receiver of the msg is informed with a msg rc code + * of EMSGSIZE in such a case. + */ + do { + status = ap_dqap(aq->qid, &aq->reply->psmid, + aq->reply->msg, aq->reply->bufsize, + &reslen, &resgr0); + parts++; + } while (status.response_code == 0xFF && resgr0 != 0); - status = ap_dqap(aq->qid, &aq->reply->psmid, - aq->reply->msg, aq->reply->bufsize); switch (status.response_code) { case AP_RESPONSE_NORMAL: aq->queue_count = max_t(int, 0, aq->queue_count - 1); @@ -150,7 +165,12 @@ static struct ap_queue_status ap_sm_recv(struct ap_queue *aq) continue; list_del_init(&ap_msg->list); aq->pendingq_count--; - ap_msg->receive(aq, ap_msg, aq->reply); + if (parts > 1) { + ap_msg->rc = -EMSGSIZE; + ap_msg->receive(aq, ap_msg, NULL); + } else { + ap_msg->receive(aq, ap_msg, aq->reply); + } found = true; break; } |