static long _zcrypt_send_ep11_cprb()

in crypto/zcrypt_api.c [1010:1137]


static long _zcrypt_send_ep11_cprb(bool userspace, struct ap_perms *perms,
				   struct zcrypt_track *tr,
				   struct ep11_urb *xcrb)
{
	struct zcrypt_card *zc, *pref_zc;
	struct zcrypt_queue *zq, *pref_zq;
	struct ep11_target_dev *targets;
	unsigned short target_num;
	unsigned int wgt = 0, pref_wgt = 0;
	unsigned int func_code;
	struct ap_message ap_msg;
	int cpen, qpen, qid = 0, rc = -ENODEV;
	struct module *mod;

	trace_s390_zcrypt_req(xcrb, TP_ZSENDEP11CPRB);

	ap_init_message(&ap_msg);

#ifdef CONFIG_ZCRYPT_DEBUG
	if (tr && tr->fi.cmd)
		ap_msg.fi.cmd = tr->fi.cmd;
#endif

	target_num = (unsigned short) xcrb->targets_num;

	/* empty list indicates autoselect (all available targets) */
	targets = NULL;
	if (target_num != 0) {
		struct ep11_target_dev __user *uptr;

		targets = kcalloc(target_num, sizeof(*targets), GFP_KERNEL);
		if (!targets) {
			func_code = 0;
			rc = -ENOMEM;
			goto out;
		}

		uptr = (struct ep11_target_dev __force __user *) xcrb->targets;
		if (z_copy_from_user(userspace, targets, uptr,
				   target_num * sizeof(*targets))) {
			func_code = 0;
			rc = -EFAULT;
			goto out_free;
		}
	}

	rc = get_ep11cprb_fc(userspace, xcrb, &ap_msg, &func_code);
	if (rc)
		goto out_free;

	pref_zc = NULL;
	pref_zq = NULL;
	spin_lock(&zcrypt_list_lock);
	for_each_zcrypt_card(zc) {
		/* Check for useable EP11 card */
		if (!zc->online || !zc->card->config ||
		    !(zc->card->functions & 0x04000000))
			continue;
		/* Check for user selected EP11 card */
		if (targets &&
		    !is_desired_ep11_card(zc->card->id, target_num, targets))
			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_ep11(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 if device is useable and eligible */
			if (!zq->online ||
			    !zq->ops->send_ep11_cprb ||
			    !zq->queue->config ||
			    (targets &&
			     !is_desired_ep11_queue(zq->queue->qid,
						    target_num, targets)))
				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_free;
	}

	qid = pref_zq->queue->qid;
	rc = pref_zq->ops->send_ep11_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_free:
	kfree(targets);
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;
}