static int dax_ioctl_ccb_thr_fini(struct file *f);
static int dax_ioctl_ccb_exec(void *, struct file *);
static int dax_ioctl_ca_dequeue(void *, struct file *f);
+static int dax_ioctl_ccb_kill(void *arg, struct file *f);
+static int dax_ioctl_ccb_info(void *arg, struct file *f);
static int dax_validate_ca_dequeue_args(struct dax_ctx *,
struct dax_ca_dequeue_arg *);
static int dax_ccb_hv_submit(struct dax_ctx *, union ccb *, size_t,
return dax_ioctl_ca_dequeue((void *)arg, f);
case DAXIOC_CCB_EXEC:
return dax_ioctl_ccb_exec((void *)arg, f);
+ case DAXIOC_CCB_KILL:
+ return dax_ioctl_ccb_kill((void *)arg, f);
+ case DAXIOC_CCB_INFO:
+ return dax_ioctl_ccb_info((void *)arg, f);
case DAXIOC_VERSION:
if (copy_to_user((void __user *)arg, &dax_version,
sizeof(dax_version)))
}
dax_state_destroy(f);
+ return 0;
+}
+
+int dax_ccb_kill_info_hv(u64 ca, unsigned long ret, char *ok_str)
+{
+ switch (ret) {
+ case HV_EOK:
+ dax_kill_info_dbg("HV returned HV_EOK for ca_ra 0x%llx, %s", ca,
+ ok_str);
+ return 0;
+
+ case HV_EBADALIGN:
+ dax_err("HV returned HV_EBADALIGN for ca_ra 0x%llx", ca);
+ return -EFAULT;
+
+ case HV_ENORADDR:
+ dax_err("HV returned HV_ENORADDR for ca_ra 0x%llx", ca);
+ return -EFAULT;
+
+ case HV_EINVAL:
+ dax_err("HV returned HV_EINVAL for ca_ra 0x%llx", ca);
+ return -EINVAL;
+
+ case HV_EWOULDBLOCK:
+ dax_err("HV returned HV_EWOULDBLOCK for ca_ra 0x%llx", ca);
+ return -EAGAIN;
+
+ case HV_ENOACCESS:
+ dax_err("HV returned HV_ENOACCESS for ca_ra 0x%llx", ca);
+ return -EPERM;
+
+ default:
+ dax_err("HV returned unknown (%ld) for ca_ra 0x%llx", ret, ca);
+ return -EIO;
+ }
+}
+
+int dax_ccb_kill(u64 ca, u16 *kill_res)
+{
+ unsigned long hv_ret;
+ char res_str[80];
+ int count;
+ int ret;
+
+ /* confirm dax drv and hv api constants the same */
+ BUILD_BUG_ON(DAX_KILL_COMPLETED != HV_DAX_KILL_COMPLETED);
+ BUILD_BUG_ON(DAX_KILL_DEQUEUED != HV_DAX_KILL_DEQUEUED);
+ BUILD_BUG_ON(DAX_KILL_KILLED != HV_DAX_KILL_KILLED);
+ BUILD_BUG_ON(DAX_KILL_NOTFOUND != HV_DAX_KILL_NOTFOUND);
+
+ for (count = 0; count < DAX_KILL_RETRIES_MAX; count++) {
+ dax_dbg("attempting kill on ca_ra 0x%llx", ca);
+ hv_ret = sun4v_dax_ccb_kill(ca, kill_res);
+
+ if (*kill_res == DAX_KILL_COMPLETED)
+ snprintf(res_str, sizeof(res_str), "COMPLETED");
+ else if (*kill_res == DAX_KILL_DEQUEUED)
+ snprintf(res_str, sizeof(res_str), "DEQUEUED");
+ else if (*kill_res == DAX_KILL_KILLED)
+ snprintf(res_str, sizeof(res_str), "KILLED");
+ else if (*kill_res == DAX_KILL_NOTFOUND)
+ snprintf(res_str, sizeof(res_str), "NOTFOUND");
+ else
+ snprintf(res_str, sizeof(res_str), "??? (%d)",
+ *kill_res);
+
+ ret = dax_ccb_kill_info_hv(ca, hv_ret, res_str);
+ if (ret != -EAGAIN)
+ return ret;
+ dax_kill_info_dbg("ccb_kill count = %d", count);
+ udelay(DAX_KILL_WAIT_USEC);
+ }
+
+ return -EAGAIN;
+}
+
+static int dax_ccb_info(u64 ca, struct dax_ccb_info_arg *info)
+{
+ u16 *info_arr = &info->dax_ccb_state;
+ unsigned long hv_ret;
+ char info_str[80];
+ int count;
+ int ret;
+
+ /* confirm dax drv and hv api constants the same */
+ BUILD_BUG_ON(DAX_CCB_COMPLETED != HV_CCB_STATE_COMPLETED);
+ BUILD_BUG_ON(DAX_CCB_ENQUEUED != HV_CCB_STATE_ENQUEUED);
+ BUILD_BUG_ON(DAX_CCB_INPROGRESS != HV_CCB_STATE_INPROGRESS);
+ BUILD_BUG_ON(DAX_CCB_NOTFOUND != HV_CCB_STATE_NOTFOUND);
+
+ for (count = 0; count < DAX_INFO_RETRIES_MAX; count++) {
+ dax_dbg("attempting info on ca_ra 0x%llx", ca);
+ hv_ret = sun4v_dax_ccb_info(ca, info_arr);
+
+ if (info->dax_ccb_state == DAX_CCB_COMPLETED) {
+ snprintf(info_str, sizeof(info_str),
+ "ccb_state COMPLETED");
+ } else if (info->dax_ccb_state == DAX_CCB_ENQUEUED) {
+ snprintf(info_str, sizeof(info_str),
+ "ccb_state ENQUEUED (dax_unit %d, queue_num %d, queue_pos %d)",
+ info->dax_inst_num, info->dax_q_num,
+ info->dax_q_pos);
+ } else if (info->dax_ccb_state == DAX_CCB_INPROGRESS) {
+ snprintf(info_str, sizeof(info_str),
+ "ccb_state INPROGRESS");
+ } else if (info->dax_ccb_state == DAX_CCB_NOTFOUND) {
+ snprintf(info_str, sizeof(info_str),
+ "ccb_state NOTFOUND");
+ } else {
+ snprintf(info_str, sizeof(info_str),
+ "ccb_state ??? (%d)", info->dax_ccb_state);
+ }
+
+ ret = dax_ccb_kill_info_hv(ca, hv_ret, info_str);
+ if (ret != -EAGAIN)
+ return ret;
+ dax_kill_info_dbg("ccb_info count = %d", count);
+ udelay(DAX_INFO_WAIT_USEC);
+ }
+
+ return -EAGAIN;
+}
+
+static int dax_ioctl_ccb_kill(void *arg, struct file *f)
+{
+ struct dax_ctx *dax_ctx = (struct dax_ctx *) f->private_data;
+ struct dax_ccb_kill_arg kill_arg;
+ int ret;
+ u64 ca;
+
+ if (dax_ctx == NULL) {
+ dax_err("CCB_INIT ioctl not previously called");
+ return -ENOENT;
+ }
+
+ if (dax_ctx->owner != current) {
+ dax_err("wrong thread");
+ return -EUSERS;
+ }
+
+ if (copy_from_user(&kill_arg, (void __user *)arg, sizeof(kill_arg))) {
+ dax_err("copy_from_user failed");
+ return -EFAULT;
+ }
+
+ dax_dbg("ca_offset=%d", kill_arg.dax_ca_offset);
+
+ if (kill_arg.dax_ca_offset >= dax_ctx->ca_buflen) {
+ dax_err("invalid dax_ca_offset (%d) >= ca_buflen (%d)",
+ kill_arg.dax_ca_offset, dax_ctx->ca_buflen);
+ return -EINVAL;
+ }
+
+ ca = dax_ctx->ca_buf_ra + kill_arg.dax_ca_offset;
+
+ ret = dax_ccb_kill(ca, &kill_arg.dax_kill_res);
+ if (ret != 0) {
+ dax_err("dax_ccb_kill failed (ret=%d)", ret);
+ return ret;
+ }
+
+ dax_kill_info_dbg("kill succeeded on ca_offset %d",
+ kill_arg.dax_ca_offset);
+
+ if (copy_to_user((void __user *)arg, &kill_arg, sizeof(kill_arg))) {
+ dax_err("copy_to_user failed");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int dax_ioctl_ccb_info(void *arg, struct file *f)
+{
+ struct dax_ctx *dax_ctx = (struct dax_ctx *) f->private_data;
+ struct dax_ccb_info_arg info_arg;
+ int ret;
+ u64 ca;
+
+ if (dax_ctx == NULL) {
+ dax_err("CCB_INIT ioctl not previously called");
+ return -ENOENT;
+ }
+
+ if (dax_ctx->owner != current) {
+ dax_err("wrong thread");
+ return -EUSERS;
+ }
+
+ if (copy_from_user(&info_arg, (void __user *)arg, sizeof(info_arg))) {
+ dax_err("copy_from_user failed");
+ return -EFAULT;
+ }
+
+ dax_dbg("ca_offset=%d", info_arg.dax_ca_offset);
+
+ if (info_arg.dax_ca_offset >= dax_ctx->ca_buflen) {
+ dax_err("invalid dax_ca_offset (%d) >= ca_buflen (%d)",
+ info_arg.dax_ca_offset, dax_ctx->ca_buflen);
+ return -EINVAL;
+ }
+
+ ca = dax_ctx->ca_buf_ra + info_arg.dax_ca_offset;
+
+ ret = dax_ccb_info(ca, &info_arg);
+ if (ret != 0) {
+ dax_err("dax_ccb_info failed (ret=%d)", ret);
+ return ret;
+ }
+
+ dax_kill_info_dbg("info succeeded on ca_offset %d",
+ info_arg.dax_ca_offset);
+
+ if (copy_to_user((void __user *)arg, &info_arg, sizeof(info_arg))) {
+ dax_err("copy_to_user failed");
+ return -EFAULT;
+ }
return 0;
}
static void dax_ccb_wait(struct dax_ctx *dax_ctx, int idx)
{
int nretries = 0;
+ u16 kill_res;
+ int ret;
+ u64 ca;
dax_dbg("idx=%d", idx);
udelay(dax_ccb_wait_usec);
if (++nretries >= dax_ccb_wait_retries_max) {
- dax_alert("dax_ctx (0x%p): CCB[%d] did not complete (timed out, wait usec=%d retries=%d). CCB kill will be attempted in future version",
- (void *)dax_ctx, idx, dax_ccb_wait_usec,
+ dax_alert("dax_ctx (0x%p): CCB[%d] did not complete (timed out, wait usec=%d retries=%d). Killing ccb",
+ (void *)dax_ctx, idx, dax_ccb_wait_usec,
dax_ccb_wait_retries_max);
+ ca = dax_ctx->ca_buf_ra + NCCB_TO_CA_BYTE(idx);
+ ret = dax_ccb_kill(ca, &kill_res);
+ if (ret != 0)
+ dax_alert("Killing CCB[%d] failed", idx);
+ else
+ dax_alert("CCB[%d] killed", idx);
+
return;
}
}
#define DAX_SUBMIT_ERR_UNAVAIL 12
#define DAX_SUBMIT_ERR_INTERNAL 13
+/* DAXIOC_CCB_KILL dax_kill_res */
+#define DAX_KILL_COMPLETED 0
+#define DAX_KILL_DEQUEUED 1
+#define DAX_KILL_KILLED 2
+#define DAX_KILL_NOTFOUND 3
+
+/* DAXIOC_CCB_INFO dax_ccb_state */
+#define DAX_CCB_COMPLETED 0
+#define DAX_CCB_ENQUEUED 1
+#define DAX_CCB_INPROGRESS 2
+#define DAX_CCB_NOTFOUND 3
#define DAX_DEV "/dev/dax"
#define DAX_DRIVER_VERSION 3
#define DAXIOC_CA_DEQUEUE _IOWR(DAXIOC, 7, struct dax_ca_dequeue_arg)
/* CCB execution */
#define DAXIOC_CCB_EXEC _IOWR(DAXIOC, 8, struct dax_ccb_exec_arg)
+/* CCB kill */
+#define DAXIOC_CCB_KILL _IOWR(DAXIOC, 9, struct dax_ccb_kill_arg)
+/* CCB info */
+#define DAXIOC_CCB_INFO _IOWR(DAXIOC, 10, struct dax_ccb_info_arg)
/* get driver version */
#define DAXIOC_VERSION _IOWR(DAXIOC, 5, long)
__u32 dcd_len_dequeued;
};
+/*
+ * DAXIOC_CCB_KILL
+ * dax_ca_offset : ca offset for ccb to kill
+ * dax_kill_res : result of kill attempt
+ */
+struct dax_ccb_kill_arg {
+ __u16 dax_ca_offset;
+ __u16 dax_kill_res;
+};
+
+/*
+ * DAXIOC_CCB_INFO
+ * dax_ca_offset : ca offset for ccb to get info
+ * dax_ccb_state : ccb state
+ * dax_inst_num : dax instance number on which ccb is enqueued
+ * (if dax_ccb_state == DAX_CCB_ENQUEUED)
+ * dax_q_num : queue number on which ccb is enqueued
+ * (if dax_ccb_state == DAX_CCB_ENQUEUED)
+ * dax_q_pos : ccb position in queue (if dax_ccb_state == DAX_CCB_ENQUEUED)
+ *
+ * Note: it is possible for CCB_INFO to return dax_ccb_state as
+ * DAX_CCB_NOTFOUND when the CCB is still executing. In order to ensure a CCB
+ * in the NOTFOUND state will never be executed, CCB_KILL must be called on
+ * that CCB.
+ */
+struct dax_ccb_info_arg {
+ __u16 dax_ca_offset;
+ __u16 dax_ccb_state;
+ __u16 dax_inst_num;
+ __u16 dax_q_num;
+ __u16 dax_q_pos;
+};
/* The number of DAX engines per node */
#define DAX_PER_NODE (8)
/* dax_ccb_info()
* TRAP: HV_FAST_TRAP
* FUNCTION: HV_DAX_CCB_INFO
- * ARG0: real address of CCB completion area
+ * ARG0: real address of CCB completion area
* RET0: status (success or error code)
- * RET1 CCB state
- * RET2 queue position
+ * RET1: info array
+ * - RET1[0]: CCB state
+ * - RET1[1]: dax unit
+ * - RET1[2]: queue number
+ * - RET1[3]: queue position
*
- * ERRORS: EWOULDBLOCK etc
- * ENOTSUPPORTED etc
- *
- * Details.
+ * ERRORS: EOK operation successful
+ * EBADALIGN address not 64B aligned
+ * ENORADDR RA in address not valid
+ * EINVAL CA not valid
+ * EWOULDBLOCK info not available for this CCB currently, try
+ * again
+ * ENOACCESS guest cannot use dax
*/
#define HV_DAX_CCB_INFO 0x35
-#define HV_DAX_STATE_COMPLETED 0
-#define HV_DAX_STATE_PENDING 1
-#define HV_DAX_STATE_INPROGRESS 2
-#define HV_DAX_STATE_NOTFOUND 3
+#ifndef __ASSEMBLY__
+unsigned long sun4v_dax_ccb_info(u64 ca, u16 *info_arr);
+#endif
+/* info array byte offsets, each elem is u16 (RET1) */
+#define DAX_CCB_INFO_OFFSET_CCB_STATE 0
+#define DAX_CCB_INFO_OFFSET_DAX_UNIT 2
+#define DAX_CCB_INFO_OFFSET_QUEUE_NUM 4
+#define DAX_CCB_INFO_OFFSET_QUEUE_POS 6
+
+/* CCB state (RET1[0]) */
+#define HV_CCB_STATE_COMPLETED 0
+#define HV_CCB_STATE_ENQUEUED 1
+#define HV_CCB_STATE_INPROGRESS 2
+#define HV_CCB_STATE_NOTFOUND 3
/* dax_ccb_kill()
* TRAP: HV_FAST_TRAP
* FUNCTION: HV_DAX_CCB_KILL
- * ARG0: real address of CCB completion area
+ * ARG0: real address of CCB completion area
* RET0: status (success or error code)
- * RET1 CCB kill status
+ * RET1: CCB kill status
*
- * ERRORS: EWOULDBLOCK etc
- * ENOTSUPPORTED etc
- *
- * Details.
+ * ERRORS: EOK operation successful
+ * EBADALIGN address not 64B aligned
+ * ENORADDR RA in address not valid
+ * EINVAL CA not valid
+ * EWOULDBLOCK kill not available for this CCB currently, try
+ * again
+ * ENOACCESS guest cannot use dax
*/
#define HV_DAX_CCB_KILL 0x36
+#ifndef __ASSEMBLY__
+unsigned long sun4v_dax_ccb_kill(u64 ca, u16 *kill_status);
+#endif
+/* CCB kill status (RET1) */
#define HV_DAX_KILL_COMPLETED 0
#define HV_DAX_KILL_DEQUEUED 1
#define HV_DAX_KILL_KILLED 2