162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/* 762306a36Sopenharmony_ci * Oracle Data Analytics Accelerator (DAX) 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * DAX is a coprocessor which resides on the SPARC M7 (DAX1) and M8 1062306a36Sopenharmony_ci * (DAX2) processor chips, and has direct access to the CPU's L3 1162306a36Sopenharmony_ci * caches as well as physical memory. It can perform several 1262306a36Sopenharmony_ci * operations on data streams with various input and output formats. 1362306a36Sopenharmony_ci * The driver provides a transport mechanism only and has limited 1462306a36Sopenharmony_ci * knowledge of the various opcodes and data formats. A user space 1562306a36Sopenharmony_ci * library provides high level services and translates these into low 1662306a36Sopenharmony_ci * level commands which are then passed into the driver and 1762306a36Sopenharmony_ci * subsequently the hypervisor and the coprocessor. The library is 1862306a36Sopenharmony_ci * the recommended way for applications to use the coprocessor, and 1962306a36Sopenharmony_ci * the driver interface is not intended for general use. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * See Documentation/arch/sparc/oradax/oracle-dax.rst for more details. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/uaccess.h> 2562306a36Sopenharmony_ci#include <linux/module.h> 2662306a36Sopenharmony_ci#include <linux/delay.h> 2762306a36Sopenharmony_ci#include <linux/cdev.h> 2862306a36Sopenharmony_ci#include <linux/slab.h> 2962306a36Sopenharmony_ci#include <linux/mm.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <asm/hypervisor.h> 3262306a36Sopenharmony_ci#include <asm/mdesc.h> 3362306a36Sopenharmony_ci#include <asm/oradax.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3662306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for Oracle Data Analytics Accelerator"); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define DAX_DBG_FLG_BASIC 0x01 3962306a36Sopenharmony_ci#define DAX_DBG_FLG_STAT 0x02 4062306a36Sopenharmony_ci#define DAX_DBG_FLG_INFO 0x04 4162306a36Sopenharmony_ci#define DAX_DBG_FLG_ALL 0xff 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define dax_err(fmt, ...) pr_err("%s: " fmt "\n", __func__, ##__VA_ARGS__) 4462306a36Sopenharmony_ci#define dax_info(fmt, ...) pr_info("%s: " fmt "\n", __func__, ##__VA_ARGS__) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define dax_dbg(fmt, ...) do { \ 4762306a36Sopenharmony_ci if (dax_debug & DAX_DBG_FLG_BASIC)\ 4862306a36Sopenharmony_ci dax_info(fmt, ##__VA_ARGS__); \ 4962306a36Sopenharmony_ci } while (0) 5062306a36Sopenharmony_ci#define dax_stat_dbg(fmt, ...) do { \ 5162306a36Sopenharmony_ci if (dax_debug & DAX_DBG_FLG_STAT) \ 5262306a36Sopenharmony_ci dax_info(fmt, ##__VA_ARGS__); \ 5362306a36Sopenharmony_ci } while (0) 5462306a36Sopenharmony_ci#define dax_info_dbg(fmt, ...) do { \ 5562306a36Sopenharmony_ci if (dax_debug & DAX_DBG_FLG_INFO) \ 5662306a36Sopenharmony_ci dax_info(fmt, ##__VA_ARGS__); \ 5762306a36Sopenharmony_ci } while (0) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define DAX1_MINOR 1 6062306a36Sopenharmony_ci#define DAX1_MAJOR 1 6162306a36Sopenharmony_ci#define DAX2_MINOR 0 6262306a36Sopenharmony_ci#define DAX2_MAJOR 2 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define DAX1_STR "ORCL,sun4v-dax" 6562306a36Sopenharmony_ci#define DAX2_STR "ORCL,sun4v-dax2" 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define DAX_CA_ELEMS (DAX_MMAP_LEN / sizeof(struct dax_cca)) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define DAX_CCB_USEC 100 7062306a36Sopenharmony_ci#define DAX_CCB_RETRIES 10000 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* stream types */ 7362306a36Sopenharmony_cienum { 7462306a36Sopenharmony_ci OUT, 7562306a36Sopenharmony_ci PRI, 7662306a36Sopenharmony_ci SEC, 7762306a36Sopenharmony_ci TBL, 7862306a36Sopenharmony_ci NUM_STREAM_TYPES 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* completion status */ 8262306a36Sopenharmony_ci#define CCA_STAT_NOT_COMPLETED 0 8362306a36Sopenharmony_ci#define CCA_STAT_COMPLETED 1 8462306a36Sopenharmony_ci#define CCA_STAT_FAILED 2 8562306a36Sopenharmony_ci#define CCA_STAT_KILLED 3 8662306a36Sopenharmony_ci#define CCA_STAT_NOT_RUN 4 8762306a36Sopenharmony_ci#define CCA_STAT_PIPE_OUT 5 8862306a36Sopenharmony_ci#define CCA_STAT_PIPE_SRC 6 8962306a36Sopenharmony_ci#define CCA_STAT_PIPE_DST 7 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* completion err */ 9262306a36Sopenharmony_ci#define CCA_ERR_SUCCESS 0x0 /* no error */ 9362306a36Sopenharmony_ci#define CCA_ERR_OVERFLOW 0x1 /* buffer overflow */ 9462306a36Sopenharmony_ci#define CCA_ERR_DECODE 0x2 /* CCB decode error */ 9562306a36Sopenharmony_ci#define CCA_ERR_PAGE_OVERFLOW 0x3 /* page overflow */ 9662306a36Sopenharmony_ci#define CCA_ERR_KILLED 0x7 /* command was killed */ 9762306a36Sopenharmony_ci#define CCA_ERR_TIMEOUT 0x8 /* Timeout */ 9862306a36Sopenharmony_ci#define CCA_ERR_ADI 0x9 /* ADI error */ 9962306a36Sopenharmony_ci#define CCA_ERR_DATA_FMT 0xA /* data format error */ 10062306a36Sopenharmony_ci#define CCA_ERR_OTHER_NO_RETRY 0xE /* Other error, do not retry */ 10162306a36Sopenharmony_ci#define CCA_ERR_OTHER_RETRY 0xF /* Other error, retry */ 10262306a36Sopenharmony_ci#define CCA_ERR_PARTIAL_SYMBOL 0x80 /* QP partial symbol warning */ 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* CCB address types */ 10562306a36Sopenharmony_ci#define DAX_ADDR_TYPE_NONE 0 10662306a36Sopenharmony_ci#define DAX_ADDR_TYPE_VA_ALT 1 /* secondary context */ 10762306a36Sopenharmony_ci#define DAX_ADDR_TYPE_RA 2 /* real address */ 10862306a36Sopenharmony_ci#define DAX_ADDR_TYPE_VA 3 /* virtual address */ 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* dax_header_t opcode */ 11162306a36Sopenharmony_ci#define DAX_OP_SYNC_NOP 0x0 11262306a36Sopenharmony_ci#define DAX_OP_EXTRACT 0x1 11362306a36Sopenharmony_ci#define DAX_OP_SCAN_VALUE 0x2 11462306a36Sopenharmony_ci#define DAX_OP_SCAN_RANGE 0x3 11562306a36Sopenharmony_ci#define DAX_OP_TRANSLATE 0x4 11662306a36Sopenharmony_ci#define DAX_OP_SELECT 0x5 11762306a36Sopenharmony_ci#define DAX_OP_INVERT 0x10 /* OR with translate, scan opcodes */ 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistruct dax_header { 12062306a36Sopenharmony_ci u32 ccb_version:4; /* 31:28 CCB Version */ 12162306a36Sopenharmony_ci /* 27:24 Sync Flags */ 12262306a36Sopenharmony_ci u32 pipe:1; /* Pipeline */ 12362306a36Sopenharmony_ci u32 longccb:1; /* Longccb. Set for scan with lu2, lu3, lu4. */ 12462306a36Sopenharmony_ci u32 cond:1; /* Conditional */ 12562306a36Sopenharmony_ci u32 serial:1; /* Serial */ 12662306a36Sopenharmony_ci u32 opcode:8; /* 23:16 Opcode */ 12762306a36Sopenharmony_ci /* 15:0 Address Type. */ 12862306a36Sopenharmony_ci u32 reserved:3; /* 15:13 reserved */ 12962306a36Sopenharmony_ci u32 table_addr_type:2; /* 12:11 Huffman Table Address Type */ 13062306a36Sopenharmony_ci u32 out_addr_type:3; /* 10:8 Destination Address Type */ 13162306a36Sopenharmony_ci u32 sec_addr_type:3; /* 7:5 Secondary Source Address Type */ 13262306a36Sopenharmony_ci u32 pri_addr_type:3; /* 4:2 Primary Source Address Type */ 13362306a36Sopenharmony_ci u32 cca_addr_type:2; /* 1:0 Completion Address Type */ 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistruct dax_control { 13762306a36Sopenharmony_ci u32 pri_fmt:4; /* 31:28 Primary Input Format */ 13862306a36Sopenharmony_ci u32 pri_elem_size:5; /* 27:23 Primary Input Element Size(less1) */ 13962306a36Sopenharmony_ci u32 pri_offset:3; /* 22:20 Primary Input Starting Offset */ 14062306a36Sopenharmony_ci u32 sec_encoding:1; /* 19 Secondary Input Encoding */ 14162306a36Sopenharmony_ci /* (must be 0 for Select) */ 14262306a36Sopenharmony_ci u32 sec_offset:3; /* 18:16 Secondary Input Starting Offset */ 14362306a36Sopenharmony_ci u32 sec_elem_size:2; /* 15:14 Secondary Input Element Size */ 14462306a36Sopenharmony_ci /* (must be 0 for Select) */ 14562306a36Sopenharmony_ci u32 out_fmt:2; /* 13:12 Output Format */ 14662306a36Sopenharmony_ci u32 out_elem_size:2; /* 11:10 Output Element Size */ 14762306a36Sopenharmony_ci u32 misc:10; /* 9:0 Opcode specific info */ 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistruct dax_data_access { 15162306a36Sopenharmony_ci u64 flow_ctrl:2; /* 63:62 Flow Control Type */ 15262306a36Sopenharmony_ci u64 pipe_target:2; /* 61:60 Pipeline Target */ 15362306a36Sopenharmony_ci u64 out_buf_size:20; /* 59:40 Output Buffer Size */ 15462306a36Sopenharmony_ci /* (cachelines less 1) */ 15562306a36Sopenharmony_ci u64 unused1:8; /* 39:32 Reserved, Set to 0 */ 15662306a36Sopenharmony_ci u64 out_alloc:5; /* 31:27 Output Allocation */ 15762306a36Sopenharmony_ci u64 unused2:1; /* 26 Reserved */ 15862306a36Sopenharmony_ci u64 pri_len_fmt:2; /* 25:24 Input Length Format */ 15962306a36Sopenharmony_ci u64 pri_len:24; /* 23:0 Input Element/Byte/Bit Count */ 16062306a36Sopenharmony_ci /* (less 1) */ 16162306a36Sopenharmony_ci}; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistruct dax_ccb { 16462306a36Sopenharmony_ci struct dax_header hdr; /* CCB Header */ 16562306a36Sopenharmony_ci struct dax_control ctrl;/* Control Word */ 16662306a36Sopenharmony_ci void *ca; /* Completion Address */ 16762306a36Sopenharmony_ci void *pri; /* Primary Input Address */ 16862306a36Sopenharmony_ci struct dax_data_access dac; /* Data Access Control */ 16962306a36Sopenharmony_ci void *sec; /* Secondary Input Address */ 17062306a36Sopenharmony_ci u64 dword5; /* depends on opcode */ 17162306a36Sopenharmony_ci void *out; /* Output Address */ 17262306a36Sopenharmony_ci void *tbl; /* Table Address or bitmap */ 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistruct dax_cca { 17662306a36Sopenharmony_ci u8 status; /* user may mwait on this address */ 17762306a36Sopenharmony_ci u8 err; /* user visible error notification */ 17862306a36Sopenharmony_ci u8 rsvd[2]; /* reserved */ 17962306a36Sopenharmony_ci u32 n_remaining; /* for QP partial symbol warning */ 18062306a36Sopenharmony_ci u32 output_sz; /* output in bytes */ 18162306a36Sopenharmony_ci u32 rsvd2; /* reserved */ 18262306a36Sopenharmony_ci u64 run_cycles; /* run time in OCND2 cycles */ 18362306a36Sopenharmony_ci u64 run_stats; /* nothing reported in version 1.0 */ 18462306a36Sopenharmony_ci u32 n_processed; /* number input elements */ 18562306a36Sopenharmony_ci u32 rsvd3[5]; /* reserved */ 18662306a36Sopenharmony_ci u64 retval; /* command return value */ 18762306a36Sopenharmony_ci u64 rsvd4[8]; /* reserved */ 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* per thread CCB context */ 19162306a36Sopenharmony_cistruct dax_ctx { 19262306a36Sopenharmony_ci struct dax_ccb *ccb_buf; 19362306a36Sopenharmony_ci u64 ccb_buf_ra; /* cached RA of ccb_buf */ 19462306a36Sopenharmony_ci struct dax_cca *ca_buf; 19562306a36Sopenharmony_ci u64 ca_buf_ra; /* cached RA of ca_buf */ 19662306a36Sopenharmony_ci struct page *pages[DAX_CA_ELEMS][NUM_STREAM_TYPES]; 19762306a36Sopenharmony_ci /* array of locked pages */ 19862306a36Sopenharmony_ci struct task_struct *owner; /* thread that owns ctx */ 19962306a36Sopenharmony_ci struct task_struct *client; /* requesting thread */ 20062306a36Sopenharmony_ci union ccb_result result; 20162306a36Sopenharmony_ci u32 ccb_count; 20262306a36Sopenharmony_ci u32 fail_count; 20362306a36Sopenharmony_ci}; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/* driver public entry points */ 20662306a36Sopenharmony_cistatic int dax_open(struct inode *inode, struct file *file); 20762306a36Sopenharmony_cistatic ssize_t dax_read(struct file *filp, char __user *buf, 20862306a36Sopenharmony_ci size_t count, loff_t *ppos); 20962306a36Sopenharmony_cistatic ssize_t dax_write(struct file *filp, const char __user *buf, 21062306a36Sopenharmony_ci size_t count, loff_t *ppos); 21162306a36Sopenharmony_cistatic int dax_devmap(struct file *f, struct vm_area_struct *vma); 21262306a36Sopenharmony_cistatic int dax_close(struct inode *i, struct file *f); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic const struct file_operations dax_fops = { 21562306a36Sopenharmony_ci .owner = THIS_MODULE, 21662306a36Sopenharmony_ci .open = dax_open, 21762306a36Sopenharmony_ci .read = dax_read, 21862306a36Sopenharmony_ci .write = dax_write, 21962306a36Sopenharmony_ci .mmap = dax_devmap, 22062306a36Sopenharmony_ci .release = dax_close, 22162306a36Sopenharmony_ci}; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int dax_ccb_exec(struct dax_ctx *ctx, const char __user *buf, 22462306a36Sopenharmony_ci size_t count, loff_t *ppos); 22562306a36Sopenharmony_cistatic int dax_ccb_info(u64 ca, struct ccb_info_result *info); 22662306a36Sopenharmony_cistatic int dax_ccb_kill(u64 ca, u16 *kill_res); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic struct cdev c_dev; 22962306a36Sopenharmony_cistatic dev_t first; 23062306a36Sopenharmony_cistatic const struct class cl = { 23162306a36Sopenharmony_ci .name = DAX_NAME, 23262306a36Sopenharmony_ci}; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic int max_ccb_version; 23562306a36Sopenharmony_cistatic int dax_debug; 23662306a36Sopenharmony_cimodule_param(dax_debug, int, 0644); 23762306a36Sopenharmony_ciMODULE_PARM_DESC(dax_debug, "Debug flags"); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic int __init dax_attach(void) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci unsigned long dummy, hv_rv, major, minor, minor_requested, max_ccbs; 24262306a36Sopenharmony_ci struct mdesc_handle *hp = mdesc_grab(); 24362306a36Sopenharmony_ci char *prop, *dax_name; 24462306a36Sopenharmony_ci bool found = false; 24562306a36Sopenharmony_ci int len, ret = 0; 24662306a36Sopenharmony_ci u64 pn; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (hp == NULL) { 24962306a36Sopenharmony_ci dax_err("Unable to grab mdesc"); 25062306a36Sopenharmony_ci return -ENODEV; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci mdesc_for_each_node_by_name(hp, pn, "virtual-device") { 25462306a36Sopenharmony_ci prop = (char *)mdesc_get_property(hp, pn, "name", &len); 25562306a36Sopenharmony_ci if (prop == NULL) 25662306a36Sopenharmony_ci continue; 25762306a36Sopenharmony_ci if (strncmp(prop, "dax", strlen("dax"))) 25862306a36Sopenharmony_ci continue; 25962306a36Sopenharmony_ci dax_dbg("Found node 0x%llx = %s", pn, prop); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci prop = (char *)mdesc_get_property(hp, pn, "compatible", &len); 26262306a36Sopenharmony_ci if (prop == NULL) 26362306a36Sopenharmony_ci continue; 26462306a36Sopenharmony_ci dax_dbg("Found node 0x%llx = %s", pn, prop); 26562306a36Sopenharmony_ci found = true; 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (!found) { 27062306a36Sopenharmony_ci dax_err("No DAX device found"); 27162306a36Sopenharmony_ci ret = -ENODEV; 27262306a36Sopenharmony_ci goto done; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (strncmp(prop, DAX2_STR, strlen(DAX2_STR)) == 0) { 27662306a36Sopenharmony_ci dax_name = DAX_NAME "2"; 27762306a36Sopenharmony_ci major = DAX2_MAJOR; 27862306a36Sopenharmony_ci minor_requested = DAX2_MINOR; 27962306a36Sopenharmony_ci max_ccb_version = 1; 28062306a36Sopenharmony_ci dax_dbg("MD indicates DAX2 coprocessor"); 28162306a36Sopenharmony_ci } else if (strncmp(prop, DAX1_STR, strlen(DAX1_STR)) == 0) { 28262306a36Sopenharmony_ci dax_name = DAX_NAME "1"; 28362306a36Sopenharmony_ci major = DAX1_MAJOR; 28462306a36Sopenharmony_ci minor_requested = DAX1_MINOR; 28562306a36Sopenharmony_ci max_ccb_version = 0; 28662306a36Sopenharmony_ci dax_dbg("MD indicates DAX1 coprocessor"); 28762306a36Sopenharmony_ci } else { 28862306a36Sopenharmony_ci dax_err("Unknown dax type: %s", prop); 28962306a36Sopenharmony_ci ret = -ENODEV; 29062306a36Sopenharmony_ci goto done; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci minor = minor_requested; 29462306a36Sopenharmony_ci dax_dbg("Registering DAX HV api with major %ld minor %ld", major, 29562306a36Sopenharmony_ci minor); 29662306a36Sopenharmony_ci if (sun4v_hvapi_register(HV_GRP_DAX, major, &minor)) { 29762306a36Sopenharmony_ci dax_err("hvapi_register failed"); 29862306a36Sopenharmony_ci ret = -ENODEV; 29962306a36Sopenharmony_ci goto done; 30062306a36Sopenharmony_ci } else { 30162306a36Sopenharmony_ci dax_dbg("Max minor supported by HV = %ld (major %ld)", minor, 30262306a36Sopenharmony_ci major); 30362306a36Sopenharmony_ci minor = min(minor, minor_requested); 30462306a36Sopenharmony_ci dax_dbg("registered DAX major %ld minor %ld", major, minor); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* submit a zero length ccb array to query coprocessor queue size */ 30862306a36Sopenharmony_ci hv_rv = sun4v_ccb_submit(0, 0, HV_CCB_QUERY_CMD, 0, &max_ccbs, &dummy); 30962306a36Sopenharmony_ci if (hv_rv != 0) { 31062306a36Sopenharmony_ci dax_err("get_hwqueue_size failed with status=%ld and max_ccbs=%ld", 31162306a36Sopenharmony_ci hv_rv, max_ccbs); 31262306a36Sopenharmony_ci ret = -ENODEV; 31362306a36Sopenharmony_ci goto done; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (max_ccbs != DAX_MAX_CCBS) { 31762306a36Sopenharmony_ci dax_err("HV reports unsupported max_ccbs=%ld", max_ccbs); 31862306a36Sopenharmony_ci ret = -ENODEV; 31962306a36Sopenharmony_ci goto done; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (alloc_chrdev_region(&first, 0, 1, DAX_NAME) < 0) { 32362306a36Sopenharmony_ci dax_err("alloc_chrdev_region failed"); 32462306a36Sopenharmony_ci ret = -ENXIO; 32562306a36Sopenharmony_ci goto done; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci ret = class_register(&cl); 32962306a36Sopenharmony_ci if (ret) 33062306a36Sopenharmony_ci goto class_error; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (device_create(&cl, NULL, first, NULL, dax_name) == NULL) { 33362306a36Sopenharmony_ci dax_err("device_create failed"); 33462306a36Sopenharmony_ci ret = -ENXIO; 33562306a36Sopenharmony_ci goto device_error; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci cdev_init(&c_dev, &dax_fops); 33962306a36Sopenharmony_ci if (cdev_add(&c_dev, first, 1) == -1) { 34062306a36Sopenharmony_ci dax_err("cdev_add failed"); 34162306a36Sopenharmony_ci ret = -ENXIO; 34262306a36Sopenharmony_ci goto cdev_error; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci pr_info("Attached DAX module\n"); 34662306a36Sopenharmony_ci goto done; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cicdev_error: 34962306a36Sopenharmony_ci device_destroy(&cl, first); 35062306a36Sopenharmony_cidevice_error: 35162306a36Sopenharmony_ci class_unregister(&cl); 35262306a36Sopenharmony_ciclass_error: 35362306a36Sopenharmony_ci unregister_chrdev_region(first, 1); 35462306a36Sopenharmony_cidone: 35562306a36Sopenharmony_ci mdesc_release(hp); 35662306a36Sopenharmony_ci return ret; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_cimodule_init(dax_attach); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic void __exit dax_detach(void) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci pr_info("Cleaning up DAX module\n"); 36362306a36Sopenharmony_ci cdev_del(&c_dev); 36462306a36Sopenharmony_ci device_destroy(&cl, first); 36562306a36Sopenharmony_ci class_unregister(&cl); 36662306a36Sopenharmony_ci unregister_chrdev_region(first, 1); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_cimodule_exit(dax_detach); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci/* map completion area */ 37162306a36Sopenharmony_cistatic int dax_devmap(struct file *f, struct vm_area_struct *vma) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct dax_ctx *ctx = (struct dax_ctx *)f->private_data; 37462306a36Sopenharmony_ci size_t len = vma->vm_end - vma->vm_start; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci dax_dbg("len=0x%lx, flags=0x%lx", len, vma->vm_flags); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (ctx->owner != current) { 37962306a36Sopenharmony_ci dax_dbg("devmap called from wrong thread"); 38062306a36Sopenharmony_ci return -EINVAL; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (len != DAX_MMAP_LEN) { 38462306a36Sopenharmony_ci dax_dbg("len(%lu) != DAX_MMAP_LEN(%d)", len, DAX_MMAP_LEN); 38562306a36Sopenharmony_ci return -EINVAL; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* completion area is mapped read-only for user */ 38962306a36Sopenharmony_ci if (vma->vm_flags & VM_WRITE) 39062306a36Sopenharmony_ci return -EPERM; 39162306a36Sopenharmony_ci vm_flags_clear(vma, VM_MAYWRITE); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (remap_pfn_range(vma, vma->vm_start, ctx->ca_buf_ra >> PAGE_SHIFT, 39462306a36Sopenharmony_ci len, vma->vm_page_prot)) 39562306a36Sopenharmony_ci return -EAGAIN; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci dax_dbg("mmapped completion area at uva 0x%lx", vma->vm_start); 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci/* Unlock user pages. Called during dequeue or device close */ 40262306a36Sopenharmony_cistatic void dax_unlock_pages(struct dax_ctx *ctx, int ccb_index, int nelem) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci int i, j; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci for (i = ccb_index; i < ccb_index + nelem; i++) { 40762306a36Sopenharmony_ci for (j = 0; j < NUM_STREAM_TYPES; j++) { 40862306a36Sopenharmony_ci struct page *p = ctx->pages[i][j]; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (p) { 41162306a36Sopenharmony_ci dax_dbg("freeing page %p", p); 41262306a36Sopenharmony_ci unpin_user_pages_dirty_lock(&p, 1, j == OUT); 41362306a36Sopenharmony_ci ctx->pages[i][j] = NULL; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic int dax_lock_page(void *va, struct page **p) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci int ret; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci dax_dbg("uva %p", va); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci ret = pin_user_pages_fast((unsigned long)va, 1, FOLL_WRITE, p); 42662306a36Sopenharmony_ci if (ret == 1) { 42762306a36Sopenharmony_ci dax_dbg("locked page %p, for VA %p", *p, va); 42862306a36Sopenharmony_ci return 0; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci dax_dbg("pin_user_pages failed, va=%p, ret=%d", va, ret); 43262306a36Sopenharmony_ci return -1; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int dax_lock_pages(struct dax_ctx *ctx, int idx, 43662306a36Sopenharmony_ci int nelem, u64 *err_va) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci int i; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci for (i = 0; i < nelem; i++) { 44162306a36Sopenharmony_ci struct dax_ccb *ccbp = &ctx->ccb_buf[i]; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* 44462306a36Sopenharmony_ci * For each address in the CCB whose type is virtual, 44562306a36Sopenharmony_ci * lock the page and change the type to virtual alternate 44662306a36Sopenharmony_ci * context. On error, return the offending address in 44762306a36Sopenharmony_ci * err_va. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_ci if (ccbp->hdr.out_addr_type == DAX_ADDR_TYPE_VA) { 45062306a36Sopenharmony_ci dax_dbg("output"); 45162306a36Sopenharmony_ci if (dax_lock_page(ccbp->out, 45262306a36Sopenharmony_ci &ctx->pages[i + idx][OUT]) != 0) { 45362306a36Sopenharmony_ci *err_va = (u64)ccbp->out; 45462306a36Sopenharmony_ci goto error; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci ccbp->hdr.out_addr_type = DAX_ADDR_TYPE_VA_ALT; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (ccbp->hdr.pri_addr_type == DAX_ADDR_TYPE_VA) { 46062306a36Sopenharmony_ci dax_dbg("input"); 46162306a36Sopenharmony_ci if (dax_lock_page(ccbp->pri, 46262306a36Sopenharmony_ci &ctx->pages[i + idx][PRI]) != 0) { 46362306a36Sopenharmony_ci *err_va = (u64)ccbp->pri; 46462306a36Sopenharmony_ci goto error; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci ccbp->hdr.pri_addr_type = DAX_ADDR_TYPE_VA_ALT; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (ccbp->hdr.sec_addr_type == DAX_ADDR_TYPE_VA) { 47062306a36Sopenharmony_ci dax_dbg("sec input"); 47162306a36Sopenharmony_ci if (dax_lock_page(ccbp->sec, 47262306a36Sopenharmony_ci &ctx->pages[i + idx][SEC]) != 0) { 47362306a36Sopenharmony_ci *err_va = (u64)ccbp->sec; 47462306a36Sopenharmony_ci goto error; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci ccbp->hdr.sec_addr_type = DAX_ADDR_TYPE_VA_ALT; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (ccbp->hdr.table_addr_type == DAX_ADDR_TYPE_VA) { 48062306a36Sopenharmony_ci dax_dbg("tbl"); 48162306a36Sopenharmony_ci if (dax_lock_page(ccbp->tbl, 48262306a36Sopenharmony_ci &ctx->pages[i + idx][TBL]) != 0) { 48362306a36Sopenharmony_ci *err_va = (u64)ccbp->tbl; 48462306a36Sopenharmony_ci goto error; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci ccbp->hdr.table_addr_type = DAX_ADDR_TYPE_VA_ALT; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* skip over 2nd 64 bytes of long CCB */ 49062306a36Sopenharmony_ci if (ccbp->hdr.longccb) 49162306a36Sopenharmony_ci i++; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci return DAX_SUBMIT_OK; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cierror: 49662306a36Sopenharmony_ci dax_unlock_pages(ctx, idx, nelem); 49762306a36Sopenharmony_ci return DAX_SUBMIT_ERR_NOACCESS; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic void dax_ccb_wait(struct dax_ctx *ctx, int idx) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci int ret, nretries; 50362306a36Sopenharmony_ci u16 kill_res; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci dax_dbg("idx=%d", idx); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci for (nretries = 0; nretries < DAX_CCB_RETRIES; nretries++) { 50862306a36Sopenharmony_ci if (ctx->ca_buf[idx].status == CCA_STAT_NOT_COMPLETED) 50962306a36Sopenharmony_ci udelay(DAX_CCB_USEC); 51062306a36Sopenharmony_ci else 51162306a36Sopenharmony_ci return; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci dax_dbg("ctx (%p): CCB[%d] timed out, wait usec=%d, retries=%d. Killing ccb", 51462306a36Sopenharmony_ci (void *)ctx, idx, DAX_CCB_USEC, DAX_CCB_RETRIES); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci ret = dax_ccb_kill(ctx->ca_buf_ra + idx * sizeof(struct dax_cca), 51762306a36Sopenharmony_ci &kill_res); 51862306a36Sopenharmony_ci dax_dbg("Kill CCB[%d] %s", idx, ret ? "failed" : "succeeded"); 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic int dax_close(struct inode *ino, struct file *f) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci struct dax_ctx *ctx = (struct dax_ctx *)f->private_data; 52462306a36Sopenharmony_ci int i; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci f->private_data = NULL; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci for (i = 0; i < DAX_CA_ELEMS; i++) { 52962306a36Sopenharmony_ci if (ctx->ca_buf[i].status == CCA_STAT_NOT_COMPLETED) { 53062306a36Sopenharmony_ci dax_dbg("CCB[%d] not completed", i); 53162306a36Sopenharmony_ci dax_ccb_wait(ctx, i); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci dax_unlock_pages(ctx, i, 1); 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci kfree(ctx->ccb_buf); 53762306a36Sopenharmony_ci kfree(ctx->ca_buf); 53862306a36Sopenharmony_ci dax_stat_dbg("CCBs: %d good, %d bad", ctx->ccb_count, ctx->fail_count); 53962306a36Sopenharmony_ci kfree(ctx); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci return 0; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic ssize_t dax_read(struct file *f, char __user *buf, 54562306a36Sopenharmony_ci size_t count, loff_t *ppos) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct dax_ctx *ctx = f->private_data; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (ctx->client != current) 55062306a36Sopenharmony_ci return -EUSERS; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci ctx->client = NULL; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (count != sizeof(union ccb_result)) 55562306a36Sopenharmony_ci return -EINVAL; 55662306a36Sopenharmony_ci if (copy_to_user(buf, &ctx->result, sizeof(union ccb_result))) 55762306a36Sopenharmony_ci return -EFAULT; 55862306a36Sopenharmony_ci return count; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic ssize_t dax_write(struct file *f, const char __user *buf, 56262306a36Sopenharmony_ci size_t count, loff_t *ppos) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct dax_ctx *ctx = f->private_data; 56562306a36Sopenharmony_ci struct dax_command hdr; 56662306a36Sopenharmony_ci unsigned long ca; 56762306a36Sopenharmony_ci int i, idx, ret; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (ctx->client != NULL) 57062306a36Sopenharmony_ci return -EINVAL; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (count == 0 || count > DAX_MAX_CCBS * sizeof(struct dax_ccb)) 57362306a36Sopenharmony_ci return -EINVAL; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (count % sizeof(struct dax_ccb) == 0) 57662306a36Sopenharmony_ci return dax_ccb_exec(ctx, buf, count, ppos); /* CCB EXEC */ 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (count != sizeof(struct dax_command)) 57962306a36Sopenharmony_ci return -EINVAL; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* immediate command */ 58262306a36Sopenharmony_ci if (ctx->owner != current) 58362306a36Sopenharmony_ci return -EUSERS; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (copy_from_user(&hdr, buf, sizeof(hdr))) 58662306a36Sopenharmony_ci return -EFAULT; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci ca = ctx->ca_buf_ra + hdr.ca_offset; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci switch (hdr.command) { 59162306a36Sopenharmony_ci case CCB_KILL: 59262306a36Sopenharmony_ci if (hdr.ca_offset >= DAX_MMAP_LEN) { 59362306a36Sopenharmony_ci dax_dbg("invalid ca_offset (%d) >= ca_buflen (%d)", 59462306a36Sopenharmony_ci hdr.ca_offset, DAX_MMAP_LEN); 59562306a36Sopenharmony_ci return -EINVAL; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci ret = dax_ccb_kill(ca, &ctx->result.kill.action); 59962306a36Sopenharmony_ci if (ret != 0) { 60062306a36Sopenharmony_ci dax_dbg("dax_ccb_kill failed (ret=%d)", ret); 60162306a36Sopenharmony_ci return ret; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci dax_info_dbg("killed (ca_offset %d)", hdr.ca_offset); 60562306a36Sopenharmony_ci idx = hdr.ca_offset / sizeof(struct dax_cca); 60662306a36Sopenharmony_ci ctx->ca_buf[idx].status = CCA_STAT_KILLED; 60762306a36Sopenharmony_ci ctx->ca_buf[idx].err = CCA_ERR_KILLED; 60862306a36Sopenharmony_ci ctx->client = current; 60962306a36Sopenharmony_ci return count; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci case CCB_INFO: 61262306a36Sopenharmony_ci if (hdr.ca_offset >= DAX_MMAP_LEN) { 61362306a36Sopenharmony_ci dax_dbg("invalid ca_offset (%d) >= ca_buflen (%d)", 61462306a36Sopenharmony_ci hdr.ca_offset, DAX_MMAP_LEN); 61562306a36Sopenharmony_ci return -EINVAL; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci ret = dax_ccb_info(ca, &ctx->result.info); 61962306a36Sopenharmony_ci if (ret != 0) { 62062306a36Sopenharmony_ci dax_dbg("dax_ccb_info failed (ret=%d)", ret); 62162306a36Sopenharmony_ci return ret; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci dax_info_dbg("info succeeded on ca_offset %d", hdr.ca_offset); 62562306a36Sopenharmony_ci ctx->client = current; 62662306a36Sopenharmony_ci return count; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci case CCB_DEQUEUE: 62962306a36Sopenharmony_ci for (i = 0; i < DAX_CA_ELEMS; i++) { 63062306a36Sopenharmony_ci if (ctx->ca_buf[i].status != 63162306a36Sopenharmony_ci CCA_STAT_NOT_COMPLETED) 63262306a36Sopenharmony_ci dax_unlock_pages(ctx, i, 1); 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci return count; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci default: 63762306a36Sopenharmony_ci return -EINVAL; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic int dax_open(struct inode *inode, struct file *f) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci struct dax_ctx *ctx = NULL; 64462306a36Sopenharmony_ci int i; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 64762306a36Sopenharmony_ci if (ctx == NULL) 64862306a36Sopenharmony_ci goto done; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci ctx->ccb_buf = kcalloc(DAX_MAX_CCBS, sizeof(struct dax_ccb), 65162306a36Sopenharmony_ci GFP_KERNEL); 65262306a36Sopenharmony_ci if (ctx->ccb_buf == NULL) 65362306a36Sopenharmony_ci goto done; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci ctx->ccb_buf_ra = virt_to_phys(ctx->ccb_buf); 65662306a36Sopenharmony_ci dax_dbg("ctx->ccb_buf=0x%p, ccb_buf_ra=0x%llx", 65762306a36Sopenharmony_ci (void *)ctx->ccb_buf, ctx->ccb_buf_ra); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* allocate CCB completion area buffer */ 66062306a36Sopenharmony_ci ctx->ca_buf = kzalloc(DAX_MMAP_LEN, GFP_KERNEL); 66162306a36Sopenharmony_ci if (ctx->ca_buf == NULL) 66262306a36Sopenharmony_ci goto alloc_error; 66362306a36Sopenharmony_ci for (i = 0; i < DAX_CA_ELEMS; i++) 66462306a36Sopenharmony_ci ctx->ca_buf[i].status = CCA_STAT_COMPLETED; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci ctx->ca_buf_ra = virt_to_phys(ctx->ca_buf); 66762306a36Sopenharmony_ci dax_dbg("ctx=0x%p, ctx->ca_buf=0x%p, ca_buf_ra=0x%llx", 66862306a36Sopenharmony_ci (void *)ctx, (void *)ctx->ca_buf, ctx->ca_buf_ra); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci ctx->owner = current; 67162306a36Sopenharmony_ci f->private_data = ctx; 67262306a36Sopenharmony_ci return 0; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cialloc_error: 67562306a36Sopenharmony_ci kfree(ctx->ccb_buf); 67662306a36Sopenharmony_cidone: 67762306a36Sopenharmony_ci kfree(ctx); 67862306a36Sopenharmony_ci return -ENOMEM; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic char *dax_hv_errno(unsigned long hv_ret, int *ret) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci switch (hv_ret) { 68462306a36Sopenharmony_ci case HV_EBADALIGN: 68562306a36Sopenharmony_ci *ret = -EFAULT; 68662306a36Sopenharmony_ci return "HV_EBADALIGN"; 68762306a36Sopenharmony_ci case HV_ENORADDR: 68862306a36Sopenharmony_ci *ret = -EFAULT; 68962306a36Sopenharmony_ci return "HV_ENORADDR"; 69062306a36Sopenharmony_ci case HV_EINVAL: 69162306a36Sopenharmony_ci *ret = -EINVAL; 69262306a36Sopenharmony_ci return "HV_EINVAL"; 69362306a36Sopenharmony_ci case HV_EWOULDBLOCK: 69462306a36Sopenharmony_ci *ret = -EAGAIN; 69562306a36Sopenharmony_ci return "HV_EWOULDBLOCK"; 69662306a36Sopenharmony_ci case HV_ENOACCESS: 69762306a36Sopenharmony_ci *ret = -EPERM; 69862306a36Sopenharmony_ci return "HV_ENOACCESS"; 69962306a36Sopenharmony_ci default: 70062306a36Sopenharmony_ci break; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci *ret = -EIO; 70462306a36Sopenharmony_ci return "UNKNOWN"; 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic int dax_ccb_kill(u64 ca, u16 *kill_res) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci unsigned long hv_ret; 71062306a36Sopenharmony_ci int count, ret = 0; 71162306a36Sopenharmony_ci char *err_str; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci for (count = 0; count < DAX_CCB_RETRIES; count++) { 71462306a36Sopenharmony_ci dax_dbg("attempting kill on ca_ra 0x%llx", ca); 71562306a36Sopenharmony_ci hv_ret = sun4v_ccb_kill(ca, kill_res); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (hv_ret == HV_EOK) { 71862306a36Sopenharmony_ci dax_info_dbg("HV_EOK (ca_ra 0x%llx): %d", ca, 71962306a36Sopenharmony_ci *kill_res); 72062306a36Sopenharmony_ci } else { 72162306a36Sopenharmony_ci err_str = dax_hv_errno(hv_ret, &ret); 72262306a36Sopenharmony_ci dax_dbg("%s (ca_ra 0x%llx)", err_str, ca); 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (ret != -EAGAIN) 72662306a36Sopenharmony_ci return ret; 72762306a36Sopenharmony_ci dax_info_dbg("ccb_kill count = %d", count); 72862306a36Sopenharmony_ci udelay(DAX_CCB_USEC); 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci return -EAGAIN; 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic int dax_ccb_info(u64 ca, struct ccb_info_result *info) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci unsigned long hv_ret; 73762306a36Sopenharmony_ci char *err_str; 73862306a36Sopenharmony_ci int ret = 0; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci dax_dbg("attempting info on ca_ra 0x%llx", ca); 74162306a36Sopenharmony_ci hv_ret = sun4v_ccb_info(ca, info); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (hv_ret == HV_EOK) { 74462306a36Sopenharmony_ci dax_info_dbg("HV_EOK (ca_ra 0x%llx): %d", ca, info->state); 74562306a36Sopenharmony_ci if (info->state == DAX_CCB_ENQUEUED) { 74662306a36Sopenharmony_ci dax_info_dbg("dax_unit %d, queue_num %d, queue_pos %d", 74762306a36Sopenharmony_ci info->inst_num, info->q_num, info->q_pos); 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci } else { 75062306a36Sopenharmony_ci err_str = dax_hv_errno(hv_ret, &ret); 75162306a36Sopenharmony_ci dax_dbg("%s (ca_ra 0x%llx)", err_str, ca); 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci return ret; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic void dax_prt_ccbs(struct dax_ccb *ccb, int nelem) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci int i, j; 76062306a36Sopenharmony_ci u64 *ccbp; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci dax_dbg("ccb buffer:"); 76362306a36Sopenharmony_ci for (i = 0; i < nelem; i++) { 76462306a36Sopenharmony_ci ccbp = (u64 *)&ccb[i]; 76562306a36Sopenharmony_ci dax_dbg(" %sccb[%d]", ccb[i].hdr.longccb ? "long " : "", i); 76662306a36Sopenharmony_ci for (j = 0; j < 8; j++) 76762306a36Sopenharmony_ci dax_dbg("\tccb[%d].dwords[%d]=0x%llx", 76862306a36Sopenharmony_ci i, j, *(ccbp + j)); 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci/* 77362306a36Sopenharmony_ci * Validates user CCB content. Also sets completion address and address types 77462306a36Sopenharmony_ci * for all addresses contained in CCB. 77562306a36Sopenharmony_ci */ 77662306a36Sopenharmony_cistatic int dax_preprocess_usr_ccbs(struct dax_ctx *ctx, int idx, int nelem) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci int i; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* 78162306a36Sopenharmony_ci * The user is not allowed to specify real address types in 78262306a36Sopenharmony_ci * the CCB header. This must be enforced by the kernel before 78362306a36Sopenharmony_ci * submitting the CCBs to HV. The only allowed values for all 78462306a36Sopenharmony_ci * address fields are VA or IMM 78562306a36Sopenharmony_ci */ 78662306a36Sopenharmony_ci for (i = 0; i < nelem; i++) { 78762306a36Sopenharmony_ci struct dax_ccb *ccbp = &ctx->ccb_buf[i]; 78862306a36Sopenharmony_ci unsigned long ca_offset; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (ccbp->hdr.ccb_version > max_ccb_version) 79162306a36Sopenharmony_ci return DAX_SUBMIT_ERR_CCB_INVAL; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci switch (ccbp->hdr.opcode) { 79462306a36Sopenharmony_ci case DAX_OP_SYNC_NOP: 79562306a36Sopenharmony_ci case DAX_OP_EXTRACT: 79662306a36Sopenharmony_ci case DAX_OP_SCAN_VALUE: 79762306a36Sopenharmony_ci case DAX_OP_SCAN_RANGE: 79862306a36Sopenharmony_ci case DAX_OP_TRANSLATE: 79962306a36Sopenharmony_ci case DAX_OP_SCAN_VALUE | DAX_OP_INVERT: 80062306a36Sopenharmony_ci case DAX_OP_SCAN_RANGE | DAX_OP_INVERT: 80162306a36Sopenharmony_ci case DAX_OP_TRANSLATE | DAX_OP_INVERT: 80262306a36Sopenharmony_ci case DAX_OP_SELECT: 80362306a36Sopenharmony_ci break; 80462306a36Sopenharmony_ci default: 80562306a36Sopenharmony_ci return DAX_SUBMIT_ERR_CCB_INVAL; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (ccbp->hdr.out_addr_type != DAX_ADDR_TYPE_VA && 80962306a36Sopenharmony_ci ccbp->hdr.out_addr_type != DAX_ADDR_TYPE_NONE) { 81062306a36Sopenharmony_ci dax_dbg("invalid out_addr_type in user CCB[%d]", i); 81162306a36Sopenharmony_ci return DAX_SUBMIT_ERR_CCB_INVAL; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (ccbp->hdr.pri_addr_type != DAX_ADDR_TYPE_VA && 81562306a36Sopenharmony_ci ccbp->hdr.pri_addr_type != DAX_ADDR_TYPE_NONE) { 81662306a36Sopenharmony_ci dax_dbg("invalid pri_addr_type in user CCB[%d]", i); 81762306a36Sopenharmony_ci return DAX_SUBMIT_ERR_CCB_INVAL; 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci if (ccbp->hdr.sec_addr_type != DAX_ADDR_TYPE_VA && 82162306a36Sopenharmony_ci ccbp->hdr.sec_addr_type != DAX_ADDR_TYPE_NONE) { 82262306a36Sopenharmony_ci dax_dbg("invalid sec_addr_type in user CCB[%d]", i); 82362306a36Sopenharmony_ci return DAX_SUBMIT_ERR_CCB_INVAL; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (ccbp->hdr.table_addr_type != DAX_ADDR_TYPE_VA && 82762306a36Sopenharmony_ci ccbp->hdr.table_addr_type != DAX_ADDR_TYPE_NONE) { 82862306a36Sopenharmony_ci dax_dbg("invalid table_addr_type in user CCB[%d]", i); 82962306a36Sopenharmony_ci return DAX_SUBMIT_ERR_CCB_INVAL; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci /* set completion (real) address and address type */ 83362306a36Sopenharmony_ci ccbp->hdr.cca_addr_type = DAX_ADDR_TYPE_RA; 83462306a36Sopenharmony_ci ca_offset = (idx + i) * sizeof(struct dax_cca); 83562306a36Sopenharmony_ci ccbp->ca = (void *)ctx->ca_buf_ra + ca_offset; 83662306a36Sopenharmony_ci memset(&ctx->ca_buf[idx + i], 0, sizeof(struct dax_cca)); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci dax_dbg("ccb[%d]=%p, ca_offset=0x%lx, compl RA=0x%llx", 83962306a36Sopenharmony_ci i, ccbp, ca_offset, ctx->ca_buf_ra + ca_offset); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci /* skip over 2nd 64 bytes of long CCB */ 84262306a36Sopenharmony_ci if (ccbp->hdr.longccb) 84362306a36Sopenharmony_ci i++; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci return DAX_SUBMIT_OK; 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cistatic int dax_ccb_exec(struct dax_ctx *ctx, const char __user *buf, 85062306a36Sopenharmony_ci size_t count, loff_t *ppos) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci unsigned long accepted_len, hv_rv; 85362306a36Sopenharmony_ci int i, idx, nccbs, naccepted; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci ctx->client = current; 85662306a36Sopenharmony_ci idx = *ppos; 85762306a36Sopenharmony_ci nccbs = count / sizeof(struct dax_ccb); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (ctx->owner != current) { 86062306a36Sopenharmony_ci dax_dbg("wrong thread"); 86162306a36Sopenharmony_ci ctx->result.exec.status = DAX_SUBMIT_ERR_THR_INIT; 86262306a36Sopenharmony_ci return 0; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci dax_dbg("args: ccb_buf_len=%ld, idx=%d", count, idx); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci /* for given index and length, verify ca_buf range exists */ 86762306a36Sopenharmony_ci if (idx < 0 || idx > (DAX_CA_ELEMS - nccbs)) { 86862306a36Sopenharmony_ci ctx->result.exec.status = DAX_SUBMIT_ERR_NO_CA_AVAIL; 86962306a36Sopenharmony_ci return 0; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* 87362306a36Sopenharmony_ci * Copy CCBs into kernel buffer to prevent modification by the 87462306a36Sopenharmony_ci * user in between validation and submission. 87562306a36Sopenharmony_ci */ 87662306a36Sopenharmony_ci if (copy_from_user(ctx->ccb_buf, buf, count)) { 87762306a36Sopenharmony_ci dax_dbg("copyin of user CCB buffer failed"); 87862306a36Sopenharmony_ci ctx->result.exec.status = DAX_SUBMIT_ERR_CCB_ARR_MMU_MISS; 87962306a36Sopenharmony_ci return 0; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci /* check to see if ca_buf[idx] .. ca_buf[idx + nccbs] are available */ 88362306a36Sopenharmony_ci for (i = idx; i < idx + nccbs; i++) { 88462306a36Sopenharmony_ci if (ctx->ca_buf[i].status == CCA_STAT_NOT_COMPLETED) { 88562306a36Sopenharmony_ci dax_dbg("CA range not available, dequeue needed"); 88662306a36Sopenharmony_ci ctx->result.exec.status = DAX_SUBMIT_ERR_NO_CA_AVAIL; 88762306a36Sopenharmony_ci return 0; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci dax_unlock_pages(ctx, idx, nccbs); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci ctx->result.exec.status = dax_preprocess_usr_ccbs(ctx, idx, nccbs); 89362306a36Sopenharmony_ci if (ctx->result.exec.status != DAX_SUBMIT_OK) 89462306a36Sopenharmony_ci return 0; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci ctx->result.exec.status = dax_lock_pages(ctx, idx, nccbs, 89762306a36Sopenharmony_ci &ctx->result.exec.status_data); 89862306a36Sopenharmony_ci if (ctx->result.exec.status != DAX_SUBMIT_OK) 89962306a36Sopenharmony_ci return 0; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (dax_debug & DAX_DBG_FLG_BASIC) 90262306a36Sopenharmony_ci dax_prt_ccbs(ctx->ccb_buf, nccbs); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci hv_rv = sun4v_ccb_submit(ctx->ccb_buf_ra, count, 90562306a36Sopenharmony_ci HV_CCB_QUERY_CMD | HV_CCB_VA_SECONDARY, 0, 90662306a36Sopenharmony_ci &accepted_len, &ctx->result.exec.status_data); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci switch (hv_rv) { 90962306a36Sopenharmony_ci case HV_EOK: 91062306a36Sopenharmony_ci /* 91162306a36Sopenharmony_ci * Hcall succeeded with no errors but the accepted 91262306a36Sopenharmony_ci * length may be less than the requested length. The 91362306a36Sopenharmony_ci * only way the driver can resubmit the remainder is 91462306a36Sopenharmony_ci * to wait for completion of the submitted CCBs since 91562306a36Sopenharmony_ci * there is no way to guarantee the ordering semantics 91662306a36Sopenharmony_ci * required by the client applications. Therefore we 91762306a36Sopenharmony_ci * let the user library deal with resubmissions. 91862306a36Sopenharmony_ci */ 91962306a36Sopenharmony_ci ctx->result.exec.status = DAX_SUBMIT_OK; 92062306a36Sopenharmony_ci break; 92162306a36Sopenharmony_ci case HV_EWOULDBLOCK: 92262306a36Sopenharmony_ci /* 92362306a36Sopenharmony_ci * This is a transient HV API error. The user library 92462306a36Sopenharmony_ci * can retry. 92562306a36Sopenharmony_ci */ 92662306a36Sopenharmony_ci dax_dbg("hcall returned HV_EWOULDBLOCK"); 92762306a36Sopenharmony_ci ctx->result.exec.status = DAX_SUBMIT_ERR_WOULDBLOCK; 92862306a36Sopenharmony_ci break; 92962306a36Sopenharmony_ci case HV_ENOMAP: 93062306a36Sopenharmony_ci /* 93162306a36Sopenharmony_ci * HV was unable to translate a VA. The VA it could 93262306a36Sopenharmony_ci * not translate is returned in the status_data param. 93362306a36Sopenharmony_ci */ 93462306a36Sopenharmony_ci dax_dbg("hcall returned HV_ENOMAP"); 93562306a36Sopenharmony_ci ctx->result.exec.status = DAX_SUBMIT_ERR_NOMAP; 93662306a36Sopenharmony_ci break; 93762306a36Sopenharmony_ci case HV_EINVAL: 93862306a36Sopenharmony_ci /* 93962306a36Sopenharmony_ci * This is the result of an invalid user CCB as HV is 94062306a36Sopenharmony_ci * validating some of the user CCB fields. Pass this 94162306a36Sopenharmony_ci * error back to the user. There is no supporting info 94262306a36Sopenharmony_ci * to isolate the invalid field. 94362306a36Sopenharmony_ci */ 94462306a36Sopenharmony_ci dax_dbg("hcall returned HV_EINVAL"); 94562306a36Sopenharmony_ci ctx->result.exec.status = DAX_SUBMIT_ERR_CCB_INVAL; 94662306a36Sopenharmony_ci break; 94762306a36Sopenharmony_ci case HV_ENOACCESS: 94862306a36Sopenharmony_ci /* 94962306a36Sopenharmony_ci * HV found a VA that did not have the appropriate 95062306a36Sopenharmony_ci * permissions (such as the w bit). The VA in question 95162306a36Sopenharmony_ci * is returned in status_data param. 95262306a36Sopenharmony_ci */ 95362306a36Sopenharmony_ci dax_dbg("hcall returned HV_ENOACCESS"); 95462306a36Sopenharmony_ci ctx->result.exec.status = DAX_SUBMIT_ERR_NOACCESS; 95562306a36Sopenharmony_ci break; 95662306a36Sopenharmony_ci case HV_EUNAVAILABLE: 95762306a36Sopenharmony_ci /* 95862306a36Sopenharmony_ci * The requested CCB operation could not be performed 95962306a36Sopenharmony_ci * at this time. Return the specific unavailable code 96062306a36Sopenharmony_ci * in the status_data field. 96162306a36Sopenharmony_ci */ 96262306a36Sopenharmony_ci dax_dbg("hcall returned HV_EUNAVAILABLE"); 96362306a36Sopenharmony_ci ctx->result.exec.status = DAX_SUBMIT_ERR_UNAVAIL; 96462306a36Sopenharmony_ci break; 96562306a36Sopenharmony_ci default: 96662306a36Sopenharmony_ci ctx->result.exec.status = DAX_SUBMIT_ERR_INTERNAL; 96762306a36Sopenharmony_ci dax_dbg("unknown hcall return value (%ld)", hv_rv); 96862306a36Sopenharmony_ci break; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* unlock pages associated with the unaccepted CCBs */ 97262306a36Sopenharmony_ci naccepted = accepted_len / sizeof(struct dax_ccb); 97362306a36Sopenharmony_ci dax_unlock_pages(ctx, idx + naccepted, nccbs - naccepted); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci /* mark unaccepted CCBs as not completed */ 97662306a36Sopenharmony_ci for (i = idx + naccepted; i < idx + nccbs; i++) 97762306a36Sopenharmony_ci ctx->ca_buf[i].status = CCA_STAT_COMPLETED; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci ctx->ccb_count += naccepted; 98062306a36Sopenharmony_ci ctx->fail_count += nccbs - naccepted; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci dax_dbg("hcall rv=%ld, accepted_len=%ld, status_data=0x%llx, ret status=%d", 98362306a36Sopenharmony_ci hv_rv, accepted_len, ctx->result.exec.status_data, 98462306a36Sopenharmony_ci ctx->result.exec.status); 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (count == accepted_len) 98762306a36Sopenharmony_ci ctx->client = NULL; /* no read needed to complete protocol */ 98862306a36Sopenharmony_ci return accepted_len; 98962306a36Sopenharmony_ci} 990