Since its introduction in 2011, the tcm_fc driver processes SCSI read commands as follows if the number of remaining bytes is a multiple of four: - Queue data by calling fc_seq_send(). The FCoE driver translates that call into a dev_queue_xmit(skb) call which sends the data asynchronously. - After having queued the data for sending, free the data buffer synchronously from inside ft_queue_status(). This race condition can be triggered by running fio --verify against the FCoE initiator driver. Since this bug causes data corruption and since nobody has reported this bug since the tcm_fc driver went upstream, this is a strong indication that the tcm_fc driver is not being used. Hence remove this driver from the kernel tree. Cc: Kiran Patil <kiran.patil@xxxxxxxxx> Cc: Hannes Reinecke <hare@xxxxxxxx> Cc: Mike Christie <michael.christie@xxxxxxxxxx> Cc: Christoph Hellwig <hch@xxxxxx> Cc: Yi Zou <yi.zou@xxxxxxxxx> Signed-off-by: Bart Van Assche <bvanassche@xxxxxxx> --- drivers/target/Kconfig | 1 - drivers/target/Makefile | 1 - drivers/target/tcm_fc/Kconfig | 6 - drivers/target/tcm_fc/Makefile | 7 - drivers/target/tcm_fc/tcm_fc.h | 169 ---------- drivers/target/tcm_fc/tfc_cmd.c | 561 ------------------------------- drivers/target/tcm_fc/tfc_conf.c | 491 --------------------------- drivers/target/tcm_fc/tfc_io.c | 359 -------------------- drivers/target/tcm_fc/tfc_sess.c | 503 --------------------------- 9 files changed, 2098 deletions(-) delete mode 100644 drivers/target/tcm_fc/Kconfig delete mode 100644 drivers/target/tcm_fc/Makefile delete mode 100644 drivers/target/tcm_fc/tcm_fc.h delete mode 100644 drivers/target/tcm_fc/tfc_cmd.c delete mode 100644 drivers/target/tcm_fc/tfc_conf.c delete mode 100644 drivers/target/tcm_fc/tfc_io.c delete mode 100644 drivers/target/tcm_fc/tfc_sess.c diff --git a/drivers/target/Kconfig b/drivers/target/Kconfig index c163b14774d7..4382e56527c0 100644 --- a/drivers/target/Kconfig +++ b/drivers/target/Kconfig @@ -44,7 +44,6 @@ config TCM_USER2 is obsolete. source "drivers/target/loopback/Kconfig" -source "drivers/target/tcm_fc/Kconfig" source "drivers/target/iscsi/Kconfig" source "drivers/target/sbp/Kconfig" diff --git a/drivers/target/Makefile b/drivers/target/Makefile index 45634747377e..fd6729cfb0ff 100644 --- a/drivers/target/Makefile +++ b/drivers/target/Makefile @@ -27,6 +27,5 @@ obj-$(CONFIG_TCM_USER2) += target_core_user.o # Fabric modules obj-$(CONFIG_LOOPBACK_TARGET) += loopback/ -obj-$(CONFIG_TCM_FC) += tcm_fc/ obj-$(CONFIG_ISCSI_TARGET) += iscsi/ obj-$(CONFIG_SBP_TARGET) += sbp/ diff --git a/drivers/target/tcm_fc/Kconfig b/drivers/target/tcm_fc/Kconfig deleted file mode 100644 index 4f3b926b6a1a..000000000000 --- a/drivers/target/tcm_fc/Kconfig +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config TCM_FC - tristate "TCM_FC fabric Plugin" - depends on LIBFC - help - Say Y here to enable the TCM FC plugin for accessing FC fabrics in TCM diff --git a/drivers/target/tcm_fc/Makefile b/drivers/target/tcm_fc/Makefile deleted file mode 100644 index a7d1593ab5af..000000000000 --- a/drivers/target/tcm_fc/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -tcm_fc-y += tfc_cmd.o \ - tfc_conf.o \ - tfc_io.o \ - tfc_sess.o - -obj-$(CONFIG_TCM_FC) += tcm_fc.o diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h deleted file mode 100644 index 2ff716d8cbdd..000000000000 --- a/drivers/target/tcm_fc/tcm_fc.h +++ /dev/null @@ -1,169 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2010 Cisco Systems, Inc. - */ -#ifndef __TCM_FC_H__ -#define __TCM_FC_H__ - -#include <linux/types.h> -#include <target/target_core_base.h> - -#define FT_VERSION "0.4" - -#define FT_NAMELEN 32 /* length of ASCII WWPNs including pad */ -#define FT_TPG_NAMELEN 32 /* max length of TPG name */ -#define FT_LUN_NAMELEN 32 /* max length of LUN name */ -#define TCM_FC_DEFAULT_TAGS 512 /* tags used for per-session preallocation */ - -struct ft_transport_id { - __u8 format; - __u8 __resvd1[7]; - __u8 wwpn[8]; - __u8 __resvd2[8]; -} __attribute__((__packed__)); - -/* - * Session (remote port). - */ -struct ft_sess { - u32 port_id; /* for hash lookup use only */ - u32 params; - u16 max_frame; /* maximum frame size */ - u64 port_name; /* port name for transport ID */ - struct ft_tport *tport; - struct se_session *se_sess; - struct hlist_node hash; /* linkage in ft_sess_hash table */ - struct rcu_head rcu; - struct kref kref; /* ref for hash and outstanding I/Os */ -}; - -/* - * Hash table of sessions per local port. - * Hash lookup by remote port FC_ID. - */ -#define FT_SESS_HASH_BITS 6 -#define FT_SESS_HASH_SIZE (1 << FT_SESS_HASH_BITS) - -/* - * Per local port data. - * This is created only after a TPG exists that allows target function - * for the local port. If the TPG exists, this is allocated when - * we're notified that the local port has been created, or when - * the first PRLI provider callback is received. - */ -struct ft_tport { - struct fc_lport *lport; - struct ft_tpg *tpg; /* NULL if TPG deleted before tport */ - u32 sess_count; /* number of sessions in hash */ - struct rcu_head rcu; - struct hlist_head hash[FT_SESS_HASH_SIZE]; /* list of sessions */ -}; - -/* - * Node ID and authentication. - */ -struct ft_node_auth { - u64 port_name; - u64 node_name; -}; - -/* - * Node ACL for FC remote port session. - */ -struct ft_node_acl { - struct se_node_acl se_node_acl; - struct ft_node_auth node_auth; -}; - -struct ft_lun { - u32 index; - char name[FT_LUN_NAMELEN]; -}; - -/* - * Target portal group (local port). - */ -struct ft_tpg { - u32 index; - struct ft_lport_wwn *lport_wwn; - struct ft_tport *tport; /* active tport or NULL */ - struct list_head lun_list; /* head of LUNs */ - struct se_portal_group se_tpg; - struct workqueue_struct *workqueue; -}; - -struct ft_lport_wwn { - u64 wwpn; - char name[FT_NAMELEN]; - struct list_head ft_wwn_node; - struct ft_tpg *tpg; - struct se_wwn se_wwn; -}; - -/* - * Commands - */ -struct ft_cmd { - struct ft_sess *sess; /* session held for cmd */ - struct fc_seq *seq; /* sequence in exchange mgr */ - struct se_cmd se_cmd; /* Local TCM I/O descriptor */ - struct fc_frame *req_frame; - u32 write_data_len; /* data received on writes */ - struct work_struct work; - /* Local sense buffer */ - unsigned char ft_sense_buffer[TRANSPORT_SENSE_BUFFER]; - u32 was_ddp_setup:1; /* Set only if ddp is setup */ - u32 aborted:1; /* Set if aborted by reset or timeout */ - struct scatterlist *sg; /* Set only if DDP is setup */ - u32 sg_cnt; /* No. of item in scatterlist */ -}; - -extern struct mutex ft_lport_lock; -extern struct fc4_prov ft_prov; -extern unsigned int ft_debug_logging; - -/* - * Fabric methods. - */ - -/* - * Session ops. - */ -void ft_sess_put(struct ft_sess *); -void ft_sess_close(struct se_session *); -u32 ft_sess_get_index(struct se_session *); -u32 ft_sess_get_port_name(struct se_session *, unsigned char *, u32); - -void ft_lport_add(struct fc_lport *, void *); -void ft_lport_del(struct fc_lport *, void *); -int ft_lport_notify(struct notifier_block *, unsigned long, void *); - -/* - * IO methods. - */ -int ft_check_stop_free(struct se_cmd *); -void ft_release_cmd(struct se_cmd *); -int ft_queue_status(struct se_cmd *); -int ft_queue_data_in(struct se_cmd *); -int ft_write_pending(struct se_cmd *); -int ft_get_cmd_state(struct se_cmd *); -void ft_queue_tm_resp(struct se_cmd *); -void ft_aborted_task(struct se_cmd *); - -/* - * other internal functions. - */ -void ft_recv_req(struct ft_sess *, struct fc_frame *); -struct ft_tpg *ft_lport_find_tpg(struct fc_lport *); - -void ft_recv_write_data(struct ft_cmd *, struct fc_frame *); -void ft_dump_cmd(struct ft_cmd *, const char *caller); - -ssize_t ft_format_wwn(char *, size_t, u64); - -/* - * Underlying HW specific helper function - */ -void ft_invl_hw_context(struct ft_cmd *); - -#endif /* __TCM_FC_H__ */ diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c deleted file mode 100644 index 768f250680d9..000000000000 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ /dev/null @@ -1,561 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2010 Cisco Systems, Inc. - */ - -/* XXX TBD some includes may be extraneous */ - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/utsname.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/kthread.h> -#include <linux/types.h> -#include <linux/string.h> -#include <linux/configfs.h> -#include <linux/ctype.h> -#include <linux/hash.h> -#include <asm/unaligned.h> -#include <scsi/scsi_tcq.h> -#include <scsi/libfc.h> - -#include <target/target_core_base.h> -#include <target/target_core_fabric.h> - -#include "tcm_fc.h" - -/* - * Dump cmd state for debugging. - */ -static void _ft_dump_cmd(struct ft_cmd *cmd, const char *caller) -{ - struct fc_exch *ep; - struct fc_seq *sp; - struct se_cmd *se_cmd; - struct scatterlist *sg; - int count; - - se_cmd = &cmd->se_cmd; - pr_debug("%s: cmd %p sess %p seq %p se_cmd %p\n", - caller, cmd, cmd->sess, cmd->seq, se_cmd); - - pr_debug("%s: cmd %p data_nents %u len %u se_cmd_flags <0x%x>\n", - caller, cmd, se_cmd->t_data_nents, - se_cmd->data_length, se_cmd->se_cmd_flags); - - for_each_sg(se_cmd->t_data_sg, sg, se_cmd->t_data_nents, count) - pr_debug("%s: cmd %p sg %p page %p " - "len 0x%x off 0x%x\n", - caller, cmd, sg, - sg_page(sg), sg->length, sg->offset); - - sp = cmd->seq; - if (sp) { - ep = fc_seq_exch(sp); - pr_debug("%s: cmd %p sid %x did %x " - "ox_id %x rx_id %x seq_id %x e_stat %x\n", - caller, cmd, ep->sid, ep->did, ep->oxid, ep->rxid, - sp->id, ep->esb_stat); - } -} - -void ft_dump_cmd(struct ft_cmd *cmd, const char *caller) -{ - if (unlikely(ft_debug_logging)) - _ft_dump_cmd(cmd, caller); -} - -static void ft_free_cmd(struct ft_cmd *cmd) -{ - struct fc_frame *fp; - struct ft_sess *sess; - - if (!cmd) - return; - sess = cmd->sess; - fp = cmd->req_frame; - if (fr_seq(fp)) - fc_seq_release(fr_seq(fp)); - fc_frame_free(fp); - target_free_tag(sess->se_sess, &cmd->se_cmd); - ft_sess_put(sess); /* undo get from lookup at recv */ -} - -void ft_release_cmd(struct se_cmd *se_cmd) -{ - struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); - - ft_free_cmd(cmd); -} - -int ft_check_stop_free(struct se_cmd *se_cmd) -{ - return transport_generic_free_cmd(se_cmd, 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 = fc_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 = fc_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; - } - fc_exch_done(cmd->seq); - /* - * Drop the extra ACK_KREF reference taken by target_submit_cmd() - * ahead of ft_check_stop_free() -> transport_generic_free_cmd() - * final se_cmd->cmd_kref put. - */ - target_put_sess_cmd(&cmd->se_cmd); - return 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; -} - -int ft_get_cmd_state(struct se_cmd *se_cmd) -{ - return 0; -} - -/* - * FC sequence response handler for follow-on sequences (data) and aborts. - */ -static void ft_recv_seq(struct fc_seq *sp, struct fc_frame *fp, void *arg) -{ - struct ft_cmd *cmd = arg; - struct fc_frame_header *fh; - - if (IS_ERR(fp)) { - /* XXX need to find cmd if queued */ - cmd->seq = NULL; - cmd->aborted = true; - return; - } - - fh = fc_frame_header_get(fp); - - switch (fh->fh_r_ctl) { - case FC_RCTL_DD_SOL_DATA: /* write data */ - ft_recv_write_data(cmd, fp); - break; - case FC_RCTL_DD_UNSOL_CTL: /* command */ - case FC_RCTL_DD_SOL_CTL: /* transfer ready */ - case FC_RCTL_DD_DATA_DESC: /* transfer ready */ - default: - pr_debug("%s: unhandled frame r_ctl %x\n", - __func__, fh->fh_r_ctl); - ft_invl_hw_context(cmd); - fc_frame_free(fp); - transport_generic_free_cmd(&cmd->se_cmd, 0); - break; - } -} - -/* - * Send a FCP response including SCSI status and optional FCP rsp_code. - * status is SAM_STAT_GOOD (zero) iff code is valid. - * This is used in error cases, such as allocation failures. - */ -static void ft_send_resp_status(struct fc_lport *lport, - const struct fc_frame *rx_fp, - u32 status, enum fcp_resp_rsp_codes code) -{ - struct fc_frame *fp; - struct fc_seq *sp; - const struct fc_frame_header *fh; - size_t len; - struct fcp_resp_with_ext *fcp; - struct fcp_resp_rsp_info *info; - - fh = fc_frame_header_get(rx_fp); - pr_debug("FCP error response: did %x oxid %x status %x code %x\n", - ntoh24(fh->fh_s_id), ntohs(fh->fh_ox_id), status, code); - len = sizeof(*fcp); - if (status == SAM_STAT_GOOD) - len += sizeof(*info); - fp = fc_frame_alloc(lport, len); - if (!fp) - return; - fcp = fc_frame_payload_get(fp, len); - memset(fcp, 0, len); - fcp->resp.fr_status = status; - if (status == SAM_STAT_GOOD) { - fcp->ext.fr_rsp_len = htonl(sizeof(*info)); - fcp->resp.fr_flags |= FCP_RSP_LEN_VAL; - info = (struct fcp_resp_rsp_info *)(fcp + 1); - info->rsp_code = code; - } - - fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_DD_CMD_STATUS, 0); - sp = fr_seq(fp); - if (sp) { - fc_seq_send(lport, sp, fp); - fc_exch_done(sp); - } else { - lport->tt.frame_send(lport, fp); - } -} - -/* - * Send error or task management response. - */ -static void ft_send_resp_code(struct ft_cmd *cmd, - enum fcp_resp_rsp_codes code) -{ - ft_send_resp_status(cmd->sess->tport->lport, - cmd->req_frame, SAM_STAT_GOOD, code); -} - - -/* - * Send error or task management response. - * Always frees the cmd and associated state. - */ -static void ft_send_resp_code_and_free(struct ft_cmd *cmd, - enum fcp_resp_rsp_codes code) -{ - ft_send_resp_code(cmd, code); - ft_free_cmd(cmd); -} - -/* - * Handle Task Management Request. - */ -static void ft_send_tm(struct ft_cmd *cmd) -{ - struct fcp_cmnd *fcp; - int rc; - 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. - */ - pr_debug("invalid FCP tm_flags %x\n", fcp->fc_tm_flags); - ft_send_resp_code_and_free(cmd, FCP_CMND_FIELDS_INVALID); - return; - } - - /* FIXME: Add referenced task tag for ABORT_TASK */ - rc = target_submit_tmr(&cmd->se_cmd, cmd->sess->se_sess, - &cmd->ft_sense_buffer[0], scsilun_to_int(&fcp->fc_lun), - cmd, tm_func, GFP_KERNEL, 0, TARGET_SCF_ACK_KREF); - if (rc < 0) - ft_send_resp_code_and_free(cmd, FCP_TMF_FAILED); -} - -/* - * Send status from completed task management request. - */ -void ft_queue_tm_resp(struct se_cmd *se_cmd) -{ - struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); - struct se_tmr_req *tmr = se_cmd->se_tmr_req; - enum fcp_resp_rsp_codes code; - - if (cmd->aborted) - return; - switch (tmr->response) { - case TMR_FUNCTION_COMPLETE: - code = FCP_TMF_CMPL; - break; - case TMR_LUN_DOES_NOT_EXIST: - code = FCP_TMF_INVALID_LUN; - break; - case TMR_FUNCTION_REJECTED: - code = FCP_TMF_REJECTED; - break; - case TMR_TASK_DOES_NOT_EXIST: - case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED: - default: - code = FCP_TMF_FAILED; - break; - } - pr_debug("tmr fn %d resp %d fcp code %d\n", - tmr->function, tmr->response, code); - ft_send_resp_code(cmd, code); - /* - * Drop the extra ACK_KREF reference taken by target_submit_tmr() - * ahead of ft_check_stop_free() -> transport_generic_free_cmd() - * final se_cmd->cmd_kref put. - */ - target_put_sess_cmd(&cmd->se_cmd); -} - -void ft_aborted_task(struct se_cmd *se_cmd) -{ - return; -} - -static void ft_send_work(struct work_struct *work); - -/* - * Handle incoming FCP command. - */ -static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp) -{ - struct ft_cmd *cmd; - struct fc_lport *lport = sess->tport->lport; - struct se_session *se_sess = sess->se_sess; - int tag, cpu; - - tag = sbitmap_queue_get(&se_sess->sess_tag_pool, &cpu); - if (tag < 0) - goto busy; - - cmd = &((struct ft_cmd *)se_sess->sess_cmd_map)[tag]; - memset(cmd, 0, sizeof(struct ft_cmd)); - - cmd->se_cmd.map_tag = tag; - cmd->se_cmd.map_cpu = cpu; - cmd->sess = sess; - cmd->seq = fc_seq_assign(lport, fp); - if (!cmd->seq) { - target_free_tag(se_sess, &cmd->se_cmd); - goto busy; - } - cmd->req_frame = fp; /* hold frame during cmd */ - - INIT_WORK(&cmd->work, ft_send_work); - queue_work(sess->tport->tpg->workqueue, &cmd->work); - return; - -busy: - pr_debug("cmd or seq allocation failure - sending BUSY\n"); - ft_send_resp_status(lport, fp, SAM_STAT_BUSY, 0); - fc_frame_free(fp); - ft_sess_put(sess); /* undo get from lookup */ -} - - -/* - * Handle incoming FCP frame. - * Caller has verified that the frame is type FCP. - */ -void ft_recv_req(struct ft_sess *sess, struct fc_frame *fp) -{ - struct fc_frame_header *fh = fc_frame_header_get(fp); - - switch (fh->fh_r_ctl) { - case FC_RCTL_DD_UNSOL_CMD: /* command */ - ft_recv_cmd(sess, fp); - break; - case FC_RCTL_DD_SOL_DATA: /* write data */ - case FC_RCTL_DD_UNSOL_CTL: - case FC_RCTL_DD_SOL_CTL: - case FC_RCTL_DD_DATA_DESC: /* transfer ready */ - case FC_RCTL_ELS4_REQ: /* SRR, perhaps */ - default: - pr_debug("%s: unhandled frame r_ctl %x\n", - __func__, fh->fh_r_ctl); - fc_frame_free(fp); - ft_sess_put(sess); /* undo get from lookup */ - break; - } -} - -/* - * Send new command to target. - */ -static void ft_send_work(struct work_struct *work) -{ - struct ft_cmd *cmd = container_of(work, struct ft_cmd, work); - struct fc_frame_header *fh = fc_frame_header_get(cmd->req_frame); - struct fcp_cmnd *fcp; - int data_dir = 0; - int task_attr; - - 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 */ - - /* - * Check for FCP task management flags - */ - if (fcp->fc_tm_flags) { - ft_send_tm(cmd); - return; - } - - 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 = TCM_HEAD_TAG; - break; - case FCP_PTA_ORDERED: - task_attr = TCM_ORDERED_TAG; - break; - case FCP_PTA_ACA: - task_attr = TCM_ACA_TAG; - break; - case FCP_PTA_SIMPLE: - default: - task_attr = TCM_SIMPLE_TAG; - } - - fc_seq_set_resp(cmd->seq, ft_recv_seq, cmd); - cmd->se_cmd.tag = fc_seq_exch(cmd->seq)->rxid; - /* - * Use a single se_cmd->cmd_kref as we expect to release se_cmd - * directly from ft_check_stop_free callback in response path. - */ - if (target_submit_cmd(&cmd->se_cmd, cmd->sess->se_sess, fcp->fc_cdb, - &cmd->ft_sense_buffer[0], scsilun_to_int(&fcp->fc_lun), - ntohl(fcp->fc_dl), task_attr, data_dir, - TARGET_SCF_ACK_KREF)) - goto err; - - pr_debug("r_ctl %x target_submit_cmd %p\n", fh->fh_r_ctl, cmd); - return; - -err: - ft_send_resp_code_and_free(cmd, FCP_CMND_FIELDS_INVALID); -} diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c deleted file mode 100644 index 1a38c98f681b..000000000000 --- a/drivers/target/tcm_fc/tfc_conf.c +++ /dev/null @@ -1,491 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/******************************************************************************* - * Filename: tcm_fc.c - * - * This file contains the configfs implementation for TCM_fc fabric node. - * Based on tcm_loop_configfs.c - * - * Copyright (c) 2010 Cisco Systems, Inc. - * Copyright (c) 2009,2010 Rising Tide, Inc. - * Copyright (c) 2009,2010 Linux-iSCSI.org - * - * Copyright (c) 2009,2010 Nicholas A. Bellinger <nab@xxxxxxxxxxxxxxx> - * - ****************************************************************************/ - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <generated/utsrelease.h> -#include <linux/utsname.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/kthread.h> -#include <linux/types.h> -#include <linux/string.h> -#include <linux/configfs.h> -#include <linux/kernel.h> -#include <linux/ctype.h> -#include <asm/unaligned.h> -#include <scsi/libfc.h> - -#include <target/target_core_base.h> -#include <target/target_core_fabric.h> - -#include "tcm_fc.h" - -static LIST_HEAD(ft_wwn_list); -DEFINE_MUTEX(ft_lport_lock); - -unsigned int ft_debug_logging; -module_param_named(debug_logging, ft_debug_logging, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); - -/* - * Parse WWN. - * If strict, we require lower-case hex and colon separators to be sure - * the name is the same as what would be generated by ft_format_wwn() - * so the name and wwn are mapped one-to-one. - */ -static ssize_t ft_parse_wwn(const char *name, u64 *wwn, int strict) -{ - const char *cp; - char c; - u32 byte = 0; - u32 pos = 0; - u32 err; - int val; - - *wwn = 0; - for (cp = name; cp < &name[FT_NAMELEN - 1]; cp++) { - c = *cp; - if (c == '\n' && cp[1] == '\0') - continue; - if (strict && pos++ == 2 && byte++ < 7) { - pos = 0; - if (c == ':') - continue; - err = 1; - goto fail; - } - if (c == '\0') { - err = 2; - if (strict && byte != 8) - goto fail; - return cp - name; - } - err = 3; - val = hex_to_bin(c); - if (val < 0 || (strict && isupper(c))) - goto fail; - *wwn = (*wwn << 4) | val; - } - err = 4; -fail: - pr_debug("err %u len %zu pos %u byte %u\n", - err, cp - name, pos, byte); - return -1; -} - -ssize_t ft_format_wwn(char *buf, size_t len, u64 wwn) -{ - u8 b[8]; - - put_unaligned_be64(wwn, b); - return snprintf(buf, len, - "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", - b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]); -} - -static ssize_t ft_wwn_show(void *arg, char *buf) -{ - u64 *wwn = arg; - ssize_t len; - - len = ft_format_wwn(buf, PAGE_SIZE - 2, *wwn); - buf[len++] = '\n'; - return len; -} - -static ssize_t ft_wwn_store(void *arg, const char *buf, size_t len) -{ - ssize_t ret; - u64 wwn; - - ret = ft_parse_wwn(buf, &wwn, 0); - if (ret > 0) - *(u64 *)arg = wwn; - return ret; -} - -/* - * ACL auth ops. - */ - -static ssize_t ft_nacl_port_name_show(struct config_item *item, char *page) -{ - struct se_node_acl *se_nacl = acl_to_nacl(item); - struct ft_node_acl *acl = container_of(se_nacl, - struct ft_node_acl, se_node_acl); - - return ft_wwn_show(&acl->node_auth.port_name, page); -} - -static ssize_t ft_nacl_port_name_store(struct config_item *item, - const char *page, size_t count) -{ - struct se_node_acl *se_nacl = acl_to_nacl(item); - struct ft_node_acl *acl = container_of(se_nacl, - struct ft_node_acl, se_node_acl); - - return ft_wwn_store(&acl->node_auth.port_name, page, count); -} - -static ssize_t ft_nacl_node_name_show(struct config_item *item, - char *page) -{ - struct se_node_acl *se_nacl = acl_to_nacl(item); - struct ft_node_acl *acl = container_of(se_nacl, - struct ft_node_acl, se_node_acl); - - return ft_wwn_show(&acl->node_auth.node_name, page); -} - -static ssize_t ft_nacl_node_name_store(struct config_item *item, - const char *page, size_t count) -{ - struct se_node_acl *se_nacl = acl_to_nacl(item); - struct ft_node_acl *acl = container_of(se_nacl, - struct ft_node_acl, se_node_acl); - - return ft_wwn_store(&acl->node_auth.node_name, page, count); -} - -CONFIGFS_ATTR(ft_nacl_, node_name); -CONFIGFS_ATTR(ft_nacl_, port_name); - -static ssize_t ft_nacl_tag_show(struct config_item *item, - char *page) -{ - return snprintf(page, PAGE_SIZE, "%s", acl_to_nacl(item)->acl_tag); -} - -static ssize_t ft_nacl_tag_store(struct config_item *item, - const char *page, size_t count) -{ - struct se_node_acl *se_nacl = acl_to_nacl(item); - int ret; - - ret = core_tpg_set_initiator_node_tag(se_nacl->se_tpg, se_nacl, page); - - if (ret < 0) - return ret; - return count; -} - -CONFIGFS_ATTR(ft_nacl_, tag); - -static struct configfs_attribute *ft_nacl_base_attrs[] = { - &ft_nacl_attr_port_name, - &ft_nacl_attr_node_name, - &ft_nacl_attr_tag, - NULL, -}; - -/* - * ACL ops. - */ - -/* - * Add ACL for an initiator. The ACL is named arbitrarily. - * The port_name and/or node_name are attributes. - */ -static int ft_init_nodeacl(struct se_node_acl *nacl, const char *name) -{ - struct ft_node_acl *acl = - container_of(nacl, struct ft_node_acl, se_node_acl); - u64 wwpn; - - if (ft_parse_wwn(name, &wwpn, 1) < 0) - return -EINVAL; - - acl->node_auth.port_name = wwpn; - return 0; -} - -/* - * local_port port_group (tpg) ops. - */ -static struct se_portal_group *ft_add_tpg(struct se_wwn *wwn, const char *name) -{ - struct ft_lport_wwn *ft_wwn; - struct ft_tpg *tpg; - struct workqueue_struct *wq; - unsigned long index; - int ret; - - pr_debug("tcm_fc: add tpg %s\n", name); - - /* - * Name must be "tpgt_" followed by the index. - */ - if (strstr(name, "tpgt_") != name) - return NULL; - - ret = kstrtoul(name + 5, 10, &index); - if (ret) - return NULL; - if (index > UINT_MAX) - return NULL; - - if ((index != 1)) { - pr_err("Error, a single TPG=1 is used for HW port mappings\n"); - return ERR_PTR(-ENOSYS); - } - - ft_wwn = container_of(wwn, struct ft_lport_wwn, se_wwn); - tpg = kzalloc(sizeof(*tpg), GFP_KERNEL); - if (!tpg) - return NULL; - tpg->index = index; - tpg->lport_wwn = ft_wwn; - INIT_LIST_HEAD(&tpg->lun_list); - - wq = alloc_workqueue("tcm_fc", 0, 1); - if (!wq) { - kfree(tpg); - return NULL; - } - - ret = core_tpg_register(wwn, &tpg->se_tpg, SCSI_PROTOCOL_FCP); - if (ret < 0) { - destroy_workqueue(wq); - kfree(tpg); - return NULL; - } - tpg->workqueue = wq; - - mutex_lock(&ft_lport_lock); - ft_wwn->tpg = tpg; - mutex_unlock(&ft_lport_lock); - - return &tpg->se_tpg; -} - -static void ft_del_tpg(struct se_portal_group *se_tpg) -{ - struct ft_tpg *tpg = container_of(se_tpg, struct ft_tpg, se_tpg); - struct ft_lport_wwn *ft_wwn = tpg->lport_wwn; - - pr_debug("del tpg %s\n", - config_item_name(&tpg->se_tpg.tpg_group.cg_item)); - - destroy_workqueue(tpg->workqueue); - - /* Wait for sessions to be freed thru RCU, for BUG_ON below */ - synchronize_rcu(); - - mutex_lock(&ft_lport_lock); - ft_wwn->tpg = NULL; - if (tpg->tport) { - tpg->tport->tpg = NULL; - tpg->tport = NULL; - } - mutex_unlock(&ft_lport_lock); - - core_tpg_deregister(se_tpg); - kfree(tpg); -} - -/* - * Verify that an lport is configured to use the tcm_fc module, and return - * the target port group that should be used. - * - * The caller holds ft_lport_lock. - */ -struct ft_tpg *ft_lport_find_tpg(struct fc_lport *lport) -{ - struct ft_lport_wwn *ft_wwn; - - list_for_each_entry(ft_wwn, &ft_wwn_list, ft_wwn_node) { - if (ft_wwn->wwpn == lport->wwpn) - return ft_wwn->tpg; - } - return NULL; -} - -/* - * target config instance ops. - */ - -/* - * Add lport to allowed config. - * The name is the WWPN in lower-case ASCII, colon-separated bytes. - */ -static struct se_wwn *ft_add_wwn( - struct target_fabric_configfs *tf, - struct config_group *group, - const char *name) -{ - struct ft_lport_wwn *ft_wwn; - struct ft_lport_wwn *old_ft_wwn; - u64 wwpn; - - pr_debug("add wwn %s\n", name); - if (ft_parse_wwn(name, &wwpn, 1) < 0) - return NULL; - ft_wwn = kzalloc(sizeof(*ft_wwn), GFP_KERNEL); - if (!ft_wwn) - return NULL; - ft_wwn->wwpn = wwpn; - - mutex_lock(&ft_lport_lock); - list_for_each_entry(old_ft_wwn, &ft_wwn_list, ft_wwn_node) { - if (old_ft_wwn->wwpn == wwpn) { - mutex_unlock(&ft_lport_lock); - kfree(ft_wwn); - return NULL; - } - } - list_add_tail(&ft_wwn->ft_wwn_node, &ft_wwn_list); - ft_format_wwn(ft_wwn->name, sizeof(ft_wwn->name), wwpn); - mutex_unlock(&ft_lport_lock); - - return &ft_wwn->se_wwn; -} - -static void ft_del_wwn(struct se_wwn *wwn) -{ - struct ft_lport_wwn *ft_wwn = container_of(wwn, - struct ft_lport_wwn, se_wwn); - - pr_debug("del wwn %s\n", ft_wwn->name); - mutex_lock(&ft_lport_lock); - list_del(&ft_wwn->ft_wwn_node); - mutex_unlock(&ft_lport_lock); - - kfree(ft_wwn); -} - -static ssize_t ft_wwn_version_show(struct config_item *item, char *page) -{ - return sprintf(page, "TCM FC " FT_VERSION " on %s/%s on " - ""UTS_RELEASE"\n", utsname()->sysname, utsname()->machine); -} - -CONFIGFS_ATTR_RO(ft_wwn_, version); - -static struct configfs_attribute *ft_wwn_attrs[] = { - &ft_wwn_attr_version, - NULL, -}; - -static inline struct ft_tpg *ft_tpg(struct se_portal_group *se_tpg) -{ - return container_of(se_tpg, struct ft_tpg, se_tpg); -} - -static char *ft_get_fabric_wwn(struct se_portal_group *se_tpg) -{ - return ft_tpg(se_tpg)->lport_wwn->name; -} - -static u16 ft_get_tag(struct se_portal_group *se_tpg) -{ - /* - * This tag is used when forming SCSI Name identifier in EVPD=1 0x83 - * to represent the SCSI Target Port. - */ - return ft_tpg(se_tpg)->index; -} - -static int ft_check_false(struct se_portal_group *se_tpg) -{ - return 0; -} - -static void ft_set_default_node_attr(struct se_node_acl *se_nacl) -{ -} - -static u32 ft_tpg_get_inst_index(struct se_portal_group *se_tpg) -{ - return ft_tpg(se_tpg)->index; -} - -static const struct target_core_fabric_ops ft_fabric_ops = { - .module = THIS_MODULE, - .fabric_name = "fc", - .node_acl_size = sizeof(struct ft_node_acl), - .tpg_get_wwn = ft_get_fabric_wwn, - .tpg_get_tag = ft_get_tag, - .tpg_check_demo_mode = ft_check_false, - .tpg_check_demo_mode_cache = ft_check_false, - .tpg_check_demo_mode_write_protect = ft_check_false, - .tpg_check_prod_mode_write_protect = ft_check_false, - .tpg_get_inst_index = ft_tpg_get_inst_index, - .check_stop_free = ft_check_stop_free, - .release_cmd = ft_release_cmd, - .close_session = ft_sess_close, - .sess_get_index = ft_sess_get_index, - .sess_get_initiator_sid = NULL, - .write_pending = ft_write_pending, - .set_default_node_attributes = ft_set_default_node_attr, - .get_cmd_state = ft_get_cmd_state, - .queue_data_in = ft_queue_data_in, - .queue_status = ft_queue_status, - .queue_tm_rsp = ft_queue_tm_resp, - .aborted_task = ft_aborted_task, - /* - * Setup function pointers for generic logic in - * target_core_fabric_configfs.c - */ - .fabric_make_wwn = &ft_add_wwn, - .fabric_drop_wwn = &ft_del_wwn, - .fabric_make_tpg = &ft_add_tpg, - .fabric_drop_tpg = &ft_del_tpg, - .fabric_init_nodeacl = &ft_init_nodeacl, - - .tfc_wwn_attrs = ft_wwn_attrs, - .tfc_tpg_nacl_base_attrs = ft_nacl_base_attrs, -}; - -static struct notifier_block ft_notifier = { - .notifier_call = ft_lport_notify -}; - -static int __init ft_init(void) -{ - int ret; - - ret = target_register_template(&ft_fabric_ops); - if (ret) - goto out; - - ret = fc_fc4_register_provider(FC_TYPE_FCP, &ft_prov); - if (ret) - goto out_unregister_template; - - blocking_notifier_chain_register(&fc_lport_notifier_head, &ft_notifier); - fc_lport_iterate(ft_lport_add, NULL); - return 0; - -out_unregister_template: - target_unregister_template(&ft_fabric_ops); -out: - return ret; -} - -static void __exit ft_exit(void) -{ - blocking_notifier_chain_unregister(&fc_lport_notifier_head, - &ft_notifier); - fc_fc4_deregister_provider(FC_TYPE_FCP, &ft_prov); - fc_lport_iterate(ft_lport_del, NULL); - target_unregister_template(&ft_fabric_ops); - synchronize_rcu(); -} - -MODULE_DESCRIPTION("FC TCM fabric driver " FT_VERSION); -MODULE_LICENSE("GPL"); -module_init(ft_init); -module_exit(ft_exit); diff --git a/drivers/target/tcm_fc/tfc_io.c b/drivers/target/tcm_fc/tfc_io.c deleted file mode 100644 index bbe2e29612fa..000000000000 --- a/drivers/target/tcm_fc/tfc_io.c +++ /dev/null @@ -1,359 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2010 Cisco Systems, Inc. - * - * Portions based on tcm_loop_fabric_scsi.c and libfc/fc_fcp.c - * - * Copyright (c) 2007 Intel Corporation. All rights reserved. - * Copyright (c) 2008 Red Hat, Inc. All rights reserved. - * Copyright (c) 2008 Mike Christie - * Copyright (c) 2009 Rising Tide, Inc. - * Copyright (c) 2009 Linux-iSCSI.org - * Copyright (c) 2009 Nicholas A. Bellinger <nab@xxxxxxxxxxxxxxx> - */ - -/* XXX TBD some includes may be extraneous */ - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/utsname.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/kthread.h> -#include <linux/types.h> -#include <linux/string.h> -#include <linux/configfs.h> -#include <linux/ctype.h> -#include <linux/hash.h> -#include <linux/ratelimit.h> -#include <asm/unaligned.h> -#include <scsi/libfc.h> - -#include <target/target_core_base.h> -#include <target/target_core_fabric.h> - -#include "tcm_fc.h" - -/* - * Deliver read data back to initiator. - * XXX TBD handle resource problems later. - */ -int ft_queue_data_in(struct se_cmd *se_cmd) -{ - struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); - struct fc_frame *fp = NULL; - struct fc_exch *ep; - struct fc_lport *lport; - struct scatterlist *sg = NULL; - size_t remaining; - u32 f_ctl = FC_FC_EX_CTX | FC_FC_REL_OFF; - u32 mem_off = 0; - u32 fh_off = 0; - u32 frame_off = 0; - size_t frame_len = 0; - size_t mem_len = 0; - size_t tlen; - size_t off_in_page; - struct page *page = NULL; - int use_sg; - int error; - void *page_addr; - void *from; - void *to = NULL; - - if (cmd->aborted) - return 0; - - if (se_cmd->scsi_status == SAM_STAT_TASK_SET_FULL) - goto queue_status; - - ep = fc_seq_exch(cmd->seq); - lport = ep->lp; - cmd->seq = fc_seq_start_next(cmd->seq); - - remaining = se_cmd->data_length; - - /* - * Setup to use first mem list entry, unless no data. - */ - BUG_ON(remaining && !se_cmd->t_data_sg); - if (remaining) { - sg = se_cmd->t_data_sg; - mem_len = sg->length; - mem_off = sg->offset; - page = sg_page(sg); - } - - /* no scatter/gather in skb for odd word length due to fc_seq_send() */ - use_sg = !(remaining % 4); - - while (remaining) { - struct fc_seq *seq = cmd->seq; - - if (!seq) { - pr_debug("%s: Command aborted, xid 0x%x\n", - __func__, ep->xid); - break; - } - if (!mem_len) { - sg = sg_next(sg); - mem_len = min((size_t)sg->length, remaining); - mem_off = sg->offset; - page = sg_page(sg); - } - if (!frame_len) { - /* - * If lport's has capability of Large Send Offload LSO) - * , then allow 'frame_len' to be as big as 'lso_max' - * if indicated transfer length is >= lport->lso_max - */ - frame_len = (lport->seq_offload) ? lport->lso_max : - cmd->sess->max_frame; - frame_len = min(frame_len, remaining); - fp = fc_frame_alloc(lport, use_sg ? 0 : frame_len); - if (!fp) - return -ENOMEM; - to = fc_frame_payload_get(fp, 0); - fh_off = frame_off; - frame_off += frame_len; - /* - * Setup the frame's max payload which is used by base - * driver to indicate HW about max frame size, so that - * HW can do fragmentation appropriately based on - * "gso_max_size" of underline netdev. - */ - fr_max_payload(fp) = cmd->sess->max_frame; - } - tlen = min(mem_len, frame_len); - - if (use_sg) { - off_in_page = mem_off; - BUG_ON(!page); - get_page(page); - skb_fill_page_desc(fp_skb(fp), - skb_shinfo(fp_skb(fp))->nr_frags, - page, off_in_page, tlen); - fr_len(fp) += tlen; - fp_skb(fp)->data_len += tlen; - fp_skb(fp)->truesize += page_size(page); - } else { - BUG_ON(!page); - from = kmap_atomic(page + (mem_off >> PAGE_SHIFT)); - page_addr = from; - from += offset_in_page(mem_off); - tlen = min(tlen, (size_t)(PAGE_SIZE - - offset_in_page(mem_off))); - memcpy(to, from, tlen); - kunmap_atomic(page_addr); - to += tlen; - } - - mem_off += tlen; - mem_len -= tlen; - frame_len -= tlen; - remaining -= tlen; - - if (frame_len && - (skb_shinfo(fp_skb(fp))->nr_frags < FC_FRAME_SG_LEN)) - continue; - if (!remaining) - f_ctl |= FC_FC_END_SEQ; - fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid, - FC_TYPE_FCP, f_ctl, fh_off); - error = fc_seq_send(lport, seq, fp); - if (error) { - pr_info_ratelimited("%s: Failed to send frame %p, " - "xid <0x%x>, remaining %zu, " - "lso_max <0x%x>\n", - __func__, fp, ep->xid, - remaining, lport->lso_max); - /* - * Go ahead and set TASK_SET_FULL status ignoring the - * rest of the DataIN, and immediately attempt to - * send the response via ft_queue_status() in order - * to notify the initiator that it should reduce it's - * per LUN queue_depth. - */ - se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL; - break; - } - } -queue_status: - return ft_queue_status(se_cmd); -} - -static void ft_execute_work(struct work_struct *work) -{ - struct ft_cmd *cmd = container_of(work, struct ft_cmd, work); - - target_execute_cmd(&cmd->se_cmd); -} - -/* - * Receive write data frame. - */ -void ft_recv_write_data(struct ft_cmd *cmd, struct fc_frame *fp) -{ - struct se_cmd *se_cmd = &cmd->se_cmd; - struct fc_seq *seq = cmd->seq; - struct fc_exch *ep; - struct fc_lport *lport; - struct fc_frame_header *fh; - struct scatterlist *sg = NULL; - u32 mem_off = 0; - u32 rel_off; - size_t frame_len; - size_t mem_len = 0; - size_t tlen; - struct page *page = NULL; - void *page_addr; - void *from; - void *to; - u32 f_ctl; - void *buf; - - fh = fc_frame_header_get(fp); - if (!(ntoh24(fh->fh_f_ctl) & FC_FC_REL_OFF)) - goto drop; - - f_ctl = ntoh24(fh->fh_f_ctl); - ep = fc_seq_exch(seq); - lport = ep->lp; - if (cmd->was_ddp_setup) { - BUG_ON(!lport); - /* - * Since DDP (Large Rx offload) was setup for this request, - * payload is expected to be copied directly to user buffers. - */ - buf = fc_frame_payload_get(fp, 1); - if (buf) - pr_err("%s: xid 0x%x, f_ctl 0x%x, cmd->sg %p, " - "cmd->sg_cnt 0x%x. DDP was setup" - " hence not expected to receive frame with " - "payload, Frame will be dropped if" - "'Sequence Initiative' bit in f_ctl is" - "not set\n", __func__, ep->xid, f_ctl, - se_cmd->t_data_sg, se_cmd->t_data_nents); - /* - * Invalidate HW DDP context if it was setup for respective - * command. Invalidation of HW DDP context is requited in both - * situation (success and error). - */ - ft_invl_hw_context(cmd); - - /* - * If "Sequence Initiative (TSI)" bit set in f_ctl, means last - * write data frame is received successfully where payload is - * posted directly to user buffer and only the last frame's - * header is posted in receive queue. - * - * If "Sequence Initiative (TSI)" bit is not set, means error - * condition w.r.t. DDP, hence drop the packet and let explict - * ABORTS from other end of exchange timer trigger the recovery. - */ - if (f_ctl & FC_FC_SEQ_INIT) - goto last_frame; - else - goto drop; - } - - rel_off = ntohl(fh->fh_parm_offset); - frame_len = fr_len(fp); - if (frame_len <= sizeof(*fh)) - goto drop; - frame_len -= sizeof(*fh); - from = fc_frame_payload_get(fp, 0); - if (rel_off >= se_cmd->data_length) - goto drop; - if (frame_len + rel_off > se_cmd->data_length) - frame_len = se_cmd->data_length - rel_off; - - /* - * Setup to use first mem list entry, unless no data. - */ - BUG_ON(frame_len && !se_cmd->t_data_sg); - if (frame_len) { - sg = se_cmd->t_data_sg; - mem_len = sg->length; - mem_off = sg->offset; - page = sg_page(sg); - } - - while (frame_len) { - if (!mem_len) { - sg = sg_next(sg); - mem_len = sg->length; - mem_off = sg->offset; - page = sg_page(sg); - } - if (rel_off >= mem_len) { - rel_off -= mem_len; - mem_len = 0; - continue; - } - mem_off += rel_off; - mem_len -= rel_off; - rel_off = 0; - - tlen = min(mem_len, frame_len); - - to = kmap_atomic(page + (mem_off >> PAGE_SHIFT)); - page_addr = to; - to += offset_in_page(mem_off); - tlen = min(tlen, (size_t)(PAGE_SIZE - - offset_in_page(mem_off))); - memcpy(to, from, tlen); - kunmap_atomic(page_addr); - - from += tlen; - frame_len -= tlen; - mem_off += tlen; - mem_len -= tlen; - cmd->write_data_len += tlen; - } -last_frame: - if (cmd->write_data_len == se_cmd->data_length) { - INIT_WORK(&cmd->work, ft_execute_work); - queue_work(cmd->sess->tport->tpg->workqueue, &cmd->work); - } -drop: - fc_frame_free(fp); -} - -/* - * Handle and cleanup any HW specific resources if - * received ABORTS, errors, timeouts. - */ -void ft_invl_hw_context(struct ft_cmd *cmd) -{ - struct fc_seq *seq; - struct fc_exch *ep = NULL; - struct fc_lport *lport = NULL; - - BUG_ON(!cmd); - seq = cmd->seq; - - /* Cleanup the DDP context in HW if DDP was setup */ - if (cmd->was_ddp_setup && seq) { - ep = fc_seq_exch(seq); - if (ep) { - lport = ep->lp; - if (lport && (ep->xid <= lport->lro_xid)) { - /* - * "ddp_done" trigger invalidation of HW - * specific DDP context - */ - cmd->write_data_len = lport->tt.ddp_done(lport, - ep->xid); - - /* - * Resetting same variable to indicate HW's - * DDP context has been invalidated to avoid - * re_invalidation of same context (context is - * identified using ep->xid) - */ - cmd->was_ddp_setup = 0; - } - } - } -} diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c deleted file mode 100644 index 23ce506d5402..000000000000 --- a/drivers/target/tcm_fc/tfc_sess.c +++ /dev/null @@ -1,503 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2010 Cisco Systems, Inc. - */ - -/* XXX TBD some includes may be extraneous */ - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/utsname.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/kthread.h> -#include <linux/types.h> -#include <linux/string.h> -#include <linux/configfs.h> -#include <linux/ctype.h> -#include <linux/hash.h> -#include <linux/rcupdate.h> -#include <linux/rculist.h> -#include <linux/kref.h> -#include <asm/unaligned.h> -#include <scsi/libfc.h> - -#include <target/target_core_base.h> -#include <target/target_core_fabric.h> - -#include "tcm_fc.h" - -#define TFC_SESS_DBG(lport, fmt, args...) \ - pr_debug("host%u: rport %6.6x: " fmt, \ - (lport)->host->host_no, \ - (lport)->port_id, ##args ) - -static void ft_sess_delete_all(struct ft_tport *); - -/* - * Lookup or allocate target local port. - * Caller holds ft_lport_lock. - */ -static struct ft_tport *ft_tport_get(struct fc_lport *lport) -{ - struct ft_tpg *tpg; - struct ft_tport *tport; - int i; - - tport = rcu_dereference_protected(lport->prov[FC_TYPE_FCP], - lockdep_is_held(&ft_lport_lock)); - if (tport && tport->tpg) - return tport; - - tpg = ft_lport_find_tpg(lport); - if (!tpg) - return NULL; - - if (tport) { - tport->tpg = tpg; - tpg->tport = tport; - return tport; - } - - tport = kzalloc(sizeof(*tport), GFP_KERNEL); - if (!tport) - return NULL; - - tport->lport = lport; - tport->tpg = tpg; - tpg->tport = tport; - for (i = 0; i < FT_SESS_HASH_SIZE; i++) - INIT_HLIST_HEAD(&tport->hash[i]); - - rcu_assign_pointer(lport->prov[FC_TYPE_FCP], tport); - return tport; -} - -/* - * Delete a target local port. - * Caller holds ft_lport_lock. - */ -static void ft_tport_delete(struct ft_tport *tport) -{ - struct fc_lport *lport; - struct ft_tpg *tpg; - - ft_sess_delete_all(tport); - lport = tport->lport; - lport->service_params &= ~FCP_SPPF_TARG_FCN; - BUG_ON(tport != lport->prov[FC_TYPE_FCP]); - RCU_INIT_POINTER(lport->prov[FC_TYPE_FCP], NULL); - - tpg = tport->tpg; - if (tpg) { - tpg->tport = NULL; - tport->tpg = NULL; - } - kfree_rcu(tport, rcu); -} - -/* - * Add local port. - * Called thru fc_lport_iterate(). - */ -void ft_lport_add(struct fc_lport *lport, void *arg) -{ - mutex_lock(&ft_lport_lock); - ft_tport_get(lport); - lport->service_params |= FCP_SPPF_TARG_FCN; - mutex_unlock(&ft_lport_lock); -} - -/* - * Delete local port. - * Called thru fc_lport_iterate(). - */ -void ft_lport_del(struct fc_lport *lport, void *arg) -{ - struct ft_tport *tport; - - mutex_lock(&ft_lport_lock); - tport = lport->prov[FC_TYPE_FCP]; - if (tport) - ft_tport_delete(tport); - mutex_unlock(&ft_lport_lock); -} - -/* - * Notification of local port change from libfc. - * Create or delete local port and associated tport. - */ -int ft_lport_notify(struct notifier_block *nb, unsigned long event, void *arg) -{ - struct fc_lport *lport = arg; - - switch (event) { - case FC_LPORT_EV_ADD: - ft_lport_add(lport, NULL); - break; - case FC_LPORT_EV_DEL: - ft_lport_del(lport, NULL); - break; - } - return NOTIFY_DONE; -} - -/* - * Hash function for FC_IDs. - */ -static u32 ft_sess_hash(u32 port_id) -{ - return hash_32(port_id, FT_SESS_HASH_BITS); -} - -/* - * Find session in local port. - * Sessions and hash lists are RCU-protected. - * A reference is taken which must be eventually freed. - */ -static struct ft_sess *ft_sess_get(struct fc_lport *lport, u32 port_id) -{ - struct ft_tport *tport; - struct hlist_head *head; - struct ft_sess *sess; - char *reason = "no session created"; - - rcu_read_lock(); - tport = rcu_dereference(lport->prov[FC_TYPE_FCP]); - if (!tport) { - reason = "not an FCP port"; - goto out; - } - - head = &tport->hash[ft_sess_hash(port_id)]; - hlist_for_each_entry_rcu(sess, head, hash) { - if (sess->port_id == port_id) { - kref_get(&sess->kref); - rcu_read_unlock(); - TFC_SESS_DBG(lport, "port_id %x found %p\n", - port_id, sess); - return sess; - } - } -out: - rcu_read_unlock(); - TFC_SESS_DBG(lport, "port_id %x not found, %s\n", - port_id, reason); - return NULL; -} - -static int ft_sess_alloc_cb(struct se_portal_group *se_tpg, - struct se_session *se_sess, void *p) -{ - struct ft_sess *sess = p; - struct ft_tport *tport = sess->tport; - struct hlist_head *head = &tport->hash[ft_sess_hash(sess->port_id)]; - - TFC_SESS_DBG(tport->lport, "port_id %x sess %p\n", sess->port_id, sess); - hlist_add_head_rcu(&sess->hash, head); - tport->sess_count++; - - return 0; -} - -/* - * Allocate session and enter it in the hash for the local port. - * Caller holds ft_lport_lock. - */ -static struct ft_sess *ft_sess_create(struct ft_tport *tport, u32 port_id, - struct fc_rport_priv *rdata) -{ - struct se_portal_group *se_tpg = &tport->tpg->se_tpg; - struct ft_sess *sess; - struct hlist_head *head; - unsigned char initiatorname[TRANSPORT_IQN_LEN]; - - ft_format_wwn(&initiatorname[0], TRANSPORT_IQN_LEN, rdata->ids.port_name); - - head = &tport->hash[ft_sess_hash(port_id)]; - hlist_for_each_entry_rcu(sess, head, hash) - if (sess->port_id == port_id) - return sess; - - sess = kzalloc(sizeof(*sess), GFP_KERNEL); - if (!sess) - return ERR_PTR(-ENOMEM); - - kref_init(&sess->kref); /* ref for table entry */ - sess->tport = tport; - sess->port_id = port_id; - - sess->se_sess = target_setup_session(se_tpg, TCM_FC_DEFAULT_TAGS, - sizeof(struct ft_cmd), - TARGET_PROT_NORMAL, &initiatorname[0], - sess, ft_sess_alloc_cb); - if (IS_ERR(sess->se_sess)) { - int rc = PTR_ERR(sess->se_sess); - kfree(sess); - sess = ERR_PTR(rc); - } - return sess; -} - -/* - * Unhash the session. - * Caller holds ft_lport_lock. - */ -static void ft_sess_unhash(struct ft_sess *sess) -{ - struct ft_tport *tport = sess->tport; - - hlist_del_rcu(&sess->hash); - BUG_ON(!tport->sess_count); - tport->sess_count--; - sess->port_id = -1; - sess->params = 0; -} - -/* - * Delete session from hash. - * Caller holds ft_lport_lock. - */ -static struct ft_sess *ft_sess_delete(struct ft_tport *tport, u32 port_id) -{ - struct hlist_head *head; - struct ft_sess *sess; - - head = &tport->hash[ft_sess_hash(port_id)]; - hlist_for_each_entry_rcu(sess, head, hash) { - if (sess->port_id == port_id) { - ft_sess_unhash(sess); - return sess; - } - } - return NULL; -} - -static void ft_close_sess(struct ft_sess *sess) -{ - target_stop_session(sess->se_sess); - target_wait_for_sess_cmds(sess->se_sess); - ft_sess_put(sess); -} - -/* - * Delete all sessions from tport. - * Caller holds ft_lport_lock. - */ -static void ft_sess_delete_all(struct ft_tport *tport) -{ - struct hlist_head *head; - struct ft_sess *sess; - - for (head = tport->hash; - head < &tport->hash[FT_SESS_HASH_SIZE]; head++) { - hlist_for_each_entry_rcu(sess, head, hash) { - ft_sess_unhash(sess); - ft_close_sess(sess); /* release from table */ - } - } -} - -/* - * TCM ops for sessions. - */ - -/* - * Remove session and send PRLO. - * This is called when the ACL is being deleted or queue depth is changing. - */ -void ft_sess_close(struct se_session *se_sess) -{ - struct ft_sess *sess = se_sess->fabric_sess_ptr; - u32 port_id; - - mutex_lock(&ft_lport_lock); - port_id = sess->port_id; - if (port_id == -1) { - mutex_unlock(&ft_lport_lock); - return; - } - TFC_SESS_DBG(sess->tport->lport, "port_id %x close session\n", port_id); - ft_sess_unhash(sess); - mutex_unlock(&ft_lport_lock); - ft_close_sess(sess); - /* XXX Send LOGO or PRLO */ - synchronize_rcu(); /* let transport deregister happen */ -} - -u32 ft_sess_get_index(struct se_session *se_sess) -{ - struct ft_sess *sess = se_sess->fabric_sess_ptr; - - return sess->port_id; /* XXX TBD probably not what is needed */ -} - -u32 ft_sess_get_port_name(struct se_session *se_sess, - unsigned char *buf, u32 len) -{ - struct ft_sess *sess = se_sess->fabric_sess_ptr; - - return ft_format_wwn(buf, len, sess->port_name); -} - -/* - * libfc ops involving sessions. - */ - -static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len, - const struct fc_els_spp *rspp, struct fc_els_spp *spp) -{ - struct ft_tport *tport; - struct ft_sess *sess; - u32 fcp_parm; - - tport = ft_tport_get(rdata->local_port); - if (!tport) - goto not_target; /* not a target for this local port */ - - if (!rspp) - goto fill; - - if (rspp->spp_flags & (FC_SPP_OPA_VAL | FC_SPP_RPA_VAL)) - return FC_SPP_RESP_NO_PA; - - /* - * If both target and initiator bits are off, the SPP is invalid. - */ - fcp_parm = ntohl(rspp->spp_params); - if (!(fcp_parm & (FCP_SPPF_INIT_FCN | FCP_SPPF_TARG_FCN))) - return FC_SPP_RESP_INVL; - - /* - * Create session (image pair) only if requested by - * EST_IMG_PAIR flag and if the requestor is an initiator. - */ - if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR) { - spp->spp_flags |= FC_SPP_EST_IMG_PAIR; - if (!(fcp_parm & FCP_SPPF_INIT_FCN)) - return FC_SPP_RESP_CONF; - sess = ft_sess_create(tport, rdata->ids.port_id, rdata); - if (IS_ERR(sess)) { - if (PTR_ERR(sess) == -EACCES) { - spp->spp_flags &= ~FC_SPP_EST_IMG_PAIR; - return FC_SPP_RESP_CONF; - } else - return FC_SPP_RESP_RES; - } - if (!sess->params) - rdata->prli_count++; - sess->params = fcp_parm; - sess->port_name = rdata->ids.port_name; - sess->max_frame = rdata->maxframe_size; - - /* XXX TBD - clearing actions. unit attn, see 4.10 */ - } - - /* - * OR in our service parameters with other provider (initiator), if any. - */ -fill: - fcp_parm = ntohl(spp->spp_params); - fcp_parm &= ~FCP_SPPF_RETRY; - spp->spp_params = htonl(fcp_parm | FCP_SPPF_TARG_FCN); - return FC_SPP_RESP_ACK; - -not_target: - fcp_parm = ntohl(spp->spp_params); - fcp_parm &= ~FCP_SPPF_TARG_FCN; - spp->spp_params = htonl(fcp_parm); - return 0; -} - -/** - * tcm_fcp_prli() - Handle incoming or outgoing PRLI for the FCP target - * @rdata: remote port private - * @spp_len: service parameter page length - * @rspp: received service parameter page (NULL for outgoing PRLI) - * @spp: response service parameter page - * - * Returns spp response code. - */ -static int ft_prli(struct fc_rport_priv *rdata, u32 spp_len, - const struct fc_els_spp *rspp, struct fc_els_spp *spp) -{ - int ret; - - mutex_lock(&ft_lport_lock); - ret = ft_prli_locked(rdata, spp_len, rspp, spp); - mutex_unlock(&ft_lport_lock); - TFC_SESS_DBG(rdata->local_port, "port_id %x flags %x ret %x\n", - rdata->ids.port_id, rspp ? rspp->spp_flags : 0, ret); - return ret; -} - -static void ft_sess_free(struct kref *kref) -{ - struct ft_sess *sess = container_of(kref, struct ft_sess, kref); - - target_remove_session(sess->se_sess); - kfree_rcu(sess, rcu); -} - -void ft_sess_put(struct ft_sess *sess) -{ - int sess_held = kref_read(&sess->kref); - - BUG_ON(!sess_held); - kref_put(&sess->kref, ft_sess_free); -} - -static void ft_prlo(struct fc_rport_priv *rdata) -{ - struct ft_sess *sess; - struct ft_tport *tport; - - mutex_lock(&ft_lport_lock); - tport = rcu_dereference_protected(rdata->local_port->prov[FC_TYPE_FCP], - lockdep_is_held(&ft_lport_lock)); - - if (!tport) { - mutex_unlock(&ft_lport_lock); - return; - } - sess = ft_sess_delete(tport, rdata->ids.port_id); - if (!sess) { - mutex_unlock(&ft_lport_lock); - return; - } - mutex_unlock(&ft_lport_lock); - ft_close_sess(sess); /* release from table */ - rdata->prli_count--; - /* XXX TBD - clearing actions. unit attn, see 4.10 */ -} - -/* - * Handle incoming FCP request. - * Caller has verified that the frame is type FCP. - */ -static void ft_recv(struct fc_lport *lport, struct fc_frame *fp) -{ - struct ft_sess *sess; - u32 sid = fc_frame_sid(fp); - - TFC_SESS_DBG(lport, "recv sid %x\n", sid); - - sess = ft_sess_get(lport, sid); - if (!sess) { - TFC_SESS_DBG(lport, "sid %x sess lookup failed\n", sid); - /* TBD XXX - if FCP_CMND, send PRLO */ - fc_frame_free(fp); - return; - } - ft_recv_req(sess, fp); /* must do ft_sess_put() */ -} - -/* - * Provider ops for libfc. - */ -struct fc4_prov ft_prov = { - .prli = ft_prli, - .prlo = ft_prlo, - .recv = ft_recv, - .module = THIS_MODULE, -};