in char/oradax.c [850:990]
static int dax_ccb_exec(struct dax_ctx *ctx, const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long accepted_len, hv_rv;
int i, idx, nccbs, naccepted;
ctx->client = current;
idx = *ppos;
nccbs = count / sizeof(struct dax_ccb);
if (ctx->owner != current) {
dax_dbg("wrong thread");
ctx->result.exec.status = DAX_SUBMIT_ERR_THR_INIT;
return 0;
}
dax_dbg("args: ccb_buf_len=%ld, idx=%d", count, idx);
/* for given index and length, verify ca_buf range exists */
if (idx < 0 || idx > (DAX_CA_ELEMS - nccbs)) {
ctx->result.exec.status = DAX_SUBMIT_ERR_NO_CA_AVAIL;
return 0;
}
/*
* Copy CCBs into kernel buffer to prevent modification by the
* user in between validation and submission.
*/
if (copy_from_user(ctx->ccb_buf, buf, count)) {
dax_dbg("copyin of user CCB buffer failed");
ctx->result.exec.status = DAX_SUBMIT_ERR_CCB_ARR_MMU_MISS;
return 0;
}
/* check to see if ca_buf[idx] .. ca_buf[idx + nccbs] are available */
for (i = idx; i < idx + nccbs; i++) {
if (ctx->ca_buf[i].status == CCA_STAT_NOT_COMPLETED) {
dax_dbg("CA range not available, dequeue needed");
ctx->result.exec.status = DAX_SUBMIT_ERR_NO_CA_AVAIL;
return 0;
}
}
dax_unlock_pages(ctx, idx, nccbs);
ctx->result.exec.status = dax_preprocess_usr_ccbs(ctx, idx, nccbs);
if (ctx->result.exec.status != DAX_SUBMIT_OK)
return 0;
ctx->result.exec.status = dax_lock_pages(ctx, idx, nccbs,
&ctx->result.exec.status_data);
if (ctx->result.exec.status != DAX_SUBMIT_OK)
return 0;
if (dax_debug & DAX_DBG_FLG_BASIC)
dax_prt_ccbs(ctx->ccb_buf, nccbs);
hv_rv = sun4v_ccb_submit(ctx->ccb_buf_ra, count,
HV_CCB_QUERY_CMD | HV_CCB_VA_SECONDARY, 0,
&accepted_len, &ctx->result.exec.status_data);
switch (hv_rv) {
case HV_EOK:
/*
* Hcall succeeded with no errors but the accepted
* length may be less than the requested length. The
* only way the driver can resubmit the remainder is
* to wait for completion of the submitted CCBs since
* there is no way to guarantee the ordering semantics
* required by the client applications. Therefore we
* let the user library deal with resubmissions.
*/
ctx->result.exec.status = DAX_SUBMIT_OK;
break;
case HV_EWOULDBLOCK:
/*
* This is a transient HV API error. The user library
* can retry.
*/
dax_dbg("hcall returned HV_EWOULDBLOCK");
ctx->result.exec.status = DAX_SUBMIT_ERR_WOULDBLOCK;
break;
case HV_ENOMAP:
/*
* HV was unable to translate a VA. The VA it could
* not translate is returned in the status_data param.
*/
dax_dbg("hcall returned HV_ENOMAP");
ctx->result.exec.status = DAX_SUBMIT_ERR_NOMAP;
break;
case HV_EINVAL:
/*
* This is the result of an invalid user CCB as HV is
* validating some of the user CCB fields. Pass this
* error back to the user. There is no supporting info
* to isolate the invalid field.
*/
dax_dbg("hcall returned HV_EINVAL");
ctx->result.exec.status = DAX_SUBMIT_ERR_CCB_INVAL;
break;
case HV_ENOACCESS:
/*
* HV found a VA that did not have the appropriate
* permissions (such as the w bit). The VA in question
* is returned in status_data param.
*/
dax_dbg("hcall returned HV_ENOACCESS");
ctx->result.exec.status = DAX_SUBMIT_ERR_NOACCESS;
break;
case HV_EUNAVAILABLE:
/*
* The requested CCB operation could not be performed
* at this time. Return the specific unavailable code
* in the status_data field.
*/
dax_dbg("hcall returned HV_EUNAVAILABLE");
ctx->result.exec.status = DAX_SUBMIT_ERR_UNAVAIL;
break;
default:
ctx->result.exec.status = DAX_SUBMIT_ERR_INTERNAL;
dax_dbg("unknown hcall return value (%ld)", hv_rv);
break;
}
/* unlock pages associated with the unaccepted CCBs */
naccepted = accepted_len / sizeof(struct dax_ccb);
dax_unlock_pages(ctx, idx + naccepted, nccbs - naccepted);
/* mark unaccepted CCBs as not completed */
for (i = idx + naccepted; i < idx + nccbs; i++)
ctx->ca_buf[i].status = CCA_STAT_COMPLETED;
ctx->ccb_count += naccepted;
ctx->fail_count += nccbs - naccepted;
dax_dbg("hcall rv=%ld, accepted_len=%ld, status_data=0x%llx, ret status=%d",
hv_rv, accepted_len, ctx->result.exec.status_data,
ctx->result.exec.status);
if (count == accepted_len)
ctx->client = NULL; /* no read needed to complete protocol */
return accepted_len;
}