mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-21 12:41:26 +00:00
fcst: Merge r4866:5189 from trunk
git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/2.2.x@5191 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
@@ -26,6 +26,10 @@
|
||||
# - install and uninstall must be made as root
|
||||
#
|
||||
|
||||
ifndef PREFIX
|
||||
PREFIX=/usr/local
|
||||
endif
|
||||
|
||||
ifeq ($(KVER),)
|
||||
ifeq ($(KDIR),)
|
||||
KVER = $(shell uname -r)
|
||||
@@ -58,9 +62,9 @@ include $(SUBDIRS)/Makefile_in-tree
|
||||
else
|
||||
SCST_INC_DIR := $(shell if [ -e "$$PWD/../scst" ]; \
|
||||
then echo "$$PWD/../scst/include"; \
|
||||
else echo "/usr/local/include/scst"; fi)
|
||||
else echo "$(PREFIX)/include/scst"; fi)
|
||||
SCST_DIR := $(shell if [ -e "$$PWD/../scst" ]; then echo "$$PWD/../scst/src"; \
|
||||
else echo "/usr/local/include/scst"; fi)
|
||||
else echo "$(PREFIX)/include/scst"; fi)
|
||||
|
||||
all: Modules.symvers Module.symvers
|
||||
$(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \
|
||||
|
||||
41
fcst/fcst.h
41
fcst/fcst.h
@@ -66,10 +66,10 @@ struct ft_sess {
|
||||
u32 max_lso_payload; /* max offloaded payload size */
|
||||
u64 port_name; /* port name for transport ID */
|
||||
struct ft_tport *tport;
|
||||
struct scst_session *scst_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 */
|
||||
struct scst_session *scst_sess;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -93,11 +93,34 @@ struct ft_tport {
|
||||
struct scst_tgt *tgt;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum ft_cmd_state - SCSI command state managed by fcst
|
||||
* @FT_STATE_NEW: New command arrived and is being processed.
|
||||
* @FT_STATE_NEED_DATA: Processing a write or bidir command and waiting
|
||||
* for data arrival.
|
||||
* @FT_STATE_DATA_IN: Data for the write or bidir command arrived and is
|
||||
* being processed.
|
||||
* @FT_STATE_CMD_RSP_SENT: Response with SCSI status has been sent.
|
||||
* @FT_STATE_MGMT: Processing a SCSI task management function.
|
||||
* @FT_STATE_MGMT_RSP_SENT: Response for task management function has been sent.
|
||||
* @FT_STATE_DONE: Command processing finished successfully, command
|
||||
* processing has been aborted or command processing
|
||||
* failed.
|
||||
*/
|
||||
enum ft_cmd_state {
|
||||
FT_STATE_NEW = 0,
|
||||
FT_STATE_NEED_DATA = 1,
|
||||
FT_STATE_DATA_IN = 2,
|
||||
FT_STATE_CMD_RSP_SENT = 3,
|
||||
FT_STATE_MGMT = 4,
|
||||
FT_STATE_MGMT_RSP_SENT = 5,
|
||||
FT_STATE_DONE = 6,
|
||||
};
|
||||
|
||||
/*
|
||||
* Commands
|
||||
*/
|
||||
struct ft_cmd {
|
||||
int serial; /* order received, for debugging */
|
||||
struct fc_seq *seq; /* sequence in exchange mgr */
|
||||
struct fc_frame *req_frame; /* original request frame */
|
||||
u32 write_data_len; /* data received from initiator */
|
||||
@@ -105,6 +128,8 @@ struct ft_cmd {
|
||||
u32 max_lso_payload; /* max offloaded (LSO) data payload */
|
||||
u16 max_payload; /* max transmitted data payload */
|
||||
struct scst_cmd *scst_cmd;
|
||||
spinlock_t lock; /* protects state */
|
||||
enum ft_cmd_state state;
|
||||
};
|
||||
|
||||
extern struct list_head ft_lport_list;
|
||||
@@ -114,15 +139,7 @@ extern struct scst_tgt_template ft_scst_template;
|
||||
/*
|
||||
* libfc interface.
|
||||
*/
|
||||
int ft_prli(struct fc_rport_priv *, u32 spp_len,
|
||||
const struct fc_els_spp *, struct fc_els_spp *);
|
||||
void ft_prlo(struct fc_rport_priv *);
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) \
|
||||
&& (!defined(RHEL_MAJOR) || RHEL_MAJOR -0 <= 5)
|
||||
void ft_recv(struct fc_lport *, struct fc_seq *, struct fc_frame *);
|
||||
#else
|
||||
void ft_recv(struct fc_lport *, struct fc_frame *);
|
||||
#endif
|
||||
extern struct fc4_prov ft_prov;
|
||||
|
||||
/*
|
||||
* SCST interface.
|
||||
@@ -150,6 +167,8 @@ void ft_lport_del(struct fc_lport *, void *);
|
||||
* other internal functions.
|
||||
*/
|
||||
int ft_thread(void *);
|
||||
bool ft_test_and_set_cmd_state(struct ft_cmd *fcmd, enum ft_cmd_state old,
|
||||
enum ft_cmd_state new);
|
||||
void ft_recv_req(struct ft_sess *, struct fc_frame *);
|
||||
void ft_recv_write_data(struct scst_cmd *, struct fc_frame *);
|
||||
int ft_send_read_data(struct scst_cmd *);
|
||||
|
||||
179
fcst/ft_cmd.c
179
fcst/ft_cmd.c
@@ -168,6 +168,89 @@ static void ft_cmd_tm_dump(struct scst_mgmt_cmd *mcmd, const char *caller)
|
||||
ft_cmd_dump(mcmd->cmd_to_abort, caller);
|
||||
}
|
||||
|
||||
/**
|
||||
* ft_set_cmd_state() - set the state of a command
|
||||
*/
|
||||
static enum ft_cmd_state ft_set_cmd_state(struct ft_cmd *fcmd,
|
||||
enum ft_cmd_state new)
|
||||
{
|
||||
enum ft_cmd_state previous;
|
||||
|
||||
spin_lock(&fcmd->lock);
|
||||
previous = fcmd->state;
|
||||
if (previous != FT_STATE_DONE)
|
||||
fcmd->state = new;
|
||||
spin_unlock(&fcmd->lock);
|
||||
|
||||
return previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* ft_test_and_set_cmd_state() - test and set the state of a command
|
||||
*
|
||||
* Returns true if and only if the previous command state was equal to 'old'.
|
||||
*/
|
||||
bool ft_test_and_set_cmd_state(struct ft_cmd *fcmd, enum ft_cmd_state old,
|
||||
enum ft_cmd_state new)
|
||||
{
|
||||
enum ft_cmd_state previous;
|
||||
|
||||
WARN_ON(old == FT_STATE_DONE);
|
||||
WARN_ON(new == FT_STATE_NEW);
|
||||
|
||||
spin_lock(&fcmd->lock);
|
||||
previous = fcmd->state;
|
||||
if (previous == old)
|
||||
fcmd->state = new;
|
||||
spin_unlock(&fcmd->lock);
|
||||
|
||||
return previous == old;
|
||||
}
|
||||
|
||||
static void ft_abort_cmd(struct scst_cmd *cmd)
|
||||
{
|
||||
struct ft_cmd *fcmd = scst_cmd_get_tgt_priv(cmd);
|
||||
struct fc_seq *sp = fcmd->seq;
|
||||
struct fc_exch *ep = fc_seq_exch(sp);
|
||||
struct fc_lport *lport = ep->lp;
|
||||
|
||||
pr_err("%s: cmd %p ox_id %#x rx_id %#x state %d\n", __func__, cmd,
|
||||
ep->oxid, ep->rxid, fcmd->state);
|
||||
|
||||
lport->tt.exch_done(sp);
|
||||
|
||||
spin_lock(&fcmd->lock);
|
||||
switch (fcmd->state) {
|
||||
case FT_STATE_NEW:
|
||||
case FT_STATE_DATA_IN:
|
||||
case FT_STATE_MGMT:
|
||||
/*
|
||||
* Do nothing - defer abort processing until
|
||||
* srpt_xmit_response() is invoked.
|
||||
*/
|
||||
break;
|
||||
case FT_STATE_NEED_DATA:
|
||||
/* SCST_DATA_WRITE */
|
||||
fcmd->state = FT_STATE_DATA_IN;
|
||||
scst_rx_data(cmd, SCST_RX_STATUS_ERROR_FATAL,
|
||||
SCST_CONTEXT_THREAD);
|
||||
break;
|
||||
case FT_STATE_CMD_RSP_SENT:
|
||||
/*
|
||||
* ft_send_response() is either in progress or has finished.
|
||||
* Wait until the SCST core has invoked ft_cmd_done().
|
||||
*/
|
||||
break;
|
||||
case FT_STATE_MGMT_RSP_SENT:
|
||||
default:
|
||||
pr_info("Unexpected command state %d\n", fcmd->state);
|
||||
__WARN();
|
||||
fcmd->state = FT_STATE_DONE;
|
||||
break;
|
||||
}
|
||||
spin_unlock(&fcmd->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Free command and associated frame.
|
||||
*/
|
||||
@@ -191,17 +274,13 @@ static void ft_cmd_done(struct ft_cmd *fcmd)
|
||||
*/
|
||||
void ft_cmd_free(struct scst_cmd *cmd)
|
||||
{
|
||||
struct ft_cmd *fcmd;
|
||||
struct ft_cmd *fcmd = scst_cmd_get_tgt_priv(cmd);
|
||||
|
||||
fcmd = scst_cmd_get_tgt_priv(cmd);
|
||||
if (fcmd) {
|
||||
scst_cmd_set_tgt_priv(cmd, NULL);
|
||||
ft_cmd_done(fcmd);
|
||||
}
|
||||
ft_cmd_done(fcmd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send response, after data if applicable.
|
||||
* Send response.
|
||||
*/
|
||||
int ft_send_response(struct scst_cmd *cmd)
|
||||
{
|
||||
@@ -223,9 +302,12 @@ int ft_send_response(struct scst_cmd *cmd)
|
||||
ep = fc_seq_exch(fcmd->seq);
|
||||
lport = ep->lp;
|
||||
|
||||
WARN_ON(fcmd->state != FT_STATE_NEW && fcmd->state != FT_STATE_DATA_IN);
|
||||
ft_set_cmd_state(fcmd, FT_STATE_CMD_RSP_SENT);
|
||||
|
||||
if (scst_cmd_aborted(cmd)) {
|
||||
FT_IO_DBG("cmd aborted did %x oxid %x\n", ep->did, ep->oxid);
|
||||
scst_set_delivery_status(cmd, SCST_CMD_DELIVERY_ABORTED);
|
||||
ft_abort_cmd(cmd);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -302,7 +384,10 @@ int ft_send_response(struct scst_cmd *cmd)
|
||||
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, fcmd->seq, fp);
|
||||
error = lport->tt.seq_send(lport, fcmd->seq, fp);
|
||||
if (error < 0)
|
||||
pr_err("Sending response for exchange with OX_ID %#x and RX_ID"
|
||||
" %#x failed: %d\n", ep->oxid, ep->rxid, error);
|
||||
done:
|
||||
lport->tt.exch_done(fcmd->seq);
|
||||
scst_tgt_cmd_done(cmd, SCST_CONTEXT_SAME);
|
||||
@@ -325,9 +410,9 @@ static void ft_recv_seq(struct fc_seq *sp, struct fc_frame *fp, void *arg)
|
||||
* the session and all pending commands, so we ignore this response.
|
||||
*/
|
||||
if (IS_ERR(fp)) {
|
||||
FT_IO_DBG("exchange error %ld - aborting cmd\n", -PTR_ERR(fp));
|
||||
scst_rx_mgmt_fn_tag(cmd->sess, SCST_ABORT_TASK, cmd->tag,
|
||||
SCST_ATOMIC, NULL);
|
||||
pr_err("exchange error %ld - aborting cmd %p / tag %lld\n",
|
||||
-PTR_ERR(fp), cmd, cmd->tag);
|
||||
ft_abort_cmd(cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -347,19 +432,6 @@ static void ft_recv_seq(struct fc_seq *sp, struct fc_frame *fp, void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
static void ft_abort_cmd(struct scst_cmd *cmd, enum scst_exec_context context)
|
||||
{
|
||||
scst_data_direction dir;
|
||||
|
||||
dir = scst_cmd_get_data_direction(cmd);
|
||||
if (dir & SCST_DATA_WRITE)
|
||||
scst_rx_data(cmd, SCST_RX_STATUS_ERROR, context);
|
||||
if (dir & SCST_DATA_READ) {
|
||||
scst_set_delivery_status(cmd, SCST_CMD_DELIVERY_ABORTED);
|
||||
scst_tgt_cmd_done(cmd, context);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Command timeout.
|
||||
* SCST calls this when the command has taken too long in the device handler.
|
||||
@@ -367,13 +439,13 @@ static void ft_abort_cmd(struct scst_cmd *cmd, enum scst_exec_context context)
|
||||
void ft_cmd_timeout(struct scst_cmd *cmd)
|
||||
{
|
||||
FT_IO_DBG("%p: timeout\n", cmd);
|
||||
ft_abort_cmd(cmd, SCST_CONTEXT_DIRECT);
|
||||
ft_abort_cmd(cmd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send TX_RDY (transfer ready).
|
||||
*/
|
||||
static int ft_send_xfer_rdy_off(struct scst_cmd *cmd, u32 offset, u32 len)
|
||||
int ft_send_xfer_rdy(struct scst_cmd *cmd)
|
||||
{
|
||||
struct ft_cmd *fcmd;
|
||||
struct fc_frame *fp;
|
||||
@@ -389,10 +461,13 @@ static int ft_send_xfer_rdy_off(struct scst_cmd *cmd, u32 offset, u32 len)
|
||||
if (!fp)
|
||||
return SCST_TGT_RES_QUEUE_FULL;
|
||||
|
||||
WARN_ON(!ft_test_and_set_cmd_state(fcmd, FT_STATE_NEW,
|
||||
FT_STATE_NEED_DATA));
|
||||
|
||||
txrdy = fc_frame_payload_get(fp, sizeof(*txrdy));
|
||||
memset(txrdy, 0, sizeof(*txrdy));
|
||||
txrdy->ft_data_ro = htonl(offset);
|
||||
txrdy->ft_burst_len = htonl(len);
|
||||
txrdy->ft_data_ro = 0;
|
||||
txrdy->ft_burst_len = htonl(scst_cmd_get_bufflen(cmd));
|
||||
|
||||
fcmd->seq = lport->tt.seq_start_next(fcmd->seq);
|
||||
fc_fill_fc_hdr(fp, FC_RCTL_DD_DATA_DESC, ep->did, ep->sid, FC_TYPE_FCP,
|
||||
@@ -401,14 +476,6 @@ static int ft_send_xfer_rdy_off(struct scst_cmd *cmd, u32 offset, u32 len)
|
||||
return SCST_TGT_RES_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send TX_RDY (transfer ready).
|
||||
*/
|
||||
int ft_send_xfer_rdy(struct scst_cmd *cmd)
|
||||
{
|
||||
return ft_send_xfer_rdy_off(cmd, 0, scst_cmd_get_bufflen(cmd));
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a FCP response including SCSI status and optional FCP rsp_code.
|
||||
* status is SAM_STAT_GOOD (zero) if code is valid.
|
||||
@@ -418,17 +485,16 @@ static void ft_send_resp_status(struct fc_frame *rx_fp, u32 status,
|
||||
enum fcp_resp_rsp_codes code)
|
||||
{
|
||||
struct fc_frame *fp;
|
||||
struct fc_frame_header *fh;
|
||||
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;
|
||||
struct fc_lport *lport;
|
||||
struct fc_seq *sp;
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
|
||||
struct fc_exch *ep;
|
||||
#endif
|
||||
|
||||
sp = fr_seq(rx_fp);
|
||||
fh = fc_frame_header_get(rx_fp);
|
||||
FT_IO_DBG("FCP error response: did %x oxid %x status %x code %x\n",
|
||||
ntoh24(fh->fh_s_id), ntohs(fh->fh_ox_id), status, code);
|
||||
@@ -454,6 +520,7 @@ static void ft_send_resp_status(struct fc_frame *rx_fp, u32 status,
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
|
||||
sp = fr_seq(rx_fp);
|
||||
sp = lport->tt.seq_start_next(sp);
|
||||
ep = fc_seq_exch(sp);
|
||||
fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP,
|
||||
@@ -461,19 +528,22 @@ static void ft_send_resp_status(struct fc_frame *rx_fp, u32 status,
|
||||
|
||||
lport->tt.seq_send(lport, sp, fp);
|
||||
out:
|
||||
lport->tt.exch_done(sp);
|
||||
lport->tt.exch_done(fr_seq(rx_fp));
|
||||
#else
|
||||
fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_DD_CMD_STATUS, 0);
|
||||
if (sp)
|
||||
sp = fr_seq(fp);
|
||||
if (sp) {
|
||||
lport->tt.seq_send(lport, sp, fp);
|
||||
else
|
||||
lport->tt.exch_done(sp);
|
||||
} else {
|
||||
lport->tt.frame_send(lport, fp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Send error or task management response.
|
||||
* Always frees the fcmd and associated state.
|
||||
* Always frees the cmd and associated state.
|
||||
*/
|
||||
static void ft_send_resp_code(struct ft_cmd *fcmd, enum fcp_resp_rsp_codes code)
|
||||
{
|
||||
@@ -490,6 +560,9 @@ void ft_cmd_tm_done(struct scst_mgmt_cmd *mcmd)
|
||||
fcmd = scst_mgmt_cmd_get_tgt_priv(mcmd);
|
||||
if (!fcmd)
|
||||
return;
|
||||
|
||||
ft_set_cmd_state(fcmd, FT_STATE_MGMT_RSP_SENT);
|
||||
|
||||
switch (scst_mgmt_cmd_get_status(mcmd)) {
|
||||
case SCST_MGMT_STATUS_SUCCESS:
|
||||
code = FCP_TMF_CMPL;
|
||||
@@ -521,8 +594,13 @@ static void ft_recv_tm(struct scst_session *scst_sess,
|
||||
struct scst_rx_mgmt_params params;
|
||||
int ret;
|
||||
|
||||
ft_set_cmd_state(fcmd, FT_STATE_MGMT);
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) || \
|
||||
defined(CONFIG_SUSE_KERNEL) && \
|
||||
LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 101)
|
||||
params.lun = fcp->fc_lun.scsi_lun;
|
||||
#else
|
||||
params.lun = fcp->fc_lun;
|
||||
@@ -571,7 +649,6 @@ static void ft_recv_tm(struct scst_session *scst_sess,
|
||||
*/
|
||||
static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp)
|
||||
{
|
||||
static atomic_t serial;
|
||||
struct fc_seq *sp;
|
||||
struct scst_cmd *cmd;
|
||||
struct ft_cmd *fcmd;
|
||||
@@ -585,10 +662,10 @@ static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp)
|
||||
fcmd = kzalloc(sizeof(*fcmd), GFP_ATOMIC);
|
||||
if (!fcmd)
|
||||
goto busy;
|
||||
fcmd->serial = atomic_inc_return(&serial); /* debug only */
|
||||
fcmd->max_payload = sess->max_payload;
|
||||
fcmd->max_lso_payload = sess->max_lso_payload;
|
||||
fcmd->req_frame = fp;
|
||||
spin_lock_init(&fcmd->lock);
|
||||
|
||||
fcp = fc_frame_payload_get(fp, sizeof(*fcp));
|
||||
if (!fcp)
|
||||
@@ -609,7 +686,9 @@ static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp)
|
||||
cdb_len += sizeof(fcp->fc_cdb);
|
||||
data_len = ntohl(*(__be32 *)(fcp->fc_cdb + cdb_len));
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) || \
|
||||
defined(CONFIG_SUSE_KERNEL) && \
|
||||
LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 101)
|
||||
cmd = scst_rx_cmd(sess->scst_sess, fcp->fc_lun.scsi_lun,
|
||||
sizeof(fcp->fc_lun), fcp->fc_cdb, cdb_len,
|
||||
SCST_ATOMIC);
|
||||
@@ -621,6 +700,7 @@ static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp)
|
||||
goto busy;
|
||||
fcmd->scst_cmd = cmd;
|
||||
scst_cmd_set_tgt_priv(cmd, fcmd);
|
||||
cmd->state = FT_STATE_NEW;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
|
||||
sp = fr_seq(fp);
|
||||
@@ -675,7 +755,8 @@ err:
|
||||
busy:
|
||||
FT_IO_DBG("cmd allocation failure - sending BUSY\n");
|
||||
ft_send_resp_status(fp, SAM_STAT_BUSY, 0);
|
||||
ft_cmd_done(fcmd);
|
||||
if (fcmd)
|
||||
ft_cmd_done(fcmd);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
203
fcst/ft_io.c
203
fcst/ft_io.c
@@ -26,99 +26,6 @@
|
||||
#include <scsi/fc_encode.h>
|
||||
#include "fcst.h"
|
||||
|
||||
/*
|
||||
* Receive write data frame.
|
||||
*/
|
||||
void ft_recv_write_data(struct scst_cmd *cmd, struct fc_frame *fp)
|
||||
{
|
||||
struct ft_cmd *fcmd;
|
||||
struct fc_frame_header *fh;
|
||||
unsigned int bufflen;
|
||||
u32 rel_off;
|
||||
size_t frame_len;
|
||||
size_t mem_len;
|
||||
size_t tlen;
|
||||
void *from;
|
||||
void *to;
|
||||
int dir;
|
||||
u8 *buf;
|
||||
|
||||
dir = scst_cmd_get_data_direction(cmd);
|
||||
if (dir == SCST_DATA_BIDI) {
|
||||
mem_len = scst_get_out_buf_first(cmd, &buf);
|
||||
bufflen = scst_cmd_get_out_bufflen(cmd);
|
||||
} else {
|
||||
mem_len = scst_get_buf_first(cmd, &buf);
|
||||
bufflen = scst_cmd_get_bufflen(cmd);
|
||||
}
|
||||
to = buf;
|
||||
|
||||
fcmd = scst_cmd_get_tgt_priv(cmd);
|
||||
fh = fc_frame_header_get(fp);
|
||||
frame_len = fr_len(fp);
|
||||
rel_off = ntohl(fh->fh_parm_offset);
|
||||
|
||||
FT_IO_DBG("sid %x oxid %x payload_len %zd rel_off %x\n",
|
||||
ntoh24(fh->fh_s_id), ntohs(fh->fh_ox_id),
|
||||
frame_len - sizeof(*fh), rel_off);
|
||||
|
||||
if (!(ntoh24(fh->fh_f_ctl) & FC_FC_REL_OFF))
|
||||
goto drop;
|
||||
if (frame_len <= sizeof(*fh))
|
||||
goto drop;
|
||||
frame_len -= sizeof(*fh);
|
||||
from = fc_frame_payload_get(fp, 0);
|
||||
|
||||
if (rel_off >= bufflen)
|
||||
goto drop;
|
||||
if (frame_len + rel_off > bufflen)
|
||||
frame_len = bufflen - rel_off;
|
||||
|
||||
while (frame_len) {
|
||||
if (!mem_len) {
|
||||
if (dir == SCST_DATA_BIDI) {
|
||||
scst_put_out_buf(cmd, buf);
|
||||
mem_len = scst_get_out_buf_next(cmd, &buf);
|
||||
} else {
|
||||
scst_put_buf(cmd, buf);
|
||||
mem_len = scst_get_buf_next(cmd, &buf);
|
||||
}
|
||||
to = buf;
|
||||
if (!mem_len)
|
||||
break;
|
||||
}
|
||||
if (rel_off) {
|
||||
if (rel_off >= mem_len) {
|
||||
rel_off -= mem_len;
|
||||
mem_len = 0;
|
||||
continue;
|
||||
}
|
||||
mem_len -= rel_off;
|
||||
to += rel_off;
|
||||
rel_off = 0;
|
||||
}
|
||||
|
||||
tlen = min(mem_len, frame_len);
|
||||
memcpy(to, from, tlen);
|
||||
|
||||
from += tlen;
|
||||
frame_len -= tlen;
|
||||
mem_len -= tlen;
|
||||
to += tlen;
|
||||
fcmd->write_data_len += tlen;
|
||||
}
|
||||
if (mem_len) {
|
||||
if (dir == SCST_DATA_BIDI)
|
||||
scst_put_out_buf(cmd, buf);
|
||||
else
|
||||
scst_put_buf(cmd, buf);
|
||||
}
|
||||
if (fcmd->write_data_len == cmd->data_len)
|
||||
scst_rx_data(cmd, SCST_RX_STATUS_SUCCESS, SCST_CONTEXT_THREAD);
|
||||
drop:
|
||||
fc_frame_free(fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send read data back to initiator.
|
||||
*/
|
||||
@@ -187,6 +94,16 @@ int ft_send_read_data(struct scst_cmd *cmd)
|
||||
|
||||
/* no scatter/gather in skb for odd word length due to fc_seq_send() */
|
||||
use_sg = !(remaining % 4) && lport->sg_supp;
|
||||
/*
|
||||
* Note: since libfc_function_template.seq_send() sends frames
|
||||
* asynchronously and since the SCST data buffer is freed as soon as
|
||||
* scst_tgt_cmd_done() has been invoked, data has to be copied into
|
||||
* the skb instead of only copying a pointer to the data. To do: defer
|
||||
* invocation of scst_tgt_cmd_done() until sending the data frames
|
||||
* finished once the paged fragment destructor or an equivalent is
|
||||
* upstream.
|
||||
*/
|
||||
use_sg = false;
|
||||
|
||||
while (remaining) {
|
||||
if (!loop_limit) {
|
||||
@@ -247,10 +164,10 @@ int ft_send_read_data(struct scst_cmd *cmd)
|
||||
frame_len -= tlen;
|
||||
}
|
||||
|
||||
mem_len -= tlen;
|
||||
mem_off += tlen;
|
||||
remaining -= tlen;
|
||||
mem_len -= tlen;
|
||||
frame_off += tlen;
|
||||
remaining -= tlen;
|
||||
|
||||
if (frame_len)
|
||||
continue;
|
||||
@@ -274,3 +191,99 @@ int ft_send_read_data(struct scst_cmd *cmd)
|
||||
}
|
||||
return SCST_TGT_RES_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive write data frame.
|
||||
*/
|
||||
void ft_recv_write_data(struct scst_cmd *cmd, struct fc_frame *fp)
|
||||
{
|
||||
struct ft_cmd *fcmd;
|
||||
struct fc_frame_header *fh;
|
||||
unsigned int bufflen;
|
||||
u32 rel_off;
|
||||
size_t frame_len;
|
||||
size_t mem_len;
|
||||
size_t tlen;
|
||||
void *from;
|
||||
void *to;
|
||||
int dir;
|
||||
u8 *buf;
|
||||
|
||||
dir = scst_cmd_get_data_direction(cmd);
|
||||
if (dir == SCST_DATA_BIDI) {
|
||||
mem_len = scst_get_out_buf_first(cmd, &buf);
|
||||
bufflen = scst_cmd_get_out_bufflen(cmd);
|
||||
} else {
|
||||
mem_len = scst_get_buf_first(cmd, &buf);
|
||||
bufflen = scst_cmd_get_bufflen(cmd);
|
||||
}
|
||||
to = buf;
|
||||
|
||||
fcmd = scst_cmd_get_tgt_priv(cmd);
|
||||
fh = fc_frame_header_get(fp);
|
||||
|
||||
if (!(ntoh24(fh->fh_f_ctl) & FC_FC_REL_OFF))
|
||||
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 >= bufflen)
|
||||
goto drop;
|
||||
if (frame_len + rel_off > bufflen)
|
||||
frame_len = bufflen - rel_off;
|
||||
|
||||
while (frame_len) {
|
||||
if (!mem_len) {
|
||||
if (dir == SCST_DATA_BIDI) {
|
||||
scst_put_out_buf(cmd, buf);
|
||||
mem_len = scst_get_out_buf_next(cmd, &buf);
|
||||
} else {
|
||||
scst_put_buf(cmd, buf);
|
||||
mem_len = scst_get_buf_next(cmd, &buf);
|
||||
}
|
||||
to = buf;
|
||||
if (!mem_len)
|
||||
break;
|
||||
}
|
||||
if (rel_off) {
|
||||
if (rel_off >= mem_len) {
|
||||
rel_off -= mem_len;
|
||||
mem_len = 0;
|
||||
continue;
|
||||
}
|
||||
mem_len -= rel_off;
|
||||
to += rel_off;
|
||||
rel_off = 0;
|
||||
}
|
||||
|
||||
tlen = min(mem_len, frame_len);
|
||||
memcpy(to, from, tlen);
|
||||
|
||||
from += tlen;
|
||||
frame_len -= tlen;
|
||||
mem_len -= tlen;
|
||||
to += tlen;
|
||||
fcmd->write_data_len += tlen;
|
||||
}
|
||||
if (mem_len) {
|
||||
if (dir == SCST_DATA_BIDI)
|
||||
scst_put_out_buf(cmd, buf);
|
||||
else
|
||||
scst_put_buf(cmd, buf);
|
||||
}
|
||||
if (fcmd->write_data_len == bufflen) {
|
||||
spin_lock(&fcmd->lock);
|
||||
if (fcmd->state == FT_STATE_NEED_DATA) {
|
||||
fcmd->state = FT_STATE_DATA_IN;
|
||||
scst_rx_data(cmd, SCST_RX_STATUS_SUCCESS,
|
||||
SCST_CONTEXT_THREAD);
|
||||
}
|
||||
spin_unlock(&fcmd->lock);
|
||||
}
|
||||
drop:
|
||||
fc_frame_free(fp);
|
||||
}
|
||||
|
||||
@@ -21,26 +21,12 @@
|
||||
#include <scsi/libfc.h>
|
||||
#include "fcst.h"
|
||||
|
||||
MODULE_AUTHOR("Joe Eykholt <jeykholt@cisco.com>");
|
||||
MODULE_DESCRIPTION("Fibre-Channel SCST target");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
unsigned int ft_debug_logging;
|
||||
module_param_named(debug_logging, ft_debug_logging, int, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(debug_logging, "log levels bigmask");
|
||||
|
||||
DEFINE_MUTEX(ft_lport_lock);
|
||||
|
||||
/*
|
||||
* Provider ops for libfc.
|
||||
*/
|
||||
static struct fc4_prov ft_prov = {
|
||||
.prli = ft_prli,
|
||||
.prlo = ft_prlo,
|
||||
.recv = ft_recv,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct notifier_block ft_notifier = {
|
||||
.notifier_call = ft_lport_notify
|
||||
};
|
||||
@@ -84,7 +70,6 @@ static int __init ft_module_init(void)
|
||||
fc_lport_iterate(ft_lport_add, NULL);
|
||||
return 0;
|
||||
}
|
||||
module_init(ft_module_init);
|
||||
|
||||
static void __exit ft_module_exit(void)
|
||||
{
|
||||
@@ -95,4 +80,9 @@ static void __exit ft_module_exit(void)
|
||||
scst_unregister_target_template(&ft_scst_template);
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Joe Eykholt <jeykholt@cisco.com>");
|
||||
MODULE_DESCRIPTION("SCST FCoE target driver v" FT_VERSION);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
module_init(ft_module_init);
|
||||
module_exit(ft_module_exit);
|
||||
|
||||
151
fcst/ft_sess.c
151
fcst/ft_sess.c
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/hash.h>
|
||||
#include <asm/unaligned.h>
|
||||
@@ -165,6 +166,31 @@ int ft_lport_notify(struct notifier_block *nb, unsigned long event, void *arg)
|
||||
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);
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) && \
|
||||
! (LINUX_VERSION_CODE >> 8 == KERNEL_VERSION(3, 4, 0) >> 8 && \
|
||||
LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 41)) && \
|
||||
! (LINUX_VERSION_CODE >> 8 == KERNEL_VERSION(3, 2, 0) >> 8 && \
|
||||
LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 44)) && \
|
||||
!defined(CONFIG_SUSE_KERNEL)
|
||||
/*
|
||||
* See also commit 4b20db3 (kref: Implement kref_get_unless_zero v3 -- v3.8).
|
||||
* See also commit e3a5505 in branch stable/linux-3.4.y (v3.4.41).
|
||||
* See also commit 3fa8ee5 in branch stable/linux-3.2.y (v3.2.44).
|
||||
*/
|
||||
static inline int __must_check kref_get_unless_zero(struct kref *kref)
|
||||
{
|
||||
return atomic_add_unless(&kref->refcount, 1, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Find session in local port.
|
||||
* Sessions and hash lists are RCU-protected.
|
||||
@@ -177,21 +203,26 @@ static struct ft_sess *ft_sess_get(struct fc_lport *lport, u32 port_id)
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
|
||||
struct hlist_node *pos;
|
||||
#endif
|
||||
struct ft_sess *sess = NULL;
|
||||
struct ft_sess *sess;
|
||||
|
||||
rcu_read_lock();
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
|
||||
tport = rcu_dereference_protected(lport->prov[FC_TYPE_FCP], true);
|
||||
#else
|
||||
tport = rcu_dereference(lport->prov[FC_TYPE_FCP]);
|
||||
#endif
|
||||
if (!tport)
|
||||
goto out;
|
||||
|
||||
head = &tport->hash[hash_32(port_id, FT_SESS_HASH_BITS)];
|
||||
head = &tport->hash[ft_sess_hash(port_id)];
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
|
||||
hlist_for_each_entry_rcu(sess, pos, head, hash) {
|
||||
#else
|
||||
hlist_for_each_entry_rcu(sess, head, hash) {
|
||||
#endif
|
||||
if (sess->port_id == port_id) {
|
||||
kref_get(&sess->kref);
|
||||
if (!kref_get_unless_zero(&sess->kref))
|
||||
sess = NULL;
|
||||
rcu_read_unlock();
|
||||
FT_SESS_DBG("port_id %x found %p\n", port_id, sess);
|
||||
return sess;
|
||||
@@ -225,7 +256,7 @@ static int ft_sess_create(struct ft_tport *tport, struct fc_rport_priv *rdata,
|
||||
return FC_SPP_RESP_CONF;
|
||||
}
|
||||
|
||||
head = &tport->hash[hash_32(port_id, FT_SESS_HASH_BITS)];
|
||||
head = &tport->hash[ft_sess_hash(port_id)];
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
|
||||
hlist_for_each_entry_rcu(sess, pos, head, hash) {
|
||||
#else
|
||||
@@ -296,7 +327,7 @@ static struct ft_sess *ft_sess_delete(struct ft_tport *tport, u32 port_id)
|
||||
#endif
|
||||
struct ft_sess *sess;
|
||||
|
||||
head = &tport->hash[hash_32(port_id, FT_SESS_HASH_BITS)];
|
||||
head = &tport->hash[ft_sess_hash(port_id)];
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
|
||||
hlist_for_each_entry_rcu(sess, pos, head, hash) {
|
||||
#else
|
||||
@@ -382,7 +413,7 @@ static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len,
|
||||
/*
|
||||
* If both target and initiator bits are off, the SPP is invalid.
|
||||
*/
|
||||
fcp_parm = ntohl(rspp->spp_params); /* requested parameters */
|
||||
fcp_parm = ntohl(rspp->spp_params);
|
||||
if (!(fcp_parm & (FCP_SPPF_INIT_FCN | FCP_SPPF_TARG_FCN)))
|
||||
return FC_SPP_RESP_INVL;
|
||||
|
||||
@@ -395,7 +426,13 @@ static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len,
|
||||
|
||||
if (!(fcp_parm & FCP_SPPF_INIT_FCN))
|
||||
return FC_SPP_RESP_CONF;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
|
||||
tport = rcu_dereference_protected(
|
||||
rdata->local_port->prov[FC_TYPE_FCP],
|
||||
lockdep_is_held(&ft_lport_lock));
|
||||
#else
|
||||
tport = rcu_dereference(rdata->local_port->prov[FC_TYPE_FCP]);
|
||||
#endif
|
||||
if (!tport) {
|
||||
/* not a target for this local port */
|
||||
return FC_SPP_RESP_CONF;
|
||||
@@ -417,7 +454,7 @@ static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len,
|
||||
* Don't force RETRY on the initiator, though.
|
||||
*/
|
||||
fill:
|
||||
fcp_parm = ntohl(spp->spp_params); /* response parameters */
|
||||
fcp_parm = ntohl(spp->spp_params);
|
||||
spp->spp_params = htonl(fcp_parm | FCP_SPPF_TARG_FCN);
|
||||
return FC_SPP_RESP_ACK;
|
||||
}
|
||||
@@ -431,17 +468,16 @@ fill:
|
||||
*
|
||||
* Returns spp response code.
|
||||
*/
|
||||
int ft_prli(struct fc_rport_priv *rdata, u32 spp_len,
|
||||
const struct fc_els_spp *rspp, struct fc_els_spp *spp)
|
||||
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;
|
||||
|
||||
FT_SESS_DBG("starting PRLI port_id %x\n", rdata->ids.port_id);
|
||||
mutex_lock(&ft_lport_lock);
|
||||
ret = ft_prli_locked(rdata, spp_len, rspp, spp);
|
||||
mutex_unlock(&ft_lport_lock);
|
||||
FT_SESS_DBG("port_id %x flags %x parms %x ret %x\n", rdata->ids.port_id,
|
||||
rspp ? rspp->spp_flags : 0, ntohl(spp->spp_params), ret);
|
||||
FT_SESS_DBG("port_id %x flags %x ret %x\n",
|
||||
rdata->ids.port_id, rspp ? rspp->spp_flags : 0, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -465,73 +501,60 @@ static void ft_sess_free(struct kref *kref)
|
||||
|
||||
static void ft_sess_put(struct ft_sess *sess)
|
||||
{
|
||||
int sess_held = atomic_read(&sess->kref.refcount);
|
||||
|
||||
BUG_ON(!sess_held);
|
||||
BUG_ON(!sess);
|
||||
BUG_ON(atomic_read(&sess->kref.refcount) <= 0);
|
||||
kref_put(&sess->kref, ft_sess_free);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete ft_sess for PRLO.
|
||||
* Called with ft_lport_lock held.
|
||||
*/
|
||||
static struct ft_sess *ft_sess_lookup_delete(struct fc_rport_priv *rdata)
|
||||
static void ft_prlo(struct fc_rport_priv *rdata)
|
||||
{
|
||||
struct ft_sess *sess;
|
||||
struct ft_tport *tport;
|
||||
|
||||
tport = rcu_dereference(rdata->local_port->prov[FC_TYPE_FCP]);
|
||||
if (!tport)
|
||||
return NULL;
|
||||
sess = ft_sess_delete(tport, rdata->ids.port_id);
|
||||
if (sess)
|
||||
sess->params = 0;
|
||||
return sess;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle PRLO.
|
||||
*/
|
||||
void ft_prlo(struct fc_rport_priv *rdata)
|
||||
{
|
||||
struct ft_sess *sess;
|
||||
|
||||
mutex_lock(&ft_lport_lock);
|
||||
sess = ft_sess_lookup_delete(rdata);
|
||||
mutex_unlock(&ft_lport_lock);
|
||||
if (!sess)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
|
||||
tport = rcu_dereference_protected(rdata->local_port->prov[FC_TYPE_FCP],
|
||||
lockdep_is_held(&ft_lport_lock));
|
||||
#else
|
||||
tport = rcu_dereference(rdata->local_port->prov[FC_TYPE_FCP]);
|
||||
#endif
|
||||
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);
|
||||
|
||||
/*
|
||||
* Release the session hold from the table.
|
||||
* When all command-starting threads have returned,
|
||||
* kref will call ft_sess_free which will unregister
|
||||
* the session.
|
||||
* fcmds referencing the session are safe.
|
||||
*/
|
||||
ft_sess_put(sess); /* release from table */
|
||||
rdata->prli_count--;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) && !defined(RHEL_MAJOR)
|
||||
static inline u32 fc_frame_sid(const struct fc_frame *fp)
|
||||
{
|
||||
return ntoh24(fc_frame_header_get(fp)->fh_s_id);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Handle incoming FCP request.
|
||||
*
|
||||
* Caller has verified that the frame is type FCP.
|
||||
* Note that this may be called directly from the softirq context.
|
||||
*/
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) \
|
||||
&& (!defined(RHEL_MAJOR) || RHEL_MAJOR -0 <= 5)
|
||||
void ft_recv(struct fc_lport *lport, struct fc_seq *sp, struct fc_frame *fp)
|
||||
static void ft_recv(struct fc_lport *lport, struct fc_seq *sp,
|
||||
struct fc_frame *fp)
|
||||
#else
|
||||
void ft_recv(struct fc_lport *lport, struct fc_frame *fp)
|
||||
static void ft_recv(struct fc_lport *lport, struct fc_frame *fp)
|
||||
#endif
|
||||
{
|
||||
struct ft_sess *sess;
|
||||
struct fc_frame_header *fh;
|
||||
u32 sid;
|
||||
|
||||
fh = fc_frame_header_get(fp);
|
||||
sid = ntoh24(fh->fh_s_id);
|
||||
u32 sid = fc_frame_sid(fp);
|
||||
|
||||
FT_SESS_DBG("sid %x preempt %x\n", sid, preempt_count());
|
||||
|
||||
@@ -588,15 +611,22 @@ int ft_tgt_enable(struct scst_tgt *tgt, bool enable)
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&ft_lport_lock);
|
||||
|
||||
if (enable) {
|
||||
FT_SESS_DBG("enable tgt %s\n", tgt->tgt_name);
|
||||
tport = scst_tgt_get_tgt_priv(tgt);
|
||||
if (tport == NULL) {
|
||||
ret = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
tport->enabled = 1;
|
||||
tport->lport->service_params |= FCP_SPPF_TARG_FCN;
|
||||
} else {
|
||||
FT_SESS_DBG("disable tgt %s\n", tgt->tgt_name);
|
||||
ft_tgt_release(tgt);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&ft_lport_lock);
|
||||
return ret;
|
||||
}
|
||||
@@ -605,6 +635,9 @@ bool ft_tgt_enabled(struct scst_tgt *tgt)
|
||||
{
|
||||
struct ft_tport *tport;
|
||||
|
||||
if (tgt == NULL)
|
||||
return false;
|
||||
|
||||
tport = scst_tgt_get_tgt_priv(tgt);
|
||||
return tport->enabled;
|
||||
}
|
||||
@@ -627,3 +660,13 @@ int ft_report_aen(struct scst_aen *aen)
|
||||
aen->event_fn, sess->port_id, scst_aen_get_lun(aen));
|
||||
return SCST_AEN_RES_FAILED; /* XXX TBD */
|
||||
}
|
||||
|
||||
/*
|
||||
* Provider ops for libfc.
|
||||
*/
|
||||
struct fc4_prov ft_prov = {
|
||||
.prli = ft_prli,
|
||||
.prlo = ft_prlo,
|
||||
.recv = ft_recv,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user