static int dax_ccb_exec()

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