/*
 *  Copyright (C) 2013 - 2018 Western Digital Corporation
 *
 */

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>

#ifdef INSIDE_KERNEL_TREE
#include <scst/scst.h>
#else
#include "scst.h"
#endif
#include "scst_priv.h"
#include "scst_pres.h"


#define SCST_CM_NAME		"copy_manager"
#define SCST_CM_TGT_NAME	(SCST_CM_NAME "_tgt")
#define SCST_CM_SESS_NAME	(SCST_CM_NAME "_sess")

#define SCST_CM_TID_SIZE	24
#define SCST_CM_TID_ID		"COPY_MGR"

#define SCST_CM_RETRIES_WAIT	HZ
#define SCST_CM_MAX_RETRIES_TIME (30*HZ)
#define SCST_CM_ID_KEEP_TIME	(5*HZ)

#define SCST_CM_MAX_EACH_IO_SIZE (512*1024)

/* Too big value is not too good for the blocking machinery */
#define SCST_CM_MAX_TGT_DESCR_CNT 5

#define SCST_CM_MAX_SEG_DESCR_CNT					\
	(((PAGE_SIZE * 2) - sizeof(struct scst_cm_ec_cmd_priv)) /	\
	 sizeof(struct scst_ext_copy_seg_descr))

/* MAXIMUM DESCRIPTOR LIST LENGTH */
#define SCST_MAX_SEG_DESC_LEN 0xFFFF

static struct scst_tgt *scst_cm_tgt;
static struct scst_session *scst_cm_sess;

/* Protected by scst_mutex */
static unsigned int scst_cm_next_lun;

static DEFINE_MUTEX(scst_cm_mutex);

/* Protected by scst_cm_mutex */
static LIST_HEAD(scst_cm_desig_list);

struct scst_cm_desig {
	struct list_head cm_desig_list_entry;

	struct scst_tgt_dev *desig_tgt_dev;

	int desig_len;
	uint8_t desig[];
};

/* It's IRQ and inner for sess_list_lock */
static spinlock_t scst_cm_lock;

/* Necessary fields protected by scst_cm_lock */
struct scst_cm_list_id {
	struct list_head sess_cm_list_id_entry;

	int cm_lid;

#define SCST_CM_LIST_ID_STATE_ACTIVE		0
#define SCST_CM_LIST_ID_STATE_DONE		1
#define SCST_CM_LIST_ID_STATE_PENDING_FREE	2
	int cm_list_id_state;

	unsigned int cm_done:1;
	unsigned int cm_can_be_immed_free:1;

	int cm_segs_processed;
	int64_t cm_written_size; /* in bytes */

	unsigned long cm_time_to_free; /* in jiffies */

	int cm_status;
	unsigned short cm_sense_len;
	uint8_t cm_sense[SCST_SENSE_BUFFERSIZE];
};

struct scst_cm_internal_cmd_priv {
	/* Must be the first for scst_finish_internal_cmd()! */
	scst_i_finish_fn_t cm_finish_fn;

	/* Internal cmd itself. Needed for unified cleanups and aborts. */
	struct scst_cmd *cm_cmd;

	/* E.g. rcmd for WRITEs, ec_cmd for READs, etc. */
	struct scst_cmd *cm_orig_cmd;

	struct list_head cm_internal_cmd_list_entry;
};

struct scst_cm_dev_entry {
	struct list_head cm_sorted_devs_list_entry;
	struct scst_cmd *cm_fcmd; /* can point to the real EC cmd as well! */
};

/*
 * Most fields here not protected, since can be accessed only from a single
 * thread at time.
 */
struct scst_cm_ec_cmd_priv {
	struct scst_cm_list_id *cm_list_id;

	/*
	 * List of all devices involved in this EC cmd (structs
	 * scst_cm_dev_entry), sorted by their dev's address.
	 */
	struct list_head cm_sorted_devs_list;

	/*
	 * List of all generated internal commands. Where needed, protected
	 * by scst_cm_lock.
	 */
	struct list_head cm_internal_cmd_list;

#define SCST_CM_ERROR_NONE	0
#define SCST_CM_ERROR_READ	1
#define SCST_CM_ERROR_WRITE	2
	int cm_error;

	struct mutex cm_mutex;

	int cm_cur_in_flight; /* commands */

	/**
	 ** READ commands stuff
	 **/
	struct scst_tgt_dev *cm_read_tgt_dev;
	int64_t cm_start_read_lba; /* in blocks */
	int64_t cm_cur_read_lba; /* in blocks */
	int cm_left_to_read; /* in blocks */
	int cm_max_each_read;/* in blocks */

	/**
	 ** WRITE commands stuff
	 **/
	struct scst_tgt_dev *cm_write_tgt_dev;
	int64_t cm_start_write_lba; /* in blocks */
	int64_t cm_written; /* in bytes */

	/**
	 ** Current data descriptors and their count
	 **/
	const struct scst_ext_copy_data_descr *cm_data_descrs;
	int cm_data_descrs_cnt;
	int cm_cur_data_descr;

	int cm_cur_seg_descr;

	/**
	 ** Parsed descriptors. Number of them is in
	 ** cmd->cmd_data_descriptors_cnt
	 **/
	struct scst_ext_copy_seg_descr cm_seg_descrs[];
};

#define SCST_ALLOW_NOT_CONN_COPY_DEF	0
/* Not protected, because no need */
static bool scst_cm_allow_not_connected_copy = SCST_ALLOW_NOT_CONN_COPY_DEF;

#define SCST_CM_STATUS_CMD_SUCCEEDED	0
#define SCST_CM_STATUS_RETRY		1
#define SCST_CM_STATUS_CMD_FAILED	-1

typedef void (*scst_cm_retry_fn_t)(struct scst_cmd *cmd);

struct scst_cm_retry {
	struct scst_cmd *cm_retry_cmd;
	struct delayed_work cm_retry_work;
	scst_cm_retry_fn_t cm_retry_fn;
};

static void scst_cm_retry_work_fn(struct work_struct *work)
{
	struct scst_cm_retry *retry = container_of(work, struct scst_cm_retry,
						   cm_retry_work.work);

	TRACE_ENTRY();

	TRACE_DBG("Retrying cmd %p", retry->cm_retry_cmd);

	retry->cm_retry_fn(retry->cm_retry_cmd);

	__scst_cmd_put(retry->cm_retry_cmd);

	kfree(retry);

	TRACE_EXIT();
	return;
}

/*
 * Checks if cmd finished successfully and performs/schedules retry, if necessary.
 * Returns one of SCST_CM_STATUS_* codes.
 */
static int scst_cm_err_check_retry(struct scst_cmd *cmd,
	unsigned long start_time, scst_cm_retry_fn_t retry_fn)
{
	int res = SCST_CM_STATUS_CMD_SUCCEEDED;
	unsigned long cur_time, max_retry_time, next_retry_time;
	struct scst_cm_retry *retry;
	bool imm_retry = false;

	TRACE_ENTRY();

	/* cmd->dev and tgt_dev can be NULL here! */

	TRACE_DBG("cmd %p, status %d, aborted %d", cmd, cmd->status,
		scst_cmd_aborted(cmd));

	if (likely((cmd->status == 0) && !scst_cmd_aborted(cmd)))
		goto out;

	cur_time = jiffies;
	max_retry_time = start_time + SCST_CM_MAX_RETRIES_TIME;

	mutex_lock(&scst_cm_mutex);

	if (test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) {
		if (test_bit(SCST_CMD_ABORTED_OTHER, &cmd->cmd_flags)) {
			TRACE_MGMT_DBG("Cmd %p aborted by other initiator, "
				"retry possible", cmd);
			goto try_retry;
		} else {
			TRACE_MGMT_DBG("Cmd %p aborted, no retry ", cmd);
			goto out_err_unlock;
		}
	}

	if ((cmd->status == SAM_STAT_BUSY) ||
	    (cmd->status == SAM_STAT_TASK_SET_FULL) ||
	    (cmd->status == SAM_STAT_RESERVATION_CONFLICT) ||
	    (cmd->status == SAM_STAT_ACA_ACTIVE)) {
		TRACE_DBG("Cmd %p finished with status %d, retry possible",
			cmd, cmd->status);
		goto try_retry;
	}

	if ((cmd->status == SAM_STAT_CHECK_CONDITION) &&
	     scst_sense_valid(cmd->sense) &&
	     scst_analyze_sense(cmd->sense, cmd->sense_valid_len,
			SCST_SENSE_KEY_VALID, UNIT_ATTENTION, 0, 0)) {
		TRACE_DBG("Cmd %p finished with UA, immediate retry "
			"possible", cmd);
		imm_retry = true;
		goto try_retry;
	}

out_err_unlock:
	mutex_unlock(&scst_cm_mutex);

out_failed:
	res = SCST_CM_STATUS_CMD_FAILED;

out:
	TRACE_EXIT_RES(res);
	return res;

try_retry:
	if (time_after_eq(cur_time, max_retry_time))
		goto out_err_unlock;

	next_retry_time = cur_time + SCST_CM_RETRIES_WAIT;

	TRACE_DBG("Retrying cmd %p (imm_retry %d, next_retry_time %ld, "
		"cur_time %ld, start_time %ld, max_retry_time %ld): going "
		"to sleep", cmd, imm_retry, next_retry_time, cur_time,
		start_time, max_retry_time);

	mutex_unlock(&scst_cm_mutex);

	if (retry_fn == NULL)
		goto out_retry_done;

	/* Wait before retry */

	retry = kzalloc(sizeof(*retry), GFP_KERNEL);
	if (retry == NULL) {
		PRINT_ERROR("Unable to allocate retry struct");
		scst_set_busy(cmd);
		goto out_failed;
	}
	retry->cm_retry_cmd = cmd;
	__scst_cmd_get(cmd);
	INIT_DELAYED_WORK(&retry->cm_retry_work, scst_cm_retry_work_fn);
	retry->cm_retry_fn = retry_fn;

	if (imm_retry) {
		/* Let's use work to avoid possible recursion */
		TRACE_DBG("Immediate retry (cmd %p)", cmd);
		schedule_work(&retry->cm_retry_work.work);
	} else {
		TRACE_DBG("Scheduling cmd %p retry", cmd);
		schedule_delayed_work(&retry->cm_retry_work,
			next_retry_time - cur_time);
	}

out_retry_done:
	res = SCST_CM_STATUS_RETRY;
	goto out;
}

static bool scst_cm_is_ec_cmd_done(struct scst_cmd *ec_cmd)
{
	bool res;

	TRACE_ENTRY();

	if (unlikely(test_bit(SCST_CMD_ABORTED, &ec_cmd->cmd_flags))) {
		TRACE_MGMT_DBG("EC cmd %p aborted", ec_cmd);
		res = true;
	} else if (unlikely(ec_cmd->completed)) {
		TRACE_MGMT_DBG("EC cmd %p already completed with status (%d)",
			ec_cmd, ec_cmd->status);
		res = true;
	} else
		res = false;

	TRACE_EXIT_RES(res);
	return res;
}

/*
 * cm_mutex suppose to be locked or no activities on this ec_cmd's priv.
 *
 * Returns 0 on success or -ENOENT, if there's no more data descriptors in
 * ec_cmd, or other negative error code. For other error codes cmd status
 * and sense supposed to be set.
 */
static int scst_cm_setup_this_data_descr(struct scst_cmd *ec_cmd)
{
	int res;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
	struct scst_ext_copy_seg_descr *sd;
	const struct scst_ext_copy_data_descr *dd;

	TRACE_ENTRY();

	TRACE_DBG("ec_cmd %p, cm_cur_data_descr %d", ec_cmd,
		priv->cm_cur_data_descr);

	EXTRACHECKS_BUG_ON(priv->cm_cur_data_descr > priv->cm_data_descrs_cnt);

	if (priv->cm_cur_data_descr == priv->cm_data_descrs_cnt) {
		TRACE_DBG("No more data descriptors for ec_cmd %p", ec_cmd);
		res = -ENOENT;
		goto out;
	}

	sd = &priv->cm_seg_descrs[priv->cm_cur_seg_descr];
	dd = &priv->cm_data_descrs[priv->cm_cur_data_descr];

	EXTRACHECKS_BUG_ON(dd->data_len == 0);

	priv->cm_read_tgt_dev = sd->src_tgt_dev;
	priv->cm_start_read_lba = dd->src_lba;
	priv->cm_cur_read_lba = dd->src_lba;
	priv->cm_left_to_read = dd->data_len >> sd->src_tgt_dev->dev->block_shift;
	priv->cm_max_each_read = SCST_CM_MAX_EACH_IO_SIZE >> sd->src_tgt_dev->dev->block_shift;

	priv->cm_write_tgt_dev = sd->dst_tgt_dev;
	priv->cm_start_write_lba = dd->dst_lba;

	TRACE_DBG("len %d, src_lba %lld, dst_lba %lld", dd->data_len,
		(long long)dd->src_lba, (long long)dd->dst_lba);

	if (unlikely((dd->data_len & (sd->src_tgt_dev->dev->block_size-1)) != 0) ||
	    unlikely((dd->data_len & (sd->dst_tgt_dev->dev->block_size-1)) != 0)) {
		PRINT_ERROR("Data len %d is not even for block size (src block "
			"size %d, dst block size %d)", dd->data_len,
			sd->src_tgt_dev->dev->block_size,
			sd->dst_tgt_dev->dev->block_size);
		scst_set_cmd_error(ec_cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
		res = -EINVAL;
		goto out;
	}

	res = 0;

out:
	TRACE_EXIT_RES(res);
	return res;
}

/*
 * cm_mutex suppose to be locked or no activities on this ec_cmd's priv.
 *
 * Returns 0 on success or -ENOENT, if there's no more data descriptors in
 * ec_cmd, or other negative error code. For other error codes cmd status
 * and sense supposed to be set.
 */
static int scst_cm_setup_next_data_descr(struct scst_cmd *ec_cmd)
{
	int res;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;

	TRACE_ENTRY();

	TRACE_DBG("ec_cmd %p", ec_cmd);

	priv->cm_cur_data_descr++;

	res = scst_cm_setup_this_data_descr(ec_cmd);

	TRACE_EXIT_RES(res);
	return res;
}

/*
 * cm_mutex suppose to be locked or no activities on this ec_cmd's priv.
 *
 * Returns 0 on success or -ENOENT, if there are no data descriptors in
 * ec_cmd, or other negative error code. For other error codes cmd status
 * and sense supposed to be set.
 */
static int scst_cm_setup_first_data_descr(struct scst_cmd *ec_cmd)
{
	int res;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;

	TRACE_ENTRY();

	TRACE_DBG("ec_cmd %p", ec_cmd);

	priv->cm_cur_data_descr = 0;

	res = scst_cm_setup_this_data_descr(ec_cmd);

	TRACE_EXIT_RES(res);
	return res;
}

static void scst_cm_destroy_data_descrs(struct scst_cmd *ec_cmd)
{
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;

	TRACE_ENTRY();

	TRACE_DBG("ec_cmd %p, data_descrs %p, data_descrs_cnt %d ", ec_cmd,
		priv->cm_data_descrs, priv->cm_data_descrs_cnt);

	if (priv->cm_data_descrs != &priv->cm_seg_descrs[priv->cm_cur_seg_descr].data_descr) {
		TRACE_DBG_FLAG(TRACE_DEBUG|TRACE_MEMORY, "Freeing "
			"data_descrs %p", priv->cm_data_descrs);
		kfree(priv->cm_data_descrs);
	}

	priv->cm_data_descrs = NULL;
	priv->cm_data_descrs_cnt = 0;

	TRACE_EXIT();
	return;
}

/*
 * cm_mutex suppose to be locked or no activities on this ec_cmd's priv.
 *
 * Returns 0 on success or -ENOENT, if there are no data descriptors in
 * ec_cmd, or other negative error code. For other error codes cmd status
 * and sense supposed to be set.
 */
static int scst_cm_setup_data_descrs(struct scst_cmd *ec_cmd,
	const struct scst_ext_copy_data_descr *dds, int dds_cnt)
{
	int res;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;

	TRACE_ENTRY();

	TRACE_DBG("ec_cmd %p, dds %p, dds_cnt %d", ec_cmd, dds, dds_cnt);

	EXTRACHECKS_BUG_ON(dds_cnt == 0);

	priv->cm_data_descrs = dds;
	priv->cm_data_descrs_cnt = dds_cnt;

	res = scst_cm_setup_first_data_descr(ec_cmd);
	if (unlikely(res != 0))
		goto out_destr;

out:
	TRACE_EXIT_RES(res);
	return res;

out_destr:
	scst_cm_destroy_data_descrs(ec_cmd);
	goto out;
}

/*
 * cm_mutex suppose to be locked or no activities on this ec_cmd's priv.
 *
 * Returns 0 on success or -ENOENT, if there's no more seg descriptors in
 * ec_cmd, or other negative error code. For other error codes cmd status
 * and sense supposed to be set.
 */
static int scst_cm_setup_seg_descr(struct scst_cmd *ec_cmd)
{
	int res;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
	struct scst_ext_copy_seg_descr *sd;

	TRACE_ENTRY();

	TRACE_DBG("ec_cmd %p, cm_cur_seg_descr %d", ec_cmd,
		priv->cm_cur_seg_descr);

	EXTRACHECKS_BUG_ON(priv->cm_cur_seg_descr > ec_cmd->cmd_data_descriptors_cnt);

	while (1) {
		if (priv->cm_cur_seg_descr == ec_cmd->cmd_data_descriptors_cnt)
			goto out_enoent;

		sd = &priv->cm_seg_descrs[priv->cm_cur_seg_descr];
		EXTRACHECKS_BUG_ON(sd->type != SCST_EXT_COPY_SEG_DATA);
		if (sd->data_descr.data_len != 0)
			break;

		priv->cm_cur_seg_descr++;
		TRACE_DBG("ec_cmd %p, cm_cur_seg_descr %d", ec_cmd,
			priv->cm_cur_seg_descr);
	}

	if (priv->cm_list_id != NULL) {
		/* SCSI: including the being processed one */
		priv->cm_list_id->cm_segs_processed = priv->cm_cur_seg_descr + 1;
	}

	res = 0;

out:
	TRACE_EXIT_RES(res);
	return res;

out_enoent:
	TRACE_DBG("ec_cmd %p finished", ec_cmd);
	res = -ENOENT;
	goto out;
}

/* cm_mutex suppose to be locked or no activities on this ec_cmd's priv */
static void scst_cm_advance_seg_descr(struct scst_cmd *ec_cmd)
{
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;

	TRACE_ENTRY();

	TRACE_DBG("ec_cmd %p", ec_cmd);

	EXTRACHECKS_BUG_ON(priv->cm_cur_in_flight != 0);

	scst_cm_destroy_data_descrs(ec_cmd);

	priv->cm_cur_seg_descr++;

	TRACE_EXIT();
	return;
}

static void scst_cm_prepare_final_sense(struct scst_cmd *ec_cmd)
{
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
	uint8_t add_sense_len, *fsense = NULL;
	int d_sense = scst_get_cmd_dev_d_sense(ec_cmd);
	bool copy_sense = false;
	int sense_to_copy = ec_cmd->sense_valid_len;
	int sense_len = ec_cmd->sense_valid_len;

	TRACE_ENTRY();

	if (likely(priv->cm_error == SCST_CM_ERROR_NONE))
		goto out;

	TRACE_DBG("ec_cmd %p, cm_error %d, sense_to_copy %d", ec_cmd,
		priv->cm_error, sense_to_copy);

	if (sense_to_copy > (SCST_SENSE_BUFFERSIZE - 18)) {
		PRINT_WARNING("Too small sense buffer, %d bytes will be "
			"truncated (ec_cmd %p)",
			sense_to_copy - (SCST_SENSE_BUFFERSIZE-18), ec_cmd);
		sense_to_copy = SCST_SENSE_BUFFERSIZE - 18;
	}

	if ((priv->cm_error == SCST_CM_ERROR_WRITE) &&
	    (ec_cmd->status != SAM_STAT_CHECK_CONDITION)) {
		int rc;
		struct scst_ext_copy_seg_descr *sd = &priv->cm_seg_descrs[priv->cm_cur_seg_descr];

		/* THIRD PARTY DEVICE FAILURE */

		rc = scst_alloc_sense(ec_cmd, 0);
		if (rc != 0)
			goto out;

		TRACE_DBG("d_sense %d, cm_cur_seg_descr %d, cur_data_descr %d, "
			"tgt_descr_offs %d", d_sense, priv->cm_cur_seg_descr,
			priv->cm_cur_data_descr, sd->tgt_descr_offs);

		if (d_sense) {
			/* Descriptor format */
			ec_cmd->sense[0] = 0x72;
			ec_cmd->sense[1] = COPY_ABORTED;
			ec_cmd->sense[2] = 0xD; /* ASC */
			ec_cmd->sense[3] = 1; /* ASCQ */
			ec_cmd->sense[7] = 20; /* additional Sense Length */

			ec_cmd->sense[8] = 1; /* Command specific descriptor */
			ec_cmd->sense[9] = 0xA;
			put_unaligned_be16(priv->cm_cur_seg_descr, &ec_cmd->sense[14]);

			ec_cmd->sense[20] = 2; /* Sense key specific descriptor */
			ec_cmd->sense[21] = 6;
			ec_cmd->sense[24] = 0x80;
			put_unaligned_be16(sd->tgt_descr_offs, &ec_cmd->sense[25]);

			ec_cmd->sense_valid_len = 16;
		} else {
			/* Fixed format */
			ec_cmd->sense[0] = 0x70;
			ec_cmd->sense[2] = COPY_ABORTED;
			ec_cmd->sense[7] = 0x0a; /* additional Sense Length */
			ec_cmd->sense[12] = 0xD; /* ASC */
			ec_cmd->sense[13] = 1; /* ASCQ */

			put_unaligned_be16(priv->cm_cur_seg_descr, &ec_cmd->sense[10]);

			ec_cmd->sense[15] = 0x80;
			put_unaligned_be16(sd->tgt_descr_offs, &ec_cmd->sense[16]);

			ec_cmd->sense_valid_len = 18;
		}

		ec_cmd->status = SAM_STAT_CHECK_CONDITION;
		goto out;
	}

	fsense = mempool_alloc(scst_sense_mempool, GFP_KERNEL);
	if (fsense == NULL) {
		PRINT_ERROR("Allocation of the intermediate Extended Copy "
			"sense buffer failed. Reported sense data can be "
			"incorrect (ec_cmd %p)", ec_cmd);
		goto out;
	}
	memset(fsense, 0, SCST_SENSE_BUFFERSIZE);

	if (d_sense) {
		/* Descriptor format */
		fsense[0] = 0x72;
		fsense[1] = COPY_ABORTED;
		add_sense_len = 12;

		fsense[8] = 1; /* Command specific descriptor */
		fsense[9] = 0xA;
		put_unaligned_be16(priv->cm_cur_seg_descr, &fsense[14]);

		sense_len = 20;
	} else {
		/* Fixed format */
		fsense[0] = 0x70;
		fsense[2] = COPY_ABORTED;
		add_sense_len = 0x0a;

		put_unaligned_be16(priv->cm_cur_seg_descr, &fsense[10]);

		if (priv->cm_error == SCST_CM_ERROR_READ) {
			fsense[8] = 18;
			copy_sense = scst_sense_valid(ec_cmd->sense);
		} else if (priv->cm_error == SCST_CM_ERROR_WRITE) {
			fsense[9] = 18;
			copy_sense = scst_sense_valid(ec_cmd->sense);
		} else
			sBUG();

		if (copy_sense) {
			TRACE_DBG("Copying %db of old sense", sense_to_copy);
			add_sense_len += 1 + sense_to_copy;
			fsense[17] = ec_cmd->status;
			memcpy(&fsense[18], ec_cmd->sense, sense_to_copy);
		}

		sense_len = add_sense_len + 8;
		TRACE_DBG("New sense len %d", sense_len);
	}

	fsense[7] = add_sense_len; /* additional Sense Length */

	ec_cmd->status = SAM_STAT_CHECK_CONDITION;
	if (ec_cmd->sense != NULL) {
		memcpy(ec_cmd->sense, fsense, sense_len);
		ec_cmd->sense_valid_len = sense_len;
	} else
		scst_alloc_set_sense(ec_cmd, 0, fsense, sense_len);

	mempool_free(fsense, scst_sense_mempool);

out:
	TRACE_EXIT();
	return;
}

static void scst_cm_store_list_id_details(struct scst_cmd *ec_cmd)
{
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
	struct scst_cm_list_id *l = priv->cm_list_id;

	TRACE_ENTRY();

	if (l != NULL) {
		TRACE_DBG("List id %p done (status %d, sense valid %d, sense "
			"len %d)", l, ec_cmd->status, scst_sense_valid(ec_cmd->sense),
			ec_cmd->sense_valid_len);
		spin_lock_irq(&scst_cm_lock);
		l->cm_list_id_state = SCST_CM_LIST_ID_STATE_DONE;
		if (ec_cmd->status != 0) {
			l->cm_status = ec_cmd->status;
			if (scst_sense_valid(ec_cmd->sense)) {
				int len = ec_cmd->sense_valid_len;

				if (len > sizeof(l->cm_sense)) {
					PRINT_WARNING("EC command's sense is "
						"too big (%d) with max allowed "
						"%d, truncating", len,
						(int)sizeof(l->cm_sense));
					len = sizeof(l->cm_sense);
				}
				l->cm_sense_len = ec_cmd->sense_valid_len;
				memcpy(l->cm_sense, ec_cmd->sense, len);
			}
		}
		spin_unlock_irq(&scst_cm_lock);
	}

	TRACE_EXIT();
	return;
}

static void scst_cm_ec_cmd_done(struct scst_cmd *ec_cmd)
{
#ifdef CONFIG_SCST_EXTRACHECKS
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
#endif

	TRACE_ENTRY();

	TRACE_DBG("ec_cmd %p finished with status %d", ec_cmd, ec_cmd->status);

	EXTRACHECKS_BUG_ON(priv->cm_cur_in_flight != 0);
	EXTRACHECKS_BUG_ON(priv->cm_data_descrs != NULL);

	scst_cm_prepare_final_sense(ec_cmd);
	scst_cm_store_list_id_details(ec_cmd);

	ec_cmd->completed = 1; /* for success */
	ec_cmd->scst_cmd_done(ec_cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_THREAD);

	TRACE_EXIT();
	return;
}

static void scst_cm_ec_sched_next_seg(struct scst_cmd *ec_cmd)
{
	TRACE_ENTRY();

	/* No lock is needed, because it's all supposed to be calm */

	scst_cm_advance_seg_descr(ec_cmd);

	/*
	 * There's recursion possible here, if dev handlers call after
	 * remapping scst_ext_copy_remap_done() from the same thread context.
	 */
	scst_cm_ext_copy_exec(ec_cmd);

	TRACE_EXIT();
	return;
}

static void scst_cm_in_flight_cmd_finished(struct scst_cmd *ec_cmd)
{
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
	int f;

	TRACE_ENTRY();

	mutex_lock(&priv->cm_mutex);

	priv->cm_cur_in_flight--;
	f = priv->cm_cur_in_flight;

	mutex_unlock(&priv->cm_mutex);

	TRACE_DBG("ec_cmd %p, priv->cm_cur_in_flight %d", ec_cmd, f);

	if (f > 0)
		goto out;

	if (priv->cm_list_id != NULL)
		priv->cm_list_id->cm_written_size += priv->cm_written;

	scst_cm_ec_sched_next_seg(ec_cmd);

out:
	TRACE_EXIT();
	return;
}

static int scst_cm_add_to_internal_cmd_list(struct scst_cmd *cmd,
	struct scst_cmd *ec_cmd, struct scst_cmd *orig_cmd,
	scst_i_finish_fn_t finish_fn)
{
	int res;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
	struct scst_cm_internal_cmd_priv *p;

	TRACE_ENTRY();

	EXTRACHECKS_BUG_ON(ec_cmd == cmd);

	p = kzalloc(sizeof(*p), GFP_KERNEL);
	if (p == NULL) {
		PRINT_ERROR("Unable to alloc scst_cm_internal_cmd_priv "
			"(size %d)", (int)sizeof(*p));
		goto out_enomem;
	}

	p->cm_finish_fn = finish_fn;
	p->cm_orig_cmd = orig_cmd;
	p->cm_cmd = cmd;

	TRACE_DBG("Adding internal cmd %p (priv %p, ec_cmd %p, orig_cmd %p)",
		cmd, p, ec_cmd, orig_cmd);
	spin_lock_irq(&scst_cm_lock);
	list_add_tail(&p->cm_internal_cmd_list_entry, &priv->cm_internal_cmd_list);
	spin_unlock_irq(&scst_cm_lock);

	cmd->tgt_i_priv = p;

	res = 0;

out:
	TRACE_EXIT_RES(res);
	return res;

out_enomem:
	scst_set_busy(ec_cmd);
	res = -ENOMEM;
	goto out;
}

static void scst_cm_del_free_from_internal_cmd_list(struct scst_cmd *cmd,
	bool unblock_dev)
{
	struct scst_cm_internal_cmd_priv *p = cmd->tgt_i_priv;

	TRACE_ENTRY();

	TRACE_DBG("Deleting/freeing internal cmd %p (op %s, priv %p, "
		"orig_cmd %p)", cmd, scst_get_opcode_name(cmd), p,
		p->cm_orig_cmd);

	spin_lock_irq(&scst_cm_lock);
	list_del(&p->cm_internal_cmd_list_entry);
	spin_unlock_irq(&scst_cm_lock);

	if (unblock_dev) {
		TRACE_DBG("dev %p (internal cmd %p)", cmd->dev, cmd);
		scst_check_unblock_dev(cmd);
	}

	cmd->tgt_i_priv = NULL;

	kfree(p);

	TRACE_EXIT();
	return;
}

static void scst_cm_read_cmd_finished(struct scst_cmd *rcmd);

/* cm_mutex suppose to be locked */
static int __scst_cm_push_single_read(struct scst_cmd *ec_cmd,
	int64_t lba, int blocks)
{
	int res;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
	uint8_t read_cdb[32];
	struct scst_device *rdev = priv->cm_read_tgt_dev->dev;
	int block_shift = rdev->block_shift;
	int len = blocks << block_shift;
	struct scst_cmd *rcmd;
	int cdb_len;
	bool check_dif = (rdev->dev_dif_mode & SCST_DIF_MODE_DEV);

	TRACE_ENTRY();

	if (unlikely(scst_cm_is_ec_cmd_done(ec_cmd))) {
		TRACE_MGMT_DBG("EC cmd %p done: aborting further read "
			"commands", ec_cmd);
		priv->cm_left_to_read = 0;
		res = -EPIPE;
		goto out;
	}

	memset(read_cdb, 0, sizeof(read_cdb));
	if (rdev->dev_dif_type != 2 || !check_dif) {
		read_cdb[0] = READ_16;
		if (check_dif)
			read_cdb[1] |= 0x20;
		put_unaligned_be64(lba, &read_cdb[2]);
		put_unaligned_be32(blocks, &read_cdb[10]);
		cdb_len = 16;
	} else {
		read_cdb[0] = VARIABLE_LENGTH_CMD;
		put_unaligned_be16(SUBCODE_READ_32, &read_cdb[8]);
		read_cdb[7] = 0x18;
		cdb_len = 32;
		read_cdb[10] = 0x20;
		put_unaligned_be64(lba, &read_cdb[12]);
		put_unaligned_be32(blocks, &read_cdb[28]);
		put_unaligned_be32(lba & 0xFFFF, &read_cdb[20]);
		/* No app tag check */
	}

	rcmd = __scst_create_prepare_internal_cmd(read_cdb,
		cdb_len, SCST_CMD_QUEUE_SIMPLE,
		priv->cm_read_tgt_dev, GFP_KERNEL, false);
	if (rcmd == NULL) {
		res = -ENOMEM;
		goto out_busy;
	}

	rcmd->internal_check_local_events = 1;

	rcmd->expected_data_direction = SCST_DATA_READ;
	rcmd->expected_transfer_len_full = len;
	if (check_dif != 0)
		rcmd->expected_transfer_len_full += len >> (block_shift - SCST_DIF_TAG_SHIFT);
	rcmd->expected_values_set = 1;

	res = scst_cm_add_to_internal_cmd_list(rcmd, ec_cmd, ec_cmd,
			scst_cm_read_cmd_finished);
	if (res != 0)
		goto out_free_rcmd;

	TRACE_DBG("Adding ec_cmd's (%p) READ rcmd %p (lba %lld, blocks %d, "
		"check_dif %d) to active cmd list", ec_cmd, rcmd,
		(long long)rcmd->lba, blocks, check_dif);
	spin_lock_irq(&rcmd->cmd_threads->cmd_list_lock);
	list_add_tail(&rcmd->cmd_list_entry, &rcmd->cmd_threads->active_cmd_list);
	spin_unlock_irq(&rcmd->cmd_threads->cmd_list_lock);

	res = 0;

out:
	TRACE_EXIT_RES(res);
	return res;

out_busy:
	scst_set_busy(ec_cmd);
	goto out;

out_free_rcmd:
	__scst_cmd_put(rcmd);
	goto out;
}

static void scst_cm_read_retry_fn(struct scst_cmd *rcmd)
{
	struct scst_cm_internal_cmd_priv *p = rcmd->tgt_i_priv;
	struct scst_cmd *ec_cmd = p->cm_orig_cmd;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
	int rc;

	TRACE_ENTRY();

	mutex_lock(&priv->cm_mutex);

	rc = __scst_cm_push_single_read(ec_cmd, rcmd->lba,
		rcmd->data_len >> priv->cm_read_tgt_dev->dev->block_shift);

	/* ec_cmd can get dead after we will drop cm_mutex! */
	scst_cm_del_free_from_internal_cmd_list(rcmd, false);

	mutex_unlock(&priv->cm_mutex);

	if (rc == 0)
		wake_up(&priv->cm_read_tgt_dev->active_cmd_threads->cmd_list_waitQ);
	else
		scst_cm_in_flight_cmd_finished(ec_cmd);

	TRACE_EXIT();
	return;
}

static int scst_cm_push_single_write(struct scst_cmd *ec_cmd,
	int64_t lba, int blocks, struct scst_cmd *rcmd);

static void scst_cm_write_retry_fn(struct scst_cmd *wcmd)
{
	struct scst_cm_internal_cmd_priv *p = wcmd->tgt_i_priv;
	struct scst_cmd *rcmd = p->cm_orig_cmd;
	struct scst_cm_internal_cmd_priv *rp = rcmd->tgt_i_priv;
	struct scst_cmd *ec_cmd = rp->cm_orig_cmd;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
	int rc;

	TRACE_ENTRY();

	mutex_lock(&priv->cm_mutex);

	rc = scst_cm_push_single_write(ec_cmd, wcmd->lba,
		wcmd->data_len >> priv->cm_write_tgt_dev->dev->block_shift,
		rcmd);

	/* ec_cmd can get dead after we will drop cm_mutex! */
	scst_cm_del_free_from_internal_cmd_list(wcmd, false);

	mutex_unlock(&priv->cm_mutex);

	__scst_cmd_put(rcmd);

	if (rc != 0)
		scst_cm_in_flight_cmd_finished(ec_cmd);

	TRACE_EXIT();
	return;
}

static int scst_cm_push_single_read(struct scst_cmd *ec_cmd, int blocks,
	bool inc_cur_in_flight);

static void scst_cm_write_cmd_finished(struct scst_cmd *wcmd)
{
	struct scst_cm_internal_cmd_priv *p = wcmd->tgt_i_priv;
	struct scst_cmd *rcmd = p->cm_orig_cmd;
	struct scst_cm_internal_cmd_priv *rp = rcmd->tgt_i_priv;
	struct scst_cmd *ec_cmd = rp->cm_orig_cmd;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
	int rc, blocks;

	TRACE_ENTRY();

	TRACE_DBG("Write cmd %p finished (ec_cmd %p, rcmd %p, cm_cur_in_flight %d)",
		wcmd, rcmd, ec_cmd, priv->cm_cur_in_flight);

	EXTRACHECKS_BUG_ON(wcmd->cdb[0] != WRITE_16);

	if (unlikely(scst_cm_is_ec_cmd_done(ec_cmd)))
		goto out_finished;

	rc = scst_cm_err_check_retry(wcmd, ec_cmd->start_time, scst_cm_write_retry_fn);
	if (likely(rc == SCST_CM_STATUS_CMD_SUCCEEDED))
		goto cont;
	else if (rc == SCST_CM_STATUS_RETRY)
		goto out;
	else {
		TRACE_DBG("Write cmd %p (ec_cmd %p) finished not successfully",
			wcmd, ec_cmd);
		if (wcmd->status == SAM_STAT_CHECK_CONDITION)
			rc = scst_set_cmd_error_sense(ec_cmd, wcmd->sense,
				wcmd->sense_valid_len);
		else {
			sBUG_ON(wcmd->sense != NULL);
			rc = scst_set_cmd_error_status(ec_cmd, wcmd->status);
		}
		if (rc != 0) {
			/*
			 * UAs should be retried unconditionally, but during
			 * limited time. If we are here, we need to requeue it.
			 */
			WARN_ON(scst_is_ua_sense(wcmd->sense, wcmd->sense_valid_len));
			sBUG_ON(priv->cm_error == SCST_CM_ERROR_NONE);
		} else
			priv->cm_error = SCST_CM_ERROR_WRITE;
		goto out_finished;
	}

cont:
	priv->cm_written += wcmd->data_len;
	TRACE_DBG("ec_cmd %p, cm_written %lld (data_len %lld)", ec_cmd,
		(long long)priv->cm_written, (long long)wcmd->data_len);

	wcmd->sg = NULL;
	wcmd->sg_cnt = 0;

	mutex_lock(&priv->cm_mutex);

	if (priv->cm_left_to_read == 0) {
		if (priv->cm_cur_data_descr >= priv->cm_data_descrs_cnt)
			goto out_unlock_finished;

		rc = scst_cm_setup_next_data_descr(ec_cmd);
		if (rc != 0)
			goto out_unlock_finished;
	}

	EXTRACHECKS_BUG_ON(priv->cm_left_to_read == 0);

	blocks = min_t(int, priv->cm_left_to_read, priv->cm_max_each_read);

	rc = scst_cm_push_single_read(ec_cmd, blocks, false);
	if (rc != 0)
		goto out_unlock_finished;

	scst_cm_del_free_from_internal_cmd_list(wcmd, false);
	scst_cm_del_free_from_internal_cmd_list(rcmd, false);

	mutex_unlock(&priv->cm_mutex);

	wake_up(&rcmd->cmd_threads->cmd_list_waitQ);

out_put:
	__scst_cmd_put(rcmd);

out:
	TRACE_EXIT();
	return;

out_unlock_finished:
	mutex_unlock(&priv->cm_mutex);

out_finished:
	scst_cm_del_free_from_internal_cmd_list(wcmd, false);
	scst_cm_del_free_from_internal_cmd_list(rcmd, false);

	scst_cm_in_flight_cmd_finished(ec_cmd);
	goto out_put;
}

static int scst_cm_push_single_write(struct scst_cmd *ec_cmd,
	int64_t lba, int blocks, struct scst_cmd *rcmd)
{
	int res;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
	uint8_t write16_cdb[16];
	struct scst_cmd *wcmd;
	int len;

	TRACE_ENTRY();

	len = blocks << priv->cm_write_tgt_dev->dev->block_shift;

	/*
	 * ToDo: if rcmd is coming with tags SG, use it after updating ref and
	 * app tags instead of regenerating guard tags again with WRITE(16)
	 */

	memset(write16_cdb, 0, sizeof(write16_cdb));
	write16_cdb[0] = WRITE_16;
	put_unaligned_be64(lba, &write16_cdb[2]);
	put_unaligned_be32(blocks, &write16_cdb[10]);

	wcmd = __scst_create_prepare_internal_cmd(write16_cdb,
		sizeof(write16_cdb), SCST_CMD_QUEUE_SIMPLE,
		priv->cm_write_tgt_dev, GFP_KERNEL, false);
	if (wcmd == NULL) {
		res = -ENOMEM;
		goto out_busy;
	}

	wcmd->internal_check_local_events = 1;

	wcmd->expected_data_direction = SCST_DATA_WRITE;
	wcmd->expected_transfer_len_full = len;
	wcmd->expected_values_set = 1;

	res = scst_cm_add_to_internal_cmd_list(wcmd, ec_cmd, rcmd,
			scst_cm_write_cmd_finished);
	if (res != 0)
		goto out_free_wcmd;

	__scst_cmd_get(rcmd);

	wcmd->tgt_i_sg = rcmd->sg;
	wcmd->tgt_i_sg_cnt = rcmd->sg_cnt;
	wcmd->tgt_i_data_buf_alloced = 1;

	TRACE_DBG("Adding EC (%p) WRITE(16) cmd %p (lba %lld, blocks %d) to "
		"active cmd list", ec_cmd, wcmd, (long long)wcmd->lba, blocks);
	spin_lock_irq(&wcmd->cmd_threads->cmd_list_lock);
	list_add_tail(&wcmd->cmd_list_entry, &wcmd->cmd_threads->active_cmd_list);
	wake_up(&wcmd->cmd_threads->cmd_list_waitQ);
	spin_unlock_irq(&wcmd->cmd_threads->cmd_list_lock);

	res = 0;

out:
	TRACE_EXIT_RES(res);
	return res;

out_busy:
	scst_set_busy(ec_cmd);
	goto out;

out_free_wcmd:
	__scst_cmd_put(wcmd);
	goto out;
}

static void scst_cm_read_cmd_finished(struct scst_cmd *rcmd)
{
	struct scst_cm_internal_cmd_priv *p = rcmd->tgt_i_priv;
	struct scst_cmd *ec_cmd = p->cm_orig_cmd;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
	int64_t lba;
	int rc, len, blocks;

	TRACE_ENTRY();

	TRACE_DBG("READ cmd %p finished (ec_cmd %p, p %p)", rcmd, ec_cmd, p);

	if (unlikely(scst_cm_is_ec_cmd_done(ec_cmd)))
		goto out_finished;

	rc = scst_cm_err_check_retry(rcmd, ec_cmd->start_time, scst_cm_read_retry_fn);
	if (likely(rc == SCST_CM_STATUS_CMD_SUCCEEDED))
		goto cont;
	else if (rc == SCST_CM_STATUS_RETRY)
		goto out;
	else {
		TRACE_DBG("Read cmd %p (ec_cmd %p) finished not successfully",
			rcmd, ec_cmd);
		if (rcmd->status == SAM_STAT_CHECK_CONDITION)
			rc = scst_set_cmd_error_sense(ec_cmd, rcmd->sense,
				rcmd->sense_valid_len);
		else {
			sBUG_ON(rcmd->sense != NULL);
			rc = scst_set_cmd_error_status(ec_cmd, rcmd->status);
		}
		if (rc != 0) {
			/*
			 * UAs should be retried unconditionally, but during
			 * limited time. If we are here, we need to requeue it.
			 */
			WARN_ON(scst_is_ua_sense(rcmd->sense, rcmd->sense_valid_len));
			sBUG_ON(priv->cm_error == SCST_CM_ERROR_NONE);
		} else
			priv->cm_error = SCST_CM_ERROR_READ;
		goto out_finished;
	}

cont:
	lba = rcmd->lba - priv->cm_start_read_lba;
	lba <<= priv->cm_read_tgt_dev->dev->block_shift;
	lba >>= priv->cm_write_tgt_dev->dev->block_shift;
	lba += priv->cm_start_write_lba;

	len = rcmd->data_len;
	blocks = len >> priv->cm_write_tgt_dev->dev->block_shift;

	TRACE_DBG("rcmd->lba %lld, start_read_lba %lld, read shift %d, write "
		"shift %d, start_write_lba %lld, lba %lld, len %d, blocks %d",
		(long long)rcmd->lba, (long long)priv->cm_start_read_lba,
		priv->cm_read_tgt_dev->dev->block_shift,
		priv->cm_write_tgt_dev->dev->block_shift,
		(long long)priv->cm_start_write_lba, lba, len, blocks);

	rc = scst_cm_push_single_write(ec_cmd, lba, blocks, rcmd);
	if (rc != 0)
		goto out_finished;

out:
	TRACE_EXIT();
	return;

out_finished:
	scst_cm_del_free_from_internal_cmd_list(rcmd, false);

	scst_cm_in_flight_cmd_finished(ec_cmd);
	goto out;
}

/* cm_mutex suppose to be locked */
static int scst_cm_push_single_read(struct scst_cmd *ec_cmd, int blocks,
	bool inc_cur_in_flight)
{
	int res;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;

	TRACE_ENTRY();

	TRACE_DBG("ec_cmd %p, cm_cur_read_lba %lld, cm_left_to_read %d, "
		"blocks %d", ec_cmd, (long long)priv->cm_cur_read_lba,
		priv->cm_left_to_read, blocks);

	res = __scst_cm_push_single_read(ec_cmd, priv->cm_cur_read_lba, blocks);
	if (res != 0)
		goto out;

	priv->cm_cur_read_lba += blocks;
	priv->cm_left_to_read -= blocks;

	if (inc_cur_in_flight) {
		priv->cm_cur_in_flight++;
		TRACE_DBG("ec_cmd %p, new cm_cur_in_flight %d", ec_cmd,
			priv->cm_cur_in_flight);
	}

out:
	TRACE_EXIT_RES(res);
	return res;
}

/*
 * Generates original bunch of internal READ commands. In case of error
 * directly finishes ec_cmd, so it might be dead upon return!
 */
static void scst_cm_gen_reads(struct scst_cmd *ec_cmd)
{
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
	int cnt = 0;

	TRACE_ENTRY();

	mutex_lock(&priv->cm_mutex);

	while (1) {
		int rc;

		while ((priv->cm_left_to_read > 0) &&
		       (priv->cm_cur_in_flight < SCST_MAX_IN_FLIGHT_INTERNAL_COMMANDS)) {
			int blocks;

			blocks = min_t(int, priv->cm_left_to_read, priv->cm_max_each_read);

			rc = scst_cm_push_single_read(ec_cmd, blocks, true);
			if (rc != 0)
				goto out_err;

			cnt++;
		}

		if (priv->cm_cur_in_flight == SCST_MAX_IN_FLIGHT_INTERNAL_COMMANDS)
			break;

		rc = scst_cm_setup_next_data_descr(ec_cmd);
		if (rc != 0)
			goto out_err;
	}

	EXTRACHECKS_BUG_ON(cnt == 0);

out_wake:
	if (cnt != 0)
		wake_up(&priv->cm_read_tgt_dev->active_cmd_threads->cmd_list_waitQ);

	mutex_unlock(&priv->cm_mutex);

out:
	TRACE_EXIT();
	return;

out_err:
	if (priv->cm_cur_in_flight != 0)
		goto out_wake;
	else {
		mutex_unlock(&priv->cm_mutex);
		scst_cm_ec_cmd_done(ec_cmd);
	}
	goto out;
}

/* cm_mutex suppose to be locked or no activities on this ec_cmd's priv */
static void scst_cm_process_data_descrs(struct scst_cmd *ec_cmd,
	const struct scst_ext_copy_data_descr *dds, int dds_cnt)
{
	int rc;

	TRACE_ENTRY();

	rc = scst_cm_setup_data_descrs(ec_cmd, dds, dds_cnt);
	if (rc != 0)
		goto out_done;

	scst_cm_gen_reads(ec_cmd);

	/* ec_cmd can be dead here! */

out:
	TRACE_EXIT();
	return;

out_done:
	scst_cm_ec_cmd_done(ec_cmd);
	goto out;
}

/**
 * scst_ext_copy_get_cur_seg_data_len() - return current segment data len
 * @ec_cmd:	EXTENDED COPY command
 */
int scst_ext_copy_get_cur_seg_data_len(struct scst_cmd *ec_cmd)
{
	int res;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;

	TRACE_ENTRY();

	res = priv->cm_seg_descrs[priv->cm_cur_seg_descr].data_descr.data_len;

	TRACE_EXIT_RES(res);
	return res;
}
EXPORT_SYMBOL_GPL(scst_ext_copy_get_cur_seg_data_len);

/**
 * scst_ext_copy_remap_done() - dev handler done with remapping this segment
 * @ec_cmd:	EXTENDED COPY command
 * @dds:	Leftover data descriptors
 * @dds_cnt:	Count of the leftover data descriptors
 *
 * Called by dev handlers from inside ext_copy_remap() callback upon finish.
 * All not finished spaces should be k?alloc() as array of data descriptors
 * in dds argument with count dds_count. SCST core then will copy them using
 * internal copy machine and then kfree() dds.
 *
 * dds can point to &descr->data_descr, where descr is pointer supplied to
 * ext_copy_remap(). In this case SCST core will not kfree() it.
 *
 * If dds is NULL, then all data have been remapped, so SCST core will switch
 * to the next segment descriptor, if any.
 */
void scst_ext_copy_remap_done(struct scst_cmd *ec_cmd,
	struct scst_ext_copy_data_descr *dds, int dds_cnt)
{
	TRACE_ENTRY();

	if (dds == NULL)
		scst_cm_ec_sched_next_seg(ec_cmd);
	else
		scst_cm_process_data_descrs(ec_cmd, dds, dds_cnt);

	/* ec_cmd can be dead here! */

	TRACE_EXIT();
	return;
}
EXPORT_SYMBOL_GPL(scst_ext_copy_remap_done);

static int scst_cm_try_to_remap(struct scst_cmd *ec_cmd);

static void scst_cm_remap_retry_fn(struct scst_cmd *cmd)
{
	struct scst_cmd *ec_cmd = cmd->tgt_i_priv;
	int rc;

	TRACE_ENTRY();

	rc = scst_cm_try_to_remap(ec_cmd);
	sBUG_ON(rc != 0);

	TRACE_EXIT();
	return;
}

/*
 * Tries to remap data before copying.
 *
 * Returns !=0 if remapping is not possible, or not 0 otherwise.
 */
static int scst_cm_try_to_remap(struct scst_cmd *ec_cmd)
{
	int res, rc;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
	struct scst_ext_copy_seg_descr *sd = &priv->cm_seg_descrs[priv->cm_cur_seg_descr];
	struct scst_ext_copy_data_descr *dd = &sd->data_descr;
	struct scst_dev_type *handler = ec_cmd->dev->handler;
	uint8_t cdb[16];
	struct scst_cmd *cmd;

	TRACE_ENTRY();

	if (handler->ext_copy_remap == NULL) {
		res = 1;
		goto out;
	}

	res = 0;

	/* !! priv data descriptors fields are not setup yet !! */

	TRACE_DBG("Checking reservations on read dev %s (ec_cmd %p)",
		sd->src_tgt_dev->dev->virt_name, ec_cmd);

	memset(cdb, 0, sizeof(cdb));
	cdb[0] = READ_16;
	put_unaligned_be64(dd->src_lba, &cdb[2]);
	put_unaligned_be32(dd->data_len >> sd->src_tgt_dev->dev->block_shift, &cdb[10]);

	cmd = __scst_create_prepare_internal_cmd(cdb,
		sizeof(cdb), SCST_CMD_QUEUE_SIMPLE,
		sd->src_tgt_dev, GFP_KERNEL, true);
	if (cmd == NULL)
		goto out_busy;

	cmd->internal_check_local_events = 1;
	cmd->tgt_i_priv = ec_cmd; /* needed for retries */

	rc = __scst_check_local_events(cmd, false);
	if (unlikely(rc != 0))
		goto out_check_retry;

	__scst_cmd_put(cmd);

	TRACE_DBG("Checking reservations on write dev %s (ec_cmd %p)",
		sd->dst_tgt_dev->dev->virt_name, ec_cmd);

	memset(cdb, 0, sizeof(cdb));
	cdb[0] = WRITE_16;
	put_unaligned_be64(dd->dst_lba, &cdb[2]);
	put_unaligned_be32(dd->data_len >> sd->dst_tgt_dev->dev->block_shift, &cdb[10]);

	cmd = __scst_create_prepare_internal_cmd(cdb,
		sizeof(cdb), SCST_CMD_QUEUE_SIMPLE,
		sd->dst_tgt_dev, GFP_KERNEL, true);
	if (cmd == NULL)
		goto out_busy;

	cmd->internal_check_local_events = 1;
	cmd->tgt_i_priv = ec_cmd; /* needed for retries */

	rc = __scst_check_local_events(cmd, false);
	if (unlikely(rc != 0))
		goto out_check_retry;

	__scst_cmd_put(cmd);

	TRACE_DBG("Calling ext_copy_remap() for dev %s (ec_cmd %p)",
		sd->dst_tgt_dev->dev->virt_name, ec_cmd);

	handler->ext_copy_remap(ec_cmd, sd);

out:
	TRACE_EXIT_RES(res);
	return res;

out_check_retry:
	rc = scst_cm_err_check_retry(cmd, ec_cmd->start_time, scst_cm_remap_retry_fn);
	sBUG_ON(rc == SCST_CM_STATUS_CMD_SUCCEEDED);
	if (rc == SCST_CM_STATUS_CMD_FAILED) {
		TRACE_DBG("Remap check cmd %p (ec_cmd %p, op %s) failed",
			cmd, ec_cmd, scst_get_opcode_name(cmd));
		if (cmd->status == SAM_STAT_CHECK_CONDITION)
			rc = scst_set_cmd_error_sense(ec_cmd, cmd->sense,
				cmd->sense_valid_len);
		else {
			sBUG_ON(cmd->sense != NULL);
			rc = scst_set_cmd_error_status(ec_cmd, cmd->status);
		}
		if (rc != 0) {
			/*
			 * UAs should be retried unconditionally, but during
			 * limited time. If we are here, we need to requeue it.
			 */
			WARN_ON(scst_is_ua_sense(cmd->sense, cmd->sense_valid_len));
		} else {
			if (cmd->cdb[0] == READ_16)
				priv->cm_error = SCST_CM_ERROR_READ;
			else {
				EXTRACHECKS_BUG_ON(cmd->cdb[0] != WRITE_16);
				priv->cm_error = SCST_CM_ERROR_WRITE;
			}
		}
		__scst_cmd_put(cmd);
		goto out_done;
	}
	__scst_cmd_put(cmd);
	goto out;

out_busy:
	res = -ENOMEM;
	scst_set_busy(ec_cmd);

out_done:
	scst_cm_ec_cmd_done(ec_cmd);
	goto out;
}

static void scst_cm_process_cur_seg_descr(struct scst_cmd *ec_cmd)
{
	int rc;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;

	TRACE_ENTRY();

	rc = scst_cm_try_to_remap(ec_cmd);
	if (rc == 0)
		goto out;

	/* No remapping supported */

	scst_ext_copy_remap_done(ec_cmd,
		&priv->cm_seg_descrs[priv->cm_cur_seg_descr].data_descr, 1);

out:
	TRACE_EXIT();
	return;
}

enum scst_exec_res scst_cm_ext_copy_exec(struct scst_cmd *ec_cmd)
{
	enum scst_exec_res res = SCST_EXEC_COMPLETED;
	int rc;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;

	TRACE_ENTRY();

	if (unlikely(priv == NULL))
		goto out_local_done;

	if (unlikely(scst_cm_is_ec_cmd_done(ec_cmd))) {
		TRACE_DBG("ec_cmd %p done", ec_cmd);
		goto out_done;
	}

	rc = scst_cm_setup_seg_descr(ec_cmd);
	if (rc != 0)
		goto out_err_done;

	scst_cm_process_cur_seg_descr(ec_cmd);

out:
	TRACE_EXIT();
	return res;

out_local_done:
	ec_cmd->completed = 1; /* for success */
	ec_cmd->scst_cmd_done(ec_cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_THREAD);
	goto out;

out_err_done:
	sBUG_ON((rc != -ENOENT) && !ec_cmd->completed);

out_done:
	scst_cm_ec_cmd_done(ec_cmd);
	goto out;
}

bool scst_cm_ec_cmd_overlap(struct scst_cmd *ec_cmd, struct scst_cmd *cmd)
{
	bool res = false;
	int i;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;

	TRACE_ENTRY();

	TRACE_DBG("ec_cmd %p, cmd %p", ec_cmd, cmd);

	EXTRACHECKS_BUG_ON(ec_cmd->cdb[0] != EXTENDED_COPY);

	if ((cmd->op_flags & SCST_LBA_NOT_VALID) != 0)
		goto out;

	for (i = 0; i < ec_cmd->cmd_data_descriptors_cnt; i++) {
		struct scst_ext_copy_seg_descr *sd = &priv->cm_seg_descrs[i];

		TRACE_DBG("type %d, dst_dev %p, dev %p", sd->type,
			sd->dst_tgt_dev->dev, cmd->dev);

		if (sd->type != SCST_EXT_COPY_SEG_DATA)
			continue;
		if (sd->dst_tgt_dev->dev != cmd->dev)
			continue;

		res = scst_lba1_inside_lba2(sd->data_descr.dst_lba, cmd->lba,
			cmd->data_len >> cmd->dev->block_shift);
		if (res)
			goto out;
		res = scst_lba1_inside_lba2(cmd->lba, sd->data_descr.dst_lba,
			sd->data_descr.data_len >> sd->dst_tgt_dev->dev->block_shift);
		if (res)
			goto out;
	}

out:
	TRACE_EXIT_RES(res);
	return res;
}

/*
 * No locks. Returns true if cmd should be blocked.
 */
bool scst_cm_check_block_all_devs(struct scst_cmd *cmd)
{
	bool res = false;
	struct scst_cmd *ec_cmd;
	struct scst_cm_ec_cmd_priv *d;
	struct scst_cm_dev_entry *e;

	TRACE_ENTRY();

	/*
	 * EXTENDED COPY command creates a new challenge: it can operate on
	 * several devices at the same time. Hence, it opens a wide possibility
	 * of deadlocks like the following.
	 *
	 * Device A received EXTENDED COPY command EC1 to copy data from device
	 * A to device B. It started doing the job by generating SCSI READ and
	 * WRITE commands to devices A and B. In the middle of the work device B
	 * received its EXTENDED COPY command EC2 to copy data from device B
	 * to device A. It also started doing its work in parallel with EC1.
	 * Then, device A received a serialized or SCSI atomic command, which
	 * can not proceed until EC1 finished and don't let EC2 generated
	 * commands to proceed ahead of it. Then, if device B received similar
	 * serialized or SCSI atomic command, blocking EC2 generated commands,
	 * we would have a deadlock.
	 *
	 * So, we can not allow EXTENDED COPY generated SCSI commands be
	 * blocked. From other side, we have to honor serialized and SCSI atomic
	 * commands as well as all other devices blocking events.
	 *
	 * To handle it, we, from one side, generate all EXTENDED COPY read and
	 * writes commands as internal commands, so they bypass blocking
	 * checking, and from another side, create "fantom" EXTENDED COPY
	 * commands on all participating devices, then pass "check blocking" on
	 * all them at once. If any device blocked its command, then undo check
	 * blocking on all previously processed devices. After the blocked
	 * command resumed, we retry again. For SCSI atomic commands we check
	 * each if any of the (fantom) EXTENDED COPY commands conflict with
	 * existing SCSI atomic commands. If yes, then restart on the conflicting
	 * (fantom) EXTENDED COPY resume as described above. Then we check all
	 * newly coming SCSI atomic commands if they conflict with existing
	 * (fantom) EXTENDED COPY commands.
	 */

	if (cmd->internal) {
		struct scst_cm_internal_cmd_priv *p = cmd->tgt_i_priv;

		/* cmd is a resumed phantom EXTENDED COPY command */

		ec_cmd = p->cm_orig_cmd;

		TRACE_BLOCK("Rewaking blocked EC cmd %p (fcmd %p)",
			ec_cmd, cmd);

		scst_check_unblock_dev(cmd);

		spin_lock_irq(&ec_cmd->cmd_threads->cmd_list_lock);
		list_add_tail(&ec_cmd->cmd_list_entry,
			&ec_cmd->cmd_threads->active_cmd_list);
		wake_up(&ec_cmd->cmd_threads->cmd_list_waitQ);
		spin_unlock_irq(&ec_cmd->cmd_threads->cmd_list_lock);

		res = true;
		goto out;
	}

	/* cmd is a real ready for exec EXTENDED COPY command */

	ec_cmd = cmd;
	/*
	 * This could be restart of previously blocked ec_cmd, so
	 * check unblock it.
	 */
	TRACE_DBG("Check unblocking ec_cmd %p", ec_cmd);
	scst_check_unblock_dev(ec_cmd);

	d = ec_cmd->cmd_data_descriptors;
	if (d == NULL) {
		spin_lock_bh(&ec_cmd->dev->dev_lock);
		res = scst_do_check_blocked_dev(ec_cmd);
		spin_unlock_bh(&ec_cmd->dev->dev_lock);
		goto out;
	}

	local_bh_disable();

#if !defined(__CHECKER__)
	list_for_each_entry(e, &d->cm_sorted_devs_list, cm_sorted_devs_list_entry) {
		spin_lock(&e->cm_fcmd->dev->dev_lock);
	}
#endif

	list_for_each_entry(e, &d->cm_sorted_devs_list, cm_sorted_devs_list_entry) {
		TRACE_DBG("dev %p (fcmd %p)", e->cm_fcmd->dev, e->cm_fcmd);
		res = scst_do_check_blocked_dev(e->cm_fcmd);
		if (unlikely(res)) {
			TRACE_BLOCK("fcmd %p (ec_cmd %p) blocked, undo "
				"check blocking devices", e->cm_fcmd, ec_cmd);
			break;
		}
	}

	if (unlikely(res)) {
		struct scst_cmd *blocked_cmd = e->cm_fcmd;

		list_for_each_entry(e, &d->cm_sorted_devs_list,
					cm_sorted_devs_list_entry) {
			if (e->cm_fcmd == blocked_cmd)
				break;
			__scst_check_unblock_dev(e->cm_fcmd);
			e->cm_fcmd->state = SCST_CMD_STATE_EXEC_CHECK_BLOCKING;
		}
	}

#if !defined(__CHECKER__)
	list_for_each_entry_reverse(e, &d->cm_sorted_devs_list,
					cm_sorted_devs_list_entry) {
		spin_unlock(&e->cm_fcmd->dev->dev_lock);
	}
#endif

	local_bh_enable();

out:
	TRACE_EXIT_RES(res);
	return res;
}

void scst_cm_abort_ec_cmd(struct scst_cmd *ec_cmd)
{
	struct scst_cm_ec_cmd_priv *p = ec_cmd->cmd_data_descriptors;
	struct scst_cm_internal_cmd_priv *ip;
	unsigned long flags;

	TRACE_ENTRY();

	EXTRACHECKS_BUG_ON(ec_cmd->cdb[0] != EXTENDED_COPY);

	spin_lock_irqsave(&scst_cm_lock, flags);

	if (p == NULL)
		goto out_unlock;

	TRACE_MGMT_DBG("Aborting fantom and internal commands of ec_cmd %p",
		ec_cmd);

	list_for_each_entry(ip, &p->cm_internal_cmd_list,
					cm_internal_cmd_list_entry) {
		struct scst_cmd *c = ip->cm_cmd;

		TRACE_MGMT_DBG("Aborting (f)cmd %p", c);
		set_bit(SCST_CMD_ABORTED, &c->cmd_flags);
	}

out_unlock:
	spin_unlock_irqrestore(&scst_cm_lock, flags);

	TRACE_EXIT();
	return;
}

static void scst_cm_del_free_list_id(struct scst_cm_list_id *l)
{
	TRACE_ENTRY();

	TRACE_DBG("Freeing list id %p", l);

	lockdep_assert_held(&scst_cm_lock);

	list_del(&l->sess_cm_list_id_entry);

	kfree(l);

	TRACE_EXIT();
	return;
}

static void scst_cm_sched_del_list_id(struct scst_cmd *ec_cmd)
{
	struct scst_session *sess = ec_cmd->sess;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
	struct scst_cm_list_id *l = priv->cm_list_id;
	unsigned long flags;

	TRACE_ENTRY();

	spin_lock_irqsave(&scst_cm_lock, flags);

	if (l->cm_list_id_state != SCST_CM_LIST_ID_STATE_DONE) {
		/*
		 * It can happen for preliminary EC command finish, e.g.,
		 * because of local event like UA.
		 */
		l->cm_can_be_immed_free = 1;
	}

	l->cm_done = 1;

	/*
	 * Barrier to sync with scst_abort_cmd() and
	 * scst_mgmt_affected_cmds_done() calling scst_cm_free_pending_list_ids().
	 * It has nothing common with cm_done set above. Just in case, actually.
	 */
	smp_rmb();
	if (test_bit(SCST_CMD_ABORTED, &ec_cmd->cmd_flags) ||
	    l->cm_can_be_immed_free) {
		TRACE_DBG("List id %p can be immed freed", l);
		scst_cm_del_free_list_id(l);
		spin_unlock_irqrestore(&scst_cm_lock, flags);
		goto out;
	}

	l->cm_list_id_state = SCST_CM_LIST_ID_STATE_PENDING_FREE;
	l->cm_time_to_free = jiffies + SCST_CM_ID_KEEP_TIME;

	spin_unlock_irqrestore(&scst_cm_lock, flags);

	TRACE_DBG("Schedule pending free list id %p", l);

	schedule_delayed_work(&sess->sess_cm_list_id_cleanup_work,
				SCST_CM_ID_KEEP_TIME);

out:
	TRACE_EXIT();
	return;
}

static struct scst_cm_list_id *scst_cm_add_list_id(struct scst_cmd *cmd,
	int list_id)
{
	struct scst_cm_list_id *res;
	struct scst_session *sess = cmd->sess;
	struct scst_cm_list_id *l;

	TRACE_ENTRY();

	res = kzalloc(sizeof(*res), GFP_KERNEL);
	if (res == NULL) {
		TRACE(TRACE_OUT_OF_MEM, "Unable to allocate list_id");
		scst_set_busy(cmd);
		goto out;
	}

	res->cm_lid = list_id;
	res->cm_list_id_state = SCST_CM_LIST_ID_STATE_ACTIVE;

	spin_lock_irq(&scst_cm_lock);

	list_for_each_entry(l, &sess->sess_cm_list_id_list, sess_cm_list_id_entry) {
		if (l->cm_lid == list_id) {
			if (l->cm_list_id_state == SCST_CM_LIST_ID_STATE_PENDING_FREE) {
				scst_cm_del_free_list_id(l);
				break;
			}

			TRACE_DBG("List id %d already exists", list_id);
			scst_set_cmd_error(cmd,
				SCST_LOAD_SENSE(scst_sense_operation_in_progress));
			goto out_unlock_free;
		}
	}

	TRACE_DBG("Adding list id %p (id %d)", res, list_id);
	list_add_tail(&res->sess_cm_list_id_entry, &sess->sess_cm_list_id_list);

	spin_unlock_irq(&scst_cm_lock);

out:
	TRACE_EXIT();
	return res;

out_unlock_free:
	spin_unlock_irq(&scst_cm_lock);
	kfree(res);
	res = NULL;
	goto out;
}

void sess_cm_list_id_cleanup_work_fn(struct work_struct *work)
{
	struct scst_session *sess = container_of(work,
			struct scst_session, sess_cm_list_id_cleanup_work.work);
	struct scst_cm_list_id *l, *t;
	unsigned long cur_time = jiffies;
	unsigned long flags;

	TRACE_ENTRY();

	/*
	 * We assume that EC commands finish _more or less_ in order, so
	 * there's no need to scan the whole list each time.
	 */

	spin_lock_irqsave(&scst_cm_lock, flags);
	list_for_each_entry_safe(l, t, &sess->sess_cm_list_id_list, sess_cm_list_id_entry) {
		if (l->cm_list_id_state != SCST_CM_LIST_ID_STATE_PENDING_FREE)
			break;
		if (time_after_eq(cur_time, l->cm_time_to_free))
			scst_cm_del_free_list_id(l);
		else {
			TRACE_DBG("Reschedule pending free list ids cleanup");
			schedule_delayed_work(&sess->sess_cm_list_id_cleanup_work,
				l->cm_time_to_free - cur_time);
		}
	}
	spin_unlock_irqrestore(&scst_cm_lock, flags);

	TRACE_EXIT();
	return;
}

void scst_cm_free_pending_list_ids(struct scst_session *sess)
{
	struct scst_cm_list_id *l, *t;

	TRACE_ENTRY();

	cancel_delayed_work_sync(&sess->sess_cm_list_id_cleanup_work);

	spin_lock_irq(&scst_cm_lock);
	list_for_each_entry_safe(l, t, &sess->sess_cm_list_id_list, sess_cm_list_id_entry) {
		TRACE_DBG("List id %p, state %d", l, l->cm_list_id_state);
		if (l->cm_list_id_state == SCST_CM_LIST_ID_STATE_PENDING_FREE)
			scst_cm_del_free_list_id(l);
	}
	spin_unlock_irq(&scst_cm_lock);

	TRACE_EXIT();
	return;
}

static void scst_cm_copy_status(struct scst_cmd *cmd)
{
	ssize_t length = 0;
	uint8_t *buf, tbuf[12];
	int list_id;
	struct scst_cm_list_id *l;
	struct scst_session *sess = cmd->sess;
	bool found = false;

	TRACE_ENTRY();

	list_id = cmd->cdb[2];

	spin_lock_irq(&scst_cm_lock);
	list_for_each_entry(l, &sess->sess_cm_list_id_list, sess_cm_list_id_entry) {
		if (l->cm_lid == list_id) {
			TRACE_DBG("list id %p found (id %d)", l, list_id);
			found = true;
			break;
		}
	}
	if (found) {
		l->cm_can_be_immed_free = 1;

		memset(tbuf, 0, sizeof(tbuf));
		put_unaligned_be32(8, &tbuf[0]);
		if (l->cm_list_id_state == SCST_CM_LIST_ID_STATE_ACTIVE)
			tbuf[4] = 0; /* in progress */
		else if (l->cm_status == 0)
			tbuf[4] = 1; /* finished, no errors */
		else
			tbuf[4] = 2; /* finished with errors */
		put_unaligned_be16(l->cm_segs_processed, &tbuf[5]);
		tbuf[7] = 1; /* KBs */
		put_unaligned_be32(l->cm_written_size >> 10, &tbuf[8]);

		if (l->cm_done)
			scst_cm_del_free_list_id(l);
	}

	l = NULL; /* after unlock it can be immediately get dead */

	spin_unlock_irq(&scst_cm_lock);

	if (!found)
		goto out_list_id_not_found;

	length = scst_get_buf_full_sense(cmd, &buf);
	if (unlikely(length <= 0))
		goto out;

	length = min_t(int, (int)sizeof(tbuf), length);

	memcpy(buf, tbuf, length);
	scst_set_resp_data_len(cmd, length);

	scst_put_buf_full(cmd, buf);

out:
	TRACE_EXIT();
	return;

out_list_id_not_found:
	TRACE_DBG("list_id %d not found", list_id);
	scst_set_invalid_field_in_cdb(cmd, 2, 0);
	goto out;
}

static void scst_cm_failed_seg_details(struct scst_cmd *cmd)
{
	ssize_t length = 0;
	uint8_t *buf, *tbuf;
	int list_id, size;
	struct scst_cm_list_id *l;
	struct scst_session *sess = cmd->sess;
	bool found = false;

	TRACE_ENTRY();

	list_id = cmd->cdb[2];

	size = 60 + SCST_SENSE_BUFFERSIZE;

	tbuf = kzalloc(size, GFP_KERNEL);
	if (tbuf == NULL) {
		TRACE(TRACE_OUT_OF_MEM, "Unable to allocate FAILED SEGMENTS "
			"DETAILS buffer (size %d)", size);
		goto out_busy;
	}

	spin_lock_irq(&scst_cm_lock);
	list_for_each_entry(l, &sess->sess_cm_list_id_list, sess_cm_list_id_entry) {
		if (l->cm_lid == list_id) {
			TRACE_DBG("list id %p found (id %d)", l, list_id);
			found = true;
			break;
		}
	}
	if (found) {
		if (l->cm_list_id_state == SCST_CM_LIST_ID_STATE_ACTIVE) {
			found = false;
			goto skip;
		}

		if ((cmd->bufflen == 0) ||
		    ((l->cm_status == 0) && (cmd->bufflen >= 60)) ||
		    ((l->cm_status != 0) && (cmd->bufflen >= 60 + SCST_SENSE_BUFFERSIZE)))
			l->cm_can_be_immed_free = 1;

		if (l->cm_status == 0)
			size = 60;
		else
			size = 60 + l->cm_sense_len;

		TRACE_DBG("l %p, status %d, sense_len %d, size %d", l,
			l->cm_status, l->cm_sense_len, size);

		put_unaligned_be32(size-3, &tbuf[0]);
		tbuf[56] = l->cm_status;
		EXTRACHECKS_BUG_ON(l->cm_sense_len > SCST_SENSE_BUFFERSIZE);
		BUILD_BUG_ON(sizeof(l->cm_sense) != SCST_SENSE_BUFFERSIZE);
		put_unaligned_be16(l->cm_sense_len, &tbuf[58]);
		if (l->cm_sense_len > 0)
			memcpy(&tbuf[60], l->cm_sense, l->cm_sense_len);

		if (l->cm_can_be_immed_free && l->cm_done)
			scst_cm_del_free_list_id(l);
	}

skip:
	l = NULL; /* after unlock it can be immediately get dead */

	spin_unlock_irq(&scst_cm_lock);

	if (!found)
		goto out_list_id_not_found;

	length = scst_get_buf_full_sense(cmd, &buf);
	if (unlikely(length <= 0))
		goto out_free;

	length = min_t(int, size, length);

	memcpy(buf, tbuf, length);
	scst_set_resp_data_len(cmd, length);

	scst_put_buf_full(cmd, buf);

out_free:
	kfree(tbuf);

	TRACE_EXIT();
	return;

out_list_id_not_found:
	TRACE_DBG("list_id %d not found", list_id);
	scst_set_invalid_field_in_cdb(cmd, 2, 0);
	goto out_free;

out_busy:
	scst_set_busy(cmd);
	goto out_free;
}

static void scst_cm_oper_parameters(struct scst_cmd *cmd)
{
	ssize_t length = 0;
	uint8_t *buf, tbuf[44+2] /* 2 descriptors implemented */;

	TRACE_ENTRY();

	memset(tbuf, 0, sizeof(tbuf));

	/* AVAILABLE DATA */
	put_unaligned_be32(sizeof(tbuf) - 4, &tbuf[0]);

	/* SNLID */
	tbuf[4] = 1;

	/* MAXIMUM TARGET DESCRIPTOR COUNT */
	put_unaligned_be16(SCST_CM_MAX_TGT_DESCR_CNT, &tbuf[8]);

	/* MAXIMUM SEGMENT DESCRIPTOR COUNT */
	put_unaligned_be16(SCST_CM_MAX_SEG_DESCR_CNT, &tbuf[10]);

	/* MAXIMUM DESCRIPTOR LIST LENGTH */
	put_unaligned_be32(SCST_MAX_SEG_DESC_LEN, &tbuf[12]);

	/* MAXIMUM SEGMENT LENGTH: 256MB */
	put_unaligned_be32(256*1024*1024, &tbuf[16]);

	/* No inline and held data. No stream device max data size. */

	/* TOTAL CONCURRENT COPIES */
	put_unaligned_be16(0xFFFF, &tbuf[34]);

	/* MAXIMUM CONCURRENT COPIES */
	tbuf[36] = 0xFF;

	/* DATA SEGMENT GRANULARITY */
	tbuf[37] = 16; /* 64K */

	/* 2 descriptor codes implemented */
	tbuf[43] = 2;

	/* Implemented descriptor codes */
	tbuf[44] = 2;
	tbuf[45] = 0xE4;

	length = scst_get_buf_full_sense(cmd, &buf);
	if (unlikely(length <= 0))
		goto out;

	length = min_t(int, (int)sizeof(tbuf), length);

	memcpy(buf, tbuf, length);
	scst_set_resp_data_len(cmd, length);

	scst_put_buf_full(cmd, buf);

out:
	TRACE_EXIT();
	return;
}

enum scst_exec_res scst_cm_rcv_copy_res_exec(struct scst_cmd *cmd)
{
	enum scst_exec_res res = SCST_EXEC_COMPLETED;
	int action;

	TRACE_ENTRY();

	action = cmd->cdb[1] & 0x1F;

	switch (action) {
	case 0: /* copy status */
		scst_cm_copy_status(cmd);
		break;
	case 3: /* operational parameters */
		scst_cm_oper_parameters(cmd);
		break;
	case 4: /* failed segment details */
		scst_cm_failed_seg_details(cmd);
		break;
	default:
		TRACE(TRACE_MINOR, "%s: action %d not supported", cmd->op_name,
			action);
		scst_set_invalid_field_in_cdb(cmd, 1,
			SCST_INVAL_FIELD_BIT_OFFS_VALID | 0);
		break;
	}

	cmd->completed = 1; /* for success */
	cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_THREAD);

	TRACE_EXIT_RES(res);
	return res;
}

struct scst_cm_init_inq_priv {
	/* Must be the first for scst_finish_internal_cmd()! */
	scst_i_finish_fn_t cm_init_inq_finish_fn;

	struct scst_device *dev;
};

static int scst_cm_send_init_inquiry(struct scst_device *dev,
	unsigned int unpacked_lun, struct scst_cm_init_inq_priv *priv);

static void scst_cm_inq_retry_fn(struct scst_cmd *cmd)
{
	struct scst_cm_init_inq_priv *priv = cmd->tgt_i_priv;

	TRACE_ENTRY();

	/* cmd->dev can be NULL here! */

	scst_cm_send_init_inquiry(priv->dev, cmd->lun, priv);

	TRACE_EXIT();
	return;
}

static void scst_cm_update_dev_fini(struct scst_device *dev);

static void scst_cm_init_inq_finish(struct scst_cmd *cmd)
{
	int length, page_len, off, rc;
	uint8_t *buf;
	struct scst_cm_init_inq_priv *priv = cmd->tgt_i_priv;
	struct scst_device *dev = priv->dev;

	TRACE_ENTRY();

	/* cmd->dev can be NULL here! */

	rc = scst_cm_err_check_retry(cmd, cmd->start_time, scst_cm_inq_retry_fn);
	if (rc == SCST_CM_STATUS_RETRY)
		goto out;

	/*
	 * Since scst_cm_inq_retry_fn() uses cmd->tgt_i_priv, only free that
	 * pointer once it is clear that that function won't be called.
	 */
	kfree(priv);
	priv = NULL;
	cmd->tgt_i_priv = NULL;

	if (WARN_ON_ONCE(!dev))
		goto out;

	spin_lock_bh(&dev->dev_lock);
	scst_unblock_dev(dev);
	spin_unlock_bh(&dev->dev_lock);

	if (rc != SCST_CM_STATUS_CMD_SUCCEEDED) {
		PRINT_CRIT_ERROR("Unable to perform initial INQUIRY for device "
			"%s. Copy manager for this device will be disabled",
			dev->virt_name);
		goto out_put_ref;
	}

	length = scst_get_buf_full(cmd, &buf, false);
	TRACE_DBG("length %d", length);
	if (unlikely(length <= 0)) {
		if (length < 0)
			PRINT_ERROR("scst_get_buf_full() failed: %d", length);
		goto out_put_ref;
	}

	TRACE_BUFF_FLAG(TRACE_DEBUG, "buf", buf, length);

	if (buf[0] != 0) {
		TRACE(TRACE_MINOR, "Not supported dev type %x, ignoring", buf[0]);
		goto out_put;
	}

	if (buf[1] != 0x83) {
		PRINT_WARNING("Incorrect page code %x, ignoring", buf[1]);
		goto out_put;
	}

	page_len = get_unaligned_be16(&buf[2]);
	if (page_len+3 > cmd->resp_data_len) {
		PRINT_WARNING("Page len (%d) doesn't match resp len (%d), ignoring",
			page_len+3, cmd->resp_data_len);
		goto out_put;
	}

	off = 4;
	while (off < page_len) {
		int des_len, des_alloc_len;
		struct scst_cm_desig *des;

		if (off + 3 >= page_len) {
			PRINT_WARNING("Too small page len %d, (off %d), ignoring",
				page_len, off);
			goto out_put;
		}

		des_len = buf[off + 3];
		if ((off + des_len) > page_len) {
			PRINT_WARNING("Too small buf len %d (off %d, des_len %d), "
				"ignoring", page_len, off, des_len);
			goto out_put;
		}

		des_len += 4;

		if (((buf[off] & 0xF0) != 0) || ((buf[off+1] & 0xF0) != 0)) {
			TRACE_DBG("Unsupported designator (%x, %x), "
				"ignoring", buf[off] & 0xF0, buf[off+1] & 0xF0);
			goto next;
		}

		des_alloc_len = sizeof(*des) + des_len;
		des = kzalloc(des_alloc_len, GFP_KERNEL);
		if (des == NULL) {
			PRINT_CRIT_ERROR("Unable to allocate designator (len %d, "
				"type %x), ignoring it", des_alloc_len,
				buf[off+1] & 0xF);
			goto out_put;
		}

		des->desig_tgt_dev = cmd->tgt_dev;

		des->desig_len = des_len;
		memcpy(des->desig, &buf[off], des_len);

		TRACE_DBG("des %p, len %d", des, des->desig_len);
		TRACE_BUFF_FLAG(TRACE_DEBUG, "des", des->desig, des->desig_len);

		mutex_lock(&scst_cm_mutex);
		list_add_tail(&des->cm_desig_list_entry, &scst_cm_desig_list);
		mutex_unlock(&scst_cm_mutex);
next:
		off += des_len;
		TRACE_DBG("off %d", off);
	}

out_put:
	scst_put_buf_full(cmd, buf);

out_put_ref:
	percpu_ref_put(&dev->refcnt);

	scst_cm_update_dev_fini(dev);
out:
	TRACE_EXIT();
	return;
}

static int scst_cm_send_init_inquiry(struct scst_device *dev,
	unsigned int unpacked_lun, struct scst_cm_init_inq_priv *priv)
{
	static const uint8_t inq_cdb[6] = { INQUIRY, 1, 0x83, 0x10, 0, 0 };
	__be64 lun;
	struct scst_cmd *cmd;
	int res = 0;

	TRACE_ENTRY();

	if (WARN_ON_ONCE(!dev)) {
		res = -EINVAL;
		goto out;
	}

	if (priv == NULL) {
		priv = kzalloc(sizeof(*priv), GFP_KERNEL);
		if (priv == NULL) {
			PRINT_ERROR("Unable to alloc priv");
			res = -ENOMEM;
			goto out;
		}
		priv->cm_init_inq_finish_fn = scst_cm_init_inq_finish;
		priv->dev = dev;
		percpu_ref_get(&dev->refcnt);
	}

	lun = scst_pack_lun(unpacked_lun, scst_cm_sess->acg->addr_method);

	cmd = scst_rx_cmd(scst_cm_sess, (const uint8_t *)&lun,
			       sizeof(lun), inq_cdb, sizeof(inq_cdb), false);
	if (cmd == NULL) {
		res = -ENOMEM;
		goto out_free;
	}

	scst_cmd_set_expected(cmd, SCST_DATA_READ, 4096);
	scst_cmd_set_queue_type(cmd, SCST_CMD_QUEUE_HEAD_OF_QUEUE);
	scst_cmd_set_tgt_priv(cmd, priv);

	cmd->bypass_blocking = 1;

	scst_cmd_init_done(cmd, SCST_CONTEXT_THREAD);

out:
	TRACE_EXIT_RES(res);
	return res;

out_free:
	percpu_ref_put(&dev->refcnt);

	kfree(priv);
	goto out;
}

/* scst_mutex supposed to be held */
static bool scst_cm_is_lun_free(unsigned int lun)
{
	bool res = true;
	struct list_head *head = &scst_cm_sess->sess_tgt_dev_list[SESS_TGT_DEV_LIST_HASH_FN(lun)];
	struct scst_tgt_dev *tgt_dev;

	TRACE_ENTRY();

	rcu_read_lock();
	list_for_each_entry_rcu(tgt_dev, head, sess_tgt_dev_list_entry) {
		if (tgt_dev->lun == lun) {
			res = false;
			break;
		}
	}
	rcu_read_unlock();

	TRACE_EXIT_RES(res);
	return res;
}

/* scst_mutex supposed to be held */
static unsigned int scst_cm_get_lun(const struct scst_device *dev)
{
	unsigned int res = SCST_MAX_LUN;
	int i;

	TRACE_ENTRY();

	rcu_read_lock();
	for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
		struct list_head *head = &scst_cm_sess->sess_tgt_dev_list[i];
		struct scst_tgt_dev *tgt_dev;

		list_for_each_entry_rcu(tgt_dev, head,
					sess_tgt_dev_list_entry) {
			if (tgt_dev->dev == dev) {
				res = tgt_dev->lun;
				rcu_read_unlock();
				TRACE_DBG("LUN %d found (full LUN %lld)",
					res, tgt_dev->lun);
				goto out;
			}
		}
	}
	rcu_read_unlock();

out:
	TRACE_EXIT_RES(res);
	return res;
}

static uint64_t scst_cm_get_free_lun(void)
{
	uint64_t lun;

	while (1) {
		lun = scst_cm_next_lun++;
		if (lun == SCST_MAX_LUN) {
			scst_cm_next_lun = 0;
			continue;
		}

		if (scst_cm_is_lun_free(lun))
			break;
	}

	return lun;
}

static int scst_cm_dev_register(struct scst_device *dev, uint64_t lun)
{
	int res;
	struct scst_acg_dev *acg_dev;

	TRACE_ENTRY();

	lockdep_assert_held(&scst_mutex);

	TRACE_DBG("dev %s, LUN %ld", dev->virt_name, (unsigned long)lun);

	if (lun == SCST_MAX_LUN) {
		if (scst_cm_get_lun(dev) != SCST_MAX_LUN) {
			TRACE_DBG("Copy Manager already registered device %s",
				  dev->virt_name);
			res = 0;
			goto out;
		}

		lun = scst_cm_get_free_lun();

		res = scst_acg_add_lun(scst_cm_tgt->default_acg,
			scst_cm_tgt->tgt_luns_kobj, dev, lun, SCST_ADD_LUN_CM,
			&acg_dev);
		if (res != 0)
			goto out_err;
	}

	spin_lock_bh(&dev->dev_lock);
	scst_block_dev(dev);
	spin_unlock_bh(&dev->dev_lock);

	atomic_set(&dev->cm_update_req_cnt, 1);

	res = scst_cm_send_init_inquiry(dev, lun, NULL);
	if (res != 0)
		goto out_unblock;

out:
	TRACE_EXIT_RES(res);
	return res;

out_unblock:
	spin_lock_bh(&dev->dev_lock);
	scst_unblock_dev(dev);
	spin_unlock_bh(&dev->dev_lock);

	atomic_set(&dev->cm_update_req_cnt, 0);

	scst_acg_del_lun(scst_cm_tgt->default_acg, lun, false);

out_err:
	scst_cm_next_lun--;
	goto out;
}

static void scst_cm_dev_free_designators(struct scst_device *dev)
{
	struct scst_cm_desig *des, *t;

	TRACE_ENTRY();

	mutex_lock(&scst_cm_mutex);

	list_for_each_entry_safe(des, t, &scst_cm_desig_list, cm_desig_list_entry) {
		if (des->desig_tgt_dev->dev == dev) {
			TRACE_DBG("Deleting des %p", des);
			list_del(&des->cm_desig_list_entry);
			kfree(des);
		}
	}

	mutex_unlock(&scst_cm_mutex);

	TRACE_EXIT();

	return;
}

/* scst_mutex supposed to be held */
static void scst_cm_dev_unregister(struct scst_device *dev)
{
	u32 lun;

	TRACE_ENTRY();

	lockdep_assert_held(&scst_mutex);

	TRACE_DBG("Unregister CM dev %s", dev->virt_name);

	scst_cm_dev_free_designators(dev);

	lun = scst_cm_get_lun(dev);
	if (lun != SCST_MAX_LUN)
		scst_acg_del_lun(scst_cm_tgt->default_acg, lun, false);

	TRACE_EXIT();

	return;
}

static int __scst_cm_update_dev(struct scst_device *dev)
{
	unsigned int lun;
	int rc = 0;

	TRACE_ENTRY();

	TRACE_MGMT_DBG("copy manager: updating device %s", dev->virt_name);

	mutex_lock(&scst_mutex);

	lun = scst_cm_get_lun(dev);
	if (lun == SCST_MAX_LUN) {
		/*
		 * Verify that scst_unregister_virtual_device() is in progress.
		 */
		WARN_ON_ONCE(!dev->remove_completion);
		rc = -EINVAL;
		goto out_unlock;
	}

	scst_cm_dev_free_designators(dev);

	spin_lock_bh(&dev->dev_lock);
	scst_block_dev(dev);
	spin_unlock_bh(&dev->dev_lock);

	rc = scst_cm_send_init_inquiry(dev, lun, NULL);
	if (rc != 0)
		goto out_unblock;

out_unlock:
	mutex_unlock(&scst_mutex);

	TRACE_EXIT();

	return rc;

out_unblock:
	spin_lock_bh(&dev->dev_lock);
	scst_unblock_dev(dev);
	spin_unlock_bh(&dev->dev_lock);

	goto out_unlock;
}

static void
scst_cm_update_dev_start(struct scst_device *dev)
{
	int update_req_cnt, rc;

	update_req_cnt = atomic_inc_return(&dev->cm_update_req_cnt);
	if (update_req_cnt > 1)
		return;

	rc = __scst_cm_update_dev(dev);
	if (rc)
		atomic_set(&dev->cm_update_req_cnt, 0);
}

static void
scst_cm_update_dev_fini(struct scst_device *dev)
{
	int update_req_cnt, rc;

	update_req_cnt = atomic_dec_return(&dev->cm_update_req_cnt);

	WARN_ON_ONCE(update_req_cnt < 0);

	if (update_req_cnt == 0)
		return;

	/*
	 * If we have received at least one update, we must re-update the
	 * designators information. We don't care about the exact number of
	 * updates we've received since the inquiry was submitted, as only the
	 * last one is indicative. So set dev->cm_update_req_cnt to 1 to avoid
	 * unnecessary __scst_cm_update_dev() calls.
	 */
	atomic_set(&dev->cm_update_req_cnt, 1);

	rc = __scst_cm_update_dev(dev);
	if (rc)
		atomic_set(&dev->cm_update_req_cnt, 0);
}

void scst_cm_update_dev(struct scst_device *dev)
{
	TRACE_ENTRY();

	if (!scst_auto_cm_assignment ||
	    !dev->handler->auto_cm_assignment_possible)
		goto out;

	scst_cm_update_dev_start(dev);

out:
	TRACE_EXIT();

	return;
}

int scst_cm_on_dev_register(struct scst_device *dev)
{
	int res = 0;

	TRACE_ENTRY();

	lockdep_assert_held(&scst_mutex);

	if (!scst_auto_cm_assignment || !dev->handler->auto_cm_assignment_possible)
		goto out;

	res = scst_cm_dev_register(dev, SCST_MAX_LUN);

out:
	TRACE_EXIT_RES(res);
	return res;
}

void scst_cm_on_dev_unregister(struct scst_device *dev)
{
	TRACE_ENTRY();

	lockdep_assert_held(&scst_mutex);

	scst_cm_dev_unregister(dev);

	TRACE_EXIT();
	return;
}

/* scst_mutex supposed to be held */
int scst_cm_on_add_acg(struct scst_acg *acg)
{
	int res = 0;

	TRACE_ENTRY();

	lockdep_assert_held(&scst_mutex);

	if (scst_cm_tgt == NULL)
		goto out;

	if (acg->tgt != scst_cm_tgt)
		goto out;

	if (acg != scst_cm_tgt->default_acg) {
		PRINT_ERROR("Copy Manager does not support security groups");
		res = -EINVAL;
		goto out;
	}

out:
	TRACE_EXIT_RES(res);
	return res;
}

/* scst_mutex supposed to be held and activities suspended */
void scst_cm_on_del_acg(struct scst_acg *acg)
{
	scst_assert_activity_suspended();
	lockdep_assert_held(&scst_mutex);
	/* Nothing to do */
}

/* scst_mutex supposed to be held */
int scst_cm_on_add_lun(struct scst_acg_dev *acg_dev, uint64_t lun,
	unsigned int *flags)
{
	int res = 0;

	TRACE_ENTRY();

	lockdep_assert_held(&scst_mutex);

	if (acg_dev->acg != scst_cm_tgt->default_acg)
		goto out;

	if (acg_dev->acg_dev_rd_only || acg_dev->dev->dev_rd_only) {
		PRINT_ERROR("Copy Manager does not support read only devices");
		res = -EINVAL;
		goto out;
	}

	*flags &= ~SCST_ADD_LUN_GEN_UA;

	res = scst_cm_dev_register(acg_dev->dev, lun);

out:
	TRACE_EXIT_RES(res);
	return res;
}

/* scst_mutex supposed to be held */
bool scst_cm_on_del_lun(struct scst_acg_dev *acg_dev, bool gen_report_luns_changed)
{
	bool res = gen_report_luns_changed;

	TRACE_ENTRY();

	lockdep_assert_held(&scst_mutex);

	if (acg_dev->acg != scst_cm_tgt->default_acg)
		goto out;

	scst_cm_dev_free_designators(acg_dev->dev);

	res = false;

out:
	TRACE_EXIT_RES(res);
	return res;
}

/* scst_mutex2 supposed to be held */
static bool scst_cm_check_access_acg(const char *initiator_name,
	const struct scst_device *dev, const struct scst_acg *acg,
	bool default_acg)
{
	bool res = true;
	struct scst_acg_dev *acg_dev;

	TRACE_ENTRY();

	lockdep_assert_held(&scst_mutex2);

	list_for_each_entry(acg_dev, &acg->acg_dev_list, acg_dev_list_entry) {
		if (acg_dev->dev == dev) {
			struct scst_acn *acn;

			if (default_acg)
				goto found;
			list_for_each_entry(acn, &acg->acn_list, acn_list_entry) {
				if (wildcmp(acn->name, initiator_name))
					goto found;
			}
		}
	}

	res = false;

found:
	TRACE_EXIT_RES(res);
	return res;
}

static bool scst_cm_check_access(const char *initiator_name,
	const struct scst_device *dev, bool *read_only)
{
	bool res = true;
	struct scst_tgt_template *tgtt;

	TRACE_ENTRY();

	if (scst_cm_allow_not_connected_copy)
		goto out_rd_only;

	/* ToDo: make it hash based */

	/*
	 * We can't use scst_mutex on commands processing path, because
	 * otherwise we can fall in a deadlock with kthread_stop() in
	 * scst_del_threads() waiting for this command to finish.
	 */
	mutex_lock(&scst_mutex2);

	list_for_each_entry(tgtt, &scst_template_list, scst_template_list_entry) {
		struct scst_tgt *tgt;

		list_for_each_entry(tgt, &tgtt->tgt_list, tgt_list_entry) {
			struct scst_acg *acg;

			if (tgt == scst_cm_tgt)
				continue;

			TRACE_DBG("Checking tgt %s", tgt->tgt_name);

			if (scst_cm_check_access_acg(initiator_name, dev, tgt->default_acg, true))
				goto out_unlock_rd_only;

			list_for_each_entry(acg, &tgt->tgt_acg_list, acg_list_entry) {
				if (scst_cm_check_access_acg(initiator_name, dev, acg, false))
					goto out_unlock_rd_only;
			}
		}
	}

	res = false;
	PRINT_WARNING("Initiator %s not allowed to use device %s in EXTENDED "
		"COPY command", initiator_name, dev->virt_name);

out_unlock_rd_only:
	mutex_unlock(&scst_mutex2);

out_rd_only:
	*read_only = dev->dev_rd_only || dev->swp;

	TRACE_EXIT_RES(res);
	return res;
}

struct scst_cm_tgt_descr {
	struct scst_tgt_dev *tgt_dev;
	unsigned int read_only:1;
	int param_offs;
};

/* Parse the copy source or copy destination (CSCD) descriptors. */
static int scst_cm_parse_id_tgt_descr(struct scst_cmd *cmd, const uint8_t *seg,
	int offs, struct scst_cm_tgt_descr *tgt_descr)
{
	int res = 32;
	struct scst_cm_desig *des;
	int block;
	bool read_only = false;

	TRACE_ENTRY();

	TRACE_BUFF_FLAG(TRACE_DEBUG, "seg", seg, 32);

	EXTRACHECKS_BUG_ON(seg[0] != 0xE4);

	if ((seg[1] & 0xC0) != 0) {
		PRINT_WARNING("LU ID %x not supported", seg[1] & 0xC0);
		scst_set_invalid_field_in_parm_list(cmd, offs+1,
			SCST_INVAL_FIELD_BIT_OFFS_VALID | 6);
		goto out_err;
	}

	if ((seg[1] & 0x20) != 0) {
		TRACE_DBG("NULL tgt descriptor");
		tgt_descr->tgt_dev = NULL;
		goto out;
	}

	if ((seg[1] & 0xF) != 0) {
		PRINT_WARNING("PERIPHERAL DEVICE TYPE %d not supported",
			seg[1] & 0xF);
		scst_set_invalid_field_in_parm_list(cmd, offs+1,
			SCST_INVAL_FIELD_BIT_OFFS_VALID | 0);
		goto out_err;
	}

	block = get_unaligned_be24(&seg[29]);

	mutex_lock(&scst_cm_mutex);

	/* ToDo: make it hash based */

	list_for_each_entry(des, &scst_cm_desig_list, cm_desig_list_entry) {
		TRACE_DBG("des %p (tgt_dev %p, lun %lld)", des, des->desig_tgt_dev,
			(unsigned long long)des->desig_tgt_dev->lun);
		/* Check the code set field. */
		if (seg[4] != des->desig[0])
			continue;
		/* Check the association and designator type fields. */
		if (seg[5] != des->desig[1])
			continue;
		/* Check the designator length. */
		if (seg[7] > 20) {
			PRINT_WARNING("Initiator sent non-compliant identification descriptor (len %u > 20)",
				      seg[7]);
			continue;
		}
		if (seg[7] != des->desig[3])
			continue;
		if (memcmp(&des->desig[4], &seg[8], seg[7]) == 0) {
			TRACE_DBG("Tgt_dev %p (lun %lld) found",
				des->desig_tgt_dev,
				(unsigned long long)des->desig_tgt_dev->lun);

			mutex_unlock(&scst_cm_mutex);

			if (block != des->desig_tgt_dev->dev->block_size) {
				PRINT_WARNING("Block size %d doesn't match %d", block,
					des->desig_tgt_dev->dev->block_size);
				scst_set_invalid_field_in_parm_list(cmd, offs+29, 0);
				goto out_err;
			}

			if (!scst_cm_check_access(cmd->sess->initiator_name,
					des->desig_tgt_dev->dev, &read_only))
				goto out_not_found;

			tgt_descr->tgt_dev = des->desig_tgt_dev;
			tgt_descr->read_only = read_only;
			TRACE_DBG("Found des %p (tgt_dev %p, read_only %d)",
				des, tgt_descr->tgt_dev, tgt_descr->read_only);
			goto out;
		}
	}

	mutex_unlock(&scst_cm_mutex);

	TRACE(TRACE_MINOR|TRACE_SCSI, "Target descriptor designator not found "
		"(initiator %s, offs %d)", cmd->sess->initiator_name, offs);
	TRACE_BUFF_FLAG(TRACE_MINOR|TRACE_SCSI, "Designator", seg, 32);

out_not_found:
	scst_set_invalid_field_in_parm_list(cmd, offs, 0);

out_err:
	res = -1;

out:
	TRACE_EXIT_RES(res);
	return res;
}

static int scst_cm_set_seg_err_sense(struct scst_cmd *cmd, int asc, int ascq,
	int seg_num, int offs)
{
	int res, d_sense = scst_get_cmd_dev_d_sense(cmd);

	TRACE_ENTRY();

	TRACE_DBG("cmd %p, seg %d, offs %d (d_sense %d)", cmd, seg_num, offs,
		d_sense);

	res = scst_set_cmd_error_status(cmd, SAM_STAT_CHECK_CONDITION);
	if (res != 0)
		goto out;

	res = scst_alloc_sense(cmd, 1);
	if (res != 0) {
		PRINT_ERROR("Lost COPY ABORTED sense data");
		goto out;
	}

	sBUG_ON(cmd->sense_buflen < 18);
	BUILD_BUG_ON(SCST_SENSE_BUFFERSIZE < 18);

	if (d_sense) {
		/* Descriptor format */
		cmd->sense[0] = 0x72;
		cmd->sense[1] = COPY_ABORTED;
		cmd->sense[2] = asc;
		cmd->sense[3] = ascq;
		cmd->sense[7] = 20; /* additional Sense Length */

		cmd->sense[8] = 1; /* Command specific descriptor */
		cmd->sense[9] = 0x0A;
		put_unaligned_be16(seg_num, &cmd->sense[14]);

		cmd->sense[20] = 2; /* Sense key specific descriptor */
		cmd->sense[21] = 6;
		cmd->sense[24] = 0xA0;
		put_unaligned_be16(offs, &cmd->sense[25]);

		cmd->sense_valid_len = 28;
	} else {
		/* Fixed format */
		cmd->sense[0] = 0x70;
		cmd->sense[2] = COPY_ABORTED;
		cmd->sense[7] = 0x0a; /* additional Sense Length */
		cmd->sense[12] = asc;
		cmd->sense[13] = ascq;

		put_unaligned_be16(seg_num, &cmd->sense[10]);

		cmd->sense[15] = 0xA0;
		put_unaligned_be16(offs, &cmd->sense[16]);

		cmd->sense_valid_len = 18;
	}

	TRACE_BUFFER("Sense set", cmd->sense, cmd->sense_valid_len);

out:
	TRACE_EXIT_RES(res);
	return res;
}

static void scst_cm_fantom_cmd_finished(struct scst_cmd *cmd)
{
	TRACE_ENTRY();

	TRACE_DBG("Fantom cmd %p", cmd);

	EXTRACHECKS_BUG_ON(!cmd->internal);

	/* Nothing to do */

	TRACE_EXIT();
	return;
}

static int scst_cm_add_to_descr_list(struct scst_cmd *ec_cmd,
	struct scst_tgt_dev *tgt_dev)
{
	int res;
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
	struct scst_cm_dev_entry *e, *t;
	struct scst_cmd *fcmd;
	bool added;

	TRACE_ENTRY();

	/* Check if we already have this device in the list */
	list_for_each_entry(e, &priv->cm_sorted_devs_list, cm_sorted_devs_list_entry) {
		if (e->cm_fcmd->dev == tgt_dev->dev) {
			TRACE_DBG("Dev %p is already in cm_sorted_devs_list",
				tgt_dev->dev);
			goto out_success;
		}
	}

	e = kzalloc(sizeof(*e), GFP_KERNEL);
	if (e == NULL) {
		PRINT_ERROR("Unable to allocate scst_cm_dev_entry (size %d)",
			(int)sizeof(*e));
		goto out_enomem;
	}

	if (ec_cmd->dev == tgt_dev->dev) {
		fcmd = ec_cmd;
		goto skip_fcmd_create;
	}

	fcmd = __scst_create_prepare_internal_cmd(ec_cmd->cdb,
		ec_cmd->cdb_len, SCST_CMD_QUEUE_SIMPLE, tgt_dev,
		GFP_KERNEL, true);
	if (fcmd == NULL)
		goto out_enomem_free_e;

	fcmd->expected_data_direction = ec_cmd->expected_data_direction;
	fcmd->expected_transfer_len_full = ec_cmd->expected_transfer_len_full;
	fcmd->expected_values_set = 1;

	fcmd->cmd_data_descriptors = ec_cmd->cmd_data_descriptors;
	fcmd->cmd_data_descriptors_cnt = ec_cmd->cmd_data_descriptors_cnt;

	fcmd->state = SCST_CMD_STATE_EXEC_CHECK_BLOCKING;

	res = scst_cm_add_to_internal_cmd_list(fcmd, ec_cmd, ec_cmd,
			scst_cm_fantom_cmd_finished);
	if (res != 0)
		goto out_free_cmd;

skip_fcmd_create:
	TRACE_DBG("ec_cmd %p, e %p, fcmd %p, tgt_dev %p (dev %p)", ec_cmd, e, fcmd,
		tgt_dev, tgt_dev->dev);

	e->cm_fcmd = fcmd;

	added = false;
	list_for_each_entry_reverse(t, &priv->cm_sorted_devs_list, cm_sorted_devs_list_entry) {
		EXTRACHECKS_BUG_ON(t->cm_fcmd->dev == tgt_dev->dev);
		if (((unsigned long)e->cm_fcmd->dev) > ((unsigned long)t->cm_fcmd->dev)) {
			__list_add(&e->cm_sorted_devs_list_entry,
				&t->cm_sorted_devs_list_entry,
				t->cm_sorted_devs_list_entry.next);
			added = true;
			break;
		}
	}
	if (!added)
		list_add(&e->cm_sorted_devs_list_entry,
			&priv->cm_sorted_devs_list);

#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_EXTRACHECKS)
	{
		struct scst_cm_dev_entry *tp = NULL;

		list_for_each_entry(t, &priv->cm_sorted_devs_list, cm_sorted_devs_list_entry) {
			TRACE_DBG("t %p, cm dev %p", t, t->cm_fcmd->dev);
			if (tp != NULL) {
				if (((unsigned long)t->cm_fcmd->dev) <= ((unsigned long)tp->cm_fcmd->dev)) {
					list_for_each_entry(t, &priv->cm_sorted_devs_list, cm_sorted_devs_list_entry) {
						pr_emerg("%s: t %p, cm dev %p\n",
							 __func__, t,
							 t->cm_fcmd->dev);
					}
					sBUG();
					break;
				}
				tp = t;
			}
			tp = t;
		}
	}
#endif

out_success:
	res = 0;

out:
	TRACE_EXIT_RES(res);
	return res;

out_free_cmd:
	__scst_cmd_put(fcmd);

out_enomem_free_e:
	kfree(e);

out_enomem:
	scst_set_busy(ec_cmd);
	res = -ENOMEM;
	goto out;
}

static int scst_cm_parse_b2b_seg_descr(struct scst_cmd *ec_cmd,
	const uint8_t *seg, const struct scst_cm_tgt_descr *tgt_descrs,
	int tgt_descrs_cnt, int seg_num)
{
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;
	struct scst_ext_copy_seg_descr *d = &priv->cm_seg_descrs[seg_num];
	int res = 28, rc, len, src_des_idx, tgt_des_idx, blocks, dc;
	const struct scst_cm_tgt_descr *src_des, *tgt_des;

	TRACE_ENTRY();

	TRACE_BUFF_FLAG(TRACE_DEBUG, "seg", seg, 28);

	EXTRACHECKS_BUG_ON(seg[0] != 2);

	len = get_unaligned_be16(&seg[2]);
	if (len != 0x18) {
		PRINT_WARNING("Incorrect B2B segment descriptor len %d", len);
		scst_cm_set_seg_err_sense(ec_cmd, 0, 0, seg_num, 2);
		goto out_err;
	}

	src_des_idx = get_unaligned_be16(&seg[4]);
	if (src_des_idx >= tgt_descrs_cnt) {
		PRINT_WARNING("Invalid src descriptor index %d", src_des_idx);
		scst_cm_set_seg_err_sense(ec_cmd, 0, 0, seg_num, 4);
		goto out_err;
	}

	src_des = &tgt_descrs[src_des_idx];
	if (src_des->tgt_dev == NULL) {
		PRINT_WARNING("Segment with NULL src tgt device");
		/* COPY TARGET DEVICE NOT REACHABLE */
		scst_cm_set_seg_err_sense(ec_cmd, 0xD, 2, seg_num, 4);
		goto out_err;
	}

	tgt_des_idx = get_unaligned_be16(&seg[6]);
	if (tgt_des_idx >= tgt_descrs_cnt) {
		PRINT_WARNING("Invalid tgt descriptor index %d", tgt_des_idx);
		scst_cm_set_seg_err_sense(ec_cmd, 0, 0, seg_num, 6);
		goto out_err;
	}

	tgt_des = &tgt_descrs[tgt_des_idx];
	if (tgt_des->tgt_dev == NULL) {
		PRINT_WARNING("Segment with NULL tgt device");
		/* COPY TARGET DEVICE NOT REACHABLE */
		scst_cm_set_seg_err_sense(ec_cmd, 0xD, 2, seg_num, 6);
		goto out_err;
	}
	if (tgt_des->read_only) {
		PRINT_WARNING("Target descriptor refers to read-only device");
		scst_cm_set_seg_err_sense(ec_cmd, 0, 0, seg_num, 6);
		goto out_err;
	}

	dc = (seg[1] >> 1) & 1;
	blocks = get_unaligned_be16(&seg[10]);
	if (dc)
		len = blocks << tgt_des->tgt_dev->dev->block_shift;
	else
		len = blocks << src_des->tgt_dev->dev->block_shift;

	if (unlikely((len & (src_des->tgt_dev->dev->block_size-1)) != 0) ||
	    unlikely((len & (tgt_des->tgt_dev->dev->block_size-1)) != 0)) {
		PRINT_WARNING("Data len %d is not even for block size (src block "
			"size %d, dst block size %d)", len,
			src_des->tgt_dev->dev->block_size,
			tgt_des->tgt_dev->dev->block_size);
		scst_cm_set_seg_err_sense(ec_cmd, 0, 0, seg_num, 10);
		goto out_err;
	}

	d->type = SCST_EXT_COPY_SEG_DATA;
	d->data_descr.data_len = len;
	d->src_tgt_dev = src_des->tgt_dev;
	d->data_descr.src_lba = get_unaligned_be64(&seg[12]);
	d->dst_tgt_dev = tgt_des->tgt_dev;
	d->data_descr.dst_lba = get_unaligned_be64(&seg[20]);
	d->tgt_descr_offs = tgt_des->param_offs;

	TRACE(TRACE_DEBUG|TRACE_SCSI, "ec_cmd %p, src dev %s, dst dev %s, "
		"len %d, src_lba %lld, dst_lba %lld", ec_cmd,
		d->src_tgt_dev->dev->virt_name, d->dst_tgt_dev->dev->virt_name,
		len, (long long)d->data_descr.src_lba,
		(long long)d->data_descr.dst_lba);

	TRACE_DBG("src tgt_dev %p, dst tgt_dev %p, tgt_descr_offs %d",
		d->src_tgt_dev, d->dst_tgt_dev, d->tgt_descr_offs);

	rc = scst_cm_add_to_descr_list(ec_cmd, src_des->tgt_dev);
	if (rc != 0) {
		res = rc;
		goto out;
	}

	rc = scst_cm_add_to_descr_list(ec_cmd, tgt_des->tgt_dev);
	if (rc != 0) {
		res = rc;
		goto out;
	}

out:
	TRACE_EXIT_RES(res);
	return res;

out_err:
	res = -1;
	goto out;
}

static void scst_cm_free_ec_priv(struct scst_cmd *ec_cmd, bool unblock_dev)
{
	struct scst_cm_ec_cmd_priv *p = ec_cmd->cmd_data_descriptors;
	struct scst_cm_dev_entry *e, *t;
	struct scst_cm_internal_cmd_priv *ip, *it;
	unsigned long flags;

	TRACE_ENTRY();

	list_for_each_entry_safe(e, t, &p->cm_sorted_devs_list,
				cm_sorted_devs_list_entry) {
		TRACE_DBG("Deleting e %p", e);
		list_del(&e->cm_sorted_devs_list_entry);
		kfree(e);
	}

	list_for_each_entry_safe(ip, it, &p->cm_internal_cmd_list,
				cm_internal_cmd_list_entry) {
		struct scst_cmd *c = ip->cm_cmd;

		scst_cm_del_free_from_internal_cmd_list(c, unblock_dev);
		__scst_cmd_put(c);
	}

	/* Lock to sync with scst_cm_abort_ec_cmd() */
	spin_lock_irqsave(&scst_cm_lock, flags);
	ec_cmd->cmd_data_descriptors = NULL;
	ec_cmd->cmd_data_descriptors_cnt = 0;
	spin_unlock_irqrestore(&scst_cm_lock, flags);

	kfree(p);

	TRACE_EXIT();
	return;
}

/* Parse the EXTENDED COPY parameter list. */
int scst_cm_parse_descriptors(struct scst_cmd *ec_cmd)
{
	int res = 0, rc;
	struct scst_cm_list_id *plist_id = NULL;
	ssize_t length = 0;
	uint8_t *buf;
	int list_id, list_id_usage, len, tgt_len, seg_len;
	struct scst_cm_ec_cmd_priv *p;
	int tgt_cnt, seg_cnt, i, offs, t;
	struct scst_cm_tgt_descr *tgt_descrs;
	unsigned long flags;

	TRACE_ENTRY();

	EXTRACHECKS_BUG_ON(ec_cmd->cmd_data_descriptors != NULL);

	if (ec_cmd->cdb[1] != 0) {
		/*
		 * SCST only supports the EXTENDED COPY(LID1) command.
		 * Reject EXTENDED COPY(LID4) commands since these have a
		 * completely different parameter format. Definitions for both
		 * commands are available in SPC-4 while the LID1 variant has
		 * been removed from SPC-5 and SPC-6.
		 */
		scst_set_invalid_field_in_cdb(ec_cmd, 1, 0);
		goto out_abn;
	}

	length = scst_get_buf_full_sense(ec_cmd, &buf);
	if (unlikely(length <= 0)) {
		if (length == 0)
			goto out_put;
		else
			goto out_abn;
	}

	if (length < 16) {
		PRINT_WARNING("Too small EXTENDED COPY data len %d", (int)length);
		scst_set_invalid_field_in_cdb(ec_cmd, 10, 0);
		goto out_abn_put;
	}

	list_id = buf[0];
	list_id_usage = (buf[1] & 0x18) >> 3;

	TRACE_BUFF_FLAG(TRACE_DEBUG, "buf", buf, length);

	TRACE_DBG("list_id %d, list_id_usage %d", list_id, list_id_usage);

	switch (list_id_usage) {
	case 0:
	case 2:
		plist_id = scst_cm_add_list_id(ec_cmd, list_id);
		if (plist_id == NULL)
			goto out_abn_put;
		break;
	case 3:
		if (list_id != 0) {
			PRINT_WARNING("Invalid list ID %d with list ID usage %d",
				list_id, list_id_usage);
			scst_set_invalid_field_in_parm_list(ec_cmd, 0, 0);
			goto out_abn_put;
		}
		break;
	default:
		PRINT_WARNING("Invalid list ID usage %d, rejecting", list_id_usage);
		scst_set_invalid_field_in_parm_list(ec_cmd, 1,
			SCST_INVAL_FIELD_BIT_OFFS_VALID | 3);
		goto out_abn_put;
	}

	len = get_unaligned_be32(&buf[12]);
	if (len != 0) {
		PRINT_WARNING("Inline data not supported (len %d)", len);
		scst_set_cmd_error(ec_cmd, SCST_LOAD_SENSE(scst_sense_inline_data_length_exceeded));
		goto out_del_abn_put;
	}

	tgt_len = get_unaligned_be16(&buf[2]);
	seg_len = get_unaligned_be32(&buf[8]);

	if (tgt_len == 0) {
		if (seg_len == 0)
			goto out_del_put;
		else {
			PRINT_WARNING("Zero target descriptors with non-zero "
				"segments len (%d)", seg_len);
			scst_set_invalid_field_in_parm_list(ec_cmd, 2, 0);
			goto out_del_abn_put;
		}
	}

	if ((tgt_len + seg_len + 16) > length) {
		PRINT_WARNING("Parameters truncation");
		scst_set_cmd_error(ec_cmd,
			SCST_LOAD_SENSE(scst_sense_parameter_list_length_invalid));
		goto out_del_abn_put;
	}

	if ((tgt_len + seg_len + 16) != length) {
		PRINT_WARNING("Unexpected inline data");
		scst_set_cmd_error(ec_cmd,
			SCST_LOAD_SENSE(scst_sense_inline_data_length_exceeded));
		goto out_del_abn_put;
	}

	if ((tgt_len % 32) != 0) {
		PRINT_WARNING("Invalid tgt len %d", tgt_len);
		scst_set_invalid_field_in_parm_list(ec_cmd, 2, 0);
		goto out_del_abn_put;
	}

	tgt_cnt = tgt_len/32;
	if (tgt_cnt > SCST_CM_MAX_TGT_DESCR_CNT) {
		PRINT_WARNING("Too many target descriptors %d", tgt_cnt);
		scst_set_cmd_error(ec_cmd,
			SCST_LOAD_SENSE(scst_sense_too_many_target_descriptors));
		goto out_del_abn_put;
	}

	TRACE_DBG("tgt_cnt %d", tgt_cnt);

	tgt_descrs = kcalloc(tgt_cnt, sizeof(*tgt_descrs), GFP_KERNEL);
	if (tgt_descrs == NULL) {
		TRACE(TRACE_OUT_OF_MEM, "Unable to allocate tgt_descrs "
			"(count %d, size %zd)", tgt_cnt,
			sizeof(*tgt_descrs) * tgt_cnt);
		scst_set_busy(ec_cmd);
		goto out_del_abn_put;
	}

	offs = 16;
	for (i = 0; i < tgt_cnt; i++) {
		TRACE_DBG("offs %d", offs);
		switch (buf[offs]) {
		case 0xE4: /* identification descriptor target descriptor format */
			rc = scst_cm_parse_id_tgt_descr(ec_cmd, &buf[offs], offs,
				&tgt_descrs[i]);
			if (rc <= 0)
				goto out_free_tgt_descr;
			break;
		default:
			PRINT_WARNING("Not supported target descriptor %x", buf[offs]);
			scst_set_cmd_error(ec_cmd, SCST_LOAD_SENSE(scst_sense_unsupported_tgt_descr_type));
			goto out_free_tgt_descr;
		}
		tgt_descrs[i].param_offs = offs;
		offs += rc;
	}

	WARN_ON(offs != tgt_len + 16);

	t = offs;
	seg_cnt = 0;
	while (offs < length) {
		if (seg_cnt == SCST_CM_MAX_SEG_DESCR_CNT) {
			PRINT_WARNING("Too many segment descriptors");
			scst_set_cmd_error(ec_cmd,
				SCST_LOAD_SENSE(
				    scst_sense_too_many_segment_descriptors));
			goto out_free_tgt_descr;
		}
		switch (buf[offs]) {
		case 2: /* block device to block device segment descriptor */
			offs += 28;
			break;
		default:
			PRINT_WARNING("Not supported segment descriptor %x", buf[offs]);
			scst_set_cmd_error(ec_cmd,
				SCST_LOAD_SENSE(scst_sense_unsupported_seg_descr_type));
			goto out_free_tgt_descr;
		}
		seg_cnt++;
	}
	offs = t;

	TRACE_DBG("seg_cnt %d", seg_cnt);

	p = kzalloc(sizeof(*p) + seg_cnt * sizeof(struct scst_ext_copy_seg_descr), GFP_KERNEL);
	if (p == NULL) {
		TRACE(TRACE_OUT_OF_MEM, "Unable to allocate Extended Copy "
			"descriptors (seg_cnt %d)", seg_cnt);
		scst_set_busy(ec_cmd);
		goto out_free_tgt_descr;
	}

	p->cm_list_id = plist_id;
	plist_id = NULL;
	INIT_LIST_HEAD(&p->cm_sorted_devs_list);
	INIT_LIST_HEAD(&p->cm_internal_cmd_list);
	p->cm_error = SCST_CM_ERROR_NONE;
	mutex_init(&p->cm_mutex);

	ec_cmd->cmd_data_descriptors = p;
	ec_cmd->cmd_data_descriptors_cnt = seg_cnt;

	res = scst_cm_add_to_descr_list(ec_cmd, ec_cmd->tgt_dev);
	if (res != 0)
		goto out_free_p;

	for (i = 0; i < seg_cnt; i++) {
		TRACE_DBG("offs %d", offs);
		switch (buf[offs]) {
		case 2: /* block device to block device segment descriptor */
			rc = scst_cm_parse_b2b_seg_descr(ec_cmd, &buf[offs],
				tgt_descrs, tgt_cnt, i);
			if (rc <= 0) {
				if (rc == -ENOMEM)
					goto out_free_p;
				else {
					/*
					 * We may need to keep list_id for a
					 * while for further FAILED SEGMENT
					 * DETAILS of RECEIVE COPY RESULTS
					 */
					scst_cm_store_list_id_details(ec_cmd);
					goto out_free_tgt_descr;
				}
			}
			EXTRACHECKS_BUG_ON(rc != 28);
			break;
		default:
			sBUG();
		}
		offs += rc;
	}

	kfree(tgt_descrs);

out_del_put:
	if (plist_id != NULL) {
		spin_lock_irqsave(&scst_cm_lock, flags);
		scst_cm_del_free_list_id(plist_id);
		spin_unlock_irqrestore(&scst_cm_lock, flags);
	}

out_put:
	scst_put_buf_full(ec_cmd, buf);

out:
	TRACE_EXIT_RES(res);
	return res;

out_free_p:
	scst_cm_free_ec_priv(ec_cmd, false);

out_free_tgt_descr:
	kfree(tgt_descrs);

out_del_abn_put:
	if (plist_id != NULL) {
		spin_lock_irqsave(&scst_cm_lock, flags);
		scst_cm_del_free_list_id(plist_id);
		spin_unlock_irqrestore(&scst_cm_lock, flags);
	}

out_abn_put:
	scst_put_buf_full(ec_cmd, buf);

out_abn:
	scst_set_cmd_abnormal_done_state(ec_cmd);
	res = -1;
	goto out;
}

void scst_cm_free_descriptors(struct scst_cmd *ec_cmd)
{
	struct scst_cm_ec_cmd_priv *priv = ec_cmd->cmd_data_descriptors;

	TRACE_ENTRY();

	TRACE_DBG("cmd %p (internal %d)", ec_cmd, ec_cmd->internal);

	if (priv == NULL) {
		/* It can be for early errors */
		goto out;
	}

	if (ec_cmd->internal)
		goto out;

	if (priv->cm_list_id != NULL)
		scst_cm_sched_del_list_id(ec_cmd);

	scst_cm_free_ec_priv(ec_cmd, true);

out:
	TRACE_EXIT();
	return;
}


static ssize_t scst_cm_allow_not_conn_copy_show(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
	ssize_t res;

	TRACE_ENTRY();

	res = sprintf(buf, "%d\n%s", scst_cm_allow_not_connected_copy,
		(scst_cm_allow_not_connected_copy == SCST_ALLOW_NOT_CONN_COPY_DEF) ?
			"" : SCST_SYSFS_KEY_MARK "\n");

	TRACE_EXIT_RES(res);
	return res;
}

static ssize_t scst_cm_allow_not_conn_copy_store(struct kobject *kobj,
	struct kobj_attribute *attr, const char *buffer, size_t size)
{
	ssize_t res;
	unsigned long val;

	TRACE_ENTRY();

	res = kstrtoul(buffer, 0, &val);
	if (res != 0) {
		PRINT_ERROR("strtoul() for %s failed: %zd", buffer, res);
		goto out;
	}

	scst_cm_allow_not_connected_copy = val;

	res = size;

out:
	TRACE_EXIT_RES(res);
	return res;
}

static struct kobj_attribute scst_cm_allow_not_conn_copy_attr =
	__ATTR(allow_not_connected_copy, S_IRUGO|S_IWUSR,
		scst_cm_allow_not_conn_copy_show,
		scst_cm_allow_not_conn_copy_store);

static const struct attribute *scst_cm_tgtt_attrs[] = {
	&scst_cm_allow_not_conn_copy_attr.attr,
	NULL,
};


static int scst_cm_get_initiator_port_transport_id(struct scst_tgt *tgt,
	struct scst_session *scst_sess, uint8_t **transport_id)
{
	int res = 0;
	uint8_t *tid = NULL;

	TRACE_ENTRY();

	BUILD_BUG_ON((sizeof(SCST_CM_TID_ID)+3) > SCST_CM_TID_SIZE);
	BUILD_BUG_ON(TID_COMMON_SIZE != SCST_CM_TID_SIZE);

	if (scst_sess == NULL) {
		res = SCST_TRANSPORTID_PROTOCOLID_COPY_MGR;
		goto out;
	}

	tid = kzalloc(SCST_CM_TID_SIZE, GFP_KERNEL);
	if (tid == NULL) {
		PRINT_ERROR("Allocation of TransportID (size %d) failed",
			SCST_CM_TID_SIZE);
		res = -ENOMEM;
		goto out;
	}

	tid[0] = SCST_TRANSPORTID_PROTOCOLID_COPY_MGR;
	memcpy(&tid[2], SCST_CM_TID_ID, sizeof(SCST_CM_TID_ID));

	*transport_id = tid;

out:
	TRACE_EXIT_RES(res);
	return res;
}

static int scst_cm_release(struct scst_tgt *tgt)
{
	TRACE_ENTRY();
	TRACE_EXIT();
	return 0;
}

static int scst_cm_xmit_response(struct scst_cmd *cmd)
{
	struct scst_icmd_priv *icmd_priv = cmd->tgt_i_priv;
	scst_i_finish_fn_t f = icmd_priv->finish_fn;
	int res = SCST_TGT_RES_SUCCESS;

	TRACE_ENTRY();

	/*
	 * Used for CM-only generated commands, i.e. commands generated without
	 * any external command, like INQUIRY.
	 */

	f(cmd);
	scst_tgt_cmd_done(cmd, SCST_CONTEXT_SAME);

	TRACE_EXIT_RES(res);
	return res;
}

static void scst_cm_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd)
{
	/* Nothing to do */
	return;
}

static int scst_cm_report_aen(struct scst_aen *aen)
{
	/* Nothing to do */
	scst_aen_done(aen);
	return SCST_AEN_RES_SUCCESS;
}

static struct scst_tgt_template scst_cm_tgtt = {
	.name			= SCST_CM_NAME,
	.sg_tablesize		= 0xffff,
	.enabled_attr_not_needed = 1,
	.dif_supported		= 1,
	.hw_dif_type1_supported = 1,
	.hw_dif_type2_supported = 1,
	.hw_dif_type3_supported = 1,
	.release		= scst_cm_release,
	.xmit_response		= scst_cm_xmit_response,
	.task_mgmt_fn_done	= scst_cm_task_mgmt_fn_done,
	.report_aen             = scst_cm_report_aen,
	.get_initiator_port_transport_id = scst_cm_get_initiator_port_transport_id,
	.tgtt_attrs = scst_cm_tgtt_attrs,
};

int __init scst_cm_init(void)
{
	int res = 0;

	TRACE_ENTRY();

	spin_lock_init(&scst_cm_lock);

	res = scst_register_target_template(&scst_cm_tgtt);
	if (res != 0) {
		PRINT_ERROR("Unable to register copy manager template: %d", res);
		goto out;
	}

	scst_cm_tgt = scst_register_target(&scst_cm_tgtt, SCST_CM_TGT_NAME);
	if (scst_cm_tgt == NULL) {
		PRINT_ERROR("%s", "scst_register_target() failed");
		res = -EFAULT;
		goto out_unreg_tgtt;
	}

	scst_cm_sess = scst_register_session(scst_cm_tgt, false,
				SCST_CM_SESS_NAME, NULL, NULL, NULL);
	if (scst_cm_sess == NULL) {
		PRINT_ERROR("%s", "scst_register_session() failed");
		res = -EFAULT;
		goto out_unreg_tgt;
	}

out:
	TRACE_EXIT_RES(res);
	return res;

out_unreg_tgt:
	scst_unregister_target(scst_cm_tgt);

out_unreg_tgtt:
	scst_unregister_target_template(&scst_cm_tgtt);
	goto out;
}

void __exit scst_cm_exit(void)
{
	TRACE_ENTRY();

	scst_unregister_session(scst_cm_sess, true, NULL);
	scst_unregister_target(scst_cm_tgt);
	scst_unregister_target_template(&scst_cm_tgtt);

	TRACE_EXIT();
	return;
}
