diff --git a/fcst/Makefile b/fcst/Makefile index c42e9f9cf..807cf1851 100644 --- a/fcst/Makefile +++ b/fcst/Makefile @@ -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 \ diff --git a/fcst/fcst.h b/fcst/fcst.h index f25b78d1a..5c6903fbf 100644 --- a/fcst/fcst.h +++ b/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 *); diff --git a/fcst/ft_cmd.c b/fcst/ft_cmd.c index 96c571ca8..9e51030e1 100644 --- a/fcst/ft_cmd.c +++ b/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); } /* diff --git a/fcst/ft_io.c b/fcst/ft_io.c index 88e95d530..26305eecb 100644 --- a/fcst/ft_io.c +++ b/fcst/ft_io.c @@ -26,99 +26,6 @@ #include #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); +} diff --git a/fcst/ft_scst.c b/fcst/ft_scst.c index fc6d5b8a3..569b9fea7 100644 --- a/fcst/ft_scst.c +++ b/fcst/ft_scst.c @@ -21,26 +21,12 @@ #include #include "fcst.h" -MODULE_AUTHOR("Joe Eykholt "); -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 "); +MODULE_DESCRIPTION("SCST FCoE target driver v" FT_VERSION); +MODULE_LICENSE("GPL v2"); +module_init(ft_module_init); module_exit(ft_module_exit); diff --git a/fcst/ft_sess.c b/fcst/ft_sess.c index 61c56a170..f418209ea 100644 --- a/fcst/ft_sess.c +++ b/fcst/ft_sess.c @@ -16,6 +16,7 @@ */ #include #include +#include #include #include #include @@ -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, +};