diff mbox

[RFC,2/2] cxlflash: Support for superpipe I/O API

Message ID 1430110237-13307-1-git-send-email-mrochs@linux.vnet.ibm.com (mailing list archive)
State New, archived
Headers show

Commit Message

Matthew R. Ochs April 27, 2015, 4:50 a.m. UTC
Allow userspace applications to obtain CXL resources and exploit the
superpipe functionality of the IBM CXL Flash adapter.

Signed-off-by: Matthew R. Ochs <mrochs@linux.vnet.ibm.com>
Signed-off-by: Manoj N. Kumar <manoj@linux.vnet.ibm.com>
---
 drivers/scsi/cxlflash/Makefile     |    2 +-
 drivers/scsi/cxlflash/main.c       |    1 +
 drivers/scsi/cxlflash/main.h       |    1 +
 drivers/scsi/cxlflash/superpipe.c  | 1673 ++++++++++++++++++++++++++++++++++++
 drivers/scsi/cxlflash/superpipe.h  |   67 ++
 include/uapi/scsi/Kbuild           |    1 +
 include/uapi/scsi/cxlflash_ioctl.h |  133 +++
 7 files changed, 1877 insertions(+), 1 deletion(-)
 create mode 100644 drivers/scsi/cxlflash/superpipe.c
 create mode 100644 drivers/scsi/cxlflash/superpipe.h
 create mode 100644 include/uapi/scsi/cxlflash_ioctl.h
diff mbox

Patch

diff --git a/drivers/scsi/cxlflash/Makefile b/drivers/scsi/cxlflash/Makefile
index 90e9382..5ddcae4 100644
--- a/drivers/scsi/cxlflash/Makefile
+++ b/drivers/scsi/cxlflash/Makefile
@@ -1,4 +1,4 @@ 
 obj-$(CONFIG_CXLFLASH) += cxlflash.o
-cxlflash-y += main.o
+cxlflash-y += main.o superpipe.o
 
 ccflags-y += -DCONFIG_PRINTK
diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c
index 2533ecb..4612ccd 100644
--- a/drivers/scsi/cxlflash/main.c
+++ b/drivers/scsi/cxlflash/main.c
@@ -601,6 +601,7 @@  static struct scsi_host_template driver_template = {
 	.module = THIS_MODULE,
 	.name = CXLFLASH_ADAPTER_NAME,
 	.info = cxlflash_driver_info,
+	.ioctl = cxlflash_ioctl,
 	.proc_name = CXLFLASH_NAME,
 	.queuecommand = cxlflash_queuecommand,
 	.eh_device_reset_handler = cxlflash_eh_device_reset_handler,
diff --git a/drivers/scsi/cxlflash/main.h b/drivers/scsi/cxlflash/main.h
index b79d4f3..67332f1 100644
--- a/drivers/scsi/cxlflash/main.h
+++ b/drivers/scsi/cxlflash/main.h
@@ -120,5 +120,6 @@  static inline u64 lun_to_lunid(u64 lun)
 /*
  * Externs and Prototypes
  */
+int cxlflash_ioctl(struct scsi_device *, int, void __user *);
 
 #endif /* _CXLFLASH_MAIN_H */
diff --git a/drivers/scsi/cxlflash/superpipe.c b/drivers/scsi/cxlflash/superpipe.c
new file mode 100644
index 0000000..9cc7666
--- /dev/null
+++ b/drivers/scsi/cxlflash/superpipe.c
@@ -0,0 +1,1673 @@ 
+/*
+ * CXL Flash Device Driver
+ *
+ * Written by: Manoj N. Kumar <manoj@linux.vnet.ibm.com>, IBM Corporation
+ *             Matthew R. Ochs <mrochs@linux.vnet.ibm.com>, IBM Corporation
+ *
+ * Copyright (C) 2015 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/file.h>
+#include <misc/cxl.h>
+#include <asm/unaligned.h>
+
+#include <scsi/scsi_host.h>
+#include <uapi/scsi/cxlflash_ioctl.h>
+
+#include "sislite.h"
+#include "common.h"
+#include "superpipe.h"
+
+static void marshall_virt_to_resize(struct dk_cxlflash_uvirtual *virt,
+				    struct dk_cxlflash_resize *resize)
+{
+	resize->hdr = virt->hdr;
+	resize->context_id = virt->context_id;
+	resize->rsrc_handle = virt->rsrc_handle;
+	resize->req_size = virt->lun_size;
+	resize->last_lba = virt->last_lba;
+}
+
+static void marshall_rele_to_resize(struct dk_cxlflash_release *release,
+				    struct dk_cxlflash_resize *resize)
+{
+	resize->hdr = release->hdr;
+	resize->context_id = release->context_id;
+	resize->rsrc_handle = release->rsrc_handle;
+}
+
+static void marshall_det_to_rele(struct dk_cxlflash_detach *detach,
+				 struct dk_cxlflash_release *release)
+{
+	release->hdr = detach->hdr;
+	release->context_id = detach->context_id;
+}
+
+static void marshall_clone_to_rele(struct dk_cxlflash_clone *clone,
+				   struct dk_cxlflash_release *release)
+{
+	release->hdr = clone->hdr;
+	release->context_id = clone->context_id_dst;
+}
+
+static int ba_init(struct ba_lun *ba_lun)
+{
+	struct ba_lun_info *lun_info = NULL;
+	int lun_size_au = 0, i = 0;
+	int last_word_underflow = 0;
+	u64 *lam;
+
+	cxlflash_info("Initializing LUN: lun_id = %llX, "
+		      "ba_lun->lsize = %lX, ba_lun->au_size = %lX",
+		      ba_lun->lun_id, ba_lun->lsize, ba_lun->au_size);
+
+	/* Calculate bit map size */
+	lun_size_au = ba_lun->lsize / ba_lun->au_size;
+	if (lun_size_au == 0) {
+		cxlflash_err("Requested LUN size of 0!");
+		return -EINVAL;
+	}
+
+	/* Allocate lun_fino */
+	lun_info = kzalloc(sizeof(struct ba_lun_info), GFP_KERNEL);
+	if (unlikely(!lun_info)) {
+		cxlflash_err("Failed to allocate lun_info for lun_id %llX",
+			     ba_lun->lun_id);
+		return -ENOMEM;
+	}
+
+	lun_info->total_aus = lun_size_au;
+	lun_info->lun_bmap_size = lun_size_au / 64;
+
+	if (lun_size_au % 64)
+		lun_info->lun_bmap_size++;
+
+	/* Allocate bitmap space */
+	lun_info->lun_alloc_map = kzalloc((lun_info->lun_bmap_size *
+					   sizeof(u64)), GFP_KERNEL);
+	if (unlikely(!lun_info->lun_alloc_map)) {
+		cxlflash_err("Failed to allocate lun allocation map: "
+			     "lun_id = %llX", ba_lun->lun_id);
+		kfree(lun_info);
+		return -ENOMEM;
+	}
+
+	/* Initialize the bit map size and set all bits to '1' */
+	lun_info->free_aun_cnt = lun_size_au;
+
+	for (i = 0; i < lun_info->lun_bmap_size; i++)
+		lun_info->lun_alloc_map[i] = (u64) ~ 0;
+
+	/* If the last word not fully utilized, mark extra bits as allocated */
+	last_word_underflow = (lun_info->lun_bmap_size * 64) -
+	    lun_info->free_aun_cnt;
+	if (last_word_underflow > 0) {
+		lam = &lun_info->lun_alloc_map[lun_info->lun_bmap_size - 1];
+		for (i = (63 - last_word_underflow + 1); i < 64; i++)
+			clear_bit(i, (ulong *)lam);
+	}
+
+	/* Initialize high elevator index, low/curr already at 0 from kzalloc */
+	lun_info->free_high_idx = lun_info->lun_bmap_size;
+
+	/* Allocate clone map */
+	lun_info->aun_clone_map = kzalloc((lun_info->total_aus *
+					   sizeof(u8)), GFP_KERNEL);
+	if (unlikely(!lun_info->aun_clone_map)) {
+		cxlflash_err("Failed to allocate clone map: lun_id = %llX",
+			     ba_lun->lun_id);
+		kfree(lun_info->lun_alloc_map);
+		kfree(lun_info);
+		return -ENOMEM;
+	}
+
+	/* Pass the allocated lun info as a handle to the user */
+	ba_lun->ba_lun_handle = (void *)lun_info;
+
+	cxlflash_info("Successfully initialized the LUN: "
+		      "lun_id = %llX, bitmap size = %X, free_aun_cnt = %llX",
+		      ba_lun->lun_id, lun_info->lun_bmap_size,
+		      lun_info->free_aun_cnt);
+	return 0;
+}
+
+static int find_free_range(u32 low,
+			   u32 high,
+			   struct ba_lun_info *lun_info, int *bit_word)
+{
+	int i;
+	u64 bit_pos = -1;
+	ulong *lam;
+
+	for (i = low; i < high; i++)
+		if (lun_info->lun_alloc_map[i] != 0) {
+			lam = (ulong *)&lun_info->lun_alloc_map[i];
+			bit_pos = find_first_bit(lam, sizeof(u64));
+
+			cxlflash_dbg("Found free bit %llX in lun "
+				     "map entry %llX at bitmap index = %X",
+				     bit_pos, lun_info->lun_alloc_map[i], i);
+
+			*bit_word = i;
+			lun_info->free_aun_cnt--;
+			clear_bit(bit_pos, lam);
+			break;
+		}
+
+	return bit_pos;
+}
+
+static u64 ba_alloc(struct ba_lun *ba_lun)
+{
+	u64 bit_pos = -1;
+	int bit_word = 0;
+	struct ba_lun_info *lun_info = NULL;
+
+	lun_info = (struct ba_lun_info *)ba_lun->ba_lun_handle;
+
+	cxlflash_dbg("Received block allocation request: "
+		     "lun_id = %llX, free_aun_cnt = %llX",
+		     ba_lun->lun_id, lun_info->free_aun_cnt);
+
+	if (lun_info->free_aun_cnt == 0) {
+		cxlflash_err("No space left on LUN: lun_id = %llX",
+			     ba_lun->lun_id);
+		return -1ULL;
+	}
+
+	/* Search to find a free entry, curr->high then low->curr */
+	bit_pos = find_free_range(lun_info->free_curr_idx,
+				  lun_info->free_high_idx, lun_info, &bit_word);
+	if (bit_pos == -1) {
+		bit_pos = find_free_range(lun_info->free_low_idx,
+					  lun_info->free_curr_idx,
+					  lun_info, &bit_word);
+		if (bit_pos == -1) {
+			cxlflash_err
+			    ("Could not find an allocation unit on LUN: "
+			     "lun_id = %llX", ba_lun->lun_id);
+			return -1ULL;
+		}
+	}
+
+	/* Update the free_curr_idx */
+	if (bit_pos == 63)
+		lun_info->free_curr_idx = bit_word + 1;
+	else
+		lun_info->free_curr_idx = bit_word;
+
+	cxlflash_dbg("Allocating AU number %llX, on lun_id %llX, "
+		     "free_aun_cnt = %llX", ((bit_word * 64) + bit_pos),
+		     ba_lun->lun_id, lun_info->free_aun_cnt);
+
+	return (u64) ((bit_word * 64) + bit_pos);
+}
+
+static int validate_alloc(struct ba_lun_info *lun_info, u64 aun)
+{
+	int idx = 0, bit_pos = 0;
+
+	idx = aun / 64;
+	bit_pos = aun % 64;
+
+	if (test_bit(bit_pos, (ulong *)&lun_info->lun_alloc_map[idx]))
+		return -1;
+
+	return 0;
+}
+
+static int ba_free(struct ba_lun *ba_lun, u64 to_free)
+{
+	int idx = 0, bit_pos = 0;
+	struct ba_lun_info *lun_info = NULL;
+
+	lun_info = (struct ba_lun_info *)ba_lun->ba_lun_handle;
+
+	if (validate_alloc(lun_info, to_free)) {
+		cxlflash_err("The AUN %llX is not allocated on lun_id %llX",
+			     to_free, ba_lun->lun_id);
+		return -1;
+	}
+
+	cxlflash_dbg("Received a request to free AU %llX on lun_id %llX, "
+		     "free_aun_cnt = %llX", to_free, ba_lun->lun_id,
+		     lun_info->free_aun_cnt);
+
+	if (lun_info->aun_clone_map[to_free] > 0) {
+		cxlflash_info("AUN %llX on lun_id %llX has been cloned. Clone "
+			      "count = %X",
+			      to_free, ba_lun->lun_id,
+			      lun_info->aun_clone_map[to_free]);
+		lun_info->aun_clone_map[to_free]--;
+		return 0;
+	}
+
+	idx = to_free / 64;
+	bit_pos = to_free % 64;
+
+	set_bit(bit_pos, (ulong *)&lun_info->lun_alloc_map[idx]);
+	lun_info->free_aun_cnt++;
+
+	if (idx < lun_info->free_low_idx)
+		lun_info->free_low_idx = idx;
+	else if (idx > lun_info->free_high_idx)
+		lun_info->free_high_idx = idx;
+
+	cxlflash_dbg("Successfully freed AU at bit_pos %X, bit map index %X on "
+		     "lun_id %llX, free_aun_cnt = %llX", bit_pos, idx,
+		     ba_lun->lun_id, lun_info->free_aun_cnt);
+
+	return 0;
+}
+
+static int ba_clone(struct ba_lun *ba_lun, u64 to_clone)
+{
+	struct ba_lun_info *lun_info =
+	    (struct ba_lun_info *)ba_lun->ba_lun_handle;
+
+	if (validate_alloc(lun_info, to_clone)) {
+		cxlflash_err("AUN %llX is not allocated on lun_id %llX",
+			     to_clone, ba_lun->lun_id);
+		return -1;
+	}
+
+	cxlflash_info("Received a request to clone AUN %llX on lun_id %llX",
+		      to_clone, ba_lun->lun_id);
+
+	if (lun_info->aun_clone_map[to_clone] == MAX_AUN_CLONE_CNT) {
+		cxlflash_err
+		    ("AUN %llX on lun_id %llX has hit max clones already",
+		     to_clone, ba_lun->lun_id);
+		return -1;
+	}
+
+	lun_info->aun_clone_map[to_clone]++;
+
+	return 0;
+}
+
+static u64 ba_space(struct ba_lun *ba_lun)
+{
+	struct ba_lun_info *lun_info =
+	    (struct ba_lun_info *)ba_lun->ba_lun_handle;
+
+	return lun_info->free_aun_cnt;
+}
+
+static int cxlflash_afu_attach(struct cxlflash *cxlflash, u64 context_id)
+{
+	struct afu *afu = cxlflash->afu;
+	struct ctx_info *ctx_info = &afu->ctx_info[context_id];
+	int rc = 0;
+	u64 reg;
+
+	/* restrict user to read/write cmds in translated
+	 * mode. User has option to choose read and/or write
+	 * permissions again in mc_open.
+	 */
+	(void)readq_be(&ctx_info->ctrl_map->mbox_r);	/* unlock ctx_cap */
+	writeq_be((SISL_CTX_CAP_READ_CMD | SISL_CTX_CAP_WRITE_CMD),
+		  &ctx_info->ctrl_map->ctx_cap);
+
+	reg = readq_be(&ctx_info->ctrl_map->ctx_cap);
+
+	/* if the write failed, the ctx must have been
+	 * closed since the mbox read and the ctx_cap
+	 * register locked up.  fail the registration
+	 */
+	if (reg != (SISL_CTX_CAP_READ_CMD | SISL_CTX_CAP_WRITE_CMD)) {
+		cxlflash_err("ctx may be closed reg=%llx", reg);
+		rc = -EAGAIN;
+		goto out;
+	}
+
+	/* the context gets a dedicated RHT tbl */
+	ctx_info->rht_info = &afu->rht_info[context_id];
+	ctx_info->rht_info->ref_cnt = 1;
+	memset(ctx_info->rht_info->rht_start, 0,
+	       sizeof(struct sisl_rht_entry) * MAX_RHT_PER_CONTEXT);
+	/* make clearing of the RHT visible to AFU before
+	 * MMIO
+	 */
+	smp_wmb();
+
+	/* set up MMIO registers pointing to the RHT */
+	writeq_be((u64) ctx_info->rht_info->rht_start,
+		  &ctx_info->ctrl_map->rht_start);
+	writeq_be(SISL_RHT_CNT_ID((u64) MAX_RHT_PER_CONTEXT,
+				  (u64) (afu->ctx_hndl)),
+		  &ctx_info->ctrl_map->rht_cnt_id);
+	ctx_info->ref_cnt = 1;
+out:
+	cxlflash_info("returning rc=%d", rc);
+	return rc;
+
+}
+
+static int cxlflash_init_ba(struct lun_info *lun_info)
+{
+	int rc = 0;
+	struct blka *blka = &lun_info->blka;
+
+	memset(blka, 0, sizeof(*blka));
+	mutex_init(&blka->mutex);
+
+	blka->ba_lun.lun_id = lun_info->lun_id;
+	blka->ba_lun.lsize = lun_info->max_lba + 1;
+	blka->ba_lun.lba_size = lun_info->blk_len;
+
+	blka->ba_lun.au_size = MC_CHUNK_SIZE;
+	blka->nchunk = blka->ba_lun.lsize / MC_CHUNK_SIZE;
+
+	rc = ba_init(&blka->ba_lun);
+	if (rc) {
+		cxlflash_err("cannot init block_alloc, rc=%d", rc);
+		goto cxlflash_init_ba_exit;
+	}
+
+cxlflash_init_ba_exit:
+	cxlflash_info("returning rc=%d lun_info=%p", rc, lun_info);
+	return rc;
+}
+
+int cxlflash_cxl_release(struct inode *inode, struct file *file)
+{
+	struct cxl_context *ctx = cxl_fops_get_context(file);
+	struct cxlflash *cxlflash = container_of(file->f_op, struct cxlflash,
+						   cxl_fops);
+	int context_id = cxl_process_element(ctx);
+
+	if (context_id < 0) {
+		cxlflash_err("context %d closed", context_id);
+		return 0;
+	}
+
+	cxlflash_info("close(%d) for context %d",
+		      cxlflash->per_context[context_id].lfd, context_id);
+
+	return cxl_fd_release(inode, file);
+}
+
+const struct file_operations cxlflash_cxl_fops = {
+	.owner = THIS_MODULE,
+	.release = cxlflash_cxl_release,
+};
+
+/*
+ * NAME:        cxlflash_disk_attach
+ *
+ * FUNCTION:    attach a LUN to context
+ *
+ * INPUTS:
+ *              sdev       - Pointer to scsi device structure
+ *              arg        - Pointer to ioctl specific structure
+ *
+ * OUTPUTS:
+ *              context_id - Unique context index
+ *              adap_fd    - New file descriptor for user
+ *
+ * RETURNS:
+ *              0           - Success
+ *              errno       - Failure
+ *
+ * NOTES:
+ *              When successful:
+ *               a. initialize AFU for this context
+ *
+ */
+static int cxlflash_disk_attach(struct scsi_device *sdev,
+				struct dk_cxlflash_attach *attach)
+{
+	struct cxlflash *cxlflash = (struct cxlflash *)sdev->host->hostdata;
+	struct afu *afu = cxlflash->afu;
+	struct lun_info *lun_info = sdev->hostdata;
+	struct cxl_ioctl_start_work *work;
+	int rc = 0;
+	u32 perms;
+	int context_id;
+	struct file *file;
+
+	struct cxl_context *ctx;
+
+	int fd = -1;
+
+	/* On first attach set fileops */
+	if (cxlflash->num_user_contexts == 0)
+		cxlflash->cxl_fops = cxlflash_cxl_fops;
+
+	if (lun_info->max_lba == 0) {
+		cxlflash_info("No capacity info yet for this LUN "
+			      "(%016llX)", lun_info->lun_id);
+		read_cap16(afu, lun_info, sdev->channel + 1);
+		cxlflash_info("LBA = %016llX", lun_info->max_lba);
+		cxlflash_info("BLK_LEN = %08X", lun_info->blk_len);
+		rc = cxlflash_init_ba(lun_info);
+		if (rc) {
+			cxlflash_err("call to cxlflash_init_ba failed "
+				     "rc=%d!", rc);
+			rc = -ENOMEM;
+			goto out;
+		}
+	}
+
+	ctx = cxl_dev_context_init(cxlflash->dev);
+	if (!ctx) {
+		cxlflash_err("Could not initialize context");
+		rc = -ENODEV;
+		goto out;
+	}
+
+	context_id = cxl_process_element(ctx);
+	if ((context_id > MAX_CONTEXT) || (context_id < 0)) {
+		cxlflash_err("context_id (%u) invalid!", context_id);
+		rc = -EPERM;
+		goto out;
+	}
+	//BUG_ON(cxlflash->per_context[context_id].lfd != -1);
+	//BUG_ON(cxlflash->per_context[context_id].pid != 0);
+
+	/*
+	 * Create and attach a new file descriptor. This must be the last
+	 * statement as once this is run, the file descritor is visible to
+	 * userspace and can't be undone. No error paths after this as we
+	 * can't free the fd safely.
+	 */
+	work = &cxlflash->per_context[context_id].work;
+	memset(work, 0, sizeof(*work));
+	work->num_interrupts = attach->num_interrupts;
+	work->flags = CXL_START_WORK_NUM_IRQS;
+
+	file = cxl_get_fd(ctx, &cxlflash->cxl_fops, &fd);
+	if (fd < 0) {
+		rc = -ENODEV;
+		cxlflash_err("Could not get file descriptor");
+		goto err1;
+	}
+
+	rc = cxl_start_work(ctx, work);
+	if (rc) {
+		cxlflash_err("Could not start context rc=%d", rc);
+		goto err2;
+	}
+
+	rc = cxlflash_afu_attach(cxlflash, context_id);
+	if (rc) {
+		cxlflash_err("Could not attach AFU rc %d", rc);
+		goto err3;
+	}
+
+	/* No error paths after installing the fd */
+	fd_install(fd, file);
+
+	cxlflash->num_user_contexts++;
+	cxlflash->per_context[context_id].lfd = fd;
+	cxlflash->per_context[context_id].pid = current->pid;
+	cxlflash->per_context[context_id].ctx = ctx;
+
+	/* Translate read/write O_* flags from fnctl.h to AFU permission bits */
+	perms = ((attach->hdr.flags + 1) & 0x3);
+	afu->ctx_info[context_id].rht_info->perms = perms;
+
+	attach->hdr.return_flags = 0;
+	attach->context_id = context_id;
+	attach->block_size = lun_info->blk_len;
+	attach->mmio_size = sizeof(afu->afu_map->hosts[0].harea);
+	attach->last_lba = lun_info->max_lba;
+	attach->max_xfer = sdev->host->max_sectors;
+
+out:
+	attach->adap_fd = fd;
+
+	cxlflash_info("returning fd=%d bs=%lld rc=%d llba=%lld",
+		      fd, attach->block_size, rc, attach->last_lba);
+	return rc;
+
+err3:
+	cxl_stop_context(ctx);
+err2:
+	fput(file);
+	put_unused_fd(fd);
+	fd = -1;
+err1:
+	cxl_release_context(ctx);
+	goto out;
+}
+
+static struct ctx_info *get_validated_context(struct cxlflash *cxlflash,
+					      u64 ctxid, bool clone_path)
+{
+	struct afu *afu = cxlflash->afu;
+	struct ctx_info *ctx_info = NULL;
+	bool mc_override = ctxid == afu->ctx_hndl;
+	pid_t pid = current->pid, ctxpid = 0;
+
+	if (unlikely(clone_path))
+		pid = current->parent->pid;
+
+	if (likely(ctxid < MAX_CONTEXT)) {
+		ctx_info = &afu->ctx_info[ctxid];
+
+		if (checkpid) {
+			ctxpid = cxlflash->per_context[ctxid].pid;
+
+			if ((pid != ctxpid) && (!mc_override))
+				ctx_info = NULL;
+		}
+	}
+
+	cxlflash_dbg("ctxid=%llu ctx_info=%p ctxpid=%u pid=%u clone_path=%d",
+		     ctxid, ctx_info, ctxpid, pid, clone_path);
+
+	return ctx_info;
+}
+
+/* Checkout a free/empty RHT entry */
+static struct sisl_rht_entry *rhte_checkout(struct cxlflash *cxlflash,
+					    u64 context_id)
+{
+	struct ctx_info *ctx_info;
+	struct rht_info *rht_info = NULL;
+	struct sisl_rht_entry *rht_entry = NULL;
+	int i;
+
+	ctx_info = get_validated_context(cxlflash, context_id, false);
+	if (unlikely(!ctx_info)) {
+		cxlflash_err("Invalid context! (%llu)", context_id);
+		goto out;
+	}
+
+	rht_info = ctx_info->rht_info;
+
+	cxlflash_info("ctx 0x%llx ctxinfo %p rhtinfo %p",
+		      context_id, ctx_info, rht_info);
+
+	/* find a free RHT entry */
+	for (i = 0; i < MAX_RHT_PER_CONTEXT; i++)
+		if (rht_info->rht_start[i].nmask == 0) {
+			rht_entry = &rht_info->rht_start[i];
+			break;
+		}
+
+	cxlflash_dbg("i %d rhti %p rhte %p", i, rht_info, rht_entry);
+
+	/* No free entries means we've reached max opens allowed per context */
+	if (unlikely(!rht_entry)) {
+		cxlflash_err("No free entries found for context id %llu",
+			     context_id);
+		goto out;
+	}
+
+out:
+	cxlflash_info("returning rht_entry=%p", rht_entry);
+	return rht_entry;
+}
+
+static void rhte_checkin(struct sisl_rht_entry *rht_entry)
+{
+	rht_entry->nmask = 0;
+	rht_entry->fp = 0;
+}
+
+void cxlflash_rht_format1(struct sisl_rht_entry *rht_entry,
+			  u64 lun_id, u32 perm)
+{
+	/*
+	 * Populate the Format 1 RHT entry for direct access (physical
+	 * LUN) using the synchronization sequence defined in the
+	 * SISLite specification.
+	 */
+	struct sisl_rht_entry_f1 dummy = { 0 };
+	struct sisl_rht_entry_f1 *rht_entry_f1 =
+	    (struct sisl_rht_entry_f1 *)rht_entry;
+	memset(rht_entry_f1, 0, sizeof(struct sisl_rht_entry_f1));
+	rht_entry_f1->fp = SISL_RHT_FP(1U, 0);
+	smp_wmb();
+
+	rht_entry_f1->lun_id = lun_id;
+	smp_wmb();
+
+	/*
+	 * Use a dummy RHT Format 1 entry to build the second dword
+	 * of the entry that must be populated in a single write when
+	 * enabled (valid bit set to TRUE).
+	 */
+	dummy.valid = 0x80;
+	dummy.fp = SISL_RHT_FP(1U, perm);
+#if 0				/* XXX - check with Andy/Todd b/c this doesn't work */
+	if (internal_lun)
+		dummy.port_sel = 0x1;
+	else
+#endif
+		dummy.port_sel = 0x3;
+	rht_entry_f1->dw = dummy.dw;
+
+	smp_wmb();
+
+	return;
+}
+
+static int grow_lxt(struct afu *afu,
+		    struct lun_info *lun_info,
+		    ctx_hndl_t ctx_hndl_u,
+		    res_hndl_t res_hndl_u,
+		    struct sisl_rht_entry *rht_entry,
+		    u64 delta, u64 * act_new_size)
+{
+	struct sisl_lxt_entry *lxt = NULL, *lxt_old = NULL;
+	unsigned int av_size;
+	unsigned int ngrps, ngrps_old;
+	u64 aun;		/* chunk# allocated by block allocator */
+	int i;
+	struct blka *blka = &lun_info->blka;
+
+	/*
+	 * Check what is available in the block allocator before re-allocating
+	 * LXT array. This is done up front under the mutex which must not be
+	 * released until after allocation is complete.
+	 */
+	mutex_lock(&blka->mutex);
+	av_size = ba_space(&blka->ba_lun);
+	if (av_size < delta)
+		delta = av_size;
+
+	lxt_old = rht_entry->lxt_start;
+	ngrps_old = LXT_NUM_GROUPS(rht_entry->lxt_cnt);
+	ngrps = LXT_NUM_GROUPS(rht_entry->lxt_cnt + delta);
+
+	if (ngrps != ngrps_old) {
+		/* reallocate to fit new size */
+		lxt = kzalloc((sizeof(*lxt) * LXT_GROUP_SIZE * ngrps),
+				GFP_KERNEL);
+		if (unlikely(!lxt)) {
+			mutex_unlock(&blka->mutex);
+			return -ENOMEM;
+		}
+
+		/* copy over all old entries */
+		memcpy(lxt, lxt_old, (sizeof(*lxt) *
+					  rht_entry->lxt_cnt));
+	} else
+		lxt = lxt_old;
+
+	/* nothing can fail from now on */
+	*act_new_size = rht_entry->lxt_cnt + delta;
+
+	/* add new entries to the end */
+	for (i = rht_entry->lxt_cnt; i < *act_new_size; i++) {
+		/*
+		 * Due to the earlier check of available space, ba_alloc
+		 * cannot fail here. If it did due to internal error,
+		 * leave a rlba_base of -1u which will likely be a
+		 * invalid LUN (too large).
+		 */
+		aun = ba_alloc(&blka->ba_lun);
+		if ((aun == -1ULL) || (aun >= blka->nchunk))
+			cxlflash_err("ba_alloc error: allocated chunk# %llX, "
+				     "max %llX", aun, blka->nchunk - 1);
+
+		/* select both ports, use r/w perms from RHT */
+		lxt[i].rlba_base = ((aun << MC_CHUNK_SHIFT) |
+				      (lun_info->lun_index <<
+				       LXT_LUNIDX_SHIFT) | 0x33);
+	}
+
+	mutex_unlock(&blka->mutex);
+
+	smp_wmb();		/* make lxt updates visible */
+
+	/* Now sync up AFU - this can take a while */
+	rht_entry->lxt_start = lxt;	/* even if lxt didn't change */
+	smp_wmb();
+
+	rht_entry->lxt_cnt = *act_new_size;
+	smp_wmb();
+
+	cxlflash_afu_sync(afu, ctx_hndl_u, res_hndl_u, AFU_LW_SYNC);
+
+	/* free old lxt if reallocated */
+	if (lxt != lxt_old)
+		kfree(lxt_old);
+	cxlflash_info("returning");
+	return 0;
+}
+
+static int shrink_lxt(struct afu *afu,
+		      struct lun_info *lun_info,
+		      ctx_hndl_t ctx_hndl_u,
+		      res_hndl_t res_hndl_u,
+		      struct sisl_rht_entry *rht_entry,
+		      u64 delta, u64 * act_new_size)
+{
+	struct sisl_lxt_entry *lxt, *lxt_old;
+	unsigned int ngrps, ngrps_old;
+	u64 aun;		/* chunk# allocated by block allocator */
+	int i;
+	struct blka *blka = &lun_info->blka;
+
+	lxt_old = rht_entry->lxt_start;
+	ngrps_old = LXT_NUM_GROUPS(rht_entry->lxt_cnt);
+	ngrps = LXT_NUM_GROUPS(rht_entry->lxt_cnt - delta);
+
+	if (ngrps != ngrps_old) {
+		/* reallocate to fit new size unless new size is 0 */
+		if (ngrps) {
+			lxt = kzalloc((sizeof(*lxt) * LXT_GROUP_SIZE *
+					 ngrps), GFP_KERNEL);
+			if (unlikely(!lxt))
+				return -ENOMEM;
+
+			/* copy over old entries that will remain */
+			memcpy(lxt, lxt_old, (sizeof(*lxt) *
+						  (rht_entry->lxt_cnt -
+						   delta)));
+		} else
+			lxt = NULL;
+	} else
+		lxt = lxt_old;
+
+	/* nothing can fail from now on */
+	*act_new_size = rht_entry->lxt_cnt - delta;
+
+	/* Now sync up AFU - this can take a while */
+	rht_entry->lxt_cnt = *act_new_size;
+	smp_wmb();		/* also makes lxt updates visible */
+
+	rht_entry->lxt_start = lxt;	/* even if lxt didn't change */
+	smp_wmb();
+
+	cxlflash_afu_sync(afu, ctx_hndl_u, res_hndl_u, AFU_HW_SYNC);
+
+	/* free LBAs allocated to freed chunks */
+	mutex_lock(&blka->mutex);
+	for (i = delta - 1; i >= 0; i--) {
+		aun = (lxt_old[*act_new_size + i].rlba_base >>
+		       MC_CHUNK_SHIFT);
+		ba_free(&blka->ba_lun, aun);
+	}
+	mutex_unlock(&blka->mutex);
+
+	/* free old lxt if reallocated */
+	if (lxt != lxt_old)
+		kfree(lxt_old);
+	cxlflash_info("returning");
+	return 0;
+}
+
+/*
+ * NAME:	cxlflash_vlun_resize()
+ *
+ * FUNCTION:	Resize a resource handle by changing the RHT entry and LXT
+ *		Tbl it points to. Synchronize all contexts that refer to
+ *		the RHT.
+ *
+ * INPUTS:
+ *              sdev       - Pointer to scsi device structure
+ *              arg        - Pointer to ioctl specific structure
+ *
+ * OUTPUTS:
+ *		act_new_size	- pointer to actual new size in chunks
+ *
+ * RETURNS:
+ *		0	- Success
+ *		errno	- Failure
+ *
+ * NOTES:
+ *		Setting new_size=0 will clear LXT_START and LXT_CNT fields
+ *		in the RHT entry.
+ */
+static int cxlflash_vlun_resize(struct scsi_device *sdev,
+				struct dk_cxlflash_resize *resize)
+{
+	struct cxlflash *cxlflash = (struct cxlflash *)sdev->host->hostdata;
+	struct lun_info *lun_info = sdev->hostdata;
+	struct afu *afu = cxlflash->afu;
+
+	u64 act_new_size = 0;
+	res_hndl_t res_hndl = resize->rsrc_handle;
+	u64 new_size;
+	u64 nsectors;
+
+	struct ctx_info *ctx_info;
+	struct rht_info *rht_info;
+	struct sisl_rht_entry *rht_entry;
+
+	int rc = 0;
+
+	/* req_size is always assumed to be in 4k blocks. So we have to convert
+	 * it from 4k to chunk size
+	 */
+	nsectors = (resize->req_size * CXLFLASH_BLOCK_SIZE) /
+	    (lun_info->blk_len);
+	new_size = (nsectors + MC_CHUNK_SIZE - 1) / MC_CHUNK_SIZE;
+
+	cxlflash_info("context=0x%llx res_hndl=0x%llx, req_size=0x%llx,"
+		      "new_size=%llx", resize->context_id,
+		      resize->rsrc_handle, resize->req_size, new_size);
+
+	if (lun_info->mode != MODE_VIRTUAL) {
+		cxlflash_err("cannot resize lun that is not virtual %d",
+			     lun_info->mode);
+		rc = -EINVAL;
+		goto out;
+
+	}
+
+	ctx_info = get_validated_context(cxlflash, resize->context_id, false);
+	if (unlikely(!ctx_info)) {
+		cxlflash_err("Invalid context! (%llu)", resize->context_id);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	rht_info = ctx_info->rht_info;
+
+	if (res_hndl < MAX_RHT_PER_CONTEXT) {
+		rht_entry = &rht_info->rht_start[res_hndl];
+
+		if (rht_entry->nmask == 0) {	/* not open */
+			cxlflash_err("not open rhti %p rhte %p",
+				     rht_info, rht_entry);
+			rc = -EINVAL;
+			goto out;
+		}
+
+		if (new_size > rht_entry->lxt_cnt)
+			grow_lxt(afu,
+				 lun_info,
+				 resize->context_id,
+				 res_hndl,
+				 rht_entry,
+				 new_size - rht_entry->lxt_cnt,
+				 &act_new_size);
+		else if (new_size < rht_entry->lxt_cnt)
+			shrink_lxt(afu,
+				   lun_info,
+				   resize->context_id,
+				   res_hndl,
+				   rht_entry,
+				   rht_entry->lxt_cnt - new_size,
+				   &act_new_size);
+		else
+			act_new_size = new_size;
+	} else {
+		cxlflash_err("res_hndl %d invalid", res_hndl);
+		rc = -EINVAL;
+	}
+	resize->hdr.return_flags = 0;
+	resize->last_lba = (((act_new_size * MC_CHUNK_SIZE *
+			    lun_info->blk_len) / CXLFLASH_BLOCK_SIZE) - 1);
+
+out:
+	cxlflash_info("resized to %lld returning rc=%d", resize->last_lba, rc);
+	return rc;
+}
+
+/*
+ * NAME:        cxlflash_disk_open
+ *
+ * FUNCTION:    open a virtual lun of specified size
+ *
+ * INPUTS:
+ *              sdev       - Pointer to scsi device structure
+ *              arg        - Pointer to ioctl specific structure
+ *
+ * OUTPUTS:
+ *              none
+ *
+ * RETURNS:
+ *              0           - Success
+ *              errno       - Failure
+ *
+ * NOTES:
+ *              When successful:
+ *               a. find a free RHT entry
+ *
+ */
+static int cxlflash_disk_open(struct scsi_device *sdev,
+			      void *arg, enum open_mode_type mode)
+{
+	struct cxlflash *cxlflash = (struct cxlflash *)sdev->host->hostdata;
+	struct afu *afu = cxlflash->afu;
+	struct lun_info *lun_info = sdev->hostdata;
+
+	struct dk_cxlflash_uvirtual *virt = (struct dk_cxlflash_uvirtual *)arg;
+	struct dk_cxlflash_udirect *pphys = (struct dk_cxlflash_udirect *)arg;
+	struct dk_cxlflash_resize resize;
+
+	u32 perms;
+	u64 context_id;
+	u64 lun_size = 0;
+	u64 last_lba = 0;
+	u64 rsrc_handle = -1;
+
+	int rc = 0;
+
+	struct ctx_info *ctx_info;
+	struct rht_info *rht_info = NULL;
+	struct sisl_rht_entry *rht_entry = NULL;
+
+	if (mode == MODE_VIRTUAL) {
+		context_id = virt->context_id;
+		lun_size = virt->lun_size;
+		/* Initialize to invalid value */
+		virt->rsrc_handle = -1;
+	} else if (mode == MODE_PHYSICAL) {
+		context_id = pphys->context_id;
+		/* Initialize to invalid value */
+		pphys->rsrc_handle = -1;
+	} else {
+		cxlflash_err("unknown mode %d", mode);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	spin_lock(&lun_info->slock);
+	if (lun_info->mode == MODE_NONE)
+		lun_info->mode = mode;
+	else if (lun_info->mode != mode) {
+		cxlflash_err
+		    ("disk already opened in mode %d, mode requested %d",
+		     lun_info->mode, mode);
+		rc = -EINVAL;
+		spin_unlock(&lun_info->slock);
+		goto out;
+	}
+	spin_unlock(&lun_info->slock);
+
+	cxlflash_info("context=0x%llx ls=0x%llx", context_id, lun_size);
+
+	rht_entry = rhte_checkout(cxlflash, context_id);
+	if (!rht_entry) {
+		cxlflash_err("too many opens for this context");
+		rc = -EMFILE;	/* too many opens  */
+		goto out;
+	}
+
+	ctx_info = get_validated_context(cxlflash, context_id, false);
+	if (unlikely(!ctx_info)) {
+		cxlflash_err("Invalid context! (%llu)", context_id);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	rht_info = ctx_info->rht_info;
+
+	/* User specified permission on attach */
+	perms = rht_info->perms;
+
+	rsrc_handle = (rht_entry - rht_info->rht_start);
+
+	if (mode == MODE_VIRTUAL) {
+		rht_entry->nmask = MC_RHT_NMASK;
+		rht_entry->fp = SISL_RHT_FP(0U, perms);
+		/* format 0 & perms */
+
+		if (lun_size != 0) {
+			marshall_virt_to_resize(virt, &resize);
+			resize.rsrc_handle = rsrc_handle;
+			rc = cxlflash_vlun_resize(sdev, &resize);
+			if (rc) {
+				cxlflash_err("resize failed rc %d", rc);
+				goto out;
+			}
+			last_lba = resize.last_lba;
+		}
+		virt->hdr.return_flags = 0;
+		virt->last_lba = last_lba;
+		virt->rsrc_handle = rsrc_handle;
+	} else if (mode == MODE_PHYSICAL) {
+		cxlflash_rht_format1(rht_entry, lun_info->lun_id, perms);
+		cxlflash_afu_sync(afu, context_id, rsrc_handle, AFU_LW_SYNC);
+
+		last_lba = lun_info->max_lba;
+		pphys->hdr.return_flags = 0;
+		pphys->last_lba = last_lba;
+		pphys->rsrc_handle = rsrc_handle;
+	}
+
+out:
+	cxlflash_info("returning handle 0x%llx rc=%d llba %lld",
+		      rsrc_handle, rc, last_lba);
+	return rc;
+}
+
+/*
+ * NAME:        cxlflash_disk_release
+ *
+ * FUNCTION:    Close a virtual LBA space setting it to 0 size and
+ *              marking the res_hndl as free/closed.
+ *
+ * INPUTS:
+ *              sdev       - Pointer to scsi device structure
+ *              arg        - Pointer to ioctl specific structure
+ *
+ * OUTPUTS:
+ *              none
+ *
+ * RETURNS:
+ *              0           - Success
+ *              errno       - Failure
+ *
+ * NOTES:
+ *              When successful, the RHT entry is cleared.
+ */
+static int cxlflash_disk_release(struct scsi_device *sdev,
+				 struct dk_cxlflash_release *release)
+{
+	struct cxlflash *cxlflash = (struct cxlflash *)sdev->host->hostdata;
+	struct lun_info *lun_info = sdev->hostdata;
+	struct afu *afu = cxlflash->afu;
+
+	struct dk_cxlflash_resize size;
+	res_hndl_t res_hndl = release->rsrc_handle;
+
+	int rc = 0;
+
+	struct ctx_info *ctx_info;
+	struct rht_info *rht_info;
+	struct sisl_rht_entry *rht_entry;
+
+	cxlflash_info("context=0x%llx res_hndl=0x%llx",
+		      release->context_id, release->rsrc_handle);
+
+	ctx_info =
+	    get_validated_context(cxlflash, release->context_id, false);
+	if (unlikely(!ctx_info)) {
+		cxlflash_err("Invalid context! (%llu)", release->context_id);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	rht_info = ctx_info->rht_info;
+
+	if (res_hndl < MAX_RHT_PER_CONTEXT) {
+		rht_entry = &rht_info->rht_start[res_hndl];
+		if (rht_entry->nmask == 0) {	/* not open */
+			rc = -EINVAL;
+			cxlflash_err("not open");
+			goto out;
+		}
+
+		/*
+		 * Resize to 0 for virtual LUNS by setting the size
+		 * to 0. This will clear LXT_START and LXT_CNT fields
+		 * in the RHT entry and properly sync with the AFU.
+		 * Afterwards we clear the remaining fields.
+		 */
+		if (lun_info->mode == MODE_VIRTUAL) {
+			marshall_rele_to_resize(release, &size);
+			size.req_size = 0;
+			rc = cxlflash_vlun_resize(sdev, &size);
+			if (rc) {
+				cxlflash_err("resize failed rc %d", rc);
+				goto out;
+			}
+			rhte_checkin(rht_entry);
+		} else if (lun_info->mode == MODE_PHYSICAL) {
+			/*
+			 * Clear the Format 1 RHT entry for direct access 
+			 * (physical LUN) using the synchronization sequence 
+			 * defined in the SISLite specification.
+			 */
+			struct sisl_rht_entry_f1 *rht_entry_f1 =
+			    (struct sisl_rht_entry_f1 *)rht_entry;
+
+			rht_entry_f1->valid = 0;
+			smp_wmb();
+
+			rht_entry_f1->lun_id = 0;
+			smp_wmb();
+
+			rht_entry_f1->dw = 0;
+			smp_wmb();
+			cxlflash_afu_sync(afu, release->context_id, res_hndl,
+					  AFU_HW_SYNC);
+		}
+
+		/* now the RHT entry is all cleared */
+		rc = 0;
+		rht_info->ref_cnt--;
+	} else {
+		rc = -EINVAL;
+		cxlflash_info("resource handle invalid %d", res_hndl);
+	}
+
+out:
+	cxlflash_info("returning rc=%d", rc);
+	return rc;
+}
+
+/*
+ * NAME:        cxlflash_disk_detach
+ *
+ * FUNCTION:    Unregister a user AFU context with master.
+ *
+ * INPUTS:
+ *              sdev       - Pointer to scsi device structure
+ *              arg        - Pointer to ioctl specific structure
+ *
+ * OUTPUTS:
+ *              none
+ *
+ * RETURNS:
+ *              0           - Success
+ *              errno       - Failure
+ *
+ * NOTES:
+ *              When successful:
+ *               a. RHT_START, RHT_CNT & CTX_CAP registers for the
+ *                  context are cleared
+ *               b. There is no need to clear RHT entries since
+ *                  RHT_CNT=0.
+ */
+static int cxlflash_disk_detach(struct scsi_device *sdev,
+				struct dk_cxlflash_detach *detach)
+{
+	struct cxlflash *cxlflash = (struct cxlflash *)sdev->host->hostdata;
+	struct lun_info *lun_info = sdev->hostdata;
+
+	struct dk_cxlflash_release rel;
+	struct ctx_info *ctx_info;
+
+	int i;
+	int rc = 0;
+
+	cxlflash_info("context=0x%llx", detach->context_id);
+
+	ctx_info = get_validated_context(cxlflash, detach->context_id, false);
+	if (unlikely(!ctx_info)) {
+		cxlflash_err("Invalid context! (%llu)", detach->context_id);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	if (ctx_info->ref_cnt-- == 1) {
+
+		/* for any resource still open, deallocate LBAs and close
+		 * if nobody else is using it.
+		 */
+
+		if (ctx_info->rht_info->ref_cnt-- == 1) {
+			marshall_det_to_rele(detach, &rel);
+			for (i = 0; i < MAX_RHT_PER_CONTEXT; i++) {
+				rel.rsrc_handle = i;
+				cxlflash_disk_release(sdev, &rel);
+			}
+		}
+
+		/* clear RHT registers for this context */
+		writeq_be(0, &ctx_info->ctrl_map->rht_start);
+		writeq_be(0, &ctx_info->ctrl_map->rht_cnt_id);
+		/* drop all capabilities */
+		writeq_be(0, &ctx_info->ctrl_map->ctx_cap);
+		/* close the context */
+		cxlflash->num_user_contexts--;
+	}
+	spin_lock(&lun_info->slock);
+	lun_info->mode = MODE_NONE;
+	spin_unlock(&lun_info->slock);
+
+	cxlflash->per_context[detach->context_id].lfd = -1;
+	cxlflash->per_context[detach->context_id].pid = 0;
+
+out:
+	cxlflash_info("returning rc=%d", rc);
+	return rc;
+}
+
+static int cxlflash_afu_recover(struct scsi_device *sdev,
+				struct dk_cxlflash_recover_afu *recover)
+{
+	struct cxlflash *cxlflash = (struct cxlflash *)sdev->host->hostdata;
+	struct afu *afu = cxlflash->afu;
+	struct ctx_info *ctx_info;
+	long reg;
+	int rc = 0;
+
+	/* Ensure that this process is attached to the context */
+	ctx_info = get_validated_context(cxlflash, recover->context_id, false);
+	if (unlikely(!ctx_info)) {
+		cxlflash_err("Invalid context! (%llu)", recover->context_id);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	reg = readq_be(&afu->ctrl_map->mbox_r);	/* Try MMIO */
+	/* MMIO returning 0xff, need to reset */
+	if (reg == -1) {
+		cxlflash_info("afu=%p reason 0x%llx", afu, recover->reason);
+		cxlflash_afu_reset(cxlflash);
+
+	} else {
+		cxlflash_info
+		    ("reason 0x%llx MMIO is working, no reset performed",
+		     recover->reason);
+		rc = -EINVAL;
+	}
+
+out:
+	return rc;
+}
+
+/*
+ * NAME:	clone_lxt()
+ *
+ * FUNCTION:	clone a LXT table
+ *
+ * INPUTS:
+ *		afu		- Pointer to afu struct
+ *		ctx_hndl_u	- context that owns the destination LXT
+ *		res_hndl_u	- res_hndl of the destination LXT
+ *		rht_entry	- destination RHT to clone into
+ *		rht_entry_src	- source RHT to clone from
+ *
+ * OUTPUTS:
+ *
+ * RETURNS:
+ *		0	- Success
+ *		errno	- Failure
+ *
+ * NOTES:
+ */
+static int clone_lxt(struct afu *afu,
+		     struct blka *blka,
+		     ctx_hndl_t ctx_hndl_u,
+		     res_hndl_t res_hndl_u,
+		     struct sisl_rht_entry *rht_entry,
+		     struct sisl_rht_entry *rht_entry_src)
+{
+	struct sisl_lxt_entry *lxt;
+	unsigned int ngrps;
+	u64 aun;		/* chunk# allocated by block allocator */
+	int i, j;
+
+	ngrps = LXT_NUM_GROUPS(rht_entry_src->lxt_cnt);
+
+	if (ngrps) {
+		/* allocate new LXTs for clone */
+		lxt = kzalloc((sizeof(*lxt) * LXT_GROUP_SIZE * ngrps),
+				GFP_KERNEL);
+		if (unlikely(!lxt))
+			return -ENOMEM;
+
+		/* copy over */
+		memcpy(lxt, rht_entry_src->lxt_start,
+		       (sizeof(*lxt) * rht_entry_src->lxt_cnt));
+
+		/* clone the LBAs in block allocator via ref_cnt */
+		mutex_lock(&blka->mutex);
+		for (i = 0; i < rht_entry_src->lxt_cnt; i++) {
+			aun = (lxt[i].rlba_base >> MC_CHUNK_SHIFT);
+			if (ba_clone(&blka->ba_lun, aun) == -1ULL) {
+				/* free the clones already made */
+				for (j = 0; j < i; j++) {
+					aun = (lxt[j].rlba_base >>
+					       MC_CHUNK_SHIFT);
+					ba_free(&blka->ba_lun, aun);
+				}
+
+				mutex_unlock(&blka->mutex);
+				kfree(lxt);
+				return -EIO;
+			}
+		}
+		mutex_unlock(&blka->mutex);
+	} else {
+		lxt = NULL;
+	}
+
+	smp_wmb();		/* make lxt updates visible */
+
+	/* Now sync up AFU - this can take a while */
+	rht_entry->lxt_start = lxt;	/* even if lxt is NULL */
+	smp_wmb();
+
+	rht_entry->lxt_cnt = rht_entry_src->lxt_cnt;
+	smp_wmb();
+
+	cxlflash_afu_sync(afu, ctx_hndl_u, res_hndl_u, AFU_LW_SYNC);
+
+	cxlflash_info("returning");
+	return 0;
+}
+
+/*
+ * NAME:        cxlflash_disk_clone
+ *
+ * FUNCTION:    Clone a context by making a snapshot copy of another, specified
+ *		context. This routine effectively performs cxlflash_disk_open
+ *		operations for each in-use virtual resource in the source
+ *		context. Note that the destination context must be in pristine
+ *		state and cannot have any resource handles open at the time
+ *		of the clone.
+ *
+ * INPUTS:
+ *              sdev       - Pointer to scsi device structure
+ *              arg        - Pointer to ioctl specific structure
+ *
+ * OUTPUTS:
+ *              None
+ *
+ * RETURNS:
+ *              0           - Success
+ *              errno       - Failure
+ */
+static int cxlflash_disk_clone(struct scsi_device *sdev,
+			       struct dk_cxlflash_clone *clone)
+{
+	struct cxlflash *cxlflash = (struct cxlflash *)sdev->host->hostdata;
+	struct lun_info *lun_info = sdev->hostdata;
+	struct blka *blka = &lun_info->blka;
+	struct afu *afu = cxlflash->afu;
+	struct dk_cxlflash_release release = { { 0 }, 0 };
+
+	struct ctx_info *ctx_info_src, *ctx_info_dst;
+	struct rht_info *rht_info_src, *rht_info_dst;
+	u32 perms;
+	int i, j;
+	int rc = 0;
+
+	cxlflash_info("ctx_hdl_src=%llu ctx_hdl_dst=%llu",
+		      clone->context_id_src, clone->context_id_dst);
+
+	/* Do not clone yourself */
+	if (clone->context_id_src == clone->context_id_dst) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	ctx_info_src = get_validated_context(cxlflash,
+					       clone->context_id_src, true);
+	ctx_info_dst = get_validated_context(cxlflash,
+					       clone->context_id_dst, false);
+	if (unlikely(!ctx_info_src || !ctx_info_dst)) {
+		cxlflash_err("Invalid context! (%llu,%llu)",
+			     clone->context_id_src, clone->context_id_dst);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	rht_info_src = ctx_info_src->rht_info;
+	rht_info_dst = ctx_info_dst->rht_info;
+
+	/* Verify there is no open resource handle in the destination context */
+	for (i = 0; i < MAX_RHT_PER_CONTEXT; i++)
+		if (rht_info_dst->rht_start[i].nmask != 0) {
+			rc = -EINVAL;
+			goto out;
+		}
+
+	/* User specified permission on attach */
+	perms = rht_info_dst->perms;
+
+	/*
+	 * This loop is equivalent to cxlflash_disk_open & cxlflash_vlun_resize.
+	 * Not checking if the source context has anything open or whether
+	 * it is even registered. Cleanup when the clone fails.
+	 */
+	for (i = 0; i < MAX_RHT_PER_CONTEXT; i++) {
+		rht_info_dst->rht_start[i].nmask =
+		    rht_info_src->rht_start[i].nmask;
+		rht_info_dst->rht_start[i].fp =
+		    SISL_RHT_FP_CLONE(rht_info_src->rht_start[i].fp, perms);
+
+		rc = clone_lxt(afu, blka, clone->context_id_dst, i,
+			       &rht_info_dst->rht_start[i],
+			       &rht_info_src->rht_start[i]);
+		if (rc) {
+			marshall_clone_to_rele(clone, &release);
+			for (j = 0; j < i; j++) {
+				release.rsrc_handle = j;
+				cxlflash_disk_release(sdev, &release);
+			}
+
+			rhte_checkin(&rht_info_dst->rht_start[i]);
+			goto out;
+		}
+	}
+
+out:
+	cxlflash_info("returning rc=%d", rc);
+	return rc;
+}
+
+/*
+ * NAME:        cxlflash_disk_verify
+ *
+ * FUNCTION:    Verify that the LUN is the same, whether its size has changed
+ *
+ * INPUTS:
+ *              sdev       - Pointer to scsi device structure
+ *              arg        - Pointer to ioctl specific structure
+ *
+ * OUTPUTS:
+ *              none
+ *
+ * RETURNS:
+ *              0           - Success
+ *              errno       - Failure
+ *
+ * NOTES:
+ *              When successful, the RHT entry is cleared.
+ */
+static int cxlflash_disk_verify(struct scsi_device *sdev,
+				struct dk_cxlflash_verify *verify)
+{
+	struct lun_info *lun_info = sdev->hostdata;
+
+	int rc = 0;
+
+	/* XXX: We would have to look at the hint/sense to see if it 
+	 * requires us to redrive inquiry (i.e. the Unit attention is
+	 * due to the WWN changing), or read capacity again (in case
+	 * the Unit attention was due to a resize)
+	 */
+	verify->last_lba = lun_info->max_lba;
+
+	cxlflash_info("returning rc=%d", rc);
+	return rc;
+}
+
+int read_cap16(struct afu *afu, struct lun_info *lun_info, u32 port_sel)
+{
+	struct afu_cmd *cmd;
+	int rc = 0;
+
+	cmd = cxlflash_cmd_checkout(afu);
+	if (unlikely(!cmd)) {
+		cxlflash_err("could not get a free command");
+		return -1;
+	}
+
+	cmd->rcb.req_flags = (SISL_REQ_FLAGS_PORT_LUN_ID |
+				SISL_REQ_FLAGS_SUP_UNDERRUN |
+				SISL_REQ_FLAGS_HOST_READ);
+
+	cmd->rcb.port_sel = port_sel;
+	cmd->rcb.lun_id = lun_info->lun_id;
+	cmd->rcb.data_len = CMD_BUFSIZE;
+	cmd->rcb.data_ea = (u64) cmd->buf;
+	cmd->rcb.timeout = MC_DISCOVERY_TIMEOUT;
+	cmd->internal = true;
+
+	cmd->rcb.cdb[0] = 0x9E;	/* read cap(16) */
+	cmd->rcb.cdb[1] = 0x10;	/* service action */
+	put_unaligned_be32(CMD_BUFSIZE, &cmd->rcb.cdb[10]);
+
+	cmd->sa.host_use_b[1] = 0;	/* reset retry cnt */
+
+	cxlflash_info("sending cmd(0x%x) with RCB EA=%p data EA=0x%llx",
+		      cmd->rcb.cdb[0], &cmd->rcb, cmd->rcb.data_ea);
+
+	do {
+		cxlflash_send_cmd(afu, cmd);
+		cxlflash_wait_resp(afu, cmd);
+	} while (cxlflash_check_status(&cmd->sa));
+
+	if (cmd->sa.host_use_b[0] & B_ERROR) {
+		cxlflash_err("command failed");
+		rc = -1;
+		goto out;
+	}
+
+	/*
+	 * Read cap was successful, grab values from the buffer;
+	 * note that we don't need to worry about unaligned access
+	 * as the buffer is allocated on an aligned boundary.
+	 */
+	spin_lock(&lun_info->slock);
+	lun_info->max_lba = swab64(*((u64 *)&cmd->buf[0]));
+	lun_info->blk_len = swab32(*((u32 *)&cmd->buf[8]));
+	spin_unlock(&lun_info->slock);
+
+out:
+	cxlflash_cmd_checkin(cmd);
+
+	cxlflash_info("maxlba=%lld blklen=%d pcmd %p",
+		      lun_info->max_lba, lun_info->blk_len, cmd);
+	return rc;
+}
+
+static char *decode_ioctl(int cmd)
+{
+#define _CASE2STR(_x) case _x: return #_x
+
+	switch (cmd) {
+		_CASE2STR(DK_CXLFLASH_ATTACH);
+		_CASE2STR(DK_CXLFLASH_USER_DIRECT);
+		_CASE2STR(DK_CXLFLASH_USER_VIRTUAL);
+		_CASE2STR(DK_CXLFLASH_DETACH);
+		_CASE2STR(DK_CXLFLASH_VLUN_RESIZE);
+		_CASE2STR(DK_CXLFLASH_RELEASE);
+		_CASE2STR(DK_CXLFLASH_CLONE);
+		_CASE2STR(DK_CXLFLASH_VERIFY);
+	}
+
+	return ("UNKNOWN");
+}
+
+static int cxlflash_disk_virtual_open(struct scsi_device *sdev, void *arg)
+{
+	return cxlflash_disk_open(sdev, arg, MODE_VIRTUAL);
+}
+
+static int cxlflash_disk_direct_open(struct scsi_device *sdev, void *arg)
+{
+	return cxlflash_disk_open(sdev, arg, MODE_PHYSICAL);
+}
+
+/**
+ * cxlflash_ioctl - IOCTL handler
+ * @sdev:       scsi device struct
+ * @cmd:        IOCTL cmd
+ * @arg:        IOCTL arg
+ *
+ * Return value:
+ *      0 on success / other on failure
+ **/
+int cxlflash_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
+{
+	typedef int (*sioctl) (struct scsi_device *, void *);
+
+	struct cxlflash *cxlflash = (struct cxlflash *)sdev->host->hostdata;
+	struct afu *afu = cxlflash->afu;
+	struct dk_cxlflash_hdr *hdr;
+	char buf[MAX_CXLFLASH_IOCTL_SZ];
+	size_t size = 0;
+	int idx;
+	int rc = 0;
+	sioctl do_ioctl = NULL;
+#define IOCTE(_s, _i) sizeof(struct _s), (sioctl)(_i)
+	static const struct {
+		size_t size;
+		sioctl ioctl;
+	} ioctl_tbl[] = {	/* NOTE: order matters here */
+		{
+		IOCTE(dk_cxlflash_attach, cxlflash_disk_attach)}, {
+		IOCTE(dk_cxlflash_udirect, cxlflash_disk_direct_open)}, {
+		IOCTE(dk_cxlflash_uvirtual, cxlflash_disk_virtual_open)}, {
+		IOCTE(dk_cxlflash_resize, cxlflash_vlun_resize)}, {
+		IOCTE(dk_cxlflash_release, cxlflash_disk_release)}, {
+		IOCTE(dk_cxlflash_detach, cxlflash_disk_detach)}, {
+		IOCTE(dk_cxlflash_verify, cxlflash_disk_verify)}, {
+		IOCTE(dk_cxlflash_log, NULL)}, {
+		IOCTE(dk_cxlflash_recover_afu, cxlflash_afu_recover)}, {
+		IOCTE(dk_cxlflash_log, NULL)}, {
+		IOCTE(dk_cxlflash_clone, cxlflash_disk_clone)}
+	};
+
+	/* Restrict command set to physical support only for internal LUN */
+	if (internal_lun || afu->internal_lun)
+		switch (cmd) {
+		case DK_CXLFLASH_USER_VIRTUAL:
+		case DK_CXLFLASH_VLUN_RESIZE:
+		case DK_CXLFLASH_RELEASE:
+		case DK_CXLFLASH_CLONE:
+			cxlflash_err("%s not supported for lun_mode=%d",
+				     decode_ioctl(cmd), internal_lun);
+			rc = -EINVAL;
+			goto cxlflash_ioctl_exit;
+		}
+
+	switch (cmd) {
+	case DK_CXLFLASH_ATTACH:
+	case DK_CXLFLASH_USER_DIRECT:
+	case DK_CXLFLASH_USER_VIRTUAL:
+	case DK_CXLFLASH_VLUN_RESIZE:
+	case DK_CXLFLASH_RELEASE:
+	case DK_CXLFLASH_DETACH:
+	case DK_CXLFLASH_VERIFY:
+	case DK_CXLFLASH_LOG_EVENT:
+	case DK_CXLFLASH_RECOVER_AFU:
+	case DK_CXLFLASH_QUERY_EXCEPTIONS:
+	case DK_CXLFLASH_CLONE:
+		idx = _IOC_NR(cmd) - _IOC_NR(DK_CXLFLASH_ATTACH);
+		size = ioctl_tbl[idx].size;
+		do_ioctl = ioctl_tbl[idx].ioctl;
+
+		if (likely(do_ioctl))
+			break;
+
+		/* fall thru */
+	default:
+		rc = -EINVAL;
+		goto cxlflash_ioctl_exit;
+	}
+
+	if (unlikely(copy_from_user(&buf, arg, size))) {
+		cxlflash_err("copy_from_user() fail! "
+			     "size=%lu cmd=%d (%s) arg=%p",
+			     size, cmd, decode_ioctl(cmd), arg);
+		rc = -EFAULT;
+		goto cxlflash_ioctl_exit;
+	}
+
+	hdr = (struct dk_cxlflash_hdr *)&buf;
+	if (hdr->version != 0) {
+		cxlflash_err("Version %u not supported for %s",
+			     hdr->version, decode_ioctl(cmd));
+		rc = -EINVAL;
+		goto cxlflash_ioctl_exit;
+	}
+
+	rc = do_ioctl(sdev, (void *)&buf);
+	if (likely(!rc))
+		if (unlikely(copy_to_user(arg, &buf, size))) {
+			cxlflash_err("copy_to_user() fail! "
+				     "size=%lu cmd=%d (%s) arg=%p",
+				     size, cmd, decode_ioctl(cmd), arg);
+			rc = -EFAULT;
+		}
+
+	/* fall thru to exit */
+
+cxlflash_ioctl_exit:
+	cxlflash_info("ioctl %s (%08X) returned rc %d",
+		      decode_ioctl(cmd), cmd, rc);
+	return rc;
+}
diff --git a/drivers/scsi/cxlflash/superpipe.h b/drivers/scsi/cxlflash/superpipe.h
new file mode 100644
index 0000000..b3033ae
--- /dev/null
+++ b/drivers/scsi/cxlflash/superpipe.h
@@ -0,0 +1,67 @@ 
+/*
+ * CXL Flash Device Driver
+ *
+ * Written by: Manoj N. Kumar <manoj@linux.vnet.ibm.com>, IBM Corporation
+ *             Matthew R. Ochs <mrochs@linux.vnet.ibm.com>, IBM Corporation
+ *
+ * Copyright (C) 2015 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _CXLFLASH_SUPERPIPE_H
+#define _CXLFLASH_SUPERPIPE_H
+
+extern u32 checkpid;
+extern u32 internal_lun;
+
+/*----------------------------------------------------------------------------*/
+/* Constants                                                                  */
+/*----------------------------------------------------------------------------*/
+
+#define SL_INI_SINI_MARKER      0x53494e49
+#define SL_INI_ELMD_MARKER      0x454c4d44
+/*----------------------------------------------------------------------------*/
+/* Types                                                                      */
+/*----------------------------------------------------------------------------*/
+
+#define MAX_AUN_CLONE_CNT    0xFF
+
+/*
+ * Terminology: use afu (and not adapter) to refer to the HW.
+ * Adapter is the entire slot and includes PSL out of which
+ * only the AFU is visible to user space.
+ */
+
+/* Chunk size parms: note sislite minimum chunk size is
+   0x10000 LBAs corresponding to a NMASK or 16.
+*/
+#define MC_RHT_NMASK      16	/* in bits */
+#define MC_CHUNK_SIZE     (1 << MC_RHT_NMASK)	/* in LBAs, see mclient.h */
+#define MC_CHUNK_SHIFT    MC_RHT_NMASK	/* shift to go from LBA to chunk# */
+#define MC_CHUNK_OFF_MASK (MC_CHUNK_SIZE - 1)	/* apply to LBA get offset
+						   into a chunk */
+#define LXT_LUNIDX_SHIFT  8	/* LXT entry, shift for LUN index */
+
+/* LXT tables are allocated dynamically in groups. This is done to
+   avoid a malloc/free overhead each time the LXT has to grow
+   or shrink.
+
+   Based on the current lxt_cnt (used), it is always possible to
+   know how many are allocated (used+free). The number of allocated
+   entries is not stored anywhere.
+
+   The LXT table is re-allocated whenever it needs to cross into
+   another group.
+*/
+#define LXT_GROUP_SIZE          8
+#define LXT_NUM_GROUPS(lxt_cnt) (((lxt_cnt) + 7)/8)	/* alloc'ed groups */
+
+#define MC_DISCOVERY_TIMEOUT 5  /* 5 secs */
+
+int read_cap16(struct afu *, struct lun_info *, u32);
+void cxlflash_rht_format1(struct sisl_rht_entry *, u64, u32);
+#endif /* ifndef _CXLFLASH_SUPERPIPE_H */
diff --git a/include/uapi/scsi/Kbuild b/include/uapi/scsi/Kbuild
index 75746d5..d791e0a 100644
--- a/include/uapi/scsi/Kbuild
+++ b/include/uapi/scsi/Kbuild
@@ -3,3 +3,4 @@  header-y += fc/
 header-y += scsi_bsg_fc.h
 header-y += scsi_netlink.h
 header-y += scsi_netlink_fc.h
+header-y += cxlflash_ioctl.h
diff --git a/include/uapi/scsi/cxlflash_ioctl.h b/include/uapi/scsi/cxlflash_ioctl.h
new file mode 100644
index 0000000..0e54122
--- /dev/null
+++ b/include/uapi/scsi/cxlflash_ioctl.h
@@ -0,0 +1,133 @@ 
+/*
+ * CXL Flash Device Driver
+ *
+ * Written by: Manoj N. Kumar <manoj@linux.vnet.ibm.com>, IBM Corporation
+ *             Matthew R. Ochs <mrochs@linux.vnet.ibm.com>, IBM Corporation
+ *
+ * Copyright (C) 2015 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _CXLFLASH_IOCTL_H
+#define _CXLFLASH_IOCTL_H
+
+#include <linux/types.h>
+
+/*
+ * Structure definitions CXL Flash driver superpipe ioctls
+ */
+
+struct dk_cxlflash_hdr {
+	__u16 version;			/* Version data */
+	__u16 rsvd[3];			/* Reserved for future use */
+	__u64 flags;			/* Input flags */
+	__u64 return_flags;		/* Returned flags */
+};
+
+struct dk_cxlflash_attach {
+	struct dk_cxlflash_hdr hdr;	/* Common fields */
+	__u64 num_interrupts;		/* Requested number of interrupts */
+	__u64 context_id;		/* Returned context ID */
+	__u64 mmio_size;		/* Returned size of MMIO area */
+	__u64 block_size;		/* Returned block size, in bytes */
+	__u64 adap_fd;			/* Returned adapter file descriptor */
+	__u64 last_lba;			/* Returned last LBA on the device */
+	__u64 max_xfer;			/* Returned max transfer size, blocks */
+};
+
+struct dk_cxlflash_detach {
+	struct dk_cxlflash_hdr hdr;	/* Common fields */
+	__u64 context_id;		/* Context ID to detach */
+};
+
+struct dk_cxlflash_udirect {
+	struct dk_cxlflash_hdr hdr;	/* Common fields */
+	__u64 context_id;		/* Context ID for the attach */
+	__u64 rsrc_handle;		/* Returned resource handle */
+	__u64 last_lba;			/* Returned last LBA on the device */
+};
+
+struct dk_cxlflash_uvirtual {
+	struct dk_cxlflash_hdr hdr;	/* Common fields */
+	__u64 context_id;		/* Context ID for the attach */
+	__u64 lun_size;			/* Requested size, blocks */
+	__u64 rsrc_handle;		/* Returned resource handle */
+	__u64 last_lba;			/* Returned last LBA of LUN */
+};
+
+struct dk_cxlflash_release {
+	struct dk_cxlflash_hdr hdr;	/* Common fields */
+	__u64 context_id;		/* Context ID for the attach */
+	__u64 rsrc_handle;		/* Resource handle to release */
+};
+
+struct dk_cxlflash_resize {
+	struct dk_cxlflash_hdr hdr;	/* Common fields */
+	__u64 context_id;		/* Context ID of LUN to resize */
+	__u64 rsrc_handle;		/* Resource handle of LUN to resize */
+	__u64 req_size;			/* New requested size, blocks */
+	__u64 last_lba;			/* Returned last LBA of LUN */
+};
+
+struct dk_cxlflash_clone {
+	struct dk_cxlflash_hdr hdr;	/* Common fields */
+	__u64 context_id_src;		/* Context ID to clone from */
+	__u64 context_id_dst;		/* Context ID to clone to */
+};
+
+struct dk_cxlflash_verify {
+	struct dk_cxlflash_hdr hdr;	/* Common fields */
+	__u64 rsrc_handle;		/* Resource handle of LUN */
+	__u64 hint;			/* Reasons for verify */
+	__u64 last_lba;			/* Returned last LBA of device */
+};
+
+struct dk_cxlflash_log {
+	struct dk_cxlflash_hdr hdr;	/* Common fields */
+	__u64 rsrc_handle;		/* Resource handle to log err against */
+	__u64 reason;			/* Reason code for error */
+	__u8 sense_data[256];		/* Sense data to include in error */
+};
+
+struct dk_cxlflash_recover_afu {
+	struct dk_cxlflash_hdr hdr;	/* Common fields */
+	__u64 context_id;		/* Context ID of LUN to resize */
+	__u64 rsrc_handle;		/* Resource handle for LUN to recover */
+	__u64 reason;			/* Reason for recovery request */
+};
+
+union cxlflash_ioctls {
+	struct dk_cxlflash_attach attach;
+	struct dk_cxlflash_detach detach;
+	struct dk_cxlflash_udirect udirect;
+	struct dk_cxlflash_uvirtual uvirtual;
+	struct dk_cxlflash_release release;
+	struct dk_cxlflash_resize resize;
+	struct dk_cxlflash_clone clone;
+	struct dk_cxlflash_verify verify;
+	struct dk_cxlflash_log log;
+	struct dk_cxlflash_recover_afu recover_afu;
+};
+
+#define MAX_CXLFLASH_IOCTL_SZ	(sizeof(union cxlflash_ioctls))
+
+
+#define CXL_MAGIC 0xCA
+
+#define DK_CXLFLASH_ATTACH           _IOW(CXL_MAGIC, 0x80, struct dk_cxlflash_attach)
+#define DK_CXLFLASH_USER_DIRECT      _IOW(CXL_MAGIC, 0x81, struct dk_cxlflash_udirect)
+#define DK_CXLFLASH_USER_VIRTUAL     _IOW(CXL_MAGIC, 0x82, struct dk_cxlflash_uvirtual)
+#define DK_CXLFLASH_VLUN_RESIZE      _IOW(CXL_MAGIC, 0x83, struct dk_cxlflash_resize)
+#define DK_CXLFLASH_RELEASE          _IOW(CXL_MAGIC, 0x84, struct dk_cxlflash_release)
+#define DK_CXLFLASH_DETACH           _IOW(CXL_MAGIC, 0x85, struct dk_cxlflash_detach)
+#define DK_CXLFLASH_VERIFY           _IOW(CXL_MAGIC, 0x86, struct dk_cxlflash_verify)
+#define DK_CXLFLASH_LOG_EVENT        _IOW(CXL_MAGIC, 0x87, struct dk_cxlflash_log)
+#define DK_CXLFLASH_RECOVER_AFU      _IOW(CXL_MAGIC, 0x88, struct dk_cxlflash_recover_afu)
+#define DK_CXLFLASH_QUERY_EXCEPTIONS _IOW(CXL_MAGIC, 0x89, struct dk_cxlflash_log)
+#define DK_CXLFLASH_CLONE	     _IOW(CXL_MAGIC, 0x8A, struct dk_cxlflash_clone)
+
+#endif /* ifndef _CXLFLASH_IOCTL_H */