Exemplo n.º 1
0
/*
 * Send TX_RDY (transfer ready).
 */
int ft_write_pending(struct se_cmd *se_cmd)
{
	struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
	struct fc_frame *fp;
	struct fcp_txrdy *txrdy;
	struct fc_lport *lport;
	struct fc_exch *ep;
	struct fc_frame_header *fh;
	u32 f_ctl;

	ft_dump_cmd(cmd, __func__);

	ep = fc_seq_exch(cmd->seq);
	lport = ep->lp;
	fp = fc_frame_alloc(lport, sizeof(*txrdy));
	if (!fp)
		return -ENOMEM; /* Signal QUEUE_FULL */

	txrdy = fc_frame_payload_get(fp, sizeof(*txrdy));
	memset(txrdy, 0, sizeof(*txrdy));
	txrdy->ft_burst_len = htonl(se_cmd->data_length);

	cmd->seq = lport->tt.seq_start_next(cmd->seq);
	fc_fill_fc_hdr(fp, FC_RCTL_DD_DATA_DESC, ep->did, ep->sid, FC_TYPE_FCP,
		       FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);

	fh = fc_frame_header_get(fp);
	f_ctl = ntoh24(fh->fh_f_ctl);

	lport->tt.seq_send(lport, cmd->seq, fp);
	return 0;
}
Exemplo n.º 2
0
/*
 * Send TX_RDY (transfer ready).
 */
int ft_write_pending(struct se_cmd *se_cmd)
{
    struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
    struct fc_frame *fp;
    struct fcp_txrdy *txrdy;
    struct fc_lport *lport;
    struct fc_exch *ep;
    struct fc_frame_header *fh;
    u32 f_ctl;

    ft_dump_cmd(cmd, __func__);

    if (cmd->aborted)
        return 0;
    ep = fc_seq_exch(cmd->seq);
    lport = ep->lp;
    fp = fc_frame_alloc(lport, sizeof(*txrdy));
    if (!fp)
        return -ENOMEM; /* Signal QUEUE_FULL */

    txrdy = fc_frame_payload_get(fp, sizeof(*txrdy));
    memset(txrdy, 0, sizeof(*txrdy));
    txrdy->ft_burst_len = htonl(se_cmd->data_length);

    cmd->seq = lport->tt.seq_start_next(cmd->seq);
    fc_fill_fc_hdr(fp, FC_RCTL_DD_DATA_DESC, ep->did, ep->sid, FC_TYPE_FCP,
                   FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);

    fh = fc_frame_header_get(fp);
    f_ctl = ntoh24(fh->fh_f_ctl);

    /* Only if it is 'Exchange Responder' */
    if (f_ctl & FC_FC_EX_CTX) {
        /* Target is 'exchange responder' and sending XFER_READY
         * to 'exchange initiator (initiator)'
         */
        if ((ep->xid <= lport->lro_xid) &&
                (fh->fh_r_ctl == FC_RCTL_DD_DATA_DESC)) {
            if (se_cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) {
                /*
                 * cmd may have been broken up into multiple
                 * tasks. Link their sgs together so we can
                 * operate on them all at once.
                 */
                transport_do_task_sg_chain(se_cmd);
                cmd->sg = se_cmd->t_tasks_sg_chained;
                cmd->sg_cnt =
                    se_cmd->t_tasks_sg_chained_no;
            }
            if (cmd->sg && lport->tt.ddp_target(lport, ep->xid,
                                                cmd->sg,
                                                cmd->sg_cnt))
                cmd->was_ddp_setup = 1;
        }
    }
    lport->tt.seq_send(lport, cmd->seq, fp);
    return 0;
}
Exemplo n.º 3
0
/*
 * Send TX_RDY (transfer ready).
 */
int ft_write_pending(struct se_cmd *se_cmd)
{
	struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
	struct fc_frame *fp;
	struct fcp_txrdy *txrdy;
	struct fc_lport *lport;
	struct fc_exch *ep;
	struct fc_frame_header *fh;
	u32 f_ctl;

	ft_dump_cmd(cmd, __func__);

	ep = fc_seq_exch(cmd->seq);
	lport = ep->lp;
	fp = fc_frame_alloc(lport, sizeof(*txrdy));
	if (!fp)
		return PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES;

	txrdy = fc_frame_payload_get(fp, sizeof(*txrdy));
	memset(txrdy, 0, sizeof(*txrdy));
	txrdy->ft_burst_len = htonl(se_cmd->data_length);

	cmd->seq = lport->tt.seq_start_next(cmd->seq);
	fc_fill_fc_hdr(fp, FC_RCTL_DD_DATA_DESC, ep->did, ep->sid, FC_TYPE_FCP,
		       FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);

	fh = fc_frame_header_get(fp);
	f_ctl = ntoh24(fh->fh_f_ctl);

	/* Only if it is 'Exchange Responder' */
	if (f_ctl & FC_FC_EX_CTX) {
		/* Target is 'exchange responder' and sending XFER_READY
		 * to 'exchange initiator (initiator)'
		 */
		if ((ep->xid <= lport->lro_xid) &&
		    (fh->fh_r_ctl == FC_RCTL_DD_DATA_DESC)) {
			if (se_cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) {
				/*
				 * Map se_mem list to scatterlist, so that
				 * DDP can be setup. DDP setup function require
				 * scatterlist. se_mem_list is internal to
				 * TCM/LIO target
				 */
				transport_do_task_sg_chain(se_cmd);
				cmd->sg = T_TASK(se_cmd)->t_tasks_sg_chained;
				cmd->sg_cnt =
					T_TASK(se_cmd)->t_tasks_sg_chained_no;
			}
			if (cmd->sg && lport->tt.ddp_setup(lport, ep->xid,
						    cmd->sg, cmd->sg_cnt))
				cmd->was_ddp_setup = 1;
		}
	}
	lport->tt.seq_send(lport, cmd->seq, fp);
	return 0;
}
Exemplo n.º 4
0
/*
 * Send response.
 */
int ft_queue_status(struct se_cmd *se_cmd)
{
	struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
	struct fc_frame *fp;
	struct fcp_resp_with_ext *fcp;
	struct fc_lport *lport;
	struct fc_exch *ep;
	size_t len;

	if (cmd->aborted)
		return 0;
	ft_dump_cmd(cmd, __func__);
	ep = fc_seq_exch(cmd->seq);
	lport = ep->lp;
	len = sizeof(*fcp) + se_cmd->scsi_sense_length;
	fp = fc_frame_alloc(lport, len);
	if (!fp) {
		/* XXX shouldn't just drop it - requeue and retry? */
		return 0;
	}
	fcp = fc_frame_payload_get(fp, len);
	memset(fcp, 0, len);
	fcp->resp.fr_status = se_cmd->scsi_status;

	len = se_cmd->scsi_sense_length;
	if (len) {
		fcp->resp.fr_flags |= FCP_SNS_LEN_VAL;
		fcp->ext.fr_sns_len = htonl(len);
		memcpy((fcp + 1), se_cmd->sense_buffer, len);
	}

	/*
	 * Test underflow and overflow with one mask.  Usually both are off.
	 * Bidirectional commands are not handled yet.
	 */
	if (se_cmd->se_cmd_flags & (SCF_OVERFLOW_BIT | SCF_UNDERFLOW_BIT)) {
		if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT)
			fcp->resp.fr_flags |= FCP_RESID_OVER;
		else
			fcp->resp.fr_flags |= FCP_RESID_UNDER;
		fcp->ext.fr_resid = cpu_to_be32(se_cmd->residual_count);
	}

	/*
	 * Send response.
	 */
	cmd->seq = lport->tt.seq_start_next(cmd->seq);
	fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP,
		       FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ, 0);

	lport->tt.seq_send(lport, cmd->seq, fp);
	lport->tt.exch_done(cmd->seq);
	return 0;
}
Exemplo n.º 5
0
int ft_write_pending(struct se_cmd *se_cmd)
{
	struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
	struct fc_frame *fp;
	struct fcp_txrdy *txrdy;
	struct fc_lport *lport;
	struct fc_exch *ep;
	struct fc_frame_header *fh;
	u32 f_ctl;

	ft_dump_cmd(cmd, __func__);

	if (cmd->aborted)
		return 0;
	ep = fc_seq_exch(cmd->seq);
	lport = ep->lp;
	fp = fc_frame_alloc(lport, sizeof(*txrdy));
	if (!fp)
		return -ENOMEM; 

	txrdy = fc_frame_payload_get(fp, sizeof(*txrdy));
	memset(txrdy, 0, sizeof(*txrdy));
	txrdy->ft_burst_len = htonl(se_cmd->data_length);

	cmd->seq = lport->tt.seq_start_next(cmd->seq);
	fc_fill_fc_hdr(fp, FC_RCTL_DD_DATA_DESC, ep->did, ep->sid, FC_TYPE_FCP,
		       FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);

	fh = fc_frame_header_get(fp);
	f_ctl = ntoh24(fh->fh_f_ctl);

	
	if (f_ctl & FC_FC_EX_CTX) {
		if ((ep->xid <= lport->lro_xid) &&
		    (fh->fh_r_ctl == FC_RCTL_DD_DATA_DESC)) {
			if (se_cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) {
				transport_do_task_sg_chain(se_cmd);
				cmd->sg = se_cmd->t_tasks_sg_chained;
				cmd->sg_cnt =
					se_cmd->t_tasks_sg_chained_no;
			}
			if (cmd->sg && lport->tt.ddp_target(lport, ep->xid,
							    cmd->sg,
							    cmd->sg_cnt))
				cmd->was_ddp_setup = 1;
		}
	}
	lport->tt.seq_send(lport, cmd->seq, fp);
	return 0;
}
Exemplo n.º 6
0
/*
 * Send TX_RDY (transfer ready).
 */
int ft_write_pending(struct se_cmd *se_cmd)
{
	struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
	struct fc_frame *fp;
	struct fcp_txrdy *txrdy;
	struct fc_lport *lport;
	struct fc_exch *ep;
	struct fc_frame_header *fh;
	u32 f_ctl;

	ft_dump_cmd(cmd, __func__);

	if (cmd->aborted)
		return 0;
	ep = fc_seq_exch(cmd->seq);
	lport = ep->lp;
	fp = fc_frame_alloc(lport, sizeof(*txrdy));
	if (!fp)
		return -ENOMEM; /* Signal QUEUE_FULL */

	txrdy = fc_frame_payload_get(fp, sizeof(*txrdy));
	memset(txrdy, 0, sizeof(*txrdy));
	txrdy->ft_burst_len = htonl(se_cmd->data_length);

	cmd->seq = fc_seq_start_next(cmd->seq);
	fc_fill_fc_hdr(fp, FC_RCTL_DD_DATA_DESC, ep->did, ep->sid, FC_TYPE_FCP,
		       FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);

	fh = fc_frame_header_get(fp);
	f_ctl = ntoh24(fh->fh_f_ctl);

	/* Only if it is 'Exchange Responder' */
	if (f_ctl & FC_FC_EX_CTX) {
		/* Target is 'exchange responder' and sending XFER_READY
		 * to 'exchange initiator (initiator)'
		 */
		if ((ep->xid <= lport->lro_xid) &&
		    (fh->fh_r_ctl == FC_RCTL_DD_DATA_DESC)) {
			if ((se_cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) &&
			    lport->tt.ddp_target(lport, ep->xid,
						 se_cmd->t_data_sg,
						 se_cmd->t_data_nents))
				cmd->was_ddp_setup = 1;
		}
	}
	fc_seq_send(lport, cmd->seq, fp);
	return 0;
}
Exemplo n.º 7
0
int ft_queue_status(struct se_cmd *se_cmd)
{
	struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
	struct fc_frame *fp;
	struct fcp_resp_with_ext *fcp;
	struct fc_lport *lport;
	struct fc_exch *ep;
	size_t len;

	if (cmd->aborted)
		return 0;
	ft_dump_cmd(cmd, __func__);
	ep = fc_seq_exch(cmd->seq);
	lport = ep->lp;
	len = sizeof(*fcp) + se_cmd->scsi_sense_length;
	fp = fc_frame_alloc(lport, len);
	if (!fp) {
		
		return 0;
	}
	fcp = fc_frame_payload_get(fp, len);
	memset(fcp, 0, len);
	fcp->resp.fr_status = se_cmd->scsi_status;

	len = se_cmd->scsi_sense_length;
	if (len) {
		fcp->resp.fr_flags |= FCP_SNS_LEN_VAL;
		fcp->ext.fr_sns_len = htonl(len);
		memcpy((fcp + 1), se_cmd->sense_buffer, len);
	}

	if (se_cmd->se_cmd_flags & (SCF_OVERFLOW_BIT | SCF_UNDERFLOW_BIT)) {
		if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT)
			fcp->resp.fr_flags |= FCP_RESID_OVER;
		else
			fcp->resp.fr_flags |= FCP_RESID_UNDER;
		fcp->ext.fr_resid = cpu_to_be32(se_cmd->residual_count);
	}

	cmd->seq = lport->tt.seq_start_next(cmd->seq);
	fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP,
		       FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ, 0);

	lport->tt.seq_send(lport, cmd->seq, fp);
	lport->tt.exch_done(cmd->seq);
	return 0;
}
Exemplo n.º 8
0
/*
 * Send new command to target.
 */
static void ft_send_cmd(struct ft_cmd *cmd)
{
	struct fc_frame_header *fh = fc_frame_header_get(cmd->req_frame);
	struct se_cmd *se_cmd;
	struct fcp_cmnd *fcp;
	int data_dir;
	u32 data_len;
	int task_attr;
	int ret;

	fcp = fc_frame_payload_get(cmd->req_frame, sizeof(*fcp));
	if (!fcp)
		goto err;

	if (fcp->fc_flags & FCP_CFL_LEN_MASK)
		goto err;		/* not handling longer CDBs yet */

	if (fcp->fc_tm_flags) {
		task_attr = FCP_PTA_SIMPLE;
		data_dir = DMA_NONE;
		data_len = 0;
	} else {
		switch (fcp->fc_flags & (FCP_CFL_RDDATA | FCP_CFL_WRDATA)) {
		case 0:
			data_dir = DMA_NONE;
			break;
		case FCP_CFL_RDDATA:
			data_dir = DMA_FROM_DEVICE;
			break;
		case FCP_CFL_WRDATA:
			data_dir = DMA_TO_DEVICE;
			break;
		case FCP_CFL_WRDATA | FCP_CFL_RDDATA:
			goto err;	/* TBD not supported by tcm_fc yet */
		}
		/*
		 * Locate the SAM Task Attr from fc_pri_ta
		 */
		switch (fcp->fc_pri_ta & FCP_PTA_MASK) {
		case FCP_PTA_HEADQ:
			task_attr = MSG_HEAD_TAG;
			break;
		case FCP_PTA_ORDERED:
			task_attr = MSG_ORDERED_TAG;
			break;
		case FCP_PTA_ACA:
			task_attr = MSG_ACA_TAG;
			break;
		case FCP_PTA_SIMPLE: /* Fallthrough */
		default:
			task_attr = MSG_SIMPLE_TAG;
		}


		task_attr = fcp->fc_pri_ta & FCP_PTA_MASK;
		data_len = ntohl(fcp->fc_dl);
		cmd->cdb = fcp->fc_cdb;
	}

	se_cmd = &cmd->se_cmd;
	/*
	 * Initialize struct se_cmd descriptor from target_core_mod
	 * infrastructure
	 */
	transport_init_se_cmd(se_cmd, &ft_configfs->tf_ops, cmd->sess->se_sess,
			      data_len, data_dir, task_attr,
			      &cmd->ft_sense_buffer[0]);
	/*
	 * Check for FCP task management flags
	 */
	if (fcp->fc_tm_flags) {
		ft_send_tm(cmd);
		return;
	}

	fc_seq_exch(cmd->seq)->lp->tt.seq_set_resp(cmd->seq, ft_recv_seq, cmd);

	cmd->lun = scsilun_to_int((struct scsi_lun *)fcp->fc_lun);
	ret = transport_get_lun_for_cmd(&cmd->se_cmd, NULL, cmd->lun);
	if (ret < 0) {
		ft_dump_cmd(cmd, __func__);
		transport_send_check_condition_and_sense(&cmd->se_cmd,
			cmd->se_cmd.scsi_sense_reason, 0);
		return;
	}

	ret = transport_generic_allocate_tasks(se_cmd, cmd->cdb);

	FT_IO_DBG("r_ctl %x alloc task ret %d\n", fh->fh_r_ctl, ret);
	ft_dump_cmd(cmd, __func__);

	if (ret == -1) {
		transport_send_check_condition_and_sense(se_cmd,
				TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0);
		transport_generic_free_cmd(se_cmd, 0, 1, 0);
		return;
	}
	if (ret == -2) {
		if (se_cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT)
			ft_queue_status(se_cmd);
		else
			transport_send_check_condition_and_sense(se_cmd,
					se_cmd->scsi_sense_reason, 0);
		transport_generic_free_cmd(se_cmd, 0, 1, 0);
		return;
	}
	transport_generic_handle_cdb(se_cmd);
	return;

err:
	ft_send_resp_code(cmd, FCP_CMND_FIELDS_INVALID);
	return;
}
Exemplo n.º 9
0
/*
 * Handle Task Management Request.
 */
static void ft_send_tm(struct ft_cmd *cmd)
{
	struct se_tmr_req *tmr;
	struct fcp_cmnd *fcp;
	struct ft_sess *sess;
	u8 tm_func;

	fcp = fc_frame_payload_get(cmd->req_frame, sizeof(*fcp));

	switch (fcp->fc_tm_flags) {
	case FCP_TMF_LUN_RESET:
		tm_func = TMR_LUN_RESET;
		break;
	case FCP_TMF_TGT_RESET:
		tm_func = TMR_TARGET_WARM_RESET;
		break;
	case FCP_TMF_CLR_TASK_SET:
		tm_func = TMR_CLEAR_TASK_SET;
		break;
	case FCP_TMF_ABT_TASK_SET:
		tm_func = TMR_ABORT_TASK_SET;
		break;
	case FCP_TMF_CLR_ACA:
		tm_func = TMR_CLEAR_ACA;
		break;
	default:
		/*
		 * FCP4r01 indicates having a combination of
		 * tm_flags set is invalid.
		 */
		FT_TM_DBG("invalid FCP tm_flags %x\n", fcp->fc_tm_flags);
		ft_send_resp_code(cmd, FCP_CMND_FIELDS_INVALID);
		return;
	}

	FT_TM_DBG("alloc tm cmd fn %d\n", tm_func);
	tmr = core_tmr_alloc_req(&cmd->se_cmd, cmd, tm_func);
	if (!tmr) {
		FT_TM_DBG("alloc failed\n");
		ft_send_resp_code(cmd, FCP_TMF_FAILED);
		return;
	}
	cmd->se_cmd.se_tmr_req = tmr;

	switch (fcp->fc_tm_flags) {
	case FCP_TMF_LUN_RESET:
		cmd->lun = scsilun_to_int((struct scsi_lun *)fcp->fc_lun);
		if (transport_get_lun_for_tmr(&cmd->se_cmd, cmd->lun) < 0) {
			/*
			 * Make sure to clean up newly allocated TMR request
			 * since "unable to  handle TMR request because failed
			 * to get to LUN"
			 */
			FT_TM_DBG("Failed to get LUN for TMR func %d, "
				  "se_cmd %p, unpacked_lun %d\n",
				  tm_func, &cmd->se_cmd, cmd->lun);
			ft_dump_cmd(cmd, __func__);
			sess = cmd->sess;
			transport_send_check_condition_and_sense(&cmd->se_cmd,
				cmd->se_cmd.scsi_sense_reason, 0);
			transport_generic_free_cmd(&cmd->se_cmd, 0, 1, 0);
			ft_sess_put(sess);
			return;
		}
		break;
	case FCP_TMF_TGT_RESET:
	case FCP_TMF_CLR_TASK_SET:
	case FCP_TMF_ABT_TASK_SET:
	case FCP_TMF_CLR_ACA:
		break;
	default:
		return;
	}
	transport_generic_handle_tmr(&cmd->se_cmd);
}
Exemplo n.º 10
0
/*
 * Send response.
 */
int ft_queue_status(struct se_cmd *se_cmd)
{
	struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
	struct fc_frame *fp;
	struct fcp_resp_with_ext *fcp;
	struct fc_lport *lport;
	struct fc_exch *ep;
	size_t len;
	int rc;

	if (cmd->aborted)
		return 0;
	ft_dump_cmd(cmd, __func__);
	ep = fc_seq_exch(cmd->seq);
	lport = ep->lp;
	len = sizeof(*fcp) + se_cmd->scsi_sense_length;
	fp = fc_frame_alloc(lport, len);
	if (!fp) {
		se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL;
		return -ENOMEM;
	}

	fcp = fc_frame_payload_get(fp, len);
	memset(fcp, 0, len);
	fcp->resp.fr_status = se_cmd->scsi_status;

	len = se_cmd->scsi_sense_length;
	if (len) {
		fcp->resp.fr_flags |= FCP_SNS_LEN_VAL;
		fcp->ext.fr_sns_len = htonl(len);
		memcpy((fcp + 1), se_cmd->sense_buffer, len);
	}

	/*
	 * Test underflow and overflow with one mask.  Usually both are off.
	 * Bidirectional commands are not handled yet.
	 */
	if (se_cmd->se_cmd_flags & (SCF_OVERFLOW_BIT | SCF_UNDERFLOW_BIT)) {
		if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT)
			fcp->resp.fr_flags |= FCP_RESID_OVER;
		else
			fcp->resp.fr_flags |= FCP_RESID_UNDER;
		fcp->ext.fr_resid = cpu_to_be32(se_cmd->residual_count);
	}

	/*
	 * Send response.
	 */
	cmd->seq = lport->tt.seq_start_next(cmd->seq);
	fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP,
		       FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ, 0);

	rc = lport->tt.seq_send(lport, cmd->seq, fp);
	if (rc) {
		pr_info_ratelimited("%s: Failed to send response frame %p, "
				    "xid <0x%x>\n", __func__, fp, ep->xid);
		/*
		 * Generate a TASK_SET_FULL status to notify the initiator
		 * to reduce it's queue_depth after the se_cmd response has
		 * been re-queued by target-core.
		 */
		se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL;
		return -ENOMEM;
	}
	lport->tt.exch_done(cmd->seq);
	return 0;
}