static int queue_cmd_ring()

in target_core_user.c [1010:1144]


static int queue_cmd_ring(struct tcmu_cmd *tcmu_cmd, sense_reason_t *scsi_err)
{
	struct tcmu_dev *udev = tcmu_cmd->tcmu_dev;
	struct se_cmd *se_cmd = tcmu_cmd->se_cmd;
	size_t base_command_size, command_size;
	struct tcmu_mailbox *mb = udev->mb_addr;
	struct tcmu_cmd_entry *entry;
	struct iovec *iov;
	int iov_cnt, iov_bidi_cnt;
	uint32_t cmd_id, cmd_head;
	uint64_t cdb_off;
	uint32_t blk_size = udev->data_blk_size;
	/* size of data buffer needed */
	size_t data_length = (size_t)tcmu_cmd->dbi_cnt * blk_size;

	*scsi_err = TCM_NO_SENSE;

	if (test_bit(TCMU_DEV_BIT_BLOCKED, &udev->flags)) {
		*scsi_err = TCM_LUN_BUSY;
		return -1;
	}

	if (test_bit(TCMU_DEV_BIT_BROKEN, &udev->flags)) {
		*scsi_err = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
		return -1;
	}

	if (!list_empty(&udev->qfull_queue))
		goto queue;

	if (data_length > (size_t)udev->max_blocks * blk_size) {
		pr_warn("TCMU: Request of size %zu is too big for %zu data area\n",
			data_length, (size_t)udev->max_blocks * blk_size);
		*scsi_err = TCM_INVALID_CDB_FIELD;
		return -1;
	}

	iov_cnt = tcmu_alloc_data_space(udev, tcmu_cmd, &iov_bidi_cnt);
	if (iov_cnt < 0)
		goto free_and_queue;

	/*
	 * Must be a certain minimum size for response sense info, but
	 * also may be larger if the iov array is large.
	 */
	base_command_size = tcmu_cmd_get_base_cmd_size(iov_cnt);
	command_size = tcmu_cmd_get_cmd_size(tcmu_cmd, base_command_size);

	if (command_size > (udev->cmdr_size / 2)) {
		pr_warn("TCMU: Request of size %zu is too big for %u cmd ring\n",
			command_size, udev->cmdr_size);
		tcmu_cmd_free_data(tcmu_cmd, tcmu_cmd->dbi_cur);
		*scsi_err = TCM_INVALID_CDB_FIELD;
		return -1;
	}

	if (!is_ring_space_avail(udev, command_size))
		/*
		 * Don't leave commands partially setup because the unmap
		 * thread might need the blocks to make forward progress.
		 */
		goto free_and_queue;

	if (xa_alloc(&udev->commands, &cmd_id, tcmu_cmd, XA_LIMIT(1, 0xffff),
		     GFP_NOWAIT) < 0) {
		pr_err("tcmu: Could not allocate cmd id.\n");

		tcmu_cmd_free_data(tcmu_cmd, tcmu_cmd->dbi_cnt);
		*scsi_err = TCM_OUT_OF_RESOURCES;
		return -1;
	}
	tcmu_cmd->cmd_id = cmd_id;

	pr_debug("allocated cmd id %u for cmd %p dev %s\n", tcmu_cmd->cmd_id,
		 tcmu_cmd, udev->name);

	cmd_head = ring_insert_padding(udev, command_size);

	entry = udev->cmdr + cmd_head;
	memset(entry, 0, command_size);
	tcmu_hdr_set_op(&entry->hdr.len_op, TCMU_OP_CMD);

	/* prepare iov list and copy data to data area if necessary */
	tcmu_cmd_reset_dbi_cur(tcmu_cmd);
	iov = &entry->req.iov[0];

	if (se_cmd->data_direction == DMA_TO_DEVICE ||
	    se_cmd->se_cmd_flags & SCF_BIDI)
		scatter_data_area(udev, tcmu_cmd, &iov);
	else
		tcmu_setup_iovs(udev, tcmu_cmd, &iov, se_cmd->data_length);

	entry->req.iov_cnt = iov_cnt - iov_bidi_cnt;

	/* Handle BIDI commands */
	if (se_cmd->se_cmd_flags & SCF_BIDI) {
		iov++;
		tcmu_setup_iovs(udev, tcmu_cmd, &iov, tcmu_cmd->data_len_bidi);
		entry->req.iov_bidi_cnt = iov_bidi_cnt;
	}

	tcmu_setup_cmd_timer(tcmu_cmd, udev->cmd_time_out, &udev->cmd_timer);

	entry->hdr.cmd_id = tcmu_cmd->cmd_id;

	tcmu_hdr_set_len(&entry->hdr.len_op, command_size);

	/* All offsets relative to mb_addr, not start of entry! */
	cdb_off = CMDR_OFF + cmd_head + base_command_size;
	memcpy((void *) mb + cdb_off, se_cmd->t_task_cdb, scsi_command_size(se_cmd->t_task_cdb));
	entry->req.cdb_off = cdb_off;
	tcmu_flush_dcache_range(entry, command_size);

	UPDATE_HEAD(mb->cmd_head, command_size, udev->cmdr_size);
	tcmu_flush_dcache_range(mb, sizeof(*mb));

	list_add_tail(&tcmu_cmd->queue_entry, &udev->inflight_queue);

	if (!test_bit(TCMU_DEV_BIT_PLUGGED, &udev->flags))
		uio_event_notify(&udev->uio_info);

	return 0;

free_and_queue:
	tcmu_cmd_free_data(tcmu_cmd, tcmu_cmd->dbi_cur);
	tcmu_cmd_reset_dbi_cur(tcmu_cmd);

queue:
	if (add_to_qfull_queue(tcmu_cmd)) {
		*scsi_err = TCM_OUT_OF_RESOURCES;
		return -1;
	}

	return 1;
}