1392 lines · 35.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Copyright IBM Corp. 2016, 2023 * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> * * Adjunct processor bus, queue related code. */ #define pr_fmt(fmt) "ap: " fmt #include <linux/export.h>#include <linux/init.h>#include <linux/slab.h>#include <asm/facility.h> #define CREATE_TRACE_POINTS#include <asm/trace/ap.h> #include "ap_bus.h"#include "ap_debug.h" EXPORT_TRACEPOINT_SYMBOL(s390_ap_nqap);EXPORT_TRACEPOINT_SYMBOL(s390_ap_dqap); static void __ap_flush_queue(struct ap_queue *aq); /* * some AP queue helper functions */ static inline bool ap_q_supported_in_se(struct ap_queue *aq){	return aq->card->hwinfo.ep11 || aq->card->hwinfo.accel;} static inline bool ap_q_supports_bind(struct ap_queue *aq){	return aq->card->hwinfo.ep11 || aq->card->hwinfo.accel;} static inline bool ap_q_supports_assoc(struct ap_queue *aq){	return aq->card->hwinfo.ep11;} static inline bool ap_q_needs_bind(struct ap_queue *aq){	return ap_q_supports_bind(aq) && ap_sb_available();} /** * ap_queue_enable_irq(): Enable interrupt support on this AP queue. * @aq: The AP queue * @ind: the notification indicator byte * * Enables interruption on AP queue via ap_aqic(). Based on the return * value it waits a while and tests the AP queue if interrupts * have been switched on using ap_test_queue(). */static int ap_queue_enable_irq(struct ap_queue *aq, void *ind){	union ap_qirq_ctrl qirqctrl = { .value = 0 };	struct ap_queue_status status; 	qirqctrl.ir = 1;	qirqctrl.isc = AP_ISC;	status = ap_aqic(aq->qid, qirqctrl, virt_to_phys(ind));	if (status.async)		return -EPERM;	switch (status.response_code) {	case AP_RESPONSE_NORMAL:	case AP_RESPONSE_OTHERWISE_CHANGED:		return 0;	case AP_RESPONSE_Q_NOT_AVAIL:	case AP_RESPONSE_DECONFIGURED:	case AP_RESPONSE_CHECKSTOPPED:	case AP_RESPONSE_INVALID_ADDRESS:		pr_err("Registering adapter interrupts for AP device %02x.%04x failed\n",		       AP_QID_CARD(aq->qid),		       AP_QID_QUEUE(aq->qid));		return -EOPNOTSUPP;	case AP_RESPONSE_RESET_IN_PROGRESS:	case AP_RESPONSE_BUSY:	default:		return -EBUSY;	}} /** * __ap_send(): Send message to adjunct processor queue. * @qid: The AP queue number * @psmid: The program supplied message identifier * @msg: The message text * @msglen: The message length * @special: Special Bit * * Returns AP queue status structure. * Condition code 1 on NQAP can't happen because the L bit is 1. * Condition code 2 on NQAP also means the send is incomplete, * because a segment boundary was reached. The NQAP is repeated. */static inline struct ap_queue_status__ap_send(ap_qid_t qid, unsigned long psmid, void *msg, size_t msglen,	  int special){	struct ap_queue_status status; 	if (special)		qid |= 0x400000UL; 	status = ap_nqap(qid, psmid, msg, msglen); 	trace_s390_ap_nqap(AP_QID_CARD(qid), AP_QID_QUEUE(qid),			   status.value, psmid); 	return status;} /* State machine definitions and helpers */ static enum ap_sm_wait ap_sm_nop(struct ap_queue *aq){	return AP_SM_WAIT_NONE;} /** * ap_sm_recv(): Receive pending reply messages from an AP queue but do *	not change the state of the device. * @aq: pointer to the AP queue * * Returns AP_SM_WAIT_NONE, AP_SM_WAIT_AGAIN, or AP_SM_WAIT_INTERRUPT */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->len, &reslen, &resgr0);		parts++;	} while (status.response_code == 0xFF && resgr0 != 0); 	trace_s390_ap_dqap(AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid),			   status.value, aq->reply->psmid); 	switch (status.response_code) {	case AP_RESPONSE_NORMAL:		print_hex_dump_debug("aprpl: ", DUMP_PREFIX_ADDRESS, 16, 1,				     aq->reply->msg, aq->reply->len, false);		aq->queue_count = max_t(int, 0, aq->queue_count - 1);		if (!status.queue_empty && !aq->queue_count)			aq->queue_count++;		if (aq->queue_count > 0)			mod_timer(&aq->timeout,				  jiffies + aq->request_timeout);		list_for_each_entry(ap_msg, &aq->pendingq, list) {			if (ap_msg->psmid != aq->reply->psmid)				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;		}		if (!found) {			AP_DBF_WARN("%s unassociated reply psmid=0x%016lx on 0x%02x.%04x\n",				    __func__, aq->reply->psmid,				    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));		}		fallthrough;	case AP_RESPONSE_NO_PENDING_REPLY:		if (!status.queue_empty || aq->queue_count <= 0)			break;		/* The card shouldn't forget requests but who knows. */		aq->queue_count = 0;		list_splice_init(&aq->pendingq, &aq->requestq);		aq->requestq_count += aq->pendingq_count;		pr_debug("queue 0x%02x.%04x rescheduled %d reqs (new req %d)\n",			 AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid),			 aq->pendingq_count, aq->requestq_count);		aq->pendingq_count = 0;		break;	default:		break;	}	return status;} /** * ap_sm_read(): Receive pending reply messages from an AP queue. * @aq: pointer to the AP queue * * Returns AP_SM_WAIT_NONE, AP_SM_WAIT_AGAIN, or AP_SM_WAIT_INTERRUPT */static enum ap_sm_wait ap_sm_read(struct ap_queue *aq){	struct ap_queue_status status; 	if (!aq->reply)		return AP_SM_WAIT_NONE;	status = ap_sm_recv(aq);	if (status.async)		return AP_SM_WAIT_NONE;	switch (status.response_code) {	case AP_RESPONSE_NORMAL:		if (aq->queue_count > 0) {			aq->sm_state = AP_SM_STATE_WORKING;			return AP_SM_WAIT_AGAIN;		}		aq->sm_state = AP_SM_STATE_IDLE;		break;	case AP_RESPONSE_NO_PENDING_REPLY:		if (aq->queue_count > 0)			return status.irq_enabled ?				AP_SM_WAIT_INTERRUPT : AP_SM_WAIT_HIGH_TIMEOUT;		aq->sm_state = AP_SM_STATE_IDLE;		break;	default:		aq->dev_state = AP_DEV_STATE_ERROR;		aq->last_err_rc = status.response_code;		AP_DBF_WARN("%s RC 0x%02x on 0x%02x.%04x -> AP_DEV_STATE_ERROR\n",			    __func__, status.response_code,			    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));		return AP_SM_WAIT_NONE;	}	/* Check and maybe enable irq support (again) on this queue */	if (!status.irq_enabled && status.queue_empty) {		void *lsi_ptr = ap_airq_ptr(); 		if (lsi_ptr && ap_queue_enable_irq(aq, lsi_ptr) == 0) {			aq->sm_state = AP_SM_STATE_SETIRQ_WAIT;			return AP_SM_WAIT_AGAIN;		}	}	return AP_SM_WAIT_NONE;} /** * ap_sm_write(): Send messages from the request queue to an AP queue. * @aq: pointer to the AP queue * * Returns AP_SM_WAIT_NONE, AP_SM_WAIT_AGAIN, or AP_SM_WAIT_INTERRUPT */static enum ap_sm_wait ap_sm_write(struct ap_queue *aq){	struct ap_queue_status status;	struct ap_message *ap_msg;	ap_qid_t qid = aq->qid; 	if (aq->requestq_count <= 0)		return AP_SM_WAIT_NONE; 	/* Start the next request on the queue. */	ap_msg = list_entry(aq->requestq.next, struct ap_message, list);	print_hex_dump_debug("apreq: ", DUMP_PREFIX_ADDRESS, 16, 1,			     ap_msg->msg, ap_msg->len, false);	status = __ap_send(qid, ap_msg->psmid,			   ap_msg->msg, ap_msg->len,			   ap_msg->flags & AP_MSG_FLAG_SPECIAL);	if (status.async)		return AP_SM_WAIT_NONE;	switch (status.response_code) {	case AP_RESPONSE_NORMAL:		aq->queue_count = max_t(int, 1, aq->queue_count + 1);		if (aq->queue_count == 1)			mod_timer(&aq->timeout, jiffies + aq->request_timeout);		list_move_tail(&ap_msg->list, &aq->pendingq);		aq->requestq_count--;		aq->pendingq_count++;		if (aq->queue_count < aq->card->hwinfo.qd + 1) {			aq->sm_state = AP_SM_STATE_WORKING;			return AP_SM_WAIT_AGAIN;		}		fallthrough;	case AP_RESPONSE_Q_FULL:		aq->sm_state = AP_SM_STATE_QUEUE_FULL;		return status.irq_enabled ?			AP_SM_WAIT_INTERRUPT : AP_SM_WAIT_HIGH_TIMEOUT;	case AP_RESPONSE_RESET_IN_PROGRESS:		aq->sm_state = AP_SM_STATE_RESET_WAIT;		return AP_SM_WAIT_LOW_TIMEOUT;	case AP_RESPONSE_INVALID_DOMAIN:		AP_DBF_WARN("%s RESPONSE_INVALID_DOMAIN on NQAP\n", __func__);		fallthrough;	case AP_RESPONSE_MESSAGE_TOO_BIG:	case AP_RESPONSE_REQ_FAC_NOT_INST:		list_del_init(&ap_msg->list);		aq->requestq_count--;		ap_msg->rc = -EINVAL;		ap_msg->receive(aq, ap_msg, NULL);		return AP_SM_WAIT_AGAIN;	default:		aq->dev_state = AP_DEV_STATE_ERROR;		aq->last_err_rc = status.response_code;		AP_DBF_WARN("%s RC 0x%02x on 0x%02x.%04x -> AP_DEV_STATE_ERROR\n",			    __func__, status.response_code,			    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));		return AP_SM_WAIT_NONE;	}} /** * ap_sm_read_write(): Send and receive messages to/from an AP queue. * @aq: pointer to the AP queue * * Returns AP_SM_WAIT_NONE, AP_SM_WAIT_AGAIN, or AP_SM_WAIT_INTERRUPT */static enum ap_sm_wait ap_sm_read_write(struct ap_queue *aq){	return min(ap_sm_read(aq), ap_sm_write(aq));} /** * ap_sm_reset(): Reset an AP queue. * @aq: The AP queue * * Submit the Reset command to an AP queue. */static enum ap_sm_wait ap_sm_reset(struct ap_queue *aq){	struct ap_queue_status status; 	status = ap_rapq(aq->qid, aq->rapq_fbit);	if (status.async)		return AP_SM_WAIT_NONE;	switch (status.response_code) {	case AP_RESPONSE_NORMAL:	case AP_RESPONSE_RESET_IN_PROGRESS:		aq->sm_state = AP_SM_STATE_RESET_WAIT;		aq->rapq_fbit = 0;		return AP_SM_WAIT_LOW_TIMEOUT;	default:		aq->dev_state = AP_DEV_STATE_ERROR;		aq->last_err_rc = status.response_code;		AP_DBF_WARN("%s RC 0x%02x on 0x%02x.%04x -> AP_DEV_STATE_ERROR\n",			    __func__, status.response_code,			    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));		return AP_SM_WAIT_NONE;	}} /** * ap_sm_reset_wait(): Test queue for completion of the reset operation * @aq: pointer to the AP queue * * Returns AP_POLL_IMMEDIATELY, AP_POLL_AFTER_TIMEROUT or 0. */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; 	/* 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;		else			aq->sm_state = (aq->queue_count > 0) ?				AP_SM_STATE_WORKING : AP_SM_STATE_IDLE;		return AP_SM_WAIT_AGAIN;	case AP_RESPONSE_BUSY:	case AP_RESPONSE_RESET_IN_PROGRESS:		return AP_SM_WAIT_LOW_TIMEOUT;	case AP_RESPONSE_Q_NOT_AVAIL:	case AP_RESPONSE_DECONFIGURED:	case AP_RESPONSE_CHECKSTOPPED:	default:		aq->dev_state = AP_DEV_STATE_ERROR;		aq->last_err_rc = status.response_code;		AP_DBF_WARN("%s RC 0x%02x on 0x%02x.%04x -> AP_DEV_STATE_ERROR\n",			    __func__, status.response_code,			    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));		return AP_SM_WAIT_NONE;	}} /** * ap_sm_setirq_wait(): Test queue for completion of the irq enablement * @aq: pointer to the AP queue * * Returns AP_POLL_IMMEDIATELY, AP_POLL_AFTER_TIMEROUT or 0. */static enum ap_sm_wait ap_sm_setirq_wait(struct ap_queue *aq){	struct ap_queue_status status; 	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); 	if (status.irq_enabled == 1) {		/* Irqs are now enabled */		aq->sm_state = (aq->queue_count > 0) ?			AP_SM_STATE_WORKING : AP_SM_STATE_IDLE;	} 	switch (status.response_code) {	case AP_RESPONSE_NORMAL:		if (aq->queue_count > 0)			return AP_SM_WAIT_AGAIN;		fallthrough;	case AP_RESPONSE_NO_PENDING_REPLY:		return AP_SM_WAIT_LOW_TIMEOUT;	default:		aq->dev_state = AP_DEV_STATE_ERROR;		aq->last_err_rc = status.response_code;		AP_DBF_WARN("%s RC 0x%02x on 0x%02x.%04x -> AP_DEV_STATE_ERROR\n",			    __func__, status.response_code,			    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));		return AP_SM_WAIT_NONE;	}} /** * ap_sm_assoc_wait(): Test queue for completion of a pending *		       association request. * @aq: pointer to the AP queue */static enum ap_sm_wait ap_sm_assoc_wait(struct ap_queue *aq){	struct ap_queue_status status;	struct ap_tapq_hwinfo hwinfo; 	status = ap_test_queue(aq->qid, 1, &hwinfo);	/* handle asynchronous error on this queue */	if (status.async && status.response_code) {		aq->dev_state = AP_DEV_STATE_ERROR;		aq->last_err_rc = status.response_code;		AP_DBF_WARN("%s asynch RC 0x%02x on 0x%02x.%04x -> AP_DEV_STATE_ERROR\n",			    __func__, status.response_code,			    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));		return AP_SM_WAIT_NONE;	}	if (status.response_code > AP_RESPONSE_BUSY) {		aq->dev_state = AP_DEV_STATE_ERROR;		aq->last_err_rc = status.response_code;		AP_DBF_WARN("%s RC 0x%02x on 0x%02x.%04x -> AP_DEV_STATE_ERROR\n",			    __func__, status.response_code,			    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));		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:		/* association is through */		aq->sm_state = AP_SM_STATE_IDLE;		pr_debug("queue 0x%02x.%04x associated with %u\n",			 AP_QID_CARD(aq->qid),			 AP_QID_QUEUE(aq->qid), aq->assoc_idx);		ap_send_se_assoc_uevent(&aq->ap_dev, aq->assoc_idx);		return AP_SM_WAIT_NONE;	case AP_BS_Q_USABLE_NO_SECURE_KEY:		/* association still pending */		return AP_SM_WAIT_LOW_TIMEOUT;	default:		/* reset from 'outside' happened or no idea at all */		aq->assoc_idx = ASSOC_IDX_INVALID;		aq->dev_state = AP_DEV_STATE_ERROR;		aq->last_err_rc = status.response_code;		AP_DBF_WARN("%s bs 0x%02x on 0x%02x.%04x -> AP_DEV_STATE_ERROR\n",			    __func__, hwinfo.bs,			    AP_QID_CARD(aq->qid), AP_QID_QUEUE(aq->qid));		return AP_SM_WAIT_NONE;	}} /* * AP state machine jump table */

Lines 1–500 of 1392.