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;
}