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:
Bart Van Assche
2014-01-10 08:17:10 +00:00
parent bea6eb98ff
commit 0596a97f5d
6 changed files with 376 additions and 226 deletions

View File

@@ -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 \

View File

@@ -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 *);

View File

@@ -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(&params, 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);
}
/*

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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,
};