mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 06:02:05 +00:00
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>
This commit is contained in:
parent
bd39654a22
commit
1f0d22defd
@ -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");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
aq->reply->msg, aq->reply->bufsize,
|
||||
&reslen, &resgr0);
|
||||
parts++;
|
||||
} while (status.response_code == 0xFF && resgr0 != 0);
|
||||
|
||||
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--;
|
||||
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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user