18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci/*
78c2ecf20Sopenharmony_ci * Oracle Data Analytics Accelerator (DAX)
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * DAX is a coprocessor which resides on the SPARC M7 (DAX1) and M8
108c2ecf20Sopenharmony_ci * (DAX2) processor chips, and has direct access to the CPU's L3
118c2ecf20Sopenharmony_ci * caches as well as physical memory. It can perform several
128c2ecf20Sopenharmony_ci * operations on data streams with various input and output formats.
138c2ecf20Sopenharmony_ci * The driver provides a transport mechanism only and has limited
148c2ecf20Sopenharmony_ci * knowledge of the various opcodes and data formats. A user space
158c2ecf20Sopenharmony_ci * library provides high level services and translates these into low
168c2ecf20Sopenharmony_ci * level commands which are then passed into the driver and
178c2ecf20Sopenharmony_ci * subsequently the hypervisor and the coprocessor.  The library is
188c2ecf20Sopenharmony_ci * the recommended way for applications to use the coprocessor, and
198c2ecf20Sopenharmony_ci * the driver interface is not intended for general use.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * See Documentation/sparc/oradax/oracle-dax.rst for more details.
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
258c2ecf20Sopenharmony_ci#include <linux/module.h>
268c2ecf20Sopenharmony_ci#include <linux/delay.h>
278c2ecf20Sopenharmony_ci#include <linux/cdev.h>
288c2ecf20Sopenharmony_ci#include <linux/slab.h>
298c2ecf20Sopenharmony_ci#include <linux/mm.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <asm/hypervisor.h>
328c2ecf20Sopenharmony_ci#include <asm/mdesc.h>
338c2ecf20Sopenharmony_ci#include <asm/oradax.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
368c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for Oracle Data Analytics Accelerator");
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define	DAX_DBG_FLG_BASIC	0x01
398c2ecf20Sopenharmony_ci#define	DAX_DBG_FLG_STAT	0x02
408c2ecf20Sopenharmony_ci#define	DAX_DBG_FLG_INFO	0x04
418c2ecf20Sopenharmony_ci#define	DAX_DBG_FLG_ALL		0xff
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define	dax_err(fmt, ...)      pr_err("%s: " fmt "\n", __func__, ##__VA_ARGS__)
448c2ecf20Sopenharmony_ci#define	dax_info(fmt, ...)     pr_info("%s: " fmt "\n", __func__, ##__VA_ARGS__)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define	dax_dbg(fmt, ...)	do {					\
478c2ecf20Sopenharmony_ci					if (dax_debug & DAX_DBG_FLG_BASIC)\
488c2ecf20Sopenharmony_ci						dax_info(fmt, ##__VA_ARGS__); \
498c2ecf20Sopenharmony_ci				} while (0)
508c2ecf20Sopenharmony_ci#define	dax_stat_dbg(fmt, ...)	do {					\
518c2ecf20Sopenharmony_ci					if (dax_debug & DAX_DBG_FLG_STAT) \
528c2ecf20Sopenharmony_ci						dax_info(fmt, ##__VA_ARGS__); \
538c2ecf20Sopenharmony_ci				} while (0)
548c2ecf20Sopenharmony_ci#define	dax_info_dbg(fmt, ...)	do { \
558c2ecf20Sopenharmony_ci					if (dax_debug & DAX_DBG_FLG_INFO) \
568c2ecf20Sopenharmony_ci						dax_info(fmt, ##__VA_ARGS__); \
578c2ecf20Sopenharmony_ci				} while (0)
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#define	DAX1_MINOR		1
608c2ecf20Sopenharmony_ci#define	DAX1_MAJOR		1
618c2ecf20Sopenharmony_ci#define	DAX2_MINOR		0
628c2ecf20Sopenharmony_ci#define	DAX2_MAJOR		2
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define	DAX1_STR    "ORCL,sun4v-dax"
658c2ecf20Sopenharmony_ci#define	DAX2_STR    "ORCL,sun4v-dax2"
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#define	DAX_CA_ELEMS		(DAX_MMAP_LEN / sizeof(struct dax_cca))
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#define	DAX_CCB_USEC		100
708c2ecf20Sopenharmony_ci#define	DAX_CCB_RETRIES		10000
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* stream types */
738c2ecf20Sopenharmony_cienum {
748c2ecf20Sopenharmony_ci	OUT,
758c2ecf20Sopenharmony_ci	PRI,
768c2ecf20Sopenharmony_ci	SEC,
778c2ecf20Sopenharmony_ci	TBL,
788c2ecf20Sopenharmony_ci	NUM_STREAM_TYPES
798c2ecf20Sopenharmony_ci};
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/* completion status */
828c2ecf20Sopenharmony_ci#define	CCA_STAT_NOT_COMPLETED	0
838c2ecf20Sopenharmony_ci#define	CCA_STAT_COMPLETED	1
848c2ecf20Sopenharmony_ci#define	CCA_STAT_FAILED		2
858c2ecf20Sopenharmony_ci#define	CCA_STAT_KILLED		3
868c2ecf20Sopenharmony_ci#define	CCA_STAT_NOT_RUN	4
878c2ecf20Sopenharmony_ci#define	CCA_STAT_PIPE_OUT	5
888c2ecf20Sopenharmony_ci#define	CCA_STAT_PIPE_SRC	6
898c2ecf20Sopenharmony_ci#define	CCA_STAT_PIPE_DST	7
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/* completion err */
928c2ecf20Sopenharmony_ci#define	CCA_ERR_SUCCESS		0x0	/* no error */
938c2ecf20Sopenharmony_ci#define	CCA_ERR_OVERFLOW	0x1	/* buffer overflow */
948c2ecf20Sopenharmony_ci#define	CCA_ERR_DECODE		0x2	/* CCB decode error */
958c2ecf20Sopenharmony_ci#define	CCA_ERR_PAGE_OVERFLOW	0x3	/* page overflow */
968c2ecf20Sopenharmony_ci#define	CCA_ERR_KILLED		0x7	/* command was killed */
978c2ecf20Sopenharmony_ci#define	CCA_ERR_TIMEOUT		0x8	/* Timeout */
988c2ecf20Sopenharmony_ci#define	CCA_ERR_ADI		0x9	/* ADI error */
998c2ecf20Sopenharmony_ci#define	CCA_ERR_DATA_FMT	0xA	/* data format error */
1008c2ecf20Sopenharmony_ci#define	CCA_ERR_OTHER_NO_RETRY	0xE	/* Other error, do not retry */
1018c2ecf20Sopenharmony_ci#define	CCA_ERR_OTHER_RETRY	0xF	/* Other error, retry */
1028c2ecf20Sopenharmony_ci#define	CCA_ERR_PARTIAL_SYMBOL	0x80	/* QP partial symbol warning */
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/* CCB address types */
1058c2ecf20Sopenharmony_ci#define	DAX_ADDR_TYPE_NONE	0
1068c2ecf20Sopenharmony_ci#define	DAX_ADDR_TYPE_VA_ALT	1	/* secondary context */
1078c2ecf20Sopenharmony_ci#define	DAX_ADDR_TYPE_RA	2	/* real address */
1088c2ecf20Sopenharmony_ci#define	DAX_ADDR_TYPE_VA	3	/* virtual address */
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/* dax_header_t opcode */
1118c2ecf20Sopenharmony_ci#define	DAX_OP_SYNC_NOP		0x0
1128c2ecf20Sopenharmony_ci#define	DAX_OP_EXTRACT		0x1
1138c2ecf20Sopenharmony_ci#define	DAX_OP_SCAN_VALUE	0x2
1148c2ecf20Sopenharmony_ci#define	DAX_OP_SCAN_RANGE	0x3
1158c2ecf20Sopenharmony_ci#define	DAX_OP_TRANSLATE	0x4
1168c2ecf20Sopenharmony_ci#define	DAX_OP_SELECT		0x5
1178c2ecf20Sopenharmony_ci#define	DAX_OP_INVERT		0x10	/* OR with translate, scan opcodes */
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistruct dax_header {
1208c2ecf20Sopenharmony_ci	u32 ccb_version:4;	/* 31:28 CCB Version */
1218c2ecf20Sopenharmony_ci				/* 27:24 Sync Flags */
1228c2ecf20Sopenharmony_ci	u32 pipe:1;		/* Pipeline */
1238c2ecf20Sopenharmony_ci	u32 longccb:1;		/* Longccb. Set for scan with lu2, lu3, lu4. */
1248c2ecf20Sopenharmony_ci	u32 cond:1;		/* Conditional */
1258c2ecf20Sopenharmony_ci	u32 serial:1;		/* Serial */
1268c2ecf20Sopenharmony_ci	u32 opcode:8;		/* 23:16 Opcode */
1278c2ecf20Sopenharmony_ci				/* 15:0 Address Type. */
1288c2ecf20Sopenharmony_ci	u32 reserved:3;		/* 15:13 reserved */
1298c2ecf20Sopenharmony_ci	u32 table_addr_type:2;	/* 12:11 Huffman Table Address Type */
1308c2ecf20Sopenharmony_ci	u32 out_addr_type:3;	/* 10:8 Destination Address Type */
1318c2ecf20Sopenharmony_ci	u32 sec_addr_type:3;	/* 7:5 Secondary Source Address Type */
1328c2ecf20Sopenharmony_ci	u32 pri_addr_type:3;	/* 4:2 Primary Source Address Type */
1338c2ecf20Sopenharmony_ci	u32 cca_addr_type:2;	/* 1:0 Completion Address Type */
1348c2ecf20Sopenharmony_ci};
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistruct dax_control {
1378c2ecf20Sopenharmony_ci	u32 pri_fmt:4;		/* 31:28 Primary Input Format */
1388c2ecf20Sopenharmony_ci	u32 pri_elem_size:5;	/* 27:23 Primary Input Element Size(less1) */
1398c2ecf20Sopenharmony_ci	u32 pri_offset:3;	/* 22:20 Primary Input Starting Offset */
1408c2ecf20Sopenharmony_ci	u32 sec_encoding:1;	/* 19    Secondary Input Encoding */
1418c2ecf20Sopenharmony_ci				/*	 (must be 0 for Select) */
1428c2ecf20Sopenharmony_ci	u32 sec_offset:3;	/* 18:16 Secondary Input Starting Offset */
1438c2ecf20Sopenharmony_ci	u32 sec_elem_size:2;	/* 15:14 Secondary Input Element Size */
1448c2ecf20Sopenharmony_ci				/*	 (must be 0 for Select) */
1458c2ecf20Sopenharmony_ci	u32 out_fmt:2;		/* 13:12 Output Format */
1468c2ecf20Sopenharmony_ci	u32 out_elem_size:2;	/* 11:10 Output Element Size */
1478c2ecf20Sopenharmony_ci	u32 misc:10;		/* 9:0 Opcode specific info */
1488c2ecf20Sopenharmony_ci};
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistruct dax_data_access {
1518c2ecf20Sopenharmony_ci	u64 flow_ctrl:2;	/* 63:62 Flow Control Type */
1528c2ecf20Sopenharmony_ci	u64 pipe_target:2;	/* 61:60 Pipeline Target */
1538c2ecf20Sopenharmony_ci	u64 out_buf_size:20;	/* 59:40 Output Buffer Size */
1548c2ecf20Sopenharmony_ci				/*	 (cachelines less 1) */
1558c2ecf20Sopenharmony_ci	u64 unused1:8;		/* 39:32 Reserved, Set to 0 */
1568c2ecf20Sopenharmony_ci	u64 out_alloc:5;	/* 31:27 Output Allocation */
1578c2ecf20Sopenharmony_ci	u64 unused2:1;		/* 26	 Reserved */
1588c2ecf20Sopenharmony_ci	u64 pri_len_fmt:2;	/* 25:24 Input Length Format */
1598c2ecf20Sopenharmony_ci	u64 pri_len:24;		/* 23:0  Input Element/Byte/Bit Count */
1608c2ecf20Sopenharmony_ci				/*	 (less 1) */
1618c2ecf20Sopenharmony_ci};
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistruct dax_ccb {
1648c2ecf20Sopenharmony_ci	struct dax_header hdr;	/* CCB Header */
1658c2ecf20Sopenharmony_ci	struct dax_control ctrl;/* Control Word */
1668c2ecf20Sopenharmony_ci	void *ca;		/* Completion Address */
1678c2ecf20Sopenharmony_ci	void *pri;		/* Primary Input Address */
1688c2ecf20Sopenharmony_ci	struct dax_data_access dac; /* Data Access Control */
1698c2ecf20Sopenharmony_ci	void *sec;		/* Secondary Input Address */
1708c2ecf20Sopenharmony_ci	u64 dword5;		/* depends on opcode */
1718c2ecf20Sopenharmony_ci	void *out;		/* Output Address */
1728c2ecf20Sopenharmony_ci	void *tbl;		/* Table Address or bitmap */
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistruct dax_cca {
1768c2ecf20Sopenharmony_ci	u8	status;		/* user may mwait on this address */
1778c2ecf20Sopenharmony_ci	u8	err;		/* user visible error notification */
1788c2ecf20Sopenharmony_ci	u8	rsvd[2];	/* reserved */
1798c2ecf20Sopenharmony_ci	u32	n_remaining;	/* for QP partial symbol warning */
1808c2ecf20Sopenharmony_ci	u32	output_sz;	/* output in bytes */
1818c2ecf20Sopenharmony_ci	u32	rsvd2;		/* reserved */
1828c2ecf20Sopenharmony_ci	u64	run_cycles;	/* run time in OCND2 cycles */
1838c2ecf20Sopenharmony_ci	u64	run_stats;	/* nothing reported in version 1.0 */
1848c2ecf20Sopenharmony_ci	u32	n_processed;	/* number input elements */
1858c2ecf20Sopenharmony_ci	u32	rsvd3[5];	/* reserved */
1868c2ecf20Sopenharmony_ci	u64	retval;		/* command return value */
1878c2ecf20Sopenharmony_ci	u64	rsvd4[8];	/* reserved */
1888c2ecf20Sopenharmony_ci};
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci/* per thread CCB context */
1918c2ecf20Sopenharmony_cistruct dax_ctx {
1928c2ecf20Sopenharmony_ci	struct dax_ccb		*ccb_buf;
1938c2ecf20Sopenharmony_ci	u64			ccb_buf_ra;	/* cached RA of ccb_buf  */
1948c2ecf20Sopenharmony_ci	struct dax_cca		*ca_buf;
1958c2ecf20Sopenharmony_ci	u64			ca_buf_ra;	/* cached RA of ca_buf   */
1968c2ecf20Sopenharmony_ci	struct page		*pages[DAX_CA_ELEMS][NUM_STREAM_TYPES];
1978c2ecf20Sopenharmony_ci						/* array of locked pages */
1988c2ecf20Sopenharmony_ci	struct task_struct	*owner;		/* thread that owns ctx  */
1998c2ecf20Sopenharmony_ci	struct task_struct	*client;	/* requesting thread     */
2008c2ecf20Sopenharmony_ci	union ccb_result	result;
2018c2ecf20Sopenharmony_ci	u32			ccb_count;
2028c2ecf20Sopenharmony_ci	u32			fail_count;
2038c2ecf20Sopenharmony_ci};
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci/* driver public entry points */
2068c2ecf20Sopenharmony_cistatic int dax_open(struct inode *inode, struct file *file);
2078c2ecf20Sopenharmony_cistatic ssize_t dax_read(struct file *filp, char __user *buf,
2088c2ecf20Sopenharmony_ci			size_t count, loff_t *ppos);
2098c2ecf20Sopenharmony_cistatic ssize_t dax_write(struct file *filp, const char __user *buf,
2108c2ecf20Sopenharmony_ci			 size_t count, loff_t *ppos);
2118c2ecf20Sopenharmony_cistatic int dax_devmap(struct file *f, struct vm_area_struct *vma);
2128c2ecf20Sopenharmony_cistatic int dax_close(struct inode *i, struct file *f);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic const struct file_operations dax_fops = {
2158c2ecf20Sopenharmony_ci	.owner	=	THIS_MODULE,
2168c2ecf20Sopenharmony_ci	.open	=	dax_open,
2178c2ecf20Sopenharmony_ci	.read	=	dax_read,
2188c2ecf20Sopenharmony_ci	.write	=	dax_write,
2198c2ecf20Sopenharmony_ci	.mmap	=	dax_devmap,
2208c2ecf20Sopenharmony_ci	.release =	dax_close,
2218c2ecf20Sopenharmony_ci};
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic int dax_ccb_exec(struct dax_ctx *ctx, const char __user *buf,
2248c2ecf20Sopenharmony_ci			size_t count, loff_t *ppos);
2258c2ecf20Sopenharmony_cistatic int dax_ccb_info(u64 ca, struct ccb_info_result *info);
2268c2ecf20Sopenharmony_cistatic int dax_ccb_kill(u64 ca, u16 *kill_res);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic struct cdev c_dev;
2298c2ecf20Sopenharmony_cistatic struct class *cl;
2308c2ecf20Sopenharmony_cistatic dev_t first;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic int max_ccb_version;
2338c2ecf20Sopenharmony_cistatic int dax_debug;
2348c2ecf20Sopenharmony_cimodule_param(dax_debug, int, 0644);
2358c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dax_debug, "Debug flags");
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic int __init dax_attach(void)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	unsigned long dummy, hv_rv, major, minor, minor_requested, max_ccbs;
2408c2ecf20Sopenharmony_ci	struct mdesc_handle *hp = mdesc_grab();
2418c2ecf20Sopenharmony_ci	char *prop, *dax_name;
2428c2ecf20Sopenharmony_ci	bool found = false;
2438c2ecf20Sopenharmony_ci	int len, ret = 0;
2448c2ecf20Sopenharmony_ci	u64 pn;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (hp == NULL) {
2478c2ecf20Sopenharmony_ci		dax_err("Unable to grab mdesc");
2488c2ecf20Sopenharmony_ci		return -ENODEV;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	mdesc_for_each_node_by_name(hp, pn, "virtual-device") {
2528c2ecf20Sopenharmony_ci		prop = (char *)mdesc_get_property(hp, pn, "name", &len);
2538c2ecf20Sopenharmony_ci		if (prop == NULL)
2548c2ecf20Sopenharmony_ci			continue;
2558c2ecf20Sopenharmony_ci		if (strncmp(prop, "dax", strlen("dax")))
2568c2ecf20Sopenharmony_ci			continue;
2578c2ecf20Sopenharmony_ci		dax_dbg("Found node 0x%llx = %s", pn, prop);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci		prop = (char *)mdesc_get_property(hp, pn, "compatible", &len);
2608c2ecf20Sopenharmony_ci		if (prop == NULL)
2618c2ecf20Sopenharmony_ci			continue;
2628c2ecf20Sopenharmony_ci		dax_dbg("Found node 0x%llx = %s", pn, prop);
2638c2ecf20Sopenharmony_ci		found = true;
2648c2ecf20Sopenharmony_ci		break;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (!found) {
2688c2ecf20Sopenharmony_ci		dax_err("No DAX device found");
2698c2ecf20Sopenharmony_ci		ret = -ENODEV;
2708c2ecf20Sopenharmony_ci		goto done;
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	if (strncmp(prop, DAX2_STR, strlen(DAX2_STR)) == 0) {
2748c2ecf20Sopenharmony_ci		dax_name = DAX_NAME "2";
2758c2ecf20Sopenharmony_ci		major = DAX2_MAJOR;
2768c2ecf20Sopenharmony_ci		minor_requested = DAX2_MINOR;
2778c2ecf20Sopenharmony_ci		max_ccb_version = 1;
2788c2ecf20Sopenharmony_ci		dax_dbg("MD indicates DAX2 coprocessor");
2798c2ecf20Sopenharmony_ci	} else if (strncmp(prop, DAX1_STR, strlen(DAX1_STR)) == 0) {
2808c2ecf20Sopenharmony_ci		dax_name = DAX_NAME "1";
2818c2ecf20Sopenharmony_ci		major = DAX1_MAJOR;
2828c2ecf20Sopenharmony_ci		minor_requested = DAX1_MINOR;
2838c2ecf20Sopenharmony_ci		max_ccb_version = 0;
2848c2ecf20Sopenharmony_ci		dax_dbg("MD indicates DAX1 coprocessor");
2858c2ecf20Sopenharmony_ci	} else {
2868c2ecf20Sopenharmony_ci		dax_err("Unknown dax type: %s", prop);
2878c2ecf20Sopenharmony_ci		ret = -ENODEV;
2888c2ecf20Sopenharmony_ci		goto done;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	minor = minor_requested;
2928c2ecf20Sopenharmony_ci	dax_dbg("Registering DAX HV api with major %ld minor %ld", major,
2938c2ecf20Sopenharmony_ci		minor);
2948c2ecf20Sopenharmony_ci	if (sun4v_hvapi_register(HV_GRP_DAX, major, &minor)) {
2958c2ecf20Sopenharmony_ci		dax_err("hvapi_register failed");
2968c2ecf20Sopenharmony_ci		ret = -ENODEV;
2978c2ecf20Sopenharmony_ci		goto done;
2988c2ecf20Sopenharmony_ci	} else {
2998c2ecf20Sopenharmony_ci		dax_dbg("Max minor supported by HV = %ld (major %ld)", minor,
3008c2ecf20Sopenharmony_ci			major);
3018c2ecf20Sopenharmony_ci		minor = min(minor, minor_requested);
3028c2ecf20Sopenharmony_ci		dax_dbg("registered DAX major %ld minor %ld", major, minor);
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	/* submit a zero length ccb array to query coprocessor queue size */
3068c2ecf20Sopenharmony_ci	hv_rv = sun4v_ccb_submit(0, 0, HV_CCB_QUERY_CMD, 0, &max_ccbs, &dummy);
3078c2ecf20Sopenharmony_ci	if (hv_rv != 0) {
3088c2ecf20Sopenharmony_ci		dax_err("get_hwqueue_size failed with status=%ld and max_ccbs=%ld",
3098c2ecf20Sopenharmony_ci			hv_rv, max_ccbs);
3108c2ecf20Sopenharmony_ci		ret = -ENODEV;
3118c2ecf20Sopenharmony_ci		goto done;
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (max_ccbs != DAX_MAX_CCBS) {
3158c2ecf20Sopenharmony_ci		dax_err("HV reports unsupported max_ccbs=%ld", max_ccbs);
3168c2ecf20Sopenharmony_ci		ret = -ENODEV;
3178c2ecf20Sopenharmony_ci		goto done;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (alloc_chrdev_region(&first, 0, 1, DAX_NAME) < 0) {
3218c2ecf20Sopenharmony_ci		dax_err("alloc_chrdev_region failed");
3228c2ecf20Sopenharmony_ci		ret = -ENXIO;
3238c2ecf20Sopenharmony_ci		goto done;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	cl = class_create(THIS_MODULE, DAX_NAME);
3278c2ecf20Sopenharmony_ci	if (IS_ERR(cl)) {
3288c2ecf20Sopenharmony_ci		dax_err("class_create failed");
3298c2ecf20Sopenharmony_ci		ret = PTR_ERR(cl);
3308c2ecf20Sopenharmony_ci		goto class_error;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	if (device_create(cl, NULL, first, NULL, dax_name) == NULL) {
3348c2ecf20Sopenharmony_ci		dax_err("device_create failed");
3358c2ecf20Sopenharmony_ci		ret = -ENXIO;
3368c2ecf20Sopenharmony_ci		goto device_error;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	cdev_init(&c_dev, &dax_fops);
3408c2ecf20Sopenharmony_ci	if (cdev_add(&c_dev, first, 1) == -1) {
3418c2ecf20Sopenharmony_ci		dax_err("cdev_add failed");
3428c2ecf20Sopenharmony_ci		ret = -ENXIO;
3438c2ecf20Sopenharmony_ci		goto cdev_error;
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	pr_info("Attached DAX module\n");
3478c2ecf20Sopenharmony_ci	goto done;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cicdev_error:
3508c2ecf20Sopenharmony_ci	device_destroy(cl, first);
3518c2ecf20Sopenharmony_cidevice_error:
3528c2ecf20Sopenharmony_ci	class_destroy(cl);
3538c2ecf20Sopenharmony_ciclass_error:
3548c2ecf20Sopenharmony_ci	unregister_chrdev_region(first, 1);
3558c2ecf20Sopenharmony_cidone:
3568c2ecf20Sopenharmony_ci	mdesc_release(hp);
3578c2ecf20Sopenharmony_ci	return ret;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_cimodule_init(dax_attach);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic void __exit dax_detach(void)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	pr_info("Cleaning up DAX module\n");
3648c2ecf20Sopenharmony_ci	cdev_del(&c_dev);
3658c2ecf20Sopenharmony_ci	device_destroy(cl, first);
3668c2ecf20Sopenharmony_ci	class_destroy(cl);
3678c2ecf20Sopenharmony_ci	unregister_chrdev_region(first, 1);
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_cimodule_exit(dax_detach);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci/* map completion area */
3728c2ecf20Sopenharmony_cistatic int dax_devmap(struct file *f, struct vm_area_struct *vma)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	struct dax_ctx *ctx = (struct dax_ctx *)f->private_data;
3758c2ecf20Sopenharmony_ci	size_t len = vma->vm_end - vma->vm_start;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	dax_dbg("len=0x%lx, flags=0x%lx", len, vma->vm_flags);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (ctx->owner != current) {
3808c2ecf20Sopenharmony_ci		dax_dbg("devmap called from wrong thread");
3818c2ecf20Sopenharmony_ci		return -EINVAL;
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	if (len != DAX_MMAP_LEN) {
3858c2ecf20Sopenharmony_ci		dax_dbg("len(%lu) != DAX_MMAP_LEN(%d)", len, DAX_MMAP_LEN);
3868c2ecf20Sopenharmony_ci		return -EINVAL;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/* completion area is mapped read-only for user */
3908c2ecf20Sopenharmony_ci	if (vma->vm_flags & VM_WRITE)
3918c2ecf20Sopenharmony_ci		return -EPERM;
3928c2ecf20Sopenharmony_ci	vma->vm_flags &= ~VM_MAYWRITE;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (remap_pfn_range(vma, vma->vm_start, ctx->ca_buf_ra >> PAGE_SHIFT,
3958c2ecf20Sopenharmony_ci			    len, vma->vm_page_prot))
3968c2ecf20Sopenharmony_ci		return -EAGAIN;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	dax_dbg("mmapped completion area at uva 0x%lx", vma->vm_start);
3998c2ecf20Sopenharmony_ci	return 0;
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci/* Unlock user pages. Called during dequeue or device close */
4038c2ecf20Sopenharmony_cistatic void dax_unlock_pages(struct dax_ctx *ctx, int ccb_index, int nelem)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	int i, j;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	for (i = ccb_index; i < ccb_index + nelem; i++) {
4088c2ecf20Sopenharmony_ci		for (j = 0; j < NUM_STREAM_TYPES; j++) {
4098c2ecf20Sopenharmony_ci			struct page *p = ctx->pages[i][j];
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci			if (p) {
4128c2ecf20Sopenharmony_ci				dax_dbg("freeing page %p", p);
4138c2ecf20Sopenharmony_ci				unpin_user_pages_dirty_lock(&p, 1, j == OUT);
4148c2ecf20Sopenharmony_ci				ctx->pages[i][j] = NULL;
4158c2ecf20Sopenharmony_ci			}
4168c2ecf20Sopenharmony_ci		}
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic int dax_lock_page(void *va, struct page **p)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	int ret;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	dax_dbg("uva %p", va);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	ret = pin_user_pages_fast((unsigned long)va, 1, FOLL_WRITE, p);
4278c2ecf20Sopenharmony_ci	if (ret == 1) {
4288c2ecf20Sopenharmony_ci		dax_dbg("locked page %p, for VA %p", *p, va);
4298c2ecf20Sopenharmony_ci		return 0;
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	dax_dbg("pin_user_pages failed, va=%p, ret=%d", va, ret);
4338c2ecf20Sopenharmony_ci	return -1;
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cistatic int dax_lock_pages(struct dax_ctx *ctx, int idx,
4378c2ecf20Sopenharmony_ci			  int nelem, u64 *err_va)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	int i;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	for (i = 0; i < nelem; i++) {
4428c2ecf20Sopenharmony_ci		struct dax_ccb *ccbp = &ctx->ccb_buf[i];
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci		/*
4458c2ecf20Sopenharmony_ci		 * For each address in the CCB whose type is virtual,
4468c2ecf20Sopenharmony_ci		 * lock the page and change the type to virtual alternate
4478c2ecf20Sopenharmony_ci		 * context. On error, return the offending address in
4488c2ecf20Sopenharmony_ci		 * err_va.
4498c2ecf20Sopenharmony_ci		 */
4508c2ecf20Sopenharmony_ci		if (ccbp->hdr.out_addr_type == DAX_ADDR_TYPE_VA) {
4518c2ecf20Sopenharmony_ci			dax_dbg("output");
4528c2ecf20Sopenharmony_ci			if (dax_lock_page(ccbp->out,
4538c2ecf20Sopenharmony_ci					  &ctx->pages[i + idx][OUT]) != 0) {
4548c2ecf20Sopenharmony_ci				*err_va = (u64)ccbp->out;
4558c2ecf20Sopenharmony_ci				goto error;
4568c2ecf20Sopenharmony_ci			}
4578c2ecf20Sopenharmony_ci			ccbp->hdr.out_addr_type = DAX_ADDR_TYPE_VA_ALT;
4588c2ecf20Sopenharmony_ci		}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		if (ccbp->hdr.pri_addr_type == DAX_ADDR_TYPE_VA) {
4618c2ecf20Sopenharmony_ci			dax_dbg("input");
4628c2ecf20Sopenharmony_ci			if (dax_lock_page(ccbp->pri,
4638c2ecf20Sopenharmony_ci					  &ctx->pages[i + idx][PRI]) != 0) {
4648c2ecf20Sopenharmony_ci				*err_va = (u64)ccbp->pri;
4658c2ecf20Sopenharmony_ci				goto error;
4668c2ecf20Sopenharmony_ci			}
4678c2ecf20Sopenharmony_ci			ccbp->hdr.pri_addr_type = DAX_ADDR_TYPE_VA_ALT;
4688c2ecf20Sopenharmony_ci		}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci		if (ccbp->hdr.sec_addr_type == DAX_ADDR_TYPE_VA) {
4718c2ecf20Sopenharmony_ci			dax_dbg("sec input");
4728c2ecf20Sopenharmony_ci			if (dax_lock_page(ccbp->sec,
4738c2ecf20Sopenharmony_ci					  &ctx->pages[i + idx][SEC]) != 0) {
4748c2ecf20Sopenharmony_ci				*err_va = (u64)ccbp->sec;
4758c2ecf20Sopenharmony_ci				goto error;
4768c2ecf20Sopenharmony_ci			}
4778c2ecf20Sopenharmony_ci			ccbp->hdr.sec_addr_type = DAX_ADDR_TYPE_VA_ALT;
4788c2ecf20Sopenharmony_ci		}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		if (ccbp->hdr.table_addr_type == DAX_ADDR_TYPE_VA) {
4818c2ecf20Sopenharmony_ci			dax_dbg("tbl");
4828c2ecf20Sopenharmony_ci			if (dax_lock_page(ccbp->tbl,
4838c2ecf20Sopenharmony_ci					  &ctx->pages[i + idx][TBL]) != 0) {
4848c2ecf20Sopenharmony_ci				*err_va = (u64)ccbp->tbl;
4858c2ecf20Sopenharmony_ci				goto error;
4868c2ecf20Sopenharmony_ci			}
4878c2ecf20Sopenharmony_ci			ccbp->hdr.table_addr_type = DAX_ADDR_TYPE_VA_ALT;
4888c2ecf20Sopenharmony_ci		}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci		/* skip over 2nd 64 bytes of long CCB */
4918c2ecf20Sopenharmony_ci		if (ccbp->hdr.longccb)
4928c2ecf20Sopenharmony_ci			i++;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci	return DAX_SUBMIT_OK;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cierror:
4978c2ecf20Sopenharmony_ci	dax_unlock_pages(ctx, idx, nelem);
4988c2ecf20Sopenharmony_ci	return DAX_SUBMIT_ERR_NOACCESS;
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic void dax_ccb_wait(struct dax_ctx *ctx, int idx)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	int ret, nretries;
5048c2ecf20Sopenharmony_ci	u16 kill_res;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	dax_dbg("idx=%d", idx);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	for (nretries = 0; nretries < DAX_CCB_RETRIES; nretries++) {
5098c2ecf20Sopenharmony_ci		if (ctx->ca_buf[idx].status == CCA_STAT_NOT_COMPLETED)
5108c2ecf20Sopenharmony_ci			udelay(DAX_CCB_USEC);
5118c2ecf20Sopenharmony_ci		else
5128c2ecf20Sopenharmony_ci			return;
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci	dax_dbg("ctx (%p): CCB[%d] timed out, wait usec=%d, retries=%d. Killing ccb",
5158c2ecf20Sopenharmony_ci		(void *)ctx, idx, DAX_CCB_USEC, DAX_CCB_RETRIES);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	ret = dax_ccb_kill(ctx->ca_buf_ra + idx * sizeof(struct dax_cca),
5188c2ecf20Sopenharmony_ci			   &kill_res);
5198c2ecf20Sopenharmony_ci	dax_dbg("Kill CCB[%d] %s", idx, ret ? "failed" : "succeeded");
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_cistatic int dax_close(struct inode *ino, struct file *f)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	struct dax_ctx *ctx = (struct dax_ctx *)f->private_data;
5258c2ecf20Sopenharmony_ci	int i;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	f->private_data = NULL;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	for (i = 0; i < DAX_CA_ELEMS; i++) {
5308c2ecf20Sopenharmony_ci		if (ctx->ca_buf[i].status == CCA_STAT_NOT_COMPLETED) {
5318c2ecf20Sopenharmony_ci			dax_dbg("CCB[%d] not completed", i);
5328c2ecf20Sopenharmony_ci			dax_ccb_wait(ctx, i);
5338c2ecf20Sopenharmony_ci		}
5348c2ecf20Sopenharmony_ci		dax_unlock_pages(ctx, i, 1);
5358c2ecf20Sopenharmony_ci	}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	kfree(ctx->ccb_buf);
5388c2ecf20Sopenharmony_ci	kfree(ctx->ca_buf);
5398c2ecf20Sopenharmony_ci	dax_stat_dbg("CCBs: %d good, %d bad", ctx->ccb_count, ctx->fail_count);
5408c2ecf20Sopenharmony_ci	kfree(ctx);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	return 0;
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic ssize_t dax_read(struct file *f, char __user *buf,
5468c2ecf20Sopenharmony_ci			size_t count, loff_t *ppos)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	struct dax_ctx *ctx = f->private_data;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (ctx->client != current)
5518c2ecf20Sopenharmony_ci		return -EUSERS;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	ctx->client = NULL;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	if (count != sizeof(union ccb_result))
5568c2ecf20Sopenharmony_ci		return -EINVAL;
5578c2ecf20Sopenharmony_ci	if (copy_to_user(buf, &ctx->result, sizeof(union ccb_result)))
5588c2ecf20Sopenharmony_ci		return -EFAULT;
5598c2ecf20Sopenharmony_ci	return count;
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_cistatic ssize_t dax_write(struct file *f, const char __user *buf,
5638c2ecf20Sopenharmony_ci			 size_t count, loff_t *ppos)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	struct dax_ctx *ctx = f->private_data;
5668c2ecf20Sopenharmony_ci	struct dax_command hdr;
5678c2ecf20Sopenharmony_ci	unsigned long ca;
5688c2ecf20Sopenharmony_ci	int i, idx, ret;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if (ctx->client != NULL)
5718c2ecf20Sopenharmony_ci		return -EINVAL;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	if (count == 0 || count > DAX_MAX_CCBS * sizeof(struct dax_ccb))
5748c2ecf20Sopenharmony_ci		return -EINVAL;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	if (count % sizeof(struct dax_ccb) == 0)
5778c2ecf20Sopenharmony_ci		return dax_ccb_exec(ctx, buf, count, ppos); /* CCB EXEC */
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	if (count != sizeof(struct dax_command))
5808c2ecf20Sopenharmony_ci		return -EINVAL;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	/* immediate command */
5838c2ecf20Sopenharmony_ci	if (ctx->owner != current)
5848c2ecf20Sopenharmony_ci		return -EUSERS;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	if (copy_from_user(&hdr, buf, sizeof(hdr)))
5878c2ecf20Sopenharmony_ci		return -EFAULT;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	ca = ctx->ca_buf_ra + hdr.ca_offset;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	switch (hdr.command) {
5928c2ecf20Sopenharmony_ci	case CCB_KILL:
5938c2ecf20Sopenharmony_ci		if (hdr.ca_offset >= DAX_MMAP_LEN) {
5948c2ecf20Sopenharmony_ci			dax_dbg("invalid ca_offset (%d) >= ca_buflen (%d)",
5958c2ecf20Sopenharmony_ci				hdr.ca_offset, DAX_MMAP_LEN);
5968c2ecf20Sopenharmony_ci			return -EINVAL;
5978c2ecf20Sopenharmony_ci		}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci		ret = dax_ccb_kill(ca, &ctx->result.kill.action);
6008c2ecf20Sopenharmony_ci		if (ret != 0) {
6018c2ecf20Sopenharmony_ci			dax_dbg("dax_ccb_kill failed (ret=%d)", ret);
6028c2ecf20Sopenharmony_ci			return ret;
6038c2ecf20Sopenharmony_ci		}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci		dax_info_dbg("killed (ca_offset %d)", hdr.ca_offset);
6068c2ecf20Sopenharmony_ci		idx = hdr.ca_offset / sizeof(struct dax_cca);
6078c2ecf20Sopenharmony_ci		ctx->ca_buf[idx].status = CCA_STAT_KILLED;
6088c2ecf20Sopenharmony_ci		ctx->ca_buf[idx].err = CCA_ERR_KILLED;
6098c2ecf20Sopenharmony_ci		ctx->client = current;
6108c2ecf20Sopenharmony_ci		return count;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	case CCB_INFO:
6138c2ecf20Sopenharmony_ci		if (hdr.ca_offset >= DAX_MMAP_LEN) {
6148c2ecf20Sopenharmony_ci			dax_dbg("invalid ca_offset (%d) >= ca_buflen (%d)",
6158c2ecf20Sopenharmony_ci				hdr.ca_offset, DAX_MMAP_LEN);
6168c2ecf20Sopenharmony_ci			return -EINVAL;
6178c2ecf20Sopenharmony_ci		}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci		ret = dax_ccb_info(ca, &ctx->result.info);
6208c2ecf20Sopenharmony_ci		if (ret != 0) {
6218c2ecf20Sopenharmony_ci			dax_dbg("dax_ccb_info failed (ret=%d)", ret);
6228c2ecf20Sopenharmony_ci			return ret;
6238c2ecf20Sopenharmony_ci		}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci		dax_info_dbg("info succeeded on ca_offset %d", hdr.ca_offset);
6268c2ecf20Sopenharmony_ci		ctx->client = current;
6278c2ecf20Sopenharmony_ci		return count;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	case CCB_DEQUEUE:
6308c2ecf20Sopenharmony_ci		for (i = 0; i < DAX_CA_ELEMS; i++) {
6318c2ecf20Sopenharmony_ci			if (ctx->ca_buf[i].status !=
6328c2ecf20Sopenharmony_ci			    CCA_STAT_NOT_COMPLETED)
6338c2ecf20Sopenharmony_ci				dax_unlock_pages(ctx, i, 1);
6348c2ecf20Sopenharmony_ci		}
6358c2ecf20Sopenharmony_ci		return count;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	default:
6388c2ecf20Sopenharmony_ci		return -EINVAL;
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_cistatic int dax_open(struct inode *inode, struct file *f)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	struct dax_ctx *ctx = NULL;
6458c2ecf20Sopenharmony_ci	int i;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
6488c2ecf20Sopenharmony_ci	if (ctx == NULL)
6498c2ecf20Sopenharmony_ci		goto done;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	ctx->ccb_buf = kcalloc(DAX_MAX_CCBS, sizeof(struct dax_ccb),
6528c2ecf20Sopenharmony_ci			       GFP_KERNEL);
6538c2ecf20Sopenharmony_ci	if (ctx->ccb_buf == NULL)
6548c2ecf20Sopenharmony_ci		goto done;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	ctx->ccb_buf_ra = virt_to_phys(ctx->ccb_buf);
6578c2ecf20Sopenharmony_ci	dax_dbg("ctx->ccb_buf=0x%p, ccb_buf_ra=0x%llx",
6588c2ecf20Sopenharmony_ci		(void *)ctx->ccb_buf, ctx->ccb_buf_ra);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	/* allocate CCB completion area buffer */
6618c2ecf20Sopenharmony_ci	ctx->ca_buf = kzalloc(DAX_MMAP_LEN, GFP_KERNEL);
6628c2ecf20Sopenharmony_ci	if (ctx->ca_buf == NULL)
6638c2ecf20Sopenharmony_ci		goto alloc_error;
6648c2ecf20Sopenharmony_ci	for (i = 0; i < DAX_CA_ELEMS; i++)
6658c2ecf20Sopenharmony_ci		ctx->ca_buf[i].status = CCA_STAT_COMPLETED;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	ctx->ca_buf_ra = virt_to_phys(ctx->ca_buf);
6688c2ecf20Sopenharmony_ci	dax_dbg("ctx=0x%p, ctx->ca_buf=0x%p, ca_buf_ra=0x%llx",
6698c2ecf20Sopenharmony_ci		(void *)ctx, (void *)ctx->ca_buf, ctx->ca_buf_ra);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	ctx->owner = current;
6728c2ecf20Sopenharmony_ci	f->private_data = ctx;
6738c2ecf20Sopenharmony_ci	return 0;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_cialloc_error:
6768c2ecf20Sopenharmony_ci	kfree(ctx->ccb_buf);
6778c2ecf20Sopenharmony_cidone:
6788c2ecf20Sopenharmony_ci	kfree(ctx);
6798c2ecf20Sopenharmony_ci	return -ENOMEM;
6808c2ecf20Sopenharmony_ci}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_cistatic char *dax_hv_errno(unsigned long hv_ret, int *ret)
6838c2ecf20Sopenharmony_ci{
6848c2ecf20Sopenharmony_ci	switch (hv_ret) {
6858c2ecf20Sopenharmony_ci	case HV_EBADALIGN:
6868c2ecf20Sopenharmony_ci		*ret = -EFAULT;
6878c2ecf20Sopenharmony_ci		return "HV_EBADALIGN";
6888c2ecf20Sopenharmony_ci	case HV_ENORADDR:
6898c2ecf20Sopenharmony_ci		*ret = -EFAULT;
6908c2ecf20Sopenharmony_ci		return "HV_ENORADDR";
6918c2ecf20Sopenharmony_ci	case HV_EINVAL:
6928c2ecf20Sopenharmony_ci		*ret = -EINVAL;
6938c2ecf20Sopenharmony_ci		return "HV_EINVAL";
6948c2ecf20Sopenharmony_ci	case HV_EWOULDBLOCK:
6958c2ecf20Sopenharmony_ci		*ret = -EAGAIN;
6968c2ecf20Sopenharmony_ci		return "HV_EWOULDBLOCK";
6978c2ecf20Sopenharmony_ci	case HV_ENOACCESS:
6988c2ecf20Sopenharmony_ci		*ret = -EPERM;
6998c2ecf20Sopenharmony_ci		return "HV_ENOACCESS";
7008c2ecf20Sopenharmony_ci	default:
7018c2ecf20Sopenharmony_ci		break;
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	*ret = -EIO;
7058c2ecf20Sopenharmony_ci	return "UNKNOWN";
7068c2ecf20Sopenharmony_ci}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_cistatic int dax_ccb_kill(u64 ca, u16 *kill_res)
7098c2ecf20Sopenharmony_ci{
7108c2ecf20Sopenharmony_ci	unsigned long hv_ret;
7118c2ecf20Sopenharmony_ci	int count, ret = 0;
7128c2ecf20Sopenharmony_ci	char *err_str;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	for (count = 0; count < DAX_CCB_RETRIES; count++) {
7158c2ecf20Sopenharmony_ci		dax_dbg("attempting kill on ca_ra 0x%llx", ca);
7168c2ecf20Sopenharmony_ci		hv_ret = sun4v_ccb_kill(ca, kill_res);
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci		if (hv_ret == HV_EOK) {
7198c2ecf20Sopenharmony_ci			dax_info_dbg("HV_EOK (ca_ra 0x%llx): %d", ca,
7208c2ecf20Sopenharmony_ci				     *kill_res);
7218c2ecf20Sopenharmony_ci		} else {
7228c2ecf20Sopenharmony_ci			err_str = dax_hv_errno(hv_ret, &ret);
7238c2ecf20Sopenharmony_ci			dax_dbg("%s (ca_ra 0x%llx)", err_str, ca);
7248c2ecf20Sopenharmony_ci		}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci		if (ret != -EAGAIN)
7278c2ecf20Sopenharmony_ci			return ret;
7288c2ecf20Sopenharmony_ci		dax_info_dbg("ccb_kill count = %d", count);
7298c2ecf20Sopenharmony_ci		udelay(DAX_CCB_USEC);
7308c2ecf20Sopenharmony_ci	}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	return -EAGAIN;
7338c2ecf20Sopenharmony_ci}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_cistatic int dax_ccb_info(u64 ca, struct ccb_info_result *info)
7368c2ecf20Sopenharmony_ci{
7378c2ecf20Sopenharmony_ci	unsigned long hv_ret;
7388c2ecf20Sopenharmony_ci	char *err_str;
7398c2ecf20Sopenharmony_ci	int ret = 0;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	dax_dbg("attempting info on ca_ra 0x%llx", ca);
7428c2ecf20Sopenharmony_ci	hv_ret = sun4v_ccb_info(ca, info);
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	if (hv_ret == HV_EOK) {
7458c2ecf20Sopenharmony_ci		dax_info_dbg("HV_EOK (ca_ra 0x%llx): %d", ca, info->state);
7468c2ecf20Sopenharmony_ci		if (info->state == DAX_CCB_ENQUEUED) {
7478c2ecf20Sopenharmony_ci			dax_info_dbg("dax_unit %d, queue_num %d, queue_pos %d",
7488c2ecf20Sopenharmony_ci				     info->inst_num, info->q_num, info->q_pos);
7498c2ecf20Sopenharmony_ci		}
7508c2ecf20Sopenharmony_ci	} else {
7518c2ecf20Sopenharmony_ci		err_str = dax_hv_errno(hv_ret, &ret);
7528c2ecf20Sopenharmony_ci		dax_dbg("%s (ca_ra 0x%llx)", err_str, ca);
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	return ret;
7568c2ecf20Sopenharmony_ci}
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_cistatic void dax_prt_ccbs(struct dax_ccb *ccb, int nelem)
7598c2ecf20Sopenharmony_ci{
7608c2ecf20Sopenharmony_ci	int i, j;
7618c2ecf20Sopenharmony_ci	u64 *ccbp;
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	dax_dbg("ccb buffer:");
7648c2ecf20Sopenharmony_ci	for (i = 0; i < nelem; i++) {
7658c2ecf20Sopenharmony_ci		ccbp = (u64 *)&ccb[i];
7668c2ecf20Sopenharmony_ci		dax_dbg(" %sccb[%d]", ccb[i].hdr.longccb ? "long " : "",  i);
7678c2ecf20Sopenharmony_ci		for (j = 0; j < 8; j++)
7688c2ecf20Sopenharmony_ci			dax_dbg("\tccb[%d].dwords[%d]=0x%llx",
7698c2ecf20Sopenharmony_ci				i, j, *(ccbp + j));
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci/*
7748c2ecf20Sopenharmony_ci * Validates user CCB content.  Also sets completion address and address types
7758c2ecf20Sopenharmony_ci * for all addresses contained in CCB.
7768c2ecf20Sopenharmony_ci */
7778c2ecf20Sopenharmony_cistatic int dax_preprocess_usr_ccbs(struct dax_ctx *ctx, int idx, int nelem)
7788c2ecf20Sopenharmony_ci{
7798c2ecf20Sopenharmony_ci	int i;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	/*
7828c2ecf20Sopenharmony_ci	 * The user is not allowed to specify real address types in
7838c2ecf20Sopenharmony_ci	 * the CCB header.  This must be enforced by the kernel before
7848c2ecf20Sopenharmony_ci	 * submitting the CCBs to HV.  The only allowed values for all
7858c2ecf20Sopenharmony_ci	 * address fields are VA or IMM
7868c2ecf20Sopenharmony_ci	 */
7878c2ecf20Sopenharmony_ci	for (i = 0; i < nelem; i++) {
7888c2ecf20Sopenharmony_ci		struct dax_ccb *ccbp = &ctx->ccb_buf[i];
7898c2ecf20Sopenharmony_ci		unsigned long ca_offset;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci		if (ccbp->hdr.ccb_version > max_ccb_version)
7928c2ecf20Sopenharmony_ci			return DAX_SUBMIT_ERR_CCB_INVAL;
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci		switch (ccbp->hdr.opcode) {
7958c2ecf20Sopenharmony_ci		case DAX_OP_SYNC_NOP:
7968c2ecf20Sopenharmony_ci		case DAX_OP_EXTRACT:
7978c2ecf20Sopenharmony_ci		case DAX_OP_SCAN_VALUE:
7988c2ecf20Sopenharmony_ci		case DAX_OP_SCAN_RANGE:
7998c2ecf20Sopenharmony_ci		case DAX_OP_TRANSLATE:
8008c2ecf20Sopenharmony_ci		case DAX_OP_SCAN_VALUE | DAX_OP_INVERT:
8018c2ecf20Sopenharmony_ci		case DAX_OP_SCAN_RANGE | DAX_OP_INVERT:
8028c2ecf20Sopenharmony_ci		case DAX_OP_TRANSLATE | DAX_OP_INVERT:
8038c2ecf20Sopenharmony_ci		case DAX_OP_SELECT:
8048c2ecf20Sopenharmony_ci			break;
8058c2ecf20Sopenharmony_ci		default:
8068c2ecf20Sopenharmony_ci			return DAX_SUBMIT_ERR_CCB_INVAL;
8078c2ecf20Sopenharmony_ci		}
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci		if (ccbp->hdr.out_addr_type != DAX_ADDR_TYPE_VA &&
8108c2ecf20Sopenharmony_ci		    ccbp->hdr.out_addr_type != DAX_ADDR_TYPE_NONE) {
8118c2ecf20Sopenharmony_ci			dax_dbg("invalid out_addr_type in user CCB[%d]", i);
8128c2ecf20Sopenharmony_ci			return DAX_SUBMIT_ERR_CCB_INVAL;
8138c2ecf20Sopenharmony_ci		}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci		if (ccbp->hdr.pri_addr_type != DAX_ADDR_TYPE_VA &&
8168c2ecf20Sopenharmony_ci		    ccbp->hdr.pri_addr_type != DAX_ADDR_TYPE_NONE) {
8178c2ecf20Sopenharmony_ci			dax_dbg("invalid pri_addr_type in user CCB[%d]", i);
8188c2ecf20Sopenharmony_ci			return DAX_SUBMIT_ERR_CCB_INVAL;
8198c2ecf20Sopenharmony_ci		}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci		if (ccbp->hdr.sec_addr_type != DAX_ADDR_TYPE_VA &&
8228c2ecf20Sopenharmony_ci		    ccbp->hdr.sec_addr_type != DAX_ADDR_TYPE_NONE) {
8238c2ecf20Sopenharmony_ci			dax_dbg("invalid sec_addr_type in user CCB[%d]", i);
8248c2ecf20Sopenharmony_ci			return DAX_SUBMIT_ERR_CCB_INVAL;
8258c2ecf20Sopenharmony_ci		}
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci		if (ccbp->hdr.table_addr_type != DAX_ADDR_TYPE_VA &&
8288c2ecf20Sopenharmony_ci		    ccbp->hdr.table_addr_type != DAX_ADDR_TYPE_NONE) {
8298c2ecf20Sopenharmony_ci			dax_dbg("invalid table_addr_type in user CCB[%d]", i);
8308c2ecf20Sopenharmony_ci			return DAX_SUBMIT_ERR_CCB_INVAL;
8318c2ecf20Sopenharmony_ci		}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci		/* set completion (real) address and address type */
8348c2ecf20Sopenharmony_ci		ccbp->hdr.cca_addr_type = DAX_ADDR_TYPE_RA;
8358c2ecf20Sopenharmony_ci		ca_offset = (idx + i) * sizeof(struct dax_cca);
8368c2ecf20Sopenharmony_ci		ccbp->ca = (void *)ctx->ca_buf_ra + ca_offset;
8378c2ecf20Sopenharmony_ci		memset(&ctx->ca_buf[idx + i], 0, sizeof(struct dax_cca));
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci		dax_dbg("ccb[%d]=%p, ca_offset=0x%lx, compl RA=0x%llx",
8408c2ecf20Sopenharmony_ci			i, ccbp, ca_offset, ctx->ca_buf_ra + ca_offset);
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci		/* skip over 2nd 64 bytes of long CCB */
8438c2ecf20Sopenharmony_ci		if (ccbp->hdr.longccb)
8448c2ecf20Sopenharmony_ci			i++;
8458c2ecf20Sopenharmony_ci	}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	return DAX_SUBMIT_OK;
8488c2ecf20Sopenharmony_ci}
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_cistatic int dax_ccb_exec(struct dax_ctx *ctx, const char __user *buf,
8518c2ecf20Sopenharmony_ci			size_t count, loff_t *ppos)
8528c2ecf20Sopenharmony_ci{
8538c2ecf20Sopenharmony_ci	unsigned long accepted_len, hv_rv;
8548c2ecf20Sopenharmony_ci	int i, idx, nccbs, naccepted;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	ctx->client = current;
8578c2ecf20Sopenharmony_ci	idx = *ppos;
8588c2ecf20Sopenharmony_ci	nccbs = count / sizeof(struct dax_ccb);
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	if (ctx->owner != current) {
8618c2ecf20Sopenharmony_ci		dax_dbg("wrong thread");
8628c2ecf20Sopenharmony_ci		ctx->result.exec.status = DAX_SUBMIT_ERR_THR_INIT;
8638c2ecf20Sopenharmony_ci		return 0;
8648c2ecf20Sopenharmony_ci	}
8658c2ecf20Sopenharmony_ci	dax_dbg("args: ccb_buf_len=%ld, idx=%d", count, idx);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	/* for given index and length, verify ca_buf range exists */
8688c2ecf20Sopenharmony_ci	if (idx < 0 || idx > (DAX_CA_ELEMS - nccbs)) {
8698c2ecf20Sopenharmony_ci		ctx->result.exec.status = DAX_SUBMIT_ERR_NO_CA_AVAIL;
8708c2ecf20Sopenharmony_ci		return 0;
8718c2ecf20Sopenharmony_ci	}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	/*
8748c2ecf20Sopenharmony_ci	 * Copy CCBs into kernel buffer to prevent modification by the
8758c2ecf20Sopenharmony_ci	 * user in between validation and submission.
8768c2ecf20Sopenharmony_ci	 */
8778c2ecf20Sopenharmony_ci	if (copy_from_user(ctx->ccb_buf, buf, count)) {
8788c2ecf20Sopenharmony_ci		dax_dbg("copyin of user CCB buffer failed");
8798c2ecf20Sopenharmony_ci		ctx->result.exec.status = DAX_SUBMIT_ERR_CCB_ARR_MMU_MISS;
8808c2ecf20Sopenharmony_ci		return 0;
8818c2ecf20Sopenharmony_ci	}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	/* check to see if ca_buf[idx] .. ca_buf[idx + nccbs] are available */
8848c2ecf20Sopenharmony_ci	for (i = idx; i < idx + nccbs; i++) {
8858c2ecf20Sopenharmony_ci		if (ctx->ca_buf[i].status == CCA_STAT_NOT_COMPLETED) {
8868c2ecf20Sopenharmony_ci			dax_dbg("CA range not available, dequeue needed");
8878c2ecf20Sopenharmony_ci			ctx->result.exec.status = DAX_SUBMIT_ERR_NO_CA_AVAIL;
8888c2ecf20Sopenharmony_ci			return 0;
8898c2ecf20Sopenharmony_ci		}
8908c2ecf20Sopenharmony_ci	}
8918c2ecf20Sopenharmony_ci	dax_unlock_pages(ctx, idx, nccbs);
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	ctx->result.exec.status = dax_preprocess_usr_ccbs(ctx, idx, nccbs);
8948c2ecf20Sopenharmony_ci	if (ctx->result.exec.status != DAX_SUBMIT_OK)
8958c2ecf20Sopenharmony_ci		return 0;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	ctx->result.exec.status = dax_lock_pages(ctx, idx, nccbs,
8988c2ecf20Sopenharmony_ci						 &ctx->result.exec.status_data);
8998c2ecf20Sopenharmony_ci	if (ctx->result.exec.status != DAX_SUBMIT_OK)
9008c2ecf20Sopenharmony_ci		return 0;
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	if (dax_debug & DAX_DBG_FLG_BASIC)
9038c2ecf20Sopenharmony_ci		dax_prt_ccbs(ctx->ccb_buf, nccbs);
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	hv_rv = sun4v_ccb_submit(ctx->ccb_buf_ra, count,
9068c2ecf20Sopenharmony_ci				 HV_CCB_QUERY_CMD | HV_CCB_VA_SECONDARY, 0,
9078c2ecf20Sopenharmony_ci				 &accepted_len, &ctx->result.exec.status_data);
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	switch (hv_rv) {
9108c2ecf20Sopenharmony_ci	case HV_EOK:
9118c2ecf20Sopenharmony_ci		/*
9128c2ecf20Sopenharmony_ci		 * Hcall succeeded with no errors but the accepted
9138c2ecf20Sopenharmony_ci		 * length may be less than the requested length.  The
9148c2ecf20Sopenharmony_ci		 * only way the driver can resubmit the remainder is
9158c2ecf20Sopenharmony_ci		 * to wait for completion of the submitted CCBs since
9168c2ecf20Sopenharmony_ci		 * there is no way to guarantee the ordering semantics
9178c2ecf20Sopenharmony_ci		 * required by the client applications.  Therefore we
9188c2ecf20Sopenharmony_ci		 * let the user library deal with resubmissions.
9198c2ecf20Sopenharmony_ci		 */
9208c2ecf20Sopenharmony_ci		ctx->result.exec.status = DAX_SUBMIT_OK;
9218c2ecf20Sopenharmony_ci		break;
9228c2ecf20Sopenharmony_ci	case HV_EWOULDBLOCK:
9238c2ecf20Sopenharmony_ci		/*
9248c2ecf20Sopenharmony_ci		 * This is a transient HV API error. The user library
9258c2ecf20Sopenharmony_ci		 * can retry.
9268c2ecf20Sopenharmony_ci		 */
9278c2ecf20Sopenharmony_ci		dax_dbg("hcall returned HV_EWOULDBLOCK");
9288c2ecf20Sopenharmony_ci		ctx->result.exec.status = DAX_SUBMIT_ERR_WOULDBLOCK;
9298c2ecf20Sopenharmony_ci		break;
9308c2ecf20Sopenharmony_ci	case HV_ENOMAP:
9318c2ecf20Sopenharmony_ci		/*
9328c2ecf20Sopenharmony_ci		 * HV was unable to translate a VA. The VA it could
9338c2ecf20Sopenharmony_ci		 * not translate is returned in the status_data param.
9348c2ecf20Sopenharmony_ci		 */
9358c2ecf20Sopenharmony_ci		dax_dbg("hcall returned HV_ENOMAP");
9368c2ecf20Sopenharmony_ci		ctx->result.exec.status = DAX_SUBMIT_ERR_NOMAP;
9378c2ecf20Sopenharmony_ci		break;
9388c2ecf20Sopenharmony_ci	case HV_EINVAL:
9398c2ecf20Sopenharmony_ci		/*
9408c2ecf20Sopenharmony_ci		 * This is the result of an invalid user CCB as HV is
9418c2ecf20Sopenharmony_ci		 * validating some of the user CCB fields.  Pass this
9428c2ecf20Sopenharmony_ci		 * error back to the user. There is no supporting info
9438c2ecf20Sopenharmony_ci		 * to isolate the invalid field.
9448c2ecf20Sopenharmony_ci		 */
9458c2ecf20Sopenharmony_ci		dax_dbg("hcall returned HV_EINVAL");
9468c2ecf20Sopenharmony_ci		ctx->result.exec.status = DAX_SUBMIT_ERR_CCB_INVAL;
9478c2ecf20Sopenharmony_ci		break;
9488c2ecf20Sopenharmony_ci	case HV_ENOACCESS:
9498c2ecf20Sopenharmony_ci		/*
9508c2ecf20Sopenharmony_ci		 * HV found a VA that did not have the appropriate
9518c2ecf20Sopenharmony_ci		 * permissions (such as the w bit). The VA in question
9528c2ecf20Sopenharmony_ci		 * is returned in status_data param.
9538c2ecf20Sopenharmony_ci		 */
9548c2ecf20Sopenharmony_ci		dax_dbg("hcall returned HV_ENOACCESS");
9558c2ecf20Sopenharmony_ci		ctx->result.exec.status = DAX_SUBMIT_ERR_NOACCESS;
9568c2ecf20Sopenharmony_ci		break;
9578c2ecf20Sopenharmony_ci	case HV_EUNAVAILABLE:
9588c2ecf20Sopenharmony_ci		/*
9598c2ecf20Sopenharmony_ci		 * The requested CCB operation could not be performed
9608c2ecf20Sopenharmony_ci		 * at this time. Return the specific unavailable code
9618c2ecf20Sopenharmony_ci		 * in the status_data field.
9628c2ecf20Sopenharmony_ci		 */
9638c2ecf20Sopenharmony_ci		dax_dbg("hcall returned HV_EUNAVAILABLE");
9648c2ecf20Sopenharmony_ci		ctx->result.exec.status = DAX_SUBMIT_ERR_UNAVAIL;
9658c2ecf20Sopenharmony_ci		break;
9668c2ecf20Sopenharmony_ci	default:
9678c2ecf20Sopenharmony_ci		ctx->result.exec.status = DAX_SUBMIT_ERR_INTERNAL;
9688c2ecf20Sopenharmony_ci		dax_dbg("unknown hcall return value (%ld)", hv_rv);
9698c2ecf20Sopenharmony_ci		break;
9708c2ecf20Sopenharmony_ci	}
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	/* unlock pages associated with the unaccepted CCBs */
9738c2ecf20Sopenharmony_ci	naccepted = accepted_len / sizeof(struct dax_ccb);
9748c2ecf20Sopenharmony_ci	dax_unlock_pages(ctx, idx + naccepted, nccbs - naccepted);
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	/* mark unaccepted CCBs as not completed */
9778c2ecf20Sopenharmony_ci	for (i = idx + naccepted; i < idx + nccbs; i++)
9788c2ecf20Sopenharmony_ci		ctx->ca_buf[i].status = CCA_STAT_COMPLETED;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	ctx->ccb_count += naccepted;
9818c2ecf20Sopenharmony_ci	ctx->fail_count += nccbs - naccepted;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	dax_dbg("hcall rv=%ld, accepted_len=%ld, status_data=0x%llx, ret status=%d",
9848c2ecf20Sopenharmony_ci		hv_rv, accepted_len, ctx->result.exec.status_data,
9858c2ecf20Sopenharmony_ci		ctx->result.exec.status);
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	if (count == accepted_len)
9888c2ecf20Sopenharmony_ci		ctx->client = NULL; /* no read needed to complete protocol */
9898c2ecf20Sopenharmony_ci	return accepted_len;
9908c2ecf20Sopenharmony_ci}
991