static long _zcrypt_send_cprb()

in crypto/zcrypt_api.c [847:975]


static long _zcrypt_send_cprb(bool userspace, struct ap_perms *perms,
			      struct zcrypt_track *tr,
			      struct ica_xcRB *xcRB)
{
	struct zcrypt_card *zc, *pref_zc;
	struct zcrypt_queue *zq, *pref_zq;
	struct ap_message ap_msg;
	unsigned int wgt = 0, pref_wgt = 0;
	unsigned int func_code;
	unsigned short *domain, tdom;
	int cpen, qpen, qid = 0, rc = -ENODEV;
	struct module *mod;

	trace_s390_zcrypt_req(xcRB, TB_ZSECSENDCPRB);

	xcRB->status = 0;
	ap_init_message(&ap_msg);

#ifdef CONFIG_ZCRYPT_DEBUG
	if (tr && tr->fi.cmd)
		ap_msg.fi.cmd = tr->fi.cmd;
	if (tr && tr->fi.action == AP_FI_ACTION_CCA_AGENT_FF) {
		ZCRYPT_DBF_WARN("%s fi cmd 0x%04x: forcing invalid agent_ID 'FF'\n",
				__func__, tr->fi.cmd);
		xcRB->agent_ID = 0x4646;
	}
#endif

	rc = get_cprb_fc(userspace, xcRB, &ap_msg, &func_code, &domain);
	if (rc)
		goto out;

	/*
	 * If a valid target domain is set and this domain is NOT a usage
	 * domain but a control only domain, autoselect target domain.
	 */
	tdom = *domain;
	if (tdom < AP_DOMAINS &&
	    !ap_test_config_usage_domain(tdom) &&
	    ap_test_config_ctrl_domain(tdom))
		tdom = AUTOSEL_DOM;

	pref_zc = NULL;
	pref_zq = NULL;
	spin_lock(&zcrypt_list_lock);
	for_each_zcrypt_card(zc) {
		/* Check for useable CCA card */
		if (!zc->online || !zc->card->config ||
		    !(zc->card->functions & 0x10000000))
			continue;
		/* Check for user selected CCA card */
		if (xcRB->user_defined != AUTOSELECT &&
		    xcRB->user_defined != zc->card->id)
			continue;
		/* check if request size exceeds card max msg size */
		if (ap_msg.len > zc->card->maxmsgsize)
			continue;
		/* check if device node has admission for this card */
		if (!zcrypt_check_card(perms, zc->card->id))
			continue;
		/* get weight index of the card device	*/
		wgt = speed_idx_cca(func_code) * zc->speed_rating[SECKEY];
		/* penalty if this msg was previously sent via this card */
		cpen = (tr && tr->again_counter && tr->last_qid &&
			AP_QID_CARD(tr->last_qid) == zc->card->id) ?
			TRACK_AGAIN_CARD_WEIGHT_PENALTY : 0;
		if (!zcrypt_card_compare(zc, pref_zc, wgt + cpen, pref_wgt))
			continue;
		for_each_zcrypt_queue(zq, zc) {
			/* check for device useable and eligible */
			if (!zq->online ||
			    !zq->ops->send_cprb ||
			    !zq->queue->config ||
			    (tdom != AUTOSEL_DOM &&
			     tdom != AP_QID_QUEUE(zq->queue->qid)))
				continue;
			/* check if device node has admission for this queue */
			if (!zcrypt_check_queue(perms,
						AP_QID_QUEUE(zq->queue->qid)))
				continue;
			/* penalty if the msg was previously sent at this qid */
			qpen = (tr && tr->again_counter && tr->last_qid &&
				tr->last_qid == zq->queue->qid) ?
				TRACK_AGAIN_QUEUE_WEIGHT_PENALTY : 0;
			if (!zcrypt_queue_compare(zq, pref_zq,
						  wgt + cpen + qpen, pref_wgt))
				continue;
			pref_zc = zc;
			pref_zq = zq;
			pref_wgt = wgt + cpen + qpen;
		}
	}
	pref_zq = zcrypt_pick_queue(pref_zc, pref_zq, &mod, wgt);
	spin_unlock(&zcrypt_list_lock);

	if (!pref_zq) {
		rc = -ENODEV;
		goto out;
	}

	/* in case of auto select, provide the correct domain */
	qid = pref_zq->queue->qid;
	if (*domain == AUTOSEL_DOM)
		*domain = AP_QID_QUEUE(qid);

#ifdef CONFIG_ZCRYPT_DEBUG
	if (tr && tr->fi.action == AP_FI_ACTION_CCA_DOM_INVAL) {
		ZCRYPT_DBF_WARN("%s fi cmd 0x%04x: forcing invalid domain\n",
				__func__, tr->fi.cmd);
		*domain = 99;
	}
#endif

	rc = pref_zq->ops->send_cprb(userspace, pref_zq, xcRB, &ap_msg);

	spin_lock(&zcrypt_list_lock);
	zcrypt_drop_queue(pref_zc, pref_zq, mod, wgt);
	spin_unlock(&zcrypt_list_lock);

out:
	ap_release_message(&ap_msg);
	if (tr) {
		tr->last_rc = rc;
		tr->last_qid = qid;
	}
	trace_s390_zcrypt_rep(xcRB, func_code, rc,
			      AP_QID_CARD(qid), AP_QID_QUEUE(qid));
	return rc;
}